diff options
Diffstat (limited to 'src/reqs.c')
-rw-r--r-- | src/reqs.c | 2639 |
1 files changed, 1257 insertions, 1382 deletions
@@ -51,7 +51,6 @@ */ #define HTTP_LINE_LENGTH (MAXBUFFSIZE / 6) - /* * Macro to help test if the Upstream proxy supported is compiled in and * enabled. @@ -83,28 +82,25 @@ */ static vector_t ports_allowed_by_connect = NULL; - /* * Now, this routine adds a "port" to the list. It also creates the list if * it hasn't already by done. */ -void -add_connect_port_allowed (int port) +void 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; + 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)); + log_message (LOG_INFO, + "Adding Port [%d] to the list allowed by CONNECT", port); + vector_append (ports_allowed_by_connect, (void **) &port, + sizeof (port)); } /* @@ -113,27 +109,26 @@ add_connect_port_allowed (int port) * Returns: 1 if allowed * 0 if denied */ -static int -check_allowed_connect_ports (int port) +static int 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 != (size_t)vector_length (ports_allowed_by_connect); ++i) - { - data = (int *)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 != (size_t) vector_length (ports_allowed_by_connect); ++i) { + data = + (int *) vector_getentry (ports_allowed_by_connect, i, NULL); + if (data && *data == port) + return 1; + } + + return 0; } /* @@ -141,365 +136,329 @@ check_allowed_connect_ports (int port) * connections. The request line is allocated from the heap, but it must * be freed in another function. */ -static int -read_request_line (struct conn_s *connptr) +static int read_request_line (struct conn_s *connptr) { - ssize_t len; + 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; + 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) +static void 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); } /* * Take a host string and if there is a username/password part, strip * it off. */ -static void -strip_username_password (char *host) +static void 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'; } /* * Take a host string and if there is a port part, strip * it off and set proper port variable i.e. for www.host.com:8001 */ -static int -strip_return_port (char *host) +static int 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; } /* * Pull the information out of the URL line. This will handle both HTTP * and FTP (proxied) URLs. */ -static int -extract_http_url (const char *url, struct request_s *request) +static int 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 = (char *)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; + 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 = (char *) 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); + if (request->host) + safefree (request->host); + if (request->path) + safefree (request->path); - return -1; + return -1; } /* * Extract the URL from a SSL connection. */ -static int -extract_ssl_url (const char *url, struct request_s *request) +static int extract_ssl_url (const char *url, struct request_s *request) { - request->host = (char *)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."); - - safefree (request->host); - return -1; - } + request->host = (char *) 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."); + + 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; } - #ifdef UPSTREAM_SUPPORT /* * Add an entry to the upstream list */ -void -upstream_add (const char *host, int port, const char *domain) +void upstream_add (const char *host, int port, const char *domain) { - char *ptr; - struct upstream *up = (struct upstream *)safemalloc(sizeof (struct upstream)); - - if (!up) - { - log_message (LOG_ERR, "Unable to allocate memory in upstream_add()"); - return; - } + char *ptr; + struct upstream *up = + (struct upstream *) 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; + 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; - } + 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; + 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; - } + 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); + 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); } - else - { - up->mask = ~((1 << (32 - atoi (ptr))) - 1); + + 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; } - } - } - 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); } - up->host = safestrdup (host); - up->port = port; - up->domain = safestrdup (domain); + if (!up->domain && !up->ip) { /* always add default to end */ + struct upstream *tmp = config.upstream_list; - log_message (LOG_INFO, "Added upstream %s:%d for %s", - host, port, domain); - } + while (tmp) { + if (!tmp->domain && !tmp->ip) { + log_message (LOG_WARNING, + "Duplicate default upstream"); + goto upstream_cleanup; + } - if (!up->domain && !up->ip) - { /* always add default to end */ - struct upstream *tmp = config.upstream_list; + if (!tmp->next) { + up->next = NULL; + tmp->next = up; + return; + } - 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; + tmp = tmp->next; + } } - } - up->next = config.upstream_list; - config.upstream_list = up; + up->next = config.upstream_list; + config.upstream_list = up; - return; + return; upstream_cleanup: - safefree (up->host); - safefree (up->domain); - safefree (up); + safefree (up->host); + safefree (up->domain); + safefree (up); - return; + return; } /* * Check if a host is in the upstream list */ -static struct upstream * -upstream_get (char *host) +static struct upstream *upstream_get (char *host) { - struct upstream *up = config.upstream_list; + struct upstream *up = config.upstream_list; - in_addr_t my_ip = INADDR_NONE; + in_addr_t my_ip = INADDR_NONE; - while (up) - { - if (up->domain) - { - if (strcasecmp (host, up->domain) == 0) - break; /* exact match */ + while (up) { + if (up->domain) { + if (strcasecmp (host, up->domain) == 0) + break; /* exact match */ - if (up->domain[0] == '.') - { - char *dot = strchr (host, '.'); + if (up->domain[0] == '.') { + char *dot = strchr (host, '.'); - if (!dot && !up->domain[1]) - break; /* local host matches "." */ + if (!dot && !up->domain[1]) + break; /* local host matches "." */ - while (dot && strcasecmp (dot, up->domain)) - dot = strchr (dot + 1, '.'); + 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 (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 */ - } + if ((my_ip & up->mask) == up->ip) + break; + } else { + break; /* No domain or IP, default upstream */ + } - up = up->next; - } + up = up->next; + } - if (up && (!up->host || !up->port)) - up = NULL; + 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); + 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; + return up; } #endif @@ -509,20 +468,20 @@ upstream_get (char *host) static int 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); } /* @@ -535,253 +494,234 @@ establish_http_connection (struct conn_s *connptr, struct request_s *request) * Send the appropriate response to the client to establish a SSL * connection. */ -static inline int -send_ssl_response (struct conn_s *connptr) +static inline int 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); } /* * Break the request line apart and figure out where to connect and * build a new request line. Finally connect to the remote server. */ -static struct request_s * -process_request (struct conn_s *connptr, hashmap_t hashofheaders) +static struct request_s *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 = (struct request_s *)safecalloc (1, sizeof (struct request_s)); - if (!request) - return NULL; - - request_len = strlen (connptr->request_line) + 1; - - request->method = (char *)safemalloc (request_len); - url = (char *)safemalloc (request_len); - request->protocol = (char *)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; - } - } -#endif - - if (strncasecmp (url, "http://", 7) == 0 - || (UPSTREAM_CONFIGURED () && strncasecmp (url, "ftp://", 6) == 0)) - { - char *skipped_type = strstr (url, "//") + 2; + char *url; + struct request_s *request; + int ret; + size_t request_len; - if (extract_http_url (skipped_type, request) < 0) - { - indicate_http_error (connptr, 400, "Bad Request", - "detail", "Could not parse URL", - "url", url, NULL); + /* NULL out all the fields so frees don't cause segfaults. */ + request = + (struct request_s *) safecalloc (1, sizeof (struct request_s)); + if (!request) + return NULL; - safefree (url); - free_request_struct (request); + request_len = strlen (connptr->request_line) + 1; - 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); + request->method = (char *) safemalloc (request_len); + url = (char *) safemalloc (request_len); + request->protocol = (char *) safemalloc (request_len); - safefree (url); - free_request_struct (request); + if (!request->method || !url || !request->protocol) { + safefree (url); + free_request_struct (request); - return NULL; + 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; + 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; } - 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; - } -#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; + 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; + } + } #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) + if (strncasecmp (url, "http://", 7) == 0 + || (UPSTREAM_CONFIGURED () && strncasecmp (url, "ftp://", 6) == 0)) { - update_stats (STAT_DENIED); + 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); - 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); + 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); - indicate_http_error (connptr, 403, "Filtered", - "detail", - "The request you made has been filtered", - "url", url, NULL); + return NULL; + } - safefree (url); - free_request_struct (request); + /* 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; + } - 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; + } +#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; + +#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; + } } - } #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; } /* @@ -790,54 +730,51 @@ process_request (struct conn_s *connptr, hashmap_t hashofheaders) * server headers can be processed. * - rjkaes */ -static int -pull_client_data (struct conn_s *connptr, long int length) +static int pull_client_data (struct conn_s *connptr, long int length) { - char *buffer; - ssize_t len; - - buffer = (char *)safemalloc (min (MAXBUFFSIZE, (unsigned long int)length)); - if (!buffer) - return -1; - - do - { - len = safe_read (connptr->client_fd, buffer, - min (MAXBUFFSIZE, (unsigned long int)length)); - if (len <= 0) - goto ERROR_EXIT; - - if (!connptr->error_variables) - { - if (safe_write (connptr->server_fd, buffer, len) < 0) - goto ERROR_EXIT; - } + char *buffer; + ssize_t len; + + buffer = + (char *) safemalloc (min (MAXBUFFSIZE, (unsigned long int) length)); + if (!buffer) + return -1; + + do { + len = safe_read (connptr->client_fd, buffer, + min (MAXBUFFSIZE, (unsigned long int) 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); + 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); + /* + * 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 < 0 && errno != EAGAIN) + goto ERROR_EXIT; - if (len == 2 && CHECK_CRLF (buffer, len)) - read (connptr->client_fd, buffer, 2); + if (len == 2 && CHECK_CRLF (buffer, len)) + read (connptr->client_fd, buffer, 2); - safefree (buffer); - return 0; + safefree (buffer); + return 0; ERROR_EXIT: - safefree (buffer); - return -1; + safefree (buffer); + return -1; } #ifdef XTINYPROXY_ENABLE @@ -846,12 +783,11 @@ ERROR_EXIT: * the server. * -rjkaes */ -static inline int -add_xtinyproxy_header (struct conn_s *connptr) +static inline int 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 */ @@ -863,186 +799,178 @@ add_xtinyproxy_header (struct conn_s *connptr) static inline int 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) +static int get_all_headers (int fd, hashmap_t hashofheaders) { - char *line = NULL; - char *header = NULL; - char *tmp; - ssize_t linelen; - ssize_t len = 0; - unsigned int double_cgi = FALSE; /* boolean */ - - assert (fd >= 0); - assert (hashofheaders != NULL); - - for (;;) - { - if ((linelen = readline (fd, &line)) <= 0) - { - safefree (header); - safefree (line); - return -1; - } + char *line = NULL; + char *header = NULL; + char *tmp; + ssize_t linelen; + ssize_t len = 0; + unsigned int double_cgi = FALSE; /* boolean */ + + assert (fd >= 0); + assert (hashofheaders != NULL); + + for (;;) { + if ((linelen = readline (fd, &line)) <= 0) { + safefree (header); + safefree (line); + return -1; + } - /* - * If we received a CR LF or a non-continuation line, then add - * the accumulated header field, if any, to the hashmap, and - * reset it. - */ - if (CHECK_CRLF (line, linelen) || !CHECK_LWS (line, linelen)) - { - if (!double_cgi - && len > 0 - && add_header_to_connection (hashofheaders, header, len) < 0) - { - safefree (header); - safefree (line); - return -1; - } - - len = 0; - } + /* + * If we received a CR LF or a non-continuation line, then add + * the accumulated header field, if any, to the hashmap, and + * reset it. + */ + if (CHECK_CRLF (line, linelen) || !CHECK_LWS (line, linelen)) { + if (!double_cgi + && len > 0 + && add_header_to_connection (hashofheaders, header, + len) < 0) { + safefree (header); + safefree (line); + return -1; + } + + len = 0; + } - /* - * If we received just a CR LF on a line, the headers are - * finished. - */ - if (CHECK_CRLF (line, linelen)) - { - safefree (header); - safefree (line); - return 0; - } + /* + * If we received just a CR LF on a line, the headers are + * finished. + */ + if (CHECK_CRLF (line, linelen)) { + safefree (header); + safefree (line); + 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 (linelen >= 5 && strncasecmp (line, "HTTP/", 5) == 0) - { - double_cgi = TRUE; - } + /* + * 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 (linelen >= 5 && strncasecmp (line, "HTTP/", 5) == 0) { + double_cgi = TRUE; + } - /* - * Append the new line to the current header field. - */ - tmp = (char *)saferealloc (header, len + linelen); - if (tmp == NULL) - { - safefree (header); - safefree (line); - return -1; - } + /* + * Append the new line to the current header field. + */ + tmp = (char *) saferealloc (header, len + linelen); + if (tmp == NULL) { + safefree (header); + safefree (line); + return -1; + } - header = tmp; - memcpy (header + len, line, linelen); - len += linelen; + header = tmp; + memcpy (header + len, line, linelen); + len += linelen; - safefree (line); - } + safefree (line); + } } /* * Extract the headers to remove. These headers were listed in the Connection * and Proxy-Connection headers. */ -static int -remove_connection_headers (hashmap_t hashofheaders) +static int remove_connection_headers (hashmap_t hashofheaders) { - static const 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); + static const 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++; + } - /* 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]); } - /* Now remove the connection header it self. */ - hashmap_remove (hashofheaders, headers[i]); - } - - return 0; + return 0; } /* * If there is a Content-Length header, then return the value; otherwise, return * a negative number. */ -static long -get_content_length (hashmap_t hashofheaders) +static long 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; } /* @@ -1056,41 +984,36 @@ static int 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; } /* @@ -1107,306 +1030,296 @@ write_via_header (int fd, hashmap_t hashofheaders, static int process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) { - static const 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 const 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; + /* 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. - */ + /* + * 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; + if (connptr->content_length.client > 0) + return pull_client_data (connptr, + connptr->content_length.client); + else + return ret; } /* * Loop through all the headers (including the response code) from the * server. */ -static int -process_server_headers (struct conn_s *connptr) +static int process_server_headers (struct conn_s *connptr) { - static const 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 const 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. */ + /* 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; + 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; + /* 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; } - 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"); + /* 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; + /* + * 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); + 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; + /* 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; + return 0; ERROR_EXIT: - hashmap_delete (hashofheaders); - return -1; + hashmap_delete (hashofheaders); + return -1; } /* @@ -1417,123 +1330,109 @@ ERROR_EXIT: * tinyproxy oh so long ago...) * - rjkaes */ -static void -relay_connection (struct conn_s *connptr) +static void 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); - } + 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; + 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; + 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; + } } - if (FD_ISSET (connptr->server_fd, &wset) - && write_buffer (connptr->server_fd, connptr->cbuffer) < 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; } - if (FD_ISSET (connptr->client_fd, &wset) - && 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; } - } - - /* - * 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; + + return; } /* @@ -1543,81 +1442,78 @@ static int 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 = (char *)safemalloc (len); - if (!combined_string) - { - return -1; + 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; } - snprintf (combined_string, len, "%s:%d", request->host, request->port); - } - else - { - len = strlen (request->host) + strlen (request->path) + 14; - combined_string = (char *)safemalloc (len); - if (!combined_string) - { - 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; } - snprintf (combined_string, len, "http://%s:%d%s", request->host, - request->port, request->path); - } + 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 = (char *) safemalloc (len); + if (!combined_string) { + return -1; + } - if (request->path) - safefree (request->path); - request->path = combined_string; + snprintf (combined_string, len, "%s:%d", request->host, + request->port); + } else { + len = strlen (request->host) + strlen (request->path) + 14; + combined_string = (char *) safemalloc (len); + if (!combined_string) { + return -1; + } + + snprintf (combined_string, len, "http://%s:%d%s", request->host, + request->port, request->path); + } - return establish_http_connection (connptr, request); + if (request->path) + safefree (request->path); + request->path = combined_string; + + return establish_http_connection (connptr, request); #endif } @@ -1630,194 +1526,173 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request) * tinyproxy code, which was confusing, redundant. Hail progress. * - rjkaes */ -void -handle_connection (int fd) +void 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 (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; + 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; } - 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; + if (check_acl (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; } - } - 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; + + 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; } - log_message (LOG_CONN, - "Established connection to host \"%s\" using " - "file descriptor %d.", request->host, connptr->server_fd); + /* + * 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; + } - if (!connptr->connect_method) - establish_http_connection (connptr, request); - } + /* + * 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; + } -send_error: - free_request_struct (request); + 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; + } - if (process_client_headers (connptr, hashofheaders) < 0) - { - update_stats (STAT_BADCONN); - if (!connptr->error_variables) - { - hashmap_delete (hashofheaders); - destroy_conn (connptr); - return; + 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); } - } - 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; +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; + } } - } - 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; + 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); + 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); + 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; + /* + * All done... close everything and go home... :) + */ + destroy_conn (connptr); + return; } |