mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2026-01-27 20:50:35 +03:00
Compare commits
75 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a859472ef3 | ||
|
|
71a6711b40 | ||
|
|
78ed6a1d72 | ||
|
|
e8d86b9df6 | ||
|
|
c5e941a53b | ||
|
|
551fb5d38d | ||
|
|
b434ef4b7f | ||
|
|
6cf2ec5504 | ||
|
|
a546e783c6 | ||
|
|
1c5d4e68d9 | ||
|
|
fa0552ba66 | ||
|
|
51c21a89fd | ||
|
|
af6e993c07 | ||
|
|
044801efb9 | ||
|
|
7f340fb033 | ||
|
|
727e909db1 | ||
|
|
460f392a91 | ||
|
|
b76cc5fcee | ||
|
|
b68052efa2 | ||
|
|
9af85abc84 | ||
|
|
59d19646a6 | ||
|
|
bcdf1810c4 | ||
|
|
0aef6a991b | ||
|
|
f3db464b97 | ||
|
|
e8519b4973 | ||
|
|
dfc2277efc | ||
|
|
0160c26618 | ||
|
|
4a8f0d18a9 | ||
|
|
d5db8c18e5 | ||
|
|
e6367c72bd | ||
|
|
aafa1a728a | ||
|
|
219062aae2 | ||
|
|
24826f851d | ||
|
|
918ccc822d | ||
|
|
e649ef5567 | ||
|
|
b2cff9699b | ||
|
|
569cfcf049 | ||
|
|
86bada6ea7 | ||
|
|
e39bc9b059 | ||
|
|
7d571e6860 | ||
|
|
05648cc7c2 | ||
|
|
8ca048d9fd | ||
|
|
fa631accb7 | ||
|
|
b4607d69f6 | ||
|
|
9c2d31f51d | ||
|
|
74a9ae3eb1 | ||
|
|
6951c0319e | ||
|
|
6df3b53d7a | ||
|
|
e719c1319b | ||
|
|
2e96aa150e | ||
|
|
d29177d783 | ||
|
|
0126e403fd | ||
|
|
80811a41e6 | ||
|
|
c39e7d81c9 | ||
|
|
b6a8a45502 | ||
|
|
21be10caed | ||
|
|
d83e54701e | ||
|
|
75b7fe3011 | ||
|
|
81fcdf41c0 | ||
|
|
ab402c573b | ||
|
|
ff5bfc9037 | ||
|
|
98e1c2f5d6 | ||
|
|
da10542edc | ||
|
|
dc03bee64b | ||
|
|
34345b127b | ||
|
|
48d8f08957 | ||
|
|
cee1b371fd | ||
|
|
8e592d8957 | ||
|
|
6bbeae3876 | ||
|
|
7b9e7b773b | ||
|
|
8d4fb1f7ad | ||
|
|
4963258c0b | ||
|
|
46e4231f7e | ||
|
|
916d575920 | ||
|
|
4f9ab69b37 |
@@ -4,3 +4,7 @@ root = true
|
|||||||
indent_style = tab
|
indent_style = tab
|
||||||
indent_size = 8
|
indent_size = 8
|
||||||
tab_width = 8
|
tab_width = 8
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|||||||
282
.github/workflows/build-ci.yml
vendored
Normal file
282
.github/workflows/build-ci.yml
vendored
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '.editorconfig'
|
||||||
|
- '.gitignore'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'README.md'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prepare:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.gh.outputs.version }}
|
||||||
|
sha: ${{ steps.gh.outputs.sha }}
|
||||||
|
steps:
|
||||||
|
- name: GH
|
||||||
|
id: gh
|
||||||
|
env:
|
||||||
|
REPO: ${{ github.repository }}
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
echo "version=$(gh api repos/$REPO/releases/latest --jq '.tag_name' | sed 's/v//')" >> $GITHUB_OUTPUT
|
||||||
|
if [[ "${{ github.event_name }}" != "pull_request" ]]; then
|
||||||
|
echo "sha=$(echo ${GITHUB_SHA::7})" >> $GITHUB_OUTPUT
|
||||||
|
else
|
||||||
|
echo "sha=$(gh api repos/$REPO/commits/main --jq '.sha[:7]')" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
build-static:
|
||||||
|
needs: prepare
|
||||||
|
name: build ${{ matrix.arch }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# arch: [x86_64, x86, aarch64, armhf, armv7, ppc64le, s390x]
|
||||||
|
arch: [x86_64, x86, aarch64, armhf]
|
||||||
|
branch: [latest-stable]
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up ccache
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: ${{ github.workspace }}/.ccache
|
||||||
|
key: ccache-${{ matrix.arch }}-${{ github.run_id }}
|
||||||
|
restore-keys: ccache-${{ matrix.arch }}-
|
||||||
|
|
||||||
|
- name: Set up Alpine Linux for ${{ matrix.arch }}
|
||||||
|
uses: jirutka/setup-alpine@v1
|
||||||
|
with:
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
|
branch: ${{ matrix.branch }}
|
||||||
|
packages: >
|
||||||
|
bash build-base ccache coreutils findutils gawk git grep tar wget xz
|
||||||
|
autoconf automake libtool pkgconf linux-headers
|
||||||
|
shell-name: alpine.sh
|
||||||
|
|
||||||
|
- name: Build inside chroot
|
||||||
|
id: build
|
||||||
|
env:
|
||||||
|
ARCH: ${{ matrix.arch }}
|
||||||
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||||
|
VERSION: ${{ needs.prepare.outputs.version }}
|
||||||
|
SHA: ${{ needs.prepare.outputs.sha }}
|
||||||
|
shell: alpine.sh {0}
|
||||||
|
run: |
|
||||||
|
case $ARCH in
|
||||||
|
x86_64) PLATFORM=x86_64 ;;
|
||||||
|
x86) PLATFORM=x86 ;;
|
||||||
|
aarch64) PLATFORM=arm64 ;;
|
||||||
|
armhf) PLATFORM=arm ;;
|
||||||
|
*) PLATFORM=$ARCH ;;
|
||||||
|
esac
|
||||||
|
make -j$(nproc) CC="ccache gcc -static-libgcc -static" || exit 1
|
||||||
|
strip -s build/youtubeUnblock
|
||||||
|
rm -rf youtubeUnblock || true
|
||||||
|
mkdir youtubeUnblock
|
||||||
|
cp build/youtubeUnblock youtubeUnblock
|
||||||
|
cp youtubeUnblock.service youtubeUnblock
|
||||||
|
cp README.md youtubeUnblock
|
||||||
|
tar -czvf youtubeUnblock-$VERSION-$SHA-$PLATFORM-static.tar.gz youtubeUnblock
|
||||||
|
ccache --show-stats
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
if: steps.build.outcome == 'success'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: static-${{ matrix.arch }}
|
||||||
|
path: ./**/youtubeUnblock*.tar.gz
|
||||||
|
|
||||||
|
build-openwrt:
|
||||||
|
needs: prepare
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
branch:
|
||||||
|
- openwrt-23.05
|
||||||
|
arch:
|
||||||
|
- aarch64_cortex-a53
|
||||||
|
- aarch64_cortex-a72
|
||||||
|
- aarch64_generic
|
||||||
|
- arm_arm1176jzf-s_vfp
|
||||||
|
- arm_arm926ej-s
|
||||||
|
- arm_cortex-a15_neon-vfpv4
|
||||||
|
- arm_cortex-a5_vfpv4
|
||||||
|
- arm_cortex-a7
|
||||||
|
- arm_cortex-a7_neon-vfpv4
|
||||||
|
- arm_cortex-a7_vfpv4
|
||||||
|
- arm_cortex-a8_vfpv3
|
||||||
|
- arm_cortex-a9
|
||||||
|
- arm_cortex-a9_neon
|
||||||
|
- arm_cortex-a9_vfpv3-d16
|
||||||
|
- arm_fa526
|
||||||
|
- arm_mpcore
|
||||||
|
- arm_xscale
|
||||||
|
- mips64_octeonplus
|
||||||
|
- mips_24kc
|
||||||
|
- mips_4kec
|
||||||
|
- mips_mips32
|
||||||
|
- mipsel_24kc
|
||||||
|
- mipsel_24kc_24kf
|
||||||
|
- mipsel_74kc
|
||||||
|
- mipsel_mips32
|
||||||
|
- x86_64
|
||||||
|
container:
|
||||||
|
image: openwrt/sdk:${{ matrix.arch }}-${{ matrix.branch }}
|
||||||
|
options: --user root
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: 'openwrt'
|
||||||
|
|
||||||
|
- name: Prepare build
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.prepare.outputs.version }}
|
||||||
|
SHA: ${{ needs.prepare.outputs.sha }}
|
||||||
|
run: |
|
||||||
|
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" youtubeUnblock/Makefile
|
||||||
|
|
||||||
|
- name: Build packages
|
||||||
|
id: build
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.prepare.outputs.version }}
|
||||||
|
SHA: ${{ needs.prepare.outputs.sha }}
|
||||||
|
working-directory: /builder
|
||||||
|
run: |
|
||||||
|
echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf
|
||||||
|
cat feeds.conf
|
||||||
|
./scripts/feeds update youtubeUnblock
|
||||||
|
./scripts/feeds install -a -p youtubeUnblock
|
||||||
|
make defconfig
|
||||||
|
make package/youtubeUnblock/compile V=s
|
||||||
|
mv $(find ./bin -type f -name 'youtubeUnblock*.ipk') ./youtubeUnblock-$VERSION-$SHA-${{ matrix.arch }}-${{ matrix.branch }}.ipk
|
||||||
|
|
||||||
|
- name: Upload packages
|
||||||
|
if: steps.build.outcome == 'success'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.branch }}-${{ matrix.arch }}
|
||||||
|
path: /builder/youtubeUnblock*.ipk
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
build-entware:
|
||||||
|
needs: prepare
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- aarch64-3.10
|
||||||
|
- armv7-3.2
|
||||||
|
- mips-3.4
|
||||||
|
- mipsel-3.4
|
||||||
|
- x64-3.2
|
||||||
|
steps:
|
||||||
|
- name: Set up Entware docker container
|
||||||
|
run: |
|
||||||
|
git clone --depth 1 https://github.com/Entware/docker.git
|
||||||
|
docker build docker --pull --tag builder
|
||||||
|
docker volume create entware-home
|
||||||
|
|
||||||
|
- name: Restore Entware from cache
|
||||||
|
id: cache-restore
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ~/entware
|
||||||
|
key: entware-${{ matrix.arch }}
|
||||||
|
|
||||||
|
- name: Load Entware from cache
|
||||||
|
if: steps.cache-restore.outputs.cache-hit == 'true'
|
||||||
|
run: |
|
||||||
|
docker run --rm --mount source=entware-home,target=/backup_vol -v ~/entware:/backup ubuntu tar -xf /backup/entware.tar -C /backup_vol
|
||||||
|
docker run --rm --mount source=entware-home,target=/home/me -w /home/me ubuntu bash -c 'cp -r ./backup_vol/* ./'
|
||||||
|
docker run --rm --mount source=entware-home,target=/home/me -w /home/me ubuntu bash -c 'chown -R 1000:1000 ./* ./'
|
||||||
|
|
||||||
|
- name: Build Entware
|
||||||
|
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
docker run --rm -i --mount source=entware-home,target=/home/me -w /home/me --name builder builder git clone --depth 1 https://github.com/Entware/Entware.git
|
||||||
|
docker run --rm -i --mount source=entware-home,target=/home/me -w /home/me/Entware --name builder builder make package/symlinks
|
||||||
|
docker run --rm -i --mount source=entware-home,target=/home/me -w /home/me/Entware --name builder builder cp -v configs/${{ matrix.arch }}.config .config
|
||||||
|
docker run --rm -i --mount source=entware-home,target=/home/me -w /home/me/Entware --name builder builder make -j$(nproc) toolchain/install
|
||||||
|
docker run --rm --mount source=entware-home,target=/backup_vol -v ~/entware:/backup ubuntu tar -cf /backup/entware.tar /backup_vol
|
||||||
|
|
||||||
|
- name: Save Entware to cache
|
||||||
|
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||||
|
id: cache-save
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ~/entware
|
||||||
|
key: entware-${{ matrix.arch }}
|
||||||
|
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: 'openwrt'
|
||||||
|
|
||||||
|
- name: Prepare build
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.prepare.outputs.version }}
|
||||||
|
SHA: ${{ needs.prepare.outputs.sha }}
|
||||||
|
run: |
|
||||||
|
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" youtubeUnblock/Makefile
|
||||||
|
|
||||||
|
- name: Build packages
|
||||||
|
id: build
|
||||||
|
run: |
|
||||||
|
echo "src-link youtubeUnblock /youtubeUnblock" | docker run --rm -i --mount source=entware-home,target=/home/me -v $GITHUB_WORKSPACE:/youtubeUnblock -w /home/me/Entware --name builder builder tee -a feeds.conf
|
||||||
|
docker run --rm -i --mount source=entware-home,target=/home/me -v $GITHUB_WORKSPACE:/youtubeUnblock -w /home/me/Entware --name builder builder ./scripts/feeds update youtubeUnblock
|
||||||
|
docker run --rm -i --mount source=entware-home,target=/home/me -v $GITHUB_WORKSPACE:/youtubeUnblock -w /home/me/Entware --name builder builder ./scripts/feeds install -a -p youtubeUnblock
|
||||||
|
echo "CONFIG_PACKAGE_youtubeUnblock=m" | docker run --rm -i --mount source=entware-home,target=/home/me -v $GITHUB_WORKSPACE:/youtubeUnblock -w /home/me/Entware --name builder builder tee -a .config
|
||||||
|
docker run --rm -i --mount source=entware-home,target=/home/me -v $GITHUB_WORKSPACE:/youtubeUnblock -w /home/me/Entware --name builder builder make package/youtubeUnblock/compile V=s
|
||||||
|
|
||||||
|
- name: Extract packages
|
||||||
|
if: steps.build.outcome == 'success'
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.prepare.outputs.version }}
|
||||||
|
SHA: ${{ needs.prepare.outputs.sha }}
|
||||||
|
run: |
|
||||||
|
mkdir output
|
||||||
|
docker run --rm --user root -i --mount source=entware-home,target=/home/me -v $(pwd):/target -w /home/me/Entware --name builder builder find ./bin -type f -name 'youtubeUnblock*.ipk' -exec cp -v {} /target/output \;
|
||||||
|
rm -rf youtubeUnblock || true
|
||||||
|
mkdir youtubeUnblock
|
||||||
|
bash -c "cp -r ./output/* youtubeUnblock"
|
||||||
|
tar -czvf youtubeUnblock-$VERSION-$SHA-${{ matrix.arch }}-entware.tar.gz youtubeUnblock
|
||||||
|
|
||||||
|
- name: Upload packages
|
||||||
|
if: steps.build.outcome == 'success'
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: entware-${{ matrix.arch }}
|
||||||
|
path: ./**/youtubeUnblock*-entware.tar.gz
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
|
pre-release:
|
||||||
|
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
||||||
|
needs: [build-static, build-openwrt, build-entware]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Download artifacts
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
|
||||||
|
- name: Upload assets
|
||||||
|
uses: slord399/action-automatic-releases@v1.0.1
|
||||||
|
with:
|
||||||
|
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
automatic_release_tag: 'continuous'
|
||||||
|
prerelease: true
|
||||||
|
title: 'Development build'
|
||||||
|
files: |
|
||||||
|
./**/youtubeUnblock*.ipk
|
||||||
|
./**/youtubeUnblock*.tar.gz
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,3 +13,5 @@ modules.order
|
|||||||
Module.symvers
|
Module.symvers
|
||||||
*.so
|
*.so
|
||||||
*.ko
|
*.ko
|
||||||
|
|
||||||
|
!/.github
|
||||||
|
|||||||
9
Makefile
9
Makefile
@@ -9,6 +9,11 @@ $(KMAKE_TARGETS):
|
|||||||
@$(MAKE) -f kmake.mk $@
|
@$(MAKE) -f kmake.mk $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-@$(MAKE) -f kmake.mk kclean
|
-@$(MAKE) -f uspace.mk clean
|
||||||
@$(MAKE) -f uspace.mk clean
|
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
|
-@$(MAKE) -f uspace.mk distclean
|
||||||
|
|
||||||
|
|
||||||
|
kclean:
|
||||||
|
-@$(MAKE) -f kmake.mk kclean
|
||||||
|
|||||||
286
README.md
286
README.md
@@ -1,90 +1,280 @@
|
|||||||
|
- [youtubeUnblock](#youtubeunblock)
|
||||||
|
- [Configuration](#configuration)
|
||||||
|
- [OpenWRT pre configuration](#openwrt-pre-configuration)
|
||||||
|
- [Entware](#entware)
|
||||||
|
- [PC configuration](#pc-configuration)
|
||||||
|
- [Firewall configuration](#firewall-configuration)
|
||||||
|
- [nftables rules](#nftables-rules)
|
||||||
|
- [Iptables rules](#iptables-rules)
|
||||||
|
- [Check it](#check-it)
|
||||||
|
- [Flags](#flags)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
|
- [TV](#tv)
|
||||||
|
- [Troubleshooting EPERMS (Operation not permitted)](#troubleshooting-eperms-operation-not-permitted)
|
||||||
|
- [How it works:](#how-it-works)
|
||||||
|
- [How it processes packets](#how-it-processes-packets)
|
||||||
|
- [Compilation](#compilation)
|
||||||
|
- [OpenWRT case](#openwrt-case)
|
||||||
|
- [Building OpenWRT .ipk package](#building-openwrt-ipk-package)
|
||||||
|
- [Building with toolchain](#building-with-toolchain)
|
||||||
|
|
||||||
|
|
||||||
# youtubeUnblock
|
# youtubeUnblock
|
||||||
Bypasses Googlevideo detection systems that relies on SNI. The package is for Linux only. The package is fully compatible with routers running OpenWRT. To learn how to build the package on OpenWRT, consult [this chapter](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#openwrt-case).
|
|
||||||
|
Bypasses Deep Packet Inspection (DPI) systems that relies on SNI. The package is for Linux only. It is also fully compatible with routers running [OpenWRT](https://github.com/openwrt).
|
||||||
|
|
||||||
|
The program was primarily developed to bypass YouTube Outage in Russia, but it works good with other websites blocked by SNI. Adjust the list of websites via `--sni-domains` flag for the program.
|
||||||
|
|
||||||
|
The program is compatible with routers based on OpenWRT, Entware(Keenetic/ASUS) and host machines. The program offers binaries via Github Actions. The binaries of main branch are published in the [development pre-release](https://github.com/Waujito/youtubeUnblock/releases/tag/continuous). Check out [Github Actions](https://github.com/Waujito/youtubeUnblock/actions/workflows/build-ci.yml) if you want to see all the binaries compiled ever. You should know the arcitecture of your hardware to use binaries. On OpenWRT you can check it with command `grep ARCH /etc/openwrt_release`.
|
||||||
|
|
||||||
|
On both OpenWRT and Entware install the program with opkg. If you got read-only filesystem error you may unpack the binary manually or specify opkg path `opkg -o <destdir>`.
|
||||||
|
|
||||||
For Windows use [GoodbyeDPI from ValdikSS](https://github.com/ValdikSS/GoodbyeDPI) (you can find how to use it for YouTube [here](https://github.com/ValdikSS/GoodbyeDPI/issues/378)) The same behavior is also implemented in [zapret package for linux](https://github.com/bol-van/zapret).
|
For Windows use [GoodbyeDPI from ValdikSS](https://github.com/ValdikSS/GoodbyeDPI) (you can find how to use it for YouTube [here](https://github.com/ValdikSS/GoodbyeDPI/issues/378)) The same behavior is also implemented in [zapret package for linux](https://github.com/bol-van/zapret).
|
||||||
|
|
||||||
## How it works:
|
## Configuration
|
||||||
Lets look from the DPIses side of view: All they have is ip and tcp information, higher-level data is encrypted. So from the IP header only IP address might be helpful for them. In tcp here is basically nothing. So they may handle IP addresses and process it. What's wrong? Google servers are on the way: It is very hard to handle all that infrastracture. One server may host multiple websites and it is very bad if them block, say Google Search trying to block googlevideo. But even if googlevideo servers have their own ip for only googlevideo purposes, here is a problem about how large is Google infrastracture and how much servers are here. The DPIs can't even parse normally all the servers, because each video may live on it's cache server. So what's else? Let's take a look at a TLS level. All information here is encrypted. All... Except hello messages! They are used to initialize handshake connections and hold tons of helpful information. If we talk about TLS v1.3, it is optimized to transfer as less information as possible unencrypted. But here is only one thing that may point us which domain the user wants to connect, the SNI extension. It transfers all domain names unencrypted. Exactly what we need! And DPIs may use this thing to detect google video connections and slow down them (In fact they are corrupting a tcp connection with bad packets).
|
|
||||||
|
|
||||||
So we aims to somehow hide the SNI from them. How?
|
### OpenWRT pre configuration
|
||||||
- We can alter the SNI name in the tls packet to something else. But what's wrong with this? The server also uses SNI name for certificates. And if we change it, the server will return an invalid certificate which browser can't normally process, which may turn out to the MITM problem.
|
|
||||||
- We can encrypt it. Here are a lot of investigations about SNI, but the server should support the technique. Also ISPs may block encrypted SNI. [Check this Wikipedia page](https://en.wikipedia.org/wiki/Server_Name_Indication)
|
|
||||||
- So what else can we do with the SNI info? If we can't hide it, let's rely on DPIs weak spots. The DPI is an extremly high loaded machine that analyzes every single packet sent to the Internet. And every performance-impacted feature should be avoided for them. One of this features is IP packet fragmentation. We can split the packet in the middle of SNI message and post it. For DPI fragmentation involves too much overhead: they should store a very big mapping table which maps IP id, Source ip and Destination ip. Also note that some packets may be lost and DPI should support auto-clean of that table. So just imagine how much memory and CPU time will this cost for DPI. But fragments are ok for clients and hosts. And that's the base idea behind this package. I have to mention here that the idea isn't mine, I get in here after some research for this side. Here already was a solution for Windows, GoodbyeDPI. I just made an alternative for Linux.
|
|
||||||
|
|
||||||
You may read further in an [yt-dlp issue page](https://github.com/yt-dlp/yt-dlp/issues/10443) and in [ntc party forum](https://ntc.party/t/%D0%BE%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%BC%D0%B5%D0%B4%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-youtube-%D0%B2-%D1%80%D0%BE%D1%81%D1%81%D0%B8%D0%B8/8074).
|
When you got the release package, you should install it. Go to your router interface and put it in via *System-Software-install_package* menu. Go to *System-Startup* menu, restart firewall and start **youtubeUnblock**.
|
||||||
|
|
||||||
## How it processes packets
|
To make it work you should register an iptables rule and install required kernel modules. The list of modules depends on the version of OpenWRT and which firewall do you use (iptables or nftables).
|
||||||
When the packet is joining the queue, the application checks sni payload to be googlevideo (right how the DPIs do), segmentates/fragmentates (both TCP and IP fragmentation techniques are supported) and posts the packet. Note that it is impossible to post two fragmented packets from one netfilter queue verdict. Instead, the application drops an original packet and makes another linux raw socket to post the packets in the network. To escape infinity loops the socket marks outgoing packets and the application automatically accepts it.
|
|
||||||
|
|
||||||
## Usage:
|
The common dependency is
|
||||||
Before compilation make sure `gcc`, `make`, `autoconf`, `automake`, `pkg-config` and `libtool` is installed. For Fedora `glibc-static` should be installed as well.
|
```text
|
||||||
Compile with `make`. Install with `make install`. The package include libnetfilter_queue, libnfnetlink and libmnl as static dependencies. The package requires linux-headers and kernel built with netfilter nfqueue support.
|
kmod-nfnetlink-queue
|
||||||
|
```
|
||||||
|
but it is provided as dependency for another firewall packages.
|
||||||
|
|
||||||
You should also configure iptables for this to start working:
|
So, if you are on **iptables** you should install:
|
||||||
```iptables -A OUTPUT -p tcp --dport 443 -j NFQUEUE --queue-num 537 --queue-bypass``` (or do ```nft add rule ip mangle OUTPUT tcp dport 443 counter queue num 537 bypass``` for nftables.)
|
```text
|
||||||
Here iptables serves every tcp packet, destinating port 443 for this userspace packet analyzer (via netfilter kernel module) queue-num may be any number from 0 to 65565. --queue-bypass allows traffic to pass if the application is down.
|
kmod-ipt-nfqueue
|
||||||
|
iptables-mod-nfqueue
|
||||||
|
kmod-ipt-conntrack-extra
|
||||||
|
iptables-mod-conntrack-extra
|
||||||
|
```
|
||||||
|
and of course, iptables user-space app should be available.
|
||||||
|
|
||||||
Also tips to explicitly accept all important outgoing raw packets from youtubeUnblock from [Troubleshooting EPERMS](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#troubleshooting-eperms-operation-not-permitted) may be useful to avoid issues.
|
On **nftables** the dependencies are:
|
||||||
|
```text
|
||||||
|
kmod-nft-queue
|
||||||
|
kmod-nf-conntrack
|
||||||
|
```
|
||||||
|
|
||||||
Run an application with `youtubeUnblock 537` where `537` stands for the queue-num (must be the same as in the iptables rule).
|
Next step is to add required firewall rules.
|
||||||
|
|
||||||
Systemd daemon is also available. Do `systemctl enable --now youtubeUnblock.service` after installation (uses queue-num `537`). Please, note that systemd will configure iptables automatically. If you have troubles with it, delete ExecStartPre and ExecStop from youtubeUnblock.service and configure iptables manually (may be a useful case for nftables).
|
For nftables on OpenWRT rules comes out-of-the-box and stored under `/usr/share/nftables.d/ruleset-post/537-youtubeUnblock.nft`. All you need is install requirements and do `/etc/init.d/firewall reload`. If no, go to [Firewall configuration](#firewall-configuration).
|
||||||
|
|
||||||
If you don't want youtubeUnblock to log on each googlevideo request pass -DSILENT as CFLAGS.
|
Now we are ready to demonize the application.
|
||||||
|
|
||||||
Also DNS over HTTPS (DOH) is preferred for additional anonimity.
|
If you installed package from Github Actions or built it yourself with OpenWRT SDK, rc scripts are preinstalled. All you need is to do `/etc/init.d/youtubeUnblock start`.
|
||||||
|
Elsewhere copy `owrt/youtubeUnblock.owrt` to `/etc/init.d/youtubeUnblock` and put the program's binary into /usr/bin/. (Don't forget to `chmod +x` both). Now run `/etc/init.d/youtubeUnblock start`.
|
||||||
|
|
||||||
|
You can also run `/etc/init.d/youtubeUnblock enable` to force OpenWRT autostart on boot, but I don't recommend this since if the package has bugs you may lose access to the router (I think you will be able to reset it with reset settings tricks documented for your router).
|
||||||
|
|
||||||
|
### Entware
|
||||||
|
|
||||||
|
For Entware on Keenetic here is an [installation guide (russian)](https://help.keenetic.com/hc/ru/articles/360021214160-%D0%A3%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BA%D0%B0-%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B-%D0%BF%D0%B0%D0%BA%D0%B5%D1%82%D0%BE%D0%B2-%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D1%8F-Entware-%D0%BD%D0%B0-USB-%D0%BD%D0%B0%D0%BA%D0%BE%D0%BF%D0%B8%D1%82%D0%B5%D0%BB%D1%8C). Note that if your Entware router is missing netfilter queue kernel modules, here is no way to deal with it since Entware does not offer kernel modules. You should probably try to install OpenWRT if the problem persist. You can check required modules with command `find /lib/modules/$(uname -r) -type f -name 'nfnetlink_queue.ko*'`. If that command return not null string, everything alright. All you need is to load the modules.
|
||||||
|
|
||||||
|
To check whether the modules are loaded, do `lsmod | grep nfnetlink_queue`. If the program return nothing, you should load them manually.
|
||||||
|
```sh
|
||||||
|
insmod /lib/modules/3.3.8/kernel/net/netfilter/nfnetlink_queue.ko
|
||||||
|
insmod /lib/modules/3.3.8/kernel/net/netfilter/xt_NFQUEUE.ko
|
||||||
|
```
|
||||||
|
|
||||||
|
### PC configuration
|
||||||
|
On local host make sure to change **FORWARD** to **OUTPUT** chain in the following Firewall rulesets.
|
||||||
|
|
||||||
|
Copy `youtubeUnblock.service` to `/usr/lib/systemd/system` (you should change the path inside the file to the program position, for example `/usr/bin/youtubeUnblock`, also you may want to delete default iptables rule addition in systemd file to controll it manually). And run `systemctl start youtubeUnblock`.
|
||||||
|
|
||||||
|
### Firewall configuration
|
||||||
|
|
||||||
|
#### nftables rules
|
||||||
|
|
||||||
|
On nftables you should put next nftables rules:
|
||||||
|
```sh
|
||||||
|
nft add rule inet fw4 mangle_forward tcp dport 443 ct original "packets < 20" counter queue num 537 bypass
|
||||||
|
nft insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Iptables rules
|
||||||
|
|
||||||
|
On iptables you should put next iptables rules:
|
||||||
|
```sh
|
||||||
|
iptables -t mangle -A FORWARD -p tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
|
||||||
|
iptables -I OUTPUT -m mark --mark 32768/32768 -j ACCEPT
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that above rules use *conntrack* to route only first 20 packets from the connection to **youtubeUnblock**.
|
||||||
|
If you got some troubles with it, for example **youtubeUnblock** doesn't detect YouTube, try to delete *connbytes* from the rules. But it is an unlikely behavior and you should probably check your ruleset.
|
||||||
|
|
||||||
|
You can use `--queue-balance` with multiple instances of **youtubeUnblock** for performance. This behavior is supported via multithreading. Just pass `--threads=n` where n stands for an number of threads you want to be enabled. The n defaults to **1**. The maximum threads defaults to **16** but may be altered programmatically. Note, that if you are about to increase it, here is 100% chance that you are on the wrong way.
|
||||||
|
|
||||||
|
Also [DNS over HTTPS](https://github.com/curl/curl/wiki/DNS-over-HTTPS) is preferred for additional anonymity.
|
||||||
|
|
||||||
|
## Check it
|
||||||
|
|
||||||
|
Here is the command to test whether it working or not:
|
||||||
|
```sh
|
||||||
|
curl -o/dev/null -k --connect-to ::google.com -k -L -H Host:\ mirror.gcr.io https://test.googlevideo.com/v2/cimg/android/blobs/sha256:6fd8bdac3da660bde7bd0b6f2b6a46e1b686afb74b9a4614def32532b73f5eaa
|
||||||
|
```
|
||||||
|
|
||||||
|
It should return low speed without **youtubeUnblock** and faster with it. With **youtubeUnblock** the speed should be the same as fast with the next command:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -o/dev/null -k --connect-to ::google.com -k -L -H Host:\ mirror.gcr.io https://mirror.gcr.io/v2/cimg/android/blobs/sha256:6fd8bdac3da660bde7bd0b6f2b6a46e1b686afb74b9a4614def32532b73f5eaa
|
||||||
|
```
|
||||||
|
|
||||||
## Flags
|
## Flags
|
||||||
|
|
||||||
Available flags:
|
Available flags:
|
||||||
- `--sni-domains=<comma separated domain list>|all` - List of domains you want to be handled by sni. Use this string if you want to change default domains. Defaults to `googlevideo.com,youtube.com,ggpht.com,ytimg.com`. You can pass all if you want for every Client Hello to be handled.
|
|
||||||
- `--seg2delay=<delay>` - This flag forces youtubeUnblock to wait little bit before send the 2nd part of the split packet.
|
- `--sni-domains=<comma separated domain list>|all` List of domains you want to be handled by SNI. Use this string if you want to change default domain list. Defaults is `googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com`. You can pass **all** if you want for every *ClientHello* to be handled.
|
||||||
- `--fake-sni={ack,ttl, none}` This flag enables fake-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. Note, that this flag is set to `ack` by default. You may disable fake sni by setting it to `none`. Note, that your ISP may have conntrack drop on invalid state enabled, so this flag won't work. Use `ttl` to escape that.
|
|
||||||
- `--fake-sni-ttl=<ttl>` Tunes the time to live of fake sni messages. TTL is specified like that the packet will go through the TSPU and captured by it, but will not reach the destination server. Defaults to 8.
|
- `--queue-num=<number of netfilter queue>` The number of netfilter queue **youtubeUnblock** will be linked to. Defaults to **537**.
|
||||||
|
|
||||||
|
- `--fake-sni={0|1}` This flag enables fake-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 before open an issue refer to [Troubleshooting for EPERMS](#troubleshooting-eperms-operation-not-permitted). Defaults to **1**.
|
||||||
|
|
||||||
|
- `--fake-sni-seq-len=<length>` This flag specifies **youtubeUnblock** to build a complicated construction of fake client hello packets. length determines how much fakes will be sent. Defaults to **1**.
|
||||||
|
|
||||||
|
- `--faking-strategy={randseq|ttl|tcp_check|pastseq}` This flag determines the strategy of fake packets invalidation. Defaults to `randseq`
|
||||||
|
- `randseq` specifies that random sequence/acknowledgemend random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled.
|
||||||
|
- `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured.
|
||||||
|
- `pastseq` is like `randseq` but sequence number is not random but references the packet sent in the past (before current).
|
||||||
|
- `tcp_check` will invalidate faking packet with invalid checksum. May be handled and dropped by some providers/TSPUs.
|
||||||
|
|
||||||
|
- `--faking-ttl=<ttl>` Tunes the time to live (TTL) of fake SNI messages. TTL is specified like that the packet will go through the DPI system and captured by it, but will not reach the destination server. Defaults to **8**.
|
||||||
|
|
||||||
|
- `--frag={tcp,ip,none}` Specifies the fragmentation strategy for the packet. tcp is used by default. Ip fragmentation may be blocked by DPI system. None specifies no fragmentation. Probably this won't work, but may be will work for some fake sni strategies.
|
||||||
|
|
||||||
|
- `--frag-sni-reverse={0|1}` Specifies **youtubeUnblock** to send *ClientHello* fragments in the reverse order. Defaults to **1**.
|
||||||
|
|
||||||
|
- `--frag-sni-faked={0|1}` Specifies **youtubeUnblock** to send fake packets near *ClientHello* (fills payload with zeroes). Defaults to **0**.
|
||||||
|
|
||||||
|
- `--frag-middle-sni={0|1}` With this options **youtubeUnblock** will split the packet in the middle of SNI data. Defaults to 1.
|
||||||
|
|
||||||
|
- `--frag-sni-pos=<pos>` With this option **youtubeUnblock** will split the packet at the position pos. Defaults to 2.
|
||||||
|
|
||||||
|
- `--quic-drop` Drop all QUIC packets which goes to youtubeUnblock. Won't affect any other UDP packets. Suitable for some TVs. Note, that for this option to work you should also add proxy udp to youtubeUnblock in firewall. `connbytes` may also be used with udp.
|
||||||
|
|
||||||
|
- `--fk-winsize=<winsize>` Specifies window size for the fragmented TCP packet. Applicable if you want for response to be fragmented. May slowdown connection initialization.
|
||||||
|
|
||||||
|
- `--sni-detection={parse|brute}` Specifies how to detect SNI. Parse will normally detect it by parsing the Client Hello message. Brute will go through the entire message and check possibility of SNI occurrence. Please note, that when `--sni-domains` option is not all brute will be O(nm) time complexity where n stands for length of the message and m is number of domains. Defaults to parse.
|
||||||
|
|
||||||
|
- `--seg2delay=<delay>` This flag forces **youtubeUnblock** to wait a little bit before send the 2nd part of the split packet.
|
||||||
|
|
||||||
|
- `--silent` Disables verbose mode.
|
||||||
|
|
||||||
|
- `--trace` Maximum verbosity for debugging purposes.
|
||||||
|
|
||||||
- `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything.
|
- `--no-gso` Disables support for Google Chrome fat packets which uses GSO. This feature is well tested now, so this flag probably won't fix anything.
|
||||||
- `--threads=<threads number>` Specifies the amount of threads you want to be running for your program. This defaults to 1 and shouldn't be edited for normal use. If you have performance issues, consult [performance chaptr](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#performance)
|
|
||||||
- `--silent` - Disables Google video detected debug logs.
|
|
||||||
- `--frag={tcp,ip,none}` Specifies the fragmentation strategy for the packet. tcp is used by default. Ip fragmentation may be blocked by TSPU. None specifies no fragmentation. Probably this won't work, but may be will work for some fake sni strategies.
|
|
||||||
|
|
||||||
If you are on Chromium you may have to disable kyber (the feature that makes the TLS ClientHello very fat). I've got the problem with it on router, so to escape possibly errors it is better to just disable it: in chrome://flags search for kyber and switch it to disabled state.
|
- `--threads=<threads number>` Specifies the amount of threads you want to be running for your program. This defaults to **1** and shouldn't be edited for normal use. If you have performance issues, consult [performance chaptr](https://github.com/Waujito/youtubeUnblock?tab=readme-ov-file#performance)
|
||||||
|
|
||||||
If your browser is using quic it may not work properly. Disable it in chrome in chrome://flags and in Firefox network.http.http{2,3}.enable(d) in about:config
|
## Troubleshooting
|
||||||
|
|
||||||
|
If you got troubles with some sites and you sure that they are blocked by SNI (youtube for example), use may play around with [flags](#flags) and their combinations. At first it is recommended to try `--faking-strategy` flag and `--frag-sni-faked=1`.
|
||||||
|
If you have troubles with some sites being proxied, you can play with flags values. For example, for someone `--faking-strategy=ttl` works. You should specify proper `--fake-sni-ttl=<ttl value>` where ttl is the amount of hops between you and DPI.
|
||||||
|
|
||||||
|
If you are on Chromium you may have to disable *kyber* (the feature that makes the TLS *ClientHello* very big). I've got the problem with it on router, so to escape possible errors, so it is better to disable it: in `chrome://flags` search for kyber and switch it to disabled state. Alternatively you may set `--sni-detection=brute` and probably adjust `--sni-domains` flag.
|
||||||
|
|
||||||
|
If your browser is using QUIC it may not work properly. Disable it in Chrome in `chrome://flags` and in Firefox `network.http.http{2,3}.enable(d)` in `about:config` option.
|
||||||
|
|
||||||
|
### TV
|
||||||
|
|
||||||
|
Televisions are the biggest headache.
|
||||||
|
|
||||||
|
In [this issue](https://github.com/Waujito/youtubeUnblock/issues/59) the problem has been resolved.
|
||||||
|
|
||||||
|
If you have troubles with televisions try `--faking-strategy=ttl` flag and play around with `--faking-ttl=n`. See [#flags](#flags) for more details. Also you might be have to disable QUIC. To do it you may use `--quic-drop` [flag](#flags) with proper firewall configuration (check description of the flag). Note, that this flag won't disable gQUIC and some TVs may relay on it. To disable gQUIC you will need to block the entire 443 port for udp in firewall configuration:
|
||||||
|
|
||||||
|
For **nftables** do
|
||||||
|
```
|
||||||
|
nft insert rule inet fw4 forward ip saddr 192.168.. udp dport 443 counter drop
|
||||||
|
```
|
||||||
|
|
||||||
|
For **iptables**
|
||||||
|
```
|
||||||
|
iptables -I OUTPUT --src 192.168.. -p udp --dport 443 -j DROP
|
||||||
|
```
|
||||||
|
|
||||||
|
Where you have to replace 192.168.. with ip of your television.
|
||||||
|
|
||||||
### Troubleshooting EPERMS (Operation not permitted)
|
### Troubleshooting EPERMS (Operation not permitted)
|
||||||
EPERM may occur in a lot of places but generally here are two: mnl_cb_run and when sending the packet via rawsocket (raw_frags_send and send fake sni).
|
|
||||||
- mnl_cb_run Operation not permitted indicates that another instance of youtubeUnblock is running on the specified queue-num.
|
*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).
|
||||||
- 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.
|
|
||||||
|
- **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)
|
* 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.
|
* 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`.
|
* 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`.
|
||||||
|
|
||||||
## Performance
|
## How it works:
|
||||||
If you have bad performance you can queue to youtubeUnblock only first, say, 20 packets from the connection. To do so, use nftables conntrack packets counter: `nft add rule inet fw4 mangle_forward tcp dport 443 ct original "packets < 20" counter queue num 537 bypass`. For my 1 CPU core device it worked pretty well. This works because we do care about only first packets with ClientHello. We don't need to process others.
|
|
||||||
|
|
||||||
The same behavior is also possible in iptables: `iptables -t mangle -A FORWARD -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass`. (The package iptables-mod-conntrack-extra is required for connbytes on OpenWRT)
|
Let's look from the DPI systems side of view: All of they have an ip and tcp information, higher-level data is encrypted. So from the IP header only IP address might be helpful for them to limit user traffic. In TCP here is basically nothing. So they may handle IP addresses and process it.
|
||||||
For hosts change FORWARD to OUTPUT.
|
|
||||||
|
|
||||||
You can use `--queue-balance` with multiple instances of youtubeUnblock. This behavior is supported via multithreading. Just pass -DTHREADS_NUM=n where n stands for an amount of threads you want to be enabled. The n defaults to 1. The maximum threads defaults to 16 but may be altered programatically. Note, that if you are about to increase it, here is 100% chance that you are on the wrong way.
|
What's wrong? Google servers are on the way: It is very hard to handle all that infrastructure. One server may host multiple websites and it is very bad if them blocks, for example, Google Search while trying to block YouTube (googlevideo). But even if YouTube servers have their own IP for only googlevideo purposes, here is a problem about how large is Google infrastracture and how much servers in it. The DPI systems can't even parse normally all the servers, because each video may live on its cache server [GGC](https://support.google.com/interconnect/answer/9058809?hl=en).
|
||||||
|
|
||||||
|
So what's else? Let's take a look at a TLS level. All information here is encrypted. All... Except *ClientHello* messages! They are used to initialize handshake connections and hold tons of helpful information. If we talk about TLS version 1.3, it is optimized to transfer as less information as possible unencrypted. But here is only one thing that may point us which domain the user wants to connect, the SNI extension. It transfers all domain names unencrypted in plain text. Exactly what we need! And DPI systems may use this text to detect YouTube connections and slow down or reject them (In fact they are corrupting a TCP connection with bad packets).
|
||||||
|
|
||||||
|
So we aim to somehow hide the SNI from them. How?
|
||||||
|
|
||||||
|
- We can alter the SNI name in the tls packet to something else. But what's wrong with this? The server also uses SNI name for certificates (CN=). And if we change it, the server will return an invalid certificate which browser can't normally process, which may turn out to the MITM problem.
|
||||||
|
|
||||||
|
- We can encrypt it. Here are a lot of investigations about SNI, but the server should support this technique. Also ISPs may block [encrypted SNI](https://en.wikipedia.org/wiki/Server_Name_Indication).
|
||||||
|
|
||||||
|
- So what else can we do with the SNI info? If we can't hide it, let's rely on DPI systems weak spots. The DPI is an extremely high loaded infrastructure that analyzes every single packet sent to the Internet. And every performance-impacted feature should be avoided for them. One of this features is IP packet fragmentation. We can split the packet in the middle of SNI message and post it. For DPI fragmentation involves too much overhead: they should store a very big mapping table which maps IP id, Source ip and Destination ip. Also note that some packets may be lost and DPI should support auto-clean of that table. So just imagine how much memory and CPU time will this cost for DPI. But fragments are ok for clients and hosts. And that's the base idea behind this package. I have to mention here that the idea isn't mine, I get in here after some research for this side. Here already was a solution for Windows, GoodbyeDPI. I just made an alternative for Linux.
|
||||||
|
|
||||||
|
You may read further in an [yt-dlp issue page](https://github.com/yt-dlp/yt-dlp/issues/10443) and in [ntc party forum](https://ntc.party/t/%D0%BE%D0%B1%D1%81%D1%83%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5-%D0%B7%D0%B0%D0%BC%D0%B5%D0%B4%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-youtube-%D0%B2-%D1%80%D0%BE%D1%81%D1%81%D0%B8%D0%B8/8074).
|
||||||
|
|
||||||
|
## How it processes packets
|
||||||
|
|
||||||
|
When the packet is joining the queue, the application checks SNI payload to be YouTube(googlevideo) (right how the DPI systems do), segmentate/fragmentates (both TCP and IP fragmentation techniques are supported) and posts the packet. Note that it is impossible to post two fragmented packets from one netfilter queue verdict. Instead, the application drops an original packet and makes another linux raw socket to post the packets in the network. To escape infinity loops the socket marks outgoing packets and the application automatically accepts it.
|
||||||
|
|
||||||
|
## Compilation
|
||||||
|
|
||||||
|
Before compilation make sure `gcc`, `make`, `autoconf`, `automake`, `pkg-config` and `libtool` is installed. For Fedora `glibc-static` should be installed as well.
|
||||||
|
|
||||||
|
Compile with `make`. Install with `make install`. The package include `libnetfilter_queue`, `libnfnetlink` and `libmnl` as static dependencies. The package requires `linux-headers` and kernel built with netfilter nfqueue support.
|
||||||
|
|
||||||
## OpenWRT case
|
## OpenWRT case
|
||||||
|
|
||||||
The package is also compatible with routers. The router should be running by linux-based system such as [OpenWRT](https://openwrt.org/).
|
The package is also compatible with routers. The router should be running by linux-based system such as [OpenWRT](https://openwrt.org/).
|
||||||
You can build under openwrt with two options: first - through the SDK, which is preferred way and second is cross-compile manually with openwrt toolchain.
|
|
||||||
|
You can build under OpenWRT with two options: first - through the SDK, which is preferred way and second is cross-compile manually with OpenWRT toolchain.
|
||||||
|
|
||||||
### Building OpenWRT .ipk package
|
### Building OpenWRT .ipk package
|
||||||
|
|
||||||
OpenWRT provides a high-level SDK for the package builds.
|
OpenWRT provides a high-level SDK for the package builds.
|
||||||
First step is to download or compile OpenWRT SDK for your specific platform. The SDK can be compiled according to [this tutorial](https://openwrt.org/docs/guide-developer/toolchain/using_the_sdk). Beside of raw source code of SDK, OpenWRT also offers precompiled SDKs for your router. You can find it on the router page. For example, I have ramips/mt76x8 based router so for me the sdk is on https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-sdk-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64`. You will need to [install sdk requirements on your system](https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem) If you have any problems, use docker ubuntu:24.04 image. Make sure to be a non-root user since some makesystem fails with it. Next, untar the SDK and cd into it. Do `echo "src-git youtubeUnblock https://github.com/Waujito/youtubeUnblock.git;openwrt" >> feeds.conf`, `./scripts/feeds update youtubeUnblock`, `./scripts/feeds install -a -p youtubeUnblock`, `make package/youtubeUnblock/compile`. Now the packet is built and you can import it to the router. Find it in `bin/packages/<target>/youtubeUnblock/youtubeUnblock-<version>.ipk`. Go to your router interface and put it in via System-Software-install_package. Now the package is on the router. Goto System-Startup, restart firewall and start youtubeUnblock. You are done!
|
|
||||||
|
First step is to download or compile OpenWRT SDK for your specific platform. The SDK can be compiled according to [this tutorial](https://openwrt.org/docs/guide-developer/toolchain/using_the_sdk).
|
||||||
|
|
||||||
|
Beside of raw source code of SDK, OpenWRT also offers precompiled SDKs for your router. You can find it on the router page. For example, I have ramips/mt76x8 based router so for me the sdk is on https://downloads.openwrt.org/releases/23.05.3/targets/ramips/mt76x8/ and called `openwrt-sdk-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64`.
|
||||||
|
|
||||||
|
You will need to [install sdk requirements on your system](https://openwrt.org/docs/guide-developer/toolchain/install-buildsystem) If you have any problems, use docker ubuntu:24.04 image. Make sure to be a non-root user since some makesystem fails with it. Next, untar the SDK and cd into it.
|
||||||
|
|
||||||
|
Do
|
||||||
|
```sh
|
||||||
|
echo "src-git youtubeUnblock https://github.com/Waujito/youtubeUnblock.git;openwrt" >> feeds.conf
|
||||||
|
./scripts/feeds update youtubeUnblock
|
||||||
|
./scripts/feeds install -a -p youtubeUnblock
|
||||||
|
make package/youtubeUnblock/compile
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the packet is built and you can import it to the router. Find it in `bin/packages/<target>/youtubeUnblock/youtubeUnblock-<version>.ipk`.
|
||||||
|
|
||||||
### Building with toolchain
|
### Building with toolchain
|
||||||
The precompiled toolchain located near the SDK. For me it is called `openwrt-toolchain-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64.tar.xz`. When you download the toolchain, 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.
|
|
||||||
|
|
||||||
### Configuration
|
The precompiled toolchain located near the SDK. For example it is called `openwrt-toolchain-23.05.3-ramips-mt76x8_gcc-12.3.0_musl.Linux-x86_64.tar.xz`. When you download the toolchain, 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.
|
||||||
If you compiled the package via the SDK everything is preinstalled. But if you got any issues (suitable for routers with iptables instead of nftables), ssh into the router and check up everything manually.
|
|
||||||
|
|
||||||
For iptables: install a normal iptables user-space app: `xtables-legacy iptables-zz-legacy` and kernel/iptables nfqueue extensions: `iptables-mod-nfqueue kmod-ipt-nfqueue` and add `iptables -t mangle -A FORWARD -p tcp -m tcp --dport 443 -j NFQUEUE --queue-num 537 --queue-bypass` rule.
|
```
|
||||||
|
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
|
||||||
|
```
|
||||||
|
|
||||||
If you prefer nftables, this should work: `nft add rule inet fw4 mangle_forward tcp dport 443 counter queue num 537 bypass`. Note that `kmod-nft-queue` should be installed.
|
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 router model name maybe an [automake cross-compile manual](https://www.gnu.org/software/automake/manual/html_node/Cross_002dCompilation.html) will be helpful.
|
||||||
|
|
||||||
Also you can copy `owrt/537-youtubeUnblock.nft` to `/usr/share/nftables.d/ruleset-post/537-youtubeUnblock.nft` and run `/etc/init.d/firewall reload`. This will reload the nftables ruleset and automatically link 537-youtubeUnblock.nft with it.
|
When compilation is done, the binary file will be in build directory. Copy it to your router. Note that a 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` it and run.
|
||||||
|
|
||||||
Next step is to daemonize the application in openwrt. Copy `owrt/youtubeUnblock.owrt` to `/etc/init.d/youtubeUnblock` and put the program into /usr/bin/. (Don't forget to `chmod +x` both). Now run `/etc/init.d/youtubeUnblock start`. You can alo run `/etc/init.d/youtubeUnblock enable` to force OpenWRT autostart the program on boot, but I don't recommend this since if the packet has bug you may lose access to the router (I think you will be able to reset it with reset settings tricks documented for your router).
|
|
||||||
|
|
||||||
## If you have any questions/suggestions/problems feel free to open an issue.
|
>If you have any questions/suggestions/problems feel free to open an [issue](https://github.com/Waujito/youtubeUnblock/issues).
|
||||||
|
|||||||
408
args.c
Normal file
408
args.c
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
#include "config.h"
|
||||||
|
#include "raw_replacements.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
|
||||||
|
struct config_t config = {
|
||||||
|
.threads = THREADS_NUM,
|
||||||
|
.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,
|
||||||
|
.frag_middle_sni = 1,
|
||||||
|
.frag_sni_pos = 2,
|
||||||
|
|
||||||
|
.sni_detection = SNI_DETECTION_PARSE,
|
||||||
|
|
||||||
|
#ifdef SEG2_DELAY
|
||||||
|
.seg2_delay = SEG2_DELAY,
|
||||||
|
#else
|
||||||
|
.seg2_delay = 0,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_GSO
|
||||||
|
.use_gso = true,
|
||||||
|
#else
|
||||||
|
.use_gso = false,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
.verbose = 1,
|
||||||
|
#else
|
||||||
|
.verbose = 0,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.domains_str = defaul_snistr,
|
||||||
|
.domains_strlen = sizeof(defaul_snistr),
|
||||||
|
|
||||||
|
.queue_start_num = DEFAULT_QUEUE_NUM,
|
||||||
|
.fake_sni_pkt = fake_sni_old,
|
||||||
|
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator
|
||||||
|
};
|
||||||
|
|
||||||
|
#define OPT_SNI_DOMAINS 1
|
||||||
|
#define OPT_FAKE_SNI 2
|
||||||
|
#define OPT_FAKING_TTL 3
|
||||||
|
#define OPT_FAKING_STRATEGY 10
|
||||||
|
#define OPT_FAKE_SNI_SEQ_LEN 11
|
||||||
|
#define OPT_FRAG 4
|
||||||
|
#define OPT_FRAG_SNI_REVERSE 12
|
||||||
|
#define OPT_FRAG_SNI_FAKED 13
|
||||||
|
#define OPT_FRAG_MIDDLE_SNI 18
|
||||||
|
#define OPT_FRAG_SNI_POS 19
|
||||||
|
#define OPT_FK_WINSIZE 14
|
||||||
|
#define OPT_TRACE 15
|
||||||
|
#define OPT_QUIC_DROP 16
|
||||||
|
#define OPT_SNI_DETECTION 17
|
||||||
|
#define OPT_SEG2DELAY 5
|
||||||
|
#define OPT_THREADS 6
|
||||||
|
#define OPT_SILENT 7
|
||||||
|
#define OPT_NO_GSO 8
|
||||||
|
#define OPT_QUEUE_NUM 9
|
||||||
|
|
||||||
|
#define OPT_MAX OPT_FRAG_SNI_POS
|
||||||
|
|
||||||
|
static struct option long_opt[] = {
|
||||||
|
{"help", 0, 0, 'h'},
|
||||||
|
{"version", 0, 0, 'v'},
|
||||||
|
{"sni-domains", 1, 0, OPT_SNI_DOMAINS},
|
||||||
|
{"fake-sni", 1, 0, OPT_FAKE_SNI},
|
||||||
|
{"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN},
|
||||||
|
{"faking-strategy", 1, 0, OPT_FAKING_STRATEGY},
|
||||||
|
{"faking-ttl", 1, 0, OPT_FAKING_TTL},
|
||||||
|
{"frag", 1, 0, OPT_FRAG},
|
||||||
|
{"frag-sni-reverse", 1, 0, OPT_FRAG_SNI_REVERSE},
|
||||||
|
{"frag-sni-faked", 1, 0, OPT_FRAG_SNI_FAKED},
|
||||||
|
{"frag-middle-sni", 1, 0, OPT_FRAG_MIDDLE_SNI},
|
||||||
|
{"frag-sni-pos", 1, 0, OPT_FRAG_SNI_POS},
|
||||||
|
{"fk-winsize", 1, 0, OPT_FK_WINSIZE},
|
||||||
|
{"quic-drop", 0, 0, OPT_QUIC_DROP},
|
||||||
|
{"sni-detection", 1, 0, OPT_SNI_DETECTION},
|
||||||
|
{"seg2delay", 1, 0, OPT_SEG2DELAY},
|
||||||
|
{"threads", 1, 0, OPT_THREADS},
|
||||||
|
{"silent", 0, 0, OPT_SILENT},
|
||||||
|
{"trace", 0, 0, OPT_TRACE},
|
||||||
|
{"no-gso", 0, 0, OPT_NO_GSO},
|
||||||
|
{"queue-num", 1, 0, OPT_QUEUE_NUM},
|
||||||
|
{0,0,0,0}
|
||||||
|
};
|
||||||
|
|
||||||
|
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() {
|
||||||
|
printf("youtubeUnblock\n");
|
||||||
|
printf("Bypasses deep packet inspection systems that relies on SNI\n");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_usage(const char *argv0) {
|
||||||
|
print_version();
|
||||||
|
|
||||||
|
printf("Usage: %s [ OPTIONS ] \n", argv0);
|
||||||
|
printf("Options:\n");
|
||||||
|
printf("\t--queue-num=<number of netfilter queue>\n");
|
||||||
|
printf("\t--sni-domains=<comma separated domain list>|all\n");
|
||||||
|
printf("\t--fake-sni={1|0}\n");
|
||||||
|
printf("\t--fake-sni-seq-len=<length>\n");
|
||||||
|
printf("\t--faking-ttl=<ttl>\n");
|
||||||
|
printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq}\n");
|
||||||
|
printf("\t--frag={tcp,ip,none}\n");
|
||||||
|
printf("\t--frag-sni-reverse={0|1}\n");
|
||||||
|
printf("\t--frag-sni-faked={0|1}\n");
|
||||||
|
printf("\t--frag-middle-sni={0|1}\n");
|
||||||
|
printf("\t--frag-sni-pos=<pos>\n");
|
||||||
|
printf("\t--fk-winsize=<winsize>\n");
|
||||||
|
printf("\t--quic-drop\n");
|
||||||
|
printf("\t--sni-detection={parse|brute}\n");
|
||||||
|
printf("\t--seg2delay=<delay>\n");
|
||||||
|
printf("\t--threads=<threads number>\n");
|
||||||
|
printf("\t--silent\n");
|
||||||
|
printf("\t--trace\n");
|
||||||
|
printf("\t--no-gso\n");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
int parse_args(int argc, char *argv[]) {
|
||||||
|
int opt;
|
||||||
|
int optIdx;
|
||||||
|
long num;
|
||||||
|
|
||||||
|
while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'h':
|
||||||
|
print_usage(argv[0]);
|
||||||
|
goto stop_exec;
|
||||||
|
case 'v':
|
||||||
|
print_version();
|
||||||
|
goto stop_exec;
|
||||||
|
case OPT_TRACE:
|
||||||
|
config.verbose = 2;
|
||||||
|
break;
|
||||||
|
case OPT_SILENT:
|
||||||
|
config.verbose = 0;
|
||||||
|
break;
|
||||||
|
case OPT_NO_GSO:
|
||||||
|
config.use_gso = 0;
|
||||||
|
break;
|
||||||
|
case OPT_QUIC_DROP:
|
||||||
|
config.quic_drop = 1;
|
||||||
|
break;
|
||||||
|
case OPT_SNI_DOMAINS:
|
||||||
|
if (!strcmp(optarg, "all")) {
|
||||||
|
config.all_domains = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.domains_str = optarg;
|
||||||
|
config.domains_strlen = strlen(config.domains_str);
|
||||||
|
break;
|
||||||
|
case OPT_SNI_DETECTION:
|
||||||
|
if (strcmp(optarg, "parse") == 0) {
|
||||||
|
config.sni_detection = SNI_DETECTION_PARSE;
|
||||||
|
} else if (strcmp(optarg, "brute") == 0) {
|
||||||
|
config.sni_detection = SNI_DETECTION_BRUTE;
|
||||||
|
} else {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPT_FRAG:
|
||||||
|
if (strcmp(optarg, "tcp") == 0) {
|
||||||
|
config.fragmentation_strategy = FRAG_STRAT_TCP;
|
||||||
|
} else if (strcmp(optarg, "ip") == 0) {
|
||||||
|
config.fragmentation_strategy = FRAG_STRAT_IP;
|
||||||
|
} else if (strcmp(optarg, "none") == 0) {
|
||||||
|
config.fragmentation_strategy = FRAG_STRAT_NONE;
|
||||||
|
} else {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPT_FRAG_SNI_FAKED:
|
||||||
|
if (strcmp(optarg, "1") == 0) {
|
||||||
|
config.frag_sni_faked = 1;
|
||||||
|
} else if (strcmp(optarg, "0") == 0) {
|
||||||
|
config.frag_sni_faked = 0;
|
||||||
|
} else {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPT_FRAG_SNI_REVERSE:
|
||||||
|
if (strcmp(optarg, "1") == 0) {
|
||||||
|
config.frag_sni_reverse = 1;
|
||||||
|
} else if (strcmp(optarg, "0") == 0) {
|
||||||
|
config.frag_sni_reverse = 0;
|
||||||
|
} else {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPT_FRAG_MIDDLE_SNI:
|
||||||
|
if (strcmp(optarg, "1") == 0) {
|
||||||
|
config.frag_middle_sni = 1;
|
||||||
|
} else if (strcmp(optarg, "0") == 0) {
|
||||||
|
config.frag_middle_sni = 0;
|
||||||
|
} else {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPT_FRAG_SNI_POS:
|
||||||
|
num = parse_numeric_option(optarg);
|
||||||
|
if (errno != 0 || num < 0) {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.frag_sni_pos = num;
|
||||||
|
break;
|
||||||
|
case OPT_FAKING_STRATEGY:
|
||||||
|
if (strcmp(optarg, "randseq") == 0) {
|
||||||
|
config.faking_strategy = FAKE_STRAT_RAND_SEQ;
|
||||||
|
} else if (strcmp(optarg, "ttl") == 0) {
|
||||||
|
config.faking_strategy = FAKE_STRAT_TTL;
|
||||||
|
} else if (strcmp(optarg, "tcp_check") == 0) {
|
||||||
|
config.faking_strategy = FAKE_STRAT_TCP_CHECK;
|
||||||
|
} else if (strcmp(optarg, "pastseq") == 0) {
|
||||||
|
config.faking_strategy = FAKE_STRAT_PAST_SEQ;
|
||||||
|
} else {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPT_FAKING_TTL:
|
||||||
|
num = parse_numeric_option(optarg);
|
||||||
|
if (errno != 0 || num < 0 || num > 255) {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.faking_ttl = num;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPT_FAKE_SNI:
|
||||||
|
if (strcmp(optarg, "1") == 0) {
|
||||||
|
config.fake_sni = 1;
|
||||||
|
} else if (strcmp(optarg, "0") == 0) {
|
||||||
|
config.fake_sni = 0;
|
||||||
|
} else {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case OPT_FAKE_SNI_SEQ_LEN:
|
||||||
|
num = parse_numeric_option(optarg);
|
||||||
|
if (errno != 0 || num < 0 || num > 255) {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.fake_sni_seq_len = num;
|
||||||
|
break;
|
||||||
|
case OPT_FK_WINSIZE:
|
||||||
|
num = parse_numeric_option(optarg);
|
||||||
|
if (errno != 0 || num < 0) {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.fk_winsize = num;
|
||||||
|
break;
|
||||||
|
case OPT_SEG2DELAY:
|
||||||
|
num = parse_numeric_option(optarg);
|
||||||
|
if (errno != 0 || num < 0) {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.seg2_delay = num;
|
||||||
|
break;
|
||||||
|
case OPT_THREADS:
|
||||||
|
num = parse_numeric_option(optarg);
|
||||||
|
if (errno != 0 || num < 0 || num > MAX_THREADS) {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.threads = num;
|
||||||
|
break;
|
||||||
|
case OPT_QUEUE_NUM:
|
||||||
|
num = parse_numeric_option(optarg);
|
||||||
|
if (errno != 0 || num < 0) {
|
||||||
|
goto invalid_opt;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.queue_start_num = num;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// out:
|
||||||
|
errno = 0;
|
||||||
|
return 0;
|
||||||
|
stop_exec:
|
||||||
|
errno = 0;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
invalid_opt:
|
||||||
|
printf("Invalid option %s\n", long_opt[optIdx].name);
|
||||||
|
error:
|
||||||
|
print_usage(argv[0]);
|
||||||
|
errno = EINVAL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_welcome() {
|
||||||
|
switch (config.fragmentation_strategy) {
|
||||||
|
case FRAG_STRAT_TCP:
|
||||||
|
printf("Using TCP segmentation\n");
|
||||||
|
break;
|
||||||
|
case FRAG_STRAT_IP:
|
||||||
|
printf("Using IP fragmentation\n");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("SNI fragmentation is disabled\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.seg2_delay) {
|
||||||
|
printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.fake_sni) {
|
||||||
|
printf("Fake SNI will be sent before each target client hello\n");
|
||||||
|
} else {
|
||||||
|
printf("Fake SNI is disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_reverse) {
|
||||||
|
printf("Fragmentation Client Hello will be reversed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_faked) {
|
||||||
|
printf("Fooling packets will be sent near the original Client Hello\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.fake_sni_seq_len > 1) {
|
||||||
|
printf("Faking sequence of length %d will be built as fake sni\n", config.fake_sni_seq_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (config.faking_strategy) {
|
||||||
|
case FAKE_STRAT_TTL:
|
||||||
|
printf("TTL faking strategy will be used with TTL %d\n", config.faking_ttl);
|
||||||
|
break;
|
||||||
|
case FAKE_STRAT_RAND_SEQ:
|
||||||
|
printf("Random seq faking strategy will be used\n");
|
||||||
|
break;
|
||||||
|
case FAKE_STRAT_TCP_CHECK:
|
||||||
|
printf("TCP checksum faking strategy will be used\n");
|
||||||
|
break;
|
||||||
|
case FAKE_STRAT_PAST_SEQ:
|
||||||
|
printf("Past seq faking strategy will be used\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.fk_winsize) {
|
||||||
|
printf("Response TCP window will be set to %d with the appropriate scale\n", config.fk_winsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (config.use_gso) {
|
||||||
|
printf("GSO is enabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.quic_drop) {
|
||||||
|
printf("All QUIC packets will be dropped\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.sni_detection == SNI_DETECTION_BRUTE) {
|
||||||
|
printf("Server Name Extension will be parsed in the bruteforce mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.all_domains) {
|
||||||
|
printf("All Client Hello will be targetted by youtubeUnblock!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
11
args.h
Normal file
11
args.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef ARGS_H
|
||||||
|
#define ARGS_H
|
||||||
|
|
||||||
|
void print_version();
|
||||||
|
void print_usage(const char *argv0);
|
||||||
|
int parse_args(int argc, char *argv[]);
|
||||||
|
|
||||||
|
/* Prints starting messages */
|
||||||
|
void print_welcome();
|
||||||
|
|
||||||
|
#endif /* ARGS_H */
|
||||||
70
config.h
70
config.h
@@ -1,18 +1,50 @@
|
|||||||
|
#ifndef YTB_CONFIG_H
|
||||||
|
#define YTB_CONFIG_H
|
||||||
|
|
||||||
|
typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len);
|
||||||
|
/**
|
||||||
|
* Sends the packet after delay_ms. The function should schedule send and return immediately
|
||||||
|
* (for example, open daemon thread)
|
||||||
|
*/
|
||||||
|
typedef void (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms);
|
||||||
|
|
||||||
|
struct instance_config_t {
|
||||||
|
raw_send_t send_raw_packet;
|
||||||
|
delayed_send_t send_delayed_packet;
|
||||||
|
};
|
||||||
|
extern struct instance_config_t instance_config;
|
||||||
|
|
||||||
struct config_t {
|
struct config_t {
|
||||||
unsigned int queue_start_num;
|
unsigned int queue_start_num;
|
||||||
int rawsocket;
|
|
||||||
int threads;
|
int threads;
|
||||||
int use_gso;
|
int use_gso;
|
||||||
int fragmentation_strategy;
|
int fragmentation_strategy;
|
||||||
unsigned char fake_sni_ttl;
|
int frag_sni_reverse;
|
||||||
int fake_sni_strategy;
|
int frag_sni_faked;
|
||||||
|
int faking_strategy;
|
||||||
|
int frag_middle_sni;
|
||||||
|
int frag_sni_pos;
|
||||||
|
unsigned char faking_ttl;
|
||||||
|
int fake_sni;
|
||||||
|
unsigned int fake_sni_seq_len;
|
||||||
|
#define VERBOSE_INFO 0
|
||||||
|
#define VERBOSE_DEBUG 1
|
||||||
|
#define VERBOSE_TRACE 2
|
||||||
int verbose;
|
int verbose;
|
||||||
|
int quic_drop;
|
||||||
|
#define SNI_DETECTION_PARSE 0
|
||||||
|
#define SNI_DETECTION_BRUTE 1
|
||||||
|
int sni_detection;
|
||||||
|
/* In milliseconds */
|
||||||
unsigned int seg2_delay;
|
unsigned int seg2_delay;
|
||||||
const char *domains_str;
|
const char *domains_str;
|
||||||
unsigned int domains_strlen;
|
unsigned int domains_strlen;
|
||||||
unsigned int all_domains;
|
unsigned int all_domains;
|
||||||
|
const char *fake_sni_pkt;
|
||||||
|
unsigned int fake_sni_pkt_sz;
|
||||||
|
unsigned int fk_winsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct config_t config;
|
extern struct config_t config;
|
||||||
|
|
||||||
#define MAX_THREADS 16
|
#define MAX_THREADS 16
|
||||||
@@ -43,23 +75,19 @@ extern struct config_t config;
|
|||||||
#define SEG2_DELAY 100
|
#define SEG2_DELAY 100
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FAKE_SNI_TTL 8
|
#define FAKE_TTL 8
|
||||||
|
|
||||||
// No fake SNI
|
// Will invalidate fake packets by out-of-ack_seq out-of-seq request
|
||||||
#define FKSN_STRAT_NONE 0
|
#define FAKE_STRAT_RAND_SEQ 1
|
||||||
// Will invalidate fake client hello by out-of-ack_seq out-of-seq request
|
// Will assume that GGC server is located further than FAKE_TTL
|
||||||
#define FKSN_STRAT_ACK_SEQ 1
|
// Thus, Fake packet will be eliminated automatically.
|
||||||
// Will assume that GGC server is located further than FAKE_SNI_TTL
|
#define FAKE_STRAT_TTL 2
|
||||||
// Thus, Fake Client Hello will be eliminated automatically.
|
#define FAKE_STRAT_PAST_SEQ 3
|
||||||
#define FKSN_STRAT_TTL 2
|
#define FAKE_STRAT_TCP_CHECK 4
|
||||||
|
|
||||||
|
|
||||||
#ifdef NO_FAKE_SNI
|
#ifndef FAKING_STRATEGY
|
||||||
#define FAKE_SNI_STRATEGY FKSN_STRAT_NONE
|
#define FAKING_STRATEGY FAKE_STRAT_RAND_SEQ
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FAKE_SNI_STRATEGY
|
|
||||||
#define FAKE_SNI_STRATEGY FKSN_STRAT_ACK_SEQ
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(SILENT) && !defined(KERNEL_SPACE)
|
#if !defined(SILENT) && !defined(KERNEL_SPACE)
|
||||||
@@ -68,6 +96,12 @@ extern struct config_t config;
|
|||||||
|
|
||||||
// The Maximum Transmission Unit size for rawsocket
|
// The Maximum Transmission Unit size for rawsocket
|
||||||
// Larger packets will be fragmented. Applicable for Chrome's kyber.
|
// Larger packets will be fragmented. Applicable for Chrome's kyber.
|
||||||
#define AVAILABLE_MTU 1384
|
#define AVAILABLE_MTU 1500
|
||||||
|
|
||||||
|
#define DEFAULT_QUEUE_NUM 537
|
||||||
|
|
||||||
|
#define MAX_PACKET_SIZE 8192
|
||||||
|
|
||||||
static const char defaul_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[] = "googlevideo.com,ggpht.com,ytimg.com,youtube.com,play.google.com,youtu.be,googleapis.com,googleusercontent.com,gstatic.com,l.google.com";
|
||||||
|
|
||||||
|
#endif /* YTB_CONFIG_H */
|
||||||
|
|||||||
39
logging.h
Normal file
39
logging.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef LOGGING_H
|
||||||
|
#define LOGGING_H
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define LOG_LEVEL (config.verbose)
|
||||||
|
|
||||||
|
#ifdef KERNEL_SPACE
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#define printf pr_info
|
||||||
|
#define perror pr_err
|
||||||
|
#define lgerror(msg, ret, ...) __extension__ ({ \
|
||||||
|
printf(msg ": %d\n", ##__VA_ARGS__, ret); \
|
||||||
|
})
|
||||||
|
#else
|
||||||
|
#include <stdio.h> // IWYU pragma: export
|
||||||
|
#include <errno.h>
|
||||||
|
#define lgerror(msg, ret, ...) __extension__ ({ \
|
||||||
|
errno = -(ret); \
|
||||||
|
printf(msg ": %s\n", ##__VA_ARGS__, strerror(errno)); \
|
||||||
|
})
|
||||||
|
#endif /* PROGRAM_SPACE */
|
||||||
|
|
||||||
|
|
||||||
|
#define lgdebugmsg(msg, ...) \
|
||||||
|
(LOG_LEVEL >= VERBOSE_DEBUG ? printf(msg "\n", ##__VA_ARGS__) : 0)
|
||||||
|
|
||||||
|
#define lgtracemsg(msg, ...) \
|
||||||
|
(LOG_LEVEL >= VERBOSE_TRACE ? printf(msg "\n", ##__VA_ARGS__) : 0)
|
||||||
|
|
||||||
|
#define lgtrace_start(msg, ...) \
|
||||||
|
(LOG_LEVEL >= VERBOSE_TRACE ? printf("[TRACE] " msg " ( ", ##__VA_ARGS__) : 0)
|
||||||
|
|
||||||
|
#define lgtrace_addp(msg, ...) \
|
||||||
|
(LOG_LEVEL >= VERBOSE_TRACE ? printf(msg", ", ##__VA_ARGS__) : 0)
|
||||||
|
|
||||||
|
#define lgtrace_end() \
|
||||||
|
(LOG_LEVEL >= VERBOSE_TRACE ? printf(") \n") : 0)
|
||||||
|
|
||||||
|
#endif /* LOGGING_H */
|
||||||
801
mangle.c
801
mangle.c
@@ -1,283 +1,484 @@
|
|||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
#include "types.h" // IWYU pragma: keep
|
||||||
#include "mangle.h"
|
#include "mangle.h"
|
||||||
#include "raw_replacements.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "quic.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
#ifdef KERNEL_SPACE
|
#ifndef KERNEL_SCOPE
|
||||||
#include <linux/printk.h>
|
#include <stdlib.h>
|
||||||
#include <linux/ip.h>
|
|
||||||
|
|
||||||
#define printf pr_info
|
|
||||||
#define perror pr_err
|
|
||||||
#define lgerror(msg, ret) (pr_err(msg ": %d\n", ret))
|
|
||||||
#else
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
|
|
||||||
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
|
|
||||||
|
|
||||||
typedef uint8_t __u8;
|
|
||||||
typedef uint32_t __u32;
|
|
||||||
typedef uint16_t __u16;
|
|
||||||
|
|
||||||
#define lgerror(msg, ret) __extension__ ({errno = -ret; perror(msg);})
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph)
|
int process_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) {
|
||||||
{
|
if (raw_payload_len > MAX_PACKET_SIZE) {
|
||||||
#ifdef KERNEL_SPACE
|
return PKT_ACCEPT;
|
||||||
uint32_t tcp_packet_len = ntohs(iph->tot_len) - (iph->ihl << 2);
|
|
||||||
tcph->check = 0;
|
|
||||||
tcph->check = csum_tcpudp_magic(
|
|
||||||
iph->saddr, iph->daddr, tcp_packet_len,
|
|
||||||
IPPROTO_TCP,
|
|
||||||
csum_partial(tcph, tcp_packet_len, 0));
|
|
||||||
#else
|
|
||||||
nfq_tcp_compute_checksum_ipv4(tcph, iph);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ip4_set_checksum(struct iphdr *iph)
|
|
||||||
{
|
|
||||||
#ifdef KERNEL_SPACE
|
|
||||||
iph->check = 0;
|
|
||||||
iph->check = ip_fast_csum(iph, iph->ihl);
|
|
||||||
#else
|
|
||||||
nfq_ip_set_checksum(iph);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int ip4_payload_split(__u8 *pkt, __u32 buflen,
|
|
||||||
struct iphdr **iph, __u32 *iph_len,
|
|
||||||
__u8 **payload, __u32 *plen) {
|
|
||||||
if (pkt == NULL || buflen < sizeof(struct iphdr)) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct iphdr *hdr = (struct iphdr *)pkt;
|
const struct iphdr *iph;
|
||||||
if (hdr->version != IPVERSION) return -EINVAL;
|
uint32_t iph_len;
|
||||||
|
const uint8_t *ip_payload;
|
||||||
|
uint32_t ip_payload_len;
|
||||||
|
|
||||||
__u32 hdr_len = hdr->ihl * 4;
|
|
||||||
__u32 pktlen = ntohs(hdr->tot_len);
|
|
||||||
if (buflen < pktlen || hdr_len > pktlen) return -EINVAL;
|
|
||||||
|
|
||||||
if (iph)
|
|
||||||
*iph = hdr;
|
|
||||||
if (iph_len)
|
|
||||||
*iph_len = hdr_len;
|
|
||||||
if (payload)
|
|
||||||
*payload = pkt + hdr_len;
|
|
||||||
if (plen)
|
|
||||||
*plen = pktlen - hdr_len;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tcp4_payload_split(__u8 *pkt, __u32 buflen,
|
|
||||||
struct iphdr **iph, __u32 *iph_len,
|
|
||||||
struct tcphdr **tcph, __u32 *tcph_len,
|
|
||||||
__u8 **payload, __u32 *plen) {
|
|
||||||
struct iphdr *hdr;
|
|
||||||
__u32 hdr_len;
|
|
||||||
struct tcphdr *thdr;
|
|
||||||
__u32 thdr_len;
|
|
||||||
|
|
||||||
__u8 *tcph_pl;
|
|
||||||
__u32 tcph_plen;
|
|
||||||
|
|
||||||
if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len,
|
|
||||||
&tcph_pl, &tcph_plen)){
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (
|
|
||||||
hdr->protocol != IPPROTO_TCP ||
|
|
||||||
tcph_plen < sizeof(struct tcphdr)) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
thdr = (struct tcphdr *)(tcph_pl);
|
|
||||||
thdr_len = thdr->doff * 4;
|
|
||||||
|
|
||||||
if (thdr_len > tcph_plen) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iph) *iph = hdr;
|
|
||||||
if (iph_len) *iph_len = hdr_len;
|
|
||||||
if (tcph) *tcph = thdr;
|
|
||||||
if (tcph_len) *tcph_len = thdr_len;
|
|
||||||
if (payload) *payload = tcph_pl + thdr_len;
|
|
||||||
if (plen) *plen = tcph_plen - thdr_len;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// split packet to two ipv4 fragments.
|
|
||||||
int ip4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset,
|
|
||||||
__u8 *frag1, __u32 *f1len,
|
|
||||||
__u8 *frag2, __u32 *f2len) {
|
|
||||||
|
|
||||||
struct iphdr *hdr;
|
|
||||||
const __u8 *payload;
|
|
||||||
__u32 plen;
|
|
||||||
__u32 hdr_len;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!frag1 || !f1len || !frag2 || !f2len)
|
ret = ip4_payload_split((uint8_t *)raw_payload, raw_payload_len,
|
||||||
return -EINVAL;
|
(struct iphdr **)&iph, &iph_len,
|
||||||
|
(uint8_t **)&ip_payload, &ip_payload_len);
|
||||||
|
|
||||||
if ((ret = ip4_payload_split(
|
|
||||||
(__u8 *)pkt, buflen,
|
if (ret < 0)
|
||||||
&hdr, &hdr_len, (__u8 **)&payload, &plen)) < 0) {
|
goto accept;
|
||||||
lgerror("ipv4_frag: TCP Header extract error", ret);
|
|
||||||
return -EINVAL;
|
switch (iph->protocol) {
|
||||||
|
case IPPROTO_TCP:
|
||||||
|
return process_tcp4_packet(raw_payload, raw_payload_len);
|
||||||
|
case IPPROTO_UDP:
|
||||||
|
return process_udp4_packet(raw_payload, raw_payload_len);
|
||||||
|
default:
|
||||||
|
goto accept;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plen <= payload_offset) {
|
accept:
|
||||||
return -EINVAL;
|
return PKT_ACCEPT;
|
||||||
|
drop:
|
||||||
|
return PKT_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len) {
|
||||||
|
const struct iphdr *iph;
|
||||||
|
uint32_t iph_len;
|
||||||
|
const struct tcphdr *tcph;
|
||||||
|
uint32_t tcph_len;
|
||||||
|
const uint8_t *data;
|
||||||
|
uint32_t dlen;
|
||||||
|
|
||||||
|
int ret = tcp4_payload_split((uint8_t *)raw_payload, raw_payload_len,
|
||||||
|
(struct iphdr **)&iph, &iph_len, (struct tcphdr **)&tcph, &tcph_len,
|
||||||
|
(uint8_t **)&data, &dlen);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
goto accept;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload_offset & ((1 << 3) - 1)) {
|
struct tls_verdict vrd = analyze_tls_data(data, dlen);
|
||||||
lgerror("ipv4_frag: Payload offset MUST be a multiply of 8!", -EINVAL);
|
|
||||||
|
|
||||||
return -EINVAL;
|
if (vrd.target_sni) {
|
||||||
|
lgdebugmsg("Target SNI detected: %.*s", vrd.sni_len, data + vrd.sni_offset);
|
||||||
|
|
||||||
|
uint8_t payload[MAX_PACKET_SIZE];
|
||||||
|
uint32_t payload_len = raw_payload_len;
|
||||||
|
memcpy(payload, raw_payload, raw_payload_len);
|
||||||
|
|
||||||
|
struct iphdr *iph;
|
||||||
|
uint32_t iph_len;
|
||||||
|
struct tcphdr *tcph;
|
||||||
|
uint32_t tcph_len;
|
||||||
|
uint8_t *data;
|
||||||
|
uint32_t dlen;
|
||||||
|
|
||||||
|
int ret = tcp4_payload_split(payload, payload_len,
|
||||||
|
&iph, &iph_len, &tcph, &tcph_len,
|
||||||
|
&data, &dlen);
|
||||||
|
|
||||||
|
if (config.fk_winsize) {
|
||||||
|
tcph->window = htons(config.fk_winsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
ip4_set_checksum(iph);
|
||||||
|
tcp4_set_checksum(tcph, iph);
|
||||||
|
|
||||||
|
|
||||||
|
if (dlen > 1480 && config.verbose) {
|
||||||
|
lgdebugmsg("WARNING! Client Hello packet is too big and may cause issues!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.fake_sni) {
|
||||||
|
post_fake_sni(iph, iph_len, tcph, tcph_len,
|
||||||
|
config.fake_sni_seq_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ipd_offset;
|
||||||
|
size_t mid_offset;
|
||||||
|
|
||||||
|
switch (config.fragmentation_strategy) {
|
||||||
|
case FRAG_STRAT_TCP: {
|
||||||
|
ipd_offset = vrd.sni_offset;
|
||||||
|
mid_offset = ipd_offset + vrd.sni_len / 2;
|
||||||
|
|
||||||
|
uint32_t poses[2];
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
if (config.frag_sni_pos && dlen > config.frag_sni_pos) {
|
||||||
|
poses[cnt++] = config.frag_sni_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_middle_sni) {
|
||||||
|
poses[cnt++] = mid_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cnt > 1 && poses[0] > poses[1]) {
|
||||||
|
uint32_t tmp = poses[0];
|
||||||
|
poses[0] = poses[1];
|
||||||
|
poses[1] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = send_tcp4_frags(payload, payload_len, poses, cnt, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("tcp4 send frags", ret);
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case FRAG_STRAT_IP: {
|
||||||
|
ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset;
|
||||||
|
mid_offset = ipd_offset + vrd.sni_len / 2;
|
||||||
|
mid_offset += 8 - mid_offset % 8;
|
||||||
|
|
||||||
|
uint32_t poses[2];
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
if (config.frag_sni_pos && dlen > config.frag_sni_pos) {
|
||||||
|
poses[cnt] = config.frag_sni_pos + ((char *)data - (char *)tcph);
|
||||||
|
poses[cnt] += 8 - poses[cnt] % 8;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_middle_sni) {
|
||||||
|
poses[cnt++] = mid_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cnt > 1 && poses[0] > poses[1]) {
|
||||||
|
uint32_t tmp = poses[0];
|
||||||
|
poses[0] = poses[1];
|
||||||
|
poses[1] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = send_ip4_frags(payload, payload_len, poses, cnt, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("ip4 send frags", ret);
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = instance_config.send_raw_packet(payload, payload_len);
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("raw pack send", ret);
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
__u32 f1_plen = payload_offset;
|
accept:
|
||||||
__u32 f1_dlen = f1_plen + hdr_len;
|
return PKT_ACCEPT;
|
||||||
|
drop:
|
||||||
|
return PKT_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
__u32 f2_plen = plen - payload_offset;
|
int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen) {
|
||||||
__u32 f2_dlen = f2_plen + hdr_len;
|
const struct iphdr *iph;
|
||||||
|
uint32_t iph_len;
|
||||||
|
const struct udphdr *udph;
|
||||||
|
const uint8_t *data;
|
||||||
|
uint32_t dlen;
|
||||||
|
|
||||||
if (*f1len < f1_dlen || *f2len < f2_dlen) {
|
int ret = udp4_payload_split((uint8_t *)pkt, pktlen,
|
||||||
return -ENOMEM;
|
(struct iphdr **)&iph, &iph_len,
|
||||||
|
(struct udphdr **)&udph,
|
||||||
|
(uint8_t **)&data, &dlen);
|
||||||
|
|
||||||
|
lgtrace_start("Got udp packet");
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
lgtrace_addp("undefined");
|
||||||
|
goto accept;
|
||||||
}
|
}
|
||||||
*f1len = f1_dlen;
|
|
||||||
*f2len = f2_dlen;
|
|
||||||
|
|
||||||
memcpy(frag1, hdr, hdr_len);
|
if (dlen > 10 && config.verbose >= VERBOSE_TRACE) {
|
||||||
memcpy(frag2, hdr, hdr_len);
|
printf("UDP payload start: [ ");
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
printf("%02x ", data[i]);
|
||||||
|
}
|
||||||
|
printf("], ");
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(frag1 + hdr_len, payload, f1_plen);
|
lgtrace_addp("QUIC probe");
|
||||||
memcpy(frag2 + hdr_len, payload + payload_offset, f2_plen);
|
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);
|
||||||
|
|
||||||
struct iphdr *f1_hdr = (void *)frag1;
|
if (ret < 0) {
|
||||||
struct iphdr *f2_hdr = (void *)frag2;
|
lgtrace_addp("undefined type");
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
__u16 f1_frag_off = ntohs(f1_hdr->frag_off);
|
lgtrace_addp("QUIC detected");
|
||||||
__u16 f2_frag_off = ntohs(f2_hdr->frag_off);
|
uint8_t qtype = qch->type;
|
||||||
|
|
||||||
f1_frag_off &= IP_OFFMASK;
|
if (config.quic_drop) {
|
||||||
f1_frag_off |= IP_MF;
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
if ((f2_frag_off & ~IP_OFFMASK) == IP_MF) {
|
if (qch->version == QUIC_V1)
|
||||||
f2_frag_off &= IP_OFFMASK;
|
qtype = quic_convtype_v1(qtype);
|
||||||
f2_frag_off |= IP_MF;
|
else if (qch->version == QUIC_V2)
|
||||||
|
qtype = quic_convtype_v2(qtype);
|
||||||
|
|
||||||
|
if (qtype != QUIC_INITIAL_TYPE) {
|
||||||
|
lgtrace_addp("quic message type: %d", qtype);
|
||||||
|
goto accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
lgtrace_addp("quic initial message");
|
||||||
|
|
||||||
|
accept:
|
||||||
|
lgtrace_addp("accepted");
|
||||||
|
lgtrace_end();
|
||||||
|
|
||||||
|
return PKT_ACCEPT;
|
||||||
|
drop:
|
||||||
|
lgtrace_addp("dropped");
|
||||||
|
lgtrace_end();
|
||||||
|
|
||||||
|
return PKT_DROP;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_ip4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_sz, uint32_t dvs) {
|
||||||
|
if (poses_sz == 0) {
|
||||||
|
if (config.seg2_delay && ((dvs > 0) ^ config.frag_sni_reverse)) {
|
||||||
|
if (!instance_config.send_delayed_packet) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance_config.send_delayed_packet(
|
||||||
|
packet, pktlen, config.seg2_delay);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return instance_config.send_raw_packet(
|
||||||
|
packet, pktlen);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
f2_frag_off &= IP_OFFMASK;
|
uint8_t frag1[MAX_PACKET_SIZE];
|
||||||
|
uint8_t frag2[MAX_PACKET_SIZE];
|
||||||
|
uint32_t f1len = MAX_PACKET_SIZE;
|
||||||
|
uint32_t f2len = MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (dvs > poses[0]) {
|
||||||
|
lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = ip4_frag(packet, pktlen, poses[0] - dvs,
|
||||||
|
frag1, &f1len, frag2, &f2len);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_reverse)
|
||||||
|
goto send_frag2;
|
||||||
|
send_frag1:
|
||||||
|
ret = send_ip4_frags(frag1, f1len, NULL, 0, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_reverse)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
send_frag2:
|
||||||
|
dvs += poses[0];
|
||||||
|
ret = send_ip4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_reverse)
|
||||||
|
goto send_frag1;
|
||||||
}
|
}
|
||||||
|
|
||||||
f2_frag_off += (__u16)payload_offset / 8;
|
out:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
f1_hdr->frag_off = htons(f1_frag_off);
|
int send_tcp4_frags(const uint8_t *packet, uint32_t pktlen, const uint32_t *poses, uint32_t poses_sz, uint32_t dvs) {
|
||||||
f1_hdr->tot_len = htons(f1_dlen);
|
if (poses_sz == 0) {
|
||||||
|
if (config.seg2_delay && ((dvs > 0) ^ config.frag_sni_reverse)) {
|
||||||
|
if (!instance_config.send_delayed_packet) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
f2_hdr->frag_off = htons(f2_frag_off);
|
instance_config.send_delayed_packet(
|
||||||
f2_hdr->tot_len = htons(f2_dlen);
|
packet, pktlen, config.seg2_delay);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return instance_config.send_raw_packet(
|
||||||
|
packet, pktlen);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint8_t frag1[MAX_PACKET_SIZE];
|
||||||
|
uint8_t frag2[MAX_PACKET_SIZE];
|
||||||
|
uint8_t fake_pad[MAX_PACKET_SIZE];
|
||||||
|
uint32_t f1len = MAX_PACKET_SIZE;
|
||||||
|
uint32_t f2len = MAX_PACKET_SIZE;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (dvs > poses[0]) {
|
||||||
|
lgerror("send_frags: Recursive dvs(%d) is more than poses0(%d)", -EINVAL, dvs, poses[0]);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = tcp4_frag(packet, pktlen, poses[0] - dvs,
|
||||||
|
frag1, &f1len, frag2, &f2len);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("send_frags: frag: with context packet with size %d, position: %d, recursive dvs: %d", ret, pktlen, poses[0], dvs);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_reverse)
|
||||||
|
goto send_frag2;
|
||||||
|
|
||||||
|
send_frag1:
|
||||||
|
{
|
||||||
|
ret = send_tcp4_frags(frag1, f1len, NULL, 0, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_reverse)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_fake:
|
||||||
|
if (config.frag_sni_faked) {
|
||||||
|
uint32_t iphfl, tcphfl;
|
||||||
|
ret = tcp4_payload_split(frag2, f2len, NULL, &iphfl, NULL, &tcphfl, NULL, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("Invalid frag2", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
memcpy(fake_pad, frag2, iphfl + tcphfl);
|
||||||
|
memset(fake_pad + iphfl + tcphfl, 0, f2len - iphfl - tcphfl);
|
||||||
|
ret = fail4_packet(fake_pad, f2len);
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("Failed to fail packet", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
ret = send_tcp4_frags(fake_pad, f2len, NULL, 0, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_reverse)
|
||||||
|
goto send_frag1;
|
||||||
|
|
||||||
|
send_frag2:
|
||||||
|
{
|
||||||
|
dvs += poses[0];
|
||||||
|
ret = send_tcp4_frags(frag2, f2len, poses + 1, poses_sz - 1, dvs);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.frag_sni_reverse)
|
||||||
|
goto send_fake;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int post_fake_sni(const struct iphdr *iph, unsigned int iph_len,
|
||||||
|
const struct tcphdr *tcph, unsigned int tcph_len,
|
||||||
|
unsigned char sequence_len) {
|
||||||
|
uint8_t rfsiph[60];
|
||||||
|
uint8_t rfstcph[60];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
memcpy(rfsiph, iph, iph_len);
|
||||||
|
memcpy(rfstcph, tcph, tcph_len);
|
||||||
|
|
||||||
|
struct iphdr *fsiph = (void *)rfsiph;
|
||||||
|
struct tcphdr *fstcph = (void *)rfstcph;
|
||||||
|
|
||||||
|
for (int i = 0; i < sequence_len; i++) {
|
||||||
|
uint8_t fake_sni[MAX_PACKET_SIZE];
|
||||||
|
uint32_t fsn_len = MAX_PACKET_SIZE;
|
||||||
|
ret = gen_fake_sni(fsiph, fstcph, fake_sni, &fsn_len);
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("gen_fake_sni", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = instance_config.send_raw_packet(fake_sni, fsn_len);
|
||||||
|
if (ret < 0) {
|
||||||
|
lgerror("send fake sni", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t iph_len;
|
||||||
|
uint32_t tcph_len;
|
||||||
|
uint32_t plen;
|
||||||
|
tcp4_payload_split(
|
||||||
|
fake_sni, fsn_len,
|
||||||
|
&fsiph, &iph_len, &fstcph, &tcph_len,
|
||||||
|
NULL, &plen);
|
||||||
|
|
||||||
|
|
||||||
if (config.verbose)
|
fstcph->seq = htonl(ntohl(fstcph->seq) + plen);
|
||||||
printf("Packet split in portion %u %u\n", f1_plen, f2_plen);
|
memcpy(rfsiph, fsiph, iph_len);
|
||||||
|
memcpy(rfstcph, fstcph, tcph_len);
|
||||||
|
fsiph = (void *)rfsiph;
|
||||||
|
fstcph = (void *)rfstcph;
|
||||||
|
|
||||||
ip4_set_checksum(f1_hdr);
|
}
|
||||||
ip4_set_checksum(f2_hdr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// split packet to two tcp-on-ipv4 segments.
|
void z_function(const char *str, int *zbuf, size_t len) {
|
||||||
int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset,
|
zbuf[0] = len;
|
||||||
__u8 *seg1, __u32 *s1len,
|
|
||||||
__u8 *seg2, __u32 *s2len) {
|
|
||||||
|
|
||||||
struct iphdr *hdr;
|
ssize_t lh = 0, rh = 1;
|
||||||
__u32 hdr_len;
|
for (ssize_t i = 1; i < len; i++) {
|
||||||
struct tcphdr *tcph;
|
zbuf[i] = 0;
|
||||||
__u32 tcph_len;
|
if (i < rh) {
|
||||||
__u32 plen;
|
zbuf[i] = min(zbuf[i - lh], rh - i);
|
||||||
const __u8 *payload;
|
}
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!seg1 || !s1len || !seg2 || !s2len)
|
while (i + zbuf[i] < len && str[zbuf[i]] == str[i + zbuf[i]])
|
||||||
return -EINVAL;
|
zbuf[i]++;
|
||||||
|
|
||||||
if ((ret = tcp4_payload_split((__u8 *)pkt, buflen,
|
if (i + zbuf[i] > rh) {
|
||||||
&hdr, &hdr_len,
|
lh = i;
|
||||||
&tcph, &tcph_len,
|
rh = i + zbuf[i];
|
||||||
(__u8 **)&payload, &plen)) < 0) {
|
}
|
||||||
lgerror("tcp4_frag: tcp4_payload_split", ret);
|
|
||||||
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (
|
|
||||||
ntohs(hdr->frag_off) & IP_MF ||
|
|
||||||
ntohs(hdr->frag_off) & IP_OFFMASK) {
|
|
||||||
printf("tcp4_frag: frag value: %d\n",
|
|
||||||
ntohs(hdr->frag_off));
|
|
||||||
lgerror("tcp4_frag: ip fragmentation is set", -EINVAL);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (plen <= payload_offset) {
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
__u32 s1_plen = payload_offset;
|
|
||||||
__u32 s1_dlen = s1_plen + hdr_len + tcph_len;
|
|
||||||
|
|
||||||
__u32 s2_plen = plen - payload_offset;
|
|
||||||
__u32 s2_dlen = s2_plen + hdr_len + tcph_len;
|
|
||||||
|
|
||||||
if (*s1len < s1_dlen || *s2len < s2_dlen)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
*s1len = s1_dlen;
|
|
||||||
*s2len = s2_dlen;
|
|
||||||
|
|
||||||
memcpy(seg1, hdr, hdr_len);
|
|
||||||
memcpy(seg2, hdr, hdr_len);
|
|
||||||
|
|
||||||
memcpy(seg1 + hdr_len, tcph, tcph_len);
|
|
||||||
memcpy(seg2 + hdr_len, tcph, tcph_len);
|
|
||||||
|
|
||||||
memcpy(seg1 + hdr_len + tcph_len, payload, s1_plen);
|
|
||||||
memcpy(seg2 + hdr_len + tcph_len, payload + payload_offset, s2_plen);
|
|
||||||
|
|
||||||
struct iphdr *s1_hdr = (void *)seg1;
|
|
||||||
struct iphdr *s2_hdr = (void *)seg2;
|
|
||||||
|
|
||||||
struct tcphdr *s1_tcph = (void *)(seg1 + hdr_len);
|
|
||||||
struct tcphdr *s2_tcph = (void *)(seg2 + hdr_len);
|
|
||||||
|
|
||||||
s1_hdr->tot_len = htons(s1_dlen);
|
|
||||||
s2_hdr->tot_len = htons(s2_dlen);
|
|
||||||
|
|
||||||
s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset);
|
|
||||||
|
|
||||||
if (config.verbose)
|
|
||||||
printf("Packet split in portion %u %u\n", s1_plen, s2_plen);
|
|
||||||
|
|
||||||
tcp4_set_checksum(s1_tcph, s1_hdr);
|
|
||||||
tcp4_set_checksum(s2_tcph, s2_hdr);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define TLS_CONTENT_TYPE_HANDSHAKE 0x16
|
#define TLS_CONTENT_TYPE_HANDSHAKE 0x16
|
||||||
@@ -285,9 +486,9 @@ int tcp4_frag(const __u8 *pkt, __u32 buflen, __u32 payload_offset,
|
|||||||
#define TLS_EXTENSION_SNI 0x0000
|
#define TLS_EXTENSION_SNI 0x0000
|
||||||
#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d
|
#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d
|
||||||
|
|
||||||
typedef __u8 uint8_t;
|
typedef uint8_t uint8_t;
|
||||||
typedef __u32 uint32_t;
|
typedef uint32_t uint32_t;
|
||||||
typedef __u16 uint16_t;
|
typedef uint16_t uint16_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes tls payload of the tcp request.
|
* Processes tls payload of the tcp request.
|
||||||
@@ -295,11 +496,11 @@ typedef __u16 uint16_t;
|
|||||||
* data Payload data of TCP.
|
* data Payload data of TCP.
|
||||||
* dlen Length of `data`.
|
* dlen Length of `data`.
|
||||||
*/
|
*/
|
||||||
struct verdict analyze_tls_data(
|
struct tls_verdict analyze_tls_data(
|
||||||
const uint8_t *data,
|
const uint8_t *data,
|
||||||
uint32_t dlen)
|
uint32_t dlen)
|
||||||
{
|
{
|
||||||
struct verdict vrd = {0};
|
struct tls_verdict vrd = {0};
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
const uint8_t *data_end = data + dlen;
|
const uint8_t *data_end = data + dlen;
|
||||||
@@ -313,12 +514,16 @@ struct verdict analyze_tls_data(
|
|||||||
uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3));
|
uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3));
|
||||||
const uint8_t *message_length_ptr = msgData + 3;
|
const uint8_t *message_length_ptr = msgData + 3;
|
||||||
|
|
||||||
|
if (tls_vmajor != 0x03) goto nextMessage;
|
||||||
|
|
||||||
if (i + 5 + message_length > dlen) break;
|
if (i + 5 > dlen) break;
|
||||||
|
|
||||||
if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE)
|
if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE)
|
||||||
goto nextMessage;
|
goto nextMessage;
|
||||||
|
|
||||||
|
if (config.sni_detection == SNI_DETECTION_BRUTE) {
|
||||||
|
goto brute;
|
||||||
|
}
|
||||||
|
|
||||||
const uint8_t *handshakeProto = msgData + 5;
|
const uint8_t *handshakeProto = msgData + 5;
|
||||||
|
|
||||||
@@ -356,7 +561,7 @@ struct verdict analyze_tls_data(
|
|||||||
|
|
||||||
const uint8_t *extensionsPtr = msgPtr;
|
const uint8_t *extensionsPtr = msgPtr;
|
||||||
const uint8_t *extensions_end = extensionsPtr + extensionsLen;
|
const uint8_t *extensions_end = extensionsPtr + extensionsLen;
|
||||||
if (extensions_end > data_end) break;
|
if (extensions_end > data_end) extensions_end = data_end;
|
||||||
|
|
||||||
while (extensionsPtr < extensions_end) {
|
while (extensionsPtr < extensions_end) {
|
||||||
const uint8_t *extensionPtr = extensionsPtr;
|
const uint8_t *extensionPtr = extensionsPtr;
|
||||||
@@ -440,8 +645,54 @@ nextMessage:
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
return vrd;
|
return vrd;
|
||||||
|
|
||||||
|
brute:
|
||||||
|
if (config.all_domains) {
|
||||||
|
vrd.target_sni = 1;
|
||||||
|
vrd.sni_len = 0;
|
||||||
|
vrd.sni_offset = dlen / 2;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int j = 0;
|
||||||
|
for (unsigned int i = 0; i <= config.domains_strlen; i++) {
|
||||||
|
if ( i > j &&
|
||||||
|
(i == config.domains_strlen ||
|
||||||
|
config.domains_str[i] == '\0' ||
|
||||||
|
config.domains_str[i] == ',' ||
|
||||||
|
config.domains_str[i] == '\n' )) {
|
||||||
|
|
||||||
|
uint8_t buf[MAX_PACKET_SIZE];
|
||||||
|
int zbuf[MAX_PACKET_SIZE];
|
||||||
|
unsigned int domain_len = (i - j);
|
||||||
|
const char *domain_startp = config.domains_str + j;
|
||||||
|
|
||||||
|
if (domain_len + dlen + 1> MAX_PACKET_SIZE) continue;
|
||||||
|
|
||||||
|
memcpy(buf, domain_startp, domain_len);
|
||||||
|
memcpy(buf + domain_len, "#", 1);
|
||||||
|
memcpy(buf + domain_len + 1, data, dlen);
|
||||||
|
|
||||||
|
z_function((char *)buf, zbuf, domain_len + 1 + dlen);
|
||||||
|
|
||||||
|
for (unsigned int k = 0; k < dlen; k++) {
|
||||||
|
if (zbuf[k] == domain_len) {
|
||||||
|
vrd.target_sni = 1;
|
||||||
|
vrd.sni_len = domain_len;
|
||||||
|
vrd.sni_offset = (k - domain_len - 1);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
j = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph,
|
int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph,
|
||||||
uint8_t *buf, uint32_t *buflen) {
|
uint8_t *buf, uint32_t *buflen) {
|
||||||
|
|
||||||
@@ -449,41 +700,69 @@ int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
int ip_len = iph->ihl * 4;
|
int ip_len = iph->ihl * 4;
|
||||||
size_t data_len = sizeof(fake_sni);
|
int tcph_len = tcph->doff * 4;
|
||||||
|
|
||||||
size_t dlen = data_len + ip_len;
|
const char *data = config.fake_sni_pkt;
|
||||||
|
size_t data_len = config.fake_sni_pkt_sz;
|
||||||
|
|
||||||
|
size_t dlen = ip_len + tcph_len + data_len;
|
||||||
|
|
||||||
if (*buflen < dlen)
|
if (*buflen < dlen)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
*buflen = dlen;
|
|
||||||
|
|
||||||
memcpy(buf, iph, ip_len);
|
memcpy(buf, iph, ip_len);
|
||||||
memcpy(buf + ip_len, fake_sni, data_len);
|
memcpy(buf + ip_len, tcph, tcph_len);
|
||||||
|
memcpy(buf + ip_len + tcph_len, data, data_len);
|
||||||
|
|
||||||
struct iphdr *niph = (struct iphdr *)buf;
|
struct iphdr *niph = (struct iphdr *)buf;
|
||||||
|
struct tcphdr *ntcph = (struct tcphdr *)(buf + ip_len);
|
||||||
|
|
||||||
niph->protocol = IPPROTO_TCP;
|
niph->protocol = IPPROTO_TCP;
|
||||||
niph->tot_len = htons(dlen);
|
niph->tot_len = htons(dlen);
|
||||||
|
|
||||||
int ret = 0;
|
fail4_packet(buf, *buflen);
|
||||||
struct tcphdr *ntcph = (struct tcphdr *)(buf + ip_len);
|
|
||||||
|
|
||||||
#ifdef KERNEL_SPACE
|
*buflen = dlen;
|
||||||
ntcph->dest = tcph->dest;
|
return 0;
|
||||||
ntcph->source = tcph->source;
|
}
|
||||||
#else
|
|
||||||
ntcph->th_dport = tcph->th_dport;
|
|
||||||
ntcph->th_sport = tcph->th_sport;
|
|
||||||
|
|
||||||
if (config.fake_sni_strategy == FKSN_STRAT_TTL) {
|
int fail4_packet(uint8_t *payload, uint32_t plen) {
|
||||||
ntcph->seq = tcph->seq;
|
struct iphdr *iph;
|
||||||
ntcph->ack_seq = tcph->ack_seq;
|
uint32_t iph_len;
|
||||||
niph->ttl = config.fake_sni_ttl;
|
struct tcphdr *tcph;
|
||||||
|
uint32_t tcph_len;
|
||||||
|
uint8_t *data;
|
||||||
|
uint32_t dlen;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = tcp4_payload_split(payload, plen,
|
||||||
|
&iph, &iph_len, &tcph, &tcph_len,
|
||||||
|
&data, &dlen);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.faking_strategy == FAKE_STRAT_RAND_SEQ) {
|
||||||
|
#ifdef KERNEL_SCOPE
|
||||||
|
tcph->seq = 124;
|
||||||
|
tcph->ack_seq = 124;
|
||||||
|
#else
|
||||||
|
tcph->seq = random();
|
||||||
|
tcph->ack_seq = random();
|
||||||
#endif
|
#endif
|
||||||
|
} else if (config.faking_strategy == FAKE_STRAT_PAST_SEQ) {
|
||||||
|
tcph->seq = htonl(ntohl(tcph->seq) - dlen);
|
||||||
|
} else if (config.faking_strategy == FAKE_STRAT_TTL) {
|
||||||
|
iph->ttl = config.faking_ttl;
|
||||||
|
}
|
||||||
|
|
||||||
ip4_set_checksum(niph);
|
ip4_set_checksum(iph);
|
||||||
tcp4_set_checksum(ntcph, niph);
|
tcp4_set_checksum(tcph, iph);
|
||||||
|
|
||||||
|
if (config.faking_strategy == FAKE_STRAT_TCP_CHECK) {
|
||||||
|
tcph->check += 1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
119
mangle.h
119
mangle.h
@@ -1,66 +1,81 @@
|
|||||||
#ifndef YU_MANGLE_H
|
#ifndef YU_MANGLE_H
|
||||||
#define YU_MANGLE_H
|
#define YU_MANGLE_H
|
||||||
|
|
||||||
#ifdef KERNEL_SPACE
|
#include "types.h"
|
||||||
#include <linux/types.h>
|
|
||||||
typedef __u8 uint8_t;
|
|
||||||
typedef __u32 uint32_t;
|
|
||||||
|
|
||||||
#include <linux/string.h>
|
/**
|
||||||
#include <linux/errno.h>
|
* Result of analyze_tls_data function
|
||||||
#include <linux/stddef.h>
|
*/
|
||||||
#include <linux/net.h>
|
struct tls_verdict {
|
||||||
#include <linux/in.h>
|
|
||||||
#include <linux/ip.h>
|
|
||||||
#include <linux/tcp.h>
|
|
||||||
|
|
||||||
#include <asm/byteorder.h>
|
|
||||||
|
|
||||||
/* from <netinet/ip.h> */
|
|
||||||
#define IP_RF 0x8000 /* reserved fragment flag */
|
|
||||||
#define IP_DF 0x4000 /* dont fragment flag */
|
|
||||||
#define IP_MF 0x2000 /* more fragments flag */
|
|
||||||
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
|
|
||||||
#else
|
|
||||||
#define USER_SPACE
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <netinet/ip.h>
|
|
||||||
#include <netinet/tcp.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct verdict {
|
|
||||||
int target_sni; /* google video hello packet */
|
int target_sni; /* google video hello packet */
|
||||||
int sni_offset; /* offset from start of tcp _payload_ */
|
int sni_offset; /* offset from start of tcp _payload_ */
|
||||||
int sni_len;
|
int sni_len;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct verdict analyze_tls_data(const uint8_t *data, uint32_t dlen);
|
/**
|
||||||
|
* Processes the packet and finds TLS Client Hello information inside it.
|
||||||
|
* data pointer points to start of TLS Message (TCP Payload)
|
||||||
|
*/
|
||||||
|
struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen);
|
||||||
|
|
||||||
int ip4_frag(const uint8_t *pkt, uint32_t pktlen,
|
|
||||||
uint32_t payload_offset,
|
|
||||||
uint8_t *frag1, uint32_t *f1len,
|
|
||||||
uint8_t *frag2, uint32_t *f2len);
|
|
||||||
|
|
||||||
int tcp4_frag(const uint8_t *pkt, uint32_t pktlen,
|
|
||||||
uint32_t payload_offset,
|
|
||||||
uint8_t *seg1, uint32_t *s1len,
|
|
||||||
uint8_t *seg2, uint32_t *s2len);
|
|
||||||
|
|
||||||
int ip4_payload_split(uint8_t *pkt, uint32_t buflen,
|
|
||||||
struct iphdr **iph, uint32_t *iph_len,
|
|
||||||
uint8_t **payload, uint32_t *plen);
|
|
||||||
|
|
||||||
int tcp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
|
||||||
struct iphdr **iph, uint32_t *iph_len,
|
|
||||||
struct tcphdr **tcph, uint32_t *tcph_len,
|
|
||||||
uint8_t **payload, uint32_t *plen);
|
|
||||||
|
|
||||||
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph);
|
|
||||||
void ip4_set_checksum(struct iphdr *iph);
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates fake client hello message
|
||||||
|
*/
|
||||||
int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph,
|
int gen_fake_sni(const struct iphdr *iph, const struct tcphdr *tcph,
|
||||||
uint8_t *buf, uint32_t *buflen);
|
uint8_t *buf, uint32_t *buflen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
int fail4_packet(uint8_t *payload, uint32_t plen);
|
||||||
|
|
||||||
|
#define PKT_ACCEPT 0
|
||||||
|
#define PKT_DROP 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the packet and returns verdict.
|
||||||
|
* This is the primary function that traverses the packet.
|
||||||
|
*/
|
||||||
|
int process_packet(const uint8_t *packet, uint32_t packet_len);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processe the TCP packet.
|
||||||
|
* Returns verdict.
|
||||||
|
*/
|
||||||
|
int process_tcp4_packet(const uint8_t *raw_payload, uint32_t raw_payload_len);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the UDP packet.
|
||||||
|
* Returns verdict.
|
||||||
|
*/
|
||||||
|
int process_udp4_packet(const uint8_t *pkt, uint32_t pktlen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends fake client hello.
|
||||||
|
*/
|
||||||
|
int post_fake_sni(const struct iphdr *iph, unsigned int iph_len,
|
||||||
|
const struct tcphdr *tcph, unsigned int tcph_len,
|
||||||
|
unsigned char sequence_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits packet by poses and posts.
|
||||||
|
* Poses are relative to start of TCP payload.
|
||||||
|
* dvs used internally and should be zero.
|
||||||
|
*/
|
||||||
|
int send_tcp4_frags(
|
||||||
|
const uint8_t *packet, uint32_t pktlen,
|
||||||
|
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits packet by poses and posts.
|
||||||
|
* Poses are relative to start of TCP payload.
|
||||||
|
* dvs used internally and should be zero.
|
||||||
|
*/
|
||||||
|
int send_ip4_frags(
|
||||||
|
const uint8_t *packet, uint32_t pktlen,
|
||||||
|
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||||
#endif /* YU_MANGLE_H */
|
#endif /* YU_MANGLE_H */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/sbin/nft -f
|
#!/usr/sbin/nft -f
|
||||||
# This file
|
# This file install nftables rules for openwrt
|
||||||
|
|
||||||
insert rule inet fw4 mangle_forward tcp dport 443 ct original packets < 20 counter queue num 537 bypass
|
add rule inet fw4 mangle_forward tcp dport 443 ct original packets < 20 counter queue num 537 bypass
|
||||||
insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept
|
insert rule inet fw4 output mark and 0x8000 == 0x8000 counter accept
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ USE_PROCD=1
|
|||||||
START=50
|
START=50
|
||||||
STOP=50
|
STOP=50
|
||||||
|
|
||||||
|
|
||||||
# Openwrt procd script: https://openwrt.org/docs/guide-developer/procd-init-script-example
|
# Openwrt procd script: https://openwrt.org/docs/guide-developer/procd-init-script-example
|
||||||
# The program should be put into /usr/bin/
|
# The program should be put into /usr/bin/
|
||||||
# This file should be put into /etc/init.d/
|
# This file should be put into /etc/init.d/
|
||||||
|
|
||||||
start_service() {
|
start_service() {
|
||||||
procd_open_instance
|
procd_open_instance
|
||||||
procd_set_param command /usr/bin/youtubeUnblock 537
|
procd_set_param command /usr/bin/youtubeUnblock
|
||||||
|
|
||||||
procd_set_param nice -20
|
procd_set_param nice -20
|
||||||
|
|
||||||
|
|||||||
140
quic.c
Normal file
140
quic.c
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include "quic.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packet number.
|
||||||
|
*/
|
||||||
|
struct quic_pnumber {
|
||||||
|
uint8_t d1;
|
||||||
|
uint8_t d2;
|
||||||
|
uint8_t d3;
|
||||||
|
uint8_t d4;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint64_t quic_parse_varlength(uint8_t *variable, uint64_t *mlen) {
|
||||||
|
if (mlen && *mlen == 0) return 0;
|
||||||
|
uint64_t vr = (*variable & 0x3F);
|
||||||
|
uint8_t len = 1 << (*variable >> 6);
|
||||||
|
|
||||||
|
if (mlen) {
|
||||||
|
if (*mlen < len) return 0;
|
||||||
|
*mlen = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
++variable;
|
||||||
|
for (uint8_t i = 1; i < len; i++) {
|
||||||
|
vr = (vr << 8) + *variable;
|
||||||
|
++variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vr;
|
||||||
|
}
|
||||||
|
|
||||||
|
int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len,
|
||||||
|
struct quic_lhdr **qch, uint32_t *qch_len,
|
||||||
|
struct quic_cids *qci,
|
||||||
|
uint8_t **payload, uint32_t *plen) {
|
||||||
|
if ( raw_payload == NULL ||
|
||||||
|
raw_payload_len < sizeof(struct quic_lhdr))
|
||||||
|
goto invalid_packet;
|
||||||
|
|
||||||
|
struct quic_lhdr *nqch = (struct quic_lhdr *)raw_payload;
|
||||||
|
uint32_t left_len = raw_payload_len - sizeof(struct quic_lhdr);
|
||||||
|
uint8_t *cur_rawptr = raw_payload + sizeof(struct quic_lhdr);
|
||||||
|
if (!nqch->fixed) {
|
||||||
|
lgtrace_addp("quic fixed uset");
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t found = 0;
|
||||||
|
for (uint8_t i = 0; i < sizeof(supported_versions); i++) {
|
||||||
|
if (ntohl(nqch->version) == supported_versions[i]) {
|
||||||
|
found = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
lgtrace_addp("quic version undefined %d", ntohl(nqch->version));
|
||||||
|
return -EPROTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
lgtrace_addp("quic version valid %d", ntohl(nqch->version));
|
||||||
|
|
||||||
|
if (left_len < 2) goto invalid_packet;
|
||||||
|
struct quic_cids nqci = {0};
|
||||||
|
|
||||||
|
nqci.dst_len = *cur_rawptr++;
|
||||||
|
left_len--;
|
||||||
|
if (left_len < nqci.dst_len) goto invalid_packet;
|
||||||
|
nqci.dst_id = cur_rawptr;
|
||||||
|
cur_rawptr += nqci.dst_len;
|
||||||
|
left_len -= nqci.dst_len;
|
||||||
|
|
||||||
|
nqci.src_len = *cur_rawptr++;
|
||||||
|
left_len--;
|
||||||
|
if (left_len < nqci.src_len) goto invalid_packet;
|
||||||
|
nqci.src_id = cur_rawptr;
|
||||||
|
cur_rawptr += nqci.src_len;
|
||||||
|
left_len -= nqci.src_len;
|
||||||
|
|
||||||
|
if (qch) *qch = nqch;
|
||||||
|
if (qch_len) {
|
||||||
|
*qch_len = sizeof(struct quic_lhdr) +
|
||||||
|
nqci.src_len + nqci.dst_len;
|
||||||
|
}
|
||||||
|
if (qci) *qci = nqci;
|
||||||
|
if (payload) *payload = cur_rawptr;
|
||||||
|
if (plen) *plen = left_len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
invalid_packet:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen,
|
||||||
|
const struct quic_lhdr *qch,
|
||||||
|
struct quici_hdr *qhdr,
|
||||||
|
uint8_t **payload, uint32_t *plen) {
|
||||||
|
if (inplen < 3) goto invalid_packet;
|
||||||
|
struct quici_hdr nqhdr;
|
||||||
|
|
||||||
|
uint8_t *cur_ptr = inpayload;
|
||||||
|
uint32_t left_len = inplen;
|
||||||
|
uint64_t tlen = left_len;
|
||||||
|
|
||||||
|
nqhdr.token_len = quic_parse_varlength(cur_ptr, &tlen);
|
||||||
|
nqhdr.token = cur_ptr + tlen;
|
||||||
|
|
||||||
|
if (left_len < nqhdr.token_len + tlen)
|
||||||
|
goto invalid_packet;
|
||||||
|
cur_ptr += tlen + nqhdr.token_len;
|
||||||
|
left_len -= tlen + nqhdr.token_len;
|
||||||
|
|
||||||
|
tlen = left_len;
|
||||||
|
nqhdr.length = quic_parse_varlength(cur_ptr, &tlen);
|
||||||
|
|
||||||
|
if (left_len != nqhdr.length + tlen &&
|
||||||
|
left_len <= qch->number_length + 1)
|
||||||
|
goto invalid_packet;
|
||||||
|
|
||||||
|
uint32_t packet_number = 0;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i <= qch->number_length; i++) {
|
||||||
|
packet_number = (packet_number << 8) + *cur_ptr++;
|
||||||
|
left_len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
nqhdr.packet_number = packet_number;
|
||||||
|
|
||||||
|
if (qhdr) *qhdr = nqhdr;
|
||||||
|
if (payload) *payload = cur_ptr;
|
||||||
|
if (plen) *plen = left_len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
invalid_packet:
|
||||||
|
lgerror("QUIC invalid Initial packet", -EINVAL);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
128
quic.h
Normal file
128
quic.h
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
#ifndef QUIC_H
|
||||||
|
#define QUIC_H
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @macro
|
||||||
|
*
|
||||||
|
* :macro:`NGTCP2_INITIAL_SALT_V1` is a salt value which is used to
|
||||||
|
* derive initial secret. It is used for QUIC v1.
|
||||||
|
*/
|
||||||
|
#define QUIC_INITIAL_SALT_V1 \
|
||||||
|
"\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17\x9a\xe6\xa4\xc8\x0c\xad" \
|
||||||
|
"\xcc\xbb\x7f\x0a"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @macro
|
||||||
|
*
|
||||||
|
* :macro:`NGTCP2_INITIAL_SALT_V2` is a salt value which is used to
|
||||||
|
* derive initial secret. It is used for QUIC v2.
|
||||||
|
*/
|
||||||
|
#define QUIC_INITIAL_SALT_V2 \
|
||||||
|
"\x0d\xed\xe3\xde\xf7\x00\xa6\xdb\x81\x93\x81\xbe\x6e\x26\x9d\xcb" \
|
||||||
|
"\xf9\xbd\x2e\xd9"
|
||||||
|
|
||||||
|
#define QUIC_INITIAL_TYPE 0
|
||||||
|
#define QUIC_0_RTT_TYPE 1
|
||||||
|
#define QUIC_HANDSHAKE_TYPE 2
|
||||||
|
#define QUIC_RETRY_TYPE 3
|
||||||
|
|
||||||
|
#define QUIC_INITIAL_TYPE_V1 0b00
|
||||||
|
#define QUIC_0_RTT_TYPE_V1 0b01
|
||||||
|
#define QUIC_HANDSHAKE_TYPE_V1 0b10
|
||||||
|
#define QUIC_RETRY_TYPE_V1 0b11
|
||||||
|
#define quic_convtype_v1(type) (type)
|
||||||
|
|
||||||
|
#define QUIC_INITIAL_TYPE_V2 0b01
|
||||||
|
#define QUIC_0_RTT_TYPE_V2 0b10
|
||||||
|
#define QUIC_HANDSHAKE_TYPE_V2 0b11
|
||||||
|
#define QUIC_RETRY_TYPE_V2 0b00
|
||||||
|
#define quic_convtype_v2(type) (((type) + 1) & 0b11)
|
||||||
|
|
||||||
|
#define QUIC_V1 1 // RFC 9000
|
||||||
|
#define QUIC_V2 0x6b3343cf // RFC 9369
|
||||||
|
|
||||||
|
static const uint32_t supported_versions[] = {
|
||||||
|
QUIC_V1,
|
||||||
|
QUIC_V2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quic Large Header
|
||||||
|
*/
|
||||||
|
struct quic_lhdr {
|
||||||
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
|
uint8_t number_length:2;
|
||||||
|
uint8_t reserved:2;
|
||||||
|
uint8_t type:2;
|
||||||
|
uint8_t fixed:1;
|
||||||
|
uint8_t form:1;
|
||||||
|
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||||
|
uint8_t form:1;
|
||||||
|
uint8_t fixed:1;
|
||||||
|
uint8_t type:2;
|
||||||
|
uint8_t reserved:2;
|
||||||
|
uint8_t number_length:2;
|
||||||
|
#else
|
||||||
|
#error "Undefined endian"
|
||||||
|
#endif
|
||||||
|
uint32_t version;
|
||||||
|
}__attribute__((packed));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quic Large Header Ids
|
||||||
|
* (separated from the original header because of varying dst
|
||||||
|
*/
|
||||||
|
struct quic_cids {
|
||||||
|
uint8_t dst_len;
|
||||||
|
uint8_t *dst_id;
|
||||||
|
uint8_t src_len;
|
||||||
|
uint8_t *src_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses QUIС raw data (UDP payload) to quic large header and
|
||||||
|
* quic payload.
|
||||||
|
*
|
||||||
|
* \qch_len is sizeof(qch) + qci->dst_len + qci->src_id
|
||||||
|
* \payload is Type-Specific payload (#17.2).
|
||||||
|
*/
|
||||||
|
int quic_parse_data(uint8_t *raw_payload, uint32_t raw_payload_len,
|
||||||
|
struct quic_lhdr **qch, uint32_t *qch_len,
|
||||||
|
struct quic_cids *qci,
|
||||||
|
uint8_t **payload, uint32_t *plen);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses QUIC variable-length integer. (#16)
|
||||||
|
* \variable is a pointer to the sequence to be parsed
|
||||||
|
* (varlen integer in big endian format)
|
||||||
|
*
|
||||||
|
* \mlen Used to signal about variable length and validate left length
|
||||||
|
* in the buffer.
|
||||||
|
*/
|
||||||
|
uint64_t quic_parse_varlength(uint8_t *variable, uint64_t *mlen);
|
||||||
|
|
||||||
|
// quici stands for QUIC Initial
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This structure should be parsed
|
||||||
|
*/
|
||||||
|
struct quici_hdr {
|
||||||
|
uint64_t token_len;
|
||||||
|
uint8_t *token;
|
||||||
|
uint64_t length;
|
||||||
|
uint32_t packet_number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses QUIC initial payload.
|
||||||
|
* \inpayload is a raw QUIC payload (payload after quic large header)
|
||||||
|
*/
|
||||||
|
int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen,
|
||||||
|
const struct quic_lhdr *qch,
|
||||||
|
struct quici_hdr *qhdr,
|
||||||
|
uint8_t **payload, uint32_t *plen);
|
||||||
|
|
||||||
|
#endif /* QUIC_H */
|
||||||
@@ -3,7 +3,8 @@
|
|||||||
|
|
||||||
#define FAKE_SNI_MAXLEN 1500
|
#define FAKE_SNI_MAXLEN 1500
|
||||||
|
|
||||||
static const char fake_sni[] = "\276(\001\273\366\234|\335\213\222\023\330\200\030\001\366\350e\000\000\001\001\b\n}\355\267Hm/\217\347\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356";
|
static const char fake_sni[] = "\026\003\001\002\000\001\000\001\374\003\003\323[\345\201f\362\200:B\356Uq\355X\315i\235*\021\367\331\272\a>\233\254\355\307/\342\372\265 \275\2459l&r\222\313\361\3729`\376\256\233\333O\001\373\33050\r\260f,\231\035 \324^\000>\023\002\023\003\023\001\300,\3000\000\237\314\251\314\250\314\252\300+\300/\000\236\300$\300(\000k\300#\300'\000g\300\n\300\024\0009\300\t\300\023\0003\000\235\000\234\000=\000<\0005\000/\000\377\001\000\001u\000\000\000\023\000\021\000\000\016www.google.com\000\v\000\004\003\000\001\002\000\n\000\026\000\024\000\035\000\027\000\036\000\031\000\030\001\000\001\001\001\002\001\003\001\004\000\020\000\016\000\f\002h2\bhttp/1.1\000\026\000\000\000\027\000\000\0001\000\000\000\r\0000\000.\004\003\005\003\006\003\b\a\b\b\b\032\b\033\b\034\b\t\b\n\b\v\b\004\b\005\b\006\004\001\005\001\006\001\003\003\003\001\003\002\004\002\005\002\006\002\000+\000\005\004\003\004\003\003\000-\000\002\001\001\0003\000&\000$\000\035\000 \004\224\206\021\256\f\222\266\3435\216\202\342\2573\341\3503\2107\341\023\016\240r|6\000^K\310s\000\025\000\255\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000";
|
||||||
|
|
||||||
|
static const char fake_sni_old[] = "\026\003\001\004\316\001\000\004\312\003\003K+\272\314\340\306\374>dw%\f\223\346\225\270\270~\335\027\f\264\341H\267\357\303\216T\322[\371 \245\320\212V6\374\3706\232\0216B\325\273P\b\300>\0332>\362\323\033\322\301\204\022f8\223\214\000\"\023\001\023\003\023\002\300+\300/\314\251\314\250\300,\3000\300\n\300\t\300\023\300\024\000\234\000\235\000/\0005\001\000\004_\000\000\000\023\000\021\000\000\016www.google.com\000\027\000\000\377\001\000\001\000\000\n\000\016\000\f\000\035\000\027\000\030\000\031\001\000\001\001\000\v\000\002\001\000\000\020\000\v\000\t\bhttp/1.1\000\005\000\005\001\000\000\000\000\000\"\000\n\000\b\004\003\005\003\006\003\002\003\0003\000k\000i\000\035\000 \333C\212\234-\t\237#\202\\\231\311\022]\333\341t(\t\276U\373u\234\316J~,^|*Z\000\027\000A\004k\n\255\254\376X\226t\001;n~\033\034.\245\027\024\3762_\352$\374\346^f\fF,\201\275\263\336O\231\001\032\200\357dI\266y\031\323\311vR\232\004\r\366FT\004\335\326\356\256\230B\t\313\000*\000\000\000+\000\005\004\003\004\003\003\000\r\000\030\000\026\004\003\005\003\006\003\b\004\b\005\b\006\004\001\005\001\006\001\002\003\002\001\000-\000\002\001\001\000\034\000\002@\001\376\r\0029\000\000\001\000\003\344\000 \337\306\243\332Y\033\a\252\352\025\365Z\035\223\226\304\255\363\215G\356g\344%}7\217\033n\211^\201\002\017g\267\334\326OD}\336\341ZC\230\226'\225\313\357\211\\\242\273\030k\216\377U\315\206\2410\200\203\332Z\223\005\370\b\304\370f\017\200\023\241\223~?\270{\037b\312\001\270\227\366\356\352\002\314\351\006\237\241q\226\300\314\321o\247{\201\317\230}B\005T\3660\335\320\332r?S\217\tq\036\031\326I|\237]\311 c\f\024r\031\310W\373\257\314q)q\030\237\261\227\217Kd?\257'G\320\020\340\256ND\247\005\341\324\024OP>\370\350\270b\311wAj\t\311\213\365i\203\230x\207\354\245<\274\202\230c\v0Y\263\364\022\303a\200\022\031\314\271rl=\327\336\001\327\264\267\342\353\352=\354[u\224\260\257\034\004\232\023\226}\227\030e\221\"\350\207\027dId\324\305\362N:\035\307`\204\337\201;\221\320\266b\362hrH\345e\206\246%\006\020a4\3430\036\225\215\274\275\360Q&\271\237)\222uK\362\017o\220\226W\357\267#\357\v\023\354\213\2629\331\ad\005/~6k\000[\247\301\270\310qJ\004\303|m5\363\376Y\002\243}6\251x\024\331)GH\335\205rI\032\f\210\a\212\347]\271\030\347.\021\213\365\026\030\340/Ny\r\332\3577\3203\026iX}>\2507\327&XRXU!\017\270I\313\352\350^?\352Uss\017\266pF\222NI\245\307_\305#\361\352\243+-\266\317Q\036s\243\277\355{S&\023>\275\360\215\032V\237XOY\345u>\002\305\252T\354\035\327v{P\352M\233\366\221\270\377\251\261f+rF\201wL2W\266X\252\242X\2536I\337c\205uZ\254Fe\305h\t\371\376\216r\336Y\327h\347*\331\257-ZQ{(\336\226\206\017\037\036\021\341\027z\033\254\235\252\227\224\004?p\243\351\\\263\352\205\327#W\345\255\256\375\267bP\3047\363!*K\003t\212(\306\214P\215\3506j\025\375\213e\254s\000)\001\034\000\367\000\361\002\276W%\232?\326\223\277\211v\017\a\361\347\312N\226\024L\260v\210\271j\324[|\270\344\3773\321-\313b>~\310\253XIR\324)&;\033{g;)\344\255\226\370\347I\\y\020\324\360\211vC\310\226s\267|\273$\341\332\2045qh\245w\2255\214\316\030\255\301\326C\343\304=\245\231h`yd\000#s\002\370\374Z\0336\245\361\226\222\306\032k\2457\016h\314(R;\326T~EHH\352\307\023^\247\363\321`V\340\253Z\233\357\227I\373\337z\177\nv\261\252\371\017\226\223\345\005\315y4\b\236N0\2630\017\215c\305&L\260\346J\237\203Q(\335W\027|>\3553\275j\307?W5\3463kc\350\262C\361 \037w!\371}\214\"I\377|\331@a;\342\3566\312\272Z\327u7\204'\215YBLL\235\236\242\345\215\245T\211a\312\263\342\000! \221\202X$\302\317\203\246\207c{\231\330\264\324\\k\271\272\336\356\002|\261O\207\030+\367P\317\356";
|
||||||
|
|
||||||
#endif /*RAW_REPLACEMENTS_H*/
|
#endif /*RAW_REPLACEMENTS_H*/
|
||||||
|
|||||||
62
types.h
Normal file
62
types.h
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
#ifndef TYPES_H
|
||||||
|
#define TYPES_H
|
||||||
|
#include <asm/byteorder.h>
|
||||||
|
|
||||||
|
#ifdef KERNEL_SCOPE
|
||||||
|
#include <linux/errno.h> // IWYU pragma: export
|
||||||
|
#include <linux/string.h> // IWYU pragma: export
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
typedef __u8 uint8_t;
|
||||||
|
typedef __u16 uint16_t;
|
||||||
|
typedef __u32 uint32_t;
|
||||||
|
typedef __u64 uint64_t;
|
||||||
|
typedef __i8 int8_t;
|
||||||
|
typedef __i16 int16_t;
|
||||||
|
typedef __i32 int32_t;
|
||||||
|
typedef __i64 int64_t;
|
||||||
|
#else /* USERSPACE_SCOPE */
|
||||||
|
|
||||||
|
#include <errno.h> // IWYU pragma: export
|
||||||
|
#include <stdint.h> // IWYU pragma: export
|
||||||
|
#include <string.h> // IWYU pragma: export
|
||||||
|
|
||||||
|
#endif /* SCOPES */
|
||||||
|
|
||||||
|
// Network specific structures
|
||||||
|
#ifdef KERNEL_SPACE
|
||||||
|
#include <linux/stddef.h> // IWYU pragma: export
|
||||||
|
#include <linux/net.h> // IWYU pragma: export
|
||||||
|
#include <linux/in.h> // IWYU pragma: export
|
||||||
|
#include <linux/ip.h> // IWYU pragma: export
|
||||||
|
#include <linux/tcp.h> // IWYU pragma: export
|
||||||
|
|
||||||
|
/* from <netinet/ip.h> */
|
||||||
|
#define IP_RF 0x8000 /* reserved fragment flag */
|
||||||
|
#define IP_DF 0x4000 /* dont fragment flag */
|
||||||
|
#define IP_MF 0x2000 /* more fragments flag */
|
||||||
|
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
|
||||||
|
#else
|
||||||
|
#define USER_SPACE
|
||||||
|
#include <arpa/inet.h> // IWYU pragma: export
|
||||||
|
#include <netinet/ip.h> // IWYU pragma: export
|
||||||
|
#include <netinet/tcp.h> // IWYU pragma: export
|
||||||
|
#include <netinet/udp.h> // IWYU pragma: export
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define max(a,b)__extension__\
|
||||||
|
({ \
|
||||||
|
__typeof__ (a) _a = (a); \
|
||||||
|
__typeof__ (b) _b = (b); \
|
||||||
|
_a > _b ? _a : _b; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define min(a,b)__extension__\
|
||||||
|
({ \
|
||||||
|
__typeof__ (a) _a = (a); \
|
||||||
|
__typeof__ (b) _b = (b); \
|
||||||
|
_a < _b ? _a : _b; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#endif /* TYPES_H */
|
||||||
25
uspace.mk
25
uspace.mk
@@ -22,19 +22,19 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF
|
|||||||
|
|
||||||
APP:=$(BUILD_DIR)/youtubeUnblock
|
APP:=$(BUILD_DIR)/youtubeUnblock
|
||||||
|
|
||||||
SRCS := youtubeUnblock.c mangle.c
|
SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c
|
||||||
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
|
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
|
||||||
|
|
||||||
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.a
|
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la
|
||||||
LIBMNL := $(DEPSDIR)/lib/libmnl.a
|
LIBMNL := $(DEPSDIR)/lib/libmnl.la
|
||||||
LIBNETFILTER_QUEUE := $(DEPSDIR)/lib/libnetfilter_queue.a
|
LIBNETFILTER_QUEUE := $(DEPSDIR)/lib/libnetfilter_queue.la
|
||||||
|
#LIBCRYPTO := $(DEPSDIR)/lib64/libcrypto.a
|
||||||
|
|
||||||
.PHONY: default all dev dev_attrs prepare_dirs
|
.PHONY: default all dev dev_attrs prepare_dirs
|
||||||
default: all
|
default: all
|
||||||
|
|
||||||
run_dev: dev
|
run_dev: dev
|
||||||
bash -c "sudo $(APP) 537"
|
bash -c "sudo $(APP)"
|
||||||
|
|
||||||
dev: dev_attrs all
|
dev: dev_attrs all
|
||||||
|
|
||||||
@@ -47,6 +47,11 @@ prepare_dirs:
|
|||||||
mkdir -p $(BUILD_DIR)
|
mkdir -p $(BUILD_DIR)
|
||||||
mkdir -p $(DEPSDIR)
|
mkdir -p $(DEPSDIR)
|
||||||
|
|
||||||
|
$(LIBCRYPTO):
|
||||||
|
cd deps/openssl && ./Configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--cross-compile-prefix=$(CROSS_COMPILE_PLATFORM)-,) --no-shared
|
||||||
|
$(MAKE) -C deps/openssl
|
||||||
|
$(MAKE) install_sw -C deps/openssl
|
||||||
|
|
||||||
$(LIBNFNETLINK):
|
$(LIBNFNETLINK):
|
||||||
cd deps/libnfnetlink && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) --enable-static --disable-shared
|
cd deps/libnfnetlink && ./autogen.sh && ./configure --prefix=$(DEPSDIR) $(if $(CROSS_COMPILE_PLATFORM),--host=$(CROSS_COMPILE_PLATFORM),) --enable-static --disable-shared
|
||||||
$(MAKE) -C deps/libnfnetlink
|
$(MAKE) -C deps/libnfnetlink
|
||||||
@@ -62,11 +67,11 @@ $(LIBNETFILTER_QUEUE): $(LIBNFNETLINK) $(LIBMNL)
|
|||||||
$(MAKE) -C deps/libnetfilter_queue
|
$(MAKE) -C deps/libnetfilter_queue
|
||||||
$(MAKE) install -C deps/libnetfilter_queue
|
$(MAKE) install -C deps/libnetfilter_queue
|
||||||
|
|
||||||
$(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL)
|
$(APP): $(OBJS) $(LIBNETFILTER_QUEUE) $(LIBMNL) $(LIBCRYPTO)
|
||||||
@echo 'CCLD $(APP)'
|
@echo 'CCLD $(APP)'
|
||||||
$(CCLD) $(OBJS) -o $(APP) $(LDFLAGS) -lmnl -lnetfilter_queue -lpthread
|
$(CCLD) $(OBJS) -o $(APP) $(LDFLAGS) -lmnl -lnetfilter_queue -lpthread
|
||||||
|
|
||||||
$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) config.h
|
$(BUILD_DIR)/%.o: %.c $(LIBNETFILTER_QUEUE) $(LIBMNL) $(LIBCRYPTO) config.h
|
||||||
@echo 'CC $@'
|
@echo 'CC $@'
|
||||||
$(CC) -c $(CFLAGS) $(LDFLAGS) $< -o $@
|
$(CC) -c $(CFLAGS) $(LDFLAGS) $< -o $@
|
||||||
|
|
||||||
@@ -84,8 +89,12 @@ uninstall:
|
|||||||
-systemctl disable youtubeUnblock.service
|
-systemctl disable youtubeUnblock.service
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
find $(BUILD_DIR) -maxdepth 1 -type f | xargs rm -rf
|
||||||
|
|
||||||
|
distclean: clean
|
||||||
rm -rf $(BUILD_DIR)
|
rm -rf $(BUILD_DIR)
|
||||||
$(MAKE) distclean -C deps/libnetfilter_queue || true
|
$(MAKE) distclean -C deps/libnetfilter_queue || true
|
||||||
$(MAKE) distclean -C deps/libmnl || true
|
$(MAKE) distclean -C deps/libmnl || true
|
||||||
$(MAKE) distclean -C deps/libnfnetlink || true
|
$(MAKE) distclean -C deps/libnfnetlink || true
|
||||||
|
#$(MAKE) distclean -C deps/openssl || true
|
||||||
|
|
||||||
|
|||||||
312
utils.c
Normal file
312
utils.c
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
|
#ifdef KERNEL_SPACE
|
||||||
|
#include <linux/ip.h>
|
||||||
|
#else
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
|
||||||
|
#include <libnetfilter_queue/libnetfilter_queue_tcp.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph)
|
||||||
|
{
|
||||||
|
#ifdef KERNEL_SPACE
|
||||||
|
uint32_t tcp_packet_len = ntohs(iph->tot_len) - (iph->ihl << 2);
|
||||||
|
tcph->check = 0;
|
||||||
|
tcph->check = csum_tcpudp_magic(
|
||||||
|
iph->saddr, iph->daddr, tcp_packet_len,
|
||||||
|
IPPROTO_TCP,
|
||||||
|
csum_partial(tcph, tcp_packet_len, 0));
|
||||||
|
#else
|
||||||
|
nfq_tcp_compute_checksum_ipv4(tcph, iph);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ip4_set_checksum(struct iphdr *iph)
|
||||||
|
{
|
||||||
|
#ifdef KERNEL_SPACE
|
||||||
|
iph->check = 0;
|
||||||
|
iph->check = ip_fast_csum(iph, iph->ihl);
|
||||||
|
#else
|
||||||
|
nfq_ip_set_checksum(iph);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int ip4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||||
|
struct iphdr **iph, uint32_t *iph_len,
|
||||||
|
uint8_t **payload, uint32_t *plen) {
|
||||||
|
if (pkt == NULL || buflen < sizeof(struct iphdr)) {
|
||||||
|
lgerror("ip4_payload_split: pkt|buflen", -EINVAL);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct iphdr *hdr = (struct iphdr *)pkt;
|
||||||
|
if (hdr->version != IPVERSION) {
|
||||||
|
lgerror("ip4_payload_split: ipversion", -EINVAL);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hdr_len = hdr->ihl * 4;
|
||||||
|
uint32_t pktlen = ntohs(hdr->tot_len);
|
||||||
|
if (buflen < pktlen || hdr_len > pktlen) {
|
||||||
|
lgerror("ip4_payload_split: buflen cmp pktlen", -EINVAL);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iph)
|
||||||
|
*iph = hdr;
|
||||||
|
if (iph_len)
|
||||||
|
*iph_len = hdr_len;
|
||||||
|
if (payload)
|
||||||
|
*payload = pkt + hdr_len;
|
||||||
|
if (plen)
|
||||||
|
*plen = pktlen - hdr_len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tcp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||||
|
struct iphdr **iph, uint32_t *iph_len,
|
||||||
|
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||||
|
uint8_t **payload, uint32_t *plen) {
|
||||||
|
struct iphdr *hdr;
|
||||||
|
uint32_t hdr_len;
|
||||||
|
struct tcphdr *thdr;
|
||||||
|
uint32_t thdr_len;
|
||||||
|
|
||||||
|
uint8_t *tcph_pl;
|
||||||
|
uint32_t tcph_plen;
|
||||||
|
|
||||||
|
if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len,
|
||||||
|
&tcph_pl, &tcph_plen)){
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
hdr->protocol != IPPROTO_TCP ||
|
||||||
|
tcph_plen < sizeof(struct tcphdr)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
thdr = (struct tcphdr *)(tcph_pl);
|
||||||
|
thdr_len = thdr->doff * 4;
|
||||||
|
|
||||||
|
if (thdr_len > tcph_plen) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iph) *iph = hdr;
|
||||||
|
if (iph_len) *iph_len = hdr_len;
|
||||||
|
if (tcph) *tcph = thdr;
|
||||||
|
if (tcph_len) *tcph_len = thdr_len;
|
||||||
|
if (payload) *payload = tcph_pl + thdr_len;
|
||||||
|
if (plen) *plen = tcph_plen - thdr_len;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int udp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||||
|
struct iphdr **iph, uint32_t *iph_len,
|
||||||
|
struct udphdr **udph,
|
||||||
|
uint8_t **payload, uint32_t *plen) {
|
||||||
|
struct iphdr *hdr;
|
||||||
|
uint32_t hdr_len;
|
||||||
|
struct udphdr *uhdr;
|
||||||
|
uint32_t uhdr_len;
|
||||||
|
|
||||||
|
uint8_t *ip_ph;
|
||||||
|
uint32_t ip_phlen;
|
||||||
|
|
||||||
|
if (ip4_payload_split(pkt, buflen, &hdr, &hdr_len,
|
||||||
|
&ip_ph, &ip_phlen)){
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
hdr->protocol != IPPROTO_UDP ||
|
||||||
|
ip_phlen < sizeof(struct udphdr)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uhdr = (struct udphdr *)(ip_ph);
|
||||||
|
if (uhdr->len != 0 && ntohs(uhdr->len) != ip_phlen) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iph) *iph = hdr;
|
||||||
|
if (iph_len) *iph_len = hdr_len;
|
||||||
|
if (udph) *udph = uhdr;
|
||||||
|
if (payload) *payload = ip_ph + sizeof(struct udphdr);
|
||||||
|
if (plen) *plen = ip_phlen - sizeof(struct udphdr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split packet to two ipv4 fragments.
|
||||||
|
int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||||
|
uint8_t *frag1, uint32_t *f1len,
|
||||||
|
uint8_t *frag2, uint32_t *f2len) {
|
||||||
|
|
||||||
|
struct iphdr *hdr;
|
||||||
|
const uint8_t *payload;
|
||||||
|
uint32_t plen;
|
||||||
|
uint32_t hdr_len;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!frag1 || !f1len || !frag2 || !f2len)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if ((ret = ip4_payload_split(
|
||||||
|
(uint8_t *)pkt, buflen,
|
||||||
|
&hdr, &hdr_len, (uint8_t **)&payload, &plen)) < 0) {
|
||||||
|
lgerror("ipv4_frag: TCP Header extract error", ret);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (plen <= payload_offset) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload_offset & ((1 << 3) - 1)) {
|
||||||
|
lgerror("ipv4_frag: Payload offset MUST be a multiply of 8!", -EINVAL);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t f1_plen = payload_offset;
|
||||||
|
uint32_t f1_dlen = f1_plen + hdr_len;
|
||||||
|
|
||||||
|
uint32_t f2_plen = plen - payload_offset;
|
||||||
|
uint32_t f2_dlen = f2_plen + hdr_len;
|
||||||
|
|
||||||
|
if (*f1len < f1_dlen || *f2len < f2_dlen) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
*f1len = f1_dlen;
|
||||||
|
*f2len = f2_dlen;
|
||||||
|
|
||||||
|
memcpy(frag1, hdr, hdr_len);
|
||||||
|
memcpy(frag2, hdr, hdr_len);
|
||||||
|
|
||||||
|
memcpy(frag1 + hdr_len, payload, f1_plen);
|
||||||
|
memcpy(frag2 + hdr_len, payload + payload_offset, f2_plen);
|
||||||
|
|
||||||
|
struct iphdr *f1_hdr = (void *)frag1;
|
||||||
|
struct iphdr *f2_hdr = (void *)frag2;
|
||||||
|
|
||||||
|
uint16_t f1_frag_off = ntohs(f1_hdr->frag_off);
|
||||||
|
uint16_t f2_frag_off = ntohs(f2_hdr->frag_off);
|
||||||
|
|
||||||
|
f1_frag_off &= IP_OFFMASK;
|
||||||
|
f1_frag_off |= IP_MF;
|
||||||
|
|
||||||
|
if ((f2_frag_off & ~IP_OFFMASK) == IP_MF) {
|
||||||
|
f2_frag_off &= IP_OFFMASK;
|
||||||
|
f2_frag_off |= IP_MF;
|
||||||
|
} else {
|
||||||
|
f2_frag_off &= IP_OFFMASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
f2_frag_off += (uint16_t)payload_offset / 8;
|
||||||
|
|
||||||
|
f1_hdr->frag_off = htons(f1_frag_off);
|
||||||
|
f1_hdr->tot_len = htons(f1_dlen);
|
||||||
|
|
||||||
|
f2_hdr->frag_off = htons(f2_frag_off);
|
||||||
|
f2_hdr->tot_len = htons(f2_dlen);
|
||||||
|
|
||||||
|
|
||||||
|
lgdebugmsg("Packet split in portion %u %u", f1_plen, f2_plen);
|
||||||
|
|
||||||
|
ip4_set_checksum(f1_hdr);
|
||||||
|
ip4_set_checksum(f2_hdr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// split packet to two tcp-on-ipv4 segments.
|
||||||
|
int tcp4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||||
|
uint8_t *seg1, uint32_t *s1len,
|
||||||
|
uint8_t *seg2, uint32_t *s2len) {
|
||||||
|
|
||||||
|
struct iphdr *hdr;
|
||||||
|
uint32_t hdr_len;
|
||||||
|
struct tcphdr *tcph;
|
||||||
|
uint32_t tcph_len;
|
||||||
|
uint32_t plen;
|
||||||
|
const uint8_t *payload;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!seg1 || !s1len || !seg2 || !s2len)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if ((ret = tcp4_payload_split((uint8_t *)pkt, buflen,
|
||||||
|
&hdr, &hdr_len,
|
||||||
|
&tcph, &tcph_len,
|
||||||
|
(uint8_t **)&payload, &plen)) < 0) {
|
||||||
|
lgerror("tcp4_frag: tcp4_payload_split", ret);
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (
|
||||||
|
ntohs(hdr->frag_off) & IP_MF ||
|
||||||
|
ntohs(hdr->frag_off) & IP_OFFMASK) {
|
||||||
|
lgdebugmsg("tcp4_frag: frag value: %d",
|
||||||
|
ntohs(hdr->frag_off));
|
||||||
|
lgerror("tcp4_frag: ip fragmentation is set", -EINVAL);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (plen <= payload_offset) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t s1_plen = payload_offset;
|
||||||
|
uint32_t s1_dlen = s1_plen + hdr_len + tcph_len;
|
||||||
|
|
||||||
|
uint32_t s2_plen = plen - payload_offset;
|
||||||
|
uint32_t s2_dlen = s2_plen + hdr_len + tcph_len;
|
||||||
|
|
||||||
|
if (*s1len < s1_dlen || *s2len < s2_dlen)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
*s1len = s1_dlen;
|
||||||
|
*s2len = s2_dlen;
|
||||||
|
|
||||||
|
memcpy(seg1, hdr, hdr_len);
|
||||||
|
memcpy(seg2, hdr, hdr_len);
|
||||||
|
|
||||||
|
memcpy(seg1 + hdr_len, tcph, tcph_len);
|
||||||
|
memcpy(seg2 + hdr_len, tcph, tcph_len);
|
||||||
|
|
||||||
|
memcpy(seg1 + hdr_len + tcph_len, payload, s1_plen);
|
||||||
|
memcpy(seg2 + hdr_len + tcph_len, payload + payload_offset, s2_plen);
|
||||||
|
|
||||||
|
struct iphdr *s1_hdr = (void *)seg1;
|
||||||
|
struct iphdr *s2_hdr = (void *)seg2;
|
||||||
|
|
||||||
|
struct tcphdr *s1_tcph = (void *)(seg1 + hdr_len);
|
||||||
|
struct tcphdr *s2_tcph = (void *)(seg2 + hdr_len);
|
||||||
|
|
||||||
|
s1_hdr->tot_len = htons(s1_dlen);
|
||||||
|
s2_hdr->tot_len = htons(s2_dlen);
|
||||||
|
|
||||||
|
s2_tcph->seq = htonl(ntohl(s2_tcph->seq) + payload_offset);
|
||||||
|
|
||||||
|
lgdebugmsg("Packet split in portion %u %u", s1_plen, s2_plen);
|
||||||
|
|
||||||
|
tcp4_set_checksum(s1_tcph, s1_hdr);
|
||||||
|
tcp4_set_checksum(s2_tcph, s2_hdr);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
51
utils.h
Normal file
51
utils.h
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the packet to two IP fragments on position payload_offset.
|
||||||
|
* payload_offset indicates the position relatively to start of IP payload
|
||||||
|
* (start of transport header)
|
||||||
|
*/
|
||||||
|
int ip4_frag(const uint8_t *pkt, uint32_t pktlen,
|
||||||
|
uint32_t payload_offset,
|
||||||
|
uint8_t *frag1, uint32_t *f1len,
|
||||||
|
uint8_t *frag2, uint32_t *f2len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the packet to two TCP segments on position payload_offset
|
||||||
|
* payload_offset indicates the position relatively to start of TCP payload.
|
||||||
|
*/
|
||||||
|
int tcp4_frag(const uint8_t *pkt, uint32_t pktlen,
|
||||||
|
uint32_t payload_offset,
|
||||||
|
uint8_t *seg1, uint32_t *s1len,
|
||||||
|
uint8_t *seg2, uint32_t *s2len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the raw packet payload to ip header and ip payload.
|
||||||
|
*/
|
||||||
|
int ip4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||||
|
struct iphdr **iph, uint32_t *iph_len,
|
||||||
|
uint8_t **payload, uint32_t *plen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the raw packet payload to ip header, tcp header and tcp payload.
|
||||||
|
*/
|
||||||
|
int tcp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||||
|
struct iphdr **iph, uint32_t *iph_len,
|
||||||
|
struct tcphdr **tcph, uint32_t *tcph_len,
|
||||||
|
uint8_t **payload, uint32_t *plen);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the raw packet payload to ip header, udp header and udp payload.
|
||||||
|
*/
|
||||||
|
int udp4_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||||
|
struct iphdr **iph, uint32_t *iph_len,
|
||||||
|
struct udphdr **udph,
|
||||||
|
uint8_t **payload, uint32_t *plen);
|
||||||
|
|
||||||
|
void tcp4_set_checksum(struct tcphdr *tcph, struct iphdr *iph);
|
||||||
|
void ip4_set_checksum(struct iphdr *iph);
|
||||||
|
|
||||||
|
#endif /* UTILS_H */
|
||||||
516
youtubeUnblock.c
516
youtubeUnblock.c
@@ -7,10 +7,12 @@
|
|||||||
#error "The build aims to the kernel, not userspace"
|
#error "The build aims to the kernel, not userspace"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <libnetfilter_queue/linux_nfnetlink_queue.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Warning is ok, use this for Entware */
|
||||||
|
#include <libnetfilter_queue/linux_nfnetlink_queue.h>
|
||||||
|
|
||||||
#include <libmnl/libmnl.h>
|
#include <libmnl/libmnl.h>
|
||||||
#include <libnetfilter_queue/libnetfilter_queue.h>
|
#include <libnetfilter_queue/libnetfilter_queue.h>
|
||||||
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
|
#include <libnetfilter_queue/libnetfilter_queue_ipv4.h>
|
||||||
@@ -28,220 +30,12 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "mangle.h"
|
#include "mangle.h"
|
||||||
|
#include "args.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "logging.h"
|
||||||
|
|
||||||
pthread_mutex_t rawsocket_lock;
|
pthread_mutex_t rawsocket_lock;
|
||||||
|
int rawsocket = -2;
|
||||||
struct config_t config = {
|
|
||||||
.rawsocket = -2,
|
|
||||||
.threads = THREADS_NUM,
|
|
||||||
.fragmentation_strategy = FRAGMENTATION_STRATEGY,
|
|
||||||
.fake_sni_strategy = FAKE_SNI_STRATEGY,
|
|
||||||
.fake_sni_ttl = FAKE_SNI_TTL,
|
|
||||||
|
|
||||||
#ifdef SEG2_DELAY
|
|
||||||
.seg2_delay = SEG2_DELAY,
|
|
||||||
#else
|
|
||||||
.seg2_delay = 0,
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_GSO
|
|
||||||
.use_gso = true,
|
|
||||||
#else
|
|
||||||
.use_gso = false,
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
.verbose = true,
|
|
||||||
#else
|
|
||||||
.verbose = false,
|
|
||||||
#endif
|
|
||||||
.domains_str = defaul_snistr,
|
|
||||||
.domains_strlen = sizeof(defaul_snistr),
|
|
||||||
};
|
|
||||||
|
|
||||||
const char* get_value(const char *option, const char *prefix)
|
|
||||||
{
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
size_t prefix_len = strlen(prefix);
|
|
||||||
size_t option_len = strlen(option);
|
|
||||||
|
|
||||||
if (option_len <= prefix_len || strncmp(prefix, option, prefix_len)) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return option + prefix_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int parse_bool_option(const char *value) {
|
|
||||||
errno = 0;
|
|
||||||
if (strcmp(value, "1") == 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else if (strcmp(value, "0") == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
long parse_numeric_option(const char* value) {
|
|
||||||
errno = 0;
|
|
||||||
|
|
||||||
char* end;
|
|
||||||
long result = strtol(value, &end, 10);
|
|
||||||
if (*end != '\0') {
|
|
||||||
errno = EINVAL;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int parse_option(const char* option) {
|
|
||||||
const char* value;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
if (!strcmp(option, "--no-gso")) {
|
|
||||||
config.use_gso = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp(option, "--silent")) {
|
|
||||||
config.verbose = 0;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((value = get_value(option, "--sni-domains")) != 0) {
|
|
||||||
if (!value) {
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(value, "all")) {
|
|
||||||
config.all_domains = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.domains_str = value;
|
|
||||||
config.domains_strlen = strlen(value);
|
|
||||||
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((value = get_value(option, "--frag=")) != 0) {
|
|
||||||
if (!value) {
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(value, "tcp") == 0) {
|
|
||||||
config.fragmentation_strategy = FRAG_STRAT_TCP;
|
|
||||||
} else if (strcmp(value, "ip") == 0) {
|
|
||||||
config.fragmentation_strategy = FRAG_STRAT_IP;
|
|
||||||
} else if (strcmp(value, "none") == 0) {
|
|
||||||
config.fragmentation_strategy = FRAG_STRAT_NONE;
|
|
||||||
} else {
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((value = get_value(option, "--fake-sni=")) != 0) {
|
|
||||||
if (strcmp(value, "ack") == 0) {
|
|
||||||
config.fake_sni_strategy = FKSN_STRAT_ACK_SEQ;
|
|
||||||
} else if (strcmp(value, "ttl") == 0) {
|
|
||||||
config.fake_sni_strategy = FKSN_STRAT_TTL;
|
|
||||||
}
|
|
||||||
else if (strcmp(value, "none") == 0) {
|
|
||||||
config.fake_sni_strategy = FKSN_STRAT_NONE;
|
|
||||||
} else {
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((value = get_value(option, "--seg2delay=")) != 0) {
|
|
||||||
long num = parse_numeric_option(value);
|
|
||||||
if (errno != 0 ||
|
|
||||||
num < 0)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
config.seg2_delay = num;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((value = get_value(option, "--threads=")) != 0) {
|
|
||||||
long num = parse_numeric_option(value);
|
|
||||||
if (errno != 0 ||
|
|
||||||
num < 0 ||
|
|
||||||
num > MAX_THREADS) {
|
|
||||||
errno = EINVAL;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.threads = num;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((value = get_value(option, "--fake-sni-ttl=")) != 0) {
|
|
||||||
long num = parse_numeric_option(value);
|
|
||||||
if (errno != 0 ||
|
|
||||||
num < 0 ||
|
|
||||||
num > 255) {
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.fake_sni_ttl = num;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
err:
|
|
||||||
errno = EINVAL;
|
|
||||||
return -1;
|
|
||||||
out:
|
|
||||||
errno = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int parse_args(int argc, const char *argv[]) {
|
|
||||||
int err;
|
|
||||||
char *end;
|
|
||||||
|
|
||||||
if (argc < 2) {
|
|
||||||
errno = EINVAL;
|
|
||||||
goto errormsg_help;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.queue_start_num = parse_numeric_option(argv[1]);
|
|
||||||
if (errno != 0) goto errormsg_help;
|
|
||||||
|
|
||||||
for (int i = 2; i < argc; i++) {
|
|
||||||
if (parse_option(argv[i])) {
|
|
||||||
printf("Invalid option %s\n", argv[i]);
|
|
||||||
goto errormsg_help;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
errormsg_help:
|
|
||||||
err = errno;
|
|
||||||
printf("Usage: %s <queue_num> [OPTIONS]\n", argv[0]);
|
|
||||||
printf("Options:\n");
|
|
||||||
printf("\t--sni-domains=<comma separated domain list>|all\n");
|
|
||||||
printf("\t--fake-sni={ack,ttl,none}\n");
|
|
||||||
printf("\t--fake-sni-ttl=<ttl>\n");
|
|
||||||
printf("\t--frag={tcp,ip,none}\n");
|
|
||||||
printf("\t--seg2delay=<delay>\n");
|
|
||||||
printf("\t--threads=<threads number>\n");
|
|
||||||
printf("\t--silent\n");
|
|
||||||
printf("\t--no-gso\n");
|
|
||||||
errno = err;
|
|
||||||
if (errno == 0) errno = EINVAL;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int open_socket(struct mnl_socket **_nl) {
|
static int open_socket(struct mnl_socket **_nl) {
|
||||||
struct mnl_socket *nl = NULL;
|
struct mnl_socket *nl = NULL;
|
||||||
@@ -278,20 +72,20 @@ static int close_socket(struct mnl_socket **_nl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int open_raw_socket(void) {
|
static int open_raw_socket(void) {
|
||||||
if (config.rawsocket != -2) {
|
if (rawsocket != -2) {
|
||||||
errno = EALREADY;
|
errno = EALREADY;
|
||||||
perror("Raw socket is already opened");
|
perror("Raw socket is already opened");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||||
if (config.rawsocket == -1) {
|
if (rawsocket == -1) {
|
||||||
perror("Unable to create raw socket");
|
perror("Unable to create raw socket");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int mark = RAWSOCKET_MARK;
|
int mark = RAWSOCKET_MARK;
|
||||||
if (setsockopt(config.rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0)
|
if (setsockopt(rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "setsockopt(SO_MARK, %d) failed\n", mark);
|
fprintf(stderr, "setsockopt(SO_MARK, %d) failed\n", mark);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -300,24 +94,24 @@ static int open_raw_socket(void) {
|
|||||||
int mst = pthread_mutex_init(&rawsocket_lock, NULL);
|
int mst = pthread_mutex_init(&rawsocket_lock, NULL);
|
||||||
if (mst) {
|
if (mst) {
|
||||||
fprintf(stderr, "Mutex err: %d\n", mst);
|
fprintf(stderr, "Mutex err: %d\n", mst);
|
||||||
close(config.rawsocket);
|
close(rawsocket);
|
||||||
errno = mst;
|
errno = mst;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return config.rawsocket;
|
return rawsocket;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int close_raw_socket(void) {
|
static int close_raw_socket(void) {
|
||||||
if (config.rawsocket < 0) {
|
if (rawsocket < 0) {
|
||||||
errno = EALREADY;
|
errno = EALREADY;
|
||||||
perror("Raw socket is not set");
|
perror("Raw socket is not set");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (close(config.rawsocket)) {
|
if (close(rawsocket)) {
|
||||||
perror("Unable to close raw socket");
|
perror("Unable to close raw socket");
|
||||||
pthread_mutex_destroy(&rawsocket_lock);
|
pthread_mutex_destroy(&rawsocket_lock);
|
||||||
return -1;
|
return -1;
|
||||||
@@ -325,7 +119,7 @@ static int close_raw_socket(void) {
|
|||||||
|
|
||||||
pthread_mutex_destroy(&rawsocket_lock);
|
pthread_mutex_destroy(&rawsocket_lock);
|
||||||
|
|
||||||
config.rawsocket = -2;
|
rawsocket = -2;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -400,13 +194,15 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pthread_mutex_lock(&rawsocket_lock);
|
if (config.threads != 1)
|
||||||
|
pthread_mutex_lock(&rawsocket_lock);
|
||||||
|
|
||||||
int sent = sendto(config.rawsocket,
|
int sent = sendto(rawsocket,
|
||||||
pkt, pktlen, 0,
|
pkt, pktlen, 0,
|
||||||
(struct sockaddr *)&daddr, sizeof(daddr));
|
(struct sockaddr *)&daddr, sizeof(daddr));
|
||||||
|
|
||||||
pthread_mutex_unlock(&rawsocket_lock);
|
if (config.threads != 1)
|
||||||
|
pthread_mutex_unlock(&rawsocket_lock);
|
||||||
|
|
||||||
/* The function will return -errno on error as well as errno value set itself */
|
/* The function will return -errno on error as well as errno value set itself */
|
||||||
if (sent < 0) sent = -errno;
|
if (sent < 0) sent = -errno;
|
||||||
@@ -414,6 +210,8 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
|||||||
return sent;
|
return sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct packet_data {
|
struct packet_data {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint16_t hw_proto;
|
uint16_t hw_proto;
|
||||||
@@ -454,7 +252,7 @@ struct dps_t {
|
|||||||
uint32_t timer;
|
uint32_t timer;
|
||||||
};
|
};
|
||||||
// Note that the thread will automatically release dps_t and pkt_buff
|
// Note that the thread will automatically release dps_t and pkt_buff
|
||||||
void *delay_packet_send(void *data) {
|
void *delay_packet_send_fn(void *data) {
|
||||||
struct dps_t *dpdt = data;
|
struct dps_t *dpdt = data;
|
||||||
|
|
||||||
uint8_t *pkt = dpdt->pkt;
|
uint8_t *pkt = dpdt->pkt;
|
||||||
@@ -472,180 +270,19 @@ void *delay_packet_send(void *data) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int process_packet(const struct packet_data packet, struct queue_data qdata) {
|
void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
|
||||||
char buf[MNL_SOCKET_BUFFER_SIZE];
|
struct dps_t *dpdt = malloc(sizeof(struct dps_t));
|
||||||
struct nlmsghdr *verdnlh;
|
dpdt->pkt = malloc(data_len);
|
||||||
|
memcpy(dpdt->pkt, data, data_len);
|
||||||
#ifdef DEBUG_LOGGING
|
dpdt->pktlen = data_len;
|
||||||
printf("packet received (id=%u hw=0x%04x hook=%u, payload len %u)\n",
|
dpdt->timer = delay_ms;
|
||||||
packet.id, packet.hw_proto, packet.hook, packet.payload_len);
|
pthread_t thr;
|
||||||
#endif
|
pthread_create(&thr, NULL, delay_packet_send_fn, dpdt);
|
||||||
|
pthread_detach(thr);
|
||||||
if (packet.hw_proto != ETH_P_IP) {
|
|
||||||
return fallback_accept_packet(packet.id, qdata);
|
|
||||||
}
|
|
||||||
|
|
||||||
const int family = AF_INET;
|
|
||||||
|
|
||||||
const uint8_t *raw_payload = packet.payload;
|
|
||||||
size_t raw_payload_len = packet.payload_len;
|
|
||||||
|
|
||||||
const struct iphdr *iph;
|
|
||||||
uint32_t iph_len;
|
|
||||||
const struct tcphdr *tcph;
|
|
||||||
uint32_t tcph_len;
|
|
||||||
const uint8_t *data;
|
|
||||||
uint32_t dlen;
|
|
||||||
|
|
||||||
int ret = tcp4_payload_split((uint8_t *)raw_payload, raw_payload_len,
|
|
||||||
(struct iphdr **)&iph, &iph_len, (struct tcphdr **)&tcph, &tcph_len,
|
|
||||||
(uint8_t **)&data, &dlen);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct verdict vrd = analyze_tls_data(data, dlen);
|
|
||||||
|
|
||||||
verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata.queue_num);
|
|
||||||
nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_ACCEPT);
|
|
||||||
|
|
||||||
if (vrd.target_sni) {
|
|
||||||
if (config.verbose)
|
|
||||||
printf("SNI target detected\n");
|
|
||||||
|
|
||||||
if (dlen > 1480) {
|
|
||||||
if (config.verbose)
|
|
||||||
fprintf(stderr, "WARNING! Client Hello packet is too big and may cause issues!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t frag1[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
uint8_t frag2[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
uint32_t f1len = MNL_SOCKET_BUFFER_SIZE;
|
|
||||||
uint32_t f2len = MNL_SOCKET_BUFFER_SIZE;
|
|
||||||
nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_DROP);
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
nfq_ip_set_checksum((struct iphdr *)iph);
|
|
||||||
nfq_tcp_compute_checksum_ipv4(
|
|
||||||
(struct tcphdr *)tcph, (struct iphdr *)iph);
|
|
||||||
|
|
||||||
if (config.fake_sni_strategy != FKSN_STRAT_NONE) {
|
|
||||||
uint8_t fake_sni[MNL_SOCKET_BUFFER_SIZE];
|
|
||||||
uint32_t fsn_len = MNL_SOCKET_BUFFER_SIZE;
|
|
||||||
|
|
||||||
ret = gen_fake_sni(iph, tcph, fake_sni, &fsn_len);
|
|
||||||
if (ret < 0) {
|
|
||||||
errno = -ret;
|
|
||||||
perror("gen_fake_sni");
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = send_raw_socket(fake_sni, fsn_len);
|
|
||||||
if (ret < 0) {
|
|
||||||
errno = -ret;
|
|
||||||
perror("send fake sni");
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ipd_offset;
|
|
||||||
size_t mid_offset;
|
|
||||||
|
|
||||||
switch (config.fragmentation_strategy) {
|
|
||||||
case FRAG_STRAT_TCP:
|
|
||||||
ipd_offset = vrd.sni_offset;
|
|
||||||
mid_offset = ipd_offset + vrd.sni_len / 2;
|
|
||||||
|
|
||||||
if ((ret = tcp4_frag(raw_payload, raw_payload_len,
|
|
||||||
mid_offset, frag1, &f1len, frag2, &f2len)) < 0) {
|
|
||||||
|
|
||||||
errno = -ret;
|
|
||||||
perror("tcp4_frag");
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case FRAG_STRAT_IP:
|
|
||||||
ipd_offset = ((char *)data - (char *)tcph) + vrd.sni_offset;
|
|
||||||
mid_offset = ipd_offset + vrd.sni_len / 2;
|
|
||||||
mid_offset += 8 - mid_offset % 8;
|
|
||||||
|
|
||||||
if ((ret = ip4_frag(raw_payload, raw_payload_len,
|
|
||||||
mid_offset, frag1, &f1len, frag2, &f2len)) < 0) {
|
|
||||||
|
|
||||||
errno = -ret;
|
|
||||||
perror("ip4_frag");
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = send_raw_socket(raw_payload, raw_payload_len);
|
|
||||||
if (ret < 0) {
|
|
||||||
errno = -ret;
|
|
||||||
perror("raw pack send");
|
|
||||||
}
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = send_raw_socket(frag2, f2len);
|
|
||||||
if (ret < 0) {
|
|
||||||
errno = -ret;
|
|
||||||
perror("raw frags send: frag2");
|
|
||||||
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.seg2_delay) {
|
|
||||||
struct dps_t *dpdt = malloc(sizeof(struct dps_t));
|
|
||||||
dpdt->pkt = malloc(f1len);
|
|
||||||
memcpy(dpdt->pkt, frag1, f1len);
|
|
||||||
dpdt->pktlen = f1len;
|
|
||||||
dpdt->timer = config.seg2_delay;
|
|
||||||
pthread_t thr;
|
|
||||||
pthread_create(&thr, NULL, delay_packet_send, dpdt);
|
|
||||||
pthread_detach(thr);
|
|
||||||
} else {
|
|
||||||
ret = send_raw_socket(frag1, f1len);
|
|
||||||
|
|
||||||
if (ret < 0) {
|
|
||||||
errno = -ret;
|
|
||||||
perror("raw frags send: frag1");
|
|
||||||
|
|
||||||
goto fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (pktb_mangled(pktb)) {
|
|
||||||
if (config.versose)
|
|
||||||
printf("Mangled!\n");
|
|
||||||
|
|
||||||
nfq_nlmsg_verdict_put_pkt(
|
|
||||||
verdnlh, pktb_data(pktb), pktb_len(pktb));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
send_verd:
|
|
||||||
if (mnl_socket_sendto(*qdata._nl, verdnlh, verdnlh->nlmsg_len) < 0) {
|
|
||||||
perror("mnl_socket_send");
|
|
||||||
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MNL_CB_OK;
|
|
||||||
|
|
||||||
fallback:
|
|
||||||
return fallback_accept_packet(packet.id, qdata);
|
|
||||||
error:
|
|
||||||
return MNL_CB_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||||
|
char buf[MNL_SOCKET_BUFFER_SIZE];
|
||||||
|
|
||||||
struct queue_data *qdata = data;
|
struct queue_data *qdata = data;
|
||||||
|
|
||||||
@@ -686,7 +323,26 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return process_packet(packet, *qdata);
|
struct nlmsghdr *verdnlh;
|
||||||
|
verdnlh = nfq_nlmsg_put(buf, NFQNL_MSG_VERDICT, qdata->queue_num);
|
||||||
|
|
||||||
|
int ret = process_packet(packet.payload, packet.payload_len);
|
||||||
|
|
||||||
|
switch (ret) {
|
||||||
|
case PKT_DROP:
|
||||||
|
nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_DROP);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
nfq_nlmsg_verdict_put(verdnlh, packet.id, NF_ACCEPT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mnl_socket_sendto(*qdata->_nl, verdnlh, verdnlh->nlmsg_len) < 0) {
|
||||||
|
perror("mnl_socket_send");
|
||||||
|
return MNL_CB_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MNL_CB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BUF_SIZE (0xffff + (MNL_SOCKET_BUFFER_SIZE / 2))
|
#define BUF_SIZE (0xffff + (MNL_SOCKET_BUFFER_SIZE / 2))
|
||||||
@@ -738,18 +394,24 @@ int init_queue(int queue_num) {
|
|||||||
.queue_num = queue_num
|
.queue_num = queue_num
|
||||||
};
|
};
|
||||||
|
|
||||||
printf("Queue %d started!\n", qdata.queue_num);
|
printf("Queue %d started\n", qdata.queue_num);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
ret = mnl_socket_recvfrom(nl, buf, BUF_SIZE);
|
ret = mnl_socket_recvfrom(nl, buf, BUF_SIZE);
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
perror("mnl_socket_recvfrom");
|
perror("mnl_socket_recvfrom");
|
||||||
continue;
|
goto die;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, &qdata);
|
ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, &qdata);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
perror("mnl_cb_run");
|
lgerror("mnl_cb_run", -EPERM);
|
||||||
|
if (errno == EPERM) {
|
||||||
|
printf("Probably another instance of youtubeUnblock with the same queue number is running\n");
|
||||||
|
} else {
|
||||||
|
printf("Make sure the nfnetlink_queue kernel module is loaded\n");
|
||||||
|
}
|
||||||
|
goto die;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -786,43 +448,23 @@ void *init_queue_wrapper(void *qdconf) {
|
|||||||
return thres;
|
return thres;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, const char *argv[]) {
|
struct instance_config_t instance_config = {
|
||||||
if (parse_args(argc, argv)) {
|
.send_raw_packet = send_raw_socket,
|
||||||
perror("Unable to parse args");
|
.send_delayed_packet = delay_packet_send,
|
||||||
exit(EXIT_FAILURE);
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int ret;
|
||||||
|
if ((ret = parse_args(argc, argv)) != 0) {
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("Unable to parse args");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (config.fragmentation_strategy) {
|
print_version();
|
||||||
case FRAG_STRAT_TCP:
|
print_welcome();
|
||||||
printf("Using TCP segmentation\n");
|
|
||||||
break;
|
|
||||||
case FRAG_STRAT_IP:
|
|
||||||
printf("Using IP fragmentation\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("SNI fragmentation is disabled\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.seg2_delay) {
|
|
||||||
printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (config.fake_sni_strategy) {
|
|
||||||
case FKSN_STRAT_TTL:
|
|
||||||
printf("Fake SNI will be sent before each googlevideo request, TTL strategy will be used with TTL %d\n", config.fake_sni_ttl);
|
|
||||||
break;
|
|
||||||
case FRAG_STRAT_IP:
|
|
||||||
printf("Fake SNI will be sent before each googlevideo request, Ack-Seq strategy will be used\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("SNI fragmentation is disabled\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.use_gso) {
|
|
||||||
printf("GSO is enabled\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (open_raw_socket() < 0) {
|
if (open_raw_socket() < 0) {
|
||||||
perror("Unable to open raw socket");
|
perror("Unable to open raw socket");
|
||||||
@@ -866,6 +508,6 @@ int main(int argc, const char *argv[]) {
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return qres->status;
|
return -qres->status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ StandardError=journal
|
|||||||
StandardOutput=journal
|
StandardOutput=journal
|
||||||
StandardInput=null
|
StandardInput=null
|
||||||
ExecStartPre=iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
|
ExecStartPre=iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
|
||||||
ExecStart=$(PREFIX)/bin/youtubeUnblock 537
|
ExecStart=$(PREFIX)/bin/youtubeUnblock
|
||||||
ExecStop=iptables -t mangle -D OUTPUT -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
|
ExecStop=iptables -t mangle -D OUTPUT -p tcp -m tcp --dport 443 -m connbytes --connbytes-dir original --connbytes-mode packets --connbytes 0:19 -j NFQUEUE --queue-num 537 --queue-bypass
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
|
|||||||
Reference in New Issue
Block a user