diff --git a/binaries/aarch64/tpws b/binaries/aarch64/tpws index 31f54f1..6c341d7 100755 Binary files a/binaries/aarch64/tpws and b/binaries/aarch64/tpws differ diff --git a/binaries/arm/tpws b/binaries/arm/tpws index 846289f..81790ab 100755 Binary files a/binaries/arm/tpws and b/binaries/arm/tpws differ diff --git a/binaries/freebsd-x64/tpws b/binaries/freebsd-x64/tpws index e3ce621..350e9de 100755 Binary files a/binaries/freebsd-x64/tpws and b/binaries/freebsd-x64/tpws differ diff --git a/binaries/mac64/tpws b/binaries/mac64/tpws index a22780c..6003be6 100755 Binary files a/binaries/mac64/tpws and b/binaries/mac64/tpws differ diff --git a/binaries/mips32r1-lsb/tpws b/binaries/mips32r1-lsb/tpws index 46e59a4..0542ddd 100755 Binary files a/binaries/mips32r1-lsb/tpws and b/binaries/mips32r1-lsb/tpws differ diff --git a/binaries/mips32r1-msb/tpws b/binaries/mips32r1-msb/tpws index 085945e..5163327 100755 Binary files a/binaries/mips32r1-msb/tpws and b/binaries/mips32r1-msb/tpws differ diff --git a/binaries/mips64r2-msb/tpws b/binaries/mips64r2-msb/tpws index 3b55be1..83e45c0 100755 Binary files a/binaries/mips64r2-msb/tpws and b/binaries/mips64r2-msb/tpws differ diff --git a/binaries/ppc/tpws b/binaries/ppc/tpws index d2095ce..abacd7d 100755 Binary files a/binaries/ppc/tpws and b/binaries/ppc/tpws differ diff --git a/binaries/x86/tpws b/binaries/x86/tpws index 4d14b14..dcdb75b 100755 Binary files a/binaries/x86/tpws and b/binaries/x86/tpws differ diff --git a/binaries/x86_64/tpws b/binaries/x86_64/tpws index de8b872..7ccd9fa 100755 Binary files a/binaries/x86_64/tpws and b/binaries/x86_64/tpws differ diff --git a/binaries/x86_64/tpws_wsl.tgz b/binaries/x86_64/tpws_wsl.tgz index e6575aa..1fc8fd2 100644 Binary files a/binaries/x86_64/tpws_wsl.tgz and b/binaries/x86_64/tpws_wsl.tgz differ diff --git a/docs/readme.eng.md b/docs/readme.eng.md index 8f5f11a..2650ea6 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -591,6 +591,8 @@ tpws is transparent proxy. --unixeol ; replace 0D0A to 0A --tlsrec=sni ; make 2 TLS records. split at SNI. don't split if SNI is not present. --tlsrec-pos= ; make 2 TLS records. split at specified pos + --mss= ; set client MSS. forces server to split messages but significantly decreases speed ! + --mss-pf=[~]port1[-port2] ; MSS port filter. ~ means negation --tamper-start=[n] ; start tampering only from specified outbound stream position. byte pos or block number ('n'). default is 0. --tamper-cutoff=[n] ; do not tamper anymore after specified outbound stream position. byte pos or block number ('n'). default is unlimited. --daemon ; daemonize @@ -664,6 +666,15 @@ This works fine in Linux and MacOS but unexpectedly in FreeBSD and OpenBSD but middleboxes such as CDNs and ddos guards - not always. Use of `--tlsrec` without filters is discouraged. +`--mss` sets TCP_MAXSEG socket option. Client sets this value in MSS TCP option in the SYN packet. +Server replies with it's own MSS in SYN,ACK packet. Usually servers lower their packet sizes but they still don't +fit to supplied MSS. The greater MSS client sets the bigger server's packets will be. +If it's enough to split TLS 1.2 ServerHello, it may fool DPI that checks certificate domain name. +This scheme may significantly lower speed. Hostlist and TLS version filters are not possible. +`--mss-pf` sets port filter for MSS. Use `mss-pf=443` to apply MSS only for https. +Likely not required for TLS1.3. If TLS1.3 is negotiable then MSS make things only worse. +Use only if nothing better is available. Works only in Linux, not BSD or MacOS. + ## Ways to get a list of blocked IP diff --git a/docs/readme.txt b/docs/readme.txt index 7fd680f..08ee599 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -672,6 +672,8 @@ tpws - это transparent proxy. --unixeol ; конвертировать 0D0A в 0A и использовать везде 0A --tlsrec=sni ; разбивка TLS ClientHello на 2 TLS records. режем между 1 и 2 символами hostname в SNI. Если SNI нет - отмена. --tlsrec-pos= ; разбивка TLS ClientHello на 2 TLS records. режем на указанной позиции, если длина слишком мелкая - на позиции 1. + --mss= ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость + --mss-pf=[~]port1[-port2] ; применять MSS только к портам назначения, подпадающим под фильтр. ~ означает инверсию --tamper-start=[n] ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока) --tamper-cutoff=[n] ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока) --hostlist= ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются. @@ -790,6 +792,21 @@ tpws полностью работает на асинхронных сокет Работает только с TLS 1.3, поскольку там эта информация шифруется. Впрочем, сейчас сайтов, не поддерживающих TLS 1.3, осталось немного. +--mss устанавливает опцию сокета TCP_MAXSEG. Клиент выдает это значение в tcp опциях SYN пакета. +Сервер в ответ в SYN,ACK выдает свой MSS. На практике сервера обычно снижают размеры отсылаемых ими пакетов, но они +все равно не вписываются в низкий MSS, указанный клиентом. Обычно чем больше указал клиент, тем больше +шлет сервер. На TLS 1.2 если сервер разбил заброс так, чтобы домен из сертификата не попал в первый пакет, +это может обмануть DPI, секущий ответ сервера. +Схема может значительно снизить скорость и сработать не на всех сайтах. +Несовместимо с фильтром по hostlist. Невозможен фильтр по версии TLS. +Взамен имеется фильтр по портам --mss-pf. --mss-pf=443 применяет дурение только к https. +Применяя данную опцию к сайтам TLS1.3, если броузер тоже поддерживает TLS1.3, то вы делаете только хуже. +Но нет способа автоматически узнать когда надо применять, когда нет, поскольку MSS идет только в +3-way handshake еще до обмены данными, а версию TLS можно узнать только по ответу сервера, который +может привести к реакции DPI. +Использовать только когда нет ничего лучше или для отдельных ресурсов. +Работает только на linux, не работает на BSD и MacOS. + --skip-nodelay может быть полезен, чтобы привести MTU к MTU системы, на которой работает tpws. Это может быть полезно для скрытия факта использования VPN. Пониженный MTU - 1 из способов обнаружения подозрительного подключения. С tcp proxy ваши соединения неотличимы от тех, что сделал бы сам шлюз. diff --git a/tpws/params.h b/tpws/params.h index b8a6f31..5744e5a 100644 --- a/tpws/params.h +++ b/tpws/params.h @@ -24,6 +24,12 @@ struct bind_s int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll; }; +typedef struct +{ + uint16_t from,to; + bool neg; +} port_filter; + struct params_s { struct bind_s binds[MAX_BINDS]; @@ -55,6 +61,9 @@ struct params_s uint8_t oob_byte; int ttl_default; + int mss; + port_filter mss_pf; + char pidfile[256]; strpool *hostlist, *hostlist_exclude; diff --git a/tpws/tpws.c b/tpws/tpws.c index d831b29..4b3ee6f 100644 --- a/tpws/tpws.c +++ b/tpws/tpws.c @@ -189,6 +189,10 @@ static void exithelp(void) " --unixeol\t\t\t\t; replace 0D0A to 0A\n" " --tlsrec=sni\t\t\t\t; make 2 TLS records. split at SNI. don't split if SNI is not present\n" " --tlsrec-pos=\t\t\t; make 2 TLS records. split at specified pos\n" +#ifdef __linux__ + " --mss=\t\t\t\t; set client MSS. forces server to split messages but significantly decreases speed !\n" + " --mss-pf=[~]port1[-port2]\t\t; MSS port filter. ~ means negation\n" +#endif " --tamper-start=[n]\t\t; start tampering only from specified outbound stream position. default is 0. 'n' means data block number.\n" " --tamper-cutoff=[n]\t\t; do not tamper anymore after specified outbound stream position. default is unlimited.\n", HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT @@ -324,6 +328,9 @@ void parse_params(int argc, char *argv[]) { "tamper-cutoff",required_argument,0,0 },// optidx=51 #if defined(BSD) && !defined(__OpenBSD__) && !defined(__APPLE__) { "enable-pf",no_argument,0,0 },// optidx=52 +#elif defined(__linux__) + { "mss",required_argument,0,0 },// optidx=52 + { "mss-pf",required_argument,0,0 },// optidx=53 #endif { "hostlist-auto-retrans-threshold",optional_argument,0,0}, // ignored. for nfqws command line compatibility { NULL,0,NULL,0 } @@ -723,6 +730,23 @@ void parse_params(int argc, char *argv[]) case 52: /* enable-pf */ params.pf_enable = true; break; +#elif defined(__linux__) + case 52: /* mss */ + // this option does not work in any BSD and MacOS. OS may accept but it changes nothing + params.mss = atoi(optarg); + if (params.mss<88 || params.mss>32767) + { + fprintf(stderr, "Invalid value for MSS. Linux accepts MSS 88-32767.\n"); + exit_clean(1); + } + break; + case 53: /* mss-pf */ + if (!pf_parse(optarg,¶ms.mss_pf)) + { + fprintf(stderr, "Invalid MSS port filter.\n"); + exit_clean(1); + } + break; #endif } } diff --git a/tpws/tpws_conn.c b/tpws/tpws_conn.c index 3c6fa45..5ac5bd5 100644 --- a/tpws/tpws_conn.c +++ b/tpws/tpws_conn.c @@ -20,7 +20,6 @@ #include "tpws_conn.h" #include "redirect.h" #include "tamper.h" -#include "params.h" #include "socks.h" #include "helpers.h" @@ -336,7 +335,7 @@ static bool proxy_remote_conn_ack(tproxy_conn_t *conn, int sock_err) //Returns -1 if something fails, >0 on success (socket fd). static int connect_remote(const struct sockaddr *remote_addr) { - int remote_fd = 0, yes = 1, no = 0; + int remote_fd = 0, yes = 1, no = 0, v; if((remote_fd = socket(remote_addr->sa_family, SOCK_STREAM, 0)) < 0) @@ -368,10 +367,28 @@ static int connect_remote(const struct sockaddr *remote_addr) } if (setsockopt(remote_fd, IPPROTO_TCP, TCP_NODELAY, params.skip_nodelay ? &no : &yes, sizeof(int)) <0) { - perror("setsockopt (SO_NODELAY, connect_remote)"); + perror("setsockopt (TCP_NODELAY, connect_remote)"); close(remote_fd); return -1; } + if (params.mss) + { + uint16_t port = saport(remote_addr); + if (pf_in_range(port,¶ms.mss_pf)) + { + VPRINT("Setting MSS %d",params.mss) + if (setsockopt(remote_fd, IPPROTO_TCP, TCP_MAXSEG, ¶ms.mss, sizeof(int)) <0) + { + perror("setsockopt (TCP_MAXSEG, connect_remote)"); + close(remote_fd); + return -1; + } + } + else + { + VPRINT("Not setting MSS. Port %u is out of MSS port range.",port) + } + } if(connect(remote_fd, remote_addr, remote_addr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) < 0) { if(errno != EINPROGRESS) @@ -1043,7 +1060,7 @@ static bool handle_epoll(tproxy_conn_t *conn, struct tailhead *conn_list, uint32 if (split_pos) { VPRINT("Splitting at pos %zu%s", split_pos, (split_flags & SPLIT_FLAG_DISORDER) ? " with disorder" : "") - if (split_flags && SPLIT_FLAG_OOB) + if (split_flags & SPLIT_FLAG_OOB) { VPRINT("Sending OOB byte %02X", params.oob_byte) uint8_t oob_save; @@ -1394,3 +1411,37 @@ ex: if (listen_conn) free(listen_conn); return retval; } + + +bool pf_in_range(uint16_t port, const port_filter *pf) +{ + return port && ((!pf->from && !pf->to || port>=pf->from && port<=pf->to) ^ pf->neg); +} +bool pf_parse(const char *s, port_filter *pf) +{ + unsigned int v1,v2; + + if (!s) return false; + if (*s=='~') + { + pf->neg=true; + s++; + } + else + pf->neg=false; + if (sscanf(s,"%u-%u",&v1,&v2)==2) + { + if (!v1 || v1>65535 || v2>65535 || v1>v2) return false; + pf->from=(uint16_t)v1; + pf->to=(uint16_t)v2; + } + else if (sscanf(s,"%u",&v1)==1) + { + if (!v1 || v1>65535) return false; + pf->to=pf->from=(uint16_t)v1; + } + else + return false; + return true; +} + diff --git a/tpws/tpws_conn.h b/tpws/tpws_conn.h index e279dea..45c2a6e 100644 --- a/tpws/tpws_conn.h +++ b/tpws/tpws_conn.h @@ -5,6 +5,7 @@ #include #include #include "tamper.h" +#include "params.h" #define BACKLOG 10 #define MAX_EPOLL_EVENTS 64 @@ -87,7 +88,7 @@ struct tproxy_conn struct send_buffer wr_buf[4]; t_ctrack track; - + //Create the struct which contains ptrs to next/prev element TAILQ_ENTRY(tproxy_conn) conn_ptrs; }; @@ -99,3 +100,6 @@ TAILQ_HEAD(tailhead, tproxy_conn); bool set_socket_buffers(int fd, int rcvbuf, int sndbuf); + +bool pf_in_range(uint16_t port, const port_filter *pf); +bool pf_parse(const char *s, port_filter *pf);