├── .gitignore ├── src ├── nss-etcd-api.h ├── ghttp │ ├── ghttpConf.sh.in │ ├── http_base64.h │ ├── http_date.h │ ├── http_global.h │ ├── http_uri.h │ ├── http_req.h │ ├── http_hdrs.h │ ├── http_resp.h │ ├── http_base64.c │ ├── http_trans.h │ ├── ghttp_constants.h │ ├── http_uri.c │ ├── ghttp.h │ ├── http_req.c │ ├── http_trans.c │ ├── http_date.c │ ├── http_hdrs.c │ ├── ghttp.c │ └── http_resp.c ├── nss-etcd-api.c ├── nss-etcd.c └── etcd-api │ ├── etcd-api2.h │ └── etcd-api2.c ├── FIXES.txt ├── test ├── etd-test.c └── showip.c ├── Vagrantfile ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .*.swp 2 | .cproject 3 | .project 4 | autom4te.cache/ 5 | -------------------------------------------------------------------------------- /src/nss-etcd-api.h: -------------------------------------------------------------------------------- 1 | #ifndef NSS_ETCD_NSS_ETCD_API 2 | #define NSS_ETCD_NSS_ETCD_API 3 | 4 | #include 5 | 6 | enum nss_status nss_etcd_ip_address(const char *name, uint32_t *ip_address); 7 | 8 | #endif //NSS_ETCD_NSS_ETCD_API -------------------------------------------------------------------------------- /src/ghttp/ghttpConf.sh.in: -------------------------------------------------------------------------------- 1 | # 2 | # Configuration file for using the ghttp library in GNOME applications. 3 | # 4 | GHTTP_LIBDIR="@GHTTP_LIBDIR@" 5 | GHTTP_LIBS="@GHTTP_LIBS@" 6 | GHTTP_INCLUDEDIR="@GHTTP_INCLUDEDIR@" 7 | MODULE_VERSION="ghttp-@VERSION@" 8 | 9 | -------------------------------------------------------------------------------- /FIXES.txt: -------------------------------------------------------------------------------- 1 | - remove fprints from etcd-api 2 | - thread safety? 3 | - recursive lookups (if curl is using a hostname) 4 | - ordering in nssswitch.conf (before or after DNS) 5 | - external config file 6 | - stress test to check for memory leaks 7 | - valgrind over it 8 | 9 | obvious 10 | - error handling 11 | - make sure we are free()ing things that need to be freed -------------------------------------------------------------------------------- /test/etd-test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void main(){ 7 | 8 | const char *name = "database.primary"; 9 | uint32_t address = NULL; 10 | enum nss_status result; 11 | 12 | result = nss_etcd_ip_address(name, &address); 13 | 14 | printf("value: %u, result: %i\n", address, result); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | config.vm.box = "ubuntu/trusty64" 9 | config.vm.network "forwarded_port", guest: 2379, host: 2379 10 | config.vm.provision 'shell', :inline => ' 11 | curl -L https://github.com/coreos/etcd/releases/download/v2.1.0-rc.0/etcd-v2.1.0-rc.0-linux-amd64.tar.gz -o etcd-v2.1.0-rc.0-linux-amd64.tar.gz 12 | tar xzvf etcd-v2.1.0-rc.0-linux-amd64.tar.gz 13 | cd etcd-v2.1.0-rc.0-linux-amd64 14 | ./etcd & 15 | ' 16 | end 17 | -------------------------------------------------------------------------------- /src/ghttp/http_base64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_base64.h -- Header file for base64 function(s). 3 | * Created: Christopher Blizzard , 20-Nov-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef HTTP_BASE64_H 23 | #define HTTP_BASE64_H 24 | 25 | char * 26 | http_base64_encode(const char *text); 27 | 28 | #endif /* HTTP_BASE64_H */ 29 | -------------------------------------------------------------------------------- /src/ghttp/http_date.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_date.h -- Routines for parsing and generating http dates 3 | * Created: Christoper Blizzard , 16-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef HTTP_DATE_H 23 | #define HTTP_DATE_H 24 | 25 | #include 26 | 27 | time_t 28 | http_date_to_time(const char *a_date); 29 | 30 | #endif /* HTTP_DATE_H */ 31 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(nss-etcd C) 4 | 5 | set(CMAKE_BUILD_TYPE Debug) 6 | 7 | include_directories( 8 | PROJECT_SOURCE_DIR 9 | src 10 | test 11 | ) 12 | 13 | add_library(nss_etcd SHARED src/nss-etcd.c) 14 | set_target_properties(nss_etcd PROPERTIES SOVERSION 2) 15 | target_link_libraries(nss_etcd etcd-nss-api) 16 | install(TARGETS nss_etcd DESTINATION lib) 17 | 18 | add_executable(etc-test test/etd-test.c) 19 | target_link_libraries(etc-test etcd-nss-api) 20 | 21 | # Libraries 22 | add_library(etcd-nss-api STATIC 23 | src/nss-etcd-api.c 24 | ) 25 | set_target_properties(etcd-nss-api PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 26 | target_link_libraries(etcd-nss-api etcd-api) 27 | 28 | add_library(etcd-api STATIC 29 | src/etcd-api/etcd-api2.c 30 | ) 31 | set_target_properties(etcd-api PROPERTIES POSITION_INDEPENDENT_CODE TRUE) 32 | target_link_libraries(etcd-api ghttp yajl) 33 | 34 | add_library(ghttp STATIC 35 | src/ghttp/ghttp.c 36 | src/ghttp/http_base64.c 37 | src/ghttp/http_date.c 38 | src/ghttp/http_hdrs.c 39 | src/ghttp/http_req.c 40 | src/ghttp/http_resp.c 41 | src/ghttp/http_trans.c 42 | src/ghttp/http_uri.c 43 | ) 44 | set_target_properties(ghttp PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -------------------------------------------------------------------------------- /src/ghttp/http_global.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_global.h -- Definitions for things that will be used throughout 3 | * the library. 4 | * Created: Christopher Blizzard , 16-Aug-1998 5 | * 6 | * Copyright (C) 1998 Free Software Foundation 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Library General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Library General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Library General Public 19 | * License along with this library; if not, write to the Free 20 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 | */ 22 | 23 | #ifndef HTTP_GLOBAL_H 24 | #define HTTP_GLOBAL_H 25 | 26 | #define HTTP_TRANS_ERR -1 27 | #define HTTP_TRANS_NOT_DONE 1 28 | #define HTTP_TRANS_DONE 2 29 | 30 | #define HTTP_TRANS_SYNC 0 31 | #define HTTP_TRANS_ASYNC 1 32 | 33 | #endif /* GHTTP_GLOBAL_H */ 34 | -------------------------------------------------------------------------------- /src/ghttp/http_uri.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_uri.h --- Contains routines for working with uri's 3 | * Created: Christopher Blizzard , 9-Jul-98 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef HTTP_URI_H 23 | #define HTTP_URI_H 24 | 25 | /* strings that are used all over the place */ 26 | 27 | typedef struct http_uri_tag 28 | { 29 | char *full; /* full URL */ 30 | char *proto; /* protocol */ 31 | char *host; /* copy semantics */ 32 | unsigned short port; 33 | char *resource; /* copy semantics */ 34 | } http_uri; 35 | 36 | http_uri * 37 | http_uri_new(void); 38 | 39 | void 40 | http_uri_destroy(http_uri *a_uri); 41 | 42 | int 43 | http_uri_parse(char *a_uri, 44 | http_uri *a_request); 45 | 46 | #endif 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /test/showip.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** showip.c -- show IP addresses for a host given on the command line 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | struct addrinfo hints, *res, *p; 16 | int status; 17 | char ipstr[INET6_ADDRSTRLEN]; 18 | 19 | if (argc != 2) { 20 | fprintf(stderr,"usage: showip hostname\n"); 21 | return 1; 22 | } 23 | 24 | memset(&hints, 0, sizeof hints); 25 | hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version 26 | hints.ai_socktype = SOCK_STREAM; 27 | 28 | if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) { 29 | fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); 30 | return 2; 31 | } 32 | 33 | printf("IP addresses for %s:\n\n", argv[1]); 34 | 35 | for(p = res;p != NULL; p = p->ai_next) { 36 | void *addr; 37 | char *ipver; 38 | 39 | // get the pointer to the address itself, 40 | // different fields in IPv4 and IPv6: 41 | if (p->ai_family == AF_INET) { // IPv4 42 | struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr; 43 | addr = &(ipv4->sin_addr); 44 | ipver = "IPv4"; 45 | } else { // IPv6 46 | struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr; 47 | addr = &(ipv6->sin6_addr); 48 | ipver = "IPv6"; 49 | } 50 | 51 | // convert the IP to a string and print it: 52 | inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr); 53 | printf(" %s: %s\n", ipver, ipstr); 54 | } 55 | 56 | freeaddrinfo(res); // free the linked list 57 | 58 | return 0; 59 | } 60 | -------------------------------------------------------------------------------- /src/nss-etcd-api.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "etcd-api/etcd-api2.h" 7 | 8 | #include "nss-etcd-api.h" 9 | 10 | 11 | static char *nss_etcd_key_from_name(const char *name){ 12 | int i; 13 | 14 | if (name == NULL) 15 | return NULL; 16 | char *new_name; 17 | size_t name_length; 18 | 19 | name_length = strlen(name)+1; 20 | new_name = malloc(name_length); 21 | for (i=0; i < name_length; i++){ 22 | /* replace '.' with '/' */ 23 | new_name[i] = (char) ((name[i] == '.') ? '/' : name[i]); 24 | } 25 | return new_name; 26 | } 27 | 28 | enum nss_status nss_etcd_ip_address(const char *name, uint32_t *ip_address){ 29 | 30 | char *key; 31 | char *value = NULL; 32 | char *servers = "localhost"; 33 | etcd_session etcd_session = NULL; 34 | enum nss_status return_code; 35 | 36 | // covert the domain to a key 37 | key = nss_etcd_key_from_name(name); 38 | if(!key){ 39 | return_code = NSS_STATUS_UNAVAIL; 40 | goto cleanup; 41 | } 42 | 43 | // create the session 44 | etcd_session = etcd_open_str(servers); 45 | if(!etcd_session){ 46 | return_code = NSS_STATUS_UNAVAIL; 47 | goto cleanup; 48 | } 49 | 50 | // get the value 51 | value = etcd_get(etcd_session,key); 52 | if(!value){ 53 | return_code = NSS_STATUS_NOTFOUND; 54 | goto cleanup; 55 | } 56 | 57 | // get the value in decimal form 58 | if(!inet_pton(AF_INET, value, ip_address)){ 59 | return_code = NSS_STATUS_NOTFOUND; 60 | goto cleanup; 61 | } 62 | 63 | // We found the record! 64 | return_code = NSS_STATUS_SUCCESS; 65 | goto cleanup; 66 | 67 | cleanup: 68 | free(key); 69 | if(value != NULL) { 70 | free(value); 71 | } 72 | if(etcd_session != NULL) { 73 | etcd_close_str(etcd_session); 74 | } 75 | return return_code; 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/nss-etcd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "nss-etcd-api.h" 8 | 9 | 10 | enum nss_status _nss_etcd_gethostbyname2_r( 11 | const char *name, 12 | int af, 13 | struct hostent * result, 14 | char *buffer, 15 | size_t buflen, 16 | int *errnop, 17 | int *h_errnop) { 18 | 19 | enum nss_status status; 20 | size_t idx; 21 | uint32_t address; 22 | 23 | /* We could get back here if ghttp is trying to lookup the host it needs to 24 | * connect to (part of etcd-api). Get out of here so we don't end up in an 25 | * endless loop 26 | */ 27 | if(!strcmp(name, "localhost")) 28 | return NSS_STATUS_UNAVAIL; 29 | 30 | /* Bail out if we're not IPv4 */ 31 | if(af != AF_INET) 32 | return NSS_STATUS_UNAVAIL; 33 | 34 | /* Get the ip address */ 35 | status = nss_etcd_ip_address(name, &address); 36 | if(status != NSS_STATUS_SUCCESS) 37 | return status; 38 | 39 | 40 | /* Populate the hostent struct */ 41 | result->h_length = sizeof(uint32_t); // ipv4 only 42 | 43 | /* null out our buffer */ 44 | *((char**) buffer) = NULL; 45 | 46 | /* get the pointer address, casted as a char** */ 47 | result->h_aliases = (char**) buffer; 48 | idx = sizeof(char*); 49 | 50 | /* Official name */ 51 | strcpy(buffer+idx, name); 52 | result->h_name = buffer+idx; 53 | idx += strlen(name)+1; 54 | 55 | /* always IPv4 for now */ 56 | result->h_addrtype = AF_INET; 57 | 58 | if (idx % sizeof(char*)) 59 | idx += (sizeof(char*) - idx % sizeof(char*)); /* Align on 32 bit boundary */ 60 | 61 | 62 | /* Copy in the IP address that we got from nss_etcd_ip_address() */ 63 | memcpy(buffer+idx, &address, sizeof(uint32_t)); 64 | 65 | /* pointer to the first address */ 66 | ((char**) (buffer+idx+4))[0] = buffer+idx; 67 | 68 | /* null denotes the end of the addr_list */ 69 | ((char**) (buffer+idx+4))[1] = NULL; 70 | 71 | /* pointer to the address list (in the buffer) */ 72 | result->h_addr_list = (char**) (buffer+idx+4); 73 | 74 | return status; 75 | } 76 | 77 | enum nss_status _nss_etcd_gethostbyname_r ( 78 | const char *name, 79 | struct hostent *result, 80 | char *buffer, 81 | size_t buflen, 82 | int *errnop, 83 | int *h_errnop) { 84 | 85 | return _nss_etcd_gethostbyname2_r( 86 | name, 87 | AF_INET, 88 | result, 89 | buffer, 90 | buflen, 91 | errnop, 92 | h_errnop); 93 | } -------------------------------------------------------------------------------- /src/ghttp/http_req.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_req.h -- Routines for setting up an http request 3 | * Created: Christopher Blizzard , 6-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | /* types of requests */ 23 | 24 | #ifndef HTTP_REQ_H 25 | #define HTTP_REQ_H 26 | 27 | #include 28 | #include "http_hdrs.h" 29 | #include "http_trans.h" 30 | 31 | typedef enum http_req_type { 32 | http_req_type_get = 0, 33 | http_req_type_options, 34 | http_req_type_head, 35 | http_req_type_post, 36 | http_req_type_put, 37 | http_req_type_delete, 38 | http_req_type_trace, 39 | http_req_type_connect, 40 | http_req_type_propfind, 41 | http_req_type_proppatch, 42 | http_req_type_mkcol, 43 | http_req_type_copy, 44 | http_req_type_move, 45 | http_req_type_lock, 46 | http_req_type_unlock 47 | } http_req_type; 48 | 49 | typedef enum http_req_state_tag { 50 | http_req_state_start = 0, 51 | http_req_state_sending_request, 52 | http_req_state_sending_headers, 53 | http_req_state_sending_body 54 | } http_req_state; 55 | 56 | /* same character representations as above. */ 57 | 58 | extern const char *http_req_type_char[]; 59 | 60 | typedef struct http_req_tag { 61 | http_req_type type; 62 | float http_ver; 63 | char *host; 64 | char *full_uri; 65 | char *resource; 66 | char *body; 67 | int body_len; 68 | http_hdr_list *headers; 69 | http_req_state state; 70 | } http_req; 71 | 72 | http_req * 73 | http_req_new(void); 74 | 75 | void 76 | http_req_destroy(http_req *a_req); 77 | 78 | int 79 | http_req_prepare(http_req *a_req); 80 | 81 | int 82 | http_req_send(http_req *a_req, http_trans_conn *a_conn); 83 | 84 | #endif /* HTTP_REQ_H */ 85 | -------------------------------------------------------------------------------- /src/ghttp/http_hdrs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_hdrs.h -- This file contains declarations for http headers 3 | * Created: Christopher Blizzard , 3-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef HTTP_HDRS_H 23 | #define HTTP_HDRS_H 24 | 25 | #include "ghttp_constants.h" 26 | 27 | /* the list of known headers */ 28 | extern const char *http_hdr_known_list[]; 29 | 30 | /* a header list */ 31 | #define HTTP_HDRS_MAX 256 32 | typedef struct http_hdr_list_tag 33 | { 34 | char *header[HTTP_HDRS_MAX]; 35 | char *value[HTTP_HDRS_MAX]; 36 | } http_hdr_list; 37 | 38 | /* functions dealing with headers */ 39 | 40 | /* check to see if the library knows about the header */ 41 | const char * 42 | http_hdr_is_known(const char *a_hdr); 43 | 44 | /* create a new list */ 45 | http_hdr_list * 46 | http_hdr_list_new(void); 47 | 48 | /* destroy a list */ 49 | void 50 | http_hdr_list_destroy(http_hdr_list *a_list); 51 | 52 | /* set a value in a list */ 53 | int 54 | http_hdr_set_value(http_hdr_list *a_list, 55 | const char *a_name, 56 | const char *a_val); 57 | 58 | /* set the value in a list from a range, not a NTS */ 59 | int 60 | http_hdr_set_value_no_nts(http_hdr_list *a_list, 61 | const char *a_name_start, 62 | int a_name_len, 63 | const char *a_val_start, 64 | int a_val_len); 65 | 66 | /* get a copy of a value in a list */ 67 | char * 68 | http_hdr_get_value(http_hdr_list *a_list, 69 | const char *a_name); 70 | 71 | /* get a copy of the headers in a list */ 72 | int 73 | http_hdr_get_headers(http_hdr_list *a_list, 74 | char ***a_names, 75 | int *a_num_names); 76 | 77 | /* clear a header in a list */ 78 | int 79 | http_hdr_clear_value(http_hdr_list *a_list, 80 | const char *a_name); 81 | 82 | 83 | #endif /* HTTP_HDRS_H */ 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Dynamic configuration is cool, but we still live in a world of static configuration. `nss-etcd` provides a host resolution module for GNU libc Name Service Switch (NSS). Think of it as a distributed and consistent hosts file without the complication and delay of normal DNS. `nss-etcd` does _not_ require any modification to your existing DNS setup. 2 | ```shell 3 | $ curl -L http://127.0.0.1:2379/v2/keys/database/primary -XPUT -d value="10.0.0.20" 4 | $ ping database.primary 5 | PING database.primary (10.0.0.20) 56(84) bytes of data. 6 | 64 bytes from 10.0.0.20: icmp_seq=1 ttl=64 time=0.320 ms 7 | ``` 8 | #Getting started 9 | ### Prerequisites 10 | CMake, libyajl and a C compiler are required to build and install `nss-etcd` 11 | ```shell 12 | apt-get install cmake libyajl-dev gcc 13 | ``` 14 | **Currently etcd is also required to be running on localhost.** 15 | ### Building and installing nss-etcd 16 | ```shell 17 | git clone https://github.com/ryandoyle/nss-etcd.git 18 | cd nss-etcd 19 | cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr . 20 | make install 21 | ``` 22 | ### Configuring 23 | `nss-etcd` has to be enabled in `/etc/nsswitch.conf` to start to be used for host resolution. Add `etcd` in the hosts section of this file. 24 | ```file:/etc/nsswitch.conf 25 | hosts: files myhostname etcd dns 26 | ``` 27 | Resolution is ordered left to right. For most cases, it should be before `dns`. 28 | 29 | ### Adding and resolving names 30 | The namespace in etcd is mapped to dot-delimitered names. You have full control of the namespace you create in etcd. 31 | ```shell 32 | $ curl -L http://127.0.0.1:2379/v2/keys/database/primary -XPUT -d value="10.0.0.20" 33 | $ getent hosts database.primary 34 | 10.0.0.20 database.primary 35 | ``` 36 | - `mysql/info/address` -> `mysql.info.address` 37 | - `dns/services/db/us-east/primary` -> `dns.services.db.us-east.primary` 38 | - You get the idea 39 | 40 | 41 | # Issues 42 | ### Minimal tolerance for bugs 43 | Bugs in `nss-etcd` have an impact on **all** processes that do name resolution. I would highly suggest that `nss-etcd` is not run in production environments. 44 | ### Caching 45 | `nss-etcd` does not cache (on purpose). It is possible that your application **does** cache resolution though. 46 | ### Out-of-band resolution 47 | Most applications will use `glibc` and `gethostbyname` but it's possible some directly query DNS by reading `/etc/resolv.conf` and performing their own resolution. This would bypass `nss` and therefore `nss-etcd`. 48 | 49 | 50 | # Other works 51 | - Consul provides a DNS interface to their service discovery tool. 52 | - SkyDNS is a DNS server built on-top of etcd 53 | -------------------------------------------------------------------------------- /src/ghttp/http_resp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_resp.h -- routines for reading http responses 3 | * Created: Christopher Blizzard 9-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef HTTP_RESP_H 23 | #define HTTP_RESP_H 24 | 25 | #include "http_hdrs.h" 26 | #include "http_trans.h" 27 | #include "http_req.h" 28 | 29 | #define HTTP_RESP_INFORMATIONAL(x) (x >=100 && < 200) 30 | #define HTTP_RESP_SUCCESS(x) (x >= 200 && x < 300) 31 | #define HTTP_RESP_REDIR(x) (x >= 300 && x < 400) 32 | #define HTTP_RESP_CLIENT_ERR(x) (x >= 400 && x < 500) 33 | #define HTTP_RESP_SERVER_ERR(x) (x >= 500 && x < 600) 34 | 35 | typedef enum http_resp_header_state_tag 36 | { 37 | http_resp_header_start = 0, 38 | http_resp_reading_header 39 | } http_resp_header_state; 40 | 41 | typedef enum http_resp_body_state_tag 42 | { 43 | http_resp_body_start = 0, 44 | http_resp_body_read_content_length, 45 | http_resp_body_read_chunked, 46 | http_resp_body_read_standard 47 | } http_resp_body_state; 48 | 49 | 50 | 51 | typedef struct http_resp_tag 52 | { 53 | float http_ver; 54 | int status_code; 55 | char *reason_phrase; 56 | http_hdr_list *headers; 57 | char *body; 58 | int body_len; 59 | int content_length; 60 | int flushed_length; 61 | http_resp_header_state header_state; 62 | http_resp_body_state body_state; 63 | } http_resp; 64 | 65 | http_resp * 66 | http_resp_new(void); 67 | 68 | void 69 | http_resp_destroy(http_resp *a_resp); 70 | 71 | int 72 | http_resp_read_body(http_resp *a_resp, 73 | http_req *a_req, 74 | http_trans_conn *a_conn); 75 | 76 | int 77 | http_resp_read_headers(http_resp *a_resp, http_trans_conn *a_conn); 78 | 79 | void 80 | http_resp_flush(http_resp *a_resp, 81 | http_trans_conn *a_conn); 82 | 83 | #endif /* HTTP_RESP_H */ 84 | -------------------------------------------------------------------------------- /src/ghttp/http_base64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * http_base64.c -- This file contains code for encoding strings with base64. 3 | * Created: Christopher Blizzard , 20-Nov-1998 4 | * Author: Joe Orton 5 | * 6 | * Copyright (C) 1998 Free Software Foundation 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Library General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Library General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Library General Public 19 | * License along with this library; if not, write to the Free 20 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | const char b64_alphabet[65] = { 28 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 29 | "abcdefghijklmnopqrstuvwxyz" 30 | "0123456789+/=" }; 31 | 32 | char * 33 | http_base64_encode(const char *text) { 34 | /* The tricky thing about this is doing the padding at the end, 35 | * doing the bit manipulation requires a bit of concentration only */ 36 | char *buffer = NULL; 37 | char *point = NULL; 38 | int inlen = 0; 39 | int outlen = 0; 40 | 41 | /* check our args */ 42 | if (text == NULL) 43 | return NULL; 44 | 45 | /* Use 'buffer' to store the output. Work out how big it should be... 46 | * This must be a multiple of 4 bytes */ 47 | 48 | inlen = strlen( text ); 49 | /* check our arg...avoid a pesky FPE */ 50 | if (inlen == 0) 51 | { 52 | buffer = malloc(sizeof(char)); 53 | buffer[0] = '\0'; 54 | return buffer; 55 | } 56 | outlen = (inlen*4)/3; 57 | if( (inlen % 3) > 0 ) /* got to pad */ 58 | outlen += 4 - (inlen % 3); 59 | 60 | buffer = malloc( outlen + 1 ); /* +1 for the \0 */ 61 | memset(buffer, 0, outlen + 1); /* initialize to zero */ 62 | 63 | /* now do the main stage of conversion, 3 bytes at a time, 64 | * leave the trailing bytes (if there are any) for later */ 65 | 66 | for( point=buffer; inlen>=3; inlen-=3, text+=3 ) { 67 | *(point++) = b64_alphabet[ *text>>2 ]; 68 | *(point++) = b64_alphabet[ (*text<<4 & 0x30) | *(text+1)>>4 ]; 69 | *(point++) = b64_alphabet[ (*(text+1)<<2 & 0x3c) | *(text+2)>>6 ]; 70 | *(point++) = b64_alphabet[ *(text+2) & 0x3f ]; 71 | } 72 | 73 | /* Now deal with the trailing bytes */ 74 | if( inlen ) { 75 | /* We always have one trailing byte */ 76 | *(point++) = b64_alphabet[ *text>>2 ]; 77 | *(point++) = b64_alphabet[ (*text<<4 & 0x30) | 78 | (inlen==2?*(text+1)>>4:0) ]; 79 | *(point++) = (inlen==1?'=':b64_alphabet[ *(text+1)<<2 & 0x3c ] ); 80 | *(point++) = '='; 81 | } 82 | 83 | *point = '\0'; 84 | 85 | return buffer; 86 | } 87 | -------------------------------------------------------------------------------- /src/etcd-api/etcd-api2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Red Hat 3 | * All rights reserved. 4 | 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. Redistributions in binary 10 | * form must reproduce the above copyright notice, this list of conditions and 11 | * the following disclaimer in the documentation and/or other materials 12 | * provided with the distribution. 13 | 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | /* 28 | * Description of an etcd server. For now it just includes the name and 29 | * port, but some day it might include other stuff like SSL certificate 30 | * information. 31 | */ 32 | 33 | typedef struct { 34 | char *host; 35 | unsigned short port; 36 | } etcd_server; 37 | 38 | typedef void *etcd_session; 39 | 40 | /* 41 | * etcd_open 42 | * 43 | * Establish a session to an etcd cluster, with automatic reconnection and 44 | * so on. 45 | * 46 | * server_list 47 | * Array of etcd_server structures, with the last having host=NULL. The 48 | * caller is responsible for ensuring that this remains valid as long as 49 | * the session exists. 50 | */ 51 | etcd_session etcd_open(etcd_server *server_list); 52 | 53 | 54 | /* 55 | * etcd_open_str 56 | * 57 | * Same as etcd_open, except that the servers are specified as a list of 58 | * host:port strings, separated by comma/semicolon or whitespace. 59 | */ 60 | etcd_session etcd_open_str (char *server_names); 61 | 62 | 63 | /* 64 | * etcd_close 65 | * 66 | * Terminate a session, closing connections and freeing memory (or any other 67 | * resources) associated with it. 68 | */ 69 | void etcd_close (etcd_session session); 70 | 71 | 72 | /* 73 | * etcd_close 74 | * 75 | * Same as etcd_close, but also free the server list as etcd_open_str would 76 | * have allocated it. 77 | */ 78 | void etcd_close_str (etcd_session session); 79 | 80 | 81 | /* 82 | * etcd_get 83 | * 84 | * Fetch a key from one of the servers in a session. The return value is a 85 | * newly allocated string, which must be freed by the caller. 86 | * 87 | * key 88 | * The etcd key (path) to fetch. 89 | */ 90 | char * etcd_get (etcd_session session, char *key); -------------------------------------------------------------------------------- /src/ghttp/http_trans.h: -------------------------------------------------------------------------------- 1 | /* 2 | * http_trans.h -- This file contains definitions for http transport functions 3 | * Created: Christopher Blizzard , 5-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef HTTP_TRANS_H 23 | #define HTTP_TRANS_H 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | typedef enum http_trans_err_type_tag { 31 | http_trans_err_type_host = 0, 32 | http_trans_err_type_errno 33 | } http_trans_err_type; 34 | 35 | typedef struct http_trans_conn_tag { 36 | struct hostent *hostinfo; 37 | struct sockaddr_in saddr; 38 | char *host; 39 | char *proxy_host; 40 | int sock; 41 | short port; 42 | short proxy_port; 43 | http_trans_err_type error_type; 44 | int error; 45 | int sync; /* sync or async? */ 46 | char *io_buf; /* buffer */ 47 | int io_buf_len; /* how big is it? */ 48 | int io_buf_alloc; /* how much is used */ 49 | int io_buf_io_done; /* how much have we already moved? */ 50 | int io_buf_io_left; /* how much data do we have left? */ 51 | int io_buf_chunksize; /* how big should the chunks be that get 52 | read in and out be? */ 53 | int last_read; /* the size of the last read */ 54 | int chunk_len; /* length of a chunk. */ 55 | char *errstr; /* a hint as to an error */ 56 | } http_trans_conn; 57 | 58 | http_trans_conn * 59 | http_trans_conn_new(void); 60 | 61 | void 62 | http_trans_conn_destroy(http_trans_conn *a_conn); 63 | 64 | void 65 | http_trans_buf_reset(http_trans_conn *a_conn); 66 | 67 | void 68 | http_trans_buf_clip(http_trans_conn *a_conn, char *a_clip_to); 69 | 70 | int 71 | http_trans_connect(http_trans_conn *a_conn); 72 | 73 | const char * 74 | http_trans_get_host_error(int a_herror); 75 | 76 | int 77 | http_trans_append_data_to_buf(http_trans_conn *a_conn, 78 | char *a_data, 79 | int a_data_len); 80 | 81 | int 82 | http_trans_read_into_buf(http_trans_conn *a_conn); 83 | 84 | int 85 | http_trans_write_buf(http_trans_conn *a_conn); 86 | 87 | char * 88 | http_trans_buf_has_patt(char *a_buf, int a_len, 89 | char *a_pat, int a_patlen); 90 | 91 | #endif /* HTTP_TRANS_H */ 92 | -------------------------------------------------------------------------------- /src/ghttp/ghttp_constants.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ghttp_constants.h -- definitions for char constants that people 3 | * might want to use 4 | * Created: Christopher Blizzard 5 | * 6 | * Copyright (C) 1998 Free Software Foundation 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Library General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Library General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Library General Public 19 | * License along with this library; if not, write to the Free 20 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 | */ 22 | 23 | #ifndef GHTTP_CONSTANTS_H 24 | #define GHTTP_CONSTANTS_H 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif /* __cplusplus */ 29 | 30 | extern const char http_hdr_Allow[]; 31 | extern const char http_hdr_Content_Encoding[]; 32 | extern const char http_hdr_Content_Language[]; 33 | extern const char http_hdr_Content_Length[]; 34 | extern const char http_hdr_Content_Location[]; 35 | extern const char http_hdr_Content_MD5[]; 36 | extern const char http_hdr_Content_Range[]; 37 | extern const char http_hdr_Content_Type[]; 38 | extern const char http_hdr_Expires[]; 39 | extern const char http_hdr_Last_Modified[]; 40 | 41 | /* general headers */ 42 | 43 | extern const char http_hdr_Cache_Control[]; 44 | extern const char http_hdr_Connection[]; 45 | extern const char http_hdr_Date[]; 46 | extern const char http_hdr_Pragma[]; 47 | extern const char http_hdr_Transfer_Encoding[]; 48 | extern const char http_hdr_Update[]; 49 | extern const char http_hdr_Trailer[]; 50 | extern const char http_hdr_Via[]; 51 | 52 | /* request headers */ 53 | 54 | extern const char http_hdr_Accept[]; 55 | extern const char http_hdr_Accept_Charset[]; 56 | extern const char http_hdr_Accept_Encoding[]; 57 | extern const char http_hdr_Accept_Language[]; 58 | extern const char http_hdr_Authorization[]; 59 | extern const char http_hdr_Expect[]; 60 | extern const char http_hdr_From[]; 61 | extern const char http_hdr_Host[]; 62 | extern const char http_hdr_If_Modified_Since[]; 63 | extern const char http_hdr_If_Match[]; 64 | extern const char http_hdr_If_None_Match[]; 65 | extern const char http_hdr_If_Range[]; 66 | extern const char http_hdr_If_Unmodified_Since[]; 67 | extern const char http_hdr_Max_Forwards[]; 68 | extern const char http_hdr_Proxy_Authorization[]; 69 | extern const char http_hdr_Range[]; 70 | extern const char http_hdr_Referrer[]; 71 | extern const char http_hdr_TE[]; 72 | extern const char http_hdr_User_Agent[]; 73 | 74 | /* response headers */ 75 | 76 | extern const char http_hdr_Accept_Ranges[]; 77 | extern const char http_hdr_Age[]; 78 | extern const char http_hdr_ETag[]; 79 | extern const char http_hdr_Location[]; 80 | extern const char http_hdr_Retry_After[]; 81 | extern const char http_hdr_Server[]; 82 | extern const char http_hdr_Vary[]; 83 | extern const char http_hdr_Warning[]; 84 | extern const char http_hdr_WWW_Authenticate[]; 85 | 86 | /* Other headers */ 87 | 88 | extern const char http_hdr_Set_Cookie[]; 89 | 90 | /* WebDAV headers */ 91 | 92 | extern const char http_hdr_DAV[]; 93 | extern const char http_hdr_Depth[]; 94 | extern const char http_hdr_Destination[]; 95 | extern const char http_hdr_If[]; 96 | extern const char http_hdr_Lock_Token[]; 97 | extern const char http_hdr_Overwrite[]; 98 | extern const char http_hdr_Status_URI[]; 99 | extern const char http_hdr_Timeout[]; 100 | 101 | #ifdef __cplusplus 102 | } 103 | #endif /* __cplusplus */ 104 | 105 | #endif /* GHTTP_CONSTANTS_H */ 106 | -------------------------------------------------------------------------------- /src/ghttp/http_uri.c: -------------------------------------------------------------------------------- 1 | /* 2 | * http_uri.c --- Contains functions to parse uri's 3 | * Created: Christopher Blizzard , 4-Jul-98 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "http_uri.h" 26 | 27 | typedef enum uri_parse_state_tag 28 | { 29 | parse_state_read_host = 0, 30 | parse_state_read_port, 31 | parse_state_read_resource 32 | } uri_parse_state; 33 | 34 | 35 | int 36 | http_uri_parse(char *a_string, 37 | http_uri *a_uri) 38 | { 39 | /* Everyone chant... "we love state machines..." */ 40 | uri_parse_state l_state = parse_state_read_host; 41 | char *l_start_string = NULL; 42 | char *l_end_string = NULL; 43 | char l_temp_port[6]; 44 | 45 | /* init the array */ 46 | memset(l_temp_port, 0, 6); 47 | /* check the parameters */ 48 | if (a_string == NULL) 49 | goto ec; 50 | if (a_uri) { 51 | a_uri->full = strdup(a_string); 52 | } 53 | l_start_string = strchr(a_string, ':'); 54 | /* check to make sure that there was a : in the string */ 55 | if (!l_start_string) 56 | goto ec; 57 | if (a_uri) { 58 | a_uri->proto = (char *)malloc(l_start_string - a_string + 1); 59 | memcpy(a_uri->proto, a_string, (l_start_string - a_string)); 60 | a_uri->proto[l_start_string - a_string] = '\0'; 61 | } 62 | /* check to make sure it starts with "http://" */ 63 | if (strncmp(l_start_string, "://", 3) != 0) 64 | goto ec; 65 | /* start at the beginning of the string */ 66 | l_start_string = l_end_string = &l_start_string[3]; 67 | while(*l_end_string) 68 | { 69 | if (l_state == parse_state_read_host) 70 | { 71 | if (*l_end_string == ':') 72 | { 73 | l_state = parse_state_read_port; 74 | if ((l_end_string - l_start_string) == 0) 75 | goto ec; 76 | /* allocate space */ 77 | if ((l_end_string - l_start_string) == 0) 78 | goto ec; 79 | /* only do this if a uri was passed in */ 80 | if (a_uri) 81 | { 82 | a_uri->host = (char *)malloc(l_end_string - l_start_string + 1); 83 | /* copy the data */ 84 | memcpy(a_uri->host, l_start_string, (l_end_string - l_start_string)); 85 | /* terminate */ 86 | a_uri->host[l_end_string - l_start_string] = '\0'; 87 | } 88 | /* reset the counters */ 89 | l_end_string++; 90 | l_start_string = l_end_string; 91 | continue; 92 | } 93 | else if (*l_end_string == '/') 94 | { 95 | l_state = parse_state_read_resource; 96 | if ((l_end_string - l_start_string) == 0) 97 | goto ec; 98 | if (a_uri) 99 | { 100 | a_uri->host = (char *)malloc(l_end_string - l_start_string + 1); 101 | memcpy(a_uri->host, l_start_string, (l_end_string - l_start_string)); 102 | a_uri->host[l_end_string - l_start_string] = '\0'; 103 | } 104 | l_start_string = l_end_string; 105 | continue; 106 | } 107 | } 108 | else if (l_state == parse_state_read_port) 109 | { 110 | if (*l_end_string == '/') 111 | { 112 | l_state = parse_state_read_resource; 113 | /* check to make sure we're not going to overflow */ 114 | if (l_end_string - l_start_string > 5) 115 | goto ec; 116 | /* check to make sure there was a port */ 117 | if ((l_end_string - l_start_string) == 0) 118 | goto ec; 119 | /* copy the port into a temp buffer */ 120 | memcpy(l_temp_port, l_start_string, l_end_string - l_start_string); 121 | /* convert it. */ 122 | if (a_uri) 123 | a_uri->port = atoi(l_temp_port); 124 | l_start_string = l_end_string; 125 | continue; 126 | } 127 | else if (isdigit(*l_end_string) == 0) 128 | { 129 | /* check to make sure they are just digits */ 130 | goto ec; 131 | } 132 | } 133 | /* next.. */ 134 | l_end_string++; 135 | continue; 136 | } 137 | 138 | if (l_state == parse_state_read_host) 139 | { 140 | if ((l_end_string - l_start_string) == 0) 141 | goto ec; 142 | if (a_uri) 143 | { 144 | a_uri->host = (char *)malloc(l_end_string - l_start_string + 1); 145 | memcpy(a_uri->host, l_start_string, (l_end_string - l_start_string)); 146 | a_uri->host[l_end_string - l_start_string] = '\0'; 147 | /* for a "/" */ 148 | a_uri->resource = strdup("/"); 149 | } 150 | } 151 | else if (l_state == parse_state_read_port) 152 | { 153 | if (strlen(l_start_string) == 0) 154 | /* oops. that's not a valid number */ 155 | goto ec; 156 | if (a_uri) 157 | { 158 | a_uri->port = atoi(l_start_string); 159 | a_uri->resource = strdup("/"); 160 | } 161 | } 162 | else if (l_state == parse_state_read_resource) 163 | { 164 | if (strlen(l_start_string) == 0) 165 | { 166 | if (a_uri) 167 | a_uri->resource = strdup("/"); 168 | } 169 | else 170 | { 171 | if (a_uri) 172 | a_uri->resource = strdup(l_start_string); 173 | } 174 | } 175 | else 176 | { 177 | /* uhh...how did we get here? */ 178 | goto ec; 179 | } 180 | return 0; 181 | 182 | ec: 183 | return -1; 184 | } 185 | 186 | http_uri * 187 | http_uri_new(void) 188 | { 189 | http_uri *l_return = NULL; 190 | 191 | l_return = (http_uri *)malloc(sizeof(http_uri)); 192 | l_return->full = NULL; 193 | l_return->proto = NULL; 194 | l_return->host = NULL; 195 | l_return->port = 80; 196 | l_return->resource = NULL; 197 | return l_return; 198 | } 199 | 200 | void 201 | http_uri_destroy(http_uri *a_uri) 202 | { 203 | if (a_uri->full) { 204 | free(a_uri->full); 205 | a_uri->full = NULL; 206 | } 207 | if (a_uri->proto) { 208 | free(a_uri->proto); 209 | a_uri->proto = NULL; 210 | } 211 | if (a_uri->host) { 212 | free(a_uri->host); 213 | a_uri->host = NULL; 214 | } 215 | if (a_uri->resource) { 216 | free(a_uri->resource); 217 | a_uri->resource = NULL; 218 | } 219 | free(a_uri); 220 | } 221 | 222 | 223 | -------------------------------------------------------------------------------- /src/ghttp/ghttp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ghttp.h -- A public interface to common http functions 3 | * Created: Christopher Blizzard , 21-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #ifndef GHTTP_H 23 | #define GHTTP_H 24 | 25 | #include "ghttp_constants.h" 26 | #include 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif /* __cplusplus */ 31 | 32 | typedef struct _ghttp_request ghttp_request; 33 | 34 | typedef enum ghttp_type_tag 35 | { 36 | ghttp_type_get = 0, 37 | ghttp_type_options, 38 | ghttp_type_head, 39 | ghttp_type_post, 40 | ghttp_type_put, 41 | ghttp_type_delete, 42 | ghttp_type_trace, 43 | ghttp_type_connect, 44 | ghttp_type_propfind, 45 | ghttp_type_proppatch, 46 | ghttp_type_mkcol, 47 | ghttp_type_copy, 48 | ghttp_type_move, 49 | ghttp_type_lock, 50 | ghttp_type_unlock 51 | } ghttp_type; 52 | 53 | typedef enum ghttp_sync_mode_tag 54 | { 55 | ghttp_sync = 0, 56 | ghttp_async 57 | } ghttp_sync_mode; 58 | 59 | typedef enum ghttp_status_tag 60 | { 61 | ghttp_error = -1, 62 | ghttp_not_done, 63 | ghttp_done 64 | } ghttp_status; 65 | 66 | typedef enum ghttp_proc_tag 67 | { 68 | ghttp_proc_none = 0, 69 | ghttp_proc_request, 70 | ghttp_proc_response_hdrs, 71 | ghttp_proc_response 72 | } ghttp_proc; 73 | 74 | typedef struct ghttp_current_status_tag 75 | { 76 | ghttp_proc proc; /* what's it doing? */ 77 | int bytes_read; /* how many bytes have been read? */ 78 | int bytes_total; /* how many total */ 79 | } ghttp_current_status; 80 | 81 | /* create a new request object */ 82 | ghttp_request * 83 | ghttp_request_new(void); 84 | 85 | /* delete a current request object */ 86 | void 87 | ghttp_request_destroy(ghttp_request *a_request); 88 | 89 | /* Validate a uri 90 | * This will return -1 if a uri is invalid 91 | */ 92 | int 93 | ghttp_uri_validate(char *a_uri); 94 | 95 | /* Set a uri in a request 96 | * This will return -1 if the uri is invalid 97 | */ 98 | 99 | int 100 | ghttp_set_uri(ghttp_request *a_request, char *a_uri); 101 | 102 | /* Set a proxy for a request 103 | * This will return -1 if the uri is invalid 104 | */ 105 | 106 | int 107 | ghttp_set_proxy(ghttp_request *a_request, char *a_uri); 108 | 109 | /* Set a request type 110 | * This will return -1 if the request type is invalid or 111 | * unsupported 112 | */ 113 | 114 | int 115 | ghttp_set_type(ghttp_request *a_request, ghttp_type a_type); 116 | 117 | /* Set the body. 118 | * This will return -1 if the request type doesn't support it 119 | */ 120 | 121 | int 122 | ghttp_set_body(ghttp_request *a_request, char *a_body, int a_len); 123 | 124 | /* Set whether or not you want to use sync or async mode. 125 | */ 126 | 127 | int 128 | ghttp_set_sync(ghttp_request *a_request, 129 | ghttp_sync_mode a_mode); 130 | 131 | /* Prepare a request. 132 | * Call this before trying to process a request or if you change the 133 | * uri. 134 | */ 135 | 136 | int 137 | ghttp_prepare(ghttp_request *a_request); 138 | 139 | /* Set the chunk size 140 | * You might want to do this to optimize for different connection speeds. 141 | */ 142 | 143 | void 144 | ghttp_set_chunksize(ghttp_request *a_request, int a_size); 145 | 146 | /* Set a random request header 147 | */ 148 | 149 | void 150 | ghttp_set_header(ghttp_request *a_request, 151 | const char *a_hdr, const char *a_val); 152 | 153 | /* Process a request 154 | */ 155 | 156 | ghttp_status 157 | ghttp_process(ghttp_request *a_request); 158 | 159 | /* Get the status of a request 160 | */ 161 | 162 | ghttp_current_status 163 | ghttp_get_status(ghttp_request *a_request); 164 | 165 | /* Flush the received data (so far) into the response body. This is 166 | * useful for asynchronous requests with large responses: you can 167 | * periodically flush the response buffer and parse the data that's 168 | * arrived so far. 169 | */ 170 | 171 | void 172 | ghttp_flush_response_buffer(ghttp_request *a_request); 173 | 174 | /* Get the value of a random response header 175 | */ 176 | 177 | const char * 178 | ghttp_get_header(ghttp_request *a_request, 179 | const char *a_hdr); 180 | 181 | /* Get the list of headers that were returned in the response. You 182 | must free the returned string values. This function will return 0 183 | on success, -1 on some kind of error. */ 184 | int 185 | ghttp_get_header_names(ghttp_request *a_request, 186 | char ***a_hdrs, int *a_num_hdrs); 187 | 188 | /* Abort a currently running request. */ 189 | int 190 | ghttp_close(ghttp_request *a_request); 191 | 192 | /* Clean a request 193 | */ 194 | void 195 | ghttp_clean(ghttp_request *a_request); 196 | 197 | /* Get the socket associated with a particular connection 198 | */ 199 | 200 | int 201 | ghttp_get_socket(ghttp_request *a_request); 202 | 203 | /* get the return entity body 204 | */ 205 | 206 | char * 207 | ghttp_get_body(ghttp_request *a_request); 208 | 209 | /* get the returned length 210 | */ 211 | 212 | int 213 | ghttp_get_body_len(ghttp_request *a_request); 214 | 215 | /* Get an error message for a request that has failed. 216 | */ 217 | 218 | const char * 219 | ghttp_get_error(ghttp_request *a_request); 220 | 221 | /* Parse a date string that is one of the standard 222 | * date formats 223 | */ 224 | 225 | time_t 226 | ghttp_parse_date(char *a_date); 227 | 228 | /* Return the status code. 229 | */ 230 | 231 | int 232 | ghttp_status_code(ghttp_request *a_request); 233 | 234 | /* Return the reason phrase. 235 | */ 236 | 237 | const char * 238 | ghttp_reason_phrase(ghttp_request *a_request); 239 | 240 | /* Set your username/password pair 241 | */ 242 | 243 | int 244 | ghttp_set_authinfo(ghttp_request *a_request, 245 | const char *a_user, 246 | const char *a_pass); 247 | 248 | 249 | /* Set your username/password pair for proxy 250 | */ 251 | 252 | int 253 | ghttp_set_proxy_authinfo(ghttp_request *a_request, 254 | const char *a_user, 255 | const char *a_pass); 256 | 257 | 258 | #ifdef __cplusplus 259 | } 260 | #endif /* __cplusplus */ 261 | 262 | 263 | #endif /* GHTTP_H */ 264 | -------------------------------------------------------------------------------- /src/ghttp/http_req.c: -------------------------------------------------------------------------------- 1 | /* 2 | * http_req.c -- Functions for making http requests 3 | * Created: Christopher Blizzard , 6-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #include 23 | #include 24 | #include "http_req.h" 25 | #include "http_trans.h" 26 | #include "http_global.h" 27 | 28 | const char * 29 | http_req_type_char[] = { 30 | "GET", 31 | "OPTIONS", 32 | "HEAD", 33 | "POST", 34 | "PUT", 35 | "DELETE", 36 | "TRACE", 37 | "CONNECT", 38 | "PROPFIND", 39 | "PROPPATCH", 40 | "MKCOL", 41 | "COPY", 42 | "MOVE", 43 | "LOCK", 44 | "UNLOCK", 45 | NULL 46 | }; 47 | 48 | http_req * 49 | http_req_new(void) 50 | { 51 | http_req *l_return = NULL; 52 | 53 | l_return = (http_req *)malloc(sizeof(http_req)); 54 | memset(l_return, 0, sizeof(http_req)); 55 | /* default to 1.1 */ 56 | l_return->http_ver = 1.1; 57 | l_return->headers = http_hdr_list_new(); 58 | return l_return; 59 | } 60 | 61 | void 62 | http_req_destroy(http_req *a_req) 63 | { 64 | if (!a_req) 65 | return; 66 | if (a_req->headers) 67 | http_hdr_list_destroy(a_req->headers); 68 | free(a_req); 69 | } 70 | 71 | int 72 | http_req_prepare(http_req *a_req) 73 | { 74 | int l_return = 0; 75 | char l_buf[30]; 76 | 77 | if (!a_req) 78 | return -1; 79 | memset(l_buf, 0, 30); 80 | /* set the host header */ 81 | http_hdr_set_value(a_req->headers, 82 | http_hdr_Host, 83 | a_req->host); 84 | /* check to see if we have an entity body */ 85 | if ((a_req->type == http_req_type_post) || 86 | (a_req->type == http_req_type_put) || 87 | (a_req->type == http_req_type_trace)) 88 | { 89 | sprintf(l_buf, "%d", a_req->body_len); 90 | http_hdr_set_value(a_req->headers, 91 | http_hdr_Content_Length, 92 | l_buf); 93 | } 94 | /* if the user agent isn't set then set a default */ 95 | if (http_hdr_get_value(a_req->headers, http_hdr_User_Agent) == NULL) 96 | http_hdr_set_value(a_req->headers, http_hdr_User_Agent, 97 | "libghttp/1.0"); 98 | return l_return; 99 | } 100 | 101 | int 102 | http_req_send(http_req *a_req, http_trans_conn *a_conn) 103 | { 104 | char *l_request = NULL; 105 | int l_request_len = 0; 106 | int i = 0; 107 | int l_len = 0; 108 | int l_headers_len = 0; 109 | int l_rv = 0; 110 | char *l_content = NULL; 111 | 112 | /* see if we need to jump into the function somewhere */ 113 | if (a_conn->sync == HTTP_TRANS_ASYNC) 114 | { 115 | if (a_req->state == http_req_state_sending_request) 116 | goto http_req_state_sending_request_jump; 117 | if (a_req->state == http_req_state_sending_headers) 118 | goto http_req_state_sending_headers_jump; 119 | if (a_req->state == http_req_state_sending_body) 120 | goto http_req_state_sending_body_jump; 121 | } 122 | /* enough for the request and the other little headers */ 123 | l_request = malloc(30 + strlen(a_req->resource) + (a_conn->proxy_host ? 124 | (strlen(a_req->host) + 20) : 0)); 125 | memset(l_request, 0, 30 + strlen(a_req->resource) + (a_conn->proxy_host ? 126 | (strlen(a_req->host) + 20) : 0)); 127 | /* copy it into the buffer */ 128 | if (a_conn->proxy_host) 129 | { 130 | l_request_len = sprintf(l_request, 131 | "%s %s HTTP/%01.1f\r\n", 132 | http_req_type_char[a_req->type], 133 | a_req->full_uri, 134 | a_req->http_ver); 135 | } 136 | else 137 | { 138 | l_request_len = sprintf(l_request, 139 | "%s %s HTTP/%01.1f\r\n", 140 | http_req_type_char[a_req->type], 141 | a_req->resource, 142 | a_req->http_ver); 143 | } 144 | /* set the request in the connection buffer */ 145 | http_trans_append_data_to_buf(a_conn, l_request, l_request_len); 146 | /* free up the request - we don't need it anymore */ 147 | free(l_request); 148 | l_request = NULL; 149 | /* set the state */ 150 | a_req->state = http_req_state_sending_request; 151 | http_req_state_sending_request_jump: 152 | /* send the request */ 153 | do { 154 | l_rv = http_trans_write_buf(a_conn); 155 | if ((a_conn->sync == HTTP_TRANS_ASYNC) && (l_rv == HTTP_TRANS_NOT_DONE)) 156 | return HTTP_TRANS_NOT_DONE; 157 | if ((l_rv == HTTP_TRANS_DONE) && (a_conn->last_read == 0)) 158 | return HTTP_TRANS_ERR; 159 | } while (l_rv == HTTP_TRANS_NOT_DONE); 160 | /* reset the buffer */ 161 | http_trans_buf_reset(a_conn); 162 | /* set up all of the headers */ 163 | for (i = 0; i < HTTP_HDRS_MAX; i++) 164 | { 165 | l_len = 0; 166 | if (a_req->headers->header[i]) 167 | { 168 | l_len = strlen(a_req->headers->header[i]); 169 | if (l_len > 0) 170 | { 171 | http_trans_append_data_to_buf(a_conn, a_req->headers->header[i], l_len); 172 | l_headers_len += l_len; 173 | http_trans_append_data_to_buf(a_conn, ": ", 2); 174 | l_headers_len += 2; 175 | /* note, it's ok to have no value for a request */ 176 | if ((l_len = strlen(a_req->headers->value[i])) > 0) 177 | { 178 | http_trans_append_data_to_buf(a_conn, a_req->headers->value[i], l_len); 179 | l_headers_len += l_len; 180 | } 181 | http_trans_append_data_to_buf(a_conn, "\r\n", 2); 182 | l_headers_len += 2; 183 | } 184 | } 185 | } 186 | http_trans_append_data_to_buf(a_conn, "\r\n", 2); 187 | l_headers_len += 2; 188 | /* set the state */ 189 | a_req->state = http_req_state_sending_headers; 190 | http_req_state_sending_headers_jump: 191 | /* blast that out to the network */ 192 | do { 193 | l_rv = http_trans_write_buf(a_conn); 194 | if ((a_conn->sync == HTTP_TRANS_ASYNC) && (l_rv == HTTP_TRANS_NOT_DONE)) 195 | return HTTP_TRANS_NOT_DONE; 196 | if ((l_rv == HTTP_TRANS_DONE) && (a_conn->last_read == 0)) 197 | return HTTP_TRANS_ERR; 198 | } while (l_rv == HTTP_TRANS_NOT_DONE); 199 | /* reset the buffer */ 200 | http_trans_buf_reset(a_conn); 201 | l_content = http_hdr_get_value(a_req->headers, http_hdr_Content_Length); 202 | if (l_content) 203 | { 204 | /* append the information to the buffer */ 205 | http_trans_append_data_to_buf(a_conn, a_req->body, a_req->body_len); 206 | a_req->state = http_req_state_sending_body; 207 | http_req_state_sending_body_jump: 208 | do { 209 | l_rv = http_trans_write_buf(a_conn); 210 | if ((a_conn->sync == HTTP_TRANS_ASYNC) && (l_rv == HTTP_TRANS_NOT_DONE)) 211 | return HTTP_TRANS_NOT_DONE; 212 | if ((l_rv == HTTP_TRANS_DONE) && (a_conn->last_read == 0)) 213 | return HTTP_TRANS_ERR; 214 | } while (l_rv == HTTP_TRANS_NOT_DONE); 215 | /* reset the buffer */ 216 | http_trans_buf_reset(a_conn); 217 | } 218 | return HTTP_TRANS_DONE; 219 | } 220 | -------------------------------------------------------------------------------- /src/ghttp/http_trans.c: -------------------------------------------------------------------------------- 1 | /* 2 | * http_trans.c -- Functions for doing transport related stuff including 3 | * automatically extending buffers and whatnot. 4 | * Created: Christopher Blizzard , 5-Aug-1998 5 | * 6 | * Copyright (C) 1998 Free Software Foundation 7 | * 8 | * This library is free software; you can redistribute it and/or 9 | * modify it under the terms of the GNU Library General Public 10 | * License as published by the Free Software Foundation; either 11 | * version 2 of the License, or (at your option) any later version. 12 | * 13 | * This library is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 | * Library General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU Library General Public 19 | * License along with this library; if not, write to the Free 20 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "http_trans.h" 30 | #include "http_global.h" 31 | 32 | static int 33 | http_trans_buf_free(http_trans_conn *a_conn); 34 | 35 | int 36 | http_trans_connect(http_trans_conn *a_conn) 37 | { 38 | if ((a_conn == NULL) || (a_conn->host == NULL)) 39 | goto ec; 40 | if (a_conn->hostinfo == NULL) 41 | { 42 | /* look up the name of the proxy if it's there. */ 43 | if (a_conn->proxy_host) 44 | { 45 | if ((a_conn->hostinfo = gethostbyname(a_conn->proxy_host)) == NULL) 46 | { 47 | a_conn->error_type = http_trans_err_type_host; 48 | a_conn->error = h_errno; 49 | goto ec; 50 | } 51 | } 52 | else 53 | { 54 | /* look up the name */ 55 | if ((a_conn->hostinfo = gethostbyname(a_conn->host)) == NULL) 56 | { 57 | a_conn->error_type = http_trans_err_type_host; 58 | a_conn->error = h_errno; 59 | goto ec; 60 | } 61 | } 62 | /* set up the saddr */ 63 | a_conn->saddr.sin_family = AF_INET; 64 | /* set the proxy port */ 65 | if (a_conn->proxy_host) 66 | a_conn->saddr.sin_port = htons(a_conn->proxy_port); 67 | else 68 | a_conn->saddr.sin_port = htons(a_conn->port); 69 | /* copy the name info */ 70 | memcpy(&a_conn->saddr.sin_addr.s_addr, 71 | a_conn->hostinfo->h_addr_list[0], 72 | sizeof(unsigned long)); 73 | } 74 | /* set up the socket */ 75 | if ((a_conn->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 76 | { 77 | a_conn->error_type = http_trans_err_type_errno; 78 | a_conn->error = errno; 79 | goto ec; 80 | } 81 | /* set up the socket */ 82 | if (connect(a_conn->sock, 83 | (struct sockaddr *)&a_conn->saddr, 84 | sizeof(struct sockaddr)) < 0) 85 | { 86 | a_conn->error_type = http_trans_err_type_errno; 87 | a_conn->error = errno; 88 | goto ec; 89 | } 90 | 91 | return 0; 92 | ec: 93 | return -1; 94 | } 95 | 96 | http_trans_conn * 97 | http_trans_conn_new(void) 98 | { 99 | http_trans_conn *l_return = NULL; 100 | 101 | /* allocate a new connection struct */ 102 | l_return = (http_trans_conn *)malloc(sizeof(http_trans_conn)); 103 | memset(l_return, 0, sizeof(http_trans_conn)); 104 | /* default to 80 */ 105 | l_return->port = 80; 106 | /* default to 1000 bytes at a time */ 107 | l_return->io_buf_chunksize = 1024; 108 | /* allocate a new trans buffer */ 109 | l_return->io_buf = malloc(l_return->io_buf_chunksize); 110 | memset(l_return->io_buf, 0, l_return->io_buf_chunksize); 111 | l_return->io_buf_len = l_return->io_buf_chunksize; 112 | /* make sure the socket looks like it's closed */ 113 | l_return->sock = -1; 114 | return l_return; 115 | } 116 | 117 | void 118 | http_trans_conn_destroy(http_trans_conn *a_conn) 119 | { 120 | /* destroy the connection structure. */ 121 | if (a_conn == NULL) 122 | return; 123 | if (a_conn->io_buf) 124 | free(a_conn->io_buf); 125 | if (a_conn->sock != -1) 126 | close(a_conn->sock); 127 | free(a_conn); 128 | return; 129 | } 130 | 131 | const char * 132 | http_trans_get_host_error(int a_herror) 133 | { 134 | switch (a_herror) 135 | { 136 | case HOST_NOT_FOUND: 137 | return "Host not found"; 138 | case NO_ADDRESS: 139 | return "An address is not associated with that host"; 140 | case NO_RECOVERY: 141 | return "An unrecoverable name server error occured"; 142 | case TRY_AGAIN: 143 | return "A temporary error occurred on an authoritative name server. Please try again later."; 144 | default: 145 | return "No error or error not known."; 146 | } 147 | } 148 | 149 | int 150 | http_trans_append_data_to_buf(http_trans_conn *a_conn, 151 | char *a_data, 152 | int a_data_len) 153 | { 154 | if (http_trans_buf_free(a_conn) < a_data_len) 155 | { 156 | a_conn->io_buf = realloc(a_conn->io_buf, a_conn->io_buf_len + a_data_len); 157 | a_conn->io_buf_len += a_data_len; 158 | } 159 | memcpy(&a_conn->io_buf[a_conn->io_buf_alloc], a_data, a_data_len); 160 | a_conn->io_buf_alloc += a_data_len; 161 | return 1; 162 | } 163 | 164 | int 165 | http_trans_read_into_buf(http_trans_conn *a_conn) 166 | { 167 | int l_read = 0; 168 | int l_bytes_to_read = 0; 169 | 170 | /* set the length if this is the first time */ 171 | if (a_conn->io_buf_io_left == 0) 172 | { 173 | a_conn->io_buf_io_left = a_conn->io_buf_chunksize; 174 | a_conn->io_buf_io_done = 0; 175 | } 176 | /* make sure there's enough space */ 177 | if (http_trans_buf_free(a_conn) < a_conn->io_buf_io_left) 178 | { 179 | a_conn->io_buf = realloc(a_conn->io_buf, 180 | a_conn->io_buf_len + a_conn->io_buf_io_left); 181 | a_conn->io_buf_len += a_conn->io_buf_io_left; 182 | } 183 | /* check to see how much we should try to read */ 184 | if (a_conn->io_buf_io_left > a_conn->io_buf_chunksize) 185 | l_bytes_to_read = a_conn->io_buf_chunksize; 186 | else 187 | l_bytes_to_read = a_conn->io_buf_io_left; 188 | /* read in some data */ 189 | if ((a_conn->last_read = l_read = read(a_conn->sock, 190 | &a_conn->io_buf[a_conn->io_buf_alloc], 191 | l_bytes_to_read)) < 0) 192 | { 193 | if (errno == EINTR) 194 | l_read = 0; 195 | else 196 | return HTTP_TRANS_ERR; 197 | } 198 | else if (l_read == 0) 199 | return HTTP_TRANS_DONE; 200 | /* mark the buffer */ 201 | a_conn->io_buf_io_left -= l_read; 202 | a_conn->io_buf_io_done += l_read; 203 | a_conn->io_buf_alloc += l_read; 204 | /* generate the result */ 205 | if (a_conn->io_buf_io_left == 0) 206 | return HTTP_TRANS_DONE; 207 | return HTTP_TRANS_NOT_DONE; 208 | } 209 | 210 | int 211 | http_trans_write_buf(http_trans_conn *a_conn) 212 | { 213 | int l_written = 0; 214 | 215 | if (a_conn->io_buf_io_left == 0) 216 | { 217 | a_conn->io_buf_io_left = a_conn->io_buf_alloc; 218 | a_conn->io_buf_io_done = 0; 219 | } 220 | /* write out some data */ 221 | if ((a_conn->last_read = l_written = write (a_conn->sock, 222 | &a_conn->io_buf[a_conn->io_buf_io_done], 223 | a_conn->io_buf_io_left)) <= 0) 224 | { 225 | if (errno == EINTR) 226 | l_written = 0; 227 | else 228 | return HTTP_TRANS_ERR; 229 | } 230 | if (l_written == 0) 231 | return HTTP_TRANS_DONE; 232 | /* advance the counters */ 233 | a_conn->io_buf_io_left -= l_written; 234 | a_conn->io_buf_io_done += l_written; 235 | if (a_conn->io_buf_io_left == 0) 236 | return HTTP_TRANS_DONE; 237 | return HTTP_TRANS_NOT_DONE; 238 | } 239 | 240 | void 241 | http_trans_buf_reset(http_trans_conn *a_conn) 242 | { 243 | if (a_conn->io_buf) 244 | free(a_conn->io_buf); 245 | a_conn->io_buf = malloc(a_conn->io_buf_chunksize); 246 | memset(a_conn->io_buf, 0, a_conn->io_buf_chunksize); 247 | a_conn->io_buf_len = a_conn->io_buf_chunksize; 248 | a_conn->io_buf_alloc = 0; 249 | a_conn->io_buf_io_done = 0; 250 | a_conn->io_buf_io_left = 0; 251 | } 252 | 253 | void 254 | http_trans_buf_clip(http_trans_conn *a_conn, char *a_clip_to) 255 | { 256 | int l_bytes = 0; 257 | 258 | /* get the number of bytes to clip off of the front */ 259 | l_bytes = a_clip_to - a_conn->io_buf; 260 | if (l_bytes > 0) 261 | { 262 | memmove(a_conn->io_buf, a_clip_to, a_conn->io_buf_alloc - l_bytes); 263 | a_conn->io_buf_alloc -= l_bytes; 264 | } 265 | a_conn->io_buf_io_done = 0; 266 | a_conn->io_buf_io_left = 0; 267 | } 268 | 269 | char * 270 | http_trans_buf_has_patt(char *a_buf, int a_len, 271 | char *a_pat, int a_patlen) 272 | { 273 | int i = 0; 274 | for ( ; i <= ( a_len - a_patlen ); i++ ) 275 | { 276 | if (a_buf[i] == a_pat[0]) 277 | { 278 | if (memcmp(&a_buf[i], a_pat, a_patlen) == 0) 279 | return &a_buf[i]; 280 | } 281 | } 282 | return NULL; 283 | } 284 | 285 | /* static functions */ 286 | 287 | static int 288 | http_trans_buf_free(http_trans_conn *a_conn) 289 | { 290 | return (a_conn->io_buf_len - a_conn->io_buf_alloc); 291 | } 292 | -------------------------------------------------------------------------------- /src/ghttp/http_date.c: -------------------------------------------------------------------------------- 1 | /* 2 | * http_date.c -- Routines for parsing and generating http dates 3 | * Created: Christopher Blizzard , 16-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #include 23 | #include 24 | #include "http_date.h" 25 | 26 | static int 27 | month_from_string_short(const char *a_month); 28 | 29 | /* 30 | * date formats come in one of the following three flavors according to 31 | * rfc 2068 and later drafts: 32 | * 33 | * Sun, 06 Nov 1994 08:49:37 GMT ; RFC 822, updated by RFC 1123 34 | * Sunday, 06-Nov-1994, 08:49:37 GMT ; RFC 850, updated by RFC 1036 35 | * Sun Nov 6 08:49:37 1994 ; ANSI C's asctime() format 36 | * 37 | * the first format is preferred however all must be supported. 38 | */ 39 | 40 | 41 | time_t 42 | http_date_to_time(const char *a_date) 43 | { 44 | struct tm l_tm_time; 45 | time_t l_return = 0; 46 | char l_buf[12]; 47 | const char *l_start_date = NULL; 48 | int i = 0; 49 | 50 | /* make sure we can use it */ 51 | if (!a_date) 52 | return -1; 53 | memset(&l_tm_time, 0, sizeof(struct tm)); 54 | memset(l_buf, 0, 12); 55 | /* try to figure out which format it's in */ 56 | /* rfc 1123 */ 57 | if (a_date[3] == ',') 58 | { 59 | if (strlen(a_date) != 29) 60 | return -1; 61 | /* make sure that everything is legal */ 62 | if (a_date[4] != ' ') 63 | return -1; 64 | /* 06 */ 65 | if ((isdigit(a_date[5]) == 0) || 66 | (isdigit(a_date[6]) == 0)) 67 | return -1; 68 | /* Nov */ 69 | if ((l_tm_time.tm_mon = month_from_string_short(&a_date[8])) < 0) 70 | return -1; 71 | /* 1994 */ 72 | if ((isdigit(a_date[12]) == 0) || 73 | (isdigit(a_date[13]) == 0) || 74 | (isdigit(a_date[14]) == 0) || 75 | (isdigit(a_date[15]) == 0)) 76 | return -1; 77 | if (a_date[16] != ' ') 78 | return -1; 79 | /* 08:49:37 */ 80 | if ((isdigit(a_date[17]) == 0) || 81 | (isdigit(a_date[18]) == 0) || 82 | (a_date[19] != ':') || 83 | (isdigit(a_date[20]) == 0) || 84 | (isdigit(a_date[21]) == 0) || 85 | (a_date[22] != ':') || 86 | (isdigit(a_date[23]) == 0) || 87 | (isdigit(a_date[24]) == 0)) 88 | return -1; 89 | if (a_date[25] != ' ') 90 | return -1; 91 | /* GMT */ 92 | if (strncmp(&a_date[26], "GMT", 3) != 0) 93 | return -1; 94 | /* ok, it's valid. Do it */ 95 | /* parse out the day of the month */ 96 | l_tm_time.tm_mday += (a_date[5] - 0x30) * 10; 97 | l_tm_time.tm_mday += (a_date[6] - 0x30); 98 | /* already got the month from above */ 99 | /* parse out the year */ 100 | l_tm_time.tm_year += (a_date[12] - 0x30) * 1000; 101 | l_tm_time.tm_year += (a_date[13] - 0x30) * 100; 102 | l_tm_time.tm_year += (a_date[14] - 0x30) * 10; 103 | l_tm_time.tm_year += (a_date[15] - 0x30); 104 | l_tm_time.tm_year -= 1900; 105 | /* parse out the time */ 106 | l_tm_time.tm_hour += (a_date[17] - 0x30) * 10; 107 | l_tm_time.tm_hour += (a_date[18] - 0x30); 108 | l_tm_time.tm_min += (a_date[20] - 0x30) * 10; 109 | l_tm_time.tm_min += (a_date[21] - 0x30); 110 | l_tm_time.tm_sec += (a_date[23] - 0x30) * 10; 111 | l_tm_time.tm_sec += (a_date[24] - 0x30); 112 | /* ok, generate the result */ 113 | l_return = mktime(&l_tm_time); 114 | } 115 | /* ansi C */ 116 | else if (a_date[3] == ' ') 117 | { 118 | if (strlen(a_date) != 24) 119 | return -1; 120 | /* Nov */ 121 | if ((l_tm_time.tm_mon = 122 | month_from_string_short(&a_date[4])) < 0) 123 | return -1; 124 | if (a_date[7] != ' ') 125 | return -1; 126 | /* "10" or " 6" */ 127 | if (((a_date[8] != ' ') && (isdigit(a_date[8]) == 0)) || 128 | (isdigit(a_date[9]) == 0)) 129 | return -1; 130 | if (a_date[10] != ' ') 131 | return -1; 132 | /* 08:49:37 */ 133 | if ((isdigit(a_date[11]) == 0) || 134 | (isdigit(a_date[12]) == 0) || 135 | (a_date[13] != ':') || 136 | (isdigit(a_date[14]) == 0) || 137 | (isdigit(a_date[15]) == 0) || 138 | (a_date[16] != ':') || 139 | (isdigit(a_date[17]) == 0) || 140 | (isdigit(a_date[18]) == 0)) 141 | return -1; 142 | if (a_date[19] != ' ') 143 | return -1; 144 | /* 1994 */ 145 | if ((isdigit(a_date[20]) == 0) || 146 | (isdigit(a_date[21]) == 0) || 147 | (isdigit(a_date[22]) == 0) || 148 | (isdigit(a_date[23]) == 0)) 149 | return -1; 150 | /* looks good */ 151 | /* got the month from above */ 152 | /* parse out the day of the month */ 153 | if (a_date[8] != ' ') 154 | l_tm_time.tm_mday += (a_date[8] - 0x30) * 10; 155 | l_tm_time.tm_mday += (a_date[9] - 0x30); 156 | /* parse out the time */ 157 | l_tm_time.tm_hour += (a_date[11] - 0x30) * 10; 158 | l_tm_time.tm_hour += (a_date[12] - 0x30); 159 | l_tm_time.tm_min += (a_date[14] - 0x30) * 10; 160 | l_tm_time.tm_min += (a_date[15] - 0x30); 161 | l_tm_time.tm_sec += (a_date[17] - 0x30) * 10; 162 | l_tm_time.tm_sec += (a_date[18] - 0x30); 163 | /* parse out the year */ 164 | l_tm_time.tm_year += (a_date[20] - 0x30) * 1000; 165 | l_tm_time.tm_year += (a_date[21] - 0x30) * 100; 166 | l_tm_time.tm_year += (a_date[22] - 0x30) * 10; 167 | l_tm_time.tm_year += (a_date[23] - 0x30); 168 | l_tm_time.tm_year -= 1900; 169 | /* generate the result */ 170 | l_return = mktime(&l_tm_time); 171 | } 172 | /* must be the 1036... */ 173 | else 174 | { 175 | /* check to make sure we won't rn out of any bounds */ 176 | if (strlen(a_date) < 11) 177 | return -1; 178 | while (a_date[i]) 179 | { 180 | if (a_date[i] == ' ') 181 | { 182 | l_start_date = &a_date[i+1]; 183 | break; 184 | } 185 | i++; 186 | } 187 | /* check to make sure there was a space found */ 188 | if (l_start_date == NULL) 189 | return -1; 190 | /* check to make sure that we don't overrun anything */ 191 | if (strlen(l_start_date) != 22) 192 | return -1; 193 | /* make sure that the rest of the date was valid */ 194 | /* 06- */ 195 | if ((isdigit(l_start_date[0]) == 0) || 196 | (isdigit(l_start_date[1]) == 0) || 197 | (l_start_date[2] != '-')) 198 | return -1; 199 | /* Nov */ 200 | if ((l_tm_time.tm_mon = 201 | month_from_string_short(&l_start_date[3])) < 0) 202 | return -1; 203 | /* -94 */ 204 | if ((l_start_date[6] != '-') || 205 | (isdigit(l_start_date[7]) == 0) || 206 | (isdigit(l_start_date[8]) == 0)) 207 | return -1; 208 | if (l_start_date[9] != ' ') 209 | return -1; 210 | /* 08:49:37 */ 211 | if ((isdigit(l_start_date[10]) == 0) || 212 | (isdigit(l_start_date[11]) == 0) || 213 | (l_start_date[12] != ':') || 214 | (isdigit(l_start_date[13]) == 0) || 215 | (isdigit(l_start_date[14]) == 0) || 216 | (l_start_date[15] != ':') || 217 | (isdigit(l_start_date[16]) == 0) || 218 | (isdigit(l_start_date[17]) == 0)) 219 | return -1; 220 | if (l_start_date[18] != ' ') 221 | return -1; 222 | if (strncmp(&l_start_date[19], "GMT", 3) != 0) 223 | return -1; 224 | /* looks ok to parse */ 225 | /* parse out the day of the month */ 226 | l_tm_time.tm_mday += (l_start_date[0] - 0x30) * 10; 227 | l_tm_time.tm_mday += (l_start_date[1] - 0x30); 228 | /* have the month from above */ 229 | /* parse out the year */ 230 | l_tm_time.tm_year += (l_start_date[7] - 0x30) * 10; 231 | l_tm_time.tm_year += (l_start_date[8] - 0x30); 232 | /* check for y2k */ 233 | if (l_tm_time.tm_year < 20) 234 | l_tm_time.tm_year += 100; 235 | /* parse out the time */ 236 | l_tm_time.tm_hour += (l_start_date[10] - 0x30) * 10; 237 | l_tm_time.tm_hour += (l_start_date[11] - 0x30); 238 | l_tm_time.tm_min += (l_start_date[13] - 0x30) * 10; 239 | l_tm_time.tm_min += (l_start_date[14] - 0x30); 240 | l_tm_time.tm_sec += (l_start_date[16] - 0x30) * 10; 241 | l_tm_time.tm_sec += (l_start_date[17] - 0x30); 242 | /* generate the result */ 243 | l_return = mktime(&l_tm_time); 244 | } 245 | return l_return; 246 | } 247 | 248 | static int 249 | month_from_string_short(const char *a_month) 250 | { 251 | if (strncmp(a_month, "Jan", 3) == 0) 252 | return 0; 253 | if (strncmp(a_month, "Feb", 3) == 0) 254 | return 1; 255 | if (strncmp(a_month, "Mar", 3) == 0) 256 | return 2; 257 | if (strncmp(a_month, "Apr", 3) == 0) 258 | return 3; 259 | if (strncmp(a_month, "May", 3) == 0) 260 | return 4; 261 | if (strncmp(a_month, "Jun", 3) == 0) 262 | return 5; 263 | if (strncmp(a_month, "Jul", 3) == 0) 264 | return 6; 265 | if (strncmp(a_month, "Aug", 3) == 0) 266 | return 7; 267 | if (strncmp(a_month, "Sep", 3) == 0) 268 | return 8; 269 | if (strncmp(a_month, "Oct", 3) == 0) 270 | return 9; 271 | if (strncmp(a_month, "Nov", 3) == 0) 272 | return 10; 273 | if (strncmp(a_month, "Dec", 3) == 0) 274 | return 11; 275 | /* not a valid date */ 276 | return -1; 277 | } 278 | 279 | -------------------------------------------------------------------------------- /src/ghttp/http_hdrs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * http_hdrs.c -- This file contains declarations for http headers 3 | * Created: Christopher Blizzard , 3-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #include 23 | #include 24 | #include "http_hdrs.h" 25 | 26 | /* entity headers */ 27 | 28 | const char http_hdr_Allow[] = "Allow"; 29 | const char http_hdr_Content_Encoding[] = "Content-Encoding"; 30 | const char http_hdr_Content_Language[] = "Content-Language"; 31 | const char http_hdr_Content_Length[] = "Content-Length"; 32 | const char http_hdr_Content_Location[] = "Content-Location"; 33 | const char http_hdr_Content_MD5[] = "Content-MD5"; 34 | const char http_hdr_Content_Range[] = "Content-Range"; 35 | const char http_hdr_Content_Type[] = "Content-Type"; 36 | const char http_hdr_Expires[] = "Expires"; 37 | const char http_hdr_Last_Modified[] = "Last-Modified"; 38 | 39 | /* general headers */ 40 | 41 | const char http_hdr_Cache_Control[] = "Cache-Control"; 42 | const char http_hdr_Connection[] = "Connection"; 43 | const char http_hdr_Date[] = "Date"; 44 | const char http_hdr_Pragma[] = "Pragma"; 45 | const char http_hdr_Transfer_Encoding[] = "Transfer-Encoding"; 46 | const char http_hdr_Update[] = "Update"; 47 | const char http_hdr_Trailer[] = "Trailer"; 48 | const char http_hdr_Via[] = "Via"; 49 | 50 | /* request headers */ 51 | 52 | const char http_hdr_Accept[] = "Accept"; 53 | const char http_hdr_Accept_Charset[] = "Accept-Charset"; 54 | const char http_hdr_Accept_Encoding[] = "Accept-Encoding"; 55 | const char http_hdr_Accept_Language[] = "Accept-Language"; 56 | const char http_hdr_Authorization[] = "Authorization"; 57 | const char http_hdr_Expect[] = "Expect"; 58 | const char http_hdr_From[] = "From"; 59 | const char http_hdr_Host[] = "Host"; 60 | const char http_hdr_If_Modified_Since[] = "If-Modified-Since"; 61 | const char http_hdr_If_Match[] = "If-Match"; 62 | const char http_hdr_If_None_Match[] = "If-None-Match"; 63 | const char http_hdr_If_Range[] = "If-Range"; 64 | const char http_hdr_If_Unmodified_Since[] = "If-Unmodified-Since"; 65 | const char http_hdr_Max_Forwards[] = "Max-Forwards"; 66 | const char http_hdr_Proxy_Authorization[] = "Proxy-Authorization"; 67 | const char http_hdr_Range[] = "Range"; 68 | const char http_hdr_Referrer[] = "Referrer"; 69 | const char http_hdr_TE[] = "TE"; 70 | const char http_hdr_User_Agent[] = "User-Agent"; 71 | 72 | /* response headers */ 73 | 74 | const char http_hdr_Accept_Ranges[] = "Accept-Ranges"; 75 | const char http_hdr_Age[] = "Age"; 76 | const char http_hdr_ETag[] = "ETag"; 77 | const char http_hdr_Location[] = "Location"; 78 | const char http_hdr_Retry_After[] = "Retry-After"; 79 | const char http_hdr_Server[] = "Server"; 80 | const char http_hdr_Vary[] = "Vary"; 81 | const char http_hdr_Warning[] = "Warning"; 82 | const char http_hdr_WWW_Authenticate[] = "WWW-Authenticate"; 83 | 84 | /* Other headers */ 85 | 86 | const char http_hdr_Set_Cookie[] = "Set-Cookie"; 87 | 88 | /* WebDAV headers */ 89 | 90 | const char http_hdr_DAV[] = "DAV"; 91 | const char http_hdr_Depth[] = "Depth"; 92 | const char http_hdr_Destination[] = "Destination"; 93 | const char http_hdr_If[] = "If"; 94 | const char http_hdr_Lock_Token[] = "Lock-Token"; 95 | const char http_hdr_Overwrite[] = "Overwrite"; 96 | const char http_hdr_Status_URI[] = "Status-URI"; 97 | const char http_hdr_Timeout[] = "Timeout"; 98 | 99 | const char *http_hdr_known_list[] = 100 | { 101 | /* entity headers */ 102 | http_hdr_Allow, 103 | http_hdr_Content_Encoding, 104 | http_hdr_Content_Language, 105 | http_hdr_Content_Length, 106 | http_hdr_Content_Location, 107 | http_hdr_Content_MD5, 108 | http_hdr_Content_Range, 109 | http_hdr_Content_Type, 110 | http_hdr_Expires, 111 | http_hdr_Last_Modified, 112 | /* general headers */ 113 | http_hdr_Cache_Control, 114 | http_hdr_Connection, 115 | http_hdr_Date, 116 | http_hdr_Pragma, 117 | http_hdr_Transfer_Encoding, 118 | http_hdr_Update, 119 | http_hdr_Trailer, 120 | http_hdr_Via, 121 | /* request headers */ 122 | http_hdr_Accept, 123 | http_hdr_Accept_Charset, 124 | http_hdr_Accept_Encoding, 125 | http_hdr_Accept_Language, 126 | http_hdr_Authorization, 127 | http_hdr_Expect, 128 | http_hdr_From, 129 | http_hdr_Host, 130 | http_hdr_If_Modified_Since, 131 | http_hdr_If_Match, 132 | http_hdr_If_None_Match, 133 | http_hdr_If_Range, 134 | http_hdr_If_Unmodified_Since, 135 | http_hdr_Max_Forwards, 136 | http_hdr_Proxy_Authorization, 137 | http_hdr_Range, 138 | http_hdr_Referrer, 139 | http_hdr_TE, 140 | http_hdr_User_Agent, 141 | /* response headers */ 142 | http_hdr_Accept_Ranges, 143 | http_hdr_Age, 144 | http_hdr_ETag, 145 | http_hdr_Location, 146 | http_hdr_Retry_After, 147 | http_hdr_Server, 148 | http_hdr_Vary, 149 | http_hdr_Warning, 150 | http_hdr_WWW_Authenticate, 151 | NULL 152 | }; 153 | 154 | /* functions dealing with headers */ 155 | 156 | const char * 157 | http_hdr_is_known(const char *a_hdr) 158 | { 159 | int l_pos = 0; 160 | const char *l_return = NULL; 161 | 162 | if (!a_hdr) 163 | goto ec; 164 | while(http_hdr_known_list[l_pos] != NULL) 165 | { 166 | if (strcasecmp(a_hdr, http_hdr_known_list[l_pos]) == 0) 167 | { 168 | l_return = http_hdr_known_list[l_pos]; 169 | break; 170 | } 171 | l_pos++; 172 | } 173 | ec: 174 | return l_return; 175 | } 176 | 177 | http_hdr_list * 178 | http_hdr_list_new(void) 179 | { 180 | http_hdr_list *l_return = NULL; 181 | 182 | l_return = (http_hdr_list *)malloc(sizeof(http_hdr_list)); 183 | memset(l_return, 0, sizeof(http_hdr_list)); 184 | return l_return; 185 | } 186 | 187 | void 188 | http_hdr_list_destroy(http_hdr_list *a_list) 189 | { 190 | int i = 0; 191 | 192 | if (a_list == NULL) 193 | return; 194 | for(i=0; i < HTTP_HDRS_MAX; i++) 195 | { 196 | if (a_list->header[i] && 197 | (http_hdr_is_known(a_list->header[i]) == NULL)) 198 | free(a_list->header[i]); 199 | if (a_list->value[i]) 200 | free(a_list->value[i]); 201 | } 202 | free (a_list); 203 | } 204 | 205 | int 206 | http_hdr_set_value_no_nts(http_hdr_list *a_list, 207 | const char *a_name_start, 208 | int a_name_len, 209 | const char *a_val_start, 210 | int a_val_len) 211 | { 212 | int l_return = 0; 213 | char *l_temp_name = NULL; 214 | char *l_temp_val = NULL; 215 | 216 | /* note that a zero len value is valid... */ 217 | if ((a_list == NULL) || 218 | (a_name_start == NULL) || 219 | (a_val_start == NULL) || 220 | (a_name_len == 0)) 221 | goto ec; 222 | l_temp_name = (char *)malloc(a_name_len + 1); 223 | memset(l_temp_name, 0, a_name_len + 1); 224 | memcpy(l_temp_name, a_name_start, a_name_len); 225 | l_temp_val = (char *)malloc(a_val_len + 1); 226 | memset(l_temp_val, 0, a_val_len + 1); 227 | memcpy(l_temp_val, a_val_start, a_val_len); 228 | /* set the value */ 229 | l_return = http_hdr_set_value(a_list, 230 | l_temp_name, 231 | l_temp_val); 232 | free(l_temp_name); 233 | free(l_temp_val); 234 | ec: 235 | return l_return; 236 | 237 | } 238 | 239 | int 240 | http_hdr_set_value(http_hdr_list *a_list, 241 | const char *a_name, 242 | const char *a_val) 243 | { 244 | int i = 0; 245 | char *l_temp_value = NULL; 246 | int l_return = 0; 247 | 248 | if ((a_list == NULL) || (a_name == NULL) || (a_val == NULL)) 249 | goto ec; 250 | l_temp_value = http_hdr_get_value(a_list, a_name); 251 | if (l_temp_value == NULL) 252 | { 253 | for (i=0; i < HTTP_HDRS_MAX; i++) 254 | { 255 | if (a_list->header[i] == NULL) 256 | { 257 | /* I promise not to mess with this value. */ 258 | l_temp_value = (char *)http_hdr_is_known(a_name); 259 | if (l_temp_value) 260 | { 261 | a_list->header[i] = l_temp_value; 262 | /* dont free this later... */ 263 | } 264 | else 265 | a_list->header[i] = strdup(a_name); 266 | a_list->value[i] = strdup(a_val); 267 | l_return = 1; 268 | break; 269 | } 270 | } 271 | } 272 | else 273 | { 274 | for(i = 0; i < HTTP_HDRS_MAX; i++) 275 | { 276 | if (a_list->value[i] == l_temp_value) 277 | { 278 | free(a_list->value[i]); 279 | a_list->value[i] = strdup(a_val); 280 | l_return = 1; 281 | break; 282 | } 283 | } 284 | } 285 | ec: 286 | return l_return; 287 | } 288 | 289 | char * 290 | http_hdr_get_value(http_hdr_list *a_list, 291 | const char *a_name) 292 | { 293 | int i = 0; 294 | char *l_return = NULL; 295 | 296 | if (a_name == NULL) 297 | goto ec; 298 | for (i=0; i < HTTP_HDRS_MAX; i++) 299 | { 300 | if (a_list->header[i] && 301 | (strcasecmp(a_list->header[i], a_name) == 0)) 302 | { 303 | if (a_list->value[i] == NULL) 304 | goto ec; 305 | l_return = a_list->value[i]; 306 | break; 307 | } 308 | } 309 | ec: 310 | return l_return; 311 | } 312 | 313 | int 314 | http_hdr_get_headers(http_hdr_list *a_list, char ***a_names, 315 | int *a_num_names) 316 | { 317 | int i = 0; 318 | int l_num_names = 0; 319 | char **l_names; 320 | 321 | if (a_num_names == NULL) 322 | return -1; 323 | if (a_names == NULL) 324 | return -1; 325 | 326 | /* set our return values */ 327 | *a_names = NULL; 328 | *a_num_names = 0; 329 | 330 | /* make a pass to find out how many headers we have. */ 331 | for (i=0; i < HTTP_HDRS_MAX; i++) 332 | { 333 | if (a_list->header[i]) 334 | l_num_names++; 335 | } 336 | 337 | /* return if there are no headers */ 338 | if (l_num_names == 0) 339 | return 0; 340 | 341 | /* now that we know how many headers we have allocate the number of 342 | slots in the return */ 343 | l_names = malloc(sizeof(char *) * l_num_names); 344 | if (l_names == NULL) 345 | return -1; 346 | 347 | /* zero the list so that we can clean up later if we have to */ 348 | memset(l_names, 0, l_num_names); 349 | 350 | /* copy the headers */ 351 | for (i=0; i < HTTP_HDRS_MAX; i++) 352 | { 353 | if (a_list->header[i]) 354 | { 355 | l_names[i] = strdup(a_list->header[i]); 356 | if (l_names[i] == NULL) 357 | goto ec; 358 | } 359 | } 360 | 361 | *a_names = l_names; 362 | *a_num_names = l_num_names; 363 | return 0; 364 | 365 | /* something bad happened. Try to free up as much as possible. */ 366 | ec: 367 | if (l_names) 368 | { 369 | for (i=0; i < l_num_names; i++) 370 | { 371 | if (l_names[i]) 372 | { 373 | free(l_names[i]); 374 | l_names[i] = NULL; 375 | } 376 | } 377 | free(l_names); 378 | *a_names = 0; 379 | } 380 | *a_num_names = 0; 381 | return -1; 382 | } 383 | 384 | int 385 | http_hdr_clear_value(http_hdr_list *a_list, 386 | const char *a_name) 387 | { 388 | int i = 0; 389 | int l_return = 0; 390 | 391 | if ((a_list == NULL) || (a_name == NULL)) 392 | goto ec; 393 | for (i=0; i < HTTP_HDRS_MAX; i++) 394 | { 395 | if (a_name && a_list->header[i] && 396 | (strcasecmp(a_list->header[i], a_name) == 0)) 397 | { 398 | if (http_hdr_is_known(a_name) == NULL) 399 | free(a_list->header[i]); 400 | a_list->header[i] = NULL; 401 | free(a_list->value[i]); 402 | a_list->value[i] = NULL; 403 | } 404 | } 405 | ec: 406 | return l_return; 407 | } 408 | 409 | -------------------------------------------------------------------------------- /src/etcd-api/etcd-api2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013, Red Hat 3 | * All rights reserved. 4 | 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. Redistributions in binary 10 | * form must reproduce the above copyright notice, this list of conditions and 11 | * the following disclaimer in the documentation and/or other materials 12 | * provided with the distribution. 13 | 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | /* For asprintf */ 28 | #if !defined(_GNU_SOURCE) 29 | #define _GNU_SOURCE 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include "etcd-api2.h" 37 | #include "ghttp/ghttp.h" 38 | 39 | 40 | #define DEFAULT_ETCD_PORT 2379 41 | #define SL_DELIM "\n\r\t ,;" 42 | 43 | typedef struct { 44 | etcd_server *servers; 45 | } _etcd_session; 46 | 47 | 48 | const char *value_path[] = { "node", "value", NULL }; 49 | const char *nodes_path[] = { "node", "nodes", NULL }; 50 | const char *entry_path[] = { "key", NULL }; 51 | 52 | /* 53 | * We only call this in case where it should be safe, but gcc doesn't know 54 | * that so we use this to shut it up. 55 | */ 56 | static char * 57 | MY_YAJL_GET_STRING (yajl_val x) 58 | { 59 | char *y = YAJL_GET_STRING(x); 60 | 61 | return y ? y : (char *)"bogus"; 62 | } 63 | 64 | #if defined(DEBUG) 65 | void 66 | print_curl_error (char *intro, CURLcode res) 67 | { 68 | printf("%s: %s\n",intro,curl_easy_strerror(res)); 69 | } 70 | #else 71 | #define print_curl_error(intro,res) 72 | #endif 73 | 74 | 75 | etcd_session 76 | etcd_open (etcd_server *server_list) 77 | { 78 | _etcd_session *session; 79 | 80 | session = malloc(sizeof(*session)); 81 | if (!session) { 82 | return NULL; 83 | } 84 | 85 | /* 86 | * Some day we'll set up more persistent connections, and keep track 87 | * (via redirects) of which server is leader so that we can always 88 | * try it first. For now we just push that to the individual request 89 | * functions, which do the most brain-dead thing that can work. 90 | */ 91 | 92 | session->servers = server_list; 93 | return session; 94 | } 95 | 96 | 97 | void 98 | etcd_close (etcd_session session) 99 | { 100 | free(session); 101 | } 102 | 103 | /* 104 | * Normal yajl_tree_get is returning NULL for these paths even when I can 105 | * verify (in gdb) that they exist. I suppose I could debug this for them, but 106 | * this is way easier. 107 | * 108 | * TBD: see if common distros are packaging a JSON library that isn't total 109 | * crap. 110 | */ 111 | static yajl_val 112 | my_yajl_tree_get (yajl_val root, char const **path, yajl_type type) 113 | { 114 | yajl_val obj = root; 115 | int i; 116 | 117 | for (;;) { 118 | if (!*path) { 119 | if (obj && (obj->type != type)) { 120 | return NULL; 121 | } 122 | return obj; 123 | } 124 | if (obj->type != yajl_t_object) { 125 | return NULL; 126 | } 127 | for (i = 0; /* nothing */; ++i) { 128 | if (i >= obj->u.object.len) { 129 | return NULL; 130 | } 131 | if (!strcmp(obj->u.object.keys[i],*path)) { 132 | obj = obj->u.object.values[i]; 133 | ++path; 134 | break; 135 | } 136 | } 137 | } 138 | } 139 | 140 | 141 | /* 142 | * Looking directly at node->u.array seems terribly un-modular, but the YAJL 143 | * tree interface doesn't seem to have any exposed API for iterating over the 144 | * elements of an array. I tried using yajl_tree_get with an index in the 145 | * path, either as a type-casted integer or as a string, but that didn't work. 146 | */ 147 | static char * 148 | parse_array_response (yajl_val parent) 149 | { 150 | size_t i; 151 | yajl_val item; 152 | yajl_val value; 153 | char *retval = NULL; 154 | char *saved; 155 | yajl_val node; 156 | 157 | node = my_yajl_tree_get(parent,nodes_path,yajl_t_array); 158 | if (!node) { 159 | return NULL; 160 | } 161 | 162 | for (i = 0; i < node->u.array.len; ++i) { 163 | item = node->u.array.values[i]; 164 | if (!item) { 165 | break; 166 | } 167 | value = my_yajl_tree_get(item,entry_path,yajl_t_string); 168 | if (!value) { 169 | break; 170 | } 171 | if (retval) { 172 | saved = retval; 173 | retval = NULL; 174 | (void)asprintf (&retval, "%s\n%s", 175 | saved, MY_YAJL_GET_STRING(value)); 176 | free(saved); 177 | } 178 | else { 179 | retval = strdup(MY_YAJL_GET_STRING(value)); 180 | } 181 | if (!retval) { 182 | break; 183 | } 184 | } 185 | 186 | return retval; 187 | } 188 | 189 | static char * 190 | parse_get_response(char *body) 191 | { 192 | yajl_val node; 193 | yajl_val value; 194 | char *json_data = NULL; 195 | 196 | node = yajl_tree_parse(body, NULL, 0); 197 | if (node) { 198 | value = my_yajl_tree_get(node,value_path,yajl_t_string); 199 | if (value) { 200 | /* 201 | * YAJL probably copied it once, now we're going to 202 | * copy it again. If anybody really cares for such 203 | * small and infrequently used values, we'd have to do 204 | * do something much more complicated (like using the 205 | * stream interface) to avoid the copy. Right now it's 206 | * just not worth it. 207 | */ 208 | json_data = strdup(MY_YAJL_GET_STRING(value)); 209 | } 210 | else { 211 | /* Might as well try this. */ 212 | json_data = parse_array_response(node); 213 | } 214 | yajl_tree_free(node); 215 | } 216 | 217 | return json_data; 218 | 219 | } 220 | 221 | 222 | static char * 223 | etcd_get_one (_etcd_session *session, const char *key, etcd_server *srv, const char *prefix) 224 | { 225 | char *url; 226 | ghttp_request *request; 227 | char *body; 228 | char *stream_to = NULL; 229 | 230 | if (asprintf(&url,"http://%s:%u/v2/%s%s", 231 | srv->host,srv->port,prefix,key) < 0) { 232 | goto done; 233 | } 234 | 235 | request = ghttp_request_new(); 236 | if (!request) { 237 | goto free_url; 238 | } 239 | 240 | ghttp_set_uri(request, url); 241 | ghttp_set_header(request, http_hdr_Connection, "close"); 242 | ghttp_prepare(request); 243 | 244 | ghttp_process(request); 245 | body = ghttp_get_body(request); 246 | if (!body) { 247 | #if defined(DEBUG) 248 | fprintf(stderr, ghttp_get_error(request)); 249 | #endif 250 | goto cleanup_ghttp; 251 | } 252 | 253 | stream_to = parse_get_response(body); 254 | if(!stream_to) { 255 | goto cleanup_ghttp; 256 | } 257 | 258 | cleanup_ghttp: 259 | ghttp_request_destroy(request); 260 | free_url: 261 | free(url); 262 | done: 263 | return stream_to; 264 | } 265 | 266 | 267 | char * 268 | etcd_get (etcd_session session_as_void, char *key) 269 | { 270 | _etcd_session *session = session_as_void; 271 | etcd_server *srv; 272 | char *res; 273 | 274 | for (srv = session->servers; srv->host; ++srv) { 275 | res = etcd_get_one(session,key,srv, (const char *)"keys/"); 276 | if (res) { 277 | return res; 278 | } 279 | } 280 | return NULL; 281 | } 282 | 283 | static void 284 | free_sl (etcd_server *server_list) 285 | { 286 | size_t num_servers; 287 | 288 | for (num_servers = 0; server_list[num_servers].host; ++num_servers) { 289 | free(server_list[num_servers].host); 290 | } 291 | free(server_list); 292 | } 293 | 294 | 295 | static int 296 | _count_matching (const char *text, const char *cset, int result) 297 | { 298 | char *t; 299 | int res = 0; 300 | 301 | for (t = (char *)text; *t; ++t) { 302 | if ((strchr(cset,*t) != NULL) != result) { 303 | break; 304 | } 305 | ++res; 306 | } 307 | 308 | return res; 309 | } 310 | 311 | #define count_matching(t,cs) _count_matching(t,cs,1) 312 | #define count_nonmatching(t,cs) _count_matching(t,cs,0) 313 | 314 | 315 | etcd_session 316 | etcd_open_str (char *server_names) 317 | { 318 | char *snp; 319 | int run_len; 320 | int host_len; 321 | size_t num_servers; 322 | etcd_server *server_list; 323 | etcd_session *session; 324 | 325 | /* 326 | * Yeah, we iterate over the string twice so we can allocate an 327 | * appropriately sized array instead of turning it into a linked list. 328 | * Unfortunately this means we can't use strtok* which is destructive 329 | * with no platform-independent way to reverse the destructive effects. 330 | */ 331 | 332 | num_servers = 0; 333 | snp = server_names; 334 | while (*snp) { 335 | run_len = count_nonmatching(snp,SL_DELIM); 336 | if (!run_len) { 337 | snp += count_matching(snp,SL_DELIM); 338 | continue; 339 | } 340 | ++num_servers; 341 | snp += run_len; 342 | } 343 | 344 | if (!num_servers) { 345 | return NULL; 346 | } 347 | 348 | server_list = calloc(num_servers+1,sizeof(*server_list)); 349 | if (!server_list) { 350 | return NULL; 351 | } 352 | num_servers = 0; 353 | 354 | snp = server_names; 355 | while (*snp) { 356 | run_len = count_nonmatching(snp,SL_DELIM); 357 | if (!run_len) { 358 | snp += count_matching(snp,SL_DELIM); 359 | continue; 360 | } 361 | host_len = count_nonmatching(snp,":"); 362 | if ((run_len - host_len) > 1) { 363 | server_list[num_servers].host = strndup(snp,host_len); 364 | server_list[num_servers].port = (unsigned short) 365 | strtoul(snp+host_len+1,NULL,10); 366 | } 367 | else { 368 | server_list[num_servers].host = strndup(snp,run_len); 369 | server_list[num_servers].port = DEFAULT_ETCD_PORT; 370 | } 371 | ++num_servers; 372 | snp += run_len; 373 | } 374 | 375 | session = etcd_open(server_list); 376 | if (!session) { 377 | free_sl(server_list); 378 | } 379 | return session; 380 | } 381 | 382 | 383 | void 384 | etcd_close_str (etcd_session session) 385 | { 386 | free_sl(((_etcd_session *)session)->servers); 387 | etcd_close(session); 388 | } 389 | -------------------------------------------------------------------------------- /src/ghttp/ghttp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ghttp.c -- Implementation of the public interface to http functions 3 | * Created: Christopher Blizzard , 21-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include "ghttp.h" 26 | #include "http_uri.h" 27 | #include "http_hdrs.h" 28 | #include "http_trans.h" 29 | #include "http_req.h" 30 | #include "http_resp.h" 31 | #include "http_date.h" 32 | #include "http_global.h" 33 | #include "http_base64.h" 34 | 35 | struct _ghttp_request 36 | { 37 | http_uri *uri; 38 | http_uri *proxy; 39 | http_req *req; 40 | http_resp *resp; 41 | http_trans_conn *conn; 42 | const char *errstr; 43 | int connected; 44 | ghttp_proc proc; 45 | char *username; 46 | char *password; 47 | char *authtoken; 48 | char *proxy_username; 49 | char *proxy_password; 50 | char *proxy_authtoken; 51 | }; 52 | 53 | static const char *basic_header = "Basic "; 54 | 55 | ghttp_request * 56 | ghttp_request_new(void) 57 | { 58 | struct _ghttp_request *l_return = NULL; 59 | 60 | /* create everything */ 61 | l_return = malloc(sizeof(struct _ghttp_request)); 62 | memset(l_return, 0, sizeof(struct _ghttp_request)); 63 | l_return->uri = http_uri_new(); 64 | l_return->proxy = http_uri_new(); 65 | l_return->req = http_req_new(); 66 | l_return->resp = http_resp_new(); 67 | l_return->conn = http_trans_conn_new(); 68 | return l_return; 69 | } 70 | 71 | void 72 | ghttp_request_destroy(ghttp_request *a_request) 73 | { 74 | if (!a_request) 75 | return; 76 | /* make sure that the socket was shut down. */ 77 | if (a_request->conn->sock >= 0) 78 | { 79 | close(a_request->conn->sock); 80 | a_request->conn->sock = -1; 81 | } 82 | /* destroy everything else */ 83 | if (a_request->uri) 84 | http_uri_destroy(a_request->uri); 85 | if (a_request->proxy) 86 | http_uri_destroy(a_request->proxy); 87 | if (a_request->req) 88 | http_req_destroy(a_request->req); 89 | if (a_request->resp) 90 | http_resp_destroy(a_request->resp); 91 | if (a_request->conn) 92 | http_trans_conn_destroy(a_request->conn); 93 | /* destroy username info. */ 94 | if (a_request->username) 95 | { 96 | free(a_request->username); 97 | a_request->username = NULL; 98 | } 99 | if (a_request->password) 100 | { 101 | free(a_request->password); 102 | a_request->password = NULL; 103 | } 104 | if (a_request->authtoken) 105 | { 106 | free(a_request->authtoken); 107 | a_request->authtoken = NULL; 108 | } 109 | /* destroy proxy authentication */ 110 | if (a_request->proxy_username) 111 | { 112 | free(a_request->proxy_username); 113 | a_request->proxy_username = NULL; 114 | } 115 | if (a_request->proxy_password) 116 | { 117 | free(a_request->proxy_password); 118 | a_request->proxy_password = NULL; 119 | } 120 | if (a_request->proxy_authtoken) 121 | { 122 | free(a_request->proxy_authtoken); 123 | a_request->proxy_authtoken = NULL; 124 | } 125 | if (a_request) 126 | free(a_request); 127 | return; 128 | } 129 | 130 | int 131 | ghttp_uri_validate(char *a_uri) 132 | { 133 | if (!a_uri) 134 | return -1; 135 | /* you can do this... */ 136 | return(http_uri_parse(a_uri, NULL)); 137 | } 138 | 139 | int 140 | ghttp_set_uri(ghttp_request *a_request, char *a_uri) 141 | { 142 | int l_rv = 0; 143 | http_uri *l_new_uri = NULL; 144 | 145 | if ((!a_request) || (!a_uri)) 146 | return -1; 147 | /* set the uri */ 148 | l_new_uri = http_uri_new(); 149 | l_rv = http_uri_parse(a_uri, l_new_uri); 150 | if (l_rv < 0) 151 | { 152 | http_uri_destroy(l_new_uri); 153 | return -1; 154 | } 155 | if (a_request->uri) 156 | { 157 | /* check to see if this has been set yet. */ 158 | if (a_request->uri->host && 159 | a_request->uri->port && 160 | a_request->uri->resource) 161 | { 162 | /* check to see if we just need to change the resource */ 163 | if ((!strcmp(a_request->uri->host, l_new_uri->host)) && 164 | (a_request->uri->port == l_new_uri->port)) 165 | { 166 | free(a_request->uri->resource); 167 | /* make a copy since we're about to destroy it */ 168 | a_request->uri->resource = strdup(l_new_uri->resource); 169 | http_uri_destroy(l_new_uri); 170 | } 171 | else 172 | { 173 | http_uri_destroy(a_request->uri); 174 | a_request->uri = l_new_uri; 175 | } 176 | } 177 | else 178 | { 179 | http_uri_destroy(a_request->uri); 180 | a_request->uri = l_new_uri; 181 | } 182 | } 183 | return 0; 184 | } 185 | 186 | int 187 | ghttp_set_proxy(ghttp_request *a_request, char *a_uri) 188 | { 189 | int l_rv = 0; 190 | 191 | if ((!a_request) || (!a_uri)) 192 | return -1; 193 | /* set the uri */ 194 | l_rv = http_uri_parse(a_uri, a_request->proxy); 195 | if (l_rv < 0) 196 | return -1; 197 | return 0; 198 | } 199 | 200 | int 201 | ghttp_set_type(ghttp_request *a_request, ghttp_type a_type) 202 | { 203 | int l_return = 0; 204 | 205 | /* check to make sure that the args are ok */ 206 | if (!a_request) 207 | return -1; 208 | /* switch on all of the supported types */ 209 | switch(a_type) 210 | { 211 | case ghttp_type_get: 212 | a_request->req->type = http_req_type_get; 213 | break; 214 | case ghttp_type_options: 215 | a_request->req->type = http_req_type_options; 216 | break; 217 | case ghttp_type_head: 218 | a_request->req->type = http_req_type_head; 219 | break; 220 | case ghttp_type_post: 221 | a_request->req->type = http_req_type_post; 222 | break; 223 | case ghttp_type_put: 224 | a_request->req->type = http_req_type_put; 225 | break; 226 | case ghttp_type_delete: 227 | a_request->req->type = http_req_type_delete; 228 | break; 229 | case ghttp_type_trace: 230 | a_request->req->type = http_req_type_trace; 231 | break; 232 | case ghttp_type_connect: 233 | a_request->req->type = http_req_type_connect; 234 | break; 235 | case ghttp_type_propfind: 236 | a_request->req->type = http_req_type_propfind; 237 | break; 238 | case ghttp_type_proppatch: 239 | a_request->req->type = http_req_type_proppatch; 240 | break; 241 | case ghttp_type_mkcol: 242 | a_request->req->type = http_req_type_mkcol; 243 | break; 244 | case ghttp_type_copy: 245 | a_request->req->type = http_req_type_copy; 246 | break; 247 | case ghttp_type_move: 248 | a_request->req->type = http_req_type_move; 249 | break; 250 | case ghttp_type_lock: 251 | a_request->req->type = http_req_type_lock; 252 | break; 253 | case ghttp_type_unlock: 254 | a_request->req->type = http_req_type_unlock; 255 | break; 256 | default: 257 | l_return = -1; 258 | break; 259 | } 260 | return l_return; 261 | } 262 | 263 | int 264 | ghttp_set_body(ghttp_request *a_request, char *a_body, int a_len) 265 | { 266 | /* check to make sure the request is there */ 267 | if (!a_request) 268 | return -1; 269 | /* check to make sure the body is there */ 270 | if ((a_len > 0) && (a_body == NULL)) 271 | return -1; 272 | /* check to make sure that it makes sense */ 273 | if ((a_request->req->type != http_req_type_post) && 274 | (a_request->req->type != http_req_type_put) && 275 | (a_request->req->type != http_req_type_proppatch) && 276 | (a_request->req->type != http_req_type_propfind) && 277 | (a_request->req->type != http_req_type_lock)) 278 | return -1; 279 | /* set the variables */ 280 | a_request->req->body = a_body; 281 | a_request->req->body_len = a_len; 282 | return 0; 283 | } 284 | 285 | int 286 | ghttp_set_sync(ghttp_request *a_request, 287 | ghttp_sync_mode a_mode) 288 | { 289 | if (!a_request) 290 | return -1; 291 | if (a_mode == ghttp_sync) 292 | a_request->conn->sync = HTTP_TRANS_SYNC; 293 | else if (a_mode == ghttp_async) 294 | a_request->conn->sync = HTTP_TRANS_ASYNC; 295 | else 296 | return -1; 297 | return 0; 298 | } 299 | 300 | int 301 | ghttp_prepare(ghttp_request *a_request) 302 | { 303 | /* only allow http requests if no proxy has been set */ 304 | if (!a_request->proxy->host && a_request->uri->proto && 305 | strcmp(a_request->uri->proto, "http")) 306 | return 1; 307 | /* check to see if we have to set up the 308 | host information */ 309 | if ((a_request->conn->host == NULL) || 310 | (a_request->conn->host != a_request->uri->host) || 311 | (a_request->conn->port != a_request->uri->port) || 312 | (a_request->conn->proxy_host != a_request->proxy->host) || 313 | (a_request->conn->proxy_port != a_request->proxy->port)) 314 | { 315 | /* reset everything. */ 316 | a_request->conn->host = a_request->uri->host; 317 | a_request->req->host = a_request->uri->host; 318 | a_request->req->full_uri = a_request->uri->full; 319 | a_request->conn->port = a_request->uri->port; 320 | a_request->conn->proxy_host = a_request->proxy->host; 321 | a_request->conn->proxy_port = a_request->proxy->port; 322 | a_request->conn->hostinfo = NULL; 323 | /* close the socket if it looks open */ 324 | if (a_request->conn->sock >= 0) 325 | { 326 | close(a_request->conn->sock); 327 | a_request->conn->sock = -1; 328 | a_request->connected = 0; 329 | } 330 | } 331 | /* check to see if we need to change the resource. */ 332 | if ((a_request->req->resource == NULL) || 333 | (a_request->req->resource != a_request->uri->resource)) 334 | { 335 | a_request->req->resource = a_request->uri->resource; 336 | a_request->req->host = a_request->uri->host; 337 | } 338 | /* set the authorization header */ 339 | if ((a_request->authtoken != NULL) && 340 | (strlen(a_request->authtoken) > 0)) 341 | { 342 | http_hdr_set_value(a_request->req->headers, 343 | http_hdr_Authorization, 344 | a_request->authtoken); 345 | } 346 | else 347 | { 348 | http_hdr_set_value(a_request->req->headers, 349 | http_hdr_WWW_Authenticate, 350 | NULL); 351 | } 352 | /* set the proxy authorization header */ 353 | if ((a_request->proxy_authtoken != NULL) && 354 | (strlen(a_request->proxy_authtoken) > 0)) 355 | { 356 | http_hdr_set_value(a_request->req->headers, 357 | http_hdr_Proxy_Authorization, 358 | a_request->proxy_authtoken); 359 | } 360 | http_req_prepare(a_request->req); 361 | return 0; 362 | } 363 | 364 | ghttp_status 365 | ghttp_process(ghttp_request *a_request) 366 | { 367 | int l_rv = 0; 368 | 369 | if (a_request->proc == ghttp_proc_none) 370 | a_request->proc = ghttp_proc_request; 371 | if (a_request->proc == ghttp_proc_request) 372 | { 373 | if (a_request->connected == 0) 374 | { 375 | if (http_trans_connect(a_request->conn) < 0) 376 | { 377 | if (a_request->conn->error_type == http_trans_err_type_errno) 378 | a_request->errstr = strerror(a_request->conn->error); 379 | else if(a_request->conn->error_type == http_trans_err_type_host) 380 | a_request->errstr = http_trans_get_host_error(h_errno); 381 | return ghttp_error; 382 | } 383 | a_request->connected = 1; 384 | } 385 | l_rv = http_req_send(a_request->req, a_request->conn); 386 | if (l_rv == HTTP_TRANS_ERR) 387 | return ghttp_error; 388 | if (l_rv == HTTP_TRANS_NOT_DONE) 389 | return ghttp_not_done; 390 | if (l_rv == HTTP_TRANS_DONE) 391 | { 392 | a_request->proc = ghttp_proc_response_hdrs; 393 | if (a_request->conn->sync == HTTP_TRANS_ASYNC) 394 | return ghttp_not_done; 395 | } 396 | } 397 | if (a_request->proc == ghttp_proc_response_hdrs) 398 | { 399 | l_rv = http_resp_read_headers(a_request->resp, a_request->conn); 400 | if (l_rv == HTTP_TRANS_ERR) 401 | return ghttp_error; 402 | if (l_rv == HTTP_TRANS_NOT_DONE) 403 | return ghttp_not_done; 404 | if (l_rv == HTTP_TRANS_DONE) 405 | { 406 | a_request->proc = ghttp_proc_response; 407 | if (a_request->conn->sync == HTTP_TRANS_ASYNC) 408 | return ghttp_not_done; 409 | } 410 | } 411 | if (a_request->proc == ghttp_proc_response) 412 | { 413 | l_rv = http_resp_read_body(a_request->resp, 414 | a_request->req, 415 | a_request->conn); 416 | if (l_rv == HTTP_TRANS_ERR) 417 | { 418 | /* make sure that the connected flag is fixed and stuff */ 419 | if (a_request->conn->sock == -1) 420 | a_request->connected = 0; 421 | return ghttp_error; 422 | } 423 | if (l_rv == HTTP_TRANS_NOT_DONE) 424 | return ghttp_not_done; 425 | if (l_rv == HTTP_TRANS_DONE) 426 | { 427 | /* make sure that the connected flag is fixed and stuff */ 428 | if (a_request->conn->sock == -1) 429 | a_request->connected = 0; 430 | a_request->proc = ghttp_proc_none; 431 | return ghttp_done; 432 | } 433 | } 434 | return ghttp_error; 435 | } 436 | 437 | ghttp_current_status 438 | ghttp_get_status(ghttp_request *a_request) 439 | { 440 | ghttp_current_status l_return; 441 | 442 | l_return.proc = a_request->proc; 443 | if (a_request->proc == ghttp_proc_request) 444 | { 445 | l_return.bytes_read = a_request->conn->io_buf_io_done; 446 | l_return.bytes_total = a_request->conn->io_buf_alloc; 447 | } 448 | else if (a_request->proc == ghttp_proc_response_hdrs) 449 | { 450 | l_return.bytes_read = 0; 451 | l_return.bytes_total = 0; 452 | } 453 | else if (a_request->proc == ghttp_proc_response) 454 | { 455 | if (a_request->resp->content_length > 0) 456 | { 457 | l_return.bytes_read = a_request->resp->body_len + 458 | a_request->conn->io_buf_alloc + 459 | a_request->resp->flushed_length; 460 | l_return.bytes_total = a_request->resp->content_length; 461 | } 462 | else 463 | { 464 | l_return.bytes_read = a_request->resp->body_len + 465 | a_request->conn->io_buf_alloc + 466 | a_request->resp->flushed_length; 467 | l_return.bytes_total = -1; 468 | } 469 | } 470 | else 471 | { 472 | l_return.bytes_read = 0; 473 | l_return.bytes_total = 0; 474 | } 475 | return l_return; 476 | } 477 | 478 | void 479 | ghttp_flush_response_buffer(ghttp_request *a_request) 480 | { 481 | http_resp_flush(a_request->resp, a_request->conn); 482 | } 483 | 484 | int 485 | ghttp_close(ghttp_request *a_request) 486 | { 487 | if (!a_request) 488 | return -1; 489 | if (a_request->conn->sock >= 0) 490 | { 491 | close(a_request->conn->sock); 492 | a_request->conn->sock = -1; 493 | } 494 | a_request->connected = 0; 495 | return 0; 496 | } 497 | 498 | void 499 | ghttp_clean(ghttp_request *a_request) 500 | { 501 | http_resp_destroy(a_request->resp); 502 | a_request->resp = http_resp_new(); 503 | http_req_destroy(a_request->req); 504 | a_request->req = http_req_new(); 505 | http_trans_buf_reset(a_request->conn); 506 | a_request->proc = ghttp_proc_none; 507 | return; 508 | } 509 | 510 | void 511 | ghttp_set_chunksize(ghttp_request *a_request, int a_size) 512 | { 513 | if (a_request && (a_size > 0)) 514 | a_request->conn->io_buf_chunksize = a_size; 515 | } 516 | 517 | void 518 | ghttp_set_header(ghttp_request *a_request, 519 | const char *a_hdr, const char *a_val) 520 | { 521 | http_hdr_set_value(a_request->req->headers, 522 | a_hdr, a_val); 523 | } 524 | 525 | const char * 526 | ghttp_get_header(ghttp_request *a_request, 527 | const char *a_hdr) 528 | { 529 | return http_hdr_get_value(a_request->resp->headers, 530 | a_hdr); 531 | } 532 | 533 | int 534 | ghttp_get_header_names(ghttp_request *a_request, 535 | char ***a_hdrs, int *a_num_hdrs) 536 | { 537 | return http_hdr_get_headers(a_request->resp->headers, 538 | a_hdrs, a_num_hdrs); 539 | } 540 | 541 | const char * 542 | ghttp_get_error(ghttp_request *a_request) 543 | { 544 | if (a_request->errstr == NULL) 545 | return "Unknown Error."; 546 | return a_request->errstr; 547 | } 548 | 549 | time_t 550 | ghttp_parse_date(char *a_date) 551 | { 552 | if (!a_date) 553 | return 0; 554 | return (http_date_to_time(a_date)); 555 | } 556 | 557 | int 558 | ghttp_status_code(ghttp_request *a_request) 559 | { 560 | if (!a_request) 561 | return 0; 562 | return(a_request->resp->status_code); 563 | } 564 | 565 | const char * 566 | ghttp_reason_phrase(ghttp_request *a_request) 567 | { 568 | if (!a_request) 569 | return 0; 570 | return(a_request->resp->reason_phrase); 571 | } 572 | 573 | int 574 | ghttp_get_socket(ghttp_request *a_request) 575 | { 576 | if (!a_request) 577 | return -1; 578 | return(a_request->conn->sock); 579 | } 580 | 581 | char * 582 | ghttp_get_body(ghttp_request *a_request) 583 | { 584 | if (!a_request) 585 | return NULL; 586 | if (a_request->proc == ghttp_proc_none) 587 | return (a_request->resp->body); 588 | if (a_request->proc == ghttp_proc_response) 589 | { 590 | if (a_request->resp->content_length > 0) 591 | { 592 | if (a_request->resp->body_len) 593 | return a_request->resp->body; 594 | else 595 | return a_request->conn->io_buf; 596 | } 597 | else 598 | { 599 | return a_request->resp->body; 600 | } 601 | } 602 | return NULL; 603 | } 604 | 605 | int 606 | ghttp_get_body_len(ghttp_request *a_request) 607 | { 608 | if (!a_request) 609 | return 0; 610 | if (a_request->proc == ghttp_proc_none) 611 | return (a_request->resp->body_len); 612 | if (a_request->proc == ghttp_proc_response) 613 | { 614 | if (a_request->resp->content_length > 0) 615 | { 616 | if (a_request->resp->body_len) 617 | return a_request->resp->body_len; 618 | else 619 | return a_request->conn->io_buf_alloc; 620 | } 621 | else 622 | { 623 | return a_request->resp->body_len; 624 | } 625 | } 626 | return 0; 627 | } 628 | 629 | int 630 | ghttp_set_authinfo(ghttp_request *a_request, 631 | const char *a_user, 632 | const char *a_pass) 633 | { 634 | char *l_authtoken = NULL; 635 | char *l_final_auth = NULL; 636 | char *l_auth64 = NULL; 637 | 638 | /* check our args */ 639 | if (!a_request) 640 | return -1; 641 | /* if we have a NULL or zero length string in the username 642 | or password field, blitz the authinfo */ 643 | if ((!a_user) || (strlen(a_user) < 1) || 644 | (!a_pass) || (strlen(a_pass)< 1)) 645 | { 646 | if (a_request->username) 647 | { 648 | free(a_request->username); 649 | a_request->username = NULL; 650 | } 651 | if (a_request->password) 652 | { 653 | free(a_request->password); 654 | a_request->password = NULL; 655 | } 656 | if (a_request->authtoken) 657 | { 658 | free(a_request->authtoken); 659 | a_request->authtoken = NULL; 660 | } 661 | return 0; 662 | } 663 | /* encode the string using base64. Usernames and passwords 664 | for basic authentication are encoded like this: 665 | username:password 666 | That's it. Easy, huh? 667 | */ 668 | /* enough for the trailing \0 and the : */ 669 | l_authtoken = malloc(strlen(a_user) + strlen(a_pass) + 2); 670 | memset(l_authtoken, 0, (strlen(a_user) + strlen(a_pass) + 2)); 671 | sprintf(l_authtoken, "%s:%s", a_user, a_pass); 672 | l_auth64 = http_base64_encode(l_authtoken); 673 | if (!l_auth64) 674 | { 675 | free(l_authtoken); 676 | return -1; 677 | } 678 | /* build the final header */ 679 | l_final_auth = malloc(strlen(l_auth64) + strlen(basic_header) + 1); 680 | memset(l_final_auth, 0, (strlen(l_auth64) + strlen(basic_header) + 1)); 681 | strcat(l_final_auth, basic_header); 682 | strcat(l_final_auth, l_auth64); 683 | free(l_auth64); 684 | free(l_authtoken); 685 | /* copy the strings into the request */ 686 | 687 | if (a_request->username) free(a_request->username); 688 | if (a_request->password) free(a_request->password); 689 | if (a_request->authtoken) free(a_request->authtoken); 690 | a_request->username = strdup(a_user); 691 | a_request->password = strdup(a_pass); 692 | a_request->authtoken = l_final_auth; 693 | 694 | return 0; 695 | } 696 | 697 | 698 | int 699 | ghttp_set_proxy_authinfo(ghttp_request *a_request, 700 | const char *a_user, 701 | const char *a_pass) 702 | { 703 | char *l_authtoken = NULL; 704 | char *l_final_auth = NULL; 705 | char *l_auth64 = NULL; 706 | 707 | /* check our args */ 708 | if (!a_request) 709 | return -1; 710 | /* if we have a NULL or zero length string in the username 711 | or password field, blitz the authinfo */ 712 | if ((!a_user) || (strlen(a_user) < 1) || 713 | (!a_pass) || (strlen(a_pass)< 1)) 714 | { 715 | if (a_request->proxy_username) 716 | { 717 | free(a_request->proxy_username); 718 | a_request->proxy_username = NULL; 719 | } 720 | if (a_request->proxy_password) 721 | { 722 | free(a_request->proxy_password); 723 | a_request->proxy_password = NULL; 724 | } 725 | if (a_request->proxy_authtoken) 726 | { 727 | free(a_request->proxy_authtoken); 728 | a_request->proxy_authtoken = NULL; 729 | } 730 | return 0; 731 | } 732 | /* encode the string using base64. Usernames and passwords 733 | for basic authentication are encoded like this: 734 | username:password 735 | That's it. Easy, huh? 736 | */ 737 | /* enough for the trailing \0 and the : */ 738 | l_authtoken = malloc(strlen(a_user) + strlen(a_pass) + 2); 739 | memset(l_authtoken, 0, (strlen(a_user) + strlen(a_pass) + 2)); 740 | sprintf(l_authtoken, "%s:%s", a_user, a_pass); 741 | l_auth64 = http_base64_encode(l_authtoken); 742 | if (!l_auth64) 743 | { 744 | free(l_authtoken); 745 | return -1; 746 | } 747 | /* build the final header */ 748 | l_final_auth = malloc(strlen(l_auth64) + strlen(basic_header) + 1); 749 | memset(l_final_auth, 0, (strlen(l_auth64) + strlen(basic_header) + 1)); 750 | strcat(l_final_auth, basic_header); 751 | strcat(l_final_auth, l_auth64); 752 | free(l_auth64); 753 | free(l_authtoken); 754 | /* copy the strings into the request */ 755 | if (a_request->proxy_username) free(a_request->proxy_username); 756 | if (a_request->proxy_password) free(a_request->proxy_password); 757 | if (a_request->proxy_authtoken) free(a_request->proxy_authtoken); 758 | a_request->proxy_username = strdup(a_user); 759 | a_request->proxy_password = strdup(a_pass); 760 | a_request->proxy_authtoken = l_final_auth; 761 | 762 | return 0; 763 | } 764 | -------------------------------------------------------------------------------- /src/ghttp/http_resp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * http_resp.c -- routines for reading http_responses 3 | * Created: Christopher Blizzard 9-Aug-1998 4 | * 5 | * Copyright (C) 1998 Free Software Foundation 6 | * 7 | * This library is free software; you can redistribute it and/or 8 | * modify it under the terms of the GNU Library General Public 9 | * License as published by the Free Software Foundation; either 10 | * version 2 of the License, or (at your option) any later version. 11 | * 12 | * This library is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 | * Library General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU Library General Public 18 | * License along with this library; if not, write to the Free 19 | * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "http_resp.h" 27 | #include "http_global.h" 28 | 29 | typedef enum header_state_tag 30 | { 31 | reading_header = 0, 32 | reading_value, 33 | reading_sep, 34 | reading_eol 35 | } header_state; 36 | 37 | static int 38 | string_is_number(char *a_string); 39 | 40 | static int 41 | read_chunk(http_trans_conn *a_conn); 42 | 43 | static int 44 | read_body_chunked(http_resp *a_resp, 45 | http_req *a_req, 46 | http_trans_conn *a_conn); 47 | 48 | static int 49 | read_body_content_length(http_resp *a_resp, 50 | http_req *a_req, 51 | http_trans_conn *a_conn); 52 | 53 | static int 54 | read_body_standard(http_resp *a_resp, 55 | http_req *a_req, 56 | http_trans_conn *a_conn); 57 | 58 | http_resp * 59 | http_resp_new(void) 60 | { 61 | http_resp *l_return = NULL; 62 | 63 | l_return = (http_resp *)malloc(sizeof(http_resp)); 64 | memset(l_return, 0, sizeof(http_resp)); 65 | l_return->headers = http_hdr_list_new(); 66 | return l_return; 67 | } 68 | 69 | void 70 | http_resp_destroy(http_resp *a_resp) 71 | { 72 | if (!a_resp) 73 | return; 74 | if (a_resp->reason_phrase) 75 | free(a_resp->reason_phrase); 76 | if (a_resp->headers) 77 | http_hdr_list_destroy(a_resp->headers); 78 | if (a_resp->body) 79 | free(a_resp->body); 80 | free(a_resp); 81 | return; 82 | } 83 | 84 | int 85 | http_resp_read_headers(http_resp *a_resp, http_trans_conn *a_conn) 86 | { 87 | char *l_start_body = NULL; 88 | int l_rv = 0; 89 | int l_done = 0; 90 | int l_return = HTTP_TRANS_DONE; 91 | char *l_start_header = NULL; 92 | int l_header_len = 0; 93 | char *l_last_header = NULL; 94 | int l_last_header_len = 0; 95 | char *l_start_value = NULL; 96 | int l_value_len = 0; 97 | char *l_cur_ptr = NULL; 98 | char *l_ptr = NULL; 99 | header_state l_state = reading_header; 100 | 101 | /* check to see if we need to jump in somewhere */ 102 | if (a_conn->sync == HTTP_TRANS_ASYNC) 103 | { 104 | if (a_resp->header_state == http_resp_reading_header) 105 | goto http_resp_reading_header_jump; 106 | } 107 | /* start reading headers */ 108 | do 109 | { 110 | a_resp->header_state = http_resp_reading_header; 111 | http_resp_reading_header_jump: 112 | /* read in the buffer */ 113 | l_rv = http_trans_read_into_buf(a_conn); 114 | /* check for an error */ 115 | if (l_rv == HTTP_TRANS_ERR) 116 | { 117 | a_conn->errstr = "Failed to read http response line"; 118 | l_return = HTTP_TRANS_ERR; 119 | goto ec; 120 | } 121 | /* check to see if the end of headers string is in the buffer */ 122 | l_start_body = http_trans_buf_has_patt(a_conn->io_buf, 123 | a_conn->io_buf_alloc, 124 | "\r\n\r\n", 4); 125 | if (l_start_body != NULL) 126 | l_done = 1; 127 | if ((l_done == 0) && (a_conn->sync == HTTP_TRANS_ASYNC) && (l_rv == HTTP_TRANS_NOT_DONE)) 128 | return HTTP_TRANS_NOT_DONE; 129 | /* yes, that !l_done is ther because in the case of a 100 130 | continue we well get back up to this loop without doing a 131 | successful read. */ 132 | if ((!l_done) && (l_rv == HTTP_TRANS_DONE) && (a_conn->last_read == 0)) 133 | { 134 | a_conn->errstr = "Short read while reading http response headers"; 135 | return HTTP_TRANS_ERR; 136 | } 137 | } while (l_done == 0); 138 | /* parse out the response header */ 139 | /* check to make sure that there's enough that came back */ 140 | if ((a_conn->io_buf_alloc) < 14) 141 | { 142 | a_conn->errstr = "The http response line was too short."; 143 | l_return = HTTP_TRANS_ERR; 144 | goto ec; 145 | } 146 | l_ptr = a_conn->io_buf; 147 | /* can you say PARANOID? I thought you could. */ 148 | if (strncmp(l_ptr, "HTTP", 4) != 0) 149 | { 150 | a_conn->errstr = "The http response line did not begin with \"HTTP\""; 151 | l_return = HTTP_TRANS_ERR; 152 | goto ec; 153 | } 154 | if ((isdigit(l_ptr[5]) == 0) || /* http ver */ 155 | (l_ptr[6] != '.') || 156 | (isdigit(l_ptr[7]) == 0) || 157 | (l_ptr[8] != ' ') || /* space */ 158 | (isdigit(l_ptr[9]) == 0) || /* code */ 159 | (isdigit(l_ptr[10]) == 0) || 160 | (isdigit(l_ptr[11]) == 0) || 161 | (l_ptr[12] != ' ')) /* space */ 162 | { 163 | a_conn->errstr = "Error parsing http response line"; 164 | l_return = HTTP_TRANS_ERR; 165 | goto ec; 166 | } 167 | /* convert char into int */ 168 | a_resp->http_ver = (l_ptr[5] - 0x30); 169 | a_resp->http_ver += ((l_ptr[7] - 0x30) / 10.0); 170 | /* convert the response into an int */ 171 | a_resp->status_code = ((l_ptr[9] - 0x30)*100); 172 | a_resp->status_code += ((l_ptr[10] - 0x30)*10); 173 | a_resp->status_code += (l_ptr[11] - 0x30); 174 | /* get the length of the reason_phrase */ 175 | l_cur_ptr = &l_ptr[13]; 176 | /* you can't overrun this because you already know that 177 | there has to be a '\r' above from searching from the 178 | end of the headers */ 179 | while (*l_cur_ptr != '\r') 180 | l_cur_ptr++; 181 | /* make sure to free the buffer if it's already allocated */ 182 | if (a_resp->reason_phrase) { 183 | free(a_resp->reason_phrase); 184 | a_resp->reason_phrase = NULL; 185 | } 186 | /* allocate space for the reason phrase */ 187 | a_resp->reason_phrase = 188 | malloc((l_cur_ptr - (&l_ptr[13])) + 1); 189 | memset(a_resp->reason_phrase, 0, 190 | ((l_cur_ptr - (&l_ptr[13])) + 1)); 191 | memcpy(a_resp->reason_phrase, 192 | &l_ptr[13], (l_cur_ptr - (&l_ptr[13]))); 193 | /* see if there are any headers. If there aren't any headers 194 | then the end of the reason phrase is the same as the start body 195 | as above. If that's the case then skip reading any headers. */ 196 | if (l_cur_ptr == l_start_body) 197 | l_done = 1; 198 | else 199 | l_done = 0; 200 | /* make sure that it's not a continue. */ 201 | if (a_resp->status_code == 100) 202 | { 203 | /* look for the next \r\n\r\n and cut it off */ 204 | char *l_end_continue = http_trans_buf_has_patt(a_conn->io_buf, 205 | a_conn->io_buf_alloc, 206 | "\r\n\r\n", 4); 207 | if (!l_end_continue) 208 | return HTTP_TRANS_ERR; 209 | http_trans_buf_clip(a_conn, l_end_continue + 4); 210 | l_start_body = NULL; 211 | a_resp->status_code = 0; 212 | l_done = 0; 213 | if (a_conn->sync == HTTP_TRANS_ASYNC) 214 | return HTTP_TRANS_NOT_DONE; 215 | else 216 | goto http_resp_reading_header_jump; 217 | } 218 | /* set the start past the end of the reason phrase, 219 | checking if there's a CRLF after it. */ 220 | while ((*l_cur_ptr == '\r') || 221 | (*l_cur_ptr == '\n')) 222 | l_cur_ptr++; 223 | 224 | /* start parsing out the headers */ 225 | /* start at the beginning */ 226 | l_start_header = l_cur_ptr; 227 | while (l_done == 0) 228 | { 229 | /* check to see if we're at the end of the 230 | headers as determined above by the _patt() call */ 231 | if (l_cur_ptr == (l_start_body + 1)) 232 | break; 233 | /* reading the header name */ 234 | if (l_state == reading_header) 235 | { 236 | /* check to see if there's leading whitespace. 237 | If that's the case then it needs to be combined 238 | from the previous header */ 239 | if (l_header_len == 0) 240 | { 241 | if ((*l_cur_ptr == ' ') || (*l_cur_ptr == '\t')) 242 | { 243 | /* bomb if it's the first header. That's not valid */ 244 | if ((l_last_header == NULL) || (l_last_header_len == 0)) 245 | { 246 | a_conn->errstr = "The first http response header began with whitespace"; 247 | l_return = HTTP_TRANS_ERR; 248 | goto ec; 249 | } 250 | l_cur_ptr++; 251 | /* set it reading sep. sep will eat all of the write space */ 252 | l_state = reading_sep; 253 | continue; 254 | } 255 | } 256 | if (*l_cur_ptr == ':') 257 | { 258 | /* make sure that there's some header there */ 259 | if (l_header_len == 0) 260 | { 261 | a_conn->errstr = "An http response header was zero length"; 262 | l_return = HTTP_TRANS_ERR; 263 | goto ec; 264 | } 265 | /* start reading the seperator */ 266 | l_state = reading_sep; 267 | l_cur_ptr++; 268 | } 269 | /* make sure there's a seperator in 270 | there somewhere */ 271 | else if (*l_cur_ptr == '\r') 272 | { 273 | a_conn->errstr = "Failed to find seperator in http response headers"; 274 | l_return = HTTP_TRANS_ERR; 275 | goto ec; 276 | } 277 | else 278 | { 279 | l_cur_ptr++; 280 | l_header_len++; 281 | } 282 | } 283 | /* read the seperator */ 284 | else if(l_state == reading_sep) 285 | { 286 | /* walk until you get a non-whitespace character */ 287 | if ((*l_cur_ptr == ' ') || (*l_cur_ptr == '\t')) 288 | l_cur_ptr++; 289 | else 290 | { 291 | l_state = reading_value; 292 | l_start_value = l_cur_ptr; 293 | l_value_len = 0; 294 | } 295 | } 296 | /* read the value */ 297 | else if(l_state == reading_value) 298 | { 299 | /* check to see if we've reached the end of the 300 | value */ 301 | if ((*l_cur_ptr == '\r') || (*l_cur_ptr == '\n')) 302 | { 303 | /* check to see if this is a continuation of the last 304 | header. If the header len is 0 and we've gotten to 305 | this point then that's the case */ 306 | if (l_header_len == 0) 307 | { 308 | http_hdr_set_value_no_nts(a_resp->headers, 309 | l_last_header, 310 | l_last_header_len, 311 | l_start_value, 312 | l_value_len); 313 | } 314 | else 315 | { 316 | http_hdr_set_value_no_nts(a_resp->headers, 317 | l_start_header, 318 | l_header_len, 319 | l_start_value, 320 | l_value_len); 321 | 322 | /* set the last header and the length so that a new line 323 | that starts with spaces can figure out what header it 324 | applies to */ 325 | l_last_header = l_start_header; 326 | l_last_header_len = l_header_len; 327 | } 328 | /* start eating the end of line */ 329 | l_state = reading_eol; 330 | } 331 | else 332 | { 333 | l_cur_ptr++; 334 | l_value_len++; 335 | } 336 | } 337 | /* read the eof */ 338 | else if(l_state == reading_eol) 339 | { 340 | /* eat the eol */ 341 | if ((*l_cur_ptr == '\r') || (*l_cur_ptr == '\n')) 342 | l_cur_ptr++; 343 | else 344 | { 345 | /* start reading a new header again. */ 346 | l_state = reading_header; 347 | l_start_header = l_cur_ptr; 348 | l_header_len = 0; 349 | } 350 | } 351 | /* what state is this? */ 352 | else 353 | { 354 | a_conn->errstr = "Unknown state while reading http response headers"; 355 | l_return = HTTP_TRANS_ERR; 356 | goto ec; 357 | } 358 | } 359 | /* clip the buffer */ 360 | http_trans_buf_clip(a_conn, l_start_body + 4); 361 | ec: 362 | a_resp->header_state = http_resp_header_start; 363 | return l_return; 364 | } 365 | 366 | int 367 | http_resp_read_body(http_resp *a_resp, 368 | http_req *a_req, 369 | http_trans_conn *a_conn) 370 | { 371 | int l_return = 0; 372 | char *l_content_length = NULL; 373 | char *l_transfer_encoding = NULL; 374 | char *l_connection = NULL; 375 | 376 | /* check to see if we have to jump in anywhere. */ 377 | if (a_conn->sync == HTTP_TRANS_ASYNC) 378 | { 379 | if (a_resp->body_state == http_resp_body_read_content_length) 380 | goto http_resp_body_read_content_length_jump; 381 | if (a_resp->body_state == http_resp_body_read_chunked) 382 | goto http_resp_body_read_chunked_jump; 383 | if (a_resp->body_state == http_resp_body_read_standard) 384 | goto http_resp_body_read_standard_jump; 385 | } 386 | /* check to make sure that things are ok. */ 387 | if ((!a_resp) || (!a_conn)) 388 | return -1; 389 | /* check to see if there should be an entity body. */ 390 | /* check to see if there's a content length */ 391 | l_content_length = 392 | http_hdr_get_value(a_resp->headers, 393 | http_hdr_Content_Length); 394 | /* check to see if there's a transfer encoding */ 395 | l_transfer_encoding = 396 | http_hdr_get_value(a_resp->headers, 397 | http_hdr_Transfer_Encoding); 398 | /* check to see if the connection header is set */ 399 | l_connection = 400 | http_hdr_get_value(a_resp->headers, 401 | http_hdr_Connection); 402 | /* if there's a content length, do your stuff */ 403 | if (l_content_length && (a_req->type != http_req_type_head)) 404 | { 405 | if (string_is_number(l_content_length) == 0) 406 | { 407 | a_conn->errstr = "Content length in http response was not a number"; 408 | return -1; 409 | } 410 | a_resp->content_length = atoi(l_content_length); 411 | /* set the state */ 412 | a_resp->body_state = http_resp_body_read_content_length; 413 | http_resp_body_read_content_length_jump: 414 | l_return = read_body_content_length(a_resp, a_req, a_conn); 415 | } 416 | else if (l_content_length) 417 | { 418 | /* this happens in a head request with content length. */ 419 | return HTTP_TRANS_DONE; 420 | } 421 | else if (l_transfer_encoding) 422 | { 423 | /* check to see if it's using chunked transfer encoding */ 424 | if (!strcasecmp(l_transfer_encoding, "chunked")) 425 | { 426 | /* set the state */ 427 | a_resp->body_state = http_resp_body_read_chunked; 428 | http_resp_body_read_chunked_jump: 429 | l_return = read_body_chunked(a_resp, a_req, a_conn); 430 | } 431 | else 432 | { 433 | /* what kind of encoding? */ 434 | a_conn->errstr = "Unknown encoding type in http response"; 435 | return -1; 436 | } 437 | } 438 | else 439 | { 440 | a_resp->body_state = http_resp_body_read_standard; 441 | /* set the state */ 442 | http_resp_body_read_standard_jump: 443 | l_return = read_body_standard(a_resp, a_req, a_conn); 444 | /* after that the connection gets closed */ 445 | if (l_return == HTTP_TRANS_DONE) 446 | { 447 | close(a_conn->sock); 448 | a_conn->sock = -1; 449 | } 450 | } 451 | /* check to see if the connection should be closed */ 452 | if (l_connection && (l_return != HTTP_TRANS_NOT_DONE)) 453 | { 454 | if (!strcasecmp(l_connection, "close")) 455 | { 456 | close (a_conn->sock); 457 | a_conn->sock = -1; 458 | } 459 | } 460 | if (l_return == HTTP_TRANS_DONE) 461 | a_resp->body_state = http_resp_body_start; 462 | return l_return; 463 | } 464 | 465 | static int 466 | string_is_number(char *a_string) 467 | { 468 | int i = 0; 469 | 470 | if (strlen(a_string) < 1) 471 | return 0; 472 | while (a_string[i]) 473 | { 474 | if (isdigit(a_string[i]) == 0) 475 | return 0; 476 | i++; 477 | } 478 | return 1; 479 | } 480 | 481 | static int 482 | read_chunk(http_trans_conn *a_conn) 483 | { 484 | char *l_end_chunk_hdr = NULL; 485 | int l_len = 0; 486 | int i = 0; 487 | int j = 0; 488 | char *l_ptr = NULL; 489 | int l_left_to_read = 0; 490 | int l_rv = 0; 491 | 492 | if (a_conn->chunk_len == 0) 493 | { 494 | /* check to make sure that the pattern is in the 495 | buffer */ 496 | do { 497 | if ((l_end_chunk_hdr = 498 | http_trans_buf_has_patt(a_conn->io_buf, a_conn->io_buf_alloc, 499 | "\r\n", 2)) == NULL) 500 | { 501 | l_rv = http_trans_read_into_buf(a_conn); 502 | if (l_rv == HTTP_TRANS_ERR) 503 | return HTTP_TRANS_ERR; 504 | /* check to see if the remote end hung up. */ 505 | if ((l_rv == HTTP_TRANS_DONE) && (a_conn->last_read == 0)) 506 | return HTTP_TRANS_ERR; 507 | if ((a_conn->sync == HTTP_TRANS_ASYNC) && (l_rv == HTTP_TRANS_NOT_DONE)) 508 | return HTTP_TRANS_NOT_DONE; 509 | } 510 | } while (l_end_chunk_hdr == NULL); 511 | /* set the begining at the start of the buffer */ 512 | l_ptr = a_conn->io_buf; 513 | /* eat the hex value of the chunk */ 514 | while ((l_ptr < l_end_chunk_hdr) && 515 | (((tolower(*l_ptr) <= 'f') && (tolower(*l_ptr) >= 'a')) || 516 | ((*l_ptr <= '9') && (*l_ptr >= '0')))) 517 | l_ptr++; 518 | /* get the length of the hex number */ 519 | l_len = l_ptr - a_conn->io_buf; 520 | if (l_len == 0) 521 | { 522 | a_conn->chunk_len = -1; 523 | return HTTP_TRANS_ERR; 524 | } 525 | /* walk the size adding the values as you go along. */ 526 | for (i=0, j=l_len-1; i < l_len; i++, j--) 527 | { 528 | if ((tolower(a_conn->io_buf[i]) <= 'f') && (tolower(a_conn->io_buf[i]) >= 'a')) 529 | a_conn->chunk_len += (tolower(a_conn->io_buf[i]) - 0x57) << (4*j); 530 | else 531 | a_conn->chunk_len += (tolower(a_conn->io_buf[i]) - 0x30) << (4*j); 532 | } 533 | /* reset the pointer past the end of the header */ 534 | http_trans_buf_clip(a_conn, l_end_chunk_hdr + 2); 535 | } 536 | /* check to see if it's the last chunk. If not then add it.*/ 537 | if (a_conn->chunk_len != 0) 538 | { 539 | /* figure out how much we need to read */ 540 | /* the + 2 is for the \r\n that always follows a chunk. */ 541 | l_left_to_read = a_conn->chunk_len - a_conn->io_buf_alloc + 2; 542 | /* check to make sure that we actually need to read anything in. */ 543 | if (l_left_to_read > 0) 544 | { 545 | /* set the vars in the struct */ 546 | a_conn->io_buf_io_left = l_left_to_read; 547 | a_conn->io_buf_io_done = 0; 548 | /* append it */ 549 | do { 550 | l_rv = http_trans_read_into_buf(a_conn); 551 | if (l_rv == HTTP_TRANS_ERR) 552 | return HTTP_TRANS_ERR; 553 | /* check and see if the server hung up. */ 554 | if ((l_rv == HTTP_TRANS_DONE) && (a_conn->last_read == 0)) 555 | return HTTP_TRANS_ERR; 556 | if ((a_conn->sync == HTTP_TRANS_ASYNC) && (l_rv == HTTP_TRANS_NOT_DONE)) 557 | return HTTP_TRANS_NOT_DONE; 558 | } while (l_rv == HTTP_TRANS_NOT_DONE); 559 | } 560 | } 561 | if (a_conn->io_buf_alloc >= a_conn->chunk_len + 2) 562 | return HTTP_TRANS_DONE; 563 | /* we only get here if there was an error. */ 564 | if (a_conn->chunk_len == 0) 565 | return HTTP_TRANS_DONE; 566 | return HTTP_TRANS_ERR; 567 | } 568 | 569 | static int 570 | read_body_chunked(http_resp *a_resp, 571 | http_req *a_req, 572 | http_trans_conn *a_conn) 573 | { 574 | int l_rv = 0; 575 | int l_done = 0; 576 | 577 | do 578 | { 579 | /* read a chunk */ 580 | l_rv = read_chunk(a_conn); 581 | if (l_rv == HTTP_TRANS_ERR) 582 | return HTTP_TRANS_ERR; 583 | if ((a_conn->sync == HTTP_TRANS_ASYNC) && (l_rv == HTTP_TRANS_NOT_DONE)) 584 | return HTTP_TRANS_NOT_DONE; 585 | /* see if it's the first time */ 586 | if (a_conn->chunk_len > 0) 587 | { 588 | if (a_resp->body == NULL) 589 | { 590 | a_resp->body = malloc(a_conn->chunk_len); 591 | memcpy(a_resp->body, a_conn->io_buf, a_conn->chunk_len); 592 | a_resp->body_len = a_conn->chunk_len; 593 | } 594 | /* append it to the body */ 595 | else 596 | { 597 | a_resp->body = realloc(a_resp->body, 598 | (a_resp->body_len + a_conn->chunk_len)); 599 | memcpy(&a_resp->body[a_resp->body_len], a_conn->io_buf, a_conn->chunk_len); 600 | a_resp->body_len += a_conn->chunk_len; 601 | } 602 | } 603 | /* make sure there's at least 2 bytes in the buffer. 604 | This happens when a read was 3 bytes as in 0\r\n 605 | and there is still 2 bytes ( \r\n ) in the read queue. */ 606 | if ((a_conn->chunk_len == 0) && (a_conn->io_buf_alloc < 2)) 607 | { 608 | a_conn->io_buf_io_left = ( 2 - a_conn->io_buf_alloc ); 609 | a_conn->io_buf_io_done = 0; 610 | do { 611 | l_rv = http_trans_read_into_buf(a_conn); 612 | } while (l_rv == HTTP_TRANS_NOT_DONE); 613 | /* check for an error */ 614 | if (l_rv == HTTP_TRANS_ERR) 615 | return HTTP_TRANS_ERR; 616 | } 617 | if (a_conn->chunk_len == 0) 618 | l_done = 1; 619 | else 620 | { 621 | /* clip the buffer */ 622 | http_trans_buf_clip(a_conn, &a_conn->io_buf[a_conn->chunk_len + 2]); 623 | } 624 | a_conn->chunk_len = 0; 625 | } while (l_done == 0); 626 | return HTTP_TRANS_DONE; 627 | } 628 | 629 | static void 630 | flush_response_body(http_resp *a_resp, 631 | http_trans_conn *a_conn) 632 | { 633 | if (a_resp->body != NULL) { 634 | free(a_resp->body); 635 | } 636 | a_resp->flushed_length += a_resp->body_len; 637 | a_resp->body_len = a_conn->io_buf_alloc; 638 | a_resp->body = malloc(a_conn->io_buf_alloc + 1); 639 | memset(a_resp->body, 0, a_conn->io_buf_alloc + 1); 640 | memcpy(a_resp->body, a_conn->io_buf, a_conn->io_buf_alloc); 641 | /* clean the buffer */ 642 | http_trans_buf_reset(a_conn); 643 | } 644 | 645 | void 646 | http_resp_flush(http_resp *a_resp, 647 | http_trans_conn *a_conn) 648 | { 649 | flush_response_body(a_resp, a_conn); 650 | } 651 | 652 | static int 653 | read_body_content_length(http_resp *a_resp, 654 | http_req *a_req, 655 | http_trans_conn *a_conn) 656 | { 657 | int l_len = 0; 658 | int l_left_to_read = 0; 659 | int l_rv = 0; 660 | 661 | l_len = a_resp->content_length; 662 | if (l_len == 0) 663 | return HTTP_TRANS_DONE; 664 | 665 | /* find out how much more we have to read */ 666 | l_left_to_read = l_len - a_conn->io_buf_alloc - a_resp->flushed_length - a_resp->body_len; 667 | /* set the variables */ 668 | a_conn->io_buf_io_left = l_left_to_read; 669 | a_conn->io_buf_io_done = 0; 670 | if (l_left_to_read > 0) 671 | { 672 | /* append the rest of the body to the 673 | buffer */ 674 | do { 675 | l_rv = http_trans_read_into_buf(a_conn); 676 | if ((l_rv == HTTP_TRANS_NOT_DONE) && 677 | (a_conn->sync == HTTP_TRANS_ASYNC)) 678 | return HTTP_TRANS_NOT_DONE; 679 | if ((l_rv == HTTP_TRANS_DONE) && (a_conn->last_read == 0)) 680 | return HTTP_TRANS_ERR; 681 | } while(l_rv == HTTP_TRANS_NOT_DONE); 682 | if (l_rv == HTTP_TRANS_ERR) 683 | return HTTP_TRANS_ERR; 684 | } 685 | /* write it into the body */ 686 | flush_response_body (a_resp, a_conn); 687 | return HTTP_TRANS_DONE; 688 | } 689 | 690 | static int 691 | read_body_standard(http_resp *a_resp, 692 | http_req *a_req, 693 | http_trans_conn *a_conn) 694 | { 695 | int l_rv = 0; 696 | /* anything without a content length or chunked encoding */ 697 | do { 698 | l_rv = http_trans_read_into_buf(a_conn); 699 | if (a_conn->sync == HTTP_TRANS_ASYNC) 700 | { 701 | if ((l_rv == HTTP_TRANS_NOT_DONE) || (a_conn->last_read != 0)) 702 | return HTTP_TRANS_NOT_DONE; 703 | } 704 | } while ((l_rv == HTTP_TRANS_NOT_DONE) || (a_conn->last_read > 0)); 705 | 706 | if (l_rv == HTTP_TRANS_ERR) 707 | return HTTP_TRANS_ERR; 708 | 709 | flush_response_body(a_resp, a_conn); 710 | return HTTP_TRANS_DONE; 711 | } 712 | --------------------------------------------------------------------------------