Compare commits

..

2 Commits

Author SHA1 Message Date
bol-van
e8f6b89efd nfqws: increase quic defrag buf 2024-04-20 21:40:52 +03:00
bol-van
51c0d1ce3c nfqws: packet delay support 2024-04-20 20:59:45 +03:00
26 changed files with 975 additions and 527 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.

View File

@ -274,3 +274,12 @@ v56
tpws: mss fooling
tpws: multi thread resolver. eliminates blocks related to hostname resolve.
v57
tpws: --nosplice option
nfqws: postnat fixes
nfqws: --dpi-desync-start option
nfqws: packet delay for kyber TLS and QUIC
nfqws: --dpi-desync-retrans obsolete
nfqws: --qnum is mandatory, no more default queue 0

View File

@ -173,7 +173,6 @@ nfqws takes the following parameters:
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]] ; auto ttl mode for both ipv4 and ipv6. default: 1:3-20
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]] ; overrides --dpi-desync-autottl for ipv6 only
--dpi-desync-fooling=<mode>[,<mode>] ; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2
--dpi-desync-retrans=0|1 ; 0(default)=reinject original data packet after fake 1=drop original data packet to force its retransmission
--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
@ -263,10 +262,7 @@ add tcp option **MD5 signature**. All of them have their own disadvantages :
`--dpi-desync-fooling` takes multiple comma separated values.
For fake,rst,rstack modes original packet can be sent after the fake one or just dropped.
If its dropped OS will perform first retransmission after 0.2 sec, then the delay increases exponentially.
Delay can help to make sure fake and original packets are properly ordered and processed on DPI.
When `dpi-desync-retrans=1` its mandatory to use connbytes in iptables rule. Otherwise loop happens.
For fake,rst,rstack modes original packet is sent after the fake.
Disorder mode splits original packet and sends packets in the following order :
1. 2nd segment
@ -461,6 +457,16 @@ If the connection falls out of the conntrack and --dpi-desync-cutoff is set, dpi
Set conntrack timeouts appropriately.
### Reassemble
nfqws supports reassemble of TLS and QUIC ClientHello.
They can consist of multiple packets if kyber crypto is used (default from chromium 124).
Chromium randomizes TLS fingerprint. SNI can be in any packet.
Stateful DPIs usually reassemble all packets in the request then apply block decision.
If nfqws receives a partial ClientHello it begins reassemble session. Packets are delayed until it's finished.
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.
### UDP support
UDP attacks are limited. Its not possible to fragment UDP on transport level, only on network (ip) level.

View File

@ -1,4 +1,4 @@
zapret v.56
zapret v.57
English
-------
@ -246,7 +246,6 @@ nfqws
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]] ; режим auto ttl для ipv4 и ipv6. по умолчанию: 1:3-20. delta=0 отключает функцию.
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]] ; переопределение предыдущего параметра для ipv6
--dpi-desync-fooling=<fooling> ; дополнительные методики как сделать, чтобы фейковый пакет не дошел до сервера. none md5sig badseq badsum datanoack hopbyhop hopbyhop2
--dpi-desync-retrans=0|1 ; (только для fake,rst,rstack) 0(default)=отправлять оригинал следом за фейком 1=дропать оригинал, заставляя ОС выполнять ретрансмиссию через 0.2 сек
--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*) разбивать пакет на указанной позиции
@ -352,11 +351,7 @@ fakeknown отличается от fake тем, что применяется
Режимы дурения могут сочетаться в любых комбинациях. --dpi-desync-fooling берет множество значений через запятую.
Для режимов fake, rst, rstack после фейка отправляем оригинальный пакет. Можно его отправить сразу следом за фейком, а можно его просто дропнуть.
Если его дропнуть, ОС выполнит ретрансмиссию. Первая ретрансмиссия случается через 0.2 сек, потом задержка увеличивается экспоненциально.
Задержка может дать надежную гарантию, что пакеты пойдут именно в нужном порядке и будут именно в нем обработаны на DPI.
По умолчанию используется первый вариант, т.к. он быстрее.
При использовании dpi-desync-retrans=1 обязательно вставлять ограничитель connbytes в iptables, иначе получим зацикливание.
Для режимов fake, rst, rstack после фейка отправляем оригинальный пакет.
Режим disorder делит оригинальный пакет на 2 части и отправляет следующую комбинацию в указанном порядке :
1. 2-я часть пакета
@ -529,16 +524,17 @@ window size итоговый размер окна стал максимальн
На склонных к бездействию соединениях следует изменить таймауты conntrack.
Если соединение выпало из conntrack и задана опция --dpi-desync-cutoff, dpi desync применяться не будет.
РЕАССЕМБЛИНГ TCP
nfqws поддерживает реассемблинг некоторых видов tcp запросов.
На текущий момент это TLS ClientHello. Он бывает длинным, если в chrome включить пост-квантовую
криптографию tls-kyber, и занимает как правило 2 пакета.
РЕАССЕМБЛИНГ
nfqws поддерживает реассемблинг некоторых видов запросов.
На текущий момент это TLS и QUIC ClientHello. Они бывает длинными, если в chrome включить пост-квантовую
криптографию tls-kyber, и занимают как правило 2 или 3 пакета. kyber включен по умолчанию, начиная с chromium 124.
chrome рандомизирует фингерпринт TLS. SNI может оказаться как в начале, так и в конце, то есть
попасть в 1 или 2 пакет. stateful DPI обычно реассемблирует запрос целиком, и только потом
попасть любой пакет. stateful DPI обычно реассемблирует запрос целиком, и только потом
принимает решение о блокировке.
nfqws реагирует десинхронизацией на каждый пакет из TLSClientHello, если задана опция
--dpi-desync-skip-nosni=0. В противном случае десинхронизация идет на сам пакет,
включающий SNI, и все последующие.
В случае получения TLS или QUIC пакета с частичным ClientHello начинается процесс сборки, а пакеты
задерживаются и не отсылаются до ее окончания. По окончании сборки первый пакет проходит через десинхронизацию
на основании полностью собранного ClientHello. Остальные пакеты отсылаются без десинхронизации.
При любой ошибке в процессе сборки задержанные пакеты немедленно отсылаются в сеть, а десинхронизация отменяется.
ПОДДЕРЖКА UDP
Атаки на udp более ограничены в возможностях. udp нельзя фрагментировать иначе, чем на уровне ip.

View File

@ -37,6 +37,7 @@ static void ConntrackClearTrack(t_ctrack *track)
{
ConntrackClearHostname(track);
ReasmClear(&track->reasm_orig);
rawpacket_queue_destroy(&track->delayed);
}
static void ConntrackFreeElem(t_conntrack_pool *elem)
@ -99,19 +100,24 @@ static void ConntrackInitTrack(t_ctrack *t)
memset(t,0,sizeof(*t));
t->scale_orig = t->scale_reply = SCALE_NONE;
time(&t->t_start);
rawpacket_queue_init(&t->delayed);
}
static void ConntrackReInitTrack(t_ctrack *t)
{
ConntrackClearTrack(t);
ConntrackInitTrack(t);
}
static t_conntrack_pool *ConntrackNew(t_conntrack_pool **pp, const t_conn *c)
{
t_conntrack_pool *new;
if (!(new = malloc(sizeof(*new)))) return NULL;
new->conn = *c;
t_conntrack_pool *ctnew;
if (!(ctnew = malloc(sizeof(*ctnew)))) return NULL;
ctnew->conn = *c;
oom = false;
HASH_ADD(hh, *pp, conn, sizeof(*c), new);
if (oom) { free(new); return NULL; }
ConntrackInitTrack(&new->track);
return new;
HASH_ADD(hh, *pp, conn, sizeof(*c), ctnew);
if (oom) { free(ctnew); return NULL; }
ConntrackInitTrack(&ctnew->track);
return ctnew;
}
// non-tcp packets are passed with tcphdr=NULL but len_payload filled
@ -135,13 +141,13 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
{
if (tcp_syn_segment(tcphdr))
{
if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry
if (t->state!=SYN) ConntrackReInitTrack(t); // erase current entry
t->seq0 = htonl(tcphdr->th_seq);
}
else if (tcp_synack_segment(tcphdr))
{
if (t->state!=SYN) ConntrackInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1;
if (t->state!=SYN) ConntrackReInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = htonl(tcphdr->th_ack)-1;
t->ack0 = htonl(tcphdr->th_seq);
}
else if (tcphdr->th_flags & (TH_FIN|TH_RST))
@ -192,6 +198,35 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
time(&t->t_last);
}
static bool ConntrackPoolDoubleSearchPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse)
{
bool b_rev;
t_conn conn,connswp;
t_conntrack_pool *ctr;
ConntrackExtractConn(&conn,false,ip,ip6,tcphdr,udphdr);
if ((ctr=ConntrackPoolSearch(*pp,&conn)))
{
if (bReverse) *bReverse = false;
if (ctrack) *ctrack = &ctr->track;
return true;
}
else
{
connswap(&conn,&connswp);
if ((ctr=ConntrackPoolSearch(*pp,&connswp)))
{
if (bReverse) *bReverse = true;
if (ctrack) *ctrack = &ctr->track;
return true;
}
}
return false;
}
bool ConntrackPoolDoubleSearch(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse)
{
return ConntrackPoolDoubleSearchPool(&p->pool, ip, ip6, tcphdr, udphdr, ctrack, bReverse);
}
static bool ConntrackPoolFeedPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse)
{
@ -343,9 +378,18 @@ bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start)
reasm->seq = seq_start;
return true;
}
bool ReasmResize(t_reassemble *reasm, size_t new_size)
{
uint8_t *p = realloc(reasm->packet, new_size);
if (!p) return false;
reasm->packet = p;
reasm->size = new_size;
if (reasm->size_present > new_size) reasm->size_present = new_size;
return true;
}
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len)
{
if (reasm->seq!=seq) return false; // fail session if out of sequence
if (seq!=-1 && reasm->seq!=seq) return false; // fail session if out of sequence
size_t szcopy;
szcopy = reasm->size - reasm->size_present;

View File

@ -14,6 +14,8 @@
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include "packet_queue.h"
//#define HASH_BLOOM 20
#define HASH_NONFATAL_OOM 1
#undef HASH_FUNCTION
@ -63,7 +65,7 @@ typedef struct
uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none
uint8_t req_retrans_counter; // number of request retransmissions
bool req_seq_start_present, req_seq_present;
bool req_seq_present,req_seq_finalized;
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
uint8_t autottl;
@ -71,10 +73,11 @@ typedef struct
bool b_cutoff; // mark for deletion
bool b_wssize_cutoff, b_desync_cutoff;
t_reassemble reasm_orig;
t_l7proto l7proto;
char *hostname;
t_reassemble reasm_orig;
struct rawpacket_tailhead delayed;
} t_ctrack;
typedef struct
@ -94,6 +97,8 @@ typedef struct
void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin, uint32_t timeout_udp);
void ConntrackPoolDestroy(t_conntrack *p);
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse);
// do not create, do not update. only find existing
bool ConntrackPoolDoubleSearch(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse);
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr);
void CaonntrackExtractConn(t_conn *c, bool bReverse, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr);
void ConntrackPoolDump(const t_conntrack *p);
@ -101,6 +106,7 @@ void ConntrackPoolPurge(t_conntrack *p);
void ConntrackClearHostname(t_ctrack *track);
bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start);
bool ReasmResize(t_reassemble *reasm, size_t new_size);
void ReasmClear(t_reassemble *reasm);
// false means reassemble session has failed and we should ReasmClear() it
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len);

View File

@ -10,7 +10,7 @@
#include <errno.h>
#include "helpers.h"
#include "params.h"
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment)
{
@ -840,6 +840,64 @@ void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type, uint8_t *
// we have garbage
}
void proto_dissect_l3l4(
uint8_t *data, size_t len,
struct ip **ip, struct ip6_hdr **ip6,
uint8_t *proto,
struct tcphdr **tcp,
struct udphdr **udp,
size_t *transport_len,
uint8_t **data_payload, size_t *len_payload)
{
*ip = NULL;
*ip6 = NULL;
*proto = 0;
*tcp = NULL;
*transport_len = 0;
*udp = NULL;
*data_payload = NULL;
*len_payload = 0;
if (proto_check_ipv4(data, len))
{
*ip = (struct ip *) data;
*proto = (*ip)->ip_p;
proto_skip_ipv4(&data, &len);
}
else if (proto_check_ipv6(data, len))
{
*ip6 = (struct ip6_hdr *) data;
proto_skip_ipv6(&data, &len, proto, NULL);
}
else
{
return;
}
if (*proto==IPPROTO_TCP && proto_check_tcp(data, len))
{
*tcp = (struct tcphdr *) data;
*transport_len = len;
proto_skip_tcp(&data, &len);
*data_payload = data;
*len_payload = len;
}
else if (*proto==IPPROTO_UDP && proto_check_udp(data, len))
{
*udp = (struct udphdr *) data;
*transport_len = len;
proto_skip_udp(&data, &len);
*data_payload = data;
*len_payload = len;
}
}
bool tcp_synack_segment(const struct tcphdr *tcphdr)
{
/* check for set bits in TCP hdr */
@ -1204,6 +1262,20 @@ nofix:
return true;
}
bool rawsend_rp(const struct rawpacket *rp)
{
return rawsend((struct sockaddr*)&rp->dst,rp->fwmark,rp->ifout,rp->packet,rp->len);
}
bool rawsend_queue(struct rawpacket_tailhead *q)
{
struct rawpacket *rp;
bool b;
for (b=true; (rp=rawpacket_dequeue(q)) ; rawpacket_free(rp))
b &= rawsend_rp(rp);
return b;
}
// return guessed fake ttl value. 0 means unsuccessfull, should not perform autottl fooling
// ttl = TTL of incoming packet
uint8_t autottl_guess(uint8_t ttl, const autottl *attl)
@ -1233,3 +1305,27 @@ uint8_t autottl_guess(uint8_t ttl, const autottl *attl)
return fake;
}
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr)
{
#ifdef __FreeBSD__
// FreeBSD tend to pass ipv6 frames with wrong checksum
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr)
#else
// if original packet was tampered earlier it needs checksum fixed
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY)
#endif
tcp_fix_checksum(tcphdr,transport_len,ip,ip6hdr);
}
void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr)
{
#ifdef __FreeBSD__
// FreeBSD tend to pass ipv6 frames with wrong checksum
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr)
#else
// if original packet was tampered earlier it needs checksum fixed
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY)
#endif
udp_fix_checksum(udphdr,transport_len,ip,ip6hdr);
}

View File

@ -11,6 +11,8 @@
#include <netinet/in.h>
#include <sys/socket.h>
#include "packet_queue.h"
#ifndef IPPROTO_DIVERT
#define IPPROTO_DIVERT 258
#endif
@ -39,6 +41,12 @@ uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment);
#define SCALE_NONE ((uint8_t)-1)
#define VERDICT_PASS 0
#define VERDICT_MODIFY 1
#define VERDICT_DROP 2
#define VERDICT_MASK 3
#define VERDICT_NOCSUM 4
// seq and wsize have network byte order
bool prepare_tcp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
@ -136,6 +144,9 @@ bool tcp_has_fastopen(const struct tcphdr *tcp);
// auto creates internal socket and uses it for subsequent calls
bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len);
bool rawsend_rp(const struct rawpacket *rp);
// return trues if all packets were send successfully
bool rawsend_queue(struct rawpacket_tailhead *q);
// should pre-do it if dropping privileges. otherwise its not necessary
bool rawsend_preinit(bool bind_fix4, bool bind_fix6);
// cleans up socket autocreated by rawsend
@ -158,6 +169,14 @@ bool proto_check_tcp(const uint8_t *data, size_t len);
void proto_skip_tcp(uint8_t **data, size_t *len);
bool proto_check_udp(const uint8_t *data, size_t len);
void proto_skip_udp(uint8_t **data, size_t *len);
void proto_dissect_l3l4(
uint8_t *data, size_t len,
struct ip **ip, struct ip6_hdr **ip6,
uint8_t *proto,
struct tcphdr **tcp,
struct udphdr **udp,
size_t *transport_len,
uint8_t **data_payload, size_t *len_payload);
bool tcp_synack_segment(const struct tcphdr *tcphdr);
bool tcp_syn_segment(const struct tcphdr *tcphdr);
@ -177,3 +196,6 @@ typedef struct
#define AUTOTTL_SET_DEFAULT(a) {(a).delta=AUTOTTL_DEFAULT_DELTA; (a).min=AUTOTTL_DEFAULT_MIN; (a).max=AUTOTTL_DEFAULT_MAX;}
uint8_t autottl_guess(uint8_t ttl, const autottl *attl);
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr);
void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr);

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
#pragma once
#include "darkmagic.h"
#include "nfqws.h"
#include <stdint.h>
#include <stdbool.h>
@ -51,5 +50,4 @@ bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode);
bool desync_valid_second_stage_udp(enum dpi_desync_mode mode);
void desync_init(void);
uint8_t dpi_desync_tcp_packet(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 len_tcp, uint8_t *data_payload, size_t len_payload);
uint8_t dpi_desync_udp_packet(uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct udphdr *udphdr, uint8_t *data_payload, size_t len_payload);
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt);

View File

@ -5,9 +5,11 @@
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <time.h>
#include <sys/stat.h>
#include "params.h"
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
{
size_t k;

View File

@ -5,9 +5,9 @@
#include <sys/socket.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include "params.h"
#include <time.h>
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit);
char *strncasestr(const char *s,const char *find, size_t slen);

View File

@ -79,108 +79,16 @@ static void onusr2(int sig)
}
static uint8_t processPacketData(uint32_t *mark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt)
{
struct ip *ip = NULL;
struct ip6_hdr *ip6hdr = NULL;
struct tcphdr *tcphdr = NULL;
struct udphdr *udphdr = NULL;
size_t len = *len_pkt, len_with_th;
uint8_t *data = data_pkt;
uint8_t res = VERDICT_PASS;
uint8_t proto;
#ifdef __linux__
if (*mark & params.desync_fwmark)
{
DLOG("ignoring generated packet\n")
return res;
return VERDICT_PASS;
}
#endif
if (proto_check_ipv4(data, len))
{
ip = (struct ip *) data;
proto = ip->ip_p;
proto_skip_ipv4(&data, &len);
if (params.debug)
{
printf("IP4: ");
print_ip(ip);
}
}
else if (proto_check_ipv6(data, len))
{
ip6hdr = (struct ip6_hdr *) data;
proto_skip_ipv6(&data, &len, &proto, NULL);
if (params.debug)
{
printf("IP6: ");
print_ip6hdr(ip6hdr, proto);
}
}
else
{
// not ipv6 and not ipv4
return res;
}
if (proto==IPPROTO_TCP && proto_check_tcp(data, len))
{
tcphdr = (struct tcphdr *) data;
len_with_th = len;
proto_skip_tcp(&data, &len);
if (params.debug)
{
printf(" ");
print_tcphdr(tcphdr);
printf("\n");
}
if (len) { DLOG("TCP: ") hexdump_limited_dlog(data, len, 32); DLOG("\n") }
res = dpi_desync_tcp_packet(*mark, ifout, data_pkt, len_pkt, ip, ip6hdr, tcphdr, len_with_th, data, len);
// in my FreeBSD divert tests only ipv4 packets were reinjected with correct checksum
// ipv6 packets were with incorrect checksum
#ifdef __FreeBSD__
// FreeBSD tend to pass ipv6 frames with wrong checksum
if (!(res & VERDICT_NOCSUM) && ((res & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr && (res & VERDICT_MASK)==VERDICT_PASS))
#else
if (!(res & VERDICT_NOCSUM) && (res & VERDICT_MASK)==VERDICT_MODIFY)
#endif
tcp_fix_checksum(tcphdr,len_with_th,ip,ip6hdr);
}
else if (proto==IPPROTO_UDP && proto_check_udp(data, len))
{
udphdr = (struct udphdr *) data;
len_with_th = len;
proto_skip_udp(&data, &len);
if (params.debug)
{
printf(" ");
print_udphdr(udphdr);
printf("\n");
}
if (len) { DLOG("UDP: ") hexdump_limited_dlog(data, len, 32); DLOG("\n") }
res = dpi_desync_udp_packet(*mark, ifout, data_pkt, len_pkt, ip, ip6hdr, udphdr, data, len);
#ifdef __FreeBSD__
// FreeBSD tend to pass ipv6 frames with wrong checksum
if (!(res & VERDICT_NOCSUM) && ((res & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr && (res & VERDICT_MASK)==VERDICT_PASS))
#else
if (!(res & VERDICT_NOCSUM) && (res & VERDICT_MASK)==VERDICT_MODIFY)
#endif
udp_fix_checksum(udphdr,len_with_th,ip,ip6hdr);
}
else
{
if (params.debug) printf("\n");
}
return res;
return dpi_desync_packet(*mark, ifout, data_pkt, len_pkt);
}
@ -214,8 +122,8 @@ static int nfq_cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_da
if (ilen >= 0)
{
len = ilen;
uint8_t res = processPacketData(&mark, ifout, data, &len);
switch(res & VERDICT_MASK)
uint8_t verdict = processPacketData(&mark, ifout, data, &len);
switch(verdict & VERDICT_MASK)
{
case VERDICT_MODIFY:
DLOG("packet: id=%d pass modified. len=%zu\n", id, len);
@ -438,16 +346,16 @@ static int dvt_main(void)
else if (rd>0)
{
uint32_t mark=0;
uint8_t res;
uint8_t verdict;
size_t len = rd;
DLOG("packet: id=%u len=%zu\n", id, len)
res = processPacketData(&mark, NULL, buf, &len);
switch (res & VERDICT_MASK)
verdict = processPacketData(&mark, NULL, buf, &len);
switch (verdict & VERDICT_MASK)
{
case VERDICT_PASS:
case VERDICT_MODIFY:
if ((res & VERDICT_MASK)==VERDICT_PASS)
if ((verdict & VERDICT_MASK)==VERDICT_PASS)
DLOG("packet: id=%u reinject unmodified\n", id)
else
DLOG("packet: id=%u reinject modified len=%zu\n", id, len)
@ -544,9 +452,6 @@ static void exithelp(void)
" --dpi-desync-autottl=[<delta>[:<min>[-<max>]]]\t; auto ttl mode for both ipv4 and ipv6. default: %u:%u-%u\n"
" --dpi-desync-autottl6=[<delta>[:<min>[-<max>]]] ; overrides --dpi-desync-autottl for ipv6 only\n"
" --dpi-desync-fooling=<mode>[,<mode>]\t\t; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2\n"
#ifdef __linux__
" --dpi-desync-retrans=0|1\t\t\t; 0(default)=reinject original data packet after fake 1=drop original data packet to force its retransmission\n"
#endif
" --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"
@ -765,38 +670,37 @@ int main(int argc, char **argv)
{"dpi-desync-autottl",optional_argument,0,0}, // optidx=18
{"dpi-desync-autottl6",optional_argument,0,0}, // optidx=19
{"dpi-desync-fooling",required_argument,0,0}, // optidx=20
{"dpi-desync-retrans",optional_argument,0,0}, // optidx=21
{"dpi-desync-repeats",required_argument,0,0}, // optidx=22
{"dpi-desync-skip-nosni",optional_argument,0,0},// optidx=23
{"dpi-desync-split-pos",required_argument,0,0},// optidx=24
{"dpi-desync-ipfrag-pos-tcp",required_argument,0,0},// optidx=25
{"dpi-desync-ipfrag-pos-udp",required_argument,0,0},// optidx=26
{"dpi-desync-badseq-increment",required_argument,0,0},// optidx=27
{"dpi-desync-badack-increment",required_argument,0,0},// optidx=28
{"dpi-desync-any-protocol",optional_argument,0,0},// optidx=29
{"dpi-desync-fake-http",required_argument,0,0},// optidx=30
{"dpi-desync-fake-tls",required_argument,0,0},// optidx=31
{"dpi-desync-fake-unknown",required_argument,0,0},// optidx=32
{"dpi-desync-fake-syndata",required_argument,0,0},// optidx=33
{"dpi-desync-fake-quic",required_argument,0,0},// optidx=34
{"dpi-desync-fake-wireguard",required_argument,0,0},// optidx=35
{"dpi-desync-fake-dht",required_argument,0,0},// optidx=36
{"dpi-desync-fake-unknown-udp",required_argument,0,0},// optidx=37
{"dpi-desync-udplen-increment",required_argument,0,0},// optidx=38
{"dpi-desync-udplen-pattern",required_argument,0,0},// optidx=39
{"dpi-desync-cutoff",required_argument,0,0},// optidx=40
{"dpi-desync-start",required_argument,0,0},// optidx=41
{"hostlist",required_argument,0,0}, // optidx=42
{"hostlist-exclude",required_argument,0,0}, // optidx=43
{"hostlist-auto",required_argument,0,0}, // optidx=44
{"hostlist-auto-fail-threshold",required_argument,0,0}, // optidx=45
{"hostlist-auto-fail-time",required_argument,0,0}, // optidx=46
{"hostlist-auto-retrans-threshold",required_argument,0,0}, // optidx=47
{"hostlist-auto-debug",required_argument,0,0}, // optidx=48
{"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
#ifdef __linux__
{"bind-fix4",no_argument,0,0}, // optidx=49
{"bind-fix6",no_argument,0,0}, // optidx=50
{"bind-fix4",no_argument,0,0}, // optidx=48
{"bind-fix6",no_argument,0,0}, // optidx=49
#endif
{NULL,0,NULL,0}
};
@ -1012,32 +916,24 @@ int main(int argc, char **argv)
}
}
break;
case 21: /* dpi-desync-retrans */
#ifdef __linux__
params.desync_retrans = !optarg || atoi(optarg);
#else
fprintf(stderr, "dpi-desync-retrans is only supported in linux\n");
exit_clean(1);
#endif
break;
case 22: /* dpi-desync-repeats */
case 21: /* dpi-desync-repeats */
if (sscanf(optarg,"%u",&params.desync_repeats)<1 || !params.desync_repeats || params.desync_repeats>20)
{
fprintf(stderr, "dpi-desync-repeats must be within 1..20\n");
exit_clean(1);
}
break;
case 23: /* dpi-desync-skip-nosni */
case 22: /* dpi-desync-skip-nosni */
params.desync_skip_nosni = !optarg || atoi(optarg);
break;
case 24: /* dpi-desync-split-pos */
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)
{
fprintf(stderr, "dpi-desync-split-pos must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN);
exit_clean(1);
}
break;
case 25: /* dpi-desync-ipfrag-pos-tcp */
case 24: /* 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);
@ -1049,7 +945,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 26: /* dpi-desync-ipfrag-pos-udp */
case 25: /* 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);
@ -1061,63 +957,63 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 27: /* dpi-desync-badseq-increments */
case 26: /* 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 28: /* dpi-desync-badack-increment */
case 27: /* 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 29: /* dpi-desync-any-protocol */
case 28: /* dpi-desync-any-protocol */
params.desync_any_proto = !optarg || atoi(optarg);
break;
case 30: /* dpi-desync-fake-http */
case 29: /* 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 31: /* dpi-desync-fake-tls */
case 30: /* 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 32: /* dpi-desync-fake-unknown */
case 31: /* 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 33: /* dpi-desync-fake-syndata */
case 32: /* 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 34: /* dpi-desync-fake-quic */
case 33: /* 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 35: /* dpi-desync-fake-wireguard */
case 34: /* 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 36: /* dpi-desync-fake-dht */
case 35: /* 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 37: /* dpi-desync-fake-unknown-udp */
case 36: /* 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 38: /* dpi-desync-udplen-increment */
case 37: /* 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 39: /* dpi-desync-udplen-pattern */
case 38: /* dpi-desync-udplen-pattern */
{
char buf[sizeof(params.udplen_pattern)];
size_t sz=sizeof(buf);
@ -1125,35 +1021,35 @@ int main(int argc, char **argv)
fill_pattern(params.udplen_pattern,sizeof(params.udplen_pattern),buf,sz);
}
break;
case 40: /* desync-cutoff */
case 39: /* 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 41: /* desync-start */
case 40: /* 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 42: /* hostlist */
case 41: /* hostlist */
if (!strlist_add(&params.hostlist_files, optarg))
{
fprintf(stderr, "strlist_add failed\n");
exit_clean(1);
}
break;
case 43: /* hostlist-exclude */
case 42: /* hostlist-exclude */
if (!strlist_add(&params.hostlist_exclude_files, optarg))
{
fprintf(stderr, "strlist_add failed\n");
exit_clean(1);
}
break;
case 44: /* hostlist-auto */
case 43: /* hostlist-auto */
if (*params.hostlist_auto_filename)
{
fprintf(stderr, "only one auto hostlist is supported\n");
@ -1184,7 +1080,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 45: /* hostlist-auto-fail-threshold */
case 44: /* 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)
{
@ -1192,7 +1088,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 46: /* hostlist-auto-fail-time */
case 45: /* hostlist-auto-fail-time */
params.hostlist_auto_fail_time = (uint8_t)atoi(optarg);
if (params.hostlist_auto_fail_time<1)
{
@ -1200,7 +1096,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 47: /* hostlist-auto-retrans-threshold */
case 46: /* 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)
{
@ -1208,7 +1104,7 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case 48: /* hostlist-auto-debug */
case 47: /* hostlist-auto-debug */
{
FILE *F = fopen(optarg,"a+t");
if (!F)
@ -1224,10 +1120,10 @@ int main(int argc, char **argv)
}
break;
#ifdef __linux__
case 49: /* bind-fix4 */
case 48: /* bind-fix4 */
params.bind_fix4 = true;
break;
case 50: /* bind-fix6 */
case 49: /* bind-fix6 */
params.bind_fix6 = true;
break;
#endif

View File

@ -1,7 +1 @@
#pragma once
#define VERDICT_PASS 0
#define VERDICT_MODIFY 1
#define VERDICT_DROP 2
#define VERDICT_MASK 3
#define VERDICT_NOCSUM 4

67
nfq/packet_queue.c Normal file
View File

@ -0,0 +1,67 @@
#include <stdlib.h>
#include <string.h>
#include "packet_queue.h"
void rawpacket_queue_init(struct rawpacket_tailhead *q)
{
TAILQ_INIT(q);
}
void rawpacket_free(struct rawpacket *rp)
{
if (rp) free(rp->packet);
free(rp);
}
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q)
{
struct rawpacket *rp;
rp = TAILQ_FIRST(q);
if (rp) TAILQ_REMOVE(q, rp, next);
return rp;
}
void rawpacket_queue_destroy(struct rawpacket_tailhead *q)
{
struct rawpacket *rp;
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 *rp = malloc(sizeof(struct rawpacket));
if (!rp) return NULL;
rp->packet = malloc(len);
if (!rp->packet)
{
free(rp);
return NULL;
}
rp->dst = *dst;
rp->fwmark = fwmark;
if (ifout)
{
strncpy(rp->ifout,ifout,sizeof(rp->ifout));
rp->ifout[sizeof(rp->ifout)-1]=0;
}
else
rp->ifout[0]=0;
memcpy(rp->packet,data,len);
rp->len=len;
TAILQ_INSERT_TAIL(q, rp, next);
return rp;
}
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q)
{
const struct rawpacket *rp;
unsigned int ct=0;
TAILQ_FOREACH(rp, q, next) ct++;
return ct;
}
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q)
{
return !TAILQ_FIRST(q);
}

26
nfq/packet_queue.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <sys/queue.h>
#include <net/if.h>
#include <sys/socket.h>
struct rawpacket
{
struct sockaddr_storage dst;
char ifout[IFNAMSIZ+1];
uint32_t fwmark;
size_t len;
uint8_t *packet;
TAILQ_ENTRY(rawpacket) next;
};
TAILQ_HEAD(rawpacket_tailhead, rawpacket);
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_dequeue(struct rawpacket_tailhead *q);
void rawpacket_free(struct rawpacket *rp);

View File

@ -125,6 +125,20 @@ bool IsTLSClientHello(const uint8_t *data, size_t len, bool bPartialIsOK)
return len >= 6 && data[0] == 0x16 && data[1] == 0x03 && data[2] >= 0x01 && data[2] <= 0x03 && data[5] == 0x01 && (bPartialIsOK || TLSRecordLen(data) <= len);
}
size_t TLSHandshakeLen(const uint8_t *data)
{
return data[1] << 16 | data[2] << 8 | data[3]; // HandshakeProtocol length
}
bool IsTLSHandshakeClientHello(const uint8_t *data, size_t len)
{
return len>=4 && data[0]==0x01;
}
bool IsTLSHandshakeFull(const uint8_t *data, size_t len)
{
return (4+TLSHandshakeLen(data))<=len;
}
// bPartialIsOK=true - accept partial packets not containing the whole TLS message
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK)
{
@ -143,14 +157,11 @@ bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const
size_t l, ll;
if (!bPartialIsOK && !IsTLSHandshakeFull(data,len)) return false;
l = 1 + 3 + 2 + 32;
// SessionIDLength
if (len < (l + 1)) return false;
if (!bPartialIsOK)
{
ll = data[1] << 16 | data[2] << 8 | data[3]; // HandshakeProtocol length
if (len < (ll + 4)) return false;
}
l += data[l] + 1;
// CipherSuitesLength
if (len < (l + 2)) return false;
@ -276,7 +287,7 @@ bool IsQUICCryptoHello(const uint8_t *data, size_t len, size_t *hello_offset, si
// offset must be 0 if it's a full segment, not just a chunk
if (coff || (offset+tvb_get_size(data[offset])) >= len) return false;
offset += tvb_get_varint(data + offset, &clen);
if (data[offset] != 0x01 || (offset + clen) > len) return false;
if ((offset + clen) > len || !IsTLSHandshakeClientHello(data+offset,clen)) return false;
if (hello_offset) *hello_offset = offset;
if (hello_len) *hello_len = (size_t)clen;
return true;

View File

@ -22,6 +22,9 @@ uint16_t TLSRecordDataLen(const uint8_t *data);
size_t TLSRecordLen(const uint8_t *data);
bool IsTLSRecordFull(const uint8_t *data, size_t len);
bool IsTLSClientHello(const uint8_t *data, size_t len, bool bPartialIsOK);
size_t TLSHandshakeLen(const uint8_t *data);
bool IsTLSHandshakeClientHello(const uint8_t *data, size_t len);
bool IsTLSHandshakeFull(const uint8_t *data, size_t len);
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK);
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);