-B: the TLS ClientHello split at the SNI, which -s is taken for splice
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled

tlspr splits the ClientHello it passes in the middle of the server name
(SNI) when the service has s_option, set by -s. In builds with splice()
(Linux, the default there) -s is splice for every service but UDP ones
and admin, so the split was never made, and the How-To's SNI break did
nothing on Linux.

-B sets it whatever the build: for TCP services but admin, where
s_option has a meaning of its own. -s still does it where it is not
splice. The How-To, 3proxy.cfg(5) and tlspr(8) give -B.

Fixes #1283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Vladimir Dubrovin 2026-10-25 18:53:26 +03:00
parent bc79fca154
commit 248af1412d
5 changed files with 22 additions and 6 deletions

View File

@ -646,13 +646,13 @@ see <a href="#DNSPR">How to set up a DNS proxy (dnspr)</a>.
<br>tlspr can be used as a parent with the "tls" type to implement SNI splitting for DPI bypass (similar to NoDPI/GoodByeDPI). <br>tlspr can be used as a parent with the "tls" type to implement SNI splitting for DPI bypass (similar to NoDPI/GoodByeDPI).
The client sends the first part of the TLS ClientHello, tlspr splits it at the SNI extension and sends it in two TCP packets, The client sends the first part of the TLS ClientHello, tlspr splits it at the SNI extension and sends it in two TCP packets,
which can bypass some DPI systems that look for blocked hostnames in TLS handshakes. which can bypass some DPI systems that look for blocked hostnames in TLS handshakes.
<br>To enable SNI break, use <code>parent ... tls 0.0.0.0 0</code> and the <code>-s</code> option on the listening service with TCP_NODELAY: <br>To enable SNI break, use <code>parent ... tls 0.0.0.0 0</code> and the <code>-B</code> option on the listening service with TCP_NODELAY:
</p><pre> </p><pre>
auth iponly auth iponly
allow * allow *
parent 1000 tls 0.0.0.0 0 parent 1000 tls 0.0.0.0 0
allow * allow *
proxy -s -i127.0.0.1 -ocTCP_NODELAY -osTCP_NODELAY -p1443 proxy -B -i127.0.0.1 -ocTCP_NODELAY -osTCP_NODELAY -p1443
</pre> </pre>
<p> <p>
TCP_NODELAY is required to prevent the kernel from merging the split packets. TCP_NODELAY is required to prevent the kernel from merging the split packets.

View File

@ -654,13 +654,13 @@ iptables -t nat -A PREROUTING -p tcp -m multiport --dports 443,993,465 -j REDIRE
<br>tlspr может использоваться как родительский прокси типа "tls" для реализации SNI-фрагментации (аналог NoDPI/GoodByeDPI). <br>tlspr может использоваться как родительский прокси типа "tls" для реализации SNI-фрагментации (аналог NoDPI/GoodByeDPI).
Клиент отправляет первую часть TLS ClientHello, tlspr разбивает его на расширении SNI и отправляет двумя TCP-пакетами, Клиент отправляет первую часть TLS ClientHello, tlspr разбивает его на расширении SNI и отправляет двумя TCP-пакетами,
что позволяет обойти некоторые DPI-системы, ищущие заблокированные имена хостов в TLS-рукопожатиях. что позволяет обойти некоторые DPI-системы, ищущие заблокированные имена хостов в TLS-рукопожатиях.
<br>Для включения SNI break используйте <code>parent ... tls 0.0.0.0 0</code> и опцию <code>-s</code> на слушающем сервисе с TCP_NODELAY: <br>Для включения SNI break используйте <code>parent ... tls 0.0.0.0 0</code> и опцию <code>-B</code> на слушающем сервисе с TCP_NODELAY:
</p><pre> </p><pre>
auth iponly auth iponly
allow * allow *
parent 1000 tls 0.0.0.0 0 parent 1000 tls 0.0.0.0 0
allow * allow *
proxy -s -i127.0.0.1 -ocTCP_NODELAY -osTCP_NODELAY -p1443 proxy -B -i127.0.0.1 -ocTCP_NODELAY -osTCP_NODELAY -p1443
</pre> </pre>
<p> <p>
TCP_NODELAY необходим, чтобы ядро не объединяло разделенные пакеты. TCP_NODELAY необходим, чтобы ядро не объединяло разделенные пакеты.

View File

@ -190,6 +190,13 @@ any protocol-specific data.
.B -g(\fIGRACE_TRAFF\fB,\fIGRACE_NUM\fB,\fIGRACE_DELAY\fR) .B -g(\fIGRACE_TRAFF\fB,\fIGRACE_NUM\fB,\fIGRACE_DELAY\fR)
delay GRACE_DELAY milliseconds before polling if average polling size is below GRACE_TRAFF bytes and GRACE_NUM read operations in a single direction are detected within 1 second. Useful to minimize polling delay GRACE_DELAY milliseconds before polling if average polling size is below GRACE_TRAFF bytes and GRACE_NUM read operations in a single direction are detected within 1 second. Useful to minimize polling
.br .br
.B -B
(for TCP services) split the TLS ClientHello across two TCP segments in the
middle of the server name (SNI) when it is passed through tlspr, as with
\fBparent ... tls 0.0.0.0 0\fR: some DPI systems looking for blocked host names
do not reassemble it. Use with \fB-ocTCP_NODELAY -osTCP_NODELAY\fR, or the kernel
may join the segments again. In builds without splice() \fB-s\fR does the same.
.br
.B -s .B -s
(for admin) secure, allow only secure operations, currently only traffic counters (for admin) secure, allow only secure operations, currently only traffic counters
view without ability to reset. view without ability to reset.

View File

@ -76,8 +76,8 @@ destination_port. Port to establish outgoing connections. Required unless the Tr
.B -c .B -c
TLS_CHECK_LEVEL. 0 (default) - allow non-TLS traffic to pass, 1 - require TLS, only check client HELLO packet, 2 - require TLS, check both client and server HELLO, 3 - require TLS, check that the server sends a certificate (not compatible with TLS 1.3), 4 - require mutual TLS, check that the server sends a certificate request and the client sends a certificate (not compatible with TLS 1.3) TLS_CHECK_LEVEL. 0 (default) - allow non-TLS traffic to pass, 1 - require TLS, only check client HELLO packet, 2 - require TLS, check both client and server HELLO, 3 - require TLS, check that the server sends a certificate (not compatible with TLS 1.3), 4 - require mutual TLS, check that the server sends a certificate request and the client sends a certificate (not compatible with TLS 1.3)
.TP .TP
.B -s .B -B
Split the TLS Client HELLO packet across multiple TCP segments to make SNI-based DPI detection harder. An optional numeric value can follow (e.g. \fB-s1\fR) to control the splitting behaviour. Split the TLS Client HELLO packet across two TCP segments, in the middle of the SNI, to make SNI-based DPI detection harder. In builds without splice() \fB-s\fR does the same; with splice() (Linux) \fB-s\fR is splice, see \fB3proxy.cfg\fR(5).
.TP .TP
.B -Ximap | -Xpop3 | -Xsmtp .B -Ximap | -Xpop3 | -Xsmtp
STARTTLS mode for the given protocol. The proxy speaks the plaintext protocol STARTTLS mode for the given protocol. The proxy speaks the plaintext protocol

View File

@ -364,6 +364,8 @@ int MODULEMAINFUNC (int argc, char** argv){
" association: 1 - log, 2 - authorize, 3 - both (default), 0 (same as -U) -\n" " association: 1 - log, 2 - authorize, 3 - both (default), 0 (same as -U) -\n"
" neither. The first destination is always authorized and logged\n" " neither. The first destination is always authorized and logged\n"
#endif #endif
" -B split the TLS ClientHello passed through tlspr at the SNI (DPI bypass),\n"
" with -ocTCP_NODELAY -osTCP_NODELAY\n"
"-g(GRACE_TRAFF,GRACE_NUM,GRACE_DELAY) - delay GRACE_DELAY milliseconds before polling if average polling size below GRACE_TRAFF bytes and GRACE_NUM read operations in single directions are detected within 1 second to minimize polling\n" "-g(GRACE_TRAFF,GRACE_NUM,GRACE_DELAY) - delay GRACE_DELAY milliseconds before polling if average polling size below GRACE_TRAFF bytes and GRACE_NUM read operations in single directions are detected within 1 second to minimize polling\n"
" -fFORMAT logging format (see documentation)\n" " -fFORMAT logging format (see documentation)\n"
" -l log to stderr\n" " -l log to stderr\n"
@ -639,6 +641,13 @@ int MODULEMAINFUNC (int argc, char** argv){
case 'U': case 'U':
srv.udpauth = *(argv[i]+2)? atoi(argv[i]+2) : 0; srv.udpauth = *(argv[i]+2)? atoi(argv[i]+2) : 0;
break; break;
case 'B':
/* the TLS ClientHello split at the SNI (tlspr), which -s is
where -s is not splice; s_option means something else
for these */
if(!isudp && srv.service != S_ADMIN)
srv.s_option = 1 + atoi(argv[i]+2);
break;
case 's': case 's':
#ifdef WITHSPLICE #ifdef WITHSPLICE
if(isudp || srv.service == S_ADMIN) if(isudp || srv.service == S_ADMIN)