mirror of
https://github.com/bol-van/zapret.git
synced 2025-05-24 22:32:58 +03:00
nfqws: tls client hello reassemble
This commit is contained in:
@@ -25,9 +25,23 @@ static void connswap(const t_conn *c, t_conn *c2)
|
||||
c2->dport = c->sport;
|
||||
}
|
||||
|
||||
void ConntrackClearHostname(t_ctrack *track)
|
||||
{
|
||||
if (track->hostname)
|
||||
{
|
||||
free(track->hostname);
|
||||
track->hostname = NULL;
|
||||
}
|
||||
}
|
||||
static void ConntrackClearTrack(t_ctrack *track)
|
||||
{
|
||||
ConntrackClearHostname(track);
|
||||
ReasmClear(&track->reasm_orig);
|
||||
}
|
||||
|
||||
static void ConntrackFreeElem(t_conntrack_pool *elem)
|
||||
{
|
||||
if (elem->track.hostname) free(elem->track.hostname);
|
||||
ConntrackClearTrack(&elem->track);
|
||||
free(elem);
|
||||
}
|
||||
|
||||
@@ -309,3 +323,36 @@ void ConntrackPoolDump(const t_conntrack *p)
|
||||
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff, t->track.hostname, ConntrackProtoName(t->track.l7proto));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void ReasmClear(t_reassemble *reasm)
|
||||
{
|
||||
if (reasm->packet)
|
||||
{
|
||||
free(reasm->packet);
|
||||
reasm->packet = NULL;
|
||||
}
|
||||
reasm->size = reasm->size_present = 0;
|
||||
}
|
||||
bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start)
|
||||
{
|
||||
reasm->packet = malloc(size_requested);
|
||||
if (!reasm->packet) return false;
|
||||
reasm->size = size_requested;
|
||||
reasm->size_present = 0;
|
||||
reasm->seq = seq_start;
|
||||
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
|
||||
|
||||
size_t szcopy;
|
||||
szcopy = reasm->size - reasm->size_present;
|
||||
if (len<szcopy) szcopy = len;
|
||||
memcpy(reasm->packet + reasm->size_present, payload, szcopy);
|
||||
reasm->size_present += szcopy;
|
||||
reasm->seq += (uint32_t)szcopy;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@@ -34,6 +34,14 @@ typedef struct
|
||||
uint8_t l4proto; // IPPROTO_TCP, IPPROTO_UDP
|
||||
} t_conn;
|
||||
|
||||
// this structure helps to reassemble continuous packets streams. it does not support out-of-orders
|
||||
typedef struct {
|
||||
uint8_t *packet; // allocated for size during reassemble request. requestor must know the message size.
|
||||
uint32_t seq; // current seq number. if a packet comes with an unexpected seq - it fails reassemble session.
|
||||
size_t size; // expected message size. success means that we have received exactly 'size' bytes and have them in 'packet'
|
||||
size_t size_present; // how many bytes already stored in 'packet'
|
||||
} t_reassemble;
|
||||
|
||||
// SYN - SYN or SYN/ACK received
|
||||
// ESTABLISHED - any except SYN or SYN/ACK received
|
||||
// FIN - FIN or RST received
|
||||
@@ -55,11 +63,14 @@ 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
|
||||
uint32_t req_seq; // sequence number of the request (to track retransmissions)
|
||||
bool req_seq_start_present, req_seq_present;
|
||||
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
|
||||
|
||||
bool b_cutoff; // mark for deletion
|
||||
bool b_wssize_cutoff, b_desync_cutoff;
|
||||
|
||||
t_reassemble reasm_orig;
|
||||
|
||||
t_l7proto l7proto;
|
||||
char *hostname;
|
||||
} t_ctrack;
|
||||
@@ -85,3 +96,11 @@ bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr
|
||||
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);
|
||||
void ConntrackPoolPurge(t_conntrack *p);
|
||||
void ConntrackClearHostname(t_ctrack *track);
|
||||
|
||||
bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start);
|
||||
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);
|
||||
inline static bool ReasmIsEmpty(t_reassemble *reasm) {return !reasm->size;}
|
||||
inline static bool ReasmIsFull(t_reassemble *reasm) {return !ReasmIsEmpty(reasm) && (reasm->size==reasm->size_present);}
|
||||
|
307
nfq/desync.c
307
nfq/desync.c
@@ -155,13 +155,14 @@ static void maybe_cutoff(t_ctrack *ctrack, uint8_t proto)
|
||||
ctrack->b_wssize_cutoff |= cutoff_test(ctrack, params.wssize_cutoff, params.wssize_cutoff_mode);
|
||||
ctrack->b_desync_cutoff |= cutoff_test(ctrack, params.desync_cutoff, params.desync_cutoff_mode);
|
||||
|
||||
// we do not need conntrack entry anymore if all cutoff conditions are either not defined or reached
|
||||
// do not drop udp entry because it will be recreated when next packet arrives
|
||||
if (proto==IPPROTO_TCP)
|
||||
// we do not need conntrack entry anymore if all cutoff conditions are either not defined or reached
|
||||
// do not drop udp entry because it will be recreated when next packet arrives
|
||||
ctrack->b_cutoff |= \
|
||||
(!params.wssize || ctrack->b_wssize_cutoff) &&
|
||||
(!params.desync_cutoff || ctrack->b_desync_cutoff) &&
|
||||
(!*params.hostlist_auto_filename || ctrack->req_retrans_counter==RETRANS_COUNTER_STOP);
|
||||
(!*params.hostlist_auto_filename || ctrack->req_retrans_counter==RETRANS_COUNTER_STOP) &&
|
||||
ReasmIsEmpty(&ctrack->reasm_orig);
|
||||
}
|
||||
}
|
||||
static void wssize_cutoff(t_ctrack *ctrack)
|
||||
@@ -172,8 +173,16 @@ static void wssize_cutoff(t_ctrack *ctrack)
|
||||
maybe_cutoff(ctrack, IPPROTO_TCP);
|
||||
}
|
||||
}
|
||||
static void forced_wssize_cutoff(t_ctrack *ctrack)
|
||||
{
|
||||
if (ctrack && params.wssize && !ctrack->b_wssize_cutoff)
|
||||
{
|
||||
DLOG("forced wssize-cutoff\n");
|
||||
wssize_cutoff(ctrack);
|
||||
}
|
||||
}
|
||||
|
||||
static void ctrack_stop_req_counter(t_ctrack *ctrack)
|
||||
static void ctrack_stop_retrans_counter(t_ctrack *ctrack)
|
||||
{
|
||||
if (ctrack && *params.hostlist_auto_filename)
|
||||
{
|
||||
@@ -187,24 +196,26 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho
|
||||
{
|
||||
if (*params.hostlist_auto_filename && ctrack && ctrack->req_retrans_counter!=RETRANS_COUNTER_STOP)
|
||||
{
|
||||
ctrack->req_retrans_counter++;
|
||||
DLOG("req retrans counter : %u/%u\n",ctrack->req_retrans_counter, threshold);
|
||||
if (ctrack->req_retrans_counter >= threshold)
|
||||
{
|
||||
DLOG("req retrans threshold reached\n");
|
||||
ctrack_stop_req_counter(ctrack);
|
||||
return true;
|
||||
}
|
||||
if (l4proto==IPPROTO_TCP)
|
||||
{
|
||||
if (!ctrack->req_seq) ctrack->req_seq = ctrack->seq_last;
|
||||
if (ctrack->seq_last != ctrack->req_seq)
|
||||
{
|
||||
DLOG("another request, not retransmission. stop tracking.\n");
|
||||
ctrack_stop_req_counter(ctrack);
|
||||
return false;
|
||||
}
|
||||
if (!ctrack->req_seq_present)
|
||||
return false;
|
||||
if (!seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end))
|
||||
{
|
||||
DLOG("req retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end);
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
ctrack->req_seq_present = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
ctrack->req_retrans_counter++;
|
||||
if (ctrack->req_retrans_counter >= threshold)
|
||||
{
|
||||
DLOG("req retrans threshold reached : %u/%u\n",ctrack->req_retrans_counter, threshold);
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
return true;
|
||||
}
|
||||
DLOG("req retrans counter : %u/%u\n",ctrack->req_retrans_counter, threshold);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -227,7 +238,7 @@ static void auto_hostlist_failed(const char *hostname)
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : fail counter %d/%d", hostname, fail_counter->counter, params.hostlist_auto_fail_threshold);
|
||||
if (fail_counter->counter >= params.hostlist_auto_fail_threshold)
|
||||
{
|
||||
DLOG("auto hostlist : fail threshold reached. adding %s to auto hostlist\n", hostname);
|
||||
DLOG("auto hostlist : fail threshold reached. about to add %s to auto hostlist\n", hostname);
|
||||
HostFailPoolDel(¶ms.hostlist_auto_fail_counters, fail_counter);
|
||||
|
||||
DLOG("auto hostlist : rechecking %s to avoid duplicates\n", hostname);
|
||||
@@ -255,7 +266,69 @@ static void auto_hostlist_failed(const char *hostname)
|
||||
}
|
||||
}
|
||||
|
||||
#define CONNTRACK_REQUIRED (params.wssize || params.desync_cutoff || *params.hostlist_auto_filename)
|
||||
static void process_retrans_fail(t_ctrack *ctrack, uint8_t proto)
|
||||
{
|
||||
if (ctrack && ctrack->hostname && auto_hostlist_retrans(ctrack, proto, params.hostlist_auto_retrans_threshold))
|
||||
{
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : tcp retrans threshold reached", ctrack->hostname);
|
||||
auto_hostlist_failed(ctrack->hostname);
|
||||
}
|
||||
}
|
||||
|
||||
static bool reasm_start(t_ctrack *ctrack, t_reassemble *reasm, size_t sz, size_t szMax, const uint8_t *data_payload, size_t len_payload)
|
||||
{
|
||||
ReasmClear(reasm);
|
||||
if (sz<=szMax)
|
||||
{
|
||||
if (ReasmInit(reasm,sz,ctrack->seq_last))
|
||||
{
|
||||
ReasmFeed(reasm,ctrack->seq_last,data_payload,len_payload);
|
||||
DLOG("starting reassemble. now we have %zu/%zu\n",reasm->size_present,reasm->size);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
DLOG("reassemble init failed. out of memory\n");
|
||||
}
|
||||
else
|
||||
DLOG("unexpected large payload for reassemble: size=%zu\n",sz);
|
||||
return false;
|
||||
}
|
||||
static bool reasm_orig_start(t_ctrack *ctrack, size_t sz, size_t szMax, const uint8_t *data_payload, size_t len_payload)
|
||||
{
|
||||
return reasm_start(ctrack,&ctrack->reasm_orig,sz,szMax,data_payload,len_payload);
|
||||
}
|
||||
static bool reasm_feed(t_ctrack *ctrack, t_reassemble *reasm, const uint8_t *data_payload, size_t len_payload)
|
||||
{
|
||||
if (ctrack && !ReasmIsEmpty(reasm))
|
||||
{
|
||||
if (ReasmFeed(reasm,ctrack->seq_last,data_payload,len_payload))
|
||||
{
|
||||
DLOG("reassemble : feeding data payload size=%zu. now we have %zu/%zu\n",len_payload,reasm->size_present,reasm->size)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ReasmClear(reasm);
|
||||
DLOG("reassemble session failed\n")
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static bool reasm_orig_feed(t_ctrack *ctrack, const uint8_t *data_payload, size_t len_payload)
|
||||
{
|
||||
return reasm_feed(ctrack, &ctrack->reasm_orig, data_payload, len_payload);
|
||||
}
|
||||
static void reasm_orig_fin(t_ctrack *ctrack)
|
||||
{
|
||||
if (ctrack && ReasmIsFull(&ctrack->reasm_orig))
|
||||
{
|
||||
DLOG("reassemble session finished\n");
|
||||
ReasmClear(&ctrack->reasm_orig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// result : true - drop original packet, false = dont drop
|
||||
packet_process_result 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)
|
||||
{
|
||||
@@ -271,13 +344,10 @@ packet_process_result dpi_desync_tcp_packet(uint32_t fwmark, const char *ifout,
|
||||
|
||||
if (!!ip == !!ip6hdr) return res; // one and only one must be present
|
||||
|
||||
if (CONNTRACK_REQUIRED)
|
||||
{
|
||||
ConntrackPoolPurge(¶ms.conntrack);
|
||||
if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, tcphdr, NULL, len_payload, &ctrack, &bReverse))
|
||||
maybe_cutoff(ctrack, IPPROTO_TCP);
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
}
|
||||
ConntrackPoolPurge(¶ms.conntrack);
|
||||
if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, tcphdr, NULL, len_payload, &ctrack, &bReverse))
|
||||
maybe_cutoff(ctrack, IPPROTO_TCP);
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
|
||||
//ConntrackPoolDump(¶ms.conntrack);
|
||||
|
||||
@@ -290,15 +360,16 @@ packet_process_result dpi_desync_tcp_packet(uint32_t fwmark, const char *ifout,
|
||||
if (bReverse)
|
||||
{
|
||||
// process reply packets for auto hostlist mode
|
||||
// by looking at RSTs or HTTP replies we decide whether original request looks to be blocked by DPI
|
||||
if (*params.hostlist_auto_filename && ctrack && ctrack->hostname && ctrack->req_retrans_counter != RETRANS_COUNTER_STOP)
|
||||
// by looking at RSTs or HTTP replies we decide whether original request looks like DPI blocked
|
||||
// we only process first-sequence replies. do not react to subsequent redirects or RSTs
|
||||
if (*params.hostlist_auto_filename && ctrack && ctrack->hostname && (ctrack->ack_last-ctrack->ack0)==1)
|
||||
{
|
||||
bool bFail=false, bStop=false;
|
||||
bool bFail=false;
|
||||
if (tcphdr->th_flags & TH_RST)
|
||||
{
|
||||
DLOG("incoming RST detected for hostname %s\n", ctrack->hostname);
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : incoming RST", ctrack->hostname);
|
||||
bFail = bStop = true;
|
||||
bFail = true;
|
||||
}
|
||||
else if (len_payload && ctrack->l7proto==HTTP)
|
||||
{
|
||||
@@ -319,12 +390,11 @@ packet_process_result dpi_desync_tcp_packet(uint32_t fwmark, const char *ifout,
|
||||
// received not http reply. do not monitor this connection anymore
|
||||
DLOG("incoming unknown HTTP data detected for hostname %s\n", ctrack->hostname);
|
||||
}
|
||||
bStop = true;
|
||||
}
|
||||
if (bFail)
|
||||
auto_hostlist_failed(ctrack->hostname);
|
||||
if (bStop)
|
||||
ctrack_stop_req_counter(ctrack);
|
||||
if (tcphdr->th_flags & TH_RST)
|
||||
ConntrackClearHostname(ctrack); // do not react to further dup RSTs
|
||||
}
|
||||
|
||||
return res; // nothing to do. do not waste cpu
|
||||
@@ -408,78 +478,119 @@ packet_process_result dpi_desync_tcp_packet(uint32_t fwmark, const char *ifout,
|
||||
bool bIsHttp;
|
||||
bool bKnownProtocol = false;
|
||||
uint8_t *p, *phost;
|
||||
if ((bIsHttp = IsHttp(data_payload,len_payload)))
|
||||
const uint8_t *rdata_payload = data_payload;
|
||||
size_t rlen_payload = len_payload;
|
||||
|
||||
if (reasm_orig_feed(ctrack,data_payload,len_payload))
|
||||
{
|
||||
rdata_payload = ctrack->reasm_orig.packet;
|
||||
rlen_payload = ctrack->reasm_orig.size_present;
|
||||
}
|
||||
|
||||
if ((bIsHttp = IsHttp(rdata_payload,rlen_payload)))
|
||||
{
|
||||
DLOG("packet contains HTTP request\n")
|
||||
if (ctrack && !ctrack->l7proto) ctrack->l7proto = HTTP;
|
||||
if (params.wssize)
|
||||
{
|
||||
DLOG("forced wssize-cutoff\n");
|
||||
wssize_cutoff(ctrack);
|
||||
}
|
||||
forced_wssize_cutoff(ctrack);
|
||||
fake = params.fake_http;
|
||||
fake_size = params.fake_http_size;
|
||||
if (params.hostlist || params.debug) bHaveHost=HttpExtractHost(data_payload,len_payload,host,sizeof(host));
|
||||
if (params.hostlist && !bHaveHost)
|
||||
if (params.hostlist || params.hostlist_exclude)
|
||||
{
|
||||
DLOG("not applying tampering to HTTP without Host:\n")
|
||||
return res;
|
||||
}
|
||||
bKnownProtocol = true;
|
||||
}
|
||||
else if (IsTLSClientHello(data_payload,len_payload))
|
||||
{
|
||||
DLOG("packet contains TLS ClientHello\n")
|
||||
if (ctrack && !ctrack->l7proto) ctrack->l7proto = TLS;
|
||||
if (params.wssize)
|
||||
{
|
||||
DLOG("forced wssize-cutoff\n");
|
||||
wssize_cutoff(ctrack);
|
||||
}
|
||||
fake = params.fake_tls;
|
||||
fake_size = params.fake_tls_size;
|
||||
if (params.hostlist || params.desync_skip_nosni || params.debug)
|
||||
{
|
||||
bHaveHost=TLSHelloExtractHost(data_payload,len_payload,host,sizeof(host));
|
||||
if (params.desync_skip_nosni && !bHaveHost)
|
||||
bHaveHost=HttpExtractHost(rdata_payload,rlen_payload,host,sizeof(host));
|
||||
if (!bHaveHost)
|
||||
{
|
||||
DLOG("not applying tampering to TLS ClientHello without hostname in the SNI\n")
|
||||
DLOG("not applying tampering to HTTP without Host:\n")
|
||||
process_retrans_fail(ctrack, IPPROTO_TCP);
|
||||
reasm_orig_fin(ctrack);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
if (ctrack)
|
||||
{
|
||||
// we do not reassemble http
|
||||
if (!ctrack->req_seq_present)
|
||||
{
|
||||
ctrack->req_seq_start=ctrack->seq_last;
|
||||
ctrack->req_seq_end=ctrack->pos_orig-1;
|
||||
ctrack->req_seq_start_present=ctrack->req_seq_present=true;
|
||||
DLOG("req retrans : tcp seq interval %u-%u\n",ctrack->req_seq_start,ctrack->req_seq_end);
|
||||
}
|
||||
}
|
||||
bKnownProtocol = true;
|
||||
}
|
||||
else
|
||||
else if (IsTLSClientHello(rdata_payload,rlen_payload,TLS_PARTIALS_ENABLE))
|
||||
{
|
||||
bool bReqFull = IsTLSRecordFull(rdata_payload,rlen_payload);
|
||||
DLOG(bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n")
|
||||
fake = params.fake_tls;
|
||||
fake_size = params.fake_tls_size;
|
||||
bHaveHost=TLSHelloExtractHost(rdata_payload,rlen_payload,host,sizeof(host),TLS_PARTIALS_ENABLE);
|
||||
if (ctrack)
|
||||
{
|
||||
if (!ctrack->l7proto) ctrack->l7proto = TLS;
|
||||
if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
|
||||
// do not reconstruct unexpected large payload (they are feeding garbage ?)
|
||||
reasm_orig_start(ctrack,TLSRecordLen(data_payload),4096,data_payload,len_payload);
|
||||
if (!ctrack->req_seq_start_present)
|
||||
{
|
||||
// lower bound of request seq interval
|
||||
ctrack->req_seq_start=ctrack->seq_last;
|
||||
ctrack->req_seq_start_present=true;
|
||||
}
|
||||
if (!ctrack->req_seq_present && bReqFull)
|
||||
{
|
||||
// upper bound of request seq interval
|
||||
ctrack->req_seq_end=ctrack->pos_orig-1;
|
||||
ctrack->req_seq_present=ctrack->req_seq_start_present;
|
||||
DLOG("req retrans : seq interval %u-%u\n",ctrack->req_seq_start,ctrack->req_seq_end);
|
||||
}
|
||||
}
|
||||
if (bReqFull || !ctrack || ReasmIsEmpty(&ctrack->reasm_orig)) forced_wssize_cutoff(ctrack);
|
||||
|
||||
if (params.desync_skip_nosni && !bHaveHost)
|
||||
{
|
||||
DLOG("not applying tampering to TLS ClientHello without hostname in the SNI\n")
|
||||
process_retrans_fail(ctrack, IPPROTO_TCP);
|
||||
reasm_orig_fin(ctrack);
|
||||
return res;
|
||||
}
|
||||
bKnownProtocol = true;
|
||||
}
|
||||
|
||||
reasm_orig_fin(ctrack);
|
||||
rdata_payload=NULL;
|
||||
|
||||
if (bHaveHost)
|
||||
{
|
||||
bool bExcluded;
|
||||
DLOG("hostname: %s\n",host)
|
||||
if ((params.hostlist || params.hostlist_exclude) && !HostlistCheck(params.hostlist, params.hostlist_exclude, host, &bExcluded))
|
||||
{
|
||||
DLOG("not applying tampering to this request\n")
|
||||
if (ctrack)
|
||||
{
|
||||
if (!bExcluded && *params.hostlist_auto_filename)
|
||||
{
|
||||
if (!ctrack->hostname) ctrack->hostname=strdup(host);
|
||||
process_retrans_fail(ctrack, IPPROTO_TCP);
|
||||
}
|
||||
else
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
}
|
||||
process_retrans_fail(ctrack, IPPROTO_TCP);
|
||||
|
||||
if (!bKnownProtocol)
|
||||
{
|
||||
// received unknown payload. it means we are out of the request retransmission phase. stop counter
|
||||
ctrack_stop_req_counter(ctrack);
|
||||
|
||||
if (!params.desync_any_proto) return res;
|
||||
DLOG("applying tampering to unknown protocol\n")
|
||||
fake = params.fake_unknown;
|
||||
fake_size = params.fake_unknown_size;
|
||||
}
|
||||
|
||||
if (bHaveHost)
|
||||
{
|
||||
DLOG("hostname: %s\n",host)
|
||||
bool bExcluded;
|
||||
if ((params.hostlist || params.hostlist_exclude) && !HostlistCheck(params.hostlist, params.hostlist_exclude, host, &bExcluded))
|
||||
{
|
||||
DLOG("not applying tampering to this request\n")
|
||||
if (!bExcluded && *params.hostlist_auto_filename && ctrack)
|
||||
{
|
||||
if (!ctrack->hostname) ctrack->hostname=strdup(host);
|
||||
if (auto_hostlist_retrans(ctrack, IPPROTO_TCP, params.hostlist_auto_retrans_threshold))
|
||||
{
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : tcp retrans threshold reached", ctrack->hostname);
|
||||
auto_hostlist_failed(host);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
if (bIsHttp && (params.hostcase || params.hostnospace || params.domcase) && (phost = (uint8_t*)memmem(data_payload, len_payload, "\r\nHost: ", 8)))
|
||||
{
|
||||
if (params.hostcase)
|
||||
@@ -773,6 +884,7 @@ packet_process_result dpi_desync_tcp_packet(uint32_t fwmark, const char *ifout,
|
||||
return frag;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -793,13 +905,10 @@ packet_process_result dpi_desync_udp_packet(uint32_t fwmark, const char *ifout,
|
||||
|
||||
if (!!ip == !!ip6hdr) return res; // one and only one must be present
|
||||
|
||||
if (CONNTRACK_REQUIRED)
|
||||
{
|
||||
ConntrackPoolPurge(¶ms.conntrack);
|
||||
if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, NULL, udphdr, len_payload, &ctrack, &bReverse))
|
||||
maybe_cutoff(ctrack, IPPROTO_UDP);
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
}
|
||||
ConntrackPoolPurge(¶ms.conntrack);
|
||||
if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, NULL, udphdr, len_payload, &ctrack, &bReverse))
|
||||
maybe_cutoff(ctrack, IPPROTO_UDP);
|
||||
HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters);
|
||||
|
||||
//ConntrackPoolDump(¶ms.conntrack);
|
||||
|
||||
@@ -883,7 +992,7 @@ packet_process_result dpi_desync_udp_packet(uint32_t fwmark, const char *ifout,
|
||||
else
|
||||
{
|
||||
// received payload without host. it means we are out of the request retransmission phase. stop counter
|
||||
ctrack_stop_req_counter(ctrack);
|
||||
ctrack_stop_retrans_counter(ctrack);
|
||||
|
||||
if (IsWireguardHandshakeInitiation(data_payload,len_payload))
|
||||
{
|
||||
@@ -920,11 +1029,7 @@ packet_process_result dpi_desync_udp_packet(uint32_t fwmark, const char *ifout,
|
||||
if (!bExcluded && *params.hostlist_auto_filename && ctrack)
|
||||
{
|
||||
if (!ctrack->hostname) ctrack->hostname=strdup(host);
|
||||
if (auto_hostlist_retrans(ctrack, IPPROTO_UDP, params.hostlist_auto_retrans_threshold))
|
||||
{
|
||||
HOSTLIST_DEBUGLOG_APPEND("%s : udp retrans threshold reached", ctrack->hostname);
|
||||
auto_hostlist_failed(host);
|
||||
}
|
||||
process_retrans_fail(ctrack, IPPROTO_UDP);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <time.h>
|
||||
|
||||
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
|
||||
{
|
||||
@@ -149,12 +150,12 @@ void dbgprint_socket_buffers(int fd)
|
||||
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf)
|
||||
{
|
||||
DLOG("set_socket_buffers fd=%d rcvbuf=%d sndbuf=%d\n", fd, rcvbuf, sndbuf)
|
||||
if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) < 0)
|
||||
{
|
||||
perror("setsockopt (SO_RCVBUF)");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
if (rcvbuf && setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) < 0)
|
||||
{
|
||||
perror("setsockopt (SO_RCVBUF)");
|
||||
close(fd);
|
||||
return false;
|
||||
}
|
||||
if (sndbuf && setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(int)) < 0)
|
||||
{
|
||||
perror("setsockopt (SO_SNDBUF)");
|
||||
@@ -188,6 +189,11 @@ void phton64(uint8_t *p, uint64_t v)
|
||||
p[7] = (uint8_t)(v >> 0);
|
||||
}
|
||||
|
||||
bool seq_within(uint32_t s, uint32_t s1, uint32_t s2)
|
||||
{
|
||||
return s2>=s1 && s>=s1 && s<=s2 || s2<s1 && (s<=s2 || s>=s1);
|
||||
}
|
||||
|
||||
bool ipv6_addr_is_zero(const struct in6_addr *a)
|
||||
{
|
||||
return !memcmp(a,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",16);
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <sys/socket.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "params.h"
|
||||
|
||||
@@ -19,6 +20,8 @@ void print_sockaddr(const struct sockaddr *sa);
|
||||
void ntop46(const struct sockaddr *sa, char *str, size_t len);
|
||||
void ntop46_port(const struct sockaddr *sa, char *str, size_t len);
|
||||
|
||||
bool seq_within(uint32_t s, uint32_t s1, uint32_t s2);
|
||||
|
||||
void dbgprint_socket_buffers(int fd);
|
||||
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf);
|
||||
|
||||
|
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "params.h"
|
||||
#include "pools.h"
|
||||
#include "conntrack.h"
|
||||
#include "desync.h"
|
||||
@@ -12,6 +11,8 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define TLS_PARTIALS_ENABLE true
|
||||
|
||||
#define Q_RCVBUF (128*1024) // in bytes
|
||||
#define Q_SNDBUF (64*1024) // in bytes
|
||||
#define RAW_SNDBUF (64*1024) // in bytes
|
||||
|
@@ -108,12 +108,25 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool IsTLSClientHello(const uint8_t *data, size_t len)
|
||||
uint16_t TLSRecordDataLen(const uint8_t *data)
|
||||
{
|
||||
return len >= 6 && data[0] == 0x16 && data[1] == 0x03 && data[2] >= 0x01 && data[2] <= 0x03 && data[5] == 0x01 && (pntoh16(data + 3) + 5) <= len;
|
||||
return pntoh16(data + 3);
|
||||
}
|
||||
bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext)
|
||||
size_t TLSRecordLen(const uint8_t *data)
|
||||
{
|
||||
return TLSRecordDataLen(data) + 5;
|
||||
}
|
||||
bool IsTLSRecordFull(const uint8_t *data, size_t len)
|
||||
{
|
||||
return TLSRecordLen(data)<=len;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
// +0
|
||||
// u8 HandshakeType: ClientHello
|
||||
@@ -133,8 +146,11 @@ bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const
|
||||
l = 1 + 3 + 2 + 32;
|
||||
// SessionIDLength
|
||||
if (len < (l + 1)) return false;
|
||||
ll = data[1] << 16 | data[2] << 8 | data[3]; // HandshakeProtocol length
|
||||
if (len < (ll + 4)) 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;
|
||||
@@ -148,7 +164,15 @@ bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const
|
||||
data += l; len -= l;
|
||||
l = pntoh16(data);
|
||||
data += 2; len -= 2;
|
||||
if (len < l) return false;
|
||||
|
||||
if (bPartialIsOK)
|
||||
{
|
||||
if (len < l) l = len;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (len < l) return false;
|
||||
}
|
||||
|
||||
while (l >= 4)
|
||||
{
|
||||
@@ -170,14 +194,14 @@ bool TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const
|
||||
|
||||
return false;
|
||||
}
|
||||
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext)
|
||||
bool TLSFindExt(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext, bool bPartialIsOK)
|
||||
{
|
||||
// +0
|
||||
// u8 ContentType: Handshake
|
||||
// u16 Version: TLS1.0
|
||||
// u16 Length
|
||||
if (!IsTLSClientHello(data, len)) return false;
|
||||
return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext);
|
||||
if (!IsTLSClientHello(data, len, bPartialIsOK)) return false;
|
||||
return TLSFindExtInHandshake(data + 5, len - 5, type, ext, len_ext, bPartialIsOK);
|
||||
}
|
||||
static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, size_t len_host)
|
||||
{
|
||||
@@ -196,20 +220,20 @@ static bool TLSExtractHostFromExt(const uint8_t *ext, size_t elen, char *host, s
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host)
|
||||
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK)
|
||||
{
|
||||
const uint8_t *ext;
|
||||
size_t elen;
|
||||
|
||||
if (!TLSFindExt(data, len, 0, &ext, &elen)) return false;
|
||||
if (!TLSFindExt(data, len, 0, &ext, &elen, bPartialIsOK)) return false;
|
||||
return TLSExtractHostFromExt(ext, elen, host, len_host);
|
||||
}
|
||||
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host)
|
||||
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK)
|
||||
{
|
||||
const uint8_t *ext;
|
||||
size_t elen;
|
||||
|
||||
if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen)) return false;
|
||||
if (!TLSFindExtInHandshake(data, len, 0, &ext, &elen, bPartialIsOK)) return false;
|
||||
return TLSExtractHostFromExt(ext, elen, host, len_host);
|
||||
}
|
||||
|
||||
@@ -580,7 +604,7 @@ bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host
|
||||
if (!IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len)) return false;
|
||||
if (bIsCryptoHello) *bIsCryptoHello=true;
|
||||
|
||||
return TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, len_host);
|
||||
return TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, len_host, true);
|
||||
}
|
||||
|
||||
bool IsQUICInitial(const uint8_t *data, size_t len)
|
||||
|
@@ -5,6 +5,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "crypto/sha.h"
|
||||
#include "crypto/aes-gcm.h"
|
||||
#include "helpers.h"
|
||||
|
||||
bool IsHttp(const uint8_t *data, size_t len);
|
||||
// header must be passed like this : "\nHost:"
|
||||
@@ -17,11 +18,14 @@ 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);
|
||||
|
||||
bool IsTLSClientHello(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 TLSFindExtInHandshake(const uint8_t *data, size_t len, uint16_t type, const uint8_t **ext, size_t *len_ext);
|
||||
bool TLSHelloExtractHost(const uint8_t *data, size_t len, char *host, size_t len_host);
|
||||
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host);
|
||||
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);
|
||||
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);
|
||||
bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *host, size_t len_host, bool bPartialIsOK);
|
||||
|
||||
bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len);
|
||||
bool IsDhtD1(const uint8_t *data, size_t len);
|
||||
|
Reference in New Issue
Block a user