commit as 1.0.3

This commit is contained in:
Vladimir Dubrovin 2026-11-07 11:50:02 +03:00
parent 14e87dbd78
commit a355fc7ba5
4 changed files with 36 additions and 49 deletions

View File

@ -1,25 +1,13 @@
3proxy-1.0.2 Released October, 24 2026
3proxy-1.0.3 Released November, 7 2026
! Fix: dnspr relayed to the client whatever came first to the socket it asked the name server from, from any address and under any id, so a forged answer could be passed on; the query now goes out under a random id, and only an answer from the name server asked, with that id and the same question, is relayed
! Fix: a client sending a part of a TLS record and then nothing kept a thread taking all of a CPU, and the read never timed out
! Fix: without -Ne, the reply to SOCKS CONNECT and BIND gave the address of the client's connection to the proxy instead of the socket towards the destination: a BIND client was told the proxy's own port and the peer could never reach it (since 0.9.6)
! Fix: a client which shut down its sending side (half-close) could get the answer cut short, megabytes short with large answers; likewise an upload after the server half-closed
! Fix: with chroot every local time was UTC and every syslog message was lost: the time zone is loaded and syslog connected before chroot; the chroot entry of 3proxy.cfg(5) lists what PAM, LDAP and ODBC still need inside the chroot
! Fix: the HTTP proxy closed the server connection of a kept-alive client and opened another one when only the request method changed (a POST after a GET to the same host)
! Fix: the last ssl_client_mode in the configuration applied to every service, and a reload changed it for the services still running
! Fix: memory leaks: on every refused configuration reload, on every failed TLS handshake, the server CA paths of every TLS service, compiled pcre: / regex: host names of access rules and patterns of httpsrv rules
! Fix: Unix sockets: on Linux a proxy listening on a Unix socket relayed only the first bytes of an answer (result 95); an abstract socket (unix:@NAME) was bound under a name padded with zeros, which clients did not reach; tcppm refused port 0 for a unix: target, as given in the manual's example; on a reload the socket file of a listening service was removed
! Fix: SIGPIPE could kill 3proxy when a client was gone while services were starting, and standalone services (proxy, socks, ...) at any time; sends use MSG_NOSIGNAL where the system has it
! Fix: TLS MitM failed for every connection with wolfSSL built with WOLFSSL_QT (wolfSSL 5.9.1 of Ubuntu 26.04): the source certificate's serial number could not be copied; a serial number made from the hash of the source certificate is used then
! Fix: Windows: closing a connection could leave its socket open (SO_LINGER on a non-blocking socket) or reset it while the peer still sent, so the end of an answer could be lost
! Fix: httpsrv: the body of a request answered without being read (for example when authentication is required) was read as the next request; a client still sending when a connection without keep-alive closed could get a reset instead of the answer
! Fix: httpsrv on Windows: UNC paths (\\server\share) did not work
! Fix: httpsrv and admin services and their clients were named ZOMBIE on the admin services page, in %N of logs and in RADIUS
! Fix: the Watcom (lite) build did not compile, 1.0.1 had no binaries for it
+ -s (splice) per direction: -s1 splices what the server sends (downloads), -s2 what the client sends (uploads), -s3 both; -s alone splices only where it pays (large bodies, long streams); pipes are kept with the connection for its next requests. splice() is built by default on Linux again and is used only by services given -s
+ Socket options (-oc, -os, -ol, -or, -oR) take NAME=VALUE; NAME=0 turns an option off, also one set by default; an unknown name or a value which is not a number is an error
+ The HTTP proxy frees its 64 KB request buffer while a CONNECT tunnel relays data and while a kept-alive connection waits for its next request: 2000 tunnels take 213 MB instead of 339 MB
+ TLS records are read ahead with OpenSSL 1.1.0 and later: decrypting takes 9-14% less CPU
+ Faster service stop and configuration reload, no fixed delays; a connection waiting for a free maxconn slot gets it at once
- TCP_NODELAY is set by default on proxy-to-client and proxy-to-server sockets; -ocTCP_NODELAY=0 / -osTCP_NODELAY=0 turns it off
- The HTTP proxy fetches ftp:// URLs only when the service is given the new -Xftp option (in a build with FTP=true)
! Fix: RADIUS authentication: the reply digest copied the shared secret right after the received bytes, so a reply of 4100 bytes with a secret of 17 or more characters wrote up to 47 bytes of it past the reply buffer on the stack, before the reply was authenticated; the digest is hashed over the reply and the secret in place now, with nothing written past the reply, and a reply is taken only from the address and port of the server the request went to, with the request's identifier and length, which was never checked before
! Fix: the PTR answer of a name server could write past the answer buffer on the stack: the label walk ran to the end of the whole message instead of the end of the record's data and could take a compression pointer for a label length (an answer of 3841 bytes or more reached past the buffer itself); the walk now stops at the end of the record's data, and a compressed, empty or truncated name is not accepted
! Fix: every connection matching a pcre: (or regex:) rule matched on the one match data block of the rule, which pcre2_match() writes into: two connections matching at the same time raced and corrupted memory, bringing the process down under load; a connection now makes a match data block of its own
! Fix: TLS: a read which could only be done once a write was, or a write once a read was (a TLS 1.3 key update answered while the socket cannot take more, for example), ended the connection with an error; each TLS socket remembers now what its pending read and write wait for, and the poll of the TLS layer waits for that
! Fix: Windows: a TLS read or write which wanted the socket again was not seen as one and dropped the connection with an unrelated error: the C library's errno was set where the socket error is read
! Fix: a client of a connect back (-R) service stayed in accept() for the connection another client of the same service took, until the next one came, past its time out and through a configuration reload; the connect back socket is non-blocking now, and an accept() with nothing to take does not count as one of the client's tries
! Fix: ftppr: the answer of a server which logs in on USER alone (230, as anonymous FTP often does) or refuses the login was read but never handed to the client, which was left waiting for the answer to its PASS until the time out (#1284)
! Fix: plugins: StringsPlugin, cutting a string table line at its end, wrote NULs past the line, into what the buffer kept of longer lines read before or past its end; FilePlugin passed the TLS state of the connection where the client's parameters were expected, so sending a string table over a TLS connection read a TLS state as a clientparam
+ -B (for TCP services) splits the TLS ClientHello passed through tlspr in the middle of the server name (SNI), so DPI systems which do not reassemble it do not see the host name; TCP_NODELAY, set by default, must not be turned off, or the kernel may join the segments again; -s keeps the same effect where -s is not splice (#1283)
+ 3proxy --help (and the usage message of a bad invocation) lists what the build was made with: TLS and its backend, RADIUS, PCRE, IPv6, UNIX sockets, the built-in HTTP server, transparency, ODBC and the optional proxy services
+ An outgoing connection bound to an external address is bound with IP_BIND_ADDRESS_NO_PORT on Linux 4.2 and later: the port is chosen by connect() for the pair with the destination, so ports in use towards other destinations or closing in TIME_WAIT do not run the ephemeral range out; bind() no longer failed with result 12 or searched long for a port under many connections

View File

@ -1,25 +1,13 @@
3proxy-1.0.2 Вышел 24 Октября 2026
3proxy-1.0.3 Вышел 7 Ноября 2026
! Исправление: dnspr передавал клиенту то, что первым пришло на сокет, с которого он обращался к серверу имён, с любого адреса и с любым id, так что поддельный ответ мог быть передан клиенту; теперь запрос отправляется со случайным id, и передаётся только ответ от того сервера имён, к которому обращались, с этим id и тем же вопросом
! Исправление: клиент, отправивший часть записи TLS и затем ничего, занимал поток на всё процессорное время, и чтение не завершалось по таймауту
! Исправление: без -Ne ответ на SOCKS CONNECT и BIND содержал адрес соединения клиента с прокси вместо адреса сокета в сторону назначения: клиенту BIND сообщался собственный порт прокси, и удалённая сторона не могла к нему подключиться (с 0.9.6)
! Исправление: клиент, закрывший свою сторону передачи (half-close), мог получить ответ не полностью, на больших ответах без нескольких мегабайт; так же обрезалась загрузка на сервер после того, как сервер закрыл свою сторону
! Исправление: при chroot всё местное время становилось UTC и все сообщения syslog терялись: часовой пояс теперь загружается, а syslog подключается до chroot; в описании chroot в 3proxy.cfg(5) перечислено, что ещё нужно внутри chroot для PAM, LDAP и ODBC
! Исправление: HTTP прокси закрывал соединение с сервером для клиента с keep-alive и открывал новое, когда менялся только метод запроса (POST после GET к тому же хосту)
! Исправление: последний ssl_client_mode в конфигурации действовал для всех служб, а перезагрузка конфигурации меняла его для уже работающих служб
! Исправление: утечки памяти: при каждой отвергнутой перезагрузке конфигурации, при каждом неудачном согласовании TLS, пути CA сервера каждой службы с TLS, скомпилированные имена хостов pcre: / regex: в правилах доступа и шаблоны правил httpsrv
! Исправление: сокеты Unix: в Linux прокси, слушающий на сокете Unix, передавал только первые байты ответа (результат 95); абстрактный сокет (unix:@NAME) привязывался к имени, дополненному нулями, до которого клиенты не доходили; tcppm отвергал порт 0 для цели unix:, как в примере из документации; при перезагрузке конфигурации файл сокета слушающей службы удалялся
! Исправление: SIGPIPE мог завершить 3proxy, если клиент отключался во время запуска служб, а отдельные службы (proxy, socks, ...) — в любой момент; отправка данных использует MSG_NOSIGNAL там, где система его поддерживает
! Исправление: TLS MitM не работал ни для одного соединения с wolfSSL, собранным с WOLFSSL_QT (wolfSSL 5.9.1 в Ubuntu 26.04): не удавалось скопировать серийный номер исходного сертификата; в этом случае используется серийный номер, полученный из хеша исходного сертификата
! Исправление: Windows: при закрытии соединения сокет мог остаться открытым (SO_LINGER на неблокирующем сокете) или соединение сбрасывалось, пока другая сторона ещё передавала данные, так что конец ответа мог потеряться
! Исправление: httpsrv: тело запроса, на который ответ дан без его чтения (например, когда требуется аутентификация), читалось как следующий запрос; клиент, ещё передающий данные при закрытии соединения без keep-alive, мог получить сброс соединения вместо ответа
! Исправление: httpsrv в Windows: не работали пути UNC (\\server\share)
! Исправление: службы httpsrv и admin и их клиенты назывались ZOMBIE на странице служб admin, в %N журнала и в RADIUS
! Исправление: сборка Watcom (lite) не компилировалась, в 1.0.1 для неё не было двоичных файлов
+ -s (splice) по направлениям: -s1 — то, что передаёт сервер (скачивание), -s2 — то, что передаёт клиент (загрузка), -s3 — оба; -s без числа использует splice только там, где это выгодно (большие тела, длинные потоки); каналы (pipe) сохраняются с соединением для следующих запросов. splice() снова собирается по умолчанию в Linux и используется только службами с -s
+ Опции сокетов (-oc, -os, -ol, -or, -oR) принимают ИМЯ=ЗНАЧЕНИЕ; ИМЯ=0 выключает опцию, в том числе установленную по умолчанию; неизвестное имя или значение, не являющееся числом, — ошибка
+ HTTP прокси освобождает буфер запроса размером 64 КБ, пока туннель CONNECT передаёт данные и пока соединение с keep-alive ждёт следующего запроса: 2000 туннелей занимают 213 МБ вместо 339 МБ
+ Записи TLS читаются с упреждением (read-ahead) с OpenSSL 1.1.0 и новее: расшифровка занимает на 9-14% меньше процессорного времени
+ Более быстрая остановка служб и перезагрузка конфигурации, без фиксированных задержек; соединение, ожидающее свободного места в maxconn, получает его сразу
- TCP_NODELAY устанавливается по умолчанию на сокетах прокси-клиент и прокси-сервер; -ocTCP_NODELAY=0 / -osTCP_NODELAY=0 выключает его
- HTTP прокси обрабатывает адреса ftp:// только если службе задана новая опция -Xftp (в сборке с FTP=true)
! Исправление: аутентификация RADIUS: дайджест ответа копировал общий секрет сразу за принятыми байтами, так что ответ размером 4100 байт с секретом в 17 и более символов записывал до 47 байт секрета за буфер ответа на стеке, ещё до проверки подлинности ответа; дайджест теперь вычисляется по ответу и секрету на месте, ничего не записывая за буфер, а ответ принимается только с адреса и порта сервера, которому был адресован запрос, с совпадающими с запросом идентификатором и длиной: раньше отправитель ответа не проверялся вовсе
! Исправление: ответ PTR от сервера имён мог записать данные за буфер ответа на стеке: обход имён шёл до конца всего сообщения вместо конца данных записи и мог принять байт-указатель сжатия за длину метки (ответ размером 3841 байт и более выходил за сам буфер); теперь обход останавливается в конце данных записи, а сжатое, пустое или обрезанное имя не принимается
! Исправление: каждое соединение, проверяемое правилом pcre: (или regex:), сопоставлялось с единственным блоком данных сопоставления правила, в который пишет pcre2_match(): два соединения, сопоставляемые одновременно, конкурировали за него и портили память, приводя процесс к падению под нагрузкой; теперь каждое соединение получает собственный блок данных сопоставления
! Исправление: TLS: чтение, возможное только после записи, или запись только после чтения (например, когда TLS 1.3 отвечает на обновление ключей, а сокет больше не принимает данные) завершало соединение с ошибкой; каждый TLS-сокет теперь помнит, чего ждут его незавершённые чтение и запись, и poll() TLS-уровня ждёт именно этого
! Исправление: Windows: чтение или запись TLS, требующая повторного обращения к сокету, не распознавалась, и соединение завершалось с посторонней ошибкой: в месте чтения ошибки сокета устанавливалась ошибка libc (errno)
! Исправление: клиент обратного соединения (connect back, -R) оставался в accept() в ожидании соединения, которое забрал другой клиент той же службы, до следующего соединения, переживая свой тайм-аут и перезагрузку конфигурации; сокет обратного соединения теперь неблокирующий, и accept() без соединения не считается попыткой клиента
! Исправление: ftppr: ответ сервера, входящего по одному USER (230, как часто бывает у анонимного FTP) или отказывающего во входе, прочитывался, но не передавался клиенту, который оставался ждать ответа на свой PASS до тайм-аута (#1284)
! Исправление: плагины: StringsPlugin, обрезая строку таблицы строк на её конце, записывал NUL за строкой, в остатки в буфере от прочитанных раньше более длинных строк или за его конец; FilePlugin передавал в socksend() состояние TLS соединения вместо параметров клиента, и отправка таблицы строк по TLS-соединению читала состояние TLS как clientparam
+ -B (для TCP-служб) разбивает проходящий через tlspr ClientHello TLS посередине имени сервера (SNI), чтобы системы DPI, не собирающие его заново, не видели имя хоста; TCP_NODELAY, устанавливаемый по умолчанию, выключать нельзя, иначе ядро может снова склеить сегменты; -s сохраняет тот же эффект там, где -s не означает splice (#1283)
+ 3proxy --help (и сообщение об использовании при неверном запуске) перечисляет, с чем собран бинарник: TLS и его серверная библиотека, RADIUS, PCRE, IPv6, сокеты Unix, встроенный HTTP-сервер, прозрачное проксирование, ODBC и необязательные прокси-службы
+ Исходящее соединение, привязываемое к внешнему адресу, на Linux 4.2 и новее привязывается с IP_BIND_ADDRESS_NO_PORT: порт выбирает connect() для пары с адресом назначения, поэтому порты, занятые соединениями к другим адресам или закрывающиеся в TIME_WAIT, больше не исчерпывают эфемерный диапазон; bind() больше не завершался с результатом 12 при большом числе соединений и не тратил миллисекунды на поиск порта

View File

@ -1 +1 @@
1.0.2
1.0.3

11
doc/changelog/1/0/3 Normal file
View File

@ -0,0 +1,11 @@
! Fix: RADIUS authentication: the reply digest copied the shared secret right after the received bytes, so a reply of 4100 bytes with a secret of 17 or more characters wrote up to 47 bytes of it past the reply buffer on the stack, before the reply was authenticated; the digest is hashed over the reply and the secret in place now, with nothing written past the reply, and a reply is taken only from the address and port of the server the request went to, with the request's identifier and length, which was never checked before
! Fix: the PTR answer of a name server could write past the answer buffer on the stack: the label walk ran to the end of the whole message instead of the end of the record's data and could take a compression pointer for a label length (an answer of 3841 bytes or more reached past the buffer itself); the walk now stops at the end of the record's data, and a compressed, empty or truncated name is not accepted
! Fix: every connection matching a pcre: (or regex:) rule matched on the one match data block of the rule, which pcre2_match() writes into: two connections matching at the same time raced and corrupted memory, bringing the process down under load; a connection now makes a match data block of its own
! Fix: TLS: a read which could only be done once a write was, or a write once a read was (a TLS 1.3 key update answered while the socket cannot take more, for example), ended the connection with an error; each TLS socket remembers now what its pending read and write wait for, and the poll of the TLS layer waits for that
! Fix: Windows: a TLS read or write which wanted the socket again was not seen as one and dropped the connection with an unrelated error: the C library's errno was set where the socket error is read
! Fix: a client of a connect back (-R) service stayed in accept() for the connection another client of the same service took, until the next one came, past its time out and through a configuration reload; the connect back socket is non-blocking now, and an accept() with nothing to take does not count as one of the client's tries
! Fix: ftppr: the answer of a server which logs in on USER alone (230, as anonymous FTP often does) or refuses the login was read but never handed to the client, which was left waiting for the answer to its PASS until the time out (#1284)
! Fix: plugins: StringsPlugin, cutting a string table line at its end, wrote NULs past the line, into what the buffer kept of longer lines read before or past its end; FilePlugin passed the TLS state of the connection where the client's parameters were expected, so sending a string table over a TLS connection read a TLS state as a clientparam
+ -B (for TCP services) splits the TLS ClientHello passed through tlspr in the middle of the server name (SNI), so DPI systems which do not reassemble it do not see the host name; TCP_NODELAY, set by default, must not be turned off, or the kernel may join the segments again; -s keeps the same effect where -s is not splice (#1283)
+ 3proxy --help (and the usage message of a bad invocation) lists what the build was made with: TLS and its backend, RADIUS, PCRE, IPv6, UNIX sockets, the built-in HTTP server, transparency, ODBC and the optional proxy services
+ An outgoing connection bound to an external address is bound with IP_BIND_ADDRESS_NO_PORT on Linux 4.2 and later: the port is chosen by connect() for the pair with the destination, so ports in use towards other destinations or closing in TIME_WAIT do not run the ephemeral range out; bind() no longer failed with result 12 or searched long for a port under many connections