diff --git a/src/Makefile.am b/src/Makefile.am index d132a75..79be210 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,7 +50,7 @@ tinyproxy_SOURCES = \ base64.c base64.h \ sblist.c sblist.h \ hsearch.c hsearch.h \ - orderedmap.c orderedmap.h \ + pseudomap.c pseudomap.h \ loop.c loop.h \ mypoll.c mypoll.h \ connect-ports.c connect-ports.h diff --git a/src/orderedmap.c b/src/orderedmap.c deleted file mode 100644 index 1818e27..0000000 --- a/src/orderedmap.c +++ /dev/null @@ -1,115 +0,0 @@ -#ifdef HAVE_CONFIG_H -# include -#else -# define _ALL_SOURCE -# define _GNU_SOURCE -#endif -#include -#include "sblist.h" -#include "orderedmap.h" - -static void orderedmap_destroy_contents(struct orderedmap *o) { - char **p, *q; - size_t i; - htab_value *v; - if(!o) return; - if(o->values) { - while(sblist_getsize(o->values)) { - p = sblist_get(o->values, 0); - if(p) free(*p); - sblist_delete(o->values, 0); - } - sblist_free(o->values); - } - if(o->map) { - i = 0; - while((i = htab_next(o->map, i, &q, &v))) - free(q); - htab_destroy(o->map); - } -} - -struct orderedmap *orderedmap_create(size_t nbuckets) { - struct orderedmap o = {0}, *new; - o.values = sblist_new(sizeof(void*), 32); - if(!o.values) goto oom; - o.map = htab_create(nbuckets); - if(!o.map) goto oom; - new = malloc(sizeof o); - if(!new) goto oom; - memcpy(new, &o, sizeof o); - return new; - oom:; - orderedmap_destroy_contents(&o); - return 0; -} - -void* orderedmap_destroy(struct orderedmap *o) { - orderedmap_destroy_contents(o); - free(o); - return 0; -} - -int orderedmap_append(struct orderedmap *o, const char *key, char *value) { - size_t index; - char *nk, *nv; - nk = nv = 0; - nk = strdup(key); - nv = strdup(value); - if(!nk || !nv) goto oom; - index = sblist_getsize(o->values); - if(!sblist_add(o->values, &nv)) goto oom; - if(!htab_insert(o->map, nk, HTV_N(index))) { - sblist_delete(o->values, index); - goto oom; - } - return 1; -oom:; - free(nk); - free(nv); - return 0; -} - -char* orderedmap_find(struct orderedmap *o, const char *key) { - char **p; - htab_value *v = htab_find(o->map, key); - if(!v) return 0; - p = sblist_get(o->values, v->n); - return p?*p:0; -} - -int orderedmap_remove(struct orderedmap *o, const char *key) { - size_t i; - char *lk; - char *sk; - char **sv; - htab_value *lv, *v = htab_find2(o->map, key, &sk); - if(!v) return 0; - sv = sblist_get(o->values, v->n); - free(*sv); - sblist_delete(o->values, v->n); - i = 0; - while((i = htab_next(o->map, i, &lk, &lv))) { - if(lv->n > v->n) lv->n--; - } - htab_delete(o->map, key); - free(sk); - return 1; -} - -size_t orderedmap_next(struct orderedmap *o, size_t iter, char** key, char** value) { - size_t h_iter; - htab_value* hval; - char **p; - if(iter < sblist_getsize(o->values)) { - h_iter = 0; - while((h_iter = htab_next(o->map, h_iter, key, &hval))) { - if(hval->n == iter) { - p = sblist_get(o->values, iter); - *value = p?*p:0; - return iter+1; - } - } - } - return 0; -} diff --git a/src/orderedmap.h b/src/orderedmap.h deleted file mode 100644 index e3f4b0e..0000000 --- a/src/orderedmap.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ORDEREDMAP_H -#define ORDEREDMAP_H - -#include -#include "sblist.h" -#include "hsearch.h" - -typedef struct orderedmap { - sblist* values; - struct htab *map; -} *orderedmap; - -struct orderedmap *orderedmap_create(size_t nbuckets); -void* orderedmap_destroy(struct orderedmap *o); -int orderedmap_append(struct orderedmap *o, const char *key, char *value ); -char* orderedmap_find(struct orderedmap *o, const char *key); -int orderedmap_remove(struct orderedmap *o, const char *key); -size_t orderedmap_next(struct orderedmap *o, size_t iter, char** key, char** value); - -#endif diff --git a/src/pseudomap.c b/src/pseudomap.c new file mode 100644 index 0000000..5870a1f --- /dev/null +++ b/src/pseudomap.c @@ -0,0 +1,97 @@ +#include "config.h" +#include "pseudomap.h" +#include +#include +#include + +/* this data structure implements a pseudo hashmap. + tinyproxy originally used a hashmap for keeping the key/value pairs + in HTTP requests; however later it turned out that items need to be + returned in order - so we implemented an "orderedmap". + again, later it turned out that there are are special case headers, + namely Set-Cookie that can happen more than once, so a hashmap isn't the + right structure to hold the key-value pairs in HTTP headers. + it's expected that: + 1) the number of headers in a HTTP request we have to process is + not big enough to cause a noticable performance drop when we have + to iterate through our list to find the right header; and + 2) use of plain HTTP is getting exceedingly extinct by the day, so + in most usecases CONNECT method is used anyway. +*/ + +/* restrict the number of headers to 256 to prevent an attacker from + launching a denial of service attack. */ +#define MAX_SIZE 256 + +pseudomap *pseudomap_create(void) { + return sblist_new(sizeof(struct pseudomap_entry), 64); +} + +void pseudomap_destroy(pseudomap *o) { + while(sblist_getsize(o)) { + /* retrieve latest element, and "shrink" list in place, + so we don't have to constantly rearrange list items + by using sblist_delete(). */ + struct pseudomap_entry *e = sblist_get(o, sblist_getsize(o)-1); + free(e->key); + free(e->value); + --o->count; + } + sblist_free(o); +} + +int pseudomap_append(pseudomap *o, const char *key, char *value ) { + struct pseudomap_entry e; + if(sblist_getsize(o) >= MAX_SIZE) return 0; + e.key = strdup(key); + e.value = strdup(value); + if(!e.key || !e.value) goto oom; + if(!sblist_add(o, &e)) goto oom; + return 1; +oom: + free(e.key); + free(e.value); + return 0; +} + +static size_t pseudomap_find_index(pseudomap *o, const char *key) { + size_t i; + struct pseudomap_entry *e; + for(i = 0; i < sblist_getsize(o); ++i) { + e = sblist_get(o, i); + if(!strcasecmp(key, e->key)) return i; + } + return (size_t)-1; +} + +char* pseudomap_find(pseudomap *o, const char *key) { + struct pseudomap_entry *e; + size_t i = pseudomap_find_index(o, key); + if(i == (size_t)-1) return 0; + e = sblist_get(o, i); + return e->value; +} + +/* remove *all* entries that match key, to mimic behaviour of hashmap */ +int pseudomap_remove(pseudomap *o, const char *key) { + struct pseudomap_entry *e; + size_t i; + int ret = 0; + while((i = pseudomap_find_index(o, key)) != (size_t)-1) { + e = sblist_get(o, i); + free(e->key); + free(e->value); + ret = 1; + } + return ret; +} + +size_t pseudomap_next(pseudomap *o, size_t iter, char** key, char** value) { + struct pseudomap_entry *e; + if(iter >= sblist_getsize(o)) return 0; + e = sblist_get(o, iter); + *key = e->key; + *value = e->value; + return iter + 1; +} + diff --git a/src/pseudomap.h b/src/pseudomap.h new file mode 100644 index 0000000..24c67b7 --- /dev/null +++ b/src/pseudomap.h @@ -0,0 +1,22 @@ +#ifndef PSEUDOMAP_H +#define PSEUDOMAP_H + +#include +#include "sblist.h" + +struct pseudomap_entry { + char *key; + char *value; +}; + +typedef sblist pseudomap; + +pseudomap *pseudomap_create(void); +void pseudomap_destroy(pseudomap *o); +int pseudomap_append(pseudomap *o, const char *key, char *value ); +char* pseudomap_find(pseudomap *o, const char *key); +int pseudomap_remove(pseudomap *o, const char *key); +size_t pseudomap_next(pseudomap *o, size_t iter, char** key, char** value); + +#endif + diff --git a/src/reqs.c b/src/reqs.c index 50f6914..e57369b 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -33,7 +33,7 @@ #include "conns.h" #include "filter.h" #include "hsearch.h" -#include "orderedmap.h" +#include "pseudomap.h" #include "heap.h" #include "html-error.h" #include "log.h" @@ -323,7 +323,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, - orderedmap hashofheaders) + pseudomap *hashofheaders) { char *url; struct request_s *request; @@ -647,7 +647,7 @@ static int add_xtinyproxy_header (struct conn_s *connptr) * can be retrieved and manipulated later. */ static int -add_header_to_connection (orderedmap hashofheaders, char *header, size_t len) +add_header_to_connection (pseudomap *hashofheaders, char *header, size_t len) { char *sep; @@ -665,7 +665,7 @@ add_header_to_connection (orderedmap hashofheaders, char *header, size_t len) /* Calculate the new length of just the data */ len -= sep - header - 1; - return orderedmap_append (hashofheaders, header, sep); + return pseudomap_append (hashofheaders, header, sep); } /* @@ -678,7 +678,7 @@ add_header_to_connection (orderedmap hashofheaders, char *header, size_t len) /* * Read all the headers from the stream */ -static int get_all_headers (int fd, orderedmap hashofheaders) +static int get_all_headers (int fd, pseudomap *hashofheaders) { char *line = NULL; char *header = NULL; @@ -771,7 +771,7 @@ static int get_all_headers (int fd, orderedmap hashofheaders) * Extract the headers to remove. These headers were listed in the Connection * and Proxy-Connection headers. */ -static int remove_connection_headers (orderedmap hashofheaders) +static int remove_connection_headers (pseudomap *hashofheaders) { static const char *headers[] = { "connection", @@ -785,7 +785,7 @@ static int remove_connection_headers (orderedmap hashofheaders) for (i = 0; i != (sizeof (headers) / sizeof (char *)); ++i) { /* Look for the connection header. If it's not found, return. */ - data = orderedmap_find(hashofheaders, headers[i]); + data = pseudomap_find(hashofheaders, headers[i]); if (!data) return 0; @@ -806,7 +806,7 @@ static int remove_connection_headers (orderedmap hashofheaders) */ ptr = data; while (ptr < data + len) { - orderedmap_remove (hashofheaders, ptr); + pseudomap_remove (hashofheaders, ptr); /* Advance ptr to the next token */ ptr += strlen (ptr) + 1; @@ -815,7 +815,7 @@ static int remove_connection_headers (orderedmap hashofheaders) } /* Now remove the connection header it self. */ - orderedmap_remove (hashofheaders, headers[i]); + pseudomap_remove (hashofheaders, headers[i]); } return 0; @@ -825,12 +825,12 @@ static int remove_connection_headers (orderedmap hashofheaders) * If there is a Content-Length header, then return the value; otherwise, return * -1. */ -static long get_content_length (orderedmap hashofheaders) +static long get_content_length (pseudomap *hashofheaders) { char *data; long content_length = -1; - data = orderedmap_find (hashofheaders, "content-length"); + data = pseudomap_find (hashofheaders, "content-length"); if (data) content_length = atol (data); @@ -838,10 +838,10 @@ static long get_content_length (orderedmap hashofheaders) return content_length; } -static int is_chunked_transfer (orderedmap hashofheaders) +static int is_chunked_transfer (pseudomap *hashofheaders) { char *data; - data = orderedmap_find (hashofheaders, "transfer-encoding"); + data = pseudomap_find (hashofheaders, "transfer-encoding"); return data ? !strcmp (data, "chunked") : 0; } @@ -853,7 +853,7 @@ static int is_chunked_transfer (orderedmap hashofheaders) * purposes. */ static int -write_via_header (int fd, orderedmap hashofheaders, +write_via_header (int fd, pseudomap *hashofheaders, unsigned int major, unsigned int minor) { char hostname[512]; @@ -875,14 +875,14 @@ write_via_header (int fd, orderedmap hashofheaders, * See if there is a "Via" header. If so, again we need to do a bit * of processing. */ - data = orderedmap_find (hashofheaders, "via"); + data = pseudomap_find (hashofheaders, "via"); if (data) { ret = write_message (fd, "Via: %s, %hu.%hu %s (%s/%s)\r\n", data, major, minor, hostname, PACKAGE, VERSION); - orderedmap_remove (hashofheaders, "via"); + pseudomap_remove (hashofheaders, "via"); } else { ret = write_message (fd, "Via: %hu.%hu %s (%s/%s)\r\n", @@ -893,11 +893,6 @@ done: return ret; } -/* - * 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 @@ -905,7 +900,7 @@ done: * - rjkaes */ static int -process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) +process_client_headers (struct conn_s *connptr, pseudomap *hashofheaders) { static const char *skipheaders[] = { "host", @@ -953,7 +948,7 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) * Delete the headers listed in the skipheaders list */ for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) { - orderedmap_remove (hashofheaders, skipheaders[i]); + pseudomap_remove (hashofheaders, skipheaders[i]); } /* Send, or add the Via header */ @@ -974,7 +969,7 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) * Output all the remaining headers to the remote machine. */ iter = 0; - while((iter = orderedmap_next(hashofheaders, iter, &data, &header))) { + while((iter = pseudomap_next(hashofheaders, iter, &data, &header))) { if (!is_anonymous_enabled (config) || anonymous_search (config, data) > 0) { ret = @@ -1029,7 +1024,7 @@ static int process_server_headers (struct conn_s *connptr) char *response_line; - orderedmap hashofheaders; + pseudomap *hashofheaders; size_t iter; char *data, *header; ssize_t len; @@ -1059,7 +1054,7 @@ retry: goto retry; } - hashofheaders = orderedmap_create (HEADER_BUCKETS); + hashofheaders = pseudomap_create (); if (!hashofheaders) { safefree (response_line); return -1; @@ -1071,7 +1066,7 @@ retry: if (get_all_headers (connptr->server_fd, hashofheaders) < 0) { log_message (LOG_WARNING, "Could not retrieve all the headers from the remote server."); - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); safefree (response_line); indicate_http_error (connptr, 503, @@ -1090,7 +1085,7 @@ retry: * Instead we'll free all the memory and return. */ if (connptr->protocol.major < 1) { - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); safefree (response_line); return 0; } @@ -1117,7 +1112,7 @@ retry: * Delete the headers listed in the skipheaders list */ for (i = 0; i != (sizeof (skipheaders) / sizeof (char *)); i++) { - orderedmap_remove (hashofheaders, skipheaders[i]); + pseudomap_remove (hashofheaders, skipheaders[i]); } /* Send, or add the Via header */ @@ -1139,7 +1134,7 @@ retry: /* Rewrite the HTTP redirect if needed */ if (config->reversebaseurl && - (header = orderedmap_find (hashofheaders, "location"))) { + (header = pseudomap_find (hashofheaders, "location"))) { /* Look for a matching entry in the reversepath list */ while (reverse) { @@ -1164,7 +1159,7 @@ retry: "Rewriting HTTP redirect: %s -> %s%s%s", header, config->reversebaseurl, (reverse->path + 1), (header + len)); - orderedmap_remove (hashofheaders, "location"); + pseudomap_remove (hashofheaders, "location"); } } #endif @@ -1173,14 +1168,14 @@ retry: * All right, output all the remaining headers to the client. */ iter = 0; - while ((iter = orderedmap_next(hashofheaders, iter, &data, &header))) { + while ((iter = pseudomap_next(hashofheaders, iter, &data, &header))) { ret = write_message (connptr->client_fd, "%s: %s\r\n", data, header); if (ret < 0) goto ERROR_EXIT; } - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); /* Write the final blank line to signify the end of the headers */ if (safe_write (connptr->client_fd, "\r\n", 2) < 0) @@ -1189,7 +1184,7 @@ retry: return 0; ERROR_EXIT: - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); return -1; } @@ -1579,7 +1574,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) int got_headers = 0, fd = connptr->client_fd; size_t i; struct request_s *request = NULL; - orderedmap hashofheaders = NULL; + pseudomap *hashofheaders = NULL; char sock_ipaddr[IP_LENGTH]; char peer_ipaddr[IP_LENGTH]; @@ -1638,7 +1633,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) /* * The "hashofheaders" store the client's headers. */ - hashofheaders = orderedmap_create (HEADER_BUCKETS); + hashofheaders = pseudomap_create (); if (hashofheaders == NULL) { update_stats (STAT_BADCONN); indicate_http_error (connptr, 503, "Internal error", @@ -1667,12 +1662,12 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) if (config->basicauth_list != NULL) { char *authstring; int failure = 1, stathost_connect = 0; - authstring = orderedmap_find (hashofheaders, "proxy-authorization"); + authstring = pseudomap_find (hashofheaders, "proxy-authorization"); if (!authstring && config->stathost) { - authstring = orderedmap_find (hashofheaders, "host"); + authstring = pseudomap_find (hashofheaders, "host"); if (authstring && !strncmp(authstring, config->stathost, strlen(config->stathost))) { - authstring = orderedmap_find (hashofheaders, "authorization"); + authstring = pseudomap_find (hashofheaders, "authorization"); stathost_connect = 1; } else authstring = 0; } @@ -1701,7 +1696,7 @@ e401: NULL); HC_FAIL(); } - orderedmap_remove (hashofheaders, "proxy-authorization"); + pseudomap_remove (hashofheaders, "proxy-authorization"); } /* @@ -1712,7 +1707,7 @@ e401: for (i = 0; i < sblist_getsize (config->add_headers); i++) { http_header_t *header = sblist_get (config->add_headers, i); - orderedmap_append (hashofheaders, header->name, header->value); + pseudomap_append (hashofheaders, header->name, header->value); } request = process_request (connptr, hashofheaders); @@ -1790,7 +1785,7 @@ e401: done: free_request_struct (request); - orderedmap_destroy (hashofheaders); + pseudomap_destroy (hashofheaders); conn_destroy_contents (connptr); return; #undef HC_FAIL diff --git a/src/reverse-proxy.c b/src/reverse-proxy.c index 68f04a6..98b0963 100644 --- a/src/reverse-proxy.c +++ b/src/reverse-proxy.c @@ -127,7 +127,7 @@ void free_reversepath_list (struct reversepath *reverse) /* * Rewrite the URL for reverse proxying. */ -char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders, +char *reverse_rewrite_url (struct conn_s *connptr, pseudomap *hashofheaders, char *url, int *status) { char *rewrite_url = NULL; @@ -153,7 +153,7 @@ char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders, sprintf (rewrite_url, "%s%s", reverse->url, url + lrp); } } else if (config->reversemagic - && (cookie = orderedmap_find (hashofheaders, + && (cookie = pseudomap_find (hashofheaders, "cookie"))) { /* No match - try the magical tracking cookie next */ diff --git a/src/reverse-proxy.h b/src/reverse-proxy.h index a9a5bdd..7ded66d 100644 --- a/src/reverse-proxy.h +++ b/src/reverse-proxy.h @@ -22,7 +22,7 @@ #define TINYPROXY_REVERSE_PROXY_H #include "conns.h" -#include "orderedmap.h" +#include "pseudomap.h" struct reversepath { struct reversepath *next; @@ -38,7 +38,7 @@ extern struct reversepath *reversepath_get (char *url, struct reversepath *reverse); void free_reversepath_list (struct reversepath *reverse); extern char *reverse_rewrite_url (struct conn_s *connptr, - orderedmap hashofheaders, char *url, + pseudomap *hashofheaders, char *url, int *status); #endif diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index d090ae3..d0ba2c8 100644 --- a/src/transparent-proxy.c +++ b/src/transparent-proxy.c @@ -53,7 +53,7 @@ static int build_url (char **url, const char *host, int port, const char *path) } int -do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders, +do_transparent_proxy (struct conn_s *connptr, pseudomap *hashofheaders, struct request_s *request, struct config_s *conf, char **url) { @@ -62,7 +62,7 @@ do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders, size_t ulen = strlen (*url); size_t i; - data = orderedmap_find (hashofheaders, "host"); + data = pseudomap_find (hashofheaders, "host"); if (!data) { union sockaddr_union dest_addr; const void *dest_inaddr; diff --git a/src/transparent-proxy.h b/src/transparent-proxy.h index b56c446..1167aed 100644 --- a/src/transparent-proxy.h +++ b/src/transparent-proxy.h @@ -26,11 +26,11 @@ #ifdef TRANSPARENT_PROXY #include "conns.h" -#include "orderedmap.h" +#include "pseudomap.h" #include "reqs.h" extern int do_transparent_proxy (struct conn_s *connptr, - orderedmap hashofheaders, + pseudomap *hashofheaders, struct request_s *request, struct config_s *config, char **url);