From dc41b3533326d745c65afaea4cb34c229a31d0c3 Mon Sep 17 00:00:00 2001 From: Vasily Date: Fri, 23 Nov 2018 17:59:03 +0300 Subject: [PATCH 01/62] 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 5a87c68..5ebf179 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 02/62] 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 | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/child.c b/src/child.c index effb2ae..60f8ead 100644 --- a/src/child.c +++ b/src/child.c @@ -206,27 +206,27 @@ static void child_main (struct child_s *ptr) * We have to wait for connections on multiple fds, * so use select. */ - - FD_ZERO(&rfds); - - 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)); - exit(1); - } - - FD_SET(*fd, &rfds); - maxfd = max(maxfd, *fd); - } - while (!config.quit) { + int listenfd = -1; + FD_ZERO(&rfds); + + 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)); + exit(1); + } + + FD_SET(*fd, &rfds); + maxfd = max(maxfd, *fd); + } + ptr->status = T_WAITING; clilen = sizeof(struct sockaddr_storage); From e666e4a35b07a406437e4c58a15d81adf7cb5fd7 Mon Sep 17 00:00:00 2001 From: Janosch Hoffmann Date: Sun, 5 May 2019 20:13:38 +0200 Subject: [PATCH 03/62] 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 3164191..e18132e 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 04/62] 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 625a586..ee3c987 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 bbdcc74..8450cff 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 05/62] 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 54024cb..f1b8817 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 06/62] 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 eef606a..59c2fa8 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 07/62] start work on 1.11.x --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 81c871d..1cac385 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 08/62] 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 b3b94ec..afd3b6b 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 f1b8817..06f5480 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 af2f621..3924909 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 60f8ead..06a977e 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; +struct client { + union sockaddr_union addr; + int fd; +}; -static struct child_config_s { - unsigned int maxclients, maxrequestsperchild; - unsigned int maxspareservers, minspareservers, startservers; -} child_config; +struct child { + pthread_t thread; + struct client client; + volatile int done; +}; -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 */ - -/* - * 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; - -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) +static sblist *childs; + +static void collect_threads(void) { - int rc; - - while ((rc = fcntl (lock_fd, F_SETLKW, &lock_it)) < 0) { - if (errno == EINTR) - continue; - else - return; - } -} - -static void _child_lock_release (void) -{ - if (fcntl (lock_fd, F_SETLKW, &unlock_it) < 0) - return; -} - -/* 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. - */ -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 */ - } + 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++; + } } /* - * This is the main (per child) loop. + * This is the main loop accepting new connections. */ -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; - } + 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_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 (); + child->done = 0; - break; - } else { - SERVER_COUNT_UNLOCK (); + if (!sblist_add(childs, &child)) { + free(child); + goto oom; } - SERVER_INC (); - } + child->client.fd = connfd; + memcpy(&child->client.addr, &cliaddr_storage, sizeof(cliaddr_storage)); - 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; - } - - 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 (); - } - } - - log_message (LOG_INFO, "Finished creating all children."); - - return 0; -} - -/* - * 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; + size_t i; - for (i = 0; i != child_config.maxclients; i++) { - if (child_ptr[i].status != T_EMPTY) - kill (child_ptr[i].tid, sig); - } + 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 5ebf179..d7cf5b6 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 beb2b01..44f12d7 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 c7d8560..0611c39 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 f3cf671..da64461 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 43170c5..06465b1 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 @@ done: 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 0000000..4ddc4aa --- /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 0000000..02c33d7 --- /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 c7b4423..93a30cf 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 dd95400..6f797ef 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 09/62] 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 d7cf5b6..575fc6c 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 10/62] 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 575fc6c..eded11a 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 11/62] 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 f85d29d..0ed686b 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 12/62] 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 06a977e..3ae3d82 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 f1225ea..a516d4f 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 13/62] 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 afd3b6b..3e24852 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 b7a334c..7918a07 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 @@ STRING_TEST: 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 2d11cef..ba0aebe 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 3ae3d82..8bd713d 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 94faeea..505b5c4 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 b63d026..393e5d4 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 ee3c987..2b15c08 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 8450cff..c576412 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 73dd030..c1c5100 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 59c2fa8..f74a588 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 a516d4f..033e179 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 14/62] 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 3924909..50e645b 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 8bd713d..861606b 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 0000000..77b073d --- /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 0000000..19877d3 --- /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 c576412..3adc473 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 f74a588..8513ba8 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 15/62] 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 eded11a..16a026d 100644 --- a/src/conf.c +++ b/src/conf.c @@ -442,6 +442,28 @@ done: 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 44f12d7..43f4f97 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 06465b1..7f6d947 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 16/62] 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 7f6d947..aac9a5b 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 @@ done: 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 17/62] 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 0ed686b..f31696d 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 18/62] 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 f31696d..7158492 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 19/62] 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 16a026d..c74216d 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 43f4f97..02fb699 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 aac9a5b..8b2167d 100644 --- a/src/main.c +++ b/src/main.c @@ -263,7 +263,7 @@ done: 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 20/62] 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 c74216d..c9a499e 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 02fb699..9e10898 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 8b2167d..b4a5e53 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 21/62] 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 3049acf..8d44465 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 861606b..4a94b98 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 e18132e..206bf31 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 2b15c08..3018b46 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 7158492..755a913 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 b4a5e53..4a5f8ea 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 ca2ee4b..5890d4a 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 3adc473..310871c 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 @@ BAD_REQUEST_ERROR: } #ifdef REVERSE_SUPPORT - if (config.reversepath_list != NULL) { + if (config->reversepath_list != NULL) { /* * Rewrite the URL based on the reverse path. After calling * reverse_rewrite_url "url" can be freed since we either @@ -387,7 +387,7 @@ BAD_REQUEST_ERROR: if (reverse_url != NULL) { safefree (url); url = reverse_url; - } else if (config.reverseonly) { + } else if (config->reverseonly) { log_message (LOG_ERR, "Bad request, no mapping for '%s' found", url); @@ -420,7 +420,7 @@ BAD_REQUEST_ERROR: /* Verify that the port in the CONNECT method is allowed */ if (!check_allowed_connect_ports (request->port, - config.connect_ports)) + config->connect_ports)) { indicate_http_error (connptr, 403, "Access violation", "detail", @@ -437,7 +437,7 @@ BAD_REQUEST_ERROR: } else { #ifdef TRANSPARENT_PROXY if (!do_transparent_proxy - (connptr, hashofheaders, request, &config, &url)) { + (connptr, hashofheaders, request, config, &url)) { goto fail; } #else @@ -455,8 +455,8 @@ BAD_REQUEST_ERROR: /* * 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 @@ BAD_REQUEST_ERROR: 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 @@ BAD_REQUEST_ERROR: /* * Check to see if they're requesting the stat host */ - if (config.stathost && strcmp (config.stathost, request->host) == 0) { + if (config->stathost && strcmp (config->stathost, request->host) == 0) { log_message (LOG_NOTICE, "Request for the stathost."); connptr->show_stats = TRUE; goto fail; @@ -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 @@ retry: #ifdef REVERSE_SUPPORT /* Write tracking cookie for the magical reverse proxy path hack */ - if (config.reversemagic && connptr->reversepath) { + if (config->reversemagic && connptr->reversepath) { ret = write_message (connptr->client_fd, "Set-Cookie: " REVERSE_COOKIE "=%s; path=/\r\n", connptr->reversepath); @@ -1082,7 +1082,7 @@ retry: } /* 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 @@ retry: ret = write_message (connptr->client_fd, "Location: %s%s%s\r\n", - config.reversebaseurl, + config->reversebaseurl, (reverse->path + 1), (header + len)); if (ret < 0) goto ERROR_EXIT; log_message (LOG_INFO, "Rewriting HTTP redirect: %s -> %s%s%s", - header, config.reversebaseurl, + header, config->reversebaseurl, (reverse->path + 1), (header + len)); hashmap_remove (hashofheaders, "location"); } @@ -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 @@ e401: * 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 0264787..32bd2a7 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 8513ba8..73ddebd 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 93a30cf..9f4acc3 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 22/62] 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 4a94b98..9ebba02 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 4a5f8ea..71b6f91 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 5890d4a..d19a2f6 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 23/62] 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 c9a499e..69a4c85 100644 --- a/src/conf.c +++ b/src/conf.c @@ -441,7 +441,7 @@ done: 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 9e10898..085169d 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 71b6f91..8a3b6fa 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 24/62] 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 8a3b6fa..2963957 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 25/62] 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 8d44465..f38fd44 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 0ca980e..6fc4518 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 69a4c85..9dd6da2 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 2963957..fc5ad45 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 310871c..041fb03 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 26/62] 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 df5fbce..727ef3e 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 27/62] 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 7918a07..06c982b 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 28/62] 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 3acd813..52eab20 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 29/62] 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 041fb03..859ac6b 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 30/62] 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 6ddbcc0..623d9a4 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 247b7ef..48e5b03 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 1bd0ffa..5e2f390 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 31/62] 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 c38a1d2..ab26ad1 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 32/62] 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 623d9a4..850eca5 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 33/62] 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 48e5b03..0428e51 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 5e2f390..bf2c09c 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 34/62] 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 ab26ad1..1066660 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 35/62] 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 850eca5..9623754 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 36/62] 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 9623754..e645dc1 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 0428e51..3447ad2 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 bf2c09c..0d1eda1 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 37/62] 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 327b727..321b282 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 38/62] 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 3e24852..c1cce09 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 39/62] 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 c1cce09..cf6555b 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 40/62] 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 cf6555b..cf8fab5 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 41/62] 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 | 548 ++++++++++++++++---------------- docs/man8/Makefile.am | 28 +- docs/man8/tinyproxy.txt.in | 157 +++++---- 5 files changed, 393 insertions(+), 401 deletions(-) diff --git a/configure.ac b/configure.ac index e645dc1..900af95 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 3447ad2..31b8ddf 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 cf8fab5..b5619dd 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 - 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*:: +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. - 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*:: +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. - 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 -*Listen*:: +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. - 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 -*Bind*:: +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. - This allows you to specify which address Tinyproxy will bind - to for outgoing connections to web servers or upstream proxies. +=item B -*BindSame*:: +This allows you to specify which address Tinyproxy will bind +to for outgoing connections to web servers or upstream proxies. - 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 -*Timeout*:: +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. - The maximum number of seconds of inactivity a connection is - allowed to have before it is closed by Tinyproxy. +=item B -*ErrorFile*:: +The maximum number of seconds of inactivity a connection is +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. +=item B -*DefaultErrorFile*:: +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. - 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 parameter controls the HTML template file returned when an +error occurs for which no specific error file has been set. - 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 -*StatFile*:: +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@`. - 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 B -*LogFile*:: +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. - This controls the location of the file to which Tinyproxy - writes its debug output. Alternatively, Tinyproxy can log - to syslog -- see the Syslog option. +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. -*Syslog*:: +=item B - 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. +This controls the location of the file to which Tinyproxy +writes its debug output. Alternatively, Tinyproxy can log +to syslog -- see the Syslog option. -*LogLevel*:: +=item B - 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: +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. - * Critical (least verbose) - * Error - * Warning - * Notice - * Connect (log connections without Info's noise) - * Info (most verbose) +=item B -*PidFile*:: +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: - This option controls the location of the file where the main - Tinyproxy process stores its process ID for signaling purposes. +=over 4 -*XTinyproxy*:: +=item * Critical (least verbose) - Setting this option to `Yes` tells Tinyproxy to add a header - `X-Tinyproxy` containing the client's IP address to the request. +=item * Error -*Upstream*:: +=item * Warning - 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: +=item * Notice - * 'upstream type host:port' turns proxy upstream support on generally. +=item * Connect (log connections without Info's noise) - * 'upstream type user:pass@host:port' does the same, but uses the - supplied credentials for authentication. +=item * Info (most verbose) - * 'upstream type host:port "site_spec"' turns on the upstream proxy - for the sites matching `site_spec`. +=back - `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. +This option controls the location of the file where the main +Tinyproxy process stores its process ID for signaling purposes. - 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 +Setting this option to `Yes` tells Tinyproxy to add a header +`X-Tinyproxy` containing the client's IP address to the request. - 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... - -*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. - -*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/" ----- - -*ReverseOnly*:: +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: - 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*:: - - 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. +=over 4 -*ReverseBaseURL*:: +=item * I turns proxy upstream support on generally. - 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. +=item * I +does the same, but uses the supplied credentials for authentication. +=item * I +turns on the upstream proxy for the sites matching `site_spec`. -BUGS ----- +`type` can be one of `http`, `socks4`, `socks5`, `none`. + +=item * I +turns off upstream support for sites matching `site_spec`, that means the +connection is done directly. + +=back + +The site can be specified in various forms as a hostname, domain +name or as an IP range: + +=over 4 + +=item * I matches host exactly + +=item * I<.name> matches any host in domain "name" + +=item * I<.> matches any host with no domain (in 'empty' domain) + +=item * I matches network/mask + +=item * I matches network/mask + +=back + +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"` + +=item B + +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. + +=item B + +=item B + +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. + +=item B + +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" + +=item B + +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. + +=item B + +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... + +=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. + +=item B + +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 + +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. -SEE ALSO --------- -tinyproxy(8) +=head1 SEE ALSO + +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 0d1eda1..d2d7e19 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 54da867..a7882c1 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 -*tinyproxy* accepts the following options: +B accepts the following options: -*-c *:: - Use an alternate configuration file. +=over 4 -*-d*:: - Don't daemonize and stay in the foreground. Useful for debugging purposes. +=item B<-c > -*-h*:: - Display a short help screen of command line arguments and exit. +Use an alternate configuration file. -*-v*:: - Display version information and exit. +=item B<-d> +Don't daemonize and stay in the foreground. Useful for debugging purposes. -SIGNALS -------- +=item B<-h> + +Display a short help screen of command line arguments and exit. + +=item B<-v> + +Display version information and exit. + +=back + +=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 -*cause*:: - The abbreviated cause of the error condition. +=item B -*clientip*:: - The IP address of the client making the request. +The full HTTP request line. -*clienthost*:: - The hostname of the client making the request. +=item B -*version*:: - The version of Tinyproxy. +The abbreviated cause of the error condition. -*package*:: - The package name. Presently, resolves to 'tinyproxy'. +=item B -*date*:: - The current date/time in HTTP format. +The IP address of the client making the request. + +=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. -SEE ALSO --------- -tinyproxy.conf(5) +=head1 SEE ALSO + +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 42/62] 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 1066660..47c3b8d 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 43/62] 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 9ebba02..15d4fe8 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 44/62] 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 5065255..e2ba221 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 52eab20..0000000 --- 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 b5619dd..66dd04e 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 45/62] 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 206bf31..8a0b085 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: %s\n", - config->filter, p->pat); + "Bad regex in %s: line %d - %s\n", + config->filter, lineno, s); + exit (EX_DATAERR); + } + if (!sblist_add(fl, &fe)) { + oom:; + fprintf (stderr, + "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) +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, 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) -{ - struct filter_list *p; - int result; - - if (!fl || !already_init) - goto COMMON_EXIT; - - for (p = fl; p; p = p->next) { - 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 8c6f270..8a7575b 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 859ac6b..c58bbed 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -457,9 +457,9 @@ BAD_REQUEST_ERROR: */ 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 46/62] 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 66dd04e..09c2ced 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 47/62] 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 0000000..9b7c1ff --- /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 0000000..4ea7892 --- /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 0000000..8b1d2c0 --- /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 0000000..d080bac --- /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 0000000..6ce2b0b --- /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 48/62] 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 900af95..74220a2 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 727ef3e..2c1e069 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 49/62] 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 74220a2..8701ad9 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 50/62] 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 8701ad9..a0503a4 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 51/62] 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 a0503a4..782ee06 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 52/62] 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 6f797ef..14575f8 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 53/62] 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 14575f8..799c9d6 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 54/62] 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 9a965dc..f3948bc 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 55/62] 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 3018b46..f04943e 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 56/62] 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 782ee06..7de6087 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 57/62] 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 fc5ad45..d9f8a91 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 58/62] 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 d9f8a91..49b4d8f 100644 --- a/src/main.c +++ b/src/main.c @@ -271,6 +271,15 @@ done: 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 59/62] 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 49b4d8f..da3feb3 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 60/62] 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 a7882c1..809873f 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 61/62] 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 09c2ced..8a18d55 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 809873f..7fa420f 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 62/62] 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 9dd6da2..27dc951 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)) {