Template
1
0
mirror of https://github.com/bol-van/zapret2.git synced 2026-03-13 22:03:09 +00:00
This commit is contained in:
bol-van
2025-11-21 22:49:11 +03:00
commit ecdaed3dcc
117 changed files with 26779 additions and 0 deletions

5
.gitattributes vendored Normal file
View File

@@ -0,0 +1,5 @@
* text=auto eol=lf
*.cmd eol=crlf
*.bat eol=crlf
init.d/windivert.filter.examples/** eol=crlf
files/** binary

569
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,569 @@
name: build
run-name: ${{ startsWith(github.ref, 'refs/tags/v') && format('Release {0}', github.ref_name) || null }}
on:
workflow_dispatch:
push:
tags:
- v[0-9]+*
# branches:
# - master
# paths:
# - 'ip2net/**'
# - 'mdig/**'
# - 'nfq2/**'
jobs:
build-linux:
name: Linux ${{ matrix.arch }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- arch: arm64
tool: aarch64-unknown-linux-musl
- arch: arm
tool: arm-unknown-linux-musleabi
# - arch: armhf
# tool: arm-unknown-linux-musleabihf
# - arch: armv7
# tool: armv7-unknown-linux-musleabi
# - arch: armv7hf
# tool: armv7-unknown-linux-musleabihf
# - arch: mips64el
# tool: mips64el-unknown-linux-musl
- arch: mips64
tool: mips64-unknown-linux-musl
# - arch: mipsel
# tool: mipsel-unknown-linux-musl
- arch: mipselsf
tool: mipsel-unknown-linux-muslsf
# - arch: mips
# tool: mips-unknown-linux-musl
- arch: mipssf
tool: mips-unknown-linux-muslsf
# - arch: ppc64
# tool: powerpc64-unknown-linux-musl
- arch: ppc
tool: powerpc-unknown-linux-musl
- arch: x86
tool: i586-unknown-linux-musl
- arch: x86_64
tool: x86_64-unknown-linux-musl
- arch: lexra
tool: mips-linux
dir: rsdk-4.6.4-5281-EB-3.10-0.9.33-m32ub-20141001
env:
CFLAGS: '-march=5281'
LDFLAGS: '-lgcc_eh'
repo: 'bol-van/build'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: zapret2
- name: Set up build tools
env:
ARCH: ${{ matrix.arch }}
TOOL: ${{ matrix.tool }}
REPO: ${{ matrix.arch == 'lexra' && matrix.repo || 'spvkgn/musl-cross' }}
DIR: ${{ matrix.arch == 'lexra' && matrix.dir || matrix.tool }}
run: |
sudo dpkg --add-architecture i386
sudo apt update -qq
if [[ "$ARCH" == lexra ]]; then
sudo apt install -y libcap-dev libc6:i386 zlib1g:i386
URL=https://github.com/$REPO/raw/refs/heads/master/$DIR.txz
else
# luajit buildvm requires 32 bit executable on host platform for 32 bit cross targets
sudo apt install -y libcap-dev libc6-dev gcc-multilib
URL=https://github.com/$REPO/releases/download/latest/$TOOL.tar.xz
fi
mkdir -p $HOME/tools
wget -qO- $URL | tar -C $HOME/tools -xJ || exit 1
[[ -d "$HOME/tools/$DIR/bin" ]] && echo "$HOME/tools/$DIR/bin" >> $GITHUB_PATH
- name: Build
env:
ARCH: ${{ matrix.arch }}
TARGET: ${{ matrix.tool }}
CFLAGS: ${{ matrix.env.CFLAGS != '' && matrix.env.CFLAGS || null }}
LDFLAGS: ${{ matrix.env.LDFLAGS != '' && matrix.env.LDFLAGS || null }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LUA_VER: 5.4
LUA_RELEASE: 5.4.8
LUAJIT_VER: 2.1
LUAJIT_RELEASE: 2.1-20250826
LUAJIT_LUAVER: 5.1
run: |
DEPS_DIR=$GITHUB_WORKSPACE/deps
export CC="$TARGET-gcc"
export LD=$TARGET-ld
export AR=$TARGET-ar
export NM=$TARGET-nm
export STRIP=$TARGET-strip
export PKG_CONFIG_PATH=$DEPS_DIR/lib/pkgconfig
export STAGING_DIR=$RUNNER_TEMP
if [[ "$ARCH" == lexra ]] || [[ "$ARCH" == ppc ]]; then
# use classic lua
wget -qO- https://www.lua.org/ftp/lua-${LUA_RELEASE}.tar.gz | tar -xz
(
cd lua-${LUA_RELEASE}
make CC=$CC CFLAGS="-Os -flto=auto $CFLAGS" linux -j$(nproc)
make install INSTALL_TOP=$DEPS_DIR INSTALL_BIN=$DEPS_DIR/bin INSTALL_INC=$DEPS_DIR/include/lua${LUA_VER} INSTALL_LIB=$DEPS_DIR/lib
)
LJIT=0
LCFLAGS="-I${DEPS_DIR}/include/lua${LUA_VER}"
LLIB="-L${DEPS_DIR}/lib -llua"
else
# luajit
wget -qO- https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz | tar -xz
case "$ARCH" in
*64*)
HOSTCC="cc"
;;
*)
HOSTCC="cc -m32"
esac
(
cd luajit2-*
make BUILDMODE=static HOST_CC="$HOSTCC" CROSS= CC="$CC" TARGET_AR="$AR rcus" TARGET_STRIP=$STRIP CFLAGS="-Os -s -flto=auto $CFLAGS" -j$(nproc)
make install PREFIX= DESTDIR=$DEPS_DIR
)
LJIT=1
LCFLAGS="-I${DEPS_DIR}/include/luajit-${LUAJIT_VER}"
LLIB="-L${DEPS_DIR}/lib -lluajit-${LUAJIT_LUAVER}"
fi
# netfilter libs
wget -qO- https://www.netfilter.org/pub/libnfnetlink/libnfnetlink-1.0.2.tar.bz2 | tar -xj
wget -qO- https://www.netfilter.org/pub/libmnl/libmnl-1.0.5.tar.bz2 | tar -xj
wget -qO- https://www.netfilter.org/pub/libnetfilter_queue/libnetfilter_queue-1.0.5.tar.bz2 | tar -xj
for i in libmnl libnfnetlink libnetfilter_queue ; do
(
cd $i-*
CFLAGS="-Os -flto=auto $CFLAGS" \
./configure --prefix= --host=$TARGET --enable-static --disable-shared --disable-dependency-tracking
make install -j$(nproc) DESTDIR=$DEPS_DIR
)
sed -i "s|^prefix=.*|prefix=$DEPS_DIR|g" $DEPS_DIR/lib/pkgconfig/$i.pc
done
# zlib
gh api repos/madler/zlib/releases/latest --jq '.tag_name' |\
xargs -I{} wget -qO- https://github.com/madler/zlib/archive/refs/tags/{}.tar.gz | tar -xz
(
cd zlib-*
CFLAGS="-Os -flto=auto $CFLAGS" \
./configure --prefix= --static
make install -j$(nproc) DESTDIR=$DEPS_DIR
)
# headers
# wget https://git.alpinelinux.org/aports/plain/main/bsd-compat-headers/queue.h && \
# wget https://git.kernel.org/pub/scm/libs/libcap/libcap.git/plain/libcap/include/sys/capability.h && \
install -Dm644 -t $DEPS_DIR/include/sys /usr/include/x86_64-linux-gnu/sys/queue.h /usr/include/sys/capability.h
# zapret2
CFLAGS="-DZAPRET_GH_VER=${{ github.ref_name }} -DZAPRET_GH_HASH=${{ github.sha }} -static-libgcc -static -I$DEPS_DIR/include $CFLAGS" \
LDFLAGS="-L$DEPS_DIR/lib $LDFLAGS" \
make -C zapret2 LUA_JIT=$LJIT LUA_CFLAGS="$LCFLAGS" LUA_LIB="$LLIB" -j$(nproc)
tar -C zapret2/binaries/my -cJf zapret2-linux-$ARCH.tar.xz .
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: zapret2-linux-${{ matrix.arch }}
path: zapret2-*.tar.xz
if-no-files-found: error
build-android:
name: Android ${{ matrix.abi }}
runs-on: ubuntu-latest
strategy:
matrix:
include:
- abi: armeabi-v7a
target: armv7a-linux-androideabi
- abi: arm64-v8a
target: aarch64-linux-android
- abi: x86
target: i686-linux-android
- abi: x86_64
target: x86_64-linux-android
steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: zapret2
- name: Set up build tools
run: |
sudo dpkg --add-architecture i386
sudo apt update -qq
# luajit buildvm requires 32 bit executable on host platform for 32 bit cross targets
sudo apt install -y gcc-multilib
- name: Build
env:
ABI: ${{ matrix.abi }}
API: 21
TARGET: ${{ matrix.target }}
GH_TOKEN: ${{ github.token }}
LUAJIT_VER: 2.1
LUAJIT_RELEASE: 2.1-20250826
LUAJIT_LUAVER: 5.1
run: |
DEPS_DIR=$GITHUB_WORKSPACE/deps
export TOOLCHAIN=$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64
export CC="$TOOLCHAIN/bin/clang --target=$TARGET$API"
export AR=$TOOLCHAIN/bin/llvm-ar
export AS=$CC
export LD=$TOOLCHAIN/bin/ld
export RANLIB=$TOOLCHAIN/bin/llvm-ranlib
export STRIP=$TOOLCHAIN/bin/llvm-strip
export PKG_CONFIG_PATH=$DEPS_DIR/lib/pkgconfig
# luajit
wget -qO- https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz | tar -xz
case "$ABI" in
*64*)
HOSTCC="cc"
;;
*)
HOSTCC="cc -m32"
esac
(
cd luajit2-*
make BUILDMODE=static HOST_CC="$HOSTCC" CROSS= CC="$CC" TARGET_AR="$AR rcus" TARGET_STRIP=$STRIP CFLAGS="-Os -flto=auto $CFLAGS" -j$(nproc)
make install PREFIX= DESTDIR=$DEPS_DIR
)
LJIT=1
LCFLAGS="-I${DEPS_DIR}/include/luajit-${LUAJIT_VER}"
LLIB="-L${DEPS_DIR}/lib -lluajit-${LUAJIT_LUAVER}"
# netfilter libs
wget -qO- https://www.netfilter.org/pub/libnfnetlink/libnfnetlink-1.0.2.tar.bz2 | tar -xj
wget -qO- https://www.netfilter.org/pub/libmnl/libmnl-1.0.5.tar.bz2 | tar -xj
wget -qO- https://www.netfilter.org/pub/libnetfilter_queue/libnetfilter_queue-1.0.5.tar.bz2 | tar -xj
patch -p1 -d libnetfilter_queue-* -i ../zapret2/.github/workflows/libnetfilter_queue-android.patch
for i in libmnl libnfnetlink libnetfilter_queue ; do
(
cd $i-*
CFLAGS="-Os -flto=auto -Wno-implicit-function-declaration" \
./configure --prefix= --host=$TARGET --enable-static --disable-shared --disable-dependency-tracking
make install -j$(nproc) DESTDIR=$DEPS_DIR
)
sed -i "s|^prefix=.*|prefix=$DEPS_DIR|g" $DEPS_DIR/lib/pkgconfig/$i.pc
done
# zapret2
CFLAGS="-DZAPRET_GH_VER=${{ github.ref_name }} -DZAPRET_GH_HASH=${{ github.sha }} -I$DEPS_DIR/include" \
LDFLAGS="-L$DEPS_DIR/lib" \
make -C zapret2 LUA_JIT=$LJIT LUA_CFLAGS="$LCFLAGS" LUA_LIB="$LLIB" -j$(nproc) android
# strip unwanted ELF sections to prevent warnings on old Android versions
gh api repos/termux/termux-elf-cleaner/releases/latest --jq '.tag_name' |\
xargs -I{} wget -O elf-cleaner https://github.com/termux/termux-elf-cleaner/releases/download/{}/termux-elf-cleaner
chmod +x elf-cleaner
./elf-cleaner --api-level $API zapret2/binaries/my/*
zip zapret2-android-$ABI.zip -j zapret2/binaries/my/*
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: zapret2-android-${{ matrix.abi }}
path: zapret2-*.zip
if-no-files-found: error
build-freebsd:
name: FreeBSD ${{ matrix.arch }}
runs-on: ubuntu-latest
env:
DEPS_DIR: /workdir/deps
LUAJIT_VER: 2.1
LUAJIT_RELEASE: 2.1-20250826
LUAJIT_LUAVER: 5.1
strategy:
matrix:
include:
- target: x86_64
arch: x86_64
# - target: i386
# arch: x86
container:
image: empterdose/freebsd-cross-build:11.4
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install packages
run: apk add xz tar build-base
- name: Build luajit
env:
TARGET: ${{ matrix.target }}
ARCH: ${{ matrix.arch }}
CC: ${{ matrix.target }}-freebsd11-clang
run: |
wget -qO- https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz | tar -xz
(
cd luajit2-*
make BUILDMODE=static HOST_CC=gcc CC=$CC CFLAGS="-Os -flto=auto $CFLAGS"
make install PREFIX= DESTDIR=$DEPS_DIR
)
- name: Build zapret2
env:
TARGET: ${{ matrix.target }}
ARCH: ${{ matrix.arch }}
CC: ${{ matrix.target }}-freebsd11-clang
LJIT: 1
run: |
export CFLAGS="-DZAPRET_GH_VER=${{ github.ref_name }} -DZAPRET_GH_HASH=${{ github.sha }}"
LCFLAGS="-I${DEPS_DIR}/include/luajit-${LUAJIT_VER}"
LLIB="-L${DEPS_DIR}/lib -lluajit-${LUAJIT_LUAVER}"
settarget $TARGET-freebsd11
make CC=$CC LUA_JIT=$LJIT LUA_CFLAGS="$LCFLAGS" LUA_LIB="$LLIB" bsd -j$(nproc)
tar -C binaries/my -cJf zapret2-freebsd-$ARCH.tar.xz .
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: zapret2-freebsd-${{ matrix.arch }}
path: zapret2-*.tar.xz
if-no-files-found: error
build-windows:
name: Windows ${{ matrix.arch }}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
arch: [ x86_64, x86 ]
steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: zapret2
- name: Set up MinGW
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.arch == 'x86_64' && 'MINGW64' || 'MINGW32' }}
install: >-
${{ matrix.arch == 'x86_64' && 'mingw-w64-x86_64-toolchain' || 'mingw-w64-i686-toolchain' }}
- name: Build ip2net, mdig
shell: msys2 {0}
run: |
export CFLAGS="-DZAPRET_GH_VER=${{ github.ref_name }} -DZAPRET_GH_HASH=${{ github.sha }}"
mkdir -p output
cd zapret2
mingw32-make -C ip2net win
mingw32-make -C mdig win
cp -a {ip2net/ip2net,mdig/mdig}.exe ../output
- name: Restore psmisc from cache
id: cache-restore-psmisc
uses: actions/cache/restore@v4
with:
path: ${{ github.workspace }}/psmisc
key: psmisc-${{ matrix.arch }}
- name: Set up Cygwin
env:
PACKAGES: ${{ steps.cache-restore-psmisc.outputs.cache-hit != 'true' && 'cygport gettext-devel libiconv-devel libncurses-devel' || null }}
uses: cygwin/cygwin-install-action@v4
with:
platform: ${{ matrix.arch }}
site: ${{ matrix.arch == 'x86_64' && 'http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215' || null }}
check-sig: ${{ matrix.arch == 'x86_64' && 'false' || null }}
packages: >-
gcc-core
make
zlib-devel
zip
unzip
wget
${{ env.PACKAGES }}
- name: Build psmisc
if: steps.cache-restore-psmisc.outputs.cache-hit != 'true'
env:
URL: https://mirrors.kernel.org/sourceware/cygwin/x86_64/release/psmisc
shell: C:\cygwin\bin\bash.exe -eo pipefail '{0}'
run: >-
export MAKEFLAGS=-j$(nproc) &&
mkdir -p psmisc && cd psmisc &&
wget -qO- ${URL} | grep -Po 'href=\"\Kpsmisc-(\d+\.)+\d+.+src\.tar\.xz(?=\")' | xargs -I{} wget -O- ${URL}/{} | tar -xJ &&
cd psmisc-*.src &&
echo CYGCONF_ARGS+=\" --disable-dependency-tracking --disable-nls\" >> psmisc.cygport &&
cygport psmisc.cygport prep compile install
- name: Save psmisc to cache
if: steps.cache-restore-psmisc.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ${{ github.workspace }}/psmisc
key: psmisc-${{ matrix.arch }}
- name: Build luajit
env:
LUAJIT_RELEASE: 2.1-20250826
shell: C:\cygwin\bin\bash.exe -eo pipefail '{0}'
run: >-
export MAKEFLAGS=-j$(nproc) &&
wget -q https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz &&
tar -xzf v${LUAJIT_RELEASE}.tar.gz &&
rm -f v${LUAJIT_RELEASE}.tar.gz &&
make -C luajit2-${LUAJIT_RELEASE} BUILDMODE=static CFLAGS="-Os -s" &&
make -C luajit2-${LUAJIT_RELEASE} install
- name: Build winws
env:
TARGET: ${{ matrix.arch == 'x86_64' && 'cygwin' || 'cygwin32' }}
shell: C:\cygwin\bin\bash.exe -eo pipefail '{0}'
run: >-
export MAKEFLAGS=-j$(nproc) &&
export CFLAGS="-DZAPRET_GH_VER=${{ github.ref_name }} -DZAPRET_GH_HASH=${{ github.sha }}" &&
cd zapret2 &&
make -C nfq2 ${TARGET} &&
cp -a nfq2/winws2.exe ../output
- name: Create zip
env:
BITS: ${{ matrix.arch == 'x86_64' && '64' || '32' }}
DIR: ${{ matrix.arch == 'x86_64' && 'x64' || 'x86' }}
shell: C:\cygwin\bin\bash.exe -e '{0}'
run: >-
cp -a -t output psmisc/psmisc-*.src/psmisc-*/inst/usr/bin/killall.exe /usr/bin/cygwin1.dll &&
wget -O WinDivert.zip https://github.com/basil00/WinDivert/releases/download/v2.2.2/WinDivert-2.2.2-A.zip &&
unzip -j WinDivert.zip "*/${DIR}/WinDivert.dll" "*/${DIR}/WinDivert${BITS}.sys" -d output &&
zip zapret2-win-${{ matrix.arch }}.zip -j output/*
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: zapret2-win-${{ matrix.arch }}
path: zapret2-*.zip
if-no-files-found: error
release:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: [ build-linux, build-windows, build-freebsd, build-android ]
permissions:
contents: write
runs-on: ubuntu-latest
env:
repo_dir: zapret2-${{ github.ref_name }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
path: ${{ env.repo_dir }}
- name: Download artifacts
uses: actions/download-artifact@v4
id: bins
with:
path: ${{ env.repo_dir }}/binaries
pattern: zapret2-*
- name: Install upx
uses: crazy-max/ghaction-upx@v3
with:
install-only: true
version: v4.2.4
- name: Prepare binaries
shell: bash
run: |
cd ${{ steps.bins.outputs.download-path }}
run_upx() {
upx --best --lzma $@ || true
}
run_dir() {
for f in $dir/* ; do
# extract binaries
case $f in
*.tar.xz )
tar -C $dir -xvf $f && rm $f
if [[ $dir =~ linux ]] && [[ $dir != *-linux-mips64 ]] && [[ $dir != *-linux-lexra ]]; then
run_upx $dir/*
fi
;;
*.zip )
unzip $f -d $dir && rm $f
if [[ $dir =~ win ]]; then
chmod -x $dir/*
fi
;;
esac
done
mv $dir $1
}
for dir in * ; do
if [ -d $dir ]; then
echo "Processing $dir"
case $dir in
*-android-arm64-v8a ) run_dir android-arm64 ;;
*-android-armeabi-v7a ) run_dir android-arm ;;
*-android-x86 ) run_dir android-x86 ;;
*-android-x86_64 ) run_dir android-x86_64 ;;
*-freebsd-x86_64 ) run_dir freebsd-x86_64 ;;
*-linux-arm ) run_dir linux-arm ;;
*-linux-arm64 ) run_dir linux-arm64 ;;
*-linux-mips64 ) run_dir linux-mips64 ;;
*-linux-mipselsf ) run_dir linux-mipsel ;;
*-linux-mipssf ) run_dir linux-mips ;;
*-linux-ppc ) run_dir linux-ppc ;;
*-linux-x86 ) run_dir linux-x86 ;;
*-linux-x86_64 ) run_dir linux-x86_64 ;;
*-linux-lexra ) run_dir linux-lexra ;;
*-win-x86 ) run_dir windows-x86 ;;
*-win-x86_64 ) run_dir windows-x86_64 ;;
esac
fi
done
ls -lhR
- name: Create release bundles
run: |
rm -rf ${{ env.repo_dir }}/.git*
find ${{ env.repo_dir }}/binaries -type f -exec sha256sum {} \; >sha256sum.txt
tar --owner=0 --group=0 -czf ${{ env.repo_dir }}.tar.gz ${{ env.repo_dir }}
zip -qr ${{ env.repo_dir }}.zip ${{ env.repo_dir }}
(
cd ${{ env.repo_dir }}
rm -rf init.d/{openrc,pfsense,runit,s6,systemd} \
nfq2 ip2net mdig docs Makefile
)
tar --owner=0 --group=0 -czf ${{ env.repo_dir }}-openwrt-embedded.tar.gz ${{ env.repo_dir }}
- name: Upload release assets
uses: softprops/action-gh-release@v2
with:
fail_on_unmatched_files: true
prerelease: false
draft: false
body: |
### zapret2 ${{ github.ref_name }}
files: |
zapret2*.tar.gz
zapret2*.zip
sha256sum.txt

View File

@@ -0,0 +1,41 @@
--- a/src/extra/pktbuff.c
+++ b/src/extra/pktbuff.c
@@ -14,7 +14,7 @@
#include <string.h> /* for memcpy */
#include <stdbool.h>
-#include <netinet/if_ether.h>
+#include <linux/if_ether.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
--- a/src/nlmsg.c
+++ b/src/nlmsg.c
@@ -21,7 +21,7 @@
#include <linux/netfilter/nfnetlink_queue.h>
-#include <libnetfilter_queue/libnetfilter_queue.h>
+// #include <libnetfilter_queue/libnetfilter_queue.h>
#include "internal.h"
--- a/src/extra/tcp.c
+++ b/src/extra/tcp.c
@@ -139,12 +139,16 @@ void nfq_tcp_compute_checksum_ipv6(struc
* (union is compatible to any of its members)
* This means this part of the code is -fstrict-aliasing safe now.
*/
+#ifndef __ANDROID__
union tcp_word_hdr {
struct tcphdr hdr;
uint32_t words[5];
};
+#endif
+#ifndef tcp_flag_word
#define tcp_flag_word(tp) ( ((union tcp_word_hdr *)(tp))->words[3])
+#endif
/**
* nfq_pkt_snprintf_tcp_hdr - print tcp header into one buffer in a humnan

13
.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
/config
ip2net/ip2net
mdig/mdig
nfq2/dvtws2
nfq2/nfqws2
nfq2/winws2.exe
nfq2/WinDivert*
tpws/tpws
binaries/my/
ipset/zapret-ip*.txt
ipset/zapret-ip*.gz
ipset/zapret-hosts*.txt
ipset/zapret-hosts*.gz

60
Makefile Normal file
View File

@@ -0,0 +1,60 @@
DIRS := nfq2 ip2net mdig
TGT := binaries/my
all: clean
@mkdir -p "$(TGT)"; \
for dir in $(DIRS); do \
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
$(MAKE) -C "$$dir" || exit; \
for exe in "$$dir/"*; do \
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
mv -f "$$exe" "${TGT}" ; \
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
fi \
done \
done
systemd: clean
@mkdir -p "$(TGT)"; \
for dir in $(DIRS); do \
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
$(MAKE) -C "$$dir" systemd || exit; \
for exe in "$$dir/"*; do \
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
mv -f "$$exe" "${TGT}" ; \
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
fi \
done \
done
android: clean
@mkdir -p "$(TGT)"; \
for dir in $(DIRS); do \
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
$(MAKE) -C "$$dir" android || exit; \
for exe in "$$dir/"*; do \
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
mv -f "$$exe" "${TGT}" ; \
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
fi \
done \
done
bsd: clean
@mkdir -p "$(TGT)"; \
for dir in $(DIRS); do \
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
$(MAKE) -C "$$dir" bsd || exit; \
for exe in "$$dir/"*; do \
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
mv -f "$$exe" "${TGT}" ; \
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
fi \
done \
done
clean:
@[ -d "$(TGT)" ] && rm -rf "$(TGT)" ; \
for dir in $(DIRS); do \
$(MAKE) -C "$$dir" clean; \
done

451
common/base.sh Normal file
View File

@@ -0,0 +1,451 @@
which()
{
# on some systems 'which' command is considered deprecated and not installed by default
# 'command -v' replacement does not work exactly the same way. it outputs shell aliases if present
# $1 - executable name
local IFS=:
for p in $PATH; do
[ -x "$p/$1" ] && {
echo "$p/$1"
return 0
}
done
return 1
}
exists()
{
which "$1" >/dev/null 2>/dev/null
}
existf()
{
type "$1" >/dev/null 2>/dev/null
}
whichq()
{
which $1 2>/dev/null
}
exist_all()
{
while [ -n "$1" ]; do
exists "$1" || return 1
shift
done
return 0
}
on_off_function()
{
# $1 : function name on
# $2 : function name off
# $3 : 0 - off, 1 - on
local F="$1"
[ "$3" = "1" ] || F="$2"
shift
shift
shift
"$F" "$@"
}
contains()
{
# check if substring $2 contains in $1
[ "${1#*$2}" != "$1" ]
}
starts_with()
{
# $1 : what
# $2 : starts with
case "$1" in
"$2"*)
return 0
;;
esac
return 1
}
extract_arg()
{
# $1 - arg number
# $2,$3,... - args
local n=$1
while [ -n "$1" ]; do
shift
[ $n -eq 1 ] && { echo "$1"; return 0; }
n=$(($n-1))
done
return 1
}
find_str_in_list()
{
# $1 - string
# $2 - space separated values
local v
[ -n "$1" ] && {
for v in $2; do
[ "$v" = "$1" ] && return 0
done
}
return 1
}
end_with_newline()
{
local c="$(tail -c 1)"
[ "$c" = "" ]
}
trim()
{
awk '{gsub(/^ +| +$/,"")}1'
}
split_by_separator()
{
# $1 - string
# $2 - separator
# $3 - var name to get "before" part
# $4 - var name to get "after" part
local before="${1%%$2*}"
local after="${1#*$2}"
[ "$after" = "$1" ] && after=
[ -n "$3" ] && eval $3="\$before"
[ -n "$4" ] && eval $4="\$after"
}
dir_is_not_empty()
{
# $1 - directory
local n
[ -d "$1" ] || return 1
n=$(ls "$1" | wc -c | xargs)
[ "$n" != 0 ]
}
append_separator_list()
{
# $1 - var name to receive result
# $2 - separator
# $3 - quoter
# $4,$5,... - elements
local _var="$1" sep="$2" quo="$3" i
eval i="\$$_var"
shift; shift; shift
while [ -n "$1" ]; do
if [ -n "$i" ] ; then
i="$i$sep$quo$1$quo"
else
i="$quo$1$quo"
fi
shift
done
eval $_var="\$i"
}
make_separator_list()
{
eval $1=''
append_separator_list "$@"
}
make_comma_list()
{
# $1 - var name to receive result
# $2,$3,... - elements
local var="$1"
shift
make_separator_list $var , '' "$@"
}
make_quoted_comma_list()
{
# $1 - var name to receive result
# $2,$3,... - elements
local var="$1"
shift
make_separator_list $var , '"' "$@"
}
unique()
{
local i
for i in "$@"; do echo $i; done | sort -u | xargs
}
is_linked_to_busybox()
{
local IFS F P
IFS=:
for path in $PATH; do
F=$path/$1
P="$(readlink $F)"
if [ -z "$P" ] && [ -x $F ] && [ ! -L $F ]; then return 1; fi
[ "${P%busybox*}" != "$P" ] && return
done
}
get_dir_inode()
{
local dir="$1"
[ -L "$dir" ] && dir=$(readlink "$dir")
ls -id "$dir" | awk '{print $1}'
}
linux_min_version()
{
# $1 - major ver
# $2 - minor ver
local V1=$(sed -nre 's/^Linux version ([0-9]+)\.[0-9]+.*$/\1/p' /proc/version)
local V2=$(sed -nre 's/^Linux version [0-9]+\.([0-9]+).*$/\1/p' /proc/version)
[ -n "$V1" -a -n "$V2" ] && [ "$V1" -gt "$1" -o "$V1" -eq "$1" -a "$V2" -ge "$2" ]
}
linux_get_subsys()
{
local INIT="$(sed 's/\x0/\n/g' /proc/1/cmdline | head -n 1)"
[ -L "$INIT" ] && INIT=$(readlink "$INIT")
INIT="$(basename "$INIT")"
if [ -f "/etc/openwrt_release" ] && [ "$INIT" = "procd" ] ; then
SUBSYS=openwrt
elif [ -x "/bin/ndm" ] ; then
SUBSYS=keenetic
else
# generic linux
SUBSYS=
fi
}
openwrt_fw3()
{
[ ! -x /sbin/fw4 -a -x /sbin/fw3 ]
}
openwrt_fw4()
{
[ -x /sbin/fw4 ]
}
openwrt_fw3_integration()
{
[ "$FWTYPE" = iptables ] && openwrt_fw3
}
create_dev_stdin()
{
[ -e /dev/stdin ] || ln -s /proc/self/fd/0 /dev/stdin
}
call_for_multiple_items()
{
# $1 - function to get an item
# $2 - variable name to put result into
# $3 - space separated parameters to function $1
local i item items
for i in $3; do
$1 item $i
[ -n "$item" ] && {
if [ -n "$items" ]; then
items="$items $item"
else
items="$item"
fi
}
done
eval $2=\"$items\"
}
fix_sbin_path()
{
local IFS=':'
printf "%s\n" $PATH | grep -Fxq '/usr/sbin' || PATH="/usr/sbin:$PATH"
printf "%s\n" $PATH | grep -Fxq '/sbin' || PATH="/sbin:$PATH"
export PATH
}
# it can calculate floating point expr
calc()
{
LC_ALL=C awk "BEGIN { print $*}";
}
fsleep_setup()
{
[ -n "$FSLEEP" ] || {
if sleep 0.001 2>/dev/null; then
FSLEEP=1
elif busybox usleep 1 2>/dev/null; then
FSLEEP=2
else
local errtext="$(read -t 0.001 2>&1)"
if [ -z "$errtext" ]; then
FSLEEP=3
# newer openwrt has ucode with system function that supports timeout in ms
elif ucode -e "system(['sleep','1'], 1)" 2>/dev/null; then
FSLEEP=4
# older openwrt may have lua and nixio lua module
elif lua -e 'require "nixio".nanosleep(0,1)' 2>/dev/null ; then
FSLEEP=5
else
FSLEEP=0
fi
fi
}
}
msleep()
{
# $1 - milliseconds
case "$FSLEEP" in
1)
sleep $(calc $1/1000)
;;
2)
busybox usleep $(calc $1*1000)
;;
3)
read -t $(calc $1/1000)
;;
4)
ucode -e "system(['sleep','2147483647'], $1)"
;;
5)
lua -e "require 'nixio'.nanosleep($(($1/1000)),$(calc $1%1000*1000000))"
;;
*)
sleep $((($1+999)/1000))
esac
}
minsleep()
{
msleep 100
}
replace_char()
{
local a="$1"
local b="$2"
shift; shift
echo "$@" | tr "$a" "$b"
}
replace_str()
{
local a=$(echo "$1" | sed 's/\//\\\//g')
local b=$(echo "$2" | sed 's/\//\\\//g')
shift; shift
echo "$@" | sed "s/$a/$b/g"
}
setup_md5()
{
[ -n "$MD5" ] && return
MD5=md5sum
exists $MD5 || MD5=md5
}
md5f()
{
setup_md5
$MD5 | cut -d ' ' -f1
}
setup_random()
{
[ -n "$RCUT" ] && return
RCUT="cut -c 1-17"
# some shells can operate with 32 bit signed int
[ $((0x100000000)) = 0 ] && RCUT="cut -c 1-9"
}
random()
{
# $1 - min, $2 - max
local r rs
setup_random
if [ -c /dev/urandom ]; then
read rs </dev/urandom
else
rs="$RANDOM$RANDOM$(date)"
fi
# shells use signed int64
r=1$(echo $rs | md5f | sed 's/[^0-9]//g' | $RCUT)
echo $(( ($r % ($2-$1+1)) + $1 ))
}
shell_name()
{
[ -n "$SHELL_NAME" ] || {
[ -n "$UNAME" ] || UNAME="$(uname)"
if [ "$UNAME" = "Linux" ]; then
SHELL_NAME="$(readlink /proc/$$/exe)"
SHELL_NAME="$(basename "$SHELL_NAME")"
else
SHELL_NAME=$(ps -p $$ -o comm=)
fi
[ -n "$SHELL_NAME" ] || SHELL_NAME="$(basename "$SHELL")"
}
}
process_exists()
{
if exists pgrep; then
pgrep ^$1$ >/dev/null
elif exists pidof; then
pidof $1 >/dev/null
else
return 1
fi
}
win_process_exists()
{
tasklist /NH /FI "IMAGENAME eq ${1}.exe" | grep -q "^${1}.exe"
}
alloc_num()
{
# $1 - source var name
# $2 - target var name
# $3 - min
# $4 - max
local v
eval v="\$$2"
# do not replace existing value
[ -n "$v" ] && return
eval v="\$$1"
[ -n "$v" ] || v=$3
eval $2="$v"
v=$((v + 1))
[ $v -gt $4 ] && v=$3
eval $1="$v"
}
std_ports()
{
NFQWS2_PORTS_TCP_IPT=$(replace_char - : $NFQWS_PORTS_TCP)
NFQWS2_PORTS_TCP_KEEPALIVE_IPT=$(replace_char - : $NFQWS_PORTS_TCP_KEEPALIVE)
NFQWS2_PORTS_UDP_IPT=$(replace_char - : $NFQWS_PORTS_UDP)
NFQWS2_PORTS_UDP_KEEPALIVE_IPT=$(replace_char - : $NFQWS_PORTS_UDP_KEEPALIVE)
}
has_bad_ws_options()
{
# $1 - nfqws2 opts
contains "$1" "--ipset" && {
echo
echo "WARNING !!! --ipset parameter is present"
echo "It's OK if you only specialize already redirected traffic and also process the rest."
echo "If you redirect port X to process several IPs from the list and do nothing with the rest - IT'S VERY INEFFECTIVE !"
echo "Kernel ipsets should be used instead. Write custom scripts and filter IPs in kernel."
echo
}
return 1
}
check_bad_ws_options()
{
# $1 - 0 = stop, 1 = start
# $2 - nfqws options
if [ "$1" = 1 ] && has_bad_ws_options "$2"; then
echo "!!! REFUSING TO USE BAD OPTIONS : $2"
help_bad_ws_options
return 1
else
return 0
fi
}
help_bad_ws_options()
{
echo "WARNING ! BAD options detected"
}

21
docs/LICENSE.txt Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016-2025 bol-van
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,107 @@
How to compile native programs for use in openwrt
-------------------------------------------------
1) Install required packages to the host system :
debian,ubuntu : apt install build-essential patch libncurses-dev python3-distutils unzip gawk wget git
fedora: dnf install make patch gcc g++ ncurses-devel git perl
Other packages may be required on your distribution. Look for the errors.
2) Download latest SDK for your target platform from https://downloads.openwrt.org
examples :
curl -o - https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz | tar -Jxv
cd openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64
curl -o - https://downloads.openwrt.org/snapshots/targets/x86/64/openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst | tar --zstd -xv
cd openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64
3) Install required libs
./scripts/feeds update base packages
./scripts/feeds install libnetfilter-queue zlib libcap luajit
4) If you need static build edit `feeds/packages/lang/luajit/Makefile`
change BUILDMODE from "dynamic" to "mixed"
add '$(CP) $(PKG_INSTALL_DIR)/usr/lib/*a $(1)/usr/lib/'
after '$(CP) $(PKG_INSTALL_DIR)/usr/lib/*so* $(1)/usr/lib/'
should look like :
define Build/Compile
$(MAKE) $(PKG_JOBS) -C $(PKG_BUILD_DIR) \
HOST_CC="$(HOSTCC) $(HOST_CFLAGS) $(HOST_BITS)" \
CROSS="$(TARGET_CROSS)" \
DPREFIX=$(PKG_INSTALL_DIR)/usr \
PREFIX=/usr \
TARGET_SYS=Linux \
TARGET_CFLAGS="$(TARGET_CFLAGS)" \
BUILDMODE=mixed
rm -rf $(PKG_INSTALL_DIR)
mkdir -p $(PKG_INSTALL_DIR)
$(MAKE) -C $(PKG_BUILD_DIR) \
DPREFIX=$(PKG_INSTALL_DIR)/usr \
PREFIX=/usr \
TARGET_SYS=Linux \
install
endef
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include/luajit-2.1
$(CP) $(PKG_INSTALL_DIR)/usr/include/luajit-2.1/*.{h,hpp} $(1)/usr/include/luajit-2.1
$(INSTALL_DIR) $(1)/usr/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/*so* $(1)/usr/lib/
$(CP) $(PKG_INSTALL_DIR)/usr/lib/*a $(1)/usr/lib/
$(INSTALL_DIR) $(1)/usr/lib/pkgconfig
$(CP) $(PKG_INSTALL_DIR)/usr/lib/pkgconfig/luajit.pc $(1)/usr/lib/pkgconfig/
$(CP) $(PKG_INSTALL_DIR)/usr/bin/luajit-$(PKG_VERSION) $(PKG_INSTALL_DIR)/usr/bin/$(PKG_NAME)
endef
5) Prepare openwrt package definitions
cp -R /opt/zapret2/docs/compile/openwrt/. .
cp -R /opt/zapret2/nfq2 package/zapret/nfqws2
cp -R /opt/zapret2/mdig package/zapret/mdig
cp -R /opt/zapret2/ip2net package/zapret/ip2net
6) Prepare .config
make defconfig
If you only need bins without packages comment 'CONFIG_AUTOREMOVE=y' line in .config
Change 'y' to 'n' in Config.in
should look like :
config AUTOREMOVE
bool "Automatic removal of build directories"
default n
and in Config-build.in
should look like :
config AUTOREMOVE
bool
default n
7) Compile
dynamic build : make package/{nfqws2,mdig,ip2net}/compile
static build : make CFLAGS=-static LDFLAGS=-lgcc_eh package/{nfqws2,mdig,ip2net}/compile
8) Get result
executables only : build_dir/target/<progname>
ipk or apk packages : bin/packages/*/base
9) Installing to openwrt to use with zapret
zapret with or without binaries should be already installed in /opt/zapret2.
Install ipk's or apk's with all compiled progs using opkg or apk.
Bins are placed to /opt/zapret2/binaries/my.
Or copy binaries there manually and set chmod 755 to them.
Run install_bin.sh or install_easy.sh. They will use bins in 'my' folder.

View File

@@ -0,0 +1,16 @@
debian,ubuntu :
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev libmnl-dev libsystemd-dev libluajit2-5.1-dev
make -C /opt/zapret2 systemd
FreeBSD :
pkg search luajit-2
# see what's the version available
pkg install luajit-2.1.0.20250728
make -C /opt/zapret2
OpenBSD :
pkg_add luajit gmake bsd
gmake -C /opt/zapret2

View File

@@ -0,0 +1,36 @@
Windows x64
1) Download latest cygwin for windows 7
curl -O https://www.cygwin.com/setup-x86_64.exe
setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215
2) During setup install packages : make gcc-core zlib-devel
3) Run Cygwin.bat
4) install and compile luajit from here : https://github.com/openresty/luajit2
download latest releast, unpack, cd to it's directory
make BUILDMODE=static CFLAGS="-Os"
make install
5) cd to %ZAPRET_BASE%/nfq
cd C:/Users/user/Downloads/zapret2/nfq
6) Compile nfqws2
make cygwin64
use winws2.exe
7) Take windivert.dll and windivert64.sys here : https://reqrypt.org/download
Choose version 2.2.2 for Windows 10 and 2.2.0 for Windows 7.
8) Copy cygwin1.dll, winws2.exe, windivert.dll and windivert64.sys to one folder.
9) Run winws2.exe from cmd.exe running as administrator.
winws will not run from cygwin shell with cygwin1.dll copy in it's folder.
winws will not run without cygwin1.dll outside of cygwin shell.

View File

@@ -0,0 +1,32 @@
#
include $(TOPDIR)/rules.mk
PKG_NAME:=ip2net
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/ip2net
SECTION:=net
CATEGORY:=Network
TITLE:=ip2net
SUBMENU:=Zapret
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./ip2net/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
endef
define Package/ip2net/install
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ip2net $(1)/opt/zapret/binaries/my
endef
$(eval $(call BuildPackage,ip2net))

View File

@@ -0,0 +1 @@
Copy "ip2net" folder here !

View File

@@ -0,0 +1,32 @@
#
include $(TOPDIR)/rules.mk
PKG_NAME:=mdig
PKG_RELEASE:=1
include $(INCLUDE_DIR)/package.mk
define Package/mdig
SECTION:=net
CATEGORY:=Network
TITLE:=mdig
SUBMENU:=Zapret
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./mdig/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS)
endef
define Package/mdig/install
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/mdig $(1)/opt/zapret/binaries/my
endef
$(eval $(call BuildPackage,mdig))

View File

@@ -0,0 +1 @@
Copy "mdig" folder here !

View File

@@ -0,0 +1,46 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=nfqws2
PKG_RELEASE:=1
LUA_JIT?=1
ifeq ($(LUA_JIT),1)
LUAJIT_VER?=2.1
LUA_VER?=5.1
LUA_DEP:=luajit
#LUA_DEP:=luajit2
LUA_INCLUDE:=-I$(STAGING_DIR)/usr/include/luajit-$(LUAJIT_VER)
LUA_LIBRARY:=-L$(STAGING_DIR)/usr/lib -lluajit-$(LUA_VER)
else
LUA_VER?=5.3
LUA_DEP:=lua$(LUA_VER)
LUA_INCLUDE:=-I$(STAGING_DIR)/usr/include/lua$(LUA_VER)
LUA_LIBRARY:=-L$(STAGING_DIR)/usr/lib -llua$(LUA_VER)
endif
include $(INCLUDE_DIR)/package.mk
define Package/nfqws2
SECTION:=net
CATEGORY:=Network
TITLE:=nfqws2
SUBMENU:=Zapret
DEPENDS:=+libnetfilter-queue +lmnl +libcap +zlib +$(LUA_DEP)
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) ./nfq/* $(PKG_BUILD_DIR)/
endef
define Build/Compile
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) LUA_CFLAGS="$(LUA_INCLUDE)" LUA_LIB="$(LUA_LIBRARY)"
endef
define Package/nfqws2/install
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my
$(INSTALL_BIN) $(PKG_BUILD_DIR)/nfqws2 $(1)/opt/zapret/binaries/my
endef
$(eval $(call BuildPackage,nfqws2))

View File

@@ -0,0 +1 @@
Copy "nfq" folder here !

290
docs/readme.md Normal file
View File

@@ -0,0 +1,290 @@
# zapret2 v0.1
## Зачем это нужно
Автономное средство противодействия DPI, которое не требует подключения каких-либо сторонних серверов. Может помочь
обойти блокировки или замедление сайтов HTTP(S), сигнатурный анализ TCP и UDP протоколов, например, с целью блокировки
VPN. Может использоваться для частичной прозрачной обфускации протоколов.
Проект нацелен прежде всего на маломощные embedded устройства - роутеры, работающие под OpenWrt. Поддерживаются
традиционные Linux-системы, FreeBSD, OpenBSD, Windows. В некоторых случаях возможна самостоятельная прикрутка
решения к различным прошивкам.
## Чем это отличается от zapret1
zapret2 является дальнейшим развитием проекта zapret.
Проблема его основной части *nfqws1* в том, что он перегружен опциями и в условиях нарастающего противостояния регулятора и пользователей
не обеспечивает достаточную гибкость воздействия на трафик.
Обход DPI требует все более тонких и специфических воздействий, которые меняются со временем, а старые перестают работать.
Стратегии - это программы, управляющие сценарием атаки на DPI. В *nfqws1* они зашиваются в C код. Написание C кода - занятие нелегкое,
требующее достаточной квалификации разработчика и времени.
Цель *nfqws2* - сделать так, чтобы программы стратегий мог написать любой человек, владеющий знаниями в области сетей, понимающий уязвимости DPI
или хотя бы область , в которой их можно искать, плюс владеющий базовыми навыками программирования.
*nfqws2* оставляет в себе практически тот же функционал - распознавание протоколов, реассемблинг, дешифровка, управление профилями, хостлисты, ipset-ы, базовая фильтрация.
Но он полностью лишается возможностей самостоятельно воздействовать на трафик. Часть "дурения" переносится в скриптовой язык программирования LUA.
LUA код получает от C кода структурированное представление приходящих пакетов в виде дерева (диссекты), подобного тем, что вы видите в wireshark.
Туда же приходят результаты сборки или дешифровки частей некоторых протоколов (tls, quic).
С код предоставляет функции-хелперы, позволяющие отсылать пакеты, работать с двоичными данными, разбирать TLS, искать маркер-позции и т.д.
Имеется библиотека хелперов, написанных на LUA, а так же готовая библиотека программ атаки на DPI (стратегий), реализующая функции *nfqws1* в расширенном варианте
и с большей гибкостью.
Вы всегда сможете взять и дописать что-то свое. В этом и есть смысл, чтобы борьбой с DPI смог заняться любой, кто разбирается в пакетах.
Мог "потыкать" его, проверить свои идеи. А потом поделиться с друзьями своим решением "одного клика".
zapret2 - инструмент для таких энтузиастов. Но это не готовое решение для чайников. Проект не ставит себе целью сделать все простым для всех.
Автор считает, что это невозможно в принципе по обьективным причинам.
## С чего начать
Хотелось бы избежать "талмуда" на главной странице. Поэтому начнем со способа запуска *nfqws2* и описания способов портирования стратегий *nfqws1* - как в *nfqws2* сделать то же самое, что можно было в *nfqws1*.
Когда вы поймете как это работает, вы можете посмотреть LUA код, находящийся "под капотом". Разобрать как он работает, попробовать написать что-то свое.
"талмуд" обязательно будет, как он есть у любых более-менее сложных проектов. Он нужен как справочник.
### Механика обработки трафика
Изначально сетевой трафик в любой ОС появляется в ядре. Первая задача - извлечь его оттуда и перенаправить на процесс *nfqws2*.
Эта задача решается в Linux с помощью iptables и nftables, в BSD - ipfw и pf, в Windows - windivert.
Процесс перенаправления трафика из ядра отнимает достаточно много ресурсов, поэтому лучше всего отфильтровать как можно больше прямо в нем.
Для экспериментов на Linux можно начать со следующих nftables, которые перенаправят начальные пакеты соединений на порты tcp 80,443 и udp 443 в очередь NFQUEUE с номером 200.
```
nft delete table inet ztest
nft create table inet ztest
nft add chain inet ztest post "{type filter hook postrouting priority 101;}"
nft add rule inet ztest post meta mark and 0x40000000 == 0 tcp dport "{80,443}" ct original packets 1-12 queue num 200 bypass
nft add rule inet ztest post meta mark and 0x40000000 == 0 udp dport "{443}" ct original packets 1-12 queue num 200 bypass
sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1
nft add chain inet ztest pre "{type filter hook prerouting priority -101;}"
nft add rule inet ztest pre meta mark and 0x40000000 == 0 tcp sport "{80,443}" ct reply packets 1-12 queue num 200 bypass
nft add rule inet ztest pre meta mark and 0x40000000 == 0 udp sport "{443}" ct reply packets 1-12 queue num 200 bypass
nft add chain inet ztest predefrag "{type filter hook output priority -401;}"
nft add rule inet ztest predefrag "mark & 0x40000000 != 0x00000000 notrack"
```
В windows функция перехвата вшита прямо в код движка для windows, который называется *winws2*. Он использует драйвер windivert.
Для перехвата портов целиком используются параметры `--wf-tcp-in`, `--wf-tcp-out`, `--wf-udp-in`, `--wf-udp-out`.
Они относятся к протоколам tcp или udp, к входящим или исходящим пакетам. Например, `--wf-tcp-out=80,443`.
Для более точного перехвата пишутся фильтры на языке фильтров windivert. Он похож на язык фильтров tcpdump или wireshark.
Фильтры отдаются *winws2* в параметрах `--wf-raw-part`. Конструктор фильтров обьединяет все указанные опции перехвата в
единый raw фильтр и запускает перехват windivert.
К сожалению, самый болезненный недостаток windivert (а так же BSD ipfw и pf) - отсутствие ограничителя на номер пакета в соединении (connbytes в iptables, ct packets в nftables).
windivert вообще не отслеживает соединения. Поэтому если перехватывать порт целиком, то все соединение по указанному направлению
пойдет на перехват, что нелегко для процессора, если там передаются многие мегабайты.
Поэтому по возможности пишите собственные фильтры windivert, проверяющие тип пейлоада хотя бы частично. Дофильтрацию может выполнить *winws2*.
Дальше под рутом нужно запустить *nfqws2* с параметрами командной строки. Они строятся примерно следующим образом :
```
nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua \
--filter-tcp=80,443 --filter-l7=tls,http \
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
--payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 \
--payload=tls_client_hello,http_req --lua-desync=multisplit:pos=1:seqovl=5:seqovl_pattern=0x1603030000
```
Данный пример предполагает, что в той же директории находятся файлы `zapret-lib.lua` - библиотека хелперов на LUA и `zapret-antidpi.lua` - библиотека базовых стратегий.
`--lua-init` может содержать LUA код в виде строки. Так удобно писать простой код, например присвоить константу переменной, чтобы не создавать файлы ради этой мелочи.
Либо подцепляется файл, если значение параметра начинается с `@`. Код из `--lua-init` выполняется 1 раз при старте.
Далее указаны параметры `--lua-desync`. Они содержат имя LUA функции, вызываемой при обработке каждого пакета, проходящего через профиль мульистратегии.
После двоеточия и через двоеточия следуют параметры для данной функции в формате `param[=value]`. В примере реализована стратегия
```
nfqws --qnum 200 --debug \
--filter-tcp=80,443 --filter-l7=tls,http \
--dpi-desync=fake,multisplit --dpi-desync-fooling=md5sig --dpi-desync-split-pos=1,midsld \
--dpi-desync-split-seqovl=5 --dpi-desync-split-seqovl-pattern=0x1603030000 \
--dpi-desync-fake-tls-mod=rnd,rndsni,dupsid
```
Что сразу заметно - это наличие понятия "payload". В *nfqws1* были только протоколы соединения, которые участвовали в фильтрации профилей.
Они так же остались в *nfqws2*, но введено другое понятие - тип пейлоада. Пейлоад - это содержание текущего пакета.
Тип пейлоада - тип данных, содержащихся в пакете или группе пакетов. Например, протокол соединения может быть tls, а пейлоады - tls_client_hello, tls_server_hello, unknown.
Другое важное отличие - отсутствие жестко определенных фаз десинхронизации. То, что вы раньше писали как `fake,multisplit` реализуется двумя
последовательно вызываемыми LUA функциями. Их может быть столько, сколько нужно, учитывая логику прохождения пакетов и операций с ними, и у каждой могут быть свои параметры.
Может даже несколько раз вызываться одна и так же функция с разными параметрами. Так, например, можно послать несколько фейков, причем с разными фулингами.
Конкретный вызов `--lua-desync` функции называется инстансом. Инстанс - это связка имени функции, номера вызова внутри профиля и номера самого профиля.
Это похоже на одну программу, которую можно запустить много раз с разными параметрами.
Другое немаловажное отличие - поддержка автоматической tcp сегментации. Вам больше не нужно думать о размерах отсылаемых tcp пакетов.
По каждому соединению отслеживается MSS. Если пакет не влезает в MSS, выполняется сегментация.
Например, это может случиться при отправке tls фейка с kyber. Или если вы режете kyber tls так, что одна из частей получается размером 1600 байт,
что, очевидно, не влезает в MTU. Или если вы задали seqovl=10000. В *nfqws1* такое значение вызвало бы ошибку. В *nfqws2* будет отправлено
несколько tcp сегментов с начальным sequence -10000 общим размером 10000 байт, в последнем из которых будет кусок оригинального сообщения.
В *nfqws2* нет жестко зашитых параметров кастомных фейков типа `--dpi-desync-fake-tls`, `dpi-desync-fake-http` и тд.
Вместо них есть блобы. Блоб (blob) - это переменная LUA типа *string*, содержащая блок двоичных данных произвольной длины. От 1 байта до гигабайтов.
*nfqws2* автоматически инициализирует блобы со стандартными фейками tls, http, quic, как это и было в *nfqws1*.
Блобы могут быть заданы как hex-строка прямо в параметре desync функции, либо пред-загружены при старте с помощью параметра `--blob=name:0xHEX|[+ofs]@filename`
Что касается профилей мультистратегии и хостлистов , то они остались практически в неизменном виде. За исключением одной тонкости автолиста.
Теперь профиль с автолистом берет на себя только те соединения, для которых уже известен хост. Пока хоста нет - они проходят мимо.
В *nfqws1* они падали на профиль с автолистом.
```
nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua \
--filter-tcp=80 --filter-l7=http --hostlist=mylist1.txt --lua-desync=multisplit --new \
--filter-tcp=80 --filter-l7=http --hostlist-exclude=mylist2.txt --lua-desync=fake:blob=0x00000000:ip_ttl=5:ip6_ttl=3 --lua-desync=multidisorder:pos=5,endhost-1 --new \
--filter-tcp=443 --filter-l7=tls --hostlist=mylist1.txt --lua-desync=multidisorder
```
Параметры *nfqws1* start/cutoff (`--dpi-desync-start`, `--dpi-desync-cutoff`, ...) теперь называются диапазонами (ranges).
Остались только 2 range : `--in-range` и `--out-range`. Они относятся к входящему и исходящему направлению соответственно.
Да, теперь можно полноценно работать как с входящими пакетами, так и с исходящими. Есть и специальный режим для сервера - `--server`, который
адаптирует интерпретацию IP адресов и портов источника/приемника, чтобы корректно работали ipset-ы и фильтры.
range задается как `mX-mY`, `mX<mY`, `-mY`, `<mY`, `mX-`.
Буква `m` означает режим счетчика. `n` - номер пакета, `d` - номер пакета с данными, `b` - количество переданных байт, `s` - относительный sequence для tcp.
После буквы режима пишется число. Например, `n5-s10000` означает с 5-го по очереди пакета до смещения 10000 байт относительно начала tcp соединения.
Есть и режимы, не требущие числа - `a` - всегда, `x` - никогда.
Если разделителем указан знак '-' - конечная позиция включительна, а если '<' - не включительна.
Установка по умолчанию `--in-range=x --out-range=a --payload all`. То есть отсекаются все входящие пакеты и берутся все исходящие.
`--in-range`, `--out-range` и `--payload` фильтры могут присутствовать множественно в одном профиле.
Их действие распространяется на все последующие `--lua-desync` функции до следующего фильтра того же типа или до конца профиля.
Следующий профиль снова принимает значения по умолчанию.
Что будет, если вы не напишите фильтр `--payload` для fake или multisplit ? В *nfqws1* без `--dpi-desync-any-protocol` они работали только по известным пейлоадам.
В *nfqws2* "any protocol" - режим по умолчанию. Однако, функции из библиотеки `zapret-antidpi.lua` написаны так, что по умолчанию работают только по известные пейлоадам
и не работают по пустым пакетам или unknown - точно так же, как это было в *nfqws1*.
Но лучше все-же писать фильтры `--payload`, потому что они работают на уровне C кода, который выполняется существенно быстрее, чем LUA.
Диссект пакета проходит поочередно по всем `--lua-desync` функциям профиля, для которых не выполняется условие отсечения (cutoff).
Отсечение может быть по range, payload или добровольное отсечение instance. Последний вариант - когда инстанс сам отказывается обрабатывать пакеты
по входящему, исходящему или обоим направлениям. Например, задача стратегии synack - отреагировать только на пакет с tcp флагами SYN,ACK. После этого функция не нужна, в ее коде вызывается функция отсечения.
Это сделано для экономии ресурсов процессора.
Если все функции в профиле точно никогда больше не будут вызваны по соединению + направлению - вошли в превышение верхней границы range или выполнили добровольный cutoff, то движок LUA не вызывается вообще.
От инстанса к инстансу содержимое диссекта может меняться функциями. Следующая функция видит изменения предыдущей.
Каждый инстанс выносит свой вердикт - что делать с текущим диссектом. VERDICT_PASS - означает отправить как есть,
VERDICT_MODIFY - отправить модифицированную версию, VERDICT_DROP - дропнуть диссект (не отправлять).
Итоговый вердикт формируется на основании вердиктов отдельных инстансов.
Если какой-либо инстанс выдал VERDICT_DROP - итоговый результат - всегда VERDICT_DROP.
Если ни один инстанс не выдал VERDICT_DROP, а какой-либо инстанс выдал VERDICT_MODIFY, то будет VERDICT_MODIFY.
Если все инстансы выдали VERDICT_PASS - будет VERDICT_PASS.
Например, функция pktmod применяет фулинг к текущему диссекту и выставляет вердикт VERDICT_MODIFY.
Если после этого будет вызван инстанс multisplit, то произойдет резка текущего уже измененного диссекта, отправка частей и в случае успеха VERDICT_DROP.
Получается, что мы применили фулинг и отправили с этим фулингом все tcp сегменты.
### Примеры портирования стратегий *nfqws1*
Кратко, на примерах, покажем как стратегии с *nfqws1* переписываются под *nfqws2*.
Для краткости здесь опущены директивы `--qnum`, `--lua-init`, `--wf-tcp-out` и тому подобное, что не касается напрямую стратегий и их непосредственного обслуживания.
Параметр `--filter-l7` относится к фильтру профиля мультистратегии. Здесь приведен как указание, что будет обрабатываться только конкретный протокол.
Автоматического использования ttl для ipv6 больше нет. Нужно писать отдельно для ipv4 и ipv6. Если не будет написано для ipv6, то к нему не будет применен ttl.
Функция pktmod применяет фулинг к текущему диссекту.
```
nfqws \
--filter-l7=http --dpi-desync=fake --dpi-desync-fake-http=0x00000000 --dpi-desync-ttl=6 \
--orig-ttl=1 --orig-mod-start=s1 --orig-mod-cutoff=d1
nfqws2 \
--filter-l7=http \
--payload=http_req --lua-desync=fake:blob=0x00000000:ip_ttl=6:ip6_ttl=6 \
--payload=empty --out-range="s1<d1" --lua-desync=pktmod:ip_ttl=1:ip6_ttl=1
```
badseq без параметров в *nfqws1* применял инкремент -10000 для syn и -66000 для ack.
В функциях `zapret-antidpi.lua` понятия badseq нет. Есть фулинги - уменьшить seq или ack на указанное значение.
tcp_ts_up - очень странное явление, обнаруженное в процессе тестирования *nfqws2*.
Оказывается, если есть tcp опция timestamp, linux стабильно отбрасывает пакеты с валидным seq и инвалидным ack только если опция идет первой.
*nfqws1* не соблюдал порядок tcp опций, timestamp получался первым всегда.
Поэтому оказалось, что старая версия работает стабильно , а новая нет.
tcp_ts_up дублирует старое поведение - двигает timestamp в самый верх.
```
nfqws \
--filter-l7=http \
--dpi-desync=fakedsplit --dpi-desync-fooling=badseq --dpi-desync-badseq-increment=0 --dpi-desync-split-pos=method+2
nfqws2 \
--filter-l7=http \
--payload=http_req --lua-desync=fakedsplit:pos=method+2:tcp_ack=-66000:tcp_ts_up
```
autottl пишется полностью в формате `delta,min-max`. Вместо двоеточия используется запятая, чтобы не конфликтовать с разделителем параметров функции.
```
nfqws \
--filter-l7=tls \
--dpi-desync=fakedsplit --dpi-desync-fakedsplit-pattern=tls_clienthello_google_com.bin \
--dpi-desync-ttl=1 --dpi-desync-autottl=-1 --dpi-desync-split-pos=method+2 --dpi-desync-fakedsplit-mod=altorder=1
nfqws2 \
--blob=tls_google:@tls_clienthello_google_com.bin \
--filter-l7=tls \
--payload tls_client_hello,http_req \
--lua-desync=fakedsplit:pattern=tls_google:pos=method+2:nofake1:ip_ttl=1:ip6_ttl=1:ip_autottl=-1,3-20:ip6_autottl=-1,3-20
```
Здесь важен порядок вызова функций.
wssize работает как модификатор диссекта - переписывает window size и scale factor. syndata должна быть отослана с модифицированными
wsize и scale. Если перепутать порядок следования, то syndata будет отправлена без wssize. Поскольку важна модификация, начиная с SYN пакета,
то wssize не сработает ожидаемым образом.
```
nfqws --dpi-desync=syndata,multisplit --dpi-desync-split-pos=midsld --wssize 1:6
nfqws2 --lua-desync=wssize:wsize=1:scale=6 --lua-desync=syndata --lua-desync=multisplit:pos=midsld
```
В первом примере все модификации tls применяются на лету.
Это так же означает, что рандомы будут применяться каждый раз, а не один раз, как в *nfqws1*.
Поведение можно привести к варианту *nfqws1* при желании - во втором примере показано как.
```
nfqws1 \
--filter-l7 tls \
--dpi-desync=fake --dpi-desync-fooling=datanoack --dpi-desync-fake-tls=! \
--dpi-desync-fake-tls-mod=rnd,rndsni,dupsid
nfqws2
--filter-l7 tls \
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_flags_unset=ack:tls_mod=rnd,rndsni,dupsid,padencap \
--payload=empty --out-range="s1<d1" --lua-desync=pktmod:ip_ttl=1:ip6_ttl=1
nfqws2 \
--lua-init="fake_default_tls=tls_mod(fake_default_tls,'rnd,rndsni')" \
--filter-l7 tls \
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_flags_unset=ack:tls_mod=dupsid,padencap \
--payload=empty --out-range="s1<d1" --lua-desync=pktmod:ip_ttl=1:ip6_ttl=1
```
IP фрагментация является теперь опцией процесса отсылки. Функция send отсылает текущий диссект, применяя указанные модификаторы, но не дропает оригинал.
Чтобы оригинал не пошел следом - применяется функция drop. Она ничего не делает, только выносит VERDICT_DROP.
При желании ipfrag можно применить и к fake, multisplit и другим функциям. Так же можно писать свои функции IP фрагментации.
Функция по умолчанию ipfrag2 делить пакет на 2 части. Но вы можете написать функцию, которая разделит его на 10 частей и указать ее как `ipfrag=my_frag_function`.
Функция фрагментации получает диссект подлежащего фрагментации пакета на вход и возвращает массив диссектов - фрагментов.
```
nfqws --dpi-desync=ipfrag2 --dpi-desync-ipfrag-pos-udp=8
nfqws2 --lua-desync=send:ipfrag:ipfrag_pos_udp=8 --lua-desync=drop
```
### Какие есть еще параметры
Как узнать какие есть еще функции и какие у них бывают параметры ? Смотрите `zapret-antidpi.lua`. Перед каждой функцией подробно описано какие параметры она берет.
Описание стандартных блоков параметров есть в начале. Позже - по мере сил и возможностей - будет писаться талмуд - справочник с руководством по программированию
*nfqws2* и описание стандартных библиотек.
### Очень важный совет
Научитесь пользоваться `--debug` логом. Без него будет очень сложно понять *nfqws2* на начальном этапе и приспособиться к новой схеме.
Ошибок будет много. Особенно, когда вы начнете писать свой LUA код. Их надо читать.

Binary file not shown.

View File

@@ -0,0 +1,2 @@
d1:ad2:id20:.+NA-¢ÔutÚÛEiJ·9:info_hash20:;
ÂÅÙä¾§¾“äÙOÁ£2IÂe1:q9:get_peers1:t2:äâ1:v4:LT1:y1:qe

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,9 @@
GET / HTTP/1.1
Host: www.iana.org
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4300.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,ru;q=0.8

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
files/fake/rdp.bin Normal file

Binary file not shown.

13
files/fake/sip.bin Normal file
View File

@@ -0,0 +1,13 @@
REGISTER sip:192.168.102.23 SIP/2.0
Via: SIP/2.0/UDP 192.168.102.250:51781;rport;branch=z9hG4bKPj3f42ea713eca49ec93f6cb69f6c38014
Max-Forwards: 70
From: <sip:233@192.168.102.23>;tag=ca565d7bd4e24a6d80c631d395ee117e
To: <sip:233@192.168.102.23>
Call-ID: a5e53e302b2d4a4d83c1455527c882c3
CSeq: 26538 REGISTER
User-Agent: MicroSIP/3.21.5
Contact: <sip:233@192.168.102.250:51781;ob>
Expires: 300
Allow: PRACK, INVITE, ACK, BYE, CANCEL, UPDATE, INFO, SUBSCRIBE, NOTIFY, REFER, MESSAGE, OPTIONS
Content-Length: 0

BIN
files/fake/stun.bin Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
files/fake/zero_1024.bin Normal file

Binary file not shown.

BIN
files/fake/zero_256.bin Normal file

Binary file not shown.

BIN
files/fake/zero_512.bin Normal file

Binary file not shown.

218
install_bin.sh Executable file
View File

@@ -0,0 +1,218 @@
#!/bin/sh
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
BINS=binaries
BINDIR="$EXEDIR/$BINS"
ZAPRET_BASE=${ZAPRET_BASE:-"$EXEDIR"}
. "$ZAPRET_BASE/common/base.sh"
read_elf_arch()
{
# $1 - elf file
local arch=$(dd if="$1" count=2 bs=1 skip=18 2>/dev/null | hexdump -e '2/1 "%02x"')
local bit=$(dd if="$1" count=1 bs=1 skip=4 2>/dev/null | hexdump -e '1/1 "%02x"')
echo $bit$arch
}
select_test_method()
{
local f ELF
TEST=run
# ash and dash try to execute invalid executables as a script. they interpret binary garbage with possible negative consequences
# bash and zsh do not do this
if exists bash; then
TEST=bash
elif exists zsh && [ "$UNAME" != CYGWIN ] ; then
TEST=zsh
elif [ "$UNAME" != Darwin -a "$UNAME" != CYGWIN ]; then
if exists hexdump and exists dd; then
# macos does not use ELF
TEST=elf
ELF=
ELF_ARCH=
for f in /bin/sh /system/bin/sh; do
[ -x "$f" ] && {
ELF=$f
break
}
done
[ -n "$ELF" ] && ELF_ARCH=$(read_elf_arch "$ELF")
[ -n "$ELF_ARCH" ] && return
fi
# find does not use its own shell exec
# it uses execvp(). in musl libc it does not call shell, in glibc it DOES call /bin/sh
# that's why prefer bash or zsh if present. otherwise it's our last chance
if exists find; then
TEST=find
FIND=find
elif exists busybox; then
busybox find /jGHUa3fh1A 2>/dev/null
# 127 - command not found
[ "$?" = 127 ] || {
TEST=find
FIND="busybox find"
}
fi
fi
}
disable_antivirus()
{
# $1 - dir
[ "$UNAME" = Darwin ] && find "$1" -maxdepth 1 -type f -perm +111 -exec xattr -d com.apple.quarantine {} \; 2>/dev/null
}
check_dir()
{
local dir="$BINDIR/$1"
local exe="$dir/ip2net"
local out
if [ -f "$exe" ]; then
if [ -x "$exe" ]; then
disable_antivirus "$dir"
case $TEST in
bash)
out=$(echo 0.0.0.0 | bash -c "\"$exe"\" 2>/dev/null)
[ -n "$out" ]
;;
zsh)
out=$(echo 0.0.0.0 | zsh -c "\"$exe\"" 2>/dev/null)
[ -n "$out" ]
;;
elf)
out=$(read_elf_arch "$exe")
[ "$ELF_ARCH" = "$out" ] && {
# exec test to verify it actually works. no illegal instruction or crash.
out=$(echo 0.0.0.0 | "$exe" 2>/dev/null)
[ -n "$out" ]
}
;;
find)
out=$(echo 0.0.0.0 | $FIND "$dir" -maxdepth 1 -name ip2net -exec {} \; 2>/dev/null)
[ -n "$out" ]
;;
run)
out=$(echo 0.0.0.0 | "$exe" 2>/dev/null)
[ -n "$out" ]
;;
*)
false
;;
esac
else
echo >&2 "$exe is not executable. set proper chmod."
return 1
fi
else
echo >&2 "$exe is absent"
return 2
fi
}
# link or copy executables. uncomment either ln or cp, comment other
ccp()
{
local F="$(basename "$1")"
[ -d "$ZAPRET_BASE/$2" ] || mkdir "$ZAPRET_BASE/$2"
[ -f "$ZAPRET_BASE/$2/$F" ] && rm -f "$ZAPRET_BASE/$2/$F"
ln -fs "../$BINS/$1" "$ZAPRET_BASE/$2" && echo linking : "../$BINS/$1" =\> "$ZAPRET_BASE/$2"
#cp -f "../$BINS/$1" "$ZAPRET_BASE/$2" && echo copying : "../$BINS/$1" =\> "$ZAPRET_BASE/$2"
}
UNAME=$(uname)
[ "$1" = getarch ] ||
if [ ! -d "$BINDIR" ] || ! dir_is_not_empty "$BINDIR" ]; then
echo "no binaries found"
case $UNAME in
Linux)
echo "you need to download release from github or build binaries from source"
echo "building from source requires debian/ubuntu packages : make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev libmnl-dev libsystemd-dev libluajit2-5.1-dev"
echo "libsystemd-dev required only on systemd based systems"
echo "on distributions with other package manager find dev package analogs"
echo "to compile on systems with systemd : make systemd"
echo "to compile on other systems : make"
;;
Darwin)
echo "you need to download release from github or build binaries from source"
echo "to compile : make mac"
;;
FreeBSD)
echo "you need to download release from github or build binaries from source"
echo "to compile : make"
;;
OpenBSD)
echo "to compile : make bsd"
;;
CYGWIN*)
echo "you need to download release from github or build binaries from source"
echo "to compile : read docs"
echo "to make things easier use zapret-win-bundle"
;;
esac
exit 1
fi
unset PKTWS
case $UNAME in
Linux)
ARCHLIST="my linux-x86_64 linux-x86 linux-arm64 linux-arm linux-mips64 linux-mipsel linux-mips linux-lexra linux-ppc"
PKTWS=nfqws2
;;
Darwin)
ARCHLIST="my mac64"
;;
FreeBSD)
ARCHLIST="my freebsd-x86_64"
PKTWS=dvtws2
;;
CYGWIN*)
UNAME=CYGWIN
ARCHLIST="windows-x86_64 windows-x86"
PKTWS=winws2
;;
*)
ARCHLIST="my"
esac
select_test_method
if [ "$1" = "getarch" ]; then
for arch in $ARCHLIST
do
[ -d "$BINDIR/$arch" ] || continue
if check_dir $arch; then
echo $arch
exit 0
fi
done
else
echo "using arch detect method : $TEST${ELF_ARCH:+ $ELF_ARCH}"
for arch in $ARCHLIST
do
[ -d "$BINDIR/$arch" ] || continue
if check_dir $arch; then
echo $arch is OK
echo installing binaries ...
ccp $arch/ip2net ip2net
ccp $arch/mdig mdig
[ -n "$PKTWS" ] && ccp $arch/$PKTWS nfq2
exit 0
else
echo $arch is NOT OK
fi
done
echo no compatible binaries found
fi
exit 1

33
ip2net/Makefile Normal file
View File

@@ -0,0 +1,33 @@
CC ?= cc
OPTIMIZE ?= -Os
CFLAGS += -std=gnu99 $(OPTIMIZE) -flto=auto
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_WIN = -static
LIBS =
LIBS_WIN = -lws2_32
SRC_FILES = ip2net.c qsort.c
all: ip2net
ip2net: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS)
systemd: ip2net
android: ip2net
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS)
mac: $(SRC_FILES)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2neta $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2netx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS) $(LDFLAGS)
strip ip2neta ip2netx
lipo -create -output ip2net ip2netx ip2neta
rm -f ip2netx ip2neta
win: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o ip2net $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
clean:
rm -f ip2net *.o

516
ip2net/ip2net.c Normal file
View File

@@ -0,0 +1,516 @@
// group ipv4/ipv6 list from stdout into subnets
// each line must contain either ip or ip/bitcount
// valid ip/bitcount and ip1-ip2 are passed through without modification
// ips are groupped into subnets
// can be compiled in mingw. msvc not supported because of absent getopt
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#ifdef _WIN32
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x600
#include <winsock2.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#else
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#endif
#include <getopt.h>
#include "qsort.h"
#define ALLOC_STEP 16384
// minimum subnet fill percent is PCTMULT/PCTDIV (for example 3/4)
#define DEFAULT_PCTMULT 3
#define DEFAULT_PCTDIV 4
// subnet search range in "zero bit count"
// means search start from /(32-ZCT_MAX) to /(32-ZCT_MIN)
#define DEFAULT_V4_ZCT_MAX 10 // /22
#define DEFAULT_V4_ZCT_MIN 2 // /30
#define DEFAULT_V6_ZCT_MAX 72 // /56
#define DEFAULT_V6_ZCT_MIN 64 // /64
// must be no less than N ipv6 in subnet
#define DEFAULT_V6_THRESHOLD 5
static int ucmp(const void * a, const void * b, void *arg)
{
if (*(uint32_t*)a < *(uint32_t*)b)
return -1;
else if (*(uint32_t*)a > *(uint32_t*)b)
return 1;
else
return 0;
}
static uint32_t mask_from_bitcount(uint32_t zct)
{
return zct<32 ? ~((1 << zct) - 1) : 0;
}
// make presorted array unique. return number of unique items.
// 1,1,2,3,3,0,0,0 (ct=8) => 1,2,3,0 (ct=4)
static uint32_t unique(uint32_t *pu, uint32_t ct)
{
uint32_t i, j, u;
for (i = j = 0; j < ct; i++)
{
u = pu[j++];
for (; j < ct && pu[j] == u; j++);
pu[i] = u;
}
return i;
}
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize ("no-strict-aliasing")))
#endif
static int cmp6(const void * a, const void * b, void *arg)
{
// this function is critical for sort performance
// on big endian systems cpu byte order is equal to network byte order
// no conversion required. it's possible to improve speed by using big size compares
// on little endian systems byte conversion also gives better result than byte comparision
// 64-bit archs often have cpu command to reverse byte order
// assume that a and b are properly aligned
#if defined(__BYTE_ORDER__) && ((__BYTE_ORDER__==__ORDER_BIG_ENDIAN__) || (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__))
uint64_t aa,bb;
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
aa = __builtin_bswap64(((uint64_t*)((struct in6_addr *)a)->s6_addr)[0]);
bb = __builtin_bswap64(((uint64_t*)((struct in6_addr *)b)->s6_addr)[0]);
#else
aa = ((uint64_t*)((struct in6_addr *)a)->s6_addr)[0];
bb = ((uint64_t*)((struct in6_addr *)b)->s6_addr)[0];
#endif
if (aa < bb)
return -1;
else if (aa > bb)
return 1;
else
{
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
aa = __builtin_bswap64(((uint64_t*)((struct in6_addr *)a)->s6_addr)[1]);
bb = __builtin_bswap64(((uint64_t*)((struct in6_addr *)b)->s6_addr)[1]);
#else
aa = ((uint64_t*)((struct in6_addr *)a)->s6_addr)[1];
bb = ((uint64_t*)((struct in6_addr *)b)->s6_addr)[1];
#endif
return aa < bb ? -1 : aa > bb ? 1 : 0;
}
#else
// fallback case
for (uint8_t i = 0; i < sizeof(((struct in6_addr *)0)->s6_addr); i++)
{
if (((struct in6_addr *)a)->s6_addr[i] < ((struct in6_addr *)b)->s6_addr[i])
return -1;
else if (((struct in6_addr *)a)->s6_addr[i] > ((struct in6_addr *)b)->s6_addr[i])
return 1;
}
return 0;
#endif
}
// make presorted array unique. return number of unique items.
static uint32_t unique6(struct in6_addr *pu, uint32_t ct)
{
uint32_t i, j, k;
for (i = j = 0; j < ct; i++)
{
for (k = j++; j < ct && !memcmp(pu + j, pu + k, sizeof(struct in6_addr)); j++);
pu[i] = pu[k];
}
return i;
}
static void mask_from_bitcount6_make(uint32_t zct, struct in6_addr *a)
{
if (zct >= 128)
memset(a->s6_addr,0x00,16);
else
{
int32_t n = (127 - zct) >> 3;
memset(a->s6_addr,0xFF,n);
memset(a->s6_addr+n,0x00,16-n);
a->s6_addr[n] = ~((1 << (zct & 7)) - 1);
}
}
static struct in6_addr ip6_mask[129];
static void mask_from_bitcount6_prepare(void)
{
for (int zct=0;zct<=128;zct++) mask_from_bitcount6_make(zct, ip6_mask+zct);
}
static inline const struct in6_addr *mask_from_bitcount6(uint32_t zct)
{
return ip6_mask+zct;
}
/*
// this is "correct" solution for strict aliasing feature
// but I don't like this style of coding
// write what I don't mean to force smart optimizer to do what it's best
// it produces better code sometimes but not on all compilers/versions/archs
// sometimes it even generates real memcpy calls (mips32,arm32)
// so I will not do it
static void ip6_and(const struct in6_addr *a, const struct in6_addr *b, struct in6_addr *result)
{
uint64_t a_addr[2], b_addr[2];
memcpy(a_addr, a->s6_addr, 16);
memcpy(b_addr, b->s6_addr, 16);
a_addr[0] &= b_addr[0];
a_addr[1] &= b_addr[1];
memcpy(result->s6_addr, a_addr, 16);
}
*/
// YES, from my point of view C should work as a portable assembler. It must do what I instruct it to do.
// that's why I disable strict aliasing for this function. I observed gcc can miscompile with O2/O3 setting if inlined and not coded "correct"
// result = a & b
// assume that a and b are properly aligned
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize ("no-strict-aliasing")))
#endif
static void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result)
{
#ifdef __SIZEOF_INT128__
// gcc and clang have 128 bit int types on some 64-bit archs. take some advantage
*((unsigned __int128*)result->s6_addr) = *((unsigned __int128*)a->s6_addr) & *((unsigned __int128*)b->s6_addr);
#else
((uint64_t*)result->s6_addr)[0] = ((uint64_t*)a->s6_addr)[0] & ((uint64_t*)b->s6_addr)[0];
((uint64_t*)result->s6_addr)[1] = ((uint64_t*)a->s6_addr)[1] & ((uint64_t*)b->s6_addr)[1];
#endif
}
static void rtrim(char *s)
{
if (s)
for (char *p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'); p--) *p = '\0';
}
static struct params_s
{
bool ipv6;
uint32_t pctmult, pctdiv; // for v4
uint32_t zct_min, zct_max; // for v4 and v6
uint32_t v6_threshold; // for v6
} params;
static void exithelp(void)
{
printf(
" -4\t\t\t\t; ipv4 list (default)\n"
" -6\t\t\t\t; ipv6 list\n"
" --prefix-length=min[-max]\t; consider prefix lengths from 'min' to 'max'. examples : 22-30 (ipv4), 56-64 (ipv6)\n"
" --v4-threshold=mul/div\t\t; ipv4 only : include subnets with more than mul/div ips. example : 3/4\n"
" --v6-threshold=N\t\t; ipv6 only : include subnets with more than N v6 ips. example : 5\n"
);
exit(1);
}
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#if defined(ZAPRET_GH_VER) || defined (ZAPRET_GH_HASH)
#define PRINT_VER printf("github version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH))
#else
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
enum opt_indices {
IDX_HELP,
IDX_H,
IDX_4,
IDX_6,
IDX_PREFIX_LENGTH,
IDX_V4_THRESHOLD,
IDX_V6_THRESHOLD,
IDX_LAST,
};
static const struct option long_options[] = {
[IDX_HELP] = {"help", no_argument, 0, 0},
[IDX_H] = {"h", no_argument, 0, 0},
[IDX_4] = {"4", no_argument, 0, 0},
[IDX_6] = {"6", no_argument, 0, 0},
[IDX_PREFIX_LENGTH] = {"prefix-length", required_argument, 0, 0},
[IDX_V4_THRESHOLD] = {"v4-threshold", required_argument, 0, 0},
[IDX_V6_THRESHOLD] = {"v6-threshold", required_argument, 0, 0},
[IDX_LAST] = {NULL, 0, NULL, 0},
};
static void parse_params(int argc, char *argv[])
{
int option_index = 0;
int v, i;
uint32_t plen1=-1, plen2=-1;
memset(&params, 0, sizeof(params));
params.pctmult = DEFAULT_PCTMULT;
params.pctdiv = DEFAULT_PCTDIV;
params.v6_threshold = DEFAULT_V6_THRESHOLD;
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1)
{
if (v) exithelp();
switch (option_index)
{
case IDX_HELP:
case IDX_H:
PRINT_VER;
exithelp();
break;
case IDX_4:
params.ipv6 = false;
break;
case IDX_6:
params.ipv6 = true;
break;
case IDX_PREFIX_LENGTH:
i = sscanf(optarg,"%u-%u",&plen1,&plen2);
if (i == 1) plen2 = plen1;
if (i<=0 || plen2<plen1 || !plen1 || !plen2)
{
fprintf(stderr, "invalid parameter for prefix-length : %s\n", optarg);
exit(1);
}
break;
case IDX_V4_THRESHOLD:
i = sscanf(optarg, "%u/%u", &params.pctmult, &params.pctdiv);
if (i!=2 || params.pctdiv<2 || params.pctmult<1 || params.pctmult>=params.pctdiv)
{
fprintf(stderr, "invalid parameter for v4-threshold : %s\n", optarg);
exit(1);
}
break;
case IDX_V6_THRESHOLD:
i = sscanf(optarg, "%u", &params.v6_threshold);
if (i != 1 || params.v6_threshold<1)
{
fprintf(stderr, "invalid parameter for v6-threshold : %s\n", optarg);
exit(1);
}
break;
}
}
if (plen1 != -1 && ((!params.ipv6 && (plen1>31 || plen2>31)) || (params.ipv6 && (plen1>127 || plen2>127))))
{
fprintf(stderr, "invalid parameter for prefix-length\n");
exit(1);
}
params.zct_min = params.ipv6 ? plen2==-1 ? DEFAULT_V6_ZCT_MIN : 128-plen2 : plen2==-1 ? DEFAULT_V4_ZCT_MIN : 32-plen2;
params.zct_max = params.ipv6 ? plen1==-1 ? DEFAULT_V6_ZCT_MAX : 128-plen1 : plen1==-1 ? DEFAULT_V4_ZCT_MAX : 32-plen1;
}
int main(int argc, char **argv)
{
char str[256],d;
uint32_t ipct = 0, iplist_size = 0, pos = 0, p, zct, ip_ct, pos_end;
parse_params(argc, argv);
if (params.ipv6) // ipv6
{
char *s;
struct in6_addr a, *iplist = NULL, *iplist_new;
while (fgets(str, sizeof(str), stdin))
{
rtrim(str);
d = 0;
if ((s = strchr(str, '/')) || (s = strchr(str, '-')))
{
d = *s;
*s = '\0';
}
if (inet_pton(AF_INET6, str, &a))
{
if (d=='/')
{
// we have subnet ip6/y
// output it as is
if (sscanf(s + 1, "%u", &zct)==1 && zct!=128)
{
if (zct<128) printf("%s/%u\n", str, zct);
continue;
}
}
else if (d=='-')
{
if (inet_pton(AF_INET6, s+1, &a)) printf("%s-%s\n", str, s+1);
continue;
}
if (ipct >= iplist_size)
{
iplist_size += ALLOC_STEP;
iplist_new = (struct in6_addr*)(iplist ? realloc(iplist, sizeof(*iplist)*iplist_size) : malloc(sizeof(*iplist)*iplist_size));
if (!iplist_new)
{
free(iplist);
fprintf(stderr, "out of memory\n");
return 100;
}
iplist = iplist_new;
}
iplist[ipct++] = a;
}
}
gnu_quicksort(iplist, ipct, sizeof(*iplist), cmp6, NULL);
ipct = unique6(iplist, ipct);
mask_from_bitcount6_prepare();
/*
for(uint32_t i=0;i<ipct;i++)
if (inet_ntop(AF_INET6,iplist+i,str,sizeof(str)))
printf("%s\n",str);
printf("\n");
*/
while (pos < ipct)
{
const struct in6_addr *mask;
struct in6_addr ip_start, ip;
uint32_t ip_ct_best = 0, zct_best = 0;
pos_end = pos + 1;
// find smallest network with maximum ip coverage with no less than ip6_subnet_threshold addresses
for (zct = params.zct_max; zct >= params.zct_min; zct--)
{
mask = mask_from_bitcount6(zct);
ip6_and(iplist + pos, mask, &ip_start);
for (p = pos + 1, ip_ct = 1; p < ipct; p++, ip_ct++)
{
ip6_and(iplist + p, mask, &ip);
if (memcmp(&ip_start, &ip, sizeof(ip)))
break;
}
if (ip_ct == 1) break;
if (ip_ct >= params.v6_threshold)
{
// network found. but is there smaller network with the same ip_ct ? dont do carpet bombing if possible, use smaller subnets
if (!ip_ct_best || ip_ct == ip_ct_best)
{
ip_ct_best = ip_ct;
zct_best = zct;
pos_end = p;
}
else
break;
}
}
if (zct_best)
// network was found
ip6_and(iplist + pos, mask_from_bitcount6(zct_best), &ip_start);
else
ip_start = iplist[pos], pos_end = pos + 1; // network not found, use single ip
inet_ntop(AF_INET6, &ip_start, str, sizeof(str));
printf(zct_best ? "%s/%u\n" : "%s\n", str, 128 - zct_best);
pos = pos_end;
}
free(iplist);
}
else // ipv4
{
uint32_t u1,u2,u3,u4, u11,u22,u33,u44, ip;
uint32_t *iplist = NULL, *iplist_new, i;
while (fgets(str, sizeof(str), stdin))
{
if ((i = sscanf(str, "%u.%u.%u.%u-%u.%u.%u.%u", &u1, &u2, &u3, &u4, &u11, &u22, &u33, &u44)) >= 8 &&
!(u1 & 0xFFFFFF00) && !(u2 & 0xFFFFFF00) && !(u3 & 0xFFFFFF00) && !(u4 & 0xFFFFFF00) &&
!(u11 & 0xFFFFFF00) && !(u22 & 0xFFFFFF00) && !(u33 & 0xFFFFFF00) && !(u44 & 0xFFFFFF00))
{
printf("%u.%u.%u.%u-%u.%u.%u.%u\n", u1, u2, u3, u4, u11, u22, u33, u44);
}
else
if ((i = sscanf(str, "%u.%u.%u.%u/%u", &u1, &u2, &u3, &u4, &zct)) >= 4 &&
!(u1 & 0xFFFFFF00) && !(u2 & 0xFFFFFF00) && !(u3 & 0xFFFFFF00) && !(u4 & 0xFFFFFF00))
{
if (i == 5 && zct != 32)
{
// we have subnet x.x.x.x/y
// output it as is if valid, ignore otherwise
if (zct < 32)
printf("%u.%u.%u.%u/%u\n", u1, u2, u3, u4, zct);
}
else
{
ip = u1 << 24 | u2 << 16 | u3 << 8 | u4;
if (ipct >= iplist_size)
{
iplist_size += ALLOC_STEP;
iplist_new = (uint32_t*)(iplist ? realloc(iplist, sizeof(*iplist)*iplist_size) : malloc(sizeof(*iplist)*iplist_size));
if (!iplist_new)
{
free(iplist);
fprintf(stderr, "out of memory\n");
return 100;
}
iplist = iplist_new;
}
iplist[ipct++] = ip;
}
}
}
gnu_quicksort(iplist, ipct, sizeof(*iplist), ucmp, NULL);
ipct = unique(iplist, ipct);
while (pos < ipct)
{
uint32_t mask, ip_start, ip_end, subnet_ct;
uint32_t ip_ct_best = 0, zct_best = 0;
// find smallest network with maximum ip coverage with no less than mul/div percent addresses
for (zct = params.zct_max; zct >= params.zct_min; zct--)
{
mask = mask_from_bitcount(zct);
ip_start = iplist[pos] & mask;
subnet_ct = ~mask + 1;
if (iplist[pos] > (ip_start + subnet_ct*(params.pctdiv - params.pctmult) / params.pctdiv))
continue; // ip is higher than (1-PCT). definitely coverage is not enough. skip searching
ip_end = ip_start | ~mask;
for (p=pos+1, ip_ct=1; p < ipct && iplist[p] <= ip_end; p++) ip_ct++; // count ips within subnet range
if (ip_ct == 1) break;
if (ip_ct >= (subnet_ct*params.pctmult / params.pctdiv))
{
// network found. but is there smaller network with the same ip_ct ? dont do carpet bombing if possible, use smaller subnets
if (!ip_ct_best || ip_ct == ip_ct_best)
{
ip_ct_best = ip_ct;
zct_best = zct;
pos_end = p;
}
else
break;
}
}
if (zct_best)
ip_start = iplist[pos] & mask_from_bitcount(zct_best);
else
ip_start = iplist[pos], pos_end = pos + 1; // network not found, use single ip
u1 = ip_start >> 24;
u2 = (ip_start >> 16) & 0xFF;
u3 = (ip_start >> 8) & 0xFF;
u4 = ip_start & 0xFF;
printf(zct_best ? "%u.%u.%u.%u/%u\n" : "%u.%u.%u.%u\n", u1, u2, u3, u4, 32 - zct_best);
pos = pos_end;
}
free(iplist);
}
return 0;
}

250
ip2net/qsort.c Normal file
View File

@@ -0,0 +1,250 @@
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Written by Douglas C. Schmidt (schmidt@ics.uci.edu).
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
/* If you consider tuning this algorithm, you should consult first:
Engineering a sort function; Jon Bentley and M. Douglas McIlroy;
Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */
//#include <alloca.h>
#include <limits.h>
#include <stdlib.h>
//#include <string.h>
#include "qsort.h"
/* Byte-wise swap two items of size SIZE. */
#define SWAP(a, b, size) \
do \
{ \
size_t __size = (size); \
char *__a = (a), *__b = (b); \
do \
{ \
char __tmp = *__a; \
*__a++ = *__b; \
*__b++ = __tmp; \
} while (--__size > 0); \
} while (0)
/* Discontinue quicksort algorithm when partition gets below this size.
This particular magic number was chosen to work best on a Sun 4/260. */
#define MAX_THRESH 4
/* Stack node declarations used to store unfulfilled partition obligations. */
typedef struct
{
char *lo;
char *hi;
} stack_node;
/* The next 4 #defines implement a very fast in-line stack abstraction. */
/* The stack needs log (total_elements) entries (we could even subtract
log(MAX_THRESH)). Since total_elements has type size_t, we get as
upper bound for log (total_elements):
bits per byte (CHAR_BIT) * sizeof(size_t). */
#define STACK_SIZE (CHAR_BIT * sizeof(size_t))
#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
#define STACK_NOT_EMPTY (stack < top)
/* Order size using quicksort. This implementation incorporates
four optimizations discussed in Sedgewick:
1. Non-recursive, using an explicit stack of pointer that store the
next array partition to sort. To save time, this maximum amount
of space required to store an array of SIZE_MAX is allocated on the
stack. Assuming a 32-bit (64 bit) integer for size_t, this needs
only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes).
Pretty cheap, actually.
2. Chose the pivot element using a median-of-three decision tree.
This reduces the probability of selecting a bad pivot value and
eliminates certain extraneous comparisons.
3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving
insertion sort to order the MAX_THRESH items within each partition.
This is a big win, since insertion sort is faster for small, mostly
sorted array segments.
4. The larger of the two sub-partitions is always pushed onto the
stack first, with the algorithm then concentrating on the
smaller partition. This *guarantees* no more than log (total_elems)
stack size is needed (actually O(1) in this case)! */
void
gnu_quicksort (void *const pbase, size_t total_elems, size_t size,
__gnu_compar_d_fn_t cmp, void *arg)
{
char *base_ptr = (char *) pbase;
const size_t max_thresh = MAX_THRESH * size;
if (total_elems == 0)
/* Avoid lossage with unsigned arithmetic below. */
return;
if (total_elems > MAX_THRESH)
{
char *lo = base_ptr;
char *hi = &lo[size * (total_elems - 1)];
stack_node stack[STACK_SIZE];
stack_node *top = stack;
PUSH (NULL, NULL);
while (STACK_NOT_EMPTY)
{
char *left_ptr;
char *right_ptr;
/* Select median value from among LO, MID, and HI. Rearrange
LO and HI so the three values are sorted. This lowers the
probability of picking a pathological pivot value and
skips a comparison for both the LEFT_PTR and RIGHT_PTR in
the while loops. */
char *mid = lo + size * ((hi - lo) / size >> 1);
if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
SWAP (mid, lo, size);
if ((*cmp) ((void *) hi, (void *) mid, arg) < 0)
SWAP (mid, hi, size);
else
goto jump_over;
if ((*cmp) ((void *) mid, (void *) lo, arg) < 0)
SWAP (mid, lo, size);
jump_over:;
left_ptr = lo + size;
right_ptr = hi - size;
/* Here's the famous ``collapse the walls'' section of quicksort.
Gotta like those tight inner loops! They are the main reason
that this algorithm runs much faster than others. */
do
{
while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0)
left_ptr += size;
while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0)
right_ptr -= size;
if (left_ptr < right_ptr)
{
SWAP (left_ptr, right_ptr, size);
if (mid == left_ptr)
mid = right_ptr;
else if (mid == right_ptr)
mid = left_ptr;
left_ptr += size;
right_ptr -= size;
}
else if (left_ptr == right_ptr)
{
left_ptr += size;
right_ptr -= size;
break;
}
}
while (left_ptr <= right_ptr);
/* Set up pointers for next iteration. First determine whether
left and right partitions are below the threshold size. If so,
ignore one or both. Otherwise, push the larger partition's
bounds on the stack and continue sorting the smaller one. */
if ((size_t) (right_ptr - lo) <= max_thresh)
{
if ((size_t) (hi - left_ptr) <= max_thresh)
/* Ignore both small partitions. */
POP (lo, hi);
else
/* Ignore small left partition. */
lo = left_ptr;
}
else if ((size_t) (hi - left_ptr) <= max_thresh)
/* Ignore small right partition. */
hi = right_ptr;
else if ((right_ptr - lo) > (hi - left_ptr))
{
/* Push larger left partition indices. */
PUSH (lo, right_ptr);
lo = left_ptr;
}
else
{
/* Push larger right partition indices. */
PUSH (left_ptr, hi);
hi = right_ptr;
}
}
}
/* Once the BASE_PTR array is partially sorted by quicksort the rest
is completely sorted using insertion sort, since this is efficient
for partitions below MAX_THRESH size. BASE_PTR points to the beginning
of the array to sort, and END_PTR points at the very last element in
the array (*not* one beyond it!). */
#define min(x, y) ((x) < (y) ? (x) : (y))
{
char *const end_ptr = &base_ptr[size * (total_elems - 1)];
char *tmp_ptr = base_ptr;
char *thresh = min(end_ptr, base_ptr + max_thresh);
char *run_ptr;
/* Find smallest element in first threshold and place it at the
array's beginning. This is the smallest array element,
and the operation speeds up insertion sort's inner loop. */
for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size)
if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
tmp_ptr = run_ptr;
if (tmp_ptr != base_ptr)
SWAP (tmp_ptr, base_ptr, size);
/* Insertion sort, running from left-hand-side up to right-hand-side. */
run_ptr = base_ptr + size;
while ((run_ptr += size) <= end_ptr)
{
tmp_ptr = run_ptr - size;
while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0)
tmp_ptr -= size;
tmp_ptr += size;
if (tmp_ptr != run_ptr)
{
char *trav;
trav = run_ptr + size;
while (--trav >= run_ptr)
{
char c = *trav;
char *hi, *lo;
for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo)
*hi = *lo;
*hi = c;
}
}
}
}
}

6
ip2net/qsort.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
// GNU qsort is 2x faster than musl
typedef int (*__gnu_compar_d_fn_t) (const void *, const void *, void *);
void gnu_quicksort (void *const pbase, size_t total_elems, size_t size, __gnu_compar_d_fn_t cmp, void *arg);

866
lua/zapret-antidpi.lua Normal file
View File

@@ -0,0 +1,866 @@
--[[
NFQWS2 ANTIDPI LIBRARY
--lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua
--lua-desync=func1:arg1[=val1]:arg2[=val2] --lua-desync=func2:arg1[=val1]:arg2[=val2] .... --lua-desync=funcN:arg1[=val1]:arg2[=val2]
BLOBS
blobs can be 0xHEX, field name in desync or global var
standard way to bring binary data to lua code is using the "--blob" parameter of nfqws2
dynamic blobs can be inside desync table. one function can prepare data for next functions.
STANDARD FUNCTION ARGS
standard direction :
* dir = in|out|any
standard fooling :
* ip_ttl=N - set ipv.ip_ttl to N
* ip6_ttl=N - set ip6.ip6_hlim to N
* ip_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
* ip6_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
* ip6_hopbyhop[=hex] - add hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_hopbyhop2 - add 2 hopbyhop ipv6 headers with optional data. data size must be 6+N*8. all zero by default.
* ip6_destopt[=hex] - add destopt ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_routing[=hex] - add routing ipv6 header with optional data. data size must be 6+N*8. all zero by default.
* ip6_ah[=hex] - add authentication ipv6 header with optional data. data size must be 6+N*4. 0000 + 4 random bytes by default.
* tcp_seq=N - add N to tcp.th_seq
* tcp_ack=N - add N to tcp.th_ack
* tcp_ts=N - add N to timestamp value
* tcp_md5[=hex] - add MD5 header with optional 16-byte data. all zero by default.
* tcp_flags_set=<list> - set tcp flags in comma separated list
* tcp_unflags_set=<list> - unset tcp flags in comma separated list
* fool - custom fooling function : fool_func(dis, fooling_options)
standard reconstruct :
* badsum - make L4 checksum invalid
standard rawsend :
* repeats - how many time send the packet
* ifout - override outbound interface (if --bind_fix4, --bind-fix6 enabled)
* fwmark - override fwmark. desync mark bit(s) will be set unconditionally
standard payload :
* payload - comma separarated list of allowed payload types. if not present - allow non-empty known payloads.
standard ip_id :
* ip_id - seq|rnd|zero|none
* ip_id_conn - in 'seq' mode save current ip_id in track.lua_state to use it between packets
standard ipfrag :
* ipfrag - ipfrag function name. "ipfrag2" by default if empty
* ipfrag2 : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 8
* ipfrag2 : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32
* ipfrag2 : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
* ipfrag2 : ipfrag_disorder - send fragments from last to first
]]
-- dummy test function. does nothing.
-- no args
function pass(ctx, desync)
DLOG("pass")
end
-- prints desync to DLOG
function pktdebug(ctx, desync)
DLOG("desync:")
var_debug(desync)
end
-- drop packet
-- standard args : direction
function drop(ctx, desync)
direction_cutoff_opposite(ctx, desync, "any")
if direction_check(desync, "any") then
DLOG("drop")
return VERDICT_DROP
end
end
-- nfqws1 : "--dup"
-- standard args : direction, fooling, ip_id, rawsend, reconstruct
function send(ctx, desync)
direction_cutoff_opposite(ctx, desync, "any")
if direction_check(desync, "any") then
DLOG("send")
local dis = deepcopy(desync.dis)
apply_fooling(desync, dis)
apply_ip_id(desync, dis, nil, "none")
-- it uses rawsend, reconstruct and ipfrag options
rawsend_dissect_ipfrag(dis, desync_opts(desync))
end
end
-- nfqws1 : "--orig"
-- apply modification to current packet
-- standard args : direction, fooling, ip_id, rawsend, reconstruct
function pktmod(ctx, desync)
direction_cutoff_opposite(ctx, desync, "any")
if direction_check(desync, "any") then
-- apply to current packet
apply_fooling(desync)
apply_ip_id(desync, nil, nil, "none")
DLOG("pktmod: applied")
return VERDICT_MODIFY
end
end
-- nfqws1 : "--domcase"
-- standard args : direction
function http_domcase(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
if desync.l7payload=="http_req" and direction_check(desync) then
local host_range = resolve_multi_pos(desync.dis.payload,desync.l7payload,"host,endhost")
if #host_range == 2 then
local host = string.sub(desync.dis.payload,host_range[1],host_range[2]-1)
local newhost="", i
for i = 1, #host do
newhost=newhost..((i%2)==0 and string.lower(string.sub(host,i,i)) or string.upper(string.sub(host,i,i)))
end
DLOG("http_domcase: "..host.." => "..newhost)
desync.dis.payload = string.sub(desync.dis.payload, 1, host_range[1]-1)..newhost..string.sub(desync.dis.payload, host_range[2])
return VERDICT_MODIFY
else
DLOG("http_domcase: cannot find host range")
end
end
end
-- nfqws1 : "--hostcase"
-- standard args : direction
-- arg : spell=<str> . spelling of the "Host" header. must be exactly 4 chars long
function http_hostcase(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
if desync.l7payload=="http_req" and direction_check(desync) then
local spell = desync.arg.spell or "host"
if #spell ~= 4 then
error("http_hostcase: invalid host spelling '"..spell.."'")
else
local hdis = http_dissect_req(desync.dis.payload)
if hdis.headers.host then
DLOG("http_hostcase: 'Host:' => '"..spell.."'")
desync.dis.payload = string.sub(desync.dis.payload,1,hdis.headers.host.pos_start-1)..spell..string.sub(desync.dis.payload,hdis.headers.host.pos_header_end+1)
return VERDICT_MODIFY
else
DLOG("http_hostcase: 'Host:' header not found")
end
end
end
end
-- nfqws1 : "--methodeol"
-- standard args : direction
function http_methodeol(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
if desync.l7payload=="http_req" and direction_check(desync) then
local hdis = http_dissect_req(desync.dis.payload)
local ua = hdis.headers["user-agent"]
if ua then
if (ua.pos_end - ua.pos_value_start) < 2 then
DLOG("http_methodeol: 'User-Agent:' header is too short")
else
DLOG("http_methodeol: applied")
desync.dis.payload="\r\n"..string.sub(desync.dis.payload,1,ua.pos_end-2)..(string.sub(desync.dis.payload,ua.pos_end+1) or "");
return VERDICT_MODIFY
end
else
DLOG("http_methodeol: 'User-Agent:' header not found")
end
end
end
-- nfqws1 : "--synack-split"
-- standard args : rawsend, reconstruct, ipfrag
-- arg : mode=syn|synack|acksyn . "synack" by default
function synack_split(ctx, desync)
if desync.dis.tcp then
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK) == (TH_SYN + TH_ACK) then
local mode = desync.arg.mode or "synack"
local options = desync_opts(desync)
if mode=="syn" then
local dis = deepcopy(desync.dis)
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
DLOG("synack_split: sending SYN")
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
return VERDICT_DROP
elseif mode=="synack" then
local dis = deepcopy(desync.dis)
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
DLOG("synack_split: sending SYN")
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_SYN))
DLOG("synack_split: sending ACK")
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
return VERDICT_DROP
elseif mode=="acksyn" then
local dis = deepcopy(desync.dis)
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_SYN))
DLOG("synack_split: sending ACK")
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
dis.tcp.th_flags = bitand(desync.dis.tcp.th_flags, bitnot(TH_ACK))
DLOG("synack_split: sending SYN")
if not rawsend_dissect_ipfrag(dis, options) then return VERDICT_PASS end
return VERDICT_DROP
else
error("synack_split: bad mode '"..mode.."'")
end
else
instance_cutoff(ctx) -- mission complete
end
else
instance_cutoff(ctx)
end
end
-- nfqws1 : "--dpi-desync=synack"
-- standard args : rawsend, reconstruct, ipfrag
function synack(ctx, desync)
if desync.dis.tcp then
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK)==TH_SYN then
local dis = deepcopy(desync.dis)
dis.tcp.th_flags = bitor(dis.tcp.th_flags, TH_ACK)
DLOG("synack: sending")
rawsend_dissect_ipfrag(dis, desync_opts(desync))
else
instance_cutoff(ctx) -- mission complete
end
else
instance_cutoff(ctx)
end
end
-- nfqws1 : "--wssize"
-- arg : wsize=N . tcp window size
-- arg : scale=N . tcp option scale factor
function wsize(ctx, desync)
if desync.dis.tcp then
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK) == (TH_SYN + TH_ACK) then
if wsize_rewrite(desync.dis, desync.arg) then
return VERDICT_MODIFY
end
else
instance_cutoff(ctx) -- mission complete
end
else
instance_cutoff(ctx)
end
end
-- nfqws1 : "--wsize"
-- standard args : direction
-- arg : wsize=N . tcp window size
-- arg : scale=N . tcp option scale factor
-- arg : forced_cutoff=<list> - comma separated list of payloads that trigger forced wssize cutoff. by default - any non-empty payload
function wssize(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
local verdict = VERDICT_PASS
direction_cutoff_opposite(ctx, desync)
if direction_check(desync) then
if wsize_rewrite(desync.dis, desync.arg) then
verdict = VERDICT_MODIFY
end
if #desync.dis.payload>0 and (not desync.arg.forced_cutoff or in_list(desync.arg.forced_cutoff, desync.l7payload)) then
DLOG("wssize: forced cutoff")
instance_cutoff(ctx)
end
end
return verdict
end
-- nfqws1 : "--dpi-desync=syndata"
-- standard args : fooling, rawsend, reconstruct, ipfrag
-- arg : blob=<blob> - fake payload. must fit to single packet. no segmentation possible. default - 16 zero bytes.
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap
function syndata(ctx, desync)
if desync.dis.tcp then
if bitand(desync.dis.tcp.th_flags, TH_SYN + TH_ACK)==TH_SYN then
local dis = deepcopy(desync.dis)
dis.payload = blob(desync, desync.arg.blob, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
apply_fooling(desync, dis)
if desync.arg.tls_mod then
dis.payload = tls_mod(dis.payload, desync.arg.tls_mod, nil)
end
if b_debug then DLOG("syndata: "..hexdump_dlog(dis.payload)) end
if rawsend_dissect_ipfrag(dis, desync_opts(desync)) then
return VERDICT_DROP
end
else
instance_cutoff(ctx) -- mission complete
end
else
instance_cutoff(ctx)
end
end
-- nfqws1 : "--dpi-desync=fake"
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
-- arg : blob=<blob> - fake payload
-- arg : tls_mod=<list> - comma separated list of tls mods : rnd,rndsni,sni=<str>,dupsid,padencap
function fake(ctx, desync)
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
if direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
if not desync.arg.blob then
error("fake: 'blob' arg required")
end
local fake_payload = blob(desync, desync.arg.blob)
if desync.reasm_data and desync.arg.tls_mod then
fake_payload = tls_mod(fake_payload, desync.arg.tls_mod, desync.reasm_data)
end
-- check debug to save CPU
if b_debug then DLOG("fake: "..hexdump_dlog(fake_payload)) end
rawsend_payload_segmented(desync,fake_payload)
else
DLOG("fake: not acting on further replay pieces")
end
end
end
-- nfqws1 : "--dpi-desync=multisplit"
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
-- arg : pos=<posmarker list> . position marker list. for example : "1,host,midsld+1,-10"
-- arg : seqovl=N . decrease seq number of the first segment by N and fill N bytes with pattern (default - all zero)
-- arg : seqovl_pattern=<blob> . override pattern
function multisplit(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local spos = desync.arg.pos or "2"
-- check debug to save CPU
if b_debug then DLOG("multisplit: split pos: "..spos) end
local pos = resolve_multi_pos(data, desync.l7payload, spos)
if b_debug then DLOG("multisplit: resolved split pos: "..table.concat(zero_based_pos(pos)," ")) end
delete_pos_1(pos) -- cannot split at the first byte
if #pos>0 then
for i=0,#pos do
local pos_start = pos[i] or 1
local pos_end = i<#pos and pos[i+1]-1 or #data
local part = string.sub(data,pos_start,pos_end)
local seqovl=0
if i==0 and desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
seqovl = tonumber(desync.arg.seqovl)
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
part = pattern(pat,1,seqovl)..part
end
if b_debug then DLOG("multisplit: sending part "..(i+1).." "..(pos_start-1).."-"..(pos_end-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,pos_start-1-seqovl) then
return VERDICT_PASS
end
end
replay_drop_set(desync)
return VERDICT_DROP
else
DLOG("multisplit: no valid split positions")
end
else
DLOG("multisplit: not acting on further replay pieces")
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
end
end
end
-- nfqws1 : "--dpi-desync=multidisorder"
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
-- arg : pos=<postmarker list> . position marker list. example : "1,host,midsld+1,-10"
-- arg : seqovl=N . decrease seq number of the second segment in the original order by N and fill N bytes with pattern (default - all zero). N must be less than the first split pos.
-- arg : seqovl_pattern=<blob> . override pattern
function multidisorder(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local spos = desync.arg.pos or "2"
-- check debug to save CPU
if b_debug then DLOG("multidisorder: split pos: "..spos) end
local pos = resolve_multi_pos(data, desync.l7payload, spos)
if b_debug then DLOG("multidisorder: resolved split pos: "..table.concat(zero_based_pos(pos)," ")) end
delete_pos_1(pos) -- cannot split at the first byte
if #pos>0 then
for i=#pos,0,-1 do
local pos_start = pos[i] or 1
local pos_end = i<#pos and pos[i+1]-1 or #data
local part = string.sub(data,pos_start,pos_end)
local seqovl=0
if i==1 and desync.arg.seqovl then
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
if not seqovl then
DLOG("multidisorder: seqovl cancelled because could not resolve marker '"..desync.arg.seqovl.."'")
seqovl = 0
else
seqovl = seqovl - 1
if seqovl>=(pos[1]-1) then
DLOG("multidisorder: seqovl cancelled because seqovl "..seqovl.." is not less than the first split pos "..(pos[1]-1))
seqovl = 0
else
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
part = pattern(pat,1,seqovl)..part
end
end
end
if b_debug then DLOG("multidisorder: sending part "..(i+1).." "..(pos_start-1).."-"..(pos_end-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,pos_start-1-seqovl) then
return VERDICT_PASS
end
end
replay_drop_set(desync)
return VERDICT_DROP
else
DLOG("multidisorder: no valid split positions")
end
else
DLOG("multidisorder: not acting on further replay pieces")
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
end
end
end
-- nfqws1 : "--dpi-desync=hostfakesplit"
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
-- arg : host=<str> - hostname template. generate hosts like "random.template". example : e8nzn.vk.com
-- arg : midhost=<posmarker> - additionally split segment containing host at specified posmarker. must be within host+1 .. endhost-1 or split won't happen. example : "midsld"
-- arg : nofake1, nofake2 - do not send individual fakes
-- arg : disorder_after=<posmarker> - send after_host part in 2 disordered segments. if posmarker is empty string use marker "-1"
function hostfakesplit(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local pos = resolve_range(data, desync.l7payload, "host,endhost-1", true)
if pos then
if b_debug then DLOG("hostfakesplit: resolved host range: "..table.concat(zero_based_pos(pos)," ")) end
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
local part, fakehost
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
part = string.sub(data,1,pos[1]-1)
if b_debug then DLOG("hostfakesplit: sending before_host part 0-"..(pos[1]-2).." len="..#part.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,0, opts_orig) then return VERDICT_PASS end
fakehost = genhost(pos[2]-pos[1]+1, desync.arg.host)
if not desync.arg.nofake1 then
if b_debug then DLOG("hostfakesplit: sending fake host part (1) "..(pos[1]-1).."-"..(pos[2]-1).." len="..#fakehost.." : "..hexdump_dlog(fakehost)) end
if not rawsend_payload_segmented(desync,fakehost,pos[1]-1, opts_fake) then return VERDICT_PASS end
end
local midhost
if desync.arg.midhost then
midhost = resolve_pos(data,desync.l7payload,desync.arg.midhost)
if not midhost then
DLOG("hostfakesplit: cannot resolve midhost marker '"..desync.arg.midhost.."'")
end
DLOG("hosfakesplit: midhost marker resolved to "..midhost)
if midhost<=pos[1] or midhost>pos[2] then
DLOG("hostfakesplit: midhost is not inside the host range")
midhost = nil
end
end
-- if present apply ipfrag only to real host parts. fakes and parts outside of the host must be visible to DPI.
if midhost then
part = string.sub(data,pos[1],midhost-1)
if b_debug then DLOG("hostfakesplit: sending real host part 1 "..(pos[1]-1).."-"..(midhost-2).." len="..#part.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,pos[1]-1, opts_orig) then return VERDICT_PASS end
part = string.sub(data,midhost,pos[2])
if b_debug then DLOG("hostfakesplit: sending real host part 2 "..(midhost-1).."-"..(pos[2]-1).." len="..#part.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,midhost-1, opts_orig) then return VERDICT_PASS end
else
part = string.sub(data,pos[1],pos[2])
if b_debug then DLOG("hostfakesplit: sending real host part "..(pos[1]-1).."-"..(pos[2]-1).." len="..#part.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,pos[1]-1, opts_orig) then return VERDICT_PASS end
end
if not desync.arg.nofake2 then
if b_debug then DLOG("hostfakesplit: sending fake host part (2) "..(pos[1]-1).."-"..(pos[2]-1).." len="..#fakehost.." : "..hexdump_dlog(fakehost)) end
if not rawsend_payload_segmented(desync,fakehost,pos[1]-1, opts_fake) then return VERDICT_PASS end
end
local disorder_after_pos
if desync.arg.disorder_after then
disorder_after_pos = resolve_pos(data, desync.l7payload, desync.arg.disorder_after=="" and "-1" or desync.arg.disorder_after)
if disorder_after_pos then
-- pos[2] points to the last letter of the host starting from 1
if disorder_after_pos<=(pos[2]+1) then
DLOG("hostfakesplit: disorder_after marker '"..(disorder_after_pos-1).."' resolved to pos not after after_host pos "..pos[2])
disorder_after_pos = nil
end
else
DLOG("hostfakesplit: could not resolve disorder_after marker '"..desync.arg.disorder_after.."'")
end
end
if disorder_after_pos then
part = string.sub(data,disorder_after_pos)
if b_debug then DLOG("hostfakesplit: sending after_host part (2) "..(disorder_after_pos-1).."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,disorder_after_pos-1, opts_orig) then return VERDICT_PASS end
part = string.sub(data,pos[2]+1,disorder_after_pos-1)
if b_debug then DLOG("hostfakesplit: sending after_host part (1) "..pos[2].."-"..(disorder_after_pos-2).." len="..#part.." : "..hexdump_dlog(part)) end
else
part = string.sub(data,pos[2]+1)
if b_debug then DLOG("hostfakesplit: sending after_host part "..pos[2].."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
end
if not rawsend_payload_segmented(desync,part,pos[2], opts_orig) then return VERDICT_PASS end
replay_drop_set(desync)
return VERDICT_DROP
else
DLOG("hostfakesplit: host range cannot be resolved")
end
else
DLOG("hostfakesplit: not acting on further replay pieces")
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
end
end
end
-- nfqws1 : "--dpi-desync=fakedsplit"
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
-- arg : pos=<posmarker> - split position marker
-- arg : nofake1, nofake2, nofake3, nofake4 - do not send individual fakes
-- arg : pattern=<blob> . fill fake parts with this pattern
-- arg : seqovl=N . decrease seq number of the first segment by N and fill N bytes with pattern (default - all zero)
-- arg : seqovl_pattern=<blob> . override seqovl pattern
function fakedsplit(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local spos = desync.arg.pos or "2"
local pos = resolve_pos(data, desync.l7payload, spos)
if pos then
if pos == 1 then
DLOG("multidisorder: split pos resolved to 0. cannot split.")
else
if b_debug then DLOG("fakedsplit: resolved split pos: "..tostring(pos-1)) end
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
local fake, fakepat, part, pat
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
fakepat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
-- first fake
fake = pattern(fakepat,1,pos-1)
if not desync.arg.nofake1 then
if b_debug then DLOG("fakedsplit: sending fake part 1 (1) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
end
-- first real
part = string.sub(data,1,pos-1)
local seqovl=0
if desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
seqovl = tonumber(desync.arg.seqovl)
pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
part = pattern(pat,1,seqovl)..part
end
if b_debug then DLOG("fakedsplit: sending real part 1 : 0-"..(pos-2).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,-seqovl, opts_orig) then return VERDICT_PASS end
-- first fake again
if not desync.arg.nofake2 then
if b_debug then DLOG("fakedsplit: sending fake part 1 (2) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
end
-- second fake
fake = pattern(fakepat,pos,#data-pos+1)
if not desync.arg.nofake3 then
if b_debug then DLOG("fakedsplit: sending fake part 2 (1) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
end
-- second real
part = string.sub(data,pos)
if b_debug then DLOG("fakedsplit: sending real part 2 : "..(pos-1).."-"..(#data-1).." len="..#part.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,pos-1, opts_orig) then return VERDICT_PASS end
-- second fake again
if not desync.arg.nofake4 then
if b_debug then DLOG("fakedsplit: sending fake part 2 (2) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
end
replay_drop_set(desync)
return VERDICT_DROP
end
else
DLOG("fakedsplit: cannot resolve pos '"..desync.arg.pos.."'")
end
else
DLOG("fakedsplit: not acting on further replay pieces")
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
end
end
end
-- nfqws1 : "--dpi-desync=fakeddisorder"
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct. FOOLING AND REPEATS APPLIED ONLY TO FAKES.
-- arg : pos=<posmarker> - split position marker
-- arg : nofake1, nofake2, nofake3, nofake4 - do not send individual fakes
-- arg : pattern=<blob> . fill fake parts with this pattern
-- arg : seqovl=N . decrease seq number of the second segment by N and fill N bytes with pattern (default - all zero). N must be less than the split pos.
-- arg : seqovl_pattern=<blob> . override seqovl pattern
function fakeddisorder(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
local spos = desync.arg.pos or "2"
local pos = resolve_pos(data, desync.l7payload, spos)
if pos then
if pos == 1 then
DLOG("multidisorder: split pos resolved to 0. cannot split.")
else
if b_debug then DLOG("fakeddisorder: resolved split pos: "..tostring(pos-1)) end
-- do not apply fooling to original parts except tcp_ts_up but apply ip_id
local fake, part, pat
local opts_orig = {rawsend = rawsend_opts_base(desync), reconstruct = {}, ipfrag = {}, ipid = desync.arg, fooling = {tcp_ts_up = desync.arg.tcp_ts_up}}
local opts_fake = {rawsend = rawsend_opts(desync), reconstruct = reconstruct_opts(desync), ipfrag = {}, ipid = desync.arg, fooling = desync.arg}
fakepat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
-- second fake
fake = pattern(fakepat,pos,#data-pos+1)
if not desync.arg.nofake1 then
if b_debug then DLOG("fakeddisorder: sending fake part 2 (1) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
end
-- second real
part = string.sub(data,pos)
local seqovl = 0
if desync.arg.seqovl then
seqovl = resolve_pos(data, desync.l7payload, desync.arg.seqovl)
if seqovl then
seqovl = seqovl - 1
if seqovl>=(pos-1) then
DLOG("fakeddisorder: seqovl cancelled because seqovl "..seqovl.." is not less than the split pos "..(pos-1))
seqovl = 0
else
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
part = pattern(pat,1,seqovl)..part
end
else
DLOG("fakeddisorder: seqovl cancelled because could not resolve marker '"..desync.arg.seqovl.."'")
seqovl = 0
end
end
if b_debug then DLOG("fakeddisorder: sending real part 2 : "..(pos-1).."-"..(#data-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,pos-1-seqovl, opts_orig) then return VERDICT_PASS end
-- second fake again
if not desync.arg.nofake2 then
if b_debug then DLOG("fakeddisorder: sending fake part 2 (2) : "..(pos-1).."-"..(#data-1).." len="..#fake.." : "..hexdump_dlog(fake)) end
if not rawsend_payload_segmented(desync,fake,pos-1, opts_fake) then return VERDICT_PASS end
end
-- first fake
fake = pattern(fakepat,1,pos-1)
if not desync.arg.nofake3 then
if b_debug then DLOG("fakeddisorder: sending fake part 1 (1) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
end
-- first real
part = string.sub(data,1,pos-1)
if b_debug then DLOG("fakeddisorder: sending real part 1 : 0-"..(pos-2).." len="..#part.." : "..hexdump_dlog(part)) end
if not rawsend_payload_segmented(desync,part,0, opts_orig) then return VERDICT_PASS end
-- first fake again
if not desync.arg.nofake4 then
if b_debug then DLOG("fakeddisorder: sending fake part 1 (2) : 0-"..(pos-2).." len="..#fake.." : "..hexdump_dlog(fake)) end
if not rawsend_payload_segmented(desync,fake,0, opts_fake) then return VERDICT_PASS end
end
replay_drop_set(desync)
return VERDICT_DROP
end
else
DLOG("fakeddisorder: cannot resolve pos '"..desync.arg.pos.."'")
end
else
DLOG("fakeddisorder: not acting on further replay pieces")
end
-- drop replayed packets if reasm was sent successfully in splitted form
if replay_drop(desync) then
return VERDICT_DROP
end
end
end
-- nfqws1 : not available
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
-- arg : pos=<postmarker list> . position marker list. 2 pos required, only 2 first pos used. example : "host,endhost"
-- arg : seqovl=N . decrease seq number of the first segment by N and fill N bytes with pattern (default - all zero)
-- arg : seqovl_pattern=<blob> . override pattern
function tcpseg(ctx, desync)
if not desync.dis.tcp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
if not desync.arg.pos then
error("tcpseg: no pos specified")
end
-- by default process only outgoing known payloads
local data = desync.reasm_data or desync.dis.payload
if #data>0 and direction_check(desync) and payload_check(desync) then
if replay_first(desync) then
if b_debug then DLOG("tcpseg: pos: "..desync.arg.pos) end
-- always returns 2 positions or nil or causes error
local pos = resolve_range(data, desync.l7payload, desync.arg.pos)
if pos then
-- check debug to save CPU
if b_debug then DLOG("tcpseg: resolved range: "..table.concat(zero_based_pos(pos)," ")) end
local part = string.sub(data,pos[1],pos[2])
local seqovl=0
if desync.arg.seqovl and tonumber(desync.arg.seqovl)>0 then
seqovl = tonumber(desync.arg.seqovl)
local pat = desync.arg.seqovl_pattern and blob(desync,desync.arg.seqovl_pattern) or "\x00"
part = pattern(pat,1,seqovl)..part
end
if b_debug then DLOG("tcpseg: sending "..(pos[1]-1).."-"..(pos[2]-1).." len="..#part.." seqovl="..seqovl.." : "..hexdump_dlog(part)) end
rawsend_payload_segmented(desync,part,pos[1]-1-seqovl)
else
DLOG("tcpseg: range cannot be resolved")
end
else
DLOG("tcpseg: not acting on further replay pieces")
end
end
end
-- nfqws1 : "--dpi-desync=udplen"
-- standard args : direction, payload
-- arg : min=N . do not act on payloads smaller than N bytes
-- arg : max=N . do not act on payloads larger than N bytes
-- arg : increment=N . 2 by default. negative increment shrinks the packet, positive grows it.
-- arg : pattern=<blob> . used to fill extra bytes when length increases
-- arg : pattern_offset=N . offset in the pattern. 0 by default
function udplen(ctx, desync)
if not desync.dis.udp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
if direction_check(desync) and payload_check(desync) then
local len = #desync.dis.payload
if (desync.arg.min and #desync.dis.payload < tonumber(desync.arg.min)) then
DLOG("udplen: payload size "..len.." is less than the minimum size "..desync.arg.min)
elseif (desync.arg.max and #desync.dis.payload > tonumber(desync.arg.max)) then
DLOG("udplen: payload size "..len.." is more than the maximum size "..desync.arg.max)
else
local inc = desync.arg.increment and tonumber(desync.arg.increment) or 2
if inc>0 then
local pat = desync.arg.pattern and blob(desync,desync.arg.pattern) or "\x00"
local pat_offset = desync.arg.pattern_offset and (tonumber(desync.arg.pattern_offset)+1) or 1
desync.dis.payload = desync.dis.payload .. pattern(pat, pat_offset, inc)
DLOG("udplen: "..len.." => "..#desync.dis.payload)
return VERDICT_MODIFY
elseif inc<0 then
if (len+inc)<1 then
DLOG("udplen: will not shrink to zero length")
else
desync.dis.payload = string.sub(desync.dis.payload,1,len+inc)
DLOG("udplen: "..len.." => "..#desync.dis.payload)
end
return VERDICT_MODIFY
end
end
end
end
-- nfqws1 : "--dpi-desync=tamper" for dht proto
-- standard args : direction
-- arg : dn=N - message starts from "dN". 2 by default
function dht_dn(ctx, desync)
if not desync.dis.udp then
instance_cutoff(ctx)
return
end
direction_cutoff_opposite(ctx, desync)
if desync.l7payload=="dht" and direction_check(desync) then
local N = tonumber(desync.arg.dn) or 2
-- remove "d1" from the start not breaking bencode
local prefix = "d"..tostring(N)..":"..string.rep("0",N).."1:x"
desync.dis.payload = prefix..string.sub(desync.dis.payload,2)
DLOG("dht_dn: tampered dht to start with '"..prefix.."' instead of 'd1:'")
return VERDICT_MODIFY
end
end

980
lua/zapret-lib.lua Normal file
View File

@@ -0,0 +1,980 @@
HEXDUMP_DLOG_MAX = HEXDUMP_DLOG_MAX or 32
NOT3=bitnot(3)
NOT7=bitnot(7)
math.randomseed(os.time())
-- prepare standard rawsend options from desync
-- repeats - how many time send the packet
-- ifout - override outbound interface (if --bind_fix4, --bind-fix6 enabled)
-- fwmark - override fwmark. desync mark bit(s) will be set unconditionally
function rawsend_opts(desync)
return {
repeats = desync.arg.repeats,
ifout = desync.arg.ifout or desync.ifout,
fwmark = desync.arg.fwmark or desync.fwmark
}
end
-- only basic options. no repeats
function rawsend_opts_base(desync)
return {
ifout = desync.arg.ifout or desync.ifout,
fwmark = desync.arg.fwmark or desync.fwmark
}
end
-- prepare standard reconstruct options from desync
-- badsum - make L4 checksum invalid
-- ip6_preserve_next - use next protocol fields from dissect, do not auto fill values. can be set from code only, not from args
-- ip6_last_proto - last ipv6 "next" protocol. used only by "reconstruct_ip6hdr". can be set from code only, not from args
function reconstruct_opts(desync)
return {
badsum = desync.arg.badsum
}
end
-- combined desync opts
function desync_opts(desync)
return {
rawsend = rawsend_opts(desync),
reconstruct = reconstruct_opts(desync),
ipfrag = desync.arg,
ipid = desync.arg,
fooling = desync.arg
}
end
-- convert binary string to hex data
function string2hex(s)
local ss = ""
for i = 1, #s do
if i>1 then
ss = ss .. " "
end
ss = ss .. string.format("%02X", string.byte(s, i))
end
return ss
end
function has_nonprintable(s)
return s:match("[^ -\\r\\n\\t]")
end
function make_readable(v)
if type(v)=="string" then
return string.gsub(v,"[^ -]",".");
else
return tostring(v)
end
end
-- return hex dump of a binary string if it has nonprintable characters or string itself otherwise
function str_or_hex(s)
if has_nonprintable(s) then
return string2hex(s)
else
return s
end
end
-- print to DLOG any variable. tables are expanded in the tree form, unprintables strings are hex dumped
function var_debug(v)
local function dbg(v,level)
if type(v)=="table" then
for key, value in pairs(v) do
DLOG(string.rep(" ",2*level).."."..key)
dbg(v[key],level+1)
end
elseif type(v)=="string" then
DLOG(string.rep(" ",2*level)..type(v).." "..str_or_hex(v))
else
DLOG(string.rep(" ",2*level)..type(v).." "..make_readable(v))
end
end
dbg(v,0)
end
-- make hex dump
function hexdump(s,max)
local l = max<#s and max or #s
local ss = string.sub(s,1,l)
return string2hex(ss)..(#s>max and " ... " or " " )..make_readable(ss)..(#s>max and " ... " or "" )
end
-- make hex dump limited by HEXDUMP_DLOG_MAX chars
function hexdump_dlog(s)
return hexdump(s,HEXDUMP_DLOG_MAX)
end
-- make copy of an array recursively
function deepcopy(orig, copies)
copies = copies or {}
local orig_type = type(orig)
local copy
if orig_type == 'table' then
if copies[orig] then
copy = copies[orig]
else
copy = {}
copies[orig] = copy
for orig_key, orig_value in next, orig, nil do
copy[deepcopy(orig_key, copies)] = deepcopy(orig_value, copies)
end
setmetatable(copy, deepcopy(getmetatable(orig), copies))
end
else -- number, string, boolean, etc
copy = orig
end
return copy
end
-- check if string 'v' in comma separated list 's'
function in_list(s, v)
if s then
for elem in string.gmatch(s, "[^,]+") do
if elem==v then
return true
end
end
end
return false
end
-- blobs can be 0xHEX, field name in desync or global var
-- if name is nil - return def
function blob(desync, name, def)
if not name or #name==0 then
if def then
return def
else
error("empty blob name")
end
end
local blob
if string.sub(name,1,2)=="0x" then
blob = parse_hex(string.sub(name,3))
if not blob then
error("invalid hex string : "..name)
end
else
blob = desync[name]
if not blob then
-- use global var if no field in dissect table
blob = _G[name]
if not blob then
error("blob '"..name.."' unavailable")
end
end
end
return blob
end
-- repeat pattern as needed to extract part of it with any length
-- pat="12345" len=10 offset=4 => "4512345123"
function pattern(pat, offset, len)
if not pat or #pat==0 then
error("pattern: bad or empty pattern")
end
local off = (offset-1) % #pat
local pats = divint((len + #pat - 1), #pat) + (off==0 and 0 or 1)
return string.sub(string.rep(pat,pats),off+1,off+len)
end
-- decrease by 1 all number values in the array
function zero_based_pos(a)
if not a then return nil end
local b={}
for i,v in ipairs(a) do
b[i] = type(a[i])=="number" and a[i] - 1 or a[i]
end
return b
end
-- delete elements with number value 1
function delete_pos_1(a)
local i=1
while i<=#a do
if type(a[i])=="number" and a[i] == 1 then
table.remove(a,i)
else
i = i+1
end
end
return a
end
-- find pos of the next eol and pos of the next non-eol character after eol
function find_next_line(s, pos)
local p1, p2
p1 = string.find(s,"[\r\n]",pos)
if p1 then
p2 = p1
p1 = p1-1
if string.sub(s,p2,p2)=='\r' then p2=p2+1 end
if string.sub(s,p2,p2)=='\n' then p2=p2+1 end
if p2>#s then p2=nil end
else
p1 = #s
end
return p1,p2
end
function http_dissect_header(header)
local p1,p2
p1,p2 = string.find(header,":")
if p1 then
p2=string.find(header,"[^ \t]",p2+1)
return string.sub(header,1,p1-1), p2 and string.sub(header,p2) or "", p1-1, p2 or #header
end
return nil
end
-- make table with structured http header representation
function http_dissect_headers(http, pos)
local eol,pnext,header,value,idx,headers,pos_endheader,pos_startvalue
headers={}
while pos do
eol,pnext = find_next_line(http,pos)
header = string.sub(http,pos,eol)
if #header == 0 then break end
header,value,pos_endheader,pos_startvalue = http_dissect_header(header)
if header then
headers[string.lower(header)] = { header = header, value = value, pos_start = pos, pos_end = eol, pos_header_end = pos+pos_endheader-1, pos_value_start = pos+pos_startvalue-1 }
end
pos=pnext
end
return headers
end
-- make table with structured http request representation
function http_dissect_req(http)
if not http then return nil; end
local eol,pnext,req,hdrpos
local pos=1
-- skip methodeol empty line(s)
while pos do
eol,pnext = find_next_line(http,pos)
req = string.sub(http,pos,eol)
pos=pnext
if #req>0 then break end
end
hdrpos = pos
if not req or #req==0 then return nil end
pos = string.find(req,"[ \t]")
if not pos then return nil end
local method = string.sub(req,1,pos-1);
pos = string.find(req,"[^ \t]",pos+1)
if not pos then return nil end
pnext = string.find(req,"[ \t]",pos+1)
if not pnext then pnext = #http + 1 end
local uri = string.sub(req,pos,pnext-1)
return { method = method, uri = uri, headers = http_dissect_headers(http,hdrpos) }
end
-- convert comma separated list of tcp flags to tcp.th_flags bit field
function parse_tcp_flags(s)
local flags={FIN=TH_FIN, SYN=TH_SYN, RST=TH_RST, PSH=TH_PUSH, PUSH=TH_PUSH, ACK=TH_ACK, URG=TH_URG, ECE=TH_ECE, CWR=TH_CWR}
local f=0
local s_upper = string.upper(s)
for flag in string.gmatch(s_upper, "[^,]+") do
if flags[flag] then
f = bitor(f,flags[flag])
else
error("tcp flag '"..flag.."' is invalid")
end
end
return f
end
-- find first tcp options of specified kind in dissect.tcp.options
function find_tcp_option(options, kind)
if options then
for i, opt in pairs(options) do
if opt.kind==kind then return i end
end
end
return nil
end
-- find first ipv6 extension header of specified protocol in dissect.ip6.exthdr
function find_ip6_exthdr(exthdr, proto)
if exthdr then
for i, hdr in pairs(exthdr) do
if hdr.type==proto then return i end
end
end
return nil
end
-- insert ipv6 extension header at specified index. fix next proto chain
function insert_ip6_exthdr(ip6, idx, header_type, data)
local prev
if not ip6.exthdr then ip6.exthdr={} end
if not idx then
-- insert to the end
idx = #ip6.exthdr+1
elseif idx<0 or idx>(#ip6.exthdr+1) then
error("insert_ip6_exthdr: invalid index "..idx)
end
if idx==1 then
prev = ip6.ip6_nxt
ip6.ip6_nxt = header_type
else
prev = ip6.exthdr[idx-1].next
ip6.exthdr[idx-1].next = header_type
end
table.insert(ip6.exthdr, idx, {type = header_type, data = data, next = prev})
end
-- delete ipv6 extension header at specified index. fix next proto chain
function del_ip6_exthdr(ip6, idx)
if idx<=0 or idx>#ip6.exthdr then
error("delete_ip6_exthdr: nonexistent index "..idx)
end
local nxt = ip6.exthdr[idx].next
if idx==1 then
ip6.ip6_nxt = nxt
else
ip6.exthdr[idx-1].next = nxt
end
table.remove(ip6.exthdr, idx)
end
-- fills next proto fields in ipv6 header and extension headers
function fix_ip6_next(ip6, last_proto)
if ip6.exthdr and #ip6.exthdr>0 then
for i=1,#ip6.exthdr do
if i==1 then
-- first header
ip6.ip6_nxt = ip6.exthdr[i].type
end
ip6.exthdr[i].next = i==#ip6.exthdr and (last_proto or IPPROTO_NONE) or ip6.exthdr[i+1].type
end
else
-- no headers
ip6.ip6_nxt = last_proto or IPPROTO_NONE
end
end
-- parse autottl : delta,min-max
function parse_autottl(s)
if s then
local delta,min,max = string.match(s,"([-+]?%d+),(%d+)-(%d+)")
min = tonumber(min)
max = tonumber(max)
delta = tonumber(delta)
if not delta or min>max then
error("parse_autottl: invalid value '"..s.."'")
end
return {delta=delta,min=min,max=max}
else
return nil
end
end
-- calculate ttl value based on incoming_ttl and parsed attl definition (delta,min-max)
function autottl(incoming_ttl, attl)
local function hop_count_guess(incoming_ttl)
-- 18.65.168.125 ( cloudfront ) 255
-- 157.254.246.178 128
-- 1.1.1.1 64
-- guess original ttl. consider path lengths less than 32 hops
local orig
if incoming_ttl>223 then
orig=255
elseif incoming_ttl<128 and incoming_ttl>96 then
orig=128
elseif incoming_ttl<64 and incoming_ttl>32 then
orig=64
else
return nil
end
return orig-incoming_ttl
end
-- return guessed fake ttl value. 0 means unsuccessfull, should not perform autottl fooling
local function autottl_eval(hop_count, attl)
local d,fake
d = hop_count + attl.delta
if d<attl.min then fake=attl.min
elseif d>attl.max then fake=attl.max
else fake=d
end
if attl.delta<0 and fake>=hop_count or attl.delta>=0 and fake<hop_count then return nil end
return fake
end
local hops = hop_count_guess(incoming_ttl)
if not hops then return nil end
return autottl_eval(hops,attl)
end
-- apply standard header mods :
-- ip_ttl=N - set ipv.ip_ttl to N
-- ip6_ttl=N - set ip6.ip6_hlim to N
-- ip_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
-- ip6_autottl=delta,min-max - set ip.ip_ttl to auto discovered ttl
-- ip6_hopbyhop[=hex] - add hopbyhop ipv6 header with optional data. data size must be 6+N*8. all zero by default.
-- ip6_hopbyhop2 - add 2 hopbyhop ipv6 headers with optional data. data size must be 6+N*8. all zero by default.
-- ip6_destopt[=hex] - add destopt ipv6 header with optional data. data size must be 6+N*8. all zero by default.
-- ip6_routing[=hex] - add routing ipv6 header with optional data. data size must be 6+N*8. all zero by default.
-- ip6_ah[=hex] - add authentication ipv6 header with optional data. data size must be 6+N*4. 0000 + 4 random bytes by default.
-- tcp_seq=N - add N to tcp.th_seq
-- tcp_ack=N - add N to tcp.th_ack
-- tcp_ts=N - add N to timestamp value
-- tcp_md5[=hex] - add MD5 header with optional 16-byte data. all zero by default.
-- tcp_flags_set=<list> - set tcp flags in comma separated list
-- tcp_unflags_set=<list> - unset tcp flags in comma separated list
-- tcp_ts_up - move timestamp tcp option to the top if it's present. this allows linux not to accept badack segments without badseq. this is very strange discovery but it works.
-- fool - custom fooling function : fool_func(dis, fooling_options)
function apply_fooling(desync, dis, fooling_options)
local function prepare_bin(hex,def)
local bin = parse_hex(hex)
if not bin then error("apply_fooling: invalid hex string '"..hex.."'") end
return #bin>0 and bin or def
end
local function ttl_discover(arg_ttl,arg_autottl)
local ttl
if arg_autottl and desync.track then
if desync.track.incoming_ttl then
-- use lua_cache to store discovered autottl
if type(desync.track.lua_state.autottl_cache)~="table" then desync.track.lua_state.autottl_cache={} end
if type(desync.track.lua_state.autottl_cache[desync.func_instance])~="table" then desync.track.lua_state.autottl_cache[desync.func_instance]={} end
if not desync.track.lua_state.autottl_cache[desync.func_instance].autottl_found then
desync.track.lua_state.autottl_cache[desync.func_instance].autottl = autottl(desync.track.incoming_ttl,parse_autottl(arg_autottl))
if desync.track.lua_state.autottl_cache[desync.func_instance].autottl then
desync.track.lua_state.autottl_cache[desync.func_instance].autottl_found = true
DLOG("apply_fooling: discovered autottl "..desync.track.lua_state.autottl_cache[desync.func_instance].autottl)
else
DLOG("apply_fooling: could not discover autottl")
end
elseif desync.track.lua_state.autottl_cache[desync.func_instance].autottl then
DLOG("apply_fooling: using cached autottl "..desync.track.lua_state.autottl_cache[desync.func_instance].autottl)
end
ttl=desync.track.lua_state.autottl_cache[desync.func_instance].autottl
else
DLOG("apply_fooling: cannot apply autottl because incoming ttl unknown")
end
end
if not ttl and tonumber(arg_ttl) then
ttl = tonumber(arg_ttl)
end
return ttl
end
local function move_ts_top()
local tsidx = find_tcp_option(dis.tcp.options, TCP_KIND_TS)
if tsidx and tsidx>1 then
table.insert(dis.tcp.options, 1, dis.tcp.options[tsidx])
table.remove(dis.tcp.options, tsidx + 1)
end
end
-- take default fooling from desync.arg
if not fooling_options then fooling_options = desync.arg end
-- use current packet if dissect not given
if not dis then dis = desync.dis end
if dis.tcp then
if tonumber(fooling_options.tcp_seq) then
dis.tcp.th_seq = dis.tcp.th_seq + fooling_options.tcp_seq
end
if tonumber(fooling_options.tcp_ack) then
dis.tcp.th_ack = dis.tcp.th_ack + fooling_options.tcp_ack
end
if fooling_options.tcp_flags_unset then
dis.tcp.th_flags = bitand(dis.tcp.th_flags, bitnot(parse_tcp_flags(fooling_options.tcp_flags_unset)))
end
if fooling_options.tcp_flags_set then
dis.tcp.th_flags = bitor(dis.tcp.th_flags, parse_tcp_flags(fooling_options.tcp_flags_set))
end
if tonumber(fooling_options.tcp_ts) then
local idx = find_tcp_option(dis.tcp.options,TCP_KIND_TS)
if idx and (dis.tcp.options[idx].data and #dis.tcp.options[idx].data or 0)==8 then
dis.tcp.options[idx].data = bu32(u32(dis.tcp.options[idx].data)+fooling_options.tcp_ts)..string.sub(dis.tcp.options[idx].data,5)
else
DLOG("apply_fooling: timestamp tcp option not present or invalid")
end
end
if fooling_options.tcp_md5 then
if find_tcp_option(dis.tcp.options,TCP_KIND_MD5) then
DLOG("apply_fooling: md5 option already present")
else
table.insert(dis.tcp.options,{kind=TCP_KIND_MD5, data=prepare_bin(fooling_options.tcp_md5,brandom(16))})
end
end
if fooling_options.tcp_ts_up then
move_ts_top(dis.tcp.options)
end
end
if dis.ip6 then
local bin
if fooling_options.ip6_hopbyhop_x2 then
bin = prepare_bin(fooling_options.ip6_hopbyhop2_x2,"\x00\x00\x00\x00\x00\x00")
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_HOPOPTS,bin)
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_HOPOPTS,bin)
elseif fooling_options.ip6_hopbyhop then
bin = prepare_bin(fooling_options.ip6_hopbyhop,"\x00\x00\x00\x00\x00\x00")
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_HOPOPTS,bin)
end
-- for possible unfragmentable part
if fooling_options.ip6_destopt then
bin = prepare_bin(fooling_options.ip6_destopt,"\x00\x00\x00\x00\x00\x00")
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_DSTOPTS,bin)
end
if fooling_options.ip6_routing then
bin = prepare_bin(fooling_options.ip6_routing,"\x00\x00\x00\x00\x00\x00")
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_ROUTING,bin)
end
-- for possible fragmentable part
if fooling_options.ip6_destopt2 then
bin = prepare_bin(fooling_options.ip6_destopt2,"\x00\x00\x00\x00\x00\x00")
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_DSTOPTS,bin)
end
if fooling_options.ip6_ah then
-- by default truncated authentication header - only 6 bytes
bin = prepare_bin(fooling_options.ip6_ah,"\x00\x00"..brandom(4))
insert_ip6_exthdr(dis.ip6,nil,IPPROTO_AH,bin)
end
end
if dis.ip then
local ttl = ttl_discover(fooling_options.ip_ttl,fooling_options.ip_autottl)
if ttl then dis.ip.ip_ttl = ttl end
end
if dis.ip6 then
local ttl = ttl_discover(fooling_options.ip6_ttl,fooling_options.ip6_autottl)
if ttl then dis.ip6.ip6_hlim = ttl end
end
if fooling_options.fool and #fooling_options.fool>0 then
if type(_G[fooling_options.fool])=="function" then
DLOG("apply_fooling: calling '"..fooling_options.fool.."'")
_G[fooling_options.fool](dis, fooling_options)
else
error("apply_fooling: fool function '"..tostring(fooling_options.fool).."' does not exist")
end
end
end
-- assign dis.ip.ip_id value according to policy in ipid_options or desync.arg. apply def or "seq" policy if no ip_id options
-- ip_id=seq|rnd|zero|none
-- ip_id_conn - in 'seq' mode save current ip_id in track.lua_state to use it between packets
-- remember ip_id in desync
function apply_ip_id(desync, dis, ipid_options, def)
-- use current packet if dissect not given
if not dis then dis = desync.dis end
if dis.ip then -- ip_id is ipv4 only, ipv6 doesn't have it
-- take default ipid options from desync.arg
if not ipid_options then ipid_options = desync.arg end
local mode = ipid_options.ip_id or def or "seq"
if mode == "seq" then
if desync.track and ipid_options.ip_id_conn then
dis.ip.ip_id = desync.track.lua_state.ip_id or dis.ip.ip_id
desync.track.lua_state.ip_id = dis.ip.ip_id + 1
else
dis.ip.ip_id = desync.ip_id or dis.ip.ip_id
desync.ip_id = dis.ip.ip_id + 1
end
elseif mode == "zero" then
dis.ip.ip_id = 0
elseif mode == "rnd" then
dis.ip.ip_id = math.random(1,0xFFFF)
end
end
end
-- return length of ipv4 or ipv6 header without options and extension headers. should be 20 for ipv4 and 40 for ipv6.
function l3_base_len(dis)
if dis.ip then
return IP_BASE_LEN
elseif dis.ip6 then
return IP6_BASE_LEN
else
return 0
end
end
-- return length of ipv4 options or summary length of all ipv6 extension headers
-- ip6_exthdr_last_idx - count lengths for headers up to this index
function l3_extra_len(dis, ip6_exthdr_last_idx)
local l=0
if dis.ip then
if dis.ip.options then
l = bitand(#dis.ip.options+3,NOT3)
end
elseif dis.ip6 and dis.ip6.exthdr then
local ct
if ip6_exthdr_last_idx and ip6_exthdr_last_idx<=#dis.ip6.exthdr then
ct = ip6_exthdr_last_idx
else
ct = #dis.ip6.exthdr
end
for i=1, ct do
if dis.ip6.exthdr[i].type == IPPROTO_AH then
-- length in 32-bit words
l = l + bitand(3+2+#dis.ip6.exthdr[i].data,NOT3)
else
-- length in 64-bit words
l = l + bitand(7+2+#dis.ip6.exthdr[i].data,NOT7)
end
end
end
return l
end
-- return length of ipv4/ipv6 header with options/extension headers
function l3_len(dis)
return l3_base_len(dis)+l3_extra_len(dis)
end
-- return length of tcp/udp headers without options. should be 20 for tcp and 8 for udp.
function l4_base_len(dis)
if dis.tcp then
return TCP_BASE_LEN
elseif dis.udp then
return UDP_BASE_LEN
else
return 0
end
end
-- return length of tcp options or 0 if not tcp
function l4_extra_len(dis)
local l=0
if dis.tcp and dis.tcp.options then
for i=1, #dis.tcp.options do
l = l + 1
if dis.tcp.options[i].kind~=TCP_KIND_NOOP and dis.tcp.options[i].kind~=TCP_KIND_END then
l = l + 1
if dis.tcp.options[i].data then l = l + #dis.tcp.options[i].data end
end
end
-- 4 byte aligned
l = bitand(3+l,NOT3)
end
return l
end
-- return length of tcp header with options or base length of udp header - 8 bytes
function l4_len(dis)
return l4_base_len(dis)+l4_extra_len(dis)
end
-- return summary extra length of ipv4/ipv6 and tcp headers. 0 if no options, no ext headers
function l3l4_extra_len(dis)
return l3_extra_len(dis)+l4_extra_len(dis)
end
-- return summary length of ipv4/ipv6 and tcp/udp headers
function l3l4_len(dis)
return l3_len(dis)+l4_len(dis)
end
-- return summary length of ipv4/ipv6 , tcp/udp headers and payload
function packet_len(dis)
return l3l4_len(dis) + #dis.payload
end
function rawsend_dissect_ipfrag(dis, options)
if options and options.ipfrag and options.ipfrag.ipfrag then
local frag_func = options.ipfrag.ipfrag=="" and "ipfrag2" or options.ipfrag.ipfrag
if type(_G[frag_func]) ~= "function" then
error("rawsend_dissect_ipfrag: ipfrag function '"..tostring(frag_func).."' does not exist")
end
local fragments = _G[frag_func](dis, options.ipfrag)
-- allow ipfrag function to do extheader magic with non-standard "next protocol"
-- NOTE : dis.ip6 must have valid next protocol fields !!!!!
local reconstruct_frag = options.reconstruct and deepcopy(options.reconstruct) or {}
reconstruct_frag.ip6_preserve_next = true
if fragments then
if options.ipfrag.ipfrag_disorder then
for i=#fragments,1,-1 do
DLOG("sending ip fragment "..i)
-- C function
if not rawsend_dissect(fragments[i], options.rawsend, reconstruct_frag) then return false end
end
else
for i, d in pairs(fragments) do
DLOG("sending ip fragment "..i)
-- C function
if not rawsend_dissect(d, options.rawsend, reconstruct_frag) then return false end
end
end
return true
end
-- ipfrag failed. send unfragmented
end
-- C function
return rawsend_dissect(dis, options and options.rawsend, options and options.reconstruct)
end
-- send dissect with tcp segmentation based on mss value. appply specified rawsend options.
function rawsend_dissect_segmented(desync, dis, mss, options)
local discopy = deepcopy(dis)
apply_ip_id(desync, discopy, options and options.ipid)
apply_fooling(desync, discopy, options and options.fooling)
if dis.tcp then
local extra_len = l3l4_extra_len(dis)
if extra_len >= mss then return false end
local max_data = mss - extra_len
if #discopy.payload > max_data then
local pos=1
local len
while pos <= #dis.payload do
len = #dis.payload - pos + 1
if len > max_data then len = max_data end
discopy.payload = string.sub(dis.payload,pos,pos+len-1)
if not rawsend_dissect_ipfrag(discopy, options) then
-- stop if failed
return false
end
discopy.tcp.th_seq = discopy.tcp.th_seq + len
pos = pos + len
end
return true
end
end
-- no reason to segment
return rawsend_dissect_ipfrag(discopy, options)
end
-- send specified payload based on existing L3/L4 headers in the dissect. add seq to tcp.th_seq.
function rawsend_payload_segmented(desync, payload, seq, options)
options = options or desync_opts(desync)
local dis = deepcopy(desync.dis)
if payload then dis.payload = payload end
if dis.tcp and seq then
dis.tcp.th_seq = dis.tcp.th_seq + seq
end
return rawsend_dissect_segmented(desync, dis, desync.tcp_mss, options)
end
-- check if desync.outgoing comply with arg.dir or def if it's not present or "out" of they are not present both. dir can be "in","out","any"
function direction_check(desync, def)
local dir = desync.arg.dir or def or "out"
return desync.outgoing and desync.arg.dir~="in" or not desync.outgoing and dir~="out"
end
-- if dir "in" or "out" cutoff current desync function from opposite direction
function direction_cutoff_opposite(ctx, desync, def)
local dir = desync.arg.dir or def or "out"
if dir=="out" then
-- cutoff in
instance_cutoff(ctx, false)
elseif dir=="in" then
-- cutoff out
instance_cutoff(ctx, true)
end
end
-- check if desync payload type comply with payload type list in arg.payload
-- if arg.payload is not present - check if desync payload is not "empty" and not "unknown" (nfqws1 behavior without "--desync-any-protocol" option)
function payload_check(desync)
if desync.arg.payload and desync.arg.payload~="known" then
if not in_list(desync.arg.payload, "all") and not in_list(desync.arg.payload, desync.l7payload) then
DLOG("payload_check: payload '"..desync.l7payload.."' does not pass '"..desync.arg.payload.."' filter")
return false
end
else
if desync.l7payload=="empty" or desync.l7payload=="unknown" then
DLOG("payload_check: payload filter accepts only known protocols")
return false
end
end
return true
end
-- return name of replay drop field in track.lua_state for the current desync function instance
function replay_drop_key(desync)
return desync.func_instance .. "_replay_drop"
end
-- set/unset replay drop flag in track.lua_state for the current desync function instance
function replay_drop_set(desync, v)
if desync.track then
if v == nil then v=true end
local rdk = replay_drop_key(desync)
if v then
if desync.replay then desync.track.lua_state[replay_drop_key] = true end
else
desync.track.lua_state[replay_drop_key] = nil
end
end
end
-- auto unset replay drop flag if desync is not replay or it's the last replay piece
-- return true if the caller should return VERDICT_DROP
function replay_drop(desync)
if desync.track then
local drop = desync.replay and desync.track.lua_state[replay_drop_key]
if not desync.replay or desync.replay_piece_last then
-- replay stopped or last piece of reasm
replay_drop_set(desync, false)
end
if drop then
DLOG("dropping replay packet because reasm was already sent")
return true
end
end
return false
end
-- true if desync is not replay or it's the first replay piece
function replay_first(desync)
return not desync.replay or desync.replay_piece==1
end
-- generate random host
-- template "google.com", len=16 : h82aj.google.com
-- template "google.com", len=11 : .google.com
-- template "google.com", len=10 : google.com
-- template "google.com", len=7 : gle.com
-- no template, len=6 : b8c54a
-- no template, len=7 : u9a.edu
-- no template, len=10 : jgha7c.com
function genhost(len, template)
if template and #template>0 then
if len <= #template then
return string.sub(template,#template-len+1)
elseif len==(#template+1) then
return "."..template
else
return brandom_az(1)..brandom_az09(len-#template-2).."."..template
end
else
if len>=7 then
local tlds = {"com","org","net","edu","gov","biz"}
local tld = tlds[math.random(#tlds)]
return brandom_az(1)..brandom_az09(len-#tld-1-1).."."..tld
else
return brandom_az(1)..brandom_az09(len-1)
end
end
end
-- arg : wsize=N . tcp window size
-- arg : scale=N . tcp option scale factor
-- return : true of changed anything
function wsize_rewrite(dis, arg)
local b = false
if arg.wsize then
local wsize = tonumber(arg.wsize)
DLOG("window size "..dis.tcp.th_win.." => "..wsize)
dis.tcp.th_win = tonumber(arg.wsize)
b = true
end
if arg.scale then
local scale = tonumber(arg.scale)
local i = find_tcp_option(dis.tcp.options, TCP_KIND_SCALE)
if i then
local oldscale = u8(dis.tcp.options[i].data)
if scale>oldscale then
DLOG("not increasing scale factor")
elseif scale<oldscale then
DLOG("scale factor "..oldscale.." => "..scale)
dis.tcp.options[i].data = bu8(scale)
b = true
end
end
end
return b
end
-- standard fragmentation to 2 ip fragments
-- function returns 2 dissects with fragments
-- option : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 8
-- option : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipb6: starting from fragmentable part. must be multiple of 8. default 32
-- option : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
-- option : ipfrag_disorder - send fragments from last to first
function ipfrag2(dis, ipfrag_options)
local function frag_idx(exthdr)
-- fragment header after hopbyhop, destopt, routing
-- allow second destopt header to be in fragmentable part
-- test case : --lua-desync=send:ipfrag:ipfrag_pos_tcp=40:ip6_hopbyhop:ip6_destopt:ip6_destopt2
-- WINDOWS may not send second ipv6 fragment with next protocol 60 (destopt)
-- test case windows : --lua-desync=send:ipfrag:ipfrag_pos_tcp=40:ip6_hopbyhop:ip6_destopt:ip6_destopt2:ipfrag_next=255
if exthdr then
local first_destopts
for i=1,#exthdr do
if exthdr[i].type==IPPROTO_DSTOPTS then
first_destopts = i
break
end
end
for i=#exthdr,1,-1 do
if exthdr[i].type==IPPROTO_HOPOPTS or exthdr[i].type==IPPROTO_ROUTING or (exthdr[i].type==IPPROTO_DSTOPTS and i==first_destopts) then
return i+1
end
end
end
return 1
end
local pos
local dis1, dis2
local l3
if dis.tcp then
pos = ipfrag_options.ipfrag_pos_tcp or 32
elseif dis.udp then
pos = ipfrag_options.ipfrag_pos_udp or 8
else
pos = ipfrag_options.ipfrag_pos or 32
end
DLOG("ipfrag2")
if not pos then
error("ipfrag2: no frag position")
end
l3 = l3_len(dis)
if bitand(pos,7)~=0 then
error("ipfrag2: frag position must be multiple of 8")
end
if (pos+l3)>0xFFFF then
error("ipfrag2: too high frag offset")
end
local plen = l3 + l4_len(dis) + #dis.payload
if (pos+l3)>=plen then
DLOG("ipfrag2: ip frag pos exceeds packet length. ipfrag cancelled.")
return nil
end
if dis.ip then
-- ipv4 frag is done by both lua and C part
-- lua code must correctly set ip_len, IP_MF and ip_off and provide full unfragmented payload
-- ip_len must be set to valid value as it would appear in the fragmented packet
-- ip_off must be set to fragment offset and IP_MF bit must be set if it's not the last fragment
-- C code constructs unfragmented packet then moves everything after ip header according to ip_off and ip_len
-- ip_id must not be zero or fragment will be dropped
local ip_id = dis.ip.ip_id==0 and math.random(1,0xFFFF) or dis.ip.ip_id
dis1 = deepcopy(dis)
-- ip_len holds the whole packet length starting from the ip header. it includes ip, transport headers and payload
dis1.ip.ip_len = l3 + pos -- ip header + first part up to frag pos
dis1.ip.ip_off = IP_MF -- offset 0, IP_MF - more fragments
dis1.ip.ip_id = ip_id
dis2 = deepcopy(dis)
dis2.ip.ip_off = bitrshift(pos,3) -- offset = frag pos, IP_MF - not set
dis2.ip.ip_len = plen - pos -- unfragmented packet length - frag pos
dis2.ip.ip_id = ip_id
end
if dis.ip6 then
-- ipv6 frag is done by both lua and C part
-- lua code must insert fragmentation extension header at any desirable position, fill fragment offset, more fragments flag and ident
-- lua must set up ip6_plen as it would appear in the fragmented packet
-- C code constructs unfragmented packet then moves fragmentable part as needed
local idxfrag = frag_idx(dis.ip6.exthdr)
local l3extra = l3_extra_len(dis, idxfrag-1) + 8 -- all ext headers before frag + 8 bytes for frag header
local ident = math.random(1,0xFFFFFFFF)
dis1 = deepcopy(dis)
insert_ip6_exthdr(dis1.ip6, idxfrag, IPPROTO_FRAGMENT, bu16(IP6F_MORE_FRAG)..bu32(ident))
dis1.ip6.ip6_plen = l3extra + pos
dis2 = deepcopy(dis)
insert_ip6_exthdr(dis2.ip6, idxfrag, IPPROTO_FRAGMENT, bu16(pos)..bu32(ident))
-- only next proto of the first fragment is considered by standard
-- fragments with non-zero offset can have different "next protocol" field
-- this can be used to evade protection systems
if ipfrag_options.ipfrag_next then
dis2.ip6.exthdr[idxfrag].next = tonumber(ipfrag_options.ipfrag_next)
end
dis2.ip6.ip6_plen = plen - IP6_BASE_LEN + 8 - pos -- packet len without frag + 8 byte frag header - ipv6 base header
end
return {dis1,dis2}
end

581
lua/zapret-tests.lua Normal file
View File

@@ -0,0 +1,581 @@
-- nfqws2 C functions tests
-- to run : --lua-init=@zapret-lib.lua --lua-init=@zapret-tests.lua --lua-init="test_all()"
function test_assert(b)
assert(b, "test failed")
end
function test_run(tests)
for k,f in pairs(tests) do
f()
end
end
function test_all()
test_run({test_crypto, test_bin, test_ipstr, test_dissect, test_csum, test_resolve, test_rawsend})
end
function test_crypto()
test_run({test_random, test_aes, test_aes_gcm, test_hkdf, test_hash})
end
function test_random()
local rnds={}
for i=1,20 do
local rnd = bcryptorandom(math.random(10,20))
print("random: "..string2hex(rnd))
test_assert(not rnds[rnd]) -- should not be repeats
rnds[rnd] = true
end
end
function test_hash()
local hashes={}
for i=1,5 do
local rnd = brandom(math.random(5,64))
print("data: "..string2hex(rnd))
for k,sha in pairs({"sha256","sha224"}) do
local hsh = hash(sha, rnd)
print(sha..": "..string2hex(hsh))
local hsh2 = hash(sha, rnd)
test_assert(hsh==hsh2)
test_assert(not hashes[hsh])
hashes[hsh] = true
end
end
end
function test_hkdf()
local nblob = 2
local okms = {}
for nsalt=1,nblob do
local salt = brandom(math.random(10,20))
for nikm=1,nblob do
local ikm = brandom(math.random(5,10))
for ninfo=1,nblob do
local info = brandom(math.random(5,10))
local okm_prev
for k,sha in pairs({"sha256","sha224"}) do
for k,okml in pairs({8, 16, 50}) do
local okm_prev
local okm
print("* hkdf "..sha)
print("salt: "..string2hex(salt))
print("ikm : "..string2hex(ikm))
print("info: "..string2hex(info))
print("okml: "..tostring(okml))
okm = hkdf(sha, salt, ikm, info, okml)
test_assert(okm)
print("okm: "..string2hex(okm))
if okms[okm] then
print("duplicate okm !")
end
okms[okm] = true
test_assert(not okm_prev or okm_prev==string.sub(okm, 1, #okm_prev))
okm_prev = okm
end
end
end
end
end
end
function test_aes()
local clear_text="test "..brandom_az09(11)
local iv, key, encrypted, decrypted
for key_size=16,32,8 do
local key = brandom(key_size)
print()
print("* aes test key_size "..tostring(key_size))
print("clear text: "..clear_text);
print("* encrypting")
encrypted = aes(true, key, clear_text)
print("encrypted: "..str_or_hex(encrypted))
print("* decrypting everything good")
decrypted = aes(false, key, encrypted)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted==clear_text)
print("* decrypting bad payload with good key")
decrypted = aes(false, key, brandom(16))
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
print("* decrypting good payload with bad key")
decrypted = aes(false, brandom(key_size), encrypted)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
end
end
function test_aes_gcm()
local authenticated_data = "authenticated message "..brandom_az09(math.random(10,50))
local clear_text="test message "..brandom_az09(math.random(10,50))
local iv, key, encrypted, atag, decrypted, atag2
for key_size=16,32,8 do
iv = brandom(12)
key = brandom(key_size)
print()
print("* aes_gcm test key_size "..tostring(key_size))
print("clear text: "..clear_text);
print("authenticated data: "..authenticated_data);
print("* encrypting")
encrypted, atag = aes_gcm(true, key, iv, clear_text, authenticated_data)
print("encrypted: "..str_or_hex(encrypted))
print("auth tag: "..string2hex(atag))
print("* decrypting everything good")
decrypted, atag2 = aes_gcm(false, key, iv, encrypted, authenticated_data)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted==clear_text)
print("auth tag: "..string2hex(atag2))
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
test_assert(atag==atag2)
print("* decrypting bad payload with good key/iv and correct authentication data")
decrypted, atag2 = aes_gcm(false, key, iv, brandom(#encrypted), authenticated_data)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
print("auth tag: "..string2hex(atag2))
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
test_assert(atag~=atag2)
print("* decrypting good payload with good key/iv and incorrect authentication data")
decrypted, atag2 = aes_gcm(false, key, iv, encrypted, authenticated_data.."x")
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted==clear_text)
print("auth tag: "..string2hex(atag2))
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
test_assert(atag~=atag2)
print("* decrypting good payload with bad key, good iv and correct authentication data")
decrypted, atag2 = aes_gcm(false, brandom(key_size), iv, encrypted, authenticated_data)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
print("auth tag: "..string2hex(atag2))
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
test_assert(atag~=atag2)
print("* decrypting good payload with good key, bad iv and correct authentication data")
decrypted, atag2 = aes_gcm(false, key, brandom(12), encrypted, authenticated_data)
print("decrypted: "..str_or_hex(decrypted))
print( decrypted==clear_text and "DECRYPT OK" or "DECRYPT ERROR" )
test_assert(decrypted~=clear_text)
print("auth tag: "..string2hex(atag2))
print( atag==atag2 and "TAG OK" or "TAG ERROR" )
test_assert(atag~=atag2)
end
end
function test_ub()
for k,f in pairs({{u8,bu8,0xFF,8}, {u16,bu16,0xFFFF,16}, {u24,bu24,0xFFFFFF,24}, {u32,bu32,0xFFFFFFFF,32}}) do
local v = math.random(0,f[3])
local pos = math.random(1,20)
local s = brandom(pos-1)..f[2](v)..brandom(20)
local v2 = f[1](s,pos)
print("u"..tostring(f[4]).." pos="..tostring(pos).." "..tostring(v).." "..tostring(v2))
test_assert(v==v2)
end
end
function test_bit()
local v, v2, v3, v4, b1, b2, pow
v = math.random(0,0xFFFFFFFFFFFF)
b1 = math.random(1,15)
v2 = bitrshift(v, b1)
pow = 2^b1
v3 = divint(v, pow)
print(string.format("rshift(0x%X,%u) = 0x%X 0x%X/%u = 0x%X", v,b1,v2, v,pow,v3))
test_assert(v2==v3)
v2 = bitlshift(v, b1)
pow = 2^b1
v3 = v * pow
print(string.format("lshift(0x%X,%u) = 0x%X 0x%X*%u = 0x%X", v,b1,v2, v,pow,v3))
test_assert(v2==v3)
v2 = math.random(0,0xFFFFFFFFFFFF)
v3 = bitxor(v, v2)
v4 = bitor(v, v2) - bitand(v, v2)
print(string.format("xor(0x%X,0x%X) = %X or/and/minus = %X", v, v2, v3, v4))
test_assert(v3==v4)
b2 = b1 + math.random(1,31)
v2 = bitget(v, b1, b2)
pow = 2^(b2-b1+1) - 1
v3 = bitand(bitrshift(v,b1), pow)
print(string.format("bitget(0x%X,%u,%u) = 0x%X bitand/bitrshift/pow = 0x%X", v, b1, b2, v2, v3))
test_assert(v2==v3)
v4 = math.random(0,pow)
v2 = bitset(v, b1, b2, v4)
v3 = bitor(bitlshift(v4, b1), bitand(v, bitnot(bitlshift(pow, b1))))
print(string.format("bitset(0x%X,%u,%u,0x%X) = 0x%X bitand/bitnot/bitlshift/pow = 0x%X", v, b1, b2, v4, v2, v3))
test_assert(v2==v3)
end
function test_bin()
test_run({test_ub, test_bit})
end
function test_ipstr()
local s_ip, ip, s_ip2
s_ip = string.format("%u.%u.%u.%u", math.random(0,255), math.random(0,255), math.random(0,255), math.random(0,255));
ip = pton(s_ip)
s_ip2 = ntop(ip)
print("IP: "..s_ip)
print("IPBIN: "..string2hex(ip))
print("IP2: "..s_ip2)
test_assert(s_ip==s_ip2)
s_ip = string.format("%x:%x:%x:%x:%x:%x:%x:%x", math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF), math.random(1,0xFFFF));
ip = pton(s_ip)
s_ip2 = ntop(ip)
print("IP: "..s_ip)
print("IPBIN: "..string2hex(ip))
print("IP2: "..s_ip2)
test_assert(s_ip==s_ip2)
end
function test_dissect()
local dis, raw1, raw2
for i=1,20 do
print("* dissect test "..tostring(i))
local ip_tcp = {
ip = {
ip_tos = math.random(0,255),
ip_id = math.random(0,0xFFFF),
ip_off = 0,
ip_ttl = math.random(0,255),
ip_p = IPPROTO_TCP,
ip_src = brandom(4),
ip_dst = brandom(4),
options = brandom(math.random(0,40))
},
tcp = {
th_sport = math.random(0,0xFFFF),
th_dport = math.random(0,0xFFFF),
th_seq = math.random(0,0xFFFFFFFF),
th_ack = math.random(0,0xFFFFFFFF),
th_x2 = math.random(0,0xF),
th_flags = math.random(0,0xFF),
th_win = math.random(0,0xFFFF),
th_urp = math.random(0,0xFFFF),
options = {
{ kind = 1 },
{ kind = 0xE0, data = brandom(math.random(1,10)) },
{ kind = 1 },
{ kind = 0xE1, data = brandom(math.random(1,10)) },
{ kind = 0 }
}
},
payload = brandom(math.random(0, 20))
}
raw1 = reconstruct_dissect(ip_tcp)
print("IP+TCP : "..string2hex(raw1))
dis1 = dissect(raw1);
raw2 = reconstruct_dissect(dis1)
dis2 = dissect(raw2);
print("IP+TCP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2)
local ip6_udp = {
ip6 = {
ip6_flow = 0x60000000 + math.random(0,0xFFFFFFF),
ip6_hlim = math.random(1,0xFF),
ip6_src = brandom(16),
ip6_dst = brandom(16),
exthdr = {
{ type = IPPROTO_HOPOPTS, data = brandom(6+8*math.random(0,2)) },
{ type = IPPROTO_AH, data = brandom(6+4*math.random(0,4)) }
}
},
udp = {
uh_sport = math.random(0,0xFFFF),
uh_dport = math.random(0,0xFFFF)
},
payload = brandom(math.random(0, 20))
}
raw1 = reconstruct_dissect(ip6_udp)
print("IP6+UDP : "..string2hex(raw1))
dis1 = dissect(raw1);
raw2 = reconstruct_dissect(dis1)
dis2 = dissect(raw2);
print("IP6+UDP2: "..string2hex(raw2))
print( raw1==raw2 and "DISSECT OK" or "DISSECT FAILED" )
test_assert(raw1==raw2)
end
end
function test_csum()
local payload = brandom(math.random(10,20))
local ip4b, ip6b, raw, tcpb, udpb, dis1, dis2
local ip = {
ip_tos = math.random(0,255),
ip_id = math.random(0,0xFFFF),
ip_len = math.random(0,0xFFFF),
ip_off = 0,
ip_ttl = math.random(0,255),
ip_p = IPPROTO_TCP,
ip_src = brandom(4),
ip_dst = brandom(4),
options = brandom(4*math.random(0,10))
}
ip4b = reconstruct_iphdr(ip)
raw = bu8(0x40 + 5 + #ip.options/4) ..
bu8(ip.ip_tos) ..
bu16(ip.ip_len) ..
bu16(ip.ip_id) ..
bu16(ip.ip_off) ..
bu8(ip.ip_ttl) ..
bu8(ip.ip_p) ..
bu16(0) ..
ip.ip_src .. ip.ip_dst ..
ip.options
raw = csum_ip4_fix(raw)
print( raw==ip4b and "IP4 RECONSTRUCT+CSUM OK" or "IP4 RECONSTRUCT+CSUM FAILED" )
test_assert(raw==ip4b)
local tcp = {
th_sport = math.random(0,0xFFFF),
th_dport = math.random(0,0xFFFF),
th_seq = math.random(0,0xFFFFFFFF),
th_ack = math.random(0,0xFFFFFFFF),
th_x2 = math.random(0,0xF),
th_flags = math.random(0,0xFF),
th_win = math.random(0,0xFFFF),
th_urp = math.random(0,0xFFFF),
options = {
{ kind = 1 },
{ kind = 0xE0, data = brandom(math.random(1,10)) },
{ kind = 1 },
{ kind = 0xE1, data = brandom(math.random(1,10)) },
{ kind = 0 }
}
}
tcpb = reconstruct_tcphdr(tcp)
raw = bu16(tcp.th_sport) ..
bu16(tcp.th_dport) ..
bu32(tcp.th_seq) ..
bu32(tcp.th_ack) ..
bu8(l4_len({tcp = tcp}) * 4 + tcp.th_x2) ..
bu8(tcp.th_flags) ..
bu16(tcp.th_win) ..
bu16(0) ..
bu16(tcp.th_urp) ..
bu8(tcp.options[1].kind)..
bu8(tcp.options[2].kind)..bu8(2 + #tcp.options[2].data)..tcp.options[2].data ..
bu8(tcp.options[3].kind)..
bu8(tcp.options[4].kind)..bu8(2 + #tcp.options[4].data)..tcp.options[4].data ..
bu8(tcp.options[5].kind)
raw = raw .. string.rep(bu8(TCP_KIND_NOOP), bitand(4-bitand(#raw,3),3))
print( raw==tcpb and "TCP RECONSTRUCT OK" or "TCP RECONSTRUCT FAILED" )
test_assert(raw==tcpb)
raw = reconstruct_dissect({ip=ip, tcp=tcp, payload=payload})
dis1 = dissect(raw)
tcpb = csum_tcp_fix(ip4b,tcpb,payload)
dis2 = dissect(ip4b..tcpb..payload)
print( dis1.tcp.th_sum==dis2.tcp.th_sum and "TCP+IP4 CSUM OK" or "TCP+IP4 CSUM FAILED" )
test_assert(dis1.tcp.th_sum==dis2.tcp.th_sum)
local ip6 = {
ip6_flow = 0x60000000 + math.random(0,0xFFFFFFF),
ip6_hlim = math.random(1,0xFF),
ip6_src = brandom(16),
ip6_dst = brandom(16),
exthdr = {
{ type = IPPROTO_HOPOPTS, data = brandom(6+8*math.random(0,2)) }
}
}
ip6.ip6_plen = packet_len({ip6=ip6,tcp=tcp,payload=payload}) - IP6_BASE_LEN
ip6b = reconstruct_ip6hdr(ip6, {ip6_last_proto=IPPROTO_TCP})
raw = bu32(ip6.ip6_flow) ..
bu16(ip6.ip6_plen) ..
bu8(ip6.exthdr[1].type) ..
bu8(ip6.ip6_hlim) ..
ip6.ip6_src .. ip6.ip6_dst ..
bu8(IPPROTO_TCP) ..
bu8((#ip6.exthdr[1].data+2)/8 - 1) ..
ip6.exthdr[1].data
print( raw==ip6b and "IP6 RECONSTRUCT OK" or "IP6 RECONSTRUCT FAILED" )
test_assert(raw==ip6b)
raw = reconstruct_dissect({ip6=ip6, tcp=tcp, payload=payload})
dis1 = dissect(raw)
tcpb = csum_tcp_fix(ip6b,tcpb,payload)
dis2 = dissect(ip6b..tcpb..payload)
print( dis1.tcp.th_sum==dis2.tcp.th_sum and "TCP+IP6 CSUM OK" or "TCP+IP6 CSUM FAILED" )
test_assert(dis1.tcp.th_sum==dis2.tcp.th_sum)
ip.ip_p = IPPROTO_UDP
ip4b = reconstruct_iphdr(ip)
ip6.ip6_plen = packet_len({ip6=ip6,udp=udp,payload=payload}) - IP6_BASE_LEN
ip6b = reconstruct_ip6hdr(ip6, {ip6_last_proto=IPPROTO_UDP})
local udp = {
uh_sport = math.random(0,0xFFFF),
uh_dport = math.random(0,0xFFFF),
uh_ulen = UDP_BASE_LEN + #payload
}
udpb = reconstruct_udphdr(udp)
raw = bu16(udp.uh_sport) ..
bu16(udp.uh_dport) ..
bu16(udp.uh_ulen) ..
bu16(0)
print( raw==udpb and "UDP RECONSTRUCT OK" or "UDP RECONSTRUCT FAILED" )
test_assert(raw==udpb)
raw = reconstruct_dissect({ip=ip, udp=udp, payload=payload})
dis1 = dissect(raw)
udpb = csum_udp_fix(ip4b,udpb,payload)
dis2 = dissect(ip4b..udpb..payload)
print( dis1.udp.uh_sum==dis2.udp.uh_sum and "UDP+IP4 CSUM OK" or "UDP+IP4 CSUM FAILED" )
test_assert(dis1.udp.uh_sum==dis2.udp.uh_sum)
raw = reconstruct_dissect({ip6=ip6, udp=udp, payload=payload})
dis1 = dissect(raw)
udpb = csum_udp_fix(ip6b,udpb,payload)
dis2 = dissect(ip6b..udpb..payload)
print( dis1.udp.uh_sum==dis2.udp.uh_sum and "UDP+IP6 CSUM OK" or "UDP+IP6 CSUM FAILED" )
test_assert(dis1.udp.uh_sum==dis2.udp.uh_sum)
end
function test_resolve()
local pos
pos = zero_based_pos(resolve_multi_pos(fake_default_tls,"tls_client_hello","1,extlen,sniext,host,sld,midsld,endsld,endhost,-5"))
test_assert(pos)
print("resolve_multi_pos tls : "..table.concat(pos," "))
pos = zero_based_pos(resolve_range(fake_default_tls,"tls_client_hello","host,endhost"))
test_assert(pos)
print("resolve_range tls : "..table.concat(pos," "))
pos = resolve_pos(fake_default_tls,"tls_client_hello","midsld")
test_assert(pos)
print("resolve_pos tls : "..pos - 1)
pos = resolve_pos(fake_default_tls,"tls_client_hello","method")
test_assert(not pos)
print("resolve_pos tls non-existent : "..tostring(pos))
pos = zero_based_pos(resolve_multi_pos(fake_default_http,"http_req","method,host,sld,midsld,endsld,endhost,-5"))
test_assert(pos)
print("resolve_multi_pos http : "..table.concat(pos," "))
pos = resolve_pos(fake_default_http,"http_req","sniext")
test_assert(not pos)
print("resolve_pos http non-existent : "..tostring(pos))
end
function test_rawsend()
local ip, ip6, udp, dis, ddis, raw_ip, raw_udp, raw
local payload = brandom(math.random(100,1200))
ip = {
ip_tos = 0,
ip_id = math.random(0,0xFFFF),
ip_off = 0,
ip_ttl = 1,
ip_p = IPPROTO_UDP,
ip_src = pton("192.168.1.1"),
ip_dst = pton("192.168.1.2")
}
udp = {
uh_sport = math.random(0,0xFFFF),
uh_dport = math.random(0,0xFFFF)
}
dis = {ip = ip, udp = udp, payload = payload}
print("send ipv4 udp")
test_assert(rawsend_dissect(dis, {repeats=3}))
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in pairs(ddis) do
print("send ipv4 udp frag "..k)
test_assert(rawsend_dissect(d))
end
raw_ip = reconstruct_iphdr(ip)
raw_udp = reconstruct_udphdr({uh_sport = udp.uh_sport, uh_dport = udp.uh_dport, uh_ulen = UDP_BASE_LEN + #payload})
raw_udp = csum_udp_fix(raw_ip,raw_udp,payload)
raw = raw_ip .. raw_udp .. payload
print("send ipv4 udp using pure rawsend without dissect")
test_assert(rawsend(raw, {repeats=5}))
ip6 = {
ip6_flow = 0x60000000,
ip6_hlim = 1,
ip6_src = pton("fdce:3124:164a:5318::1"),
ip6_dst = pton("fdce:3124:164a:5318::2")
}
dis = {ip6 = ip6, udp = udp, payload = payload}
print("send ipv6 udp")
test_assert(rawsend_dissect(dis, {repeats=3}))
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in pairs(ddis) do
print("send ipv6 udp frag "..k)
test_assert(rawsend_dissect(d))
end
ip6.exthdr={{ type = IPPROTO_HOPOPTS, data = "\x00\x00\x00\x00\x00\x00" }}
print("send ipv6 udp with hopbyhop ext header")
test_assert(rawsend_dissect(dis, {repeats=3}))
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in pairs(ddis) do
print("send ipv6 udp frag "..k.." with hopbyhop ext header")
test_assert(rawsend_dissect(d))
end
table.insert(ip6.exthdr, { type = IPPROTO_DSTOPTS, data = "\x00\x00\x00\x00\x00\x00" })
table.insert(ip6.exthdr, { type = IPPROTO_DSTOPTS, data = "\x00\x00\x00\x00\x00\x00" })
ip6.ip6_flow = 0x60001234;
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80})
for k,d in pairs(ddis) do
print("send ipv6 udp frag "..k.." with hopbyhop, destopt ext headers in unfragmentable part and another destopt ext header in fragmentable part")
test_assert(rawsend_dissect(d, {fwmark = 0x50EA}))
end
fix_ip6_next(ip6) -- required to forge next proto in the second fragment
ip6.ip6_flow = 0x6000AE38;
ddis = ipfrag2(dis, {ipfrag_pos_udp = 80, ipfrag_next = IPPROTO_TCP})
for k,d in pairs(ddis) do
print("send ipv6 udp frag "..k.." with hopbyhop, destopt ext headers in unfragmentable part and another destopt ext header in fragmentable part. forge next proto in fragment header of the second fragment to TCP")
-- reconstruct dissect using next proto fields in the dissect. do not auto fix next proto chain.
-- by default reconstruct fixes next proto chain
test_assert(rawsend_dissect(d, {fwmark = 0x409A, repeats=2}, {ip6_preserve_next = true}))
end
end

82
lua/zapret-wgobfs.lua Normal file
View File

@@ -0,0 +1,82 @@
-- test case : nfqws2 --qnum 200 --debug --lua-init=@zapret-wgobfs.lua --in-range=a --out-range=a --lua-desync=wgobfs:secret=mycoolpassword
-- encrypt standard wireguard messages - initiation, response, cookie - and change udp packet size
-- do not encrypt data messages and keepalives
-- wgobfs adds maximum of 30+padmax bytes to udp size
-- reduce MTU of wireguard interface to avoid ip fragmentation !
-- without knowing the secret encrypted packets should be crypto strong white noise with no signature
-- arg : secret - shared secret. any string. must be the same on both peers
-- arg : padmin - min random garbage bytes. 0 by default
-- arg : padmax - max random garbage bytes. 16 by default
function wgobfs(ctx, desync)
local padmin = desync.arg.padmin and tonumber(desync.arg.padmin) or 0
local padmax = desync.arg.padmax and tonumber(desync.arg.padmax) or 16
local function genkey()
-- cache key in lua_state of conntrack is present
if desync.track and desync.track.lua_state.wgobfs_key then
key = desync.track.lua_state.wgobfs_key
end
if not key then
key = hkdf("sha256", "wgobfs_salt", desync.arg.secret, nil, 16)
if desync.track then
desync.track.lua_state.wgobfs_key = key
end
end
return key
end
local function maybe_encrypted_payload(payload)
for k,plsize in pairs({2+12+16+148, 2+12+16+92, 2+12+16+64}) do
if #payload>=(plsize+padmin) and #payload<=(plsize+padmax) then
return true
end
end
return false
end
local function wg_payload_from_size(payload)
if #payload==148 then return "wireguard_initiation"
elseif #payload==92 then return "wireguard_response"
elseif #payload==64 then return "wireguard_cookie"
else return nil
end
end
if not desync.dis.udp then
instance_cutoff(ctx)
return
end
if not desync.arg.secret or #desync.arg.secret==0 then
error("wgobfs requires secret")
end
if padmin>padmax then
error("wgobfs: padmin>padmax")
end
if desync.l7payload=="wireguard_initiation" or desync.l7payload=="wireguard_response" or desync.l7payload=="wireguard_cookie" and #desync.dis.payload<65506 then
DLOG("wgobfs: encrypting '"..desync.l7payload.."'. size "..#desync.dis.payload)
local key = genkey()
-- in aes-gcm every message require it's own crypto secure random iv
-- encryption more than one message with the same iv is considered catastrophic failure
-- iv must be sent with encrypted message
local iv = bcryptorandom(12)
local encrypted, atag = aes_gcm(true, key, iv, bu16(#desync.dis.payload)..desync.dis.payload..brandom(math.random(padmin,padmax)), nil)
desync.dis.payload = iv..atag..encrypted
return VERDICT_MODIFY
end
if desync.l7payload=="unknown" and maybe_encrypted_payload(desync.dis.payload) then
local key = genkey()
local iv = string.sub(desync.dis.payload,1,12)
local atag = string.sub(desync.dis.payload,13,28)
local decrypted, atag2 = aes_gcm(false, key, iv, string.sub(desync.dis.payload,29))
if atag==atag2 then
local plen = u16(decrypted)
if plen>(#decrypted-2) then
DLOG("wgobfs: bad decrypted payload data")
else
desync.dis.payload = string.sub(decrypted, 3, 3+plen-1)
if b_debug then DLOG("wgobfs: decrypted '"..(wg_payload_from_size(desync.dis.payload) or "unknown").."' message. size "..plen) end
return VERDICT_MODIFY
end
else
DLOG("wgobfs: decrypt auth tag mismatch")
end
end
end

35
mdig/Makefile Normal file
View File

@@ -0,0 +1,35 @@
CC ?= cc
OPTIMIZE ?= -Os
CFLAGS += -std=gnu99 $(OPTIMIZE)
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_WIN = -static
LIBS = -lpthread
LIBS_ANDROID =
LIBS_WIN = -lws2_32
SRC_FILES = *.c
all: mdig
mdig: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS)
systemd: mdig
android: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS_ANDROID) $(LDFLAGS)
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS)
mac: $(SRC_FILES)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o mdiga $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o mdigx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS)
strip mdiga mdigx
lipo -create -output mdig mdigx mdiga
rm -f mdigx mdiga
win: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o mdig $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
clean:
rm -f mdig *.o

596
mdig/mdig.c Normal file
View File

@@ -0,0 +1,596 @@
// multi thread dns resolver
// domain list <stdin
// ip list >stdout
// errors, verbose >stderr
// transparent for valid ip or ip/subnet of allowed address family
#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <pthread.h>
#include <getopt.h>
#ifdef _WIN32
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x600
#include <winsock2.h>
#include <ws2ipdef.h>
#include <ws2tcpip.h>
#include <fcntl.h>
#else
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#endif
#include <time.h>
#define RESOLVER_EAGAIN_ATTEMPTS 2
static void trimstr(char *s)
{
char *p;
for (p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'); p--) *p = '\0';
}
static const char* eai_str(int r)
{
switch (r)
{
case EAI_NONAME:
return "EAI_NONAME";
case EAI_AGAIN:
return "EAI_AGAIN";
#ifdef EAI_ADDRFAMILY
case EAI_ADDRFAMILY:
return "EAI_ADDRFAMILY";
#endif
#ifdef EAI_NODATA
case EAI_NODATA:
return "EAI_NODATA";
#endif
case EAI_BADFLAGS:
return "EAI_BADFLAGS";
case EAI_FAIL:
return "EAI_FAIL";
case EAI_MEMORY:
return "EAI_MEMORY";
case EAI_FAMILY:
return "EAI_FAMILY";
case EAI_SERVICE:
return "EAI_SERVICE";
case EAI_SOCKTYPE:
return "EAI_SOCKTYPE";
#ifdef EAI_SYSTEM
case EAI_SYSTEM:
return "EAI_SYSTEM";
#endif
default:
return "UNKNOWN";
}
}
static bool dom_valid(char *dom)
{
if (!dom || *dom=='.') return false;
for (; *dom; dom++)
if (*dom < 0x20 || (*dom & 0x80) || !(*dom == '.' || *dom == '-' || *dom == '_' || (*dom >= '0' && *dom <= '9') || (*dom >= 'a' && *dom <= 'z') || (*dom >= 'A' && *dom <= 'Z')))
return false;
return true;
}
static void invalid_domain_beautify(char *dom)
{
for (int i = 0; *dom && i < 64; i++, dom++)
if (*dom < 0x20 || *dom>0x7F) *dom = '?';
if (*dom) *dom = 0;
}
#define FAMILY4 1
#define FAMILY6 2
static struct
{
char verbose;
char family;
int threads;
time_t start_time;
pthread_mutex_t flock;
pthread_mutex_t slock; // stats lock
int stats_every, stats_ct, stats_ct_ok; // stats
FILE *F_log_resolved, *F_log_failed;
} glob;
// get next domain. return 0 if failure
static char interlocked_get_dom(char *dom, size_t size)
{
char *s;
pthread_mutex_lock(&glob.flock);
s = fgets(dom, size, stdin);
pthread_mutex_unlock(&glob.flock);
if (!s) return 0;
trimstr(s);
return 1;
}
static void interlocked_fprintf(FILE *stream, const char * format, ...)
{
if (stream)
{
va_list args;
va_start(args, format);
pthread_mutex_lock(&glob.flock);
vfprintf(stream, format, args);
pthread_mutex_unlock(&glob.flock);
va_end(args);
}
}
#define ELOG(format, ...) interlocked_fprintf(stderr, "[%d] " format "\n", tid, ##__VA_ARGS__)
#define VLOG(format, ...) {if (glob.verbose) ELOG(format, ##__VA_ARGS__);}
static void print_addrinfo(struct addrinfo *ai)
{
char str[64];
while (ai)
{
switch (ai->ai_family)
{
case AF_INET:
if (inet_ntop(ai->ai_family, &((struct sockaddr_in*)ai->ai_addr)->sin_addr, str, sizeof(str)))
interlocked_fprintf(stdout, "%s\n", str);
break;
case AF_INET6:
if (inet_ntop(ai->ai_family, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, str, sizeof(str)))
interlocked_fprintf(stdout, "%s\n", str);
break;
}
ai = ai->ai_next;
}
}
static void stat_print(int ct, int ct_ok)
{
if (glob.stats_every > 0)
{
time_t tm = time(NULL)-glob.start_time;
interlocked_fprintf(stderr, "mdig stats : %02u:%02u:%02u : domains=%d success=%d error=%d\n", (unsigned int)(tm/3600), (unsigned int)((tm/60)%60), (unsigned int)(tm%60), ct, ct_ok, ct - ct_ok);
}
}
static void stat_plus(bool is_ok)
{
int ct, ct_ok;
if (glob.stats_every > 0)
{
pthread_mutex_lock(&glob.slock);
ct = ++glob.stats_ct;
ct_ok = glob.stats_ct_ok += is_ok;
pthread_mutex_unlock(&glob.slock);
if (!(ct % glob.stats_every)) stat_print(ct, ct_ok);
}
}
static uint16_t GetAddrFamily(const char *saddr)
{
struct in_addr a4;
struct in6_addr a6;
if (inet_pton(AF_INET, saddr, &a4))
return AF_INET;
else if (inet_pton(AF_INET6, saddr, &a6))
return AF_INET6;
return 0;
}
static void *t_resolver(void *arg)
{
int tid = (int)(size_t)arg;
int i, r;
char dom[256];
bool is_ok;
struct addrinfo hints;
struct addrinfo *result;
VLOG("started");
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = (glob.family == FAMILY4) ? AF_INET : (glob.family == FAMILY6) ? AF_INET6 : AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
while (interlocked_get_dom(dom, sizeof(dom)))
{
is_ok = false;
if (*dom)
{
uint16_t family;
char *s_mask, s_ip[sizeof(dom)];
strncpy(s_ip, dom, sizeof(s_ip));
s_mask = strchr(s_ip, '/');
if (s_mask) *s_mask++ = 0;
family = GetAddrFamily(s_ip);
if (family)
{
if ((family == AF_INET && (glob.family & FAMILY4)) || (family == AF_INET6 && (glob.family & FAMILY6)))
{
unsigned int mask;
bool mask_needed = false;
if (s_mask)
{
if (sscanf(s_mask, "%u", &mask)==1)
{
switch (family)
{
case AF_INET: is_ok = mask <= 32; mask_needed = mask < 32; break;
case AF_INET6: is_ok = mask <= 128; mask_needed = mask < 128; break;
}
}
}
else
is_ok = true;
if (is_ok)
interlocked_fprintf(stdout, mask_needed ? "%s/%u\n" : "%s\n", s_ip, mask);
else
VLOG("bad ip/subnet %s", dom);
}
else
VLOG("wrong address family %s", s_ip);
}
else if (dom_valid(dom))
{
VLOG("resolving %s", dom);
for (i = 0; i < RESOLVER_EAGAIN_ATTEMPTS; i++)
{
if ((r = getaddrinfo(dom, NULL, &hints, &result)))
{
VLOG("failed to resolve %s : result %d (%s)", dom, r, eai_str(r));
if (r == EAI_AGAIN) continue; // temporary failure. should retry
}
else
{
print_addrinfo(result);
freeaddrinfo(result);
is_ok = true;
}
break;
}
}
else if (glob.verbose)
{
char dom2[sizeof(dom)];
strcpy(dom2,dom);
invalid_domain_beautify(dom2);
VLOG("invalid domain : %s", dom2);
}
interlocked_fprintf(is_ok ? glob.F_log_resolved : glob.F_log_failed,"%s\n",dom);
}
stat_plus(is_ok);
}
VLOG("ended");
return NULL;
}
static int run_threads(void)
{
int i, thread;
pthread_t *t;
glob.stats_ct = glob.stats_ct_ok = 0;
time(&glob.start_time);
if (pthread_mutex_init(&glob.flock, NULL) != 0)
{
fprintf(stderr, "mutex init failed\n");
return 10;
}
if (pthread_mutex_init(&glob.slock, NULL) != 0)
{
fprintf(stderr, "mutex init failed\n");
pthread_mutex_destroy(&glob.flock);
return 10;
}
t = (pthread_t*)malloc(sizeof(pthread_t)*glob.threads);
if (!t)
{
fprintf(stderr, "out of memory\n");
pthread_mutex_destroy(&glob.slock);
pthread_mutex_destroy(&glob.flock);
return 11;
}
for (thread = 0; thread < glob.threads; thread++)
{
if (pthread_create(t + thread, NULL, t_resolver, (void*)(size_t)thread))
{
interlocked_fprintf(stderr, "failed to create thread #%d\n", thread);
break;
}
}
for (i = 0; i < thread; i++)
{
pthread_join(t[i], NULL);
}
free(t);
stat_print(glob.stats_ct, glob.stats_ct_ok);
pthread_mutex_destroy(&glob.slock);
pthread_mutex_destroy(&glob.flock);
return thread ? 0 : 12;
}
// slightly patched musl code
size_t dns_mk_query_blob(uint8_t op, const char *dname, uint8_t class, uint8_t type, uint8_t *buf, size_t buflen)
{
int i, j;
uint16_t id;
struct timespec ts;
size_t l = strnlen(dname, 255);
size_t n;
if (l && dname[l-1]=='.') l--;
if (l && dname[l-1]=='.') return 0;
n = 17+l+!!l;
if (l>253 || buflen<n || op>15u) return 0;
/* Construct query template - ID will be filled later */
memset(buf, 0, n);
buf[2] = (op<<3) | 1;
buf[5] = 1;
memcpy((char *)buf+13, dname, l);
for (i=13; buf[i]; i=j+1)
{
for (j=i; buf[j] && buf[j] != '.'; j++);
if (j-i-1u > 62u) return 0;
buf[i-1] = j-i;
}
buf[i+1] = type;
buf[i+3] = class;
/* Make a reasonably unpredictable id */
clock_gettime(CLOCK_REALTIME, &ts);
id = (uint16_t)ts.tv_nsec + (uint16_t)(ts.tv_nsec>>16);
buf[0] = id>>8;
buf[1] = id;
return n;
}
int dns_make_query(const char *dom, char family)
{
uint8_t q[280];
size_t l = dns_mk_query_blob(0, dom, 1, family == FAMILY6 ? 28 : 1, q, sizeof(q));
if (!l)
{
fprintf(stderr, "could not make DNS query\n");
return 1;
}
#ifdef _WIN32
_setmode(_fileno(stdout), _O_BINARY);
#endif
if (fwrite(q,l,1,stdout)!=1)
{
fprintf(stderr, "could not write DNS query blob to stdout\n");
return 10;
}
return 0;
}
bool dns_parse_print(const uint8_t *a, size_t len)
{
// check of minimum header length and response flag
uint16_t k, dlen, qcount = a[4]<<8 | a[5], acount = a[6]<<8 | a[7];
char s_ip[40];
if (len<12 || !(a[2]&0x80)) return false;
a+=12; len-=12;
for(k=0;k<qcount;k++)
{
while (len && *a)
{
if ((*a+1)>len) return false;
// skip to next label
len -= *a+1; a += *a+1;
}
if (len<5) return false;
// skip zero length label, type, class
a+=5; len-=5;
}
for(k=0;k<acount;k++)
{
// 11 higher bits indicate pointer
if (len<12 || (*a & 0xC0)!=0xC0) return false;
dlen = a[10]<<8 | a[11];
if (len<(dlen+12)) return false;
if (a[4]==0 && a[5]==1 && a[2]==0) // IN class and higher byte of type = 0
{
switch(a[3])
{
case 1: // A
if (dlen!=4) break;
if (inet_ntop(AF_INET, a+12, s_ip, sizeof(s_ip)))
printf("%s\n", s_ip);
break;
case 28: // AAAA
if (dlen!=16) break;
if (inet_ntop(AF_INET6, a+12, s_ip, sizeof(s_ip)))
printf("%s\n", s_ip);
break;
}
}
len -= 12+dlen; a += 12+dlen;
}
return true;
}
int dns_parse_query()
{
uint8_t a[8192];
size_t l;
#ifdef _WIN32
_setmode(_fileno(stdin), _O_BINARY);
#endif
l = fread(a,1,sizeof(a),stdin);
if (!l || !feof(stdin))
{
fprintf(stderr, "could not read DNS reply blob from stdin\n");
return 10;
}
if (!dns_parse_print(a,l))
{
fprintf(stderr, "could not parse DNS reply blob\n");
return 11;
}
return 0;
}
static void exithelp(void)
{
printf(
" --threads=<threads_number>\n"
" --family=<4|6|46>\t\t; ipv4, ipv6, ipv4+ipv6\n"
" --verbose\t\t\t; print query progress to stderr\n"
" --stats=N\t\t\t; print resolve stats to stderr every N domains\n"
" --log-resolved=<file>\t\t; log successfully resolved domains to a file\n"
" --log-failed=<file>\t\t; log failed domains to a file\n"
" --dns-make-query=<domain>\t; output to stdout binary blob with DNS query. use --family to specify ip version.\n"
" --dns-parse-query\t\t; read from stdin binary DNS answer blob and parse it to ipv4/ipv6 addresses\n"
);
exit(1);
}
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#if defined(ZAPRET_GH_VER) || defined (ZAPRET_GH_HASH)
#define PRINT_VER printf("github version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH))
#else
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
enum opt_indices {
IDX_HELP,
IDX_THREADS,
IDX_FAMILY,
IDX_VERBOSE,
IDX_STATS,
IDX_LOG_RESOLVED,
IDX_LOG_FAILED,
IDX_DNS_MAKE_QUERY,
IDX_DNS_PARSE_QUERY,
IDX_LAST,
};
static const struct option long_options[] = {
[IDX_HELP] = {"help", no_argument, 0, 0},
[IDX_THREADS] = {"threads", required_argument, 0, 0},
[IDX_FAMILY] = {"family", required_argument, 0, 0},
[IDX_VERBOSE] = {"verbose", no_argument, 0, 0},
[IDX_STATS] = {"stats", required_argument, 0, 0},
[IDX_LOG_RESOLVED] = {"log-resolved", required_argument, 0, 0},
[IDX_LOG_FAILED] = {"log-failed", required_argument, 0, 0},
[IDX_DNS_MAKE_QUERY] = {"dns-make-query", required_argument, 0, 0},
[IDX_DNS_PARSE_QUERY] = {"dns-parse-query", no_argument, 0, 0},
[IDX_LAST] = {NULL, 0, NULL, 0},
};
int main(int argc, char **argv)
{
int r, v, option_index = 0;
char fn1[256],fn2[256];
char dom[256];
memset(&glob, 0, sizeof(glob));
*fn1 = *fn2 = *dom = 0;
glob.family = FAMILY4;
glob.threads = 1;
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1)
{
if (v) exithelp();
switch (option_index)
{
case IDX_HELP:
PRINT_VER;
exithelp();
break;
case IDX_THREADS:
glob.threads = optarg ? atoi(optarg) : 0;
if (glob.threads <= 0 || glob.threads > 100)
{
fprintf(stderr, "thread number must be within 1..100\n");
return 1;
}
break;
case IDX_FAMILY:
if (!strcmp(optarg, "4"))
glob.family = FAMILY4;
else if (!strcmp(optarg, "6"))
glob.family = FAMILY6;
else if (!strcmp(optarg, "46"))
glob.family = FAMILY4 | FAMILY6;
else
{
fprintf(stderr, "ip family must be 4,6 or 46\n");
return 1;
}
break;
case IDX_VERBOSE:
glob.verbose = '\1';
break;
case IDX_STATS:
glob.stats_every = optarg ? atoi(optarg) : 0;
break;
case IDX_LOG_RESOLVED:
strncpy(fn1,optarg,sizeof(fn1));
fn1[sizeof(fn1)-1] = 0;
break;
case IDX_LOG_FAILED:
strncpy(fn2,optarg,sizeof(fn2));
fn2[sizeof(fn2)-1] = 0;
break;
case IDX_DNS_MAKE_QUERY:
strncpy(dom,optarg,sizeof(dom));
dom[sizeof(dom)-1] = 0;
break;
case IDX_DNS_PARSE_QUERY:
return dns_parse_query();
}
}
#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData))
{
fprintf(stderr,"WSAStartup failed\n");
return 4;
}
#endif
if (*dom) return dns_make_query(dom, glob.family);
if (*fn1)
{
glob.F_log_resolved = fopen(fn1,"wt");
if (!glob.F_log_resolved)
{
fprintf(stderr,"failed to create %s\n",fn1);
r=5; goto ex;
}
}
if (*fn2)
{
glob.F_log_failed = fopen(fn2,"wt");
if (!glob.F_log_failed)
{
fprintf(stderr,"failed to create %s\n",fn2);
r=5; goto ex;
}
}
r = run_threads();
ex:
if (glob.F_log_resolved) fclose(glob.F_log_resolved);
if (glob.F_log_failed) fclose(glob.F_log_failed);
return r;
}

42
nfq2/BSDmakefile Normal file
View File

@@ -0,0 +1,42 @@
CC ?= cc
OPTIMIZE ?= -Os
CFLAGS += -std=gnu99 -s $(OPTIMIZE) -flto=auto -Wno-address-of-packed-member
LIBS = -lz -lm
SRC_FILES = *.c crypto/*.c
LUA_JIT ?= 1
.if "${LUA_JIT}" == "1"
LUAJIT_VER?=2.1
LUA_VER?=5.1
LUA_PKG:=luajit
.else
LUA_VER ?= 5.4
LUA_VER_UNDOTTED!= echo $(LUA_VER) | sed 's/\.//g'
OSNAME!=uname
.if ${OSNAME} == "OpenBSD"
LUA_PKG ?= lua$(LUA_VER_UNDOTTED)
.elif ${OSNAME} == "FreeBSD"
LUA_PKG ?= lua-$(LUA_VER)
.endif
.endif
LUA_LIB!= pkg-config --libs $(LUA_PKG)
LUA_CFLAGS!= pkg-config --cflags $(LUA_PKG)
.if "${LUA_JIT}" == "1"
LUA_CFLAGS+=-DLUAJIT
.endif
all: dvtws2
dvtws2: $(SRC_FILES)
$(CC) $(CFLAGS) $(LUA_CFLAGS) -o dvtws2 $(SRC_FILES) $(LIBS) $(LUA_LIB) $(LDFLAGS)
clean:
rm -f dvtws2

152
nfq2/Makefile Normal file
View File

@@ -0,0 +1,152 @@
CC ?= cc
OPTIMIZE ?= -Os
CFLAGS += -std=gnu99 $(OPTIMIZE) -flto=auto
CFLAGS_SYSTEMD = -DUSE_SYSTEMD
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_CYGWIN = -Wno-address-of-packed-member -static
LDFLAGS_ANDROID = -llog
LIBS =
LIBS_LINUX = -lz -lnetfilter_queue -lnfnetlink -lmnl -lm
LIBS_SYSTEMD = -lsystemd
LIBS_BSD = -lz -lm
LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32
LIBS_CYGWIN32 = -lwindivert32
LIBS_CYGWIN64 = -lwindivert64
RES_CYGWIN32 = windows/res/32/winmanifest.o windows/res/32/winicon.o
RES_CYGWIN64 = windows/res/64/winmanifest.o windows/res/64/winicon.o
SRC_FILES = *.c crypto/*.c
LUA_JIT?=1
ifeq ($(LUA_JIT),1)
LUAJIT_VER?=2.1
LUAJIT_LUA_VER?=5.1
LUA_PKG:=luajit
$(info trying luajit $(LUAJIT_VER) lua $(LUAJIT_LUA_VER))
LUA_LIB_NAME=
ifeq ($(LUA_CFLAGS),)
LUA_CFLAGS := $(shell pkg-config --cflags $(LUA_PKG) 2>/dev/null)
ifeq ($(LUA_CFLAGS),)
LUA_CFLAGS := -I/usr/local/include/luajit-$(LUAJIT_VER) -I/usr/include/luajit-$(LUAJIT_VER)
endif
endif
ifeq ($(LUA_LIB),)
LUA_LIB := $(shell pkg-config --libs $(LUA_PKG) 2>/dev/null)
LUA_LIB_DIR :=
ifeq ($(LUA_LIB),)
ifneq ($(wildcard /usr/local/lib/libluajit-$(LUAJIT_LUA_VER).*),)
LUA_LIB_NAME:=luajit-$(LUAJIT_LUA_VER)
LUA_LIB_DIR:=/usr/local/lib
else ifneq ($(wildcard /usr/lib/libluajit-$(LUAJIT_LUA_VER).*),)
LUA_LIB_NAME:=luajit-$(LUAJIT_LUA_VER)
endif
ifeq ($(LUA_LIB_NAME),)
$(info could not find luajit lib name)
LUA_LIB:=
LUA_CFLAGS:=
else
ifneq ($(LUA_LIB_DIR),)
LUA_LIB = -L$(LUA_LIB_DIR)
endif
LUA_LIB += -l$(LUA_LIB_NAME)
endif
endif
endif
LUA_CFL := -DLUAJIT
else
LUA_CFL :=
endif
ifeq ($(LUA_LIB),)
# no success with luajit
LUA_VER?=5.4
LUA_VER_UNDOTTED:=$(shell echo $(LUA_VER) | sed 's/\.//g')
LUA_CFL :=
$(info trying lua $(LUA_VER))
OSNAME := $(shell uname)
ifeq ($(OSNAME),FreeBSD)
LUA_PKG:=lua-$(LUA_VER)
else
LUA_PKG:=lua$(LUA_VER_UNDOTTED)
endif
ifeq ($(LUA_CFLAGS),)
LUA_CFLAGS := $(shell pkg-config --cflags $(LUA_PKG) 2>/dev/null)
ifeq ($(LUA_CFLAGS),)
LUA_CFLAGS := -I/usr/local/include/lua$(LUA_VER) -I/usr/local/include/lua-$(LUA_VER) -I/usr/include/lua$(LUA_VER) -I/usr/include/lua-$(LUA_VER)
endif
endif
ifeq ($(LUA_LIB),)
LUA_LIB := $(shell pkg-config --libs $(LUA_PKG) 2>/dev/null)
LUA_LIB_DIR :=
ifeq ($(LUA_LIB),)
ifneq ($(wildcard /usr/local/lib/liblua-$(LUA_VER).*),)
LUA_LIB_NAME:=lua-$(LUA_VER)
LUA_LIB_DIR:=/usr/local/lib
else ifneq ($(wildcard /usr/local/lib/liblua$(LUA_VER).*),)
LUA_LIB_NAME:=lua$(LUA_VER)
LUA_LIB_DIR:=/usr/local/lib
else ifneq ($(wildcard /usr/local/lib/liblua.*),)
LUA_LIB_NAME:=lua
LUA_LIB_DIR:=/usr/local/lib
else ifneq ($(wildcard /usr/lib/liblua-$(LUA_VER).*),)
LUA_LIB_NAME:=lua-$(LUA_VER)
else ifneq ($(wildcard /usr/lib/liblua$(LUA_VER).*),)
LUA_LIB_NAME:=lua$(LUA_VER)
else ifneq ($(wildcard /usr/lib/liblua.*),)
LUA_LIB_NAME:=lua
endif
ifeq ($(LUA_LIB_NAME),)
$(error could not find lua lib name)
endif
ifneq ($(LUA_LIB_DIR),)
LUA_LIB = -L$(LUA_LIB_DIR)
endif
LUA_LIB += -l$(LUA_LIB_NAME)
endif
endif
endif
LUA_CFL += $(LUA_CFLAGS)
all: nfqws2
nfqws2: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(LUA_CFL) -o nfqws2 $(SRC_FILES) $(LIBS) $(LUA_LIB) $(LIBS_LINUX) $(LDFLAGS)
systemd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(LUA_CFL) $(CFLAGS_SYSTEMD) -o nfqws2 $(SRC_FILES) $(LIBS) $(LUA_LIB) $(LIBS_LINUX) $(LIBS_SYSTEMD) $(LDFLAGS)
android: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(LUA_CFL) -o nfqws2 $(SRC_FILES) $(LIBS) $(LUA_LIB) $(LIBS_LINUX) $(LDFLAGS) $(LDFLAGS_ANDROID)
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(LUA_CFL) $(CFLAGS_BSD) -o dvtws2 $(SRC_FILES) $(LIBS) $(LUA_LIB) $(LIBS_BSD) $(LDFLAGS)
cygwin64:
$(CC) -s $(CFLAGS) $(LUA_CFL) $(CFLAGS_CYGWIN) -o winws2 $(SRC_FILES) $(LIBS) $(LUA_LIB) $(LIBS_CYGWIN) $(LIBS_CYGWIN64) $(RES_CYGWIN64) $(LDFLAGS)
cygwin32:
$(CC) -s $(CFLAGS) $(LUA_CFL) $(CFLAGS_CYGWIN) -o winws2 $(SRC_FILES) $(LIBS) $(LUA_LIB) $(LIBS_CYGWIN) $(LIBS_CYGWIN32) $(RES_CYGWIN32) $(LDFLAGS)
cygwin: cygwin64
clean:
rm -f nfqws2 dvtws2 winws2.exe

159
nfq2/checksum.c Normal file
View File

@@ -0,0 +1,159 @@
#define _GNU_SOURCE
#include "checksum.h"
#include <netinet/in.h>
//#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
//#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
static uint16_t from64to16(uint64_t x)
{
uint32_t u = (uint32_t)(uint16_t)x + (uint16_t)(x>>16) + (uint16_t)(x>>32) + (uint16_t)(x>>48);
return (uint16_t)u + (uint16_t)(u>>16);
}
// this function preserves data alignment requirements (otherwise it will be damn slow on mips arch)
// and uses 64-bit arithmetics to improve speed
// taken from linux source code
static uint16_t do_csum(const uint8_t * buff, size_t len)
{
uint8_t odd;
size_t count;
uint64_t result,w,carry=0;
uint16_t u16;
if (!len) return 0;
odd = (uint8_t)(1 & (size_t)buff);
if (odd)
{
// any endian compatible
u16 = 0;
*((uint8_t*)&u16+1) = *buff;
result = u16;
len--;
buff++;
}
else
result = 0;
count = len >> 1; /* nr of 16-bit words.. */
if (count)
{
if (2 & (size_t) buff)
{
result += *(uint16_t *) buff;
count--;
len -= 2;
buff += 2;
}
count >>= 1; /* nr of 32-bit words.. */
if (count)
{
if (4 & (size_t) buff)
{
result += *(uint32_t *) buff;
count--;
len -= 4;
buff += 4;
}
count >>= 1; /* nr of 64-bit words.. */
if (count)
{
do
{
w = *(uint64_t *) buff;
count--;
buff += 8;
result += carry;
result += w;
carry = (w > result);
} while (count);
result += carry;
result = (result & 0xffffffff) + (result >> 32);
}
if (len & 4)
{
result += *(uint32_t *) buff;
buff += 4;
}
}
if (len & 2)
{
result += *(uint16_t *) buff;
buff += 2;
}
}
if (len & 1)
{
// any endian compatible
u16 = 0;
*(uint8_t*)&u16 = *buff;
result += u16;
}
u16 = from64to16(result);
if (odd) u16 = ((u16 >> 8) & 0xff) | ((u16 & 0xff) << 8);
return u16;
}
uint16_t csum_partial(const void *buff, size_t len)
{
return do_csum(buff,len);
}
uint16_t csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, size_t len, uint8_t proto, uint16_t sum)
{
return ~from64to16((uint64_t)saddr + daddr + sum + htonl(len+proto));
}
uint16_t ip4_compute_csum(const void *buff, size_t len)
{
return ~from64to16(do_csum(buff,len));
}
void ip4_fix_checksum(struct ip *ip)
{
ip->ip_sum = 0;
ip->ip_sum = ip4_compute_csum(ip, ip->ip_hl<<2);
}
uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8_t proto, uint16_t sum)
{
uint64_t a = (uint64_t)sum + htonl(len+proto) +
*(uint32_t*)saddr + *((uint32_t*)saddr+1) + *((uint32_t*)saddr+2) + *((uint32_t*)saddr+3) +
*(uint32_t*)daddr + *((uint32_t*)daddr+1) + *((uint32_t*)daddr+2) + *((uint32_t*)daddr+3);
return ~from64to16(a);
}
void tcp4_fix_checksum(struct tcphdr *tcp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr)
{
tcp->th_sum = 0;
tcp->th_sum = csum_tcpudp_magic(src_addr->s_addr,dest_addr->s_addr,len,IPPROTO_TCP,csum_partial(tcp,len));
}
void tcp6_fix_checksum(struct tcphdr *tcp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
{
tcp->th_sum = 0;
tcp->th_sum = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_TCP,csum_partial(tcp,len));
}
void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr)
{
if (ip)
tcp4_fix_checksum(tcp, len, &ip->ip_src, &ip->ip_dst);
else if (ip6hdr)
tcp6_fix_checksum(tcp, len, &ip6hdr->ip6_src, &ip6hdr->ip6_dst);
}
void udp4_fix_checksum(struct udphdr *udp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr)
{
udp->uh_sum = 0;
udp->uh_sum = csum_tcpudp_magic(src_addr->s_addr,dest_addr->s_addr,len,IPPROTO_UDP,csum_partial(udp,len));
}
void udp6_fix_checksum(struct udphdr *udp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr)
{
udp->uh_sum = 0;
udp->uh_sum = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_UDP,csum_partial(udp,len));
}
void udp_fix_checksum(struct udphdr *udp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr)
{
if (ip)
udp4_fix_checksum(udp, len, &ip->ip_src, &ip->ip_dst);
else if (ip6hdr)
udp6_fix_checksum(udp, len, &ip6hdr->ip6_src, &ip6hdr->ip6_dst);
}

27
nfq2/checksum.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <netinet/in.h>
#define __FAVOR_BSD
#include <netinet/ip6.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
uint16_t csum_partial(const void *buff, size_t len);
uint16_t csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, size_t len, uint8_t proto, uint16_t sum);
uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8_t proto, uint16_t sum);
uint16_t ip4_compute_csum(const void *buff, size_t len);
void ip4_fix_checksum(struct ip *ip);
void tcp4_fix_checksum(struct tcphdr *tcp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr);
void tcp6_fix_checksum(struct tcphdr *tcp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr);
void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr);
void udp4_fix_checksum(struct udphdr *udp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr);
void udp6_fix_checksum(struct udphdr *udp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr);
void udp_fix_checksum(struct udphdr *udp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr);

413
nfq2/conntrack.c Normal file
View File

@@ -0,0 +1,413 @@
#include "conntrack.h"
#include "darkmagic.h"
#include <arpa/inet.h>
#include <stdio.h>
#include "params.h"
#include "lua.h"
#undef uthash_nonfatal_oom
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt)
static bool oom = false;
static void ut_oom_recover(void *elem)
{
oom = true;
}
static const char *connstate_s[] = { "SYN","ESTABLISHED","FIN" };
static void connswap(const t_conn *c, t_conn *c2)
{
memset(c2, 0, sizeof(*c2));
c2->l3proto = c->l3proto;
c2->l4proto = c->l4proto;
c2->src = c->dst;
c2->dst = c->src;
c2->sport = c->dport;
c2->dport = c->sport;
}
void ConntrackClearHostname(t_ctrack *track)
{
free(track->hostname);
track->hostname = NULL;
track->hostname_is_ip = false;
}
static void ConntrackClearTrack(t_ctrack *track)
{
ConntrackClearHostname(track);
ReasmClear(&track->reasm_orig);
rawpacket_queue_destroy(&track->delayed);
luaL_unref(params.L, LUA_REGISTRYINDEX, track->lua_state);
luaL_unref(params.L, LUA_REGISTRYINDEX, track->lua_instance_cutoff);
}
static void ConntrackFreeElem(t_conntrack_pool *elem)
{
ConntrackClearTrack(&elem->track);
free(elem);
}
static void ConntrackPoolDestroyPool(t_conntrack_pool **pp)
{
t_conntrack_pool *elem, *tmp;
HASH_ITER(hh, *pp, elem, tmp) { HASH_DEL(*pp, elem); ConntrackFreeElem(elem); }
}
void ConntrackPoolDestroy(t_conntrack *p)
{
ConntrackPoolDestroyPool(&p->pool);
}
void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin, uint32_t timeout_udp)
{
p->timeout_syn = timeout_syn;
p->timeout_established = timeout_established;
p->timeout_fin = timeout_fin;
p->timeout_udp = timeout_udp;
p->t_purge_interval = purge_interval;
time(&p->t_last_purge);
p->pool = NULL;
}
void ConntrackExtractConn(t_conn *c, bool bReverse, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr)
{
memset(c, 0, sizeof(*c));
if (ip)
{
c->l3proto = IPPROTO_IP;
c->dst.ip = bReverse ? ip->ip_src : ip->ip_dst;
c->src.ip = bReverse ? ip->ip_dst : ip->ip_src;
}
else if (ip6)
{
c->l3proto = IPPROTO_IPV6;
c->dst.ip6 = bReverse ? ip6->ip6_src : ip6->ip6_dst;
c->src.ip6 = bReverse ? ip6->ip6_dst : ip6->ip6_src;
}
else
c->l3proto = -1;
extract_ports(tcphdr, udphdr, &c->l4proto, bReverse ? &c->dport : &c->sport, bReverse ? &c->sport : &c->dport);
}
static t_conntrack_pool *ConntrackPoolSearch(t_conntrack_pool *p, const t_conn *c)
{
t_conntrack_pool *t;
HASH_FIND(hh, p, c, sizeof(*c), t);
return t;
}
static void ConntrackInitTrack(t_ctrack *t)
{
memset(t, 0, sizeof(*t));
t->l7proto = L7_UNKNOWN;
t->scale_orig = t->scale_reply = SCALE_NONE;
time(&t->t_start);
rawpacket_queue_init(&t->delayed);
lua_newtable(params.L);
t->lua_state = luaL_ref(params.L, LUA_REGISTRYINDEX);
lua_newtable(params.L);
t->lua_instance_cutoff = luaL_ref(params.L, LUA_REGISTRYINDEX);
}
static void ConntrackReInitTrack(t_ctrack *t)
{
ConntrackClearTrack(t);
ConntrackInitTrack(t);
}
static t_conntrack_pool *ConntrackNew(t_conntrack_pool **pp, const t_conn *c)
{
t_conntrack_pool *ctnew;
if (!(ctnew = malloc(sizeof(*ctnew)))) return NULL;
ctnew->conn = *c;
oom = false;
HASH_ADD(hh, *pp, conn, sizeof(*c), ctnew);
if (oom) { free(ctnew); return NULL; }
ConntrackInitTrack(&ctnew->track);
return ctnew;
}
// non-tcp packets are passed with tcphdr=NULL but len_payload filled
static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr *tcphdr, uint32_t len_payload)
{
uint8_t scale;
uint16_t mss;
if (bReverse)
{
t->pcounter_reply++;
t->pdcounter_reply += !!len_payload;
t->pbcounter_reply += len_payload;
}
else
{
t->pcounter_orig++;
t->pdcounter_orig += !!len_payload;
t->pbcounter_orig += len_payload;
}
if (tcphdr)
{
if (tcp_syn_segment(tcphdr))
{
if (t->state != SYN) ConntrackReInitTrack(t); // erase current entry
t->seq0 = ntohl(tcphdr->th_seq);
}
else if (tcp_synack_segment(tcphdr))
{
// ignore SA dups
uint32_t seq0 = ntohl(tcphdr->th_ack) - 1;
if (t->state != SYN && t->seq0 != seq0)
ConntrackReInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = seq0;
t->ack0 = ntohl(tcphdr->th_seq);
}
else if (tcphdr->th_flags & (TH_FIN | TH_RST))
{
t->state = FIN;
}
else
{
if (t->state == SYN)
{
t->state = ESTABLISHED;
if (!bReverse && !t->ack0) t->ack0 = ntohl(tcphdr->th_ack) - 1;
}
}
scale = tcp_find_scale_factor(tcphdr);
mss = ntohs(tcp_find_mss(tcphdr));
if (bReverse)
{
t->pos_orig = t->seq_last = ntohl(tcphdr->th_ack);
t->ack_last = ntohl(tcphdr->th_seq);
t->pos_reply = t->ack_last + len_payload;
t->winsize_reply = ntohs(tcphdr->th_win);
t->winsize_reply_calc = t->winsize_reply;
if (t->scale_reply != SCALE_NONE) t->winsize_reply_calc <<= t->scale_reply;
if (mss && !t->mss_reply) t->mss_reply = mss;
if (scale != SCALE_NONE) t->scale_reply = scale;
}
else
{
t->seq_last = ntohl(tcphdr->th_seq);
t->pos_orig = t->seq_last + len_payload;
t->pos_reply = t->ack_last = ntohl(tcphdr->th_ack);
t->winsize_orig = ntohs(tcphdr->th_win);
t->winsize_orig_calc = t->winsize_orig;
if (t->scale_orig != SCALE_NONE) t->winsize_orig_calc <<= t->scale_orig;
if (mss && !t->mss_reply) t->mss_orig = mss;
if (scale != SCALE_NONE) t->scale_orig = scale;
}
}
else
{
if (bReverse)
{
t->ack_last = t->pos_reply;
t->pos_reply += len_payload;
}
else
{
t->seq_last = t->pos_orig;
t->pos_orig += len_payload;
}
}
time(&t->t_last);
}
static bool ConntrackPoolDoubleSearchPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse)
{
t_conn conn, connswp;
t_conntrack_pool *ctr;
ConntrackExtractConn(&conn, false, ip, ip6, tcphdr, udphdr);
if ((ctr = ConntrackPoolSearch(*pp, &conn)))
{
if (bReverse) *bReverse = false;
if (ctrack) *ctrack = &ctr->track;
return true;
}
else
{
connswap(&conn, &connswp);
if ((ctr = ConntrackPoolSearch(*pp, &connswp)))
{
if (bReverse) *bReverse = true;
if (ctrack) *ctrack = &ctr->track;
return true;
}
}
return false;
}
bool ConntrackPoolDoubleSearch(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse)
{
return ConntrackPoolDoubleSearchPool(&p->pool, ip, ip6, tcphdr, udphdr, ctrack, bReverse);
}
static bool ConntrackPoolFeedPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse)
{
t_conn conn, connswp;
t_conntrack_pool *ctr;
bool b_rev;
uint8_t proto = tcphdr ? IPPROTO_TCP : udphdr ? IPPROTO_UDP : IPPROTO_NONE;
ConntrackExtractConn(&conn, false, ip, ip6, tcphdr, udphdr);
if ((ctr = ConntrackPoolSearch(*pp, &conn)))
{
ConntrackFeedPacket(&ctr->track, (b_rev = false), tcphdr, len_payload);
goto ok;
}
else
{
connswap(&conn, &connswp);
if ((ctr = ConntrackPoolSearch(*pp, &connswp)))
{
ConntrackFeedPacket(&ctr->track, (b_rev = true), tcphdr, len_payload);
goto ok;
}
}
b_rev = tcphdr && tcp_synack_segment(tcphdr);
if ((tcphdr && tcp_syn_segment(tcphdr)) || b_rev || udphdr)
{
if ((ctr = ConntrackNew(pp, b_rev ? &connswp : &conn)))
{
ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload);
goto ok;
}
}
return false;
ok:
ctr->track.ipproto = proto;
if (ctrack) *ctrack = &ctr->track;
if (bReverse) *bReverse = b_rev;
return true;
}
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse)
{
return ConntrackPoolFeedPool(&p->pool, ip, ip6, tcphdr, udphdr, len_payload, ctrack, bReverse);
}
static bool ConntrackPoolDropPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr)
{
t_conn conn, connswp;
t_conntrack_pool *t;
ConntrackExtractConn(&conn, false, ip, ip6, tcphdr, udphdr);
if (!(t = ConntrackPoolSearch(*pp, &conn)))
{
connswap(&conn, &connswp);
t = ConntrackPoolSearch(*pp, &connswp);
}
if (!t) return false;
HASH_DEL(*pp, t); ConntrackFreeElem(t);
return true;
}
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr)
{
return ConntrackPoolDropPool(&p->pool, ip, ip6, tcphdr, udphdr);
}
void ConntrackPoolPurge(t_conntrack *p)
{
time_t tidle, tnow = time(NULL);
t_conntrack_pool *t, *tmp;
if ((tnow - p->t_last_purge) >= p->t_purge_interval)
{
HASH_ITER(hh, p->pool, t, tmp) {
tidle = tnow - t->track.t_last;
if (t->track.b_cutoff ||
(t->conn.l4proto == IPPROTO_TCP && (
(t->track.state == SYN && tidle >= p->timeout_syn) ||
(t->track.state == ESTABLISHED && tidle >= p->timeout_established) ||
(t->track.state == FIN && tidle >= p->timeout_fin))
) || (t->conn.l4proto == IPPROTO_UDP && tidle >= p->timeout_udp)
)
{
HASH_DEL(p->pool, t); ConntrackFreeElem(t);
}
}
p->t_last_purge = tnow;
}
}
static void taddr2str(uint8_t l3proto, const t_addr *a, char *buf, size_t bufsize)
{
if (!inet_ntop(family_from_proto(l3proto), a, buf, bufsize) && bufsize) *buf = 0;
}
void ConntrackPoolDump(const t_conntrack *p)
{
t_conntrack_pool *t, *tmp;
char sa1[40], sa2[40];
time_t tnow = time(NULL);
HASH_ITER(hh, p->pool, t, tmp) {
taddr2str(t->conn.l3proto, &t->conn.src, sa1, sizeof(sa1));
taddr2str(t->conn.l3proto, &t->conn.dst, sa2, sizeof(sa2));
printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu orig=d%llu/n%llu/b%llu reply=d%llu/n%llu/b%lld ",
proto_name(t->conn.l4proto),
sa1, t->conn.sport, sa2, t->conn.dport,
t->conn.l4proto == IPPROTO_TCP ? connstate_s[t->track.state] : "-",
(unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last),
(unsigned long long)t->track.pdcounter_orig, (unsigned long long)t->track.pcounter_orig, (unsigned long long)t->track.pbcounter_orig,
(unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply, (unsigned long long)t->track.pbcounter_reply);
if (t->conn.l4proto == IPPROTO_TCP)
printf("seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u mss_orig=%u mss_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d",
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.pos_orig - t->track.seq0,
t->track.ack0, t->track.ack_last - t->track.ack0, t->track.pos_reply - t->track.ack0,
t->track.mss_orig, t->track.mss_reply,
t->track.winsize_orig, t->track.scale_orig == SCALE_NONE ? -1 : t->track.scale_orig,
t->track.winsize_reply, t->track.scale_reply == SCALE_NONE ? -1 : t->track.scale_reply);
else
printf("rseq=%u pos_orig=%u rack=%u pos_reply=%u",
t->track.seq_last, t->track.pos_orig,
t->track.ack_last, t->track.pos_reply);
printf(" req_retrans=%u cutoff=%u lua_in_cutoff=%u lua_out_cutoff=%u hostname=%s l7proto=%s\n",
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_lua_in_cutoff, t->track.b_lua_out_cutoff, t->track.hostname, l7proto_str(t->track.l7proto));
};
}
void ReasmClear(t_reassemble *reasm)
{
free(reasm->packet);
reasm->packet = NULL;
reasm->size = reasm->size_present = 0;
}
bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start)
{
reasm->packet = malloc(size_requested);
if (!reasm->packet) return false;
reasm->size = size_requested;
reasm->size_present = 0;
reasm->seq = seq_start;
return true;
}
bool ReasmResize(t_reassemble *reasm, size_t new_size)
{
uint8_t *p = realloc(reasm->packet, new_size);
if (!p) return false;
reasm->packet = p;
reasm->size = new_size;
if (reasm->size_present > new_size) reasm->size_present = new_size;
return true;
}
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len)
{
if (reasm->seq != seq) return false; // fail session if out of sequence
size_t szcopy;
szcopy = reasm->size - reasm->size_present;
if (len < szcopy) szcopy = len;
memcpy(reasm->packet + reasm->size_present, payload, szcopy);
reasm->size_present += szcopy;
reasm->seq += (uint32_t)szcopy;
return true;
}
bool ReasmHasSpace(t_reassemble *reasm, size_t len)
{
return (reasm->size_present + len) <= reasm->size;
}

136
nfq2/conntrack.h Normal file
View File

@@ -0,0 +1,136 @@
#pragma once
// this conntrack is not bullet-proof
// its designed to satisfy dpi desync needs only
#include <stdbool.h>
#include <stdint.h>
#include <ctype.h>
#include <sys/types.h>
#include <time.h>
#include <netinet/in.h>
#define __FAVOR_BSD
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include "packet_queue.h"
#include "protocol.h"
//#define HASH_BLOOM 20
#define HASH_NONFATAL_OOM 1
#undef HASH_FUNCTION
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
#define RETRANS_COUNTER_STOP ((uint8_t)-1)
typedef union {
struct in_addr ip;
struct in6_addr ip6;
} t_addr;
typedef struct
{
t_addr src, dst;
uint16_t sport,dport;
uint8_t l3proto; // IPPROTO_IP, IPPROTO_IPV6
uint8_t l4proto; // IPPROTO_TCP, IPPROTO_UDP
} t_conn;
// this structure helps to reassemble continuous packets streams. it does not support out-of-orders
typedef struct {
uint8_t *packet; // allocated for size during reassemble request. requestor must know the message size.
uint32_t seq; // current seq number. if a packet comes with an unexpected seq - it fails reassemble session.
size_t size; // expected message size. success means that we have received exactly 'size' bytes and have them in 'packet'
size_t size_present; // how many bytes already stored in 'packet'
} t_reassemble;
// SYN - SYN or SYN/ACK received
// ESTABLISHED - any except SYN or SYN/ACK received
// FIN - FIN or RST received
typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate;
typedef struct
{
bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache
uint8_t ipproto;
struct desync_profile *dp; // desync profile cache
bool dp_search_complete;
// common state
time_t t_start, t_last;
uint64_t pcounter_orig, pcounter_reply; // packet counter
uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload)
uint64_t pbcounter_orig, pbcounter_reply; // transferred byte counter. includes retransmissions. it's not the same as relative seq.
uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current
uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current
// tcp only state, not used in udp
t_connstate state;
uint32_t seq0, ack0; // starting seq and ack
uint16_t winsize_orig, winsize_reply; // last seen window size
uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none
uint32_t winsize_orig_calc, winsize_reply_calc; // calculated window size
uint16_t mss_orig, mss_reply;
uint8_t req_retrans_counter; // number of request retransmissions
bool req_seq_present,req_seq_finalized,req_seq_abandoned;
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
uint8_t incoming_ttl;
bool b_cutoff; // mark for deletion
bool b_lua_in_cutoff,b_lua_out_cutoff;
t_l7proto l7proto;
bool l7proto_discovered;
char *hostname;
bool hostname_is_ip;
bool hostname_discovered;
bool hostname_ah_check; // should perform autohostlist checks
int lua_state; // registry index of associated LUA object
int lua_instance_cutoff; // registry index of per connection function instance cutoff table
t_reassemble reasm_orig;
struct rawpacket_tailhead delayed;
} t_ctrack;
typedef struct
{
t_ctrack track;
UT_hash_handle hh; // makes this structure hashable
t_conn conn; // key
} t_conntrack_pool;
typedef struct
{
// inactivity time to purge an entry in each connection state
uint32_t timeout_syn,timeout_established,timeout_fin,timeout_udp;
time_t t_purge_interval, t_last_purge;
t_conntrack_pool *pool;
} t_conntrack;
void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin, uint32_t timeout_udp);
void ConntrackPoolDestroy(t_conntrack *p);
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse);
// do not create, do not update. only find existing
bool ConntrackPoolDoubleSearch(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse);
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr);
void CaonntrackExtractConn(t_conn *c, bool bReverse, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr);
void ConntrackPoolDump(const t_conntrack *p);
void ConntrackPoolPurge(t_conntrack *p);
void ConntrackClearHostname(t_ctrack *track);
bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start);
bool ReasmResize(t_reassemble *reasm, size_t new_size);
void ReasmClear(t_reassemble *reasm);
// false means reassemble session has failed and we should ReasmClear() it
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len);
// check if it has enough space to buffer 'len' bytes
bool ReasmHasSpace(t_reassemble *reasm, size_t len);
inline static bool ReasmIsEmpty(t_reassemble *reasm) {return !reasm->size;}
inline static bool ReasmIsFull(t_reassemble *reasm) {return !ReasmIsEmpty(reasm) && (reasm->size==reasm->size_present);}

15
nfq2/crypto/aes-gcm.c Normal file
View File

@@ -0,0 +1,15 @@
#include "aes-gcm.h"
int aes_gcm_crypt(int mode, uint8_t *output, const uint8_t *input, size_t input_length, const uint8_t *key, const size_t key_len, const uint8_t *iv, const size_t iv_len, const uint8_t *adata, size_t adata_len, uint8_t *atag, size_t atag_len)
{
int ret = 0;
gcm_context ctx;
if (!(ret = gcm_setkey(&ctx, key, (const uint)key_len)))
{
ret = gcm_crypt_and_tag(&ctx, mode, iv, iv_len, adata, adata_len, input, output, input_length, atag, atag_len);
gcm_zero_ctx(&ctx);
}
return ret;
}

6
nfq2/crypto/aes-gcm.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
#include "gcm.h"
// mode : AES_ENCRYPT, AES_DECRYPT
int aes_gcm_crypt(int mode, uint8_t *output, const uint8_t *input, size_t input_length, const uint8_t *key, const size_t key_len, const uint8_t *iv, const size_t iv_len, const uint8_t *adata, size_t adata_len, uint8_t *atag, size_t atag_len);

483
nfq2/crypto/aes.c Normal file
View File

@@ -0,0 +1,483 @@
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of the AES Rijndael
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
* of this work was correctness & accuracy. It is written in 'C' without any
* particular focus upon optimization or speed. It should be endian (memory
* byte order) neutral since the few places that care are handled explicitly.
*
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#include "aes.h"
static int aes_tables_inited = 0; // run-once flag for performing key
// expasion table generation (see below)
/*
* The following static local tables must be filled-in before the first use of
* the GCM or AES ciphers. They are used for the AES key expansion/scheduling
* and once built are read-only and thread safe. The "gcm_initialize" function
* must be called once during system initialization to populate these arrays
* for subsequent use by the AES key scheduler. If they have not been built
* before attempted use, an error will be returned to the caller.
*
* NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since
* GCM uses AES in counter-mode, where the AES cipher output is XORed with
* the GCM input, we ONLY NEED AES encryption. Thus, to save space AES
* decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h.
*/
// We always need our forward tables
static uchar FSb[256]; // Forward substitution box (FSb)
static uint32_t FT0[256]; // Forward key schedule assembly tables
static uint32_t FT1[256];
static uint32_t FT2[256];
static uint32_t FT3[256];
#if AES_DECRYPTION // We ONLY need reverse for decryption
static uchar RSb[256]; // Reverse substitution box (RSb)
static uint32_t RT0[256]; // Reverse key schedule assembly tables
static uint32_t RT1[256];
static uint32_t RT2[256];
static uint32_t RT3[256];
#endif /* AES_DECRYPTION */
static uint32_t RCON[10]; // AES round constants
/*
* Platform Endianness Neutralizing Load and Store Macro definitions
* AES wants platform-neutral Little Endian (LE) byte ordering
*/
#define GET_UINT32_LE(n,b,i) { \
(n) = ( (uint32_t) (b)[(i) ] ) \
| ( (uint32_t) (b)[(i) + 1] << 8 ) \
| ( (uint32_t) (b)[(i) + 2] << 16 ) \
| ( (uint32_t) (b)[(i) + 3] << 24 ); }
#define PUT_UINT32_LE(n,b,i) { \
(b)[(i) ] = (uchar) ( (n) ); \
(b)[(i) + 1] = (uchar) ( (n) >> 8 ); \
(b)[(i) + 2] = (uchar) ( (n) >> 16 ); \
(b)[(i) + 3] = (uchar) ( (n) >> 24 ); }
/*
* AES forward and reverse encryption round processing macros
*/
#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
{ \
X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \
FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \
FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \
FT3[ ( Y3 >> 24 ) & 0xFF ]; \
\
X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \
FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \
FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \
FT3[ ( Y0 >> 24 ) & 0xFF ]; \
\
X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \
FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \
FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \
FT3[ ( Y1 >> 24 ) & 0xFF ]; \
\
X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \
FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \
FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \
FT3[ ( Y2 >> 24 ) & 0xFF ]; \
}
#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \
{ \
X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \
RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \
RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \
RT3[ ( Y1 >> 24 ) & 0xFF ]; \
\
X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \
RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \
RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \
RT3[ ( Y2 >> 24 ) & 0xFF ]; \
\
X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \
RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \
RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \
RT3[ ( Y3 >> 24 ) & 0xFF ]; \
\
X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \
RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \
RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \
RT3[ ( Y0 >> 24 ) & 0xFF ]; \
}
/*
* These macros improve the readability of the key
* generation initialization code by collapsing
* repetitive common operations into logical pieces.
*/
#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 )
#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) )
#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 )
#define MIX(x,y) { y = ( (y << 1) | (y >> 7) ) & 0xFF; x ^= y; }
#define CPY128 { *RK++ = *SK++; *RK++ = *SK++; \
*RK++ = *SK++; *RK++ = *SK++; }
/******************************************************************************
*
* AES_INIT_KEYGEN_TABLES
*
* Fills the AES key expansion tables allocated above with their static
* data. This is not "per key" data, but static system-wide read-only
* table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once
* at system initialization to setup the tables for all subsequent use.
*
******************************************************************************/
void aes_init_keygen_tables(void)
{
int i, x, y, z; // general purpose iteration and computation locals
int pow[256];
int log[256];
if (aes_tables_inited) return;
// fill the 'pow' and 'log' tables over GF(2^8)
for (i = 0, x = 1; i < 256; i++) {
pow[i] = x;
log[x] = i;
x = (x ^ XTIME(x)) & 0xFF;
}
// compute the round constants
for (i = 0, x = 1; i < 10; i++) {
RCON[i] = (uint32_t)x;
x = XTIME(x) & 0xFF;
}
// fill the forward and reverse substitution boxes
FSb[0x00] = 0x63;
#if AES_DECRYPTION // whether AES decryption is supported
RSb[0x63] = 0x00;
#endif /* AES_DECRYPTION */
for (i = 1; i < 256; i++) {
x = y = pow[255 - log[i]];
MIX(x, y);
MIX(x, y);
MIX(x, y);
MIX(x, y);
FSb[i] = (uchar)(x ^= 0x63);
#if AES_DECRYPTION // whether AES decryption is supported
RSb[x] = (uchar)i;
#endif /* AES_DECRYPTION */
}
// generate the forward and reverse key expansion tables
for (i = 0; i < 256; i++) {
x = FSb[i];
y = XTIME(x) & 0xFF;
z = (y ^ x) & 0xFF;
FT0[i] = ((uint32_t)y) ^ ((uint32_t)x << 8) ^
((uint32_t)x << 16) ^ ((uint32_t)z << 24);
FT1[i] = ROTL8(FT0[i]);
FT2[i] = ROTL8(FT1[i]);
FT3[i] = ROTL8(FT2[i]);
#if AES_DECRYPTION // whether AES decryption is supported
x = RSb[i];
RT0[i] = ((uint32_t)MUL(0x0E, x)) ^
((uint32_t)MUL(0x09, x) << 8) ^
((uint32_t)MUL(0x0D, x) << 16) ^
((uint32_t)MUL(0x0B, x) << 24);
RT1[i] = ROTL8(RT0[i]);
RT2[i] = ROTL8(RT1[i]);
RT3[i] = ROTL8(RT2[i]);
#endif /* AES_DECRYPTION */
}
aes_tables_inited = 1; // flag that the tables have been generated
} // to permit subsequent use of the AES cipher
/******************************************************************************
*
* AES_SET_ENCRYPTION_KEY
*
* This is called by 'aes_setkey' when we're establishing a key for
* subsequent encryption. We give it a pointer to the encryption
* context, a pointer to the key, and the key's length in bytes.
* Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits).
*
******************************************************************************/
int aes_set_encryption_key(aes_context *ctx,
const uchar *key,
uint keysize)
{
uint i; // general purpose iteration local
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
for (i = 0; i < (keysize >> 2); i++) {
GET_UINT32_LE(RK[i], key, i << 2);
}
switch (ctx->rounds)
{
case 10:
for (i = 0; i < 10; i++, RK += 4) {
RK[4] = RK[0] ^ RCON[i] ^
((uint32_t)FSb[(RK[3] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[3] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[3] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[3]) & 0xFF] << 24);
RK[5] = RK[1] ^ RK[4];
RK[6] = RK[2] ^ RK[5];
RK[7] = RK[3] ^ RK[6];
}
break;
case 12:
for (i = 0; i < 8; i++, RK += 6) {
RK[6] = RK[0] ^ RCON[i] ^
((uint32_t)FSb[(RK[5] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[5] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[5] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[5]) & 0xFF] << 24);
RK[7] = RK[1] ^ RK[6];
RK[8] = RK[2] ^ RK[7];
RK[9] = RK[3] ^ RK[8];
RK[10] = RK[4] ^ RK[9];
RK[11] = RK[5] ^ RK[10];
}
break;
case 14:
for (i = 0; i < 7; i++, RK += 8) {
RK[8] = RK[0] ^ RCON[i] ^
((uint32_t)FSb[(RK[7] >> 8) & 0xFF]) ^
((uint32_t)FSb[(RK[7] >> 16) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[7] >> 24) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[7]) & 0xFF] << 24);
RK[9] = RK[1] ^ RK[8];
RK[10] = RK[2] ^ RK[9];
RK[11] = RK[3] ^ RK[10];
RK[12] = RK[4] ^
((uint32_t)FSb[(RK[11]) & 0xFF]) ^
((uint32_t)FSb[(RK[11] >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(RK[11] >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(RK[11] >> 24) & 0xFF] << 24);
RK[13] = RK[5] ^ RK[12];
RK[14] = RK[6] ^ RK[13];
RK[15] = RK[7] ^ RK[14];
}
break;
default:
return -1;
}
return(0);
}
#if AES_DECRYPTION // whether AES decryption is supported
/******************************************************************************
*
* AES_SET_DECRYPTION_KEY
*
* This is called by 'aes_setkey' when we're establishing a
* key for subsequent decryption. We give it a pointer to
* the encryption context, a pointer to the key, and the key's
* length in bits. Valid lengths are: 128, 192, or 256 bits.
*
******************************************************************************/
int aes_set_decryption_key(aes_context *ctx,
const uchar *key,
uint keysize)
{
int i, j;
aes_context cty; // a calling aes context for set_encryption_key
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
uint32_t *SK;
int ret;
cty.rounds = ctx->rounds; // initialize our local aes context
cty.rk = cty.buf; // round count and key buf pointer
if ((ret = aes_set_encryption_key(&cty, key, keysize)) != 0)
return(ret);
SK = cty.rk + cty.rounds * 4;
CPY128 // copy a 128-bit block from *SK to *RK
for (i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8) {
for (j = 0; j < 4; j++, SK++) {
*RK++ = RT0[FSb[(*SK) & 0xFF]] ^
RT1[FSb[(*SK >> 8) & 0xFF]] ^
RT2[FSb[(*SK >> 16) & 0xFF]] ^
RT3[FSb[(*SK >> 24) & 0xFF]];
}
}
CPY128 // copy a 128-bit block from *SK to *RK
memset(&cty, 0, sizeof(aes_context)); // clear local aes context
return(0);
}
#endif /* AES_DECRYPTION */
/******************************************************************************
*
* AES_SETKEY
*
* Invoked to establish the key schedule for subsequent encryption/decryption
*
******************************************************************************/
int aes_setkey(aes_context *ctx, // AES context provided by our caller
int mode, // ENCRYPT or DECRYPT flag
const uchar *key, // pointer to the key
uint keysize) // key length in bytes
{
// since table initialization is not thread safe, we could either add
// system-specific mutexes and init the AES key generation tables on
// demand, or ask the developer to simply call "gcm_initialize" once during
// application startup before threading begins. That's what we choose.
if (!aes_tables_inited) return (-1); // fail the call when not inited.
ctx->mode = mode; // capture the key type we're creating
ctx->rk = ctx->buf; // initialize our round key pointer
switch (keysize) // set the rounds count based upon the keysize
{
case 16: ctx->rounds = 10; break; // 16-byte, 128-bit key
case 24: ctx->rounds = 12; break; // 24-byte, 192-bit key
case 32: ctx->rounds = 14; break; // 32-byte, 256-bit key
default: return(-1);
}
#if AES_DECRYPTION
if (mode == AES_DECRYPT) // expand our key for encryption or decryption
return(aes_set_decryption_key(ctx, key, keysize));
else /* ENCRYPT */
#endif /* AES_DECRYPTION */
return(aes_set_encryption_key(ctx, key, keysize));
}
/******************************************************************************
*
* AES_CIPHER
*
* Perform AES encryption and decryption.
* The AES context will have been setup with the encryption mode
* and all keying information appropriate for the task.
*
******************************************************************************/
int aes_cipher(aes_context *ctx,
const uchar input[16],
uchar output[16])
{
int i;
uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals
RK = ctx->rk;
GET_UINT32_LE(X0, input, 0); X0 ^= *RK++; // load our 128-bit
GET_UINT32_LE(X1, input, 4); X1 ^= *RK++; // input buffer in a storage
GET_UINT32_LE(X2, input, 8); X2 ^= *RK++; // memory endian-neutral way
GET_UINT32_LE(X3, input, 12); X3 ^= *RK++;
#if AES_DECRYPTION // whether AES decryption is supported
if (ctx->mode == AES_DECRYPT)
{
for (i = (ctx->rounds >> 1) - 1; i > 0; i--)
{
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
}
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
X0 = *RK++ ^ \
((uint32_t)RSb[(Y0) & 0xFF]) ^
((uint32_t)RSb[(Y3 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y2 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y1 >> 24) & 0xFF] << 24);
X1 = *RK++ ^ \
((uint32_t)RSb[(Y1) & 0xFF]) ^
((uint32_t)RSb[(Y0 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y3 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y2 >> 24) & 0xFF] << 24);
X2 = *RK++ ^ \
((uint32_t)RSb[(Y2) & 0xFF]) ^
((uint32_t)RSb[(Y1 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y0 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y3 >> 24) & 0xFF] << 24);
X3 = *RK++ ^ \
((uint32_t)RSb[(Y3) & 0xFF]) ^
((uint32_t)RSb[(Y2 >> 8) & 0xFF] << 8) ^
((uint32_t)RSb[(Y1 >> 16) & 0xFF] << 16) ^
((uint32_t)RSb[(Y0 >> 24) & 0xFF] << 24);
}
else /* ENCRYPT */
{
#endif /* AES_DECRYPTION */
for (i = (ctx->rounds >> 1) - 1; i > 0; i--)
{
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3);
}
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3);
X0 = *RK++ ^ \
((uint32_t)FSb[(Y0) & 0xFF]) ^
((uint32_t)FSb[(Y1 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y2 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y3 >> 24) & 0xFF] << 24);
X1 = *RK++ ^ \
((uint32_t)FSb[(Y1) & 0xFF]) ^
((uint32_t)FSb[(Y2 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y3 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y0 >> 24) & 0xFF] << 24);
X2 = *RK++ ^ \
((uint32_t)FSb[(Y2) & 0xFF]) ^
((uint32_t)FSb[(Y3 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y0 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y1 >> 24) & 0xFF] << 24);
X3 = *RK++ ^ \
((uint32_t)FSb[(Y3) & 0xFF]) ^
((uint32_t)FSb[(Y0 >> 8) & 0xFF] << 8) ^
((uint32_t)FSb[(Y1 >> 16) & 0xFF] << 16) ^
((uint32_t)FSb[(Y2 >> 24) & 0xFF] << 24);
#if AES_DECRYPTION // whether AES decryption is supported
}
#endif /* AES_DECRYPTION */
PUT_UINT32_LE(X0, output, 0);
PUT_UINT32_LE(X1, output, 4);
PUT_UINT32_LE(X2, output, 8);
PUT_UINT32_LE(X3, output, 12);
return(0);
}
/* end of aes.c */

78
nfq2/crypto/aes.h Normal file
View File

@@ -0,0 +1,78 @@
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of the AES Rijndael
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus
* of this work was correctness & accuracy. It is written in 'C' without any
* particular focus upon optimization or speed. It should be endian (memory
* byte order) neutral since the few places that care are handled explicitly.
*
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#pragma once
/******************************************************************************/
#define AES_DECRYPTION 1 // whether AES decryption is supported
/******************************************************************************/
#include <string.h>
#define AES_ENCRYPT 1 // specify whether we're encrypting
#define AES_DECRYPT 0 // or decrypting
#if defined(_MSC_VER)
#include <basetsd.h>
typedef UINT32 uint32_t;
#else
#include <inttypes.h>
#endif
typedef unsigned char uchar; // add some convienent shorter types
typedef unsigned int uint;
/******************************************************************************
* AES_INIT_KEYGEN_TABLES : MUST be called once before any AES use
******************************************************************************/
void aes_init_keygen_tables(void);
/******************************************************************************
* AES_CONTEXT : cipher context / holds inter-call data
******************************************************************************/
typedef struct {
int mode; // 1 for Encryption, 0 for Decryption
int rounds; // keysize-based rounds count
uint32_t *rk; // pointer to current round key
uint32_t buf[68]; // key expansion buffer
} aes_context;
/******************************************************************************
* AES_SETKEY : called to expand the key for encryption or decryption
******************************************************************************/
int aes_setkey(aes_context *ctx, // pointer to context
int mode, // 1 or 0 for Encrypt/Decrypt
const uchar *key, // AES input key
uint keysize); // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
// returns 0 for success
/******************************************************************************
* AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data
******************************************************************************/
int aes_cipher(aes_context *ctx, // pointer to context
const uchar input[16], // 128-bit block to en/decipher
uchar output[16]); // 128-bit output result block
// returns 0 for success

512
nfq2/crypto/gcm.c Normal file
View File

@@ -0,0 +1,512 @@
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of AES-GCM authenticated
* encryption. The focus of this work was correctness & accuracy. It is written
* in straight 'C' without any particular focus upon optimization or speed. It
* should be endian (memory byte order) neutral since the few places that care
* are handled explicitly.
*
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
* gcm/gcm-revised-spec.pdf
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#include "gcm.h"
#include "aes.h"
/******************************************************************************
* ==== IMPLEMENTATION WARNING ====
*
* This code was developed for use within SQRL's fixed environmnent. Thus, it
* is somewhat less "general purpose" than it would be if it were designed as
* a general purpose AES-GCM library. Specifically, it bothers with almost NO
* error checking on parameter limits, buffer bounds, etc. It assumes that it
* is being invoked by its author or by someone who understands the values it
* expects to receive. Its behavior will be undefined otherwise.
*
* All functions that might fail are defined to return 'ints' to indicate a
* problem. Most do not do so now. But this allows for error propagation out
* of internal functions if robust error checking should ever be desired.
*
******************************************************************************/
/* Calculating the "GHASH"
*
* There are many ways of calculating the so-called GHASH in software, each with
* a traditional size vs performance tradeoff. The GHASH (Galois field hash) is
* an intriguing construction which takes two 128-bit strings (also the cipher's
* block size and the fundamental operation size for the system) and hashes them
* into a third 128-bit result.
*
* Many implementation solutions have been worked out that use large precomputed
* table lookups in place of more time consuming bit fiddling, and this approach
* can be scaled easily upward or downward as needed to change the time/space
* tradeoff. It's been studied extensively and there's a solid body of theory and
* practice. For example, without using any lookup tables an implementation
* might obtain 119 cycles per byte throughput, whereas using a simple, though
* large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13
* cycles per byte.
*
* And Intel's processors have, since 2010, included an instruction which does
* the entire 128x128->128 bit job in just several 64x64->128 bit pieces.
*
* Since SQRL is interactive, and only processing a few 128-bit blocks, I've
* settled upon a relatively slower but appealing small-table compromise which
* folds a bunch of not only time consuming but also bit twiddling into a simple
* 16-entry table which is attributed to Victor Shoup's 1996 work while at
* Bellcore: "On Fast and Provably Secure MessageAuthentication Based on
* Universal Hashing." See: http://www.shoup.net/papers/macs.pdf
* See, also section 4.1 of the "gcm-revised-spec" cited above.
*/
/*
* This 16-entry table of pre-computed constants is used by the
* GHASH multiplier to improve over a strictly table-free but
* significantly slower 128x128 bit multiple within GF(2^128).
*/
static const uint64_t last4[16] = {
0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0 };
/*
* Platform Endianness Neutralizing Load and Store Macro definitions
* GCM wants platform-neutral Big Endian (BE) byte ordering
*/
#define GET_UINT32_BE(n,b,i) { \
(n) = ( (uint32_t) (b)[(i) ] << 24 ) \
| ( (uint32_t) (b)[(i) + 1] << 16 ) \
| ( (uint32_t) (b)[(i) + 2] << 8 ) \
| ( (uint32_t) (b)[(i) + 3] ); }
#define PUT_UINT32_BE(n,b,i) { \
(b)[(i) ] = (uchar) ( (n) >> 24 ); \
(b)[(i) + 1] = (uchar) ( (n) >> 16 ); \
(b)[(i) + 2] = (uchar) ( (n) >> 8 ); \
(b)[(i) + 3] = (uchar) ( (n) ); }
/******************************************************************************
*
* GCM_INITIALIZE
*
* Must be called once to initialize the GCM library.
*
* At present, this only calls the AES keygen table generator, which expands
* the AES keying tables for use. This is NOT A THREAD-SAFE function, so it
* MUST be called during system initialization before a multi-threading
* environment is running.
*
******************************************************************************/
int gcm_initialize(void)
{
aes_init_keygen_tables();
return(0);
}
/******************************************************************************
*
* GCM_MULT
*
* Performs a GHASH operation on the 128-bit input vector 'x', setting
* the 128-bit output vector to 'x' times H using our precomputed tables.
* 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field.
*
******************************************************************************/
static void gcm_mult(gcm_context *ctx, // pointer to established context
const uchar x[16], // pointer to 128-bit input vector
uchar output[16]) // pointer to 128-bit output vector
{
int i;
uchar lo, hi, rem;
uint64_t zh, zl;
lo = (uchar)(x[15] & 0x0f);
hi = (uchar)(x[15] >> 4);
zh = ctx->HH[lo];
zl = ctx->HL[lo];
for (i = 15; i >= 0; i--) {
lo = (uchar)(x[i] & 0x0f);
hi = (uchar)(x[i] >> 4);
if (i != 15) {
rem = (uchar)(zl & 0x0f);
zl = (zh << 60) | (zl >> 4);
zh = (zh >> 4);
zh ^= (uint64_t)last4[rem] << 48;
zh ^= ctx->HH[lo];
zl ^= ctx->HL[lo];
}
rem = (uchar)(zl & 0x0f);
zl = (zh << 60) | (zl >> 4);
zh = (zh >> 4);
zh ^= (uint64_t)last4[rem] << 48;
zh ^= ctx->HH[hi];
zl ^= ctx->HL[hi];
}
PUT_UINT32_BE(zh >> 32, output, 0);
PUT_UINT32_BE(zh, output, 4);
PUT_UINT32_BE(zl >> 32, output, 8);
PUT_UINT32_BE(zl, output, 12);
}
/******************************************************************************
*
* GCM_SETKEY
*
* This is called to set the AES-GCM key. It initializes the AES key
* and populates the gcm context's pre-calculated HTables.
*
******************************************************************************/
int gcm_setkey(gcm_context *ctx, // pointer to caller-provided gcm context
const uchar *key, // pointer to the AES encryption key
const uint keysize) // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
{
int ret, i, j;
uint64_t hi, lo;
uint64_t vl, vh;
unsigned char h[16];
memset(ctx, 0, sizeof(gcm_context)); // zero caller-provided GCM context
memset(h, 0, 16); // initialize the block to encrypt
// encrypt the null 128-bit block to generate a key-based value
// which is then used to initialize our GHASH lookup tables
if ((ret = aes_setkey(&ctx->aes_ctx, AES_ENCRYPT, key, keysize)) != 0)
return(ret);
if ((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0)
return(ret);
GET_UINT32_BE(hi, h, 0); // pack h as two 64-bit ints, big-endian
GET_UINT32_BE(lo, h, 4);
vh = (uint64_t)hi << 32 | lo;
GET_UINT32_BE(hi, h, 8);
GET_UINT32_BE(lo, h, 12);
vl = (uint64_t)hi << 32 | lo;
ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128)
ctx->HH[8] = vh;
ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128)
ctx->HL[0] = 0;
for (i = 4; i > 0; i >>= 1) {
uint32_t T = (uint32_t)(vl & 1) * 0xe1000000U;
vl = (vh << 63) | (vl >> 1);
vh = (vh >> 1) ^ ((uint64_t)T << 32);
ctx->HL[i] = vl;
ctx->HH[i] = vh;
}
for (i = 2; i < 16; i <<= 1) {
uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i;
vh = *HiH;
vl = *HiL;
for (j = 1; j < i; j++) {
HiH[j] = vh ^ ctx->HH[j];
HiL[j] = vl ^ ctx->HL[j];
}
}
return(0);
}
/******************************************************************************
*
* GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH.
*
* SETKEY:
*
* START: Sets the Encryption/Decryption mode.
* Accepts the initialization vector and additional data.
*
* UPDATE: Encrypts or decrypts the plaintext or ciphertext.
*
* FINISH: Performs a final GHASH to generate the authentication tag.
*
******************************************************************************
*
* GCM_START
*
* Given a user-provided GCM context, this initializes it, sets the encryption
* mode, and preprocesses the initialization vector and additional AEAD data.
*
******************************************************************************/
int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
int mode, // AES_ENCRYPT or AES_DECRYPT
const uchar *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
const uchar *add, // ptr to additional AEAD data (NULL if none)
size_t add_len) // length of additional AEAD data (bytes)
{
int ret; // our error return if the AES encrypt fails
uchar work_buf[16]; // XOR source built from provided IV if len != 16
const uchar *p; // general purpose array pointer
size_t use_len; // byte count to process, up to 16 bytes
size_t i; // local loop iterator
// since the context might be reused under the same key
// we zero the working buffers for this next new process
memset(ctx->y, 0x00, sizeof(ctx->y));
memset(ctx->buf, 0x00, sizeof(ctx->buf));
ctx->len = 0;
ctx->add_len = 0;
ctx->mode = mode; // set the GCM encryption/decryption mode
ctx->aes_ctx.mode = AES_ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
if (iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV
memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff
ctx->y[15] = 1; // start "counting" from 1 (not 0)
}
else // if we don't have a 12-byte IV, we GHASH whatever we've been given
{
memset(work_buf, 0x00, 16); // clear the working buffer
PUT_UINT32_BE(iv_len * 8, work_buf, 12); // place the IV into buffer
p = iv;
while (iv_len > 0) {
use_len = (iv_len < 16) ? iv_len : 16;
for (i = 0; i < use_len; i++) ctx->y[i] ^= p[i];
gcm_mult(ctx, ctx->y, ctx->y);
iv_len -= use_len;
p += use_len;
}
for (i = 0; i < 16; i++) ctx->y[i] ^= work_buf[i];
gcm_mult(ctx, ctx->y, ctx->y);
}
if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ctx->base_ectr)) != 0)
return(ret);
ctx->add_len = add_len;
p = add;
while (add_len > 0) {
use_len = (add_len < 16) ? add_len : 16;
for (i = 0; i < use_len; i++) ctx->buf[i] ^= p[i];
gcm_mult(ctx, ctx->buf, ctx->buf);
add_len -= use_len;
p += use_len;
}
return(0);
}
/******************************************************************************
*
* GCM_UPDATE
*
* This is called once or more to process bulk plaintext or ciphertext data.
* We give this some number of bytes of input and it returns the same number
* of output bytes. If called multiple times (which is fine) all but the final
* invocation MUST be called with length mod 16 == 0. (Only the final call can
* have a partial block length of < 128 bits.)
*
******************************************************************************/
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process
const uchar *input, // pointer to source data
uchar *output) // pointer to destination data
{
int ret; // our error return if the AES encrypt fails
uchar ectr[16]; // counter-mode cipher output for XORing
size_t use_len; // byte count to process, up to 16 bytes
size_t i; // local loop iterator
ctx->len += length; // bump the GCM context's running length count
while (length > 0) {
// clamp the length to process at 16 bytes
use_len = (length < 16) ? length : 16;
// increment the context's 128-bit IV||Counter 'y' vector
for (i = 16; i > 12; i--) if (++ctx->y[i - 1] != 0) break;
// encrypt the context's 'y' vector under the established key
if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ectr)) != 0)
return(ret);
// encrypt or decrypt the input to the output
if (ctx->mode == AES_ENCRYPT)
{
for (i = 0; i < use_len; i++) {
// XOR the cipher's ouptut vector (ectr) with our input
output[i] = (uchar)(ectr[i] ^ input[i]);
// now we mix in our data into the authentication hash.
// if we're ENcrypting we XOR in the post-XOR (output)
// results, but if we're DEcrypting we XOR in the input
// data
ctx->buf[i] ^= output[i];
}
}
else
{
for (i = 0; i < use_len; i++) {
// but if we're DEcrypting we XOR in the input data first,
// i.e. before saving to ouput data, otherwise if the input
// and output buffer are the same (inplace decryption) we
// would not get the correct auth tag
ctx->buf[i] ^= input[i];
// XOR the cipher's ouptut vector (ectr) with our input
output[i] = (uchar)(ectr[i] ^ input[i]);
}
}
gcm_mult(ctx, ctx->buf, ctx->buf); // perform a GHASH operation
length -= use_len; // drop the remaining byte count to process
input += use_len; // bump our input pointer forward
output += use_len; // bump our output pointer forward
}
return(0);
}
/******************************************************************************
*
* GCM_FINISH
*
* This is called once after all calls to GCM_UPDATE to finalize the GCM.
* It performs the final GHASH to produce the resulting authentication TAG.
*
******************************************************************************/
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
uchar *tag, // pointer to buffer which receives the tag
size_t tag_len) // length, in bytes, of the tag-receiving buf
{
uchar work_buf[16];
uint64_t orig_len = ctx->len * 8;
uint64_t orig_add_len = ctx->add_len * 8;
size_t i;
if (tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len);
if (orig_len || orig_add_len) {
memset(work_buf, 0x00, 16);
PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0);
PUT_UINT32_BE((orig_add_len), work_buf, 4);
PUT_UINT32_BE((orig_len >> 32), work_buf, 8);
PUT_UINT32_BE((orig_len), work_buf, 12);
for (i = 0; i < 16; i++) ctx->buf[i] ^= work_buf[i];
gcm_mult(ctx, ctx->buf, ctx->buf);
for (i = 0; i < tag_len; i++) tag[i] ^= ctx->buf[i];
}
return(0);
}
/******************************************************************************
*
* GCM_CRYPT_AND_TAG
*
* This either encrypts or decrypts the user-provided data and, either
* way, generates an authentication tag of the requested length. It must be
* called with a GCM context whose key has already been set with GCM_SETKEY.
*
* The user would typically call this explicitly to ENCRYPT a buffer of data
* and optional associated data, and produce its an authentication tag.
*
* To reverse the process the user would typically call the companion
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
* authentication tag. The GCM_AUTH_DECRYPT function calls this function
* to perform its decryption and tag generation, which it then compares.
*
******************************************************************************/
int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: AES_ENCRYPT or AES_DECRYPT
const uchar *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
const uchar *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
const uchar *input, // pointer to the cipher data source
uchar *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
uchar *tag, // pointer to the tag to be generated
size_t tag_len) // byte length of the tag to be generated
{ /*
assuming that the caller has already invoked gcm_setkey to
prepare the gcm context with the keying material, we simply
invoke each of the three GCM sub-functions in turn...
*/
gcm_start(ctx, mode, iv, iv_len, add, add_len);
gcm_update(ctx, length, input, output);
gcm_finish(ctx, tag, tag_len);
return(0);
}
/******************************************************************************
*
* GCM_AUTH_DECRYPT
*
* This DECRYPTS a user-provided data buffer with optional associated data.
* It then verifies a user-supplied authentication tag against the tag just
* re-created during decryption to verify that the data has not been altered.
*
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption
* and authentication tag generation.
*
******************************************************************************/
int gcm_auth_decrypt(
gcm_context *ctx, // gcm context with key already setup
const uchar *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
const uchar *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
const uchar *input, // pointer to the cipher data source
uchar *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
const uchar *tag, // pointer to the tag to be authenticated
size_t tag_len) // byte length of the tag <= 16
{
uchar check_tag[16]; // the tag generated and returned by decryption
int diff; // an ORed flag to detect authentication errors
size_t i; // our local iterator
/*
we use GCM_DECRYPT_AND_TAG (above) to perform our decryption
(which is an identical XORing to reverse the previous one)
and also to re-generate the matching authentication tag
*/
gcm_crypt_and_tag(ctx, AES_DECRYPT, iv, iv_len, add, add_len,
input, output, length, check_tag, tag_len);
// now we verify the authentication tag in 'constant time'
for (diff = 0, i = 0; i < tag_len; i++)
diff |= tag[i] ^ check_tag[i];
if (diff != 0) { // see whether any bits differed?
memset(output, 0, length); // if so... wipe the output data
return(GCM_AUTH_FAILURE); // return GCM_AUTH_FAILURE
}
return(0);
}
/******************************************************************************
*
* GCM_ZERO_CTX
*
* The GCM context contains both the GCM context and the AES context.
* This includes keying and key-related material which is security-
* sensitive, so it MUST be zeroed after use. This function does that.
*
******************************************************************************/
void gcm_zero_ctx(gcm_context *ctx)
{
// zero the context originally provided to us
memset(ctx, 0, sizeof(gcm_context));
}

183
nfq2/crypto/gcm.h Normal file
View File

@@ -0,0 +1,183 @@
/******************************************************************************
*
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL
*
* This is a simple and straightforward implementation of AES-GCM authenticated
* encryption. The focus of this work was correctness & accuracy. It is written
* in straight 'C' without any particular focus upon optimization or speed. It
* should be endian (memory byte order) neutral since the few places that care
* are handled explicitly.
*
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com.
*
* It is intended for general purpose use, but was written in support of GRC's
* reference implementation of the SQRL (Secure Quick Reliable Login) client.
*
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \
* gcm/gcm-revised-spec.pdf
*
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK.
*
*******************************************************************************/
#pragma once
#define GCM_AUTH_FAILURE 0x55555555 // authentication failure
#include "aes.h" // gcm_context includes aes_context
#if defined(_MSC_VER)
#include <basetsd.h>
typedef unsigned int size_t;// use the right type for length declarations
typedef UINT32 uint32_t;
typedef UINT64 uint64_t;
#else
#include <stdint.h>
#endif
/******************************************************************************
* GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx
******************************************************************************/
typedef struct {
int mode; // cipher direction: encrypt/decrypt
uint64_t len; // cipher data length processed so far
uint64_t add_len; // total add data length
uint64_t HL[16]; // precalculated lo-half HTable
uint64_t HH[16]; // precalculated hi-half HTable
uchar base_ectr[16]; // first counter-mode cipher output for tag
uchar y[16]; // the current cipher-input IV|Counter value
uchar buf[16]; // buf working value
aes_context aes_ctx; // cipher context used
} gcm_context;
/******************************************************************************
* GCM_CONTEXT : MUST be called once before ANY use of this library
******************************************************************************/
int gcm_initialize(void);
/******************************************************************************
* GCM_SETKEY : sets the GCM (and AES) keying material for use
******************************************************************************/
int gcm_setkey(gcm_context *ctx, // caller-provided context ptr
const uchar *key, // pointer to cipher key
const uint keysize // size in bytes (must be 16, 24, 32 for
// 128, 192 or 256-bit keys respectively)
); // returns 0 for success
/******************************************************************************
*
* GCM_CRYPT_AND_TAG
*
* This either encrypts or decrypts the user-provided data and, either
* way, generates an authentication tag of the requested length. It must be
* called with a GCM context whose key has already been set with GCM_SETKEY.
*
* The user would typically call this explicitly to ENCRYPT a buffer of data
* and optional associated data, and produce its an authentication tag.
*
* To reverse the process the user would typically call the companion
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided
* authentication tag. The GCM_AUTH_DECRYPT function calls this function
* to perform its decryption and tag generation, which it then compares.
*
******************************************************************************/
int gcm_crypt_and_tag(
gcm_context *ctx, // gcm context with key already setup
int mode, // cipher direction: ENCRYPT (1) or DECRYPT (0)
const uchar *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
const uchar *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
const uchar *input, // pointer to the cipher data source
uchar *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
uchar *tag, // pointer to the tag to be generated
size_t tag_len); // byte length of the tag to be generated
/******************************************************************************
*
* GCM_AUTH_DECRYPT
*
* This DECRYPTS a user-provided data buffer with optional associated data.
* It then verifies a user-supplied authentication tag against the tag just
* re-created during decryption to verify that the data has not been altered.
*
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption
* and authentication tag generation.
*
******************************************************************************/
int gcm_auth_decrypt(
gcm_context *ctx, // gcm context with key already setup
const uchar *iv, // pointer to the 12-byte initialization vector
size_t iv_len, // byte length if the IV. should always be 12
const uchar *add, // pointer to the non-ciphered additional data
size_t add_len, // byte length of the additional AEAD data
const uchar *input, // pointer to the cipher data source
uchar *output, // pointer to the cipher data destination
size_t length, // byte length of the cipher data
const uchar *tag, // pointer to the tag to be authenticated
size_t tag_len); // byte length of the tag <= 16
/******************************************************************************
*
* GCM_START
*
* Given a user-provided GCM context, this initializes it, sets the encryption
* mode, and preprocesses the initialization vector and additional AEAD data.
*
******************************************************************************/
int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
int mode, // ENCRYPT (1) or DECRYPT (0)
const uchar *iv, // pointer to initialization vector
size_t iv_len, // IV length in bytes (should == 12)
const uchar *add, // pointer to additional AEAD data (NULL if none)
size_t add_len); // length of additional AEAD data (bytes)
/******************************************************************************
*
* GCM_UPDATE
*
* This is called once or more to process bulk plaintext or ciphertext data.
* We give this some number of bytes of input and it returns the same number
* of output bytes. If called multiple times (which is fine) all but the final
* invocation MUST be called with length mod 16 == 0. (Only the final call can
* have a partial block length of < 128 bits.)
*
******************************************************************************/
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
size_t length, // length, in bytes, of data to process
const uchar *input, // pointer to source data
uchar *output); // pointer to destination data
/******************************************************************************
*
* GCM_FINISH
*
* This is called once after all calls to GCM_UPDATE to finalize the GCM.
* It performs the final GHASH to produce the resulting authentication TAG.
*
******************************************************************************/
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
uchar *tag, // ptr to tag buffer - NULL if tag_len = 0
size_t tag_len); // length, in bytes, of the tag-receiving buf
/******************************************************************************
*
* GCM_ZERO_CTX
*
* The GCM context contains both the GCM context and the AES context.
* This includes keying and key-related material which is security-
* sensitive, so it MUST be zeroed after use. This function does that.
*
******************************************************************************/
void gcm_zero_ctx(gcm_context *ctx);

337
nfq2/crypto/hkdf.c Normal file
View File

@@ -0,0 +1,337 @@
/**************************** hkdf.c ***************************/
/***************** See RFC 6234 for details. *******************/
/* Copyright (c) 2011 IETF Trust and the persons identified as */
/* authors of the code. All rights reserved. */
/* See sha.h for terms of use and redistribution. */
/*
* Description:
* This file implements the HKDF algorithm (HMAC-based
* Extract-and-Expand Key Derivation Function, RFC 5869),
* expressed in terms of the various SHA algorithms.
*/
#include "sha.h"
#include <string.h>
#include <stdlib.h>
/*
* hkdf
*
* Description:
* This function will generate keying material using HKDF.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* salt[ ]: [in]
* The optional salt value (a non-secret random value);
* if not provided (salt == NULL), it is set internally
* to a string of HashLen(whichSha) zeros.
* salt_len: [in]
* The length of the salt value. (Ignored if salt == NULL.)
* ikm[ ]: [in]
* Input keying material.
* ikm_len: [in]
* The length of the input keying material.
* info[ ]: [in]
* The optional context and application specific information.
* If info == NULL or a zero-length string, it is ignored.
* info_len: [in]
* The length of the optional context and application specific
* information. (Ignored if info == NULL.)
* okm[ ]: [out]
* Where the HKDF is to be stored.
* okm_len: [in]
* The length of the buffer to hold okm.
* okm_len must be <= 255 * USHABlockSize(whichSha)
*
* Notes:
* Calls hkdfExtract() and hkdfExpand().
*
* Returns:
* sha Error Code.
*
*/
int hkdf(SHAversion whichSha,
const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
const unsigned char *info, size_t info_len,
uint8_t okm[], size_t okm_len)
{
uint8_t prk[USHAMaxHashSize];
return hkdfExtract(whichSha, salt, salt_len, ikm, ikm_len, prk) ||
hkdfExpand(whichSha, prk, USHAHashSize(whichSha), info,
info_len, okm, okm_len);
}
/*
* hkdfExtract
*
* Description:
* This function will perform HKDF extraction.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* salt[ ]: [in]
* The optional salt value (a non-secret random value);
* if not provided (salt == NULL), it is set internally
* to a string of HashLen(whichSha) zeros.
* salt_len: [in]
* The length of the salt value. (Ignored if salt == NULL.)
* ikm[ ]: [in]
* Input keying material.
* ikm_len: [in]
* The length of the input keying material.
* prk[ ]: [out]
* Array where the HKDF extraction is to be stored.
* Must be larger than USHAHashSize(whichSha);
*
* Returns:
* sha Error Code.
*
*/
int hkdfExtract(SHAversion whichSha,
const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
uint8_t prk[USHAMaxHashSize])
{
unsigned char nullSalt[USHAMaxHashSize];
if (salt == 0) {
salt = nullSalt;
salt_len = USHAHashSize(whichSha);
memset(nullSalt, '\0', salt_len);
}
else if (salt_len < 0) {
return shaBadParam;
}
return hmac(whichSha, ikm, ikm_len, salt, salt_len, prk);
}
/*
* hkdfExpand
*
* Description:
* This function will perform HKDF expansion.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* prk[ ]: [in]
* The pseudo-random key to be expanded; either obtained
* directly from a cryptographically strong, uniformly
* distributed pseudo-random number generator, or as the
* output from hkdfExtract().
* prk_len: [in]
* The length of the pseudo-random key in prk;
* should at least be equal to USHAHashSize(whichSHA).
* info[ ]: [in]
* The optional context and application specific information.
* If info == NULL or a zero-length string, it is ignored.
* info_len: [in]
* The length of the optional context and application specific
* information. (Ignored if info == NULL.)
* okm[ ]: [out]
* Where the HKDF is to be stored.
* okm_len: [in]
* The length of the buffer to hold okm.
* okm_len must be <= 255 * USHABlockSize(whichSha)
*
* Returns:
* sha Error Code.
*
*/
int hkdfExpand(SHAversion whichSha, const uint8_t prk[], size_t prk_len,
const unsigned char *info, size_t info_len,
uint8_t okm[], size_t okm_len)
{
size_t hash_len, N;
unsigned char T[USHAMaxHashSize];
size_t Tlen, where, i;
if (info == 0) {
info = (const unsigned char *)"";
info_len = 0;
}
else if (info_len < 0) {
return shaBadParam;
}
if (okm_len <= 0) return shaBadParam;
if (!okm) return shaBadParam;
hash_len = USHAHashSize(whichSha);
if (prk_len < hash_len) return shaBadParam;
N = okm_len / hash_len;
if ((okm_len % hash_len) != 0) N++;
if (N > 255) return shaBadParam;
Tlen = 0;
where = 0;
for (i = 1; i <= N; i++) {
HMACContext context;
unsigned char c = i;
int ret = hmacReset(&context, whichSha, prk, prk_len) ||
hmacInput(&context, T, Tlen) ||
hmacInput(&context, info, info_len) ||
hmacInput(&context, &c, 1) ||
hmacResult(&context, T);
if (ret != shaSuccess) return ret;
memcpy(okm + where, T,
(i != N) ? hash_len : (okm_len - where));
where += hash_len;
Tlen = hash_len;
}
return shaSuccess;
}
/*
* hkdfReset
*
* Description:
* This function will initialize the hkdfContext in preparation
* for key derivation using the modular HKDF interface for
* arbitrary length inputs.
*
* Parameters:
* context: [in/out]
* The context to reset.
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* salt[ ]: [in]
* The optional salt value (a non-secret random value);
* if not provided (salt == NULL), it is set internally
* to a string of HashLen(whichSha) zeros.
* salt_len: [in]
* The length of the salt value. (Ignored if salt == NULL.)
*
* Returns:
* sha Error Code.
*
*/
int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
const unsigned char *salt, size_t salt_len)
{
unsigned char nullSalt[USHAMaxHashSize];
if (!context) return shaNull;
context->whichSha = whichSha;
context->hashSize = USHAHashSize(whichSha);
if (salt == 0) {
salt = nullSalt;
salt_len = context->hashSize;
memset(nullSalt, '\0', salt_len);
}
return hmacReset(&context->hmacContext, whichSha, salt, salt_len);
}
/*
* hkdfInput
*
* Description:
* This function accepts an array of octets as the next portion
* of the input keying material. It may be called multiple times.
*
* Parameters:
* context: [in/out]
* The HKDF context to update.
* ikm[ ]: [in]
* An array of octets representing the next portion of
* the input keying material.
* ikm_len: [in]
* The length of ikm.
*
* Returns:
* sha Error Code.
*
*/
int hkdfInput(HKDFContext *context, const unsigned char *ikm,
size_t ikm_len)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
return hmacInput(&context->hmacContext, ikm, ikm_len);
}
/*
* hkdfFinalBits
*
* Description:
* This function will add in any final bits of the
* input keying material.
*
* Parameters:
* context: [in/out]
* The HKDF context to update
* ikm_bits: [in]
* The final bits of the input keying material, in the upper
* portion of the byte. (Use 0b###00000 instead of 0b00000###
* to input the three bits ###.)
* ikm_bit_count: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
unsigned int ikm_bit_count)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
return hmacFinalBits(&context->hmacContext, ikm_bits, ikm_bit_count);
}
/*
* hkdfResult
*
* Description:
* This function will finish the HKDF extraction and perform the
* final HKDF expansion.
*
* Parameters:
* context: [in/out]
* The HKDF context to use to calculate the HKDF hash.
* prk[ ]: [out]
* An optional location to store the HKDF extraction.
* Either NULL, or pointer to a buffer that must be
* larger than USHAHashSize(whichSha);
* info[ ]: [in]
* The optional context and application specific information.
* If info == NULL or a zero-length string, it is ignored.
* info_len: [in]
* The length of the optional context and application specific
* information. (Ignored if info == NULL.)
* okm[ ]: [out]
* Where the HKDF is to be stored.
* okm_len: [in]
* The length of the buffer to hold okm.
* okm_len must be <= 255 * USHABlockSize(whichSha)
*
* Returns:
* sha Error Code.
*
*/
int hkdfResult(HKDFContext *context,
uint8_t prk[USHAMaxHashSize],
const unsigned char *info, size_t info_len,
uint8_t okm[], size_t okm_len)
{
uint8_t prkbuf[USHAMaxHashSize];
int ret;
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
if (!okm) return context->Corrupted = shaBadParam;
if (!prk) prk = prkbuf;
ret = hmacResult(&context->hmacContext, prk) ||
hkdfExpand(context->whichSha, prk, context->hashSize, info,
info_len, okm, okm_len);
context->Computed = 1;
return context->Corrupted = ret;
}

250
nfq2/crypto/hmac.c Normal file
View File

@@ -0,0 +1,250 @@
/**************************** hmac.c ***************************/
/***************** See RFC 6234 for details. *******************/
/* Copyright (c) 2011 IETF Trust and the persons identified as */
/* authors of the code. All rights reserved. */
/* See sha.h for terms of use and redistribution. */
/*
* Description:
* This file implements the HMAC algorithm (Keyed-Hashing for
* Message Authentication, [RFC 2104]), expressed in terms of
* the various SHA algorithms.
*/
#include "sha.h"
#include <stddef.h>
/*
* hmac
*
* Description:
* This function will compute an HMAC message digest.
*
* Parameters:
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* message_array[ ]: [in]
* An array of octets representing the message.
* Note: in RFC 2104, this parameter is known
* as 'text'.
* length: [in]
* The length of the message in message_array.
* key[ ]: [in]
* The secret shared key.
* key_len: [in]
* The length of the secret shared key.
* digest[ ]: [out]
* Where the digest is to be returned.
* NOTE: The length of the digest is determined by
* the value of whichSha.
*
* Returns:
* sha Error Code.
*
*/
int hmac(SHAversion whichSha,
const unsigned char *message_array, size_t length,
const unsigned char *key, size_t key_len,
uint8_t digest[USHAMaxHashSize])
{
HMACContext context;
return hmacReset(&context, whichSha, key, key_len) ||
hmacInput(&context, message_array, length) ||
hmacResult(&context, digest);
}
/*
* hmacReset
*
* Description:
* This function will initialize the hmacContext in preparation
* for computing a new HMAC message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* whichSha: [in]
* One of SHA1, SHA224, SHA256, SHA384, SHA512
* key[ ]: [in]
* The secret shared key.
* key_len: [in]
* The length of the secret shared key.
*
* Returns:
* sha Error Code.
*
*/
int hmacReset(HMACContext *context, enum SHAversion whichSha,
const unsigned char *key, size_t key_len)
{
size_t i, blocksize, hashsize;
int ret;
/* inner padding - key XORd with ipad */
unsigned char k_ipad[USHA_Max_Message_Block_Size];
/* temporary buffer when keylen > blocksize */
unsigned char tempkey[USHAMaxHashSize];
if (!context) return shaNull;
context->Computed = 0;
context->Corrupted = shaSuccess;
blocksize = context->blockSize = USHABlockSize(whichSha);
hashsize = context->hashSize = USHAHashSize(whichSha);
context->whichSha = whichSha;
/*
* If key is longer than the hash blocksize,
* reset it to key = HASH(key).
*/
if (key_len > blocksize) {
USHAContext tcontext;
int err = USHAReset(&tcontext, whichSha) ||
USHAInput(&tcontext, key, key_len) ||
USHAResult(&tcontext, tempkey);
if (err != shaSuccess) return err;
key = tempkey;
key_len = hashsize;
}
/*
* The HMAC transform looks like:
*
* SHA(K XOR opad, SHA(K XOR ipad, text))
*
* where K is an n byte key, 0-padded to a total of blocksize bytes,
* ipad is the byte 0x36 repeated blocksize times,
* opad is the byte 0x5c repeated blocksize times,
* and text is the data being protected.
*/
/* store key into the pads, XOR'd with ipad and opad values */
for (i = 0; i < key_len; i++) {
k_ipad[i] = key[i] ^ 0x36;
context->k_opad[i] = key[i] ^ 0x5c;
}
/* remaining pad bytes are '\0' XOR'd with ipad and opad values */
for (; i < blocksize; i++) {
k_ipad[i] = 0x36;
context->k_opad[i] = 0x5c;
}
/* perform inner hash */
/* init context for 1st pass */
ret = USHAReset(&context->shaContext, whichSha) ||
/* and start with inner pad */
USHAInput(&context->shaContext, k_ipad, blocksize);
return context->Corrupted = ret;
}
/*
* hmacInput
*
* Description:
* This function accepts an array of octets as the next portion
* of the message. It may be called multiple times.
*
* Parameters:
* context: [in/out]
* The HMAC context to update.
* text[ ]: [in]
* An array of octets representing the next portion of
* the message.
* text_len: [in]
* The length of the message in text.
*
* Returns:
* sha Error Code.
*
*/
int hmacInput(HMACContext *context, const unsigned char *text,
size_t text_len)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
/* then text of datagram */
return context->Corrupted =
USHAInput(&context->shaContext, text, text_len);
}
/*
* hmacFinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The HMAC context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int hmacFinalBits(HMACContext *context,
uint8_t bits, unsigned int bit_count)
{
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
/* then final bits of datagram */
return context->Corrupted =
USHAFinalBits(&context->shaContext, bits, bit_count);
}
/*
* hmacResult
*
* Description:
* This function will return the N-byte message digest into the
* Message_Digest array provided by the caller.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the HMAC hash.
* digest[ ]: [out]
* Where the digest is returned.
* NOTE 2: The length of the hash is determined by the value of
* whichSha that was passed to hmacReset().
*
* Returns:
* sha Error Code.
*
*/
int hmacResult(HMACContext *context, uint8_t *digest)
{
int ret;
if (!context) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
/* finish up 1st pass */
/* (Use digest here as a temporary buffer.) */
ret =
USHAResult(&context->shaContext, digest) ||
/* perform outer SHA */
/* init context for 2nd pass */
USHAReset(&context->shaContext, context->whichSha) ||
/* start with outer pad */
USHAInput(&context->shaContext, context->k_opad,
context->blockSize) ||
/* then results of 1st hash */
USHAInput(&context->shaContext, digest, context->hashSize) ||
/* finish up 2nd pass */
USHAResult(&context->shaContext, digest);
context->Computed = 1;
return context->Corrupted = ret;
}

25
nfq2/crypto/sha-private.h Normal file
View File

@@ -0,0 +1,25 @@
/************************ sha-private.h ************************/
/***************** See RFC 6234 for details. *******************/
#pragma once
/*
* These definitions are defined in FIPS 180-3, section 4.1.
* Ch() and Maj() are defined identically in sections 4.1.1,
* 4.1.2, and 4.1.3.
*
* The definitions used in FIPS 180-3 are as follows:
*/
#ifndef USE_MODIFIED_MACROS
#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#else /* USE_MODIFIED_MACROS */
/*
* The following definitions are equivalent and potentially faster.
*/
#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
#endif /* USE_MODIFIED_MACROS */
#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))

278
nfq2/crypto/sha.h Normal file
View File

@@ -0,0 +1,278 @@
/**************************** sha.h ****************************/
/***************** See RFC 6234 for details. *******************/
/*
Copyright (c) 2011 IETF Trust and the persons identified as
authors of the code. All rights reserved.
Redistribution and use in source and binary forms, with or
without modification, are permitted provided that the following
conditions are met:
- Redistributions of source code must retain the above
copyright notice, this list of conditions and
the following disclaimer.
- Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
- Neither the name of Internet Society, IETF or IETF Trust, nor
the names of specific contributors, may be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
/*
* Description:
* This file implements the Secure Hash Algorithms
* as defined in the U.S. National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-3 published in October 2008
* and formerly defined in its predecessors, FIPS PUB 180-1
* and FIP PUB 180-2.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-3/fips180-3_final.pdf
*
* The five hashes are defined in these sizes:
* SHA-1 20 byte / 160 bit
* SHA-224 28 byte / 224 bit
* SHA-256 32 byte / 256 bit
* SHA-384 48 byte / 384 bit
* SHA-512 64 byte / 512 bit
*
* Compilation Note:
* These files may be compiled with two options:
* USE_32BIT_ONLY - use 32-bit arithmetic only, for systems
* without 64-bit integers
*
* USE_MODIFIED_MACROS - use alternate form of the SHA_Ch()
* and SHA_Maj() macros that are equivalent
* and potentially faster on many systems
*
*/
#include <stdint.h>
#include <stddef.h>
/*
* If you do not have the ISO standard stdint.h header file, then you
* must typedef the following:
* name meaning
* uint64_t unsigned 64-bit integer
* uint32_t unsigned 32-bit integer
* uint8_t unsigned 8-bit integer (i.e., unsigned char)
* int_least16_t integer of >= 16 bits
*
* See stdint-example.h
*/
#ifndef _SHA_enum_
#define _SHA_enum_
/*
* All SHA functions return one of these values.
*/
enum {
shaSuccess = 0,
shaNull, /* Null pointer parameter */
shaInputTooLong, /* input data too long */
shaStateError, /* called Input after FinalBits or Result */
shaBadParam /* passed a bad parameter */
};
#endif /* _SHA_enum_ */
/*
* These constants hold size information for each of the SHA
* hashing operations
*/
enum {
SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64,
SHA256_Message_Block_Size = 64,
USHA_Max_Message_Block_Size = SHA256_Message_Block_Size,
SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32,
USHAMaxHashSize = SHA256HashSize,
SHA1HashSizeBits = 160, SHA224HashSizeBits = 224,
SHA256HashSizeBits = 256, USHAMaxHashSizeBits = SHA256HashSizeBits
};
/*
* These constants are used in the USHA (Unified SHA) functions.
*/
typedef enum SHAversion {
SHA224, SHA256
} SHAversion;
/*
* This structure will hold context information for the SHA-256
* hashing operation.
*/
typedef struct SHA256Context {
uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */
uint32_t Length_High; /* Message length in bits */
uint32_t Length_Low; /* Message length in bits */
int_least16_t Message_Block_Index; /* Message_Block array index */
/* 512-bit message blocks */
uint8_t Message_Block[SHA256_Message_Block_Size];
int Computed; /* Is the hash computed? */
int Corrupted; /* Cumulative corruption code */
} SHA256Context;
/*
* This structure will hold context information for the SHA-224
* hashing operation. It uses the SHA-256 structure for computation.
*/
typedef struct SHA256Context SHA224Context;
/*
* This structure holds context information for all SHA
* hashing operations.
*/
typedef struct USHAContext {
int whichSha; /* which SHA is being used */
union {
SHA224Context sha224Context; SHA256Context sha256Context;
} ctx;
} USHAContext;
/*
* This structure will hold context information for the HMAC
* keyed-hashing operation.
*/
typedef struct HMACContext {
int whichSha; /* which SHA is being used */
int hashSize; /* hash size of SHA being used */
int blockSize; /* block size of SHA being used */
USHAContext shaContext; /* SHA context */
unsigned char k_opad[USHA_Max_Message_Block_Size];
/* outer padding - key XORd with opad */
int Computed; /* Is the MAC computed? */
int Corrupted; /* Cumulative corruption code */
} HMACContext;
/*
* This structure will hold context information for the HKDF
* extract-and-expand Key Derivation Functions.
*/
typedef struct HKDFContext {
int whichSha; /* which SHA is being used */
HMACContext hmacContext;
int hashSize; /* hash size of SHA being used */
unsigned char prk[USHAMaxHashSize];
/* pseudo-random key - output of hkdfInput */
int Computed; /* Is the key material computed? */
int Corrupted; /* Cumulative corruption code */
} HKDFContext;
/*
* Function Prototypes
*/
/* SHA-224 */
int SHA224Reset(SHA224Context *);
int SHA224Input(SHA224Context *, const uint8_t *bytes,
unsigned int bytecount);
int SHA224FinalBits(SHA224Context *, uint8_t bits,
unsigned int bit_count);
int SHA224Result(SHA224Context *,
uint8_t Message_Digest[SHA224HashSize]);
/* SHA-256 */
int SHA256Reset(SHA256Context *);
int SHA256Input(SHA256Context *, const uint8_t *bytes,
unsigned int bytecount);
int SHA256FinalBits(SHA256Context *, uint8_t bits,
unsigned int bit_count);
int SHA256Result(SHA256Context *,
uint8_t Message_Digest[SHA256HashSize]);
/* Unified SHA functions, chosen by whichSha */
int USHAReset(USHAContext *context, SHAversion whichSha);
int USHAInput(USHAContext *context,
const uint8_t *bytes, unsigned int bytecount);
int USHAFinalBits(USHAContext *context,
uint8_t bits, unsigned int bit_count);
int USHAResult(USHAContext *context,
uint8_t Message_Digest[USHAMaxHashSize]);
int USHABlockSize(enum SHAversion whichSha);
int USHAHashSize(enum SHAversion whichSha);
/*
* HMAC Keyed-Hashing for Message Authentication, RFC 2104,
* for all SHAs.
* This interface allows a fixed-length text input to be used.
*/
int hmac(SHAversion whichSha, /* which SHA algorithm to use */
const unsigned char *text, /* pointer to data stream */
size_t text_len, /* length of data stream */
const unsigned char *key, /* pointer to authentication key */
size_t key_len, /* length of authentication key */
uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */
/*
* HMAC Keyed-Hashing for Message Authentication, RFC 2104,
* for all SHAs.
* This interface allows any length of text input to be used.
*/
int hmacReset(HMACContext *context, enum SHAversion whichSha,
const unsigned char *key, size_t key_len);
int hmacInput(HMACContext *context, const unsigned char *text,
size_t text_len);
int hmacFinalBits(HMACContext *context, uint8_t bits,
unsigned int bit_count);
int hmacResult(HMACContext *context,
uint8_t digest[USHAMaxHashSize]);
/*
* HKDF HMAC-based Extract-and-Expand Key Derivation Function,
* RFC 5869, for all SHAs.
*/
int hkdf(SHAversion whichSha,
const unsigned char *salt, size_t salt_len,
const unsigned char *ikm, size_t ikm_len,
const unsigned char *info, size_t info_len,
uint8_t okm[ ], size_t okm_len);
int hkdfExtract(SHAversion whichSha, const unsigned char *salt,
size_t salt_len, const unsigned char *ikm,
size_t ikm_len, uint8_t prk[USHAMaxHashSize]);
int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ],
size_t prk_len, const unsigned char *info,
size_t info_len, uint8_t okm[ ], size_t okm_len);
/*
* HKDF HMAC-based Extract-and-Expand Key Derivation Function,
* RFC 5869, for all SHAs.
* This interface allows any length of text input to be used.
*/
int hkdfReset(HKDFContext *context, enum SHAversion whichSha,
const unsigned char *salt, size_t salt_len);
int hkdfInput(HKDFContext *context, const unsigned char *ikm,
size_t ikm_len);
int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits,
unsigned int ikm_bit_count);
int hkdfResult(HKDFContext *context,
uint8_t prk[USHAMaxHashSize],
const unsigned char *info, size_t info_len,
uint8_t okm[USHAMaxHashSize], size_t okm_len);

581
nfq2/crypto/sha224-256.c Normal file
View File

@@ -0,0 +1,581 @@
/************************* sha224-256.c ************************/
/***************** See RFC 6234 for details. *******************/
/* Copyright (c) 2011 IETF Trust and the persons identified as */
/* authors of the code. All rights reserved. */
/* See sha.h for terms of use and redistribution. */
/*
* Description:
* This file implements the Secure Hash Algorithms SHA-224 and
* SHA-256 as defined in the U.S. National Institute of Standards
* and Technology Federal Information Processing Standards
* Publication (FIPS PUB) 180-3 published in October 2008
* and formerly defined in its predecessors, FIPS PUB 180-1
* and FIP PUB 180-2.
*
* A combined document showing all algorithms is available at
* http://csrc.nist.gov/publications/fips/
* fips180-3/fips180-3_final.pdf
*
* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
* message digests for a given data stream. It should take about
* 2**n steps to find a message with the same digest as a given
* message and 2**(n/2) to find any two messages with the same
* digest, when n is the digest size in bits. Therefore, this
* algorithm can serve as a means of providing a
* "fingerprint" for a message.
*
* Portability Issues:
* SHA-224 and SHA-256 are defined in terms of 32-bit "words".
* This code uses <stdint.h> (included via "sha.h") to define 32-
* and 8-bit unsigned integer types. If your C compiler does not
* support 32-bit unsigned integers, this code is not
* appropriate.
*
* Caveats:
* SHA-224 and SHA-256 are designed to work with messages less
* than 2^64 bits long. This implementation uses SHA224/256Input()
* to hash the bits that are a multiple of the size of an 8-bit
* octet, and then optionally uses SHA224/256FinalBits()
* to hash the final few bits of the input.
*/
#include "sha.h"
#include "sha-private.h"
/* Define the SHA shift, rotate left, and rotate right macros */
#define SHA256_SHR(bits,word) ((word) >> (bits))
#define SHA256_ROTL(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))
#define SHA256_ROTR(bits,word) \
(((word) >> (bits)) | ((word) << (32-(bits))))
/* Define the SHA SIGMA and sigma macros */
#define SHA256_SIGMA0(word) \
(SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
#define SHA256_SIGMA1(word) \
(SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
#define SHA256_sigma0(word) \
(SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
#define SHA256_sigma1(word) \
(SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
/*
* Add "length" to the length.
* Set Corrupted when overflow has occurred.
*/
static uint32_t addTemp;
#define SHA224_256AddLength(context, length) \
(addTemp = (context)->Length_Low, (context)->Corrupted = \
(((context)->Length_Low += (length)) < addTemp) && \
(++(context)->Length_High == 0) ? shaInputTooLong : \
(context)->Corrupted )
/* Local Function Prototypes */
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0);
static void SHA224_256ProcessMessageBlock(SHA256Context *context);
static void SHA224_256Finalize(SHA256Context *context,
uint8_t Pad_Byte);
static void SHA224_256PadMessage(SHA256Context *context,
uint8_t Pad_Byte);
static int SHA224_256ResultN(SHA256Context *context,
uint8_t Message_Digest[ ], int HashSize);
/* Initial Hash Values: FIPS 180-3 section 5.3.2 */
static uint32_t SHA224_H0[SHA256HashSize/4] = {
0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939,
0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4
};
/* Initial Hash Values: FIPS 180-3 section 5.3.3 */
static uint32_t SHA256_H0[SHA256HashSize/4] = {
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
};
/*
* SHA224Reset
*
* Description:
* This function will initialize the SHA224Context in preparation
* for computing a new SHA224 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*/
int SHA224Reset(SHA224Context *context)
{
return SHA224_256Reset(context, SHA224_H0);
}
/*
* SHA224Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_array[ ]: [in]
* An array of octets representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array.
*
* Returns:
* sha Error Code.
*
*/
int SHA224Input(SHA224Context *context, const uint8_t *message_array,
unsigned int length)
{
return SHA256Input(context, message_array, length);
}
/*
* SHA224FinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int SHA224FinalBits(SHA224Context *context,
uint8_t message_bits, unsigned int length)
{
return SHA256FinalBits(context, message_bits, length);
}
/*
* SHA224Result
*
* Description:
* This function will return the 224-bit message digest
* into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 27.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*/
int SHA224Result(SHA224Context *context,
uint8_t Message_Digest[SHA224HashSize])
{
return SHA224_256ResultN(context, Message_Digest, SHA224HashSize);
}
/*
* SHA256Reset
*
* Description:
* This function will initialize the SHA256Context in preparation
* for computing a new SHA256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
*
* Returns:
* sha Error Code.
*/
int SHA256Reset(SHA256Context *context)
{
return SHA224_256Reset(context, SHA256_H0);
}
/*
* SHA256Input
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_array[ ]: [in]
* An array of octets representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array.
*
* Returns:
* sha Error Code.
*/
int SHA256Input(SHA256Context *context, const uint8_t *message_array,
unsigned int length)
{
if (!context) return shaNull;
if (!length) return shaSuccess;
if (!message_array) return shaNull;
if (context->Computed) return context->Corrupted = shaStateError;
if (context->Corrupted) return context->Corrupted;
while (length--) {
context->Message_Block[context->Message_Block_Index++] =
*message_array;
if ((SHA224_256AddLength(context, 8) == shaSuccess) &&
(context->Message_Block_Index == SHA256_Message_Block_Size))
SHA224_256ProcessMessageBlock(context);
message_array++;
}
return context->Corrupted;
}
/*
* SHA256FinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int SHA256FinalBits(SHA256Context *context,
uint8_t message_bits, unsigned int length)
{
static uint8_t masks[8] = {
/* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
/* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
/* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
/* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
};
static uint8_t markbit[8] = {
/* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
/* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
/* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
/* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
};
if (!context) return shaNull;
if (!length) return shaSuccess;
if (context->Corrupted) return context->Corrupted;
if (context->Computed) return context->Corrupted = shaStateError;
if (length >= 8) return context->Corrupted = shaBadParam;
SHA224_256AddLength(context, length);
SHA224_256Finalize(context, (uint8_t)
((message_bits & masks[length]) | markbit[length]));
return context->Corrupted;
}
/*
* SHA256Result
*
* Description:
* This function will return the 256-bit message digest
* into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*/
int SHA256Result(SHA256Context *context,
uint8_t Message_Digest[SHA256HashSize])
{
return SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
}
/*
* SHA224_256Reset
*
* Description:
* This helper function will initialize the SHA256Context in
* preparation for computing a new SHA-224 or SHA-256 message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* H0[ ]: [in]
* The initial hash value array to use.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0)
{
if (!context) return shaNull;
context->Length_High = context->Length_Low = 0;
context->Message_Block_Index = 0;
context->Intermediate_Hash[0] = H0[0];
context->Intermediate_Hash[1] = H0[1];
context->Intermediate_Hash[2] = H0[2];
context->Intermediate_Hash[3] = H0[3];
context->Intermediate_Hash[4] = H0[4];
context->Intermediate_Hash[5] = H0[5];
context->Intermediate_Hash[6] = H0[6];
context->Intermediate_Hash[7] = H0[7];
context->Computed = 0;
context->Corrupted = shaSuccess;
return shaSuccess;
}
/*
* SHA224_256ProcessMessageBlock
*
* Description:
* This helper function will process the next 512 bits of the
* message stored in the Message_Block array.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
*
* Returns:
* Nothing.
*
* Comments:
* Many of the variable names in this code, especially the
* single character names, were used because those were the
* names used in the Secure Hash Standard.
*/
static void SHA224_256ProcessMessageBlock(SHA256Context *context)
{
/* Constants defined in FIPS 180-3, section 4.2.2 */
static const uint32_t K[64] = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
int t, t4; /* Loop counter */
uint32_t temp1, temp2; /* Temporary word value */
uint32_t W[64]; /* Word sequence */
uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
/*
* Initialize the first 16 words in the array W
*/
for (t = t4 = 0; t < 16; t++, t4 += 4)
W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
(((uint32_t)context->Message_Block[t4 + 1]) << 16) |
(((uint32_t)context->Message_Block[t4 + 2]) << 8) |
(((uint32_t)context->Message_Block[t4 + 3]));
for (t = 16; t < 64; t++)
W[t] = SHA256_sigma1(W[t-2]) + W[t-7] +
SHA256_sigma0(W[t-15]) + W[t-16];
A = context->Intermediate_Hash[0];
B = context->Intermediate_Hash[1];
C = context->Intermediate_Hash[2];
D = context->Intermediate_Hash[3];
E = context->Intermediate_Hash[4];
F = context->Intermediate_Hash[5];
G = context->Intermediate_Hash[6];
H = context->Intermediate_Hash[7];
for (t = 0; t < 64; t++) {
temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t];
temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C);
H = G;
G = F;
F = E;
E = D + temp1;
D = C;
C = B;
B = A;
A = temp1 + temp2;
}
context->Intermediate_Hash[0] += A;
context->Intermediate_Hash[1] += B;
context->Intermediate_Hash[2] += C;
context->Intermediate_Hash[3] += D;
context->Intermediate_Hash[4] += E;
context->Intermediate_Hash[5] += F;
context->Intermediate_Hash[6] += G;
context->Intermediate_Hash[7] += H;
context->Message_Block_Index = 0;
}
/*
* SHA224_256Finalize
*
* Description:
* This helper function finishes off the digest calculations.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* sha Error Code.
*/
static void SHA224_256Finalize(SHA256Context *context,
uint8_t Pad_Byte)
{
int i;
SHA224_256PadMessage(context, Pad_Byte);
/* message may be sensitive, so clear it out */
for (i = 0; i < SHA256_Message_Block_Size; ++i)
context->Message_Block[i] = 0;
context->Length_High = 0; /* and clear length */
context->Length_Low = 0;
context->Computed = 1;
}
/*
* SHA224_256PadMessage
*
* Description:
* According to the standard, the message must be padded to the next
* even multiple of 512 bits. The first padding bit must be a '1'.
* The last 64 bits represent the length of the original message.
* All bits in between should be 0. This helper function will pad
* the message according to those rules by filling the
* Message_Block array accordingly. When it returns, it can be
* assumed that the message digest has been computed.
*
* Parameters:
* context: [in/out]
* The context to pad.
* Pad_Byte: [in]
* The last byte to add to the message block before the 0-padding
* and length. This will contain the last bits of the message
* followed by another single bit. If the message was an
* exact multiple of 8-bits long, Pad_Byte will be 0x80.
*
* Returns:
* Nothing.
*/
static void SHA224_256PadMessage(SHA256Context *context,
uint8_t Pad_Byte)
{
/*
* Check to see if the current message block is too small to hold
* the initial padding bits and length. If so, we will pad the
* block, process it, and then continue padding into a second
* block.
*/
if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) {
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < SHA256_Message_Block_Size)
context->Message_Block[context->Message_Block_Index++] = 0;
SHA224_256ProcessMessageBlock(context);
} else
context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
while (context->Message_Block_Index < (SHA256_Message_Block_Size-8))
context->Message_Block[context->Message_Block_Index++] = 0;
/*
* Store the message length as the last 8 octets
*/
context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
context->Message_Block[59] = (uint8_t)(context->Length_High);
context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
context->Message_Block[63] = (uint8_t)(context->Length_Low);
SHA224_256ProcessMessageBlock(context);
}
/*
* SHA224_256ResultN
*
* Description:
* This helper function will return the 224-bit or 256-bit message
* digest into the Message_Digest array provided by the caller.
* NOTE:
* The first octet of hash is stored in the element with index 0,
* the last octet of hash in the element with index 27/31.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA hash.
* Message_Digest[ ]: [out]
* Where the digest is returned.
* HashSize: [in]
* The size of the hash, either 28 or 32.
*
* Returns:
* sha Error Code.
*/
static int SHA224_256ResultN(SHA256Context *context,
uint8_t Message_Digest[ ], int HashSize)
{
int i;
if (!context) return shaNull;
if (!Message_Digest) return shaNull;
if (context->Corrupted) return context->Corrupted;
if (!context->Computed)
SHA224_256Finalize(context, 0x80);
for (i = 0; i < HashSize; ++i)
Message_Digest[i] = (uint8_t)
(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
return shaSuccess;
}

191
nfq2/crypto/usha.c Normal file
View File

@@ -0,0 +1,191 @@
/**************************** usha.c ***************************/
/***************** See RFC 6234 for details. *******************/
/* Copyright (c) 2011 IETF Trust and the persons identified as */
/* authors of the code. All rights reserved. */
/* See sha.h for terms of use and redistribution. */
/*
* Description:
* This file implements a unified interface to the SHA algorithms.
*/
#include "sha.h"
/*
* USHAReset
*
* Description:
* This function will initialize the SHA Context in preparation
* for computing a new SHA message digest.
*
* Parameters:
* context: [in/out]
* The context to reset.
* whichSha: [in]
* Selects which SHA reset to call
*
* Returns:
* sha Error Code.
*
*/
int USHAReset(USHAContext *context, enum SHAversion whichSha)
{
if (!context) return shaNull;
context->whichSha = whichSha;
switch (whichSha) {
case SHA224: return SHA224Reset((SHA224Context*)&context->ctx);
case SHA256: return SHA256Reset((SHA256Context*)&context->ctx);
default: return shaBadParam;
}
}
/*
* USHAInput
*
* Description:
* This function accepts an array of octets as the next portion
* of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_array: [in]
* An array of octets representing the next portion of
* the message.
* length: [in]
* The length of the message in message_array.
*
* Returns:
* sha Error Code.
*
*/
int USHAInput(USHAContext *context,
const uint8_t *bytes, unsigned int bytecount)
{
if (!context) return shaNull;
switch (context->whichSha) {
case SHA224:
return SHA224Input((SHA224Context*)&context->ctx, bytes,
bytecount);
case SHA256:
return SHA256Input((SHA256Context*)&context->ctx, bytes,
bytecount);
default: return shaBadParam;
}
}
/*
* USHAFinalBits
*
* Description:
* This function will add in any final bits of the message.
*
* Parameters:
* context: [in/out]
* The SHA context to update.
* message_bits: [in]
* The final bits of the message, in the upper portion of the
* byte. (Use 0b###00000 instead of 0b00000### to input the
* three bits ###.)
* length: [in]
* The number of bits in message_bits, between 1 and 7.
*
* Returns:
* sha Error Code.
*/
int USHAFinalBits(USHAContext *context,
uint8_t bits, unsigned int bit_count)
{
if (!context) return shaNull;
switch (context->whichSha) {
case SHA224:
return SHA224FinalBits((SHA224Context*)&context->ctx, bits,
bit_count);
case SHA256:
return SHA256FinalBits((SHA256Context*)&context->ctx, bits,
bit_count);
default: return shaBadParam;
}
}
/*
* USHAResult
*
* Description:
* This function will return the message digest of the appropriate
* bit size, as returned by USHAHashSizeBits(whichSHA) for the
* 'whichSHA' value used in the preceeding call to USHAReset,
* into the Message_Digest array provided by the caller.
*
* Parameters:
* context: [in/out]
* The context to use to calculate the SHA-1 hash.
* Message_Digest: [out]
* Where the digest is returned.
*
* Returns:
* sha Error Code.
*
*/
int USHAResult(USHAContext *context,
uint8_t Message_Digest[USHAMaxHashSize])
{
if (!context) return shaNull;
switch (context->whichSha) {
case SHA224:
return SHA224Result((SHA224Context*)&context->ctx,
Message_Digest);
case SHA256:
return SHA256Result((SHA256Context*)&context->ctx,
Message_Digest);
default: return shaBadParam;
}
}
/*
* USHABlockSize
*
* Description:
* This function will return the blocksize for the given SHA
* algorithm.
*
* Parameters:
* whichSha:
* which SHA algorithm to query
*
* Returns:
* block size
*
*/
int USHABlockSize(enum SHAversion whichSha)
{
switch (whichSha) {
case SHA224: return SHA224_Message_Block_Size;
default:
case SHA256: return SHA256_Message_Block_Size;
}
}
/*
* USHAHashSize
*
* Description:
* This function will return the hashsize for the given SHA
* algorithm.
*
* Parameters:
* whichSha:
* which SHA algorithm to query
*
* Returns:
* hash size
*
*/
int USHAHashSize(enum SHAversion whichSha)
{
switch (whichSha) {
case SHA224: return SHA224HashSize;
default:
case SHA256: return SHA256HashSize;
}
}

1737
nfq2/darkmagic.c Normal file

File diff suppressed because it is too large Load Diff

198
nfq2/darkmagic.h Normal file
View File

@@ -0,0 +1,198 @@
#pragma once
#include "nfqws.h"
#include "checksum.h"
#include "packet_queue.h"
#include "pools.h"
#include <stdint.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <netinet/in.h>
#define __FAVOR_BSD
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#ifndef IPV6_FREEBIND
#define IPV6_FREEBIND 78
#endif
#ifdef __CYGWIN__
#define INITGUID
#include "windivert/windivert.h"
#endif
#ifndef IPPROTO_DIVERT
#define IPPROTO_DIVERT 258
#endif
#ifndef AF_DIVERT
#define AF_DIVERT 44 /* divert(4) */
#endif
#ifndef PF_DIVERT
#define PF_DIVERT AF_DIVERT
#endif
#define TCP_KIND_END 0
#define TCP_KIND_NOOP 1
#define TCP_KIND_MSS 2
#define TCP_KIND_SCALE 3
#define TCP_KIND_SACK_PERM 4
#define TCP_KIND_SACK 5
#define TCP_KIND_TS 8
#define TCP_KIND_MD5 19
#define TCP_KIND_AO 29
#define TCP_KIND_FASTOPEN 34
#ifndef IPPROTO_MH
#define IPPROTO_MH 135
#endif
#ifndef IPPROTO_HIP
#define IPPROTO_HIP 139
#endif
#ifndef IPPROTO_SHIM6
#define IPPROTO_SHIM6 140
#endif
// returns netorder value
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment);
uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment);
#define SCALE_NONE ((uint8_t)-1)
#define VERDICT_PASS 0
#define VERDICT_MODIFY 1
#define VERDICT_DROP 2
#define VERDICT_MASK 3
#define VERDICT_NOCSUM 4
#define VERDICT_MASK_VALID 7
#define IP4_TOS(ip_header) (ip_header ? ip_header->ip_tos : 0)
#define IP4_IP_ID(ip_header) (ip_header ? ip_header->ip_id : 0)
#define IP6_FLOW(ip6_header) (ip6_header ? ip6_header->ip6_ctlun.ip6_un1.ip6_un1_flow : 0)
void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport);
void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr,const struct udphdr *udphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst);
bool extract_dst(const uint8_t *data, size_t len, struct sockaddr* dst);
uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind);
uint32_t *tcp_find_timestamps(struct tcphdr *tcp);
uint8_t tcp_find_scale_factor(const struct tcphdr *tcp);
uint16_t tcp_find_mss(const struct tcphdr *tcp);
bool tcp_has_sack(struct tcphdr *tcp);
bool tcp_has_fastopen(const struct tcphdr *tcp);
bool ip_has_df(const struct ip *ip);
#ifdef __CYGWIN__
extern uint32_t w_win32_error;
bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter);
bool win_dark_deinit(void);
bool logical_net_filter_match(void);
bool nlm_list(bool bAll);
bool windivert_init(const char *filter);
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa);
bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa);
#else
// should pre-do it if dropping privileges. otherwise its not necessary
bool rawsend_preinit(bool bind_fix4, bool bind_fix6);
#endif
// auto creates internal socket and uses it for subsequent calls
bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len);
bool rawsend_rp(const struct rawpacket *rp);
// return trues if all packets were send successfully
bool rawsend_queue(struct rawpacket_tailhead *q);
// cleans up socket autocreated by rawsend
void rawsend_cleanup(void);
bool rawsend_rep(int repeats, const struct sockaddr* dst, uint32_t fwmark, const char *ifout, const void *data, size_t len);
#ifdef BSD
int socket_divert(sa_family_t family);
#endif
const char *proto_name(uint8_t proto);
uint16_t family_from_proto(uint8_t l3proto);
void print_ip(const struct ip *ip);
void print_ip6hdr(const struct ip6_hdr *ip6hdr, uint8_t proto);
void print_tcphdr(const struct tcphdr *tcphdr);
void print_udphdr(const struct udphdr *udphdr);
void str_ip(char *s, size_t s_len, const struct ip *ip);
void str_ip6hdr(char *s, size_t s_len, const struct ip6_hdr *ip6hdr, uint8_t proto);
void str_srcdst_ip6(char *s, size_t s_len, const void *saddr,const void *daddr);
void str_tcphdr(char *s, size_t s_len, const struct tcphdr *tcphdr);
void str_udphdr(char *s, size_t s_len, const struct udphdr *udphdr);
bool proto_check_ipv4(const uint8_t *data, size_t len);
void proto_skip_ipv4(const uint8_t **data, size_t *len);
bool proto_check_ipv6(const uint8_t *data, size_t len);
void proto_skip_ipv6(const uint8_t **data, size_t *len, uint8_t *proto_type, const uint8_t **last_header_type);
bool proto_set_last_ip6_proto(struct ip6_hdr *ip6, size_t len, uint8_t proto);
uint8_t *proto_find_ip6_exthdr(struct ip6_hdr *ip6, size_t len, uint8_t proto);
bool proto_check_tcp(const uint8_t *data, size_t len);
void proto_skip_tcp(const uint8_t **data, size_t *len);
bool proto_check_udp(const uint8_t *data, size_t len);
void proto_skip_udp(const uint8_t **data, size_t *len);
struct dissect
{
const uint8_t *data_pkt;
size_t len_pkt;
const struct ip *ip;
const struct ip6_hdr *ip6;
size_t len_l3;
uint8_t proto;
const struct tcphdr *tcp;
const struct udphdr *udp;
size_t len_l4;
size_t transport_len;
const uint8_t *data_payload;
size_t len_payload;
};
void proto_dissect_l3l4(const uint8_t *data, size_t len, struct dissect *dis);
bool tcp_synack_segment(const struct tcphdr *tcphdr);
bool tcp_syn_segment(const struct tcphdr *tcphdr);
bool tcp_ack_segment(const struct tcphdr *tcphdr);
// scale_factor=SCALE_NONE - do not change
void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor);
void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor);
uint8_t ttl46(const struct ip *ip, const struct ip6_hdr *ip6);
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, const struct ip *ip, const struct ip6_hdr *ip6hdr);
void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transport_len, const struct ip *ip, const struct ip6_hdr *ip6hdr);
void dbgprint_socket_buffers(int fd);
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf);
#ifdef HAS_FILTER_SSID
struct wlan_interface
{
int ifindex;
char ifname[IFNAMSIZ], ssid[33];
};
#define WLAN_INTERFACE_MAX 16
struct wlan_interface_collection
{
int count;
struct wlan_interface wlan[WLAN_INTERFACE_MAX];
};
extern struct wlan_interface_collection wlans;
void wlan_info_deinit(void);
bool wlan_info_init(void);
bool wlan_info_get(void);
bool wlan_info_get_rate_limited(void);
const char *wlan_ssid_search_ifname(const char *ifname);
const char *wlan_ssid_search_ifidx(int ifidx);
#endif

1943
nfq2/desync.c Normal file

File diff suppressed because it is too large Load Diff

20
nfq2/desync.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "darkmagic.h"
#include <stdint.h>
#include <stdbool.h>
#define __FAVOR_BSD
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#ifdef __linux__
#define DPI_DESYNC_FWMARK_DEFAULT 0x40000000
#else
#define DPI_DESYNC_FWMARK_DEFAULT 512
#endif
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifin, const char *ifout, const uint8_t *data_pkt, size_t len_pkt, uint8_t *mod_pkt, size_t *len_mod_pkt);

79
nfq2/gzip.c Normal file
View File

@@ -0,0 +1,79 @@
#include "gzip.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ZCHUNK 16384
#define BUFMIN 128
#define BUFCHUNK (1024*128)
int z_readfile(FILE *F, char **buf, size_t *size)
{
z_stream zs;
int r;
unsigned char in[ZCHUNK];
size_t bufsize;
void *newbuf;
memset(&zs, 0, sizeof(zs));
*buf = NULL;
bufsize = *size = 0;
r = inflateInit2(&zs, 47);
if (r != Z_OK) return r;
do
{
zs.avail_in = fread(in, 1, sizeof(in), F);
if (ferror(F))
{
r = Z_ERRNO;
goto zerr;
}
if (!zs.avail_in) break;
zs.next_in = in;
do
{
if ((bufsize - *size) < BUFMIN)
{
bufsize += BUFCHUNK;
newbuf = *buf ? realloc(*buf, bufsize) : malloc(bufsize);
if (!newbuf)
{
r = Z_MEM_ERROR;
goto zerr;
}
*buf = newbuf;
}
zs.avail_out = bufsize - *size;
zs.next_out = (unsigned char*)(*buf + *size);
r = inflate(&zs, Z_NO_FLUSH);
if (r != Z_OK && r != Z_STREAM_END) goto zerr;
*size = bufsize - zs.avail_out;
} while (r == Z_OK && zs.avail_in);
} while (r == Z_OK);
if (*size < bufsize)
{
// free extra space
if ((newbuf = realloc(*buf, *size))) *buf = newbuf;
}
inflateEnd(&zs);
return Z_OK;
zerr:
inflateEnd(&zs);
free(*buf);
*buf = NULL;
return r;
}
bool is_gzip(FILE* F)
{
unsigned char magic[2];
bool b = !fseek(F, 0, SEEK_SET) && fread(magic, 1, 2, F) == 2 && magic[0] == 0x1F && magic[1] == 0x8B;
fseek(F, 0, SEEK_SET);
return b;
}

8
nfq2/gzip.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include <stdio.h>
#include <zlib.h>
#include <stdbool.h>
int z_readfile(FILE *F,char **buf,size_t *size);
bool is_gzip(FILE* F);

746
nfq2/helpers.c Normal file
View File

@@ -0,0 +1,746 @@
#define _GNU_SOURCE
#include "helpers.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <libgen.h>
#include <fcntl.h>
#define UNIQ_SORT \
{ \
int i, j, u; \
for (i = j = 0; j < ct; i++) \
{ \
u = pu[j++]; \
for (; j < ct && pu[j] == u; j++); \
pu[i] = u; \
} \
return i; \
}
int unique_size_t(size_t *pu, int ct) UNIQ_SORT
int unique_ssize_t(ssize_t *pu, int ct) UNIQ_SORT
static int cmp_size_t(const void * a, const void * b)
{
return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b;
}
void qsort_size_t(size_t *array, int ct)
{
qsort(array,ct,sizeof(*array),cmp_size_t);
}
static int cmp_ssize_t(const void * a, const void * b)
{
return *(ssize_t*)a < *(ssize_t*)b ? -1 : *(ssize_t*)a > *(ssize_t*)b;
}
void qsort_ssize_t(ssize_t *array, int ct)
{
qsort(array,ct,sizeof(*array),cmp_ssize_t);
}
int str_index(const char **strs, int count, const char *str)
{
for(int i=0;i<count;i++)
if (!strcmp(strs[i],str)) return i;
return -1;
}
void rtrim(char *s)
{
if (s)
for (char *p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r'); p--) *p = '\0';
}
void replace_char(char *s, char from, char to)
{
for(;*s;s++) if (*s==from) *s=to;
}
char *strncasestr(const char *s, const char *find, size_t slen)
{
char c, sc;
size_t len;
if ((c = *find++) != '\0')
{
len = strlen(find);
do
{
do
{
if (slen-- < 1 || (sc = *s++) == '\0') return NULL;
} while (toupper(c) != toupper(sc));
if (len > slen) return NULL;
} while (strncasecmp(s, find, len) != 0);
s--;
}
return (char *)s;
}
static inline bool is_letter(char c)
{
return (c>='a' && c<='z') || (c>='A' && c<='Z');
}
static inline bool is_digit(char c)
{
return c>='0' && c<='9';
}
bool is_identifier(const char *p)
{
if (*p!='_' && !is_letter(*p))
return false;
for(++p;*p;p++)
if (!is_letter(*p) && !is_digit(*p) && *p!='_')
return false;
return true;
}
bool load_file(const char *filename, off_t offset, void *buffer, size_t *buffer_size)
{
FILE *F;
F = fopen(filename, "rb");
if (!F) return false;
if (offset)
{
if (-1 == lseek(fileno(F), offset, SEEK_SET))
{
fclose(F);
return false;
}
}
*buffer_size = fread(buffer, 1, *buffer_size, F);
if (ferror(F))
{
fclose(F);
return false;
}
fclose(F);
return true;
}
bool load_file_nonempty(const char *filename, off_t offset, void *buffer, size_t *buffer_size)
{
bool b = load_file(filename, offset, buffer, buffer_size);
return b && *buffer_size;
}
bool save_file(const char *filename, const void *buffer, size_t buffer_size)
{
FILE *F;
F = fopen(filename, "wb");
if (!F) return false;
fwrite(buffer, 1, buffer_size, F);
if (ferror(F))
{
fclose(F);
return false;
}
fclose(F);
return true;
}
bool append_to_list_file(const char *filename, const char *s)
{
FILE *F = fopen(filename,"at");
if (!F) return false;
bool bOK = fprintf(F,"%s\n",s)>0;
fclose(F);
return bOK;
}
void expand_bits(void *target, const void *source, unsigned int source_bitlen, unsigned int target_bytelen)
{
unsigned int target_bitlen = target_bytelen<<3;
unsigned int bitlen = target_bitlen<source_bitlen ? target_bitlen : source_bitlen;
unsigned int bytelen = bitlen>>3;
if ((target_bytelen-bytelen)>=1) memset(target+bytelen,0,target_bytelen-bytelen);
memcpy(target,source,bytelen);
if ((bitlen &= 7)) ((uint8_t*)target)[bytelen] = ((uint8_t*)source)[bytelen] & (~((1 << (8-bitlen)) - 1));
}
// " [fd00::1]" => "fd00::1"
// "[fd00::1]:8000" => "fd00::1"
// "127.0.0.1" => "127.0.0.1"
// " 127.0.0.1:8000" => "127.0.0.1"
// " vk.com:8000" => "vk.com"
// return value: true - host is ip addr
bool strip_host_to_ip(char *host)
{
size_t l;
char *h,*p;
uint8_t addr[16];
for (h = host ; *h==' ' || *h=='\t' ; h++);
l = strlen(h);
if (l>=2)
{
if (*h=='[')
{
// ipv6 ?
for (p=++h ; *p && *p!=']' ; p++);
if (*p==']')
{
l = p-h;
memmove(host,h,l);
host[l]=0;
return inet_pton(AF_INET6, host, addr)>0;
}
}
else
{
if (inet_pton(AF_INET6, h, addr)>0)
{
// ipv6 ?
if (host!=h)
{
l = strlen(h);
memmove(host,h,l);
host[l]=0;
}
return true;
}
else
{
// ipv4 ?
for (p=h ; *p && *p!=':' ; p++);
l = p-h;
if (host!=h) memmove(host,h,l);
host[l]=0;
return inet_pton(AF_INET, host, addr)>0;
}
}
}
return false;
}
void ntopa46(const struct in_addr *ip, const struct in6_addr *ip6,char *str, size_t len)
{
if (!len) return;
*str = 0;
if (ip) inet_ntop(AF_INET, ip, str, len);
else if (ip6) inet_ntop(AF_INET6, ip6, str, len);
else snprintf(str, len, "UNKNOWN_FAMILY");
}
void ntop46(const struct sockaddr *sa, char *str, size_t len)
{
ntopa46(sa->sa_family==AF_INET ? &((struct sockaddr_in*)sa)->sin_addr : NULL,
sa->sa_family==AF_INET6 ? &((struct sockaddr_in6*)sa)->sin6_addr : NULL,
str, len);
}
void ntop46_port(const struct sockaddr *sa, char *str, size_t len)
{
char ip[40];
ntop46(sa, ip, sizeof(ip));
switch (sa->sa_family)
{
case AF_INET:
snprintf(str, len, "%s:%u", ip, ntohs(((struct sockaddr_in*)sa)->sin_port));
break;
case AF_INET6:
snprintf(str, len, "[%s]:%u", ip, ntohs(((struct sockaddr_in6*)sa)->sin6_port));
break;
default:
snprintf(str, len, "%s", ip);
}
}
void print_sockaddr(const struct sockaddr *sa)
{
char ip_port[48];
ntop46_port(sa, ip_port, sizeof(ip_port));
printf("%s", ip_port);
}
bool pton4_port(const char *s, struct sockaddr_in *sa)
{
char ip[16],*p;
size_t l;
unsigned int u;
p = strchr(s,':');
if (!p) return false;
l = p-s;
if (l<7 || l>15) return false;
memcpy(ip,s,l);
ip[l]=0;
p++;
sa->sin_family = AF_INET;
if (inet_pton(AF_INET,ip,&sa->sin_addr)!=1 || sscanf(p,"%u",&u)!=1 || !u || u>0xFFFF) return false;
sa->sin_port = htons((uint16_t)u);
return true;
}
bool pton6_port(const char *s, struct sockaddr_in6 *sa)
{
char ip[40],*p;
size_t l;
unsigned int u;
if (*s++!='[') return false;
p = strchr(s,']');
if (!p || p[1]!=':') return false;
l = p-s;
if (l<2 || l>39) return false;
p+=2;
memcpy(ip,s,l);
ip[l]=0;
sa->sin6_family = AF_INET6;
if (inet_pton(AF_INET6,ip,&sa->sin6_addr)!=1 || sscanf(p,"%u",&u)!=1 || !u || u>0xFFFF) return false;
sa->sin6_port = htons((uint16_t)u);
sa->sin6_flowinfo = 0;
sa->sin6_scope_id = 0;
return true;
}
uint16_t saport(const struct sockaddr *sa)
{
return ntohs(sa->sa_family==AF_INET ? ((struct sockaddr_in*)sa)->sin_port :
sa->sa_family==AF_INET6 ? ((struct sockaddr_in6*)sa)->sin6_port : 0);
}
bool sa_has_addr(const struct sockaddr *sa)
{
switch(sa->sa_family)
{
case AF_INET:
return ((struct sockaddr_in*)sa)->sin_addr.s_addr!=INADDR_ANY;
case AF_INET6:
return memcmp(((struct sockaddr_in6*)sa)->sin6_addr.s6_addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16);
default:
return false;
}
}
bool seq_within(uint32_t s, uint32_t s1, uint32_t s2)
{
return (s2>=s1 && s>=s1 && s<=s2) || (s2<s1 && (s<=s2 || s>=s1));
}
bool ipv6_addr_is_zero(const struct in6_addr *a)
{
return !memcmp(a,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",16);
}
uint16_t pntoh16(const uint8_t *p)
{
return ((uint16_t)p[0] << 8) | (uint16_t)p[1];
}
void phton16(uint8_t *p, uint16_t v)
{
p[0] = (uint8_t)(v >> 8);
p[1] = v & 0xFF;
}
uint32_t pntoh24(const uint8_t *p)
{
return ((uint32_t)p[0] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[2];
}
void phton24(uint8_t *p, uint32_t v)
{
p[0] = (uint8_t)(v>>16);
p[1] = (uint8_t)(v>>8);
p[2] = (uint8_t)v;
}
uint32_t pntoh32(const uint8_t *p)
{
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
}
void phton32(uint8_t *p, uint32_t v)
{
p[0] = (uint8_t)(v>>24);
p[1] = (uint8_t)(v>>16);
p[2] = (uint8_t)(v>>8);
p[3] = (uint8_t)v;
}
uint64_t pntoh64(const uint8_t *p)
{
return ((uint64_t)p[0] << 56) | ((uint64_t)p[1] << 48) | ((uint64_t)p[2] << 40) | ((uint64_t)p[3] << 32) | ((uint64_t)p[4] << 24) | ((uint64_t)p[5] << 16) | ((uint64_t)p[6] << 8) | p[7];
}
void phton64(uint8_t *p, uint64_t v)
{
p[0] = (uint8_t)(v>>56);
p[1] = (uint8_t)(v>>48);
p[2] = (uint8_t)(v>>40);
p[3] = (uint8_t)(v>>32);
p[4] = (uint8_t)(v>>24);
p[5] = (uint8_t)(v>>16);
p[6] = (uint8_t)(v>>8);
p[7] = (uint8_t)v;
}
#define INVALID_HEX_DIGIT ((uint8_t)-1)
static inline uint8_t parse_hex_digit(char c)
{
return (c>='0' && c<='9') ? c-'0' : (c>='a' && c<='f') ? c-'a'+0xA : (c>='A' && c<='F') ? c-'A'+0xA : INVALID_HEX_DIGIT;
}
static inline bool parse_hex_byte(const char *s, uint8_t *pbyte)
{
uint8_t u,l;
u = parse_hex_digit(s[0]);
l = parse_hex_digit(s[1]);
if (u==INVALID_HEX_DIGIT || l==INVALID_HEX_DIGIT)
{
*pbyte=0;
return false;
}
else
{
*pbyte=(u<<4) | l;
return true;
}
}
bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size)
{
uint8_t *pe = pbuf+*size;
*size=0;
while(pbuf<pe && *s)
{
if (!parse_hex_byte(s,pbuf))
return false;
pbuf++; s+=2; (*size)++;
}
return true;
}
void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize,size_t offset)
{
size_t size;
if (offset%=patsize)
{
size = patsize-offset;
size = bufsize>size ? size : bufsize;
memcpy(buf,pattern+offset,size);
buf += size;
bufsize -= size;
}
while (bufsize)
{
size = bufsize>patsize ? patsize : bufsize;
memcpy(buf,pattern,size);
buf += size;
bufsize -= size;
}
}
int fprint_localtime(FILE *F)
{
struct tm t;
time_t now;
time(&now);
localtime_r(&now,&t);
return fprintf(F, "%02d.%02d.%04d %02d:%02d:%02d", t.tm_mday, t.tm_mon + 1, t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec);
}
bool file_size(const char *filename, off_t *size)
{
struct stat st;
if (stat(filename,&st)==-1) return false;
*size = st.st_size;
return true;
}
time_t file_mod_time(const char *filename)
{
struct stat st;
return stat(filename,&st)==-1 ? 0 : st.st_mtime;
}
bool file_mod_signature(const char *filename, file_mod_sig *ms)
{
struct stat st;
if (stat(filename,&st)==-1)
{
FILE_MOD_RESET(ms);
return false;
}
ms->mod_time=st.st_mtime;
ms->size=st.st_size;
return true;
}
bool file_open_test(const char *filename, int flags)
{
int fd = open(filename,flags);
if (fd>=0)
{
close(fd);
return true;
}
return false;
}
bool pf_in_range(uint16_t port, const port_filter *pf)
{
return port && (((!pf->from && !pf->to) || (port>=pf->from && port<=pf->to)) ^ pf->neg);
}
bool pf_parse(const char *s, port_filter *pf)
{
unsigned int v1,v2;
char c;
if (!s) return false;
if (*s=='*' && s[1]==0)
{
pf->from=1; pf->to=0xFFFF;
return true;
}
if (*s=='~')
{
pf->neg=true;
s++;
}
else
pf->neg=false;
if (sscanf(s,"%u-%u%c",&v1,&v2,&c)==2)
{
if (v1>65535 || v2>65535 || v1>v2) return false;
pf->from=(uint16_t)v1;
pf->to=(uint16_t)v2;
}
else if (sscanf(s,"%u%c",&v1,&c)==1)
{
if (v1>65535) return false;
pf->to=pf->from=(uint16_t)v1;
}
else
return false;
// deny all case
if (!pf->from && !pf->to) pf->neg=true;
return true;
}
bool pf_is_empty(const port_filter *pf)
{
return !pf->neg && !pf->from && !pf->to;
}
bool packet_pos_parse(const char *s, struct packet_pos *pos)
{
if (*s!='n' && *s!='d' && *s!='s' && *s!='b' && *s!='x' && *s!='a') return false;
pos->mode=*s;
if (pos->mode=='x' || pos->mode=='a')
{
pos->pos=0;
return true;
}
return sscanf(s+1,"%u",&pos->pos)==1;
}
bool packet_range_parse(const char *s, struct packet_range *range)
{
const char *p;
range->upper_cutoff = false;
if (*s=='-' || *s=='<')
{
range->from = PACKET_POS_ALWAYS;
range->upper_cutoff = *s=='<';
}
else
{
if (!packet_pos_parse(s,&range->from)) return false;
if (range->from.mode=='x')
{
range->to = range->from;
return true;
}
if (!(p = strchr(s,'-')))
p = strchr(s,'<');
if (p)
{
s = p;
range->upper_cutoff = *s=='<';
}
else
{
if (range->from.mode=='a')
{
range->to = range->from;
return true;
}
return false;
}
}
s++;
if (*s)
{
return packet_pos_parse(s,&range->to);
}
else
{
range->to = PACKET_POS_ALWAYS;
return true;
}
}
void fill_random_bytes(uint8_t *p,size_t sz)
{
size_t k,sz16 = sz>>1;
for(k=0;k<sz16;k++) ((uint16_t*)p)[k]=(uint16_t)random();
if (sz & 1) p[sz-1]=(uint8_t)random();
}
void fill_random_az(uint8_t *p,size_t sz)
{
size_t k;
for(k=0;k<sz;k++) p[k] = 'a'+(random() % ('z'-'a'));
}
void fill_random_az09(uint8_t *p,size_t sz)
{
size_t k;
uint8_t rnd;
for(k=0;k<sz;k++)
{
rnd = random() % (10 + 'z'-'a'+1);
p[k] = rnd<10 ? rnd+'0' : 'a'+rnd-10;
}
}
bool fill_crypto_random_bytes(uint8_t *p,size_t sz)
{
bool b;
FILE *F = fopen("/dev/random","rb");
if (!F) return false;
b = fread(p,sz,1,F)==1;
fclose(F);
return b;
}
void set_console_io_buffering(void)
{
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(stderr, NULL, _IOLBF, 0);
}
bool set_env_exedir(const char *argv0)
{
char *s,*d;
bool bOK=false;
if ((s = strdup(argv0)))
{
if ((d = dirname(s)))
setenv("EXEDIR",s,1);
free(s);
}
return bOK;
}
static void mask_from_preflen6_make(uint8_t plen, struct in6_addr *a)
{
if (plen >= 128)
memset(a->s6_addr,0xFF,16);
else
{
uint8_t n = plen >> 3;
memset(a->s6_addr,0xFF,n);
memset(a->s6_addr+n,0x00,16-n);
a->s6_addr[n] = (uint8_t)(0xFF00 >> (plen & 7));
}
}
struct in6_addr ip6_mask[129];
void mask_from_preflen6_prepare(void)
{
for (int plen=0;plen<=128;plen++) mask_from_preflen6_make(plen, ip6_mask+plen);
}
#if defined(__GNUC__) && !defined(__llvm__)
__attribute__((optimize ("no-strict-aliasing")))
#endif
void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result)
{
// int128 requires 16-bit alignment. in struct sockaddr_in6.sin6_addr is 8-byte aligned.
// it causes segfault on x64 arch with latest compiler. it can cause misalign slowdown on other archs
// use 64-bit AND
((uint64_t*)result->s6_addr)[0] = ((uint64_t*)a->s6_addr)[0] & ((uint64_t*)b->s6_addr)[0];
((uint64_t*)result->s6_addr)[1] = ((uint64_t*)a->s6_addr)[1] & ((uint64_t*)b->s6_addr)[1];
}
void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr)
{
char s_ip[16];
*s_ip=0;
inet_ntop(AF_INET, &cidr->addr, s_ip, sizeof(s_ip));
snprintf(s,s_len,cidr->preflen<32 ? "%s/%u" : "%s", s_ip, cidr->preflen);
}
void print_cidr4(const struct cidr4 *cidr)
{
char s[19];
str_cidr4(s,sizeof(s),cidr);
printf("%s",s);
}
void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr)
{
char s_ip[40];
*s_ip=0;
inet_ntop(AF_INET6, &cidr->addr, s_ip, sizeof(s_ip));
snprintf(s,s_len,cidr->preflen<128 ? "%s/%u" : "%s", s_ip, cidr->preflen);
}
void print_cidr6(const struct cidr6 *cidr)
{
char s[44];
str_cidr6(s,sizeof(s),cidr);
printf("%s",s);
}
bool parse_cidr4(char *s, struct cidr4 *cidr)
{
char *p,d;
bool b;
unsigned int plen;
if ((p = strchr(s, '/')))
{
if (sscanf(p + 1, "%u", &plen)!=1 || plen>32)
return false;
cidr->preflen = (uint8_t)plen;
d=*p; *p=0; // backup char
}
else
cidr->preflen = 32;
b = (inet_pton(AF_INET, s, &cidr->addr)==1);
if (p) *p=d; // restore char
return b;
}
bool parse_cidr6(char *s, struct cidr6 *cidr)
{
char *p,d;
bool b;
unsigned int plen;
if ((p = strchr(s, '/')))
{
if (sscanf(p + 1, "%u", &plen)!=1 || plen>128)
return false;
cidr->preflen = (uint8_t)plen;
d=*p; *p=0; // backup char
}
else
cidr->preflen = 128;
b = (inet_pton(AF_INET6, s, &cidr->addr)==1);
if (p) *p=d; // restore char
return b;
}
bool parse_int16(const char *p, int16_t *v)
{
if (*p == '+' || *p == '-' || *p >= '0' && *p <= '9')
{
int i = atoi(p);
*v = (int16_t)i;
return *v == i; // check overflow
}
return false;
}

147
nfq2/helpers.h Normal file
View File

@@ -0,0 +1,147 @@
#pragma once
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#define UNARY_PLUS(v) (v>0 ? "+" : "")
// this saves memory. sockaddr_storage is larger than required. it can be 128 bytes. sockaddr_in6 is 28 bytes.
typedef union
{
struct sockaddr_in sa4; // size 16
struct sockaddr_in6 sa6; // size 28
char _align[32]; // force 16-byte alignment for ip6_and int128 ops
} sockaddr_in46;
int unique_size_t(size_t *pu, int ct);
int unique_ssize_t(ssize_t *pu, int ct);
void qsort_size_t(size_t *array, int ct);
void qsort_ssize_t(ssize_t *array, int ct);
int str_index(const char **strs, int count, const char *str);
void rtrim(char *s);
void replace_char(char *s, char from, char to);
char *strncasestr(const char *s,const char *find, size_t slen);
// [a-zA-z][a-zA-Z0-9]*
bool is_identifier(const char *p);
bool load_file(const char *filename, off_t offset, void *buffer, size_t *buffer_size);
bool load_file_nonempty(const char *filename, off_t offset, void *buffer, size_t *buffer_size);
bool save_file(const char *filename, const void *buffer, size_t buffer_size);
bool append_to_list_file(const char *filename, const char *s);
void expand_bits(void *target, const void *source, unsigned int source_bitlen, unsigned int target_bytelen);
bool strip_host_to_ip(char *host);
void print_sockaddr(const struct sockaddr *sa);
void ntopa46(const struct in_addr *ip, const struct in6_addr *ip6,char *str, size_t len);
void ntop46(const struct sockaddr *sa, char *str, size_t len);
void ntop46_port(const struct sockaddr *sa, char *str, size_t len);
bool pton4_port(const char *s, struct sockaddr_in *sa);
bool pton6_port(const char *s, struct sockaddr_in6 *sa);
uint16_t saport(const struct sockaddr *sa);
bool sa_has_addr(const struct sockaddr *sa);
bool seq_within(uint32_t s, uint32_t s1, uint32_t s2);
bool ipv6_addr_is_zero(const struct in6_addr *a);
uint16_t pntoh16(const uint8_t *p);
void phton16(uint8_t *p, uint16_t v);
uint32_t pntoh24(const uint8_t *p);
void phton24(uint8_t *p, uint32_t v);
uint32_t pntoh32(const uint8_t *p);
void phton32(uint8_t *p, uint32_t v);
uint64_t pntoh64(const uint8_t *p);
void phton64(uint8_t *p, uint64_t v);
bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size);
void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize,size_t offset);
int fprint_localtime(FILE *F);
typedef struct
{
time_t mod_time;
off_t size;
} file_mod_sig;
#define FILE_MOD_COMPARE(ms1,ms2) (((ms1)->mod_time==(ms2)->mod_time) && ((ms1)->size==(ms2)->size))
#define FILE_MOD_RESET(ms) memset(ms,0,sizeof(file_mod_sig))
bool file_mod_signature(const char *filename, file_mod_sig *ms);
time_t file_mod_time(const char *filename);
bool file_size(const char *filename, off_t *size);
bool file_open_test(const char *filename, int flags);
typedef struct
{
uint16_t from,to;
bool neg;
} port_filter;
bool pf_in_range(uint16_t port, const port_filter *pf);
bool pf_parse(const char *s, port_filter *pf);
bool pf_is_empty(const port_filter *pf);
struct packet_pos
{
char mode; // n - packets, d - data packets, s - relative sequence
unsigned int pos;
};
struct packet_range
{
struct packet_pos from, to;
bool upper_cutoff; // true - do not include upper limit, false - include upper limit
};
#define PACKET_POS_NEVER (struct packet_pos){'x',0}
#define PACKET_POS_ALWAYS (struct packet_pos){'a',0}
#define PACKET_RANGE_NEVER (struct packet_range){PACKET_POS_NEVER,PACKET_POS_NEVER}
#define PACKET_RANGE_ALWAYS (struct packet_range){PACKET_POS_ALWAYS,PACKET_POS_ALWAYS}
bool packet_range_parse(const char *s, struct packet_range *range);
void fill_random_bytes(uint8_t *p,size_t sz);
void fill_random_az(uint8_t *p,size_t sz);
void fill_random_az09(uint8_t *p,size_t sz);
bool fill_crypto_random_bytes(uint8_t *p,size_t sz);
void set_console_io_buffering(void);
bool set_env_exedir(const char *argv0);
struct cidr4
{
struct in_addr addr;
uint8_t preflen;
};
struct cidr6
{
struct in6_addr addr;
uint8_t preflen;
};
void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr);
void print_cidr4(const struct cidr4 *cidr);
void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr);
void print_cidr6(const struct cidr6 *cidr);
bool parse_cidr4(char *s, struct cidr4 *cidr);
bool parse_cidr6(char *s, struct cidr6 *cidr);
bool parse_int16(const char *p, int16_t *v);
static inline uint32_t mask_from_preflen(uint32_t preflen)
{
return preflen ? preflen<32 ? ~((1 << (32-preflen)) - 1) : 0xFFFFFFFF : 0;
}
void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result);
extern struct in6_addr ip6_mask[129];
void mask_from_preflen6_prepare(void);
static inline const struct in6_addr *mask_from_preflen6(uint8_t preflen)
{
return ip6_mask+preflen;
}

340
nfq2/hostlist.c Normal file
View File

@@ -0,0 +1,340 @@
#include <stdio.h>
#include "hostlist.h"
#include "gzip.h"
#include "helpers.h"
// inplace tolower() and add to pool
static bool addpool(hostlist_pool **hostlist, char **s, const char *end, int *ct)
{
char *p=*s;
// comment line
if ( *p == '#' || *p == ';' || *p == '/' || *p == '\r' || *p == '\n')
{
// advance until eol
for (; p<end && *p && *p!='\r' && *p != '\n'; p++);
}
else
{
// advance until eol lowering all chars
uint32_t flags = 0;
if (*p=='^')
{
p = ++(*s);
flags |= HOSTLIST_POOL_FLAG_STRICT_MATCH;
}
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!HostlistPoolAddStrLen(hostlist, *s, p-*s, flags))
{
HostlistPoolDestroy(hostlist);
*hostlist = NULL;
return false;
}
if (ct) (*ct)++;
}
// advance to the next line
for (; p<end && (!*p || *p=='\r' || *p=='\n') ; p++);
*s = p;
return true;
}
bool AppendHostlistItem(hostlist_pool **hostlist, char *s)
{
return addpool(hostlist,&s,s+strlen(s),NULL);
}
bool AppendHostList(hostlist_pool **hostlist, const char *filename)
{
char *p, *e, s[256], *zbuf;
size_t zsize;
int ct = 0;
FILE *F;
int r;
DLOG_CONDUP("Loading hostlist %s\n",filename);
if (!(F = fopen(filename, "rb")))
{
DLOG_ERR("Could not open %s\n", filename);
return false;
}
if (is_gzip(F))
{
r = z_readfile(F,&zbuf,&zsize);
fclose(F);
if (r==Z_OK)
{
DLOG_CONDUP("zlib compression detected. uncompressed size : %zu\n", zsize);
p = zbuf;
e = zbuf + zsize;
while(p<e)
{
if (!addpool(hostlist,&p,e,&ct))
{
DLOG_ERR("Not enough memory to store host list : %s\n", filename);
free(zbuf);
return false;
}
}
free(zbuf);
}
else
{
DLOG_ERR("zlib decompression failed : result %d\n",r);
return false;
}
}
else
{
DLOG_CONDUP("loading plain text list\n");
while (fgets(s, sizeof(s), F))
{
p = s;
if (!addpool(hostlist,&p,p+strlen(p),&ct))
{
DLOG_ERR("Not enough memory to store host list : %s\n", filename);
fclose(F);
return false;
}
}
fclose(F);
}
DLOG_CONDUP("Loaded %d hosts from %s\n", ct, filename);
return true;
}
static bool LoadHostList(struct hostlist_file *hfile)
{
if (hfile->filename)
{
file_mod_sig fsig;
if (!file_mod_signature(hfile->filename, &fsig))
{
// stat() error
DLOG_PERROR("file_mod_signature");
DLOG_ERR("cannot access hostlist file '%s'. in-memory content remains unchanged.\n",hfile->filename);
return true;
}
if (FILE_MOD_COMPARE(&hfile->mod_sig,&fsig)) return true; // up to date
HostlistPoolDestroy(&hfile->hostlist);
if (!AppendHostList(&hfile->hostlist, hfile->filename))
{
HostlistPoolDestroy(&hfile->hostlist);
return false;
}
hfile->mod_sig=fsig;
}
return true;
}
static bool LoadHostLists(struct hostlist_files_head *list)
{
bool bres=true;
struct hostlist_file *hfile;
LIST_FOREACH(hfile, list, next)
{
if (!LoadHostList(hfile))
// at least one failed
bres=false;
}
return bres;
}
bool NonEmptyHostlist(hostlist_pool **hostlist)
{
// add impossible hostname if the list is empty
return *hostlist ? true : HostlistPoolAddStrLen(hostlist, "@&()", 4, 0);
}
static void MakeAutolistsNonEmpty()
{
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
if (dpl->dp.hostlist_auto)
NonEmptyHostlist(&dpl->dp.hostlist_auto->hostlist);
}
}
bool LoadAllHostLists()
{
if (!LoadHostLists(&params.hostlists))
return false;
MakeAutolistsNonEmpty();
return true;
}
static bool SearchHostList(hostlist_pool *hostlist, const char *host, bool no_match_subdomains)
{
if (hostlist)
{
const char *p = host;
const struct hostlist_pool *hp;
bool bHostFull=true;
while (p)
{
DLOG("hostlist check for %s : ", p);
hp = HostlistPoolGetStr(hostlist, p);
if (hp)
{
if ((hp->flags & HOSTLIST_POOL_FLAG_STRICT_MATCH) && !bHostFull)
{
DLOG("negative : strict_mismatch : %s != %s\n", p, host);
}
else
{
DLOG("positive\n");
return true;
}
}
else
DLOG("negative\n");
if (no_match_subdomains) break;
p = strchr(p, '.');
if (p) p++;
bHostFull = false;
}
}
return false;
}
static bool HostlistsReloadCheck(const struct hostlist_collection_head *hostlists)
{
struct hostlist_item *item;
LIST_FOREACH(item, hostlists, next)
{
if (!LoadHostList(item->hfile))
return false;
}
MakeAutolistsNonEmpty();
return true;
}
bool HostlistsReloadCheckForProfile(const struct desync_profile *dp)
{
return HostlistsReloadCheck(&dp->hl_collection) && HostlistsReloadCheck(&dp->hl_collection_exclude);
}
// return : true = apply fooling, false = do not apply
static bool HostlistCheck_(const struct hostlist_collection_head *hostlists, const struct hostlist_collection_head *hostlists_exclude, const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck)
{
struct hostlist_item *item;
if (excluded) *excluded = false;
if (!bSkipReloadCheck)
if (!HostlistsReloadCheck(hostlists) || !HostlistsReloadCheck(hostlists_exclude))
return false;
LIST_FOREACH(item, hostlists_exclude, next)
{
DLOG("[%s] exclude ", item->hfile->filename ? item->hfile->filename : "fixed");
if (SearchHostList(item->hfile->hostlist, host, no_match_subdomains))
{
if (excluded) *excluded = true;
return false;
}
}
// old behavior compat: all include lists are empty means check passes
if (!hostlist_collection_is_empty(hostlists))
{
LIST_FOREACH(item, hostlists, next)
{
DLOG("[%s] include ", item->hfile->filename ? item->hfile->filename : "fixed");
if (SearchHostList(item->hfile->hostlist, host, no_match_subdomains))
return true;
}
return false;
}
return true;
}
// return : true = apply fooling, false = do not apply
bool HostlistCheck(const struct desync_profile *dp, const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck)
{
DLOG("* hostlist check for profile %u\n",dp->n);
return HostlistCheck_(&dp->hl_collection, &dp->hl_collection_exclude, host, no_match_subdomains, excluded, bSkipReloadCheck);
}
static struct hostlist_file *RegisterHostlist_(struct hostlist_files_head *hostlists, struct hostlist_collection_head *hl_collection, const char *filename)
{
struct hostlist_file *hfile;
if (filename)
{
if (!(hfile=hostlist_files_search(hostlists, filename)))
if (!(hfile=hostlist_files_add(hostlists, filename)))
return NULL;
if (!hostlist_collection_search(hl_collection, filename))
if (!hostlist_collection_add(hl_collection, hfile))
return NULL;
}
else
{
if (!(hfile=hostlist_files_add(hostlists, NULL)))
return NULL;
if (!hostlist_collection_add(hl_collection, hfile))
return NULL;
}
return hfile;
}
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename)
{
/*
if (filename && !file_mod_time(filename))
{
DLOG_ERR("cannot access hostlist file '%s'\n",filename);
return NULL;
}
*/
return RegisterHostlist_(
&params.hostlists,
bExclude ? &dp->hl_collection_exclude : &dp->hl_collection,
filename);
}
void HostlistsDebug()
{
if (!params.debug) return;
struct hostlist_file *hfile;
struct desync_profile_list *dpl;
struct hostlist_item *hl_item;
LIST_FOREACH(hfile, &params.hostlists, next)
{
if (hfile->filename)
DLOG("hostlist file %s%s\n",hfile->filename,hfile->hostlist ? "" : " (empty)");
else
DLOG("hostlist fixed%s\n",hfile->hostlist ? "" : " (empty)");
}
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
LIST_FOREACH(hl_item, &dpl->dp.hl_collection, next)
if (hl_item->hfile!=dpl->dp.hostlist_auto)
{
if (hl_item->hfile->filename)
DLOG("profile %u include hostlist %s%s\n",dpl->dp.n, hl_item->hfile->filename,hl_item->hfile->hostlist ? "" : " (empty)");
else
DLOG("profile %u include fixed hostlist%s\n",dpl->dp.n, hl_item->hfile->hostlist ? "" : " (empty)");
}
LIST_FOREACH(hl_item, &dpl->dp.hl_collection_exclude, next)
{
if (hl_item->hfile->filename)
DLOG("profile %u exclude hostlist %s%s\n",dpl->dp.n,hl_item->hfile->filename,hl_item->hfile->hostlist ? "" : " (empty)");
else
DLOG("profile %u exclude fixed hostlist%s\n",dpl->dp.n,hl_item->hfile->hostlist ? "" : " (empty)");
}
if (dpl->dp.hostlist_auto)
DLOG("profile %u auto hostlist %s%s\n",dpl->dp.n,dpl->dp.hostlist_auto->filename,dpl->dp.hostlist_auto->hostlist ? "" : " (empty)");
}
}

17
nfq2/hostlist.h Normal file
View File

@@ -0,0 +1,17 @@
#pragma once
#include <stdbool.h>
#include "pools.h"
#include "params.h"
bool AppendHostlistItem(hostlist_pool **hostlist, char *s);
bool AppendHostList(hostlist_pool **hostlist, const char *filename);
bool LoadAllHostLists();
bool NonEmptyHostlist(hostlist_pool **hostlist);
// return : true = apply fooling, false = do not apply
bool HostlistCheck(const struct desync_profile *dp,const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck);
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename);
bool HostlistsReloadCheckForProfile(const struct desync_profile *dp);
void HostlistsDebug();
#define ResetAllHostlistsModTime() hostlist_files_reset_modtime(&params.hostlists)

319
nfq2/ipset.c Normal file
View File

@@ -0,0 +1,319 @@
#include <stdio.h>
#include "ipset.h"
#include "gzip.h"
#include "helpers.h"
// inplace tolower() and add to pool
static bool addpool(ipset *ips, char **s, const char *end, int *ct)
{
char *p, cidr[128];
size_t l;
struct cidr4 c4;
struct cidr6 c6;
// advance until eol
for (p=*s; p<end && *p && *p!='\r' && *p != '\n'; p++);
// comment line
if (!(**s == '#' || **s == ';' || **s == '/' || **s == '\r' || **s == '\n' ))
{
l = p-*s;
if (l>=sizeof(cidr)) l=sizeof(cidr)-1;
memcpy(cidr,*s,l);
cidr[l]=0;
rtrim(cidr);
if (parse_cidr4(cidr,&c4))
{
if (!ipset4AddCidr(&ips->ips4, &c4))
{
ipsetDestroy(ips);
return false;
}
if (ct) (*ct)++;
}
else if (parse_cidr6(cidr,&c6))
{
if (!ipset6AddCidr(&ips->ips6, &c6))
{
ipsetDestroy(ips);
return false;
}
if (ct) (*ct)++;
}
else
DLOG_ERR("bad ip or subnet : %s\n",cidr);
}
// advance to the next line
for (; p<end && (!*p || *p=='\r' || *p=='\n') ; p++);
*s = p;
return true;
}
bool AppendIpsetItem(ipset *ips, char *ip)
{
return addpool(ips,&ip,ip+strlen(ip),NULL);
}
static bool AppendIpset(ipset *ips, const char *filename)
{
char *p, *e, s[256], *zbuf;
size_t zsize;
int ct = 0;
FILE *F;
int r;
DLOG_CONDUP("Loading ipset %s\n",filename);
if (!(F = fopen(filename, "rb")))
{
DLOG_ERR("Could not open %s\n", filename);
return false;
}
if (is_gzip(F))
{
r = z_readfile(F,&zbuf,&zsize);
fclose(F);
if (r==Z_OK)
{
DLOG_CONDUP("zlib compression detected. uncompressed size : %zu\n", zsize);
p = zbuf;
e = zbuf + zsize;
while(p<e)
{
if (!addpool(ips,&p,e,&ct))
{
DLOG_ERR("Not enough memory to store ipset : %s\n", filename);
free(zbuf);
return false;
}
}
free(zbuf);
}
else
{
DLOG_ERR("zlib decompression failed : result %d\n",r);
return false;
}
}
else
{
DLOG_CONDUP("loading plain text list\n");
while (fgets(s, sizeof(s)-1, F))
{
p = s;
if (!addpool(ips,&p,p+strlen(p),&ct))
{
DLOG_ERR("Not enough memory to store ipset : %s\n", filename);
fclose(F);
return false;
}
}
fclose(F);
}
DLOG_CONDUP("Loaded %d ip/subnets from %s\n", ct, filename);
return true;
}
static bool LoadIpset(struct ipset_file *hfile)
{
if (hfile->filename)
{
file_mod_sig fsig;
if (!file_mod_signature(hfile->filename, &fsig))
{
// stat() error
DLOG_PERROR("file_mod_signature");
DLOG_ERR("cannot access ipset file '%s'. in-memory content remains unchanged.\n",hfile->filename);
return true;
}
if (FILE_MOD_COMPARE(&hfile->mod_sig,&fsig)) return true; // up to date
ipsetDestroy(&hfile->ipset);
if (!AppendIpset(&hfile->ipset, hfile->filename))
{
ipsetDestroy(&hfile->ipset);
return false;
}
hfile->mod_sig=fsig;
}
return true;
}
static bool LoadIpsets(struct ipset_files_head *list)
{
bool bres=true;
struct ipset_file *hfile;
LIST_FOREACH(hfile, list, next)
{
if (!LoadIpset(hfile))
// at least one failed
bres=false;
}
return bres;
}
bool LoadAllIpsets()
{
return LoadIpsets(&params.ipsets);
}
static bool SearchIpset(const ipset *ips, const struct in_addr *ipv4, const struct in6_addr *ipv6)
{
char s_ip[40];
bool bInSet=false;
if (!!ipv4 != !!ipv6)
{
*s_ip=0;
if (ipv4)
{
if (params.debug) inet_ntop(AF_INET, ipv4, s_ip, sizeof(s_ip));
if (ips->ips4) bInSet = ipset4Check(ips->ips4, ipv4, 32);
}
if (ipv6)
{
if (params.debug) inet_ntop(AF_INET6, ipv6, s_ip, sizeof(s_ip));
if (ips->ips6) bInSet = ipset6Check(ips->ips6, ipv6, 128);
}
DLOG("ipset check for %s : %s\n", s_ip, bInSet ? "positive" : "negative");
}
else
// ipv4 and ipv6 are both empty or non-empty
DLOG("ipset check error !!!!!!!! ipv4=%p ipv6=%p\n",ipv4,ipv6);
return bInSet;
}
static bool IpsetsReloadCheck(const struct ipset_collection_head *ipsets)
{
struct ipset_item *item;
LIST_FOREACH(item, ipsets, next)
{
if (!LoadIpset(item->hfile))
return false;
}
return true;
}
bool IpsetsReloadCheckForProfile(const struct desync_profile *dp)
{
return IpsetsReloadCheck(&dp->ips_collection) && IpsetsReloadCheck(&dp->ips_collection_exclude);
}
static bool IpsetCheck_(const struct ipset_collection_head *ips, const struct ipset_collection_head *ips_exclude, const struct in_addr *ipv4, const struct in6_addr *ipv6)
{
struct ipset_item *item;
if (!IpsetsReloadCheck(ips) || !IpsetsReloadCheck(ips_exclude))
return false;
LIST_FOREACH(item, ips_exclude, next)
{
DLOG("[%s] exclude ",item->hfile->filename ? item->hfile->filename : "fixed");
if (SearchIpset(&item->hfile->ipset, ipv4, ipv6))
return false;
}
// old behavior compat: all include lists are empty means check passes
if (!ipset_collection_is_empty(ips))
{
LIST_FOREACH(item, ips, next)
{
DLOG("[%s] include ",item->hfile->filename ? item->hfile->filename : "fixed");
if (SearchIpset(&item->hfile->ipset, ipv4, ipv6))
return true;
}
return false;
}
return true;
}
bool IpsetCheck(const struct desync_profile *dp, const struct in_addr *ipv4, const struct in6_addr *ipv6)
{
if (PROFILE_IPSETS_ABSENT(dp)) return true;
DLOG("* ipset check for profile %u\n",dp->n);
return IpsetCheck_(&dp->ips_collection,&dp->ips_collection_exclude,ipv4,ipv6);
}
static struct ipset_file *RegisterIpset_(struct ipset_files_head *ipsets, struct ipset_collection_head *ips_collection, const char *filename)
{
struct ipset_file *hfile;
if (filename)
{
if (!(hfile=ipset_files_search(ipsets, filename)))
if (!(hfile=ipset_files_add(ipsets, filename)))
return NULL;
if (!ipset_collection_search(ips_collection, filename))
if (!ipset_collection_add(ips_collection, hfile))
return NULL;
}
else
{
if (!(hfile=ipset_files_add(ipsets, NULL)))
return NULL;
if (!ipset_collection_add(ips_collection, hfile))
return NULL;
}
return hfile;
}
struct ipset_file *RegisterIpset(struct desync_profile *dp, bool bExclude, const char *filename)
{
if (filename && !file_mod_time(filename))
{
DLOG_ERR("cannot access ipset file '%s'\n",filename);
return NULL;
}
return RegisterIpset_(
&params.ipsets,
bExclude ? &dp->ips_collection_exclude : &dp->ips_collection,
filename);
}
static const char *dbg_ipset_fill(const ipset *ips)
{
if (ips->ips4)
if (ips->ips6)
return "ipv4+ipv6";
else
return "ipv4";
else
if (ips->ips6)
return "ipv6";
else
return "empty";
}
void IpsetsDebug()
{
if (!params.debug) return;
struct ipset_file *hfile;
struct desync_profile_list *dpl;
struct ipset_item *ips_item;
LIST_FOREACH(hfile, &params.ipsets, next)
{
if (hfile->filename)
DLOG("ipset file %s (%s)\n",hfile->filename,dbg_ipset_fill(&hfile->ipset));
else
DLOG("ipset fixed (%s)\n",dbg_ipset_fill(&hfile->ipset));
}
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
LIST_FOREACH(ips_item, &dpl->dp.ips_collection, next)
if (ips_item->hfile->filename)
DLOG("profile %u include ipset %s (%s)\n",dpl->dp.n,ips_item->hfile->filename,dbg_ipset_fill(&ips_item->hfile->ipset));
else
DLOG("profile %u include fixed ipset (%s)\n",dpl->dp.n,dbg_ipset_fill(&ips_item->hfile->ipset));
LIST_FOREACH(ips_item, &dpl->dp.ips_collection_exclude, next)
if (ips_item->hfile->filename)
DLOG("profile %u exclude ipset %s (%s)\n",dpl->dp.n,ips_item->hfile->filename,dbg_ipset_fill(&ips_item->hfile->ipset));
else
DLOG("profile %u exclude fixed ipset (%s)\n",dpl->dp.n,dbg_ipset_fill(&ips_item->hfile->ipset));
}
}

14
nfq2/ipset.h Normal file
View File

@@ -0,0 +1,14 @@
#pragma once
#include <stdbool.h>
#include <arpa/inet.h>
#include "params.h"
#include "pools.h"
bool LoadAllIpsets();
bool IpsetCheck(const struct desync_profile *dp, const struct in_addr *ipv4, const struct in6_addr *ipv6);
struct ipset_file *RegisterIpset(struct desync_profile *dp, bool bExclude, const char *filename);
void IpsetsDebug();
bool AppendIpsetItem(ipset *ips, char *ip);
#define ResetAllIpsetModTime() ipset_files_reset_modtime(&params.ipsets)

400
nfq2/kavl.h Normal file
View File

@@ -0,0 +1,400 @@
/* The MIT License
Copyright (c) 2018 by Attractive Chaos <attractor@live.co.uk>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/* An example:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "kavl.h"
struct my_node {
char key;
KAVL_HEAD(struct my_node) head;
};
#define my_cmp(p, q) (((q)->key < (p)->key) - ((p)->key < (q)->key))
KAVL_INIT(my, struct my_node, head, my_cmp)
int main(void) {
const char *str = "MNOLKQOPHIA"; // from wiki, except a duplicate
struct my_node *root = 0;
int i, l = strlen(str);
for (i = 0; i < l; ++i) { // insert in the input order
struct my_node *q, *p = malloc(sizeof(*p));
p->key = str[i];
q = kavl_insert(my, &root, p, 0);
if (p != q) free(p); // if already present, free
}
kavl_itr_t(my) itr;
kavl_itr_first(my, root, &itr); // place at first
do { // traverse
const struct my_node *p = kavl_at(&itr);
putchar(p->key);
free((void*)p); // free node
} while (kavl_itr_next(my, &itr));
putchar('\n');
return 0;
}
*/
#ifndef KAVL_H
#define KAVL_H
#ifdef __STRICT_ANSI__
#define inline __inline__
#endif
#define KAVL_MAX_DEPTH 64
#define kavl_size(head, p) ((p)? (p)->head.size : 0)
#define kavl_size_child(head, q, i) ((q)->head.p[(i)]? (q)->head.p[(i)]->head.size : 0)
#define KAVL_HEAD(__type) \
struct { \
__type *p[2]; \
signed char balance; /* balance factor */ \
unsigned size; /* #elements in subtree */ \
}
#define __KAVL_FIND(suf, __scope, __type, __head, __cmp) \
__scope __type *kavl_find_##suf(const __type *root, const __type *x, unsigned *cnt_) { \
const __type *p = root; \
unsigned cnt = 0; \
while (p != 0) { \
int cmp; \
cmp = __cmp(x, p); \
if (cmp >= 0) cnt += kavl_size_child(__head, p, 0) + 1; \
if (cmp < 0) p = p->__head.p[0]; \
else if (cmp > 0) p = p->__head.p[1]; \
else break; \
} \
if (cnt_) *cnt_ = cnt; \
return (__type*)p; \
}
#define __KAVL_ROTATE(suf, __type, __head) \
/* one rotation: (a,(b,c)q)p => ((a,b)p,c)q */ \
static inline __type *kavl_rotate1_##suf(__type *p, int dir) { /* dir=0 to left; dir=1 to right */ \
int opp = 1 - dir; /* opposite direction */ \
__type *q = p->__head.p[opp]; \
unsigned size_p = p->__head.size; \
p->__head.size -= q->__head.size - kavl_size_child(__head, q, dir); \
q->__head.size = size_p; \
p->__head.p[opp] = q->__head.p[dir]; \
q->__head.p[dir] = p; \
return q; \
} \
/* two consecutive rotations: (a,((b,c)r,d)q)p => ((a,b)p,(c,d)q)r */ \
static inline __type *kavl_rotate2_##suf(__type *p, int dir) { \
int b1, opp = 1 - dir; \
__type *q = p->__head.p[opp], *r = q->__head.p[dir]; \
unsigned size_x_dir = kavl_size_child(__head, r, dir); \
r->__head.size = p->__head.size; \
p->__head.size -= q->__head.size - size_x_dir; \
q->__head.size -= size_x_dir + 1; \
p->__head.p[opp] = r->__head.p[dir]; \
r->__head.p[dir] = p; \
q->__head.p[dir] = r->__head.p[opp]; \
r->__head.p[opp] = q; \
b1 = dir == 0? +1 : -1; \
if (r->__head.balance == b1) q->__head.balance = 0, p->__head.balance = -b1; \
else if (r->__head.balance == 0) q->__head.balance = p->__head.balance = 0; \
else q->__head.balance = b1, p->__head.balance = 0; \
r->__head.balance = 0; \
return r; \
}
#define __KAVL_INSERT(suf, __scope, __type, __head, __cmp) \
__scope __type *kavl_insert_##suf(__type **root_, __type *x, unsigned *cnt_) { \
unsigned char stack[KAVL_MAX_DEPTH]; \
__type *path[KAVL_MAX_DEPTH]; \
__type *bp, *bq; \
__type *p, *q, *r = 0; /* _r_ is potentially the new root */ \
int i, which = 0, top, b1, path_len; \
unsigned cnt = 0; \
bp = *root_, bq = 0; \
/* find the insertion location */ \
for (p = bp, q = bq, top = path_len = 0; p; q = p, p = p->__head.p[which]) { \
int cmp; \
cmp = __cmp(x, p); \
if (cmp >= 0) cnt += kavl_size_child(__head, p, 0) + 1; \
if (cmp == 0) { \
if (cnt_) *cnt_ = cnt; \
return p; \
} \
if (p->__head.balance != 0) \
bq = q, bp = p, top = 0; \
stack[top++] = which = (cmp > 0); \
path[path_len++] = p; \
} \
if (cnt_) *cnt_ = cnt; \
x->__head.balance = 0, x->__head.size = 1, x->__head.p[0] = x->__head.p[1] = 0; \
if (q == 0) *root_ = x; \
else q->__head.p[which] = x; \
if (bp == 0) return x; \
for (i = 0; i < path_len; ++i) ++path[i]->__head.size; \
for (p = bp, top = 0; p != x; p = p->__head.p[stack[top]], ++top) /* update balance factors */ \
if (stack[top] == 0) --p->__head.balance; \
else ++p->__head.balance; \
if (bp->__head.balance > -2 && bp->__head.balance < 2) return x; /* no re-balance needed */ \
/* re-balance */ \
which = (bp->__head.balance < 0); \
b1 = which == 0? +1 : -1; \
q = bp->__head.p[1 - which]; \
if (q->__head.balance == b1) { \
r = kavl_rotate1_##suf(bp, which); \
q->__head.balance = bp->__head.balance = 0; \
} else r = kavl_rotate2_##suf(bp, which); \
if (bq == 0) *root_ = r; \
else bq->__head.p[bp != bq->__head.p[0]] = r; \
return x; \
}
#define __KAVL_ERASE(suf, __scope, __type, __head, __cmp) \
__scope __type *kavl_erase_##suf(__type **root_, const __type *x, unsigned *cnt_) { \
__type *p, *path[KAVL_MAX_DEPTH], fake; \
unsigned char dir[KAVL_MAX_DEPTH]; \
int i, d = 0, cmp; \
unsigned cnt = 0; \
fake.__head.p[0] = *root_, fake.__head.p[1] = 0; \
if (cnt_) *cnt_ = 0; \
if (x) { \
for (cmp = -1, p = &fake; cmp; cmp = __cmp(x, p)) { \
int which = (cmp > 0); \
if (cmp > 0) cnt += kavl_size_child(__head, p, 0) + 1; \
dir[d] = which; \
path[d++] = p; \
p = p->__head.p[which]; \
if (p == 0) { \
if (cnt_) *cnt_ = 0; \
return 0; \
} \
} \
cnt += kavl_size_child(__head, p, 0) + 1; /* because p==x is not counted */ \
} else { \
for (p = &fake, cnt = 1; p; p = p->__head.p[0]) \
dir[d] = 0, path[d++] = p; \
p = path[--d]; \
} \
if (cnt_) *cnt_ = cnt; \
for (i = 1; i < d; ++i) --path[i]->__head.size; \
if (p->__head.p[1] == 0) { /* ((1,.)2,3)4 => (1,3)4; p=2 */ \
path[d-1]->__head.p[dir[d-1]] = p->__head.p[0]; \
} else { \
__type *q = p->__head.p[1]; \
if (q->__head.p[0] == 0) { /* ((1,2)3,4)5 => ((1)2,4)5; p=3 */ \
q->__head.p[0] = p->__head.p[0]; \
q->__head.balance = p->__head.balance; \
path[d-1]->__head.p[dir[d-1]] = q; \
path[d] = q, dir[d++] = 1; \
q->__head.size = p->__head.size - 1; \
} else { /* ((1,((.,2)3,4)5)6,7)8 => ((1,(2,4)5)3,7)8; p=6 */ \
__type *r; \
int e = d++; /* backup _d_ */\
for (;;) { \
dir[d] = 0; \
path[d++] = q; \
r = q->__head.p[0]; \
if (r->__head.p[0] == 0) break; \
q = r; \
} \
r->__head.p[0] = p->__head.p[0]; \
q->__head.p[0] = r->__head.p[1]; \
r->__head.p[1] = p->__head.p[1]; \
r->__head.balance = p->__head.balance; \
path[e-1]->__head.p[dir[e-1]] = r; \
path[e] = r, dir[e] = 1; \
for (i = e + 1; i < d; ++i) --path[i]->__head.size; \
r->__head.size = p->__head.size - 1; \
} \
} \
while (--d > 0) { \
__type *q = path[d]; \
int which, other, b1 = 1, b2 = 2; \
which = dir[d], other = 1 - which; \
if (which) b1 = -b1, b2 = -b2; \
q->__head.balance += b1; \
if (q->__head.balance == b1) break; \
else if (q->__head.balance == b2) { \
__type *r = q->__head.p[other]; \
if (r->__head.balance == -b1) { \
path[d-1]->__head.p[dir[d-1]] = kavl_rotate2_##suf(q, which); \
} else { \
path[d-1]->__head.p[dir[d-1]] = kavl_rotate1_##suf(q, which); \
if (r->__head.balance == 0) { \
r->__head.balance = -b1; \
q->__head.balance = b1; \
break; \
} else r->__head.balance = q->__head.balance = 0; \
} \
} \
} \
*root_ = fake.__head.p[0]; \
return p; \
}
#define kavl_free(__type, __head, __root, __free) do { \
__type *_p, *_q; \
for (_p = __root; _p; _p = _q) { \
if (_p->__head.p[0] == 0) { \
_q = _p->__head.p[1]; \
__free(_p); \
} else { \
_q = _p->__head.p[0]; \
_p->__head.p[0] = _q->__head.p[1]; \
_q->__head.p[1] = _p; \
} \
} \
} while (0)
#define __KAVL_ITR(suf, __scope, __type, __head, __cmp) \
struct kavl_itr_##suf { \
const __type *stack[KAVL_MAX_DEPTH], **top, *right; /* _right_ points to the right child of *top */ \
}; \
__scope void kavl_itr_first_##suf(const __type *root, struct kavl_itr_##suf *itr) { \
const __type *p; \
for (itr->top = itr->stack - 1, p = root; p; p = p->__head.p[0]) \
*++itr->top = p; \
itr->right = (*itr->top)->__head.p[1]; \
} \
__scope int kavl_itr_find_##suf(const __type *root, const __type *x, struct kavl_itr_##suf *itr) { \
const __type *p = root; \
itr->top = itr->stack - 1; \
while (p != 0) { \
int cmp; \
cmp = __cmp(x, p); \
if (cmp < 0) *++itr->top = p, p = p->__head.p[0]; \
else if (cmp > 0) p = p->__head.p[1]; \
else break; \
} \
if (p) { \
*++itr->top = p; \
itr->right = p->__head.p[1]; \
return 1; \
} else if (itr->top >= itr->stack) { \
itr->right = (*itr->top)->__head.p[1]; \
return 0; \
} else return 0; \
} \
__scope int kavl_itr_next_##suf(struct kavl_itr_##suf *itr) { \
for (;;) { \
const __type *p; \
for (p = itr->right, --itr->top; p; p = p->__head.p[0]) \
*++itr->top = p; \
if (itr->top < itr->stack) return 0; \
itr->right = (*itr->top)->__head.p[1]; \
return 1; \
} \
}
/**
* Insert a node to the tree
*
* @param suf name suffix used in KAVL_INIT()
* @param proot pointer to the root of the tree (in/out: root may change)
* @param x node to insert (in)
* @param cnt number of nodes smaller than or equal to _x_; can be NULL (out)
*
* @return _x_ if not present in the tree, or the node equal to x.
*/
#define kavl_insert(suf, proot, x, cnt) kavl_insert_##suf(proot, x, cnt)
/**
* Find a node in the tree
*
* @param suf name suffix used in KAVL_INIT()
* @param root root of the tree
* @param x node value to find (in)
* @param cnt number of nodes smaller than or equal to _x_; can be NULL (out)
*
* @return node equal to _x_ if present, or NULL if absent
*/
#define kavl_find(suf, root, x, cnt) kavl_find_##suf(root, x, cnt)
/**
* Delete a node from the tree
*
* @param suf name suffix used in KAVL_INIT()
* @param proot pointer to the root of the tree (in/out: root may change)
* @param x node value to delete; if NULL, delete the first node (in)
*
* @return node removed from the tree if present, or NULL if absent
*/
#define kavl_erase(suf, proot, x, cnt) kavl_erase_##suf(proot, x, cnt)
#define kavl_erase_first(suf, proot) kavl_erase_##suf(proot, 0, 0)
#define kavl_itr_t(suf) struct kavl_itr_##suf
/**
* Place the iterator at the smallest object
*
* @param suf name suffix used in KAVL_INIT()
* @param root root of the tree
* @param itr iterator
*/
#define kavl_itr_first(suf, root, itr) kavl_itr_first_##suf(root, itr)
/**
* Place the iterator at the object equal to or greater than the query
*
* @param suf name suffix used in KAVL_INIT()
* @param root root of the tree
* @param x query (in)
* @param itr iterator (out)
*
* @return 1 if find; 0 otherwise. kavl_at(itr) is NULL if and only if query is
* larger than all objects in the tree
*/
#define kavl_itr_find(suf, root, x, itr) kavl_itr_find_##suf(root, x, itr)
/**
* Move to the next object in order
*
* @param itr iterator (modified)
*
* @return 1 if there is a next object; 0 otherwise
*/
#define kavl_itr_next(suf, itr) kavl_itr_next_##suf(itr)
/**
* Return the pointer at the iterator
*
* @param itr iterator
*
* @return pointer if present; NULL otherwise
*/
#define kavl_at(itr) ((itr)->top < (itr)->stack? 0 : *(itr)->top)
#define KAVL_INIT2(suf, __scope, __type, __head, __cmp) \
__KAVL_FIND(suf, __scope, __type, __head, __cmp) \
__KAVL_ROTATE(suf, __type, __head) \
__KAVL_INSERT(suf, __scope, __type, __head, __cmp) \
__KAVL_ERASE(suf, __scope, __type, __head, __cmp) \
__KAVL_ITR(suf, __scope, __type, __head, __cmp)
#define KAVL_INIT(suf, __type, __head, __cmp) \
KAVL_INIT2(suf,, __type, __head, __cmp)
#endif

2626
nfq2/lua.c Normal file

File diff suppressed because it is too large Load Diff

82
nfq2/lua.h Normal file
View File

@@ -0,0 +1,82 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#ifdef LUAJIT
#include "luajit.h"
#else
#include <lua.h>
#endif
#include <lualib.h>
#include <lauxlib.h>
#include "pools.h"
#include "conntrack.h"
#include "darkmagic.h"
#if LUA_VERSION_NUM < 503
#define lua_isinteger lua_isnumber
#endif
#ifndef LUA_UNSIGNED
#define LUA_UNSIGNED uint64_t
#endif
// pushing and not popping inside luacall cause memory leak
#define LUA_STACK_GUARD_ENTER(L) int _lsg=lua_gettop(L);
#define LUA_STACK_GUARD_LEAVE(L,N) if ((_lsg+N)!=lua_gettop(L)) luaL_error(L,"stack guard failure");
#define LUA_STACK_GUARD_RETURN(L,N) LUA_STACK_GUARD_LEAVE(L,N); return N;
bool lua_init(void);
void lua_shutdown(void);
void lua_dlog_error(void);
void lua_do_gc(void);
#if LUA_VERSION_NUM < 502
int lua_absindex(lua_State *L, int idx);
#endif
// push - create object and push to the stack
// pushf - create object and set it as a named field of a table already present on the stack
// pushi - create object and set it as a index field of a table already present on the stack
void lua_pushf_nil(const char *field);
void lua_pushi_nil(lua_Integer idx);
void lua_pushf_bool(const char *field, bool b);
void lua_pushi_bool(lua_Integer idx, bool b);
void lua_pushf_str(const char *field, const char *str);
void lua_pushi_str(lua_Integer idx, const char *str);
void lua_pushf_int(const char *field, lua_Integer v);
void lua_pushi_int(lua_Integer idx, lua_Integer v);
void lua_push_raw(const void *v, size_t l);
void lua_pushf_raw(const char *field, const void *v, size_t l);
void lua_pushi_raw(lua_Integer idx, const void *v, size_t l);
void lua_pushf_reg(const char *field, int ref);
void lua_pushf_lud(const char *field, void *p);
void lua_pushf_table(const char *field);
void lua_pushi_table(lua_Integer idx);
void lua_pushf_tcphdr_options(const struct tcphdr *tcp, size_t len);
void lua_pushf_tcphdr(const struct tcphdr *tcp, size_t len);
void lua_pushf_udphdr(const struct udphdr *udp, size_t len);
void lua_pushf_iphdr(const struct ip *ip, size_t len);
void lua_pushf_ip6hdr(const struct ip6_hdr *ip6, size_t len);
void lua_push_dissect(const struct dissect *dis);
void lua_pushf_dissect(const struct dissect *dis);
void lua_pushf_ctrack(const t_ctrack *ctrack);
void lua_pushf_args(const struct ptr_list_head *args);
void lua_pushf_global(const char *field, const char *global);
bool lua_reconstruct_ip6hdr(int idx, struct ip6_hdr *ip6, size_t *len, uint8_t last_proto, bool preserve_next);
bool lua_reconstruct_iphdr(int idx, struct ip *ip, size_t *len);
bool lua_reconstruct_tcphdr(int idx, struct tcphdr *tcp, size_t *len);
bool lua_reconstruct_udphdr(int idx, struct udphdr *udp);
bool lua_reconstruct_dissect(int idx, uint8_t *buf, size_t *len, bool badsum, bool ip6_preserve_next);
typedef struct {
const char *func, *instance;
const struct desync_profile *dp;
const t_ctrack *ctrack;
} t_lua_desync_context;
bool lua_instance_cutoff_check(const t_lua_desync_context *ctx, bool bIn);

2558
nfq2/nfqws.c Normal file

File diff suppressed because it is too large Load Diff

12
nfq2/nfqws.h Normal file
View File

@@ -0,0 +1,12 @@
#pragma once
#include <stdbool.h>
#ifdef __linux__
#define HAS_FILTER_SSID 1
#endif
#ifdef __CYGWIN__
extern bool bQuit;
#endif
int main(int argc, char *argv[]);

71
nfq2/packet_queue.c Normal file
View File

@@ -0,0 +1,71 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "packet_queue.h"
void rawpacket_queue_init(struct rawpacket_tailhead *q)
{
TAILQ_INIT(q);
}
void rawpacket_free(struct rawpacket *rp)
{
if (rp) free(rp->packet);
free(rp);
}
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q)
{
struct rawpacket *rp;
rp = TAILQ_FIRST(q);
if (rp) TAILQ_REMOVE(q, rp, next);
return rp;
}
void rawpacket_queue_destroy(struct rawpacket_tailhead *q)
{
struct rawpacket *rp;
while((rp = rawpacket_dequeue(q))) rawpacket_free(rp);
}
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload)
{
struct rawpacket *rp = malloc(sizeof(struct rawpacket));
if (!rp) return NULL;
rp->packet = malloc(len);
if (!rp->packet)
{
free(rp);
return NULL;
}
rp->dst = *dst;
rp->fwmark_orig = fwmark_orig;
rp->fwmark = fwmark;
if (ifin)
snprintf(rp->ifin,sizeof(rp->ifin),"%s",ifin);
else
*rp->ifin = 0;
if (ifout)
snprintf(rp->ifout,sizeof(rp->ifout),"%s",ifout);
else
*rp->ifout = 0;
memcpy(rp->packet,data,len);
rp->len=len;
rp->len_payload=len_payload;
TAILQ_INSERT_TAIL(q, rp, next);
return rp;
}
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q)
{
const struct rawpacket *rp;
unsigned int ct=0;
TAILQ_FOREACH(rp, q, next) ct++;
return ct;
}
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q)
{
return !TAILQ_FIRST(q);
}

27
nfq2/packet_queue.h Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <inttypes.h>
#include <stdbool.h>
#include <sys/queue.h>
#include <net/if.h>
#include <sys/socket.h>
struct rawpacket
{
struct sockaddr_storage dst;
char ifin[IFNAMSIZ], ifout[IFNAMSIZ];
uint32_t fwmark_orig;
uint32_t fwmark;
size_t len, len_payload;
uint8_t *packet;
TAILQ_ENTRY(rawpacket) next;
};
TAILQ_HEAD(rawpacket_tailhead, rawpacket);
void rawpacket_queue_init(struct rawpacket_tailhead *q);
void rawpacket_queue_destroy(struct rawpacket_tailhead *q);
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q);
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload);
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q);
void rawpacket_free(struct rawpacket *rp);

399
nfq2/params.c Normal file
View File

@@ -0,0 +1,399 @@
#include "params.h"
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#ifdef __ANDROID__
#include <android/log.h>
#endif
#include "pools.h"
#include "lua.h"
#ifdef BSD
const char *progname = "dvtws2";
#elif defined(__CYGWIN__)
const char *progname = "winws2";
#elif defined(__linux__)
const char *progname = "nfqws2";
#else
#error UNKNOWN_SYSTEM_TIME
#endif
const char *fake_http_request_default = "GET / HTTP/1.1\r\nHost: www.iana.org\r\n"
"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0\r\n"
"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\r\n"
"Accept-Encoding: gzip, deflate, br\r\n\r\n";
// SNI - www.microsoft.com
const uint8_t fake_tls_clienthello_default[680] = {
0x16, 0x03, 0x01, 0x02, 0xa3, 0x01, 0x00, 0x02, 0x9f, 0x03, 0x03, 0x41,
0x88, 0x82, 0x2d, 0x4f, 0xfd, 0x81, 0x48, 0x9e, 0xe7, 0x90, 0x65, 0x1f,
0xba, 0x05, 0x7b, 0xff, 0xa7, 0x5a, 0xf9, 0x5b, 0x8a, 0x8f, 0x45, 0x8b,
0x41, 0xf0, 0x3d, 0x1b, 0xdd, 0xe3, 0xf8, 0x20, 0x9b, 0x23, 0xa5, 0xd2,
0x21, 0x1e, 0x9f, 0xe7, 0x85, 0x6c, 0xfc, 0x61, 0x80, 0x3a, 0x3f, 0xba,
0xb9, 0x60, 0xba, 0xb3, 0x0e, 0x98, 0x27, 0x6c, 0xf7, 0x38, 0x28, 0x65,
0x80, 0x5d, 0x40, 0x38, 0x00, 0x22, 0x13, 0x01, 0x13, 0x03, 0x13, 0x02,
0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x2c, 0xc0, 0x30,
0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,
0x00, 0x2f, 0x00, 0x35, 0x01, 0x00, 0x02, 0x34, 0x00, 0x00, 0x00, 0x16,
0x00, 0x14, 0x00, 0x00, 0x11, 0x77, 0x77, 0x77, 0x2e, 0x6d, 0x69, 0x63,
0x72, 0x6f, 0x73, 0x6f, 0x66, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x17,
0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x0e, 0x00,
0x0c, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x01, 0x00, 0x01,
0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74,
0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x22, 0x00, 0x0a, 0x00, 0x08, 0x04, 0x03, 0x05, 0x03,
0x06, 0x03, 0x02, 0x03, 0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00, 0x6b,
0x00, 0x69, 0x00, 0x1d, 0x00, 0x20, 0x69, 0x15, 0x16, 0x29, 0x6d, 0xad,
0xd5, 0x68, 0x88, 0x27, 0x2f, 0xde, 0xaf, 0xac, 0x3c, 0x4c, 0xa4, 0xe4,
0xd8, 0xc8, 0xfb, 0x41, 0x87, 0xf4, 0x76, 0x4e, 0x0e, 0xfa, 0x64, 0xc4,
0xe9, 0x29, 0x00, 0x17, 0x00, 0x41, 0x04, 0xfe, 0x62, 0xb9, 0x08, 0xc8,
0xc3, 0x2a, 0xb9, 0x87, 0x37, 0x84, 0x42, 0x6b, 0x5c, 0xcd, 0xc9, 0xca,
0x62, 0x38, 0xd3, 0xd9, 0x99, 0x8a, 0xc4, 0x2d, 0xc6, 0xd0, 0xa3, 0x60,
0xb2, 0x12, 0x54, 0x41, 0x8e, 0x52, 0x5e, 0xe3, 0xab, 0xf9, 0xc2, 0x07,
0x81, 0xdc, 0xf8, 0xf2, 0x6a, 0x91, 0x40, 0x2f, 0xcb, 0xa4, 0xff, 0x6f,
0x24, 0xc7, 0x4d, 0x77, 0x77, 0x2d, 0x6f, 0xe0, 0x77, 0xaa, 0x92, 0x00,
0x2b, 0x00, 0x05, 0x04, 0x03, 0x04, 0x03, 0x03, 0x00, 0x0d, 0x00, 0x18,
0x00, 0x16, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x04, 0x08, 0x05,
0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01,
0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01,
0x00, 0x1b, 0x00, 0x07, 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0xfe,
0x0d, 0x01, 0x19, 0x00, 0x00, 0x01, 0x00, 0x03, 0x21, 0x00, 0x20, 0x62,
0xe8, 0x83, 0xd8, 0x97, 0x05, 0x8a, 0xbe, 0xa1, 0xf2, 0x63, 0x4e, 0xce,
0x93, 0x84, 0x8e, 0xcf, 0xe7, 0xdd, 0xb2, 0xe4, 0x87, 0x06, 0xac, 0x11,
0x19, 0xbe, 0x0e, 0x71, 0x87, 0xf1, 0xa6, 0x00, 0xef, 0xd8, 0x6b, 0x27,
0x5e, 0xc0, 0xa7, 0x5d, 0x42, 0x4e, 0x8c, 0xdc, 0xf3, 0x9f, 0x1c, 0x51,
0x62, 0xef, 0xff, 0x5b, 0xed, 0xc8, 0xfd, 0xee, 0x6f, 0xbb, 0x88, 0x9b,
0xb1, 0x30, 0x9c, 0x66, 0x42, 0xab, 0x0f, 0x66, 0x89, 0x18, 0x8b, 0x11,
0xc1, 0x6d, 0xe7, 0x2a, 0xeb, 0x96, 0x3b, 0x7f, 0x52, 0x78, 0xdb, 0xf8,
0x6d, 0x04, 0xf7, 0x95, 0x1a, 0xa8, 0xf0, 0x64, 0x52, 0x07, 0x39, 0xf0,
0xa8, 0x1d, 0x0d, 0x16, 0x36, 0xb7, 0x18, 0x0e, 0xc8, 0x44, 0x27, 0xfe,
0xf3, 0x31, 0xf0, 0xde, 0x8c, 0x74, 0xf5, 0xa1, 0xd8, 0x8f, 0x6f, 0x45,
0x97, 0x69, 0x79, 0x5e, 0x2e, 0xd4, 0xb0, 0x2c, 0x0c, 0x1a, 0x6f, 0xcc,
0xce, 0x90, 0xc7, 0xdd, 0xc6, 0x60, 0x95, 0xf3, 0xc2, 0x19, 0xde, 0x50,
0x80, 0xbf, 0xde, 0xf2, 0x25, 0x63, 0x15, 0x26, 0x63, 0x09, 0x1f, 0xc5,
0xdf, 0x32, 0xf5, 0xea, 0x9c, 0xd2, 0xff, 0x99, 0x4e, 0x67, 0xa2, 0xe5,
0x1a, 0x94, 0x85, 0xe3, 0xdf, 0x36, 0xa5, 0x83, 0x4b, 0x0a, 0x1c, 0xaf,
0xd7, 0x48, 0xc9, 0x4b, 0x8a, 0x27, 0xdd, 0x58, 0x7f, 0x95, 0xf2, 0x6b,
0xde, 0x2b, 0x12, 0xd3, 0xec, 0x4d, 0x69, 0x37, 0x9c, 0x13, 0x9b, 0x16,
0xb0, 0x45, 0x52, 0x38, 0x77, 0x69, 0xef, 0xaa, 0x65, 0x19, 0xbc, 0xc2,
0x93, 0x4d, 0xb0, 0x1b, 0x7f, 0x5b, 0x41, 0xff, 0xaf, 0xba, 0x50, 0x51,
0xc3, 0xf1, 0x27, 0x09, 0x25, 0xf5, 0x60, 0x90, 0x09, 0xb1, 0xe5, 0xc0,
0xc7, 0x42, 0x78, 0x54, 0x3b, 0x23, 0x19, 0x7d, 0x8e, 0x72, 0x13, 0xb4,
0xd3, 0xcd, 0x63, 0xb6, 0xc4, 0x4a, 0x28, 0x3d, 0x45, 0x3e, 0x8b, 0xdb,
0x84, 0x4f, 0x78, 0x64, 0x30, 0x69, 0xe2, 0x1b
};
const char * tld[6] = { "com","org","net","edu","gov","biz" };
int DLOG_FILE(FILE *F, const char *format, va_list args)
{
return vfprintf(F, format, args);
}
int DLOG_CON(const char *format, int syslog_priority, va_list args)
{
return DLOG_FILE(syslog_priority==LOG_ERR ? stderr : stdout, format, args);
}
int DLOG_FILENAME(const char *filename, const char *format, va_list args)
{
int r;
FILE *F = fopen(filename,"at");
if (F)
{
r = DLOG_FILE(F, format, args);
fclose(F);
}
else
r=-1;
return r;
}
typedef void (*f_log_function)(int priority, const char *line);
static char log_buf[1024];
static size_t log_buf_sz=0;
static void syslog_log_function(int priority, const char *line)
{
syslog(priority,"%s",log_buf);
}
#ifdef __ANDROID__
static enum android_LogPriority syslog_priority_to_android(int priority)
{
enum android_LogPriority ap;
switch(priority)
{
case LOG_INFO:
case LOG_NOTICE: ap=ANDROID_LOG_INFO; break;
case LOG_ERR: ap=ANDROID_LOG_ERROR; break;
case LOG_WARNING: ap=ANDROID_LOG_WARN; break;
case LOG_EMERG:
case LOG_ALERT:
case LOG_CRIT: ap=ANDROID_LOG_FATAL; break;
case LOG_DEBUG: ap=ANDROID_LOG_DEBUG; break;
default: ap=ANDROID_LOG_UNKNOWN;
}
return ap;
}
static void android_log_function(int priority, const char *line)
{
__android_log_print(syslog_priority_to_android(priority), progname, "%s", line);
}
#endif
static void log_buffered(f_log_function log_function, int syslog_priority, const char *format, va_list args)
{
if (vsnprintf(log_buf+log_buf_sz,sizeof(log_buf)-log_buf_sz,format,args)>0)
{
log_buf_sz=strlen(log_buf);
// log when buffer is full or buffer ends with \n
if (log_buf_sz>=(sizeof(log_buf)-1) || (log_buf_sz && log_buf[log_buf_sz-1]=='\n'))
{
log_function(syslog_priority,log_buf);
log_buf_sz = 0;
}
}
}
static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list args)
{
int r=0;
va_list args2;
if (condup && !(params.debug && params.debug_target==LOG_TARGET_CONSOLE))
{
va_copy(args2,args);
DLOG_CON(format,syslog_priority,args2);
va_end(args2);
}
if (params.debug)
{
switch(params.debug_target)
{
case LOG_TARGET_CONSOLE:
r = DLOG_CON(format,syslog_priority,args);
break;
case LOG_TARGET_FILE:
r = DLOG_FILENAME(params.debug_logfile,format,args);
break;
case LOG_TARGET_SYSLOG:
// skip newlines
log_buffered(syslog_log_function,syslog_priority,format,args);
r = 1;
break;
#ifdef __ANDROID__
case LOG_TARGET_ANDROID:
// skip newlines
log_buffered(android_log_function,syslog_priority,format,args);
r = 1;
break;
#endif
default:
break;
}
}
return r;
}
int DLOG(const char *format, ...)
{
int r;
va_list args;
va_start(args, format);
r = DLOG_VA(format, LOG_DEBUG, false, args);
va_end(args);
return r;
}
int DLOG_CONDUP(const char *format, ...)
{
int r;
va_list args;
va_start(args, format);
r = DLOG_VA(format, LOG_DEBUG, true, args);
va_end(args);
return r;
}
int DLOG_ERR(const char *format, ...)
{
int r;
va_list args;
va_start(args, format);
r = DLOG_VA(format, LOG_ERR, true, args);
va_end(args);
return r;
}
int DLOG_PERROR(const char *s)
{
return DLOG_ERR("%s: %s\n", s, strerror(errno));
}
int LOG_APPEND(const char *filename, const char *format, va_list args)
{
int r;
FILE *F = fopen(filename,"at");
if (F)
{
fprint_localtime(F);
fprintf(F, " : ");
r = vfprintf(F, format, args);
fprintf(F, "\n");
fclose(F);
}
else
r=-1;
return r;
}
int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...)
{
if (*params.hostlist_auto_debuglog)
{
int r;
va_list args;
va_start(args, format);
r = LOG_APPEND(params.hostlist_auto_debuglog, format, args);
va_end(args);
return r;
}
else
return 0;
}
void hexdump_limited_dlog(const uint8_t *data, size_t size, size_t limit)
{
size_t k;
bool bcut = false;
if (size > limit)
{
size = limit;
bcut = true;
}
if (!size) return;
for (k = 0; k < size; k++) DLOG("%02X ", data[k]);
DLOG(bcut ? "... : " : ": ");
for (k = 0; k < size; k++) DLOG("%c", data[k] >= 0x20 && data[k] <= 0x7F ? (char)data[k] : '.');
if (bcut) DLOG(" ...");
}
void dp_init(struct desync_profile *dp)
{
LIST_INIT(&dp->hl_collection);
LIST_INIT(&dp->hl_collection_exclude);
LIST_INIT(&dp->ips_collection);
LIST_INIT(&dp->ips_collection_exclude);
LIST_INIT(&dp->pf_tcp);
LIST_INIT(&dp->pf_udp);
LIST_INIT(&dp->lua_desync);
dp->hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT;
dp->hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT;
dp->hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT;
dp->filter_ipv4 = dp->filter_ipv6 = true;
}
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
{
struct desync_profile_list *entry = calloc(1,sizeof(struct desync_profile_list));
if (!entry) return NULL;
dp_init(&entry->dp);
// add to the tail
struct desync_profile_list *dpn,*dpl=LIST_FIRST(&params.desync_profiles);
if (dpl)
{
while ((dpn=LIST_NEXT(dpl,next))) dpl = dpn;
LIST_INSERT_AFTER(dpl, entry, next);
}
else
LIST_INSERT_HEAD(&params.desync_profiles, entry, next);
return entry;
}
static void dp_clear_dynamic(struct desync_profile *dp)
{
hostlist_collection_destroy(&dp->hl_collection);
hostlist_collection_destroy(&dp->hl_collection_exclude);
ipset_collection_destroy(&dp->ips_collection);
ipset_collection_destroy(&dp->ips_collection_exclude);
port_filters_destroy(&dp->pf_tcp);
port_filters_destroy(&dp->pf_udp);
funclist_destroy(&dp->lua_desync);
#ifdef HAS_FILTER_SSID
strlist_destroy(&dp->filter_ssid);
#endif
HostFailPoolDestroy(&dp->hostlist_auto_fail_counters);
}
void dp_clear(struct desync_profile *dp)
{
dp_clear_dynamic(dp);
memset(dp,0,sizeof(*dp));
}
void dp_entry_destroy(struct desync_profile_list *entry)
{
dp_clear_dynamic(&entry->dp);
free(entry);
}
void dp_list_destroy(struct desync_profile_list_head *head)
{
struct desync_profile_list *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
dp_entry_destroy(entry);
}
}
bool dp_list_have_autohostlist(struct desync_profile_list_head *head)
{
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, head, next)
if (dpl->dp.hostlist_auto)
return true;
return false;
}
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
void cleanup_args(struct params_s *params)
{
wordfree(&params->wexp);
}
#endif
#ifdef __CYGWIN__
void cleanup_windivert_portfilters(struct params_s *params)
{
char **wdbufs[] =
{&params->wf_pf_tcp_src_in, &params->wf_pf_tcp_dst_in, &params->wf_pf_udp_src_in, &params->wf_pf_udp_dst_in,
&params->wf_pf_tcp_src_out, &params->wf_pf_tcp_dst_out, &params->wf_pf_udp_src_out, &params->wf_pf_udp_dst_out};
for (int i=0 ; i<(sizeof(wdbufs)/sizeof(*wdbufs)) ; i++)
{
free(*wdbufs[i]); *wdbufs[i] = NULL;
}
}
#endif
void cleanup_params(struct params_s *params)
{
lua_shutdown();
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
cleanup_args(params);
#endif
ConntrackPoolDestroy(&params->conntrack);
dp_list_destroy(&params->desync_profiles);
hostlist_files_destroy(&params->hostlists);
ipset_files_destroy(&params->ipsets);
ipcacheDestroy(&params->ipcache);
#ifdef __CYGWIN__
strlist_destroy(&params->ssid_filter);
strlist_destroy(&params->nlm_filter);
strlist_destroy(&params->wf_raw_part);
cleanup_windivert_portfilters(params);
free(params->windivert_filter); params->windivert_filter=NULL;
#else
free(params->user); params->user=NULL;
#endif
}

Some files were not shown because too many files have changed in this diff Show More