From b2ac2e0d0373fe1ff1e10a4b594a3e0db6e025b4 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 4 Dec 2024 23:48:18 +0300 Subject: [PATCH 01/14] UDP faking support --- config.h | 3 +- mangle.c | 81 ++++++++++++++++++++++++++--------------- quic.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ quic.h | 10 +++++ utils.h | 14 +++++++ 5 files changed, 187 insertions(+), 30 deletions(-) diff --git a/config.h b/config.h index 02c4969..9178fdf 100644 --- a/config.h +++ b/config.h @@ -165,8 +165,9 @@ for (struct section_config_t *section = &config.default_config + config.custom_c #define FAKE_STRAT_PAST_SEQ (1 << 2) #define FAKE_STRAT_TCP_CHECK (1 << 3) #define FAKE_STRAT_TCP_MD5SUM (1 << 4) +#define FAKE_STRAT_UDP_CHECK (1 << 5) -#define FAKE_STRAT_COUNT 5 +#define FAKE_STRAT_COUNT 6 /** * This macros iterates through all faking strategies and executes code under it. diff --git a/mangle.c b/mangle.c index cc8d811..9bd3a7a 100644 --- a/mangle.c +++ b/mangle.c @@ -74,16 +74,32 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { break; } - if (verdict == PKT_CONTINUE) + if (verdict == PKT_CONTINUE) { + lgtrace_addp("continue_flow"); continue; + } - lgtrace_end(); - return verdict; + goto ret_verdict; } accept: + verdict = PKT_ACCEPT; + +ret_verdict: + + switch (verdict) { + case PKT_ACCEPT: + lgtrace_addp("accept"); + break; + case PKT_DROP: + lgtrace_addp("drop"); + break; + default: + lgtrace_addp("unknow verdict: %d", verdict); + } lgtrace_end(); - return PKT_ACCEPT; + + return verdict; } int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, uint32_t raw_payload_len) { @@ -309,13 +325,10 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra } continue_flow: - lgtrace_addp("continue_flow"); return PKT_CONTINUE; accept: - lgtrace_addp("accept"); return PKT_ACCEPT; drop: - lgtrace_addp("drop"); return PKT_DROP; } @@ -350,6 +363,7 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk } + if (section->quic_drop) { lgtrace_addp("QUIC probe"); const struct quic_lhdr *qch; @@ -384,39 +398,48 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk lgtrace_addp("quic initial message"); } -/* if (1) { - lgtrace_addp("Probe udp"); - if (ipver == IP4VERSION && ntohs(udph->dest) > 30) { - lgtrace_addp("udp fool"); - const uint8_t *payload; - uint32_t payload_len; - - uint32_t poses[10]; - int cnt = 3; - - poses[0] = 8; - for (int i = 1; i < cnt; i++) { - poses[i] = poses[i - 1] + 8; + for (int i = 0; i < 6; i++) { + NETBUF_ALLOC(fake_udp, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(fake_udp)) { + lgerror(-ENOMEM, "Allocation error"); + return -ENOMEM; } + uint32_t fsn_len = MAX_PACKET_SIZE; - ret = send_ip4_frags(pkt, pktlen, poses, cnt, 0); + struct udp_fake_type fake_type = { + .fake_len = 64, + .strategy = { + .strategy = FAKE_STRAT_UDP_CHECK, + }, + }; + ret = gen_fake_udp(fake_type, iph, iph_len, udph, fake_udp, &fsn_len); if (ret < 0) { - lgerror("ip4 send frags", ret); - goto accept; + lgerror(ret, "gen_fake_udp"); + goto erret_lc; } - goto drop; - } else { - lginfo("WARNING: IP fragmentation is supported only for IPv4\n"); + lgtrace_addp("post fake udp #%d", i + 1); + + ret = instance_config.send_raw_packet(fake_udp, fsn_len); + if (ret < 0) { + lgerror(ret, "send fake udp"); + goto erret_lc; + } + + NETBUF_FREE(fake_udp); + continue; +erret_lc: + NETBUF_FREE(fake_udp); goto accept; } - } -*/ + + ret = instance_config.send_raw_packet(pkt, pktlen); + goto drop; + } continue_flow: - lgtrace_addp("continue_flow"); return PKT_CONTINUE; accept_quic: accept: diff --git a/quic.c b/quic.c index 66bdb4e..77ea926 100644 --- a/quic.c +++ b/quic.c @@ -138,3 +138,112 @@ int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen, lgerror(-EINVAL, "QUIC invalid Initial packet"); return -EINVAL; } + +int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) { + void *iph; + uint32_t iph_len; + struct udphdr *udph; + uint8_t *data; + uint32_t dlen; + int ret; + + ret = udp_payload_split(payload, *plen, + &iph, &iph_len, &udph, + &data, &dlen); + + uint32_t ipxv = netproto_version(payload, *plen); + + if (ret < 0) { + return ret; + } + + + if (strategy.strategy == FAKE_STRAT_TTL) { + lgtrace_addp("set fake ttl to %d", strategy.faking_ttl); + + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->ttl = strategy.faking_ttl; + } else if (ipxv == IP6VERSION) { + ((struct ip6_hdr *)iph)->ip6_hops = strategy.faking_ttl; + } else { + lgerror(-EINVAL, "fail_packet: IP version is unsupported"); + return -EINVAL; + } + } + + if (ipxv == IP4VERSION) { + ((struct iphdr *)iph)->frag_off = 0; + } + + + set_ip_checksum(iph, iph_len); + + if (strategy.strategy == FAKE_STRAT_UDP_CHECK) { + lgtrace_addp("break fake tcp checksum"); + udph->check += 1; + } + + return 0; +} + +int gen_fake_udp(struct udp_fake_type type, + const void *ipxh, uint32_t iph_len, + const struct udphdr *udph, + uint8_t *buf, uint32_t *buflen) { + uint32_t data_len = type.fake_len; + int ret; + + + if (!ipxh || !udph || !buf || !buflen) + return -EINVAL; + + int ipxv = netproto_version(ipxh, iph_len); + + if (ipxv == IP4VERSION) { + const struct iphdr *iph = ipxh; + + memcpy(buf, iph, iph_len); + struct iphdr *niph = (struct iphdr *)buf; + + niph->protocol = IPPROTO_UDP; + } else if (ipxv == IP6VERSION) { + const struct ip6_hdr *iph = ipxh; + + iph_len = sizeof(struct ip6_hdr); + memcpy(buf, iph, iph_len); + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + + niph->ip6_nxt = IPPROTO_UDP; + } else { + return -EINVAL; + } + + uint32_t dlen = iph_len + sizeof(struct udphdr) + data_len; + + if (*buflen < dlen) + return -ENOMEM; + + memcpy(buf + iph_len, udph, sizeof(struct udphdr)); + uint8_t *bfdptr = buf + iph_len + sizeof(struct udphdr); + + memset(bfdptr, 0, data_len); + + if (ipxv == IP4VERSION) { + struct iphdr *niph = (struct iphdr *)buf; + niph->tot_len = htons(dlen); + niph->id = randint(); + } else if (ipxv == IP6VERSION) { + struct ip6_hdr *niph = (struct ip6_hdr *)buf; + niph->ip6_plen = htons(dlen - iph_len); + } + + struct udphdr *nudph = (struct udphdr *)(buf + iph_len); + nudph->len = htons(sizeof(struct udphdr) + data_len); + + + udp_fail_packet(type.strategy, buf, &dlen, *buflen); + + *buflen = dlen; + + return 0; +} diff --git a/quic.h b/quic.h index 1edbfca..4fc2430 100644 --- a/quic.h +++ b/quic.h @@ -1,6 +1,7 @@ #ifndef QUIC_H #define QUIC_H #include "types.h" +#include "utils.h" /** @@ -125,4 +126,13 @@ int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen, struct quici_hdr *qhdr, uint8_t **payload, uint32_t *plen); +// Like fail_packet for TCP +int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen); + +// Like gen_fake_sni for TCP +int gen_fake_udp(struct udp_fake_type type, + const void *ipxh, uint32_t iph_len, + const struct udphdr *udph, + uint8_t *buf, uint32_t *buflen); + #endif /* QUIC_H */ diff --git a/utils.h b/utils.h index d78b7b5..7a5a629 100644 --- a/utils.h +++ b/utils.h @@ -142,6 +142,20 @@ struct fake_type { struct failing_strategy strategy; }; +struct udp_failing_strategy { + unsigned int strategy; + uint8_t faking_ttl; +}; + +struct udp_fake_type { + uint16_t fake_len; + + // faking strategy of the fake packet. + // Does not support bitmask, pass standalone strategy. + // Pass 0 if you don't want any faking procedures. + struct udp_failing_strategy strategy; +}; + /** * Invalidates the raw packet. The function aims to invalid the packet * in such way as it will be accepted by DPI, but dropped by target server From 7480cd31b8fb924f5b89efb06a4b474e961debb5 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 6 Dec 2024 11:00:09 +0300 Subject: [PATCH 02/14] Fix lgerror errno logging --- youtubeUnblock.c | 67 ++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 6e9dcb2..fe67a25 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -45,12 +45,12 @@ static int open_socket(struct mnl_socket **_nl) { nl = mnl_socket_open(NETLINK_NETFILTER); if (nl == NULL) { - lgerror(errno, "mnl_socket_open"); + lgerror(-errno, "mnl_socket_open"); return -1; } if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) { - lgerror(errno, "mnl_socket_bind"); + lgerror(-errno, "mnl_socket_bind"); mnl_socket_close(nl); return -1; } @@ -65,7 +65,7 @@ static int close_socket(struct mnl_socket **_nl) { struct mnl_socket *nl = *_nl; if (nl == NULL) return 1; if (mnl_socket_close(nl) < 0) { - lgerror(errno, "mnl_socket_close"); + lgerror(-errno, "mnl_socket_close"); return -1; } @@ -77,26 +77,26 @@ static int close_socket(struct mnl_socket **_nl) { static int open_raw_socket(void) { if (rawsocket != -2) { errno = EALREADY; - lgerror(errno, "Raw socket is already opened"); + lgerror(-errno, "Raw socket is already opened"); return -1; } rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); if (rawsocket == -1) { - lgerror(errno, "Unable to create raw socket"); + lgerror(-errno, "Unable to create raw socket"); return -1; } int mark = config.mark; if (setsockopt(rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { - lgerror(errno, "setsockopt(SO_MARK, %d) failed\n", mark); + lgerror(-errno, "setsockopt(SO_MARK, %d) failed\n", mark); return -1; } int mst = pthread_mutex_init(&rawsocket_lock, NULL); if (mst) { - lgerror(errno, "Mutex err: %d\n", mst); + lgerror(-errno, "Mutex err: %d\n", mst); close(rawsocket); errno = mst; @@ -110,12 +110,12 @@ static int open_raw_socket(void) { static int close_raw_socket(void) { if (rawsocket < 0) { errno = EALREADY; - lgerror(errno, "Raw socket is not set"); + lgerror(-errno, "Raw socket is not set"); return -1; } if (close(rawsocket)) { - lgerror(errno, "Unable to close raw socket"); + lgerror(-errno, "Unable to close raw socket"); pthread_mutex_destroy(&rawsocket_lock); return -1; } @@ -129,28 +129,27 @@ static int close_raw_socket(void) { static int open_raw6_socket(void) { if (raw6socket != -2) { errno = EALREADY; - lgerror(errno, "Raw socket is already opened"); + lgerror(-errno, "Raw socket is already opened"); return -1; } raw6socket = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW); if (rawsocket == -1) { - lgerror(errno, "Unable to create raw socket"); + lgerror(-errno, "Unable to create raw socket"); return -1; } int mark = config.mark; if (setsockopt(raw6socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) { - lgerror(errno, "setsockopt(SO_MARK, %d) failed\n", mark); + lgerror(-errno, "setsockopt(SO_MARK, %d) failed\n", mark); return -1; } int mst = pthread_mutex_init(&raw6socket_lock, NULL); if (mst) { - lgerror(mst, "Mutex err: %d\n", mst); + lgerror(-errno, "Mutex err: %d\n", mst); close(raw6socket); - errno = mst; return -1; } @@ -162,12 +161,12 @@ static int open_raw6_socket(void) { static int close_raw6_socket(void) { if (raw6socket < 0) { errno = EALREADY; - lgerror(errno, "Raw socket is not set"); + lgerror(-errno, "Raw socket is not set"); return -1; } if (close(raw6socket)) { - lgerror(errno, "Unable to close raw socket"); + lgerror(-errno, "Unable to close raw socket"); pthread_mutex_destroy(&rawsocket_lock); return -1; } @@ -345,7 +344,7 @@ static int fallback_accept_packet(uint32_t id, struct queue_data qdata) { nfq_nlmsg_verdict_put(verdnlh, id, NF_ACCEPT); if (mnl_socket_sendto(*qdata._nl, verdnlh, verdnlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); return MNL_CB_ERROR; } @@ -370,7 +369,7 @@ void *delay_packet_send_fn(void *data) { int ret = send_raw_socket(pkt, pktlen); if (ret < 0) { errno = -ret; - lgerror(errno, "send delayed raw packet"); + lgerror(-errno, "send delayed raw packet"); } free(pkt); @@ -402,13 +401,13 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { struct packet_data packet = {0}; if (nfq_nlmsg_parse(nlh, attr) < 0) { - lgerror(errno, "Attr parse"); + lgerror(-errno, "Attr parse"); return MNL_CB_ERROR; } if (attr[NFQA_PACKET_HDR] == NULL) { errno = ENODATA; - lgerror(errno, "Metaheader not set"); + lgerror(-errno, "Metaheader not set"); return MNL_CB_ERROR; } @@ -449,7 +448,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { } if (mnl_socket_sendto(*qdata->_nl, verdnlh, verdnlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); return MNL_CB_ERROR; } @@ -462,7 +461,7 @@ int init_queue(int queue_num) { struct mnl_socket *nl; if (open_socket(&nl)) { - lgerror(errno, "Unable to open socket"); + lgerror(-errno, "Unable to open socket"); return -1; } @@ -484,7 +483,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, PF_INET, NFQNL_CFG_CMD_PF_UNBIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -492,7 +491,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, PF_INET, NFQNL_CFG_CMD_PF_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -501,7 +500,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, PF_INET6, NFQNL_CFG_CMD_PF_UNBIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -509,7 +508,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, PF_INET6, NFQNL_CFG_CMD_PF_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } } @@ -519,7 +518,7 @@ int init_queue(int queue_num) { nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND); if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -532,7 +531,7 @@ int init_queue(int queue_num) { } if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) { - lgerror(errno, "mnl_socket_send"); + lgerror(-errno, "mnl_socket_send"); goto die; } @@ -554,7 +553,7 @@ int init_queue(int queue_num) { while (1) { ret = mnl_socket_recvfrom(nl, buf, BUF_SIZE); if (ret == -1) { - lgerror(errno, "mnl_socket_recvfrom"); + lgerror(-errno, "mnl_socket_recvfrom"); goto die; } @@ -562,9 +561,9 @@ int init_queue(int queue_num) { if (ret < 0) { lgerror(-EPERM, "mnl_cb_run"); if (errno == EPERM) { - lgerror(errno, "Probably another instance of youtubeUnblock with the same queue number is running\n"); + lgerror(-errno, "Probably another instance of youtubeUnblock with the same queue number is running\n"); } else { - lgerror(errno, "Make sure the nfnetlink_queue kernel module is loaded\n"); + lgerror(-errno, "Make sure the nfnetlink_queue kernel module is loaded\n"); } goto die; } @@ -615,7 +614,7 @@ int main(int argc, char *argv[]) { int ret; if ((ret = parse_args(argc, argv)) != 0) { if (ret < 0) { - lgerror(errno, "Unable to parse args"); + lgerror(-errno, "Unable to parse args"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); @@ -626,13 +625,13 @@ int main(int argc, char *argv[]) { if (open_raw_socket() < 0) { - lgerror(errno, "Unable to open raw socket"); + lgerror(-errno, "Unable to open raw socket"); exit(EXIT_FAILURE); } if (config.use_ipv6) { if (open_raw6_socket() < 0) { - lgerror(errno, "Unable to open raw socket for ipv6"); + lgerror(-errno, "Unable to open raw socket for ipv6"); close_raw_socket(); exit(EXIT_FAILURE); } From f9a51944dd204618c676b973a35393b007ffd0b5 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 6 Dec 2024 13:45:50 +0300 Subject: [PATCH 03/14] Add args for UDP faking support. --- README.md | 21 +++++-- args.c | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++- config.h | 95 ++++++++++++++++++++----------- kargs.c | 2 +- mangle.c | 44 +++------------ quic.c | 76 ++++++++++++++++++++++++- quic.h | 3 + 7 files changed, 324 insertions(+), 80 deletions(-) diff --git a/README.md b/README.md index 12b99f7..fa59476 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,8 @@ Copy `youtubeUnblock.service` to `/usr/lib/systemd/system` (you should change th On nftables you should put next nftables rules: ```sh nft add chain inet fw4 youtubeUnblock '{ type filter hook postrouting priority mangle - 1; policy accept; }' -nft add rule inet fw4 youtubeUnblock 'meta l4proto { tcp, udp } th dport 443 ct original packets < 20 counter queue num 537 bypass' +nft add rule inet fw4 youtubeUnblock 'tcp dport 443 ct original packets < 20 counter queue num 537 bypass' +nft add rule inet fw4 youtubeUnblock 'meta l4proto udp ct original packets < 9 counter queue num 537 bypass' nft insert rule inet fw4 output 'mark and 0x8000 == 0x8000 counter accept' ``` @@ -143,7 +144,7 @@ On iptables you should put next iptables rules: ```sh iptables -t mangle -N YOUTUBEUNBLOCK iptables -t mangle -A YOUTUBEUNBLOCK -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass -iptables -t mangle -A YOUTUBEUNBLOCK -p udp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass +iptables -t mangle -A YOUTUBEUNBLOCK -p udp -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:8 -j NFQUEUE --queue-num 537 --queue-bypass iptables -t mangle -A POSTROUTING -j YOUTUBEUNBLOCK iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT ``` @@ -154,7 +155,7 @@ For IPv6 on iptables you need to duplicate rules above for ip6tables: ```sh ip6tables -t mangle -N YOUTUBEUNBLOCK ip6tables -t mangle -A YOUTUBEUNBLOCK -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass -ip6tables -t mangle -A YOUTUBEUNBLOCK -p udp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass +ip6tables -t mangle -A YOUTUBEUNBLOCK -p udp -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:8 -j NFQUEUE --queue-num 537 --queue-bypass ip6tables -t mangle -A POSTROUTING -j YOUTUBEUNBLOCK ip6tables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT ``` @@ -219,8 +220,6 @@ Available flags: - `--frag-sni-pos=` With this option **youtubeUnblock** will split the packet at the position pos. Defaults to 1. -- `--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. - `--synfake={1|0}` If 1, syn payload will be sent before each request. The idea is taken from syndata from zapret project. Syn payload will normally be discarded by endpoint but may be handled by TSPU. This option sends normal fake in that payload. Please note, that the option works for all the sites, so --sni-domains won't change anything. @@ -231,6 +230,18 @@ Available flags: - `--seg2delay=` This flag forces **youtubeUnblock** to wait a little bit before send the 2nd part of the split packet. +- `--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. + +- `--udp-fake-len=` Size of udp fake payload (typically payload is zeroes). Defaults to 64. + +- `--udp-dport-filter=<5,6,200-500>` Filter the UDP destination ports. Defaults to no ports. Specifie the ports you want to be handled by youtubeUnblock. + +- `--udp-filter-quic={disabled|all}` Enables QUIC filtering for UDP handler. If disabled, quic won't be processed, if all all quic initial packets will be handled. Defaults to disabled. + +- `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Just an alias for `--udp-filter-quic=all --udp-mode=drop`. + - `--silent` Disables verbose mode. - `--trace` Maximum verbosity for debugging purposes. diff --git a/args.c b/args.c index 3d97d59..77b3c4d 100644 --- a/args.c +++ b/args.c @@ -62,6 +62,12 @@ enum { 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, }; static struct option long_opt[] = { @@ -87,6 +93,12 @@ static struct option long_opt[] = { {"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}, @@ -157,6 +169,12 @@ void print_usage(const char *argv0) { 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--silent\n"); @@ -171,6 +189,87 @@ void print_usage(const char *argv0) { printf("\n"); } +int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) { + int ret = 0; + int seclen = 1; + int strlen = 0; + const char *p = optarg; + while (*p != '\0') { + if (*p == ',') + seclen++; + p++; + } + strlen = p - optarg; + + struct udp_dport_range *udp_dport_ranges = malloc( + seclen * sizeof(struct udp_dport_range)); + + int i = 0; + + + p = optarg; + const char *ep = p; + while (1) { + if (*ep == '\0' || *ep == ',') { + if (ep == p) { + if (*ep == '\0') + break; + + p++, ep++; + continue; + } + + char *endp; + long num1 = strtol(p, &endp, 10); + long num2 = num1; + if (errno) + goto erret; + + if (endp != ep) { + if (*endp == '-') { + endp++; + num2 = strtol(endp, &endp, 10); + + if (endp != ep || errno) + goto erret; + } else { + goto erret; + } + } + + if ( + !(num1 > 0 && num1 < (1 << 16)) || + !(num2 > 0 && num2 < (1 << 16)) || + num2 < num1 + ) + goto erret; + + udp_dport_ranges[i] = (struct udp_dport_range){ + .start = num1, + .end = num2 + }; + i++; + + if (*ep == '\0') { + break; + } else { + p = ep + 1; + ep = p; + } + } else { + ep++; + } + } + + *udpr = udp_dport_ranges; + *udpr_len = seclen; + return 0; + +erret: + free(udp_dport_ranges); + return -1; +} + int parse_args(int argc, char *argv[]) { int opt; int optIdx = 0; @@ -443,7 +542,8 @@ int parse_args(int argc, char *argv[]) { sect_config->seg2_delay = num; break; case OPT_QUIC_DROP: - sect_config->quic_drop = 1; + 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) { @@ -471,6 +571,63 @@ int parse_args(int argc, char *argv[]) { 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: + { + struct udp_dport_range *udp_dport_range; + int udp_range_len = 0; + if (parse_udp_dport_range(optarg, &udp_dport_range, &udp_range_len) < 0) { + goto invalid_opt; + } + sect_config->udp_dport_range = udp_dport_range; + sect_config->udp_dport_range_len = udp_range_len; + break; + } + case OPT_UDP_FILTER_QUIC: + 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; @@ -490,7 +647,7 @@ int parse_args(int argc, char *argv[]) { error: print_usage(argv[0]); errno = EINVAL; - return -1; + return -errno; } void print_welcome() { @@ -575,7 +732,7 @@ void print_welcome() { lginfo("Fake SYN payload will be sent with each TCP request SYN packet\n"); } - if (section->quic_drop) { + if (section->udp_filter_quic && section->udp_mode == UDP_MODE_DROP) { lginfo("All QUIC packets will be dropped\n"); } diff --git a/config.h b/config.h index 9178fdf..d9ac592 100644 --- a/config.h +++ b/config.h @@ -6,6 +6,7 @@ #endif #include "raw_replacements.h" +#include "types.h" typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len); /** @@ -20,6 +21,11 @@ struct instance_config_t { }; extern struct instance_config_t instance_config; +struct udp_dport_range { + uint16_t start; + uint16_t end; +}; + struct section_config_t { const char *domains_str; unsigned int domains_strlen; @@ -40,8 +46,6 @@ struct section_config_t { #define FAKE_PAYLOAD_DEFAULT 2 int fake_sni_type; - int quic_drop; - /* In milliseconds */ unsigned int seg2_delay; int synfake; @@ -64,6 +68,14 @@ struct section_config_t { #define SNI_DETECTION_BRUTE 1 int sni_detection; + int udp_mode; + unsigned int udp_fake_seq_len; + unsigned int udp_fake_len; + int udp_faking_strategy; + + struct udp_dport_range *udp_dport_range; + int udp_dport_range_len; + int udp_filter_quic; }; #define MAX_CONFIGLIST_LEN 64 @@ -96,37 +108,6 @@ for (struct section_config_t *section = &config.default_config + config.custom_c #define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config) -#define default_section_config { \ - .frag_sni_reverse = 1, \ - .frag_sni_faked = 0, \ - .fragmentation_strategy = FRAGMENTATION_STRATEGY, \ - .faking_strategy = FAKING_STRATEGY, \ - .faking_ttl = FAKE_TTL, \ - .fake_sni = 1, \ - .fake_sni_seq_len = 1, \ - .fake_sni_type = FAKE_PAYLOAD_DEFAULT, \ - .frag_middle_sni = 1, \ - .frag_sni_pos = 1, \ - .fakeseq_offset = 10000, \ - .synfake = 0, \ - .synfake_len = 0, \ - .quic_drop = 0, \ - \ - .seg2_delay = 0, \ - \ - .domains_str = defaul_snistr, \ - .domains_strlen = sizeof(defaul_snistr), \ - \ - .exclude_domains_str = "", \ - .exclude_domains_strlen = 0, \ - \ - .fake_sni_pkt = fake_sni_old, \ - .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \ - .fake_custom_pkt = custom_fake_buf, \ - .fake_custom_pkt_sz = 0, \ - .sni_detection = SNI_DETECTION_PARSE, \ -} - #define MAX_THREADS 16 #ifndef THREADS_NUM @@ -199,4 +180,52 @@ if ((fake_bitmask) & strategy) static const char defaul_snistr[] = DEFAULT_SNISTR; +enum { + UDP_MODE_DROP, + UDP_MODE_FAKE, +}; + +enum { + UDP_FILTER_QUIC_DISABLED, + UDP_FILTER_QUIC_ALL, +}; + +#define default_section_config { \ + .frag_sni_reverse = 1, \ + .frag_sni_faked = 0, \ + .fragmentation_strategy = FRAGMENTATION_STRATEGY, \ + .faking_strategy = FAKING_STRATEGY, \ + .faking_ttl = FAKE_TTL, \ + .fake_sni = 1, \ + .fake_sni_seq_len = 1, \ + .fake_sni_type = FAKE_PAYLOAD_DEFAULT, \ + .frag_middle_sni = 1, \ + .frag_sni_pos = 1, \ + .fakeseq_offset = 10000, \ + .synfake = 0, \ + .synfake_len = 0, \ + \ + .seg2_delay = 0, \ + \ + .domains_str = defaul_snistr, \ + .domains_strlen = sizeof(defaul_snistr), \ + \ + .exclude_domains_str = "", \ + .exclude_domains_strlen = 0, \ + \ + .fake_sni_pkt = fake_sni_old, \ + .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \ + .fake_custom_pkt = custom_fake_buf, \ + .fake_custom_pkt_sz = 0, \ + .sni_detection = SNI_DETECTION_PARSE, \ + \ + .udp_mode = UDP_MODE_FAKE, \ + .udp_fake_seq_len = 6, \ + .udp_fake_len = 64, \ + .udp_faking_strategy = FAKE_STRAT_UDP_CHECK, \ + .udp_dport_range = NULL, \ + .udp_dport_range_len = 0, \ + .udp_filter_quic = UDP_FILTER_QUIC_DISABLED, \ +} + #endif /* YTB_CONFIG_H */ diff --git a/kargs.c b/kargs.c index 0e596b3..461f104 100644 --- a/kargs.c +++ b/kargs.c @@ -161,7 +161,7 @@ static const struct kernel_param_ops exclude_domains_ops = { module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664); module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664); -module_param_cb(quic_drop, &boolean_parameter_ops, &def_section->quic_drop, 0664); +// module_param_cb(quic_drop, &boolean_parameter_ops, &def_section->quic_drop, 0664); static int verbosity_set(const char *val, const struct kernel_param *kp) { size_t len; diff --git a/mangle.c b/mangle.c index 9bd3a7a..e661454 100644 --- a/mangle.c +++ b/mangle.c @@ -363,43 +363,13 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk } + if (!detect_udp_filtered(section, pkt, pktlen)) + goto continue_flow; - if (section->quic_drop) { - lgtrace_addp("QUIC probe"); - const struct quic_lhdr *qch; - uint32_t qch_len; - struct quic_cids qci; - uint8_t *quic_raw_payload; - uint32_t quic_raw_plen; - ret = quic_parse_data((uint8_t *)data, dlen, - (struct quic_lhdr **)&qch, &qch_len, &qci, - &quic_raw_payload, &quic_raw_plen); - - if (ret < 0) { - lgtrace_addp("undefined type"); - goto accept_quic; - } - - lgtrace_addp("QUIC detected"); - uint8_t qtype = qch->type; - + if (section->udp_mode == UDP_MODE_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_quic; - } - - lgtrace_addp("quic initial message"); - } - - if (1) { - for (int i = 0; i < 6; i++) { + else if (section->udp_mode == UDP_MODE_FAKE) { + for (int i = 0; i < section->udp_fake_seq_len; i++) { NETBUF_ALLOC(fake_udp, MAX_PACKET_SIZE); if (!NETBUF_CHECK(fake_udp)) { lgerror(-ENOMEM, "Allocation error"); @@ -408,9 +378,9 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk uint32_t fsn_len = MAX_PACKET_SIZE; struct udp_fake_type fake_type = { - .fake_len = 64, + .fake_len = section->udp_fake_len, .strategy = { - .strategy = FAKE_STRAT_UDP_CHECK, + .strategy = section->udp_faking_strategy, }, }; ret = gen_fake_udp(fake_type, iph, iph_len, udph, fake_udp, &fsn_len); diff --git a/quic.c b/quic.c index 77ea926..b10bebd 100644 --- a/quic.c +++ b/quic.c @@ -179,7 +179,7 @@ int udp_fail_packet(struct udp_failing_strategy strategy, uint8_t *payload, uint set_ip_checksum(iph, iph_len); if (strategy.strategy == FAKE_STRAT_UDP_CHECK) { - lgtrace_addp("break fake tcp checksum"); + lgtrace_addp("break fake udp checksum"); udph->check += 1; } @@ -247,3 +247,77 @@ int gen_fake_udp(struct udp_fake_type type, return 0; } + +int detect_udp_filtered(const struct section_config_t *section, + const uint8_t *payload, uint32_t plen) { + const void *iph; + uint32_t iph_len; + const struct udphdr *udph; + const uint8_t *data; + uint32_t dlen; + int ret; + int ipver; + + ipver = netproto_version(payload, plen); + + ret = udp_payload_split((uint8_t *)payload, plen, + (void **)&iph, &iph_len, + (struct udphdr **)&udph, + (uint8_t **)&data, &dlen); + int udp_dport = ntohs(udph->dest); + lgtrace_addp("UDP dport: %d", udp_dport); + + + if (ret < 0) { + goto skip; + } + + if (section->udp_filter_quic) { + const struct quic_lhdr *qch; + uint32_t qch_len; + struct quic_cids qci; + uint8_t *quic_raw_payload; + uint32_t quic_raw_plen; + + lgtrace_addp("QUIC probe"); + + 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 skip; + } + + lgtrace_addp("QUIC detected"); + uint8_t qtype = qch->type; + + goto approve; + + // if (qch->version == QUIC_V1) + // qtype = quic_convtype_v1(qtype); + // else if (qch->version == QUIC_V2) + // qtype = quic_convtype_v2(qtype); + // + // if (qtype != QUIC_INITIAL_TYPE) { + // lgtrace_addp("quic message type: %d", qtype); + // goto accept_quic; + // } + // + // lgtrace_addp("quic initial message"); + } + + for (int i = 0; i < section->udp_dport_range_len; i++) { + struct udp_dport_range crange = section->udp_dport_range[i]; + if (udp_dport >= crange.start && udp_dport <= crange.end) { + lgtrace_addp("matched to %d-%d", crange.start, crange.end); + goto approve; + } + } + +skip: + return 0; +approve: + return 1; +} diff --git a/quic.h b/quic.h index 4fc2430..0422941 100644 --- a/quic.h +++ b/quic.h @@ -135,4 +135,7 @@ int gen_fake_udp(struct udp_fake_type type, const struct udphdr *udph, uint8_t *buf, uint32_t *buflen); +int detect_udp_filtered(const struct section_config_t *section, + const uint8_t *payload, uint32_t plen); + #endif /* QUIC_H */ From b452ed2d553ed9f1eea2eb5aef094f151f51c9c1 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 6 Dec 2024 14:05:37 +0300 Subject: [PATCH 04/14] quic_drop for kmod --- kargs.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/kargs.c b/kargs.c index 461f104..f0ee412 100644 --- a/kargs.c +++ b/kargs.c @@ -161,7 +161,38 @@ static const struct kernel_param_ops exclude_domains_ops = { module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664); module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664); -// module_param_cb(quic_drop, &boolean_parameter_ops, &def_section->quic_drop, 0664); + +static int quic_drop_set(const char *val, const struct kernel_param *kp) { + int n = 0, ret; + ret = kstrtoint(val, 10, &n); + if (ret != 0 || (n != 0 && n != 1)) + return -EINVAL; + + if (n) { + def_section->udp_mode = UDP_MODE_DROP; + def_section->udp_filter_quic = UDP_FILTER_QUIC_ALL; + } else { + def_section->udp_filter_quic = UDP_FILTER_QUIC_DISABLED; + } + + return 0; +} + +static int quic_drop_get(char *buffer, const struct kernel_param *kp) { + if (def_section->udp_mode == UDP_MODE_DROP && + def_section->udp_filter_quic == UDP_FILTER_QUIC_ALL) { + return sprintf(buffer, "%d\n", 1); + } else { + return sprintf(buffer, "%d\n", 0); + } +} + +static const struct kernel_param_ops quic_drop_ops = { + .set = quic_drop_set, + .get = quic_drop_get +}; + +module_param_cb(quic_drop, &quic_drop_ops, NULL, 0664); static int verbosity_set(const char *val, const struct kernel_param *kp) { size_t len; From 9b5c8a729dd63349d63ef8bd6b0b0258b3dfc670 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Fri, 6 Dec 2024 18:25:43 +0300 Subject: [PATCH 05/14] Allow to disable TLS processing for the section --- README.md | 2 ++ args.c | 20 ++++++++++++++++++-- args.h | 4 ++-- config.h | 3 +++ mangle.c | 3 +++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fa59476..9cf9836 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,8 @@ Available flags: - `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Just an alias for `--udp-filter-quic=all --udp-mode=drop`. +- `--tls={enabled|disabled}` Set it if you want not to process TLS traffic in current section. May be used if you want to set only UDP-based section. (Here section is a unit between `--fbegin` and `--fend` flags). + - `--silent` Disables verbose mode. - `--trace` Maximum verbosity for debugging purposes. diff --git a/args.c b/args.c index 77b3c4d..e19b3e1 100644 --- a/args.c +++ b/args.c @@ -68,6 +68,7 @@ enum { OPT_UDP_FAKING_STRATEGY, OPT_UDP_DPORT_FILTER, OPT_UDP_FILTER_QUIC, + OPT_TLS_ENABLED, }; static struct option long_opt[] = { @@ -78,6 +79,7 @@ static struct option long_opt[] = { {"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}, @@ -132,7 +134,7 @@ static long parse_numeric_option(const char* value) { return result; } -void print_version() { +void print_version(void) { printf("youtubeUnblock" #if defined(PKG_VERSION) " " PKG_VERSION @@ -151,6 +153,7 @@ void print_usage(const char *argv0) { 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"); @@ -375,6 +378,16 @@ int parse_args(int argc, char *argv[]) { 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: if (!strcmp(optarg, "all")) { sect_config->all_domains = 1; @@ -650,7 +663,7 @@ int parse_args(int argc, char *argv[]) { return -errno; } -void print_welcome() { +void print_welcome(void) { if (config.syslog) { printf("Logging to system log\n"); } @@ -671,6 +684,9 @@ void print_welcome() { int section_number = CONFIG_SECTION_NUMBER(section); lginfo("Section #%d\n", section_number); + if (!section->tls_enabled) { + lginfo("TCP TLS is disabled for section!\n"); + } switch (section->fragmentation_strategy) { case FRAG_STRAT_TCP: lginfo("Using TCP segmentation\n"); diff --git a/args.h b/args.h index 98d65cf..102772f 100644 --- a/args.h +++ b/args.h @@ -1,11 +1,11 @@ #ifndef ARGS_H #define ARGS_H -void print_version(); +void print_version(void); void print_usage(const char *argv0); int parse_args(int argc, char *argv[]); /* Prints starting messages */ -void print_welcome(); +void print_welcome(void); #endif /* ARGS_H */ diff --git a/config.h b/config.h index d9ac592..3bebfdb 100644 --- a/config.h +++ b/config.h @@ -30,6 +30,8 @@ struct section_config_t { const char *domains_str; unsigned int domains_strlen; + int tls_enabled; + int fragmentation_strategy; int frag_sni_reverse; int frag_sni_faked; @@ -191,6 +193,7 @@ enum { }; #define default_section_config { \ + .tls_enabled = 1, \ .frag_sni_reverse = 1, \ .frag_sni_faked = 0, \ .fragmentation_strategy = FRAGMENTATION_STRATEGY, \ diff --git a/mangle.c b/mangle.c index e661454..961ec98 100644 --- a/mangle.c +++ b/mangle.c @@ -170,6 +170,9 @@ int process_tcp_packet(const struct section_config_t *section, const uint8_t *ra if (tcph->syn) goto continue_flow; + if (!section->tls_enabled) + goto continue_flow; + struct tls_verdict vrd = analyze_tls_data(section, data, dlen); lgtrace_addp("TLS analyzed"); From 457a7a7f04c0f520b6d70d59b0f652c1c927d83d Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 8 Dec 2024 16:06:50 +0300 Subject: [PATCH 06/14] Massive update of argparse system This is required for furhter maintance of kernel module. Aims to provide common interface for both --- Kbuild | 2 +- args.c | 777 +++++++++++++++++++++++++--------- args.h | 14 +- config.h | 77 ++-- getopt.c | 204 +++++++++ getopt.h | 45 ++ kargs.c | 1033 ++++++++++++++++++++++++---------------------- kytunblock.c | 9 +- mangle.c | 2 +- tls.c | 251 ++++++----- types.h | 8 + uspace.mk | 2 +- youtubeUnblock.c | 2 +- 13 files changed, 1578 insertions(+), 848 deletions(-) create mode 100644 getopt.c create mode 100644 getopt.h diff --git a/Kbuild b/Kbuild index 36eef22..3dbbfe3 100644 --- a/Kbuild +++ b/Kbuild @@ -1,3 +1,3 @@ obj-m := kyoutubeUnblock.o -kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o +kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o getopt.o args.o ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement diff --git a/args.c b/args.c index e19b3e1..89c0284 100644 --- a/args.c +++ b/args.c @@ -1,33 +1,219 @@ #include "config.h" -#include -#include -#include -#include -#include -#include -#include +#include "types.h" + #include "types.h" #include "args.h" #include "logging.h" +#include "getopt.h" +#include "raw_replacements.h" -static char custom_fake_buf[MAX_FAKE_SIZE]; +#ifdef KERNEL_SPACE +static int errno = 0; +#define strtol kstrtol +#endif -struct config_t config = { - .threads = THREADS_NUM, - .queue_start_num = DEFAULT_QUEUE_NUM, - .mark = DEFAULT_RAWSOCKET_MARK, - .use_ipv6 = 1, +struct config_t config = default_config_set; - .verbose = VERBOSE_DEBUG, - .use_gso = true, +static int parse_sni_domains(struct domains_list **dlist, const char *domains_str, size_t domains_strlen) { + // Empty and shouldn't be used + struct domains_list ndomain = {0}; + struct domains_list *cdomain = &ndomain; - .default_config = default_section_config, - .custom_configs_len = 0, + unsigned int j = 0; + for (unsigned int i = 0; i <= domains_strlen; i++) { + if (( i == domains_strlen || + domains_str[i] == '\0' || + domains_str[i] == ',' || + domains_str[i] == '\n' )) { - .daemonize = 0, - .noclose = 0, - .syslog = 0, -}; + if (i == j) { + j++; + continue; + } + + unsigned int domain_len = (i - j); + const char *domain_startp = domains_str + j; + struct domains_list *edomain = malloc(sizeof(struct domains_list)); + *edomain = (struct domains_list){0}; + if (edomain == NULL) { + return -ENOMEM; + } + + edomain->domain_len = domain_len; + edomain->domain_name = malloc(domain_len + 1); + if (edomain->domain_name == NULL) { + return -ENOMEM; + } + + strncpy(edomain->domain_name, domain_startp, domain_len); + edomain->domain_name[domain_len] = '\0'; + cdomain->next = edomain; + cdomain = edomain; + + j = i + 1; + } + } + + *dlist = ndomain.next; + return 0; +} + +static void free_sni_domains(struct domains_list *dlist) { + for (struct domains_list *ldl = dlist; ldl != NULL;) { + struct domains_list *ndl = ldl->next; + printf("freeing domains\n"); + SFREE(ldl->domain_name); + SFREE(ldl); + ldl = ndl; + } +} + +static long parse_numeric_option(const char* value) { + errno = 0; + + if (*value == '\0') { + errno = EINVAL; + return 0; + } + + long result; + int len; + sscanf(value, "%ld%n", &result, &len); + if (*(value + len) != '\0') { + errno = EINVAL; + return 0; + } + + return result; +} + +static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) { + int ret = 0; + int seclen = 1; + const char *p = str; + while (*p != '\0') { + if (*p == ',') + seclen++; + p++; + } + +#ifdef KERNEL_SPACE + struct udp_dport_range *udp_dport_ranges = kmalloc( + seclen * sizeof(struct udp_dport_range), GFP_KERNEL); + +#else + struct udp_dport_range *udp_dport_ranges = malloc( + seclen * sizeof(struct udp_dport_range)); +#endif + if (udp_dport_ranges == NULL) { + return -ENOMEM; + } + + int i = 0; + + + p = str; + const char *ep = p; + while (1) { + if (*ep == '\0' || *ep == ',') { + if (ep == p) { + if (*ep == '\0') + break; + + p++, ep++; + continue; + } + + const char *endp; + long num1; + int len; + sscanf(p, "%ld%n", &num1, &len); + endp = p + len; + long num2 = num1; + + if (endp != ep) { + if (*endp == '-') { + endp++; + int len; + sscanf(endp, "%ld%n", &num2, &len); + endp = endp + len; + + if (endp != ep) + goto erret; + } else { + goto erret; + } + } + + if ( + !(num1 > 0 && num1 < (1 << 16)) || + !(num2 > 0 && num2 < (1 << 16)) || + num2 < num1 + ) + goto erret; + + udp_dport_ranges[i] = (struct udp_dport_range){ + .start = num1, + .end = num2 + }; + i++; + + if (*ep == '\0') { + break; + } else { + p = ep + 1; + ep = p; + } + } else { + ep++; + } + } + + if (i == 0) { + free(udp_dport_ranges); + } + + *udpr = udp_dport_ranges; + *udpr_len = i; + return 0; + +erret: + free(udp_dport_ranges); + + return -1; +} + +// Allocates and fills custom fake buffer +static int parse_fake_custom_payload( + const char *custom_hex_fake, + char **custom_fake_buf, unsigned int *custom_fake_len) { + int ret; + + size_t custom_hlen = strlen(custom_hex_fake); + if ((custom_hlen & 1) == 1) { + printf("Custom fake hex should be divisible by two\n"); + return -EINVAL; + } + + size_t custom_len = custom_hlen >> 1; + if (custom_len > MAX_FAKE_SIZE) { + printf("Custom fake is too large\n"); + return -EINVAL; + } + unsigned char *custom_buf = malloc(custom_len); + + for (int i = 0; i < custom_len; i++) { + ret = sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); + if (ret != 1) { + free(custom_buf); + return -EINVAL; + } + } + + *custom_fake_buf = (char *)custom_buf; + *custom_fake_len = custom_len; + return 0; +} enum { OPT_SNI_DOMAINS, @@ -69,11 +255,14 @@ enum { OPT_UDP_DPORT_FILTER, OPT_UDP_FILTER_QUIC, OPT_TLS_ENABLED, + OPT_CLS, + OPT_HELP, + OPT_VERSION, }; static struct option long_opt[] = { - {"help", 0, 0, 'h'}, - {"version", 0, 0, 'v'}, + {"help", 0, 0, OPT_HELP}, + {"version", 0, 0, OPT_VERSION}, {"sni-domains", 1, 0, OPT_SNI_DOMAINS}, {"exclude-domains", 1, 0, OPT_EXCLUDE_DOMAINS}, {"fake-sni", 1, 0, OPT_FAKE_SNI}, @@ -113,27 +302,10 @@ static struct option long_opt[] = { {"packet-mark", 1, 0, OPT_PACKET_MARK}, {"fbegin", 0, 0, OPT_START_SECTION}, {"fend", 0, 0, OPT_END_SECTION}, - {0,0,0,0} + {"cls", 0, 0, OPT_CLS}, + {0, 0, 0, 0}, }; -static long parse_numeric_option(const char* value) { - errno = 0; - - if (*value == '\0') { - errno = EINVAL; - return 0; - } - - char* end; - long result = strtol(value, &end, 10); - if (*end != '\0') { - errno = EINVAL; - return 0; - } - - return result; -} - void print_version(void) { printf("youtubeUnblock" #if defined(PKG_VERSION) @@ -192,93 +364,22 @@ void print_usage(const char *argv0) { printf("\n"); } -int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) { - int ret = 0; - int seclen = 1; - int strlen = 0; - const char *p = optarg; - while (*p != '\0') { - if (*p == ',') - seclen++; - p++; - } - strlen = p - optarg; - - struct udp_dport_range *udp_dport_ranges = malloc( - seclen * sizeof(struct udp_dport_range)); - - int i = 0; - - - p = optarg; - const char *ep = p; - while (1) { - if (*ep == '\0' || *ep == ',') { - if (ep == p) { - if (*ep == '\0') - break; - - p++, ep++; - continue; - } - - char *endp; - long num1 = strtol(p, &endp, 10); - long num2 = num1; - if (errno) - goto erret; - - if (endp != ep) { - if (*endp == '-') { - endp++; - num2 = strtol(endp, &endp, 10); - - if (endp != ep || errno) - goto erret; - } else { - goto erret; - } - } - - if ( - !(num1 > 0 && num1 < (1 << 16)) || - !(num2 > 0 && num2 < (1 << 16)) || - num2 < num1 - ) - goto erret; - - udp_dport_ranges[i] = (struct udp_dport_range){ - .start = num1, - .end = num2 - }; - i++; - - if (*ep == '\0') { - break; - } else { - p = ep + 1; - ep = p; - } - } else { - ep++; - } - } - - *udpr = udp_dport_ranges; - *udpr_len = seclen; - return 0; - -erret: - free(udp_dport_ranges); - return -1; -} - -int parse_args(int argc, char *argv[]) { +int yparse_args(int argc, char *argv[]) { int opt; int optIdx = 0; + optind=1, opterr=1, optreset=0; long num; + int ret; + + struct 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 = &config.default_config; + struct section_config_t *sect_config = rep_config.last_section; + int sect_i = 0; + sect_config->id = sect_i++; #define SECT_ITER_DEFAULT 1 #define SECT_ITER_INSIDE 2 @@ -286,86 +387,95 @@ int parse_args(int argc, char *argv[]) { int section_iter = SECT_ITER_DEFAULT; - while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) { + while ((opt = getopt_long(argc, argv, "", long_opt, &optIdx)) != -1) { switch (opt) { + case OPT_CLS: + free_config(rep_config); + ret = init_config(&rep_config); + if (ret < 0) + return ret; + default_section = rep_config.last_section; + + sect_config = rep_config.last_section; + sect_i = 0; + sect_config->id = sect_i++; + section_iter = SECT_ITER_DEFAULT; + + break; + /* config_t scoped configs */ - case 'h': + case OPT_HELP: print_usage(argv[0]); +#ifndef KERNEL_SPACE goto stop_exec; - case 'v': +#else + break; +#endif + case OPT_VERSION: print_version(); +#ifndef KERNEL_SPACE goto stop_exec; +#else + break; +#endif case OPT_TRACE: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - config.verbose = 2; + rep_config.verbose = 2; break; case OPT_SILENT: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.verbose = 0; + rep_config.verbose = 0; break; case OPT_NO_GSO: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.use_gso = 0; + rep_config.use_gso = 0; break; case OPT_NO_IPV6: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - - config.use_ipv6 = 0; + rep_config.use_ipv6 = 0; break; case OPT_DAEMONIZE: - config.daemonize = 1; + rep_config.daemonize = 1; break; case OPT_NOCLOSE: - config.noclose = 1; + rep_config.noclose = 1; break; case OPT_SYSLOG: - config.syslog = 1; + rep_config.syslog = 1; break; case OPT_THREADS: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - num = parse_numeric_option(optarg); if (errno != 0 || num < 0 || num > MAX_THREADS) { goto invalid_opt; } - config.threads = num; + rep_config.threads = num; break; case OPT_QUEUE_NUM: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } - config.queue_start_num = num; + rep_config.queue_start_num = num; break; case OPT_PACKET_MARK: - if (section_iter != SECT_ITER_DEFAULT) - goto invalid_opt; - num = parse_numeric_option(optarg); if (errno != 0 || num < 0) { goto invalid_opt; } - config.mark = num; + rep_config.mark = num; break; case OPT_START_SECTION: if (section_iter != SECT_ITER_DEFAULT && section_iter != SECT_ITER_OUTSIDE) goto invalid_opt; - sect_config = &config.custom_configs[config.custom_configs_len++]; - *sect_config = (struct section_config_t)default_section_config; + struct section_config_t *nsect; + ret = init_section_config(&nsect, rep_config.last_section); + if (ret < 0) { + goto error; + } + rep_config.last_section->next = nsect; + rep_config.last_section = nsect; + sect_config = nsect; + sect_config->id = sect_i++; section_iter = SECT_ITER_INSIDE; break; @@ -374,7 +484,7 @@ int parse_args(int argc, char *argv[]) { goto invalid_opt; section_iter = SECT_ITER_OUTSIDE; - sect_config = &config.default_config; + sect_config = default_section; break; /* section_config_t scoped configs */ @@ -389,16 +499,20 @@ int parse_args(int argc, char *argv[]) { break; case OPT_SNI_DOMAINS: + sect_config->all_domains = 0; if (!strcmp(optarg, "all")) { sect_config->all_domains = 1; } - sect_config->domains_str = optarg; - sect_config->domains_strlen = strlen(sect_config->domains_str); + ret = parse_sni_domains(§_config->sni_domains, optarg, strlen(optarg)); + if (ret < 0) + goto error; break; case OPT_EXCLUDE_DOMAINS: - sect_config->exclude_domains_str = optarg; - sect_config->exclude_domains_strlen = strlen(sect_config->exclude_domains_str); + ret = parse_sni_domains(§_config->exclude_sni_domains, optarg, strlen(optarg)); + if (ret < 0) + goto error; + break; case OPT_FRAG: if (strcmp(optarg, "tcp") == 0) { @@ -512,30 +626,16 @@ int parse_args(int argc, char *argv[]) { } break; - case OPT_FAKE_CUSTOM_PAYLOAD: { - uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; - - const char *custom_hex_fake = optarg; - size_t custom_hlen = strlen(custom_hex_fake); - if ((custom_hlen & 1) == 1) { - printf("Custom fake hex should be divisible by two\n"); - goto invalid_opt; - } - - - size_t custom_len = custom_hlen >> 1; - if (custom_len > MAX_FAKE_SIZE) { - printf("Custom fake is too large\n"); - goto invalid_opt; - } - - for (int i = 0; i < custom_len; i++) { - sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); - } + case OPT_FAKE_CUSTOM_PAYLOAD: + SFREE(sect_config->udp_dport_range); - sect_config->fake_custom_pkt_sz = custom_len; - sect_config->fake_custom_pkt = (char *)custom_buf; + ret = parse_fake_custom_payload(optarg, §_config->fake_custom_pkt, §_config->fake_custom_pkt_sz); + if (ret == -EINVAL) { + goto invalid_opt; + } else if (ret < 0) { + goto error; } + break; case OPT_FK_WINSIZE: num = parse_numeric_option(optarg); @@ -623,13 +723,10 @@ int parse_args(int argc, char *argv[]) { break; case OPT_UDP_DPORT_FILTER: { - struct udp_dport_range *udp_dport_range; - int udp_range_len = 0; - if (parse_udp_dport_range(optarg, &udp_dport_range, &udp_range_len) < 0) { + SFREE(sect_config->udp_dport_range); + if (parse_udp_dport_range(optarg, §_config->udp_dport_range, §_config->udp_dport_range_len) < 0) { goto invalid_opt; } - sect_config->udp_dport_range = udp_dport_range; - sect_config->udp_dport_range_len = udp_range_len; break; } case OPT_UDP_FILTER_QUIC: @@ -648,22 +745,248 @@ int parse_args(int argc, char *argv[]) { } + struct config_t old_config = config; + config = rep_config; + free_config(old_config); errno = 0; return 0; + stop_exec: + free_config(rep_config); errno = 0; return 1; invalid_opt: printf("Invalid option %s\n", long_opt[optIdx].name); + ret = -EINVAL; error: +#ifndef KERNEL_SPACE print_usage(argv[0]); - errno = EINVAL; +#endif + if (ret != -EINVAL) { + lgerror(ret, "Error thrown in %s\n", long_opt[optIdx].name); + } + + errno = -ret; + free_config(rep_config); return -errno; } +#define print_cnf_raw(fmt, ...) do { \ + sz = snprintf(buf_ptr, buf_sz, fmt, ##__VA_ARGS__); \ + if (sz > buf_sz) { buf_sz = 0; } \ + else { buf_sz -= sz; } \ + buf_ptr += sz; \ +} while(0) + +#define print_cnf_buf(fmt, ...) print_cnf_raw(fmt " ", ##__VA_ARGS__) +// Returns written buffer size +static size_t print_config_section(const struct section_config_t *section, char *buffer, size_t buffer_size) { + char *buf_ptr = buffer; + size_t buf_sz = buffer_size; + size_t sz; + + if (section->tls_enabled) { + print_cnf_buf("--tls=enabled"); + if (section->sni_domains != NULL) { + print_cnf_raw("--sni-domains="); + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + print_cnf_raw("%s,", sne->domain_name); + } + print_cnf_raw(" "); + } + if (section->exclude_sni_domains != NULL) { + print_cnf_raw("--exclude-domains="); + for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) { + print_cnf_raw("%s,", sne->domain_name); + } + print_cnf_raw(" "); + } + + switch(section->fragmentation_strategy) { + case FRAG_STRAT_IP: + print_cnf_buf("--frag=ip"); + break; + case FRAG_STRAT_TCP: + print_cnf_buf("--frag=tcp"); + break; + case FRAG_STRAT_NONE: + print_cnf_buf("--frag=none"); + break; + } + + print_cnf_buf("frag-sni-reverse=%d", section->frag_sni_reverse); + print_cnf_buf("frag-sni-faked=%d", section->frag_sni_faked); + print_cnf_buf("frag-middle-sni=%d", section->frag_middle_sni); + print_cnf_buf("frag-sni-pos=%d", section->frag_sni_pos); + print_cnf_buf("fk-winsize=%d", section->fk_winsize); + + if (section->fake_sni) { + print_cnf_buf("--fake-sni=1"); + print_cnf_buf("--fake-sni-seq-len=%d", section->fake_sni_seq_len); + switch(section->fake_sni_type) { + case FAKE_PAYLOAD_CUSTOM: + print_cnf_buf("--fake-sni-type=custom"); + print_cnf_buf("--fake-custom-payload="); + break; + case FAKE_PAYLOAD_RANDOM: + print_cnf_buf("--fake-sni-type=random"); + break; + case FAKE_PAYLOAD_DEFAULT: + print_cnf_buf("--fake-sni-type=default"); + break; + } + + switch(section->faking_strategy) { + case FAKE_STRAT_TTL: + print_cnf_buf("--faking-strategy=ttl"); + print_cnf_buf("--faking-ttl=%d", section->faking_ttl); + break; + case FAKE_STRAT_RAND_SEQ: + print_cnf_buf("--faking-strategy=randseq"); + break; + case FAKE_STRAT_TCP_CHECK: + print_cnf_buf("--faking-strategy=tcp_check"); + break; + case FAKE_STRAT_TCP_MD5SUM: + print_cnf_buf("--faking-strategy=md5sum"); + break; + case FAKE_STRAT_PAST_SEQ: + print_cnf_buf("--faking-strategy=pastseq"); + print_cnf_buf("--fake-seq-offset=%d", section->fakeseq_offset); + break; + + } + + switch(section->sni_detection) { + case SNI_DETECTION_BRUTE: + print_cnf_buf("--sni_detection=brute"); + break; + case SNI_DETECTION_PARSE: + print_cnf_buf("--sni_detection=parse"); + break; + + } + + print_cnf_buf("--seg2delay=%d", section->seg2_delay); + } + } else { + print_cnf_buf("--tls=disabled"); + } + + if (section->synfake) { + print_cnf_buf("--synfake=1"); + print_cnf_buf("--synfake-len=%d", section->synfake_len); + } else { + print_cnf_buf("--synfake=0"); + } + + + if (section->udp_filter_quic == UDP_FILTER_QUIC_ALL && section->udp_mode == UDP_MODE_DROP) { + print_cnf_buf("--drop-quic"); + } + + switch(section->udp_filter_quic) { + case UDP_FILTER_QUIC_ALL: + print_cnf_buf("--udp-filter-quic=all"); + break; + case UDP_FILTER_QUIC_DISABLED: + print_cnf_buf("--udp-filter-quic=disabled"); + break; + } + + if (section->udp_dport_range_len != 0) + print_cnf_raw("--udp-dport-filter="); + for (int i = 0; i < section->udp_dport_range_len; i++) { + struct udp_dport_range range = section->udp_dport_range[i]; + print_cnf_raw("%d-%d,", range.start, range.end); + } + print_cnf_raw(" "); + + + if (section->udp_filter_quic != UDP_FILTER_QUIC_DISABLED || section->udp_dport_range_len != 0) { + switch(section->udp_mode) { + case UDP_MODE_DROP: + print_cnf_buf("--udp-mode=drop"); + break; + case UDP_MODE_FAKE: + print_cnf_buf("--udp-mode=fake"); + print_cnf_buf("--udp-fake-seq-len=%d", section->udp_fake_seq_len); + { + switch(section->udp_faking_strategy) { + case FAKE_STRAT_UDP_CHECK: + print_cnf_buf("--udp-faking-strategy=checksum"); + break; + case FAKE_STRAT_TTL: + print_cnf_buf("--udp-faking-strategy=ttl"); + } + } + break; + } + } + + return buffer_size - buf_sz; +} +// Returns written buffer length +size_t print_config(char *buffer, size_t buffer_size) { + char *buf_ptr = buffer; + size_t buf_sz = buffer_size; + size_t sz; + +#ifndef KERNEL_SPACE + print_cnf_buf("--queue-num=%d", config.queue_start_num); + print_cnf_buf("--threads=%d", config.threads); +#endif + print_cnf_buf("--mark=%d", config.mark); + +#ifndef KERNEL_SPACE + if (config.daemonize) { + print_cnf_buf("--daemonize"); + } + if (config.syslog) { + print_cnf_buf("--syslog"); + } + if (config.noclose) { + print_cnf_buf("--noclose"); + } + if (!config.use_gso) { + print_cnf_buf("--no-gso"); + } +#endif + if (!config.use_ipv6) { + print_cnf_buf("--no-ipv6"); + } + if (config.verbose == VERBOSE_TRACE) { + print_cnf_buf("--trace"); + } + if (config.verbose == VERBOSE_INFO) { + print_cnf_buf("--silent"); + } + + size_t wbuf_len = print_config_section(config.first_section, buf_ptr, buf_sz); + buf_ptr += wbuf_len; + buf_sz -= wbuf_len; + + for (struct section_config_t *section = config.first_section->next; + section != NULL; section = section->next) { + print_cnf_buf("--fbegin"); + wbuf_len = print_config_section(section, buf_ptr, buf_sz); + buf_ptr += wbuf_len; + buf_sz -= wbuf_len; + print_cnf_buf("--fend"); + } + + return buffer_size - buf_sz; +} + void print_welcome(void) { + char welcome_message[4000]; + + size_t sz = print_config(welcome_message, 4000); + printf("Running with flags: %.*s\n", (int)sz, welcome_message); + return; +/** if (config.syslog) { printf("Logging to system log\n"); } @@ -762,5 +1085,73 @@ void print_welcome(void) { lginfo("Target sni domains: %s\n", section->domains_str); } } +*/ +} + +int init_section_config(struct section_config_t **section, struct section_config_t *prev) { + struct section_config_t *def_section = NULL; + int ret; +#ifdef KERNEL_SPACE + def_section = kmalloc(sizeof(struct section_config_t), GFP_KERNEL); +#else + def_section = malloc(sizeof(struct section_config_t)); +#endif + *def_section = (struct section_config_t)default_section_config; + def_section->prev = prev; + + if (def_section == NULL) + return -ENOMEM; + + ret = parse_sni_domains(&def_section->sni_domains, default_snistr, sizeof(default_snistr)); + if (ret < 0) { + free(def_section); + return ret; + } + + def_section->fake_sni_pkt = fake_sni_old; + def_section->fake_sni_pkt_sz = sizeof(fake_sni_old) - 1; + + *section = def_section; + return 0; } +int init_config(struct config_t *config) { + struct config_t def_config = default_config_set; + int ret = 0; + struct section_config_t *def_section = NULL; + ret = init_section_config(&def_section, NULL); + if (ret < 0) + return ret; + def_config.last_section = def_section; + def_config.first_section = def_section; + + *config = def_config; + + return 0; +} + +void free_config_section(struct section_config_t *section) { + lginfo("freeing %d\n", section->id); + if (section->udp_dport_range_len != 0) { + SFREE(section->udp_dport_range); + } + + free_sni_domains(section->sni_domains); + section->sni_domains = NULL; + free_sni_domains(section->exclude_sni_domains); + section->exclude_sni_domains = NULL; + + section->fake_custom_pkt_sz = 0; + SFREE(section->fake_custom_pkt); + + free(section); +} + +void free_config(struct config_t config) { + lginfo("freeing config\n"); + for (struct section_config_t *sct = config.last_section; sct != NULL;) { + struct section_config_t *psct = sct->prev; + free_config_section(sct); + sct = psct; + } +} diff --git a/args.h b/args.h index 102772f..8c4e88f 100644 --- a/args.h +++ b/args.h @@ -1,9 +1,21 @@ #ifndef ARGS_H #define ARGS_H +#include "types.h" +#include "config.h" void print_version(void); void print_usage(const char *argv0); -int parse_args(int argc, char *argv[]); +int yparse_args(int argc, char *argv[]); +size_t print_config(char *buffer, size_t buffer_size); + +// Initializes configuration storage. +int init_config(struct config_t *config); +// Allocates and initializes configuration section. +int init_section_config(struct section_config_t **section, struct section_config_t *prev); +// Frees configuration section +void free_config_section(struct section_config_t *config); +// Frees sections under config +void free_config(struct config_t config); /* Prints starting messages */ void print_welcome(void); diff --git a/config.h b/config.h index 3bebfdb..8adb331 100644 --- a/config.h +++ b/config.h @@ -5,7 +5,6 @@ #define USER_SPACE #endif -#include "raw_replacements.h" #include "types.h" typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len); @@ -26,9 +25,21 @@ struct udp_dport_range { uint16_t end; }; +struct domains_list { + char *domain_name; + uint16_t domain_len; + + struct domains_list *next; +}; + struct section_config_t { - const char *domains_str; - unsigned int domains_strlen; + int id; + struct section_config_t *next; + struct section_config_t *prev; + + struct domains_list *sni_domains; + struct domains_list *exclude_sni_domains; + unsigned int all_domains; int tls_enabled; @@ -53,14 +64,10 @@ struct section_config_t { int synfake; unsigned int synfake_len; - const char *exclude_domains_str; - unsigned int exclude_domains_strlen; - unsigned int all_domains; - const char *fake_sni_pkt; unsigned int fake_sni_pkt_sz; - const char *fake_custom_pkt; + char *fake_custom_pkt; unsigned int fake_custom_pkt_sz; unsigned int fk_winsize; @@ -98,17 +105,16 @@ struct config_t { #define VERBOSE_TRACE 2 int verbose; - struct section_config_t default_config; - struct section_config_t custom_configs[MAX_CONFIGLIST_LEN]; - int custom_configs_len; + struct section_config_t *first_section; + struct section_config_t *last_section; }; extern struct config_t config; -#define ITER_CONFIG_SECTIONS(section) \ -for (struct section_config_t *section = &config.default_config + config.custom_configs_len; section >= &config.default_config; section--) +#define ITER_CONFIG_SECTIONS(config, section) \ +for (struct section_config_t *section = (config)->last_section; section != NULL; section = section->prev) -#define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config) +#define CONFIG_SECTION_NUMBER(section) ((section)->id) #define MAX_THREADS 16 @@ -180,7 +186,7 @@ if ((fake_bitmask) & strategy) #define DEFAULT_SNISTR "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com" -static const char defaul_snistr[] = DEFAULT_SNISTR; +static const char default_snistr[] = DEFAULT_SNISTR; enum { UDP_MODE_DROP, @@ -193,6 +199,9 @@ enum { }; #define default_section_config { \ + .sni_domains = NULL, \ + .exclude_sni_domains = NULL, \ + .all_domains = 0, \ .tls_enabled = 1, \ .frag_sni_reverse = 1, \ .frag_sni_faked = 0, \ @@ -202,6 +211,8 @@ enum { .fake_sni = 1, \ .fake_sni_seq_len = 1, \ .fake_sni_type = FAKE_PAYLOAD_DEFAULT, \ + .fake_custom_pkt = NULL, \ + .fake_custom_pkt_sz = 0, \ .frag_middle_sni = 1, \ .frag_sni_pos = 1, \ .fakeseq_offset = 10000, \ @@ -210,16 +221,6 @@ enum { \ .seg2_delay = 0, \ \ - .domains_str = defaul_snistr, \ - .domains_strlen = sizeof(defaul_snistr), \ - \ - .exclude_domains_str = "", \ - .exclude_domains_strlen = 0, \ - \ - .fake_sni_pkt = fake_sni_old, \ - .fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \ - .fake_custom_pkt = custom_fake_buf, \ - .fake_custom_pkt_sz = 0, \ .sni_detection = SNI_DETECTION_PARSE, \ \ .udp_mode = UDP_MODE_FAKE, \ @@ -229,6 +230,32 @@ enum { .udp_dport_range = NULL, \ .udp_dport_range_len = 0, \ .udp_filter_quic = UDP_FILTER_QUIC_DISABLED, \ + \ + .prev = NULL, \ + .next = NULL, \ + .id = 0, \ +} + +#define default_config_set { \ + .threads = THREADS_NUM, \ + .queue_start_num = DEFAULT_QUEUE_NUM, \ + .mark = DEFAULT_RAWSOCKET_MARK, \ + .use_ipv6 = 1, \ + \ + .verbose = VERBOSE_DEBUG, \ + .use_gso = 1, \ + \ + .first_section = NULL, \ + .last_section = NULL, \ + \ + .daemonize = 0, \ + .noclose = 0, \ + .syslog = 0, \ } +#define CONFIG_SET(config) \ +struct config_t config = default_config_set; \ +config->last_section = &(config.default_config) \ + + #endif /* YTB_CONFIG_H */ diff --git a/getopt.c b/getopt.c new file mode 100644 index 0000000..48e76e1 --- /dev/null +++ b/getopt.c @@ -0,0 +1,204 @@ +#include "types.h" +#include "logging.h" +#include "getopt.h" + +char *optarg; +int optind=1, opterr=1, optopt, __optpos, optreset=0; + +#define optpos __optpos + +static void __getopt_msg(const char *b, const char *c, size_t l) +{ + lgerr("%s %.*s\n", b, (int)l, c); +} + +int getopt(int argc, char * const argv[], const char *optstring) +{ + int i, c, d; + int k, l; + char *optchar; + + if (!optind || optreset) { + optreset = 0; + __optpos = 0; + optind = 1; + } + + if (optind >= argc || !argv[optind]) + return -1; + + if (argv[optind][0] != '-') { + if (optstring[0] == '-') { + optarg = argv[optind++]; + return 1; + } + return -1; + } + + if (!argv[optind][1]) + return -1; + + if (argv[optind][1] == '-' && !argv[optind][2]) + return optind++, -1; + + if (!optpos) optpos++; + c = argv[optind][optpos], k = 1; + optchar = argv[optind]+optpos; + optopt = c; + optpos += k; + + if (!argv[optind][optpos]) { + optind++; + optpos = 0; + } + + if (optstring[0] == '-' || optstring[0] == '+') + optstring++; + + i = 0; + d = 0; + do { + d = optstring[i], l = 1; + if (l>0) i+=l; else i++; + } while (l && d != c); + + if (d != c) { + if (optstring[0] != ':' && opterr) + __getopt_msg("Unrecognized option: ", optchar, k); + return '?'; + } + if (optstring[i] == ':') { + if (optstring[i+1] == ':') optarg = 0; + else if (optind >= argc) { + if (optstring[0] == ':') return ':'; + if (opterr) __getopt_msg("Option requires an argument: ", + optchar, k); + return '?'; + } + if (optstring[i+1] != ':' || optpos) { + optarg = argv[optind++] + optpos; + optpos = 0; + } + } + return c; +} + +static void permute(char *const *argv, int dest, int src) +{ + char **av = (char **)argv; + char *tmp = av[src]; + int i; + for (i=src; i>dest; i--) + av[i] = av[i-1]; + av[dest] = tmp; +} + +static int __getopt_long_core(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) +{ + optarg = 0; + if (longopts && argv[optind][0] == '-' && + ((longonly && argv[optind][1] && argv[optind][1] != '-') || + (argv[optind][1] == '-' && argv[optind][2]))) + { + int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; + int i, cnt, match = -1; + char *opt; + for (cnt=i=0; longopts[i].name; i++) { + const char *name = longopts[i].name; + opt = argv[optind]+1; + if (*opt == '-') opt++; + for (; *name && *name == *opt; name++, opt++); + if (*opt && *opt != '=') continue; + match = i; + if (!*name) { + cnt = 1; + break; + } + cnt++; + } + if (cnt==1) { + i = match; + optind++; + optopt = longopts[i].val; + if (*opt == '=') { + if (!longopts[i].has_arg) { + if (colon || !opterr) + return '?'; + __getopt_msg( + "Option does not take an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optarg = opt+1; + } else if (longopts[i].has_arg == required_argument) { + if (!(optarg = argv[optind])) { + if (colon) return ':'; + if (!opterr) return '?'; + __getopt_msg( + "Option requires an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optind++; + } + if (idx) *idx = i; + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + return longopts[i].val; + } + if (argv[optind][1] == '-') { + if (!colon && opterr) + __getopt_msg(cnt ? + "Option is ambiguous: " : + "Unrecognized option: ", + argv[optind]+2, + strlen(argv[optind]+2)); + optind++; + return '?'; + } + } + return getopt(argc, argv, optstring); +} + +static int __getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx, int longonly) +{ + int ret, skipped, resumed; + if (!optind || optreset) { + optreset = 0; + __optpos = 0; + optind = 1; + } + if (optind >= argc || !argv[optind]) return -1; + skipped = optind; + if (optstring[0] != '+' && optstring[0] != '-') { + int i; + for (i=optind; ; i++) { + if (i >= argc || !argv[i]) return -1; + if (argv[i][0] == '-' && argv[i][1]) break; + } + optind = i; + } + resumed = optind; + ret = __getopt_long_core(argc, argv, optstring, longopts, idx, longonly); + if (resumed > skipped) { + int i, cnt = optind-resumed; + for (i=0; i #include "types.h" +#include "args.h" +#include "logging.h" -#define STR_MAXLEN 2048 +#define MAX_ARGC 1024 -static char custom_fake_buf[MAX_FAKE_SIZE]; +static int params_set(const char *cval, const struct kernel_param *kp) { + int ret = 0; -struct config_t config = { - .threads = THREADS_NUM, - .queue_start_num = DEFAULT_QUEUE_NUM, - .mark = DEFAULT_RAWSOCKET_MARK, - .use_ipv6 = 1, - - .verbose = VERBOSE_DEBUG, - .use_gso = 1, - - .default_config = default_section_config, - .custom_configs_len = 0 -}; - -#define def_section (&config.default_config) - -static int unumeric_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; - ret = kstrtoint(val, 10, &n); - if (ret != 0 || n < 0) - return -EINVAL; - - - return param_set_int(val, kp); -} - -static int boolean_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; - ret = kstrtoint(val, 10, &n); - if (ret != 0 || (n != 0 && n != 1)) - return -EINVAL; - - return param_set_int(val, kp); -} - -static int inverse_boolean_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; - ret = kstrtoint(val, 10, &n); - if (ret != 0 || (n != 0 && n != 1)) - return -EINVAL; - - n = !n; - if (kp->arg == NULL) - return -EINVAL; - - *(int *)kp->arg = n; - return 0; -} - -static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) { - if (*(int *)kp->arg == 0) { - buffer[0] = '1'; - } else { - buffer[0] = '0'; + int cv_len = strlen(cval); + if (cv_len >= 1 && cval[cv_len - 1] == '\n') { + cv_len--; } - buffer[1] = '\0'; - return strlen(buffer); -} -static const struct kernel_param_ops unumeric_parameter_ops = { - .set = unumeric_set, - .get = param_get_int -}; + const char *ytb_prefix = "youtubeUnblock "; + int ytbp_len = strlen(ytb_prefix); + int len = cv_len + ytbp_len; -static const struct kernel_param_ops boolean_parameter_ops = { - .set = boolean_set, - .get = param_get_int -}; + char *val = kmalloc(len + 1, GFP_KERNEL); // 1 for null-terminator + strncpy(val, ytb_prefix, ytbp_len); + strncpy(val + ytbp_len, cval, cv_len); + val[len] = '\0'; -static const struct kernel_param_ops inverse_boolean_ops = { - .set = inverse_boolean_set, - .get = inverse_boolean_get, -}; + int argc = 0; + char *argv[MAX_ARGC]; + argv[argc++] = val; + + for (int i = 0; i < len; i++) { + if (val[i] == ' ') { + val[i] = '\0'; -module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664); -module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &def_section->fake_sni_seq_len, 0664); -module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664); -module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664); -module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &def_section->frag_sni_reverse, 0664); -module_param_cb(frag_sni_faked, &boolean_parameter_ops, &def_section->frag_sni_faked, 0664); -module_param_cb(frag_middle_sni, &boolean_parameter_ops, &def_section->frag_middle_sni, 0664); -module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664); -module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664); -module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664); -module_param_cb(synfake_len, &unumeric_parameter_ops, &def_section->synfake_len, 0664); -module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664); -// module_param_cb(seg2delay, &unumeric_parameter_ops, &def_section->seg2_delay, 0664); - -static int sni_domains_set(const char *val, const struct kernel_param *kp) { - size_t len; - int ret; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - ret = param_set_charp(val, kp); - - if (ret < 0) { - def_section->domains_strlen = 0; - } else { - def_section->domains_strlen = len; - if (len == 3 && !strncmp(val, "all", len)) { - def_section->all_domains = 1; - } else { - def_section->all_domains = 0; + // safe because of null-terminator + if (val[i + 1] != ' ' && val[i + 1] != '\0') { + argv[argc++] = val + i + 1; + } } } - - return ret; -} - -static const struct kernel_param_ops sni_domains_ops = { - .set = sni_domains_set, - .get = param_get_charp, -}; - -module_param_cb(sni_domains, &sni_domains_ops, &def_section->domains_str, 0664); - -static int exclude_domains_set(const char *val, const struct kernel_param *kp) { - size_t len; - int ret; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - ret = param_set_charp(val, kp); - - if (ret < 0) { - def_section->exclude_domains_strlen = 0; - } else { - def_section->exclude_domains_strlen = len; + for (int i = 0; i < argc; i++) { + lginfo("%s %d\n", argv[i], strlen(argv[i])); } + ret = yparse_args(argc, argv); + kfree(val); return ret; } -static const struct kernel_param_ops exclude_domains_ops = { - .set = exclude_domains_set, - .get = param_get_charp, -}; - -module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664); - -module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664); - -static int quic_drop_set(const char *val, const struct kernel_param *kp) { - int n = 0, ret; - ret = kstrtoint(val, 10, &n); - if (ret != 0 || (n != 0 && n != 1)) - return -EINVAL; - - if (n) { - def_section->udp_mode = UDP_MODE_DROP; - def_section->udp_filter_quic = UDP_FILTER_QUIC_ALL; - } else { - def_section->udp_filter_quic = UDP_FILTER_QUIC_DISABLED; - } - - return 0; -} - -static int quic_drop_get(char *buffer, const struct kernel_param *kp) { - if (def_section->udp_mode == UDP_MODE_DROP && - def_section->udp_filter_quic == UDP_FILTER_QUIC_ALL) { - return sprintf(buffer, "%d\n", 1); - } else { - return sprintf(buffer, "%d\n", 0); - } -} - -static const struct kernel_param_ops quic_drop_ops = { - .set = quic_drop_set, - .get = quic_drop_get -}; - -module_param_cb(quic_drop, &quic_drop_ops, NULL, 0664); - -static int verbosity_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "trace", len) == 0) { - *(int *)kp->arg = VERBOSE_TRACE; - } else if (strncmp(val, "debug", len) == 0) { - *(int *)kp->arg = VERBOSE_DEBUG; - } else if (strncmp(val, "silent", len) == 0) { - *(int *)kp->arg = VERBOSE_INFO; - } else { - return -EINVAL; - } - - return 0; -} - - -static int verbosity_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case VERBOSE_TRACE: - strcpy(buffer, "trace\n"); - break; - case VERBOSE_DEBUG: - strcpy(buffer, "debug\n"); - break; - case VERBOSE_INFO: - strcpy(buffer, "silent\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops verbosity_ops = { - .set = verbosity_set, - .get = verbosity_get, -}; - -module_param_cb(verbosity, &verbosity_ops, &config.verbose, 0664); - -static int frag_strat_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "tcp", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_TCP; - } else if (strncmp(val, "ip", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_IP; - } else if (strncmp(val, "none", len) == 0) { - *(int *)kp->arg = FRAG_STRAT_NONE; - } else { - return -EINVAL; - } - - return 0; -} - -static int frag_strat_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case FRAG_STRAT_TCP: - strcpy(buffer, "tcp\n"); - break; - case FRAG_STRAT_IP: - strcpy(buffer, "ip\n"); - break; - case FRAG_STRAT_NONE: - strcpy(buffer, "none\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops frag_strat_ops = { - .set = frag_strat_set, - .get = frag_strat_get, -}; - -module_param_cb(fragmentation_strategy, &frag_strat_ops, &def_section->fragmentation_strategy, 0664); - -static int fake_strat_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "randseq", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_RAND_SEQ; - } else if (strncmp(val, "ttl", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TTL; - } else if (strncmp(val, "tcp_check", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TCP_CHECK; - } else if (strncmp(val, "pastseq", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_PAST_SEQ; - } else if (strncmp(val, "md5sum", len) == 0) { - *(int *)kp->arg = FAKE_STRAT_TCP_MD5SUM; - } else { - return -EINVAL; - } - - return 0; -} - -static int fake_strat_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case FAKE_STRAT_RAND_SEQ: - strcpy(buffer, "randseq\n"); - break; - case FAKE_STRAT_TTL: - strcpy(buffer, "ttl\n"); - break; - case FAKE_STRAT_TCP_CHECK: - strcpy(buffer, "tcp_check\n"); - break; - case FAKE_STRAT_PAST_SEQ: - strcpy(buffer, "pastseq\n"); - break; - case FAKE_STRAT_TCP_MD5SUM: - strcpy(buffer, "md5sum\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops fake_strat_ops = { - .set = fake_strat_set, - .get = fake_strat_get, -}; - -module_param_cb(faking_strategy, &fake_strat_ops, &def_section->faking_strategy, 0664); - -static int sni_detection_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "parse", len) == 0) { - *(int *)kp->arg = SNI_DETECTION_PARSE; - } else if (strncmp(val, "brute", len) == 0) { - *(int *)kp->arg = SNI_DETECTION_BRUTE; - } else { - return -EINVAL; - } - - return 0; -} - -static int sni_detection_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case SNI_DETECTION_PARSE: - strcpy(buffer, "parse\n"); - break; - case SNI_DETECTION_BRUTE: - strcpy(buffer, "brute\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops sni_detection_ops = { - .set = sni_detection_set, - .get = sni_detection_get, -}; - -module_param_cb(sni_detection, &sni_detection_ops, &def_section->sni_detection, 0664); - -static int fake_type_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - if (strncmp(val, "default", len) == 0) { - *(int *)kp->arg = FAKE_PAYLOAD_DEFAULT; - } else if (strncmp(val, "custom", len) == 0) { - *(int *)kp->arg = FAKE_PAYLOAD_CUSTOM; - } else if (strncmp(val, "random", len) == 0) { - *(int *)kp->arg = FAKE_PAYLOAD_RANDOM; - } else { - return -EINVAL; - } - - return 0; -} - -static int fake_type_get(char *buffer, const struct kernel_param *kp) { - switch (*(int *)kp->arg) { - case FAKE_PAYLOAD_DEFAULT: - strcpy(buffer, "default\n"); - break; - case FAKE_PAYLOAD_RANDOM: - strcpy(buffer, "random\n"); - break; - case FAKE_PAYLOAD_CUSTOM: - strcpy(buffer, "custom\n"); - break; - default: - strcpy(buffer, "unknown\n"); - } - - return strlen(buffer); -} - -static const struct kernel_param_ops fake_type_ops = { - .set = fake_type_set, - .get = fake_type_get, -}; - -module_param_cb(fake_sni_type, &fake_type_ops, &def_section->fake_sni_type, 0664); - -static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) { - size_t len; - - len = strnlen(val, STR_MAXLEN + 1); - if (len == STR_MAXLEN + 1) { - pr_err("%s: string parameter too long\n", kp->name); - return -ENOSPC; - } - - if (len >= 1 && val[len - 1] == '\n') { - len--; - } - - uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; - const char *custom_hex_fake = val; - size_t custom_hlen = len; - - if ((custom_hlen & 1) == 1) { - return -EINVAL; - } - - - size_t custom_len = custom_hlen >> 1; - if (custom_len > MAX_FAKE_SIZE) { - return -EINVAL; - } - - for (int i = 0; i < custom_len; i++) { - sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); - } - - def_section->fake_custom_pkt_sz = custom_len; - def_section->fake_custom_pkt = (char *)custom_buf; - - return 0; -} - -static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) { - int cflen = def_section->fake_custom_pkt_sz; - const uint8_t *cbf_data = def_section->fake_custom_pkt; - int bflen = def_section->fake_custom_pkt_sz << 1; - - for (int i = 0; i < cflen; i++) { - sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i)); - } - - return bflen; +static int params_get(char *buffer, const struct kernel_param *kp) { + size_t len = print_config(buffer, 4000); + return len; } -static const struct kernel_param_ops fake_custom_pl_ops = { - .set = fake_custom_pl_set, - .get = fake_custom_pl_get, +static const struct kernel_param_ops params_ops = { + .set = params_set, + .get = params_get, }; -module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &def_section->fake_custom_pkt, 0664); +module_param_cb(parameters, ¶ms_ops, NULL, 0664); + + +// +// static char custom_fake_buf[MAX_FAKE_SIZE]; +// +// struct config_t config = { +// .threads = THREADS_NUM, +// .queue_start_num = DEFAULT_QUEUE_NUM, +// .mark = DEFAULT_RAWSOCKET_MARK, +// .use_ipv6 = 1, +// +// .verbose = VERBOSE_DEBUG, +// .use_gso = 1, +// +// .default_config = default_section_config, +// .custom_configs_len = 0 +// }; +// +// #define def_section (&config.default_config) +// +// static int unumeric_set(const char *val, const struct kernel_param *kp) { +// int n = 0, ret; +// ret = kstrtoint(val, 10, &n); +// if (ret != 0 || n < 0) +// return -EINVAL; +// +// +// return param_set_int(val, kp); +// } +// +// static int boolean_set(const char *val, const struct kernel_param *kp) { +// int n = 0, ret; +// ret = kstrtoint(val, 10, &n); +// if (ret != 0 || (n != 0 && n != 1)) +// return -EINVAL; +// +// return param_set_int(val, kp); +// } +// +// static int inverse_boolean_set(const char *val, const struct kernel_param *kp) { +// int n = 0, ret; +// ret = kstrtoint(val, 10, &n); +// if (ret != 0 || (n != 0 && n != 1)) +// return -EINVAL; +// +// n = !n; +// if (kp->arg == NULL) +// return -EINVAL; +// +// *(int *)kp->arg = n; +// return 0; +// } +// +// static int inverse_boolean_get(char *buffer, const struct kernel_param *kp) { +// if (*(int *)kp->arg == 0) { +// buffer[0] = '1'; +// } else { +// buffer[0] = '0'; +// } +// buffer[1] = '\0'; +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops unumeric_parameter_ops = { +// .set = unumeric_set, +// .get = param_get_int +// }; +// +// static const struct kernel_param_ops boolean_parameter_ops = { +// .set = boolean_set, +// .get = param_get_int +// }; +// +// static const struct kernel_param_ops inverse_boolean_ops = { +// .set = inverse_boolean_set, +// .get = inverse_boolean_get, +// }; +// +// module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664); +// module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &def_section->fake_sni_seq_len, 0664); +// module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664); +// module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664); +// module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &def_section->frag_sni_reverse, 0664); +// module_param_cb(frag_sni_faked, &boolean_parameter_ops, &def_section->frag_sni_faked, 0664); +// module_param_cb(frag_middle_sni, &boolean_parameter_ops, &def_section->frag_middle_sni, 0664); +// module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664); +// module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664); +// module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664); +// module_param_cb(synfake_len, &unumeric_parameter_ops, &def_section->synfake_len, 0664); +// module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664); +// // module_param_cb(seg2delay, &unumeric_parameter_ops, &def_section->seg2_delay, 0664); +// +// static int sni_domains_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// int ret; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// ret = param_set_charp(val, kp); +// +// if (ret < 0) { +// def_section->domains_strlen = 0; +// } else { +// def_section->domains_strlen = len; +// if (len == 3 && !strncmp(val, "all", len)) { +// def_section->all_domains = 1; +// } else { +// def_section->all_domains = 0; +// } +// } +// +// +// return ret; +// } +// +// static const struct kernel_param_ops sni_domains_ops = { +// .set = sni_domains_set, +// .get = param_get_charp, +// }; +// +// module_param_cb(sni_domains, &sni_domains_ops, &def_section->domains_str, 0664); +// +// static int exclude_domains_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// int ret; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// ret = param_set_charp(val, kp); +// +// if (ret < 0) { +// def_section->exclude_domains_strlen = 0; +// } else { +// def_section->exclude_domains_strlen = len; +// } +// +// return ret; +// } +// +// static const struct kernel_param_ops exclude_domains_ops = { +// .set = exclude_domains_set, +// .get = param_get_charp, +// }; +// +// module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664); +// +// module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664); +// +// static int quic_drop_set(const char *val, const struct kernel_param *kp) { +// int n = 0, ret; +// ret = kstrtoint(val, 10, &n); +// if (ret != 0 || (n != 0 && n != 1)) +// return -EINVAL; +// +// if (n) { +// def_section->udp_mode = UDP_MODE_DROP; +// def_section->udp_filter_quic = UDP_FILTER_QUIC_ALL; +// } else { +// def_section->udp_filter_quic = UDP_FILTER_QUIC_DISABLED; +// } +// +// return 0; +// } +// +// static int quic_drop_get(char *buffer, const struct kernel_param *kp) { +// if (def_section->udp_mode == UDP_MODE_DROP && +// def_section->udp_filter_quic == UDP_FILTER_QUIC_ALL) { +// return sprintf(buffer, "%d\n", 1); +// } else { +// return sprintf(buffer, "%d\n", 0); +// } +// } +// +// static const struct kernel_param_ops quic_drop_ops = { +// .set = quic_drop_set, +// .get = quic_drop_get +// }; +// +// module_param_cb(quic_drop, &quic_drop_ops, NULL, 0664); +// +// static int verbosity_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "trace", len) == 0) { +// *(int *)kp->arg = VERBOSE_TRACE; +// } else if (strncmp(val, "debug", len) == 0) { +// *(int *)kp->arg = VERBOSE_DEBUG; +// } else if (strncmp(val, "silent", len) == 0) { +// *(int *)kp->arg = VERBOSE_INFO; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// +// static int verbosity_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case VERBOSE_TRACE: +// strcpy(buffer, "trace\n"); +// break; +// case VERBOSE_DEBUG: +// strcpy(buffer, "debug\n"); +// break; +// case VERBOSE_INFO: +// strcpy(buffer, "silent\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops verbosity_ops = { +// .set = verbosity_set, +// .get = verbosity_get, +// }; +// +// module_param_cb(verbosity, &verbosity_ops, &config.verbose, 0664); +// +// static int frag_strat_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "tcp", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_TCP; +// } else if (strncmp(val, "ip", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_IP; +// } else if (strncmp(val, "none", len) == 0) { +// *(int *)kp->arg = FRAG_STRAT_NONE; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int frag_strat_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case FRAG_STRAT_TCP: +// strcpy(buffer, "tcp\n"); +// break; +// case FRAG_STRAT_IP: +// strcpy(buffer, "ip\n"); +// break; +// case FRAG_STRAT_NONE: +// strcpy(buffer, "none\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops frag_strat_ops = { +// .set = frag_strat_set, +// .get = frag_strat_get, +// }; +// +// module_param_cb(fragmentation_strategy, &frag_strat_ops, &def_section->fragmentation_strategy, 0664); +// +// static int fake_strat_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "randseq", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_RAND_SEQ; +// } else if (strncmp(val, "ttl", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TTL; +// } else if (strncmp(val, "tcp_check", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TCP_CHECK; +// } else if (strncmp(val, "pastseq", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_PAST_SEQ; +// } else if (strncmp(val, "md5sum", len) == 0) { +// *(int *)kp->arg = FAKE_STRAT_TCP_MD5SUM; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int fake_strat_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case FAKE_STRAT_RAND_SEQ: +// strcpy(buffer, "randseq\n"); +// break; +// case FAKE_STRAT_TTL: +// strcpy(buffer, "ttl\n"); +// break; +// case FAKE_STRAT_TCP_CHECK: +// strcpy(buffer, "tcp_check\n"); +// break; +// case FAKE_STRAT_PAST_SEQ: +// strcpy(buffer, "pastseq\n"); +// break; +// case FAKE_STRAT_TCP_MD5SUM: +// strcpy(buffer, "md5sum\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops fake_strat_ops = { +// .set = fake_strat_set, +// .get = fake_strat_get, +// }; +// +// module_param_cb(faking_strategy, &fake_strat_ops, &def_section->faking_strategy, 0664); +// +// static int sni_detection_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "parse", len) == 0) { +// *(int *)kp->arg = SNI_DETECTION_PARSE; +// } else if (strncmp(val, "brute", len) == 0) { +// *(int *)kp->arg = SNI_DETECTION_BRUTE; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int sni_detection_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case SNI_DETECTION_PARSE: +// strcpy(buffer, "parse\n"); +// break; +// case SNI_DETECTION_BRUTE: +// strcpy(buffer, "brute\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops sni_detection_ops = { +// .set = sni_detection_set, +// .get = sni_detection_get, +// }; +// +// module_param_cb(sni_detection, &sni_detection_ops, &def_section->sni_detection, 0664); +// +// static int fake_type_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// if (strncmp(val, "default", len) == 0) { +// *(int *)kp->arg = FAKE_PAYLOAD_DEFAULT; +// } else if (strncmp(val, "custom", len) == 0) { +// *(int *)kp->arg = FAKE_PAYLOAD_CUSTOM; +// } else if (strncmp(val, "random", len) == 0) { +// *(int *)kp->arg = FAKE_PAYLOAD_RANDOM; +// } else { +// return -EINVAL; +// } +// +// return 0; +// } +// +// static int fake_type_get(char *buffer, const struct kernel_param *kp) { +// switch (*(int *)kp->arg) { +// case FAKE_PAYLOAD_DEFAULT: +// strcpy(buffer, "default\n"); +// break; +// case FAKE_PAYLOAD_RANDOM: +// strcpy(buffer, "random\n"); +// break; +// case FAKE_PAYLOAD_CUSTOM: +// strcpy(buffer, "custom\n"); +// break; +// default: +// strcpy(buffer, "unknown\n"); +// } +// +// return strlen(buffer); +// } +// +// static const struct kernel_param_ops fake_type_ops = { +// .set = fake_type_set, +// .get = fake_type_get, +// }; +// +// module_param_cb(fake_sni_type, &fake_type_ops, &def_section->fake_sni_type, 0664); +// +// static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) { +// size_t len; +// +// len = strnlen(val, STR_MAXLEN + 1); +// if (len == STR_MAXLEN + 1) { +// pr_err("%s: string parameter too long\n", kp->name); +// return -ENOSPC; +// } +// +// if (len >= 1 && val[len - 1] == '\n') { +// len--; +// } +// +// uint8_t *const custom_buf = (uint8_t *)custom_fake_buf; +// const char *custom_hex_fake = val; +// size_t custom_hlen = len; +// +// if ((custom_hlen & 1) == 1) { +// return -EINVAL; +// } +// +// +// size_t custom_len = custom_hlen >> 1; +// if (custom_len > MAX_FAKE_SIZE) { +// return -EINVAL; +// } +// +// for (int i = 0; i < custom_len; i++) { +// sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i); +// } +// +// def_section->fake_custom_pkt_sz = custom_len; +// def_section->fake_custom_pkt = (char *)custom_buf; +// +// return 0; +// } +// +// static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) { +// int cflen = def_section->fake_custom_pkt_sz; +// const uint8_t *cbf_data = def_section->fake_custom_pkt; +// int bflen = def_section->fake_custom_pkt_sz << 1; +// +// for (int i = 0; i < cflen; i++) { +// sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i)); +// } +// +// return bflen; +// } +// +// static const struct kernel_param_ops fake_custom_pl_ops = { +// .set = fake_custom_pl_set, +// .get = fake_custom_pl_get, +// }; +// +// module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &def_section->fake_custom_pkt, 0664); diff --git a/kytunblock.c b/kytunblock.c index d32cecf..b4c789d 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -20,6 +20,7 @@ #include "config.h" #include "utils.h" #include "logging.h" +#include "args.h" #if defined(PKG_VERSION) MODULE_VERSION(PKG_VERSION); @@ -206,9 +207,9 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) { int ipvx = netproto_version(pkt, pktlen); if (ipvx == IP4VERSION) { - return send_raw_ipv4(pkt, pktlen); + ret = send_raw_ipv4(pkt, pktlen); } else if (ipvx == IP6VERSION) { - return send_raw_ipv6(pkt, pktlen); + ret = send_raw_ipv6(pkt, pktlen); } else { printf("proto version %d is unsupported\n", ipvx); return -EINVAL; @@ -346,6 +347,8 @@ static struct nf_hook_ops ykb6_nf_reg __read_mostly = { static int __init ykb_init(void) { int ret = 0; + ret = init_config(&config); + if (ret < 0) goto err; ret = open_raw_socket(); if (ret < 0) goto err; @@ -420,6 +423,8 @@ static void __exit ykb_destroy(void) { #endif close_raw_socket(); + + free_config(config); lginfo("youtubeUnblock kernel module destroyed.\n"); } diff --git a/mangle.c b/mangle.c index 961ec98..a06a6c6 100644 --- a/mangle.c +++ b/mangle.c @@ -62,7 +62,7 @@ int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) { lgtrace_addp("UDP"); - ITER_CONFIG_SECTIONS(section) { + ITER_CONFIG_SECTIONS(&config, section) { lgtrace_addp("Section #%d", CONFIG_SECTION_NUMBER(section)); switch (transport_proto) { diff --git a/tls.c b/tls.c index b3f0e4f..16b43a9 100644 --- a/tls.c +++ b/tls.c @@ -9,6 +9,114 @@ #include #endif +static int bruteforce_analyze_sni_str( + const struct section_config_t *section, + const uint8_t *data, size_t dlen, + struct tls_verdict *vrd +) { + if (section->all_domains) { + vrd->target_sni = 1; + vrd->sni_len = 0; + vrd->sni_offset = dlen / 2; + return 0; + } + + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + const char *domain_startp = sne->domain_name; + int domain_len = sne->domain_len; + + if (sne->domain_len + dlen + 1 > MAX_PACKET_SIZE) { + continue; + } + + NETBUF_ALLOC(buf, MAX_PACKET_SIZE); + if (!NETBUF_CHECK(buf)) { + lgerror(-ENOMEM, "Allocation error"); + return -ENOMEM; + } + NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); + if (!NETBUF_CHECK(nzbuf)) { + lgerror(-ENOMEM, "Allocation error"); + NETBUF_FREE(buf); + return -ENOMEM; + } + + int *zbuf = (void *)nzbuf; + + memcpy(buf, domain_startp, domain_len); + memcpy(buf + domain_len, "#", 1); + memcpy(buf + domain_len + 1, data, dlen); + + z_function((char *)buf, zbuf, domain_len + 1 + dlen); + + for (unsigned int k = 0; k < dlen; k++) { + if (zbuf[k] == domain_len) { + vrd->target_sni = 1; + vrd->sni_len = domain_len; + vrd->sni_offset = (k - domain_len - 1); + vrd->sni_target_offset = vrd->sni_offset; + vrd->sni_target_len = vrd->sni_len; + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + return 0; + } + } + + + NETBUF_FREE(buf); + NETBUF_FREE(nzbuf); + } + + return 0; +} +static int analyze_sni_str( + const struct section_config_t *section, + const char *sni_name, int sni_len, const uint8_t *data, + struct tls_verdict *vrd +) { + if (section->all_domains) { + vrd->target_sni = 1; + goto check_domain; + } + + for (struct domains_list *sne = section->sni_domains; sne != NULL; sne = sne->next) { + const char *sni_startp = sni_name + sni_len - sne->domain_len; + const char *domain_startp = sne->domain_name; + + if (sni_len >= sne->domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + sne->domain_len)) { + vrd->target_sni = 1; + vrd->sni_target_offset = (const uint8_t *)sni_startp - data; + vrd->sni_target_len = sne->domain_len; + break; + } + } + +check_domain: + if (vrd->target_sni == 1) { + for (struct domains_list *sne = section->exclude_sni_domains; sne != NULL; sne = sne->next) { + const char *sni_startp = sni_name + sni_len - sne->domain_len; + const char *domain_startp = sne->domain_name; + + if (sni_len >= sne->domain_len && + sni_len < 128 && + !strncmp(sni_startp, + domain_startp, + sne->domain_len)) { + vrd->target_sni = 0; + lgdebugmsg("Excluded SNI: %.*s", + vrd->sni_len, data + vrd->sni_offset); + } + } + } + + return 0; +} + + #define TLS_CONTENT_TYPE_HANDSHAKE 0x16 #define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01 #define TLS_EXTENSION_SNI 0x0000 @@ -26,10 +134,16 @@ struct tls_verdict analyze_tls_data( uint32_t dlen) { struct tls_verdict vrd = {0}; + int ret; size_t i = 0; const uint8_t *data_end = data + dlen; + if (section->sni_detection == SNI_DETECTION_BRUTE) { + ret = bruteforce_analyze_sni_str(section, data, dlen, &vrd); + goto out; + } + while (i + 4 < dlen) { const uint8_t *msgData = data + i; @@ -44,10 +158,6 @@ struct tls_verdict analyze_tls_data( if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE) goto nextMessage; - if (section->sni_detection == SNI_DETECTION_BRUTE) { - goto brute; - } - const uint8_t *handshakeProto = msgData + 5; if (handshakeProto + 1 >= data_end) break; @@ -120,76 +230,14 @@ struct tls_verdict analyze_tls_data( if (sni_ext_ptr + sni_len > sni_ext_end) break; - char *sni_name = (char *)sni_ext_ptr; + const char *sni_name = (char *)sni_ext_ptr; vrd.sni_offset = (uint8_t *)sni_name - data; vrd.sni_target_offset = vrd.sni_offset; vrd.sni_len = sni_len; vrd.sni_target_len = vrd.sni_len; - if (section->all_domains) { - vrd.target_sni = 1; - goto check_domain; - } - - unsigned int j = 0; - for (unsigned int i = 0; i <= section->domains_strlen; i++) { - if ( i > j && - (i == section->domains_strlen || - section->domains_str[i] == '\0' || - section->domains_str[i] == ',' || - section->domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = section->domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - domain_len)) { - vrd.target_sni = 1; - vrd.sni_target_offset = (const uint8_t *)sni_startp - data; - vrd.sni_target_len = domain_len; - goto check_domain; - } - - j = i + 1; - } - } - -check_domain: - if (vrd.target_sni == 1 && section->exclude_domains_strlen != 0) { - unsigned int j = 0; - for (unsigned int i = 0; i <= section->exclude_domains_strlen; i++) { - if ( i > j && - (i == section->exclude_domains_strlen || - section->exclude_domains_str[i] == '\0' || - section->exclude_domains_str[i] == ',' || - section->exclude_domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *sni_startp = sni_name + sni_len - domain_len; - const char *domain_startp = section->exclude_domains_str + j; - - if (sni_len >= domain_len && - sni_len < 128 && - !strncmp(sni_startp, - domain_startp, - domain_len)) { - - vrd.target_sni = 0; - lgdebugmsg("Excluded SNI: %.*s", - vrd.sni_len, data + vrd.sni_offset); - goto out; - } - - j = i + 1; - } - } - } - + ret = analyze_sni_str(section, sni_name, sni_len, data, &vrd); goto out; nextExtension: @@ -201,73 +249,6 @@ struct tls_verdict analyze_tls_data( out: return vrd; - - -brute: - if (section->all_domains) { - vrd.target_sni = 1; - vrd.sni_len = 0; - vrd.sni_offset = dlen / 2; - goto out; - } - - unsigned int j = 0; - for (unsigned int i = 0; i <= section->domains_strlen; i++) { - if ( i > j && - (i == section->domains_strlen || - section->domains_str[i] == '\0' || - section->domains_str[i] == ',' || - section->domains_str[i] == '\n' )) { - - unsigned int domain_len = (i - j); - const char *domain_startp = section->domains_str + j; - - if (domain_len + dlen + 1> MAX_PACKET_SIZE) { - continue; - } - - NETBUF_ALLOC(buf, MAX_PACKET_SIZE); - if (!NETBUF_CHECK(buf)) { - lgerror(-ENOMEM, "Allocation error"); - goto out; - } - NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int)); - if (!NETBUF_CHECK(nzbuf)) { - lgerror(-ENOMEM, "Allocation error"); - NETBUF_FREE(buf); - goto out; - } - - int *zbuf = (void *)nzbuf; - - memcpy(buf, domain_startp, domain_len); - memcpy(buf + domain_len, "#", 1); - memcpy(buf + domain_len + 1, data, dlen); - - z_function((char *)buf, zbuf, domain_len + 1 + dlen); - - for (unsigned int k = 0; k < dlen; k++) { - if (zbuf[k] == domain_len) { - vrd.target_sni = 1; - vrd.sni_len = domain_len; - vrd.sni_offset = (k - domain_len - 1); - vrd.sni_target_offset = vrd.sni_offset; - vrd.sni_target_len = vrd.sni_len; - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - goto out; - } - } - - - j = i + 1; - - NETBUF_FREE(buf); - NETBUF_FREE(nzbuf); - } - } - - goto out; } int gen_fake_sni(struct fake_type type, diff --git a/types.h b/types.h index 32f88aa..e35733b 100644 --- a/types.h +++ b/types.h @@ -34,6 +34,9 @@ #include // IWYU pragma: export #include +#define free kfree +#define malloc(size) kmalloc((size), GFP_KERNEL) + #define ip6_hdr ipv6hdr /* from */ @@ -67,6 +70,11 @@ #include // IWYU pragma: export #endif +#define SFREE(item) do { \ +free((item)); \ +(item) = NULL; \ +} while (0) + #ifndef KERNEL_SPACE #define max(a,b)__extension__\ diff --git a/uspace.mk b/uspace.mk index 2506c97..0e618a0 100644 --- a/uspace.mk +++ b/uspace.mk @@ -31,7 +31,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF APP:=$(BUILD_DIR)/youtubeUnblock -SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c +SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c getopt.c OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la diff --git a/youtubeUnblock.c b/youtubeUnblock.c index fe67a25..0d7d662 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -612,7 +612,7 @@ struct instance_config_t instance_config = { int main(int argc, char *argv[]) { int ret; - if ((ret = parse_args(argc, argv)) != 0) { + if ((ret = yparse_args(argc, argv)) != 0) { if (ret < 0) { lgerror(-errno, "Unable to parse args"); exit(EXIT_FAILURE); From 42e6d574a030853f2075df3f1f847c0bfd120d37 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 8 Dec 2024 22:08:57 +0300 Subject: [PATCH 07/14] Add connbytes to kernel module --- README.md | 2 ++ args.c | 14 ++++++++++++++ config.h | 3 +++ kytunblock.c | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 51 insertions(+) diff --git a/README.md b/README.md index 9cf9836..0a5ed54 100644 --- a/README.md +++ b/README.md @@ -254,6 +254,8 @@ Available flags: - `--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. But if you really want multiple queue instances of youtubeUnblock, note that you should change --queue-num to --queue balance. For example, with 4 threads, use `--queue-balance 537:540` on iptables and `queue num 537-540` on nftables. +- `--connbytes-limit=` **Kernel module only!** Specify how much packets of connection should be processed by kyoutubeUnblock. Pass 0 if you want for each packet to be processed. This flag may be useful for UDP traffic since unlimited youtubeUnblock may lead to traffic flood and unexpected bans. Defaults to 5. In most cases you don't want to change it. + - `--daemonize` Daemonizes the youtubeUnblock (forks and detaches it from the shell). Terminate the program with `killall youtubeUnblock`. If you want to track the logs of youtubeUnblock in logread or journalctl, use **--syslog** flag. - `--syslog` Redirects logs to the system log. You can read it with `journalctl` or `logread`. diff --git a/args.c b/args.c index 89c0284..61cc43f 100644 --- a/args.c +++ b/args.c @@ -258,6 +258,7 @@ enum { OPT_CLS, OPT_HELP, OPT_VERSION, + OPT_CONNBYTES_LIMIT, }; static struct option long_opt[] = { @@ -300,6 +301,7 @@ static struct option long_opt[] = { {"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}, @@ -352,6 +354,7 @@ void print_usage(const char *argv0) { 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"); @@ -463,6 +466,13 @@ int yparse_args(int argc, char *argv[]) { 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: if (section_iter != SECT_ITER_DEFAULT && section_iter != SECT_ITER_OUTSIDE) goto invalid_opt; @@ -954,6 +964,10 @@ size_t print_config(char *buffer, size_t buffer_size) { 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"); } diff --git a/config.h b/config.h index 8adb331..5a1cd7a 100644 --- a/config.h +++ b/config.h @@ -100,6 +100,8 @@ struct config_t { int noclose; int syslog; + int connbytes_limit; + #define VERBOSE_INFO 0 #define VERBOSE_DEBUG 1 #define VERBOSE_TRACE 2 @@ -241,6 +243,7 @@ enum { .queue_start_num = DEFAULT_QUEUE_NUM, \ .mark = DEFAULT_RAWSOCKET_MARK, \ .use_ipv6 = 1, \ + .connbytes_limit = 8, \ \ .verbose = VERBOSE_DEBUG, \ .use_gso = 1, \ diff --git a/kytunblock.c b/kytunblock.c index b4c789d..7d079c5 100644 --- a/kytunblock.c +++ b/kytunblock.c @@ -16,6 +16,9 @@ #include #include +#include +#include + #include "mangle.h" #include "config.h" #include "utils.h" @@ -229,6 +232,32 @@ struct instance_config_t instance_config = { .send_delayed_packet = delay_packet_send, }; +static int connbytes_pkts(const struct sk_buff *skb) { + const struct nf_conn *ct; + enum ip_conntrack_info ctinfo; + u_int64_t pkts = 0; + const struct nf_conn_counter *counters; + + ct = nf_ct_get(skb, &ctinfo); + if (!ct) + return -1; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0) + const struct nf_conn_acct *acct; + acct = nf_conn_acct_find(ct); + if (!acct) + return -1; + counters = acct->counter; +#else + counters = nf_conn_acct_find(ct); + if (!counters) + return -1; +#endif + + pkts = atomic64_read(&counters[IP_CT_DIR_ORIGINAL].packets); + + return pkts; +} /* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */ #ifdef RHEL_RELEASE_CODE @@ -308,6 +337,9 @@ static NF_CALLBACK(ykb_nf_hook, skb) { if (skb->len > MAX_PACKET_SIZE) goto accept; + if (config.connbytes_limit != 0 && connbytes_pkts(skb) > config.connbytes_limit) + goto accept; + ret = skb_linearize(skb); if (ret < 0) { lgerror(ret, "Cannot linearize"); From 8b602a9f44f169f47bfed9594cdcf1e73933897a Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 8 Dec 2024 22:35:29 +0300 Subject: [PATCH 08/14] Fix GCC warnings --- args.c | 10 +++++++--- kargs.c | 4 ++-- mangle.c | 10 +++++----- quic.c | 7 +------ tls.c | 11 +++++------ 5 files changed, 20 insertions(+), 22 deletions(-) diff --git a/args.c b/args.c index 61cc43f..b4c0bfd 100644 --- a/args.c +++ b/args.c @@ -88,7 +88,6 @@ static long parse_numeric_option(const char* value) { } static int parse_udp_dport_range(char *str, struct udp_dport_range **udpr, int *udpr_len) { - int ret = 0; int seclen = 1; const char *p = str; while (*p != '\0') { @@ -762,10 +761,12 @@ int yparse_args(int argc, char *argv[]) { 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); @@ -995,10 +996,13 @@ size_t print_config(char *buffer, size_t buffer_size) { } void print_welcome(void) { - char welcome_message[4000]; - + 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); return; /** if (config.syslog) { diff --git a/kargs.c b/kargs.c index d9fca1f..72cc300 100644 --- a/kargs.c +++ b/kargs.c @@ -6,6 +6,7 @@ #include "logging.h" #define MAX_ARGC 1024 +static char *argv[MAX_ARGC]; static int params_set(const char *cval, const struct kernel_param *kp) { int ret = 0; @@ -25,7 +26,6 @@ static int params_set(const char *cval, const struct kernel_param *kp) { val[len] = '\0'; int argc = 0; - char *argv[MAX_ARGC]; argv[argc++] = val; for (int i = 0; i < len; i++) { @@ -40,7 +40,7 @@ static int params_set(const char *cval, const struct kernel_param *kp) { } for (int i = 0; i < argc; i++) { - lginfo("%s %d\n", argv[i], strlen(argv[i])); + lginfo("%s %lu\n", argv[i], strlen(argv[i])); } ret = yparse_args(argc, argv); diff --git a/mangle.c b/mangle.c index a06a6c6..d474f88 100644 --- a/mangle.c +++ b/mangle.c @@ -341,7 +341,6 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk const struct udphdr *udph; const uint8_t *data; uint32_t dlen; - int ipver = netproto_version(pkt, pktlen); int ret = udp_payload_split((uint8_t *)pkt, pktlen, (void **)&iph, &iph_len, @@ -414,7 +413,6 @@ int process_udp_packet(const struct section_config_t *section, const uint8_t *pk continue_flow: return PKT_CONTINUE; -accept_quic: accept: return PKT_ACCEPT; drop: @@ -452,6 +450,7 @@ int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet return -ENOMEM; } +/* NETBUF_ALLOC(fake_pad, MAX_PACKET_SIZE); if (!NETBUF_CHECK(fake_pad)) { lgerror(-ENOMEM, "Allocation error"); @@ -459,10 +458,11 @@ int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet NETBUF_FREE(frag2); return -ENOMEM; } +*/ uint32_t f1len = MAX_PACKET_SIZE; uint32_t f2len = MAX_PACKET_SIZE; - uint32_t fake_pad_len = MAX_PACKET_SIZE; + // uint32_t fake_pad_len = MAX_PACKET_SIZE; int ret; @@ -538,12 +538,12 @@ int send_ip4_frags(const struct section_config_t *section, const uint8_t *packet out_lc: NETBUF_FREE(frag1); NETBUF_FREE(frag2); - NETBUF_FREE(fake_pad); + // NETBUF_FREE(fake_pad); goto out; erret_lc: NETBUF_FREE(frag1); NETBUF_FREE(frag2); - NETBUF_FREE(fake_pad); + // NETBUF_FREE(fake_pad); return ret; } diff --git a/quic.c b/quic.c index b10bebd..46e7f91 100644 --- a/quic.c +++ b/quic.c @@ -191,8 +191,6 @@ int gen_fake_udp(struct udp_fake_type type, const struct udphdr *udph, uint8_t *buf, uint32_t *buflen) { uint32_t data_len = type.fake_len; - int ret; - if (!ipxh || !udph || !buf || !buflen) return -EINVAL; @@ -256,9 +254,6 @@ int detect_udp_filtered(const struct section_config_t *section, const uint8_t *data; uint32_t dlen; int ret; - int ipver; - - ipver = netproto_version(payload, plen); ret = udp_payload_split((uint8_t *)payload, plen, (void **)&iph, &iph_len, @@ -291,10 +286,10 @@ int detect_udp_filtered(const struct section_config_t *section, } lgtrace_addp("QUIC detected"); - uint8_t qtype = qch->type; goto approve; + // uint8_t qtype = qch->type; // if (qch->version == QUIC_V1) // qtype = quic_convtype_v1(qtype); // else if (qch->version == QUIC_V2) diff --git a/tls.c b/tls.c index 16b43a9..a32bb86 100644 --- a/tls.c +++ b/tls.c @@ -134,13 +134,12 @@ struct tls_verdict analyze_tls_data( uint32_t dlen) { struct tls_verdict vrd = {0}; - int ret; size_t i = 0; const uint8_t *data_end = data + dlen; if (section->sni_detection == SNI_DETECTION_BRUTE) { - ret = bruteforce_analyze_sni_str(section, data, dlen, &vrd); + bruteforce_analyze_sni_str(section, data, dlen, &vrd); goto out; } @@ -237,7 +236,7 @@ struct tls_verdict analyze_tls_data( vrd.sni_len = sni_len; vrd.sni_target_len = vrd.sni_len; - ret = analyze_sni_str(section, sni_name, sni_len, data, &vrd); + analyze_sni_str(section, sni_name, sni_len, data, &vrd); goto out; nextExtension: @@ -256,7 +255,6 @@ int gen_fake_sni(struct fake_type type, const struct tcphdr *tcph, uint32_t tcph_len, uint8_t *buf, uint32_t *buflen) { uint32_t data_len = type.fake_len; - int ret; if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) { data_len = (uint32_t)randint() % 1200; @@ -303,7 +301,8 @@ int gen_fake_sni(struct fake_type type, get_random_bytes(bfdptr, data_len); #else /* KERNEL_SPACE */ #if _NO_GETRANDOM - ret = open("/dev/urandom", O_RDONLY); + { + int ret = open("/dev/urandom", O_RDONLY); if (ret < 0) { lgerror(ret, "Unable to open /dev/urandom"); return ret; @@ -311,7 +310,7 @@ int gen_fake_sni(struct fake_type type, read(ret, bfdptr, data_len); close(ret); - + } #else /* _NO_GETRANDOM */ getrandom(bfdptr, data_len, 0); #endif /* _NO_GETRANDOM */ From f4e5cdb328c339d64f4c206b5d248f3308c1ae6b Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Sun, 8 Dec 2024 22:47:33 +0300 Subject: [PATCH 09/14] Update documentation for kernel module --- README.md | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0a5ed54..c76bbb6 100644 --- a/README.md +++ b/README.md @@ -358,24 +358,20 @@ When compilation is done, the binary file will be in build directory. Copy it to This section describes the kernel module version of youtubeUnblock. The kernel module operates as a normal module inside the kernel and integrates within the netfilter stack to statelessly mangle the packets sent over the Internet. -You can configure the module with its flags in insmod: -``` -insmod kyoutubeUnblock.ko fake_sni=1 exclude_domains=.ru quic_drop=1 -``` - -Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`quic_drop=1`) - -Also a good thig to mention is verbosity. The kernel module combines --trace and --silent option to the one parameter `verbosity`. This parameter accepts 3 arguments: `trace`, `debug` and `silent`. I highly don't recommend to enable `trace` mod on router because it may cause huge problems with performance and even freeze your device. +You can configure the module with its flags: -Also a drop in replacement is supported for all the parameters excluding packet mark. A drop in replacement does not require module restart if you want to change the parameters. You can specify and check the parameters within module's directory inside the sysfs: `/sys/module/kyoutubeUnblock/parameters/`. For example, to set quic_drop to true you may use next command: ```sh -echo 1 | sudo tee /sys/module/kyoutubeUnblock/parameters/quic_drop +insmod kyoutubeUnblock.ko +echo "--fake_sni=1 --exclude_domains=.ru --quic_drop" | sudo tee /sys/module/kyoutubeUnblock/parameters/parameters ``` -and + +You can also do + ```sh -cat /sys/module/kyoutubeUnblock/parameters/quic_drop +cat /sys/module/kyoutubeUnblock/parameters/parameters ``` -to check the parameter. + +and check all the parameters configured. ### Building kernel module From e8bdd05839661a1822e1cd0be4bf688b40d578f2 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 9 Dec 2024 01:28:16 +0300 Subject: [PATCH 10/14] Fix possible memory leak sni_domains wasn't freeed after default initialization --- args.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/args.c b/args.c index b4c0bfd..bdf262a 100644 --- a/args.c +++ b/args.c @@ -508,6 +508,7 @@ int yparse_args(int argc, char *argv[]) { 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; @@ -518,6 +519,7 @@ int yparse_args(int argc, char *argv[]) { 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; @@ -636,7 +638,7 @@ int yparse_args(int argc, char *argv[]) { break; case OPT_FAKE_CUSTOM_PAYLOAD: - SFREE(sect_config->udp_dport_range); + SFREE(sect_config->fake_custom_pkt); ret = parse_fake_custom_payload(optarg, §_config->fake_custom_pkt, §_config->fake_custom_pkt_sz); if (ret == -EINVAL) { From ba0f87d19522e4f3a8eaaa2e9926db4c924025eb Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 9 Dec 2024 01:34:31 +0300 Subject: [PATCH 11/14] Fix possible errors while building kernel module --- utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/utils.c b/utils.c index eb3f168..bfaf0ba 100644 --- a/utils.c +++ b/utils.c @@ -514,7 +514,9 @@ void z_function(const char *str, int *zbuf, size_t len) { for (int i = 1; i < (int)len; i++) { zbuf[i] = 0; if (i < rh) { - zbuf[i] = min(zbuf[i - lh], rh - i); + zbuf[i] = zbuf[i - lh]; + if (rh - i < zbuf[i]) + zbuf[i] = rh - i; } while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]]) From 1822983b34d5c08d4a3e6f7605c7db99c4729570 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Mon, 9 Dec 2024 01:45:14 +0300 Subject: [PATCH 12/14] Delete debug logs --- args.c | 3 --- kargs.c | 4 ---- 2 files changed, 7 deletions(-) diff --git a/args.c b/args.c index bdf262a..a71fa0f 100644 --- a/args.c +++ b/args.c @@ -61,7 +61,6 @@ static int parse_sni_domains(struct domains_list **dlist, const char *domains_st static void free_sni_domains(struct domains_list *dlist) { for (struct domains_list *ldl = dlist; ldl != NULL;) { struct domains_list *ndl = ldl->next; - printf("freeing domains\n"); SFREE(ldl->domain_name); SFREE(ldl); ldl = ndl; @@ -1151,7 +1150,6 @@ int init_config(struct config_t *config) { } void free_config_section(struct section_config_t *section) { - lginfo("freeing %d\n", section->id); if (section->udp_dport_range_len != 0) { SFREE(section->udp_dport_range); } @@ -1168,7 +1166,6 @@ void free_config_section(struct section_config_t *section) { } void free_config(struct config_t config) { - lginfo("freeing config\n"); for (struct section_config_t *sct = config.last_section; sct != NULL;) { struct section_config_t *psct = sct->prev; free_config_section(sct); diff --git a/kargs.c b/kargs.c index 72cc300..4b55553 100644 --- a/kargs.c +++ b/kargs.c @@ -39,10 +39,6 @@ static int params_set(const char *cval, const struct kernel_param *kp) { } } - for (int i = 0; i < argc; i++) { - lginfo("%s %lu\n", argv[i], strlen(argv[i])); - } - ret = yparse_args(argc, argv); kfree(val); return ret; From 25199288e841a4abecf59aea62ec6073d5c814b6 Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 18 Dec 2024 20:24:57 +0300 Subject: [PATCH 13/14] Cleanup args welcome --- args.c | 114 ++++----------------------------------------------------- 1 file changed, 8 insertions(+), 106 deletions(-) diff --git a/args.c b/args.c index a71fa0f..4205f05 100644 --- a/args.c +++ b/args.c @@ -908,13 +908,16 @@ static size_t print_config_section(const struct section_config_t *section, char break; } - if (section->udp_dport_range_len != 0) + 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); + 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(" "); + } - print_cnf_raw(" "); if (section->udp_filter_quic != UDP_FILTER_QUIC_DISABLED || section->udp_dport_range_len != 0) { @@ -1004,107 +1007,6 @@ void print_welcome(void) { size_t sz = print_config(welcome_message, 4000); printf("Running with flags: %.*s\n", (int)sz, welcome_message); free(welcome_message); - return; -/** - if (config.syslog) { - printf("Logging to system log\n"); - } - if (config.use_gso) { - lginfo("GSO is enabled\n"); - } - - if (config.use_ipv6) { - lginfo("IPv6 is enabled\n"); - } else { - lginfo("IPv6 is disabled\n"); - } - - lginfo("Detected %d config sections\n", config.custom_configs_len + 1); - lginfo("The sections will be processed in order they goes in this output\n"); - - ITER_CONFIG_SECTIONS(section) { - int section_number = CONFIG_SECTION_NUMBER(section); - lginfo("Section #%d\n", section_number); - - if (!section->tls_enabled) { - lginfo("TCP TLS is disabled for section!\n"); - } - switch (section->fragmentation_strategy) { - case FRAG_STRAT_TCP: - lginfo("Using TCP segmentation\n"); - break; - case FRAG_STRAT_IP: - lginfo("Using IP fragmentation\n"); - break; - default: - lginfo("SNI fragmentation is disabled\n"); - break; - } - - if (section->seg2_delay) { - lginfo("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", section->seg2_delay); - } - - if (section->fake_sni) { - lginfo("Fake SNI will be sent before each target client hello\n"); - } else { - lginfo("Fake SNI is disabled\n"); - } - - if (section->frag_sni_reverse) { - lginfo("Fragmentation Client Hello will be reversed\n"); - } - - if (section->frag_sni_faked) { - lginfo("Fooling packets will be sent near the original Client Hello\n"); - } - - if (section->fake_sni_seq_len > 1) { - lginfo("Faking sequence of length %d will be built as fake sni\n", section->fake_sni_seq_len); - } - - switch (section->faking_strategy) { - case FAKE_STRAT_TTL: - lginfo("TTL faking strategy will be used with TTL %d\n", section->faking_ttl); - break; - case FAKE_STRAT_RAND_SEQ: - lginfo("Random seq faking strategy will be used\n"); - lginfo("Fake seq offset set to %u\n", section->fakeseq_offset); - break; - case FAKE_STRAT_TCP_CHECK: - lginfo("TCP checksum faking strategy will be used\n"); - break; - case FAKE_STRAT_PAST_SEQ: - lginfo("Past seq faking strategy will be used\n"); - break; - case FAKE_STRAT_TCP_MD5SUM: - lginfo("md5sum faking strategy will be used\n"); - break; - } - - if (section->fk_winsize) { - lginfo("Response TCP window will be set to %d with the appropriate scale\n", section->fk_winsize); - } - - if (section->synfake) { - lginfo("Fake SYN payload will be sent with each TCP request SYN packet\n"); - } - - if (section->udp_filter_quic && section->udp_mode == UDP_MODE_DROP) { - lginfo("All QUIC packets will be dropped\n"); - } - - if (section->sni_detection == SNI_DETECTION_BRUTE) { - lginfo("Server Name Extension will be parsed in the bruteforce mode\n"); - } - - if (section->all_domains) { - lginfo("All Client Hello will be targeted by youtubeUnblock!\n"); - } else { - lginfo("Target sni domains: %s\n", section->domains_str); - } - } -*/ } int init_section_config(struct section_config_t **section, struct section_config_t *prev) { From 062200d9ea47d589dcf00add3d8fade33d6b85aa Mon Sep 17 00:00:00 2001 From: Vadim Vetrov Date: Wed, 18 Dec 2024 22:00:49 +0300 Subject: [PATCH 14/14] Allow to use fbegin without fend --- README.md | 2 +- args.c | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index c76bbb6..7a1ae50 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,7 @@ Available flags: - `--packet-mark=` Use this option if youtubeUnblock conflicts with other systems rely on packet mark. Note that you may want to change accept rule for iptables to follow the mark. -- `--fbegin` and `--fend` flags: youtubeUnblock supports multiple sets of strategies for specific filters. You may want to initiate a new set after the default one, like: `--sni-domains=googlevideo.com --faking-strategy=md5sum --fbegin --sni-domains=youtube.com --faking-strategy=tcp_check --fend --fbegin --sni-domains=l.google.com --faking-strategy=pastseq --fend`. Note, that the priority of these sets goes backwards: last is first, default (one that does not start with --fbegin) is last. If you start the new section, the default settings are implemented just like youtubeUnblock without any parameters. Note that the config above is just an example and won't work for you. +- `--fbegin` and `--fend` flags: youtubeUnblock supports multiple sets of strategies for specific filters. You may want to initiate a new set after the default one, like: `--sni-domains=googlevideo.com --faking-strategy=md5sum --fbegin --sni-domains=youtube.com --faking-strategy=tcp_check --fbegin --sni-domains=l.google.com --faking-strategy=pastseq`. Note, that the priority of these sets goes backwards: last is first, default (one that does not start with --fbegin) is last. If you start the new section, the default settings are implemented just like youtubeUnblock without any parameters. Note that the config above is just an example and won't work for you. ## Troubleshooting diff --git a/args.c b/args.c index 4205f05..38c6c70 100644 --- a/args.c +++ b/args.c @@ -471,10 +471,8 @@ int yparse_args(int argc, char *argv[]) { } rep_config.connbytes_limit = num; break; - case OPT_START_SECTION: - if (section_iter != SECT_ITER_DEFAULT && section_iter != SECT_ITER_OUTSIDE) - goto invalid_opt; - + case OPT_START_SECTION: + { struct section_config_t *nsect; ret = init_section_config(&nsect, rep_config.last_section); if (ret < 0) { @@ -487,6 +485,7 @@ int yparse_args(int argc, char *argv[]) { section_iter = SECT_ITER_INSIDE; break; + } case OPT_END_SECTION: if (section_iter != SECT_ITER_INSIDE) goto invalid_opt;