mirror of
https://github.com/Waujito/youtubeUnblock.git
synced 2024-12-22 06:15:31 +00:00
commit
ee56b67d20
43
.github/workflows/build-ci.yml
vendored
43
.github/workflows/build-ci.yml
vendored
@ -228,6 +228,48 @@ jobs:
|
||||
path: /builder/youtubeUnblock*.ipk
|
||||
if-no-files-found: error
|
||||
|
||||
build-openwrt-luci:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: openwrt/sdk:x86_64-openwrt-23.05
|
||||
options: --user root
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: 'openwrt'
|
||||
|
||||
- name: Prepare build
|
||||
env:
|
||||
VERSION: ${{ needs.prepare.outputs.version }}
|
||||
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:
|
||||
needs: prepare
|
||||
runs-on: ubuntu-latest
|
||||
@ -341,3 +383,4 @@ jobs:
|
||||
files: |
|
||||
./**/youtubeUnblock*.ipk
|
||||
./**/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
|
||||
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
|
||||
|
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).
|
||||
|
||||
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`.
|
||||
|
||||
@ -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-type={default|custom|random}` This flag specifies which faking message type should be used for fake packets. For `random`, the message of random length and with random payload will be sent. For `default` the default payload (sni=www.google.com) is used. And for the `custom` option, the payload from `--fake-custom-payload` section utilized. Defaults to `default`.
|
||||
- `--fake-custom-payload=<payload>` Useful with `--fake-sni-type=custom`. You should specify the payload for fake message manually. Use hex format: `--fake-custom-payload=0001020304` mean that 5 bytes sequence: `0x00`, `0x01`, `0x02`, `0x03`, `0x04` used as fake.
|
||||
|
||||
- `--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}` This flag determines the strategy of fake packets invalidation. Defaults to `randseq`
|
||||
- `randseq` specifies that random sequence/acknowledgemend random will be set. This option may be handled by provider which uses *conntrack* with drop on invalid *conntrack* state firewall rule enabled.
|
||||
- `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.
|
||||
|
||||
- `--fbegin` and `--fend` flags: youtubeUnblock supports multiple sets of strategies for specific filters. You may want to initiate a new set after the default one, like: `--sni-domains=googlevideo.com --faking-strategy=md5sum --fbegin --sni-domains=youtube.com --faking-strategy=tcp_check --fend --fbegin --sni-domains=l.google.com --faking-strategy=pastseq --fend`. Note, that the priority of these sets goes backwards: last is first, default (one that does not start with --fbegin) is last. If you start the new section, the default settings are implemented just like youtubeUnblock without any parameters. Note that the config above is just an example and won't work for you.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Check up [this issue](https://github.com/Waujito/youtubeUnblock/issues/148) for useful configs.
|
||||
|
||||
If you got troubles with some sites and you sure that they are blocked by SNI (youtube for example), use may play around with [flags](#flags) and their combinations. At first it is recommended to try `--faking-strategy` flag and `--frag-sni-faked=1`.
|
||||
If you have troubles with some sites being proxied, you can play with flags values. For example, for someone `--faking-strategy=ttl` works. You should specify proper `--fake-sni-ttl=<ttl value>` where ttl is the amount of hops between you and DPI.
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`silent=1 quic_drop=1`)
|
||||
Note that the flags names are different from ones used for the regular youtubeUnblock(right like in UCI configuration for OpenWRT): replace `-` with `_` and no leading `--`. Also to configure togglers you should set them to `1` (`quic_drop=1`)
|
||||
|
||||
Also a good thig to mention is verbosity. The kernel module combines --trace and --silent option to the one parameter `verbosity`. This parameter accepts 3 arguments: `trace`, `debug` and `silent`. I highly don't recommend to enable `trace` mod on router because it may cause huge problems with performance and even freeze your device.
|
||||
|
||||
Also a drop in replacement is supported for all the parameters excluding packet mark. A drop in replacement does not require module restart if you want to change the parameters. You can specify and check the parameters within module's directory inside the sysfs: `/sys/module/kyoutubeUnblock/parameters/`. For example, to set quic_drop to true you may use next command:
|
||||
```sh
|
||||
|
452
args.c
452
args.c
@ -1,5 +1,4 @@
|
||||
#include "config.h"
|
||||
#include "raw_replacements.h"
|
||||
#include <stdbool.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
@ -7,54 +6,22 @@
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "types.h"
|
||||
#include "args.h"
|
||||
|
||||
static char custom_fake_buf[MAX_FAKE_SIZE];
|
||||
|
||||
struct config_t config = {
|
||||
.threads = THREADS_NUM,
|
||||
.frag_sni_reverse = 1,
|
||||
.frag_sni_faked = 0,
|
||||
.fragmentation_strategy = FRAGMENTATION_STRATEGY,
|
||||
.faking_strategy = FAKING_STRATEGY,
|
||||
.faking_ttl = FAKE_TTL,
|
||||
.fake_sni = 1,
|
||||
.fake_sni_seq_len = 1,
|
||||
.frag_middle_sni = 1,
|
||||
.frag_sni_pos = 1,
|
||||
.use_ipv6 = 1,
|
||||
.fakeseq_offset = 10000,
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.synfake = 0,
|
||||
.synfake_len = 0,
|
||||
|
||||
.sni_detection = SNI_DETECTION_PARSE,
|
||||
|
||||
#ifdef SEG2_DELAY
|
||||
.seg2_delay = SEG2_DELAY,
|
||||
#else
|
||||
.seg2_delay = 0,
|
||||
#endif
|
||||
|
||||
#ifdef USE_GSO
|
||||
.use_gso = true,
|
||||
#else
|
||||
.use_gso = false,
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
.verbose = 1,
|
||||
#else
|
||||
.verbose = 0,
|
||||
#endif
|
||||
|
||||
.domains_str = defaul_snistr,
|
||||
.domains_strlen = sizeof(defaul_snistr),
|
||||
|
||||
.exclude_domains_str = "",
|
||||
.exclude_domains_strlen = 0,
|
||||
|
||||
.queue_start_num = DEFAULT_QUEUE_NUM,
|
||||
.fake_sni_pkt = fake_sni_old,
|
||||
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.use_ipv6 = 1,
|
||||
|
||||
.verbose = VERBOSE_DEBUG,
|
||||
.use_gso = true,
|
||||
|
||||
.default_config = default_section_config,
|
||||
.custom_configs_len = 0
|
||||
};
|
||||
|
||||
#define OPT_SNI_DOMAINS 1
|
||||
@ -63,6 +30,10 @@ struct config_t config = {
|
||||
#define OPT_FAKING_TTL 3
|
||||
#define OPT_FAKING_STRATEGY 10
|
||||
#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_SNI_REVERSE 12
|
||||
#define OPT_FRAG_SNI_FAKED 13
|
||||
@ -83,7 +54,7 @@ struct config_t config = {
|
||||
#define OPT_NO_GSO 8
|
||||
#define OPT_QUEUE_NUM 9
|
||||
|
||||
#define OPT_MAX OPT_SNI_DOMAINS
|
||||
#define OPT_MAX OPT_END_SECTION
|
||||
|
||||
static struct option long_opt[] = {
|
||||
{"help", 0, 0, 'h'},
|
||||
@ -94,6 +65,8 @@ static struct option long_opt[] = {
|
||||
{"synfake", 1, 0, OPT_SYNFAKE},
|
||||
{"synfake-len", 1, 0, OPT_SYNFAKE_LEN},
|
||||
{"fake-sni-seq-len", 1, 0, OPT_FAKE_SNI_SEQ_LEN},
|
||||
{"fake-sni-type", 1, 0, OPT_FAKE_SNI_TYPE},
|
||||
{"fake-custom-payload", 1, 0, OPT_FAKE_CUSTOM_PAYLOAD},
|
||||
{"faking-strategy", 1, 0, OPT_FAKING_STRATEGY},
|
||||
{"fake-seq-offset", 1, 0, OPT_FAKE_SEQ_OFFSET},
|
||||
{"faking-ttl", 1, 0, OPT_FAKING_TTL},
|
||||
@ -113,6 +86,8 @@ static struct option long_opt[] = {
|
||||
{"no-ipv6", 0, 0, OPT_NO_IPV6},
|
||||
{"queue-num", 1, 0, OPT_QUEUE_NUM},
|
||||
{"packet-mark", 1, 0, OPT_PACKET_MARK},
|
||||
{"fbegin", 0, 0, OPT_START_SECTION},
|
||||
{"fend", 0, 0, OPT_END_SECTION},
|
||||
{0,0,0,0}
|
||||
};
|
||||
|
||||
@ -150,6 +125,8 @@ void print_usage(const char *argv0) {
|
||||
printf("\t--exclude-domains=<comma separated domain list>\n");
|
||||
printf("\t--fake-sni={1|0}\n");
|
||||
printf("\t--fake-sni-seq-len=<length>\n");
|
||||
printf("\t--fake-sni-type={default|random|custom}\n");
|
||||
printf("\t--fake-custom-payload=<hex payload>\n");
|
||||
printf("\t--fake-seq-offset=<offset>\n");
|
||||
printf("\t--faking-ttl=<ttl>\n");
|
||||
printf("\t--faking-strategy={randseq|ttl|tcp_check|pastseq|md5sum}\n");
|
||||
@ -170,16 +147,27 @@ void print_usage(const char *argv0) {
|
||||
printf("\t--trace\n");
|
||||
printf("\t--no-gso\n");
|
||||
printf("\t--no-ipv6\n");
|
||||
printf("\t--fbegin\n");
|
||||
printf("\t--fend\n");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int parse_args(int argc, char *argv[]) {
|
||||
int opt;
|
||||
int optIdx;
|
||||
int optIdx = 0;
|
||||
long num;
|
||||
|
||||
struct section_config_t *sect_config = &config.default_config;
|
||||
|
||||
#define SECT_ITER_DEFAULT 1
|
||||
#define SECT_ITER_INSIDE 2
|
||||
#define SECT_ITER_OUTSIDE 3
|
||||
|
||||
int section_iter = SECT_ITER_DEFAULT;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "hv", long_opt, &optIdx)) != -1) {
|
||||
switch (opt) {
|
||||
/* config_t scoped configs */
|
||||
case 'h':
|
||||
print_usage(argv[0]);
|
||||
goto stop_exec;
|
||||
@ -187,49 +175,98 @@ int parse_args(int argc, char *argv[]) {
|
||||
print_version();
|
||||
goto stop_exec;
|
||||
case OPT_TRACE:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
config.verbose = 2;
|
||||
break;
|
||||
case OPT_SILENT:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.verbose = 0;
|
||||
break;
|
||||
case OPT_NO_GSO:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.use_gso = 0;
|
||||
break;
|
||||
case OPT_NO_IPV6:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.use_ipv6 = 0;
|
||||
break;
|
||||
case OPT_QUIC_DROP:
|
||||
config.quic_drop = 1;
|
||||
break;
|
||||
case OPT_SNI_DOMAINS:
|
||||
if (!strcmp(optarg, "all")) {
|
||||
config.all_domains = 1;
|
||||
}
|
||||
case OPT_THREADS:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
config.domains_str = optarg;
|
||||
config.domains_strlen = strlen(config.domains_str);
|
||||
break;
|
||||
case OPT_EXCLUDE_DOMAINS:
|
||||
config.exclude_domains_str = optarg;
|
||||
config.exclude_domains_strlen = strlen(config.exclude_domains_str);
|
||||
break;
|
||||
case OPT_SNI_DETECTION:
|
||||
if (strcmp(optarg, "parse") == 0) {
|
||||
config.sni_detection = SNI_DETECTION_PARSE;
|
||||
} else if (strcmp(optarg, "brute") == 0) {
|
||||
config.sni_detection = SNI_DETECTION_BRUTE;
|
||||
} else {
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0 || num > MAX_THREADS) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.threads = num;
|
||||
break;
|
||||
case OPT_QUEUE_NUM:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.queue_start_num = num;
|
||||
break;
|
||||
case OPT_PACKET_MARK:
|
||||
if (section_iter != SECT_ITER_DEFAULT)
|
||||
goto invalid_opt;
|
||||
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.mark = num;
|
||||
break;
|
||||
case OPT_START_SECTION:
|
||||
if (section_iter != SECT_ITER_DEFAULT && section_iter != SECT_ITER_OUTSIDE)
|
||||
goto invalid_opt;
|
||||
|
||||
sect_config = &config.custom_configs[config.custom_configs_len++];
|
||||
*sect_config = (struct section_config_t)default_section_config;
|
||||
section_iter = SECT_ITER_INSIDE;
|
||||
|
||||
break;
|
||||
case OPT_END_SECTION:
|
||||
if (section_iter != SECT_ITER_INSIDE)
|
||||
goto invalid_opt;
|
||||
|
||||
section_iter = SECT_ITER_OUTSIDE;
|
||||
sect_config = &config.default_config;
|
||||
break;
|
||||
|
||||
/* section_config_t scoped configs */
|
||||
case OPT_SNI_DOMAINS:
|
||||
if (!strcmp(optarg, "all")) {
|
||||
sect_config->all_domains = 1;
|
||||
}
|
||||
|
||||
sect_config->domains_str = optarg;
|
||||
sect_config->domains_strlen = strlen(sect_config->domains_str);
|
||||
break;
|
||||
case OPT_EXCLUDE_DOMAINS:
|
||||
sect_config->exclude_domains_str = optarg;
|
||||
sect_config->exclude_domains_strlen = strlen(sect_config->exclude_domains_str);
|
||||
break;
|
||||
case OPT_FRAG:
|
||||
if (strcmp(optarg, "tcp") == 0) {
|
||||
config.fragmentation_strategy = FRAG_STRAT_TCP;
|
||||
sect_config->fragmentation_strategy = FRAG_STRAT_TCP;
|
||||
} else if (strcmp(optarg, "ip") == 0) {
|
||||
config.fragmentation_strategy = FRAG_STRAT_IP;
|
||||
sect_config->fragmentation_strategy = FRAG_STRAT_IP;
|
||||
} else if (strcmp(optarg, "none") == 0) {
|
||||
config.fragmentation_strategy = FRAG_STRAT_NONE;
|
||||
sect_config->fragmentation_strategy = FRAG_STRAT_NONE;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@ -237,9 +274,9 @@ int parse_args(int argc, char *argv[]) {
|
||||
break;
|
||||
case OPT_FRAG_SNI_FAKED:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.frag_sni_faked = 1;
|
||||
sect_config->frag_sni_faked = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.frag_sni_faked = 0;
|
||||
sect_config->frag_sni_faked = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@ -247,9 +284,9 @@ int parse_args(int argc, char *argv[]) {
|
||||
break;
|
||||
case OPT_FRAG_SNI_REVERSE:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.frag_sni_reverse = 1;
|
||||
sect_config->frag_sni_reverse = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.frag_sni_reverse = 0;
|
||||
sect_config->frag_sni_reverse = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@ -257,9 +294,9 @@ int parse_args(int argc, char *argv[]) {
|
||||
break;
|
||||
case OPT_FRAG_MIDDLE_SNI:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.frag_middle_sni = 1;
|
||||
sect_config->frag_middle_sni = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.frag_middle_sni = 0;
|
||||
sect_config->frag_middle_sni = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@ -271,19 +308,19 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.frag_sni_pos = num;
|
||||
sect_config->frag_sni_pos = num;
|
||||
break;
|
||||
case OPT_FAKING_STRATEGY:
|
||||
if (strcmp(optarg, "randseq") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_RAND_SEQ;
|
||||
sect_config->faking_strategy = FAKE_STRAT_RAND_SEQ;
|
||||
} else if (strcmp(optarg, "ttl") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_TTL;
|
||||
sect_config->faking_strategy = FAKE_STRAT_TTL;
|
||||
} else if (strcmp(optarg, "tcp_check") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_TCP_CHECK;
|
||||
sect_config->faking_strategy = FAKE_STRAT_TCP_CHECK;
|
||||
} else if (strcmp(optarg, "pastseq") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_PAST_SEQ;
|
||||
sect_config->faking_strategy = FAKE_STRAT_PAST_SEQ;
|
||||
} else if (strcmp(optarg, "md5sum") == 0) {
|
||||
config.faking_strategy = FAKE_STRAT_TCP_MD5SUM;
|
||||
sect_config->faking_strategy = FAKE_STRAT_TCP_MD5SUM;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@ -295,21 +332,21 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.faking_ttl = num;
|
||||
sect_config->faking_ttl = num;
|
||||
break;
|
||||
case OPT_FAKE_SEQ_OFFSET:
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
if (errno != 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.fakeseq_offset = num;
|
||||
sect_config->fakeseq_offset = num;
|
||||
break;
|
||||
case OPT_FAKE_SNI:
|
||||
if (strcmp(optarg, "1") == 0) {
|
||||
config.fake_sni = 1;
|
||||
sect_config->fake_sni = 1;
|
||||
} else if (strcmp(optarg, "0") == 0) {
|
||||
config.fake_sni = 0;
|
||||
sect_config->fake_sni = 0;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
@ -321,7 +358,44 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.fake_sni_seq_len = num;
|
||||
sect_config->fake_sni_seq_len = num;
|
||||
break;
|
||||
case OPT_FAKE_SNI_TYPE:
|
||||
if (strcmp(optarg, "default") == 0) {
|
||||
sect_config->fake_sni_type = FAKE_PAYLOAD_DEFAULT;
|
||||
} else if (strcmp(optarg, "random") == 0) {
|
||||
sect_config->fake_sni_type = FAKE_PAYLOAD_RANDOM;
|
||||
} else if (strcmp(optarg, "custom") == 0) {
|
||||
sect_config->fake_sni_type = FAKE_PAYLOAD_CUSTOM;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
break;
|
||||
case OPT_FAKE_CUSTOM_PAYLOAD: {
|
||||
uint8_t *const custom_buf = (uint8_t *)custom_fake_buf;
|
||||
|
||||
const char *custom_hex_fake = optarg;
|
||||
size_t custom_hlen = strlen(custom_hex_fake);
|
||||
if ((custom_hlen & 1) == 1) {
|
||||
printf("Custom fake hex should be divisible by two\n");
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
|
||||
size_t custom_len = custom_hlen >> 1;
|
||||
if (custom_len > MAX_FAKE_SIZE) {
|
||||
printf("Custom fake is too large\n");
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
for (int i = 0; i < custom_len; i++) {
|
||||
sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i);
|
||||
}
|
||||
|
||||
sect_config->fake_custom_pkt_sz = custom_len;
|
||||
sect_config->fake_custom_pkt = (char *)custom_buf;
|
||||
}
|
||||
break;
|
||||
case OPT_FK_WINSIZE:
|
||||
num = parse_numeric_option(optarg);
|
||||
@ -329,66 +403,37 @@ int parse_args(int argc, char *argv[]) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.fk_winsize = num;
|
||||
sect_config->fk_winsize = num;
|
||||
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:
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.seg2_delay = num;
|
||||
sect_config->seg2_delay = num;
|
||||
break;
|
||||
case OPT_THREADS:
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0 || num > MAX_THREADS) {
|
||||
case OPT_QUIC_DROP:
|
||||
sect_config->quic_drop = 1;
|
||||
break;
|
||||
case OPT_SNI_DETECTION:
|
||||
if (strcmp(optarg, "parse") == 0) {
|
||||
sect_config->sni_detection = SNI_DETECTION_PARSE;
|
||||
} else if (strcmp(optarg, "brute") == 0) {
|
||||
sect_config->sni_detection = SNI_DETECTION_BRUTE;
|
||||
} else {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.threads = num;
|
||||
break;
|
||||
case OPT_QUEUE_NUM:
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.queue_start_num = num;
|
||||
break;
|
||||
case OPT_PACKET_MARK:
|
||||
num = parse_numeric_option(optarg);
|
||||
if (errno != 0 || num < 0) {
|
||||
goto invalid_opt;
|
||||
}
|
||||
|
||||
config.mark = num;
|
||||
break;
|
||||
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// out:
|
||||
errno = 0;
|
||||
return 0;
|
||||
stop_exec:
|
||||
@ -404,68 +449,6 @@ error:
|
||||
}
|
||||
|
||||
void print_welcome() {
|
||||
switch (config.fragmentation_strategy) {
|
||||
case FRAG_STRAT_TCP:
|
||||
printf("Using TCP segmentation\n");
|
||||
break;
|
||||
case FRAG_STRAT_IP:
|
||||
printf("Using IP fragmentation\n");
|
||||
break;
|
||||
default:
|
||||
printf("SNI fragmentation is disabled\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (config.seg2_delay) {
|
||||
printf("Some outgoing googlevideo request segments will be delayed for %d ms as of seg2_delay define\n", config.seg2_delay);
|
||||
}
|
||||
|
||||
if (config.fake_sni) {
|
||||
printf("Fake SNI will be sent before each target client hello\n");
|
||||
} else {
|
||||
printf("Fake SNI is disabled\n");
|
||||
}
|
||||
|
||||
if (config.frag_sni_reverse) {
|
||||
printf("Fragmentation Client Hello will be reversed\n");
|
||||
}
|
||||
|
||||
if (config.frag_sni_faked) {
|
||||
printf("Fooling packets will be sent near the original Client Hello\n");
|
||||
}
|
||||
|
||||
if (config.fake_sni_seq_len > 1) {
|
||||
printf("Faking sequence of length %d will be built as fake sni\n", config.fake_sni_seq_len);
|
||||
}
|
||||
|
||||
switch (config.faking_strategy) {
|
||||
case FAKE_STRAT_TTL:
|
||||
printf("TTL faking strategy will be used with TTL %d\n", config.faking_ttl);
|
||||
break;
|
||||
case FAKE_STRAT_RAND_SEQ:
|
||||
printf("Random seq faking strategy will be used\n");
|
||||
printf("Fake seq offset set to %u\n", config.fakeseq_offset);
|
||||
break;
|
||||
case FAKE_STRAT_TCP_CHECK:
|
||||
printf("TCP checksum faking strategy will be used\n");
|
||||
break;
|
||||
case FAKE_STRAT_PAST_SEQ:
|
||||
printf("Past seq faking strategy will be used\n");
|
||||
break;
|
||||
case FAKE_STRAT_TCP_MD5SUM:
|
||||
printf("md5sum faking strategy will be used\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (config.fk_winsize) {
|
||||
printf("Response TCP window will be set to %d with the appropriate scale\n", config.fk_winsize);
|
||||
}
|
||||
|
||||
if (config.synfake) {
|
||||
printf("Fake SYN payload will be sent with each TCP request SYN packet\n");
|
||||
}
|
||||
|
||||
|
||||
if (config.use_gso) {
|
||||
printf("GSO is enabled\n");
|
||||
}
|
||||
@ -476,16 +459,87 @@ void print_welcome() {
|
||||
printf("IPv6 is disabled\n");
|
||||
}
|
||||
|
||||
if (config.quic_drop) {
|
||||
printf("All QUIC packets will be dropped\n");
|
||||
}
|
||||
printf("Detected %d config sections\n", config.custom_configs_len + 1);
|
||||
printf("The sections will be processed in ordred they goes in this output");
|
||||
|
||||
if (config.sni_detection == SNI_DETECTION_BRUTE) {
|
||||
printf("Server Name Extension will be parsed in the bruteforce mode\n");
|
||||
}
|
||||
ITER_CONFIG_SECTIONS(section) {
|
||||
int section_number = CONFIG_SECTION_NUMBER(section);
|
||||
printf("Section #%d\n", section_number);
|
||||
|
||||
if (config.all_domains) {
|
||||
printf("All Client Hello will be targetted by youtubeUnblock!\n");
|
||||
}
|
||||
switch (section->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 (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
|
||||
#endif
|
||||
|
||||
#include "raw_replacements.h"
|
||||
|
||||
typedef int (*raw_send_t)(const unsigned char *data, unsigned int data_len);
|
||||
/**
|
||||
* Sends the packet after delay_ms. The function should schedule send and return immediately
|
||||
* (for example, open daemon thread)
|
||||
*/
|
||||
typedef void (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms);
|
||||
typedef int (*delayed_send_t)(const unsigned char *data, unsigned int data_len, unsigned int delay_ms);
|
||||
|
||||
struct instance_config_t {
|
||||
raw_send_t send_raw_packet;
|
||||
@ -18,11 +20,10 @@ struct instance_config_t {
|
||||
};
|
||||
extern struct instance_config_t instance_config;
|
||||
|
||||
struct config_t {
|
||||
unsigned int queue_start_num;
|
||||
int threads;
|
||||
int use_gso;
|
||||
int use_ipv6;
|
||||
struct section_config_t {
|
||||
const char *domains_str;
|
||||
unsigned int domains_strlen;
|
||||
|
||||
int fragmentation_strategy;
|
||||
int frag_sni_reverse;
|
||||
int frag_sni_faked;
|
||||
@ -32,32 +33,97 @@ struct config_t {
|
||||
unsigned char faking_ttl;
|
||||
int fake_sni;
|
||||
unsigned int fake_sni_seq_len;
|
||||
|
||||
#define FAKE_PAYLOAD_RANDOM 0
|
||||
#define FAKE_PAYLOAD_CUSTOM 1
|
||||
// In default mode all other options will be skipped.
|
||||
#define FAKE_PAYLOAD_DEFAULT 2
|
||||
int fake_sni_type;
|
||||
|
||||
int quic_drop;
|
||||
|
||||
/* In milliseconds */
|
||||
unsigned int seg2_delay;
|
||||
int synfake;
|
||||
unsigned int synfake_len;
|
||||
|
||||
const char *exclude_domains_str;
|
||||
unsigned int exclude_domains_strlen;
|
||||
unsigned int all_domains;
|
||||
|
||||
const char *fake_sni_pkt;
|
||||
unsigned int fake_sni_pkt_sz;
|
||||
|
||||
const char *fake_custom_pkt;
|
||||
unsigned int fake_custom_pkt_sz;
|
||||
|
||||
unsigned int fk_winsize;
|
||||
int fakeseq_offset;
|
||||
|
||||
#define SNI_DETECTION_PARSE 0
|
||||
#define SNI_DETECTION_BRUTE 1
|
||||
int sni_detection;
|
||||
|
||||
|
||||
};
|
||||
|
||||
#define MAX_CONFIGLIST_LEN 64
|
||||
|
||||
struct config_t {
|
||||
unsigned int queue_start_num;
|
||||
int threads;
|
||||
int use_gso;
|
||||
int use_ipv6;
|
||||
unsigned int mark;
|
||||
|
||||
#define VERBOSE_INFO 0
|
||||
#define VERBOSE_DEBUG 1
|
||||
#define VERBOSE_TRACE 2
|
||||
int verbose;
|
||||
int quic_drop;
|
||||
#define SNI_DETECTION_PARSE 0
|
||||
#define SNI_DETECTION_BRUTE 1
|
||||
int sni_detection;
|
||||
/* In milliseconds */
|
||||
unsigned int seg2_delay;
|
||||
const char *domains_str;
|
||||
unsigned int domains_strlen;
|
||||
const char *exclude_domains_str;
|
||||
unsigned int exclude_domains_strlen;
|
||||
unsigned int all_domains;
|
||||
const char *fake_sni_pkt;
|
||||
unsigned int fake_sni_pkt_sz;
|
||||
unsigned int fk_winsize;
|
||||
unsigned int fakeseq_offset;
|
||||
unsigned int mark;
|
||||
int synfake;
|
||||
unsigned int synfake_len;
|
||||
|
||||
struct section_config_t default_config;
|
||||
struct section_config_t custom_configs[MAX_CONFIGLIST_LEN];
|
||||
int custom_configs_len;
|
||||
};
|
||||
|
||||
extern struct config_t config;
|
||||
|
||||
#define ITER_CONFIG_SECTIONS(section) \
|
||||
for (struct section_config_t *section = &config.default_config + config.custom_configs_len; section >= &config.default_config; section--)
|
||||
|
||||
#define CONFIG_SECTION_NUMBER(section) (int)((section) - &config.default_config)
|
||||
|
||||
#define default_section_config { \
|
||||
.frag_sni_reverse = 1, \
|
||||
.frag_sni_faked = 0, \
|
||||
.fragmentation_strategy = FRAGMENTATION_STRATEGY, \
|
||||
.faking_strategy = FAKING_STRATEGY, \
|
||||
.faking_ttl = FAKE_TTL, \
|
||||
.fake_sni = 1, \
|
||||
.fake_sni_seq_len = 1, \
|
||||
.fake_sni_type = FAKE_PAYLOAD_DEFAULT, \
|
||||
.frag_middle_sni = 1, \
|
||||
.frag_sni_pos = 1, \
|
||||
.fakeseq_offset = 10000, \
|
||||
.synfake = 0, \
|
||||
.synfake_len = 0, \
|
||||
.quic_drop = 0, \
|
||||
\
|
||||
.seg2_delay = 0, \
|
||||
\
|
||||
.domains_str = defaul_snistr, \
|
||||
.domains_strlen = sizeof(defaul_snistr), \
|
||||
\
|
||||
.exclude_domains_str = "", \
|
||||
.exclude_domains_strlen = 0, \
|
||||
\
|
||||
.fake_sni_pkt = fake_sni_old, \
|
||||
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, \
|
||||
.fake_custom_pkt = custom_fake_buf, \
|
||||
.fake_custom_pkt_sz = 0, \
|
||||
.sni_detection = SNI_DETECTION_PARSE, \
|
||||
}
|
||||
|
||||
#define MAX_THREADS 16
|
||||
|
||||
#ifndef THREADS_NUM
|
||||
@ -89,19 +155,30 @@ extern struct config_t config;
|
||||
#define FAKE_TTL 8
|
||||
|
||||
// Will invalidate fake packets by out-of-ack_seq out-of-seq request
|
||||
#define FAKE_STRAT_RAND_SEQ 1
|
||||
#define FAKE_STRAT_RAND_SEQ (1 << 0)
|
||||
// Will assume that GGC server is located further than FAKE_TTL
|
||||
// Thus, Fake packet will be eliminated automatically.
|
||||
#define FAKE_STRAT_TTL 2
|
||||
#define FAKE_STRAT_PAST_SEQ 3
|
||||
#define FAKE_STRAT_TCP_CHECK 4
|
||||
#define FAKE_STRAT_TCP_MD5SUM 5
|
||||
#define FAKE_STRAT_TTL (1 << 1)
|
||||
#define FAKE_STRAT_PAST_SEQ (1 << 2)
|
||||
#define FAKE_STRAT_TCP_CHECK (1 << 3)
|
||||
#define FAKE_STRAT_TCP_MD5SUM (1 << 4)
|
||||
|
||||
#define FAKE_STRAT_COUNT 5
|
||||
|
||||
/**
|
||||
* This macros iterates through all faking strategies and executes code under it.
|
||||
* destination strategy will be available under name of `strategy` variable.
|
||||
*/
|
||||
#define ITER_FAKE_STRAT(fake_bitmask, strategy) \
|
||||
for (int strategy = 1; strategy <= (1 << FAKE_STRAT_COUNT); strategy <<= 1) \
|
||||
if ((fake_bitmask) & strategy)
|
||||
|
||||
#ifndef FAKING_STRATEGY
|
||||
#define FAKING_STRATEGY FAKE_STRAT_PAST_SEQ
|
||||
#endif
|
||||
|
||||
#define MAX_FAKE_SIZE 1300
|
||||
|
||||
#if !defined(SILENT) && !defined(KERNEL_SPACE)
|
||||
#define DEBUG
|
||||
#endif
|
||||
|
269
kargs.c
269
kargs.c
@ -1,54 +1,27 @@
|
||||
#include "config.h"
|
||||
#include "raw_replacements.h"
|
||||
#include "types.h"
|
||||
#include <linux/moduleparam.h>
|
||||
#include "types.h"
|
||||
|
||||
#define STR_MAXLEN 2048
|
||||
|
||||
static char custom_fake_buf[MAX_FAKE_SIZE];
|
||||
|
||||
struct config_t config = {
|
||||
.frag_sni_reverse = 1,
|
||||
.frag_sni_faked = 0,
|
||||
.fragmentation_strategy = FRAGMENTATION_STRATEGY,
|
||||
.faking_strategy = FAKING_STRATEGY,
|
||||
.faking_ttl = FAKE_TTL,
|
||||
.fake_sni = 1,
|
||||
.fake_sni_seq_len = 1,
|
||||
.frag_middle_sni = 1,
|
||||
.frag_sni_pos = 1,
|
||||
.use_ipv6 = 1,
|
||||
.fakeseq_offset = 10000,
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.synfake = 0,
|
||||
.synfake_len = 0,
|
||||
|
||||
.sni_detection = SNI_DETECTION_PARSE,
|
||||
|
||||
#ifdef SEG2_DELAY
|
||||
.seg2_delay = SEG2_DELAY,
|
||||
#else
|
||||
.seg2_delay = 0,
|
||||
#endif
|
||||
|
||||
#ifdef USE_GSO
|
||||
.use_gso = 1,
|
||||
#else
|
||||
.use_gso = false,
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
.verbose = 2,
|
||||
#else
|
||||
.verbose = 1,
|
||||
#endif
|
||||
|
||||
.domains_str = defaul_snistr,
|
||||
.domains_strlen = sizeof(defaul_snistr),
|
||||
|
||||
.threads = THREADS_NUM,
|
||||
.queue_start_num = DEFAULT_QUEUE_NUM,
|
||||
.fake_sni_pkt = fake_sni_old,
|
||||
.fake_sni_pkt_sz = sizeof(fake_sni_old) - 1, // - 1 for null-terminator
|
||||
.mark = DEFAULT_RAWSOCKET_MARK,
|
||||
.use_ipv6 = 1,
|
||||
|
||||
.verbose = VERBOSE_DEBUG,
|
||||
.use_gso = 1,
|
||||
|
||||
.default_config = default_section_config,
|
||||
.custom_configs_len = 0
|
||||
};
|
||||
|
||||
#define def_section (&config.default_config)
|
||||
|
||||
static int unumeric_set(const char *val, const struct kernel_param *kp) {
|
||||
int n = 0, ret;
|
||||
ret = kstrtoint(val, 10, &n);
|
||||
@ -107,18 +80,19 @@ static const struct kernel_param_ops inverse_boolean_ops = {
|
||||
.get = inverse_boolean_get,
|
||||
};
|
||||
|
||||
module_param_cb(fake_sni, &boolean_parameter_ops, &config.fake_sni, 0664);
|
||||
module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &config.fake_sni_seq_len, 0664);
|
||||
module_param_cb(faking_ttl, &unumeric_parameter_ops, &config.faking_ttl, 0664);
|
||||
module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &config.fakeseq_offset, 0664);
|
||||
module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &config.frag_sni_reverse, 0664);
|
||||
module_param_cb(frag_sni_faked, &boolean_parameter_ops, &config.frag_sni_faked, 0664);
|
||||
module_param_cb(frag_middle_sni, &boolean_parameter_ops, &config.frag_middle_sni, 0664);
|
||||
module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &config.frag_sni_pos, 0664);
|
||||
module_param_cb(fk_winsize, &unumeric_parameter_ops, &config.fk_winsize, 0664);
|
||||
module_param_cb(synfake, &boolean_parameter_ops, &config.synfake, 0664);
|
||||
module_param_cb(synfake_len, &unumeric_parameter_ops, &config.synfake_len, 0664);
|
||||
module_param_cb(fake_sni, &boolean_parameter_ops, &def_section->fake_sni, 0664);
|
||||
module_param_cb(fake_sni_seq_len, &unumeric_parameter_ops, &def_section->fake_sni_seq_len, 0664);
|
||||
module_param_cb(faking_ttl, &unumeric_parameter_ops, &def_section->faking_ttl, 0664);
|
||||
module_param_cb(fake_seq_offset, &unumeric_parameter_ops, &def_section->fakeseq_offset, 0664);
|
||||
module_param_cb(frag_sni_reverse, &unumeric_parameter_ops, &def_section->frag_sni_reverse, 0664);
|
||||
module_param_cb(frag_sni_faked, &boolean_parameter_ops, &def_section->frag_sni_faked, 0664);
|
||||
module_param_cb(frag_middle_sni, &boolean_parameter_ops, &def_section->frag_middle_sni, 0664);
|
||||
module_param_cb(frag_sni_pos, &unumeric_parameter_ops, &def_section->frag_sni_pos, 0664);
|
||||
module_param_cb(fk_winsize, &unumeric_parameter_ops, &def_section->fk_winsize, 0664);
|
||||
module_param_cb(synfake, &boolean_parameter_ops, &def_section->synfake, 0664);
|
||||
module_param_cb(synfake_len, &unumeric_parameter_ops, &def_section->synfake_len, 0664);
|
||||
module_param_cb(packet_mark, &unumeric_parameter_ops, &config.mark, 0664);
|
||||
// module_param_cb(seg2delay, &unumeric_parameter_ops, &def_section->seg2_delay, 0664);
|
||||
|
||||
static int sni_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@ -137,13 +111,13 @@ static int sni_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
ret = param_set_charp(val, kp);
|
||||
|
||||
if (ret < 0) {
|
||||
config.domains_strlen = 0;
|
||||
def_section->domains_strlen = 0;
|
||||
} else {
|
||||
config.domains_strlen = len;
|
||||
def_section->domains_strlen = len;
|
||||
if (len == 3 && !strncmp(val, "all", len)) {
|
||||
config.all_domains = 1;
|
||||
def_section->all_domains = 1;
|
||||
} else {
|
||||
config.all_domains = 0;
|
||||
def_section->all_domains = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +130,7 @@ static const struct kernel_param_ops sni_domains_ops = {
|
||||
.get = param_get_charp,
|
||||
};
|
||||
|
||||
module_param_cb(sni_domains, &sni_domains_ops, &config.domains_str, 0664);
|
||||
module_param_cb(sni_domains, &sni_domains_ops, &def_section->domains_str, 0664);
|
||||
|
||||
static int exclude_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@ -171,9 +145,9 @@ static int exclude_domains_set(const char *val, const struct kernel_param *kp) {
|
||||
ret = param_set_charp(val, kp);
|
||||
|
||||
if (ret < 0) {
|
||||
config.exclude_domains_strlen = 0;
|
||||
def_section->exclude_domains_strlen = 0;
|
||||
} else {
|
||||
config.exclude_domains_strlen = len;
|
||||
def_section->exclude_domains_strlen = len;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -184,36 +158,62 @@ static const struct kernel_param_ops exclude_domains_ops = {
|
||||
.get = param_get_charp,
|
||||
};
|
||||
|
||||
module_param_cb(exclude_domains, &exclude_domains_ops, &config.exclude_domains_str, 0664);
|
||||
module_param_cb(exclude_domains, &exclude_domains_ops, &def_section->exclude_domains_str, 0664);
|
||||
|
||||
module_param_cb(no_ipv6, &inverse_boolean_ops, &config.use_ipv6, 0664);
|
||||
module_param_cb(silent, &inverse_boolean_ops, &config.verbose, 0664);
|
||||
module_param_cb(quic_drop, &boolean_parameter_ops, &config.quic_drop, 0664);
|
||||
module_param_cb(quic_drop, &boolean_parameter_ops, &def_section->quic_drop, 0664);
|
||||
|
||||
static int verbose_trace_set(const char *val, const struct kernel_param *kp) {
|
||||
int n = 0, ret;
|
||||
ret = kstrtoint(val, 10, &n);
|
||||
if (ret != 0 || (n != 0 && n != 1))
|
||||
return -EINVAL;
|
||||
static int verbosity_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
|
||||
if (n) {
|
||||
n = VERBOSE_TRACE;
|
||||
} else {
|
||||
n = VERBOSE_DEBUG;
|
||||
len = strnlen(val, STR_MAXLEN + 1);
|
||||
if (len == STR_MAXLEN + 1) {
|
||||
pr_err("%s: string parameter too long\n", kp->name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (len >= 1 && val[len - 1] == '\n') {
|
||||
len--;
|
||||
}
|
||||
|
||||
if (strncmp(val, "trace", len) == 0) {
|
||||
*(int *)kp->arg = VERBOSE_TRACE;
|
||||
} else if (strncmp(val, "debug", len) == 0) {
|
||||
*(int *)kp->arg = VERBOSE_DEBUG;
|
||||
} else if (strncmp(val, "silent", len) == 0) {
|
||||
*(int *)kp->arg = VERBOSE_INFO;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (kp->arg == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
*(int *)kp->arg = n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops verbose_trace_ops = {
|
||||
.set = verbose_trace_set,
|
||||
.get = param_get_int,
|
||||
|
||||
static int verbosity_get(char *buffer, const struct kernel_param *kp) {
|
||||
switch (*(int *)kp->arg) {
|
||||
case VERBOSE_TRACE:
|
||||
strcpy(buffer, "trace\n");
|
||||
break;
|
||||
case VERBOSE_DEBUG:
|
||||
strcpy(buffer, "debug\n");
|
||||
break;
|
||||
case VERBOSE_INFO:
|
||||
strcpy(buffer, "silent\n");
|
||||
break;
|
||||
default:
|
||||
strcpy(buffer, "unknown\n");
|
||||
}
|
||||
|
||||
return strlen(buffer);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops verbosity_ops = {
|
||||
.set = verbosity_set,
|
||||
.get = verbosity_get,
|
||||
};
|
||||
|
||||
module_param_cb(trace, &verbose_trace_ops, &config.verbose, 0664);
|
||||
module_param_cb(verbosity, &verbosity_ops, &config.verbose, 0664);
|
||||
|
||||
static int frag_strat_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@ -264,7 +264,7 @@ static const struct kernel_param_ops frag_strat_ops = {
|
||||
.get = frag_strat_get,
|
||||
};
|
||||
|
||||
module_param_cb(fragmentation_strategy, &frag_strat_ops, &config.fragmentation_strategy, 0664);
|
||||
module_param_cb(fragmentation_strategy, &frag_strat_ops, &def_section->fragmentation_strategy, 0664);
|
||||
|
||||
static int fake_strat_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@ -325,7 +325,7 @@ static const struct kernel_param_ops fake_strat_ops = {
|
||||
.get = fake_strat_get,
|
||||
};
|
||||
|
||||
module_param_cb(faking_strategy, &fake_strat_ops, &config.faking_strategy, 0664);
|
||||
module_param_cb(faking_strategy, &fake_strat_ops, &def_section->faking_strategy, 0664);
|
||||
|
||||
static int sni_detection_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
@ -371,4 +371,111 @@ static const struct kernel_param_ops sni_detection_ops = {
|
||||
.get = sni_detection_get,
|
||||
};
|
||||
|
||||
module_param_cb(sni_detection, &sni_detection_ops, &config.sni_detection, 0664);
|
||||
module_param_cb(sni_detection, &sni_detection_ops, &def_section->sni_detection, 0664);
|
||||
|
||||
static int fake_type_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
|
||||
len = strnlen(val, STR_MAXLEN + 1);
|
||||
if (len == STR_MAXLEN + 1) {
|
||||
pr_err("%s: string parameter too long\n", kp->name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (len >= 1 && val[len - 1] == '\n') {
|
||||
len--;
|
||||
}
|
||||
|
||||
if (strncmp(val, "default", len) == 0) {
|
||||
*(int *)kp->arg = FAKE_PAYLOAD_DEFAULT;
|
||||
} else if (strncmp(val, "custom", len) == 0) {
|
||||
*(int *)kp->arg = FAKE_PAYLOAD_CUSTOM;
|
||||
} else if (strncmp(val, "random", len) == 0) {
|
||||
*(int *)kp->arg = FAKE_PAYLOAD_RANDOM;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fake_type_get(char *buffer, const struct kernel_param *kp) {
|
||||
switch (*(int *)kp->arg) {
|
||||
case FAKE_PAYLOAD_DEFAULT:
|
||||
strcpy(buffer, "default\n");
|
||||
break;
|
||||
case FAKE_PAYLOAD_RANDOM:
|
||||
strcpy(buffer, "random\n");
|
||||
break;
|
||||
case FAKE_PAYLOAD_CUSTOM:
|
||||
strcpy(buffer, "custom\n");
|
||||
break;
|
||||
default:
|
||||
strcpy(buffer, "unknown\n");
|
||||
}
|
||||
|
||||
return strlen(buffer);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops fake_type_ops = {
|
||||
.set = fake_type_set,
|
||||
.get = fake_type_get,
|
||||
};
|
||||
|
||||
module_param_cb(fake_sni_type, &fake_type_ops, &def_section->fake_sni_type, 0664);
|
||||
|
||||
static int fake_custom_pl_set(const char *val, const struct kernel_param *kp) {
|
||||
size_t len;
|
||||
|
||||
len = strnlen(val, STR_MAXLEN + 1);
|
||||
if (len == STR_MAXLEN + 1) {
|
||||
pr_err("%s: string parameter too long\n", kp->name);
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
if (len >= 1 && val[len - 1] == '\n') {
|
||||
len--;
|
||||
}
|
||||
|
||||
uint8_t *const custom_buf = (uint8_t *)custom_fake_buf;
|
||||
const char *custom_hex_fake = val;
|
||||
size_t custom_hlen = len;
|
||||
|
||||
if ((custom_hlen & 1) == 1) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
size_t custom_len = custom_hlen >> 1;
|
||||
if (custom_len > MAX_FAKE_SIZE) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < custom_len; i++) {
|
||||
sscanf(custom_hex_fake + (i << 1), "%2hhx", custom_buf + i);
|
||||
}
|
||||
|
||||
def_section->fake_custom_pkt_sz = custom_len;
|
||||
def_section->fake_custom_pkt = (char *)custom_buf;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fake_custom_pl_get(char *buffer, const struct kernel_param *kp) {
|
||||
int cflen = def_section->fake_custom_pkt_sz;
|
||||
const uint8_t *cbf_data = def_section->fake_custom_pkt;
|
||||
int bflen = def_section->fake_custom_pkt_sz << 1;
|
||||
|
||||
for (int i = 0; i < cflen; i++) {
|
||||
sprintf(buffer + (i << 1), "%02x", *((unsigned char *)cbf_data + i));
|
||||
}
|
||||
|
||||
return bflen;
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops fake_custom_pl_ops = {
|
||||
.set = fake_custom_pl_set,
|
||||
.get = fake_custom_pl_get,
|
||||
};
|
||||
|
||||
module_param_cb(fake_custom_payload, &fake_custom_pl_ops, &def_section->fake_custom_pkt, 0664);
|
||||
|
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
|
||||
#error "You are trying to compile the kernel module not in the kernel space"
|
||||
#endif
|
||||
|
||||
// Kernel module for youtubeUnblock.
|
||||
// Make with make kmake && sudo iptables -t mangle -D OUTPUT 1 && sudo make kreload && sudo iptables -t mangle -I OUTPUT -p tcp -j YTUNBLOCK
|
||||
// Build with make kmake
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include <linux/netfilter.h>
|
||||
#include <linux/netfilter_ipv4.h>
|
||||
@ -18,12 +20,277 @@
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
#include "logging.h"
|
||||
#include "kmod_utils.h"
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION("0.3.2");
|
||||
MODULE_AUTHOR("Vadim Vetrov <vetrovvd@gmail.com>");
|
||||
MODULE_DESCRIPTION("Linux kernel module for youtube unblock");
|
||||
MODULE_DESCRIPTION("Linux kernel module for youtubeUnblock");
|
||||
|
||||
static struct socket *rawsocket;
|
||||
|
||||
static struct socket *raw6socket;
|
||||
|
||||
static int open_raw_socket(void) {
|
||||
int ret = 0;
|
||||
ret = sock_create(AF_INET, SOCK_RAW, IPPROTO_RAW, &rawsocket);
|
||||
|
||||
if (ret < 0) {
|
||||
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) {
|
||||
int ret;
|
||||
|
48
mangle.h
48
mangle.h
@ -2,38 +2,12 @@
|
||||
#define YU_MANGLE_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* Result of analyze_tls_data function
|
||||
*/
|
||||
struct tls_verdict {
|
||||
int target_sni; /* google video hello packet */
|
||||
int sni_offset; /* offset from start of tcp _payload_ */
|
||||
int sni_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Processes the packet and finds TLS Client Hello information inside it.
|
||||
* data pointer points to start of TLS Message (TCP Payload)
|
||||
*/
|
||||
struct tls_verdict analyze_tls_data(const uint8_t *data, uint32_t dlen);
|
||||
|
||||
|
||||
/**
|
||||
* Generates fake client hello message
|
||||
*/
|
||||
int gen_fake_sni(const void *iph, uint32_t iph_len,
|
||||
const struct tcphdr *tcph, uint32_t tcph_len,
|
||||
uint8_t *buf, uint32_t *buflen);
|
||||
|
||||
/**
|
||||
* Invalidates the raw packet. The function aims to invalid the packet
|
||||
* in such way as it will be accepted by DPI, but dropped by target server
|
||||
*/
|
||||
int fail_packet(uint8_t *payload, uint32_t *plen, uint32_t avail_buflen);
|
||||
#include "tls.h"
|
||||
|
||||
#define PKT_ACCEPT 0
|
||||
#define PKT_DROP 1
|
||||
// Used for section config
|
||||
#define PKT_CONTINUE 2
|
||||
|
||||
/**
|
||||
* Processes the packet and returns verdict.
|
||||
@ -46,28 +20,30 @@ int process_packet(const uint8_t *packet, uint32_t packet_len);
|
||||
* Processe the TCP packet.
|
||||
* Returns verdict.
|
||||
*/
|
||||
int process_tcp_packet(const uint8_t *raw_payload, uint32_t raw_payload_len);
|
||||
int process_tcp_packet(const struct section_config_t *section, const uint8_t *raw_payload, uint32_t raw_payload_len);
|
||||
|
||||
|
||||
/**
|
||||
* Processes the UDP packet.
|
||||
* Returns verdict.
|
||||
*/
|
||||
int process_udp_packet(const uint8_t *pkt, uint32_t pktlen);
|
||||
int process_udp_packet(const struct section_config_t *section, const uint8_t *pkt, uint32_t pktlen);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Sends fake client hello.
|
||||
*/
|
||||
int post_fake_sni(const void *iph, unsigned int iph_len,
|
||||
const struct tcphdr *tcph, unsigned int tcph_len,
|
||||
unsigned char sequence_len);
|
||||
int post_fake_sni(struct fake_type f_type,
|
||||
const void *iph, unsigned int iph_len,
|
||||
const struct tcphdr *tcph, unsigned int tcph_len);
|
||||
|
||||
/**
|
||||
* Splits packet by poses and posts.
|
||||
* Poses are relative to start of TCP payload.
|
||||
* dvs used internally and should be zero.
|
||||
*/
|
||||
int send_tcp_frags(
|
||||
int send_tcp_frags(const struct section_config_t *section,
|
||||
const uint8_t *packet, uint32_t pktlen,
|
||||
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||
|
||||
@ -76,7 +52,7 @@ int send_tcp_frags(
|
||||
* Poses are relative to start of TCP payload.
|
||||
* dvs used internally and should be zero.
|
||||
*/
|
||||
int send_ip4_frags(
|
||||
int send_ip4_frags(const struct section_config_t *section,
|
||||
const uint8_t *packet, uint32_t pktlen,
|
||||
const uint32_t *poses, uint32_t poses_len, uint32_t dvs);
|
||||
#endif /* YU_MANGLE_H */
|
||||
|
84
nf_wrapper.h
84
nf_wrapper.h
@ -1,84 +0,0 @@
|
||||
/**
|
||||
* Thanks https://github.com/NICMx/Jool/blob/5f60dcda5944b01cc43c3be342aad26af8161bcb/include/nat64/mod/common/nf_wrapper.h for mapped kernel versions
|
||||
*/
|
||||
#ifndef _JOOL_MOD_NF_WRAPPER_H
|
||||
#define _JOOL_MOD_NF_WRAPPER_H
|
||||
|
||||
/**
|
||||
* @file
|
||||
* The kernel API is far from static. In particular, the Netfilter packet entry
|
||||
* function keeps changing. nf_hook.c, the file where we declare our packet
|
||||
* entry function, has been quite difficult to read for a while now. It's pretty
|
||||
* amusing, because we don't even use any of the noisy arguments.
|
||||
*
|
||||
* This file declares a usable function header that abstracts away all those
|
||||
* useless arguments.
|
||||
*/
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
/* If this is a Red Hat-based kernel (Red Hat, CentOS, Fedora, etc)... */
|
||||
#ifdef RHEL_RELEASE_CODE
|
||||
|
||||
#if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 2)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
const struct nf_hook_state *state) \
|
||||
|
||||
#elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#else
|
||||
|
||||
#error "Sorry; this version of RHEL is not supported because it's kind of old."
|
||||
|
||||
#endif /* RHEL_RELEASE_CODE >= x */
|
||||
|
||||
|
||||
/* If this NOT a RedHat-based kernel (Ubuntu, Debian, SuSE, etc)... */
|
||||
#else
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
void *priv, \
|
||||
struct sk_buff *skb, \
|
||||
const struct nf_hook_state *state)
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct nf_hook_state *state)
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
const struct nf_hook_ops *ops, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
|
||||
#define NF_CALLBACK(name, skb) unsigned int name( \
|
||||
unsigned int hooknum, \
|
||||
struct sk_buff *skb, \
|
||||
const struct net_device *in, \
|
||||
const struct net_device *out, \
|
||||
int (*okfn)(struct sk_buff *))
|
||||
|
||||
#else
|
||||
#error "Linux < 3.0 isn't supported at all."
|
||||
|
||||
#endif /* LINUX_VERSION_CODE > n */
|
||||
|
||||
#endif /* RHEL or not RHEL */
|
||||
|
||||
#endif /* _JOOL_MOD_NF_WRAPPER_H */
|
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 <stdint.h> // IWYU pragma: export
|
||||
#include <string.h> // IWYU pragma: export
|
||||
#include <stdlib.h> // IWYU pragma: export
|
||||
|
||||
|
||||
#define _NO_GETRANDOM ((__GLIBC__ <= 2 && __GLIBC_MINOR__ < 25))
|
||||
|
||||
#if !_NO_GETRANDOM
|
||||
#include <sys/random.h> // IWYU pragma: export
|
||||
#endif
|
||||
|
||||
#endif /* SPACES */
|
||||
|
||||
@ -99,4 +107,16 @@
|
||||
#define NETBUF_FREE(buf) ;
|
||||
#endif
|
||||
|
||||
static inline int randint(void) {
|
||||
int rnd;
|
||||
|
||||
#ifdef KERNEL_SPACE
|
||||
get_random_bytes(&rnd, sizeof(rnd));
|
||||
#else
|
||||
rnd = random();
|
||||
#endif
|
||||
|
||||
return rnd;
|
||||
}
|
||||
|
||||
#endif /* TYPES_H */
|
||||
|
@ -31,7 +31,7 @@ export CC CCLD LD CFLAGS LDFLAGS LIBNFNETLINK_CFLAGS LIBNFNETLINK_LIBS LIBMNL_CF
|
||||
|
||||
APP:=$(BUILD_DIR)/youtubeUnblock
|
||||
|
||||
SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c
|
||||
SRCS := youtubeUnblock.c mangle.c args.c utils.c quic.c tls.c
|
||||
OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o)
|
||||
|
||||
LIBNFNETLINK := $(DEPSDIR)/lib/libnfnetlink.la
|
||||
|
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 *seg2, uint32_t *s2len) {
|
||||
|
||||
// struct ip6_hdr *hdr6;
|
||||
void *hdr;
|
||||
uint32_t hdr_len;
|
||||
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;
|
||||
s1_hdr->tot_len = htons(s1_dlen);
|
||||
s2_hdr->tot_len = htons(s2_dlen);
|
||||
s1_hdr->id = randint();
|
||||
s2_hdr->id = randint();
|
||||
|
||||
set_ip_checksum(s1_hdr, sizeof(struct iphdr));
|
||||
set_ip_checksum(s2_hdr, sizeof(struct iphdr));
|
||||
} else {
|
||||
struct ip6_hdr *s1_hdr = (void *)seg1;
|
||||
struct ip6_hdr *s2_hdr = (void *)seg2;
|
||||
@ -523,3 +527,162 @@ void z_function(const char *str, int *zbuf, size_t len) {
|
||||
}
|
||||
}
|
||||
|
||||
void shift_data(uint8_t *data, uint32_t dlen, uint32_t delta) {
|
||||
uint8_t *ndptr = data + delta + dlen;
|
||||
uint8_t *dptr = data + dlen;
|
||||
uint8_t *ndlptr = data;
|
||||
for (size_t i = dlen + 1; i > 0; i--) {
|
||||
*ndptr = *dptr;
|
||||
--ndptr, --dptr;
|
||||
}
|
||||
for (size_t i = 0; i < delta; i++) {
|
||||
*ndlptr++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define TCP_MD5SIG_LEN 16
|
||||
#define TCP_MD5SIG_KIND 19
|
||||
struct tcp_md5sig_opt {
|
||||
uint8_t kind;
|
||||
uint8_t len;
|
||||
uint8_t sig[TCP_MD5SIG_LEN];
|
||||
};
|
||||
#define TCP_MD5SIG_OPT_LEN (sizeof(struct tcp_md5sig_opt))
|
||||
// Real length of the option, with NOOP fillers
|
||||
#define TCP_MD5SIG_OPT_RLEN 20
|
||||
|
||||
int fail_packet(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen) {
|
||||
void *iph;
|
||||
uint32_t iph_len;
|
||||
struct tcphdr *tcph;
|
||||
uint32_t tcph_len;
|
||||
uint8_t *data;
|
||||
uint32_t dlen;
|
||||
int ret;
|
||||
|
||||
ret = tcp_payload_split(payload, *plen,
|
||||
&iph, &iph_len, &tcph, &tcph_len,
|
||||
&data, &dlen);
|
||||
|
||||
uint32_t ipxv = netproto_version(payload, *plen);
|
||||
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
if (strategy.strategy == FAKE_STRAT_RAND_SEQ) {
|
||||
lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
|
||||
|
||||
tcph->seq = htonl(ntohl(tcph->seq) - (strategy.randseq_offset + dlen));
|
||||
|
||||
lgtrace_addp("%u", ntohl(tcph->seq));
|
||||
} else if (strategy.strategy == FAKE_STRAT_PAST_SEQ) {
|
||||
lgtrace("fake seq: %u -> ", ntohl(tcph->seq));
|
||||
tcph->seq = htonl(ntohl(tcph->seq) - dlen);
|
||||
lgtrace_addp("%u", ntohl(tcph->seq));
|
||||
|
||||
} else if (strategy.strategy == FAKE_STRAT_TTL) {
|
||||
lgtrace_addp("set fake ttl to %d", strategy.faking_ttl);
|
||||
|
||||
if (ipxv == IP4VERSION) {
|
||||
((struct iphdr *)iph)->ttl = strategy.faking_ttl;
|
||||
} else if (ipxv == IP6VERSION) {
|
||||
((struct ip6_hdr *)iph)->ip6_hops = strategy.faking_ttl;
|
||||
} else {
|
||||
lgerror("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
|
||||
|
||||
#include "types.h"
|
||||
#include "config.h"
|
||||
|
||||
#define IP4VERSION 4
|
||||
#define IP6VERSION 6
|
||||
@ -101,4 +102,92 @@ int set_tcp_checksum(struct tcphdr *tcph, void *iph, uint32_t iphb_len);
|
||||
|
||||
void z_function(const char *str, int *zbuf, size_t len);
|
||||
|
||||
/**
|
||||
* Shifts data left delta bytes. Fills delta buffer with zeroes.
|
||||
*/
|
||||
void shift_data(uint8_t *data, uint32_t dlen, uint32_t delta);
|
||||
|
||||
|
||||
struct failing_strategy {
|
||||
unsigned int strategy;
|
||||
uint8_t faking_ttl;
|
||||
uint32_t randseq_offset;
|
||||
};
|
||||
|
||||
|
||||
struct fake_type {
|
||||
|
||||
#define FAKE_PAYLOAD_RANDOM 0
|
||||
#define FAKE_PAYLOAD_DATA 1
|
||||
// In default mode all other options will be skipped.
|
||||
#define FAKE_PAYLOAD_DEFAULT 2
|
||||
int type;
|
||||
|
||||
// Length of the final fake message.
|
||||
// Pass 0 in RANDOM mode to make it random
|
||||
uint16_t fake_len;
|
||||
|
||||
// Payload of the fake message of fake_len length.
|
||||
// Will be omitted in RANDOM mode.
|
||||
const char *fake_data;
|
||||
|
||||
unsigned int sequence_len;
|
||||
|
||||
// If non-0 the packet send will be delayed for n milliseconds
|
||||
unsigned int seg2delay;
|
||||
|
||||
// faking strategy of the fake packet.
|
||||
// Does not support bitmask, pass standalone strategy.
|
||||
// Pass 0 if you don't want any faking procedures.
|
||||
struct failing_strategy strategy;
|
||||
};
|
||||
|
||||
/**
|
||||
* Invalidates the raw packet. The function aims to invalid the packet
|
||||
* in such way as it will be accepted by DPI, but dropped by target server
|
||||
*
|
||||
* Does not support bitmask, pass standalone strategy.
|
||||
*/
|
||||
int fail_packet(struct failing_strategy strategy, uint8_t *payload, uint32_t *plen, uint32_t avail_buflen);
|
||||
|
||||
/**
|
||||
* Shifts the payload right and pushes zeroes before it. Useful for TCP TLS faking.
|
||||
*/
|
||||
int seqovl_packet(uint8_t *payload, uint32_t *plen, uint32_t seq_delta);
|
||||
|
||||
|
||||
|
||||
static inline struct failing_strategy args_default_failing_strategy(const struct section_config_t *section) {
|
||||
struct failing_strategy fl_strat = {
|
||||
.strategy = (unsigned int)section->faking_strategy,
|
||||
.faking_ttl = section->faking_ttl,
|
||||
.randseq_offset = (uint32_t)section->fakeseq_offset
|
||||
};
|
||||
return fl_strat;
|
||||
}
|
||||
|
||||
static inline struct fake_type args_default_fake_type(const struct section_config_t *section) {
|
||||
struct fake_type f_type = {
|
||||
.sequence_len = section->fake_sni_seq_len,
|
||||
.strategy = args_default_failing_strategy(section),
|
||||
};
|
||||
|
||||
switch (section->fake_sni_type) {
|
||||
case FAKE_PAYLOAD_RANDOM:
|
||||
f_type.type = FAKE_PAYLOAD_RANDOM;
|
||||
break;
|
||||
case FAKE_PAYLOAD_CUSTOM:
|
||||
f_type.type = FAKE_PAYLOAD_CUSTOM;
|
||||
f_type.fake_data = section->fake_custom_pkt;
|
||||
f_type.fake_len = section->fake_custom_pkt_sz;
|
||||
break;
|
||||
default:
|
||||
f_type.type = FAKE_PAYLOAD_CUSTOM;
|
||||
f_type.fake_data = section->fake_sni_pkt;
|
||||
f_type.fake_len = section->fake_sni_pkt_sz;
|
||||
}
|
||||
|
||||
return f_type;
|
||||
}
|
||||
|
||||
#endif /* UTILS_H */
|
||||
|
@ -264,27 +264,11 @@ static int send_raw_socket(const uint8_t *pkt, uint32_t pktlen) {
|
||||
uint8_t buff2[MNL_SOCKET_BUFFER_SIZE];
|
||||
uint32_t buff2_size = MNL_SOCKET_BUFFER_SIZE;
|
||||
|
||||
switch (config.fragmentation_strategy) {
|
||||
case FRAG_STRAT_TCP:
|
||||
if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
if ((ret = tcp_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
case FRAG_STRAT_IP:
|
||||
if ((ret = ip4_frag(pkt, pktlen, AVAILABLE_MTU-128,
|
||||
buff1, &buff1_size, buff2, &buff2_size)) < 0) {
|
||||
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
printf("send_raw_socket: Packet is too big but fragmentation is disabled!\n");
|
||||
return -EINVAL;
|
||||
errno = -ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (ipvx == IP4VERSION)
|
||||
return send_raw_ipv4(pkt, pktlen);
|
||||
else if (ipvx == IP6VERSION)
|
||||
return send_raw_ipv6(pkt, pktlen);
|
||||
if (ipvx == IP4VERSION) {
|
||||
ret = send_raw_ipv4(pkt, pktlen);
|
||||
} else if (ipvx == IP6VERSION) {
|
||||
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);
|
||||
return -EINVAL;
|
||||
lgtrace_addp("raw_sock_send: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct packet_data {
|
||||
uint32_t id;
|
||||
uint16_t hw_proto;
|
||||
@ -375,7 +361,7 @@ void *delay_packet_send_fn(void *data) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
|
||||
int delay_packet_send(const unsigned char *data, unsigned int data_len, unsigned int delay_ms) {
|
||||
struct dps_t *dpdt = malloc(sizeof(struct dps_t));
|
||||
dpdt->pkt = malloc(data_len);
|
||||
memcpy(dpdt->pkt, data, data_len);
|
||||
@ -384,6 +370,9 @@ void delay_packet_send(const unsigned char *data, unsigned int data_len, unsigne
|
||||
pthread_t thr;
|
||||
pthread_create(&thr, NULL, delay_packet_send_fn, dpdt);
|
||||
pthread_detach(thr);
|
||||
lgtrace_addp("Scheduled packet send after %d ms", delay_ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int queue_cb(const struct nlmsghdr *nlh, void *data) {
|
||||
|
Loading…
Reference in New Issue
Block a user