From c94766dff7052273cf50d3224803a0cb6be15cd9 Mon Sep 17 00:00:00 2001 From: Vladimir Dubrovin <3proxy@3proxy.ru> Date: Wed, 22 Jul 2026 17:34:51 +0300 Subject: [PATCH] Fix: dnspr was broken since udppm rewrite. -F (fake resolve to redirect traffic) option added to dnspr --- doc/html/howtoe.html | 50 +++++++++++++++++++++++- doc/html/howtor.html | 90 ++++++++++++++++++++++++++++++++++++++++++++ src/conf.c | 2 +- src/dnspr.c | 23 +++++++++-- src/proxymain.c | 12 ++++++ src/structures.h | 2 + 6 files changed, 173 insertions(+), 6 deletions(-) diff --git a/doc/html/howtoe.html b/doc/html/howtoe.html index a3f24de..ad60364 100644 --- a/doc/html/howtoe.html +++ b/doc/html/howtoe.html @@ -33,6 +33,7 @@
Implicit vs explicit TLS:
-Implicit TLS means TLS from the first byte of the connection: https (443), imaps (993), submissions (465). -Explicit TLS means the connection starts in plaintext and is upgraded with a STARTTLS/STLS command: imap (143), submission (587), pop3 (110). +Implicit TLS means TLS from the first byte of the connection: https (443), imaps (993), SMTP submission secure (submissions) (465). +Explicit TLS means the connection starts in plaintext and is upgraded with a STARTTLS/STLS command: imap (143), SMTP submission (587), pop3 (110). tlspr supports both: for implicit TLS the destination host is taken from SNI and the port from -P; for explicit TLS the -X option makes tlspr speak the plaintext protocol phase with the client (greeting, STARTTLS command) before upgrading both sides to TLS. Example: @@ -637,6 +638,10 @@ with, for example, iptables: iptables -t nat -A PREROUTING -p tcp -m multiport --dports 443,993,465 -j REDIRECT --to-port 1443
+Alternatively, if clients use dnspr as their DNS resolver, traffic can be redirected via DNS — +see How to set up a DNS proxy (dnspr). +
+
SNI Break (DPI Bypass):
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,
@@ -681,6 +686,47 @@ parent 1000 tls 0.0.0.0 0
deny * * blocked.example.com
allow *
proxy
+
+
+dnspr is a DNS forwarding service. It answers queries from the 3proxy name cache (including static nsrecord +entries), forwards other queries to the resolvers configured with nserver, and caches the results if +nscache/nscache6 are configured. Options: +
+-s - simple DNS forwarding: do not use 3proxy resolver / name cache +-F<ip> - fake: answer all A (or AAAA) queries with the given IP address. + May be given twice - once with an IPv4 and once with an IPv6 address. ++
+dnspr can be used to redirect traffic by hostname, e.g. to point mail clients to a +tlspr installation, by adding nsrecord entries for the service hostnames +(nscache/nscache6 keep the answers for all other names intact): +
+nscache 65536 +nscache6 65536 +dnspr -p53 + +# google +nsrecord smtp.gmail.com 10.0.0.1 +nsrecord imap.gmail.com 10.0.0.1 +nsrecord pop.gmail.com 10.0.0.1 +# mail.ru +nsrecord smtp.mail.ru 10.0.0.1 +nsrecord imap.mail.ru 10.0.0.1 +nsrecord pop.mail.ru 10.0.0.1 +# yandex.ru +nsrecord smtp.yandex.ru 10.0.0.1 +nsrecord imap.yandex.ru 10.0.0.1 +nsrecord pop.yandex.ru 10.0.0.1 ++
+where 10.0.0.1 is the address tlspr listens on. If every hostname must be redirected, a single fake +address can be used instead of individual nsrecords: +
+nscache 65536 +nscache6 65536 +dnspr -p53 -F10.0.0.1
Опции:
@@ -600,8 +602,54 @@ 2 - требовать TLS, проверять наличие client и server HELLO 3 - требовать TLS, проверять наличие серверного сертификата (не совместим с TLS 1.3+) 4 - требовать взаимный (mutual) TLS, проверять что сервер запрашивает сертификат и клиент его отправляет (не совместим с TLS 1.3+) +-Ximap | -Xpop3 | -Xsmtp - режим explicit TLS (STARTTLS): tlspr говорит с клиентом + на plaintext-протоколе (приветствие, команда STARTTLS), затем поднимает TLS с обеих сторон
+Implicit и explicit TLS: +
+Implicit TLS означает TLS с первого байта соединения: https (443), imaps (993), SMTP submission secure (submissions) (465). +Explicit TLS означает, что соединение начинается в plaintext и поднимается до TLS командой STARTTLS/STLS: imap (143), SMTP submission (587), pop3 (110). +tlspr поддерживает оба варианта: для implicit TLS хост назначения берется из SNI, а порт из -P; для explicit TLS +опция -X заставляет tlspr говорить с клиентом на plaintext-фазе протокола (приветствие, команда STARTTLS) перед +поднятием TLS с обеих сторон. Пример: +
+# https (implicit) +tlspr -p443 -P443 -c1 +# imaps (implicit) +tlspr -p993 -P993 -c1 +# submissions (implicit) +tlspr -p465 -P465 -c1 +# imap STARTTLS (explicit) +tlspr -p143 -P143 -Ximap +# submission STARTTLS (explicit) +tlspr -p587 -P587 -Xsmtp +# pop3 STLS (explicit) +tlspr -p110 -P110 -Xpop3 ++
+Примечание: для explicit TLS вместо tlspr -X можно использовать протокольные сервисы smtpp, imapp и pop3p; они +также поддерживают STARTTLS. +
+Рекомендуемая конфигурация - перенаправление трафика: наиболее универсальная конфигурация - перенаправлять +TLS-трафик (порты 443, 993, 465, 143, 587, 110) на tlspr правилами внешнего маршрутизатора или локальными +правилами (плагин Transparent). Исходные адрес и порт назначения сохраняются, -P и отдельные сервисы для каждого +порта не требуются, implicit и explicit TLS работают в рамках одной установки. +
+auth iponly +allow * +tlspr -p1443 -c1 ++
+например, с iptables: +
+iptables -t nat -A PREROUTING -p tcp -m multiport --dports 443,993,465 -j REDIRECT --to-port 1443 ++
+Также, если клиенты используют dnspr в качестве DNS-резолвера, трафик можно перенаправлять через DNS — +см. Как настроить DNS proxy (dnspr). +
+
SNI Break (обход DPI):
tlspr может использоваться как родительский прокси типа "tls" для реализации SNI-фрагментации (аналог NoDPI/GoodByeDPI).
Клиент отправляет первую часть TLS ClientHello, tlspr разбивает его на расширении SNI и отправляет двумя TCP-пакетами,
@@ -649,6 +697,48 @@ proxy
+dnspr - сервис пересылки DNS-запросов. Он отвечает на запросы из кэша имен 3proxy (включая статические записи +nsrecord), пересылает остальные запросы на резолверы, заданные командой nserver, и кэширует результаты, если +настроены nscache/nscache6. Опции: +
+-s - простая пересылка DNS: не использовать резолвер / кэш имен 3proxy +-F<ip> - fake: отвечать на все A (или AAAA) запросы заданным IP-адресом. + Может быть указана дважды - один раз с IPv4 и один раз с IPv6 адресом. ++
+dnspr можно использовать для перенаправления трафика по имени хоста, например, чтобы направить почтовых +клиентов на установку tlspr, добавив записи nsrecord для имен почтовых сервисов +(nscache/nscache6 сохраняют ответы для всех остальных имен): +
+nscache 65536 +nscache6 65536 +dnspr -p53 + +# google +nsrecord smtp.gmail.com 10.0.0.1 +nsrecord imap.gmail.com 10.0.0.1 +nsrecord pop.gmail.com 10.0.0.1 +# mail.ru +nsrecord smtp.mail.ru 10.0.0.1 +nsrecord imap.mail.ru 10.0.0.1 +nsrecord pop.mail.ru 10.0.0.1 +# yandex.ru +nsrecord smtp.yandex.ru 10.0.0.1 +nsrecord imap.yandex.ru 10.0.0.1 +nsrecord pop.yandex.ru 10.0.0.1 ++
+где 10.0.0.1 - адрес, на котором слушает tlspr. Если перенаправлять нужно все имена, вместо отдельных +записей nsrecord можно использовать один fake-адрес: +
+nscache 65536 +nscache6 65536 +dnspr -p53 -F10.0.0.1 ++ +
Начиная с версии 0.9.7 поддержка TLS/SSL встроена в 3proxy при компиляции с OpenSSL diff --git a/src/conf.c b/src/conf.c index f418be6..8f2e10f 100644 --- a/src/conf.c +++ b/src/conf.c @@ -264,7 +264,7 @@ static int h_proxy(int argc, unsigned char ** argv){ childdef.port = 53; childdef.isudp = 1; childdef.service = S_DNSPR; - childdef.helpmessage = " -s - simple DNS forwarding - do not use 3proxy resolver / name cache\n"; + childdef.helpmessage = " -s - simple DNS forwarding - do not use 3proxy resolver / name cache\n -Fip - fake: answer all A queries with this IP\n"; #ifndef NOIPV6 if(!resolvfunc || (resolvfunc == myresolver && !dns_table.poolsize) || resolvfunc == fakeresolver){ fprintf(stderr, "[line %d] Warning: no nserver/nscache configured, dnspr will not work as expected\n", linenum); diff --git a/src/dnspr.c b/src/dnspr.c index 532a13a..06f596d 100644 --- a/src/dnspr.c +++ b/src/dnspr.c @@ -25,6 +25,7 @@ void * dnsprchild(struct clientparam* param) { SASIZETYPE size; int res, i; int len; + int semlocked = 1; unsigned type=0; uint32_t ttl; unsigned char addr[16]; @@ -38,10 +39,13 @@ void * dnsprchild(struct clientparam* param) { RETURN (21); } buf = bbuf+2; - size = sizeof(param->sincr); - i = param->srv->so._recvfrom(param->sostate, param->srv->srvsock, (char *)buf, BUFSIZE, 0, (struct sockaddr *)¶m->sincr, &size); size = sizeof(param->sinsl); getsockname(param->srv->srvsock, (struct sockaddr *)¶m->sincl, &size); + i = param->srv->udplen; + if(i > BUFSIZE) i = BUFSIZE; + memcpy(buf, param->srv->udpbuf, i); + _3proxy_sem_unlock(udpinit); + semlocked = 0; #ifdef _WIN32 if((param->clisock=param->srv->so._socket(param->sostate, AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET) { RETURN(818); @@ -82,7 +86,19 @@ void * dnsprchild(struct clientparam* param) { *s2 = (len - (int)(s2 - buf)) - 1; type = ((unsigned)buf[len+1])*256 + (unsigned)buf[len+2]; - if((type==0x01 || type==0x1c) && !param->srv->s_option){ + if(type==0x01 && param->srv->fakeip){ + ip = 1; + ttl = 3600; + *(uint32_t *)addr = param->srv->fakeip; + } +#ifndef NOIPV6 + else if(type==0x1c && *(uint32_t *)param->srv->fakeip6){ + ip = 1; + ttl = 3600; + memcpy(addr, param->srv->fakeip6, 16); + } +#endif + else if((type==0x01 || type==0x1c) && !param->srv->s_option){ ip = udpresolve((type==0x1c)?AF_INET6:AF_INET, (unsigned char *)host, addr, &ttl, param, 0); } @@ -191,6 +207,7 @@ void * dnsprchild(struct clientparam* param) { CLEANRET: + if(semlocked) _3proxy_sem_unlock(udpinit); if(param->res!=813){ sprintf((char *)buf, "%04x/%s/", (unsigned)type, diff --git a/src/proxymain.c b/src/proxymain.c index 94e4e61..ab1fad4 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -537,6 +537,18 @@ int MODULEMAINFUNC (int argc, char** argv){ else if(!strncasecmp(argv[i]+2, "smtp", 4)) srv.srvstarttls = S_SMTPP; else error = 1; break; + case 'F': + { + PROXYSOCKADDRTYPE fsa; + memset(&fsa, 0, sizeof(fsa)); + if(!getip46(46, (unsigned char *)argv[i]+2, (struct sockaddr *)&fsa)) error = 1; + else if(*SAFAMILY(&fsa) == AF_INET) srv.fakeip = *(uint32_t *)SAADDR(&fsa); +#ifndef NOIPV6 + else if(*SAFAMILY(&fsa) == AF_INET6) memcpy(srv.fakeip6, SAADDR(&fsa), 16); +#endif + else error = 1; + } + break; case 'T': srv.transparent = 1; break; diff --git a/src/structures.h b/src/structures.h index 50d56fa..ba92283 100644 --- a/src/structures.h +++ b/src/structures.h @@ -539,6 +539,8 @@ struct srvparam { int haproxy; int nostarttls; PROXYSERVICE srvstarttls; + uint32_t fakeip; + unsigned char fakeip6[16]; #ifdef WITHSPLICE int usesplice; #endif