Compare commits

...

6 Commits

Author SHA1 Message Date
bol-van
dcae31fae8 readme: typo 2024-06-18 18:03:19 +03:00
bol-van
223895fffb update blockcheck 2024-06-18 17:42:35 +03:00
bol-van
4189becb02 update bins 2024-06-18 17:42:15 +03:00
bol-van
a39a6226bd docs update 2024-06-18 17:41:22 +03:00
bol-van
dcf6bf8cc7 nfqws: split-http-req,split-tls 2024-06-18 17:40:56 +03:00
bol-van
d43d88b9d0 tpws: unify http and tls position finders 2024-06-18 17:38:13 +03:00
37 changed files with 420 additions and 180 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -882,7 +882,28 @@ pktws_check_domain_http_bypass_()
ok=1
}
[ "$ret" != 0 -o "$SCANLEVEL" = force ] && {
[ "$sec" = 0 ] && pktws_curl_test_update $1 $3 $s --hostcase && [ "$SCANLEVEL" = quick ] && return
[ "$sec" = 0 ] && {
pktws_curl_test_update $1 $3 $s --hostcase && {
[ "$SCANLEVEL" = quick ] && return
ok=1
}
for pos in method host; do
for hostcase in '' '--hostcase'; do
pktws_curl_test_update $1 $3 $s --dpi-desync-split-http-req=$pos $hostcase && {
[ "$SCANLEVEL" = quick ] && return
ok=1
}
done
done
}
[ "$sec" = 1 ] && {
for pos in sni sniext; do
pktws_curl_test_update $1 $3 $s --dpi-desync-split-tls=$pos && {
[ "$SCANLEVEL" = quick ] && return
ok=1
}
done
}
for pos in 1 3 4 5 10 50; do
s="--dpi-desync=split2 --dpi-desync-split-pos=$pos"
if pktws_curl_test_update $1 $3 $s; then
@ -1047,6 +1068,13 @@ tpws_check_domain_http_bypass_()
for mss in '' 88; do
s3=${mss:+--mss=$mss --mss-pf=$HTTPS_PORT}
for s2 in '' '--oob' '--disorder' '--oob --disorder'; do
for pos in sni sniext; do
s="--split-tls=$pos"
tpws_curl_test_update $1 $3 $s $s2 $s3 && warn_mss $s3 && [ "$SCANLEVEL" != force ] && {
[ "$SCANLEVEL" = quick ] && return
break
}
done
for pos in 1 2 3 4 5 10 50; do
s="--split-pos=$pos"
tpws_curl_test_update $1 $3 $s $s2 $s3 && warn_mss $s3 && [ "$SCANLEVEL" != force ] && {
@ -1055,8 +1083,8 @@ tpws_check_domain_http_bypass_()
}
done
done
for s2 in '--tlsrec=sni' '--tlsrec=sni --split-pos=10' '--tlsrec=sni --split-pos=10 --oob' \
'--tlsrec=sni --split-pos=10 --disorder' '--tlsrec=sni --split-pos=10 --oob --disorder' \
for s2 in '--tlsrec=sni' '--tlsrec=sni --split-tls=sni' '--tlsrec=sni --split-tls=sni --oob' \
'--tlsrec=sni --split-tls=sni --disorder' '--tlsrec=sni --split-tls=sni --oob --disorder' \
'--tlsrec=sni --split-pos=1' '--tlsrec=sni --split-pos=1 --oob' '--tlsrec=sni --split-pos=1 --disorder' \
'--tlsrec=sni --split-pos=1 --oob --disorder'; do
tpws_curl_test_update $1 $3 $s2 $s3 && warn_mss $s3 && [ "$SCANLEVEL" != force ] && {

View File

@ -287,3 +287,10 @@ nfqws: --qnum is mandatory, no more default queue 0
v58
winws
v59
tpws: --split-tls
tpws: --tlsrec=sniext
nfqws: --dpi-desync-split-http-req, --dpi-desync-split-tls. multi segment TLS support for split.
blockcheck: mdig dns cache

View File

@ -178,6 +178,8 @@ nfqws takes the following parameters:
--dpi-desync-repeats=<N> ; send every desync packet N times
--dpi-desync-skip-nosni=0|1 ; 1(default)=do not act on ClientHello without SNI (ESNI ?)
--dpi-desync-split-pos=<1..9216> ; data payload split position
--dpi-desync-split-http-req=method|host ; split at specified logical part of plain http request
--dpi-desync-split-tls=sni|sniext ; split at specified logical part of TLS ClientHello
--dpi-desync-ipfrag-pos-tcp=<8..9216> ; ip frag position starting from the transport header. multiple of 8, default 8.
--dpi-desync-ipfrag-pos-udp=<8..9216> ; ip frag position starting from the transport header. multiple of 8, default 32.
--dpi-desync-badseq-increment=<int|0xHEX> ; badseq fooling seq signed increment. default -10000
@ -467,6 +469,12 @@ If nfqws receives a partial ClientHello it begins reassemble session. Packets ar
Then the first packet goes through desync using fully reassembled message. Other packets are sent
without desync. On any error reassemble is cancelled and all delayed packets are sent immediately without desync.
There is special support for all tcp split options for multi segment TLS. Split position is treated
as message-oriented, not packet oriented. For example, if your client sends TLS ClientHello with size 2000
and SNI is at 1700, desync mode is fake,split2, then fake is sent first, then original first segment
and the last splitted segment. 3 segments total.
### UDP support
UDP attacks are limited. Its not possible to fragment UDP on transport level, only on network (ip) level.
@ -587,6 +595,7 @@ tpws is transparent proxy.
--hostlist-auto-debug=<logfile> ; debug auto hostlist positives
--split-http-req=method|host ; split http request at specified logical position.
--split-tls=sni|sniext ; split at specified logical part of TLS ClientHello
--split-pos=<numeric_offset> ; split at specified pos. split-http-req takes precedence over split-pos for http reqs.
--split-any-protocol ; split not only http and https
--disorder[=http|tls] ; when splitting simulate sending second fragment first
@ -602,7 +611,7 @@ tpws is transparent proxy.
--methodspace ; add extra space after method
--methodeol ; add end-of-line before method
--unixeol ; replace 0D0A to 0A
--tlsrec=sni ; make 2 TLS records. split at SNI. don't split if SNI is not present.
--tlsrec=sni|sniext ; make 2 TLS records. split at specified logical part. don't split if SNI is not present.
--tlsrec-pos=<pos> ; make 2 TLS records. split at specified pos
--mss=<int> ; set client MSS. forces server to split messages but significantly decreases speed !
--mss-pf=[~]port1[-port2] ; MSS port filter. ~ means negation

View File

@ -1,4 +1,4 @@
zapret v.58
zapret v.59
English
-------
@ -251,6 +251,8 @@ nfqws
--dpi-desync-repeats=<N> ; посылать каждый генерируемый в nfqws пакет N раз (не влияет на остальные пакеты)
--dpi-desync-skip-nosni=0| 1 ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI
--dpi-desync-split-pos=<1..1500> ; (только для split*, disorder*) разбивать пакет на указанной позиции
--dpi-desync-split-http-req=method|host ; разбивка http request на указанном логическом месте
--dpi-desync-split-tls=sni|sniext ; разбивка tls client hello на указанном логическом месте
--dpi-desync-badseq-increment=<int|0xHEX> ; инкремент sequence number для badseq. по умолчанию -10000
--dpi-desync-badack-increment=<int|0xHEX> ; инкремент ack sequence number для badseq. по умолчанию -66000
--dpi-desync-any-protocol=0|1 ; 0(default)=работать только по http request и tls clienthello 1=по всем непустым пакетам данных
@ -538,6 +540,13 @@ chrome рандомизирует фингерпринт TLS. SNI может о
на основании полностью собранного ClientHello. Остальные пакеты отсылаются без десинхронизации.
При любой ошибке в процессе сборки задержанные пакеты немедленно отсылаются в сеть, а десинхронизация отменяется.
Есть специальная поддержка всех вариантов tcp сплита для многосегментного TLS.
Если указать позицию сплита больше длины первого пакета или использовать --dpi-desync-split-tls,
то разбивка происходит не обязательно первого пакета, а того, на который пришлась итоговая позиция.
Если, допустим, клиент послал TLS ClientHello длиной 2000, а SNI начинается с 1700,
и заданы опции fake,split2, то перед первым пакетом идет fake, затем первый пакет в оригинале,
а последний пакет разбивается на 2 сегмента. В итоге имеем фейк в начале и 3 реальных сегмента.
ПОДДЕРЖКА UDP
Атаки на udp более ограничены в возможностях. udp нельзя фрагментировать иначе, чем на уровне ip.
Для UDP действуют только режимы десинхронизации fake,hopbyhop,destopt,ipfrag1,ipfrag2,udplen,tamper.
@ -676,7 +685,7 @@ tpws - это transparent proxy.
--methodspace ; добавить пробел после метода : "GET /" => "GET /"
--methodeol ; добавить перевод строки перед методом : "GET /" => "\r\nGET /"
--unixeol ; конвертировать 0D0A в 0A и использовать везде 0A
--tlsrec=sni ; разбивка TLS ClientHello на 2 TLS records. режем между 1 и 2 символами hostname в SNI. Если SNI нет - отмена.
--tlsrec=sni|sniext ; разбивка TLS ClientHello на 2 TLS records. режем между 1 и 2 символами hostname в SNI или между байтами длины SNI extension. Если SNI нет - отмена.
--tlsrec-pos=<pos> ; разбивка TLS ClientHello на 2 TLS records. режем на указанной позиции, если длина слишком мелкая - на позиции 1.
--mss=<int> ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость
--mss-pf=[~]port1[-port2] ; применять MSS только к портам назначения, подпадающим под фильтр. ~ означает инверсию

View File

@ -437,7 +437,27 @@ static bool process_desync_interval(t_ctrack *ctrack)
static bool replay_queue(struct rawpacket_tailhead *q);
static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t transport_len, uint8_t *data_payload, size_t len_payload)
static size_t pos_normalize(size_t split_pos, size_t reasm_offset, size_t len_payload)
{
size_t rsplit_pos = split_pos;
// normalize split pos to current packet
split_pos=(split_pos>reasm_offset && (split_pos-reasm_offset)<len_payload) ? split_pos-reasm_offset : 0;
if (rsplit_pos)
{
if (split_pos==rsplit_pos)
DLOG("split pos %zu\n",split_pos)
else
{
if (split_pos)
DLOG("split pos was normalized to packet data offset : %zu -> %zu\n",rsplit_pos,split_pos)
else
DLOG("split pos %zu is outside of this packet %zu-%zu\n",rsplit_pos,reasm_offset,reasm_offset+len_payload)
}
}
return split_pos;
}
static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t transport_len, uint8_t *data_payload, size_t len_payload)
{
uint8_t verdict=VERDICT_PASS;
@ -634,7 +654,8 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
uint8_t *p, *phost;
const uint8_t *rdata_payload = data_payload;
size_t rlen_payload = len_payload;
size_t split_pos;
if (replay)
{
rdata_payload = ctrack_replay->reasm_orig.packet;
@ -655,7 +676,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
// we do not reassemble http
reasm_orig_cancel(ctrack);
forced_wssize_cutoff(ctrack);
fake = params.fake_http;
fake_size = params.fake_http_size;
@ -679,6 +700,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
DLOG("req retrans : tcp seq interval %u-%u\n",ctrack->req_seq_start,ctrack->req_seq_end);
}
}
split_pos = HttpPos(params.desync_split_http_req, params.desync_split_pos, rdata_payload, rlen_payload);
bKnownProtocol = true;
}
else if (IsTLSClientHello(rdata_payload,rlen_payload,TLS_PARTIALS_ENABLE))
@ -722,9 +744,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
if (!ReasmIsEmpty(&ctrack->reasm_orig))
{
verdict_tcp_csum_fix(verdict, tcphdr, transport_len, ip, ip6hdr);
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, data_pkt, *len_pkt))
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, data_pkt, *len_pkt, len_payload))
{
DLOG("DELAY desync until reasm is complete (%u)\n", rawpacket_queue_count(&ctrack->delayed));
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
}
else
{
@ -750,8 +772,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
fake = params.fake_tls;
fake_size = params.fake_tls_size;
split_pos = TLSPos(params.desync_split_tls, params.desync_split_pos, rdata_payload, rlen_payload, 0);
bKnownProtocol = true;
}
else
split_pos=params.desync_split_pos;
reasm_orig_cancel(ctrack);
rdata_payload=NULL;
@ -842,6 +867,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
print_sockaddr((struct sockaddr *)&dst);
printf("\n");
}
if (!split_pos || split_pos>rlen_payload) split_pos=1;
split_pos=pos_normalize(split_pos,reasm_offset,len_payload);
enum dpi_desync_mode desync_mode = params.desync_mode;
uint32_t fooling_orig = FOOL_NONE;
@ -852,6 +880,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
switch(desync_mode)
{
case DESYNC_FAKE_KNOWN:
if (reasm_offset)
{
desync_mode = params.desync_mode2;
break;
}
if (!bKnownProtocol)
{
DLOG("not applying fake because of unknown protocol\n");
@ -859,6 +892,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
break;
}
case DESYNC_FAKE:
if (reasm_offset) break;
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps,
ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment,
fake, fake_size, pkt1, &pkt1_len))
@ -871,6 +905,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
break;
case DESYNC_RST:
case DESYNC_RSTACK:
if (reasm_offset) break;
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (desync_mode==DESYNC_RSTACK ? TH_ACK:0), tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps,
ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment,
NULL, 0, pkt1, &pkt1_len))
@ -884,7 +919,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
case DESYNC_DESTOPT:
case DESYNC_IPFRAG1:
fooling_orig = (desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1;
if (ip6hdr && (params.desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(params.desync_mode2)))
desync_mode = params.desync_mode2;
if (ip6hdr && (desync_mode==DESYNC_NONE || !desync_valid_second_stage_tcp(desync_mode) ||
(!split_pos && (desync_mode==DESYNC_SPLIT || desync_mode==DESYNC_SPLIT2 || desync_mode==DESYNC_DISORDER || desync_mode==DESYNC_DISORDER2))))
{
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps,
ttl_orig,fooling_orig,0,0,
@ -898,7 +935,6 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
// this mode is final, no other options available
return VERDICT_DROP;
}
desync_mode = params.desync_mode2;
}
if (b)
@ -916,12 +952,12 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
desync_mode = params.desync_mode2;
}
size_t split_pos=len_payload>params.desync_split_pos ? params.desync_split_pos : 1;
pkt1_len = sizeof(pkt1);
switch(desync_mode)
{
case DESYNC_DISORDER:
case DESYNC_DISORDER2:
if (split_pos)
{
uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100];
size_t fakeseg_len;
@ -976,6 +1012,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
break;
case DESYNC_SPLIT:
case DESYNC_SPLIT2:
if (split_pos)
{
uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100];
size_t fakeseg_len;
@ -1028,6 +1065,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch
}
break;
case DESYNC_IPFRAG2:
if (!reasm_offset)
{
verdict_tcp_csum_fix(verdict, tcphdr, transport_len, ip, ip6hdr);
@ -1091,10 +1129,13 @@ static bool quic_reasm_cancel(t_ctrack *ctrack, const char *reason)
}
}
static uint8_t dpi_desync_udp_packet_play(bool replay, uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct udphdr *udphdr, size_t transport_len, uint8_t *data_payload, size_t len_payload)
static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct udphdr *udphdr, size_t transport_len, uint8_t *data_payload, size_t len_payload)
{
uint8_t verdict=VERDICT_PASS;
// no need to desync middle packets in reasm session
if (reasm_offset) return verdict;
t_ctrack *ctrack=NULL, *ctrack_replay=NULL;
bool bReverse=false;
@ -1207,9 +1248,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, uint32_t fwmark, const ch
if (!ReasmIsEmpty(&ctrack->reasm_orig))
{
verdict_udp_csum_fix(verdict, udphdr, transport_len, ip, ip6hdr);
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, data_pkt, *len_pkt))
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, data_pkt, *len_pkt, len_payload))
{
DLOG("DELAY desync until reasm is complete (%u)\n", rawpacket_queue_count(&ctrack->delayed));
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
}
else
{
@ -1512,7 +1553,7 @@ static void packet_debug(bool replay, uint8_t proto, const struct ip *ip, const
}
static uint8_t dpi_desync_packet_play(bool replay, uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt)
static uint8_t dpi_desync_packet_play(bool replay, size_t reasm_offset, uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt)
{
struct ip *ip;
struct ip6_hdr *ip6hdr;
@ -1532,14 +1573,14 @@ static uint8_t dpi_desync_packet_play(bool replay, uint32_t fwmark, const char *
case IPPROTO_TCP:
if (tcphdr)
{
verdict = dpi_desync_tcp_packet_play(replay, fwmark, ifout, data_pkt, len_pkt, ip, ip6hdr, tcphdr, transport_len, data_payload, len_payload);
verdict = dpi_desync_tcp_packet_play(replay, reasm_offset, fwmark, ifout, data_pkt, len_pkt, ip, ip6hdr, tcphdr, transport_len, data_payload, len_payload);
verdict_tcp_csum_fix(verdict, tcphdr, transport_len, ip, ip6hdr);
}
break;
case IPPROTO_UDP:
if (udphdr)
{
verdict = dpi_desync_udp_packet_play(replay, fwmark, ifout, data_pkt, len_pkt, ip, ip6hdr, udphdr, transport_len, data_payload, len_payload);
verdict = dpi_desync_udp_packet_play(replay, reasm_offset, fwmark, ifout, data_pkt, len_pkt, ip, ip6hdr, udphdr, transport_len, data_payload, len_payload);
verdict_udp_csum_fix(verdict, udphdr, transport_len, ip, ip6hdr);
}
break;
@ -1549,37 +1590,35 @@ static uint8_t dpi_desync_packet_play(bool replay, uint32_t fwmark, const char *
}
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt)
{
return dpi_desync_packet_play(false, fwmark, ifout, data_pkt, len_pkt);
return dpi_desync_packet_play(false, 0, fwmark, ifout, data_pkt, len_pkt);
}
static bool replay_queue(struct rawpacket_tailhead *q)
{
struct rawpacket *rp = rawpacket_dequeue(q);
if (rp)
struct rawpacket *rp;
size_t offset;
unsigned int i;
bool b = true;
for (i=1,offset=0 ; (rp=rawpacket_dequeue(q)) ; offset+=rp->len_payload, rawpacket_free(rp), i++)
{
DLOG("REPLAYING first delayed packet\n")
bool b = true;
uint8_t verdict = dpi_desync_packet_play(true, rp->fwmark, rp->ifout, rp->packet, &rp->len);
DLOG("REPLAYING delayed packet #%u offset %zu\n",i,offset)
uint8_t verdict = dpi_desync_packet_play(true, offset, rp->fwmark, rp->ifout, rp->packet, &rp->len);
switch(verdict & VERDICT_MASK)
{
case VERDICT_MODIFY:
DLOG("SENDING first delayed packet modified\n")
DLOG("SENDING delayed packet #%u modified\n", i)
b &= rawsend_rp(rp);
break;
case VERDICT_PASS:
DLOG("SENDING first delayed packet unmodified\n")
DLOG("SENDING delayed packet #%u unmodified\n", i)
b &= rawsend_rp(rp);
break;
case VERDICT_DROP:
DLOG("DROPPING first delayed packet\n")
DLOG("DROPPING delayed packet #%u\n", i)
break;
}
rawpacket_free(rp);
DLOG("SENDING %u remaining delayed packets\n", rawpacket_queue_count(q))
b &= rawsend_queue(q);
return b;
}
return false;
return b;
}

View File

@ -791,6 +791,8 @@ static void exithelp(void)
" --dpi-desync-repeats=<N>\t\t\t; send every desync packet N times\n"
" --dpi-desync-skip-nosni=0|1\t\t\t; 1(default)=do not act on ClientHello without SNI (ESNI ?)\n"
" --dpi-desync-split-pos=<1..%u>\t\t; data payload split position\n"
" --dpi-desync-split-http-req=method|host\t; split at specified logical part of plain http request\n"
" --dpi-desync-split-tls=sni|sniext\t\t; split at specified logical part of TLS ClientHello\n"
" --dpi-desync-ipfrag-pos-tcp=<8..%u>\t\t; ip frag position starting from the transport header. multiple of 8, default %u.\n"
" --dpi-desync-ipfrag-pos-udp=<8..%u>\t\t; ip frag position starting from the transport header. multiple of 8, default %u.\n"
" --dpi-desync-badseq-increment=<int|0xHEX>\t; badseq fooling seq signed increment. default %d\n"
@ -828,6 +830,26 @@ static void exithelp_clean(void)
exithelp();
}
bool parse_httpreqpos(const char *s, enum httpreqpos *pos)
{
if (!strcmp(s, "method"))
*pos = httpreqpos_method;
else if (!strcmp(s, "host"))
*pos = httpreqpos_host;
else
return false;
return true;
}
bool parse_tlspos(const char *s, enum tlspos *pos)
{
if (!strcmp(s, "sni"))
*pos = tlspos_sni;
else if (!strcmp(s, "sniext"))
*pos = tlspos_sniext;
else
return false;
return true;
}
int main(int argc, char **argv)
{
@ -943,41 +965,43 @@ int main(int argc, char **argv)
{"dpi-desync-repeats",required_argument,0,0}, // optidx=21
{"dpi-desync-skip-nosni",optional_argument,0,0},// optidx=22
{"dpi-desync-split-pos",required_argument,0,0},// optidx=23
{"dpi-desync-ipfrag-pos-tcp",required_argument,0,0},// optidx=24
{"dpi-desync-ipfrag-pos-udp",required_argument,0,0},// optidx=25
{"dpi-desync-badseq-increment",required_argument,0,0},// optidx=26
{"dpi-desync-badack-increment",required_argument,0,0},// optidx=27
{"dpi-desync-any-protocol",optional_argument,0,0},// optidx=28
{"dpi-desync-fake-http",required_argument,0,0},// optidx=29
{"dpi-desync-fake-tls",required_argument,0,0},// optidx=30
{"dpi-desync-fake-unknown",required_argument,0,0},// optidx=31
{"dpi-desync-fake-syndata",required_argument,0,0},// optidx=32
{"dpi-desync-fake-quic",required_argument,0,0},// optidx=33
{"dpi-desync-fake-wireguard",required_argument,0,0},// optidx=34
{"dpi-desync-fake-dht",required_argument,0,0},// optidx=35
{"dpi-desync-fake-unknown-udp",required_argument,0,0},// optidx=36
{"dpi-desync-udplen-increment",required_argument,0,0},// optidx=37
{"dpi-desync-udplen-pattern",required_argument,0,0},// optidx=38
{"dpi-desync-cutoff",required_argument,0,0},// optidx=39
{"dpi-desync-start",required_argument,0,0},// optidx=40
{"hostlist",required_argument,0,0}, // optidx=41
{"hostlist-exclude",required_argument,0,0}, // optidx=42
{"hostlist-auto",required_argument,0,0}, // optidx=43
{"hostlist-auto-fail-threshold",required_argument,0,0}, // optidx=44
{"hostlist-auto-fail-time",required_argument,0,0}, // optidx=45
{"hostlist-auto-retrans-threshold",required_argument,0,0}, // optidx=46
{"hostlist-auto-debug",required_argument,0,0}, // optidx=47
{"dpi-desync-split-http-req",required_argument,0,0 },// optidx=24
{"dpi-desync-split-tls",required_argument,0,0 },// optidx=25
{"dpi-desync-ipfrag-pos-tcp",required_argument,0,0},// optidx=26
{"dpi-desync-ipfrag-pos-udp",required_argument,0,0},// optidx=27
{"dpi-desync-badseq-increment",required_argument,0,0},// optidx=28
{"dpi-desync-badack-increment",required_argument,0,0},// optidx=29
{"dpi-desync-any-protocol",optional_argument,0,0},// optidx=30
{"dpi-desync-fake-http",required_argument,0,0},// optidx=31
{"dpi-desync-fake-tls",required_argument,0,0},// optidx=32
{"dpi-desync-fake-unknown",required_argument,0,0},// optidx=33
{"dpi-desync-fake-syndata",required_argument,0,0},// optidx=34
{"dpi-desync-fake-quic",required_argument,0,0},// optidx=35
{"dpi-desync-fake-wireguard",required_argument,0,0},// optidx=36
{"dpi-desync-fake-dht",required_argument,0,0},// optidx=37
{"dpi-desync-fake-unknown-udp",required_argument,0,0},// optidx=38
{"dpi-desync-udplen-increment",required_argument,0,0},// optidx=39
{"dpi-desync-udplen-pattern",required_argument,0,0},// optidx=40
{"dpi-desync-cutoff",required_argument,0,0},// optidx=41
{"dpi-desync-start",required_argument,0,0},// optidx=42
{"hostlist",required_argument,0,0}, // optidx=43
{"hostlist-exclude",required_argument,0,0}, // optidx=44
{"hostlist-auto",required_argument,0,0}, // optidx=45
{"hostlist-auto-fail-threshold",required_argument,0,0}, // optidx=46
{"hostlist-auto-fail-time",required_argument,0,0}, // optidx=47
{"hostlist-auto-retrans-threshold",required_argument,0,0}, // optidx=48
{"hostlist-auto-debug",required_argument,0,0}, // optidx=49
#ifdef __linux__
{"bind-fix4",no_argument,0,0}, // optidx=48
{"bind-fix6",no_argument,0,0}, // optidx=49
{"bind-fix4",no_argument,0,0}, // optidx=50
{"bind-fix6",no_argument,0,0}, // optidx=51
#elif defined(__CYGWIN__)
{"wf-iface",required_argument,0,0}, // optidx=48
{"wf-l3",required_argument,0,0}, // optidx=49
{"wf-tcp",required_argument,0,0}, // optidx=50
{"wf-udp",required_argument,0,0}, // optidx=51
{"wf-raw",required_argument,0,0}, // optidx=52
{"wf-save",required_argument,0,0}, // optidx=53
{"wf-iface",required_argument,0,0}, // optidx=50
{"wf-l3",required_argument,0,0}, // optidx=51
{"wf-tcp",required_argument,0,0}, // optidx=52
{"wf-udp",required_argument,0,0}, // optidx=53
{"wf-raw",required_argument,0,0}, // optidx=54
{"wf-save",required_argument,0,0}, // optidx=55
#endif
{NULL,0,NULL,0}
};
@ -1210,13 +1234,27 @@ int main(int argc, char **argv)
params.desync_skip_nosni = !optarg || atoi(optarg);
break;
case 23: /* dpi-desync-split-pos */
if (sscanf(optarg,"%u",&params.desync_split_pos)<1 || params.desync_split_pos<1 || params.desync_split_pos>DPI_DESYNC_MAX_FAKE_LEN)
if (sscanf(optarg,"%u",&params.desync_split_pos)<1 || params.desync_split_pos<1)
{
fprintf(stderr, "dpi-desync-split-pos must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN);
fprintf(stderr, "dpi-desync-split-pos is not valid\n");
exit_clean(1);
}
break;
case 24: /* dpi-desync-ipfrag-pos-tcp */
case 24: /* dpi-desync-split-http-req */
if (!parse_httpreqpos(optarg, &params.desync_split_http_req))
{
fprintf(stderr, "Invalid argument for dpi-desync-split-http-req\n");
exit_clean(1);
}
break;
case 25: /* dpi-desync-split-tls */
if (!parse_tlspos(optarg, &params.desync_split_tls))
{
fprintf(stderr, "Invalid argument for dpi-desync-split-tls\n");
exit_clean(1);
}
break;
case 26: /* dpi-desync-ipfrag-pos-tcp */
if (sscanf(optarg,"%u",&params.desync_ipfrag_pos_tcp)<1 || params.desync_ipfrag_pos_tcp<1 || params.desync_ipfrag_pos_tcp>DPI_DESYNC_MAX_FAKE_LEN)
{
fprintf(stderr, "dpi-desync-ipfrag-pos-tcp must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN);
@ -1228,7 +1266,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 25: /* dpi-desync-ipfrag-pos-udp */
case 27: /* dpi-desync-ipfrag-pos-udp */
if (sscanf(optarg,"%u",&params.desync_ipfrag_pos_udp)<1 || params.desync_ipfrag_pos_udp<1 || params.desync_ipfrag_pos_udp>DPI_DESYNC_MAX_FAKE_LEN)
{
fprintf(stderr, "dpi-desync-ipfrag-pos-udp must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN);
@ -1240,63 +1278,63 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 26: /* dpi-desync-badseq-increments */
case 28: /* dpi-desync-badseq-increments */
if (!parse_badseq_increment(optarg,&params.desync_badseq_increment))
{
fprintf(stderr, "dpi-desync-badseq-increment should be signed decimal or signed 0xHEX\n");
exit_clean(1);
}
break;
case 27: /* dpi-desync-badack-increment */
case 29: /* dpi-desync-badack-increment */
if (!parse_badseq_increment(optarg,&params.desync_badseq_ack_increment))
{
fprintf(stderr, "dpi-desync-badack-increment should be signed decimal or signed 0xHEX\n");
exit_clean(1);
}
break;
case 28: /* dpi-desync-any-protocol */
case 30: /* dpi-desync-any-protocol */
params.desync_any_proto = !optarg || atoi(optarg);
break;
case 29: /* dpi-desync-fake-http */
case 31: /* dpi-desync-fake-http */
params.fake_http_size = sizeof(params.fake_http);
load_file_or_exit(optarg,params.fake_http,&params.fake_http_size);
break;
case 30: /* dpi-desync-fake-tls */
case 32: /* dpi-desync-fake-tls */
params.fake_tls_size = sizeof(params.fake_tls);
load_file_or_exit(optarg,params.fake_tls,&params.fake_tls_size);
break;
case 31: /* dpi-desync-fake-unknown */
case 33: /* dpi-desync-fake-unknown */
params.fake_unknown_size = sizeof(params.fake_unknown);
load_file_or_exit(optarg,params.fake_unknown,&params.fake_unknown_size);
break;
case 32: /* dpi-desync-fake-syndata */
case 34: /* dpi-desync-fake-syndata */
params.fake_syndata_size = sizeof(params.fake_syndata);
load_file_or_exit(optarg,params.fake_syndata,&params.fake_syndata_size);
break;
case 33: /* dpi-desync-fake-quic */
case 35: /* dpi-desync-fake-quic */
params.fake_quic_size = sizeof(params.fake_quic);
load_file_or_exit(optarg,params.fake_quic,&params.fake_quic_size);
break;
case 34: /* dpi-desync-fake-wireguard */
case 36: /* dpi-desync-fake-wireguard */
params.fake_wg_size = sizeof(params.fake_wg);
load_file_or_exit(optarg,params.fake_wg,&params.fake_wg_size);
break;
case 35: /* dpi-desync-fake-dht */
case 37: /* dpi-desync-fake-dht */
params.fake_dht_size = sizeof(params.fake_dht);
load_file_or_exit(optarg,params.fake_dht,&params.fake_dht_size);
break;
case 36: /* dpi-desync-fake-unknown-udp */
case 38: /* dpi-desync-fake-unknown-udp */
params.fake_unknown_udp_size = sizeof(params.fake_unknown_udp);
load_file_or_exit(optarg,params.fake_unknown_udp,&params.fake_unknown_udp_size);
break;
case 37: /* dpi-desync-udplen-increment */
case 39: /* dpi-desync-udplen-increment */
if (sscanf(optarg,"%d",&params.udplen_increment)<1 || params.udplen_increment>0x7FFF || params.udplen_increment<-0x8000)
{
fprintf(stderr, "dpi-desync-udplen-increment must be integer within -32768..32767 range\n");
exit_clean(1);
}
break;
case 38: /* dpi-desync-udplen-pattern */
case 40: /* dpi-desync-udplen-pattern */
{
char buf[sizeof(params.udplen_pattern)];
size_t sz=sizeof(buf);
@ -1304,35 +1342,35 @@ int main(int argc, char **argv)
fill_pattern(params.udplen_pattern,sizeof(params.udplen_pattern),buf,sz);
}
break;
case 39: /* desync-cutoff */
case 41: /* desync-cutoff */
if (!parse_cutoff(optarg, &params.desync_cutoff, &params.desync_cutoff_mode))
{
fprintf(stderr, "invalid desync-cutoff value\n");
exit_clean(1);
}
break;
case 40: /* desync-start */
case 42: /* desync-start */
if (!parse_cutoff(optarg, &params.desync_start, &params.desync_start_mode))
{
fprintf(stderr, "invalid desync-start value\n");
exit_clean(1);
}
break;
case 41: /* hostlist */
case 43: /* hostlist */
if (!strlist_add(&params.hostlist_files, optarg))
{
fprintf(stderr, "strlist_add failed\n");
exit_clean(1);
}
break;
case 42: /* hostlist-exclude */
case 44: /* hostlist-exclude */
if (!strlist_add(&params.hostlist_exclude_files, optarg))
{
fprintf(stderr, "strlist_add failed\n");
exit_clean(1);
}
break;
case 43: /* hostlist-auto */
case 45: /* hostlist-auto */
if (*params.hostlist_auto_filename)
{
fprintf(stderr, "only one auto hostlist is supported\n");
@ -1365,7 +1403,7 @@ int main(int argc, char **argv)
strncpy(params.hostlist_auto_filename, optarg, sizeof(params.hostlist_auto_filename));
params.hostlist_auto_filename[sizeof(params.hostlist_auto_filename) - 1] = '\0';
break;
case 44: /* hostlist-auto-fail-threshold */
case 46: /* hostlist-auto-fail-threshold */
params.hostlist_auto_fail_threshold = (uint8_t)atoi(optarg);
if (params.hostlist_auto_fail_threshold<1 || params.hostlist_auto_fail_threshold>20)
{
@ -1373,7 +1411,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 45: /* hostlist-auto-fail-time */
case 47: /* hostlist-auto-fail-time */
params.hostlist_auto_fail_time = (uint8_t)atoi(optarg);
if (params.hostlist_auto_fail_time<1)
{
@ -1381,7 +1419,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 46: /* hostlist-auto-retrans-threshold */
case 48: /* hostlist-auto-retrans-threshold */
params.hostlist_auto_retrans_threshold = (uint8_t)atoi(optarg);
if (params.hostlist_auto_retrans_threshold<2 || params.hostlist_auto_retrans_threshold>10)
{
@ -1389,7 +1427,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 47: /* hostlist-auto-debug */
case 49: /* hostlist-auto-debug */
{
FILE *F = fopen(optarg,"a+t");
if (!F)
@ -1407,28 +1445,28 @@ int main(int argc, char **argv)
}
break;
#ifdef __linux__
case 48: /* bind-fix4 */
case 50: /* bind-fix4 */
params.bind_fix4 = true;
break;
case 49: /* bind-fix6 */
case 51: /* bind-fix6 */
params.bind_fix6 = true;
break;
#elif defined(__CYGWIN__)
case 48: /* wf-iface */
case 50: /* wf-iface */
if (!sscanf(optarg,"%u.%u",&IfIdx,&SubIfIdx))
{
fprintf(stderr, "bad value for --wf-iface\n");
exit_clean(1);
}
break;
case 49: /* wf-l3 */
case 51: /* wf-l3 */
if (!wf_make_l3(optarg,&wf_ipv4,&wf_ipv6))
{
fprintf(stderr, "bad value for --wf-l3\n");
exit_clean(1);
}
break;
case 50: /* wf-tcp */
case 52: /* wf-tcp */
if (!wf_make_pf(optarg,"tcp","SrcPort",wf_pf_tcp_src,sizeof(wf_pf_tcp_src)) ||
!wf_make_pf(optarg,"tcp","DstPort",wf_pf_tcp_dst,sizeof(wf_pf_tcp_dst)))
{
@ -1436,7 +1474,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 51: /* wf-udp */
case 53: /* wf-udp */
if (!wf_make_pf(optarg,"udp","SrcPort",wf_pf_udp_src,sizeof(wf_pf_udp_src)) ||
!wf_make_pf(optarg,"udp","DstPort",wf_pf_udp_dst,sizeof(wf_pf_udp_dst)))
{
@ -1444,7 +1482,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 52: /* wf-raw */
case 54: /* wf-raw */
if (optarg[0]=='@')
{
size_t sz = sizeof(windivert_filter)-1;
@ -1457,7 +1495,7 @@ int main(int argc, char **argv)
windivert_filter[sizeof(windivert_filter) - 1] = '\0';
}
break;
case 53: /* wf-save */
case 55: /* wf-save */
strncpy(wf_save_file, optarg, sizeof(wf_save_file));
wf_save_file[sizeof(wf_save_file) - 1] = '\0';
break;
@ -1514,6 +1552,8 @@ int main(int argc, char **argv)
DLOG("autottl ipv4 %u:%u-%u\n",params.desync_autottl.delta,params.desync_autottl.min,params.desync_autottl.max)
if (AUTOTTL_ENABLED(params.desync_autottl6))
DLOG("autottl ipv6 %u:%u-%u\n",params.desync_autottl6.delta,params.desync_autottl6.min,params.desync_autottl6.max)
if (params.desync_split_tls==tlspos_none && params.desync_split_pos) params.desync_split_tls=tlspos_pos;
if (params.desync_split_http_req==httpreqpos_none && params.desync_split_pos) params.desync_split_http_req=httpreqpos_pos;
if (!LoadIncludeHostLists())
{

View File

@ -25,7 +25,7 @@ void rawpacket_queue_destroy(struct rawpacket_tailhead *q)
while((rp = rawpacket_dequeue(q))) rawpacket_free(rp);
}
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len)
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len,size_t len_payload)
{
struct rawpacket *rp = malloc(sizeof(struct rawpacket));
if (!rp) return NULL;
@ -48,6 +48,7 @@ struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sock
rp->ifout[0]=0;
memcpy(rp->packet,data,len);
rp->len=len;
rp->len_payload=len_payload;
TAILQ_INSERT_TAIL(q, rp, next);

View File

@ -11,7 +11,7 @@ struct rawpacket
struct sockaddr_storage dst;
char ifout[IFNAMSIZ+1];
uint32_t fwmark;
size_t len;
size_t len, len_payload;
uint8_t *packet;
TAILQ_ENTRY(rawpacket) next;
};
@ -21,6 +21,6 @@ void rawpacket_queue_init(struct rawpacket_tailhead *q);
void rawpacket_queue_destroy(struct rawpacket_tailhead *q);
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q);
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len,size_t len_payload);
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q);
void rawpacket_free(struct rawpacket *rp);

View File

@ -3,6 +3,7 @@
#include "pools.h"
#include "conntrack.h"
#include "desync.h"
#include "protocol.h"
#include <sys/param.h>
#include <sys/types.h>
@ -50,6 +51,8 @@ struct params_s
enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2;
bool desync_retrans,desync_skip_nosni,desync_any_proto;
unsigned int desync_repeats,desync_split_pos,desync_ipfrag_pos_tcp,desync_ipfrag_pos_udp;
enum httpreqpos desync_split_http_req;
enum tlspos desync_split_tls;
char desync_start_mode, desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int desync_start, desync_cutoff;
uint8_t desync_ttl, desync_ttl6;

View File

@ -8,7 +8,7 @@
#include <string.h>
const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
bool IsHttp(const uint8_t *data, size_t len)
const char *HttpMethod(const uint8_t *data, size_t len)
{
const char **method;
size_t method_len;
@ -16,10 +16,34 @@ bool IsHttp(const uint8_t *data, size_t len)
{
method_len = strlen(*method);
if (method_len <= len && !memcmp(data, *method, method_len))
return true;
return *method;
}
return false;
return NULL;
}
bool IsHttp(const uint8_t *data, size_t len)
{
return !!HttpMethod(data,len);
}
// pHost points to "Host: ..."
bool HttpFindHost(uint8_t **pHost,uint8_t *buf,size_t bs)
{
if (!*pHost)
{
*pHost = memmem(buf, bs, "\nHost:", 6);
if (*pHost) (*pHost)++;
}
return !!*pHost;
}
bool HttpFindHostConst(const uint8_t **pHost,const uint8_t *buf,size_t bs)
{
if (!*pHost)
{
*pHost = memmem(buf, bs, "\nHost:", 6);
if (*pHost) (*pHost)++;
}
return !!*pHost;
}
bool IsHttpReply(const uint8_t *data, size_t len)
{
// HTTP/1.x 200\r\n
@ -106,6 +130,37 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
return strcasecmp(dhost, drhost)!=0;
}
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz)
{
const uint8_t *method, *host;
int i;
switch(tpos_type)
{
case httpreqpos_method:
// recognize some tpws pre-applied hacks
method=http;
if (sz<10) break;
if (*method=='\n' || *method=='\r') method++;
if (*method=='\n' || *method=='\r') method++;
for (i=0;i<7;i++) if (*method>='A' && *method<='Z') method++;
if (i<3 || *method!=' ') break;
return method-http-1;
case httpreqpos_host:
if (HttpFindHostConst(&host,http,sz) && (host-http+7)<sz)
{
host+=5;
if (*host==' ') host++;
return host-http;
}
break;
case httpreqpos_pos:
break;
default:
return 0;
}
return hpos_pos<sz ? hpos_pos : 0;
}
uint16_t TLSRecordDataLen(const uint8_t *data)
@ -250,6 +305,23 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen, bPartialIsOK)) return false;
return TLSExtractHostFromExt(ext, elen, host, len_host);
}
size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type)
{
size_t elen;
const uint8_t *ext;
switch(tpos_type)
{
case tlspos_sni:
case tlspos_sniext:
if (TLSFindExt(tls,sz,0,&ext,&elen,false))
return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1;
// fall through
case tlspos_pos:
return tpos_pos<sz ? tpos_pos : 0;
default:
return 0;
}
}

View File

@ -7,7 +7,11 @@
#include "crypto/aes-gcm.h"
#include "helpers.h"
extern const char *http_methods[9];
const char *HttpMethod(const uint8_t *data, size_t len);
bool IsHttp(const uint8_t *data, size_t len);
bool HttpFindHost(uint8_t **pHost,uint8_t *buf,size_t bs);
bool HttpFindHostConst(const uint8_t **pHost,const uint8_t *buf,size_t bs);
// header must be passed like this : "\nHost:"
bool HttpExtractHeader(const uint8_t *data, size_t len, const char *header, char *buf, size_t len_buf);
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);
@ -17,6 +21,8 @@ const char *HttpFind2ndLevelDomain(const char *host);
int HttpReplyCode(const uint8_t *data, size_t len);
// must be pre-checked by IsHttpReply
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host, httpreqpos_pos };
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz);
uint16_t TLSRecordDataLen(const uint8_t *data);
size_t TLSRecordLen(const uint8_t *data);
@ -29,6 +35,8 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK);
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_pos };
size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type);
bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len);
bool IsDhtD1(const uint8_t *data, size_t len);

View File

@ -9,12 +9,11 @@
#include "tpws.h"
#include "pools.h"
#include "helpers.h"
#include "protocol.h"
#define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 3
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host };
enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_pos };
enum bindll { unwanted=0, no, prefer, force };
#define MAX_BINDS 32

View File

@ -9,7 +9,7 @@
const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
bool IsHttp(const uint8_t *data, size_t len)
const char *HttpMethod(const uint8_t *data, size_t len)
{
const char **method;
size_t method_len;
@ -17,9 +17,32 @@ bool IsHttp(const uint8_t *data, size_t len)
{
method_len = strlen(*method);
if (method_len <= len && !memcmp(data, *method, method_len))
return true;
return *method;
}
return false;
return NULL;
}
bool IsHttp(const uint8_t *data, size_t len)
{
return !!HttpMethod(data,len);
}
// pHost points to "Host: ..."
bool HttpFindHost(uint8_t **pHost,uint8_t *buf,size_t bs)
{
if (!*pHost)
{
*pHost = memmem(buf, bs, "\nHost:", 6);
if (*pHost) (*pHost)++;
}
return !!*pHost;
}
bool HttpFindHostConst(const uint8_t **pHost,const uint8_t *buf,size_t bs)
{
if (!*pHost)
{
*pHost = memmem(buf, bs, "\nHost:", 6);
if (*pHost) (*pHost)++;
}
return !!*pHost;
}
bool IsHttpReply(const uint8_t *data, size_t len)
{
@ -107,6 +130,37 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
return strcasecmp(dhost, drhost)!=0;
}
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz)
{
const uint8_t *method, *host;
int i;
switch(tpos_type)
{
case httpreqpos_method:
// recognize some tpws pre-applied hacks
method=http;
if (sz<10) break;
if (*method=='\n' || *method=='\r') method++;
if (*method=='\n' || *method=='\r') method++;
for (i=0;i<7;i++) if (*method>='A' && *method<='Z') method++;
if (i<3 || *method!=' ') break;
return method-http-1;
case httpreqpos_host:
if (HttpFindHostConst(&host,http,sz) && (host-http+7)<sz)
{
host+=5;
if (*host==' ') host++;
return host-http;
}
break;
case httpreqpos_pos:
break;
default:
return 0;
}
return hpos_pos<sz ? hpos_pos : 0;
}
@ -241,3 +295,20 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen, bPartialIsOK)) return false;
return TLSExtractHostFromExt(ext, elen, host, len_host);
}
size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type)
{
size_t elen;
const uint8_t *ext;
switch(tpos_type)
{
case tlspos_sni:
case tlspos_sniext:
if (TLSFindExt(tls,sz,0,&ext,&elen,false))
return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1;
// fall through
case tlspos_pos:
return tpos_pos<sz ? tpos_pos : 0;
default:
return 0;
}
}

View File

@ -4,7 +4,11 @@
#include <stdint.h>
#include <stdbool.h>
extern const char *http_methods[9];
const char *HttpMethod(const uint8_t *data, size_t len);
bool IsHttp(const uint8_t *data, size_t len);
bool HttpFindHost(uint8_t **pHost,uint8_t *buf,size_t bs);
bool HttpFindHostConst(const uint8_t **pHost,const uint8_t *buf,size_t bs);
// header must be passed like this : "\nHost:"
bool HttpExtractHeader(const uint8_t *data, size_t len, const char *header, char *buf, size_t len_buf);
bool HttpExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);
@ -14,6 +18,8 @@ const char *HttpFind2ndLevelDomain(const char *host);
int HttpReplyCode(const uint8_t *data, size_t len);
// must be pre-checked by IsHttpReply
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
enum httpreqpos { httpreqpos_none = 0, httpreqpos_method, httpreqpos_host, httpreqpos_pos };
size_t HttpPos(enum httpreqpos tpos_type, size_t hpos_pos, const uint8_t *http, size_t sz);
uint16_t TLSRecordDataLen(const uint8_t *data);
size_t TLSRecordLen(const uint8_t *data);
@ -23,3 +29,5 @@ bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK);
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
enum tlspos { tlspos_none = 0, tlspos_sni, tlspos_sniext, tlspos_pos };
size_t TLSPos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type);

View File

@ -8,48 +8,13 @@
#include <string.h>
#include <stdio.h>
// pHost points to "Host: ..."
static bool find_host(uint8_t **pHost,uint8_t *buf,size_t bs)
{
if (!*pHost)
{
*pHost = memmem(buf, bs, "\nHost:", 6);
if (*pHost)
{
(*pHost)++;
VPRINT("Found Host: at pos %td",*pHost - buf)
}
}
return !!*pHost;
}
static size_t tls_pos(enum tlspos tpos_type, size_t tpos_pos, const uint8_t *tls, size_t sz, uint8_t type)
{
size_t elen;
const uint8_t *ext;
switch(tpos_type)
{
case tlspos_sni:
case tlspos_sniext:
if (TLSFindExt(tls,sz,0,&ext,&elen,false))
return (tpos_type==tlspos_sni) ? ext-tls+6 : ext-tls+1;
// fall through
case tlspos_pos:
return tpos_pos;
default:
return 0;
}
}
static const char *http_methods[] = { "GET /","POST /","HEAD /","OPTIONS /","PUT /","DELETE /","CONNECT /","TRACE /",NULL };
// segment buffer has at least 5 extra bytes to extend data block
void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *split_pos, uint8_t *split_flags)
{
uint8_t *p, *pp, *pHost = NULL;
size_t method_len = 0, pos;
const char **method;
bool bIsHttp = false, bBypass = false, bHaveHost = false, bHostExcluded = false;
const char *method;
bool bBypass = false, bHaveHost = false, bHostExcluded = false;
char bRemovedHostSpace = 0;
char *pc, Host[256];
@ -58,22 +23,13 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
*split_pos=0;
*split_flags=0;
for (method = http_methods; *method; method++)
if ((method = HttpMethod(segment,*size)))
{
method_len = strlen(*method);
if (method_len <= *size && !memcmp(segment, *method, method_len))
{
bIsHttp = true;
method_len -= 2; // "GET /" => "GET"
break;
}
}
if (bIsHttp)
{
VPRINT("Data block looks like http request start : %s", *method)
method_len = strlen(method)-2;
VPRINT("Data block looks like http request start : %s", method)
if (!ctrack->l7proto) ctrack->l7proto=HTTP;
// cpu saving : we search host only if and when required. we do not research host every time we need its position
if ((params.hostlist || params.hostlist_exclude) && find_host(&pHost,segment,*size))
if ((params.hostlist || params.hostlist_exclude) && HttpFindHost(&pHost,segment,*size))
{
p = pHost + 5;
while (p < (segment + *size) && (*p == ' ' || *p == '\t')) p++;
@ -135,7 +91,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
(*size)++; // block will grow by 1 byte
if (pHost) pHost++; // Host: position will move by 1 byte
}
if ((params.hostdot || params.hosttab) && *size<segment_buffer_size && find_host(&pHost,segment,*size))
if ((params.hostdot || params.hosttab) && *size<segment_buffer_size && HttpFindHost(&pHost,segment,*size))
{
p = pHost + 5;
while (p < (segment + *size) && *p != '\r' && *p != '\n') p++;
@ -148,7 +104,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
(*size)++; // block will grow by 1 byte
}
}
if (params.domcase && find_host(&pHost,segment,*size))
if (params.domcase && HttpFindHost(&pHost,segment,*size))
{
p = pHost + 5;
pos = p - segment;
@ -156,7 +112,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
for (; p < (segment + *size) && *p != '\r' && *p != '\n'; p++)
*p = (((size_t)p) & 1) ? tolower(*p) : toupper(*p);
}
if (params.hostnospace && find_host(&pHost,segment,*size) && (pHost+5)<(segment+*size) && pHost[5] == ' ')
if (params.hostnospace && HttpFindHost(&pHost,segment,*size) && (pHost+5)<(segment+*size) && pHost[5] == ' ')
{
p = pHost + 6;
pos = p - segment;
@ -165,12 +121,12 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
(*size)--; // block will shrink by 1 byte
bRemovedHostSpace = 1;
}
if (params.hostcase && find_host(&pHost,segment,*size))
if (params.hostcase && HttpFindHost(&pHost,segment,*size))
{
VPRINT("Changing 'Host:' => '%c%c%c%c:' at pos %td", params.hostspell[0], params.hostspell[1], params.hostspell[2], params.hostspell[3], pHost - segment)
memcpy(pHost, params.hostspell, 4);
}
if (params.hostpad && find_host(&pHost,segment,*size))
if (params.hostpad && HttpFindHost(&pHost,segment,*size))
{
// add : XXXXX: <padding?[\r\n|\n]
char s[8];
@ -217,18 +173,7 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
pHost = NULL; // invalidate
}
}
switch (params.split_http_req)
{
case httpreqpos_method:
*split_pos = method_len - 1 + params.methodeol + (params.methodeol && !params.unixeol);
break;
case httpreqpos_host:
if (find_host(&pHost,segment,*size))
*split_pos = pHost + 6 - bRemovedHostSpace - segment;
break;
default:
if (params.split_pos < *size) *split_pos = params.split_pos;
}
*split_pos = HttpPos(params.split_http_req, params.split_pos, segment, *size);
if (params.disorder_http) *split_flags |= SPLIT_FLAG_DISORDER;
if (params.oob_http) *split_flags |= SPLIT_FLAG_OOB;
}
@ -258,11 +203,11 @@ void tamper_out(t_ctrack *ctrack, uint8_t *segment,size_t segment_buffer_size,si
}
else
{
spos = tls_pos(params.split_tls, params.split_pos, segment, *size, 0);
spos = TLSPos(params.split_tls, params.split_pos, segment, *size, 0);
if ((5+*size)<=segment_buffer_size)
{
tpos = tls_pos(params.tlsrec, params.tlsrec_pos+5, segment, *size, 0);
tpos = TLSPos(params.tlsrec, params.tlsrec_pos+5, segment, *size, 0);
if (tpos>5)
{
// construct 2 TLS records from one

View File

@ -829,6 +829,7 @@ void parse_params(int argc, char *argv[])
}
if (!params.resolver_threads) params.resolver_threads = 5 + params.maxconn/50;
if (params.split_tls==tlspos_none && params.split_pos) params.split_tls=tlspos_pos;
if (params.split_http_req==httpreqpos_none && params.split_pos) params.split_http_req=httpreqpos_pos;
if (*params.hostlist_auto_filename) params.hostlist_auto_mod_time = file_mod_time(params.hostlist_auto_filename);
if (!LoadIncludeHostLists())