mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2026-01-29 21:50:33 +03:00
43
.github/workflows/build-ci.yml
vendored
43
.github/workflows/build-ci.yml
vendored
@@ -228,6 +228,48 @@ jobs:
|
|||||||
path: /builder/youtubeUnblock*.ipk
|
path: /builder/youtubeUnblock*.ipk
|
||||||
if-no-files-found: error
|
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 }}
|
||||||
|
SHA: ${{ needs.prepare.outputs.sha }}
|
||||||
|
run: |
|
||||||
|
sed -i "s/PKG_REV:=.*$/PKG_REV:=$SHA/;s/PKG_VERSION:=.*$/PKG_VERSION:=$VERSION-$SHA/" youtubeUnblock/Makefile
|
||||||
|
|
||||||
|
- name: Build packages
|
||||||
|
id: build
|
||||||
|
env:
|
||||||
|
VERSION: ${{ needs.prepare.outputs.version }}
|
||||||
|
SHA: ${{ needs.prepare.outputs.sha }}
|
||||||
|
working-directory: /builder
|
||||||
|
run: |
|
||||||
|
echo "src-link youtubeUnblock $GITHUB_WORKSPACE" >> feeds.conf
|
||||||
|
cat feeds.conf
|
||||||
|
./scripts/feeds update youtubeUnblock
|
||||||
|
./scripts/feeds install -a -p youtubeUnblock
|
||||||
|
make defconfig
|
||||||
|
make package/luci-app-youtubeUnblock/compile V=s
|
||||||
|
mv $(find ./bin -type f -name 'luci-app-youtubeUnblock*.ipk') ./luci-app-youtubeUnblock-$VERSION-$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:
|
build-entware:
|
||||||
needs: prepare
|
needs: prepare
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -341,3 +383,4 @@ jobs:
|
|||||||
files: |
|
files: |
|
||||||
./**/youtubeUnblock*.ipk
|
./**/youtubeUnblock*.ipk
|
||||||
./**/youtubeUnblock*.tar.gz
|
./**/youtubeUnblock*.tar.gz
|
||||||
|
./**/luci-app-youtubeUnblock*.ipk
|
||||||
|
|||||||
202
.github/workflows/test.yml
vendored
Normal file
202
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
# Tests whether the youtubeUnblock builds properly
|
||||||
|
|
||||||
|
name: "youtubeUnblock build test"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
paths-ignore:
|
||||||
|
- '.editorconfig'
|
||||||
|
- '.gitignore'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'README.md'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
paths-ignore:
|
||||||
|
- '.editorconfig'
|
||||||
|
- '.gitignore'
|
||||||
|
- 'LICENSE'
|
||||||
|
- 'README.md'
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
prepare:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
version: ${{ steps.gh.outputs.version }}
|
||||||
|
sha: ${{ steps.gh.outputs.sha }}
|
||||||
|
steps:
|
||||||
|
- name: 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:
|
||||||
|
include:
|
||||||
|
- kernel_version: "6.6.52"
|
||||||
|
source: "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.52.tar.xz"
|
||||||
|
container_version: "24.04"
|
||||||
|
|
||||||
|
- kernel_version: "5.15.167"
|
||||||
|
source: "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.167.tar.xz"
|
||||||
|
container_version: "24.04"
|
||||||
|
|
||||||
|
- kernel_version: "5.4.284"
|
||||||
|
source: "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.284.tar.xz"
|
||||||
|
container_version: "24.04"
|
||||||
|
|
||||||
|
- kernel_version: "4.19.322"
|
||||||
|
source: "https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.19.322.tar.xz"
|
||||||
|
container_version: "24.04"
|
||||||
|
|
||||||
|
- kernel_version: "4.4.302"
|
||||||
|
source: "https://cdn.kernel.org/pub/linux/kernel/v4.x/linux-4.4.302.tar.xz"
|
||||||
|
container_version: "24.04"
|
||||||
|
|
||||||
|
- kernel_version: "3.10.108"
|
||||||
|
source: "https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.10.108.tar.xz"
|
||||||
|
container_version: "16.04"
|
||||||
|
|
||||||
|
- kernel_version: "3.0.101"
|
||||||
|
source: "https://cdn.kernel.org/pub/linux/kernel/v3.x/linux-3.0.101.tar.xz"
|
||||||
|
container_version: "14.04"
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Restore builder from cache
|
||||||
|
id: cache-restore
|
||||||
|
uses: actions/cache/restore@v4
|
||||||
|
with:
|
||||||
|
path: ~/builder.tar
|
||||||
|
key: builder-${{ matrix.kernel_version }}
|
||||||
|
|
||||||
|
- name: Load builder from cache
|
||||||
|
if: steps.cache-restore.outputs.cache-hit == 'true'
|
||||||
|
run: |
|
||||||
|
docker import - builder < ~/builder.tar
|
||||||
|
|
||||||
|
- name: Prepare build env
|
||||||
|
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
mkdir ~/linux
|
||||||
|
pwd
|
||||||
|
ls /
|
||||||
|
ls ~
|
||||||
|
|
||||||
|
- name: Obtain kernel
|
||||||
|
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd ~/linux
|
||||||
|
wget ${{ matrix.source }} -O kernel.tar.xz -q
|
||||||
|
tar -xf kernel.tar.xz
|
||||||
|
rm -f kernel.tar.xz
|
||||||
|
/bin/bash -c "mv linux-* linux"
|
||||||
|
ls
|
||||||
|
ls linux
|
||||||
|
|
||||||
|
- name: Install docker
|
||||||
|
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd ~/linux
|
||||||
|
docker pull ubuntu:${{ matrix.container_version }}
|
||||||
|
docker container create --name ubu_builder -w / ubuntu:${{ matrix.container_version }} tail -f /dev/null
|
||||||
|
docker container start ubu_builder
|
||||||
|
docker container exec ubu_builder bash -c "apt update && apt install -y build-essential flex bc bison libelf-dev elfutils libssl-dev"
|
||||||
|
docker cp ./linux ubu_builder:/linux
|
||||||
|
|
||||||
|
- name: Build kernel
|
||||||
|
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd ~/linux
|
||||||
|
docker container exec -w /linux ubu_builder bash -c 'make defconfig'
|
||||||
|
docker container exec -w /linux ubu_builder bash -c 'make -j $(nproc)'
|
||||||
|
|
||||||
|
- name: Export container
|
||||||
|
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd ~/linux
|
||||||
|
docker container kill ubu_builder
|
||||||
|
docker container export ubu_builder > ubu_builder.tar
|
||||||
|
docker container rm ubu_builder
|
||||||
|
mv ./ubu_builder.tar ~/builder.tar
|
||||||
|
docker import - builder < ~/builder.tar
|
||||||
|
|
||||||
|
- name: Save kernel image to cache
|
||||||
|
if: steps.cache-restore.outputs.cache-hit != 'true'
|
||||||
|
id: cache-save
|
||||||
|
uses: actions/cache/save@v4
|
||||||
|
with:
|
||||||
|
path: ~/builder.tar
|
||||||
|
key: builder-${{ matrix.kernel_version }}
|
||||||
|
|
||||||
|
- 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 builder 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
|
||||||
|
|
||||||
2
Kbuild
2
Kbuild
@@ -1,3 +1,3 @@
|
|||||||
obj-m := kyoutubeUnblock.o
|
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
|
ccflags-y := -std=gnu99 -DKERNEL_SPACE -Wno-error -Wno-declaration-after-statement
|
||||||
|
|||||||
15
README.md
15
README.md
@@ -85,7 +85,9 @@ 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).
|
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**).
|
||||||
|
|
||||||
|
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`.
|
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`.
|
||||||
|
|
||||||
@@ -181,6 +183,9 @@ 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-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`
|
- `--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/acknowledgemend random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled.
|
||||||
- `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured.
|
- `ttl` specifies that packet will be invalidated after `--faking-ttl=n` hops. `ttl` is better but may cause issues if unconfigured.
|
||||||
@@ -226,8 +231,12 @@ Available flags:
|
|||||||
|
|
||||||
- `--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.
|
- `--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
|
## 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 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 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.
|
||||||
|
|
||||||
@@ -319,7 +328,9 @@ You can configure the module with its flags in insmod:
|
|||||||
insmod kyoutubeUnblock.ko fake_sni=1 exclude_domains=.ru quic_drop=1
|
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:
|
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
|
```sh
|
||||||
|
|||||||
452
args.c
452
args.c
@@ -1,5 +1,4 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "raw_replacements.h"
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -7,54 +6,22 @@
|
|||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include "types.h"
|
||||||
|
#include "args.h"
|
||||||
|
|
||||||
|
static char custom_fake_buf[MAX_FAKE_SIZE];
|
||||||
|
|
||||||
struct config_t config = {
|
struct config_t config = {
|
||||||
.threads = THREADS_NUM,
|
.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,
|
.queue_start_num = DEFAULT_QUEUE_NUM,
|
||||||
.fake_sni_pkt = fake_sni_old,
|
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||||
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator
|
.use_ipv6 = 1,
|
||||||
|
|
||||||
|
.verbose = VERBOSE_DEBUG,
|
||||||
|
.use_gso = true,
|
||||||
|
|
||||||
|
.default_config = default_section_config,
|
||||||
|
.custom_configs_len = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
#define OPT_SNI_DOMAINS 1
|
#define OPT_SNI_DOMAINS 1
|
||||||
@@ -63,6 +30,10 @@ struct config_t config = {
|
|||||||
#define OPT_FAKING_TTL 3
|
#define OPT_FAKING_TTL 3
|
||||||
#define OPT_FAKING_STRATEGY 10
|
#define OPT_FAKING_STRATEGY 10
|
||||||
#define OPT_FAKE_SNI_SEQ_LEN 11
|
#define OPT_FAKE_SNI_SEQ_LEN 11
|
||||||
|
#define OPT_FAKE_SNI_TYPE 27
|
||||||
|
#define OPT_FAKE_CUSTOM_PAYLOAD 28
|
||||||
|
#define OPT_START_SECTION 29
|
||||||
|
#define OPT_END_SECTION 30
|
||||||
#define OPT_FRAG 4
|
#define OPT_FRAG 4
|
||||||
#define OPT_FRAG_SNI_REVERSE 12
|
#define OPT_FRAG_SNI_REVERSE 12
|
||||||
#define OPT_FRAG_SNI_FAKED 13
|
#define OPT_FRAG_SNI_FAKED 13
|
||||||
@@ -83,7 +54,7 @@ struct config_t config = {
|
|||||||
#define OPT_NO_GSO 8
|
#define OPT_NO_GSO 8
|
||||||
#define OPT_QUEUE_NUM 9
|
#define OPT_QUEUE_NUM 9
|
||||||
|
|
||||||
#define OPT_MAX OPT_SNI_DOMAINS
|
#define OPT_MAX OPT_END_SECTION
|
||||||
|
|
||||||
static struct option long_opt[] = {
|
static struct option long_opt[] = {
|
||||||
{"help", 0, 0, 'h'},
|
{"help", 0, 0, 'h'},
|
||||||
@@ -94,6 +65,8 @@ static struct option long_opt[] = {
|
|||||||
{"synfake", 1, 0, OPT_SYNFAKE},
|
{"synfake", 1, 0, OPT_SYNFAKE},
|
||||||
{"synfake-len", 1, 0, OPT_SYNFAKE_LEN},
|
{"synfake-len", 1, 0, OPT_SYNFAKE_LEN},
|
||||||
{"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_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},
|
{"faking-strategy", 1, 0, OPT_FAKING_STRATEGY},
|
||||||
{"fake-seq-offset", 1, 0, OPT_FAKE_SEQ_OFFSET},
|
{"fake-seq-offset", 1, 0, OPT_FAKE_SEQ_OFFSET},
|
||||||
{"faking-ttl", 1, 0, OPT_FAKING_TTL},
|
{"faking-ttl", 1, 0, OPT_FAKING_TTL},
|
||||||
@@ -113,6 +86,8 @@ static struct option long_opt[] = {
|
|||||||
{"no-ipv6", 0, 0, OPT_NO_IPV6},
|
{"no-ipv6", 0, 0, OPT_NO_IPV6},
|
||||||
{"queue-num", 1, 0, OPT_QUEUE_NUM},
|
{"queue-num", 1, 0, OPT_QUEUE_NUM},
|
||||||
{"packet-mark", 1, 0, OPT_PACKET_MARK},
|
{"packet-mark", 1, 0, OPT_PACKET_MARK},
|
||||||
|
{"fbegin", 0, 0, OPT_START_SECTION},
|
||||||
|
{"fend", 0, 0, OPT_END_SECTION},
|
||||||
{0,0,0,0}
|
{0,0,0,0}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -150,6 +125,8 @@ void print_usage(const char *argv0) {
|
|||||||
printf("\t--exclude-domains=<comma separated domain list>\n");
|
printf("\t--exclude-domains=<comma separated domain list>\n");
|
||||||
printf("\t--fake-sni={1|0}\n");
|
printf("\t--fake-sni={1|0}\n");
|
||||||
printf("\t--fake-sni-seq-len=<length>\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--fake-seq-offset=<offset>\n");
|
||||||
printf("\t--faking-ttl=<ttl>\n");
|
printf("\t--faking-ttl=<ttl>\n");
|
||||||
printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}\n");
|
printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}\n");
|
||||||
@@ -170,16 +147,27 @@ void print_usage(const char *argv0) {
|
|||||||
printf("\t--trace\n");
|
printf("\t--trace\n");
|
||||||
printf("\t--no-gso\n");
|
printf("\t--no-gso\n");
|
||||||
printf("\t--no-ipv6\n");
|
printf("\t--no-ipv6\n");
|
||||||
|
printf("\t--fbegin\n");
|
||||||
|
printf("\t--fend\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int parse_args(int argc, char *argv[]) {
|
int parse_args(int argc, char *argv[]) {
|
||||||
int opt;
|
int opt;
|
||||||
int optIdx;
|
int optIdx = 0;
|
||||||
long num;
|
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) {
|
while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
/* config_t scoped configs */
|
||||||
case 'h':
|
case 'h':
|
||||||
print_usage(argv[0]);
|
print_usage(argv[0]);
|
||||||
goto stop_exec;
|
goto stop_exec;
|
||||||
@@ -187,49 +175,98 @@ int parse_args(int argc, char *argv[]) {
|
|||||||
print_version();
|
print_version();
|
||||||
goto stop_exec;
|
goto stop_exec;
|
||||||
case OPT_TRACE:
|
case OPT_TRACE:
|
||||||
|
if (section_iter != SECT_ITER_DEFAULT)
|
||||||
|
goto invalid_opt;
|
||||||
config.verbose = 2;
|
config.verbose = 2;
|
||||||
break;
|
break;
|
||||||
case OPT_SILENT:
|
case OPT_SILENT:
|
||||||
|
if (section_iter != SECT_ITER_DEFAULT)
|
||||||
|
goto invalid_opt;
|
||||||
|
|
||||||
config.verbose = 0;
|
config.verbose = 0;
|
||||||
break;
|
break;
|
||||||
case OPT_NO_GSO:
|
case OPT_NO_GSO:
|
||||||
|
if (section_iter != SECT_ITER_DEFAULT)
|
||||||
|
goto invalid_opt;
|
||||||
|
|
||||||
config.use_gso = 0;
|
config.use_gso = 0;
|
||||||
break;
|
break;
|
||||||
case OPT_NO_IPV6:
|
case OPT_NO_IPV6:
|
||||||
|
if (section_iter != SECT_ITER_DEFAULT)
|
||||||
|
goto invalid_opt;
|
||||||
|
|
||||||
config.use_ipv6 = 0;
|
config.use_ipv6 = 0;
|
||||||
break;
|
break;
|
||||||
case OPT_QUIC_DROP:
|
case OPT_THREADS:
|
||||||
config.quic_drop = 1;
|
if (section_iter != SECT_ITER_DEFAULT)
|
||||||
break;
|
goto invalid_opt;
|
||||||
case OPT_SNI_DOMAINS:
|
|
||||||
if (!strcmp(optarg, "all")) {
|
|
||||||
config.all_domains = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.domains_str = optarg;
|
num = parse_numeric_option(optarg);
|
||||||
config.domains_strlen = strlen(config.domains_str);
|
if (errno != 0 || num < 0 || num > MAX_THREADS) {
|
||||||
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 {
|
|
||||||
goto invalid_opt;
|
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;
|
break;
|
||||||
case OPT_FRAG:
|
case OPT_FRAG:
|
||||||
if (strcmp(optarg, "tcp") == 0) {
|
if (strcmp(optarg, "tcp") == 0) {
|
||||||
config.fragmentation_strategy = FRAG_STRAT_TCP;
|
sect_config->fragmentation_strategy = FRAG_STRAT_TCP;
|
||||||
} else if (strcmp(optarg, "ip") == 0) {
|
} 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) {
|
} else if (strcmp(optarg, "none") == 0) {
|
||||||
config.fragmentation_strategy = FRAG_STRAT_NONE;
|
sect_config->fragmentation_strategy = FRAG_STRAT_NONE;
|
||||||
} else {
|
} else {
|
||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
@@ -237,9 +274,9 @@ int parse_args(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
case OPT_FRAG_SNI_FAKED:
|
case OPT_FRAG_SNI_FAKED:
|
||||||
if (strcmp(optarg, "1") == 0) {
|
if (strcmp(optarg, "1") == 0) {
|
||||||
config.frag_sni_faked = 1;
|
sect_config->frag_sni_faked = 1;
|
||||||
} else if (strcmp(optarg, "0") == 0) {
|
} else if (strcmp(optarg, "0") == 0) {
|
||||||
config.frag_sni_faked = 0;
|
sect_config->frag_sni_faked = 0;
|
||||||
} else {
|
} else {
|
||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
@@ -247,9 +284,9 @@ int parse_args(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
case OPT_FRAG_SNI_REVERSE:
|
case OPT_FRAG_SNI_REVERSE:
|
||||||
if (strcmp(optarg, "1") == 0) {
|
if (strcmp(optarg, "1") == 0) {
|
||||||
config.frag_sni_reverse = 1;
|
sect_config->frag_sni_reverse = 1;
|
||||||
} else if (strcmp(optarg, "0") == 0) {
|
} else if (strcmp(optarg, "0") == 0) {
|
||||||
config.frag_sni_reverse = 0;
|
sect_config->frag_sni_reverse = 0;
|
||||||
} else {
|
} else {
|
||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
@@ -257,9 +294,9 @@ int parse_args(int argc, char *argv[]) {
|
|||||||
break;
|
break;
|
||||||
case OPT_FRAG_MIDDLE_SNI:
|
case OPT_FRAG_MIDDLE_SNI:
|
||||||
if (strcmp(optarg, "1") == 0) {
|
if (strcmp(optarg, "1") == 0) {
|
||||||
config.frag_middle_sni = 1;
|
sect_config->frag_middle_sni = 1;
|
||||||
} else if (strcmp(optarg, "0") == 0) {
|
} else if (strcmp(optarg, "0") == 0) {
|
||||||
config.frag_middle_sni = 0;
|
sect_config->frag_middle_sni = 0;
|
||||||
} else {
|
} else {
|
||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
@@ -271,19 +308,19 @@ int parse_args(int argc, char *argv[]) {
|
|||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.frag_sni_pos = num;
|
sect_config->frag_sni_pos = num;
|
||||||
break;
|
break;
|
||||||
case OPT_FAKING_STRATEGY:
|
case OPT_FAKING_STRATEGY:
|
||||||
if (strcmp(optarg, "randseq") == 0) {
|
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) {
|
} 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) {
|
} 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) {
|
} 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) {
|
} else if (strcmp(optarg, "md5sum") == 0) {
|
||||||
config.faking_strategy = FAKE_STRAT_TCP_MD5SUM;
|
sect_config->faking_strategy = FAKE_STRAT_TCP_MD5SUM;
|
||||||
} else {
|
} else {
|
||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
@@ -295,21 +332,21 @@ int parse_args(int argc, char *argv[]) {
|
|||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.faking_ttl = num;
|
sect_config->faking_ttl = num;
|
||||||
break;
|
break;
|
||||||
case OPT_FAKE_SEQ_OFFSET:
|
case OPT_FAKE_SEQ_OFFSET:
|
||||||
num = parse_numeric_option(optarg);
|
num = parse_numeric_option(optarg);
|
||||||
if (errno != 0 || num < 0) {
|
if (errno != 0) {
|
||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.fakeseq_offset = num;
|
sect_config->fakeseq_offset = num;
|
||||||
break;
|
break;
|
||||||
case OPT_FAKE_SNI:
|
case OPT_FAKE_SNI:
|
||||||
if (strcmp(optarg, "1") == 0) {
|
if (strcmp(optarg, "1") == 0) {
|
||||||
config.fake_sni = 1;
|
sect_config->fake_sni = 1;
|
||||||
} else if (strcmp(optarg, "0") == 0) {
|
} else if (strcmp(optarg, "0") == 0) {
|
||||||
config.fake_sni = 0;
|
sect_config->fake_sni = 0;
|
||||||
} else {
|
} else {
|
||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
@@ -321,7 +358,44 @@ int parse_args(int argc, char *argv[]) {
|
|||||||
goto invalid_opt;
|
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;
|
break;
|
||||||
case OPT_FK_WINSIZE:
|
case OPT_FK_WINSIZE:
|
||||||
num = parse_numeric_option(optarg);
|
num = parse_numeric_option(optarg);
|
||||||
@@ -329,66 +403,37 @@ int parse_args(int argc, char *argv[]) {
|
|||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.fk_winsize = num;
|
sect_config->fk_winsize = num;
|
||||||
break;
|
break;
|
||||||
case OPT_SYNFAKE:
|
|
||||||
if (strcmp(optarg, "1") == 0) {
|
|
||||||
config.synfake = 1;
|
|
||||||
} else if (strcmp(optarg, "0") == 0) {
|
|
||||||
config.synfake = 0;
|
|
||||||
} else {
|
|
||||||
goto invalid_opt;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case OPT_SYNFAKE_LEN:
|
|
||||||
num = parse_numeric_option(optarg);
|
|
||||||
if (errno != 0 || num < 0) {
|
|
||||||
goto invalid_opt;
|
|
||||||
}
|
|
||||||
|
|
||||||
config.synfake_len = num;
|
|
||||||
break;
|
|
||||||
case OPT_SEG2DELAY:
|
case OPT_SEG2DELAY:
|
||||||
num = parse_numeric_option(optarg);
|
num = parse_numeric_option(optarg);
|
||||||
if (errno != 0 || num < 0) {
|
if (errno != 0 || num < 0) {
|
||||||
goto invalid_opt;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.seg2_delay = num;
|
sect_config->seg2_delay = num;
|
||||||
break;
|
break;
|
||||||
case OPT_THREADS:
|
case OPT_QUIC_DROP:
|
||||||
num = parse_numeric_option(optarg);
|
sect_config->quic_drop = 1;
|
||||||
if (errno != 0 || num < 0 || num > MAX_THREADS) {
|
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;
|
goto invalid_opt;
|
||||||
}
|
}
|
||||||
|
|
||||||
config.threads = num;
|
|
||||||
break;
|
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:
|
default:
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// out:
|
|
||||||
errno = 0;
|
errno = 0;
|
||||||
return 0;
|
return 0;
|
||||||
stop_exec:
|
stop_exec:
|
||||||
@@ -404,68 +449,6 @@ error:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void print_welcome() {
|
void print_welcome() {
|
||||||
switch (config.fragmentation_strategy) {
|
|
||||||
case FRAG_STRAT_TCP:
|
|
||||||
printf("Using TCP segmentation\n");
|
|
||||||
break;
|
|
||||||
case FRAG_STRAT_IP:
|
|
||||||
printf("Using IP fragmentation\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("SNI fragmentation is disabled\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.seg2_delay) {
|
|
||||||
printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.fake_sni) {
|
|
||||||
printf("Fake SNI will be sent before each target client hello\n");
|
|
||||||
} else {
|
|
||||||
printf("Fake SNI is disabled\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.frag_sni_reverse) {
|
|
||||||
printf("Fragmentation Client Hello will be reversed\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.frag_sni_faked) {
|
|
||||||
printf("Fooling packets will be sent near the original Client Hello\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.fake_sni_seq_len > 1) {
|
|
||||||
printf("Faking sequence of length %d will be built as fake sni\n", config.fake_sni_seq_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (config.faking_strategy) {
|
|
||||||
case FAKE_STRAT_TTL:
|
|
||||||
printf("TTL faking strategy will be used with TTL %d\n", config.faking_ttl);
|
|
||||||
break;
|
|
||||||
case FAKE_STRAT_RAND_SEQ:
|
|
||||||
printf("Random seq faking strategy will be used\n");
|
|
||||||
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) {
|
if (config.use_gso) {
|
||||||
printf("GSO is enabled\n");
|
printf("GSO is enabled\n");
|
||||||
}
|
}
|
||||||
@@ -476,16 +459,87 @@ void print_welcome() {
|
|||||||
printf("IPv6 is disabled\n");
|
printf("IPv6 is disabled\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.quic_drop) {
|
printf("Detected %d config sections\n", config.custom_configs_len + 1);
|
||||||
printf("All QUIC packets will be dropped\n");
|
printf("The sections will be processed in ordred they goes in this output");
|
||||||
}
|
|
||||||
|
|
||||||
if (config.sni_detection == SNI_DETECTION_BRUTE) {
|
ITER_CONFIG_SECTIONS(section) {
|
||||||
printf("Server Name Extension will be parsed in the bruteforce mode\n");
|
int section_number = CONFIG_SECTION_NUMBER(section);
|
||||||
}
|
printf("Section #%d\n", section_number);
|
||||||
|
|
||||||
if (config.all_domains) {
|
switch (section->fragmentation_strategy) {
|
||||||
printf("All Client Hello will be targetted by youtubeUnblock!\n");
|
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 (section->seg2_delay) {
|
||||||
|
printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", section->seg2_delay);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->fake_sni) {
|
||||||
|
printf("Fake SNI will be sent before each target client hello\n");
|
||||||
|
} else {
|
||||||
|
printf("Fake SNI is disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->frag_sni_reverse) {
|
||||||
|
printf("Fragmentation Client Hello will be reversed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->frag_sni_faked) {
|
||||||
|
printf("Fooling packets will be sent near the original Client Hello\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->fake_sni_seq_len > 1) {
|
||||||
|
printf("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:
|
||||||
|
printf("TTL faking strategy will be used with TTL %d\n", section->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", section->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 (section->fk_winsize) {
|
||||||
|
printf("Response TCP window will be set to %d with the appropriate scale\n", section->fk_winsize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->synfake) {
|
||||||
|
printf("Fake SYN payload will be sent with each TCP request SYN packet\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->quic_drop) {
|
||||||
|
printf("All QUIC packets will be dropped\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->sni_detection == SNI_DETECTION_BRUTE) {
|
||||||
|
printf("Server Name Extension will be parsed in the bruteforce mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (section->all_domains) {
|
||||||
|
printf("All Client Hello will be targetted by youtubeUnblock!\n");
|
||||||
|
} else {
|
||||||
|
printf("Target sni domains: %s\n", section->domains_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
135
config.h
135
config.h
@@ -5,12 +5,14 @@
|
|||||||
#define USER_SPACE
|
#define USER_SPACE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "raw_replacements.h"
|
||||||
|
|
||||||
typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len);
|
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
|
* Sends the packet after delay_ms. The function should schedule send and return immediately
|
||||||
* (for example, open daemon thread)
|
* (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 {
|
struct instance_config_t {
|
||||||
raw_send_t send_raw_packet;
|
raw_send_t send_raw_packet;
|
||||||
@@ -18,11 +20,10 @@ struct instance_config_t {
|
|||||||
};
|
};
|
||||||
extern struct instance_config_t instance_config;
|
extern struct instance_config_t instance_config;
|
||||||
|
|
||||||
struct config_t {
|
struct section_config_t {
|
||||||
unsigned int queue_start_num;
|
const char *domains_str;
|
||||||
int threads;
|
unsigned int domains_strlen;
|
||||||
int use_gso;
|
|
||||||
int use_ipv6;
|
|
||||||
int fragmentation_strategy;
|
int fragmentation_strategy;
|
||||||
int frag_sni_reverse;
|
int frag_sni_reverse;
|
||||||
int frag_sni_faked;
|
int frag_sni_faked;
|
||||||
@@ -32,32 +33,97 @@ struct config_t {
|
|||||||
unsigned char faking_ttl;
|
unsigned char faking_ttl;
|
||||||
int fake_sni;
|
int fake_sni;
|
||||||
unsigned int fake_sni_seq_len;
|
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;
|
||||||
|
|
||||||
#define VERBOSE_INFO 0
|
#define VERBOSE_INFO 0
|
||||||
#define VERBOSE_DEBUG 1
|
#define VERBOSE_DEBUG 1
|
||||||
#define VERBOSE_TRACE 2
|
#define VERBOSE_TRACE 2
|
||||||
int verbose;
|
int verbose;
|
||||||
int quic_drop;
|
|
||||||
#define SNI_DETECTION_PARSE 0
|
struct section_config_t default_config;
|
||||||
#define SNI_DETECTION_BRUTE 1
|
struct section_config_t custom_configs[MAX_CONFIGLIST_LEN];
|
||||||
int sni_detection;
|
int custom_configs_len;
|
||||||
/* 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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct config_t config;
|
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
|
#define MAX_THREADS 16
|
||||||
|
|
||||||
#ifndef THREADS_NUM
|
#ifndef THREADS_NUM
|
||||||
@@ -89,19 +155,30 @@ extern struct config_t config;
|
|||||||
#define FAKE_TTL 8
|
#define FAKE_TTL 8
|
||||||
|
|
||||||
// Will invalidate fake packets by out-of-ack_seq out-of-seq request
|
// 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
|
// Will assume that GGC server is located further than FAKE_TTL
|
||||||
// Thus, Fake packet will be eliminated automatically.
|
// Thus, Fake packet will be eliminated automatically.
|
||||||
#define FAKE_STRAT_TTL 2
|
#define FAKE_STRAT_TTL (1 << 1)
|
||||||
#define FAKE_STRAT_PAST_SEQ 3
|
#define FAKE_STRAT_PAST_SEQ (1 << 2)
|
||||||
#define FAKE_STRAT_TCP_CHECK 4
|
#define FAKE_STRAT_TCP_CHECK (1 << 3)
|
||||||
#define FAKE_STRAT_TCP_MD5SUM 5
|
#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
|
#ifndef FAKING_STRATEGY
|
||||||
#define FAKING_STRATEGY FAKE_STRAT_PAST_SEQ
|
#define FAKING_STRATEGY FAKE_STRAT_PAST_SEQ
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define MAX_FAKE_SIZE 1300
|
||||||
|
|
||||||
#if !defined(SILENT) && !defined(KERNEL_SPACE)
|
#if !defined(SILENT) && !defined(KERNEL_SPACE)
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
269
kargs.c
269
kargs.c
@@ -1,54 +1,27 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "raw_replacements.h"
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include <linux/moduleparam.h>
|
#include <linux/moduleparam.h>
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
#define STR_MAXLEN 2048
|
#define STR_MAXLEN 2048
|
||||||
|
|
||||||
|
static char custom_fake_buf[MAX_FAKE_SIZE];
|
||||||
|
|
||||||
struct config_t config = {
|
struct config_t config = {
|
||||||
.frag_sni_reverse = 1,
|
.threads = THREADS_NUM,
|
||||||
.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),
|
|
||||||
|
|
||||||
.queue_start_num = DEFAULT_QUEUE_NUM,
|
.queue_start_num = DEFAULT_QUEUE_NUM,
|
||||||
.fake_sni_pkt = fake_sni_old,
|
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||||
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator
|
.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) {
|
static int unumeric_set(const char *val, const struct kernel_param *kp) {
|
||||||
int n = 0, ret;
|
int n = 0, ret;
|
||||||
ret = kstrtoint(val, 10, &n);
|
ret = kstrtoint(val, 10, &n);
|
||||||
@@ -107,18 +80,19 @@ static const struct kernel_param_ops inverse_boolean_ops = {
|
|||||||
.get = inverse_boolean_get,
|
.get = inverse_boolean_get,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_param_cb(fake_sni, &boolean_parameter_ops, &config.fake_sni, 0664);
|
module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664);
|
||||||
module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &config.fake_sni_seq_len, 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, &config.faking_ttl, 0664);
|
module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664);
|
||||||
module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &config.fakeseq_offset, 0664);
|
module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664);
|
||||||
module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &config.frag_sni_reverse, 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, &config.frag_sni_faked, 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, &config.frag_middle_sni, 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, &config.frag_sni_pos, 0664);
|
module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664);
|
||||||
module_param_cb(fk_winsize, &unumeric_parameter_ops, &config.fk_winsize, 0664);
|
module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664);
|
||||||
module_param_cb(synfake, &boolean_parameter_ops, &config.synfake, 0664);
|
module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664);
|
||||||
module_param_cb(synfake_len, &unumeric_parameter_ops, &config.synfake_len, 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(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) {
|
static int sni_domains_set(const char *val, const struct kernel_param *kp) {
|
||||||
size_t len;
|
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);
|
ret = param_set_charp(val, kp);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
config.domains_strlen = 0;
|
def_section->domains_strlen = 0;
|
||||||
} else {
|
} else {
|
||||||
config.domains_strlen = len;
|
def_section->domains_strlen = len;
|
||||||
if (len == 3 && !strncmp(val, "all", len)) {
|
if (len == 3 && !strncmp(val, "all", len)) {
|
||||||
config.all_domains = 1;
|
def_section->all_domains = 1;
|
||||||
} else {
|
} 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,
|
.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) {
|
static int exclude_domains_set(const char *val, const struct kernel_param *kp) {
|
||||||
size_t len;
|
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);
|
ret = param_set_charp(val, kp);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
config.exclude_domains_strlen = 0;
|
def_section->exclude_domains_strlen = 0;
|
||||||
} else {
|
} else {
|
||||||
config.exclude_domains_strlen = len;
|
def_section->exclude_domains_strlen = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -184,36 +158,62 @@ static const struct kernel_param_ops exclude_domains_ops = {
|
|||||||
.get = param_get_charp,
|
.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(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, &def_section->quic_drop, 0664);
|
||||||
module_param_cb(quic_drop, &boolean_parameter_ops, &config.quic_drop, 0664);
|
|
||||||
|
|
||||||
static int verbose_trace_set(const char *val, const struct kernel_param *kp) {
|
static int verbosity_set(const char *val, const struct kernel_param *kp) {
|
||||||
int n = 0, ret;
|
size_t len;
|
||||||
ret = kstrtoint(val, 10, &n);
|
|
||||||
if (ret != 0 || (n != 0 && n != 1))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (n) {
|
len = strnlen(val, STR_MAXLEN + 1);
|
||||||
n = VERBOSE_TRACE;
|
if (len == STR_MAXLEN + 1) {
|
||||||
} else {
|
pr_err("%s: string parameter too long\n", kp->name);
|
||||||
n = VERBOSE_DEBUG;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct kernel_param_ops verbose_trace_ops = {
|
|
||||||
.set = verbose_trace_set,
|
static int verbosity_get(char *buffer, const struct kernel_param *kp) {
|
||||||
.get = param_get_int,
|
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) {
|
static int frag_strat_set(const char *val, const struct kernel_param *kp) {
|
||||||
size_t len;
|
size_t len;
|
||||||
@@ -264,7 +264,7 @@ static const struct kernel_param_ops frag_strat_ops = {
|
|||||||
.get = frag_strat_get,
|
.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) {
|
static int fake_strat_set(const char *val, const struct kernel_param *kp) {
|
||||||
size_t len;
|
size_t len;
|
||||||
@@ -325,7 +325,7 @@ static const struct kernel_param_ops fake_strat_ops = {
|
|||||||
.get = fake_strat_get,
|
.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) {
|
static int sni_detection_set(const char *val, const struct kernel_param *kp) {
|
||||||
size_t len;
|
size_t len;
|
||||||
@@ -371,4 +371,111 @@ static const struct kernel_param_ops sni_detection_ops = {
|
|||||||
.get = sni_detection_get,
|
.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);
|
||||||
|
|||||||
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 */
|
|
||||||
275
kytunblock.c
275
kytunblock.c
@@ -1,14 +1,16 @@
|
|||||||
#include "nf_wrapper.h"
|
|
||||||
#ifndef KERNEL_SPACE
|
#ifndef KERNEL_SPACE
|
||||||
#error "You are trying to compile the kernel module not in the kernel space"
|
#error "You are trying to compile the kernel module not in the kernel space"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Kernel module for youtubeUnblock.
|
// 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/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/socket.h>
|
#include <linux/socket.h>
|
||||||
#include <linux/net.h>
|
#include <linux/net.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
#include <linux/netfilter.h>
|
#include <linux/netfilter.h>
|
||||||
#include <linux/netfilter_ipv4.h>
|
#include <linux/netfilter_ipv4.h>
|
||||||
@@ -18,12 +20,277 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "kmod_utils.h"
|
|
||||||
|
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_VERSION("0.3.2");
|
MODULE_VERSION("0.3.2");
|
||||||
MODULE_AUTHOR("Vadim Vetrov <vetrovvd@gmail.com>");
|
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) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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("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;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
pr_info("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) {
|
static NF_CALLBACK(ykb_nf_hook, skb) {
|
||||||
int ret;
|
int ret;
|
||||||
|
|||||||
48
mangle.h
48
mangle.h
@@ -2,38 +2,12 @@
|
|||||||
#define YU_MANGLE_H
|
#define YU_MANGLE_H
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "tls.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);
|
|
||||||
|
|
||||||
#define PKT_ACCEPT 0
|
#define PKT_ACCEPT 0
|
||||||
#define PKT_DROP 1
|
#define PKT_DROP 1
|
||||||
|
// Used for section config
|
||||||
|
#define PKT_CONTINUE 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the packet and returns verdict.
|
* 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.
|
* Processe the TCP packet.
|
||||||
* Returns verdict.
|
* 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.
|
* Processes the UDP packet.
|
||||||
* Returns verdict.
|
* 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.
|
* Sends fake client hello.
|
||||||
*/
|
*/
|
||||||
int post_fake_sni(const void *iph, unsigned int iph_len,
|
int post_fake_sni(struct fake_type f_type,
|
||||||
const struct tcphdr *tcph, unsigned int tcph_len,
|
const void *iph, unsigned int iph_len,
|
||||||
unsigned char sequence_len);
|
const struct tcphdr *tcph, unsigned int tcph_len);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits packet by poses and posts.
|
* Splits packet by poses and posts.
|
||||||
* Poses are relative to start of TCP payload.
|
* Poses are relative to start of TCP payload.
|
||||||
* dvs used internally and should be zero.
|
* 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 uint8_t *packet, uint32_t pktlen,
|
||||||
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
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.
|
* Poses are relative to start of TCP payload.
|
||||||
* dvs used internally and should be zero.
|
* 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 uint8_t *packet, uint32_t pktlen,
|
||||||
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||||
#endif /* YU_MANGLE_H */
|
#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 */
|
|
||||||
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("Allocation error", -ENOMEM);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
NETBUF_ALLOC(nzbuf, MAX_PACKET_SIZE * sizeof(int));
|
||||||
|
if (!NETBUF_CHECK(nzbuf)) {
|
||||||
|
lgerror("Allocation error", -ENOMEM);
|
||||||
|
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("Unable to open /dev/urandom", ret);
|
||||||
|
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 */
|
||||||
20
types.h
20
types.h
@@ -13,6 +13,14 @@
|
|||||||
#include <errno.h> // IWYU pragma: export
|
#include <errno.h> // IWYU pragma: export
|
||||||
#include <stdint.h> // IWYU pragma: export
|
#include <stdint.h> // IWYU pragma: export
|
||||||
#include <string.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 */
|
#endif /* SPACES */
|
||||||
|
|
||||||
@@ -99,4 +107,16 @@
|
|||||||
#define NETBUF_FREE(buf) ;
|
#define NETBUF_FREE(buf) ;
|
||||||
#endif
|
#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 */
|
#endif /* TYPES_H */
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF
|
|||||||
|
|
||||||
APP:=$(BUILD_DIR)/youtubeUnblock
|
APP:=$(BUILD_DIR)/youtubeUnblock
|
||||||
|
|
||||||
SRCS := youtubeUnblock.c mangle.c 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)
|
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
|
||||||
|
|
||||||
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la
|
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la
|
||||||
|
|||||||
165
utils.c
165
utils.c
@@ -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 *seg1, uint32_t *s1len,
|
||||||
uint8_t *seg2, uint32_t *s2len) {
|
uint8_t *seg2, uint32_t *s2len) {
|
||||||
|
|
||||||
// struct ip6_hdr *hdr6;
|
|
||||||
void *hdr;
|
void *hdr;
|
||||||
uint32_t hdr_len;
|
uint32_t hdr_len;
|
||||||
struct tcphdr *tcph;
|
struct tcphdr *tcph;
|
||||||
@@ -485,6 +484,11 @@ int tcp_frag(const uint8_t *pkt, uint32_t buflen, uint32_t payload_offset,
|
|||||||
struct iphdr *s2_hdr = (void *)seg2;
|
struct iphdr *s2_hdr = (void *)seg2;
|
||||||
s1_hdr->tot_len = htons(s1_dlen);
|
s1_hdr->tot_len = htons(s1_dlen);
|
||||||
s2_hdr->tot_len = htons(s2_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 {
|
} else {
|
||||||
struct ip6_hdr *s1_hdr = (void *)seg1;
|
struct ip6_hdr *s1_hdr = (void *)seg1;
|
||||||
struct ip6_hdr *s2_hdr = (void *)seg2;
|
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("fail_packet: IP version is unsupported", -EINVAL);
|
||||||
|
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("fail_packet: IP version is unsupported", -EINVAL);
|
||||||
|
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
|
#define UTILS_H
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
#define IP4VERSION 4
|
#define IP4VERSION 4
|
||||||
#define IP6VERSION 6
|
#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);
|
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 */
|
#endif /* UTILS_H */
|
||||||
|
|||||||
@@ -264,27 +264,11 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
|||||||
uint8_t buff2[MNL_SOCKET_BUFFER_SIZE];
|
uint8_t buff2[MNL_SOCKET_BUFFER_SIZE];
|
||||||
uint32_t buff2_size = MNL_SOCKET_BUFFER_SIZE;
|
uint32_t buff2_size = MNL_SOCKET_BUFFER_SIZE;
|
||||||
|
|
||||||
switch (config.fragmentation_strategy) {
|
if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||||
case FRAG_STRAT_TCP:
|
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||||
if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
|
||||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
|
||||||
|
|
||||||
errno = -ret;
|
errno = -ret;
|
||||||
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sent = 0;
|
int sent = 0;
|
||||||
@@ -306,17 +290,19 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
|||||||
|
|
||||||
int ipvx = netproto_version(pkt, pktlen);
|
int ipvx = netproto_version(pkt, pktlen);
|
||||||
|
|
||||||
if (ipvx == IP4VERSION)
|
if (ipvx == IP4VERSION) {
|
||||||
return send_raw_ipv4(pkt, pktlen);
|
ret = send_raw_ipv4(pkt, pktlen);
|
||||||
else if (ipvx == IP6VERSION)
|
} else if (ipvx == IP6VERSION) {
|
||||||
return send_raw_ipv6(pkt, pktlen);
|
ret = send_raw_ipv6(pkt, pktlen);
|
||||||
|
} else {
|
||||||
|
printf("proto version %d is unsupported\n", ipvx);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
printf("proto version %d is unsupported\n", ipvx);
|
lgtrace_addp("raw_sock_send: %d", ret);
|
||||||
return -EINVAL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct packet_data {
|
struct packet_data {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint16_t hw_proto;
|
uint16_t hw_proto;
|
||||||
@@ -375,7 +361,7 @@ void *delay_packet_send_fn(void *data) {
|
|||||||
return NULL;
|
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));
|
struct dps_t *dpdt = malloc(sizeof(struct dps_t));
|
||||||
dpdt->pkt = malloc(data_len);
|
dpdt->pkt = malloc(data_len);
|
||||||
memcpy(dpdt->pkt, data, data_len);
|
memcpy(dpdt->pkt, data, data_len);
|
||||||
@@ -384,6 +370,9 @@ void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigne
|
|||||||
pthread_t thr;
|
pthread_t thr;
|
||||||
pthread_create(&thr, NULL, delay_packet_send_fn, dpdt);
|
pthread_create(&thr, NULL, delay_packet_send_fn, dpdt);
|
||||||
pthread_detach(thr);
|
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) {
|
static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||||
|
|||||||
Reference in New Issue
Block a user