diff options
Diffstat (limited to 'src/reqs.c')
-rw-r--r-- | src/reqs.c | 489 |
1 files changed, 250 insertions, 239 deletions
@@ -32,7 +32,8 @@ #include "buffer.h" #include "conns.h" #include "filter.h" -#include "hashmap.h" +#include "hsearch.h" +#include "orderedmap.h" #include "heap.h" #include "html-error.h" #include "log.h" @@ -42,13 +43,15 @@ #include "stats.h" #include "text.h" #include "utils.h" -#include "vector.h" +#include "sblist.h" #include "reverse-proxy.h" #include "transparent-proxy.h" #include "upstream.h" #include "connect-ports.h" #include "conf.h" #include "basicauth.h" +#include "loop.h" +#include "mypoll.h" /* * Maximum length of a HTTP line @@ -60,8 +63,8 @@ * enabled. */ #ifdef UPSTREAM_SUPPORT -# define UPSTREAM_CONFIGURED() (config.upstream_list != NULL) -# define UPSTREAM_HOST(host) upstream_get(host, config.upstream_list) +# define UPSTREAM_CONFIGURED() (config->upstream_list != NULL) +# define UPSTREAM_HOST(host) upstream_get(host, config->upstream_list) # define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP) #else # define UPSTREAM_CONFIGURED() (0) @@ -271,28 +274,34 @@ establish_http_connection (struct conn_s *connptr, struct request_s *request) /* host is an IPv6 address literal, so surround it with * [] */ return write_message (connptr->server_fd, - "%s %s HTTP/1.0\r\n" + "%s %s HTTP/1.%u\r\n" "Host: [%s]%s\r\n" "Connection: close\r\n", request->method, request->path, + connptr->protocol.major != 1 ? 0 : + connptr->protocol.minor, request->host, portbuff); } else if (connptr->upstream_proxy && connptr->upstream_proxy->type == PT_HTTP && connptr->upstream_proxy->ua.authstr) { return write_message (connptr->server_fd, - "%s %s HTTP/1.0\r\n" + "%s %s HTTP/1.%u\r\n" "Host: %s%s\r\n" "Connection: close\r\n" "Proxy-Authorization: Basic %s\r\n", request->method, request->path, + connptr->protocol.major != 1 ? 0 : + connptr->protocol.minor, request->host, portbuff, connptr->upstream_proxy->ua.authstr); } else { return write_message (connptr->server_fd, - "%s %s HTTP/1.0\r\n" + "%s %s HTTP/1.%u\r\n" "Host: %s%s\r\n" "Connection: close\r\n", request->method, request->path, + connptr->protocol.major != 1 ? 0 : + connptr->protocol.minor, request->host, portbuff); } } @@ -320,7 +329,7 @@ static int send_ssl_response (struct conn_s *connptr) * build a new request line. Finally connect to the remote server. */ static struct request_s *process_request (struct conn_s *connptr, - hashmap_t hashofheaders) + orderedmap hashofheaders) { char *url; struct request_s *request; @@ -378,7 +387,7 @@ BAD_REQUEST_ERROR: } #ifdef REVERSE_SUPPORT - if (config.reversepath_list != NULL) { + 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 @@ -392,7 +401,7 @@ BAD_REQUEST_ERROR: if (reverse_url != NULL) { safefree (url); url = reverse_url; - } else if (config.reverseonly) { + } else if (config->reverseonly) { log_message (LOG_ERR, "Bad request, no mapping for '%s' found", url); @@ -425,7 +434,7 @@ BAD_REQUEST_ERROR: /* Verify that the port in the CONNECT method is allowed */ if (!check_allowed_connect_ports (request->port, - config.connect_ports)) + config->connect_ports)) { indicate_http_error (connptr, 403, "Access violation", "detail", @@ -442,7 +451,7 @@ BAD_REQUEST_ERROR: } else { #ifdef TRANSPARENT_PROXY if (!do_transparent_proxy - (connptr, hashofheaders, request, &config, &url)) { + (connptr, hashofheaders, request, config, &url)) { goto fail; } #else @@ -460,16 +469,16 @@ BAD_REQUEST_ERROR: /* * Filter restricted domains/urls */ - if (config.filter) { - if (config.filter_url) - ret = filter_url (url); + if (config->filter) { + if (config->filter_url) + ret = filter_run (url); else - ret = filter_domain (request->host); + ret = filter_run (request->host); if (ret) { update_stats (STAT_DENIED); - if (config.filter_url) + if (config->filter_url) log_message (LOG_NOTICE, "Proxying refused on filtered url \"%s\"", url); @@ -491,7 +500,7 @@ BAD_REQUEST_ERROR: /* * Check to see if they're requesting the stat host */ - if (config.stathost && strcmp (config.stathost, request->host) == 0) { + if (config->stathost && strcmp (config->stathost, request->host) == 0) { log_message (LOG_NOTICE, "Request for the stathost."); connptr->show_stats = TRUE; goto fail; @@ -601,7 +610,7 @@ static int add_xtinyproxy_header (struct conn_s *connptr) * can be retrieved and manipulated later. */ static int -add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len) +add_header_to_connection (orderedmap hashofheaders, char *header, size_t len) { char *sep; @@ -610,7 +619,7 @@ add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len) sep = strchr (header, ':'); if (!sep) - return -1; + return 0; /* just skip invalid header, do not give error */ /* Blank out colons, spaces, and tabs. */ while (*sep == ':' || *sep == ' ' || *sep == '\t') @@ -619,7 +628,7 @@ add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len) /* Calculate the new length of just the data */ len -= sep - header - 1; - return hashmap_insert (hashofheaders, header, sep, len); + return orderedmap_append (hashofheaders, header, sep); } /* @@ -733,7 +742,7 @@ static int get_all_headers (int fd, hashmap_t hashofheaders, char** lines, size_ * 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 (orderedmap hashofheaders) { static const char *headers[] = { "connection", @@ -747,12 +756,13 @@ static int remove_connection_headers (hashmap_t hashofheaders) 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) + data = orderedmap_find(hashofheaders, headers[i]); + + if (!data) return 0; + len = strlen(data); + /* * Go through the data line and replace any special characters * with a NULL. @@ -767,7 +777,7 @@ static int remove_connection_headers (hashmap_t hashofheaders) */ ptr = data; while (ptr < data + len) { - hashmap_remove (hashofheaders, ptr); + orderedmap_remove (hashofheaders, ptr); /* Advance ptr to the next token */ ptr += strlen (ptr) + 1; @@ -776,7 +786,7 @@ static int remove_connection_headers (hashmap_t hashofheaders) } /* Now remove the connection header it self. */ - hashmap_remove (hashofheaders, headers[i]); + orderedmap_remove (hashofheaders, headers[i]); } return 0; @@ -786,16 +796,14 @@ static int remove_connection_headers (hashmap_t hashofheaders) * 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 (orderedmap hashofheaders) { - ssize_t len; char *data; long content_length = -1; - len = - hashmap_entry_by_key (hashofheaders, "content-length", - (void **) &data); - if (len > 0) + data = orderedmap_find (hashofheaders, "content-length"); + + if (data) content_length = atol (data); return content_length; @@ -809,21 +817,20 @@ static long get_content_length (hashmap_t hashofheaders) * purposes. */ static int -write_via_header (int fd, hashmap_t hashofheaders, +write_via_header (int fd, orderedmap hashofheaders, unsigned int major, unsigned int minor) { - ssize_t len; char hostname[512]; char *data; int ret; - if (config.disable_viaheader) { + if (config->disable_viaheader) { ret = 0; goto done; } - if (config.via_proxy_name) { - strlcpy (hostname, config.via_proxy_name, sizeof (hostname)); + if (config->via_proxy_name) { + strlcpy (hostname, config->via_proxy_name, sizeof (hostname)); } else if (gethostname (hostname, sizeof (hostname)) < 0) { strlcpy (hostname, "unknown", 512); } @@ -832,14 +839,14 @@ write_via_header (int fd, hashmap_t hashofheaders, * 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) { + data = orderedmap_find (hashofheaders, "via"); + if (data) { ret = write_message (fd, "Via: %s, %hu.%hu %s (%s/%s)\r\n", data, major, minor, hostname, PACKAGE, VERSION); - hashmap_remove (hashofheaders, "via"); + orderedmap_remove (hashofheaders, "via"); } else { ret = write_message (fd, "Via: %hu.%hu %s (%s/%s)\r\n", @@ -853,7 +860,7 @@ done: /* * Number of buckets to use internally in the hashmap. */ -#define HEADER_BUCKETS 256 +#define HEADER_BUCKETS 32 /* * Here we loop through all the headers the client is sending. If we @@ -862,7 +869,7 @@ done: * - rjkaes */ static int -process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) +process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) { static const char *skipheaders[] = { "host", @@ -873,7 +880,7 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) "upgrade" }; int i; - hashmap_iter iter; + size_t iter; int ret = 0; char *data, *header; @@ -906,7 +913,7 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) * Delete the headers listed in the skipheaders list */ for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) { - hashmap_remove (hashofheaders, skipheaders[i]); + orderedmap_remove (hashofheaders, skipheaders[i]); } /* Send, or add the Via header */ @@ -926,32 +933,27 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) /* * 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; - } + iter = 0; + while((iter = orderedmap_next(hashofheaders, iter, &data, &header))) { + if (!is_anonymous_enabled (config) + || anonymous_search (config, 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.add_xtinyproxy) + if (config->add_xtinyproxy) add_xtinyproxy_header (connptr); #endif @@ -986,15 +988,15 @@ static int process_server_headers (struct conn_s *connptr) char *response_line; - hashmap_t hashofheaders; - hashmap_iter iter; + orderedmap hashofheaders; + size_t 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. */ @@ -1016,7 +1018,7 @@ retry: goto retry; } - hashofheaders = hashmap_create (HEADER_BUCKETS); + hashofheaders = orderedmap_create (HEADER_BUCKETS); if (!hashofheaders) { safefree (response_line); return -1; @@ -1028,7 +1030,7 @@ retry: if (get_all_headers (connptr->server_fd, hashofheaders, NULL, NULL) < 0) { log_message (LOG_WARNING, "Could not retrieve all the headers from the remote server."); - hashmap_delete (hashofheaders); + orderedmap_destroy (hashofheaders); safefree (response_line); indicate_http_error (connptr, 503, @@ -1047,7 +1049,7 @@ retry: * Instead we'll free all the memory and return. */ if (connptr->protocol.major < 1) { - hashmap_delete (hashofheaders); + orderedmap_destroy (hashofheaders); safefree (response_line); return 0; } @@ -1074,7 +1076,7 @@ retry: * Delete the headers listed in the skipheaders list */ for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) { - hashmap_remove (hashofheaders, skipheaders[i]); + orderedmap_remove (hashofheaders, skipheaders[i]); } /* Send, or add the Via header */ @@ -1086,7 +1088,7 @@ retry: #ifdef REVERSE_SUPPORT /* Write tracking cookie for the magical reverse proxy path hack */ - if (config.reversemagic && connptr->reversepath) { + if (config->reversemagic && connptr->reversepath) { ret = write_message (connptr->client_fd, "Set-Cookie: " REVERSE_COOKIE "=%s; path=/\r\n", connptr->reversepath); @@ -1095,9 +1097,8 @@ retry: } /* Rewrite the HTTP redirect if needed */ - if (config.reversebaseurl && - hashmap_entry_by_key (hashofheaders, "location", - (void **) &header) > 0) { + if (config->reversebaseurl && + (header = orderedmap_find (hashofheaders, "location"))) { /* Look for a matching entry in the reversepath list */ while (reverse) { @@ -1113,16 +1114,16 @@ retry: ret = write_message (connptr->client_fd, "Location: %s%s%s\r\n", - config.reversebaseurl, + 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, + header, config->reversebaseurl, (reverse->path + 1), (header + len)); - hashmap_remove (hashofheaders, "location"); + orderedmap_remove (hashofheaders, "location"); } } #endif @@ -1130,19 +1131,15 @@ retry: /* * 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; - } + iter = 0; + while ((iter = orderedmap_next(hashofheaders, iter, &data, &header))) { + + ret = write_message (connptr->client_fd, + "%s: %s\r\n", data, header); + if (ret < 0) + goto ERROR_EXIT; } - hashmap_delete (hashofheaders); + orderedmap_destroy (hashofheaders); /* Write the final blank line to signify the end of the headers */ if (safe_write (connptr->client_fd, "\r\n", 2) < 0) @@ -1151,7 +1148,7 @@ retry: return 0; ERROR_EXIT: - hashmap_delete (hashofheaders); + orderedmap_destroy (hashofheaders); return -1; } @@ -1165,74 +1162,39 @@ ERROR_EXIT: */ 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; - ret = socket_nonblocking (connptr->client_fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the client socket " - "to non-blocking: %s", strerror(errno)); - return; - } - - ret = socket_nonblocking (connptr->server_fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the server socket " - "to non-blocking: %s", strerror(errno)); - return; - } - - 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; + pollfd_struct fds[2] = {0}; + fds[0].fd = connptr->client_fd; + fds[1].fd = connptr->server_fd; if (buffer_size (connptr->sbuffer) > 0) - FD_SET (connptr->client_fd, &wset); + fds[0].events |= MYPOLL_WRITE; if (buffer_size (connptr->cbuffer) > 0) - FD_SET (connptr->server_fd, &wset); + fds[1].events |= MYPOLL_WRITE; if (buffer_size (connptr->sbuffer) < MAXBUFFSIZE) - FD_SET (connptr->server_fd, &rset); + fds[1].events |= MYPOLL_READ; if (buffer_size (connptr->cbuffer) < MAXBUFFSIZE) - FD_SET (connptr->client_fd, &rset); + fds[0].events |= MYPOLL_READ; - ret = select (maxfd, &rset, &wset, NULL, &tv); + ret = mypoll(fds, 2, config->idletimeout); 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); + log_message (LOG_INFO, + "Idle Timeout (after " SELECT_OR_POLL ")"); return; - } else { - continue; - } } else if (ret < 0) { log_message (LOG_ERR, - "relay_connection: select() error \"%s\". " + "relay_connection: " SELECT_OR_POLL "() 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)) { + if (fds[1].revents & MYPOLL_READ) { bytes_received = read_buffer (connptr->server_fd, connptr->sbuffer); if (bytes_received < 0) @@ -1242,32 +1204,20 @@ static void relay_connection (struct conn_s *connptr) if (connptr->content_length.server == 0) break; } - if (FD_ISSET (connptr->client_fd, &rset) + if ((fds[0].revents & MYPOLL_READ) && read_buffer (connptr->client_fd, connptr->cbuffer) < 0) { break; } - if (FD_ISSET (connptr->server_fd, &wset) + if ((fds[1].revents & MYPOLL_WRITE) && write_buffer (connptr->server_fd, connptr->cbuffer) < 0) { break; } - if (FD_ISSET (connptr->client_fd, &wset) + if ((fds[0].revents & MYPOLL_WRITE) && 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. - */ - ret = socket_blocking (connptr->client_fd); - if (ret != 0) { - log_message(LOG_ERR, - "Failed to set client socket to blocking: %s", - strerror(errno)); - return; - } - while (buffer_size (connptr->sbuffer) > 0) { if (write_buffer (connptr->client_fd, connptr->sbuffer) < 0) break; @@ -1438,7 +1388,7 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request) connptr->server_fd = opensock (cur_upstream->host, cur_upstream->port, - connptr->server_ip_addr); + connptr->server_ip_addr, NULL); if (connptr->server_fd < 0) { log_message (LOG_WARNING, @@ -1494,26 +1444,27 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request) #endif } +/* this function "drains" remaining bytes in the read pipe from + the client. it's usually only called on error before displaying + an error code/page. */ static int get_request_entity(struct conn_s *connptr) { int ret; - fd_set rset; - struct timeval tv; + pollfd_struct fds[1] = {0}; - FD_ZERO (&rset); - FD_SET (connptr->client_fd, &rset); - tv.tv_sec = 0; - tv.tv_usec = 0; - ret = select (connptr->client_fd + 1, &rset, NULL, NULL, &tv); + fds[0].fd = connptr->client_fd; + fds[0].events |= MYPOLL_READ; + + ret = mypoll(fds, 1, config->idletimeout); if (ret == -1) { log_message (LOG_ERR, - "Error calling select on client fd %d: %s", + "Error calling " SELECT_OR_POLL " on client fd %d: %s", connptr->client_fd, strerror(errno)); } else if (ret == 0) { log_message (LOG_INFO, "no entity"); - } else if (ret == 1 && FD_ISSET (connptr->client_fd, &rset)) { + } else if (ret == 1 && (fds[0].revents & MYPOLL_READ)) { ssize_t nread; nread = read_buffer (connptr->client_fd, connptr->cbuffer); if (nread < 0) { @@ -1523,12 +1474,12 @@ get_request_entity(struct conn_s *connptr) ret = -1; } else { log_message (LOG_INFO, - "Read request entity of %d bytes", - nread); + "Read request entity of %ld bytes", + (long) nread); ret = 0; } } else { - log_message (LOG_ERR, "strange situation after select: " + log_message (LOG_ERR, "strange situation after " SELECT_OR_POLL ": " "ret = %d, but client_fd (%d) is not readable...", ret, connptr->client_fd); ret = -1; @@ -1537,6 +1488,31 @@ get_request_entity(struct conn_s *connptr) return ret; } +static void handle_connection_failure(struct conn_s *connptr, int got_headers) +{ + /* + * First, get the body if there is one. + * If we don't read all there is from the socket first, + * it is still marked for reading and we won't be able + * to send our data properly. + */ + if (!got_headers && get_request_entity (connptr) < 0) { + log_message (LOG_WARNING, + "Could not retrieve request entity"); + indicate_http_error (connptr, 400, "Bad Request", + "detail", + "Could not retrieve the request entity " + "the client.", NULL); + update_stats (STAT_BADCONN); + } + + if (connptr->error_variables) { + send_http_error_message (connptr); + } else if (connptr->show_stats) { + showstats (connptr); + } +} + /* * This is the main drive for each connection. As you can tell, for the @@ -1547,44 +1523,83 @@ get_request_entity(struct conn_s *connptr) * tinyproxy code, which was confusing, redundant. Hail progress. * - rjkaes */ -void handle_connection (int fd) +void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) { - ssize_t i; - struct conn_s *connptr; + +#define HC_FAIL() \ + do {handle_connection_failure(connptr, got_headers); goto done;} \ + while(0) + + int got_headers = 0, fd = connptr->client_fd; + size_t i; struct request_s *request = NULL; - hashmap_t hashofheaders = NULL; + struct timeval tv; + orderedmap hashofheaders = NULL; char sock_ipaddr[IP_LENGTH]; + char sock_ipaddr_alt[IP_LENGTH]=""; char peer_ipaddr[IP_LENGTH]; char peer_string[HOSTNAME_LENGTH]; char *lines = NULL; size_t lines_len = 0; - getpeer_information (fd, peer_ipaddr, peer_string); + getpeer_information (addr, peer_ipaddr, sizeof(peer_ipaddr)); - if (config.bindsame) + 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); + switch (SOCKADDR_UNION_AF(addr)) { + case AF_INET: + if (config->bind_ipv4mapped) + getmapped_ipv6 (config->bind_ipv4mapped, sock_ipaddr, sock_ipaddr_alt); + break; + case AF_INET6: + if (config->bind_ipv6mapped) + getmapped_ipv4 (config->bind_ipv6mapped, sock_ipaddr, sock_ipaddr_alt); + break; + } + + log_message (LOG_CONN, config->bindsame ? + "Connect (file descriptor %d): %s at [%s]" : + "Connect (file descriptor %d): %s", + fd, peer_ipaddr, sock_ipaddr); - connptr = initialize_conn (fd, peer_ipaddr, peer_string, - config.bindsame ? sock_ipaddr : NULL); - if (!connptr) { + if(!conn_init_contents (connptr, peer_ipaddr, + config->bindsame ? sock_ipaddr : NULL, + sock_ipaddr_alt[0] ? sock_ipaddr_alt : NULL)) { close (fd); return; } - if (check_acl (peer_ipaddr, peer_string, config.access_list) <= 0) { + tv.tv_usec = 0; + tv.tv_sec = config->idletimeout; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (void*) &tv, sizeof(tv)); + tv.tv_usec = 0; + tv.tv_sec = config->idletimeout; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (void*) &tv, sizeof(tv)); + + if (connection_loops (addr)) { + log_message (LOG_CONN, + "Prevented endless loop (file descriptor %d): %s", + fd, peer_ipaddr); + + indicate_http_error(connptr, 400, "Bad Request", + "detail", + "You tried to connect to the " + "machine the proxy is running on", + NULL); + HC_FAIL(); + } + + + if (check_acl (peer_ipaddr, addr, config->access_list) <= 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); - goto fail; + HC_FAIL(); } if (read_request_line (connptr, &lines, &lines_len) < 0) { @@ -1593,13 +1608,13 @@ void handle_connection (int fd) "detail", "Server timeout waiting for the HTTP request " "from the client.", NULL); - goto fail; + HC_FAIL(); } /* * The "hashofheaders" store the client's headers. */ - hashofheaders = hashmap_create (HEADER_BUCKETS); + hashofheaders = orderedmap_create (HEADER_BUCKETS); if (hashofheaders == NULL) { update_stats (STAT_BADCONN); indicate_http_error (connptr, 503, "Internal error", @@ -1607,7 +1622,7 @@ void handle_connection (int fd) "An internal server error occurred while processing " "your request. Please contact the administrator.", NULL); - goto fail; + HC_FAIL(); } /* @@ -1621,52 +1636,59 @@ void handle_connection (int fd) "Could not retrieve all the headers from " "the client.", NULL); update_stats (STAT_BADCONN); - goto fail; + HC_FAIL(); } + got_headers = 1; - if (config.basicauth_list != NULL) { - ssize_t len; + if (config->basicauth_list != NULL) { char *authstring; - int failure = 1; - len = hashmap_entry_by_key (hashofheaders, "proxy-authorization", - (void **) &authstring); + int failure = 1, stathost_connect = 0; + authstring = orderedmap_find (hashofheaders, "proxy-authorization"); + + if (!authstring && config->stathost) { + authstring = orderedmap_find (hashofheaders, "host"); + if (authstring && !strncmp(authstring, config->stathost, strlen(config->stathost))) { + authstring = orderedmap_find (hashofheaders, "authorization"); + stathost_connect = 1; + } else authstring = 0; + } - if (len == 0) { + if (!authstring) { + if (stathost_connect) goto e401; update_stats (STAT_DENIED); indicate_http_error (connptr, 407, "Proxy Authentication Required", "detail", "This proxy requires authentication.", NULL); - goto fail; + HC_FAIL(); } if ( /* currently only "basic" auth supported */ (strncmp(authstring, "Basic ", 6) == 0 || strncmp(authstring, "basic ", 6) == 0) && - basicauth_check (config.basicauth_list, authstring + 6) == 1) + basicauth_check (config->basicauth_list, authstring + 6) == 1) failure = 0; if(failure) { +e401: update_stats (STAT_DENIED); indicate_http_error (connptr, 401, "Unauthorized", "detail", "The administrator of this proxy has not configured " "it to service requests from you.", NULL); - goto fail; + HC_FAIL(); } - hashmap_remove (hashofheaders, "proxy-authorization"); + orderedmap_remove (hashofheaders, "proxy-authorization"); } /* * Add any user-specified headers (AddHeader directive) to the * outgoing HTTP request. */ - for (i = 0; i < vector_length (config.add_headers); i++) { - http_header_t *header = (http_header_t *) - vector_getentry (config.add_headers, i, NULL); + if (config->add_headers) + for (i = 0; i < sblist_getsize (config->add_headers); i++) { + http_header_t *header = sblist_get (config->add_headers, i); - hashmap_insert (hashofheaders, - header->name, - header->value, strlen (header->value) + 1); + orderedmap_append (hashofheaders, header->name, header->value); } request = process_request (connptr, hashofheaders); @@ -1674,24 +1696,25 @@ void handle_connection (int fd) if (!connptr->show_stats) { update_stats (STAT_BADCONN); } - goto fail; + HC_FAIL(); } connptr->upstream_proxy = UPSTREAM_HOST (request->host); if (connptr->upstream_proxy != NULL) { if (connect_to_upstream (connptr, request) < 0) { - goto fail; + HC_FAIL(); } } else { connptr->server_fd = opensock (request->host, request->port, - connptr->server_ip_addr); + connptr->server_ip_addr, + connptr->server_ip_addr_alt); if (connptr->server_fd < 0) { indicate_http_error (connptr, 500, "Unable to connect", "detail", PACKAGE_NAME " " "was unable to connect to the remote web server.", "error", strerror (errno), NULL); - goto fail; + HC_FAIL(); } log_message (LOG_CONN, @@ -1710,13 +1733,25 @@ void handle_connection (int fd) if (process_client_headers (connptr, hashofheaders) < 0) { update_stats (STAT_BADCONN); - goto fail; + log_message (LOG_INFO, + "process_client_headers failed: %s. host \"%s\" using " + "file descriptor %d.", strerror(errno), + request->host, + connptr->server_fd); + + HC_FAIL(); } if (!connptr->connect_method || UPSTREAM_IS_HTTP(connptr)) { if (process_server_headers (connptr) < 0) { update_stats (STAT_BADCONN); - goto fail; + log_message (LOG_INFO, + "process_server_headers failed: %s. host \"%s\" using " + "file descriptor %d.", strerror(errno), + request->host, + connptr->server_fd); + + HC_FAIL(); } } else if (connptr->connect_method == CM_UPGRADE) { /* NOP */ ; @@ -1726,7 +1761,7 @@ void handle_connection (int fd) "handle_connection: Could not send SSL greeting " "to client."); update_stats (STAT_BADCONN); - goto fail; + HC_FAIL(); } } @@ -1737,35 +1772,11 @@ void handle_connection (int fd) "and remote client (fd:%d)", connptr->client_fd, connptr->server_fd); - goto done; - -fail: - /* - * First, get the body if there is one. - * If we don't read all there is from the socket first, - * it is still marked for reading and we won't be able - * to send our data properly. - */ - if (get_request_entity (connptr) < 0) { - log_message (LOG_WARNING, - "Could not retrieve request entity"); - indicate_http_error (connptr, 400, "Bad Request", - "detail", - "Could not retrieve the request entity " - "the client.", NULL); - update_stats (STAT_BADCONN); - } - - if (connptr->error_variables) { - send_http_error_message (connptr); - } else if (connptr->show_stats) { - showstats (connptr); - } - done: safefree(lines); free_request_struct (request); - hashmap_delete (hashofheaders); - destroy_conn (connptr); + orderedmap_destroy (hashofheaders); + conn_destroy_contents (connptr); return; +#undef HC_FAIL } |