194 Commits
v70 ... v71

Author SHA1 Message Date
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
bol-van
c993f117a2 blockcheck: test http3 google fake 2025-05-20 12:05:43 +03:00
bol-van
b2f0c46388 update changes.txt 2025-05-20 11:32:58 +03:00
bol-van
2b095f863f blockcheck: report test function and domain every test 2025-05-20 11:32:08 +03:00
bol-van
a141dff374 nfqws: warn dup trash flood 2025-05-20 10:30:37 +03:00
bol-van
b34bfda8b5 blockcheck: tls multi fake test 2025-05-10 18:12:51 +03:00
bol-van
c1046a20db nfqws,tpws: sec_harden after daemonize 2025-05-10 16:57:04 +03:00
bol-van
24b93cca7e blockcheck: some dup and orig-autottl mods 2025-05-10 15:58:36 +03:00
bol-van
4f0fdb24f2 nfqws,tpws: support multiple gids in --uid 2025-05-10 11:11:56 +03:00
bol-van
6d52b49b98 nfqws: do not reconstruct synack-split in syn mode 2025-05-10 09:41:26 +03:00
bol-van
4b632313e2 nfqws: improve --synack-split 2025-05-09 20:57:14 +03:00
bol-van
22f3ecaec1 doc works 2025-05-09 20:41:02 +03:00
bol-van
2a23bc99f6 nfqws: --synack-split select syn or ack first 2025-05-09 20:20:55 +03:00
bol-van
8a1d7c7abd doc works 2025-05-09 17:21:08 +03:00
bol-van
ba712f308d doc works 2025-05-09 17:19:46 +03:00
bol-van
9ace0328ad doc works 2025-05-09 17:18:44 +03:00
bol-van
5c6f79799a doc works 2025-05-09 16:45:00 +03:00
bol-van
a84d015b1e doc works 2025-05-09 16:44:08 +03:00
bol-van
2d90a28dbc Revert "doc works"
This reverts commit 3c77bab002.
2025-05-09 16:42:17 +03:00
bol-van
3c77bab002 doc works 2025-05-09 16:22:21 +03:00
bol-van
8f27725d6a nfqws: --synack-split 2025-05-09 16:20:28 +03:00
bol-van
729ded0c61 nfqws: conntrack workaround TTL=1 2025-05-09 11:17:21 +03:00
bol-van
691a501b0d nfqws,tpws: do most checks before daemonize 2025-05-09 10:32:19 +03:00
bol-van
e62fb2f0f4 nfqws: simplify conntrack workaround 2025-05-08 19:46:35 +03:00
bol-van
603265dac2 nfqws: do not realloc hostname in ipcache if it's the same 2025-05-08 13:45:28 +03:00
bol-van
ed0bb4c106 tpws: ipcache socks hostname 2025-05-08 13:42:24 +03:00
bol-van
6eae2b0e71 doc works 2025-05-08 12:23:51 +03:00
bol-van
c59771f744 doc works 2025-05-08 12:20:55 +03:00
bol-van
dd23d6f3f4 doc works 2025-05-08 12:08:38 +03:00
bol-van
92dc012f08 doc works 2025-05-08 12:01:04 +03:00
bol-van
9bcefde37a doc works 2025-05-08 11:45:50 +03:00
bol-van
d2f7a53927 nfqws: --ctrack-disable 2025-05-08 09:00:20 +03:00
bol-van
f1dd351854 nfqws: --ctrack-disable 2025-05-08 08:54:05 +03:00
bol-van
5c63cb43e7 readme: update vps section 2025-05-06 09:58:03 +03:00
bol-van
7f24f82002 readme: update vps section 2025-05-06 09:51:53 +03:00
bol-van
b0c7af789a init: remove autohostlist touch, not needed anymore 2025-05-04 22:02:36 +03:00
bol-van
a426ea6dad nfqws,tpws: check list files accessibility after all params are parsed 2025-05-04 22:01:00 +03:00
bol-van
bda4226162 init: create autohostlist file if not exists 2025-05-04 21:47:26 +03:00
bol-van
dc1dc5c876 drop time exceeded icmp for nfqws-related connections 2025-05-04 18:21:43 +03:00
bol-van
3ca682e25a drop time exceeded icmp for nfqws-related connections 2025-05-04 18:15:33 +03:00
bol-van
9629ce5cb7 tpws: ipcache 2025-05-04 10:57:23 +03:00
bol-van
c626d88f54 nfqws: minor changes 2025-05-04 10:42:27 +03:00
bol-van
c91ddf4a54 nfqws: do not use interface name for ip->hostname 2025-05-03 22:06:14 +03:00
bol-van
6f1286b5b9 nfqws: fix mem leak 2025-05-03 21:59:43 +03:00
bol-van
c96bc62d3b nfqws: ip->hostname cache 2025-05-03 20:25:53 +03:00
bol-van
8432388b37 nfqws: debug autottl cache lifetime 2025-05-03 15:15:26 +03:00
bol-van
7efa83e61e init.d: remove --ipset prohibition 2025-05-03 13:03:33 +03:00
bol-van
abe91a4bfa nfqws: ipcache destroy in cleanup_params 2025-05-03 12:50:53 +03:00
bol-van
43173e6396 nfqws: return autottl path len check 2025-05-03 12:28:49 +03:00
bol-van
5cc888cd2c nfqws: autottl cache, --dup-autottl, --orig-autottl 2025-05-03 12:11:16 +03:00
bol-van
5b625fa709 update nftables.txt,iptables.txt 2025-05-03 10:54:59 +03:00
bol-van
0a8135b2de update config defaults 2025-05-03 10:52:20 +03:00
bol-van
d21175b4a3 nfqws: prepare for +- autottl 2025-04-29 17:45:34 +03:00
bol-van
68a538daed nfqws: conntrack: do not reset entry on dup SA 2025-04-29 16:31:37 +03:00
bol-van
d2c9ff50cd nfqws: copy DF ip flag 2025-04-29 13:36:05 +03:00
bol-van
50539d6cbf nfqws: windows fixes for recent changes 2025-04-29 12:25:23 +03:00
bol-van
8b5dfcfae1 nfqws: dup,orig_mod 2025-04-26 19:48:35 +03:00
bol-van
ccc60b5f07 init.d: fix missing - sign in 50-nfqws-ipset 2025-04-22 19:28:14 +03:00
bol-van
7f94f42b1d init.d: fix local var name 2025-04-22 19:07:26 +03:00
bol-van
1c1f259b39 init.d: improve nfqws ipset example 2025-04-22 19:03:18 +03:00
bol-van
6ef6c8ee5a nfqws: do not use overlapping memcmp 2025-04-21 15:54:06 +03:00
bol-van
581badfb73 nfqws: --dpi-desync-fake-tls=! 2025-04-21 14:52:51 +03:00
bol-van
8fce75daa4 hardware offload: be closer to fw4 in interface names 2025-04-20 11:26:07 +03:00
bol-van
c1e2e56576 hardware offload: be closer to fw4 in interface names 2025-04-20 11:24:55 +03:00
bol-van
e16ec69922 nfqws: fix unitialized use of host buffer (udp) 2025-04-20 08:49:50 +03:00
bol-van
63256a142f nfqws: fix unitialized use of host buffer 2025-04-19 19:59:42 +03:00
bol-van
4a9a8bd48e typo 2025-04-19 19:41:40 +03:00
bol-van
b996abd5ce nfqws,tpws: use tls record length in TLSDebug 2025-04-14 12:18:07 +03:00
bol-van
12461de3b0 nfqws,tpws: optimize tls debug, show quic 2025-04-14 11:21:16 +03:00
bol-van
7dab497b57 nfqws,tpws: optimize tls debug, show quic 2025-04-14 11:20:20 +03:00
bol-van
41dbba1c4c nfqws,tpws: debug alpn and ech 2025-04-13 18:07:46 +03:00
bol-van
d19f6c19a4 nfqws,tpws: debug tls version 2025-04-13 15:27:50 +03:00
bol-van
b12b1a5a17 winws build fix 2025-04-08 17:33:14 +03:00
bol-van
8022e2576d nfqws: BSD/clang build fix 2025-04-08 17:23:15 +03:00
bol-van
f4ea264ba9 minor var spelling fix 2025-04-08 17:02:33 +03:00
bol-van
061acb27e4 Merge pull request #1334 from tie/master
use enum for option indices
2025-04-08 17:00:53 +03:00
Ivan Trubach
8eb830d304 use enum for option indices 2025-04-08 16:56:17 +03:00
bol-van
2fb93c6add blockcheck: test tpws exists 2025-04-08 16:17:40 +03:00
bol-van
ad5c246629 blockcheck: test whether tpws supports fix-seg 2025-04-08 16:15:06 +03:00
bol-van
58e73d0331 github actions: do not use broken upx 5.0.0 2025-04-07 17:52:11 +03:00
bol-van
9ebeff621a readme.en : update ver 2025-04-07 10:16:30 +03:00
bol-van
69df271a16 readme: update crypto addresses 2025-04-07 10:15:36 +03:00
bol-van
e285b2401d isakmp fake 2025-04-06 16:42:56 +03:00
bol-van
6e1e7e43bc nfqws: optimize tls mod parse 2025-04-06 11:53:57 +03:00
bol-van
d04419a60c nfqws: safety check 2025-04-06 11:43:25 +03:00
bol-van
fc1bf47e82 update changes.txt 2025-04-06 11:34:43 +03:00
bol-van
929df3f094 nfqws: support different tls mods for every tls fake 2025-04-06 11:29:58 +03:00
bol-van
7272b243cb blockcheck: optimize 2025-04-05 18:13:16 +03:00
bol-van
72d48d957a update changes.txt 2025-04-05 18:10:46 +03:00
bol-van
f4069d484a update changes.txt 2025-04-05 18:10:18 +03:00
bol-van
1c82b0a6af blockcheck: --fix seg only if multiple split pos 2025-04-05 16:35:26 +03:00
bol-van
c08e69aa65 blockcheck: --fix seg only if multiple split pos 2025-04-05 16:31:22 +03:00
bol-van
8097f08020 ipset: some pkill's do not support multiple patterns 2025-04-05 13:56:31 +03:00
bol-van
4cae291e6f blockcheck: remove fix-seg for single split 2025-04-05 12:32:16 +03:00
bol-van
82ad5508dc blockcheck: --fix-seg for tpws multisplits 2025-04-05 12:24:43 +03:00
bol-van
fa8ddcfc79 desync.h fix 2025-04-05 11:53:59 +03:00
bol-van
b560e32e18 nfqws: update default tls fake 2025-04-05 09:45:44 +03:00
bol-van
67e1aee8a8 update compile docs 2025-04-04 17:38:52 +03:00
bol-van
1d8385a9b4 update compile docs 2025-04-04 17:37:49 +03:00
bol-van
340dec62a7 update changes.txt 2025-04-04 15:13:58 +03:00
bol-van
db4585c02f remove discord custom 2025-04-04 15:03:06 +03:00
bol-van
e792ca67ef nfqws: display original SNI value 2025-04-04 14:32:37 +03:00
bol-van
e5e53db6b8 nfqws: fixes 2025-04-04 14:20:36 +03:00
bol-van
e14ee9d1fe nfqws: fix wrong and mask 2025-04-04 14:09:45 +03:00
bol-van
360506ba4e discord and stun fakes 2025-04-04 13:58:46 +03:00
bol-van
aa769e05c6 nfqws: minor optimize 2025-04-04 13:58:33 +03:00
bol-van
6b0bc7a96b nfqws: tls mod set sni 2025-04-04 13:24:02 +03:00
bol-van
93bdfdb6be nfqws: loop for multiple blob cleanup 2025-04-04 09:25:46 +03:00
bol-van
6d95eada2b Merge pull request #1316 from tie/master
nfqws: also add stun l7proto to CLI help output
2025-04-03 21:33:39 +03:00
bol-van
e452ee8688 nfqws: cosmetics 2025-04-03 21:32:28 +03:00
bol-van
6e746f94cd nfqws: help text cosmetics 2025-04-03 21:29:38 +03:00
Ivan Trubach
9fd61e5d38 nfqws: also add stun l7proto to CLI help output 2025-04-03 21:28:46 +03:00
bol-van
0c0fba4461 Merge pull request #1314 from tie/master
nfqws: detect Discord Voice IP Discovery and STUN packets
2025-04-03 21:27:10 +03:00
Ivan Trubach
056e4c588a nfqws: detect STUN message packets 2025-04-03 21:02:42 +03:00
Ivan Trubach
4b288643ac nfqws: detect Discord Voice IP Discovery packets 2025-04-03 17:55:02 +03:00
bol-van
cbdee74e5f Merge pull request #1301 from Lost-gamer/master
update discord subnets
2025-04-01 10:25:22 +03:00
bol-van
743eb5a4a2 tpws makefile support systemd target for old systems 2025-03-31 16:26:00 +03:00
Lost
4e8e3a9ed9 update discord subnets 2025-03-31 10:25:52 +03:00
bol-van
b9b91a0e68 replace tls fake google 2025-03-26 12:09:49 +03:00
bol-van
9de7b66eef update build docs 2025-03-25 13:44:27 +03:00
bol-van
a2ffa3455d nfqws: minor beautify text 2025-03-24 11:20:51 +03:00
bol-van
60b97dbed0 nfqws: remove debug printfs 2025-03-24 11:14:38 +03:00
bol-van
e56e4f5f35 update changes 2025-03-24 10:32:02 +03:00
bol-van
5305ea83c8 fakes: GGC kyber with inter-packet CRYPTO frag 2025-03-24 09:44:50 +03:00
bol-van
14b3dd459b nfqws: define reasm buffer sizes 2025-03-24 09:34:37 +03:00
bol-van
66fda2c33d nfqws: support QUIC multi packet CRYPTO fragmentation 2025-03-23 23:29:16 +03:00
bol-van
77df43b9cb nfqws: minor optimize 2025-03-22 13:03:31 +03:00
bol-van
85f2b37c88 update docs 2025-03-21 21:00:47 +03:00
bol-van
e2d600fcc6 update docs 2025-03-21 20:58:53 +03:00
bol-van
37eda0ad98 nfqws: mod skipped DLOG_ERR -> DLOG 2025-03-21 19:40:25 +03:00
bol-van
770be21e1c nfqws: fix custom tls fake fallback logic 2025-03-21 19:09:37 +03:00
bol-van
1b880d42f9 nfqws,tpws: missing va_end 2025-03-21 17:33:57 +03:00
bol-van
6387315c0b nfqws: multiple fakes 2025-03-21 17:12:36 +03:00
bol-van
3d4b395bfe ignore windivert files in nfq 2025-03-21 14:23:19 +03:00
bol-van
55950ed7d0 remove bad files 2025-03-21 14:22:28 +03:00
bol-van
f2b0341484 blockcheck: add dupsid to tls-mod 2025-03-20 18:14:35 +03:00
bol-van
b2d89c5d22 blockcheck: warn MTU overflow with md5sig 2025-03-17 09:39:33 +03:00
bol-van
778b611f86 readme: md5sig with fakedsplit/fakeddisorder warning 2025-03-16 17:06:24 +03:00
bol-van
ffaf91c251 nfqws: debug packet length in sendto() 2025-03-16 15:56:47 +03:00
bol-van
326b42fafd nfqws: debug packet length in sendto() 2025-03-16 15:52:50 +03:00
bol-van
94d4238af2 update changes.txt 2025-03-14 11:41:52 +03:00
bol-van
15e22fa1bd tpws: detect WSL 1 and warn about non-working options 2025-03-14 11:38:52 +03:00
bol-van
bd8decddc5 nfqws,tpws: separate droproot from dropcaps 2025-03-13 21:54:28 +03:00
bol-van
2db1ebafe3 tpws systemd unit fix comment 2025-03-12 18:14:43 +03:00
bol-van
33bcf6f7b4 systemd improve cheat sheet 2025-03-12 17:47:17 +03:00
bol-van
f037f1acb2 update docs 2025-03-12 17:45:19 +03:00
bol-van
cdd9b32b27 update docs 2025-03-12 17:44:04 +03:00
bol-van
7934125c09 init.d: systemd unit examples for tpws and nfqws 2025-03-12 17:27:55 +03:00
bol-van
6493d55977 tpws: move systemd notify deeper 2025-03-12 14:45:52 +03:00
bol-van
cafbb17e70 install_easy: make systemd if systemd detected 2025-03-12 14:32:00 +03:00
bol-van
9ac73f7d2f readme.eng: hostlist ^ note 2025-03-12 13:07:29 +03:00
bol-van
08a6e8e069 nfqws,tpws : rename function 2025-03-12 13:03:33 +03:00
bol-van
644a934099 nfqws.service : add special compile warning 2025-03-12 12:58:36 +03:00
bol-van
0eec445af0 Merge pull request #1237 from tie/systemd
nfqws: add support for systemd readiness notifications
2025-03-12 12:50:02 +03:00
bol-van
b8acc1b979 Revert "Revert "Revert "nfqws,tpws: fflush stdin,stdout"""
This reverts commit 123eb057ae.
2025-03-12 12:49:06 +03:00
bol-van
123eb057ae Revert "Revert "nfqws,tpws: fflush stdin,stdout""
This reverts commit 56d06456fb.
2025-03-12 12:48:32 +03:00
bol-van
56d06456fb Revert "nfqws,tpws: fflush stdin,stdout"
This reverts commit a6efe05aa6.
2025-03-12 12:48:18 +03:00
bol-van
a6efe05aa6 nfqws,tpws: fflush stdin,stdout 2025-03-12 12:13:53 +03:00
Ivan Trubach
a1d29b0c3a nfqws,tpws: always use line buffering for console IO
Forces stdout and stderr to always use line buffering. Note that glibc
does automatically flush on newline iff connected to a terminal, but
that is not the case when running under systemd. See also
https://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html
https://www.gnu.org/software/libc/manual/html_node/Controlling-Buffering.html
2025-03-12 12:00:28 +03:00
Ivan Trubach
756603338b nfqws,tpws: add support for systemd readiness notifications 2025-03-12 11:39:03 +03:00
bol-van
8b73e2ea8e update docs 2025-03-06 16:07:44 +03:00
bol-van
2a0e952153 update docs 2025-03-06 16:06:55 +03:00
bol-van
1065202349 nfqws,tpws: ^ prefix in hostlist disables subdomain matches 2025-03-06 15:13:40 +03:00
bol-van
307d38f6af readme: update ver 2025-02-26 10:04:26 +03:00
bol-van
8ac4fc0af5 fix wrong var 2025-02-26 01:11:07 +03:00
bol-van
af89d03118 nfqws,tpws: fix hostlist-domains file open test 2025-02-25 13:07:38 +03:00
bol-van
d89daaaeac add localhost to zapret-hosts-user-exclude.txt 2025-02-24 13:10:54 +03:00
bol-van
f62b289cb5 nfqws,tpws: --version 2025-02-05 13:04:24 +03:00
bol-van
5f9fa28251 nfqws,tpws: r/o open instead of stat() for list file check 2025-02-04 23:28:18 +03:00
bol-van
bd67b41f32 nfqws,tpws: check accessibility of list files after droproot 2025-02-03 22:37:08 +03:00
bol-van
00619c8dab nfqws,tpws: show hostlist/ipset stat() error 2025-02-02 22:48:23 +03:00
bol-van
58e26c3e9d Merge pull request #1128 from Yoti/patch-1
quick_start: fix spelling
2025-01-30 22:31:37 +03:00
Yoti
eddbc3c3e0 quick_start: fix spelling 2025-01-30 22:20:15 +03:00
bol-van
2cc73de15c readme: move ip2net/mdig chapters 2025-01-30 12:09:34 +03:00
bol-van
9762f2d22b update docs 2025-01-30 12:03:57 +03:00
bol-van
8c9aa188c3 readme: update flow offload info 2025-01-29 14:57:55 +03:00
bol-van
2f151c0943 readme: update flow offload info 2025-01-29 14:55:11 +03:00
bol-van
9498456c4a readme: update flow offload info 2025-01-29 14:47:20 +03:00
bol-van
860607bce2 config.default: remove obsolete comment 2025-01-29 13:42:34 +03:00
bol-van
94f59511f0 update docs 2025-01-28 21:25:44 +03:00
bol-van
b07ce8d8ca nfqws: --dpi-desync-fake-tls-mod=dupsid 2025-01-28 21:22:33 +03:00
75 changed files with 5228 additions and 1677 deletions

View File

@@ -401,6 +401,7 @@ jobs:
uses: crazy-max/ghaction-upx@v3
with:
install-only: true
version: v4.2.4
- name: Prepare binaries
shell: bash

1
.gitignore vendored
View File

@@ -4,6 +4,7 @@ mdig/mdig
nfq/dvtws
nfq/nfqws
nfq/winws.exe
nfq/WinDivert*
tpws/tpws
binaries/my/
ipset/zapret-ip*.txt

View File

@@ -15,6 +15,19 @@ all: clean
done \
done
systemd: clean
@mkdir -p "$(TGT)"; \
for dir in $(DIRS); do \
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
$(MAKE) -C "$$dir" systemd || exit; \
for exe in "$$dir/"*; do \
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
mv -f "$$exe" "${TGT}" ; \
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
fi \
done \
done
android: clean
@mkdir -p "$(TGT)"; \
for dir in $(DIRS); do \

View File

@@ -341,12 +341,19 @@ netcat_test()
}
}
tpws_can_fix_seg()
{
# fix-seg requires kernel 4.6+
"$TPWS" --port 1 --dry-run --fix-seg >/dev/null 2>/dev/null
}
check_system()
{
echo \* checking system
UNAME=$(uname)
SUBSYS=
FIX_SEG=
local s
# can be passed FWTYPE=iptables to override default nftables preference
@@ -354,6 +361,14 @@ check_system()
Linux)
PKTWS="$NFQWS"
PKTWSD=nfqws
if [ -x "$TPWS" ] ; then
if tpws_can_fix_seg ; then
echo tpws supports --fix-seg on this system
FIX_SEG='--fix-seg'
else
echo tpws does not support --fix-seg on this system
fi
fi
linux_fwtype
[ "$FWTYPE" = iptables -o "$FWTYPE" = nftables ] || {
echo firewall type $FWTYPE not supported in $UNAME
@@ -720,6 +735,11 @@ ipt_aux_scheme()
# to avoid possible INVALID state drop
[ "$2" = tcp ] && IPT_ADD_DEL $1 INPUT -p $2 --sport $3 ! --syn -j ACCEPT
local icmp_filter="-p icmp -m icmp --icmp-type"
[ "$IPV" = 6 ] && icmp_filter="-p icmpv6 -m icmp6 --icmpv6-type"
IPT_ADD_DEL $1 INPUT $icmp_filter time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
# for strategies with incoming packets involved (autottl)
IPT_ADD_DEL $1 OUTPUT -p $2 --dport $3 -m conntrack --ctstate INVALID -j ACCEPT
if [ "$IPV" = 6 -a -n "$IP6_DEFRAG_DISABLE" ]; then
@@ -751,6 +771,7 @@ ipt_scheme()
$IPTABLES -t mangle -A blockcheck_output -p $1 ! --dport $2 -j RETURN
for ip in $3; do
$IPTABLES -t mangle -A blockcheck_output -d $ip -j CONNMARK --or-mark $DESYNC_MARK
$IPTABLES -t mangle -A blockcheck_output -d $ip -j NFQUEUE --queue-num $QNUM
done
@@ -768,12 +789,20 @@ nft_scheme()
nft add table inet $NFT_TABLE
nft "add chain inet $NFT_TABLE postnat { type filter hook output priority 102; }"
nft "add rule inet $NFT_TABLE postnat meta nfproto ipv${IPV} $1 dport $2 mark and $DESYNC_MARK != $DESYNC_MARK ip${ipver} daddr {$iplist} queue num $QNUM"
nft "add rule inet $NFT_TABLE postnat meta nfproto ipv${IPV} $1 dport $2 mark and $DESYNC_MARK == 0 ip${ipver} daddr {$iplist} ct mark set ct mark or $DESYNC_MARK queue num $QNUM"
# for strategies with incoming packets involved (autottl)
nft "add chain inet $NFT_TABLE prenat { type filter hook prerouting priority -102; }"
# enable everything generated by nfqws (works only in OUTPUT, not in FORWARD)
nft "add chain inet $NFT_TABLE predefrag { type filter hook output priority -402; }"
nft "add rule inet $NFT_TABLE predefrag meta nfproto ipv${IPV} mark and $DESYNC_MARK !=0 notrack"
[ "$IPV" = 4 ] && {
nft "add rule inet $NFT_TABLE prenat icmp type time-exceeded ct mark and $DESYNC_MARK != 0 drop"
nft "add rule inet $NFT_TABLE prenat icmp type time-exceeded ct state invalid drop"
}
[ "$IPV" = 6 ] && {
nft "add rule inet $NFT_TABLE prenat icmpv6 type time-exceeded ct mark and $DESYNC_MARK != 0 drop"
nft "add rule inet $NFT_TABLE prenat icmpv6 type time-exceeded ct state invalid drop"
}
}
pktws_ipt_prepare()
@@ -1013,6 +1042,7 @@ curl_test()
}
ws_curl_test()
{
# $1 - ws start function
# $2 - test function
# $3 - domain
@@ -1032,7 +1062,7 @@ tpws_curl_test()
# $1 - test function
# $2 - domain
# $3,$4,$5, ... - tpws params
echo - checking 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 $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=$?
@@ -1049,11 +1079,14 @@ pktws_curl_test()
# $1 - test function
# $2 - domain
# $3,$4,$5, ... - nfqws/dvtws params
echo - checking $PKTWSD ${WF:+$WF }$3 $4 $5 $6 $7 $8 $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 "$@"${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"}
local testf=$1 dom=$2 strategy code=$?
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"}
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=$?
[ "$code" = 0 ] && {
shift; shift;
strategy="$@"
strategy_append_extra_pktws
report_append "ipv${IPV} $dom $testf : $PKTWSD ${WF:+$WF }$strategy"
@@ -1063,11 +1096,11 @@ pktws_curl_test()
strategy_append_extra_pktws()
{
strategy="${strategy:+$strategy${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"}}"
strategy="${strategy:+${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" }$strategy${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"}}"
}
strategy_append_extra_tpws()
{
strategy="${strategy:+$strategy${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"}}"
strategy="${strategy:+${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" }$strategy${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"}}"
}
xxxws_curl_test_update()
@@ -1145,7 +1178,10 @@ test_has_fake()
warn_fool()
{
case "$1" in
md5sig) echo 'WARNING ! although md5sig fooling worked it will not work on all sites. it typically works only on linux servers.' ;;
md5sig) echo 'WARNING ! although md5sig fooling worked it will not work on all sites. it typically works only on linux servers.'
[ "$2" = "fakedsplit" -o "$2" = "fakeddisorder" ] && \
echo "WARNING ! fakedsplit/fakeddisorder with md5sig fooling and low split position causes MTU overflow with multi-segment TLS (kyber)"
;;
datanoack) echo 'WARNING ! although datanoack fooling worked it may break NAT and may only work with external IP. Additionally it may require nftables to work correctly.' ;;
esac
}
@@ -1157,15 +1193,19 @@ pktws_curl_test_update_vary()
# $4 - desync mode
# $5,$6,... - strategy
local testf=$1 sec=$2 domain=$3 desync=$4 proto zerofake= tlsmod= splits= pos fake ret=1
local testf=$1 sec=$2 domain=$3 desync=$4 proto splits= pos fake ret=1
local fake1=- fake2=- fake3=-
shift; shift; shift; shift
proto=http
[ "$sec" = 0 ] || proto=tls
test_has_fake $desync && {
zerofake="--dpi-desync-fake-$proto=0x00000000"
[ "$sec" = 0 ] || tlsmod="--dpi-desync-fake-tls-mod=rnd,rndsni,padencap"
fake1="--dpi-desync-fake-$proto=0x00000000"
[ "$sec" = 0 ] || {
fake2="--dpi-desync-fake-tls=0x00000000 --dpi-desync-fake-tls=! --dpi-desync-fake-tls-mod=rnd,rndsni,dupsid"
fake3="--dpi-desync-fake-tls-mod=rnd,dupsid,rndsni,padencap"
}
}
if test_has_fakedsplit $desync ; then
splits="method+2 midsld"
@@ -1174,7 +1214,8 @@ pktws_curl_test_update_vary()
splits="method+2 midsld"
[ "$sec" = 0 ] || splits="1 midsld 1,midsld"
fi
for fake in '' $zerofake $tlsmod ; do
for fake in '' "$fake1" "$fake2" "$fake3" ; do
[ "$fake" = "-" ] && continue
if [ -n "$splits" ]; then
for pos in $splits ; do
pktws_curl_test_update $testf $domain --dpi-desync=$desync "$@" --dpi-desync-split-pos=$pos $fake && {
@@ -1199,14 +1240,14 @@ pktws_check_domain_http_bypass_()
# $2 - encrypted test : 0 = plain, 1 - encrypted with server reply risk, 2 - encrypted without server reply risk
# $3 - domain
local ok ttls s f f2 e desync pos fooling frag sec="$2" delta splits
local ok ttls s f f2 e desync pos fooling frag sec="$2" delta orig splits
local need_split need_disorder need_fakedsplit need_fakeddisorder need_fake need_wssize
local splits_http='method+2 midsld method+2,midsld'
local splits_tls='2 1 sniext+1 sniext+4 host+1 midsld 1,midsld 1,sniext+1,host+1,midsld-2,midsld,midsld+2,endhost-1'
[ "$sec" = 0 ] && {
for s in '--hostcase' '--hostspell=hoSt' '--hostnospace' '--domcase' '--methodeol'; do
pktws_curl_test_update $1 $3 $s
pktws_curl_test_update $1 $3 $s && [ "$SCANLEVEL" = quick ] && return
done
}
@@ -1271,10 +1312,21 @@ pktws_check_domain_http_bypass_()
f="$f badseq datanoack md5sig"
[ "$IPV" = 6 ] && f="$f hopbyhop hopbyhop2"
for fooling in $f; do
ok=0
pktws_curl_test_update_vary $1 $2 $3 $desync --dpi-desync-fooling=$fooling $e && {
warn_fool $fooling
warn_fool $fooling $desync
[ "$SCANLEVEL" = quick ] && return
need_wssize=0
ok=1
}
[ "$fooling" = md5sig ] && {
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && continue
pktws_curl_test_update_vary $1 $2 $3 $desync --dpi-desync-fooling=$fooling --dup=1 --dup-cutoff=n2 --dup-fooling=md5sig $e && {
warn_fool $fooling $desync
echo "HINT ! To avoid possible 1 sec server response delay use --dup-ttl or --dup-autottl and block ICMP time exceeded"
[ "$SCANLEVEL" = quick ] && return
need_wssize=0
}
}
done
done
@@ -1337,8 +1389,11 @@ pktws_check_domain_http_bypass_()
[ "$need_fakedsplit" = 0 ] && contains "$desync" fakedsplit && continue
[ "$need_fakeddisorder" = 0 ] && contains "$desync" fakeddisorder && continue
ok=0
for delta in 1 2 3 4 5; do
pktws_curl_test_update_vary $1 $2 $3 $desync --dpi-desync-ttl=1 --dpi-desync-autottl=$delta $e && ok=1
for orig in '' 1 2 3; do
for delta in 1 2 3 4 5; do
pktws_curl_test_update_vary $1 $2 $3 $desync ${orig:+--orig-autottl=+$orig} --dpi-desync-ttl=1 --dpi-desync-autottl=-$delta $e && ok=1
done
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && break
done
[ "$ok" = 1 ] &&
{
@@ -1383,13 +1438,15 @@ pktws_check_domain_http3_bypass_()
# $1 - test function
# $2 - domain
local f desync frag tests rep
local f desync frag tests rep fake
for rep in '' 2 5 10 20; do
pktws_curl_test_update $1 $2 --dpi-desync=fake ${rep:+--dpi-desync-repeats=$rep} && [ "$SCANLEVEL" != force ] && {
[ "$SCANLEVEL" = quick ] && return
break
}
for fake in '' "--dpi-desync-fake-quic=$ZAPRET_BASE/files/fake/quic_initial_www_google_com.bin"; do
for rep in '' 2 5 10 20; do
pktws_curl_test_update $1 $2 --dpi-desync=fake ${fake:+$fake }${rep:+--dpi-desync-repeats=$rep} && [ "$SCANLEVEL" != force ] && {
[ "$SCANLEVEL" = quick ] && return
break
}
done
done
[ "$IPV" = 6 ] && {
@@ -1427,6 +1484,11 @@ warn_mss()
[ -n "$1" ] && echo 'WARNING ! although mss worked it may not work on all sites and will likely cause significant slowdown. it may only be required for TLS1.2, not TLS1.3'
return 0
}
fix_seg()
{
# $1 - split-pos
[ -n "$FIX_SEG" ] && contains "$1" , && echo "$FIX_SEG"
}
tpws_check_domain_http_bypass_()
{
@@ -1452,7 +1514,7 @@ tpws_check_domain_http_bypass_()
done
for s2 in '' '--hostcase' '--oob' '--disorder' ${oobdis:+"$oobdis"}; do
for s in $splits_http ; do
tpws_curl_test_update $1 $3 --split-pos=$s $s2 && [ "$SCANLEVEL" != force ] && {
tpws_curl_test_update $1 $3 --split-pos=$s $(fix_seg $s) $s2 && [ "$SCANLEVEL" != force ] && {
[ "$SCANLEVEL" = quick ] && return
break
}
@@ -1467,7 +1529,7 @@ tpws_check_domain_http_bypass_()
s3=${mss:+--mss=$mss}
for s2 in '' '--oob' '--disorder' ${oobdis:+"$oobdis"}; do
for pos in $splits_tls; do
tpws_curl_test_update $1 $3 --split-pos=$pos $s2 $s3 && warn_mss $s3 && [ "$SCANLEVEL" != force ] && {
tpws_curl_test_update $1 $3 --split-pos=$pos $(fix_seg $pos) $s2 $s3 && warn_mss $s3 && [ "$SCANLEVEL" != force ] && {
[ "$SCANLEVEL" = quick ] && return
need_mss=0
break
@@ -1475,7 +1537,7 @@ tpws_check_domain_http_bypass_()
done
done
for s in '' '--oob' '--disorder' ${oobdis:+"$oobdis"}; do
for s2 in '--tlsrec=midsld' '--tlsrec=sniext+1 --split-pos=midsld' '--tlsrec=sniext+4 --split-pos=midsld' '--tlsrec=sniext+1 --split-pos=1,midsld' '--tlsrec=sniext+4 --split-pos=1,midsld' ; do
for s2 in '--tlsrec=midsld' '--tlsrec=sniext+1 --split-pos=midsld' '--tlsrec=sniext+4 --split-pos=midsld' "--tlsrec=sniext+1 --split-pos=1,midsld $FIX_SEG" "--tlsrec=sniext+4 --split-pos=1,midsld $FIX_SEG" ; do
tpws_curl_test_update $1 $3 $s2 $s $s3 && warn_mss $s3 && [ "$SCANLEVEL" != force ] && {
[ "$SCANLEVEL" = quick ] && return
need_mss=0

View File

@@ -405,14 +405,14 @@ std_ports()
has_bad_ws_options()
{
# $1 - nfqws/tpws opts
# ПРИМЕЧАНИЕ ДЛЯ РАСПРОСТРАНИТЕЛЕЙ КОПИПАСТЫ
# ЭТОТ КОД СДЕЛАН СПЕЦИАЛЬНО ДЛЯ ВАС, ЧТОБЫ ВЫ НЕ ПОСТИЛИ В СЕТЬ ПЛОХИЕ РЕЦЕПТЫ
# ЕСЛИ ВАМ ХОЧЕТСЯ ЕГО УДАЛИТЬ И НАПИСАТЬ ИНСТРУКЦИЮ КАК ЕГО УДАЛЯТЬ, ВЫ ДЕЛАЕТЕ ХРЕНОВУЮ УСЛУГУ. НАПИШИТЕ ЛУЧШЕ custom script.
# custom script - ЭТО ФАЙЛИК, КОТОРЫЙ ДОСТАТОЧНО СКОПИРОВАТЬ В НУЖНУЮ ДИРЕКТОРИЮ, ЧТОБЫ ОН СДЕЛАЛ ТОЖЕ САМОЕ, НО ЭФФЕКТИВНО.
# ФИЛЬТРАЦИЯ ПО IPSET В ЯДРЕ НЕСРАВНИМО ЭФФЕКТИВНЕЕ, ЧЕМ ПЕРЕКИДЫВАТЬ ВСЕ ПАКЕТЫ В nfqws И ТАМ ФИЛЬТРОВАТЬ
# --ipset СУЩЕСТВУЕТ ТОЛЬКО ДЛЯ ВИНДЫ И LINUX СИСТЕМ БЕЗ ipset (НАПРИМЕР, Android).
# И ТОЛЬКО ПО ЭТОЙ ПРИЧИНЕ ОНО НЕ ВЫКИНУТО ПОЛНОСТЬЮ ИЗ LINUX ВЕРСИИ
contains "$1" "--ipset"
# kernel or user mode ipset usage should be wise
# if all traffic is already intercepted it would be OK to use ip-based specialized profiles
# but if all traffic is intercepted only to filter a group of ip its BAD. kernel ipset should be used.
# I cannot insert brain to copy-pasters, I know they will misuse. But it's their problem.
# zapret is not made for newbies
#contains "$1" "--ipset"
return 1
}
check_bad_ws_options()
{

View File

@@ -391,6 +391,27 @@ zapret_do_firewall_rules_ipt()
zapret_do_firewall_standard_rules_ipt $1
custom_runner zapret_custom_firewall $1
zapret_do_icmp_filter $1
}
zapret_do_icmp_filter()
{
# $1 - 1 - add, 0 - del
local FW_EXTRA_PRE= FW_EXTRA_POST=
[ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && {
[ "$DISABLE_IPV4" = 1 ] || {
ipt_add_del $1 POSTROUTING -t mangle -m mark --mark $DESYNC_MARK/$DESYNC_MARK -j CONNMARK --or-mark $DESYNC_MARK
ipt_add_del $1 INPUT -p icmp -m icmp --icmp-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
ipt_add_del $1 FORWARD -p icmp -m icmp --icmp-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
}
[ "$DISABLE_IPV6" = 1 ] || {
ipt6_add_del $1 POSTROUTING -t mangle -m mark --mark $DESYNC_MARK/$DESYNC_MARK -j CONNMARK --or-mark $DESYNC_MARK
ipt6_add_del $1 INPUT -p icmpv6 -m icmp6 --icmpv6-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
ipt6_add_del $1 FORWARD -p icmpv6 -m icmp6 --icmpv6-type time-exceeded -m connmark --mark $DESYNC_MARK/$DESYNC_MARK -j DROP
}
}
}
zapret_do_firewall_ipt()

View File

@@ -111,6 +111,14 @@ unprepare_route_localnet()
set_route_localnet 0 "$@"
}
get_uevent_devtype()
{
local DEVTYPE INTERFACE IFINDEX OF_NAME OF_FULLNAME OF_COMPATIBLE_N
[ -f "/sys/class/net/$1/uevent" ] && {
. "/sys/class/net/$1/uevent"
echo -n $DEVTYPE
}
}
resolve_lower_devices()
{
# $1 - bridge interface name

View File

@@ -106,7 +106,7 @@ cat << EOF | nft -f -
flush chain inet $ZAPRET_NFT_TABLE predefrag_nfqws
add rule inet $ZAPRET_NFT_TABLE predefrag mark and $DESYNC_MARK !=0 jump predefrag_nfqws comment "nfqws generated : avoid drop by INVALID conntrack state"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws mark and $DESYNC_MARK_POSTNAT !=0 notrack comment "postnat traffic"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws ip frag-off != 0 notrack comment "ipfrag"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws ip frag-off & 0x1fff != 0 notrack comment "ipfrag"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws exthdr frag exists notrack comment "ipfrag"
add rule inet $ZAPRET_NFT_TABLE predefrag_nfqws tcp flags ! syn,rst,ack notrack comment "datanoack"
add set inet $ZAPRET_NFT_TABLE lanif { type ifname; }
@@ -119,6 +119,20 @@ EOF
nft_flush_chain predefrag_nfqws
nft_add_rule predefrag_nfqws notrack comment \"do not track nfqws generated packets to avoid nat tampering and defragmentation\"
}
[ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && {
if is_postnat; then
# can be caused by untracked nfqws-generated packets
nft_add_rule prerouting icmp type time-exceeded ct state invalid drop
else
nft_add_rule postrouting_hook mark and $DESYNC_MARK != 0 ct mark set ct mark or $DESYNC_MARK comment \"nfqws related : prevent ttl expired socket errors\"
fi
[ "$DISABLE_IPV4" = "1" ] || {
nft_add_rule prerouting icmp type time-exceeded ct mark and $DESYNC_MARK != 0 drop comment \"nfqws related : prevent ttl expired socket errors\"
}
[ "$DISABLE_IPV6" = "1" ] || {
nft_add_rule prerouting icmpv6 type time-exceeded ct mark and $DESYNC_MARK != 0 drop comment \"nfqws related : prevent ttl expired socket errors\"
}
}
}
nft_del_chains()
{
@@ -320,7 +334,7 @@ nft_fill_ifsets()
# $5 - space separated wan physical interface names (optional)
# $6 - space separated wan6 physical interface names (optional)
local script i j ALLDEVS devs
local script i j ALLDEVS devs b
# if large sets exist nft works very ineffectively
# looks like it analyzes the whole table blob to find required data pieces
@@ -348,15 +362,18 @@ flush set inet $ZAPRET_NFT_TABLE lanif"
nft_create_or_update_flowtable 'offload' 2>/dev/null
# then add elements. some of them can cause error because unsupported
for i in $ALLDEVS; do
# first try to add interface itself
nft_create_or_update_flowtable 'offload' $i 2>/dev/null
# bridge members must be added instead of the bridge itself
# some members may not support hw offload. example : lan1 lan2 lan3 support, wlan0 wlan1 - not
b=
devs=$(resolve_lower_devices $i)
for j in $devs; do
# do not display error if addition failed
nft_create_or_update_flowtable 'offload' $j 2>/dev/null
nft_create_or_update_flowtable 'offload' $j && b=1 2>/dev/null
done
[ -n "$b" ] || {
# no lower devices added ? try to add interface itself
nft_create_or_update_flowtable 'offload' $i 2>/dev/null
}
done
;;
esac
@@ -453,7 +470,7 @@ _nft_fw_nfqws_post4()
nft_print_op "$filter" "nfqws postrouting (qnum $port)" 4
rule="${3:+oifname @wanif }$filter ip daddr != @nozapret"
is_postnat && setmark="meta mark set meta mark or $DESYNC_MARK_POSTNAT"
nft_insert_rule $chain $rule $setmark $FW_EXTRA_POST queue num $port bypass
nft_insert_rule $chain $rule $setmark $CONNMARKER $FW_EXTRA_POST queue num $port bypass
nft_add_nfqws_flow_exempt_rule "$rule"
}
}
@@ -468,7 +485,7 @@ _nft_fw_nfqws_post6()
nft_print_op "$filter" "nfqws postrouting (qnum $port)" 6
rule="${3:+oifname @wanif6 }$filter ip6 daddr != @nozapret6"
is_postnat && setmark="meta mark set meta mark or $DESYNC_MARK_POSTNAT"
nft_insert_rule $chain $rule $setmark $FW_EXTRA_POST queue num $port bypass
nft_insert_rule $chain $rule $setmark $CONNMARKER $FW_EXTRA_POST queue num $port bypass
nft_add_nfqws_flow_exempt_rule "$rule"
}
}
@@ -492,7 +509,7 @@ _nft_fw_nfqws_pre4()
local filter="$1" port="$2" rule
nft_print_op "$filter" "nfqws prerouting (qnum $port)" 4
rule="${3:+iifname @wanif }$filter ip saddr != @nozapret"
nft_insert_rule $(get_prechain) $rule $FW_EXTRA_POST queue num $port bypass
nft_insert_rule $(get_prechain) $rule $CONNMARKER $FW_EXTRA_POST queue num $port bypass
}
}
_nft_fw_nfqws_pre6()
@@ -505,7 +522,7 @@ _nft_fw_nfqws_pre6()
local filter="$1" port="$2" rule
nft_print_op "$filter" "nfqws prerouting (qnum $port)" 6
rule="${3:+iifname @wanif6 }$filter ip6 saddr != @nozapret6"
nft_insert_rule $(get_prechain) $rule $FW_EXTRA_POST queue num $port bypass
nft_insert_rule $(get_prechain) $rule $CONNMARKER $FW_EXTRA_POST queue num $port bypass
}
}
nft_fw_nfqws_pre()
@@ -683,3 +700,7 @@ zapret_do_firewall_nft()
return 0
}
# ctmark is not available in POSTNAT mode
CONNMARKER=
[ "$FILTER_TTL_EXPIRED_ICMP" = 1 ] && is_postnat && CONNMARKER="ct mark set ct mark or $DESYNC_MARK"

View File

@@ -97,7 +97,7 @@ NFQWS_OPT="
# none,ipset,hostlist,autohostlist
MODE_FILTER=none
# openwrt only : donttouch,none,software,hardware
# donttouch,none,software,hardware
FLOWOFFLOAD=donttouch
# openwrt: specify networks to be treated as LAN. default is "lan"
@@ -129,6 +129,11 @@ INIT_APPLY_FW=1
# do not work with ipv6
DISABLE_IPV6=1
# drop icmp time exceeded messages for nfqws tampered connections
# in POSTNAT mode this can interfere with default mtr/traceroute in tcp or udp mode. use source port not redirected to nfqws
# set to 0 if you are not expecting connection breakage due to icmp in response to TCP SYN or UDP
FILTER_TTL_EXPIRED_ICMP=1
# select which init script will be used to get ip or host list
# possible values : get_user.sh get_antizapret.sh get_combined.sh get_reestr.sh get_hostlist.sh
# comment if not required

View File

@@ -452,3 +452,53 @@ install_easy: dnf packager support
nfqws,tpws: hostlist/ipset track not only file mod time but also file size
nfqws,tpws,ipset: return lists reload on HUP
nfqws,blockcheck: --dpi-desync-fake-tls-mod
v70.1
nfqws: --dpi-desync-fake-tls-mod=dupsid
nfqws,tpws: test accessibility of list files after privs drop
nfqws,tpws: --version
v70.4
nfqws,tpws: ^ prefix in hostlist to disable subdomain matches
nfqws,tpws: optional systemd notify support. compile using 'make systemd'
nfqws,tpws: systemd instance templates for nfqws and tpws
nfqws,tpws: separate droproot from dropcaps
tpws: detect WSL 1 and warn about non-working options
v70.5
nfqws: multiple --dpi-desync-fake-xxx
nfqws: support of inter-packet fragmented QUIC CRYPTO
v70.6
nfqws: detect Discord Voice IP discovery packets
nfqws: detect STUN message packets
nfqws: change SNI to specified value tls mod : --dpi-desync-fake-tls-mod sni=<sni>
nfqws: update default TLS ClientHello fake. firefox 136.0.4 finger, no kyber, SNI=microsoft.com
nfqws: multiple mods for multiple TLS fakes
init.d: remove 50-discord
blockcheck: use tpws --fix-seg on linux for multiple splits
v71
nfqws,tpws: debug tls version, alpn, ech
nfqws: --dpi-desync-fake-tls=! means default tls fake
nfqws: --dup*
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

View File

@@ -12,10 +12,10 @@ Other packages may be required on your distribution. Look for the errors.
examples :
curl -o - https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz | tar -Jxvf -
curl -o - https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz | tar -Jxv
cd openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64
curl -o - https://downloads.openwrt.org/snapshots/targets/x86/64/openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst | tar --zstd -xvf -
curl -o - https://downloads.openwrt.org/snapshots/targets/x86/64/openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst | tar --zstd -xv
cd openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64
3) Install required libs
@@ -48,7 +48,7 @@ static build : make CFLAGS=-static package/{tpws,nfqws,mdig,ip2net}/compile
executables only : build_dir/target/<progname>
ipk or apk packages : bin/packages/*/base
8) Installating to openwrt to use with zapret
8) Installing to openwrt to use with zapret
zapret with or without binaries should be already installed in /opt/zapret.
Install ipk's or apk's with all compiled progs using opkg or apk.

View File

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

View File

@@ -2,8 +2,8 @@
> [!CAUTION]
> Не пишите в issue вопросы типа "как скопировать файл", "как скачать", "как
> запустить", ... То есть все , что касается базовых навыков обращения с ОС
> linux. Эти вопросы будут закрывать сразу. Если у вас подобные вопросы
> запустить" и т.п. То есть все, что касается базовых навыков обращения с ОС
> Linux. Эти вопросы будут закрывать сразу. Если у вас подобные вопросы
> возникают, рекомендую не использовать данный софт или искать помощь где-то в
> другом месте. То же самое могу сказать тем, кто хочет нажать 1 кнопку, чтобы
> все заработало, и совсем не хочет читать и изучать. Увы, такое не подвезли и
@@ -89,14 +89,15 @@
>
> Проверить работает ли этот вариант можно так:
> ```sh
> $ dig -p 53 @77.88.8.88 rutracker.org dig -p 1253 @77.88.8.88 rutracker.org
> $ dig -p 53 @77.88.8.88 rutracker.org
> $ dig -p 1253 @77.88.8.88 rutracker.org
> ```
>
> Если DNS действительно подменяется, и ответ на эти 2 команды разный,
> значит метод вероятно работает.
>
> В openwrt DNS на нестандартном порту можно прописать в `/etc/config/dhcp`
> таким способом :
> таким способом:
>
> ```
> config dnsmasq
@@ -163,7 +164,7 @@
> Если кратко, то обычно параметры конструируются так:
> ```sh
> "--filter-udp=443 'параметры для quic' <HOSTLIST_NOAUTO> --new
> --filter-tcp=80,443 'обьединенные параметры для http и https' <HOSTLIST>"
> --filter-tcp=80,443 'объединенные параметры для http и https' <HOSTLIST>"
> ```
>
> Или так:
@@ -193,7 +194,7 @@
> "--filter-l3=ipv4 --filter-udp=443 lпараметры для quic ipv4' <HOSTLIST_NOAUTO> --new
> --filter-l3=ipv4 --filter-tcp=80 'параметры для http ipv4' <HOSTLIST> --new
> --filter-l3=ipv4 --filter-tcp=443 'параметры для https ipv4' <HOSTLIST> --new
> --filter-l3=ipv6 --filter-udp=443 "параметры для quic ipv6" <HOSTLIST_NOAUTO> --new
> --filter-l3=ipv6 --filter-udp=443 'параметры для quic ipv6' <HOSTLIST_NOAUTO> --new
> --filter-l3=ipv6 --filter-tcp=80 'параметры для http ipv6' <HOSTLIST> --new
> --filter-l3=ipv6 --filter-tcp=443 'параметры для https ipv6' <HOSTLIST>"
> ```

View File

@@ -1,9 +1,10 @@
# zapret v70
# zapret v71
# 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,9 +24,12 @@ ___
- [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)
@@ -34,6 +38,8 @@ ___
- [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)
@@ -133,29 +139,51 @@ nfqws takes the following parameters:
--debug=0|1
--dry-run ; verify parameters and exit with code 0 if successful
--version ; print version and exit
--comment ; any text (ignored)
--qnum=<nfqueue_number>
--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 ?)
@@ -172,33 +200,35 @@ nfqws takes the following parameters:
--dpi-desync-badack-increment=<int|0xHEX> ; badseq fooling ackseq signed increment. default -66000
--dpi-desync-any-protocol=0|1 ; 0(default)=desync only http and tls 1=desync any nonempty data packet
--dpi-desync-fake-http=<filename>|0xHEX ; file containing fake http request
--dpi-desync-fake-tls=<filename>|0xHEX ; file containing fake TLS ClientHello (for https)
--dpi-desync-fake-tls-mod=mod[,mod] ; comma separated list of TLS fake mods. available mods : none,rnd,rndsni,padencap
--dpi-desync-fake-tls=<filename>|0xHEX|! ; file containing fake TLS ClientHello (for https). '!' = standard fake
--dpi-desync-fake-tls-mod=mod[,mod] ; comma separated list of TLS fake mods. available mods : none,rnd,rndsni,sni=<sni>,dupsid,padencap
--dpi-desync-fake-unknown=<filename>|0xHEX ; file containing unknown protocol fake payload
--dpi-desync-fake-syndata=<filename>|0xHEX ; file containing SYN data payload
--dpi-desync-fake-quic=<filename>|0xHEX ; file containing fake QUIC Initial
--dpi-desync-fake-wireguard=<filename>|0xHEX ; file containing fake wireguard handshake initiation
--dpi-desync-fake-dht=<filename>|0xHEX ; file containing fake DHT (d1..e)
--dpi-desync-fake-discord=<filename>|0xHEX ; file containing fake Discord voice connection initiation packet (IP Discovery)
--dpi-desync-fake-stun=<filename>|0xHEX ; file containing fake STUN message
--dpi-desync-fake-unknown-udp=<filename>|0xHEX ; file containing unknown udp protocol fake payload
--dpi-desync-udplen-increment=<int> ; increase or decrease udp packet length by N bytes (default 2). negative values decrease length.
--dpi-desync-udplen-pattern=<filename>|0xHEX ; udp tail fill pattern
--dpi-desync-start=[n|d|s]N ; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N
--dpi-desync-cutoff=[n|d|s]N ; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--hostlist=<filename> ; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)
--hostlist=<filename> ; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply if not prefixed with `^`, gzip supported, multiple hostlists allowed)
--hostlist-domains=<domain_list> ; comma separated fixed domain list
--hostlist-exclude=<filename> ; do not apply dpi desync to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)
--hostlist-exclude=<filename> ; do not apply dpi desync to the listed hosts (one host per line, subdomains auto apply if not prefixed with `^`, gzip supported, multiple hostlists allowed)
--hostlist-exclude-domains=<domain_list> ; comma separated fixed domain list
--hostlist-auto=<filename> ; detect DPI blocks and build hostlist automatically
--hostlist-auto-fail-threshold=<int> ; how many failed attempts cause hostname to be added to auto hostlist (default : 3)
--hostlist-auto-fail-time=<int> ; all failed attemps must be within these seconds (default : 60)
--hostlist-auto-retrans-threshold=<int> ; how many request retransmissions cause attempt to fail (default : 3)
--hostlist-auto-debug=<logfile> ; debug auto hostlist positives
--hostlist-auto-debug=<logfile> ; debug auto hostlist positives
--new ; begin new strategy (new profile)
--skip ; do not use this profile
--filter-l3=ipv4|ipv6 ; L3 protocol filter. multiple comma separated values allowed.
--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=[http|tls|quic|wireguard|dht|unknown] ; L6-L7 protocol filter. multiple comma separated values allowed.
--filter-l7=<proto> ; L6-L7 protocol filter. multiple comma separated values allowed. proto: http tls quic wireguard dht discord stun unknown
--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)
@@ -218,7 +248,9 @@ There're attacks based on TCP sequence numbers. Methods can be combined in many
Fakes are separate generated by nfqws packets carrying false information for DPI. They must either not reach the server or be rejected by it. Otherwise TCP connection or data stream would be broken. There're multiple ways to solve this task.
* **md5sig** does not work on all servers
* **md5sig** does not work on all servers. It typically works only on Linux servers. MD5 tcp option requires additional space in TCP header
and can cause MTU overflow during fakedsplit/fakeddisorder on low positions when multisegment query (TLS kyber) is transmitted.
`nfqws` cannot redistribute data between original TCP segments. The error displayed is 'message too long'.
* **badsum** doesn't work if your device is behind NAT which does not pass invalid packets.
The most common Linux NAT router configuration does not pass them. Most home routers are Linux based.
The default sysctl configuration `net.netfilter.nf_conntrack_checksum=1` causes contrack to verify tcp and udp checksums
@@ -252,16 +284,25 @@ 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.
`--dpi-desync-fooling` takes multiple comma separated values.
Multiple parameters `--dpi-desync-fake-???` are supported except for the `--dpi-desync-fake-syndata`.
Fakes are sent in the specified order. `--dpi-desync-repeats` resends each fake.
Resulting order would be : `fake1 fake1 fake1 fake2 fake2 fake2 fake3 fake3 fake3 .....`
### FAKE mods
**nfqws** has built-in TLS fake. It can be customized with `--dpi-desync-fake-tls` option.
@@ -273,10 +314,20 @@ It's possible to use TLS Client Hello with any fingerprint and any SNI.
* `none`. Do not do any mods.
* `rnd`. Randomize `random` and `session id` fields. Applied on every request.
* `rndsni`. Randomize SNI. If SNI >=7 symbols random SLD is applied with known TLD. Otherwise filled with random symbols. Applied only once at startup.
* `dupsid`. Copy `session ID` from original TLS Client Hello. Takes precedence over `rnd`. Applied on every request.
* `sni=<sni>`. Set specified SNI value. Changes TLS fake length, fixes lengths in TLS structure. Applied once at startup before `rndsni`.
* `padencap`. Padding extension is extended by original TLS Client Hello size (including multi packet variation with kyber). Padding extension is added to the end if not present, otherwise it must be the last extension. All lengths are increased. Fake size is not changed. Can be useful if DPI does not analyze sequence numbers properly. Applied on every request.
By default if custom fake is not defined `rnd,rndsni` mods are applied. If defined - `none`.
This behaviour is compatible with previous versions.
By default if custom fake is not defined `rnd,rndsni,dupsid` mods are applied. If defined - `none`.
This behaviour is compatible with previous versions with addition of `dupsid`.
If multiple TLS fakes are present each one takes the last mod.
If a mod is specified after fake it replaces previous mod.
This way it's possible to use different mods for every TLS fake.
If a mod is set to non-TLS fake it causes error. Use `--dpi-desync-fake-tls-mod=none'.
Example : `--dpi-desync-fake-tls=iana_org.bin --dpi-desync-fake-tls-mod=rndsni --dpi-desync-fake-tls=0xaabbccdd --dpi-desync-fake-tls-mod=none'
### TCP segmentation
@@ -335,6 +386,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.
@@ -356,12 +445,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)
@@ -460,7 +563,7 @@ This option can resist DPIs that track outgoing UDP packet sizes.
Requires that application protocol does not depend on udp payload size.
QUIC initial packets are recognized. Decryption and hostname extraction is supported so `--hostlist` parameter will work.
Wireguard handshake initiation and DHT packets are also recognized.
Wireguard handshake initiation, DHT, STUN and [Discord Voice IP Discovery](https://discord.com/developers/docs/topics/voice-connections#ip-discovery) packets are also recognized.
For other protocols desync use `--dpi-desync-any-protocol`.
Conntrack supports udp. `--dpi-desync-cutoff` will work. UDP conntrack timeout can be set in the 4th parameter of `--ctrack-timeouts`.
@@ -648,6 +751,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
@@ -659,6 +787,7 @@ tpws is transparent proxy.
--debug=0|1|2|syslog|@<filename> ; 1 and 2 means log to console and set debug level. for other targets use --debug-level.
--debug-level=0|1|2 ; specify debug level for syslog and @<filename>
--dry-run ; verify parameters and exit with code 0 if successful
--version ; print version and exit
--bind-addr=<v4_addr>|<v6_addr> ; for v6 link locals append %interface_name : fe80::1%br-lan
--bind-iface4=<interface_name> ; bind to the first ipv4 addr of interface
--bind-iface6=<interface_name> ; bind to the first ipv6 addr of interface
@@ -683,6 +812,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
@@ -700,9 +831,9 @@ tpws is transparent proxy.
--ipset-exclude=<filename> ; ipset exclude filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)
--ipset-exclude-ip=<ip_list> ; comma separated fixed subnet list
--hostlist=<filename> ; only act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)
--hostlist=<filename> ; only act on hosts in the list (one host per line, subdomains auto apply if not prefixed with '^', gzip supported, multiple hostlists allowed)
--hostlist-domains=<domain_list> ; comma separated fixed domain list
--hostlist-exclude=<filename> ; do not act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)
--hostlist-exclude=<filename> ; do not act on hosts in the list (one host per line, subdomains auto apply if not prefixed with '^', gzip supported, multiple hostlists allowed)
--hostlist-exclude-domains=<domain_list> ; comma separated fixed domain list
--hostlist-auto=<filename> ; detect DPI blocks and build hostlist automatically
--hostlist-auto-fail-threshold=<int> ; how many failed attempts cause hostname to be added to auto hostlist (default : 3)
@@ -733,7 +864,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
@@ -767,7 +898,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
@@ -981,6 +1112,7 @@ If all include lists are empty it works like no include lists exist at all.
If you need "all except" mode you dont have to delete zapret-hosts-users.txt. Just make it empty.
Subdomains auto apply. For example, "ru" in the list affects "*.ru" .
`^` prefix symbol disables subdomain match.
**tpws** and **nfqws** automatically reload lists if their modification time or file size is changed.
HUP signal forcibly reloads all lists.
@@ -1247,6 +1379,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:
@@ -1428,12 +1564,8 @@ If this is the case then run another script in background and add some delay the
Are welcome here :
<img src=https://cdn-icons-png.flaticon.com/16/14446/14446252.png alt="USDT" style="vertical-align: middle;"/> USDT
```
0x3d52Ce15B7Be734c53fc9526ECbAB8267b63d66E
```
USDT `0x3d52Ce15B7Be734c53fc9526ECbAB8267b63d66E`
<img src=https://cdn-icons-png.flaticon.com/16/5968/5968260.png alt="USDT" style="vertical-align: middle;"/> BTC
```
bc1qhqew3mrvp47uk2vevt5sctp7p2x9m7m5kkchve
```
BTC `bc1qhqew3mrvp47uk2vevt5sctp7p2x9m7m5kkchve`
ETH `0x3d52Ce15B7Be734c53fc9526ECbAB8267b63d66E`

View File

@@ -1,9 +1,10 @@
# zapret v70
# zapret v71
# ВНИМАНИЕ, остерегайтесь мошенников
zapret является свободным и open source.
Всякий, кто понуждает вас скачивать zapret только с его ресурса, требует удалить ссылки, видео, файлы, обосновывая эти требования авторскими правами, сам нарушает [лицензию](./LICENSE.txt).
Однако, это не исключает [добровольные пожертвования](#поддержать-разработчика).
# Multilanguage README
@@ -25,7 +26,10 @@ zapret является свободным и open source.
- [TCP СЕГМЕНТАЦИЯ](#tcp-сегментация)
- [ПЕРЕКРЫТИЕ SEQUENCE NUMBERS](#перекрытие-sequence-numbers)
- [СПЕЦИФИЧЕСКИЕ РЕЖИМЫ IPV6](#специфические-режимы-ipv6)
- [МОДИФИКАЦИЯ ОРИГИНАЛА](#модификация-оригинала)
- [ДУБЛИКАТЫ](#дубликаты)
- [КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ](#комбинирование-методов-десинхронизации)
- [КЭШ IP](#кэш-ip)
- [РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА](#реакция-dpi-на-ответ-сервера)
- [РЕЖИМ SYNACK](#режим-synack)
- [РЕЖИМ SYNDATA](#режим-syndata)
@@ -38,6 +42,7 @@ zapret является свободным и open source.
- [IPTABLES ДЛЯ NFQWS](#iptables-для-nfqws)
- [NFTABLES ДЛЯ NFQWS](#nftables-для-nfqws)
- [FLOW OFFLOADING](#flow-offloading)
- [ДУРЕНИЕ СО СТОРОНЫ СЕРВЕРА](#дурение-со-стороны-сервера)
- [tpws](#tpws)
- [TCP СЕГМЕНТАЦИЯ В TPWS](#tcp-сегментация-в-tpws)
- [TLSREC](#tlsrec)
@@ -47,9 +52,9 @@ zapret является свободным и open source.
- [СЛУЖЕБНЫЕ ПАРАМЕТРЫ](#служебные-параметры)
- [IPTABLES ДЛЯ TPWS](#iptables-для-tpws)
- [NFTABLES ДЛЯ TPWS](#nftables-для-tpws)
- [Способы получения списка заблокированных IP](#способы-получения-списка-заблокированных-ip)
- [ip2net](#ip2net)
- [mdig](#mdig)
- [Способы получения списка заблокированных IP](#способы-получения-списка-заблокированных-ip)
- [Фильтрация по именам доменов](#фильтрация-по-именам-доменов)
- [Режим фильтрации autohostlist](#режим-фильтрации-autohostlist)
- [Проверка провайдера](#проверка-провайдера)
@@ -57,6 +62,7 @@ zapret является свободным и open source.
- [Прикручивание к системе управления фаерволом или своей системе запуска](#прикручивание-к-системе-управления-фаерволом-или-своей-системе-запуска)
- [Вариант custom](#вариант-custom)
- [Простая установка](#простая-установка)
- [Установка под systemd](#установка-под-systemd)
- [Простая установка на openwrt](#простая-установка-на-openwrt)
- [Установка на openwrt в режиме острой нехватки места на диске](#установка-на-openwrt-в-режиме-острой-нехватки-места-на-диске)
- [Android](#android)
@@ -159,6 +165,7 @@ dvtws, собираемый из тех же исходников (см. [док
--debug=0|1 ; 1=выводить отладочные сообщения
--dry-run ; проверить опции командной строки и выйти. код 0 - успешная проверка.
--version ; вывести версию и выйти
--comment ; любой текст (игнорируется)
--daemon ; демонизировать прогу
--pidfile=<file> ; сохранить PID в файл
@@ -167,10 +174,31 @@ dvtws, собираемый из тех же исходников (см. [док
--qnum=N ; номер очереди N
--bind-fix4 ; пытаться решить проблему неверного выбора исходящего интерфейса для сгенерированных ipv4 пакетов
--bind-fix6 ; пытаться решить проблему неверного выбора исходящего интерфейса для сгенерированных ipv6 пакетов
--ctrack-timeouts=S:E:F[:U] ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN, таймаут udp. по умолчанию 60:300:60:60
--ctrack-disable=[0|1] ; 1 или остутствие аргумента отключает conntrack
--ipcache-lifetime=<int> ; время жизни записей кэша IP в секундах. 0 - без ограничений.
--ipcache-hostname=[0|1] ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы
--wsize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в SYN,ACK. если не задан scale_factor, то он не меняется (устарело !)
--wssize=<winsize>[:<scale_factor>] ; менять tcp window size на указанный размер в исходящих пакетах. scale_factor по умолчанию 0. (см. conntrack !)
--wssize-cutoff=[n|d|s]N ; изменять server window size в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--ctrack-timeouts=S:E:F[:U] ; таймауты внутреннего conntrack в состояниях SYN, ESTABLISHED, FIN, таймаут udp. по умолчанию 60:300:60:60
--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. "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. "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
--dup-start=[n|d|s]N ; применять dup только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--dup-cutoff=[n|d|s]N ; применять dup только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:".
--hostnospace ; убрать пробел после "Host:" и переместить его в конец значения "User-Agent:" для сохранения длины пакета
--methodeol ; добавить перевод строки в unix стиле ('\n') перед методом и убрать пробел из Host: : "GET / ... Host: domain.com" => "\nGET / ... Host:domain.com"
@@ -179,9 +207,9 @@ dvtws, собираемый из тех же исходников (см. [док
--dpi-desync=[<mode0>,]<mode>[,<mode2] ; атака по десинхронизации DPI. mode : synack syndata fake fakeknown rst rstack hopbyhop destopt ipfrag1 multisplit multidisorder fakedsplit fakeddisorder ipfrag2 udplen tamper
--dpi-desync-fwmark=<int|0xHEX> ; бит fwmark для пометки десинхронизирующих пакетов, чтобы они повторно не падали в очередь. default = 0x40000000
--dpi-desync-ttl=<int> ; установить ttl для десинхронизирующих пакетов
--dpi-desync-ttl6=<int> ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение 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-ttl6=<int> ; установить ipv6 hop limit для десинхронизирующих пакетов. если не указано, используется значение --dpi-desync-ttl
--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
@@ -193,18 +221,20 @@ dvtws, собираемый из тех же исходников (см. [док
--dpi-desync-badack-increment=<int|0xHEX> ; инкремент ack sequence number для badseq. по умолчанию -66000
--dpi-desync-any-protocol=0|1 ; 0(default)=работать только по http request и tls clienthello 1=по всем непустым пакетам данных
--dpi-desync-fake-http=<filename>|0xHEX ; файл, содержащий фейковый http запрос для dpi-desync=fake, на замену стандартному www.iana.org
--dpi-desync-fake-tls=<filename>|0xHEX ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному
--dpi-desync-fake-tls-mod=mod[,mod] ; список через запятую режимов runtime модификации фейков : none,rnd,rndsni,padencap
--dpi-desync-fake-tls=<filename>|0xHEX|! ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному. '!' = стандартный фейк
--dpi-desync-fake-tls-mod=mod[,mod] ; список через запятую режимов runtime модификации фейков : none,rnd,rndsni,sni=<sni>,dupsid,padencap
--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-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 байт
--dpi-desync-fake-unknown-udp=<filename>|0xHEX ; файл, содержащий фейковый пейлоад неизвестного udp протокола для dpi-desync=fake, на замену стандартным нулям 64 байт
--dpi-desync-udplen-increment=<int> ; насколько увеличивать длину udp пейлоада в режиме udplen
--dpi-desync-udplen-pattern=<filename>|0xHEX ; чем добивать udp пакет в режиме udplen. по умолчанию - нули
--dpi-desync-start=[n|d|s]N ; применять dpi desync только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--dpi-desync-cutoff=[n|d|s]N ; применять dpi desync только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются.
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются, если хост не начинается с '^'.
; в файле должен быть хост на каждой строке.
; список читается при старте и хранится в памяти в виде иерархической структуры для быстрого поиска.
; при изменении времени модификации файла он перечитывается автоматически по необходимости
@@ -224,7 +254,7 @@ dvtws, собираемый из тех же исходников (см. [док
--filter-l3=ipv4|ipv6 ; фильтр версии ip для текущей стратегии
--filter-tcp=[~]port1[-port2]|* ; фильтр портов tcp для текущей стратегии. ~ означает инверсию. установка фильтра tcp и неустановка фильтра udp запрещает udp. поддерживается список через запятую.
--filter-udp=[~]port1[-port2]|* ; фильтр портов udp для текущей стратегии. ~ означает инверсию. установка фильтра udp и неустановка фильтра tcp запрещает tcp. поддерживается список через запятую.
--filter-l7=[http|tls|quic|wireguard|dht|unknown] ; фильтр протокола L6-L7. поддерживается несколько значений через запятую.
--filter-l7=<proto> ; фильтр протокола L6-L7. поддерживается несколько значений через запятую. proto : http tls quic wireguard dht discord stun unknown
--ipset=<filename> ; включающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
--ipset-ip=<ip_list> ; фиксированный список подсетей через запятую. можно использовать # в начале для комментирования отдельных подсетей.
--ipset-exclude=<filename> ; исключающий ip list. на каждой строчке ip или cidr ipv4 или ipv6. поддерживается множество листов и gzip. перечитка автоматическая.
@@ -259,6 +289,10 @@ dvtws, собираемый из тех же исходников (см. [док
Есть ряд методов для решения этой задачи.
* `md5sig` добавляет TCP опцию **MD5 signature**. Работает не на всех серверах. Пакеты с md5 обычно отбрасывают только linux.
Требуется значительное увеличение длины tcp пакета, чтобы вместить tcp option. При обработке многосегментных запросов (TLS Kyber)
первый пакет идет полный под MTU. При fakedsplit/fakeddisorder на небольших позициях отдельные tcp сегменты достаточно велики, чтобы внедрение
md5 tcp option вызвало переполнение MTU и ошибку отправки "message too long". `nfqws` не умеет перераспределять данные между tcp сегментами,
поэтому надо или отказываться от kyber, или увеличивать сплит-позицию, или отказываться от fakedsplit/fakeddisorder.
* `badsum` портит контрольную сумму TCP. Не сработает, если ваше устройство за NAT, который не пропускает пакеты с инвалидной суммой. Наиболее
распространенная настройка NAT роутера в Linux их не пропускает. На Linux построено большинство домашних роутеров.
Непропускание обеспечивается так : настройка ядра sysctl по умолчанию
@@ -279,7 +313,7 @@ dvtws, собираемый из тех же исходников (см. [док
Такие пакеты будут наверняка отброшены принимающим узлом, но так же и DPI, если он ориентируется на sequence
numbers. По умолчанию смещение seq выбирается -10000. Практика показала, что некоторые DPI не пропускают seq вне
определенного окна. Однако, такое небольшое смещение может вызвать проблемы при существенной потоковой передаче и
потере пакетов. Если вы используете `--dpi-desync-any-protocol`, может понадобится установить badseq increment
потере пакетов. Если вы используете `--dpi-desync-any-protocol`, может понадобиться установить badseq increment
0x80000000. Это обеспечит надежную гарантию, что поддельный пакет не вклинится в tcp window на сервере. Так же было
замечено, что badseq ломает логику некоторых DPI при анализе http, вызывая зависание соединения. Причем на тех же DPI
TLS с badseq работает нормально.
@@ -294,7 +328,7 @@ dvtws, собираемый из тех же исходников (см. [док
* `hopbyhop` относится только к ipv6. Добавляется ipv6 extenstion header `hop-by-hop options`. В варианте `hopbyhop2`
добавляются 2 хедера, что является нарушением стандарта и гарантированно отбрасывается стеком протоколов во всех ОС.
Один хедер hop-by-hop принимается всеми ОС, однако на некоторых каналах/провайдерах такие пакеты могут фильтроваться и
не доходить. Расчет идет на то, что DPI проанализирует пакет с hop-by-hop, но он либо не дойдет до адресата всилу
не доходить. Расчет идет на то, что DPI проанализирует пакет с hop-by-hop, но он либо не дойдет до адресата в силу
фильтров провайдера, либо будет отброшен сервером, потому что хедера два.
* `datanoack` высылает фейки со снятым tcp флагом ACK. Сервера такое не принимают, а DPI может принять. Эта техника
может ломать NAT и не всегда работает с iptables, если используется masquerade, даже с локальной системы (почти всегда
@@ -302,18 +336,27 @@ dvtws, собираемый из тех же исходников (см. [док
выяснено, что многие провайдерские NAT не отбрасывают эти пакеты, потому работает даже с внутренним провайдерским IP.
Но linux NAT оно не пройдет, так что за домашним роутером эта техника скорее всего не сработает, но может сработать с него.
Может сработать и через роутер, если подключение по проводу, и на роутере включено аппаратное ускорение.
* `autottl`. Суть режима в автоматическом определении TTL, чтобы он почти наверняка прошел DPI и немного не дошел до
сервера. Берутся базовые значения TTL 64,128,255, смотрится входящий пакет
(да, требуется направить первый входящий пакет на nfqws !). Вычисляется длина пути, отнимается `delta` (1 по
умолчанию). Если TTL вне диапазона (min,max - 3,20 по умолчанию), то берутся значения min,max, чтобы вписаться в
диапазон. Если при этом полученный TTL больше длины пути, то автоматизм не сработал и берутся фиксированные значения
TTL для атаки. Техника позволяет решить вопрос, когда вся сеть перегорожена шлагбаумами (DPI, ТСПУ) везде где только
* `autottl`. Суть режима в автоматическом определении TTL, чтобы пакет почти наверняка прошел DPI и немного не дошел до
сервера (`--dpi-desync-autottl`). Или наоборот - TTL едва хватило, чтобы он все-таки дошел до сервера (см `--dup-autottl`, `--orig-autottl`).
Берутся базовые значения TTL 64,128,255, смотрится входящий пакет (да, требуется направить первый входящий пакет на nfqws !).
Вычисляется длина пути, прибавляется `delta`. delta может быть положительной или отрицательной.
Чтобы задать положительную дельту, нужно указать унарный знак **+** перед числом.
В случае его отсутствия или при наличии унарного знака **-** дельта считается отрицательной.
Если TTL вне диапазона min,max, то берутся значения min,max, чтобы вписаться в
диапазон. Если при этом дельта отрицательная и полученный TTL больше длины пути или дельта положительная и полученный TTL меньше длины пути,
то автоматизм не сработал и берутся фиксированные значения : `--dpi-desync-ttl`, `--orig-ttl`, `--dup-ttl`.
Техника позволяет решить вопрос, когда вся сеть перегорожена шлагбаумами (DPI, ТСПУ) везде где только
можно, включая магистралов. Но потенциально может давать сбои. Например, при асимметрии входящего и исходящего канала
до конкретного сервера. На каких-то провайдерах эта техника будет работать неплохо, на других доставит больше проблем,
до конкретного сервера. Некоторые сервера выдают нестандартный TTL (google), потому на них получается полная ерунда.
Если не учитывать подобные исключения, то на каких-то провайдерах эта техника будет работать неплохо, на других доставит больше проблем,
чем пользы. Где-то может потребоваться тюнинг параметров. Лучше использовать с дополнительным ограничителем.
Режимы дурения могут сочетаться в любых комбинациях. `--dpi-desync-fooling` берет множество значений через запятую.
Возможно задание множества фейков через повторение парамеров `--dpi-desync-fake-???`, кроме `--dpi-desync-fake-syndata`.
Фейки будут отосланы в указанном порядке. `--dpi-desync-repeats` повторяет каждый отосланный фейк.
Итоговый порядок будет такой : `fake1 fake1 fake1 fake2 fake2 fake2 fake3 fake3 fake3 .....`
### МОДИФИКАЦИЯ ФЕЙКОВ
В nfqws зашит базовый вариант фейка для TLS. Его можно переопределить опцией `--dpi-desync-fake-tls`.
@@ -326,11 +369,23 @@ dvtws, собираемый из тех же исходников (см. [док
* `none`. Не применять никакие модификации.
* `rnd`. Рандомизировать поля `random` и `session id`. Выполняется на каждый запрос.
* `dupsid`. Копировать `session ID` из передаваемого TLS Client Hello. Имеет приоритет над `rnd`. Выполняется на каждый запрос.
* `rndsni`. Рандомизировать SNI. Если SNI >=7 символов, применяется случайный домен 2 уровня с известным TLD, иначе заполняется случайными символами без точки. Выполняется один раз при старте.
* `sni=<sni>`. Заменить sni на указанное значение. Макс длина SNI - 63 байта. Общая длина TLS фейка и длины в структуре TLS Client Hello меняются. Выполняется один раз при старте. Если сочетается с `rndsni`, выполняется до него.
* `padencap`. Расширяется padding extension на размер передаваемого TLS Client Hello (включая многопакетный вариант с kyber). Если padding отсутствует, он добавляется в конец. Если присутствует - требуется, чтобы padding шел последним extension. Правятся все длины, чтобы создать видимость включения передаваемого TLS Client Hello в padding extension. Размер фейка не изменяется. Расчет идет на DPI, который не анализирует sequence numbers должным образом. Выполняется на каждый запрос.
По умолчанию если не задан собственный фейк для TLS используются модификации `rnd,rndsni`. Если фейк задан, используется `none`.
Это соответствует поведению программы более старых версий.
По умолчанию если не задан собственный фейк для TLS используются модификации `rnd,rndsni,dupsid`. Если фейк задан, используется `none`.
Это соответствует поведению программы более старых версий с добавлением функции `dupsid`.
Если задан режим модификации и имеется множество TLS фейков, к каждому из них применяется последний режим модификации.
Если режим модификации задан после фейка, то он замещает предыдущий режим.
Таким образом можно использовать разные режимы модификации для разных фейков.
При невозможности модифицировать фейк на этапе запуска программа завершается с ошибкой.
Если сначала идет TLS фейк, для него задан режим однократной модификации, затем идет не TLS фейк, то будет ошибка.
Нужно использовать `--dpi-desync-fake-tls-mod=none'.
Пример : `--dpi-desync-fake-tls=iana_org.bin --dpi-desync-fake-tls-mod=rndsni --dpi-desync-fake-tls=0xaabbccdd --dpi-desync-fake-tls-mod=none'
### TCP СЕГМЕНТАЦИЯ
@@ -344,6 +399,9 @@ dvtws, собираемый из тех же исходников (см. [док
Размеры фейков соответствуют длинам отсылаемых частей.
Цель этих режимов - максимально усложнить выявление оригинальных данных среди фейков.
Использование `fakedsplit` или `fakeddisorder` на TLS kyber с md5sig fooling может привести к ошибкам "message too long", если позиция сплита мала,
поскольку будет превышение MTU из-за md5 tcp option.
Для определения позиций нарезки используются маркеры.
* **Абсолютный положительный маркер** - числовое смещение внутри пакета или группы пакетов от начала.
@@ -423,16 +481,69 @@ extension хедерам в поисках транспортного хедер
При `hopbyhop,ipfrag2` последовательность хедеров будет : `ipv6,hop-by-hop`,`fragment`,`tcp/udp`.
Режим `ipfrag1` может срабатывать не всегда без специальной подготовки. См. раздел `IP фрагментация`.
### МОДИФИКАЦИЯ ОРИГИНАЛА
Параметры `--orig-ttl` и `--orig-ttl6` позволяют изменить TTL оригинальных пакетов.
Если дальнейшие манипуляции связаны с оригиналом, например, идет TCP сегментация, то исходными
данными являются измененные оригинальные пакеты. То есть в данном примере TCP сегменты пойдут с измененным TTL.
Вариант `--orig-autottl` и `--orig-autottl6` работает аналогично `dpi-desync-autottl`, но по оригинальным пакетам.
Дельту стоит указывать положительную с унарным знаком `+`, иначе оригинал не дойдет до сервера, и вы вообще ничего не получите.
Пример : `--orig-autottl=+5:3-64`.
`--orig-mod-start` и `--orig-mod-cutoff` задают ограничитель по началу и концу модификации оригинала.
Схема аналогична `--dpi-desync-start` и `--dpi-desync-cutoff`.
Функция может быть полезна, когда DPI охотится за фейками и блокирует соединение при наличии подозрительных признаков,
в частности, измененный TTL у фейка относительно оригинала.
### ДУБЛИКАТЫ
Дубликаты - это копии оригинальных пакетов, высылаемые перед ними. Включаются параметром `--dup=N`, где N - количество дублей,
не включающее оригинал. `--dup-replace` отключает отсылку оригинала.
Отсылка дублей имеет место только в тех случаях, когда высылается и оригинал без реконструкции.
Например, если случилась TCP сегментация, то оригинал фактически дропается и заменяется искусственно сконструированными сегментами.
Дубли высланы не будут. Это же касается изменения состава хедеров ipv6, режима tamper для DHT и других.
Возможно применение всех вариантов дурения, как и для desync : `--dup-ttl`. `--dup-ttl6`, `--dup-fooling`. Нужно ли, чтобы эти пакеты доходили до сервера и в каком виде, решаете вы согласно задуманной стратегии.
Вариант `--dup-autottl` и `--dup-autottl6` работает аналогично `dpi-desync-autottl`, но по дублям.
Дельту можно указывать положительную с унарным знаком `+`, а можно и отрицательную. Зависит от вашей задумки.
Пример : `--dup-autottl=-2:3-64`.
`--dup-start` и `--dup-cutoff` задают ограничитель по началу и концу применения стратегии дубликатов.
Схема аналогична `--dpi-desync-start` и `--dpi-desync-cutoff`.
Функция может помочь, когда DPI сечет разницу в характеристиках фейков и оригинала.
Дубликатами можно попытаться заставить DPI принять , что весь сеанс идет аномальным.
Например, у нас имеется TCP сеанс с MD5 сразу с первого SYN пакета. Значит последующие MD5 будут восприниматься нормально.
### КОМБИНИРОВАНИЕ МЕТОДОВ ДЕСИНХРОНИЗАЦИИ
В параметре dpi-desync можно указать до 3 режимов через запятую.
* 0 фаза - предполагает работу на этапе установления соединения : `synack`, `syndata`, `--wsize`, `--wssize`. На эту фазу не действуют фильтры по [hostlist](#множественные-стратегии).
* 0 фаза - предполагает работу на этапе установления соединения : `synack`, `syndata`, `--wsize`, `--wssize`. На эту фазу не действуют фильтры по [hostlist](#множественные-стратегии), кроме случая, описанного [далее](#кэш-ip).
* 1 фаза - отсылка чего-либо до оригинального пакета данных : `fake`, `rst`, `rstack`.
* 2 фаза - отсылка в модифицированном виде оригинального пакета данных (например, `fakedsplit` или `ipfrag2`).
Режимы требуют указания в порядке возрастания номеров фаз.
### КЭШ IP
ipcache представляет собой структуру в памяти процесса, позволяющую по ключу IP адреса и имени интерфейса запоминать некоторую информацию,
которую впоследствии можно извлечь и использовать как недостающие данные. На текущий момент это применяются в следующих ситуациях :
1. IP,interface => hop count . Кэшируется количество хопов до сервера для последующего применения в autottl прямо с первого пакета, когда еще ответа не было. Пока записи в кэше нет, autottl не будет применен сразу. При повторном запросе до истечения времени жизни записи autottl будет применение сразу.
2. IP => hostname . Кэшируется имя хоста, вне привязки к интерфейсу, для последующего применения в стратегиях нулевой фазы. Режим отключен по умолчанию и включается через параметры `ipcache-hostname`.
Данная техника является экспериментальной. Ее проблема в том, что как такового нет однозначного соответствия между доменом и IP. Множество доменов могут ссылаться на тот же IP адрес.
При коллизии происходит замещение имени хоста на последний вариант.
Домен может скакать по разным IP на CDN. Сейчас один адрес, через час - другой. Эта проблема решается через время жизни записей кэша : `--ipcache-lifetime`. По умолчанию 2 часа.
Однако, может случиться и так, что в вашем случае применение техники несет больше пользы, чем проблем. Будьте готовы к непонятному на первый взгляд поведению, которое может быть исследовано только через `--debug` лог.
При подаче сигнала SIGUSR2 процесс выводит содержимое ipcache на консоль.
### РЕАКЦИЯ DPI НА ОТВЕТ СЕРВЕРА
Есть DPI, которые анализируют ответы от сервера, в частности сертификат из ServerHello, где прописаны домены.
@@ -528,8 +639,8 @@ window size итоговый размер окна стал максимальн
### РЕАССЕМБЛИНГ
nfqws поддерживает реассемблинг некоторых видов запросов.
На текущий момент это TLS и QUIC ClientHello. Они бывает длинными, если в chrome включить пост-квантовую
криптографию tls-kyber, и занимают как правило 2 или 3 пакета. kyber включен по умолчанию, начиная с chromium 124.
На текущий момент это TLS и QUIC ClientHello. Они бывают длинными, если в chrome включить пост-квантовую
криптографию tls-kyber, и занимают, как правило, 2 или 3 пакета. kyber включен по умолчанию, начиная с chromium 124.
chrome рандомизирует фингерпринт TLS. SNI может оказаться как в начале, так и в конце, то есть
попасть в любой пакет. stateful DPI обычно реассемблирует запрос целиком, и только потом
принимает решение о блокировке.
@@ -558,7 +669,8 @@ chrome рандомизирует фингерпринт TLS. SNI может о
На текущий момент работает только с DHT.
Поддерживается определение пакетов QUIC Initial с расшифровкой содержимого и имени хоста, то есть параметр
`--hostlist` будет работать.
Определяются пакеты wireguard handshake initiation и DHT (начинается с 'd1', кончается 'e').
Определяются пакеты wireguard handshake initiation, DHT (начинается с 'd1', кончается 'e'), STUN и
[Discord Voice IP Discovery](https://discord.com/developers/docs/topics/voice-connections#ip-discovery).
Для десинхронизации других протоколов обязательно указывать `--dpi-desync-any-protocol`.
Реализован conntrack для udp. Можно пользоваться --dpi-desync-cutoff. Таймаут conntrack для udp
можно изменить 4-м параметром в `--ctrack-timeouts`.
@@ -618,7 +730,7 @@ options ip6table_raw raw_before_defrag=1
При использовании iptables и NAT, похоже, что нет способа прицепить обработчик очереди после NAT.
Пакет попадает в nfqws с source адресом внутренней сети, затем фрагментируется и уже не обрабатывается NAT.
Так и уходит во внешюю сеть с src ip 192.168.x.x. Следовательно, метод не срабатывает.
Так и уходит во внешнюю сеть с src ip 192.168.x.x. Следовательно, метод не срабатывает.
Видимо единственный рабочий метод - отказаться от iptables и использовать nftables.
Хук должен быть с приоритетом 101 или выше.
@@ -642,7 +754,7 @@ L7 протокол становится известен обычно посл
Если имя хоста удовлетворяет листам, выбирается этот профиль. Иначе идет переход к следующему.
Может так случиться, что до получения имени хоста или узнавания L7 протокола соединение идет по одному профилю,
а при выяснении этих параметров профиль меняется на лету. Это может произойти даже дважды - при выяснении L7
и имени хоста. Чаще всего это выяснение совмещается в одно действие, поскольку по одному пакету как правило узнается и L7, и хост.
и имени хоста. Чаще всего это выяснение совмещается в одно действие, поскольку по одному пакету, как правило, узнается и L7, и хост.
Поэтому если у вас есть параметры дурения нулевой фазы, тщательно продумывайте что может произойти при переключении стратегии.
Смотрите debug log, чтобы лучше понять что делает nfqws.
Нумерация профилей идет с 1 до N. Последним в цепочке создается пустой профиль с номером 0.
@@ -656,7 +768,7 @@ L7 протокол становится известен обычно посл
> [!IMPORTANT]
> user-mode реализация ipset создавалась не как удобная замена *nix версии, реализованной в ядре.
> Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без подержки ipset в ядре.
> Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без подд3ержки ipset в ядре.
> Конкретно - Windows и ядра Linux, собранные без nftables и ipset модулей ядра. Например, в android нет ipset.
### IPTABLES ДЛЯ NFQWS
@@ -676,7 +788,7 @@ iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp
```
mark нужен, чтобы сгенерированный поддельный пакет не попал опять к нам на обработку. nfqws выставляет fwmark при его отсылке.
хотя nfqws способен самостоятельно различать помеченные пакеты, фильтр в iptables по mark нужен при использовании connbytes,
Хотя nfqws способен самостоятельно различать помеченные пакеты, фильтр в iptables по mark нужен при использовании connbytes,
чтобы не допустить изменения порядка следования пакетов. Процессинг очереди - процесс отложенный.
Если ядро имеет пакеты на отсылку вне очереди - оно их отправляет незамедлительно.
Изменение правильного порядка следования пакетов при десинхронизации ломает всю идею.
@@ -689,7 +801,7 @@ mark нужен, чтобы сгенерированный поддельный
* 3 - стандартная ситуация приема одного пакета запроса
* 4-6 - на случай ретрансмиссии или запроса длиной в несколько пакетов (TLSClientHello с kyber, например)
Для режима autottl необходимо перенаправление входящего `SYN,ACK` пакета или первого пакета соединения (что обычно есть тоже самое).
Для режима autottl необходимо перенаправление входящего `SYN,ACK` пакета или первого пакета соединения (что обычно есть то же самое).
Для режима autohostlist необходимы входящие RST и http redirect.
Можно построить фильтр на tcp flags для выделения `SYN,ACK` и модуле u32 для поиска характерных паттернов http redirect,
но проще использовать connbytes для выделения нескольких начальных входящих пакетов.
@@ -759,12 +871,47 @@ iptables могут не работать. При включенном offloadin
Пакеты, проходящие через SFO, так же проходят мимо большей части механизмов iptables. При включенном SFO работает
DNAT/REDIRECT (tpws). Эти соединения исключаются из offloading. Однако, остальные соединения идут через SFO, потому
NFQUEUE будет срабатывать только до помещения соединения в flowtable. Практически это означает, что почти весь функционал nfqws работать не будет.
Offload включается через специальный target в iptables `FLOWOFFLOAD`. Не обязательно пропускать весь трафик через offload. Можно исключить из
offload соединения, которые должны попасть на tpws или nfqws. OpenWrt не предусматривает выборочного управления offload.
Поэтому скрипты zapret поддерживают свою систему выборочного управления offload в OpenWrt.
Offload включается через специальный target в iptables `FLOWOFFLOAD` или через flowtable в nftables.
Не обязательно пропускать весь трафик через offload.
tpws и так обходит offload "by design", а для отработки nfqws достаточно первых нескольких пакетов в tcp соединении или udp сеансе.
Пока сеанс не направлен на offload, он процессится обычным образом через полноценный netfilter.
Как только срабатывает правило offload по любому входящему или исходящему пакету, весь сеанс окончательно уходит из netfilter в offload.
Поэтому скрипты zapret берут правила для NFQUEUE, что они создали, и из них создают exemption правила, которые не дают раньше времени попасть сеансу в offload, а потом его "отпускают".
При этом входящим пакетам не дают начать offload, триггером выступают только исходящие пакеты.
Эта схема обеспечивает практически нулевой негативный эффект на скорость, одновременно покрывая нужды nfqws и упрощая правила таблиц.
OpenWrt не предусматривает выборочного управления offload, поэтому скрипты zapret поддерживают свою систему выборочного управления.
iptables target `FLOWOFFLOAD` - это проприетарное изобретение OpenWrt.
Управление offload в nftables реализовано в базовом ядре linux без патчей.
nftables - единственный способ включения offload на классическом Linux.
### ДУРЕНИЕ СО СТОРОНЫ СЕРВЕРА
Это тоже возможно.
nfqws рассчитан на атаку со стороны клиента, поэтому он распознает прямой и обратный трафик на основании роли в установлении tcp соединения.
Если проходит SYN, то source IP - это клиент. Если проходит SYN,ACK , то source IP - это сервер.
Для UDP клиентом считается source IP первого прошедшего пакета по двум связкам ip-port.
На сервере трафиком клиента будет считаться принятый трафик, а трафиком сервера - исходящий.
`--wsize` работает в любом случае, он может использоваться как на клиенте, так и на сервере.
Остальные техники работают только если nfqws считает трафик трафиком клиента.
Поэтому для их применения по исходящему с сервера трафику conntrack нужно выключить параметром `--ctrack-disable`.
Если пакет не найден в conntrack, по нему идет работа как по пакету клиента.
Большинство протоколов опознаваться не будет, потому что система их опознавания рассчитана на содержание пакетов от клиента.
Чтобы задействовать техники типа `fake` или `multisplit` нужно использовать `--dpi-desync-any-protocol` с ограничителем connbytes или
с ограничителем на основании содержания пакета или его заголовков.
start/cutoff недоступны, поскольку завязаны на conntrack.
Техника `synack-split` позволяет разбить tcp сегмент SYN,ACK на отдельные части с SYN и с ACK.
В ответ на это клиент шлет SYN,ACK , что обычно характеризует сервер.
У некоторых DPI от этого может ломаться алгоритм, и они перестают блокировать запрещенный контент.
Здесь [подробное описание](https://nmap.org/misc/split-handshake.pdf) что есть split handshake.
Перенаправление трафика обычно идет по номеру source портов и направлению original.
original - это исходящий с системы трафик, reply - входящий.
## tpws
@@ -776,6 +923,7 @@ tpws - это transparent proxy.
--debug=0|1|2|syslog|@<filename> ; 0,1,2 = логирование на косоль : 0=тихо, 1(default)=подробно, 2=отладка.
--debug-level=0|1|2 ; указать уровень логирования для syslog и @<filename>
--dry-run ; проверить опции командной строки и выйти. код 0 - успешная проверка.
--version ; вывести версию и выйти
--daemon ; демонизировать прогу
--pidfile=<file> ; сохранить PID в файл
@@ -823,6 +971,8 @@ tpws - это transparent proxy.
--local-tcp-user-timeout=<seconds> ; таймаут соединений client-proxy (по умолчанию : 10 сек, 0 = оставить системное значение)
--remote-tcp-user-timeout=<seconds> ; таймаут соединений proxy-target (по умолчанию : 20 сек, 0 = оставить системное значение)
--fix-seg=<int> ; исправлять неудачи tcp сегментации ценой задержек для всех клиентов и замедления. ждать до N мс. по умолчанию 30 мс.
--ipcache-lifetime=<int> ; время жизни записей кэша IP в секундах. 0 - без ограничений.
--ipcache-hostname=[0|1] ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы
--split-pos=N|-N|marker+N|marker-N ; список через запятую маркеров для tcp сегментации
--split-any-protocol ; применять сегментацию к любым пакетам. по умолчанию - только к известным протоколам (http, TLS)
@@ -843,7 +993,7 @@ tpws - это transparent proxy.
--mss=<int> ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость
--tamper-start=[n]<pos> ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока)
--tamper-cutoff=[n]<pos> ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока)
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются.
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются, если хост не начинается с '^'.
; в файле должен быть хост на каждой строке.
; список читается при старте и хранится в памяти в виде иерархической структуры для быстрого поиска.
; при изменении времени модификации файла он перечитывается автоматически по необходимости
@@ -880,7 +1030,7 @@ tpws, как и nfqws, поддерживает множественную се
Однако, если в момент send уже имеется неотосланный буфер, то ОС присоединит данные к нему,
никакой отсылки отдельным пакетом не будет. Но в этом случае и так нет никакой гарантии,
что какой-то блок сообщения пойдет в начале пакета, на что собственно и заточены DPI.
Разбиение будет производится согласно MSS, который зависит от MTU исходящего интерфейса.
Разбиение будет производиться согласно MSS, который зависит от MTU исходящего интерфейса.
Таким образом DPI, смотрящие в начало поля данных TCP пакета, будут поломаны в любом случае.
Протокол http относится к запрос-ответным протоколам. Новое сообщение посылается только тогда,
когда сервер получил запрос и полностью вернул ответ. Значит запрос фактически был не только отослан,
@@ -893,13 +1043,15 @@ tpws, как и nfqws, поддерживает множественную се
указанным сплит позициям. Другие ОС в этом вопросе ведут себя более предсказуемо. Спонтанного обьединения замечено не было.
Поэтому не стоит злоупотреблять сплитами и в особенности мелкими соседними пакетами.
Как показывается практика, проблемы могут начаться , если количество сплит позиций превышает 8.
Как показывается практика, проблемы могут начаться , если количество сплитов более одного.
На каких-то системах наблюдался стабильный результат до 8 сплитов, на других проблемы уже начинались после 2 сплитов.
Один сплит работает стабильно, если не является частью массивной потоковой передачи.
При неудаче сегментации будет выводиться сообщение `WARNING ! segmentation failed`.
Если вы его видите, это повод снизить количество сплит позиций.
Если это не вариант, для ядер Linux >=4.6 есть параметр `--fix-seg`. Он позволяет подождать завершение отсылки перед отправкой следующей части.
Но этот вариант ломает модель асинхронной обработки событий. Пока идет ожидание, все остальные соединения не обрабатываются
и кратковременно подвисают. На практике это может быть совсем небольшое ожидание - менее 10 мс.
И производится оно только , если происходит split, и в ожидании есть реальная необходимость.
Выполняется оно только , если происходит split, и в ожидании есть реальная необходимость.
В высоконагруженных системах данный вариант не рекомендуется. Но для домашнего использования может подойти, и вы эти задержки даже не заметите.
Если вы пытаетесь сплитнуть массивную передачу с `--split-any-protocol`, когда информация поступает быстрее отсылки,
@@ -942,9 +1094,9 @@ tpws работает на уровне сокетов, поэтому длин
шлет сервер. На TLS 1.2 если сервер разбил заброс так, чтобы домен из сертификата не попал в первый пакет,
это может обмануть DPI, секущий ответ сервера.
Схема может значительно снизить скорость и сработать не на всех сайтах.
С фильтром по hostlist совместимо только в режиме socks при включенном удаленном ресолвинге хостов.
(firefox network.proxy.socks_remote_dns). Это единственный вариант, когда tpws может узнать имя хоста
еще на этапе установления соединения.
С фильтром по hostlist совместимо только в [некоторых случаях](#множественные-стратегии-1), когда возможно узнать имя хоста на момент применения дурения.
Применяя данную опцию к сайтам TLS1.3, если броузер тоже поддерживает TLS1.3, то вы делаете только хуже.
Но нет способа автоматически узнать когда надо применять, когда нет, поскольку MSS идет только в
3-way handshake еще до обмена данными, а версию TLS можно узнать только по ответу сервера, который
@@ -968,14 +1120,17 @@ tpws работает на уровне сокетов, поэтому длин
Работают аналогично **nfqws**, кроме некоторых моментов.
Нет параметра `--filter-udp`, поскольку **tpws** udp не поддерживает.
Методы нулевой фазы (`--mss`) могут работать по хостлисту в одном единственном случае:
если используется режим socks и удаленный ресолвинг хостов через прокси.
То есть работоспособность вашей настройки в одном и том же режиме может зависеть от того,
применяет ли клиент удаленный ресолвинг. Это может быть неочевидно.
В одной программе работает, в другой - нет.
Если вы используете профиль с хостлистом , и вам нужен mss, укажите mss в профиле с хостлистом,
Методы нулевой фазы (`--mss`) могут работать по хостлисту только в двух случаях:
если используется режим socks и удаленный ресолвинг хостов через прокси, либо используется система [кэша IP](#кэш-ip) для запоминания соответствия IP->hostname.
Работоспособность вашей настройки в одном и том же режиме может зависеть от того,
применяет ли клиент удаленный ресолвинг. Это может быть неочевидно. В одной программе работает, в другой - нет.
Если вы используете профиль с хостлистом , и вам нужен mss всегда, укажите mss в профиле с хостлистом,
создайте еще один профиль без хостлиста, если его еще нет, и в нем еще раз укажите mss.
Тогда при любом раскладе будет выполняться mss.
Если вам нужен mss по хостлисту, указывайте `--mss` только в профиле с хостлистом и убедитесь в наличии любого из необходимых условий работы в таком режиме.
Используйте `curl --socks5` и `curl --socks5-hostname` для проверки вашей стратегии.
Смотрите вывод `--debug`, чтобы убедиться в правильности настроек.
@@ -1066,7 +1221,7 @@ route_localnet :
динамически вписывать в команду. В любом случае требуются дополнительные усилия. Использование route_localnet тоже имеет
потенциальные проблемы с безопасностью. Вы делаете доступным все, что висит на `127.0.0.0/8` для локальной подсети <
внутренний_интерфейс>. Службы обычно привязываются к `127.0.0.1`, поэтому можно средствами iptables запретить входящие
на `127.0.0.1` не с интерфейса lo, либо повесить tpws на любой другой IP из из `127.0.0.0/8`, например на `127.0.0.127`,
на `127.0.0.1` не с интерфейса lo, либо повесить tpws на любой другой IP из `127.0.0.0/8`, например на `127.0.0.127`,
и разрешить входящие не с lo только на этот IP.
```
@@ -1115,6 +1270,78 @@ nft add rule inet ztest dnat_pre meta iifname $IFACE_LAN tcp dport { 80, 443 } d
nft delete table inet ztest
```
## ip2net
Утилита ip2net предназначена для преобразования ipv4 или ipv6 списка ip в список подсетей
с целью сокращения размера списка. Входные данные берутся из stdin, выходные выдаются в `stdout`.
```
-4 ; лист - ipv4 (по умолчанию)
-6 ; лист - ipv6
--prefix-length=min[-max] ; диапазон рассматриваемых длин префиксов. например : 22-30 (ipv4), 56-64 (ipv6)
--v4-threshold=mul/div ; ipv4 : включать подсети, в которых заполнено по крайней мере mul/div адресов. например : 3/4
--v6-threshold=N ; ipv6 : минимальное количество ip для создания подсети
```
В списке могут присутствовать записи вида ip/prefix и ip1-ip2. Такие записи выкидываются в stdout без изменений.
Они принимаются командой ipset. ipset умеет для листов hash:net из ip1-ip2 делать оптимальное покрытие ip/prefix.
ipfw из FreeBSD понимает ip/prefix, но не понимает ip1-ip2.
ip2net фильтрует входные данные, выкидывая неправильные IP адреса.
Выбирается подсеть, в которой присутствует указанный минимум адресов.
Для ipv4 минимум задается как процент от размера подсети (mul/div. например, 3/4), для ipv6 минимум задается напрямую.
Размер подсети выбирается следующим алгоритмом:
Сначала в указанном диапазоне длин префиксов ищутся подсети, в которых количество адресов - максимально.
Если таких сетей найдено несколько, берется наименьшая сеть (префикс больше).
Например, заданы параметры v6_threshold=2 prefix_length=32-64, имеются следующие ipv6 :
```
1234:5678:aaaa::5
1234:5678:aaaa::6
1234:5678:aaac::5
Результат будет :
1234:5678:aaa8::/45
```
Эти адреса так же входят в подсеть /32. Однако, нет смысла проходиться ковровой бомбардировкой,
когда те же самые адреса вполне влезают в /45 и их ровно столько же.
Если изменить v6_threshold=4, то результат будет:
```
1234:5678:aaaa::5
1234:5678:aaaa::6
1234:5678:aaac::5
```
То есть ip не объединятся в подсеть, потому что их слишком мало.
Если изменить `prefix_length=56-64`, результат будет:
```
1234:5678:aaaa::/64
1234:5678:aaac::5
```
Требуемое процессорное время для вычислений сильно зависит от ширины диапазона длин префиксов, размера искомых подсетей и длины листа.
Если ip2net думает слишком долго, не используйте слишком большие подсети и уменьшите диапазон длин префиксов.
Учтите, что арифметика mul/div - целочисленная. При превышении разрядной сетки 32 bit результат непредсказуем.
Не надо делать такое: 5000000/10000000. 1/2 - гораздо лучше.
## mdig
Программа предназначена для многопоточного ресолвинга больших листов через системный DNS.
Она берет из stdin список доменов и выводит в stdout результат ресолвинга. Ошибки выводятся в stderr.
```
--threads=<threads_number> ; количество потоков. по умолчанию 1.
--family=<4|6|46> ; выбор семейства IP адресов : ipv4, ipv6, ipv4+ipv6
--verbose ; дебаг-лог на консоль
--stats=N ; выводить статистику каждые N доменов
--log-resolved=<file> ; сохранять успешно отресолвленные домены в файл
--log-failed=<file> ; сохранять неудачно отресолвленные домены в файл
--dns-make-query=<domain> ; вывести в stdout бинарный DNS запрос по домену. если --family=6, запрос будет AAAA, иначе A.
--dns-parse-query ; распарсить бинарный DNS ответ и выдать все ivp4 и ipv6 адреса из него в stdout
```
Параметры `--dns-make-query` и `--dns-parse-query` позволяют провести ресолвинг одного домена через произвольный канал.
Например, следующим образом можно выполнить DoH запрос, используя лишь mdig и curl :
```
mdig --family=6 --dns-make-query=rutracker.org | curl --data-binary @- -H "Content-Type: application/dns-message" https://cloudflare-dns.com/dns-query | mdig --dns-parse-query
```
## Способы получения списка заблокированных IP
@@ -1213,79 +1440,6 @@ ipfw таблицы в отличие от ipset могут содержать
Это особенно полезно на BSD системах с PF.
LISTS_RELOAD=- отключает перезагрузку листов.
## ip2net
Утилита ip2net предназначена для преобразования ipv4 или ipv6 списка ip в список подсетей
с целью сокращения размера списка. Входные данные берутся из stdin, выходные выдаются в `stdout`.
```
-4 ; лист - ipv4 (по умолчанию)
-6 ; лист - ipv6
--prefix-length=min[-max] ; диапазон рассматриваемых длин префиксов. например : 22-30 (ipv4), 56-64 (ipv6)
--v4-threshold=mul/div ; ipv4 : включать подсети, в которых заполнено по крайней мере mul/div адресов. например : 3/4
--v6-threshold=N ; ipv6 : минимальное количество ip для создания подсети
```
В списке могут присутствовать записи вида ip/prefix и ip1-ip2. Такие записи выкидываются в stdout без изменений.
Они принимаются командой ipset. ipset умеет для листов hash:net из ip1-ip2 делать оптимальное покрытие ip/prefix.
ipfw из FreeBSD понимает ip/prefix, но не понимает ip1-ip2.
ip2net фильтрует входные данные, выкидывая неправильные IP адреса.
Выбирается подсеть, в которой присутствует указанный минимум адресов.
Для ipv4 минимум задается как процент от размера подсети (mul/div. например, 3/4), для ipv6 минимум задается напрямую.
Размер подсети выбирается следующим алгоритмом:
Сначала в указанном диапазоне длин префиксов ищутся подсети, в которых количество адресов - максимально.
Если таких сетей найдено несколько, берется наименьшая сеть (префикс больше).
Например, заданы параметры v6_threshold=2 prefix_length=32-64, имеются следующие ipv6 :
```
1234:5678:aaaa::5
1234:5678:aaaa::6
1234:5678:aaac::5
Результат будет :
1234:5678:aaa8::/45
```
Эти адреса так же входят в подсеть /32. Однако, нет смысла проходиться ковровой бомбардировкой,
когда те же самые адреса вполне влезают в /45 и их ровно столько же.
Если изменить v6_threshold=4, то результат будет:
```
1234:5678:aaaa::5
1234:5678:aaaa::6
1234:5678:aaac::5
```
То есть ip не объединятся в подсеть, потому что их слишком мало.
Если изменить `prefix_length=56-64`, результат будет:
```
1234:5678:aaaa::/64
1234:5678:aaac::5
```
Требуемое процессорное время для вычислений сильно зависит от ширины диапазона длин префиксов, размера искомых подсетей и длины листа.
Если ip2net думает слишком долго, не используйте слишком большие подсети и уменьшите диапазон длин префиксов.
Учтите, что арифметика mul/div - целочисленная. При превышении разрядной сетки 32 bit результат непредсказуем.
Не надо делать такое: 5000000/10000000. 1/2 - гораздо лучше.
## mdig
Программа предназначена для многопоточного ресолвинга больших листов через системный DNS.
Она берет из stdin список доменов и выводит в stdout результат ресолвинга. Ошибки выводятся в stderr.
```
--threads=<threads_number> ; количество потоков. по умолчанию 1.
--family=<4|6|46> ; выбор семейства IP адресов : ipv4, ipv6, ipv4+ipv6
--verbose ; дебаг-лог на консоль
--stats=N ; выводить статистику каждые N доменов
--log-resolved=<file> ; сохранять успешно отресолвленные домены в файл
--log-failed=<file> ; сохранять неудачно отресолвленные домены в файл
--dns-make-query=<domain> ; вывести в stdout бинарный DNS запрос по домену. если --family=6, запрос будет AAAA, иначе A.
--dns-parse-query ; распарсить бинарный DNS ответ и выдать все ivp4 и ipv6 адреса из него в stdout
```
Параметры `--dns-make-query` и `--dns-parse-query` позволяют провести ресолвинг одного домена через произвольный канал.
Например, следующим образом можно выполнить DoH запрос, используя лишь mdig и curl :
```
mdig --family=6 --dns-make-query=rutracker.org | curl --data-binary @- -H "Content-Type: application/dns-message" https://cloudflare-dns.com/dns-query | mdig --dns-parse-query
```
## Фильтрация по именам доменов
Альтернативой ipset является использование tpws или nfqws со списком доменов.
@@ -1309,10 +1463,11 @@ mdig --family=6 --dns-make-query=rutracker.org | curl --data-binary @- -H "Conte
При режимах фильтрации `MODE_FILTER=hostlist` или `MODE_FILTER=autohostlist` система запуска передает **nfqws** или **tpws** все листы, файлы которых присутствуют.
Передача происходит через замену маркеров `<HOSTLIST>` и `<HOSTLIST_NOAUTO>` на реальные параметры `--hostlist`, `--hostlist-exclude`, `--hostlist-auto`.
Если вдруг листы include присутствуют, но все они пустые, то работа аналогична отсутствию include листа.
Файл есть, но не смотря на это дурится все, кроме exclude.
Файл есть, но несмотря на это дурится все, кроме exclude.
Если вам нужен именно такой режим - не обязательно удалять `zapret-hosts-users.txt`. Достаточно сделать его пустым.
Поддомены учитываются автоматически. Например, строчка "ru" вносит в список "*.ru". Строчка "*.ru" в списке не сработает.
Можно использовать символ `^` в начале хоста, чтобы отказаться от автоматического учета поддоменов.
Список доменов РКН может быть получен скриптами
```
@@ -1346,7 +1501,7 @@ tpws и nfqws решают нужно ли применять дурение в
Крайне рекомендовано использовать ограничитель `connbytes`, чтобы **nfqws** не обрабатывал гигабайты.
По этой же причине не рекомендуется использование режима на BSD системах. Там нет фильтра `connbytes`.
На linux системах при использовании nfqws и фильтра connbytes может понадобится :
На linux системах при использовании nfqws и фильтра connbytes может понадобиться :
`sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1`
Было замечено, что некоторые DPI в России возвращают RST с неверным ACK. Это принимается tcp/ip стеком
linux, но через раз приобретает статус INVALID в conntrack. Поэтому правила с `connbytes` срабатывают
@@ -1361,7 +1516,7 @@ linux, но через раз приобретает статус INVALID в con
свое клиенту. Применяется нечасто, поскольку броузеры на такое ругаются.
**nfqws** и **tpws** могут сечь варианты 1-3, 4 они не распознают.
Всилу специфики работы с отдельными пакетами или с TCP каналом tpws и nfqws распознают эти ситуации
В силу специфики работы с отдельными пакетами или с TCP каналом tpws и nfqws распознают эти ситуации
по-разному.
Что считается ситуацией, похожей на блокировку :
1) **nfqws** Несколько ретрансмиссий первого запроса в TCP сеансе, в котором имеется host.
@@ -1387,7 +1542,7 @@ linux, но через раз приобретает статус INVALID в con
Если сайт не ведет себя как заблокированный, значит обход применен не будет.
В противном случае терять все равно нечего.
Однако, могут быть временные сбои сервера, приводящие к ситуации, аналогичной блокировке.
Могут происходит ложные срабатывания. Если такое произошло, стратегия может начать ломать
Могут происходить ложные срабатывания. Если такое произошло, стратегия может начать ломать
незаблокированный сайт. Эту ситуацию, увы, придется вам контролировать вручную.
Заносите такие домены в `ipset/zapret-hosts-user-exclude.txt`, чтобы избежать повторения.
Чтобы впоследствии разобраться почему домен был занесен в лист, можно включить `autohostlist debug log`.
@@ -1466,8 +1621,10 @@ SKIP_DNSCHECK=1 - отказ от проверки DNS
SKIP_IPBLOCK=1 - отказ от тестов блокировки по порту или IP
SKIP_TPWS=1 - отказ от тестов tpws
SKIP_PKTWS=1 - отказ от тестов nfqws/dvtws/winws
PKTWS_EXTRA, TPWS_EXTRA - дополнительные параметры nfqws/dvtws/winws и tpws
PKTWS_EXTRA, TPWS_EXTRA - дополнительные параметры nfqws/dvtws/winws и tpws, указываемые после основной стратегии
PKTWS_EXTRA_1 .. PKTWS_EXTRA_9, TPWS_EXTRA_1 .. TPWS_EXTRA_9 - отдельно дополнительные параметры, содержащие пробелы
PKTWS_EXTRA_PRE - дополнительные параметры для nfqws/dvtws/winws, указываемые перед основной стратегией
PKTWS_EXTRA_PRE_1 .. PKTWS_EXTRA_PRE_9 - отдельно дополнительные параметры, содержащие пробелы
SECURE_DNS=0|1 - принудительно выключить или включить DoH
DOH_SERVERS - список URL DoH через пробел для автоматического выбора работающего сервера
DOH_SERVER - конкретный DoH URL, отказ от поиска
@@ -1620,13 +1777,16 @@ nfqws начнет получать адреса пакетов из локал
Каждая опция предполагает запуск одного инстанса соответствующего демона. Все различия методов дурения
для `http`, `https`, `quic` и т.д. должны быть отражены через схему мультистратегий.
В этом смысле настройка похожа на вариант `winws` на Windows, а перенос конфигов не должен представлять больших сложностей.
Основное правило настройки перехвата - перехватывайте только необходимый минимум.
Любой перехват лишнего - это бессмысленная нагрузка на вашу систему.
Опции демонов `--ipset` использовать запрещено. Это сделано намеренно и искусственно, чтобы не поощрять простой и
работающий, но неэффективный метод на *nix системах. Используйте `ipset`-ы режима ядра.
При необходимости пишите и задействуйте `custom scripts`.
Опции демонов `--ipset` использовать нужно с умом. Не стоит перехватывать весь трафик, чтобы потом по параметру --ipset
выделить лишь горстку IP. Это будет работать, но очень неэффективно с точки зрения нагрузки на систему.
Используйте `ipset`-ы режима ядра. При необходимости пишите и задействуйте `custom scripts`.
Но если у вас и так идет работа по всем IP, и нужно написать небольшую специализацию по IP, то --ipset вполне уместен.
Настройки демонов можно для удобства писать на нескольких строках, используя двойные или одинарные кавычки.
Чтобы задействовать стандартные обновляемые хост-листы из `ipset`, используйте маркер <HOSTLIST>.
Чтобы задействовать стандартные обновляемые хост-листы из каталога `ipset`, используйте маркер <HOSTLIST>.
Он будет заменен на параметры, соответствующие режиму MODE_FILTER, и будут подставлены реально существующие файлы.
Если MODE_FILTER не предполагает стандартного хостлиста, <HOSTLIST> будет заменен на пустую строку.
Стандартные хостлисты следует вставлять в финальных стратегиях (стратегиях по умолчанию), закрывающих цепочки по
@@ -1720,7 +1880,7 @@ hardware: выборочное управление включено в режи
```
`FLOWOFFLOAD=donttouch`
Параметр GETLIST указывает инсталлятору `install_easy.sh` какой скрипт дергать
Параметр `GETLIST` указывает инсталлятору `install_easy.sh` какой скрипт дергать
для обновления списка заблокированных ip или хостов.
Он же вызывается через `get_config.sh` из запланированных заданий (crontab или systemd timer).
Поместите сюда название скрипта, который будете использовать для обновления листов.
@@ -1734,7 +1894,7 @@ DISABLE_IPV6=1
```
Количество потоков для многопоточного DNS ресолвера mdig (1..100).
Чем их больше, тем быстрее, но не обидется ли на долбежку ваш DNS сервер?\
Чем их больше, тем быстрее, но не обидится ли на долбежку ваш DNS сервер?\
`MDIG_THREADS=30`
Место для хранения временных файлов. При скачивании огромных реестров в `/tmp` места может не хватить.
@@ -1807,11 +1967,18 @@ OPENWRT_WAN4="wan4 vpn"
OPENWRT_WAN6="wan6 vpn6"
```
Параметр INIT_APPLY_FW=1 разрешает init скрипту самостоятельно применять правила iptables.\
Параметр `INIT_APPLY_FW=1` разрешает init скрипту самостоятельно применять правила iptables.\
При иных значениях или если параметр закомментирован, правила применены не будут.\
Это полезно, если у вас есть система управления фаерволом, в настройки которой и следует прикрутить правила.\
На OpenWrt неприменимо при использовании firewall3+iptables.
`FILTER_TTL_EXPIRED_ICMP=1` включает механизмы блокировки пакетов icmp time exceeded, высылаемые роутерами по пути следования пакета в ответ на исчерпание TTL/HL.
В linux соединение обрывается системой, если в ответ на первый пакет (для tcp - SYN) пришел такой icmp. Аналогичная схема имеется и в datagram сокетах.
Блокировка icmp идет исключительно за счет средств iptables/nftables.
Чтобы не трогать весь трафик, в режиме PRENAT используется connmark для пометки сеансов, над которыми поработал nfqws. В режиме POSTNAT так сделать нельзя,
поэтому помечаются все сеансы, заворачиваемые на nfqws.
Настройку лучше отключить, если вы не ожидаете проблем от icmp, тк в этом случае будет меньше ненужных вмешательств в трафик.
***Следующие настройки не актуальны для openwrt:***
Если ваша система работает как роутер, то нужно вписать названия внутренних и внешних интерфейсов:
@@ -1888,7 +2055,7 @@ nfset-ы принадлежат только одной таблице, след
custom скрипты - это маленькие shell программы, управляющие нестандартными режимами применения zapret
или частными случаями, которые не могут быть интегрированы в основную часть без загромождения и замусоривания кода.
Для применеия custom следует помещать файлы в следующие директории в зависимости от вашей системы:
Для применения custom следует помещать файлы в следующие директории в зависимости от вашей системы:
```
/opt/zapret/init.d/sysv/custom.d
/opt/zapret/init.d/openwrt/custom.d
@@ -1978,7 +2145,7 @@ zapret_custom_firewall_nft поднимает правила nftables.
Под OpenWrt все уже сразу готово для использования системы в качестве роутера.
Имена интерфейсов WAN и LAN известны из настроек системы.
Под другими системами роутер вы настраиваете самостоятельно. Инсталлятор в это не вмешивается.
инсталлятор в зависимости от выбранного режима может спросить LAN и WAN интерфейсы.
Инсталлятор в зависимости от выбранного режима может спросить LAN и WAN интерфейсы.
Нужно понимать, что заворот проходящего трафика на **tpws** в прозрачном режиме происходит до выполнения маршрутизации,
следовательно возможна фильтрация по LAN и невозможна по WAN.
Решение о завороте на **tpws** локального исходящего трафика принимается после выполнения маршрутизации,
@@ -1988,6 +2155,15 @@ zapret_custom_firewall_nft поднимает правила nftables.
Деинсталляция выполняется через `uninstall_easy.sh`. После выполнения деинсталляции можно удалить каталог `/opt/zapret`.
## Установка под systemd
Если вам нравится systemd и хочется максимально под него заточиться, можно отказаться от скриптов запуска zapret
и поднимать инстансы `tpws` и `nfqws` как отдельные юниты systemd. При этом вам придется вручную написать правила iptables/nftables
и каким-то образом их поднимать. Например, написать дополнительный systemd unit для этого.
Так же требуется собрать бинарники особым образом через `make systemd`.
В комплекте zapret есть шаблоны `init.d/systemd/{nfqws@.service,tpws@.service}`.
Краткий перечень команд для их использования приведен в комментариях в этих файлах.
## Простая установка на openwrt
@@ -2159,7 +2335,7 @@ Wifi сеть - обычно `wlan0`.
tpws работает обычным образом.
`nfqueue` поломан, можно собрать фиксящий модуль https://github.com/im-0/unfuck-nfqueue-on-e3372h,
`nfqueue` поломан, можно собрать фиксящий модуль https://github.com/im-0/unfuck-nfqueue-on-e3372h,
используя исходники с huawei open source. Исходники содержат тулчейн и полусобирающееся,
неактуальное ядро. Конфиг можно взять с рабочего модема из `/proc/config.gz`.
С помощью этих исходников умельцы могут собрать модуль `unfuck_nfqueue.ko`.
@@ -2212,10 +2388,10 @@ curl: (7) Failed to connect to www.ru port 80: Host is unreachable
## Другие прошивки
Для статических бинариков не имеет значения на чем они запущены: PC, android, приставка, роутер, любой другой девайс.
Для статических бинарников не имеет значения на чем они запущены: PC, android, приставка, роутер, любой другой девайс.
Подойдет любая прошивка, дистрибутив linux. Статические бинарники запустятся на всем.
Им нужно только ядро с необходимыми опциями сборки или модулями.
Но кроме бинариков в проекте используются еще и скрипты, в которых задействуются некоторые
Но кроме бинарников в проекте используются еще и скрипты, в которых задействуются некоторые
стандартные программы.
Основные причины почему нельзя просто так взять и установить эту систему на что угодно:
@@ -2253,14 +2429,14 @@ entware содержит репозиторий user-mode компонент, к
одробное описание настроек для других прошивок выходит за рамки данного проекта._
OpenWrt является одной из немногих относительно полноценных linux систем для embedded devices.
Она характеризуется следующими вещами, которые и послужили основой выбора именно этой прошивк:
Она характеризуется следующими вещами, которые и послужили основой выбора именно этой прошивки:
* полный root доступ к девайсу через shell. на заводских прошивках чаще всего отсутствует, на многих альтернативных есть
* корень r/w. это практически уникальная особенность OpenWrt. заводские и большинство альтернативных прошивок
построены на базе squashfs root (r/o), а конфигурация хранится в специально отформатированной области
встроенной памяти, называемой nvram. не имеющие r/w корня системы сильно кастрированы. они не имеют
возможности доустановки ПО из репозитория без специальных вывертов и заточены в основном
на чуть более продвинутого, чем обычно, пользователя и управление имеющимся функционалом через веб интерфейс,
но функционал фиксированно ограничен. альтернативные прошивки как правило могут монтировать r/w раздел
но функционал фиксированно ограничен. альтернативные прошивки, как правило, могут монтировать r/w раздел
в какую-то область файловой системы, заводские обычно могут монтировать лишь флэшки, подключенные к USB,
и не факт, что есть поддержка unix файловых системы. может быть поддержка только fat и ntfs.
* возможность выноса корневой файловой системы на внешний носитель (extroot) или создания на нем оверлея (overlay)
@@ -2319,12 +2495,8 @@ VPS можно приобрести в множестве мест. Сущест
## Поддержать разработчика
<img src=https://cdn-icons-png.flaticon.com/16/14446/14446252.png alt="USDT" style="vertical-align: middle;"/> USDT
```
0x3d52Ce15B7Be734c53fc9526ECbAB8267b63d66E
```
USDT `0x3d52Ce15B7Be734c53fc9526ECbAB8267b63d66E`
<img src=https://cdn-icons-png.flaticon.com/16/5968/5968260.png alt="USDT" style="vertical-align: middle;"/> BTC
```
bc1qhqew3mrvp47uk2vevt5sctp7p2x9m7m5kkchve
```
BTC `bc1qhqew3mrvp47uk2vevt5sctp7p2x9m7m5kkchve`
ETH `0x3d52Ce15B7Be734c53fc9526ECbAB8267b63d66E`

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
files/fake/stun.bin Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,144 @@
# this custom script demonstrates how to launch extra nfqws instance limited by ipset
# can override in config :
NFQWS_MY1_OPT="${NFQWS_MY1_OPT:---filter-udp=* --dpi-desync=fake --dpi-desync-repeats=6 --dpi-desync-any-protocol --new --filter-tcp=* --dpi-desync=multisplit}"
NFQWS_MY1_SUBNETS4="${NFQWS_MY1_SUBNETS4:-173.194.0.0/16 108.177.0.0/17 74.125.0.0/16 64.233.160.0/19 172.217.0.0/16}"
NFQWS_MY1_SUBNETS6="${NFQWS_MY1_SUBNETS6:-2a00:1450::/29}"
NFQWS_MY1_PORTS_TCP=${NFQWS_MY1_PORTS_TCP:-$NFQWS_PORTS_TCP}
NFQWS_MY1_PORTS_UDP=${NFQWS_MY1_PORTS_UDP:-$NFQWS_PORTS_UDP}
NFQWS_MY1_TCP_PKT_OUT=${NFQWS_MY1_TCP_PKT_OUT:-$NFQWS_TCP_PKT_OUT}
NFQWS_MY1_UDP_PKT_OUT=${NFQWS_MY1_UDP_PKT_OUT:-$NFQWS_UDP_PKT_OUT}
NFQWS_MY1_TCP_PKT_IN=${NFQWS_MY1_TCP_PKT_IN:-$NFQWS_TCP_PKT_IN}
NFQWS_MY1_UDP_PKT_IN=${NFQWS_MY1_UDP_PKT_IN:-$NFQWS_UDP_PKT_IN}
NFQWS_MY1_IPSET_SIZE=${NFQWS_MY1_IPSET_SIZE:-4096}
NFQWS_MY1_IPSET_OPT="${NFQWS_MY1_IPSET_OPT:-hash:net hashsize 8192 maxelem $NFQWS_MY1_IPSET_SIZE}"
alloc_dnum DNUM_NFQWS_MY1
alloc_qnum QNUM_NFQWS_MY1
NFQWS_MY1_NAME4=my1nfqws4
NFQWS_MY1_NAME6=my1nfqws6
zapret_custom_daemons()
{
# $1 - 1 - run, 0 - stop
local opt="--qnum=$QNUM_NFQWS_MY1 $NFQWS_MY1_OPT"
do_nfqws $1 $DNUM_NFQWS_MY1 "$opt"
}
zapret_custom_firewall()
{
# $1 - 1 - run, 0 - stop
local f4 f6 subnet
local NFQWS_MY1_PORTS_TCP=$(replace_char - : $NFQWS_MY1_PORTS_TCP)
local NFQWS_MY1_PORTS_UDP=$(replace_char - : $NFQWS_MY1_PORTS_UDP)
[ "$1" = 1 -a "$DISABLE_IPV4" != 1 ] && {
ipset create $NFQWS_MY1_NAME4 $NFQWS_MY1_IPSET_OPT family inet 2>/dev/null
ipset flush $NFQWS_MY1_NAME4
for subnet in $NFQWS_MY1_SUBNETS4; do
echo add $NFQWS_MY1_NAME4 $subnet
done | ipset -! restore
}
[ "$1" = 1 -a "$DISABLE_IPV6" != 1 ] && {
ipset create $NFQWS_MY1_NAME6 $NFQWS_MY1_IPSET_OPT family inet6 2>/dev/null
ipset flush $NFQWS_MY1_NAME6
for subnet in $NFQWS_MY1_SUBNETS6; do
echo add $NFQWS_MY1_NAME6 $subnet
done | ipset -! restore
}
[ -n "$NFQWS_MY1_PORTS_TCP" ] && {
[ -n "$NFQWS_MY1_TCP_PKT_OUT" -a "$NFQWS_MY1_TCP_PKT_OUT" != 0 ] && {
f4="-p tcp -m multiport --dports $NFQWS_MY1_PORTS_TCP $ipt_connbytes 1:$NFQWS_MY1_TCP_PKT_OUT -m set --match-set"
f6="$f4 $NFQWS_MY1_NAME6 dst"
f4="$f4 $NFQWS_MY1_NAME4 dst"
fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
[ -n "$NFQWS_MY1_TCP_PKT_IN" -a "$NFQWS_MY1_TCP_PKT_IN" != 0 ] && {
f4="-p tcp -m multiport --sports $NFQWS_MY1_PORTS_TCP $ipt_connbytes 1:$NFQWS_MY1_TCP_PKT_IN -m set --match-set"
f6="$f4 $NFQWS_MY1_NAME6 src"
f4="$f4 $NFQWS_MY1_NAME4 src"
fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
}
[ -n "$NFQWS_MY1_PORTS_UDP" ] && {
[ -n "$NFQWS_MY1_UDP_PKT_OUT" -a "$NFQWS_MY1_UDP_PKT_OUT" != 0 ] && {
f4="-p udp -m multiport --dports $NFQWS_MY1_PORTS_UDP $ipt_connbytes 1:$NFQWS_MY1_UDP_PKT_OUT -m set --match-set"
f6="$f4 $NFQWS_MY1_NAME6 dst"
f4="$f4 $NFQWS_MY1_NAME4 dst"
fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
[ -n "$NFQWS_MY1_UDP_PKT_IN" -a "$NFQWS_MY1_UDP_PKT_IN" != 0 ] && {
f4="-p udp -m multiport --sports $NFQWS_MY1_PORTS_UDP $ipt_connbytes 1:$NFQWS_MY1_UDP_PKT_IN -m set --match-set"
f6="$f4 $NFQWS_MY1_NAME6 src"
f4="$f4 $NFQWS_MY1_NAME4 src"
fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
}
[ "$1" = 1 ] || {
ipset destroy $NFQWS_MY1_NAME4 2>/dev/null
ipset destroy $NFQWS_MY1_NAME6 2>/dev/null
}
}
zapret_custom_firewall_nft()
{
local f4 f6 subnets
local first_packets_only="$nft_connbytes 1-$NFQWS_MY1_PKT_OUT"
[ "$DISABLE_IPV4" != 1 ] && {
make_comma_list subnets $NFQWS_MY1_SUBNETS4
nft_create_set $NFQWS_MY1_NAME4 "type ipv4_addr; size $NFQWS_MY1_IPSET_SIZE; auto-merge; flags interval;"
nft_flush_set $NFQWS_MY1_NAME4
nft_add_set_element $NFQWS_MY1_NAME4 "$subnets"
}
[ "$DISABLE_IPV6" != 1 ] && {
make_comma_list subnets $NFQWS_MY1_SUBNETS6
nft_create_set $NFQWS_MY1_NAME6 "type ipv6_addr; size $NFQWS_MY1_IPSET_SIZE; auto-merge; flags interval;"
nft_flush_set $NFQWS_MY1_NAME6
nft_add_set_element $NFQWS_MY1_NAME6 "$subnets"
}
[ -n "$NFQWS_MY1_PORTS_TCP" ] && {
[ -n "$NFQWS_MY1_TCP_PKT_OUT" -a "$NFQWS_MY1_TCP_PKT_OUT" != 0 ] && {
f4="tcp dport {$NFQWS_MY1_PORTS_TCP} $(nft_first_packets $NFQWS_MY1_TCP_PKT_OUT)"
f6="$f4 ip6 daddr @$NFQWS_MY1_NAME6"
f4="$f4 ip daddr @$NFQWS_MY1_NAME4"
nft_fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
[ -n "$NFQWS_MY1_TCP_PKT_IN" -a "$NFQWS_MY1_TCP_PKT_IN" != 0 ] && {
f4="tcp sport {$NFQWS_MY1_PORTS_TCP} $(nft_first_packets $NFQWS_MY1_TCP_PKT_IN)"
f6="$f4 ip6 saddr @$NFQWS_MY1_NAME6"
f4="$f4 ip saddr @$NFQWS_MY1_NAME4"
nft_fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
}
[ -n "$NFQWS_MY1_PORTS_UDP" ] && {
[ -n "$NFQWS_MY1_UDP_PKT_OUT" -a "$NFQWS_MY1_UDP_PKT_OUT" != 0 ] && {
f4="udp dport {$NFQWS_MY1_PORTS_UDP} $(nft_first_packets $NFQWS_MY1_UDP_PKT_OUT)"
f6="$f4 ip6 daddr @$NFQWS_MY1_NAME6"
f4="$f4 ip daddr @$NFQWS_MY1_NAME4"
nft_fw_nfqws_post $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
[ -n "$NFQWS_MY1_UDP_PKT_IN" -a "$NFQWS_MY1_UDP_PKT_IN" != 0 ] && {
f4="udp sport {$NFQWS_MY1_PORTS_UDP} $(nft_first_packets $NFQWS_MY1_UDP_PKT_IN)"
f6="$f4 ip6 saddr @$NFQWS_MY1_NAME6"
f4="$f4 ip saddr @$NFQWS_MY1_NAME4"
nft_fw_nfqws_pre $1 "$f4" "$f6" $QNUM_NFQWS_MY1
}
}
}
zapret_custom_firewall_nft_flush()
{
# this function is called after all nft fw rules are deleted
# however sets are not deleted. it's desired to clear sets here.
nft_del_set $NFQWS_MY1_NAME4 2>/dev/null
nft_del_set $NFQWS_MY1_NAME6 2>/dev/null
}

View File

@@ -28,7 +28,6 @@ zapret_custom_firewall()
local f4 f6 subnet
local PORTS_IPT=$(replace_char - : $TPWS_MY1_PORTS)
local dest_set="-m set --match-set $TPWS_MY1_NAME4 dst"
[ "$1" = 1 -a "$DISABLE_IPV4" != 1 ] && {
ipset create $TPWS_MY1_NAME4 $TPWS_MY1_IPSET_OPT family inet 2>/dev/null
@@ -58,7 +57,7 @@ zapret_custom_firewall()
zapret_custom_firewall_nft()
{
local f4 f6 subnet
local f4 f6 subnets
[ "$DISABLE_IPV4" != 1 ] && {
make_comma_list subnets $TPWS_MY1_SUBNETS4

View File

@@ -0,0 +1,65 @@
# Example systemd service unit for nfqws. Adjust for your installation.
# WARNING ! This unit requires to compile nfqws using `make systemd`
# WARNING ! This makefile target enables special systemd notify support.
# PREPARE
# install build depends
# make -C /opt/zapret systemd
# cp nfqws@service /lib/systemd/system
# systemctl daemon-reload
# MANAGE INSTANCE
# prepare /etc/zapret/nfqws1.conf with nfqws parameters
# systemctl start nfqws@nfqws1
# systemctl status nfqws@nfqws1
# systemctl restart nfqws@nfqws1
# systemctl enable nfqws@nfqws1
# systemctl disable nfqws@nfqws1
# systemctl stop nfqws@nfqws1
# DELETE
# rm /lib/systemd/system/nfqws@.service
# systemctl daemon-reload
[Unit]
After=network.target
[Service]
Type=notify
Restart=on-failure
ExecSearchPath=/opt/zapret/binaries/my
ExecStart=nfqws @${CONFIG_DIR}/${INSTANCE}.conf
Environment=CONFIG_DIR=/etc/zapret
Environment=INSTANCE=%i
RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET6 AF_INET
LockPersonality=true
MemoryDenyWriteExecute=true
PrivateDevices=true
PrivateMounts=true
PrivateTmp=true
ProcSubset=pid
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectProc=invisible
ProtectSystem=full
RemoveIPC=true
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@resources
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,63 @@
# Example systemd service unit for tpws. Adjust for your installation.
# WARNING ! This unit requires to compile tpws using `make systemd`
# WARNING ! This makefile target enables special systemd notify support.
# PREPARE
# install build depends
# make -C /opt/zapret systemd
# cp tpws@service /lib/systemd/system
# systemctl daemon-reload
# MANAGE INSTANCE
# prepare /etc/zapret/tpws1.conf with tpws parameters
# systemctl start tpws@tpws1
# systemctl status tpws@tpws1
# systemctl restart tpws@tpws1
# systemctl enable tpws@tpws1
# systemctl disable tpws@tpws1
# systemctl stop tpws@tpws1
# DELETE
# rm /lib/systemd/system/tpws@.service
# systemctl daemon-reload
[Unit]
After=network.target
[Service]
Type=notify
Restart=on-failure
ExecSearchPath=/opt/zapret/binaries/my
ExecStart=tpws @${CONFIG_DIR}/${INSTANCE}.conf
Environment=CONFIG_DIR=/etc/zapret
Environment=INSTANCE=%i
RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET6 AF_INET
LockPersonality=true
MemoryDenyWriteExecute=true
PrivateDevices=true
PrivateMounts=true
PrivateTmp=true
ProcSubset=pid
ProtectClock=true
ProtectControlGroups=true
ProtectHome=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectProc=invisible
ProtectSystem=full
RemoveIPC=true
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
SystemCallArchitectures=native
SystemCallFilter=@system-service
UMask=0077
[Install]
WantedBy=multi-user.target

View File

@@ -69,7 +69,14 @@ check_bins()
echo found architecture "\"$arch\""
elif [ -f "$EXEDIR/Makefile" ] && exists make; then
echo trying to compile
[ "$SYSTEM" = "macos" ] && make_target=mac
case $SYSTEM in
macos)
make_target=mac
;;
systemd)
make_target=systemd
;;
esac
CFLAGS="-march=native ${CFLAGS}" make -C "$EXEDIR" $make_target || {
echo could not compile
make -C "$EXEDIR" clean

View File

@@ -11,6 +11,8 @@ all: ip2net
ip2net: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS)
systemd: ip2net
android: ip2net
bsd: $(SRC_FILES)

View File

@@ -225,6 +225,28 @@ static void exithelp(void)
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
enum opt_indices {
IDX_HELP,
IDX_H,
IDX_4,
IDX_6,
IDX_PREFIX_LENGTH,
IDX_V4_THRESHOLD,
IDX_V6_THRESHOLD,
IDX_LAST,
};
static const struct option long_options[] = {
[IDX_HELP] = {"help", no_argument, 0, 0},
[IDX_H] = {"h", no_argument, 0, 0},
[IDX_4] = {"4", no_argument, 0, 0},
[IDX_6] = {"6", no_argument, 0, 0},
[IDX_PREFIX_LENGTH] = {"prefix-length", required_argument, 0, 0},
[IDX_V4_THRESHOLD] = {"v4-threshold", required_argument, 0, 0},
[IDX_V6_THRESHOLD] = {"v6-threshold", required_argument, 0, 0},
[IDX_LAST] = {NULL, 0, NULL, 0},
};
static void parse_params(int argc, char *argv[])
{
int option_index = 0;
@@ -236,33 +258,23 @@ static void parse_params(int argc, char *argv[])
params.pctdiv = DEFAULT_PCTDIV;
params.v6_threshold = DEFAULT_V6_THRESHOLD;
const struct option long_options[] = {
{ "help",no_argument,0,0 },// optidx=0
{ "h",no_argument,0,0 },// optidx=1
{ "4",no_argument,0,0 },// optidx=2
{ "6",no_argument,0,0 },// optidx=3
{ "prefix-length",required_argument,0,0 },// optidx=4
{ "v4-threshold",required_argument,0,0 },// optidx=5
{ "v6-threshold",required_argument,0,0 },// optidx=6
{ NULL,0,NULL,0 }
};
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1)
{
if (v) exithelp();
switch (option_index)
{
case 0:
case 1:
case IDX_HELP:
case IDX_H:
PRINT_VER;
exithelp();
break;
case 2:
case IDX_4:
params.ipv6 = false;
break;
case 3:
case IDX_6:
params.ipv6 = true;
break;
case 4:
case IDX_PREFIX_LENGTH:
i = sscanf(optarg,"%u-%u",&plen1,&plen2);
if (i == 1) plen2 = plen1;
if (i<=0 || plen2<plen1 || !plen1 || !plen2)
@@ -271,7 +283,7 @@ static void parse_params(int argc, char *argv[])
exit(1);
}
break;
case 5:
case IDX_V4_THRESHOLD:
i = sscanf(optarg, "%u/%u", &params.pctmult, &params.pctdiv);
if (i!=2 || params.pctdiv<2 || params.pctmult<1 || params.pctmult>=params.pctdiv)
{
@@ -279,7 +291,7 @@ static void parse_params(int argc, char *argv[])
exit(1);
}
break;
case 6:
case IDX_V6_THRESHOLD:
i = sscanf(optarg, "%u", &params.v6_threshold);
if (i != 1 || params.v6_threshold<1)
{

View File

@@ -274,7 +274,9 @@ hup_zapret_daemons()
if exists killall; then
killall -HUP tpws nfqws dvtws 2>/dev/null
elif exists pkill; then
pkill -HUP ^tpws$ ^nfqws$ ^dvtws$
pkill -HUP ^tpws$
pkill -HUP ^nfqws$
pkill -HUP ^dvtws$
else
echo no mass killer available ! cant HUP zapret daemons
fi

View File

@@ -1,6 +1,8 @@
127.0.0.0/8
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
169.254.0.0/16
::1
fc00::/7
fe80::/10

View File

@@ -12,6 +12,8 @@ all: mdig
mdig: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS)
systemd: mdig
android: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS_ANDROID) $(LDFLAGS)

View File

@@ -467,25 +467,38 @@ static void exithelp(void)
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__)
#endif
enum opt_indices {
IDX_HELP,
IDX_THREADS,
IDX_FAMILY,
IDX_VERBOSE,
IDX_STATS,
IDX_LOG_RESOLVED,
IDX_LOG_FAILED,
IDX_DNS_MAKE_QUERY,
IDX_DNS_PARSE_QUERY,
IDX_LAST,
};
static const struct option long_options[] = {
[IDX_HELP] = {"help", no_argument, 0, 0},
[IDX_THREADS] = {"threads", required_argument, 0, 0},
[IDX_FAMILY] = {"family", required_argument, 0, 0},
[IDX_VERBOSE] = {"verbose", no_argument, 0, 0},
[IDX_STATS] = {"stats", required_argument, 0, 0},
[IDX_LOG_RESOLVED] = {"log-resolved", required_argument, 0, 0},
[IDX_LOG_FAILED] = {"log-failed", required_argument, 0, 0},
[IDX_DNS_MAKE_QUERY] = {"dns-make-query", required_argument, 0, 0},
[IDX_DNS_PARSE_QUERY] = {"dns-parse-query", no_argument, 0, 0},
[IDX_LAST] = {NULL, 0, NULL, 0},
};
int main(int argc, char **argv)
{
int r, v, option_index = 0;
char fn1[256],fn2[256];
char dom[256];
static const struct option long_options[] = {
{"help",no_argument,0,0}, // optidx=0
{"threads",required_argument,0,0}, // optidx=1
{"family",required_argument,0,0}, // optidx=2
{"verbose",no_argument,0,0}, // optidx=3
{"stats",required_argument,0,0}, // optidx=4
{"log-resolved",required_argument,0,0}, // optidx=5
{"log-failed",required_argument,0,0}, // optidx=6
{"dns-make-query",required_argument,0,0}, // optidx=7
{"dns-parse-query",no_argument,0,0}, // optidx=8
{NULL,0,NULL,0}
};
memset(&glob, 0, sizeof(glob));
*fn1 = *fn2 = *dom = 0;
glob.family = FAMILY4;
@@ -495,11 +508,11 @@ int main(int argc, char **argv)
if (v) exithelp();
switch (option_index)
{
case 0: /* help */
case IDX_HELP:
PRINT_VER;
exithelp();
break;
case 1: /* threads */
case IDX_THREADS:
glob.threads = optarg ? atoi(optarg) : 0;
if (glob.threads <= 0 || glob.threads > 100)
{
@@ -507,7 +520,7 @@ int main(int argc, char **argv)
return 1;
}
break;
case 2: /* family */
case IDX_FAMILY:
if (!strcmp(optarg, "4"))
glob.family = FAMILY4;
else if (!strcmp(optarg, "6"))
@@ -520,25 +533,25 @@ int main(int argc, char **argv)
return 1;
}
break;
case 3: /* verbose */
case IDX_VERBOSE:
glob.verbose = '\1';
break;
case 4: /* stats */
case IDX_STATS:
glob.stats_every = optarg ? atoi(optarg) : 0;
break;
case 5: /* log-resolved */
case IDX_LOG_RESOLVED:
strncpy(fn1,optarg,sizeof(fn1));
fn1[sizeof(fn1)-1] = 0;
break;
case 6: /* log-failed */
case IDX_LOG_FAILED:
strncpy(fn2,optarg,sizeof(fn2));
fn2[sizeof(fn2)-1] = 0;
break;
case 7: /* dns-make-query */
case IDX_DNS_MAKE_QUERY:
strncpy(dom,optarg,sizeof(dom));
dom[sizeof(dom)-1] = 0;
break;
case 8: /* dns-parse-query */
case IDX_DNS_PARSE_QUERY:
return dns_parse_query();
}
}

View File

@@ -1,8 +1,10 @@
CC ?= gcc
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
LIBS_SYSTEMD = -lsystemd
LIBS_BSD = -lz
LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32
LIBS_CYGWIN32 = -lwindivert32
@@ -16,6 +18,9 @@ all: nfqws
nfqws: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LDFLAGS)
systemd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_SYSTEMD) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LIBS_SYSTEMD) $(LDFLAGS)
android: nfqws
bsd: $(SRC_FILES)

View File

@@ -143,8 +143,11 @@ static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr
}
else if (tcp_synack_segment(tcphdr))
{
if (t->state!=SYN) ConntrackReInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = ntohl(tcphdr->th_ack)-1;
// ignore SA dups
uint32_t seq0 = ntohl(tcphdr->th_ack)-1;
if (t->state!=SYN && t->seq0!=seq0)
ConntrackReInitTrack(t); // erase current entry
if (!t->seq0) t->seq0 = seq0;
t->ack0 = ntohl(tcphdr->th_seq);
}
else if (tcphdr->th_flags & (TH_FIN|TH_RST))
@@ -338,8 +341,8 @@ void ConntrackPoolDump(const t_conntrack *p)
printf("rseq=%u pos_orig=%u rack=%u pos_reply=%u",
t->track.seq_last, t->track.pos_orig,
t->track.ack_last, t->track.pos_reply);
printf(" req_retrans=%u cutoff=%u wss_cutoff=%u d_cutoff=%u hostname=%s l7proto=%s\n",
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff, t->track.hostname, l7proto_str(t->track.l7proto));
printf(" req_retrans=%u cutoff=%u wss_cutoff=%u desync_cutoff=%u dup_cutoff=%u orig_cutoff=%u hostname=%s l7proto=%s\n",
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff, t->track.b_dup_cutoff, t->track.b_orig_mod_cutoff, t->track.hostname, l7proto_str(t->track.l7proto));
};
}

View File

@@ -77,14 +77,16 @@ typedef struct
bool req_seq_present,req_seq_finalized,req_seq_abandoned;
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
uint8_t incoming_ttl, autottl;
uint8_t incoming_ttl, desync_autottl, orig_autottl, dup_autottl;
bool b_autottl_discovered;
bool b_cutoff; // mark for deletion
bool b_wssize_cutoff, b_desync_cutoff;
bool b_wssize_cutoff, b_desync_cutoff, b_dup_cutoff, b_orig_mod_cutoff;
t_l7proto l7proto;
bool l7proto_discovered;
char *hostname;
bool hostname_discovered;
bool hostname_ah_check; // should perform autohostlist checks
t_reassemble reasm_orig;

View File

@@ -38,6 +38,11 @@ uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment)
return htons(ntohs(netorder_value)+cpuorder_increment);
}
bool ip_has_df(const struct ip *ip)
{
return ip && !!(ntohs(ip->ip_off) & IP_DF);
}
uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind)
{
uint8_t *t = (uint8_t*)(tcp+1);
@@ -83,10 +88,22 @@ bool tcp_has_fastopen(const struct tcphdr *tcp)
opt = tcp_find_option((struct tcphdr*)tcp, 254);
return opt && opt[1]>=4 && opt[2]==0xF9 && opt[3]==0x89;
}
uint16_t tcp_find_mss(struct tcphdr *tcp)
{
uint8_t *t = tcp_find_option(tcp,2);
return (t && t[1]==4) ? *(uint16_t*)(t+2) : 0;
}
bool tcp_has_sack(struct tcphdr *tcp)
{
uint8_t *t = tcp_find_option(tcp,4);
return !!t;
}
// n prefix (nsport, nwsize) means network byte order
static void fill_tcphdr(
struct tcphdr *tcp, uint32_t fooling, uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nsport, uint16_t ndport,
uint16_t nwsize, uint8_t scale_factor,
@@ -111,20 +128,32 @@ static void fill_tcphdr(
tcp->th_seq = nseq;
tcp->th_ack = nack_seq;
}
tcp->th_off = 5;
tcp->th_off = 5;
if ((fooling & FOOL_DATANOACK) && !(tcp_flags & (TH_SYN|TH_RST)) && data_len)
tcp_flags &= ~TH_ACK;
*((uint8_t*)tcp+13)= tcp_flags;
tcp->th_win = nwsize;
if (nmss)
{
tcpopt[t++] = 2; // kind
tcpopt[t++] = 4; // len
*(uint16_t*)(tcpopt+t) = nmss;
t+=2;
}
if (sack)
{
tcpopt[t++] = 4; // kind
tcpopt[t++] = 2; // len
}
if (fooling & FOOL_MD5SIG)
{
tcpopt[0] = 19; // kind
tcpopt[1] = 18; // len
*(uint32_t*)(tcpopt+2)=random();
*(uint32_t*)(tcpopt+6)=random();
*(uint32_t*)(tcpopt+10)=random();
*(uint32_t*)(tcpopt+14)=random();
t=18;
tcpopt[t] = 19; // kind
tcpopt[t+1] = 18; // len
*(uint32_t*)(tcpopt+t+2)=random();
*(uint32_t*)(tcpopt+t+6)=random();
*(uint32_t*)(tcpopt+t+10)=random();
*(uint32_t*)(tcpopt+t+14)=random();
t+=18;
}
if (timestamps || (fooling & FOOL_TS))
{
@@ -145,10 +174,12 @@ static void fill_tcphdr(
tcp->th_off += t>>2;
tcp->th_sum = 0;
}
static uint16_t tcpopt_len(uint32_t fooling, const uint32_t *timestamps, uint8_t scale_factor)
static uint16_t tcpopt_len(bool sack, bool mss, uint32_t fooling, const uint32_t *timestamps, uint8_t scale_factor)
{
uint16_t t=0;
if (fooling & FOOL_MD5SIG) t=18;
if (sack) t+=2;
if (mss) t+=4;
if (fooling & FOOL_MD5SIG) t+=18;
if ((fooling & FOOL_TS) || timestamps) t+=10;
if (scale_factor!=SCALE_NONE) t+=3;
return (t+3)&~3;
@@ -163,11 +194,11 @@ static void fill_udphdr(struct udphdr *udp, uint16_t nsport, uint16_t ndport, ui
udp->uh_sum = 0;
}
static void fill_iphdr(struct ip *ip, const struct in_addr *src, const struct in_addr *dst, uint16_t pktlen, uint8_t proto, uint8_t ttl, uint8_t tos, uint16_t ip_id)
static void fill_iphdr(struct ip *ip, const struct in_addr *src, const struct in_addr *dst, uint16_t pktlen, uint8_t proto, bool DF, uint8_t ttl, uint8_t tos, uint16_t ip_id)
{
ip->ip_tos = tos;
ip->ip_sum = 0;
ip->ip_off = 0;
ip->ip_off = DF ? htons(IP_DF) : 0;
ip->ip_v = 4;
ip->ip_hl = 5;
ip->ip_len = htons(pktlen);
@@ -190,10 +221,13 @@ static void fill_ip6hdr(struct ip6_hdr *ip6, const struct in6_addr *src, const s
bool prepare_tcp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
uint32_t *timestamps,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@@ -203,7 +237,7 @@ bool prepare_tcp_segment4(
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen)
{
uint16_t tcpoptlen = tcpopt_len(fooling,timestamps,scale_factor);
uint16_t tcpoptlen = tcpopt_len(sack,!!nmss,fooling,timestamps,scale_factor);
uint16_t ip_payload_len = sizeof(struct tcphdr) + tcpoptlen + len;
uint16_t pktlen = sizeof(struct ip) + ip_payload_len;
if (pktlen>*buflen) return false;
@@ -212,12 +246,12 @@ bool prepare_tcp_segment4(
struct tcphdr *tcp = (struct tcphdr*)(ip+1);
uint8_t *payload = (uint8_t*)(tcp+1)+tcpoptlen;
fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_TCP, ttl, tos, ip_id);
fill_tcphdr(tcp,fooling,tcp_flags,nseq,nack_seq,src->sin_port,dst->sin_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment,len);
fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_TCP, DF, ttl, tos, ip_id);
fill_tcphdr(tcp,fooling,tcp_flags,sack,nmss,nseq,nack_seq,src->sin_port,dst->sin_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment,len);
memcpy(payload,data,len);
tcp4_fix_checksum(tcp,ip_payload_len,&ip->ip_src,&ip->ip_dst);
if (fooling & FOOL_BADSUM) tcp->th_sum^=htons(0xBEAF);
if (fooling & FOOL_BADSUM) tcp->th_sum^=(uint16_t)(1+random()%0xFFFF);
*buflen = pktlen;
return true;
@@ -226,6 +260,8 @@ bool prepare_tcp_segment4(
bool prepare_tcp_segment6(
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
@@ -238,7 +274,7 @@ bool prepare_tcp_segment6(
const void *data, uint16_t len,
uint8_t *buf, size_t *buflen)
{
uint16_t tcpoptlen = tcpopt_len(fooling,timestamps,scale_factor);
uint16_t tcpoptlen = tcpopt_len(sack,!!nmss,fooling,timestamps,scale_factor);
uint16_t transport_payload_len = sizeof(struct tcphdr) + tcpoptlen + len;
uint16_t ip_payload_len = transport_payload_len +
8*!!((fooling & (FOOL_HOPBYHOP|FOOL_HOPBYHOP2))==FOOL_HOPBYHOP) +
@@ -297,11 +333,11 @@ bool prepare_tcp_segment6(
uint8_t *payload = (uint8_t*)(tcp+1)+tcpoptlen;
fill_ip6hdr(ip6, &src->sin6_addr, &dst->sin6_addr, ip_payload_len, proto, ttl, flow_label);
fill_tcphdr(tcp,fooling,tcp_flags,nseq,nack_seq,src->sin6_port,dst->sin6_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment,len);
fill_tcphdr(tcp,fooling,tcp_flags,sack,nmss,nseq,nack_seq,src->sin6_port,dst->sin6_port,nwsize,scale_factor,timestamps,badseq_increment,badseq_ack_increment,len);
memcpy(payload,data,len);
tcp6_fix_checksum(tcp,transport_payload_len,&ip6->ip6_src,&ip6->ip6_dst);
if (fooling & FOOL_BADSUM) tcp->th_sum^=htons(0xBEAF);
if (fooling & FOOL_BADSUM) tcp->th_sum^=(1+random()%0xFFFF);
*buflen = pktlen;
return true;
@@ -310,10 +346,13 @@ bool prepare_tcp_segment6(
bool prepare_tcp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
uint32_t *timestamps,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@@ -325,9 +364,9 @@ bool prepare_tcp_segment(
uint8_t *buf, size_t *buflen)
{
return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ?
prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,tos,ip_id,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) :
prepare_tcp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,tcp_flags,sack,nmss,nseq,nack_seq,nwsize,scale_factor,timestamps,DF,ttl,tos,ip_id,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) :
(src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ?
prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,flow_label,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) :
prepare_tcp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,tcp_flags,sack,nmss,nseq,nack_seq,nwsize,scale_factor,timestamps,ttl,flow_label,fooling,badseq_increment,badseq_ack_increment,data,len,buf,buflen) :
false;
}
@@ -335,6 +374,7 @@ bool prepare_tcp_segment(
// padlen<0 means payload shrinking
bool prepare_udp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@@ -361,7 +401,7 @@ bool prepare_udp_segment4(
uint8_t *payload = (uint8_t*)(udp+1);
fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_UDP, ttl, tos, ip_id);
fill_iphdr(ip, &src->sin_addr, &dst->sin_addr, pktlen, IPPROTO_UDP, DF, ttl, tos, ip_id);
fill_udphdr(udp, src->sin_port, dst->sin_port, datalen);
memcpy(payload,data,len);
@@ -370,7 +410,7 @@ bool prepare_udp_segment4(
else
memset(payload+len,0,padlen);
udp4_fix_checksum(udp,ip_payload_len,&ip->ip_src,&ip->ip_dst);
if (fooling & FOOL_BADSUM) udp->uh_sum^=htons(0xBEAF);
if (fooling & FOOL_BADSUM) udp->uh_sum^=(1+random()%0xFFFF);
*buflen = pktlen;
return true;
@@ -459,13 +499,14 @@ bool prepare_udp_segment6(
else
memset(payload+len,0,padlen);
udp6_fix_checksum(udp,transport_payload_len,&ip6->ip6_src,&ip6->ip6_dst);
if (fooling & FOOL_BADSUM) udp->uh_sum^=htons(0xBEAF);
if (fooling & FOOL_BADSUM) udp->uh_sum^=(1+random()%0xFFFF);
*buflen = pktlen;
return true;
}
bool prepare_udp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@@ -477,7 +518,7 @@ bool prepare_udp_segment(
uint8_t *buf, size_t *buflen)
{
return (src->sa_family==AF_INET && dst->sa_family==AF_INET) ?
prepare_udp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,ttl,tos,ip_id,fooling,padding,padding_size,padlen,data,len,buf,buflen) :
prepare_udp_segment4((struct sockaddr_in *)src,(struct sockaddr_in *)dst,DF,ttl,tos,ip_id,fooling,padding,padding_size,padlen,data,len,buf,buflen) :
(src->sa_family==AF_INET6 && dst->sa_family==AF_INET6) ?
prepare_udp_segment6((struct sockaddr_in6 *)src,(struct sockaddr_in6 *)dst,ttl,flow_label,fooling,padding,padding_size,padlen,data,len,buf,buflen) :
false;
@@ -601,10 +642,29 @@ bool ip_frag(
return false;
}
void rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl)
bool rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl)
{
if (ip) ip->ip_ttl = ttl;
if (ip6) ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl;
if (ttl)
{
if (ip)
{
if (ip->ip_ttl!=ttl)
{
ip->ip_ttl = ttl;
ip4_fix_checksum(ip);
return true;
}
}
else if (ip6)
{
if (ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim!=ttl)
{
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = ttl;
return true;
}
}
}
return false;
}
@@ -1747,7 +1807,9 @@ nofix:
bytes = sendto(sock, data, len, 0, (struct sockaddr*)&dst2, salen);
if (bytes==-1)
{
DLOG_PERROR("rawsend: sendto");
char s[40];
snprintf(s,sizeof(s),"rawsend: sendto (%zu)",len);
DLOG_PERROR(s);
return false;
}
return true;
@@ -1770,16 +1832,15 @@ bool rawsend_queue(struct rawpacket_tailhead *q)
}
// return guessed fake ttl value. 0 means unsuccessfull, should not perform autottl fooling
// ttl = TTL of incoming packet
uint8_t autottl_guess(uint8_t ttl, const autottl *attl)
uint8_t hop_count_guess(uint8_t ttl)
{
uint8_t orig, path, fake;
// 18.65.168.125 ( cloudfront ) 255
// 157.254.246.178 128
// 1.1.1.1 64
// guess original ttl. consider path lengths less than 32 hops
uint8_t orig;
if (ttl>223)
orig=255;
else if (ttl<128 && ttl>96)
@@ -1789,13 +1850,21 @@ uint8_t autottl_guess(uint8_t ttl, const autottl *attl)
else
return 0;
path = orig - ttl;
return orig - ttl;
}
// return guessed fake ttl value. 0 means unsuccessfull, should not perform autottl fooling
uint8_t autottl_eval(uint8_t hop_count, const autottl *attl)
{
uint8_t fake;
int d;
fake = path > attl->delta ? path - attl->delta : attl->min;
if (fake<attl->min) fake=attl->min;
else if (fake>attl->max) fake=attl->max;
d = (int)hop_count + attl->delta;
if (d<attl->min) fake=attl->min;
else if (d>attl->max) fake=attl->max;
else fake=(uint8_t)d;
if (fake>=path) return 0;
if (attl->delta<0 && fake>=hop_count || attl->delta>=0 && fake<hop_count)
return 0;
return fake;
}
@@ -1847,15 +1916,15 @@ void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transpo
{
if (!(verdict & VERDICT_NOCSUM))
{
#ifdef __CYGWIN__
// always fix csum for windivert. original can be partial or bad
#ifndef __CYGWIN__
#ifdef __FreeBSD__
if ((verdict & VERDICT_MASK)!=VERDICT_DROP)
#elif defined(__FreeBSD__)
// FreeBSD tend to pass ipv6 frames with wrong checksum
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr)
#else
// if original packet was tampered earlier it needs checksum fixed
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY)
#endif
#endif
tcp_fix_checksum(tcphdr,transport_len,ip,ip6hdr);
}
@@ -1864,15 +1933,15 @@ void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transpo
{
if (!(verdict & VERDICT_NOCSUM))
{
#ifdef __CYGWIN__
// always fix csum for windivert. original can be partial or bad
#ifndef __CYGWIN__
#ifdef __FreeBSD__
if ((verdict & VERDICT_MASK)!=VERDICT_DROP)
#elif defined(__FreeBSD__)
// FreeBSD tend to pass ipv6 frames with wrong checksum
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr)
#else
// if original packet was tampered earlier it needs checksum fixed
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY)
#endif
#endif
udp_fix_checksum(udphdr,transport_len,ip,ip6hdr);
}

View File

@@ -59,6 +59,7 @@ uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment);
#define VERDICT_DROP 2
#define VERDICT_MASK 3
#define VERDICT_NOCSUM 4
#define VERDICT_GARBAGE 8
#define IP4_TOS(ip_header) (ip_header ? ip_header->ip_tos : 0)
#define IP4_IP_ID(ip_header) (ip_header ? ip_header->ip_id : 0)
@@ -68,10 +69,13 @@ uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment);
bool prepare_tcp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
uint32_t *timestamps,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@@ -83,6 +87,8 @@ bool prepare_tcp_segment4(
bool prepare_tcp_segment6(
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
@@ -97,10 +103,13 @@ bool prepare_tcp_segment6(
bool prepare_tcp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
uint8_t tcp_flags,
bool sack,
uint16_t nmss,
uint32_t nseq, uint32_t nack_seq,
uint16_t nwsize,
uint8_t scale_factor,
uint32_t *timestamps,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@@ -114,6 +123,7 @@ bool prepare_tcp_segment(
bool prepare_udp_segment4(
const struct sockaddr_in *src, const struct sockaddr_in *dst,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@@ -133,6 +143,7 @@ bool prepare_udp_segment6(
uint8_t *buf, size_t *buflen);
bool prepare_udp_segment(
const struct sockaddr *src, const struct sockaddr *dst,
bool DF,
uint8_t ttl,
uint8_t tos,
uint16_t ip_id,
@@ -162,15 +173,20 @@ bool ip_frag(
uint8_t *pkt1, size_t *pkt1_size,
uint8_t *pkt2, size_t *pkt2_size);
void rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl);
bool rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl);
void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport);
void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr,const struct udphdr *udphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst);
uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind);
uint32_t *tcp_find_timestamps(struct tcphdr *tcp);
uint8_t tcp_find_scale_factor(const struct tcphdr *tcp);
uint16_t tcp_find_mss(struct tcphdr *tcp);
bool tcp_has_sack(struct tcphdr *tcp);
bool tcp_has_fastopen(const struct tcphdr *tcp);
bool ip_has_df(const struct ip *ip);
#ifdef __CYGWIN__
extern uint32_t w_win32_error;
@@ -242,15 +258,13 @@ void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_fac
typedef struct
{
uint8_t delta, min, max;
int8_t delta;
uint8_t min, max;
} autottl;
#define AUTOTTL_DEFAULT_DELTA 1
#define AUTOTTL_DEFAULT_MIN 3
#define AUTOTTL_DEFAULT_MAX 20
#define AUTOTTL_ENABLED(a) (!!(a).delta)
#define AUTOTTL_SET_DEFAULT(a) {(a).delta=AUTOTTL_DEFAULT_DELTA; (a).min=AUTOTTL_DEFAULT_MIN; (a).max=AUTOTTL_DEFAULT_MAX;}
#define AUTOTTL_ENABLED(a) ((a).delta || (a).min || (a).max)
uint8_t autottl_guess(uint8_t ttl, const autottl *attl);
uint8_t hop_count_guess(uint8_t ttl);
uint8_t autottl_eval(uint8_t hop_count, const autottl *attl);
void do_nat(bool bOutbound, struct ip *ip, struct ip6_hdr *ip6, struct tcphdr *tcphdr, struct udphdr *udphdr, const struct sockaddr_in *target4, const struct sockaddr_in6 *target6);
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr);

File diff suppressed because it is too large Load Diff

View File

@@ -41,7 +41,7 @@ enum dpi_desync_mode {
};
extern const char *fake_http_request_default;
extern const uint8_t fake_tls_clienthello_default[648];
extern const uint8_t fake_tls_clienthello_default[680];
void randomize_default_tls_payload(uint8_t *p);
enum dpi_desync_mode desync_mode_from_string(const char *s);
@@ -52,4 +52,4 @@ bool desync_valid_second_stage(enum dpi_desync_mode mode);
bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode);
bool desync_valid_second_stage_udp(enum dpi_desync_mode mode);
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt);
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifin, const char *ifout, uint8_t *data_pkt, size_t *len_pkt);

View File

@@ -9,6 +9,7 @@
#include <ctype.h>
#include <sys/stat.h>
#include <libgen.h>
#include <fcntl.h>
int unique_size_t(size_t *pu, int ct)
{
@@ -313,6 +314,17 @@ bool file_mod_signature(const char *filename, file_mod_sig *ms)
return true;
}
bool file_open_test(const char *filename, int flags)
{
int fd = open(filename,flags);
if (fd>=0)
{
close(fd);
return true;
}
return false;
}
bool pf_in_range(uint16_t port, const port_filter *pf)
{
return port && (((!pf->from && !pf->to) || (port>=pf->from && port<=pf->to)) ^ pf->neg);
@@ -379,6 +391,12 @@ void fill_random_az09(uint8_t *p,size_t sz)
}
}
void set_console_io_buffering(void)
{
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(stderr, NULL, _IOLBF, 0);
}
bool set_env_exedir(const char *argv0)
{
char *s,*d;

View File

@@ -9,6 +9,8 @@
#include <stdio.h>
#include <time.h>
#define UNARY_PLUS(v) (v>0 ? "+" : "")
// this saves memory. sockaddr_storage is larger than required. it can be 128 bytes. sockaddr_in6 is 28 bytes.
typedef union
{
@@ -77,6 +79,7 @@ typedef struct
#define FILE_MOD_RESET(ms) memset(ms,0,sizeof(file_mod_sig))
bool file_mod_signature(const char *filename, file_mod_sig *ms);
time_t file_mod_time(const char *filename);
bool file_open_test(const char *filename, int flags);
typedef struct
{
@@ -91,6 +94,7 @@ void fill_random_bytes(uint8_t *p,size_t sz);
void fill_random_az(uint8_t *p,size_t sz);
void fill_random_az09(uint8_t *p,size_t sz);
void set_console_io_buffering(void);
bool set_env_exedir(const char *argv0);

View File

@@ -4,7 +4,7 @@
#include "helpers.h"
// inplace tolower() and add to pool
static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
static bool addpool(hostlist_pool **hostlist, char **s, const char *end, int *ct)
{
char *p=*s;
@@ -17,10 +17,16 @@ static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
else
{
// advance until eol lowering all chars
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!StrPoolAddStrLen(hostlist, *s, p-*s))
uint32_t flags = 0;
if (*p=='^')
{
StrPoolDestroy(hostlist);
p = ++(*s);
flags |= HOSTLIST_POOL_FLAG_STRICT_MATCH;
}
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!HostlistPoolAddStrLen(hostlist, *s, p-*s, flags))
{
HostlistPoolDestroy(hostlist);
*hostlist = NULL;
return false;
}
@@ -32,12 +38,12 @@ static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
return true;
}
bool AppendHostlistItem(strpool **hostlist, char *s)
bool AppendHostlistItem(hostlist_pool **hostlist, char *s)
{
return addpool(hostlist,&s,s+strlen(s),NULL);
}
bool AppendHostList(strpool **hostlist, const char *filename)
bool AppendHostList(hostlist_pool **hostlist, const char *filename)
{
char *p, *e, s[256], *zbuf;
size_t zsize;
@@ -109,14 +115,15 @@ static bool LoadHostList(struct hostlist_file *hfile)
if (!file_mod_signature(hfile->filename, &fsig))
{
// stat() error
DLOG_PERROR("file_mod_signature");
DLOG_ERR("cannot access hostlist file '%s'. in-memory content remains unchanged.\n",hfile->filename);
return true;
}
if (FILE_MOD_COMPARE(&hfile->mod_sig,&fsig)) return true; // up to date
StrPoolDestroy(&hfile->hostlist);
HostlistPoolDestroy(&hfile->hostlist);
if (!AppendHostList(&hfile->hostlist, hfile->filename))
{
StrPoolDestroy(&hfile->hostlist);
HostlistPoolDestroy(&hfile->hostlist);
return false;
}
hfile->mod_sig=fsig;
@@ -137,10 +144,10 @@ static bool LoadHostLists(struct hostlist_files_head *list)
return bres;
}
bool NonEmptyHostlist(strpool **hostlist)
bool NonEmptyHostlist(hostlist_pool **hostlist)
{
// add impossible hostname if the list is empty
return *hostlist ? true : StrPoolAddStrLen(hostlist, "@&()", 4);
return *hostlist ? true : HostlistPoolAddStrLen(hostlist, "@&()", 4, 0);
}
static void MakeAutolistsNonEmpty()
@@ -163,19 +170,34 @@ bool LoadAllHostLists()
static bool SearchHostList(strpool *hostlist, const char *host)
static bool SearchHostList(hostlist_pool *hostlist, const char *host)
{
if (hostlist)
{
const char *p = host;
bool bInHostList;
const struct hostlist_pool *hp;
bool bHostFull=true;
while (p)
{
bInHostList = StrPoolCheckStr(hostlist, p);
DLOG("hostlist check for %s : %s\n", p, bInHostList ? "positive" : "negative");
if (bInHostList) return true;
DLOG("hostlist check for %s : ", p);
hp = HostlistPoolGetStr(hostlist, p);
if (hp)
{
if ((hp->flags & HOSTLIST_POOL_FLAG_STRICT_MATCH) && !bHostFull)
{
DLOG("negative : strict_mismatch : %s != %s\n", p, host);
}
else
{
DLOG("positive\n");
return true;
}
}
else
DLOG("negative\n");
p = strchr(p, '.');
if (p) p++;
bHostFull = false;
}
}
return false;
@@ -265,11 +287,13 @@ static struct hostlist_file *RegisterHostlist_(struct hostlist_files_head *hostl
}
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename)
{
/*
if (filename && !file_mod_time(filename))
{
DLOG_ERR("cannot access hostlist file '%s'\n",filename);
return NULL;
}
*/
return RegisterHostlist_(
&params.hostlists,
bExclude ? &dp->hl_collection_exclude : &dp->hl_collection,

View File

@@ -4,10 +4,10 @@
#include "pools.h"
#include "params.h"
bool AppendHostlistItem(strpool **hostlist, char *s);
bool AppendHostList(strpool **hostlist, const char *filename);
bool AppendHostlistItem(hostlist_pool **hostlist, char *s);
bool AppendHostList(hostlist_pool **hostlist, const char *filename);
bool LoadAllHostLists();
bool NonEmptyHostlist(strpool **hostlist);
bool NonEmptyHostlist(hostlist_pool **hostlist);
// return : true = apply fooling, false = do not apply
bool HostlistCheck(const struct desync_profile *dp,const char *host, bool *excluded, bool bSkipReloadCheck);
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename);

View File

@@ -130,6 +130,7 @@ static bool LoadIpset(struct ipset_file *hfile)
if (!file_mod_signature(hfile->filename, &fsig))
{
// stat() error
DLOG_PERROR("file_mod_signature");
DLOG_ERR("cannot access ipset file '%s'. in-memory content remains unchanged.\n",hfile->filename);
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "packet_queue.h"
@@ -25,7 +26,7 @@ void rawpacket_queue_destroy(struct rawpacket_tailhead *q)
while((rp = rawpacket_dequeue(q))) rawpacket_free(rp);
}
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len,size_t len_payload)
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload)
{
struct rawpacket *rp = malloc(sizeof(struct rawpacket));
if (!rp) return NULL;
@@ -39,13 +40,14 @@ struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sock
rp->dst = *dst;
rp->fwmark = fwmark;
if (ifout)
{
strncpy(rp->ifout,ifout,sizeof(rp->ifout));
rp->ifout[sizeof(rp->ifout)-1]=0;
}
if (ifin)
snprintf(rp->ifin,sizeof(rp->ifin),"%s",ifin);
else
rp->ifout[0]=0;
*rp->ifin = 0;
if (ifout)
snprintf(rp->ifout,sizeof(rp->ifout),"%s",ifout);
else
*rp->ifout = 0;
memcpy(rp->packet,data,len);
rp->len=len;
rp->len_payload=len_payload;

View File

@@ -9,7 +9,7 @@
struct rawpacket
{
struct sockaddr_storage dst;
char ifout[IFNAMSIZ+1];
char ifin[IFNAMSIZ], ifout[IFNAMSIZ];
uint32_t fwmark;
size_t len, len_payload;
uint8_t *packet;
@@ -21,6 +21,6 @@ void rawpacket_queue_init(struct rawpacket_tailhead *q);
void rawpacket_queue_destroy(struct rawpacket_tailhead *q);
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q);
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len,size_t len_payload);
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload);
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q);
void rawpacket_free(struct rawpacket *rp);

View File

@@ -65,6 +65,7 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list
{
va_copy(args2,args);
DLOG_CON(format,syslog_priority,args2);
va_end(args2);
}
if (params.debug)
{
@@ -184,34 +185,62 @@ void dp_init(struct desync_profile *dp)
dp->desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT;
dp->desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT;
dp->desync_repeats = 1;
dp->fake_tls_size = sizeof(fake_tls_clienthello_default);
memcpy(dp->fake_tls,fake_tls_clienthello_default,dp->fake_tls_size);
dp->fake_tls_mod = 0;
dp->fake_http_size = strlen(fake_http_request_default);
memcpy(dp->fake_http,fake_http_request_default,dp->fake_http_size);
dp->fake_quic_size = 620; // must be 601+ for TSPU hack
dp->fake_quic[0] = 0x40; // russian TSPU QUIC short header fake
dp->fake_wg_size = 64;
dp->fake_dht_size = 64;
dp->fake_unknown_size = 256;
dp->fake_syndata_size = 16;
dp->fake_unknown_udp_size = 64;
dp->wscale=-1; // default - dont change scale factor (client)
dp->desync_ttl6 = 0xFF; // unused
dp->desync_badseq_increment = BADSEQ_INCREMENT_DEFAULT;
dp->desync_badseq_ack_increment = BADSEQ_ACK_INCREMENT_DEFAULT;
dp->wssize_cutoff_mode = dp->desync_start_mode = dp->desync_cutoff_mode = 'n'; // packet number by default
dp->desync_ttl6 = dp->dup_ttl6 = dp->orig_mod_ttl6 = 0xFF; // unused
dp->desync_badseq_increment = dp->dup_badseq_increment = BADSEQ_INCREMENT_DEFAULT;
dp->desync_badseq_ack_increment = dp->dup_badseq_ack_increment = BADSEQ_ACK_INCREMENT_DEFAULT;
dp->wssize_cutoff_mode = dp->desync_start_mode = dp->desync_cutoff_mode = dp->dup_start_mode = dp->dup_cutoff_mode = dp->orig_mod_start_mode = dp->orig_mod_cutoff_mode = 'n'; // packet number by default
dp->udplen_increment = UDPLEN_INCREMENT_DEFAULT;
dp->hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT;
dp->hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT;
dp->hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT;
dp->filter_ipv4 = dp->filter_ipv6 = true;
}
bool dp_fake_defaults(struct desync_profile *dp)
{
struct blob_item *item;
if (blob_collection_empty(&dp->fake_http))
if (!blob_collection_add_blob(&dp->fake_http,fake_http_request_default,strlen(fake_http_request_default),0))
return false;
if (blob_collection_empty(&dp->fake_tls))
{
if (!(item=blob_collection_add_blob(&dp->fake_tls,fake_tls_clienthello_default,sizeof(fake_tls_clienthello_default),4+sizeof(((struct fake_tls_mod*)0)->sni))))
return false;
if (!(item->extra2 = malloc(sizeof(struct fake_tls_mod))))
return false;
*(struct fake_tls_mod*)item->extra2 = dp->tls_mod_last;
}
if (blob_collection_empty(&dp->fake_unknown))
{
if (!(item=blob_collection_add_blob(&dp->fake_unknown,NULL,256,0)))
return false;
memset(item->data,0,item->size);
}
if (blob_collection_empty(&dp->fake_quic))
{
if (!(item=blob_collection_add_blob(&dp->fake_quic,NULL,620,0)))
return false;
memset(item->data,0,item->size);
item->data[0] = 0x40;
}
struct blob_collection_head **fake,*fakes_z64[] = {&dp->fake_wg, &dp->fake_dht, &dp->fake_discord, &dp->fake_stun, &dp->fake_unknown_udp,NULL};
for(fake=fakes_z64;*fake;fake++)
{
if (blob_collection_empty(*fake))
{
if (!(item=blob_collection_add_blob(*fake,NULL,64,0)))
return false;
memset(item->data,0,item->size);
}
}
return true;
}
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
{
struct desync_profile_list *entry = calloc(1,sizeof(struct desync_profile_list));
if (!entry) return NULL;
dp_init(&entry->dp);
// add to the tail
@@ -235,6 +264,8 @@ static void dp_clear_dynamic(struct desync_profile *dp)
port_filters_destroy(&dp->pf_tcp);
port_filters_destroy(&dp->pf_udp);
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);
}
void dp_clear(struct desync_profile *dp)
{
@@ -263,3 +294,12 @@ bool dp_list_have_autohostlist(struct desync_profile_list_head *head)
return true;
return false;
}
// check if we need empty outgoing ACK
bool dp_list_need_all_out(struct desync_profile_list_head *head)
{
struct desync_profile_list *dpl;
LIST_FOREACH(dpl, head, next)
if (dpl->dp.dup_repeats || PROFILE_HAS_ORIG_MOD(&dpl->dp))
return true;
return false;
}

View File

@@ -36,17 +36,49 @@
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
#define HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT 3
#define IPCACHE_LIFETIME 7200
#define AUTOTTL_DEFAULT_DESYNC_DELTA -1
#define AUTOTTL_DEFAULT_DESYNC_MIN 3
#define AUTOTTL_DEFAULT_DESYNC_MAX 20
#define AUTOTTL_DEFAULT_ORIG_DELTA +5
#define AUTOTTL_DEFAULT_ORIG_MIN 3
#define AUTOTTL_DEFAULT_ORIG_MAX 64
#define AUTOTTL_DEFAULT_DUP_DELTA -1
#define AUTOTTL_DEFAULT_DUP_MIN 3
#define AUTOTTL_DEFAULT_DUP_MAX 64
#define MAX_SPLITS 64
#define FAKE_TLS_MOD_SAVE_MASK 0x0F
#define FAKE_TLS_MOD_SET 0x01
#define FAKE_TLS_MOD_CUSTOM_FAKE 0x02
#define FAKE_TLS_MOD_RND 0x10
#define FAKE_TLS_MOD_RND_SNI 0x20
#define FAKE_TLS_MOD_PADENCAP 0x40
#define FAKE_TLS_MOD_DUP_SID 0x20
#define FAKE_TLS_MOD_RND_SNI 0x40
#define FAKE_TLS_MOD_SNI 0x80
#define FAKE_TLS_MOD_PADENCAP 0x100
#define FAKE_MAX_TCP 1460
#define FAKE_MAX_UDP 1472
#define MAX_GIDS 64
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
struct fake_tls_mod_cache
{
size_t extlen_offset, padlen_offset;
};
struct fake_tls_mod
{
char sni[64];
uint32_t mod;
};
typedef enum {SS_NONE=0,SS_SYN,SS_SYNACK,SS_ACKSYN} t_synack_split;
struct desync_profile
{
int n; // number of the profile
@@ -56,6 +88,8 @@ struct desync_profile
char wssize_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int wssize_cutoff;
t_synack_split synack_split;
bool hostcase, hostnospace, domcase, methodeol;
char hostspell[4];
enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2;
@@ -67,18 +101,33 @@ struct desync_profile
int split_count;
struct proto_pos seqovl;
char dup_start_mode, dup_cutoff_mode; // n - packets, d - data packets, s - relative sequence
bool dup_replace;
unsigned int dup_start, dup_cutoff;
unsigned int dup_repeats;
uint8_t dup_ttl, dup_ttl6;
uint32_t dup_fooling_mode;
uint32_t dup_badseq_increment, dup_badseq_ack_increment;
autottl dup_autottl, dup_autottl6;
char orig_mod_start_mode, orig_mod_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int orig_mod_start, orig_mod_cutoff;
uint8_t orig_mod_ttl, orig_mod_ttl6;
autottl orig_autottl, orig_autottl6;
char desync_start_mode, desync_cutoff_mode; // n - packets, d - data packets, s - relative sequence
unsigned int desync_start, desync_cutoff;
uint8_t desync_ttl, desync_ttl6;
autottl desync_autottl, desync_autottl6;
uint32_t desync_fooling_mode;
uint32_t desync_badseq_increment, desync_badseq_ack_increment;
uint8_t fake_http[1460],fake_unknown[1460],fake_syndata[1460],seqovl_pattern[1460],fsplit_pattern[1460];
uint8_t fake_unknown_udp[1472],udplen_pattern[1472],fake_quic[1472],fake_wg[1472],fake_dht[1472];
size_t fake_http_size,fake_quic_size,fake_wg_size,fake_dht_size,fake_unknown_size,fake_syndata_size,fake_unknown_udp_size;
uint8_t fake_tls[1460],fake_tls_mod;
size_t fake_tls_size, fake_tls_extlen_offset, fake_tls_padlen_offset;
struct blob_collection_head fake_http,fake_tls,fake_unknown,fake_unknown_udp,fake_quic,fake_wg,fake_dht,fake_discord,fake_stun;
uint8_t fake_syndata[FAKE_MAX_TCP],seqovl_pattern[FAKE_MAX_TCP],fsplit_pattern[FAKE_MAX_TCP],udplen_pattern[FAKE_MAX_UDP];
size_t fake_syndata_size;
struct fake_tls_mod tls_mod_last;
struct blob_item *tls_fake_last;
int udplen_increment;
@@ -98,9 +147,10 @@ struct desync_profile
hostfail_pool *hostlist_auto_fail_counters;
};
#define PROFILE_IPSETS_ABSENT(dp) (!LIST_FIRST(&dp->ips_collection) && !LIST_FIRST(&dp->ips_collection_exclude))
#define PROFILE_IPSETS_EMPTY(dp) (ipset_collection_is_empty(&dp->ips_collection) && ipset_collection_is_empty(&dp->ips_collection_exclude))
#define PROFILE_HOSTLISTS_EMPTY(dp) (hostlist_collection_is_empty(&dp->hl_collection) && hostlist_collection_is_empty(&dp->hl_collection_exclude))
#define PROFILE_IPSETS_ABSENT(dp) (!LIST_FIRST(&(dp)->ips_collection) && !LIST_FIRST(&(dp)->ips_collection_exclude))
#define PROFILE_IPSETS_EMPTY(dp) (ipset_collection_is_empty(&(dp)->ips_collection) && ipset_collection_is_empty(&(dp)->ips_collection_exclude))
#define PROFILE_HOSTLISTS_EMPTY(dp) (hostlist_collection_is_empty(&(dp)->hl_collection) && hostlist_collection_is_empty(&(dp)->hl_collection_exclude))
#define PROFILE_HAS_ORIG_MOD(dp) ((dp)->orig_mod_ttl || (dp)->orig_mod_ttl6)
struct desync_profile_list {
struct desync_profile dp;
@@ -111,7 +161,9 @@ struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head);
void dp_entry_destroy(struct desync_profile_list *entry);
void dp_list_destroy(struct desync_profile_list_head *head);
bool dp_list_have_autohostlist(struct desync_profile_list_head *head);
bool dp_list_need_all_out(struct desync_profile_list_head *head);
void dp_init(struct desync_profile *dp);
bool dp_fake_defaults(struct desync_profile *dp);
void dp_clear(struct desync_profile *dp);
struct params_s
@@ -124,6 +176,8 @@ struct params_s
char debug_logfile[PATH_MAX];
bool debug;
bool daemon;
#ifdef __linux__
int qnum;
#elif defined(BSD)
@@ -139,8 +193,10 @@ struct params_s
#else
bool droproot;
uid_t uid;
gid_t gid;
gid_t gid[MAX_GIDS];
int gid_count;
#endif
char pidfile[PATH_MAX];
char hostlist_auto_debuglog[PATH_MAX];
@@ -151,6 +207,11 @@ struct params_s
unsigned int ctrack_t_syn, ctrack_t_est, ctrack_t_fin, ctrack_t_udp;
t_conntrack conntrack;
bool ctrack_disable;
bool autottl_present,cache_hostname;
unsigned int ipcache_lifetime;
ip_cache ipcache;
};
extern struct params_s params;

View File

@@ -3,6 +3,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#define DESTROY_STR_POOL(etype, ppool) \
etype *elem, *tmp; \
@@ -31,6 +32,9 @@
free(elem); \
return false; \
}
#define ADD_HOSTLIST_POOL(etype, ppool, keystr, keystr_len, flg) \
ADD_STR_POOL(etype,ppool,keystr,keystr_len); \
elem->flags = flg;
#undef uthash_nonfatal_oom
@@ -42,27 +46,31 @@ static void ut_oom_recover(void *elem)
}
// for not zero terminated strings
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
bool HostlistPoolAddStrLen(hostlist_pool **pp, const char *s, size_t slen, uint32_t flags)
{
ADD_STR_POOL(strpool, pp, s, slen)
ADD_HOSTLIST_POOL(hostlist_pool, pp, s, slen, flags)
return true;
}
// for zero terminated strings
bool StrPoolAddStr(strpool **pp, const char *s)
bool HostlistPoolAddStr(hostlist_pool **pp, const char *s, uint32_t flags)
{
return StrPoolAddStrLen(pp, s, strlen(s));
return HostlistPoolAddStrLen(pp, s, strlen(s), flags);
}
bool StrPoolCheckStr(strpool *p, const char *s)
hostlist_pool *HostlistPoolGetStr(hostlist_pool *p, const char *s)
{
strpool *elem;
hostlist_pool *elem;
HASH_FIND_STR(p, s, elem);
return elem != NULL;
return elem;
}
bool HostlistPoolCheckStr(hostlist_pool *p, const char *s)
{
return !!HostlistPoolGetStr(p,s);
}
void StrPoolDestroy(strpool **pp)
void HostlistPoolDestroy(hostlist_pool **pp)
{
DESTROY_STR_POOL(strpool, pp)
DESTROY_STR_POOL(hostlist_pool, pp)
}
@@ -178,7 +186,7 @@ struct hostlist_file *hostlist_files_add(struct hostlist_files_head *head, const
static void hostlist_files_entry_destroy(struct hostlist_file *entry)
{
free(entry->filename);
StrPoolDestroy(&entry->hostlist);
HostlistPoolDestroy(&entry->hostlist);
free(entry);
}
void hostlist_files_destroy(struct hostlist_files_head *head)
@@ -510,3 +518,281 @@ bool port_filters_deny_if_empty(struct port_filters_head *head)
if (LIST_FIRST(head)) return true;
return pf_parse("0",&pf) && port_filter_add(head,&pf);
}
struct blob_item *blob_collection_add(struct blob_collection_head *head)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
if (entry)
{
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
{
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
LIST_INSERT_AFTER(iteml, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
}
return entry;
}
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
if (!entry) return NULL;
if (!(entry->data = malloc(size+size_reserve)))
{
free(entry);
return NULL;
}
if (data) memcpy(entry->data,data,size);
entry->size = size;
entry->size_buf = size+size_reserve;
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
{
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
LIST_INSERT_AFTER(iteml, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
return entry;
}
void blob_collection_destroy(struct blob_collection_head *head)
{
struct blob_item *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
free(entry->extra);
free(entry->extra2);
free(entry->data);
free(entry);
}
}
bool blob_collection_empty(const struct blob_collection_head *head)
{
return !LIST_FIRST(head);
}
static void ipcache_item_touch(ip_cache_item *item)
{
time(&item->last);
}
static void ipcache_item_init(ip_cache_item *item)
{
ipcache_item_touch(item);
item->hostname = NULL;
item->hops = 0;
}
static void ipcache_item_destroy(ip_cache_item *item)
{
free(item->hostname);
}
static void ipcache4Destroy(ip_cache4 **ipcache)
{
ip_cache4 *elem, *tmp;
HASH_ITER(hh, *ipcache, elem, tmp)
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
static void ipcache4Key(ip4if *key, const struct in_addr *a, const char *iface)
{
memset(key,0,sizeof(*key)); // make sure everything is zero
key->addr = *a;
if (iface) snprintf(key->iface,sizeof(key->iface),"%s",iface);
}
static ip_cache4 *ipcache4Find(ip_cache4 *ipcache, const struct in_addr *a, const char *iface)
{
ip_cache4 *entry;
struct ip4if key;
ipcache4Key(&key,a,iface);
HASH_FIND(hh, ipcache, &key, sizeof(key), entry);
return entry;
}
static ip_cache4 *ipcache4Add(ip_cache4 **ipcache, const struct in_addr *a, const char *iface)
{
// avoid dups
ip_cache4 *entry = ipcache4Find(*ipcache,a,iface);
if (entry) return entry; // already included
entry = malloc(sizeof(ip_cache4));
if (!entry) return NULL;
ipcache4Key(&entry->key,a,iface);
oom = false;
HASH_ADD(hh, *ipcache, key, sizeof(entry->key), entry);
if (oom) { free(entry); return NULL; }
ipcache_item_init(&entry->data);
return entry;
}
static void ipcache4Print(ip_cache4 *ipcache)
{
char s_ip[16];
time_t now;
ip_cache4 *ipc, *tmp;
time(&now);
HASH_ITER(hh, ipcache , ipc, tmp)
{
*s_ip=0;
inet_ntop(AF_INET, &ipc->key.addr, s_ip, sizeof(s_ip));
printf("%s iface=%s : hops %u hostname=%s now=last+%llu\n", s_ip, ipc->key.iface, ipc->data.hops, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last));
}
}
static void ipcache6Destroy(ip_cache6 **ipcache)
{
ip_cache6 *elem, *tmp;
HASH_ITER(hh, *ipcache, elem, tmp)
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
static void ipcache6Key(ip6if *key, const struct in6_addr *a, const char *iface)
{
memset(key,0,sizeof(*key)); // make sure everything is zero
key->addr = *a;
if (iface) snprintf(key->iface,sizeof(key->iface),"%s",iface);
}
static ip_cache6 *ipcache6Find(ip_cache6 *ipcache, const struct in6_addr *a, const char *iface)
{
ip_cache6 *entry;
ip6if key;
ipcache6Key(&key,a,iface);
HASH_FIND(hh, ipcache, &key, sizeof(key), entry);
return entry;
}
static ip_cache6 *ipcache6Add(ip_cache6 **ipcache, const struct in6_addr *a, const char *iface)
{
// avoid dups
ip_cache6 *entry = ipcache6Find(*ipcache,a,iface);
if (entry) return entry; // already included
entry = malloc(sizeof(ip_cache6));
if (!entry) return NULL;
ipcache6Key(&entry->key,a,iface);
oom = false;
HASH_ADD(hh, *ipcache, key, sizeof(entry->key), entry);
if (oom) { free(entry); return NULL; }
ipcache_item_init(&entry->data);
return entry;
}
static void ipcache6Print(ip_cache6 *ipcache)
{
char s_ip[40];
time_t now;
ip_cache6 *ipc, *tmp;
time(&now);
HASH_ITER(hh, ipcache , ipc, tmp)
{
*s_ip=0;
inet_ntop(AF_INET6, &ipc->key.addr, s_ip, sizeof(s_ip));
printf("%s iface=%s : hops %u hostname=%s now=last+%llu\n", s_ip, ipc->key.iface, ipc->data.hops, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last));
}
}
void ipcacheDestroy(ip_cache *ipcache)
{
ipcache4Destroy(&ipcache->ipcache4);
ipcache6Destroy(&ipcache->ipcache6);
}
void ipcachePrint(ip_cache *ipcache)
{
ipcache4Print(ipcache->ipcache4);
ipcache6Print(ipcache->ipcache6);
}
ip_cache_item *ipcacheTouch(ip_cache *ipcache, const struct in_addr *a4, const struct in6_addr *a6, const char *iface)
{
ip_cache4 *ipcache4;
ip_cache6 *ipcache6;
if (a4)
{
if ((ipcache4 = ipcache4Add(&ipcache->ipcache4,a4,iface)))
{
ipcache_item_touch(&ipcache4->data);
return &ipcache4->data;
}
}
else if (a6)
{
if ((ipcache6 = ipcache6Add(&ipcache->ipcache6,a6,iface)))
{
ipcache_item_touch(&ipcache6->data);
return &ipcache6->data;
}
}
return NULL;
}
static void ipcache4_purge(ip_cache4 **ipcache, time_t lifetime)
{
ip_cache4 *elem, *tmp;
time_t now = time(NULL);
HASH_ITER(hh, *ipcache, elem, tmp)
{
if (now >= (elem->data.last + lifetime))
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
}
static void ipcache6_purge(ip_cache6 **ipcache, time_t lifetime)
{
ip_cache6 *elem, *tmp;
time_t now = time(NULL);
HASH_ITER(hh, *ipcache, elem, tmp)
{
if (now >= (elem->data.last + lifetime))
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
}
static void ipcache_purge(ip_cache *ipcache, time_t lifetime)
{
if (lifetime) // 0 = no expire
{
ipcache4_purge(&ipcache->ipcache4, lifetime);
ipcache6_purge(&ipcache->ipcache6, lifetime);
}
}
static time_t ipcache_purge_prev=0;
void ipcachePurgeRateLimited(ip_cache *ipcache, time_t lifetime)
{
time_t now = time(NULL);
// do not purge too often to save resources
if (ipcache_purge_prev != now)
{
ipcache_purge(ipcache, lifetime);
ipcache_purge_prev = now;
}
}

View File

@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <ctype.h>
#include <sys/queue.h>
#include <net/if.h>
#include <time.h>
#include "helpers.h"
@@ -12,15 +13,18 @@
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
typedef struct strpool {
char *str; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} strpool;
#define HOSTLIST_POOL_FLAG_STRICT_MATCH 1
void StrPoolDestroy(strpool **pp);
bool StrPoolAddStr(strpool **pp,const char *s);
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
bool StrPoolCheckStr(strpool *p,const char *s);
typedef struct hostlist_pool {
char *str; /* key */
uint32_t flags; /* custom data */
UT_hash_handle hh; /* makes this structure hashable */
} hostlist_pool;
void HostlistPoolDestroy(hostlist_pool **pp);
bool HostlistPoolAddStr(hostlist_pool **pp, const char *s, uint32_t flags);
bool HostlistPoolAddStrLen(hostlist_pool **pp, const char *s, size_t slen, uint32_t flags);
hostlist_pool *HostlistPoolGetStr(hostlist_pool *p, const char *s);
struct str_list {
char *str;
@@ -29,10 +33,10 @@ struct str_list {
LIST_HEAD(str_list_head, str_list);
typedef struct hostfail_pool {
char *str; /* key */
int counter; /* value */
time_t expire; /* when to expire record (unixtime) */
UT_hash_handle hh; /* makes this structure hashable */
char *str; /* key */
int counter; /* value */
time_t expire; /* when to expire record (unixtime) */
UT_hash_handle hh; /* makes this structure hashable */
} hostfail_pool;
void HostFailPoolDestroy(hostfail_pool **pp);
@@ -51,7 +55,7 @@ void strlist_destroy(struct str_list_head *head);
struct hostlist_file {
char *filename;
file_mod_sig mod_sig;
strpool *hostlist;
hostlist_pool *hostlist;
LIST_ENTRY(hostlist_file) next;
};
LIST_HEAD(hostlist_files_head, hostlist_file);
@@ -143,3 +147,58 @@ bool port_filter_add(struct port_filters_head *head, const port_filter *pf);
void port_filters_destroy(struct port_filters_head *head);
bool port_filters_in_range(const struct port_filters_head *head, uint16_t port);
bool port_filters_deny_if_empty(struct port_filters_head *head);
struct blob_item {
uint8_t *data; // main data blob
size_t size; // main data blob size
size_t size_buf;// main data blob allocated size
void *extra; // any data without size
void *extra2; // any data without size
LIST_ENTRY(blob_item) next;
};
LIST_HEAD(blob_collection_head, blob_item);
struct blob_item *blob_collection_add(struct blob_collection_head *head);
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve);
void blob_collection_destroy(struct blob_collection_head *head);
bool blob_collection_empty(const struct blob_collection_head *head);
typedef struct ip4if
{
char iface[IFNAMSIZ];
struct in_addr addr;
} ip4if;
typedef struct ip6if
{
char iface[IFNAMSIZ];
struct in6_addr addr;
} ip6if;
typedef struct ip_cache_item
{
time_t last;
char *hostname;
uint8_t hops;
} ip_cache_item;
typedef struct ip_cache4
{
ip4if key;
ip_cache_item data;
UT_hash_handle hh; /* makes this structure hashable */
} ip_cache4;
typedef struct ip_cache6
{
ip6if key;
ip_cache_item data;
UT_hash_handle hh; /* makes this structure hashable */
} ip_cache6;
typedef struct ip_cache
{
ip_cache4 *ipcache4;
ip_cache6 *ipcache6;
} ip_cache;
ip_cache_item *ipcacheTouch(ip_cache *ipcache, const struct in_addr *a4, const struct in6_addr *a6, const char *iface);
void ipcachePurgeRateLimited(ip_cache *ipcache, time_t lifetime);
void ipcacheDestroy(ip_cache *ipcache);
void ipcachePrint(ip_cache *ipcache);

View File

@@ -35,6 +35,8 @@ const char *l7proto_str(t_l7proto l7)
case QUIC: return "quic";
case WIREGUARD: return "wireguard";
case DHT: return "dht";
case DISCORD: return "discord";
case STUN: return "stun";
default: return "unknown";
}
}
@@ -45,7 +47,9 @@ bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7)
(l7proto==TLS && (filter_l7 & L7_PROTO_TLS)) ||
(l7proto==QUIC && (filter_l7 & L7_PROTO_QUIC)) ||
(l7proto==WIREGUARD && (filter_l7 & L7_PROTO_WIREGUARD)) ||
(l7proto==DHT && (filter_l7 & L7_PROTO_DHT));
(l7proto==DHT && (filter_l7 & L7_PROTO_DHT)) ||
(l7proto==DISCORD && (filter_l7 & L7_PROTO_DISCORD)) ||
(l7proto==STUN && (filter_l7 & L7_PROTO_STUN));
}
#define PM_ABS 0
@@ -341,6 +345,19 @@ size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
}
const char *TLSVersionStr(uint16_t tlsver)
{
switch(tlsver)
{
case 0x0301: return "TLS 1.0";
case 0x0302: return "TLS 1.1";
case 0x0303: return "TLS 1.2";
case 0x0304: return "TLS 1.3";
default:
// 0x0a0a, 0x1a1a, ..., 0xfafa
return (((tlsver & 0x0F0F) == 0x0A0A) && ((tlsver>>12)==((tlsver>>4)&0xF))) ? "GREASE" : "UNKNOWN";
}
}
uint16_t TLSRecordDataLen(const uint8_t *data)
{
@@ -844,7 +861,16 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
return !memcmp(data + pn_offset + pkn_len + cryptlen, atag, 16);
}
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len)
struct range64
{
uint64_t offset,len;
};
#define MAX_DEFRAG_PIECES 128
static int cmp_range64(const void * a, const void * b)
{
return (((struct range64*)a)->offset < ((struct range64*)b)->offset) ? -1 : (((struct range64*)a)->offset > ((struct range64*)b)->offset) ? 1 : 0;
}
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull)
{
// Crypto frame can be split into multiple chunks
// chromium randomly splits it and pads with zero/one bytes to force support the standard
@@ -853,13 +879,15 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
if (*defrag_len<10) return false;
uint8_t *defrag_data = defrag+10;
size_t defrag_data_len = *defrag_len-10;
uint8_t ft;
uint64_t offset,sz,szmax=0,zeropos=0,pos=0;
bool found=false;
struct range64 ranges[MAX_DEFRAG_PIECES];
int i,range=0;
while(pos<clean_len)
{
// frame type
ft = clean[pos];
pos++;
if (ft>1) // 00 - padding, 01 - ping
@@ -867,6 +895,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
if (ft!=6) return false; // dont want to know all possible frame type formats
if (pos>=clean_len) return false;
if (range>=MAX_DEFRAG_PIECES) return false;
if ((pos+tvb_get_size(clean[pos])>=clean_len)) return false;
pos += tvb_get_varint(clean+pos, &offset);
@@ -875,7 +904,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
pos += tvb_get_varint(clean+pos, &sz);
if ((pos+sz)>clean_len) return false;
if ((offset+sz)>defrag_data_len) return false;
if ((offset+sz)>defrag_data_len) return false; // defrag buf overflow
if (zeropos < offset)
// make sure no uninitialized gaps exist in case of not full fragment coverage
memset(defrag_data+zeropos,0,offset-zeropos);
@@ -886,6 +915,10 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
found=true;
pos+=sz;
ranges[range].offset = offset;
ranges[range].len = sz;
range++;
}
}
if (found)
@@ -897,6 +930,23 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
phton64(defrag+2,szmax);
defrag[2] |= 0xC0; // 64 bit value
*defrag_len = (size_t)(szmax+10);
qsort(ranges, range, sizeof(*ranges), cmp_range64);
//for(i=0 ; i<range ; i++)
// printf("RANGE %zu len %zu\n",ranges[i].offset,ranges[i].len);
for(i=0,offset=0,*bFull=true ; i<range ; i++)
{
if (ranges[i].offset!=offset)
{
*bFull = false;
break;
}
offset += ranges[i].len;
}
//printf("bFull=%u\n",*bFull);
}
return found;
}
@@ -973,3 +1023,19 @@ bool IsDhtD1(const uint8_t *data, size_t len)
{
return len>=7 && data[0]=='d' && data[1]=='1' && data[len-1]=='e';
}
bool IsDiscordIpDiscoveryRequest(const uint8_t *data, size_t len)
{
return len==74 &&
data[0]==0 && data[1]==1 &&
data[2]==0 && data[3]==70 &&
!memcmp(data+8,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",64);
// address is not set in request
}
bool IsStunMessage(const uint8_t *data, size_t len)
{
return len>=20 && // header size
(data[0]&0xC0)==0 && // 2 most significant bits must be zeroes
(data[3]&0b11)==0 && // length must be a multiple of 4
ntohl(*(uint32_t*)(&data[4]))==0x2112A442 && // magic cookie
ntohs(*(uint16_t*)(&data[2]))==len-20;
}

View File

@@ -7,12 +7,14 @@
#include "crypto/aes-gcm.h"
#include "helpers.h"
typedef enum {UNKNOWN=0, HTTP, TLS, QUIC, WIREGUARD, DHT} t_l7proto;
typedef enum {UNKNOWN=0, HTTP, TLS, QUIC, WIREGUARD, DHT, DISCORD, STUN} t_l7proto;
#define L7_PROTO_HTTP 0x00000001
#define L7_PROTO_TLS 0x00000002
#define L7_PROTO_QUIC 0x00000004
#define L7_PROTO_WIREGUARD 0x00000008
#define L7_PROTO_DHT 0x00000010
#define L7_PROTO_DISCORD 0x00000020
#define L7_PROTO_STUN 0x00000040
#define L7_PROTO_UNKNOWN 0x80000000
const char *l7proto_str(t_l7proto l7);
bool l7_proto_match(t_l7proto l7proto, uint32_t filter_l7);
@@ -55,6 +57,7 @@ int HttpReplyCode(const uint8_t *data, size_t len);
// must be pre-checked by IsHttpReply
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
const char *TLSVersionStr(uint16_t tlsver);
uint16_t TLSRecordDataLen(const uint8_t *data);
size_t TLSRecordLen(const uint8_t *data);
bool IsTLSRecordFull(const uint8_t *data, size_t len);
@@ -72,6 +75,8 @@ bool TLSHelloExtractHostFromHandshake(const uint8_t *data, size_t len, char *hos
bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len);
bool IsDhtD1(const uint8_t *data, size_t len);
bool IsDiscordIpDiscoveryRequest(const uint8_t *data, size_t len);
bool IsStunMessage(const uint8_t *data, size_t len);
#define QUIC_MAX_CID_LENGTH 20
typedef struct quic_cid {
@@ -87,5 +92,6 @@ uint8_t QUICDraftVersion(uint32_t version);
bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid);
bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len);
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len);
// returns true if crypto frames were found . bFull = true if crypto frame fragments have full coverage
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull);
//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);

View File

@@ -192,20 +192,21 @@ static bool set_seccomp(void)
bool sec_harden(void)
{
bool bRes = true;
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
{
DLOG_PERROR("PR_SET_NO_NEW_PRIVS(prctl)");
return false;
bRes = false;
}
#if ARCH_NR!=0
if (!set_seccomp())
{
DLOG_PERROR("seccomp");
if (errno==EINVAL) DLOG_ERR("seccomp: this can be safely ignored if kernel does not support seccomp\n");
return false;
bRes = false;
}
#endif
return true;
return bRes;
}
@@ -287,15 +288,20 @@ bool can_drop_root(void)
{
#ifdef __linux__
// has some caps
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID)|(1<<CAP_SETPCAP));
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID));
#else
// effective root
return !geteuid();
#endif
}
bool droproot(uid_t uid, gid_t gid)
bool droproot(uid_t uid, gid_t *gid, int gid_count)
{
if (gid_count<1)
{
DLOG_ERR("droproot: no groups specified");
return false;
}
#ifdef __linux__
if (prctl(PR_SET_KEEPCAPS, 1L))
{
@@ -304,12 +310,12 @@ bool droproot(uid_t uid, gid_t gid)
}
#endif
// drop all SGIDs
if (setgroups(0,NULL))
if (setgroups(gid_count,gid))
{
DLOG_PERROR("setgroups");
return false;
}
if (setgid(gid))
if (setgid(gid[0]))
{
DLOG_PERROR("setgid");
return false;
@@ -319,11 +325,7 @@ bool droproot(uid_t uid, gid_t gid)
DLOG_PERROR("setuid");
return false;
}
#ifdef __linux__
return dropcaps();
#else
return true;
#endif
}
void print_id(void)
@@ -346,9 +348,13 @@ void print_id(void)
#endif
void daemonize(void)
{
int pid;
#ifdef __CYGWIN__
char *cwd = get_current_dir_name();
#endif
pid = fork();
if (pid == -1)
@@ -359,6 +365,10 @@ void daemonize(void)
else if (pid != 0)
exit(0);
#ifdef __CYGWIN__
chdir(get_current_dir_name());
#endif
if (setsid() == -1)
exit(2);
if (chdir("/") == -1)

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);
bool droproot(uid_t uid, gid_t *gid, int gid_count);
void print_id(void);
#endif

View File

@@ -1,7 +1,9 @@
CC ?= gcc
CFLAGS += -std=gnu99 -Os -flto=auto
CFLAGS_SYSTEMD = -DUSE_SYSTEMD
CFLAGS_BSD = -Wno-address-of-packed-member
LIBS = -lz -lpthread
LIBS_SYSTEMD = -lsystemd
LIBS_ANDROID = -lz
SRC_FILES = *.c
SRC_FILES_ANDROID = $(SRC_FILES) andr/*.c
@@ -11,6 +13,9 @@ all: tpws
tpws: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o tpws $(SRC_FILES) $(LIBS) $(LDFLAGS)
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)

View File

@@ -11,6 +11,7 @@
#include <sys/stat.h>
#include <libgen.h>
#include <unistd.h>
#include <fcntl.h>
#ifdef __ANDROID__
#include "andr/ifaddrs.h"
@@ -77,6 +78,13 @@ char *strncasestr(const char *s, const char *find, size_t slen)
return (char *)s;
}
bool str_ends_with(const char *s, const char *suffix)
{
size_t slen = strlen(s);
size_t suffix_len = strlen(suffix);
return suffix_len <= slen && !strcmp(s + slen - suffix_len, suffix);
}
bool load_file(const char *filename, void *buffer, size_t *buffer_size)
{
FILE *F;
@@ -327,6 +335,17 @@ bool file_mod_signature(const char *filename, file_mod_sig *ms)
return true;
}
bool file_open_test(const char *filename, int flags)
{
int fd = open(filename,flags);
if (fd>=0)
{
close(fd);
return true;
}
return false;
}
bool pf_in_range(uint16_t port, const port_filter *pf)
{
return port && (((!pf->from && !pf->to) || (port >= pf->from && port <= pf->to)) ^ pf->neg);
@@ -371,6 +390,11 @@ bool pf_is_empty(const port_filter *pf)
return !pf->neg && !pf->from && !pf->to;
}
void set_console_io_buffering(void)
{
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(stderr, NULL, _IOLBF, 0);
}
bool set_env_exedir(const char *argv0)
{
@@ -548,4 +572,20 @@ bool socket_wait_notsent(int sfd, unsigned int delay_ms, unsigned int *wasted_ms
}
return false;
}
int is_wsl(void)
{
struct utsname buf;
if (uname(&buf) != 0)
return -1;
if (strcmp(buf.sysname, "Linux") != 0)
return 0;
if (str_ends_with(buf.release, "microsoft-standard-WSL2"))
return 2;
if (str_ends_with(buf.release, "-Microsoft"))
return 1;
return 0;
}
#endif

View File

@@ -7,10 +7,12 @@
#include <netdb.h>
#include <stdio.h>
#include <time.h>
#include <sys/utsname.h>
// this saves memory. sockaddr_storage is larger than required. it can be 128 bytes. sockaddr_in6 is 28 bytes.
typedef union
{
sa_family_t sa_family;
struct sockaddr_in sa4; // size 16
struct sockaddr_in6 sa6; // size 28
} sockaddr_in46;
@@ -22,6 +24,8 @@ void rtrim(char *s);
void replace_char(char *s, char from, char to);
char *strncasestr(const char *s,const char *find, size_t slen);
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);
@@ -71,6 +75,7 @@ typedef struct
#define FILE_MOD_RESET(ms) memset(ms,0,sizeof(file_mod_sig))
bool file_mod_signature(const char *filename, file_mod_sig *ms);
time_t file_mod_time(const char *filename);
bool file_open_test(const char *filename, int flags);
typedef struct
{
@@ -81,6 +86,7 @@ bool pf_in_range(uint16_t port, const port_filter *pf);
bool pf_parse(const char *s, port_filter *pf);
bool pf_is_empty(const port_filter *pf);
void set_console_io_buffering(void);
bool set_env_exedir(const char *argv0);
#ifndef IN_LOOPBACK
@@ -131,4 +137,6 @@ void msleep(unsigned int ms);
bool socket_supports_notsent();
bool socket_has_notsent(int sfd);
bool socket_wait_notsent(int sfd, unsigned int delay_ms, unsigned int *wasted_ms);
int is_wsl();
#endif

View File

@@ -4,7 +4,7 @@
#include "helpers.h"
// inplace tolower() and add to pool
static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
static bool addpool(hostlist_pool **hostlist, char **s, const char *end, int *ct)
{
char *p=*s;
@@ -17,10 +17,16 @@ static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
else
{
// advance until eol lowering all chars
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!StrPoolAddStrLen(hostlist, *s, p-*s))
uint32_t flags = 0;
if (*p=='^')
{
StrPoolDestroy(hostlist);
p = ++(*s);
flags |= HOSTLIST_POOL_FLAG_STRICT_MATCH;
}
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!HostlistPoolAddStrLen(hostlist, *s, p-*s, flags))
{
HostlistPoolDestroy(hostlist);
*hostlist = NULL;
return false;
}
@@ -32,12 +38,12 @@ static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
return true;
}
bool AppendHostlistItem(strpool **hostlist, char *s)
bool AppendHostlistItem(hostlist_pool **hostlist, char *s)
{
return addpool(hostlist,&s,s+strlen(s),NULL);
}
bool AppendHostList(strpool **hostlist, const char *filename)
bool AppendHostList(hostlist_pool **hostlist, const char *filename)
{
char *p, *e, s[256], *zbuf;
size_t zsize;
@@ -109,14 +115,15 @@ static bool LoadHostList(struct hostlist_file *hfile)
if (!file_mod_signature(hfile->filename, &fsig))
{
// stat() error
DLOG_PERROR("file_mod_signature");
DLOG_ERR("cannot access hostlist file '%s'. in-memory content remains unchanged.\n",hfile->filename);
return true;
}
if (FILE_MOD_COMPARE(&hfile->mod_sig,&fsig)) return true; // up to date
StrPoolDestroy(&hfile->hostlist);
HostlistPoolDestroy(&hfile->hostlist);
if (!AppendHostList(&hfile->hostlist, hfile->filename))
{
StrPoolDestroy(&hfile->hostlist);
HostlistPoolDestroy(&hfile->hostlist);
return false;
}
hfile->mod_sig=fsig;
@@ -137,10 +144,10 @@ static bool LoadHostLists(struct hostlist_files_head *list)
return bres;
}
bool NonEmptyHostlist(strpool **hostlist)
bool NonEmptyHostlist(hostlist_pool **hostlist)
{
// add impossible hostname if the list is empty
return *hostlist ? true : StrPoolAddStrLen(hostlist, "@&()", 4);
return *hostlist ? true : HostlistPoolAddStrLen(hostlist, "@&()", 4, 0);
}
static void MakeAutolistsNonEmpty()
@@ -163,19 +170,34 @@ bool LoadAllHostLists()
static bool SearchHostList(strpool *hostlist, const char *host)
static bool SearchHostList(hostlist_pool *hostlist, const char *host)
{
if (hostlist)
{
const char *p = host;
bool bInHostList;
const struct hostlist_pool *hp;
bool bHostFull=true;
while (p)
{
bInHostList = StrPoolCheckStr(hostlist, p);
VPRINT("hostlist check for %s : %s\n", p, bInHostList ? "positive" : "negative");
if (bInHostList) return true;
VPRINT("hostlist check for %s : ", p);
hp = HostlistPoolGetStr(hostlist, p);
if (hp)
{
if ((hp->flags & HOSTLIST_POOL_FLAG_STRICT_MATCH) && !bHostFull)
{
VPRINT("negative : strict_mismatch : %s != %s\n", p, host);
}
else
{
VPRINT("positive\n");
return true;
}
}
else
VPRINT("negative\n");
p = strchr(p, '.');
if (p) p++;
bHostFull = false;
}
}
return false;
@@ -265,11 +287,13 @@ static struct hostlist_file *RegisterHostlist_(struct hostlist_files_head *hostl
}
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename)
{
/*
if (filename && !file_mod_time(filename))
{
DLOG_ERR("cannot access hostlist file '%s'\n",filename);
return NULL;
}
*/
return RegisterHostlist_(
&params.hostlists,
bExclude ? &dp->hl_collection_exclude : &dp->hl_collection,

View File

@@ -4,10 +4,10 @@
#include "pools.h"
#include "params.h"
bool AppendHostlistItem(strpool **hostlist, char *s);
bool AppendHostList(strpool **hostlist, const char *filename);
bool AppendHostlistItem(hostlist_pool **hostlist, char *s);
bool AppendHostList(hostlist_pool **hostlist, const char *filename);
bool LoadAllHostLists();
bool NonEmptyHostlist(strpool **hostlist);
bool NonEmptyHostlist(hostlist_pool **hostlist);
// return : true = apply fooling, false = do not apply
bool HostlistCheck(const struct desync_profile *dp,const char *host, bool *excluded, bool bSkipReloadCheck);
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename);

View File

@@ -130,6 +130,7 @@ static bool LoadIpset(struct ipset_file *hfile)
if (!file_mod_signature(hfile->filename, &fsig))
{
// stat() error
DLOG_PERROR("file_mod_signature");
DLOG_ERR("cannot access ipset file '%s'. in-memory content remains unchanged.\n",hfile->filename);
return true;
}

View File

@@ -50,6 +50,7 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, int lev
{
va_copy(args2,args);
DLOG_CON(format,syslog_priority,args2);
va_end(args2);
}
if (params.debug>=level)
{

View File

@@ -4,6 +4,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <time.h>
#if !defined( __OpenBSD__) && !defined(__ANDROID__)
@@ -18,11 +19,15 @@
#define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 3
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
#define FIX_SEG_DEFAULT_MAX_WAIT 50
#define FIX_SEG_DEFAULT_MAX_WAIT 50
#define IPCACHE_LIFETIME 7200
#define MAX_GIDS 64
enum bindll { unwanted=0, no, prefer, force };
#define MAX_BINDS 32
#define MAX_BINDS 32
struct bind_s
{
char bindaddr[64],bindiface[IF_NAMESIZE];
@@ -31,7 +36,7 @@ struct bind_s
int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll;
};
#define MAX_SPLITS 16
#define MAX_SPLITS 16
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
@@ -114,8 +119,9 @@ struct params_s
bool droproot;
bool daemon;
uid_t uid;
gid_t gid;
char pidfile[256];
gid_t gid[MAX_GIDS];
int gid_count;
char pidfile[PATH_MAX];
int maxconn,resolver_threads,maxfiles,max_orphan_time;
int local_rcvbuf,local_sndbuf,remote_rcvbuf,remote_sndbuf;
#if defined(__linux__) || defined(__APPLE__)
@@ -140,6 +146,10 @@ struct params_s
bool tamper; // any tamper option is set
bool tamper_lim; // tamper-start or tamper-cutoff set in any profile
struct desync_profile_list_head desync_profiles;
unsigned int ipcache_lifetime;
bool cache_hostname;
ip_cache ipcache;
};
extern struct params_s params;

View File

@@ -3,6 +3,7 @@
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#define DESTROY_STR_POOL(etype, ppool) \
etype *elem, *tmp; \
@@ -31,6 +32,9 @@
free(elem); \
return false; \
}
#define ADD_HOSTLIST_POOL(etype, ppool, keystr, keystr_len, flg) \
ADD_STR_POOL(etype,ppool,keystr,keystr_len); \
elem->flags = flg;
#undef uthash_nonfatal_oom
@@ -42,27 +46,31 @@ static void ut_oom_recover(void *elem)
}
// for not zero terminated strings
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
bool HostlistPoolAddStrLen(hostlist_pool **pp, const char *s, size_t slen, uint32_t flags)
{
ADD_STR_POOL(strpool, pp, s, slen)
ADD_HOSTLIST_POOL(hostlist_pool, pp, s, slen, flags)
return true;
}
// for zero terminated strings
bool StrPoolAddStr(strpool **pp, const char *s)
bool HostlistPoolAddStr(hostlist_pool **pp, const char *s, uint32_t flags)
{
return StrPoolAddStrLen(pp, s, strlen(s));
return HostlistPoolAddStrLen(pp, s, strlen(s), flags);
}
bool StrPoolCheckStr(strpool *p, const char *s)
hostlist_pool *HostlistPoolGetStr(hostlist_pool *p, const char *s)
{
strpool *elem;
hostlist_pool *elem;
HASH_FIND_STR(p, s, elem);
return elem != NULL;
return elem;
}
bool HostlistPoolCheckStr(hostlist_pool *p, const char *s)
{
return !!HostlistPoolGetStr(p,s);
}
void StrPoolDestroy(strpool **pp)
void HostlistPoolDestroy(hostlist_pool **pp)
{
DESTROY_STR_POOL(strpool, pp)
DESTROY_STR_POOL(hostlist_pool, pp)
}
@@ -178,7 +186,7 @@ struct hostlist_file *hostlist_files_add(struct hostlist_files_head *head, const
static void hostlist_files_entry_destroy(struct hostlist_file *entry)
{
free(entry->filename);
StrPoolDestroy(&entry->hostlist);
HostlistPoolDestroy(&entry->hostlist);
free(entry);
}
void hostlist_files_destroy(struct hostlist_files_head *head)
@@ -510,3 +518,277 @@ bool port_filters_deny_if_empty(struct port_filters_head *head)
if (LIST_FIRST(head)) return true;
return pf_parse("0",&pf) && port_filter_add(head,&pf);
}
struct blob_item *blob_collection_add(struct blob_collection_head *head)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
if (entry)
{
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
{
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
LIST_INSERT_AFTER(iteml, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
}
return entry;
}
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
if (!entry) return NULL;
if (!(entry->data = malloc(size+size_reserve)))
{
free(entry);
return NULL;
}
if (data) memcpy(entry->data,data,size);
entry->size = size;
entry->size_buf = size+size_reserve;
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
{
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
LIST_INSERT_AFTER(iteml, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
return entry;
}
void blob_collection_destroy(struct blob_collection_head *head)
{
struct blob_item *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
free(entry->extra);
free(entry->extra2);
free(entry->data);
free(entry);
}
}
bool blob_collection_empty(const struct blob_collection_head *head)
{
return !LIST_FIRST(head);
}
static void ipcache_item_touch(ip_cache_item *item)
{
time(&item->last);
}
static void ipcache_item_init(ip_cache_item *item)
{
ipcache_item_touch(item);
item->hostname = NULL;
}
static void ipcache_item_destroy(ip_cache_item *item)
{
free(item->hostname);
}
static void ipcache4Destroy(ip_cache4 **ipcache)
{
ip_cache4 *elem, *tmp;
HASH_ITER(hh, *ipcache, elem, tmp)
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
static void ipcache4Key(ip4if *key, const struct in_addr *a)
{
memset(key,0,sizeof(*key)); // make sure everything is zero
key->addr = *a;
}
static ip_cache4 *ipcache4Find(ip_cache4 *ipcache, const struct in_addr *a)
{
ip_cache4 *entry;
struct ip4if key;
ipcache4Key(&key,a);
HASH_FIND(hh, ipcache, &key, sizeof(key), entry);
return entry;
}
static ip_cache4 *ipcache4Add(ip_cache4 **ipcache, const struct in_addr *a)
{
// avoid dups
ip_cache4 *entry = ipcache4Find(*ipcache,a);
if (entry) return entry; // already included
entry = malloc(sizeof(ip_cache4));
if (!entry) return NULL;
ipcache4Key(&entry->key,a);
oom = false;
HASH_ADD(hh, *ipcache, key, sizeof(entry->key), entry);
if (oom) { free(entry); return NULL; }
ipcache_item_init(&entry->data);
return entry;
}
static void ipcache4Print(ip_cache4 *ipcache)
{
char s_ip[16];
time_t now;
ip_cache4 *ipc, *tmp;
time(&now);
HASH_ITER(hh, ipcache , ipc, tmp)
{
*s_ip=0;
inet_ntop(AF_INET, &ipc->key.addr, s_ip, sizeof(s_ip));
printf("%s : hostname=%s now=last+%llu\n", s_ip, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last));
}
}
static void ipcache6Destroy(ip_cache6 **ipcache)
{
ip_cache6 *elem, *tmp;
HASH_ITER(hh, *ipcache, elem, tmp)
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
static void ipcache6Key(ip6if *key, const struct in6_addr *a)
{
memset(key,0,sizeof(*key)); // make sure everything is zero
key->addr = *a;
}
static ip_cache6 *ipcache6Find(ip_cache6 *ipcache, const struct in6_addr *a)
{
ip_cache6 *entry;
ip6if key;
ipcache6Key(&key,a);
HASH_FIND(hh, ipcache, &key, sizeof(key), entry);
return entry;
}
static ip_cache6 *ipcache6Add(ip_cache6 **ipcache, const struct in6_addr *a)
{
// avoid dups
ip_cache6 *entry = ipcache6Find(*ipcache,a);
if (entry) return entry; // already included
entry = malloc(sizeof(ip_cache6));
if (!entry) return NULL;
ipcache6Key(&entry->key,a);
oom = false;
HASH_ADD(hh, *ipcache, key, sizeof(entry->key), entry);
if (oom) { free(entry); return NULL; }
ipcache_item_init(&entry->data);
return entry;
}
static void ipcache6Print(ip_cache6 *ipcache)
{
char s_ip[40];
time_t now;
ip_cache6 *ipc, *tmp;
time(&now);
HASH_ITER(hh, ipcache , ipc, tmp)
{
*s_ip=0;
inet_ntop(AF_INET6, &ipc->key.addr, s_ip, sizeof(s_ip));
printf("%s : hostname=%s now=last+%llu\n", s_ip, ipc->data.hostname ? ipc->data.hostname : "", (unsigned long long)(now-ipc->data.last));
}
}
void ipcacheDestroy(ip_cache *ipcache)
{
ipcache4Destroy(&ipcache->ipcache4);
ipcache6Destroy(&ipcache->ipcache6);
}
void ipcachePrint(ip_cache *ipcache)
{
ipcache4Print(ipcache->ipcache4);
ipcache6Print(ipcache->ipcache6);
}
ip_cache_item *ipcacheTouch(ip_cache *ipcache, const struct in_addr *a4, const struct in6_addr *a6)
{
ip_cache4 *ipcache4;
ip_cache6 *ipcache6;
if (a4)
{
if ((ipcache4 = ipcache4Add(&ipcache->ipcache4,a4)))
{
ipcache_item_touch(&ipcache4->data);
return &ipcache4->data;
}
}
else if (a6)
{
if ((ipcache6 = ipcache6Add(&ipcache->ipcache6,a6)))
{
ipcache_item_touch(&ipcache6->data);
return &ipcache6->data;
}
}
return NULL;
}
static void ipcache4_purge(ip_cache4 **ipcache, time_t lifetime)
{
ip_cache4 *elem, *tmp;
time_t now = time(NULL);
HASH_ITER(hh, *ipcache, elem, tmp)
{
if (now >= (elem->data.last + lifetime))
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
}
static void ipcache6_purge(ip_cache6 **ipcache, time_t lifetime)
{
ip_cache6 *elem, *tmp;
time_t now = time(NULL);
HASH_ITER(hh, *ipcache, elem, tmp)
{
if (now >= (elem->data.last + lifetime))
{
HASH_DEL(*ipcache, elem);
ipcache_item_destroy(&elem->data);
free(elem);
}
}
}
static void ipcache_purge(ip_cache *ipcache, time_t lifetime)
{
if (lifetime) // 0 = no expire
{
ipcache4_purge(&ipcache->ipcache4, lifetime);
ipcache6_purge(&ipcache->ipcache6, lifetime);
}
}
static time_t ipcache_purge_prev=0;
void ipcachePurgeRateLimited(ip_cache *ipcache, time_t lifetime)
{
time_t now = time(NULL);
// do not purge too often to save resources
if (ipcache_purge_prev != now)
{
ipcache_purge(ipcache, lifetime);
ipcache_purge_prev = now;
}
}

View File

@@ -3,6 +3,7 @@
#include <stdbool.h>
#include <ctype.h>
#include <sys/queue.h>
#include <net/if.h>
#include <time.h>
#include "helpers.h"
@@ -12,15 +13,18 @@
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
typedef struct strpool {
char *str; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} strpool;
#define HOSTLIST_POOL_FLAG_STRICT_MATCH 1
void StrPoolDestroy(strpool **pp);
bool StrPoolAddStr(strpool **pp,const char *s);
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
bool StrPoolCheckStr(strpool *p,const char *s);
typedef struct hostlist_pool {
char *str; /* key */
uint32_t flags; /* custom data */
UT_hash_handle hh; /* makes this structure hashable */
} hostlist_pool;
void HostlistPoolDestroy(hostlist_pool **pp);
bool HostlistPoolAddStr(hostlist_pool **pp, const char *s, uint32_t flags);
bool HostlistPoolAddStrLen(hostlist_pool **pp, const char *s, size_t slen, uint32_t flags);
hostlist_pool *HostlistPoolGetStr(hostlist_pool *p, const char *s);
struct str_list {
char *str;
@@ -29,10 +33,10 @@ struct str_list {
LIST_HEAD(str_list_head, str_list);
typedef struct hostfail_pool {
char *str; /* key */
int counter; /* value */
time_t expire; /* when to expire record (unixtime) */
UT_hash_handle hh; /* makes this structure hashable */
char *str; /* key */
int counter; /* value */
time_t expire; /* when to expire record (unixtime) */
UT_hash_handle hh; /* makes this structure hashable */
} hostfail_pool;
void HostFailPoolDestroy(hostfail_pool **pp);
@@ -51,7 +55,7 @@ void strlist_destroy(struct str_list_head *head);
struct hostlist_file {
char *filename;
file_mod_sig mod_sig;
strpool *hostlist;
hostlist_pool *hostlist;
LIST_ENTRY(hostlist_file) next;
};
LIST_HEAD(hostlist_files_head, hostlist_file);
@@ -143,3 +147,55 @@ bool port_filter_add(struct port_filters_head *head, const port_filter *pf);
void port_filters_destroy(struct port_filters_head *head);
bool port_filters_in_range(const struct port_filters_head *head, uint16_t port);
bool port_filters_deny_if_empty(struct port_filters_head *head);
struct blob_item {
uint8_t *data; // main data blob
size_t size; // main data blob size
size_t size_buf;// main data blob allocated size
void *extra; // any data without size
void *extra2; // any data without size
LIST_ENTRY(blob_item) next;
};
LIST_HEAD(blob_collection_head, blob_item);
struct blob_item *blob_collection_add(struct blob_collection_head *head);
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve);
void blob_collection_destroy(struct blob_collection_head *head);
bool blob_collection_empty(const struct blob_collection_head *head);
typedef struct ip4if
{
struct in_addr addr;
} ip4if;
typedef struct ip6if
{
struct in6_addr addr;
} ip6if;
typedef struct ip_cache_item
{
time_t last;
char *hostname;
} ip_cache_item;
typedef struct ip_cache4
{
ip4if key;
ip_cache_item data;
UT_hash_handle hh; /* makes this structure hashable */
} ip_cache4;
typedef struct ip_cache6
{
ip6if key;
ip_cache_item data;
UT_hash_handle hh; /* makes this structure hashable */
} ip_cache6;
typedef struct ip_cache
{
ip_cache4 *ipcache4;
ip_cache6 *ipcache6;
} ip_cache;
ip_cache_item *ipcacheTouch(ip_cache *ipcache, const struct in_addr *a4, const struct in6_addr *a6);
void ipcachePurgeRateLimited(ip_cache *ipcache, time_t lifetime);
void ipcacheDestroy(ip_cache *ipcache);
void ipcachePrint(ip_cache *ipcache);

View File

@@ -339,6 +339,20 @@ size_t HttpPos(uint8_t posmarker, int16_t pos, const uint8_t *data, size_t sz)
const char *TLSVersionStr(uint16_t tlsver)
{
switch(tlsver)
{
case 0x0301: return "TLS 1.0";
case 0x0302: return "TLS 1.1";
case 0x0303: return "TLS 1.2";
case 0x0304: return "TLS 1.3";
default:
// 0x0a0a, 0x1a1a, ..., 0xfafa
return (((tlsver & 0x0F0F) == 0x0A0A) && ((tlsver>>12)==((tlsver>>4)&0xF))) ? "GREASE" : "UNKNOWN";
}
}
uint16_t TLSRecordDataLen(const uint8_t *data)
{
return pntoh16(data + 3);

View File

@@ -53,6 +53,7 @@ int HttpReplyCode(const uint8_t *data, size_t len);
// must be pre-checked by IsHttpReply
bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *host);
const char *TLSVersionStr(uint16_t tlsver);
uint16_t TLSRecordDataLen(const uint8_t *data);
size_t TLSRecordLen(const uint8_t *data);
bool IsTLSRecordFull(const uint8_t *data, size_t len);

View File

@@ -169,25 +169,24 @@ static bool set_seccomp(void)
bool sec_harden(void)
{
bool bRes = true;
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0))
{
DLOG_PERROR("PR_SET_NO_NEW_PRIVS(prctl)");
return false;
bRes = false;
}
#if ARCH_NR!=0
if (!set_seccomp())
{
DLOG_PERROR("seccomp");
if (errno==EINVAL) DLOG_ERR("seccomp: this can be safely ignored if kernel does not support seccomp\n");
return false;
bRes = false;
}
#endif
return true;
return bRes;
}
bool checkpcap(uint64_t caps)
{
if (!caps) return true; // no special caps reqd
@@ -263,15 +262,20 @@ bool can_drop_root(void)
{
#ifdef __linux__
// has some caps
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID)|(1<<CAP_SETPCAP));
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID));
#else
// effective root
return !geteuid();
#endif
}
bool droproot(uid_t uid, gid_t gid)
bool droproot(uid_t uid, gid_t *gid, int gid_count)
{
if (gid_count<1)
{
DLOG_ERR("droproot: no groups specified");
return false;
}
#ifdef __linux__
if (prctl(PR_SET_KEEPCAPS, 1L))
{
@@ -280,12 +284,12 @@ bool droproot(uid_t uid, gid_t gid)
}
#endif
// drop all SGIDs
if (setgroups(0,NULL))
if (setgroups(gid_count,gid))
{
DLOG_PERROR("setgroups");
return false;
}
if (setgid(gid))
if (setgid(gid[0]))
{
DLOG_PERROR("setgid");
return false;
@@ -295,11 +299,7 @@ bool droproot(uid_t uid, gid_t gid)
DLOG_PERROR("setuid");
return false;
}
#ifdef __linux__
return dropcaps();
#else
return true;
#endif
}
void print_id(void)

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);
bool droproot(uid_t uid, gid_t *gid, int gid_count);
void print_id(void);
void daemonize(void);
bool writepid(const char *filename);

View File

@@ -7,6 +7,7 @@
#include "ipset.h"
#include "protocol.h"
#include "helpers.h"
#include "pools.h"
#define PKTDATA_MAXDUMP 32
@@ -15,6 +16,126 @@ void packet_debug(const uint8_t *data, size_t sz)
hexdump_limited_dlog(data, sz, PKTDATA_MAXDUMP); VPRINT("\n");
}
static void TLSDebugHandshake(const uint8_t *tls,size_t sz)
{
if (!params.debug) return;
if (sz<6) return;
const uint8_t *ext;
size_t len,len2;
uint16_t v_handshake=pntoh16(tls+4), v, v2;
VPRINT("TLS handshake version : %s\n",TLSVersionStr(v_handshake));
if (TLSFindExtInHandshake(tls,sz,43,&ext,&len,false))
{
if (len)
{
len2 = ext[0];
if (len2<len)
{
for(ext++,len2&=~1 ; len2 ; len2-=2,ext+=2)
{
v = pntoh16(ext);
VPRINT("TLS supported versions ext : %s\n",TLSVersionStr(v));
}
}
}
}
else
VPRINT("TLS supported versions ext : not present\n");
if (TLSFindExtInHandshake(tls,sz,16,&ext,&len,false))
{
if (len>=2)
{
len2 = pntoh16(ext);
if (len2<=(len-2))
{
char s[32];
for(ext+=2; len2 ;)
{
v = *ext; ext++; len2--;
if (v<=len2)
{
v2 = v<sizeof(s) ? v : sizeof(s)-1;
memcpy(s,ext,v2);
s[v2]=0;
VPRINT("TLS ALPN ext : %s\n",s);
len2-=v;
ext+=v;
}
else
break;
}
}
}
}
else
VPRINT("TLS ALPN ext : not present\n");
VPRINT("TLS ECH ext : %s\n",TLSFindExtInHandshake(tls,sz,65037,NULL,NULL,false) ? "present" : "not present");
}
static void TLSDebug(const uint8_t *tls,size_t sz)
{
if (!params.debug) return;
if (sz<11) return;
VPRINT("TLS record layer version : %s\n",TLSVersionStr(pntoh16(tls+1)));
size_t reclen=TLSRecordLen(tls);
if (reclen<sz) sz=reclen; // correct len if it has more data than the first tls record has
TLSDebugHandshake(tls+5,sz-5);
}
bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname)
{
if (!params.cache_hostname) return true;
ip_cache_item *ipc = ipcacheTouch(&params.ipcache,a4,a6);
if (!ipc)
{
DLOG_ERR("ipcache_put_hostname: out of memory\n");
return false;
}
if (!ipc->hostname || strcmp(ipc->hostname,hostname))
{
free(ipc->hostname);
if (!(ipc->hostname = strdup(hostname)))
{
DLOG_ERR("ipcache_put_hostname: out of memory\n");
return false;
}
VPRINT("hostname cached: %s\n", hostname);
}
return true;
}
static bool ipcache_get_hostname(const struct in_addr *a4, const struct in6_addr *a6, char *hostname, size_t hostname_buf_len)
{
if (!params.cache_hostname)
{
*hostname = 0;
return true;
}
ip_cache_item *ipc = ipcacheTouch(&params.ipcache,a4,a6);
if (!ipc)
{
DLOG_ERR("ipcache_get_hostname: out of memory\n");
return false;
}
if (ipc->hostname)
{
VPRINT("got cached hostname: %s\n", ipc->hostname);
snprintf(hostname,hostname_buf_len,"%s",ipc->hostname);
}
else
*hostname = 0;
return true;
}
static bool dp_match(struct desync_profile *dp, const struct sockaddr *dest, const char *hostname, t_l7proto l7proto)
{
bool bHostlistsEmpty;
@@ -70,8 +191,17 @@ static struct desync_profile *dp_find(struct desync_profile_list_head *head, con
VPRINT("desync profile not found\n");
return NULL;
}
void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest)
{
ipcachePurgeRateLimited(&params.ipcache, params.ipcache_lifetime);
if (!ctrack->hostname)
{
char host[256];
if (ipcache_get_hostname(dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL , host, sizeof(host)) && *host)
if (!(ctrack->hostname=strdup(host)))
DLOG_ERR("hostname dup : out of memory");
}
ctrack->dp = dp_find(&params.desync_profiles, dest, ctrack->hostname, ctrack->l7proto);
}
@@ -130,6 +260,7 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
{
VPRINT("Data block contains TLS ClientHello\n");
l7proto=TLS;
TLSDebug(segment,*size);
bHaveHost=TLSHelloExtractHost((uint8_t*)segment,*size,Host,sizeof(Host),false);
}
else
@@ -139,7 +270,11 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
}
if (bHaveHost)
{
VPRINT("request hostname: %s\n", Host);
if (!ipcache_put_hostname(dest->sa_family==AF_INET ? &((struct sockaddr_in*)dest)->sin_addr : NULL, dest->sa_family==AF_INET6 ? &((struct sockaddr_in6*)dest)->sin6_addr : NULL , Host))
DLOG_ERR("ipcache_put_hostname: out of memory");
}
bool bDiscoveredL7 = ctrack->l7proto==UNKNOWN && l7proto!=UNKNOWN;
if (bDiscoveredL7)
@@ -148,15 +283,17 @@ void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,
ctrack->l7proto=l7proto;
}
bool bDiscoveredHostname = bHaveHost && !ctrack->hostname;
bool bDiscoveredHostname = bHaveHost && !ctrack->hostname_discovered;
if (bDiscoveredHostname)
{
VPRINT("discovered hostname\n");
free(ctrack->hostname);
if (!(ctrack->hostname=strdup(Host)))
{
DLOG_ERR("strdup hostname : out of memory\n");
return;
}
ctrack->hostname_discovered = true;
}
if (bDiscoveredL7 || bDiscoveredHostname)
@@ -433,7 +570,7 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
{
VPRINT("auto hostlist (profile %d) : adding %s to %s\n", dp->n, hostname, dp->hostlist_auto->filename);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : client %s : proto %s : adding to %s", hostname, dp->n, client_ip_port, l7proto_str(l7proto), dp->hostlist_auto->filename);
if (!StrPoolAddStr(&dp->hostlist_auto->hostlist, hostname))
if (!HostlistPoolAddStr(&dp->hostlist_auto->hostlist, hostname, 0))
{
DLOG_ERR("StrPoolAddStr out of memory\n");
return;

View File

@@ -15,11 +15,13 @@ typedef struct
t_l7proto l7proto;
bool bTamperInCutoff;
bool b_host_checked,b_host_matches,b_ah_check;
bool hostname_discovered;
char *hostname;
struct desync_profile *dp; // desync profile cache
} t_ctrack;
void apply_desync_profile(t_ctrack *ctrack, const struct sockaddr *dest);
bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname);
void tamper_out(t_ctrack *ctrack, const struct sockaddr *dest, uint8_t *segment,size_t segment_buffer_size,size_t *size, size_t *multisplit_pos, int *multisplit_count, uint8_t *split_flags);
void tamper_in(t_ctrack *ctrack, const struct sockaddr *client, uint8_t *segment,size_t segment_buffer_size,size_t *size);

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,10 @@
#include <fcntl.h>
#include <netdb.h>
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#include "tpws.h"
#include "tpws_conn.h"
#include "redirect.h"
@@ -25,6 +29,15 @@
#include "hostlist.h"
#include "linux_compat.h"
static void notify_ready(void)
{
#ifdef USE_SYSTEMD
int r = sd_notify(0, "READY=1");
if (r < 0)
DLOG_ERR("sd_notify: %s\n", strerror(-r));
#endif
}
// keep separate legs counter. counting every time thousands of legs can consume cpu
static int legs_local, legs_remote;
/*
@@ -481,6 +494,9 @@ static bool connect_remote_conn(tproxy_conn_t *conn)
{
int mss=0;
if (conn->track.hostname)
if (!ipcache_put_hostname(conn->dest.sa_family==AF_INET ? &((struct sockaddr_in*)&conn->dest)->sin_addr : NULL, conn->dest.sa_family==AF_INET6 ? &((struct sockaddr_in6*)&conn->dest)->sin6_addr : NULL , conn->track.hostname))
DLOG_ERR("ipcache_put_hostname: out of memory");
apply_desync_profile(&conn->track, (struct sockaddr *)&conn->dest);
if (conn->track.dp && conn->track.dp->mss)
@@ -1074,7 +1090,8 @@ static bool resolve_complete(struct resolve_item *ri, struct tailhead *conn_list
if (!conn->track.hostname)
{
DBGPRINT("resolve_complete put hostname : %s\n", ri->dom);
conn->track.hostname = strdup(ri->dom);
if (!(conn->track.hostname = strdup(ri->dom)))
DLOG_ERR("dup hostname: out of memory\n");
}
sa46copy(&conn->dest, (struct sockaddr *)&ri->ss);
return proxy_mode_connect_remote(conn,conn_list);
@@ -1542,6 +1559,8 @@ int event_loop(const int *listen_fd, size_t listen_fd_ct)
VPRINT("initialized multi threaded resolver with %d threads\n",resolver_thread_count());
}
notify_ready();
for(;;)
{
ReloadCheck();
@@ -1755,8 +1774,6 @@ int event_loop(const int *listen_fd, size_t listen_fd_ct)
// at least one leg was removed. recount legs
print_legs();
}
fflush(stderr); fflush(stdout); // for console messages
}
ex: