diff --git a/docs/changes.txt b/docs/changes.txt index dfd9562..6532457 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -176,3 +176,4 @@ v0.8.1 * zapret-lib, zapret-antidpi: use numeric indexes in http dissects * nfqws2: move ctx from lightuserdata to userdata. prevents crashes on specific ARM cpus * nfqws2: alternative representation of payload filter in execution_plan item +* nfqws2: --payload-disable diff --git a/docs/manual.en.md b/docs/manual.en.md index e49fa51..89fffe9 100644 --- a/docs/manual.en.md +++ b/docs/manual.en.md @@ -612,10 +612,11 @@ General parameters for all versions - nfqws2, dvtws2, winws2. --pidfile= ; write PID to a file --ctrack-timeouts=S:E:F[:U] ; conntrack timeouts for tcp stages (SYN, ESTABLISHED, FIN) and for udp --ctrack-disable=[0|1] ; 1 disables conntrack + --payload-disable=[type[,type]] ; do not discover these payload types. for available payload types see '--payload'. disable all if no argument. --server=[0|1] ; server mode. modifies various aspects of direction selection and source/destination ip/port for handling listeners --ipcache-lifetime= ; IP cache entry lifetime in seconds. 0 - unlimited. --ipcache-hostname=[0|1] ; 1 or no argument enables hostname caching for use in zero-phase strategies - --reasm-disable=[proto[,proto]] ; disable fragment reassembly for a list of payloads: tls_client_hello quic_initial. without arguments - disable reasm for everything. + --reasm-disable=[type[,type]] ; disable fragment reassembly for a list of payloads: tls_client_hello quic_initial. without arguments - disable reasm for everything. DESYNC ENGINE INIT: --writeable[=] ; create a directory for Lua with write permissions and store its path in the "WRITEABLE" env variable (only one directory) diff --git a/docs/manual.md b/docs/manual.md index 34bacd7..4d82ea2 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -668,10 +668,11 @@ nfqws2 использует стандартный парсер getopt_long_only --pidfile= ; запись PID в файл --ctrack-timeouts=S:E:F[:U] ; таймауты conntrack для стадий tcp SYN, ESTABLISHED, FIN и для udp --ctrack-disable=[0|1] ; 1 отключает conntrack + --payload-disable=[type[,type]] ; отключить распознавание указанных типов пейлоадов. без аргумента - отключить все. --server=[0|1] ; серверный режим. для обслуживания listener-ов меняются многие аспекты выбора направления и ip/port источника/приемника --ipcache-lifetime= ; время жизни записей кэша IP в секундах. 0 - без ограничений. --ipcache-hostname=[0|1] ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы - --reasm-disable=[proto[,proto]] ; отключить сборку фрагментов для списка пейлоадов : tls_client_hello quic_initial . без аргумента - отключить reasm для всего. + --reasm-disable=[type[,type]] ; отключить сборку фрагментов для списка пейлоадов : tls_client_hello quic_initial . без аргумента - отключить reasm для всего. DESYNC ENGINE INIT: --writeable[=] ; создать директорию для Lua с разрешением записи и поместить путь к ней в переменную env "WRITEABLE" (только одна директория) diff --git a/nfq2/desync.c b/nfq2/desync.c index ef83357..cb7f6ad 100644 --- a/nfq2/desync.c +++ b/nfq2/desync.c @@ -30,7 +30,7 @@ static void protocol_probe(t_protocol_probe *probe, int probe_count, const uint8 { for (int i = 0; i < probe_count; i++) { - if ((!probe[i].l7match || *l7proto==probe[i].l7) && probe[i].check(data_payload, len_payload)) + if (!l7_payload_match(probe[i].l7p, params.payload_disable) && (!probe[i].l7match || *l7proto==probe[i].l7) && probe[i].check(data_payload, len_payload)) { *l7payload = probe[i].l7p; if (*l7proto == L7_UNKNOWN) @@ -1280,88 +1280,78 @@ static uint8_t dpi_desync_tcp_packet_play( process_retrans_fail(ctrack, dis, (struct sockaddr*)&src, ifin); - if (IsHttp(rdata_payload, rlen_payload)) + t_protocol_probe testers[] = { + {L7P_TLS_CLIENT_HELLO,L7_TLS,IsTLSClientHelloPartial}, + {L7P_HTTP_REQ,L7_HTTP,IsHttp,false}, + {L7P_XMPP_STREAM,L7_XMPP,IsXMPPStream,false}, + {L7P_XMPP_STARTTLS,L7_XMPP,IsXMPPStartTLS,false} + }; + protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload); + + if (l7payload==L7P_UNKNOWN) { - DLOG("packet contains HTTP request\n"); - l7payload = L7P_HTTP_REQ; - if (l7proto == L7_UNKNOWN) + // this is special type. detection requires AES and can be successful only for the first data packet. no reason to AES every packet + if (ctrack && (ctrack->pos.client.seq_last - ctrack->pos.client.seq0)==1) { - l7proto = L7_HTTP; - if (ctrack && ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto; - } - - // we do not reassemble http - reasm_client_cancel(ctrack); - - bHaveHost = HttpExtractHost(rdata_payload, rlen_payload, host, sizeof(host)); - if (!bHaveHost) - { - DLOG("not applying tampering to HTTP without Host:\n"); - goto pass; + t_protocol_probe testers[] = { + {L7P_MTPROTO_INITIAL,L7_MTPROTO,IsMTProto} + }; + protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload); } } - else if (IsTLSClientHello(rdata_payload, rlen_payload, TLS_PARTIALS_ENABLE)) + + switch(l7payload) { - bool bReqFull = IsTLSRecordFull(rdata_payload, rlen_payload); - DLOG(bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n"); - l7payload = L7P_TLS_CLIENT_HELLO; - if (l7proto == L7_UNKNOWN) - { - l7proto = L7_TLS; - if (ctrack && ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto; - } + case L7P_HTTP_REQ: + // we do not reassemble http + reasm_client_cancel(ctrack); - if (bReqFull) TLSDebug(rdata_payload, rlen_payload); - - bHaveHost = TLSHelloExtractHost(rdata_payload, rlen_payload, host, sizeof(host), TLS_PARTIALS_ENABLE); - if (ctrack && !(params.reasm_payload_disable && l7_payload_match(l7payload, params.reasm_payload_disable))) - { - // do not reasm retransmissions - if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_client) && !is_retransmission(&ctrack->pos.client)) + bHaveHost = HttpExtractHost(rdata_payload, rlen_payload, host, sizeof(host)); + if (!bHaveHost) { - // do not reconstruct unexpected large payload (they are feeding garbage ?) - if (!reasm_client_start(ctrack, IPPROTO_TCP, TLSRecordLen(dis->data_payload), TCP_MAX_REASM, dis->data_payload, dis->len_payload)) - goto pass_reasm_cancel; + DLOG("not applying tampering to HTTP without Host:\n"); + goto pass; } - - if (!ReasmIsEmpty(&ctrack->reasm_client)) + break; + case L7P_TLS_CLIENT_HELLO: { - if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout)) + bool bReqFull = IsTLSRecordFull(rdata_payload, rlen_payload); + DLOG(bReqFull ? "TLS ClientHello is FULL\n" : "TLS ClientHello is PARTIAL\n"); + + if (bReqFull) TLSDebug(rdata_payload, rlen_payload); + + bHaveHost = TLSHelloExtractHost(rdata_payload, rlen_payload, host, sizeof(host), true); + if (ctrack && !l7_payload_match(l7payload, params.reasm_payload_disable)) + { + // do not reasm retransmissions + if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_client) && !is_retransmission(&ctrack->pos.client)) { - DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed)); + // do not reconstruct unexpected large payload (they are feeding garbage ?) + if (!reasm_client_start(ctrack, IPPROTO_TCP, TLSRecordLen(dis->data_payload), TCP_MAX_REASM, dis->data_payload, dis->len_payload)) + goto pass_reasm_cancel; } - else + + if (!ReasmIsEmpty(&ctrack->reasm_client)) { - DLOG_ERR("rawpacket_queue failed !\n"); - goto pass_reasm_cancel; + if (rawpacket_queue_csum_fix(&ctrack->delayed, dis, &ctrack->pos, &dst, fwmark, desync_fwmark, ifin, ifout)) + { + DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed)); + } + else + { + DLOG_ERR("rawpacket_queue failed !\n"); + goto pass_reasm_cancel; + } + if (ReasmIsFull(&ctrack->reasm_client)) + { + replay_queue(&ctrack->delayed); + reasm_client_fin(ctrack); + } + return VERDICT_DROP; } - if (ReasmIsFull(&ctrack->reasm_client)) - { - replay_queue(&ctrack->delayed); - reasm_client_fin(ctrack); - } - return VERDICT_DROP; } - } - } - else if (ctrack && (ctrack->pos.client.seq_last - ctrack->pos.client.seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload)) - { - DLOG("packet contains telegram mtproto2 initial\n"); - // mtproto detection requires aes. react only on the first tcp data packet. do not detect if ctrack unavailable. - l7payload = L7P_MTPROTO_INITIAL; - if (l7proto == L7_UNKNOWN) - { - l7proto = L7_MTPROTO; - if (ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto; - } - } - else - { - t_protocol_probe testers[] = { - {L7P_XMPP_STREAM,L7_XMPP,IsXMPPStream,false}, - {L7P_XMPP_STARTTLS,L7_XMPP,IsXMPPStartTLS,false} - }; - protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload); + break; + } } } @@ -1764,7 +1754,7 @@ static uint8_t dpi_desync_udp_packet_play( } if (pclean) { - bool reasm_disable = params.reasm_payload_disable && l7_payload_match(l7payload, params.reasm_payload_disable); + bool reasm_disable = l7_payload_match(l7payload, params.reasm_payload_disable); if (ctrack && !reasm_disable && !ReasmIsEmpty(&ctrack->reasm_client)) { if (ReasmHasSpace(&ctrack->reasm_client, clean_len)) @@ -1826,7 +1816,7 @@ static uint8_t dpi_desync_udp_packet_play( { data_decrypt = defrag + hello_offset; len_decrypt = hello_len; - bHaveHost = TLSHelloExtractHostFromHandshake(data_decrypt, len_decrypt, host, sizeof(host), TLS_PARTIALS_ENABLE); + bHaveHost = TLSHelloExtractHostFromHandshake(data_decrypt, len_decrypt, host, sizeof(host), true); } else { diff --git a/nfq2/nfqws.c b/nfq2/nfqws.c index 2dee899..cdcc041 100644 --- a/nfq2/nfqws.c +++ b/nfq2/nfqws.c @@ -1378,10 +1378,11 @@ static void exithelp(void) #endif " --ctrack-timeouts=S:E:F[:U]\t\t\t\t; internal conntrack timeouts for TCP SYN, ESTABLISHED, FIN stages, UDP timeout. default %u:%u:%u:%u\n" " --ctrack-disable=[0|1]\t\t\t\t\t; 1 or no argument disables conntrack\n" + " --payload-disable=[type[,type]]\t\t\t; do not discover these payload types. for available payload types see '--payload'. disable all if no argument.\n" " --server=[0|1]\t\t\t\t\t\t; change multiple aspects of src/dst ip/port handling for incoming connections\n" " --ipcache-lifetime=\t\t\t\t; time in seconds to keep cached hop count and domain name (default %u). 0 = no expiration\n" " --ipcache-hostname=[0|1]\t\t\t\t; 1 or no argument enables ip->hostname caching\n" - " --reasm-disable=[proto[,proto]]\t\t\t; disable reasm for these L7 payloads : tls_client_hello quic_initial . if no argument - disable all reasm.\n" + " --reasm-disable=[type[,type]]\t\t\t\t; disable reasm for these L7 payloads : tls_client_hello quic_initial . if no argument - disable all reasm.\n" #ifdef __CYGWIN__ "\nWINDIVERT FILTER:\n" " --wf-iface=[.]\t\t\t\t; numeric network interface and subinterface indexes\n" @@ -1523,6 +1524,7 @@ enum opt_indices { #endif IDX_CTRACK_TIMEOUTS, IDX_CTRACK_DISABLE, + IDX_PAYLOAD_DISABLE, IDX_SERVER, IDX_IPCACHE_LIFETIME, IDX_IPCACHE_HOSTNAME, @@ -1616,6 +1618,7 @@ static const struct option long_options[] = { #endif [IDX_CTRACK_TIMEOUTS] = {"ctrack-timeouts", required_argument, 0, 0}, [IDX_CTRACK_DISABLE] = {"ctrack-disable", optional_argument, 0, 0}, + [IDX_PAYLOAD_DISABLE] = {"payload-disable", optional_argument, 0, 0}, [IDX_SERVER] = {"server", optional_argument, 0, 0}, [IDX_IPCACHE_LIFETIME] = {"ipcache-lifetime", required_argument, 0, 0}, [IDX_IPCACHE_HOSTNAME] = {"ipcache-hostname", optional_argument, 0, 0}, @@ -1945,6 +1948,18 @@ int main(int argc, char **argv) case IDX_IPCACHE_HOSTNAME: params.cache_hostname = !optarg || atoi(optarg); break; + case IDX_PAYLOAD_DISABLE: + if (optarg) + { + if (!parse_l7p_list(optarg, ¶ms.payload_disable)) + { + DLOG_ERR("Invalid payload filter : %s\n", optarg); + exit_clean(1); + } + } + else + params.payload_disable = L7P_ALL; + break; case IDX_REASM_DISABLE: if (optarg) { @@ -1955,7 +1970,7 @@ int main(int argc, char **argv) } } else - params.reasm_payload_disable = 0xFFFFFFFFFFFFFFFF; + params.reasm_payload_disable = L7P_ALL; break; #if defined(__linux__) case IDX_FWMARK: diff --git a/nfq2/params.c b/nfq2/params.c index dbf2f72..5f4f384 100644 --- a/nfq2/params.c +++ b/nfq2/params.c @@ -532,6 +532,8 @@ void init_params(struct params_s *params) LIST_INIT(¶ms->blobs); LIST_INIT(¶ms->lua_init_scripts); + params->reasm_payload_disable = params->payload_disable = 1<ssid_filter); LIST_INIT(¶ms->nlm_filter); diff --git a/nfq2/params.h b/nfq2/params.h index 89147ae..d536656 100644 --- a/nfq2/params.h +++ b/nfq2/params.h @@ -21,8 +21,6 @@ #include #endif -#define TLS_PARTIALS_ENABLE true - #define RAW_SNDBUF (64*1024) // in bytes #define Q_MAXLEN 1024 // in packets @@ -176,6 +174,7 @@ struct params_s unsigned int ipcache_lifetime; ip_cache ipcache; uint64_t reasm_payload_disable; + uint64_t payload_disable; struct str_list_head lua_init_scripts; bool writeable_dir_enable; diff --git a/nfq2/protocol.c b/nfq2/protocol.c index 50b7c55..5e94138 100644 --- a/nfq2/protocol.c +++ b/nfq2/protocol.c @@ -675,7 +675,7 @@ ssize_t TLSPos(t_marker posmarker, int16_t pos, const uint8_t *data, size_t sz) case PM_HOST_MIDSLD: case PM_HOST_ENDSLD: case PM_SNI_EXT: - if (TLSFindExt(data,sz,0,&ext,&elen,TLS_PARTIALS_ENABLE)) + if (TLSFindExt(data,sz,0,&ext,&elen,true)) { if (posmarker==PM_SNI_EXT) { diff --git a/nfq2/protocol.h b/nfq2/protocol.h index 07869ab..64ef7ed 100644 --- a/nfq2/protocol.h +++ b/nfq2/protocol.h @@ -20,7 +20,7 @@ typedef enum { L7_XMPP, L7_DNS, L7_MTPROTO, - L7_LAST, L7_INVALID=L7_LAST + L7_LAST, L7_INVALID=L7_LAST, L7_NONE=L7_LAST } t_l7proto; const char *l7proto_str(t_l7proto l7); t_l7proto l7proto_from_name(const char *name); @@ -53,7 +53,7 @@ typedef enum { L7P_DNS_QUERY, L7P_DNS_RESPONSE, L7P_MTPROTO_INITIAL, - L7P_LAST, L7P_INVALID=L7P_LAST + L7P_LAST, L7P_INVALID=L7P_LAST, L7P_NONE=L7P_LAST } t_l7payload; t_l7payload l7payload_from_name(const char *name); const char *l7payload_str(t_l7payload l7);