From b383efe77d16dd1b7b9cd94a81dd640c96f8e469 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 7 Mar 2026 14:30:42 +0000 Subject: [PATCH 01/10] fix making of man8 regression from 942d0c6b03673ad816c42176422d7fe691143064 --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 329c5633..2f6f159b 100644 --- a/configure.ac +++ b/configure.ac @@ -223,6 +223,7 @@ docs/Makefile docs/man5/Makefile docs/man5/tinyproxy.conf.txt docs/man8/Makefile +docs/man8/tinyproxy.txt m4macros/Makefile tests/Makefile tests/scripts/Makefile From baecbf4c3e006fa68ab92f65bbd4138c47ede111 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 7 Mar 2026 14:07:54 +0000 Subject: [PATCH 02/10] release 1.11.3 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ca717669..0a5af26d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.11.2 +1.11.3 From 86a8f272f6af3975b37f34ce38d4b5e152ff9864 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 8 Mar 2026 16:31:03 +0000 Subject: [PATCH 03/10] version.sh: fix use of non-annotated tags the script always returned 1.11.0-rc1, because apparently that tag was annotated, unlike the newer ones. --- scripts/version.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/version.sh b/scripts/version.sh index 03fb3aa0..4ad08b61 100755 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -5,7 +5,7 @@ GIT_DIR="${SCRIPT_DIR}/../.git" if test -d "${GIT_DIR}" ; then if type git >/dev/null 2>&1 ; then - gitstr=$(git describe --match '[0-9]*.[0-9]*.*' 2>/dev/null) + gitstr=$(git describe --tags --match '[0-9]*.[0-9]*.*' 2>/dev/null) if test "x$?" != x0 ; then sed 's/$/-git/' < VERSION else From 9240e60f358a86771d9df02f4012a6b8535f2a3a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 8 Mar 2026 17:03:34 +0000 Subject: [PATCH 04/10] CI: also check that website generation works --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f077b192..067105da 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,7 @@ jobs: - run: ./configure - run: make - run: make test + - run: cd docs/web ; make test-macos: runs-on: macos-latest steps: From 1c4af6745a247a7dc17cdf2ffa52e976df08c2bb Mon Sep 17 00:00:00 2001 From: Mohamed Akram Date: Sun, 8 Mar 2026 19:28:09 +0400 Subject: [PATCH 05/10] Fix paths in man page --- configure.ac | 1 - docs/man8/Makefile.am | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 2f6f159b..329c5633 100644 --- a/configure.ac +++ b/configure.ac @@ -223,7 +223,6 @@ docs/Makefile docs/man5/Makefile docs/man5/tinyproxy.conf.txt docs/man8/Makefile -docs/man8/tinyproxy.txt m4macros/Makefile tests/Makefile tests/scripts/Makefile diff --git a/docs/man8/Makefile.am b/docs/man8/Makefile.am index 17281cd3..28e361ef 100644 --- a/docs/man8/Makefile.am +++ b/docs/man8/Makefile.am @@ -29,8 +29,12 @@ else @echo "*** pod2man is required to regenerate $(@) ***"; exit 1; endif +CLEANFILES = \ + tinyproxy.txt + MAINTAINERCLEANFILES = \ $(MAN8_FILES:.txt=.8) EXTRA_DIST = \ - $(MAN8_FILES:.txt=.8) + $(MAN8_FILES:.txt=.8) \ + tinyproxy.txt.in From 969852ccdb1d19d7ed302f0e1d324661be641e0a Mon Sep 17 00:00:00 2001 From: rofl0r Date: Thu, 12 Mar 2026 14:26:24 +0000 Subject: [PATCH 06/10] reqs: check negative length values when reading chunked data this could lead to a DoS when a legitimate client reads from an attacker-controlled web server. closes #597 --- src/reqs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/reqs.c b/src/reqs.c index a562c68a..94ce7673 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -613,6 +613,7 @@ static int pull_client_data_chunked (struct conn_s *connptr) { } chunklen = strtol (buffer, (char**)0, 16); + if (chunklen < 0) goto ERROR_EXIT; if (pull_client_data (connptr, chunklen+2, 0) < 0) goto ERROR_EXIT; From e77570a56ccd3e2a2aaa6b1d75761be815ab7b5c Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 28 Mar 2026 18:22:03 +0100 Subject: [PATCH 07/10] fix catching timed-out socket (#599) due to some ancient piece of code that's supposed to fix a bug in 1990's internet explorer, sockets were switched between blocking and non-blocking mode, making it hard to differentiate when socket timeouts kicked in. with the IE bug workaround removed, sockets are now always in blocking mode so we no longer need to catch EAGAIN/EWOULDBLOCK and treat them specially, they are now always treated as an error (whenever they are returned, the timeout kicked in). this should fix once and for all the cases where tinyproxy would not respect the user-provided socket timeouts, potentially causing endless loops. closes #598 --- src/buffer.c | 14 ----------- src/reqs.c | 67 ++++++---------------------------------------------- src/sock.c | 27 --------------------- src/sock.h | 3 --- 4 files changed, 7 insertions(+), 104 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index b3381838..4cf15a0f 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -241,13 +241,6 @@ ssize_t read_buffer (int fd, struct buffer_s * buffptr) bytesin = -1; } else { switch (errno) { -#ifdef EWOULDBLOCK - case EWOULDBLOCK: -#else -# ifdef EAGAIN - case EAGAIN: -# endif -#endif case EINTR: bytesin = 0; break; @@ -295,13 +288,6 @@ ssize_t write_buffer (int fd, struct buffer_s * buffptr) return bytessent; } else { switch (errno) { -#ifdef EWOULDBLOCK - case EWOULDBLOCK: -#else -# ifdef EAGAIN - case EAGAIN: -# endif -#endif case EINTR: return 0; case ENOBUFS: diff --git a/src/reqs.c b/src/reqs.c index 94ce7673..a82fff34 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -524,11 +524,10 @@ static struct request_s *process_request (struct conn_s *connptr, * server headers can be processed. * - rjkaes */ -static int pull_client_data (struct conn_s *connptr, long int length, int iehack) +static int pull_client_data (struct conn_s *connptr, long int length) { char *buffer; ssize_t len; - int ret; buffer = (char *) safemalloc (min (MAXBUFFSIZE, (unsigned long int) length)); @@ -549,43 +548,6 @@ static int pull_client_data (struct conn_s *connptr, long int length, int iehack length -= len; } while (length > 0); - if (iehack) { - /* - * BUG FIX: Internet Explorer will leave two bytes (carriage - * return and line feed) at the end of a POST message. These - * need to be eaten for tinyproxy to work correctly. - */ - ret = socket_nonblocking (connptr->client_fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the client socket " - "to non-blocking: %s", strerror(errno)); - goto ERROR_EXIT; - } - - len = recv (connptr->client_fd, buffer, 2, MSG_PEEK); - - ret = socket_blocking (connptr->client_fd); - if (ret != 0) { - log_message(LOG_ERR, "Failed to set the client socket " - "to blocking: %s", strerror(errno)); - goto ERROR_EXIT; - } - - if (len < 0 && errno != EAGAIN) - goto ERROR_EXIT; - - if ((len == 2) && CHECK_CRLF (buffer, len)) { - ssize_t bytes_read; - - bytes_read = read (connptr->client_fd, buffer, 2); - if (bytes_read == -1) { - log_message - (LOG_WARNING, - "Could not read two bytes from POST message"); - } - } - } - safefree (buffer); return 0; @@ -615,7 +577,7 @@ static int pull_client_data_chunked (struct conn_s *connptr) { chunklen = strtol (buffer, (char**)0, 16); if (chunklen < 0) goto ERROR_EXIT; - if (pull_client_data (connptr, chunklen+2, 0) < 0) + if (pull_client_data (connptr, chunklen+2) < 0) goto ERROR_EXIT; if(!chunklen) break; @@ -1008,7 +970,7 @@ process_client_headers (struct conn_s *connptr, pseudomap *hashofheaders) PULL_CLIENT_DATA: if (connptr->content_length.client > 0) { ret = pull_client_data (connptr, - connptr->content_length.client, 1); + connptr->content_length.client); } else if (connptr->content_length.client == -2) ret = pull_client_data_chunked (connptr); @@ -1195,8 +1157,8 @@ static int process_server_headers (struct conn_s *connptr) } /* - * Switch the sockets into nonblocking mode and begin relaying the bytes - * between the two connections. We continue to use the buffering code + * Begin relaying the bytes between the two connections. + * We continue to use the buffering code * since we want to be able to buffer a certain amount for slower * connections (as this was the reason why I originally modified * tinyproxy oh so long ago...) @@ -1269,14 +1231,6 @@ static void relay_connection (struct conn_s *connptr) /* * Try to send any remaining data to the server if we can. */ - ret = socket_blocking (connptr->server_fd); - if (ret != 0) { - log_message(LOG_ERR, - "Failed to set server socket to blocking: %s", - strerror(errno)); - return; - } - while (buffer_size (connptr->cbuffer) > 0) { if (write_buffer (connptr->server_fd, connptr->cbuffer) < 0) break; @@ -1573,16 +1527,9 @@ static void auth_error(struct conn_s *connptr, int code) { } /* - * This is the main drive for each connection. As you can tell, for the - * first few steps we are using a blocking socket. If you remember the - * older tinyproxy code, this use to be a very confusing state machine. - * Well, no more! :) The sockets are only switched into nonblocking mode - * when we start the relay portion. This makes most of the original - * tinyproxy code, which was confusing, redundant. Hail progress. - * - rjkaes - + * This is the main drive for each connection. * this function is called directly from child_thread() with the newly - * received fd from accept(). + * received fd from accept(). */ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) { diff --git a/src/sock.c b/src/sock.c index b3b0920d..ec8ad4c8 100644 --- a/src/sock.c +++ b/src/sock.c @@ -212,33 +212,6 @@ int opensock (const char *host, int port, const char *bind_to) return sockfd; } -/* - * Set the socket to non blocking -rjkaes - */ -int socket_nonblocking (int sock) -{ - int flags; - - assert (sock >= 0); - - flags = fcntl (sock, F_GETFL, 0); - return fcntl (sock, F_SETFL, flags | O_NONBLOCK); -} - -/* - * Set the socket to blocking -rjkaes - */ -int socket_blocking (int sock) -{ - int flags; - - assert (sock >= 0); - - flags = fcntl (sock, F_GETFL, 0); - return fcntl (sock, F_SETFL, flags & ~O_NONBLOCK); -} - - /** * Try to listen on one socket based on the addrinfo * as returned from getaddrinfo. diff --git a/src/sock.h b/src/sock.h index 70c44739..9763490d 100644 --- a/src/sock.h +++ b/src/sock.h @@ -53,9 +53,6 @@ union sockaddr_union { extern int opensock (const char *host, int port, const char *bind_to); extern int listen_sock (const char *addr, uint16_t port, sblist* listen_fds); -extern int socket_nonblocking (int sock); -extern int socket_blocking (int sock); - extern void set_socket_timeout(int fd); extern int getsock_ip (int fd, char *ipaddr); From bb7edc4778041b3bc8ad7fca448b67d98039cc7d Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sun, 29 Mar 2026 16:48:54 +0200 Subject: [PATCH 08/10] reqs: prevent potential int overflow when parsing chunked data (#603) follow-up to 969852ccdb1d19d7ed302f0e1d324661be641e0a closes #602 --- src/reqs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/reqs.c b/src/reqs.c index a82fff34..34a715c2 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -575,7 +575,8 @@ static int pull_client_data_chunked (struct conn_s *connptr) { } chunklen = strtol (buffer, (char**)0, 16); - if (chunklen < 0) goto ERROR_EXIT; + /* prevent negative or huge values causing overflow */ + if (chunklen < 0 || chunklen > 0x0fffffff) goto ERROR_EXIT; if (pull_client_data (connptr, chunklen+2) < 0) goto ERROR_EXIT; From 879bf844abffa0bf5fae6aff0c73179024dd9f98 Mon Sep 17 00:00:00 2001 From: rofl0r Date: Fri, 3 Apr 2026 11:21:23 +0200 Subject: [PATCH 09/10] reqs: fix case-sensitive matching of "chunked" (#605) the chunked transfer encoding needs to be matched in a case- insensitive manner. closes #604 --- src/reqs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reqs.c b/src/reqs.c index 34a715c2..a0fdd87d 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -812,7 +812,7 @@ static int is_chunked_transfer (pseudomap *hashofheaders) { char *data; data = pseudomap_find (hashofheaders, "transfer-encoding"); - return data ? !strcmp (data, "chunked") : 0; + return data ? !strcasecmp (data, "chunked") : 0; } /* From 09312a185ae25cc486b4ff5987638a7917a48bce Mon Sep 17 00:00:00 2001 From: rofl0r Date: Sat, 18 Apr 2026 00:03:15 +0200 Subject: [PATCH 10/10] reqs: improve stathost detection (#606) until now, only the basicauth code checked the host header, regular connections didn't. - add a new helper function to compare a hostname with optional trailingcolon/port against the stathost. - add stathost check via host header before transparent proxy check, else stathost might be misdetected as a trans host request. - refactor existing stathost checks to use the new helper this should make it easier to access the stathost, for example by injecting a host header into a curl command line with -H: $ curl -H "Host: tinyproxy.stats" 127.0.0.1:8080 the stathost can also be specified as an ip address, e.g. Stathost "127.0.0.10" + a separate Listen statement for that ip. in such a case e.g. $ curl http://127.0.0.10:8080 would work too, even if curl didn't add a Host header (but it does anyway). --- src/reqs.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/reqs.c b/src/reqs.c index a0fdd87d..2e7542cb 100644 --- a/src/reqs.c +++ b/src/reqs.c @@ -316,6 +316,17 @@ static int send_connect_method_response (struct conn_s *connptr) connptr->protocol.minor); } +/* determine whether a hostname with optional trailing colon/port is the + stathost */ +static int is_stathost (const char* host) +{ + const char *p = config->stathost; + const char *q = host; + if (!p || !q) return 0; + while (*p && *(p++) == *(q++)); + return *p == 0 && (*q == 0 || *q == ':'); +} + /* * Break the request line apart and figure out where to connect and * build a new request line. Finally connect to the remote server. @@ -384,6 +395,16 @@ static struct request_s *process_request (struct conn_s *connptr, goto fail; } + /* + * Check to see if they're requesting the stat host + */ + if (is_stathost (pseudomap_find (hashofheaders, "host"))) { +got_stathost: + log_message (LOG_NOTICE, "Request for the stathost."); + connptr->show_stats = TRUE; + goto fail; + } + #ifdef REVERSE_SUPPORT if (config->reversepath_list != NULL) { /* @@ -497,19 +518,11 @@ static struct request_s *process_request (struct conn_s *connptr, } } #endif - - - /* - * Check to see if they're requesting the stat host - */ - if (config->stathost && strcmp (config->stathost, request->host) == 0) { - log_message (LOG_NOTICE, "Request for the stathost."); - connptr->show_stats = TRUE; - goto fail; - } + /* check whether hostname from url is the stathost */ + if (is_stathost (request->host)) + goto got_stathost; safefree (url); - return request; fail: @@ -1630,7 +1643,7 @@ void handle_connection (struct conn_s *connptr, union sockaddr_union* addr) if (!authstring && config->stathost) { authstring = pseudomap_find (hashofheaders, "host"); - if (authstring && !strncmp(authstring, config->stathost, strlen(config->stathost))) { + if (authstring && is_stathost(authstring)) { authstring = pseudomap_find (hashofheaders, "authorization"); stathost_connect = 1; } else authstring = 0;