summaryrefslogtreecommitdiffhomepage
path: root/src/reqs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/reqs.c')
-rw-r--r--src/reqs.c489
1 files changed, 250 insertions, 239 deletions
diff --git a/src/reqs.c b/src/reqs.c
index 9675980..cf87a9b 100644
--- a/src/reqs.c
+++ b/src/reqs.c
@@ -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
}