From 24826f851d2e36f8e56d03ff19f875c8f1685bbb Mon Sep 17 00:00:00 2001 From: zabbius Date: Mon, 12 Aug 2024 04:52:03 +0300 Subject: [PATCH 01/13] removed duplicated code in args.c --- args.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/args.c b/args.c index fd1b49e..db4a2df 100644 --- a/args.c +++ b/args.c @@ -163,7 +163,6 @@ int parse_args(int argc, char *argv[]) { } else if (strcmp(optarg, "none") == 0) { config.fragmentation_strategy = FRAG_STRAT_NONE; } else { - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -175,7 +174,6 @@ int parse_args(int argc, char *argv[]) { config.frag_sni_faked = 0; } else { errno = EINVAL; - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -187,7 +185,6 @@ int parse_args(int argc, char *argv[]) { config.frag_sni_reverse = 0; } else { errno = EINVAL; - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -199,7 +196,6 @@ int parse_args(int argc, char *argv[]) { config.faking_strategy = FAKE_STRAT_TTL; } else { errno = EINVAL; - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -207,7 +203,6 @@ int parse_args(int argc, char *argv[]) { case OPT_FAKING_TTL: num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > 255) { - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -221,7 +216,6 @@ int parse_args(int argc, char *argv[]) { config.fake_sni = 0; } else { errno = EINVAL; - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -229,7 +223,6 @@ int parse_args(int argc, char *argv[]) { case OPT_FAKE_SNI_SEQ_LEN: num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > 255) { - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -238,7 +231,6 @@ int parse_args(int argc, char *argv[]) { case OPT_FK_WINSIZE: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -247,7 +239,6 @@ int parse_args(int argc, char *argv[]) { case OPT_SEG2DELAY: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -256,7 +247,6 @@ int parse_args(int argc, char *argv[]) { case OPT_THREADS: num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > MAX_THREADS) { - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -265,7 +255,6 @@ int parse_args(int argc, char *argv[]) { case OPT_QUEUE_NUM: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { - printf("Invalid option %s\n", long_opt[optIdx].name); goto error; } @@ -284,6 +273,7 @@ out: errno = 0; return 1; error: + printf("Invalid option %s\n", long_opt[optIdx].name); print_usage(argv[0]); errno = EINVAL; return -1; From 219062aae227cbd291dee5a4eaca189dde4cec71 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 12 Aug 2024 15:22:04 +0300 Subject: [PATCH 02/13] Fix segfault bufs, update coding style --- args.c | 233 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 115 insertions(+), 118 deletions(-) diff --git a/args.c b/args.c index db4a2df..61c9a8f 100644 --- a/args.c +++ b/args.c @@ -134,146 +134,143 @@ int parse_args(int argc, char *argv[]) { while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) { switch (opt) { - case 'h': - print_usage(argv[0]); - goto out; - case 'v': - print_version(); - goto out; - case OPT_SILENT: - config.verbose = 0; - break; - case OPT_NO_GSO: - config.use_gso = 0; - break; - case OPT_SNI_DOMAINS: - if (!strcmp(optarg, "all")) { - config.all_domains = 1; - } + case 'h': + print_usage(argv[0]); + goto stop_exec; + case 'v': + print_version(); + goto stop_exec; + case OPT_SILENT: + config.verbose = 0; + break; + case OPT_NO_GSO: + config.use_gso = 0; + break; + case OPT_SNI_DOMAINS: + if (!strcmp(optarg, "all")) { + config.all_domains = 1; + } - config.domains_str = optarg; - config.domains_strlen = strlen(config.domains_str); + config.domains_str = optarg; + config.domains_strlen = strlen(config.domains_str); + break; + case OPT_FRAG: + if (strcmp(optarg, "tcp") == 0) { + config.fragmentation_strategy = FRAG_STRAT_TCP; + } else if (strcmp(optarg, "ip") == 0) { + config.fragmentation_strategy = FRAG_STRAT_IP; + } else if (strcmp(optarg, "none") == 0) { + config.fragmentation_strategy = FRAG_STRAT_NONE; + } else { + goto invalid_opt; + } - break; - case OPT_FRAG: - if (strcmp(optarg, "tcp") == 0) { - config.fragmentation_strategy = FRAG_STRAT_TCP; - } else if (strcmp(optarg, "ip") == 0) { - config.fragmentation_strategy = FRAG_STRAT_IP; - } else if (strcmp(optarg, "none") == 0) { - config.fragmentation_strategy = FRAG_STRAT_NONE; - } else { - goto error; - } + break; + case OPT_FRAG_SNI_FAKED: + if (strcmp(optarg, "1") == 0) { + config.frag_sni_faked = 1; + } else if (strcmp(optarg, "0") == 0) { + config.frag_sni_faked = 0; + } else { + goto invalid_opt; + } - break; - case OPT_FRAG_SNI_FAKED: - if (strcmp(optarg, "1") == 0) { - config.frag_sni_faked = 1; - } else if (strcmp(optarg, "0") == 0) { - config.frag_sni_faked = 0; - } else { - errno = EINVAL; - goto error; - } + break; + case OPT_FRAG_SNI_REVERSE: + if (strcmp(optarg, "1") == 0) { + config.frag_sni_reverse = 1; + } else if (strcmp(optarg, "0") == 0) { + config.frag_sni_reverse = 0; + } else { + goto invalid_opt; + } - break; - case OPT_FRAG_SNI_REVERSE: - if (strcmp(optarg, "1") == 0) { - config.frag_sni_reverse = 1; - } else if (strcmp(optarg, "0") == 0) { - config.frag_sni_reverse = 0; - } else { - errno = EINVAL; - goto error; - } + break; + case OPT_FAKING_STRATEGY: + if (strcmp(optarg, "ack") == 0) { + config.faking_strategy = FAKE_STRAT_ACK_SEQ; + } else if (strcmp(optarg, "ttl") == 0) { + config.faking_strategy = FAKE_STRAT_TTL; + } else { + goto invalid_opt; + } - break; - case OPT_FAKING_STRATEGY: - if (strcmp(optarg, "ack") == 0) { - config.faking_strategy = FAKE_STRAT_ACK_SEQ; - } else if (strcmp(optarg, "ttl") == 0) { - config.faking_strategy = FAKE_STRAT_TTL; - } else { - errno = EINVAL; - goto error; - } + break; + case OPT_FAKING_TTL: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0 || num > 255) { + goto invalid_opt; + } - break; - case OPT_FAKING_TTL: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0 || num > 255) { - goto error; - } + config.faking_ttl = num; + break; - config.faking_ttl = num; - break; + case OPT_FAKE_SNI: + if (strcmp(optarg, "1") == 0) { + config.fake_sni = 1; + } else if (strcmp(optarg, "0") == 0) { + config.fake_sni = 0; + } else { + goto invalid_opt; + } - case OPT_FAKE_SNI: - if (strcmp(optarg, "1") == 0) { - config.fake_sni = 1; - } else if (strcmp(optarg, "0") == 0) { - config.fake_sni = 0; - } else { - errno = EINVAL; - goto error; - } + break; + case OPT_FAKE_SNI_SEQ_LEN: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0 || num > 255) { + goto invalid_opt; + } - break; - case OPT_FAKE_SNI_SEQ_LEN: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0 || num > 255) { - goto error; - } + config.fake_sni_seq_len = num; + break; + case OPT_FK_WINSIZE: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0) { + goto invalid_opt; + } - config.fake_sni_seq_len = num; - break; - case OPT_FK_WINSIZE: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0) { - goto error; - } + config.fk_winsize = num; + break; + case OPT_SEG2DELAY: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0) { + goto invalid_opt; + } - config.fk_winsize = num; - break; - case OPT_SEG2DELAY: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0) { - goto error; - } + config.seg2_delay = num; + break; + case OPT_THREADS: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0 || num > MAX_THREADS) { + goto invalid_opt; + } - config.seg2_delay = num; - break; - case OPT_THREADS: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0 || num > MAX_THREADS) { - goto error; - } + config.threads = num; + break; + case OPT_QUEUE_NUM: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0) { + goto invalid_opt; + } - config.threads = num; - break; - case OPT_QUEUE_NUM: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0) { - goto error; - } - - config.queue_start_num = num; - break; - default: - goto error; + config.queue_start_num = num; + break; + default: + goto error; } } - +// out: errno = 0; return 0; -out: +stop_exec: errno = 0; return 1; -error: + +invalid_opt: printf("Invalid option %s\n", long_opt[optIdx].name); +error: print_usage(argv[0]); errno = EINVAL; return -1; From d5db8c18e583e1316df501076b63edc2a679a2de Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 13 Aug 2024 01:59:04 +0300 Subject: [PATCH 03/13] Types to distinct file common for the entire program --- mangle.c | 102 +++++++++++++++++++++++++------------------------------ mangle.h | 8 ++--- types.h | 36 ++++++++++++++++++++ 3 files changed, 84 insertions(+), 62 deletions(-) create mode 100644 types.h diff --git a/mangle.c b/mangle.c index 321a27c..d47733a 100644 --- a/mangle.c +++ b/mangle.c @@ -1,25 +1,15 @@ -#include #define _GNU_SOURCE +#include "types.h" // IWYU pragma: keep #include "mangle.h" #include "config.h" #ifdef KERNEL_SPACE -#include #include -#define printf pr_info -#define perror pr_err -#define lgerror(msg, ret) (pr_err(msg ": %d\n", ret)) #else -#include +#include #include #include - -typedef uint8_t __u8; -typedef uint32_t __u32; -typedef uint16_t __u16; - -#define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);}) #endif @@ -365,9 +355,9 @@ void ip4_set_checksum(struct iphdr *iph) } -int ip4_payload_split(__u8 *pkt, __u32 buflen, - struct iphdr **iph, __u32 *iph_len, - __u8 **payload, __u32 *plen) { +int ip4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + uint8_t **payload, uint32_t *plen) { if (pkt == NULL || buflen < sizeof(struct iphdr)) { lgerror("ip4_payload_split: pkt|buflen", -EINVAL); return -EINVAL; @@ -379,8 +369,8 @@ int ip4_payload_split(__u8 *pkt, __u32 buflen, return -EINVAL; } - __u32 hdr_len = hdr->ihl * 4; - __u32 pktlen = ntohs(hdr->tot_len); + uint32_t hdr_len = hdr->ihl * 4; + uint32_t pktlen = ntohs(hdr->tot_len); if (buflen < pktlen || hdr_len > pktlen) { lgerror("ip4_payload_split: buflen cmp pktlen", -EINVAL); return -EINVAL; @@ -398,17 +388,17 @@ int ip4_payload_split(__u8 *pkt, __u32 buflen, return 0; } -int tcp4_payload_split(__u8 *pkt, __u32 buflen, - struct iphdr **iph, __u32 *iph_len, - struct tcphdr **tcph, __u32 *tcph_len, - __u8 **payload, __u32 *plen) { +int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + struct tcphdr **tcph, uint32_t *tcph_len, + uint8_t **payload, uint32_t *plen) { struct iphdr *hdr; - __u32 hdr_len; + uint32_t hdr_len; struct tcphdr *thdr; - __u32 thdr_len; + uint32_t thdr_len; - __u8 *tcph_pl; - __u32 tcph_plen; + uint8_t *tcph_pl; + uint32_t tcph_plen; if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len, &tcph_pl, &tcph_plen)){ @@ -441,22 +431,22 @@ int tcp4_payload_split(__u8 *pkt, __u32 buflen, } // split packet to two ipv4 fragments. -int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, - __u8 *frag1, __u32 *f1len, - __u8 *frag2, __u32 *f2len) { +int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, + uint8_t *frag1, uint32_t *f1len, + uint8_t *frag2, uint32_t *f2len) { struct iphdr *hdr; - const __u8 *payload; - __u32 plen; - __u32 hdr_len; + const uint8_t *payload; + uint32_t plen; + uint32_t hdr_len; int ret; if (!frag1 || !f1len || !frag2 || !f2len) return -EINVAL; if ((ret = ip4_payload_split( - (__u8 *)pkt, buflen, - &hdr, &hdr_len, (__u8 **)&payload, &plen)) < 0) { + (uint8_t *)pkt, buflen, + &hdr, &hdr_len, (uint8_t **)&payload, &plen)) < 0) { lgerror("ipv4_frag: TCP Header extract error", ret); return -EINVAL; } @@ -471,11 +461,11 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, return -EINVAL; } - __u32 f1_plen = payload_offset; - __u32 f1_dlen = f1_plen + hdr_len; + uint32_t f1_plen = payload_offset; + uint32_t f1_dlen = f1_plen + hdr_len; - __u32 f2_plen = plen - payload_offset; - __u32 f2_dlen = f2_plen + hdr_len; + uint32_t f2_plen = plen - payload_offset; + uint32_t f2_dlen = f2_plen + hdr_len; if (*f1len < f1_dlen || *f2len < f2_dlen) { return -ENOMEM; @@ -492,8 +482,8 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, struct iphdr *f1_hdr = (void *)frag1; struct iphdr *f2_hdr = (void *)frag2; - __u16 f1_frag_off = ntohs(f1_hdr->frag_off); - __u16 f2_frag_off = ntohs(f2_hdr->frag_off); + uint16_t f1_frag_off = ntohs(f1_hdr->frag_off); + uint16_t f2_frag_off = ntohs(f2_hdr->frag_off); f1_frag_off &= IP_OFFMASK; f1_frag_off |= IP_MF; @@ -505,7 +495,7 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, f2_frag_off &= IP_OFFMASK; } - f2_frag_off += (__u16)payload_offset / 8; + f2_frag_off += (uint16_t)payload_offset / 8; f1_hdr->frag_off = htons(f1_frag_off); f1_hdr->tot_len = htons(f1_dlen); @@ -524,25 +514,25 @@ int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, } // split packet to two tcp-on-ipv4 segments. -int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, - __u8 *seg1, __u32 *s1len, - __u8 *seg2, __u32 *s2len) { +int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, + uint8_t *seg1, uint32_t *s1len, + uint8_t *seg2, uint32_t *s2len) { struct iphdr *hdr; - __u32 hdr_len; + uint32_t hdr_len; struct tcphdr *tcph; - __u32 tcph_len; - __u32 plen; - const __u8 *payload; + uint32_t tcph_len; + uint32_t plen; + const uint8_t *payload; int ret; if (!seg1 || !s1len || !seg2 || !s2len) return -EINVAL; - if ((ret = tcp4_payload_split((__u8 *)pkt, buflen, + if ((ret = tcp4_payload_split((uint8_t *)pkt, buflen, &hdr, &hdr_len, &tcph, &tcph_len, - (__u8 **)&payload, &plen)) < 0) { + (uint8_t **)&payload, &plen)) < 0) { lgerror("tcp4_frag: tcp4_payload_split", ret); return -EINVAL; @@ -563,11 +553,11 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, return -EINVAL; } - __u32 s1_plen = payload_offset; - __u32 s1_dlen = s1_plen + hdr_len + tcph_len; + uint32_t s1_plen = payload_offset; + uint32_t s1_dlen = s1_plen + hdr_len + tcph_len; - __u32 s2_plen = plen - payload_offset; - __u32 s2_dlen = s2_plen + hdr_len + tcph_len; + uint32_t s2_plen = plen - payload_offset; + uint32_t s2_dlen = s2_plen + hdr_len + tcph_len; if (*s1len < s1_dlen || *s2len < s2_dlen) return -ENOMEM; @@ -609,9 +599,9 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset, #define TLS_EXTENSION_SNI 0x0000 #define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d -typedef __u8 uint8_t; -typedef __u32 uint32_t; -typedef __u16 uint16_t; +typedef uint8_t uint8_t; +typedef uint32_t uint32_t; +typedef uint16_t uint16_t; /** * Processes tls payload of the tcp request. diff --git a/mangle.h b/mangle.h index b3a1137..7e98358 100644 --- a/mangle.h +++ b/mangle.h @@ -1,13 +1,10 @@ #ifndef YU_MANGLE_H #define YU_MANGLE_H -#ifdef KERNEL_SPACE -#include -typedef __u8 uint8_t; -typedef __u32 uint32_t; +#include "types.h" +#ifdef KERNEL_SPACE #include -#include #include #include #include @@ -25,7 +22,6 @@ typedef __u32 uint32_t; #define USER_SPACE #include #include -#include #include #include #include diff --git a/types.h b/types.h new file mode 100644 index 0000000..324afd7 --- /dev/null +++ b/types.h @@ -0,0 +1,36 @@ +#ifndef TYPES_H +#define TYPES_H +#include + +#ifdef KERNEL_SCOPE +#include // IWYU pragma: export +#include // IWYU pragma: export + +#include +typedef __u8 uint8_t; +typedef __u16 uint16_t; +typedef __u32 uint32_t; +typedef __u64 uint64_t; +typedef __i8 int8_t; +typedef __i16 int16_t; +typedef __i32 int32_t; +typedef __i64 int64_t; + +#include +#define printf pr_info +#define perror pr_err +#define lgerror(msg, ret) (pr_err(msg ": %d\n", ret)) + +#else /* USERSPACE_SCOPE */ + +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +#include // IWYU pragma: export +#define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);}) + + +#endif /* SCOPES */ + +#endif /* TYPES_H */ From 4a8f0d18a91cd4af32d0d39fb443a2e784dcc7a1 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 13 Aug 2024 01:59:47 +0300 Subject: [PATCH 04/13] Skeleton for quic initial message parser --- quic.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ quic.h | 113 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 quic.c create mode 100644 quic.h diff --git a/quic.c b/quic.c new file mode 100644 index 0000000..a2dc291 --- /dev/null +++ b/quic.c @@ -0,0 +1,137 @@ +#include "quic.h" + +static const uint32_t supported_versions[] = { + 1, // version 1, RFC 9000 + 0x6b3343cf, // version 2, RFC 9369 +}; + +/** + * Packet number. + */ +struct quic_pnumber { + uint8_t d1; + uint8_t d2; + uint8_t d3; + uint8_t d4; +}; + +uint64_t quic_parse_varlength(uint8_t *variable, uint64_t *mlen) { + if (mlen && *mlen == 0) return 0; + uint64_t vr = (*variable & 0x3F); + uint8_t len = 1 << (*variable >> 6); + + if (mlen) { + if (*mlen < len) return 0; + *mlen = len; + } + + ++variable; + for (uint8_t i = 1; i < len; i++) { + vr = (vr << 8) + *variable; + ++variable; + } + + return vr; +} + +int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len, + struct quic_lhdr **qch, uint32_t *qch_len, + struct quic_cids *qci, + uint8_t **payload, uint32_t *plen) { + if ( raw_payload == NULL || + raw_payload_len < sizeof(struct quic_lhdr)) + goto invalid_packet; + + struct quic_lhdr *nqch = (struct quic_lhdr *)raw_payload; + uint32_t left_len = raw_payload_len - sizeof(struct quic_lhdr); + uint8_t *cur_rawptr = raw_payload + sizeof(struct quic_lhdr); + if (!nqch->fixed) + return -EPROTO; + + uint8_t found = 0; + for (uint8_t i = 0; i < sizeof(supported_versions); i++) { + if (nqch->version == supported_versions[i]) { + found = 1; + } + } + + if (!found) + return -EPROTO; + + if (left_len < 2) goto invalid_packet; + struct quic_cids nqci = {0}; + + nqci.dst_len = *cur_rawptr++; + left_len--; + if (left_len < nqci.dst_len) goto invalid_packet; + nqci.dst_id = cur_rawptr; + cur_rawptr += nqci.dst_len; + left_len -= nqci.dst_len; + + nqci.src_len = *cur_rawptr++; + left_len--; + if (left_len < nqci.src_len) goto invalid_packet; + nqci.src_id = cur_rawptr; + cur_rawptr += nqci.src_len; + left_len -= nqci.src_len; + + if (qch) *qch = nqch; + if (qch_len) { + *qch_len = sizeof(struct quic_lhdr) + + nqci.src_len + nqci.dst_len; + } + if (qci) *qci = nqci; + if (payload) *payload = cur_rawptr; + if (plen) *plen = left_len; + + return 0; + +invalid_packet: + return -EINVAL; +} + +int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen, + const struct quic_lhdr *qch, + struct quici_hdr *qhdr, + uint8_t **payload, uint32_t *plen) { + if (inplen < 3) goto invalid_packet; + struct quici_hdr nqhdr; + + uint8_t *cur_ptr = inpayload; + uint32_t left_len = inplen; + uint64_t tlen = left_len; + + nqhdr.token_len = quic_parse_varlength(cur_ptr, &tlen); + nqhdr.token = cur_ptr + tlen; + + if (left_len < nqhdr.token_len + tlen) + goto invalid_packet; + cur_ptr += tlen + nqhdr.token_len; + left_len -= tlen + nqhdr.token_len; + + tlen = left_len; + nqhdr.length = quic_parse_varlength(cur_ptr, &tlen); + + if (left_len != nqhdr.length + tlen && + left_len <= qch->number_length + 1) + goto invalid_packet; + + uint32_t packet_number = 0; + + for (uint8_t i = 0; i <= qch->number_length; i++) { + packet_number = (packet_number << 8) + *cur_ptr++; + left_len--; + } + + nqhdr.packet_number = packet_number; + + if (qhdr) *qhdr = nqhdr; + if (payload) *payload = cur_ptr; + if (plen) *plen = left_len; + + return 0; + +invalid_packet: + lgerror("QUIC invalid Initial packet", -EINVAL); + return -EINVAL; +} diff --git a/quic.h b/quic.h new file mode 100644 index 0000000..cf46df6 --- /dev/null +++ b/quic.h @@ -0,0 +1,113 @@ +#ifndef QUIC_H +#define QUIC_H +#include "types.h" + +/** +* @macro +* +* :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to +* derive initial secret. It is used for QUIC v1. +*/ +#define QUIC_INITIAL_SALT_V1 \ + "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad" \ + "\xcc\xbb\x7f\x0a" + +/** +* @macro +* +* :macro:`NGTCP2_INITIAL_SALT_V2` is a salt value which is used to +* derive initial secret. It is used for QUIC v2. +*/ +#define QUIC_INITIAL_SALT_V2 \ + "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb" \ + "\xf9\xbd\x2e\xd9" + +#define QUIC_INITIAL_TYPE_V1 0x00 +#define QUIC_0_RTT_TYPE_V1 0x01 +#define QUIC_HANDSHAKE_TYPE_V1 0x02 +#define QUIC_RETRY_TYPE_V1 0x03 + +#define QUIC_INITIAL_TYPE_V2 0b01 +#define QUIC_0_RTT_TYPE_V2 0b10 +#define QUIC_HANDSHAKE_TYPE_V2 0b11 +#define QUIC_RETRY_TYPE_V2 0b00 + +/** + * Quic Large Header + */ +struct quic_lhdr { +#if __BYTE_ORDER == __LITTLE_ENDIAN + uint8_t number_length: 2; + uint8_t reserved: 2; + uint8_t type: 2; + uint8_t fixed: 1; + uint8_t form: 1; +#elif __BYTE_ORDER == __BIG_ENDIAN + uint8_t form: 1; + uint8_t fixed: 1; + uint8_t type: 2; + uint8_t reserved: 2; + uint8_t number_length: 2; +#else +#error "Undefined endian" +#endif + + uint32_t version; +}; + +/** + * Quic Large Header Ids + * (separated from the original header because of varying dst + */ +struct quic_cids { + uint8_t dst_len; + uint8_t *dst_id; + uint8_t src_len; + uint8_t *src_id; +}; + +/** + * Parses QUIС raw data (UDP payload) to quic large header and + * quic payload. + * + * \qch_len is sizeof(qch) + qci->dst_len + qci->src_id + * \payload is Type-Specific payload (#17.2). + */ +int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len, + struct quic_lhdr **qch, uint32_t *qch_len, + struct quic_cids *qci, + uint8_t **payload, uint32_t *plen); + + +/** + * Parses QUIC variable-length integer. (#16) + * \variable is a pointer to the sequence to be parsed + * (varlen integer in big endian format) + * + * \mlen Used to signal about variable length and validate left length + * in the buffer. + */ +uint64_t quic_parse_varlength(uint8_t *variable, uint64_t *mlen); + +// quici stands for QUIC Initial + +/** + * This structure should be parsed + */ +struct quici_hdr { + uint64_t token_len; + uint8_t *token; + uint64_t length; + uint32_t packet_number; +}; + +/** + * Parses QUIC initial payload. + * \inpayload is a raw QUIC payload (payload after quic large header) + */ +int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen, + const struct quic_lhdr *qch, + struct quici_hdr *qhdr, + uint8_t **payload, uint32_t *plen); + +#endif /* QUIC_H */ From f3db464b97ece1b0a0e12b33427e1d56838a2aab Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Tue, 13 Aug 2024 20:48:35 +0300 Subject: [PATCH 05/13] Add initial support for QUIC, improve logging capabilities. Add TRACE logging mode --- args.c | 24 ++- config.h | 4 + logging.h | 39 +++++ mangle.c | 397 ++++++++++++++--------------------------------- mangle.h | 74 ++------- quic.c | 17 +- quic.h | 55 ++++--- types.h | 31 ++-- uspace.mk | 2 +- utils.c | 312 +++++++++++++++++++++++++++++++++++++ utils.h | 51 ++++++ youtubeUnblock.c | 1 + 12 files changed, 628 insertions(+), 379 deletions(-) create mode 100644 logging.h create mode 100644 utils.c create mode 100644 utils.h diff --git a/args.c b/args.c index 61c9a8f..46677e6 100644 --- a/args.c +++ b/args.c @@ -32,10 +32,11 @@ struct config_t config = { #endif #ifdef DEBUG - .verbose = true, + .verbose = 1, #else - .verbose = false, + .verbose = 0, #endif + .domains_str = defaul_snistr, .domains_strlen = sizeof(defaul_snistr), @@ -53,13 +54,15 @@ struct config_t config = { #define OPT_FRAG_SNI_REVERSE 12 #define OPT_FRAG_SNI_FAKED 13 #define OPT_FK_WINSIZE 14 +#define OPT_TRACE 15 +#define OPT_QUIC_DROP 16 #define OPT_SEG2DELAY 5 #define OPT_THREADS 6 #define OPT_SILENT 7 #define OPT_NO_GSO 8 #define OPT_QUEUE_NUM 9 -#define OPT_MAX OPT_FRAG_SNI_FAKED +#define OPT_MAX OPT_QUIC_DROP static struct option long_opt[] = { {"help", 0, 0, 'h'}, @@ -73,9 +76,11 @@ static struct option long_opt[] = { {"frag-sni-reverse", 1, 0, OPT_FRAG_SNI_REVERSE}, {"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED}, {"fk-winsize", 1, 0, OPT_FK_WINSIZE}, + {"quic-drop", 0, 0, OPT_QUIC_DROP}, {"seg2delay", 1, 0, OPT_SEG2DELAY}, {"threads", 1, 0, OPT_THREADS}, {"silent", 0, 0, OPT_SILENT}, + {"trace", 0, 0, OPT_TRACE}, {"no-gso", 0, 0, OPT_NO_GSO}, {"queue-num", 1, 0, OPT_QUEUE_NUM}, {0,0,0,0} @@ -120,9 +125,11 @@ void print_usage(const char *argv0) { printf("\t--frag-sni-reverse={0|1}\n"); printf("\t--frag-sni-faked={0|1}\n"); printf("\t--fk-winsize=\n"); + printf("\t--quic-drop\n"); printf("\t--seg2delay=\n"); printf("\t--threads=\n"); printf("\t--silent\n"); + printf("\t--trace\n"); printf("\t--no-gso\n"); printf("\n"); } @@ -140,12 +147,18 @@ int parse_args(int argc, char *argv[]) { case 'v': print_version(); goto stop_exec; + case OPT_TRACE: + config.verbose = 2; + break; case OPT_SILENT: config.verbose = 0; break; case OPT_NO_GSO: config.use_gso = 0; break; + case OPT_QUIC_DROP: + config.quic_drop = 1; + break; case OPT_SNI_DOMAINS: if (!strcmp(optarg, "all")) { config.all_domains = 1; @@ -329,7 +342,12 @@ void print_welcome() { printf("GSO is enabled\n"); } + if (config.quic_drop) { + printf("All QUIC packets will be dropped\n"); + } + if (config.all_domains) { printf("All Client Hello will be targetted by youtubeUnblock!\n"); } + } diff --git a/config.h b/config.h index 93db448..2769176 100644 --- a/config.h +++ b/config.h @@ -25,7 +25,11 @@ struct config_t { unsigned char faking_ttl; int fake_sni; unsigned int fake_sni_seq_len; +#define VERBOSE_INFO 0 +#define VERBOSE_DEBUG 1 +#define VERBOSE_TRACE 2 int verbose; + int quic_drop; /* In milliseconds */ unsigned int seg2_delay; const char *domains_str; diff --git a/logging.h b/logging.h new file mode 100644 index 0000000..60b21eb --- /dev/null +++ b/logging.h @@ -0,0 +1,39 @@ +#ifndef LOGGING_H +#define LOGGING_H +#include "config.h" + +#define LOG_LEVEL (config.verbose) + +#ifdef KERNEL_SPACE +#include +#define printf pr_info +#define perror pr_err +#define lgerror(msg, ret, ...) __extension__ ({ \ + printf(msg ": %d\n", ##__VA_ARGS__, ret); \ +}) +#else +#include // IWYU pragma: export +#include +#define lgerror(msg, ret, ...) __extension__ ({ \ + errno = -(ret); \ + printf(msg ": %s\n", ##__VA_ARGS__, strerror(errno)); \ +}) +#endif /* PROGRAM_SPACE */ + + +#define lgdebugmsg(msg, ...) \ +(LOG_LEVEL >= VERBOSE_DEBUG ? printf(msg "\n", ##__VA_ARGS__) : 0) + +#define lgtracemsg(msg, ...) \ +(LOG_LEVEL >= VERBOSE_TRACE ? printf(msg "\n", ##__VA_ARGS__) : 0) + +#define lgtrace_start(msg, ...) \ +(LOG_LEVEL >= VERBOSE_TRACE ? printf("[TRACE] " msg " ( ", ##__VA_ARGS__) : 0) + +#define lgtrace_addp(msg, ...) \ +(LOG_LEVEL >= VERBOSE_TRACE ? printf(msg", ", ##__VA_ARGS__) : 0) + +#define lgtrace_end() \ +(LOG_LEVEL >= VERBOSE_TRACE ? printf(") \n") : 0) + +#endif /* LOGGING_H */ diff --git a/mangle.c b/mangle.c index d47733a..0d7c60d 100644 --- a/mangle.c +++ b/mangle.c @@ -2,22 +2,50 @@ #include "types.h" // IWYU pragma: keep #include "mangle.h" #include "config.h" +#include "utils.h" +#include "quic.h" +#include "logging.h" -#ifdef KERNEL_SPACE -#include - -#else +#ifndef KERNEL_SCOPE #include -#include -#include #endif - int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (raw_payload_len > MAX_PACKET_SIZE) { return PKT_ACCEPT; } + const struct iphdr *iph; + uint32_t iph_len; + const uint8_t *ip_payload; + uint32_t ip_payload_len; + + int ret; + + ret = ip4_payload_split((uint8_t *)raw_payload, raw_payload_len, + (struct iphdr **)&iph, &iph_len, + (uint8_t **)&ip_payload, &ip_payload_len); + + + if (ret < 0) + goto accept; + + switch (iph->protocol) { + case IPPROTO_TCP: + return process_tcp4_packet(raw_payload, raw_payload_len); + case IPPROTO_UDP: + return process_udp4_packet(raw_payload, raw_payload_len); + default: + goto accept; + } + +accept: + return PKT_ACCEPT; +drop: + return PKT_DROP; +} + +int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { const struct iphdr *iph; uint32_t iph_len; const struct tcphdr *tcph; @@ -36,8 +64,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { struct tls_verdict vrd = analyze_tls_data(data, dlen); if (vrd.target_sni) { - if (config.verbose) - printf("Target SNI detected: %.*s\n", vrd.sni_len, data + vrd.sni_offset); + lgdebugmsg("Target SNI detected: %.*s", vrd.sni_len, data + vrd.sni_offset); uint8_t payload[MAX_PACKET_SIZE]; uint32_t payload_len = raw_payload_len; @@ -63,7 +90,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (dlen > 1480 && config.verbose) { - printf("WARNING! Client Hello packet is too big and may cause issues!\n"); + lgdebugmsg("WARNING! Client Hello packet is too big and may cause issues!"); } if (config.fake_sni) { @@ -126,6 +153,79 @@ drop: return PKT_DROP; } +int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen) { + const struct iphdr *iph; + uint32_t iph_len; + const struct udphdr *udph; + const uint8_t *data; + uint32_t dlen; + + int ret = udp4_payload_split((uint8_t *)pkt, pktlen, + (struct iphdr **)&iph, &iph_len, + (struct udphdr **)&udph, + (uint8_t **)&data, &dlen); + + lgtrace_start("Got udp packet"); + + if (ret < 0) { + lgtrace_addp("undefined"); + goto accept; + } + + if (dlen > 10 && config.verbose >= VERBOSE_TRACE) { + printf("UDP payload start: [ "); + for (int i = 0; i < 10; i++) { + printf("%02x ", data[i]); + } + printf("], "); + } + + lgtrace_addp("QUIC probe"); + const struct quic_lhdr *qch; + uint32_t qch_len; + struct quic_cids qci; + uint8_t *quic_raw_payload; + uint32_t quic_raw_plen; + ret = quic_parse_data((uint8_t *)data, dlen, + (struct quic_lhdr **)&qch, &qch_len, &qci, + &quic_raw_payload, &quic_raw_plen); + + if (ret < 0) { + lgtrace_addp("undefined type"); + goto accept; + } + + lgtrace_addp("QUIC detected"); + uint8_t qtype = qch->type; + + if (config.quic_drop) { + goto drop; + } + + if (qch->version == QUIC_V1) + qtype = quic_convtype_v1(qtype); + else if (qch->version == QUIC_V2) + qtype = quic_convtype_v2(qtype); + + if (qtype != QUIC_INITIAL_TYPE) { + lgtrace_addp("quic message type: %d", qtype); + goto accept; + } + + lgtrace_addp("quic initial message"); + +accept: + lgtrace_addp("accepted"); + lgtrace_end(); + + return PKT_ACCEPT; +drop: + lgtrace_addp("dropped"); + lgtrace_end(); + + return PKT_DROP; +} + int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_sz, uint32_t dvs) { if (poses_sz == 0) { if (config.seg2_delay && ((dvs > 0) ^ config.frag_sni_reverse)) { @@ -150,7 +250,7 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses int ret; if (dvs > poses[0]) { - printf("send_frags: Recursive dvs(%d) is more than poses0(%d)\n", dvs, poses[0]); + lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]); return -EINVAL; } @@ -158,8 +258,7 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses frag1, &f1len, frag2, &f2len); if (ret < 0) { - lgerror("send_frags: frag", ret); - printf("Error context: packet with size %d, position: %d, recursive dvs: %d\n", pktlen, poses[0], dvs); + lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs); return ret; } @@ -214,7 +313,7 @@ int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *pose int ret; if (dvs > poses[0]) { - printf("send_frags: Recursive dvs(%d) is more than poses0(%d)\n", dvs, poses[0]); + lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]); return -EINVAL; } @@ -222,8 +321,7 @@ int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *pose frag1, &f1len, frag2, &f2len); if (ret < 0) { - lgerror("send_frags: frag", ret); - printf("Error context: packet with size %d, position: %d, recursive dvs: %d\n", pktlen, poses[0], dvs); + lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs); return ret; } @@ -330,269 +428,7 @@ int post_fake_sni(const struct iphdr *iph, unsigned int iph_len, return 0; } -void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph) -{ -#ifdef KERNEL_SPACE - uint32_t tcp_packet_len = ntohs(iph->tot_len) - (iph->ihl << 2); - tcph->check = 0; - tcph->check = csum_tcpudp_magic( - iph->saddr, iph->daddr, tcp_packet_len, - IPPROTO_TCP, - csum_partial(tcph, tcp_packet_len, 0)); -#else - nfq_tcp_compute_checksum_ipv4(tcph, iph); -#endif -} -void ip4_set_checksum(struct iphdr *iph) -{ -#ifdef KERNEL_SPACE - iph->check = 0; - iph->check = ip_fast_csum(iph, iph->ihl); -#else - nfq_ip_set_checksum(iph); -#endif -} - - -int ip4_payload_split(uint8_t *pkt, uint32_t buflen, - struct iphdr **iph, uint32_t *iph_len, - uint8_t **payload, uint32_t *plen) { - if (pkt == NULL || buflen < sizeof(struct iphdr)) { - lgerror("ip4_payload_split: pkt|buflen", -EINVAL); - return -EINVAL; - } - - struct iphdr *hdr = (struct iphdr *)pkt; - if (hdr->version != IPVERSION) { - lgerror("ip4_payload_split: ipversion", -EINVAL); - return -EINVAL; - } - - uint32_t hdr_len = hdr->ihl * 4; - uint32_t pktlen = ntohs(hdr->tot_len); - if (buflen < pktlen || hdr_len > pktlen) { - lgerror("ip4_payload_split: buflen cmp pktlen", -EINVAL); - return -EINVAL; - } - - if (iph) - *iph = hdr; - if (iph_len) - *iph_len = hdr_len; - if (payload) - *payload = pkt + hdr_len; - if (plen) - *plen = pktlen - hdr_len; - - return 0; -} - -int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, - struct iphdr **iph, uint32_t *iph_len, - struct tcphdr **tcph, uint32_t *tcph_len, - uint8_t **payload, uint32_t *plen) { - struct iphdr *hdr; - uint32_t hdr_len; - struct tcphdr *thdr; - uint32_t thdr_len; - - uint8_t *tcph_pl; - uint32_t tcph_plen; - - if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len, - &tcph_pl, &tcph_plen)){ - return -EINVAL; - } - - - if ( - hdr->protocol != IPPROTO_TCP || - tcph_plen < sizeof(struct tcphdr)) { - return -EINVAL; - } - - - thdr = (struct tcphdr *)(tcph_pl); - thdr_len = thdr->doff * 4; - - if (thdr_len > tcph_plen) { - return -EINVAL; - } - - if (iph) *iph = hdr; - if (iph_len) *iph_len = hdr_len; - if (tcph) *tcph = thdr; - if (tcph_len) *tcph_len = thdr_len; - if (payload) *payload = tcph_pl + thdr_len; - if (plen) *plen = tcph_plen - thdr_len; - - return 0; -} - -// split packet to two ipv4 fragments. -int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, - uint8_t *frag1, uint32_t *f1len, - uint8_t *frag2, uint32_t *f2len) { - - struct iphdr *hdr; - const uint8_t *payload; - uint32_t plen; - uint32_t hdr_len; - int ret; - - if (!frag1 || !f1len || !frag2 || !f2len) - return -EINVAL; - - if ((ret = ip4_payload_split( - (uint8_t *)pkt, buflen, - &hdr, &hdr_len, (uint8_t **)&payload, &plen)) < 0) { - lgerror("ipv4_frag: TCP Header extract error", ret); - return -EINVAL; - } - - if (plen <= payload_offset) { - return -EINVAL; - } - - if (payload_offset & ((1 << 3) - 1)) { - lgerror("ipv4_frag: Payload offset MUST be a multiply of 8!", -EINVAL); - - return -EINVAL; - } - - uint32_t f1_plen = payload_offset; - uint32_t f1_dlen = f1_plen + hdr_len; - - uint32_t f2_plen = plen - payload_offset; - uint32_t f2_dlen = f2_plen + hdr_len; - - if (*f1len < f1_dlen || *f2len < f2_dlen) { - return -ENOMEM; - } - *f1len = f1_dlen; - *f2len = f2_dlen; - - memcpy(frag1, hdr, hdr_len); - memcpy(frag2, hdr, hdr_len); - - memcpy(frag1 + hdr_len, payload, f1_plen); - memcpy(frag2 + hdr_len, payload + payload_offset, f2_plen); - - struct iphdr *f1_hdr = (void *)frag1; - struct iphdr *f2_hdr = (void *)frag2; - - uint16_t f1_frag_off = ntohs(f1_hdr->frag_off); - uint16_t f2_frag_off = ntohs(f2_hdr->frag_off); - - f1_frag_off &= IP_OFFMASK; - f1_frag_off |= IP_MF; - - if ((f2_frag_off & ~IP_OFFMASK) == IP_MF) { - f2_frag_off &= IP_OFFMASK; - f2_frag_off |= IP_MF; - } else { - f2_frag_off &= IP_OFFMASK; - } - - f2_frag_off += (uint16_t)payload_offset / 8; - - f1_hdr->frag_off = htons(f1_frag_off); - f1_hdr->tot_len = htons(f1_dlen); - - f2_hdr->frag_off = htons(f2_frag_off); - f2_hdr->tot_len = htons(f2_dlen); - - - if (config.verbose) - printf("Packet split in portion %u %u\n", f1_plen, f2_plen); - - ip4_set_checksum(f1_hdr); - ip4_set_checksum(f2_hdr); - - return 0; -} - -// split packet to two tcp-on-ipv4 segments. -int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, - uint8_t *seg1, uint32_t *s1len, - uint8_t *seg2, uint32_t *s2len) { - - struct iphdr *hdr; - uint32_t hdr_len; - struct tcphdr *tcph; - uint32_t tcph_len; - uint32_t plen; - const uint8_t *payload; - int ret; - - if (!seg1 || !s1len || !seg2 || !s2len) - return -EINVAL; - - if ((ret = tcp4_payload_split((uint8_t *)pkt, buflen, - &hdr, &hdr_len, - &tcph, &tcph_len, - (uint8_t **)&payload, &plen)) < 0) { - lgerror("tcp4_frag: tcp4_payload_split", ret); - - return -EINVAL; - } - - - if ( - ntohs(hdr->frag_off) & IP_MF || - ntohs(hdr->frag_off) & IP_OFFMASK) { - printf("tcp4_frag: frag value: %d\n", - ntohs(hdr->frag_off)); - lgerror("tcp4_frag: ip fragmentation is set", -EINVAL); - return -EINVAL; - } - - - if (plen <= payload_offset) { - return -EINVAL; - } - - uint32_t s1_plen = payload_offset; - uint32_t s1_dlen = s1_plen + hdr_len + tcph_len; - - uint32_t s2_plen = plen - payload_offset; - uint32_t s2_dlen = s2_plen + hdr_len + tcph_len; - - if (*s1len < s1_dlen || *s2len < s2_dlen) - return -ENOMEM; - - *s1len = s1_dlen; - *s2len = s2_dlen; - - memcpy(seg1, hdr, hdr_len); - memcpy(seg2, hdr, hdr_len); - - memcpy(seg1 + hdr_len, tcph, tcph_len); - memcpy(seg2 + hdr_len, tcph, tcph_len); - - memcpy(seg1 + hdr_len + tcph_len, payload, s1_plen); - memcpy(seg2 + hdr_len + tcph_len, payload + payload_offset, s2_plen); - - struct iphdr *s1_hdr = (void *)seg1; - struct iphdr *s2_hdr = (void *)seg2; - - struct tcphdr *s1_tcph = (void *)(seg1 + hdr_len); - struct tcphdr *s2_tcph = (void *)(seg2 + hdr_len); - - s1_hdr->tot_len = htons(s1_dlen); - s2_hdr->tot_len = htons(s2_dlen); - - s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset); - - if (config.verbose) - printf("Packet split in portion %u %u\n", s1_plen, s2_plen); - - tcp4_set_checksum(s1_tcph, s1_hdr); - tcp4_set_checksum(s2_tcph, s2_hdr); - - return 0; -} #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 @@ -807,8 +643,13 @@ int fail4_packet(uint8_t *payload, uint32_t plen) { } if (config.faking_strategy == FAKE_STRAT_ACK_SEQ) { +#ifdef KERNEL_SCOPE + tcph->seq = 124; + tcph->ack_seq = 124; +#else tcph->seq = random(); tcph->ack_seq = random(); +#endif } else if (config.faking_strategy == FAKE_STRAT_TTL) { iph->ttl = config.faking_ttl; } diff --git a/mangle.h b/mangle.h index 7e98358..2196255 100644 --- a/mangle.h +++ b/mangle.h @@ -3,30 +3,6 @@ #include "types.h" -#ifdef KERNEL_SPACE -#include -#include -#include -#include -#include -#include - -#include - -/* from */ -#define IP_RF 0x8000 /* reserved fragment flag */ -#define IP_DF 0x4000 /* dont fragment flag */ -#define IP_MF 0x2000 /* more fragments flag */ -#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ -#else -#define USER_SPACE -#include -#include -#include -#include -#include -#endif - /** * Result of analyze_tls_data function */ @@ -42,42 +18,6 @@ struct tls_verdict { */ struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); -/** - * Splits the packet to two IP fragments on position payload_offset. - * payload_offset indicates the position relatively to start of IP payload - * (start of transport header) - */ -int ip4_frag(const uint8_t *pkt, uint32_t pktlen, - uint32_t payload_offset, - uint8_t *frag1, uint32_t *f1len, - uint8_t *frag2, uint32_t *f2len); - -/** - * Splits the packet to two TCP segments on position payload_offset - * payload_offset indicates the position relatively to start of TCP payload. - */ -int tcp4_frag(const uint8_t *pkt, uint32_t pktlen, - uint32_t payload_offset, - uint8_t *seg1, uint32_t *s1len, - uint8_t *seg2, uint32_t *s2len); - -/** - * Splits the raw packet payload to ip header and ip payload. - */ -int ip4_payload_split(uint8_t *pkt, uint32_t buflen, - struct iphdr **iph, uint32_t *iph_len, - uint8_t **payload, uint32_t *plen); - -/** - * Splits the raw packet payload to ip header, tcp header and tcp payload. - */ -int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, - struct iphdr **iph, uint32_t *iph_len, - struct tcphdr **tcph, uint32_t *tcph_len, - uint8_t **payload, uint32_t *plen); - -void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph); -void ip4_set_checksum(struct iphdr *iph); /** * Generates fake client hello message @@ -100,6 +40,20 @@ int fail4_packet(uint8_t *payload, uint32_t plen); */ int process_packet(const uint8_t *packet, uint32_t packet_len); + +/** + * Processe the TCP packet. + * Returns verdict. + */ +int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len); + + +/** + * Processes the UDP packet. + * Returns verdict. + */ +int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen); + /** * Sends fake client hello. */ diff --git a/quic.c b/quic.c index a2dc291..31ec7df 100644 --- a/quic.c +++ b/quic.c @@ -1,9 +1,6 @@ #include "quic.h" +#include "logging.h" -static const uint32_t supported_versions[] = { - 1, // version 1, RFC 9000 - 0x6b3343cf, // version 2, RFC 9369 -}; /** * Packet number. @@ -45,18 +42,24 @@ int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len, struct quic_lhdr *nqch = (struct quic_lhdr *)raw_payload; uint32_t left_len = raw_payload_len - sizeof(struct quic_lhdr); uint8_t *cur_rawptr = raw_payload + sizeof(struct quic_lhdr); - if (!nqch->fixed) + if (!nqch->fixed) { + lgtrace_addp("quic fixed uset"); return -EPROTO; + } uint8_t found = 0; for (uint8_t i = 0; i < sizeof(supported_versions); i++) { - if (nqch->version == supported_versions[i]) { + if (ntohl(nqch->version) == supported_versions[i]) { found = 1; } } - if (!found) + if (!found) { + lgtrace_addp("quic version undefined %d", ntohl(nqch->version)); return -EPROTO; + } + + lgtrace_addp("quic version valid %d", ntohl(nqch->version)); if (left_len < 2) goto invalid_packet; struct quic_cids nqci = {0}; diff --git a/quic.h b/quic.h index cf46df6..1edbfca 100644 --- a/quic.h +++ b/quic.h @@ -2,6 +2,7 @@ #define QUIC_H #include "types.h" + /** * @macro * @@ -22,38 +23,52 @@ "\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb" \ "\xf9\xbd\x2e\xd9" -#define QUIC_INITIAL_TYPE_V1 0x00 -#define QUIC_0_RTT_TYPE_V1 0x01 -#define QUIC_HANDSHAKE_TYPE_V1 0x02 -#define QUIC_RETRY_TYPE_V1 0x03 +#define QUIC_INITIAL_TYPE 0 +#define QUIC_0_RTT_TYPE 1 +#define QUIC_HANDSHAKE_TYPE 2 +#define QUIC_RETRY_TYPE 3 -#define QUIC_INITIAL_TYPE_V2 0b01 -#define QUIC_0_RTT_TYPE_V2 0b10 -#define QUIC_HANDSHAKE_TYPE_V2 0b11 -#define QUIC_RETRY_TYPE_V2 0b00 +#define QUIC_INITIAL_TYPE_V1 0b00 +#define QUIC_0_RTT_TYPE_V1 0b01 +#define QUIC_HANDSHAKE_TYPE_V1 0b10 +#define QUIC_RETRY_TYPE_V1 0b11 +#define quic_convtype_v1(type) (type) + +#define QUIC_INITIAL_TYPE_V2 0b01 +#define QUIC_0_RTT_TYPE_V2 0b10 +#define QUIC_HANDSHAKE_TYPE_V2 0b11 +#define QUIC_RETRY_TYPE_V2 0b00 +#define quic_convtype_v2(type) (((type) + 1) & 0b11) + +#define QUIC_V1 1 // RFC 9000 +#define QUIC_V2 0x6b3343cf // RFC 9369 + +static const uint32_t supported_versions[] = { + QUIC_V1, + QUIC_V2, +}; /** * Quic Large Header */ struct quic_lhdr { #if __BYTE_ORDER == __LITTLE_ENDIAN - uint8_t number_length: 2; - uint8_t reserved: 2; - uint8_t type: 2; - uint8_t fixed: 1; - uint8_t form: 1; + uint8_t number_length:2; + uint8_t reserved:2; + uint8_t type:2; + uint8_t fixed:1; + uint8_t form:1; #elif __BYTE_ORDER == __BIG_ENDIAN - uint8_t form: 1; - uint8_t fixed: 1; - uint8_t type: 2; - uint8_t reserved: 2; - uint8_t number_length: 2; + uint8_t form:1; + uint8_t fixed:1; + uint8_t type:2; + uint8_t reserved:2; + uint8_t number_length:2; #else #error "Undefined endian" #endif - uint32_t version; -}; +}__attribute__((packed)); /** * Quic Large Header Ids diff --git a/types.h b/types.h index 324afd7..07599fe 100644 --- a/types.h +++ b/types.h @@ -15,22 +15,33 @@ typedef __i8 int8_t; typedef __i16 int16_t; typedef __i32 int32_t; typedef __i64 int64_t; - -#include -#define printf pr_info -#define perror pr_err -#define lgerror(msg, ret) (pr_err(msg ": %d\n", ret)) - #else /* USERSPACE_SCOPE */ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export -#include // IWYU pragma: export -#define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);}) - - #endif /* SCOPES */ +// Network specific structures +#ifdef KERNEL_SPACE +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export + +/* from */ +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ +#else +#define USER_SPACE +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#endif + #endif /* TYPES_H */ diff --git a/uspace.mk b/uspace.mk index 5c8d062..971a54a 100644 --- a/uspace.mk +++ b/uspace.mk @@ -22,7 +22,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c mangle.c args.c +SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..dee16d9 --- /dev/null +++ b/utils.c @@ -0,0 +1,312 @@ +#include "utils.h" +#include "logging.h" + +#ifdef KERNEL_SPACE +#include +#else +#include +#include +#include +#endif + + +void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph) +{ +#ifdef KERNEL_SPACE + uint32_t tcp_packet_len = ntohs(iph->tot_len) - (iph->ihl << 2); + tcph->check = 0; + tcph->check = csum_tcpudp_magic( + iph->saddr, iph->daddr, tcp_packet_len, + IPPROTO_TCP, + csum_partial(tcph, tcp_packet_len, 0)); +#else + nfq_tcp_compute_checksum_ipv4(tcph, iph); +#endif +} + +void ip4_set_checksum(struct iphdr *iph) +{ +#ifdef KERNEL_SPACE + iph->check = 0; + iph->check = ip_fast_csum(iph, iph->ihl); +#else + nfq_ip_set_checksum(iph); +#endif +} + + +int ip4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + uint8_t **payload, uint32_t *plen) { + if (pkt == NULL || buflen < sizeof(struct iphdr)) { + lgerror("ip4_payload_split: pkt|buflen", -EINVAL); + return -EINVAL; + } + + struct iphdr *hdr = (struct iphdr *)pkt; + if (hdr->version != IPVERSION) { + lgerror("ip4_payload_split: ipversion", -EINVAL); + return -EINVAL; + } + + uint32_t hdr_len = hdr->ihl * 4; + uint32_t pktlen = ntohs(hdr->tot_len); + if (buflen < pktlen || hdr_len > pktlen) { + lgerror("ip4_payload_split: buflen cmp pktlen", -EINVAL); + return -EINVAL; + } + + if (iph) + *iph = hdr; + if (iph_len) + *iph_len = hdr_len; + if (payload) + *payload = pkt + hdr_len; + if (plen) + *plen = pktlen - hdr_len; + + return 0; +} + +int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + struct tcphdr **tcph, uint32_t *tcph_len, + uint8_t **payload, uint32_t *plen) { + struct iphdr *hdr; + uint32_t hdr_len; + struct tcphdr *thdr; + uint32_t thdr_len; + + uint8_t *tcph_pl; + uint32_t tcph_plen; + + if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len, + &tcph_pl, &tcph_plen)){ + return -EINVAL; + } + + + if ( + hdr->protocol != IPPROTO_TCP || + tcph_plen < sizeof(struct tcphdr)) { + return -EINVAL; + } + + + thdr = (struct tcphdr *)(tcph_pl); + thdr_len = thdr->doff * 4; + + if (thdr_len > tcph_plen) { + return -EINVAL; + } + + if (iph) *iph = hdr; + if (iph_len) *iph_len = hdr_len; + if (tcph) *tcph = thdr; + if (tcph_len) *tcph_len = thdr_len; + if (payload) *payload = tcph_pl + thdr_len; + if (plen) *plen = tcph_plen - thdr_len; + + return 0; +} + +int udp4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + struct udphdr **udph, + uint8_t **payload, uint32_t *plen) { + struct iphdr *hdr; + uint32_t hdr_len; + struct udphdr *uhdr; + uint32_t uhdr_len; + + uint8_t *ip_ph; + uint32_t ip_phlen; + + if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len, + &ip_ph, &ip_phlen)){ + return -EINVAL; + } + + + if ( + hdr->protocol != IPPROTO_UDP || + ip_phlen < sizeof(struct udphdr)) { + return -EINVAL; + } + + + uhdr = (struct udphdr *)(ip_ph); + if (uhdr->len != 0 && ntohs(uhdr->len) != ip_phlen) { + return -EINVAL; + } + + if (iph) *iph = hdr; + if (iph_len) *iph_len = hdr_len; + if (udph) *udph = uhdr; + if (payload) *payload = ip_ph + sizeof(struct udphdr); + if (plen) *plen = ip_phlen - sizeof(struct udphdr); + + return 0; +} + +// split packet to two ipv4 fragments. +int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, + uint8_t *frag1, uint32_t *f1len, + uint8_t *frag2, uint32_t *f2len) { + + struct iphdr *hdr; + const uint8_t *payload; + uint32_t plen; + uint32_t hdr_len; + int ret; + + if (!frag1 || !f1len || !frag2 || !f2len) + return -EINVAL; + + if ((ret = ip4_payload_split( + (uint8_t *)pkt, buflen, + &hdr, &hdr_len, (uint8_t **)&payload, &plen)) < 0) { + lgerror("ipv4_frag: TCP Header extract error", ret); + return -EINVAL; + } + + if (plen <= payload_offset) { + return -EINVAL; + } + + if (payload_offset & ((1 << 3) - 1)) { + lgerror("ipv4_frag: Payload offset MUST be a multiply of 8!", -EINVAL); + + return -EINVAL; + } + + uint32_t f1_plen = payload_offset; + uint32_t f1_dlen = f1_plen + hdr_len; + + uint32_t f2_plen = plen - payload_offset; + uint32_t f2_dlen = f2_plen + hdr_len; + + if (*f1len < f1_dlen || *f2len < f2_dlen) { + return -ENOMEM; + } + *f1len = f1_dlen; + *f2len = f2_dlen; + + memcpy(frag1, hdr, hdr_len); + memcpy(frag2, hdr, hdr_len); + + memcpy(frag1 + hdr_len, payload, f1_plen); + memcpy(frag2 + hdr_len, payload + payload_offset, f2_plen); + + struct iphdr *f1_hdr = (void *)frag1; + struct iphdr *f2_hdr = (void *)frag2; + + uint16_t f1_frag_off = ntohs(f1_hdr->frag_off); + uint16_t f2_frag_off = ntohs(f2_hdr->frag_off); + + f1_frag_off &= IP_OFFMASK; + f1_frag_off |= IP_MF; + + if ((f2_frag_off & ~IP_OFFMASK) == IP_MF) { + f2_frag_off &= IP_OFFMASK; + f2_frag_off |= IP_MF; + } else { + f2_frag_off &= IP_OFFMASK; + } + + f2_frag_off += (uint16_t)payload_offset / 8; + + f1_hdr->frag_off = htons(f1_frag_off); + f1_hdr->tot_len = htons(f1_dlen); + + f2_hdr->frag_off = htons(f2_frag_off); + f2_hdr->tot_len = htons(f2_dlen); + + + lgdebugmsg("Packet split in portion %u %u", f1_plen, f2_plen); + + ip4_set_checksum(f1_hdr); + ip4_set_checksum(f2_hdr); + + return 0; +} + +// split packet to two tcp-on-ipv4 segments. +int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, + uint8_t *seg1, uint32_t *s1len, + uint8_t *seg2, uint32_t *s2len) { + + struct iphdr *hdr; + uint32_t hdr_len; + struct tcphdr *tcph; + uint32_t tcph_len; + uint32_t plen; + const uint8_t *payload; + int ret; + + if (!seg1 || !s1len || !seg2 || !s2len) + return -EINVAL; + + if ((ret = tcp4_payload_split((uint8_t *)pkt, buflen, + &hdr, &hdr_len, + &tcph, &tcph_len, + (uint8_t **)&payload, &plen)) < 0) { + lgerror("tcp4_frag: tcp4_payload_split", ret); + + return -EINVAL; + } + + + if ( + ntohs(hdr->frag_off) & IP_MF || + ntohs(hdr->frag_off) & IP_OFFMASK) { + lgdebugmsg("tcp4_frag: frag value: %d", + ntohs(hdr->frag_off)); + lgerror("tcp4_frag: ip fragmentation is set", -EINVAL); + return -EINVAL; + } + + + if (plen <= payload_offset) { + return -EINVAL; + } + + uint32_t s1_plen = payload_offset; + uint32_t s1_dlen = s1_plen + hdr_len + tcph_len; + + uint32_t s2_plen = plen - payload_offset; + uint32_t s2_dlen = s2_plen + hdr_len + tcph_len; + + if (*s1len < s1_dlen || *s2len < s2_dlen) + return -ENOMEM; + + *s1len = s1_dlen; + *s2len = s2_dlen; + + memcpy(seg1, hdr, hdr_len); + memcpy(seg2, hdr, hdr_len); + + memcpy(seg1 + hdr_len, tcph, tcph_len); + memcpy(seg2 + hdr_len, tcph, tcph_len); + + memcpy(seg1 + hdr_len + tcph_len, payload, s1_plen); + memcpy(seg2 + hdr_len + tcph_len, payload + payload_offset, s2_plen); + + struct iphdr *s1_hdr = (void *)seg1; + struct iphdr *s2_hdr = (void *)seg2; + + struct tcphdr *s1_tcph = (void *)(seg1 + hdr_len); + struct tcphdr *s2_tcph = (void *)(seg2 + hdr_len); + + s1_hdr->tot_len = htons(s1_dlen); + s2_hdr->tot_len = htons(s2_dlen); + + s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset); + + lgdebugmsg("Packet split in portion %u %u", s1_plen, s2_plen); + + tcp4_set_checksum(s1_tcph, s1_hdr); + tcp4_set_checksum(s2_tcph, s2_hdr); + + return 0; +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..e0f512e --- /dev/null +++ b/utils.h @@ -0,0 +1,51 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "types.h" + +/** + * Splits the packet to two IP fragments on position payload_offset. + * payload_offset indicates the position relatively to start of IP payload + * (start of transport header) + */ +int ip4_frag(const uint8_t *pkt, uint32_t pktlen, + uint32_t payload_offset, + uint8_t *frag1, uint32_t *f1len, + uint8_t *frag2, uint32_t *f2len); + +/** + * Splits the packet to two TCP segments on position payload_offset + * payload_offset indicates the position relatively to start of TCP payload. + */ +int tcp4_frag(const uint8_t *pkt, uint32_t pktlen, + uint32_t payload_offset, + uint8_t *seg1, uint32_t *s1len, + uint8_t *seg2, uint32_t *s2len); + +/** + * Splits the raw packet payload to ip header and ip payload. + */ +int ip4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + uint8_t **payload, uint32_t *plen); + +/** + * Splits the raw packet payload to ip header, tcp header and tcp payload. + */ +int tcp4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + struct tcphdr **tcph, uint32_t *tcph_len, + uint8_t **payload, uint32_t *plen); + +/** + * Splits the raw packet payload to ip header, udp header and udp payload. + */ +int udp4_payload_split(uint8_t *pkt, uint32_t buflen, + struct iphdr **iph, uint32_t *iph_len, + struct udphdr **udph, + uint8_t **payload, uint32_t *plen); + +void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph); +void ip4_set_checksum(struct iphdr *iph); + +#endif /* UTILS_H */ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 1b72351..f526acf 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -31,6 +31,7 @@ #include "config.h" #include "mangle.h" #include "args.h" +#include "utils.h" pthread_mutex_t rawsocket_lock; int rawsocket = -2; From 727e909db1ff0563113a8b0660ffcba47faf2879 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 15 Aug 2024 01:50:12 +0300 Subject: [PATCH 06/13] Add documentation for QUIC --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.md b/README.md index 8aacda7..bc18c7a 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ - [Check it](#check-it) - [Flags](#flags) - [Troubleshooting](#troubleshooting) + - [TV](#tv) - [Troubleshooting EPERMS (Operation not permitted)](#troubleshooting-eperms-operation-not-permitted) - [How it works:](#how-it-works) - [How it processes packets](#how-it-processes-packets) @@ -137,12 +138,16 @@ Available flags: - `--frag-sni-faked={0|1}` Specifies **youtubeUnblock** to send fake packets near *ClientHello* (fills payload with zeroes). Defaults to **0**. +- `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Suitable for some TVs. + - `--fk-winsize=` Specifies window size for the fragmented TCP packet. Applicable if you want for response to be fragmented. May slowdown connection initialization. - `--seg2delay=` This flag forces **youtubeUnblock** to wait a little bit before send the 2nd part of the split packet. - `--silent` Disables verbose mode. +- `--trace` Maximum verbosity for debugging purposes. + - `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything. - `--threads=` Specifies the amount of threads you want to be running for your program. This defaults to **1** and shouldn't be edited for normal use. If you have performance issues, consult [performance chaptr](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#performance) @@ -155,6 +160,22 @@ If you are on Chromium you may have to disable *kyber* (the feature that makes t If your browser is using QUIC it may not work properly. Disable it in Chrome in `chrome://flags` and in Firefox `network.http.http{2,3}.enable(d)` in `about:config` option. +### TV + +Televisions are the biggest headache. Some users report that disabling QUIC + `--sni-domains=all` may work. To disable QUIC you may use `--quic-drop` [flag](#flags) with proper firewall configuration (check description of the flag). Note, that this flag won't disable gQUIC and some TVs may relay on it. To disable gQUIC you will need to block the entire 443 port for udp in firewall configuration: + +For **nftables** do +``` +nft insert rule inet fw4 forward udp dport 443 counter drop +``` + +For **iptables** +``` +iptables -I OUTPUT -p udp --dport 443 -j DROP +``` + +Note that these rules may **break the stability of internet** so use them carefully and **only if** --quic-drop doesn't work. + ### Troubleshooting EPERMS (Operation not permitted) *EPERM* may occur in a lot of places but generally here are two: *mnl_cb_run* and when sending the packet via *rawsocket* (raw_frags_send and send fake sni). From 044801efb95acdd00f17d8ef0707fee64eb5b3c4 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 15 Aug 2024 02:31:48 +0300 Subject: [PATCH 07/13] Add support for bruteforce mode of parsing SNI from Client Hello. --- README.md | 2 ++ args.c | 19 +++++++++++++++ config.h | 5 +++- mangle.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- types.h | 14 +++++++++++ 5 files changed, 109 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bc18c7a..184415d 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,8 @@ Available flags: - `--fk-winsize=` Specifies window size for the fragmented TCP packet. Applicable if you want for response to be fragmented. May slowdown connection initialization. +- `--sni-detection={parse|brute}` Specifies how to detect SNI. Parse will normally detect it by parsing the Client Hello message. Brute will go through the entire message and check possibility of SNI occurrence. Please note, that when `--sni-domains` option is not all brute will be O(nm) time complexity where n stands for length of the message and m is number of domains. Defaults to parse. + - `--seg2delay=` This flag forces **youtubeUnblock** to wait a little bit before send the 2nd part of the split packet. - `--silent` Disables verbose mode. diff --git a/args.c b/args.c index 46677e6..a723ae7 100644 --- a/args.c +++ b/args.c @@ -19,6 +19,8 @@ struct config_t config = { .fake_sni = 1, .fake_sni_seq_len = 1, + .sni_detection = SNI_DETECTION_PARSE, + #ifdef SEG2_DELAY .seg2_delay = SEG2_DELAY, #else @@ -56,6 +58,7 @@ struct config_t config = { #define OPT_FK_WINSIZE 14 #define OPT_TRACE 15 #define OPT_QUIC_DROP 16 +#define OPT_SNI_DETECTION 17 #define OPT_SEG2DELAY 5 #define OPT_THREADS 6 #define OPT_SILENT 7 @@ -77,6 +80,7 @@ static struct option long_opt[] = { {"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED}, {"fk-winsize", 1, 0, OPT_FK_WINSIZE}, {"quic-drop", 0, 0, OPT_QUIC_DROP}, + {"sni-detection", 1, 0, OPT_SNI_DETECTION}, {"seg2delay", 1, 0, OPT_SEG2DELAY}, {"threads", 1, 0, OPT_THREADS}, {"silent", 0, 0, OPT_SILENT}, @@ -126,6 +130,7 @@ void print_usage(const char *argv0) { printf("\t--frag-sni-faked={0|1}\n"); printf("\t--fk-winsize=\n"); printf("\t--quic-drop\n"); + printf("\t--sni-detection={parse|brute}\n"); printf("\t--seg2delay=\n"); printf("\t--threads=\n"); printf("\t--silent\n"); @@ -166,6 +171,16 @@ int parse_args(int argc, char *argv[]) { config.domains_str = optarg; config.domains_strlen = strlen(config.domains_str); + break; + case OPT_SNI_DETECTION: + if (strcmp(optarg, "parse") == 0) { + config.sni_detection = SNI_DETECTION_PARSE; + } else if (strcmp(optarg, "brute") == 0) { + config.sni_detection = SNI_DETECTION_BRUTE; + } else { + goto invalid_opt; + } + break; case OPT_FRAG: if (strcmp(optarg, "tcp") == 0) { @@ -346,6 +361,10 @@ void print_welcome() { printf("All QUIC packets will be dropped\n"); } + if (config.sni_detection == SNI_DETECTION_BRUTE) { + printf("Server Name Extension will be parsed in the bruteforce mode\n"); + } + if (config.all_domains) { printf("All Client Hello will be targetted by youtubeUnblock!\n"); } diff --git a/config.h b/config.h index 2769176..98ca573 100644 --- a/config.h +++ b/config.h @@ -30,6 +30,9 @@ struct config_t { #define VERBOSE_TRACE 2 int verbose; int quic_drop; +#define SNI_DETECTION_PARSE 0 +#define SNI_DETECTION_BRUTE 1 + int sni_detection; /* In milliseconds */ unsigned int seg2_delay; const char *domains_str; @@ -89,7 +92,7 @@ extern struct config_t config; // The Maximum Transmission Unit size for rawsocket // Larger packets will be fragmented. Applicable for Chrome's kyber. -#define AVAILABLE_MTU 1384 +#define AVAILABLE_MTU 1500 #define DEFAULT_QUEUE_NUM 537 diff --git a/mangle.c b/mangle.c index 0d7c60d..5f50bf9 100644 --- a/mangle.c +++ b/mangle.c @@ -428,7 +428,25 @@ int post_fake_sni(const struct iphdr *iph, unsigned int iph_len, return 0; } +void z_function(const char *str, int *zbuf, size_t len) { + zbuf[0] = len; + ssize_t lh = 0, rh = 1; + for (ssize_t i = 1; i < len; i++) { + zbuf[i] = 0; + if (i < rh) { + zbuf[i] = min(zbuf[i - lh], rh - i); + } + + while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]]) + zbuf[i]++; + + if (i + zbuf[i] > rh) { + lh = i; + rh = i + zbuf[i]; + } + } +} #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 @@ -463,12 +481,16 @@ struct tls_verdict analyze_tls_data( uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3)); const uint8_t *message_length_ptr = msgData + 3; + if (tls_vmajor != 0x03) goto nextMessage; - if (i + 5 + message_length > dlen) break; + if (i + 5 > dlen) break; if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) goto nextMessage; + if (config.sni_detection == SNI_DETECTION_BRUTE) { + goto brute; + } const uint8_t *handshakeProto = msgData + 5; @@ -506,7 +528,7 @@ struct tls_verdict analyze_tls_data( const uint8_t *extensionsPtr = msgPtr; const uint8_t *extensions_end = extensionsPtr + extensionsLen; - if (extensions_end > data_end) break; + if (extensions_end > data_end) extensions_end = data_end; while (extensionsPtr < extensions_end) { const uint8_t *extensionPtr = extensionsPtr; @@ -590,8 +612,54 @@ nextMessage: out: return vrd; + +brute: + if (config.all_domains) { + vrd.target_sni = 1; + vrd.sni_len = 0; + vrd.sni_offset = dlen / 2; + goto out; + } + + unsigned int j = 0; + for (unsigned int i = 0; i <= config.domains_strlen; i++) { + if ( i > j && + (i == config.domains_strlen || + config.domains_str[i] == '\0' || + config.domains_str[i] == ',' || + config.domains_str[i] == '\n' )) { + + uint8_t buf[MAX_PACKET_SIZE]; + int zbuf[MAX_PACKET_SIZE]; + unsigned int domain_len = (i - j); + const char *domain_startp = config.domains_str + j; + + if (domain_len + dlen + 1> MAX_PACKET_SIZE) continue; + + memcpy(buf, domain_startp, domain_len); + memcpy(buf + domain_len, "#", 1); + memcpy(buf + domain_len + 1, data, dlen); + + z_function((char *)buf, zbuf, domain_len + 1 + dlen); + + for (unsigned int k = 0; k < dlen; k++) { + if (zbuf[k] == domain_len) { + vrd.target_sni = 1; + vrd.sni_len = domain_len; + vrd.sni_offset = (k - domain_len - 1); + goto out; + } + } + + + j = i + 1; + } + } + + goto out; } + int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph, uint8_t *buf, uint32_t *buflen) { diff --git a/types.h b/types.h index 07599fe..edf9505 100644 --- a/types.h +++ b/types.h @@ -44,4 +44,18 @@ typedef __i64 int64_t; #include // IWYU pragma: export #endif +#define max(a,b)__extension__\ +({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; \ +}) + +#define min(a,b)__extension__\ +({ \ + __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a < _b ? _a : _b; \ +}) + #endif /* TYPES_H */ From 51c21a89fd52c80c11aa35e6aaed85bb188f2e90 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 15 Aug 2024 02:51:40 +0300 Subject: [PATCH 08/13] Fix endian source --- types.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types.h b/types.h index edf9505..8978cf6 100644 --- a/types.h +++ b/types.h @@ -1,6 +1,7 @@ +#define _GNU_SOURCE #ifndef TYPES_H #define TYPES_H -#include +#include #ifdef KERNEL_SCOPE #include // IWYU pragma: export From 1c5d4e68d93e64ae8309ada7828a9a2dadb06c2d Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 16 Aug 2024 22:23:55 +0300 Subject: [PATCH 09/13] Add few logs, minor improvements --- youtubeUnblock.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/youtubeUnblock.c b/youtubeUnblock.c index f526acf..3c63db8 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -32,6 +32,7 @@ #include "mangle.h" #include "args.h" #include "utils.h" +#include "logging.h" pthread_mutex_t rawsocket_lock; int rawsocket = -2; @@ -193,13 +194,15 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { } }; - pthread_mutex_lock(&rawsocket_lock); + if (config.threads != 1) + pthread_mutex_lock(&rawsocket_lock); int sent = sendto(rawsocket, pkt, pktlen, 0, (struct sockaddr *)&daddr, sizeof(daddr)); - pthread_mutex_unlock(&rawsocket_lock); + if (config.threads != 1) + pthread_mutex_unlock(&rawsocket_lock); /* The function will return -errno on error as well as errno value set itself */ if (sent < 0) sent = -errno; @@ -278,9 +281,6 @@ void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigne pthread_detach(thr); } - - - static int queue_cb(const struct nlmsghdr *nlh, void *data) { char buf[MNL_SOCKET_BUFFER_SIZE]; @@ -400,12 +400,18 @@ int init_queue(int queue_num) { ret = mnl_socket_recvfrom(nl, buf, BUF_SIZE); if (ret == -1) { perror("mnl_socket_recvfrom"); - continue; + goto die; } ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, &qdata); if (ret < 0) { - perror("mnl_cb_run"); + lgerror("mnl_cb_run", -EPERM); + if (errno == EPERM) { + printf("Probably another instance of youtubeUnblock with the same queue number is running\n"); + } else { + printf("Make sure the nfnetlink_queue kernel module is loaded\n"); + } + goto die; } } @@ -502,6 +508,6 @@ int main(int argc, char *argv[]) { exit(EXIT_FAILURE); } - return qres->status; + return -qres->status; } From a546e783c67f02f678e1ed3655c4a64ff6bf774f Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 16 Aug 2024 22:47:55 +0300 Subject: [PATCH 10/13] Add support for tcp_check and past sequence faking strategies --- README.md | 20 ++++++++++++++------ args.c | 20 +++++++++++++++----- config.h | 8 +++++--- mangle.c | 8 +++++++- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 59273e5..5df0dfa 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,11 @@ Available flags: - `--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**. -- `--faking-strategy={ack,ttl}` This flag determines the strategy of fake packets invalidation. `ack` specifies that random sequence/acknowledgemend random will be set. These options may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled. `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured. Defaults to `ack` +- `--faking-strategy={randseq|ttl|tcp_check|pastseq}` This flag determines the strategy of fake packets invalidation. Defaults to `randseq` + - `randseq` specifies that random sequence/acknowledgemend random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled. + - `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured. + - `pastseq` is like `randseq` but sequence number is not random but references the packet sent in the past (before current). + - `tcp_check` will invalidate faking packet with invalid checksum. May be handled and dropped by some providers/TSPUs. - `--faking-ttl=` Tunes the time to live (TTL) of fake SNI messages. TTL is specified like that the packet will go through the DPI system and captured by it, but will not reach the destination server. Defaults to **8**. @@ -146,7 +150,7 @@ Available flags: - `--frag-sni-faked={0|1}` Specifies **youtubeUnblock** to send fake packets near *ClientHello* (fills payload with zeroes). Defaults to **0**. -- `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Suitable for some TVs. +- `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Suitable for some TVs. Note, that for this option to work you should also add proxy udp to youtubeUnblock in firewall. `connbytes` may also be used with udp. - `--fk-winsize=` Specifies window size for the fragmented TCP packet. Applicable if you want for response to be fragmented. May slowdown connection initialization. @@ -172,19 +176,23 @@ If your browser is using QUIC it may not work properly. Disable it in Chrome in ### TV -Televisions are the biggest headache. Some users report that disabling QUIC + `--sni-domains=all` may work. To disable QUIC you may use `--quic-drop` [flag](#flags) with proper firewall configuration (check description of the flag). Note, that this flag won't disable gQUIC and some TVs may relay on it. To disable gQUIC you will need to block the entire 443 port for udp in firewall configuration: +Televisions are the biggest headache. + +In [this issue](https://github.com/Waujito/youtubeUnblock/issues/59) the problem has been resolved. + +If you have troubles with televisions try `--faking-strategy=ttl` flag and play around with `--faking-ttl=n`. See [#flags](#flags) for more details. Also you might be have to disable QUIC. To do it you may use `--quic-drop` [flag](#flags) with proper firewall configuration (check description of the flag). Note, that this flag won't disable gQUIC and some TVs may relay on it. To disable gQUIC you will need to block the entire 443 port for udp in firewall configuration: For **nftables** do ``` -nft insert rule inet fw4 forward udp dport 443 counter drop +nft insert rule inet fw4 forward ip saddr 192.168.. udp dport 443 counter drop ``` For **iptables** ``` -iptables -I OUTPUT -p udp --dport 443 -j DROP +iptables -I OUTPUT --src 192.168.. -p udp --dport 443 -j DROP ``` -Note that these rules may **break the stability of internet** so use them carefully and **only if** --quic-drop doesn't work. +Where you have to replace 192.168.. with ip of your television. ### Troubleshooting EPERMS (Operation not permitted) diff --git a/args.c b/args.c index a723ae7..9876cae 100644 --- a/args.c +++ b/args.c @@ -124,7 +124,7 @@ void print_usage(const char *argv0) { printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni-seq-len=\n"); printf("\t--faking-ttl=\n"); - printf("\t--faking-strategy={ack,ttl}\n"); + printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq}\n"); printf("\t--frag={tcp,ip,none}\n"); printf("\t--frag-sni-reverse={0|1}\n"); printf("\t--frag-sni-faked={0|1}\n"); @@ -215,10 +215,14 @@ int parse_args(int argc, char *argv[]) { break; case OPT_FAKING_STRATEGY: - if (strcmp(optarg, "ack") == 0) { - config.faking_strategy = FAKE_STRAT_ACK_SEQ; + if (strcmp(optarg, "randseq") == 0) { + config.faking_strategy = FAKE_STRAT_RAND_SEQ; } else if (strcmp(optarg, "ttl") == 0) { config.faking_strategy = FAKE_STRAT_TTL; + } else if (strcmp(optarg, "tcp_check") == 0) { + config.faking_strategy = FAKE_STRAT_TCP_CHECK; + } else if (strcmp(optarg, "pastseq") == 0) { + config.faking_strategy = FAKE_STRAT_PAST_SEQ; } else { goto invalid_opt; } @@ -343,8 +347,14 @@ void print_welcome() { case FAKE_STRAT_TTL: printf("TTL faking strategy will be used with TTL %d\n", config.faking_ttl); break; - case FAKE_STRAT_ACK_SEQ: - printf("Ack-Seq faking strategy will be used\n"); + case FAKE_STRAT_RAND_SEQ: + printf("Random seq faking strategy will be used\n"); + break; + case FAKE_STRAT_TCP_CHECK: + printf("TCP checksum faking strategy will be used\n"); + break; + case FAKE_STRAT_PAST_SEQ: + printf("Past seq faking strategy will be used\n"); break; } diff --git a/config.h b/config.h index 98ca573..b3e6a42 100644 --- a/config.h +++ b/config.h @@ -76,14 +76,16 @@ extern struct config_t config; #define FAKE_TTL 8 // Will invalidate fake packets by out-of-ack_seq out-of-seq request -#define FAKE_STRAT_ACK_SEQ 1 +#define FAKE_STRAT_RAND_SEQ 1 // Will assume that GGC server is located further than FAKE_TTL // Thus, Fake packet will be eliminated automatically. -#define FAKE_STRAT_TTL 2 +#define FAKE_STRAT_TTL 2 +#define FAKE_STRAT_PAST_SEQ 3 +#define FAKE_STRAT_TCP_CHECK 4 #ifndef FAKING_STRATEGY -#define FAKING_STRATEGY FAKE_STRAT_ACK_SEQ +#define FAKING_STRATEGY FAKE_STRAT_RAND_SEQ #endif #if !defined(SILENT) && !defined(KERNEL_SPACE) diff --git a/mangle.c b/mangle.c index 5f50bf9..bac5561 100644 --- a/mangle.c +++ b/mangle.c @@ -710,7 +710,7 @@ int fail4_packet(uint8_t *payload, uint32_t plen) { return ret; } - if (config.faking_strategy == FAKE_STRAT_ACK_SEQ) { + if (config.faking_strategy == FAKE_STRAT_RAND_SEQ) { #ifdef KERNEL_SCOPE tcph->seq = 124; tcph->ack_seq = 124; @@ -718,6 +718,8 @@ int fail4_packet(uint8_t *payload, uint32_t plen) { tcph->seq = random(); tcph->ack_seq = random(); #endif + } else if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { + tcph->seq = htonl(ntohl(tcph->seq) - dlen); } else if (config.faking_strategy == FAKE_STRAT_TTL) { iph->ttl = config.faking_ttl; } @@ -725,5 +727,9 @@ int fail4_packet(uint8_t *payload, uint32_t plen) { ip4_set_checksum(iph); tcp4_set_checksum(tcph, iph); + if (config.faking_strategy == FAKE_STRAT_TCP_CHECK) { + tcph->check += 1; + } + return 0; } From 6cf2ec5504d08b7e83bd977a6e02d16dffbc3372 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 16 Aug 2024 22:55:59 +0300 Subject: [PATCH 11/13] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5df0dfa..8472cba 100644 --- a/README.md +++ b/README.md @@ -168,9 +168,10 @@ Available flags: ## Troubleshooting -If you have troubles with some sites being proxied, you can play with flags values. For example, for someone `--fake-sni=ttl` works. You should specify proper `--fake-sni-ttl=` where ttl is the amount of hops between you and DPI. +If you got troubles with some sites and you sure that they are blocked by SNI (youtube for example), use may play around with [flags](#flags) and their combinations. At first it is recommended to try `--faking-strategy` flag and `--frag-sni-faked=1`. +If you have troubles with some sites being proxied, you can play with flags values. For example, for someone `--faking-strategy=ttl` works. You should specify proper `--fake-sni-ttl=` where ttl is the amount of hops between you and DPI. -If you are on Chromium you may have to disable *kyber* (the feature that makes the TLS *ClientHello* very big). I've got the problem with it on router, so to escape possible errors, so it is better to disable it: in `chrome://flags` search for kyber and switch it to disabled state. +If you are on Chromium you may have to disable *kyber* (the feature that makes the TLS *ClientHello* very big). I've got the problem with it on router, so to escape possible errors, so it is better to disable it: in `chrome://flags` search for kyber and switch it to disabled state. Alternatively you may set `--sni-detection=brute` and probably adjust `--sni-domains` flag. If your browser is using QUIC it may not work properly. Disable it in Chrome in `chrome://flags` and in Firefox `network.http.http{2,3}.enable(d)` in `about:config` option. From b434ef4b7f6fcd63e7443572e1fa2b822d88706f Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 17 Aug 2024 12:51:53 +0300 Subject: [PATCH 12/13] Add compatibility with v0.2.2 --- README.md | 4 ++++ args.c | 32 +++++++++++++++++++++++++++++--- config.h | 2 ++ mangle.c | 41 +++++++++++++++++++++++++++++++++++++---- raw_replacements.h | 2 ++ 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 8472cba..7753890 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,10 @@ Available flags: - `--frag-sni-faked={0|1}` Specifies **youtubeUnblock** to send fake packets near *ClientHello* (fills payload with zeroes). Defaults to **0**. +- `--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 2. + - `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Suitable for some TVs. Note, that for this option to work you should also add proxy udp to youtubeUnblock in firewall. `connbytes` may also be used with udp. - `--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/args.c b/args.c index 9876cae..b69e0ce 100644 --- a/args.c +++ b/args.c @@ -18,6 +18,8 @@ struct config_t config = { .faking_ttl = FAKE_TTL, .fake_sni = 1, .fake_sni_seq_len = 1, + .frag_middle_sni = 1, + .frag_sni_pos = 2, .sni_detection = SNI_DETECTION_PARSE, @@ -43,8 +45,8 @@ struct config_t config = { .domains_strlen = sizeof(defaul_snistr), .queue_start_num = DEFAULT_QUEUE_NUM, - .fake_sni_pkt = fake_sni, - .fake_sni_pkt_sz = sizeof(fake_sni) - 1, // - 1 for null-terminator + .fake_sni_pkt = fake_sni_old, + .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator }; #define OPT_SNI_DOMAINS 1 @@ -55,6 +57,8 @@ struct config_t config = { #define OPT_FRAG 4 #define OPT_FRAG_SNI_REVERSE 12 #define OPT_FRAG_SNI_FAKED 13 +#define OPT_FRAG_MIDDLE_SNI 18 +#define OPT_FRAG_SNI_POS 19 #define OPT_FK_WINSIZE 14 #define OPT_TRACE 15 #define OPT_QUIC_DROP 16 @@ -65,7 +69,7 @@ struct config_t config = { #define OPT_NO_GSO 8 #define OPT_QUEUE_NUM 9 -#define OPT_MAX OPT_QUIC_DROP +#define OPT_MAX OPT_FRAG_SNI_POS static struct option long_opt[] = { {"help", 0, 0, 'h'}, @@ -78,6 +82,8 @@ static struct option long_opt[] = { {"frag", 1, 0, OPT_FRAG}, {"frag-sni-reverse", 1, 0, OPT_FRAG_SNI_REVERSE}, {"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}, {"fk-winsize", 1, 0, OPT_FK_WINSIZE}, {"quic-drop", 0, 0, OPT_QUIC_DROP}, {"sni-detection", 1, 0, OPT_SNI_DETECTION}, @@ -128,6 +134,8 @@ void print_usage(const char *argv0) { printf("\t--frag={tcp,ip,none}\n"); printf("\t--frag-sni-reverse={0|1}\n"); printf("\t--frag-sni-faked={0|1}\n"); + printf("\t--frag-middle-sni={0|1}\n"); + printf("\t--frag-sni-pos=\n"); printf("\t--fk-winsize=\n"); printf("\t--quic-drop\n"); printf("\t--sni-detection={parse|brute}\n"); @@ -213,6 +221,24 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; } + break; + case OPT_FRAG_MIDDLE_SNI: + if (strcmp(optarg, "1") == 0) { + config.frag_middle_sni = 1; + } else if (strcmp(optarg, "0") == 0) { + config.frag_middle_sni = 0; + } else { + goto invalid_opt; + } + + break; + case OPT_FRAG_SNI_POS: + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0) { + goto invalid_opt; + } + + config.frag_sni_pos = num; break; case OPT_FAKING_STRATEGY: if (strcmp(optarg, "randseq") == 0) { diff --git a/config.h b/config.h index b3e6a42..beb08f4 100644 --- a/config.h +++ b/config.h @@ -22,6 +22,8 @@ struct config_t { int frag_sni_reverse; int frag_sni_faked; int faking_strategy; + int frag_middle_sni; + int frag_sni_pos; unsigned char faking_ttl; int fake_sni; unsigned int fake_sni_seq_len; diff --git a/mangle.c b/mangle.c index bac5561..bfb1932 100644 --- a/mangle.c +++ b/mangle.c @@ -106,9 +106,24 @@ int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { ipd_offset = vrd.sni_offset; mid_offset = ipd_offset + vrd.sni_len / 2; - uint32_t poses[] = { 2, mid_offset }; + uint32_t poses[2]; + int cnt = 0; - ret = send_tcp4_frags(payload, payload_len, poses, 2, 0); + if (config.frag_sni_pos && dlen > config.frag_sni_pos) { + poses[cnt++] = config.frag_sni_pos; + } + + if (config.frag_middle_sni) { + poses[cnt++] = mid_offset; + } + + if (cnt > 1 && poses[0] > poses[1]) { + uint32_t tmp = poses[0]; + poses[0] = poses[1]; + poses[1] = tmp; + } + + ret = send_tcp4_frags(payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("tcp4 send frags", ret); goto accept; @@ -122,8 +137,26 @@ int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { mid_offset = ipd_offset + vrd.sni_len / 2; mid_offset += 8 - mid_offset % 8; - uint32_t poses[] = { mid_offset }; - ret = send_ip4_frags(payload, payload_len, poses, 1, 0); + uint32_t poses[2]; + int cnt = 0; + + if (config.frag_sni_pos && dlen > config.frag_sni_pos) { + poses[cnt] = config.frag_sni_pos + ((char *)data - (char *)tcph); + poses[cnt] += 8 - poses[cnt] % 8; + cnt++; + } + + if (config.frag_middle_sni) { + poses[cnt++] = mid_offset; + } + + if (cnt > 1 && poses[0] > poses[1]) { + uint32_t tmp = poses[0]; + poses[0] = poses[1]; + poses[1] = tmp; + } + + ret = send_ip4_frags(payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("ip4 send frags", ret); goto accept; diff --git a/raw_replacements.h b/raw_replacements.h index 5bc5a08..4123bd8 100644 --- a/raw_replacements.h +++ b/raw_replacements.h @@ -5,4 +5,6 @@ static const char fake_sni[] = "\026\003\001\002\000\001\000\001\374\003\003\323[\345\201f\362\200:B\356Uq\355X\315i\235*\021\367\331\272\a>\233\254\355\307/\342\372\265 \275\2459l&r\222\313\361\3729`\376\256\233\333O\001\373\33050\r\260f,\231\035 \324^\000>\023\002\023\003\023\001\300,\3000\000\237\314\251\314\250\314\252\300+\300/\000\236\300$\300(\000k\300#\300'\000g\300\n\300\024\0009\300\t\300\023\0003\000\235\000\234\000=\000<\0005\000/\000\377\001\000\001u\000\000\000\023\000\021\000\000\016www.google.com\000\v\000\004\003\000\001\002\000\n\000\026\000\024\000\035\000\027\000\036\000\031\000\030\001\000\001\001\001\002\001\003\001\004\000\020\000\016\000\f\002h2\bhttp/1.1\000\026\000\000\000\027\000\000\0001\000\000\000\r\0000\000.\004\003\005\003\006\003\b\a\b\b\b\032\b\033\b\034\b\t\b\n\b\v\b\004\b\005\b\006\004\001\005\001\006\001\003\003\003\001\003\002\004\002\005\002\006\002\000+\000\005\004\003\004\003\003\000-\000\002\001\001\0003\000&\000$\000\035\000 \004\224\206\021\256\f\222\266\3435\216\202\342\2573\341\3503\2107\341\023\016\240r|6\000^K\310s\000\025\000\255\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000"; +static const char fake_sni_old[] = "\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356"; + #endif /*RAW_REPLACEMENTS_H*/ From e8d86b9df6899cdc55f86d3f73cbd757a27adc81 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 18 Aug 2024 17:19:03 +0300 Subject: [PATCH 13/13] Do not delete all libraries on every clean --- Makefile | 9 +++++++-- uspace.mk | 23 ++++++++++++++++------- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 0e05091..010bcdb 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,11 @@ $(KMAKE_TARGETS): @$(MAKE) -f kmake.mk $@ clean: - -@$(MAKE) -f kmake.mk kclean - @$(MAKE) -f uspace.mk clean + -@$(MAKE) -f uspace.mk clean +distclean: clean + -@$(MAKE) -f uspace.mk distclean + + +kclean: + -@$(MAKE) -f kmake.mk kclean diff --git a/uspace.mk b/uspace.mk index 971a54a..127f6f1 100644 --- a/uspace.mk +++ b/uspace.mk @@ -25,10 +25,10 @@ APP:=$(BUILD_DIR)/youtubeUnblock SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) -LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a -LIBMNL := $(DEPSDIR)/lib/libmnl.a -LIBNETFILTER_QUEUE := $(DEPSDIR)/lib/libnetfilter_queue.a - +LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la +LIBMNL := $(DEPSDIR)/lib/libmnl.la +LIBNETFILTER_QUEUE := $(DEPSDIR)/lib/libnetfilter_queue.la +#LIBCRYPTO := $(DEPSDIR)/lib64/libcrypto.a .PHONY: default all dev dev_attrs prepare_dirs default: all @@ -47,6 +47,11 @@ prepare_dirs: mkdir -p $(BUILD_DIR) mkdir -p $(DEPSDIR) +$(LIBCRYPTO): + cd deps/openssl && ./Configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--cross-compile-prefix=$(CROSS_COMPILE_PLATFORM)-,) --no-shared + $(MAKE) -C deps/openssl + $(MAKE) install_sw -C deps/openssl + $(LIBNFNETLINK): cd deps/libnfnetlink && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) --enable-static --disable-shared $(MAKE) -C deps/libnfnetlink @@ -57,16 +62,16 @@ $(LIBMNL): $(MAKE) -C deps/libmnl $(MAKE) install -C deps/libmnl -$(LIBNETFILTER_QUEUE): $(LIBNFNETLINK) $(LIBMNL) +$(LIBNETFILTER_QUEUE): $(LIBNFNETLINK) $(LIBMNL) cd deps/libnetfilter_queue && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) --enable-static --disable-shared $(MAKE) -C deps/libnetfilter_queue $(MAKE) install -C deps/libnetfilter_queue -$(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL) +$(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL) $(LIBCRYPTO) @echo 'CCLD $(APP)' $(CCLD) $(OBJS) -o $(APP) $(LDFLAGS) -lmnl -lnetfilter_queue -lpthread -$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) config.h +$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) $(LIBCRYPTO) config.h @echo 'CC $@' $(CC) -c $(CFLAGS) $(LDFLAGS) $< -o $@ @@ -84,8 +89,12 @@ uninstall: -systemctl disable youtubeUnblock.service clean: + find $(BUILD_DIR) -maxdepth 1 -type f | xargs rm -rf + +distclean: clean rm -rf $(BUILD_DIR) $(MAKE) distclean -C deps/libnetfilter_queue || true $(MAKE) distclean -C deps/libmnl || true $(MAKE) distclean -C deps/libnfnetlink || true + #$(MAKE) distclean -C deps/openssl || true