mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2026-01-27 12:40:36 +03:00
Compare commits
52 Commits
v1.0.0-rc1
...
v1.0.0-rc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5a6c5718f | ||
|
|
aefabe7e0a | ||
|
|
fd1ae1e574 | ||
|
|
7303abdaf2 | ||
|
|
cb138b902a | ||
|
|
3a5dbff8ac | ||
|
|
62551a9f82 | ||
|
|
ef120ca100 | ||
|
|
a4975dcdcd | ||
|
|
7ec29bd47b | ||
|
|
20ce07821b | ||
|
|
eb544c11ce | ||
|
|
3376860c0f | ||
|
|
438a3c17d8 | ||
|
|
046ecc4c97 | ||
|
|
bb8992578f | ||
|
|
cb46c89423 | ||
|
|
d87d3c9efb | ||
|
|
562f17e932 | ||
|
|
56b6126f52 | ||
|
|
235bf823db | ||
|
|
c9537bae95 | ||
|
|
0ee6d667e4 | ||
|
|
aa96769559 | ||
|
|
ae9592c71f | ||
|
|
93972f8404 | ||
|
|
7070ddfc74 | ||
|
|
0e9b3ff018 | ||
|
|
f344e525d2 | ||
|
|
f1ab2ac1a9 | ||
|
|
55d359d3e7 | ||
|
|
37a517eb8a | ||
|
|
ee56b67d20 | ||
|
|
ed08feaf20 | ||
|
|
96cf0365ee | ||
|
|
4c7b63fa7f | ||
|
|
58f4802f64 | ||
|
|
05cc0054d8 | ||
|
|
e9b033ccca | ||
|
|
30bc3a8d3f | ||
|
|
c617f238af | ||
|
|
43ca3fe07e | ||
|
|
9dc40bbdf1 | ||
|
|
666b3575fc | ||
|
|
263a04bb95 | ||
|
|
6b21e9b3b3 | ||
|
|
98a3fd5acd | ||
|
|
3ee979f7d1 | ||
|
|
82c49119d3 | ||
|
|
0fe45ec33f | ||
|
|
43d0118767 | ||
|
|
79df04cb07 |
42
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: Bug report
|
||||
description: Use this issue template when you are pretty sure here is a bug. Make sure the trouble you are experiencing haven't been already reported earlier and haven't been talked about in discussions.
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Use this issue template when you are pretty sure here is a bug.
|
||||
Make sure the trouble you are experiencing haven't been already reported earlier and haven't been talked about in [discussions](https://github.com/Waujito/youtubeUnblock/discussions).
|
||||
|
||||
Note, that *bugs* are internal program errors: crashes, freezes and other types of undefined behavior. You may also report problems with routers infrastructure here, but **NOT** questions like *My router doesn't work help please*. Your questions and other stuff with custom routers setup should be discussed [here](https://github.com/Waujito/youtubeUnblock/discussions/172)
|
||||
|
||||
Discuss problems like *youtube doesn't unblock* [here](https://github.com/Waujito/youtubeUnblock/discussions/173)
|
||||
- type: textarea
|
||||
id: bug-report
|
||||
attributes:
|
||||
label: Bug description
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: linux-distro
|
||||
attributes:
|
||||
label: Linux distribution
|
||||
description: Pass here your linux distro e.g. OpenWRT, Entware, Merlin, Padavan, Ubuntu, Debian
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: architecture
|
||||
attributes:
|
||||
label: Device architecture
|
||||
description: Pass here the architecture of your device. (On openwrt do `cat /etc/openwrt_release`, on other systems `lscpu` or `cat /proc/cpuinfo`)
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: configs
|
||||
attributes:
|
||||
label: Configuration
|
||||
description: Pass here the configuration of youtubeUnblock being used. You may pass output of `cat /etc/config/youtubeUnblock` on OpenWRT or raw flags of youtubeUnblock.
|
||||
validations:
|
||||
required: false
|
||||
10
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
|
||||
1
.github/builder_containers/README.md
vendored
Normal file
1
.github/builder_containers/README.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
This directory contains dockerfiles for large docker containers. This allows not to rebuild binaries every build and not to utilize cache.
|
||||
6
.github/builder_containers/entware-aarch64-3.10.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-aarch64-3.10.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/aarch64-3.10.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-armv7-2.6.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-armv7-2.6.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git -b k2.6
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/armv7-2.6.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-armv7-3.2.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-armv7-3.2.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/armv7-3.2.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-mips-3.4.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-mips-3.4.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/mips-3.4.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-mipsel-3.4.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-mipsel-3.4.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/mipsel-3.4.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-x64-3.2.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-x64-3.2.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/x64-3.2.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
6
.github/builder_containers/entware-x86-2.6.Dockerfile
vendored
Normal file
6
.github/builder_containers/entware-x86-2.6.Dockerfile
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
FROM waujito/entware_builder
|
||||
RUN git clone --depth 1 https://github.com/Entware/Entware.git -b k2.6
|
||||
WORKDIR /home/me/Entware
|
||||
RUN make package/symlinks
|
||||
RUN cp -v configs/x86-2.6.config .config
|
||||
RUN make -j$(nproc) toolchain/install
|
||||
1
.github/builder_containers/entware_docker
vendored
Submodule
1
.github/builder_containers/entware_docker
vendored
Submodule
Submodule .github/builder_containers/entware_docker added at 90eb6bc0a4
12
.github/builder_containers/kernel-3.0.101.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-3.0.101.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:14.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.0.101.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-3.10.108.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-3.10.108.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:16.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.10.108.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-4.19.322.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-4.19.322.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.322.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-4.4.302.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-4.4.302.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.302.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-5.15.167.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-5.15.167.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.167.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-5.4.284.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-5.4.284.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.284.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
12
.github/builder_containers/kernel-6.6.52.Dockerfile
vendored
Normal file
12
.github/builder_containers/kernel-6.6.52.Dockerfile
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM ubuntu:24.04
|
||||
|
||||
RUN apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev wget
|
||||
|
||||
RUN wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.52.tar.xz -O kernel.tar.xz
|
||||
RUN tar -xf kernel.tar.xz
|
||||
RUN rm -f kernel.tar.xz
|
||||
RUN /bin/bash -c "mv linux-* linux"
|
||||
|
||||
WORKDIR /linux
|
||||
RUN make defconfig
|
||||
RUN make -j$(nproc)
|
||||
169
.github/workflows/build-ci.yml
vendored
169
.github/workflows/build-ci.yml
vendored
@@ -10,18 +10,18 @@ on:
|
||||
- 'LICENSE'
|
||||
- 'README.md'
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.gh.outputs.version }}
|
||||
release: ${{ steps.gh.outputs.release }}
|
||||
sha: ${{ steps.gh.outputs.sha }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: GH
|
||||
id: gh
|
||||
@@ -30,7 +30,8 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "version=$(cat youtubeUnblock/Makefile | grep PKG_VERSION | sed 's/PKG_VERSION:=//')" >> $GITHUB_OUTPUT
|
||||
echo "version=$(cat Makefile | grep "PKG_VERSION :=" | sed 's/PKG_VERSION := //')" >> $GITHUB_OUTPUT
|
||||
echo "release=$(cat Makefile | grep "PKG_RELEASE :=" | sed 's/PKG_RELEASE := //')" >> $GITHUB_OUTPUT
|
||||
if [[ "${{ github.event_name }}" != "pull_request" ]]; then
|
||||
echo "sha=$(echo ${GITHUB_SHA::7})" >> $GITHUB_OUTPUT
|
||||
else
|
||||
@@ -43,7 +44,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# arch: [x86_64, x86, aarch64, armhf, armv7, ppc64le, s390x]
|
||||
arch: [x86_64, x86, aarch64, armhf, armv7]
|
||||
branch: [latest-stable]
|
||||
steps:
|
||||
@@ -73,6 +73,7 @@ jobs:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
shell: alpine.sh {0}
|
||||
run: |
|
||||
@@ -86,7 +87,7 @@ jobs:
|
||||
make -j$(nproc) CC="ccache gcc -static-libgcc -static" || exit 1
|
||||
strip -s build/youtubeUnblock
|
||||
cp -va build/youtubeUnblock .
|
||||
tar -czvf youtubeUnblock-$VERSION-$SHA-$PLATFORM-static.tar.gz youtubeUnblock youtubeUnblock.service README.md
|
||||
tar -czvf youtubeUnblock-$VERSION-$RELEASE-$SHA-$PLATFORM-static.tar.gz youtubeUnblock youtubeUnblock.service README.md
|
||||
ccache --show-stats
|
||||
|
||||
- name: Upload artifacts
|
||||
@@ -115,6 +116,8 @@ jobs:
|
||||
tool: mips-unknown-linux-musl
|
||||
- arch: mipssf
|
||||
tool: mips-unknown-linux-muslsf
|
||||
- arch: armv7sf
|
||||
tool: armv7-unknown-linux-musleabi
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -127,7 +130,7 @@ jobs:
|
||||
run: |
|
||||
mkdir -p $HOME/tools
|
||||
gh api repos/$REPO/releases/latest --jq '.tag_name' |\
|
||||
xargs -I{} wget -qO- https://github.com/$REPO/releases/download/{}/$TOOL.tgz | tar -C $HOME/tools -xz || exit 1
|
||||
xargs -I{} wget -qO- https://github.com/$REPO/releases/download/{}/$TOOL.tar.xz | tar -C $HOME/tools -xJ || exit 1
|
||||
[ -d "$HOME/tools/$TOOL/bin" ] && echo "$HOME/tools/$TOOL/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build
|
||||
@@ -136,6 +139,7 @@ jobs:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
TOOL: ${{ matrix.tool }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: |
|
||||
make -j$(nproc) \
|
||||
@@ -147,13 +151,13 @@ jobs:
|
||||
CROSS_COMPILE_PLATFORM=$TOOL || exit 1
|
||||
$TOOL-strip -s build/youtubeUnblock
|
||||
cp -va build/youtubeUnblock .
|
||||
tar -czvf youtubeUnblock-$VERSION-$SHA-$ARCH-static.tar.gz youtubeUnblock youtubeUnblock.service README.md
|
||||
tar -czvf youtubeUnblock-$VERSION-$RELEASE-$SHA-$ARCH-static.tar.gz youtubeUnblock youtubeUnblock.service README.md
|
||||
|
||||
- name: Upload artifacts
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-${{ matrix.arch }}
|
||||
name: youtubeUnblock-static-${{ matrix.arch }}
|
||||
path: ./**/youtubeUnblock*.tar.gz
|
||||
|
||||
build-openwrt:
|
||||
@@ -202,14 +206,26 @@ jobs:
|
||||
- name: Prepare build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: |
|
||||
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" youtubeUnblock/Makefile
|
||||
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$RELEASE-$SHA/" youtubeUnblock/Makefile
|
||||
|
||||
- name: Initilalize SDK
|
||||
id: init_sdk
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
working-directory: /builder
|
||||
run: |
|
||||
HOME=/builder ./setup.sh
|
||||
|
||||
- name: Build packages
|
||||
id: build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
working-directory: /builder
|
||||
run: |
|
||||
@@ -219,7 +235,7 @@ jobs:
|
||||
./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
|
||||
mv $(find ./bin -type f -name 'youtubeUnblock*.ipk') ./youtubeUnblock-$VERSION-$RELEASE-$SHA-${{ matrix.arch }}-${{ matrix.branch }}.ipk
|
||||
|
||||
- name: Upload packages
|
||||
if: steps.build.outcome == 'success'
|
||||
@@ -229,6 +245,60 @@ jobs:
|
||||
path: /builder/youtubeUnblock*.ipk
|
||||
if-no-files-found: error
|
||||
|
||||
build-openwrt-luci:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: openwrt/sdk:x86_64-openwrt-23.05
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: Prepare build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: |
|
||||
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$RELEASE-$SHA/" youtubeUnblock/Makefile
|
||||
|
||||
- name: Initilalize SDK
|
||||
id: init_sdk
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
working-directory: /builder
|
||||
run: |
|
||||
HOME=/builder ./setup.sh
|
||||
|
||||
- name: Build packages
|
||||
id: build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
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/luci-app-youtubeUnblock/compile V=s
|
||||
mv $(find ./bin -type f -name 'luci-app-youtubeUnblock*.ipk') ./luci-app-youtubeUnblock-$VERSION-$RELEASE-$SHA.ipk
|
||||
|
||||
- name: Upload packages
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: luci-app-youtubeUnblock
|
||||
path: /builder/luci-app-youtubeUnblock*.ipk
|
||||
if-no-files-found: error
|
||||
|
||||
build-entware:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
@@ -240,90 +310,53 @@ jobs:
|
||||
- mips-3.4
|
||||
- mipsel-3.4
|
||||
- x64-3.2
|
||||
- x86-2.6
|
||||
- armv7-2.6
|
||||
container:
|
||||
image: waujito/entware_builder:${{ matrix.arch }}
|
||||
options: --user root
|
||||
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: 'entware'
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: Prepare build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
run: |
|
||||
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" youtubeUnblock/Makefile
|
||||
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$RELEASE-$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
|
||||
working-directory: /home/me/Entware
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
RELEASE: ${{ needs.prepare.outputs.release }}
|
||||
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
|
||||
echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf
|
||||
cat feeds.conf
|
||||
./scripts/feeds update youtubeUnblock
|
||||
./scripts/feeds install -a -p youtubeUnblock
|
||||
echo "CONFIG_PACKAGE_youtubeUnblockEntware=m" | tee -a .config
|
||||
make package/youtubeUnblockEntware/compile V=s
|
||||
|
||||
mv $(find ./bin -type f -name 'youtubeUnblockEntware*.ipk') ./youtubeUnblock-$VERSION-$RELEASE-$SHA-entware-${{ matrix.arch }}.ipk
|
||||
|
||||
- name: Upload packages
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: youtubeUnblock-entware-${{ matrix.arch }}
|
||||
path: ./**/youtubeUnblock*-entware.tar.gz
|
||||
path: /home/me/Entware/youtubeUnblock*.ipk
|
||||
if-no-files-found: error
|
||||
|
||||
pre-release:
|
||||
if: github.event_name != 'pull_request' && github.ref_name == 'main'
|
||||
needs: [build-static, build-static-cross, build-openwrt, build-entware]
|
||||
needs: [build-static, build-static-cross, build-openwrt, build-entware, build-openwrt-luci]
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
@@ -341,5 +374,5 @@ jobs:
|
||||
title: 'Development build'
|
||||
files: |
|
||||
./**/youtubeUnblock*.ipk
|
||||
./**/kmod-youtubeUnblock*.ipk
|
||||
./**/youtubeUnblock*.tar.gz
|
||||
./**/luci-app-youtubeUnblock*.ipk
|
||||
|
||||
107
.github/workflows/test.yml
vendored
Normal file
107
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
name: "youtubeUnblock build test"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.editorconfig'
|
||||
- '.gitignore'
|
||||
- 'LICENSE'
|
||||
- 'README.md'
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
version: ${{ steps.gh.outputs.version }}
|
||||
sha: ${{ steps.gh.outputs.sha }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: GH
|
||||
id: gh
|
||||
env:
|
||||
REPO: ${{ github.repository }}
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
shell: bash
|
||||
run: |
|
||||
echo "version=$(cat youtubeUnblock/Makefile | grep PKG_VERSION | sed 's/PKG_VERSION:=//')" >> $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-static ${{ matrix.arch }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64]
|
||||
branch: [latest-stable]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
id: build
|
||||
env:
|
||||
ARCH: ${{ matrix.arch }}
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
shell: bash
|
||||
run: |
|
||||
make -j$(nproc)
|
||||
strip -s build/youtubeUnblock
|
||||
cp -va build/youtubeUnblock .
|
||||
tar -czvf static-youtubeUnblock-$VERSION-$SHA-$PLATFORM.tar.gz youtubeUnblock youtubeUnblock.service README.md
|
||||
|
||||
- name: Upload artifacts
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: static-youtubeUnblock-${{ matrix.arch }}
|
||||
path: ./**/static-youtubeUnblock*.tar.gz
|
||||
|
||||
build-kmod:
|
||||
needs: prepare
|
||||
name: build-kmod ${{ matrix.kernel_version }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
kernel_version:
|
||||
- 6.6.52
|
||||
- 5.15.167
|
||||
- 5.4.284
|
||||
- 4.19.322
|
||||
- 4.4.302
|
||||
- 3.10.108
|
||||
- 3.0.101
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build kernel module
|
||||
id: build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
SHA: ${{ needs.prepare.outputs.sha }}
|
||||
shell: bash
|
||||
run: |
|
||||
docker run --rm -v ./:/youtubeUnblock -w /youtubeUnblock waujito/kernel-bins:${{ matrix.kernel_version }} make kmake KERNEL_BUILDER_MAKEDIR:=/linux
|
||||
tar -czvf kmod-youtubeUnblock-$VERSION-$SHA-linux-${{ matrix.kernel_version }}.tar.gz kyoutubeUnblock.ko
|
||||
|
||||
- name: Upload artifacts
|
||||
if: steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: kmod-youtubeUnblock-linux-${{ matrix.kernel_version }}
|
||||
path: ./**/kmod-youtubeUnblock*.tar.gz
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule ".github/builder_containers/entware_docker"]
|
||||
path = .github/builder_containers/entware_docker
|
||||
url = https://github.com/Entware/docker.git
|
||||
2
Kbuild
2
Kbuild
@@ -1,3 +1,3 @@
|
||||
obj-m := kyoutubeUnblock.o
|
||||
kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kmod_utils.o kargs.o
|
||||
kyoutubeUnblock-objs := kytunblock.o mangle.o quic.o utils.o kargs.o tls.o
|
||||
ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement
|
||||
|
||||
7
Makefile
7
Makefile
@@ -1,6 +1,13 @@
|
||||
USPACE_TARGETS := default all install uninstall dev run_dev
|
||||
KMAKE_TARGETS := kmake kload kunload kreload xmod xtclean
|
||||
|
||||
PKG_VERSION := 1.0.0
|
||||
PKG_RELEASE := 4
|
||||
|
||||
PKG_FULLVERSION := $(PKG_VERSION)-$(PKG_RELEASE)
|
||||
|
||||
export PKG_VERSION PKG_RELEASE PKG_FULLVERSION
|
||||
|
||||
.PHONY: $(USPACE_TARGETS) $(KMAKE_TARGETS) clean
|
||||
$(USPACE_TARGETS):
|
||||
@$(MAKE) -f uspace.mk $@
|
||||
|
||||
30
Padavan.md
Normal file
30
Padavan.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## Padavan
|
||||
На падаване есть раздел, доступный для записи (/etc/storage), и, докинув нужные модули, можно запустить youtubeUblock на уже установленной прошивке без USB. Установка самого youtubeUblock мало будет отличаться от классичкской установки. Наибольшая сложность заключается в получении модулей ядра специально для вашего роутера.
|
||||
|
||||
**Версия youtubeUblock должна быть не меньше v1.0.0-rc4.**
|
||||
|
||||
### Сборка прошивки с модулями
|
||||
|
||||
Необходимо собрать ядро с модулями nfqueue. Собирать можно у себя локально, а можно и в github actions (https://github.com/shvchk/padavan-builder-workflow)
|
||||
|
||||
Добавить строки ниже в `padavan-ng/trunk/configs/boards/TPLINK/TL_C5-V4/kernel-3.4.x.config` (вместо TPLINK/TL_C5-V4 нужно выбрать свою модель):
|
||||
|
||||
```sh
|
||||
CONFIG_NETFILTER_NETLINK=m
|
||||
CONFIG_NETFILTER_NETLINK_QUEUE=m
|
||||
CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
|
||||
CONFIG_IP_NF_QUEUE=m
|
||||
CONFIG_IP6_NF_QUEUE=m
|
||||
```
|
||||
|
||||
Сборка
|
||||
```sh
|
||||
cd padavan-ng/trunk
|
||||
cp configs/templates/tplink/tl_c5-v4.config .config
|
||||
./build_firmware.sh
|
||||
```
|
||||
Если финальный размер превышает максимум, то можно отключить что-нибудь в .config, например FTP.
|
||||
|
||||
После сборки необходимо установить прошивку на роутер. Подробнее в гитлабе падавана: https://gitlab.com/hadzhioglu/padavan-ng. Как устанавливать: https://4pda.to/forum/index.php?showtopic=975687&st=12980#Spoil-115912586-5
|
||||
|
||||
Далее скачать youtubeUnblock, закинуть его на роутер, добавить правила фаервола и запустить. Можно скачивать static бинарник и запускать вручную, а можно загрузить entware на usb или в память, и поставить соответствующую версию youtubeUblock.
|
||||
58
README.md
58
README.md
@@ -21,6 +21,7 @@
|
||||
- [Building on host system](#building-on-host-system)
|
||||
- [Building on any kernel](#building-on-any-kernel)
|
||||
- [Building with openwrt SDK](#building-with-openwrt-sdk)
|
||||
- [Padavan](#padavan)
|
||||
|
||||
|
||||
# youtubeUnblock
|
||||
@@ -42,9 +43,9 @@ GNU General Public License for more details.
|
||||
|
||||
The program is distributed in two version:
|
||||
- A userspace application works on top of nfnetlink queue which requires nfnetlink modules in the kernel and firewall rules. This approach is default and normally should be used but it has some limitations on embedded devices which may have no nfnetlink support. Also this solution may break down the internet speed and CPU load on your device because of jumps between userspace and kernelspace for each packet (this behavior may be fixed with connbytes but it also requires conntrack kernel module).
|
||||
- A kernel module which integrates deeply within the netfilter stack and does not interact with the userspace firewall. The module requires only netfilter kernel support but it definetly present on every device connected to the Internet. The only difficulity is how to build it. I cannot provide modules within Github Actions for each single one kernel, even if we talk only about OpenWRT versions. If you want to learn more about the module, jump on [its section in the README](#kernel-module)
|
||||
- A kernel module which integrates deeply within the netfilter stack and does not interact with the userspace firewall. The module requires only netfilter kernel support but it definetly present on every device connected to the Internet. The only difficulity is how to build it. I cannot provide modules within Github Actions for each single one kernel, even if we talk only about OpenWRT versions. If you want to learn more about the module, jump on [its section in the README](#kernel-module). Whats the benefits of the kernel module? The benefits come for some specific cases: the kernel module is the fastest thing that allows us to process every single packet sent to the linux network stack, while the normal youtubeUnblock requires connbytes to keep the internet speed. Speaking about connbytes, it also requires conntrack to operate, which may be a limitation on some transit-traffic machines. Also userspace youtubeUnblock requires modules for netlink queue, userspace firewall application and modules for it. The kernel module is much simpler and requires only the linux kernel with netfilter built in.
|
||||
|
||||
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`.
|
||||
The program is compatible with routers based on OpenWRT, Entware(Keenetic/ASUS) and host machines. The program offers binaries via Github Actions. The binaries are also available via [github releases](https://github.com/Waujito/youtubeUnblock/releases). Use the latest pre-release for the most up to date build. 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>`.
|
||||
|
||||
@@ -54,9 +55,9 @@ For Windows use [GoodbyeDPI by ValdikSS](https://github.com/ValdikSS/GoodbyeDPI)
|
||||
|
||||
### OpenWRT pre configuration
|
||||
|
||||
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**.
|
||||
When you got the release package, you should install it. Go to your router interface, to *System->Software*, do *Update lists* and install youtubeUnblock via *install_package* button. Then, you should go to *System-Startup* menu and reload the firewall (You may also do it within *Services->youtubeUnblock* menu).
|
||||
|
||||
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).
|
||||
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). For most modern versions of OpenWRT (v23.x, v22.x) you should use nftables rules, for older ones it depends, but typically iptables.
|
||||
|
||||
The common dependency is
|
||||
```text
|
||||
@@ -85,7 +86,11 @@ For nftables on OpenWRT rules comes out-of-the-box and stored under `/usr/share/
|
||||
|
||||
Now we go to the configuration. For OpenWRT here is configuration via [UCI](https://openwrt.org/docs/guide-user/base-system/uci) and [LuCI](https://openwrt.org/docs/guide-user/luci/start) available (CLI and GUI respectively).
|
||||
|
||||
Luci is a configuration interface for your router (which you connect when enter 192.168.1.1 in browser). LuCI configuration lives in **Services->youtubeUnblock** section. It is self descriptive, with description for each flag. Note, that after you push `Save & Apply` button, the configuration is applied automatically and the service is restarted.
|
||||
For **LuCI** aka **GUI** aka **web-interface of router** you should install **luci-app-youtubeUnblock** package like you did it with the normal youtubeUnblock package. Note, that lists of official opkg feeds should be loaded (**Do it with Update lists option**).
|
||||
|
||||
If you got ` * pkg_hash_check_unresolved: cannot find dependency luci-lua-runtime for luci-app-youtubeUnblock` error, you are using old openwrt. Install [this dummy package](https://github.com/xiaorouji/openwrt-passwall/files/12605732/luci-lua-runtime_all_fake.zip). [Check this comment](https://github.com/Waujito/youtubeUnblock/issues/168#issuecomment-2449227547) for more details.
|
||||
|
||||
LuCI configuration lives in **Services->youtubeUnblock** section. It is self descriptive, with description for each flag. Note, that after you push `Save & Apply` button, the configuration is applied automatically and the service is restarted.
|
||||
|
||||
UCI configuration is available in /etc/config/youtubeUnblock file, in section `youtubeUnblock.youtubeUnblock`. The configuration is done with [flags](#flags). Note, that names of flags are not the same: you should replace `-` with `_`, you shouldn't use leading `--` for flag. Also you will enable toggle flags (without parameters) with `1`.
|
||||
|
||||
@@ -103,10 +108,19 @@ for example, you can enable it with `/etc/init.d/youtubeUnblock enable`.
|
||||
|
||||
### 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.
|
||||
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).
|
||||
|
||||
Install the binary with `opkg install youtubeUnblock-*.ipk`. After installation, the binary in /opt/bin and the init script in /opt/etc/init.d/S51youtubeUnblock will be available. To run the youtubeUnblock, simply run `/opt/etc/init.d/S51youtubeUnblock start`
|
||||
|
||||
### NFNETLINK_QUEUE kernel modules
|
||||
|
||||
Note, that you should feed the target kernel with nfnetlink_queue kernel module. The module may be disabled or even not present. Entware S51youtubeUnblock will try to insert kmods any way but if they are not provided by software, you should install them manually. AFAIK on keenetics here is a repository with modules compiled by customer. You can find them somewhere in the web interface of your device. On other routers you may want to do deeper research in that case and find your kmods. If you can't find anything, you may ask the customer for GPL codes of linux kernel (and may be even OpenWRT) and compile kmods manually.
|
||||
|
||||
You should insert the module with (this step may be omitted on Entware and OpenWRT):
|
||||
```sh
|
||||
modprobe nfnetlink_queue
|
||||
```
|
||||
|
||||
### PC configuration
|
||||
On local host make sure to change **FORWARD** to **OUTPUT** chain in the following Firewall rulesets.
|
||||
|
||||
@@ -173,7 +187,7 @@ 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 domain list. Defaults to `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. You can exclude some domains with `--exclude-domains` flag.
|
||||
|
||||
- `--exclude-domains=<comma separated domain list>` List of domains to be excluded from targetting.
|
||||
- `--exclude-domains=<comma separated domain list>` List of domains to be excluded from targeting.
|
||||
|
||||
- `--queue-num=<number of netfilter queue>` The number of netfilter queue **youtubeUnblock** will be linked to. Defaults to **537**.
|
||||
|
||||
@@ -181,8 +195,11 @@ Available flags:
|
||||
|
||||
- `--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**.
|
||||
|
||||
- `--fake-sni-type={default|custom|random}` This flag specifies which faking message type should be used for fake packets. For `random`, the message of random length and with random payload will be sent. For `default` the default payload (sni=www.google.com) is used. And for the `custom` option, the payload from `--fake-custom-payload` section utilized. Defaults to `default`.
|
||||
- `--fake-custom-payload=<payload>` Useful with `--fake-sni-type=custom`. You should specify the payload for fake message manually. Use hex format: `--fake-custom-payload=0001020304` mean that 5 bytes sequence: `0x00`, `0x01`, `0x02`, `0x03`, `0x04` used as fake.
|
||||
|
||||
- `--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}` This flag determines the strategy of fake packets invalidation. Defaults to `randseq`
|
||||
- `randseq` specifies that random sequence/acknowledgemend random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled.
|
||||
- `randseq` specifies that random sequence/acknowledgment random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled.
|
||||
- `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured.
|
||||
- `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.
|
||||
@@ -222,17 +239,29 @@ Available flags:
|
||||
|
||||
- `--no-ipv6` Disables support for ipv6. May be useful if you don't want for ipv6 socket to be opened.
|
||||
|
||||
- `--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)
|
||||
- `--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. But if you really want multiple queue instances of youtubeUnblock, note that you should change --queue-num to --queue balance. For example, with 4 threads, use `--queue-balance 537:540` on iptables and `queue num 537-540` on nftables.
|
||||
|
||||
- `--daemonize` Daemonizes the youtubeUnblock (forks and detaches it from the shell). Terminate the program with `killall youtubeUnblock`. If you want to track the logs of youtubeUnblock in logread or journalctl, use **--syslog** flag.
|
||||
|
||||
- `--syslog` Redirects logs to the system log. You can read it with `journalctl` or `logread`.
|
||||
|
||||
- `--noclose` Usable only with `--daemonize`. Will not redirect io streams to /dev/null.
|
||||
|
||||
- `--packet-mark=<mark>` Use this option if youtubeUnblock conflicts with other systems rely on packet mark. Note that you may want to change accept rule for iptables to follow the mark.
|
||||
|
||||
- `--fbegin` and `--fend` flags: youtubeUnblock supports multiple sets of strategies for specific filters. You may want to initiate a new set after the default one, like: `--sni-domains=googlevideo.com --faking-strategy=md5sum --fbegin --sni-domains=youtube.com --faking-strategy=tcp_check --fend --fbegin --sni-domains=l.google.com --faking-strategy=pastseq --fend`. Note, that the priority of these sets goes backwards: last is first, default (one that does not start with --fbegin) is last. If you start the new section, the default settings are implemented just like youtubeUnblock without any parameters. Note that the config above is just an example and won't work for you.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Check up [this issue](https://github.com/Waujito/youtubeUnblock/issues/148) for useful configs.
|
||||
|
||||
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.
|
||||
|
||||
*Kyber* on firefox disables with `security.tls.enable_kyber` in `about:config`.
|
||||
|
||||
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.
|
||||
|
||||
It seems like some TSPUs started to block wrongseq packets, so you should play around with faking strategies. I personally recommend to start with `md5sum` faking strategy.
|
||||
@@ -319,7 +348,9 @@ You can configure the module with its flags in insmod:
|
||||
insmod kyoutubeUnblock.ko fake_sni=1 exclude_domains=.ru quic_drop=1
|
||||
```
|
||||
|
||||
Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`silent=1 quic_drop=1`)
|
||||
Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`quic_drop=1`)
|
||||
|
||||
Also a good thig to mention is verbosity. The kernel module combines --trace and --silent option to the one parameter `verbosity`. This parameter accepts 3 arguments: `trace`, `debug` and `silent`. I highly don't recommend to enable `trace` mod on router because it may cause huge problems with performance and even freeze your device.
|
||||
|
||||
Also a drop in replacement is supported for all the parameters excluding packet mark. A drop in replacement does not require module restart if you want to change the parameters. You can specify and check the parameters within module's directory inside the sysfs: `/sys/module/kyoutubeUnblock/parameters/`. For example, to set quic_drop to true you may use next command:
|
||||
```sh
|
||||
@@ -370,4 +401,11 @@ make package/kyoutubeUnblock/compile V=s
|
||||
|
||||
When the commands finish, the module is ready. Find it with `find bin -name "kmod-youtubeUnblock*.ipk"`, copy to your host and install to the router via gui software interface. The module should start immediately. If not, do `modprobe kyoutubeUnblock`.
|
||||
|
||||
|
||||
## Padavan
|
||||
YoutubeUnblock may also run on Padavan. [Check the manual here\[rus\]](Padavan.md)
|
||||
|
||||
|
||||
>If you have any questions/suggestions/problems feel free to open an [issue](https://github.com/Waujito/youtubeUnblock/issues).
|
||||
|
||||
|
||||
|
||||
548
args.c
548
args.c
@@ -1,5 +1,4 @@
|
||||
#include "config.h"
|
||||
#include "raw_replacements.h"
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
@@ -7,83 +6,63 @@
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "args.h"
|
||||
#include "logging.h"
|
||||
|
||||
static char custom_fake_buf[MAX_FAKE_SIZE];
|
||||
|
||||
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 = 1,
|
||||
.use_ipv6 = 1,
|
||||
.fakeseq_offset = 10000,
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.synfake = 0,
|
||||
.synfake_len = 0,
|
||||
|
||||
.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),
|
||||
|
||||
.exclude_domains_str = "",
|
||||
.exclude_domains_strlen = 0,
|
||||
|
||||
.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
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.use_ipv6 = 1,
|
||||
|
||||
.verbose = VERBOSE_DEBUG,
|
||||
.use_gso = true,
|
||||
|
||||
.default_config = default_section_config,
|
||||
.custom_configs_len = 0,
|
||||
|
||||
.daemonize = 0,
|
||||
.noclose = 0,
|
||||
.syslog = 0,
|
||||
};
|
||||
|
||||
#define OPT_SNI_DOMAINS 1
|
||||
#define OPT_EXCLUDE_DOMAINS 25
|
||||
#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_NO_IPV6 20
|
||||
#define OPT_FAKE_SEQ_OFFSET 21
|
||||
#define OPT_PACKET_MARK 22
|
||||
#define OPT_SYNFAKE 23
|
||||
#define OPT_SYNFAKE_LEN 24
|
||||
#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_SNI_DOMAINS
|
||||
enum {
|
||||
OPT_SNI_DOMAINS,
|
||||
OPT_EXCLUDE_DOMAINS,
|
||||
OPT_FAKE_SNI,
|
||||
OPT_FAKING_TTL,
|
||||
OPT_FAKING_STRATEGY,
|
||||
OPT_FAKE_SNI_SEQ_LEN,
|
||||
OPT_FAKE_SNI_TYPE,
|
||||
OPT_FAKE_CUSTOM_PAYLOAD,
|
||||
OPT_START_SECTION,
|
||||
OPT_END_SECTION,
|
||||
OPT_DAEMONIZE,
|
||||
OPT_NOCLOSE,
|
||||
OPT_SYSLOG,
|
||||
OPT_FRAG,
|
||||
OPT_FRAG_SNI_REVERSE,
|
||||
OPT_FRAG_SNI_FAKED,
|
||||
OPT_FRAG_MIDDLE_SNI,
|
||||
OPT_FRAG_SNI_POS,
|
||||
OPT_FK_WINSIZE,
|
||||
OPT_TRACE,
|
||||
OPT_QUIC_DROP,
|
||||
OPT_SNI_DETECTION,
|
||||
OPT_NO_IPV6,
|
||||
OPT_FAKE_SEQ_OFFSET,
|
||||
OPT_PACKET_MARK,
|
||||
OPT_SYNFAKE,
|
||||
OPT_SYNFAKE_LEN,
|
||||
OPT_SEG2DELAY,
|
||||
OPT_THREADS,
|
||||
OPT_SILENT,
|
||||
OPT_NO_GSO,
|
||||
OPT_QUEUE_NUM,
|
||||
};
|
||||
|
||||
static struct option long_opt[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
@@ -94,6 +73,8 @@ static struct option long_opt[] = {
|
||||
{"synfake", 1, 0, OPT_SYNFAKE},
|
||||
{"synfake-len", 1, 0, OPT_SYNFAKE_LEN},
|
||||
{"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN},
|
||||
{"fake-sni-type", 1, 0, OPT_FAKE_SNI_TYPE},
|
||||
{"fake-custom-payload", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD},
|
||||
{"faking-strategy", 1, 0, OPT_FAKING_STRATEGY},
|
||||
{"fake-seq-offset", 1, 0, OPT_FAKE_SEQ_OFFSET},
|
||||
{"faking-ttl", 1, 0, OPT_FAKING_TTL},
|
||||
@@ -111,8 +92,13 @@ static struct option long_opt[] = {
|
||||
{"trace", 0, 0, OPT_TRACE},
|
||||
{"no-gso", 0, 0, OPT_NO_GSO},
|
||||
{"no-ipv6", 0, 0, OPT_NO_IPV6},
|
||||
{"daemonize", 0, 0, OPT_DAEMONIZE},
|
||||
{"noclose", 0, 0, OPT_NOCLOSE},
|
||||
{"syslog", 0, 0, OPT_SYSLOG},
|
||||
{"queue-num", 1, 0, OPT_QUEUE_NUM},
|
||||
{"packet-mark", 1, 0, OPT_PACKET_MARK},
|
||||
{"fbegin", 0, 0, OPT_START_SECTION},
|
||||
{"fend", 0, 0, OPT_END_SECTION},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
@@ -135,8 +121,13 @@ static long parse_numeric_option(const char* value) {
|
||||
}
|
||||
|
||||
void print_version() {
|
||||
printf("youtubeUnblock\n");
|
||||
printf("Bypasses deep packet inspection systems that relies on SNI\n");
|
||||
printf("youtubeUnblock"
|
||||
#if defined(PKG_VERSION)
|
||||
" " PKG_VERSION
|
||||
#endif
|
||||
"\n"
|
||||
);
|
||||
printf("Bypasses deep packet inspection systems that rely on SNI\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
@@ -150,6 +141,8 @@ void print_usage(const char *argv0) {
|
||||
printf("\t--exclude-domains=<comma separated domain list>\n");
|
||||
printf("\t--fake-sni={1|0}\n");
|
||||
printf("\t--fake-sni-seq-len=<length>\n");
|
||||
printf("\t--fake-sni-type={default|random|custom}\n");
|
||||
printf("\t--fake-custom-payload=<hex payload>\n");
|
||||
printf("\t--fake-seq-offset=<offset>\n");
|
||||
printf("\t--faking-ttl=<ttl>\n");
|
||||
printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}\n");
|
||||
@@ -170,16 +163,30 @@ void print_usage(const char *argv0) {
|
||||
printf("\t--trace\n");
|
||||
printf("\t--no-gso\n");
|
||||
printf("\t--no-ipv6\n");
|
||||
printf("\t--daemonize\n");
|
||||
printf("\t--noclose\n");
|
||||
printf("\t--syslog\n");
|
||||
printf("\t--fbegin\n");
|
||||
printf("\t--fend\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int parse_args(int argc, char *argv[]) {
|
||||
int opt;
|
||||
int optIdx;
|
||||
int optIdx = 0;
|
||||
long num;
|
||||
|
||||
struct section_config_t *sect_config = &config.default_config;
|
||||
|
||||
#define SECT_ITER_DEFAULT 1
|
||||
#define SECT_ITER_INSIDE 2
|
||||
#define SECT_ITER_OUTSIDE 3
|
||||
|
||||
int section_iter = SECT_ITER_DEFAULT;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) {
|
||||
switch (opt) {
|
||||
/* config_t scoped configs */
|
||||
case 'h':
|
||||
print_usage(argv[0]);
|
||||
goto stop_exec;
|
||||
@@ -187,49 +194,107 @@ int parse_args(int argc, char *argv[]) {
|
||||
print_version();
|
||||
goto stop_exec;
|
||||
case OPT_TRACE:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
config.verbose = 2;
|
||||
break;
|
||||
case OPT_SILENT:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.verbose = 0;
|
||||
break;
|
||||
case OPT_NO_GSO:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.use_gso = 0;
|
||||
break;
|
||||
case OPT_NO_IPV6:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.use_ipv6 = 0;
|
||||
break;
|
||||
case OPT_QUIC_DROP:
|
||||
config.quic_drop = 1;
|
||||
case OPT_DAEMONIZE:
|
||||
config.daemonize = 1;
|
||||
break;
|
||||
case OPT_SNI_DOMAINS:
|
||||
if (!strcmp(optarg, "all")) {
|
||||
config.all_domains = 1;
|
||||
}
|
||||
case OPT_NOCLOSE:
|
||||
config.noclose = 1;
|
||||
break;
|
||||
case OPT_SYSLOG:
|
||||
config.syslog = 1;
|
||||
break;
|
||||
case OPT_THREADS:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.domains_str = optarg;
|
||||
config.domains_strlen = strlen(config.domains_str);
|
||||
break;
|
||||
case OPT_EXCLUDE_DOMAINS:
|
||||
config.exclude_domains_str = optarg;
|
||||
config.exclude_domains_strlen = strlen(config.exclude_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 {
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0 || num > MAX_THREADS) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.threads = num;
|
||||
break;
|
||||
case OPT_QUEUE_NUM:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.queue_start_num = num;
|
||||
break;
|
||||
case OPT_PACKET_MARK:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.mark = num;
|
||||
break;
|
||||
case OPT_START_SECTION:
|
||||
if (section_iter != SECT_ITER_DEFAULT && section_iter != SECT_ITER_OUTSIDE)
|
||||
goto invalid_opt;
|
||||
|
||||
sect_config = &config.custom_configs[config.custom_configs_len++];
|
||||
*sect_config = (struct section_config_t)default_section_config;
|
||||
section_iter = SECT_ITER_INSIDE;
|
||||
|
||||
break;
|
||||
case OPT_END_SECTION:
|
||||
if (section_iter != SECT_ITER_INSIDE)
|
||||
goto invalid_opt;
|
||||
|
||||
section_iter = SECT_ITER_OUTSIDE;
|
||||
sect_config = &config.default_config;
|
||||
break;
|
||||
|
||||
/* section_config_t scoped configs */
|
||||
case OPT_SNI_DOMAINS:
|
||||
if (!strcmp(optarg, "all")) {
|
||||
sect_config->all_domains = 1;
|
||||
}
|
||||
|
||||
sect_config->domains_str = optarg;
|
||||
sect_config->domains_strlen = strlen(sect_config->domains_str);
|
||||
break;
|
||||
case OPT_EXCLUDE_DOMAINS:
|
||||
sect_config->exclude_domains_str = optarg;
|
||||
sect_config->exclude_domains_strlen = strlen(sect_config->exclude_domains_str);
|
||||
break;
|
||||
case OPT_FRAG:
|
||||
if (strcmp(optarg, "tcp") == 0) {
|
||||
config.fragmentation_strategy = FRAG_STRAT_TCP;
|
||||
sect_config->fragmentation_strategy = FRAG_STRAT_TCP;
|
||||
} else if (strcmp(optarg, "ip") == 0) {
|
||||
config.fragmentation_strategy = FRAG_STRAT_IP;
|
||||
sect_config->fragmentation_strategy = FRAG_STRAT_IP;
|
||||
} else if (strcmp(optarg, "none") == 0) {
|
||||
config.fragmentation_strategy = FRAG_STRAT_NONE;
|
||||
sect_config->fragmentation_strategy = FRAG_STRAT_NONE;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@@ -237,9 +302,9 @@ int parse_args(int argc, char *argv[]) {
|
||||
break;
|
||||
case OPT_FRAG_SNI_FAKED:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.frag_sni_faked = 1;
|
||||
sect_config->frag_sni_faked = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.frag_sni_faked = 0;
|
||||
sect_config->frag_sni_faked = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@@ -247,9 +312,9 @@ int parse_args(int argc, char *argv[]) {
|
||||
break;
|
||||
case OPT_FRAG_SNI_REVERSE:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.frag_sni_reverse = 1;
|
||||
sect_config->frag_sni_reverse = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.frag_sni_reverse = 0;
|
||||
sect_config->frag_sni_reverse = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@@ -257,9 +322,9 @@ int parse_args(int argc, char *argv[]) {
|
||||
break;
|
||||
case OPT_FRAG_MIDDLE_SNI:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.frag_middle_sni = 1;
|
||||
sect_config->frag_middle_sni = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.frag_middle_sni = 0;
|
||||
sect_config->frag_middle_sni = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@@ -271,19 +336,19 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.frag_sni_pos = num;
|
||||
sect_config->frag_sni_pos = num;
|
||||
break;
|
||||
case OPT_FAKING_STRATEGY:
|
||||
if (strcmp(optarg, "randseq") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_RAND_SEQ;
|
||||
sect_config->faking_strategy = FAKE_STRAT_RAND_SEQ;
|
||||
} else if (strcmp(optarg, "ttl") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_TTL;
|
||||
sect_config->faking_strategy = FAKE_STRAT_TTL;
|
||||
} else if (strcmp(optarg, "tcp_check") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_TCP_CHECK;
|
||||
sect_config->faking_strategy = FAKE_STRAT_TCP_CHECK;
|
||||
} else if (strcmp(optarg, "pastseq") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_PAST_SEQ;
|
||||
sect_config->faking_strategy = FAKE_STRAT_PAST_SEQ;
|
||||
} else if (strcmp(optarg, "md5sum") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_TCP_MD5SUM;
|
||||
sect_config->faking_strategy = FAKE_STRAT_TCP_MD5SUM;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@@ -295,21 +360,21 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.faking_ttl = num;
|
||||
sect_config->faking_ttl = num;
|
||||
break;
|
||||
case OPT_FAKE_SEQ_OFFSET:
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
if (errno != 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.fakeseq_offset = num;
|
||||
sect_config->fakeseq_offset = num;
|
||||
break;
|
||||
case OPT_FAKE_SNI:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.fake_sni = 1;
|
||||
sect_config->fake_sni = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.fake_sni = 0;
|
||||
sect_config->fake_sni = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@@ -321,7 +386,44 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.fake_sni_seq_len = num;
|
||||
sect_config->fake_sni_seq_len = num;
|
||||
break;
|
||||
case OPT_FAKE_SNI_TYPE:
|
||||
if (strcmp(optarg, "default") == 0) {
|
||||
sect_config->fake_sni_type = FAKE_PAYLOAD_DEFAULT;
|
||||
} else if (strcmp(optarg, "random") == 0) {
|
||||
sect_config->fake_sni_type = FAKE_PAYLOAD_RANDOM;
|
||||
} else if (strcmp(optarg, "custom") == 0) {
|
||||
sect_config->fake_sni_type = FAKE_PAYLOAD_CUSTOM;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
break;
|
||||
case OPT_FAKE_CUSTOM_PAYLOAD: {
|
||||
uint8_t *const custom_buf = (uint8_t *)custom_fake_buf;
|
||||
|
||||
const char *custom_hex_fake = optarg;
|
||||
size_t custom_hlen = strlen(custom_hex_fake);
|
||||
if ((custom_hlen & 1) == 1) {
|
||||
printf("Custom fake hex should be divisible by two\n");
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
|
||||
size_t custom_len = custom_hlen >> 1;
|
||||
if (custom_len > MAX_FAKE_SIZE) {
|
||||
printf("Custom fake is too large\n");
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
for (int i = 0; i < custom_len; i++) {
|
||||
sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i);
|
||||
}
|
||||
|
||||
sect_config->fake_custom_pkt_sz = custom_len;
|
||||
sect_config->fake_custom_pkt = (char *)custom_buf;
|
||||
}
|
||||
break;
|
||||
case OPT_FK_WINSIZE:
|
||||
num = parse_numeric_option(optarg);
|
||||
@@ -329,13 +431,35 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.fk_winsize = num;
|
||||
sect_config->fk_winsize = num;
|
||||
break;
|
||||
|
||||
case OPT_SEG2DELAY:
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
sect_config->seg2_delay = num;
|
||||
break;
|
||||
case OPT_QUIC_DROP:
|
||||
sect_config->quic_drop = 1;
|
||||
break;
|
||||
case OPT_SNI_DETECTION:
|
||||
if (strcmp(optarg, "parse") == 0) {
|
||||
sect_config->sni_detection = SNI_DETECTION_PARSE;
|
||||
} else if (strcmp(optarg, "brute") == 0) {
|
||||
sect_config->sni_detection = SNI_DETECTION_BRUTE;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
break;
|
||||
case OPT_SYNFAKE:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.synfake = 1;
|
||||
sect_config->synfake = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.synfake = 0;
|
||||
sect_config->synfake = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@@ -346,49 +470,15 @@ int parse_args(int argc, char *argv[]) {
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.synfake_len = num;
|
||||
sect_config->synfake_len = 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;
|
||||
case OPT_PACKET_MARK:
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.mark = num;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// out:
|
||||
errno = 0;
|
||||
return 0;
|
||||
stop_exec:
|
||||
@@ -404,88 +494,100 @@ error:
|
||||
}
|
||||
|
||||
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.syslog) {
|
||||
printf("Logging to system log\n");
|
||||
}
|
||||
|
||||
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");
|
||||
printf("Fake seq offset set to %u\n", config.fakeseq_offset);
|
||||
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;
|
||||
case FAKE_STRAT_TCP_MD5SUM:
|
||||
printf("md5sum 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.synfake) {
|
||||
printf("Fake SYN payload will be sent with each TCP request SYN packet\n");
|
||||
}
|
||||
|
||||
|
||||
if (config.use_gso) {
|
||||
printf("GSO is enabled\n");
|
||||
lginfo("GSO is enabled\n");
|
||||
}
|
||||
|
||||
if (config.use_ipv6) {
|
||||
printf("IPv6 is enabled\n");
|
||||
lginfo("IPv6 is enabled\n");
|
||||
} else {
|
||||
printf("IPv6 is disabled\n");
|
||||
lginfo("IPv6 is disabled\n");
|
||||
}
|
||||
|
||||
if (config.quic_drop) {
|
||||
printf("All QUIC packets will be dropped\n");
|
||||
lginfo("Detected %d config sections\n", config.custom_configs_len + 1);
|
||||
lginfo("The sections will be processed in order they goes in this output\n");
|
||||
|
||||
ITER_CONFIG_SECTIONS(section) {
|
||||
int section_number = CONFIG_SECTION_NUMBER(section);
|
||||
lginfo("Section #%d\n", section_number);
|
||||
|
||||
switch (section->fragmentation_strategy) {
|
||||
case FRAG_STRAT_TCP:
|
||||
lginfo("Using TCP segmentation\n");
|
||||
break;
|
||||
case FRAG_STRAT_IP:
|
||||
lginfo("Using IP fragmentation\n");
|
||||
break;
|
||||
default:
|
||||
lginfo("SNI fragmentation is disabled\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (config.sni_detection == SNI_DETECTION_BRUTE) {
|
||||
printf("Server Name Extension will be parsed in the bruteforce mode\n");
|
||||
if (section->seg2_delay) {
|
||||
lginfo("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", section->seg2_delay);
|
||||
}
|
||||
|
||||
if (config.all_domains) {
|
||||
printf("All Client Hello will be targetted by youtubeUnblock!\n");
|
||||
if (section->fake_sni) {
|
||||
lginfo("Fake SNI will be sent before each target client hello\n");
|
||||
} else {
|
||||
lginfo("Fake SNI is disabled\n");
|
||||
}
|
||||
|
||||
if (section->frag_sni_reverse) {
|
||||
lginfo("Fragmentation Client Hello will be reversed\n");
|
||||
}
|
||||
|
||||
if (section->frag_sni_faked) {
|
||||
lginfo("Fooling packets will be sent near the original Client Hello\n");
|
||||
}
|
||||
|
||||
if (section->fake_sni_seq_len > 1) {
|
||||
lginfo("Faking sequence of length %d will be built as fake sni\n", section->fake_sni_seq_len);
|
||||
}
|
||||
|
||||
switch (section->faking_strategy) {
|
||||
case FAKE_STRAT_TTL:
|
||||
lginfo("TTL faking strategy will be used with TTL %d\n", section->faking_ttl);
|
||||
break;
|
||||
case FAKE_STRAT_RAND_SEQ:
|
||||
lginfo("Random seq faking strategy will be used\n");
|
||||
lginfo("Fake seq offset set to %u\n", section->fakeseq_offset);
|
||||
break;
|
||||
case FAKE_STRAT_TCP_CHECK:
|
||||
lginfo("TCP checksum faking strategy will be used\n");
|
||||
break;
|
||||
case FAKE_STRAT_PAST_SEQ:
|
||||
lginfo("Past seq faking strategy will be used\n");
|
||||
break;
|
||||
case FAKE_STRAT_TCP_MD5SUM:
|
||||
lginfo("md5sum faking strategy will be used\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (section->fk_winsize) {
|
||||
lginfo("Response TCP window will be set to %d with the appropriate scale\n", section->fk_winsize);
|
||||
}
|
||||
|
||||
if (section->synfake) {
|
||||
lginfo("Fake SYN payload will be sent with each TCP request SYN packet\n");
|
||||
}
|
||||
|
||||
if (section->quic_drop) {
|
||||
lginfo("All QUIC packets will be dropped\n");
|
||||
}
|
||||
|
||||
if (section->sni_detection == SNI_DETECTION_BRUTE) {
|
||||
lginfo("Server Name Extension will be parsed in the bruteforce mode\n");
|
||||
}
|
||||
|
||||
if (section->all_domains) {
|
||||
lginfo("All Client Hello will be targeted by youtubeUnblock!\n");
|
||||
} else {
|
||||
lginfo("Target sni domains: %s\n", section->domains_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
140
config.h
140
config.h
@@ -5,12 +5,14 @@
|
||||
#define USER_SPACE
|
||||
#endif
|
||||
|
||||
#include "raw_replacements.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);
|
||||
typedef int (*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;
|
||||
@@ -18,11 +20,10 @@ struct instance_config_t {
|
||||
};
|
||||
extern struct instance_config_t instance_config;
|
||||
|
||||
struct config_t {
|
||||
unsigned int queue_start_num;
|
||||
int threads;
|
||||
int use_gso;
|
||||
int use_ipv6;
|
||||
struct section_config_t {
|
||||
const char *domains_str;
|
||||
unsigned int domains_strlen;
|
||||
|
||||
int fragmentation_strategy;
|
||||
int frag_sni_reverse;
|
||||
int frag_sni_faked;
|
||||
@@ -32,32 +33,100 @@ struct config_t {
|
||||
unsigned char faking_ttl;
|
||||
int fake_sni;
|
||||
unsigned int fake_sni_seq_len;
|
||||
|
||||
#define FAKE_PAYLOAD_RANDOM 0
|
||||
#define FAKE_PAYLOAD_CUSTOM 1
|
||||
// In default mode all other options will be skipped.
|
||||
#define FAKE_PAYLOAD_DEFAULT 2
|
||||
int fake_sni_type;
|
||||
|
||||
int quic_drop;
|
||||
|
||||
/* In milliseconds */
|
||||
unsigned int seg2_delay;
|
||||
int synfake;
|
||||
unsigned int synfake_len;
|
||||
|
||||
const char *exclude_domains_str;
|
||||
unsigned int exclude_domains_strlen;
|
||||
unsigned int all_domains;
|
||||
|
||||
const char *fake_sni_pkt;
|
||||
unsigned int fake_sni_pkt_sz;
|
||||
|
||||
const char *fake_custom_pkt;
|
||||
unsigned int fake_custom_pkt_sz;
|
||||
|
||||
unsigned int fk_winsize;
|
||||
int fakeseq_offset;
|
||||
|
||||
#define SNI_DETECTION_PARSE 0
|
||||
#define SNI_DETECTION_BRUTE 1
|
||||
int sni_detection;
|
||||
|
||||
};
|
||||
|
||||
#define MAX_CONFIGLIST_LEN 64
|
||||
|
||||
struct config_t {
|
||||
unsigned int queue_start_num;
|
||||
int threads;
|
||||
int use_gso;
|
||||
int use_ipv6;
|
||||
unsigned int mark;
|
||||
int daemonize;
|
||||
// Same as daemon() noclose
|
||||
int noclose;
|
||||
int syslog;
|
||||
|
||||
#define VERBOSE_INFO 0
|
||||
#define VERBOSE_DEBUG 1
|
||||
#define VERBOSE_TRACE 2
|
||||
int verbose;
|
||||
int quic_drop;
|
||||
#define SNI_DETECTION_PARSE 0
|
||||
#define SNI_DETECTION_BRUTE 1
|
||||
int sni_detection;
|
||||
/* In milliseconds */
|
||||
unsigned int seg2_delay;
|
||||
const char *domains_str;
|
||||
unsigned int domains_strlen;
|
||||
const char *exclude_domains_str;
|
||||
unsigned int exclude_domains_strlen;
|
||||
unsigned int all_domains;
|
||||
const char *fake_sni_pkt;
|
||||
unsigned int fake_sni_pkt_sz;
|
||||
unsigned int fk_winsize;
|
||||
unsigned int fakeseq_offset;
|
||||
unsigned int mark;
|
||||
int synfake;
|
||||
unsigned int synfake_len;
|
||||
|
||||
struct section_config_t default_config;
|
||||
struct section_config_t custom_configs[MAX_CONFIGLIST_LEN];
|
||||
int custom_configs_len;
|
||||
};
|
||||
|
||||
extern struct config_t config;
|
||||
|
||||
#define ITER_CONFIG_SECTIONS(section) \
|
||||
for (struct section_config_t *section = &config.default_config + config.custom_configs_len; section >= &config.default_config; section--)
|
||||
|
||||
#define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config)
|
||||
|
||||
#define default_section_config { \
|
||||
.frag_sni_reverse = 1, \
|
||||
.frag_sni_faked = 0, \
|
||||
.fragmentation_strategy = FRAGMENTATION_STRATEGY, \
|
||||
.faking_strategy = FAKING_STRATEGY, \
|
||||
.faking_ttl = FAKE_TTL, \
|
||||
.fake_sni = 1, \
|
||||
.fake_sni_seq_len = 1, \
|
||||
.fake_sni_type = FAKE_PAYLOAD_DEFAULT, \
|
||||
.frag_middle_sni = 1, \
|
||||
.frag_sni_pos = 1, \
|
||||
.fakeseq_offset = 10000, \
|
||||
.synfake = 0, \
|
||||
.synfake_len = 0, \
|
||||
.quic_drop = 0, \
|
||||
\
|
||||
.seg2_delay = 0, \
|
||||
\
|
||||
.domains_str = defaul_snistr, \
|
||||
.domains_strlen = sizeof(defaul_snistr), \
|
||||
\
|
||||
.exclude_domains_str = "", \
|
||||
.exclude_domains_strlen = 0, \
|
||||
\
|
||||
.fake_sni_pkt = fake_sni_old, \
|
||||
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \
|
||||
.fake_custom_pkt = custom_fake_buf, \
|
||||
.fake_custom_pkt_sz = 0, \
|
||||
.sni_detection = SNI_DETECTION_PARSE, \
|
||||
}
|
||||
|
||||
#define MAX_THREADS 16
|
||||
|
||||
#ifndef THREADS_NUM
|
||||
@@ -89,26 +158,37 @@ extern struct config_t config;
|
||||
#define FAKE_TTL 8
|
||||
|
||||
// Will invalidate fake packets by out-of-ack_seq out-of-seq request
|
||||
#define FAKE_STRAT_RAND_SEQ 1
|
||||
#define FAKE_STRAT_RAND_SEQ (1 << 0)
|
||||
// Will assume that GGC server is located further than FAKE_TTL
|
||||
// Thus, Fake packet will be eliminated automatically.
|
||||
#define FAKE_STRAT_TTL 2
|
||||
#define FAKE_STRAT_PAST_SEQ 3
|
||||
#define FAKE_STRAT_TCP_CHECK 4
|
||||
#define FAKE_STRAT_TCP_MD5SUM 5
|
||||
#define FAKE_STRAT_TTL (1 << 1)
|
||||
#define FAKE_STRAT_PAST_SEQ (1 << 2)
|
||||
#define FAKE_STRAT_TCP_CHECK (1 << 3)
|
||||
#define FAKE_STRAT_TCP_MD5SUM (1 << 4)
|
||||
|
||||
#define FAKE_STRAT_COUNT 5
|
||||
|
||||
/**
|
||||
* This macros iterates through all faking strategies and executes code under it.
|
||||
* destination strategy will be available under name of `strategy` variable.
|
||||
*/
|
||||
#define ITER_FAKE_STRAT(fake_bitmask, strategy) \
|
||||
for (int strategy = 1; strategy <= (1 << FAKE_STRAT_COUNT); strategy <<= 1) \
|
||||
if ((fake_bitmask) & strategy)
|
||||
|
||||
#ifndef FAKING_STRATEGY
|
||||
#define FAKING_STRATEGY FAKE_STRAT_PAST_SEQ
|
||||
#endif
|
||||
|
||||
#define MAX_FAKE_SIZE 1300
|
||||
|
||||
#if !defined(SILENT) && !defined(KERNEL_SPACE)
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
||||
// The Maximum Transmission Unit size for rawsocket
|
||||
// Larger packets will be fragmented. Applicable for Chrome's kyber.
|
||||
#define AVAILABLE_MTU 1500
|
||||
#define AVAILABLE_MTU 1400
|
||||
|
||||
#define DEFAULT_QUEUE_NUM 537
|
||||
|
||||
|
||||
269
kargs.c
269
kargs.c
@@ -1,54 +1,27 @@
|
||||
#include "config.h"
|
||||
#include "raw_replacements.h"
|
||||
#include "types.h"
|
||||
#include <linux/moduleparam.h>
|
||||
#include "types.h"
|
||||
|
||||
#define STR_MAXLEN 2048
|
||||
|
||||
static char custom_fake_buf[MAX_FAKE_SIZE];
|
||||
|
||||
struct config_t config = {
|
||||
.frag_sni_reverse = 1,
|
||||
.frag_sni_faked = 0,
|
||||
.fragmentation_strategy = FRAGMENTATION_STRATEGY,
|
||||
.faking_strategy = FAKING_STRATEGY,
|
||||
.faking_ttl = FAKE_TTL,
|
||||
.fake_sni = 1,
|
||||
.fake_sni_seq_len = 1,
|
||||
.frag_middle_sni = 1,
|
||||
.frag_sni_pos = 1,
|
||||
.use_ipv6 = 1,
|
||||
.fakeseq_offset = 10000,
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.synfake = 0,
|
||||
.synfake_len = 0,
|
||||
|
||||
.sni_detection = SNI_DETECTION_PARSE,
|
||||
|
||||
#ifdef SEG2_DELAY
|
||||
.seg2_delay = SEG2_DELAY,
|
||||
#else
|
||||
.seg2_delay = 0,
|
||||
#endif
|
||||
|
||||
#ifdef USE_GSO
|
||||
.use_gso = 1,
|
||||
#else
|
||||
.use_gso = false,
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
.verbose = 2,
|
||||
#else
|
||||
.verbose = 1,
|
||||
#endif
|
||||
|
||||
.domains_str = defaul_snistr,
|
||||
.domains_strlen = sizeof(defaul_snistr),
|
||||
|
||||
.threads = THREADS_NUM,
|
||||
.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
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.use_ipv6 = 1,
|
||||
|
||||
.verbose = VERBOSE_DEBUG,
|
||||
.use_gso = 1,
|
||||
|
||||
.default_config = default_section_config,
|
||||
.custom_configs_len = 0
|
||||
};
|
||||
|
||||
#define def_section (&config.default_config)
|
||||
|
||||
static int unumeric_set(const char *val, const struct kernel_param *kp) {
|
||||
int n = 0, ret;
|
||||
ret = kstrtoint(val, 10, &n);
|
||||
@@ -107,18 +80,19 @@ static const struct kernel_param_ops inverse_boolean_ops = {
|
||||
.get = inverse_boolean_get,
|
||||
};
|
||||
|
||||
module_param_cb(fake_sni, &boolean_parameter_ops, &config.fake_sni, 0664);
|
||||
module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &config.fake_sni_seq_len, 0664);
|
||||
module_param_cb(faking_ttl, &unumeric_parameter_ops, &config.faking_ttl, 0664);
|
||||
module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &config.fakeseq_offset, 0664);
|
||||
module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &config.frag_sni_reverse, 0664);
|
||||
module_param_cb(frag_sni_faked, &boolean_parameter_ops, &config.frag_sni_faked, 0664);
|
||||
module_param_cb(frag_middle_sni, &boolean_parameter_ops, &config.frag_middle_sni, 0664);
|
||||
module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &config.frag_sni_pos, 0664);
|
||||
module_param_cb(fk_winsize, &unumeric_parameter_ops, &config.fk_winsize, 0664);
|
||||
module_param_cb(synfake, &boolean_parameter_ops, &config.synfake, 0664);
|
||||
module_param_cb(synfake_len, &unumeric_parameter_ops, &config.synfake_len, 0664);
|
||||
module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664);
|
||||
module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &def_section->fake_sni_seq_len, 0664);
|
||||
module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664);
|
||||
module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664);
|
||||
module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &def_section->frag_sni_reverse, 0664);
|
||||
module_param_cb(frag_sni_faked, &boolean_parameter_ops, &def_section->frag_sni_faked, 0664);
|
||||
module_param_cb(frag_middle_sni, &boolean_parameter_ops, &def_section->frag_middle_sni, 0664);
|
||||
module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664);
|
||||
module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664);
|
||||
module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664);
|
||||
module_param_cb(synfake_len, &unumeric_parameter_ops, &def_section->synfake_len, 0664);
|
||||
module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664);
|
||||
// module_param_cb(seg2delay, &unumeric_parameter_ops, &def_section->seg2_delay, 0664);
|
||||
|
||||
static int sni_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@@ -137,13 +111,13 @@ static int sni_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
ret = param_set_charp(val, kp);
|
||||
|
||||
if (ret < 0) {
|
||||
config.domains_strlen = 0;
|
||||
def_section->domains_strlen = 0;
|
||||
} else {
|
||||
config.domains_strlen = len;
|
||||
def_section->domains_strlen = len;
|
||||
if (len == 3 && !strncmp(val, "all", len)) {
|
||||
config.all_domains = 1;
|
||||
def_section->all_domains = 1;
|
||||
} else {
|
||||
config.all_domains = 0;
|
||||
def_section->all_domains = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,7 +130,7 @@ static const struct kernel_param_ops sni_domains_ops = {
|
||||
.get = param_get_charp,
|
||||
};
|
||||
|
||||
module_param_cb(sni_domains, &sni_domains_ops, &config.domains_str, 0664);
|
||||
module_param_cb(sni_domains, &sni_domains_ops, &def_section->domains_str, 0664);
|
||||
|
||||
static int exclude_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@@ -171,9 +145,9 @@ static int exclude_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
ret = param_set_charp(val, kp);
|
||||
|
||||
if (ret < 0) {
|
||||
config.exclude_domains_strlen = 0;
|
||||
def_section->exclude_domains_strlen = 0;
|
||||
} else {
|
||||
config.exclude_domains_strlen = len;
|
||||
def_section->exclude_domains_strlen = len;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -184,36 +158,62 @@ static const struct kernel_param_ops exclude_domains_ops = {
|
||||
.get = param_get_charp,
|
||||
};
|
||||
|
||||
module_param_cb(exclude_domains, &exclude_domains_ops, &config.exclude_domains_str, 0664);
|
||||
module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664);
|
||||
|
||||
module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664);
|
||||
module_param_cb(silent, &inverse_boolean_ops, &config.verbose, 0664);
|
||||
module_param_cb(quic_drop, &boolean_parameter_ops, &config.quic_drop, 0664);
|
||||
module_param_cb(quic_drop, &boolean_parameter_ops, &def_section->quic_drop, 0664);
|
||||
|
||||
static int verbose_trace_set(const char *val, const struct kernel_param *kp) {
|
||||
int n = 0, ret;
|
||||
ret = kstrtoint(val, 10, &n);
|
||||
if (ret != 0 || (n != 0 && n != 1))
|
||||
return -EINVAL;
|
||||
static int verbosity_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
|
||||
if (n) {
|
||||
n = VERBOSE_TRACE;
|
||||
} else {
|
||||
n = VERBOSE_DEBUG;
|
||||
len = strnlen(val, STR_MAXLEN + 1);
|
||||
if (len == STR_MAXLEN + 1) {
|
||||
pr_err("%s: string parameter too long\n", kp->name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (len >= 1 && val[len - 1] == '\n') {
|
||||
len--;
|
||||
}
|
||||
|
||||
if (strncmp(val, "trace", len) == 0) {
|
||||
*(int *)kp->arg = VERBOSE_TRACE;
|
||||
} else if (strncmp(val, "debug", len) == 0) {
|
||||
*(int *)kp->arg = VERBOSE_DEBUG;
|
||||
} else if (strncmp(val, "silent", len) == 0) {
|
||||
*(int *)kp->arg = VERBOSE_INFO;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (kp->arg == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
*(int *)kp->arg = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops verbose_trace_ops = {
|
||||
.set = verbose_trace_set,
|
||||
.get = param_get_int,
|
||||
|
||||
static int verbosity_get(char *buffer, const struct kernel_param *kp) {
|
||||
switch (*(int *)kp->arg) {
|
||||
case VERBOSE_TRACE:
|
||||
strcpy(buffer, "trace\n");
|
||||
break;
|
||||
case VERBOSE_DEBUG:
|
||||
strcpy(buffer, "debug\n");
|
||||
break;
|
||||
case VERBOSE_INFO:
|
||||
strcpy(buffer, "silent\n");
|
||||
break;
|
||||
default:
|
||||
strcpy(buffer, "unknown\n");
|
||||
}
|
||||
|
||||
return strlen(buffer);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops verbosity_ops = {
|
||||
.set = verbosity_set,
|
||||
.get = verbosity_get,
|
||||
};
|
||||
|
||||
module_param_cb(trace, &verbose_trace_ops, &config.verbose, 0664);
|
||||
module_param_cb(verbosity, &verbosity_ops, &config.verbose, 0664);
|
||||
|
||||
static int frag_strat_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@@ -264,7 +264,7 @@ static const struct kernel_param_ops frag_strat_ops = {
|
||||
.get = frag_strat_get,
|
||||
};
|
||||
|
||||
module_param_cb(fragmentation_strategy, &frag_strat_ops, &config.fragmentation_strategy, 0664);
|
||||
module_param_cb(fragmentation_strategy, &frag_strat_ops, &def_section->fragmentation_strategy, 0664);
|
||||
|
||||
static int fake_strat_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@@ -325,7 +325,7 @@ static const struct kernel_param_ops fake_strat_ops = {
|
||||
.get = fake_strat_get,
|
||||
};
|
||||
|
||||
module_param_cb(faking_strategy, &fake_strat_ops, &config.faking_strategy, 0664);
|
||||
module_param_cb(faking_strategy, &fake_strat_ops, &def_section->faking_strategy, 0664);
|
||||
|
||||
static int sni_detection_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@@ -371,4 +371,111 @@ static const struct kernel_param_ops sni_detection_ops = {
|
||||
.get = sni_detection_get,
|
||||
};
|
||||
|
||||
module_param_cb(sni_detection, &sni_detection_ops, &config.sni_detection, 0664);
|
||||
module_param_cb(sni_detection, &sni_detection_ops, &def_section->sni_detection, 0664);
|
||||
|
||||
static int fake_type_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
|
||||
len = strnlen(val, STR_MAXLEN + 1);
|
||||
if (len == STR_MAXLEN + 1) {
|
||||
pr_err("%s: string parameter too long\n", kp->name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (len >= 1 && val[len - 1] == '\n') {
|
||||
len--;
|
||||
}
|
||||
|
||||
if (strncmp(val, "default", len) == 0) {
|
||||
*(int *)kp->arg = FAKE_PAYLOAD_DEFAULT;
|
||||
} else if (strncmp(val, "custom", len) == 0) {
|
||||
*(int *)kp->arg = FAKE_PAYLOAD_CUSTOM;
|
||||
} else if (strncmp(val, "random", len) == 0) {
|
||||
*(int *)kp->arg = FAKE_PAYLOAD_RANDOM;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fake_type_get(char *buffer, const struct kernel_param *kp) {
|
||||
switch (*(int *)kp->arg) {
|
||||
case FAKE_PAYLOAD_DEFAULT:
|
||||
strcpy(buffer, "default\n");
|
||||
break;
|
||||
case FAKE_PAYLOAD_RANDOM:
|
||||
strcpy(buffer, "random\n");
|
||||
break;
|
||||
case FAKE_PAYLOAD_CUSTOM:
|
||||
strcpy(buffer, "custom\n");
|
||||
break;
|
||||
default:
|
||||
strcpy(buffer, "unknown\n");
|
||||
}
|
||||
|
||||
return strlen(buffer);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops fake_type_ops = {
|
||||
.set = fake_type_set,
|
||||
.get = fake_type_get,
|
||||
};
|
||||
|
||||
module_param_cb(fake_sni_type, &fake_type_ops, &def_section->fake_sni_type, 0664);
|
||||
|
||||
static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
|
||||
len = strnlen(val, STR_MAXLEN + 1);
|
||||
if (len == STR_MAXLEN + 1) {
|
||||
pr_err("%s: string parameter too long\n", kp->name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (len >= 1 && val[len - 1] == '\n') {
|
||||
len--;
|
||||
}
|
||||
|
||||
uint8_t *const custom_buf = (uint8_t *)custom_fake_buf;
|
||||
const char *custom_hex_fake = val;
|
||||
size_t custom_hlen = len;
|
||||
|
||||
if ((custom_hlen & 1) == 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
size_t custom_len = custom_hlen >> 1;
|
||||
if (custom_len > MAX_FAKE_SIZE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < custom_len; i++) {
|
||||
sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i);
|
||||
}
|
||||
|
||||
def_section->fake_custom_pkt_sz = custom_len;
|
||||
def_section->fake_custom_pkt = (char *)custom_buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) {
|
||||
int cflen = def_section->fake_custom_pkt_sz;
|
||||
const uint8_t *cbf_data = def_section->fake_custom_pkt;
|
||||
int bflen = def_section->fake_custom_pkt_sz << 1;
|
||||
|
||||
for (int i = 0; i < cflen; i++) {
|
||||
sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i));
|
||||
}
|
||||
|
||||
return bflen;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops fake_custom_pl_ops = {
|
||||
.set = fake_custom_pl_set,
|
||||
.get = fake_custom_pl_get,
|
||||
};
|
||||
|
||||
module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &def_section->fake_custom_pkt, 0664);
|
||||
|
||||
2
kmake.mk
2
kmake.mk
@@ -13,7 +13,7 @@ KERNEL_BUILDER_MAKEDIR:=/lib/modules/$(shell uname -r)/build
|
||||
kmake: kmod
|
||||
|
||||
kmod:
|
||||
$(MAKE) -C $(KERNEL_BUILDER_MAKEDIR) M=$(PWD) modules
|
||||
$(MAKE) -C $(KERNEL_BUILDER_MAKEDIR) M=$(PWD) EXTRA_CFLAGS='-DPKG_VERSION=\"$(PKG_FULLVERSION)\"' modules
|
||||
|
||||
kload:
|
||||
insmod kyoutubeUnblock.ko
|
||||
|
||||
226
kmod_utils.c
226
kmod_utils.c
@@ -1,226 +0,0 @@
|
||||
#ifndef KERNEL_SPACE
|
||||
#error "You are trying to compile the kernel module not in the kernel space"
|
||||
#endif
|
||||
#include "kmod_utils.h"
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
|
||||
static struct socket *rawsocket;
|
||||
|
||||
static struct socket *raw6socket;
|
||||
|
||||
|
||||
int open_raw_socket(void) {
|
||||
int ret = 0;
|
||||
ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_alert("Unable to create raw socket\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// That's funny, but this is how it is done in the kernel
|
||||
// https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916
|
||||
rawsocket->sk->sk_mark=config.mark;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void close_raw_socket(void) {
|
||||
sock_release(rawsocket);
|
||||
}
|
||||
|
||||
static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret = 0;
|
||||
if (pktlen > AVAILABLE_MTU) return -ENOMEM;
|
||||
|
||||
struct iphdr *iph;
|
||||
|
||||
if ((ret = ip4_payload_split(
|
||||
(uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in daddr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = 0,
|
||||
.sin_addr = {
|
||||
.s_addr = iph->daddr
|
||||
}
|
||||
};
|
||||
|
||||
struct msghdr msg;
|
||||
struct kvec iov;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
iov.iov_base = (__u8 *)pkt;
|
||||
iov.iov_len = pktlen;
|
||||
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_name = &daddr;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
|
||||
|
||||
ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int open_raw6_socket(void) {
|
||||
int ret = 0;
|
||||
ret = sock_create(AF_INET6, SOCK_RAW, IPPROTO_RAW, &raw6socket);
|
||||
|
||||
if (ret < 0) {
|
||||
pr_alert("Unable to create raw socket\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// That's funny, but this is how it is done in the kernel
|
||||
// https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916
|
||||
raw6socket->sk->sk_mark=config.mark;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void close_raw6_socket(void) {
|
||||
sock_release(raw6socket);
|
||||
}
|
||||
|
||||
int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret = 0;
|
||||
if (pktlen > AVAILABLE_MTU) return -ENOMEM;
|
||||
|
||||
struct ip6_hdr *iph;
|
||||
|
||||
if ((ret = ip6_payload_split(
|
||||
(uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in6 daddr = {
|
||||
.sin6_family = AF_INET6,
|
||||
/* Always 0 for raw socket */
|
||||
.sin6_port = 0,
|
||||
.sin6_addr = iph->ip6_dst
|
||||
};
|
||||
|
||||
struct kvec iov;
|
||||
struct msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
iov.iov_base = (__u8 *)pkt;
|
||||
iov.iov_len = pktlen;
|
||||
|
||||
msg.msg_flags = 0;
|
||||
msg.msg_name = &daddr;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
|
||||
ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret;
|
||||
|
||||
if (pktlen > AVAILABLE_MTU) {
|
||||
lgdebug("The packet is too big and may cause issues!");
|
||||
|
||||
NETBUF_ALLOC(buff1, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buff1)) {
|
||||
lgerror("Allocation error", -ENOMEM);
|
||||
return -ENOMEM;
|
||||
}
|
||||
NETBUF_ALLOC(buff2, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buff2)) {
|
||||
lgerror("Allocation error", -ENOMEM);
|
||||
NETBUF_FREE(buff2);
|
||||
return -ENOMEM;
|
||||
}
|
||||
uint32_t buff1_size = MAX_PACKET_SIZE;
|
||||
uint32_t buff2_size = MAX_PACKET_SIZE;
|
||||
|
||||
switch (config.fragmentation_strategy) {
|
||||
case FRAG_STRAT_TCP:
|
||||
if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
|
||||
goto erret_lc;
|
||||
}
|
||||
break;
|
||||
case FRAG_STRAT_IP:
|
||||
if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
|
||||
goto erret_lc;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pr_info("send_raw_socket: Packet is too big but fragmentation is disabled!");
|
||||
ret = -EINVAL;
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
int sent = 0;
|
||||
ret = send_raw_socket(buff1, buff1_size);
|
||||
|
||||
if (ret >= 0) sent += ret;
|
||||
else {
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
ret = send_raw_socket(buff2, buff2_size);
|
||||
if (ret >= 0) sent += ret;
|
||||
else {
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
NETBUF_FREE(buff1);
|
||||
NETBUF_FREE(buff2);
|
||||
return sent;
|
||||
erret_lc:
|
||||
NETBUF_FREE(buff1);
|
||||
NETBUF_FREE(buff2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipvx = netproto_version(pkt, pktlen);
|
||||
|
||||
if (ipvx == IP4VERSION)
|
||||
return send_raw_ipv4(pkt, pktlen);
|
||||
|
||||
else if (ipvx == IP6VERSION)
|
||||
return send_raw_ipv6(pkt, pktlen);
|
||||
|
||||
printf("proto version %d is unsupported\n", ipvx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
|
||||
pr_info("delay_packet_send won't work on current youtubeUnblock version");
|
||||
send_raw_socket(data, data_len);
|
||||
}
|
||||
|
||||
struct instance_config_t instance_config = {
|
||||
.send_raw_packet = send_raw_socket,
|
||||
.send_delayed_packet = delay_packet_send,
|
||||
};
|
||||
14
kmod_utils.h
14
kmod_utils.h
@@ -1,14 +0,0 @@
|
||||
#include "types.h"
|
||||
|
||||
#ifndef KMOD_UTILS_H
|
||||
#define KMOD_UTILS_H
|
||||
|
||||
int open_raw_socket(void);
|
||||
void close_raw_socket(void);
|
||||
int open_raw6_socket(void);
|
||||
void close_raw6_socket(void);
|
||||
int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen);
|
||||
int send_raw_socket(const uint8_t *pkt, uint32_t pktlen);
|
||||
void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms);
|
||||
|
||||
#endif /* KMOD_UTILS_H */
|
||||
342
kytunblock.c
342
kytunblock.c
@@ -1,14 +1,16 @@
|
||||
#include "nf_wrapper.h"
|
||||
#ifndef KERNEL_SPACE
|
||||
#error "You are trying to compile the kernel module not in the kernel space"
|
||||
#endif
|
||||
|
||||
// Kernel module for youtubeUnblock.
|
||||
// Make with make kmake && sudo iptables -t mangle -D OUTPUT 1 && sudo make kreload && sudo iptables -t mangle -I OUTPUT -p tcp -j YTUNBLOCK
|
||||
// Build with make kmake
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
@@ -18,12 +20,280 @@
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
#include "kmod_utils.h"
|
||||
|
||||
#if defined(PKG_VERSION)
|
||||
MODULE_VERSION(PKG_VERSION);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.3.2");
|
||||
MODULE_AUTHOR("Vadim Vetrov <vetrovvd@gmail.com>");
|
||||
MODULE_DESCRIPTION("Linux kernel module for youtube unblock");
|
||||
MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock");
|
||||
|
||||
static struct socket *rawsocket;
|
||||
|
||||
static struct socket *raw6socket;
|
||||
|
||||
static int open_raw_socket(void) {
|
||||
int ret = 0;
|
||||
ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket);
|
||||
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "Unable to create raw socket\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// That's funny, but this is how it is done in the kernel
|
||||
// https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916
|
||||
rawsocket->sk->sk_mark=config.mark;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void close_raw_socket(void) {
|
||||
sock_release(rawsocket);
|
||||
}
|
||||
|
||||
static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret = 0;
|
||||
if (pktlen > AVAILABLE_MTU) return -ENOMEM;
|
||||
|
||||
struct iphdr *iph;
|
||||
|
||||
if ((ret = ip4_payload_split(
|
||||
(uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in daddr = {
|
||||
.sin_family = AF_INET,
|
||||
.sin_port = 0,
|
||||
.sin_addr = {
|
||||
.s_addr = iph->daddr
|
||||
}
|
||||
};
|
||||
|
||||
struct msghdr msg;
|
||||
struct kvec iov;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
iov.iov_base = (__u8 *)pkt;
|
||||
iov.iov_len = pktlen;
|
||||
|
||||
msg.msg_flags = MSG_DONTWAIT;
|
||||
msg.msg_name = &daddr;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
|
||||
|
||||
ret = kernel_sendmsg(rawsocket, &msg, &iov, 1, pktlen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int open_raw6_socket(void) {
|
||||
int ret = 0;
|
||||
ret = sock_create(AF_INET6, SOCK_RAW, IPPROTO_RAW, &raw6socket);
|
||||
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "Unable to create raw socket\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
// That's funny, but this is how it is done in the kernel
|
||||
// https://elixir.bootlin.com/linux/v3.17.7/source/net/core/sock.c#L916
|
||||
raw6socket->sk->sk_mark=config.mark;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void close_raw6_socket(void) {
|
||||
sock_release(raw6socket);
|
||||
}
|
||||
|
||||
static int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret = 0;
|
||||
if (pktlen > AVAILABLE_MTU) return -ENOMEM;
|
||||
|
||||
struct ip6_hdr *iph;
|
||||
|
||||
if ((ret = ip6_payload_split(
|
||||
(uint8_t *)pkt, pktlen, &iph, NULL, NULL, NULL)) < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct sockaddr_in6 daddr = {
|
||||
.sin6_family = AF_INET6,
|
||||
/* Always 0 for raw socket */
|
||||
.sin6_port = 0,
|
||||
.sin6_addr = iph->ip6_dst
|
||||
};
|
||||
|
||||
struct kvec iov;
|
||||
struct msghdr msg;
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
|
||||
iov.iov_base = (__u8 *)pkt;
|
||||
iov.iov_len = pktlen;
|
||||
|
||||
msg.msg_flags = MSG_DONTWAIT;
|
||||
msg.msg_name = &daddr;
|
||||
msg.msg_namelen = sizeof(struct sockaddr_in6);
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
|
||||
ret = kernel_sendmsg(raw6socket, &msg, &iov, 1, pktlen);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret;
|
||||
|
||||
if (pktlen > AVAILABLE_MTU) {
|
||||
lgdebug("The packet is too big and may cause issues!");
|
||||
|
||||
NETBUF_ALLOC(buff1, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buff1)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
return -ENOMEM;
|
||||
}
|
||||
NETBUF_ALLOC(buff2, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buff2)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
NETBUF_FREE(buff2);
|
||||
return -ENOMEM;
|
||||
}
|
||||
uint32_t buff1_size = MAX_PACKET_SIZE;
|
||||
uint32_t buff2_size = MAX_PACKET_SIZE;
|
||||
|
||||
if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
int sent = 0;
|
||||
ret = send_raw_socket(buff1, buff1_size);
|
||||
|
||||
if (ret >= 0) sent += ret;
|
||||
else {
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
ret = send_raw_socket(buff2, buff2_size);
|
||||
if (ret >= 0) sent += ret;
|
||||
else {
|
||||
goto erret_lc;
|
||||
}
|
||||
|
||||
NETBUF_FREE(buff1);
|
||||
NETBUF_FREE(buff2);
|
||||
return sent;
|
||||
erret_lc:
|
||||
NETBUF_FREE(buff1);
|
||||
NETBUF_FREE(buff2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipvx = netproto_version(pkt, pktlen);
|
||||
|
||||
if (ipvx == IP4VERSION) {
|
||||
return send_raw_ipv4(pkt, pktlen);
|
||||
} else if (ipvx == IP6VERSION) {
|
||||
return send_raw_ipv6(pkt, pktlen);
|
||||
} else {
|
||||
printf("proto version %d is unsupported\n", ipvx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lgtrace_addp("raw_sock_send: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
|
||||
lginfo("delay_packet_send won't work on current youtubeUnblock version");
|
||||
return send_raw_socket(data, data_len);
|
||||
}
|
||||
|
||||
struct instance_config_t instance_config = {
|
||||
.send_raw_packet = send_raw_socket,
|
||||
.send_delayed_packet = delay_packet_send,
|
||||
};
|
||||
|
||||
|
||||
/* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */
|
||||
#ifdef RHEL_RELEASE_CODE
|
||||
|
||||
#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 2)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
const struct nf_hook_state *state) \
|
||||
|
||||
#elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#else
|
||||
|
||||
#error "Sorry; this version of RHEL is not supported because it's kind of old."
|
||||
|
||||
#endif /* RHEL_RELEASE_CODE >= x */
|
||||
|
||||
|
||||
/* If this NOT a RedHat-based kernel (Ubuntu, Debian, SuSE, etc)... */
|
||||
#else
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
void *priv, \
|
||||
struct sk_buff *skb, \
|
||||
const struct nf_hook_state *state)
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct nf_hook_state *state)
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
unsigned int hooknum, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#else
|
||||
#error "Linux < 3.0 isn't supported at all."
|
||||
|
||||
#endif /* LINUX_VERSION_CODE > n */
|
||||
|
||||
#endif /* RHEL or not RHEL */
|
||||
|
||||
|
||||
|
||||
static NF_CALLBACK(ykb_nf_hook, skb) {
|
||||
int ret;
|
||||
@@ -39,7 +309,7 @@ static NF_CALLBACK(ykb_nf_hook, skb) {
|
||||
|
||||
ret = skb_linearize(skb);
|
||||
if (ret < 0) {
|
||||
lgerror("Cannot linearize", ret);
|
||||
lgerror(ret, "Cannot linearize");
|
||||
goto accept;
|
||||
}
|
||||
|
||||
@@ -80,41 +350,51 @@ static int __init ykb_init(void) {
|
||||
ret = open_raw_socket();
|
||||
if (ret < 0) goto err;
|
||||
|
||||
|
||||
if (config.use_ipv6) {
|
||||
ret = open_raw6_socket();
|
||||
if (ret < 0) goto close_rawsocket;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
|
||||
struct net *n;
|
||||
for_each_net(n) {
|
||||
ret = nf_register_net_hook(n, &ykb6_nf_reg);
|
||||
if (ret < 0)
|
||||
lgerror("bad rat",ret);
|
||||
}
|
||||
#else
|
||||
nf_register_hook(&ykb6_nf_reg);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
|
||||
struct net *n;
|
||||
|
||||
for_each_net(n) {
|
||||
ret = nf_register_net_hook(n, &ykb_nf_reg);
|
||||
if (ret < 0)
|
||||
lgerror("bad rat",ret);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "register net_hook");
|
||||
}
|
||||
}
|
||||
#else
|
||||
nf_register_hook(&ykb_nf_reg);
|
||||
ret = nf_register_hook(&ykb_nf_reg);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "register net_hook");
|
||||
}
|
||||
#endif
|
||||
|
||||
pr_info("youtubeUnblock kernel module started.\n");
|
||||
|
||||
if (config.use_ipv6) {
|
||||
ret = open_raw6_socket();
|
||||
if (ret < 0) {
|
||||
config.use_ipv6 = 0;
|
||||
lgwarning("ipv6 disabled!");
|
||||
goto ipv6_fallback;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,13,0)
|
||||
struct net *n;
|
||||
for_each_net(n) {
|
||||
ret = nf_register_net_hook(n, &ykb6_nf_reg);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "register net6_hook");
|
||||
}
|
||||
}
|
||||
#else
|
||||
ret = nf_register_hook(&ykb6_nf_reg);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "register net6_hook");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ipv6_fallback:
|
||||
lginfo("youtubeUnblock kernel module started.\n");
|
||||
return 0;
|
||||
|
||||
close_rawsocket:
|
||||
close_raw_socket();
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
@@ -140,7 +420,7 @@ static void __exit ykb_destroy(void) {
|
||||
#endif
|
||||
|
||||
close_raw_socket();
|
||||
pr_info("youtubeUnblock kernel module destroyed.\n");
|
||||
lginfo("youtubeUnblock kernel module destroyed.\n");
|
||||
}
|
||||
|
||||
module_init(ykb_init);
|
||||
|
||||
46
logging.h
46
logging.h
@@ -9,36 +9,62 @@
|
||||
#include <linux/module.h>
|
||||
#define printf pr_info
|
||||
#define perror pr_err
|
||||
#define lgerror(msg, ret, ...) __extension__ ({ \
|
||||
pr_err(msg ": %d\n", ##__VA_ARGS__, ret); \
|
||||
})
|
||||
|
||||
#define LOG_ERR KERN_ERR
|
||||
#define LOG_INFO KERN_INFO
|
||||
#define LOG_WARN KERN_WARNING
|
||||
|
||||
#define log_message(level, msg, ...) \
|
||||
(printk(level msg, ##__VA_ARGS__))
|
||||
|
||||
#define lgerror(ret, msg, ...) \
|
||||
(log_message(LOG_ERR, msg ": %d\n", ##__VA_ARGS__, ret))
|
||||
|
||||
#else
|
||||
#include <stdio.h> // IWYU pragma: export
|
||||
#include <errno.h>
|
||||
#define lgerror(msg, ret, ...) __extension__ ({ \
|
||||
#include <syslog.h>
|
||||
|
||||
#define log_message(level, msg, ...) \
|
||||
(config.syslog ? (void)(syslog((level), msg, ##__VA_ARGS__)) : (void)(printf(msg, ##__VA_ARGS__)))
|
||||
|
||||
#define lgerror(ret, msg, ...) __extension__ ({ \
|
||||
errno = -(ret); \
|
||||
printf(msg ": %s\n", ##__VA_ARGS__, strerror(errno)); \
|
||||
log_message(LOG_ERR, msg ": %s\n", ##__VA_ARGS__, strerror(errno)); \
|
||||
})
|
||||
#endif /* PROGRAM_SPACE */
|
||||
|
||||
#define lgerr(msg, ...) \
|
||||
(log_message(LOG_ERR, msg, ##__VA_ARGS__))
|
||||
|
||||
#define lgwarning(msg, ...) \
|
||||
(log_message(LOG_WARN, msg, ##__VA_ARGS__))
|
||||
|
||||
|
||||
#define lginfo(msg, ...) \
|
||||
(log_message(LOG_INFO, msg, ##__VA_ARGS__))
|
||||
|
||||
#define print_message(...) \
|
||||
(lginfo(__VA_ARGS__))
|
||||
|
||||
#define lgdebug(msg, ...) \
|
||||
(LOG_LEVEL >= VERBOSE_DEBUG ? printf(msg, ##__VA_ARGS__) : 0)
|
||||
(LOG_LEVEL >= VERBOSE_DEBUG ? log_message(LOG_INFO, msg, ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#define lgdebugmsg(msg, ...) lgdebug(msg "\n", ##__VA_ARGS__)
|
||||
|
||||
|
||||
#define lgtrace(msg, ...) \
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? printf(msg, ##__VA_ARGS__) : 0)
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? log_message(LOG_INFO, msg, ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#define lgtracemsg(msg, ...) lgtrace(msg "\n", __VA_ARGS__)
|
||||
|
||||
#define lgtrace_start(msg, ...) \
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? printf("[TRACE] " msg " ( ", ##__VA_ARGS__) : 0)
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? log_message(LOG_INFO, "[TRACE] " msg " ( ", ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#define lgtrace_addp(msg, ...) \
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? printf(msg", ", ##__VA_ARGS__) : 0)
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? log_message(LOG_INFO, msg", ", ##__VA_ARGS__) : (void)0)
|
||||
|
||||
#define lgtrace_end() \
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? printf(") \n") : 0)
|
||||
(LOG_LEVEL >= VERBOSE_TRACE ? log_message(LOG_INFO, ") \n") : (void)0)
|
||||
|
||||
#endif /* LOGGING_H */
|
||||
|
||||
48
mangle.h
48
mangle.h
@@ -2,38 +2,12 @@
|
||||
#define YU_MANGLE_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* Result of analyze_tls_data function
|
||||
*/
|
||||
struct tls_verdict {
|
||||
int target_sni; /* google video hello packet */
|
||||
int sni_offset; /* offset from start of tcp _payload_ */
|
||||
int sni_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
|
||||
/**
|
||||
* Generates fake client hello message
|
||||
*/
|
||||
int gen_fake_sni(const void *iph, uint32_t iph_len,
|
||||
const struct tcphdr *tcph, uint32_t tcph_len,
|
||||
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 fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen);
|
||||
#include "tls.h"
|
||||
|
||||
#define PKT_ACCEPT 0
|
||||
#define PKT_DROP 1
|
||||
// Used for section config
|
||||
#define PKT_CONTINUE 2
|
||||
|
||||
/**
|
||||
* Processes the packet and returns verdict.
|
||||
@@ -46,28 +20,30 @@ int process_packet(const uint8_t *packet, uint32_t packet_len);
|
||||
* Processe the TCP packet.
|
||||
* Returns verdict.
|
||||
*/
|
||||
int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len);
|
||||
int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, uint32_t raw_payload_len);
|
||||
|
||||
|
||||
/**
|
||||
* Processes the UDP packet.
|
||||
* Returns verdict.
|
||||
*/
|
||||
int process_udp_packet(const uint8_t *pkt, uint32_t pktlen);
|
||||
int process_udp_packet(const struct section_config_t *section, const uint8_t *pkt, uint32_t pktlen);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sends fake client hello.
|
||||
*/
|
||||
int post_fake_sni(const void *iph, unsigned int iph_len,
|
||||
const struct tcphdr *tcph, unsigned int tcph_len,
|
||||
unsigned char sequence_len);
|
||||
int post_fake_sni(struct fake_type f_type,
|
||||
const void *iph, unsigned int iph_len,
|
||||
const struct tcphdr *tcph, unsigned int tcph_len);
|
||||
|
||||
/**
|
||||
* Splits packet by poses and posts.
|
||||
* Poses are relative to start of TCP payload.
|
||||
* dvs used internally and should be zero.
|
||||
*/
|
||||
int send_tcp_frags(
|
||||
int send_tcp_frags(const struct section_config_t *section,
|
||||
const uint8_t *packet, uint32_t pktlen,
|
||||
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||
|
||||
@@ -76,7 +52,7 @@ int send_tcp_frags(
|
||||
* Poses are relative to start of TCP payload.
|
||||
* dvs used internally and should be zero.
|
||||
*/
|
||||
int send_ip4_frags(
|
||||
int send_ip4_frags(const struct section_config_t *section,
|
||||
const uint8_t *packet, uint32_t pktlen,
|
||||
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||
#endif /* YU_MANGLE_H */
|
||||
|
||||
84
nf_wrapper.h
84
nf_wrapper.h
@@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Thanks https://github.com/NICMx/Jool/blob/5f60dcda5944b01cc43c3be342aad26af8161bcb/include/nat64/mod/common/nf_wrapper.h for mapped kernel versions
|
||||
*/
|
||||
#ifndef _JOOL_MOD_NF_WRAPPER_H
|
||||
#define _JOOL_MOD_NF_WRAPPER_H
|
||||
|
||||
/**
|
||||
* @file
|
||||
* The kernel API is far from static. In particular, the Netfilter packet entry
|
||||
* function keeps changing. nf_hook.c, the file where we declare our packet
|
||||
* entry function, has been quite difficult to read for a while now. It's pretty
|
||||
* amusing, because we don't even use any of the noisy arguments.
|
||||
*
|
||||
* This file declares a usable function header that abstracts away all those
|
||||
* useless arguments.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
/* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */
|
||||
#ifdef RHEL_RELEASE_CODE
|
||||
|
||||
#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 2)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
const struct nf_hook_state *state) \
|
||||
|
||||
#elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#else
|
||||
|
||||
#error "Sorry; this version of RHEL is not supported because it's kind of old."
|
||||
|
||||
#endif /* RHEL_RELEASE_CODE >= x */
|
||||
|
||||
|
||||
/* If this NOT a RedHat-based kernel (Ubuntu, Debian, SuSE, etc)... */
|
||||
#else
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
void *priv, \
|
||||
struct sk_buff *skb, \
|
||||
const struct nf_hook_state *state)
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct nf_hook_state *state)
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
unsigned int hooknum, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#else
|
||||
#error "Linux < 3.0 isn't supported at all."
|
||||
|
||||
#endif /* LINUX_VERSION_CODE > n */
|
||||
|
||||
#endif /* RHEL or not RHEL */
|
||||
|
||||
#endif /* _JOOL_MOD_NF_WRAPPER_H */
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/usr/sbin/nft -f
|
||||
# This file install nftables rules for openwrt
|
||||
|
||||
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
|
||||
@@ -1,22 +0,0 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
USE_PROCD=1
|
||||
|
||||
START=50
|
||||
STOP=50
|
||||
|
||||
|
||||
# Openwrt procd script: https://openwrt.org/docs/guide-developer/procd-init-script-example
|
||||
# The program should be put into /usr/bin/
|
||||
# This file should be put into /etc/init.d/
|
||||
|
||||
start_service() {
|
||||
procd_open_instance
|
||||
procd_set_param command /usr/bin/youtubeUnblock
|
||||
|
||||
procd_set_param nice -20
|
||||
|
||||
procd_set_param stdout 1
|
||||
procd_set_param stderr 1
|
||||
|
||||
procd_close_instance
|
||||
}
|
||||
2
quic.c
2
quic.c
@@ -135,6 +135,6 @@ int quic_parse_initial_message(uint8_t *inpayload, uint32_t inplen,
|
||||
return 0;
|
||||
|
||||
invalid_packet:
|
||||
lgerror("QUIC invalid Initial packet", -EINVAL);
|
||||
lgerror(-EINVAL, "QUIC invalid Initial packet");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
354
tls.c
Normal file
354
tls.c
Normal file
@@ -0,0 +1,354 @@
|
||||
#include "types.h"
|
||||
#include "tls.h"
|
||||
#include "config.h"
|
||||
#include "logging.h"
|
||||
#include "utils.h"
|
||||
|
||||
#ifndef KERNEL_SPACE
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#define TLS_CONTENT_TYPE_HANDSHAKE 0x16
|
||||
#define TLS_HANDSHAKE_TYPE_CLIENT_HELLO 0x01
|
||||
#define TLS_EXTENSION_SNI 0x0000
|
||||
#define TLS_EXTENSION_CLIENT_HELLO_ENCRYPTED 0xfe0d
|
||||
|
||||
/**
|
||||
* Processes tls payload of the tcp request.
|
||||
*
|
||||
* data Payload data of TCP.
|
||||
* dlen Length of `data`.
|
||||
*/
|
||||
struct tls_verdict analyze_tls_data(
|
||||
const struct section_config_t *section,
|
||||
const uint8_t *data,
|
||||
uint32_t dlen)
|
||||
{
|
||||
struct tls_verdict vrd = {0};
|
||||
|
||||
size_t i = 0;
|
||||
const uint8_t *data_end = data + dlen;
|
||||
|
||||
while (i + 4 < dlen) {
|
||||
const uint8_t *msgData = data + i;
|
||||
|
||||
uint8_t tls_content_type = *msgData;
|
||||
uint8_t tls_vmajor = *(msgData + 1);
|
||||
uint16_t message_length = ntohs(*(uint16_t *)(msgData + 3));
|
||||
|
||||
if (tls_vmajor != 0x03) goto nextMessage;
|
||||
|
||||
if (i + 5 > dlen) break;
|
||||
|
||||
if (tls_content_type != TLS_CONTENT_TYPE_HANDSHAKE)
|
||||
goto nextMessage;
|
||||
|
||||
if (section->sni_detection == SNI_DETECTION_BRUTE) {
|
||||
goto brute;
|
||||
}
|
||||
|
||||
const uint8_t *handshakeProto = msgData + 5;
|
||||
|
||||
if (handshakeProto + 1 >= data_end) break;
|
||||
|
||||
uint8_t handshakeType = *handshakeProto;
|
||||
|
||||
if (handshakeType != TLS_HANDSHAKE_TYPE_CLIENT_HELLO)
|
||||
goto nextMessage;
|
||||
|
||||
const uint8_t *msgPtr = handshakeProto;
|
||||
msgPtr += 1;
|
||||
msgPtr += 3 + 2 + 32;
|
||||
|
||||
if (msgPtr + 1 >= data_end) break;
|
||||
uint8_t sessionIdLength = *msgPtr;
|
||||
msgPtr++;
|
||||
msgPtr += sessionIdLength;
|
||||
|
||||
if (msgPtr + 2 >= data_end) break;
|
||||
uint16_t ciphersLength = ntohs(*(uint16_t *)msgPtr);
|
||||
msgPtr += 2;
|
||||
msgPtr += ciphersLength;
|
||||
|
||||
if (msgPtr + 1 >= data_end) break;
|
||||
uint8_t compMethodsLen = *msgPtr;
|
||||
msgPtr++;
|
||||
msgPtr += compMethodsLen;
|
||||
|
||||
if (msgPtr + 2 >= data_end) break;
|
||||
uint16_t extensionsLen = ntohs(*(uint16_t *)msgPtr);
|
||||
msgPtr += 2;
|
||||
|
||||
const uint8_t *extensionsPtr = msgPtr;
|
||||
const uint8_t *extensions_end = extensionsPtr + extensionsLen;
|
||||
if (extensions_end > data_end) extensions_end = data_end;
|
||||
|
||||
while (extensionsPtr < extensions_end) {
|
||||
const uint8_t *extensionPtr = extensionsPtr;
|
||||
if (extensionPtr + 4 >= extensions_end) break;
|
||||
|
||||
uint16_t extensionType =
|
||||
ntohs(*(uint16_t *)extensionPtr);
|
||||
extensionPtr += 2;
|
||||
|
||||
uint16_t extensionLen =
|
||||
ntohs(*(uint16_t *)extensionPtr);
|
||||
extensionPtr += 2;
|
||||
|
||||
|
||||
if (extensionPtr + extensionLen > extensions_end)
|
||||
break;
|
||||
|
||||
if (extensionType != TLS_EXTENSION_SNI)
|
||||
goto nextExtension;
|
||||
|
||||
const uint8_t *sni_ext_ptr = extensionPtr;
|
||||
|
||||
if (sni_ext_ptr + 2 >= extensions_end) break;
|
||||
uint16_t sni_ext_dlen = ntohs(*(uint16_t *)sni_ext_ptr);
|
||||
|
||||
sni_ext_ptr += 2;
|
||||
|
||||
const uint8_t *sni_ext_end = sni_ext_ptr + sni_ext_dlen;
|
||||
if (sni_ext_end >= extensions_end) break;
|
||||
|
||||
if (sni_ext_ptr + 3 >= sni_ext_end) break;
|
||||
sni_ext_ptr++;
|
||||
uint16_t sni_len = ntohs(*(uint16_t *)sni_ext_ptr);
|
||||
sni_ext_ptr += 2;
|
||||
|
||||
if (sni_ext_ptr + sni_len > sni_ext_end) break;
|
||||
|
||||
char *sni_name = (char *)sni_ext_ptr;
|
||||
|
||||
vrd.sni_offset = (uint8_t *)sni_name - data;
|
||||
vrd.sni_target_offset = vrd.sni_offset;
|
||||
vrd.sni_len = sni_len;
|
||||
vrd.sni_target_len = vrd.sni_len;
|
||||
|
||||
if (section->all_domains) {
|
||||
vrd.target_sni = 1;
|
||||
goto check_domain;
|
||||
}
|
||||
|
||||
unsigned int j = 0;
|
||||
for (unsigned int i = 0; i <= section->domains_strlen; i++) {
|
||||
if ( i > j &&
|
||||
(i == section->domains_strlen ||
|
||||
section->domains_str[i] == '\0' ||
|
||||
section->domains_str[i] == ',' ||
|
||||
section->domains_str[i] == '\n' )) {
|
||||
|
||||
unsigned int domain_len = (i - j);
|
||||
const char *sni_startp = sni_name + sni_len - domain_len;
|
||||
const char *domain_startp = section->domains_str + j;
|
||||
|
||||
if (sni_len >= domain_len &&
|
||||
sni_len < 128 &&
|
||||
!strncmp(sni_startp,
|
||||
domain_startp,
|
||||
domain_len)) {
|
||||
vrd.target_sni = 1;
|
||||
vrd.sni_target_offset = (const uint8_t *)sni_startp - data;
|
||||
vrd.sni_target_len = domain_len;
|
||||
goto check_domain;
|
||||
}
|
||||
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
check_domain:
|
||||
if (vrd.target_sni == 1 && section->exclude_domains_strlen != 0) {
|
||||
unsigned int j = 0;
|
||||
for (unsigned int i = 0; i <= section->exclude_domains_strlen; i++) {
|
||||
if ( i > j &&
|
||||
(i == section->exclude_domains_strlen ||
|
||||
section->exclude_domains_str[i] == '\0' ||
|
||||
section->exclude_domains_str[i] == ',' ||
|
||||
section->exclude_domains_str[i] == '\n' )) {
|
||||
|
||||
unsigned int domain_len = (i - j);
|
||||
const char *sni_startp = sni_name + sni_len - domain_len;
|
||||
const char *domain_startp = section->exclude_domains_str + j;
|
||||
|
||||
if (sni_len >= domain_len &&
|
||||
sni_len < 128 &&
|
||||
!strncmp(sni_startp,
|
||||
domain_startp,
|
||||
domain_len)) {
|
||||
|
||||
vrd.target_sni = 0;
|
||||
lgdebugmsg("Excluded SNI: %.*s",
|
||||
vrd.sni_len, data + vrd.sni_offset);
|
||||
goto out;
|
||||
}
|
||||
|
||||
j = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goto out;
|
||||
|
||||
nextExtension:
|
||||
extensionsPtr += 2 + 2 + extensionLen;
|
||||
}
|
||||
nextMessage:
|
||||
i += 5 + message_length;
|
||||
}
|
||||
|
||||
out:
|
||||
return vrd;
|
||||
|
||||
|
||||
brute:
|
||||
if (section->all_domains) {
|
||||
vrd.target_sni = 1;
|
||||
vrd.sni_len = 0;
|
||||
vrd.sni_offset = dlen / 2;
|
||||
goto out;
|
||||
}
|
||||
|
||||
unsigned int j = 0;
|
||||
for (unsigned int i = 0; i <= section->domains_strlen; i++) {
|
||||
if ( i > j &&
|
||||
(i == section->domains_strlen ||
|
||||
section->domains_str[i] == '\0' ||
|
||||
section->domains_str[i] == ',' ||
|
||||
section->domains_str[i] == '\n' )) {
|
||||
|
||||
unsigned int domain_len = (i - j);
|
||||
const char *domain_startp = section->domains_str + j;
|
||||
|
||||
if (domain_len + dlen + 1> MAX_PACKET_SIZE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NETBUF_ALLOC(buf, MAX_PACKET_SIZE);
|
||||
if (!NETBUF_CHECK(buf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
goto out;
|
||||
}
|
||||
NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int));
|
||||
if (!NETBUF_CHECK(nzbuf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
NETBUF_FREE(buf);
|
||||
goto out;
|
||||
}
|
||||
|
||||
int *zbuf = (void *)nzbuf;
|
||||
|
||||
memcpy(buf, domain_startp, domain_len);
|
||||
memcpy(buf + domain_len, "#", 1);
|
||||
memcpy(buf + domain_len + 1, data, dlen);
|
||||
|
||||
z_function((char *)buf, zbuf, domain_len + 1 + dlen);
|
||||
|
||||
for (unsigned int k = 0; k < dlen; k++) {
|
||||
if (zbuf[k] == domain_len) {
|
||||
vrd.target_sni = 1;
|
||||
vrd.sni_len = domain_len;
|
||||
vrd.sni_offset = (k - domain_len - 1);
|
||||
vrd.sni_target_offset = vrd.sni_offset;
|
||||
vrd.sni_target_len = vrd.sni_len;
|
||||
NETBUF_FREE(buf);
|
||||
NETBUF_FREE(nzbuf);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
j = i + 1;
|
||||
|
||||
NETBUF_FREE(buf);
|
||||
NETBUF_FREE(nzbuf);
|
||||
}
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
int gen_fake_sni(struct fake_type type,
|
||||
const void *ipxh, uint32_t iph_len,
|
||||
const struct tcphdr *tcph, uint32_t tcph_len,
|
||||
uint8_t *buf, uint32_t *buflen) {
|
||||
uint32_t data_len = type.fake_len;
|
||||
int ret;
|
||||
|
||||
if (type.type == FAKE_PAYLOAD_RANDOM && data_len == 0) {
|
||||
data_len = (uint32_t)randint() % 1200;
|
||||
}
|
||||
|
||||
if (!ipxh || !tcph || !buf || !buflen)
|
||||
return -EINVAL;
|
||||
|
||||
int ipxv = netproto_version(ipxh, iph_len);
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
const struct iphdr *iph = ipxh;
|
||||
|
||||
memcpy(buf, iph, iph_len);
|
||||
struct iphdr *niph = (struct iphdr *)buf;
|
||||
|
||||
niph->protocol = IPPROTO_TCP;
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
const struct ip6_hdr *iph = ipxh;
|
||||
|
||||
iph_len = sizeof(struct ip6_hdr);
|
||||
memcpy(buf, iph, iph_len);
|
||||
struct ip6_hdr *niph = (struct ip6_hdr *)buf;
|
||||
|
||||
niph->ip6_nxt = IPPROTO_TCP;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t dlen = iph_len + tcph_len + data_len;
|
||||
|
||||
if (*buflen < dlen)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(buf + iph_len, tcph, tcph_len);
|
||||
uint8_t *bfdptr = buf + iph_len + tcph_len;
|
||||
|
||||
switch (type.type) {
|
||||
case FAKE_PAYLOAD_DATA:
|
||||
memcpy(bfdptr, type.fake_data, data_len);
|
||||
break;
|
||||
default: // FAKE_PAYLOAD_RANDOM
|
||||
#ifdef KERNEL_SPACE
|
||||
get_random_bytes(bfdptr, data_len);
|
||||
#else /* KERNEL_SPACE */
|
||||
#if _NO_GETRANDOM
|
||||
ret = open("/dev/urandom", O_RDONLY);
|
||||
if (ret < 0) {
|
||||
lgerror(ret, "Unable to open /dev/urandom");
|
||||
return ret;
|
||||
}
|
||||
|
||||
read(ret, bfdptr, data_len);
|
||||
close(ret);
|
||||
|
||||
#else /* _NO_GETRANDOM */
|
||||
getrandom(bfdptr, data_len, 0);
|
||||
#endif /* _NO_GETRANDOM */
|
||||
#endif /* KERNEL_SPACE */
|
||||
}
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
struct iphdr *niph = (struct iphdr *)buf;
|
||||
niph->tot_len = htons(dlen);
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
struct ip6_hdr *niph = (struct ip6_hdr *)buf;
|
||||
niph->ip6_plen = htons(dlen - iph_len);
|
||||
}
|
||||
|
||||
fail_packet(type.strategy, buf, &dlen, *buflen);
|
||||
|
||||
*buflen = dlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
34
tls.h
Normal file
34
tls.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef TLS_H
|
||||
#define TLS_H
|
||||
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
/**
|
||||
* Result of analyze_tls_data function
|
||||
*/
|
||||
struct tls_verdict {
|
||||
int target_sni; /* google video hello packet */
|
||||
int sni_offset; /* offset from start of tcp _payload_ */
|
||||
int sni_target_offset; /* offset of target domain instead of entire sni */
|
||||
int sni_target_len; /* offset of target domain instead of entire sni */
|
||||
int sni_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 struct section_config_t *section, const uint8_t *data, uint32_t dlen);
|
||||
|
||||
|
||||
/**
|
||||
* Generates the fake client hello message
|
||||
*/
|
||||
int gen_fake_sni(struct fake_type type,
|
||||
const void *iph, uint32_t iph_len,
|
||||
const struct tcphdr *tcph, uint32_t tcph_len,
|
||||
uint8_t *buf, uint32_t *buflen);
|
||||
|
||||
#endif /* TLS_H */
|
||||
23
types.h
23
types.h
@@ -13,6 +13,14 @@
|
||||
#include <errno.h> // IWYU pragma: export
|
||||
#include <stdint.h> // IWYU pragma: export
|
||||
#include <string.h> // IWYU pragma: export
|
||||
#include <stdlib.h> // IWYU pragma: export
|
||||
|
||||
|
||||
#define _NO_GETRANDOM ((__GLIBC__ <= 2 && __GLIBC_MINOR__ < 25))
|
||||
|
||||
#if !_NO_GETRANDOM
|
||||
#include <sys/random.h> // IWYU pragma: export
|
||||
#endif
|
||||
|
||||
#endif /* SPACES */
|
||||
|
||||
@@ -77,6 +85,9 @@
|
||||
|
||||
#endif /* not a KERNEL_SPACE */
|
||||
|
||||
/* An alternative memory allocation strategy for userspace app */
|
||||
// #define ALLOC_MALLOC
|
||||
|
||||
/**
|
||||
* Use NETBUF_ALLOC and NETBUF_FREE as an abstraction of memory allocation.
|
||||
* Do not use it within expressions, consider these defines as separate statements.
|
||||
@@ -99,4 +110,16 @@
|
||||
#define NETBUF_FREE(buf) ;
|
||||
#endif
|
||||
|
||||
static inline int randint(void) {
|
||||
int rnd;
|
||||
|
||||
#ifdef KERNEL_SPACE
|
||||
get_random_bytes(&rnd, sizeof(rnd));
|
||||
#else
|
||||
rnd = random();
|
||||
#endif
|
||||
|
||||
return rnd;
|
||||
}
|
||||
|
||||
#endif /* TYPES_H */
|
||||
|
||||
@@ -10,13 +10,13 @@ CCLD:=$(CC)
|
||||
LD:=ld
|
||||
|
||||
ifeq ($(USE_SYS_LIBS), no)
|
||||
override CFLAGS += -Wall -Wpedantic -Wno-unused-variable -I$(DEPSDIR)/include -std=gnu11
|
||||
override CFLAGS += -I$(DEPSDIR)/include
|
||||
override LDFLAGS += -L$(DEPSDIR)/lib
|
||||
REQ = $(LIBNETFILTER_QUEUE) $(LIBMNL) $(LIBCRYPTO)
|
||||
else
|
||||
override CFLAGS += -Wall -Wpedantic -Wno-unused-variable -std=gnu11
|
||||
endif
|
||||
|
||||
override CFLAGS += -DPKG_VERSION=\"$(PKG_FULLVERSION)\" -Wall -Wpedantic -Wno-unused-variable -std=gnu99
|
||||
|
||||
LIBNFNETLINK_CFLAGS := -I$(DEPSDIR)/include
|
||||
LIBNFNETLINK_LIBS := -L$(DEPSDIR)/lib
|
||||
LIBMNL_CFLAGS := -I$(DEPSDIR)/include
|
||||
@@ -31,7 +31,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF
|
||||
|
||||
APP:=$(BUILD_DIR)/youtubeUnblock
|
||||
|
||||
SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c
|
||||
SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c
|
||||
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
|
||||
|
||||
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la
|
||||
|
||||
189
utils.c
189
utils.c
@@ -81,20 +81,20 @@ 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);
|
||||
lgerror(-EINVAL, "ip4_payload_split: pkt|buflen");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct iphdr *hdr = (struct iphdr *)pkt;
|
||||
if (netproto_version(pkt, buflen) != IP4VERSION) {
|
||||
lgerror("ip4_payload_split: ipversion", -EINVAL);
|
||||
lgerror(-EINVAL, "ip4_payload_split: ipversion");
|
||||
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);
|
||||
lgerror(-EINVAL, "ip4_payload_split: buflen cmp pktlen");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -156,20 +156,20 @@ int ip6_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
struct ip6_hdr **iph, uint32_t *iph_len,
|
||||
uint8_t **payload, uint32_t *plen) {
|
||||
if (pkt == NULL || buflen < sizeof(struct ip6_hdr)) {
|
||||
lgerror("ip6_payload_split: pkt|buflen", -EINVAL);
|
||||
lgerror(-EINVAL, "ip6_payload_split: pkt|buflen");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
struct ip6_hdr *hdr = (struct ip6_hdr *)pkt;
|
||||
if (netproto_version(pkt, buflen) != 6) {
|
||||
lgerror("ip6_payload_split: ip6version", -EINVAL);
|
||||
lgerror(-EINVAL, "ip6_payload_split: ip6version");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
uint32_t hdr_len = sizeof(struct ip6_hdr);
|
||||
uint32_t pktlen = ntohs(hdr->ip6_plen);
|
||||
if (buflen < pktlen) {
|
||||
lgerror("ip6_payload_split: buflen cmp pktlen: %d %d", -EINVAL, buflen, pktlen);
|
||||
lgerror(-EINVAL, "ip6_payload_split: buflen cmp pktlen: %d %d", buflen, pktlen);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ int tcp_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
} else if (netvers == IP6VERSION) {
|
||||
return tcp6_payload_split(pkt, buflen, (struct ip6_hdr **)iph, iph_len, tcph, tcph_len, payload, plen);
|
||||
} else {
|
||||
lgerror("Internet Protocol version is unsupported", -EINVAL);
|
||||
lgerror(-EINVAL, "Internet Protocol version is unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@@ -329,7 +329,7 @@ int udp_payload_split(uint8_t *pkt, uint32_t buflen,
|
||||
} else if (netvers == IP6VERSION) {
|
||||
return udp6_payload_split(pkt, buflen, (struct ip6_hdr **)iph, iph_len, udph, payload, plen);
|
||||
} else {
|
||||
lgerror("Internet Protocol version is unsupported", -EINVAL);
|
||||
lgerror(-EINVAL, "Internet Protocol version is unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@@ -351,7 +351,7 @@ int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||
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);
|
||||
lgerror(ret, "ipv4_frag: TCP Header extract error");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -360,7 +360,7 @@ int ip4_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||
}
|
||||
|
||||
if (payload_offset & ((1 << 3) - 1)) {
|
||||
lgerror("ipv4_frag: Payload offset MUST be a multiply of 8!", -EINVAL);
|
||||
lgerror(-EINVAL, "ipv4_frag: Payload offset MUST be a multiply of 8!");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -418,7 +418,6 @@ int tcp_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 ip6_hdr *hdr6;
|
||||
void *hdr;
|
||||
uint32_t hdr_len;
|
||||
struct tcphdr *tcph;
|
||||
@@ -434,7 +433,7 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||
&hdr, &hdr_len,
|
||||
&tcph, &tcph_len,
|
||||
(uint8_t **)&payload, &plen)) < 0) {
|
||||
lgerror("tcp_frag: tcp_payload_split", ret);
|
||||
lgerror(ret, "tcp_frag: tcp_payload_split");
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -449,7 +448,7 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||
ntohs(iphdr->frag_off) & IP_OFFMASK) {
|
||||
lgdebugmsg("tcp_frag: ip4: frag value: %d",
|
||||
ntohs(iphdr->frag_off));
|
||||
lgerror("tcp_frag: ip4: ip fragmentation is set", -EINVAL);
|
||||
lgerror(-EINVAL, "tcp_frag: ip4: ip fragmentation is set");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
@@ -485,6 +484,11 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
||||
struct iphdr *s2_hdr = (void *)seg2;
|
||||
s1_hdr->tot_len = htons(s1_dlen);
|
||||
s2_hdr->tot_len = htons(s2_dlen);
|
||||
s1_hdr->id = randint();
|
||||
s2_hdr->id = randint();
|
||||
|
||||
set_ip_checksum(s1_hdr, sizeof(struct iphdr));
|
||||
set_ip_checksum(s2_hdr, sizeof(struct iphdr));
|
||||
} else {
|
||||
struct ip6_hdr *s1_hdr = (void *)seg1;
|
||||
struct ip6_hdr *s2_hdr = (void *)seg2;
|
||||
@@ -523,3 +527,162 @@ void z_function(const char *str, int *zbuf, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
void shift_data(uint8_t *data, uint32_t dlen, uint32_t delta) {
|
||||
uint8_t *ndptr = data + delta + dlen;
|
||||
uint8_t *dptr = data + dlen;
|
||||
uint8_t *ndlptr = data;
|
||||
for (size_t i = dlen + 1; i > 0; i--) {
|
||||
*ndptr = *dptr;
|
||||
--ndptr, --dptr;
|
||||
}
|
||||
for (size_t i = 0; i < delta; i++) {
|
||||
*ndlptr++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define TCP_MD5SIG_LEN 16
|
||||
#define TCP_MD5SIG_KIND 19
|
||||
struct tcp_md5sig_opt {
|
||||
uint8_t kind;
|
||||
uint8_t len;
|
||||
uint8_t sig[TCP_MD5SIG_LEN];
|
||||
};
|
||||
#define TCP_MD5SIG_OPT_LEN (sizeof(struct tcp_md5sig_opt))
|
||||
// Real length of the option, with NOOP fillers
|
||||
#define TCP_MD5SIG_OPT_RLEN 20
|
||||
|
||||
int fail_packet(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) {
|
||||
void *iph;
|
||||
uint32_t iph_len;
|
||||
struct tcphdr *tcph;
|
||||
uint32_t tcph_len;
|
||||
uint8_t *data;
|
||||
uint32_t dlen;
|
||||
int ret;
|
||||
|
||||
ret = tcp_payload_split(payload, *plen,
|
||||
&iph, &iph_len, &tcph, &tcph_len,
|
||||
&data, &dlen);
|
||||
|
||||
uint32_t ipxv = netproto_version(payload, *plen);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
if (strategy.strategy == FAKE_STRAT_RAND_SEQ) {
|
||||
lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
|
||||
|
||||
tcph->seq = htonl(ntohl(tcph->seq) - (strategy.randseq_offset + dlen));
|
||||
|
||||
lgtrace_addp("%u", ntohl(tcph->seq));
|
||||
} else if (strategy.strategy == FAKE_STRAT_PAST_SEQ) {
|
||||
lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
|
||||
tcph->seq = htonl(ntohl(tcph->seq) - dlen);
|
||||
lgtrace_addp("%u", ntohl(tcph->seq));
|
||||
|
||||
} else if (strategy.strategy == FAKE_STRAT_TTL) {
|
||||
lgtrace_addp("set fake ttl to %d", strategy.faking_ttl);
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
((struct iphdr *)iph)->ttl = strategy.faking_ttl;
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
((struct ip6_hdr *)iph)->ip6_hops = strategy.faking_ttl;
|
||||
} else {
|
||||
lgerror(-EINVAL, "fail_packet: IP version is unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
} else if (strategy.strategy == FAKE_STRAT_TCP_MD5SUM) {
|
||||
int optp_len = tcph_len - sizeof(struct tcphdr);
|
||||
int delta = TCP_MD5SIG_OPT_RLEN - optp_len;
|
||||
lgtrace_addp("Incr delta %d: %d -> %d", delta, optp_len, optp_len + delta);
|
||||
|
||||
if (delta > 0) {
|
||||
if (avail_buflen - *plen < delta) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
shift_data(data, dlen, delta);
|
||||
data += delta;
|
||||
tcph_len = tcph_len + delta;
|
||||
tcph->doff = tcph_len >> 2;
|
||||
if (ipxv == IP4VERSION) {
|
||||
((struct iphdr *)iph)->tot_len = htons(ntohs(((struct iphdr *)iph)->tot_len) + delta);
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
((struct ip6_hdr *)iph)->ip6_plen = htons(ntohs(((struct ip6_hdr *)iph)->ip6_plen) + delta);
|
||||
} else {
|
||||
lgerror(-EINVAL, "fail_packet: IP version is unsupported");
|
||||
return -EINVAL;
|
||||
}
|
||||
optp_len += delta;
|
||||
*plen += delta;
|
||||
}
|
||||
|
||||
uint8_t *optplace = (uint8_t *)tcph + sizeof(struct tcphdr);
|
||||
struct tcp_md5sig_opt *mdopt = (void *)optplace;
|
||||
mdopt->kind = TCP_MD5SIG_KIND;
|
||||
mdopt->len = TCP_MD5SIG_OPT_LEN;
|
||||
|
||||
optplace += sizeof(struct tcp_md5sig_opt);
|
||||
optp_len -= sizeof(struct tcp_md5sig_opt);
|
||||
|
||||
while (optp_len-- > 0) {
|
||||
*optplace++ = 0x01;
|
||||
}
|
||||
}
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
((struct iphdr *)iph)->frag_off = 0;
|
||||
}
|
||||
|
||||
|
||||
set_ip_checksum(iph, iph_len);
|
||||
set_tcp_checksum(tcph, iph, iph_len);
|
||||
|
||||
if (strategy.strategy == FAKE_STRAT_TCP_CHECK) {
|
||||
lgtrace_addp("break fake tcp checksum");
|
||||
tcph->check += 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int seqovl_packet(uint8_t *payload, uint32_t *plen, uint32_t seq_delta) {
|
||||
int ipxv = netproto_version(payload, *plen);
|
||||
|
||||
void *iph;
|
||||
uint32_t iph_len;
|
||||
struct tcphdr *tcph;
|
||||
uint32_t tcph_len;
|
||||
uint8_t *data;
|
||||
uint32_t dlen;
|
||||
|
||||
|
||||
int ret = tcp_payload_split(payload, *plen,
|
||||
&iph, &iph_len, &tcph, &tcph_len,
|
||||
&data, &dlen);
|
||||
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
struct iphdr *ip4h = iph;
|
||||
ip4h->tot_len = htons(ntohs(ip4h->tot_len) + seq_delta);
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
struct ip6_hdr *ip6h = iph;
|
||||
ip6h->ip6_plen = htons(ntohs(ip6h->ip6_plen) + seq_delta);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
tcph->seq = htons(ntohs(tcph->seq) - seq_delta);
|
||||
shift_data(data, dlen, seq_delta);
|
||||
*plen += seq_delta;
|
||||
|
||||
set_ip_checksum(iph, iph_len);
|
||||
set_tcp_checksum(tcph, iph, iph_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
89
utils.h
89
utils.h
@@ -2,6 +2,7 @@
|
||||
#define UTILS_H
|
||||
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
#define IP4VERSION 4
|
||||
#define IP6VERSION 6
|
||||
@@ -101,4 +102,92 @@ int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len);
|
||||
|
||||
void z_function(const char *str, int *zbuf, size_t len);
|
||||
|
||||
/**
|
||||
* Shifts data left delta bytes. Fills delta buffer with zeroes.
|
||||
*/
|
||||
void shift_data(uint8_t *data, uint32_t dlen, uint32_t delta);
|
||||
|
||||
|
||||
struct failing_strategy {
|
||||
unsigned int strategy;
|
||||
uint8_t faking_ttl;
|
||||
uint32_t randseq_offset;
|
||||
};
|
||||
|
||||
|
||||
struct fake_type {
|
||||
|
||||
#define FAKE_PAYLOAD_RANDOM 0
|
||||
#define FAKE_PAYLOAD_DATA 1
|
||||
// In default mode all other options will be skipped.
|
||||
#define FAKE_PAYLOAD_DEFAULT 2
|
||||
int type;
|
||||
|
||||
// Length of the final fake message.
|
||||
// Pass 0 in RANDOM mode to make it random
|
||||
uint16_t fake_len;
|
||||
|
||||
// Payload of the fake message of fake_len length.
|
||||
// Will be omitted in RANDOM mode.
|
||||
const char *fake_data;
|
||||
|
||||
unsigned int sequence_len;
|
||||
|
||||
// If non-0 the packet send will be delayed for n milliseconds
|
||||
unsigned int seg2delay;
|
||||
|
||||
// faking strategy of the fake packet.
|
||||
// Does not support bitmask, pass standalone strategy.
|
||||
// Pass 0 if you don't want any faking procedures.
|
||||
struct failing_strategy strategy;
|
||||
};
|
||||
|
||||
/**
|
||||
* Invalidates the raw packet. The function aims to invalid the packet
|
||||
* in such way as it will be accepted by DPI, but dropped by target server
|
||||
*
|
||||
* Does not support bitmask, pass standalone strategy.
|
||||
*/
|
||||
int fail_packet(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen);
|
||||
|
||||
/**
|
||||
* Shifts the payload right and pushes zeroes before it. Useful for TCP TLS faking.
|
||||
*/
|
||||
int seqovl_packet(uint8_t *payload, uint32_t *plen, uint32_t seq_delta);
|
||||
|
||||
|
||||
|
||||
static inline struct failing_strategy args_default_failing_strategy(const struct section_config_t *section) {
|
||||
struct failing_strategy fl_strat = {
|
||||
.strategy = (unsigned int)section->faking_strategy,
|
||||
.faking_ttl = section->faking_ttl,
|
||||
.randseq_offset = (uint32_t)section->fakeseq_offset
|
||||
};
|
||||
return fl_strat;
|
||||
}
|
||||
|
||||
static inline struct fake_type args_default_fake_type(const struct section_config_t *section) {
|
||||
struct fake_type f_type = {
|
||||
.sequence_len = section->fake_sni_seq_len,
|
||||
.strategy = args_default_failing_strategy(section),
|
||||
};
|
||||
|
||||
switch (section->fake_sni_type) {
|
||||
case FAKE_PAYLOAD_RANDOM:
|
||||
f_type.type = FAKE_PAYLOAD_RANDOM;
|
||||
break;
|
||||
case FAKE_PAYLOAD_CUSTOM:
|
||||
f_type.type = FAKE_PAYLOAD_CUSTOM;
|
||||
f_type.fake_data = section->fake_custom_pkt;
|
||||
f_type.fake_len = section->fake_custom_pkt_sz;
|
||||
break;
|
||||
default:
|
||||
f_type.type = FAKE_PAYLOAD_CUSTOM;
|
||||
f_type.fake_data = section->fake_sni_pkt;
|
||||
f_type.fake_len = section->fake_sni_pkt_sz;
|
||||
}
|
||||
|
||||
return f_type;
|
||||
}
|
||||
|
||||
#endif /* UTILS_H */
|
||||
|
||||
198
youtubeUnblock.c
198
youtubeUnblock.c
@@ -45,12 +45,12 @@ static int open_socket(struct mnl_socket **_nl) {
|
||||
nl = mnl_socket_open(NETLINK_NETFILTER);
|
||||
|
||||
if (nl == NULL) {
|
||||
perror("mnl_socket_open");
|
||||
lgerror(errno, "mnl_socket_open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
|
||||
perror("mnl_socket_bind");
|
||||
lgerror(errno, "mnl_socket_bind");
|
||||
mnl_socket_close(nl);
|
||||
return -1;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ static int close_socket(struct mnl_socket **_nl) {
|
||||
struct mnl_socket *nl = *_nl;
|
||||
if (nl == NULL) return 1;
|
||||
if (mnl_socket_close(nl) < 0) {
|
||||
perror("mnl_socket_close");
|
||||
lgerror(errno, "mnl_socket_close");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -77,26 +77,26 @@ static int close_socket(struct mnl_socket **_nl) {
|
||||
static int open_raw_socket(void) {
|
||||
if (rawsocket != -2) {
|
||||
errno = EALREADY;
|
||||
perror("Raw socket is already opened");
|
||||
lgerror(errno, "Raw socket is already opened");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rawsocket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
|
||||
if (rawsocket == -1) {
|
||||
perror("Unable to create raw socket");
|
||||
lgerror(errno, "Unable to create raw socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mark = config.mark;
|
||||
if (setsockopt(rawsocket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0)
|
||||
{
|
||||
fprintf(stderr, "setsockopt(SO_MARK, %d) failed\n", mark);
|
||||
lgerror(errno, "setsockopt(SO_MARK, %d) failed\n", mark);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mst = pthread_mutex_init(&rawsocket_lock, NULL);
|
||||
if (mst) {
|
||||
fprintf(stderr, "Mutex err: %d\n", mst);
|
||||
lgerror(errno, "Mutex err: %d\n", mst);
|
||||
close(rawsocket);
|
||||
errno = mst;
|
||||
|
||||
@@ -110,12 +110,12 @@ static int open_raw_socket(void) {
|
||||
static int close_raw_socket(void) {
|
||||
if (rawsocket < 0) {
|
||||
errno = EALREADY;
|
||||
perror("Raw socket is not set");
|
||||
lgerror(errno, "Raw socket is not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (close(rawsocket)) {
|
||||
perror("Unable to close raw socket");
|
||||
lgerror(errno, "Unable to close raw socket");
|
||||
pthread_mutex_destroy(&rawsocket_lock);
|
||||
return -1;
|
||||
}
|
||||
@@ -129,26 +129,26 @@ static int close_raw_socket(void) {
|
||||
static int open_raw6_socket(void) {
|
||||
if (raw6socket != -2) {
|
||||
errno = EALREADY;
|
||||
perror("Raw socket is already opened");
|
||||
lgerror(errno, "Raw socket is already opened");
|
||||
return -1;
|
||||
}
|
||||
|
||||
raw6socket = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW);
|
||||
if (rawsocket == -1) {
|
||||
perror("Unable to create raw socket");
|
||||
lgerror(errno, "Unable to create raw socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mark = config.mark;
|
||||
if (setsockopt(raw6socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0)
|
||||
{
|
||||
fprintf(stderr, "setsockopt(SO_MARK, %d) failed\n", mark);
|
||||
lgerror(errno, "setsockopt(SO_MARK, %d) failed\n", mark);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int mst = pthread_mutex_init(&raw6socket_lock, NULL);
|
||||
if (mst) {
|
||||
fprintf(stderr, "Mutex err: %d\n", mst);
|
||||
lgerror(mst, "Mutex err: %d\n", mst);
|
||||
close(raw6socket);
|
||||
errno = mst;
|
||||
|
||||
@@ -162,12 +162,12 @@ static int open_raw6_socket(void) {
|
||||
static int close_raw6_socket(void) {
|
||||
if (raw6socket < 0) {
|
||||
errno = EALREADY;
|
||||
perror("Raw socket is not set");
|
||||
lgerror(errno, "Raw socket is not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (close(raw6socket)) {
|
||||
perror("Unable to close raw socket");
|
||||
lgerror(errno, "Unable to close raw socket");
|
||||
pthread_mutex_destroy(&rawsocket_lock);
|
||||
return -1;
|
||||
}
|
||||
@@ -203,7 +203,7 @@ static int send_raw_ipv4(const uint8_t *pkt, uint32_t pktlen) {
|
||||
pthread_mutex_lock(&rawsocket_lock);
|
||||
|
||||
int sent = sendto(rawsocket,
|
||||
pkt, pktlen, 0,
|
||||
pkt, pktlen, MSG_DONTWAIT,
|
||||
(struct sockaddr *)&daddr, sizeof(daddr));
|
||||
|
||||
if (config.threads != 1)
|
||||
@@ -238,7 +238,7 @@ static int send_raw_ipv6(const uint8_t *pkt, uint32_t pktlen) {
|
||||
pthread_mutex_lock(&rawsocket_lock);
|
||||
|
||||
int sent = sendto(raw6socket,
|
||||
pkt, pktlen, 0,
|
||||
pkt, pktlen, MSG_DONTWAIT,
|
||||
(struct sockaddr *)&daddr, sizeof(daddr));
|
||||
|
||||
lgtrace_addp("rawsocket sent %d", sent);
|
||||
@@ -256,35 +256,29 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
||||
int ret;
|
||||
|
||||
if (pktlen > AVAILABLE_MTU) {
|
||||
if (config.verbose)
|
||||
printf("Split packet!\n");
|
||||
lgtrace("Split packet!\n");
|
||||
|
||||
NETBUF_ALLOC(buff1, MNL_SOCKET_BUFFER_SIZE);
|
||||
if (!NETBUF_CHECK(buff1)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
NETBUF_ALLOC(buff2, MNL_SOCKET_BUFFER_SIZE);
|
||||
if (!NETBUF_CHECK(buff2)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
NETBUF_FREE(buff1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
uint8_t buff1[MNL_SOCKET_BUFFER_SIZE];
|
||||
uint32_t buff1_size = MNL_SOCKET_BUFFER_SIZE;
|
||||
uint8_t buff2[MNL_SOCKET_BUFFER_SIZE];
|
||||
uint32_t buff2_size = MNL_SOCKET_BUFFER_SIZE;
|
||||
|
||||
switch (config.fragmentation_strategy) {
|
||||
case FRAG_STRAT_TCP:
|
||||
if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case FRAG_STRAT_IP:
|
||||
if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
printf("send_raw_socket: Packet is too big but fragmentation is disabled!\n");
|
||||
return -EINVAL;
|
||||
goto free_buffs;
|
||||
}
|
||||
|
||||
int sent = 0;
|
||||
@@ -292,31 +286,40 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
||||
|
||||
if (status >= 0) sent += status;
|
||||
else {
|
||||
return status;
|
||||
ret = status;
|
||||
goto free_buffs;
|
||||
}
|
||||
|
||||
status = send_raw_socket(buff2, buff2_size);
|
||||
if (status >= 0) sent += status;
|
||||
else {
|
||||
return status;
|
||||
ret = status;
|
||||
goto free_buffs;
|
||||
}
|
||||
|
||||
return sent;
|
||||
ret = sent;
|
||||
|
||||
free_buffs:
|
||||
NETBUF_FREE(buff1)
|
||||
NETBUF_FREE(buff2)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipvx = netproto_version(pkt, pktlen);
|
||||
|
||||
if (ipvx == IP4VERSION)
|
||||
return send_raw_ipv4(pkt, pktlen);
|
||||
else if (ipvx == IP6VERSION)
|
||||
return send_raw_ipv6(pkt, pktlen);
|
||||
|
||||
printf("proto version %d is unsupported\n", ipvx);
|
||||
if (ipvx == IP4VERSION) {
|
||||
ret = send_raw_ipv4(pkt, pktlen);
|
||||
} else if (ipvx == IP6VERSION) {
|
||||
ret = send_raw_ipv6(pkt, pktlen);
|
||||
} else {
|
||||
lginfo("proto version %d is unsupported\n", ipvx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lgtrace_addp("raw_sock_send: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct packet_data {
|
||||
uint32_t id;
|
||||
uint16_t hw_proto;
|
||||
@@ -342,7 +345,7 @@ static int fallback_accept_packet(uint32_t id, struct queue_data qdata) {
|
||||
nfq_nlmsg_verdict_put(verdnlh, id, NF_ACCEPT);
|
||||
|
||||
if (mnl_socket_sendto(*qdata._nl, verdnlh, verdnlh->nlmsg_len) < 0) {
|
||||
perror("mnl_socket_send");
|
||||
lgerror(errno, "mnl_socket_send");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
|
||||
@@ -367,7 +370,7 @@ void *delay_packet_send_fn(void *data) {
|
||||
int ret = send_raw_socket(pkt, pktlen);
|
||||
if (ret < 0) {
|
||||
errno = -ret;
|
||||
perror("send delayed raw packet");
|
||||
lgerror(errno, "send delayed raw packet");
|
||||
}
|
||||
|
||||
free(pkt);
|
||||
@@ -375,7 +378,7 @@ void *delay_packet_send_fn(void *data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
|
||||
int delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
|
||||
struct dps_t *dpdt = malloc(sizeof(struct dps_t));
|
||||
dpdt->pkt = malloc(data_len);
|
||||
memcpy(dpdt->pkt, data, data_len);
|
||||
@@ -384,6 +387,9 @@ void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigne
|
||||
pthread_t thr;
|
||||
pthread_create(&thr, NULL, delay_packet_send_fn, dpdt);
|
||||
pthread_detach(thr);
|
||||
lgtrace_addp("Scheduled packet send after %d ms", delay_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||
@@ -396,13 +402,13 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||
struct packet_data packet = {0};
|
||||
|
||||
if (nfq_nlmsg_parse(nlh, attr) < 0) {
|
||||
perror("Attr parse");
|
||||
lgerror(errno, "Attr parse");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
|
||||
if (attr[NFQA_PACKET_HDR] == NULL) {
|
||||
errno = ENODATA;
|
||||
perror("Metaheader not set");
|
||||
lgerror(errno, "Metaheader not set");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
|
||||
@@ -415,7 +421,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||
packet.payload = mnl_attr_get_payload(attr[NFQA_PAYLOAD]);
|
||||
|
||||
if (attr[NFQA_CAP_LEN] != NULL && ntohl(mnl_attr_get_u32(attr[NFQA_CAP_LEN])) != packet.payload_len) {
|
||||
fprintf(stderr, "The packet was truncated! Skip!\n");
|
||||
lgerr("The packet was truncated! Skip!\n");
|
||||
return fallback_accept_packet(packet.id, *qdata);
|
||||
}
|
||||
|
||||
@@ -443,7 +449,7 @@ static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||
}
|
||||
|
||||
if (mnl_socket_sendto(*qdata->_nl, verdnlh, verdnlh->nlmsg_len) < 0) {
|
||||
perror("mnl_socket_send");
|
||||
lgerror(errno, "mnl_socket_send");
|
||||
return MNL_CB_ERROR;
|
||||
}
|
||||
|
||||
@@ -456,20 +462,64 @@ int init_queue(int queue_num) {
|
||||
struct mnl_socket *nl;
|
||||
|
||||
if (open_socket(&nl)) {
|
||||
perror("Unable to open socket");
|
||||
lgerror(errno, "Unable to open socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t portid = mnl_socket_get_portid(nl);
|
||||
|
||||
struct nlmsghdr *nlh;
|
||||
char buf[BUF_SIZE];
|
||||
NETBUF_ALLOC(bbuf, BUF_SIZE);
|
||||
if (!NETBUF_CHECK(bbuf)) {
|
||||
lgerror(-ENOMEM, "Allocation error");
|
||||
goto die_alloc;
|
||||
}
|
||||
char *buf = (char *)bbuf;
|
||||
|
||||
/* Support for kernels versions < 3.8 */
|
||||
// Obsolete and ignored in kernel version 3.8
|
||||
// https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=0360ae412d09bc6f4864c801effcb20bfd84520e
|
||||
|
||||
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num);
|
||||
nfq_nlmsg_cfg_put_cmd(nlh, PF_INET, NFQNL_CFG_CMD_PF_UNBIND);
|
||||
|
||||
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
||||
lgerror(errno, "mnl_socket_send");
|
||||
goto die;
|
||||
}
|
||||
|
||||
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num);
|
||||
nfq_nlmsg_cfg_put_cmd(nlh, PF_INET, NFQNL_CFG_CMD_PF_BIND);
|
||||
|
||||
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
||||
lgerror(errno, "mnl_socket_send");
|
||||
goto die;
|
||||
}
|
||||
|
||||
if (config.use_ipv6) {
|
||||
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num);
|
||||
nfq_nlmsg_cfg_put_cmd(nlh, PF_INET6, NFQNL_CFG_CMD_PF_UNBIND);
|
||||
|
||||
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
||||
lgerror(errno, "mnl_socket_send");
|
||||
goto die;
|
||||
}
|
||||
|
||||
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num);
|
||||
nfq_nlmsg_cfg_put_cmd(nlh, PF_INET6, NFQNL_CFG_CMD_PF_BIND);
|
||||
|
||||
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
||||
lgerror(errno, "mnl_socket_send");
|
||||
goto die;
|
||||
}
|
||||
}
|
||||
/* End of support for kernel versions < 3.8 */
|
||||
|
||||
nlh = nfq_nlmsg_put(buf, NFQNL_MSG_CONFIG, queue_num);
|
||||
nfq_nlmsg_cfg_put_cmd(nlh, AF_INET, NFQNL_CFG_CMD_BIND);
|
||||
|
||||
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
||||
perror("mnl_socket_send");
|
||||
lgerror(errno, "mnl_socket_send");
|
||||
goto die;
|
||||
}
|
||||
|
||||
@@ -482,7 +532,7 @@ int init_queue(int queue_num) {
|
||||
}
|
||||
|
||||
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0) {
|
||||
perror("mnl_socket_send");
|
||||
lgerror(errno, "mnl_socket_send");
|
||||
goto die;
|
||||
}
|
||||
|
||||
@@ -499,32 +549,35 @@ int init_queue(int queue_num) {
|
||||
.queue_num = queue_num
|
||||
};
|
||||
|
||||
printf("Queue %d started\n", qdata.queue_num);
|
||||
lginfo("Queue %d started\n", qdata.queue_num);
|
||||
|
||||
while (1) {
|
||||
ret = mnl_socket_recvfrom(nl, buf, BUF_SIZE);
|
||||
if (ret == -1) {
|
||||
perror("mnl_socket_recvfrom");
|
||||
lgerror(errno, "mnl_socket_recvfrom");
|
||||
goto die;
|
||||
}
|
||||
|
||||
ret = mnl_cb_run(buf, ret, 0, portid, queue_cb, &qdata);
|
||||
if (ret < 0) {
|
||||
lgerror("mnl_cb_run", -EPERM);
|
||||
lgerror(-EPERM, "mnl_cb_run");
|
||||
if (errno == EPERM) {
|
||||
printf("Probably another instance of youtubeUnblock with the same queue number is running\n");
|
||||
lgerror(errno, "Probably another instance of youtubeUnblock with the same queue number is running\n");
|
||||
} else {
|
||||
printf("Make sure the nfnetlink_queue kernel module is loaded\n");
|
||||
lgerror(errno, "Make sure the nfnetlink_queue kernel module is loaded\n");
|
||||
}
|
||||
goto die;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NETBUF_FREE(bbuf)
|
||||
close_socket(&nl);
|
||||
return 0;
|
||||
|
||||
die:
|
||||
NETBUF_FREE(bbuf)
|
||||
die_alloc:
|
||||
close_socket(&nl);
|
||||
return -1;
|
||||
}
|
||||
@@ -548,7 +601,7 @@ void *init_queue_wrapper(void *qdconf) {
|
||||
|
||||
thres->status = init_queue(qconf->queue_num);
|
||||
|
||||
fprintf(stderr, "Thread %d exited with status %d\n", qconf->i, thres->status);
|
||||
lgerror(thres->status, "Thread %d exited with status %d\n", qconf->i, thres->status);
|
||||
|
||||
return thres;
|
||||
}
|
||||
@@ -562,7 +615,7 @@ int main(int argc, char *argv[]) {
|
||||
int ret;
|
||||
if ((ret = parse_args(argc, argv)) != 0) {
|
||||
if (ret < 0) {
|
||||
perror("Unable to parse args");
|
||||
lgerror(errno, "Unable to parse args");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
@@ -571,19 +624,24 @@ int main(int argc, char *argv[]) {
|
||||
print_version();
|
||||
print_welcome();
|
||||
|
||||
|
||||
if (open_raw_socket() < 0) {
|
||||
perror("Unable to open raw socket");
|
||||
lgerror(errno, "Unable to open raw socket");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (config.use_ipv6) {
|
||||
if (open_raw6_socket() < 0) {
|
||||
perror("Unable to open raw socket for ipv6");
|
||||
lgerror(errno, "Unable to open raw socket for ipv6");
|
||||
close_raw_socket();
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.daemonize) {
|
||||
daemon(0, config.noclose);
|
||||
}
|
||||
|
||||
struct queue_res *qres = &defqres;
|
||||
|
||||
if (config.threads == 1) {
|
||||
@@ -594,7 +652,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
qres = init_queue_wrapper(&tconf);
|
||||
} else {
|
||||
printf("%d threads wil be used\n", config.threads);
|
||||
lginfo("%d threads wil be used\n", config.threads);
|
||||
|
||||
struct queue_conf thread_confs[MAX_THREADS];
|
||||
pthread_t threads[MAX_THREADS];
|
||||
|
||||
Reference in New Issue
Block a user