mirror of
https://github.com/3proxy/3proxy.git
synced 2026-11-16 06:05:50 +08:00
Compare commits
4 Commits
f4c5c723bd
...
359ce5f8b4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
359ce5f8b4 | ||
|
|
65213b172d | ||
|
|
237a4499fa | ||
|
|
2d06b4672a |
@ -648,16 +648,16 @@ 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).
|
||||
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.
|
||||
<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:
|
||||
</p><pre>
|
||||
auth iponly
|
||||
allow *
|
||||
parent 1000 tls 0.0.0.0 0
|
||||
allow *
|
||||
proxy -s -i127.0.0.1 -ocTCP_NODELAY -osTCP_NODELAY -p1443
|
||||
proxy -B -i127.0.0.1 -p1443
|
||||
</pre>
|
||||
<p>
|
||||
TCP_NODELAY is required to prevent the kernel from merging the split packets.
|
||||
TCP_NODELAY, set by default, is required to prevent the kernel from merging the split packets: do not turn it off with -ocTCP_NODELAY=0 or -osTCP_NODELAY=0.
|
||||
</p>
|
||||
<p>
|
||||
<b>Configuration examples:</b>
|
||||
|
||||
@ -656,16 +656,16 @@ iptables -t nat -A PREROUTING -p tcp -m multiport --dports 443,993,465 -j REDIRE
|
||||
<br>tlspr может использоваться как родительский прокси типа "tls" для реализации SNI-фрагментации (аналог NoDPI/GoodByeDPI).
|
||||
Клиент отправляет первую часть TLS ClientHello, tlspr разбивает его на расширении SNI и отправляет двумя TCP-пакетами,
|
||||
что позволяет обойти некоторые 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> на слушающем сервисе:
|
||||
</p><pre>
|
||||
auth iponly
|
||||
allow *
|
||||
parent 1000 tls 0.0.0.0 0
|
||||
allow *
|
||||
proxy -s -i127.0.0.1 -ocTCP_NODELAY -osTCP_NODELAY -p1443
|
||||
proxy -B -i127.0.0.1 -p1443
|
||||
</pre>
|
||||
<p>
|
||||
TCP_NODELAY необходим, чтобы ядро не объединяло разделенные пакеты.
|
||||
TCP_NODELAY, установленный по умолчанию, необходим, чтобы ядро не объединяло разделенные пакеты: не отключайте его опциями -ocTCP_NODELAY=0 или -osTCP_NODELAY=0.
|
||||
</p>
|
||||
<p>
|
||||
<b>Примеры конфигурации:</b>
|
||||
|
||||
@ -198,6 +198,13 @@ any protocol-specific data.
|
||||
.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
|
||||
.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. TCP_NODELAY, set by default, must not be turned off, or the
|
||||
kernel may join the segments again. In builds without splice() \fB-s\fR does the same.
|
||||
.br
|
||||
.B -s
|
||||
(for admin) secure, allow only secure operations, currently only traffic counters
|
||||
view without ability to reset.
|
||||
|
||||
@ -76,8 +76,8 @@ destination_port. Port to establish outgoing connections. Required unless the Tr
|
||||
.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)
|
||||
.TP
|
||||
.B -s
|
||||
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.
|
||||
.B -B
|
||||
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
|
||||
.B -Ximap | -Xpop3 | -Xsmtp
|
||||
STARTTLS mode for the given protocol. The proxy speaks the plaintext protocol
|
||||
|
||||
36
src/common.c
36
src/common.c
@ -649,6 +649,26 @@ int parseconnusername(char *username, struct clientparam *param, int extpasswd,
|
||||
}
|
||||
|
||||
|
||||
/* What waits for a socket waits in sockpoll(): a call on it never does. */
|
||||
void nonblock(SOCKET sock){
|
||||
#ifdef _WIN32
|
||||
unsigned long ul = 1;
|
||||
ioctlsocket(sock, FIONBIO, &ul);
|
||||
#else
|
||||
fcntl(sock, F_SETFL, O_NONBLOCK | fcntl(sock, F_GETFL));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* accept() with nothing to accept: another took it, or it was gone */
|
||||
int acceptagain(void){
|
||||
#ifdef _WIN32
|
||||
int err = WSAGetLastError();
|
||||
return err == WSAEWOULDBLOCK || err == WSAECONNRESET || err == WSAEINTR;
|
||||
#else
|
||||
return errno == EAGAIN || errno == EWOULDBLOCK || errno == ECONNABORTED || errno == EINTR;
|
||||
#endif
|
||||
}
|
||||
|
||||
int connectwithpoll(struct clientparam *param, SOCKET sock, struct sockaddr *sa, SASIZETYPE size, int to){
|
||||
struct pollfd fds[1];
|
||||
#ifdef _WIN32
|
||||
@ -775,6 +795,22 @@ int doconnect(struct clientparam * param){
|
||||
param->srv->so._setsockopt(param->sostate, param->remsock, SOL_SOCKET, SO_LINGER, (char *)&lg, sizeof(lg));
|
||||
#endif
|
||||
if(extbindtodevice(param, param->remsock)) return 12;
|
||||
#ifdef IP_BIND_ADDRESS_NO_PORT
|
||||
/* A port the kernel chooses is chosen by connect(), for the pair with
|
||||
the destination: one in use towards another destination, or closing
|
||||
from an earlier connection, does as well. bind() would take one of
|
||||
its own, and with many connections find none left, or search long
|
||||
for it. */
|
||||
if((param->operation >= 256 || (param->operation & CONNECT) || param->redirected)
|
||||
# ifdef WITH_UN
|
||||
&& *SAFAMILY(¶m->sinsl) != AF_UNIX
|
||||
# endif
|
||||
){
|
||||
int one = 1;
|
||||
|
||||
param->srv->so._setsockopt(param->sostate, param->remsock, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, (char *)&one, sizeof(one));
|
||||
}
|
||||
#endif
|
||||
#ifdef WITH_UN
|
||||
if(*SAFAMILY(¶m->sinsl) != AF_UNIX)
|
||||
#endif
|
||||
|
||||
@ -132,7 +132,7 @@ void * ftpprchild(struct clientparam* param) {
|
||||
param->srv->so._closesocket(param->sostate, clidatasock);
|
||||
clidatasock = INVALID_SOCKET;
|
||||
}
|
||||
if ((clidatasock=socket(SASOCK(¶m->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);}
|
||||
if ((clidatasock=nssocket(param->srv, ¶m->srv->so, param->sostate, NS_IN, SASOCK(¶m->sincl), SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {RETURN(821);}
|
||||
if(intbindtodevice(param, clidatasock)){RETURN(822);}
|
||||
*SAPORT(¶m->sincl) = 0;
|
||||
if(bindwithrange(param, clidatasock, ¶m->sincl, param->intport)){RETURN(822);}
|
||||
|
||||
@ -54,6 +54,8 @@ static void serveparam(struct clientparam *param){
|
||||
}
|
||||
param->remsock = param->srv->so._accept(param->sostate, param->srv->cbsock, (struct sockaddr*)¶m->sinsr, &size);
|
||||
if(param->remsock == INVALID_SOCKET) {
|
||||
/* another client of the service took it: not a try */
|
||||
if(acceptagain()) { i++; continue; }
|
||||
dolog(param, (unsigned char *)"Connect back accept() failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -221,6 +221,7 @@ static int srvprepare(struct srvstart *s, int argc, char **argv){
|
||||
" association: 1 - log, 2 - authorize, 3 - both (default), 0 (same as -U) -\n"
|
||||
" neither. The first destination is always authorized and logged\n"
|
||||
#endif
|
||||
" -B split the TLS ClientHello passed through tlspr at the SNI (DPI bypass)\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"
|
||||
" -l log to stdout\n"
|
||||
@ -486,6 +487,13 @@ static int srvprepare(struct srvstart *s, int argc, char **argv){
|
||||
case 'U':
|
||||
srv->udpauth = *(argv[i]+2)? atoi(argv[i]+2) : 0;
|
||||
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(!s->isudp && srv->service != S_ADMIN && srv->service != S_HTTPSRV)
|
||||
srv->s_option = 1 + atoi(argv[i]+2);
|
||||
break;
|
||||
case 's':
|
||||
#ifdef WITHSPLICE
|
||||
/* admin is an httpsrv, where -s is the limited mode */
|
||||
|
||||
@ -23,6 +23,8 @@ static int cblisten(struct srvstart *s){
|
||||
return -6;
|
||||
}
|
||||
setopts(srv->cbsock, &srv->cbssockopts, srv->maxseg);
|
||||
/* the clients of the service all wait for it: one accepts what comes */
|
||||
nonblock(srv->cbsock);
|
||||
|
||||
if(srv->so._bind(srv->so.state, srv->cbsock, (struct sockaddr*)&cbsa, SASIZE(&cbsa))==-1) {
|
||||
dolog(defparam, (unsigned char *)"Failed to bind connect back socket");
|
||||
|
||||
@ -332,6 +332,8 @@ int nsbroken(void);
|
||||
/* a function everywhere, never a macro: its callers have preprocessor
|
||||
lines among its arguments */
|
||||
SOCKET nssocket(struct srvparam *srv, struct sockfuncs *sf, void *state, int side, int domain, int type, int protocol);
|
||||
void nonblock(SOCKET sock);
|
||||
int acceptagain(void);
|
||||
int socksend(struct clientparam *param, SOCKET sock, unsigned char * buf, int bufsize, int to);
|
||||
int socksendto(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to);
|
||||
int sockrecvfrom(struct clientparam *param, SOCKET sock, struct sockaddr * sin, unsigned char * buf, int bufsize, int to);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user