#include "config.h" #include "types.h" #include "types.h" #include "args.h" #include "logging.h" #include "getopt.h" #include "raw_replacements.h" #ifdef KERNEL_SPACE static int errno = 0; #define strtol kstrtol #endif struct config_t config = default_config_set; 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; 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' )) { 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; 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 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, OPT_EXCLUDE_DOMAINS, OPT_FAKE_SNI, OPT_FAKING_TTL, OPT_FAKING_STRATEGY, OPT_FAKE_SNI_SEQ_LEN, OPT_FAKE_SNI_TYPE, OPT_FAKE_CUSTOM_PAYLOAD, OPT_START_SECTION, OPT_END_SECTION, OPT_DAEMONIZE, OPT_NOCLOSE, OPT_SYSLOG, OPT_FRAG, OPT_FRAG_SNI_REVERSE, OPT_FRAG_SNI_FAKED, OPT_FRAG_MIDDLE_SNI, OPT_FRAG_SNI_POS, OPT_FK_WINSIZE, OPT_TRACE, OPT_QUIC_DROP, OPT_SNI_DETECTION, OPT_NO_IPV6, OPT_FAKE_SEQ_OFFSET, OPT_PACKET_MARK, OPT_SYNFAKE, OPT_SYNFAKE_LEN, OPT_SEG2DELAY, OPT_THREADS, OPT_SILENT, OPT_NO_GSO, OPT_QUEUE_NUM, OPT_UDP_MODE, OPT_UDP_FAKE_SEQ_LEN, OPT_UDP_FAKE_PAYLOAD_LEN, OPT_UDP_FAKING_STRATEGY, OPT_UDP_DPORT_FILTER, OPT_UDP_FILTER_QUIC, OPT_TLS_ENABLED, OPT_CLS, OPT_HELP, OPT_VERSION, OPT_CONNBYTES_LIMIT, }; static struct option long_opt[] = { {"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}, {"synfake", 1, 0, OPT_SYNFAKE}, {"synfake-len", 1, 0, OPT_SYNFAKE_LEN}, {"tls", 1, 0, OPT_TLS_ENABLED}, {"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN}, {"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}, {"frag", 1, 0, OPT_FRAG}, {"frag-sni-reverse", 1, 0, OPT_FRAG_SNI_REVERSE}, {"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED}, {"frag-middle-sni", 1, 0, OPT_FRAG_MIDDLE_SNI}, {"frag-sni-pos", 1, 0, OPT_FRAG_SNI_POS}, {"fk-winsize", 1, 0, OPT_FK_WINSIZE}, {"quic-drop", 0, 0, OPT_QUIC_DROP}, {"sni-detection", 1, 0, OPT_SNI_DETECTION}, {"seg2delay", 1, 0, OPT_SEG2DELAY}, {"udp-mode", 1, 0, OPT_UDP_MODE}, {"udp-fake-seq-len", 1, 0, OPT_UDP_FAKE_SEQ_LEN}, {"udp-fake-len", 1, 0, OPT_UDP_FAKE_PAYLOAD_LEN}, {"udp-faking-strategy", 1, 0, OPT_UDP_FAKING_STRATEGY}, {"udp-dport-filter", 1, 0, OPT_UDP_DPORT_FILTER}, {"udp-filter-quic", 1, 0, OPT_UDP_FILTER_QUIC}, {"threads", 1, 0, OPT_THREADS}, {"silent", 0, 0, OPT_SILENT}, {"trace", 0, 0, OPT_TRACE}, {"no-gso", 0, 0, OPT_NO_GSO}, {"no-ipv6", 0, 0, OPT_NO_IPV6}, {"daemonize", 0, 0, OPT_DAEMONIZE}, {"noclose", 0, 0, OPT_NOCLOSE}, {"syslog", 0, 0, OPT_SYSLOG}, {"queue-num", 1, 0, OPT_QUEUE_NUM}, {"packet-mark", 1, 0, OPT_PACKET_MARK}, {"connbytes-limit", 1, 0, OPT_CONNBYTES_LIMIT}, {"fbegin", 0, 0, OPT_START_SECTION}, {"fend", 0, 0, OPT_END_SECTION}, {"cls", 0, 0, OPT_CLS}, {0, 0, 0, 0}, }; void print_version(void) { printf("youtubeUnblock" #if defined(PKG_VERSION) " " PKG_VERSION #endif "\n" ); printf("Bypasses deep packet inspection systems that rely on SNI\n"); printf("\n"); } void print_usage(const char *argv0) { print_version(); printf("Usage: %s [ OPTIONS ] \n", argv0); printf("Options:\n"); printf("\t--queue-num=\n"); printf("\t--sni-domains=|all\n"); printf("\t--exclude-domains=\n"); printf("\t--tls={enabled|disabled}\n"); printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni-seq-len=\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"); printf("\t--synfake={1|0}\n"); printf("\t--synfake-len=\n"); printf("\t--frag={tcp,ip,none}\n"); printf("\t--frag-sni-reverse={0|1}\n"); printf("\t--frag-sni-faked={0|1}\n"); printf("\t--frag-middle-sni={0|1}\n"); printf("\t--frag-sni-pos=\n"); printf("\t--fk-winsize=\n"); printf("\t--quic-drop\n"); printf("\t--sni-detection={parse|brute}\n"); printf("\t--seg2delay=\n"); printf("\t--udp-mode={drop|fake}\n"); printf("\t--udp-fake-seq-len=\n"); printf("\t--udp-fake-len=\n"); printf("\t--udp-faking-strategy={checksum|ttl}\n"); printf("\t--udp-dport-filter=<5,6,200-500>\n"); printf("\t--udp-filter-quic={disabled|all}\n"); printf("\t--threads=\n"); printf("\t--packet-mark=\n"); printf("\t--connbytes-limit=\n"); printf("\t--silent\n"); printf("\t--trace\n"); printf("\t--no-gso\n"); printf("\t--no-ipv6\n"); printf("\t--daemonize\n"); printf("\t--noclose\n"); printf("\t--syslog\n"); printf("\t--fbegin\n"); printf("\t--fend\n"); printf("\n"); } int yparse_args(int argc, char *argv[]) { int opt; int optIdx = 0; optind=1, opterr=1, optreset=0; long num; int ret; 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 #define SECT_ITER_OUTSIDE 3 int section_iter = SECT_ITER_DEFAULT; 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 OPT_HELP: print_usage(argv[0]); #ifndef KERNEL_SPACE goto stop_exec; #else break; #endif case OPT_VERSION: print_version(); #ifndef KERNEL_SPACE goto stop_exec; #else break; #endif case OPT_TRACE: rep_config.verbose = 2; break; case OPT_SILENT: rep_config.verbose = 0; break; case OPT_NO_GSO: rep_config.use_gso = 0; break; case OPT_NO_IPV6: rep_config.use_ipv6 = 0; break; case OPT_DAEMONIZE: rep_config.daemonize = 1; break; case OPT_NOCLOSE: rep_config.noclose = 1; break; case OPT_SYSLOG: rep_config.syslog = 1; break; case OPT_THREADS: num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > MAX_THREADS) { goto invalid_opt; } rep_config.threads = num; break; case OPT_QUEUE_NUM: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } rep_config.queue_start_num = num; break; case OPT_PACKET_MARK: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } rep_config.mark = num; break; case OPT_CONNBYTES_LIMIT: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } rep_config.connbytes_limit = num; break; case OPT_START_SECTION: { 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; } case OPT_END_SECTION: if (section_iter != SECT_ITER_INSIDE) goto invalid_opt; section_iter = SECT_ITER_OUTSIDE; sect_config = default_section; break; /* section_config_t scoped configs */ case OPT_TLS_ENABLED: if (strcmp(optarg, "enabled") == 0) { sect_config->tls_enabled = 1; } else if (strcmp(optarg, "disabled") == 0) { sect_config->tls_enabled = 0; } else { goto invalid_opt; } break; case OPT_SNI_DOMAINS: free_sni_domains(sect_config->sni_domains); sect_config->all_domains = 0; if (!strcmp(optarg, "all")) { sect_config->all_domains = 1; } ret = parse_sni_domains(§_config->sni_domains, optarg, strlen(optarg)); if (ret < 0) goto error; break; case OPT_EXCLUDE_DOMAINS: free_sni_domains(sect_config->exclude_sni_domains); 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) { sect_config->fragmentation_strategy = FRAG_STRAT_TCP; } else if (strcmp(optarg, "ip") == 0) { sect_config->fragmentation_strategy = FRAG_STRAT_IP; } else if (strcmp(optarg, "none") == 0) { sect_config->fragmentation_strategy = FRAG_STRAT_NONE; } else { goto invalid_opt; } break; case OPT_FRAG_SNI_FAKED: if (strcmp(optarg, "1") == 0) { sect_config->frag_sni_faked = 1; } else if (strcmp(optarg, "0") == 0) { sect_config->frag_sni_faked = 0; } else { goto invalid_opt; } break; case OPT_FRAG_SNI_REVERSE: if (strcmp(optarg, "1") == 0) { sect_config->frag_sni_reverse = 1; } else if (strcmp(optarg, "0") == 0) { sect_config->frag_sni_reverse = 0; } else { goto invalid_opt; } break; case OPT_FRAG_MIDDLE_SNI: if (strcmp(optarg, "1") == 0) { sect_config->frag_middle_sni = 1; } else if (strcmp(optarg, "0") == 0) { sect_config->frag_middle_sni = 0; } else { goto invalid_opt; } break; case OPT_FRAG_SNI_POS: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } sect_config->frag_sni_pos = num; break; case OPT_FAKING_STRATEGY: if (strcmp(optarg, "randseq") == 0) { sect_config->faking_strategy = FAKE_STRAT_RAND_SEQ; } else if (strcmp(optarg, "ttl") == 0) { sect_config->faking_strategy = FAKE_STRAT_TTL; } else if (strcmp(optarg, "tcp_check") == 0) { sect_config->faking_strategy = FAKE_STRAT_TCP_CHECK; } else if (strcmp(optarg, "pastseq") == 0) { sect_config->faking_strategy = FAKE_STRAT_PAST_SEQ; } else if (strcmp(optarg, "md5sum") == 0) { sect_config->faking_strategy = FAKE_STRAT_TCP_MD5SUM; } else { goto invalid_opt; } break; case OPT_FAKING_TTL: num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > 255) { goto invalid_opt; } sect_config->faking_ttl = num; break; case OPT_FAKE_SEQ_OFFSET: num = parse_numeric_option(optarg); if (errno != 0) { goto invalid_opt; } sect_config->fakeseq_offset = num; break; case OPT_FAKE_SNI: if (strcmp(optarg, "1") == 0) { sect_config->fake_sni = 1; } else if (strcmp(optarg, "0") == 0) { sect_config->fake_sni = 0; } else { goto invalid_opt; } break; case OPT_FAKE_SNI_SEQ_LEN: num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > 255) { goto invalid_opt; } sect_config->fake_sni_seq_len = num; break; case OPT_FAKE_SNI_TYPE: if (strcmp(optarg, "default") == 0) { sect_config->fake_sni_type = FAKE_PAYLOAD_DEFAULT; } else if (strcmp(optarg, "random") == 0) { sect_config->fake_sni_type = FAKE_PAYLOAD_RANDOM; } else if (strcmp(optarg, "custom") == 0) { sect_config->fake_sni_type = FAKE_PAYLOAD_CUSTOM; } else { goto invalid_opt; } break; case OPT_FAKE_CUSTOM_PAYLOAD: SFREE(sect_config->fake_custom_pkt); 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); if (errno != 0 || num < 0) { goto invalid_opt; } sect_config->fk_winsize = num; break; case OPT_SEG2DELAY: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } sect_config->seg2_delay = num; break; case OPT_QUIC_DROP: sect_config->udp_filter_quic = UDP_FILTER_QUIC_ALL; sect_config->udp_mode = UDP_MODE_DROP; 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; } break; case OPT_SYNFAKE: if (strcmp(optarg, "1") == 0) { sect_config->synfake = 1; } else if (strcmp(optarg, "0") == 0) { sect_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; } sect_config->synfake_len = num; break; case OPT_UDP_MODE: if (strcmp(optarg, "drop") == 0) { sect_config->udp_mode = UDP_MODE_DROP; } else if (strcmp(optarg, "fake") == 0) { sect_config->udp_mode = UDP_MODE_FAKE; } else { goto invalid_opt; } break; case OPT_UDP_FAKING_STRATEGY: if (strcmp(optarg, "checksum") == 0) { sect_config->udp_faking_strategy = FAKE_STRAT_UDP_CHECK; } else if (strcmp(optarg, "ttl") == 0) { sect_config->udp_faking_strategy = FAKE_STRAT_TTL; } else { goto invalid_opt; } break; case OPT_UDP_FAKE_SEQ_LEN: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } sect_config->udp_fake_seq_len = num; break; case OPT_UDP_FAKE_PAYLOAD_LEN: num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > 1300) { goto invalid_opt; } sect_config->udp_fake_len = num; break; case OPT_UDP_DPORT_FILTER: { 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; } break; } case OPT_UDP_FILTER_QUIC: if (strcmp(optarg, "disabled") == 0) { sect_config->udp_filter_quic = UDP_FILTER_QUIC_DISABLED; } else if (strcmp(optarg, "all") == 0) { sect_config->udp_filter_quic = UDP_FILTER_QUIC_ALL; } else { goto invalid_opt; } break; default: goto error; } } struct config_t old_config = config; config = rep_config; free_config(old_config); errno = 0; return 0; #ifndef KERNEL_SPACE stop_exec: free_config(rep_config); errno = 0; return 1; #endif invalid_opt: printf("Invalid option %s\n", long_opt[optIdx].name); ret = -EINVAL; error: #ifndef KERNEL_SPACE print_usage(argv[0]); #endif if (errno) ret = -errno; if (ret != -EINVAL) { lgerror(ret, "argparse: error thrown in %s", long_opt[optIdx].name); ret = -EINVAL; } errno = -ret; free_config(rep_config); return ret; } #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->all_domains) { print_cnf_buf("--sni-domains=all"); } else 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("--quic-drop"); } 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("--packet-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 #ifdef KERNEL_SPACE print_cnf_buf("--connbytes-limit=%d", config.connbytes_limit); #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 = malloc(4000); if (welcome_message == NULL) return; size_t sz = print_config(welcome_message, 4000); printf("Running with flags: %.*s\n", (int)sz, welcome_message); free(welcome_message); } 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) { 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) { for (struct section_config_t *sct = config.last_section; sct != NULL;) { struct section_config_t *psct = sct->prev; free_config_section(sct); sct = psct; } }