diff --git a/README.md b/README.md index 1585b39..e5a8211 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,17 @@ Also DNS over HTTPS (DOH) is preferred for additional anonimity. If you have any troubles with youtubeUnblock, here are some options to tune. If them don't work in your case, please, open an issue. You can pass these options in make CFLAGS (`make CFLAGS=...`) or edit CFLAGS variable in Makefile. Available flags: - -DUSE_SEG2_DELAY This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet. You can tune the amount of time in `#define SEG2_DELAY 100` where 100 stands for milliseconds. -- -DNO_FAKE_SNI This flag disables -DFAKE_SNI which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. Use this flag if you got gen_fake_sni error. Track this issue in https://github.com/Waujito/youtubeUnblock/issues/17 +- -DNO_FAKE_SNI This flag disables -DFAKE_SNI which forces youtubeUnblock to send at least three packets instead of one with TLS ClientHello: Fake ClientHello, 1st part of original ClientHello, 2nd part of original ClientHello. This flag may be related to some Operation not permitted error messages, so befor open an issue refer to FAQ for EPERMS. - -DNOUSE_GSO This flag disables fix for Google Chrome fat ClientHello. The GSO is well tested now, so this flag probably won't fix anything. +### FAQ for EPERMS (Operation not permitted) +EPERM may occur in a lot of places but generally here are two: mnl_cb_run and when sending the packet via rawsocket (raw_frags_send and send fake sni). +- mnl_cb_run Operation not permitted indicates that another instance of youtubeUnblock is running on the specified queue-num. +- rawsocket Operation not permitted indicates that the packet is being dropped by nefilter rules. In fact this is a hint from the kernel that something wrong is going on and we should check the firewall rules. Before dive into the problem let's make it clean how the mangled packets are being sent. Nefilter queue provides us with the ability to mangle the packet on fly but that is not suitable for this program because we need to split the packet to at least two independent packets. So we are using [linux raw sockets](https://man7.org/linux/man-pages/man7/raw.7.html) which allows us to send any ipv4 packet. **The packet goes from the OUTPUT chain even when NFQUEUE is set up on FORWARD (suitable for OpenWRT).** So we need to escape packet rejects here. + * raw_frags_send EPERM: just make sure outgoing traffic is allowed (RELATED,ESTABLISHED should work, if not, go to step 3) + * send fake sni EPERM: Fake SNI is out-of-state thing and will likely corrupt the connection (the behavior is expected). conntrack considers it as an invalid packet. By default OpenWRT set up to drop outgoing packets like this one. You may delete nftables/iptables rule that drops packets with invalid conntrack state, but I don't recommend to do this. The step 3 is better solution. + * Step 3, ultimate solution. Use mark (don't confuse with connmark). The youtubeUnblock uses mark internally to avoid infinity packet loops (when the packet is sent by youtubeUnblock but on next step handled by itself). Currently it uses mark (1 << 15) = 32768. You should put iptables/nftables that ultimately accepts such marks at the very start of the filter OUTPUT chain: `iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT` or `nft insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept`. + ## OpenWRT case The package is also compatible with routers. The router should be running by free opensource linux-based system such as [OpenWRT](https://openwrt.org/). You should cross-compile it under your host machine. Be ready for compilation errors and a lot of googling about it. It is not such a trivial process! You can get crosscompilation toolsuite compatible with your router from OpenWRT repositories. For example, I have ramips/mt76x8 based router so for me the toolsuite is on https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-toolchain-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64.tar.xz`. You can find out more about your router model on it's openwrt page. When you download the toolsuite, untar it somewhere. Now we are ready for compilation. My cross gcc asked me to create a staging dir for it and pass it as an environment variable. Also you should notice toolsuite packages and replace my make command with yours. ```STAGING_DIR=temp make CC=/usr/bin/mipsel-openwrt-linux-gcc LD=/usr/bin/mipsel-openwrt-linux-ld AR=/usr/bin/mipsel-openwrt-linux-ar OBJDUMP=/usr/bin/mipsel-openwrt-linux-objdump NM=/usr/bin/mipsel-openwrt-linux-nm STRIP=/usr/bin/mipsel-openwrt-linux-strip CROSS_COMPILE_PLATFORM=mipsel-buildroot-linux-gnu```. Take a look at `CROSS_COMPILE_PLATFORM` It is required by autotools but I think it is not necessary. Anyways I put `mipsel-buildroot-linux-gnu` in here. For your model may be an [automake cross-compile manual](https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html) will be helpful. When compilation is done, the binary file will be in build directory. Copy it to your router. Note that an ssh access is likely to be required to proceed. sshfs don't work on my model so I injected the application to the router via Software Upload Package page. It has given me an error, but also a `/tmp/upload.ipk` file which I copied in root directory, `chmod +x`-ed and run. diff --git a/youtubeUnblock.c b/youtubeUnblock.c index 6ef171c..88a4a91 100644 --- a/youtubeUnblock.c +++ b/youtubeUnblock.c @@ -31,7 +31,7 @@ #define USE_TCP_SEGMENTATION #endif -#define RAWSOCKET_MARK 0xfc70 +#define RAWSOCKET_MARK (1 << 15) #ifdef USE_SEG2_DELAY #define SEG2_DELAY 100 @@ -251,18 +251,27 @@ static int tcp4_frag(struct pkt_buff *pktb, size_t payload_offset, struct iphdr *hdr = nfq_ip_get_hdr(pktb); size_t hdr_len = hdr->ihl * 4; - if (hdr == NULL) {errno = EINVAL; return -1;} + if (hdr == NULL) { + perror("tcp4_frag: nfq_ip_get_hdr is null\n"); + errno = EINVAL; + return -1; + } if (hdr->protocol != IPPROTO_TCP || !(ntohs(hdr->frag_off) & IP_DF)) { + perror("tcp4_frag: proto is tcp or request is fragmented"); errno = EINVAL; return -1; } - if (nfq_ip_set_transport_header(pktb, hdr)) + if (nfq_ip_set_transport_header(pktb, hdr)) { + perror("tcp4_frag: ip_set_transport_header"); return -1; + } + struct tcphdr *tcph = nfq_tcp_get_hdr(pktb); size_t tcph_len = tcph->doff * 4; if (tcph == NULL) { + perror("tcp4_frag: tcph is NULL"); errno = EINVAL; return -1; } @@ -270,7 +279,8 @@ static int tcp4_frag(struct pkt_buff *pktb, size_t payload_offset, uint8_t *payload = nfq_tcp_get_payload(tcph, pktb); size_t plen = nfq_tcp_get_payload_len(tcph, pktb); - if (hdr == NULL || payload == NULL || plen <= payload_offset) { + if (payload == NULL || plen <= payload_offset) { + perror("tcp4_frag: payload is too small or NULL"); errno = EINVAL; return -1; } @@ -280,6 +290,10 @@ static int tcp4_frag(struct pkt_buff *pktb, size_t payload_offset, size_t s2_plen = plen - payload_offset; size_t s2_dlen = s2_plen + hdr_len + tcph_len; + if (s1_dlen > MNL_SOCKET_BUFFER_SIZE || s2_dlen > MNL_SOCKET_BUFFER_SIZE) { + errno = ENOMEM; + return -1; + } memcpy(buff1, hdr, hdr_len); memcpy(buff2, hdr, hdr_len); @@ -711,7 +725,7 @@ static int process_packet(const struct packet_data packet) { ret = send_raw_socket(fake_sni); if (ret < 0) { - perror("send fake sni\n"); + perror("send fake sni"); pktb_free(fake_sni); goto fallback; } @@ -859,7 +873,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) { if (attr[NFQA_MARK] != NULL) { // Skip packets sent by rawsocket to escape infinity loop. - if (ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) == + if ((ntohl(mnl_attr_get_u32(attr[NFQA_MARK])) & RAWSOCKET_MARK) == RAWSOCKET_MARK) { return fallback_accept_packet(packet.id); }