diff options
Diffstat (limited to 'src/reqs.c')
-rw-r--r-- | src/reqs.c | 2697 |
1 files changed, 1373 insertions, 1324 deletions
@@ -1,4 +1,4 @@ -/* $Id: reqs.c,v 1.119 2005-07-12 17:39:44 rjkaes Exp $ +/* $Id: reqs.c,v 1.120 2005-08-15 03:54:31 rjkaes Exp $ * * This is where all the work in tinyproxy is actually done. Incoming * connections have a new child created for them. The child then @@ -78,13 +78,13 @@ static vector_t ports_allowed_by_connect = NULL; * This structure holds the information pulled from a URL request. */ struct request_s { - char *method; - char *protocol; + char *method; + char *protocol; - char *host; - uint16_t port; + char *host; + uint16_t port; - char *path; + char *path; }; /* @@ -94,16 +94,18 @@ struct request_s { 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; - } - } - - 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)); } /* @@ -115,23 +117,23 @@ add_connect_port_allowed(int port) static int check_allowed_connect_ports(int port) { - ssize_t i; - int *data; + ssize_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; + 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); + 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 1; + } - return 0; + return 0; } /* @@ -142,35 +144,35 @@ check_allowed_connect_ports(int port) 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; } /* @@ -179,18 +181,18 @@ read_request_line(struct conn_s *connptr) 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); } /* @@ -198,9 +200,9 @@ free_request_struct(struct request_s *request) * it off. */ static void -strip_username_password(char* host) +strip_username_password(char *host) { - char *p; + char *p; assert(host); assert(strlen(host) > 0); @@ -213,7 +215,7 @@ strip_username_password(char* host) * until the NUL to the beginning of the host buffer. */ p++; - while (*p) + while (*p) *host++ = *p++; *host = '\0'; } @@ -223,19 +225,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; } /* @@ -245,42 +247,42 @@ strip_return_port(char* host) 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 = 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; } /* @@ -289,24 +291,24 @@ extract_http_url(const char *url, struct request_s *request) static int extract_ssl_url(const char *url, struct request_s *request) { - 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."); - - safefree(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."); + + 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 TRANSPARENT_PROXY @@ -316,21 +318,21 @@ extract_ssl_url(const char *url, struct request_s *request) static int build_url(char **url, const char *host, int port, const char *path) { - int len; + int len; - assert(url != NULL); - assert(host != NULL); - assert(port > 0 && port < 32768); - assert(path != NULL); + assert(url != NULL); + assert(host != NULL); + assert(port > 0 && port < 32768); + assert(path != NULL); - len = strlen(host) + strlen(path) + 14; - *url = safemalloc(len); - if (*url == NULL) - return -1; + len = strlen(host) + strlen(path) + 14; + *url = safemalloc(len); + if (*url == NULL) + return -1; - return snprintf(*url, len, "http://%s:%d%s", host, port, path); + return snprintf(*url, len, "http://%s:%d%s", host, port, path); } -#endif /* TRANSPARENT_PROXY */ +#endif /* TRANSPARENT_PROXY */ #ifdef UPSTREAM_SUPPORT /* @@ -339,99 +341,107 @@ build_url(char **url, const char *host, int port, const char *path) void 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; } /* @@ -440,50 +450,50 @@ upstream_cleanup: 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 @@ -494,40 +504,47 @@ upstream_get(char *host) void reversepath_add(const char *path, const char *url) { - struct reversepath *reverse; + struct reversepath *reverse; - if (url == NULL) { - log_message(LOG_WARNING, "Illegal reverse proxy rule: missing url"); - return; - } + if (url == NULL) { + log_message(LOG_WARNING, + "Illegal reverse proxy rule: missing url"); + return; + } - if (!strstr(url, "://")) { - log_message(LOG_WARNING, - "Skipping reverse proxy rule: '%s' is not a valid url", url); - return; - } + if (!strstr(url, "://")) { + log_message(LOG_WARNING, + "Skipping reverse proxy rule: '%s' is not a valid url", + url); + return; + } - if (path && *path != '/') { - log_message(LOG_WARNING, - "Skipping reverse proxy rule: path '%s' doesn't start with a /", path); - return; - } + if (path && *path != '/') { + log_message(LOG_WARNING, + "Skipping reverse proxy rule: path '%s' doesn't start with a /", + path); + return; + } - if (!(reverse = safemalloc(sizeof (struct reversepath)))) { - log_message(LOG_ERR, "Unable to allocate memory in reversepath_add()"); - return; - } + if (!(reverse = safemalloc(sizeof(struct reversepath)))) { + log_message(LOG_ERR, + "Unable to allocate memory in reversepath_add()"); + return; + } - if (!path) reverse->path = safestrdup("/"); - else reverse->path = safestrdup(path); + if (!path) + reverse->path = safestrdup("/"); + else + reverse->path = safestrdup(path); - reverse->url = safestrdup(url); + reverse->url = safestrdup(url); - reverse->next = config.reversepath_list; - config.reversepath_list = reverse; + reverse->next = config.reversepath_list; + config.reversepath_list = reverse; - log_message(LOG_INFO, - "Added reverse proxy rule: %s -> %s", reverse->path, reverse->url); + log_message(LOG_INFO, + "Added reverse proxy rule: %s -> %s", reverse->path, + reverse->url); } /* @@ -536,16 +553,16 @@ reversepath_add(const char *path, const char *url) static struct reversepath * reversepath_get(char *url) { - struct reversepath *reverse = config.reversepath_list; + struct reversepath *reverse = config.reversepath_list; - while (reverse) { - if (strstr(url, reverse->path) == url) - return reverse; + while (reverse) { + if (strstr(url, reverse->path) == url) + return reverse; - reverse = reverse->next; - } + reverse = reverse->next; + } - return NULL; + return NULL; } #endif @@ -555,20 +572,20 @@ reversepath_get(char *url) 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); } /* @@ -584,11 +601,10 @@ establish_http_connection(struct conn_s *connptr, struct request_s *request) 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); } /* @@ -598,42 +614,42 @@ send_ssl_response(struct conn_s *connptr) static struct request_s * process_request(struct conn_s *connptr, hashmap_t hashofheaders) { - char *url; - struct request_s *request; + char *url; + struct request_s *request; #ifdef REVERSE_SUPPORT - char *rewrite_url = NULL; - char *cookie = NULL; - char *cookieval; - struct reversepath *reverse; + char *rewrite_url = NULL; + char *cookie = NULL; + char *cookieval; + struct reversepath *reverse; #endif - int ret; + int ret; - size_t request_len; + 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; + /* 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_len = strlen(connptr->request_line) + 1; - request->method = safemalloc(request_len); - url = safemalloc(request_len); - request->protocol = safemalloc(request_len); + 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); + if (!request->method || !url || !request->protocol) { + safefree(url); + free_request_struct(request); - return NULL; - } + return NULL; + } - ret = sscanf(connptr->request_line, "%[^ ] %[^ ] %[^ ]", + ret = sscanf(connptr->request_line, "%[^ ] %[^ ] %[^ ]", request->method, url, request->protocol); if (ret == 2 && !strcasecmp(request->method, "GET")) { - request->protocol[0] = 0; + request->protocol[0] = 0; /* Indicate that this is a HTTP/0.9 GET request */ connptr->protocol.major = 0; @@ -654,271 +670,283 @@ process_request(struct conn_s *connptr, hashmap_t hashofheaders) 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; - } + 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 - /* - * Reverse proxy URL rewriting. - */ - if (config.reversepath_list != NULL) { - /* Reverse requests always start with a slash */ - if (*url == '/') { - /* First try locating the reverse mapping by request url */ - reverse = reversepath_get(url); - if (reverse) { - rewrite_url = safemalloc(strlen(url) + - strlen(reverse->url) + 1); - strcpy(rewrite_url, reverse->url); - strcat(rewrite_url, url + strlen(reverse->path)); - } else if (config.reversemagic && - hashmap_entry_by_key(hashofheaders, "cookie", - (void **)&cookie) > 0) { - - /* No match - try the magical tracking cookie next */ - if ((cookieval = strstr(cookie, REVERSE_COOKIE "=")) && - (reverse = reversepath_get(cookieval + - strlen(REVERSE_COOKIE) + 1))) { - - rewrite_url = safemalloc(strlen(url) + - strlen(reverse->url) + 1); - strcpy(rewrite_url, reverse->url); - strcat(rewrite_url, url + 1); - - log_message(LOG_INFO, - "Magical tracking cookie says: %s", - reverse->path); - } - } - } - - /* Forward proxy support off and no reverse path match found */ - if (config.reverseonly && !rewrite_url) { - log_message(LOG_ERR, "Bad request"); - indicate_http_error(connptr, 400, "Bad Request", - "detail", "Request has an invalid URL", - "url", url, - NULL); - - safefree(url); - free_request_struct(request); - - return NULL; - } - - log_message(LOG_CONN, "Rewriting URL: %s -> %s", - url, rewrite_url); - - safefree(url); - url = rewrite_url; - - /* Store reverse path so that the magical tracking cookie can be set */ - if (config.reversemagic) connptr->reversepath = safestrdup(reverse->path); - } + /* + * Reverse proxy URL rewriting. + */ + if (config.reversepath_list != NULL) { + /* Reverse requests always start with a slash */ + if (*url == '/') { + /* First try locating the reverse mapping by request url */ + reverse = reversepath_get(url); + if (reverse) { + rewrite_url = safemalloc(strlen(url) + + strlen(reverse->url) + + 1); + strcpy(rewrite_url, reverse->url); + strcat(rewrite_url, + url + strlen(reverse->path)); + } else if (config.reversemagic + && hashmap_entry_by_key(hashofheaders, + "cookie", + (void **)&cookie) > + 0) { + + /* No match - try the magical tracking cookie next */ + if ((cookieval = + strstr(cookie, REVERSE_COOKIE "=")) + && (reverse = + reversepath_get(cookieval + + strlen(REVERSE_COOKIE) + + 1))) { + + rewrite_url = safemalloc(strlen(url) + + strlen + (reverse-> + url) + 1); + strcpy(rewrite_url, reverse->url); + strcat(rewrite_url, url + 1); + + log_message(LOG_INFO, + "Magical tracking cookie says: %s", + reverse->path); + } + } + } + + /* Forward proxy support off and no reverse path match found */ + if (config.reverseonly && !rewrite_url) { + log_message(LOG_ERR, "Bad request"); + indicate_http_error(connptr, 400, "Bad Request", + "detail", + "Request has an invalid URL", "url", + url, NULL); + + safefree(url); + free_request_struct(request); + + return NULL; + } + + log_message(LOG_CONN, "Rewriting URL: %s -> %s", + url, rewrite_url); + + safefree(url); + url = rewrite_url; + + /* Store reverse path so that the magical tracking cookie can be set */ + if (config.reversemagic) + connptr->reversepath = safestrdup(reverse->path); + } #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 - /* - * This section of code is used for the transparent proxy - * option. You will need to configure your firewall to - * redirect all connections for HTTP traffic to tinyproxy - * for this to work properly. - * - * This code was written by Petr Lampa <lampa@fit.vutbr.cz> - */ - int length; - char *data; - length = hashmap_entry_by_key(hashofheaders, "host", (void **)&data); - if (length <= 0) { - struct sockaddr_in dest_addr; - - if (getsockname(connptr->client_fd, (struct sockaddr *)&dest_addr, &length) < 0) { - log_message(LOG_ERR, - "process_request: cannot get destination IP for %d", - connptr->client_fd); - indicate_http_error(connptr, 400, "Bad Request", - "detail", "Unknown destination", - "url", url, - NULL); - safefree(url); - free_request_struct(request); - return NULL; - } - request->host = safemalloc(17); - strcpy(request->host, inet_ntoa(dest_addr.sin_addr)); - request->port = ntohs(dest_addr.sin_port); - request->path = safemalloc(strlen(url) + 1); - strcpy(request->path, url); - safefree(url); - build_url(&url, request->host, request->port, request->path); - log_message(LOG_INFO, - "process_request: trans IP %s %s for %d", - request->method, url, connptr->client_fd); - } else { - request->host = safemalloc(length+1); - if (sscanf(data, "%[^:]:%hu", request->host, &request->port) != 2) { - strcpy(request->host, data); - request->port = HTTP_PORT; - } - request->path = safemalloc(strlen(url) + 1); - strcpy(request->path, url); - safefree(url); - build_url(&url, request->host, request->port, request->path); - log_message(LOG_INFO, - "process_request: trans Host %s %s for %d", - request->method, url, connptr->client_fd); - } - if (config.ipAddr && - strcmp(request->host, config.ipAddr) == 0) { - log_message(LOG_ERR, - "process_request: destination IP is localhost %d", - connptr->client_fd); - indicate_http_error(connptr, 400, "Bad Request", - "detail", "You tried to connect to the machine the proxy is running on", - "url", url, - NULL); - safefree(url); - free_request_struct(request); - return NULL; - } + /* + * This section of code is used for the transparent proxy + * option. You will need to configure your firewall to + * redirect all connections for HTTP traffic to tinyproxy + * for this to work properly. + * + * This code was written by Petr Lampa <lampa@fit.vutbr.cz> + */ + int length; + char *data; + + length = + hashmap_entry_by_key(hashofheaders, "host", (void **)&data); + if (length <= 0) { + struct sockaddr_in dest_addr; + + if (getsockname + (connptr->client_fd, (struct sockaddr *)&dest_addr, + &length) < 0) { + log_message(LOG_ERR, + "process_request: cannot get destination IP for %d", + connptr->client_fd); + indicate_http_error(connptr, 400, "Bad Request", + "detail", + "Unknown destination", + "url", url, NULL); + safefree(url); + free_request_struct(request); + return NULL; + } + request->host = safemalloc(17); + strcpy(request->host, inet_ntoa(dest_addr.sin_addr)); + request->port = ntohs(dest_addr.sin_port); + request->path = safemalloc(strlen(url) + 1); + strcpy(request->path, url); + safefree(url); + build_url(&url, request->host, request->port, + request->path); + log_message(LOG_INFO, + "process_request: trans IP %s %s for %d", + request->method, url, connptr->client_fd); + } else { + request->host = safemalloc(length + 1); + if (sscanf + (data, "%[^:]:%hu", request->host, + &request->port) != 2) { + strcpy(request->host, data); + request->port = HTTP_PORT; + } + request->path = safemalloc(strlen(url) + 1); + strcpy(request->path, url); + safefree(url); + build_url(&url, request->host, request->port, + request->path); + log_message(LOG_INFO, + "process_request: trans Host %s %s for %d", + request->method, url, connptr->client_fd); + } + if (config.ipAddr && strcmp(request->host, config.ipAddr) == 0) { + log_message(LOG_ERR, + "process_request: destination IP is localhost %d", + connptr->client_fd); + indicate_http_error(connptr, 400, "Bad Request", + "detail", + "You tried to connect to the machine the proxy is running on", + "url", url, NULL); + safefree(url); + free_request_struct(request); + return NULL; + } #else - log_message(LOG_ERR, - "process_request: Unknown URL type on file descriptor %d", - connptr->client_fd); - indicate_http_error(connptr, 400, "Bad Request", - "detail", "Unknown URL type", - "url", url, - NULL); - - safefree(url); - free_request_struct(request); - - return NULL; + log_message(LOG_ERR, + "process_request: Unknown URL type on file descriptor %d", + connptr->client_fd); + indicate_http_error(connptr, 400, "Bad Request", + "detail", "Unknown URL type", + "url", url, NULL); + + 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; } /* @@ -930,48 +958,48 @@ process_request(struct conn_s *connptr, hashmap_t hashofheaders) static int 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 @@ -984,11 +1012,10 @@ 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); + 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.) @@ -998,23 +1025,23 @@ 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); } /* @@ -1023,54 +1050,55 @@ add_header_to_connection(hashmap_t hashofheaders, char *header, size_t len) static int 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); + } } /* @@ -1080,49 +1108,51 @@ get_all_headers(int fd, hashmap_t hashofheaders) static int 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; } /* @@ -1132,15 +1162,17 @@ remove_connection_headers(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; } /* @@ -1152,40 +1184,38 @@ get_content_length(hashmap_t hashofheaders) */ static int write_via_header(int fd, hashmap_t hashofheaders, - unsigned int major, unsigned int minor) + 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; } /* @@ -1202,107 +1232,108 @@ write_via_header(int fd, hashmap_t hashofheaders, static int 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; } /* @@ -1312,65 +1343,69 @@ process_client_headers(struct conn_s *connptr, hashmap_t hashofheaders) static int 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. */ + /* 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; - } + 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 @@ -1384,103 +1419,106 @@ process_server_headers(struct conn_s *connptr) return 0; } - - /* Send the saved response line first */ + /* 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; + /* + * 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; } /* @@ -1494,105 +1532,106 @@ process_server_headers(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); - } - - 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; } /* @@ -1602,71 +1641,75 @@ 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 = 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 } @@ -1682,112 +1725,117 @@ connect_to_upstream(struct conn_s *connptr, struct request_s *request) 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(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); - } + 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); + free_request_struct(request); if (process_client_headers(connptr, hashofheaders) < 0) { update_stats(STAT_BADCONN); @@ -1799,43 +1847,44 @@ handle_connection(int fd) } 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; + 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; } |