diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index 375f518..bfb04f4 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/arm/nfqws b/binaries/arm/nfqws index 25b0d0f..201b23c 100755 Binary files a/binaries/arm/nfqws and b/binaries/arm/nfqws differ diff --git a/binaries/freebsd-x64/dvtws b/binaries/freebsd-x64/dvtws index 83aae99..c0edc07 100755 Binary files a/binaries/freebsd-x64/dvtws and b/binaries/freebsd-x64/dvtws differ diff --git a/binaries/mips32r1-lsb/nfqws b/binaries/mips32r1-lsb/nfqws index a2f069f..c2324b5 100755 Binary files a/binaries/mips32r1-lsb/nfqws and b/binaries/mips32r1-lsb/nfqws differ diff --git a/binaries/mips32r1-msb/nfqws b/binaries/mips32r1-msb/nfqws index 592a659..db01231 100755 Binary files a/binaries/mips32r1-msb/nfqws and b/binaries/mips32r1-msb/nfqws differ diff --git a/binaries/mips64r2-msb/nfqws b/binaries/mips64r2-msb/nfqws index 6c8d796..7cefb01 100755 Binary files a/binaries/mips64r2-msb/nfqws and b/binaries/mips64r2-msb/nfqws differ diff --git a/binaries/ppc/nfqws b/binaries/ppc/nfqws index 1df5254..ad43ed4 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index d27cbcb..3525302 100755 Binary files a/binaries/x86/nfqws and b/binaries/x86/nfqws differ diff --git a/binaries/x86_64/nfqws b/binaries/x86_64/nfqws index b788326..15c3f4a 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/nfq/conntrack.c b/nfq/conntrack.c index 938c2f9..34e6ebd 100644 --- a/nfq/conntrack.c +++ b/nfq/conntrack.c @@ -387,9 +387,9 @@ bool ReasmResize(t_reassemble *reasm, size_t 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) +bool ReasmFeed(t_reassemble *reasm, size_t seq, const void *payload, size_t len) { - if (seq!=-1 && reasm->seq!=seq) return false; // fail session if out of sequence + if (reasm->seq!=seq) return false; // fail session if out of sequence size_t szcopy; szcopy = reasm->size - reasm->size_present; @@ -400,3 +400,7 @@ bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t le return true; } +bool ReasmHasSpace(t_reassemble *reasm, size_t len) +{ + return (reasm->size_present+len)<=reasm->size; +} diff --git a/nfq/conntrack.h b/nfq/conntrack.h index 7114c99..86366e2 100644 --- a/nfq/conntrack.h +++ b/nfq/conntrack.h @@ -39,7 +39,7 @@ typedef struct // 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 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; @@ -109,6 +109,8 @@ 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); +bool ReasmFeed(t_reassemble *reasm, size_t seq, const void *payload, size_t len); +// check if it has enough space to buffer 'len' bytes +bool ReasmHasSpace(t_reassemble *reasm, 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);} diff --git a/nfq/desync.c b/nfq/desync.c index b0df9ba..08c83ec 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -277,7 +277,7 @@ static bool reasm_start(t_ctrack *ctrack, t_reassemble *reasm, size_t sz, size_t if (ReasmInit(reasm,sz,ctrack->seq_last)) { ReasmFeed(reasm,ctrack->seq_last,data_payload,len_payload); - DLOG(reasm->size_present==reasm->size ? "starting reassemble. now we have %zu\n" : "starting reassemble. now we have %zu/%zu\n",reasm->size_present,reasm->size); + DLOG("starting reassemble. now we have %zu/%zu\n",reasm->size_present,reasm->size); return true; } else @@ -295,9 +295,9 @@ static bool reasm_feed(t_ctrack *ctrack, t_reassemble *reasm, uint8_t proto, con { if (ctrack && !ReasmIsEmpty(reasm)) { - if (ReasmFeed(reasm,proto==IPPROTO_TCP ? ctrack->seq_last : -1,data_payload,len_payload)) + if (ReasmFeed(reasm,proto==IPPROTO_TCP ? (size_t)ctrack->seq_last : reasm->size_present, data_payload, len_payload)) { - DLOG(reasm->size_present==reasm->size ? "reassemble : feeding data payload size=%zu. now we have %zu\n" : "reassemble : feeding data payload size=%zu. now we have %zu/%zu\n", len_payload,reasm->size_present,reasm->size) + DLOG("reassemble : feeding data payload size=%zu. now we have %zu/%zu\n", len_payload,reasm->size_present,reasm->size) return true; } else @@ -313,27 +313,26 @@ static bool reasm_orig_feed(t_ctrack *ctrack, uint8_t proto, const uint8_t *data { return reasm_feed(ctrack, &ctrack->reasm_orig, proto, 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); - send_delayed(ctrack); - } -} -static void reasm_orig_cancel(t_ctrack *ctrack) +static void reasm_orig_stop(t_ctrack *ctrack, const char *dlog_msg) { if (ctrack) { if (!ReasmIsEmpty(&ctrack->reasm_orig)) { - DLOG("reassemble session cancelled\n"); + DLOG("%s",dlog_msg); ReasmClear(&ctrack->reasm_orig); } send_delayed(ctrack); } } +static void reasm_orig_cancel(t_ctrack *ctrack) +{ + reasm_orig_stop(ctrack, "reassemble session cancelled\n"); +} +static void reasm_orig_fin(t_ctrack *ctrack) +{ + reasm_orig_stop(ctrack, "reassemble session finished\n"); +} static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, struct ip *ip, struct ip6_hdr *ip6, uint8_t proto, struct udphdr *udp, struct tcphdr *tcp, size_t *len_pkt) @@ -655,6 +654,10 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch { DLOG("packet contains HTTP request\n") if (ctrack && !ctrack->l7proto) ctrack->l7proto = HTTP; + + // we do not reassemble http + reasm_orig_cancel(ctrack); + forced_wssize_cutoff(ctrack); fake = params.fake_http; fake_size = params.fake_http_size; @@ -664,7 +667,6 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch if (!bHaveHost) { DLOG("not applying tampering to HTTP without Host:\n") - reasm_orig_fin(ctrack); return verdict; } } @@ -698,7 +700,6 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch // do not reconstruct unexpected large payload (they are feeding garbage ?) if (!reasm_orig_start(ctrack,TLSRecordLen(data_payload),16384,data_payload,len_payload)) { - fprintf(stderr, "reasm start failed ! out of memory.\n"); reasm_orig_cancel(ctrack); return verdict; } @@ -746,7 +747,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch if (params.desync_skip_nosni && !bHaveHost) { DLOG("not applying tampering to TLS ClientHello without hostname in the SNI\n") - reasm_orig_fin(ctrack); + reasm_orig_cancel(ctrack); return verdict; } @@ -755,7 +756,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch bKnownProtocol = true; } - reasm_orig_fin(ctrack); + reasm_orig_cancel(ctrack); rdata_payload=NULL; if (bHaveHost) @@ -1065,6 +1066,21 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, uint32_t fwmark, const ch return verdict; } +// return : true - should continue, false - should stop with verdict +static bool quic_reasm_cancel(t_ctrack *ctrack, const char *reason) +{ + reasm_orig_cancel(ctrack); + if (params.desync_any_proto) + { + DLOG("%s. applying tampering because desync_any_proto is set\n",reason) + return true; + } + else + { + DLOG("%s. not applying tampering because desync_any_proto is not set\n",reason) + return false; + } +} 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) { @@ -1127,7 +1143,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, uint32_t fwmark, const ch if (ctrack && !ctrack->l7proto) ctrack->l7proto = QUIC; - uint8_t clean[9000], *pclean; + uint8_t clean[16384], *pclean; size_t clean_len; bool bIsHello = false; @@ -1145,14 +1161,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, uint32_t fwmark, const ch { if (ctrack && !ReasmIsEmpty(&ctrack->reasm_orig)) { - size_t newlen = ctrack->reasm_orig.size_present + clean_len; - if (newlen>16384) - { - DLOG("QUIC reasm is too long. cancelling.\n"); - reasm_orig_cancel(ctrack); - return verdict; // cannot be first packet - } - if (ReasmResize(&ctrack->reasm_orig, newlen)) + if (ReasmHasSpace(&ctrack->reasm_orig, clean_len)) { reasm_orig_feed(ctrack,IPPROTO_UDP,clean,clean_len); pclean = ctrack->reasm_orig.packet; @@ -1160,7 +1169,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, uint32_t fwmark, const ch } else { - fprintf(stderr, "reasm update failed ! out of memory.\n"); + DLOG("QUIC reasm is too long. cancelling.\n"); reasm_orig_cancel(ctrack); return verdict; // cannot be first packet } @@ -1179,9 +1188,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, uint32_t fwmark, const ch { if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig)) { - if (!reasm_orig_start(ctrack,clean_len,clean_len,clean,clean_len)) + // preallocate max buffer to avoid reallocs that cause memory copy + if (!reasm_orig_start(ctrack,16384,16384,clean,clean_len)) { - fprintf(stderr, "reasm start failed ! out of memory.\n"); reasm_orig_cancel(ctrack); return verdict; } @@ -1220,40 +1229,19 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, uint32_t fwmark, const ch } else { - reasm_orig_cancel(ctrack); - if (params.desync_any_proto) - DLOG("QUIC initial without ClientHello. applying tampering because desync_any_proto is set\n") - else - { - DLOG("not applying tampering to QUIC initial without ClientHello\n") - return verdict; - } + if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict; } } else { // defrag failed - reasm_orig_cancel(ctrack); - if (params.desync_any_proto) - DLOG("QUIC initial without CRYPTO frame. applying tampering because desync_any_proto is set\n") - else - { - DLOG("not applying tampering to QUIC initial without CRYPTO frame\n") - return verdict; - } + if (!quic_reasm_cancel(ctrack,"QUIC initial defrag CRYPTO failed")) return verdict; } } else { // decrypt failed - reasm_orig_cancel(ctrack); - if (params.desync_any_proto) - DLOG("QUIC initial decryption failed. applying tampering because desync_any_proto is set\n") - else - { - DLOG("not applying tampering to QUIC initial that could not be decrypted\n") - return verdict; - } + if (!quic_reasm_cancel(ctrack,"QUIC initial decryption failed")) return verdict; } fake = params.fake_quic; @@ -1301,8 +1289,11 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, uint32_t fwmark, const ch DLOG("not applying tampering to this request\n") if (!bExcluded && *params.hostlist_auto_filename && ctrack) { - if (!ctrack->hostname) ctrack->hostname=strdup(host); - process_retrans_fail(ctrack, IPPROTO_UDP); + if (!ctrack->hostname) + // first request is not retrans + ctrack->hostname=strdup(host); + else + process_retrans_fail(ctrack, IPPROTO_UDP); } return verdict; }