diff options
Diffstat (limited to 'src/reqs.c')
-rw-r--r-- | src/reqs.c | 391 |
1 files changed, 228 insertions, 163 deletions
@@ -1,4 +1,4 @@ -/* $Id: reqs.c,v 1.52 2001-12-24 00:01:02 rjkaes Exp $ +/* $Id: reqs.c,v 1.53 2002-04-07 21:35:59 rjkaes Exp $ * * This is where all the work in tinyproxy is actually done. Incoming * connections have a new thread created for them. The thread then @@ -30,6 +30,7 @@ #include "buffer.h" #include "conns.h" #include "filter.h" +#include "hashmap.h" #include "log.h" #include "regexp.h" #include "reqs.h" @@ -75,39 +76,38 @@ * connections. The request line is allocated from the heap, but it must * be freed in another function. */ -static char * +static int read_request_line(struct conn_s *connptr) { - char *request_buffer; size_t len; retry: - len = readline(connptr->client_fd, &request_buffer); + 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); - safefree(request_buffer); - return NULL; + safefree(connptr->request_line); + return -1; } /* * Strip the new line and character return from the string. */ - if (chomp(request_buffer, len) == len) { + 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(request_buffer); + safefree(connptr->request_line); goto retry; } log_message(LOG_CONN, "Request (file descriptor %d): %s", - connptr->client_fd, request_buffer); + connptr->client_fd, connptr->request_line); - return request_buffer; + return 0; } /* @@ -118,8 +118,9 @@ struct request_s { char *protocol; char *host; + uint16_t port; + char *path; - int port; }; static void @@ -154,11 +155,11 @@ extract_http_url(const char *url, struct request_s *request) } if (sscanf - (url, "http://%[^:/]:%d%s", request->host, &request->port, + (url, "http://%[^:/]:%hu%s", request->host, &request->port, request->path) == 3) ; else if (sscanf(url, "http://%[^/]%s", request->host, request->path) == 2) request->port = 80; - else if (sscanf(url, "http://%[^:/]:%d", request->host, &request->port) + else if (sscanf(url, "http://%[^:/]:%hu", request->host, &request->port) == 2) strcpy(request->path, "/"); else if (sscanf(url, "http://%[^/]", request->host) == 1) { @@ -186,7 +187,7 @@ extract_ssl_url(const char *url, struct request_s *request) if (!request->host) return -1; - if (sscanf(url, "%[^:]:%d", request->host, &request->port) == 2) ; + if (sscanf(url, "%[^:]:%hu", request->host, &request->port) == 2) ; else if (sscanf(url, "%s", request->host) == 1) request->port = 443; else { @@ -255,7 +256,7 @@ send_ssl_response(struct conn_s *connptr) * build a new request line. Finally connect to the remote server. */ static struct request_s * -process_request(struct conn_s *connptr, char *request_line) +process_request(struct conn_s *connptr) { char *url; struct request_s *request; @@ -269,7 +270,7 @@ process_request(struct conn_s *connptr, char *request_line) if (!request) return NULL; - request_len = strlen(request_line) + 1; + request_len = strlen(connptr->request_line) + 1; request->method = safemalloc(request_len); url = safemalloc(request_len); @@ -283,8 +284,8 @@ process_request(struct conn_s *connptr, char *request_line) } ret = - sscanf(request_line, "%[^ ] %[^ ] %[^ ]", request->method, url, - request->protocol); + sscanf(connptr->request_line, "%[^ ] %[^ ] %[^ ]", + request->method, url, request->protocol); if (ret < 2) { log_message(LOG_ERR, "process_request: Bad Request on file descriptor %d", @@ -398,33 +399,6 @@ process_request(struct conn_s *connptr, char *request_line) } /* - * Check to see if the line is allowed or not depending on the anonymous - * headers which are to be allowed. If the header is found in the - * anonymous list return 0, otherwise return -1. - */ -static int -compare_header(char *line) -{ - char *buffer; - char *ptr; - int ret; - - if ((ptr = strstr(line, ":")) == NULL) - return -1; - - if ((buffer = safemalloc(ptr - line + 1)) == NULL) - return -1; - - memcpy(buffer, line, (size_t) (ptr - line)); - buffer[ptr - line] = '\0'; - - ret = anonymous_search(buffer); - safefree(buffer); - - return ret; -} - -/* * pull_client_data is used to pull across any client data (like in a * POST) which needs to be handled before an error can be reported, or * server headers can be processed. @@ -450,7 +424,7 @@ pull_client_data(struct conn_s *connptr, unsigned long int length) return -1; } - if (!connptr->send_response_message) { + if (!connptr->response_message_sent) { if (safe_write(connptr->server_fd, buffer, len) < 0) { safefree(buffer); return -1; @@ -488,6 +462,112 @@ add_xtinyproxy_header(struct conn_s *connptr) #endif /* XTINYPROXY */ /* + * Check to see if the header is allowed or not depending on the anonymous + * headers which are to be allowed. If the header is found in the + * anonymous list return 0, otherwise return -1. + */ +static inline int +compare_header(char *header) +{ + return anonymous_search(header); +} + +/* + * Take a complete header line and break it apart (into a key and the data.) + * Now insert this information into the hashmap for the connection so it + * can be retrieved and manipulated later. + */ +static inline int +add_header_to_connection(hashmap_t hashofheaders, char *header, size_t len) +{ + char *sep; + size_t data_len; + + /* Get rid of the new line and return at the end */ + chomp(header, len); + + sep = strchr(header, ':'); + if (!sep) + return -1; + + /* Blank out colons, spaces, and tabs. */ + while (*sep == ':' || *sep == ' ' || *sep == '\t') + *sep++ = '\0'; + + data_len = strlen(sep) + 1; /* need to add the null to the length */ + return hashmap_insert(hashofheaders, header, sep, data_len); +} + +/* + * Read all the headers from the stream + */ +static int +get_all_headers(int fd, hashmap_t hashofheaders) +{ + char *header; + ssize_t len; + + for (;;) { + if ((len = readline(fd, &header)) <= 0) { + return -1; + } + + /* + * If we received just a CR LF on a line, the headers are + * finished. + */ + if (CHECK_CRLF(header, len)) + break; + + if (add_header_to_connection(hashofheaders, header, len) < 0) + return -1; + } + + return 0; +} + +/* + * Extract the headers to remove. These headers were listed in the Connection + * header sent via the client (which is stored in data right now.) + */ +static int +remove_connection_headers(hashmap_t hashofheaders, char* data, ssize_t len) +{ + char* ptr; + + /* + * 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 tokens + * and remove them from the hashofheaders. + */ + ptr = data; + while (ptr < data + len) { + DEBUG2("Removing header [%s]", ptr); + hashmap_remove(hashofheaders, ptr); + + /* Advance ptr to the next token */ + ptr += strlen(ptr) + 1; + while (*ptr == '\0') + ptr++; + } + + return 0; +} + +/* + * Number of buckets to use internally in the hashmap. + */ +#define HEADER_BUCKETS 32 + +/* * Here we loop through all the headers the client is sending. If we * are running in anonymous mode, we will _only_ send the headers listed * (plus a few which are required for various methods). @@ -496,11 +576,6 @@ add_xtinyproxy_header(struct conn_s *connptr) static int process_client_headers(struct conn_s *connptr) { - char *header; - long content_length = -1; - short int sent_via_header = 0; - ssize_t len; - static char *skipheaders[] = { "host", "connection", @@ -513,122 +588,120 @@ process_client_headers(struct conn_s *connptr) "upgrade" }; int i; + hashmap_t hashofheaders; + vector_t listofheaders; + long content_length = -1; - for (;;) { - if ((len = readline(connptr->client_fd, &header)) <= 0) { - DEBUG2("Client (file descriptor %d) closed connection.", - connptr->client_fd); - return -1; - } - - /* - * If we receive a CR LF (or just a LF) on a line by itself, - * the headers are finished. - */ - if (CHECK_CRLF(header, len)) - break; + char *data, *header; + size_t len; - /* - * Don't send headers if there's already an error, or if - * this was a CONNECT method (unless upstream proxy is in - * use.) - */ - if (connptr->server_fd == -1 - || (connptr->connect_method && !UPSTREAM_CONFIGURED())) { - safefree(header); - continue; - } + hashofheaders = hashmap_create(HEADER_BUCKETS); + if (!hashofheaders) + return -1; - /* - * If we find a Via header we need to append our information - * to the end of it. - */ - if (strncasecmp(header, "via", 3) == 0) { - if (sent_via_header == 0) { - char hostname[128]; - - chomp(header, len); - gethostname(hostname, sizeof(hostname)); - write_message(connptr->server_fd, - "%s, %hu.%hu %s (%s/%s)\r\n", - header, - connptr->protocol.major, - connptr->protocol.minor, - hostname, PACKAGE, VERSION); - - sent_via_header = 1; - } - safefree(header); + /* + * Get all the headers from the client in a big hash. + */ + if (get_all_headers(connptr->client_fd, hashofheaders) < 0) { + hashmap_delete(hashofheaders); + return -1; + } - continue; - } + + /* + * Don't send headers if there's already an error, or if this was + * a CONNECT method (unless upstream proxy is in use.) + */ + if (connptr->server_fd == -1 + || (connptr->connect_method && !UPSTREAM_CONFIGURED())) { + hashmap_delete(hashofheaders); + return 0; + } + /* + * See if there is a "Connection" header. If so, we need to do a bit + * of processing. :) + */ + len = hashmap_search(hashofheaders, "connection", (void **)&data); + if (len > 0) { /* - * Don't send certain headers. + * Go through the tokens in the connection header and + * remove the headers from the hash. */ - for (i = 0; i < (sizeof(skipheaders) / sizeof(char *)); i++) { - if (strncasecmp - (header, skipheaders[i], - strlen(skipheaders[i])) == 0) { - break; - } - } - if (i != (sizeof(skipheaders) / sizeof(char *))) { - safefree(header); - continue; - } - - if (is_anonymous_enabled() && compare_header(header) < 0) { - safefree(header); - continue; - } + remove_connection_headers(hashofheaders, data, len); + hashmap_remove(hashofheaders, "connection"); + } - if (content_length == -1 - && strncasecmp(header, "content-length", 14) == 0) { - char *content_ptr = strchr(header, ':') + 1; - content_length = atol(content_ptr); - } + /* + * See if there is a "Content-Length" header. If so, again we need + * to do a bit of processing. + */ + len = hashmap_search(hashofheaders, "content-length", (void **)&data); + if (len > 0) { + content_length = atol(data); + } - if (safe_write(connptr->server_fd, header, strlen(header)) < 0) { - safefree(header); - return -1; - } + /* + * See if there is a "Via" header. If so, again we need to do a bit + * of processing. + */ + len = hashmap_search(hashofheaders, "via", (void **)&data); + if (len > 0) { + /* Take on our information */ + char hostname[128]; + gethostname(hostname, sizeof(hostname)); + + write_message(connptr->server_fd, + "Via: %s, %hu.%hu %s (%s/%s)\r\n", + data, + connptr->protocol.major, connptr->protocol.minor, + hostname, PACKAGE, VERSION); + + hashmap_remove(hashofheaders, "via"); + } else { + /* There is no header, so we need to create it. */ + char hostname[128]; + gethostname(hostname, sizeof(hostname)); + + write_message(connptr->server_fd, + "Via: %hu.%hu %s (%s/%s)\r\n", + connptr->protocol.major, connptr->protocol.minor, + hostname, PACKAGE, VERSION); + } - safefree(header); + /* + * Delete the headers listed in the skipheaders list + */ + for (i = 0; i < (sizeof(skipheaders) / sizeof(char *)); i++) { + hashmap_remove(hashofheaders, skipheaders[i]); } - if (!connptr->send_response_message - && (!connptr->connect_method || UPSTREAM_CONFIGURED())) { -#ifdef XTINYPROXY_ENABLE - if (config.my_domain && add_xtinyproxy_header(connptr) < 0) { - safefree(header); - return -1; - } -#endif /* XTINYPROXY */ + /* + * Output all the remaining headers to the remote machine. + */ + listofheaders = hashmap_keys(hashofheaders); + for (i = 0; i < vector_length(listofheaders); i++) { + len = vector_getentry(listofheaders, i, (void **)&data); - if (sent_via_header == 0) { - /* - * We're the first proxy so send the first Via header. - */ - char hostname[128]; + hashmap_search(hashofheaders, data, (void **)&header); - gethostname(hostname, sizeof(hostname)); + if (!is_anonymous_enabled() || compare_header(data) == 0) { write_message(connptr->server_fd, - "Via: %hu.%hu %s (%s/%s)\r\n", - connptr->protocol.major, - connptr->protocol.minor, - hostname, PACKAGE, VERSION); - } - - if ((connptr->server_fd != -1) - && safe_write(connptr->server_fd, header, - strlen(header)) < 0) { - safefree(header); - return -1; + "%s: %s\r\n", + data, header); } } + vector_delete(listofheaders); - safefree(header); + /* Free the hashofheaders since it's no longer needed */ + hashmap_delete(hashofheaders); + +#if defined(XTINYPROXY_ENABLE) + add_xtinyproxy_header(connptr); +#endif + + /* Write the final "blank" line to signify the end of the headers */ + safe_write(connptr->server_fd, "\r\n", 2); /* * Spin here pulling the data from the client. @@ -896,20 +969,15 @@ handle_connection(int fd) char peer_ipaddr[PEER_IP_LENGTH]; char peer_string[PEER_STRING_LENGTH]; - char *request_line = NULL; - log_message(LOG_CONN, "Connect (file descriptor %d): %s [%s]", fd, getpeer_string(fd, peer_string), getpeer_ip(fd, peer_ipaddr)); - connptr = safemalloc(sizeof(struct conn_s)); + connptr = initialize_conn(fd); if (!connptr) return; - initialize_conn(connptr); - connptr->client_fd = fd; - if (check_acl(fd) <= 0) { update_stats(STAT_DENIED); httperr(connptr, 403, @@ -925,18 +993,15 @@ handle_connection(int fd) } internal_proxy: - request_line = read_request_line(connptr); - if (!request_line) { + if (read_request_line(connptr) < 0) { update_stats(STAT_BADCONN); destroy_conn(connptr); return; } - request = process_request(connptr, request_line); - safefree(request_line); - + request = process_request(connptr); if (!request) { - if (!connptr->send_response_message) { + if (!connptr->response_message_sent) { update_stats(STAT_BADCONN); destroy_conn(connptr); return; @@ -967,13 +1032,13 @@ handle_connection(int fd) if (process_client_headers(connptr) < 0) { update_stats(STAT_BADCONN); - if (!connptr->send_response_message) { + if (!connptr->response_message_sent) { destroy_conn(connptr); return; } } - if (connptr->send_response_message) { + if (connptr->response_message_sent) { destroy_conn(connptr); return; } |