From 82c49119d38b61f9b4b6bbf38e8ed4bde8cbc05b Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Thu, 26 Sep 2024 18:11:05 +0300 Subject: [PATCH 01/14] Add more custom fake message, random fake message --- Kbuild | 2 +- args.c | 79 +++++- config.h | 37 ++- mangle.c | 606 ++++++++++----------------------------------- mangle.h | 29 --- raw_replacements.h | 4 +- tls.c | 336 +++++++++++++++++++++++++ tls.h | 53 ++++ uspace.mk | 2 +- utils.c | 116 +++++++++ utils.h | 10 + 11 files changed, 759 insertions(+), 515 deletions(-) create mode 100644 tls.c create mode 100644 tls.h diff --git a/Kbuild b/Kbuild index 3962a79..1290c84 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o -kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o kargs.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o kargs.o tls.o ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/args.c b/args.c index 42df82a..142d969 100644 --- a/args.c +++ b/args.c @@ -7,7 +7,9 @@ #include #include #include +#include "types.h" +static char custom_fake_buf[MAX_FAKE_SIZE]; struct config_t config = { .threads = THREADS_NUM, @@ -18,6 +20,8 @@ struct config_t config = { .faking_ttl = FAKE_TTL, .fake_sni = 1, .fake_sni_seq_len = 1, + .fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT, + .fake_sni_type = FAKE_PAYLOAD_DEFAULT, .frag_middle_sni = 1, .frag_sni_pos = 1, .use_ipv6 = 1, @@ -55,6 +59,8 @@ struct config_t config = { .queue_start_num = DEFAULT_QUEUE_NUM, .fake_sni_pkt = fake_sni_old, .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator + .fake_custom_pkt = custom_fake_buf, + .fake_custom_pkt_sz = 0 }; #define OPT_SNI_DOMAINS 1 @@ -63,6 +69,9 @@ struct config_t config = { #define OPT_FAKING_TTL 3 #define OPT_FAKING_STRATEGY 10 #define OPT_FAKE_SNI_SEQ_LEN 11 +#define OPT_FAKE_SNI_SEQ_TYPE 26 +#define OPT_FAKE_SNI_TYPE 27 +#define OPT_FAKE_CUSTOM_PAYLOAD 28 #define OPT_FRAG 4 #define OPT_FRAG_SNI_REVERSE 12 #define OPT_FRAG_SNI_FAKED 13 @@ -83,7 +92,7 @@ struct config_t config = { #define OPT_NO_GSO 8 #define OPT_QUEUE_NUM 9 -#define OPT_MAX OPT_SNI_DOMAINS +#define OPT_MAX OPT_FAKE_CUSTOM_PAYLOAD static struct option long_opt[] = { {"help", 0, 0, 'h'}, @@ -94,6 +103,9 @@ static struct option long_opt[] = { {"synfake", 1, 0, OPT_SYNFAKE}, {"synfake-len", 1, 0, OPT_SYNFAKE_LEN}, {"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN}, + {"fake-sni-seq-type", 1, 0, OPT_FAKE_SNI_SEQ_TYPE}, + {"fake-sni-type", 1, 0, OPT_FAKE_SNI_TYPE}, + {"fake-custom-payload", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD}, {"faking-strategy", 1, 0, OPT_FAKING_STRATEGY}, {"fake-seq-offset", 1, 0, OPT_FAKE_SEQ_OFFSET}, {"faking-ttl", 1, 0, OPT_FAKING_TTL}, @@ -150,6 +162,9 @@ void print_usage(const char *argv0) { printf("\t--exclude-domains=\n"); printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni-seq-len=\n"); + printf("\t--fake-sni-seq-type={default|random|custom}\n"); + printf("\t--fake-sni-type={default|random|custom}\n"); + printf("\t--fake-custom-payload=\n"); printf("\t--fake-seq-offset=\n"); printf("\t--faking-ttl=\n"); printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}\n"); @@ -299,7 +314,7 @@ int parse_args(int argc, char *argv[]) { break; case OPT_FAKE_SEQ_OFFSET: num = parse_numeric_option(optarg); - if (errno != 0 || num < 0) { + if (errno != 0) { goto invalid_opt; } @@ -323,6 +338,66 @@ int parse_args(int argc, char *argv[]) { config.fake_sni_seq_len = num; break; + case OPT_FAKE_SNI_SEQ_TYPE: + if (strcmp(optarg, "default") == 0) { + config.fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT; + } else if (strcmp(optarg, "random") == 0) { + config.fake_sni_seq_type = FAKE_PAYLOAD_RANDOM; + } else if (strcmp(optarg, "custom") == 0) { + config.fake_sni_seq_type = FAKE_PAYLOAD_CUSTOM; + } else { + goto invalid_opt; + } + + break; + case OPT_FAKE_SNI_TYPE: + if (strcmp(optarg, "default") == 0) { + config.fake_sni_type = FAKE_PAYLOAD_DEFAULT; + } else if (strcmp(optarg, "random") == 0) { + config.fake_sni_type = FAKE_PAYLOAD_RANDOM; + } else if (strcmp(optarg, "custom") == 0) { + config.fake_sni_type = FAKE_PAYLOAD_CUSTOM; + } else { + goto invalid_opt; + } + + break; + case OPT_FAKE_CUSTOM_PAYLOAD: { + uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; + + const char *custom_hex_fake = optarg; + size_t custom_hlen = strlen(custom_hex_fake); + if ((custom_hlen & 1) == 1) { + printf("Custom fake hex should be divisible by two\n"); + goto invalid_opt; + } + + + size_t custom_len = custom_hlen >> 1; + if (custom_len > MAX_FAKE_SIZE) { + printf("Custom fake is too large\n"); + goto invalid_opt; + } + + for (int i = 0; i < custom_len; i++) { + sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); + } + + config.fake_custom_pkt_sz = custom_len; + config.fake_custom_pkt = (char *)custom_buf; + + // if (strcmp(optarg, "default") == 0) { + // config.fake_sni_type = FAKE_PAYLOAD_DEFAULT; + // } else if (strcmp(optarg, "random") == 0) { + // config.fake_sni_type = FAKE_PAYLOAD_RANDOM; + // } else if (strcmp(optarg, "custom") == 0) { + // config.fake_sni_type = FAKE_PAYLOAD_CUSTOM; + // } else { + // goto invalid_opt; + // } + // + } + break; case OPT_FK_WINSIZE: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { diff --git a/config.h b/config.h index 2b5393f..43fd9f8 100644 --- a/config.h +++ b/config.h @@ -32,6 +32,14 @@ struct config_t { unsigned char faking_ttl; int fake_sni; unsigned int fake_sni_seq_len; + +#define FAKE_PAYLOAD_RANDOM 0 +#define FAKE_PAYLOAD_CUSTOM 1 +// In default mode all other options will be skipped. +#define FAKE_PAYLOAD_DEFAULT 2 + int fake_sni_seq_type; + int fake_sni_type; + #define VERBOSE_INFO 0 #define VERBOSE_DEBUG 1 #define VERBOSE_TRACE 2 @@ -47,10 +55,16 @@ struct config_t { const char *exclude_domains_str; unsigned int exclude_domains_strlen; unsigned int all_domains; + const char *fake_sni_pkt; unsigned int fake_sni_pkt_sz; + + const char *fake_custom_pkt; + unsigned int fake_custom_pkt_sz; + + unsigned int fk_winsize; - unsigned int fakeseq_offset; + int fakeseq_offset; unsigned int mark; int synfake; unsigned int synfake_len; @@ -89,19 +103,30 @@ 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_RAND_SEQ 1 +#define FAKE_STRAT_RAND_SEQ (1 << 0) // 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_PAST_SEQ 3 -#define FAKE_STRAT_TCP_CHECK 4 -#define FAKE_STRAT_TCP_MD5SUM 5 +#define FAKE_STRAT_TTL (1 << 1) +#define FAKE_STRAT_PAST_SEQ (1 << 2) +#define FAKE_STRAT_TCP_CHECK (1 << 3) +#define FAKE_STRAT_TCP_MD5SUM (1 << 4) +#define FAKE_STRAT_COUNT 5 + +/** + * This macros iterates through all faking strategies and executes code under it. + * destination strategy will be available under name of `strategy` variable. + */ +#define ITER_FAKE_STRAT(fake_bitmask, strategy) \ +for (int strategy = 1; strategy <= (1 << FAKE_STRAT_COUNT); strategy <<= 1) \ +if ((fake_bitmask) & strategy) #ifndef FAKING_STRATEGY #define FAKING_STRATEGY FAKE_STRAT_PAST_SEQ #endif +#define MAX_FAKE_SIZE 1300 + #if !defined(SILENT) && !defined(KERNEL_SPACE) #define DEBUG #endif diff --git a/mangle.c b/mangle.c index 0b6c45d..88380e7 100644 --- a/mangle.c +++ b/mangle.c @@ -5,6 +5,7 @@ #include "utils.h" #include "quic.h" #include "logging.h" +#include "tls.h" #ifndef KERNEL_SPACE #include @@ -527,31 +528,33 @@ send_frag1: send_fake: if (config.frag_sni_faked) { - uint32_t iphfl, tcphfl; - fake_pad_len = f2len; - ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); - if (ret < 0) { - lgerror("Invalid frag2", ret); - goto erret_lc; + ITER_FAKE_STRAT(config.faking_strategy, strategy) { + uint32_t iphfl, tcphfl; + fake_pad_len = f2len; + ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); + if (ret < 0) { + lgerror("Invalid frag2", ret); + goto erret_lc; + } + memcpy(fake_pad, frag2, iphfl + tcphfl); + memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); + struct tcphdr *fakethdr = (void *)(fake_pad + iphfl); + if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { + lgtrace("frag fake sent with %u -> ", ntohl(fakethdr->seq)); + fakethdr->seq = htonl(ntohl(fakethdr->seq) - dvs); + lgtrace_addp("%u, ", ntohl(fakethdr->seq)); + } + ret = fail_packet(strategy, + fake_pad, &fake_pad_len, MAX_PACKET_SIZE); + if (ret < 0) { + lgerror("Failed to fail packet", ret); + goto erret_lc; + } + ret = send_tcp_frags(fake_pad, fake_pad_len, NULL, 0, 0); + if (ret < 0) { + goto erret_lc; + } } - memcpy(fake_pad, frag2, iphfl + tcphfl); - memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); - struct tcphdr *fakethdr = (void *)(fake_pad + iphfl); - if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { - lgtrace("frag fake sent with %u -> ", ntohl(fakethdr->seq)); - fakethdr->seq = htonl(ntohl(fakethdr->seq) - dvs); - lgtrace_addp("%u, ", ntohl(fakethdr->seq)); - } - ret = fail_packet(fake_pad, &fake_pad_len, MAX_PACKET_SIZE); - if (ret < 0) { - lgerror("Failed to fail packet", ret); - goto erret_lc; - } - ret = send_tcp_frags(fake_pad, fake_pad_len, NULL, 0, 0); - if (ret < 0) { - goto erret_lc; - } - } if (config.frag_sni_reverse) @@ -596,476 +599,131 @@ int post_fake_sni(const void *iph, unsigned int iph_len, void *fsiph = (void *)rfsiph; struct tcphdr *fstcph = (void *)rfstcph; - for (int i = 0; i < sequence_len; i++) { + ITER_FAKE_STRAT(config.faking_strategy, strategy) { + struct fake_type fake_seq_type = { + .type = FAKE_PAYLOAD_DEFAULT, + .strategy = strategy, + }; + + switch (config.fake_sni_seq_type) { + case FAKE_PAYLOAD_RANDOM: + fake_seq_type.type = FAKE_PAYLOAD_RANDOM; + break; + case FAKE_PAYLOAD_CUSTOM: + fake_seq_type.type = FAKE_PAYLOAD_CUSTOM; + fake_seq_type.fake_data = config.fake_custom_pkt; + fake_seq_type.fake_len = config.fake_custom_pkt_sz; + break; + default: + fake_seq_type.type = FAKE_PAYLOAD_DEFAULT; + } + + for (int i = 0; i < sequence_len; i++) { + NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(fake_sni)) { + lgerror("Allocation error", -ENOMEM); + return -ENOMEM; + } + uint32_t fsn_len = MAX_PACKET_SIZE; + + ret = gen_fake_sni( + fake_seq_type, + fsiph, iph_len, fstcph, tcph_len, + fake_sni, &fsn_len); + if (ret < 0) { + lgerror("gen_fake_sni", ret); + goto erret_lc; + } + + lgtrace_addp("post fake sni #%d", i + 1); + lgtrace_addp("post with %d bytes", fsn_len); + ret = instance_config.send_raw_packet(fake_sni, fsn_len); + if (ret < 0) { + lgerror("send fake sni", ret); + goto erret_lc; + } + + if (!(config.faking_strategy == FAKE_STRAT_PAST_SEQ || + config.faking_strategy == FAKE_STRAT_RAND_SEQ)) { + + uint32_t iph_len; + uint32_t tcph_len; + uint32_t plen; + ret = tcp_payload_split( + fake_sni, fsn_len, + &fsiph, &iph_len, + &fstcph, &tcph_len, + NULL, &plen); + + if (ret < 0) { + lgtrace_addp("continue fake seq"); + goto erret_lc; + } + + fstcph->seq = htonl(ntohl(fstcph->seq) + plen); + memcpy(rfsiph, fsiph, iph_len); + memcpy(rfstcph, fstcph, tcph_len); + fsiph = (void *)rfsiph; + fstcph = (void *)rfstcph; + } + + NETBUF_FREE(fake_sni); + continue; +erret_lc: + NETBUF_FREE(fake_sni); + return ret; + } + + struct fake_type ftype = { + .type = FAKE_PAYLOAD_DEFAULT, + .strategy = strategy + }; + + switch (config.fake_sni_type) { + case FAKE_PAYLOAD_RANDOM: + ftype.type = FAKE_PAYLOAD_RANDOM; + break; + case FAKE_PAYLOAD_CUSTOM: + ftype.type = FAKE_PAYLOAD_CUSTOM; + ftype.fake_data = config.fake_custom_pkt; + ftype.fake_len = config.fake_custom_pkt_sz; + break; + default: + ftype.type = FAKE_PAYLOAD_DEFAULT; + } + NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); if (!NETBUF_CHECK(fake_sni)) { lgerror("Allocation error", -ENOMEM); return -ENOMEM; } uint32_t fsn_len = MAX_PACKET_SIZE; - ret = gen_fake_sni(fsiph, iph_len, fstcph, tcph_len, - fake_sni, &fsn_len); + ret = gen_fake_sni( + ftype, + iph, iph_len, tcph, tcph_len, + fake_sni, &fsn_len); if (ret < 0) { lgerror("gen_fake_sni", ret); - goto erret_lc; + goto erret_lc_cst; } - lgtrace_addp("post fake sni #%d", i + 1); - lgtrace_addp("post with %d", fsn_len); + lgtrace_addp("post normal fake sni"); + lgtrace_addp("post with %d bytes", fsn_len); ret = instance_config.send_raw_packet(fake_sni, fsn_len); if (ret < 0) { lgerror("send fake sni", ret); - goto erret_lc; + goto erret_lc_cst; } - uint32_t iph_len; - uint32_t tcph_len; - uint32_t plen; - tcp_payload_split( - fake_sni, fsn_len, - &fsiph, &iph_len, - &fstcph, &tcph_len, - NULL, &plen); + goto after_cus2; - - fstcph->seq = htonl(ntohl(fstcph->seq) + plen); - memcpy(rfsiph, fsiph, iph_len); - memcpy(rfstcph, fstcph, tcph_len); - fsiph = (void *)rfsiph; - fstcph = (void *)rfstcph; - - NETBUF_FREE(fake_sni); - continue; -erret_lc: +erret_lc_cst: NETBUF_FREE(fake_sni); return ret; +after_cus2: + ; } return 0; } -#define TLS_CONTENT_TYPE_HANDSHAKE 0x16 -#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 -#define TLS_EXTENSION_SNI 0x0000 -#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d - -/** - * Processes tls payload of the tcp request. - * - * data Payload data of TCP. - * dlen Length of `data`. - */ -struct tls_verdict analyze_tls_data( - const uint8_t *data, - uint32_t dlen) -{ - struct tls_verdict vrd = {0}; - - size_t i = 0; - const uint8_t *data_end = data + dlen; - - while (i + 4 < dlen) { - const uint8_t *msgData = data + i; - - uint8_t tls_content_type = *msgData; - uint8_t tls_vmajor = *(msgData + 1); - uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3)); - - if (tls_vmajor != 0x03) goto nextMessage; - - 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; - - if (handshakeProto + 1 >= data_end) break; - - uint8_t handshakeType = *handshakeProto; - - if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) - goto nextMessage; - - const uint8_t *msgPtr = handshakeProto; - msgPtr += 1; - msgPtr += 3 + 2 + 32; - - if (msgPtr + 1 >= data_end) break; - uint8_t sessionIdLength = *msgPtr; - msgPtr++; - msgPtr += sessionIdLength; - - if (msgPtr + 2 >= data_end) break; - uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr); - msgPtr += 2; - msgPtr += ciphersLength; - - if (msgPtr + 1 >= data_end) break; - uint8_t compMethodsLen = *msgPtr; - msgPtr++; - msgPtr += compMethodsLen; - - if (msgPtr + 2 >= data_end) break; - uint16_t extensionsLen = ntohs(*(uint16_t *)msgPtr); - msgPtr += 2; - - const uint8_t *extensionsPtr = msgPtr; - const uint8_t *extensions_end = extensionsPtr + extensionsLen; - if (extensions_end > data_end) extensions_end = data_end; - - while (extensionsPtr < extensions_end) { - const uint8_t *extensionPtr = extensionsPtr; - if (extensionPtr + 4 >= extensions_end) break; - - uint16_t extensionType = - ntohs(*(uint16_t *)extensionPtr); - extensionPtr += 2; - - uint16_t extensionLen = - ntohs(*(uint16_t *)extensionPtr); - extensionPtr += 2; - - - if (extensionPtr + extensionLen > extensions_end) - break; - - if (extensionType != TLS_EXTENSION_SNI) - goto nextExtension; - - const uint8_t *sni_ext_ptr = extensionPtr; - - if (sni_ext_ptr + 2 >= extensions_end) break; - uint16_t sni_ext_dlen = ntohs(*(uint16_t *)sni_ext_ptr); - - sni_ext_ptr += 2; - - const uint8_t *sni_ext_end = sni_ext_ptr + sni_ext_dlen; - if (sni_ext_end >= extensions_end) break; - - if (sni_ext_ptr + 3 >= sni_ext_end) break; - sni_ext_ptr++; - uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr); - sni_ext_ptr += 2; - - if (sni_ext_ptr + sni_len > sni_ext_end) break; - - char *sni_name = (char *)sni_ext_ptr; - - vrd.sni_offset = (uint8_t *)sni_name - data; - vrd.sni_len = sni_len; - - if (config.all_domains) { - vrd.target_sni = 1; - goto check_domain; - } - - - 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' )) { - - unsigned int domain_len = (i - j); - const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = config.domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - domain_len)) { - vrd.target_sni = 1; - goto check_domain; - } - - j = i + 1; - } - } - -check_domain: - if (vrd.target_sni == 1 && config.exclude_domains_strlen != 0) { - unsigned int j = 0; - for (unsigned int i = 0; i <= config.exclude_domains_strlen; i++) { - if ( i > j && - (i == config.exclude_domains_strlen || - config.exclude_domains_str[i] == '\0' || - config.exclude_domains_str[i] == ',' || - config.exclude_domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = config.exclude_domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - domain_len)) { - - vrd.target_sni = 0; - lgdebugmsg("Excluded SNI: %.*s", - vrd.sni_len, data + vrd.sni_offset); - goto out; - } - - j = i + 1; - } - } - } - - goto out; - -nextExtension: - extensionsPtr += 2 + 2 + extensionLen; - } -nextMessage: - i += 5 + message_length; - } - -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' )) { - - unsigned int domain_len = (i - j); - const char *domain_startp = config.domains_str + j; - - if (domain_len + dlen + 1> MAX_PACKET_SIZE) { - continue; - } - - NETBUF_ALLOC(buf, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buf)) { - lgerror("Allocation error", -ENOMEM); - goto out; - } - NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); - if (!NETBUF_CHECK(nzbuf)) { - lgerror("Allocation error", -ENOMEM); - NETBUF_FREE(buf); - goto out; - } - - int *zbuf = (void *)nzbuf; - - 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); - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - goto out; - } - } - - - j = i + 1; - - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - } - } - - goto out; -} - -int gen_fake_sni(const void *ipxh, uint32_t iph_len, - const struct tcphdr *tcph, uint32_t tcph_len, - uint8_t *buf, uint32_t *buflen) { - - if (!ipxh || !tcph || !buf || !buflen) - return -EINVAL; - - int ipxv = netproto_version(ipxh, iph_len); - - if (ipxv == IP4VERSION) { - const struct iphdr *iph = ipxh; - - memcpy(buf, iph, iph_len); - struct iphdr *niph = (struct iphdr *)buf; - - niph->protocol = IPPROTO_TCP; - } else if (ipxv == IP6VERSION) { - const struct ip6_hdr *iph = ipxh; - - iph_len = sizeof(struct ip6_hdr); - memcpy(buf, iph, iph_len); - struct ip6_hdr *niph = (struct ip6_hdr *)buf; - - niph->ip6_nxt = IPPROTO_TCP; - } else { - return -EINVAL; - } - - const char *data = config.fake_sni_pkt; - size_t data_len = config.fake_sni_pkt_sz; - - uint32_t dlen = iph_len + tcph_len + data_len; - - if (*buflen < dlen) - return -ENOMEM; - - memcpy(buf + iph_len, tcph, tcph_len); - memcpy(buf + iph_len + tcph_len, data, data_len); - - - if (ipxv == IP4VERSION) { - struct iphdr *niph = (struct iphdr *)buf; - niph->tot_len = htons(dlen); - } else if (ipxv == IP6VERSION) { - struct ip6_hdr *niph = (struct ip6_hdr *)buf; - niph->ip6_plen = htons(dlen - iph_len); - } - - fail_packet(buf, &dlen, *buflen); - *buflen = dlen; - - return 0; -} - -#define TCP_MD5SIG_LEN 16 -#define TCP_MD5SIG_KIND 19 -struct tcp_md5sig_opt { - uint8_t kind; - uint8_t len; - uint8_t sig[TCP_MD5SIG_LEN]; -}; -#define TCP_MD5SIG_OPT_LEN (sizeof(struct tcp_md5sig_opt)) -// Real length of the option, with NOOP fillers -#define TCP_MD5SIG_OPT_RLEN 20 - -int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { - void *iph; - uint32_t iph_len; - struct tcphdr *tcph; - uint32_t tcph_len; - uint8_t *data; - uint32_t dlen; - int ret; - - ret = tcp_payload_split(payload, *plen, - &iph, &iph_len, &tcph, &tcph_len, - &data, &dlen); - - uint32_t ipxv = netproto_version(payload, *plen); - - if (ret < 0) { - return ret; - } - - - if (config.faking_strategy == FAKE_STRAT_RAND_SEQ) { - lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); - - if (config.fakeseq_offset) { - tcph->seq = htonl(ntohl(tcph->seq) - config.fakeseq_offset); - } else { -#ifdef KERNEL_SPACE - tcph->seq = 124; -#else - tcph->seq = random(); -#endif - - } - - lgtrace_addp("%u", ntohl(tcph->seq)); - } else if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { - lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); - tcph->seq = htonl(ntohl(tcph->seq) - dlen); - lgtrace_addp("%u", ntohl(tcph->seq)); - - } else if (config.faking_strategy == FAKE_STRAT_TTL) { - lgtrace_addp("set fake ttl to %d", config.faking_ttl); - - if (ipxv == IP4VERSION) { - ((struct iphdr *)iph)->ttl = config.faking_ttl; - } else if (ipxv == IP6VERSION) { - ((struct ip6_hdr *)iph)->ip6_hops = config.faking_ttl; - } else { - lgerror("fail_packet: IP version is unsupported", -EINVAL); - return -EINVAL; - } - } else if (config.faking_strategy == FAKE_STRAT_TCP_MD5SUM) { - int optp_len = tcph_len - sizeof(struct tcphdr); - int delta = TCP_MD5SIG_OPT_RLEN - optp_len; - lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta); - - if (delta > 0) { - if (avail_buflen - *plen < delta) { - return -1; - } - uint8_t *ndata = data + delta; - uint8_t *ndptr = ndata + dlen; - uint8_t *dptr = data + dlen; - for (size_t i = dlen + 1; i > 0; i--) { - *ndptr = *dptr; - --ndptr, --dptr; - } - data = ndata; - tcph_len = tcph_len + delta; - tcph->doff = tcph_len >> 2; - if (ipxv == IP4VERSION) { - ((struct iphdr *)iph)->tot_len = htons(ntohs(((struct iphdr *)iph)->tot_len) + delta); - } else if (ipxv == IP6VERSION) { - ((struct ip6_hdr *)iph)->ip6_plen = htons(ntohs(((struct ip6_hdr *)iph)->ip6_plen) + delta); - } else { - lgerror("fail_packet: IP version is unsupported", -EINVAL); - return -EINVAL; - } - optp_len += delta; - *plen += delta; - } - - uint8_t *optplace = (uint8_t *)tcph + sizeof(struct tcphdr); - struct tcp_md5sig_opt *mdopt = (void *)optplace; - mdopt->kind = TCP_MD5SIG_KIND; - mdopt->len = TCP_MD5SIG_OPT_LEN; - - optplace += sizeof(struct tcp_md5sig_opt); - optp_len -= sizeof(struct tcp_md5sig_opt); - - while (optp_len-- > 0) { - *optplace++ = 0x01; - } - } - - set_ip_checksum(iph, iph_len); - set_tcp_checksum(tcph, iph, iph_len); - - if (config.faking_strategy == FAKE_STRAT_TCP_CHECK) { - lgtrace_addp("break fake tcp checksum"); - tcph->check += 1; - } - - return 0; -} diff --git a/mangle.h b/mangle.h index f07ab17..4b8afa7 100644 --- a/mangle.h +++ b/mangle.h @@ -3,35 +3,6 @@ #include "types.h" -/** - * Result of analyze_tls_data function - */ -struct tls_verdict { - int target_sni; /* google video hello packet */ - int sni_offset; /* offset from start of tcp _payload_ */ - int sni_len; -}; - -/** - * Processes the packet and finds TLS Client Hello information inside it. - * data pointer points to start of TLS Message (TCP Payload) - */ -struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); - - -/** - * Generates fake client hello message - */ -int gen_fake_sni(const void *iph, uint32_t iph_len, - const struct tcphdr *tcph, uint32_t tcph_len, - uint8_t *buf, uint32_t *buflen); - -/** - * Invalidates the raw packet. The function aims to invalid the packet - * in such way as it will be accepted by DPI, but dropped by target server - */ -int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); - #define PKT_ACCEPT 0 #define PKT_DROP 1 diff --git a/raw_replacements.h b/raw_replacements.h index 4123bd8..8dde7ff 100644 --- a/raw_replacements.h +++ b/raw_replacements.h @@ -3,8 +3,8 @@ #define FAKE_SNI_MAXLEN 1500 -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[] = "\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"; -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"; +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"; #endif /*RAW_REPLACEMENTS_H*/ diff --git a/tls.c b/tls.c new file mode 100644 index 0000000..287f7c8 --- /dev/null +++ b/tls.c @@ -0,0 +1,336 @@ +#include "types.h" +#include "tls.h" +#include "config.h" +#include "logging.h" +#include "utils.h" + +#ifndef KERNEL_SPACE +#include +#include +#endif + +#define TLS_CONTENT_TYPE_HANDSHAKE 0x16 +#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 +#define TLS_EXTENSION_SNI 0x0000 +#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d + +/** + * Processes tls payload of the tcp request. + * + * data Payload data of TCP. + * dlen Length of `data`. + */ +struct tls_verdict analyze_tls_data( + const uint8_t *data, + uint32_t dlen) +{ + struct tls_verdict vrd = {0}; + + size_t i = 0; + const uint8_t *data_end = data + dlen; + + while (i + 4 < dlen) { + const uint8_t *msgData = data + i; + + uint8_t tls_content_type = *msgData; + uint8_t tls_vmajor = *(msgData + 1); + uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3)); + + if (tls_vmajor != 0x03) goto nextMessage; + + 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; + + if (handshakeProto + 1 >= data_end) break; + + uint8_t handshakeType = *handshakeProto; + + if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) + goto nextMessage; + + const uint8_t *msgPtr = handshakeProto; + msgPtr += 1; + msgPtr += 3 + 2 + 32; + + if (msgPtr + 1 >= data_end) break; + uint8_t sessionIdLength = *msgPtr; + msgPtr++; + msgPtr += sessionIdLength; + + if (msgPtr + 2 >= data_end) break; + uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr); + msgPtr += 2; + msgPtr += ciphersLength; + + if (msgPtr + 1 >= data_end) break; + uint8_t compMethodsLen = *msgPtr; + msgPtr++; + msgPtr += compMethodsLen; + + if (msgPtr + 2 >= data_end) break; + uint16_t extensionsLen = ntohs(*(uint16_t *)msgPtr); + msgPtr += 2; + + const uint8_t *extensionsPtr = msgPtr; + const uint8_t *extensions_end = extensionsPtr + extensionsLen; + if (extensions_end > data_end) extensions_end = data_end; + + while (extensionsPtr < extensions_end) { + const uint8_t *extensionPtr = extensionsPtr; + if (extensionPtr + 4 >= extensions_end) break; + + uint16_t extensionType = + ntohs(*(uint16_t *)extensionPtr); + extensionPtr += 2; + + uint16_t extensionLen = + ntohs(*(uint16_t *)extensionPtr); + extensionPtr += 2; + + + if (extensionPtr + extensionLen > extensions_end) + break; + + if (extensionType != TLS_EXTENSION_SNI) + goto nextExtension; + + const uint8_t *sni_ext_ptr = extensionPtr; + + if (sni_ext_ptr + 2 >= extensions_end) break; + uint16_t sni_ext_dlen = ntohs(*(uint16_t *)sni_ext_ptr); + + sni_ext_ptr += 2; + + const uint8_t *sni_ext_end = sni_ext_ptr + sni_ext_dlen; + if (sni_ext_end >= extensions_end) break; + + if (sni_ext_ptr + 3 >= sni_ext_end) break; + sni_ext_ptr++; + uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr); + sni_ext_ptr += 2; + + if (sni_ext_ptr + sni_len > sni_ext_end) break; + + char *sni_name = (char *)sni_ext_ptr; + + vrd.sni_offset = (uint8_t *)sni_name - data; + vrd.sni_len = sni_len; + + if (config.all_domains) { + vrd.target_sni = 1; + goto check_domain; + } + + + 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' )) { + + unsigned int domain_len = (i - j); + const char *sni_startp = sni_name + sni_len - domain_len; + const char *domain_startp = config.domains_str + j; + + if (sni_len >= domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + domain_len)) { + vrd.target_sni = 1; + goto check_domain; + } + + j = i + 1; + } + } + +check_domain: + if (vrd.target_sni == 1 && config.exclude_domains_strlen != 0) { + unsigned int j = 0; + for (unsigned int i = 0; i <= config.exclude_domains_strlen; i++) { + if ( i > j && + (i == config.exclude_domains_strlen || + config.exclude_domains_str[i] == '\0' || + config.exclude_domains_str[i] == ',' || + config.exclude_domains_str[i] == '\n' )) { + + unsigned int domain_len = (i - j); + const char *sni_startp = sni_name + sni_len - domain_len; + const char *domain_startp = config.exclude_domains_str + j; + + if (sni_len >= domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + domain_len)) { + + vrd.target_sni = 0; + lgdebugmsg("Excluded SNI: %.*s", + vrd.sni_len, data + vrd.sni_offset); + goto out; + } + + j = i + 1; + } + } + } + + goto out; + +nextExtension: + extensionsPtr += 2 + 2 + extensionLen; + } +nextMessage: + i += 5 + message_length; + } + +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' )) { + + unsigned int domain_len = (i - j); + const char *domain_startp = config.domains_str + j; + + if (domain_len + dlen + 1> MAX_PACKET_SIZE) { + continue; + } + + NETBUF_ALLOC(buf, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buf)) { + lgerror("Allocation error", -ENOMEM); + goto out; + } + NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); + if (!NETBUF_CHECK(nzbuf)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(buf); + goto out; + } + + int *zbuf = (void *)nzbuf; + + 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); + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + goto out; + } + } + + + j = i + 1; + + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + } + } + + goto out; +} + +int gen_fake_sni(struct fake_type type, + const void *ipxh, uint32_t iph_len, + const struct tcphdr *tcph, uint32_t tcph_len, + uint8_t *buf, uint32_t *buflen) { + + uint32_t data_len = type.fake_len; + if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { + data_len = random() % 1200; + } else if (type.type == FAKE_PAYLOAD_DEFAULT) { + data_len = config.fake_sni_pkt_sz; + } + + if (!ipxh || !tcph || !buf || !buflen) + return -EINVAL; + + int ipxv = netproto_version(ipxh, iph_len); + + if (ipxv == IP4VERSION) { + const struct iphdr *iph = ipxh; + + memcpy(buf, iph, iph_len); + struct iphdr *niph = (struct iphdr *)buf; + + niph->protocol = IPPROTO_TCP; + } else if (ipxv == IP6VERSION) { + const struct ip6_hdr *iph = ipxh; + + iph_len = sizeof(struct ip6_hdr); + memcpy(buf, iph, iph_len); + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + + niph->ip6_nxt = IPPROTO_TCP; + } else { + return -EINVAL; + } + + uint32_t dlen = iph_len + tcph_len + data_len; + + if (*buflen < dlen) + return -ENOMEM; + + memcpy(buf + iph_len, tcph, tcph_len); + uint8_t *bfdptr = buf + iph_len + tcph_len; + + switch (type.type) { + case FAKE_PAYLOAD_DEFAULT: + memcpy(bfdptr, config.fake_sni_pkt, data_len); + break; + case FAKE_PAYLOAD_DATA: + memcpy(bfdptr, type.fake_data, data_len); + break; + default: // FAKE_PAYLOAD_RANDOM + getrandom(bfdptr, data_len, 0); + } + + if (ipxv == IP4VERSION) { + struct iphdr *niph = (struct iphdr *)buf; + niph->tot_len = htons(dlen); + } else if (ipxv == IP6VERSION) { + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + niph->ip6_plen = htons(dlen - iph_len); + } + + fail_packet(type.strategy, buf, &dlen, *buflen); + + *buflen = dlen; + + return 0; +} + diff --git a/tls.h b/tls.h new file mode 100644 index 0000000..ba71d19 --- /dev/null +++ b/tls.h @@ -0,0 +1,53 @@ +#ifndef TLS_H +#define TLS_H + +#include "types.h" + + +/** + * Result of analyze_tls_data function + */ +struct tls_verdict { + int target_sni; /* google video hello packet */ + int sni_offset; /* offset from start of tcp _payload_ */ + int sni_len; +}; + +/** + * Processes the packet and finds TLS Client Hello information inside it. + * data pointer points to start of TLS Message (TCP Payload) + */ +struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); + + +struct fake_type { + +#define FAKE_PAYLOAD_RANDOM 0 +#define FAKE_PAYLOAD_DATA 1 +// In default mode all other options will be skipped. +#define FAKE_PAYLOAD_DEFAULT 2 + int type; + + // Length of the final fake message. + // Pass 0 in RANDOM mode to make it random + uint16_t fake_len; + + // Payload of the fake message of fake_len length. + // Will be omitted in RANDOM mode. + const char *fake_data; + + // faking strategy of the fake packet. + // Does not support bitmask, pass standalone strategy. + // Pass 0 if you don't want any faking procedures. + unsigned int strategy; +}; + +/** + * Generates the fake client hello message + */ +int gen_fake_sni(struct fake_type type, + const void *iph, uint32_t iph_len, + const struct tcphdr *tcph, uint32_t tcph_len, + uint8_t *buf, uint32_t *buflen); + +#endif /* TLS_H */ diff --git a/uspace.mk b/uspace.mk index db43b15..d119c84 100644 --- a/uspace.mk +++ b/uspace.mk @@ -31,7 +31,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c +SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la diff --git a/utils.c b/utils.c index 19f14fa..e93b4c1 100644 --- a/utils.c +++ b/utils.c @@ -523,3 +523,119 @@ void z_function(const char *str, int *zbuf, size_t len) { } } +#define TCP_MD5SIG_LEN 16 +#define TCP_MD5SIG_KIND 19 +struct tcp_md5sig_opt { + uint8_t kind; + uint8_t len; + uint8_t sig[TCP_MD5SIG_LEN]; +}; +#define TCP_MD5SIG_OPT_LEN (sizeof(struct tcp_md5sig_opt)) +// Real length of the option, with NOOP fillers +#define TCP_MD5SIG_OPT_RLEN 20 + +int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { + void *iph; + uint32_t iph_len; + struct tcphdr *tcph; + uint32_t tcph_len; + uint8_t *data; + uint32_t dlen; + int ret; + + ret = tcp_payload_split(payload, *plen, + &iph, &iph_len, &tcph, &tcph_len, + &data, &dlen); + + uint32_t ipxv = netproto_version(payload, *plen); + + if (ret < 0) { + return ret; + } + + + if (strategy == FAKE_STRAT_RAND_SEQ) { + lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); + + if (config.fakeseq_offset) { + tcph->seq = htonl(ntohl(tcph->seq) - config.fakeseq_offset); + } else { +#ifdef KERNEL_SPACE + tcph->seq = 124; +#else + tcph->seq = random(); +#endif + + } + + lgtrace_addp("%u", ntohl(tcph->seq)); + } else if (strategy == FAKE_STRAT_PAST_SEQ) { + lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); + tcph->seq = htonl(ntohl(tcph->seq) - dlen); + lgtrace_addp("%u", ntohl(tcph->seq)); + + } else if (strategy == FAKE_STRAT_TTL) { + lgtrace_addp("set fake ttl to %d", config.faking_ttl); + + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->ttl = config.faking_ttl; + } else if (ipxv == IP6VERSION) { + ((struct ip6_hdr *)iph)->ip6_hops = config.faking_ttl; + } else { + lgerror("fail_packet: IP version is unsupported", -EINVAL); + return -EINVAL; + } + } else if (strategy == FAKE_STRAT_TCP_MD5SUM) { + int optp_len = tcph_len - sizeof(struct tcphdr); + int delta = TCP_MD5SIG_OPT_RLEN - optp_len; + lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta); + + if (delta > 0) { + if (avail_buflen - *plen < delta) { + return -1; + } + uint8_t *ndata = data + delta; + uint8_t *ndptr = ndata + dlen; + uint8_t *dptr = data + dlen; + for (size_t i = dlen + 1; i > 0; i--) { + *ndptr = *dptr; + --ndptr, --dptr; + } + data = ndata; + tcph_len = tcph_len + delta; + tcph->doff = tcph_len >> 2; + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->tot_len = htons(ntohs(((struct iphdr *)iph)->tot_len) + delta); + } else if (ipxv == IP6VERSION) { + ((struct ip6_hdr *)iph)->ip6_plen = htons(ntohs(((struct ip6_hdr *)iph)->ip6_plen) + delta); + } else { + lgerror("fail_packet: IP version is unsupported", -EINVAL); + return -EINVAL; + } + optp_len += delta; + *plen += delta; + } + + uint8_t *optplace = (uint8_t *)tcph + sizeof(struct tcphdr); + struct tcp_md5sig_opt *mdopt = (void *)optplace; + mdopt->kind = TCP_MD5SIG_KIND; + mdopt->len = TCP_MD5SIG_OPT_LEN; + + optplace += sizeof(struct tcp_md5sig_opt); + optp_len -= sizeof(struct tcp_md5sig_opt); + + while (optp_len-- > 0) { + *optplace++ = 0x01; + } + } + + set_ip_checksum(iph, iph_len); + set_tcp_checksum(tcph, iph, iph_len); + + if (strategy == FAKE_STRAT_TCP_CHECK) { + lgtrace_addp("break fake tcp checksum"); + tcph->check += 1; + } + + return 0; +} diff --git a/utils.h b/utils.h index b9ad085..77106b0 100644 --- a/utils.h +++ b/utils.h @@ -101,4 +101,14 @@ int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len); void z_function(const char *str, int *zbuf, size_t len); + +/** + * Invalidates the raw packet. The function aims to invalid the packet + * in such way as it will be accepted by DPI, but dropped by target server + * + * Does not support bitmask, pass standalone strategy. + */ +int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); + + #endif /* UTILS_H */ From 3ee979f7d173a6828aa5e551a6e1cb94a2e25fd4 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 28 Sep 2024 11:31:46 +0300 Subject: [PATCH 02/14] Enhance middle sni split Instead of real middle sni we use targetted middle sni for explicit (not all) sni domain list --- mangle.c | 7 ++++--- tls.c | 4 +++- tls.h | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mangle.c b/mangle.c index 88380e7..09b6c30 100644 --- a/mangle.c +++ b/mangle.c @@ -190,7 +190,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { switch (config.fragmentation_strategy) { case FRAG_STRAT_TCP: { - ipd_offset = vrd.sni_offset; + ipd_offset = vrd.sni_target_offset; mid_offset = ipd_offset + vrd.sni_len / 2; uint32_t poses[2]; @@ -221,7 +221,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { break; case FRAG_STRAT_IP: if (ipxv == IP4VERSION) { - ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset; + ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_target_offset; mid_offset = ipd_offset + vrd.sni_len / 2; mid_offset += 8 - mid_offset % 8; @@ -618,7 +618,8 @@ int post_fake_sni(const void *iph, unsigned int iph_len, fake_seq_type.type = FAKE_PAYLOAD_DEFAULT; } - for (int i = 0; i < sequence_len; i++) { + // one goes for default fake + for (int i = 1; i < sequence_len; i++) { NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); if (!NETBUF_CHECK(fake_sni)) { lgerror("Allocation error", -ENOMEM); diff --git a/tls.c b/tls.c index 287f7c8..5c8fa11 100644 --- a/tls.c +++ b/tls.c @@ -122,6 +122,7 @@ struct tls_verdict analyze_tls_data( char *sni_name = (char *)sni_ext_ptr; vrd.sni_offset = (uint8_t *)sni_name - data; + vrd.sni_target_offset = vrd.sni_offset; vrd.sni_len = sni_len; if (config.all_domains) { @@ -129,7 +130,6 @@ struct tls_verdict analyze_tls_data( goto check_domain; } - unsigned int j = 0; for (unsigned int i = 0; i <= config.domains_strlen; i++) { if ( i > j && @@ -148,6 +148,7 @@ struct tls_verdict analyze_tls_data( domain_startp, domain_len)) { vrd.target_sni = 1; + vrd.sni_target_offset = (const uint8_t *)sni_startp - data; goto check_domain; } @@ -247,6 +248,7 @@ brute: vrd.target_sni = 1; vrd.sni_len = domain_len; vrd.sni_offset = (k - domain_len - 1); + vrd.sni_target_offset = vrd.sni_offset NETBUF_FREE(buf); NETBUF_FREE(nzbuf); goto out; diff --git a/tls.h b/tls.h index ba71d19..bcc7cab 100644 --- a/tls.h +++ b/tls.h @@ -10,6 +10,7 @@ struct tls_verdict { int target_sni; /* google video hello packet */ int sni_offset; /* offset from start of tcp _payload_ */ + int sni_target_offset; /* offset of target domain instead of entire sni */ int sni_len; }; From 98a3fd5acd81543d895e16ac1ef9ce006757937a Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 28 Sep 2024 12:03:24 +0300 Subject: [PATCH 03/14] Add workflow for test build --- .github/workflows/test.yml | 171 +++++++++++++++++++++++++++++++++++++ tls.c | 12 ++- 2 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..b97951a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,171 @@ +# Tests whether the youtubeUnblock builds properly + +name: "youtubeUnblock build test" + +on: + push: + branches: [ "main" ] + paths-ignore: + - '.editorconfig' + - '.gitignore' + - 'LICENSE' + - 'README.md' + + pull_request: + branches: [ "main" ] + paths-ignore: + - '.editorconfig' + - '.gitignore' + - 'LICENSE' + - 'README.md' + + workflow_dispatch: + +jobs: + prepare: + runs-on: ubuntu-latest + outputs: + version: ${{ steps.gh.outputs.version }} + sha: ${{ steps.gh.outputs.sha }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: 'openwrt' + + - name: GH + id: gh + env: + REPO: ${{ github.repository }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: bash + run: | + echo "version=$(cat youtubeUnblock/Makefile | grep PKG_VERSION | sed 's/PKG_VERSION:=//')" >> $GITHUB_OUTPUT + if [[ "${{ github.event_name }}" != "pull_request" ]]; then + echo "sha=$(echo ${GITHUB_SHA::7})" >> $GITHUB_OUTPUT + else + echo "sha=$(gh api repos/$REPO/commits/main --jq '.sha[:7]')" >> $GITHUB_OUTPUT + fi + + build-static: + needs: prepare + name: build-static ${{ matrix.arch }} + runs-on: ubuntu-latest + strategy: + matrix: + arch: [x86_64] + branch: [latest-stable] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build + id: build + env: + ARCH: ${{ matrix.arch }} + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + shell: bash + run: | + make -j$(nproc) + strip -s build/youtubeUnblock + cp -va build/youtubeUnblock . + tar -czvf static-youtubeUnblock-$VERSION-$SHA-$PLATFORM.tar.gz youtubeUnblock youtubeUnblock.service README.md + + - name: Upload artifacts + if: steps.build.outcome == 'success' + uses: actions/upload-artifact@v4 + with: + name: static-youtubeUnblock-${{ matrix.arch }} + path: ./**/youtubeUnblock*.tar.gz + + build-kmod: + needs: prepare + name: build-kmod ${{ matrix.arch }} + runs-on: ubuntu-latest + strategy: + matrix: + arch: [x86_64] + branch: [latest-stable] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build + id: build + env: + ARCH: ${{ matrix.arch }} + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + shell: bash + run: | + make kmake + tar -czvf kmod-youtubeUnblock-$VERSION-$SHA-$PLATFORM-$(uname -r).tar.gz kyoutubeUnblock.ko + + - name: Upload artifacts + if: steps.build.outcome == 'success' + uses: actions/upload-artifact@v4 + with: + name: kmod-youtubeUnblock-${{ matrix.arch }} + path: ./**/kmod-youtubeUnblock*.tar.gz + + build-kmod-20.04: + needs: prepare + name: build-kmod ${{ matrix.arch }} + runs-on: ubuntu-latest + strategy: + matrix: + arch: [x86_64] + branch: [latest-stable] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: prepare runner + shell: bash + run: | + apt install -y \ + bridge-utils \ + qemu-kvm \ + libvirt-daemon-system \ + libvirt-clients \ + virtinst \ + libguestfs-tools \ + wait-for-it \ + whois \ + sshpass + + - name: build virtual machine + shell: bash + run: | + virt-builder ubuntu-20.04 \ + --network \ + --format qcow2 -o "vm_ubuntu.qcow2" \ + --update \ + --install build-essential \ + --run-command "useradd -p '' -s /bin/bash -m -G sudo ubuntu" \ + --edit '/etc/sudoers:s/^%sudo.*/%sudo ALL=(ALL) NOPASSWD:ALL/' \ + --edit '/etc/default/grub:s/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,115200n8"/' \ + --run-command update-grub + + + + + - name: Build + id: build + env: + ARCH: ${{ matrix.arch }} + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + shell: bash + run: | + make kmake + tar -czvf kmod-youtubeUnblock-$VERSION-$SHA-$PLATFORM-$(uname -r).tar.gz kyoutubeUnblock.ko + + - name: Upload artifacts + if: steps.build.outcome == 'success' + uses: actions/upload-artifact@v4 + with: + name: kmod-youtubeUnblock-${{ matrix.arch }} + path: ./**/kmod-youtubeUnblock*.tar.gz + diff --git a/tls.c b/tls.c index 5c8fa11..b459ef0 100644 --- a/tls.c +++ b/tls.c @@ -248,7 +248,7 @@ brute: vrd.target_sni = 1; vrd.sni_len = domain_len; vrd.sni_offset = (k - domain_len - 1); - vrd.sni_target_offset = vrd.sni_offset + vrd.sni_target_offset = vrd.sni_offset; NETBUF_FREE(buf); NETBUF_FREE(nzbuf); goto out; @@ -273,7 +273,13 @@ int gen_fake_sni(struct fake_type type, uint32_t data_len = type.fake_len; if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { +#ifdef KERNEL_SPACE + + // get_random_bytes(&data_len, sizeof(data_len)); + data_len = get_random_u32() % 1200; +#else data_len = random() % 1200; +#endif } else if (type.type == FAKE_PAYLOAD_DEFAULT) { data_len = config.fake_sni_pkt_sz; } @@ -318,7 +324,11 @@ int gen_fake_sni(struct fake_type type, memcpy(bfdptr, type.fake_data, data_len); break; default: // FAKE_PAYLOAD_RANDOM +#ifdef KERNEL_SPACE + get_random_bytes(bfdptr, data_len); +#else getrandom(bfdptr, data_len, 0); +#endif } if (ipxv == IP4VERSION) { From 6b21e9b3b3382ed3ae6c7412590ff5aabe55a542 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 28 Sep 2024 20:23:31 +0300 Subject: [PATCH 04/14] Add checker for kernel module --- .github/workflows/test.yml | 161 ++++++++++++++++++++++--------------- tls.c | 5 +- 2 files changed, 99 insertions(+), 67 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b97951a..81d19e7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -77,95 +77,126 @@ jobs: uses: actions/upload-artifact@v4 with: name: static-youtubeUnblock-${{ matrix.arch }} - path: ./**/youtubeUnblock*.tar.gz + path: ./**/static-youtubeUnblock*.tar.gz build-kmod: needs: prepare - name: build-kmod ${{ matrix.arch }} + name: build-kmod ${{ matrix.kernel_version }} runs-on: ubuntu-latest strategy: matrix: - arch: [x86_64] - branch: [latest-stable] - steps: - - name: Checkout - uses: actions/checkout@v4 + include: + - kernel_version: "6.6.52" + source: "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.52.tar.xz" + container_version: "24.04" - - name: Build - id: build - env: - ARCH: ${{ matrix.arch }} - VERSION: ${{ needs.prepare.outputs.version }} - SHA: ${{ needs.prepare.outputs.sha }} - shell: bash - run: | - make kmake - tar -czvf kmod-youtubeUnblock-$VERSION-$SHA-$PLATFORM-$(uname -r).tar.gz kyoutubeUnblock.ko + - kernel_version: "5.15.167" + source: "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.167.tar.xz" + container_version: "24.04" - - name: Upload artifacts - if: steps.build.outcome == 'success' - uses: actions/upload-artifact@v4 - with: - name: kmod-youtubeUnblock-${{ matrix.arch }} - path: ./**/kmod-youtubeUnblock*.tar.gz + - kernel_version: "5.4.284" + source: "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.284.tar.xz" + container_version: "24.04" - build-kmod-20.04: - needs: prepare - name: build-kmod ${{ matrix.arch }} - runs-on: ubuntu-latest - strategy: - matrix: - arch: [x86_64] - branch: [latest-stable] - steps: - - name: Checkout - uses: actions/checkout@v4 + - kernel_version: "4.19.322" + source: "https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.322.tar.xz" + container_version: "24.04" - - name: prepare runner - shell: bash - run: | - apt install -y \ - bridge-utils \ - qemu-kvm \ - libvirt-daemon-system \ - libvirt-clients \ - virtinst \ - libguestfs-tools \ - wait-for-it \ - whois \ - sshpass + - kernel_version: "4.4.302" + source: "https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.302.tar.xz" + container_version: "24.04" - - name: build virtual machine - shell: bash - run: | - virt-builder ubuntu-20.04 \ - --network \ - --format qcow2 -o "vm_ubuntu.qcow2" \ - --update \ - --install build-essential \ - --run-command "useradd -p '' -s /bin/bash -m -G sudo ubuntu" \ - --edit '/etc/sudoers:s/^%sudo.*/%sudo ALL=(ALL) NOPASSWD:ALL/' \ - --edit '/etc/default/grub:s/^GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT="console=tty0 console=ttyS0,115200n8"/' \ - --run-command update-grub + - kernel_version: "3.10.108" + source: "https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.10.108.tar.xz" + container_version: "16.04" + - kernel_version: "3.0.101" + source: "https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.0.101.tar.xz" + container_version: "14.04" + steps: + - name: Checkout + uses: actions/checkout@v4 - + - name: Restore builder from cache + id: cache-restore + uses: actions/cache/restore@v4 + with: + path: ~/builder.tar + key: builder-${{ matrix.kernel_version }} - - name: Build + - name: Load builder from cache + if: steps.cache-restore.outputs.cache-hit == 'true' + run: | + docker import - builder < ~/builder.tar + + - name: Prepare build env + if: steps.cache-restore.outputs.cache-hit != 'true' + run: | + mkdir ~/linux + pwd + ls / + ls ~ + + - name: Obtain kernel + if: steps.cache-restore.outputs.cache-hit != 'true' + run: | + cd ~/linux + wget ${{ matrix.source }} -O kernel.tar.xz -q + tar -xf kernel.tar.xz + rm -f kernel.tar.xz + /bin/bash -c "mv linux-* linux" + ls + ls linux + + - name: Install docker + if: steps.cache-restore.outputs.cache-hit != 'true' + run: | + cd ~/linux + docker pull ubuntu:${{ matrix.container_version }} + docker container create --name ubu_builder -w / ubuntu:${{ matrix.container_version }} tail -f /dev/null + docker container start ubu_builder + docker container exec ubu_builder bash -c "apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev" + docker cp ./linux ubu_builder:/linux + + - name: Build kernel + if: steps.cache-restore.outputs.cache-hit != 'true' + run: | + cd ~/linux + docker container exec -w /linux ubu_builder bash -c 'make defconfig' + docker container exec -w /linux ubu_builder bash -c 'make -j $(nproc)' + + - name: Export container + if: steps.cache-restore.outputs.cache-hit != 'true' + run: | + cd ~/linux + docker container kill ubu_builder + docker container export ubu_builder > ubu_builder.tar + docker container rm ubu_builder + mv ./ubu_builder.tar ~/builder.tar + docker import - builder < ~/builder.tar + + - name: Save kernel image to cache + if: steps.cache-restore.outputs.cache-hit != 'true' + id: cache-save + uses: actions/cache/save@v4 + with: + path: ~/builder.tar + key: builder-${{ matrix.kernel_version }} + + - name: Build kernel module id: build env: - ARCH: ${{ matrix.arch }} VERSION: ${{ needs.prepare.outputs.version }} SHA: ${{ needs.prepare.outputs.sha }} shell: bash run: | - make kmake - tar -czvf kmod-youtubeUnblock-$VERSION-$SHA-$PLATFORM-$(uname -r).tar.gz kyoutubeUnblock.ko + docker run --rm -v ./:/youtubeUnblock -w /youtubeUnblock builder make kmake KERNEL_BUILDER_MAKEDIR:=/linux + tar -czvf kmod-youtubeUnblock-$VERSION-$SHA-linux-${{ matrix.kernel_version }}.tar.gz kyoutubeUnblock.ko - name: Upload artifacts if: steps.build.outcome == 'success' uses: actions/upload-artifact@v4 with: - name: kmod-youtubeUnblock-${{ matrix.arch }} + name: kmod-youtubeUnblock-linux-${{ matrix.kernel_version }} path: ./**/kmod-youtubeUnblock*.tar.gz diff --git a/tls.c b/tls.c index b459ef0..962f776 100644 --- a/tls.c +++ b/tls.c @@ -275,8 +275,9 @@ int gen_fake_sni(struct fake_type type, if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { #ifdef KERNEL_SPACE - // get_random_bytes(&data_len, sizeof(data_len)); - data_len = get_random_u32() % 1200; + get_random_bytes(&data_len, sizeof(data_len)); + data_len = data_len % 1200; + // data_len = get_random_u32() % 1200; #else data_len = random() % 1200; #endif From 263a04bb9578024d8a0032e26d87b80e33c8cf7c Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 28 Sep 2024 22:17:11 +0300 Subject: [PATCH 05/14] Kernel module code cleanup --- Kbuild | 2 +- kmod_utils.c | 226 ---------------------------------------- kmod_utils.h | 14 --- kytunblock.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++- nf_wrapper.h | 84 --------------- 5 files changed, 284 insertions(+), 329 deletions(-) delete mode 100644 kmod_utils.c delete mode 100644 kmod_utils.h delete mode 100644 nf_wrapper.h diff --git a/Kbuild b/Kbuild index 1290c84..36eef22 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o -kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o kargs.o tls.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/kmod_utils.c b/kmod_utils.c deleted file mode 100644 index 12ca4ac..0000000 --- a/kmod_utils.c +++ /dev/null @@ -1,226 +0,0 @@ -#ifndef KERNEL_SPACE -#error "You are trying to compile the kernel module not in the kernel space" -#endif -#include "kmod_utils.h" - -#include -#include -#include -#include -#include - -#include "config.h" -#include "utils.h" -#include "logging.h" - -static struct socket *rawsocket; - -static struct socket *raw6socket; - - -int open_raw_socket(void) { - int ret = 0; - ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket); - - if (ret < 0) { - pr_alert("Unable to create raw socket\n"); - goto err; - } - - // That's funny, but this is how it is done in the kernel - // https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916 - rawsocket->sk->sk_mark=config.mark; - - return 0; - -err: - return ret; -} - -void close_raw_socket(void) { - sock_release(rawsocket); -} - -static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { - int ret = 0; - if (pktlen > AVAILABLE_MTU) return -ENOMEM; - - struct iphdr *iph; - - if ((ret = ip4_payload_split( - (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { - return ret; - } - - struct sockaddr_in daddr = { - .sin_family = AF_INET, - .sin_port = 0, - .sin_addr = { - .s_addr = iph->daddr - } - }; - - struct msghdr msg; - struct kvec iov; - - memset(&msg, 0, sizeof(msg)); - - iov.iov_base = (__u8 *)pkt; - iov.iov_len = pktlen; - - msg.msg_flags = 0; - msg.msg_name = &daddr; - msg.msg_namelen = sizeof(struct sockaddr_in); - msg.msg_control = NULL; - msg.msg_controllen = 0; - - - ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); - - return ret; -} - -int open_raw6_socket(void) { - int ret = 0; - ret = sock_create(AF_INET6, SOCK_RAW, IPPROTO_RAW, &raw6socket); - - if (ret < 0) { - pr_alert("Unable to create raw socket\n"); - goto err; - } - - // That's funny, but this is how it is done in the kernel - // https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916 - raw6socket->sk->sk_mark=config.mark; - - return 0; - -err: - return ret; -} - -void close_raw6_socket(void) { - sock_release(raw6socket); -} - -int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { - int ret = 0; - if (pktlen > AVAILABLE_MTU) return -ENOMEM; - - struct ip6_hdr *iph; - - if ((ret = ip6_payload_split( - (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { - return ret; - } - - struct sockaddr_in6 daddr = { - .sin6_family = AF_INET6, - /* Always 0 for raw socket */ - .sin6_port = 0, - .sin6_addr = iph->ip6_dst - }; - - struct kvec iov; - struct msghdr msg; - memset(&msg, 0, sizeof(msg)); - - iov.iov_base = (__u8 *)pkt; - iov.iov_len = pktlen; - - msg.msg_flags = 0; - msg.msg_name = &daddr; - msg.msg_namelen = sizeof(struct sockaddr_in6); - msg.msg_control = NULL; - msg.msg_controllen = 0; - - ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen); - - return ret; -} - -int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { - int ret; - - if (pktlen > AVAILABLE_MTU) { - lgdebug("The packet is too big and may cause issues!"); - - NETBUF_ALLOC(buff1, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buff1)) { - lgerror("Allocation error", -ENOMEM); - return -ENOMEM; - } - NETBUF_ALLOC(buff2, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buff2)) { - lgerror("Allocation error", -ENOMEM); - NETBUF_FREE(buff2); - return -ENOMEM; - } - uint32_t buff1_size = MAX_PACKET_SIZE; - uint32_t buff2_size = MAX_PACKET_SIZE; - - switch (config.fragmentation_strategy) { - case FRAG_STRAT_TCP: - if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) { - - goto erret_lc; - } - break; - case FRAG_STRAT_IP: - if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) { - - goto erret_lc; - } - break; - default: - pr_info("send_raw_socket: Packet is too big but fragmentation is disabled!"); - ret = -EINVAL; - goto erret_lc; - } - - int sent = 0; - ret = send_raw_socket(buff1, buff1_size); - - if (ret >= 0) sent += ret; - else { - goto erret_lc; - } - - ret = send_raw_socket(buff2, buff2_size); - if (ret >= 0) sent += ret; - else { - goto erret_lc; - } - - NETBUF_FREE(buff1); - NETBUF_FREE(buff2); - return sent; -erret_lc: - NETBUF_FREE(buff1); - NETBUF_FREE(buff2); - return ret; - } - - int ipvx = netproto_version(pkt, pktlen); - - if (ipvx == IP4VERSION) - return send_raw_ipv4(pkt, pktlen); - - else if (ipvx == IP6VERSION) - return send_raw_ipv6(pkt, pktlen); - - printf("proto version %d is unsupported\n", ipvx); - return -EINVAL; -} - -void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { - pr_info("delay_packet_send won't work on current youtubeUnblock version"); - send_raw_socket(data, data_len); -} - -struct instance_config_t instance_config = { - .send_raw_packet = send_raw_socket, - .send_delayed_packet = delay_packet_send, -}; diff --git a/kmod_utils.h b/kmod_utils.h deleted file mode 100644 index ade128e..0000000 --- a/kmod_utils.h +++ /dev/null @@ -1,14 +0,0 @@ -#include "types.h" - -#ifndef KMOD_UTILS_H -#define KMOD_UTILS_H - -int open_raw_socket(void); -void close_raw_socket(void); -int open_raw6_socket(void); -void close_raw6_socket(void); -int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen); -int send_raw_socket(const uint8_t *pkt, uint32_t pktlen); -void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms); - -#endif /* KMOD_UTILS_H */ diff --git a/kytunblock.c b/kytunblock.c index 31f1c54..ff68d36 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -1,14 +1,16 @@ -#include "nf_wrapper.h" #ifndef KERNEL_SPACE #error "You are trying to compile the kernel module not in the kernel space" #endif + // Kernel module for youtubeUnblock. -// Make with make kmake && sudo iptables -t mangle -D OUTPUT 1 && sudo make kreload && sudo iptables -t mangle -I OUTPUT -p tcp -j YTUNBLOCK +// Build with make kmake #include #include #include #include #include +#include +#include #include #include @@ -18,12 +20,289 @@ #include "config.h" #include "utils.h" #include "logging.h" -#include "kmod_utils.h" MODULE_LICENSE("GPL"); MODULE_VERSION("0.3.2"); MODULE_AUTHOR("Vadim Vetrov "); -MODULE_DESCRIPTION("Linux kernel module for youtube unblock"); +MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock"); + +static struct socket *rawsocket; + +static struct socket *raw6socket; + +static int open_raw_socket(void) { + int ret = 0; + ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket); + + if (ret < 0) { + pr_alert("Unable to create raw socket\n"); + goto err; + } + + // That's funny, but this is how it is done in the kernel + // https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916 + rawsocket->sk->sk_mark=config.mark; + + return 0; + +err: + return ret; +} + +static void close_raw_socket(void) { + sock_release(rawsocket); +} + +static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) { + int ret = 0; + if (pktlen > AVAILABLE_MTU) return -ENOMEM; + + struct iphdr *iph; + + if ((ret = ip4_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + return ret; + } + + struct sockaddr_in daddr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr = { + .s_addr = iph->daddr + } + }; + + struct msghdr msg; + struct kvec iov; + + memset(&msg, 0, sizeof(msg)); + + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + + msg.msg_flags = 0; + msg.msg_name = &daddr; + msg.msg_namelen = sizeof(struct sockaddr_in); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + + ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen); + + return ret; +} + +static int open_raw6_socket(void) { + int ret = 0; + ret = sock_create(AF_INET6, SOCK_RAW, IPPROTO_RAW, &raw6socket); + + if (ret < 0) { + pr_alert("Unable to create raw socket\n"); + goto err; + } + + // That's funny, but this is how it is done in the kernel + // https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916 + raw6socket->sk->sk_mark=config.mark; + + return 0; + +err: + return ret; +} + +static void close_raw6_socket(void) { + sock_release(raw6socket); +} + +static int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) { + int ret = 0; + if (pktlen > AVAILABLE_MTU) return -ENOMEM; + + struct ip6_hdr *iph; + + if ((ret = ip6_payload_split( + (uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) { + return ret; + } + + struct sockaddr_in6 daddr = { + .sin6_family = AF_INET6, + /* Always 0 for raw socket */ + .sin6_port = 0, + .sin6_addr = iph->ip6_dst + }; + + struct kvec iov; + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + + iov.iov_base = (__u8 *)pkt; + iov.iov_len = pktlen; + + msg.msg_flags = 0; + msg.msg_name = &daddr; + msg.msg_namelen = sizeof(struct sockaddr_in6); + msg.msg_control = NULL; + msg.msg_controllen = 0; + + ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen); + + return ret; +} + +static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { + int ret; + + if (pktlen > AVAILABLE_MTU) { + lgdebug("The packet is too big and may cause issues!"); + + NETBUF_ALLOC(buff1, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buff1)) { + lgerror("Allocation error", -ENOMEM); + return -ENOMEM; + } + NETBUF_ALLOC(buff2, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buff2)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(buff2); + return -ENOMEM; + } + uint32_t buff1_size = MAX_PACKET_SIZE; + uint32_t buff2_size = MAX_PACKET_SIZE; + + switch (config.fragmentation_strategy) { + case FRAG_STRAT_TCP: + if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { + + goto erret_lc; + } + break; + case FRAG_STRAT_IP: + if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { + + goto erret_lc; + } + break; + default: + pr_info("send_raw_socket: Packet is too big but fragmentation is disabled!"); + ret = -EINVAL; + goto erret_lc; + } + + int sent = 0; + ret = send_raw_socket(buff1, buff1_size); + + if (ret >= 0) sent += ret; + else { + goto erret_lc; + } + + ret = send_raw_socket(buff2, buff2_size); + if (ret >= 0) sent += ret; + else { + goto erret_lc; + } + + NETBUF_FREE(buff1); + NETBUF_FREE(buff2); + return sent; +erret_lc: + NETBUF_FREE(buff1); + NETBUF_FREE(buff2); + return ret; + } + + int ipvx = netproto_version(pkt, pktlen); + + if (ipvx == IP4VERSION) + return send_raw_ipv4(pkt, pktlen); + + else if (ipvx == IP6VERSION) + return send_raw_ipv6(pkt, pktlen); + + printf("proto version %d is unsupported\n", ipvx); + return -EINVAL; +} + +static void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { + pr_info("delay_packet_send won't work on current youtubeUnblock version"); + send_raw_socket(data, data_len); +} + +struct instance_config_t instance_config = { + .send_raw_packet = send_raw_socket, + .send_delayed_packet = delay_packet_send, +}; + + +/* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */ +#ifdef RHEL_RELEASE_CODE + +#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 2) +#define NF_CALLBACK(name, skb) unsigned int name( \ + const struct nf_hook_ops *ops, \ + struct sk_buff *skb, \ + const struct net_device *in, \ + const struct net_device *out, \ + const struct nf_hook_state *state) \ + +#elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + const struct nf_hook_ops *ops, \ + struct sk_buff *skb, \ + const struct net_device *in, \ + const struct net_device *out, \ + int (*okfn)(struct sk_buff *)) + +#else + +#error "Sorry; this version of RHEL is not supported because it's kind of old." + +#endif /* RHEL_RELEASE_CODE >= x */ + + +/* If this NOT a RedHat-based kernel (Ubuntu, Debian, SuSE, etc)... */ +#else + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + void *priv, \ + struct sk_buff *skb, \ + const struct nf_hook_state *state) + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + const struct nf_hook_ops *ops, \ + struct sk_buff *skb, \ + const struct nf_hook_state *state) + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + const struct nf_hook_ops *ops, \ + struct sk_buff *skb, \ + const struct net_device *in, \ + const struct net_device *out, \ + int (*okfn)(struct sk_buff *)) + +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) +#define NF_CALLBACK(name, skb) unsigned int name( \ + unsigned int hooknum, \ + struct sk_buff *skb, \ + const struct net_device *in, \ + const struct net_device *out, \ + int (*okfn)(struct sk_buff *)) + +#else +#error "Linux < 3.0 isn't supported at all." + +#endif /* LINUX_VERSION_CODE > n */ + +#endif /* RHEL or not RHEL */ + + static NF_CALLBACK(ykb_nf_hook, skb) { int ret; diff --git a/nf_wrapper.h b/nf_wrapper.h deleted file mode 100644 index a254a6d..0000000 --- a/nf_wrapper.h +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Thanks https://github.com/NICMx/Jool/blob/5f60dcda5944b01cc43c3be342aad26af8161bcb/include/nat64/mod/common/nf_wrapper.h for mapped kernel versions - */ -#ifndef _JOOL_MOD_NF_WRAPPER_H -#define _JOOL_MOD_NF_WRAPPER_H - -/** - * @file - * The kernel API is far from static. In particular, the Netfilter packet entry - * function keeps changing. nf_hook.c, the file where we declare our packet - * entry function, has been quite difficult to read for a while now. It's pretty - * amusing, because we don't even use any of the noisy arguments. - * - * This file declares a usable function header that abstracts away all those - * useless arguments. - */ - -#include - -/* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */ -#ifdef RHEL_RELEASE_CODE - -#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 2) -#define NF_CALLBACK(name, skb) unsigned int name( \ - const struct nf_hook_ops *ops, \ - struct sk_buff *skb, \ - const struct net_device *in, \ - const struct net_device *out, \ - const struct nf_hook_state *state) \ - -#elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0) -#define NF_CALLBACK(name, skb) unsigned int name( \ - const struct nf_hook_ops *ops, \ - struct sk_buff *skb, \ - const struct net_device *in, \ - const struct net_device *out, \ - int (*okfn)(struct sk_buff *)) - -#else - -#error "Sorry; this version of RHEL is not supported because it's kind of old." - -#endif /* RHEL_RELEASE_CODE >= x */ - - -/* If this NOT a RedHat-based kernel (Ubuntu, Debian, SuSE, etc)... */ -#else - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) -#define NF_CALLBACK(name, skb) unsigned int name( \ - void *priv, \ - struct sk_buff *skb, \ - const struct nf_hook_state *state) - -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) -#define NF_CALLBACK(name, skb) unsigned int name( \ - const struct nf_hook_ops *ops, \ - struct sk_buff *skb, \ - const struct nf_hook_state *state) - -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) -#define NF_CALLBACK(name, skb) unsigned int name( \ - const struct nf_hook_ops *ops, \ - struct sk_buff *skb, \ - const struct net_device *in, \ - const struct net_device *out, \ - int (*okfn)(struct sk_buff *)) - -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0) -#define NF_CALLBACK(name, skb) unsigned int name( \ - unsigned int hooknum, \ - struct sk_buff *skb, \ - const struct net_device *in, \ - const struct net_device *out, \ - int (*okfn)(struct sk_buff *)) - -#else -#error "Linux < 3.0 isn't supported at all." - -#endif /* LINUX_VERSION_CODE > n */ - -#endif /* RHEL or not RHEL */ - -#endif /* _JOOL_MOD_NF_WRAPPER_H */ From 666b3575fc5b8d5c7f14009fda6816427732f427 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 28 Sep 2024 22:45:35 +0300 Subject: [PATCH 06/14] Fix kmod fake, update kmod verbosity settings --- README.md | 4 ++- kargs.c | 67 ++++++++++++++++++++++++++++++++++------------ raw_replacements.h | 4 +-- 3 files changed, 55 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 0f799e6..5c9f926 100644 --- a/README.md +++ b/README.md @@ -319,7 +319,9 @@ You can configure the module with its flags in insmod: insmod kyoutubeUnblock.ko fake_sni=1 exclude_domains=.ru quic_drop=1 ``` -Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`silent=1 quic_drop=1`) +Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`quic_drop=1`) + +Also a good thig to mention is verbosity. The kernel module combines --trace and --silent option to the one parameter `verbosity`. This parameter accepts 3 arguments: `trace`, `debug` and `silent`. I highly don't recommend to enable `trace` mod on router because it may cause huge problems with performance and even freeze your device. Also a drop in replacement is supported for all the parameters excluding packet mark. A drop in replacement does not require module restart if you want to change the parameters. You can specify and check the parameters within module's directory inside the sysfs: `/sys/module/kyoutubeUnblock/parameters/`. For example, to set quic_drop to true you may use next command: ```sh diff --git a/kargs.c b/kargs.c index 00ed9c0..345da77 100644 --- a/kargs.c +++ b/kargs.c @@ -2,6 +2,9 @@ #include "raw_replacements.h" #include "types.h" #include +#include "types.h" + +static char custom_fake_buf[MAX_FAKE_SIZE]; #define STR_MAXLEN 2048 @@ -20,6 +23,8 @@ struct config_t config = { .mark = DEFAULT_RAWSOCKET_MARK, .synfake = 0, .synfake_len = 0, + .fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT, + .fake_sni_type = FAKE_PAYLOAD_DEFAULT, .sni_detection = SNI_DETECTION_PARSE, @@ -47,6 +52,8 @@ struct config_t config = { .queue_start_num = DEFAULT_QUEUE_NUM, .fake_sni_pkt = fake_sni_old, .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator + .fake_custom_pkt = custom_fake_buf, + .fake_custom_pkt_sz = 0 }; static int unumeric_set(const char *val, const struct kernel_param *kp) { @@ -187,33 +194,59 @@ static const struct kernel_param_ops exclude_domains_ops = { module_param_cb(exclude_domains, &exclude_domains_ops, &config.exclude_domains_str, 0664); module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664); -module_param_cb(silent, &inverse_boolean_ops, &config.verbose, 0664); module_param_cb(quic_drop, &boolean_parameter_ops, &config.quic_drop, 0664); -static int verbose_trace_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; - ret = kstrtoint(val, 10, &n); - if (ret != 0 || (n != 0 && n != 1)) - return -EINVAL; +static int verbosity_set(const char *val, const struct kernel_param *kp) { + size_t len; - if (n) { - n = VERBOSE_TRACE; - } else { - n = VERBOSE_DEBUG; + len = strnlen(val, STR_MAXLEN + 1); + if (len == STR_MAXLEN + 1) { + pr_err("%s: string parameter too long\n", kp->name); + return -ENOSPC; + } + + if (len >= 1 && val[len - 1] == '\n') { + len--; + } + + if (strncmp(val, "trace", len) == 0) { + *(int *)kp->arg = VERBOSE_TRACE; + } else if (strncmp(val, "debug", len) == 0) { + *(int *)kp->arg = VERBOSE_DEBUG; + } else if (strncmp(val, "silent", len) == 0) { + *(int *)kp->arg = VERBOSE_INFO; + } else { + return -EINVAL; } - if (kp->arg == NULL) - return -EINVAL; - *(int *)kp->arg = n; return 0; } -static const struct kernel_param_ops verbose_trace_ops = { - .set = verbose_trace_set, - .get = param_get_int, + +static int verbosity_get(char *buffer, const struct kernel_param *kp) { + switch (*(int *)kp->arg) { + case VERBOSE_TRACE: + strcpy(buffer, "trace\n"); + break; + case VERBOSE_DEBUG: + strcpy(buffer, "debug\n"); + break; + case VERBOSE_INFO: + strcpy(buffer, "silent\n"); + break; + default: + strcpy(buffer, "unknown\n"); + } + + return strlen(buffer); +} + +static const struct kernel_param_ops verbosity_ops = { + .set = verbosity_set, + .get = verbosity_get, }; -module_param_cb(trace, &verbose_trace_ops, &config.verbose, 0664); +module_param_cb(verbosity, &verbosity_ops, &config.verbose, 0664); static int frag_strat_set(const char *val, const struct kernel_param *kp) { size_t len; diff --git a/raw_replacements.h b/raw_replacements.h index 8dde7ff..4123bd8 100644 --- a/raw_replacements.h +++ b/raw_replacements.h @@ -3,8 +3,8 @@ #define FAKE_SNI_MAXLEN 1500 -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"; +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"; +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 9dc40bbdf1eae811bd7bf17b5319aaf4118c8954 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 28 Sep 2024 23:06:45 +0300 Subject: [PATCH 07/14] Fix split on preset domain list --- args.c | 13 +------------ mangle.c | 10 +++++++--- tls.c | 6 ++++-- tls.h | 1 + 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/args.c b/args.c index 142d969..c720afa 100644 --- a/args.c +++ b/args.c @@ -385,18 +385,7 @@ int parse_args(int argc, char *argv[]) { config.fake_custom_pkt_sz = custom_len; config.fake_custom_pkt = (char *)custom_buf; - - // if (strcmp(optarg, "default") == 0) { - // config.fake_sni_type = FAKE_PAYLOAD_DEFAULT; - // } else if (strcmp(optarg, "random") == 0) { - // config.fake_sni_type = FAKE_PAYLOAD_RANDOM; - // } else if (strcmp(optarg, "custom") == 0) { - // config.fake_sni_type = FAKE_PAYLOAD_CUSTOM; - // } else { - // goto invalid_opt; - // } - // - } + } break; case OPT_FK_WINSIZE: num = parse_numeric_option(optarg); diff --git a/mangle.c b/mangle.c index 09b6c30..28bc3b5 100644 --- a/mangle.c +++ b/mangle.c @@ -139,7 +139,11 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (tcph->syn) goto accept; struct tls_verdict vrd = analyze_tls_data(data, dlen); - lgtrace_addp("Analyzed, %d", vrd.target_sni); + lgtrace_addp("Analyzed"); + + if (vrd.sni_len != 0) { + lgtrace_addp("SNI detected: %.*s", vrd.sni_len, data + vrd.sni_offset); + } if (vrd.target_sni) { lgdebugmsg("Target SNI detected: %.*s", vrd.sni_len, data + vrd.sni_offset); @@ -191,7 +195,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { switch (config.fragmentation_strategy) { case FRAG_STRAT_TCP: { ipd_offset = vrd.sni_target_offset; - mid_offset = ipd_offset + vrd.sni_len / 2; + mid_offset = ipd_offset + vrd.sni_target_len / 2; uint32_t poses[2]; int cnt = 0; @@ -222,7 +226,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { case FRAG_STRAT_IP: if (ipxv == IP4VERSION) { ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_target_offset; - mid_offset = ipd_offset + vrd.sni_len / 2; + mid_offset = ipd_offset + vrd.sni_target_len / 2; mid_offset += 8 - mid_offset % 8; uint32_t poses[2]; diff --git a/tls.c b/tls.c index 962f776..d19db51 100644 --- a/tls.c +++ b/tls.c @@ -124,6 +124,7 @@ struct tls_verdict analyze_tls_data( vrd.sni_offset = (uint8_t *)sni_name - data; vrd.sni_target_offset = vrd.sni_offset; vrd.sni_len = sni_len; + vrd.sni_target_len = vrd.sni_len; if (config.all_domains) { vrd.target_sni = 1; @@ -149,6 +150,7 @@ struct tls_verdict analyze_tls_data( domain_len)) { vrd.target_sni = 1; vrd.sni_target_offset = (const uint8_t *)sni_startp - data; + vrd.sni_target_len = domain_len; goto check_domain; } @@ -249,6 +251,7 @@ brute: vrd.sni_len = domain_len; vrd.sni_offset = (k - domain_len - 1); vrd.sni_target_offset = vrd.sni_offset; + vrd.sni_target_len = vrd.sni_len; NETBUF_FREE(buf); NETBUF_FREE(nzbuf); goto out; @@ -277,7 +280,6 @@ int gen_fake_sni(struct fake_type type, get_random_bytes(&data_len, sizeof(data_len)); data_len = data_len % 1200; - // data_len = get_random_u32() % 1200; #else data_len = random() % 1200; #endif @@ -326,7 +328,7 @@ int gen_fake_sni(struct fake_type type, break; default: // FAKE_PAYLOAD_RANDOM #ifdef KERNEL_SPACE - get_random_bytes(bfdptr, data_len); + get_random_bytes(bfdptr, data_len); #else getrandom(bfdptr, data_len, 0); #endif diff --git a/tls.h b/tls.h index bcc7cab..fe151ff 100644 --- a/tls.h +++ b/tls.h @@ -11,6 +11,7 @@ struct tls_verdict { int target_sni; /* google video hello packet */ int sni_offset; /* offset from start of tcp _payload_ */ int sni_target_offset; /* offset of target domain instead of entire sni */ + int sni_target_len; /* offset of target domain instead of entire sni */ int sni_len; }; From 30bc3a8d3fe869b36fa2c79025c0661ab17083cb Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 11 Oct 2024 21:33:56 +0300 Subject: [PATCH 08/14] Update workflow for separate luci-app-youtubeUnblock --- .github/workflows/build-ci.yml | 43 ++++++++++++++++++++++++++++++++++ README.md | 4 +++- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-ci.yml b/.github/workflows/build-ci.yml index 6fb00af..2a30701 100644 --- a/.github/workflows/build-ci.yml +++ b/.github/workflows/build-ci.yml @@ -228,6 +228,48 @@ jobs: path: /builder/youtubeUnblock*.ipk if-no-files-found: error + build-openwrt-luci: + needs: prepare + runs-on: ubuntu-latest + container: + image: openwrt/sdk:x86_64-openwrt-23.05 + options: --user root + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: 'openwrt' + + - name: Prepare build + env: + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + run: | + sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" youtubeUnblock/Makefile + + - name: Build packages + id: build + env: + VERSION: ${{ needs.prepare.outputs.version }} + SHA: ${{ needs.prepare.outputs.sha }} + working-directory: /builder + run: | + echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf + cat feeds.conf + ./scripts/feeds update youtubeUnblock + ./scripts/feeds install -a -p youtubeUnblock + make defconfig + make package/luci-app-youtubeUnblock/compile V=s + mv $(find ./bin -type f -name 'luci-app-youtubeUnblock*.ipk') ./luci-app-youtubeUnblock-$VERSION-$SHA.ipk + + - name: Upload packages + if: steps.build.outcome == 'success' + uses: actions/upload-artifact@v4 + with: + name: luci-app-youtubeUnblock + path: /builder/luci-app-youtubeUnblock*.ipk + if-no-files-found: error + build-entware: needs: prepare runs-on: ubuntu-latest @@ -341,3 +383,4 @@ jobs: files: | ./**/youtubeUnblock*.ipk ./**/youtubeUnblock*.tar.gz + ./**/luci-app-youtubeUnblock*.ipk diff --git a/README.md b/README.md index 5c9f926..36cdb13 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,9 @@ For nftables on OpenWRT rules comes out-of-the-box and stored under `/usr/share/ Now we go to the configuration. For OpenWRT here is configuration via [UCI](https://openwrt.org/docs/guide-user/base-system/uci) and [LuCI](https://openwrt.org/docs/guide-user/luci/start) available (CLI and GUI respectively). -Luci is a configuration interface for your router (which you connect when enter 192.168.1.1 in browser). LuCI configuration lives in **Services->youtubeUnblock** section. It is self descriptive, with description for each flag. Note, that after you push `Save & Apply` button, the configuration is applied automatically and the service is restarted. +For **LuCI** aka **GUI** aka **web-interface of router** you should install luci-app-youtubeUnblock package like you did it with the normal youtubeUnblock package. Note, that lists of official opkg feeds should be loaded (**Do it with Update lists option**). + +LuCI configuration lives in **Services->youtubeUnblock** section. It is self descriptive, with description for each flag. Note, that after you push `Save & Apply` button, the configuration is applied automatically and the service is restarted. UCI configuration is available in /etc/config/youtubeUnblock file, in section `youtubeUnblock.youtubeUnblock`. The configuration is done with [flags](#flags). Note, that names of flags are not the same: you should replace `-` with `_`, you shouldn't use leading `--` for flag. Also you will enable toggle flags (without parameters) with `1`. From e9b033cccaea0f4c75ad08564cb6fc294f9ffa62 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 12 Oct 2024 12:19:33 +0300 Subject: [PATCH 09/14] Update faking strategies Use random ip4 id for frags, use sequential ip4 id for fakes --- README.md | 6 +- args.c | 18 +-- args.h | 4 + config.h | 3 +- kargs.c | 1 - kytunblock.c | 17 ++- mangle.c | 351 +++++++++++++++++++++++++---------------------- mangle.h | 9 +- tls.c | 14 +- tls.h | 23 +--- types.h | 14 ++ utils.c | 103 ++++++++++---- utils.h | 81 ++++++++++- youtubeUnblock.c | 23 ++-- 14 files changed, 397 insertions(+), 270 deletions(-) diff --git a/README.md b/README.md index 36cdb13..cd65677 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ For nftables on OpenWRT rules comes out-of-the-box and stored under `/usr/share/ Now we go to the configuration. For OpenWRT here is configuration via [UCI](https://openwrt.org/docs/guide-user/base-system/uci) and [LuCI](https://openwrt.org/docs/guide-user/luci/start) available (CLI and GUI respectively). -For **LuCI** aka **GUI** aka **web-interface of router** you should install luci-app-youtubeUnblock package like you did it with the normal youtubeUnblock package. Note, that lists of official opkg feeds should be loaded (**Do it with Update lists option**). +For **LuCI** aka **GUI** aka **web-interface of router** you should install **luci-app-youtubeUnblock** package like you did it with the normal youtubeUnblock package. Note, that lists of official opkg feeds should be loaded (**Do it with Update lists option**). LuCI configuration lives in **Services->youtubeUnblock** section. It is self descriptive, with description for each flag. Note, that after you push `Save & Apply` button, the configuration is applied automatically and the service is restarted. @@ -183,6 +183,10 @@ 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**. +- `--fake-sni-type={default|custom|random}` This flag specifies which faking messages should use fake packets. If you pass random, the message of random length and random payload will be sent. For default the default payload is used. And for custom the payload from `--fake-custom-payload` is used. Default to `default`. + +- `--fake-custom-payload=` Usable with `--fake-sni-type=custom`. You should specify the payload for fake message manually. Use hex format: `--fake-custom-payload=0001020304` mean that 5 bytes sequence: `0x00`, `0x01`, `0x02`, `0x03`, `0x04` used as fake. + - `--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}` 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. diff --git a/args.c b/args.c index c720afa..9243103 100644 --- a/args.c +++ b/args.c @@ -8,6 +8,7 @@ #include #include #include "types.h" +#include "args.h" static char custom_fake_buf[MAX_FAKE_SIZE]; @@ -20,7 +21,6 @@ struct config_t config = { .faking_ttl = FAKE_TTL, .fake_sni = 1, .fake_sni_seq_len = 1, - .fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT, .fake_sni_type = FAKE_PAYLOAD_DEFAULT, .frag_middle_sni = 1, .frag_sni_pos = 1, @@ -69,7 +69,6 @@ struct config_t config = { #define OPT_FAKING_TTL 3 #define OPT_FAKING_STRATEGY 10 #define OPT_FAKE_SNI_SEQ_LEN 11 -#define OPT_FAKE_SNI_SEQ_TYPE 26 #define OPT_FAKE_SNI_TYPE 27 #define OPT_FAKE_CUSTOM_PAYLOAD 28 #define OPT_FRAG 4 @@ -103,7 +102,6 @@ static struct option long_opt[] = { {"synfake", 1, 0, OPT_SYNFAKE}, {"synfake-len", 1, 0, OPT_SYNFAKE_LEN}, {"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN}, - {"fake-sni-seq-type", 1, 0, OPT_FAKE_SNI_SEQ_TYPE}, {"fake-sni-type", 1, 0, OPT_FAKE_SNI_TYPE}, {"fake-custom-payload", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD}, {"faking-strategy", 1, 0, OPT_FAKING_STRATEGY}, @@ -162,7 +160,6 @@ void print_usage(const char *argv0) { printf("\t--exclude-domains=\n"); printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni-seq-len=\n"); - printf("\t--fake-sni-seq-type={default|random|custom}\n"); printf("\t--fake-sni-type={default|random|custom}\n"); printf("\t--fake-custom-payload=\n"); printf("\t--fake-seq-offset=\n"); @@ -337,18 +334,6 @@ int parse_args(int argc, char *argv[]) { } config.fake_sni_seq_len = num; - break; - case OPT_FAKE_SNI_SEQ_TYPE: - if (strcmp(optarg, "default") == 0) { - config.fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT; - } else if (strcmp(optarg, "random") == 0) { - config.fake_sni_seq_type = FAKE_PAYLOAD_RANDOM; - } else if (strcmp(optarg, "custom") == 0) { - config.fake_sni_seq_type = FAKE_PAYLOAD_CUSTOM; - } else { - goto invalid_opt; - } - break; case OPT_FAKE_SNI_TYPE: if (strcmp(optarg, "default") == 0) { @@ -553,3 +538,4 @@ void print_welcome() { } } + diff --git a/args.h b/args.h index 98d65cf..0c3d52e 100644 --- a/args.h +++ b/args.h @@ -1,3 +1,6 @@ +#include "utils.h" +#include "tls.h" + #ifndef ARGS_H #define ARGS_H @@ -8,4 +11,5 @@ int parse_args(int argc, char *argv[]); /* Prints starting messages */ void print_welcome(); + #endif /* ARGS_H */ diff --git a/config.h b/config.h index 43fd9f8..8d0d55d 100644 --- a/config.h +++ b/config.h @@ -10,7 +10,7 @@ typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len); * Sends the packet after delay_ms. The function should schedule send and return immediately * (for example, open daemon thread) */ -typedef void (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms); +typedef int (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms); struct instance_config_t { raw_send_t send_raw_packet; @@ -37,7 +37,6 @@ struct config_t { #define FAKE_PAYLOAD_CUSTOM 1 // In default mode all other options will be skipped. #define FAKE_PAYLOAD_DEFAULT 2 - int fake_sni_seq_type; int fake_sni_type; #define VERBOSE_INFO 0 diff --git a/kargs.c b/kargs.c index 345da77..abc55f7 100644 --- a/kargs.c +++ b/kargs.c @@ -23,7 +23,6 @@ struct config_t config = { .mark = DEFAULT_RAWSOCKET_MARK, .synfake = 0, .synfake_len = 0, - .fake_sni_seq_type = FAKE_PAYLOAD_DEFAULT, .fake_sni_type = FAKE_PAYLOAD_DEFAULT, .sni_detection = SNI_DETECTION_PARSE, diff --git a/kytunblock.c b/kytunblock.c index ff68d36..aa01183 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -217,19 +217,22 @@ erret_lc: int ipvx = netproto_version(pkt, pktlen); - if (ipvx == IP4VERSION) + if (ipvx == IP4VERSION) { return send_raw_ipv4(pkt, pktlen); - - else if (ipvx == IP6VERSION) + } else if (ipvx == IP6VERSION) { return send_raw_ipv6(pkt, pktlen); + } else { + printf("proto version %d is unsupported\n", ipvx); + return -EINVAL; + } - printf("proto version %d is unsupported\n", ipvx); - return -EINVAL; + lgtrace_addp("raw_sock_send: %d", ret); + return ret; } -static void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { +static int delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { pr_info("delay_packet_send won't work on current youtubeUnblock version"); - send_raw_socket(data, data_len); + return send_raw_socket(data, data_len); } struct instance_config_t instance_config = { diff --git a/mangle.c b/mangle.c index 28bc3b5..d28cbc5 100644 --- a/mangle.c +++ b/mangle.c @@ -175,18 +175,27 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { if (config.fk_winsize) { tcph->window = htons(config.fk_winsize); + set_tcp_checksum(tcph, iph, iph_len); + } + + if (0) { + int delta = 2; + ret = seqovl_packet(payload, &payload_len, delta); + int ret = tcp_payload_split(payload, payload_len, + &iph, &iph_len, &tcph, &tcph_len, + &data, &dlen); + if (ret < 0) { + lgerror("seqovl_packet delta %d", ret, delta); + } } - set_ip_checksum(iph, iph_len); - set_tcp_checksum(tcph, iph, iph_len); if (dlen > 1480 && config.verbose) { lgdebugmsg("WARNING! Client Hello packet is too big and may cause issues!"); } if (config.fake_sni) { - post_fake_sni(iph, iph_len, tcph, tcph_len, - config.fake_sni_seq_len); + post_fake_sni(args_default_fake_type(), iph, iph_len, tcph, tcph_len); } size_t ipd_offset; @@ -324,39 +333,75 @@ int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) { 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) { + 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_quic; + } + + lgtrace_addp("QUIC detected"); + uint8_t qtype = qch->type; + 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_quic; + } + + lgtrace_addp("quic initial message"); } - if (qch->version == QUIC_V1) - qtype = quic_convtype_v1(qtype); - else if (qch->version == QUIC_V2) - qtype = quic_convtype_v2(qtype); +accept_quic: + ; - if (qtype != QUIC_INITIAL_TYPE) { - lgtrace_addp("quic message type: %d", qtype); - goto accept; +/* + if (1) { + lgtrace_addp("Probe udp"); + if (ipver == IP4VERSION && ntohs(udph->dest) > 30) { + lgtrace_addp("udp fool"); + const uint8_t *payload; + uint32_t payload_len; + + uint32_t poses[10]; + int cnt = 3; + + poses[0] = 8; + for (int i = 1; i < cnt; i++) { + poses[i] = poses[i - 1] + 8; + } + + ret = send_ip4_frags(pkt, pktlen, poses, cnt, 0); + if (ret < 0) { + lgerror("ip4 send frags", ret); + goto accept; + } + + goto drop; + } else { + printf("WARNING: IP fragmentation is supported only for IPv4\n"); + goto accept; + } } - - lgtrace_addp("quic initial message"); +*/ + + accept: lgtrace_addp("accepted"); @@ -377,11 +422,13 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses return -EINVAL; } + lgtrace_addp("Sent %d delayed for %d", pktlen, config.seg2_delay); instance_config.send_delayed_packet( packet, pktlen, config.seg2_delay); return 0; } else { + lgtrace_addp("Sent %d bytes", pktlen); return instance_config.send_raw_packet( packet, pktlen); } @@ -399,8 +446,17 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses return -ENOMEM; } + NETBUF_ALLOC(fake_pad, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(fake_pad)) { + lgerror("Allocation error", -ENOMEM); + NETBUF_FREE(frag1); + NETBUF_FREE(frag2); + return -ENOMEM; + } + uint32_t f1len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE; + uint32_t fake_pad_len = MAX_PACKET_SIZE; int ret; @@ -410,7 +466,10 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses goto erret_lc; } - ret = ip4_frag(packet, pktlen, poses[0] - dvs, + uint32_t frag_pos = poses[0] - dvs; + frag_pos += 8 - frag_pos % 8; + + ret = ip4_frag(packet, pktlen, frag_pos, frag1, &f1len, frag2, &f2len); if (ret < 0) { @@ -418,6 +477,8 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses goto erret_lc; } + dvs += frag_pos; + if (config.frag_sni_reverse) goto send_frag2; send_frag1: @@ -429,23 +490,54 @@ send_frag1: if (config.frag_sni_reverse) goto out_lc; +send_fake: +/* + if (config.frag_sni_faked) { + ITER_FAKE_STRAT(config.faking_strategy, strategy) { + uint32_t iphfl; + fake_pad_len = f2len; + ret = ip4_payload_split(frag2, f2len, NULL, &iphfl, NULL, NULL); + if (ret < 0) { + lgerror("Invalid frag2", ret); + goto erret_lc; + } + memcpy(fake_pad, frag2, iphfl + sizeof(struct udphdr)); + memset(fake_pad + iphfl + sizeof(struct udphdr), 0, f2len - iphfl - sizeof(struct udphdr)); + ((struct iphdr *)fake_pad)->tot_len = htons(fake_pad_len); + ((struct iphdr *)fake_pad)->id = 1; + ((struct iphdr *)fake_pad)->ttl = 8; + ((struct iphdr *)fake_pad)->frag_off = 0; + ip4_set_checksum((struct iphdr*)fake_pad); + // *(struct udphdr *)(fake_pad + iphfl) = *(struct udphdr *)(frag2 + iphfl); + ret = send_ip4_frags(fake_pad, fake_pad_len, NULL, 0, 0); + if (ret < 0) { + goto erret_lc; + } + } + } +*/ + + if (config.frag_sni_reverse) + goto send_frag1; + send_frag2: - dvs += poses[0]; ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { goto erret_lc; } if (config.frag_sni_reverse) - goto send_frag1; + goto send_fake; out_lc: NETBUF_FREE(frag1); NETBUF_FREE(frag2); + NETBUF_FREE(fake_pad); goto out; erret_lc: NETBUF_FREE(frag1); NETBUF_FREE(frag2); + NETBUF_FREE(fake_pad); return ret; } @@ -483,18 +575,8 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses return -ENOMEM; } - NETBUF_ALLOC(fake_pad, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(fake_pad)) { - lgerror("Allocation error", -ENOMEM); - NETBUF_FREE(frag1); - NETBUF_FREE(frag2); - return -ENOMEM; - } - - uint32_t f1len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE; - uint32_t fake_pad_len = MAX_PACKET_SIZE; int ret; @@ -508,6 +590,7 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses ret = tcp_frag(packet, pktlen, poses[0] - dvs, frag1, &f1len, frag2, &f2len); + lgtrace_addp("Packet split in %d bytes position of payload start, dvs: %d to two packets of %d and %d lengths", poses[0], dvs, f1len, f2len); if (ret < 0) { @@ -516,6 +599,8 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses } + dvs += poses[0]; + if (config.frag_sni_reverse) goto send_frag2; @@ -532,33 +617,20 @@ send_frag1: send_fake: if (config.frag_sni_faked) { - ITER_FAKE_STRAT(config.faking_strategy, strategy) { - uint32_t iphfl, tcphfl; - fake_pad_len = f2len; - ret = tcp_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL); - if (ret < 0) { - lgerror("Invalid frag2", ret); - goto erret_lc; - } - memcpy(fake_pad, frag2, iphfl + tcphfl); - memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl); - struct tcphdr *fakethdr = (void *)(fake_pad + iphfl); - if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) { - lgtrace("frag fake sent with %u -> ", ntohl(fakethdr->seq)); - fakethdr->seq = htonl(ntohl(fakethdr->seq) - dvs); - lgtrace_addp("%u, ", ntohl(fakethdr->seq)); - } - ret = fail_packet(strategy, - fake_pad, &fake_pad_len, MAX_PACKET_SIZE); - if (ret < 0) { - lgerror("Failed to fail packet", ret); - goto erret_lc; - } - ret = send_tcp_frags(fake_pad, fake_pad_len, NULL, 0, 0); - if (ret < 0) { - goto erret_lc; - } + uint32_t iphfl, tcphfl; + void *iph; + struct tcphdr *tcph; + ret = tcp_payload_split(frag2, f2len, &iph, &iphfl, &tcph, &tcphfl, NULL, NULL); + struct fake_type f_type = args_default_fake_type(); + if ((f_type.strategy.strategy & FAKE_STRAT_PAST_SEQ) == FAKE_STRAT_PAST_SEQ) { + f_type.strategy.strategy ^= FAKE_STRAT_PAST_SEQ; + f_type.strategy.strategy |= FAKE_STRAT_RAND_SEQ; + f_type.strategy.randseq_offset = dvs; } + + f_type.seg2delay = config.seg2_delay; + + post_fake_sni(f_type, iph, iphfl, tcph, tcphfl); } if (config.frag_sni_reverse) @@ -566,7 +638,6 @@ send_fake: send_frag2: { - dvs += poses[0]; ret = send_tcp_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { goto erret_lc; @@ -578,52 +649,38 @@ send_frag2: out_lc: NETBUF_FREE(frag1); NETBUF_FREE(frag2); - NETBUF_FREE(fake_pad); goto out; erret_lc: NETBUF_FREE(frag1); NETBUF_FREE(frag2); - NETBUF_FREE(fake_pad); return ret; } out: return 0; } -int post_fake_sni(const void *iph, unsigned int iph_len, - const struct tcphdr *tcph, unsigned int tcph_len, - unsigned char sequence_len) { +int post_fake_sni(struct fake_type f_type, + const void *iph, unsigned int iph_len, + const struct tcphdr *tcph, unsigned int tcph_len) { + uint8_t rfsiph[128]; uint8_t rfstcph[60]; int ret; + int ipxv = netproto_version(iph, iph_len); + memcpy(rfsiph, iph, iph_len); memcpy(rfstcph, tcph, tcph_len); void *fsiph = (void *)rfsiph; struct tcphdr *fstcph = (void *)rfstcph; - ITER_FAKE_STRAT(config.faking_strategy, strategy) { - struct fake_type fake_seq_type = { - .type = FAKE_PAYLOAD_DEFAULT, - .strategy = strategy, - }; - - switch (config.fake_sni_seq_type) { - case FAKE_PAYLOAD_RANDOM: - fake_seq_type.type = FAKE_PAYLOAD_RANDOM; - break; - case FAKE_PAYLOAD_CUSTOM: - fake_seq_type.type = FAKE_PAYLOAD_CUSTOM; - fake_seq_type.fake_data = config.fake_custom_pkt; - fake_seq_type.fake_len = config.fake_custom_pkt_sz; - break; - default: - fake_seq_type.type = FAKE_PAYLOAD_DEFAULT; - } + ITER_FAKE_STRAT(f_type.strategy.strategy, strategy) { + struct fake_type fake_seq_type = f_type; + fake_seq_type.strategy.strategy = strategy; // one goes for default fake - for (int i = 1; i < sequence_len; i++) { + for (int i = 0; i < fake_seq_type.sequence_len; i++) { NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); if (!NETBUF_CHECK(fake_sni)) { lgerror("Allocation error", -ENOMEM); @@ -641,36 +698,45 @@ int post_fake_sni(const void *iph, unsigned int iph_len, } lgtrace_addp("post fake sni #%d", i + 1); - lgtrace_addp("post with %d bytes", fsn_len); - ret = instance_config.send_raw_packet(fake_sni, fsn_len); + + if (f_type.seg2delay) { + ret = instance_config.send_delayed_packet(fake_sni, fsn_len, f_type.seg2delay); + } else { + ret = instance_config.send_raw_packet(fake_sni, fsn_len); + } if (ret < 0) { lgerror("send fake sni", ret); goto erret_lc; } + uint32_t iph_len; + uint32_t tcph_len; + uint32_t plen; + ret = tcp_payload_split( + fake_sni, fsn_len, + &fsiph, &iph_len, + &fstcph, &tcph_len, + NULL, &plen); - if (!(config.faking_strategy == FAKE_STRAT_PAST_SEQ || - config.faking_strategy == FAKE_STRAT_RAND_SEQ)) { - - uint32_t iph_len; - uint32_t tcph_len; - uint32_t plen; - ret = tcp_payload_split( - fake_sni, fsn_len, - &fsiph, &iph_len, - &fstcph, &tcph_len, - NULL, &plen); - - if (ret < 0) { - lgtrace_addp("continue fake seq"); - goto erret_lc; - } - - fstcph->seq = htonl(ntohl(fstcph->seq) + plen); - memcpy(rfsiph, fsiph, iph_len); - memcpy(rfstcph, fstcph, tcph_len); - fsiph = (void *)rfsiph; - fstcph = (void *)rfstcph; + if (ret < 0) { + lgtrace_addp("continue fake seq"); + goto erret_lc; } + + + if (!(strategy == FAKE_STRAT_PAST_SEQ || + strategy == FAKE_STRAT_RAND_SEQ)) { + fstcph->seq = htonl(ntohl(fstcph->seq) + plen); + } + + if (ipxv == IP4VERSION) { + ((struct iphdr *)fsiph)->id = htons(ntohs(((struct iphdr *)fsiph)->id) + 1); + } + + memcpy(rfsiph, fsiph, iph_len); + + memcpy(rfstcph, fstcph, tcph_len); + fsiph = (void *)rfsiph; + fstcph = (void *)rfstcph; NETBUF_FREE(fake_sni); continue; @@ -678,55 +744,6 @@ erret_lc: NETBUF_FREE(fake_sni); return ret; } - - struct fake_type ftype = { - .type = FAKE_PAYLOAD_DEFAULT, - .strategy = strategy - }; - - switch (config.fake_sni_type) { - case FAKE_PAYLOAD_RANDOM: - ftype.type = FAKE_PAYLOAD_RANDOM; - break; - case FAKE_PAYLOAD_CUSTOM: - ftype.type = FAKE_PAYLOAD_CUSTOM; - ftype.fake_data = config.fake_custom_pkt; - ftype.fake_len = config.fake_custom_pkt_sz; - break; - default: - ftype.type = FAKE_PAYLOAD_DEFAULT; - } - - NETBUF_ALLOC(fake_sni, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(fake_sni)) { - lgerror("Allocation error", -ENOMEM); - return -ENOMEM; - } - uint32_t fsn_len = MAX_PACKET_SIZE; - ret = gen_fake_sni( - ftype, - iph, iph_len, tcph, tcph_len, - fake_sni, &fsn_len); - if (ret < 0) { - lgerror("gen_fake_sni", ret); - goto erret_lc_cst; - } - - lgtrace_addp("post normal fake sni"); - lgtrace_addp("post with %d bytes", fsn_len); - ret = instance_config.send_raw_packet(fake_sni, fsn_len); - if (ret < 0) { - lgerror("send fake sni", ret); - goto erret_lc_cst; - } - - goto after_cus2; - -erret_lc_cst: - NETBUF_FREE(fake_sni); - return ret; -after_cus2: - ; } return 0; diff --git a/mangle.h b/mangle.h index 4b8afa7..aa503d3 100644 --- a/mangle.h +++ b/mangle.h @@ -2,6 +2,7 @@ #define YU_MANGLE_H #include "types.h" +#include "tls.h" #define PKT_ACCEPT 0 #define PKT_DROP 1 @@ -26,12 +27,14 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len); */ int process_udp_packet(const uint8_t *pkt, uint32_t pktlen); + + /** * Sends fake client hello. */ -int post_fake_sni(const void *iph, unsigned int iph_len, - const struct tcphdr *tcph, unsigned int tcph_len, - unsigned char sequence_len); +int post_fake_sni(struct fake_type f_type, + const void *iph, unsigned int iph_len, + const struct tcphdr *tcph, unsigned int tcph_len); /** * Splits packet by poses and posts. diff --git a/tls.c b/tls.c index d19db51..b1e6099 100644 --- a/tls.c +++ b/tls.c @@ -273,18 +273,9 @@ int gen_fake_sni(struct fake_type type, const void *ipxh, uint32_t iph_len, const struct tcphdr *tcph, uint32_t tcph_len, uint8_t *buf, uint32_t *buflen) { - uint32_t data_len = type.fake_len; if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { -#ifdef KERNEL_SPACE - - get_random_bytes(&data_len, sizeof(data_len)); - data_len = data_len % 1200; -#else - data_len = random() % 1200; -#endif - } else if (type.type == FAKE_PAYLOAD_DEFAULT) { - data_len = config.fake_sni_pkt_sz; + data_len = (uint32_t)randint() % 1200; } if (!ipxh || !tcph || !buf || !buflen) @@ -320,9 +311,6 @@ int gen_fake_sni(struct fake_type type, uint8_t *bfdptr = buf + iph_len + tcph_len; switch (type.type) { - case FAKE_PAYLOAD_DEFAULT: - memcpy(bfdptr, config.fake_sni_pkt, data_len); - break; case FAKE_PAYLOAD_DATA: memcpy(bfdptr, type.fake_data, data_len); break; diff --git a/tls.h b/tls.h index fe151ff..9b4c2a2 100644 --- a/tls.h +++ b/tls.h @@ -2,6 +2,7 @@ #define TLS_H #include "types.h" +#include "utils.h" /** @@ -22,28 +23,6 @@ struct tls_verdict { struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); -struct fake_type { - -#define FAKE_PAYLOAD_RANDOM 0 -#define FAKE_PAYLOAD_DATA 1 -// In default mode all other options will be skipped. -#define FAKE_PAYLOAD_DEFAULT 2 - int type; - - // Length of the final fake message. - // Pass 0 in RANDOM mode to make it random - uint16_t fake_len; - - // Payload of the fake message of fake_len length. - // Will be omitted in RANDOM mode. - const char *fake_data; - - // faking strategy of the fake packet. - // Does not support bitmask, pass standalone strategy. - // Pass 0 if you don't want any faking procedures. - unsigned int strategy; -}; - /** * Generates the fake client hello message */ diff --git a/types.h b/types.h index 65b4749..39f33cf 100644 --- a/types.h +++ b/types.h @@ -13,6 +13,8 @@ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export #endif /* SPACES */ @@ -99,4 +101,16 @@ #define NETBUF_FREE(buf) ; #endif +static inline int randint(void) { + int rnd; + +#ifdef KERNEL_SPACE + get_random_bytes(&rnd, sizeof(rnd)); +#else + rnd = random(); +#endif + + return rnd; +} + #endif /* TYPES_H */ diff --git a/utils.c b/utils.c index e93b4c1..0659bb5 100644 --- a/utils.c +++ b/utils.c @@ -418,7 +418,6 @@ int tcp_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 ip6_hdr *hdr6; void *hdr; uint32_t hdr_len; struct tcphdr *tcph; @@ -485,6 +484,11 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset, struct iphdr *s2_hdr = (void *)seg2; s1_hdr->tot_len = htons(s1_dlen); s2_hdr->tot_len = htons(s2_dlen); + s1_hdr->id = randint(); + s2_hdr->id = randint(); + + set_ip_checksum(s1_hdr, sizeof(struct iphdr)); + set_ip_checksum(s2_hdr, sizeof(struct iphdr)); } else { struct ip6_hdr *s1_hdr = (void *)seg1; struct ip6_hdr *s2_hdr = (void *)seg2; @@ -523,6 +527,19 @@ void z_function(const char *str, int *zbuf, size_t len) { } } +void shift_data(uint8_t *data, uint32_t dlen, uint32_t delta) { + uint8_t *ndptr = data + delta + dlen; + uint8_t *dptr = data + dlen; + uint8_t *ndlptr = data; + for (size_t i = dlen + 1; i > 0; i--) { + *ndptr = *dptr; + --ndptr, --dptr; + } + for (size_t i = 0; i < delta; i++) { + *ndlptr++ = 0; + } +} + #define TCP_MD5SIG_LEN 16 #define TCP_MD5SIG_KIND 19 struct tcp_md5sig_opt { @@ -534,7 +551,7 @@ struct tcp_md5sig_opt { // Real length of the option, with NOOP fillers #define TCP_MD5SIG_OPT_RLEN 20 -int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { +int fail_packet(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { void *iph; uint32_t iph_len; struct tcphdr *tcph; @@ -554,38 +571,29 @@ int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_ } - if (strategy == FAKE_STRAT_RAND_SEQ) { + if (strategy.strategy == FAKE_STRAT_RAND_SEQ) { lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); - if (config.fakeseq_offset) { - tcph->seq = htonl(ntohl(tcph->seq) - config.fakeseq_offset); - } else { -#ifdef KERNEL_SPACE - tcph->seq = 124; -#else - tcph->seq = random(); -#endif - - } + tcph->seq = htonl(ntohl(tcph->seq) - (strategy.randseq_offset + dlen)); lgtrace_addp("%u", ntohl(tcph->seq)); - } else if (strategy == FAKE_STRAT_PAST_SEQ) { + } else if (strategy.strategy == FAKE_STRAT_PAST_SEQ) { lgtrace("fake seq: %u -> ", ntohl(tcph->seq)); tcph->seq = htonl(ntohl(tcph->seq) - dlen); lgtrace_addp("%u", ntohl(tcph->seq)); - } else if (strategy == FAKE_STRAT_TTL) { - lgtrace_addp("set fake ttl to %d", config.faking_ttl); + } else if (strategy.strategy == FAKE_STRAT_TTL) { + lgtrace_addp("set fake ttl to %d", strategy.faking_ttl); if (ipxv == IP4VERSION) { - ((struct iphdr *)iph)->ttl = config.faking_ttl; + ((struct iphdr *)iph)->ttl = strategy.faking_ttl; } else if (ipxv == IP6VERSION) { - ((struct ip6_hdr *)iph)->ip6_hops = config.faking_ttl; + ((struct ip6_hdr *)iph)->ip6_hops = strategy.faking_ttl; } else { lgerror("fail_packet: IP version is unsupported", -EINVAL); return -EINVAL; } - } else if (strategy == FAKE_STRAT_TCP_MD5SUM) { + } else if (strategy.strategy == FAKE_STRAT_TCP_MD5SUM) { int optp_len = tcph_len - sizeof(struct tcphdr); int delta = TCP_MD5SIG_OPT_RLEN - optp_len; lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta); @@ -594,14 +602,9 @@ int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_ if (avail_buflen - *plen < delta) { return -1; } - uint8_t *ndata = data + delta; - uint8_t *ndptr = ndata + dlen; - uint8_t *dptr = data + dlen; - for (size_t i = dlen + 1; i > 0; i--) { - *ndptr = *dptr; - --ndptr, --dptr; - } - data = ndata; + + shift_data(data, dlen, delta); + data += delta; tcph_len = tcph_len + delta; tcph->doff = tcph_len >> 2; if (ipxv == IP4VERSION) { @@ -629,13 +632,57 @@ int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_ } } + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->frag_off = 0; + } + + set_ip_checksum(iph, iph_len); set_tcp_checksum(tcph, iph, iph_len); - if (strategy == FAKE_STRAT_TCP_CHECK) { + if (strategy.strategy == FAKE_STRAT_TCP_CHECK) { lgtrace_addp("break fake tcp checksum"); tcph->check += 1; } return 0; } + +int seqovl_packet(uint8_t *payload, uint32_t *plen, uint32_t seq_delta) { + int ipxv = netproto_version(payload, *plen); + + void *iph; + uint32_t iph_len; + struct tcphdr *tcph; + uint32_t tcph_len; + uint8_t *data; + uint32_t dlen; + + + int ret = tcp_payload_split(payload, *plen, + &iph, &iph_len, &tcph, &tcph_len, + &data, &dlen); + + if (ret < 0) { + return -1; + } + + if (ipxv == IP4VERSION) { + struct iphdr *ip4h = iph; + ip4h->tot_len = htons(ntohs(ip4h->tot_len) + seq_delta); + } else if (ipxv == IP6VERSION) { + struct ip6_hdr *ip6h = iph; + ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + seq_delta); + } else { + return -1; + } + + tcph->seq = htons(ntohs(tcph->seq) - seq_delta); + shift_data(data, dlen, seq_delta); + *plen += seq_delta; + + set_ip_checksum(iph, iph_len); + set_tcp_checksum(tcph, iph, iph_len); + return 0; +} + diff --git a/utils.h b/utils.h index 77106b0..ddae729 100644 --- a/utils.h +++ b/utils.h @@ -2,6 +2,7 @@ #define UTILS_H #include "types.h" +#include "config.h" #define IP4VERSION 4 #define IP6VERSION 6 @@ -101,6 +102,45 @@ int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len); void z_function(const char *str, int *zbuf, size_t len); +/** + * Shifts data left delta bytes. Fills delta buffer with zeroes. + */ +void shift_data(uint8_t *data, uint32_t dlen, uint32_t delta); + + +struct failing_strategy { + unsigned int strategy; + uint8_t faking_ttl; + uint32_t randseq_offset; +}; + + +struct fake_type { + +#define FAKE_PAYLOAD_RANDOM 0 +#define FAKE_PAYLOAD_DATA 1 +// In default mode all other options will be skipped. +#define FAKE_PAYLOAD_DEFAULT 2 + int type; + + // Length of the final fake message. + // Pass 0 in RANDOM mode to make it random + uint16_t fake_len; + + // Payload of the fake message of fake_len length. + // Will be omitted in RANDOM mode. + const char *fake_data; + + unsigned int sequence_len; + + // If non-0 the packet send will be delayed for n milliseconds + unsigned int seg2delay; + + // faking strategy of the fake packet. + // Does not support bitmask, pass standalone strategy. + // Pass 0 if you don't want any faking procedures. + struct failing_strategy strategy; +}; /** * Invalidates the raw packet. The function aims to invalid the packet @@ -108,7 +148,46 @@ void z_function(const char *str, int *zbuf, size_t len); * * Does not support bitmask, pass standalone strategy. */ -int fail_packet(unsigned int strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); +int fail_packet(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); +/** + * Shifts the payload right and pushes zeroes before it. Useful for TCP TLS faking. + */ +int seqovl_packet(uint8_t *payload, uint32_t *plen, uint32_t seq_delta); + + + +static inline struct failing_strategy args_default_failing_strategy(void) { + struct failing_strategy fl_strat = { + .strategy = (unsigned int)config.faking_strategy, + .faking_ttl = config.faking_ttl, + .randseq_offset = (uint32_t)config.fakeseq_offset + }; + return fl_strat; +} + +static inline struct fake_type args_default_fake_type(void) { + struct fake_type f_type = { + .sequence_len = config.fake_sni_seq_len, + .strategy = args_default_failing_strategy(), + }; + + switch (config.fake_sni_type) { + case FAKE_PAYLOAD_RANDOM: + f_type.type = FAKE_PAYLOAD_RANDOM; + break; + case FAKE_PAYLOAD_CUSTOM: + f_type.type = FAKE_PAYLOAD_CUSTOM; + f_type.fake_data = config.fake_custom_pkt; + f_type.fake_len = config.fake_custom_pkt_sz; + break; + default: + f_type.type = FAKE_PAYLOAD_CUSTOM; + f_type.fake_data = config.fake_sni_pkt; + f_type.fake_len = config.fake_sni_pkt_sz; + } + + return f_type; +} #endif /* UTILS_H */ diff --git a/youtubeUnblock.c b/youtubeUnblock.c index a57cb69..086dbaf 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -306,17 +306,19 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { int ipvx = netproto_version(pkt, pktlen); - if (ipvx == IP4VERSION) - return send_raw_ipv4(pkt, pktlen); - else if (ipvx == IP6VERSION) - return send_raw_ipv6(pkt, pktlen); + if (ipvx == IP4VERSION) { + ret = send_raw_ipv4(pkt, pktlen); + } else if (ipvx == IP6VERSION) { + ret = send_raw_ipv6(pkt, pktlen); + } else { + printf("proto version %d is unsupported\n", ipvx); + return -EINVAL; + } - printf("proto version %d is unsupported\n", ipvx); - return -EINVAL; + lgtrace_addp("raw_sock_send: %d", ret); + return ret; } - - struct packet_data { uint32_t id; uint16_t hw_proto; @@ -375,7 +377,7 @@ void *delay_packet_send_fn(void *data) { return NULL; } -void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { +int delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) { struct dps_t *dpdt = malloc(sizeof(struct dps_t)); dpdt->pkt = malloc(data_len); memcpy(dpdt->pkt, data, data_len); @@ -384,6 +386,9 @@ void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigne pthread_t thr; pthread_create(&thr, NULL, delay_packet_send_fn, dpdt); pthread_detach(thr); + lgtrace_addp("Scheduled packet send after %d ms", delay_ms); + + return 0; } static int queue_cb(const struct nlmsghdr *nlh, void *data) { From 05cc0054d8d3e07bd4ff62aab247bfa6ac070355 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 12 Oct 2024 16:55:27 +0300 Subject: [PATCH 10/14] Fix getrandom on older versions --- args.h | 4 ---- tls.c | 22 ++++++++++++++++++---- types.h | 6 ++++++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/args.h b/args.h index 0c3d52e..98d65cf 100644 --- a/args.h +++ b/args.h @@ -1,6 +1,3 @@ -#include "utils.h" -#include "tls.h" - #ifndef ARGS_H #define ARGS_H @@ -11,5 +8,4 @@ int parse_args(int argc, char *argv[]); /* Prints starting messages */ void print_welcome(); - #endif /* ARGS_H */ diff --git a/tls.c b/tls.c index b1e6099..6b13c3a 100644 --- a/tls.c +++ b/tls.c @@ -5,8 +5,8 @@ #include "utils.h" #ifndef KERNEL_SPACE -#include -#include +#include +#include #endif #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 @@ -274,6 +274,8 @@ int gen_fake_sni(struct fake_type type, const struct tcphdr *tcph, uint32_t tcph_len, uint8_t *buf, uint32_t *buflen) { uint32_t data_len = type.fake_len; + int ret; + if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { data_len = (uint32_t)randint() % 1200; } @@ -317,9 +319,21 @@ int gen_fake_sni(struct fake_type type, default: // FAKE_PAYLOAD_RANDOM #ifdef KERNEL_SPACE get_random_bytes(bfdptr, data_len); -#else +#else /* KERNEL_SPACE */ +#if _NO_GETRANDOM + ret = open("/dev/urandom", O_RDONLY); + if (ret < 0) { + lgerror("Unable to open /dev/urandom", ret); + return ret; + } + + read(ret, bfdptr, data_len); + close(ret); + +#else /* _NO_GETRANDOM */ getrandom(bfdptr, data_len, 0); -#endif +#endif /* _NO_GETRANDOM */ +#endif /* KERNEL_SPACE */ } if (ipxv == IP4VERSION) { diff --git a/types.h b/types.h index 39f33cf..ab63f61 100644 --- a/types.h +++ b/types.h @@ -14,7 +14,13 @@ #include // IWYU pragma: export #include // IWYU pragma: export #include // IWYU pragma: export + + +#define _NO_GETRANDOM ((__GLIBC__ <= 2 && __GLIBC_MINOR__ < 25)) + +#if !_NO_GETRANDOM #include // IWYU pragma: export +#endif #endif /* SPACES */ From 58f4802f6466e48734d303ff0a2deb2bfe9e7f16 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sat, 12 Oct 2024 19:08:49 +0300 Subject: [PATCH 11/14] Update kernel module parameters --- README.md | 5 +-- kargs.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cd65677..bae083a 100644 --- a/README.md +++ b/README.md @@ -183,9 +183,8 @@ 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**. -- `--fake-sni-type={default|custom|random}` This flag specifies which faking messages should use fake packets. If you pass random, the message of random length and random payload will be sent. For default the default payload is used. And for custom the payload from `--fake-custom-payload` is used. Default to `default`. - -- `--fake-custom-payload=` Usable with `--fake-sni-type=custom`. You should specify the payload for fake message manually. Use hex format: `--fake-custom-payload=0001020304` mean that 5 bytes sequence: `0x00`, `0x01`, `0x02`, `0x03`, `0x04` used as fake. +- `--fake-sni-type={default|custom|random}` This flag specifies which faking message type should be used for fake packets. For `random`, the message of random length and with random payload will be sent. For `default` the default payload (sni=www.google.com) is used. And for the `custom` option, the payload from `--fake-custom-payload` section utilized. Defaults to `default`. +- `--fake-custom-payload=` Useful with `--fake-sni-type=custom`. You should specify the payload for fake message manually. Use hex format: `--fake-custom-payload=0001020304` mean that 5 bytes sequence: `0x00`, `0x01`, `0x02`, `0x03`, `0x04` used as fake. - `--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}` 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. diff --git a/kargs.c b/kargs.c index abc55f7..8b63f31 100644 --- a/kargs.c +++ b/kargs.c @@ -125,6 +125,7 @@ module_param_cb(fk_winsize, &unumeric_parameter_ops, &config.fk_winsize, 0664); module_param_cb(synfake, &boolean_parameter_ops, &config.synfake, 0664); module_param_cb(synfake_len, &unumeric_parameter_ops, &config.synfake_len, 0664); module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664); +// module_param_cb(seg2delay, &unumeric_parameter_ops, &config.seg2_delay, 0664); static int sni_domains_set(const char *val, const struct kernel_param *kp) { size_t len; @@ -404,3 +405,110 @@ static const struct kernel_param_ops sni_detection_ops = { }; module_param_cb(sni_detection, &sni_detection_ops, &config.sni_detection, 0664); + +static int fake_type_set(const char *val, const struct kernel_param *kp) { + size_t len; + + len = strnlen(val, STR_MAXLEN + 1); + if (len == STR_MAXLEN + 1) { + pr_err("%s: string parameter too long\n", kp->name); + return -ENOSPC; + } + + if (len >= 1 && val[len - 1] == '\n') { + len--; + } + + if (strncmp(val, "default", len) == 0) { + *(int *)kp->arg = FAKE_PAYLOAD_DEFAULT; + } else if (strncmp(val, "custom", len) == 0) { + *(int *)kp->arg = FAKE_PAYLOAD_CUSTOM; + } else if (strncmp(val, "random", len) == 0) { + *(int *)kp->arg = FAKE_PAYLOAD_RANDOM; + } else { + return -EINVAL; + } + + return 0; +} + +static int fake_type_get(char *buffer, const struct kernel_param *kp) { + switch (*(int *)kp->arg) { + case FAKE_PAYLOAD_DEFAULT: + strcpy(buffer, "default\n"); + break; + case FAKE_PAYLOAD_RANDOM: + strcpy(buffer, "random\n"); + break; + case FAKE_PAYLOAD_CUSTOM: + strcpy(buffer, "custom\n"); + break; + default: + strcpy(buffer, "unknown\n"); + } + + return strlen(buffer); +} + +static const struct kernel_param_ops fake_type_ops = { + .set = fake_type_set, + .get = fake_type_get, +}; + +module_param_cb(fake_sni_type, &fake_type_ops, &config.fake_sni_type, 0664); + +static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) { + size_t len; + + len = strnlen(val, STR_MAXLEN + 1); + if (len == STR_MAXLEN + 1) { + pr_err("%s: string parameter too long\n", kp->name); + return -ENOSPC; + } + + if (len >= 1 && val[len - 1] == '\n') { + len--; + } + + uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; + const char *custom_hex_fake = val; + size_t custom_hlen = len; + + if ((custom_hlen & 1) == 1) { + return -EINVAL; + } + + + size_t custom_len = custom_hlen >> 1; + if (custom_len > MAX_FAKE_SIZE) { + return -EINVAL; + } + + for (int i = 0; i < custom_len; i++) { + sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); + } + + config.fake_custom_pkt_sz = custom_len; + config.fake_custom_pkt = (char *)custom_buf; + + return 0; +} + +static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) { + int cflen = config.fake_custom_pkt_sz; + const uint8_t *cbf_data = config.fake_custom_pkt; + int bflen = config.fake_custom_pkt_sz << 1; + + for (int i = 0; i < cflen; i++) { + sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i)); + } + + return bflen; +} + +static const struct kernel_param_ops fake_custom_pl_ops = { + .set = fake_custom_pl_set, + .get = fake_custom_pl_get, +}; + +module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &config.fake_custom_pkt, 0664); From 4c7b63fa7f0f9bfdb01c0034dc8908b21034f92e Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 13 Oct 2024 23:31:26 +0300 Subject: [PATCH 12/14] Add multiple sections with config sets for various domains --- README.md | 2 + args.c | 407 ++++++++++++++++++++++++++--------------------- config.h | 54 +++++-- kargs.c | 113 +++++++------ kytunblock.c | 21 +-- mangle.c | 161 ++++++++++--------- mangle.h | 10 +- tls.c | 45 +++--- tls.h | 2 +- utils.h | 24 +-- youtubeUnblock.c | 24 +-- 11 files changed, 450 insertions(+), 413 deletions(-) diff --git a/README.md b/README.md index bae083a..dfd31d4 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,8 @@ Available flags: - `--packet-mark=` Use this option if youtubeUnblock conflicts with other systems rely on packet mark. Note that you may want to change accept rule for iptables to follow the mark. +- `--fbegin` and `--fend` flags: youtubeUnblock supports multiple sets of strategies for specific filters. You may want to initiate a new set after the default one, like: `--sni-domains=googlevideo.com --faking-strategy=md5sum --fbegin --sni-domains=youtube.com --faking-strategy=tcp_check --fend --fbegin --sni-domains=l.google.com --faking-strategy=pastseq --fend`. Note, that the priority of these sets goes backwards: last is first, default (one that does not start with --fbegin) is last. If you start the new section, the default settings are implemented just like youtubeUnblock without any parameters. Note that the config above is just an example and won't work for you. + ## Troubleshooting 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`. diff --git a/args.c b/args.c index 9243103..c7ac2c0 100644 --- a/args.c +++ b/args.c @@ -12,8 +12,7 @@ static char custom_fake_buf[MAX_FAKE_SIZE]; -struct config_t config = { - .threads = THREADS_NUM, +static const struct section_config_t default_section_config = { .frag_sni_reverse = 1, .frag_sni_faked = 0, .fragmentation_strategy = FRAGMENTATION_STRATEGY, @@ -24,31 +23,12 @@ struct config_t config = { .fake_sni_type = FAKE_PAYLOAD_DEFAULT, .frag_middle_sni = 1, .frag_sni_pos = 1, - .use_ipv6 = 1, .fakeseq_offset = 10000, - .mark = DEFAULT_RAWSOCKET_MARK, .synfake = 0, .synfake_len = 0, + .quic_drop = 0, - .sni_detection = SNI_DETECTION_PARSE, - -#ifdef SEG2_DELAY - .seg2_delay = SEG2_DELAY, -#else .seg2_delay = 0, -#endif - -#ifdef USE_GSO - .use_gso = true, -#else - .use_gso = false, -#endif - -#ifdef DEBUG - .verbose = 1, -#else - .verbose = 0, -#endif .domains_str = defaul_snistr, .domains_strlen = sizeof(defaul_snistr), @@ -56,11 +36,25 @@ struct config_t config = { .exclude_domains_str = "", .exclude_domains_strlen = 0, - .queue_start_num = DEFAULT_QUEUE_NUM, .fake_sni_pkt = fake_sni_old, .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator .fake_custom_pkt = custom_fake_buf, - .fake_custom_pkt_sz = 0 + .fake_custom_pkt_sz = 0, + + .sni_detection = SNI_DETECTION_PARSE, +}; + +struct config_t config = { + .threads = THREADS_NUM, + .queue_start_num = DEFAULT_QUEUE_NUM, + .mark = DEFAULT_RAWSOCKET_MARK, + .use_ipv6 = 1, + + .verbose = VERBOSE_DEBUG, + .use_gso = true, + + .default_config = default_section_config, + .custom_configs_len = 0 }; #define OPT_SNI_DOMAINS 1 @@ -71,6 +65,8 @@ struct config_t config = { #define OPT_FAKE_SNI_SEQ_LEN 11 #define OPT_FAKE_SNI_TYPE 27 #define OPT_FAKE_CUSTOM_PAYLOAD 28 +#define OPT_START_SECTION 29 +#define OPT_END_SECTION 30 #define OPT_FRAG 4 #define OPT_FRAG_SNI_REVERSE 12 #define OPT_FRAG_SNI_FAKED 13 @@ -91,7 +87,7 @@ struct config_t config = { #define OPT_NO_GSO 8 #define OPT_QUEUE_NUM 9 -#define OPT_MAX OPT_FAKE_CUSTOM_PAYLOAD +#define OPT_MAX OPT_END_SECTION static struct option long_opt[] = { {"help", 0, 0, 'h'}, @@ -123,6 +119,8 @@ static struct option long_opt[] = { {"no-ipv6", 0, 0, OPT_NO_IPV6}, {"queue-num", 1, 0, OPT_QUEUE_NUM}, {"packet-mark", 1, 0, OPT_PACKET_MARK}, + {"fbegin", 0, 0, OPT_START_SECTION}, + {"fend", 0, 0, OPT_END_SECTION}, {0,0,0,0} }; @@ -182,16 +180,27 @@ void print_usage(const char *argv0) { printf("\t--trace\n"); printf("\t--no-gso\n"); printf("\t--no-ipv6\n"); + printf("\t--fbegin\n"); + printf("\t--fend\n"); printf("\n"); } int parse_args(int argc, char *argv[]) { int opt; - int optIdx; + int optIdx = 0; long num; + struct section_config_t *sect_config = &config.default_config; + +#define SECT_ITER_DEFAULT 1 +#define SECT_ITER_INSIDE 2 +#define SECT_ITER_OUTSIDE 3 + + int section_iter = SECT_ITER_DEFAULT; + while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) { switch (opt) { +/* config_t scoped configs */ case 'h': print_usage(argv[0]); goto stop_exec; @@ -199,49 +208,98 @@ int parse_args(int argc, char *argv[]) { print_version(); goto stop_exec; case OPT_TRACE: + if (section_iter != SECT_ITER_DEFAULT) + goto invalid_opt; config.verbose = 2; break; case OPT_SILENT: + if (section_iter != SECT_ITER_DEFAULT) + goto invalid_opt; + config.verbose = 0; break; case OPT_NO_GSO: + if (section_iter != SECT_ITER_DEFAULT) + goto invalid_opt; + config.use_gso = 0; break; case OPT_NO_IPV6: + if (section_iter != SECT_ITER_DEFAULT) + goto invalid_opt; + config.use_ipv6 = 0; break; - case OPT_QUIC_DROP: - config.quic_drop = 1; - break; - case OPT_SNI_DOMAINS: - if (!strcmp(optarg, "all")) { - config.all_domains = 1; - } + case OPT_THREADS: + if (section_iter != SECT_ITER_DEFAULT) + goto invalid_opt; - config.domains_str = optarg; - config.domains_strlen = strlen(config.domains_str); - break; - case OPT_EXCLUDE_DOMAINS: - config.exclude_domains_str = optarg; - config.exclude_domains_strlen = strlen(config.exclude_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 { + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0 || num > MAX_THREADS) { goto invalid_opt; } + config.threads = num; + break; + case OPT_QUEUE_NUM: + if (section_iter != SECT_ITER_DEFAULT) + goto invalid_opt; + + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0) { + goto invalid_opt; + } + + config.queue_start_num = num; + break; + case OPT_PACKET_MARK: + if (section_iter != SECT_ITER_DEFAULT) + goto invalid_opt; + + num = parse_numeric_option(optarg); + if (errno != 0 || num < 0) { + goto invalid_opt; + } + + config.mark = num; + break; + case OPT_START_SECTION: + if (section_iter != SECT_ITER_DEFAULT && section_iter != SECT_ITER_OUTSIDE) + goto invalid_opt; + + sect_config = &config.custom_configs[config.custom_configs_len++]; + *sect_config = default_section_config; + section_iter = SECT_ITER_INSIDE; + + break; + case OPT_END_SECTION: + if (section_iter != SECT_ITER_INSIDE) + goto invalid_opt; + + section_iter = SECT_ITER_OUTSIDE; + sect_config = &config.default_config; + break; + +/* section_config_t scoped configs */ + case OPT_SNI_DOMAINS: + if (!strcmp(optarg, "all")) { + sect_config->all_domains = 1; + } + + sect_config->domains_str = optarg; + sect_config->domains_strlen = strlen(sect_config->domains_str); + break; + case OPT_EXCLUDE_DOMAINS: + sect_config->exclude_domains_str = optarg; + sect_config->exclude_domains_strlen = strlen(sect_config->exclude_domains_str); break; case OPT_FRAG: if (strcmp(optarg, "tcp") == 0) { - config.fragmentation_strategy = FRAG_STRAT_TCP; + sect_config->fragmentation_strategy = FRAG_STRAT_TCP; } else if (strcmp(optarg, "ip") == 0) { - config.fragmentation_strategy = FRAG_STRAT_IP; + sect_config->fragmentation_strategy = FRAG_STRAT_IP; } else if (strcmp(optarg, "none") == 0) { - config.fragmentation_strategy = FRAG_STRAT_NONE; + sect_config->fragmentation_strategy = FRAG_STRAT_NONE; } else { goto invalid_opt; } @@ -249,9 +307,9 @@ int parse_args(int argc, char *argv[]) { break; case OPT_FRAG_SNI_FAKED: if (strcmp(optarg, "1") == 0) { - config.frag_sni_faked = 1; + sect_config->frag_sni_faked = 1; } else if (strcmp(optarg, "0") == 0) { - config.frag_sni_faked = 0; + sect_config->frag_sni_faked = 0; } else { goto invalid_opt; } @@ -259,9 +317,9 @@ int parse_args(int argc, char *argv[]) { break; case OPT_FRAG_SNI_REVERSE: if (strcmp(optarg, "1") == 0) { - config.frag_sni_reverse = 1; + sect_config->frag_sni_reverse = 1; } else if (strcmp(optarg, "0") == 0) { - config.frag_sni_reverse = 0; + sect_config->frag_sni_reverse = 0; } else { goto invalid_opt; } @@ -269,9 +327,9 @@ int parse_args(int argc, char *argv[]) { break; case OPT_FRAG_MIDDLE_SNI: if (strcmp(optarg, "1") == 0) { - config.frag_middle_sni = 1; + sect_config->frag_middle_sni = 1; } else if (strcmp(optarg, "0") == 0) { - config.frag_middle_sni = 0; + sect_config->frag_middle_sni = 0; } else { goto invalid_opt; } @@ -283,19 +341,19 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; } - config.frag_sni_pos = num; + sect_config->frag_sni_pos = num; break; case OPT_FAKING_STRATEGY: if (strcmp(optarg, "randseq") == 0) { - config.faking_strategy = FAKE_STRAT_RAND_SEQ; + sect_config->faking_strategy = FAKE_STRAT_RAND_SEQ; } else if (strcmp(optarg, "ttl") == 0) { - config.faking_strategy = FAKE_STRAT_TTL; + sect_config->faking_strategy = FAKE_STRAT_TTL; } else if (strcmp(optarg, "tcp_check") == 0) { - config.faking_strategy = FAKE_STRAT_TCP_CHECK; + sect_config->faking_strategy = FAKE_STRAT_TCP_CHECK; } else if (strcmp(optarg, "pastseq") == 0) { - config.faking_strategy = FAKE_STRAT_PAST_SEQ; + sect_config->faking_strategy = FAKE_STRAT_PAST_SEQ; } else if (strcmp(optarg, "md5sum") == 0) { - config.faking_strategy = FAKE_STRAT_TCP_MD5SUM; + sect_config->faking_strategy = FAKE_STRAT_TCP_MD5SUM; } else { goto invalid_opt; } @@ -307,7 +365,7 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; } - config.faking_ttl = num; + sect_config->faking_ttl = num; break; case OPT_FAKE_SEQ_OFFSET: num = parse_numeric_option(optarg); @@ -315,13 +373,13 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; } - config.fakeseq_offset = num; + sect_config->fakeseq_offset = num; break; case OPT_FAKE_SNI: if (strcmp(optarg, "1") == 0) { - config.fake_sni = 1; + sect_config->fake_sni = 1; } else if (strcmp(optarg, "0") == 0) { - config.fake_sni = 0; + sect_config->fake_sni = 0; } else { goto invalid_opt; } @@ -333,15 +391,15 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; } - config.fake_sni_seq_len = num; + sect_config->fake_sni_seq_len = num; break; case OPT_FAKE_SNI_TYPE: if (strcmp(optarg, "default") == 0) { - config.fake_sni_type = FAKE_PAYLOAD_DEFAULT; + sect_config->fake_sni_type = FAKE_PAYLOAD_DEFAULT; } else if (strcmp(optarg, "random") == 0) { - config.fake_sni_type = FAKE_PAYLOAD_RANDOM; + sect_config->fake_sni_type = FAKE_PAYLOAD_RANDOM; } else if (strcmp(optarg, "custom") == 0) { - config.fake_sni_type = FAKE_PAYLOAD_CUSTOM; + sect_config->fake_sni_type = FAKE_PAYLOAD_CUSTOM; } else { goto invalid_opt; } @@ -368,8 +426,8 @@ int parse_args(int argc, char *argv[]) { sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); } - config.fake_custom_pkt_sz = custom_len; - config.fake_custom_pkt = (char *)custom_buf; + sect_config->fake_custom_pkt_sz = custom_len; + sect_config->fake_custom_pkt = (char *)custom_buf; } break; case OPT_FK_WINSIZE: @@ -378,66 +436,37 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; } - config.fk_winsize = num; + sect_config->fk_winsize = num; break; - case OPT_SYNFAKE: - if (strcmp(optarg, "1") == 0) { - config.synfake = 1; - } else if (strcmp(optarg, "0") == 0) { - config.synfake = 0; - } else { - goto invalid_opt; - } - break; - case OPT_SYNFAKE_LEN: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0) { - goto invalid_opt; - } - - config.synfake_len = num; - break; case OPT_SEG2DELAY: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } - config.seg2_delay = num; + sect_config->seg2_delay = num; break; - case OPT_THREADS: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0 || num > MAX_THREADS) { + case OPT_QUIC_DROP: + sect_config->quic_drop = 1; + break; + case OPT_SNI_DETECTION: + if (strcmp(optarg, "parse") == 0) { + sect_config->sni_detection = SNI_DETECTION_PARSE; + } else if (strcmp(optarg, "brute") == 0) { + sect_config->sni_detection = SNI_DETECTION_BRUTE; + } else { goto invalid_opt; } - config.threads = num; break; - case OPT_QUEUE_NUM: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0) { - goto invalid_opt; - } - - config.queue_start_num = num; - break; - case OPT_PACKET_MARK: - num = parse_numeric_option(optarg); - if (errno != 0 || num < 0) { - goto invalid_opt; - } - - config.mark = num; - break; - default: goto error; } + } -// out: errno = 0; return 0; stop_exec: @@ -453,68 +482,6 @@ error: } void print_welcome() { - switch (config.fragmentation_strategy) { - case FRAG_STRAT_TCP: - printf("Using TCP segmentation\n"); - break; - case FRAG_STRAT_IP: - printf("Using IP fragmentation\n"); - break; - default: - printf("SNI fragmentation is disabled\n"); - break; - } - - if (config.seg2_delay) { - printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay); - } - - if (config.fake_sni) { - printf("Fake SNI will be sent before each target client hello\n"); - } else { - printf("Fake SNI is disabled\n"); - } - - if (config.frag_sni_reverse) { - printf("Fragmentation Client Hello will be reversed\n"); - } - - if (config.frag_sni_faked) { - printf("Fooling packets will be sent near the original Client Hello\n"); - } - - if (config.fake_sni_seq_len > 1) { - printf("Faking sequence of length %d will be built as fake sni\n", config.fake_sni_seq_len); - } - - switch (config.faking_strategy) { - case FAKE_STRAT_TTL: - printf("TTL faking strategy will be used with TTL %d\n", config.faking_ttl); - break; - case FAKE_STRAT_RAND_SEQ: - printf("Random seq faking strategy will be used\n"); - printf("Fake seq offset set to %u\n", config.fakeseq_offset); - 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; - case FAKE_STRAT_TCP_MD5SUM: - printf("md5sum faking strategy will be used\n"); - break; - } - - if (config.fk_winsize) { - printf("Response TCP window will be set to %d with the appropriate scale\n", config.fk_winsize); - } - - if (config.synfake) { - printf("Fake SYN payload will be sent with each TCP request SYN packet\n"); - } - - if (config.use_gso) { printf("GSO is enabled\n"); } @@ -524,18 +491,88 @@ void print_welcome() { } else { printf("IPv6 is disabled\n"); } + + printf("Detected %d config sections\n", config.custom_configs_len + 1); + printf("The sections will be processed in ordred they goes in this output"); - if (config.quic_drop) { - printf("All QUIC packets will be dropped\n"); + ITER_CONFIG_SECTIONS(section) { + int section_number = CONFIG_SECTION_NUMBER(section); + printf("Section #%d\n", section_number); + + switch (section->fragmentation_strategy) { + case FRAG_STRAT_TCP: + printf("Using TCP segmentation\n"); + break; + case FRAG_STRAT_IP: + printf("Using IP fragmentation\n"); + break; + default: + printf("SNI fragmentation is disabled\n"); + break; + } + + if (section->seg2_delay) { + printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", section->seg2_delay); + } + + if (section->fake_sni) { + printf("Fake SNI will be sent before each target client hello\n"); + } else { + printf("Fake SNI is disabled\n"); + } + + if (section->frag_sni_reverse) { + printf("Fragmentation Client Hello will be reversed\n"); + } + + if (section->frag_sni_faked) { + printf("Fooling packets will be sent near the original Client Hello\n"); + } + + if (section->fake_sni_seq_len > 1) { + printf("Faking sequence of length %d will be built as fake sni\n", section->fake_sni_seq_len); + } + + switch (section->faking_strategy) { + case FAKE_STRAT_TTL: + printf("TTL faking strategy will be used with TTL %d\n", section->faking_ttl); + break; + case FAKE_STRAT_RAND_SEQ: + printf("Random seq faking strategy will be used\n"); + printf("Fake seq offset set to %u\n", section->fakeseq_offset); + 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; + case FAKE_STRAT_TCP_MD5SUM: + printf("md5sum faking strategy will be used\n"); + break; + } + + if (section->fk_winsize) { + printf("Response TCP window will be set to %d with the appropriate scale\n", section->fk_winsize); + } + + if (section->synfake) { + printf("Fake SYN payload will be sent with each TCP request SYN packet\n"); + } + + if (section->quic_drop) { + printf("All QUIC packets will be dropped\n"); + } + + if (section->sni_detection == SNI_DETECTION_BRUTE) { + printf("Server Name Extension will be parsed in the bruteforce mode\n"); + } + + if (section->all_domains) { + printf("All Client Hello will be targetted by youtubeUnblock!\n"); + } else { + printf("Target sni domains: %s\n", section->domains_str); + } } - - 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 8d0d55d..086a289 100644 --- a/config.h +++ b/config.h @@ -18,11 +18,10 @@ struct instance_config_t { }; extern struct instance_config_t instance_config; -struct config_t { - unsigned int queue_start_num; - int threads; - int use_gso; - int use_ipv6; +struct section_config_t { + const char *domains_str; + unsigned int domains_strlen; + int fragmentation_strategy; int frag_sni_reverse; int frag_sni_faked; @@ -39,18 +38,13 @@ struct config_t { #define FAKE_PAYLOAD_DEFAULT 2 int fake_sni_type; -#define VERBOSE_INFO 0 -#define VERBOSE_DEBUG 1 -#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; - unsigned int domains_strlen; + int synfake; + unsigned int synfake_len; + const char *exclude_domains_str; unsigned int exclude_domains_strlen; unsigned int all_domains; @@ -61,16 +55,42 @@ struct config_t { const char *fake_custom_pkt; unsigned int fake_custom_pkt_sz; - unsigned int fk_winsize; int fakeseq_offset; + +#define SNI_DETECTION_PARSE 0 +#define SNI_DETECTION_BRUTE 1 + int sni_detection; + + +}; + +#define MAX_CONFIGLIST_LEN 64 + +struct config_t { + unsigned int queue_start_num; + int threads; + int use_gso; + int use_ipv6; unsigned int mark; - int synfake; - unsigned int synfake_len; + +#define VERBOSE_INFO 0 +#define VERBOSE_DEBUG 1 +#define VERBOSE_TRACE 2 + int verbose; + + struct section_config_t default_config; + struct section_config_t custom_configs[MAX_CONFIGLIST_LEN]; + int custom_configs_len; }; extern struct config_t config; +#define ITER_CONFIG_SECTIONS(section) \ +for (struct section_config_t *section = &config.default_config + config.custom_configs_len; section >= &config.default_config; section--) + +#define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config) + #define MAX_THREADS 16 #ifndef THREADS_NUM diff --git a/kargs.c b/kargs.c index 8b63f31..953118f 100644 --- a/kargs.c +++ b/kargs.c @@ -4,11 +4,11 @@ #include #include "types.h" -static char custom_fake_buf[MAX_FAKE_SIZE]; - #define STR_MAXLEN 2048 -struct config_t config = { +static char custom_fake_buf[MAX_FAKE_SIZE]; + +static const struct section_config_t default_section_config = { .frag_sni_reverse = 1, .frag_sni_faked = 0, .fragmentation_strategy = FRAGMENTATION_STRATEGY, @@ -16,45 +16,44 @@ struct config_t config = { .faking_ttl = FAKE_TTL, .fake_sni = 1, .fake_sni_seq_len = 1, + .fake_sni_type = FAKE_PAYLOAD_DEFAULT, .frag_middle_sni = 1, .frag_sni_pos = 1, - .use_ipv6 = 1, .fakeseq_offset = 10000, - .mark = DEFAULT_RAWSOCKET_MARK, .synfake = 0, .synfake_len = 0, - .fake_sni_type = FAKE_PAYLOAD_DEFAULT, + .quic_drop = 0, - .sni_detection = SNI_DETECTION_PARSE, - -#ifdef SEG2_DELAY - .seg2_delay = SEG2_DELAY, -#else .seg2_delay = 0, -#endif - -#ifdef USE_GSO - .use_gso = 1, -#else - .use_gso = false, -#endif - -#ifdef DEBUG - .verbose = 2, -#else - .verbose = 1, -#endif .domains_str = defaul_snistr, .domains_strlen = sizeof(defaul_snistr), - .queue_start_num = DEFAULT_QUEUE_NUM, + .exclude_domains_str = "", + .exclude_domains_strlen = 0, + .fake_sni_pkt = fake_sni_old, .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator .fake_custom_pkt = custom_fake_buf, - .fake_custom_pkt_sz = 0 + .fake_custom_pkt_sz = 0, + .sni_detection = SNI_DETECTION_PARSE, }; +struct config_t config = { + .threads = THREADS_NUM, + .queue_start_num = DEFAULT_QUEUE_NUM, + .mark = DEFAULT_RAWSOCKET_MARK, + .use_ipv6 = 1, + + .verbose = VERBOSE_DEBUG, + .use_gso = 1, + + .default_config = default_section_config, + .custom_configs_len = 0 +}; + +static struct section_config_t *const def_section = &config.default_config; + static int unumeric_set(const char *val, const struct kernel_param *kp) { int n = 0, ret; ret = kstrtoint(val, 10, &n); @@ -113,19 +112,19 @@ static const struct kernel_param_ops inverse_boolean_ops = { .get = inverse_boolean_get, }; -module_param_cb(fake_sni, &boolean_parameter_ops, &config.fake_sni, 0664); -module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &config.fake_sni_seq_len, 0664); -module_param_cb(faking_ttl, &unumeric_parameter_ops, &config.faking_ttl, 0664); -module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &config.fakeseq_offset, 0664); -module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &config.frag_sni_reverse, 0664); -module_param_cb(frag_sni_faked, &boolean_parameter_ops, &config.frag_sni_faked, 0664); -module_param_cb(frag_middle_sni, &boolean_parameter_ops, &config.frag_middle_sni, 0664); -module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &config.frag_sni_pos, 0664); -module_param_cb(fk_winsize, &unumeric_parameter_ops, &config.fk_winsize, 0664); -module_param_cb(synfake, &boolean_parameter_ops, &config.synfake, 0664); -module_param_cb(synfake_len, &unumeric_parameter_ops, &config.synfake_len, 0664); +module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664); +module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &def_section->fake_sni_seq_len, 0664); +module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664); +module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664); +module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &def_section->frag_sni_reverse, 0664); +module_param_cb(frag_sni_faked, &boolean_parameter_ops, &def_section->frag_sni_faked, 0664); +module_param_cb(frag_middle_sni, &boolean_parameter_ops, &def_section->frag_middle_sni, 0664); +module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664); +module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664); +module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664); +module_param_cb(synfake_len, &unumeric_parameter_ops, &def_section->synfake_len, 0664); module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664); -// module_param_cb(seg2delay, &unumeric_parameter_ops, &config.seg2_delay, 0664); +// module_param_cb(seg2delay, &unumeric_parameter_ops, &def_section->seg2_delay, 0664); static int sni_domains_set(const char *val, const struct kernel_param *kp) { size_t len; @@ -144,13 +143,13 @@ static int sni_domains_set(const char *val, const struct kernel_param *kp) { ret = param_set_charp(val, kp); if (ret < 0) { - config.domains_strlen = 0; + def_section->domains_strlen = 0; } else { - config.domains_strlen = len; + def_section->domains_strlen = len; if (len == 3 && !strncmp(val, "all", len)) { - config.all_domains = 1; + def_section->all_domains = 1; } else { - config.all_domains = 0; + def_section->all_domains = 0; } } @@ -163,7 +162,7 @@ static const struct kernel_param_ops sni_domains_ops = { .get = param_get_charp, }; -module_param_cb(sni_domains, &sni_domains_ops, &config.domains_str, 0664); +module_param_cb(sni_domains, &sni_domains_ops, &def_section->domains_str, 0664); static int exclude_domains_set(const char *val, const struct kernel_param *kp) { size_t len; @@ -178,9 +177,9 @@ static int exclude_domains_set(const char *val, const struct kernel_param *kp) { ret = param_set_charp(val, kp); if (ret < 0) { - config.exclude_domains_strlen = 0; + def_section->exclude_domains_strlen = 0; } else { - config.exclude_domains_strlen = len; + def_section->exclude_domains_strlen = len; } return ret; @@ -191,10 +190,10 @@ static const struct kernel_param_ops exclude_domains_ops = { .get = param_get_charp, }; -module_param_cb(exclude_domains, &exclude_domains_ops, &config.exclude_domains_str, 0664); +module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664); module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664); -module_param_cb(quic_drop, &boolean_parameter_ops, &config.quic_drop, 0664); +module_param_cb(quic_drop, &boolean_parameter_ops, &def_section->quic_drop, 0664); static int verbosity_set(const char *val, const struct kernel_param *kp) { size_t len; @@ -297,7 +296,7 @@ static const struct kernel_param_ops frag_strat_ops = { .get = frag_strat_get, }; -module_param_cb(fragmentation_strategy, &frag_strat_ops, &config.fragmentation_strategy, 0664); +module_param_cb(fragmentation_strategy, &frag_strat_ops, &def_section->fragmentation_strategy, 0664); static int fake_strat_set(const char *val, const struct kernel_param *kp) { size_t len; @@ -358,7 +357,7 @@ static const struct kernel_param_ops fake_strat_ops = { .get = fake_strat_get, }; -module_param_cb(faking_strategy, &fake_strat_ops, &config.faking_strategy, 0664); +module_param_cb(faking_strategy, &fake_strat_ops, &def_section->faking_strategy, 0664); static int sni_detection_set(const char *val, const struct kernel_param *kp) { size_t len; @@ -404,7 +403,7 @@ static const struct kernel_param_ops sni_detection_ops = { .get = sni_detection_get, }; -module_param_cb(sni_detection, &sni_detection_ops, &config.sni_detection, 0664); +module_param_cb(sni_detection, &sni_detection_ops, &def_section->sni_detection, 0664); static int fake_type_set(const char *val, const struct kernel_param *kp) { size_t len; @@ -455,7 +454,7 @@ static const struct kernel_param_ops fake_type_ops = { .get = fake_type_get, }; -module_param_cb(fake_sni_type, &fake_type_ops, &config.fake_sni_type, 0664); +module_param_cb(fake_sni_type, &fake_type_ops, &def_section->fake_sni_type, 0664); static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) { size_t len; @@ -488,16 +487,16 @@ static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) { sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); } - config.fake_custom_pkt_sz = custom_len; - config.fake_custom_pkt = (char *)custom_buf; + def_section->fake_custom_pkt_sz = custom_len; + def_section->fake_custom_pkt = (char *)custom_buf; return 0; } static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) { - int cflen = config.fake_custom_pkt_sz; - const uint8_t *cbf_data = config.fake_custom_pkt; - int bflen = config.fake_custom_pkt_sz << 1; + int cflen = def_section->fake_custom_pkt_sz; + const uint8_t *cbf_data = def_section->fake_custom_pkt; + int bflen = def_section->fake_custom_pkt_sz << 1; for (int i = 0; i < cflen; i++) { sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i)); @@ -511,4 +510,4 @@ static const struct kernel_param_ops fake_custom_pl_ops = { .get = fake_custom_pl_get, }; -module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &config.fake_custom_pkt, 0664); +module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &def_section->fake_custom_pkt, 0664); diff --git a/kytunblock.c b/kytunblock.c index aa01183..c473018 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -171,25 +171,10 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { uint32_t buff1_size = MAX_PACKET_SIZE; uint32_t buff2_size = MAX_PACKET_SIZE; - switch (config.fragmentation_strategy) { - case FRAG_STRAT_TCP: - if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) { + if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { - goto erret_lc; - } - break; - case FRAG_STRAT_IP: - if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) { - - goto erret_lc; - } - break; - default: - pr_info("send_raw_socket: Packet is too big but fragmentation is disabled!"); - ret = -EINVAL; - goto erret_lc; + goto erret_lc; } int sent = 0; diff --git a/mangle.c b/mangle.c index d28cbc5..ca3b65b 100644 --- a/mangle.c +++ b/mangle.c @@ -26,6 +26,8 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { int ipver = netproto_version(raw_payload, raw_payload_len); int ret; + lgtrace_start(); + lgtrace_addp("IPv%d", ipver); if (ipver == IP4VERSION) { ret = ip4_payload_split((uint8_t *)raw_payload, raw_payload_len, @@ -52,21 +54,39 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { goto accept; } + int verdict = PKT_CONTINUE; + + if (transport_proto == IPPROTO_TCP) + lgtrace_addp("TCP"); + else if (transport_proto == IPPROTO_UDP) + lgtrace_addp("UDP"); + - switch (transport_proto) { - case IPPROTO_TCP: - return process_tcp_packet(raw_payload, raw_payload_len); - case IPPROTO_UDP: - return process_udp_packet(raw_payload, raw_payload_len); - default: - goto accept; + ITER_CONFIG_SECTIONS(section) { + lgtrace_addp("Section #%d", CONFIG_SECTION_NUMBER(section)); + + switch (transport_proto) { + case IPPROTO_TCP: + verdict = process_tcp_packet(section, raw_payload, raw_payload_len); + break; + case IPPROTO_UDP: + verdict = process_udp_packet(section, raw_payload, raw_payload_len); + break; + } + + if (verdict == PKT_CONTINUE) + continue; + + lgtrace_end(); + return verdict; } - -accept: + +accept: + lgtrace_end(); return PKT_ACCEPT; } -int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { +int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, uint32_t raw_payload_len) { const void *ipxh; uint32_t iph_len; const struct tcphdr *tcph; @@ -77,9 +97,6 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { int ipxv = netproto_version(raw_payload, raw_payload_len); - lgtrace_start("TCP"); - lgtrace_addp("IPv%d", ipxv); - int ret = tcp_payload_split((uint8_t *)raw_payload, raw_payload_len, (void *)&ipxh, &iph_len, (struct tcphdr **)&tcph, &tcph_len, @@ -90,7 +107,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { goto accept; } - if (tcph->syn && config.synfake) { + if (tcph->syn && section->synfake) { lgtrace_addp("TCP syn alter"); NETBUF_ALLOC(payload, MAX_PACKET_SIZE); @@ -101,12 +118,12 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { memcpy(payload, ipxh, iph_len); memcpy(payload + iph_len, tcph, tcph_len); - uint32_t fake_len = config.fake_sni_pkt_sz; + uint32_t fake_len = section->fake_sni_pkt_sz; - if (config.synfake_len) - fake_len = min(config.synfake_len, fake_len); + if (section->synfake_len) + fake_len = min(section->synfake_len, fake_len); - memcpy(payload + iph_len + tcph_len, config.fake_sni_pkt, fake_len); + memcpy(payload + iph_len + tcph_len, section->fake_sni_pkt, fake_len); struct tcphdr *tcph = (struct tcphdr *)(payload + iph_len); @@ -130,16 +147,15 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { NETBUF_FREE(payload); goto accept; } - lgtrace_addp("rawsocket sent %d", ret); NETBUF_FREE(payload); goto drop; } - if (tcph->syn) goto accept; + if (tcph->syn) goto continue_flow; - struct tls_verdict vrd = analyze_tls_data(data, dlen); - lgtrace_addp("Analyzed"); + struct tls_verdict vrd = analyze_tls_data(section, data, dlen); + lgtrace_addp("TLS analyzed"); if (vrd.sni_len != 0) { lgtrace_addp("SNI detected: %.*s", vrd.sni_len, data + vrd.sni_offset); @@ -173,8 +189,8 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { goto accept_lc; } - if (config.fk_winsize) { - tcph->window = htons(config.fk_winsize); + if (section->fk_winsize) { + tcph->window = htons(section->fk_winsize); set_tcp_checksum(tcph, iph, iph_len); } @@ -194,14 +210,14 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { lgdebugmsg("WARNING! Client Hello packet is too big and may cause issues!"); } - if (config.fake_sni) { - post_fake_sni(args_default_fake_type(), iph, iph_len, tcph, tcph_len); + if (section->fake_sni) { + post_fake_sni(args_default_fake_type(section), iph, iph_len, tcph, tcph_len); } size_t ipd_offset; size_t mid_offset; - switch (config.fragmentation_strategy) { + switch (section->fragmentation_strategy) { case FRAG_STRAT_TCP: { ipd_offset = vrd.sni_target_offset; mid_offset = ipd_offset + vrd.sni_target_len / 2; @@ -209,11 +225,11 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { uint32_t poses[2]; int cnt = 0; - if (config.frag_sni_pos && dlen > config.frag_sni_pos) { - poses[cnt++] = config.frag_sni_pos; + if (section->frag_sni_pos && dlen > section->frag_sni_pos) { + poses[cnt++] = section->frag_sni_pos; } - if (config.frag_middle_sni) { + if (section->frag_middle_sni) { poses[cnt++] = mid_offset; } @@ -223,7 +239,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { poses[1] = tmp; } - ret = send_tcp_frags(payload, payload_len, poses, cnt, 0); + ret = send_tcp_frags(section, payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("tcp4 send frags", ret); goto accept_lc; @@ -241,13 +257,13 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { 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); + if (section->frag_sni_pos && dlen > section->frag_sni_pos) { + poses[cnt] = section->frag_sni_pos + ((char *)data - (char *)tcph); poses[cnt] += 8 - poses[cnt] % 8; cnt++; } - if (config.frag_middle_sni) { + if (section->frag_middle_sni) { poses[cnt++] = mid_offset; } @@ -257,7 +273,7 @@ int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { poses[1] = tmp; } - ret = send_ip4_frags(payload, payload_len, poses, cnt, 0); + ret = send_ip4_frags(section, payload, payload_len, poses, cnt, 0); if (ret < 0) { lgerror("ip4 send frags", ret); goto accept_lc; @@ -292,27 +308,24 @@ drop_lc: } +continue_flow: + lgtrace_addp("continue_flow"); + return PKT_CONTINUE; accept: lgtrace_addp("accept"); - lgtrace_end(); - return PKT_ACCEPT; drop: lgtrace_addp("drop"); - lgtrace_end(); - return PKT_DROP; } -int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) { +int process_udp_packet(const struct section_config_t *section, const uint8_t *pkt, uint32_t pktlen) { const void *iph; uint32_t iph_len; const struct udphdr *udph; const uint8_t *data; uint32_t dlen; int ipver = netproto_version(pkt, pktlen); - lgtrace_start("Got udp packet"); - lgtrace_addp("IPv%d", ipver); int ret = udp_payload_split((uint8_t *)pkt, pktlen, (void **)&iph, &iph_len, @@ -334,7 +347,7 @@ int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) { } - if (config.quic_drop) { + if (section->quic_drop) { lgtrace_addp("QUIC probe"); const struct quic_lhdr *qch; uint32_t qch_len; @@ -368,9 +381,6 @@ int process_udp_packet(const uint8_t *pkt, uint32_t pktlen) { lgtrace_addp("quic initial message"); } -accept_quic: - ; - /* if (1) { lgtrace_addp("Probe udp"); @@ -402,29 +412,26 @@ accept_quic: */ - +continue_flow: + lgtrace_addp("continue_flow"); + return PKT_CONTINUE; +accept_quic: 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) { +int send_ip4_frags(const struct section_config_t *section, 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)) { + if (section->seg2_delay && ((dvs > 0) ^ section->frag_sni_reverse)) { if (!instance_config.send_delayed_packet) { return -EINVAL; } - lgtrace_addp("Sent %d delayed for %d", pktlen, config.seg2_delay); + lgtrace_addp("Sent %d delayed for %d", pktlen, section->seg2_delay); instance_config.send_delayed_packet( - packet, pktlen, config.seg2_delay); + packet, pktlen, section->seg2_delay); return 0; } else { @@ -479,21 +486,21 @@ int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses dvs += frag_pos; - if (config.frag_sni_reverse) + if (section->frag_sni_reverse) goto send_frag2; send_frag1: - ret = send_ip4_frags(frag1, f1len, NULL, 0, 0); + ret = send_ip4_frags(section, frag1, f1len, NULL, 0, 0); if (ret < 0) { goto erret_lc; } - if (config.frag_sni_reverse) + if (section->frag_sni_reverse) goto out_lc; send_fake: /* - if (config.frag_sni_faked) { - ITER_FAKE_STRAT(config.faking_strategy, strategy) { + if (section->frag_sni_faked) { + ITER_FAKE_STRAT(section->faking_strategy, strategy) { uint32_t iphfl; fake_pad_len = f2len; ret = ip4_payload_split(frag2, f2len, NULL, &iphfl, NULL, NULL); @@ -517,16 +524,16 @@ send_fake: } */ - if (config.frag_sni_reverse) + if (section->frag_sni_reverse) goto send_frag1; send_frag2: - ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); + ret = send_ip4_frags(section, frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { goto erret_lc; } - if (config.frag_sni_reverse) + if (section->frag_sni_reverse) goto send_fake; out_lc: @@ -545,15 +552,15 @@ out: return 0; } -int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_sz, uint32_t dvs) { +int send_tcp_frags(const struct section_config_t *section, 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)) { + if (section->seg2_delay && ((dvs > 0) ^ section->frag_sni_reverse)) { if (!instance_config.send_delayed_packet) { return -EINVAL; } instance_config.send_delayed_packet( - packet, pktlen, config.seg2_delay); + packet, pktlen, section->seg2_delay); return 0; } else { @@ -601,49 +608,49 @@ int send_tcp_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses dvs += poses[0]; - if (config.frag_sni_reverse) + if (section->frag_sni_reverse) goto send_frag2; send_frag1: { - ret = send_tcp_frags(frag1, f1len, NULL, 0, 0); + ret = send_tcp_frags(section, frag1, f1len, NULL, 0, 0); if (ret < 0) { goto erret_lc; } - if (config.frag_sni_reverse) + if (section->frag_sni_reverse) goto out_lc; } send_fake: - if (config.frag_sni_faked) { + if (section->frag_sni_faked) { uint32_t iphfl, tcphfl; void *iph; struct tcphdr *tcph; ret = tcp_payload_split(frag2, f2len, &iph, &iphfl, &tcph, &tcphfl, NULL, NULL); - struct fake_type f_type = args_default_fake_type(); + struct fake_type f_type = args_default_fake_type(section); if ((f_type.strategy.strategy & FAKE_STRAT_PAST_SEQ) == FAKE_STRAT_PAST_SEQ) { f_type.strategy.strategy ^= FAKE_STRAT_PAST_SEQ; f_type.strategy.strategy |= FAKE_STRAT_RAND_SEQ; f_type.strategy.randseq_offset = dvs; } - f_type.seg2delay = config.seg2_delay; + f_type.seg2delay = section->seg2_delay; post_fake_sni(f_type, iph, iphfl, tcph, tcphfl); } - if (config.frag_sni_reverse) + if (section->frag_sni_reverse) goto send_frag1; send_frag2: { - ret = send_tcp_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs); + ret = send_tcp_frags(section, frag2, f2len, poses + 1, poses_sz - 1, dvs); if (ret < 0) { goto erret_lc; } - if (config.frag_sni_reverse) + if (section->frag_sni_reverse) goto send_fake; } out_lc: diff --git a/mangle.h b/mangle.h index aa503d3..0644d6c 100644 --- a/mangle.h +++ b/mangle.h @@ -6,6 +6,8 @@ #define PKT_ACCEPT 0 #define PKT_DROP 1 +// Used for section config +#define PKT_CONTINUE 2 /** * Processes the packet and returns verdict. @@ -18,14 +20,14 @@ int process_packet(const uint8_t *packet, uint32_t packet_len); * Processe the TCP packet. * Returns verdict. */ -int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len); +int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, uint32_t raw_payload_len); /** * Processes the UDP packet. * Returns verdict. */ -int process_udp_packet(const uint8_t *pkt, uint32_t pktlen); +int process_udp_packet(const struct section_config_t *section, const uint8_t *pkt, uint32_t pktlen); @@ -41,7 +43,7 @@ int post_fake_sni(struct fake_type f_type, * Poses are relative to start of TCP payload. * dvs used internally and should be zero. */ -int send_tcp_frags( +int send_tcp_frags(const struct section_config_t *section, const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_len, uint32_t dvs); @@ -50,7 +52,7 @@ int send_tcp_frags( * Poses are relative to start of TCP payload. * dvs used internally and should be zero. */ -int send_ip4_frags( +int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_len, uint32_t dvs); #endif /* YU_MANGLE_H */ diff --git a/tls.c b/tls.c index 6b13c3a..e67c18c 100644 --- a/tls.c +++ b/tls.c @@ -21,6 +21,7 @@ * dlen Length of `data`. */ struct tls_verdict analyze_tls_data( + const struct section_config_t *section, const uint8_t *data, uint32_t dlen) { @@ -43,7 +44,7 @@ struct tls_verdict analyze_tls_data( if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) goto nextMessage; - if (config.sni_detection == SNI_DETECTION_BRUTE) { + if (section->sni_detection == SNI_DETECTION_BRUTE) { goto brute; } @@ -126,22 +127,22 @@ struct tls_verdict analyze_tls_data( vrd.sni_len = sni_len; vrd.sni_target_len = vrd.sni_len; - if (config.all_domains) { + if (section->all_domains) { vrd.target_sni = 1; goto check_domain; } unsigned int j = 0; - for (unsigned int i = 0; i <= config.domains_strlen; i++) { + for (unsigned int i = 0; i <= section->domains_strlen; i++) { if ( i > j && - (i == config.domains_strlen || - config.domains_str[i] == '\0' || - config.domains_str[i] == ',' || - config.domains_str[i] == '\n' )) { + (i == section->domains_strlen || + section->domains_str[i] == '\0' || + section->domains_str[i] == ',' || + section->domains_str[i] == '\n' )) { unsigned int domain_len = (i - j); const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = config.domains_str + j; + const char *domain_startp = section->domains_str + j; if (sni_len >= domain_len && sni_len < 128 && @@ -159,18 +160,18 @@ struct tls_verdict analyze_tls_data( } check_domain: - if (vrd.target_sni == 1 && config.exclude_domains_strlen != 0) { + if (vrd.target_sni == 1 && section->exclude_domains_strlen != 0) { unsigned int j = 0; - for (unsigned int i = 0; i <= config.exclude_domains_strlen; i++) { + for (unsigned int i = 0; i <= section->exclude_domains_strlen; i++) { if ( i > j && - (i == config.exclude_domains_strlen || - config.exclude_domains_str[i] == '\0' || - config.exclude_domains_str[i] == ',' || - config.exclude_domains_str[i] == '\n' )) { + (i == section->exclude_domains_strlen || + section->exclude_domains_str[i] == '\0' || + section->exclude_domains_str[i] == ',' || + section->exclude_domains_str[i] == '\n' )) { unsigned int domain_len = (i - j); const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = config.exclude_domains_str + j; + const char *domain_startp = section->exclude_domains_str + j; if (sni_len >= domain_len && sni_len < 128 && @@ -203,7 +204,7 @@ out: brute: - if (config.all_domains) { + if (section->all_domains) { vrd.target_sni = 1; vrd.sni_len = 0; vrd.sni_offset = dlen / 2; @@ -211,15 +212,15 @@ brute: } unsigned int j = 0; - for (unsigned int i = 0; i <= config.domains_strlen; i++) { + for (unsigned int i = 0; i <= section->domains_strlen; i++) { if ( i > j && - (i == config.domains_strlen || - config.domains_str[i] == '\0' || - config.domains_str[i] == ',' || - config.domains_str[i] == '\n' )) { + (i == section->domains_strlen || + section->domains_str[i] == '\0' || + section->domains_str[i] == ',' || + section->domains_str[i] == '\n' )) { unsigned int domain_len = (i - j); - const char *domain_startp = config.domains_str + j; + const char *domain_startp = section->domains_str + j; if (domain_len + dlen + 1> MAX_PACKET_SIZE) { continue; diff --git a/tls.h b/tls.h index 9b4c2a2..024a1cc 100644 --- a/tls.h +++ b/tls.h @@ -20,7 +20,7 @@ struct tls_verdict { * Processes the packet and finds TLS Client Hello information inside it. * data pointer points to start of TLS Message (TCP Payload) */ -struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen); +struct tls_verdict analyze_tls_data(const struct section_config_t *section, const uint8_t *data, uint32_t dlen); /** diff --git a/utils.h b/utils.h index ddae729..d78b7b5 100644 --- a/utils.h +++ b/utils.h @@ -157,34 +157,34 @@ int seqovl_packet(uint8_t *payload, uint32_t *plen, uint32_t seq_delta); -static inline struct failing_strategy args_default_failing_strategy(void) { +static inline struct failing_strategy args_default_failing_strategy(const struct section_config_t *section) { struct failing_strategy fl_strat = { - .strategy = (unsigned int)config.faking_strategy, - .faking_ttl = config.faking_ttl, - .randseq_offset = (uint32_t)config.fakeseq_offset + .strategy = (unsigned int)section->faking_strategy, + .faking_ttl = section->faking_ttl, + .randseq_offset = (uint32_t)section->fakeseq_offset }; return fl_strat; } -static inline struct fake_type args_default_fake_type(void) { +static inline struct fake_type args_default_fake_type(const struct section_config_t *section) { struct fake_type f_type = { - .sequence_len = config.fake_sni_seq_len, - .strategy = args_default_failing_strategy(), + .sequence_len = section->fake_sni_seq_len, + .strategy = args_default_failing_strategy(section), }; - switch (config.fake_sni_type) { + switch (section->fake_sni_type) { case FAKE_PAYLOAD_RANDOM: f_type.type = FAKE_PAYLOAD_RANDOM; break; case FAKE_PAYLOAD_CUSTOM: f_type.type = FAKE_PAYLOAD_CUSTOM; - f_type.fake_data = config.fake_custom_pkt; - f_type.fake_len = config.fake_custom_pkt_sz; + f_type.fake_data = section->fake_custom_pkt; + f_type.fake_len = section->fake_custom_pkt_sz; break; default: f_type.type = FAKE_PAYLOAD_CUSTOM; - f_type.fake_data = config.fake_sni_pkt; - f_type.fake_len = config.fake_sni_pkt_sz; + f_type.fake_data = section->fake_sni_pkt; + f_type.fake_len = section->fake_sni_pkt_sz; } return f_type; diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 086dbaf..6e4ee5c 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -264,27 +264,11 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { uint8_t buff2[MNL_SOCKET_BUFFER_SIZE]; uint32_t buff2_size = MNL_SOCKET_BUFFER_SIZE; - switch (config.fragmentation_strategy) { - case FRAG_STRAT_TCP: - if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) { + if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128, + buff1, &buff1_size, buff2, &buff2_size)) < 0) { - errno = -ret; - return ret; - } - break; - case FRAG_STRAT_IP: - if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128, - buff1, &buff1_size, buff2, &buff2_size)) < 0) { - - errno = -ret; - return ret; - } - break; - default: - errno = EINVAL; - printf("send_raw_socket: Packet is too big but fragmentation is disabled!\n"); - return -EINVAL; + errno = -ret; + return ret; } int sent = 0; From 96cf0365ee4fed76623c64fc2a2039f17f32b1ca Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 14 Oct 2024 00:11:58 +0300 Subject: [PATCH 13/14] Fix possible errors on older compilers --- args.c | 35 +---------------------------------- config.h | 33 +++++++++++++++++++++++++++++++++ kargs.c | 34 +--------------------------------- 3 files changed, 35 insertions(+), 67 deletions(-) diff --git a/args.c b/args.c index c7ac2c0..7f01001 100644 --- a/args.c +++ b/args.c @@ -1,5 +1,4 @@ #include "config.h" -#include "raw_replacements.h" #include #include #include @@ -12,38 +11,6 @@ static char custom_fake_buf[MAX_FAKE_SIZE]; -static const struct section_config_t default_section_config = { - .frag_sni_reverse = 1, - .frag_sni_faked = 0, - .fragmentation_strategy = FRAGMENTATION_STRATEGY, - .faking_strategy = FAKING_STRATEGY, - .faking_ttl = FAKE_TTL, - .fake_sni = 1, - .fake_sni_seq_len = 1, - .fake_sni_type = FAKE_PAYLOAD_DEFAULT, - .frag_middle_sni = 1, - .frag_sni_pos = 1, - .fakeseq_offset = 10000, - .synfake = 0, - .synfake_len = 0, - .quic_drop = 0, - - .seg2_delay = 0, - - .domains_str = defaul_snistr, - .domains_strlen = sizeof(defaul_snistr), - - .exclude_domains_str = "", - .exclude_domains_strlen = 0, - - .fake_sni_pkt = fake_sni_old, - .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator - .fake_custom_pkt = custom_fake_buf, - .fake_custom_pkt_sz = 0, - - .sni_detection = SNI_DETECTION_PARSE, -}; - struct config_t config = { .threads = THREADS_NUM, .queue_start_num = DEFAULT_QUEUE_NUM, @@ -268,7 +235,7 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; sect_config = &config.custom_configs[config.custom_configs_len++]; - *sect_config = default_section_config; + *sect_config = (struct section_config_t)default_section_config; section_iter = SECT_ITER_INSIDE; break; diff --git a/config.h b/config.h index 086a289..f3d6683 100644 --- a/config.h +++ b/config.h @@ -5,6 +5,8 @@ #define USER_SPACE #endif +#include "raw_replacements.h" + typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len); /** * Sends the packet after delay_ms. The function should schedule send and return immediately @@ -91,6 +93,37 @@ for (struct section_config_t *section = &config.default_config + config.custom_c #define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config) +#define default_section_config { \ + .frag_sni_reverse = 1, \ + .frag_sni_faked = 0, \ + .fragmentation_strategy = FRAGMENTATION_STRATEGY, \ + .faking_strategy = FAKING_STRATEGY, \ + .faking_ttl = FAKE_TTL, \ + .fake_sni = 1, \ + .fake_sni_seq_len = 1, \ + .fake_sni_type = FAKE_PAYLOAD_DEFAULT, \ + .frag_middle_sni = 1, \ + .frag_sni_pos = 1, \ + .fakeseq_offset = 10000, \ + .synfake = 0, \ + .synfake_len = 0, \ + .quic_drop = 0, \ + \ + .seg2_delay = 0, \ + \ + .domains_str = defaul_snistr, \ + .domains_strlen = sizeof(defaul_snistr), \ + \ + .exclude_domains_str = "", \ + .exclude_domains_strlen = 0, \ + \ + .fake_sni_pkt = fake_sni_old, \ + .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \ + .fake_custom_pkt = custom_fake_buf, \ + .fake_custom_pkt_sz = 0, \ + .sni_detection = SNI_DETECTION_PARSE, \ +} + #define MAX_THREADS 16 #ifndef THREADS_NUM diff --git a/kargs.c b/kargs.c index 953118f..0e596b3 100644 --- a/kargs.c +++ b/kargs.c @@ -1,5 +1,4 @@ #include "config.h" -#include "raw_replacements.h" #include "types.h" #include #include "types.h" @@ -8,37 +7,6 @@ static char custom_fake_buf[MAX_FAKE_SIZE]; -static const struct section_config_t default_section_config = { - .frag_sni_reverse = 1, - .frag_sni_faked = 0, - .fragmentation_strategy = FRAGMENTATION_STRATEGY, - .faking_strategy = FAKING_STRATEGY, - .faking_ttl = FAKE_TTL, - .fake_sni = 1, - .fake_sni_seq_len = 1, - .fake_sni_type = FAKE_PAYLOAD_DEFAULT, - .frag_middle_sni = 1, - .frag_sni_pos = 1, - .fakeseq_offset = 10000, - .synfake = 0, - .synfake_len = 0, - .quic_drop = 0, - - .seg2_delay = 0, - - .domains_str = defaul_snistr, - .domains_strlen = sizeof(defaul_snistr), - - .exclude_domains_str = "", - .exclude_domains_strlen = 0, - - .fake_sni_pkt = fake_sni_old, - .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator - .fake_custom_pkt = custom_fake_buf, - .fake_custom_pkt_sz = 0, - .sni_detection = SNI_DETECTION_PARSE, -}; - struct config_t config = { .threads = THREADS_NUM, .queue_start_num = DEFAULT_QUEUE_NUM, @@ -52,7 +20,7 @@ struct config_t config = { .custom_configs_len = 0 }; -static struct section_config_t *const def_section = &config.default_config; +#define def_section (&config.default_config) static int unumeric_set(const char *val, const struct kernel_param *kp) { int n = 0, ret; From ed08feaf202e7132391b5d9e1e1fed99e9c353e7 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 14 Oct 2024 00:22:25 +0300 Subject: [PATCH 14/14] Mention #148 in README Troubleshooting --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dfd31d4..942610f 100644 --- a/README.md +++ b/README.md @@ -235,6 +235,8 @@ Available flags: ## Troubleshooting +Check up [this issue](https://github.com/Waujito/youtubeUnblock/issues/148) for useful configs. + 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.