mirror of
https://github.com/bol-van/zapret.git
synced 2025-04-12 01:52:57 +03:00
nfqws: support QUIC multi packet CRYPTO fragmentation
This commit is contained in:
parent
77df43b9cb
commit
66fda2c33d
98
nfq/desync.c
98
nfq/desync.c
@ -1953,29 +1953,80 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
|||||||
return verdict; // cannot be first packet
|
return verdict; // cannot be first packet
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t defrag[16384];
|
uint8_t defrag[16384];
|
||||||
size_t hello_offset, hello_len, defrag_len = sizeof(defrag);
|
size_t hello_offset, hello_len, defrag_len = sizeof(defrag);
|
||||||
if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len))
|
bool bFull;
|
||||||
|
if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len,&bFull))
|
||||||
{
|
{
|
||||||
bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
|
if (bFull)
|
||||||
bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
|
|
||||||
|
|
||||||
DLOG(bIsHello ? bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n" : "packet does not contain TLS ClientHello\n");
|
|
||||||
|
|
||||||
if (ctrack)
|
|
||||||
{
|
{
|
||||||
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
|
bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
|
||||||
|
bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
|
||||||
|
|
||||||
|
DLOG(bIsHello ? bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n" : "packet does not contain TLS ClientHello\n");
|
||||||
|
|
||||||
|
if (ctrack)
|
||||||
{
|
{
|
||||||
// preallocate max buffer to avoid reallocs that cause memory copy
|
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
|
||||||
if (!reasm_orig_start(ctrack,IPPROTO_UDP,16384,16384,clean,clean_len))
|
{
|
||||||
|
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||||
|
if (!reasm_orig_start(ctrack,IPPROTO_UDP,16384,16384,clean,clean_len))
|
||||||
|
{
|
||||||
|
reasm_orig_cancel(ctrack);
|
||||||
|
return verdict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
||||||
|
{
|
||||||
|
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
|
||||||
|
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
|
||||||
|
{
|
||||||
|
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DLOG_ERR("rawpacket_queue failed !\n");
|
||||||
|
reasm_orig_cancel(ctrack);
|
||||||
|
return verdict;
|
||||||
|
}
|
||||||
|
if (bReqFull)
|
||||||
|
{
|
||||||
|
replay_queue(&ctrack->delayed);
|
||||||
|
reasm_orig_fin(ctrack);
|
||||||
|
}
|
||||||
|
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bIsHello)
|
||||||
|
{
|
||||||
|
bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE);
|
||||||
|
if (!bHaveHost && dp->desync_skip_nosni)
|
||||||
{
|
{
|
||||||
reasm_orig_cancel(ctrack);
|
reasm_orig_cancel(ctrack);
|
||||||
|
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n");
|
||||||
return verdict;
|
return verdict;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ReasmIsEmpty(&ctrack->reasm_orig))
|
else
|
||||||
{
|
{
|
||||||
|
if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DLOG("QUIC initial contains CRYPTO without full fragment coverage\n");
|
||||||
|
if (ctrack)
|
||||||
|
{
|
||||||
|
if (ReasmIsEmpty(&ctrack->reasm_orig))
|
||||||
|
{
|
||||||
|
// preallocate max buffer to avoid reallocs that cause memory copy
|
||||||
|
if (!reasm_orig_start(ctrack,IPPROTO_UDP,16384,16384,clean,clean_len))
|
||||||
|
{
|
||||||
|
reasm_orig_cancel(ctrack);
|
||||||
|
return verdict;
|
||||||
|
}
|
||||||
|
}
|
||||||
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
|
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
|
||||||
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
|
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
|
||||||
{
|
{
|
||||||
@ -1987,28 +2038,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
|||||||
reasm_orig_cancel(ctrack);
|
reasm_orig_cancel(ctrack);
|
||||||
return verdict;
|
return verdict;
|
||||||
}
|
}
|
||||||
if (bReqFull)
|
|
||||||
{
|
|
||||||
replay_queue(&ctrack->delayed);
|
|
||||||
reasm_orig_fin(ctrack);
|
|
||||||
}
|
|
||||||
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
|
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
|
||||||
}
|
}
|
||||||
}
|
if (!quic_reasm_cancel(ctrack,"QUIC initial fragmented CRYPTO")) return verdict;
|
||||||
|
|
||||||
if (bIsHello)
|
|
||||||
{
|
|
||||||
bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE);
|
|
||||||
if (!bHaveHost && dp->desync_skip_nosni)
|
|
||||||
{
|
|
||||||
reasm_orig_cancel(ctrack);
|
|
||||||
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n");
|
|
||||||
return verdict;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2027,7 +2059,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
|
|||||||
{
|
{
|
||||||
// received payload without host. it means we are out of the request retransmission phase. stop counter
|
// received payload without host. it means we are out of the request retransmission phase. stop counter
|
||||||
ctrack_stop_retrans_counter(ctrack);
|
ctrack_stop_retrans_counter(ctrack);
|
||||||
|
|
||||||
reasm_orig_cancel(ctrack);
|
reasm_orig_cancel(ctrack);
|
||||||
|
|
||||||
if (IsWireguardHandshakeInitiation(dis->data_payload,dis->len_payload))
|
if (IsWireguardHandshakeInitiation(dis->data_payload,dis->len_payload))
|
||||||
|
@ -844,7 +844,16 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
|
|||||||
return !memcmp(data + pn_offset + pkn_len + cryptlen, atag, 16);
|
return !memcmp(data + pn_offset + pkn_len + cryptlen, atag, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len)
|
struct range64
|
||||||
|
{
|
||||||
|
uint64_t offset,len;
|
||||||
|
};
|
||||||
|
#define MAX_DEFRAG_PIECES 128
|
||||||
|
static int cmp_range64(const void * a, const void * b)
|
||||||
|
{
|
||||||
|
return (((struct range64*)a)->offset < ((struct range64*)b)->offset) ? -1 : (((struct range64*)a)->offset > ((struct range64*)b)->offset) ? 1 : 0;
|
||||||
|
}
|
||||||
|
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull)
|
||||||
{
|
{
|
||||||
// Crypto frame can be split into multiple chunks
|
// Crypto frame can be split into multiple chunks
|
||||||
// chromium randomly splits it and pads with zero/one bytes to force support the standard
|
// chromium randomly splits it and pads with zero/one bytes to force support the standard
|
||||||
@ -853,13 +862,15 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
|||||||
if (*defrag_len<10) return false;
|
if (*defrag_len<10) return false;
|
||||||
uint8_t *defrag_data = defrag+10;
|
uint8_t *defrag_data = defrag+10;
|
||||||
size_t defrag_data_len = *defrag_len-10;
|
size_t defrag_data_len = *defrag_len-10;
|
||||||
|
|
||||||
uint8_t ft;
|
uint8_t ft;
|
||||||
uint64_t offset,sz,szmax=0,zeropos=0,pos=0;
|
uint64_t offset,sz,szmax=0,zeropos=0,pos=0;
|
||||||
bool found=false;
|
bool found=false;
|
||||||
|
struct range64 ranges[MAX_DEFRAG_PIECES];
|
||||||
|
int i,range=0;
|
||||||
|
|
||||||
while(pos<clean_len)
|
while(pos<clean_len)
|
||||||
{
|
{
|
||||||
|
// frame type
|
||||||
ft = clean[pos];
|
ft = clean[pos];
|
||||||
pos++;
|
pos++;
|
||||||
if (ft>1) // 00 - padding, 01 - ping
|
if (ft>1) // 00 - padding, 01 - ping
|
||||||
@ -867,6 +878,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
|||||||
if (ft!=6) return false; // dont want to know all possible frame type formats
|
if (ft!=6) return false; // dont want to know all possible frame type formats
|
||||||
|
|
||||||
if (pos>=clean_len) return false;
|
if (pos>=clean_len) return false;
|
||||||
|
if (range>=MAX_DEFRAG_PIECES) return false;
|
||||||
|
|
||||||
if ((pos+tvb_get_size(clean[pos])>=clean_len)) return false;
|
if ((pos+tvb_get_size(clean[pos])>=clean_len)) return false;
|
||||||
pos += tvb_get_varint(clean+pos, &offset);
|
pos += tvb_get_varint(clean+pos, &offset);
|
||||||
@ -875,7 +887,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
|||||||
pos += tvb_get_varint(clean+pos, &sz);
|
pos += tvb_get_varint(clean+pos, &sz);
|
||||||
if ((pos+sz)>clean_len) return false;
|
if ((pos+sz)>clean_len) return false;
|
||||||
|
|
||||||
if ((offset+sz)>defrag_data_len) return false;
|
if ((offset+sz)>defrag_data_len) return false; // defrag buf overflow
|
||||||
if (zeropos < offset)
|
if (zeropos < offset)
|
||||||
// make sure no uninitialized gaps exist in case of not full fragment coverage
|
// make sure no uninitialized gaps exist in case of not full fragment coverage
|
||||||
memset(defrag_data+zeropos,0,offset-zeropos);
|
memset(defrag_data+zeropos,0,offset-zeropos);
|
||||||
@ -886,6 +898,10 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
|||||||
|
|
||||||
found=true;
|
found=true;
|
||||||
pos+=sz;
|
pos+=sz;
|
||||||
|
|
||||||
|
ranges[range].offset = offset;
|
||||||
|
ranges[range].len = sz;
|
||||||
|
range++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (found)
|
if (found)
|
||||||
@ -897,6 +913,23 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
|
|||||||
phton64(defrag+2,szmax);
|
phton64(defrag+2,szmax);
|
||||||
defrag[2] |= 0xC0; // 64 bit value
|
defrag[2] |= 0xC0; // 64 bit value
|
||||||
*defrag_len = (size_t)(szmax+10);
|
*defrag_len = (size_t)(szmax+10);
|
||||||
|
|
||||||
|
qsort(ranges, range, sizeof(*ranges), cmp_range64);
|
||||||
|
|
||||||
|
for(i=0 ; i<range ; i++)
|
||||||
|
printf("RANGE %zu len %zu\n",ranges[i].offset,ranges[i].len);
|
||||||
|
|
||||||
|
for(i=0,offset=0,*bFull=true ; i<range ; i++)
|
||||||
|
{
|
||||||
|
if (ranges[i].offset!=offset)
|
||||||
|
{
|
||||||
|
*bFull = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
offset += ranges[i].len;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("bFull=%d\n",*bFull);
|
||||||
}
|
}
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
@ -87,5 +87,6 @@ uint8_t QUICDraftVersion(uint32_t version);
|
|||||||
bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid);
|
bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid);
|
||||||
|
|
||||||
bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len);
|
bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len);
|
||||||
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len);
|
// returns true if crypto frames were found . bFull = true if crypto frame fragments have full coverage
|
||||||
|
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull);
|
||||||
//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);
|
//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user