From 457a7a7f04c0f520b6d70d59b0f652c1c927d83d Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 8 Dec 2024 16:06:50 +0300 Subject: [PATCH] Massive update of argparse system This is required for furhter maintance of kernel module. Aims to provide common interface for both --- Kbuild | 2 +- args.c | 777 +++++++++++++++++++++++++--------- args.h | 14 +- config.h | 77 ++-- getopt.c | 204 +++++++++ getopt.h | 45 ++ kargs.c | 1041 ++++++++++++++++++++++++---------------------- kytunblock.c | 9 +- mangle.c | 2 +- tls.c | 251 ++++++----- types.h | 8 + uspace.mk | 2 +- youtubeUnblock.c | 2 +- 13 files changed, 1582 insertions(+), 852 deletions(-) create mode 100644 getopt.c create mode 100644 getopt.h diff --git a/Kbuild b/Kbuild index 36eef22..3dbbfe3 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o -kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o getopt.o args.o ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/args.c b/args.c index e19b3e1..89c0284 100644 --- a/args.c +++ b/args.c @@ -1,33 +1,219 @@ #include "config.h" -#include -#include -#include -#include -#include -#include -#include +#include "types.h" + #include "types.h" #include "args.h" #include "logging.h" +#include "getopt.h" +#include "raw_replacements.h" -static char custom_fake_buf[MAX_FAKE_SIZE]; +#ifdef KERNEL_SPACE +static int errno = 0; +#define strtol kstrtol +#endif -struct config_t config = { - .threads = THREADS_NUM, - .queue_start_num = DEFAULT_QUEUE_NUM, - .mark = DEFAULT_RAWSOCKET_MARK, - .use_ipv6 = 1, +struct config_t config = default_config_set; - .verbose = VERBOSE_DEBUG, - .use_gso = true, +static int parse_sni_domains(struct domains_list **dlist, const char *domains_str, size_t domains_strlen) { + // Empty and shouldn't be used + struct domains_list ndomain = {0}; + struct domains_list *cdomain = &ndomain; - .default_config = default_section_config, - .custom_configs_len = 0, + unsigned int j = 0; + for (unsigned int i = 0; i <= domains_strlen; i++) { + if (( i == domains_strlen || + domains_str[i] == '\0' || + domains_str[i] == ',' || + domains_str[i] == '\n' )) { - .daemonize = 0, - .noclose = 0, - .syslog = 0, -}; + if (i == j) { + j++; + continue; + } + + unsigned int domain_len = (i - j); + const char *domain_startp = domains_str + j; + struct domains_list *edomain = malloc(sizeof(struct domains_list)); + *edomain = (struct domains_list){0}; + if (edomain == NULL) { + return -ENOMEM; + } + + edomain->domain_len = domain_len; + edomain->domain_name = malloc(domain_len + 1); + if (edomain->domain_name == NULL) { + return -ENOMEM; + } + + strncpy(edomain->domain_name, domain_startp, domain_len); + edomain->domain_name[domain_len] = '\0'; + cdomain->next = edomain; + cdomain = edomain; + + j = i + 1; + } + } + + *dlist = ndomain.next; + return 0; +} + +static void free_sni_domains(struct domains_list *dlist) { + for (struct domains_list *ldl = dlist; ldl != NULL;) { + struct domains_list *ndl = ldl->next; + printf("freeing domains\n"); + SFREE(ldl->domain_name); + SFREE(ldl); + ldl = ndl; + } +} + +static long parse_numeric_option(const char* value) { + errno = 0; + + if (*value == '\0') { + errno = EINVAL; + return 0; + } + + long result; + int len; + sscanf(value, "%ld%n", &result, &len); + if (*(value + len) != '\0') { + errno = EINVAL; + return 0; + } + + return result; +} + +static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) { + int ret = 0; + int seclen = 1; + const char *p = str; + while (*p != '\0') { + if (*p == ',') + seclen++; + p++; + } + +#ifdef KERNEL_SPACE + struct udp_dport_range *udp_dport_ranges = kmalloc( + seclen * sizeof(struct udp_dport_range), GFP_KERNEL); + +#else + struct udp_dport_range *udp_dport_ranges = malloc( + seclen * sizeof(struct udp_dport_range)); +#endif + if (udp_dport_ranges == NULL) { + return -ENOMEM; + } + + int i = 0; + + + p = str; + const char *ep = p; + while (1) { + if (*ep == '\0' || *ep == ',') { + if (ep == p) { + if (*ep == '\0') + break; + + p++, ep++; + continue; + } + + const char *endp; + long num1; + int len; + sscanf(p, "%ld%n", &num1, &len); + endp = p + len; + long num2 = num1; + + if (endp != ep) { + if (*endp == '-') { + endp++; + int len; + sscanf(endp, "%ld%n", &num2, &len); + endp = endp + len; + + if (endp != ep) + goto erret; + } else { + goto erret; + } + } + + if ( + !(num1 > 0 && num1 < (1 << 16)) || + !(num2 > 0 && num2 < (1 << 16)) || + num2 < num1 + ) + goto erret; + + udp_dport_ranges[i] = (struct udp_dport_range){ + .start = num1, + .end = num2 + }; + i++; + + if (*ep == '\0') { + break; + } else { + p = ep + 1; + ep = p; + } + } else { + ep++; + } + } + + if (i == 0) { + free(udp_dport_ranges); + } + + *udpr = udp_dport_ranges; + *udpr_len = i; + return 0; + +erret: + free(udp_dport_ranges); + + return -1; +} + +// Allocates and fills custom fake buffer +static int parse_fake_custom_payload( + const char *custom_hex_fake, + char **custom_fake_buf, unsigned int *custom_fake_len) { + int ret; + + size_t custom_hlen = strlen(custom_hex_fake); + if ((custom_hlen & 1) == 1) { + printf("Custom fake hex should be divisible by two\n"); + return -EINVAL; + } + + size_t custom_len = custom_hlen >> 1; + if (custom_len > MAX_FAKE_SIZE) { + printf("Custom fake is too large\n"); + return -EINVAL; + } + unsigned char *custom_buf = malloc(custom_len); + + for (int i = 0; i < custom_len; i++) { + ret = sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); + if (ret != 1) { + free(custom_buf); + return -EINVAL; + } + } + + *custom_fake_buf = (char *)custom_buf; + *custom_fake_len = custom_len; + return 0; +} enum { OPT_SNI_DOMAINS, @@ -69,11 +255,14 @@ enum { OPT_UDP_DPORT_FILTER, OPT_UDP_FILTER_QUIC, OPT_TLS_ENABLED, + OPT_CLS, + OPT_HELP, + OPT_VERSION, }; static struct option long_opt[] = { - {"help", 0, 0, 'h'}, - {"version", 0, 0, 'v'}, + {"help", 0, 0, OPT_HELP}, + {"version", 0, 0, OPT_VERSION}, {"sni-domains", 1, 0, OPT_SNI_DOMAINS}, {"exclude-domains", 1, 0, OPT_EXCLUDE_DOMAINS}, {"fake-sni", 1, 0, OPT_FAKE_SNI}, @@ -113,27 +302,10 @@ static struct option long_opt[] = { {"packet-mark", 1, 0, OPT_PACKET_MARK}, {"fbegin", 0, 0, OPT_START_SECTION}, {"fend", 0, 0, OPT_END_SECTION}, - {0,0,0,0} + {"cls", 0, 0, OPT_CLS}, + {0, 0, 0, 0}, }; -static long parse_numeric_option(const char* value) { - errno = 0; - - if (*value == '\0') { - errno = EINVAL; - return 0; - } - - char* end; - long result = strtol(value, &end, 10); - if (*end != '\0') { - errno = EINVAL; - return 0; - } - - return result; -} - void print_version(void) { printf("youtubeUnblock" #if defined(PKG_VERSION) @@ -192,93 +364,22 @@ void print_usage(const char *argv0) { printf("\n"); } -int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) { - int ret = 0; - int seclen = 1; - int strlen = 0; - const char *p = optarg; - while (*p != '\0') { - if (*p == ',') - seclen++; - p++; - } - strlen = p - optarg; - - struct udp_dport_range *udp_dport_ranges = malloc( - seclen * sizeof(struct udp_dport_range)); - - int i = 0; - - - p = optarg; - const char *ep = p; - while (1) { - if (*ep == '\0' || *ep == ',') { - if (ep == p) { - if (*ep == '\0') - break; - - p++, ep++; - continue; - } - - char *endp; - long num1 = strtol(p, &endp, 10); - long num2 = num1; - if (errno) - goto erret; - - if (endp != ep) { - if (*endp == '-') { - endp++; - num2 = strtol(endp, &endp, 10); - - if (endp != ep || errno) - goto erret; - } else { - goto erret; - } - } - - if ( - !(num1 > 0 && num1 < (1 << 16)) || - !(num2 > 0 && num2 < (1 << 16)) || - num2 < num1 - ) - goto erret; - - udp_dport_ranges[i] = (struct udp_dport_range){ - .start = num1, - .end = num2 - }; - i++; - - if (*ep == '\0') { - break; - } else { - p = ep + 1; - ep = p; - } - } else { - ep++; - } - } - - *udpr = udp_dport_ranges; - *udpr_len = seclen; - return 0; - -erret: - free(udp_dport_ranges); - return -1; -} - -int parse_args(int argc, char *argv[]) { +int yparse_args(int argc, char *argv[]) { int opt; int optIdx = 0; + optind=1, opterr=1, optreset=0; long num; + int ret; - struct section_config_t *sect_config = &config.default_config; + struct config_t rep_config; + ret = init_config(&rep_config); + if (ret < 0) + return ret; + struct section_config_t *default_section = rep_config.last_section; + + struct section_config_t *sect_config = rep_config.last_section; + int sect_i = 0; + sect_config->id = sect_i++; #define SECT_ITER_DEFAULT 1 #define SECT_ITER_INSIDE 2 @@ -286,86 +387,95 @@ int parse_args(int argc, char *argv[]) { int section_iter = SECT_ITER_DEFAULT; - while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) { + while ((opt = getopt_long(argc, argv, "", long_opt, &optIdx)) != -1) { switch (opt) { + case OPT_CLS: + free_config(rep_config); + ret = init_config(&rep_config); + if (ret < 0) + return ret; + default_section = rep_config.last_section; + + sect_config = rep_config.last_section; + sect_i = 0; + sect_config->id = sect_i++; + section_iter = SECT_ITER_DEFAULT; + + break; + /* config_t scoped configs */ - case 'h': + case OPT_HELP: print_usage(argv[0]); +#ifndef KERNEL_SPACE goto stop_exec; - case 'v': +#else + break; +#endif + case OPT_VERSION: print_version(); +#ifndef KERNEL_SPACE goto stop_exec; +#else + break; +#endif case OPT_TRACE: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - config.verbose = 2; + rep_config.verbose = 2; break; case OPT_SILENT: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.verbose = 0; + rep_config.verbose = 0; break; case OPT_NO_GSO: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.use_gso = 0; + rep_config.use_gso = 0; break; case OPT_NO_IPV6: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.use_ipv6 = 0; + rep_config.use_ipv6 = 0; break; case OPT_DAEMONIZE: - config.daemonize = 1; + rep_config.daemonize = 1; break; case OPT_NOCLOSE: - config.noclose = 1; + rep_config.noclose = 1; break; case OPT_SYSLOG: - config.syslog = 1; + rep_config.syslog = 1; break; case OPT_THREADS: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > MAX_THREADS) { goto invalid_opt; } - config.threads = num; + rep_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; + rep_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; + rep_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 = (struct section_config_t)default_section_config; + struct section_config_t *nsect; + ret = init_section_config(&nsect, rep_config.last_section); + if (ret < 0) { + goto error; + } + rep_config.last_section->next = nsect; + rep_config.last_section = nsect; + sect_config = nsect; + sect_config->id = sect_i++; section_iter = SECT_ITER_INSIDE; break; @@ -374,7 +484,7 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; section_iter = SECT_ITER_OUTSIDE; - sect_config = &config.default_config; + sect_config = default_section; break; /* section_config_t scoped configs */ @@ -389,16 +499,20 @@ int parse_args(int argc, char *argv[]) { break; case OPT_SNI_DOMAINS: + sect_config->all_domains = 0; if (!strcmp(optarg, "all")) { sect_config->all_domains = 1; } - sect_config->domains_str = optarg; - sect_config->domains_strlen = strlen(sect_config->domains_str); + ret = parse_sni_domains(§_config->sni_domains, optarg, strlen(optarg)); + if (ret < 0) + goto error; break; case OPT_EXCLUDE_DOMAINS: - sect_config->exclude_domains_str = optarg; - sect_config->exclude_domains_strlen = strlen(sect_config->exclude_domains_str); + ret = parse_sni_domains(§_config->exclude_sni_domains, optarg, strlen(optarg)); + if (ret < 0) + goto error; + break; case OPT_FRAG: if (strcmp(optarg, "tcp") == 0) { @@ -512,30 +626,16 @@ int parse_args(int argc, char *argv[]) { } break; - case OPT_FAKE_CUSTOM_PAYLOAD: { - uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; + case OPT_FAKE_CUSTOM_PAYLOAD: + SFREE(sect_config->udp_dport_range); - 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); - } - - sect_config->fake_custom_pkt_sz = custom_len; - sect_config->fake_custom_pkt = (char *)custom_buf; + ret = parse_fake_custom_payload(optarg, §_config->fake_custom_pkt, §_config->fake_custom_pkt_sz); + if (ret == -EINVAL) { + goto invalid_opt; + } else if (ret < 0) { + goto error; } + break; case OPT_FK_WINSIZE: num = parse_numeric_option(optarg); @@ -623,13 +723,10 @@ int parse_args(int argc, char *argv[]) { break; case OPT_UDP_DPORT_FILTER: { - struct udp_dport_range *udp_dport_range; - int udp_range_len = 0; - if (parse_udp_dport_range(optarg, &udp_dport_range, &udp_range_len) < 0) { + SFREE(sect_config->udp_dport_range); + if (parse_udp_dport_range(optarg, §_config->udp_dport_range, §_config->udp_dport_range_len) < 0) { goto invalid_opt; } - sect_config->udp_dport_range = udp_dport_range; - sect_config->udp_dport_range_len = udp_range_len; break; } case OPT_UDP_FILTER_QUIC: @@ -648,22 +745,248 @@ int parse_args(int argc, char *argv[]) { } + struct config_t old_config = config; + config = rep_config; + free_config(old_config); errno = 0; return 0; + stop_exec: + free_config(rep_config); errno = 0; return 1; invalid_opt: printf("Invalid option %s\n", long_opt[optIdx].name); + ret = -EINVAL; error: +#ifndef KERNEL_SPACE print_usage(argv[0]); - errno = EINVAL; +#endif + if (ret != -EINVAL) { + lgerror(ret, "Error thrown in %s\n", long_opt[optIdx].name); + } + + errno = -ret; + free_config(rep_config); return -errno; } +#define print_cnf_raw(fmt, ...) do { \ + sz = snprintf(buf_ptr, buf_sz, fmt, ##__VA_ARGS__); \ + if (sz > buf_sz) { buf_sz = 0; } \ + else { buf_sz -= sz; } \ + buf_ptr += sz; \ +} while(0) + +#define print_cnf_buf(fmt, ...) print_cnf_raw(fmt " ", ##__VA_ARGS__) +// Returns written buffer size +static size_t print_config_section(const struct section_config_t *section, char *buffer, size_t buffer_size) { + char *buf_ptr = buffer; + size_t buf_sz = buffer_size; + size_t sz; + + if (section->tls_enabled) { + print_cnf_buf("--tls=enabled"); + if (section->sni_domains != NULL) { + print_cnf_raw("--sni-domains="); + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + print_cnf_raw("%s,", sne->domain_name); + } + print_cnf_raw(" "); + } + if (section->exclude_sni_domains != NULL) { + print_cnf_raw("--exclude-domains="); + for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) { + print_cnf_raw("%s,", sne->domain_name); + } + print_cnf_raw(" "); + } + + switch(section->fragmentation_strategy) { + case FRAG_STRAT_IP: + print_cnf_buf("--frag=ip"); + break; + case FRAG_STRAT_TCP: + print_cnf_buf("--frag=tcp"); + break; + case FRAG_STRAT_NONE: + print_cnf_buf("--frag=none"); + break; + } + + print_cnf_buf("frag-sni-reverse=%d", section->frag_sni_reverse); + print_cnf_buf("frag-sni-faked=%d", section->frag_sni_faked); + print_cnf_buf("frag-middle-sni=%d", section->frag_middle_sni); + print_cnf_buf("frag-sni-pos=%d", section->frag_sni_pos); + print_cnf_buf("fk-winsize=%d", section->fk_winsize); + + if (section->fake_sni) { + print_cnf_buf("--fake-sni=1"); + print_cnf_buf("--fake-sni-seq-len=%d", section->fake_sni_seq_len); + switch(section->fake_sni_type) { + case FAKE_PAYLOAD_CUSTOM: + print_cnf_buf("--fake-sni-type=custom"); + print_cnf_buf("--fake-custom-payload="); + break; + case FAKE_PAYLOAD_RANDOM: + print_cnf_buf("--fake-sni-type=random"); + break; + case FAKE_PAYLOAD_DEFAULT: + print_cnf_buf("--fake-sni-type=default"); + break; + } + + switch(section->faking_strategy) { + case FAKE_STRAT_TTL: + print_cnf_buf("--faking-strategy=ttl"); + print_cnf_buf("--faking-ttl=%d", section->faking_ttl); + break; + case FAKE_STRAT_RAND_SEQ: + print_cnf_buf("--faking-strategy=randseq"); + break; + case FAKE_STRAT_TCP_CHECK: + print_cnf_buf("--faking-strategy=tcp_check"); + break; + case FAKE_STRAT_TCP_MD5SUM: + print_cnf_buf("--faking-strategy=md5sum"); + break; + case FAKE_STRAT_PAST_SEQ: + print_cnf_buf("--faking-strategy=pastseq"); + print_cnf_buf("--fake-seq-offset=%d", section->fakeseq_offset); + break; + + } + + switch(section->sni_detection) { + case SNI_DETECTION_BRUTE: + print_cnf_buf("--sni_detection=brute"); + break; + case SNI_DETECTION_PARSE: + print_cnf_buf("--sni_detection=parse"); + break; + + } + + print_cnf_buf("--seg2delay=%d", section->seg2_delay); + } + } else { + print_cnf_buf("--tls=disabled"); + } + + if (section->synfake) { + print_cnf_buf("--synfake=1"); + print_cnf_buf("--synfake-len=%d", section->synfake_len); + } else { + print_cnf_buf("--synfake=0"); + } + + + if (section->udp_filter_quic == UDP_FILTER_QUIC_ALL && section->udp_mode == UDP_MODE_DROP) { + print_cnf_buf("--drop-quic"); + } + + switch(section->udp_filter_quic) { + case UDP_FILTER_QUIC_ALL: + print_cnf_buf("--udp-filter-quic=all"); + break; + case UDP_FILTER_QUIC_DISABLED: + print_cnf_buf("--udp-filter-quic=disabled"); + break; + } + + if (section->udp_dport_range_len != 0) + print_cnf_raw("--udp-dport-filter="); + for (int i = 0; i < section->udp_dport_range_len; i++) { + struct udp_dport_range range = section->udp_dport_range[i]; + print_cnf_raw("%d-%d,", range.start, range.end); + } + print_cnf_raw(" "); + + + if (section->udp_filter_quic != UDP_FILTER_QUIC_DISABLED || section->udp_dport_range_len != 0) { + switch(section->udp_mode) { + case UDP_MODE_DROP: + print_cnf_buf("--udp-mode=drop"); + break; + case UDP_MODE_FAKE: + print_cnf_buf("--udp-mode=fake"); + print_cnf_buf("--udp-fake-seq-len=%d", section->udp_fake_seq_len); + { + switch(section->udp_faking_strategy) { + case FAKE_STRAT_UDP_CHECK: + print_cnf_buf("--udp-faking-strategy=checksum"); + break; + case FAKE_STRAT_TTL: + print_cnf_buf("--udp-faking-strategy=ttl"); + } + } + break; + } + } + + return buffer_size - buf_sz; +} +// Returns written buffer length +size_t print_config(char *buffer, size_t buffer_size) { + char *buf_ptr = buffer; + size_t buf_sz = buffer_size; + size_t sz; + +#ifndef KERNEL_SPACE + print_cnf_buf("--queue-num=%d", config.queue_start_num); + print_cnf_buf("--threads=%d", config.threads); +#endif + print_cnf_buf("--mark=%d", config.mark); + +#ifndef KERNEL_SPACE + if (config.daemonize) { + print_cnf_buf("--daemonize"); + } + if (config.syslog) { + print_cnf_buf("--syslog"); + } + if (config.noclose) { + print_cnf_buf("--noclose"); + } + if (!config.use_gso) { + print_cnf_buf("--no-gso"); + } +#endif + if (!config.use_ipv6) { + print_cnf_buf("--no-ipv6"); + } + if (config.verbose == VERBOSE_TRACE) { + print_cnf_buf("--trace"); + } + if (config.verbose == VERBOSE_INFO) { + print_cnf_buf("--silent"); + } + + size_t wbuf_len = print_config_section(config.first_section, buf_ptr, buf_sz); + buf_ptr += wbuf_len; + buf_sz -= wbuf_len; + + for (struct section_config_t *section = config.first_section->next; + section != NULL; section = section->next) { + print_cnf_buf("--fbegin"); + wbuf_len = print_config_section(section, buf_ptr, buf_sz); + buf_ptr += wbuf_len; + buf_sz -= wbuf_len; + print_cnf_buf("--fend"); + } + + return buffer_size - buf_sz; +} + void print_welcome(void) { + char welcome_message[4000]; + + size_t sz = print_config(welcome_message, 4000); + printf("Running with flags: %.*s\n", (int)sz, welcome_message); + return; +/** if (config.syslog) { printf("Logging to system log\n"); } @@ -762,5 +1085,73 @@ void print_welcome(void) { lginfo("Target sni domains: %s\n", section->domains_str); } } +*/ } +int init_section_config(struct section_config_t **section, struct section_config_t *prev) { + struct section_config_t *def_section = NULL; + int ret; +#ifdef KERNEL_SPACE + def_section = kmalloc(sizeof(struct section_config_t), GFP_KERNEL); +#else + def_section = malloc(sizeof(struct section_config_t)); +#endif + *def_section = (struct section_config_t)default_section_config; + def_section->prev = prev; + + if (def_section == NULL) + return -ENOMEM; + + ret = parse_sni_domains(&def_section->sni_domains, default_snistr, sizeof(default_snistr)); + if (ret < 0) { + free(def_section); + return ret; + } + + def_section->fake_sni_pkt = fake_sni_old; + def_section->fake_sni_pkt_sz = sizeof(fake_sni_old) - 1; + + *section = def_section; + return 0; +} + +int init_config(struct config_t *config) { + struct config_t def_config = default_config_set; + int ret = 0; + struct section_config_t *def_section = NULL; + ret = init_section_config(&def_section, NULL); + if (ret < 0) + return ret; + def_config.last_section = def_section; + def_config.first_section = def_section; + + *config = def_config; + + return 0; +} + +void free_config_section(struct section_config_t *section) { + lginfo("freeing %d\n", section->id); + if (section->udp_dport_range_len != 0) { + SFREE(section->udp_dport_range); + } + + free_sni_domains(section->sni_domains); + section->sni_domains = NULL; + free_sni_domains(section->exclude_sni_domains); + section->exclude_sni_domains = NULL; + + section->fake_custom_pkt_sz = 0; + SFREE(section->fake_custom_pkt); + + free(section); +} + +void free_config(struct config_t config) { + lginfo("freeing config\n"); + for (struct section_config_t *sct = config.last_section; sct != NULL;) { + struct section_config_t *psct = sct->prev; + free_config_section(sct); + sct = psct; + } +} diff --git a/args.h b/args.h index 102772f..8c4e88f 100644 --- a/args.h +++ b/args.h @@ -1,9 +1,21 @@ #ifndef ARGS_H #define ARGS_H +#include "types.h" +#include "config.h" void print_version(void); void print_usage(const char *argv0); -int parse_args(int argc, char *argv[]); +int yparse_args(int argc, char *argv[]); +size_t print_config(char *buffer, size_t buffer_size); + +// Initializes configuration storage. +int init_config(struct config_t *config); +// Allocates and initializes configuration section. +int init_section_config(struct section_config_t **section, struct section_config_t *prev); +// Frees configuration section +void free_config_section(struct section_config_t *config); +// Frees sections under config +void free_config(struct config_t config); /* Prints starting messages */ void print_welcome(void); diff --git a/config.h b/config.h index 3bebfdb..8adb331 100644 --- a/config.h +++ b/config.h @@ -5,7 +5,6 @@ #define USER_SPACE #endif -#include "raw_replacements.h" #include "types.h" typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len); @@ -26,9 +25,21 @@ struct udp_dport_range { uint16_t end; }; +struct domains_list { + char *domain_name; + uint16_t domain_len; + + struct domains_list *next; +}; + struct section_config_t { - const char *domains_str; - unsigned int domains_strlen; + int id; + struct section_config_t *next; + struct section_config_t *prev; + + struct domains_list *sni_domains; + struct domains_list *exclude_sni_domains; + unsigned int all_domains; int tls_enabled; @@ -53,14 +64,10 @@ struct section_config_t { int synfake; unsigned int synfake_len; - 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; + char *fake_custom_pkt; unsigned int fake_custom_pkt_sz; unsigned int fk_winsize; @@ -98,17 +105,16 @@ struct config_t { #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; + struct section_config_t *first_section; + struct section_config_t *last_section; }; 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 ITER_CONFIG_SECTIONS(config, section) \ +for (struct section_config_t *section = (config)->last_section; section != NULL; section = section->prev) -#define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config) +#define CONFIG_SECTION_NUMBER(section) ((section)->id) #define MAX_THREADS 16 @@ -180,7 +186,7 @@ if ((fake_bitmask) & strategy) #define DEFAULT_SNISTR "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com" -static const char defaul_snistr[] = DEFAULT_SNISTR; +static const char default_snistr[] = DEFAULT_SNISTR; enum { UDP_MODE_DROP, @@ -193,6 +199,9 @@ enum { }; #define default_section_config { \ + .sni_domains = NULL, \ + .exclude_sni_domains = NULL, \ + .all_domains = 0, \ .tls_enabled = 1, \ .frag_sni_reverse = 1, \ .frag_sni_faked = 0, \ @@ -202,6 +211,8 @@ enum { .fake_sni = 1, \ .fake_sni_seq_len = 1, \ .fake_sni_type = FAKE_PAYLOAD_DEFAULT, \ + .fake_custom_pkt = NULL, \ + .fake_custom_pkt_sz = 0, \ .frag_middle_sni = 1, \ .frag_sni_pos = 1, \ .fakeseq_offset = 10000, \ @@ -210,16 +221,6 @@ enum { \ .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, \ \ .udp_mode = UDP_MODE_FAKE, \ @@ -229,6 +230,32 @@ enum { .udp_dport_range = NULL, \ .udp_dport_range_len = 0, \ .udp_filter_quic = UDP_FILTER_QUIC_DISABLED, \ + \ + .prev = NULL, \ + .next = NULL, \ + .id = 0, \ } +#define default_config_set { \ + .threads = THREADS_NUM, \ + .queue_start_num = DEFAULT_QUEUE_NUM, \ + .mark = DEFAULT_RAWSOCKET_MARK, \ + .use_ipv6 = 1, \ + \ + .verbose = VERBOSE_DEBUG, \ + .use_gso = 1, \ + \ + .first_section = NULL, \ + .last_section = NULL, \ + \ + .daemonize = 0, \ + .noclose = 0, \ + .syslog = 0, \ +} + +#define CONFIG_SET(config) \ +struct config_t config = default_config_set; \ +config->last_section = &(config.default_config) \ + + #endif /* YTB_CONFIG_H */ diff --git a/getopt.c b/getopt.c new file mode 100644 index 0000000..48e76e1 --- /dev/null +++ b/getopt.c @@ -0,0 +1,204 @@ +#include "types.h" +#include "logging.h" +#include "getopt.h" + +char *optarg; +int optind=1, opterr=1, optopt, __optpos, optreset=0; + +#define optpos __optpos + +static void __getopt_msg(const char *b, const char *c, size_t l) +{ + lgerr("%s %.*s\n", b, (int)l, c); +} + +int getopt(int argc, char * const argv[], const char *optstring) +{ + int i, c, d; + int k, l; + char *optchar; + + if (!optind || optreset) { + optreset = 0; + __optpos = 0; + optind = 1; + } + + if (optind >= argc || !argv[optind]) + return -1; + + if (argv[optind][0] != '-') { + if (optstring[0] == '-') { + optarg = argv[optind++]; + return 1; + } + return -1; + } + + if (!argv[optind][1]) + return -1; + + if (argv[optind][1] == '-' && !argv[optind][2]) + return optind++, -1; + + if (!optpos) optpos++; + c = argv[optind][optpos], k = 1; + optchar = argv[optind]+optpos; + optopt = c; + optpos += k; + + if (!argv[optind][optpos]) { + optind++; + optpos = 0; + } + + if (optstring[0] == '-' || optstring[0] == '+') + optstring++; + + i = 0; + d = 0; + do { + d = optstring[i], l = 1; + if (l>0) i+=l; else i++; + } while (l && d != c); + + if (d != c) { + if (optstring[0] != ':' && opterr) + __getopt_msg("Unrecognized option: ", optchar, k); + return '?'; + } + if (optstring[i] == ':') { + if (optstring[i+1] == ':') optarg = 0; + else if (optind >= argc) { + if (optstring[0] == ':') return ':'; + if (opterr) __getopt_msg("Option requires an argument: ", + optchar, k); + return '?'; + } + if (optstring[i+1] != ':' || optpos) { + optarg = argv[optind++] + optpos; + optpos = 0; + } + } + return c; +} + +static void permute(char *const *argv, int dest, int src) +{ + char **av = (char **)argv; + char *tmp = av[src]; + int i; + for (i=src; i>dest; i--) + av[i] = av[i-1]; + av[dest] = tmp; +} + +static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) +{ + optarg = 0; + if (longopts && argv[optind][0] == '-' && + ((longonly && argv[optind][1] && argv[optind][1] != '-') || + (argv[optind][1] == '-' && argv[optind][2]))) + { + int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; + int i, cnt, match = -1; + char *opt; + for (cnt=i=0; longopts[i].name; i++) { + const char *name = longopts[i].name; + opt = argv[optind]+1; + if (*opt == '-') opt++; + for (; *name && *name == *opt; name++, opt++); + if (*opt && *opt != '=') continue; + match = i; + if (!*name) { + cnt = 1; + break; + } + cnt++; + } + if (cnt==1) { + i = match; + optind++; + optopt = longopts[i].val; + if (*opt == '=') { + if (!longopts[i].has_arg) { + if (colon || !opterr) + return '?'; + __getopt_msg( + "Option does not take an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optarg = opt+1; + } else if (longopts[i].has_arg == required_argument) { + if (!(optarg = argv[optind])) { + if (colon) return ':'; + if (!opterr) return '?'; + __getopt_msg( + "Option requires an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optind++; + } + if (idx) *idx = i; + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + return longopts[i].val; + } + if (argv[optind][1] == '-') { + if (!colon && opterr) + __getopt_msg(cnt ? + "Option is ambiguous: " : + "Unrecognized option: ", + argv[optind]+2, + strlen(argv[optind]+2)); + optind++; + return '?'; + } + } + return getopt(argc, argv, optstring); +} + +static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) +{ + int ret, skipped, resumed; + if (!optind || optreset) { + optreset = 0; + __optpos = 0; + optind = 1; + } + if (optind >= argc || !argv[optind]) return -1; + skipped = optind; + if (optstring[0] != '+' && optstring[0] != '-') { + int i; + for (i=optind; ; i++) { + if (i >= argc || !argv[i]) return -1; + if (argv[i][0] == '-' && argv[i][1]) break; + } + optind = i; + } + resumed = optind; + ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly); + if (resumed > skipped) { + int i, cnt = optind-resumed; + for (i=0; i #include "types.h" +#include "args.h" +#include "logging.h" -#define STR_MAXLEN 2048 +#define MAX_ARGC 1024 -static char custom_fake_buf[MAX_FAKE_SIZE]; +static int params_set(const char *cval, const struct kernel_param *kp) { + int ret = 0; -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 -}; - -#define 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); - if (ret != 0 || n < 0) - return -EINVAL; - - - return param_set_int(val, kp); -} - -static int boolean_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; - - return param_set_int(val, kp); -} - -static int inverse_boolean_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; - - n = !n; - if (kp->arg == NULL) - return -EINVAL; - - *(int *)kp->arg = n; - return 0; -} - -static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) { - if (*(int *)kp->arg == 0) { - buffer[0] = '1'; - } else { - buffer[0] = '0'; - } - buffer[1] = '\0'; - return strlen(buffer); -} - -static const struct kernel_param_ops unumeric_parameter_ops = { - .set = unumeric_set, - .get = param_get_int -}; - -static const struct kernel_param_ops boolean_parameter_ops = { - .set = boolean_set, - .get = param_get_int -}; - -static const struct kernel_param_ops inverse_boolean_ops = { - .set = inverse_boolean_set, - .get = inverse_boolean_get, -}; - -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, &def_section->seg2_delay, 0664); - -static int sni_domains_set(const char *val, const struct kernel_param *kp) { - size_t len; - int ret; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; + int cv_len = strlen(cval); + if (cv_len >= 1 && cval[cv_len - 1] == '\n') { + cv_len--; } - if (len >= 1 && val[len - 1] == '\n') { - len--; - } + const char *ytb_prefix = "youtubeUnblock "; + int ytbp_len = strlen(ytb_prefix); + int len = cv_len + ytbp_len; - ret = param_set_charp(val, kp); + char *val = kmalloc(len + 1, GFP_KERNEL); // 1 for null-terminator + strncpy(val, ytb_prefix, ytbp_len); + strncpy(val + ytbp_len, cval, cv_len); + val[len] = '\0'; - if (ret < 0) { - def_section->domains_strlen = 0; - } else { - def_section->domains_strlen = len; - if (len == 3 && !strncmp(val, "all", len)) { - def_section->all_domains = 1; - } else { - def_section->all_domains = 0; + int argc = 0; + char *argv[MAX_ARGC]; + argv[argc++] = val; + + for (int i = 0; i < len; i++) { + if (val[i] == ' ') { + val[i] = '\0'; + + // safe because of null-terminator + if (val[i + 1] != ' ' && val[i + 1] != '\0') { + argv[argc++] = val + i + 1; + } } } + for (int i = 0; i < argc; i++) { + lginfo("%s %d\n", argv[i], strlen(argv[i])); + } + ret = yparse_args(argc, argv); + kfree(val); return ret; } -static const struct kernel_param_ops sni_domains_ops = { - .set = sni_domains_set, - .get = param_get_charp, +static int params_get(char *buffer, const struct kernel_param *kp) { + size_t len = print_config(buffer, 4000); + return len; +} + +static const struct kernel_param_ops params_ops = { + .set = params_set, + .get = params_get, }; -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; - int ret; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - ret = param_set_charp(val, kp); - - if (ret < 0) { - def_section->exclude_domains_strlen = 0; - } else { - def_section->exclude_domains_strlen = len; - } - - return ret; -} - -static const struct kernel_param_ops exclude_domains_ops = { - .set = exclude_domains_set, - .get = param_get_charp, -}; - -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); - -static int quic_drop_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; - - if (n) { - def_section->udp_mode = UDP_MODE_DROP; - def_section->udp_filter_quic = UDP_FILTER_QUIC_ALL; - } else { - def_section->udp_filter_quic = UDP_FILTER_QUIC_DISABLED; - } - - return 0; -} - -static int quic_drop_get(char *buffer, const struct kernel_param *kp) { - if (def_section->udp_mode == UDP_MODE_DROP && - def_section->udp_filter_quic == UDP_FILTER_QUIC_ALL) { - return sprintf(buffer, "%d\n", 1); - } else { - return sprintf(buffer, "%d\n", 0); - } -} - -static const struct kernel_param_ops quic_drop_ops = { - .set = quic_drop_set, - .get = quic_drop_get -}; - -module_param_cb(quic_drop, &quic_drop_ops, NULL, 0664); - -static int verbosity_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, "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; - } - - return 0; -} - - -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(verbosity, &verbosity_ops, &config.verbose, 0664); - -static int frag_strat_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, "tcp", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_TCP; - } else if (strncmp(val, "ip", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_IP; - } else if (strncmp(val, "none", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_NONE; - } else { - return -EINVAL; - } - - return 0; -} - -static int frag_strat_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case FRAG_STRAT_TCP: - strcpy(buffer, "tcp\n"); - break; - case FRAG_STRAT_IP: - strcpy(buffer, "ip\n"); - break; - case FRAG_STRAT_NONE: - strcpy(buffer, "none\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops frag_strat_ops = { - .set = frag_strat_set, - .get = frag_strat_get, -}; - -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; - - 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, "randseq", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_RAND_SEQ; - } else if (strncmp(val, "ttl", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TTL; - } else if (strncmp(val, "tcp_check", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TCP_CHECK; - } else if (strncmp(val, "pastseq", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_PAST_SEQ; - } else if (strncmp(val, "md5sum", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TCP_MD5SUM; - } else { - return -EINVAL; - } - - return 0; -} - -static int fake_strat_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case FAKE_STRAT_RAND_SEQ: - strcpy(buffer, "randseq\n"); - break; - case FAKE_STRAT_TTL: - strcpy(buffer, "ttl\n"); - break; - case FAKE_STRAT_TCP_CHECK: - strcpy(buffer, "tcp_check\n"); - break; - case FAKE_STRAT_PAST_SEQ: - strcpy(buffer, "pastseq\n"); - break; - case FAKE_STRAT_TCP_MD5SUM: - strcpy(buffer, "md5sum\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops fake_strat_ops = { - .set = fake_strat_set, - .get = fake_strat_get, -}; - -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; - - 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, "parse", len) == 0) { - *(int *)kp->arg = SNI_DETECTION_PARSE; - } else if (strncmp(val, "brute", len) == 0) { - *(int *)kp->arg = SNI_DETECTION_BRUTE; - } else { - return -EINVAL; - } - - return 0; -} - -static int sni_detection_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case SNI_DETECTION_PARSE: - strcpy(buffer, "parse\n"); - break; - case SNI_DETECTION_BRUTE: - strcpy(buffer, "brute\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops sni_detection_ops = { - .set = sni_detection_set, - .get = sni_detection_get, -}; - -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; - - 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, &def_section->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); - } - - 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 = 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)); - } - - 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, &def_section->fake_custom_pkt, 0664); +module_param_cb(parameters, ¶ms_ops, NULL, 0664); + + +// +// static char custom_fake_buf[MAX_FAKE_SIZE]; +// +// 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 +// }; +// +// #define 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); +// if (ret != 0 || n < 0) +// return -EINVAL; +// +// +// return param_set_int(val, kp); +// } +// +// static int boolean_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; +// +// return param_set_int(val, kp); +// } +// +// static int inverse_boolean_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; +// +// n = !n; +// if (kp->arg == NULL) +// return -EINVAL; +// +// *(int *)kp->arg = n; +// return 0; +// } +// +// static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) { +// if (*(int *)kp->arg == 0) { +// buffer[0] = '1'; +// } else { +// buffer[0] = '0'; +// } +// buffer[1] = '\0'; +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops unumeric_parameter_ops = { +// .set = unumeric_set, +// .get = param_get_int +// }; +// +// static const struct kernel_param_ops boolean_parameter_ops = { +// .set = boolean_set, +// .get = param_get_int +// }; +// +// static const struct kernel_param_ops inverse_boolean_ops = { +// .set = inverse_boolean_set, +// .get = inverse_boolean_get, +// }; +// +// 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, &def_section->seg2_delay, 0664); +// +// static int sni_domains_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// int ret; +// +// 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--; +// } +// +// ret = param_set_charp(val, kp); +// +// if (ret < 0) { +// def_section->domains_strlen = 0; +// } else { +// def_section->domains_strlen = len; +// if (len == 3 && !strncmp(val, "all", len)) { +// def_section->all_domains = 1; +// } else { +// def_section->all_domains = 0; +// } +// } +// +// +// return ret; +// } +// +// static const struct kernel_param_ops sni_domains_ops = { +// .set = sni_domains_set, +// .get = param_get_charp, +// }; +// +// 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; +// int ret; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// ret = param_set_charp(val, kp); +// +// if (ret < 0) { +// def_section->exclude_domains_strlen = 0; +// } else { +// def_section->exclude_domains_strlen = len; +// } +// +// return ret; +// } +// +// static const struct kernel_param_ops exclude_domains_ops = { +// .set = exclude_domains_set, +// .get = param_get_charp, +// }; +// +// 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); +// +// static int quic_drop_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; +// +// if (n) { +// def_section->udp_mode = UDP_MODE_DROP; +// def_section->udp_filter_quic = UDP_FILTER_QUIC_ALL; +// } else { +// def_section->udp_filter_quic = UDP_FILTER_QUIC_DISABLED; +// } +// +// return 0; +// } +// +// static int quic_drop_get(char *buffer, const struct kernel_param *kp) { +// if (def_section->udp_mode == UDP_MODE_DROP && +// def_section->udp_filter_quic == UDP_FILTER_QUIC_ALL) { +// return sprintf(buffer, "%d\n", 1); +// } else { +// return sprintf(buffer, "%d\n", 0); +// } +// } +// +// static const struct kernel_param_ops quic_drop_ops = { +// .set = quic_drop_set, +// .get = quic_drop_get +// }; +// +// module_param_cb(quic_drop, &quic_drop_ops, NULL, 0664); +// +// static int verbosity_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, "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; +// } +// +// return 0; +// } +// +// +// 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(verbosity, &verbosity_ops, &config.verbose, 0664); +// +// static int frag_strat_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, "tcp", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_TCP; +// } else if (strncmp(val, "ip", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_IP; +// } else if (strncmp(val, "none", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_NONE; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int frag_strat_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case FRAG_STRAT_TCP: +// strcpy(buffer, "tcp\n"); +// break; +// case FRAG_STRAT_IP: +// strcpy(buffer, "ip\n"); +// break; +// case FRAG_STRAT_NONE: +// strcpy(buffer, "none\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops frag_strat_ops = { +// .set = frag_strat_set, +// .get = frag_strat_get, +// }; +// +// 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; +// +// 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, "randseq", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_RAND_SEQ; +// } else if (strncmp(val, "ttl", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TTL; +// } else if (strncmp(val, "tcp_check", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TCP_CHECK; +// } else if (strncmp(val, "pastseq", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_PAST_SEQ; +// } else if (strncmp(val, "md5sum", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TCP_MD5SUM; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int fake_strat_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case FAKE_STRAT_RAND_SEQ: +// strcpy(buffer, "randseq\n"); +// break; +// case FAKE_STRAT_TTL: +// strcpy(buffer, "ttl\n"); +// break; +// case FAKE_STRAT_TCP_CHECK: +// strcpy(buffer, "tcp_check\n"); +// break; +// case FAKE_STRAT_PAST_SEQ: +// strcpy(buffer, "pastseq\n"); +// break; +// case FAKE_STRAT_TCP_MD5SUM: +// strcpy(buffer, "md5sum\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops fake_strat_ops = { +// .set = fake_strat_set, +// .get = fake_strat_get, +// }; +// +// 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; +// +// 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, "parse", len) == 0) { +// *(int *)kp->arg = SNI_DETECTION_PARSE; +// } else if (strncmp(val, "brute", len) == 0) { +// *(int *)kp->arg = SNI_DETECTION_BRUTE; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int sni_detection_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case SNI_DETECTION_PARSE: +// strcpy(buffer, "parse\n"); +// break; +// case SNI_DETECTION_BRUTE: +// strcpy(buffer, "brute\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops sni_detection_ops = { +// .set = sni_detection_set, +// .get = sni_detection_get, +// }; +// +// 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; +// +// 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, &def_section->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); +// } +// +// 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 = 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)); +// } +// +// 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, &def_section->fake_custom_pkt, 0664); diff --git a/kytunblock.c b/kytunblock.c index d32cecf..b4c789d 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -20,6 +20,7 @@ #include "config.h" #include "utils.h" #include "logging.h" +#include "args.h" #if defined(PKG_VERSION) MODULE_VERSION(PKG_VERSION); @@ -206,9 +207,9 @@ erret_lc: int ipvx = netproto_version(pkt, pktlen); if (ipvx == IP4VERSION) { - return send_raw_ipv4(pkt, pktlen); + ret = send_raw_ipv4(pkt, pktlen); } else if (ipvx == IP6VERSION) { - return send_raw_ipv6(pkt, pktlen); + ret = send_raw_ipv6(pkt, pktlen); } else { printf("proto version %d is unsupported\n", ipvx); return -EINVAL; @@ -346,6 +347,8 @@ static struct nf_hook_ops ykb6_nf_reg __read_mostly = { static int __init ykb_init(void) { int ret = 0; + ret = init_config(&config); + if (ret < 0) goto err; ret = open_raw_socket(); if (ret < 0) goto err; @@ -420,6 +423,8 @@ static void __exit ykb_destroy(void) { #endif close_raw_socket(); + + free_config(config); lginfo("youtubeUnblock kernel module destroyed.\n"); } diff --git a/mangle.c b/mangle.c index 961ec98..a06a6c6 100644 --- a/mangle.c +++ b/mangle.c @@ -62,7 +62,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { lgtrace_addp("UDP"); - ITER_CONFIG_SECTIONS(section) { + ITER_CONFIG_SECTIONS(&config, section) { lgtrace_addp("Section #%d", CONFIG_SECTION_NUMBER(section)); switch (transport_proto) { diff --git a/tls.c b/tls.c index b3f0e4f..16b43a9 100644 --- a/tls.c +++ b/tls.c @@ -9,6 +9,114 @@ #include #endif +static int bruteforce_analyze_sni_str( + const struct section_config_t *section, + const uint8_t *data, size_t dlen, + struct tls_verdict *vrd +) { + if (section->all_domains) { + vrd->target_sni = 1; + vrd->sni_len = 0; + vrd->sni_offset = dlen / 2; + return 0; + } + + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + const char *domain_startp = sne->domain_name; + int domain_len = sne->domain_len; + + if (sne->domain_len + dlen + 1 > MAX_PACKET_SIZE) { + continue; + } + + NETBUF_ALLOC(buf, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buf)) { + lgerror(-ENOMEM, "Allocation error"); + return -ENOMEM; + } + NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); + if (!NETBUF_CHECK(nzbuf)) { + lgerror(-ENOMEM, "Allocation error"); + NETBUF_FREE(buf); + return -ENOMEM; + } + + 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); + vrd->sni_target_offset = vrd->sni_offset; + vrd->sni_target_len = vrd->sni_len; + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + return 0; + } + } + + + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + } + + return 0; +} +static int analyze_sni_str( + const struct section_config_t *section, + const char *sni_name, int sni_len, const uint8_t *data, + struct tls_verdict *vrd +) { + if (section->all_domains) { + vrd->target_sni = 1; + goto check_domain; + } + + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + const char *sni_startp = sni_name + sni_len - sne->domain_len; + const char *domain_startp = sne->domain_name; + + if (sni_len >= sne->domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + sne->domain_len)) { + vrd->target_sni = 1; + vrd->sni_target_offset = (const uint8_t *)sni_startp - data; + vrd->sni_target_len = sne->domain_len; + break; + } + } + +check_domain: + if (vrd->target_sni == 1) { + for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) { + const char *sni_startp = sni_name + sni_len - sne->domain_len; + const char *domain_startp = sne->domain_name; + + if (sni_len >= sne->domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + sne->domain_len)) { + vrd->target_sni = 0; + lgdebugmsg("Excluded SNI: %.*s", + vrd->sni_len, data + vrd->sni_offset); + } + } + } + + return 0; +} + + #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 #define TLS_EXTENSION_SNI 0x0000 @@ -26,10 +134,16 @@ struct tls_verdict analyze_tls_data( uint32_t dlen) { struct tls_verdict vrd = {0}; + int ret; size_t i = 0; const uint8_t *data_end = data + dlen; + if (section->sni_detection == SNI_DETECTION_BRUTE) { + ret = bruteforce_analyze_sni_str(section, data, dlen, &vrd); + goto out; + } + while (i + 4 < dlen) { const uint8_t *msgData = data + i; @@ -44,10 +158,6 @@ struct tls_verdict analyze_tls_data( if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) goto nextMessage; - if (section->sni_detection == SNI_DETECTION_BRUTE) { - goto brute; - } - const uint8_t *handshakeProto = msgData + 5; if (handshakeProto + 1 >= data_end) break; @@ -120,76 +230,14 @@ struct tls_verdict analyze_tls_data( if (sni_ext_ptr + sni_len > sni_ext_end) break; - char *sni_name = (char *)sni_ext_ptr; + const 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; vrd.sni_target_len = vrd.sni_len; - if (section->all_domains) { - vrd.target_sni = 1; - goto check_domain; - } - - unsigned int j = 0; - for (unsigned int i = 0; i <= section->domains_strlen; i++) { - if ( i > j && - (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 = section->domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - 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; - } - - j = i + 1; - } - } - -check_domain: - if (vrd.target_sni == 1 && section->exclude_domains_strlen != 0) { - unsigned int j = 0; - for (unsigned int i = 0; i <= section->exclude_domains_strlen; i++) { - if ( i > j && - (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 = section->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; - } - } - } - + ret = analyze_sni_str(section, sni_name, sni_len, data, &vrd); goto out; nextExtension: @@ -201,73 +249,6 @@ nextMessage: out: return vrd; - - -brute: - if (section->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 <= section->domains_strlen; i++) { - if ( i > j && - (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 = section->domains_str + j; - - if (domain_len + dlen + 1> MAX_PACKET_SIZE) { - continue; - } - - NETBUF_ALLOC(buf, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buf)) { - lgerror(-ENOMEM, "Allocation error"); - goto out; - } - NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); - if (!NETBUF_CHECK(nzbuf)) { - lgerror(-ENOMEM, "Allocation error"); - 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); - vrd.sni_target_offset = vrd.sni_offset; - vrd.sni_target_len = vrd.sni_len; - 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, diff --git a/types.h b/types.h index 32f88aa..e35733b 100644 --- a/types.h +++ b/types.h @@ -34,6 +34,9 @@ #include // IWYU pragma: export #include +#define free kfree +#define malloc(size) kmalloc((size), GFP_KERNEL) + #define ip6_hdr ipv6hdr /* from */ @@ -67,6 +70,11 @@ #include // IWYU pragma: export #endif +#define SFREE(item) do { \ +free((item)); \ +(item) = NULL; \ +} while (0) + #ifndef KERNEL_SPACE #define max(a,b)__extension__\ diff --git a/uspace.mk b/uspace.mk index 2506c97..0e618a0 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 tls.c +SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c getopt.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la diff --git a/youtubeUnblock.c b/youtubeUnblock.c index fe67a25..0d7d662 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -612,7 +612,7 @@ struct instance_config_t instance_config = { int main(int argc, char *argv[]) { int ret; - if ((ret = parse_args(argc, argv)) != 0) { + if ((ret = yparse_args(argc, argv)) != 0) { if (ret < 0) { lgerror(-errno, "Unable to parse args"); exit(EXIT_FAILURE);