From 63767929a0010de9f92ecdf33f8e661ac90ec825 Mon Sep 17 00:00:00 2001 From: bol-van Date: Mon, 22 Dec 2025 17:42:53 +0300 Subject: [PATCH] nfqws2: dtls, reevaulate profile on l7/host discovery in any direction --- docs/changes.txt | 5 + nfq2/desync.c | 333 +++++++++++++++++++++++------------------------ nfq2/nfqws.c | 1 + nfq2/protocol.c | 29 ++++- nfq2/protocol.h | 7 + 5 files changed, 204 insertions(+), 171 deletions(-) diff --git a/docs/changes.txt b/docs/changes.txt index 898a707..2b8edc9 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -125,3 +125,8 @@ v0.7.5 * ipset: remove get_reestr_hostlist.sh and get_reestr_resolve.sh because zapret-info does not and will probably not ever update * nfqws2: fix "reasm cancelled" if no incoming traffic redirected * blockcheck2: MULTIDISORDER=multidisorder_legacy + +v0.7.6 + +* nfqws2: reevaluate profile on l7/host discovery in any direction +* nfqws2: dtls protocol detection diff --git a/nfq2/desync.c b/nfq2/desync.c index 7457378..372c81b 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -1144,6 +1144,7 @@ static uint8_t dpi_desync_tcp_packet_play( else bCheckDone = bCheckResult = bCheckExcluded = false; + bool bHaveHost = false, bHostIsIp = false; if (bReverse) { // protocol detection @@ -1215,7 +1216,6 @@ static uint8_t dpi_desync_tcp_packet_play( struct blob_collection_head *fake; uint8_t *p, *phost = NULL; int i; - bool bHaveHost = false, bHostIsIp = false; if (replay_piece_count) { @@ -1313,91 +1313,90 @@ static uint8_t dpi_desync_tcp_packet_play( protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload); } - if (bHaveHost) - { - bHostIsIp = strip_host_to_ip(host); - DLOG("hostname: %s\n", host); - } + } - bool bDiscoveredL7; + if (bHaveHost) + { + bHostIsIp = strip_host_to_ip(host); + DLOG("hostname: %s\n", host); + } + + bool bDiscoveredL7; + if (ctrack_replay) + { + if ((bDiscoveredL7 = !ctrack_replay->l7proto_discovered && ctrack_replay->l7proto != L7_UNKNOWN)) + ctrack_replay->l7proto_discovered = true; + } + else + bDiscoveredL7 = l7proto != L7_UNKNOWN; + if (bDiscoveredL7) DLOG("discovered l7 protocol\n"); + + bool bDiscoveredHostname = bHaveHost && !(ctrack_replay && ctrack_replay->hostname_discovered); + if (bDiscoveredHostname) + { + DLOG("discovered hostname\n"); if (ctrack_replay) { - if ((bDiscoveredL7 = !ctrack_replay->l7proto_discovered && ctrack_replay->l7proto != L7_UNKNOWN)) - ctrack_replay->l7proto_discovered = true; - } - else - bDiscoveredL7 = l7proto != L7_UNKNOWN; - if (bDiscoveredL7) DLOG("discovered l7 protocol\n"); - - bool bDiscoveredHostname = bHaveHost && !(ctrack_replay && ctrack_replay->hostname_discovered); - if (bDiscoveredHostname) - { - DLOG("discovered hostname\n"); - if (ctrack_replay) + free(ctrack_replay->hostname); + ctrack_replay->hostname = strdup(host); + ctrack_replay->hostname_is_ip = bHostIsIp; + if (!ctrack_replay->hostname) { - free(ctrack_replay->hostname); - ctrack_replay->hostname = strdup(host); - ctrack_replay->hostname_is_ip = bHostIsIp; - if (!ctrack_replay->hostname) - { - DLOG_ERR("hostname dup : out of memory"); - goto pass_reasm_cancel; - } - ctrack_replay->hostname_discovered = true; - if (!ipcache_put_hostname(sdip4, sdip6, host, bHostIsIp)) - goto pass_reasm_cancel; - + DLOG_ERR("hostname dup : out of memory"); + goto pass_reasm_cancel; } - } + ctrack_replay->hostname_discovered = true; + if (!ipcache_put_hostname(sdip4, sdip6, host, bHostIsIp)) + goto pass_reasm_cancel; + } + } - if (bDiscoveredL7 || bDiscoveredHostname) + if (bDiscoveredL7 || bDiscoveredHostname) + { + struct desync_profile *dp_prev = dp; + // search for desync profile again. it may have changed. + dp = dp_find(¶ms.desync_profiles, IPPROTO_TCP, sdip4, sdip6, sdport, + ctrack_replay ? ctrack_replay->hostname : bHaveHost ? host : NULL, + ctrack_replay ? ctrack_replay->hostname_is_ip : bHostIsIp, + l7proto, ssid, + &bCheckDone, &bCheckResult, &bCheckExcluded); + if (ctrack_replay) { - struct desync_profile *dp_prev = dp; - - // search for desync profile again. it may have changed. - dp = dp_find(¶ms.desync_profiles, IPPROTO_TCP, sdip4, sdip6, sdport, - ctrack_replay ? ctrack_replay->hostname : bHaveHost ? host : NULL, - ctrack_replay ? ctrack_replay->hostname_is_ip : bHostIsIp, - l7proto, ssid, - &bCheckDone, &bCheckResult, &bCheckExcluded); + ctrack_replay->dp = dp; + ctrack_replay->dp_search_complete = true; + ctrack_replay->bCheckDone = bCheckDone; + ctrack_replay->bCheckResult = bCheckResult; + ctrack_replay->bCheckExcluded = bCheckExcluded; + } + if (!dp) goto pass_reasm_cancel; + if (dp != dp_prev) + { + dp_changed(ctrack_replay); + DLOG("desync profile changed by revealed l7 protocol or hostname !\n"); + } + } + if (bHaveHost && !PROFILE_HOSTLISTS_EMPTY(dp)) + { + if (!bCheckDone) + { + bCheckResult = HostlistCheck(dp, host, bHostIsIp, &bCheckExcluded, false); + bCheckDone = true; if (ctrack_replay) { - ctrack_replay->dp = dp; - ctrack_replay->dp_search_complete = true; ctrack_replay->bCheckDone = bCheckDone; ctrack_replay->bCheckResult = bCheckResult; ctrack_replay->bCheckExcluded = bCheckExcluded; } - if (!dp) goto pass_reasm_cancel; - if (dp != dp_prev) - { - dp_changed(ctrack_replay); - DLOG("desync profile changed by revealed l7 protocol or hostname !\n"); - } } - if (bHaveHost && !PROFILE_HOSTLISTS_EMPTY(dp)) + if (bCheckResult) + ctrack_stop_retrans_counter(ctrack_replay); + else { - if (!bCheckDone) + if (ctrack_replay) { - bCheckResult = HostlistCheck(dp, host, bHostIsIp, &bCheckExcluded, false); - bCheckDone = true; - if (ctrack_replay) - { - ctrack_replay->bCheckDone = bCheckDone; - ctrack_replay->bCheckResult = bCheckResult; - ctrack_replay->bCheckExcluded = bCheckExcluded; - } - } - if (bCheckResult) - ctrack_stop_retrans_counter(ctrack_replay); - else - { - if (ctrack_replay) - { - ctrack_replay->hostname_ah_check = dp->hostlist_auto && !bCheckExcluded; - if (!ctrack_replay->hostname_ah_check) - ctrack_stop_retrans_counter(ctrack_replay); - } + ctrack_replay->hostname_ah_check = dp->hostlist_auto && !bCheckExcluded; + if (!ctrack_replay->hostname_ah_check) + ctrack_stop_retrans_counter(ctrack_replay); } } } @@ -1430,6 +1429,25 @@ static void quic_reasm_cancel(t_ctrack *ctrack, const char *reason) DLOG("%s\n", reason); } +static void udp_standard_protocol_probe(const uint8_t *data_payload, size_t len_payload, t_ctrack *ctrack, t_l7proto *l7proto, t_l7payload *l7payload) +{ + t_protocol_probe testers[] = { + {L7P_DISCORD_IP_DISCOVERY,L7_DISCORD,IsDiscordIpDiscoveryRequest,false}, + {L7P_STUN,L7_STUN,IsStunMessage,false}, + {L7P_DNS_QUERY,L7_DNS,IsDNSQuery,false}, + {L7P_DNS_RESPONSE,L7_DNS,IsDNSResponse,false}, + {L7P_DHT,L7_DHT,IsDht,false}, + {L7P_DTLS_CLIENT_HELLO,L7_DTLS,IsDTLSClientHello,false}, + {L7P_DTLS_SERVER_HELLO,L7_DTLS,IsDTLSServerHello,false}, + {L7P_WIREGUARD_INITIATION,L7_WIREGUARD,IsWireguardHandshakeInitiation,false}, + {L7P_WIREGUARD_RESPONSE,L7_WIREGUARD,IsWireguardHandshakeResponse,false}, + {L7P_WIREGUARD_COOKIE,L7_WIREGUARD,IsWireguardHandshakeCookie,false}, + {L7P_WIREGUARD_KEEPALIVE,L7_WIREGUARD,IsWireguardKeepalive,false}, + {L7P_WIREGUARD_DATA,L7_WIREGUARD,IsWireguardData,true}}; + + protocol_probe(testers, sizeof(testers) / sizeof(*testers), data_payload, len_payload, ctrack, l7proto, l7payload); +} + static uint8_t dpi_desync_udp_packet_play( unsigned int replay_piece, unsigned int replay_piece_count, size_t reasm_offset, @@ -1589,24 +1607,14 @@ static uint8_t dpi_desync_udp_packet_play( if (dis->len_payload) { + bool bHaveHost = false, bHostIsIp = false; if (bReverse) { - t_protocol_probe testers[] = { - {L7P_DNS_RESPONSE,L7_DNS,IsDNSResponse,false}, - {L7P_DHT,L7_DHT,IsDht,false}, - {L7P_STUN,L7_STUN,IsStunMessage,false}, - {L7P_WIREGUARD_INITIATION,L7_WIREGUARD,IsWireguardHandshakeInitiation,false}, - {L7P_WIREGUARD_RESPONSE,L7_WIREGUARD,IsWireguardHandshakeResponse,false}, - {L7P_WIREGUARD_COOKIE,L7_WIREGUARD,IsWireguardHandshakeCookie,false}, - {L7P_WIREGUARD_KEEPALIVE,L7_WIREGUARD,IsWireguardKeepalive,false}, - {L7P_WIREGUARD_DATA,L7_WIREGUARD,IsWireguardData,true} - }; - protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload); + udp_standard_protocol_probe(dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload); } else { struct blob_collection_head *fake; - bool bHaveHost = false, bHostIsIp = false; if (IsQUICInitial(dis->data_payload, dis->len_payload)) { DLOG("packet contains QUIC initial\n"); @@ -1739,114 +1747,101 @@ static uint8_t dpi_desync_udp_packet_play( } else // not QUIC initial { - // received payload without host. it means we are out of the request retransmission phase. stop counter - ctrack_stop_retrans_counter(ctrack); - + // not quic initial - stop reasm reasm_client_cancel(ctrack); - t_protocol_probe testers[] = { - {L7P_DISCORD_IP_DISCOVERY,L7_DISCORD,IsDiscordIpDiscoveryRequest,false}, - {L7P_STUN,L7_STUN,IsStunMessage,false}, - {L7P_DNS_QUERY,L7_DNS,IsDNSQuery,false}, - {L7P_DHT,L7_DHT,IsDht,false}, - {L7P_WIREGUARD_INITIATION,L7_WIREGUARD,IsWireguardHandshakeInitiation,false}, - {L7P_WIREGUARD_RESPONSE,L7_WIREGUARD,IsWireguardHandshakeResponse,false}, - {L7P_WIREGUARD_COOKIE,L7_WIREGUARD,IsWireguardHandshakeCookie,false}, - {L7P_WIREGUARD_KEEPALIVE,L7_WIREGUARD,IsWireguardKeepalive,false}, - {L7P_WIREGUARD_DATA,L7_WIREGUARD,IsWireguardData,true} - }; - protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload); + udp_standard_protocol_probe(dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload); } + } - if (bHaveHost) - { - bHostIsIp = strip_host_to_ip(host); - DLOG("hostname: %s\n", host); - } + if (bHaveHost) + { + bHostIsIp = strip_host_to_ip(host); + DLOG("hostname: %s\n", host); + } - bool bDiscoveredL7; + bool bDiscoveredL7; + if (ctrack_replay) + { + if ((bDiscoveredL7 = !ctrack_replay->l7proto_discovered && l7proto != L7_UNKNOWN)) + ctrack_replay->l7proto_discovered = true; + } + else + bDiscoveredL7 = l7proto != L7_UNKNOWN; + if (bDiscoveredL7) DLOG("discovered l7 protocol\n"); + + bool bDiscoveredHostname = bHaveHost && !(ctrack_replay && ctrack_replay->hostname_discovered); + if (bDiscoveredHostname) + { + DLOG("discovered hostname\n"); if (ctrack_replay) { - if ((bDiscoveredL7 = !ctrack_replay->l7proto_discovered && l7proto != L7_UNKNOWN)) - ctrack_replay->l7proto_discovered = true; - } - else - bDiscoveredL7 = l7proto != L7_UNKNOWN; - if (bDiscoveredL7) DLOG("discovered l7 protocol\n"); - - bool bDiscoveredHostname = bHaveHost && !(ctrack_replay && ctrack_replay->hostname_discovered); - if (bDiscoveredHostname) - { - DLOG("discovered hostname\n"); - if (ctrack_replay) + ctrack_replay->hostname_discovered = true; + free(ctrack_replay->hostname); + ctrack_replay->hostname = strdup(host); + ctrack_replay->hostname_is_ip = bHostIsIp; + if (!ctrack_replay->hostname) { - ctrack_replay->hostname_discovered = true; - free(ctrack_replay->hostname); - ctrack_replay->hostname = strdup(host); - ctrack_replay->hostname_is_ip = bHostIsIp; - if (!ctrack_replay->hostname) - { - DLOG_ERR("hostname dup : out of memory"); - goto pass; - } - if (!ipcache_put_hostname(sdip4, sdip6, host, bHostIsIp)) - goto pass; + DLOG_ERR("hostname dup : out of memory"); + goto pass; } + if (!ipcache_put_hostname(sdip4, sdip6, host, bHostIsIp)) + goto pass; } + } - if (bDiscoveredL7 || bDiscoveredHostname) + if (bDiscoveredL7 || bDiscoveredHostname) + { + struct desync_profile *dp_prev = dp; + + // search for desync profile again. it may have changed. + dp = dp_find(¶ms.desync_profiles, IPPROTO_UDP, sdip4, sdip6, sdport, + ctrack_replay ? ctrack_replay->hostname : bHaveHost ? host : NULL, + ctrack_replay ? ctrack_replay->hostname_is_ip : bHostIsIp, + l7proto, ssid, + &bCheckDone, &bCheckResult, &bCheckExcluded); + if (ctrack_replay) { - struct desync_profile *dp_prev = dp; + ctrack_replay->dp = dp; + ctrack_replay->dp_search_complete = true; + ctrack_replay->bCheckDone = bCheckDone; + ctrack_replay->bCheckResult = bCheckResult; + ctrack_replay->bCheckExcluded = bCheckExcluded; + } + if (!dp) + goto pass_reasm_cancel; + if (dp != dp_prev) + { + dp_changed(ctrack_replay); + DLOG("desync profile changed by revealed l7 protocol or hostname !\n"); + } + } + else if (ctrack_replay) + { + bCheckDone = ctrack_replay->bCheckDone; + bCheckResult = ctrack_replay->bCheckResult; + bCheckExcluded = ctrack_replay->bCheckExcluded; + } - // search for desync profile again. it may have changed. - dp = dp_find(¶ms.desync_profiles, IPPROTO_UDP, sdip4, sdip6, sdport, - ctrack_replay ? ctrack_replay->hostname : bHaveHost ? host : NULL, - ctrack_replay ? ctrack_replay->hostname_is_ip : bHostIsIp, - l7proto, ssid, - &bCheckDone, &bCheckResult, &bCheckExcluded); + if (bHaveHost && !PROFILE_HOSTLISTS_EMPTY(dp)) + { + if (!bCheckDone) + { + bCheckResult = HostlistCheck(dp, host, bHostIsIp, &bCheckExcluded, false); + bCheckDone = true; if (ctrack_replay) { - ctrack_replay->dp = dp; - ctrack_replay->dp_search_complete = true; ctrack_replay->bCheckDone = bCheckDone; ctrack_replay->bCheckResult = bCheckResult; ctrack_replay->bCheckExcluded = bCheckExcluded; } - if (!dp) - goto pass_reasm_cancel; - if (dp != dp_prev) - { - dp_changed(ctrack_replay); - DLOG("desync profile changed by revealed l7 protocol or hostname !\n"); - } } - else if (ctrack_replay) + if (bCheckResult) + ctrack_stop_retrans_counter(ctrack_replay); + else { - bCheckDone = ctrack_replay->bCheckDone; - bCheckResult = ctrack_replay->bCheckResult; - bCheckExcluded = ctrack_replay->bCheckExcluded; - } - - if (bHaveHost && !PROFILE_HOSTLISTS_EMPTY(dp)) - { - if (!bCheckDone) - { - bCheckResult = HostlistCheck(dp, host, bHostIsIp, &bCheckExcluded, false); - bCheckDone = true; - if (ctrack_replay) - { - ctrack_replay->bCheckDone = bCheckDone; - ctrack_replay->bCheckResult = bCheckResult; - ctrack_replay->bCheckExcluded = bCheckExcluded; - } - } - if (bCheckResult) - ctrack_stop_retrans_counter(ctrack_replay); - else - { - if (ctrack_replay) - ctrack_replay->hostname_ah_check = dp->hostlist_auto && !bCheckExcluded; - } + if (ctrack_replay) + ctrack_replay->hostname_ah_check = dp->hostlist_auto && !bCheckExcluded; } } diff --git a/nfq2/nfqws.c b/nfq2/nfqws.c index 782f702..a36ddb3 100644 --- a/nfq2/nfqws.c +++ b/nfq2/nfqws.c @@ -726,6 +726,7 @@ ex: static void exit_clean(int code) { cleanup_params(¶ms); + exit(code); } diff --git a/nfq2/protocol.c b/nfq2/protocol.c index 5cd9f34..20e2418 100644 --- a/nfq2/protocol.c +++ b/nfq2/protocol.c @@ -29,7 +29,9 @@ static bool FindNLD(const uint8_t *dom, size_t dlen, int level, const uint8_t ** return true; } -static const char *l7proto_name[] = {"all","unknown","known","http","tls","quic","wireguard","dht","discord","stun","xmpp","dns","mtproto"}; +static const char *l7proto_name[] = { +"all","unknown","known","http","tls","dtls","quic","wireguard","dht","discord","stun","xmpp","dns","mtproto" +}; const char *l7proto_str(t_l7proto l7) { if (l7>=L7_LAST) return NULL; @@ -46,7 +48,11 @@ bool l7_proto_match(t_l7proto l7proto, uint64_t filter_l7) } static const char *l7payload_name[] = { - "all","unknown","empty","known","http_req","http_reply","tls_client_hello","tls_server_hello","quic_initial", + "all","unknown","empty","known", + "http_req","http_reply", + "tls_client_hello","tls_server_hello", + "dtls_client_hello","dtls_server_hello", + "quic_initial", "wireguard_initiation","wireguard_response","wireguard_cookie","wireguard_keepalive","wireguard_data", "dht","discord_ip_discovery","stun", "xmpp_stream", "xmpp_starttls", "xmpp_proceed", "xmpp_features", @@ -1445,3 +1451,22 @@ bool IsMTProto(const uint8_t *data, size_t len) } return false; } + +bool IsDTLS(const uint8_t *data, size_t len) +{ + return ((len > 13) && + (data[0]>=0x14 && data[0]<=0x17) && /* Handshake, change-cipher-spec, Application-Data, Alert */ + ((data[1] == 0xfe && data[2] == 0xff) || /* Versions */ + (data[1] == 0xfe && data[2] == 0xfd) || + (data[1] == 0xfe && data[2] == 0xfc) || + (data[1] == 0x01 && data[2] == 0x00)) && + (pntoh16(data+11)+13)<=len); +} +bool IsDTLSClientHello(const uint8_t *data, size_t len) +{ + return IsDTLS(data,len) && data[0]==0x16 && data[13]==1; +} +bool IsDTLSServerHello(const uint8_t *data, size_t len) +{ + return IsDTLS(data,len) && data[0]==0x16 && data[13]==2; +} diff --git a/nfq2/protocol.h b/nfq2/protocol.h index 07bf82f..07869ab 100644 --- a/nfq2/protocol.h +++ b/nfq2/protocol.h @@ -11,6 +11,7 @@ typedef enum { L7_KNOWN, L7_HTTP, L7_TLS, + L7_DTLS, L7_QUIC, L7_WIREGUARD, L7_DHT, @@ -34,6 +35,8 @@ typedef enum { L7P_HTTP_REPLY, L7P_TLS_CLIENT_HELLO, L7P_TLS_SERVER_HELLO, + L7P_DTLS_CLIENT_HELLO, + L7P_DTLS_SERVER_HELLO, L7P_QUIC_INITIAL, L7P_WIREGUARD_INITIATION, L7P_WIREGUARD_RESPONSE, @@ -155,6 +158,10 @@ bool IsDht(const uint8_t *data, size_t len); bool IsDiscordIpDiscoveryRequest(const uint8_t *data, size_t len); bool IsStunMessage(const uint8_t *data, size_t len); bool IsMTProto(const uint8_t *data, size_t len); +bool IsDTLS(const uint8_t *data, size_t len); +bool IsDTLSClientHello(const uint8_t *data, size_t len); +bool IsDTLSServerHello(const uint8_t *data, size_t len); + #define QUIC_MAX_CID_LENGTH 20 typedef struct quic_cid {