61 Commits

Author SHA1 Message Date
bol-van
9776d5d8a9 nfqws: old gcc 4.x compat 2025-06-13 11:44:11 +03:00
bol-van
f88c9a662d nfqws: allow linux build without ssid 2025-06-13 11:31:50 +03:00
bol-van
97bcb9740d update docs 2025-06-13 11:12:37 +03:00
bol-van
b6e9fa3434 nfqws: check wext ssid len not zero 2025-06-12 13:22:54 +03:00
bol-van
32cfee705a nfqws: optimize wext 2025-06-12 11:58:20 +03:00
bol-van
1303bf0fef update changes.txt 2025-06-12 11:46:34 +03:00
bol-van
2417d4ee76 nfqws: use wireless ext in case nl80211 does not return ssid 2025-06-12 11:45:14 +03:00
bol-van
64280cbf3d doc works 2025-06-12 09:08:21 +03:00
bol-van
54dd06056e doc works 2025-06-12 09:06:06 +03:00
bol-van
0be4cbf8a4 rename windows bin dirs 2025-06-11 21:28:18 +03:00
bol-van
acf888c493 rename android bin dir 2025-06-11 21:26:32 +03:00
bol-van
f3d90d4e00 rename freebsd bin dir 2025-06-11 21:25:14 +03:00
bol-van
ceec80a8ed openwrt makefile -lmnl 2025-06-11 20:52:12 +03:00
bol-van
e876096072 rename bin dirs 2025-06-11 20:49:46 +03:00
bol-van
796e4a40b6 rename bin dirs 2025-06-11 20:48:51 +03:00
bol-van
5b7c4a4318 rename bin dirs 2025-06-11 20:46:53 +03:00
bol-van
f09d918b40 nfqws,tpws: use initgroups() if --user specified 2025-06-11 20:15:43 +03:00
bol-van
f3d48b7160 nfqws: debug wlan info init 2025-06-04 10:05:42 +03:00
bol-van
58d306b552 nfqws: --filter-ssid (linux only) 2025-06-04 09:26:38 +03:00
bol-van
30a947b42b install_bin.sh : minor bug 2025-06-01 19:16:37 +03:00
bol-van
9fe26c92a5 install_bin: remove quarantine xattr on macos 2025-06-01 15:30:51 +03:00
bol-van
ab0d16f4e1 install_easy: fix broken compile on mac 2025-06-01 09:11:33 +03:00
bol-van
c37c39996d static 2025-05-31 13:42:36 +03:00
bol-van
87a0cf0797 nfqws,tpws: --debug=android for NDK builds 2025-05-31 10:34:03 +03:00
bol-van
893a133f86 install_easy: rephrase 2025-05-30 10:54:20 +03:00
bol-van
fa9e3c09f1 install_easy: ask MODE_FILTER before daemon test 2025-05-29 19:14:35 +03:00
bol-van
2edf6bee34 install_easy: minor fix 2025-05-29 19:05:50 +03:00
bol-van
468d293b46 install_easy: run daemon test under proper user 2025-05-29 19:02:50 +03:00
bol-van
fd698d801b nfqws,tpws: check list files accessibility with dropped privs in --dry-run mode 2025-05-29 15:43:27 +03:00
bol-van
38a3833351 install_bin: do exec test after elf test 2025-05-28 23:06:12 +03:00
bol-van
6c9ff2010b install_bin: fix TEST=run 2025-05-28 22:46:55 +03:00
bol-van
06f57e27ed install_bin: fix minor problems 2025-05-28 20:40:26 +03:00
bol-van
06921bdbee install_bin: read elf detection method 2025-05-28 20:27:34 +03:00
bol-van
3999e0b350 install_bin: read elf detection method 2025-05-28 20:22:09 +03:00
bol-van
1301aca25b Upx 5.0.1 still has bugs on mips
This reverts commit f0a8a35c12.
2025-05-28 19:37:57 +03:00
bol-van
bf4244809f install_easy: stop if running embedded release on traditional linux 2025-05-28 14:37:32 +03:00
bol-van
99b5406905 redsocks: shell /bin/false 2025-05-27 22:24:58 +03:00
bol-van
ebd01868ee redsocks: no bash for su 2025-05-27 22:20:46 +03:00
bol-van
c451addd86 redsocks: shadow-su 2025-05-27 21:53:44 +03:00
bol-van
4c8ea084d7 redsocks.txt: remove obsolete 2025-05-27 21:46:46 +03:00
bol-van
02badb3c4c nfqws,tpws: move --ipset to kavl 2025-05-27 16:03:28 +03:00
bol-van
323307cbfa Merge pull request #1464 from Dronatar/patch-1
Omission fix in readme.md
2025-05-26 09:38:57 +03:00
Dronatar
00560719bd Omission fix in readme.md 2025-05-26 06:15:29 +02:00
bol-van
56c96df47b remove seccomp from systemd units 2025-05-25 20:07:52 +03:00
bol-van
f0a8a35c12 They fixed problems
This reverts commit 58e73d0331.
2025-05-25 09:49:01 +03:00
bol-van
d566f48f94 blockcheck: display ip version every test 2025-05-24 13:35:11 +03:00
bol-van
3513cbb65e winws: fix trash flood warning message 2025-05-24 10:51:01 +03:00
bol-van
20f91cb7ab doc works 2025-05-24 09:58:41 +03:00
bol-van
4becc07572 update changes 2025-05-22 11:01:34 +03:00
bol-van
a39c18737b nfqws: - disables autottl 2025-05-22 11:00:11 +03:00
bol-van
ed7b743fe2 update changes.txt 2025-05-22 10:44:26 +03:00
bol-van
d3b0b3e0b1 nfqws,tpws: display android in version string 2025-05-22 10:19:30 +03:00
bol-van
ba040769a7 doc works 2025-05-21 17:40:46 +03:00
bol-van
0ced50e393 doc works 2025-05-21 17:39:50 +03:00
bol-van
f3abd6815a doc works 2025-05-20 19:07:42 +03:00
bol-van
4572799750 doc works 2025-05-20 19:06:48 +03:00
bol-van
696167509a doc works 2025-05-20 19:03:47 +03:00
bol-van
2374df6d74 doc works 2025-05-20 19:02:22 +03:00
bol-van
ab06d6b640 doc works 2025-05-20 19:01:14 +03:00
bol-van
60efab1cc6 doc works 2025-05-20 18:57:46 +03:00
bol-van
71aebbb4d3 nfqws: allow zero autottl 2025-05-20 18:06:56 +03:00
41 changed files with 2283 additions and 530 deletions

View File

@@ -427,7 +427,6 @@ jobs:
unzip $f -d $dir && rm $f
if [[ $dir =~ win ]]; then
chmod -x $dir/*
run_upx --force $dir/cygwin1.dll
fi
;;
esac
@@ -438,23 +437,23 @@ jobs:
if [ -d $dir ]; then
echo "Processing $dir"
case $dir in
*-android-arm64-v8a ) run_dir android-aarch64 ;;
*-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-x64 ;;
*-linux-arm ) run_dir arm ;;
*-linux-arm64 ) run_dir aarch64 ;;
*-linux-mips64 ) run_dir mips64r2-msb ;;
*-linux-mipselsf ) run_dir mips32r1-lsb ;;
*-linux-mipssf ) run_dir mips32r1-msb ;;
*-linux-ppc ) run_dir ppc ;;
*-linux-x86 ) run_dir x86 ;;
*-linux-x86_64 ) run_dir x86_64 ;;
*-linux-lexra ) run_dir lexra ;;
*-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 ;;
*-mac-x64 ) run_dir mac64 ;;
*-win-x86 ) run_dir win32 ;;
*-win-x86_64 ) run_dir win64 ;;
*-win-x86 ) run_dir windows-x86 ;;
*-win-x86_64 ) run_dir windows-x86_64 ;;
esac
fi
done

View File

@@ -1062,7 +1062,7 @@ tpws_curl_test()
# $1 - test function
# $2 - domain
# $3,$4,$5, ... - tpws params
echo - $1 $2 : tpws $3 $4 $5 $6 $7 $8 $9${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}
echo - $1 ipv$IPV $2 : tpws $3 $4 $5 $6 $7 $8 $9${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}
local ALL_PROXY="socks5://127.0.0.1:$SOCKS_PORT"
ws_curl_test tpws_start "$@"${TPWS_EXTRA:+ $TPWS_EXTRA}${TPWS_EXTRA_1:+ "$TPWS_EXTRA_1"}${TPWS_EXTRA_2:+ "$TPWS_EXTRA_2"}${TPWS_EXTRA_3:+ "$TPWS_EXTRA_3"}${TPWS_EXTRA_4:+ "$TPWS_EXTRA_4"}${TPWS_EXTRA_5:+ "$TPWS_EXTRA_5"}${TPWS_EXTRA_6:+ "$TPWS_EXTRA_6"}${TPWS_EXTRA_7:+ "$TPWS_EXTRA_7"}${TPWS_EXTRA_8:+ "$TPWS_EXTRA_8"}${TPWS_EXTRA_9:+ "$TPWS_EXTRA_9"}
local testf=$1 dom=$2 strategy code=$?
@@ -1082,7 +1082,7 @@ pktws_curl_test()
local testf=$1 dom=$2 strategy code
shift; shift;
echo - $testf $dom : $PKTWSD ${WF:+$WF }${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }$@${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}
echo - $testf ipv$IPV $dom : $PKTWSD ${WF:+$WF }${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }$@${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}
ws_curl_test pktws_start $testf $dom ${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }"$@"${PKTWS_EXTRA:+ $PKTWS_EXTRA}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_1"}${PKTWS_EXTRA_2:+ "$PKTWS_EXTRA_2"}${PKTWS_EXTRA_3:+ "$PKTWS_EXTRA_3"}${PKTWS_EXTRA_4:+ "$PKTWS_EXTRA_4"}${PKTWS_EXTRA_5:+ "$PKTWS_EXTRA_5"}${PKTWS_EXTRA_6:+ "$PKTWS_EXTRA_6"}${PKTWS_EXTRA_7:+ "$PKTWS_EXTRA_7"}${PKTWS_EXTRA_8:+ "$PKTWS_EXTRA_8"}${PKTWS_EXTRA_9:+ "$PKTWS_EXTRA_9"}
code=$?

View File

@@ -187,7 +187,7 @@ check_system()
SYSTEM=
SUBSYS=
SYSTEMCTL=$(whichq systemctl)
SYSTEMCTL="$(whichq systemctl)"
get_fwtype
OPENWRT_FW3=
@@ -203,6 +203,7 @@ check_system()
# some distros include systemctl without systemd
if [ -d "$SYSTEMD_DIR" ] && [ -x "$SYSTEMCTL" ] && [ "$INIT" = "systemd" ]; then
SYSTEM=systemd
[ -f "$EXEDIR/init.d/sysv/functions" ] && . "$EXEDIR/init.d/sysv/functions"
elif [ -f "/etc/openwrt_release" ] && exists opkg || exists apk && exists uci && [ "$INIT" = "procd" ] ; then
SYSTEM=openwrt
OPENWRT_PACKAGER=opkg
@@ -226,8 +227,10 @@ check_system()
OPENWRT_FW4=1
info="${info}firewall fw4. flow offloading requires nftables."
fi
[ -f "$EXEDIR/init.d/openwrt/functions" ] && . "$EXEDIR/init.d/openwrt/functions"
elif openrc_test; then
SYSTEM=openrc
[ -f "$EXEDIR/init.d/sysv/functions" ] && . "$EXEDIR/init.d/sysv/functions"
else
echo system is not either systemd, openrc or openwrt based
echo easy installer can set up config settings but can\'t configure auto start
@@ -237,10 +240,12 @@ check_system()
else
exitp 5
fi
[ -f "$EXEDIR/init.d/sysv/functions" ] && . "$EXEDIR/init.d/sysv/functions"
fi
linux_get_subsys
elif [ "$UNAME" = "Darwin" ]; then
SYSTEM=macos
[ -f "$EXEDIR/init.d/macos/functions" ] && . "$EXEDIR/init.d/macos/functions"
else
echo easy installer only supports Linux and MacOS. check readme.md for supported systems and manual setup info.
exitp 5
@@ -834,13 +839,13 @@ dry_run_tpws_()
{
local TPWS="$ZAPRET_BASE/tpws/tpws"
echo verifying tpws options
"$TPWS" --dry-run "$@"
"$TPWS" --dry-run ${WS_USER:+--user=$WS_USER} "$@"
}
dry_run_nfqws_()
{
local NFQWS="$ZAPRET_BASE/nfq/nfqws"
echo verifying nfqws options
"$NFQWS" --dry-run "$@"
"$NFQWS" --dry-run ${WS_USER:+--user=$WS_USER} "$@"
}
dry_run_tpws()
{

View File

@@ -491,12 +491,29 @@ nfqws: --orig*
nfqws: ipcache of hop count and host names
nfqws: --ctrack-disable
nfqws: --synack-split
nfqws: --autottl=- or --autottl=0:0-0 disable autottl. previous "0" does not work anymore.
tpws: ipcache of host names
nfqws,tpws: set 1024 repeat limit to fakes and dups
nfqws,tpws: do more before daemonize
nfqws,tpws: accept multiple gids in --gid
nfqws,tpws: display "android" in version string if built for android
init.d: remove --ipset parameter prohibition
init.d, blockcheck: drop time exceeded icmp for nfqws-related connections
blockcheck: some dup and orig-ttl mods
blockcheck: PKTWS_EXTRA_PRE
blockcheck: report test function and domain every test
v71.1
nfqws,tpws: much faster ipset implementation. move from hash to avl tree
nfqws,tpws: check list files accessibility with dropped privs in --dry-run mode
nfqws,tpws: --debug=android for NDK builds
nfqws,tpws: use initgroups instead of setgroups if --user specified
nfqws: --filter-ssid (linux-only)
install_easy: stop if running embedded release on traditional linux system (some files missing)
install_bin: add "read elf" arch detection method
binaries: renamed arch dirs in binaries
v71.2
nfqws: use wireless ext in case nl80211 does not return SSID

View File

@@ -1,6 +1,6 @@
debian,ubuntu :
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev libsystemd-dev
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev libmnl-dev libsystemd-dev
make -C /opt/zapret systemd
FreeBSD :

View File

@@ -12,7 +12,7 @@ define Package/nfqws
CATEGORY:=Network
TITLE:=nfqws
SUBMENU:=Zapret
DEPENDS:=+libnetfilter-queue +libcap +zlib
DEPENDS:=+libnetfilter-queue +lmnl +libcap +zlib
endef
define Build/Prepare

View File

@@ -1,9 +1,10 @@
# zapret v70.7
# zapret v71.1.1
# SCAMMER WARNING
This software is free and open source under [MIT license](./LICENSE.txt).
If anyone demands you to download this software only from their webpage, telegram channel, forces you to delete links, videos, makes copyright claims, you are dealing with scammers.
However, [donations](#donations) are welcome.
# Multilanguage/Мультиязычный README
___
@@ -23,17 +24,23 @@ ___
- [TCP segmentation](#tcp-segmentation)
- [Sequence numbers overlap](#sequence-numbers-overlap)
- [ipv6 specific modes](#ipv6-specific-modes)
- [Original modding](#original-modding)
- [Duplicates](#duplicates)
- [Server reply reaction](#server-reply-reaction)
- [SYNDATA mode](#syndata-mode)
- [DPI desync combos](#dpi-desync-combos)
- [IP cache](#ip-cache)
- [CONNTRACK](#conntrack)
- [Reassemble](#reassemble)
- [UDP support](#udp-support)
- [IP fragmentation](#ip-fragmentation)
- [Multiple strategies](#multiple-strategies)
- [WIFI filtering](#wifi-filtering)
- [Virtual machines](#virtual-machines)
- [IPTABLES for nfqws](#iptables-for-nfqws)
- [NFTABLES for nfqws](#nftables-for-nfqws)
- [Flow offloading](#flow-offloading)
- [Server side fooling](#server-side-fooling)
- [tpws](#tpws)
- [TCP segmentation in tpws](#tcp-segmentation-in-tpws)
- [TLSREC](#tlsrec)
@@ -139,24 +146,45 @@ nfqws takes the following parameters:
--daemon ; daemonize
--pidfile=<filename> ; write pid to file
--user=<username> ; drop root privs
--uid=uid[:gid] ; drop root privs
--uid=uid[:gid1,gid2,...] ; drop root privs
--bind-fix4 ; apply outgoing interface selection fix for generated ipv4 packets
--bind-fix6 ; apply outgoing interface selection fix for generated ipv6 packets
--wsize=<window_size>[:<scale_factor>] ; set window size. 0 = do not modify. OBSOLETE !
--wssize=<window_size>[:<scale_factor>] ; set window size for server. 0 = do not modify. default scale_factor = 0.
--wssize-cutoff=[n|d|s]N ; apply server wsize only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--ctrack-timeouts=S:E:F[:U] ; internal conntrack timeouts for TCP SYN, ESTABLISHED, FIN stages, UDP timeout. default 60:300:60:60
--ctrack-disable=[0|1] ; 1 or no argument disables conntrack
--ipcache-lifetime=<int> ; time in seconds to keep cached hop count and domain name (default 7200). 0 = no expiration
--ipcache-hostname=[0|1] ; 1 or no argument enables ip->hostname caching
--hostcase ; change Host: => host:
--hostspell ; exact spelling of "Host" header. must be 4 chars. default is "host"
--hostnospace ; remove space after Host: and add it to User-Agent: to preserve packet size
--domcase ; mix domain case : Host: TeSt.cOm
--methodeol ; add '\n' before method and remove space after Host:
--synack-split=[syn|synack|acksyn] ; perform TCP split handshake : send SYN only, SYN+ACK or ACK+SYN
--orig-ttl=<int> ; set TTL for original packets
--orig-ttl6=<int> ; set ipv6 hop limit for original packets. by default ttl value is used
--orig-autottl=[<delta>[:<min>[-<max>]]|-] ; auto ttl mode for both ipv4 and ipv6. default: +5:3-64. "0:0-0" or "-" disables autottl.
--orig-autottl6=[<delta>[:<min>[-<max>]]|-] ; overrides --orig-autottl for ipv6 only
--orig-mod-start=[n|d|s]N ; apply orig TTL mod to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N
--orig-mod-cutoff=[n|d|s]N ; apply orig TTL mod to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--dup=<int> ; duplicate original packets. send N dups before original.
--dup-replace=[0|1] ; 1 or no argument means do not send original, only dups
--dup-ttl=<int> ; set TTL for dups
--dup-ttl6=<int> ; set ipv6 hop limit for dups. by default ttl value is used
--dup-autottl=[<delta>[:<min>[-<max>]]|-] ; auto ttl mode for both ipv4 and ipv6. default: -1:3-64. "0:0-0" or "-" disables autottl.
--dup-autottl6=[<delta>[:<min>[-<max>]]|-] ; overrides --dup-autottl for ipv6 only
--dup-fooling=<mode>[,<mode>] ; can use multiple comma separated values. modes : none md5sig badseq badsum datanoack hopbyhop hopbyhop2
--dup-badseq-increment=<int|0xHEX> ; badseq fooling seq signed increment for dup. default -10000
--dup-badack-increment=<int|0xHEX> ; badseq fooling ackseq signed increment for dup. default -66000
--dup-start=[n|d|s]N ; apply dup to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N
--dup-cutoff=[n|d|s]N ; apply dup to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--dpi-desync=[<mode0>,]<mode>[,<mode2>] ; try to desync dpi state. modes : synack fake fakeknown rst rstack hopbyhop destopt ipfrag1 multisplit multidisorder fakedsplit fakeddisorder ipfrag2 udplen tamper
--dpi-desync-fwmark=<int|0xHEX> ; override fwmark for desync packet. default = 0x40000000 (1073741824)
--dpi-desync-ttl=<int> ; set ttl for desync packet
--dpi-desync-ttl6=<int> ; set ipv6 hop limit for desync packet. by default ttl value is used.
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]] ; auto ttl mode for both ipv4 and ipv6. default: 1:3-20
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]] ; overrides --dpi-desync-autottl for ipv6 only
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]|-] ; auto ttl mode for both ipv4 and ipv6. default: -1:3-20. "0:0-0" or "-" disables autottl.
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]|-] ; overrides --dpi-desync-autottl for ipv6 only
--dpi-desync-fooling=<mode>[,<mode>] ; can use multiple comma separated values. modes : none md5sig ts badseq badsum datanoack hopbyhop hopbyhop2
--dpi-desync-repeats=<N> ; send every desync packet N times
--dpi-desync-skip-nosni=0|1 ; 1(default)=do not act on ClientHello without SNI (ESNI ?)
@@ -202,6 +230,7 @@ nfqws takes the following parameters:
--filter-tcp=[~]port1[-port2]|* ; TCP port filter. ~ means negation. setting tcp and not setting udp filter denies udp. comma separated list supported.
--filter-udp=[~]port1[-port2]|* ; UDP port filter. ~ means negation. setting udp and not setting tcp filter denies tcp. comma separated list supported.
--filter-l7=<proto> ; L6-L7 protocol filter. multiple comma separated values allowed. proto: http tls quic wireguard dht discord stun unknown
--filter-ssid=ssid1[,ssid2,ssid3,...] ; per profile wifi SSID filter
--ipset=<filename> ; ipset include filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)
--ipset-ip=<ip_list> ; comma separated fixed subnet list
--ipset-exclude=<filename> ; ipset exclude filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)
@@ -257,10 +286,13 @@ Fakes are separate generated by nfqws packets carrying false information for DPI
* **datanoack** sends tcp fakes without ACK flag. Servers do not accept this but DPI may accept.
This mode may break NAT and may not work with iptables if masquerade is used, even from the router itself.
Works with nftables properly. Likely requires external IP address (some ISPs pass these packets through their NAT).
* **autottl** tries to automatically guess TTL value that allows DPI to receive fakes and does not allow them to reach the server.
This tech relies on well known TTL values used by OS : 64,128,255. nfqws takes first incoming packet (YES, you need to redirect it too),
guesses path length and decreases by `delta` value (default 1). If resulting value is outside the range (min,max - default 3,20)
then its normalized to min or max. If the path shorter than the value then autottl fails and falls back to the fixed value.
* **autottl** tries to automatically guess hop count to the server and compute TTL by adding some delta value that can be positive or negative.
Positive deltas must be preceeded by unary `+` sign. Deltas without any unary sign are treated negative for old versions compatibility reasons.
This tech relies on well known TTL default values used by OS : 64,128,255.
nfqws needs first incoming packet to see it's TTL. You must redirect it too.
If resulting value TTL is outside the range (min,max) then its normalized to min or max.
If delta is negative and TTL is longer than guessed hop count or delta is positive and TTL is shorter than guessed hop count
then autottl fails and falls back to the fixed value.
This can help if multiple DPIs exists on backbone channels, not just near the ISP.
Can fail if inbound and outbound paths are not symmetric.
@@ -356,6 +388,44 @@ For example, `hopbyhop,multisplit` means split original tcp packet into several
With `hopbyhop,ipfrag2` header sequence will be : `ipv6,hop-by-hop,fragment,tcp/udp`.
`ipfrag1` mode may not always work without special preparations. See "IP Fragmentation" notices.
### Original modding
Parameters `--orig-ttl` and `--orig-ttl6` allow to set TTL on original packets.
All further packet manipulations, e.g. segmentation, take modded original as data source and inherit modded TTL.
`--orig-autottl` and `--orig-autottl6` work the same way as `dpi-desync-autottl`, but on original packets.
Delta should have unary `+` sign to produce TTL longer than guessed hop count. Otherwise nothing will reach the server.
Example : `--orig-autottl=+5:3-64`.
`--orig-mod-start` and `--orig-mod-cutoff` specify start and end conditions for original modding. The work the same way as
`--dpi-desync-start` and `--dpi-desync-cutoff`.
This function can be useful when DPI hunts for fakes and blocks suspicious connections.
DPI can compute TTL difference between packets and fire block trigger if it exceedes some threshold.
### Duplicates
Duplicates are copies of original packets which are sent before them. Duplicates are enabled by `--dup=N`, where N is dup count.
`--dup-replace` disables sending of original.
Dups are sent only when original would also be sent without reconstruction.
For example, if TCP segmentation happens, original is actually dropped and is being replaced by artificially constructed new packets.
Dups are not sent in this case.
All dup fooling modes are available : `--dup-ttl`. `--dup-ttl6`, `--dup-fooling`.
You decide whether these packets need to reach the server and in what form, according to the intended strategy.
`--dup-autottl` and `--dup-autottl6` work the same way as `dpi-desync-autottl`.
Delta can be preceeded by unary `+` or `-` sign.
Example : `--dup-autottl=-2:3-64`.
`--dup-start` and `--dup-cutoff` specify start and end conditions for dupping. The work the same way as
`--dpi-desync-start` and `--dpi-desync-cutoff`.
This function can help if DPI compares some characteristics of fake and original packets and block connection if they differ some way.
Fooled duplicates can convince DPI that the whole session has an anomaly.
For example, all connection is protected by MD5 signature, not individual packets.
### Server reply reaction
There are DPIs that analyze responses from the server, particularly the certificate from the ServerHello that contain domain name(s). The ClientHello delivery confirmation is an ACK packet from the server with ACK sequence number corresponding to the length of the ClientHello+1.
@@ -377,12 +447,26 @@ Without extra parameter payload is 16 zero bytes.
`--dpi-desync` takes up to 3 comma separated modes.
* 0 phase modes work during the connection establishement : `synack`, `syndata` `--wsize`, `--wssize`. [hostlist](((#multiple-strategies))) filters are not applicable.
* 0 phase modes work during the connection establishement : `synack`, `syndata` `--wsize`, `--wssize`. [hostlist](#multiple-strategies) filters are applicable only if [`--ipcache-hostname`](#ip-cache) is enabled.
* In the 1st phase fakes are sent before original data : `fake`, `rst`, `rstack`.
* In the 2nd phase original data is sent in a modified way (for example `fakedsplit` or `ipfrag2`).
Modes must be specified in phase ascending order.
### IP cache
`ipcache` is the structure in the process memory that stores some information by IP address and interface name key.
This information can be used as missing data. Currently it's used in the following cases :
1. IP,interface => hop count . This is used to apply autottl at 0 phase since the first session packet. If the record is absent autottl will not be applied immediately. Second time it will be applied immediately using cached hop count.
2. IP => hostname . Hostname is cached to be used in 0 phase strategies. Mode is disabled by default and can be enabled by `ipcache-hostname` parameter.
This tech is experimental. There's no one-to-one correspondence between IP and domain name. Multiple domains can resolve to the same IP.
If collision happens hostname is replaced. On CDNs a domain can resolve to different IPs over time. `--ipcache-lifetime` limits how long cached record is valid. It's 2 hours by default.
Be prepared for unexpected results that can be explained only by reading debug logs.
SIGUSR2 forces process to output it's ipcache to stdout.
### CONNTRACK
nfqws is equipped with minimalistic connection tracking system (conntrack)
@@ -577,6 +661,30 @@ This way you may never unblock all resources and only confuse yourself.
IMPORTANT : user-mode ipset implementation was not designed as a kernel version replacement. Kernel version is much more effective.
It's for the systems that lack ipset support : Windows and Linux without nftables and ipset kernel modules (Android, for example).
### WIFI filtering
Wifi interface name is not related to connected SSID.
It's possible to connect interface to different SSIDs.
They may require different strategies. How to solve this problem ?
You can run and stop nfqws instances manually. But you can also automate this.
Windows version `winws` has global filter `--ssid-filter`.
It connects or disconnects `winws` depending on connected SSIDs.
Routing is not take into account. This approach is possible because windivert can have multiple handlers with intersecting filter.
If SSID changes one `winws` connects and others disconnect.
`winws` solution is hard to implement in Linux because one nfqueue can have only one handler and it's impossible to pass same traffic to multiple queues.
One must connect when others have already disconnected.
Instead, `nfqws` has per-profile `--filter-ssid` parameter. Like `--ssid-filter` it takes comma separated SSID list.
`nfqws` maintains ifname->SSID list which is updated not faster than once a second.
When a packet comes incoming or outgoing interface name is matched to the SSID and then used in profile selection algorithm.
SSID info is taken the same way as `iw dev <ifname> info` does (nl80211).
Unfortunately it's broken since kernel 5.19 and still unfixed in 6.14.
In the latter case `iwgetid` way is used (wireless extensions).
Wireless extensions are deprecated. Some kernels can be built without wext support.
Before using `--filter-ssid` check that any of the mentioned commands can return SSID.
### Virtual machines
Most of nfqws packet magic does not work from VMs powered by virtualbox and vmware when network is NATed.
@@ -669,6 +777,31 @@ In `iptables` flow offloading is controlled by openwrt proprietary extension `FL
Flow offloading does not interfere with **tpws** and `OUTPUT` traffic. It only breaks nfqws that fools `FORWARD` traffic.
### Server side fooling
It's also possible.
nfqws is intended for client side attacks. That's why it recognizes direct and reply traffic based on role in connection establishement.
If it sees SYN then source IP is client IP. If it sees SYN,ACK then source ip is server IP.
For UDP client address is considered as source IP of the first seen packet of src_ip,src_port,dst_ip,dst_port tuple.
This does not work correctly on the server side. Client traffic is reply traffic, server traffic is direct traffic.
`--wsize` works in any case. It can be used on both client and server.
Other techs work only if nfqws treats traffic as direct traffic.
To apply them to server originated traffic disable conntrack by `--ctrack-disable` parameter.
If a packet is not found in conntrack it's treated as direct and techs like `multidisorder` will be applied.
Most of the protocols will not be recognized because protocol recognition system only reacts to client packets.
To make things working use `--dpi-desync-any-protocol` with connbytes or packet payload limiter.
start/cutoff are unavailable because they are conntrack based.
`--synack-split` removes standard SYN,ACK packet and replaces it with one SYN packet, SYN then ACK separate packets or ACK then SYN separate packets.
Client sends SYN,ACK in reply which usually only server does.
This makes some DPI's to treat connection establishement roles wrong. They stop to block.
See [split handshake](https://nmap.org/misc/split-handshake.pdf).
On server side traffic should be redirected to nfqws using source port numbers and original connbytes direction.
## tpws
@@ -705,6 +838,8 @@ tpws is transparent proxy.
--local-tcp-user-timeout=<seconds> ; set tcp user timeout for local leg (default : 10, 0 = system default)
--remote-tcp-user-timeout=<seconds> ; set tcp user timeout for remote leg (default : 20, 0 = system default)
--fix-seg=<int> ; recover failed TCP segmentation at the cost of slowdown. wait up to N msec.
--ipcache-lifetime=<int> ; time in seconds to keep cached domain name (default 7200). 0 = no expiration
--ipcache-hostname=[0|1] ; 1 or no argument enables ip->hostname caching
--no-resolve ; disable socks5 remote dns
--resolver-threads=<int> ; number of resolver worker threads
--maxconn=<max_connections> ; max number of local legs
@@ -755,7 +890,7 @@ tpws is transparent proxy.
--daemon ; daemonize
--pidfile=<filename> ; write pid to file
--user=<username> ; drop root privs
--uid=uid[:gid] ; drop root privs
--uid=uid[:gid1,gid2,...] ; drop root privs
```
### TCP segmentation in tpws
@@ -789,7 +924,7 @@ If you're attempting to split massive transmission with `--split-any-protocol` o
`--mss` sets TCP_MAXSEG socket option. Client sets this value in MSS TCP option in the SYN packet.
Server replies with it's own MSS in SYN,ACK packet. Usually servers lower their packet sizes but they still don't fit to supplied MSS. The greater MSS client sets the bigger server's packets will be.
If it's enough to split TLS 1.2 ServerHello, it may fool DPI that checks certificate domain name.
This scheme may significantly lower speed. Hostlist filter is possible only in socks mode if client uses remote resolving (firefox `network.proxy.socks_remote_dns`).
This scheme may significantly lower speed. Hostlist filter is possible only in socks mode if client uses remote resolving (firefox `network.proxy.socks_remote_dns`) or if `ipcache-hostname` is enabled.
`--mss` is not required for TLS1.3. If TLS1.3 is negotiable then MSS make things only worse. Use only if nothing better is available. Works only in Linux, not BSD or MacOS.
### Other tamper options
@@ -1270,6 +1405,10 @@ With other values or if the parameter is commented out, the rules will not be ap
This is useful if you have a firewall management system, in the settings of which you should tie the rules.
Not applicable to `OpenWRT` if used with `firewall3+iptables`.
`FILTER_TTL_EXPIRED_ICMP=1` blocks icmp time exceeded messages in response to connections handled by nfqws.
Linux closes socket if it receives this icmp in response to SYN packet. Similar mechanism exists for datagram sockets.
It's better to disable this if you do not expect problems caused by icmp.
The following settings are not relevant for openwrt :
If your system works as a router, then you need to enter the names of the internal and external interfaces:

View File

@@ -1,9 +1,10 @@
# zapret v71
# zapret v71.1.1
# ВНИМАНИЕ, остерегайтесь мошенников
zapret является свободным и open source.
Всякий, кто понуждает вас скачивать zapret только с его ресурса, требует удалить ссылки, видео, файлы, обосновывая эти требования авторскими правами, сам нарушает [лицензию](./LICENSE.txt).
Однако, это не исключает [добровольные пожертвования](#поддержать-разработчика).
# Multilanguage README
@@ -38,6 +39,7 @@ zapret является свободным и open source.
- [ПОДДЕРЖКА UDP](#поддержка-udp)
- [IP ФРАГМЕНТАЦИЯ](#ip-фрагментация)
- [МНОЖЕСТВЕННЫЕ СТРАТЕГИИ](#множественные-стратегии)
- [ФИЛЬТРАЦИЯ ПО WIFI](#фильтрация-по-wifi)
- [IPTABLES ДЛЯ NFQWS](#iptables-для-nfqws)
- [NFTABLES ДЛЯ NFQWS](#nftables-для-nfqws)
- [FLOW OFFLOADING](#flow-offloading)
@@ -183,16 +185,16 @@ dvtws, собираемый из тех же исходников (см. [док
--synack-split=[syn|synack|acksyn] ; выполнить tcp split handshake. вместо SYN,ACK отсылать только SYN, SYN+ACK или ACK+SYN
--orig-ttl=<int> ; модифицировать TTL оригинального пакета
--orig-ttl6=<int> ; модифицировать ipv6 hop limit оригинальных пакетов. если не указано, используется значение --orig-ttl
--orig-autottl=[<delta>[:<min>[-<max>]]] ; режим auto ttl для ipv4 и ipv6. по умолчанию: +5:3-64. delta=0 отключает функцию
--orig-autottl6=[<delta>[:<min>[-<max>]]] ; переопределение предыдущего параметра для ipv6
--orig-autottl=[<delta>[:<min>[-<max>]]|-] ; режим auto ttl для ipv4 и ipv6. по умолчанию: +5:3-64. "0:0-0" или "-" отключает функцию
--orig-autottl6=[<delta>[:<min>[-<max>]]|-] ; переопределение предыдущего параметра для ipv6
--orig-mod-start=[n|d|s]N ; применять orig-mod только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--orig-mod-cutoff=[n|d|s]N ; применять orig-mod только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--dup=<int> ; высылать N дубликатов до оригинала
--dup-replace=[0|1] ; 1 или отсутствие аргумента блокирует отправку оригинала. отправляются только дубликаты.
--dup-ttl=<int> ; модифицировать TTL дубликатов
--dup-ttl6=<int> ; модифицировать ipv6 hop limit дубликатов. если не указано, используется значение --dup-ttl
--dup-autottl=[<delta>[:<min>[-<max>]]] ; режим auto ttl для ipv4 и ipv6. по умолчанию: +1:3-64. delta=0 отключает функцию
--dup-autottl6=[<delta>[:<min>[-<max>]]] ; переопределение предыдущего параметра для ipv6
--dup-autottl=[<delta>[:<min>[-<max>]]|-] ; режим auto ttl для ipv4 и ipv6. по умолчанию: +1:3-64. "0:0-0" или "-" отключает функцию
--dup-autottl6=[<delta>[:<min>[-<max>]]|-] ; переопределение предыдущего параметра для ipv6
--dup-fooling=<fooling> ; дополнительные методики как сделать, чтобы дубликат не дошел до сервера. none md5sig badseq badsum datanoack hopbyhop hopbyhop2
--dup-badseq-increment=<int|0xHEX> ; инкремент sequence number для badseq. по умолчанию -10000
--dup-badack-increment=<int|0xHEX> ; инкремент ack sequence number для badseq. по умолчанию -66000
@@ -207,8 +209,8 @@ dvtws, собираемый из тех же исходников (см. [док
--dpi-desync-fwmark=<int|0xHEX> ; бит fwmark для пометки десинхронизирующих пакетов, чтобы они повторно не падали в очередь. default = 0x40000000
--dpi-desync-ttl=<int> ; установить ttl для десинхронизирующих пакетов
--dpi-desync-ttl6=<int> ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение --dpi-desync-ttl
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]] ; режим auto ttl для ipv4 и ipv6. по умолчанию: 1:3-20. delta=0 отключает функцию.
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]] ; переопределение предыдущего параметра для ipv6
--dpi-desync-autottl=[<delta>[:<min>[-<max>]]|-] ; режим auto ttl для ipv4 и ipv6. по умолчанию: 1:3-20. "0:0-0" или "-" отключает функцию
--dpi-desync-autottl6=[<delta>[:<min>[-<max>]]|-] ; переопределение предыдущего параметра для ipv6
--dpi-desync-fooling=<fooling> ; дополнительные методики как сделать, чтобы фейковый пакет не дошел до сервера. none md5sig badseq badsum datanoack hopbyhop hopbyhop2
--dpi-desync-repeats=<N> ; посылать каждый генерируемый в nfqws пакет N раз (не влияет на остальные пакеты)
--dpi-desync-skip-nosni=0|1 ; 1(default)=не применять dpi desync для запросов без hostname в SNI, в частности для ESNI
@@ -225,6 +227,7 @@ dvtws, собираемый из тех же исходников (см. [док
--dpi-desync-fake-unknown=<filename>|0xHEX ; файл, содержащий фейковый пейлоад неизвестного протокола для dpi-desync=fake, на замену стандартным нулям 256 байт
--dpi-desync-fake-syndata=<filename>|0xHEX ; файл, содержащий фейковый пейлоад пакета SYN для режима десинхронизации syndata
--dpi-desync-fake-quic=<filename>|0xHEX ; файл, содержащий фейковый QUIC Initial
--dpi-desync-fake-wireguard=<filename>|0xHEX ; файл, содержащий фейковый wireguard handshake initiation
--dpi-desync-fake-dht=<filename>|0xHEX ; файл, содержащий фейковый пейлоад DHT протокола для dpi-desync=fake, на замену стандартным нулям 64 байт
--dpi-desync-fake-discord=<filename>|0xHEX ; файл, содержащий фейковый пейлоад Discord протокола нахождения IP адреса для голосовых чатов для dpi-desync=fake, на замену стандартным нулям 64 байт
--dpi-desync-fake-stun=<filename>|0xHEX ; файл, содержащий фейковый пейлоад STUN протокола для dpi-desync=fake, на замену стандартным нулям 64 байт
@@ -254,6 +257,7 @@ dvtws, собираемый из тех же исходников (см. [док
--filter-tcp=[~]port1[-port2]|* ; фильтр портов tcp для текущей стратегии. ~ означает инверсию. установка фильтра tcp и неустановка фильтра udp запрещает udp. поддерживается список через запятую.
--filter-udp=[~]port1[-port2]|* ; фильтр портов udp для текущей стратегии. ~ означает инверсию. установка фильтра udp и неустановка фильтра tcp запрещает tcp. поддерживается список через запятую.
--filter-l7=<proto> ; фильтр протокола L6-L7. поддерживается несколько значений через запятую. proto : http tls quic wireguard dht discord stun unknown
--filter-ssid=ssid1[,ssid2,ssid3,...] ; фильтр по имени wifi сети (только для linux)
--ipset=<filename> ; включающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
--ipset-ip=<ip_list> ; фиксированный список подсетей через запятую. можно использовать # в начале для комментирования отдельных подсетей.
--ipset-exclude=<filename> ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
@@ -770,6 +774,36 @@ L7 протокол становится известен обычно посл
> Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без подд3ержки ipset в ядре.
> Конкретно - Windows и ядра Linux, собранные без nftables и ipset модулей ядра. Например, в android нет ipset.
### ФИЛЬТРАЦИЯ ПО WIFI
Имя wifi сети никак не связано с сетевым интерфейсом адаптера wifi.
Интерфейс один, подключиться можно к любой сети. Для разных сетей разные стратегии.
Стратегия от сети A не работает или ломает сеть B. Что делать ?
Можно вручную запускать и снимать инстансы nfqws. Но можно поступить иначе.
В windows версии winws есть глобальный фильтр `--ssid-filter`.
Он включает или отключает инстанс winws в зависимости от подключенности любого адаптера к конкретной wifi сети.
При этом не учитывается маршрутизация. Такой подход возможен потому, что к windivert можно прицепить несколько инстансов winws на пересекающихся фильтрах.
При смене wifi сети одни будут включаться, другие выключаться.
Для linux применяется иное решение. Фильтр `--filter-ssid` относится к конкретному профилю.
Невозможно повесить несколько инстансов nfqws на одну и ту же очередь или направить один и тот же трафик на несколько очередей.
Подключение и отключение от очереди разных инстансов сопряжено со сложностями синхронизации между ними.
Поэтому обрабатывать трафик должен один инстанс, и он должен уметь работать с разными wifi сетями.
Это и реализовано в параметре `--filter-ssid`. Он берет список имен wifi сетей (SSID) через запятую аналогично `--ssid-filter` для winws.
При выборе профиля имеет значение куда идет конкретный обрабатываемый пакет. На какой интерфейс. Или с какого интерфейса пакет пришел, если он считается входящим.
Поэтому даже если у вас часть трафика идет на одну сеть, часть на другую, а часть вообще не идет по wifi, то все это можно настроить.
Информация о подключенных сетях берется способом, используемым командой `iw dev <ifname> info` (nl80211).
К сожалению, на ядрах с 5.19 до самых последних (6.14 не работает) этот способ сломан.
В этом случае используется способ iwgetid (wireless extensions).
wireless extensions считаются deprecated и на новых ядрах реализованы как прослойка совместимости.
Некоторые ядра могут быть собраны без wireless extensions.
Перед использованием `--filter-ssid` удостоверьтесь, что любая из этих команд возвращает SSID.
Сканируются все wifi интерфейсы, составляется список interface->SSID. Он обновляется по мере поступления
пакетов, но не чаще 1 раза в секунду.
### IPTABLES ДЛЯ NFQWS
iptables для задействования атаки на первые пакеты данных в tcp соединении :
@@ -2285,7 +2319,7 @@ chcon u:object_r:system_file:s0 /data/local/tmp/zapret/tpws
Если компа нет, то развертка chroot - единственный вариант, хотя и неудобный.
Подойдет что-то легковесное, например, alpine или даже OpenWrt.
Если это не эмулятор android, то универсальная архитектура - arm (любой вариант).
Если вы точно знаете, что ОС у вас 64-разрядная, то лучше вместо arm - aarch64.
Если вы точно знаете, что ОС у вас 64-разрядная, то лучше вместо arm - arm64.
Выяснить архитектуру можно командой `uname -a`.
```

View File

@@ -24,17 +24,15 @@ Tor поддерживает "из коробки" режим transparent proxy.
Устанавливать их нужно с опцией opkg --force-overwrite, поскольку они перепишут ssh клиент от dropbear.
После установки пакетов расслабим неоправданно жестокие права : chmod 755 /etc/ssh.
Следует создать пользователя, под которым будем крутить ssh client. Допустим, это будет 'proxy'.
Сначала установить пакет shadow-useradd.
Сначала установить пакеты shadow-useradd и shadow-su.
------------------
useradd -d /home/proxy proxy
useradd -s /bin/false -d /home/proxy proxy
mkdir -p /home/proxy
chown proxy:proxy /home/proxy
------------------
Openssh ловит разные глюки, если у него нет доступа к /dev/tty.
Добавим в /etc/rc.local строчку : "chmod 666 /dev/tty"
Сгенерируем для него ключ RSA для доступа к ssh серверу.
Сгенерируем ключ RSA для доступа к ssh серверу.
------------------
su proxy
su -s /bin/ash proxy
cd
mkdir -m 700 .ssh
cd .ssh

View File

@@ -83,7 +83,7 @@ You must choose to install `curl`. To compile from sources install `gcc-core`,`m
Make from directory `nfq` using `make cygwin64` or `make cygwin32` for 64 and 32 bit versions.
`winws` requires `cygwin1.dll`, `windivert.dll`, `windivert64.sys` or `windivert32.sys`.
You can take them from `binaries/win64` or `binaries/win32`.
You can take them from `binaries/windows-x86_64` or `binaries/windows-x86`.
There's no `arm64` signed `windivert` driver and no `cygwin`.
But it's possible to use unsigned driver version in test mode and user mode components with x64 emulation.
@@ -128,19 +128,6 @@ If you want to run `winws` from `cygwin` delete, rename or move `cygwin1.dll`.
To simplify things it's advised to use `zapret-win-bundle`.
### auto start
To start `winws` with windows use windows task scheduler. There are `task_*.cmd` batch files in `binaries/win64/zapret-winws`.
They create, remove, start and stop scheduled task `winws1`. They must be run as administrator.
Edit `task_create.cmd` and write your `winws` parameters to `%WINWS1%` variable. If you need multiple `winws` instances
clone the code in all cmd files to support multiple tasks `winws1,winws2,winws3,...`.
Tasks can also be controlled from GUI `taskschd.msc`.
Also you can use windows services the same way with `service_*.cmd`.
### zapret-win-bundle
To make your life easier there's ready to use [bundle](https://github.com/bol-van/zapret-win-bundle) with `cygwin`,`blockcheck` and `winws`.
@@ -166,3 +153,15 @@ unix2dos winws.log
`winws.log` will be in `cygwin/home/<username>`. `unix2dos` helps with `windows 7` notepad. It's not necessary in `Windows 10` and later.
Because 32-bit systems are rare nowadays `zapret-win-bundle` exists only for `Windows x64/arm64`.
### auto start
To start `winws` with windows use windows task scheduler. There are `task_*.cmd` batch files in `binaries/windows-x86-64/zapret-winws`.
They create, remove, start and stop scheduled task `winws1`. They must be run as administrator.
Edit `task_create.cmd` and write your `winws` parameters to `%WINWS1%` variable. If you need multiple `winws` instances
clone the code in all cmd files to support multiple tasks `winws1,winws2,winws3,...`.
Tasks can also be controlled from GUI `taskschd.msc`.
Also you can use windows services the same way with `service_*.cmd`.

View File

@@ -125,7 +125,7 @@ network locations в win10/11. Кое-что есть в **powershell**.
Существует неочевидный момент, каcаемый запуска **winws** из cygwin shell\`а. Если в директории, где находится winws, находится
копия `cygwin1.dll`, **winws** не запустится.
Если нужен запуск под cygwin, то следует удалить или переместить `cygwin1.dll` из `binaries/win64`. Это нужно для работы blockcheck.
Если нужен запуск под cygwin, то следует удалить или переместить `cygwin1.dll` из `binaries/windows-x86_64`. Это нужно для работы blockcheck.
Из cygwin шелла можно посылать winws сигналы через `kill` точно так же, как в `*nix`.
Как получить совместимый с windows 7 и winws cygwin :
@@ -203,22 +203,6 @@ _cygwin_ для обычной работы **winws** не нужен.
Однако, хотя такой способ и работает, использование **winws** сильно облегчает [zapret-win-bundle](https://github.com/bol-van/zapret-win-bundle).
Там нет проблемы с `cygwin.dll`.
## Автозапуск winws
Для запуска **winws** вместе с windows есть 2 варианта. Планировщик задач или службы windows.
Можно создавать задачи и управлять ими через консольную программу schtasks.
В директории `binaries/win64/winws` подготовлены файлы `task_*.cmd` .
В них реализовано создание, удаление, старт и стоп одной копии процесса winws с параметрами из переменной `%WINWS1%`.
Исправьте параметры на нужную вам стратегию. Если для разных фильтров применяется разная стратегия, размножьте код
для задач _winws1_,_winws2_,_winws3_,_..._
Аналогично настраивается вариант запуска через службы windows. Смотрите `service_*.cmd`.
Все батники требуется запускать от имени администратора.
Управлять задачами можно так же из графической программы управления планировщиком `taskschd.msc`
## Zapret-win-bundle
Можно не возиться с _cygwin_, а взять готовый пакет, включающий в себя _cygwin_ и _blockcheck_ : https://github.com/bol-van/zapret-win-bundle
@@ -257,3 +241,19 @@ _zapret-winws_ - это отдельный комплект для повсед
> [!CAUTION]
> Поскольку 32-битные windows мало востребованы, _zapret-win-bundle_ существует только в варианте для windows _x64/arm64_.
## Автозапуск winws
Для запуска **winws** вместе с windows есть 2 варианта. Планировщик задач или службы windows.
Можно создавать задачи и управлять ими через консольную программу schtasks.
В директории `binaries/windows-x86_64/winws` подготовлены файлы `task_*.cmd` .
В них реализовано создание, удаление, старт и стоп одной копии процесса winws с параметрами из переменной `%WINWS1%`.
Исправьте параметры на нужную вам стратегию. Если для разных фильтров применяется разная стратегия, размножьте код
для задач _winws1_,_winws2_,_winws3_,_..._
Аналогично настраивается вариант запуска через службы windows. Смотрите `service_*.cmd`.
Все батники требуется запускать от имени администратора.
Управлять задачами можно так же из графической программы управления планировщиком `taskschd.msc`

View File

@@ -56,9 +56,6 @@ RemoveIPC=true
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@resources
UMask=0077
[Install]

View File

@@ -55,8 +55,6 @@ RemoveIPC=true
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=@system-service
UMask=0077
[Install]

View File

@@ -8,6 +8,68 @@ 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"
@@ -15,23 +77,36 @@ check_dir()
local out
if [ -f "$exe" ]; then
if [ -x "$exe" ]; then
# 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
out=$(echo 0.0.0.0 | bash -c "\"$exe"\" 2>/dev/null)
elif exists zsh; then
out=$(echo 0.0.0.0 | zsh -c "\"$exe\"" 2>/dev/null)
else
# 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
local FIND=find
if ! exists find && exists busybox; then
FIND="busybox find"
fi
out=$(echo 0.0.0.0 | $FIND "$dir" -maxdepth 1 -name ip2net -exec {} \; 2>/dev/null)
fi
[ -n "$out" ]
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
@@ -53,28 +128,31 @@ ccp()
}
UNAME=$(uname)
unset PKTWS
case $UNAME in
Linux)
ARCHLIST="my x86_64 x86 aarch64 arm mips64r2-msb mips32r1-lsb mips32r1-msb lexra ppc"
ARCHLIST="my linux-x86_64 linux-x86 linux-arm64 linux-arm linux-mips64 linux-mipsel linux-mips linux-lexra linux-ppc"
PKTWS=nfqws
;;
Darwin)
ARCHLIST="my mac64"
;;
FreeBSD)
ARCHLIST="my freebsd-x64"
ARCHLIST="my freebsd-x86_64"
PKTWS=dvtws
;;
CYGWIN*)
UNAME=CYGWIN
ARCHLIST="win64 win32"
ARCHLIST="windows-x86_64 windows-x86"
PKTWS=winws
;;
*)
ARCHLIST="my"
esac
select_test_method
if [ "$1" = "getarch" ]; then
for arch in $ARCHLIST
do
@@ -85,6 +163,8 @@ if [ "$1" = "getarch" ]; then
fi
done
else
echo "using arch detect method : $TEST${ELF_ARCH:+ $ELF_ARCH}"
for arch in $ARCHLIST
do
[ -d "$BINDIR/$arch" ] || continue

View File

@@ -50,6 +50,29 @@ check_readonly_system()
}
}
check_source()
{
local bad=0
echo \* checking source files
case $SYSTEM in
systemd)
[ -f "$EXEDIR/init.d/systemd/zapret.service" ] || bad=1
;;
openrc)
[ -f "$EXEDIR/init.d/openrc/zapret" ] || bad=1
;;
macos)
[ -f "$EXEDIR/init.d/macos/zapret" ] || bad=1
;;
esac
[ "$bad" = 1 ] && {
echo 'some critical files are missing'
echo 'are you sure you are not using embedded release ? you need full version for traditional systems'
exitp 5
}
}
check_bins()
{
echo \* checking executables
@@ -57,6 +80,7 @@ check_bins()
fix_perms_bin_test "$EXEDIR"
local arch="$(get_bin_arch)"
local make_target
local cf="-march=native"
[ "$FORCE_BUILD" = "1" ] && {
echo forced build mode
if [ "$arch" = "my" ]; then
@@ -72,12 +96,13 @@ check_bins()
case $SYSTEM in
macos)
make_target=mac
cf=
;;
systemd)
make_target=systemd
;;
esac
CFLAGS="-march=native ${CFLAGS}" make -C "$EXEDIR" $make_target || {
CFLAGS="${cf:+$cf }${CFLAGS}" make -C "$EXEDIR" $make_target || {
echo could not compile
make -C "$EXEDIR" clean
exitp 8
@@ -230,9 +255,9 @@ select_mode_filter()
select_mode()
{
select_mode_filter
select_mode_mode
select_mode_iface
select_mode_filter
}
select_getlist()
@@ -883,8 +908,7 @@ umask 0022
fix_sbin_path
fsleep_setup
check_system
[ "$SYSTEM" = "macos" ] && . "$EXEDIR/init.d/macos/functions"
check_source
case $SYSTEM in
systemd)

View File

@@ -3,7 +3,8 @@ CFLAGS += -std=gnu99 -Os -flto=auto
CFLAGS_SYSTEMD = -DUSE_SYSTEMD
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_CYGWIN = -Wno-address-of-packed-member -static
LIBS_LINUX = -lnetfilter_queue -lnfnetlink -lz
LDFLAGS_ANDROID = -llog
LIBS_LINUX = -lz -lnetfilter_queue -lnfnetlink -lmnl
LIBS_SYSTEMD = -lsystemd
LIBS_BSD = -lz
LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32
@@ -21,7 +22,8 @@ nfqws: $(SRC_FILES)
systemd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_SYSTEMD) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LIBS_SYSTEMD) $(LDFLAGS)
android: nfqws
android: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LDFLAGS) $(LDFLAGS_ANDROID)
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o dvtws $(SRC_FILES) $(LIBS_BSD) $(LDFLAGS)

View File

@@ -29,6 +29,16 @@
#endif
#ifdef __linux__
#include <linux/nl80211.h>
#include <linux/genetlink.h>
#include <libmnl/libmnl.h>
#include <net/if.h>
#define _LINUX_IF_H // prevent conflict between linux/if.h and net/if.h in old gcc 4.x
#include <linux/wireless.h>
#include <sys/ioctl.h>
#endif
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment)
{
return htonl(ntohl(netorder_value)+cpuorder_increment);
@@ -1832,6 +1842,259 @@ bool rawsend_queue(struct rawpacket_tailhead *q)
}
#if defined(HAS_FILTER_SSID) && defined(__linux__)
// linux-specific wlan retrieval implementation
typedef void netlink_prepare_nlh_cb_t(struct nlmsghdr *nlh);
static bool netlink_genl_simple_transact(struct mnl_socket* nl, uint16_t type, uint16_t flags, uint8_t cmd, uint8_t version, netlink_prepare_nlh_cb_t cb_prepare_nlh, mnl_cb_t cb_data, void *data)
{
char buf[MNL_SOCKET_BUFFER_SIZE];
struct nlmsghdr *nlh;
struct genlmsghdr *genl;
ssize_t rd;
nlh = mnl_nlmsg_put_header(buf);
nlh->nlmsg_type = type;
nlh->nlmsg_flags = flags;
genl = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
genl->cmd = cmd;
genl->version = version;
if (cb_prepare_nlh) cb_prepare_nlh(nlh);
if (mnl_socket_sendto(nl, nlh, nlh->nlmsg_len) < 0)
{
DLOG_PERROR("mnl_socket_sendto");
return false;
}
while ((rd=mnl_socket_recvfrom(nl, buf, sizeof(buf))) > 0)
{
switch(mnl_cb_run(buf, rd, 0, 0, cb_data, data))
{
case MNL_CB_STOP:
return true;
case MNL_CB_OK:
break;
default:
return false;
}
}
return false;
}
static void wlan_id_prepare(struct nlmsghdr *nlh)
{
mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, "nl80211");
}
static int wlan_id_attr_cb(const struct nlattr *attr, void *data)
{
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
{
DLOG_PERROR("mnl_attr_type_valid");
return MNL_CB_ERROR;
}
switch(mnl_attr_get_type(attr))
{
case CTRL_ATTR_FAMILY_ID:
if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
{
DLOG_PERROR("mnl_attr_validate(family_id)");
return MNL_CB_ERROR;
}
*((uint16_t*)data) = mnl_attr_get_u16(attr);
break;
}
return MNL_CB_OK;
}
static int wlan_id_cb(const struct nlmsghdr *nlh, void *data)
{
return mnl_attr_parse(nlh, sizeof(struct genlmsghdr), wlan_id_attr_cb, data);
}
static uint16_t wlan_get_family_id(struct mnl_socket* nl)
{
uint16_t id;
return netlink_genl_simple_transact(nl, GENL_ID_CTRL, NLM_F_REQUEST | NLM_F_ACK, CTRL_CMD_GETFAMILY, 1, wlan_id_prepare, wlan_id_cb, &id) ? id : 0;
}
static int wlan_info_attr_cb(const struct nlattr *attr, void *data)
{
struct wlan_interface *wlan = (struct wlan_interface *)data;
switch(mnl_attr_get_type(attr))
{
case NL80211_ATTR_IFINDEX:
if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
{
DLOG_PERROR("mnl_attr_validate(ifindex)");
return MNL_CB_ERROR;
}
wlan->ifindex = mnl_attr_get_u32(attr);
break;
case NL80211_ATTR_SSID:
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
{
DLOG_PERROR("mnl_attr_validate(ssid)");
return MNL_CB_ERROR;
}
snprintf(wlan->ssid,sizeof(wlan->ssid),"%s",mnl_attr_get_str(attr));
break;
case NL80211_ATTR_IFNAME:
if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
{
DLOG_PERROR("mnl_attr_validate(ifname)");
return MNL_CB_ERROR;
}
snprintf(wlan->ifname,sizeof(wlan->ifname),"%s",mnl_attr_get_str(attr));
break;
}
return MNL_CB_OK;
}
static int wlan_info_cb(const struct nlmsghdr *nlh, void *data)
{
int ret;
struct wlan_interface_collection *wc = (struct wlan_interface_collection*)data;
if (wc->count>=WLAN_INTERFACE_MAX) return MNL_CB_OK;
memset(wc->wlan+wc->count,0,sizeof(wc->wlan[0]));
ret = mnl_attr_parse(nlh, sizeof(struct genlmsghdr), wlan_info_attr_cb, wc->wlan+wc->count);
if (ret>=0 && *wc->wlan[wc->count].ifname && wc->wlan[wc->count].ifindex)
{
if (*wc->wlan[wc->count].ssid)
wc->count++;
else
{
// sometimes nl80211 does not return SSID but wireless ext does
int wext_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (wext_fd!=-1)
{
struct iwreq req;
snprintf(req.ifr_ifrn.ifrn_name,sizeof(req.ifr_ifrn.ifrn_name),"%s",wc->wlan[wc->count].ifname);
req.u.essid.pointer = wc->wlan[wc->count].ssid;
req.u.essid.length = sizeof(wc->wlan[wc->count].ssid);
req.u.essid.flags = 0;
if (ioctl(wext_fd, SIOCGIWESSID, &req)!=-1)
if (*wc->wlan[wc->count].ssid)
wc->count++;
close(wext_fd);
}
}
}
return ret;
}
static bool wlan_info(struct mnl_socket* nl, uint16_t wlan_family_id, struct wlan_interface_collection* w)
{
return netlink_genl_simple_transact(nl, wlan_family_id, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP, NL80211_CMD_GET_INTERFACE, 0, NULL, wlan_info_cb, w);
}
static bool wlan_init80211(struct mnl_socket** nl)
{
if (!(*nl = mnl_socket_open(NETLINK_GENERIC)))
{
DLOG_PERROR("mnl_socket_open");
return false;
}
if (mnl_socket_bind(*nl, 0, MNL_SOCKET_AUTOPID))
{
DLOG_PERROR("mnl_socket_bind");
return false;
}
return true;
}
static void wlan_deinit80211(struct mnl_socket** nl)
{
if (*nl)
{
mnl_socket_close(*nl);
*nl = NULL;
}
}
static time_t wlan_info_last = 0;
static bool wlan_info_rate_limited(struct mnl_socket* nl, uint16_t wlan_family_id, struct wlan_interface_collection* w)
{
bool bres = true;
time_t now = time(NULL);
// do not purge too often to save resources
if (wlan_info_last != now)
{
bres = wlan_info(nl,wlan_family_id,w);
wlan_info_last = now;
}
return bres;
}
static struct mnl_socket* nl_wifi = NULL;
static uint16_t id_nl80211;
struct wlan_interface_collection wlans = { .count = 0 };
void wlan_info_deinit(void)
{
wlan_deinit80211(&nl_wifi);
}
bool wlan_info_init(void)
{
wlan_info_deinit();
if (!wlan_init80211(&nl_wifi)) return false;
if (!(id_nl80211 = wlan_get_family_id(nl_wifi)))
{
wlan_info_deinit();
return false;
}
return true;
}
bool wlan_info_get(void)
{
return wlan_info(nl_wifi, id_nl80211, &wlans);
}
bool wlan_info_get_rate_limited(void)
{
return wlan_info_rate_limited(nl_wifi, id_nl80211, &wlans);
}
#endif
#ifdef HAS_FILTER_SSID
const char *wlan_ifname2ssid(const struct wlan_interface_collection *w, const char *ifname)
{
int i;
if (ifname)
{
for (i=0;i<w->count;i++)
if (!strcmp(w->wlan[i].ifname,ifname))
return w->wlan[i].ssid;
}
return NULL;
}
const char *wlan_ifidx2ssid(const struct wlan_interface_collection *w,int ifidx)
{
int i;
for (i=0;i<w->count;i++)
if (w->wlan[i].ifindex == ifidx)
return w->wlan[i].ssid;
return NULL;
}
const char *wlan_ssid_search_ifname(const char *ifname)
{
return wlan_ifname2ssid(&wlans,ifname);
}
const char *wlan_ssid_search_ifidx(int ifidx)
{
return wlan_ifidx2ssid(&wlans,ifidx);
}
#endif
uint8_t hop_count_guess(uint8_t ttl)
{
// 18.65.168.125 ( cloudfront ) 255

View File

@@ -1,5 +1,6 @@
#pragma once
#include "nfqws.h"
#include "checksum.h"
#include "packet_queue.h"
#include "pools.h"
@@ -261,7 +262,7 @@ typedef struct
int8_t delta;
uint8_t min, max;
} autottl;
#define AUTOTTL_ENABLED(a) (!!(a).delta)
#define AUTOTTL_ENABLED(a) ((a).delta || (a).min || (a).max)
uint8_t hop_count_guess(uint8_t ttl);
uint8_t autottl_eval(uint8_t hop_count, const autottl *attl);
@@ -272,3 +273,29 @@ void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transpo
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

View File

@@ -223,7 +223,7 @@ enum dpi_desync_mode desync_mode_from_string(const char *s)
static bool dp_match(
struct desync_profile *dp,
uint8_t l3proto, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto,
uint8_t l3proto, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto, const char *ssid,
bool *bCheckDone, bool *bCheckResult, bool *bExcluded)
{
bool bHostlistsEmpty;
@@ -241,6 +241,11 @@ static bool dp_match(
if (dp->filter_l7 && !l7_proto_match(l7proto, dp->filter_l7))
// L7 filter does not match
return false;
#ifdef HAS_FILTER_SSID
if (!LIST_EMPTY(&dp->filter_ssid) && !strlist_search(&dp->filter_ssid,ssid))
return false;
#endif
bHostlistsEmpty = PROFILE_HOSTLISTS_EMPTY(dp);
if (!dp->hostlist_auto && !hostname && !bHostlistsEmpty)
// avoid cpu consuming ipset check. profile cannot win if regular hostlists are present without auto hostlist and hostname is unknown.
@@ -271,7 +276,7 @@ static bool dp_match(
}
static struct desync_profile *dp_find(
struct desync_profile_list_head *head,
uint8_t l3proto, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto,
uint8_t l3proto, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto, const char *ssid,
bool *bCheckDone, bool *bCheckResult, bool *bExcluded)
{
struct desync_profile_list *dpl;
@@ -279,12 +284,12 @@ static struct desync_profile *dp_find(
{
char ip_port[48];
ntop46_port(dest, ip_port,sizeof(ip_port));
DLOG("desync profile search for %s target=%s l7proto=%s hostname='%s'\n", proto_name(l3proto), ip_port, l7proto_str(l7proto), hostname ? hostname : "");
DLOG("desync profile search for %s target=%s l7proto=%s ssid='%s' hostname='%s'\n", proto_name(l3proto), ip_port, l7proto_str(l7proto), ssid ? ssid : "", hostname ? hostname : "");
}
if (bCheckDone) *bCheckDone = false;
LIST_FOREACH(dpl, head, next)
{
if (dp_match(&dpl->dp,l3proto,dest,hostname,l7proto,bCheckDone,bCheckResult,bExcluded))
if (dp_match(&dpl->dp,l3proto,dest,hostname,l7proto,ssid,bCheckDone,bCheckResult,bExcluded))
{
DLOG("desync profile %d matches\n",dpl->dp.n);
return &dpl->dp;
@@ -1107,6 +1112,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
bool bSack,DF;
uint16_t nmss;
char host[256];
const char *ifname = NULL, *ssid = NULL;
uint32_t desync_fwmark = fwmark | params.desync_fwmark;
extract_endpoints(dis->ip, dis->ip6, dis->tcp, NULL, &src, &dst);
@@ -1122,6 +1128,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
if (!ConntrackPoolDoubleSearch(&params.conntrack, dis->ip, dis->ip6, dis->tcp, NULL, &ctrack_replay, &bReverse) || bReverse)
return verdict;
ifname = bReverse ? ifin : ifout;
#ifdef HAS_FILTER_SSID
ssid = wlan_ssid_search_ifname(ifname);
if (ssid) DLOG("found ssid for %s : %s\n",ifname,ssid);
#endif
dp = ctrack_replay->dp;
if (dp)
DLOG("using cached desync profile %d\n",dp->n);
@@ -1133,7 +1144,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
if (!(ctrack_replay->hostname = strdup(host)))
DLOG_ERR("strdup(host): out of memory\n");
}
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, IPPROTO_TCP, (struct sockaddr *)&dst, ctrack_replay->hostname, ctrack_replay->l7proto, NULL, NULL, NULL);
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, IPPROTO_TCP, (struct sockaddr *)&dst, ctrack_replay->hostname, ctrack_replay->l7proto, ssid, NULL, NULL, NULL);
ctrack_replay->dp_search_complete = true;
}
if (!dp)
@@ -1155,6 +1166,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
ctrack_replay = ctrack;
}
}
ifname = bReverse ? ifin : ifout;
#ifdef HAS_FILTER_SSID
ssid = wlan_ssid_search_ifname(ifname);
if (ssid) DLOG("found ssid for %s : %s\n",ifname,ssid);
#endif
if (dp)
DLOG("using cached desync profile %d\n",dp->n);
else if (!ctrack || !ctrack->dp_search_complete)
@@ -1170,7 +1186,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
DLOG_ERR("strdup(host): out of memory\n");
}
}
dp = dp_find(&params.desync_profiles, IPPROTO_TCP, (struct sockaddr *)&dst, hostname, ctrack ? ctrack->l7proto : UNKNOWN, NULL, NULL, NULL);
dp = dp_find(&params.desync_profiles, IPPROTO_TCP, (struct sockaddr *)&dst, hostname, ctrack ? ctrack->l7proto : UNKNOWN, ssid, NULL, NULL, NULL);
if (ctrack)
{
ctrack->dp = dp;
@@ -1574,7 +1590,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
dp = dp_find(&params.desync_profiles, IPPROTO_TCP, (struct sockaddr *)&dst,
ctrack_replay ? ctrack_replay->hostname : bHaveHost ? host : NULL,
ctrack_replay ? ctrack_replay->l7proto : l7proto,
ctrack_replay ? ctrack_replay->l7proto : l7proto, ssid,
&bCheckDone, &bCheckResult, &bCheckExcluded);
if (ctrack_replay)
{
@@ -2368,6 +2384,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
bool DF;
char host[256];
t_l7proto l7proto = UNKNOWN;
const char *ifname = NULL, *ssid = NULL;
extract_endpoints(dis->ip, dis->ip6, NULL, dis->udp, &src, &dst);
@@ -2379,6 +2396,12 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
if (!ConntrackPoolDoubleSearch(&params.conntrack, dis->ip, dis->ip6, NULL, dis->udp, &ctrack_replay, &bReverse) || bReverse)
return verdict;
ifname = bReverse ? ifin : ifout;
#ifdef HAS_FILTER_SSID
ssid = wlan_ssid_search_ifname(ifname);
if (ssid) DLOG("found ssid for %s : %s\n",ifname,ssid);
#endif
dp = ctrack_replay->dp;
if (dp)
DLOG("using cached desync profile %d\n",dp->n);
@@ -2390,7 +2413,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
if (!(ctrack_replay->hostname = strdup(host)))
DLOG_ERR("strdup(host): out of memory\n");
}
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, IPPROTO_UDP, (struct sockaddr *)&dst, ctrack_replay->hostname, ctrack_replay->l7proto, NULL, NULL, NULL);
dp = ctrack_replay->dp = dp_find(&params.desync_profiles, IPPROTO_UDP, (struct sockaddr *)&dst, ctrack_replay->hostname, ctrack_replay->l7proto, ssid, NULL, NULL, NULL);
ctrack_replay->dp_search_complete = true;
}
if (!dp)
@@ -2415,6 +2438,11 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
ctrack_replay = ctrack;
}
}
ifname = bReverse ? ifin : ifout;
#ifdef HAS_FILTER_SSID
ssid = wlan_ssid_search_ifname(ifname);
if (ssid) DLOG("found ssid for %s : %s\n",ifname,ssid);
#endif
if (dp)
DLOG("using cached desync profile %d\n",dp->n);
else if (!ctrack || !ctrack->dp_search_complete)
@@ -2430,7 +2458,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
DLOG_ERR("strdup(host): out of memory\n");
}
}
dp = dp_find(&params.desync_profiles, IPPROTO_UDP, (struct sockaddr *)&dst, hostname, ctrack ? ctrack->l7proto : UNKNOWN, NULL, NULL, NULL);
dp = dp_find(&params.desync_profiles, IPPROTO_UDP, (struct sockaddr *)&dst, hostname, ctrack ? ctrack->l7proto : UNKNOWN, ssid, NULL, NULL, NULL);
if (ctrack)
{
ctrack->dp = dp;
@@ -2703,7 +2731,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
dp = dp_find(&params.desync_profiles, IPPROTO_UDP, (struct sockaddr *)&dst,
ctrack_replay ? ctrack_replay->hostname : bHaveHost ? host : NULL,
ctrack_replay ? ctrack_replay->l7proto : l7proto,
ctrack_replay ? ctrack_replay->l7proto : l7proto, ssid,
&bCheckDone, &bCheckResult, &bCheckExcluded);
if (ctrack_replay)
{

View File

@@ -113,6 +113,16 @@ bool append_to_list_file(const char *filename, const char *s)
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));
}
void ntop46(const struct sockaddr *sa, char *str, size_t len)
{

View File

@@ -31,6 +31,8 @@ bool load_file_nonempty(const char *filename,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);
void print_sockaddr(const struct sockaddr *sa);
void ntop46(const struct sockaddr *sa, char *str, size_t len);
void ntop46_port(const struct sockaddr *sa, char *str, size_t len);

400
nfq/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

View File

@@ -297,7 +297,7 @@ static int nfq_main(void)
return 1;
}
if (params.droproot && !droproot(params.uid, params.gid, params.gid_count) || !dropcaps())
if (params.droproot && !droproot(params.uid, params.user, params.gid, params.gid_count) || !dropcaps())
goto err;
print_id();
if (params.droproot && !test_list_files())
@@ -306,6 +306,18 @@ static int nfq_main(void)
if (!nfq_init(&h,&qh))
goto err;
#ifdef HAS_FILTER_SSID
if (params.filter_ssid_present)
{
if (!wlan_info_init())
{
DLOG_ERR("cannot initialize wlan info capture\n");
goto err;
}
DLOG("wlan info capture initialized\n");
}
#endif
if (params.daemon) daemonize();
sec_harden();
@@ -330,6 +342,11 @@ static int nfq_main(void)
while ((rd = recv(fd, buf, sizeof(buf), 0)) >= 0)
{
ReloadCheck();
#ifdef HAS_FILTER_SSID
if (params.filter_ssid_present)
if (!wlan_info_get_rate_limited())
DLOG_ERR("cannot get wlan info\n");
#endif
if (rd)
{
int r = nfq_handle_packet(h, (char *)buf, (int)rd);
@@ -346,9 +363,16 @@ static int nfq_main(void)
} while(e==ENOBUFS);
nfq_deinit(&h,&qh);
#ifdef HAS_FILTER_SSID
wlan_info_deinit();
#endif
return 0;
err:
if (Fpid) fclose(Fpid);
nfq_deinit(&h,&qh);
#ifdef HAS_FILTER_SSID
wlan_info_deinit();
#endif
return 1;
}
@@ -423,7 +447,7 @@ static int dvt_main(void)
goto exiterr;
if (params.droproot && !droproot(params.uid, params.gid, params.gid_count))
if (params.droproot && !droproot(params.uid, params.user, params.gid, params.gid_count))
goto exiterr;
print_id();
if (params.droproot && !test_list_files())
@@ -645,34 +669,9 @@ static int win_main(const char *windivert_filter)
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
static void cleanup_args()
{
wordfree(&params.wexp);
}
#endif
static void cleanup_params(void)
{
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
cleanup_args();
#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);
#endif
}
static void exit_clean(int code)
{
cleanup_params();
cleanup_params(&params);
exit(code);
}
@@ -780,27 +779,33 @@ static bool parse_autottl(const char *s, autottl *t, int8_t def_delta, uint8_t d
t->max = def_max;
if (s)
{
max = t->max;
if (*s=='+')
// "-" means disable
if (s[0]=='-' && s[1]==0)
memset(t,0,sizeof(*t));
else
{
neg=false;
s++;
} else if (*s=='-')
s++;
switch (sscanf(s,"%u:%u-%u",&delta,&min,&max))
{
case 3:
if ((delta && !max) || max>255) return false;
t->max=(uint8_t)max;
case 2:
if ((delta && !min) || min>255 || min>max) return false;
t->min=(uint8_t)min;
case 1:
if (delta>127) return false;
t->delta=(int8_t)(neg ? -delta : delta);
break;
default:
return false;
max = t->max;
if (*s=='+')
{
neg=false;
s++;
} else if (*s=='-')
s++;
switch (sscanf(s,"%u:%u-%u",&delta,&min,&max))
{
case 3:
if ((delta && !max) || max>255) return false;
t->max=(uint8_t)max;
case 2:
if ((delta && !min) || min>255 || min>max) return false;
t->min=(uint8_t)min;
case 1:
if (delta>127) return false;
t->delta=(int8_t)(neg ? -delta : delta);
break;
default:
return false;
}
}
}
return true;
@@ -1121,6 +1126,20 @@ static bool parse_fooling(char *opt, unsigned int *fooling_mode)
return true;
}
static bool parse_strlist(char *opt, struct str_list_head *list)
{
char *e,*p = optarg;
while (p)
{
e = strchr(p,',');
if (e) *e++=0;
if (*p && !strlist_add(list, p))
return false;
p = e;
}
return true;
}
static void split_compat(struct desync_profile *dp)
{
if (!dp->split_count)
@@ -1479,7 +1498,11 @@ static void exithelp(void)
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
" @<config_file>|$<config_file>\t\t\t; read file for options. must be the only argument. other options are ignored.\n\n"
#endif
#ifdef __ANDROID__
" --debug=0|1|syslog|android|@<filename>\n"
#else
" --debug=0|1|syslog|@<filename>\n"
#endif
" --version\t\t\t\t\t; print version and exit\n"
" --dry-run\t\t\t\t\t; verify parameters and exit with code 0 if successful\n"
" --comment=any_text\n"
@@ -1522,6 +1545,9 @@ static void exithelp(void)
" --filter-tcp=[~]port1[-port2]|*\t\t; TCP port filter. ~ means negation. setting tcp and not setting udp filter denies udp. comma separated list allowed.\n"
" --filter-udp=[~]port1[-port2]|*\t\t; UDP port filter. ~ means negation. setting udp and not setting tcp filter denies tcp. comma separated list allowed.\n"
" --filter-l7=[http|tls|quic|wireguard|dht|discord|stun|unknown] ; L6-L7 protocol filter. multiple comma separated values allowed.\n"
#ifdef HAS_FILTER_SSID
" --filter-ssid=ssid1[,ssid2,ssid3,...]\t\t; per profile wifi SSID filter\n"
#endif
" --ipset=<filename>\t\t\t\t; ipset include filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)\n"
" --ipset-ip=<ip_list>\t\t\t\t; comma separated fixed subnet list\n"
" --ipset-exclude=<filename>\t\t\t; ipset exclude filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)\n"
@@ -1543,16 +1569,16 @@ static void exithelp(void)
" --synack-split=[syn|synack|acksyn]\t\t; perform TCP split handshake : send SYN only, SYN+ACK or ACK+SYN\n"
" --orig-ttl=<int>\t\t\t\t; set TTL for original packets\n"
" --orig-ttl6=<int>\t\t\t\t; set ipv6 hop limit for original packets. by default ttl value is used\n"
" --orig-autottl=[<delta>[:<min>[-<max>]]]\t; auto ttl mode for both ipv4 and ipv6. default: +%d:%u-%u\n"
" --orig-autottl6=[<delta>[:<min>[-<max>]]]\t; overrides --orig-autottl for ipv6 only\n"
" --orig-autottl=[<delta>[:<min>[-<max>]]|-]\t; auto ttl mode for both ipv4 and ipv6. default: +%d:%u-%u\n"
" --orig-autottl6=[<delta>[:<min>[-<max>]]|-]\t; overrides --orig-autottl for ipv6 only\n"
" --orig-mod-start=[n|d|s]N\t\t\t; apply orig TTL mod to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N\n"
" --orig-mod-cutoff=[n|d|s]N\t\t\t; apply orig TTL mod to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n"
" --dup=<int>\t\t\t\t\t; duplicate original packets. send N dups before original.\n"
" --dup-replace=[0|1]\t\t\t\t; 1 or no argument means do not send original, only dups\n"
" --dup-ttl=<int>\t\t\t\t; set TTL for dups\n"
" --dup-ttl6=<int>\t\t\t\t; set ipv6 hop limit for dups. by default ttl value is used\n"
" --dup-autottl=[<delta>[:<min>[-<max>]]]\t; auto ttl mode for both ipv4 and ipv6. default: %d:%u-%u\n"
" --dup-autottl6=[<delta>[:<min>[-<max>]]]\t; overrides --dup-autottl for ipv6 only\n"
" --dup-autottl=[<delta>[:<min>[-<max>]]|-]\t; auto ttl mode for both ipv4 and ipv6. default: %d:%u-%u\n"
" --dup-autottl6=[<delta>[:<min>[-<max>]]|-]\t; overrides --dup-autottl for ipv6 only\n"
" --dup-fooling=<mode>[,<mode>]\t\t\t; can use multiple comma separated values. modes : none md5sig badseq badsum datanoack hopbyhop hopbyhop2\n"
" --dup-badseq-increment=<int|0xHEX>\t\t; badseq fooling seq signed increment for dup. default %d\n"
" --dup-badack-increment=<int|0xHEX>\t\t; badseq fooling ackseq signed increment for dup. default %d\n"
@@ -1573,8 +1599,8 @@ static void exithelp(void)
#endif
" --dpi-desync-ttl=<int>\t\t\t\t; set ttl for fakes packets\n"
" --dpi-desync-ttl6=<int>\t\t\t; set ipv6 hop limit for fake packet. by default --dpi-desync-ttl value is used.\n"
" --dpi-desync-autottl=[<delta>[:<min>[-<max>]]]\t; auto ttl mode for both ipv4 and ipv6. default: %d:%u-%u\n"
" --dpi-desync-autottl6=[<delta>[:<min>[-<max>]]] ; overrides --dpi-desync-autottl for ipv6 only\n"
" --dpi-desync-autottl=[<delta>[:<min>[-<max>]]|-] ; auto ttl mode for both ipv4 and ipv6. default: %d:%u-%u\n"
" --dpi-desync-autottl6=[<delta>[:<min>[-<max>]]|-] ; overrides --dpi-desync-autottl for ipv6 only\n"
" --dpi-desync-fooling=<mode>[,<mode>]\t\t; can use multiple comma separated values. modes : none md5sig badseq badsum datanoack hopbyhop hopbyhop2\n"
" --dpi-desync-repeats=<N>\t\t\t; send every desync packet N times\n"
" --dpi-desync-skip-nosni=0|1\t\t\t; 1(default)=do not act on ClientHello without SNI\n"
@@ -1624,7 +1650,7 @@ static void exithelp(void)
}
static void exithelp_clean(void)
{
cleanup_params();
cleanup_params(&params);
exithelp();
}
@@ -1669,7 +1695,8 @@ void check_dp(const struct desync_profile *dp)
DLOG_CONDUP("WARNING !!! it's completely ok if connbytes or payload based ip/nf tables limiter is applied. Make sure it exists.\n");
#else
DLOG_CONDUP("WARNING !!! possible TRASH FLOOD configuration detected in profile %d\n", dp->n);
DLOG_CONDUP("WARNING !!! it's highly recommended to use --dpi-desync-cutoff limiter or fakes will be sent on every processed packet\n");
DLOG_CONDUP("WARNING !!! in profile %d you are using --dpi-desync-any-protocol without --dpi-desync-cutoff or --dup without --dup-cutoff\n", dp->n);
DLOG_CONDUP("WARNING !!! fakes or dups will be sent on every processed packet\n");
DLOG_CONDUP("WARNING !!! make sure it's really what you want\n");
#ifdef __CYGWIN__
DLOG_CONDUP("WARNING !!! in most cases this is acceptable only with custom payload based windivert filter (--wf-raw)\n");
@@ -1681,10 +1708,18 @@ void check_dp(const struct desync_profile *dp)
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#if defined(ZAPRET_GH_VER) || defined (ZAPRET_GH_HASH)
#ifdef __ANDROID__
#define PRINT_VER printf("github android version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH))
#else
#define PRINT_VER printf("github version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH))
#endif
#else
#ifdef __ANDROID__
#define PRINT_VER printf("self-built android version %s %s\n\n", __DATE__, __TIME__)
#else
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
#endif
enum opt_indices {
IDX_DEBUG,
@@ -1786,6 +1821,9 @@ enum opt_indices {
IDX_FILTER_TCP,
IDX_FILTER_UDP,
IDX_FILTER_L7,
#ifdef HAS_FILTER_SSID
IDX_FILTER_SSID,
#endif
IDX_IPSET,
IDX_IPSET_IP,
IDX_IPSET_EXCLUDE,
@@ -1907,6 +1945,9 @@ static const struct option long_options[] = {
[IDX_FILTER_TCP] = {"filter-tcp", required_argument, 0, 0},
[IDX_FILTER_UDP] = {"filter-udp", required_argument, 0, 0},
[IDX_FILTER_L7] = {"filter-l7", required_argument, 0, 0},
#ifdef HAS_FILTER_SSID
[IDX_FILTER_SSID] = {"filter-ssid", required_argument, 0, 0},
#endif
[IDX_IPSET] = {"ipset", required_argument, 0, 0},
[IDX_IPSET_IP] = {"ipset-ip", required_argument, 0, 0},
[IDX_IPSET_EXCLUDE] = {"ipset-exclude", required_argument, 0, 0},
@@ -2042,10 +2083,22 @@ int main(int argc, char **argv)
params.debug_target = LOG_TARGET_SYSLOG;
openlog(progname,LOG_PID,LOG_USER);
}
#ifdef __ANDROID__
else if (!strcmp(optarg,"android"))
{
if (!params.debug) params.debug = 1;
params.debug_target = LOG_TARGET_ANDROID;
}
#endif
else if (optarg[0]>='0' && optarg[0]<='1')
{
params.debug = atoi(optarg);
params.debug_target = LOG_TARGET_CONSOLE;
}
else
{
params.debug = !!atoi(optarg);
params.debug_target = LOG_TARGET_CONSOLE;
fprintf(stderr, "invalid debug mode : %s\n", optarg);
exit_clean(1);
}
}
else
@@ -2093,6 +2146,7 @@ int main(int argc, char **argv)
#ifndef __CYGWIN__
case IDX_USER:
{
free(params.user); params.user=NULL;
struct passwd *pwd = getpwnam(optarg);
if (!pwd)
{
@@ -2100,27 +2154,18 @@ int main(int argc, char **argv)
exit_clean(1);
}
params.uid = pwd->pw_uid;
params.gid_count=MAX_GIDS;
#ifdef __APPLE__
// silence warning
if (getgrouplist(optarg,pwd->pw_gid,(int*)params.gid,&params.gid_count)<0)
#else
if (getgrouplist(optarg,pwd->pw_gid,params.gid,&params.gid_count)<0)
#endif
params.gid[0]=pwd->pw_gid;
params.gid_count=1;
if (!(params.user=strdup(optarg)))
{
DLOG_ERR("getgrouplist failed. too much groups ?\n");
DLOG_ERR("strdup: out of memory\n");
exit_clean(1);
}
if (!params.gid_count)
{
params.gid[0] = pwd->pw_gid;
params.gid_count = 1;
}
params.droproot = true;
break;
}
case IDX_UID:
params.droproot = true;
free(params.user); params.user=NULL;
if (!parse_uid(optarg,&params.uid,params.gid,&params.gid_count,MAX_GIDS))
{
DLOG_ERR("--uid should be : uid[:gid,gid,...]\n");
@@ -2131,6 +2176,7 @@ int main(int argc, char **argv)
params.gid[0] = 0x7FFFFFFF;
params.gid_count = 1;
}
params.droproot = true;
break;
#endif
case IDX_WSIZE:
@@ -2779,6 +2825,16 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
#ifdef HAS_FILTER_SSID
case IDX_FILTER_SSID:
if (!parse_strlist(optarg,&dp->filter_ssid))
{
DLOG_ERR("strlist_add failed\n");
exit_clean(1);
}
params.filter_ssid_present = true;
break;
#endif
case IDX_IPSET:
if (bSkip) break;
if (!RegisterIpset(dp, false, optarg))
@@ -2883,38 +2939,18 @@ int main(int argc, char **argv)
break;
case IDX_SSID_FILTER:
hash_ssid_filter=hash_jen(optarg,strlen(optarg));
if (!parse_strlist(optarg,&params.ssid_filter))
{
char *e,*p = optarg;
while (p)
{
e = strchr(p,',');
if (e) *e++=0;
if (*p && !strlist_add(&params.ssid_filter, p))
{
DLOG_ERR("strlist_add failed\n");
exit_clean(1);
}
p = e;
}
DLOG_ERR("strlist_add failed\n");
exit_clean(1);
}
break;
case IDX_NLM_FILTER:
hash_nlm_filter=hash_jen(optarg,strlen(optarg));
if (!parse_strlist(optarg,&params.nlm_filter))
{
char *e,*p = optarg;
while (p)
{
e = strchr(p,',');
if (e) *e++=0;
if (*p && !strlist_add(&params.nlm_filter, p))
{
DLOG_ERR("strlist_add failed\n");
exit_clean(1);
}
p = e;
}
DLOG_ERR("strlist_add failed\n");
exit_clean(1);
}
break;
case IDX_NLM_LIST:
@@ -2939,7 +2975,7 @@ int main(int argc, char **argv)
// do not need args from file anymore
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
cleanup_args();
cleanup_args(&params);
#endif
argv=NULL; argc=0;
@@ -3079,6 +3115,20 @@ int main(int argc, char **argv)
if (bDry)
{
#ifndef __CYGWIN__
if (params.droproot)
{
if (!droproot(params.uid,params.user,params.gid,params.gid_count))
exit_clean(1);
#ifdef __linux__
if (!dropcaps())
exit_clean(1);
#endif
print_id();
if (!test_list_files())
exit_clean(1);
}
#endif
DLOG_CONDUP("command line parameters verified\n");
exit_clean(0);
}
@@ -3103,7 +3153,7 @@ int main(int argc, char **argv)
#endif
ex:
rawsend_cleanup();
cleanup_params();
cleanup_params(&params);
#ifdef __CYGWIN__
if (hMutexArg)
{

View File

@@ -2,6 +2,10 @@
#include <stdbool.h>
#ifdef __linux__
#define HAS_FILTER_SSID 1
#endif
#ifdef __CYGWIN__
extern bool bQuit;
#endif

View File

@@ -3,6 +3,9 @@
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#ifdef __ANDROID__
#include <android/log.h>
#endif
#include "pools.h"
#include "desync.h"
@@ -40,18 +43,47 @@ int DLOG_FILENAME(const char *filename, const char *format, va_list args)
return r;
}
static char syslog_buf[1024];
static size_t syslog_buf_sz=0;
static void syslog_buffered(int priority, const char *format, va_list args)
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)
{
if (vsnprintf(syslog_buf+syslog_buf_sz,sizeof(syslog_buf)-syslog_buf_sz,format,args)>0)
syslog(priority,"%s",log_buf);
}
#ifdef __ANDROID__
static enum android_LogPriority syslog_priority_to_android(int priority)
{
enum android_LogPriority ap;
switch(priority)
{
syslog_buf_sz=strlen(syslog_buf);
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 (syslog_buf_sz>=(sizeof(syslog_buf)-1) || (syslog_buf_sz && syslog_buf[syslog_buf_sz-1]=='\n'))
if (log_buf_sz>=(sizeof(log_buf)-1) || (log_buf_sz && log_buf[log_buf_sz-1]=='\n'))
{
syslog(priority,"%s",syslog_buf);
syslog_buf_sz = 0;
log_function(syslog_priority,log_buf);
log_buf_sz = 0;
}
}
}
@@ -79,9 +111,16 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list
break;
case LOG_TARGET_SYSLOG:
// skip newlines
syslog_buffered(syslog_priority,format,args);
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;
}
@@ -263,6 +302,9 @@ static void dp_clear_dynamic(struct desync_profile *dp)
ipset_collection_destroy(&dp->ips_collection_exclude);
port_filters_destroy(&dp->pf_tcp);
port_filters_destroy(&dp->pf_udp);
#ifdef HAS_FILTER_SSID
strlist_destroy(&dp->filter_ssid);
#endif
HostFailPoolDestroy(&dp->hostlist_auto_fail_counters);
struct blob_collection_head **fake,*fakes[] = {&dp->fake_http, &dp->fake_tls, &dp->fake_unknown, &dp->fake_unknown_udp, &dp->fake_quic, &dp->fake_wg, &dp->fake_dht, &dp->fake_discord, &dp->fake_stun, NULL};
for(fake=fakes;*fake;fake++) blob_collection_destroy(*fake);
@@ -303,3 +345,32 @@ bool dp_list_need_all_out(struct desync_profile_list_head *head)
return true;
return false;
}
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
void cleanup_args(struct params_s *params)
{
wordfree(&params->wexp);
}
#endif
void cleanup_params(struct params_s *params)
{
#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);
#else
free(params->user); params->user=NULL;
#endif
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include "nfqws.h"
#include "pools.h"
#include "conntrack.h"
#include "desync.h"
@@ -65,7 +66,7 @@
#define MAX_GIDS 64
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG, LOG_TARGET_ANDROID };
struct fake_tls_mod_cache
{
@@ -135,6 +136,13 @@ struct desync_profile
struct port_filters_head pf_tcp,pf_udp;
uint32_t filter_l7; // L7_PROTO_* bits
#ifdef HAS_FILTER_SSID
// per profile ssid filter
// annot use global filter because it's not possible to bind multiple instances to a single queue
// it's possible to run multiple winws instances on the same windivert filter, but it's not the case for linux
struct str_list_head filter_ssid;
#endif
// list of pointers to ipsets
struct ipset_collection_head ips_collection, ips_collection_exclude;
@@ -192,6 +200,7 @@ struct params_s
struct str_list_head ssid_filter,nlm_filter;
#else
bool droproot;
char *user;
uid_t uid;
gid_t gid[MAX_GIDS];
int gid_count;
@@ -209,13 +218,22 @@ struct params_s
t_conntrack conntrack;
bool ctrack_disable;
bool autottl_present,cache_hostname;
bool autottl_present;
#ifdef HAS_FILTER_SSID
bool filter_ssid_present;
#endif
bool cache_hostname;
unsigned int ipcache_lifetime;
ip_cache ipcache;
};
extern struct params_s params;
extern const char *progname;
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
void cleanup_args(struct params_s *params);
#endif
void cleanup_params(struct params_s *params);
int DLOG(const char *format, ...);
int DLOG_ERR(const char *format, ...);

View File

@@ -159,6 +159,19 @@ void strlist_destroy(struct str_list_head *head)
strlist_entry_destroy(entry);
}
}
bool strlist_search(const struct str_list_head *head, const char *str)
{
struct str_list *entry;
if (str)
{
LIST_FOREACH(entry, head, next)
{
if (!strcmp(entry->str, str))
return true;
}
}
return false;
}
@@ -260,121 +273,146 @@ bool hostlist_collection_is_empty(const struct hostlist_collection_head *head)
}
void ipset4Destroy(ipset4 **ipset)
static int kavl_bit_cmp(const struct kavl_bit_elem *p, const struct kavl_bit_elem *q)
{
ipset4 *elem, *tmp;
HASH_ITER(hh, *ipset, elem, tmp)
unsigned int bitlen = q->bitlen < p->bitlen ? q->bitlen : p->bitlen;
unsigned int df = bitlen & 7, bytes = bitlen >> 3;
int cmp = memcmp(p->data, q->data, bytes);
if (cmp || !df) return cmp;
uint8_t c1 = p->data[bytes] >> (8 - df);
uint8_t c2 = q->data[bytes] >> (8 - df);
return c1<c2 ? -1 : c1==c2 ? 0 : 1;
}
KAVL_INIT(kavl_bit, struct kavl_bit_elem, head, kavl_bit_cmp)
static void kavl_bit_destroy_elem(struct kavl_bit_elem *e)
{
if (e)
{
HASH_DEL(*ipset, elem);
free(elem);
free(e->data);
free(e);
}
}
bool ipset4Check(ipset4 *ipset, const struct in_addr *a, uint8_t preflen)
void kavl_bit_delete(struct kavl_bit_elem **hdr, const void *data, unsigned int bitlen)
{
uint32_t ip = ntohl(a->s_addr);
struct cidr4 cidr;
ipset4 *ips_found;
// zero alignment bytes
memset(&cidr,0,sizeof(cidr));
cidr.preflen = preflen+1;
do
{
cidr.preflen--;
cidr.addr.s_addr = htonl(ip & mask_from_preflen(cidr.preflen));
HASH_FIND(hh, ipset, &cidr, sizeof(cidr), ips_found);
if (ips_found) return true;
} while(cidr.preflen);
return false;
struct kavl_bit_elem temp = {
.bitlen = bitlen, .data = (uint8_t*)data
};
kavl_bit_destroy_elem(kavl_erase(kavl_bit, hdr, &temp, 0));
}
bool ipset4Add(ipset4 **ipset, const struct in_addr *a, uint8_t preflen)
void kavl_bit_destroy(struct kavl_bit_elem **hdr)
{
while (*hdr)
{
struct kavl_bit_elem *e = kavl_erase_first(kavl_bit, hdr);
if (!e) break;
kavl_bit_destroy_elem(e);
}
free(*hdr);
}
struct kavl_bit_elem *kavl_bit_add(struct kavl_bit_elem **hdr, void *data, unsigned int bitlen, size_t struct_size)
{
if (!struct_size) struct_size=sizeof(struct kavl_bit_elem);
struct kavl_bit_elem *v, *e = calloc(1, struct_size);
if (!e) return 0;
e->bitlen = bitlen;
e->data = data;
v = kavl_insert(kavl_bit, hdr, e, 0);
while (e != v && e->bitlen < v->bitlen)
{
kavl_bit_delete(hdr, v->data, v->bitlen);
v = kavl_insert(kavl_bit, hdr, e, 0);
}
if (e != v) kavl_bit_destroy_elem(e);
return v;
}
struct kavl_bit_elem *kavl_bit_get(const struct kavl_bit_elem *hdr, const void *data, unsigned int bitlen)
{
struct kavl_bit_elem temp = {
.bitlen = bitlen, .data = (uint8_t*)data
};
return kavl_find(kavl_bit, hdr, &temp, 0);
}
static bool ipset_kavl_add(struct kavl_bit_elem **ipset, const void *a, uint8_t preflen)
{
uint8_t bytelen = (preflen+7)>>3;
uint8_t *abuf = malloc(bytelen);
if (!abuf) return false;
memcpy(abuf,a,bytelen);
if (!kavl_bit_add(ipset,abuf,preflen,0))
{
free(abuf);
return false;
}
return true;
}
bool ipset4Check(const struct kavl_bit_elem *ipset, const struct in_addr *a, uint8_t preflen)
{
return !!kavl_bit_get(ipset,a,preflen);
}
bool ipset4Add(struct kavl_bit_elem **ipset, const struct in_addr *a, uint8_t preflen)
{
if (preflen>32) return false;
// avoid dups
if (ipset4Check(*ipset, a, preflen)) return true; // already included
struct ipset4 *entry = calloc(1,sizeof(ipset4));
if (!entry) return false;
entry->cidr.addr.s_addr = htonl(ntohl(a->s_addr) & mask_from_preflen(preflen));
entry->cidr.preflen = preflen;
oom = false;
HASH_ADD(hh, *ipset, cidr, sizeof(entry->cidr), entry);
if (oom) { free(entry); return false; }
return true;
return ipset_kavl_add(ipset,a,preflen);
}
void ipset4Print(ipset4 *ipset)
void ipset4Print(struct kavl_bit_elem *ipset)
{
ipset4 *ips, *tmp;
HASH_ITER(hh, ipset , ips, tmp)
{
print_cidr4(&ips->cidr);
printf("\n");
}
}
if (!ipset) return;
void ipset6Destroy(ipset6 **ipset)
{
ipset6 *elem, *tmp;
HASH_ITER(hh, *ipset, elem, tmp)
{
HASH_DEL(*ipset, elem);
free(elem);
}
}
bool ipset6Check(ipset6 *ipset, const struct in6_addr *a, uint8_t preflen)
{
struct cidr6 cidr;
ipset6 *ips_found;
// zero alignment bytes
memset(&cidr,0,sizeof(cidr));
cidr.preflen = preflen+1;
struct cidr4 c;
const struct kavl_bit_elem *elem;
kavl_itr_t(kavl_bit) itr;
kavl_itr_first(kavl_bit, ipset, &itr);
do
{
cidr.preflen--;
ip6_and(a, mask_from_preflen6(cidr.preflen), &cidr.addr);
HASH_FIND(hh, ipset, &cidr, sizeof(cidr), ips_found);
if (ips_found) return true;
} while(cidr.preflen);
return false;
}
bool ipset6Add(ipset6 **ipset, const struct in6_addr *a, uint8_t preflen)
{
if (preflen>128) return false;
// avoid dups
if (ipset6Check(*ipset, a, preflen)) return true; // already included
struct ipset6 *entry = calloc(1,sizeof(ipset6));
if (!entry) return false;
ip6_and(a, mask_from_preflen6(preflen), &entry->cidr.addr);
entry->cidr.preflen = preflen;
oom = false;
HASH_ADD(hh, *ipset, cidr, sizeof(entry->cidr), entry);
if (oom) { free(entry); return false; }
return true;
}
void ipset6Print(ipset6 *ipset)
{
ipset6 *ips, *tmp;
HASH_ITER(hh, ipset , ips, tmp)
{
print_cidr6(&ips->cidr);
elem = kavl_at(&itr);
c.preflen = elem->bitlen;
expand_bits(&c.addr, elem->data, elem->bitlen, sizeof(c.addr));
print_cidr4(&c);
printf("\n");
}
while (kavl_itr_next(kavl_bit, &itr));
}
bool ipset6Check(const struct kavl_bit_elem *ipset, const struct in6_addr *a, uint8_t preflen)
{
return !!kavl_bit_get(ipset,a,preflen);
}
bool ipset6Add(struct kavl_bit_elem **ipset, const struct in6_addr *a, uint8_t preflen)
{
if (preflen>128) return false;
return ipset_kavl_add(ipset,a,preflen);
}
void ipset6Print(struct kavl_bit_elem *ipset)
{
if (!ipset) return;
struct cidr6 c;
const struct kavl_bit_elem *elem;
kavl_itr_t(kavl_bit) itr;
kavl_itr_first(kavl_bit, ipset, &itr);
do
{
elem = kavl_at(&itr);
c.preflen = elem->bitlen;
expand_bits(&c.addr, elem->data, elem->bitlen, sizeof(c.addr));
print_cidr6(&c);
printf("\n");
}
while (kavl_itr_next(kavl_bit, &itr));
}
void ipsetDestroy(ipset *ipset)
{
ipset4Destroy(&ipset->ips4);
ipset6Destroy(&ipset->ips6);
kavl_bit_destroy(&ipset->ips4);
kavl_bit_destroy(&ipset->ips6);
}
void ipsetPrint(ipset *ipset)
{

View File

@@ -13,6 +13,8 @@
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
#include "kavl.h"
#define HOSTLIST_POOL_FLAG_STRICT_MATCH 1
typedef struct hostlist_pool {
@@ -32,6 +34,11 @@ struct str_list {
};
LIST_HEAD(str_list_head, str_list);
bool strlist_add(struct str_list_head *head, const char *filename);
void strlist_destroy(struct str_list_head *head);
bool strlist_search(const struct str_list_head *head, const char *str);
typedef struct hostfail_pool {
char *str; /* key */
int counter; /* value */
@@ -47,10 +54,6 @@ void HostFailPoolPurge(hostfail_pool **pp);
void HostFailPoolPurgeRateLimited(hostfail_pool **pp);
void HostFailPoolDump(hostfail_pool *p);
bool strlist_add(struct str_list_head *head, const char *filename);
void strlist_destroy(struct str_list_head *head);
struct hostlist_file {
char *filename;
@@ -76,39 +79,40 @@ struct hostlist_item *hostlist_collection_search(struct hostlist_collection_head
bool hostlist_collection_is_empty(const struct hostlist_collection_head *head);
typedef struct ipset4 {
struct cidr4 cidr; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} ipset4;
typedef struct ipset6 {
struct cidr6 cidr; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} ipset6;
struct kavl_bit_elem
{
unsigned int bitlen;
uint8_t *data;
KAVL_HEAD(struct kavl_bit_elem) head;
};
struct kavl_bit_elem *kavl_bit_get(const struct kavl_bit_elem *hdr, const void *data, unsigned int bitlen);
struct kavl_bit_elem *kavl_bit_add(struct kavl_bit_elem **hdr, void *data, unsigned int bitlen, size_t struct_size);
void kavl_bit_delete(struct kavl_bit_elem **hdr, const void *data, unsigned int bitlen);
void kavl_bit_destroy(struct kavl_bit_elem **hdr);
// combined ipset ipv4 and ipv6
typedef struct ipset {
ipset4 *ips4;
ipset6 *ips6;
struct kavl_bit_elem *ips4,*ips6;
} ipset;
#define IPSET_EMPTY(ips) (!(ips)->ips4 && !(ips)->ips6)
void ipset4Destroy(ipset4 **ipset);
bool ipset4Add(ipset4 **ipset, const struct in_addr *a, uint8_t preflen);
static inline bool ipset4AddCidr(ipset4 **ipset, const struct cidr4 *cidr)
bool ipset4Add(struct kavl_bit_elem **ipset, const struct in_addr *a, uint8_t preflen);
static inline bool ipset4AddCidr(struct kavl_bit_elem **ipset, const struct cidr4 *cidr)
{
return ipset4Add(ipset,&cidr->addr,cidr->preflen);
}
bool ipset4Check(ipset4 *ipset, const struct in_addr *a, uint8_t preflen);
void ipset4Print(ipset4 *ipset);
bool ipset4Check(const struct kavl_bit_elem *ipset, const struct in_addr *a, uint8_t preflen);
void ipset4Print(struct kavl_bit_elem *ipset);
void ipset6Destroy(ipset6 **ipset);
bool ipset6Add(ipset6 **ipset, const struct in6_addr *a, uint8_t preflen);
static inline bool ipset6AddCidr(ipset6 **ipset, const struct cidr6 *cidr)
bool ipset6Add(struct kavl_bit_elem **ipset, const struct in6_addr *a, uint8_t preflen);
static inline bool ipset6AddCidr(struct kavl_bit_elem **ipset, const struct cidr6 *cidr)
{
return ipset6Add(ipset,&cidr->addr,cidr->preflen);
}
bool ipset6Check(ipset6 *ipset, const struct in6_addr *a, uint8_t preflen);
void ipset6Print(ipset6 *ipset);
bool ipset6Check(const struct kavl_bit_elem *ipset, const struct in6_addr *a, uint8_t preflen);
void ipset6Print(struct kavl_bit_elem *ipset);
void ipsetDestroy(ipset *ipset);
void ipsetPrint(ipset *ipset);

View File

@@ -295,7 +295,7 @@ bool can_drop_root(void)
#endif
}
bool droproot(uid_t uid, gid_t *gid, int gid_count)
bool droproot(uid_t uid, const char *user, const gid_t *gid, int gid_count)
{
if (gid_count<1)
{
@@ -309,11 +309,23 @@ bool droproot(uid_t uid, gid_t *gid, int gid_count)
return false;
}
#endif
// drop all SGIDs
if (setgroups(gid_count,gid))
if (user)
{
DLOG_PERROR("setgroups");
return false;
// macos has strange supp gid handling. they cache only 16 groups and fail setgroups if more than 16 gids specified.
// better to leave it to the os
if (initgroups(user,gid[0]))
{
DLOG_PERROR("initgroups");
return false;
}
}
else
{
if (setgroups(gid_count,gid))
{
DLOG_PERROR("setgroups");
return false;
}
}
if (setgid(gid[0]))
{

View File

@@ -84,7 +84,7 @@ bool dropcaps(void);
#ifndef __CYGWIN__
bool sec_harden(void);
bool can_drop_root(void);
bool droproot(uid_t uid, gid_t *gid, int gid_count);
bool droproot(uid_t uid, const char *user, const gid_t *gid, int gid_count);
void print_id(void);
#endif

View File

@@ -2,6 +2,7 @@ CC ?= gcc
CFLAGS += -std=gnu99 -Os -flto=auto
CFLAGS_SYSTEMD = -DUSE_SYSTEMD
CFLAGS_BSD = -Wno-address-of-packed-member
LDFLAGS_ANDROID = -llog
LIBS = -lz -lpthread
LIBS_SYSTEMD = -lsystemd
LIBS_ANDROID = -lz
@@ -17,7 +18,7 @@ systemd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_SYSTEMD) -o tpws $(SRC_FILES) $(LIBS) $(LIBS_SYSTEMD) $(LDFLAGS)
android: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o tpws $(SRC_FILES_ANDROID) $(LIBS_ANDROID) $(LDFLAGS)
$(CC) -s $(CFLAGS) -o tpws $(SRC_FILES_ANDROID) $(LIBS_ANDROID) $(LDFLAGS) $(LDFLAGS_ANDROID)
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -Iepoll-shim/include -o tpws $(SRC_FILES) epoll-shim/src/*.c $(LIBS) $(LDFLAGS)

View File

@@ -112,6 +112,17 @@ bool append_to_list_file(const char *filename, const char *s)
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));
}
void ntop46(const struct sockaddr *sa, char *str, size_t len)
{
if (!len) return;

View File

@@ -29,6 +29,8 @@ bool str_ends_with(const char *s, const char *suffix);
bool load_file(const char *filename,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);
void ntop46(const struct sockaddr *sa, char *str, size_t len);
void ntop46_port(const struct sockaddr *sa, char *str, size_t len);
void print_sockaddr(const struct sockaddr *sa);

400
tpws/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

View File

@@ -2,6 +2,11 @@
#include <stdarg.h>
#include <syslog.h>
#include <errno.h>
#ifdef __ANDROID__
#include <android/log.h>
#endif
const char *progname = "tpws";
int DLOG_FILE(FILE *F, const char *format, va_list args)
{
@@ -25,18 +30,47 @@ int DLOG_FILENAME(const char *filename, const char *format, va_list args)
return r;
}
static char syslog_buf[1024];
static size_t syslog_buf_sz=0;
static void syslog_buffered(int priority, const char *format, va_list args)
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)
{
if (vsnprintf(syslog_buf+syslog_buf_sz,sizeof(syslog_buf)-syslog_buf_sz,format,args)>0)
syslog(priority,"%s",log_buf);
}
#ifdef __ANDROID__
static enum android_LogPriority syslog_priority_to_android(int priority)
{
enum android_LogPriority ap;
switch(priority)
{
syslog_buf_sz=strlen(syslog_buf);
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 (syslog_buf_sz>=(sizeof(syslog_buf)-1) || (syslog_buf_sz && syslog_buf[syslog_buf_sz-1]=='\n'))
if (log_buf_sz>=(sizeof(log_buf)-1) || (log_buf_sz && log_buf[log_buf_sz-1]=='\n'))
{
syslog(priority,"%s",syslog_buf);
syslog_buf_sz = 0;
log_function(syslog_priority,log_buf);
log_buf_sz = 0;
}
}
}
@@ -64,9 +98,16 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, int lev
break;
case LOG_TARGET_SYSLOG:
// skip newlines
syslog_buffered(syslog_priority,format,args);
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;
}
@@ -217,3 +258,24 @@ void dp_list_destroy(struct desync_profile_list_head *head)
dp_entry_destroy(entry);
}
}
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
void cleanup_args(struct params_s *params)
{
wordfree(&params->wexp);
}
#endif
void cleanup_params(struct params_s *params)
{
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
cleanup_args(params);
#endif
dp_list_destroy(&params->desync_profiles);
hostlist_files_destroy(&params->hostlists);
ipset_files_destroy(&params->ipsets);
ipcacheDestroy(&params->ipcache);
free(params->user); params->user=NULL;
}

View File

@@ -11,6 +11,7 @@
#include <wordexp.h>
#endif
#include "tpws.h"
#include "pools.h"
#include "helpers.h"
@@ -38,7 +39,7 @@ struct bind_s
#define MAX_SPLITS 16
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG, LOG_TARGET_ANDROID };
struct desync_profile
{
@@ -116,8 +117,9 @@ struct params_s
bool fix_seg_avail;
bool no_resolve;
bool skip_nodelay;
bool droproot;
bool daemon;
bool droproot;
char *user;
uid_t uid;
gid_t gid[MAX_GIDS];
int gid_count;
@@ -153,6 +155,11 @@ struct params_s
};
extern struct params_s params;
extern const char *progname;
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
void cleanup_args(struct params_s *params);
#endif
void cleanup_params(struct params_s *params);
int DLOG(const char *format, int level, ...);
int DLOG_CONDUP(const char *format, ...);

View File

@@ -260,121 +260,146 @@ bool hostlist_collection_is_empty(const struct hostlist_collection_head *head)
}
void ipset4Destroy(ipset4 **ipset)
static int kavl_bit_cmp(const struct kavl_bit_elem *p, const struct kavl_bit_elem *q)
{
ipset4 *elem, *tmp;
HASH_ITER(hh, *ipset, elem, tmp)
unsigned int bitlen = q->bitlen < p->bitlen ? q->bitlen : p->bitlen;
unsigned int df = bitlen & 7, bytes = bitlen >> 3;
int cmp = memcmp(p->data, q->data, bytes);
if (cmp || !df) return cmp;
uint8_t c1 = p->data[bytes] >> (8 - df);
uint8_t c2 = q->data[bytes] >> (8 - df);
return c1<c2 ? -1 : c1==c2 ? 0 : 1;
}
KAVL_INIT(kavl_bit, struct kavl_bit_elem, head, kavl_bit_cmp)
static void kavl_bit_destroy_elem(struct kavl_bit_elem *e)
{
if (e)
{
HASH_DEL(*ipset, elem);
free(elem);
free(e->data);
free(e);
}
}
bool ipset4Check(ipset4 *ipset, const struct in_addr *a, uint8_t preflen)
void kavl_bit_delete(struct kavl_bit_elem **hdr, const void *data, unsigned int bitlen)
{
uint32_t ip = ntohl(a->s_addr);
struct cidr4 cidr;
ipset4 *ips_found;
// zero alignment bytes
memset(&cidr,0,sizeof(cidr));
cidr.preflen = preflen+1;
do
{
cidr.preflen--;
cidr.addr.s_addr = htonl(ip & mask_from_preflen(cidr.preflen));
HASH_FIND(hh, ipset, &cidr, sizeof(cidr), ips_found);
if (ips_found) return true;
} while(cidr.preflen);
return false;
struct kavl_bit_elem temp = {
.bitlen = bitlen, .data = (uint8_t*)data
};
kavl_bit_destroy_elem(kavl_erase(kavl_bit, hdr, &temp, 0));
}
bool ipset4Add(ipset4 **ipset, const struct in_addr *a, uint8_t preflen)
void kavl_bit_destroy(struct kavl_bit_elem **hdr)
{
while (*hdr)
{
struct kavl_bit_elem *e = kavl_erase_first(kavl_bit, hdr);
if (!e) break;
kavl_bit_destroy_elem(e);
}
free(*hdr);
}
struct kavl_bit_elem *kavl_bit_add(struct kavl_bit_elem **hdr, void *data, unsigned int bitlen, size_t struct_size)
{
if (!struct_size) struct_size=sizeof(struct kavl_bit_elem);
struct kavl_bit_elem *v, *e = calloc(1, struct_size);
if (!e) return 0;
e->bitlen = bitlen;
e->data = data;
v = kavl_insert(kavl_bit, hdr, e, 0);
while (e != v && e->bitlen < v->bitlen)
{
kavl_bit_delete(hdr, v->data, v->bitlen);
v = kavl_insert(kavl_bit, hdr, e, 0);
}
if (e != v) kavl_bit_destroy_elem(e);
return v;
}
struct kavl_bit_elem *kavl_bit_get(const struct kavl_bit_elem *hdr, const void *data, unsigned int bitlen)
{
struct kavl_bit_elem temp = {
.bitlen = bitlen, .data = (uint8_t*)data
};
return kavl_find(kavl_bit, hdr, &temp, 0);
}
static bool ipset_kavl_add(struct kavl_bit_elem **ipset, const void *a, uint8_t preflen)
{
uint8_t bytelen = (preflen+7)>>3;
uint8_t *abuf = malloc(bytelen);
if (!abuf) return false;
memcpy(abuf,a,bytelen);
if (!kavl_bit_add(ipset,abuf,preflen,0))
{
free(abuf);
return false;
}
return true;
}
bool ipset4Check(const struct kavl_bit_elem *ipset, const struct in_addr *a, uint8_t preflen)
{
return !!kavl_bit_get(ipset,a,preflen);
}
bool ipset4Add(struct kavl_bit_elem **ipset, const struct in_addr *a, uint8_t preflen)
{
if (preflen>32) return false;
// avoid dups
if (ipset4Check(*ipset, a, preflen)) return true; // already included
struct ipset4 *entry = calloc(1,sizeof(ipset4));
if (!entry) return false;
entry->cidr.addr.s_addr = htonl(ntohl(a->s_addr) & mask_from_preflen(preflen));
entry->cidr.preflen = preflen;
oom = false;
HASH_ADD(hh, *ipset, cidr, sizeof(entry->cidr), entry);
if (oom) { free(entry); return false; }
return true;
return ipset_kavl_add(ipset,a,preflen);
}
void ipset4Print(ipset4 *ipset)
void ipset4Print(struct kavl_bit_elem *ipset)
{
ipset4 *ips, *tmp;
HASH_ITER(hh, ipset , ips, tmp)
{
print_cidr4(&ips->cidr);
printf("\n");
}
}
if (!ipset) return;
void ipset6Destroy(ipset6 **ipset)
{
ipset6 *elem, *tmp;
HASH_ITER(hh, *ipset, elem, tmp)
{
HASH_DEL(*ipset, elem);
free(elem);
}
}
bool ipset6Check(ipset6 *ipset, const struct in6_addr *a, uint8_t preflen)
{
struct cidr6 cidr;
ipset6 *ips_found;
// zero alignment bytes
memset(&cidr,0,sizeof(cidr));
cidr.preflen = preflen+1;
struct cidr4 c;
const struct kavl_bit_elem *elem;
kavl_itr_t(kavl_bit) itr;
kavl_itr_first(kavl_bit, ipset, &itr);
do
{
cidr.preflen--;
ip6_and(a, mask_from_preflen6(cidr.preflen), &cidr.addr);
HASH_FIND(hh, ipset, &cidr, sizeof(cidr), ips_found);
if (ips_found) return true;
} while(cidr.preflen);
return false;
}
bool ipset6Add(ipset6 **ipset, const struct in6_addr *a, uint8_t preflen)
{
if (preflen>128) return false;
// avoid dups
if (ipset6Check(*ipset, a, preflen)) return true; // already included
struct ipset6 *entry = calloc(1,sizeof(ipset6));
if (!entry) return false;
ip6_and(a, mask_from_preflen6(preflen), &entry->cidr.addr);
entry->cidr.preflen = preflen;
oom = false;
HASH_ADD(hh, *ipset, cidr, sizeof(entry->cidr), entry);
if (oom) { free(entry); return false; }
return true;
}
void ipset6Print(ipset6 *ipset)
{
ipset6 *ips, *tmp;
HASH_ITER(hh, ipset , ips, tmp)
{
print_cidr6(&ips->cidr);
elem = kavl_at(&itr);
c.preflen = elem->bitlen;
expand_bits(&c.addr, elem->data, elem->bitlen, sizeof(c.addr));
print_cidr4(&c);
printf("\n");
}
while (kavl_itr_next(kavl_bit, &itr));
}
bool ipset6Check(const struct kavl_bit_elem *ipset, const struct in6_addr *a, uint8_t preflen)
{
return !!kavl_bit_get(ipset,a,preflen);
}
bool ipset6Add(struct kavl_bit_elem **ipset, const struct in6_addr *a, uint8_t preflen)
{
if (preflen>128) return false;
return ipset_kavl_add(ipset,a,preflen);
}
void ipset6Print(struct kavl_bit_elem *ipset)
{
if (!ipset) return;
struct cidr6 c;
const struct kavl_bit_elem *elem;
kavl_itr_t(kavl_bit) itr;
kavl_itr_first(kavl_bit, ipset, &itr);
do
{
elem = kavl_at(&itr);
c.preflen = elem->bitlen;
expand_bits(&c.addr, elem->data, elem->bitlen, sizeof(c.addr));
print_cidr6(&c);
printf("\n");
}
while (kavl_itr_next(kavl_bit, &itr));
}
void ipsetDestroy(ipset *ipset)
{
ipset4Destroy(&ipset->ips4);
ipset6Destroy(&ipset->ips6);
kavl_bit_destroy(&ipset->ips4);
kavl_bit_destroy(&ipset->ips6);
}
void ipsetPrint(ipset *ipset)
{
@@ -520,7 +545,7 @@ bool port_filters_deny_if_empty(struct port_filters_head *head)
}
struct blob_item *blob_collection_add(struct blob_collection_head *head)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));

View File

@@ -13,6 +13,8 @@
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
#include "kavl.h"
#define HOSTLIST_POOL_FLAG_STRICT_MATCH 1
typedef struct hostlist_pool {
@@ -76,39 +78,40 @@ struct hostlist_item *hostlist_collection_search(struct hostlist_collection_head
bool hostlist_collection_is_empty(const struct hostlist_collection_head *head);
typedef struct ipset4 {
struct cidr4 cidr; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} ipset4;
typedef struct ipset6 {
struct cidr6 cidr; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} ipset6;
struct kavl_bit_elem
{
unsigned int bitlen;
uint8_t *data;
KAVL_HEAD(struct kavl_bit_elem) head;
};
struct kavl_bit_elem *kavl_bit_get(const struct kavl_bit_elem *hdr, const void *data, unsigned int bitlen);
struct kavl_bit_elem *kavl_bit_add(struct kavl_bit_elem **hdr, void *data, unsigned int bitlen, size_t struct_size);
void kavl_bit_delete(struct kavl_bit_elem **hdr, const void *data, unsigned int bitlen);
void kavl_bit_destroy(struct kavl_bit_elem **hdr);
// combined ipset ipv4 and ipv6
typedef struct ipset {
ipset4 *ips4;
ipset6 *ips6;
struct kavl_bit_elem *ips4,*ips6;
} ipset;
#define IPSET_EMPTY(ips) (!(ips)->ips4 && !(ips)->ips6)
void ipset4Destroy(ipset4 **ipset);
bool ipset4Add(ipset4 **ipset, const struct in_addr *a, uint8_t preflen);
static inline bool ipset4AddCidr(ipset4 **ipset, const struct cidr4 *cidr)
bool ipset4Add(struct kavl_bit_elem **ipset, const struct in_addr *a, uint8_t preflen);
static inline bool ipset4AddCidr(struct kavl_bit_elem **ipset, const struct cidr4 *cidr)
{
return ipset4Add(ipset,&cidr->addr,cidr->preflen);
}
bool ipset4Check(ipset4 *ipset, const struct in_addr *a, uint8_t preflen);
void ipset4Print(ipset4 *ipset);
bool ipset4Check(const struct kavl_bit_elem *ipset, const struct in_addr *a, uint8_t preflen);
void ipset4Print(struct kavl_bit_elem *ipset);
void ipset6Destroy(ipset6 **ipset);
bool ipset6Add(ipset6 **ipset, const struct in6_addr *a, uint8_t preflen);
static inline bool ipset6AddCidr(ipset6 **ipset, const struct cidr6 *cidr)
bool ipset6Add(struct kavl_bit_elem **ipset, const struct in6_addr *a, uint8_t preflen);
static inline bool ipset6AddCidr(struct kavl_bit_elem **ipset, const struct cidr6 *cidr)
{
return ipset6Add(ipset,&cidr->addr,cidr->preflen);
}
bool ipset6Check(ipset6 *ipset, const struct in6_addr *a, uint8_t preflen);
void ipset6Print(ipset6 *ipset);
bool ipset6Check(const struct kavl_bit_elem *ipset, const struct in6_addr *a, uint8_t preflen);
void ipset6Print(struct kavl_bit_elem *ipset);
void ipsetDestroy(ipset *ipset);
void ipsetPrint(ipset *ipset);

View File

@@ -269,7 +269,7 @@ bool can_drop_root(void)
#endif
}
bool droproot(uid_t uid, gid_t *gid, int gid_count)
bool droproot(uid_t uid, const char *user, const gid_t *gid, int gid_count)
{
if (gid_count<1)
{
@@ -283,11 +283,23 @@ bool droproot(uid_t uid, gid_t *gid, int gid_count)
return false;
}
#endif
// drop all SGIDs
if (setgroups(gid_count,gid))
if (user)
{
DLOG_PERROR("setgroups");
return false;
// macos has strange supp gid handling. they cache only 16 groups and fail setgroups if more than 16 gids specified.
// better to leave it to the os
if (initgroups(user,gid[0]))
{
DLOG_PERROR("initgroups");
return false;
}
}
else
{
if (setgroups(gid_count,gid))
{
DLOG_PERROR("setgroups");
return false;
}
}
if (setgid(gid[0]))
{

View File

@@ -84,7 +84,7 @@ bool dropcaps(void);
bool sec_harden(void);
bool can_drop_root();
bool droproot(uid_t uid, gid_t *gid, int gid_count);
bool droproot(uid_t uid, const char *user, const gid_t *gid, int gid_count);
void print_id(void);
void daemonize(void);
bool writepid(const char *filename);

View File

@@ -224,7 +224,11 @@ static void exithelp(void)
#endif
" --ipcache-lifetime=<int>\t\t; time in seconds to keep cached domain name (default %u). 0 = no expiration\n"
" --ipcache-hostname=[0|1]\t\t; 1 or no argument enables ip->hostname caching\n"
#ifdef __ANDROID__
" --debug=0|1|2|syslog|android|@<filename> ; 1 and 2 means log to console and set debug level. for other targets use --debug-level.\n"
#else
" --debug=0|1|2|syslog|@<filename>\t; 1 and 2 means log to console and set debug level. for other targets use --debug-level.\n"
#endif
" --debug-level=0|1|2\t\t\t; specify debug level\n"
" --dry-run\t\t\t\t; verify parameters and exit with code 0 if successful\n"
" --version\t\t\t\t; print version and exit\n"
@@ -286,32 +290,14 @@ static void exithelp(void)
);
exit(1);
}
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
static void cleanup_args()
{
wordfree(&params.wexp);
}
#endif
static void cleanup_params(void)
{
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
cleanup_args();
#endif
dp_list_destroy(&params.desync_profiles);
hostlist_files_destroy(&params.hostlists);
ipset_files_destroy(&params.ipsets);
ipcacheDestroy(&params.ipcache);
}
static void exithelp_clean(void)
{
cleanup_params();
cleanup_params(&params);
exithelp();
}
static void exit_clean(int code)
{
cleanup_params();
cleanup_params(&params);
exit(code);
}
static void nextbind_clean(void)
@@ -978,6 +964,7 @@ void parse_params(int argc, char *argv[])
break;
case IDX_USER:
{
free(params.user); params.user=NULL;
struct passwd *pwd = getpwnam(optarg);
if (!pwd)
{
@@ -985,27 +972,18 @@ void parse_params(int argc, char *argv[])
exit_clean(1);
}
params.uid = pwd->pw_uid;
params.gid_count=MAX_GIDS;
#ifdef __APPLE__
// silence warning
if (getgrouplist(optarg,pwd->pw_gid,(int*)params.gid,&params.gid_count)<0)
#else
if (getgrouplist(optarg,pwd->pw_gid,params.gid,&params.gid_count)<0)
#endif
params.gid[0]=pwd->pw_gid;
params.gid_count=1;
if (!(params.user=strdup(optarg)))
{
DLOG_ERR("getgrouplist failed. too much groups ?\n");
DLOG_ERR("strdup: out of memory\n");
exit_clean(1);
}
if (!params.gid_count)
{
params.gid[0] = pwd->pw_gid;
params.gid_count = 1;
}
params.droproot = true;
break;
}
case IDX_UID:
params.droproot = true;
free(params.user); params.user=NULL;
if (!parse_uid(optarg,&params.uid,params.gid,&params.gid_count,MAX_GIDS))
{
DLOG_ERR("--uid should be : uid[:gid,gid,...]\n");
@@ -1016,6 +994,7 @@ void parse_params(int argc, char *argv[])
params.gid[0] = 0x7FFFFFFF;
params.gid_count = 1;
}
params.droproot = true;
break;
case IDX_MAXCONN:
params.maxconn = atoi(optarg);
@@ -1345,13 +1324,25 @@ void parse_params(int argc, char *argv[])
{
if (!params.debug) params.debug = 1;
params.debug_target = LOG_TARGET_SYSLOG;
openlog("tpws",LOG_PID,LOG_USER);
openlog(progname,LOG_PID,LOG_USER);
}
else
#ifdef __ANDROID__
else if (!strcmp(optarg,"android"))
{
if (!params.debug) params.debug = 1;
params.debug_target = LOG_TARGET_ANDROID;
}
#endif
else if (optarg[0]>='0' && optarg[0]<='2')
{
params.debug = atoi(optarg);
params.debug_target = LOG_TARGET_CONSOLE;
}
else
{
fprintf(stderr, "invalid debug mode : %s\n", optarg);
exit_clean(1);
}
}
else
{
@@ -1711,10 +1702,22 @@ void parse_params(int argc, char *argv[])
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
// do not need args from file anymore
cleanup_args();
cleanup_args(&params);
#endif
if (bDry)
{
if (params.droproot)
{
if (!droproot(params.uid,params.user,params.gid,params.gid_count))
exit_clean(1);
#ifdef __linux__
if (!dropcaps())
exit_clean(1);
#endif
print_id();
if (!test_list_files())
exit_clean(1);
}
DLOG_CONDUP("command line parameters verified\n");
exit_clean(0);
}
@@ -1871,10 +1874,18 @@ static const char *bindll_s[] = { "unwanted","no","prefer","force" };
#define STRINGIFY(x) #x
#define TOSTRING(x) STRINGIFY(x)
#if defined(ZAPRET_GH_VER) || defined (ZAPRET_GH_HASH)
#ifdef __ANDROID__
#define PRINT_VER printf("github android version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH))
#else
#define PRINT_VER printf("github version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH))
#endif
#else
#ifdef __ANDROID__
#define PRINT_VER printf("self-built android version %s %s\n\n", __DATE__, __TIME__)
#else
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
#endif
int main(int argc, char *argv[])
{
@@ -2135,7 +2146,7 @@ int main(int argc, char *argv[])
}
set_ulimit();
if (params.droproot && !droproot(params.uid,params.gid,params.gid_count))
if (params.droproot && !droproot(params.uid,params.user,params.gid,params.gid_count))
goto exiterr;
#ifdef __linux__
if (!dropcaps())
@@ -2179,6 +2190,6 @@ exiterr:
if (Fpid) fclose(Fpid);
redir_close();
for(i=0;i<=params.binds_last;i++) if (listen_fd[i]!=-1) close(listen_fd[i]);
cleanup_params();
cleanup_params(&params);
return exit_v;
}