From 49de2cad6a507c8d6f8cfee823448ee0bd6d3fd5 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 20 Jan 2025 22:19:36 +0300 Subject: [PATCH 1/2] Allow to specify sni domains as file --- README.md | 4 +++ src/args.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c75a08e..f30970c 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,10 @@ Flags that do not scoped to a specific section, used over all the youtubeUnblock - `--exclude-domains=` List of domains to be excluded from targeting. +- `--sni-domains-file=` Same as `--sni-domains` but accepts path to container file instead of inline domains list. The format is file may consist of both comma-separated domains list as well as new-line separated list. + +- `--exclude-domains-file=` Same as `--exclude-domains` but accepts path to container file instead of inline domains list. The format is file may consist of both comma-separated domains list as well as new-line separated list. + - `--udp-mode={drop|fake}` This flag specifies udp handling strategy. If drop udp packets will be dropped (useful for quic when browser can fallback to tcp), if fake udp will be faked. Defaults to fake. - `--udp-fake-seq-len=` Specifies how much faking packets will be sent over the network. Defaults to 6. diff --git a/src/args.c b/src/args.c index d609fef..6c07aa3 100644 --- a/src/args.c +++ b/src/args.c @@ -26,6 +26,7 @@ #include "getopt.h" #include "raw_replacements.h" + /** * Logging definitions */ @@ -47,6 +48,51 @@ void parse_global_lgconf(const struct config_t *config) { logging_conf.instaflush = config->instaflush; } +#ifndef KERNEL_SPACE +#define MAX_FILE_LENGTH 8196 +static uint8_t glob_file_buffer[MAX_FILE_LENGTH]; +static size_t glob_file_size = 0; +static int read_file(const char* filename) { + int ret; + + FILE* fd = fopen(optarg, "r"); + if (fd == NULL) { + return -errno; + } + + ret = fseek(fd, 0, SEEK_END); + if (ret < 0) { + ret = -errno; + goto close_file; + } + + size_t fsize = ftell(fd); + fseek(fd, 0, SEEK_SET); + if (ret < 0) { + ret = -errno; + goto close_file; + } + + if (fsize > MAX_FILE_LENGTH) { + ret = -ENOMEM; + goto close_file; + } + + glob_file_size = fsize; + unsigned long uret = fread(glob_file_buffer, sizeof(uint8_t), fsize, fd); + if (uret != fsize) { + ret = -EINVAL; + goto close_file; + } + + ret = 0; + +close_file: + fclose(fd); + return ret; +} +#endif + 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}; @@ -57,7 +103,13 @@ static int parse_sni_domains(struct domains_list **dlist, const char *domains_st if (( i == domains_strlen || domains_str[i] == '\0' || domains_str[i] == ',' || - domains_str[i] == '\n' )) { + domains_str[i] == '\n' || + ( + i < domains_strlen - 1 && + domains_str[i] == '\r' && + domains_str[i + 1] == '\n' + ) + )) { if (i == j) { j++; @@ -249,6 +301,8 @@ static int parse_fake_custom_payload( enum { OPT_SNI_DOMAINS, OPT_EXCLUDE_DOMAINS, + OPT_SNI_DOMAINS_FILE, + OPT_EXCLUDE_DOMAINS_FILE, OPT_FAKE_SNI, OPT_FAKING_TTL, OPT_FAKING_STRATEGY, @@ -300,6 +354,8 @@ static struct option long_opt[] = { {"version", 0, 0, OPT_VERSION}, {"sni-domains", 1, 0, OPT_SNI_DOMAINS}, {"exclude-domains", 1, 0, OPT_EXCLUDE_DOMAINS}, + {"sni-domains-file", 1, 0, OPT_SNI_DOMAINS_FILE}, + {"exclude-domains-file",1, 0, OPT_EXCLUDE_DOMAINS_FILE}, {"fake-sni", 1, 0, OPT_FAKE_SNI}, {"synfake", 1, 0, OPT_SYNFAKE}, {"synfake-len", 1, 0, OPT_SYNFAKE_LEN}, @@ -364,6 +420,8 @@ void print_usage(const char *argv0) { printf("\t--queue-num=\n"); printf("\t--sni-domains=|all\n"); printf("\t--exclude-domains=\n"); + printf("\t--sni-domains-file=\n"); + printf("\t--exclude-domains-file=\n"); printf("\t--tls={enabled|disabled}\n"); printf("\t--fake-sni={1|0}\n"); printf("\t--fake-sni-seq-len=\n"); @@ -581,6 +639,24 @@ int yparse_args(struct config_t *config, int argc, char *argv[]) { if (ret < 0) goto error; break; + case OPT_SNI_DOMAINS_FILE: +#ifdef KERNEL_SPACE + lgerr("--sni-domains-file is not allowed in kernel space. Use --sni-domains argument instead"); + goto error; +#else + { + free_sni_domains(sect_config->sni_domains); + ret = read_file(optarg); + if (ret < 0) { + goto error; + } + + ret = parse_sni_domains(§_config->sni_domains, (char *)glob_file_buffer, glob_file_size); + if (ret < 0) + goto error; + break; + } +#endif case OPT_EXCLUDE_DOMAINS: free_sni_domains(sect_config->exclude_sni_domains); ret = parse_sni_domains(§_config->exclude_sni_domains, optarg, strlen(optarg)); @@ -588,6 +664,24 @@ int yparse_args(struct config_t *config, int argc, char *argv[]) { goto error; break; + case OPT_EXCLUDE_DOMAINS_FILE: +#ifdef KERNEL_SPACE + lgerr("--sni-domains-file is not allowed in kernel space. Use --sni-domains argument instead"); + goto error; +#else + { + free_sni_domains(sect_config->exclude_sni_domains); + ret = read_file(optarg); + if (ret < 0) { + goto error; + } + + ret = parse_sni_domains(§_config->exclude_sni_domains, (char *)glob_file_buffer, glob_file_size); + if (ret < 0) + goto error; + break; + } +#endif case OPT_FRAG: if (strcmp(optarg, "tcp") == 0) { sect_config->fragmentation_strategy = FRAG_STRAT_TCP; From ef78f5e185185814bb87fd9ba9b0fc20a4212552 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 27 Jan 2025 19:51:28 +0300 Subject: [PATCH 2/2] Add --fake-custom-sni-file option Allows to specify fake as a binary file --- README.md | 3 +++ src/args.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f30970c..cfe573e 100644 --- a/README.md +++ b/README.md @@ -229,8 +229,11 @@ Flags that do not scoped to a specific section, used over all the youtubeUnblock - `--fake-sni-seq-len=` This flag specifies **youtubeUnblock** to build a complicated construction of fake client hello packets. length determines how much fakes will be sent. Defaults to **1**. - `--fake-sni-type={default|custom|random}` This flag specifies which faking message type should be used for fake packets. For `random`, the message of random length and with random payload will be sent. For `default` the default payload (sni=www.google.com) is used. And for the `custom` option, the payload from `--fake-custom-payload` section utilized. Defaults to `default`. + - `--fake-custom-payload=` Useful with `--fake-sni-type=custom`. You should specify the payload for fake message manually. Use hex format: `--fake-custom-payload=0001020304` mean that 5 bytes sequence: `0x00`, `0x01`, `0x02`, `0x03`, `0x04` used as fake. +- `--fake-custom-payload-file=` Same as `--fake-custom-payload` but binary file instead of hex. The file should contain raw binary TLS message (TCP payload). + - `--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}` This flag determines the strategy of fake packets invalidation. Defaults to `randseq` - `randseq` specifies that random sequence/acknowledgment random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled. - `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured. diff --git a/src/args.c b/src/args.c index 6c07aa3..6ada186 100644 --- a/src/args.c +++ b/src/args.c @@ -309,6 +309,7 @@ enum { OPT_FAKE_SNI_SEQ_LEN, OPT_FAKE_SNI_TYPE, OPT_FAKE_CUSTOM_PAYLOAD, + OPT_FAKE_CUSTOM_PAYLOAD_FILE, OPT_START_SECTION, OPT_END_SECTION, OPT_DAEMONIZE, @@ -363,6 +364,7 @@ static struct option long_opt[] = { {"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}, + {"fake-custom-payload-file", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD_FILE}, {"faking-strategy", 1, 0, OPT_FAKING_STRATEGY}, {"fake-seq-offset", 1, 0, OPT_FAKE_SEQ_OFFSET}, {"faking-ttl", 1, 0, OPT_FAKING_TTL}, @@ -427,6 +429,7 @@ void print_usage(const char *argv0) { 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-custom-payload-file=\n"); printf("\t--fake-seq-offset=\n"); printf("\t--faking-ttl=\n"); printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}\n"); @@ -796,6 +799,7 @@ int yparse_args(struct config_t *config, int argc, char *argv[]) { break; case OPT_FAKE_CUSTOM_PAYLOAD: SFREE(sect_config->fake_custom_pkt); + sect_config->fake_custom_pkt_sz = 0; ret = parse_fake_custom_payload(optarg, §_config->fake_custom_pkt, §_config->fake_custom_pkt_sz); if (ret == -EINVAL) { @@ -805,6 +809,36 @@ int yparse_args(struct config_t *config, int argc, char *argv[]) { } break; + case OPT_FAKE_CUSTOM_PAYLOAD_FILE: +#ifdef KERNEL_SPACE + lgerr("--fake-custom-payload-file is not allowed in kernel space. Use --fake-custom-payload argument instead"); + goto error; +#else + { + SFREE(sect_config->fake_custom_pkt); + sect_config->fake_custom_pkt_sz = 0; + + ret = read_file(optarg); + if (ret < 0) { + goto error; + } + + + if (glob_file_size > MAX_FAKE_SIZE) { + goto invalid_opt; + } + sect_config->fake_custom_pkt = malloc(glob_file_size); + if (sect_config->fake_custom_pkt != NULL) { + memcpy(sect_config->fake_custom_pkt, glob_file_buffer, glob_file_size); + sect_config->fake_custom_pkt_sz = glob_file_size; + } else { + goto error; + } + + break; + } +#endif + case OPT_FK_WINSIZE: num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { @@ -931,7 +965,8 @@ stop_exec: #endif invalid_opt: - printf("Invalid option %s\n", long_opt[optIdx].name); + if (optind > 0 && optind <= argc) + lgerr("Invalid option %s\n", argv[optind - 1]); ret = -EINVAL; error: #ifndef KERNEL_SPACE @@ -939,7 +974,11 @@ error: #endif if (errno) ret = -errno; if (ret != -EINVAL) { - lgerror(ret, "argparse: error thrown in %s", long_opt[optIdx].name); + if (optind > 0 && optind <= argc) + lgerror( + ret == 0 ? EINVAL : -ret, + "argparse: error thrown in %s", argv[optind - 1] + ); ret = -EINVAL; } @@ -1030,6 +1069,7 @@ static size_t print_config_section(const struct section_config_t *section, char 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); }