From 620f448fb21d394195e5f3da769c026929f38e7c Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 11 Feb 2026 01:24:20 +0300 Subject: [PATCH] Add --frag-origin-retries and --tcp-match-all --- README.md | 3 +++ src/args.c | 24 +++++++++++++++++++++++- src/config.h | 4 ++++ src/dpi.c | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fbeb6b0..721d204 100644 --- a/README.md +++ b/README.md @@ -248,6 +248,8 @@ Flags that do not scoped to a specific section, used over all the youtubeUnblock - `--tcp-match-connpackets` Use this with `--use-conntrack` set. Instead of matching by TLS domains will match packets by OS conntrack connpackets variable (e. g. number of packets sent while connection is alive (SYN is included in the connpackets counter, but anyways will be skipped by youtubeUnblock). You should not set too high number for matching. I recommend something like 4 or 5. If matching happens, youtubeUnblock will send fake and fragement the packet according to fragmentation and faking settings. +- `--tcp-match-all` Matches every packet caught by youtubeUnblock. More strict rules may be applied via tcp-dport-filter. + - `--fake-sni={0|1}` This flag enables fake-sni which forces **youtubeUnblock** to send at least three packets instead of one with TLS *ClientHello*: Fake *ClientHello*, 1st part of original *ClientHello*, 2nd part of original *ClientHello*. This flag may be related to some Operation not permitted error messages, so before open an issue refer to [Troubleshooting for EPERMS](#troubleshooting-eperms-operation-not-permitted). Defaults to **1**. - `--fake-sni-seq-len=` This flag specifies **youtubeUnblock** to build a complicated construction of fake client hello packets. length determines how much fakes will be sent. Defaults to **1**. @@ -281,6 +283,7 @@ Flags that do not scoped to a specific section, used over all the youtubeUnblock - `--frag-middle-sni={0|1}` With this options **youtubeUnblock** will split the packet in the middle of SNI data. Defaults to 1. - `--frag-sni-pos=` With this option **youtubeUnblock** will split the packet at the position pos. Defaults to 1. +- `--frag-origin-retries=` If set, youtubeUnblock will send n + 1 packets with original contents to the socket. Any additional fragmentation does not applied. - `--fk-winsize=` Specifies window size for the fragmented TCP packet. Applicable if you want for response to be fragmented. May slowdown connection initialization. diff --git a/src/args.c b/src/args.c index d774712..dee292a 100644 --- a/src/args.c +++ b/src/args.c @@ -369,6 +369,7 @@ enum { OPT_FRAG_SNI_FAKED, OPT_FRAG_MIDDLE_SNI, OPT_FRAG_SNI_POS, + OPT_FRAG_ORIGIN_RETRIES, OPT_FK_WINSIZE, OPT_TRACE, OPT_INSTAFLUSH, @@ -399,6 +400,7 @@ enum { OPT_VERSION, OPT_CONNBYTES_LIMIT, OPT_TCP_M_CONNPKTS, + OPT_TCP_M_ALL, }; static struct option long_opt[] = { @@ -426,6 +428,7 @@ static struct option long_opt[] = { {"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED}, {"frag-middle-sni", 1, 0, OPT_FRAG_MIDDLE_SNI}, {"frag-sni-pos", 1, 0, OPT_FRAG_SNI_POS}, + {"frag-origin-retries", 1, 0, OPT_FRAG_ORIGIN_RETRIES}, {"fk-winsize", 1, 0, OPT_FK_WINSIZE}, {"quic-drop", 0, 0, OPT_QUIC_DROP}, {"sni-detection", 1, 0, OPT_SNI_DETECTION}, @@ -439,6 +442,7 @@ static struct option long_opt[] = { {"udp-filter-quic", 1, 0, OPT_UDP_FILTER_QUIC}, {"no-dport-filter", 0, 0, OPT_NO_DPORT_FILTER}, {"tcp-match-connpackets", 1, 0, OPT_TCP_M_CONNPKTS}, + {"tcp-match-all", 0, 0, OPT_TCP_M_ALL}, {"threads", 1, 0, OPT_THREADS}, {"silent", 0, 0, OPT_SILENT}, {"trace", 0, 0, OPT_TRACE}, @@ -497,6 +501,7 @@ void print_usage(const char *argv0) { printf("\t--frag-sni-faked={0|1}\n"); printf("\t--frag-middle-sni={0|1}\n"); printf("\t--frag-sni-pos=\n"); + printf("\t--frag-origin-retries=\n"); printf("\t--fk-winsize=\n"); printf("\t--quic-drop\n"); printf("\t--sni-detection={parse|brute}\n"); @@ -513,6 +518,7 @@ void print_usage(const char *argv0) { printf("\t--packet-mark=\n"); printf("\t--connbytes-limit=\n"); printf("\t--tcp-match-connpackets=\n"); + printf("\t--tcp-match-all\n"); printf("\t--silent\n"); printf("\t--trace\n"); printf("\t--instaflush\n"); @@ -794,6 +800,14 @@ int yparse_args(struct config_t *config, int argc, char *argv[]) { sect_config->frag_sni_pos = num; break; + case OPT_FRAG_ORIGIN_RETRIES: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0 || num > 20) { + goto invalid_opt; + } + + sect_config->frag_origin_retries = num; + break; case OPT_TCP_DPORT_FILTER: { SFREE(sect_config->tcp_dport_range); @@ -810,7 +824,9 @@ int yparse_args(struct config_t *config, int argc, char *argv[]) { sect_config->tcp_match_connpkts = num; break; - + case OPT_TCP_M_ALL: + sect_config->tcp_match_all = 1; + break; case OPT_FAKING_STRATEGY: if (parse_faking_strategy( optarg, §_config->faking_strategy) < 0) { @@ -1093,6 +1109,10 @@ static size_t print_config_section(const struct section_config_t *section, char section->tcp_match_connpkts); } + if (section->tcp_match_all) { + print_cnf_buf("--tcp-match-all"); + } + if (section->tls_enabled || section->tcp_dport_range_len != 0) { if (section->tls_enabled) { print_cnf_buf("--tls=enabled"); @@ -1113,6 +1133,8 @@ static size_t print_config_section(const struct section_config_t *section, char print_cnf_buf("--frag-sni-reverse=%d", section->frag_sni_reverse); print_cnf_buf("--frag-sni-faked=%d", section->frag_sni_faked); + if (section->frag_origin_retries) + print_cnf_buf("--frag-origin-retries=%d", section->frag_origin_retries); print_cnf_buf("--frag-middle-sni=%d", section->frag_middle_sni); print_cnf_buf("--frag-sni-pos=%d", section->frag_sni_pos); print_cnf_buf("--fk-winsize=%d", section->fk_winsize); diff --git a/src/config.h b/src/config.h index c5507eb..b9fd75b 100644 --- a/src/config.h +++ b/src/config.h @@ -68,10 +68,12 @@ struct section_config_t { int tcp_dport_range_len; int tcp_match_connpkts; + int tcp_match_all; int fragmentation_strategy; int frag_sni_reverse; int frag_sni_faked; + int frag_origin_retries; int faking_strategy; int frag_middle_sni; int frag_sni_pos; @@ -246,9 +248,11 @@ enum { .tcp_dport_range = NULL, \ .tcp_dport_range_len = 0, \ .tcp_match_connpkts = 0, \ + .tcp_match_all = 0, \ .tls_enabled = 1, \ .frag_sni_reverse = 1, \ .frag_sni_faked = 0, \ + .frag_origin_retries = 0, \ .fragmentation_strategy = FRAGMENTATION_STRATEGY, \ .faking_strategy = FAKING_STRATEGY, \ .faking_ttl = FAKE_TTL, \ diff --git a/src/dpi.c b/src/dpi.c index 190d4b5..314dc91 100644 --- a/src/dpi.c +++ b/src/dpi.c @@ -207,9 +207,9 @@ int process_tcp_packet(const struct section_config_t *section, const struct pars if (pkt->tcph->syn) return PKT_CONTINUE; - int is_matched = 0; struct fragmentation_points frag_pts = {0}; + int is_matched = section->tcp_match_all; if (!is_matched && section->tls_enabled) { enum tls_proc_verdict vrd = process_tls_packet(section, pkt, &frag_pts); @@ -238,6 +238,16 @@ int process_tcp_packet(const struct section_config_t *section, const struct pars } } + if (section->tcp_match_all) { + frag_pts.used_points = 0; + if (section->frag_sni_pos && + pkt->transport_payload_len > section->frag_sni_pos) { + frag_pts.payload_points[frag_pts.used_points++] = + section->frag_sni_pos; + lgtrace_addp("frag set to %d", section->frag_sni_pos); + } + } + if (is_matched) { return perform_attack(section, pkt, &frag_pts); @@ -355,6 +365,42 @@ int perform_attack(const struct section_config_t *section, post_fake_sni(f_type, iph, iph_len, tcph, tcph_len); } + if (section->frag_origin_retries) { + void *iph; + size_t iph_len; + struct tcphdr *tcph; + size_t tcph_len; + uint8_t *data; + size_t dlen; + + int ret = tcp_payload_split(payload, payload_len, + &iph, &iph_len, &tcph, &tcph_len, + &data, &dlen); + + if (ret < 0) { + lgerror(ret, "tcp_payload_split in targ_sni"); + goto accept_lc; + } + + + for (int i = 0; i < section->frag_origin_retries; i++) { + if (pkt->ipver == IP4VERSION) { + struct iphdr *ip4h = (struct iphdr *)iph; + ip4h->id = htons(ntohs(ip4h->id) + i + 1); + } + + set_ip_checksum(iph, iph_len); + set_tcp_checksum(tcph, iph, iph_len); + + lgtrace_addp("post frag dup #%d", i + 1); + ret = instance_config.send_raw_packet(payload, payload_len); + if (ret < 0) { + lgerr("send frag dup failed"); + } + } + + goto accept_lc; + } if (frag_pts->used_points > 0) { if (section->fragmentation_strategy == FRAG_STRAT_TCP) {