diff options
Diffstat (limited to 'src/reqs.c')
-rw-r--r-- | src/reqs.c | 2745 |
1 files changed, 1429 insertions, 1316 deletions
@@ -81,20 +81,22 @@ static vector_t ports_allowed_by_connect = NULL; * it hasn't already by done. */ void -add_connect_port_allowed(int port) +add_connect_port_allowed (int port) { - if (!ports_allowed_by_connect) { - ports_allowed_by_connect = vector_create(); - if (!ports_allowed_by_connect) { - log_message(LOG_WARNING, - "Could not create a list of allowed CONNECT ports"); - return; - } - } - - log_message(LOG_INFO, "Adding Port [%d] to the list allowed by CONNECT", - port); - vector_append(ports_allowed_by_connect, (void **)&port, sizeof(port)); + if (!ports_allowed_by_connect) + { + ports_allowed_by_connect = vector_create (); + if (!ports_allowed_by_connect) + { + log_message (LOG_WARNING, + "Could not create a list of allowed CONNECT ports"); + return; + } + } + + log_message (LOG_INFO, "Adding Port [%d] to the list allowed by CONNECT", + port); + vector_append (ports_allowed_by_connect, (void **) &port, sizeof (port)); } /* @@ -104,25 +106,26 @@ add_connect_port_allowed(int port) * 0 if denied */ static int -check_allowed_connect_ports(int port) +check_allowed_connect_ports (int port) { - size_t i; - int *data; - - /* - * A port list is REQUIRED for a CONNECT request to function - * properly. This closes a potential security hole. - */ - if (!ports_allowed_by_connect) - return 0; - - for (i = 0; i != vector_length(ports_allowed_by_connect); ++i) { - data = vector_getentry(ports_allowed_by_connect, i, NULL); - if (data && *data == port) - return 1; - } - - return 0; + size_t i; + int *data; + + /* + * A port list is REQUIRED for a CONNECT request to function + * properly. This closes a potential security hole. + */ + if (!ports_allowed_by_connect) + return 0; + + for (i = 0; i != vector_length (ports_allowed_by_connect); ++i) + { + data = vector_getentry (ports_allowed_by_connect, i, NULL); + if (data && *data == port) + return 1; + } + + return 0; } /* @@ -131,57 +134,59 @@ check_allowed_connect_ports(int port) * be freed in another function. */ static int -read_request_line(struct conn_s *connptr) +read_request_line (struct conn_s *connptr) { - ssize_t len; - - retry: - len = readline(connptr->client_fd, &connptr->request_line); - if (len <= 0) { - log_message(LOG_ERR, - "read_request_line: Client (file descriptor: %d) closed socket before read.", - connptr->client_fd); - - return -1; - } - - /* - * Strip the new line and carriage return from the string. - */ - if (chomp(connptr->request_line, len) == len) { - /* - * If the number of characters removed is the same as the - * length then it was a blank line. Free the buffer and - * try again (since we're looking for a request line.) - */ - safefree(connptr->request_line); - goto retry; - } - - log_message(LOG_CONN, "Request (file descriptor %d): %s", - connptr->client_fd, connptr->request_line); - - return 0; + ssize_t len; + +retry: + len = readline (connptr->client_fd, &connptr->request_line); + if (len <= 0) + { + log_message (LOG_ERR, + "read_request_line: Client (file descriptor: %d) closed socket before read.", + connptr->client_fd); + + return -1; + } + + /* + * Strip the new line and carriage return from the string. + */ + if (chomp (connptr->request_line, len) == len) + { + /* + * If the number of characters removed is the same as the + * length then it was a blank line. Free the buffer and + * try again (since we're looking for a request line.) + */ + safefree (connptr->request_line); + goto retry; + } + + log_message (LOG_CONN, "Request (file descriptor %d): %s", + connptr->client_fd, connptr->request_line); + + return 0; } /* * Free all the memory allocated in a request. */ static void -free_request_struct(struct request_s *request) +free_request_struct (struct request_s *request) { - if (!request) - return; + if (!request) + return; - safefree(request->method); - safefree(request->protocol); + safefree (request->method); + safefree (request->protocol); - if (request->host) - safefree(request->host); - if (request->path) - safefree(request->path); + if (request->host) + safefree (request->host); + if (request->path) + safefree (request->path); - safefree(request); + safefree (request); } /* @@ -189,24 +194,24 @@ free_request_struct(struct request_s *request) * it off. */ static void -strip_username_password(char *host) +strip_username_password (char *host) { - char *p; - - assert(host); - assert(strlen(host) > 0); - - if ((p = strchr(host, '@')) == NULL) - return; - - /* - * Move the pointer past the "@" and then copy from that point - * until the NUL to the beginning of the host buffer. - */ - p++; - while (*p) - *host++ = *p++; - *host = '\0'; + char *p; + + assert (host); + assert (strlen (host) > 0); + + if ((p = strchr (host, '@')) == NULL) + return; + + /* + * Move the pointer past the "@" and then copy from that point + * until the NUL to the beginning of the host buffer. + */ + p++; + while (*p) + *host++ = *p++; + *host = '\0'; } /* @@ -214,19 +219,19 @@ strip_username_password(char *host) * it off and set proper port variable i.e. for www.host.com:8001 */ static int -strip_return_port(char *host) +strip_return_port (char *host) { - char *ptr1; - int port; + char *ptr1; + int port; - ptr1 = strchr(host, ':'); - if (ptr1 == NULL) - return 0; + ptr1 = strchr (host, ':'); + if (ptr1 == NULL) + return 0; - *ptr1++ = '\0'; - if (sscanf(ptr1, "%d", &port) != 1) /* one conversion required */ - return 0; - return port; + *ptr1++ = '\0'; + if (sscanf (ptr1, "%d", &port) != 1) /* one conversion required */ + return 0; + return port; } /* @@ -234,70 +239,74 @@ strip_return_port(char *host) * and FTP (proxied) URLs. */ static int -extract_http_url(const char *url, struct request_s *request) +extract_http_url (const char *url, struct request_s *request) { - char *p; - int len; - int port; - - /* Split the URL on the slash to separate host from path */ - p = strchr(url, '/'); - if (p != NULL) { - len = p - url; - request->host = safemalloc(len + 1); - memcpy(request->host, url, len); - request->host[len] = '\0'; - request->path = safestrdup(p); - } else { - request->host = safestrdup(url); - request->path = safestrdup("/"); - } - - if (!request->host || !request->path) - goto ERROR_EXIT; - - /* Remove the username/password if they're present */ - strip_username_password(request->host); - - /* Find a proper port in www.site.com:8001 URLs */ - port = strip_return_port(request->host); - request->port = (port != 0) ? port : HTTP_PORT; - - return 0; - - ERROR_EXIT: - if (request->host) - safefree(request->host); - if (request->path) - safefree(request->path); - - return -1; + char *p; + int len; + int port; + + /* Split the URL on the slash to separate host from path */ + p = strchr (url, '/'); + if (p != NULL) + { + len = p - url; + request->host = safemalloc (len + 1); + memcpy (request->host, url, len); + request->host[len] = '\0'; + request->path = safestrdup (p); + } + else + { + request->host = safestrdup (url); + request->path = safestrdup ("/"); + } + + if (!request->host || !request->path) + goto ERROR_EXIT; + + /* Remove the username/password if they're present */ + strip_username_password (request->host); + + /* Find a proper port in www.site.com:8001 URLs */ + port = strip_return_port (request->host); + request->port = (port != 0) ? port : HTTP_PORT; + + return 0; + +ERROR_EXIT: + if (request->host) + safefree (request->host); + if (request->path) + safefree (request->path); + + return -1; } /* * Extract the URL from a SSL connection. */ static int -extract_ssl_url(const char *url, struct request_s *request) +extract_ssl_url (const char *url, struct request_s *request) { - request->host = safemalloc(strlen(url) + 1); - if (!request->host) - return -1; + request->host = safemalloc (strlen (url) + 1); + if (!request->host) + return -1; - if (sscanf(url, "%[^:]:%hu", request->host, &request->port) == 2) ; - else if (sscanf(url, "%s", request->host) == 1) - request->port = HTTP_PORT_SSL; - else { - log_message(LOG_ERR, "extract_ssl_url: Can't parse URL."); + if (sscanf (url, "%[^:]:%hu", request->host, &request->port) == 2); + else if (sscanf (url, "%s", request->host) == 1) + request->port = HTTP_PORT_SSL; + else + { + log_message (LOG_ERR, "extract_ssl_url: Can't parse URL."); - safefree(request->host); - return -1; - } + safefree (request->host); + return -1; + } - /* Remove the username/password if they're present */ - strip_username_password(request->host); + /* Remove the username/password if they're present */ + strip_username_password (request->host); - return 0; + return 0; } @@ -306,161 +315,183 @@ extract_ssl_url(const char *url, struct request_s *request) * Add an entry to the upstream list */ void -upstream_add(const char *host, int port, const char *domain) +upstream_add (const char *host, int port, const char *domain) { - char *ptr; - struct upstream *up = safemalloc(sizeof(struct upstream)); - - if (!up) { - log_message(LOG_ERR, - "Unable to allocate memory in upstream_add()"); - return; - } - - up->host = up->domain = NULL; - up->ip = up->mask = 0; - - if (domain == NULL) { - if (!host || host[0] == '\0' || port < 1) { - log_message(LOG_WARNING, - "Nonsense upstream rule: invalid host or port"); - goto upstream_cleanup; - } - - up->host = safestrdup(host); - up->port = port; - - log_message(LOG_INFO, "Added upstream %s:%d for [default]", - host, port); - } else if (host == NULL) { - if (!domain || domain[0] == '\0') { - log_message(LOG_WARNING, - "Nonsense no-upstream rule: empty domain"); - goto upstream_cleanup; - } - - ptr = strchr(domain, '/'); - if (ptr) { - struct in_addr addrstruct; - - *ptr = '\0'; - if (inet_aton(domain, &addrstruct) != 0) { - up->ip = ntohl(addrstruct.s_addr); - *ptr++ = '/'; - - if (strchr(ptr, '.')) { - if (inet_aton(ptr, &addrstruct) != 0) - up->mask = - ntohl(addrstruct.s_addr); - } else { - up->mask = - ~((1 << (32 - atoi(ptr))) - 1); - } - } - } else { - up->domain = safestrdup(domain); - } - - log_message(LOG_INFO, "Added no-upstream for %s", domain); - } else { - if (!host || host[0] == '\0' || port < 1 || !domain - || domain == '\0') { - log_message(LOG_WARNING, - "Nonsense upstream rule: invalid parameters"); - goto upstream_cleanup; - } - - up->host = safestrdup(host); - up->port = port; - up->domain = safestrdup(domain); - - log_message(LOG_INFO, "Added upstream %s:%d for %s", - host, port, domain); - } - - if (!up->domain && !up->ip) { /* always add default to end */ - struct upstream *tmp = config.upstream_list; - - while (tmp) { - if (!tmp->domain && !tmp->ip) { - log_message(LOG_WARNING, - "Duplicate default upstream"); - goto upstream_cleanup; - } - - if (!tmp->next) { - up->next = NULL; - tmp->next = up; - return; - } - - tmp = tmp->next; - } - } - - up->next = config.upstream_list; - config.upstream_list = up; - - return; - - upstream_cleanup: - safefree(up->host); - safefree(up->domain); - safefree(up); - - return; + char *ptr; + struct upstream *up = safemalloc (sizeof (struct upstream)); + + if (!up) + { + log_message (LOG_ERR, "Unable to allocate memory in upstream_add()"); + return; + } + + up->host = up->domain = NULL; + up->ip = up->mask = 0; + + if (domain == NULL) + { + if (!host || host[0] == '\0' || port < 1) + { + log_message (LOG_WARNING, + "Nonsense upstream rule: invalid host or port"); + goto upstream_cleanup; + } + + up->host = safestrdup (host); + up->port = port; + + log_message (LOG_INFO, "Added upstream %s:%d for [default]", + host, port); + } + else if (host == NULL) + { + if (!domain || domain[0] == '\0') + { + log_message (LOG_WARNING, + "Nonsense no-upstream rule: empty domain"); + goto upstream_cleanup; + } + + ptr = strchr (domain, '/'); + if (ptr) + { + struct in_addr addrstruct; + + *ptr = '\0'; + if (inet_aton (domain, &addrstruct) != 0) + { + up->ip = ntohl (addrstruct.s_addr); + *ptr++ = '/'; + + if (strchr (ptr, '.')) + { + if (inet_aton (ptr, &addrstruct) != 0) + up->mask = ntohl (addrstruct.s_addr); + } + else + { + up->mask = ~((1 << (32 - atoi (ptr))) - 1); + } + } + } + else + { + up->domain = safestrdup (domain); + } + + log_message (LOG_INFO, "Added no-upstream for %s", domain); + } + else + { + if (!host || host[0] == '\0' || port < 1 || !domain || domain == '\0') + { + log_message (LOG_WARNING, + "Nonsense upstream rule: invalid parameters"); + goto upstream_cleanup; + } + + up->host = safestrdup (host); + up->port = port; + up->domain = safestrdup (domain); + + log_message (LOG_INFO, "Added upstream %s:%d for %s", + host, port, domain); + } + + if (!up->domain && !up->ip) + { /* always add default to end */ + struct upstream *tmp = config.upstream_list; + + while (tmp) + { + if (!tmp->domain && !tmp->ip) + { + log_message (LOG_WARNING, "Duplicate default upstream"); + goto upstream_cleanup; + } + + if (!tmp->next) + { + up->next = NULL; + tmp->next = up; + return; + } + + tmp = tmp->next; + } + } + + up->next = config.upstream_list; + config.upstream_list = up; + + return; + +upstream_cleanup: + safefree (up->host); + safefree (up->domain); + safefree (up); + + return; } /* * Check if a host is in the upstream list */ static struct upstream * -upstream_get(char *host) +upstream_get (char *host) { - struct upstream *up = config.upstream_list; - - in_addr_t my_ip = INADDR_NONE; - - while (up) { - if (up->domain) { - if (strcasecmp(host, up->domain) == 0) - break; /* exact match */ - - if (up->domain[0] == '.') { - char *dot = strchr(host, '.'); - - if (!dot && !up->domain[1]) - break; /* local host matches "." */ - - while (dot && strcasecmp(dot, up->domain)) - dot = strchr(dot + 1, '.'); - - if (dot) - break; /* subdomain match */ - } - } else if (up->ip) { - if (my_ip == INADDR_NONE) - my_ip = ntohl(inet_addr(host)); - - if ((my_ip & up->mask) == up->ip) - break; - } else { - break; /* No domain or IP, default upstream */ - } - - up = up->next; - } - - if (up && (!up->host || !up->port)) - up = NULL; - - if (up) - log_message(LOG_INFO, "Found proxy %s:%d for %s", - up->host, up->port, host); - else - log_message(LOG_INFO, "No proxy for %s", host); - - return up; + struct upstream *up = config.upstream_list; + + in_addr_t my_ip = INADDR_NONE; + + while (up) + { + if (up->domain) + { + if (strcasecmp (host, up->domain) == 0) + break; /* exact match */ + + if (up->domain[0] == '.') + { + char *dot = strchr (host, '.'); + + if (!dot && !up->domain[1]) + break; /* local host matches "." */ + + while (dot && strcasecmp (dot, up->domain)) + dot = strchr (dot + 1, '.'); + + if (dot) + break; /* subdomain match */ + } + } + else if (up->ip) + { + if (my_ip == INADDR_NONE) + my_ip = ntohl (inet_addr (host)); + + if ((my_ip & up->mask) == up->ip) + break; + } + else + { + break; /* No domain or IP, default upstream */ + } + + up = up->next; + } + + if (up && (!up->host || !up->port)) + up = NULL; + + if (up) + log_message (LOG_INFO, "Found proxy %s:%d for %s", + up->host, up->port, host); + else + log_message (LOG_INFO, "No proxy for %s", host); + + return up; } #endif @@ -468,22 +499,22 @@ upstream_get(char *host) * Create a connection for HTTP connections. */ static int -establish_http_connection(struct conn_s *connptr, struct request_s *request) +establish_http_connection (struct conn_s *connptr, struct request_s *request) { - char portbuff[7]; - - /* Build a port string if it's not a standard port */ - if (request->port != HTTP_PORT && request->port != HTTP_PORT_SSL) - snprintf(portbuff, 7, ":%u", request->port); - else - portbuff[0] = '\0'; - - return write_message(connptr->server_fd, - "%s %s HTTP/1.0\r\n" - "Host: %s%s\r\n" - "Connection: close\r\n", - request->method, request->path, - request->host, portbuff); + char portbuff[7]; + + /* Build a port string if it's not a standard port */ + if (request->port != HTTP_PORT && request->port != HTTP_PORT_SSL) + snprintf (portbuff, 7, ":%u", request->port); + else + portbuff[0] = '\0'; + + return write_message (connptr->server_fd, + "%s %s HTTP/1.0\r\n" + "Host: %s%s\r\n" + "Connection: close\r\n", + request->method, request->path, + request->host, portbuff); } /* @@ -497,12 +528,12 @@ establish_http_connection(struct conn_s *connptr, struct request_s *request) * connection. */ static inline int -send_ssl_response(struct conn_s *connptr) +send_ssl_response (struct conn_s *connptr) { - return write_message(connptr->client_fd, - "%s\r\n" - "%s\r\n" - "\r\n", SSL_CONNECTION_RESPONSE, PROXY_AGENT); + return write_message (connptr->client_fd, + "%s\r\n" + "%s\r\n" + "\r\n", SSL_CONNECTION_RESPONSE, PROXY_AGENT); } /* @@ -510,218 +541,239 @@ send_ssl_response(struct conn_s *connptr) * build a new request line. Finally connect to the remote server. */ static struct request_s * -process_request(struct conn_s *connptr, hashmap_t hashofheaders) +process_request (struct conn_s *connptr, hashmap_t hashofheaders) { - char *url; - struct request_s *request; - int ret; - size_t request_len; - - /* NULL out all the fields so frees don't cause segfaults. */ - request = safecalloc(1, sizeof(struct request_s)); - if (!request) - return NULL; - - request_len = strlen(connptr->request_line) + 1; - - request->method = safemalloc(request_len); - url = safemalloc(request_len); - request->protocol = safemalloc(request_len); - - if (!request->method || !url || !request->protocol) { - safefree(url); - free_request_struct(request); - - return NULL; - } - - ret = sscanf(connptr->request_line, "%[^ ] %[^ ] %[^ ]", - request->method, url, request->protocol); - if (ret == 2 && !strcasecmp(request->method, "GET")) { - request->protocol[0] = 0; - - /* Indicate that this is a HTTP/0.9 GET request */ - connptr->protocol.major = 0; - connptr->protocol.minor = 9; - } else if (ret == 3 && !strncasecmp(request->protocol, "HTTP/", 5)) { - /* - * Break apart the protocol and update the connection - * structure. - */ - ret = sscanf(request->protocol + 5, "%u.%u", - &connptr->protocol.major, - &connptr->protocol.minor); - - /* - * If the conversion doesn't succeed, drop down below and - * send the error to the user. - */ - if (ret != 2) - goto BAD_REQUEST_ERROR; - } else { - BAD_REQUEST_ERROR: - log_message(LOG_ERR, - "process_request: Bad Request on file descriptor %d", - connptr->client_fd); - indicate_http_error(connptr, 400, "Bad Request", - "detail", "Request has an invalid format", - "url", url, NULL); - - safefree(url); - free_request_struct(request); - - return NULL; - } - - if (!url) { - log_message(LOG_ERR, - "process_request: Null URL on file descriptor %d", - connptr->client_fd); - indicate_http_error(connptr, 400, "Bad Request", - "detail", "Request has an empty URL", - "url", url, NULL); - - safefree(url); - free_request_struct(request); - - return NULL; - } + char *url; + struct request_s *request; + int ret; + size_t request_len; + + /* NULL out all the fields so frees don't cause segfaults. */ + request = safecalloc (1, sizeof (struct request_s)); + if (!request) + return NULL; + + request_len = strlen (connptr->request_line) + 1; + + request->method = safemalloc (request_len); + url = safemalloc (request_len); + request->protocol = safemalloc (request_len); + + if (!request->method || !url || !request->protocol) + { + safefree (url); + free_request_struct (request); + + return NULL; + } + + ret = sscanf (connptr->request_line, "%[^ ] %[^ ] %[^ ]", + request->method, url, request->protocol); + if (ret == 2 && !strcasecmp (request->method, "GET")) + { + request->protocol[0] = 0; + + /* Indicate that this is a HTTP/0.9 GET request */ + connptr->protocol.major = 0; + connptr->protocol.minor = 9; + } + else if (ret == 3 && !strncasecmp (request->protocol, "HTTP/", 5)) + { + /* + * Break apart the protocol and update the connection + * structure. + */ + ret = sscanf (request->protocol + 5, "%u.%u", + &connptr->protocol.major, &connptr->protocol.minor); + + /* + * If the conversion doesn't succeed, drop down below and + * send the error to the user. + */ + if (ret != 2) + goto BAD_REQUEST_ERROR; + } + else + { + BAD_REQUEST_ERROR: + log_message (LOG_ERR, + "process_request: Bad Request on file descriptor %d", + connptr->client_fd); + indicate_http_error (connptr, 400, "Bad Request", + "detail", "Request has an invalid format", + "url", url, NULL); + + safefree (url); + free_request_struct (request); + + return NULL; + } + + if (!url) + { + log_message (LOG_ERR, + "process_request: Null URL on file descriptor %d", + connptr->client_fd); + indicate_http_error (connptr, 400, "Bad Request", + "detail", "Request has an empty URL", + "url", url, NULL); + + safefree (url); + free_request_struct (request); + + return NULL; + } #ifdef REVERSE_SUPPORT - if (config.reversepath_list != NULL) { - /* - * Rewrite the URL based on the reverse path. After calling - * reverse_rewrite_url "url" can be freed since we either - * have the newly rewritten URL, or something failed and - * we'll be closing anyway. - */ - char *reverse_url; - - reverse_url = reverse_rewrite_url(connptr, hashofheaders, url); - safefree(url); - - if (!reverse_url) { - free_request_struct(request); - return NULL; - } else { - url = reverse_url; - } - } + if (config.reversepath_list != NULL) + { + /* + * Rewrite the URL based on the reverse path. After calling + * reverse_rewrite_url "url" can be freed since we either + * have the newly rewritten URL, or something failed and + * we'll be closing anyway. + */ + char *reverse_url; + + reverse_url = reverse_rewrite_url (connptr, hashofheaders, url); + safefree (url); + + if (!reverse_url) + { + free_request_struct (request); + return NULL; + } + else + { + url = reverse_url; + } + } #endif - if (strncasecmp(url, "http://", 7) == 0 - || (UPSTREAM_CONFIGURED() && strncasecmp(url, "ftp://", 6) == 0)) { - char *skipped_type = strstr(url, "//") + 2; - - if (extract_http_url(skipped_type, request) < 0) { - indicate_http_error(connptr, 400, "Bad Request", - "detail", "Could not parse URL", - "url", url, NULL); - - safefree(url); - free_request_struct(request); - - return NULL; - } - } else if (strcmp(request->method, "CONNECT") == 0) { - if (extract_ssl_url(url, request) < 0) { - indicate_http_error(connptr, 400, "Bad Request", - "detail", "Could not parse URL", - "url", url, NULL); - - safefree(url); - free_request_struct(request); - - return NULL; - } - - /* Verify that the port in the CONNECT method is allowed */ - if (!check_allowed_connect_ports(request->port)) { - indicate_http_error(connptr, 403, "Access violation", - "detail", - "The CONNECT method not allowed " - "with the port you tried to use.", - "url", url, NULL); - log_message(LOG_INFO, - "Refused CONNECT method on port %d", - request->port); - - safefree(url); - free_request_struct(request); - - return NULL; - } - - connptr->connect_method = TRUE; - } else { + if (strncasecmp (url, "http://", 7) == 0 + || (UPSTREAM_CONFIGURED () && strncasecmp (url, "ftp://", 6) == 0)) + { + char *skipped_type = strstr (url, "//") + 2; + + if (extract_http_url (skipped_type, request) < 0) + { + indicate_http_error (connptr, 400, "Bad Request", + "detail", "Could not parse URL", + "url", url, NULL); + + safefree (url); + free_request_struct (request); + + return NULL; + } + } + else if (strcmp (request->method, "CONNECT") == 0) + { + if (extract_ssl_url (url, request) < 0) + { + indicate_http_error (connptr, 400, "Bad Request", + "detail", "Could not parse URL", + "url", url, NULL); + + safefree (url); + free_request_struct (request); + + return NULL; + } + + /* Verify that the port in the CONNECT method is allowed */ + if (!check_allowed_connect_ports (request->port)) + { + indicate_http_error (connptr, 403, "Access violation", + "detail", + "The CONNECT method not allowed " + "with the port you tried to use.", + "url", url, NULL); + log_message (LOG_INFO, + "Refused CONNECT method on port %d", request->port); + + safefree (url); + free_request_struct (request); + + return NULL; + } + + connptr->connect_method = TRUE; + } + else + { #ifdef TRANSPARENT_PROXY - if (!do_transparent_proxy(connptr, hashofheaders, request, &config, url)) { - safefree(url); - free_request_struct(request); - return NULL; - } + if (!do_transparent_proxy + (connptr, hashofheaders, request, &config, url)) + { + safefree (url); + free_request_struct (request); + return NULL; + } #else - indicate_http_error(connptr, 501, "Not Implemented", - "detail", "Unknown method or unsupported protocol.", - "url", url, NULL); - log_message(LOG_INFO, - "Unknown method (%s) or protocol (%s)", - request->method, url); - safefree(url); - free_request_struct(request); - return NULL; - + indicate_http_error (connptr, 501, "Not Implemented", + "detail", + "Unknown method or unsupported protocol.", "url", + url, NULL); + log_message (LOG_INFO, "Unknown method (%s) or protocol (%s)", + request->method, url); + safefree (url); + free_request_struct (request); + return NULL; + #endif - } + } #ifdef FILTER_ENABLE - /* - * Filter restricted domains/urls - */ - if (config.filter) { - if (config.filter_url) - ret = filter_url(url); - else - ret = filter_domain(request->host); - - if (ret) { - update_stats(STAT_DENIED); - - if (config.filter_url) - log_message(LOG_NOTICE, - "Proxying refused on filtered url \"%s\"", - url); - else - log_message(LOG_NOTICE, - "Proxying refused on filtered domain \"%s\"", - request->host); - - indicate_http_error(connptr, 403, "Filtered", - "detail", - "The request you made has been filtered", - "url", url, NULL); - - safefree(url); - free_request_struct(request); - - return NULL; - } - } + /* + * Filter restricted domains/urls + */ + if (config.filter) + { + if (config.filter_url) + ret = filter_url (url); + else + ret = filter_domain (request->host); + + if (ret) + { + update_stats (STAT_DENIED); + + if (config.filter_url) + log_message (LOG_NOTICE, + "Proxying refused on filtered url \"%s\"", url); + else + log_message (LOG_NOTICE, + "Proxying refused on filtered domain \"%s\"", + request->host); + + indicate_http_error (connptr, 403, "Filtered", + "detail", + "The request you made has been filtered", + "url", url, NULL); + + safefree (url); + free_request_struct (request); + + return NULL; + } + } #endif - safefree(url); + safefree (url); - /* - * Check to see if they're requesting the stat host - */ - if (config.stathost && strcmp(config.stathost, request->host) == 0) { - log_message(LOG_NOTICE, "Request for the stathost."); - connptr->show_stats = TRUE; + /* + * Check to see if they're requesting the stat host + */ + if (config.stathost && strcmp (config.stathost, request->host) == 0) + { + log_message (LOG_NOTICE, "Request for the stathost."); + connptr->show_stats = TRUE; - free_request_struct(request); - return NULL; - } + free_request_struct (request); + return NULL; + } - return request; + return request; } /* @@ -731,50 +783,52 @@ process_request(struct conn_s *connptr, hashmap_t hashofheaders) * - rjkaes */ static int -pull_client_data(struct conn_s *connptr, long int length) +pull_client_data (struct conn_s *connptr, long int length) { - char *buffer; - ssize_t len; - - buffer = safemalloc(min(MAXBUFFSIZE, length)); - if (!buffer) - return -1; - - do { - len = safe_read(connptr->client_fd, buffer, - min(MAXBUFFSIZE, length)); - if (len <= 0) - goto ERROR_EXIT; - - if (!connptr->error_variables) { - if (safe_write(connptr->server_fd, buffer, len) < 0) - goto ERROR_EXIT; - } - - length -= len; - } while (length > 0); - - /* - * BUG FIX: Internet Explorer will leave two bytes (carriage - * return and line feed) at the end of a POST message. These - * need to be eaten for tinyproxy to work correctly. - */ - socket_nonblocking(connptr->client_fd); - len = recv(connptr->client_fd, buffer, 2, MSG_PEEK); - socket_blocking(connptr->client_fd); - - if (len < 0 && errno != EAGAIN) - goto ERROR_EXIT; - - if (len == 2 && CHECK_CRLF(buffer, len)) - read(connptr->client_fd, buffer, 2); - - safefree(buffer); - return 0; - - ERROR_EXIT: - safefree(buffer); - return -1; + char *buffer; + ssize_t len; + + buffer = safemalloc (min (MAXBUFFSIZE, length)); + if (!buffer) + return -1; + + do + { + len = safe_read (connptr->client_fd, buffer, min (MAXBUFFSIZE, length)); + if (len <= 0) + goto ERROR_EXIT; + + if (!connptr->error_variables) + { + if (safe_write (connptr->server_fd, buffer, len) < 0) + goto ERROR_EXIT; + } + + length -= len; + } + while (length > 0); + + /* + * BUG FIX: Internet Explorer will leave two bytes (carriage + * return and line feed) at the end of a POST message. These + * need to be eaten for tinyproxy to work correctly. + */ + socket_nonblocking (connptr->client_fd); + len = recv (connptr->client_fd, buffer, 2, MSG_PEEK); + socket_blocking (connptr->client_fd); + + if (len < 0 && errno != EAGAIN) + goto ERROR_EXIT; + + if (len == 2 && CHECK_CRLF (buffer, len)) + read (connptr->client_fd, buffer, 2); + + safefree (buffer); + return 0; + +ERROR_EXIT: + safefree (buffer); + return -1; } #ifdef XTINYPROXY_ENABLE @@ -784,13 +838,13 @@ pull_client_data(struct conn_s *connptr, long int length) * -rjkaes */ static inline int -add_xtinyproxy_header(struct conn_s *connptr) +add_xtinyproxy_header (struct conn_s *connptr) { - assert(connptr && connptr->server_fd >= 0); - return write_message(connptr->server_fd, - "X-Tinyproxy: %s\r\n", connptr->client_ip_addr); + assert (connptr && connptr->server_fd >= 0); + return write_message (connptr->server_fd, + "X-Tinyproxy: %s\r\n", connptr->client_ip_addr); } -#endif /* XTINYPROXY */ +#endif /* XTINYPROXY */ /* * Take a complete header line and break it apart (into a key and the data.) @@ -798,82 +852,86 @@ add_xtinyproxy_header(struct conn_s *connptr) * can be retrieved and manipulated later. */ static inline int -add_header_to_connection(hashmap_t hashofheaders, char *header, size_t len) +add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len) { - char *sep; + char *sep; - /* Get rid of the new line and return at the end */ - len -= chomp(header, len); + /* Get rid of the new line and return at the end */ + len -= chomp (header, len); - sep = strchr(header, ':'); - if (!sep) - return -1; + sep = strchr (header, ':'); + if (!sep) + return -1; - /* Blank out colons, spaces, and tabs. */ - while (*sep == ':' || *sep == ' ' || *sep == '\t') - *sep++ = '\0'; + /* Blank out colons, spaces, and tabs. */ + while (*sep == ':' || *sep == ' ' || *sep == '\t') + *sep++ = '\0'; - /* Calculate the new length of just the data */ - len -= sep - header - 1; + /* Calculate the new length of just the data */ + len -= sep - header - 1; - return hashmap_insert(hashofheaders, header, sep, len); + return hashmap_insert (hashofheaders, header, sep, len); } /* * Read all the headers from the stream */ static int -get_all_headers(int fd, hashmap_t hashofheaders) +get_all_headers (int fd, hashmap_t hashofheaders) { - char *header; - ssize_t len; - unsigned int double_cgi = FALSE; /* boolean */ - - assert(fd >= 0); - assert(hashofheaders != NULL); - - for (;;) { - if ((len = readline(fd, &header)) <= 0) { - safefree(header); - return -1; - } - - /* - * If we received just a CR LF on a line, the headers are - * finished. - */ - if (CHECK_CRLF(header, len)) { - safefree(header); - return 0; - } - - /* - * BUG FIX: The following code detects a "Double CGI" - * situation so that we can handle the nonconforming system. - * This problem was found when accessing cgi.ebay.com, and it - * turns out to be a wider spread problem as well. - * - * If "Double CGI" is in effect, duplicate headers are - * ignored. - * - * FIXME: Might need to change this to a more robust check. - */ - if (strncasecmp(header, "HTTP/", 5) == 0) { - double_cgi = TRUE; - - safefree(header); - continue; - } - - if (!double_cgi - && add_header_to_connection(hashofheaders, header, - len) < 0) { - safefree(header); - return -1; - } - - safefree(header); - } + char *header; + ssize_t len; + unsigned int double_cgi = FALSE; /* boolean */ + + assert (fd >= 0); + assert (hashofheaders != NULL); + + for (;;) + { + if ((len = readline (fd, &header)) <= 0) + { + safefree (header); + return -1; + } + + /* + * If we received just a CR LF on a line, the headers are + * finished. + */ + if (CHECK_CRLF (header, len)) + { + safefree (header); + return 0; + } + + /* + * BUG FIX: The following code detects a "Double CGI" + * situation so that we can handle the nonconforming system. + * This problem was found when accessing cgi.ebay.com, and it + * turns out to be a wider spread problem as well. + * + * If "Double CGI" is in effect, duplicate headers are + * ignored. + * + * FIXME: Might need to change this to a more robust check. + */ + if (strncasecmp (header, "HTTP/", 5) == 0) + { + double_cgi = TRUE; + + safefree (header); + continue; + } + + if (!double_cgi + && add_header_to_connection (hashofheaders, header, len) < 0) + { + safefree (header); + return -1; + } + + safefree (header); + } } /* @@ -881,53 +939,53 @@ get_all_headers(int fd, hashmap_t hashofheaders) * and Proxy-Connection headers. */ static int -remove_connection_headers(hashmap_t hashofheaders) +remove_connection_headers (hashmap_t hashofheaders) { - static char *headers[] = { - "connection", - "proxy-connection" - }; - - char *data; - char *ptr; - ssize_t len; - int i; - - for (i = 0; i != (sizeof(headers) / sizeof(char *)); ++i) { - /* Look for the connection header. If it's not found, return. */ - len = - hashmap_entry_by_key(hashofheaders, headers[i], - (void **)&data); - if (len <= 0) - return 0; - - /* - * Go through the data line and replace any special characters - * with a NULL. - */ - ptr = data; - while ((ptr = strpbrk(ptr, "()<>@,;:\\\"/[]?={} \t"))) - *ptr++ = '\0'; - - /* - * All the tokens are separated by NULLs. Now go through the - * token and remove them from the hashofheaders. - */ - ptr = data; - while (ptr < data + len) { - hashmap_remove(hashofheaders, ptr); - - /* Advance ptr to the next token */ - ptr += strlen(ptr) + 1; - while (ptr < data + len && *ptr == '\0') - ptr++; - } - - /* Now remove the connection header it self. */ - hashmap_remove(hashofheaders, headers[i]); - } - - return 0; + static char *headers[] = { + "connection", + "proxy-connection" + }; + + char *data; + char *ptr; + ssize_t len; + int i; + + for (i = 0; i != (sizeof (headers) / sizeof (char *)); ++i) + { + /* Look for the connection header. If it's not found, return. */ + len = hashmap_entry_by_key (hashofheaders, headers[i], (void **) &data); + if (len <= 0) + return 0; + + /* + * Go through the data line and replace any special characters + * with a NULL. + */ + ptr = data; + while ((ptr = strpbrk (ptr, "()<>@,;:\\\"/[]?={} \t"))) + *ptr++ = '\0'; + + /* + * All the tokens are separated by NULLs. Now go through the + * token and remove them from the hashofheaders. + */ + ptr = data; + while (ptr < data + len) + { + hashmap_remove (hashofheaders, ptr); + + /* Advance ptr to the next token */ + ptr += strlen (ptr) + 1; + while (ptr < data + len && *ptr == '\0') + ptr++; + } + + /* Now remove the connection header it self. */ + hashmap_remove (hashofheaders, headers[i]); + } + + return 0; } /* @@ -935,19 +993,18 @@ remove_connection_headers(hashmap_t hashofheaders) * a negative number. */ static long -get_content_length(hashmap_t hashofheaders) +get_content_length (hashmap_t hashofheaders) { - ssize_t len; - char *data; - long content_length = -1; + ssize_t len; + char *data; + long content_length = -1; - len = - hashmap_entry_by_key(hashofheaders, "content-length", - (void **)&data); - if (len > 0) - content_length = atol(data); + len = + hashmap_entry_by_key (hashofheaders, "content-length", (void **) &data); + if (len > 0) + content_length = atol (data); - return content_length; + return content_length; } /* @@ -958,39 +1015,44 @@ get_content_length(hashmap_t hashofheaders) * purposes. */ static int -write_via_header(int fd, hashmap_t hashofheaders, - unsigned int major, unsigned int minor) +write_via_header (int fd, hashmap_t hashofheaders, + unsigned int major, unsigned int minor) { - ssize_t len; - char hostname[512]; - char *data; - int ret; - - if (config.via_proxy_name) { - strlcpy(hostname, config.via_proxy_name, sizeof(hostname)); - } else if (gethostname(hostname, sizeof(hostname)) < 0) { - strcpy(hostname, "unknown"); - } - - /* - * See if there is a "Via" header. If so, again we need to do a bit - * of processing. - */ - len = hashmap_entry_by_key(hashofheaders, "via", (void **)&data); - if (len > 0) { - ret = write_message(fd, - "Via: %s, %hu.%hu %s (%s/%s)\r\n", - data, - major, minor, hostname, PACKAGE, VERSION); - - hashmap_remove(hashofheaders, "via"); - } else { - ret = write_message(fd, - "Via: %hu.%hu %s (%s/%s)\r\n", - major, minor, hostname, PACKAGE, VERSION); - } - - return ret; + ssize_t len; + char hostname[512]; + char *data; + int ret; + + if (config.via_proxy_name) + { + strlcpy (hostname, config.via_proxy_name, sizeof (hostname)); + } + else if (gethostname (hostname, sizeof (hostname)) < 0) + { + strcpy (hostname, "unknown"); + } + + /* + * See if there is a "Via" header. If so, again we need to do a bit + * of processing. + */ + len = hashmap_entry_by_key (hashofheaders, "via", (void **) &data); + if (len > 0) + { + ret = write_message (fd, + "Via: %s, %hu.%hu %s (%s/%s)\r\n", + data, major, minor, hostname, PACKAGE, VERSION); + + hashmap_remove (hashofheaders, "via"); + } + else + { + ret = write_message (fd, + "Via: %hu.%hu %s (%s/%s)\r\n", + major, minor, hostname, PACKAGE, VERSION); + } + + return ret; } /* @@ -1005,110 +1067,113 @@ write_via_header(int fd, hashmap_t hashofheaders, * - rjkaes */ static int -process_client_headers(struct conn_s *connptr, hashmap_t hashofheaders) +process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) { - static char *skipheaders[] = { - "host", - "keep-alive", - "proxy-connection", - "te", - "trailers", - "transfer-encoding", - "upgrade" - }; - int i; - hashmap_iter iter; - int ret = 0; - - char *data, *header; - - /* - * Don't send headers if there's already an error, if the request was - * a stats request, or if this was a CONNECT method (unless upstream - * proxy is in use.) - */ - if (connptr->server_fd == -1 || connptr->show_stats - || (connptr->connect_method && (connptr->upstream_proxy == NULL))) { - log_message(LOG_INFO, - "Not sending client headers to remote machine"); - return 0; - } - - /* - * See if there is a "Content-Length" header. If so, again we need - * to do a bit of processing. - */ - connptr->content_length.client = get_content_length(hashofheaders); - - /* - * See if there is a "Connection" header. If so, we need to do a bit - * of processing. :) - */ - remove_connection_headers(hashofheaders); - - /* - * Delete the headers listed in the skipheaders list - */ - for (i = 0; i != (sizeof(skipheaders) / sizeof(char *)); i++) { - hashmap_remove(hashofheaders, skipheaders[i]); - } - - /* Send, or add the Via header */ - ret = write_via_header(connptr->server_fd, hashofheaders, - connptr->protocol.major, - connptr->protocol.minor); - if (ret < 0) { - indicate_http_error(connptr, 503, - "Could not send data to remote server", - "detail", - "A network error occurred while trying to write data to the remote web server.", - NULL); - goto PULL_CLIENT_DATA; - } - - /* - * Output all the remaining headers to the remote machine. - */ - iter = hashmap_first(hashofheaders); - if (iter >= 0) { - for (; !hashmap_is_end(hashofheaders, iter); ++iter) { - hashmap_return_entry(hashofheaders, - iter, &data, (void **)&header); - - if (!is_anonymous_enabled() - || anonymous_search(data) > 0) { - ret = - write_message(connptr->server_fd, - "%s: %s\r\n", data, header); - if (ret < 0) { - indicate_http_error(connptr, 503, - "Could not send data to remote server", - "detail", - "A network error occurred while trying to write data to the remote web server.", - NULL); - goto PULL_CLIENT_DATA; - } - } - } - } + static char *skipheaders[] = { + "host", + "keep-alive", + "proxy-connection", + "te", + "trailers", + "transfer-encoding", + "upgrade" + }; + int i; + hashmap_iter iter; + int ret = 0; + + char *data, *header; + + /* + * Don't send headers if there's already an error, if the request was + * a stats request, or if this was a CONNECT method (unless upstream + * proxy is in use.) + */ + if (connptr->server_fd == -1 || connptr->show_stats + || (connptr->connect_method && (connptr->upstream_proxy == NULL))) + { + log_message (LOG_INFO, "Not sending client headers to remote machine"); + return 0; + } + + /* + * See if there is a "Content-Length" header. If so, again we need + * to do a bit of processing. + */ + connptr->content_length.client = get_content_length (hashofheaders); + + /* + * See if there is a "Connection" header. If so, we need to do a bit + * of processing. :) + */ + remove_connection_headers (hashofheaders); + + /* + * Delete the headers listed in the skipheaders list + */ + for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) + { + hashmap_remove (hashofheaders, skipheaders[i]); + } + + /* Send, or add the Via header */ + ret = write_via_header (connptr->server_fd, hashofheaders, + connptr->protocol.major, connptr->protocol.minor); + if (ret < 0) + { + indicate_http_error (connptr, 503, + "Could not send data to remote server", + "detail", + "A network error occurred while trying to write data to the remote web server.", + NULL); + goto PULL_CLIENT_DATA; + } + + /* + * Output all the remaining headers to the remote machine. + */ + iter = hashmap_first (hashofheaders); + if (iter >= 0) + { + for (; !hashmap_is_end (hashofheaders, iter); ++iter) + { + hashmap_return_entry (hashofheaders, + iter, &data, (void **) &header); + + if (!is_anonymous_enabled () || anonymous_search (data) > 0) + { + ret = + write_message (connptr->server_fd, + "%s: %s\r\n", data, header); + if (ret < 0) + { + indicate_http_error (connptr, 503, + "Could not send data to remote server", + "detail", + "A network error occurred while trying to write data to the remote web server.", + NULL); + goto PULL_CLIENT_DATA; + } + } + } + } #if defined(XTINYPROXY_ENABLE) - if (config.my_domain) - add_xtinyproxy_header(connptr); + if (config.my_domain) + add_xtinyproxy_header (connptr); #endif - /* Write the final "blank" line to signify the end of the headers */ - if (safe_write(connptr->server_fd, "\r\n", 2) < 0) - return -1; - - /* - * Spin here pulling the data from the client. - */ - PULL_CLIENT_DATA: - if (connptr->content_length.client > 0) - return pull_client_data(connptr, - connptr->content_length.client); - else - return ret; + /* Write the final "blank" line to signify the end of the headers */ + if (safe_write (connptr->server_fd, "\r\n", 2) < 0) + return -1; + + /* + * Spin here pulling the data from the client. + */ +PULL_CLIENT_DATA: + if (connptr->content_length.client > 0) + return pull_client_data (connptr, connptr->content_length.client); + else + return ret; } /* @@ -1116,184 +1181,192 @@ process_client_headers(struct conn_s *connptr, hashmap_t hashofheaders) * server. */ static int -process_server_headers(struct conn_s *connptr) +process_server_headers (struct conn_s *connptr) { - static char *skipheaders[] = { - "keep-alive", - "proxy-authenticate", - "proxy-authorization", - "proxy-connection", - "transfer-encoding", - }; - - char *response_line; - - hashmap_t hashofheaders; - hashmap_iter iter; - char *data, *header; - ssize_t len; - int i; - int ret; + static char *skipheaders[] = { + "keep-alive", + "proxy-authenticate", + "proxy-authorization", + "proxy-connection", + "transfer-encoding", + }; + + char *response_line; + + hashmap_t hashofheaders; + hashmap_iter iter; + char *data, *header; + ssize_t len; + int i; + int ret; #ifdef REVERSE_SUPPORT - struct reversepath *reverse = config.reversepath_list; + struct reversepath *reverse = config.reversepath_list; #endif - /* Get the response line from the remote server. */ - retry: - len = readline(connptr->server_fd, &response_line); - if (len <= 0) - return -1; - - /* - * Strip the new line and character return from the string. - */ - if (chomp(response_line, len) == len) { - /* - * If the number of characters removed is the same as the - * length then it was a blank line. Free the buffer and - * try again (since we're looking for a request line.) - */ - safefree(response_line); - goto retry; - } - - hashofheaders = hashmap_create(HEADER_BUCKETS); - if (!hashofheaders) { - safefree(response_line); - return -1; - } - - /* - * Get all the headers from the remote server in a big hash - */ - if (get_all_headers(connptr->server_fd, hashofheaders) < 0) { - log_message(LOG_WARNING, - "Could not retrieve all the headers from the remote server."); - hashmap_delete(hashofheaders); - safefree(response_line); - - indicate_http_error(connptr, 503, - "Could not retrieve all the headers", - "detail", - PACKAGE - " was unable to retrieve and process headers from the remote web server.", - NULL); - return -1; - } - - /* - * At this point we've received the response line and all the - * headers. However, if this is a simple HTTP/0.9 request we - * CAN NOT send any of that information back to the client. - * Instead we'll free all the memory and return. - */ - if (connptr->protocol.major < 1) { - hashmap_delete(hashofheaders); - safefree(response_line); - return 0; - } - - /* Send the saved response line first */ - ret = write_message(connptr->client_fd, "%s\r\n", response_line); - safefree(response_line); - if (ret < 0) - goto ERROR_EXIT; - - /* - * If there is a "Content-Length" header, retrieve the information - * from it for later use. - */ - connptr->content_length.server = get_content_length(hashofheaders); - - /* - * See if there is a connection header. If so, we need to to a bit of - * processing. - */ - remove_connection_headers(hashofheaders); - - /* - * Delete the headers listed in the skipheaders list - */ - for (i = 0; i != (sizeof(skipheaders) / sizeof(char *)); i++) { - hashmap_remove(hashofheaders, skipheaders[i]); - } - - /* Send, or add the Via header */ - ret = write_via_header(connptr->client_fd, hashofheaders, - connptr->protocol.major, - connptr->protocol.minor); - if (ret < 0) - goto ERROR_EXIT; + /* Get the response line from the remote server. */ +retry: + len = readline (connptr->server_fd, &response_line); + if (len <= 0) + return -1; + + /* + * Strip the new line and character return from the string. + */ + if (chomp (response_line, len) == len) + { + /* + * If the number of characters removed is the same as the + * length then it was a blank line. Free the buffer and + * try again (since we're looking for a request line.) + */ + safefree (response_line); + goto retry; + } + + hashofheaders = hashmap_create (HEADER_BUCKETS); + if (!hashofheaders) + { + safefree (response_line); + return -1; + } + + /* + * Get all the headers from the remote server in a big hash + */ + if (get_all_headers (connptr->server_fd, hashofheaders) < 0) + { + log_message (LOG_WARNING, + "Could not retrieve all the headers from the remote server."); + hashmap_delete (hashofheaders); + safefree (response_line); + + indicate_http_error (connptr, 503, + "Could not retrieve all the headers", + "detail", + PACKAGE + " was unable to retrieve and process headers from the remote web server.", + NULL); + return -1; + } + + /* + * At this point we've received the response line and all the + * headers. However, if this is a simple HTTP/0.9 request we + * CAN NOT send any of that information back to the client. + * Instead we'll free all the memory and return. + */ + if (connptr->protocol.major < 1) + { + hashmap_delete (hashofheaders); + safefree (response_line); + return 0; + } + + /* Send the saved response line first */ + ret = write_message (connptr->client_fd, "%s\r\n", response_line); + safefree (response_line); + if (ret < 0) + goto ERROR_EXIT; + + /* + * If there is a "Content-Length" header, retrieve the information + * from it for later use. + */ + connptr->content_length.server = get_content_length (hashofheaders); + + /* + * See if there is a connection header. If so, we need to to a bit of + * processing. + */ + remove_connection_headers (hashofheaders); + + /* + * Delete the headers listed in the skipheaders list + */ + for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) + { + hashmap_remove (hashofheaders, skipheaders[i]); + } + + /* Send, or add the Via header */ + ret = write_via_header (connptr->client_fd, hashofheaders, + connptr->protocol.major, connptr->protocol.minor); + if (ret < 0) + goto ERROR_EXIT; #ifdef REVERSE_SUPPORT - /* Write tracking cookie for the magical reverse proxy path hack */ - if (config.reversemagic && connptr->reversepath) { - ret = write_message(connptr->client_fd, - "Set-Cookie: " REVERSE_COOKIE - "=%s; path=/\r\n", connptr->reversepath); - if (ret < 0) - goto ERROR_EXIT; - } - - /* Rewrite the HTTP redirect if needed */ - if (config.reversebaseurl && - hashmap_entry_by_key(hashofheaders, "location", - (void **)&header) > 0) { - - /* Look for a matching entry in the reversepath list */ - while (reverse) { - if (strncasecmp(header, - reverse->url, - (len = strlen(reverse->url))) == 0) - break; - reverse = reverse->next; - } - - if (reverse) { - ret = - write_message(connptr->client_fd, - "Location: %s%s%s\r\n", - config.reversebaseurl, - (reverse->path + 1), (header + len)); - if (ret < 0) - goto ERROR_EXIT; - - log_message(LOG_INFO, - "Rewriting HTTP redirect: %s -> %s%s%s", - header, config.reversebaseurl, - (reverse->path + 1), (header + len)); - hashmap_remove(hashofheaders, "location"); - } - } + /* Write tracking cookie for the magical reverse proxy path hack */ + if (config.reversemagic && connptr->reversepath) + { + ret = write_message (connptr->client_fd, + "Set-Cookie: " REVERSE_COOKIE + "=%s; path=/\r\n", connptr->reversepath); + if (ret < 0) + goto ERROR_EXIT; + } + + /* Rewrite the HTTP redirect if needed */ + if (config.reversebaseurl && + hashmap_entry_by_key (hashofheaders, "location", (void **) &header) > 0) + { + + /* Look for a matching entry in the reversepath list */ + while (reverse) + { + if (strncasecmp (header, + reverse->url, (len = strlen (reverse->url))) == 0) + break; + reverse = reverse->next; + } + + if (reverse) + { + ret = + write_message (connptr->client_fd, + "Location: %s%s%s\r\n", + config.reversebaseurl, + (reverse->path + 1), (header + len)); + if (ret < 0) + goto ERROR_EXIT; + + log_message (LOG_INFO, + "Rewriting HTTP redirect: %s -> %s%s%s", + header, config.reversebaseurl, + (reverse->path + 1), (header + len)); + hashmap_remove (hashofheaders, "location"); + } + } #endif - /* - * All right, output all the remaining headers to the client. - */ - iter = hashmap_first(hashofheaders); - if (iter >= 0) { - for (; !hashmap_is_end(hashofheaders, iter); ++iter) { - hashmap_return_entry(hashofheaders, - iter, &data, (void **)&header); - - ret = write_message(connptr->client_fd, - "%s: %s\r\n", data, header); - if (ret < 0) - goto ERROR_EXIT; - } - } - hashmap_delete(hashofheaders); - - /* Write the final blank line to signify the end of the headers */ - if (safe_write(connptr->client_fd, "\r\n", 2) < 0) - return -1; - - return 0; - - ERROR_EXIT: - hashmap_delete(hashofheaders); - return -1; + /* + * All right, output all the remaining headers to the client. + */ + iter = hashmap_first (hashofheaders); + if (iter >= 0) + { + for (; !hashmap_is_end (hashofheaders, iter); ++iter) + { + hashmap_return_entry (hashofheaders, + iter, &data, (void **) &header); + + ret = write_message (connptr->client_fd, + "%s: %s\r\n", data, header); + if (ret < 0) + goto ERROR_EXIT; + } + } + hashmap_delete (hashofheaders); + + /* Write the final blank line to signify the end of the headers */ + if (safe_write (connptr->client_fd, "\r\n", 2) < 0) + return -1; + + return 0; + +ERROR_EXIT: + hashmap_delete (hashofheaders); + return -1; } /* @@ -1305,186 +1378,204 @@ process_server_headers(struct conn_s *connptr) * - rjkaes */ static void -relay_connection(struct conn_s *connptr) +relay_connection (struct conn_s *connptr) { - fd_set rset, wset; - struct timeval tv; - time_t last_access; - int ret; - double tdiff; - int maxfd = max(connptr->client_fd, connptr->server_fd) + 1; - ssize_t bytes_received; - - socket_nonblocking(connptr->client_fd); - socket_nonblocking(connptr->server_fd); - - last_access = time(NULL); - - for (;;) { - FD_ZERO(&rset); - FD_ZERO(&wset); - - tv.tv_sec = - config.idletimeout - difftime(time(NULL), last_access); - tv.tv_usec = 0; - - if (buffer_size(connptr->sbuffer) > 0) - FD_SET(connptr->client_fd, &wset); - if (buffer_size(connptr->cbuffer) > 0) - FD_SET(connptr->server_fd, &wset); - if (buffer_size(connptr->sbuffer) < MAXBUFFSIZE) - FD_SET(connptr->server_fd, &rset); - if (buffer_size(connptr->cbuffer) < MAXBUFFSIZE) - FD_SET(connptr->client_fd, &rset); - - ret = select(maxfd, &rset, &wset, NULL, &tv); - - if (ret == 0) { - tdiff = difftime(time(NULL), last_access); - if (tdiff > config.idletimeout) { - log_message(LOG_INFO, - "Idle Timeout (after select) as %g > %u.", - tdiff, config.idletimeout); - return; - } else { - continue; - } - } else if (ret < 0) { - log_message(LOG_ERR, - "relay_connection: select() error \"%s\". Closing connection (client_fd:%d, server_fd:%d)", - strerror(errno), connptr->client_fd, - connptr->server_fd); - return; - } else { - /* - * All right, something was actually selected so mark it. - */ - last_access = time(NULL); - } - - if (FD_ISSET(connptr->server_fd, &rset)) { - bytes_received = - read_buffer(connptr->server_fd, connptr->sbuffer); - if (bytes_received < 0) - break; - - connptr->content_length.server -= bytes_received; - if (connptr->content_length.server == 0) - break; - } - if (FD_ISSET(connptr->client_fd, &rset) - && read_buffer(connptr->client_fd, connptr->cbuffer) < 0) { - break; - } - if (FD_ISSET(connptr->server_fd, &wset) - && write_buffer(connptr->server_fd, connptr->cbuffer) < 0) { - break; - } - if (FD_ISSET(connptr->client_fd, &wset) - && write_buffer(connptr->client_fd, connptr->sbuffer) < 0) { - break; - } - } - - /* - * Here the server has closed the connection... write the - * remainder to the client and then exit. - */ - socket_blocking(connptr->client_fd); - while (buffer_size(connptr->sbuffer) > 0) { - if (write_buffer(connptr->client_fd, connptr->sbuffer) < 0) - break; - } - shutdown(connptr->client_fd, SHUT_WR); - - /* - * Try to send any remaining data to the server if we can. - */ - socket_blocking(connptr->server_fd); - while (buffer_size(connptr->cbuffer) > 0) { - if (write_buffer(connptr->server_fd, connptr->cbuffer) < 0) - break; - } - - return; + fd_set rset, wset; + struct timeval tv; + time_t last_access; + int ret; + double tdiff; + int maxfd = max (connptr->client_fd, connptr->server_fd) + 1; + ssize_t bytes_received; + + socket_nonblocking (connptr->client_fd); + socket_nonblocking (connptr->server_fd); + + last_access = time (NULL); + + for (;;) + { + FD_ZERO (&rset); + FD_ZERO (&wset); + + tv.tv_sec = config.idletimeout - difftime (time (NULL), last_access); + tv.tv_usec = 0; + + if (buffer_size (connptr->sbuffer) > 0) + FD_SET (connptr->client_fd, &wset); + if (buffer_size (connptr->cbuffer) > 0) + FD_SET (connptr->server_fd, &wset); + if (buffer_size (connptr->sbuffer) < MAXBUFFSIZE) + FD_SET (connptr->server_fd, &rset); + if (buffer_size (connptr->cbuffer) < MAXBUFFSIZE) + FD_SET (connptr->client_fd, &rset); + + ret = select (maxfd, &rset, &wset, NULL, &tv); + + if (ret == 0) + { + tdiff = difftime (time (NULL), last_access); + if (tdiff > config.idletimeout) + { + log_message (LOG_INFO, + "Idle Timeout (after select) as %g > %u.", + tdiff, config.idletimeout); + return; + } + else + { + continue; + } + } + else if (ret < 0) + { + log_message (LOG_ERR, + "relay_connection: select() error \"%s\". Closing connection (client_fd:%d, server_fd:%d)", + strerror (errno), connptr->client_fd, + connptr->server_fd); + return; + } + else + { + /* + * All right, something was actually selected so mark it. + */ + last_access = time (NULL); + } + + if (FD_ISSET (connptr->server_fd, &rset)) + { + bytes_received = read_buffer (connptr->server_fd, connptr->sbuffer); + if (bytes_received < 0) + break; + + connptr->content_length.server -= bytes_received; + if (connptr->content_length.server == 0) + break; + } + if (FD_ISSET (connptr->client_fd, &rset) + && read_buffer (connptr->client_fd, connptr->cbuffer) < 0) + { + break; + } + if (FD_ISSET (connptr->server_fd, &wset) + && write_buffer (connptr->server_fd, connptr->cbuffer) < 0) + { + break; + } + if (FD_ISSET (connptr->client_fd, &wset) + && write_buffer (connptr->client_fd, connptr->sbuffer) < 0) + { + break; + } + } + + /* + * Here the server has closed the connection... write the + * remainder to the client and then exit. + */ + socket_blocking (connptr->client_fd); + while (buffer_size (connptr->sbuffer) > 0) + { + if (write_buffer (connptr->client_fd, connptr->sbuffer) < 0) + break; + } + shutdown (connptr->client_fd, SHUT_WR); + + /* + * Try to send any remaining data to the server if we can. + */ + socket_blocking (connptr->server_fd); + while (buffer_size (connptr->cbuffer) > 0) + { + if (write_buffer (connptr->server_fd, connptr->cbuffer) < 0) + break; + } + + return; } /* * Establish a connection to the upstream proxy server. */ static int -connect_to_upstream(struct conn_s *connptr, struct request_s *request) +connect_to_upstream (struct conn_s *connptr, struct request_s *request) { #ifndef UPSTREAM_SUPPORT - /* - * This function does nothing if upstream support was not compiled - * into tinyproxy. - */ - return -1; + /* + * This function does nothing if upstream support was not compiled + * into tinyproxy. + */ + return -1; #else - char *combined_string; - int len; - - struct upstream *cur_upstream = connptr->upstream_proxy; - - if (!cur_upstream) { - log_message(LOG_WARNING, - "No upstream proxy defined for %s.", request->host); - indicate_http_error(connptr, 404, - "Unable to connect to upstream proxy."); - return -1; - } - - connptr->server_fd = - opensock(cur_upstream->host, cur_upstream->port, - connptr->server_ip_addr); - - if (connptr->server_fd < 0) { - log_message(LOG_WARNING, - "Could not connect to upstream proxy."); - indicate_http_error(connptr, 404, - "Unable to connect to upstream proxy", - "detail", - "A network error occurred while trying to connect to the upstream web proxy.", - NULL); - return -1; - } - - log_message(LOG_CONN, - "Established connection to upstream proxy \"%s\" using file descriptor %d.", - cur_upstream->host, connptr->server_fd); - - /* - * We need to re-write the "path" part of the request so that we - * can reuse the establish_http_connection() function. It expects a - * method and path. - */ - if (connptr->connect_method) { - len = strlen(request->host) + 7; - - combined_string = safemalloc(len); - if (!combined_string) { - return -1; - } - - snprintf(combined_string, len, "%s:%d", request->host, - request->port); - } else { - len = strlen(request->host) + strlen(request->path) + 14; - combined_string = safemalloc(len); - if (!combined_string) { - return -1; - } - - snprintf(combined_string, len, "http://%s:%d%s", request->host, - request->port, request->path); - } - - if (request->path) - safefree(request->path); - request->path = combined_string; - - return establish_http_connection(connptr, request); + char *combined_string; + int len; + + struct upstream *cur_upstream = connptr->upstream_proxy; + + if (!cur_upstream) + { + log_message (LOG_WARNING, + "No upstream proxy defined for %s.", request->host); + indicate_http_error (connptr, 404, + "Unable to connect to upstream proxy."); + return -1; + } + + connptr->server_fd = + opensock (cur_upstream->host, cur_upstream->port, + connptr->server_ip_addr); + + if (connptr->server_fd < 0) + { + log_message (LOG_WARNING, "Could not connect to upstream proxy."); + indicate_http_error (connptr, 404, + "Unable to connect to upstream proxy", + "detail", + "A network error occurred while trying to connect to the upstream web proxy.", + NULL); + return -1; + } + + log_message (LOG_CONN, + "Established connection to upstream proxy \"%s\" using file descriptor %d.", + cur_upstream->host, connptr->server_fd); + + /* + * We need to re-write the "path" part of the request so that we + * can reuse the establish_http_connection() function. It expects a + * method and path. + */ + if (connptr->connect_method) + { + len = strlen (request->host) + 7; + + combined_string = safemalloc (len); + if (!combined_string) + { + return -1; + } + + snprintf (combined_string, len, "%s:%d", request->host, request->port); + } + else + { + len = strlen (request->host) + strlen (request->path) + 14; + combined_string = safemalloc (len); + if (!combined_string) + { + return -1; + } + + snprintf (combined_string, len, "http://%s:%d%s", request->host, + request->port, request->path); + } + + if (request->path) + safefree (request->path); + request->path = combined_string; + + return establish_http_connection (connptr, request); #endif } @@ -1498,168 +1589,190 @@ connect_to_upstream(struct conn_s *connptr, struct request_s *request) * - rjkaes */ void -handle_connection(int fd) +handle_connection (int fd) { - struct conn_s *connptr; - struct request_s *request = NULL; - hashmap_t hashofheaders = NULL; - - char sock_ipaddr[IP_LENGTH]; - char peer_ipaddr[IP_LENGTH]; - char peer_string[HOSTNAME_LENGTH]; - - getpeer_information(fd, peer_ipaddr, peer_string); - - if (config.bindsame) - getsock_ip(fd, sock_ipaddr); - - log_message(LOG_CONN, config.bindsame ? - "Connect (file descriptor %d): %s [%s] at [%s]" : - "Connect (file descriptor %d): %s [%s]", - fd, peer_string, peer_ipaddr, sock_ipaddr); - - connptr = initialize_conn(fd, peer_ipaddr, peer_string, - config.bindsame ? sock_ipaddr : 0); - if (!connptr) { - close(fd); - return; - } - - if (check_acl(fd, peer_ipaddr, peer_string) <= 0) { - update_stats(STAT_DENIED); - indicate_http_error(connptr, 403, "Access denied", - "detail", - "The administrator of this proxy has not configured it to service requests from your host.", - NULL); - send_http_error_message(connptr); - destroy_conn(connptr); - return; - } - - if (read_request_line(connptr) < 0) { - update_stats(STAT_BADCONN); - indicate_http_error(connptr, 408, "Timeout", - "detail", - "Server timeout waiting for the HTTP request from the client.", - NULL); - send_http_error_message(connptr); - destroy_conn(connptr); - return; - } - - /* - * The "hashofheaders" store the client's headers. - */ - if (!(hashofheaders = hashmap_create(HEADER_BUCKETS))) { - update_stats(STAT_BADCONN); - indicate_http_error(connptr, 503, "Internal error", - "detail", - "An internal server error occurred while processing your request. Please contact the administrator.", - NULL); - send_http_error_message(connptr); - destroy_conn(connptr); - return; - } - - /* - * Get all the headers from the client in a big hash. - */ - if (get_all_headers(connptr->client_fd, hashofheaders) < 0) { - log_message(LOG_WARNING, - "Could not retrieve all the headers from the client"); - hashmap_delete(hashofheaders); - update_stats(STAT_BADCONN); - destroy_conn(connptr); - return; - } - - request = process_request(connptr, hashofheaders); - if (!request) { - if (!connptr->error_variables && !connptr->show_stats) { - update_stats(STAT_BADCONN); - destroy_conn(connptr); - hashmap_delete(hashofheaders); - return; - } - goto send_error; - } - - connptr->upstream_proxy = UPSTREAM_HOST(request->host); - if (connptr->upstream_proxy != NULL) { - if (connect_to_upstream(connptr, request) < 0) { - goto send_error; - } - } else { - connptr->server_fd = opensock(request->host, request->port, - connptr->server_ip_addr); - if (connptr->server_fd < 0) { - indicate_http_error(connptr, 500, "Unable to connect", - "detail", - PACKAGE - " was unable to connect to the remote web server.", - "error", strerror(errno), NULL); - goto send_error; - } - - log_message(LOG_CONN, - "Established connection to host \"%s\" using file descriptor %d.", - request->host, connptr->server_fd); - - if (!connptr->connect_method) - establish_http_connection(connptr, request); - } - - send_error: - free_request_struct(request); - - if (process_client_headers(connptr, hashofheaders) < 0) { - update_stats(STAT_BADCONN); - if (!connptr->error_variables) { - hashmap_delete(hashofheaders); - destroy_conn(connptr); - return; - } - } - hashmap_delete(hashofheaders); - - if (connptr->error_variables) { - send_http_error_message(connptr); - destroy_conn(connptr); - return; - } else if (connptr->show_stats) { - showstats(connptr); - destroy_conn(connptr); - return; - } - - if (!connptr->connect_method || (connptr->upstream_proxy != NULL)) { - if (process_server_headers(connptr) < 0) { - if (connptr->error_variables) - send_http_error_message(connptr); - - update_stats(STAT_BADCONN); - destroy_conn(connptr); - return; - } - } else { - if (send_ssl_response(connptr) < 0) { - log_message(LOG_ERR, - "handle_connection: Could not send SSL greeting to client."); - update_stats(STAT_BADCONN); - destroy_conn(connptr); - return; - } - } - - relay_connection(connptr); - - log_message(LOG_INFO, - "Closed connection between local client (fd:%d) and remote client (fd:%d)", - connptr->client_fd, connptr->server_fd); - - /* - * All done... close everything and go home... :) - */ - destroy_conn(connptr); - return; + struct conn_s *connptr; + struct request_s *request = NULL; + hashmap_t hashofheaders = NULL; + + char sock_ipaddr[IP_LENGTH]; + char peer_ipaddr[IP_LENGTH]; + char peer_string[HOSTNAME_LENGTH]; + + getpeer_information (fd, peer_ipaddr, peer_string); + + if (config.bindsame) + getsock_ip (fd, sock_ipaddr); + + log_message (LOG_CONN, config.bindsame ? + "Connect (file descriptor %d): %s [%s] at [%s]" : + "Connect (file descriptor %d): %s [%s]", + fd, peer_string, peer_ipaddr, sock_ipaddr); + + connptr = initialize_conn (fd, peer_ipaddr, peer_string, + config.bindsame ? sock_ipaddr : 0); + if (!connptr) + { + close (fd); + return; + } + + if (check_acl (fd, peer_ipaddr, peer_string) <= 0) + { + update_stats (STAT_DENIED); + indicate_http_error (connptr, 403, "Access denied", + "detail", + "The administrator of this proxy has not configured it to service requests from your host.", + NULL); + send_http_error_message (connptr); + destroy_conn (connptr); + return; + } + + if (read_request_line (connptr) < 0) + { + update_stats (STAT_BADCONN); + indicate_http_error (connptr, 408, "Timeout", + "detail", + "Server timeout waiting for the HTTP request from the client.", + NULL); + send_http_error_message (connptr); + destroy_conn (connptr); + return; + } + + /* + * The "hashofheaders" store the client's headers. + */ + if (!(hashofheaders = hashmap_create (HEADER_BUCKETS))) + { + update_stats (STAT_BADCONN); + indicate_http_error (connptr, 503, "Internal error", + "detail", + "An internal server error occurred while processing your request. Please contact the administrator.", + NULL); + send_http_error_message (connptr); + destroy_conn (connptr); + return; + } + + /* + * Get all the headers from the client in a big hash. + */ + if (get_all_headers (connptr->client_fd, hashofheaders) < 0) + { + log_message (LOG_WARNING, + "Could not retrieve all the headers from the client"); + hashmap_delete (hashofheaders); + update_stats (STAT_BADCONN); + destroy_conn (connptr); + return; + } + + request = process_request (connptr, hashofheaders); + if (!request) + { + if (!connptr->error_variables && !connptr->show_stats) + { + update_stats (STAT_BADCONN); + destroy_conn (connptr); + hashmap_delete (hashofheaders); + return; + } + goto send_error; + } + + connptr->upstream_proxy = UPSTREAM_HOST (request->host); + if (connptr->upstream_proxy != NULL) + { + if (connect_to_upstream (connptr, request) < 0) + { + goto send_error; + } + } + else + { + connptr->server_fd = opensock (request->host, request->port, + connptr->server_ip_addr); + if (connptr->server_fd < 0) + { + indicate_http_error (connptr, 500, "Unable to connect", + "detail", + PACKAGE + " was unable to connect to the remote web server.", + "error", strerror (errno), NULL); + goto send_error; + } + + log_message (LOG_CONN, + "Established connection to host \"%s\" using file descriptor %d.", + request->host, connptr->server_fd); + + if (!connptr->connect_method) + establish_http_connection (connptr, request); + } + +send_error: + free_request_struct (request); + + if (process_client_headers (connptr, hashofheaders) < 0) + { + update_stats (STAT_BADCONN); + if (!connptr->error_variables) + { + hashmap_delete (hashofheaders); + destroy_conn (connptr); + return; + } + } + hashmap_delete (hashofheaders); + + if (connptr->error_variables) + { + send_http_error_message (connptr); + destroy_conn (connptr); + return; + } + else if (connptr->show_stats) + { + showstats (connptr); + destroy_conn (connptr); + return; + } + + if (!connptr->connect_method || (connptr->upstream_proxy != NULL)) + { + if (process_server_headers (connptr) < 0) + { + if (connptr->error_variables) + send_http_error_message (connptr); + + update_stats (STAT_BADCONN); + destroy_conn (connptr); + return; + } + } + else + { + if (send_ssl_response (connptr) < 0) + { + log_message (LOG_ERR, + "handle_connection: Could not send SSL greeting to client."); + update_stats (STAT_BADCONN); + destroy_conn (connptr); + return; + } + } + + relay_connection (connptr); + + log_message (LOG_INFO, + "Closed connection between local client (fd:%d) and remote client (fd:%d)", + connptr->client_fd, connptr->server_fd); + + /* + * All done... close everything and go home... :) + */ + destroy_conn (connptr); + return; } |