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