From c7c88e9c59a15d23a3de7d2e22781c57ec49b1fe Mon Sep 17 00:00:00 2001 From: John Weldon Date: Tue, 20 Feb 2018 09:29:11 -0700 Subject: [PATCH 001/265] Remove #ifdef for HAVE_SYSLOG_H - syslog.h is a standard posix header, this #ifdef is an artifact accidentally left in. --- src/conf.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/conf.c b/src/conf.c index 4ee209d2..80bdab8a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -783,12 +783,7 @@ static HANDLE_FUNC (handle_xtinyproxy) static HANDLE_FUNC (handle_syslog) { -#ifdef HAVE_SYSLOG_H return set_bool_arg (&conf->syslog, line, &match[2]); -#else - fprintf (stderr, "Syslog support not compiled in executable.\n"); - return 1; -#endif } static HANDLE_FUNC (handle_bindsame) From 81ea4feb2ed6ee2fabd4e2ec0557318d764757a2 Mon Sep 17 00:00:00 2001 From: bertliao Date: Sun, 25 Feb 2018 12:18:46 +0800 Subject: [PATCH 002/265] fix possible memory leak --- src/transparent-proxy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index 5d0c8f6c..df5fbcee 100644 --- a/src/transparent-proxy.c +++ b/src/transparent-proxy.c @@ -45,7 +45,7 @@ static int build_url (char **url, const char *host, int port, const char *path) assert (path != NULL); len = strlen (host) + strlen (path) + 14; - *url = (char *) safemalloc (len); + *url = (char *) saferealloc (*url, len); if (*url == NULL) return -1; From 4d9891e59e7c7ee77137fcdb3d02798af96811ff Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 23 Feb 2018 20:16:39 +0000 Subject: [PATCH 003/265] basicauth.[ch]: refactor to make basicauth_string() reusable --- src/basicauth.c | 41 ++++++++++++++++++++++++++--------------- src/basicauth.h | 4 ++++ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/basicauth.c b/src/basicauth.c index 4360a1a0..70926578 100644 --- a/src/basicauth.c +++ b/src/basicauth.c @@ -26,35 +26,46 @@ #include "conf.h" #include "base64.h" +/* + * Create basic-auth token in buf. + * Returns strlen of token on success, + * -1 if user/pass missing + * 0 if user/pass too long + */ +ssize_t basicauth_string(const char *user, const char *pass, + char *buf, size_t bufsize) +{ + char tmp[256+2]; + int l; + if (!user || !pass) return -1; + l = snprintf(tmp, sizeof tmp, "%s:%s", user, pass); + if (l < 0 || l >= (ssize_t) sizeof tmp) return 0; + if (bufsize < (BASE64ENC_BYTES((unsigned)l) + 1)) return 0; + base64enc(buf, tmp, l); + return BASE64ENC_BYTES(l); +} + /* * Add entry to the basicauth list */ void basicauth_add (vector_t authlist, const char *user, const char *pass) { - char tmp[256+2]; - char b[BASE64ENC_BYTES((sizeof tmp)-1) + 1]; - int l; - size_t bl; + char b[BASE64ENC_BYTES((256+2)-1) + 1]; + ssize_t ret; - if (user == NULL || pass == NULL) { + ret = basicauth_string(user, pass, b, sizeof b); + if (ret == -1) { log_message (LOG_WARNING, "Illegal basicauth rule: missing user or pass"); return; - } - - l = snprintf(tmp, sizeof tmp, "%s:%s", user, pass); - - if(l >= (ssize_t) sizeof tmp) { + } else if (ret == 0) { log_message (LOG_WARNING, - "User / pass in basicauth rule too long"); + "User / pass in basicauth rule too long"); return; } - base64enc(b, tmp, l); - bl = BASE64ENC_BYTES(l) + 1; - - if (vector_append(authlist, b, bl) == -ENOMEM) { + if (vector_append(authlist, b, ret + 1) == -ENOMEM) { log_message (LOG_ERR, "Unable to allocate memory in basicauth_add()"); return; diff --git a/src/basicauth.h b/src/basicauth.h index e9366bb5..61dc5c32 100644 --- a/src/basicauth.h +++ b/src/basicauth.h @@ -21,8 +21,12 @@ #ifndef TINYPROXY_BASICAUTH_H #define TINYPROXY_BASICAUTH_H +#include #include "vector.h" +extern ssize_t basicauth_string(const char *user, const char *pass, + char *buf, size_t bufsize); + extern void basicauth_add (vector_t authlist, const char *user, const char *pass); From bf76aeeba1cf868ebe4d2f28976abb75c48031b0 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 23 Feb 2018 20:21:32 +0000 Subject: [PATCH 004/265] implement HTTP basic auth for upstream proxies loosely based on @valenbg1's code from PR #38 closes #38 closes #96 --- etc/tinyproxy.conf.in | 3 +++ src/conf.c | 44 +++++++++++++++++++++++++++---------------- src/reqs.c | 11 +++++++++++ src/upstream.c | 26 +++++++++++++++++++++++-- src/upstream.h | 6 ++++++ 5 files changed, 72 insertions(+), 18 deletions(-) diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index c43266bb..ac16efe2 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -146,6 +146,9 @@ LogLevel Info # upstream testproxy:8008 ".our_testbed.example.com" # upstream testproxy:8008 "192.168.128.0/255.255.254.0" # +# # upstream proxy using basic authentication +# upstream user:pass@testproxy:8008 ".test.domain.invalid" +# # # no upstream proxy for internal websites and unqualified hosts # no upstream ".internal.example.com" # no upstream "www.example.com" diff --git a/src/conf.c b/src/conf.c index 80bdab8a..7f62172a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -259,8 +259,11 @@ struct { BEGIN "(no" WS "upstream)" WS STR END, handle_upstream_no, NULL }, { - BEGIN "(upstream)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR - ")?" END, handle_upstream, NULL + BEGIN "(upstream)" WS + "(" ALNUM /*username*/ ":" ALNUM /*password*/ "@" ")?" + "(" IP "|" ALNUM ")" + ":" INT "(" WS STR ")?" + END, handle_upstream, NULL }, { BEGIN "(upstream4)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR @@ -1100,24 +1103,33 @@ static int _handle_upstream(struct config_s* conf, const char* line, regmatch_t match[], proxy_type type) { char *ip; - int port; - char *domain; + int port, mi = 3; + char *domain = 0, *user = 0, *pass = 0; + + if (match[mi].rm_so != -1) + user = get_string_arg (line, &match[mi]); + mi++; + + if (match[mi].rm_so != -1) + pass = get_string_arg (line, &match[mi]); + mi++; - ip = get_string_arg (line, &match[2]); + ip = get_string_arg (line, &match[mi]); if (!ip) return -1; - port = (int) get_long_arg (line, &match[7]); + mi += 5; - if (match[10].rm_so != -1) { - domain = get_string_arg (line, &match[10]); - if (domain) { - upstream_add (ip, port, domain, type, &conf->upstream_list); - safefree (domain); - } - } else { - upstream_add (ip, port, NULL, type, &conf->upstream_list); - } + port = (int) get_long_arg (line, &match[mi]); + mi += 3; + if (match[mi].rm_so != -1) + domain = get_string_arg (line, &match[mi]); + + upstream_add (ip, port, domain, user, pass, type, &conf->upstream_list); + + safefree (user); + safefree (pass); + safefree (domain); safefree (ip); return 0; @@ -1146,7 +1158,7 @@ static HANDLE_FUNC (handle_upstream_no) if (!domain) return -1; - upstream_add (NULL, 0, domain, HTTP_TYPE, &conf->upstream_list); + upstream_add (NULL, 0, domain, 0, 0, HTTP_TYPE, &conf->upstream_list); safefree (domain); return 0; diff --git a/src/reqs.c b/src/reqs.c index 0e4e5f77..180b35a3 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -270,6 +270,17 @@ establish_http_connection (struct conn_s *connptr, struct request_s *request) "Connection: close\r\n", request->method, request->path, request->host, portbuff); + } else if (connptr->upstream_proxy && + connptr->upstream_proxy->type == HTTP_TYPE && + connptr->upstream_proxy->ua.authstr) { + return write_message (connptr->server_fd, + "%s %s HTTP/1.0\r\n" + "Host: %s%s\r\n" + "Connection: close\r\n" + "Proxy-Authorization: Basic %s\r\n", + request->method, request->path, + request->host, portbuff, + connptr->upstream_proxy->ua.authstr); } else { return write_message (connptr->server_fd, "%s %s HTTP/1.0\r\n" diff --git a/src/upstream.c b/src/upstream.c index 91bf4572..03a6b8ef 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -27,6 +27,8 @@ #include "upstream.h" #include "heap.h" #include "log.h" +#include "base64.h" +#include "basicauth.h" #ifdef UPSTREAM_SUPPORT const char * @@ -44,6 +46,7 @@ proxy_type_name(proxy_type type) * Construct an upstream struct from input data. */ static struct upstream *upstream_build (const char *host, int port, const char *domain, + const char *user, const char *pass, proxy_type type) { char *ptr; @@ -57,8 +60,24 @@ static struct upstream *upstream_build (const char *host, int port, const char * } up->type = type; - up->host = up->domain = NULL; + up->host = up->domain = up->ua.user = up->pass = NULL; up->ip = up->mask = 0; + if (user) { + if (type == HTTP_TYPE) { + char b[BASE64ENC_BYTES((256+2)-1) + 1]; + ssize_t ret; + ret = basicauth_string(user, pass, b, sizeof b); + if (ret == 0) { + log_message (LOG_ERR, + "User / pass in upstream config too long"); + return NULL; + } + up->ua.authstr = safestrdup (b); + } else { + up->ua.user = safestrdup (user); + up->pass = safestrdup (pass); + } + } if (domain == NULL) { if (!host || host[0] == '\0' || port < 1) { @@ -121,6 +140,8 @@ static struct upstream *upstream_build (const char *host, int port, const char * return up; fail: + safefree (up->ua.user); + safefree (up->pass); safefree (up->host); safefree (up->domain); safefree (up); @@ -132,11 +153,12 @@ static struct upstream *upstream_build (const char *host, int port, const char * * Add an entry to the upstream list */ void upstream_add (const char *host, int port, const char *domain, + const char *user, const char *pass, proxy_type type, struct upstream **upstream_list) { struct upstream *up; - up = upstream_build (host, port, domain, type); + up = upstream_build (host, port, domain, user, pass, type); if (up == NULL) { return; } diff --git a/src/upstream.h b/src/upstream.h index 78552149..9b664c89 100644 --- a/src/upstream.h +++ b/src/upstream.h @@ -36,6 +36,11 @@ struct upstream { struct upstream *next; char *domain; /* optional */ char *host; + union { + char *user; + char *authstr; + } ua; + char *pass; int port; in_addr_t ip, mask; proxy_type type; @@ -44,6 +49,7 @@ struct upstream { #ifdef UPSTREAM_SUPPORT const char *proxy_type_name(proxy_type type); extern void upstream_add (const char *host, int port, const char *domain, + const char *user, const char *pass, proxy_type type, struct upstream **upstream_list); extern struct upstream *upstream_get (char *host, struct upstream *up); extern void free_upstream_list (struct upstream *up); From e1c11c47db91597f0041152fc06e7b558cdfc9e1 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 25 Feb 2018 17:59:16 +0000 Subject: [PATCH 005/265] make send_stored_logs static --- src/log.c | 2 +- src/log.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/log.c b/src/log.c index c5368b8f..69b6dc07 100644 --- a/src/log.c +++ b/src/log.c @@ -217,7 +217,7 @@ void log_message (int level, const char *fmt, ...) /* * This needs to send any stored log messages. */ -void send_stored_logs (void) +static void send_stored_logs (void) { char *string; char *ptr; diff --git a/src/log.h b/src/log.h index 68c89c3e..76bfe6bb 100644 --- a/src/log.h +++ b/src/log.h @@ -106,7 +106,6 @@ extern void close_log_file (void); extern void log_message (int level, const char *fmt, ...); extern void set_log_level (int level); -extern void send_stored_logs (void); extern int setup_logging (void); extern void shutdown_logging (void); From 86632a91eb65d6b3df7bcdaebef3e1ea4e3a568a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 25 Feb 2018 17:59:24 +0000 Subject: [PATCH 006/265] fix early logging tinyproxy uses a curious mechanism to log those early messages that result from parsing the config file before the logging mechanism has been properly set up yet by finishing parsing of the config file: those early messages are written into a memory buffer and then are printed later on. this slipped my attention when making it possible to log to stdout in ccbbb81a. --- src/log.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/log.c b/src/log.c index 69b6dc07..f85d29d8 100644 --- a/src/log.c +++ b/src/log.c @@ -115,9 +115,6 @@ void log_message (int level, const char *fmt, ...) ssize_t ret; - if(!config.syslog && log_file_fd == -1) - return; - #ifdef NDEBUG /* * Figure out if we should write the message or not. @@ -164,6 +161,9 @@ void log_message (int level, const char *fmt, ...) goto out; } + if(!config.syslog && log_file_fd == -1) + goto out; + if (config.syslog) { #ifdef HAVE_VSYSLOG_H vsyslog (level, fmt, args); From 39132b978707fc284199b2b947af6c8c846cade8 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 25 Feb 2018 15:53:43 +0000 Subject: [PATCH 007/265] rename members of proxy_type enum to have a common prefix and add a NONE member. --- src/conf.c | 8 ++++---- src/reqs.c | 10 +++++----- src/upstream.c | 9 +++++---- src/upstream.h | 8 +++++++- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/conf.c b/src/conf.c index 7f62172a..ca9764e5 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1137,17 +1137,17 @@ static int _handle_upstream(struct config_s* conf, const char* line, static HANDLE_FUNC (handle_upstream) { - return _handle_upstream(conf, line, match, HTTP_TYPE); + return _handle_upstream(conf, line, match, PT_HTTP); } static HANDLE_FUNC (handle_upstream4) { - return _handle_upstream(conf, line, match, SOCKS4_TYPE); + return _handle_upstream(conf, line, match, PT_SOCKS4); } static HANDLE_FUNC (handle_upstream5) { - return _handle_upstream(conf, line, match, SOCKS5_TYPE); + return _handle_upstream(conf, line, match, PT_SOCKS5); } static HANDLE_FUNC (handle_upstream_no) @@ -1158,7 +1158,7 @@ static HANDLE_FUNC (handle_upstream_no) if (!domain) return -1; - upstream_add (NULL, 0, domain, 0, 0, HTTP_TYPE, &conf->upstream_list); + upstream_add (NULL, 0, domain, 0, 0, PT_HTTP, &conf->upstream_list); safefree (domain); return 0; diff --git a/src/reqs.c b/src/reqs.c index 180b35a3..a71cd470 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -62,7 +62,7 @@ #ifdef UPSTREAM_SUPPORT # 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 == HTTP_TYPE) +# define UPSTREAM_IS_HTTP(conn) (conn->upstream_proxy != NULL && conn->upstream_proxy->type == PT_HTTP) #else # define UPSTREAM_CONFIGURED() (0) # define UPSTREAM_HOST(host) (NULL) @@ -271,7 +271,7 @@ establish_http_connection (struct conn_s *connptr, struct request_s *request) request->method, request->path, request->host, portbuff); } else if (connptr->upstream_proxy && - connptr->upstream_proxy->type == HTTP_TYPE && + connptr->upstream_proxy->type == PT_HTTP && connptr->upstream_proxy->ua.authstr) { return write_message (connptr->server_fd, "%s %s HTTP/1.0\r\n" @@ -1292,7 +1292,7 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) "Established connection to %s proxy \"%s\" using file descriptor %d.", proxy_type_name(cur_upstream->type), cur_upstream->host, connptr->server_fd); - if (cur_upstream->type == SOCKS4_TYPE) { + if (cur_upstream->type == PT_SOCKS4) { buff[0] = 4; /* socks version */ buff[1] = 1; /* connect command */ @@ -1308,7 +1308,7 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) if (buff[0]!=0 || buff[1]!=90) return -1; - } else if (cur_upstream->type == SOCKS5_TYPE) { + } else if (cur_upstream->type == PT_SOCKS5) { /* init */ buff[0] = 5; /* socks version */ @@ -1404,7 +1404,7 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request) return -1; } - if (cur_upstream->type != HTTP_TYPE) + if (cur_upstream->type != PT_HTTP) return connect_to_upstream_proxy(connptr, request); log_message (LOG_CONN, diff --git a/src/upstream.c b/src/upstream.c index 03a6b8ef..0c6b14e6 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -35,9 +35,10 @@ const char * proxy_type_name(proxy_type type) { switch(type) { - case HTTP_TYPE: return "http"; - case SOCKS4_TYPE: return "socks4"; - case SOCKS5_TYPE: return "socks5"; + case PT_NONE: return "none"; + case PT_HTTP: return "http"; + case PT_SOCKS4: return "socks4"; + case PT_SOCKS5: return "socks5"; default: return "unknown"; } } @@ -63,7 +64,7 @@ static struct upstream *upstream_build (const char *host, int port, const char * up->host = up->domain = up->ua.user = up->pass = NULL; up->ip = up->mask = 0; if (user) { - if (type == HTTP_TYPE) { + if (type == PT_HTTP) { char b[BASE64ENC_BYTES((256+2)-1) + 1]; ssize_t ret; ret = basicauth_string(user, pass, b, sizeof b); diff --git a/src/upstream.h b/src/upstream.h index 9b664c89..c1127849 100644 --- a/src/upstream.h +++ b/src/upstream.h @@ -31,7 +31,13 @@ * Even if upstream support is not compiled into tinyproxy, this * structure still needs to be defined. */ -typedef enum {HTTP_TYPE, SOCKS4_TYPE, SOCKS5_TYPE} proxy_type; +typedef enum proxy_type { + PT_NONE = 0, + PT_HTTP, + PT_SOCKS4, + PT_SOCKS5 +} proxy_type; + struct upstream { struct upstream *next; char *domain; /* optional */ From 9cde492d686ec77d5cfe7d98dfa7bddfe9c477e4 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 25 Feb 2018 17:02:15 +0000 Subject: [PATCH 008/265] configure.ac: remove -pedantic --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c3d4ac6d..6b40679f 100644 --- a/configure.ac +++ b/configure.ac @@ -173,7 +173,7 @@ AC_CHECK_FUNCS([inet_ntoa strdup]) AC_CHECK_FUNCS([strlcpy strlcat setgroups]) dnl Enable extra warnings -DESIRED_FLAGS="-fdiagnostics-show-option -Wall -Wextra -Wno-unused-parameter -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -Wfloat-equal -Wundef -Wformat=2 -Wlogical-op -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Waggregate-return -Winit-self -Wpacked --std=c89 -ansi -pedantic -Wno-overlength-strings -Wno-long-long -Wno-overlength-strings -Wdeclaration-after-statement -Wredundant-decls -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-qual -Wcast-align -Wwrite-strings -Wp,-D_FORTIFY_SOURCE=2 -fno-common" +DESIRED_FLAGS="-fdiagnostics-show-option -Wall -Wextra -Wno-unused-parameter -Wmissing-prototypes -Wstrict-prototypes -Wmissing-declarations -Wfloat-equal -Wundef -Wformat=2 -Wlogical-op -Wmissing-include-dirs -Wformat-nonliteral -Wold-style-definition -Wpointer-arith -Waggregate-return -Winit-self -Wpacked --std=c89 -ansi -Wno-overlength-strings -Wno-long-long -Wno-overlength-strings -Wdeclaration-after-statement -Wredundant-decls -Wmissing-noreturn -Wshadow -Wendif-labels -Wcast-qual -Wcast-align -Wwrite-strings -Wp,-D_FORTIFY_SOURCE=2 -fno-common" if test -n "${MAINTAINER_MODE_FALSE}"; then DESIRED_FLAGS="-Werror $DESIRED_FLAGS" From 057cf068058e99b47734898268c8c77861b05136 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 25 Feb 2018 17:14:24 +0000 Subject: [PATCH 009/265] config: unify upstream syntax for http,socks4,socks5 and none closes #50 --- etc/tinyproxy.conf.in | 37 +++++++++++++----------- src/conf.c | 65 ++++++++++++++++++++----------------------- src/upstream.c | 2 +- 3 files changed, 52 insertions(+), 52 deletions(-) diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index ac16efe2..54024cbd 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -140,32 +140,37 @@ LogLevel Info # The upstream rules allow you to selectively route upstream connections # based on the host/domain of the site being accessed. # +# Syntax: upstream type (user:pass@)ip:port ("domain") +# Or: upstream none "domain" +# The parts in parens are optional. +# Possible types are http, socks4, socks5, none +# # For example: # # connection to test domain goes through testproxy -# upstream testproxy:8008 ".test.domain.invalid" -# upstream testproxy:8008 ".our_testbed.example.com" -# upstream testproxy:8008 "192.168.128.0/255.255.254.0" +# upstream http testproxy:8008 ".test.domain.invalid" +# upstream http testproxy:8008 ".our_testbed.example.com" +# upstream http testproxy:8008 "192.168.128.0/255.255.254.0" # # # upstream proxy using basic authentication -# upstream user:pass@testproxy:8008 ".test.domain.invalid" +# upstream http user:pass@testproxy:8008 ".test.domain.invalid" # # # no upstream proxy for internal websites and unqualified hosts -# no upstream ".internal.example.com" -# no upstream "www.example.com" -# no upstream "10.0.0.0/8" -# no upstream "192.168.0.0/255.255.254.0" -# no upstream "." +# upstream none ".internal.example.com" +# upstream none "www.example.com" +# upstream none "10.0.0.0/8" +# upstream none "192.168.0.0/255.255.254.0" +# upstream none "." # # # connection to these boxes go through their DMZ firewalls -# upstream cust1_firewall:8008 "testbed_for_cust1" -# upstream cust2_firewall:8008 "testbed_for_cust2" +# upstream http cust1_firewall:8008 "testbed_for_cust1" +# upstream http cust2_firewall:8008 "testbed_for_cust2" # # # default upstream is internet firewall -# upstream firewall.internal.example.com:80 +# upstream http firewall.internal.example.com:80 # -# You may also use SOCKS4/SOCKS5 upstream proxies by using upstream4/upstream5: -# upstream4 127.0.0.1:9050 -# upstream5 socksproxy:1080 +# You may also use SOCKS4/SOCKS5 upstream proxies: +# upstream socks4 127.0.0.1:9050 +# upstream socks5 socksproxy:1080 # # The LAST matching rule wins the route decision. As you can see, you # can use a host, or a domain: @@ -175,7 +180,7 @@ LogLevel Info # IP/bits matches network/mask # IP/mask matches network/mask # -#Upstream some.remote.proxy:port +#Upstream http some.remote.proxy:port # # MaxClients: This is the absolute highest number of threads which will diff --git a/src/conf.c b/src/conf.c index ca9764e5..ac7354fb 100644 --- a/src/conf.c +++ b/src/conf.c @@ -162,8 +162,6 @@ static HANDLE_FUNC (handle_xtinyproxy); #ifdef UPSTREAM_SUPPORT static HANDLE_FUNC (handle_upstream); -static HANDLE_FUNC (handle_upstream4); -static HANDLE_FUNC (handle_upstream5); static HANDLE_FUNC (handle_upstream_no); #endif @@ -254,25 +252,16 @@ struct { STDCONF ("reversepath", STR "(" WS STR ")?", handle_reversepath), #endif #ifdef UPSTREAM_SUPPORT - /* upstream is rather complicated */ { - BEGIN "(no" WS "upstream)" WS STR END, handle_upstream_no, NULL + BEGIN "(upstream)" WS "(none)" WS STR END, handle_upstream_no, NULL }, { - BEGIN "(upstream)" WS + BEGIN "(upstream)" WS "(http|socks4|socks5)" WS "(" ALNUM /*username*/ ":" ALNUM /*password*/ "@" ")?" "(" IP "|" ALNUM ")" ":" INT "(" WS STR ")?" END, handle_upstream, NULL }, - { - BEGIN "(upstream4)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR - ")?" END, handle_upstream4, NULL - }, - { - BEGIN "(upstream5)" WS "(" IP "|" ALNUM ")" ":" INT "(" WS STR - ")?" END, handle_upstream5, NULL - }, #endif /* loglevel */ STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)", @@ -1099,12 +1088,33 @@ static HANDLE_FUNC (handle_reversepath) #endif #ifdef UPSTREAM_SUPPORT -static int _handle_upstream(struct config_s* conf, const char* line, - regmatch_t match[], proxy_type type) + +static enum proxy_type pt_from_string(const char *s) +{ + static const char pt_map[][7] = { + [PT_NONE] = "none", + [PT_HTTP] = "http", + [PT_SOCKS4] = "socks4", + [PT_SOCKS5] = "socks5", + }; + unsigned i; + for (i = 0; i < sizeof(pt_map)/sizeof(pt_map[0]); i++) + if (!strcmp(pt_map[i], s)) + return i; + return PT_NONE; +} + +static HANDLE_FUNC (handle_upstream) { char *ip; - int port, mi = 3; - char *domain = 0, *user = 0, *pass = 0; + int port, mi = 2; + char *domain = 0, *user = 0, *pass = 0, *tmp; + enum proxy_type pt; + + tmp = get_string_arg (line, &match[mi]); + pt = pt_from_string(tmp); + safefree(tmp); + mi += 2; if (match[mi].rm_so != -1) user = get_string_arg (line, &match[mi]); @@ -1125,7 +1135,7 @@ static int _handle_upstream(struct config_s* conf, const char* line, if (match[mi].rm_so != -1) domain = get_string_arg (line, &match[mi]); - upstream_add (ip, port, domain, user, pass, type, &conf->upstream_list); + upstream_add (ip, port, domain, user, pass, pt, &conf->upstream_list); safefree (user); safefree (pass); @@ -1135,30 +1145,15 @@ static int _handle_upstream(struct config_s* conf, const char* line, return 0; } -static HANDLE_FUNC (handle_upstream) -{ - return _handle_upstream(conf, line, match, PT_HTTP); -} - -static HANDLE_FUNC (handle_upstream4) -{ - return _handle_upstream(conf, line, match, PT_SOCKS4); -} - -static HANDLE_FUNC (handle_upstream5) -{ - return _handle_upstream(conf, line, match, PT_SOCKS5); -} - static HANDLE_FUNC (handle_upstream_no) { char *domain; - domain = get_string_arg (line, &match[2]); + domain = get_string_arg (line, &match[3]); if (!domain) return -1; - upstream_add (NULL, 0, domain, 0, 0, PT_HTTP, &conf->upstream_list); + upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list); safefree (domain); return 0; diff --git a/src/upstream.c b/src/upstream.c index 0c6b14e6..38afacd7 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -92,7 +92,7 @@ static struct upstream *upstream_build (const char *host, int port, const char * log_message (LOG_INFO, "Added upstream %s %s:%d for [default]", proxy_type_name(type), host, port); - } else if (host == NULL) { + } else if (host == NULL || type == PT_NONE) { if (!domain || domain[0] == '\0') { log_message (LOG_WARNING, "Nonsense no-upstream rule: empty domain"); From e78b4616077a4a37058aba6e5aae68534310511e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 25 Feb 2018 18:18:53 +0000 Subject: [PATCH 010/265] update upstream syntax in manpage template --- docs/man5/tinyproxy.conf.txt.in | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index e40135b4..8f73720a 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -144,21 +144,25 @@ The possible keywords and their descriptions are as follows: `X-Tinyproxy` containing the client's IP address to the request. *Upstream*:: -*No Upstream*:: This option allows you to set up a set of rules for deciding whether an upstream proxy server is to be used, based on the host or domain of the site being accessed. The rules are stored in the order encountered in the configuration file and the - LAST matching rule wins. There are three possible forms for - specifying upstream rules: + LAST matching rule wins. The following forms for specifying upstream + rules exist: - * 'upstream host:port' turns proxy upstream support on generally. + * 'upstream type host:port' turns proxy upstream support on generally. - * 'upstream host:port "site_spec"' turns on the upstream proxy for - the sites matching `site_spec`. + * 'upstream type user:pass@host:port' does the same, but uses the + supplied credentials for authentication. - * 'no upstream "site_spec"' turns off upstream support for sites + * 'upstream type host:port "site_spec"' turns on the upstream proxy + for the sites matching `site_spec`. + + `type` can be one of `http`, `socks4`, `socks5`, `none`. + + * 'upstream none "site_spec"' turns off upstream support for sites matching `site_spec`. The site can be specified in various forms as a hostname, domain From b8c6a2127de637c2c41eaf5d9b61c857116690e6 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 26 Feb 2018 00:13:58 +0000 Subject: [PATCH 011/265] implement user/password auth for socks5 upstream proxy just like the rest of the socks code, this was stolen from proxychains-ng, of which i'm happen to be the maintainer of, so it's not an issue (the licenses are identical, too). --- src/reqs.c | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index a71cd470..202f77fa 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1285,9 +1285,15 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) unsigned len; unsigned char buff[512]; /* won't use more than 7 + 255 */ unsigned short port; + size_t ulen, passlen; + struct hostent *host; struct upstream *cur_upstream = connptr->upstream_proxy; + ulen = cur_upstream->ua.user ? strlen(cur_upstream->ua.user) : 0; + passlen = cur_upstream->pass ? strlen(cur_upstream->pass) : 0; + + log_message(LOG_CONN, "Established connection to %s proxy \"%s\" using file descriptor %d.", proxy_type_name(cur_upstream->type), cur_upstream->host, connptr->server_fd); @@ -1311,15 +1317,43 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) } else if (cur_upstream->type == PT_SOCKS5) { /* init */ + int n_methods = ulen ? 2 : 1; buff[0] = 5; /* socks version */ - buff[1] = 1; /* number of methods */ + buff[1] = n_methods; /* number of methods */ buff[2] = 0; /* no auth method */ - if (3 != safe_write(connptr->server_fd, buff, 3)) + if (ulen) buff[3] = 2; /* auth method -> username / password */ + if (2+n_methods != safe_write(connptr->server_fd, buff, 2+n_methods)) return -1; if (2 != safe_read(connptr->server_fd, buff, 2)) return -1; - if (buff[0]!=5 || buff[1]!=0) + if (buff[0] != 5 || (buff[1] != 0 && buff[1] != 2)) return -1; + + if (buff[1] == 2) { + /* authentication */ + char in[2]; + char out[515]; + char *cur = out; + size_t c; + *cur++ = 1; /* version */ + c = ulen & 0xFF; + *cur++ = c; + memcpy(cur, cur_upstream->ua.user, c); + cur += c; + c = passlen & 0xFF; + *cur++ = c; + memcpy(cur, cur_upstream->pass, c); + cur += c; + + if((cur - out) != safe_write(connptr->server_fd, out, cur - out)) + return -1; + + if(2 != safe_read(connptr->server_fd, in, 2)) + return -1; + if(in[0] != 5 || in[1] != 0) { + return -1; + } + } /* connect */ buff[0] = 5; /* socks version */ buff[1] = 1; /* connect */ From a53f62a212947600a785b3e94db55a856325920e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 25 Feb 2018 23:42:31 +0000 Subject: [PATCH 012/265] enable transparent proxy by default by having all features turned on by default, the binary is only slightly bigger, but users of binary distros get the whole package and don't need to compile tinyproxy by hand if they need a feature that wasn't compiled in. it also prevents the confusion from getting syntax errors when a config file using those features is parsed. another advantage is that by enabling them these features may actually get some more testing. --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 6b40679f..66e6031d 100644 --- a/configure.ac +++ b/configure.ac @@ -124,8 +124,8 @@ dnl Include the transparent proxy support AH_TEMPLATE([TRANSPARENT_PROXY], [Include support for using tinyproxy as a transparent proxy.]) TP_ARG_ENABLE(transparent, - [Enable transparent proxying code (default is NO)], - no) + [Enable transparent proxying code (default is YES)], + yes) if test x"$transparent_enabled" = x"yes"; then ADDITIONAL_OBJECTS="$ADDITIONAL_OBJECTS transparent-proxy.o" AC_DEFINE(TRANSPARENT_PROXY) From 09979629c03ae237617e7142594b5b9508be24a9 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 27 Feb 2018 20:34:50 +0000 Subject: [PATCH 013/265] make bind option usable with transparent proxy too closes #15 for real. the previous patch that was merged[0] was halfbaked and only removed the warning part of the original patch from openwrt[1], but didn't actually activate bind support. further it invoked UB by removing the return value from the function, if transparent proxy support was compiled in. [0]: d97d486d53ce214ae952378308292f333b8c7a36 [1]: https://gitlab.labs.nic.cz/turris/openwrt-packages/commit/7c01da4a72e6f0b7613a86529547659ea4007eba --- src/conf.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/conf.c b/src/conf.c index ac7354fb..5a87c689 100644 --- a/src/conf.c +++ b/src/conf.c @@ -876,7 +876,6 @@ static HANDLE_FUNC (handle_deny) static HANDLE_FUNC (handle_bind) { -#ifndef TRANSPARENT_PROXY int r = set_string_arg (&conf->bind_address, line, &match[2]); if (r) @@ -884,7 +883,6 @@ static HANDLE_FUNC (handle_bind) log_message (LOG_INFO, "Outgoing connections bound to IP %s", conf->bind_address); return 0; -#endif } static HANDLE_FUNC (handle_listen) From 8d0ea71486688323fff65bc3730bae209a70db7b Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 16 Mar 2018 15:21:20 +0000 Subject: [PATCH 014/265] install tinyproxy to bin/, not /sbin sbin/ is meant for programs only usable by root, but in tinyproxy's case, regular users can and *should* use tinyproxy; meaning it is preferable from a security PoV to use tinyproxy as regular user. --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index 565eb4db..18b47159 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,7 +17,7 @@ pkgsysconfdir = $(sysconfdir)/$(PACKAGE) -sbin_PROGRAMS = tinyproxy +bin_PROGRAMS = tinyproxy AM_CPPFLAGS = \ -DSYSCONFDIR=\"${pkgsysconfdir}\" \ From badb6435d616066a8648152a24a09f9e090a6e5e Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Thu, 16 Nov 2017 01:53:21 +0100 Subject: [PATCH 015/265] upstream: Fix case of empty string domain. Found by compiler note. Signed-off-by: Michael Adam --- src/upstream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/upstream.c b/src/upstream.c index 38afacd7..327b727d 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -124,7 +124,7 @@ static struct upstream *upstream_build (const char *host, int port, const char * log_message (LOG_INFO, "Added no-upstream for %s", domain); } else { if (!host || host[0] == '\0' || port < 1 || !domain - || domain == '\0') { + || domain[0] == '\0') { log_message (LOG_WARNING, "Nonsense upstream rule: invalid parameters"); goto fail; From ae0cbfe3f2f6d6c2ac3734b7b2026c9a8502c200 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Fri, 23 Mar 2018 02:03:54 +0100 Subject: [PATCH 016/265] html-error: Make a switch fallthrough explicit This silences a gcc v7 compile warning. Signed-off-by: Michael Adam --- src/html-error.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/html-error.c b/src/html-error.c index 0c011a76..625a586d 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -132,6 +132,7 @@ send_html_file (FILE *infile, struct conn_s *connptr) } else in_variable = 0; + /* FALL THROUGH */ default: if (!in_variable) { r = write_message (connptr->client_fd, From 0aad2f5b92c7690a25f849ffa0f7a41ee8b5ff46 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 28 Mar 2018 02:02:45 +0100 Subject: [PATCH 017/265] fix basicauth string comparison closes #160 --- src/basicauth.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/basicauth.c b/src/basicauth.c index 70926578..d6c24201 100644 --- a/src/basicauth.c +++ b/src/basicauth.c @@ -83,16 +83,15 @@ void basicauth_add (vector_t authlist, int basicauth_check (vector_t authlist, const char *authstring) { ssize_t vl, i; - size_t al, el; + size_t el; const char* entry; vl = vector_length (authlist); if (vl == -EINVAL) return 0; - al = strlen (authstring); for (i = 0; i < vl; i++) { entry = vector_getentry (authlist, i, &el); - if (strncmp (authstring, entry, al) == 0) + if (strcmp (authstring, entry) == 0) return 1; } return 0; From c651664720d1fc21aeb36ca8dbb625a874af1d97 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 29 May 2018 19:17:41 +0100 Subject: [PATCH 018/265] fix socks5 upstream user/pass subnegotiation check RFC 1929 specifies that the user/pass auth subnegotation repurposes the version field for the version of that specification, which is 1, not 5. however there's quite a good deal of software out there which got it wrong and replies with version 5 to a successful authentication, so let's just accept both forms - other socks5 client programs like curl do the same. closes #172 --- src/reqs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reqs.c b/src/reqs.c index 202f77fa..bbdcc74d 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1350,7 +1350,7 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) if(2 != safe_read(connptr->server_fd, in, 2)) return -1; - if(in[0] != 5 || in[1] != 0) { + if(in[1] != 0 || !(in[0] == 5 || in[0] == 1)) { return -1; } } From ec95d8b553e54dd4ed82d92f8e27b7ef29c55c77 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 01:26:42 +0200 Subject: [PATCH 019/265] configure: remove unused variables Signed-off-by: Michael Adam --- configure.ac | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/configure.ac b/configure.ac index 66e6031d..a47316b8 100644 --- a/configure.ac +++ b/configure.ac @@ -13,11 +13,6 @@ m4_define([tinyproxy_version], [tinyproxy_real_version]) # For overriding the version string. Comment out if not needed. # m4_define([tinyproxy_version], [1.9.0]) -m4_define([tinyproxy_unstable], - m4_if(m4_eval(tinyproxy_minor_version % 2), [1], [yes], [no])) -m4_define([tinyproxy_stable], - m4_if(m4_eval(tinyproxy_minor_version % 2), [0], [yes], [no])) - AC_INIT([Tinyproxy], [tinyproxy_version], [https://tinyproxy.github.io/], [tinyproxy]) @@ -29,19 +24,6 @@ AC_CONFIG_MACRO_DIR([m4macros]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) -TINYPROXY_MAJOR_VERSION=tinyproxy_major_version -TINYPROXY_MINOR_VERSION=tinyproxy_minor_version -TINYPROXY_MICRO_VERSION=tinyproxy_micro_version -TINYPROXY_REAL_VERSION=tinyproxy_real_version -TINYPROXY_VERSION=tinyproxy_version -TINYPROXY_UNSTABLE=tinyproxy_unstable -AC_SUBST(TINYPROXY_MAJOR_VERSION) -AC_SUBST(TINYPROXY_MINOR_VERSION) -AC_SUBST(TINYPROXY_MICRO_VERSION) -AC_SUBST(TINYPROXY_REAL_VERSION) -AC_SUBST(TINYPROXY_VERSION) -AC_SUBST(TINYPROXY_UNSTABLE) - dnl Temporarily defined here until we get tinyproxy-version.h AC_DEFINE(TINYPROXY_VERSION, "tinyproxy_version", [Tinyproxy version number]) From 89b2c68b2bf568dbedff52bc813bce39c8e6d027 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 01:45:27 +0200 Subject: [PATCH 020/265] NEWS: just mention to use git log Signed-off-by: Michael Adam --- NEWS | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/NEWS b/NEWS index 550e7fe0..c0fddc8c 100644 --- a/NEWS +++ b/NEWS @@ -1,12 +1 @@ -Tinyproxy NEWS -============== - -Version 1.9.0 -------------- - -Bugs fixed -~~~~~~~~~~ - -Contributors -~~~~~~~~~~~~ - +See git log for recent changes in Tinyproxy. From 56f30a438643ec3ec82d1ade7d8bed394120c0df Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 02:03:09 +0200 Subject: [PATCH 021/265] main: remove the "-l" switch to display the license and authors Signed-off-by: Michael Adam --- docs/man8/tinyproxy.txt.in | 3 -- src/main.c | 57 +------------------------------------- 2 files changed, 1 insertion(+), 59 deletions(-) diff --git a/docs/man8/tinyproxy.txt.in b/docs/man8/tinyproxy.txt.in index e259dcb7..b1c4a9a5 100644 --- a/docs/man8/tinyproxy.txt.in +++ b/docs/man8/tinyproxy.txt.in @@ -40,9 +40,6 @@ OPTIONS *-h*:: Display a short help screen of command line arguments and exit. -*-l*:: - Display the licensing agreement. - *-v*:: Display version information and exit. diff --git a/src/main.c b/src/main.c index 35ff704f..43170c56 100644 --- a/src/main.c +++ b/src/main.c @@ -32,7 +32,6 @@ #include "main.h" #include "anonymous.h" -#include "authors.h" #include "buffer.h" #include "conf.h" #include "daemon.h" @@ -87,55 +86,6 @@ display_version (void) printf ("%s %s\n", PACKAGE, VERSION); } -/* - * Display the copyright and license for this program. - */ -static void -display_license (void) -{ - const char * const *authors; - const char * const *documenters; - - display_version (); - - printf ("\ -\n\ - Copyright 1998 Steven Young (sdyoung@well.com)\n\ - Copyright 1998-2002 Robert James Kaes (rjkaes@users.sourceforge.net)\n\ - Copyright 1999 George Talusan (gstalusan@uwaterloo.ca)\n\ - Copyright 2000 Chris Lightfoot (chris@ex-parrot.com)\n\ - Copyright 2009-2010 Mukund Sivaraman (muks@banu.com)\n\ - Copyright 2009-2010 Michael Adam (obnox@samba.org)\n\ -\n\ - This program is free software; you can redistribute it and/or modify\n\ - it under the terms of the GNU General Public License as published by\n\ - the Free Software Foundation; either version 2, or (at your option)\n\ - any later version.\n\ -\n\ - This program is distributed in the hope that it will be useful,\n\ - but WITHOUT ANY WARRANTY; without even the implied warranty of\n\ - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\ - GNU General Public License for more details.\n\ -\n\ - You should have received a copy of the GNU General Public License\n\ - along with this program; if not, write to the Free Software\n\ - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.\n\ -\n"); - - printf ("\nAUTHORS:\n"); - for (authors = authors_get_authors (); *authors; authors++) { - printf (" %s\n", *authors); - } - - printf ("\nDOCUMENTERS:\n"); - for (documenters = authors_get_documenters (); - *documenters; documenters++) { - printf (" %s\n", *documenters); - } - - printf ("\n"); -} - /* * Display usage to the user. */ @@ -150,7 +100,6 @@ display_usage (void) " -d Do not daemonize (run in foreground).\n" " -c FILE Use an alternate configuration file.\n" " -h Display this usage information.\n" - " -l Display the license.\n" " -v Display version information.\n"); /* Display the modes compiled into tinyproxy */ @@ -224,16 +173,12 @@ process_cmdline (int argc, char **argv, struct config_s *conf) { int opt; - while ((opt = getopt (argc, argv, "c:vldh")) != EOF) { + while ((opt = getopt (argc, argv, "c:vdh")) != EOF) { switch (opt) { case 'v': display_version (); exit (EX_OK); - case 'l': - display_license (); - exit (EX_OK); - case 'd': conf->godaemon = FALSE; break; From a5e6d343d78462f9233015af9b7b0a1911d5e6db Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 02:07:39 +0200 Subject: [PATCH 022/265] Remove unused authors.c/authors.h and generation mechanism. Signed-off-by: Michael Adam --- src/Makefile.am | 14 ----------- src/authors.c | 50 -------------------------------------- src/authors.h | 30 ----------------------- src/authors.xsl | 64 ------------------------------------------------- 4 files changed, 158 deletions(-) delete mode 100644 src/authors.c delete mode 100644 src/authors.h delete mode 100644 src/authors.xsl diff --git a/src/Makefile.am b/src/Makefile.am index 18b47159..af2f621e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,6 @@ AM_CPPFLAGS = \ tinyproxy_SOURCES = \ acl.c acl.h \ anonymous.c anonymous.h \ - authors.c authors.h \ buffer.c buffer.h \ child.c child.h \ common.h \ @@ -56,16 +55,3 @@ EXTRA_tinyproxy_SOURCES = filter.c filter.h \ transparent-proxy.c transparent-proxy.h tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@ tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ - -EXTRA_DIST = \ - authors.xsl - -authors.c: $(top_srcdir)/authors.xml $(srcdir)/authors.xsl -if HAVE_XSLTPROC - $(AM_V_GEN) $(XSLTPROC) $(srcdir)/authors.xsl $< > $(@) || rm -f $(@) -else - @echo "*** xsltproc is required to regenerate $(@) ***"; exit 1; -endif - -BUILT_SOURCES = \ - authors.c diff --git a/src/authors.c b/src/authors.c deleted file mode 100644 index 496df93a..00000000 --- a/src/authors.c +++ /dev/null @@ -1,50 +0,0 @@ - -/* NOTE: This file is auto-generated from authors.xml, do not edit it. */ - -#include "authors.h" - -static const char * const authors[] = -{ - "Andrew Stribblehill", - "Chris Lightfoot", - "Daniel Egger", - "David Shanks", - "Dmitry Semyonov", - "George Talusan", - "James E. Flemer", - "Jeremy Hinegardner", - "John van der Kamp", - "Jordi Mallach", - "Kim Holviala", - "Mathew Mrosko", - "Matthew Dempsky", - "Michael Adam", - "Moritz Muehlenhoff", - "Mukund Sivaraman", - "Petr Lampa", - "Robert James Kaes", - "Steven Young", - NULL -}; - -static const char * const documenters[] = -{ - "Marc Silver", - "Michael Adam", - "Mukund Sivaraman", - "Robert James Kaes", - "Steven Young", - NULL -}; - -const char * const * -authors_get_authors (void) -{ - return authors; -} - -const char * const * -authors_get_documenters (void) -{ - return documenters; -} diff --git a/src/authors.h b/src/authors.h deleted file mode 100644 index 10e5110e..00000000 --- a/src/authors.h +++ /dev/null @@ -1,30 +0,0 @@ -/* tinyproxy - A fast light-weight HTTP proxy - * Copyright (C) 2010 Mukund Sivaraman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef __AUTHORS_H__ -#define __AUTHORS_H__ - -#include "common.h" - -const char * const * -authors_get_authors (void); - -const char * const * -authors_get_documenters (void); - -#endif /* __AUTHORS_H__ */ diff --git a/src/authors.xsl b/src/authors.xsl deleted file mode 100644 index 1388b9a7..00000000 --- a/src/authors.xsl +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - -/* NOTE: This file is auto-generated from authors.xml, do not edit it. */ - -#include "authors.h" - -static const char * const authors[] = -{ - - - - - NULL -}; - - - -static const char * const documenters[] = -{ - - - - - NULL -}; - -const char * const * -authors_get_authors (void) -{ - return authors; -} - -const char * const * -authors_get_documenters (void) -{ - return documenters; -} - - - - "", - - - From 3eb3c2c099a6097cd821e96c22a247648784c14b Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 02:25:50 +0200 Subject: [PATCH 023/265] Remove xml-based AUTHORS mechanism. AUTHORS is checked in. Signed-off-by: Michael Adam --- Makefile.am | 20 +------------------ authors.dtd | 12 ----------- authors.xml | 36 --------------------------------- authors.xsl | 57 ----------------------------------------------------- 4 files changed, 1 insertion(+), 124 deletions(-) delete mode 100644 authors.dtd delete mode 100644 authors.xml delete mode 100644 authors.xsl diff --git a/Makefile.am b/Makefile.am index 9d62d2cc..e9113c24 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,22 +9,7 @@ SUBDIRS = \ # tools want this on a single line ACLOCAL_AMFLAGS = -I m4macros -AUTHORS: authors.xml authors.xsl -if HAVE_XSLTPROC - $(AM_V_GEN) $(XSLTPROC) authors.xsl $< > $(@) || rm -f $(@) -else - @echo "*** xsltproc is required to regenerate $(@) ***"; exit 1; -endif - -validate-authors: -if HAVE_XMLLINT - @$(XMLLINT) --noout --path $(srcdir) --valid authors.xml || \ - ( echo "*** authors.xml IS INVALID ***"; exit 1; ) -endif - -all-local: AUTHORS - -check-local: validate-authors +all-local: dist_doc_DATA = \ AUTHORS \ @@ -33,9 +18,6 @@ dist_doc_DATA = \ README.md EXTRA_DIST = \ - authors.dtd \ - authors.xml \ - authors.xsl \ autogen.sh \ tinyproxy-indent.sh \ TODO diff --git a/authors.dtd b/authors.dtd deleted file mode 100644 index 77d43c53..00000000 --- a/authors.dtd +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/authors.xml b/authors.xml deleted file mode 100644 index 7fc94f2a..00000000 --- a/authors.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - Andrew Stribblehill - Chris Lightfoot - Daniel Egger - David Shanks - Dmitry Semyonov - George Talusan - James E. Flemer - Jeremy Hinegardner - John van der Kamp - Jordi Mallach - Kim Holviala - Marc Silver - Mathew Mrosko - Matthew Dempsky - Michael Adam - Moritz Muehlenhoff - Mukund Sivaraman - Petr Lampa - Robert James Kaes - Steven Young - - diff --git a/authors.xsl b/authors.xsl deleted file mode 100644 index 77ba4e0f..00000000 --- a/authors.xsl +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - Tinyproxy AUTHORS -================= - -//// -This file is generated from authors.xml, do not edit it directly. -//// - -Coding ------- - -The following people have contributed code to Tinyproxy: - - - - - - - -Documentation -------------- - -The following people have helped to document Tinyproxy: - - - - - - - - - - * - - - - From 4c9289802a8c1f73fed7fc43a70c5f57a9617057 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 03:32:11 +0200 Subject: [PATCH 024/265] scripts: add a script to generate the AUTHORS file from git Signed-off-by: Michael Adam --- scripts/gen-authors.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100755 scripts/gen-authors.sh diff --git a/scripts/gen-authors.sh b/scripts/gen-authors.sh new file mode 100755 index 00000000..671f0c17 --- /dev/null +++ b/scripts/gen-authors.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)" +BASE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +AUTHORS_FILE="${BASE_DIR}/AUTHORS" + +type git > /dev/null || exit +test -d "${BASE_DIR}/.git" || exit + +git log --all --format='%aN' | sort -u > "${AUTHORS_FILE}" From e2a92ea33475280b631c326e8082454e49047086 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 02:38:46 +0200 Subject: [PATCH 025/265] Update AUTHORS Signed-off-by: Michael Adam --- AUTHORS | 82 +++++++++++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9c82e402..d0e6b303 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,43 +1,39 @@ -Tinyproxy AUTHORS -================= - -//// -This file is generated from authors.xml, do not edit it directly. -//// - -Coding ------- - -The following people have contributed code to Tinyproxy: - - * Andrew Stribblehill - * Chris Lightfoot - * Daniel Egger - * David Shanks - * Dmitry Semyonov - * George Talusan - * James E. Flemer - * Jeremy Hinegardner - * John van der Kamp - * Jordi Mallach - * Kim Holviala - * Mathew Mrosko - * Matthew Dempsky - * Michael Adam - * Moritz Muehlenhoff - * Mukund Sivaraman - * Petr Lampa - * Robert James Kaes - * Steven Young - - -Documentation -------------- - -The following people have helped to document Tinyproxy: - - * Marc Silver - * Michael Adam - * Mukund Sivaraman - * Robert James Kaes - * Steven Young +Andrew Stribblehill +bertliao +Bob Showalter +Brian Cain +cvs2svn +Daniel Egger +Daniel M. Drucker +David Shanks +Dmitry Semyonov +dmz-uk +Drew G. Wallace +Frank Morgner +gary-wzl77 +Gaudenz Steinlin +goba62000374 +Gonzalo Tornaria +Greg +Jeremy Hinegardner +John Spencer +John van der Kamp +John Weldon +Jordi +Jordi Mallach +Julien Hartmann +kikuchan +Mathew Mrosko +Matthew Dempsky +Michael Adam +Mike Mead +Mukund Sivaraman +Pablo Panero +Peter H. Froehlich +Robert James Kaes +rofl0r +Stephan Leemburg +Steven Conaway +Steven Young +Valen Blanco +Vladimir Belov From 8333a4bb1a1b0599137a4fc7cf3d8ea590764082 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 04:02:36 +0200 Subject: [PATCH 026/265] docs: update the copyright notice in the manpages Signed-off-by: Michael Adam --- docs/man5/tinyproxy.conf.txt.in | 5 +---- docs/man8/tinyproxy.txt.in | 5 +---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 8f73720a..b3b94ece 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -364,10 +364,7 @@ This manpage was written by the Tinyproxy project team. COPYRIGHT --------- -Copyright (c) 1998-2000 Steven Young; -Copyright (c) 2000-2001 Robert James Kaes; -Copyright (c) 2009-2010 Mukund Sivaraman; -Copyright (c) 2009-2010 Michael Adam. +Copyright (c) 1998-2018 the Tinyproxy authors. This program is distributed under the terms of the GNU General Public License version 2 or above. See the COPYING file for additional diff --git a/docs/man8/tinyproxy.txt.in b/docs/man8/tinyproxy.txt.in index b1c4a9a5..ba6cc15f 100644 --- a/docs/man8/tinyproxy.txt.in +++ b/docs/man8/tinyproxy.txt.in @@ -150,10 +150,7 @@ This manpage was written by the Tinyproxy project team. COPYRIGHT --------- -Copyright (c) 1998-2000 Steven Young; -Copyright (c) 2000-2001 Robert James Kaes; -Copyright (c) 2009-2010 Mukund Sivaraman; -Copyright (c) 2009-2010 Michael Adam. +Copyright (c) 1998-2018 the Tinyproxy authors. This program is distributed under the terms of the GNU General Public License version 2 or above. See the COPYING file for additional From a662c11b8d106e0a2f3379b89739ed4770c5bbd3 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Sat, 1 Sep 2018 02:40:17 +0200 Subject: [PATCH 027/265] Release 1.10.0 Signed-off-by: Michael Adam --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index a47316b8..48ee0b22 100644 --- a/configure.ac +++ b/configure.ac @@ -4,14 +4,14 @@ AC_PREREQ(2.54) m4_define([tinyproxy_major_version], [1]) -m4_define([tinyproxy_minor_version], [9]) +m4_define([tinyproxy_minor_version], [10]) m4_define([tinyproxy_micro_version], [0]) m4_define([tinyproxy_real_version], [tinyproxy_major_version.tinyproxy_minor_version.tinyproxy_micro_version]) m4_define([tinyproxy_version], [tinyproxy_real_version]) # For overriding the version string. Comment out if not needed. -# m4_define([tinyproxy_version], [1.9.0]) +# m4_define([tinyproxy_version], [1.10.0]) AC_INIT([Tinyproxy], [tinyproxy_version], [https://tinyproxy.github.io/], From 9f4ed46c0adf6ce6cb8dd44aff611448b64a37a7 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Tue, 4 Sep 2018 01:23:01 +0200 Subject: [PATCH 028/265] build: add new version mechanism based on VERSION file and a version.sh script If this is a git checkout, and git is available, then git describe is used. Otherwise, the new checked in VERSION file is taken for the version. This mechanism uses a version.sh script inspired by http://git.musl-libc.org/cgit/musl/tree/tools/version.sh Signed-off-by: Michael Adam --- Makefile.am | 6 ++++-- VERSION | 1 + configure.ac | 11 ++--------- scripts/Makefile.am | 2 ++ scripts/version.sh | 15 +++++++++++++++ 5 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 VERSION create mode 100644 scripts/Makefile.am create mode 100755 scripts/version.sh diff --git a/Makefile.am b/Makefile.am index e9113c24..4a3ead6f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,7 +4,8 @@ SUBDIRS = \ etc \ docs \ m4macros \ - tests + tests \ + scripts # tools want this on a single line ACLOCAL_AMFLAGS = -I m4macros @@ -20,7 +21,8 @@ dist_doc_DATA = \ EXTRA_DIST = \ autogen.sh \ tinyproxy-indent.sh \ - TODO + TODO \ + VERSION test: all ./tests/scripts/run_tests.sh diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..81c871de --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.10.0 diff --git a/configure.ac b/configure.ac index 48ee0b22..1e71e355 100644 --- a/configure.ac +++ b/configure.ac @@ -3,15 +3,7 @@ AC_PREREQ(2.54) -m4_define([tinyproxy_major_version], [1]) -m4_define([tinyproxy_minor_version], [10]) -m4_define([tinyproxy_micro_version], [0]) -m4_define([tinyproxy_real_version], - [tinyproxy_major_version.tinyproxy_minor_version.tinyproxy_micro_version]) -m4_define([tinyproxy_version], [tinyproxy_real_version]) - -# For overriding the version string. Comment out if not needed. -# m4_define([tinyproxy_version], [1.10.0]) +m4_define([tinyproxy_version], esyscmd(sh scripts/version.sh | tr -d '\n')) AC_INIT([Tinyproxy], [tinyproxy_version], [https://tinyproxy.github.io/], @@ -227,6 +219,7 @@ docs/man8/tinyproxy.txt m4macros/Makefile tests/Makefile tests/scripts/Makefile +scripts/Makefile ]) AC_OUTPUT diff --git a/scripts/Makefile.am b/scripts/Makefile.am new file mode 100644 index 00000000..4876c5d9 --- /dev/null +++ b/scripts/Makefile.am @@ -0,0 +1,2 @@ +EXTRA_DIST = \ + version.sh diff --git a/scripts/version.sh b/scripts/version.sh new file mode 100755 index 00000000..9a965dc8 --- /dev/null +++ b/scripts/version.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +SCRIPT_DIR="$(cd "$(dirname "${0}")" && pwd)" +GIT_DIR="${SCRIPT_DIR}/../.git" + +if test -d "${GIT_DIR}" ; then + if type git >/dev/null 2>&1 ; then + git describe --match '[0-9]*.[0-9]*.[0-9]*' 2>/dev/null \ + | sed -e 's/-/-git-/' + else + sed 's/$/-git/' < VERSION + fi +else + cat VERSION +fi From 04f68e21e76047ed8a0747016d7f6f03e070fc59 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 1 Sep 2018 03:27:45 +0100 Subject: [PATCH 029/265] tinyproxy.8: remove l flag from short options --- docs/man8/tinyproxy.txt.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man8/tinyproxy.txt.in b/docs/man8/tinyproxy.txt.in index ba6cc15f..54da8673 100644 --- a/docs/man8/tinyproxy.txt.in +++ b/docs/man8/tinyproxy.txt.in @@ -12,7 +12,7 @@ tinyproxy - A light-weight HTTP proxy daemon SYNOPSIS -------- -*tinyproxy* [-vldch] +*tinyproxy* [-vdch] DESCRIPTION From f44d0f387b2e9bfcacf1c5ee15363fbc3a8e0e75 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Tue, 4 Sep 2018 01:32:09 +0200 Subject: [PATCH 030/265] build: Remove now unused TINYPROXY_UNSTABLE variable from configure Signed-off-by: Michael Adam --- configure.ac | 9 --------- 1 file changed, 9 deletions(-) diff --git a/configure.ac b/configure.ac index 1e71e355..6ddbcc0f 100644 --- a/configure.ac +++ b/configure.ac @@ -19,15 +19,6 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) dnl Temporarily defined here until we get tinyproxy-version.h AC_DEFINE(TINYPROXY_VERSION, "tinyproxy_version", [Tinyproxy version number]) -# The symbol TINYPROXY_UNSTABLE is defined above for substitution in -# Makefiles and conditionally defined here as a preprocessor symbol -# and automake conditional. -if test "x$TINYPROXY_UNSTABLE" = "xyes"; then - AC_DEFINE(TINYPROXY_UNSTABLE, 1, - [Define to 1 if this is an unstable version of Tinyproxy]) -fi -AM_CONDITIONAL(TINYPROXY_UNSTABLE, test "x$TINYPROXY_UNSTABLE" = "xyes") - dnl Check if we're compiling on a weird platform :) AC_USE_SYSTEM_EXTENSIONS From dc41b3533326d745c65afaea4cb34c229a31d0c3 Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 23 Nov 2018 17:59:03 +0300 Subject: [PATCH 031/265] Basic Auth: allow almost all possible characters for user/pass previously was restricted to alphanumeric chars only. --- src/conf.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index 5a87c689..5ebf1790 100644 --- a/src/conf.c +++ b/src/conf.c @@ -52,6 +52,8 @@ #define BOOL "(yes|on|no|off)" #define INT "((0x)?[[:digit:]]+)" #define ALNUM "([-a-z0-9._]+)" +#define USERNAME "([^:]*)" +#define PASSWORD "([^@]*)" #define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" #define IPMASK "(" IP "(/[[:digit:]]+)?)" #define IPV6 "(" \ @@ -257,7 +259,7 @@ struct { }, { BEGIN "(upstream)" WS "(http|socks4|socks5)" WS - "(" ALNUM /*username*/ ":" ALNUM /*password*/ "@" ")?" + "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" "(" IP "|" ALNUM ")" ":" INT "(" WS STR ")?" END, handle_upstream, NULL From b131f45cbb4b829d7e520392a2dcfc9b41044351 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 15 Dec 2018 17:09:04 +0000 Subject: [PATCH 032/265] child.c: properly initialize fdset for each select() call (#216) it was reported that because the fdset was only initialized once, tinyproxy would fail to properly listen on more than one interface. closes #214 closes #127 --- src/child.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/child.c b/src/child.c index effb2ae4..60f8eadf 100644 --- a/src/child.c +++ b/src/child.c @@ -206,26 +206,26 @@ static void child_main (struct child_s *ptr) * We have to wait for connections on multiple fds, * so use select. */ + while (!config.quit) { - FD_ZERO(&rfds); + int listenfd = -1; - for (i = 0; i < vector_length(listen_fds); i++) { - int *fd = (int *) vector_getentry(listen_fds, i, NULL); + FD_ZERO(&rfds); - ret = socket_nonblocking(*fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the listening " - "socket %d to non-blocking: %s", - fd, strerror(errno)); - exit(1); - } + for (i = 0; i < vector_length(listen_fds); i++) { + int *fd = (int *) vector_getentry(listen_fds, i, NULL); - FD_SET(*fd, &rfds); - maxfd = max(maxfd, *fd); - } + ret = socket_nonblocking(*fd); + if (ret != 0) { + log_message(LOG_ERR, "Failed to set the listening " + "socket %d to non-blocking: %s", + fd, strerror(errno)); + exit(1); + } - while (!config.quit) { - int listenfd = -1; + FD_SET(*fd, &rfds); + maxfd = max(maxfd, *fd); + } ptr->status = T_WAITING; From e666e4a35b07a406437e4c58a15d81adf7cb5fd7 Mon Sep 17 00:00:00 2001 From: Janosch Hoffmann Date: Sun, 5 May 2019 20:13:38 +0200 Subject: [PATCH 033/265] filter file: Don't ignore lines with leading whitespace (#239) The new code skips leading whitespaces before removing trailing whitespaces and comments. Without doing this, lines with leading whitespace are treated like empty lines (i.e. they are ignored). --- src/filter.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/filter.c b/src/filter.c index 31641916..e18132e2 100644 --- a/src/filter.c +++ b/src/filter.c @@ -52,7 +52,7 @@ void filter_init (void) FILE *fd; struct filter_list *p; char buf[FILTER_BUFFER_LEN]; - char *s; + char *s, *start; int cflags; if (fl || already_init) { @@ -73,11 +73,16 @@ void filter_init (void) cflags |= REG_ICASE; while (fgets (buf, FILTER_BUFFER_LEN, fd)) { + /* skip leading whitespace */ + s = buf; + while (*s && isspace ((unsigned char) *s)) + s++; + start = s; + /* * Remove any trailing white space and * comments. */ - s = buf; while (*s) { if (isspace ((unsigned char) *s)) break; @@ -93,11 +98,7 @@ void filter_init (void) ++s; } *s = '\0'; - - /* skip leading whitespace */ - s = buf; - while (*s && isspace ((unsigned char) *s)) - s++; + s = start; /* skip blank lines and comments */ if (*s == '\0') From 734ba1d9702cd7d420c624c3574bec1470ebf590 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 14 Jun 2019 01:18:17 +0100 Subject: [PATCH 034/265] fix usage of stathost in combination with basic auth http protocol requires different treatment of proxy auth vs server auth. fixes #246 --- src/html-error.c | 8 ++++++-- src/reqs.c | 14 +++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/html-error.c b/src/html-error.c index 625a586d..ee3c9877 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -164,13 +164,17 @@ int send_http_headers (struct conn_s *connptr, int code, const char *message) "%s" "Connection: close\r\n" "\r\n"; - const char auth_str[] = + const char p_auth_str[] = "Proxy-Authenticate: Basic realm=\"" PACKAGE_NAME "\"\r\n"; + const char w_auth_str[] = + "WWW-Authenticate: Basic realm=\"" + PACKAGE_NAME "\"\r\n"; + /* according to rfc7235, the 407 error must be accompanied by a Proxy-Authenticate header field. */ - const char *add = code == 407 ? auth_str : ""; + const char *add = code == 407 ? p_auth_str : (code == 401 ? w_auth_str : ""); return (write_message (connptr->client_fd, headers, code, message, PACKAGE, VERSION, diff --git a/src/reqs.c b/src/reqs.c index bbdcc74d..8450cffc 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1611,11 +1611,22 @@ void handle_connection (int fd) if (config.basicauth_list != NULL) { ssize_t len; char *authstring; - int failure = 1; + int failure = 1, stathost_connect = 0; len = hashmap_entry_by_key (hashofheaders, "proxy-authorization", (void **) &authstring); + if (len == 0 && config.stathost) { + len = hashmap_entry_by_key (hashofheaders, "host", + (void **) &authstring); + if (len && !strncmp(authstring, config.stathost, strlen(config.stathost))) { + len = hashmap_entry_by_key (hashofheaders, "authorization", + (void **) &authstring); + stathost_connect = 1; + } else len = 0; + } + if (len == 0) { + if (stathost_connect) goto e401; update_stats (STAT_DENIED); indicate_http_error (connptr, 407, "Proxy Authentication Required", "detail", @@ -1629,6 +1640,7 @@ void handle_connection (int fd) basicauth_check (config.basicauth_list, authstring + 6) == 1) failure = 0; if(failure) { +e401: update_stats (STAT_DENIED); indicate_http_error (connptr, 401, "Unauthorized", "detail", From c2d3470a35132ffb443fe3ff781fc1f630cd2477 Mon Sep 17 00:00:00 2001 From: Andre Mas Date: Tue, 20 Aug 2019 16:45:42 -0400 Subject: [PATCH 035/265] Fixes #256 Provides ::1 as allowed --- etc/tinyproxy.conf.in | 1 + 1 file changed, 1 insertion(+) diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index 54024cbd..f1b88174 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -222,6 +222,7 @@ MaxRequestsPerChild 0 # tested against the controls based on order. # Allow 127.0.0.1 +Allow ::1 # BasicAuth: HTTP "Basic Authentication" for accessing the proxy. # If there are any entries specified, access is only granted for authenticated From 69c86b987bca45ab8e62ab6b255bb4a8991de034 Mon Sep 17 00:00:00 2001 From: Martin Kutschker Date: Mon, 18 Nov 2019 21:19:06 +0100 Subject: [PATCH 036/265] Use gai_strerror() to report errors of getaddrinfo() and getnameinfo() --- src/sock.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/sock.c b/src/sock.c index eef606a6..59c2fa8d 100644 --- a/src/sock.c +++ b/src/sock.c @@ -34,6 +34,17 @@ #include "text.h" #include "conf.h" +/* + * Return a human readable error for getaddrinfo() and getnameinfo(). + */ +static const char * get_gai_error (int n) +{ + if (n == EAI_SYSTEM) + return strerror (errno); + else + return gai_strerror (n); +} + /* * Bind the given socket to the supplied address. The socket is * returned if the bind succeeded. Otherwise, -1 is returned @@ -43,6 +54,7 @@ static int bind_socket (int sockfd, const char *addr, int family) { struct addrinfo hints, *res, *ressave; + int n; assert (sockfd >= 0); assert (addr != NULL && strlen (addr) != 0); @@ -51,9 +63,13 @@ bind_socket (int sockfd, const char *addr, int family) hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; - /* The local port it not important */ - if (getaddrinfo (addr, NULL, &hints, &res) != 0) + /* The local port is not important */ + n = getaddrinfo (addr, NULL, &hints, &res); + if (n != 0) { + log_message (LOG_INFO, + "bind_socket: getaddrinfo failed for %s: ", addr, get_gai_error (n)); return -1; + } ressave = res; @@ -96,7 +112,7 @@ int opensock (const char *host, int port, const char *bind_to) n = getaddrinfo (host, portstr, &hints, &res); if (n != 0) { log_message (LOG_ERR, - "opensock: Could not retrieve info for %s", host); + "opensock: Could not retrieve address info for %s:%d: %s", host, port, get_gai_error (n)); return -1; } @@ -134,8 +150,9 @@ int opensock (const char *host, int port, const char *bind_to) freeaddrinfo (ressave); if (res == NULL) { log_message (LOG_ERR, - "opensock: Could not establish a connection to %s", - host); + "opensock: Could not establish a connection to %s:%d", + host, + port); return -1; } @@ -186,8 +203,7 @@ static int listen_on_one_socket(struct addrinfo *ad) ret = getnameinfo(ad->ai_addr, ad->ai_addrlen, numerichost, NI_MAXHOST, NULL, 0, flags); if (ret != 0) { - log_message(LOG_ERR, "error calling getnameinfo: %s", - gai_strerror(errno)); + log_message(LOG_ERR, "getnameinfo failed: %s", get_gai_error (ret)); return -1; } @@ -256,6 +272,7 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) struct addrinfo hints, *result, *rp; char portstr[6]; int ret = -1; + int n; assert (port > 0); assert (listen_fds != NULL); @@ -270,10 +287,13 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) snprintf (portstr, sizeof (portstr), "%d", port); - if (getaddrinfo (addr, portstr, &hints, &result) != 0) { + n = getaddrinfo (addr, portstr, &hints, &result); + if (n != 0) { log_message (LOG_ERR, - "Unable to getaddrinfo() because of %s", - strerror (errno)); + "Unable to getaddrinfo() for %s:%d because of %s", + addr, + port, + get_gai_error (n)); return -1; } From 3a7aa1583488d11b264014de5edfe097209de59e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 21 Dec 2019 00:38:56 +0000 Subject: [PATCH 037/265] start work on 1.11.x --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 81c871de..1cac385c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.10.0 +1.11.0 From b935dc85c3fca51de8e131d6aa2047f8a0404f0c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 17 Dec 2018 00:23:09 +0000 Subject: [PATCH 038/265] simplify codebase by using one thread/conn, instead of preforked procs the existing codebase used an elaborate and complex approach for its parallelism: 5 different config file options, namely - MaxClients - MinSpareServers - MaxSpareServers - StartServers - MaxRequestsPerChild were used to steer how (and how many) parallel processes tinyproxy would spin up at start, how many processes at each point needed to be idle, etc. it seems all preforked processes would listen on the server port and compete with each other about who would get assigned the new incoming connections. since some data needs to be shared across those processes, a half- baked "shared memory" implementation was provided for this purpose. that implementation used to use files in the filesystem, and since it had a big FIXME comment, the author was well aware of how hackish that approach was. this entire complexity is now removed. the main thread enters a loop which polls on the listening fds, then spins up a new thread per connection, until the maximum number of connections (MaxClients) is hit. this is the only of the 5 config options left after this cleanup. since threads share the same address space, the code necessary for shared memory access has been removed. this means that the other 4 mentioned config option will now produce a parse error, when encountered. currently each thread uses a hardcoded default of 256KB per thread for the thread stack size, which is quite lavish and should be sufficient for even the worst C libraries, but people may want to tweak this value to the bare minimum, thus we may provide a new config option for this purpose in the future. i suspect that on heavily optimized C libraries such a musl, a stack size of 8-16 KB per thread could be sufficient. since the existing list implementation in vector.c did not provide a way to remove a single item from an existing list, i added my own list implementation from my libulz library which offers this functionality, rather than trying to add an ad-hoc, and perhaps buggy implementation to the vector_t list code. the sblist code is contained in an 80 line C file and as simple as it can get, while offering good performance and is proven bugfree due to years of use in other projects. --- docs/man5/tinyproxy.conf.txt.in | 28 +- etc/tinyproxy.conf.in | 24 -- src/Makefile.am | 3 +- src/child.c | 503 ++++++++------------------------ src/conf.c | 38 +-- src/conf.h | 1 + src/heap.c | 58 ---- src/heap.h | 6 - src/main.c | 11 +- src/sblist.c | 80 +++++ src/sblist.h | 92 ++++++ src/stats.c | 27 +- tests/scripts/run_tests.sh | 4 - 13 files changed, 316 insertions(+), 559 deletions(-) create mode 100644 src/sblist.c create mode 100644 src/sblist.h diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index b3b94ece..afd3b6b2 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -176,37 +176,11 @@ The possible keywords and their descriptions are as follows: *MaxClients*:: - Tinyproxy creates one child process for each connected client. + Tinyproxy creates one thread for each connected client. This options specifies the absolute highest number processes that will be created. With other words, only MaxClients clients can be connected to Tinyproxy simultaneously. -*MinSpareServers*:: -*MaxSpareServers*:: - - Tinyproxy always keeps a certain number of idle child processes - so that it can handle new incoming client requests quickly. - `MinSpareServer` and `MaxSpareServers` control the lower and upper - limits for the number of spare processes. I.e. when the number of - spare servers drops below `MinSpareServers` then Tinyproxy will - start forking new spare processes in the background and when the - number of spare processes exceeds `MaxSpareServers` then Tinyproxy - will kill off extra processes. - -*StartServers*:: - - The number of servers to start initially. This should usually be - set to a value between MinSpareServers and MaxSpareServers. - -*MaxRequestsPerChild*:: - - This limits the number of connections that a child process - will handle before it is killed. The default value is `0` - which disables this feature. This option is meant as an - emergency measure in the case of problems with memory leakage. - In that case, setting `MaxRequestsPerChild` to a value of e.g. - 1000, or 10000 can be useful. - *Allow*:: *Deny*:: diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index f1b88174..06f5480a 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -189,30 +189,6 @@ LogLevel Info # MaxClients 100 -# -# MinSpareServers/MaxSpareServers: These settings set the upper and -# lower limit for the number of spare servers which should be available. -# -# If the number of spare servers falls below MinSpareServers then new -# server processes will be spawned. If the number of servers exceeds -# MaxSpareServers then the extras will be killed off. -# -MinSpareServers 5 -MaxSpareServers 20 - -# -# StartServers: The number of servers to start initially. -# -StartServers 10 - -# -# MaxRequestsPerChild: The number of connections a thread will handle -# before it is killed. In practise this should be set to 0, which -# disables thread reaping. If you do notice problems with memory -# leakage, then set this to something like 10000. -# -MaxRequestsPerChild 0 - # # Allow: Customization of authorization controls. If there are any # access control keywords then the default action is to DENY. Otherwise, diff --git a/src/Makefile.am b/src/Makefile.am index af2f621e..39249097 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,10 +48,11 @@ tinyproxy_SOURCES = \ upstream.c upstream.h \ basicauth.c basicauth.h \ base64.c base64.h \ + sblist.c sblist.h \ connect-ports.c connect-ports.h EXTRA_tinyproxy_SOURCES = filter.c filter.h \ reverse-proxy.c reverse-proxy.h \ transparent-proxy.c transparent-proxy.h tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@ -tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ +tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread diff --git a/src/child.c b/src/child.c index 60f8eadf..06a977eb 100644 --- a/src/child.c +++ b/src/child.c @@ -31,176 +31,66 @@ #include "sock.h" #include "utils.h" #include "conf.h" +#include "sblist.h" +#include static vector_t listen_fds; -/* - * Stores the internal data needed for each child (connection) - */ -enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED }; -struct child_s { - pid_t tid; - unsigned int connects; - enum child_status_t status; +union sockaddr_union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; }; -/* - * A pointer to an array of children. A certain number of children are - * created when the program is started. - */ -static struct child_s *child_ptr; - -static struct child_config_s { - unsigned int maxclients, maxrequestsperchild; - unsigned int maxspareservers, minspareservers, startservers; -} child_config; - -static unsigned int *servers_waiting; /* servers waiting for a connection */ - -/* - * Lock/Unlock the "servers_waiting" variable so that two children cannot - * modify it at the same time. - */ -#define SERVER_COUNT_LOCK() _child_lock_wait() -#define SERVER_COUNT_UNLOCK() _child_lock_release() - -/* START OF LOCKING SECTION */ +struct client { + union sockaddr_union addr; + int fd; +}; -/* - * These variables are required for the locking mechanism. Also included - * are the "private" functions for locking/unlocking. - */ -static struct flock lock_it, unlock_it; -static int lock_fd = -1; +struct child { + pthread_t thread; + struct client client; + volatile int done; +}; -static void _child_lock_init (void) +static void* child_thread(void* data) { - char lock_file[] = "/tmp/tinyproxy.servers.lock.XXXXXX"; - - /* Only allow u+rw bits. This may be required for some versions - * of glibc so that mkstemp() doesn't make us vulnerable. - */ - umask (0177); - - lock_fd = mkstemp (lock_file); - unlink (lock_file); - - lock_it.l_type = F_WRLCK; - lock_it.l_whence = SEEK_SET; - lock_it.l_start = 0; - lock_it.l_len = 0; - - unlock_it.l_type = F_UNLCK; - unlock_it.l_whence = SEEK_SET; - unlock_it.l_start = 0; - unlock_it.l_len = 0; + struct child *c = data; + handle_connection (c->client.fd); + c->done = 1; + return NULL; } -static void _child_lock_wait (void) -{ - int rc; +static sblist *childs; - while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) { - if (errno == EINTR) - continue; - else - return; - } -} - -static void _child_lock_release (void) +static void collect_threads(void) { - if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0) - return; + size_t i; + for (i = 0; i < sblist_getsize(childs); ) { + struct child *c = *((struct child**)sblist_get(childs, i)); + if (c->done) { + pthread_join(c->thread, 0); + sblist_delete(childs, i); + safefree(c); + } else i++; + } } -/* END OF LOCKING SECTION */ - -#define SERVER_INC() do { \ - SERVER_COUNT_LOCK(); \ - ++(*servers_waiting); \ - DEBUG2("INC: servers_waiting: %d", *servers_waiting); \ - SERVER_COUNT_UNLOCK(); \ -} while (0) - -#define SERVER_DEC() do { \ - SERVER_COUNT_LOCK(); \ - assert(*servers_waiting > 0); \ - --(*servers_waiting); \ - DEBUG2("DEC: servers_waiting: %d", *servers_waiting); \ - SERVER_COUNT_UNLOCK(); \ -} while (0) - /* - * Set the configuration values for the various child related settings. + * This is the main loop accepting new connections. */ -short int child_configure (child_config_t type, unsigned int val) -{ - switch (type) { - case CHILD_MAXCLIENTS: - child_config.maxclients = val; - break; - case CHILD_MAXSPARESERVERS: - child_config.maxspareservers = val; - break; - case CHILD_MINSPARESERVERS: - child_config.minspareservers = val; - break; - case CHILD_STARTSERVERS: - child_config.startservers = val; - break; - case CHILD_MAXREQUESTSPERCHILD: - child_config.maxrequestsperchild = val; - break; - default: - DEBUG2 ("Invalid type (%d)", type); - return -1; - } - - return 0; -} - -/** - * child signal handler for sighup - */ -static void child_sighup_handler (int sig) -{ - if (sig == SIGHUP) { - /* - * Ignore the return value of reload_config for now. - * This should actually be handled somehow... - */ - reload_config (); - -#ifdef FILTER_ENABLE - filter_reload (); -#endif /* FILTER_ENABLE */ - } -} - -/* - * This is the main (per child) loop. - */ -static void child_main (struct child_s *ptr) +void child_main_loop (void) { int connfd; - struct sockaddr *cliaddr; - socklen_t clilen; + union sockaddr_union cliaddr_storage; + struct sockaddr *cliaddr = (void*) &cliaddr_storage; + socklen_t clilen = sizeof(cliaddr_storage); fd_set rfds; - int maxfd = 0; ssize_t i; - int ret; + int ret, listenfd, maxfd, was_full = 0; + pthread_attr_t *attrp, attr; + struct child *child; - cliaddr = (struct sockaddr *) - safemalloc (sizeof(struct sockaddr_storage)); - if (!cliaddr) { - log_message (LOG_CRIT, - "Could not allocate memory for child address."); - exit (0); - } - - ptr->connects = 0; - srand(time(NULL)); + childs = sblist_new(sizeof (struct child*), config.maxclients); /* * We have to wait for connections on multiple fds, @@ -208,7 +98,37 @@ static void child_main (struct child_s *ptr) */ while (!config.quit) { - int listenfd = -1; + collect_threads(); + + if (sblist_getsize(childs) >= config.maxclients) { + if (!was_full) + log_message (LOG_NOTICE, + "Maximum number of connections reached. " + "Refusing new connections."); + was_full = 1; + usleep(16); + continue; + } + + was_full = 0; + listenfd = -1; + maxfd = 0; + + /* Handle log rotation if it was requested */ + if (received_sighup) { + /* + * Ignore the return value of reload_config for now. + * This should actually be handled somehow... + */ + reload_config (); + +#ifdef FILTER_ENABLE + filter_reload (); +#endif /* FILTER_ENABLE */ + + received_sighup = FALSE; + } + FD_ZERO(&rfds); @@ -220,17 +140,13 @@ static void child_main (struct child_s *ptr) log_message(LOG_ERR, "Failed to set the listening " "socket %d to non-blocking: %s", fd, strerror(errno)); - exit(1); + continue; } FD_SET(*fd, &rfds); maxfd = max(maxfd, *fd); } - ptr->status = T_WAITING; - - clilen = sizeof(struct sockaddr_storage); - ret = select(maxfd + 1, &rfds, NULL, NULL, NULL); if (ret == -1) { if (errno == EINTR) { @@ -238,7 +154,7 @@ static void child_main (struct child_s *ptr) } log_message (LOG_ERR, "error calling select: %s", strerror(errno)); - exit(1); + continue; } else if (ret == 0) { log_message (LOG_WARNING, "Strange: select returned 0 " "but we did not specify a timeout..."); @@ -269,7 +185,7 @@ static void child_main (struct child_s *ptr) log_message(LOG_ERR, "Failed to set listening " "socket %d to blocking for accept: %s", listenfd, strerror(errno)); - exit(1); + continue; } /* @@ -279,21 +195,6 @@ static void child_main (struct child_s *ptr) connfd = accept (listenfd, cliaddr, &clilen); -#ifndef NDEBUG - /* - * Enable the TINYPROXY_DEBUG environment variable if you - * want to use the GDB debugger. - */ - if (getenv ("TINYPROXY_DEBUG")) { - /* Pause for 10 seconds to allow us to connect debugger */ - fprintf (stderr, - "Process has accepted connection: %ld\n", - (long int) ptr->tid); - sleep (10); - fprintf (stderr, "Continuing process: %ld\n", - (long int) ptr->tid); - } -#endif /* * Make sure no error occurred... @@ -305,224 +206,37 @@ static void child_main (struct child_s *ptr) continue; } - ptr->status = T_CONNECTED; - - SERVER_DEC (); - - handle_connection (connfd); - ptr->connects++; - - if (child_config.maxrequestsperchild != 0) { - DEBUG2 ("%u connections so far...", ptr->connects); - - if (ptr->connects == child_config.maxrequestsperchild) { - log_message (LOG_NOTICE, - "Child has reached MaxRequestsPerChild (%u). " - "Killing child.", ptr->connects); - break; - } - } - - SERVER_COUNT_LOCK (); - if (*servers_waiting > child_config.maxspareservers) { - /* - * There are too many spare children, kill ourself - * off. - */ - log_message (LOG_NOTICE, - "Waiting servers (%d) exceeds MaxSpareServers (%d). " - "Killing child.", - *servers_waiting, - child_config.maxspareservers); - SERVER_COUNT_UNLOCK (); - - break; - } else { - SERVER_COUNT_UNLOCK (); + child = safemalloc(sizeof(struct child)); + if (!child) { +oom: + close(connfd); + log_message (LOG_CRIT, + "Could not allocate memory for child."); + usleep(16); /* prevent 100% CPU usage in OOM situation */ + continue; } - SERVER_INC (); - } - - ptr->status = T_EMPTY; - - safefree (cliaddr); - exit (0); -} - -/* - * Fork a child "child" (or in our case a process) and then start up the - * child_main() function. - */ -static pid_t child_make (struct child_s *ptr) -{ - pid_t pid; - - if ((pid = fork ()) > 0) - return pid; /* parent */ - - /* - * Reset the SIGNALS so that the child can be reaped. - */ - set_signal_handler (SIGCHLD, SIG_DFL); - set_signal_handler (SIGTERM, SIG_DFL); - set_signal_handler (SIGHUP, child_sighup_handler); - - child_main (ptr); /* never returns */ - return -1; -} - -/* - * Create a pool of children to handle incoming connections - */ -short int child_pool_create (void) -{ - unsigned int i; - - /* - * Make sure the number of MaxClients is not zero, since this - * variable determines the size of the array created for children - * later on. - */ - if (child_config.maxclients == 0) { - log_message (LOG_ERR, - "child_pool_create: \"MaxClients\" must be " - "greater than zero."); - return -1; - } - if (child_config.startservers == 0) { - log_message (LOG_ERR, - "child_pool_create: \"StartServers\" must be " - "greater than zero."); - return -1; - } - - child_ptr = - (struct child_s *) calloc_shared_memory (child_config.maxclients, - sizeof (struct child_s)); - if (!child_ptr) { - log_message (LOG_ERR, - "Could not allocate memory for children."); - return -1; - } + child->done = 0; - servers_waiting = - (unsigned int *) malloc_shared_memory (sizeof (unsigned int)); - if (servers_waiting == MAP_FAILED) { - log_message (LOG_ERR, - "Could not allocate memory for child counting."); - return -1; - } - *servers_waiting = 0; - - /* - * Create a "locking" file for use around the servers_waiting - * variable. - */ - _child_lock_init (); - - if (child_config.startservers > child_config.maxclients) { - log_message (LOG_WARNING, - "Can not start more than \"MaxClients\" servers. " - "Starting %u servers instead.", - child_config.maxclients); - child_config.startservers = child_config.maxclients; - } - - for (i = 0; i != child_config.maxclients; i++) { - child_ptr[i].status = T_EMPTY; - child_ptr[i].connects = 0; - } - - for (i = 0; i != child_config.startservers; i++) { - DEBUG2 ("Trying to create child %d of %d", i + 1, - child_config.startservers); - child_ptr[i].status = T_WAITING; - child_ptr[i].tid = child_make (&child_ptr[i]); - - if (child_ptr[i].tid < 0) { - log_message (LOG_WARNING, - "Could not create child number %d of %d", - i, child_config.startservers); - return -1; - } else { - log_message (LOG_INFO, - "Creating child number %d of %d ...", - i + 1, child_config.startservers); - - SERVER_INC (); + if (!sblist_add(childs, &child)) { + free(child); + goto oom; } - } - - log_message (LOG_INFO, "Finished creating all children."); - return 0; -} + child->client.fd = connfd; + memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage)); -/* - * Keep the proper number of servers running. This is the birth of the - * servers. It monitors this at least once a second. - */ -void child_main_loop (void) -{ - unsigned int i; - - while (1) { - if (config.quit) - return; - - /* If there are not enough spare servers, create more */ - SERVER_COUNT_LOCK (); - if (*servers_waiting < child_config.minspareservers) { - log_message (LOG_NOTICE, - "Waiting servers (%d) is less than MinSpareServers (%d). " - "Creating new child.", - *servers_waiting, - child_config.minspareservers); - - SERVER_COUNT_UNLOCK (); - - for (i = 0; i != child_config.maxclients; i++) { - if (child_ptr[i].status == T_EMPTY) { - child_ptr[i].status = T_WAITING; - child_ptr[i].tid = - child_make (&child_ptr[i]); - if (child_ptr[i].tid < 0) { - log_message (LOG_NOTICE, - "Could not create child"); - - child_ptr[i].status = T_EMPTY; - break; - } - - SERVER_INC (); - - break; - } - } - } else { - SERVER_COUNT_UNLOCK (); + attrp = 0; + if (pthread_attr_init(&attr) == 0) { + attrp = &attr; + pthread_attr_setstacksize(attrp, 256*1024); } - sleep (5); - - /* Handle log rotation if it was requested */ - if (received_sighup) { - /* - * Ignore the return value of reload_config for now. - * This should actually be handled somehow... - */ - reload_config (); - -#ifdef FILTER_ENABLE - filter_reload (); -#endif /* FILTER_ENABLE */ - - /* propagate filter reload to all children */ - child_kill_children (SIGHUP); - - received_sighup = FALSE; - } + if (pthread_create(&child->thread, attrp, child_thread, child) != 0) { + sblist_delete(childs, sblist_getsize(childs) -1); + free(child); + goto oom; + } } } @@ -531,12 +245,25 @@ void child_main_loop (void) */ void child_kill_children (int sig) { - unsigned int i; - - for (i = 0; i != child_config.maxclients; i++) { - if (child_ptr[i].status != T_EMPTY) - kill (child_ptr[i].tid, sig); - } + size_t i; + + if (sig != SIGTERM) return; + + for (i = 0; i < sblist_getsize(childs); i++) { + struct child *c = *((struct child**)sblist_get(childs, i)); + if (!c->done) { + /* interrupt blocking operations. + this should cause the threads to shutdown orderly. */ + close(c->client.fd); + } + } + usleep(16); + collect_threads(); + if (sblist_getsize(childs) != 0) + log_message (LOG_CRIT, + "child_kill_children: %zu threads still alive!", + sblist_getsize(childs) + ); } diff --git a/src/conf.c b/src/conf.c index 5ebf1790..d7cf5b60 100644 --- a/src/conf.c +++ b/src/conf.c @@ -27,7 +27,6 @@ #include "acl.h" #include "anonymous.h" -#include "child.h" #include "filter.h" #include "heap.h" #include "html-error.h" @@ -140,9 +139,6 @@ static HANDLE_FUNC (handle_listen); static HANDLE_FUNC (handle_logfile); static HANDLE_FUNC (handle_loglevel); static HANDLE_FUNC (handle_maxclients); -static HANDLE_FUNC (handle_maxrequestsperchild); -static HANDLE_FUNC (handle_maxspareservers); -static HANDLE_FUNC (handle_minspareservers); static HANDLE_FUNC (handle_pidfile); static HANDLE_FUNC (handle_port); #ifdef REVERSE_SUPPORT @@ -151,7 +147,6 @@ static HANDLE_FUNC (handle_reversemagic); static HANDLE_FUNC (handle_reverseonly); static HANDLE_FUNC (handle_reversepath); #endif -static HANDLE_FUNC (handle_startservers); static HANDLE_FUNC (handle_statfile); static HANDLE_FUNC (handle_stathost); static HANDLE_FUNC (handle_syslog); @@ -217,10 +212,6 @@ struct { /* integer arguments */ STDCONF ("port", INT, handle_port), STDCONF ("maxclients", INT, handle_maxclients), - STDCONF ("maxspareservers", INT, handle_maxspareservers), - STDCONF ("minspareservers", INT, handle_minspareservers), - STDCONF ("startservers", INT, handle_startservers), - STDCONF ("maxrequestsperchild", INT, handle_maxrequestsperchild), STDCONF ("timeout", INT, handle_timeout), STDCONF ("connectport", INT, handle_connectport), /* alphanumeric arguments */ @@ -805,34 +796,7 @@ static HANDLE_FUNC (handle_port) static HANDLE_FUNC (handle_maxclients) { - child_configure (CHILD_MAXCLIENTS, get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_maxspareservers) -{ - child_configure (CHILD_MAXSPARESERVERS, - get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_minspareservers) -{ - child_configure (CHILD_MINSPARESERVERS, - get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_startservers) -{ - child_configure (CHILD_STARTSERVERS, get_long_arg (line, &match[2])); - return 0; -} - -static HANDLE_FUNC (handle_maxrequestsperchild) -{ - child_configure (CHILD_MAXREQUESTSPERCHILD, - get_long_arg (line, &match[2])); + set_int_arg (&conf->maxclients, line, &match[2]); return 0; } diff --git a/src/conf.h b/src/conf.h index beb2b01b..44f12d79 100644 --- a/src/conf.h +++ b/src/conf.h @@ -45,6 +45,7 @@ struct config_s { char *stathost; unsigned int godaemon; /* boolean */ unsigned int quit; /* boolean */ + unsigned int maxclients; char *user; char *group; vector_t listen_addrs; diff --git a/src/heap.c b/src/heap.c index c7d85600..0611c396 100644 --- a/src/heap.c +++ b/src/heap.c @@ -97,61 +97,3 @@ char *debugging_strdup (const char *s, const char *file, unsigned long line) #endif /* !NDEBUG */ -/* - * Allocate a block of memory in the "shared" memory region. - * - * FIXME: This uses the most basic (and slowest) means of creating a - * shared memory location. It requires the use of a temporary file. We might - * want to look into something like MM (Shared Memory Library) for a better - * solution. - */ -void *malloc_shared_memory (size_t size) -{ - int fd; - void *ptr; - char buffer[32]; - - static const char *shared_file = "/tmp/tinyproxy.shared.XXXXXX"; - - assert (size > 0); - - strlcpy (buffer, shared_file, sizeof (buffer)); - - /* Only allow u+rw bits. This may be required for some versions - * of glibc so that mkstemp() doesn't make us vulnerable. - */ - umask (0177); - - if ((fd = mkstemp (buffer)) == -1) - return MAP_FAILED; - unlink (buffer); - - if (ftruncate (fd, size) == -1) - return MAP_FAILED; - ptr = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); - - return ptr; -} - -/* - * Allocate a block of memory from the "shared" region an initialize it to - * zero. - */ -void *calloc_shared_memory (size_t nmemb, size_t size) -{ - void *ptr; - long length; - - assert (nmemb > 0); - assert (size > 0); - - length = nmemb * size; - - ptr = malloc_shared_memory (length); - if (ptr == MAP_FAILED) - return ptr; - - memset (ptr, 0, length); - - return ptr; -} diff --git a/src/heap.h b/src/heap.h index f3cf6715..da644616 100644 --- a/src/heap.h +++ b/src/heap.h @@ -52,10 +52,4 @@ extern char *debugging_strdup (const char *s, const char *file, #endif -/* - * Allocate memory from the "shared" region of memory. - */ -extern void *malloc_shared_memory (size_t size); -extern void *calloc_shared_memory (size_t nmemb, size_t size); - #endif diff --git a/src/main.c b/src/main.c index 43170c56..06465b14 100644 --- a/src/main.c +++ b/src/main.c @@ -65,6 +65,7 @@ takesig (int sig) received_sighup = TRUE; break; + case SIGINT: case SIGTERM: config.quit = TRUE; break; @@ -302,6 +303,7 @@ static void initialize_config_defaults (struct config_s *conf) conf->idletimeout = MAX_IDLE_TIME; conf->logf_name = NULL; conf->pidpath = NULL; + conf->maxclients = 100; } /** @@ -329,6 +331,8 @@ int reload_config (void) int main (int argc, char **argv) { + srand(time(NULL)); /* for hashmap seeds */ + /* Only allow u+rw bits. This may be required for some versions * of glibc so that mkstemp() doesn't make us vulnerable. */ @@ -407,13 +411,6 @@ main (int argc, char **argv) exit (EX_SOFTWARE); } - if (child_pool_create () < 0) { - fprintf (stderr, - "%s: Could not create the pool of children.\n", - argv[0]); - exit (EX_SOFTWARE); - } - /* These signals are only for the parent process. */ log_message (LOG_INFO, "Setting the various signals."); diff --git a/src/sblist.c b/src/sblist.c new file mode 100644 index 00000000..4ddc4aa5 --- /dev/null +++ b/src/sblist.c @@ -0,0 +1,80 @@ +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#include "sblist.h" +#include +#include +#include +#define MY_PAGE_SIZE 4096 + +sblist* sblist_new(size_t itemsize, size_t blockitems) { + sblist* ret = (sblist*) malloc(sizeof(sblist)); + sblist_init(ret, itemsize, blockitems); + return ret; +} + +static void sblist_clear(sblist* l) { + l->items = NULL; + l->capa = 0; + l->count = 0; +} + +void sblist_init(sblist* l, size_t itemsize, size_t blockitems) { + if(l) { + l->blockitems = blockitems ? blockitems : MY_PAGE_SIZE / itemsize; + l->itemsize = itemsize; + sblist_clear(l); + } +} + +void sblist_free_items(sblist* l) { + if(l) { + if(l->items) free(l->items); + sblist_clear(l); + } +} + +void sblist_free(sblist* l) { + if(l) { + sblist_free_items(l); + free(l); + } +} + +char* sblist_item_from_index(sblist* l, size_t idx) { + return l->items + (idx * l->itemsize); +} + +void* sblist_get(sblist* l, size_t item) { + if(item < l->count) return (void*) sblist_item_from_index(l, item); + return NULL; +} + +int sblist_set(sblist* l, void* item, size_t pos) { + if(pos >= l->count) return 0; + memcpy(sblist_item_from_index(l, pos), item, l->itemsize); + return 1; +} + +int sblist_grow_if_needed(sblist* l) { + char* temp; + if(l->count == l->capa) { + temp = realloc(l->items, (l->capa + l->blockitems) * l->itemsize); + if(!temp) return 0; + l->capa += l->blockitems; + l->items = temp; + } + return 1; +} + +int sblist_add(sblist* l, void* item) { + if(!sblist_grow_if_needed(l)) return 0; + l->count++; + return sblist_set(l, item, l->count - 1); +} + +void sblist_delete(sblist* l, size_t item) { + if (l->count && item < l->count) { + memmove(sblist_item_from_index(l, item), sblist_item_from_index(l, item + 1), (sblist_getsize(l) - (item + 1)) * l->itemsize); + l->count--; + } +} diff --git a/src/sblist.h b/src/sblist.h new file mode 100644 index 00000000..02c33d79 --- /dev/null +++ b/src/sblist.h @@ -0,0 +1,92 @@ +#ifndef SBLIST_H +#define SBLIST_H + +/* this file is part of libulz, as of commit 8ab361a27743aaf025323ee43b8b8876dc054fdd + modified for direct inclusion in tinyproxy, and for this purpose released under + the license of tinyproxy. */ + + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/* + * simple buffer list. + * + * this thing here is basically a generic dynamic array + * will realloc after every blockitems inserts + * can store items of any size. + * + * so think of it as a by-value list, as opposed to a typical by-ref list. + * you typically use it by having some struct on the stack, and pass a pointer + * to sblist_add, which will copy the contents into its internal memory. + * + */ + +typedef struct { + size_t itemsize; + size_t blockitems; + size_t count; + size_t capa; + char* items; +} sblist; + +#define sblist_getsize(X) ((X)->count) +#define sblist_get_count(X) ((X)->count) +#define sblist_empty(X) ((X)->count == 0) + +/* for dynamic style */ +sblist* sblist_new(size_t itemsize, size_t blockitems); +void sblist_free(sblist* l); + +/*for static style*/ +void sblist_init(sblist* l, size_t itemsize, size_t blockitems); +void sblist_free_items(sblist* l); + +/* accessors */ +void* sblist_get(sblist* l, size_t item); +/* returns 1 on success, 0 on OOM */ +int sblist_add(sblist* l, void* item); +int sblist_set(sblist* l, void* item, size_t pos); +void sblist_delete(sblist* l, size_t item); +char* sblist_item_from_index(sblist* l, size_t idx); +int sblist_grow_if_needed(sblist* l); +int sblist_insert(sblist* l, void* item, size_t pos); +/* same as sblist_add, but returns list index of new item, or -1 */ +size_t sblist_addi(sblist* l, void* item); +void sblist_sort(sblist *l, int (*compar)(const void *, const void *)); +/* insert element into presorted list, returns listindex of new entry or -1*/ +size_t sblist_insert_sorted(sblist* l, void* o, int (*compar)(const void *, const void *)); + +#ifndef __COUNTER__ +#define __COUNTER__ __LINE__ +#endif + +#define __sblist_concat_impl( x, y ) x##y +#define __sblist_macro_concat( x, y ) __sblist_concat_impl( x, y ) +#define __sblist_iterator_name __sblist_macro_concat(sblist_iterator, __COUNTER__) + +/* use with custom iterator variable */ +#define sblist_iter_counter(LIST, ITER, PTR) \ + for(size_t ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++) + +/* use with custom iterator variable, which is predeclared */ +#define sblist_iter_counter2(LIST, ITER, PTR) \ + for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < sblist_getsize(LIST); ITER++) + +/* use with custom iterator variable, which is predeclared and signed */ +/* useful for a loop which can delete items from the list, and then decrease the iterator var. */ +#define sblist_iter_counter2s(LIST, ITER, PTR) \ + for(ITER = 0; (PTR = sblist_get(LIST, ITER)), ITER < (ssize_t) sblist_getsize(LIST); ITER++) + + +/* uses "magic" iterator variable */ +#define sblist_iter(LIST, PTR) sblist_iter_counter(LIST, __sblist_iterator_name, PTR) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/stats.c b/src/stats.c index c7b44232..93a30cfb 100644 --- a/src/stats.c +++ b/src/stats.c @@ -33,6 +33,7 @@ #include "stats.h" #include "utils.h" #include "conf.h" +#include struct stat_s { unsigned long int num_reqs; @@ -43,14 +44,16 @@ struct stat_s { }; static struct stat_s *stats; +static pthread_mutex_t stats_update_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER; /* * Initialize the statistics information to zero. */ void init_stats (void) { - stats = (struct stat_s *) malloc_shared_memory (sizeof (struct stat_s)); - if (stats == MAP_FAILED) + stats = (struct stat_s *) safemalloc (sizeof (struct stat_s)); + if (!stats) return; memset (stats, 0, sizeof (struct stat_s)); @@ -72,10 +75,15 @@ showstats (struct conn_s *connptr) snprintf (denied, sizeof (denied), "%lu", stats->num_denied); snprintf (refused, sizeof (refused), "%lu", stats->num_refused); + pthread_mutex_lock(&stats_file_lock); + if (!config.statpage || (!(statfile = fopen (config.statpage, "r")))) { message_buffer = (char *) safemalloc (MAXBUFFSIZE); - if (!message_buffer) + if (!message_buffer) { +err_minus_one: + pthread_mutex_unlock(&stats_file_lock); return -1; + } snprintf (message_buffer, MAXBUFFSIZE, @@ -105,13 +113,13 @@ showstats (struct conn_s *connptr) if (send_http_message (connptr, 200, "OK", message_buffer) < 0) { safefree (message_buffer); - return -1; + goto err_minus_one; } safefree (message_buffer); + pthread_mutex_unlock(&stats_file_lock); return 0; } - add_error_variable (connptr, "opens", opens); add_error_variable (connptr, "reqs", reqs); add_error_variable (connptr, "badconns", badconns); @@ -121,6 +129,7 @@ showstats (struct conn_s *connptr) send_http_headers (connptr, 200, "Statistic requested"); send_html_file (statfile, connptr); fclose (statfile); + pthread_mutex_unlock(&stats_file_lock); return 0; } @@ -131,6 +140,9 @@ showstats (struct conn_s *connptr) */ int update_stats (status_t update_level) { + int ret = 0; + + pthread_mutex_lock(&stats_update_lock); switch (update_level) { case STAT_BADCONN: ++stats->num_badcons; @@ -149,8 +161,9 @@ int update_stats (status_t update_level) ++stats->num_denied; break; default: - return -1; + ret = -1; } + pthread_mutex_unlock(&stats_update_lock); - return 0; + return ret; } diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index dd95400c..6f797ef7 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -84,10 +84,6 @@ Logfile "$TINYPROXY_LOG_DIR/tinyproxy.log" PidFile "$TINYPROXY_PID_FILE" LogLevel Info MaxClients 100 -MinSpareServers 5 -MaxSpareServers 20 -StartServers 10 -MaxRequestsPerChild 0 Allow 127.0.0.0/8 ViaProxyName "tinyproxy" #DisableViaHeader Yes From 1186c297b4651d5c84ac7a387b07cbc12ec0c69a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 18 Dec 2018 23:36:04 +0000 Subject: [PATCH 039/265] conf.c: pass lineno to handler funcs --- src/conf.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/conf.c b/src/conf.c index d7cf5b60..575fc6c0 100644 --- a/src/conf.c +++ b/src/conf.c @@ -91,7 +91,8 @@ * All configuration handling functions are REQUIRED to be defined * with the same function template as below. */ -typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, regmatch_t[]); +typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, + unsigned long, regmatch_t[]); /* * Define the pattern used by any directive handling function. The @@ -106,7 +107,7 @@ typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, regmatch_t[]); */ #define HANDLE_FUNC(func) \ int func(struct config_s* conf, const char* line, \ - regmatch_t match[]) + unsigned long lineno, regmatch_t match[]) /* * List all the handling functions. These are defined later, but they need @@ -369,7 +370,8 @@ config_free_regex (void) * Returns 0 if a match was found and successfully processed; otherwise, * a negative number is returned. */ -static int check_match (struct config_s *conf, const char *line) +static int check_match (struct config_s *conf, const char *line, + unsigned long lineno) { regmatch_t match[RE_MAX_MATCHES]; unsigned int i; @@ -380,7 +382,7 @@ static int check_match (struct config_s *conf, const char *line) assert (directives[i].cre); if (!regexec (directives[i].cre, line, RE_MAX_MATCHES, match, 0)) - return (*directives[i].handler) (conf, line, match); + return (*directives[i].handler) (conf, line, lineno, match); } return -1; @@ -395,7 +397,7 @@ static int config_parse (struct config_s *conf, FILE * f) unsigned long lineno = 1; while (fgets (buffer, sizeof (buffer), f)) { - if (check_match (conf, buffer)) { + if (check_match (conf, buffer, lineno)) { printf ("Syntax error on line %ld\n", lineno); return 1; } From b09d8d927de61e5b4411f8e9f713bfcf10a04796 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 18 Dec 2018 23:38:00 +0000 Subject: [PATCH 040/265] conf.c: merely warn on encountering recently obsoleted config items if we don't handle these gracefully, pretty much every existing config file will fail with an error, which is probably not very friendly. the obsoleted config items can be made hard errors after the next release. --- src/conf.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/conf.c b/src/conf.c index 575fc6c0..eded11a1 100644 --- a/src/conf.c +++ b/src/conf.c @@ -140,6 +140,7 @@ static HANDLE_FUNC (handle_listen); static HANDLE_FUNC (handle_logfile); static HANDLE_FUNC (handle_loglevel); static HANDLE_FUNC (handle_maxclients); +static HANDLE_FUNC (handle_obsolete); static HANDLE_FUNC (handle_pidfile); static HANDLE_FUNC (handle_port); #ifdef REVERSE_SUPPORT @@ -213,6 +214,10 @@ struct { /* integer arguments */ STDCONF ("port", INT, handle_port), STDCONF ("maxclients", INT, handle_maxclients), + STDCONF ("maxspareservers", INT, handle_obsolete), + STDCONF ("minspareservers", INT, handle_obsolete), + STDCONF ("startservers", INT, handle_obsolete), + STDCONF ("maxrequestsperchild", INT, handle_obsolete), STDCONF ("timeout", INT, handle_timeout), STDCONF ("connectport", INT, handle_connectport), /* alphanumeric arguments */ @@ -802,6 +807,13 @@ static HANDLE_FUNC (handle_maxclients) return 0; } +static HANDLE_FUNC (handle_obsolete) +{ + fprintf (stderr, "WARNING: obsolete config item on line %lu\n", + lineno); + return 0; +} + static HANDLE_FUNC (handle_timeout) { return set_int_arg (&conf->idletimeout, line, &match[2]); From fa2ad0cf9ac1bf24c7c4ddb188b5922132e38f73 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 18 Dec 2018 23:48:57 +0000 Subject: [PATCH 041/265] log.c: protect logging facility with a mutex since the write syscall is used instead of stdio, accesses have been safe already, but it's better to use a mutex anyway to prevent out- of-order writes. --- src/log.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/log.c b/src/log.c index f85d29d8..0ed686bb 100644 --- a/src/log.c +++ b/src/log.c @@ -29,6 +29,7 @@ #include "utils.h" #include "vector.h" #include "conf.h" +#include static const char *syslog_level[] = { NULL, @@ -45,6 +46,8 @@ static const char *syslog_level[] = { #define TIME_LENGTH 16 #define STRING_LENGTH 800 +static pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; + /* * Global file descriptor for the log file */ @@ -165,12 +168,14 @@ void log_message (int level, const char *fmt, ...) goto out; if (config.syslog) { + pthread_mutex_lock(&log_mutex); #ifdef HAVE_VSYSLOG_H vsyslog (level, fmt, args); #else vsnprintf (str, STRING_LENGTH, fmt, args); syslog (level, "%s", str); #endif + pthread_mutex_unlock(&log_mutex); } else { char *p; @@ -196,7 +201,10 @@ void log_message (int level, const char *fmt, ...) assert (log_file_fd >= 0); + pthread_mutex_lock(&log_mutex); ret = write (log_file_fd, str, strlen (str)); + pthread_mutex_unlock(&log_mutex); + if (ret == -1) { config.syslog = TRUE; @@ -207,7 +215,10 @@ void log_message (int level, const char *fmt, ...) "Falling back to syslog logging"); } + pthread_mutex_lock(&log_mutex); fsync (log_file_fd); + pthread_mutex_unlock(&log_mutex); + } out: From 82e10935d2955923d419cb46ee97e0022a8dfdb0 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 31 Dec 2018 14:24:17 +0000 Subject: [PATCH 042/265] move sockaddr_union to sock.h --- src/child.c | 5 ----- src/sock.h | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/child.c b/src/child.c index 06a977eb..3ae3d82c 100644 --- a/src/child.c +++ b/src/child.c @@ -36,11 +36,6 @@ static vector_t listen_fds; -union sockaddr_union { - struct sockaddr_in v4; - struct sockaddr_in6 v6; -}; - struct client { union sockaddr_union addr; int fd; diff --git a/src/sock.h b/src/sock.h index f1225eab..a516d4f2 100644 --- a/src/sock.h +++ b/src/sock.h @@ -28,8 +28,14 @@ #define MAXLINE (1024 * 4) +#include "common.h" #include "vector.h" +union sockaddr_union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; +}; + extern int opensock (const char *host, int port, const char *bind_to); extern int listen_sock (const char *addr, uint16_t port, vector_t listen_fds); From f6d4da5d81694721bf50b2275621e7ce84e6da30 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 31 Dec 2018 15:47:40 +0000 Subject: [PATCH 043/265] do hostname resolution only when it is absolutely necessary for ACL check tinyproxy used to do a full hostname resolution whenever a new client connection happened, which could cause very long delays (as reported in #198). there's only a single place/scenario that actually requires a hostname, and that is when an Allow/Deny rule exists for a hostname or domain, rather than a raw IP address. since it is very likely this feature is not very widely used, it makes absolute sense to only do the costly resolution when it is unavoidable. --- docs/man5/tinyproxy.conf.txt.in | 3 +++ src/acl.c | 28 +++++++++++++++++++--------- src/acl.h | 3 ++- src/child.c | 2 +- src/conns.c | 4 ---- src/conns.h | 4 +--- src/html-error.c | 1 - src/reqs.c | 15 +++++++-------- src/reqs.h | 3 ++- src/sock.c | 26 ++++---------------------- src/sock.h | 2 +- 11 files changed, 40 insertions(+), 51 deletions(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index afd3b6b2..3e24852d 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -196,6 +196,9 @@ The possible keywords and their descriptions are as follows: end of the client host name, i.e, this can be a full host name like `host.example.com` or a domain name like `.example.com` or even a top level domain name like `.com`. + Note that by adding a rule using a host or domain name, a costly name + lookup has to be done for every new connection, which could slow down + the service considerably. *AddHeader*:: diff --git a/src/acl.c b/src/acl.c index b7a334c9..7918a079 100644 --- a/src/acl.c +++ b/src/acl.c @@ -221,8 +221,8 @@ insert_acl (char *location, acl_access_t access_type, vector_t *access_list) * -1 if no tests match, so skip */ static int -acl_string_processing (struct acl_s *acl, - const char *ip_address, const char *string_address) +acl_string_processing (struct acl_s *acl, const char *ip_address, + union sockaddr_union *addr, char *string_addr) { int match; struct addrinfo hints, *res, *ressave; @@ -231,7 +231,6 @@ acl_string_processing (struct acl_s *acl, assert (acl && acl->type == ACL_STRING); assert (ip_address && strlen (ip_address) > 0); - assert (string_address && strlen (string_address) > 0); /* * If the first character of the ACL string is a period, we need to @@ -267,7 +266,15 @@ acl_string_processing (struct acl_s *acl, } STRING_TEST: - test_length = strlen (string_address); + if(string_addr[0] == 0) { + /* only do costly hostname resolution when it is absolutely needed, + and only once */ + if(getnameinfo ((void *) addr, sizeof (*addr), + string_addr, HOSTNAME_LENGTH, NULL, 0, 0) != 0) + return -1; + } + + test_length = strlen (string_addr); match_length = strlen (acl->address.string); /* @@ -278,7 +285,7 @@ acl_string_processing (struct acl_s *acl, return -1; if (strcasecmp - (string_address + (test_length - match_length), + (string_addr + (test_length - match_length), acl->address.string) == 0) { if (acl->access == ACL_DENY) return 0; @@ -329,15 +336,18 @@ static int check_numeric_acl (const struct acl_s *acl, const char *ip) * 1 if allowed * 0 if denied */ -int check_acl (const char *ip, const char *host, vector_t access_list) +int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list) { struct acl_s *acl; int perm = 0; size_t i; + char string_addr[HOSTNAME_LENGTH]; assert (ip != NULL); assert (host != NULL); + string_addr[0] = 0; + /* * If there is no access list allow everything. */ @@ -348,7 +358,7 @@ int check_acl (const char *ip, const char *host, vector_t access_list) acl = (struct acl_s *) vector_getentry (access_list, i, NULL); switch (acl->type) { case ACL_STRING: - perm = acl_string_processing (acl, ip, host); + perm = acl_string_processing (acl, ip, addr, string_addr); break; case ACL_NUMERIC: @@ -371,8 +381,8 @@ int check_acl (const char *ip, const char *host, vector_t access_list) /* * Deny all connections by default. */ - log_message (LOG_NOTICE, "Unauthorized connection from \"%s\" [%s].", - host, ip); + log_message (LOG_NOTICE, "Unauthorized connection from \"%s\".", + ip); return 0; } diff --git a/src/acl.h b/src/acl.h index 2d11cef1..ba0aebee 100644 --- a/src/acl.h +++ b/src/acl.h @@ -22,12 +22,13 @@ #define TINYPROXY_ACL_H #include "vector.h" +#include "sock.h" typedef enum { ACL_ALLOW, ACL_DENY } acl_access_t; extern int insert_acl (char *location, acl_access_t access_type, vector_t *access_list); -extern int check_acl (const char *ip_address, const char *string_address, +extern int check_acl (const char *ip_address, union sockaddr_union *addr, vector_t access_list); extern void flush_access_list (vector_t access_list); diff --git a/src/child.c b/src/child.c index 3ae3d82c..8bd713d7 100644 --- a/src/child.c +++ b/src/child.c @@ -50,7 +50,7 @@ struct child { static void* child_thread(void* data) { struct child *c = data; - handle_connection (c->client.fd); + handle_connection (c->client.fd, &c->client.addr); c->done = 1; return NULL; } diff --git a/src/conns.c b/src/conns.c index 94faeea6..505b5c4a 100644 --- a/src/conns.c +++ b/src/conns.c @@ -31,7 +31,6 @@ #include "stats.h" struct conn_s *initialize_conn (int client_fd, const char *ipaddr, - const char *string_addr, const char *sock_ipaddr) { struct conn_s *connptr; @@ -79,7 +78,6 @@ struct conn_s *initialize_conn (int client_fd, const char *ipaddr, connptr->server_ip_addr = (sock_ipaddr ? safestrdup (sock_ipaddr) : NULL); connptr->client_ip_addr = safestrdup (ipaddr); - connptr->client_string_addr = safestrdup (string_addr); connptr->upstream_proxy = NULL; @@ -134,8 +132,6 @@ void destroy_conn (struct conn_s *connptr) safefree (connptr->server_ip_addr); if (connptr->client_ip_addr) safefree (connptr->client_ip_addr); - if (connptr->client_string_addr) - safefree (connptr->client_string_addr); #ifdef REVERSE_SUPPORT if (connptr->reversepath) diff --git a/src/conns.h b/src/conns.h index b63d0266..393e5d42 100644 --- a/src/conns.h +++ b/src/conns.h @@ -62,10 +62,9 @@ struct conn_s { char *server_ip_addr; /* - * Store the client's IP and hostname information + * Store the client's IP information */ char *client_ip_addr; - char *client_string_addr; /* * Store the incoming request's HTTP protocol. @@ -92,7 +91,6 @@ struct conn_s { * Functions for the creation and destruction of a connection structure. */ extern struct conn_s *initialize_conn (int client_fd, const char *ipaddr, - const char *string_addr, const char *sock_ipaddr); extern void destroy_conn (struct conn_s *connptr); diff --git a/src/html-error.c b/src/html-error.c index ee3c9877..2b15c087 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -262,7 +262,6 @@ int add_standard_vars (struct conn_s *connptr) ADD_VAR_RET ("cause", connptr->error_string); ADD_VAR_RET ("request", connptr->request_line); ADD_VAR_RET ("clientip", connptr->client_ip_addr); - ADD_VAR_RET ("clienthost", connptr->client_string_addr); /* The following value parts are all non-NULL and will * trigger warnings in ADD_VAR_RET(), so we use diff --git a/src/reqs.c b/src/reqs.c index 8450cffc..c576412c 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1533,7 +1533,7 @@ get_request_entity(struct conn_s *connptr) * tinyproxy code, which was confusing, redundant. Hail progress. * - rjkaes */ -void handle_connection (int fd) +void handle_connection (int fd, union sockaddr_union* addr) { ssize_t i; struct conn_s *connptr; @@ -1542,26 +1542,25 @@ void handle_connection (int fd) char sock_ipaddr[IP_LENGTH]; char peer_ipaddr[IP_LENGTH]; - char peer_string[HOSTNAME_LENGTH]; - getpeer_information (fd, peer_ipaddr, peer_string); + getpeer_information (addr, peer_ipaddr, sizeof(peer_ipaddr)); 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); + "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, + connptr = initialize_conn (fd, peer_ipaddr, config.bindsame ? sock_ipaddr : NULL); if (!connptr) { close (fd); return; } - if (check_acl (peer_ipaddr, peer_string, config.access_list) <= 0) { + if (check_acl (peer_ipaddr, addr, config.access_list) <= 0) { update_stats (STAT_DENIED); indicate_http_error (connptr, 403, "Access denied", "detail", diff --git a/src/reqs.h b/src/reqs.h index 73dd030a..c1c51001 100644 --- a/src/reqs.h +++ b/src/reqs.h @@ -23,6 +23,7 @@ #define _TINYPROXY_REQS_H_ #include "common.h" +#include "sock.h" /* * Port constants for HTTP (80) and SSL (443) @@ -43,6 +44,6 @@ struct request_s { char *path; }; -extern void handle_connection (int fd); +extern void handle_connection (int fd, union sockaddr_union* addr); #endif diff --git a/src/sock.c b/src/sock.c index 59c2fa8d..f74a588f 100644 --- a/src/sock.c +++ b/src/sock.c @@ -354,27 +354,9 @@ int getsock_ip (int fd, char *ipaddr) /* * Return the peer's socket information. */ -int getpeer_information (int fd, char *ipaddr, char *string_addr) +void getpeer_information (union sockaddr_union* addr, char *ipaddr, size_t ipaddr_len) { - struct sockaddr_storage sa; - socklen_t salen = sizeof sa; - - assert (fd >= 0); - assert (ipaddr != NULL); - assert (string_addr != NULL); - - /* Set the strings to default values */ - ipaddr[0] = '\0'; - strlcpy (string_addr, "[unknown]", HOSTNAME_LENGTH); - - /* Look up the IP address */ - if (getpeername (fd, (struct sockaddr *) &sa, &salen) != 0) - return -1; - - if (get_ip_string ((struct sockaddr *) &sa, ipaddr, IP_LENGTH) == NULL) - return -1; - - /* Get the full host name */ - return getnameinfo ((struct sockaddr *) &sa, salen, - string_addr, HOSTNAME_LENGTH, NULL, 0, 0); + int af = addr->v4.sin_family; + void *ipdata = af == AF_INET ? (void*)&addr->v4.sin_addr : (void*)&addr->v6.sin6_addr; + inet_ntop(af, ipdata, ipaddr, ipaddr_len); } diff --git a/src/sock.h b/src/sock.h index a516d4f2..033e179b 100644 --- a/src/sock.h +++ b/src/sock.h @@ -43,6 +43,6 @@ extern int socket_nonblocking (int sock); extern int socket_blocking (int sock); extern int getsock_ip (int fd, char *ipaddr); -extern int getpeer_information (int fd, char *ipaddr, char *string_addr); +extern void getpeer_information (union sockaddr_union *addr, char *ipaddr, size_t ipaddr_len); #endif From cd005a94cec38e73ca796f1d142c193f48aaa27f Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 31 Dec 2018 22:25:04 +0000 Subject: [PATCH 044/265] implement detection and denial of endless connection loops it is quite easy to bring down a proxy server by forcing it to make connections to one of its own ports, because this will result in an endless loop spawning more and more connections, until all available fds are exhausted. since there's a potentially infinite number of potential DNS/ip addresses resolving to the proxy, it is impossible to detect an endless loop by simply looking at the destination ip address and port. what *is* possible though is to record the ip/port tuples assigned to outgoing connections, and then compare them against new incoming connections. if they match, the sender was the proxy itself and therefore needs to reject that connection. fixes #199. --- src/Makefile.am | 1 + src/child.c | 3 ++ src/loop.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++ src/loop.h | 11 +++++++ src/reqs.c | 15 ++++++++++ src/sock.c | 12 +++++++- 6 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 src/loop.c create mode 100644 src/loop.h diff --git a/src/Makefile.am b/src/Makefile.am index 39249097..50e645ba 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,6 +49,7 @@ tinyproxy_SOURCES = \ basicauth.c basicauth.h \ base64.c base64.h \ sblist.c sblist.h \ + loop.c loop.h \ connect-ports.c connect-ports.h EXTRA_tinyproxy_SOURCES = filter.c filter.h \ diff --git a/src/child.c b/src/child.c index 8bd713d7..861606ba 100644 --- a/src/child.c +++ b/src/child.c @@ -32,6 +32,7 @@ #include "utils.h" #include "conf.h" #include "sblist.h" +#include "loop.h" #include static vector_t listen_fds; @@ -87,6 +88,8 @@ void child_main_loop (void) childs = sblist_new(sizeof (struct child*), config.maxclients); + loop_records_init(); + /* * We have to wait for connections on multiple fds, * so use select. diff --git a/src/loop.c b/src/loop.c new file mode 100644 index 00000000..77b073df --- /dev/null +++ b/src/loop.c @@ -0,0 +1,76 @@ +#include +#include +#include "loop.h" +#include "conf.h" +#include "main.h" +#include "sblist.h" +#include "sock.h" + +struct loop_record { + union sockaddr_union addr; + time_t tstamp; +}; + +static sblist *loop_records; +static pthread_mutex_t loop_records_lock = PTHREAD_MUTEX_INITIALIZER; + +void loop_records_init(void) { + loop_records = sblist_new(sizeof (struct loop_record), 32); +} + +#if 0 +static void su_to_str(union sockaddr_union *addr, char *buf) { + int af = addr->v4.sin_family; + unsigned port = ntohs(af == AF_INET ? addr->v4.sin_port : addr->v6.sin6_port); + char portb[32]; + sprintf(portb, ":%u", port); + getpeer_information (addr, buf, 256); + strcat(buf, portb); +} +#endif + +void loop_records_add(union sockaddr_union *addr) { + time_t now =time(0); + struct loop_record rec; + pthread_mutex_lock(&loop_records_lock); + rec.tstamp = now; + rec.addr = *addr; + sblist_add(loop_records, &rec); + pthread_mutex_unlock(&loop_records_lock); +} + +#define TIMEOUT_SECS 15 + +int connection_loops (union sockaddr_union *addr) { + int ret = 0, af, our_af = addr->v4.sin_family; + void *ipdata, *our_ipdata = our_af == AF_INET ? (void*)&addr->v4.sin_addr.s_addr : (void*)&addr->v6.sin6_addr.s6_addr; + size_t i, cmp_len = our_af == AF_INET ? sizeof(addr->v4.sin_addr.s_addr) : sizeof(addr->v6.sin6_addr.s6_addr); + unsigned port, our_port = ntohs(our_af == AF_INET ? addr->v4.sin_port : addr->v6.sin6_port); + time_t now = time(0); + + pthread_mutex_lock(&loop_records_lock); + for (i = 0; i < sblist_getsize(loop_records); ) { + struct loop_record *rec = sblist_get(loop_records, i); + + if (rec->tstamp + TIMEOUT_SECS < now) { + sblist_delete(loop_records, i); + continue; + } + + if (!ret) { + af = rec->addr.v4.sin_family; + if (af != our_af) goto next; + port = ntohs(af == AF_INET ? rec->addr.v4.sin_port : rec->addr.v6.sin6_port); + if (port != our_port) goto next; + ipdata = af == AF_INET ? (void*)&rec->addr.v4.sin_addr.s_addr : (void*)&rec->addr.v6.sin6_addr.s6_addr; + if (!memcmp(ipdata, our_ipdata, cmp_len)) { + ret = 1; + } + } +next: + i++; + } + pthread_mutex_unlock(&loop_records_lock); + return ret; +} + diff --git a/src/loop.h b/src/loop.h new file mode 100644 index 00000000..19877d35 --- /dev/null +++ b/src/loop.h @@ -0,0 +1,11 @@ +#ifndef LOOP_H +#define LOOP_H + +#include "sock.h" + +void loop_records_init(void); +void loop_records_add(union sockaddr_union *addr); +int connection_loops (union sockaddr_union *addr); + +#endif + diff --git a/src/reqs.c b/src/reqs.c index c576412c..3adc473a 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -49,6 +49,7 @@ #include "connect-ports.h" #include "conf.h" #include "basicauth.h" +#include "loop.h" /* * Maximum length of a HTTP line @@ -1560,6 +1561,20 @@ void handle_connection (int fd, union sockaddr_union* addr) return; } + 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); + goto fail; + } + + if (check_acl (peer_ipaddr, addr, config.access_list) <= 0) { update_stats (STAT_DENIED); indicate_http_error (connptr, 403, "Access denied", diff --git a/src/sock.c b/src/sock.c index f74a588f..8513ba89 100644 --- a/src/sock.c +++ b/src/sock.c @@ -33,6 +33,7 @@ #include "sock.h" #include "text.h" #include "conf.h" +#include "loop.h" /* * Return a human readable error for getaddrinfo() and getnameinfo(). @@ -141,8 +142,17 @@ int opensock (const char *host, int port, const char *bind_to) } } - if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) + if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) { + union sockaddr_union *p = (void*) res->ai_addr, u; + int af = res->ai_addr->sa_family; + unsigned dport = ntohs(af == AF_INET ? p->v4.sin_port : p->v6.sin6_port); + socklen_t slen = sizeof u; + if (dport == config.port) { + getsockname(sockfd, (void*)&u, &slen); + loop_records_add(&u); + } break; /* success */ + } close (sockfd); } while ((res = res->ai_next) != NULL); From 25205fd1f328df935aa416c9478d6a0bcf1a7d96 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 14:17:13 +0000 Subject: [PATCH 045/265] move initialize_config_defaults to conf.c --- src/conf.c | 22 ++++++++++++++++++++++ src/conf.h | 2 ++ src/main.c | 22 ---------------------- 3 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/conf.c b/src/conf.c index eded11a1..16a026de 100644 --- a/src/conf.c +++ b/src/conf.c @@ -442,6 +442,28 @@ static int load_config_file (const char *config_fname, struct config_s *conf) return ret; } +void initialize_config_defaults (struct config_s *conf) +{ + memset (conf, 0, sizeof(*conf)); + + conf->config_file = safestrdup (SYSCONFDIR "/tinyproxy.conf"); + if (!conf->config_file) { + fprintf (stderr, PACKAGE ": Could not allocate memory.\n"); + exit (EX_SOFTWARE); + } + conf->godaemon = TRUE; + /* + * Make sure the HTML error pages array is NULL to begin with. + * (FIXME: Should have a better API for all this) + */ + conf->errorpages = NULL; + conf->stathost = safestrdup (TINYPROXY_STATHOST); + conf->idletimeout = MAX_IDLE_TIME; + conf->logf_name = NULL; + conf->pidpath = NULL; + conf->maxclients = 100; +} + static void initialize_with_defaults (struct config_s *conf, struct config_s *defaults) { diff --git a/src/conf.h b/src/conf.h index 44f12d79..43f4f97f 100644 --- a/src/conf.h +++ b/src/conf.h @@ -114,6 +114,8 @@ struct config_s { vector_t add_headers; }; +void initialize_config_defaults (struct config_s *conf); + extern int reload_config_file (const char *config_fname, struct config_s *conf, struct config_s *defaults); diff --git a/src/main.c b/src/main.c index 06465b14..7f6d9477 100644 --- a/src/main.c +++ b/src/main.c @@ -284,28 +284,6 @@ change_user (const char *program) } } -static void initialize_config_defaults (struct config_s *conf) -{ - memset (conf, 0, sizeof(*conf)); - - conf->config_file = safestrdup (SYSCONFDIR "/tinyproxy.conf"); - if (!conf->config_file) { - fprintf (stderr, PACKAGE ": Could not allocate memory.\n"); - exit (EX_SOFTWARE); - } - conf->godaemon = TRUE; - /* - * Make sure the HTML error pages array is NULL to begin with. - * (FIXME: Should have a better API for all this) - */ - conf->errorpages = NULL; - conf->stathost = safestrdup (TINYPROXY_STATHOST); - conf->idletimeout = MAX_IDLE_TIME; - conf->logf_name = NULL; - conf->pidpath = NULL; - conf->maxclients = 100; -} - /** * convenience wrapper around reload_config_file * that also re-initializes logging. From 40afaeb63754c8495b92696a3f663a9e42b7b7ac Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 14:45:23 +0000 Subject: [PATCH 046/265] move commandline parsing to main() --- src/main.c | 82 +++++++++++++++++++++++------------------------------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/src/main.c b/src/main.c index 7f6d9477..aac9a5b3 100644 --- a/src/main.c +++ b/src/main.c @@ -162,52 +162,6 @@ get_id (char *str) return atoi (str); } -/** - * process_cmdline: - * @argc: argc as passed to main() - * @argv: argv as passed to main() - * - * This function parses command line arguments. - **/ -static void -process_cmdline (int argc, char **argv, struct config_s *conf) -{ - int opt; - - while ((opt = getopt (argc, argv, "c:vdh")) != EOF) { - switch (opt) { - case 'v': - display_version (); - exit (EX_OK); - - case 'd': - conf->godaemon = FALSE; - break; - - case 'c': - if (conf->config_file != NULL) { - safefree (conf->config_file); - } - conf->config_file = safestrdup (optarg); - if (!conf->config_file) { - fprintf (stderr, - "%s: Could not allocate memory.\n", - argv[0]); - exit (EX_SOFTWARE); - } - break; - - case 'h': - display_usage (); - exit (EX_OK); - - default: - display_usage (); - exit (EX_USAGE); - } - } -} - /** * change_user: * @program: The name of the program. Pass argv[0] here. @@ -309,6 +263,8 @@ int reload_config (void) int main (int argc, char **argv) { + int opt; + srand(time(NULL)); /* for hashmap seeds */ /* Only allow u+rw bits. This may be required for some versions @@ -323,7 +279,39 @@ main (int argc, char **argv) } initialize_config_defaults (&config_defaults); - process_cmdline (argc, argv, &config_defaults); + + while ((opt = getopt (argc, argv, "c:vdh")) != EOF) { + switch (opt) { + case 'v': + display_version (); + exit (EX_OK); + + case 'd': + (&config_defaults)->godaemon = FALSE; + break; + + case 'c': + if ((&config_defaults)->config_file != NULL) { + safefree ((&config_defaults)->config_file); + } + (&config_defaults)->config_file = safestrdup (optarg); + if (!(&config_defaults)->config_file) { + fprintf (stderr, + "%s: Could not allocate memory.\n", + argv[0]); + exit (EX_SOFTWARE); + } + break; + + case 'h': + display_usage (); + exit (EX_OK); + + default: + display_usage (); + exit (EX_USAGE); + } + } if (reload_config_file (config_defaults.config_file, &config, From 4fb2c1403907a860c407471537484ba5cf1ef992 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 15:09:37 +0000 Subject: [PATCH 047/265] syslog: always use LOG_USER facility LOG_DAEMON isn't specified in POSIX and the gratuitously different treatment is in the way of a planned cleanup. --- src/log.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/log.c b/src/log.c index 0ed686bb..f31696dc 100644 --- a/src/log.c +++ b/src/log.c @@ -292,10 +292,7 @@ int setup_logging (void) } if (config.syslog) { - if (config.godaemon == TRUE) - openlog ("tinyproxy", LOG_PID, LOG_DAEMON); - else - openlog ("tinyproxy", LOG_PID, LOG_USER); + openlog ("tinyproxy", LOG_PID, LOG_USER); } logging_initialized = TRUE; From eb2104e1ff07dcc7a05f81f8b87db9b6c77d8f5d Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 15:18:50 +0000 Subject: [PATCH 048/265] log: remove special case code for daemonized mode without logfile if daemon mode is used and neither logfile nor syslog options specified, this is clearly a misconfiguration issue. don't try to be smart and work around that, so less global state information is required. also, this case is already checked for in main.c:334. --- src/log.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/log.c b/src/log.c index f31696dc..7158492a 100644 --- a/src/log.c +++ b/src/log.c @@ -74,10 +74,7 @@ static unsigned int logging_initialized = FALSE; /* boolean */ int open_log_file (const char *log_file_name) { if (log_file_name == NULL) { - if(config.godaemon == FALSE) - log_file_fd = fileno(stdout); - else - log_file_fd = -1; + log_file_fd = fileno(stdout); } else { log_file_fd = create_file_safely (log_file_name, FALSE); } From 180c0664aa2af528745f997df0fbeadb2c87bcff Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 15:26:40 +0000 Subject: [PATCH 049/265] remove godaemon member from config structure since this option can't be set via config file, it makes sense to factor it out and use it only where strictly needed, e.g. in startup code. --- src/conf.c | 2 -- src/conf.h | 1 - src/main.c | 6 +++--- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/conf.c b/src/conf.c index 16a026de..c74216d9 100644 --- a/src/conf.c +++ b/src/conf.c @@ -451,7 +451,6 @@ void initialize_config_defaults (struct config_s *conf) fprintf (stderr, PACKAGE ": Could not allocate memory.\n"); exit (EX_SOFTWARE); } - conf->godaemon = TRUE; /* * Make sure the HTML error pages array is NULL to begin with. * (FIXME: Should have a better API for all this) @@ -482,7 +481,6 @@ static void initialize_with_defaults (struct config_s *conf, conf->stathost = safestrdup (defaults->stathost); } - conf->godaemon = defaults->godaemon; conf->quit = defaults->quit; if (defaults->user) { diff --git a/src/conf.h b/src/conf.h index 43f4f97f..02fb6998 100644 --- a/src/conf.h +++ b/src/conf.h @@ -43,7 +43,6 @@ struct config_s { unsigned int syslog; /* boolean */ unsigned int port; char *stathost; - unsigned int godaemon; /* boolean */ unsigned int quit; /* boolean */ unsigned int maxclients; char *user; diff --git a/src/main.c b/src/main.c index aac9a5b3..8b2167d2 100644 --- a/src/main.c +++ b/src/main.c @@ -263,7 +263,7 @@ int reload_config (void) int main (int argc, char **argv) { - int opt; + int opt, daemonized = TRUE; srand(time(NULL)); /* for hashmap seeds */ @@ -287,7 +287,7 @@ main (int argc, char **argv) exit (EX_OK); case 'd': - (&config_defaults)->godaemon = FALSE; + daemonized = FALSE; break; case 'c': @@ -330,7 +330,7 @@ main (int argc, char **argv) anonymous_insert ("Content-Type"); } - if (config.godaemon == TRUE) { + if (daemonized == TRUE) { if (!config.syslog && config.logf_name == NULL) fprintf(stderr, "WARNING: logging deactivated " "(can't log to stdout when daemonized)\n"); From bffa70500562f0ed675ed8d7e2385925c25f14fc Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 15:42:20 +0000 Subject: [PATCH 050/265] remove config file name item from conf struct since this is set via command line, we can deal with it easily from where it is actually needed. --- src/conf.c | 10 ---------- src/conf.h | 1 - src/main.c | 20 +++++++------------- 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/src/conf.c b/src/conf.c index c74216d9..c9a499e6 100644 --- a/src/conf.c +++ b/src/conf.c @@ -287,7 +287,6 @@ free_added_headers (vector_t add_headers) static void free_config (struct config_s *conf) { - safefree (conf->config_file); safefree (conf->logf_name); safefree (conf->stathost); safefree (conf->user); @@ -446,11 +445,6 @@ void initialize_config_defaults (struct config_s *conf) { memset (conf, 0, sizeof(*conf)); - conf->config_file = safestrdup (SYSCONFDIR "/tinyproxy.conf"); - if (!conf->config_file) { - fprintf (stderr, PACKAGE ": Could not allocate memory.\n"); - exit (EX_SOFTWARE); - } /* * Make sure the HTML error pages array is NULL to begin with. * (FIXME: Should have a better API for all this) @@ -470,10 +464,6 @@ static void initialize_with_defaults (struct config_s *conf, conf->logf_name = safestrdup (defaults->logf_name); } - if (defaults->config_file) { - conf->config_file = safestrdup (defaults->config_file); - } - conf->syslog = defaults->syslog; conf->port = defaults->port; diff --git a/src/conf.h b/src/conf.h index 02fb6998..9e10898f 100644 --- a/src/conf.h +++ b/src/conf.h @@ -39,7 +39,6 @@ typedef struct { struct config_s { vector_t basicauth_list; char *logf_name; - char *config_file; unsigned int syslog; /* boolean */ unsigned int port; char *stathost; diff --git a/src/main.c b/src/main.c index 8b2167d2..b4a5e536 100644 --- a/src/main.c +++ b/src/main.c @@ -49,6 +49,7 @@ */ struct config_s config; struct config_s config_defaults; +static const char* config_file; unsigned int received_sighup = FALSE; /* boolean */ /* @@ -248,7 +249,7 @@ int reload_config (void) shutdown_logging (); - ret = reload_config_file (config_defaults.config_file, &config, + ret = reload_config_file (config_file, &config, &config_defaults); if (ret != 0) { goto done; @@ -278,7 +279,7 @@ main (int argc, char **argv) exit (EX_SOFTWARE); } - initialize_config_defaults (&config_defaults); + config_file = SYSCONFDIR "/tinyproxy.conf"; while ((opt = getopt (argc, argv, "c:vdh")) != EOF) { switch (opt) { @@ -291,16 +292,7 @@ main (int argc, char **argv) break; case 'c': - if ((&config_defaults)->config_file != NULL) { - safefree ((&config_defaults)->config_file); - } - (&config_defaults)->config_file = safestrdup (optarg); - if (!(&config_defaults)->config_file) { - fprintf (stderr, - "%s: Could not allocate memory.\n", - argv[0]); - exit (EX_SOFTWARE); - } + config_file = optarg; break; case 'h': @@ -313,7 +305,9 @@ main (int argc, char **argv) } } - if (reload_config_file (config_defaults.config_file, + initialize_config_defaults (&config_defaults); + + if (reload_config_file (config_file, &config, &config_defaults)) { exit (EX_SOFTWARE); From c63d5d26b47b44d70af54aa31f811e4815fe4ad9 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 16:09:41 +0000 Subject: [PATCH 051/265] access config via a pointer, not a hardcoded struct address this is required so we can elegantly swap out an old config for a new one in the future and remove lots of boilerplate from config initialization code. unfortunately this is a quite intrusive change as the config struct was accessed in numerous places, but frankly it should have been done via a pointer right from the start. right now, we simply point to a static struct in main.c, so there shouldn't be any noticeable changes in behaviour. --- src/anonymous.c | 16 ++++++------ src/child.c | 6 ++--- src/filter.c | 10 +++---- src/html-error.c | 20 +++++++------- src/log.c | 22 ++++++++-------- src/main.c | 54 ++++++++++++++++++++------------------ src/main.h | 2 +- src/reqs.c | 64 ++++++++++++++++++++++----------------------- src/reverse-proxy.c | 8 +++--- src/sock.c | 6 ++--- src/stats.c | 2 +- 11 files changed, 107 insertions(+), 103 deletions(-) diff --git a/src/anonymous.c b/src/anonymous.c index 3049acf3..8d444652 100644 --- a/src/anonymous.c +++ b/src/anonymous.c @@ -30,7 +30,7 @@ short int is_anonymous_enabled (void) { - return (config.anonymous_map != NULL) ? 1 : 0; + return (config->anonymous_map != NULL) ? 1 : 0; } /* @@ -40,9 +40,9 @@ short int is_anonymous_enabled (void) int anonymous_search (const char *s) { assert (s != NULL); - assert (config.anonymous_map != NULL); + assert (config->anonymous_map != NULL); - return hashmap_search (config.anonymous_map, s); + return hashmap_search (config->anonymous_map, s); } /* @@ -57,17 +57,17 @@ int anonymous_insert (const char *s) assert (s != NULL); - if (!config.anonymous_map) { - config.anonymous_map = hashmap_create (32); - if (!config.anonymous_map) + if (!config->anonymous_map) { + config->anonymous_map = hashmap_create (32); + if (!config->anonymous_map) return -1; } - if (hashmap_search (config.anonymous_map, s) > 0) { + if (hashmap_search (config->anonymous_map, s) > 0) { /* The key was already found, so return a positive number. */ return 0; } /* Insert the new key */ - return hashmap_insert (config.anonymous_map, s, &data, sizeof (data)); + return hashmap_insert (config->anonymous_map, s, &data, sizeof (data)); } diff --git a/src/child.c b/src/child.c index 861606ba..4a94b98e 100644 --- a/src/child.c +++ b/src/child.c @@ -86,7 +86,7 @@ void child_main_loop (void) pthread_attr_t *attrp, attr; struct child *child; - childs = sblist_new(sizeof (struct child*), config.maxclients); + childs = sblist_new(sizeof (struct child*), config->maxclients); loop_records_init(); @@ -94,11 +94,11 @@ void child_main_loop (void) * We have to wait for connections on multiple fds, * so use select. */ - while (!config.quit) { + while (!config->quit) { collect_threads(); - if (sblist_getsize(childs) >= config.maxclients) { + if (sblist_getsize(childs) >= config->maxclients) { if (!was_full) log_message (LOG_NOTICE, "Maximum number of connections reached. " diff --git a/src/filter.c b/src/filter.c index e18132e2..206bf31d 100644 --- a/src/filter.c +++ b/src/filter.c @@ -59,7 +59,7 @@ void filter_init (void) return; } - fd = fopen (config.filter, "r"); + fd = fopen (config->filter, "r"); if (!fd) { return; } @@ -67,9 +67,9 @@ void filter_init (void) p = NULL; cflags = REG_NEWLINE | REG_NOSUB; - if (config.filter_extended) + if (config->filter_extended) cflags |= REG_EXTENDED; - if (!config.filter_casesensitive) + if (!config->filter_casesensitive) cflags |= REG_ICASE; while (fgets (buf, FILTER_BUFFER_LEN, fd)) { @@ -121,7 +121,7 @@ void filter_init (void) if (err != 0) { fprintf (stderr, "Bad regex in %s: %s\n", - config.filter, p->pat); + config->filter, p->pat); exit (EX_DATAERR); } } @@ -157,7 +157,7 @@ void filter_destroy (void) */ void filter_reload (void) { - if (config.filter) { + if (config->filter) { log_message (LOG_NOTICE, "Re-reading filter file."); filter_destroy (); filter_init (); diff --git a/src/html-error.c b/src/html-error.c index 2b15c087..3018b467 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -41,13 +41,13 @@ int add_new_errorpage (char *filepath, unsigned int errornum) { char errornbuf[ERRORNUM_BUFSIZE]; - config.errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT); - if (!config.errorpages) + config->errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT); + if (!config->errorpages) return (-1); snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum); - if (hashmap_insert (config.errorpages, errornbuf, + if (hashmap_insert (config->errorpages, errornbuf, filepath, strlen (filepath) + 1) < 0) return (-1); @@ -66,19 +66,19 @@ static char *get_html_file (unsigned int errornum) assert (errornum >= 100 && errornum < 1000); - if (!config.errorpages) - return (config.errorpage_undef); + if (!config->errorpages) + return (config->errorpage_undef); snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum); - result_iter = hashmap_find (config.errorpages, errornbuf); + result_iter = hashmap_find (config->errorpages, errornbuf); - if (hashmap_is_end (config.errorpages, result_iter)) - return (config.errorpage_undef); + if (hashmap_is_end (config->errorpages, result_iter)) + return (config->errorpage_undef); - if (hashmap_return_entry (config.errorpages, result_iter, + if (hashmap_return_entry (config->errorpages, result_iter, &key, (void **) &val) < 0) - return (config.errorpage_undef); + return (config->errorpage_undef); return (val); } diff --git a/src/log.c b/src/log.c index 7158492a..755a913c 100644 --- a/src/log.c +++ b/src/log.c @@ -129,7 +129,7 @@ void log_message (int level, const char *fmt, ...) return; #endif - if (config.syslog && level == LOG_CONN) + if (config && config->syslog && level == LOG_CONN) level = LOG_INFO; va_start (args, fmt); @@ -161,10 +161,10 @@ void log_message (int level, const char *fmt, ...) goto out; } - if(!config.syslog && log_file_fd == -1) + if(!config->syslog && log_file_fd == -1) goto out; - if (config.syslog) { + if (config->syslog) { pthread_mutex_lock(&log_mutex); #ifdef HAVE_VSYSLOG_H vsyslog (level, fmt, args); @@ -203,11 +203,11 @@ void log_message (int level, const char *fmt, ...) pthread_mutex_unlock(&log_mutex); if (ret == -1) { - config.syslog = TRUE; + config->syslog = TRUE; log_message(LOG_CRIT, "ERROR: Could not write to log " "file %s: %s.", - config.logf_name, strerror(errno)); + config->logf_name, strerror(errno)); log_message(LOG_CRIT, "Falling back to syslog logging"); } @@ -272,23 +272,23 @@ static void send_stored_logs (void) */ int setup_logging (void) { - if (!config.syslog) { - if (open_log_file (config.logf_name) < 0) { + if (!config->syslog) { + if (open_log_file (config->logf_name) < 0) { /* * If opening the log file fails, we try * to fall back to syslog logging... */ - config.syslog = TRUE; + config->syslog = TRUE; log_message (LOG_CRIT, "ERROR: Could not create log " "file %s: %s.", - config.logf_name, strerror (errno)); + config->logf_name, strerror (errno)); log_message (LOG_CRIT, "Falling back to syslog logging."); } } - if (config.syslog) { + if (config->syslog) { openlog ("tinyproxy", LOG_PID, LOG_USER); } @@ -307,7 +307,7 @@ void shutdown_logging (void) return; } - if (config.syslog) { + if (config->syslog) { closelog (); } else { close_log_file (); diff --git a/src/main.c b/src/main.c index b4a5e536..4a5f8ea4 100644 --- a/src/main.c +++ b/src/main.c @@ -47,8 +47,9 @@ /* * Global Structures */ -struct config_s config; -struct config_s config_defaults; +struct config_s *config; +static struct config_s config_main; +static struct config_s config_defaults; static const char* config_file; unsigned int received_sighup = FALSE; /* boolean */ @@ -68,7 +69,7 @@ takesig (int sig) case SIGINT: case SIGTERM: - config.quit = TRUE; + config->quit = TRUE; break; case SIGCHLD: @@ -174,16 +175,16 @@ get_id (char *str) static void change_user (const char *program) { - if (config.group && strlen (config.group) > 0) { - int gid = get_id (config.group); + if (config->group && strlen (config->group) > 0) { + int gid = get_id (config->group); if (gid < 0) { - struct group *thisgroup = getgrnam (config.group); + struct group *thisgroup = getgrnam (config->group); if (!thisgroup) { fprintf (stderr, "%s: Unable to find group \"%s\".\n", - program, config.group); + program, config->group); exit (EX_NOUSER); } @@ -193,7 +194,7 @@ change_user (const char *program) if (setgid (gid) < 0) { fprintf (stderr, "%s: Unable to change to group \"%s\".\n", - program, config.group); + program, config->group); exit (EX_NOPERM); } @@ -208,19 +209,19 @@ change_user (const char *program) #endif log_message (LOG_INFO, "Now running as group \"%s\".", - config.group); + config->group); } - if (config.user && strlen (config.user) > 0) { - int uid = get_id (config.user); + if (config->user && strlen (config->user) > 0) { + int uid = get_id (config->user); if (uid < 0) { - struct passwd *thisuser = getpwnam (config.user); + struct passwd *thisuser = getpwnam (config->user); if (!thisuser) { fprintf (stderr, "%s: Unable to find user \"%s\".\n", - program, config.user); + program, config->user); exit (EX_NOUSER); } @@ -230,12 +231,12 @@ change_user (const char *program) if (setuid (uid) < 0) { fprintf (stderr, "%s: Unable to change to user \"%s\".\n", - program, config.user); + program, config->user); exit (EX_NOPERM); } log_message (LOG_INFO, "Now running as user \"%s\".", - config.user); + config->user); } } @@ -249,12 +250,14 @@ int reload_config (void) shutdown_logging (); - ret = reload_config_file (config_file, &config, + ret = reload_config_file (config_file, &config_main, &config_defaults); if (ret != 0) { goto done; } + config = &config_main; + ret = setup_logging (); done: @@ -308,10 +311,11 @@ main (int argc, char **argv) initialize_config_defaults (&config_defaults); if (reload_config_file (config_file, - &config, + &config_main, &config_defaults)) { exit (EX_SOFTWARE); } + config = &config_main; init_stats (); @@ -325,7 +329,7 @@ main (int argc, char **argv) } if (daemonized == TRUE) { - if (!config.syslog && config.logf_name == NULL) + if (!config->syslog && config->logf_name == NULL) fprintf(stderr, "WARNING: logging deactivated " "(can't log to stdout when daemonized)\n"); @@ -339,20 +343,20 @@ main (int argc, char **argv) } #ifdef FILTER_ENABLE - if (config.filter) + if (config->filter) filter_init (); #endif /* FILTER_ENABLE */ /* Start listening on the selected port. */ - if (child_listening_sockets(config.listen_addrs, config.port) < 0) { + if (child_listening_sockets(config->listen_addrs, config->port) < 0) { fprintf (stderr, "%s: Could not create listening sockets.\n", argv[0]); exit (EX_OSERR); } /* Create pid file before we drop privileges */ - if (config.pidpath) { - if (pidfile_create (config.pidpath) < 0) { + if (config->pidpath) { + if (pidfile_create (config->pidpath) < 0) { fprintf (stderr, "%s: Could not create PID file.\n", argv[0]); exit (EX_OSERR); @@ -403,14 +407,14 @@ main (int argc, char **argv) child_close_sock (); /* Remove the PID file */ - if (config.pidpath != NULL && unlink (config.pidpath) < 0) { + if (config->pidpath != NULL && unlink (config->pidpath) < 0) { log_message (LOG_WARNING, "Could not remove PID file \"%s\": %s.", - config.pidpath, strerror (errno)); + config->pidpath, strerror (errno)); } #ifdef FILTER_ENABLE - if (config.filter) + if (config->filter) filter_destroy (); #endif /* FILTER_ENABLE */ diff --git a/src/main.h b/src/main.h index ca2ee4bf..5890d4a1 100644 --- a/src/main.h +++ b/src/main.h @@ -29,7 +29,7 @@ #define MAX_IDLE_TIME (60 * 10) /* 10 minutes of no activity */ /* Global Structures used in the program */ -extern struct config_s config; +extern struct config_s *config; extern unsigned int received_sighup; /* boolean */ extern int reload_config (void); diff --git a/src/reqs.c b/src/reqs.c index 3adc473a..310871cd 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -61,8 +61,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) @@ -373,7 +373,7 @@ static struct request_s *process_request (struct conn_s *connptr, } #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 @@ -387,7 +387,7 @@ static struct request_s *process_request (struct conn_s *connptr, 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); @@ -420,7 +420,7 @@ static struct request_s *process_request (struct conn_s *connptr, /* 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", @@ -437,7 +437,7 @@ static struct request_s *process_request (struct conn_s *connptr, } else { #ifdef TRANSPARENT_PROXY if (!do_transparent_proxy - (connptr, hashofheaders, request, &config, &url)) { + (connptr, hashofheaders, request, config, &url)) { goto fail; } #else @@ -455,8 +455,8 @@ static struct request_s *process_request (struct conn_s *connptr, /* * Filter restricted domains/urls */ - if (config.filter) { - if (config.filter_url) + if (config->filter) { + if (config->filter_url) ret = filter_url (url); else ret = filter_domain (request->host); @@ -464,7 +464,7 @@ static struct request_s *process_request (struct conn_s *connptr, 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); @@ -486,7 +486,7 @@ static struct request_s *process_request (struct conn_s *connptr, /* * 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; @@ -804,13 +804,13 @@ write_via_header (int fd, hashmap_t hashofheaders, 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); } @@ -938,7 +938,7 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) } } #if defined(XTINYPROXY_ENABLE) - if (config.add_xtinyproxy) + if (config->add_xtinyproxy) add_xtinyproxy_header (connptr); #endif @@ -981,7 +981,7 @@ static int process_server_headers (struct conn_s *connptr) 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. */ @@ -1073,7 +1073,7 @@ static int process_server_headers (struct conn_s *connptr) #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); @@ -1082,7 +1082,7 @@ static int process_server_headers (struct conn_s *connptr) } /* Rewrite the HTTP redirect if needed */ - if (config.reversebaseurl && + if (config->reversebaseurl && hashmap_entry_by_key (hashofheaders, "location", (void **) &header) > 0) { @@ -1100,14 +1100,14 @@ static int process_server_headers (struct conn_s *connptr) 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"); } @@ -1181,7 +1181,7 @@ static void relay_connection (struct conn_s *connptr) FD_ZERO (&wset); tv.tv_sec = - config.idletimeout - difftime (time (NULL), last_access); + config->idletimeout - difftime (time (NULL), last_access); tv.tv_usec = 0; if (buffer_size (connptr->sbuffer) > 0) @@ -1197,10 +1197,10 @@ static void relay_connection (struct conn_s *connptr) if (ret == 0) { tdiff = difftime (time (NULL), last_access); - if (tdiff > config.idletimeout) { + if (tdiff > config->idletimeout) { log_message (LOG_INFO, "Idle Timeout (after select) as %g > %u.", - tdiff, config.idletimeout); + tdiff, config->idletimeout); return; } else { continue; @@ -1546,16 +1546,16 @@ void handle_connection (int fd, union sockaddr_union* addr) 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 ? + 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, - config.bindsame ? sock_ipaddr : NULL); + config->bindsame ? sock_ipaddr : NULL); if (!connptr) { close (fd); return; @@ -1575,7 +1575,7 @@ void handle_connection (int fd, union sockaddr_union* addr) } - if (check_acl (peer_ipaddr, addr, config.access_list) <= 0) { + if (check_acl (peer_ipaddr, addr, config->access_list) <= 0) { update_stats (STAT_DENIED); indicate_http_error (connptr, 403, "Access denied", "detail", @@ -1622,17 +1622,17 @@ void handle_connection (int fd, union sockaddr_union* addr) goto fail; } - if (config.basicauth_list != NULL) { + if (config->basicauth_list != NULL) { ssize_t len; char *authstring; int failure = 1, stathost_connect = 0; len = hashmap_entry_by_key (hashofheaders, "proxy-authorization", (void **) &authstring); - if (len == 0 && config.stathost) { + if (len == 0 && config->stathost) { len = hashmap_entry_by_key (hashofheaders, "host", (void **) &authstring); - if (len && !strncmp(authstring, config.stathost, strlen(config.stathost))) { + if (len && !strncmp(authstring, config->stathost, strlen(config->stathost))) { len = hashmap_entry_by_key (hashofheaders, "authorization", (void **) &authstring); stathost_connect = 1; @@ -1651,7 +1651,7 @@ void handle_connection (int fd, union sockaddr_union* addr) 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: @@ -1670,9 +1670,9 @@ void handle_connection (int fd, union sockaddr_union* addr) * Add any user-specified headers (AddHeader directive) to the * outgoing HTTP request. */ - for (i = 0; i < vector_length (config.add_headers); i++) { + for (i = 0; i < vector_length (config->add_headers); i++) { http_header_t *header = (http_header_t *) - vector_getentry (config.add_headers, i, NULL); + vector_getentry (config->add_headers, i, NULL); hashmap_insert (hashofheaders, header->name, diff --git a/src/reverse-proxy.c b/src/reverse-proxy.c index 02647874..32bd2a75 100644 --- a/src/reverse-proxy.c +++ b/src/reverse-proxy.c @@ -122,14 +122,14 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders, /* Reverse requests always start with a slash */ if (*url == '/') { /* First try locating the reverse mapping by request url */ - reverse = reversepath_get (url, config.reversepath_list); + reverse = reversepath_get (url, config->reversepath_list); if (reverse) { rewrite_url = (char *) safemalloc (strlen (url) + strlen (reverse->url) + 1); strcpy (rewrite_url, reverse->url); strcat (rewrite_url, url + strlen (reverse->path)); - } else if (config.reversemagic + } else if (config->reversemagic && hashmap_entry_by_key (hashofheaders, "cookie", (void **) &cookie) > 0) { @@ -139,7 +139,7 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders, && (reverse = reversepath_get (cookieval + strlen (REVERSE_COOKIE) + 1, - config.reversepath_list))) + config->reversepath_list))) { rewrite_url = (char *) safemalloc @@ -163,7 +163,7 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders, log_message (LOG_CONN, "Rewriting URL: %s -> %s", url, rewrite_url); /* Store reverse path so that the magical tracking cookie can be set */ - if (config.reversemagic && reverse) + if (config->reversemagic && reverse) connptr->reversepath = safestrdup (reverse->path); return rewrite_url; diff --git a/src/sock.c b/src/sock.c index 8513ba89..73ddebdc 100644 --- a/src/sock.c +++ b/src/sock.c @@ -134,8 +134,8 @@ int opensock (const char *host, int port, const char *bind_to) close (sockfd); continue; /* can't bind, so try again */ } - } else if (config.bind_address) { - if (bind_socket (sockfd, config.bind_address, + } else if (config->bind_address) { + if (bind_socket (sockfd, config->bind_address, res->ai_family) < 0) { close (sockfd); continue; /* can't bind, so try again */ @@ -147,7 +147,7 @@ int opensock (const char *host, int port, const char *bind_to) int af = res->ai_addr->sa_family; unsigned dport = ntohs(af == AF_INET ? p->v4.sin_port : p->v6.sin6_port); socklen_t slen = sizeof u; - if (dport == config.port) { + if (dport == config->port) { getsockname(sockfd, (void*)&u, &slen); loop_records_add(&u); } diff --git a/src/stats.c b/src/stats.c index 93a30cfb..9f4acc37 100644 --- a/src/stats.c +++ b/src/stats.c @@ -77,7 +77,7 @@ showstats (struct conn_s *connptr) pthread_mutex_lock(&stats_file_lock); - if (!config.statpage || (!(statfile = fopen (config.statpage, "r")))) { + if (!config->statpage || (!(statfile = fopen (config->statpage, "r")))) { message_buffer = (char *) safemalloc (MAXBUFFSIZE); if (!message_buffer) { err_minus_one: From 27d96df99900c5a62ab0fdf2a37565e78f256d6a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 16:35:43 +0000 Subject: [PATCH 052/265] remove duplicate code calling reload_config_file() as a side effect of not updating the config pointer when loading the config file fails, the "FIXME" level comment to take appropriate action in that case has been removed. the only issue remaining when receiving a SIGHUP and encountering a malformed config file would now be the case that output to syslog/logfile won't be resumed, if initially so configured. --- src/child.c | 7 ++----- src/main.c | 12 +++++------- src/main.h | 2 +- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/child.c b/src/child.c index 4a94b98e..9ebba022 100644 --- a/src/child.c +++ b/src/child.c @@ -114,11 +114,8 @@ void child_main_loop (void) /* Handle log rotation if it was requested */ if (received_sighup) { - /* - * Ignore the return value of reload_config for now. - * This should actually be handled somehow... - */ - reload_config (); + + reload_config (1); #ifdef FILTER_ENABLE filter_reload (); diff --git a/src/main.c b/src/main.c index 4a5f8ea4..71b6f91c 100644 --- a/src/main.c +++ b/src/main.c @@ -244,21 +244,22 @@ change_user (const char *program) * convenience wrapper around reload_config_file * that also re-initializes logging. */ -int reload_config (void) +int reload_config (int reload_logging) { int ret; - shutdown_logging (); + if (reload_logging) shutdown_logging (); ret = reload_config_file (config_file, &config_main, &config_defaults); + if (ret != 0) { goto done; } config = &config_main; - ret = setup_logging (); + if (reload_logging) ret = setup_logging (); done: return ret; @@ -310,12 +311,9 @@ main (int argc, char **argv) initialize_config_defaults (&config_defaults); - if (reload_config_file (config_file, - &config_main, - &config_defaults)) { + if (reload_config(0)) { exit (EX_SOFTWARE); } - config = &config_main; init_stats (); diff --git a/src/main.h b/src/main.h index 5890d4a1..d19a2f6d 100644 --- a/src/main.h +++ b/src/main.h @@ -32,6 +32,6 @@ extern struct config_s *config; extern unsigned int received_sighup; /* boolean */ -extern int reload_config (void); +extern int reload_config (int reload_logging); #endif /* __MAIN_H__ */ From 5dd514af93675602cdece2b7b2112989b87d5d51 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 16:57:00 +0000 Subject: [PATCH 053/265] conf: fix loading of default values previously, default values were stored once into a static struct, then on each reload item by item copied manually into a "new" config struct. this has proven to be errorprone, as additions in one of the 2 locations were not propagated to the second one, apart from being simply a lot of gratuitous code. we now simply load the default values directly into the config struct to be used on each reload. closes #283 --- src/conf.c | 104 ++--------------------------------------------------- src/conf.h | 5 +-- src/main.c | 5 +-- 3 files changed, 5 insertions(+), 109 deletions(-) diff --git a/src/conf.c b/src/conf.c index c9a499e6..69a4c852 100644 --- a/src/conf.c +++ b/src/conf.c @@ -441,7 +441,7 @@ static int load_config_file (const char *config_fname, struct config_s *conf) return ret; } -void initialize_config_defaults (struct config_s *conf) +static void initialize_config_defaults (struct config_s *conf) { memset (conf, 0, sizeof(*conf)); @@ -457,108 +457,10 @@ void initialize_config_defaults (struct config_s *conf) conf->maxclients = 100; } -static void initialize_with_defaults (struct config_s *conf, - struct config_s *defaults) -{ - if (defaults->logf_name) { - conf->logf_name = safestrdup (defaults->logf_name); - } - - conf->syslog = defaults->syslog; - conf->port = defaults->port; - - if (defaults->stathost) { - conf->stathost = safestrdup (defaults->stathost); - } - - conf->quit = defaults->quit; - - if (defaults->user) { - conf->user = safestrdup (defaults->user); - } - - if (defaults->group) { - conf->group = safestrdup (defaults->group); - } - - if (defaults->listen_addrs) { - ssize_t i; - - conf->listen_addrs = vector_create(); - for (i=0; i < vector_length(defaults->listen_addrs); i++) { - char *addr; - size_t size; - addr = (char *)vector_getentry(defaults->listen_addrs, - i, &size); - vector_append(conf->listen_addrs, addr, size); - } - - } - -#ifdef FILTER_ENABLE - if (defaults->filter) { - conf->filter = safestrdup (defaults->filter); - } - - conf->filter_url = defaults->filter_url; - conf->filter_extended = defaults->filter_extended; - conf->filter_casesensitive = defaults->filter_casesensitive; -#endif /* FILTER_ENABLE */ - -#ifdef XTINYPROXY_ENABLE - conf->add_xtinyproxy = defaults->add_xtinyproxy; -#endif - -#ifdef REVERSE_SUPPORT - /* struct reversepath *reversepath_list; */ - conf->reverseonly = defaults->reverseonly; - conf->reversemagic = defaults->reversemagic; - - if (defaults->reversebaseurl) { - conf->reversebaseurl = safestrdup (defaults->reversebaseurl); - } -#endif - -#ifdef UPSTREAM_SUPPORT - /* struct upstream *upstream_list; */ -#endif /* UPSTREAM_SUPPORT */ - - if (defaults->pidpath) { - conf->pidpath = safestrdup (defaults->pidpath); - } - - conf->idletimeout = defaults->idletimeout; - - if (defaults->bind_address) { - conf->bind_address = safestrdup (defaults->bind_address); - } - - conf->bindsame = defaults->bindsame; - - if (defaults->via_proxy_name) { - conf->via_proxy_name = safestrdup (defaults->via_proxy_name); - } - - conf->disable_viaheader = defaults->disable_viaheader; - - if (defaults->errorpage_undef) { - conf->errorpage_undef = safestrdup (defaults->errorpage_undef); - } - - if (defaults->statpage) { - conf->statpage = safestrdup (defaults->statpage); - } - - /* vector_t access_list; */ - /* vector_t connect_ports; */ - /* hashmap_t anonymous_map; */ -} - /** * Load the configuration. */ -int reload_config_file (const char *config_fname, struct config_s *conf, - struct config_s *defaults) +int reload_config_file (const char *config_fname, struct config_s *conf) { int ret; @@ -566,7 +468,7 @@ int reload_config_file (const char *config_fname, struct config_s *conf, free_config (conf); - initialize_with_defaults (conf, defaults); + initialize_config_defaults (conf); ret = load_config_file (config_fname, conf); if (ret != 0) { diff --git a/src/conf.h b/src/conf.h index 9e10898f..085169dd 100644 --- a/src/conf.h +++ b/src/conf.h @@ -112,10 +112,7 @@ struct config_s { vector_t add_headers; }; -void initialize_config_defaults (struct config_s *conf); - -extern int reload_config_file (const char *config_fname, struct config_s *conf, - struct config_s *defaults); +extern int reload_config_file (const char *config_fname, struct config_s *conf); int config_compile_regex (void); diff --git a/src/main.c b/src/main.c index 71b6f91c..8a3b6fa1 100644 --- a/src/main.c +++ b/src/main.c @@ -250,8 +250,7 @@ int reload_config (int reload_logging) if (reload_logging) shutdown_logging (); - ret = reload_config_file (config_file, &config_main, - &config_defaults); + ret = reload_config_file (config_file, &config_main); if (ret != 0) { goto done; @@ -309,8 +308,6 @@ main (int argc, char **argv) } } - initialize_config_defaults (&config_defaults); - if (reload_config(0)) { exit (EX_SOFTWARE); } From 2e02dce0c3de4a231f74b44c34647406de507768 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jan 2020 17:02:22 +0000 Subject: [PATCH 054/265] conf: use 2 swappable conf slots, so old config can stay valid ... in case reloading of it after SIGHUP fails, the old config can continue working. (apart from the logging-related issue mentioned in 27d96df99900c5a62ab0fdf2a37565e78f256d6a ) --- src/main.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main.c b/src/main.c index 8a3b6fa1..29639577 100644 --- a/src/main.c +++ b/src/main.c @@ -48,11 +48,17 @@ * Global Structures */ struct config_s *config; -static struct config_s config_main; -static struct config_s config_defaults; +static struct config_s configs[2]; static const char* config_file; unsigned int received_sighup = FALSE; /* boolean */ +static struct config_s* +get_next_config(void) +{ + if (config == &configs[0]) return &configs[1]; + return &configs[0]; +} + /* * Handle a signal */ @@ -247,16 +253,17 @@ change_user (const char *program) int reload_config (int reload_logging) { int ret; + struct config_s *c_next = get_next_config(); if (reload_logging) shutdown_logging (); - ret = reload_config_file (config_file, &config_main); + ret = reload_config_file (config_file, c_next); if (ret != 0) { goto done; } - config = &config_main; + config = c_next; if (reload_logging) ret = setup_logging (); From 3230ce0bc2b7d5c1379c358f4e69346d6ed43429 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 16 Mar 2020 13:19:39 +0000 Subject: [PATCH 055/265] anonymous: fix segfault loading config item unlike other functions called from the config parser code, anonymous_insert() accesses the global config variable rather than passing it as an argument. however the global variable is only set after successful loading of the entire config. we fix this by adding a conf argument to each anonymous_* function, passing the global pointer in calls done from outside the config parser. fixes #292 --- src/anonymous.c | 22 +++++++++++----------- src/anonymous.h | 6 +++--- src/conf.c | 2 +- src/main.c | 6 +++--- src/reqs.c | 4 ++-- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/anonymous.c b/src/anonymous.c index 8d444652..f38fd441 100644 --- a/src/anonymous.c +++ b/src/anonymous.c @@ -28,21 +28,21 @@ #include "log.h" #include "conf.h" -short int is_anonymous_enabled (void) +short int is_anonymous_enabled (struct config_s *conf) { - return (config->anonymous_map != NULL) ? 1 : 0; + return (conf->anonymous_map != NULL) ? 1 : 0; } /* * Search for the header. This function returns a positive value greater than * zero if the string was found, zero if it wasn't and negative upon error. */ -int anonymous_search (const char *s) +int anonymous_search (struct config_s *conf, const char *s) { assert (s != NULL); - assert (config->anonymous_map != NULL); + assert (conf->anonymous_map != NULL); - return hashmap_search (config->anonymous_map, s); + return hashmap_search (conf->anonymous_map, s); } /* @@ -51,23 +51,23 @@ int anonymous_search (const char *s) * Return -1 if there is an error, otherwise a 0 is returned if the insert was * successful. */ -int anonymous_insert (const char *s) +int anonymous_insert (struct config_s *conf, const char *s) { char data = 1; assert (s != NULL); - if (!config->anonymous_map) { - config->anonymous_map = hashmap_create (32); - if (!config->anonymous_map) + if (!conf->anonymous_map) { + conf->anonymous_map = hashmap_create (32); + if (!conf->anonymous_map) return -1; } - if (hashmap_search (config->anonymous_map, s) > 0) { + if (hashmap_search (conf->anonymous_map, s) > 0) { /* The key was already found, so return a positive number. */ return 0; } /* Insert the new key */ - return hashmap_insert (config->anonymous_map, s, &data, sizeof (data)); + return hashmap_insert (conf->anonymous_map, s, &data, sizeof (data)); } diff --git a/src/anonymous.h b/src/anonymous.h index 0ca980e1..6fc45186 100644 --- a/src/anonymous.h +++ b/src/anonymous.h @@ -21,8 +21,8 @@ #ifndef _TINYPROXY_ANONYMOUS_H_ #define _TINYPROXY_ANONYMOUS_H_ -extern short int is_anonymous_enabled (void); -extern int anonymous_search (const char *s); -extern int anonymous_insert (const char *s); +extern short int is_anonymous_enabled (struct config_s *conf); +extern int anonymous_search (struct config_s *conf, const char *s); +extern int anonymous_insert (struct config_s *conf, const char *s); #endif diff --git a/src/conf.c b/src/conf.c index 69a4c852..9dd6da26 100644 --- a/src/conf.c +++ b/src/conf.c @@ -624,7 +624,7 @@ static HANDLE_FUNC (handle_anonymous) if (!arg) return -1; - anonymous_insert (arg); + anonymous_insert (conf, arg); safefree (arg); return 0; } diff --git a/src/main.c b/src/main.c index 29639577..fc5ad457 100644 --- a/src/main.c +++ b/src/main.c @@ -325,9 +325,9 @@ main (int argc, char **argv) * in the list of allowed headers, since it is required in a * HTTP/1.0 request. Also add the Content-Type header since it * goes hand in hand with Content-Length. */ - if (is_anonymous_enabled ()) { - anonymous_insert ("Content-Length"); - anonymous_insert ("Content-Type"); + if (is_anonymous_enabled (config)) { + anonymous_insert (config, "Content-Length"); + anonymous_insert (config, "Content-Type"); } if (daemonized == TRUE) { diff --git a/src/reqs.c b/src/reqs.c index 310871cd..041fb03f 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -919,8 +919,8 @@ process_client_headers (struct conn_s *connptr, hashmap_t hashofheaders) hashmap_return_entry (hashofheaders, iter, &data, (void **) &header); - if (!is_anonymous_enabled () - || anonymous_search (data) > 0) { + if (!is_anonymous_enabled (config) + || anonymous_search (config, data) > 0) { ret = write_message (connptr->server_fd, "%s: %s\r\n", data, header); From d98aabf47f43289f9e66230b3c70a9d682c7865c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 18 Mar 2020 12:31:13 +0000 Subject: [PATCH 056/265] transparent: fix invalid memory access getsockname() requires addrlen to be set to the size of the sockaddr struct passed as the addr, and a check whether the returned addrlen exceeds the initially passed size (to determine whether the address returned is truncated). with a request like "GET /\r\n\r\n" where length is 0 this caused the code to assume success and use the values of the uninitialized sockaddr struct. --- src/transparent-proxy.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index df5fbcee..727ef3e9 100644 --- a/src/transparent-proxy.c +++ b/src/transparent-proxy.c @@ -65,10 +65,11 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders, length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data); if (length <= 0) { struct sockaddr_in dest_addr; + length = sizeof(dest_addr); if (getsockname (connptr->client_fd, (struct sockaddr *) &dest_addr, - &length) < 0) { + &length) < 0 || length > sizeof(dest_addr)) { log_message (LOG_ERR, "process_request: cannot get destination IP for %d", connptr->client_fd); From db4bd162a3472205fb847a52948c1133e65bd9ce Mon Sep 17 00:00:00 2001 From: xiejianjun Date: Sat, 9 May 2020 11:45:30 +0800 Subject: [PATCH 057/265] fix check_acl compilation with --enable-debug regression introduced in f6d4da5d81694721bf50b2275621e7ce84e6da30. this has been overlooked due to the assert macro being optimized out in non-debug builds. --- src/acl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/acl.c b/src/acl.c index 7918a079..06c982b4 100644 --- a/src/acl.c +++ b/src/acl.c @@ -344,7 +344,7 @@ int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list) char string_addr[HOSTNAME_LENGTH]; assert (ip != NULL); - assert (host != NULL); + assert (addr != NULL); string_addr[0] = 0; From 25e2cc330c5f6b403d91a316304f92bae0047227 Mon Sep 17 00:00:00 2001 From: LucasVerneyDGE <58298410+LucasVerneyDGE@users.noreply.github.com> Date: Tue, 26 May 2020 11:47:12 +0200 Subject: [PATCH 058/265] add a comment example in the filter file example --- docs/filter-howto.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/filter-howto.txt b/docs/filter-howto.txt index 3acd813e..52eab208 100644 --- a/docs/filter-howto.txt +++ b/docs/filter-howto.txt @@ -34,6 +34,7 @@ start blocking some sites. Example "filter" file entries: +# This is a comment bannerads.zwire.com ad.doubleclick.net ads.fortunecity.com From 0b9a74c29036f9215b2b97a301b7b25933054302 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 15 Jul 2020 09:59:25 +0100 Subject: [PATCH 059/265] enforce socket timeout on new sockets via setsockopt() the timeout option set by the config file wasn't respected at all so it could happen that connections became stale and were never released, which eventually caused tinyproxy to hit the limit of open connections and never accepting new ones. addresses #274 --- src/reqs.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/reqs.c b/src/reqs.c index 041fb03f..859ac6b5 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1539,6 +1539,7 @@ void handle_connection (int fd, union sockaddr_union* addr) ssize_t i; struct conn_s *connptr; struct request_s *request = NULL; + struct timeval tv; hashmap_t hashofheaders = NULL; char sock_ipaddr[IP_LENGTH]; @@ -1561,6 +1562,13 @@ void handle_connection (int fd, union sockaddr_union* addr) return; } + 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", From c2d4114427421864f9152856c6e2b4b9ff6d83dd Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 11 Aug 2020 14:52:16 +0100 Subject: [PATCH 060/265] add support for asciidoctor to generate manpages asciidoctor is a modern replacement for asciidoc and much more lightweight, issuing "apt-get install asciidoc" on ubuntu 16.04 results in an attempt to install more than 1.3 GB of dependencies. --- configure.ac | 6 +++++- docs/man5/Makefile.am | 8 +++++++- docs/man8/Makefile.am | 8 +++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 6ddbcc0f..623d9a4c 100644 --- a/configure.ac +++ b/configure.ac @@ -181,6 +181,10 @@ AM_CONDITIONAL(HAVE_XSLTPROC, test "x$XSLTPROC" != "xno") AC_PATH_PROG(A2X, a2x, no) AM_CONDITIONAL(HAVE_A2X, test "x$A2X" != "xno") +# Check for asciidoctor +AC_PATH_PROG(ASCIIDOCTOR, asciidoctor, no) +AM_CONDITIONAL(HAVE_ASCIIDOCTOR, test "x$ASCIIDOCTOR" != "xno") + # checking xmllint AC_PATH_PROG(XMLLINT, xmllint, no) if test "x$XMLLINT" != "xno"; then @@ -221,7 +225,7 @@ AC_OUTPUT # runtime, so we need to touch them after config.status terminated to prevent # make from rebuild them. -if test "x$A2X" = "xno"; then +if test "x$A2X" = "xno" -a "x$HAVE_ASCIIDOCTOR" = "xno" ; then touch docs/man5/tinyproxy.conf.txt touch docs/man8/tinyproxy.txt if test -e docs/man5/tinyproxy.conf.5 ; then diff --git a/docs/man5/Makefile.am b/docs/man5/Makefile.am index 247b7ef3..48e5b03e 100644 --- a/docs/man5/Makefile.am +++ b/docs/man5/Makefile.am @@ -7,6 +7,8 @@ else A2X_ARGS = -d manpage -f manpage -L endif +ASCIIDOCTOR_ARGS = -b manpage + man_MANS = \ $(MAN5_FILES:.txt=.5) @@ -14,7 +16,11 @@ man_MANS = \ if HAVE_A2X $(AM_V_GEN) $(A2X) $(A2X_ARGS) $< else - @echo "*** a2x (asciidoc) is required to regenerate $(@) ***"; exit 1; +if HAVE_ASCIIDOCTOR + $(AM_V_GEN) $(ASCIIDOCTOR) $(ASCIIDOCTOR_ARGS) $< +else + @echo "*** a2x (asciidoc) or asciidoctor is required to regenerate $(@) ***"; exit 1; +endif endif CLEANFILES = \ diff --git a/docs/man8/Makefile.am b/docs/man8/Makefile.am index 1bd0ffa7..5e2f3904 100644 --- a/docs/man8/Makefile.am +++ b/docs/man8/Makefile.am @@ -7,6 +7,8 @@ else A2X_ARGS = -d manpage -f manpage -L endif +ASCIIDOCTOR_ARGS = -b manpage + man_MANS = \ $(MAN8_FILES:.txt=.8) @@ -14,7 +16,11 @@ man_MANS = \ if HAVE_A2X $(AM_V_GEN) $(A2X) $(A2X_ARGS) $< else - @echo "*** a2x (asciidoc) is required to regenerate $(@) ***"; exit 1; +if HAVE_ASCIIDOCTOR + $(AM_V_GEN) $(ASCIIDOCTOR) $(ASCIIDOCTOR_ARGS) $< +else + @echo "*** a2x (asciidoc) or asciidoctor is required to regenerate $(@) ***"; exit 1; +endif endif CLEANFILES = \ From d9953d795dd1b66a583303dff3b11c1b3f24ba09 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 11 Aug 2020 15:49:18 +0100 Subject: [PATCH 061/265] travis: run make clean before second compile otherwise object files will not be rebuilt with the new configure options. this will prevent cases like db4bd162a3472205fb847a52948c1133e65bd9ce where it turned out there was a build error with --enable-debug since several git revisions. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c38a1d21..ab26ad1d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ script: - ./configure - make - make test +- make clean - ./configure --enable-debug --enable-transparent --enable-reverse - make - make test From 8ceebbf0c6996fa8a41da8c646294b6bd4c24c9f Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 11 Aug 2020 17:50:07 +0100 Subject: [PATCH 062/265] configure.ac: fix typo checking for asciidoctor in release --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 623d9a4c..850eca5d 100644 --- a/configure.ac +++ b/configure.ac @@ -225,7 +225,7 @@ AC_OUTPUT # runtime, so we need to touch them after config.status terminated to prevent # make from rebuild them. -if test "x$A2X" = "xno" -a "x$HAVE_ASCIIDOCTOR" = "xno" ; then +if test "x$A2X" = "xno" -a "x$ASCIIDOCTOR" = "xno" ; then touch docs/man5/tinyproxy.conf.txt touch docs/man8/tinyproxy.txt if test -e docs/man5/tinyproxy.conf.5 ; then From 77853caa0ab043cb80d9d69864141f800502bb4a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 11 Aug 2020 16:48:57 +0000 Subject: [PATCH 063/265] move manpages to maintainer-clean make target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit according to https://www.gnu.org/prep/standards/html_node/Standard-Targets.html#Standard-Targets `maintainer-clean` is the proper make target for files that are distributed in a release tarball: > The ‘maintainer-clean’ target is intended to be used by a maintainer of the > package, not by ordinary users. > You may need special tools to reconstruct some of the files that > ‘make maintainer-clean’ deletes. this prevents users without a2x or asciidoctor from losing their ability to recompile tinyproxy after `make clean`, but it also means that users wanting to regenerate the documentation need to run `make maintainer-clean`. --- docs/man5/Makefile.am | 4 +++- docs/man8/Makefile.am | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/man5/Makefile.am b/docs/man5/Makefile.am index 48e5b03e..0428e51b 100644 --- a/docs/man5/Makefile.am +++ b/docs/man5/Makefile.am @@ -23,8 +23,10 @@ else endif endif +MAINTAINERCLEANFILES = \ + $(MAN5_FILES:.txt=.5) + CLEANFILES = \ - $(MAN5_FILES:.txt=.5) \ $(MAN5_FILES:.txt=.xml) EXTRA_DIST = \ diff --git a/docs/man8/Makefile.am b/docs/man8/Makefile.am index 5e2f3904..bf2c09c0 100644 --- a/docs/man8/Makefile.am +++ b/docs/man8/Makefile.am @@ -23,8 +23,10 @@ else endif endif +MAINTAINERCLEANFILES = \ + $(MAN8_FILES:.txt=.8) + CLEANFILES = \ - $(MAN8_FILES:.txt=.8) \ $(MAN8_FILES:.txt=.xml) EXTRA_DIST= \ From 8a251023b23ada42ae978c5bd707b06a356b6f8e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 11 Aug 2020 19:51:45 +0100 Subject: [PATCH 064/265] travis: remove make distcheck distcheck chokes on man5/8 files still in the file tree, while the input files (.txt) are not. these are generated by the configure script and it would require quite some effort to get this test working. as it is non-essential, we simply disable it. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ab26ad1d..10666607 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,4 +16,3 @@ script: - make - make test - make valgrind-test -- make distcheck From e1d36d3f74f02b74429d92dd9af6fcab05038dad Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 11 Aug 2020 23:35:50 +0100 Subject: [PATCH 065/265] configure.ac: remove obsolete check for xsltproc xsltproc was once[1] used to generate AUTHORS from xml input, but fortunately this is no longer the case. [1]: in a time when everybody thought XML would be a Good Idea (TM) --- configure.ac | 4 ---- 1 file changed, 4 deletions(-) diff --git a/configure.ac b/configure.ac index 850eca5d..9623754f 100644 --- a/configure.ac +++ b/configure.ac @@ -173,10 +173,6 @@ AC_SUBST(CPPFLAGS) AC_SUBST(LIBS) AC_SUBST(ADDITIONAL_OBJECTS) -# Check for xml tools -AC_PATH_PROG(XSLTPROC, xsltproc, no) -AM_CONDITIONAL(HAVE_XSLTPROC, test "x$XSLTPROC" != "xno") - # Check for asciidoc AC_PATH_PROG(A2X, a2x, no) AM_CONDITIONAL(HAVE_A2X, test "x$A2X" != "xno") From 5ba958829f73ecc02658a46f5b1bba5ffed2281d Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 12 Aug 2020 00:15:45 +0100 Subject: [PATCH 066/265] add configure option to disable manpage generation using --disable-manpage-support it's finally possibly to disable the formerly obligatory use of a2x to generate the manpage documentation. this is the final solution to the decade old problem that users need to install the enormous asciidoc package to compile TINYproxy from source, or otherwise get a build error, even though the vast majority is only interested in the program itself. solution was inspired by PR #179. closes #179 closes #111 note that since 1.10.0 release the generated release tarball includes the generated manpages too; in which case neither the use of a2x nor --disable-manpage-support is required. --- configure.ac | 33 +++++++++++++++++++++++++++------ docs/man5/Makefile.am | 2 ++ docs/man8/Makefile.am | 2 ++ 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 9623754f..e645dc11 100644 --- a/configure.ac +++ b/configure.ac @@ -96,6 +96,15 @@ if test x"$transparent_enabled" = x"yes"; then AC_DEFINE(TRANSPARENT_PROXY) fi +dnl Let user decide whether he wants support for manpages +dnl Which require either a2x/asciidoctor or a tarball release +AH_TEMPLATE([MANPAGE_SUPPORT], + [Build manpages with a2x/asciidoctor if they are missing from the distribution.]) +TP_ARG_ENABLE(manpage_support, + [Enable support for building manpages (default is YES)], + yes) +AM_CONDITIONAL(HAVE_MANPAGE_INTEREST, test x"$manpage_support_enabled" = x"yes") + # This is required to build test programs below AC_PROG_CC @@ -173,15 +182,14 @@ AC_SUBST(CPPFLAGS) AC_SUBST(LIBS) AC_SUBST(ADDITIONAL_OBJECTS) +if test x"$manpage_support_enabled" = x"yes"; then # Check for asciidoc AC_PATH_PROG(A2X, a2x, no) -AM_CONDITIONAL(HAVE_A2X, test "x$A2X" != "xno") - +if test "x$A2X" = "xno" ; then # Check for asciidoctor AC_PATH_PROG(ASCIIDOCTOR, asciidoctor, no) -AM_CONDITIONAL(HAVE_ASCIIDOCTOR, test "x$ASCIIDOCTOR" != "xno") - -# checking xmllint +else +# checking xmllint, which is only used together with a2x AC_PATH_PROG(XMLLINT, xmllint, no) if test "x$XMLLINT" != "xno"; then AS_ECHO_N("testing xmllint... ") @@ -194,7 +202,20 @@ if test "x$XMLLINT" != "xno"; then fi rm -f conftest.txt conftest.xml fi -AM_CONDITIONAL(HAVE_XMLLINT, test "x$XMLLINT" != "xno") +fi #a2x installed + +if test "x$A2X" = "xno" -a "x$ASCIIDOCTOR" = "xno" && \ + ! test -e docs/man5/tinyproxy.conf.5 -a -e docs/man8/tinyproxy.8 ; then +AC_MSG_ERROR([ + manpage generation requested, but neither a2x, asciidoctor + nor pre-generated manpages found. + Use --disable-manpage-support if you want to compile anyway.]) +fi +fi #manpage_support_enabled + +AM_CONDITIONAL(HAVE_A2X, test "x$A2X" != "x" -a "x$A2X" != "xno") +AM_CONDITIONAL(HAVE_XMLLINT, test "x$XMLLINT" != "x" -a "x$XMLLINT" != "xno") +AM_CONDITIONAL(HAVE_ASCIIDOCTOR, test "x$ASCIIDOCTOR" != "x" -a "x$ASCIIDOCTOR" != "xno") AC_CONFIG_FILES([ Makefile diff --git a/docs/man5/Makefile.am b/docs/man5/Makefile.am index 0428e51b..3447ad2f 100644 --- a/docs/man5/Makefile.am +++ b/docs/man5/Makefile.am @@ -1,5 +1,7 @@ +if HAVE_MANPAGE_INTEREST MAN5_FILES = \ tinyproxy.conf.txt +endif if HAVE_XMLLINT A2X_ARGS = -d manpage -f manpage diff --git a/docs/man8/Makefile.am b/docs/man8/Makefile.am index bf2c09c0..0d1eda13 100644 --- a/docs/man8/Makefile.am +++ b/docs/man8/Makefile.am @@ -1,5 +1,7 @@ +if HAVE_MANPAGE_INTEREST MAN8_FILES = \ tinyproxy.txt +endif if HAVE_XMLLINT A2X_ARGS = -d manpage -f manpage From 335477b16e8926a51d35d94789906397524eb7bb Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 19 Aug 2020 12:01:20 +0100 Subject: [PATCH 067/265] upstream: allow port 0 to be specified this is useful to use upstream directive to null-route a specific target domain. e.g. upstream http 0.0.0.0:0 ".adserver.com" --- src/upstream.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/upstream.c b/src/upstream.c index 327b727d..321b2824 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -123,7 +123,7 @@ static struct upstream *upstream_build (const char *host, int port, const char * log_message (LOG_INFO, "Added no-upstream for %s", domain); } else { - if (!host || host[0] == '\0' || port < 1 || !domain + if (!host || host[0] == '\0' || !domain || domain[0] == '\0') { log_message (LOG_WARNING, "Nonsense upstream rule: invalid parameters"); @@ -234,7 +234,7 @@ struct upstream *upstream_get (char *host, struct upstream *up) up = up->next; } - if (up && (!up->host || !up->port)) + if (up && (!up->host)) up = NULL; if (up) From c984122acd0850216f6fe18f80065c0573aafffa Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 19 Aug 2020 12:07:19 +0100 Subject: [PATCH 068/265] tinyproxy.conf.5: clarify that upstream none makes direct connection --- docs/man5/tinyproxy.conf.txt.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 3e24852d..c1cce096 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -163,7 +163,7 @@ The possible keywords and their descriptions are as follows: `type` can be one of `http`, `socks4`, `socks5`, `none`. * 'upstream none "site_spec"' turns off upstream support for sites - matching `site_spec`. + matching `site_spec`, that means the direction is done directly. The site can be specified in various forms as a hostname, domain name or as an IP range: From fc681e26b8334a329376c80de51057a79787bcf4 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 19 Aug 2020 12:16:02 +0100 Subject: [PATCH 069/265] tinyproxy.conf.5: document upstream null-routing --- docs/man5/tinyproxy.conf.txt.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index c1cce096..cf6555ba 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -174,6 +174,10 @@ The possible keywords and their descriptions are as follows: * 'IP/bits' matches network/mask * 'IP/mask' matches network/mask + Note that the upstream directive can also be used to null-route + a specific target domain/host, e.g.: + `upstream http 0.0.0.0:0 ".adserver.com"` + *MaxClients*:: Tinyproxy creates one thread for each connected client. From 3fa53f866019aa5794a3a67e2e476c26918ac4df Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 19 Aug 2020 20:37:03 +0100 Subject: [PATCH 070/265] tinyproxy.conf.5: fix typo --- docs/man5/tinyproxy.conf.txt.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index cf6555ba..cf8fab53 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -163,7 +163,7 @@ The possible keywords and their descriptions are as follows: `type` can be one of `http`, `socks4`, `socks5`, `none`. * 'upstream none "site_spec"' turns off upstream support for sites - matching `site_spec`, that means the direction is done directly. + matching `site_spec`, that means the connection is done directly. The site can be specified in various forms as a hostname, domain name or as an IP range: From a547a298c77c331061511a6befffc346c1a44a05 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 19 Aug 2020 22:33:59 +0100 Subject: [PATCH 071/265] generate manpages with pod2man instead of a2x/asciidoc(tor) it turned out that the upstream section in tinyproxy.conf.5 wasn't rendered properly, because in asciidoc items following a list item are always explicitly appended to the last list item. after several hours of finding a workaround, it was decided to change the manpage generator to pod2man instead. as pod2man ships together with any perl base install, it should be available on almost every UNIX system, unlike asciidoc which requires installation of a huge set of dependencies (more than 1.3 GB on Ubuntu 16.04), and the replacement asciidoctor requires a ruby installation plus a "gem" (which is by far better than asciidoc, but still more effort than using the already available pod2man). tinyproxy's hard requirement of a2x (asciidoctor) for building from source caused rivers of tears (and dozens of support emails/issues) in the past, but finally we get rid of it. a tool such as a2x with its XML based bloat- technology isn't really suited to go along with a supposedly lightweight C program. if it ever turns out that even pod2man is too heavy a dependency, we could still write our own replacement in less than 50 lines of awk, as the pod syntax is very low level and easy to parse. --- configure.ac | 37 +-- docs/man5/Makefile.am | 24 +- docs/man5/tinyproxy.conf.txt.in | 540 ++++++++++++++++---------------- docs/man8/Makefile.am | 28 +- docs/man8/tinyproxy.txt.in | 157 ++++++---- 5 files changed, 389 insertions(+), 397 deletions(-) diff --git a/configure.ac b/configure.ac index e645dc11..900af95d 100644 --- a/configure.ac +++ b/configure.ac @@ -97,9 +97,9 @@ if test x"$transparent_enabled" = x"yes"; then fi dnl Let user decide whether he wants support for manpages -dnl Which require either a2x/asciidoctor or a tarball release +dnl Which require either pod2man or a tarball release AH_TEMPLATE([MANPAGE_SUPPORT], - [Build manpages with a2x/asciidoctor if they are missing from the distribution.]) + [Build manpages with pod2man if they are missing from the distribution.]) TP_ARG_ENABLE(manpage_support, [Enable support for building manpages (default is YES)], yes) @@ -183,39 +183,18 @@ AC_SUBST(LIBS) AC_SUBST(ADDITIONAL_OBJECTS) if test x"$manpage_support_enabled" = x"yes"; then -# Check for asciidoc -AC_PATH_PROG(A2X, a2x, no) -if test "x$A2X" = "xno" ; then -# Check for asciidoctor -AC_PATH_PROG(ASCIIDOCTOR, asciidoctor, no) -else -# checking xmllint, which is only used together with a2x -AC_PATH_PROG(XMLLINT, xmllint, no) -if test "x$XMLLINT" != "xno"; then - AS_ECHO_N("testing xmllint... ") - echo "TEST" > conftest.txt - if $A2X -f docbook conftest.txt 2>/dev/null; then - AS_ECHO("ok") - else - AS_ECHO("failed") - XMLLINT="no" - fi - rm -f conftest.txt conftest.xml -fi -fi #a2x installed +AC_PATH_PROG(POD2MAN, pod2man, no) -if test "x$A2X" = "xno" -a "x$ASCIIDOCTOR" = "xno" && \ +if test "x$POD2MAN" = "xno" && \ ! test -e docs/man5/tinyproxy.conf.5 -a -e docs/man8/tinyproxy.8 ; then AC_MSG_ERROR([ - manpage generation requested, but neither a2x, asciidoctor + manpage generation requested, but neither pod2man nor pre-generated manpages found. Use --disable-manpage-support if you want to compile anyway.]) fi fi #manpage_support_enabled -AM_CONDITIONAL(HAVE_A2X, test "x$A2X" != "x" -a "x$A2X" != "xno") -AM_CONDITIONAL(HAVE_XMLLINT, test "x$XMLLINT" != "x" -a "x$XMLLINT" != "xno") -AM_CONDITIONAL(HAVE_ASCIIDOCTOR, test "x$ASCIIDOCTOR" != "x" -a "x$ASCIIDOCTOR" != "xno") +AM_CONDITIONAL(HAVE_POD2MAN, test "x$POD2MAN" != "x" -a "x$POD2MAN" != "xno") AC_CONFIG_FILES([ Makefile @@ -237,12 +216,12 @@ scripts/Makefile AC_OUTPUT # the manpages are shipped in the release tarball and we don't want them to -# get regenerated if a2x is not available. the intermediate files from +# get regenerated if pod2man is not available. the intermediate files from # AC_CONFIG_FILES are created with config.status, which is created at configure # runtime, so we need to touch them after config.status terminated to prevent # make from rebuild them. -if test "x$A2X" = "xno" -a "x$ASCIIDOCTOR" = "xno" ; then +if test "x$POD2MAN" = "xno" ; then touch docs/man5/tinyproxy.conf.txt touch docs/man8/tinyproxy.txt if test -e docs/man5/tinyproxy.conf.5 ; then diff --git a/docs/man5/Makefile.am b/docs/man5/Makefile.am index 3447ad2f..31b8ddfa 100644 --- a/docs/man5/Makefile.am +++ b/docs/man5/Makefile.am @@ -3,33 +3,23 @@ MAN5_FILES = \ tinyproxy.conf.txt endif -if HAVE_XMLLINT -A2X_ARGS = -d manpage -f manpage -else -A2X_ARGS = -d manpage -f manpage -L -endif - -ASCIIDOCTOR_ARGS = -b manpage +M_SECTION=5 +M_NAME=TINYPROXY.CONF man_MANS = \ $(MAN5_FILES:.txt=.5) .txt.5: -if HAVE_A2X - $(AM_V_GEN) $(A2X) $(A2X_ARGS) $< +if HAVE_POD2MAN + $(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \ + --section=$(M_SECTION) --name=$(M_NAME) --release="Version @VERSION@" \ + $< > $@ else -if HAVE_ASCIIDOCTOR - $(AM_V_GEN) $(ASCIIDOCTOR) $(ASCIIDOCTOR_ARGS) $< -else - @echo "*** a2x (asciidoc) or asciidoctor is required to regenerate $(@) ***"; exit 1; -endif + @echo "*** pod2man is required to regenerate $(@) ***"; exit 1; endif MAINTAINERCLEANFILES = \ $(MAN5_FILES:.txt=.5) -CLEANFILES = \ - $(MAN5_FILES:.txt=.xml) - EXTRA_DIST = \ $(MAN5_FILES:.txt=.5) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index cf8fab53..b5619dd0 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -1,24 +1,20 @@ -TINYPROXY.CONF(5) -================= -:man source: Version @VERSION@ -:man manual: Tinyproxy manual +=pod -NAME ----- +=encoding utf8 + +=head1 NAME tinyproxy.conf - Tinyproxy HTTP proxy daemon configuration file -SYNOPSIS --------- +=head1 SYNOPSIS -*tinyproxy.conf* +B -DESCRIPTION ------------ +=head1 DESCRIPTION -`tinyproxy(8)` reads its configuration file, typically stored in +L reads its configuration file, typically stored in `/etc/tinyproxy/tinyproxy.conf` (or passed to Tinyproxy with -c on the command line). This manpage describes the syntax and contents of the configuration file. @@ -31,322 +27,340 @@ contain spaces. The possible keywords and their descriptions are as follows: -*User*:: +=over 4 + +=item B + +The user which the Tinyproxy process should run as, after the +initial port-binding has been done as the `root` user. Either the +user name or the UID may be specified. + +=item B + +The group which the Tinyproxy process should run as, after the +initial port-binding has been done as the `root` user. Either the +group name or the GID may be specified. + +=item B + +The port which the Tinyproxy service will listen on. If the port is +less than 1024, you will need to start the Tinyproxy process as the +`root` user. + +=item B + +By default, Tinyproxy listens for connections on all available +interfaces (i.e. it listens on the wildcard address `0.0.0.0`). +With this configuration parameter, Tinyproxy can be told to listen +only on one specific address. + +=item B + +This allows you to specify which address Tinyproxy will bind +to for outgoing connections to web servers or upstream proxies. + +=item B + +If this boolean parameter is set to `yes`, then Tinyproxy will +bind the outgoing connection to the IP address of the incoming +connection that triggered the outgoing request. + +=item B + +The maximum number of seconds of inactivity a connection is +allowed to have before it is closed by Tinyproxy. + +=item B + +This parameter controls which HTML file Tinyproxy returns when a +given HTTP error occurs. It takes two arguments, the error number +and the location of the HTML error file. + +=item B + +This parameter controls the HTML template file returned when an +error occurs for which no specific error file has been set. + +=item B + +This configures the host name or IP address that is treated +as the `stat host`: Whenever a request for this host is received, +Tinyproxy will return an internal statistics page instead of +forwarding the request to that host. The template for this +page can be configured with the `StatFile` configuration option. +The default value of `StatHost` is `@TINYPROXY_STATHOST@`. + +=item B + +This configures the HTML file that Tinyproxy sends when +a request for the stathost is received. If this parameter is +not set, Tinyproxy returns a hard-coded basic statistics page. +See the STATHOST section in the L manual page +for details. + +Note that the StatFile and the error files configured with ErrorFile +and DefaultErrorFile are template files that can contain a few +template variables that Tinyproxy expands prior to delivery. +Examples are "{cause}" for an abbreviated error description and +"{detail}" for a detailed error message. The L +manual page contains a description of all template variables. + +=item B + +This controls the location of the file to which Tinyproxy +writes its debug output. Alternatively, Tinyproxy can log +to syslog -- see the Syslog option. - The user which the Tinyproxy process should run as, after the - initial port-binding has been done as the `root` user. Either the - user name or the UID may be specified. +=item B -*Group*:: +When set to `On`, this option tells Tinyproxy to write its +debug messages to syslog instead of to a log file configured +with `LogFile`. These two options are mutually exclusive. - The group which the Tinyproxy process should run as, after the - initial port-binding has been done as the `root` user. Either the - group name or the GID may be specified. +=item B -*Port*:: +Sets the log level. Messages from the set level and above are +logged. For example, if the LogLevel was set to Warning, then all +log messages from Warning to Critical would be output, but Notice +and below would be suppressed. Allowed values are: - The port which the Tinyproxy service will listen on. If the port is - less than 1024, you will need to start the Tinyproxy process as the - `root` user. +=over 4 -*Listen*:: +=item * Critical (least verbose) - By default, Tinyproxy listens for connections on all available - interfaces (i.e. it listens on the wildcard address `0.0.0.0`). - With this configuration parameter, Tinyproxy can be told to listen - only on one specific address. +=item * Error -*Bind*:: +=item * Warning - This allows you to specify which address Tinyproxy will bind - to for outgoing connections to web servers or upstream proxies. +=item * Notice -*BindSame*:: +=item * Connect (log connections without Info's noise) - If this boolean parameter is set to `yes`, then Tinyproxy will - bind the outgoing connection to the IP address of the incoming - connection that triggered the outgoing request. +=item * Info (most verbose) -*Timeout*:: +=back - The maximum number of seconds of inactivity a connection is - allowed to have before it is closed by Tinyproxy. +=item B -*ErrorFile*:: +This option controls the location of the file where the main +Tinyproxy process stores its process ID for signaling purposes. - This parameter controls which HTML file Tinyproxy returns when a - given HTTP error occurs. It takes two arguments, the error number - and the location of the HTML error file. +=item B -*DefaultErrorFile*:: +Setting this option to `Yes` tells Tinyproxy to add a header +`X-Tinyproxy` containing the client's IP address to the request. - This parameter controls the HTML template file returned when an - error occurs for which no specific error file has been set. +=item B -*StatHost*:: +This option allows you to set up a set of rules for deciding +whether an upstream proxy server is to be used, based on the +host or domain of the site being accessed. The rules are stored +in the order encountered in the configuration file and the +LAST matching rule wins. The following forms for specifying upstream +rules exist: - This configures the host name or IP address that is treated - as the `stat host`: Whenever a request for this host is received, - Tinyproxy will return an internal statistics page instead of - forwarding the request to that host. The template for this - page can be configured with the `StatFile` configuration option. - The default value of `StatHost` is `@TINYPROXY_STATHOST@`. +=over 4 -*StatFile*:: +=item * I turns proxy upstream support on generally. - This configures the HTML file that Tinyproxy sends when - a request for the stathost is received. If this parameter is - not set, Tinyproxy returns a hard-coded basic statistics page. - See the STATHOST section in the `tinyproxy(8)` manual page - for details. - + - Note that the StatFile and the error files configured with ErrorFile - and DefaultErrorFile are template files that can contain a few - template variables that Tinyproxy expands prior to delivery. - Examples are "\{cause}" for an abbreviated error description and - "\{detail}" for a detailed error message. The `tinyproxy(8)` - manual page contains a description of all template variables. +=item * I +does the same, but uses the supplied credentials for authentication. -*LogFile*:: +=item * I +turns on the upstream proxy for the sites matching `site_spec`. - This controls the location of the file to which Tinyproxy - writes its debug output. Alternatively, Tinyproxy can log - to syslog -- see the Syslog option. +`type` can be one of `http`, `socks4`, `socks5`, `none`. -*Syslog*:: +=item * I +turns off upstream support for sites matching `site_spec`, that means the +connection is done directly. - When set to `On`, this option tells Tinyproxy to write its - debug messages to syslog instead of to a log file configured - with `LogFile`. These two options are mutually exclusive. +=back -*LogLevel*:: +The site can be specified in various forms as a hostname, domain +name or as an IP range: - Sets the log level. Messages from the set level and above are - logged. For example, if the LogLevel was set to Warning, then all - log messages from Warning to Critical would be output, but Notice - and below would be suppressed. Allowed values are: +=over 4 - * Critical (least verbose) - * Error - * Warning - * Notice - * Connect (log connections without Info's noise) - * Info (most verbose) +=item * I matches host exactly -*PidFile*:: +=item * I<.name> matches any host in domain "name" - This option controls the location of the file where the main - Tinyproxy process stores its process ID for signaling purposes. +=item * I<.> matches any host with no domain (in 'empty' domain) -*XTinyproxy*:: +=item * I matches network/mask - Setting this option to `Yes` tells Tinyproxy to add a header - `X-Tinyproxy` containing the client's IP address to the request. +=item * I matches network/mask -*Upstream*:: +=back - This option allows you to set up a set of rules for deciding - whether an upstream proxy server is to be used, based on the - host or domain of the site being accessed. The rules are stored - in the order encountered in the configuration file and the - LAST matching rule wins. The following forms for specifying upstream - rules exist: +Note that the upstream directive can also be used to null-route +a specific target domain/host, e.g.: +`upstream http 0.0.0.0:0 ".adserver.com"` - * 'upstream type host:port' turns proxy upstream support on generally. +=item B - * 'upstream type user:pass@host:port' does the same, but uses the - supplied credentials for authentication. +Tinyproxy creates one thread for each connected client. +This options specifies the absolute highest number processes that +will be created. With other words, only MaxClients clients can be +connected to Tinyproxy simultaneously. - * 'upstream type host:port "site_spec"' turns on the upstream proxy - for the sites matching `site_spec`. +=item B - `type` can be one of `http`, `socks4`, `socks5`, `none`. +=item B - * 'upstream none "site_spec"' turns off upstream support for sites - matching `site_spec`, that means the connection is done directly. +The `Allow` and `Deny` options provide a means to customize +which clients are allowed to access Tinyproxy. `Allow` and `Deny` +lines can be specified multiple times to build the access control +list for Tinyproxy. The order in the config file is important. +If there are no `Allow` or `Deny` lines, then all clients are +allowed. Otherwise, the default action is to deny access. +The argument to `Allow` or `Deny` can be a single IP address +of a client host, like `127.0.0.1`, an IP address range, like +`192.168.0.1/24` or a string that will be matched against the +end of the client host name, i.e, this can be a full host name +like `host.example.com` or a domain name like `.example.com` or +even a top level domain name like `.com`. +Note that by adding a rule using a host or domain name, a costly name +lookup has to be done for every new connection, which could slow down +the service considerably. - The site can be specified in various forms as a hostname, domain - name or as an IP range: +=item B - * 'name' matches host exactly - * '.name' matches any host in domain "name" - * '.' matches any host with no domain (in 'empty' domain) - * 'IP/bits' matches network/mask - * 'IP/mask' matches network/mask +Configure one or more HTTP request headers to be added to outgoing +HTTP requests that Tinyproxy makes. Note that this option will not +work for HTTPS traffic, as Tinyproxy has no control over what +headers are exchanged. - Note that the upstream directive can also be used to null-route - a specific target domain/host, e.g.: - `upstream http 0.0.0.0:0 ".adserver.com"` - -*MaxClients*:: - - Tinyproxy creates one thread for each connected client. - This options specifies the absolute highest number processes that - will be created. With other words, only MaxClients clients can be - connected to Tinyproxy simultaneously. - -*Allow*:: -*Deny*:: - - The `Allow` and `Deny` options provide a means to customize - which clients are allowed to access Tinyproxy. `Allow` and `Deny` - lines can be specified multiple times to build the access control - list for Tinyproxy. The order in the config file is important. - If there are no `Allow` or `Deny` lines, then all clients are - allowed. Otherwise, the default action is to deny access. - The argument to `Allow` or `Deny` can be a single IP address - of a client host, like `127.0.0.1`, an IP address range, like - `192.168.0.1/24` or a string that will be matched against the - end of the client host name, i.e, this can be a full host name - like `host.example.com` or a domain name like `.example.com` or - even a top level domain name like `.com`. - Note that by adding a rule using a host or domain name, a costly name - lookup has to be done for every new connection, which could slow down - the service considerably. - -*AddHeader*:: - - Configure one or more HTTP request headers to be added to outgoing - HTTP requests that Tinyproxy makes. Note that this option will not - work for HTTPS traffic, as Tinyproxy has no control over what - headers are exchanged. - + ----- -AddHeader "X-My-Header" "Powered by Tinyproxy" ----- - -*ViaProxyName*:: - - RFC 2616 requires proxies to add a `Via` header to the HTTP - requests, but using the real host name can be a security - concern. If the `ViaProxyname` option is present, then its - string value will be used as the host name in the Via header. - Otherwise, the server's host name will be used. - -*DisableViaHeader*:: - - When this is set to yes, Tinyproxy does NOT add the `Via` header - to the requests. This virtually puts Tinyproxy into stealth mode. - Note that RFC 2616 requires proxies to set the `Via` header, so by - enabling this option, you break compliance. - Don't disable the `Via` header unless you know what you are doing... + AddHeader "X-My-Header" "Powered by Tinyproxy" -*Filter*:: +=item B - Tinyproxy supports filtering of web sites based on URLs or - domains. This option specifies the location of the file - containing the filter rules, one rule per line. +RFC 2616 requires proxies to add a `Via` header to the HTTP +requests, but using the real host name can be a security +concern. If the `ViaProxyname` option is present, then its +string value will be used as the host name in the Via header. +Otherwise, the server's host name will be used. -*FilterURLs*:: - - If this boolean option is set to `Yes` or `On`, filtering is - performed for URLs rather than for domains. The default is to - filter based on domains. - -*FilterExtended*:: - - If this boolean option is set to `Yes`, then extended POSIX - regular expressions are used for matching the filter rules. - The default is to use basic POSIX regular expressions. - -*FilterCaseSensitive*:: - - If this boolean option is set to `Yes`, then the filter rules - are matched in a case sensitive manner. The default is to - match case-insensitively. - -*FilterDefaultDeny*:: - - The default filtering policy is to allow everything that is - not matched by a filtering rule. Setting `FilterDefaultDeny` - to `Yes` changes the policy do deny everything but the domains - or URLs matched by the filtering rules. - -*Anonymous*:: - - If an `Anonymous` keyword is present, then anonymous proxying - is enabled. The headers listed with `Anonymous` are allowed - through, while all others are denied. If no Anonymous keyword - is present, then all headers are allowed through. You must - include quotes around the headers. - + - Most sites require cookies to be enabled for them to work correctly, so - you will need to allow cookies through if you access those sites. - + - Example: - + ----- -Anonymous "Host" -Anonymous "Authorization" -Anonymous "Cookie" ----- - -*ConnectPort*:: - - This option can be used to specify the ports allowed for the - CONNECT method. If no `ConnectPort` line is found, then all - ports are allowed. To disable CONNECT altogether, include a - single ConnectPort line with a value of `0`. - -*ReversePath*:: - - Configure one or more ReversePath directives to enable reverse proxy - support. With reverse proxying it's possible to make a number of - sites appear as if they were part of a single site. - + - If you uncomment the following two directives and run Tinyproxy - on your own computer at port 8888, you can access example.com, - using http://localhost:8888/example/. - + ----- -ReversePath "/example/" "http://www.example.com/" ----- +=item B -*ReverseOnly*:: +When this is set to yes, Tinyproxy does NOT add the `Via` header +to the requests. This virtually puts Tinyproxy into stealth mode. +Note that RFC 2616 requires proxies to set the `Via` header, so by +enabling this option, you break compliance. +Don't disable the `Via` header unless you know what you are doing... - When using Tinyproxy as a reverse proxy, it is STRONGLY - recommended that the normal proxy is turned off by setting - this boolean option to `Yes`. - -*ReverseMagic*:: +=item B - Setting this option to `Yes`, makes Tinyproxy use a cookie to - track reverse proxy mappings. If you need to reverse proxy - sites which have absolute links you must use this option. +Tinyproxy supports filtering of web sites based on URLs or +domains. This option specifies the location of the file +containing the filter rules, one rule per line. -*ReverseBaseURL*:: +=item B - The URL that is used to access this reverse proxy. The URL is - used to rewrite HTTP redirects so that they won't escape the - proxy. If you have a chain of reverse proxies, you'll need to - put the outermost URL here (the address which the end user - types into his/her browser). If this option is not set then - no rewriting of redirects occurs. +If this boolean option is set to `Yes` or `On`, filtering is +performed for URLs rather than for domains. The default is to +filter based on domains. +=item B -BUGS ----- +If this boolean option is set to `Yes`, then extended POSIX +regular expressions are used for matching the filter rules. +The default is to use basic POSIX regular expressions. + +=item B + +If this boolean option is set to `Yes`, then the filter rules +are matched in a case sensitive manner. The default is to +match case-insensitively. + +=item B + +The default filtering policy is to allow everything that is +not matched by a filtering rule. Setting `FilterDefaultDeny` +to `Yes` changes the policy do deny everything but the domains +or URLs matched by the filtering rules. + +=item B + +If an `Anonymous` keyword is present, then anonymous proxying +is enabled. The headers listed with `Anonymous` are allowed +through, while all others are denied. If no Anonymous keyword +is present, then all headers are allowed through. You must +include quotes around the headers. + +Most sites require cookies to be enabled for them to work correctly, so +you will need to allow cookies through if you access those sites. + +Example: + + Anonymous "Host" + Anonymous "Authorization" + Anonymous "Cookie" + +=item B + +This option can be used to specify the ports allowed for the +CONNECT method. If no `ConnectPort` line is found, then all +ports are allowed. To disable CONNECT altogether, include a +single ConnectPort line with a value of `0`. + +=item B + +Configure one or more ReversePath directives to enable reverse proxy +support. With reverse proxying it's possible to make a number of +sites appear as if they were part of a single site. + +If you uncomment the following two directives and run Tinyproxy +on your own computer at port 8888, you can access example.com, +using http://localhost:8888/example/. + + ReversePath "/example/" "http://www.example.com/" + +=item B + +When using Tinyproxy as a reverse proxy, it is STRONGLY +recommended that the normal proxy is turned off by setting +this boolean option to `Yes`. + +=item B + +Setting this option to `Yes`, makes Tinyproxy use a cookie to +track reverse proxy mappings. If you need to reverse proxy +sites which have absolute links you must use this option. + +=item B + +The URL that is used to access this reverse proxy. The URL is +used to rewrite HTTP redirects so that they won't escape the +proxy. If you have a chain of reverse proxies, you'll need to +put the outermost URL here (the address which the end user +types into his/her browser). If this option is not set then +no rewriting of redirects occurs. + +=back + +=head1 BUGS To report bugs in Tinyproxy, please visit -. +L. + +=head1 SEE ALSO -SEE ALSO --------- -tinyproxy(8) +L -AUTHOR ------- +=head1 AUTHOR This manpage was written by the Tinyproxy project team. -COPYRIGHT ---------- +=head1 COPYRIGHT Copyright (c) 1998-2018 the Tinyproxy authors. This program is distributed under the terms of the GNU General Public License version 2 or above. See the COPYING file for additional information. + diff --git a/docs/man8/Makefile.am b/docs/man8/Makefile.am index 0d1eda13..d2d7e19b 100644 --- a/docs/man8/Makefile.am +++ b/docs/man8/Makefile.am @@ -1,35 +1,25 @@ if HAVE_MANPAGE_INTEREST -MAN8_FILES = \ +MAN8_FILES = \ tinyproxy.txt endif -if HAVE_XMLLINT -A2X_ARGS = -d manpage -f manpage -else -A2X_ARGS = -d manpage -f manpage -L -endif - -ASCIIDOCTOR_ARGS = -b manpage +M_SECTION=8 +M_NAME=TINYPROXY man_MANS = \ $(MAN8_FILES:.txt=.8) .txt.8: -if HAVE_A2X - $(AM_V_GEN) $(A2X) $(A2X_ARGS) $< +if HAVE_POD2MAN + $(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \ + --section=$(M_SECTION) --name=$(M_NAME) --release="Version @VERSION@" \ + $< > $@ else -if HAVE_ASCIIDOCTOR - $(AM_V_GEN) $(ASCIIDOCTOR) $(ASCIIDOCTOR_ARGS) $< -else - @echo "*** a2x (asciidoc) or asciidoctor is required to regenerate $(@) ***"; exit 1; -endif + @echo "*** pod2man is required to regenerate $(@) ***"; exit 1; endif MAINTAINERCLEANFILES = \ $(MAN8_FILES:.txt=.8) -CLEANFILES = \ - $(MAN8_FILES:.txt=.xml) - -EXTRA_DIST= \ +EXTRA_DIST = \ $(MAN8_FILES:.txt=.8) diff --git a/docs/man8/tinyproxy.txt.in b/docs/man8/tinyproxy.txt.in index 54da8673..a7882c18 100644 --- a/docs/man8/tinyproxy.txt.in +++ b/docs/man8/tinyproxy.txt.in @@ -1,24 +1,20 @@ -TINYPROXY(8) -============ -:man source: Version @VERSION@ -:man manual: Tinyproxy manual +=pod -NAME ----- +=encoding utf8 + +=head1 NAME tinyproxy - A light-weight HTTP proxy daemon -SYNOPSIS --------- +=head1 SYNOPSIS -*tinyproxy* [-vdch] +B [-vdch] -DESCRIPTION ------------ +=head1 DESCRIPTION -*tinyproxy* is a light-weight HTTP proxy daemon designed to consume a +B is a light-weight HTTP proxy daemon designed to consume a minimum amount of system resources. It listens on a given TCP port and handles HTTP proxy requests. Designed from the ground up to be fast and yet small, it is an ideal solution for use cases such as embedded @@ -26,46 +22,59 @@ deployments where a full featured HTTP proxy is required, but the system resources for a larger proxy are unavailable. -OPTIONS -------- +=head1 OPTIONS + +B accepts the following options: + +=over 4 + +=item B<-c > + +Use an alternate configuration file. + +=item B<-d> -*tinyproxy* accepts the following options: +Don't daemonize and stay in the foreground. Useful for debugging purposes. -*-c *:: - Use an alternate configuration file. +=item B<-h> -*-d*:: - Don't daemonize and stay in the foreground. Useful for debugging purposes. +Display a short help screen of command line arguments and exit. -*-h*:: - Display a short help screen of command line arguments and exit. +=item B<-v> -*-v*:: - Display version information and exit. +Display version information and exit. +=back -SIGNALS -------- +=head1 SIGNALS In addition to command-line options, there are also several signals that -can be sent to *tinyproxy* while it is running to generate debugging +can be sent to B while it is running to generate debugging information and to force certain events. -*SIGHUP*:: - Force Tinyproxy to do a garbage collection on the current - connections linked list. This is usually done automatically after a - certain number of connections have been handled. +=over 4 +=item B -TEMPLATE FILES --------------- +Force Tinyproxy to do a garbage collection on the current +connections linked list. This is usually done automatically after a +certain number of connections have been handled. + +=back + +=head1 TEMPLATE FILES There are two occasions when Tinyproxy delivers HTML pages to the client on it's own right: -. When an error occurred, a corresponding error page is returned. -. When a request for the stathost is made, a page summarizing the - connection statistics is returned. (See STATHOST below.) +=over 4 + +=item * When an error occurred, a corresponding error page is returned. + +=item * When a request for the stathost is made, a page summarizing the +connection statistics is returned. (See STATHOST below.) + +=back The layout of both error pages and the statistics page can be controlled via configurable HTML template files that are plain @@ -73,46 +82,60 @@ HTML files that additionally understand a few template variables. -TEMPLATE VARIABLES ------------------- +=head1 TEMPLATE VARIABLES There are several standard HTML variables that are available in every template file: -*request*:: - The full HTTP request line. +=over 4 + +=item B -*cause*:: - The abbreviated cause of the error condition. +The full HTTP request line. -*clientip*:: - The IP address of the client making the request. +=item B -*clienthost*:: - The hostname of the client making the request. +The abbreviated cause of the error condition. -*version*:: - The version of Tinyproxy. +=item B -*package*:: - The package name. Presently, resolves to 'tinyproxy'. +The IP address of the client making the request. -*date*:: - The current date/time in HTTP format. +=item B + +The hostname of the client making the request. + +=item B + +The version of Tinyproxy. + +=item B + +The package name. Presently, resolves to 'tinyproxy'. + +=item B + +The current date/time in HTTP format. + +=back In addition, almost all templates support: -*detail*:: - A detailed, plain English explanation of the error and possible - causes. +=over 4 + +=item B + +A detailed, plain English explanation of the error and possible +causes. + +=back When Tinyproxy finds a variable name enclosed in braces, e.g. -"\{request}", then this is replaced by the value of the corresponding +"{request}", then this is replaced by the value of the corresponding variable before delivery of the page. -STATHOST --------- +=head1 STATHOST Tinyproxy returns a HTML page with connection statistics when it receives a HTTP request for a certain host -- the stathost. The @@ -124,31 +147,27 @@ The stat file template can be changed at runtime through the configuration variable `StatFile`. -FILES ------ +=head1 FILES `/etc/tinyproxy/tinyproxy.conf`, `/var/run/tinyproxy/tinyproxy.pid`, `/var/log/tinyproxy/tinyproxy.log` -BUGS ----- +=head1 BUGS To report bugs in Tinyproxy, please visit -. +L. + +=head1 SEE ALSO -SEE ALSO --------- -tinyproxy.conf(5) +L -AUTHOR ------- +=head1 AUTHOR This manpage was written by the Tinyproxy project team. -COPYRIGHT ---------- +=head1 COPYRIGHT Copyright (c) 1998-2018 the Tinyproxy authors. From f825bea4c1141cd7b0cb8407d81273854be25a88 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 20 Aug 2020 14:32:16 +0100 Subject: [PATCH 072/265] travis: asciidoc is no longer needed --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 10666607..47c3b8d2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ sudo: true before_install: - sudo apt-get update -qq -- sudo apt-get install --assume-yes asciidoc valgrind +- sudo apt-get install --assume-yes valgrind script: - ./autogen.sh From 281488a72924c6c28f8c7a0b534a4e030766a70f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolai=20S=C3=B8borg?= <55689628+nis-spiir@users.noreply.github.com> Date: Tue, 1 Sep 2020 10:17:25 +0200 Subject: [PATCH 073/265] Change loglevel for "Maximum number of connections reached" I was hit by this, and did not see anything in the log, connections was just hanging. Think warning is a better log level --- src/child.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/child.c b/src/child.c index 9ebba022..15d4fe86 100644 --- a/src/child.c +++ b/src/child.c @@ -100,7 +100,7 @@ void child_main_loop (void) if (sblist_getsize(childs) >= config->maxclients) { if (!was_full) - log_message (LOG_NOTICE, + log_message (LOG_WARNING, "Maximum number of connections reached. " "Refusing new connections."); was_full = 1; From c4dc3ba007fe34d6b7704ffb5ec812a436c326e7 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 5 Sep 2020 17:29:07 +0100 Subject: [PATCH 074/265] filter: fix documentation about rules the file docs/filter-howto.txt was removed, as it contained misleading information since it was first checked in. it suggests the syntax for filter rules is fnmatch()-like, when in fact they need to be specified as posix regular expressions. additionally it contained a lot of utterly unrelated and irrelevant/ outdated text. a few examples with the correct syntax have now been added to tinyproxy.conf.5 manpage. closes #212 --- docs/Makefile.am | 3 +- docs/filter-howto.txt | 53 --------------------------------- docs/man5/tinyproxy.conf.txt.in | 21 +++++++++++++ 3 files changed, 22 insertions(+), 55 deletions(-) delete mode 100644 docs/filter-howto.txt diff --git a/docs/Makefile.am b/docs/Makefile.am index 50652556..e2ba221a 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -4,6 +4,5 @@ SUBDIRS = \ EXTRA_DIST = \ http-error-codes.txt \ - http-rfcs.txt \ - filter-howto.txt + http-rfcs.txt diff --git a/docs/filter-howto.txt b/docs/filter-howto.txt deleted file mode 100644 index 52eab208..00000000 --- a/docs/filter-howto.txt +++ /dev/null @@ -1,53 +0,0 @@ -Using tinyproxy with Your Home/Small Business Network - -Written: Patrick L. McGillan -Edited: Robert James Kaes (2002-06-04) ------------------------------------------------------ - -Being as this will be the most common usage and there were no clear -basic instructions for this scenario, I thought I would write up what -I did for my home system. - -First the layout of the network. A cable modem is connected through a -Linksys Router to a small hub. The computers hanging off the hub and -have a clear shot to the Internet. - -So, the connection from the Internet to the hub looks like this: - - Internet->Cable TV Line->Cable Modem->Linksys Router->Hub/Switch - -Restricting Internet web access on some of those computers (connected -to the hub) is what using tinyproxy is all about. Using the web -interface to the Linksys router, turn off all Internet access to those -computers that shouldn't have direct access to the Internet. This is -done by clicking on the advanced tab and entering the IP number in the -filter range. Now those computers have to go through a proxy, for -their access, as they have no direct access. - -On one of the Linux computers which still has Internet access (I use -an old 486) load up tinyproxy. Now have the users configure their -Internet Explorer/Netscape Navigator programs to use the proxy on the -tinyproxy computer box, along with the port number declared in the -tinyproxy configuration file. By default, there is no blocking of web -sites with this program, so I created a file, called "filter", to -start blocking some sites. - -Example "filter" file entries: - -# This is a comment -bannerads.zwire.com -ad.doubleclick.net -ads.fortunecity.com - -This filter file usually goes into the same folder, as your -configuration file. Be sure and uncomment the 'Filter' line in your -configuration file and make sure it points at your newly created -filter file. - ------------------------------------------------------------------------- - -Copyright (c) 2002 Patrick L. McGillan - -This document is released under the same copyright license as -tinyproxy. You should have found a COPYING file in the top level -directory of this distribution which contains the current license. \ No newline at end of file diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index b5619dd0..66dd04e8 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -256,6 +256,27 @@ Tinyproxy supports filtering of web sites based on URLs or domains. This option specifies the location of the file containing the filter rules, one rule per line. +Rules are specified as POSIX basic regular expressions (BRE), unless +FilterExtended is activated. +Comment lines start with a `#` character. + +Example filter file contents: + + # filter exactly cnn.com + ^cnn\.com$ + + # filter all subdomains of cnn.com, but not cnn.com itself + .*\.cnn.com$ + + # filter any domain that has cnn.com in it, like xcnn.comfy.org + cnn\.com + + # filter any domain that ends in cnn.com + cnn\.com$ + + # filter any domain that starts with adserver + ^adserver + =item B If this boolean option is set to `Yes` or `On`, filtering is From 233ce6de3b4a23f44c0e8a1acdc76cc98be00358 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 5 Sep 2020 19:32:21 +0100 Subject: [PATCH 075/265] filter: reduce memory usage, fix OOM crashes * check return values of memory allocation and abort gracefully in out-of-memory situations * use sblist (linear dynamic array) instead of linked list - this removes one pointer per filter rule - removes need to manually allocate/free every single list item (instead block allocation is used) - simplifies code * remove storage of (unused) input rule - removes one char* pointer per filter rule - removes storage of the raw bytes of each filter rule * add line number to display on out-of-memory/invalid regex situation * replace duplicate filter_domain()/filter_host() code with a single function filter_run() - reduces code size and management effort with these improvements, >1 million regex rules can be loaded with 4 GB of RAM, whereas previously it crashed with about 950K. the list for testing was assembled from http://www.shallalist.de/Downloads/shallalist.tar.gz closes #20 --- src/filter.c | 93 ++++++++++++++++++---------------------------------- src/filter.h | 3 +- src/reqs.c | 4 +-- 3 files changed, 35 insertions(+), 65 deletions(-) diff --git a/src/filter.c b/src/filter.c index 206bf31d..8a0b0855 100644 --- a/src/filter.c +++ b/src/filter.c @@ -29,18 +29,17 @@ #include "log.h" #include "reqs.h" #include "conf.h" +#include "sblist.h" #define FILTER_BUFFER_LEN (512) static int err; struct filter_list { - struct filter_list *next; - char *pat; - regex_t *cpat; + regex_t cpatb; }; -static struct filter_list *fl = NULL; +static sblist *fl = NULL; static int already_init = 0; static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW; @@ -50,10 +49,10 @@ static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW; void filter_init (void) { FILE *fd; - struct filter_list *p; + struct filter_list fe; char buf[FILTER_BUFFER_LEN]; char *s, *start; - int cflags; + int cflags, lineno = 0; if (fl || already_init) { return; @@ -64,8 +63,6 @@ void filter_init (void) return; } - p = NULL; - cflags = REG_NEWLINE | REG_NOSUB; if (config->filter_extended) cflags |= REG_EXTENDED; @@ -73,6 +70,7 @@ void filter_init (void) cflags |= REG_ICASE; while (fgets (buf, FILTER_BUFFER_LEN, fd)) { + ++lineno; /* skip leading whitespace */ s = buf; while (*s && isspace ((unsigned char) *s)) @@ -104,24 +102,22 @@ void filter_init (void) if (*s == '\0') continue; - if (!p) /* head of list */ - fl = p = - (struct filter_list *) - safecalloc (1, sizeof (struct filter_list)); - else { /* next entry */ - p->next = - (struct filter_list *) - safecalloc (1, sizeof (struct filter_list)); - p = p->next; - } + if (!fl) fl = sblist_new(sizeof(struct filter_list), + 4096/sizeof(struct filter_list)); - p->pat = safestrdup (s); - p->cpat = (regex_t *) safemalloc (sizeof (regex_t)); - err = regcomp (p->cpat, p->pat, cflags); + err = regcomp (&fe.cpatb, s, cflags); if (err != 0) { + if (err == REG_ESPACE) goto oom; + fprintf (stderr, + "Bad regex in %s: line %d - %s\n", + config->filter, lineno, s); + exit (EX_DATAERR); + } + if (!sblist_add(fl, &fe)) { + oom:; fprintf (stderr, - "Bad regex in %s: %s\n", - config->filter, p->pat); + "out of memory parsing filter file %s: line %d\n", + config->filter, lineno); exit (EX_DATAERR); } } @@ -137,15 +133,16 @@ void filter_init (void) /* unlink the list */ void filter_destroy (void) { - struct filter_list *p, *q; + struct filter_list *p; + size_t i; if (already_init) { - for (p = q = fl; p; p = q) { - regfree (p->cpat); - safefree (p->cpat); - safefree (p->pat); - q = p->next; - safefree (p); + if (fl) { + for (i = 0; i < sblist_getsize(fl); ++i) { + p = sblist_get(fl, i); + regfree (&p->cpatb); + } + sblist_free(fl); } fl = NULL; already_init = 0; @@ -165,45 +162,19 @@ void filter_reload (void) } /* Return 0 to allow, non-zero to block */ -int filter_domain (const char *host) -{ - struct filter_list *p; - int result; - - if (!fl || !already_init) - goto COMMON_EXIT; - - for (p = fl; p; p = p->next) { - result = - regexec (p->cpat, host, (size_t) 0, (regmatch_t *) 0, 0); - - if (result == 0) { - if (default_policy == FILTER_DEFAULT_ALLOW) - return 1; - else - return 0; - } - } - -COMMON_EXIT: - if (default_policy == FILTER_DEFAULT_ALLOW) - return 0; - else - return 1; -} - -/* returns 0 to allow, non-zero to block */ -int filter_url (const char *url) +int filter_run (const char *str) { struct filter_list *p; + size_t i; int result; if (!fl || !already_init) goto COMMON_EXIT; - for (p = fl; p; p = p->next) { + for (i = 0; i < sblist_getsize(fl); ++i) { + p = sblist_get(fl, i); result = - regexec (p->cpat, url, (size_t) 0, (regmatch_t *) 0, 0); + regexec (&p->cpatb, str, (size_t) 0, (regmatch_t *) 0, 0); if (result == 0) { if (default_policy == FILTER_DEFAULT_ALLOW) diff --git a/src/filter.h b/src/filter.h index 8c6f270b..8a7575bf 100644 --- a/src/filter.h +++ b/src/filter.h @@ -29,8 +29,7 @@ typedef enum { extern void filter_init (void); extern void filter_destroy (void); extern void filter_reload (void); -extern int filter_domain (const char *host); -extern int filter_url (const char *url); +extern int filter_run (const char *str); extern void filter_set_default_policy (filter_policy_t policy); diff --git a/src/reqs.c b/src/reqs.c index 859ac6b5..c58bbedf 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -457,9 +457,9 @@ static struct request_s *process_request (struct conn_s *connptr, */ if (config->filter) { if (config->filter_url) - ret = filter_url (url); + ret = filter_run (url); else - ret = filter_domain (request->host); + ret = filter_run (request->host); if (ret) { update_stats (STAT_DENIED); From 5e594e593a77f25f48e2ff073058ec71cbec1680 Mon Sep 17 00:00:00 2001 From: Brett Randall Date: Sun, 6 Sep 2020 20:45:42 +1000 Subject: [PATCH 076/265] Added BasicAuth to tinyproxy.conf man page. --- docs/man5/tinyproxy.conf.txt.in | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 66dd04e8..09c2ced8 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -225,6 +225,14 @@ Note that by adding a rule using a host or domain name, a costly name lookup has to be done for every new connection, which could slow down the service considerably. +=item B + +Configure HTTP "Basic Authentication" username and password +for accessing the proxy. If there are any entries specified, +access is only granted for authenticated users. + + BasicAuth user password + =item B Configure one or more HTTP request headers to be added to outgoing From 51b8be3ee4b9017287400b76a55a04f39f56a2ab Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 13:45:40 +0100 Subject: [PATCH 077/265] add tinyproxy website template to docs/web this allows to automatically generate the website from the current tinyproxy.conf.5 template. make cd docs/web make --- docs/web/Makefile | 15 + docs/web/podhtml-filter.awk | 5 + docs/web/stylesheets/stylesheet.css | 425 ++++++++++++++++++++++++++++ docs/web/tp.html.foot | 21 ++ docs/web/tp.html.head | 82 ++++++ 5 files changed, 548 insertions(+) create mode 100644 docs/web/Makefile create mode 100644 docs/web/podhtml-filter.awk create mode 100644 docs/web/stylesheets/stylesheet.css create mode 100644 docs/web/tp.html.foot create mode 100644 docs/web/tp.html.head diff --git a/docs/web/Makefile b/docs/web/Makefile new file mode 100644 index 00000000..9b7c1ff6 --- /dev/null +++ b/docs/web/Makefile @@ -0,0 +1,15 @@ +# test webpage with `python -m SimpleHTTPServer` + +all: index.html + +tp.html.conf: ../man5/tinyproxy.conf.txt + pod2html --noindex < $^ | awk -f podhtml-filter.awk > $@ + +index.html: tp.html.head tp.html.conf tp.html.foot + cat $^ > $@ + +clean: + rm tp.html.conf index.html *.tmp + +.PHONY: all clean + diff --git a/docs/web/podhtml-filter.awk b/docs/web/podhtml-filter.awk new file mode 100644 index 00000000..4ea7892f --- /dev/null +++ b/docs/web/podhtml-filter.awk @@ -0,0 +1,5 @@ +BEGIN {i=0} +/<\/{0,1}h1/ {if(!i)i=1; gsub("h1", "h4", $0);} +#/<\/body>/ {i=0;} +/BUGS/ {i=-1} +{if(i==1) print;} diff --git a/docs/web/stylesheets/stylesheet.css b/docs/web/stylesheets/stylesheet.css new file mode 100644 index 00000000..8b1d2c0e --- /dev/null +++ b/docs/web/stylesheets/stylesheet.css @@ -0,0 +1,425 @@ +/******************************************************************************* +Slate Theme for GitHub Pages +by Jason Costello, @jsncostello +*******************************************************************************/ + +@import url(github-light.css); + +/******************************************************************************* +MeyerWeb Reset +*******************************************************************************/ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; +} + +/* HTML5 display-role reset for older browsers */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; +} + +ol, ul { + list-style: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +/******************************************************************************* +Theme Styles +*******************************************************************************/ + +body { + box-sizing: border-box; + color:#373737; + background: #212121; + font-size: 16px; + font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +h1, h2, h3, h4, h5, h6 { + margin: 10px 0; + font-weight: 700; + color:#222222; + font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; + letter-spacing: -1px; +} + +h1 { + font-size: 36px; + font-weight: 700; +} + +h2 { + padding-bottom: 10px; + font-size: 32px; + background: url('../images/bg_hr.png') repeat-x bottom; +} + +h3 { + font-size: 24px; +} + +h4 { + font-size: 21px; +} + +h5 { + font-size: 18px; +} + +h6 { + font-size: 16px; +} + +p { + margin: 10px 0 15px 0; +} + +footer p { + color: #f2f2f2; +} + +a { + text-decoration: none; + color: #007edf; + text-shadow: none; + + transition: color 0.5s ease; + transition: text-shadow 0.5s ease; + -webkit-transition: color 0.5s ease; + -webkit-transition: text-shadow 0.5s ease; + -moz-transition: color 0.5s ease; + -moz-transition: text-shadow 0.5s ease; + -o-transition: color 0.5s ease; + -o-transition: text-shadow 0.5s ease; + -ms-transition: color 0.5s ease; + -ms-transition: text-shadow 0.5s ease; +} + +a:hover, a:focus {text-decoration: underline;} + +footer a { + color: #F2F2F2; + text-decoration: underline; +} + +em { + font-style: italic; +} + +strong { + font-weight: bold; +} + +img { + position: relative; + margin: 0 auto; + max-width: 739px; + padding: 5px; + margin: 10px 0 10px 0; + border: 1px solid #ebebeb; + + box-shadow: 0 0 5px #ebebeb; + -webkit-box-shadow: 0 0 5px #ebebeb; + -moz-box-shadow: 0 0 5px #ebebeb; + -o-box-shadow: 0 0 5px #ebebeb; + -ms-box-shadow: 0 0 5px #ebebeb; +} + +p img { + display: inline; + margin: 0; + padding: 0; + vertical-align: middle; + text-align: center; + border: none; +} + +pre, code { + width: 100%; + color: #222; + background-color: #fff; + + font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; + font-size: 14px; + + border-radius: 2px; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; +} + +pre { + width: 100%; + padding: 10px; + box-shadow: 0 0 10px rgba(0,0,0,.1); + overflow: auto; +} + +code { + padding: 3px; + margin: 0 3px; + box-shadow: 0 0 10px rgba(0,0,0,.1); +} + +pre code { + display: block; + box-shadow: none; +} + +blockquote { + color: #666; + margin-bottom: 20px; + padding: 0 0 0 20px; + border-left: 3px solid #bbb; +} + + +ul, ol, dl { + margin-bottom: 15px +} + +ul { + list-style-position: inside; + list-style: disc; + padding-left: 20px; +} + +ol { + list-style-position: inside; + list-style: decimal; + padding-left: 20px; +} + +dl dt { + font-weight: bold; +} + +dl dd { + padding-left: 20px; +/* font-style: italic; */ +} + +dl p { + padding-left: 20px; +/* font-style: italic; */ +} + +hr { + height: 1px; + margin-bottom: 5px; + border: none; + background: url('../images/bg_hr.png') repeat-x center; +} + +table { + border: 1px solid #373737; + margin-bottom: 20px; + text-align: left; + } + +th { + font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; + padding: 10px; + background: #373737; + color: #fff; + } + +td { + padding: 10px; + border: 1px solid #373737; + } + +form { + background: #f2f2f2; + padding: 20px; +} + +/******************************************************************************* +Full-Width Styles +*******************************************************************************/ + +.outer { + width: 100%; +} + +.inner { + position: relative; + max-width: 640px; + padding: 20px 10px; + margin: 0 auto; +} + +#forkme_banner { + display: block; + position: absolute; + top:0; + right: 10px; + z-index: 10; + padding: 10px 50px 10px 10px; + color: #fff; + background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%; + font-weight: 700; + box-shadow: 0 0 10px rgba(0,0,0,.5); + border-bottom-left-radius: 2px; + border-bottom-right-radius: 2px; +} + +#header_wrap { + background: #212121; + background: -moz-linear-gradient(top, #373737, #212121); + background: -webkit-linear-gradient(top, #373737, #212121); + background: -ms-linear-gradient(top, #373737, #212121); + background: -o-linear-gradient(top, #373737, #212121); + background: linear-gradient(top, #373737, #212121); +} + +#header_wrap .inner { + padding: 50px 10px 30px 10px; +} + +#project_title { + margin: 0; + color: #fff; + font-size: 42px; + font-weight: 700; + text-shadow: #111 0px 0px 10px; +} + +#project_tagline { + color: #fff; + font-size: 24px; + font-weight: 300; + background: none; + text-shadow: #111 0px 0px 10px; +} + +#downloads { + position: absolute; + width: 210px; + z-index: 10; + bottom: -40px; + right: 0; + height: 70px; + background: url('../images/icon_download.png') no-repeat 0% 90%; +} + +.zip_download_link { + display: block; + float: right; + width: 90px; + height:70px; + text-indent: -5000px; + overflow: hidden; + background: url(../images/sprite_download.png) no-repeat bottom left; +} + +.tar_download_link { + display: block; + float: right; + width: 90px; + height:70px; + text-indent: -5000px; + overflow: hidden; + background: url(../images/sprite_download.png) no-repeat bottom right; + margin-left: 10px; +} + +.zip_download_link:hover { + background: url(../images/sprite_download.png) no-repeat top left; +} + +.tar_download_link:hover { + background: url(../images/sprite_download.png) no-repeat top right; +} + +#main_content_wrap { + background: #f2f2f2; + border-top: 1px solid #111; + border-bottom: 1px solid #111; +} + +#main_content { + padding-top: 40px; +} + +#footer_wrap { + background: #212121; +} + + + +/******************************************************************************* +Small Device Styles +*******************************************************************************/ + +@media screen and (max-width: 480px) { + body { + font-size:14px; + } + + #downloads { + display: none; + } + + .inner { + min-width: 320px; + max-width: 480px; + } + + #project_title { + font-size: 32px; + } + + h1 { + font-size: 28px; + } + + h2 { + font-size: 24px; + } + + h3 { + font-size: 21px; + } + + h4 { + font-size: 18px; + } + + h5 { + font-size: 14px; + } + + h6 { + font-size: 12px; + } + + code, pre { + min-width: 320px; + max-width: 480px; + font-size: 11px; + } + +} diff --git a/docs/web/tp.html.foot b/docs/web/tp.html.foot new file mode 100644 index 00000000..d080bac1 --- /dev/null +++ b/docs/web/tp.html.foot @@ -0,0 +1,21 @@ +

+Support

+ +
    +
  • Feel free to report a new bug or suggest features via github issues.
  • +
  • Tinyproxy developers hang out in #tinyproxy on irc.freenode.net.
  • +
+ + + + + + + + + + diff --git a/docs/web/tp.html.head b/docs/web/tp.html.head new file mode 100644 index 00000000..6ce2b0b1 --- /dev/null +++ b/docs/web/tp.html.head @@ -0,0 +1,82 @@ + + + + + + + + + + + Tinyproxy + + + + + +
+
+ View on GitHub + +

Tinyproxy

+

lightweight http(s) proxy daemon

+ +
+
+ + +
+
+

+Tinyproxy

+ +

Tinyproxy is a light-weight HTTP/HTTPS proxy daemon for POSIX operating systems. Designed from the ground up to be fast and yet small, it is an ideal solution for use cases such as embedded deployments where a full featured HTTP proxy is required, but the system resources for a larger proxy are unavailable.

+ +

Tinyproxy is distributed using the GNU GPL license (version 2 or above).

+ +

+Features

+ +

Tinyproxy has a small footprint and requires very little in the way of system resources. The memory footprint tends to be around 2 MB with glibc, and the CPU load increases linearly with the number of simultaneous connections (depending on the speed of the connection). Thus, Tinyproxy can be run on an older machine, or on a network appliance such as a Linux-based broadband router, without any noticeable impact on performance.

+ +

Tinyproxy requires only a minimal POSIX environment to build and operate. It can use additional libraries to add functionality though.

+ +

Tinyproxy allows forwarding of HTTPS connections without modifying traffic in any way through the CONNECT method (see the ConnectPort directive).

+ +

Tinyproxy supports being configured as a transparent proxy, so that a proxy can be used without requiring any client-side configuration. You can also use it as a reverse proxy front-end to your websites.

+ +

Using the AddHeader directive, you can add/insert HTTP headers to outgoing traffic.

+ +

If you're looking to build a custom web proxy, Tinyproxy is easy to modify to your custom needs. The source is straightforward, adhering to the KISS principle. As such, it can be used as a foundation for anything you may need a web proxy to do.

+ +

Tinyproxy has privacy features which can let you configure which HTTP headers should be allowed through, and which should be blocked. This allows you to restrict both what data comes to your web browser from the HTTP server (e.g., cookies), and to restrict what data is allowed through from your web browser to the HTTP server (e.g., version information).

+ +

Using the remote monitoring facility, you can access proxy statistics from afar, letting you know exactly how busy the proxy is.

+ +

You can configure Tinyproxy to control access by only allowing requests from a certain subnet, or from a certain interface, thus ensuring that random, unauthorized people will not be using your proxy.

+ +

With a bit of configuration (specifically, making Tinyproxy created files owned by a non-root user and running it on a port greater than 1024), Tinyproxy can be made to run without any special privileges, thus minimizing the chance of system compromise. Furthermore, it was designed with an eye towards preventing buffer overflows. The simplicity of the code ensures it remains easy to spot such bugs.

+ +

+Downloads

+ +
    +
  • On Red Hat Enterprise Linux, or its derivatives such as CentOS, install Tinyproxy from the EPEL repository by running yum install tinyproxy.
  • +
  • On Fedora, install Tinyproxy by running yum install tinyproxy.
  • +
  • On Debian and derived distributions, run apt-get install tinyproxy to install Tinyproxy.
  • +
  • For openSUSE run: zypper in tinyproxy
  • +
  • Arch users can install the Tinyproxy package from the community repository. Run pacman -S tinyproxy to install it.
  • +
  • FreeBSD, OpenBSD or NetBSD users can use the pkg_add utility to install the tinyproxy package.
  • +
  • Mac OS X users can check MacPorts to see if the Tinyproxy port there is recent enough.
  • +
+ +

If you feel that the Tinyproxy binary package in your operating system is not recent, please contact the package maintainer for that particular operating system. If this fails, you can always compile the latest stable version from source code.

+ +

We distribute Tinyproxy in source code form, and it has to be compiled in order to be used on your system. Please see the INSTALL file in the source code tree for build instructions. The current stable version of Tinyproxy is available on the releases page. The Tinyproxy NEWS file contains the release notes. You can verify the tarball using its PGP signature. You can also browse the older releases of Tinyproxy.

+ +

We use Git as the version control system for the Tinyproxy source code repository. To get a copy of the Tinyproxy repository, use the command:

+ +

git clone https://github.com/tinyproxy/tinyproxy.git

+ +

+Documentation

From 36c9b93cfec19efdbc6600b85dac4a2b2e841013 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 16:22:11 +0100 Subject: [PATCH 078/265] transparent: remove usage of inet_ntoa(), make IPv6 ready inet_ntoa() uses a static buffer and is therefore not threadsafe. additionally it has been deprecated by POSIX. by using inet_ntop() instead the code has been made ipv6 aware. note that this codepath was only entered in the unlikely event that no hosts header was being passed to the proxy, i.e. pre-HTTP/1.1. --- configure.ac | 2 +- src/transparent-proxy.c | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 900af95d..74220a23 100644 --- a/configure.ac +++ b/configure.ac @@ -143,7 +143,7 @@ AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK AC_FUNC_MALLOC AC_FUNC_REALLOC -AC_CHECK_FUNCS([inet_ntoa strdup]) +AC_CHECK_FUNCS([strdup]) AC_CHECK_FUNCS([strlcpy strlcat setgroups]) dnl Enable extra warnings diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index 727ef3e9..2c1e069a 100644 --- a/src/transparent-proxy.c +++ b/src/transparent-proxy.c @@ -64,12 +64,16 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders, length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data); if (length <= 0) { - struct sockaddr_in dest_addr; + union sockaddr_union dest_addr; + const void *dest_inaddr; + char namebuf[INET6_ADDRSTRLEN+1]; + int af; length = sizeof(dest_addr); if (getsockname - (connptr->client_fd, (struct sockaddr *) &dest_addr, + (connptr->client_fd, (void *) &dest_addr, &length) < 0 || length > sizeof(dest_addr)) { + addr_err:; log_message (LOG_ERR, "process_request: cannot get destination IP for %d", connptr->client_fd); @@ -79,10 +83,16 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders, return 0; } - request->host = (char *) safemalloc (17); - strlcpy (request->host, inet_ntoa (dest_addr.sin_addr), 17); + af = length == sizeof(dest_addr.v4) ? AF_INET : AF_INET6; + if (af == AF_INET) dest_inaddr = &dest_addr.v4.sin_addr; + else dest_inaddr = &dest_addr.v6.sin6_addr; - request->port = ntohs (dest_addr.sin_port); + if (!inet_ntop(af, dest_inaddr, namebuf, sizeof namebuf)) + goto addr_err; + + request->host = safestrdup (namebuf); + request->port = ntohs (af == AF_INET ? dest_addr.v4.sin_port + : dest_addr.v6.sin6_port); request->path = (char *) safemalloc (ulen + 1); strlcpy (request->path, *url, ulen + 1); From 8685d23225f536920b0d4b007c148946e3289beb Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 16:32:34 +0100 Subject: [PATCH 079/265] configure.ac: remove check for strdup() it was being used unconditionally anyway. --- configure.ac | 1 - 1 file changed, 1 deletion(-) diff --git a/configure.ac b/configure.ac index 74220a23..8701ad95 100644 --- a/configure.ac +++ b/configure.ac @@ -143,7 +143,6 @@ AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK AC_FUNC_MALLOC AC_FUNC_REALLOC -AC_CHECK_FUNCS([strdup]) AC_CHECK_FUNCS([strlcpy strlcat setgroups]) dnl Enable extra warnings From f20681e0c628fa1d5f222e69615e9d48529b61ec Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 16:40:52 +0100 Subject: [PATCH 080/265] configure.ac: remove unused checks for malloc/realloc --- configure.ac | 2 -- 1 file changed, 2 deletions(-) diff --git a/configure.ac b/configure.ac index 8701ad95..a0503a48 100644 --- a/configure.ac +++ b/configure.ac @@ -140,8 +140,6 @@ AC_CHECK_HEADERS([sys/ioctl.h alloca.h memory.h malloc.h sysexits.h \ dnl Checks for libary functions AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK -AC_FUNC_MALLOC -AC_FUNC_REALLOC AC_CHECK_FUNCS([strlcpy strlcat setgroups]) From ab27e4c68b8a5cf89e8534f3a5011083c558139a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 16:58:28 +0100 Subject: [PATCH 081/265] configure.ac: check for all "desired" CFLAGS at once in case they're all accepted, which would be the case with any halfways recent GCC, we save a lot of time over testing each flag sequentially. --- configure.ac | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configure.ac b/configure.ac index a0503a48..782ee06d 100644 --- a/configure.ac +++ b/configure.ac @@ -150,9 +150,15 @@ if test -n "${MAINTAINER_MODE_FALSE}"; then DESIRED_FLAGS="-Werror $DESIRED_FLAGS" fi +all_desired_work=false +AS_COMPILER_FLAG([$DESIRED_FLAGS], [all_desired_work=true]) +if $all_desired_work ; then + CFLAGS="$CFLAGS $DESIRED_FLAGS" +else for flag in $DESIRED_FLAGS; do AS_COMPILER_FLAG([$flag], [CFLAGS="$CFLAGS $flag"]) done +fi dnl Disable debugging if it's not specified if test x"$debug_enabled" != x"yes" ; then From 55208eb2f6f3ffdc4b76d50d1dc804a4d72f3532 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 17:20:06 +0100 Subject: [PATCH 082/265] run_tests.sh: print pid if killing tp fails --- tests/scripts/run_tests.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index 6f797ef7..14575f8b 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -105,11 +105,12 @@ start_tinyproxy() { stop_tinyproxy() { echo -n "killing tinyproxy..." - kill $(cat $TINYPROXY_PID_FILE) + pid=$(cat $TINYPROXY_PID_FILE) + kill $pid if test "x$?" = "x0" ; then echo " ok" else - echo " error" + echo " error killing pid $pid" fi } From 0d26fab3172453cc5fed4c3a9b01cc2d66baec3c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 17:48:14 +0100 Subject: [PATCH 083/265] run_tests.sh: print more diagnostic if killing tp fails --- tests/scripts/run_tests.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index 14575f8b..799c9d65 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -30,6 +30,7 @@ TINYPROXY_USER=$(id -un) TINYPROXY_PID_DIR=$TESTENV_DIR/var/run/tinyproxy TINYPROXY_PID_FILE=$TINYPROXY_PID_DIR/tinyproxy.pid TINYPROXY_LOG_DIR=$LOG_DIR/tinyproxy +TINYPROXY_LOG_FILE=$TINYPROXY_LOG_DIR/tinyproxy.log TINYPROXY_DATA_DIR=$TESTENV_DIR/usr/share/tinyproxy TINYPROXY_CONF_DIR=$TESTENV_DIR/etc/tinyproxy TINYPROXY_CONF_FILE=$TINYPROXY_CONF_DIR/tinyproxy.conf @@ -80,7 +81,7 @@ Timeout 600 StatHost "$TINYPROXY_STATHOST_IP" DefaultErrorFile "$TINYPROXY_DATA_DIR/debug.html" StatFile "$TINYPROXY_DATA_DIR/stats.html" -Logfile "$TINYPROXY_LOG_DIR/tinyproxy.log" +Logfile "$TINYPROXY_LOG_FILE" PidFile "$TINYPROXY_PID_FILE" LogLevel Info MaxClients 100 @@ -111,6 +112,11 @@ stop_tinyproxy() { echo " ok" else echo " error killing pid $pid" + ps aux | grep tinyproxy + echo "### printing logfile" + cat $TINYPROXY_LOG_FILE + echo "### printing stderr logfile" + cat $TINYPROXY_STDERR_LOG fi } From f1a6d063b0bf0ca98e0578482b1541907a78bb3a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 19:58:21 +0100 Subject: [PATCH 084/265] version.sh: fix empty result when git describe fails fixes an error in travis, which makes a shallow clone of 50 commits. if the last tag is older than 50 commits, we get: "fatal: No names found, cannot describe anything." this caused a premature exit due to an assert error in safe_write() on this line: assert (count > 0); because the version variable in tinyproxy was empty. --- scripts/version.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/version.sh b/scripts/version.sh index 9a965dc8..f3948bcc 100755 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -5,8 +5,12 @@ GIT_DIR="${SCRIPT_DIR}/../.git" if test -d "${GIT_DIR}" ; then if type git >/dev/null 2>&1 ; then - git describe --match '[0-9]*.[0-9]*.[0-9]*' 2>/dev/null \ - | sed -e 's/-/-git-/' + gitstr=$(git describe --match '[0-9]*.[0-9]*.[0-9]*' 2>/dev/null) + if test "x$?" != x0 ; then + sed 's/$/-git/' < VERSION + else + printf "%s\n" "$gitstr" | sed -e 's/-/-git-/' + fi else sed 's/$/-git/' < VERSION fi From 0d71223a1dce3745da443cc6239e27f07fe07113 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 20:06:59 +0100 Subject: [PATCH 085/265] send_html_file(): also set empty variables to "(unknown)" --- src/html-error.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/html-error.c b/src/html-error.c index 3018b467..f04943ee 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -107,7 +107,7 @@ send_html_file (FILE *infile, struct conn_s *connptr) varval = (const char *) lookup_variable (connptr->error_variables, varstart); - if (!varval) + if (!varval || !varval[0]) varval = "(unknown)"; r = write_message (connptr->client_fd, "%s", varval); From 3da66364dee0ae22abea20350af047cd95eb9f05 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 20:32:52 +0100 Subject: [PATCH 086/265] configure.ac: fail if version script returns empty string --- configure.ac | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/configure.ac b/configure.ac index 782ee06d..7de60871 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,11 @@ AC_INIT([Tinyproxy], [tinyproxy_version], [https://tinyproxy.github.io/], [tinyproxy]) +tpv=tinyproxy_version +if test "x$tpv" = "x" ; then +AC_MSG_ERROR([got empty result from version script!]) +fi + AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([dist-bzip2 dist-xz]) AC_CONFIG_HEADERS(config.h) From 8ba0ac4e8662ddc9c109229594f8a8ff0471b0df Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 22:46:26 +0100 Subject: [PATCH 087/265] do not catch SIGHUP in foreground-mode it's quite unexpected for an application running foreground in a terminal to keep running when the terminal is closed. also in such a case (if file logging is disabled) there's no way to see what's happening to the proxy. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index fc5ad457..d9f8a910 100644 --- a/src/main.c +++ b/src/main.c @@ -392,7 +392,7 @@ main (int argc, char **argv) exit (EX_OSERR); } - if (set_signal_handler (SIGHUP, takesig) == SIG_ERR) { + if (daemonized && set_signal_handler (SIGHUP, takesig) == SIG_ERR) { fprintf (stderr, "%s: Could not set the \"SIGHUP\" signal.\n", argv[0]); exit (EX_OSERR); From 95b1a8ea063004d80bb36f4d1c856d4e22354ed3 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 23:08:10 +0100 Subject: [PATCH 088/265] main.c: remove set_signal_handler code duplication --- src/main.c | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/src/main.c b/src/main.c index d9f8a910..49b4d8f4 100644 --- a/src/main.c +++ b/src/main.c @@ -271,6 +271,15 @@ int reload_config (int reload_logging) return ret; } +static void setup_sig(int sig, signal_func *sigh, + const char* signame, const char* argv0) { + if (set_signal_handler (sig, sigh) == SIG_ERR) { + fprintf (stderr, "%s: Could not set the \"%s\" signal.\n", + argv0, signame); + exit (EX_OSERR); + } +} + int main (int argc, char **argv) { @@ -338,11 +347,7 @@ main (int argc, char **argv) makedaemon (); } - if (set_signal_handler (SIGPIPE, SIG_IGN) == SIG_ERR) { - fprintf (stderr, "%s: Could not set the \"SIGPIPE\" signal.\n", - argv[0]); - exit (EX_OSERR); - } + setup_sig(SIGPIPE, SIG_IGN, "SIGPIPE", argv[0]); #ifdef FILTER_ENABLE if (config->filter) @@ -380,23 +385,9 @@ main (int argc, char **argv) /* These signals are only for the parent process. */ log_message (LOG_INFO, "Setting the various signals."); - if (set_signal_handler (SIGCHLD, takesig) == SIG_ERR) { - fprintf (stderr, "%s: Could not set the \"SIGCHLD\" signal.\n", - argv[0]); - exit (EX_OSERR); - } - - if (set_signal_handler (SIGTERM, takesig) == SIG_ERR) { - fprintf (stderr, "%s: Could not set the \"SIGTERM\" signal.\n", - argv[0]); - exit (EX_OSERR); - } - - if (daemonized && set_signal_handler (SIGHUP, takesig) == SIG_ERR) { - fprintf (stderr, "%s: Could not set the \"SIGHUP\" signal.\n", - argv[0]); - exit (EX_OSERR); - } + setup_sig (SIGCHLD, takesig, "SIGCHLD", argv[0]); + setup_sig (SIGTERM, takesig, "SIGTERM", argv[0]); + if (daemonized) setup_sig (SIGHUP, takesig, "SIGHUP", argv[0]); /* Start the main loop */ log_message (LOG_INFO, "Starting main loop. Accepting connections."); From 8c86e8b3ae6ffcec693a06a4ea7d03274f3e5687 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 23:11:19 +0100 Subject: [PATCH 089/265] allow SIGUSR1 to be used as an alternative to SIGHUP this allows a tinyproxy session in terminal foreground mode to reload its configuration without dropping active connections. --- src/main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.c b/src/main.c index 49b4d8f4..da3feb3a 100644 --- a/src/main.c +++ b/src/main.c @@ -69,6 +69,7 @@ takesig (int sig) int status; switch (sig) { + case SIGUSR1: case SIGHUP: received_sighup = TRUE; break; @@ -388,6 +389,7 @@ main (int argc, char **argv) setup_sig (SIGCHLD, takesig, "SIGCHLD", argv[0]); setup_sig (SIGTERM, takesig, "SIGTERM", argv[0]); if (daemonized) setup_sig (SIGHUP, takesig, "SIGHUP", argv[0]); + setup_sig (SIGUSR1, takesig, "SIGUSR1", argv[0]); /* Start the main loop */ log_message (LOG_INFO, "Starting main loop. Accepting connections."); From 65e79b84a4225d40061ebe061cb87c86f887e692 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 23:15:41 +0100 Subject: [PATCH 090/265] update documentation about signals --- docs/man8/tinyproxy.txt.in | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/man8/tinyproxy.txt.in b/docs/man8/tinyproxy.txt.in index a7882c18..809873fe 100644 --- a/docs/man8/tinyproxy.txt.in +++ b/docs/man8/tinyproxy.txt.in @@ -59,6 +59,13 @@ information and to force certain events. Force Tinyproxy to do a garbage collection on the current connections linked list. This is usually done automatically after a certain number of connections have been handled. +(Daemon mode only) + +=item B + +Force reload of config file and filter list. +This is handy to update the configuration if Tinyproxy is running +in foreground without dropping active connections. =back From 7c37a61e00851b3cd8f563007a69b43a2941e472 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 6 Sep 2020 23:16:29 +0100 Subject: [PATCH 091/265] manpages: update copyright years --- docs/man5/tinyproxy.conf.txt.in | 2 +- docs/man8/tinyproxy.txt.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 09c2ced8..8a18d55a 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -387,7 +387,7 @@ This manpage was written by the Tinyproxy project team. =head1 COPYRIGHT -Copyright (c) 1998-2018 the Tinyproxy authors. +Copyright (c) 1998-2020 the Tinyproxy authors. This program is distributed under the terms of the GNU General Public License version 2 or above. See the COPYING file for additional diff --git a/docs/man8/tinyproxy.txt.in b/docs/man8/tinyproxy.txt.in index 809873fe..7fa420f6 100644 --- a/docs/man8/tinyproxy.txt.in +++ b/docs/man8/tinyproxy.txt.in @@ -176,7 +176,7 @@ This manpage was written by the Tinyproxy project team. =head1 COPYRIGHT -Copyright (c) 1998-2018 the Tinyproxy authors. +Copyright (c) 1998-2020 the Tinyproxy authors. This program is distributed under the terms of the GNU General Public License version 2 or above. See the COPYING file for additional From d0fae1176094f0da46ed21d773fff64284dfa4a8 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 01:04:30 +0100 Subject: [PATCH 092/265] config parser: increase possible line length limit let's use POSIX LINE_MAX (usually 4KB) instead of 1KB. closes #226 --- src/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index 9dd6da26..27dc951c 100644 --- a/src/conf.c +++ b/src/conf.c @@ -397,7 +397,7 @@ static int check_match (struct config_s *conf, const char *line, */ static int config_parse (struct config_s *conf, FILE * f) { - char buffer[1024]; /* 1KB lines should be plenty */ + char buffer[LINE_MAX]; unsigned long lineno = 1; while (fgets (buffer, sizeof (buffer), f)) { From 17ae1b512c386970d0f5b84b385b89749a89b1e5 Mon Sep 17 00:00:00 2001 From: "[anp/hsw]" Date: Tue, 1 Jan 2019 11:00:43 +0700 Subject: [PATCH 093/265] Do not give error while storing invalid header --- src/reqs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reqs.c b/src/reqs.c index c58bbedf..365b27db 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -605,7 +605,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') From a8848d4bd898a15e02a8f4210917166bca9f50b1 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 04:32:13 +0100 Subject: [PATCH 094/265] html-error: substitute template variables via a regex previously, in order to detect and insert {variables} into error/stats templates, tinyproxy iterated char-by-char over the input file, and would try to parse anything inside {} pairs and treat it like a variable name. this breaks CSS, and additionally it's dog slow as tinyproxy wrote every single character to the client via a write syscall. now we process line-by-line, and inspect all matches of the regex \{[a-z]{1,32}\}. if the contents of the regex are a known variable name, substitution is taking place. if not, the contents are passed as-is to the client. also the chunks before and after matches are written in a single syscall. closes #108 --- src/html-error.c | 93 +++++++++++++++++------------------------------- 1 file changed, 33 insertions(+), 60 deletions(-) diff --git a/src/html-error.c b/src/html-error.c index f04943ee..c94dbd70 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -83,76 +83,49 @@ static char *get_html_file (unsigned int errornum) return (val); } +static void varsubst_sendline(struct conn_s *connptr, regex_t *re, char *p) { + int fd = connptr->client_fd; + while(*p) { + regmatch_t match; + char varname[32+1], *varval; + size_t l; + int st = regexec(re, p, 1, &match, 0); + if(st == 0) { + if(match.rm_so > 0) safe_write(fd, p, match.rm_so); + l = match.rm_eo - match.rm_so; + assert(l>2 && l-2 < sizeof(varname)); + p += match.rm_so; + memcpy(varname, p+1, l-2); + varname[l-2] = 0; + varval = lookup_variable(connptr->error_variables, varname); + if(varval) write_message(fd, "%s", varval); + else if(varval && !*varval) write_message(fd, "(unknown)"); + else safe_write(fd, p, l); + p += l; + } else { + write_message(fd, "%s", p); + break; + } + } +} + /* * Send an already-opened file to the client with variable substitution. */ int send_html_file (FILE *infile, struct conn_s *connptr) { - char *inbuf; - char *varstart = NULL; - char *p; - const char *varval; - int in_variable = 0; - int r = 0; - - inbuf = (char *) safemalloc (4096); - - while (fgets (inbuf, 4096, infile) != NULL) { - for (p = inbuf; *p; p++) { - switch (*p) { - case '}': - if (in_variable) { - *p = '\0'; - varval = (const char *) - lookup_variable (connptr->error_variables, - varstart); - if (!varval || !varval[0]) - varval = "(unknown)"; - r = write_message (connptr->client_fd, - "%s", varval); - in_variable = 0; - } else { - r = write_message (connptr->client_fd, - "%c", *p); - } - - break; - - case '{': - /* a {{ will print a single {. If we are NOT - * already in a { variable, then proceed with - * setup. If we ARE already in a { variable, - * this code will fallthrough to the code that - * just dumps a character to the client fd. - */ - if (!in_variable) { - varstart = p + 1; - in_variable++; - } else - in_variable = 0; - - /* FALL THROUGH */ - default: - if (!in_variable) { - r = write_message (connptr->client_fd, - "%c", *p); - } - } - - if (r) - break; - } + regex_t re; + char *inbuf = safemalloc (4096); + (void) regcomp(&re, "{[a-z]\\{1,32\\}}", 0); - if (r) - break; - - in_variable = 0; + while (fgets (inbuf, 4096, infile)) { + varsubst_sendline(connptr, &re, inbuf); } + regfree (&re); safefree (inbuf); - - return r; + return 1; } int send_http_headers (struct conn_s *connptr, int code, const char *message) From 559faf7957ffed2fff3cc282426dd0f7bfa4485b Mon Sep 17 00:00:00 2001 From: Brett Randall Date: Mon, 7 Sep 2020 14:27:36 +1000 Subject: [PATCH 095/265] website stylesheet: added pre margin-bottom: 20px. this improves rendering of literal code paragraphs. --- docs/web/stylesheets/stylesheet.css | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/web/stylesheets/stylesheet.css b/docs/web/stylesheets/stylesheet.css index 8b1d2c0e..eac0014c 100644 --- a/docs/web/stylesheets/stylesheet.css +++ b/docs/web/stylesheets/stylesheet.css @@ -173,6 +173,7 @@ pre, code { pre { width: 100%; padding: 10px; + margin-bottom: 20px; box-shadow: 0 0 10px rgba(0,0,0,.1); overflow: auto; } From 23b0c8465306a0e069a639085bb08266b803a2de Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 16:11:51 +0100 Subject: [PATCH 096/265] upstream: fix ip/mask calculation for types other than none the code wrongly processed the site_spec (here: domain) parameter only when PT_TYPE == PT_NONE. re-arranged code to process it correctly whenever passed. additionally the mask is now also applied to the passed subnet/ip, so a site_spec like 127.0.0.1/8 is converted into 127.0.0.0/8. also the case where inet_aton fails now produces a proper error message. note that the code still doesn't process ipv6 addresses and mask. to support it, we should use the existing code in acl.c and refactor it so it can be used from both call sites. closes #83 closes #165 --- src/upstream.c | 49 ++++++++++++++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/src/upstream.c b/src/upstream.c index 321b2824..0f7f24fb 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -81,7 +81,13 @@ static struct upstream *upstream_build (const char *host, int port, const char * } if (domain == NULL) { - if (!host || host[0] == '\0' || port < 1) { + if (type == PT_NONE) { + e_nonedomain:; + log_message (LOG_WARNING, + "Nonsense upstream none rule: empty domain"); + goto fail; + } + if (!host || !host[0] || port < 1) { log_message (LOG_WARNING, "Nonsense upstream rule: invalid host or port"); goto fail; @@ -92,11 +98,17 @@ static struct upstream *upstream_build (const char *host, int port, const char * log_message (LOG_INFO, "Added upstream %s %s:%d for [default]", proxy_type_name(type), host, port); - } else if (host == NULL || type == PT_NONE) { - if (!domain || domain[0] == '\0') { - log_message (LOG_WARNING, - "Nonsense no-upstream rule: empty domain"); - goto fail; + } else { + if (type == PT_NONE) { + if (!domain[0]) goto e_nonedomain; + } else { + if (!host || !host[0] || !domain[0]) { + log_message (LOG_WARNING, + "Nonsense upstream rule: invalid parameters"); + goto fail; + } + up->host = safestrdup (host); + up->port = port; } ptr = strchr (domain, '/'); @@ -116,26 +128,21 @@ static struct upstream *upstream_build (const char *host, int port, const char * up->mask = ~((1 << (32 - atoi (ptr))) - 1); } + up->ip = up->ip & up->mask; + } else { + log_message (LOG_WARNING, + "Nonsense upstream rule: failed to parse netmask"); + goto fail; } } else { up->domain = safestrdup (domain); } - log_message (LOG_INFO, "Added no-upstream for %s", domain); - } else { - if (!host || host[0] == '\0' || !domain - || domain[0] == '\0') { - log_message (LOG_WARNING, - "Nonsense upstream rule: invalid parameters"); - goto fail; - } - - up->host = safestrdup (host); - up->port = port; - up->domain = safestrdup (domain); - - log_message (LOG_INFO, "Added upstream %s %s:%d for %s", - proxy_type_name(type), host, port, domain); + if (type == PT_NONE) + log_message (LOG_INFO, "Added upstream none for %s", domain); + else + log_message (LOG_INFO, "Added upstream %s %s:%d for %s", + proxy_type_name(type), host, port, domain); } return up; From 487a062fccbd1632b183debc146d61b4bdbce048 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 16:59:37 +0100 Subject: [PATCH 097/265] change loglevel of start/stop/reload messages to NOTICE this allows to see them when the verbose INFO loglevel is not desired. closes #78 --- src/conf.c | 2 +- src/main.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/conf.c b/src/conf.c index 27dc951c..bf2458f3 100644 --- a/src/conf.c +++ b/src/conf.c @@ -464,7 +464,7 @@ int reload_config_file (const char *config_fname, struct config_s *conf) { int ret; - log_message (LOG_INFO, "Reloading config file"); + log_message (LOG_NOTICE, "Reloading config file"); free_config (conf); diff --git a/src/main.c b/src/main.c index da3feb3a..7b702ab9 100644 --- a/src/main.c +++ b/src/main.c @@ -293,7 +293,7 @@ main (int argc, char **argv) */ umask (0177); - log_message (LOG_INFO, "Initializing " PACKAGE " ..."); + log_message (LOG_NOTICE, "Initializing " PACKAGE " ..."); if (config_compile_regex()) { exit (EX_SOFTWARE); @@ -396,7 +396,7 @@ main (int argc, char **argv) child_main_loop (); - log_message (LOG_INFO, "Shutting down."); + log_message (LOG_NOTICE, "Shutting down."); child_kill_children (SIGTERM); child_close_sock (); From f720244baa0f92c5e8c9500d2cc41415775196fd Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 18:08:09 +0100 Subject: [PATCH 098/265] README.md: describe how transparent proxying can be used addressing #45 --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f13e670c..bba85dcd 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,9 @@ Enable support for proxying connections through another proxy server. - `--enable-transparent`: Allow Tinyproxy to be used as a transparent proxy daemon. +Unlike other work modes, transparent proxying doesn't require explicit +configuration and works automatically when traffic is redirected to +the proxy using the appropriate firewall rules. - `--enable-reverse`: Enable reverse proxying. From 88153e944f7d28f57cccc77f3228a3f54f78ce4e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 20:49:07 +0100 Subject: [PATCH 099/265] get_request_entity: respect user-set timeout get_request_entity() is only called on error, for example if a client doesn't pass a check_acl() check. in such a case it's possible that the client fd isn't yet ready to read from. using select() with a timeout timeval of {0,0} causes it to return immediately and return 0 if there's no data ready to be read. this resulted in immediate connection termination rather than returning the 403 access denied error page to the client and a confusing "no entity" message displayed in the proxy log. --- src/reqs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reqs.c b/src/reqs.c index 365b27db..87a1a828 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1490,7 +1490,7 @@ get_request_entity(struct conn_s *connptr) FD_ZERO (&rset); FD_SET (connptr->client_fd, &rset); - tv.tv_sec = 0; + tv.tv_sec = config->idletimeout; tv.tv_usec = 0; ret = select (connptr->client_fd + 1, &rset, NULL, NULL, &tv); From efa5892011af588dd3adc5d67ab80cd02c330d68 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 20:57:16 +0100 Subject: [PATCH 100/265] check_acl: do full_inet_pton() only once per ip if there's a long list of acl's, doing full_inet_pton() over and over with the same IP isn't really efficient. --- src/acl.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/acl.c b/src/acl.c index 06c982b4..15bc2a14 100644 --- a/src/acl.c +++ b/src/acl.c @@ -305,16 +305,12 @@ acl_string_processing (struct acl_s *acl, const char *ip_address, * 0 IP address is denied * -1 neither allowed nor denied. */ -static int check_numeric_acl (const struct acl_s *acl, const char *ip) +static int check_numeric_acl (const struct acl_s *acl, uint8_t addr[IPV6_LEN]) { - uint8_t addr[IPV6_LEN], x, y; + uint8_t x, y; int i; assert (acl && acl->type == ACL_NUMERIC); - assert (ip && strlen (ip) > 0); - - if (full_inet_pton (ip, &addr) <= 0) - return -1; for (i = 0; i != IPV6_LEN; ++i) { x = addr[i] & acl->address.ip.mask[i]; @@ -339,9 +335,10 @@ static int check_numeric_acl (const struct acl_s *acl, const char *ip) int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list) { struct acl_s *acl; - int perm = 0; + int perm = 0, is_numeric_addr; size_t i; char string_addr[HOSTNAME_LENGTH]; + uint8_t numeric_addr[IPV6_LEN]; assert (ip != NULL); assert (addr != NULL); @@ -354,6 +351,8 @@ int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list) if (!access_list) return 1; + is_numeric_addr = (full_inet_pton (ip, &numeric_addr) > 0); + for (i = 0; i != (size_t) vector_length (access_list); ++i) { acl = (struct acl_s *) vector_getentry (access_list, i, NULL); switch (acl->type) { @@ -364,7 +363,10 @@ int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list) case ACL_NUMERIC: if (ip[0] == '\0') continue; - perm = check_numeric_acl (acl, ip); + + perm = is_numeric_addr + ? check_numeric_acl (acl, numeric_addr) + : -1; break; } From ebc7f15ec75526ffacad7b82c99764b90eef021a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 21:53:14 +0100 Subject: [PATCH 101/265] acl: typedef access_list to acl_list_t this allows to switch the underlying implementation easily. --- src/acl.c | 8 ++++---- src/acl.h | 7 ++++--- src/conf.h | 3 ++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/acl.c b/src/acl.c index 15bc2a14..9e905d7e 100644 --- a/src/acl.c +++ b/src/acl.c @@ -111,7 +111,7 @@ fill_netmask_array (char *bitmask_string, int v6, /** * If the access list has not been set up, create it. */ -static int init_access_list(vector_t *access_list) +static int init_access_list(acl_list_t *access_list) { if (!*access_list) { *access_list = vector_create (); @@ -135,7 +135,7 @@ static int init_access_list(vector_t *access_list) * 0 otherwise. */ int -insert_acl (char *location, acl_access_t access_type, vector_t *access_list) +insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list) { struct acl_s acl; int ret; @@ -332,7 +332,7 @@ static int check_numeric_acl (const struct acl_s *acl, uint8_t addr[IPV6_LEN]) * 1 if allowed * 0 if denied */ -int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list) +int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_list) { struct acl_s *acl; int perm = 0, is_numeric_addr; @@ -388,7 +388,7 @@ int check_acl (const char *ip, union sockaddr_union *addr, vector_t access_list) return 0; } -void flush_access_list (vector_t access_list) +void flush_access_list (acl_list_t access_list) { struct acl_s *acl; size_t i; diff --git a/src/acl.h b/src/acl.h index ba0aebee..495a4b3e 100644 --- a/src/acl.h +++ b/src/acl.h @@ -25,11 +25,12 @@ #include "sock.h" typedef enum { ACL_ALLOW, ACL_DENY } acl_access_t; +typedef vector_t acl_list_t; extern int insert_acl (char *location, acl_access_t access_type, - vector_t *access_list); + acl_list_t *access_list); extern int check_acl (const char *ip_address, union sockaddr_union *addr, - vector_t access_list); -extern void flush_access_list (vector_t access_list); + acl_list_t access_list); +extern void flush_access_list (acl_list_t access_list); #endif diff --git a/src/conf.h b/src/conf.h index 085169dd..e89806a5 100644 --- a/src/conf.h +++ b/src/conf.h @@ -24,6 +24,7 @@ #include "hashmap.h" #include "vector.h" +#include "acl.h" /* * Stores a HTTP header created using the AddHeader directive. @@ -93,7 +94,7 @@ struct config_s { */ char *statpage; - vector_t access_list; + acl_list_t access_list; /* * Store the list of port allowed by CONNECT. From 58cfaf2659a4daca9bc8933f1ce135ba619557da Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 7 Sep 2020 22:09:35 +0100 Subject: [PATCH 102/265] make acl lookup 450x faster by using sblist tested with 32K acl rules, generated by for x in `seq 128` ; do for y in `seq 255` ; do \ echo "Deny 10.$x.$y.0/24" ; done ; done after loading the config (which is dogslow too), tinyproxy required 9.5 seconds for the acl check on every request. after switching the list implementation to sblist, a request with the full acl check now takes only 0.025 seconds. the time spent for loading the config file is identical for both list implementations, roughly 30 seconds. (in a previous test, 65K acl rules were generated, but every connection required almost 2 minutes to crunch through the list...) --- src/acl.c | 18 +++++++++--------- src/acl.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/acl.c b/src/acl.c index 9e905d7e..75216c3e 100644 --- a/src/acl.c +++ b/src/acl.c @@ -28,7 +28,7 @@ #include "log.h" #include "network.h" #include "sock.h" -#include "vector.h" +#include "sblist.h" #include @@ -114,7 +114,7 @@ fill_netmask_array (char *bitmask_string, int v6, static int init_access_list(acl_list_t *access_list) { if (!*access_list) { - *access_list = vector_create (); + *access_list = sblist_new(sizeof(struct acl_s), 16); if (!*access_list) { log_message (LOG_ERR, "Unable to allocate memory for access list"); @@ -207,8 +207,8 @@ insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list) } } - ret = vector_append (*access_list, &acl, sizeof (struct acl_s)); - return ret; + if(!sblist_add(*access_list, &acl)) return -1; + return 0; } /* @@ -353,8 +353,8 @@ int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_lis is_numeric_addr = (full_inet_pton (ip, &numeric_addr) > 0); - for (i = 0; i != (size_t) vector_length (access_list); ++i) { - acl = (struct acl_s *) vector_getentry (access_list, i, NULL); + for (i = 0; i < sblist_getsize (access_list); ++i) { + acl = sblist_get (access_list, i); switch (acl->type) { case ACL_STRING: perm = acl_string_processing (acl, ip, addr, string_addr); @@ -402,12 +402,12 @@ void flush_access_list (acl_list_t access_list) * before we can free the acl entries themselves. * A hierarchical memory system would be great... */ - for (i = 0; i != (size_t) vector_length (access_list); ++i) { - acl = (struct acl_s *) vector_getentry (access_list, i, NULL); + for (i = 0; i < sblist_getsize (access_list); ++i) { + acl = sblist_get (access_list, i); if (acl->type == ACL_STRING) { safefree (acl->address.string); } } - vector_delete (access_list); + sblist_free (access_list); } diff --git a/src/acl.h b/src/acl.h index 495a4b3e..867e6f0b 100644 --- a/src/acl.h +++ b/src/acl.h @@ -21,11 +21,11 @@ #ifndef TINYPROXY_ACL_H #define TINYPROXY_ACL_H -#include "vector.h" +#include "sblist.h" #include "sock.h" typedef enum { ACL_ALLOW, ACL_DENY } acl_access_t; -typedef vector_t acl_list_t; +typedef sblist* acl_list_t; extern int insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list); From 78cc5b72b18a3c0d196126bfbc5d3b6473386da9 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 8 Sep 2020 14:36:22 +0100 Subject: [PATCH 103/265] get_request_entity: fix regression w/ CONNECT method introduced in 88153e944f7d28f57cccc77f3228a3f54f78ce4e. when connect method is used (HTTPS), and e.g. a filtered domain requested, there's no data on readfds, only on writefds. this caused the response from the connection to hang until the timeout was hit. in the past in such scenario always a "no entity" response was produced in tinyproxy logs. --- src/reqs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 87a1a828..370b3758 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1485,14 +1485,15 @@ static int get_request_entity(struct conn_s *connptr) { int ret; - fd_set rset; + fd_set rset, wset; struct timeval tv; FD_ZERO (&rset); FD_SET (connptr->client_fd, &rset); + memcpy(&wset, &rset, sizeof wset); tv.tv_sec = config->idletimeout; tv.tv_usec = 0; - ret = select (connptr->client_fd + 1, &rset, NULL, NULL, &tv); + ret = select (connptr->client_fd + 1, &rset, &wset, NULL, &tv); if (ret == -1) { log_message (LOG_ERR, @@ -1514,6 +1515,8 @@ get_request_entity(struct conn_s *connptr) nread); ret = 0; } + } else if (ret == 1 && FD_ISSET (connptr->client_fd, &wset) && connptr->connect_method) { + ret = 0; } else { log_message (LOG_ERR, "strange situation after select: " "ret = %d, but client_fd (%d) is not readable...", From b4e3f1a896989d1e5190e114b5bc05cd613ef646 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 9 Sep 2020 11:59:40 +0100 Subject: [PATCH 104/265] fix negative timeout resulting in select() EINVAL --- src/reqs.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 370b3758..074b416e 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1177,13 +1177,18 @@ static void relay_connection (struct conn_s *connptr) last_access = time (NULL); for (;;) { - FD_ZERO (&rset); - FD_ZERO (&wset); - tv.tv_sec = config->idletimeout - difftime (time (NULL), last_access); tv.tv_usec = 0; + if (tv.tv_sec < 0) { + tdiff = config->idletimeout + 1; + goto e_timedout; + } + + FD_ZERO (&rset); + FD_ZERO (&wset); + if (buffer_size (connptr->sbuffer) > 0) FD_SET (connptr->client_fd, &wset); if (buffer_size (connptr->cbuffer) > 0) @@ -1198,6 +1203,7 @@ static void relay_connection (struct conn_s *connptr) if (ret == 0) { tdiff = difftime (time (NULL), last_access); if (tdiff > config->idletimeout) { + e_timedout:; log_message (LOG_INFO, "Idle Timeout (after select) as %g > %u.", tdiff, config->idletimeout); From b549ba5af38786319f27ce7abb40c610f11cdea9 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 9 Sep 2020 12:31:19 +0100 Subject: [PATCH 105/265] remove bogus custom timeout handling code in networking, hitting a timeout requires that *nothing* happens during the interval. whenever anything happens, the timeout is reset. there's no need to do custom time calculations, it's perfectly fine to let the kernel handle it using the select() syscall. additionally the code added in 0b9a74c29036f9215b2b97a301b7b25933054302 assures that read and write syscalls() don't block indefinitely and return on the timeout too, so there's no need to switch sockets back and forth between blocking/nonblocking. --- src/child.c | 16 ---------------- src/reqs.c | 54 +++-------------------------------------------------- 2 files changed, 3 insertions(+), 67 deletions(-) diff --git a/src/child.c b/src/child.c index 15d4fe86..9d59d20a 100644 --- a/src/child.c +++ b/src/child.c @@ -130,14 +130,6 @@ void child_main_loop (void) for (i = 0; i < vector_length(listen_fds); i++) { int *fd = (int *) vector_getentry(listen_fds, i, NULL); - ret = socket_nonblocking(*fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the listening " - "socket %d to non-blocking: %s", - fd, strerror(errno)); - continue; - } - FD_SET(*fd, &rfds); maxfd = max(maxfd, *fd); } @@ -175,14 +167,6 @@ void child_main_loop (void) continue; } - ret = socket_blocking(listenfd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set listening " - "socket %d to blocking for accept: %s", - listenfd, strerror(errno)); - continue; - } - /* * We have a socket that is readable. * Continue handling this connection. diff --git a/src/reqs.c b/src/reqs.c index 074b416e..fb914ebb 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1154,38 +1154,14 @@ 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 (;;) { - tv.tv_sec = - config->idletimeout - difftime (time (NULL), last_access); + tv.tv_sec = config->idletimeout; tv.tv_usec = 0; - if (tv.tv_sec < 0) { - tdiff = config->idletimeout + 1; - goto e_timedout; - } - FD_ZERO (&rset); FD_ZERO (&wset); @@ -1201,16 +1177,9 @@ static void relay_connection (struct conn_s *connptr) ret = select (maxfd, &rset, &wset, NULL, &tv); if (ret == 0) { - tdiff = difftime (time (NULL), last_access); - if (tdiff > config->idletimeout) { - e_timedout:; - log_message (LOG_INFO, - "Idle Timeout (after select) as %g > %u.", - tdiff, config->idletimeout); + log_message (LOG_INFO, + "Idle Timeout (after select)"); return; - } else { - continue; - } } else if (ret < 0) { log_message (LOG_ERR, "relay_connection: select() error \"%s\". " @@ -1218,11 +1187,6 @@ static void relay_connection (struct conn_s *connptr) 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)) { @@ -1249,18 +1213,6 @@ static void relay_connection (struct conn_s *connptr) } } - /* - * 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; From e94cbdb3a5694b9dfe1f0dcfcef21a38631f7eb8 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 10 Sep 2020 14:37:56 +0100 Subject: [PATCH 106/265] handle_connection(): factor out failure code this allows us in a next step to replace goto fail with a call to that function, so we can see in a backtrace from where the failure was triggered. --- src/reqs.c | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index fb914ebb..cfeefcf9 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1485,6 +1485,31 @@ get_request_entity(struct conn_s *connptr) return ret; } +static void handle_connection_failure(struct conn_s *connptr) +{ + /* + * 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); + } +} + /* * This is the main drive for each connection. As you can tell, for the @@ -1712,27 +1737,7 @@ void handle_connection (int fd, union sockaddr_union* addr) 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); - } + handle_connection_failure(connptr); done: free_request_struct (request); From f1bd259e6e5c284a1068bb00c5cf1cc70d550b0a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 10 Sep 2020 14:48:39 +0100 Subject: [PATCH 107/265] handle_connection: replace "goto fail" with func call this allows to see in a backtrace from where the error was triggered. --- src/reqs.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index cfeefcf9..cec7ced8 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1522,6 +1522,10 @@ static void handle_connection_failure(struct conn_s *connptr) */ void handle_connection (int fd, union sockaddr_union* addr) { + +#define HC_FAIL() \ + do {handle_connection_failure(connptr); goto done;} while(0) + ssize_t i; struct conn_s *connptr; struct request_s *request = NULL; @@ -1565,7 +1569,7 @@ void handle_connection (int fd, union sockaddr_union* addr) "You tried to connect to the " "machine the proxy is running on", NULL); - goto fail; + HC_FAIL(); } @@ -1576,7 +1580,7 @@ void handle_connection (int fd, union sockaddr_union* addr) "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) < 0) { @@ -1585,7 +1589,7 @@ void handle_connection (int fd, union sockaddr_union* addr) "detail", "Server timeout waiting for the HTTP request " "from the client.", NULL); - goto fail; + HC_FAIL(); } /* @@ -1599,7 +1603,7 @@ void handle_connection (int fd, union sockaddr_union* addr) "An internal server error occurred while processing " "your request. Please contact the administrator.", NULL); - goto fail; + HC_FAIL(); } /* @@ -1613,7 +1617,7 @@ void handle_connection (int fd, union sockaddr_union* addr) "Could not retrieve all the headers from " "the client.", NULL); update_stats (STAT_BADCONN); - goto fail; + HC_FAIL(); } if (config->basicauth_list != NULL) { @@ -1640,7 +1644,7 @@ void handle_connection (int fd, union sockaddr_union* addr) "detail", "This proxy requires authentication.", NULL); - goto fail; + HC_FAIL(); } if ( /* currently only "basic" auth supported */ (strncmp(authstring, "Basic ", 6) == 0 || @@ -1655,7 +1659,7 @@ void handle_connection (int fd, union sockaddr_union* addr) "The administrator of this proxy has not configured " "it to service requests from you.", NULL); - goto fail; + HC_FAIL(); } hashmap_remove (hashofheaders, "proxy-authorization"); } @@ -1678,13 +1682,13 @@ void handle_connection (int fd, union sockaddr_union* addr) 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, @@ -1695,7 +1699,7 @@ void handle_connection (int fd, union sockaddr_union* addr) PACKAGE_NAME " " "was unable to connect to the remote web server.", "error", strerror (errno), NULL); - goto fail; + HC_FAIL(); } log_message (LOG_CONN, @@ -1709,13 +1713,13 @@ void handle_connection (int fd, union sockaddr_union* addr) if (process_client_headers (connptr, hashofheaders) < 0) { update_stats (STAT_BADCONN); - goto fail; + HC_FAIL(); } if (!connptr->connect_method || UPSTREAM_IS_HTTP(connptr)) { if (process_server_headers (connptr) < 0) { update_stats (STAT_BADCONN); - goto fail; + HC_FAIL(); } } else { if (send_ssl_response (connptr) < 0) { @@ -1723,7 +1727,7 @@ void handle_connection (int fd, union sockaddr_union* addr) "handle_connection: Could not send SSL greeting " "to client."); update_stats (STAT_BADCONN); - goto fail; + HC_FAIL(); } } @@ -1734,14 +1738,10 @@ void handle_connection (int fd, union sockaddr_union* addr) "and remote client (fd:%d)", connptr->client_fd, connptr->server_fd); - goto done; - -fail: - handle_connection_failure(connptr); - done: free_request_struct (request); hashmap_delete (hashofheaders); destroy_conn (connptr); return; +#undef HC_FAIL } From 9e40f8311fc4f6b3238cd4fc47977cc49adc5481 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 10 Sep 2020 21:12:46 +0100 Subject: [PATCH 108/265] handle_connection(): print process_*_headers errno information --- src/reqs.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/reqs.c b/src/reqs.c index cec7ced8..514bc215 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1713,12 +1713,24 @@ void handle_connection (int fd, union sockaddr_union* addr) if (process_client_headers (connptr, hashofheaders) < 0) { update_stats (STAT_BADCONN); + 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); + 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 { From df9074db6e68fbf7847fab0bc33b3a3c9a94620d Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 12 Sep 2020 15:54:48 +0100 Subject: [PATCH 109/265] vector.h: missing include for ssize_t --- src/vector.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vector.h b/src/vector.h index ef8f9535..45a78d47 100644 --- a/src/vector.h +++ b/src/vector.h @@ -21,6 +21,8 @@ #ifndef _VECTOR_H #define _VECTOR_H +#include + /* * We're using a typedef here to "hide" the implementation details of the * vector. Sure, it's a pointer, but the struct is hidden in the C file. From 4847d8cdb3bfd9b30a10bfed848174250475a69b Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 12 Sep 2020 21:00:17 +0100 Subject: [PATCH 110/265] add_new_errorpage(): fix segfault accessing global config another fallout of the config refactoring finished by 2e02dce0c3de4a231f74b44c34647406de507768. apparently no one using the ErrorFile directive used git master during the last months, as there have been no reports about this issue. --- src/conf.c | 2 +- src/html-error.c | 9 +++++---- src/html-error.h | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/conf.c b/src/conf.c index bf2458f3..0d164a61 100644 --- a/src/conf.c +++ b/src/conf.c @@ -815,7 +815,7 @@ static HANDLE_FUNC (handle_errorfile) unsigned long int err = get_long_arg (line, &match[2]); char *page = get_string_arg (line, &match[4]); - add_new_errorpage (page, err); + add_new_errorpage (conf, page, err); safefree (page); return 0; } diff --git a/src/html-error.c b/src/html-error.c index c94dbd70..d643aaa3 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -37,17 +37,18 @@ #define ERRORNUM_BUFSIZE 8 /* this is more than required */ #define ERRPAGES_BUCKETCOUNT 16 -int add_new_errorpage (char *filepath, unsigned int errornum) +int add_new_errorpage (struct config_s *conf, char *filepath, + unsigned int errornum) { char errornbuf[ERRORNUM_BUFSIZE]; - config->errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT); - if (!config->errorpages) + conf->errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT); + if (!conf->errorpages) return (-1); snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum); - if (hashmap_insert (config->errorpages, errornbuf, + if (hashmap_insert (conf->errorpages, errornbuf, filepath, strlen (filepath) + 1) < 0) return (-1); diff --git a/src/html-error.h b/src/html-error.h index 03cec988..c133cef8 100644 --- a/src/html-error.h +++ b/src/html-error.h @@ -23,8 +23,9 @@ /* Forward declaration */ struct conn_s; +struct config_s; -extern int add_new_errorpage (char *filepath, unsigned int errornum); +extern int add_new_errorpage (struct config_s *, char *filepath, unsigned int errornum); extern int send_http_error_message (struct conn_s *connptr); extern int indicate_http_error (struct conn_s *connptr, int number, const char *message, ...); From bfe59856b2ebbb09e8fc36d6a3af5521eec2b98b Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 13 Sep 2020 00:33:16 +0100 Subject: [PATCH 111/265] tests/webclient: return error when HTTP status > 399 --- tests/scripts/webclient.pl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/scripts/webclient.pl b/tests/scripts/webclient.pl index 25c0fb79..4dddb69e 100755 --- a/tests/scripts/webclient.pl +++ b/tests/scripts/webclient.pl @@ -122,11 +122,16 @@ ($$$$$$) print $remote $request; + $_ = <$remote>; + print; # /* HTTP/1.0 400 Bad Request */ + my($errn) = ($_ =~ /HTTP\/\d\.\d (\d{3})/); + while (<$remote>) { print; } close $remote; + exit($errn - 399) if($errn > 399); } exit(0); From c64ac9edbeda411550e0dbeec731639b29c66b3c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 13 Sep 2020 00:19:46 +0100 Subject: [PATCH 112/265] fix get_request_entity() get_request_entity()'s purpose is to drain remaining unread bytes in the request read pipe before handing out an error page, and kinda surprisingly, also when connection to the stathost is done. in the stathost case tinyproxy just skipped proper processing and jumped to the error handler code, and remembering whether a connection to the stathost was desired in a variable, then doing things a bit differently depending on whether it's set. i tried to fix issues with get_request_entity in 88153e944f7d28f57cccc77f3228a3f54f78ce4e (which is basically the right fix for the issue it tried to solve, but incomplete), and resulting from there in 78cc5b72b18a3c0d196126bfbc5d3b6473386da9. the latter fix wasn't quite right since we're not supposed to check whether the socket is ready for writing, and having a return value of 2 instead of 1 got resulted in some of the if statements not kicking in when they should have. this also resulted in the stathost page no longer working. after in-depth study of the issue i realized that we only need to call get_request_entity() when the headers aren't completely read, additional to setting the proper connection timeout as 88153e944f7d28f57cccc77f3228a3f54f78ce4e already implemented. the changes of 78cc5b72b18a3c0d196126bfbc5d3b6473386da9 have been reverted. --- src/reqs.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 514bc215..e3c59c16 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1439,19 +1439,21 @@ 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, wset; + fd_set rset; struct timeval tv; FD_ZERO (&rset); FD_SET (connptr->client_fd, &rset); - memcpy(&wset, &rset, sizeof wset); tv.tv_sec = config->idletimeout; tv.tv_usec = 0; - ret = select (connptr->client_fd + 1, &rset, &wset, NULL, &tv); + ret = select (connptr->client_fd + 1, &rset, NULL, NULL, &tv); if (ret == -1) { log_message (LOG_ERR, @@ -1473,8 +1475,6 @@ get_request_entity(struct conn_s *connptr) nread); ret = 0; } - } else if (ret == 1 && FD_ISSET (connptr->client_fd, &wset) && connptr->connect_method) { - ret = 0; } else { log_message (LOG_ERR, "strange situation after select: " "ret = %d, but client_fd (%d) is not readable...", @@ -1485,7 +1485,7 @@ get_request_entity(struct conn_s *connptr) return ret; } -static void handle_connection_failure(struct conn_s *connptr) +static void handle_connection_failure(struct conn_s *connptr, int got_headers) { /* * First, get the body if there is one. @@ -1493,7 +1493,7 @@ static void handle_connection_failure(struct conn_s *connptr) * it is still marked for reading and we won't be able * to send our data properly. */ - if (get_request_entity (connptr) < 0) { + if (!got_headers && get_request_entity (connptr) < 0) { log_message (LOG_WARNING, "Could not retrieve request entity"); indicate_http_error (connptr, 400, "Bad Request", @@ -1524,8 +1524,10 @@ void handle_connection (int fd, union sockaddr_union* addr) { #define HC_FAIL() \ - do {handle_connection_failure(connptr); goto done;} while(0) + do {handle_connection_failure(connptr, got_headers); goto done;} \ + while(0) + int got_headers = 0; ssize_t i; struct conn_s *connptr; struct request_s *request = NULL; @@ -1619,6 +1621,7 @@ void handle_connection (int fd, union sockaddr_union* addr) update_stats (STAT_BADCONN); HC_FAIL(); } + got_headers = 1; if (config->basicauth_list != NULL) { ssize_t len; From 17e19a67cfd2af4d05d50981d3a09a46bec684ac Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 13 Sep 2020 01:04:00 +0100 Subject: [PATCH 113/265] run_tests: do some more extensive testing 1) force a config reload after some initial tests. this will allow to identify memleaks using the valgrind test, as this will free all structures allocated for the config, and recreate them. 2) test ErrorFile directive by adding several of them. this should help catch regressions such as the one fixed in 4847d8cdb3bfd9b30a10bfed848174250475a69b. it will also test memleaks in the related code paths. 3) test some scenarios that should produce errors and use the configured ErrorFile directives. --- tests/scripts/run_tests.sh | 61 +++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index 799c9d65..b7214142 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -80,6 +80,9 @@ Listen $TINYPROXY_IP Timeout 600 StatHost "$TINYPROXY_STATHOST_IP" DefaultErrorFile "$TINYPROXY_DATA_DIR/debug.html" +ErrorFile 400 "$TINYPROXY_DATA_DIR/debug.html" +ErrorFile 403 "$TINYPROXY_DATA_DIR/debug.html" +ErrorFile 501 "$TINYPROXY_DATA_DIR/debug.html" StatFile "$TINYPROXY_DATA_DIR/stats.html" Logfile "$TINYPROXY_LOG_FILE" PidFile "$TINYPROXY_PID_FILE" @@ -90,12 +93,14 @@ ViaProxyName "tinyproxy" #DisableViaHeader Yes ConnectPort 443 ConnectPort 563 -FilterURLs On +#FilterURLs On Filter "$TINYPROXY_FILTER_FILE" XTinyproxy Yes EOF - touch $TINYPROXY_FILTER_FILE +cat << 'EOF' > $TINYPROXY_FILTER_FILE +.*\.google-analytics\.com$ +EOF } start_tinyproxy() { @@ -104,6 +109,13 @@ start_tinyproxy() { echo " done (listening on $TINYPROXY_IP:$TINYPROXY_PORT)" } +reload_config() { + echo -n "signaling tinyproxy to reload config..." + pid=$(cat $TINYPROXY_PID_FILE) + kill -s SIGHUP $pid + echo +} + stop_tinyproxy() { echo -n "killing tinyproxy..." pid=$(cat $TINYPROXY_PID_FILE) @@ -157,7 +169,7 @@ wait_for_some_seconds() { } run_basic_webclient_request() { - $WEBCLIENT_BIN $1 $2 >> $WEBCLIENT_LOG 2>&1 + $WEBCLIENT_BIN $1 $2 > $WEBCLIENT_LOG 2>&1 WEBCLIENT_EXIT_CODE=$? if test "x$WEBCLIENT_EXIT_CODE" = "x0" ; then echo " ok" @@ -165,11 +177,31 @@ run_basic_webclient_request() { echo "ERROR ($WEBCLIENT_EXIT_CODE)" echo "webclient output:" cat $WEBCLIENT_LOG + echo "######################################" fi return $WEBCLIENT_EXIT_CODE } +run_failure_webclient_request() { + ec=$1 + expected_error=$(($1 - 399)) + shift + $WEBCLIENT_BIN "$1" "$2" "$3" "$4" > $WEBCLIENT_LOG 2>&1 + WEBCLIENT_EXIT_CODE=$? + if test "x$WEBCLIENT_EXIT_CODE" = "x$expected_error" ; then + echo " ok, got expected error code $ec" + return 0 + else + echo "ERROR ($WEBCLIENT_EXIT_CODE)" + echo "webclient output:" + cat $WEBCLIENT_LOG + echo "######################################" + fi + + return 1 +} + # "main" provision_initial @@ -179,10 +211,11 @@ provision_webserver start_webserver start_tinyproxy -wait_for_some_seconds 3 +wait_for_some_seconds 1 FAILED=0 +basic_test() { echo -n "checking direct connection to web server..." run_basic_webclient_request "$WEBSERVER_IP:$WEBSERVER_PORT" / test "x$?" = "x0" || FAILED=$((FAILED + 1)) @@ -194,6 +227,26 @@ test "x$?" = "x0" || FAILED=$((FAILED + 1)) echo -n "requesting statspage via stathost url..." run_basic_webclient_request "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$TINYPROXY_STATHOST_IP" test "x$?" = "x0" || FAILED=$((FAILED + 1)) +} + +ext_test() { +echo -n "checking bogus request..." +run_failure_webclient_request 400 --method="BIG FART" "$TINYPROXY_IP:$TINYPROXY_PORT" "http://$WEBSERVER_IP:$WEBSERVER_PORT" +test "x$?" = "x0" || FAILED=$((FAILED + 1)) + +echo -n "testing connection to filtered domain..." +run_failure_webclient_request 403 "$TINYPROXY_IP:$TINYPROXY_PORT" "http://badgoy.google-analytics.com/" +test "x$?" = "x0" || FAILED=$((FAILED + 1)) + +echo -n "requesting connect method to denied port..." +run_failure_webclient_request 403 --method=CONNECT "$TINYPROXY_IP:$TINYPROXY_PORT" "localhost:12345" +test "x$?" = "x0" || FAILED=$((FAILED + 1)) +} + +basic_test +reload_config +basic_test +ext_test echo "$FAILED errors" From ff23f3249b775713da7ded13ac1e91526645718e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 13 Sep 2020 01:27:41 +0100 Subject: [PATCH 114/265] conf.c: include common.h --- src/conf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/conf.c b/src/conf.c index 0d164a61..b7c8da4d 100644 --- a/src/conf.c +++ b/src/conf.c @@ -23,6 +23,7 @@ * add new directives to. Who knows if I'm right though. */ +#include "common.h" #include "conf.h" #include "acl.h" From 7014d050d9738ae5ed8cd92e19a0cbb91d703025 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 13 Sep 2020 01:31:08 +0100 Subject: [PATCH 115/265] run_tests: make travis happy, use signal nr instead of name --- tests/scripts/run_tests.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index b7214142..3cb139ad 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -112,8 +112,8 @@ start_tinyproxy() { reload_config() { echo -n "signaling tinyproxy to reload config..." pid=$(cat $TINYPROXY_PID_FILE) - kill -s SIGHUP $pid - echo + #1: SIGHUP + kill -1 $pid && echo "ok" || echo "fail" } stop_tinyproxy() { From 7eb6600aeb5226b0035616563fc560cb16b42df2 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 14 Sep 2020 20:55:52 +0100 Subject: [PATCH 116/265] main: orderly shutdown on SIGINT too the appropriate code in the signal handler was already set up, but for some reason the signal itself not being handled. --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index 7b702ab9..0f445f6b 100644 --- a/src/main.c +++ b/src/main.c @@ -388,6 +388,7 @@ main (int argc, char **argv) setup_sig (SIGCHLD, takesig, "SIGCHLD", argv[0]); setup_sig (SIGTERM, takesig, "SIGTERM", argv[0]); + setup_sig (SIGINT, takesig, "SIGINT", argv[0]); if (daemonized) setup_sig (SIGHUP, takesig, "SIGHUP", argv[0]); setup_sig (SIGUSR1, takesig, "SIGUSR1", argv[0]); From 6dd3806f7d1a337fb89e335e986e1fa4eab8340c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 19:09:43 +0100 Subject: [PATCH 117/265] childs.c: fix minor memory leak this would leak only once on program termination, so it's no big deal apart from having spurious reachable memory in valgrind logs. --- src/child.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/child.c b/src/child.c index 9d59d20a..fb05cbe1 100644 --- a/src/child.c +++ b/src/child.c @@ -217,6 +217,7 @@ void child_main_loop (void) goto oom; } } + sblist_free(childs); } /* From 2f3a3828accfc6fca0e6f35e1bf28bace7f5851a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 22:25:28 +0100 Subject: [PATCH 118/265] Revert "childs.c: fix minor memory leak" This reverts commit 6dd3806f7d1a337fb89e335e986e1fa4eab8340c. --- src/child.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/child.c b/src/child.c index fb05cbe1..9d59d20a 100644 --- a/src/child.c +++ b/src/child.c @@ -217,7 +217,6 @@ void child_main_loop (void) goto oom; } } - sblist_free(childs); } /* From 372d7ff8241888bc3895182b39497fa23381b7e7 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 22:32:25 +0100 Subject: [PATCH 119/265] shutdown: free children from right place --- src/child.c | 4 ++++ src/child.h | 1 + src/main.c | 1 + 3 files changed, 6 insertions(+) diff --git a/src/child.c b/src/child.c index 9d59d20a..3b1800f7 100644 --- a/src/child.c +++ b/src/child.c @@ -245,6 +245,10 @@ void child_kill_children (int sig) ); } +void child_free_children(void) { + sblist_free(childs); + childs = 0; +} /** * Listen on the various configured interfaces diff --git a/src/child.h b/src/child.h index 70f52b7f..efc6fe3d 100644 --- a/src/child.h +++ b/src/child.h @@ -36,6 +36,7 @@ extern int child_listening_sockets (vector_t listen_addrs, uint16_t port); extern void child_close_sock (void); extern void child_main_loop (void); extern void child_kill_children (int sig); +extern void child_free_children(void); extern short int child_configure (child_config_t type, unsigned int val); diff --git a/src/main.c b/src/main.c index 0f445f6b..2d695ed6 100644 --- a/src/main.c +++ b/src/main.c @@ -401,6 +401,7 @@ main (int argc, char **argv) child_kill_children (SIGTERM); child_close_sock (); + child_free_children(); /* Remove the PID file */ if (config->pidpath != NULL && unlink (config->pidpath) < 0) { From 9d5ee85c3ecc01d679ff23f25fa0efbf6743e7a2 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 23:01:44 +0100 Subject: [PATCH 120/265] fix free()ing of config items - we need to free the config after it has been succesfully loaded, not unconditionally before reloading. - we also need to free them before exiting from the main program to have clean valgrind output. --- src/conf.c | 4 +--- src/conf.h | 1 + src/main.c | 3 +++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/conf.c b/src/conf.c index b7c8da4d..f0db5006 100644 --- a/src/conf.c +++ b/src/conf.c @@ -286,7 +286,7 @@ free_added_headers (vector_t add_headers) vector_delete (add_headers); } -static void free_config (struct config_s *conf) +void free_config (struct config_s *conf) { safefree (conf->logf_name); safefree (conf->stathost); @@ -467,8 +467,6 @@ int reload_config_file (const char *config_fname, struct config_s *conf) log_message (LOG_NOTICE, "Reloading config file"); - free_config (conf); - initialize_config_defaults (conf); ret = load_config_file (config_fname, conf); diff --git a/src/conf.h b/src/conf.h index e89806a5..8433381d 100644 --- a/src/conf.h +++ b/src/conf.h @@ -116,5 +116,6 @@ struct config_s { extern int reload_config_file (const char *config_fname, struct config_s *conf); int config_compile_regex (void); +void free_config (struct config_s *conf); #endif diff --git a/src/main.c b/src/main.c index 2d695ed6..42e80483 100644 --- a/src/main.c +++ b/src/main.c @@ -264,6 +264,7 @@ int reload_config (int reload_logging) goto done; } + if(config) free_config (config); config = c_next; if (reload_logging) ret = setup_logging (); @@ -415,6 +416,8 @@ main (int argc, char **argv) filter_destroy (); #endif /* FILTER_ENABLE */ + free_config (config); + shutdown_logging (); return EXIT_SUCCESS; From 34a8b28414de419c984f832a299383f6d7149f72 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 12 Sep 2020 13:49:10 +0100 Subject: [PATCH 121/265] save headers in an ordered dictionary due to the usage of a hashmap to store headers, when relaying them to the other side the order was not prevented. even though correct from a standards point-of-view, this caused issues with various programs, and it allows to fingerprint the use of tinyproxy. to implement this, i imported the MIT-licensed hsearch.[ch] from https://github.com/rofl0r/htab which was originally taken from musl libc. it's a simple and efficient hashtable implementation with far better performance characteristic than the one previously used by tinyproxy. additionally it has an API much more well-suited for this purpose. orderedmap.[ch] was implemented from scratch to address this issue. behind the scenes it uses an sblist to store string values, and a htab to store keys and the indices into the sblist. this allows us to iterate linearly over the sblist and then find the corresponding key in the hash table, so the headers can be reproduced in the order they were received. closes #73 --- src/Makefile.am | 2 + src/hsearch.c | 199 ++++++++++++++++++++++++++++++++++++++++ src/hsearch.h | 21 +++++ src/orderedmap.c | 110 ++++++++++++++++++++++ src/orderedmap.h | 20 ++++ src/reqs.c | 159 ++++++++++++++------------------ src/reverse-proxy.c | 7 +- src/reverse-proxy.h | 3 +- src/transparent-proxy.c | 7 +- src/transparent-proxy.h | 4 +- 10 files changed, 434 insertions(+), 98 deletions(-) create mode 100644 src/hsearch.c create mode 100644 src/hsearch.h create mode 100644 src/orderedmap.c create mode 100644 src/orderedmap.h diff --git a/src/Makefile.am b/src/Makefile.am index 50e645ba..c3b8dca6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -49,6 +49,8 @@ tinyproxy_SOURCES = \ basicauth.c basicauth.h \ base64.c base64.h \ sblist.c sblist.h \ + hsearch.c hsearch.h \ + orderedmap.c orderedmap.h \ loop.c loop.h \ connect-ports.c connect-ports.h diff --git a/src/hsearch.c b/src/hsearch.c new file mode 100644 index 00000000..0d022a2b --- /dev/null +++ b/src/hsearch.c @@ -0,0 +1,199 @@ +/* +musl license, hsearch.c originally written by Szabolcs Nagy + +Copyright © 2005-2020 Rich Felker, et al. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include "hsearch.h" + +/* +open addressing hash table with 2^n table size +quadratic probing is used in case of hash collision +tab indices and hash are size_t +after resize fails with ENOMEM the state of tab is still usable +*/ + +typedef struct htab_entry { + char *key; + htab_value data; +} htab_entry; + +struct elem { + htab_entry item; + size_t hash; +}; + +struct htab { + struct elem *elems; + size_t mask; + size_t used; +}; + +#define MINSIZE 8 +#define MAXSIZE ((size_t)-1/2 + 1) + +#define CASE_INSENSITIVE +#ifdef CASE_INSENSITIVE +#include +#include +#define LOWER_OR_NOT(X) tolower(X) +#define STRCMP(X, Y) strcasecmp(X, Y) +#else +#define LOWER_OR_NOT(X) X +#define STRCMP(X, Y) strcmp(X, Y) +#endif + +static size_t keyhash(const char *k) +{ + const unsigned char *p = (const void *)k; + size_t h = 0; + + while (*p) + h = 31*h + LOWER_OR_NOT(*p++); + return h; +} + +static int resize(struct htab *htab, size_t nel) +{ + size_t newsize; + size_t i, j; + struct elem *e, *newe; + struct elem *oldtab = htab->elems; + struct elem *oldend = htab->elems + htab->mask + 1; + + if (nel > MAXSIZE) + nel = MAXSIZE; + for (newsize = MINSIZE; newsize < nel; newsize *= 2); + htab->elems = calloc(newsize, sizeof *htab->elems); + if (!htab->elems) { + htab->elems = oldtab; + return 0; + } + htab->mask = newsize - 1; + if (!oldtab) + return 1; + for (e = oldtab; e < oldend; e++) + if (e->item.key) { + for (i=e->hash,j=1; ; i+=j++) { + newe = htab->elems + (i & htab->mask); + if (!newe->item.key) + break; + } + *newe = *e; + } + free(oldtab); + return 1; +} + +static struct elem *lookup(struct htab *htab, const char *key, size_t hash) +{ + size_t i, j; + struct elem *e; + + for (i=hash,j=1; ; i+=j++) { + e = htab->elems + (i & htab->mask); + if (!e->item.key || + (e->hash==hash && STRCMP(e->item.key, key)==0)) + break; + } + return e; +} + +struct htab *htab_create(size_t nel) +{ + struct htab *r = calloc(1, sizeof *r); + if(r && !resize(r, nel)) { + free(r); + r = 0; + } + return r; +} + +void htab_destroy(struct htab *htab) +{ + free(htab->elems); + free(htab); +} + +static htab_entry *htab_find_item(struct htab *htab, const char* key) +{ + size_t hash = keyhash(key); + struct elem *e = lookup(htab, key, hash); + + if (e->item.key) { + return &e->item; + } + return 0; +} + +htab_value* htab_find(struct htab *htab, const char* key) +{ + htab_entry *i = htab_find_item(htab, key); + if(i) return &i->data; + return 0; +} + +int htab_delete(struct htab *htab, const char* key) +{ + htab_entry *i = htab_find_item(htab, key); + if(!i) return 0; + i->key = 0; + return 1; +} + +int htab_insert(struct htab *htab, char* key, htab_value value) +{ + size_t hash = keyhash(key); + struct elem *e = lookup(htab, key, hash); + if(e->item.key) { + /* it's not allowed to overwrite existing data */ + return 0; + } + + e->item.key = key; + e->item.data = value; + e->hash = hash; + if (++htab->used > htab->mask - htab->mask/4) { + if (!resize(htab, 2*htab->used)) { + htab->used--; + e->item.key = 0; + return 0; + } + } + return 1; +} + +size_t htab_next(struct htab *htab, size_t iterator, char** key, htab_value **v) +{ + size_t i; + for(i=iterator;imask+1;++i) { + struct elem *e = htab->elems + i; + if(e->item.key) { + *key = e->item.key; + *v = &e->item.data; + return i+1; + } + } + return 0; +} diff --git a/src/hsearch.h b/src/hsearch.h new file mode 100644 index 00000000..ec81cc31 --- /dev/null +++ b/src/hsearch.h @@ -0,0 +1,21 @@ +#ifndef HSEARCH_H +#define HSEARCH_H + +#include + +typedef union htab_value { + void *p; + size_t n; +} htab_value; + +#define HTV_N(N) (htab_value) {.n = N} +#define HTV_P(P) (htab_value) {.p = P} + +struct htab * htab_create(size_t); +void htab_destroy(struct htab *); +htab_value* htab_find(struct htab *, const char* key); +int htab_insert(struct htab *, char*, htab_value); +int htab_delete(struct htab *htab, const char* key); +size_t htab_next(struct htab *, size_t iterator, char** key, htab_value **v); + +#endif diff --git a/src/orderedmap.c b/src/orderedmap.c new file mode 100644 index 00000000..4902be03 --- /dev/null +++ b/src/orderedmap.c @@ -0,0 +1,110 @@ +#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; + htab_value *lv, *v = htab_find(o->map, key); + if(!v) return 0; + htab_delete(o->map, key); + sblist_delete(o->values, v->n); + i = 0; + while((i = htab_next(o->map, i, &lk, &lv))) { + if(lv->n > v->n) lv->n--; + } + 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 new file mode 100644 index 00000000..e3f4b0eb --- /dev/null +++ b/src/orderedmap.h @@ -0,0 +1,20 @@ +#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/reqs.c b/src/reqs.c index e3c59c16..47bb3048 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -33,6 +33,7 @@ #include "conns.h" #include "filter.h" #include "hashmap.h" +#include "orderedmap.h" #include "heap.h" #include "html-error.h" #include "log.h" @@ -315,7 +316,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; @@ -596,7 +597,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; @@ -614,7 +615,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); } /* @@ -627,7 +628,7 @@ add_header_to_connection (hashmap_t hashofheaders, char *header, size_t len) /* * Read all the headers from the stream */ -static int get_all_headers (int fd, hashmap_t hashofheaders) +static int get_all_headers (int fd, orderedmap hashofheaders) { char *line = NULL; char *header = NULL; @@ -720,7 +721,7 @@ static int get_all_headers (int fd, hashmap_t hashofheaders) * 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", @@ -734,12 +735,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. @@ -754,7 +756,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; @@ -763,7 +765,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; @@ -773,16 +775,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; @@ -796,10 +796,9 @@ 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; @@ -819,14 +818,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", @@ -840,7 +839,7 @@ write_via_header (int fd, hashmap_t hashofheaders, /* * 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 @@ -849,7 +848,7 @@ write_via_header (int fd, hashmap_t hashofheaders, * - 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", @@ -860,7 +859,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; @@ -893,7 +892,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 */ @@ -913,27 +912,22 @@ 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 (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; - } + 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; } } } @@ -973,8 +967,8 @@ 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; @@ -1003,7 +997,7 @@ static int process_server_headers (struct conn_s *connptr) goto retry; } - hashofheaders = hashmap_create (HEADER_BUCKETS); + hashofheaders = orderedmap_create (HEADER_BUCKETS); if (!hashofheaders) { safefree (response_line); return -1; @@ -1015,7 +1009,7 @@ static int process_server_headers (struct conn_s *connptr) if (get_all_headers (connptr->server_fd, hashofheaders) < 0) { log_message (LOG_WARNING, "Could not retrieve all the headers from the remote server."); - hashmap_delete (hashofheaders); + orderedmap_destroy (hashofheaders); safefree (response_line); indicate_http_error (connptr, 503, @@ -1034,7 +1028,7 @@ static int process_server_headers (struct conn_s *connptr) * 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; } @@ -1061,7 +1055,7 @@ static int process_server_headers (struct conn_s *connptr) * 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 */ @@ -1083,8 +1077,7 @@ static int process_server_headers (struct conn_s *connptr) /* Rewrite the HTTP redirect if needed */ if (config->reversebaseurl && - hashmap_entry_by_key (hashofheaders, "location", - (void **) &header) > 0) { + (header = orderedmap_find (hashofheaders, "location"))) { /* Look for a matching entry in the reversepath list */ while (reverse) { @@ -1109,7 +1102,7 @@ static int process_server_headers (struct conn_s *connptr) "Rewriting HTTP redirect: %s -> %s%s%s", header, config->reversebaseurl, (reverse->path + 1), (header + len)); - hashmap_remove (hashofheaders, "location"); + orderedmap_remove (hashofheaders, "location"); } } #endif @@ -1117,19 +1110,15 @@ static int process_server_headers (struct conn_s *connptr) /* * 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) @@ -1138,7 +1127,7 @@ static int process_server_headers (struct conn_s *connptr) return 0; ERROR_EXIT: - hashmap_delete (hashofheaders); + orderedmap_destroy (hashofheaders); return -1; } @@ -1532,7 +1521,7 @@ void handle_connection (int fd, union sockaddr_union* addr) struct conn_s *connptr; struct request_s *request = NULL; struct timeval tv; - hashmap_t hashofheaders = NULL; + orderedmap hashofheaders = NULL; char sock_ipaddr[IP_LENGTH]; char peer_ipaddr[IP_LENGTH]; @@ -1597,7 +1586,7 @@ void handle_connection (int fd, union sockaddr_union* addr) /* * 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", @@ -1624,23 +1613,19 @@ void handle_connection (int fd, union sockaddr_union* addr) got_headers = 1; if (config->basicauth_list != NULL) { - ssize_t len; char *authstring; int failure = 1, stathost_connect = 0; - len = hashmap_entry_by_key (hashofheaders, "proxy-authorization", - (void **) &authstring); - - if (len == 0 && config->stathost) { - len = hashmap_entry_by_key (hashofheaders, "host", - (void **) &authstring); - if (len && !strncmp(authstring, config->stathost, strlen(config->stathost))) { - len = hashmap_entry_by_key (hashofheaders, "authorization", - (void **) &authstring); + 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 len = 0; + } 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", @@ -1664,7 +1649,7 @@ void handle_connection (int fd, union sockaddr_union* addr) NULL); HC_FAIL(); } - hashmap_remove (hashofheaders, "proxy-authorization"); + orderedmap_remove (hashofheaders, "proxy-authorization"); } /* @@ -1675,9 +1660,7 @@ void handle_connection (int fd, union sockaddr_union* addr) http_header_t *header = (http_header_t *) vector_getentry (config->add_headers, i, NULL); - hashmap_insert (hashofheaders, - header->name, - header->value, strlen (header->value) + 1); + orderedmap_append (hashofheaders, header->name, header->value); } request = process_request (connptr, hashofheaders); @@ -1755,7 +1738,7 @@ void handle_connection (int fd, union sockaddr_union* addr) done: free_request_struct (request); - hashmap_delete (hashofheaders); + orderedmap_destroy (hashofheaders); destroy_conn (connptr); return; #undef HC_FAIL diff --git a/src/reverse-proxy.c b/src/reverse-proxy.c index 32bd2a75..af58d56b 100644 --- a/src/reverse-proxy.c +++ b/src/reverse-proxy.c @@ -111,7 +111,7 @@ void free_reversepath_list (struct reversepath *reverse) /* * Rewrite the URL for reverse proxying. */ -char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders, +char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders, char *url) { char *rewrite_url = NULL; @@ -130,9 +130,8 @@ char *reverse_rewrite_url (struct conn_s *connptr, hashmap_t hashofheaders, strcpy (rewrite_url, reverse->url); strcat (rewrite_url, url + strlen (reverse->path)); } else if (config->reversemagic - && hashmap_entry_by_key (hashofheaders, - "cookie", - (void **) &cookie) > 0) { + && (cookie = orderedmap_find (hashofheaders, + "cookie"))) { /* No match - try the magical tracking cookie next */ if ((cookieval = strstr (cookie, REVERSE_COOKIE "=")) diff --git a/src/reverse-proxy.h b/src/reverse-proxy.h index 64b4acd9..a2d66198 100644 --- a/src/reverse-proxy.h +++ b/src/reverse-proxy.h @@ -22,6 +22,7 @@ #define TINYPROXY_REVERSE_PROXY_H #include "conns.h" +#include "orderedmap.h" struct reversepath { struct reversepath *next; @@ -37,6 +38,6 @@ 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, - hashmap_t hashofheaders, char *url); + orderedmap hashofheaders, char *url); #endif diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index 2c1e069a..e40bce2b 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, hashmap_t hashofheaders, +do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders, struct request_s *request, struct config_s *conf, char **url) { @@ -62,8 +62,8 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders, size_t ulen = strlen (*url); ssize_t i; - length = hashmap_entry_by_key (hashofheaders, "host", (void **) &data); - if (length <= 0) { + data = orderedmap_find (hashofheaders, "host"); + if (!data) { union sockaddr_union dest_addr; const void *dest_inaddr; char namebuf[INET6_ADDRSTRLEN+1]; @@ -102,6 +102,7 @@ do_transparent_proxy (struct conn_s *connptr, hashmap_t hashofheaders, "process_request: trans IP %s %s for %d", request->method, *url, connptr->client_fd); } else { + length = strlen (data); request->host = (char *) safemalloc (length + 1); if (sscanf (data, "%[^:]:%hu", request->host, &request->port) != 2) { diff --git a/src/transparent-proxy.h b/src/transparent-proxy.h index 4fc3a4d6..b56c446e 100644 --- a/src/transparent-proxy.h +++ b/src/transparent-proxy.h @@ -26,11 +26,11 @@ #ifdef TRANSPARENT_PROXY #include "conns.h" -#include "hashmap.h" +#include "orderedmap.h" #include "reqs.h" extern int do_transparent_proxy (struct conn_s *connptr, - hashmap_t hashofheaders, + orderedmap hashofheaders, struct request_s *request, struct config_s *config, char **url); From 155bfbbe87daae596d9c77ea3ee9bf9624afcce0 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 12 Sep 2020 21:33:59 +0100 Subject: [PATCH 122/265] replace leftover users of hashmap with htab also fixes a bug where the ErrorFile directive would create a new hashmap on every added item, effectively allowing only the use of the last specified errornumber, and producing memory leaks on each config reload. --- src/Makefile.am | 1 - src/anonymous.c | 16 +- src/anonymous.h | 2 +- src/conf.c | 37 +++- src/conf.h | 6 +- src/conns.c | 12 +- src/conns.h | 4 +- src/hashmap.c | 516 ----------------------------------------------- src/hashmap.h | 125 ------------ src/html-error.c | 51 +++-- src/main.c | 4 +- src/reqs.c | 2 +- 12 files changed, 89 insertions(+), 687 deletions(-) delete mode 100644 src/hashmap.c delete mode 100644 src/hashmap.h diff --git a/src/Makefile.am b/src/Makefile.am index c3b8dca6..fd8a4992 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -32,7 +32,6 @@ tinyproxy_SOURCES = \ conf.c conf.h \ conns.c conns.h \ daemon.c daemon.h \ - hashmap.c hashmap.h \ heap.c heap.h \ html-error.c html-error.h \ http-message.c http-message.h \ diff --git a/src/anonymous.c b/src/anonymous.c index f38fd441..91e490c4 100644 --- a/src/anonymous.c +++ b/src/anonymous.c @@ -23,7 +23,7 @@ #include "main.h" #include "anonymous.h" -#include "hashmap.h" +#include "hsearch.h" #include "heap.h" #include "log.h" #include "conf.h" @@ -42,7 +42,7 @@ int anonymous_search (struct config_s *conf, const char *s) assert (s != NULL); assert (conf->anonymous_map != NULL); - return hashmap_search (conf->anonymous_map, s); + return !!htab_find (conf->anonymous_map, s); } /* @@ -51,23 +51,21 @@ int anonymous_search (struct config_s *conf, const char *s) * Return -1 if there is an error, otherwise a 0 is returned if the insert was * successful. */ -int anonymous_insert (struct config_s *conf, const char *s) +int anonymous_insert (struct config_s *conf, char *s) { - char data = 1; - assert (s != NULL); if (!conf->anonymous_map) { - conf->anonymous_map = hashmap_create (32); + conf->anonymous_map = htab_create (32); if (!conf->anonymous_map) return -1; } - if (hashmap_search (conf->anonymous_map, s) > 0) { - /* The key was already found, so return a positive number. */ + if (htab_find (conf->anonymous_map, s)) { + /* The key was already found. */ return 0; } /* Insert the new key */ - return hashmap_insert (conf->anonymous_map, s, &data, sizeof (data)); + return htab_insert (conf->anonymous_map, s, HTV_N(1)) ? 0 : -1; } diff --git a/src/anonymous.h b/src/anonymous.h index 6fc45186..78ce7710 100644 --- a/src/anonymous.h +++ b/src/anonymous.h @@ -23,6 +23,6 @@ extern short int is_anonymous_enabled (struct config_s *conf); extern int anonymous_search (struct config_s *conf, const char *s); -extern int anonymous_insert (struct config_s *conf, const char *s); +extern int anonymous_insert (struct config_s *conf, char *s); #endif diff --git a/src/conf.c b/src/conf.c index f0db5006..20d60d69 100644 --- a/src/conf.c +++ b/src/conf.c @@ -288,6 +288,9 @@ free_added_headers (vector_t add_headers) void free_config (struct config_s *conf) { + char *k; + htab_value *v; + size_t it; safefree (conf->logf_name); safefree (conf->stathost); safefree (conf->user); @@ -307,13 +310,25 @@ void free_config (struct config_s *conf) safefree (conf->pidpath); safefree (conf->bind_address); safefree (conf->via_proxy_name); - hashmap_delete (conf->errorpages); + if (conf->errorpages) { + it = 0; + while((it = htab_next(conf->errorpages, it, &k, &v))) { + safefree(k); + safefree(v->p); + } + htab_destroy (conf->errorpages); + } free_added_headers (conf->add_headers); safefree (conf->errorpage_undef); safefree (conf->statpage); flush_access_list (conf->access_list); free_connect_ports_list (conf->connect_ports); - hashmap_delete (conf->anonymous_map); + if (conf->anonymous_map) { + it = 0; + while((it = htab_next(conf->anonymous_map, it, &k, &v))) + safefree(k); + htab_destroy (conf->anonymous_map); + } memset (conf, 0, sizeof(*conf)); } @@ -623,8 +638,14 @@ static HANDLE_FUNC (handle_anonymous) if (!arg) return -1; - anonymous_insert (conf, arg); - safefree (arg); + if(anonymous_insert (conf, arg) < 0) { + log_message (LOG_WARNING, + "anonymous_insert() failed: '%s'", + arg); + safefree(arg); + return -1; + } + return 0; } @@ -814,8 +835,12 @@ static HANDLE_FUNC (handle_errorfile) unsigned long int err = get_long_arg (line, &match[2]); char *page = get_string_arg (line, &match[4]); - add_new_errorpage (conf, page, err); - safefree (page); + if(add_new_errorpage (conf, page, err) < 0) { + log_message (LOG_WARNING, + "add_new_errorpage() failed: '%s'", + page); + safefree (page); + } return 0; } diff --git a/src/conf.h b/src/conf.h index 8433381d..128d0d2d 100644 --- a/src/conf.h +++ b/src/conf.h @@ -22,7 +22,7 @@ #ifndef TINYPROXY_CONF_H #define TINYPROXY_CONF_H -#include "hashmap.h" +#include "hsearch.h" #include "vector.h" #include "acl.h" @@ -81,7 +81,7 @@ struct config_s { /* * Error page support. Map error numbers to file paths. */ - hashmap_t errorpages; + struct htab *errorpages; /* * Error page to be displayed if appropriate page cannot be located @@ -105,7 +105,7 @@ struct config_s { * Map of headers which should be let through when the * anonymous feature is turned on. */ - hashmap_t anonymous_map; + struct htab *anonymous_map; /* * Extra headers to be added to outgoing HTTP requests. diff --git a/src/conns.c b/src/conns.c index 505b5c4a..c6c267fc 100644 --- a/src/conns.c +++ b/src/conns.c @@ -122,8 +122,16 @@ void destroy_conn (struct conn_s *connptr) if (connptr->request_line) safefree (connptr->request_line); - if (connptr->error_variables) - hashmap_delete (connptr->error_variables); + if (connptr->error_variables) { + char *k; + htab_value *v; + size_t it = 0; + while((it = htab_next(connptr->error_variables, it, &k, &v))) { + safefree(v->p); + safefree(k); + } + htab_destroy (connptr->error_variables); + } if (connptr->error_string) safefree (connptr->error_string); diff --git a/src/conns.h b/src/conns.h index 393e5d42..dc1670d5 100644 --- a/src/conns.h +++ b/src/conns.h @@ -22,7 +22,7 @@ #define TINYPROXY_CONNS_H #include "main.h" -#include "hashmap.h" +#include "hsearch.h" /* * Connection Definition @@ -45,7 +45,7 @@ struct conn_s { * This structure stores key -> value mappings for substitution * in the error HTML files. */ - hashmap_t error_variables; + struct htab *error_variables; int error_number; char *error_string; diff --git a/src/hashmap.c b/src/hashmap.c deleted file mode 100644 index 7793d08a..00000000 --- a/src/hashmap.c +++ /dev/null @@ -1,516 +0,0 @@ -/* tinyproxy - A fast light-weight HTTP proxy - * Copyright (C) 2002 Robert James Kaes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* A hashmap implementation. The keys are case-insensitive NULL terminated - * strings, and the data is arbitrary lumps of data. Copies of both the - * key and the data in the hashmap itself, so you must free the original - * key and data to avoid a memory leak. The hashmap returns a pointer - * to the data when a key is searched for, so take care in modifying the - * data as it's modifying the data stored in the hashmap. (In other words, - * don't try to free the data, or realloc the memory. :) - */ - -#include "main.h" - -#include "hashmap.h" -#include "heap.h" - -/* - * These structures are the storage for the hashmap. Entries are stored in - * struct hashentry_s (the key, data, and length), and all the "buckets" are - * grouped together in hashmap_s. The hashmap_s.size member is for - * internal use. It stores the number of buckets the hashmap was created - * with. - */ -struct hashentry_s { - char *key; - void *data; - size_t len; - - struct hashentry_s *prev, *next; -}; - -struct hashbucket_s { - struct hashentry_s *head, *tail; -}; - -struct hashmap_s { - uint32_t seed; - unsigned int size; - hashmap_iter end_iterator; - - struct hashbucket_s *buckets; -}; - -/* - * A NULL terminated string is passed to this function and a "hash" value - * is produced within the range of [0 .. size) (In other words, 0 to one - * less than size.) - * The contents of the key are converted to lowercase, so this function - * is not case-sensitive. - * - * This is Dan Bernstein's hash function as described, for example, here: - * http://www.cse.yorku.ca/~oz/hash.html - * - * If any of the arguments are invalid a negative number is returned. - */ -static int hashfunc (const char *key, unsigned int size, uint32_t seed) -{ - uint32_t hash; - - if (key == NULL) - return -EINVAL; - if (size == 0) - return -ERANGE; - - for (hash = seed; *key != '\0'; key++) { - hash = ((hash << 5) + hash) ^ tolower (*key); - } - - /* Keep the hash within the table limits */ - return hash % size; -} - -/* - * Create a hashmap with the requested number of buckets. If "nbuckets" is - * not greater than zero a NULL is returned; otherwise, a _token_ to the - * hashmap is returned. - * - * NULLs are also returned if memory could not be allocated for hashmap. - */ -hashmap_t hashmap_create (unsigned int nbuckets) -{ - struct hashmap_s *ptr; - - if (nbuckets == 0) - return NULL; - - ptr = (struct hashmap_s *) safecalloc (1, sizeof (struct hashmap_s)); - if (!ptr) - return NULL; - - ptr->seed = (uint32_t)rand(); - ptr->size = nbuckets; - ptr->buckets = (struct hashbucket_s *) safecalloc (nbuckets, - sizeof (struct - hashbucket_s)); - if (!ptr->buckets) { - safefree (ptr); - return NULL; - } - - /* This points to "one" past the end of the hashmap. */ - ptr->end_iterator = 0; - - return ptr; -} - -/* - * Follow the chain of hashentries and delete them (including the data and - * the key.) - * - * Returns: 0 if the function completed successfully - * negative number is returned if "entry" was NULL - */ -static int delete_hashbucket (struct hashbucket_s *bucket) -{ - struct hashentry_s *nextptr; - struct hashentry_s *ptr; - - if (bucket == NULL || bucket->head == NULL) - return -EINVAL; - - ptr = bucket->head; - while (ptr) { - nextptr = ptr->next; - - safefree (ptr->key); - safefree (ptr->data); - safefree (ptr); - - ptr = nextptr; - } - - return 0; -} - -/* - * Deletes a hashmap. All the key/data pairs are also deleted. - * - * Returns: 0 on success - * negative if a NULL "map" was supplied - */ -int hashmap_delete (hashmap_t map) -{ - unsigned int i; - - if (map == NULL) - return -EINVAL; - - for (i = 0; i != map->size; i++) { - if (map->buckets[i].head != NULL) { - delete_hashbucket (&map->buckets[i]); - } - } - - safefree (map->buckets); - safefree (map); - - return 0; -} - -/* - * Inserts a NULL terminated string (as the key), plus any arbitrary "data" - * of "len" bytes. Both the key and the data are copied, so the original - * key/data must be freed to avoid a memory leak. - * The "data" must be non-NULL and "len" must be greater than zero. You - * cannot insert NULL data in association with the key. - * - * Returns: 0 on success - * negative number if there are errors - */ -int -hashmap_insert (hashmap_t map, const char *key, const void *data, size_t len) -{ - struct hashentry_s *ptr; - int hash; - char *key_copy; - void *data_copy; - - assert (map != NULL); - assert (key != NULL); - assert (data != NULL); - assert (len > 0); - - if (map == NULL || key == NULL) - return -EINVAL; - if (!data || len < 1) - return -ERANGE; - - hash = hashfunc (key, map->size, map->seed); - if (hash < 0) - return hash; - - /* - * First make copies of the key and data in case there is a memory - * problem later. - */ - key_copy = safestrdup (key); - if (!key_copy) - return -ENOMEM; - - data_copy = safemalloc (len); - if (!data_copy) { - safefree (key_copy); - return -ENOMEM; - } - memcpy (data_copy, data, len); - - ptr = (struct hashentry_s *) safemalloc (sizeof (struct hashentry_s)); - if (!ptr) { - safefree (key_copy); - safefree (data_copy); - return -ENOMEM; - } - - ptr->key = key_copy; - ptr->data = data_copy; - ptr->len = len; - - /* - * Now add the entry to the end of the bucket chain. - */ - ptr->next = NULL; - ptr->prev = map->buckets[hash].tail; - if (map->buckets[hash].tail) - map->buckets[hash].tail->next = ptr; - - map->buckets[hash].tail = ptr; - if (!map->buckets[hash].head) - map->buckets[hash].head = ptr; - - map->end_iterator++; - return 0; -} - -/* - * Get an iterator to the first entry. - * - * Returns: an negative value upon error. - */ -hashmap_iter hashmap_first (hashmap_t map) -{ - assert (map != NULL); - - if (!map) - return -EINVAL; - - if (map->end_iterator == 0) - return -1; - else - return 0; -} - -/* - * Checks to see if the iterator is pointing at the "end" of the entries. - * - * Returns: 1 if it is the end - * 0 otherwise - */ -int hashmap_is_end (hashmap_t map, hashmap_iter iter) -{ - assert (map != NULL); - assert (iter >= 0); - - if (!map || iter < 0) - return -EINVAL; - - if (iter == map->end_iterator) - return 1; - else - return 0; -} - -/* - * Return a "pointer" to the first instance of the particular key. It can - * be tested against hashmap_is_end() to see if the key was not found. - * - * Returns: negative upon an error - * an "iterator" pointing at the first key - * an "end-iterator" if the key wasn't found - */ -hashmap_iter hashmap_find (hashmap_t map, const char *key) -{ - unsigned int i; - hashmap_iter iter = 0; - struct hashentry_s *ptr; - - assert (map != NULL); - assert (key != NULL); - - if (!map || !key) - return -EINVAL; - - /* - * Loop through all the keys and look for the first occurrence - * of a particular key. - */ - for (i = 0; i != map->size; i++) { - ptr = map->buckets[i].head; - - while (ptr) { - if (strcasecmp (ptr->key, key) == 0) { - /* Found it, so return the current count */ - return iter; - } - - iter++; - ptr = ptr->next; - } - } - - return iter; -} - -/* - * Retrieve the data associated with a particular iterator. - * - * Returns: the length of the data block upon success - * negative upon error - */ -ssize_t -hashmap_return_entry (hashmap_t map, hashmap_iter iter, char **key, void **data) -{ - unsigned int i; - struct hashentry_s *ptr; - hashmap_iter count = 0; - - assert (map != NULL); - assert (iter >= 0); - assert (iter != map->end_iterator); - assert (key != NULL); - assert (data != NULL); - - if (!map || iter < 0 || !key || !data) - return -EINVAL; - - for (i = 0; i != map->size; i++) { - ptr = map->buckets[i].head; - while (ptr) { - if (count == iter) { - /* This is the data so return it */ - *key = ptr->key; - *data = ptr->data; - return ptr->len; - } - - ptr = ptr->next; - count++; - } - } - - return -EFAULT; -} - -/* - * Searches for _any_ occurrences of "key" within the hashmap. - * - * Returns: negative upon an error - * zero if no key is found - * count found - */ -ssize_t hashmap_search (hashmap_t map, const char *key) -{ - int hash; - struct hashentry_s *ptr; - ssize_t count = 0; - - if (map == NULL || key == NULL) - return -EINVAL; - - hash = hashfunc (key, map->size, map->seed); - if (hash < 0) - return hash; - - ptr = map->buckets[hash].head; - - /* All right, there is an entry here, now see if it's the one we want */ - while (ptr) { - if (strcasecmp (ptr->key, key) == 0) - ++count; - - /* This entry didn't contain the key; move to the next one */ - ptr = ptr->next; - } - - return count; -} - -/* - * Get the first entry (assuming there is more than one) for a particular - * key. The data MUST be non-NULL. - * - * Returns: negative upon error - * zero if no entry is found - * length of data for the entry - */ -ssize_t hashmap_entry_by_key (hashmap_t map, const char *key, void **data) -{ - int hash; - struct hashentry_s *ptr; - - if (!map || !key || !data) - return -EINVAL; - - hash = hashfunc (key, map->size, map->seed); - if (hash < 0) - return hash; - - ptr = map->buckets[hash].head; - - while (ptr) { - if (strcasecmp (ptr->key, key) == 0) { - *data = ptr->data; - return ptr->len; - } - - ptr = ptr->next; - } - - return 0; -} - -/* - * Go through the hashmap and remove the particular key. - * NOTE: This will invalidate any iterators which have been created. - * - * Remove: negative upon error - * 0 if the key was not found - * positive count of entries deleted - */ -ssize_t hashmap_remove (hashmap_t map, const char *key) -{ - int hash; - struct hashentry_s *ptr, *next; - short int deleted = 0; - - if (map == NULL || key == NULL) - return -EINVAL; - - hash = hashfunc (key, map->size, map->seed); - if (hash < 0) - return hash; - - ptr = map->buckets[hash].head; - while (ptr) { - if (strcasecmp (ptr->key, key) == 0) { - /* - * Found the data, now need to remove everything - * and update the hashmap. - */ - next = ptr->next; - - if (ptr->prev) - ptr->prev->next = ptr->next; - if (ptr->next) - ptr->next->prev = ptr->prev; - - if (map->buckets[hash].head == ptr) - map->buckets[hash].head = ptr->next; - if (map->buckets[hash].tail == ptr) - map->buckets[hash].tail = ptr->prev; - - safefree (ptr->key); - safefree (ptr->data); - safefree (ptr); - - ++deleted; - --map->end_iterator; - - ptr = next; - continue; - } - - /* This entry didn't contain the key; move to the next one */ - ptr = ptr->next; - } - - /* The key was not found, so return 0 */ - return deleted; -} - -/* - * Look up the value for a variable. - */ -char *lookup_variable (hashmap_t map, const char *varname) -{ - hashmap_iter result_iter; - char *key; - char *data; - - result_iter = hashmap_find (map, varname); - - if (hashmap_is_end (map, result_iter)) - return (NULL); - - if (hashmap_return_entry (map, result_iter, - &key, (void **) &data) < 0) - return (NULL); - - return (data); -} diff --git a/src/hashmap.h b/src/hashmap.h deleted file mode 100644 index 92067370..00000000 --- a/src/hashmap.h +++ /dev/null @@ -1,125 +0,0 @@ -/* tinyproxy - A fast light-weight HTTP proxy - * Copyright (C) 2002 Robert James Kaes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* See 'hashmap.c' for detailed information. */ - -#ifndef _HASHMAP_H -#define _HASHMAP_H - -#include "common.h" - -/* - * We're using a typedef here to "hide" the implementation details of the - * hash map. Sure, it's a pointer, but the struct is hidden in the C file. - * So, just use the hashmap_t like it's a cookie. :) - */ -typedef struct hashmap_s *hashmap_t; -typedef int hashmap_iter; - -/* - * hashmap_create() takes one argument, which is the number of buckets to - * use internally. hashmap_delete() is self explanatory. - */ -extern hashmap_t hashmap_create (unsigned int nbuckets); -extern int hashmap_delete (hashmap_t map); - -/* - * When the you insert a key/data pair into the hashmap it will the key - * and data are duplicated, so you must free your copy if it was created - * on the heap. The key must be a NULL terminated string. "data" must be - * non-NULL and length must be greater than zero. - * - * Returns: negative on error - * 0 upon successful insert - */ -extern int hashmap_insert (hashmap_t map, const char *key, - const void *data, size_t len); - -/* - * Get an iterator to the first entry. - * - * Returns: an negative value upon error. - */ -extern hashmap_iter hashmap_first (hashmap_t map); - -/* - * Checks to see if the iterator is pointing at the "end" of the entries. - * - * Returns: 1 if it is the end - * 0 otherwise - */ -extern int hashmap_is_end (hashmap_t map, hashmap_iter iter); - -/* - * Return a "pointer" to the first instance of the particular key. It can - * be tested against hashmap_is_end() to see if the key was not found. - * - * Returns: negative upon an error - * an "iterator" pointing at the first key - * an "end-iterator" if the key wasn't found - */ -extern hashmap_iter hashmap_find (hashmap_t map, const char *key); - -/* - * Retrieve the key/data associated with a particular iterator. - * NOTE: These are pointers to the actual data, so don't mess around with them - * too much. - * - * Returns: the length of the data block upon success - * negative upon error - */ -extern ssize_t hashmap_return_entry (hashmap_t map, hashmap_iter iter, - char **key, void **data); - -/* - * Get the first entry (assuming there is more than one) for a particular - * key. The data MUST be non-NULL. - * - * Returns: negative upon error - * zero if no entry is found - * length of data for the entry - */ -extern ssize_t hashmap_entry_by_key (hashmap_t map, const char *key, - void **data); - -/* - * Searches for _any_ occurrances of "key" within the hashmap and returns the - * number of matching entries. - * - * Returns: negative upon an error - * zero if no key is found - * count found (positive value) - */ -extern ssize_t hashmap_search (hashmap_t map, const char *key); - -/* - * Go through the hashmap and remove the particular key. - * NOTE: This will invalidate any iterators which have been created. - * - * Remove: negative upon error - * 0 if the key was not found - * positive count of entries deleted - */ -extern ssize_t hashmap_remove (hashmap_t map, const char *key); - -/* - * Look up the value for a variable. - */ -extern char *lookup_variable (hashmap_t map, const char *varname); - -#endif /* _HASHMAP_H */ diff --git a/src/html-error.c b/src/html-error.c index d643aaa3..7c71f74a 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -40,17 +40,22 @@ int add_new_errorpage (struct config_s *conf, char *filepath, unsigned int errornum) { - char errornbuf[ERRORNUM_BUFSIZE]; + char errornbuf[ERRORNUM_BUFSIZE], *k; - conf->errorpages = hashmap_create (ERRPAGES_BUCKETCOUNT); + if (!conf->errorpages) + conf->errorpages = htab_create (ERRPAGES_BUCKETCOUNT); if (!conf->errorpages) return (-1); snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum); - if (hashmap_insert (conf->errorpages, errornbuf, - filepath, strlen (filepath) + 1) < 0) + k = safestrdup(errornbuf); + if (!k) return -1; + + if (!htab_insert (conf->errorpages, k, HTV_P(filepath))) { + safefree(k); return (-1); + } return (0); } @@ -60,10 +65,8 @@ int add_new_errorpage (struct config_s *conf, char *filepath, */ static char *get_html_file (unsigned int errornum) { - hashmap_iter result_iter; char errornbuf[ERRORNUM_BUFSIZE]; - char *key; - char *val; + htab_value *hv; assert (errornum >= 100 && errornum < 1000); @@ -72,16 +75,15 @@ static char *get_html_file (unsigned int errornum) snprintf (errornbuf, ERRORNUM_BUFSIZE, "%u", errornum); - result_iter = hashmap_find (config->errorpages, errornbuf); - - if (hashmap_is_end (config->errorpages, result_iter)) - return (config->errorpage_undef); - - if (hashmap_return_entry (config->errorpages, result_iter, - &key, (void **) &val) < 0) - return (config->errorpage_undef); + hv = htab_find (config->errorpages, errornbuf); + if (!hv) return (config->errorpage_undef); + return hv->p; +} - return (val); +static char *lookup_variable (struct htab *map, const char *varname) { + htab_value *v; + v = htab_find(map, varname); + return v ? v->p : 0; } static void varsubst_sendline(struct conn_s *connptr, regex_t *re, char *p) { @@ -203,14 +205,25 @@ int send_http_error_message (struct conn_s *connptr) int add_error_variable (struct conn_s *connptr, const char *key, const char *val) { + char *k, *v; + if (!connptr->error_variables) if (! (connptr->error_variables = - hashmap_create (ERRVAR_BUCKETCOUNT))) + htab_create (ERRVAR_BUCKETCOUNT))) return (-1); - return hashmap_insert (connptr->error_variables, key, val, - strlen (val) + 1); + k = safestrdup(key); + v = safestrdup(val); + + if (!v || !k) goto oom; + + if(htab_insert (connptr->error_variables, k, HTV_P(v))) + return 1; +oom:; + safefree(k); + safefree(v); + return -1; } #define ADD_VAR_RET(x, y) \ diff --git a/src/main.c b/src/main.c index 42e80483..8bda2b97 100644 --- a/src/main.c +++ b/src/main.c @@ -337,8 +337,8 @@ main (int argc, char **argv) * HTTP/1.0 request. Also add the Content-Type header since it * goes hand in hand with Content-Length. */ if (is_anonymous_enabled (config)) { - anonymous_insert (config, "Content-Length"); - anonymous_insert (config, "Content-Type"); + anonymous_insert (config, safestrdup("Content-Length")); + anonymous_insert (config, safestrdup("Content-Type")); } if (daemonized == TRUE) { diff --git a/src/reqs.c b/src/reqs.c index 47bb3048..b9621533 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -32,7 +32,7 @@ #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" From 5779ba8697f16afa2b8fe042a409f192161b7706 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 20:28:19 +0100 Subject: [PATCH 123/265] hsearch: add seed to prevent another CVE-2012-3505 instance --- src/hsearch.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/hsearch.c b/src/hsearch.c index 0d022a2b..0cc4aca5 100644 --- a/src/hsearch.c +++ b/src/hsearch.c @@ -48,6 +48,7 @@ struct htab { struct elem *elems; size_t mask; size_t used; + size_t seed; }; #define MINSIZE 8 @@ -64,10 +65,10 @@ struct htab { #define STRCMP(X, Y) strcmp(X, Y) #endif -static size_t keyhash(const char *k) +static size_t keyhash(const char *k, size_t seed) { const unsigned char *p = (const void *)k; - size_t h = 0; + size_t h = seed; while (*p) h = 31*h + LOWER_OR_NOT(*p++); @@ -127,6 +128,7 @@ struct htab *htab_create(size_t nel) free(r); r = 0; } + r->seed = rand(); return r; } @@ -138,7 +140,7 @@ void htab_destroy(struct htab *htab) static htab_entry *htab_find_item(struct htab *htab, const char* key) { - size_t hash = keyhash(key); + size_t hash = keyhash(key, htab->seed); struct elem *e = lookup(htab, key, hash); if (e->item.key) { @@ -164,7 +166,7 @@ int htab_delete(struct htab *htab, const char* key) int htab_insert(struct htab *htab, char* key, htab_value value) { - size_t hash = keyhash(key); + size_t hash = keyhash(key, htab->seed); struct elem *e = lookup(htab, key, hash); if(e->item.key) { /* it's not allowed to overwrite existing data */ From 0c8275a90ed16c4230fb311aebbf34c5667667c7 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 14 Sep 2020 21:59:27 +0100 Subject: [PATCH 124/265] refactor conns.[ch], put conn_s into child struct this allows to access the conn member from the main thread handling the childs, plus simplifies the code. --- src/child.c | 13 ++++++++----- src/conns.c | 54 +++++++++++++++-------------------------------------- src/conns.h | 12 +++++++----- src/reqs.c | 12 +++++------- src/reqs.h | 3 ++- 5 files changed, 37 insertions(+), 57 deletions(-) diff --git a/src/child.c b/src/child.c index 3b1800f7..107475b7 100644 --- a/src/child.c +++ b/src/child.c @@ -33,25 +33,26 @@ #include "conf.h" #include "sblist.h" #include "loop.h" +#include "conns.h" #include static vector_t listen_fds; struct client { union sockaddr_union addr; - int fd; }; struct child { pthread_t thread; struct client client; + struct conn_s conn; volatile int done; }; static void* child_thread(void* data) { struct child *c = data; - handle_connection (c->client.fd, &c->client.addr); + handle_connection (&c->conn, &c->client.addr); c->done = 1; return NULL; } @@ -185,7 +186,7 @@ void child_main_loop (void) continue; } - child = safemalloc(sizeof(struct child)); + child = safecalloc(1, sizeof(struct child)); if (!child) { oom: close(connfd); @@ -202,7 +203,9 @@ void child_main_loop (void) goto oom; } - child->client.fd = connfd; + conn_struct_init(&child->conn); + child->conn.client_fd = connfd; + memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage)); attrp = 0; @@ -233,7 +236,7 @@ void child_kill_children (int sig) if (!c->done) { /* interrupt blocking operations. this should cause the threads to shutdown orderly. */ - close(c->client.fd); + close(c->conn.client_fd); } } usleep(16); diff --git a/src/conns.c b/src/conns.c index c6c267fc..19aaa49c 100644 --- a/src/conns.c +++ b/src/conns.c @@ -30,13 +30,20 @@ #include "log.h" #include "stats.h" -struct conn_s *initialize_conn (int client_fd, const char *ipaddr, +void conn_struct_init(struct conn_s *connptr) { + connptr->error_number = -1; + connptr->client_fd = -1; + connptr->server_fd = -1; + /* There is _no_ content length initially */ + connptr->content_length.server = connptr->content_length.client = -1; +} + +int conn_init_contents (struct conn_s *connptr, const char *ipaddr, const char *sock_ipaddr) { - struct conn_s *connptr; struct buffer_s *cbuffer, *sbuffer; - assert (client_fd >= 0); + assert (connptr->client_fd >= 0); /* * Allocate the memory for all the internal components @@ -47,47 +54,16 @@ struct conn_s *initialize_conn (int client_fd, const char *ipaddr, if (!cbuffer || !sbuffer) goto error_exit; - /* - * Allocate the space for the conn_s structure itself. - */ - connptr = (struct conn_s *) safemalloc (sizeof (struct conn_s)); - if (!connptr) - goto error_exit; - - connptr->client_fd = client_fd; - connptr->server_fd = -1; - connptr->cbuffer = cbuffer; connptr->sbuffer = sbuffer; - connptr->request_line = NULL; - - /* These store any error strings */ - connptr->error_variables = NULL; - connptr->error_string = NULL; - connptr->error_number = -1; - - connptr->connect_method = FALSE; - connptr->show_stats = FALSE; - - connptr->protocol.major = connptr->protocol.minor = 0; - - /* There is _no_ content length initially */ - connptr->content_length.server = connptr->content_length.client = -1; - connptr->server_ip_addr = (sock_ipaddr ? safestrdup (sock_ipaddr) : NULL); connptr->client_ip_addr = safestrdup (ipaddr); - connptr->upstream_proxy = NULL; - update_stats (STAT_OPEN); -#ifdef REVERSE_SUPPORT - connptr->reversepath = NULL; -#endif - - return connptr; + return 1; error_exit: /* @@ -98,10 +74,10 @@ struct conn_s *initialize_conn (int client_fd, const char *ipaddr, if (sbuffer) delete_buffer (sbuffer); - return NULL; + return 0; } -void destroy_conn (struct conn_s *connptr) +void conn_destroy_contents (struct conn_s *connptr) { assert (connptr != NULL); @@ -109,10 +85,12 @@ void destroy_conn (struct conn_s *connptr) if (close (connptr->client_fd) < 0) log_message (LOG_INFO, "Client (%d) close message: %s", connptr->client_fd, strerror (errno)); + connptr->client_fd = -1; if (connptr->server_fd != -1) if (close (connptr->server_fd) < 0) log_message (LOG_INFO, "Server (%d) close message: %s", connptr->server_fd, strerror (errno)); + connptr->server_fd = -1; if (connptr->cbuffer) delete_buffer (connptr->cbuffer); @@ -146,7 +124,5 @@ void destroy_conn (struct conn_s *connptr) safefree (connptr->reversepath); #endif - safefree (connptr); - update_stats (STAT_CLOSE); } diff --git a/src/conns.h b/src/conns.h index dc1670d5..9618efbb 100644 --- a/src/conns.h +++ b/src/conns.h @@ -87,11 +87,13 @@ struct conn_s { struct upstream *upstream_proxy; }; -/* - * Functions for the creation and destruction of a connection structure. - */ -extern struct conn_s *initialize_conn (int client_fd, const char *ipaddr, +/* expects pointer to zero-initialized struct, set up struct + with default values for initial use */ +extern void conn_struct_init(struct conn_s *connptr); + +/* second stage initializiation, sets up buffers and connection details */ +extern int conn_init_contents (struct conn_s *connptr, const char *ipaddr, const char *sock_ipaddr); -extern void destroy_conn (struct conn_s *connptr); +extern void conn_destroy_contents (struct conn_s *connptr); #endif diff --git a/src/reqs.c b/src/reqs.c index b9621533..2b7649ee 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1509,16 +1509,15 @@ static void handle_connection_failure(struct conn_s *connptr, int got_headers) * tinyproxy code, which was confusing, redundant. Hail progress. * - rjkaes */ -void handle_connection (int fd, union sockaddr_union* addr) +void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) { #define HC_FAIL() \ do {handle_connection_failure(connptr, got_headers); goto done;} \ while(0) - int got_headers = 0; + int got_headers = 0, fd = connptr->client_fd; ssize_t i; - struct conn_s *connptr; struct request_s *request = NULL; struct timeval tv; orderedmap hashofheaders = NULL; @@ -1536,9 +1535,8 @@ void handle_connection (int fd, union sockaddr_union* addr) "Connect (file descriptor %d): %s", fd, peer_ipaddr, sock_ipaddr); - connptr = initialize_conn (fd, peer_ipaddr, - config->bindsame ? sock_ipaddr : NULL); - if (!connptr) { + if(!conn_init_contents (connptr, peer_ipaddr, + config->bindsame ? sock_ipaddr : NULL)) { close (fd); return; } @@ -1739,7 +1737,7 @@ void handle_connection (int fd, union sockaddr_union* addr) done: free_request_struct (request); orderedmap_destroy (hashofheaders); - destroy_conn (connptr); + conn_destroy_contents (connptr); return; #undef HC_FAIL } diff --git a/src/reqs.h b/src/reqs.h index c1c51001..4a0374ff 100644 --- a/src/reqs.h +++ b/src/reqs.h @@ -24,6 +24,7 @@ #include "common.h" #include "sock.h" +#include "conns.h" /* * Port constants for HTTP (80) and SSL (443) @@ -44,6 +45,6 @@ struct request_s { char *path; }; -extern void handle_connection (int fd, union sockaddr_union* addr); +extern void handle_connection (struct conn_s *, union sockaddr_union* addr); #endif From 10cdee3bc55274d7f6278cdd0c66edacb5b520a5 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 19:29:03 +0100 Subject: [PATCH 125/265] prepare transition to poll() usage of select() is inefficient (because a huge fd_set array has to be initialized on each call) and insecure (because an fd >= FD_SETSIZE will cause out-of-bounds accesses using the FD_*SET macros, and a system can be set up to allow more than that number of fds using ulimit). for the moment we prepared a poll-like wrapper that still runs select() to test for regressions, and so we have fallback code for systems without poll(). --- src/Makefile.am | 1 + src/child.c | 33 +++++++++++++++------------------ src/mypoll.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/mypoll.h | 22 ++++++++++++++++++++++ src/reqs.c | 44 +++++++++++++++++++------------------------- 5 files changed, 99 insertions(+), 43 deletions(-) create mode 100644 src/mypoll.c create mode 100644 src/mypoll.h diff --git a/src/Makefile.am b/src/Makefile.am index fd8a4992..4973eb71 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -51,6 +51,7 @@ tinyproxy_SOURCES = \ hsearch.c hsearch.h \ orderedmap.c orderedmap.h \ loop.c loop.h \ + mypoll.c mypoll.h \ connect-ports.c connect-ports.h EXTRA_tinyproxy_SOURCES = filter.c filter.h \ diff --git a/src/child.c b/src/child.c index 107475b7..96864b1d 100644 --- a/src/child.c +++ b/src/child.c @@ -34,6 +34,7 @@ #include "sblist.h" #include "loop.h" #include "conns.h" +#include "mypoll.h" #include static vector_t listen_fds; @@ -81,9 +82,10 @@ void child_main_loop (void) union sockaddr_union cliaddr_storage; struct sockaddr *cliaddr = (void*) &cliaddr_storage; socklen_t clilen = sizeof(cliaddr_storage); - fd_set rfds; + int nfds = vector_length(listen_fds); + pollfd_struct *fds = safecalloc(nfds, sizeof *fds); ssize_t i; - int ret, listenfd, maxfd, was_full = 0; + int ret, listenfd, was_full = 0; pthread_attr_t *attrp, attr; struct child *child; @@ -91,6 +93,12 @@ void child_main_loop (void) loop_records_init(); + for (i = 0; i < nfds; i++) { + int *fd = (int *) vector_getentry(listen_fds, i, NULL); + fds[i].fd = *fd; + fds[i].events |= MYPOLL_READ; + } + /* * We have to wait for connections on multiple fds, * so use select. @@ -111,7 +119,6 @@ void child_main_loop (void) was_full = 0; listenfd = -1; - maxfd = 0; /* Handle log rotation if it was requested */ if (received_sighup) { @@ -125,17 +132,8 @@ void child_main_loop (void) received_sighup = FALSE; } + ret = mypoll(fds, nfds, -1); - FD_ZERO(&rfds); - - for (i = 0; i < vector_length(listen_fds); i++) { - int *fd = (int *) vector_getentry(listen_fds, i, NULL); - - FD_SET(*fd, &rfds); - maxfd = max(maxfd, *fd); - } - - ret = select(maxfd + 1, &rfds, NULL, NULL, NULL); if (ret == -1) { if (errno == EINTR) { continue; @@ -149,15 +147,13 @@ void child_main_loop (void) continue; } - for (i = 0; i < vector_length(listen_fds); i++) { - int *fd = (int *) vector_getentry(listen_fds, i, NULL); - - if (FD_ISSET(*fd, &rfds)) { + for (i = 0; i < nfds; i++) { + if (fds[i].revents & MYPOLL_READ) { /* * only accept the connection on the first * fd that we find readable. - fair? */ - listenfd = *fd; + listenfd = fds[i].fd; break; } } @@ -220,6 +216,7 @@ void child_main_loop (void) goto oom; } } + safefree(fds); } /* diff --git a/src/mypoll.c b/src/mypoll.c new file mode 100644 index 00000000..0e068058 --- /dev/null +++ b/src/mypoll.c @@ -0,0 +1,42 @@ +#include "mypoll.h" + +#define MYPOLL_READ (1<<1) +#define MYPOLL_WRITE (1<<2) + +int mypoll(pollfd_struct* fds, int nfds, int timeout) { + fd_set rset, wset, *r=0, *w=0; + int i, ret, maxfd=-1; + struct timeval tv = {0}, *t = 0; + + for(i=0; i maxfd) maxfd = fds[i].fd; + if(fds[i].events & MYPOLL_READ) FD_SET(fds[i].fd, r); + if(fds[i].events & MYPOLL_WRITE) FD_SET(fds[i].fd, w); + } + + if(timeout >= 0) t = &tv; + if(timeout > 0) tv.tv_sec = timeout; + + ret = select(maxfd+1, r, w, 0, t); + + switch(ret) { + case -1: + case 0: + return ret; + } + + for(i=0; i + +#ifdef HAVE_POLL_H +#include +typedef struct pollfd pollfd_struct; +#else +typedef struct mypollfd { + int fd; + short events; + short revents; +} pollfd_struct; +#endif + +#define MYPOLL_READ (1<<1) +#define MYPOLL_WRITE (1<<2) + +int mypoll(pollfd_struct* fds, int nfds, int timeout); + +#endif diff --git a/src/reqs.c b/src/reqs.c index 2b7649ee..6bb456b8 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -51,6 +51,7 @@ #include "conf.h" #include "basicauth.h" #include "loop.h" +#include "mypoll.h" /* * Maximum length of a HTTP line @@ -1141,29 +1142,24 @@ static int process_server_headers (struct conn_s *connptr) */ static void relay_connection (struct conn_s *connptr) { - fd_set rset, wset; - struct timeval tv; int ret; - int maxfd = max (connptr->client_fd, connptr->server_fd) + 1; ssize_t bytes_received; for (;;) { - tv.tv_sec = config->idletimeout; - tv.tv_usec = 0; - - FD_ZERO (&rset); - FD_ZERO (&wset); + 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) { log_message (LOG_INFO, @@ -1178,7 +1174,7 @@ static void relay_connection (struct conn_s *connptr) return; } - 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) @@ -1188,15 +1184,15 @@ 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; } @@ -1435,14 +1431,12 @@ 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 = config->idletimeout; - 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, @@ -1450,7 +1444,7 @@ get_request_entity(struct conn_s *connptr) 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) { From bd92446184fe692e0ceda700945c588ae56cc6cd Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 20:01:01 +0100 Subject: [PATCH 126/265] use poll() where available --- configure.ac | 2 +- src/mypoll.c | 14 ++++++++++---- src/mypoll.h | 11 +++++++++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 7de60871..cf2f19c2 100644 --- a/configure.ac +++ b/configure.ac @@ -141,7 +141,7 @@ AC_HEADER_STDC AC_HEADER_TIME AC_HEADER_SYS_WAIT AC_CHECK_HEADERS([sys/ioctl.h alloca.h memory.h malloc.h sysexits.h \ - values.h]) + values.h poll.h]) dnl Checks for libary functions AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK diff --git a/src/mypoll.c b/src/mypoll.c index 0e068058..495e2c37 100644 --- a/src/mypoll.c +++ b/src/mypoll.c @@ -1,8 +1,14 @@ #include "mypoll.h" -#define MYPOLL_READ (1<<1) -#define MYPOLL_WRITE (1<<2) - +#ifdef HAVE_POLL_H +int mypoll(pollfd_struct* fds, int nfds, int timeout) { + int i, ret; + for(i=0; i +#include "config.h" #ifdef HAVE_POLL_H + #include typedef struct pollfd pollfd_struct; + +#define MYPOLL_READ POLLIN +#define MYPOLL_WRITE POLLOUT + #else + +#include typedef struct mypollfd { int fd; short events; short revents; } pollfd_struct; -#endif #define MYPOLL_READ (1<<1) #define MYPOLL_WRITE (1<<2) +#endif int mypoll(pollfd_struct* fds, int nfds, int timeout); From 192f8194e1e15c9f13e6a73e71c63ed695bd931e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 23:11:09 +0100 Subject: [PATCH 127/265] free() loop records too --- src/child.c | 2 -- src/loop.c | 5 +++++ src/loop.h | 1 + src/main.c | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/child.c b/src/child.c index 96864b1d..b220da33 100644 --- a/src/child.c +++ b/src/child.c @@ -91,8 +91,6 @@ void child_main_loop (void) childs = sblist_new(sizeof (struct child*), config->maxclients); - loop_records_init(); - for (i = 0; i < nfds; i++) { int *fd = (int *) vector_getentry(listen_fds, i, NULL); fds[i].fd = *fd; diff --git a/src/loop.c b/src/loop.c index 77b073df..d696760d 100644 --- a/src/loop.c +++ b/src/loop.c @@ -18,6 +18,11 @@ void loop_records_init(void) { loop_records = sblist_new(sizeof (struct loop_record), 32); } +void loop_records_destroy(void) { + sblist_free(loop_records); + loop_records = 0; +} + #if 0 static void su_to_str(union sockaddr_union *addr, char *buf) { int af = addr->v4.sin_family; diff --git a/src/loop.h b/src/loop.h index 19877d35..27a26c77 100644 --- a/src/loop.h +++ b/src/loop.h @@ -4,6 +4,7 @@ #include "sock.h" void loop_records_init(void); +void loop_records_destroy(void); void loop_records_add(union sockaddr_union *addr); int connection_loops (union sockaddr_union *addr); diff --git a/src/main.c b/src/main.c index 8bda2b97..b9c264a8 100644 --- a/src/main.c +++ b/src/main.c @@ -393,6 +393,8 @@ main (int argc, char **argv) if (daemonized) setup_sig (SIGHUP, takesig, "SIGHUP", argv[0]); setup_sig (SIGUSR1, takesig, "SIGUSR1", argv[0]); + loop_records_init(); + /* Start the main loop */ log_message (LOG_INFO, "Starting main loop. Accepting connections."); @@ -404,6 +406,8 @@ main (int argc, char **argv) child_close_sock (); child_free_children(); + loop_records_destroy(); + /* Remove the PID file */ if (config->pidpath != NULL && unlink (config->pidpath) < 0) { log_message (LOG_WARNING, From d453a4c2a4413d8aeafea70b2389194a7e5e24b0 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 23:20:14 +0100 Subject: [PATCH 128/265] main: include loop header --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index b9c264a8..5b2691a3 100644 --- a/src/main.c +++ b/src/main.c @@ -38,6 +38,7 @@ #include "heap.h" #include "filter.h" #include "child.h" +#include "loop.h" #include "log.h" #include "reqs.h" #include "sock.h" From 2037bc64f587006520a385d06904c2710ab9a8ba Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 23:28:33 +0100 Subject: [PATCH 129/265] free a mem leak by statically allocating global statsbuf --- src/stats.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/stats.c b/src/stats.c index 9f4acc37..dfe054c2 100644 --- a/src/stats.c +++ b/src/stats.c @@ -43,7 +43,7 @@ struct stat_s { unsigned long int num_denied; }; -static struct stat_s *stats; +static struct stat_s stats_buf, *stats; static pthread_mutex_t stats_update_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER; @@ -52,11 +52,7 @@ static pthread_mutex_t stats_file_lock = PTHREAD_MUTEX_INITIALIZER; */ void init_stats (void) { - stats = (struct stat_s *) safemalloc (sizeof (struct stat_s)); - if (!stats) - return; - - memset (stats, 0, sizeof (struct stat_s)); + stats = &stats_buf; } /* From a5890b621b0985b807865e56f9d230cdd3b6b070 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 15 Sep 2020 23:39:04 +0100 Subject: [PATCH 130/265] run_tests_valgrind: use tougher valgrind settings --- tests/scripts/run_tests_valgrind.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/run_tests_valgrind.sh b/tests/scripts/run_tests_valgrind.sh index b93371f0..72b21154 100755 --- a/tests/scripts/run_tests_valgrind.sh +++ b/tests/scripts/run_tests_valgrind.sh @@ -24,5 +24,5 @@ TESTS_DIR=$SCRIPTS_DIR/.. TESTENV_DIR=$TESTS_DIR/env LOG_DIR=$TESTENV_DIR/var/log -VALGRIND="valgrind -q --tool=memcheck --leak-check=full --log-file=$LOG_DIR/valgrind.log" $SCRIPTS_DIR/run_tests.sh +VALGRIND="valgrind --track-origins=yes --show-leak-kinds=all --tool=memcheck --leak-check=full --log-file=$LOG_DIR/valgrind.log" $SCRIPTS_DIR/run_tests.sh From 7d33fc8e8a8802f8962f612510d252bbbe465757 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 01:05:58 +0100 Subject: [PATCH 131/265] listen_fds: use sblist --- src/child.c | 16 ++++++++-------- src/sock.c | 4 ++-- src/sock.h | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/child.c b/src/child.c index b220da33..bec0ca30 100644 --- a/src/child.c +++ b/src/child.c @@ -37,7 +37,7 @@ #include "mypoll.h" #include -static vector_t listen_fds; +static sblist* listen_fds; struct client { union sockaddr_union addr; @@ -82,7 +82,7 @@ void child_main_loop (void) union sockaddr_union cliaddr_storage; struct sockaddr *cliaddr = (void*) &cliaddr_storage; socklen_t clilen = sizeof(cliaddr_storage); - int nfds = vector_length(listen_fds); + int nfds = sblist_getsize(listen_fds); pollfd_struct *fds = safecalloc(nfds, sizeof *fds); ssize_t i; int ret, listenfd, was_full = 0; @@ -92,7 +92,7 @@ void child_main_loop (void) childs = sblist_new(sizeof (struct child*), config->maxclients); for (i = 0; i < nfds; i++) { - int *fd = (int *) vector_getentry(listen_fds, i, NULL); + int *fd = sblist_get(listen_fds, i); fds[i].fd = *fd; fds[i].events |= MYPOLL_READ; } @@ -259,7 +259,7 @@ int child_listening_sockets(vector_t listen_addrs, uint16_t port) assert (port > 0); if (listen_fds == NULL) { - listen_fds = vector_create(); + listen_fds = sblist_new(sizeof(int), 16); if (listen_fds == NULL) { log_message (LOG_ERR, "Could not create the list " "of listening fds"); @@ -299,14 +299,14 @@ int child_listening_sockets(vector_t listen_addrs, uint16_t port) void child_close_sock (void) { - ssize_t i; + size_t i; - for (i = 0; i < vector_length(listen_fds); i++) { - int *fd = (int *) vector_getentry(listen_fds, i, NULL); + for (i = 0; i < sblist_getsize(listen_fds); i++) { + int *fd = sblist_get(listen_fds, i); close (*fd); } - vector_delete(listen_fds); + sblist_free(listen_fds); listen_fds = NULL; } diff --git a/src/sock.c b/src/sock.c index 73ddebdc..983c4ea8 100644 --- a/src/sock.c +++ b/src/sock.c @@ -277,7 +277,7 @@ static int listen_on_one_socket(struct addrinfo *ad) * Upon success, the listen-fds are added to the listen_fds list * and 0 is returned. Upon error, -1 is returned. */ -int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) +int listen_sock (const char *addr, uint16_t port, sblist* listen_fds) { struct addrinfo hints, *result, *rp; char portstr[6]; @@ -315,7 +315,7 @@ int listen_sock (const char *addr, uint16_t port, vector_t listen_fds) continue; } - vector_append (listen_fds, &listenfd, sizeof(int)); + sblist_add (listen_fds, &listenfd); /* success */ ret = 0; diff --git a/src/sock.h b/src/sock.h index 033e179b..722371de 100644 --- a/src/sock.h +++ b/src/sock.h @@ -29,7 +29,7 @@ #define MAXLINE (1024 * 4) #include "common.h" -#include "vector.h" +#include "sblist.h" union sockaddr_union { struct sockaddr_in v4; @@ -37,7 +37,7 @@ union sockaddr_union { }; extern int opensock (const char *host, int port, const char *bind_to); -extern int listen_sock (const char *addr, uint16_t port, vector_t listen_fds); +extern int listen_sock (const char *addr, uint16_t port, sblist* listen_fds); extern int socket_nonblocking (int sock); extern int socket_blocking (int sock); From e929e81a55241b63d8921c071806195eae91ab91 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 01:25:59 +0100 Subject: [PATCH 132/265] add_header: use sblist note that the old code inserted added headers at the beginning of the list, reasoning unknown. this seems counter-intuitive as the headers would end up in the request in the reverse order they were added, but this was irrelevant, as the headers were originally first put into the hashmap hashofheaders before sending it to the client. since the hashmap didn't preserve ordering, the headers would appear in random order anyway. --- src/conf.c | 26 ++++++++++++-------------- src/conf.h | 2 +- src/reqs.c | 8 ++++---- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/conf.c b/src/conf.c index 20d60d69..b29c1fb3 100644 --- a/src/conf.c +++ b/src/conf.c @@ -271,19 +271,20 @@ struct { const unsigned int ndirectives = sizeof (directives) / sizeof (directives[0]); static void -free_added_headers (vector_t add_headers) +free_added_headers (sblist* add_headers) { - ssize_t i; + size_t i; - for (i = 0; i < vector_length (add_headers); i++) { - http_header_t *header = (http_header_t *) - vector_getentry (add_headers, i, NULL); + if (!add_headers) return; + + for (i = 0; i < sblist_getsize (add_headers); i++) { + http_header_t *header = sblist_get (add_headers, i); safefree (header->name); safefree (header->value); } - vector_delete (add_headers); + sblist_free (add_headers); } void free_config (struct config_s *conf) @@ -848,19 +849,16 @@ static HANDLE_FUNC (handle_addheader) { char *name = get_string_arg (line, &match[2]); char *value = get_string_arg (line, &match[3]); - http_header_t *header; + http_header_t header; if (!conf->add_headers) { - conf->add_headers = vector_create (); + conf->add_headers = sblist_new (sizeof(http_header_t), 16); } - header = (http_header_t *) safemalloc (sizeof (http_header_t)); - header->name = name; - header->value = value; - - vector_prepend (conf->add_headers, header, sizeof *header); + header.name = name; + header.value = value; - safefree (header); + sblist_add (conf->add_headers, &header); /* Don't free name or value here, as they are referenced in the * struct inserted into the vector. */ diff --git a/src/conf.h b/src/conf.h index 128d0d2d..3ed774ba 100644 --- a/src/conf.h +++ b/src/conf.h @@ -110,7 +110,7 @@ struct config_s { /* * Extra headers to be added to outgoing HTTP requests. */ - vector_t add_headers; + sblist* add_headers; }; extern int reload_config_file (const char *config_fname, struct config_s *conf); diff --git a/src/reqs.c b/src/reqs.c index 6bb456b8..5f26333d 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1511,7 +1511,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) while(0) int got_headers = 0, fd = connptr->client_fd; - ssize_t i; + size_t i; struct request_s *request = NULL; struct timeval tv; orderedmap hashofheaders = NULL; @@ -1648,9 +1648,9 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) * 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); orderedmap_append (hashofheaders, header->name, header->value); } From 487f2aba4720c3d7b3e7eac4786208cbb7ceb492 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 01:38:50 +0100 Subject: [PATCH 133/265] connect_ports: use sblist --- src/conf.h | 2 +- src/connect-ports.c | 16 ++++++++-------- src/connect-ports.h | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/conf.h b/src/conf.h index 3ed774ba..c0752157 100644 --- a/src/conf.h +++ b/src/conf.h @@ -99,7 +99,7 @@ struct config_s { /* * Store the list of port allowed by CONNECT. */ - vector_t connect_ports; + sblist *connect_ports; /* * Map of headers which should be let through when the diff --git a/src/connect-ports.c b/src/connect-ports.c index 41b4e3d1..6070e92a 100644 --- a/src/connect-ports.c +++ b/src/connect-ports.c @@ -25,10 +25,10 @@ * Now, this routine adds a "port" to the list. It also creates the list if * it hasn't already by done. */ -void add_connect_port_allowed (int port, vector_t *connect_ports) +void add_connect_port_allowed (int port, sblist **connect_ports) { if (!*connect_ports) { - *connect_ports = vector_create (); + *connect_ports = sblist_new (sizeof(int), 16); if (!*connect_ports) { log_message (LOG_WARNING, "Could not create a list of allowed CONNECT ports"); @@ -38,7 +38,7 @@ void add_connect_port_allowed (int port, vector_t *connect_ports) log_message (LOG_INFO, "Adding Port [%d] to the list allowed by CONNECT", port); - vector_append (*connect_ports, &port, sizeof (port)); + sblist_add (*connect_ports, &port); } /* @@ -47,7 +47,7 @@ void add_connect_port_allowed (int port, vector_t *connect_ports) * Returns: 1 if allowed * 0 if denied */ -int check_allowed_connect_ports (int port, vector_t connect_ports) +int check_allowed_connect_ports (int port, sblist *connect_ports) { size_t i; int *data; @@ -59,8 +59,8 @@ int check_allowed_connect_ports (int port, vector_t connect_ports) if (!connect_ports) return 1; - for (i = 0; i != (size_t) vector_length (connect_ports); ++i) { - data = (int *) vector_getentry (connect_ports, i, NULL); + for (i = 0; i < sblist_getsize (connect_ports); ++i) { + data = sblist_get (connect_ports, i); if (data && *data == port) return 1; } @@ -71,7 +71,7 @@ int check_allowed_connect_ports (int port, vector_t connect_ports) /** * Free a connect_ports list. */ -void free_connect_ports_list (vector_t connect_ports) +void free_connect_ports_list (sblist *connect_ports) { - vector_delete (connect_ports); + sblist_free (connect_ports); } diff --git a/src/connect-ports.h b/src/connect-ports.h index 4b3aaf72..38f511ca 100644 --- a/src/connect-ports.h +++ b/src/connect-ports.h @@ -22,10 +22,10 @@ #define _TINYPROXY_CONNECT_PORTS_H_ #include "common.h" -#include "vector.h" +#include "sblist.h" -extern void add_connect_port_allowed (int port, vector_t *connect_ports); -int check_allowed_connect_ports (int port, vector_t connect_ports); -void free_connect_ports_list (vector_t connect_ports); +extern void add_connect_port_allowed (int port, sblist **connect_ports); +int check_allowed_connect_ports (int port, sblist *connect_ports); +void free_connect_ports_list (sblist *connect_ports); #endif /* _TINYPROXY_CONNECT_PORTS_ */ From a5381223df30968054c61dffd8bb81b2a29a1535 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 01:54:05 +0100 Subject: [PATCH 134/265] basicauth: use sblist --- src/basicauth.c | 23 +++++++++++------------ src/basicauth.h | 6 +++--- src/conf.c | 12 +++++++++--- src/conf.h | 2 +- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/basicauth.c b/src/basicauth.c index d6c24201..ed0553b8 100644 --- a/src/basicauth.c +++ b/src/basicauth.c @@ -48,10 +48,10 @@ ssize_t basicauth_string(const char *user, const char *pass, /* * Add entry to the basicauth list */ -void basicauth_add (vector_t authlist, +void basicauth_add (sblist *authlist, const char *user, const char *pass) { - char b[BASE64ENC_BYTES((256+2)-1) + 1]; + char b[BASE64ENC_BYTES((256+2)-1) + 1], *s; ssize_t ret; ret = basicauth_string(user, pass, b, sizeof b); @@ -65,7 +65,8 @@ void basicauth_add (vector_t authlist, return; } - if (vector_append(authlist, b, ret + 1) == -ENOMEM) { + if (!(s = safestrdup(b)) || !sblist_add(authlist, &s)) { + safefree(s); log_message (LOG_ERR, "Unable to allocate memory in basicauth_add()"); return; @@ -80,18 +81,16 @@ void basicauth_add (vector_t authlist, * is in the basicauth list. * return 1 on success, 0 on failure. */ -int basicauth_check (vector_t authlist, const char *authstring) +int basicauth_check (sblist *authlist, const char *authstring) { - ssize_t vl, i; - size_t el; - const char* entry; + size_t i; + char** entry; - vl = vector_length (authlist); - if (vl == -EINVAL) return 0; + if (!authlist) return 0; - for (i = 0; i < vl; i++) { - entry = vector_getentry (authlist, i, &el); - if (strcmp (authstring, entry) == 0) + for (i = 0; i < sblist_getsize(authlist); i++) { + entry = sblist_get (authlist, i); + if (entry && strcmp (authstring, *entry) == 0) return 1; } return 0; diff --git a/src/basicauth.h b/src/basicauth.h index 61dc5c32..ef25b665 100644 --- a/src/basicauth.h +++ b/src/basicauth.h @@ -22,14 +22,14 @@ #define TINYPROXY_BASICAUTH_H #include -#include "vector.h" +#include "sblist.h" extern ssize_t basicauth_string(const char *user, const char *pass, char *buf, size_t bufsize); -extern void basicauth_add (vector_t authlist, +extern void basicauth_add (sblist *authlist, const char *user, const char *pass); -extern int basicauth_check (vector_t authlist, const char *authstring); +extern int basicauth_check (sblist *authlist, const char *authstring); #endif diff --git a/src/conf.c b/src/conf.c index b29c1fb3..206ff3a6 100644 --- a/src/conf.c +++ b/src/conf.c @@ -289,7 +289,7 @@ free_added_headers (sblist* add_headers) void free_config (struct config_s *conf) { - char *k; + char *k, **s; htab_value *v; size_t it; safefree (conf->logf_name); @@ -297,7 +297,13 @@ void free_config (struct config_s *conf) safefree (conf->user); safefree (conf->group); vector_delete(conf->listen_addrs); - vector_delete(conf->basicauth_list); + if(conf->basicauth_list) { + for(it = 0; it < sblist_getsize(conf->basicauth_list); it++) { + s = sblist_get(conf->basicauth_list, it); + if(s) safefree(*s); + } + sblist_free(conf->basicauth_list); + } #ifdef FILTER_ENABLE safefree (conf->filter); #endif /* FILTER_ENABLE */ @@ -915,7 +921,7 @@ static HANDLE_FUNC (handle_basicauth) return -1; } if (!conf->basicauth_list) { - conf->basicauth_list = vector_create (); + conf->basicauth_list = sblist_new (sizeof(char*), 16); } basicauth_add (conf->basicauth_list, user, pass); diff --git a/src/conf.h b/src/conf.h index c0752157..1ba32f25 100644 --- a/src/conf.h +++ b/src/conf.h @@ -38,7 +38,7 @@ typedef struct { * Hold all the configuration time information. */ struct config_s { - vector_t basicauth_list; + sblist *basicauth_list; char *logf_name; unsigned int syslog; /* boolean */ unsigned int port; From e843519fb899f36780756894f634238d67857ad8 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 02:07:52 +0100 Subject: [PATCH 135/265] listen_addrs: use sblist --- src/child.c | 17 ++++++++--------- src/child.h | 4 ++-- src/conf.c | 29 +++++++++++++++++------------ src/conf.h | 2 +- src/transparent-proxy.c | 10 +++++----- 5 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/child.c b/src/child.c index bec0ca30..89a081c5 100644 --- a/src/child.c +++ b/src/child.c @@ -251,10 +251,10 @@ void child_free_children(void) { /** * Listen on the various configured interfaces */ -int child_listening_sockets(vector_t listen_addrs, uint16_t port) +int child_listening_sockets(sblist *listen_addrs, uint16_t port) { int ret; - ssize_t i; + size_t i; assert (port > 0); @@ -267,8 +267,7 @@ int child_listening_sockets(vector_t listen_addrs, uint16_t port) } } - if ((listen_addrs == NULL) || - (vector_length(listen_addrs) == 0)) + if (!listen_addrs || !sblist_getsize(listen_addrs)) { /* * no Listen directive: @@ -278,17 +277,17 @@ int child_listening_sockets(vector_t listen_addrs, uint16_t port) return ret; } - for (i = 0; i < vector_length(listen_addrs); i++) { - const char *addr; + for (i = 0; i < sblist_getsize(listen_addrs); i++) { + char **addr; - addr = (char *)vector_getentry(listen_addrs, i, NULL); - if (addr == NULL) { + addr = sblist_get(listen_addrs, i); + if (!addr || !*addr) { log_message(LOG_WARNING, "got NULL from listen_addrs - skipping"); continue; } - ret = listen_sock(addr, port, listen_fds); + ret = listen_sock(*addr, port, listen_fds); if (ret != 0) { return ret; } diff --git a/src/child.h b/src/child.h index efc6fe3d..ffcd9d06 100644 --- a/src/child.h +++ b/src/child.h @@ -21,7 +21,7 @@ #ifndef TINYPROXY_CHILD_H #define TINYPROXY_CHILD_H -#include "vector.h" +#include "sblist.h" typedef enum { CHILD_MAXCLIENTS, @@ -32,7 +32,7 @@ typedef enum { } child_config_t; extern short int child_pool_create (void); -extern int child_listening_sockets (vector_t listen_addrs, uint16_t port); +extern int child_listening_sockets (sblist *listen_addrs, uint16_t port); extern void child_close_sock (void); extern void child_main_loop (void); extern void child_kill_children (int sig); diff --git a/src/conf.c b/src/conf.c index 206ff3a6..214475dd 100644 --- a/src/conf.c +++ b/src/conf.c @@ -287,23 +287,29 @@ free_added_headers (sblist* add_headers) sblist_free (add_headers); } +static void stringlist_free(sblist *sl) { + size_t i; + char **s; + if(sl) { + for(i = 0; i < sblist_getsize(sl); i++) { + s = sblist_get(sl, i); + if(s) safefree(*s); + } + sblist_free(sl); + } +} + void free_config (struct config_s *conf) { - char *k, **s; + char *k; htab_value *v; size_t it; safefree (conf->logf_name); safefree (conf->stathost); safefree (conf->user); safefree (conf->group); - vector_delete(conf->listen_addrs); - if(conf->basicauth_list) { - for(it = 0; it < sblist_getsize(conf->basicauth_list); it++) { - s = sblist_get(conf->basicauth_list, it); - if(s) safefree(*s); - } - sblist_free(conf->basicauth_list); - } + stringlist_free(conf->basicauth_list); + stringlist_free(conf->listen_addrs); #ifdef FILTER_ENABLE safefree (conf->filter); #endif /* FILTER_ENABLE */ @@ -813,7 +819,7 @@ static HANDLE_FUNC (handle_listen) } if (conf->listen_addrs == NULL) { - conf->listen_addrs = vector_create(); + conf->listen_addrs = sblist_new(sizeof(char*), 16); if (conf->listen_addrs == NULL) { log_message(LOG_WARNING, "Could not create a list " "of listen addresses."); @@ -822,11 +828,10 @@ static HANDLE_FUNC (handle_listen) } } - vector_append (conf->listen_addrs, arg, strlen(arg) + 1); + sblist_add (conf->listen_addrs, &arg); log_message(LOG_INFO, "Added address [%s] to listen addresses.", arg); - safefree (arg); return 0; } diff --git a/src/conf.h b/src/conf.h index 1ba32f25..e0fbb51a 100644 --- a/src/conf.h +++ b/src/conf.h @@ -47,7 +47,7 @@ struct config_s { unsigned int maxclients; char *user; char *group; - vector_t listen_addrs; + sblist *listen_addrs; #ifdef FILTER_ENABLE char *filter; unsigned int filter_url; /* boolean */ diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index e40bce2b..e3265f9f 100644 --- a/src/transparent-proxy.c +++ b/src/transparent-proxy.c @@ -60,7 +60,7 @@ do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders, socklen_t length; char *data; size_t ulen = strlen (*url); - ssize_t i; + size_t i; data = orderedmap_find (hashofheaders, "host"); if (!data) { @@ -123,12 +123,12 @@ do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders, return 1; } - for (i = 0; i < vector_length(conf->listen_addrs); i++) { - const char *addr; + for (i = 0; i < sblist_getsize(conf->listen_addrs); i++) { + char **addr; - addr = (char *)vector_getentry(conf->listen_addrs, i, NULL); + addr = sblist_get(conf->listen_addrs, i); - if (addr && strcmp(request->host, addr) == 0) { + if (addr && *addr && strcmp(request->host, *addr) == 0) { log_message(LOG_ERR, "transparent: destination IP %s is local " "on socket fd %d", From 54ae2d2a19d8c4a621fdcfd963412b729a3238bc Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 02:21:12 +0100 Subject: [PATCH 136/265] tests: add some AddHeader directives --- tests/scripts/run_tests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index 3cb139ad..eb1d3c42 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -96,6 +96,9 @@ ConnectPort 563 #FilterURLs On Filter "$TINYPROXY_FILTER_FILE" XTinyproxy Yes +AddHeader "X-My-Header1" "Powered by Tinyproxy" +AddHeader "X-My-Header2" "Powered by Tinyproxy" +AddHeader "X-My-Header3" "Powered by Tinyproxy" EOF cat << 'EOF' > $TINYPROXY_FILTER_FILE From 06c96761d56dca11f580ece1cca8ff2294c7f536 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 02:33:17 +0100 Subject: [PATCH 137/265] log_message_storage: use sblist --- src/log.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/log.c b/src/log.c index 755a913c..dc76c1a1 100644 --- a/src/log.c +++ b/src/log.c @@ -27,7 +27,7 @@ #include "heap.h" #include "log.h" #include "utils.h" -#include "vector.h" +#include "sblist.h" #include "conf.h" #include @@ -64,7 +64,7 @@ static int log_level = LOG_INFO; * The key is the actual messages (already filled in full), and the value * is the log level. */ -static vector_t log_message_storage; +static sblist *log_message_storage; static unsigned int logging_initialized = FALSE; /* boolean */ @@ -142,7 +142,7 @@ void log_message (int level, const char *fmt, ...) char *entry_buffer; if (!log_message_storage) { - log_message_storage = vector_create (); + log_message_storage = sblist_new (sizeof(char*), 64); if (!log_message_storage) goto out; } @@ -154,10 +154,8 @@ void log_message (int level, const char *fmt, ...) goto out; sprintf (entry_buffer, "%d %s", level, str); - vector_append (log_message_storage, entry_buffer, - strlen (entry_buffer) + 1); - - safefree (entry_buffer); + if(!sblist_add (log_message_storage, &entry_buffer)) + safefree (entry_buffer); goto out; } @@ -227,7 +225,7 @@ void log_message (int level, const char *fmt, ...) */ static void send_stored_logs (void) { - char *string; + char **string; char *ptr; int level; size_t i; @@ -237,12 +235,12 @@ static void send_stored_logs (void) log_message(LOG_DEBUG, "sending stored logs"); - for (i = 0; (ssize_t) i != vector_length (log_message_storage); ++i) { - string = - (char *) vector_getentry (log_message_storage, i, NULL); + for (i = 0; i < sblist_getsize (log_message_storage); ++i) { + string = sblist_get (log_message_storage, i); + if (!string || !*string) continue; - ptr = strchr (string, ' ') + 1; - level = atoi (string); + ptr = strchr (*string, ' ') + 1; + level = atoi (*string); #ifdef NDEBUG if (log_level == LOG_CONN && level == LOG_INFO) @@ -255,9 +253,10 @@ static void send_stored_logs (void) #endif log_message (level, "%s", ptr); + safefree(*string); } - vector_delete (log_message_storage); + sblist_free (log_message_storage); log_message_storage = NULL; log_message(LOG_DEBUG, "done sending stored logs"); From 683a3541968681c3753954dd724db58b24a1db33 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 02:37:05 +0100 Subject: [PATCH 138/265] remove vector remains --- src/Makefile.am | 1 - src/conf.h | 2 +- src/reqs.c | 2 +- src/vector.c | 214 ------------------------------------------------ src/vector.h | 77 ----------------- 5 files changed, 2 insertions(+), 294 deletions(-) delete mode 100644 src/vector.c delete mode 100644 src/vector.h diff --git a/src/Makefile.am b/src/Makefile.am index 4973eb71..51665f04 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,7 +43,6 @@ tinyproxy_SOURCES = \ text.c text.h \ main.c main.h \ utils.c utils.h \ - vector.c vector.h \ upstream.c upstream.h \ basicauth.c basicauth.h \ base64.c base64.h \ diff --git a/src/conf.h b/src/conf.h index e0fbb51a..d71208c1 100644 --- a/src/conf.h +++ b/src/conf.h @@ -23,7 +23,7 @@ #define TINYPROXY_CONF_H #include "hsearch.h" -#include "vector.h" +#include "sblist.h" #include "acl.h" /* diff --git a/src/reqs.c b/src/reqs.c index 5f26333d..49f77b6a 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -43,7 +43,7 @@ #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" diff --git a/src/vector.c b/src/vector.c deleted file mode 100644 index cf9fc75e..00000000 --- a/src/vector.c +++ /dev/null @@ -1,214 +0,0 @@ -/* tinyproxy - A fast light-weight HTTP proxy - * Copyright (C) 2002 Robert James Kaes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* A vector implementation. The vector can be of an arbitrary length, and - * the data for each entry is an lump of data (the size is stored in the - * vector.) - */ - -#include "main.h" - -#include "heap.h" -#include "vector.h" - -/* - * These structures are the storage for the "vector". Entries are - * stored in struct vectorentry_s (the data and the length), and the - * "vector" structure is implemented as a linked-list. The struct - * vector_s stores a pointer to the first vector (vector[0]) and a - * count of the number of entries (or how long the vector is.) - */ -struct vectorentry_s { - void *data; - size_t len; - - struct vectorentry_s *next; -}; - -struct vector_s { - size_t num_entries; - struct vectorentry_s *head; - struct vectorentry_s *tail; -}; - -/* - * Create an vector. The vector initially has no elements and no - * storage has been allocated for the entries. - * - * A NULL is returned if memory could not be allocated for the - * vector. - */ -vector_t vector_create (void) -{ - vector_t vector; - - vector = (vector_t) safemalloc (sizeof (struct vector_s)); - if (!vector) - return NULL; - - vector->num_entries = 0; - vector->head = vector->tail = NULL; - - return vector; -} - -/* - * Deletes an vector. All the entries when this function is run. - * - * Returns: 0 on success - * negative if a NULL vector is supplied - */ -int vector_delete (vector_t vector) -{ - struct vectorentry_s *ptr, *next; - - if (!vector) - return -EINVAL; - - ptr = vector->head; - while (ptr) { - next = ptr->next; - safefree (ptr->data); - safefree (ptr); - - ptr = next; - } - - safefree (vector); - - return 0; -} - -/* - * Appends an entry into the vector. The entry is an arbitrary - * collection of bytes of _len_ octets. The data is copied into the - * vector, so the original data must be freed to avoid a memory leak. - * The "data" must be non-NULL and the "len" must be greater than zero. - * "pos" is either 0 to prepend the data, or 1 to append the data. - * - * Returns: 0 on success - * negative number if there are errors - */ - -typedef enum { - INSERT_PREPEND, - INSERT_APPEND -} vector_pos_t; - -static int -vector_insert (vector_t vector, - void *data, - size_t len, - vector_pos_t pos) -{ - struct vectorentry_s *entry; - - if (!vector || !data || len <= 0 || - (pos != INSERT_PREPEND && pos != INSERT_APPEND)) - return -EINVAL; - - entry = - (struct vectorentry_s *) safemalloc (sizeof (struct vectorentry_s)); - if (!entry) - return -ENOMEM; - - entry->data = safemalloc (len); - if (!entry->data) { - safefree (entry); - return -ENOMEM; - } - - memcpy (entry->data, data, len); - entry->len = len; - entry->next = NULL; - - /* If there is no head or tail, create them */ - if (!vector->head && !vector->tail) - vector->head = vector->tail = entry; - else if (pos == INSERT_PREPEND) { - /* prepend the entry */ - entry->next = vector->head; - vector->head = entry; - } else { - /* append the entry */ - vector->tail->next = entry; - vector->tail = entry; - } - - vector->num_entries++; - - return 0; -} - -/* - * The following two function are used to make the API clearer. As you - * can see they simply call the vector_insert() function with appropriate - * arguments. - */ -int vector_append (vector_t vector, void *data, size_t len) -{ - return vector_insert (vector, data, len, INSERT_APPEND); -} - -int vector_prepend (vector_t vector, void *data, size_t len) -{ - return vector_insert (vector, data, len, INSERT_PREPEND); -} - -/* - * A pointer to the data at position "pos" (zero based) is returned. - * If the vector is out of bound, data is set to NULL. - * - * Returns: negative upon an error - * length of data if position is valid - */ -void *vector_getentry (vector_t vector, size_t pos, size_t * size) -{ - struct vectorentry_s *ptr; - size_t loc; - - if (!vector || pos >= vector->num_entries) - return NULL; - - loc = 0; - ptr = vector->head; - - while (loc != pos) { - ptr = ptr->next; - loc++; - } - - if (size) - *size = ptr->len; - - return ptr->data; -} - -/* - * Returns the number of entries (or the length) of the vector. - * - * Returns: negative if vector is not valid - * positive length of vector otherwise - */ -ssize_t vector_length (vector_t vector) -{ - if (!vector) - return -EINVAL; - - return vector->num_entries; -} diff --git a/src/vector.h b/src/vector.h deleted file mode 100644 index 45a78d47..00000000 --- a/src/vector.h +++ /dev/null @@ -1,77 +0,0 @@ -/* tinyproxy - A fast light-weight HTTP proxy - * Copyright (C) 2002 Robert James Kaes - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* See 'vector.c' for detailed information. */ - -#ifndef _VECTOR_H -#define _VECTOR_H - -#include - -/* - * We're using a typedef here to "hide" the implementation details of the - * vector. Sure, it's a pointer, but the struct is hidden in the C file. - * So, just use the vector_t like it's a cookie. :) - */ -typedef struct vector_s *vector_t; - -/* - * vector_create() takes no arguments. - * vector_delete() is self explanatory. - */ -extern vector_t vector_create (void); -extern int vector_delete (vector_t vector); - -/* - * When you insert a piece of data into the vector, the data will be - * duplicated, so you must free your copy if it was created on the heap. - * The data must be non-NULL and the length must be greater than zero. - * - * Returns: negative on error - * 0 upon successful insert. - */ -extern int vector_append (vector_t vector, void *data, size_t len); -extern int vector_prepend (vector_t vector, void *data, size_t len); - -/* - * A pointer to the data at position "pos" (zero based) is returned and the - * size pointer contains the length of the data stored. - * - * The pointer points to the actual data in the vector, so you have - * the power to modify the data, but do it responsibly since the - * library doesn't take any steps to prevent you from messing up the - * vector. (A better rule is, don't modify the data since you'll - * likely mess up the "length" parameter of the data.) However, DON'T - * try to realloc or free the data; doing so will break the vector. - * - * If "size" is NULL the size of the data is not returned. - * - * Returns: NULL on error - * valid pointer to data - */ -extern void *vector_getentry (vector_t vector, size_t pos, size_t * size); - -/* - * Returns the number of enteries (or the length) of the vector. - * - * Returns: negative if vector is not valid - * positive length of vector otherwise - */ -extern ssize_t vector_length (vector_t vector); - -#endif /* _VECTOR_H */ From bad36cd9cd803f1bb6baaf96b76fac3f16f90161 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 17:04:05 +0100 Subject: [PATCH 139/265] move config reload message to reload_config() move it to before disabling logging, so a message with the correct timestamp is printed if logging was already enabled. also add a message when loading finished, so one can see from the timestamp how long it took. note that this only works on a real config reload triggered by SIGHUP/SIGUSR1, because on startup we don't know yet where to log to. --- src/conf.c | 2 -- src/main.c | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/conf.c b/src/conf.c index 214475dd..9c47032b 100644 --- a/src/conf.c +++ b/src/conf.c @@ -493,8 +493,6 @@ int reload_config_file (const char *config_fname, struct config_s *conf) { int ret; - log_message (LOG_NOTICE, "Reloading config file"); - initialize_config_defaults (conf); ret = load_config_file (config_fname, conf); diff --git a/src/main.c b/src/main.c index 5b2691a3..98e024c2 100644 --- a/src/main.c +++ b/src/main.c @@ -257,6 +257,8 @@ int reload_config (int reload_logging) int ret; struct config_s *c_next = get_next_config(); + log_message (LOG_NOTICE, "Reloading config file"); + if (reload_logging) shutdown_logging (); ret = reload_config_file (config_file, c_next); @@ -269,6 +271,7 @@ int reload_config (int reload_logging) config = c_next; if (reload_logging) ret = setup_logging (); + log_message (LOG_NOTICE, "Reloading config file finished"); done: return ret; From 551e914d24e3484f3532029b7b49caa5f6f4c388 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 06:55:48 +0100 Subject: [PATCH 140/265] conf: merge upstream/upstream_none into single regex/handler --- src/conf.c | 50 ++++++++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/conf.c b/src/conf.c index 9c47032b..9ed660bc 100644 --- a/src/conf.c +++ b/src/conf.c @@ -86,7 +86,7 @@ * number. Given the usual structure of the configuration file, sixteen * substring matches should be plenty. */ -#define RE_MAX_MATCHES 16 +#define RE_MAX_MATCHES 24 /* * All configuration handling functions are REQUIRED to be defined @@ -162,7 +162,6 @@ static HANDLE_FUNC (handle_xtinyproxy); #ifdef UPSTREAM_SUPPORT static HANDLE_FUNC (handle_upstream); -static HANDLE_FUNC (handle_upstream_no); #endif static void config_free_regex (void); @@ -252,16 +251,12 @@ struct { STDCONF ("reversepath", STR "(" WS STR ")?", handle_reversepath), #endif #ifdef UPSTREAM_SUPPORT - { - BEGIN "(upstream)" WS "(none)" WS STR END, handle_upstream_no, NULL - }, - { - BEGIN "(upstream)" WS "(http|socks4|socks5)" WS - "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" - "(" IP "|" ALNUM ")" - ":" INT "(" WS STR ")?" - END, handle_upstream, NULL - }, + STDCONF ("upstream", + "(" "(none)" WS STR ")|" \ + "(" "(http|socks4|socks5)" WS \ + "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" + "(" IP "|" ALNUM ")" + ":" INT "(" WS STR ")?" ")", handle_upstream), #endif /* loglevel */ STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)", @@ -1028,10 +1023,26 @@ static enum proxy_type pt_from_string(const char *s) static HANDLE_FUNC (handle_upstream) { char *ip; - int port, mi = 2; + int port, mi; char *domain = 0, *user = 0, *pass = 0, *tmp; enum proxy_type pt; + if (match[3].rm_so != -1) { + tmp = get_string_arg (line, &match[3]); + if(!strcmp(tmp, "none")) { + safefree(tmp); + if (match[4].rm_so == -1) return -1; + domain = get_string_arg (line, &match[4]); + if (!domain) + return -1; + upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list); + safefree (domain); + return 0; + } + } + + mi = 6; + tmp = get_string_arg (line, &match[mi]); pt = pt_from_string(tmp); safefree(tmp); @@ -1066,17 +1077,4 @@ static HANDLE_FUNC (handle_upstream) return 0; } -static HANDLE_FUNC (handle_upstream_no) -{ - char *domain; - - domain = get_string_arg (line, &match[3]); - if (!domain) - return -1; - - upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list); - safefree (domain); - - return 0; -} #endif From 880a8b0ab6ab616cfab8c2048d3663fa6672ee35 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 07:07:53 +0100 Subject: [PATCH 141/265] conf: use cpp stringification for STDCONF macro --- src/conf.c | 80 +++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/conf.c b/src/conf.c index 9ed660bc..b96fbcec 100644 --- a/src/conf.c +++ b/src/conf.c @@ -177,7 +177,7 @@ static void config_free_regex (void); * do not follow the pattern above. This macro is for convenience * only. */ -#define STDCONF(d, re, func) { BEGIN "(" d ")" WS re END, func, NULL } +#define STDCONF(d, re, func) { BEGIN "(" #d ")" WS re END, func, NULL } /* * Holds the regular expression used to match the configuration directive, @@ -199,59 +199,59 @@ struct { "^[[:space:]]+$", handle_nop, NULL }, /* string arguments */ - STDCONF ("logfile", STR, handle_logfile), - STDCONF ("pidfile", STR, handle_pidfile), - STDCONF ("anonymous", STR, handle_anonymous), - STDCONF ("viaproxyname", STR, handle_viaproxyname), - STDCONF ("defaulterrorfile", STR, handle_defaulterrorfile), - STDCONF ("statfile", STR, handle_statfile), - STDCONF ("stathost", STR, handle_stathost), - STDCONF ("xtinyproxy", BOOL, handle_xtinyproxy), + STDCONF (logfile, STR, handle_logfile), + STDCONF (pidfile, STR, handle_pidfile), + STDCONF (anonymous, STR, handle_anonymous), + STDCONF (viaproxyname, STR, handle_viaproxyname), + STDCONF (defaulterrorfile, STR, handle_defaulterrorfile), + STDCONF (statfile, STR, handle_statfile), + STDCONF (stathost, STR, handle_stathost), + STDCONF (xtinyproxy, BOOL, handle_xtinyproxy), /* boolean arguments */ - STDCONF ("syslog", BOOL, handle_syslog), - STDCONF ("bindsame", BOOL, handle_bindsame), - STDCONF ("disableviaheader", BOOL, handle_disableviaheader), + STDCONF (syslog, BOOL, handle_syslog), + STDCONF (bindsame, BOOL, handle_bindsame), + STDCONF (disableviaheader, BOOL, handle_disableviaheader), /* integer arguments */ - STDCONF ("port", INT, handle_port), - STDCONF ("maxclients", INT, handle_maxclients), - STDCONF ("maxspareservers", INT, handle_obsolete), - STDCONF ("minspareservers", INT, handle_obsolete), - STDCONF ("startservers", INT, handle_obsolete), - STDCONF ("maxrequestsperchild", INT, handle_obsolete), - STDCONF ("timeout", INT, handle_timeout), - STDCONF ("connectport", INT, handle_connectport), + STDCONF (port, INT, handle_port), + STDCONF (maxclients, INT, handle_maxclients), + STDCONF (maxspareservers, INT, handle_obsolete), + STDCONF (minspareservers, INT, handle_obsolete), + STDCONF (startservers, INT, handle_obsolete), + STDCONF (maxrequestsperchild, INT, handle_obsolete), + STDCONF (timeout, INT, handle_timeout), + STDCONF (connectport, INT, handle_connectport), /* alphanumeric arguments */ - STDCONF ("user", ALNUM, handle_user), - STDCONF ("group", ALNUM, handle_group), + STDCONF (user, ALNUM, handle_user), + STDCONF (group, ALNUM, handle_group), /* ip arguments */ - STDCONF ("listen", "(" IP "|" IPV6 ")", handle_listen), - STDCONF ("allow", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", + STDCONF (listen, "(" IP "|" IPV6 ")", handle_listen), + STDCONF (allow, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", handle_allow), - STDCONF ("deny", "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", + STDCONF (deny, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", handle_deny), - STDCONF ("bind", "(" IP "|" IPV6 ")", handle_bind), + STDCONF (bind, "(" IP "|" IPV6 ")", handle_bind), /* other */ - STDCONF ("basicauth", ALNUM WS ALNUM, handle_basicauth), - STDCONF ("errorfile", INT WS STR, handle_errorfile), - STDCONF ("addheader", STR WS STR, handle_addheader), + STDCONF (basicauth, ALNUM WS ALNUM, handle_basicauth), + STDCONF (errorfile, INT WS STR, handle_errorfile), + STDCONF (addheader, STR WS STR, handle_addheader), #ifdef FILTER_ENABLE /* filtering */ - STDCONF ("filter", STR, handle_filter), - STDCONF ("filterurls", BOOL, handle_filterurls), - STDCONF ("filterextended", BOOL, handle_filterextended), - STDCONF ("filterdefaultdeny", BOOL, handle_filterdefaultdeny), - STDCONF ("filtercasesensitive", BOOL, handle_filtercasesensitive), + STDCONF (filter, STR, handle_filter), + STDCONF (filterurls, BOOL, handle_filterurls), + STDCONF (filterextended, BOOL, handle_filterextended), + STDCONF (filterdefaultdeny, BOOL, handle_filterdefaultdeny), + STDCONF (filtercasesensitive, BOOL, handle_filtercasesensitive), #endif #ifdef REVERSE_SUPPORT /* Reverse proxy arguments */ - STDCONF ("reversebaseurl", STR, handle_reversebaseurl), - STDCONF ("reverseonly", BOOL, handle_reverseonly), - STDCONF ("reversemagic", BOOL, handle_reversemagic), - STDCONF ("reversepath", STR "(" WS STR ")?", handle_reversepath), + STDCONF (reversebaseurl, STR, handle_reversebaseurl), + STDCONF (reverseonly, BOOL, handle_reverseonly), + STDCONF (reversemagic, BOOL, handle_reversemagic), + STDCONF (reversepath, STR "(" WS STR ")?", handle_reversepath), #endif #ifdef UPSTREAM_SUPPORT - STDCONF ("upstream", + STDCONF (upstream, "(" "(none)" WS STR ")|" \ "(" "(http|socks4|socks5)" WS \ "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" @@ -259,7 +259,7 @@ struct { ":" INT "(" WS STR ")?" ")", handle_upstream), #endif /* loglevel */ - STDCONF ("loglevel", "(critical|error|warning|notice|connect|info)", + STDCONF (loglevel, "(critical|error|warning|notice|connect|info)", handle_loglevel) }; From 99ed66cbc47b1fc1ba7572057c0776ccbc6d81d6 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 20:37:10 +0100 Subject: [PATCH 142/265] conf.c: warn when encountering invalid address --- src/conf.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/conf.c b/src/conf.c index b96fbcec..aa1a770a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -774,11 +774,16 @@ static HANDLE_FUNC (handle_group) return set_string_arg (&conf->group, line, &match[2]); } +static void warn_invalid_address(char *arg, unsigned long lineno) { + log_message (LOG_WARNING, "Invalid address %s on line %lu", arg, lineno); +} + static HANDLE_FUNC (handle_allow) { char *arg = get_string_arg (line, &match[2]); - insert_acl (arg, ACL_ALLOW, &conf->access_list); + if(insert_acl (arg, ACL_ALLOW, &conf->access_list) < 0) + warn_invalid_address (arg, lineno); safefree (arg); return 0; } @@ -787,7 +792,8 @@ static HANDLE_FUNC (handle_deny) { char *arg = get_string_arg (line, &match[2]); - insert_acl (arg, ACL_DENY, &conf->access_list); + if(insert_acl (arg, ACL_DENY, &conf->access_list) < 0) + warn_invalid_address (arg, lineno); safefree (arg); return 0; } From 0ad8904b40d699405f60655606db42475c011b67 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 20:37:30 +0100 Subject: [PATCH 143/265] acl.c: detect invalid ipv6 string --- src/acl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/acl.c b/src/acl.c index 75216c3e..261b7ddd 100644 --- a/src/acl.c +++ b/src/acl.c @@ -163,6 +163,9 @@ insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list) memset (acl.address.ip.mask, 0xff, IPV6_LEN); } else { int i; + /* bogus ipv6 ? */ + if (strchr (location, ':')) + return -1; /* * At this point we're either a hostname or an From caeab31fcaab5b4fc1985314447c4509f96c7265 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 20:39:34 +0100 Subject: [PATCH 144/265] conf.c: simplify the huge IPV6 regex even though the existing IPV6 regex caught (almost?) all invalid ipv6 addresses, it did so with a huge performance penalty. parsing a file with 32K allow or deny statement took 30 secs in a test setup, after this change less than 3. the new regex is sufficient to recognize all valid ipv6 addresses, and hands down the responsibility to detect corner cases to the system's inet_pton() function, which is e.g. called from insert_acl(), which now causes a warning to be printed in the log if a seemingly valid address is in fact invalid. the new regex has been tested with 486 testcases from http://download.dartware.com/thirdparty/test-ipv6-regex.pl and accepts all valid ones and rejects most of the invalid ones. note that the IPV4 regex already did a similar thing and checked only whether the ip looks like [0-9]+.[0-9]+.[0-9]+.[0-9]+ without pedantry. --- src/conf.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/src/conf.c b/src/conf.c index aa1a770a..4a845c1d 100644 --- a/src/conf.c +++ b/src/conf.c @@ -57,24 +57,8 @@ #define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" #define IPMASK "(" IP "(/[[:digit:]]+)?)" #define IPV6 "(" \ - "(([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,6})|" \ - "(([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,5})|" \ - "(([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,4})|" \ - "(([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,3})|" \ - "(([0-9a-f]{1,4}:){1,5}(:[0-9a-f]{1,4}){1,2})|" \ - "(([0-9a-f]{1,4}:){1,6}(:[0-9a-f]{1,4}){1,1})|" \ - "((([0-9a-f]{1,4}:){1,7}|:):)|" \ - "(:(:[0-9a-f]{1,4}){1,7})|" \ - "([0-9a-f]{1,4}(:[0-9a-f]{1,4}){1,7})|" \ - "(((([0-9a-f]{1,4}:){6})(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}))|" \ - "((([0-9a-f]{1,4}:){5}[0-9a-f]{1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}))|" \ - "(([0-9a-f]{1,4}:){5}:[0-9a-f]{1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(([0-9a-f]{1,4}:){1,1}(:[0-9a-f]{1,4}){1,4}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(([0-9a-f]{1,4}:){1,2}(:[0-9a-f]{1,4}){1,3}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(([0-9a-f]{1,4}:){1,3}(:[0-9a-f]{1,4}){1,2}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(([0-9a-f]{1,4}:){1,4}(:[0-9a-f]{1,4}){1,1}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "((([0-9a-f]{1,4}:){1,5}|:):(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})|" \ - "(:(:[0-9a-f]{1,4}){1,5}:(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3})" \ + "(([0-9a-f:]{2,39}))|" \ + "(([0-9a-f:]{0,29}:" IP "))" \ ")" #define IPV6MASK "(" IPV6 "(/[[:digit:]]+)?)" From 45323584a0b24dc82d583cb34c85051363488bc9 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 18:23:35 +0100 Subject: [PATCH 145/265] speed up big config parsing by 2x using gperf --- src/Makefile.am | 4 +++ src/conf-tokens.gperf | 61 +++++++++++++++++++++++++++++++++++++++ src/conf-tokens.h | 53 ++++++++++++++++++++++++++++++++++ src/conf.c | 66 ++++++++++++++++++++++++------------------- src/conf.h | 2 +- src/main.c | 2 +- 6 files changed, 157 insertions(+), 31 deletions(-) create mode 100644 src/conf-tokens.gperf create mode 100644 src/conf-tokens.h diff --git a/src/Makefile.am b/src/Makefile.am index 51665f04..f028e4af 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,6 +29,7 @@ tinyproxy_SOURCES = \ buffer.c buffer.h \ child.c child.h \ common.h \ + conf-tokens.c conf-tokens.h \ conf.c conf.h \ conns.c conns.h \ daemon.c daemon.h \ @@ -58,3 +59,6 @@ EXTRA_tinyproxy_SOURCES = filter.c filter.h \ transparent-proxy.c transparent-proxy.h tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@ tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread + +conf-tokens.c: conf-tokens.gperf + gperf $< > $@ diff --git a/src/conf-tokens.gperf b/src/conf-tokens.gperf new file mode 100644 index 00000000..ef93245c --- /dev/null +++ b/src/conf-tokens.gperf @@ -0,0 +1,61 @@ +%{ +#include +#include +#include "conf-tokens.h" +%} + +struct config_directive_entry { const char* name; enum config_directive value; }; + +%struct-type +%define slot-name name +%define initializer-suffix ,CD_NIL +%define lookup-function-name config_directive_find +%ignore-case +%7bit +%compare-lengths +%readonly-tables +%define constants-prefix CDS_ +%omit-struct-type + +%% +logfile, CD_logfile +pidfile, CD_pidfile +anonymous, CD_anonymous +viaproxyname, CD_viaproxyname +defaulterrorfile, CD_defaulterrorfile +statfile, CD_statfile +stathost, CD_stathost +xtinyproxy, CD_xtinyproxy +syslog, CD_syslog +bindsame, CD_bindsame +disableviaheader, CD_disableviaheader +port, CD_port +maxclients, CD_maxclients +maxspareservers, CD_maxspareservers +minspareservers, CD_minspareservers +startservers, CD_startservers +maxrequestsperchild, CD_maxrequestsperchild +timeout, CD_timeout +connectport, CD_connectport +user, CD_user +group, CD_group +listen, CD_listen +allow, CD_allow +deny, CD_deny +bind, CD_bind +basicauth, CD_basicauth +errorfile, CD_errorfile +addheader, CD_addheader +filter, CD_filter +filterurls, CD_filterurls +filterextended, CD_filterextended +filterdefaultdeny, CD_filterdefaultdeny +filtercasesensitive, CD_filtercasesensitive +reversebaseurl, CD_reversebaseurl +reverseonly, CD_reverseonly +reversemagic, CD_reversemagic +reversepath, CD_reversepath +upstream, CD_upstream +loglevel, CD_loglevel +%% + diff --git a/src/conf-tokens.h b/src/conf-tokens.h new file mode 100644 index 00000000..d9f03cd7 --- /dev/null +++ b/src/conf-tokens.h @@ -0,0 +1,53 @@ +#ifndef CONF_TOKENS_H +#define CONF_TOKENS_H + +enum config_directive { +CD_NIL = 0, +CD_logfile, +CD_pidfile, +CD_anonymous, +CD_viaproxyname, +CD_defaulterrorfile, +CD_statfile, +CD_stathost, +CD_xtinyproxy, +CD_syslog, +CD_bindsame, +CD_disableviaheader, +CD_port, +CD_maxclients, +CD_maxspareservers, +CD_minspareservers, +CD_startservers, +CD_maxrequestsperchild, +CD_timeout, +CD_connectport, +CD_user, +CD_group, +CD_listen, +CD_allow, +CD_deny, +CD_bind, +CD_basicauth, +CD_errorfile, +CD_addheader, +CD_filter, +CD_filterurls, +CD_filterextended, +CD_filterdefaultdeny, +CD_filtercasesensitive, +CD_reversebaseurl, +CD_reverseonly, +CD_reversemagic, +CD_reversepath, +CD_upstream, +CD_loglevel, +}; + +struct config_directive_entry { const char* name; enum config_directive value; }; + +const struct config_directive_entry * +config_directive_find (register const char *str, register size_t len); + +#endif + diff --git a/src/conf.c b/src/conf.c index 4a845c1d..e7e0f327 100644 --- a/src/conf.c +++ b/src/conf.c @@ -37,6 +37,7 @@ #include "upstream.h" #include "connect-ports.h" #include "basicauth.h" +#include "conf-tokens.h" /* * The configuration directives are defined in the structure below. Each @@ -98,10 +99,13 @@ typedef int (*CONFFILE_HANDLER) (struct config_s *, const char *, * List all the handling functions. These are defined later, but they need * to be in-scope before the big structure below. */ -static HANDLE_FUNC (handle_nop) +static HANDLE_FUNC (handle_disabled_feature) { - return 0; -} /* do nothing function */ + fprintf (stderr, "ERROR: accessing feature that was disabled at compiletime on line %lu\n", + lineno); + + return -1; +} static HANDLE_FUNC (handle_allow); static HANDLE_FUNC (handle_basicauth); @@ -161,7 +165,7 @@ static void config_free_regex (void); * do not follow the pattern above. This macro is for convenience * only. */ -#define STDCONF(d, re, func) { BEGIN "(" #d ")" WS re END, func, NULL } +#define STDCONF(d, re, func) [CD_ ## d] = { BEGIN "()" WS re END, func, NULL } /* * Holds the regular expression used to match the configuration directive, @@ -174,14 +178,6 @@ struct { CONFFILE_HANDLER handler; regex_t *cre; } directives[] = { - /* comments */ - { - BEGIN "#", handle_nop, NULL - }, - /* blank lines */ - { - "^[[:space:]]+$", handle_nop, NULL - }, /* string arguments */ STDCONF (logfile, STR, handle_logfile), STDCONF (pidfile, STR, handle_pidfile), @@ -326,20 +322,25 @@ void free_config (struct config_s *conf) } /* + * Initializes Config parser. Currently this means: * Compiles the regular expressions used by the configuration file. This * routine MUST be called before trying to parse the configuration file. * * Returns 0 on success; negative upon failure. */ int -config_compile_regex (void) +config_init (void) { unsigned int i, r; for (i = 0; i != ndirectives; ++i) { - assert (directives[i].handler); assert (!directives[i].cre); + if (!directives[i].handler) { + directives[i].handler = handle_disabled_feature; + continue; + } + directives[i].cre = (regex_t *) safemalloc (sizeof (regex_t)); if (!directives[i].cre) return -1; @@ -383,20 +384,17 @@ config_free_regex (void) * a negative number is returned. */ static int check_match (struct config_s *conf, const char *line, - unsigned long lineno) + unsigned long lineno, enum config_directive cd) { regmatch_t match[RE_MAX_MATCHES]; - unsigned int i; - - assert (ndirectives > 0); + unsigned int i = cd; - for (i = 0; i != ndirectives; ++i) { - assert (directives[i].cre); - if (!regexec - (directives[i].cre, line, RE_MAX_MATCHES, match, 0)) - return (*directives[i].handler) (conf, line, lineno, match); - } + if (!directives[i].cre) + return (*directives[i].handler) (conf, line, lineno, match); + if (!regexec + (directives[i].cre, line, RE_MAX_MATCHES, match, 0)) + return (*directives[i].handler) (conf, line, lineno, match); return -1; } @@ -405,15 +403,25 @@ static int check_match (struct config_s *conf, const char *line, */ static int config_parse (struct config_s *conf, FILE * f) { - char buffer[LINE_MAX]; + char buffer[LINE_MAX], *p, *q, c; + const struct config_directive_entry *e; unsigned long lineno = 1; - while (fgets (buffer, sizeof (buffer), f)) { - if (check_match (conf, buffer, lineno)) { - printf ("Syntax error on line %ld\n", lineno); + for (;fgets (buffer, sizeof (buffer), f);++lineno) { + if(buffer[0] == '#') continue; + p = buffer; + while(isspace(*p))p++; + if(!*p) continue; + q = p; + while(!isspace(*q))q++; + c = *q; + *q = 0; + e = config_directive_find(p, strlen(p)); + *q = c; + if (!e || e->value == CD_NIL || check_match (conf, q, lineno, e->value)) { + fprintf (stderr, "ERROR: Syntax error on line %lu\n", lineno); return 1; } - ++lineno; } return 0; } diff --git a/src/conf.h b/src/conf.h index d71208c1..9453d660 100644 --- a/src/conf.h +++ b/src/conf.h @@ -115,7 +115,7 @@ struct config_s { extern int reload_config_file (const char *config_fname, struct config_s *conf); -int config_compile_regex (void); +int config_init (void); void free_config (struct config_s *conf); #endif diff --git a/src/main.c b/src/main.c index 98e024c2..deb1f6de 100644 --- a/src/main.c +++ b/src/main.c @@ -300,7 +300,7 @@ main (int argc, char **argv) log_message (LOG_NOTICE, "Initializing " PACKAGE " ..."); - if (config_compile_regex()) { + if (config_init()) { exit (EX_SOFTWARE); } From 45b238fc6f2dc4705578e6a35cf6031e818a04c0 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 20:38:20 +0100 Subject: [PATCH 146/265] main: print error when config_init() fails --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index deb1f6de..c478ccfc 100644 --- a/src/main.c +++ b/src/main.c @@ -301,6 +301,7 @@ main (int argc, char **argv) log_message (LOG_NOTICE, "Initializing " PACKAGE " ..."); if (config_init()) { + fprintf(stderr, "ERROR: config_init() failed\n"); exit (EX_SOFTWARE); } From 22e4898519e9db7aabacad1343c20fc49176dbf4 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Sep 2020 23:04:12 +0100 Subject: [PATCH 147/265] add autoconf test and fallback code for systems without gperf --- configure.ac | 9 ++++++ src/Makefile.am | 7 +++-- src/conf-tokens.c | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 src/conf-tokens.c diff --git a/configure.ac b/configure.ac index cf2f19c2..00f7f0e6 100644 --- a/configure.ac +++ b/configure.ac @@ -204,6 +204,15 @@ fi #manpage_support_enabled AM_CONDITIONAL(HAVE_POD2MAN, test "x$POD2MAN" != "x" -a "x$POD2MAN" != "xno") +AC_PATH_PROG(GPERF, gperf, no) +AM_CONDITIONAL(HAVE_GPERF, test "x$GPERF" != "x" -a "x$GPERF" != "xno") +AH_TEMPLATE([HAVE_GPERF], + [Whether you have gperf installed for faster config parsing.]) + +if test "x$GPERF" != "x" -a "x$GPERF" != "xno" ; then + AC_DEFINE(HAVE_GPERF) +fi + AC_CONFIG_FILES([ Makefile src/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index f028e4af..9c8458e3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -60,5 +60,8 @@ EXTRA_tinyproxy_SOURCES = filter.c filter.h \ tinyproxy_DEPENDENCIES = @ADDITIONAL_OBJECTS@ tinyproxy_LDADD = @ADDITIONAL_OBJECTS@ -lpthread -conf-tokens.c: conf-tokens.gperf - gperf $< > $@ +if HAVE_GPERF +conf-tokens.c: conf-tokens-gperf.inc +conf-tokens-gperf.inc: conf-tokens.gperf + $(GPERF) $< > $@ +endif diff --git a/src/conf-tokens.c b/src/conf-tokens.c new file mode 100644 index 00000000..bad70136 --- /dev/null +++ b/src/conf-tokens.c @@ -0,0 +1,70 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "conf-tokens.h" + +#ifdef HAVE_GPERF +#include "conf-tokens-gperf.inc" +#else + +#include + +const struct config_directive_entry * +config_directive_find (register const char *str, register size_t len) +{ + size_t i; + static const struct config_directive_entry wordlist[] = + { + {"",CD_NIL}, {"",CD_NIL}, + {"allow", CD_allow}, + {"stathost", CD_stathost}, + {"listen", CD_listen}, + {"timeout", CD_timeout}, + {"statfile", CD_statfile}, + {"pidfile", CD_pidfile}, + {"bindsame", CD_bindsame}, + {"reversebaseurl", CD_reversebaseurl}, + {"viaproxyname", CD_viaproxyname}, + {"upstream", CD_upstream}, + {"anonymous", CD_anonymous}, + {"group", CD_group}, + {"defaulterrorfile", CD_defaulterrorfile}, + {"startservers", CD_startservers}, + {"filtercasesensitive", CD_filtercasesensitive}, + {"filterurls", CD_filterurls}, + {"filter", CD_filter}, + {"reversemagic", CD_reversemagic}, + {"errorfile", CD_errorfile}, + {"minspareservers", CD_minspareservers}, + {"user", CD_user}, + {"disableviaheader", CD_disableviaheader}, + {"deny", CD_deny}, + {"xtinyproxy", CD_xtinyproxy}, + {"reversepath", CD_reversepath}, + {"bind", CD_bind}, + {"maxclients", CD_maxclients}, + {"reverseonly", CD_reverseonly}, + {"port", CD_port}, + {"maxspareservers", CD_maxspareservers}, + {"syslog", CD_syslog}, + {"filterdefaultdeny", CD_filterdefaultdeny}, + {"loglevel", CD_loglevel}, + {"filterextended", CD_filterextended}, + {"connectport", CD_connectport}, + {"logfile", CD_logfile}, + {"basicauth", CD_basicauth}, + {"addheader", CD_addheader}, + {"maxrequestsperchild", CD_maxrequestsperchild} + }; + + for(i=0;i Date: Thu, 17 Sep 2020 21:03:51 +0100 Subject: [PATCH 148/265] tune error messages to show select or poll depending on what is used --- src/child.c | 8 ++++---- src/mypoll.h | 2 ++ src/reqs.c | 8 ++++---- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/child.c b/src/child.c index 89a081c5..a4a90f6a 100644 --- a/src/child.c +++ b/src/child.c @@ -99,7 +99,7 @@ void child_main_loop (void) /* * We have to wait for connections on multiple fds, - * so use select. + * so use select/poll/whatever. */ while (!config->quit) { @@ -136,11 +136,11 @@ void child_main_loop (void) if (errno == EINTR) { continue; } - log_message (LOG_ERR, "error calling select: %s", + log_message (LOG_ERR, "error calling " SELECT_OR_POLL ": %s", strerror(errno)); continue; } else if (ret == 0) { - log_message (LOG_WARNING, "Strange: select returned 0 " + log_message (LOG_WARNING, "Strange: " SELECT_OR_POLL " returned 0 " "but we did not specify a timeout..."); continue; } @@ -158,7 +158,7 @@ void child_main_loop (void) if (listenfd == -1) { log_message(LOG_WARNING, "Strange: None of our listen " - "fds was readable after select"); + "fds was readable after " SELECT_OR_POLL); continue; } diff --git a/src/mypoll.h b/src/mypoll.h index a3366802..87360156 100644 --- a/src/mypoll.h +++ b/src/mypoll.h @@ -4,6 +4,7 @@ #include "config.h" #ifdef HAVE_POLL_H +#define SELECT_OR_POLL "poll" #include typedef struct pollfd pollfd_struct; @@ -13,6 +14,7 @@ typedef struct pollfd pollfd_struct; #else +#define SELECT_OR_POLL "select" #include typedef struct mypollfd { int fd; diff --git a/src/reqs.c b/src/reqs.c index 49f77b6a..f6a92331 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1163,11 +1163,11 @@ static void relay_connection (struct conn_s *connptr) if (ret == 0) { log_message (LOG_INFO, - "Idle Timeout (after select)"); + "Idle Timeout (after " SELECT_OR_POLL ")"); return; } 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); @@ -1440,7 +1440,7 @@ get_request_entity(struct conn_s *connptr) 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"); @@ -1459,7 +1459,7 @@ get_request_entity(struct conn_s *connptr) 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; From d4ef2cfa625d4a9011d61dcc7537963906ff3105 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 17 Sep 2020 21:13:28 +0100 Subject: [PATCH 149/265] child_kill_children(): use method that actually works it turned out that close()ing an fd behind the back of a thread doesn't actually cause blocking operations to get a read/write event, because the fd will stay valid to in-progress operations. --- src/child.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/child.c b/src/child.c index a4a90f6a..985357da 100644 --- a/src/child.c +++ b/src/child.c @@ -222,20 +222,24 @@ void child_main_loop (void) */ void child_kill_children (int sig) { - size_t i; + size_t i, tries = 0; if (sig != SIGTERM) return; + log_message (LOG_INFO, + "trying to bring down %zu threads...", + sblist_getsize(childs) + ); + +again: for (i = 0; i < sblist_getsize(childs); i++) { struct child *c = *((struct child**)sblist_get(childs, i)); - if (!c->done) { - /* interrupt blocking operations. - this should cause the threads to shutdown orderly. */ - close(c->conn.client_fd); - } + if (!c->done) pthread_kill(c->thread, SIGCHLD); } - usleep(16); + usleep(8192); collect_threads(); + if (sblist_getsize(childs) != 0) + if(tries++ < 8) goto again; if (sblist_getsize(childs) != 0) log_message (LOG_CRIT, "child_kill_children: %zu threads still alive!", From c74fe5726259c508a541eb5b97ca1def7733a5c9 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 18 Sep 2020 12:12:14 +0100 Subject: [PATCH 150/265] transparent: workaround old glibc bug on RHEL7 it's been reported[0] that RHEL7 fails to properly set the length parameter of the getsockname() call to the length of the required struct sockaddr type, and always returns the length passed if it is big enough. the SOCKADDR_UNION_* macros originate from my microsocks[1] project, and facilitate handling of the sockaddr mess without nasty casts. [0]: https://github.com/tinyproxy/tinyproxy/issues/45#issuecomment-694594990 [1]: https://github.com/rofl0r/microsocks --- src/sock.h | 14 ++++++++++++++ src/transparent-proxy.c | 8 +++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/sock.h b/src/sock.h index 722371de..aee5bf59 100644 --- a/src/sock.h +++ b/src/sock.h @@ -31,6 +31,20 @@ #include "common.h" #include "sblist.h" +#define SOCKADDR_UNION_AF(PTR) (PTR)->v4.sin_family + +#define SOCKADDR_UNION_LENGTH(PTR) ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? sizeof((PTR)->v4) : ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? sizeof((PTR)->v6) : 0 ) ) + +#define SOCKADDR_UNION_ADDRESS(PTR) ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? (void*) &(PTR)->v4.sin_addr : ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? (void*) &(PTR)->v6.sin6_addr : (void*) 0 ) ) + +#define SOCKADDR_UNION_PORT(PTR) ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET ) ? (PTR)->v4.sin_port : ( \ + ( SOCKADDR_UNION_AF(PTR) == AF_INET6 ) ? (PTR)->v6.sin6_port : 0 ) ) + union sockaddr_union { struct sockaddr_in v4; struct sockaddr_in6 v6; diff --git a/src/transparent-proxy.c b/src/transparent-proxy.c index e3265f9f..d090ae37 100644 --- a/src/transparent-proxy.c +++ b/src/transparent-proxy.c @@ -83,16 +83,14 @@ do_transparent_proxy (struct conn_s *connptr, orderedmap hashofheaders, return 0; } - af = length == sizeof(dest_addr.v4) ? AF_INET : AF_INET6; - if (af == AF_INET) dest_inaddr = &dest_addr.v4.sin_addr; - else dest_inaddr = &dest_addr.v6.sin6_addr; + af = SOCKADDR_UNION_AF(&dest_addr); + dest_inaddr = SOCKADDR_UNION_ADDRESS(&dest_addr); if (!inet_ntop(af, dest_inaddr, namebuf, sizeof namebuf)) goto addr_err; request->host = safestrdup (namebuf); - request->port = ntohs (af == AF_INET ? dest_addr.v4.sin_port - : dest_addr.v6.sin6_port); + request->port = ntohs (SOCKADDR_UNION_PORT(&dest_addr)); request->path = (char *) safemalloc (ulen + 1); strlcpy (request->path, *url, ulen + 1); From 4dfac863a5b8b310133e1ee5e9186f7604b0a33f Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 27 Sep 2020 15:41:54 +0100 Subject: [PATCH 151/265] version.sh: replace -g with -git- git describe prefixes the sha1 commit hash with -g, which is exactly what we're after. this change gets rid of the confusing "g" in the commit hash and allows tag names that include "-". --- scripts/version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/version.sh b/scripts/version.sh index f3948bcc..2de4c21e 100755 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -9,7 +9,7 @@ if test -d "${GIT_DIR}" ; then if test "x$?" != x0 ; then sed 's/$/-git/' < VERSION else - printf "%s\n" "$gitstr" | sed -e 's/-/-git-/' + printf "%s\n" "$gitstr" | sed -e 's/-g/-git-/' fi else sed 's/$/-git/' < VERSION From 094db9d6703a5077520d80ed70ff66b8f1766d55 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 27 Sep 2020 15:44:50 +0100 Subject: [PATCH 152/265] version.sh: relax regex for release tag detection this allows to use tag names with a custom suffix too. --- scripts/version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/version.sh b/scripts/version.sh index 2de4c21e..03fb3aa0 100755 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -5,7 +5,7 @@ GIT_DIR="${SCRIPT_DIR}/../.git" if test -d "${GIT_DIR}" ; then if type git >/dev/null 2>&1 ; then - gitstr=$(git describe --match '[0-9]*.[0-9]*.[0-9]*' 2>/dev/null) + gitstr=$(git describe --match '[0-9]*.[0-9]*.*' 2>/dev/null) if test "x$?" != x0 ; then sed 's/$/-git/' < VERSION else From 8594e9b8cce214507711630cd642ea5788437d31 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 27 Sep 2020 15:55:23 +0100 Subject: [PATCH 153/265] add conf-tokens.gperf to EXTRA_DIST otherwise it will be missing in `make dist`-generated tarballs. --- src/Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile.am b/src/Makefile.am index 9c8458e3..6d806e03 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -65,3 +65,6 @@ conf-tokens.c: conf-tokens-gperf.inc conf-tokens-gperf.inc: conf-tokens.gperf $(GPERF) $< > $@ endif + +EXTRA_DIST = conf-tokens.gperf + From 7c664ad0b2280cfb5ee1ba923f362fceee4c89ca Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 27 Sep 2020 16:22:21 +0100 Subject: [PATCH 154/265] Release 1.11.0-rc1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1cac385c..01945ef8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.0 +1.11.0-rc1 From 35c8edcf73056e20b0c055acb2f286cc37093ce3 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 30 Sep 2020 05:13:45 +0100 Subject: [PATCH 155/265] speed up build by only including regex.h where needed --- src/common.h | 1 - src/conf.c | 1 + src/filter.c | 1 + src/html-error.c | 1 + 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/common.h b/src/common.h index 47a1ed18..563ee6fc 100644 --- a/src/common.h +++ b/src/common.h @@ -68,7 +68,6 @@ # include # include # include -# include /* rest - some oddball headers */ #ifdef HAVE_VALUES_H diff --git a/src/conf.c b/src/conf.c index e7e0f327..91124bb3 100644 --- a/src/conf.c +++ b/src/conf.c @@ -23,6 +23,7 @@ * add new directives to. Who knows if I'm right though. */ +#include #include "common.h" #include "conf.h" diff --git a/src/filter.c b/src/filter.c index 8a0b0855..d70cb59d 100644 --- a/src/filter.c +++ b/src/filter.c @@ -24,6 +24,7 @@ #include "main.h" +#include #include "filter.h" #include "heap.h" #include "log.h" diff --git a/src/html-error.c b/src/html-error.c index 7c71f74a..78c3ef01 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -20,6 +20,7 @@ * HTML error pages with variable substitution. */ +#include #include "main.h" #include "common.h" From 4f1a1663ff748d0c7fb3f7d600cdfdadf16ea887 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 30 Sep 2020 05:14:57 +0100 Subject: [PATCH 156/265] conf: remove bogus support for hex literals the INT regex macro supported a 0x prefix (used e.g. for port numbers), however following that, only digits were accepted, and not the full range of hexdigits. it's unlikely this was used, so remove it. note that the () expression is kept, so we don't have to adjust match number indices all over the place. --- src/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index 91124bb3..b17f6eeb 100644 --- a/src/conf.c +++ b/src/conf.c @@ -52,7 +52,7 @@ #define WS "[[:space:]]+" #define STR "\"([^\"]+)\"" #define BOOL "(yes|on|no|off)" -#define INT "((0x)?[[:digit:]]+)" +#define INT "(()[[:digit:]]+)" #define ALNUM "([-a-z0-9._]+)" #define USERNAME "([^:]*)" #define PASSWORD "([^@]*)" From 10494cab8c19843aa415229e3b796d0322384cb3 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 30 Sep 2020 05:19:16 +0100 Subject: [PATCH 157/265] change loglevel of "Not running as root" message to INFO there's no reason to display this as warning. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index c478ccfc..13092844 100644 --- a/src/main.c +++ b/src/main.c @@ -381,7 +381,7 @@ main (int argc, char **argv) if (geteuid () == 0) change_user (argv[0]); else - log_message (LOG_WARNING, + log_message (LOG_INFO, "Not running as root, so not changing UID/GID."); /* Create log file after we drop privileges */ From f55c46eb397e0371b03ba91276beea9e8dd95d87 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 30 Sep 2020 05:20:09 +0100 Subject: [PATCH 158/265] log: print timestamps with millisecond precision this allows easier time measurements for benchmarks. --- src/log.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/log.c b/src/log.c index dc76c1a1..873abb2c 100644 --- a/src/log.c +++ b/src/log.c @@ -108,7 +108,7 @@ void set_log_level (int level) void log_message (int level, const char *fmt, ...) { va_list args; - time_t nowtime; + struct timespec nowtime; char time_string[TIME_LENGTH]; char str[STRING_LENGTH]; @@ -174,13 +174,14 @@ void log_message (int level, const char *fmt, ...) } else { char *p; - nowtime = time (NULL); + clock_gettime(CLOCK_REALTIME, &nowtime); /* Format is month day hour:minute:second (24 time) */ strftime (time_string, TIME_LENGTH, "%b %d %H:%M:%S", - localtime (&nowtime)); + localtime (&nowtime.tv_sec)); - snprintf (str, STRING_LENGTH, "%-9s %s [%ld]: ", + snprintf (str, STRING_LENGTH, "%-9s %s.%03u [%ld]: ", syslog_level[level], time_string, + nowtime.tv_nsec/1000000u, (long int) getpid ()); /* From 960972865c0722a9f769d642e4ef36769439c444 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 30 Sep 2020 05:21:26 +0100 Subject: [PATCH 159/265] print linenumber from all conf-emitted warnings --- src/conf.c | 27 +++++++++++++++------------ src/upstream.c | 49 ++++++++++++++++++++++++++++++------------------- src/upstream.h | 14 +++++++++++++- 3 files changed, 58 insertions(+), 32 deletions(-) diff --git a/src/conf.c b/src/conf.c index b17f6eeb..f2b8c311 100644 --- a/src/conf.c +++ b/src/conf.c @@ -74,6 +74,9 @@ */ #define RE_MAX_MATCHES 24 +#define CP_WARN(FMT, ...) \ + log_message (LOG_WARNING, "line %lu: " FMT, lineno, __VA_ARGS__) + /* * All configuration handling functions are REQUIRED to be defined * with the same function template as below. @@ -638,9 +641,7 @@ static HANDLE_FUNC (handle_anonymous) return -1; if(anonymous_insert (conf, arg) < 0) { - log_message (LOG_WARNING, - "anonymous_insert() failed: '%s'", - arg); + CP_WARN ("anonymous_insert() failed: '%s'", arg); safefree(arg); return -1; } @@ -768,7 +769,7 @@ static HANDLE_FUNC (handle_group) } static void warn_invalid_address(char *arg, unsigned long lineno) { - log_message (LOG_WARNING, "Invalid address %s on line %lu", arg, lineno); + CP_WARN ("Invalid address %s", arg); } static HANDLE_FUNC (handle_allow) @@ -813,8 +814,8 @@ static HANDLE_FUNC (handle_listen) if (conf->listen_addrs == NULL) { conf->listen_addrs = sblist_new(sizeof(char*), 16); if (conf->listen_addrs == NULL) { - log_message(LOG_WARNING, "Could not create a list " - "of listen addresses."); + CP_WARN ("Could not create a list " + "of listen addresses.", ""); safefree(arg); return -1; } @@ -840,9 +841,7 @@ static HANDLE_FUNC (handle_errorfile) char *page = get_string_arg (line, &match[4]); if(add_new_errorpage (conf, page, err) < 0) { - log_message (LOG_WARNING, - "add_new_errorpage() failed: '%s'", - page); + CP_WARN ("add_new_errorpage() failed: '%s'", page); safefree (page); } return 0; @@ -1025,6 +1024,7 @@ static HANDLE_FUNC (handle_upstream) int port, mi; char *domain = 0, *user = 0, *pass = 0, *tmp; enum proxy_type pt; + enum upstream_build_error ube; if (match[3].rm_so != -1) { tmp = get_string_arg (line, &match[3]); @@ -1034,9 +1034,9 @@ static HANDLE_FUNC (handle_upstream) domain = get_string_arg (line, &match[4]); if (!domain) return -1; - upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list); + ube = upstream_add (NULL, 0, domain, 0, 0, PT_NONE, &conf->upstream_list); safefree (domain); - return 0; + goto check_err; } } @@ -1066,13 +1066,16 @@ static HANDLE_FUNC (handle_upstream) if (match[mi].rm_so != -1) domain = get_string_arg (line, &match[mi]); - upstream_add (ip, port, domain, user, pass, pt, &conf->upstream_list); + ube = upstream_add (ip, port, domain, user, pass, pt, &conf->upstream_list); safefree (user); safefree (pass); safefree (domain); safefree (ip); +check_err:; + if(ube != UBE_SUCCESS) + CP_WARN("%s", upstream_build_error_string(ube)); return 0; } diff --git a/src/upstream.c b/src/upstream.c index 0f7f24fb..c8fee22d 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -43,20 +43,34 @@ proxy_type_name(proxy_type type) } } + +const char* upstream_build_error_string(enum upstream_build_error ube) { + static const char *emap[] = { + [UBE_SUCCESS] = "", + [UBE_OOM] = "Unable to allocate memory in upstream_build()", + [UBE_USERLEN] = "User / pass in upstream config too long", + [UBE_EDOMAIN] = "Nonsense upstream none rule: empty domain", + [UBE_INVHOST] = "Nonsense upstream rule: invalid host or port", + [UBE_INVPARAMS] = "Nonsense upstream rule: invalid parameters", + [UBE_NETMASK] = "Nonsense upstream rule: failed to parse netmask", + }; + return emap[ube]; +} + /** * Construct an upstream struct from input data. */ static struct upstream *upstream_build (const char *host, int port, const char *domain, const char *user, const char *pass, - proxy_type type) + proxy_type type, enum upstream_build_error *ube) { char *ptr; struct upstream *up; + *ube = UBE_SUCCESS; up = (struct upstream *) safemalloc (sizeof (struct upstream)); if (!up) { - log_message (LOG_ERR, - "Unable to allocate memory in upstream_build()"); + *ube = UBE_OOM; return NULL; } @@ -69,8 +83,7 @@ static struct upstream *upstream_build (const char *host, int port, const char * ssize_t ret; ret = basicauth_string(user, pass, b, sizeof b); if (ret == 0) { - log_message (LOG_ERR, - "User / pass in upstream config too long"); + *ube = UBE_USERLEN; return NULL; } up->ua.authstr = safestrdup (b); @@ -83,13 +96,11 @@ static struct upstream *upstream_build (const char *host, int port, const char * if (domain == NULL) { if (type == PT_NONE) { e_nonedomain:; - log_message (LOG_WARNING, - "Nonsense upstream none rule: empty domain"); + *ube = UBE_EDOMAIN; goto fail; } if (!host || !host[0] || port < 1) { - log_message (LOG_WARNING, - "Nonsense upstream rule: invalid host or port"); + *ube = UBE_INVHOST; goto fail; } @@ -103,8 +114,7 @@ static struct upstream *upstream_build (const char *host, int port, const char * if (!domain[0]) goto e_nonedomain; } else { if (!host || !host[0] || !domain[0]) { - log_message (LOG_WARNING, - "Nonsense upstream rule: invalid parameters"); + *ube = UBE_INVPARAMS; goto fail; } up->host = safestrdup (host); @@ -130,8 +140,7 @@ static struct upstream *upstream_build (const char *host, int port, const char * } up->ip = up->ip & up->mask; } else { - log_message (LOG_WARNING, - "Nonsense upstream rule: failed to parse netmask"); + *ube = UBE_NETMASK; goto fail; } } else { @@ -160,15 +169,17 @@ static struct upstream *upstream_build (const char *host, int port, const char * /* * Add an entry to the upstream list */ -void upstream_add (const char *host, int port, const char *domain, +enum upstream_build_error upstream_add ( + const char *host, int port, const char *domain, const char *user, const char *pass, proxy_type type, struct upstream **upstream_list) { struct upstream *up; + enum upstream_build_error ube; - up = upstream_build (host, port, domain, user, pass, type); + up = upstream_build (host, port, domain, user, pass, type, &ube); if (up == NULL) { - return; + return ube; } if (!up->domain && !up->ip) { /* always add default to end */ @@ -184,7 +195,7 @@ void upstream_add (const char *host, int port, const char *domain, if (!tmp->next) { up->next = NULL; tmp->next = up; - return; + return ube; } tmp = tmp->next; @@ -194,14 +205,14 @@ void upstream_add (const char *host, int port, const char *domain, up->next = *upstream_list; *upstream_list = up; - return; + return ube; upstream_cleanup: safefree (up->host); safefree (up->domain); safefree (up); - return; + return ube; } /* diff --git a/src/upstream.h b/src/upstream.h index c1127849..a6118073 100644 --- a/src/upstream.h +++ b/src/upstream.h @@ -27,6 +27,16 @@ #include "common.h" +enum upstream_build_error { + UBE_SUCCESS = 0, + UBE_OOM, + UBE_USERLEN, + UBE_EDOMAIN, + UBE_INVHOST, + UBE_INVPARAMS, + UBE_NETMASK, +}; + /* * Even if upstream support is not compiled into tinyproxy, this * structure still needs to be defined. @@ -54,11 +64,13 @@ struct upstream { #ifdef UPSTREAM_SUPPORT const char *proxy_type_name(proxy_type type); -extern void upstream_add (const char *host, int port, const char *domain, +extern enum upstream_build_error upstream_add ( + const char *host, int port, const char *domain, const char *user, const char *pass, proxy_type type, struct upstream **upstream_list); extern struct upstream *upstream_get (char *host, struct upstream *up); extern void free_upstream_list (struct upstream *up); +extern const char* upstream_build_error_string(enum upstream_build_error); #endif /* UPSTREAM_SUPPORT */ #endif /* _TINYPROXY_UPSTREAM_H_ */ From a8944b93e707a8f3ba9fde0ba530ee8f4427f950 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 30 Sep 2020 05:28:00 +0100 Subject: [PATCH 160/265] conf: use [0-9] instead of [[:digit:]] for shorter re strings --- src/conf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/conf.c b/src/conf.c index f2b8c311..6deaa100 100644 --- a/src/conf.c +++ b/src/conf.c @@ -49,21 +49,22 @@ * can (and likely should) be used when building the regex for the * given directive. */ +#define DIGIT "[0-9]" #define WS "[[:space:]]+" #define STR "\"([^\"]+)\"" #define BOOL "(yes|on|no|off)" -#define INT "(()[[:digit:]]+)" +#define INT "(()" DIGIT "+)" #define ALNUM "([-a-z0-9._]+)" #define USERNAME "([^:]*)" #define PASSWORD "([^@]*)" #define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" -#define IPMASK "(" IP "(/[[:digit:]]+)?)" +#define IPMASK "(" IP "(/" DIGIT "+)?)" #define IPV6 "(" \ "(([0-9a-f:]{2,39}))|" \ "(([0-9a-f:]{0,29}:" IP "))" \ ")" -#define IPV6MASK "(" IPV6 "(/[[:digit:]]+)?)" +#define IPV6MASK "(" IPV6 "(/" DIGIT "+)?)" #define BEGIN "^[[:space:]]*" #define END "[[:space:]]*$" From 3950a606a47fa62b46ecfa0ac45dbb7e1fa3bb8a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 30 Sep 2020 05:31:56 +0100 Subject: [PATCH 161/265] conf: only treat space and tab as whitespace other characters in the [[:space:]] set can't possibly be encountered, and this speeds up parsing by approximately 10%. --- src/conf.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/conf.c b/src/conf.c index 6deaa100..b7c4b759 100644 --- a/src/conf.c +++ b/src/conf.c @@ -50,7 +50,8 @@ * given directive. */ #define DIGIT "[0-9]" -#define WS "[[:space:]]+" +#define SPACE "[ \t]" +#define WS SPACE "+" #define STR "\"([^\"]+)\"" #define BOOL "(yes|on|no|off)" #define INT "(()" DIGIT "+)" @@ -65,8 +66,8 @@ ")" #define IPV6MASK "(" IPV6 "(/" DIGIT "+)?)" -#define BEGIN "^[[:space:]]*" -#define END "[[:space:]]*$" +#define BEGIN "^" SPACE "*" +#define END SPACE "*$" /* * Limit the maximum number of substring matches to a reasonably high From 8d27503cc365dccdd65fef7fcb63d36eda717443 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 30 Sep 2020 19:23:34 +0100 Subject: [PATCH 162/265] acl: fix regression using ipv6 with netmask introduced in 0ad8904b40d699405f60655606db42475c011b67 closes #327 --- src/acl.c | 63 ++++++++++++++++++++++--------------------------------- 1 file changed, 25 insertions(+), 38 deletions(-) diff --git a/src/acl.c b/src/acl.c index 261b7ddd..b989219b 100644 --- a/src/acl.c +++ b/src/acl.c @@ -138,15 +138,12 @@ int insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list) { struct acl_s acl; - int ret; - char *p, ip_dst[IPV6_LEN]; + char *mask, ip_dst[IPV6_LEN]; assert (location != NULL); - ret = init_access_list(access_list); - if (ret != 0) { + if (init_access_list(access_list) != 0) return -1; - } /* * Start populating the access control structure. @@ -154,38 +151,19 @@ insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list) memset (&acl, 0, sizeof (struct acl_s)); acl.access = access_type; + if ((mask = strrchr(location, '/'))) + *(mask++) = 0; + /* * Check for a valid IP address (the simplest case) first. */ if (full_inet_pton (location, ip_dst) > 0) { acl.type = ACL_NUMERIC; memcpy (acl.address.ip.network, ip_dst, IPV6_LEN); - memset (acl.address.ip.mask, 0xff, IPV6_LEN); - } else { - int i; - /* bogus ipv6 ? */ - if (strchr (location, ':')) - return -1; - - /* - * At this point we're either a hostname or an - * IP address with a slash. - */ - p = strchr (location, '/'); - if (p != NULL) { + if(!mask) memset (acl.address.ip.mask, 0xff, IPV6_LEN); + else { char dst[sizeof(struct in6_addr)]; - int v6; - - /* - * We have a slash, so it's intended to be an - * IP address with mask - */ - *p = '\0'; - if (full_inet_pton (location, ip_dst) <= 0) - return -1; - - acl.type = ACL_NUMERIC; - + int v6, i; /* Check if the IP address before the netmask is * an IPv6 address */ if (inet_pton(AF_INET6, location, dst) > 0) @@ -194,24 +172,33 @@ insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list) v6 = 0; if (fill_netmask_array - (p + 1, v6, &(acl.address.ip.mask[0]), IPV6_LEN) + (mask, v6, &(acl.address.ip.mask[0]), IPV6_LEN) < 0) - return -1; + goto err; for (i = 0; i < IPV6_LEN; i++) acl.address.ip.network[i] = ip_dst[i] & acl.address.ip.mask[i]; - } else { - /* In all likelihood a string */ - acl.type = ACL_STRING; - acl.address.string = safestrdup (location); - if (!acl.address.string) - return -1; } + } else { + /* either bogus IP or hostname */ + /* bogus ipv6 ? */ + if (mask || strchr (location, ':')) + goto err; + + /* In all likelihood a string */ + acl.type = ACL_STRING; + acl.address.string = safestrdup (location); + if (!acl.address.string) + goto err; } if(!sblist_add(*access_list, &acl)) return -1; return 0; +err:; + /* restore mask for proper error message */ + if(mask) *(--mask) = '/'; + return -1; } /* From e20aa221ff143902758cafca668034e29652c9da Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 1 Oct 2020 15:25:35 +0100 Subject: [PATCH 163/265] conf: move inclusion of common.h back to the start otherwise the feature-test-macros won't kick in as they should. should fix #329 --- src/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index b7c4b759..e0d6634f 100644 --- a/src/conf.c +++ b/src/conf.c @@ -23,8 +23,8 @@ * add new directives to. Who knows if I'm right though. */ -#include #include "common.h" +#include #include "conf.h" #include "acl.h" From cc0a7eb9a2846116630c1c2aa445597f2b9b2369 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 19 Oct 2020 20:05:14 +0100 Subject: [PATCH 164/265] html-error: move common.h inclusion back to top this seems to cause an implicit declaration of snprintf() thanks to feature test macro hell. --- src/html-error.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/html-error.c b/src/html-error.c index 78c3ef01..daf44d39 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -20,10 +20,9 @@ * HTML error pages with variable substitution. */ -#include +#include "common.h" #include "main.h" -#include "common.h" #include "buffer.h" #include "conns.h" #include "heap.h" @@ -32,6 +31,8 @@ #include "utils.h" #include "conf.h" +#include + /* * Add an error number -> filename mapping to the errorpages list. */ From f7c616d2b95b807b0a4d1e1cb4b10421e135c785 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 19 Oct 2020 20:07:46 +0100 Subject: [PATCH 165/265] log.c: fix format string args --- src/log.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/log.c b/src/log.c index 873abb2c..516fcd87 100644 --- a/src/log.c +++ b/src/log.c @@ -179,9 +179,9 @@ void log_message (int level, const char *fmt, ...) strftime (time_string, TIME_LENGTH, "%b %d %H:%M:%S", localtime (&nowtime.tv_sec)); - snprintf (str, STRING_LENGTH, "%-9s %s.%03u [%ld]: ", + snprintf (str, STRING_LENGTH, "%-9s %s.%03lu [%ld]: ", syslog_level[level], time_string, - nowtime.tv_nsec/1000000u, + (unsigned long) nowtime.tv_nsec/1000000ul, (long int) getpid ()); /* From 2b49ef0e0f653eec6a128050c12ae8f7d630b1af Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 26 Aug 2020 12:31:33 +0200 Subject: [PATCH 166/265] sock: add missing format specifier to log_message() --- src/sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sock.c b/src/sock.c index 983c4ea8..28a7f881 100644 --- a/src/sock.c +++ b/src/sock.c @@ -68,7 +68,7 @@ bind_socket (int sockfd, const char *addr, int family) n = getaddrinfo (addr, NULL, &hints, &res); if (n != 0) { log_message (LOG_INFO, - "bind_socket: getaddrinfo failed for %s: ", addr, get_gai_error (n)); + "bind_socket: getaddrinfo failed for %s: %s", addr, get_gai_error (n)); return -1; } From 3bb14e04405ef918487489abb644949d03d7baa8 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Wed, 26 Aug 2020 12:32:27 +0200 Subject: [PATCH 167/265] Allow multiple Bind directives. Try all the addresses specified with Bind in order. This is necessary e.g. for maintaining IPv4+6 connectivity while still being restricted to one interface. --- docs/man5/tinyproxy.conf.txt.in | 2 ++ src/conf.c | 25 ++++++++++++++++++++----- src/conf.h | 2 +- src/sock.c | 27 ++++++++++++++++++++++++--- 4 files changed, 47 insertions(+), 9 deletions(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 8a18d55a..0629a9af 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -58,6 +58,8 @@ only on one specific address. This allows you to specify which address Tinyproxy will bind to for outgoing connections to web servers or upstream proxies. +This parameter may be specified multiple times, then Tinyproxy +will try all the specified addresses in order. =item B diff --git a/src/conf.c b/src/conf.c index e0d6634f..7e3c031e 100644 --- a/src/conf.c +++ b/src/conf.c @@ -291,6 +291,7 @@ void free_config (struct config_s *conf) safefree (conf->group); stringlist_free(conf->basicauth_list); stringlist_free(conf->listen_addrs); + stringlist_free(conf->bind_addrs); #ifdef FILTER_ENABLE safefree (conf->filter); #endif /* FILTER_ENABLE */ @@ -302,7 +303,6 @@ void free_config (struct config_s *conf) free_upstream_list (conf->upstream_list); #endif /* UPSTREAM_SUPPORT */ safefree (conf->pidpath); - safefree (conf->bind_address); safefree (conf->via_proxy_name); if (conf->errorpages) { it = 0; @@ -796,12 +796,27 @@ static HANDLE_FUNC (handle_deny) static HANDLE_FUNC (handle_bind) { - int r = set_string_arg (&conf->bind_address, line, &match[2]); + char *arg = get_string_arg (line, &match[2]); + + if (arg == NULL) { + return -1; + } + + if (conf->bind_addrs == NULL) { + conf->bind_addrs = sblist_new(sizeof(char*), 16); + if (conf->bind_addrs == NULL) { + CP_WARN ("Could not create a list " + "of bind addresses.", ""); + safefree(arg); + return -1; + } + } + + sblist_add (conf->bind_addrs, &arg); - if (r) - return r; log_message (LOG_INFO, - "Outgoing connections bound to IP %s", conf->bind_address); + "Added bind address [%s] for outgoing connections.", arg); + return 0; } diff --git a/src/conf.h b/src/conf.h index 9453d660..99140492 100644 --- a/src/conf.h +++ b/src/conf.h @@ -68,7 +68,7 @@ struct config_s { #endif /* UPSTREAM_SUPPORT */ char *pidpath; unsigned int idletimeout; - char *bind_address; + sblist *bind_addrs; unsigned int bindsame; /* diff --git a/src/sock.c b/src/sock.c index 28a7f881..70169a68 100644 --- a/src/sock.c +++ b/src/sock.c @@ -34,6 +34,7 @@ #include "text.h" #include "conf.h" #include "loop.h" +#include "sblist.h" /* * Return a human readable error for getaddrinfo() and getnameinfo(). @@ -87,6 +88,26 @@ bind_socket (int sockfd, const char *addr, int family) return sockfd; } +/** + * Try binding the given socket to supplied addresses, stopping when one succeeds. + */ +static int +bind_socket_list (int sockfd, sblist *addresses, int family) +{ + size_t nb_addresses = sblist_getsize(addresses); + size_t i; + + for (i = 0; i < nb_addresses; i++) { + const char *address = *(const char **)sblist_get(addresses, i); + if (bind_socket(sockfd, address, family) >= 0) { + log_message(LOG_INFO, "Bound to %s", address); + return 0; + } + } + + return -1; +} + /* * Open a connection to a remote host. It's been re-written to use * the getaddrinfo() library function, which allows for a protocol @@ -134,9 +155,9 @@ int opensock (const char *host, int port, const char *bind_to) close (sockfd); continue; /* can't bind, so try again */ } - } else if (config->bind_address) { - if (bind_socket (sockfd, config->bind_address, - res->ai_family) < 0) { + } else if (config->bind_addrs) { + if (bind_socket_list (sockfd, config->bind_addrs, + res->ai_family) < 0) { close (sockfd); continue; /* can't bind, so try again */ } From 732bdd0f5678cabae32849e0dac1c4f8478ca33c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 19 Oct 2020 20:21:26 +0100 Subject: [PATCH 168/265] replace usage of non-threadsafe gmtime() with gmtime_r() the latter is a standard POSIX function too. --- src/html-error.c | 3 ++- src/http-message.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/html-error.c b/src/html-error.c index daf44d39..071d4154 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -244,6 +244,7 @@ int add_standard_vars (struct conn_s *connptr) char errnobuf[16]; char timebuf[30]; time_t global_time; + struct tm tm_buf; snprintf (errnobuf, sizeof errnobuf, "%d", connptr->error_number); ADD_VAR_RET ("errno", errnobuf); @@ -259,7 +260,7 @@ int add_standard_vars (struct conn_s *connptr) global_time = time (NULL); strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT", - gmtime (&global_time)); + gmtime_r (&global_time, &tm_buf)); add_error_variable (connptr, "date", timebuf); add_error_variable (connptr, "website", diff --git a/src/http-message.c b/src/http-message.c index 8b94f191..96a6994f 100644 --- a/src/http-message.c +++ b/src/http-message.c @@ -232,6 +232,7 @@ int http_message_send (http_message_t msg, int fd) char timebuf[30]; time_t global_time; unsigned int i; + struct tm tm_buf; assert (is_http_message_valid (msg)); @@ -254,7 +255,7 @@ int http_message_send (http_message_t msg, int fd) /* Output the date */ global_time = time (NULL); strftime (timebuf, sizeof (timebuf), "%a, %d %b %Y %H:%M:%S GMT", - gmtime (&global_time)); + gmtime_r (&global_time, &tm_buf)); write_message (fd, "Date: %s\r\n", timebuf); /* Output the content-length */ From 8ebbd50cb2d5c20163272fe081fc58329fd896bd Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 19 Oct 2020 20:26:12 +0100 Subject: [PATCH 169/265] log: replace non-mt-safe localtime() with localtime_r() --- src/log.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/log.c b/src/log.c index 516fcd87..14fc3fe9 100644 --- a/src/log.c +++ b/src/log.c @@ -109,6 +109,7 @@ void log_message (int level, const char *fmt, ...) { va_list args; struct timespec nowtime; + struct tm tm_buf; char time_string[TIME_LENGTH]; char str[STRING_LENGTH]; @@ -177,7 +178,7 @@ void log_message (int level, const char *fmt, ...) clock_gettime(CLOCK_REALTIME, &nowtime); /* Format is month day hour:minute:second (24 time) */ strftime (time_string, TIME_LENGTH, "%b %d %H:%M:%S", - localtime (&nowtime.tv_sec)); + localtime_r (&nowtime.tv_sec, &tm_buf)); snprintf (str, STRING_LENGTH, "%-9s %s.%03lu [%ld]: ", syslog_level[level], time_string, From db5c0e99b433ae75c6112e10a384082d35fb5918 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 19 Oct 2020 20:30:10 +0100 Subject: [PATCH 170/265] reqs: fix UB passing ssize_t to format string expecting int --- src/reqs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index f6a92331..4d495833 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1454,8 +1454,8 @@ 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 { From adad565c03f3ffde6520646bc5d119ce1eadbadb Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 19 Oct 2020 20:33:04 +0100 Subject: [PATCH 171/265] http-message: fix UB passing long to format string expecting int --- src/http-message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/http-message.c b/src/http-message.c index 96a6994f..4ff37ae9 100644 --- a/src/http-message.c +++ b/src/http-message.c @@ -259,7 +259,7 @@ int http_message_send (http_message_t msg, int fd) write_message (fd, "Date: %s\r\n", timebuf); /* Output the content-length */ - write_message (fd, "Content-length: %u\r\n", msg->body.length); + write_message (fd, "Content-length: %lu\r\n", (unsigned long) msg->body.length); /* Write the separator between the headers and body */ safe_write (fd, "\r\n", 2); From 4147e917d610f793ec14f7bf4ffc82c3b4c92dab Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 13 Feb 2021 09:53:55 +0000 Subject: [PATCH 172/265] configure: check whether gperf is compatible closes #337 --- configure.ac | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure.ac b/configure.ac index 00f7f0e6..38493835 100644 --- a/configure.ac +++ b/configure.ac @@ -210,7 +210,14 @@ AH_TEMPLATE([HAVE_GPERF], [Whether you have gperf installed for faster config parsing.]) if test "x$GPERF" != "x" -a "x$GPERF" != "xno" ; then + AS_ECHO_N(["checking whether gperf is recent enough... "]) + if "$GPERF" < src/conf-tokens.gperf >/dev/null 2>&1 ; then + AS_ECHO("yes") AC_DEFINE(HAVE_GPERF) + else + AM_CONDITIONAL(HAVE_GPERF, false) + AS_ECHO("no") + fi fi AC_CONFIG_FILES([ From 38934921c4d6801ebdc13d32b4238c4a2ffa9c98 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 14 Mar 2021 01:06:35 +0000 Subject: [PATCH 173/265] htab_delete(): fix failure to set tombstone we can't just set an item's key to zero and be done with a deletion, because this will break the item search chain. a deleted item requires a special marker, also known as tombstone. when searching for an item, all slots with a tombstone need to treated as if they were in use, but when inserting an item such a slot needs to be filled with the new item. a common procedure is to rehash the table when the number of deleted items crosses a certain threshold, though for simplicity we leave this task to the resize() function which does the same thing anyway when the hashtable grows. this allows to fix the issue quite elegantly and with almost no additional overhead, so we don't penalize applications that do very few deletions. --- src/hsearch.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/hsearch.c b/src/hsearch.c index 0cc4aca5..d8d395ae 100644 --- a/src/hsearch.c +++ b/src/hsearch.c @@ -107,14 +107,14 @@ static int resize(struct htab *htab, size_t nel) return 1; } -static struct elem *lookup(struct htab *htab, const char *key, size_t hash) +static struct elem *lookup(struct htab *htab, const char *key, size_t hash, size_t dead) { size_t i, j; struct elem *e; for (i=hash,j=1; ; i+=j++) { e = htab->elems + (i & htab->mask); - if (!e->item.key || + if ((!e->item.key && (!e->hash || e->hash == dead)) || (e->hash==hash && STRCMP(e->item.key, key)==0)) break; } @@ -138,36 +138,38 @@ void htab_destroy(struct htab *htab) free(htab); } -static htab_entry *htab_find_item(struct htab *htab, const char* key) +static struct elem *htab_find_elem(struct htab *htab, const char* key) { size_t hash = keyhash(key, htab->seed); - struct elem *e = lookup(htab, key, hash); + struct elem *e = lookup(htab, key, hash, 0); if (e->item.key) { - return &e->item; + return e; } return 0; } htab_value* htab_find(struct htab *htab, const char* key) { - htab_entry *i = htab_find_item(htab, key); - if(i) return &i->data; - return 0; + struct elem *e = htab_find_elem(htab, key); + if(!e) return 0; + return &e->item.data; } int htab_delete(struct htab *htab, const char* key) { - htab_entry *i = htab_find_item(htab, key); - if(!i) return 0; - i->key = 0; + struct elem *e = htab_find_elem(htab, key); + if(!e) return 0; + e->item.key = 0; + e->hash = 0xdeadc0de; + --htab->used; return 1; } int htab_insert(struct htab *htab, char* key, htab_value value) { size_t hash = keyhash(key, htab->seed); - struct elem *e = lookup(htab, key, hash); + struct elem *e = lookup(htab, key, hash, 0xdeadc0de); if(e->item.key) { /* it's not allowed to overwrite existing data */ return 0; From c4231e58bf2613b6ab8b34f234c117c5d1488931 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 14 Mar 2021 16:06:10 +0000 Subject: [PATCH 174/265] orderedmap: fix memory leak when using orderedmap_remove() closes #351 --- src/hsearch.c | 8 ++++++++ src/hsearch.h | 2 ++ src/orderedmap.c | 9 +++++++-- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/hsearch.c b/src/hsearch.c index d8d395ae..70d757a6 100644 --- a/src/hsearch.c +++ b/src/hsearch.c @@ -156,6 +156,14 @@ htab_value* htab_find(struct htab *htab, const char* key) return &e->item.data; } +htab_value* htab_find2(struct htab *htab, const char* key, char **saved_key) +{ + struct elem *e = htab_find_elem(htab, key); + if(!e) return 0; + *saved_key = e->item.key; + return &e->item.data; +} + int htab_delete(struct htab *htab, const char* key) { struct elem *e = htab_find_elem(htab, key); diff --git a/src/hsearch.h b/src/hsearch.h index ec81cc31..7e9d7709 100644 --- a/src/hsearch.h +++ b/src/hsearch.h @@ -14,6 +14,8 @@ typedef union htab_value { struct htab * htab_create(size_t); void htab_destroy(struct htab *); htab_value* htab_find(struct htab *, const char* key); +/* same as htab_find, but can retrieve the saved key (for freeing) */ +htab_value* htab_find2(struct htab *htab, const char* key, char **saved_key); int htab_insert(struct htab *, char*, htab_value); int htab_delete(struct htab *htab, const char* key); size_t htab_next(struct htab *, size_t iterator, char** key, htab_value **v); diff --git a/src/orderedmap.c b/src/orderedmap.c index 4902be03..1818e276 100644 --- a/src/orderedmap.c +++ b/src/orderedmap.c @@ -81,14 +81,19 @@ char* orderedmap_find(struct orderedmap *o, const char *key) { int orderedmap_remove(struct orderedmap *o, const char *key) { size_t i; char *lk; - htab_value *lv, *v = htab_find(o->map, key); + char *sk; + char **sv; + htab_value *lv, *v = htab_find2(o->map, key, &sk); if(!v) return 0; - htab_delete(o->map, key); + 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; } From 48860bbe26867cd0ad880b6358cfcba8d142c267 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 28 Mar 2021 20:22:32 +0100 Subject: [PATCH 175/265] refactor html-error so send_http_headers() can take extra arg we already required an extra argument inside the headers sent for 401 and 407 error responses, move those to sent_http_error_message() and refactor send_http_headers() to always take the extra argument. in calling sites where the extra arg isn't needed, use "". --- src/html-error.c | 33 ++++++++++++++++++--------------- src/html-error.h | 2 +- src/stats.c | 2 +- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/html-error.c b/src/html-error.c index 071d4154..b6dd29d1 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -133,7 +133,9 @@ send_html_file (FILE *infile, struct conn_s *connptr) return 1; } -int send_http_headers (struct conn_s *connptr, int code, const char *message) +int send_http_headers ( + struct conn_s *connptr, int code, + const char *message, const char *extra) { const char headers[] = "HTTP/1.0 %d %s\r\n" @@ -142,21 +144,9 @@ int send_http_headers (struct conn_s *connptr, int code, const char *message) "%s" "Connection: close\r\n" "\r\n"; - const char p_auth_str[] = - "Proxy-Authenticate: Basic realm=\"" - PACKAGE_NAME "\"\r\n"; - - const char w_auth_str[] = - "WWW-Authenticate: Basic realm=\"" - PACKAGE_NAME "\"\r\n"; - - /* according to rfc7235, the 407 error must be accompanied by - a Proxy-Authenticate header field. */ - const char *add = code == 407 ? p_auth_str : (code == 401 ? w_auth_str : ""); - return (write_message (connptr->client_fd, headers, code, message, PACKAGE, VERSION, - add)); + extra)); } /* @@ -180,8 +170,21 @@ int send_http_error_message (struct conn_s *connptr) "

Generated by %s version %s.

\n" "\n" "\n"; + const char p_auth_str[] = + "Proxy-Authenticate: Basic realm=\"" + PACKAGE_NAME "\"\r\n"; + + const char w_auth_str[] = + "WWW-Authenticate: Basic realm=\"" + PACKAGE_NAME "\"\r\n"; + + /* according to rfc7235, the 407 error must be accompanied by + a Proxy-Authenticate header field. */ + const char *add = connptr->error_number == 407 ? p_auth_str : + (connptr->error_number == 401 ? w_auth_str : ""); + send_http_headers (connptr, connptr->error_number, - connptr->error_string); + connptr->error_string, add); error_file = get_html_file (connptr->error_number); if (!(infile = fopen (error_file, "r"))) { diff --git a/src/html-error.h b/src/html-error.h index c133cef8..bc9b7ce9 100644 --- a/src/html-error.h +++ b/src/html-error.h @@ -33,7 +33,7 @@ extern int add_error_variable (struct conn_s *connptr, const char *key, const char *val); extern int send_html_file (FILE * infile, struct conn_s *connptr); extern int send_http_headers (struct conn_s *connptr, int code, - const char *message); + const char *message, const char *extra); extern int add_standard_vars (struct conn_s *connptr); #endif /* !TINYPROXY_HTML_ERROR_H */ diff --git a/src/stats.c b/src/stats.c index dfe054c2..1228aa33 100644 --- a/src/stats.c +++ b/src/stats.c @@ -122,7 +122,7 @@ showstats (struct conn_s *connptr) add_error_variable (connptr, "deniedconns", denied); add_error_variable (connptr, "refusedconns", refused); add_standard_vars (connptr); - send_http_headers (connptr, 200, "Statistic requested"); + send_http_headers (connptr, 200, "Statistic requested", ""); send_html_file (statfile, connptr); fclose (statfile); pthread_mutex_unlock(&stats_file_lock); From 64badd6b373b243e6f431c8adcff92e58925a8b9 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 16 Mar 2021 21:33:30 +0000 Subject: [PATCH 176/265] htab: prevent filling up of table with tombstones as pointed out by @craigbarnes [0], using the latest fix for the tombstone issue, it's possible to provoke a situation that causes an endless loop when all free slots in the table are filled up with tombstones and htab_find() is called. therefore we need to account for those as well when deciding if there's a need to call resize() so there's never more than 75% of the table used by either dead or live items. the resize() serves as a rehash which gets rid of all deleted entries, and it might cause the table size to shrink if htab_insert() is called after a lot of items have been removed. [0]: https://github.com/rofl0r/htab/issues/1#issuecomment-800094442 testcase: #include #include #include #include #include "hsearch.h" #define HTAB_OOM_TEST #include "hsearch.c" static char *xstrdup(const char *str) { char *dup = strdup(str); assert(dup); return dup; } void utoa(unsigned number, char* buffer) { int lentest, len = 0, i, start = 0; lentest = number; do { len++; lentest /= 10; } while(lentest); buffer[start+len] = 0; do { i = number % 10; buffer[start+len - 1] = '0' + i; number -= i; len -= 1; number /= 10; } while (number); } #define TESTSIZE 8 #define KEEP 1 static char* notorious[TESTSIZE]; static void prep() { srand(0); char buf[16]; size_t filled = 0; while(filled < TESTSIZE) { utoa(rand(), buf); size_t idx = keyhash(buf) & (TESTSIZE-1); if(!notorious[idx]) { notorious[idx] = xstrdup(buf); ++filled; } } } int main(void) { struct htab *h = htab_create(TESTSIZE); size_t i; assert(h); prep(); for(i=0; i= KEEP) htab_delete(h, key); } htab_find(h, "looooop"); return 0; } --- src/hsearch.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/hsearch.c b/src/hsearch.c index 70d757a6..be0434c2 100644 --- a/src/hsearch.c +++ b/src/hsearch.c @@ -49,6 +49,7 @@ struct htab { size_t mask; size_t used; size_t seed; + size_t dead; }; #define MINSIZE 8 @@ -171,27 +172,34 @@ int htab_delete(struct htab *htab, const char* key) e->item.key = 0; e->hash = 0xdeadc0de; --htab->used; + ++htab->dead; return 1; } int htab_insert(struct htab *htab, char* key, htab_value value) { - size_t hash = keyhash(key, htab->seed); + size_t hash = keyhash(key, htab->seed), oh; struct elem *e = lookup(htab, key, hash, 0xdeadc0de); if(e->item.key) { /* it's not allowed to overwrite existing data */ return 0; } + oh = e->hash; /* save old hash in case it's tombstone marker */ e->item.key = key; e->item.data = value; e->hash = hash; - if (++htab->used > htab->mask - htab->mask/4) { + if (++htab->used + htab->dead > htab->mask - htab->mask/4) { if (!resize(htab, 2*htab->used)) { htab->used--; e->item.key = 0; + e->hash = oh; return 0; } + htab->dead = 0; + } else if (oh == 0xdeadc0de) { + /* re-used tomb */ + --htab->dead; } return 1; } From 11a4f6c5cfc078d037930b2618b9bc8bb0550493 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 28 Mar 2021 20:36:55 +0100 Subject: [PATCH 177/265] reverse: ensure paths always end with a slash --- src/reverse-proxy.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/reverse-proxy.c b/src/reverse-proxy.c index af58d56b..40081204 100644 --- a/src/reverse-proxy.c +++ b/src/reverse-proxy.c @@ -34,6 +34,7 @@ void reversepath_add (const char *path, const char *url, struct reversepath **reversepath_list) { struct reversepath *reverse; + size_t l; if (url == NULL) { log_message (LOG_WARNING, @@ -65,8 +66,17 @@ void reversepath_add (const char *path, const char *url, if (!path) reverse->path = safestrdup ("/"); - else - reverse->path = safestrdup (path); + else { + l = strlen (path); + if (l && path[l-1] == '/') + reverse->path = safestrdup (path); + else { + reverse->path = safemalloc (l + 2); + memcpy (reverse->path, path, l); + reverse->path[l] = '/'; + reverse->path[l+1] = 0; + } + } reverse->url = safestrdup (url); From bc87de3482b8bbbc8e59651638eb8545831ccd64 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 16 Apr 2021 13:46:58 +0100 Subject: [PATCH 178/265] Release 1.11.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 01945ef8..1cac385c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.0-rc1 +1.11.0 From 2529597ea0201c147b4bb5fb713040b117368b50 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 28 Mar 2021 20:40:17 +0100 Subject: [PATCH 179/265] reverse: redirect if path without trailing slash is detected if for example: ReversePath = "/foo/" and user requests "http://tinyproxy/foo" the common behaviour for HTTP servers is to send a http 301 redirect to the correct url. we now do the same. --- src/reqs.c | 9 ++++++++- src/reverse-proxy.c | 29 +++++++++++++++++++++-------- src/reverse-proxy.h | 3 ++- 3 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 4d495833..61a5eb0a 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -383,10 +383,17 @@ static struct request_s *process_request (struct conn_s *connptr, * we'll be closing anyway. */ char *reverse_url; + int reverse_status; - reverse_url = reverse_rewrite_url (connptr, hashofheaders, url); + reverse_url = reverse_rewrite_url (connptr, hashofheaders, url, &reverse_status); if (reverse_url != NULL) { + if (reverse_status == 301) { + char buf[PATH_MAX]; + snprintf (buf, sizeof buf, "Location: %s\r\n", reverse_url); + send_http_headers (connptr, 301, "Moved Permanently", buf); + goto fail; + } safefree (url); url = reverse_url; } else if (config->reverseonly) { diff --git a/src/reverse-proxy.c b/src/reverse-proxy.c index 40081204..68f04a69 100644 --- a/src/reverse-proxy.c +++ b/src/reverse-proxy.c @@ -93,10 +93,16 @@ void reversepath_add (const char *path, const char *url, */ struct reversepath *reversepath_get (char *url, struct reversepath *reverse) { + size_t l, lu, lp; while (reverse) { - if (strstr (url, reverse->path) == url) + lu = strlen (url); + lp = strlen (reverse->path); + if (( + (l = lu) == lp-1 || + (l = lp) <= lu + ) && + !memcmp(url, reverse->path, l)) return reverse; - reverse = reverse->next; } @@ -122,23 +128,30 @@ void free_reversepath_list (struct reversepath *reverse) * Rewrite the URL for reverse proxying. */ char *reverse_rewrite_url (struct conn_s *connptr, orderedmap hashofheaders, - char *url) + char *url, int *status) { char *rewrite_url = NULL; char *cookie = NULL; char *cookieval; struct reversepath *reverse = NULL; + *status = 0; + /* Reverse requests always start with a slash */ if (*url == '/') { /* First try locating the reverse mapping by request url */ reverse = reversepath_get (url, config->reversepath_list); if (reverse) { - rewrite_url = (char *) - safemalloc (strlen (url) + strlen (reverse->url) + - 1); - strcpy (rewrite_url, reverse->url); - strcat (rewrite_url, url + strlen (reverse->path)); + size_t lu = strlen (url); + size_t lrp = strlen (reverse->path); + if (lrp > lu) { + rewrite_url = safestrdup (reverse->path); + *status = 301; + } else { + rewrite_url = safemalloc ( + strlen (reverse->url) + lu + 1); + sprintf (rewrite_url, "%s%s", reverse->url, url + lrp); + } } else if (config->reversemagic && (cookie = orderedmap_find (hashofheaders, "cookie"))) { diff --git a/src/reverse-proxy.h b/src/reverse-proxy.h index a2d66198..a9a5bdd5 100644 --- a/src/reverse-proxy.h +++ b/src/reverse-proxy.h @@ -38,6 +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); + orderedmap hashofheaders, char *url, + int *status); #endif From 979c737f9b811c5441ae0573a90b72dc1e44e142 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 13 Feb 2021 15:11:42 +0000 Subject: [PATCH 180/265] make upstream site-spec ipv6 compatible, refactor acl code the acl.c code parsing a site-spec has been factored out into a new TU: hostspec. it was superior to the parsing code in upstream.c in that it properly deals with both ipv4 and ipv6. both upstream and acl now use the new code for parsing, and upstream also for checking for a match. acl.c still uses the old matching code as it has a lot of special case code for specifications containing a hostname, and in case such a spec is encountered, tries to do reverse name lookup to see if a numeric ip matches that spec. removing that code could break existing usecases, however since that was never implemented for upstream nobody will miss it there. --- src/Makefile.am | 1 + src/acl.c | 147 ++++++------------------------------------ src/hostspec.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ src/hostspec.h | 26 ++++++++ src/upstream.c | 80 ++++++----------------- src/upstream.h | 6 +- 6 files changed, 237 insertions(+), 189 deletions(-) create mode 100644 src/hostspec.c create mode 100644 src/hostspec.h diff --git a/src/Makefile.am b/src/Makefile.am index 6d806e03..d132a75f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,7 @@ AM_CPPFLAGS = \ -DLOCALSTATEDIR=\"${localstatedir}\" tinyproxy_SOURCES = \ + hostspec.c hostspec.h \ acl.c acl.h \ anonymous.c anonymous.h \ buffer.c buffer.h \ diff --git a/src/acl.c b/src/acl.c index b989219b..63a3d348 100644 --- a/src/acl.c +++ b/src/acl.c @@ -29,17 +29,10 @@ #include "network.h" #include "sock.h" #include "sblist.h" +#include "hostspec.h" #include -/* Define how long an IPv6 address is in bytes (128 bits, 16 bytes) */ -#define IPV6_LEN 16 - -enum acl_type { - ACL_STRING, - ACL_NUMERIC -}; - /* * Hold the information about a particular access control. We store * whether it's an ALLOW or DENY entry, and also whether it's a string @@ -47,66 +40,9 @@ enum acl_type { */ struct acl_s { acl_access_t access; - enum acl_type type; - union { - char *string; - struct { - unsigned char network[IPV6_LEN]; - unsigned char mask[IPV6_LEN]; - } ip; - } address; + struct hostspec h; }; -/* - * Fills in the netmask array given a numeric value. - * - * Returns: - * 0 on success - * -1 on failure (invalid mask value) - * - */ -static int -fill_netmask_array (char *bitmask_string, int v6, - unsigned char array[], size_t len) -{ - unsigned int i; - unsigned long int mask; - char *endptr; - - errno = 0; /* to distinguish success/failure after call */ - mask = strtoul (bitmask_string, &endptr, 10); - - /* check for various conversion errors */ - if ((errno == ERANGE && mask == ULONG_MAX) - || (errno != 0 && mask == 0) || (endptr == bitmask_string)) - return -1; - - if (v6 == 0) { - /* The mask comparison is done as an IPv6 address, so - * convert to a longer mask in the case of IPv4 - * addresses. */ - mask += 12 * 8; - } - - /* check valid range for a bit mask */ - if (mask > (8 * len)) - return -1; - - /* we have a valid range to fill in the array */ - for (i = 0; i != len; ++i) { - if (mask >= 8) { - array[i] = 0xff; - mask -= 8; - } else if (mask > 0) { - array[i] = (unsigned char) (0xff << (8 - mask)); - mask = 0; - } else { - array[i] = 0; - } - } - - return 0; -} /** * If the access list has not been set up, create it. @@ -138,7 +74,6 @@ int insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list) { struct acl_s acl; - char *mask, ip_dst[IPV6_LEN]; assert (location != NULL); @@ -150,55 +85,11 @@ insert_acl (char *location, acl_access_t access_type, acl_list_t *access_list) */ memset (&acl, 0, sizeof (struct acl_s)); acl.access = access_type; - - if ((mask = strrchr(location, '/'))) - *(mask++) = 0; - - /* - * Check for a valid IP address (the simplest case) first. - */ - if (full_inet_pton (location, ip_dst) > 0) { - acl.type = ACL_NUMERIC; - memcpy (acl.address.ip.network, ip_dst, IPV6_LEN); - if(!mask) memset (acl.address.ip.mask, 0xff, IPV6_LEN); - else { - char dst[sizeof(struct in6_addr)]; - int v6, i; - /* Check if the IP address before the netmask is - * an IPv6 address */ - if (inet_pton(AF_INET6, location, dst) > 0) - v6 = 1; - else - v6 = 0; - - if (fill_netmask_array - (mask, v6, &(acl.address.ip.mask[0]), IPV6_LEN) - < 0) - goto err; - - for (i = 0; i < IPV6_LEN; i++) - acl.address.ip.network[i] = ip_dst[i] & - acl.address.ip.mask[i]; - } - } else { - /* either bogus IP or hostname */ - /* bogus ipv6 ? */ - if (mask || strchr (location, ':')) - goto err; - - /* In all likelihood a string */ - acl.type = ACL_STRING; - acl.address.string = safestrdup (location); - if (!acl.address.string) - goto err; - } + if(hostspec_parse(location, &acl.h) || acl.h.type == HST_NONE) + return -1; if(!sblist_add(*access_list, &acl)) return -1; return 0; -err:; - /* restore mask for proper error message */ - if(mask) *(--mask) = '/'; - return -1; } /* @@ -219,7 +110,7 @@ acl_string_processing (struct acl_s *acl, const char *ip_address, size_t test_length, match_length; char ipbuf[512]; - assert (acl && acl->type == ACL_STRING); + assert (acl && acl->h.type == HST_STRING); assert (ip_address && strlen (ip_address) > 0); /* @@ -227,11 +118,11 @@ acl_string_processing (struct acl_s *acl, const char *ip_address, * do a string based test only; otherwise, we can do a reverse * lookup test as well. */ - if (acl->address.string[0] != '.') { + if (acl->h.address.string[0] != '.') { memset (&hints, 0, sizeof (struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; - if (getaddrinfo (acl->address.string, NULL, &hints, &res) != 0) + if (getaddrinfo (acl->h.address.string, NULL, &hints, &res) != 0) goto STRING_TEST; ressave = res; @@ -265,7 +156,7 @@ acl_string_processing (struct acl_s *acl, const char *ip_address, } test_length = strlen (string_addr); - match_length = strlen (acl->address.string); + match_length = strlen (acl->h.address.string); /* * If the string length is shorter than AC string, return a -1 so @@ -276,7 +167,7 @@ acl_string_processing (struct acl_s *acl, const char *ip_address, if (strcasecmp (string_addr + (test_length - match_length), - acl->address.string) == 0) { + acl->h.address.string) == 0) { if (acl->access == ACL_DENY) return 0; else @@ -300,11 +191,11 @@ static int check_numeric_acl (const struct acl_s *acl, uint8_t addr[IPV6_LEN]) uint8_t x, y; int i; - assert (acl && acl->type == ACL_NUMERIC); + assert (acl && acl->h.type == HST_NUMERIC); for (i = 0; i != IPV6_LEN; ++i) { - x = addr[i] & acl->address.ip.mask[i]; - y = acl->address.ip.network[i]; + x = addr[i] & acl->h.address.ip.mask[i]; + y = acl->h.address.ip.network[i]; /* If x and y don't match, the IP addresses don't match */ if (x != y) @@ -345,12 +236,12 @@ int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_lis for (i = 0; i < sblist_getsize (access_list); ++i) { acl = sblist_get (access_list, i); - switch (acl->type) { - case ACL_STRING: + switch (acl->h.type) { + case HST_STRING: perm = acl_string_processing (acl, ip, addr, string_addr); break; - case ACL_NUMERIC: + case HST_NUMERIC: if (ip[0] == '\0') continue; @@ -358,6 +249,10 @@ int check_acl (const char *ip, union sockaddr_union *addr, acl_list_t access_lis ? check_numeric_acl (acl, numeric_addr) : -1; break; + + case HST_NONE: + perm = -1; + break; } /* @@ -394,8 +289,8 @@ void flush_access_list (acl_list_t access_list) */ for (i = 0; i < sblist_getsize (access_list); ++i) { acl = sblist_get (access_list, i); - if (acl->type == ACL_STRING) { - safefree (acl->address.string); + if (acl->h.type == HST_STRING) { + safefree (acl->h.address.string); } } diff --git a/src/hostspec.c b/src/hostspec.c new file mode 100644 index 00000000..adbad538 --- /dev/null +++ b/src/hostspec.c @@ -0,0 +1,166 @@ +#include "common.h" +#include "hostspec.h" +#include "heap.h" +#include "network.h" + +/* + * Fills in the netmask array given a numeric value. + * + * Returns: + * 0 on success + * -1 on failure (invalid mask value) + * + */ +static int +fill_netmask_array (char *bitmask_string, int v6, + unsigned char array[], size_t len) +{ + unsigned int i; + unsigned long int mask; + char *endptr; + + errno = 0; /* to distinguish success/failure after call */ + mask = strtoul (bitmask_string, &endptr, 10); + + /* check for various conversion errors */ + if ((errno == ERANGE && mask == ULONG_MAX) + || (errno != 0 && mask == 0) || (endptr == bitmask_string)) + return -1; + + if (v6 == 0) { + /* The mask comparison is done as an IPv6 address, so + * convert to a longer mask in the case of IPv4 + * addresses. */ + mask += 12 * 8; + } + + /* check valid range for a bit mask */ + if (mask > (8 * len)) + return -1; + + /* we have a valid range to fill in the array */ + for (i = 0; i != len; ++i) { + if (mask >= 8) { + array[i] = 0xff; + mask -= 8; + } else if (mask > 0) { + array[i] = (unsigned char) (0xff << (8 - mask)); + mask = 0; + } else { + array[i] = 0; + } + } + + return 0; +} + + +/* parse a location string containing either an ipv4/ipv4 + hostmask tuple + or a dnsname into a struct hostspec. + returns 0 on success, non-0 on error (might be memory allocation, bogus + ip address or mask). +*/ +int hostspec_parse(char *location, struct hostspec *h) { + char *mask, ip_dst[IPV6_LEN]; + + h->type = HST_NONE; + if(!location) return 0; + + memset(h, 0, sizeof(*h)); + if ((mask = strrchr(location, '/'))) + *(mask++) = 0; + + /* + * Check for a valid IP address (the simplest case) first. + */ + if (full_inet_pton (location, ip_dst) > 0) { + h->type = HST_NUMERIC; + memcpy (h->address.ip.network, ip_dst, IPV6_LEN); + if(!mask) memset (h->address.ip.mask, 0xff, IPV6_LEN); + else { + char dst[sizeof(struct in6_addr)]; + int v6, i; + /* Check if the IP address before the netmask is + * an IPv6 address */ + if (inet_pton(AF_INET6, location, dst) > 0) + v6 = 1; + else + v6 = 0; + + if (fill_netmask_array + (mask, v6, &(h->address.ip.mask[0]), IPV6_LEN) + < 0) + goto err; + + for (i = 0; i < IPV6_LEN; i++) + h->address.ip.network[i] = ip_dst[i] & + h->address.ip.mask[i]; + } + } else { + /* either bogus IP or hostname */ + /* bogus ipv6 ? */ + if (mask || strchr (location, ':')) + goto err; + + /* In all likelihood a string */ + h->type = HST_STRING; + h->address.string = safestrdup (location); + if (!h->address.string) + goto err; + } + /* restore mask */ + if(mask) *(--mask) = '/'; + return 0; +err:; + if(mask) *(--mask) = '/'; + return -1; +} + +static int string_match(const char *ip, const char *addrspec) +{ + size_t test_length, match_length; + if(!strcasecmp(ip, addrspec)) return 1; + if(addrspec[0] != '.') return 0; + test_length = strlen (ip); + match_length = strlen (addrspec); + if (test_length < match_length) return 0; + return (strcasecmp + (ip + (test_length - match_length), + addrspec) == 0); +} + +static int numeric_match(const uint8_t addr[], const struct hostspec *h) +{ + uint8_t x, y; + int i; + + for (i = 0; i != IPV6_LEN; ++i) { + x = addr[i] & h->address.ip.mask[i]; + y = h->address.ip.network[i]; + + /* If x and y don't match, the IP addresses don't match */ + if (x != y) + return 0; + } + + return 1; +} + +/* check whether ip matches hostspec. + return 1 on match, 0 on non-match */ +int hostspec_match(const char *ip, const struct hostspec *h) { + int is_numeric_addr; + uint8_t numeric_addr[IPV6_LEN]; + if (ip[0] == '\0') return 0; + is_numeric_addr = (full_inet_pton (ip, &numeric_addr) > 0); + switch (h->type) { + case HST_STRING: + if(is_numeric_addr) return 0; + return string_match (ip, h->address.string); + case HST_NUMERIC: + return numeric_match (numeric_addr, h); + case HST_NONE: + return 0; + } + return 0; +} diff --git a/src/hostspec.h b/src/hostspec.h new file mode 100644 index 00000000..9d1d7bfb --- /dev/null +++ b/src/hostspec.h @@ -0,0 +1,26 @@ +#ifndef HOSTSPEC_H +#define HOSTSPEC_H + +#define IPV6_LEN 16 + +enum hostspec_type { + HST_NONE, + HST_STRING, + HST_NUMERIC, +}; + +struct hostspec { + enum hostspec_type type; + union { + char *string; + struct { + unsigned char network[IPV6_LEN]; + unsigned char mask[IPV6_LEN]; + } ip; + } address; +}; + +int hostspec_parse(char *domain, struct hostspec *h); +int hostspec_match(const char *ip, const struct hostspec *h); + +#endif diff --git a/src/upstream.c b/src/upstream.c index c8fee22d..52dad808 100644 --- a/src/upstream.c +++ b/src/upstream.c @@ -60,11 +60,10 @@ const char* upstream_build_error_string(enum upstream_build_error ube) { /** * Construct an upstream struct from input data. */ -static struct upstream *upstream_build (const char *host, int port, const char *domain, +static struct upstream *upstream_build (const char *host, int port, char *domain, const char *user, const char *pass, proxy_type type, enum upstream_build_error *ube) { - char *ptr; struct upstream *up; *ube = UBE_SUCCESS; @@ -75,8 +74,8 @@ static struct upstream *upstream_build (const char *host, int port, const char * } up->type = type; - up->host = up->domain = up->ua.user = up->pass = NULL; - up->ip = up->mask = 0; + up->target.type = HST_NONE; + up->host = up->ua.user = up->pass = NULL; if (user) { if (type == PT_HTTP) { char b[BASE64ENC_BYTES((256+2)-1) + 1]; @@ -121,30 +120,10 @@ static struct upstream *upstream_build (const char *host, int port, const char * up->port = port; } - ptr = strchr (domain, '/'); - if (ptr) { - struct in_addr addrstruct; - - *ptr = '\0'; - if (inet_aton (domain, &addrstruct) != 0) { - up->ip = ntohl (addrstruct.s_addr); - *ptr++ = '/'; - - if (strchr (ptr, '.')) { - if (inet_aton (ptr, &addrstruct) != 0) - up->mask = - ntohl (addrstruct.s_addr); - } else { - up->mask = - ~((1 << (32 - atoi (ptr))) - 1); - } - up->ip = up->ip & up->mask; - } else { - *ube = UBE_NETMASK; - goto fail; - } - } else { - up->domain = safestrdup (domain); + if (hostspec_parse(domain, &up->target) + || up->target.type == HST_NONE) { + *ube = UBE_NETMASK; + goto fail; } if (type == PT_NONE) @@ -160,7 +139,8 @@ static struct upstream *upstream_build (const char *host, int port, const char * safefree (up->ua.user); safefree (up->pass); safefree (up->host); - safefree (up->domain); + if(up->target.type == HST_STRING) + safefree (up->target.address.string); safefree (up); return NULL; @@ -170,7 +150,7 @@ static struct upstream *upstream_build (const char *host, int port, const char * * Add an entry to the upstream list */ enum upstream_build_error upstream_add ( - const char *host, int port, const char *domain, + const char *host, int port, char *domain, const char *user, const char *pass, proxy_type type, struct upstream **upstream_list) { @@ -182,11 +162,11 @@ enum upstream_build_error upstream_add ( return ube; } - if (!up->domain && !up->ip) { /* always add default to end */ + if (up->target.type == HST_NONE) { /* always add default to end */ struct upstream *tmp = *upstream_list; while (tmp) { - if (!tmp->domain && !tmp->ip) { + if (tmp->target.type == HST_NONE) { log_message (LOG_WARNING, "Duplicate default upstream"); goto upstream_cleanup; @@ -209,7 +189,8 @@ enum upstream_build_error upstream_add ( upstream_cleanup: safefree (up->host); - safefree (up->domain); + if(up->target.type == HST_STRING) + safefree (up->target.address.string); safefree (up); return ube; @@ -220,34 +201,12 @@ enum upstream_build_error upstream_add ( */ struct upstream *upstream_get (char *host, struct upstream *up) { - in_addr_t my_ip = INADDR_NONE; - while (up) { - if (up->domain) { - if (strcasecmp (host, up->domain) == 0) - break; /* exact match */ - - if (up->domain[0] == '.') { - char *dot = strchr (host, '.'); + if (up->target.type == HST_NONE) + break; - if (!dot && !up->domain[1]) - break; /* local host matches "." */ - - while (dot && strcasecmp (dot, up->domain)) - dot = strchr (dot + 1, '.'); - - if (dot) - break; /* subdomain match */ - } - } else if (up->ip) { - if (my_ip == INADDR_NONE) - my_ip = ntohl (inet_addr (host)); - - if ((my_ip & up->mask) == up->ip) - break; - } else { - break; /* No domain or IP, default upstream */ - } + if (hostspec_match(host, &up->target)) + break; up = up->next; } @@ -269,7 +228,8 @@ void free_upstream_list (struct upstream *up) while (up) { struct upstream *tmp = up; up = up->next; - safefree (tmp->domain); + if(tmp->target.type == HST_STRING) + safefree (tmp->target.address.string); safefree (tmp->host); safefree (tmp); } diff --git a/src/upstream.h b/src/upstream.h index a6118073..9a2314d9 100644 --- a/src/upstream.h +++ b/src/upstream.h @@ -26,6 +26,7 @@ #define _TINYPROXY_UPSTREAM_H_ #include "common.h" +#include "hostspec.h" enum upstream_build_error { UBE_SUCCESS = 0, @@ -50,7 +51,6 @@ typedef enum proxy_type { struct upstream { struct upstream *next; - char *domain; /* optional */ char *host; union { char *user; @@ -58,14 +58,14 @@ struct upstream { } ua; char *pass; int port; - in_addr_t ip, mask; + struct hostspec target; proxy_type type; }; #ifdef UPSTREAM_SUPPORT const char *proxy_type_name(proxy_type type); extern enum upstream_build_error upstream_add ( - const char *host, int port, const char *domain, + const char *host, int port, char *domain, const char *user, const char *pass, proxy_type type, struct upstream **upstream_list); extern struct upstream *upstream_get (char *host, struct upstream *up); From a869e71ac382acb2a8b4442477ed675e5bf0ce76 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 11 Aug 2020 16:35:40 +0100 Subject: [PATCH 181/265] add support for outgoing connections with HTTP/1.1 since there are numerous changes in HTTP/1.1, the proxyserver will stick to using HTTP/1.0 for internal usage, however when a connection is requested with HTTP/1.x from now on we will duplicate the minor revision the client requested, because apparently some servers refuse to accept HTTP/1.0 addresses #152. --- src/html-error.c | 3 ++- src/reqs.c | 12 +++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/html-error.c b/src/html-error.c index b6dd29d1..998a6ee7 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -138,13 +138,14 @@ int send_http_headers ( const char *message, const char *extra) { const char headers[] = - "HTTP/1.0 %d %s\r\n" + "HTTP/1.%u %d %s\r\n" "Server: %s/%s\r\n" "Content-Type: text/html\r\n" "%s" "Connection: close\r\n" "\r\n"; return (write_message (connptr->client_fd, headers, + connptr->protocol.major != 1 ? 0 : connptr->protocol.minor, code, message, PACKAGE, VERSION, extra)); } diff --git a/src/reqs.c b/src/reqs.c index 61a5eb0a..977af6b6 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -268,28 +268,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); } } From cc47fbf1f748e4be6ee2b6d0b0788e1b45b552b3 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 9 May 2021 17:53:52 +0100 Subject: [PATCH 182/265] manpage: URL-based filtering is no longer recommended --- docs/man5/tinyproxy.conf.txt.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 0629a9af..acab6c41 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -293,6 +293,11 @@ If this boolean option is set to `Yes` or `On`, filtering is performed for URLs rather than for domains. The default is to filter based on domains. +Note that filtering for URLs works only in plain HTTP scenarios. +Since HTTPS has become ubiquitous during the last years, this +will only work on a tiny fraction of websites, so it is +recommended not to use this option. + =item B If this boolean option is set to `Yes`, then extended POSIX From 9d815f69a4fa9baf2b2ce99fa09b12c8b5254589 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 9 May 2021 23:41:49 +0100 Subject: [PATCH 183/265] filter: hard error when filter file doesn't exist --- src/filter.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/filter.c b/src/filter.c index d70cb59d..b9b50661 100644 --- a/src/filter.c +++ b/src/filter.c @@ -61,7 +61,8 @@ void filter_init (void) fd = fopen (config->filter, "r"); if (!fd) { - return; + perror ("filter file"); + exit (EX_DATAERR); } cflags = REG_NEWLINE | REG_NOSUB; From aeb7b19c53b26bfcd85805737ed3c6263f634a7c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 10 May 2021 00:02:35 +0100 Subject: [PATCH 184/265] conf: do not warn about missing user directive unless root there's no point in printing a warning if the program is already started as a restricted user. --- src/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index 7e3c031e..1a788161 100644 --- a/src/conf.c +++ b/src/conf.c @@ -505,7 +505,7 @@ int reload_config_file (const char *config_fname, struct config_s *conf) goto done; } - if (!conf->user) { + if (!conf->user && !geteuid()) { log_message (LOG_WARNING, "You SHOULD set a UserName in the " "config file. Using current user instead."); } From 7af5b1a553026336ef935ea8ac240bba548b88fd Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 10 May 2021 00:25:50 +0100 Subject: [PATCH 185/265] manpage: improve FilterDefaultDeny paragraph --- docs/man5/tinyproxy.conf.txt.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index acab6c41..758382cf 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -316,6 +316,8 @@ The default filtering policy is to allow everything that is not matched by a filtering rule. Setting `FilterDefaultDeny` to `Yes` changes the policy do deny everything but the domains or URLs matched by the filtering rules. +In other words, if set to `No` the Filter list acts as a +blacklist, if set to `Yes` as a whitelist. =item B From e81d7767d321d4aca2abbfbac6eba5dea0da9786 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Wed, 12 May 2021 22:30:10 +0200 Subject: [PATCH 186/265] Add github actions workflow for new CI Signed-off-by: Michael Adam --- .github/workflows/main.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..d2f66566 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,28 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: ./autogen.sh + - run: ./configure + - run: make + - run: make test + valgrind: + runs-on: ubnutu-latest + steps: + - uses: actions/checkout@v2 + - name: install valgrind + run: apt-get install --assume-yes valgrind + - run: ./autogen.sh + - run: ./configure --enable-debug --enable-transparent --enable-reverse + - run: make + - run: make test + - run: make valgrind-test From 132a55cefbff018dab38a56e9e14569e3310fca1 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Wed, 12 May 2021 22:44:39 +0200 Subject: [PATCH 187/265] Fix github actions Signed-off-by: Michael Adam --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d2f66566..8f4a7f03 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,12 +15,12 @@ jobs: - run: ./configure - run: make - run: make test - valgrind: - runs-on: ubnutu-latest + valgrind-test: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: install valgrind - run: apt-get install --assume-yes valgrind + run: sudo apt-get install --assume-yes valgrind - run: ./autogen.sh - run: ./configure --enable-debug --enable-transparent --enable-reverse - run: make From 2db9a2a00fa22525c4d6850bd3f157ddc16d9ba0 Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Wed, 12 May 2021 23:16:23 +0200 Subject: [PATCH 188/265] github actions: add macos tests Signed-off-by: Michael Adam --- .github/workflows/main.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8f4a7f03..3be05e2f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,15 @@ jobs: - run: ./configure - run: make - run: make test + test-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - run: brew install automake + - run: ./autogen.sh + - run: ./configure + - run: make + - run: make test valgrind-test: runs-on: ubuntu-latest steps: From 60bf8b9e4d73648673a9218ece645b28eac377dc Mon Sep 17 00:00:00 2001 From: Michael Adam Date: Thu, 13 May 2021 01:58:08 +0200 Subject: [PATCH 189/265] github actions: don't run "make test" on macos it currenctly fails Signed-off-by: Michael Adam --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3be05e2f..43541078 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -23,7 +23,6 @@ jobs: - run: ./autogen.sh - run: ./configure - run: make - - run: make test valgrind-test: runs-on: ubuntu-latest steps: From 7168a42624fb9ce3305c9e666e44cc8a533af5f6 Mon Sep 17 00:00:00 2001 From: Alex Wied Date: Wed, 12 May 2021 15:15:11 -0400 Subject: [PATCH 190/265] Include limits.h to fix build on OSX --- src/acl.c | 2 -- src/common.h | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/acl.c b/src/acl.c index 63a3d348..2d141ba6 100644 --- a/src/acl.c +++ b/src/acl.c @@ -31,8 +31,6 @@ #include "sblist.h" #include "hostspec.h" -#include - /* * Hold the information about a particular access control. We store * whether it's an ALLOW or DENY entry, and also whether it's a string diff --git a/src/common.h b/src/common.h index 563ee6fc..a4925825 100644 --- a/src/common.h +++ b/src/common.h @@ -68,6 +68,7 @@ # include # include # include +# include /* rest - some oddball headers */ #ifdef HAVE_VALUES_H From 558e3f748df8587b0b313ed690935ae0c33f0a78 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 23 Jun 2021 16:59:55 +0100 Subject: [PATCH 191/265] configure.ac: remove unneeded "defs" linker flag the flag was added in 753010f571fcbe82c682cf00ab823d1e165a2f46 without explanation, and according to my research it is used to make the linker report undefined symbols when linking a shared library. since we don't build any shared libs, this isn't needed at all, but reportedly causes issues with cygwin (#382). --- configure.ac | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/configure.ac b/configure.ac index 38493835..d904c788 100644 --- a/configure.ac +++ b/configure.ac @@ -170,17 +170,6 @@ if test x"$debug_enabled" != x"yes" ; then CFLAGS="-DNDEBUG $CFLAGS" fi -AS_ECHO_N(["checking to see if linker understands -z,defs... "]) -LDFLAGS_OLD="-Wl $LDFLAGS" -LDFLAGS="-Wl,-z,defs $LDFLAGS" -AC_LINK_IFELSE([AC_LANG_PROGRAM()], - AS_ECHO("yes"), - AS_ECHO("no"); LDFLAGS="$LDFLAGS_OLD") - -dnl -dnl Make sure we can actually handle the "--with-*" and "--enable-*" stuff. -dnl - dnl dnl Substitute the variables into the various Makefiles dnl From bc81b4d9e87d2cbf51868288a07d453a03cd0e96 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 24 Jun 2021 22:55:33 +0100 Subject: [PATCH 192/265] put an end to LINE_MAX issues for some reason, getting this macro is really hard across platforms, requiring either different feature test macros or even the right order of included headers, and its usage caused several build failures in the past. fix it once and for all by just using 1024 as max line length if the macro can't be retrieved. closes #382 --- src/conf.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index 1a788161..4e6d19ea 100644 --- a/src/conf.c +++ b/src/conf.c @@ -40,6 +40,12 @@ #include "basicauth.h" #include "conf-tokens.h" +#ifdef LINE_MAX +#define TP_LINE_MAX LINE_MAX +#else +#define TP_LINE_MAX 1024 +#endif + /* * The configuration directives are defined in the structure below. Each * directive requires a regular expression to match against, and a @@ -409,7 +415,7 @@ static int check_match (struct config_s *conf, const char *line, */ static int config_parse (struct config_s *conf, FILE * f) { - char buffer[LINE_MAX], *p, *q, c; + char buffer[TP_LINE_MAX], *p, *q, c; const struct config_directive_entry *e; unsigned long lineno = 1; From 7ea9f80d3f31c85a4729854b47977e282632e6ed Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 25 Jun 2021 02:43:00 +0100 Subject: [PATCH 193/265] fix segfault in socks4 upstream with unresolvable hostname using a socks4 tor upstream with an .onion url resulted in gethostbyname() returning NULL and a subsequent segfault. not only did the code not check the return value of gethostbyname(), that resolver API itself isn't threadsafe. as pure SOCKS4 supports only IPv4 addresses, and the main SOCKS4 user to this date is tor, we just use SOCKS4a unconditionally and pass the hostname to the proxy without trying to do any local name resolving. i suspect in 2021 almost all SOCKS4 proxy servers in existence use SOCKS4a extension, but should i be wrong on this, i prefer issue reports to show up and implement plain SOCKS4 fallback only when i see it is actually used in practice. --- src/reqs.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 977af6b6..90160515 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1244,7 +1244,6 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) unsigned short port; size_t ulen, passlen; - struct hostent *host; struct upstream *cur_upstream = connptr->upstream_proxy; ulen = cur_upstream->ua.user ? strlen(cur_upstream->ua.user) : 0; @@ -1261,10 +1260,11 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) buff[1] = 1; /* connect command */ port = htons(request->port); memcpy(&buff[2], &port, 2); /* dest port */ - host = gethostbyname(request->host); - memcpy(&buff[4], host->h_addr_list[0], 4); /* dest ip */ - buff[8] = 0; /* user */ - if (9 != safe_write(connptr->server_fd, buff, 9)) + memcpy(&buff[4], "\0\0\0\1" /* socks4a fake ip */ + "\0" /* user */, 5); + len = strlen(request->host); + memcpy(&buff[9], request->host, len+1); + if (9+len+1 != safe_write(connptr->server_fd, buff, 9+len+1)) return -1; if (8 != safe_read(connptr->server_fd, buff, 8)) return -1; From 563978a3ea4e8b51fcf547298dfdafc516e04526 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 25 Jun 2021 02:55:22 +0100 Subject: [PATCH 194/265] socks4 upstream: add safety check for hostname length --- src/reqs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/reqs.c b/src/reqs.c index 90160515..03d25990 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1263,6 +1263,8 @@ connect_to_upstream_proxy(struct conn_s *connptr, struct request_s *request) memcpy(&buff[4], "\0\0\0\1" /* socks4a fake ip */ "\0" /* user */, 5); len = strlen(request->host); + if(len>255) + return -1; memcpy(&buff[9], request->host, len+1); if (9+len+1 != safe_write(connptr->server_fd, buff, 9+len+1)) return -1; From e91e48dd60f91fe3b65d869de5cb0793d2a98106 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 21 Jul 2021 14:29:20 +0100 Subject: [PATCH 195/265] add an issue template (#387) as it's unproductive to be getting the same bug report for old tinyproxy versions over and over, and people not even stating which version they're using, this new issue template makes people aware of what information to include when filing an issue request. --- .../new-issue--bug-report--question.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/new-issue--bug-report--question.md diff --git a/.github/ISSUE_TEMPLATE/new-issue--bug-report--question.md b/.github/ISSUE_TEMPLATE/new-issue--bug-report--question.md new file mode 100644 index 00000000..32d49956 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-issue--bug-report--question.md @@ -0,0 +1,20 @@ +--- +name: New Issue, Bug report, Question +about: New Issue, Bug report, Question +title: '' +labels: '' +assignees: '' + +--- + +# IMPORTANT NOTICE + +Before filing an issue here PLEASE keep in mind that **tinyproxy 1.10.0 and older are no longer supported**. +Do not report issues with 1.10.0 or older, first try latest release 1.11.0, or even better, git master, and see whether the issue is already fixed. + +## Tinyproxy version +State the tinyproxy version you're using; whether git master or 1.11.0 stable. + +## Issue +Fill in your Issue text here. +A good issue report is detailed and includes full error messages from tinyproxy's output, not "X doesn't work". From 39d7bf6c70f661bc6bc69794d288361eea07178c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 23 Jul 2021 20:17:18 +0100 Subject: [PATCH 196/265] improve error message for "Error reading readable client_fd" maybe this helps to track down the cause of #383. --- src/reqs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 03d25990..dcbdd830 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1464,8 +1464,8 @@ get_request_entity(struct conn_s *connptr) nread = read_buffer (connptr->client_fd, connptr->cbuffer); if (nread < 0) { log_message (LOG_ERR, - "Error reading readable client_fd %d", - connptr->client_fd); + "Error reading readable client_fd %d (%s)", + connptr->client_fd, strerror(errno)); ret = -1; } else { log_message (LOG_INFO, From c1023f68217b708b3c1c5f1849f7a4a680e1e012 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 23 Aug 2021 13:46:42 +0000 Subject: [PATCH 197/265] fix regression failing to parse dotted netmask in upstream introduced in 979c737f9b811c5441ae0573a90b72dc1e44e142. when refactoring the "site-spec" parsing code i failed to realize that the code dealing with acl allow/deny directives didn't provide the option to specify netmasks in dotted ipv4 notation, unlike the code in the upstream parser. since both scenarios now use the same parsing, both dotted notation and CIDR slash-notation are possible. while at it, removed the len parameter from fill_netmask_array() which provided the illusion the array length could be of variable size. fixes #394 --- src/hostspec.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/hostspec.c b/src/hostspec.c index adbad538..1f956f2b 100644 --- a/src/hostspec.c +++ b/src/hostspec.c @@ -3,6 +3,15 @@ #include "heap.h" #include "network.h" +static int dotted_mask(char *bitmask_string, unsigned char array[]) +{ + unsigned char v4bits[4]; + if (1 != inet_pton (AF_INET, bitmask_string, v4bits)) return -1; + memset (array, 0xff, IPV6_LEN-4); + memcpy (array + IPV6_LEN-4, v4bits, 4); + return 0; +} + /* * Fills in the netmask array given a numeric value. * @@ -13,13 +22,17 @@ */ static int fill_netmask_array (char *bitmask_string, int v6, - unsigned char array[], size_t len) + unsigned char array[]) { unsigned int i; unsigned long int mask; char *endptr; errno = 0; /* to distinguish success/failure after call */ + if (strchr (bitmask_string, '.')) { + if (v6) return -1; /* ipv6 doesn't supported dotted netmasks */ + return dotted_mask(bitmask_string, array); + } mask = strtoul (bitmask_string, &endptr, 10); /* check for various conversion errors */ @@ -35,11 +48,11 @@ fill_netmask_array (char *bitmask_string, int v6, } /* check valid range for a bit mask */ - if (mask > (8 * len)) + if (mask > (8 * IPV6_LEN)) return -1; /* we have a valid range to fill in the array */ - for (i = 0; i != len; ++i) { + for (i = 0; i != IPV6_LEN; ++i) { if (mask >= 8) { array[i] = 0xff; mask -= 8; @@ -88,7 +101,7 @@ int hostspec_parse(char *location, struct hostspec *h) { v6 = 0; if (fill_netmask_array - (mask, v6, &(h->address.ip.mask[0]), IPV6_LEN) + (mask, v6, &(h->address.ip.mask[0])) < 0) goto err; From 207f7903140f280a6e32ebb1d92c01194ef606cb Mon Sep 17 00:00:00 2001 From: "Aaron M. Ucko" Date: Tue, 21 Sep 2021 21:46:40 -0400 Subject: [PATCH 198/265] Fix FTBFS on hurd-i386: run_tests.sh duplicates build path. --- tests/scripts/run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index eb1d3c42..d64ea6ae 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -18,7 +18,7 @@ # this program; if not, see . -SCRIPTS_DIR=$(pwd)/$(dirname $0) +SCRIPTS_DIR=$(cd $(dirname $0) && pwd) BASEDIR=$SCRIPTS_DIR/../.. TESTS_DIR=$SCRIPTS_DIR/.. TESTENV_DIR=$TESTS_DIR/env From f0c291e1daa78ac6dbc9c27bff75f09d46153b51 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 22 Sep 2021 12:07:33 +0000 Subject: [PATCH 199/265] README.md: freenode is dead, long live libera since freenode.net doesn't even serve a website anymore, it's safe to finally announce the switch. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bba85dcd..83eb99be 100644 --- a/README.md +++ b/README.md @@ -89,4 +89,4 @@ and create a [pull request](https://github.com/tinyproxy/tinyproxy/pulls). You can meet developers and users to discuss development, patches and deployment issues in the `#tinyproxy` IRC channel on -Freenode (`irc.freenode.net`). +libera (`irc.libera.chat`). From d3d8943fe45e53a39864070b74fdfa95dd93f1f0 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 22 Sep 2021 12:14:58 +0000 Subject: [PATCH 200/265] update website footer re: freenode --- docs/web/tp.html.foot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/web/tp.html.foot b/docs/web/tp.html.foot index d080bac1..90654f9c 100644 --- a/docs/web/tp.html.foot +++ b/docs/web/tp.html.foot @@ -3,7 +3,7 @@
  • Feel free to report a new bug or suggest features via github issues.
  • -
  • Tinyproxy developers hang out in #tinyproxy on irc.freenode.net.
  • +
  • Tinyproxy developers hang out in #tinyproxy on irc.libera.chat.
From 79d0b0fa79249a714cbb1992d597e21adc30b16e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 20 Jan 2022 20:25:42 +0000 Subject: [PATCH 201/265] fix timeout not being applied to outgoing connections the fix in 0b9a74c29036f9215b2b97a301b7b25933054302 was incomplete, as it applied the socket timeout only to the socket received from accept(), but not to sockets created for outgoing connections. --- src/reqs.c | 11 ++++------- src/sock.c | 12 ++++++++++++ src/sock.h | 2 ++ 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index dcbdd830..2be8e241 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1517,6 +1517,9 @@ static void handle_connection_failure(struct conn_s *connptr, int got_headers) * when we start the relay portion. This makes most of the original * tinyproxy code, which was confusing, redundant. Hail progress. * - rjkaes + + * this function is called directly from child_thread() with the newly + * received fd from accept(). */ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) { @@ -1528,7 +1531,6 @@ 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; - struct timeval tv; orderedmap hashofheaders = NULL; char sock_ipaddr[IP_LENGTH]; @@ -1550,12 +1552,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) return; } - 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)); + set_socket_timeout(fd); if (connection_loops (addr)) { log_message (LOG_CONN, diff --git a/src/sock.c b/src/sock.c index 70169a68..427221c0 100644 --- a/src/sock.c +++ b/src/sock.c @@ -108,6 +108,16 @@ bind_socket_list (int sockfd, sblist *addresses, int family) return -1; } +void set_socket_timeout(int fd) { + struct timeval tv; + 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)); +} + /* * Open a connection to a remote host. It's been re-written to use * the getaddrinfo() library function, which allows for a protocol @@ -163,6 +173,8 @@ int opensock (const char *host, int port, const char *bind_to) } } + set_socket_timeout(sockfd); + if (connect (sockfd, res->ai_addr, res->ai_addrlen) == 0) { union sockaddr_union *p = (void*) res->ai_addr, u; int af = res->ai_addr->sa_family; diff --git a/src/sock.h b/src/sock.h index aee5bf59..70c44739 100644 --- a/src/sock.h +++ b/src/sock.h @@ -56,6 +56,8 @@ extern int listen_sock (const char *addr, uint16_t port, sblist* listen_fds); extern int socket_nonblocking (int sock); extern int socket_blocking (int sock); +extern void set_socket_timeout(int fd); + extern int getsock_ip (int fd, char *ipaddr); extern void getpeer_information (union sockaddr_union *addr, char *ipaddr, size_t ipaddr_len); From 17d3733be36b0daabda1bdb19a3c4452a360d8f9 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 26 Jan 2022 12:13:22 +0000 Subject: [PATCH 202/265] main: fix logging being disabled after reload conf fails fixes #417 --- src/main.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main.c b/src/main.c index 13092844..e8c9c071 100644 --- a/src/main.c +++ b/src/main.c @@ -254,7 +254,7 @@ change_user (const char *program) */ int reload_config (int reload_logging) { - int ret; + int ret, ret2; struct config_s *c_next = get_next_config(); log_message (LOG_NOTICE, "Reloading config file"); @@ -263,18 +263,19 @@ int reload_config (int reload_logging) ret = reload_config_file (config_file, c_next); - if (ret != 0) { - goto done; + if (ret == 0) { + if(config) free_config (config); + config = c_next; } - if(config) free_config (config); - config = c_next; + if (reload_logging) ret2 = setup_logging (); - if (reload_logging) ret = setup_logging (); - log_message (LOG_NOTICE, "Reloading config file finished"); + if (ret != 0) + log_message (LOG_WARNING, "Reloading config file failed!"); + else + log_message (LOG_NOTICE, "Reloading config file finished"); -done: - return ret; + return ret ? ret : ret2; } static void setup_sig(int sig, signal_func *sigh, From eced6822f8b2947e2001da6092fc147c0f7b6687 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 13 Feb 2022 21:11:37 +0000 Subject: [PATCH 203/265] properly deal with client sending chunked data this fixes OPTIONS requests sent from apache SVN client using their native HTTP proxy support. closes #421 tested with `svn info http://svnmir.bme.freebsd.org/ports/` --- src/reqs.c | 108 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 30 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 2be8e241..cdc3b618 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -523,7 +523,7 @@ static struct request_s *process_request (struct conn_s *connptr, * server headers can be processed. * - rjkaes */ -static int pull_client_data (struct conn_s *connptr, long int length) +static int pull_client_data (struct conn_s *connptr, long int length, int iehack) { char *buffer; ssize_t len; @@ -548,39 +548,75 @@ static int pull_client_data (struct conn_s *connptr, long int length) length -= len; } while (length > 0); - /* - * BUG FIX: Internet Explorer will leave two bytes (carriage - * return and line feed) at the end of a POST message. These - * need to be eaten for tinyproxy to work correctly. - */ - 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)); - goto ERROR_EXIT; + if (iehack) { + /* + * BUG FIX: Internet Explorer will leave two bytes (carriage + * return and line feed) at the end of a POST message. These + * need to be eaten for tinyproxy to work correctly. + */ + 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)); + goto ERROR_EXIT; + } + + len = recv (connptr->client_fd, buffer, 2, MSG_PEEK); + + ret = socket_blocking (connptr->client_fd); + if (ret != 0) { + log_message(LOG_ERR, "Failed to set the client socket " + "to blocking: %s", strerror(errno)); + goto ERROR_EXIT; + } + + if (len < 0 && errno != EAGAIN) + goto ERROR_EXIT; + + if ((len == 2) && CHECK_CRLF (buffer, len)) { + ssize_t bytes_read; + + bytes_read = read (connptr->client_fd, buffer, 2); + if (bytes_read == -1) { + log_message + (LOG_WARNING, + "Could not read two bytes from POST message"); + } + } } - len = recv (connptr->client_fd, buffer, 2, MSG_PEEK); + safefree (buffer); + return 0; - ret = socket_blocking (connptr->client_fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the client socket " - "to blocking: %s", strerror(errno)); - goto ERROR_EXIT; - } +ERROR_EXIT: + safefree (buffer); + return -1; +} - if (len < 0 && errno != EAGAIN) - goto ERROR_EXIT; +/* pull chunked client data */ +static int pull_client_data_chunked (struct conn_s *connptr) { + char *buffer = 0; + ssize_t len; + long chunklen; + + while(1) { + if (buffer) safefree(buffer); + len = readline (connptr->client_fd, &buffer); - if ((len == 2) && CHECK_CRLF (buffer, len)) { - ssize_t bytes_read; + if (len <= 0) + goto ERROR_EXIT; - bytes_read = read (connptr->client_fd, buffer, 2); - if (bytes_read == -1) { - log_message - (LOG_WARNING, - "Could not read two bytes from POST message"); + if (!connptr->error_variables) { + if (safe_write (connptr->server_fd, buffer, len) < 0) + goto ERROR_EXIT; } + + chunklen = strtol (buffer, (char**)0, 16); + + if (pull_client_data (connptr, chunklen+2, 0) < 0) + goto ERROR_EXIT; + + if(!chunklen) break; } safefree (buffer); @@ -787,7 +823,7 @@ static int remove_connection_headers (orderedmap hashofheaders) /* * If there is a Content-Length header, then return the value; otherwise, return - * a negative number. + * -1. */ static long get_content_length (orderedmap hashofheaders) { @@ -802,6 +838,13 @@ static long get_content_length (orderedmap hashofheaders) return content_length; } +static int is_chunked_transfer (orderedmap hashofheaders) +{ + char *data; + data = orderedmap_find (hashofheaders, "transfer-encoding"); + return data ? !strcmp (data, "chunked") : 0; +} + /* * Search for Via header in a hash of headers and either write a new Via * header, or append our information to the end of an existing Via header. @@ -896,6 +939,10 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) */ connptr->content_length.client = get_content_length (hashofheaders); + /* Check whether client sends chunked data. */ + if (connptr->content_length.client == -1 && is_chunked_transfer (hashofheaders)) + connptr->content_length.client = -2; + /* * See if there is a "Connection" header. If so, we need to do a bit * of processing. :) @@ -960,8 +1007,9 @@ process_client_headers (struct conn_s *connptr, orderedmap hashofheaders) PULL_CLIENT_DATA: if (connptr->content_length.client > 0) { ret = pull_client_data (connptr, - connptr->content_length.client); - } + connptr->content_length.client, 1); + } else if (connptr->content_length.client == -2) + ret = pull_client_data_chunked (connptr); return ret; } From 1576ee279f03dad56808acb6bb3e3b30b4706c31 Mon Sep 17 00:00:00 2001 From: "Malte S. Stretz" Date: Sat, 20 Nov 2021 17:39:21 +0100 Subject: [PATCH 204/265] Return 5xx when upstream is unreachable Currently a 404 is returned for a misconfigured or unavailable upstream server. Since that's a server error it should be a 5xx instead; a 404 is confusing when used as a forward proxy and might even be harmful when used as a reverse proxy. It is debatable if another 5xx code might be better; the misconfigured situation might better be a 500 whereas the connection issue could be a 503 instead (as used eg. in haproxy). --- etc/tinyproxy.conf.in | 2 +- src/reqs.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index 06f5480a..ce27f7ec 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -56,8 +56,8 @@ Timeout 600 # /usr/share/tinyproxy # /etc/tinyproxy # -#ErrorFile 404 "@pkgdatadir@/404.html" #ErrorFile 400 "@pkgdatadir@/400.html" +#ErrorFile 502 "@pkgdatadir@/502.html" #ErrorFile 503 "@pkgdatadir@/503.html" #ErrorFile 403 "@pkgdatadir@/403.html" #ErrorFile 408 "@pkgdatadir@/408.html" diff --git a/src/reqs.c b/src/reqs.c index cdc3b618..50f69144 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1424,7 +1424,7 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request) log_message (LOG_WARNING, "No upstream proxy defined for %s.", request->host); - indicate_http_error (connptr, 404, + indicate_http_error (connptr, 502, "Unable to connect to upstream proxy."); return -1; } @@ -1436,7 +1436,7 @@ connect_to_upstream (struct conn_s *connptr, struct request_s *request) if (connptr->server_fd < 0) { log_message (LOG_WARNING, "Could not connect to upstream proxy."); - indicate_http_error (connptr, 404, + indicate_http_error (connptr, 502, "Unable to connect to upstream proxy", "detail", "A network error occurred while trying to " From 479df8ecec5c5f52a61d9a1295dd51a1933226cc Mon Sep 17 00:00:00 2001 From: "Malte S. Stretz" Date: Sat, 20 Nov 2021 17:55:21 +0100 Subject: [PATCH 205/265] Add test case for unavailable upstream --- tests/scripts/run_tests.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/scripts/run_tests.sh b/tests/scripts/run_tests.sh index d64ea6ae..5a8ecd8f 100755 --- a/tests/scripts/run_tests.sh +++ b/tests/scripts/run_tests.sh @@ -83,6 +83,7 @@ DefaultErrorFile "$TINYPROXY_DATA_DIR/debug.html" ErrorFile 400 "$TINYPROXY_DATA_DIR/debug.html" ErrorFile 403 "$TINYPROXY_DATA_DIR/debug.html" ErrorFile 501 "$TINYPROXY_DATA_DIR/debug.html" +ErrorFile 502 "$TINYPROXY_DATA_DIR/debug.html" StatFile "$TINYPROXY_DATA_DIR/stats.html" Logfile "$TINYPROXY_LOG_FILE" PidFile "$TINYPROXY_PID_FILE" @@ -99,6 +100,7 @@ XTinyproxy Yes AddHeader "X-My-Header1" "Powered by Tinyproxy" AddHeader "X-My-Header2" "Powered by Tinyproxy" AddHeader "X-My-Header3" "Powered by Tinyproxy" +Upstream http 255.255.255.255:65535 ".invalid" EOF cat << 'EOF' > $TINYPROXY_FILTER_FILE @@ -244,6 +246,10 @@ test "x$?" = "x0" || FAILED=$((FAILED + 1)) echo -n "requesting connect method to denied port..." run_failure_webclient_request 403 --method=CONNECT "$TINYPROXY_IP:$TINYPROXY_PORT" "localhost:12345" test "x$?" = "x0" || FAILED=$((FAILED + 1)) + +echo -n "testing unavailable backend..." +run_failure_webclient_request 502 "$TINYPROXY_IP:$TINYPROXY_PORT" "http://bogus.invalid" +test "x$?" = "x0" || FAILED=$((FAILED + 1)) } basic_test From 9718be09c16ea4b4fb3cd5cb2b53c33055a4361f Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 16 Feb 2022 02:28:11 +0000 Subject: [PATCH 206/265] reload_logging: fix returning possibly uninitialized value introduced in 17d3733be36b0daabda1bdb19a3c4452a360d8f9 may fix #422 --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index e8c9c071..7ea55408 100644 --- a/src/main.c +++ b/src/main.c @@ -268,7 +268,7 @@ int reload_config (int reload_logging) config = c_next; } - if (reload_logging) ret2 = setup_logging (); + ret2 = reload_logging ? setup_logging () : 0; if (ret != 0) log_message (LOG_WARNING, "Reloading config file failed!"); From c63028d6758a01326f43707f7e5fe29e302ddc5f Mon Sep 17 00:00:00 2001 From: rofl0r Date: Tue, 1 Mar 2022 15:17:32 +0000 Subject: [PATCH 207/265] bind_socket(): improve log output bind_socket most often fails due to having wrong address family, so it's helpful to see which one was used. --- src/sock.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sock.c b/src/sock.c index 427221c0..b3b0920d 100644 --- a/src/sock.c +++ b/src/sock.c @@ -47,6 +47,16 @@ static const char * get_gai_error (int n) return gai_strerror (n); } +static const char * family_string (int af) +{ + switch(af) { + case AF_UNSPEC: return "AF_UNSPEC"; + case AF_INET: return "AF_INET"; + case AF_INET6: return "AF_INET6"; + } + return "unknown"; +} + /* * Bind the given socket to the supplied address. The socket is * returned if the bind succeeded. Otherwise, -1 is returned @@ -69,7 +79,7 @@ bind_socket (int sockfd, const char *addr, int family) n = getaddrinfo (addr, NULL, &hints, &res); if (n != 0) { log_message (LOG_INFO, - "bind_socket: getaddrinfo failed for %s: %s", addr, get_gai_error (n)); + "bind_socket: getaddrinfo failed for %s: %s (af: %s)", addr, get_gai_error (n), family_string(family)); return -1; } From 14d31ed63fceb733e219ed2a290f0c2b87749953 Mon Sep 17 00:00:00 2001 From: dr|z3d Date: Fri, 22 Apr 2022 05:27:47 +0000 Subject: [PATCH 208/265] Prettify stats.html --- data/templates/stats.html | 162 ++++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 68 deletions(-) diff --git a/data/templates/stats.html b/data/templates/stats.html index 71798592..a8c3e074 100644 --- a/data/templates/stats.html +++ b/data/templates/stats.html @@ -1,69 +1,95 @@ - - - - - - -{package} version {version} run-time statistics - - - - - - - - -

{package} version {version} run-time statistics

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameValue
Number of open connections{opens}
Number of requests{reqs}
Number of bad connections{badconns}
Number of denied connections{deniedconns}
Number of refused connections due to high load{refusedconns}
- -
- -

Generated by {package} version {version}.

- - - + + + + + Stats [{package} v{version}] + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
{package} v{version} statistics
Open connections{opens}
Bad connections{badconns}
Denied connections{deniedconns}
Refused (high load){refusedconns}
Total requests{reqs}
+
+
+ From 26db3f6cc95c2eae533234b6140c23233284a404 Mon Sep 17 00:00:00 2001 From: Tristan Stenner Date: Fri, 29 Apr 2022 15:32:53 +0200 Subject: [PATCH 209/265] Allow "XTinyProxy No" with xtinyproxy disabled --- src/conf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/conf.c b/src/conf.c index 4e6d19ea..7033661a 100644 --- a/src/conf.c +++ b/src/conf.c @@ -707,6 +707,8 @@ static HANDLE_FUNC (handle_xtinyproxy) #ifdef XTINYPROXY_ENABLE return set_bool_arg (&conf->add_xtinyproxy, line, &match[2]); #else + if(!get_bool_arg(line, &match[2])) + return 0; fprintf (stderr, "XTinyproxy NOT Enabled! Recompile with --enable-xtinyproxy\n"); return 1; From 235b1c10a76a02f4837febcf84efd9b7edcd020e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 10 May 2021 00:28:54 +0100 Subject: [PATCH 210/265] implement filtertype keyword and fnmatch-based filtering as suggested in #212, it seems the majority of people don't understand that input was expected to be in regex format and people were using filter lists containing plain hostnames, e.g. `www.google.com`. apart from that, using fnmatch() for matching is actually a lot less computationally expensive and allows to use big blacklists without incurring a huge performance hit. the config file now understands a new option `FilterType` which can be one of `bre`, `ere` and `fnmatch`. The `FilterExtended` option was deprecated in favor of it. It still works, but will be removed in the release after the next. --- docs/man5/tinyproxy.conf.txt.in | 23 +++++++++++-- etc/tinyproxy.conf.in | 5 ++- src/conf-tokens.c | 1 + src/conf-tokens.gperf | 1 + src/conf-tokens.h | 1 + src/conf.c | 46 +++++++++++++++++++++---- src/conf.h | 4 +-- src/filter.c | 59 ++++++++++++++++++--------------- src/filter.h | 18 ++++++---- src/reqs.c | 18 ++++------ 10 files changed, 117 insertions(+), 59 deletions(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 758382cf..1e1ee3ef 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -267,7 +267,7 @@ domains. This option specifies the location of the file containing the filter rules, one rule per line. Rules are specified as POSIX basic regular expressions (BRE), unless -FilterExtended is activated. +another FilterType is specified. Comment lines start with a `#` character. Example filter file contents: @@ -287,6 +287,20 @@ Example filter file contents: # filter any domain that starts with adserver ^adserver +=item B + +This option can be set to one of `bre`, `ere`, or `fnmatch`. +If `bre` is set, the rules specified in the filter file are matched +using POSIX basic regular expressions, when set to `ere`, using +POSIX extended regular expressions, and when set to `fnmatch` using +the `fnmatch` function as specified in the manpage `man 3p fnmatch`. +`fnmatch` matching is identical to what's used in the shell to match +filenames, so for example `*.google.com` matches everything that +ends with `.google.com`. +If you don't know what regular expressions are or you're using filter +lists from 3rd party sources, `fnmatch` is probably what you want. +It's also the fastest matching method of the three. + =item B If this boolean option is set to `Yes` or `On`, filtering is @@ -300,6 +314,7 @@ recommended not to use this option. =item B +Deprecated. Use `FilterType ere` instead. If this boolean option is set to `Yes`, then extended POSIX regular expressions are used for matching the filter rules. The default is to use basic POSIX regular expressions. @@ -308,7 +323,11 @@ The default is to use basic POSIX regular expressions. If this boolean option is set to `Yes`, then the filter rules are matched in a case sensitive manner. The default is to -match case-insensitively. +match case-insensitively, unfortunately. +If you set this to `Yes`, then your matching will be almost +twice as fast. +This setting affects only `bre` and `ere` FilterTypes, fnmatch +is always case sensitive. =item B diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index ce27f7ec..d2687093 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -240,10 +240,9 @@ ViaProxyName "tinyproxy" #FilterURLs On # -# FilterExtended: Use POSIX Extended regular expressions rather than -# basic. +# FilterType: Use bre (default), ere, or fnmatch for filtering. # -#FilterExtended On +#FilterType fnmatch # # FilterCaseSensitive: Use case sensitive regular expressions. diff --git a/src/conf-tokens.c b/src/conf-tokens.c index bad70136..2a1ddbe8 100644 --- a/src/conf-tokens.c +++ b/src/conf-tokens.c @@ -34,6 +34,7 @@ config_directive_find (register const char *str, register size_t len) {"defaulterrorfile", CD_defaulterrorfile}, {"startservers", CD_startservers}, {"filtercasesensitive", CD_filtercasesensitive}, + {"filtertype", CD_filtertype}, {"filterurls", CD_filterurls}, {"filter", CD_filter}, {"reversemagic", CD_reversemagic}, diff --git a/src/conf-tokens.gperf b/src/conf-tokens.gperf index ef93245c..f027a23b 100644 --- a/src/conf-tokens.gperf +++ b/src/conf-tokens.gperf @@ -51,6 +51,7 @@ filterurls, CD_filterurls filterextended, CD_filterextended filterdefaultdeny, CD_filterdefaultdeny filtercasesensitive, CD_filtercasesensitive +filtertype, CD_filtertype reversebaseurl, CD_reversebaseurl reverseonly, CD_reverseonly reversemagic, CD_reversemagic diff --git a/src/conf-tokens.h b/src/conf-tokens.h index d9f03cd7..a6338f8f 100644 --- a/src/conf-tokens.h +++ b/src/conf-tokens.h @@ -33,6 +33,7 @@ CD_errorfile, CD_addheader, CD_filter, CD_filterurls, +CD_filtertype, CD_filterextended, CD_filterdefaultdeny, CD_filtercasesensitive, diff --git a/src/conf.c b/src/conf.c index 7033661a..7f94b654 100644 --- a/src/conf.c +++ b/src/conf.c @@ -135,6 +135,7 @@ static HANDLE_FUNC (handle_filtercasesensitive); static HANDLE_FUNC (handle_filterdefaultdeny); static HANDLE_FUNC (handle_filterextended); static HANDLE_FUNC (handle_filterurls); +static HANDLE_FUNC (handle_filtertype); #endif static HANDLE_FUNC (handle_group); static HANDLE_FUNC (handle_listen); @@ -234,6 +235,7 @@ struct { STDCONF (filterextended, BOOL, handle_filterextended), STDCONF (filterdefaultdeny, BOOL, handle_filterdefaultdeny), STDCONF (filtercasesensitive, BOOL, handle_filtercasesensitive), + STDCONF (filtertype, "(bre|ere|fnmatch)", handle_filtertype), #endif #ifdef REVERSE_SUPPORT /* Reverse proxy arguments */ @@ -952,6 +954,11 @@ static HANDLE_FUNC (handle_basicauth) } #ifdef FILTER_ENABLE + +static void warn_deprecated(const char *arg, unsigned long lineno) { + CP_WARN ("deprecated option %s", arg); +} + static HANDLE_FUNC (handle_filter) { return set_string_arg (&conf->filter, line, &match[2]); @@ -959,26 +966,53 @@ static HANDLE_FUNC (handle_filter) static HANDLE_FUNC (handle_filterurls) { - return set_bool_arg (&conf->filter_url, line, &match[2]); + conf->filter_opts |= + get_bool_arg (line, &match[2]) * FILTER_OPT_URL; + return 0; } static HANDLE_FUNC (handle_filterextended) { - return set_bool_arg (&conf->filter_extended, line, &match[2]); + warn_deprecated("FilterExtended, use FilterType", lineno); + conf->filter_opts |= + get_bool_arg (line, &match[2]) * FILTER_OPT_TYPE_ERE; + return 0; } static HANDLE_FUNC (handle_filterdefaultdeny) { assert (match[2].rm_so != -1); - - if (get_bool_arg (line, &match[2])) - filter_set_default_policy (FILTER_DEFAULT_DENY); + conf->filter_opts |= + get_bool_arg (line, &match[2]) * FILTER_OPT_DEFAULT_DENY; return 0; } static HANDLE_FUNC (handle_filtercasesensitive) { - return set_bool_arg (&conf->filter_casesensitive, line, &match[2]); + conf->filter_opts |= + get_bool_arg (line, &match[2]) * FILTER_OPT_CASESENSITIVE; + return 0; +} + +static HANDLE_FUNC (handle_filtertype) +{ + static const struct { unsigned short flag; char type[8]; } + ftmap[] = { + {FILTER_OPT_TYPE_ERE, "ere"}, + {FILTER_OPT_TYPE_BRE, "bre"}, + {FILTER_OPT_TYPE_FNMATCH, "fnmatch"}, + }; + char *type; + unsigned i; + type = get_string_arg(line, &match[2]); + if (!type) return -1; + + for(i=0;ifilter_opts |= ftmap[i].flag; + + safefree (type); + return 0; } #endif diff --git a/src/conf.h b/src/conf.h index 99140492..0a0f06f7 100644 --- a/src/conf.h +++ b/src/conf.h @@ -50,9 +50,7 @@ struct config_s { sblist *listen_addrs; #ifdef FILTER_ENABLE char *filter; - unsigned int filter_url; /* boolean */ - unsigned int filter_extended; /* boolean */ - unsigned int filter_casesensitive; /* boolean */ + unsigned int filter_opts; /* enum filter_options */ #endif /* FILTER_ENABLE */ #ifdef XTINYPROXY_ENABLE unsigned int add_xtinyproxy; /* boolean */ diff --git a/src/filter.c b/src/filter.c index b9b50661..0dbc93d2 100644 --- a/src/filter.c +++ b/src/filter.c @@ -25,6 +25,7 @@ #include "main.h" #include +#include #include "filter.h" #include "heap.h" #include "log.h" @@ -37,15 +38,17 @@ static int err; struct filter_list { - regex_t cpatb; + union { + regex_t cpatb; + char *pattern; + } u; }; static sblist *fl = NULL; static int already_init = 0; -static filter_policy_t default_policy = FILTER_DEFAULT_ALLOW; /* - * Initializes a linked list of strings containing hosts/urls to be filtered + * Initializes a list of strings containing hosts/urls to be filtered */ void filter_init (void) { @@ -66,10 +69,8 @@ void filter_init (void) } cflags = REG_NEWLINE | REG_NOSUB; - if (config->filter_extended) - cflags |= REG_EXTENDED; - if (!config->filter_casesensitive) - cflags |= REG_ICASE; + cflags |= (REG_EXTENDED * !!(config->filter_opts & FILTER_OPT_TYPE_ERE)); + cflags |= (REG_ICASE * !(config->filter_opts & FILTER_OPT_CASESENSITIVE)); while (fgets (buf, FILTER_BUFFER_LEN, fd)) { ++lineno; @@ -107,13 +108,19 @@ void filter_init (void) if (!fl) fl = sblist_new(sizeof(struct filter_list), 4096/sizeof(struct filter_list)); - err = regcomp (&fe.cpatb, s, cflags); - if (err != 0) { - if (err == REG_ESPACE) goto oom; - fprintf (stderr, - "Bad regex in %s: line %d - %s\n", - config->filter, lineno, s); - exit (EX_DATAERR); + if (config->filter_opts & FILTER_OPT_TYPE_FNMATCH) { + fe.u.pattern = safestrdup(s); + if (!fe.u.pattern) goto oom; + } else { + + err = regcomp (&fe.u.cpatb, s, cflags); + if (err != 0) { + if (err == REG_ESPACE) goto oom; + fprintf (stderr, + "Bad regex in %s: line %d - %s\n", + config->filter, lineno, s); + exit (EX_DATAERR); + } } if (!sblist_add(fl, &fe)) { oom:; @@ -142,7 +149,10 @@ void filter_destroy (void) if (fl) { for (i = 0; i < sblist_getsize(fl); ++i) { p = sblist_get(fl, i); - regfree (&p->cpatb); + if (config->filter_opts & FILTER_OPT_TYPE_FNMATCH) + safefree(p->u.pattern); + else + regfree (&p->u.cpatb); } sblist_free(fl); } @@ -175,11 +185,14 @@ int filter_run (const char *str) for (i = 0; i < sblist_getsize(fl); ++i) { p = sblist_get(fl, i); - result = - regexec (&p->cpatb, str, (size_t) 0, (regmatch_t *) 0, 0); + if (config->filter_opts & FILTER_OPT_TYPE_FNMATCH) + result = fnmatch (p->u.pattern, str, 0); + else + result = + regexec (&p->u.cpatb, str, (size_t) 0, (regmatch_t *) 0, 0); if (result == 0) { - if (default_policy == FILTER_DEFAULT_ALLOW) + if (!(config->filter_opts & FILTER_OPT_DEFAULT_DENY)) return 1; else return 0; @@ -187,16 +200,8 @@ int filter_run (const char *str) } COMMON_EXIT: - if (default_policy == FILTER_DEFAULT_ALLOW) + if (!(config->filter_opts & FILTER_OPT_DEFAULT_DENY)) return 0; else return 1; } - -/* - * Set the default filtering policy - */ -void filter_set_default_policy (filter_policy_t policy) -{ - default_policy = policy; -} diff --git a/src/filter.h b/src/filter.h index 8a7575bf..e5f34687 100644 --- a/src/filter.h +++ b/src/filter.h @@ -21,16 +21,22 @@ #ifndef _TINYPROXY_FILTER_H_ #define _TINYPROXY_FILTER_H_ -typedef enum { - FILTER_DEFAULT_ALLOW, - FILTER_DEFAULT_DENY -} filter_policy_t; +enum filter_options { + FILTER_OPT_CASESENSITIVE = 1 << 0, + FILTER_OPT_URL = 1 << 1, + FILTER_OPT_DEFAULT_DENY = 1 << 2, + + FILTER_OPT_TYPE_BRE = 1 << 8, + FILTER_OPT_TYPE_ERE = 1 << 9, + FILTER_OPT_TYPE_FNMATCH = 1 << 10, +}; + +#define FILTER_TYPE_MASK \ + (FILTER_OPT_TYPE_BRE | FILTER_OPT_TYPE_ERE | FILTER_OPT_TYPE_FNMATCH) extern void filter_init (void); extern void filter_destroy (void); extern void filter_reload (void); extern int filter_run (const char *str); -extern void filter_set_default_policy (filter_policy_t policy); - #endif diff --git a/src/reqs.c b/src/reqs.c index 50f69144..a6289a4d 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -471,22 +471,16 @@ static struct request_s *process_request (struct conn_s *connptr, * Filter restricted domains/urls */ if (config->filter) { - if (config->filter_url) - ret = filter_run (url); - else - ret = filter_run (request->host); + int fu = config->filter_opts & FILTER_OPT_URL; + ret = filter_run (fu ? url : request->host); if (ret) { update_stats (STAT_DENIED); - if (config->filter_url) - log_message (LOG_NOTICE, - "Proxying refused on filtered url \"%s\"", - url); - else - log_message (LOG_NOTICE, - "Proxying refused on filtered domain \"%s\"", - request->host); + log_message (LOG_NOTICE, + "Proxying refused on filtered %s \"%s\"", + fu ? "url" : "domain", + fu ? url : request->host); indicate_http_error (connptr, 403, "Filtered", "detail", From 7d1e86ccaef27d135c77d2c7a03a15cd957e41d6 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 2 May 2022 14:50:42 +0000 Subject: [PATCH 211/265] don't try to send 408 error to closed client socket read_request_line() is exercised on the client's fd, and it fails when the client closed the connection. therefore it's wrong to send an error message to the client in this situation. additionally, the error message states that the server closed the connection. might fix #383 --- src/reqs.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index a6289a4d..50aaec64 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1622,11 +1622,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) if (read_request_line (connptr) < 0) { update_stats (STAT_BADCONN); - indicate_http_error (connptr, 408, "Timeout", - "detail", - "Server timeout waiting for the HTTP request " - "from the client.", NULL); - HC_FAIL(); + goto done; } /* From 77cd87efef26e0adf277ed8a16b442a818ecec9b Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 27 May 2022 12:48:41 +0000 Subject: [PATCH 212/265] print error message if errorfile is configured but can't be opened also, don't use fopen(NULL) if usage of errorfiles is not configured. --- src/html-error.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/html-error.c b/src/html-error.c index 998a6ee7..ccafc59b 100644 --- a/src/html-error.c +++ b/src/html-error.c @@ -30,6 +30,7 @@ #include "network.h" #include "utils.h" #include "conf.h" +#include "log.h" #include @@ -188,8 +189,12 @@ int send_http_error_message (struct conn_s *connptr) connptr->error_string, add); error_file = get_html_file (connptr->error_number); - if (!(infile = fopen (error_file, "r"))) { - char *detail = lookup_variable (connptr->error_variables, "detail"); + if (!error_file || !(infile = fopen (error_file, "r"))) { + char *detail; + if (error_file) log_message (LOG_ERR, + "Error opening error file '%s' (%s)", + error_file, strerror (errno)); + detail = lookup_variable (connptr->error_variables, "detail"); return (write_message (connptr->client_fd, fallback_error, connptr->error_number, connptr->error_string, From d9e38babb77ce3827835e6983ed3248a0e0c5c16 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 27 May 2022 13:29:04 +0000 Subject: [PATCH 213/265] add gperf generated files to make dist --- src/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile.am b/src/Makefile.am index d132a75f..a7fef4ee 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -67,5 +67,5 @@ conf-tokens-gperf.inc: conf-tokens.gperf $(GPERF) $< > $@ endif -EXTRA_DIST = conf-tokens.gperf +EXTRA_DIST = conf-tokens.gperf conf-tokens-gperf.inc From d6ee3835f0c0466ff58db1c6a6913c554e1c67a7 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 27 May 2022 13:37:49 +0000 Subject: [PATCH 214/265] configure: use release tarball provided gperf file --- configure.ac | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index d904c788..51bbd5d8 100644 --- a/configure.ac +++ b/configure.ac @@ -194,20 +194,21 @@ fi #manpage_support_enabled AM_CONDITIONAL(HAVE_POD2MAN, test "x$POD2MAN" != "x" -a "x$POD2MAN" != "xno") AC_PATH_PROG(GPERF, gperf, no) -AM_CONDITIONAL(HAVE_GPERF, test "x$GPERF" != "x" -a "x$GPERF" != "xno") AH_TEMPLATE([HAVE_GPERF], [Whether you have gperf installed for faster config parsing.]) +tmp_gperf=false if test "x$GPERF" != "x" -a "x$GPERF" != "xno" ; then AS_ECHO_N(["checking whether gperf is recent enough... "]) if "$GPERF" < src/conf-tokens.gperf >/dev/null 2>&1 ; then AS_ECHO("yes") AC_DEFINE(HAVE_GPERF) + tmp_gperf=true else - AM_CONDITIONAL(HAVE_GPERF, false) AS_ECHO("no") fi fi +AM_CONDITIONAL(HAVE_GPERF, $tmp_gperf) AC_CONFIG_FILES([ Makefile @@ -244,3 +245,7 @@ if test "x$POD2MAN" = "xno" ; then touch docs/man8/tinyproxy.8 fi fi + +if test "x$HAVE_GPERF" = "xno" && test -e src/conf-tokens-gperf.inc ; then + touch src/conf-tokens-gperf.inc +fi From ea75e79609391fbe2f57b827871ad9bede33db88 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 27 May 2022 13:12:47 +0000 Subject: [PATCH 215/265] CI: add github workflow to build release tarball --- .github/workflows/release_tarball.yml | 61 +++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 .github/workflows/release_tarball.yml diff --git a/.github/workflows/release_tarball.yml b/.github/workflows/release_tarball.yml new file mode 100644 index 00000000..99ef49e0 --- /dev/null +++ b/.github/workflows/release_tarball.yml @@ -0,0 +1,61 @@ +name: Generate Source Tarball + +# Trigger whenever a release is created +on: + release: + types: + - created + +jobs: + build: + name: build + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v2 + with: + submodules: recursive + + - name: archive + id: archive + run: | + sudo apt install -y gperf + rm -rf .git + autoreconf -i + VERSION=$(cat VERSION) + PKGNAME="tinyproxy-$VERSION" + ./configure + make dist + echo "::set-output name=tarball_xz::${PKGNAME}.tar.xz" + echo "::set-output name=tarball_gz::${PKGNAME}.tar.gz" + echo "::set-output name=tarball_bz2::${PKGNAME}.tar.bz2" + + - name: upload tarball_xz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./${{ steps.archive.outputs.tarball_xz }} + asset_name: ${{ steps.archive.outputs.tarball_xz }} + asset_content_type: application/x-xz + + - name: upload tarball_gz + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./${{ steps.archive.outputs.tarball_gz }} + asset_name: ${{ steps.archive.outputs.tarball_gz }} + asset_content_type: application/x-gzip + + - name: upload tarball_bz2 + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ./${{ steps.archive.outputs.tarball_bz2 }} + asset_name: ${{ steps.archive.outputs.tarball_bz2 }} + asset_content_type: application/x-bzip2 From 90adf28663874e6d34d3eebeae6279906443aea5 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 27 May 2022 14:08:03 +0000 Subject: [PATCH 216/265] release 1.11.1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 1cac385c..720c7384 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.0 +1.11.1 From 8b373f804e210e9ee2b8a17ba852f1b78716827e Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 30 May 2022 19:10:27 +0000 Subject: [PATCH 217/265] update html documentation, add quickstart section --- docs/web/tp.html.head | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/web/tp.html.head b/docs/web/tp.html.head index 6ce2b0b1..351564ce 100644 --- a/docs/web/tp.html.head +++ b/docs/web/tp.html.head @@ -41,25 +41,27 @@

Tinyproxy requires only a minimal POSIX environment to build and operate. It can use additional libraries to add functionality though.

-

Tinyproxy allows forwarding of HTTPS connections without modifying traffic in any way through the CONNECT method (see the ConnectPort directive).

+

Tinyproxy allows forwarding of HTTPS connections without modifying traffic in any way through the CONNECT method (see the ConnectPort directive, which you should disable, unless you want to restrict the users).

Tinyproxy supports being configured as a transparent proxy, so that a proxy can be used without requiring any client-side configuration. You can also use it as a reverse proxy front-end to your websites.

-

Using the AddHeader directive, you can add/insert HTTP headers to outgoing traffic.

+

Using the AddHeader directive, you can add/insert HTTP headers to outgoing traffic (HTTP only).

If you're looking to build a custom web proxy, Tinyproxy is easy to modify to your custom needs. The source is straightforward, adhering to the KISS principle. As such, it can be used as a foundation for anything you may need a web proxy to do.

-

Tinyproxy has privacy features which can let you configure which HTTP headers should be allowed through, and which should be blocked. This allows you to restrict both what data comes to your web browser from the HTTP server (e.g., cookies), and to restrict what data is allowed through from your web browser to the HTTP server (e.g., version information).

+

Tinyproxy has privacy features which can let you configure which HTTP headers should be allowed through, and which should be blocked. This allows you to restrict both what data comes to your web browser from the HTTP server (e.g., cookies), and to restrict what data is allowed through from your web browser to the HTTP server (e.g., version information). Note that these features do not affect HTTPS connections.

Using the remote monitoring facility, you can access proxy statistics from afar, letting you know exactly how busy the proxy is.

You can configure Tinyproxy to control access by only allowing requests from a certain subnet, or from a certain interface, thus ensuring that random, unauthorized people will not be using your proxy.

-

With a bit of configuration (specifically, making Tinyproxy created files owned by a non-root user and running it on a port greater than 1024), Tinyproxy can be made to run without any special privileges, thus minimizing the chance of system compromise. Furthermore, it was designed with an eye towards preventing buffer overflows. The simplicity of the code ensures it remains easy to spot such bugs.

+

With a bit of configuration (specifically, making Tinyproxy created files owned by a non-root user and running it on a port greater than 1024), Tinyproxy can be made to run without any special privileges, thus minimizing the chance of system compromise. In fact, it is recommended to run it as a regular/restricted user. Furthermore, it was designed with an eye towards preventing buffer overflows. The simplicity of the code ensures it remains easy to spot such bugs.

Downloads

+

Note that many distributions ship horribly outdated versions of tinyproxy, therefore it is recommended to compile it from source.

+
  • On Red Hat Enterprise Linux, or its derivatives such as CentOS, install Tinyproxy from the EPEL repository by running yum install tinyproxy.
  • On Fedora, install Tinyproxy by running yum install tinyproxy.
  • @@ -70,7 +72,7 @@
  • Mac OS X users can check MacPorts to see if the Tinyproxy port there is recent enough.
-

If you feel that the Tinyproxy binary package in your operating system is not recent, please contact the package maintainer for that particular operating system. If this fails, you can always compile the latest stable version from source code.

+

If you feel that the Tinyproxy binary package in your operating system is not recent (likely), please contact the package maintainer for that particular operating system. If this fails, you can always compile the latest stable, or even better, the latest git master version, from source code.

We distribute Tinyproxy in source code form, and it has to be compiled in order to be used on your system. Please see the INSTALL file in the source code tree for build instructions. The current stable version of Tinyproxy is available on the releases page. The Tinyproxy NEWS file contains the release notes. You can verify the tarball using its PGP signature. You can also browse the older releases of Tinyproxy.

@@ -78,5 +80,19 @@

git clone https://github.com/tinyproxy/tinyproxy.git

+

+Quickstart

+ +

The quickest way to get started is using a minimal config file like the below:

+ +

+Port 8888
+Listen 127.0.0.1
+Timeout 600
+Allow 127.0.0.1
+
+ +

And then simply run tinyproxy -d -c tinyproxy.conf as your current user. This starts tinyproxy in foreground mode with tinyproxy.conf as its config, while logging to stdout. Now, all programs supporting a HTTP proxy can use 127.0.0.1:8888 as a proxy. You can try it out using http_proxy=127.0.0.1:8888 curl example.com.

+

Documentation

From 121be4a74e1c917f49d54a0cd34f7e20fb58123d Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 15 Jul 2022 23:43:13 +0000 Subject: [PATCH 218/265] echo http protocol version on CONNECT request response while at it, the function doing it was renamed from the misleading ssl name to what it actually does. also inlined the strings that were previously defined as macros. addressing #152 --- src/reqs.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 50aaec64..fcc0dbf2 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -301,21 +301,16 @@ establish_http_connection (struct conn_s *connptr, struct request_s *request) } /* - * These two defines are for the SSL tunnelling. + * Send the appropriate response to the client to establish a + * connection via CONNECT method. */ -#define SSL_CONNECTION_RESPONSE "HTTP/1.0 200 Connection established" -#define PROXY_AGENT "Proxy-agent: " PACKAGE "/" VERSION - -/* - * Send the appropriate response to the client to establish a SSL - * connection. - */ -static int send_ssl_response (struct conn_s *connptr) +static int send_connect_method_response (struct conn_s *connptr) { return write_message (connptr->client_fd, - "%s\r\n" - "%s\r\n" - "\r\n", SSL_CONNECTION_RESPONSE, PROXY_AGENT); + "HTTP/1.%u 200 Connection established\r\n" + "Proxy-agent: " PACKAGE "/" VERSION "\r\n" + "\r\n", connptr->protocol.major != 1 ? 0 : + connptr->protocol.minor); } /* @@ -1762,10 +1757,10 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) HC_FAIL(); } } else { - if (send_ssl_response (connptr) < 0) { + if (send_connect_method_response (connptr) < 0) { log_message (LOG_ERR, - "handle_connection: Could not send SSL greeting " - "to client."); + "handle_connection: Could not send CONNECT" + " method greeting to client."); update_stats (STAT_BADCONN); HC_FAIL(); } From 84f203fb1c4733608c7283bbe794005a469c4b00 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 20 Aug 2022 14:43:24 +0000 Subject: [PATCH 219/265] fix reversepath directive using https url giving misleading error it's not possible to use a https url in a ReversePath directive, without removing the security provided by https, and would require adding a dependency on a TLS library like openssl and a lot of code complexity to fetch the requested resource via https and relay it back to the client. in case the reversepath directive kicked in, but the protocol wasn't recognized, and support for transparent proxying built-in, the code wrongfully tried to turn the request into a trans request, leading to a bogus rewritten url like http://localhost:8888https://www.endpoint.com and an error message that we're trying to connect to the machine the proxy runs on. now instead use the generic code that signals an invalid protocol/url was used. closes #419 --- src/reqs.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index fcc0dbf2..bce69819 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -322,9 +322,11 @@ static struct request_s *process_request (struct conn_s *connptr, { char *url; struct request_s *request; - int ret; + int ret, skip_trans; size_t request_len; + skip_trans = 0; + /* NULL out all the fields so frees don't cause segfaults. */ request = (struct request_s *) safecalloc (1, sizeof (struct request_s)); @@ -397,6 +399,7 @@ static struct request_s *process_request (struct conn_s *connptr, } safefree (url); url = reverse_url; + skip_trans = 1; } else if (config->reverseonly) { log_message (LOG_ERR, "Bad request, no mapping for '%s' found", @@ -446,11 +449,13 @@ static struct request_s *process_request (struct conn_s *connptr, connptr->connect_method = TRUE; } else { #ifdef TRANSPARENT_PROXY - if (!do_transparent_proxy - (connptr, hashofheaders, request, config, &url)) { - goto fail; - } -#else + if (!skip_trans) { + if (!do_transparent_proxy + (connptr, hashofheaders, request, config, &url)) + goto fail; + } else +#endif + { indicate_http_error (connptr, 501, "Not Implemented", "detail", "Unknown method or unsupported protocol.", @@ -458,7 +463,7 @@ static struct request_s *process_request (struct conn_s *connptr, log_message (LOG_INFO, "Unknown method (%s) or protocol (%s)", request->method, url); goto fail; -#endif + } } #ifdef FILTER_ENABLE From 3764b8551463b900b5b4e3ec0cd9bb9182191cb7 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 8 Sep 2022 15:18:04 +0000 Subject: [PATCH 220/265] prevent junk from showing up in error page in invalid requests fixes #457 --- src/reqs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/reqs.c b/src/reqs.c index bce69819..45db118d 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -343,8 +343,12 @@ static struct request_s *process_request (struct conn_s *connptr, goto fail; } + /* zero-terminate the strings so they don't contain junk in error page */ + request->method[0] = url[0] = request->protocol[0] = 0; + ret = sscanf (connptr->request_line, "%[^ ] %[^ ] %[^ ]", request->method, url, request->protocol); + if (ret == 2 && !strcasecmp (request->method, "GET")) { request->protocol[0] = 0; From 6ffd9af2c7b88a43be8865cc8e1d450643786b1d Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 1 Feb 2023 13:16:58 +0000 Subject: [PATCH 221/265] hsearch: fix potential UB (pointer arithmetics on nullptr) closes #471 addresses #470 --- src/hsearch.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/hsearch.c b/src/hsearch.c index be0434c2..dfe1404a 100644 --- a/src/hsearch.c +++ b/src/hsearch.c @@ -80,9 +80,10 @@ static int resize(struct htab *htab, size_t nel) { size_t newsize; size_t i, j; + size_t oldmask = htab->mask; struct elem *e, *newe; struct elem *oldtab = htab->elems; - struct elem *oldend = htab->elems + htab->mask + 1; + struct elem *oldend; if (nel > MAXSIZE) nel = MAXSIZE; @@ -95,6 +96,8 @@ static int resize(struct htab *htab, size_t nel) htab->mask = newsize - 1; if (!oldtab) return 1; + + oldend = oldtab + oldmask + 1; for (e = oldtab; e < oldend; e++) if (e->item.key) { for (i=e->hash,j=1; ; i+=j++) { From 470cc0863d276c339db0e84b64055523823c2587 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 1 Feb 2023 13:32:45 +0000 Subject: [PATCH 222/265] conf: fix potential crash with invalid input data closes #474 --- src/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index 7f94b654..257cafe3 100644 --- a/src/conf.c +++ b/src/conf.c @@ -427,7 +427,7 @@ static int config_parse (struct config_s *conf, FILE * f) while(isspace(*p))p++; if(!*p) continue; q = p; - while(!isspace(*q))q++; + while(*q && !isspace(*q))q++; c = *q; *q = 0; e = config_directive_find(p, strlen(p)); From 31339cb1615765f2fbb4ec7779400535bad6da46 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 1 Feb 2023 15:46:58 +0000 Subject: [PATCH 223/265] tinyproxy.conf.5: update text for bind directive the existing text was sort of misleading as it was written in a pre-HTTPS era. addressing #475 --- docs/man5/tinyproxy.conf.txt.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 1e1ee3ef..23f72a47 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -57,7 +57,7 @@ only on one specific address. =item B This allows you to specify which address Tinyproxy will bind -to for outgoing connections to web servers or upstream proxies. +to for outgoing connections. This parameter may be specified multiple times, then Tinyproxy will try all the specified addresses in order. From ef60434b392f9efe85510a1086be5ea1e24fd2a1 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Mon, 20 Mar 2023 20:35:58 +0000 Subject: [PATCH 224/265] docs: typo fix closes #487 --- etc/tinyproxy.conf.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index d2687093..d9598d3e 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -3,7 +3,7 @@ ## ## This example tinyproxy.conf file contains example settings ## with explanations in comments. For decriptions of all -## parameters, see the tinproxy.conf(5) manual page. +## parameters, see the tinyproxy.conf(5) manual page. ## # From 2bec15ee40dd26920e4f275acd3a7c84a1b6012c Mon Sep 17 00:00:00 2001 From: Mario-Klebsch <62026480+Mario-Klebsch@users.noreply.github.com> Date: Tue, 23 May 2023 15:04:48 +0200 Subject: [PATCH 225/265] Allow configuring IPv6 address for upstream proxy (#492) * Added support to configure IPv6 upstream proxy servers using bracket syntax. * Added regular expression for IPv6 scope identifier to re for IPv6 address. --- src/conf.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/conf.c b/src/conf.c index 257cafe3..002d2ff9 100644 --- a/src/conf.c +++ b/src/conf.c @@ -66,9 +66,10 @@ #define PASSWORD "([^@]*)" #define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" #define IPMASK "(" IP "(/" DIGIT "+)?)" +#define IPV6SCOPE "((%[^ \t\\/]{1,16})?)" #define IPV6 "(" \ - "(([0-9a-f:]{2,39}))|" \ - "(([0-9a-f:]{0,29}:" IP "))" \ + "([0-9a-f:]{2,39})" IPV6SCOPE "|" \ + "([0-9a-f:]{0,29}:" IP ")" IPV6SCOPE \ ")" #define IPV6MASK "(" IPV6 "(/" DIGIT "+)?)" @@ -80,7 +81,7 @@ * number. Given the usual structure of the configuration file, sixteen * substring matches should be plenty. */ -#define RE_MAX_MATCHES 24 +#define RE_MAX_MATCHES 33 #define CP_WARN(FMT, ...) \ log_message (LOG_WARNING, "line %lu: " FMT, lineno, __VA_ARGS__) @@ -249,7 +250,7 @@ struct { "(" "(none)" WS STR ")|" \ "(" "(http|socks4|socks5)" WS \ "(" USERNAME /*username*/ ":" PASSWORD /*password*/ "@" ")?" - "(" IP "|" ALNUM ")" + "(" IP "|" "\\[(" IPV6 ")\\]" "|" ALNUM ")" ":" INT "(" WS STR ")?" ")", handle_upstream), #endif /* loglevel */ @@ -1114,10 +1115,13 @@ static HANDLE_FUNC (handle_upstream) pass = get_string_arg (line, &match[mi]); mi++; - ip = get_string_arg (line, &match[mi]); + if (match[mi+4].rm_so != -1) /* IPv6 address in square brackets */ + ip = get_string_arg (line, &match[mi+4]); + else + ip = get_string_arg (line, &match[mi]); if (!ip) return -1; - mi += 5; + mi += 16; port = (int) get_long_arg (line, &match[mi]); mi += 3; From 1e615e66a9944d3169f5cbba62a85c125693b7cf Mon Sep 17 00:00:00 2001 From: ivanwick Date: Wed, 24 May 2023 07:06:15 -0700 Subject: [PATCH 226/265] tinyproxy.conf.5: document config strings that require double quotes (#493) * tinyproxy.conf.5: document config strings that require double quotes String config values matched by the STR regex must be enclosed in double quotes Edit descriptions for brevity conf.c: move boolean arguments comment before BOOL group addresses #491 * Revert conf.c: move boolean arguments comment before BOOL group --- docs/man5/tinyproxy.conf.txt.in | 47 +++++++++++++++++---------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 23f72a47..28f4ab9f 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -22,8 +22,8 @@ configuration file. The Tinyproxy configuration file contains key-value pairs, one per line. Lines starting with `#` and empty lines are comments and are ignored. Keywords are case-insensitive, whereas values are -case-sensitive. Values may be enclosed in double-quotes (") if they -contain spaces. +case-sensitive. Some string values must be enclosed in double +quotes (") as noted below. The possible keywords and their descriptions are as follows: @@ -76,29 +76,29 @@ allowed to have before it is closed by Tinyproxy. This parameter controls which HTML file Tinyproxy returns when a given HTTP error occurs. It takes two arguments, the error number -and the location of the HTML error file. +and the location of the HTML error file. Enclose the file location +in double quotes. =item B -This parameter controls the HTML template file returned when an -error occurs for which no specific error file has been set. +The HTML template file returned when an error occurs for which no +specific error file has been set. Enclose in double quotes. =item B -This configures the host name or IP address that is treated -as the `stat host`: Whenever a request for this host is received, -Tinyproxy will return an internal statistics page instead of -forwarding the request to that host. The template for this -page can be configured with the `StatFile` configuration option. -The default value of `StatHost` is `@TINYPROXY_STATHOST@`. +The host name or IP address that is treated as the `stat host`. +Enclose in double quotes. Whenever Tinyproxy receives a request for +the `stat host` it returns an internal statistics page instead of +forwarding the request to that host. The template for this page can be +configured with the `StatFile` configuration option. The default value +of `StatHost` is `@TINYPROXY_STATHOST@`. =item B -This configures the HTML file that Tinyproxy sends when -a request for the stathost is received. If this parameter is -not set, Tinyproxy returns a hard-coded basic statistics page. -See the STATHOST section in the L manual page -for details. +The HTML file that Tinyproxy sends in response to a request for the +`stat host`. Enclose in double quotes. If this parameter is not set, +Tinyproxy returns a hard-coded basic statistics page. See the STATHOST +section in the L manual page for details. Note that the StatFile and the error files configured with ErrorFile and DefaultErrorFile are template files that can contain a few @@ -109,9 +109,9 @@ manual page contains a description of all template variables. =item B -This controls the location of the file to which Tinyproxy -writes its debug output. Alternatively, Tinyproxy can log -to syslog -- see the Syslog option. +The location of the file to which Tinyproxy writes its debug output. +Enclose in double quotes. Alternatively, Tinyproxy can log to syslog +-- see the Syslog option. =item B @@ -144,8 +144,8 @@ and below would be suppressed. Allowed values are: =item B -This option controls the location of the file where the main -Tinyproxy process stores its process ID for signaling purposes. +The location of the file where the main Tinyproxy process stores its +process ID for signaling purposes. Enclose in double quotes. =item B @@ -250,7 +250,8 @@ RFC 2616 requires proxies to add a `Via` header to the HTTP requests, but using the real host name can be a security concern. If the `ViaProxyname` option is present, then its string value will be used as the host name in the Via header. -Otherwise, the server's host name will be used. +Otherwise, the server's host name will be used. Enclose in double +quotes. =item B @@ -344,7 +345,7 @@ If an `Anonymous` keyword is present, then anonymous proxying is enabled. The headers listed with `Anonymous` are allowed through, while all others are denied. If no Anonymous keyword is present, then all headers are allowed through. You must -include quotes around the headers. +include double quotes around the headers. Most sites require cookies to be enabled for them to work correctly, so you will need to allow cookies through if you access those sites. From d7c20e663f3f85238665fed52b0bb35bcc3fc942 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 25 May 2023 19:42:02 +0000 Subject: [PATCH 227/265] tinyproxy.conf.5: document syntax for upstream IPv6 addresses follow-up to 2bec15ee40dd26920e4f275acd3a7c84a1b6012c --- docs/man5/tinyproxy.conf.txt.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/man5/tinyproxy.conf.txt.in b/docs/man5/tinyproxy.conf.txt.in index 28f4ab9f..ed137e2b 100644 --- a/docs/man5/tinyproxy.conf.txt.in +++ b/docs/man5/tinyproxy.conf.txt.in @@ -179,6 +179,10 @@ connection is done directly. =back +It's recommended to use raw IP addresses to specify the upstream host, so +no costly DNS lookup has to be done everytime it is used. +IPv6 addresses need to be enclosed in square brackets. + The site can be specified in various forms as a hostname, domain name or as an IP range: From 2935519eb7f0d1d740fc1c89a3dc2279c9c80208 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 7 Jun 2023 18:57:05 +0000 Subject: [PATCH 228/265] fix omission to reset socklen parameter for accept() since accept() uses the socklen parameter as in/out, after processing an IPv4 the socklen fed to it waiting for the next client was only the length of sockaddr_in, so if a connection from an IPv6 came in the client sockaddr was only partially filled in. this caused wrongly printed ipv6 addresses in log, and failure to match them correctly against the acl. closes #495 --- src/child.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/child.c b/src/child.c index 985357da..8bae89b2 100644 --- a/src/child.c +++ b/src/child.c @@ -81,7 +81,7 @@ void child_main_loop (void) int connfd; union sockaddr_union cliaddr_storage; struct sockaddr *cliaddr = (void*) &cliaddr_storage; - socklen_t clilen = sizeof(cliaddr_storage); + socklen_t clilen; int nfds = sblist_getsize(listen_fds); pollfd_struct *fds = safecalloc(nfds, sizeof *fds); ssize_t i; @@ -167,6 +167,7 @@ void child_main_loop (void) * Continue handling this connection. */ + clilen = sizeof(cliaddr_storage); connfd = accept (listenfd, cliaddr, &clilen); From 1289d8afc8b50fb95cbfee37d3d394e119fe4832 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 13 Oct 2023 19:54:26 +0000 Subject: [PATCH 229/265] conf: use case-independent match for Filtertype parameter --- src/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index 002d2ff9..01162e67 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1009,7 +1009,7 @@ static HANDLE_FUNC (handle_filtertype) if (!type) return -1; for(i=0;ifilter_opts |= ftmap[i].flag; safefree (type); From c83407396852e2300940c9b3da4d57841e256ede Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 15 Oct 2023 10:50:48 +0000 Subject: [PATCH 230/265] fix CI by running apt update --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 43541078..f077b192 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,8 +27,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: install valgrind - run: sudo apt-get install --assume-yes valgrind + - run: sudo apt update + - run: sudo apt install --assume-yes valgrind - run: ./autogen.sh - run: ./configure --enable-debug --enable-transparent --enable-reverse - run: make From 84285b640de76508e4deddbc6cbad751628769ae Mon Sep 17 00:00:00 2001 From: Victor Kislov Date: Thu, 2 Nov 2023 21:24:42 +0200 Subject: [PATCH 231/265] BasicAuth: Accept special chars in username and password (#516) Co-authored-by: Victor Kislov --- src/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index 01162e67..4b5f33a8 100644 --- a/src/conf.c +++ b/src/conf.c @@ -225,7 +225,7 @@ struct { handle_deny), STDCONF (bind, "(" IP "|" IPV6 ")", handle_bind), /* other */ - STDCONF (basicauth, ALNUM WS ALNUM, handle_basicauth), + STDCONF (basicauth, USERNAME WS PASSWORD, handle_basicauth), STDCONF (errorfile, INT WS STR, handle_errorfile), STDCONF (addheader, STR WS STR, handle_addheader), From c4df45b7e416dc1a26bb4e4511e1e7de08fd49af Mon Sep 17 00:00:00 2001 From: strongleong Date: Tue, 7 Nov 2023 13:55:01 +1100 Subject: [PATCH 232/265] BasicAuth: Added logging for failed login attemps closes #514 --- src/reqs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/reqs.c b/src/reqs.c index 45db118d..58c97a88 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -1688,6 +1688,10 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) if(failure) { e401: update_stats (STAT_DENIED); + log_message (LOG_INFO, + "Failed auth attempt (file descriptor: %d), ip %s", + connptr->client_fd, + connptr->client_ip_addr); indicate_http_error (connptr, 401, "Unauthorized", "detail", "The administrator of this proxy has not configured " From 92289d5a4c1bc53fa19fcf4dcc06e3e633134edb Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 1 May 2024 23:48:37 +0000 Subject: [PATCH 233/265] main: print filename of config file used on (re)load --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 7ea55408..268255f1 100644 --- a/src/main.c +++ b/src/main.c @@ -257,7 +257,7 @@ int reload_config (int reload_logging) int ret, ret2; struct config_s *c_next = get_next_config(); - log_message (LOG_NOTICE, "Reloading config file"); + log_message (LOG_NOTICE, "Reloading config file (%s)", config_file); if (reload_logging) shutdown_logging (); From 12a8484265f7b00591293da492bb3c9987001956 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 5 May 2024 10:37:29 +0000 Subject: [PATCH 234/265] fix potential UAF in header handling (CVE-2023-49606) https://talosintelligence.com/vulnerability_reports/TALOS-2023-1889 this bug was brought to my attention today by the debian tinyproxy package maintainer. the above link states that the issue was known since last year and that maintainers have been contacted, but if that is even true then it probably was done via a private email to a potentially outdated email address of one of the maintainers, not through the channels described clearly on the tinyproxy homepage: > Feel free to report a new bug or suggest features via github issues. > Tinyproxy developers hang out in #tinyproxy on irc.libera.chat. no github issue was filed, and nobody mentioned a vulnerability on the mentioned IRC chat. if the issue had been reported on github or IRC, the bug would have been fixed within a day. --- src/reqs.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index 58c97a88..a65ed54d 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -779,7 +779,7 @@ static int remove_connection_headers (orderedmap hashofheaders) char *data; char *ptr; ssize_t len; - int i; + int i,j,df; for (i = 0; i != (sizeof (headers) / sizeof (char *)); ++i) { /* Look for the connection header. If it's not found, return. */ @@ -804,7 +804,12 @@ static int remove_connection_headers (orderedmap hashofheaders) */ ptr = data; while (ptr < data + len) { - orderedmap_remove (hashofheaders, ptr); + df = 0; + /* check that ptr isn't one of headers to prevent + double-free (CVE-2023-49606) */ + for (j = 0; j != (sizeof (headers) / sizeof (char *)); ++j) + if(!strcasecmp(ptr, headers[j])) df = 1; + if (!df) orderedmap_remove (hashofheaders, ptr); /* Advance ptr to the next token */ ptr += strlen (ptr) + 1; From e69788b761dd6dad99facebe094a86009a0c1fe1 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 5 May 2024 20:56:17 +0200 Subject: [PATCH 235/265] Add SECURITY.md given the catastrophic way TALOS Intelligence "communicated" with upstream (i.e. by probably sending a single mail to an unused email address), it's probably best to explicitly document how to approach upstream when a security issue is discovered. --- SECURITY.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..93ef8148 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,28 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +| --------- | ------------------ | +| 1.11.x | :white_check_mark: | +| <= 1.10.x | :x: | + +## Reporting a Vulnerability + +Open a public issue on github. The issue will most likely be fixed +within a day, unless all maintainers happen to just be taking a +vacation at the same time, which is unlikely. + +Even then, having the bug publicly known will allow competent people +to come up with custom patches for distros, most likely quicker +than black hats can craft a remote execution exploit. + +If you really really do not want to make the issue public, come +to the tinyproxy IRC channel and ask for a maintainer, which you +can then contact via private messages. + +Do not, however, like ["TALOS Intelligence"](https://talosintelligence.com/vulnerability_reports/TALOS-2023-1889) +pull a random email address out of git log, then send an email +nobody reads or responds to, and wait for 6 months for publication. +this only gives black hats plenty time to sell, use and circulate +zero days and get the best possible ROI. From dd49e975a04a66c2a32e6d2fc7cd7ddf0cb9fe33 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Wed, 8 May 2024 18:22:52 +0000 Subject: [PATCH 236/265] release 1.11.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 720c7384..ca717669 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.1 +1.11.2 From 942d0c6b03673ad816c42176422d7fe691143064 Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Sun, 2 Jun 2024 18:52:59 +0400 Subject: [PATCH 237/265] Use appropriate installation path variables --- configure.ac | 4 +++- docs/man8/Makefile.am | 11 +++++++++++ docs/man8/tinyproxy.txt.in | 6 +++++- etc/Makefile.am | 1 + etc/tinyproxy.conf.in | 5 +---- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 51bbd5d8..37e7d276 100644 --- a/configure.ac +++ b/configure.ac @@ -173,6 +173,9 @@ fi dnl dnl Substitute the variables into the various Makefiles dnl +# runstatedir isn't available for Autoconf < 2.70 +AS_IF([test -z "${runstatedir}"], [runstatedir='${localstatedir}/run']) +AC_SUBST([runstatedir]) AC_SUBST(CFLAGS) AC_SUBST(LDFLAGS) AC_SUBST(CPPFLAGS) @@ -220,7 +223,6 @@ docs/Makefile docs/man5/Makefile docs/man5/tinyproxy.conf.txt docs/man8/Makefile -docs/man8/tinyproxy.txt m4macros/Makefile tests/Makefile tests/scripts/Makefile diff --git a/docs/man8/Makefile.am b/docs/man8/Makefile.am index d2d7e19b..17281cd3 100644 --- a/docs/man8/Makefile.am +++ b/docs/man8/Makefile.am @@ -9,6 +9,17 @@ M_NAME=TINYPROXY man_MANS = \ $(MAN8_FILES:.txt=.8) +edit = sed \ + -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@runstatedir[@]|$(runstatedir)|g' \ + -e 's|@sysconfdir[@]|$(sysconfdir)|g' \ + -e 's|@TINYPROXY_STATHOST[@]|$(TINYPROXY_STATHOST)|g' + +tinyproxy.txt: $(top_srcdir)/docs/man8/tinyproxy.txt.in Makefile + @rm -f $@ $@.tmp + $(AM_V_GEN) $(edit) $(top_srcdir)/docs/man8/$@.in > $@.tmp + @mv $@.tmp $@ + .txt.8: if HAVE_POD2MAN $(AM_V_GEN) $(POD2MAN) --center="Tinyproxy manual" \ diff --git a/docs/man8/tinyproxy.txt.in b/docs/man8/tinyproxy.txt.in index 7fa420f6..9cf2d426 100644 --- a/docs/man8/tinyproxy.txt.in +++ b/docs/man8/tinyproxy.txt.in @@ -156,7 +156,11 @@ configuration variable `StatFile`. =head1 FILES -`/etc/tinyproxy/tinyproxy.conf`, `/var/run/tinyproxy/tinyproxy.pid`, `/var/log/tinyproxy/tinyproxy.log` +F<@sysconfdir@/tinyproxy/tinyproxy.conf> + +F<@runstatedir@/tinyproxy/tinyproxy.pid> + +F<@localstatedir@/log/tinyproxy/tinyproxy.log> =head1 BUGS diff --git a/etc/Makefile.am b/etc/Makefile.am index 57a5c010..045baac3 100644 --- a/etc/Makefile.am +++ b/etc/Makefile.am @@ -12,6 +12,7 @@ edit = sed \ -e 's|@datarootdir[@]|$(datarootdir)|g' \ -e 's|@pkgsysconfdir[@]|$(pkgsysconfdir)|g' \ -e 's|@localstatedir[@]|$(localstatedir)|g' \ + -e 's|@runstatedir[@]|$(runstatedir)|g' \ -e 's|@pkgdatadir[@]|$(pkgdatadir)|g' \ -e 's|@prefix[@]|$(prefix)|g' \ -e 's|@TINYPROXY_STATHOST[@]|$(TINYPROXY_STATHOST)|g' diff --git a/etc/tinyproxy.conf.in b/etc/tinyproxy.conf.in index d9598d3e..af91d039 100644 --- a/etc/tinyproxy.conf.in +++ b/etc/tinyproxy.conf.in @@ -124,7 +124,7 @@ LogLevel Info # can be used for signalling purposes. # If not specified, no pidfile will be written. # -#PidFile "@localstatedir@/run/tinyproxy/tinyproxy.pid" +#PidFile "@runstatedir@/tinyproxy/tinyproxy.pid" # # XTinyproxy: Tell Tinyproxy to include the X-Tinyproxy header, which @@ -320,6 +320,3 @@ ViaProxyName "tinyproxy" # If not set then no rewriting occurs. # #ReverseBaseURL "http://localhost:8888/" - - - From 72b93f6d4b598a1f809f4e5ff383757c52fa9765 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 16 Jun 2024 12:02:26 +0000 Subject: [PATCH 238/265] CI: update release workflow to non-deprecated actions github continues to deprecate actions and idioms in their CI system. hopefully these changes will last for a while and maintaining a simple CI task doesn't turn into a neverending story. --- .github/workflows/release_tarball.yml | 41 +++++++-------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/.github/workflows/release_tarball.yml b/.github/workflows/release_tarball.yml index 99ef49e0..7999f179 100644 --- a/.github/workflows/release_tarball.yml +++ b/.github/workflows/release_tarball.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive @@ -26,36 +26,15 @@ jobs: PKGNAME="tinyproxy-$VERSION" ./configure make dist - echo "::set-output name=tarball_xz::${PKGNAME}.tar.xz" - echo "::set-output name=tarball_gz::${PKGNAME}.tar.gz" - echo "::set-output name=tarball_bz2::${PKGNAME}.tar.bz2" + echo "tarball_xz=${PKGNAME}.tar.xz" >> "$GITHUB_OUTPUT" + echo "tarball_gz=${PKGNAME}.tar.gz" >> "$GITHUB_OUTPUT" + echo "tarball_bz2=${PKGNAME}.tar.bz2" >> "$GITHUB_OUTPUT" - - name: upload tarball_xz - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: upload tarballs + uses: softprops/action-gh-release@v2 with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ./${{ steps.archive.outputs.tarball_xz }} - asset_name: ${{ steps.archive.outputs.tarball_xz }} - asset_content_type: application/x-xz + files: | + ${{ steps.archive.outputs.tarball_xz }} + ${{ steps.archive.outputs.tarball_gz }} + ${{ steps.archive.outputs.tarball_bz2 }} - - name: upload tarball_gz - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ./${{ steps.archive.outputs.tarball_gz }} - asset_name: ${{ steps.archive.outputs.tarball_gz }} - asset_content_type: application/x-gzip - - - name: upload tarball_bz2 - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ github.event.release.upload_url }} - asset_path: ./${{ steps.archive.outputs.tarball_bz2 }} - asset_name: ${{ steps.archive.outputs.tarball_bz2 }} - asset_content_type: application/x-bzip2 From d652ed85386675c4f59b5b511cb059a084d18f6d Mon Sep 17 00:00:00 2001 From: James McKinney <26463+jpmckinney@users.noreply.github.com> Date: Thu, 20 Jun 2024 04:51:29 -0400 Subject: [PATCH 239/265] Omit the version number from headers and HTML responses (#543) Omit the version number from headers, HTML responses, and templates --- data/templates/debug.html | 5 +---- data/templates/default.html | 2 +- data/templates/stats.html | 4 ++-- src/html-error.c | 8 ++++---- src/reqs.c | 11 +++++------ src/stats.c | 10 +++++----- src/utils.c | 2 +- tests/scripts/webclient.pl | 3 +-- tests/scripts/webserver.pl | 3 +-- 9 files changed, 21 insertions(+), 27 deletions(-) diff --git a/data/templates/debug.html b/data/templates/debug.html index 6ee33674..0e7f0549 100644 --- a/data/templates/debug.html +++ b/data/templates/debug.html @@ -30,9 +30,6 @@

{cause}

clienthost
{clienthost}
-
version
-
{version}
-
package
{package}
@@ -49,7 +46,7 @@

{cause}


-

Generated by {package} version {version}.

+

Generated by {package}.

diff --git a/data/templates/default.html b/data/templates/default.html index 67354b7a..8a9c8f6c 100644 --- a/data/templates/default.html +++ b/data/templates/default.html @@ -16,7 +16,7 @@

{cause}


-

Generated by {package} version {version}.

+

Generated by {package}.

diff --git a/data/templates/stats.html b/data/templates/stats.html index a8c3e074..f039c970 100644 --- a/data/templates/stats.html +++ b/data/templates/stats.html @@ -2,7 +2,7 @@ - Stats [{package} v{version}] + Stats [{package}]