Compare commits

..
113 Commits
Author SHA1 Message Date
bol-van 8283f4cd76 update makefiles 2026-10-29 15:55:59 +03:00
bol-van 019bbe845f update docs 2026-10-28 10:03:42 +03:00
bol-van 9679d58499 cosmetic 2026-10-28 06:43:50 +03:00
bol-van ca7ed6a986 zapret-obfs: udp2icmp update id cache access time in both directions 2026-10-27 19:01:17 +03:00
bol-van c460884213 zapret-obfs: support cgnat in udp2icmp 2026-10-27 18:50:51 +03:00
bol-van 3570f193d5 update docs 2026-10-27 18:48:28 +03:00
bol-van 8be19e079d update docs 2026-10-27 11:23:37 +03:00
bol-van 4a33055448 nfqws2: fix UAF 2026-10-22 11:06:42 +03:00
bol-van 221d7dff24 update docs 2026-10-21 11:49:48 +03:00
bol-van f94b562482 blockcheck2: more sweeps 2026-10-21 11:49:22 +03:00
bol-van 6ec7ee67ce Merge pull request #315 from sasha-spb-ru/add-veryquick-scan-level
[blockcheck2] Add a separate very quick scan level
2026-10-21 09:08:59 +03:00
bol-van 98757082b8 blockcheck2: use guard timer in all OS 2026-10-20 18:40:30 +03:00
bol-van 5786448c93 update docs 2026-10-20 17:33:26 +03:00
bol-van a0028b9895 blockcheck2: optimize PACE 2026-10-20 17:10:25 +03:00
bol-van 486626d28f Merge pull request #329 from maxsupermanhd/blockcheck-pace
blockcheck2: request pacing
2026-10-20 17:05:53 +03:00
bol-van f8d08b8436 AI fixes 2026-10-20 15:28:52 +03:00
bol-van a4515beb32 update docs 2026-10-20 09:57:59 +03:00
bol-van 299f833f39 blockcheck2.sh: temp files sweeper fix 2026-10-20 09:56:13 +03:00
bol-van a0c928be45 Merge pull request #314 from sasha-spb-ru/clean-up-leftover-tmpfiles
[blockcheck2] Sweep leftover temp files from a killed/crashed run
2026-10-20 09:40:10 +03:00
bol-van f256fea2d8 fix inconsistances in docs and comments 2026-10-20 09:16:53 +03:00
bol-van 3a6f75cd3c update docs 2026-10-20 08:32:10 +03:00
maxsupermanhd 44fbf1b18e blockcheck2: request pacing 2026-10-19 01:49:03 +03:00
bol-van a1adf7b868 nfqws2: support rare partial root environment 2026-10-04 10:18:18 +03:00
bol-van ca950d838a zapret-lib: comments spelling 2026-09-29 08:18:27 +03:00
bol-van 873642573e update docs 2026-09-28 22:35:22 +03:00
Alexander Golovanov e41e4f18c7 [blockcheck2] Add a separate very quick scan level
quick is meant to be the fast option, but it only changes how repeated
attempts are handled - it still works through every variation of a
strategy regardless. For strategies built around a fake TLS handshake,
that means testing dozens of small tweaks (different TTLs, different
TCP tricks, extra padding, split positions) well past the point where
an early exit would have been possible.

The new veryquick level stops as soon as a strategy succeeds, instead
of continuing through the rest just to be thorough. It's a separate
option rather than a change to quick, so quick, standard, and force
behave exactly as they did.
2026-09-18 16:30:33 +03:00
Alexander Golovanov 03ce20156a [blockcheck2] Sweep leftover temp files from a killed/crashed run
The pad file, PARALLEL_OUT, and HDRTEMP are all already removed on
a normal exit - Ctrl+C, closing the terminal, a plain kill all clean
up after themselves. kill -9 and crashes skip that entirely, same as
for any script, and a later run has no way to know about a different
PID's leftovers - so all three can pile up in /tmp indefinitely.

Before each is used, the script now checks for existing files matching
its pattern and removes any whose PID is confirmed no longer running.
A file still owned by a live process - a second instance running
at the same time - is left alone.
2026-09-18 14:08:10 +03:00
bol-van 00f5aaa36d update docs 2026-09-18 11:30:00 +03:00
bol-van 1e329918dc update docs 2026-09-18 11:26:08 +03:00
bol-van 8a398236ac update docs 2026-09-18 11:25:13 +03:00
bol-van 074c981a37 update docs 2026-09-18 11:24:24 +03:00
bol-van 03c139f34a update docs 2026-09-18 11:21:19 +03:00
bol-van cdcb8d4711 update docs 2026-09-18 11:20:16 +03:00
bol-van 970adb5d55 update docs 2026-09-18 11:18:44 +03:00
bol-van 6535202a9a update docs 2026-09-18 11:15:43 +03:00
bol-van 9bff218b9a update docs 2026-09-18 11:14:28 +03:00
bol-van 3e3b902902 update docs 2026-09-18 11:12:24 +03:00
bol-van 0f65389571 blockcheck2: CURL_PAD_KB=>CURL_PAD. split padding into smaller headers 2026-09-18 10:54:36 +03:00
bol-van ec25da2277 blockcheck2: warn about eval failing strategies in custom list 2026-09-18 10:04:10 +03:00
bol-van 0d2227caf6 Merge pull request #313 from sasha-spb-ru/add-request-padding
[blockcheck2] Add a way to test the size-based block without a real download
2026-09-18 09:55:41 +03:00
bol-van eb42084b30 Merge pull request #311 from sasha-spb-ru/show-strategy-coverage
[blockcheck2] Add a coverage summary next to the results
2026-09-18 09:54:42 +03:00
bol-van 3b6f0cc345 Merge pull request #310 from sasha-spb-ru/fix-strategy-escaping
[blockcheck2] Stop requiring manual escaping in strategy lists
2026-09-18 09:51:38 +03:00
bol-van 28b6fb853e Merge pull request #297 from zaveshaa/fix-doubled-word
docs: fix doubled word in manual.en.md
2026-09-17 22:03:01 +03:00
bol-van bf3b30af9c Merge pull request #290 from 0-x-0-0/fix-static-analyzer-warnings
Fix -Wcast-qual and -Wlogical-op-parentheses warnings
2026-09-17 22:01:57 +03:00
Alexander Golovanov 34a95e8a41 [blockcheck2] Add a way to test the size-based block without a real download
The "16 KB" block triggers based on how much data passes through the
connection, not on what that data actually is. Testing for it usually
means pointing at a page you know is big enough to download - but that
only works if your target actually has one.

Adds CURL_PAD_KB: set it to a number and every request gets padded
with that many KB of junk data, so the block can be triggered against
any target, even a bare server with nothing to download. Off by
default, and doesn't change anything unless you turn it on.

The padding is written to a temp file and attached from there, not
built inline: a large inline value can hit low, easy-to-reach limits
on some platforms (Cygwin among them).
2026-09-17 12:57:31 +03:00
Alexander Golovanov bf64220ab8 [blockcheck2] Stop requiring manual escaping in strategy lists
Custom strategy lists are plain text, and each line gets run through
the shell - so a strategy containing certain characters (<, parentheses,
quotes) would fail unless you added quotes around it by hand. Some real,
valid strategies use exactly these characters.

Now, when a line fails for that reason, it's automatically retried in
a simpler way that doesn't care about those characters at all. You can
paste a strategy in exactly as blockcheck2 prints it and it'll just
work. Manual escaping still works too, and is tried first if you're
already doing it.
2026-09-17 11:31:30 +03:00
Alexander Golovanov adf7791367 [blockcheck2] Add a coverage summary next to the results
When testing several targets at once, the results only ever show
strategies that worked for every single one. If your targets are
a bunch of unrelated servers, that list can easily come up empty
even when several strategies each work for most of them.

Adds a second summary that lists every strategy that worked at all,
with how many targets it worked for, best first - so you can see
which one comes closest instead of getting an all-or-nothing answer.
2026-09-17 11:24:21 +03:00
bol-van 1ea7d10f6f update docs 2026-09-16 16:41:14 +03:00
bol-van 859c24d32b update docs 2026-09-16 16:22:20 +03:00
bol-van 5df635efdb nfqws2: HttpReplyLooksLikeDPIRedirect accept # and ? chars as domain end 2026-09-16 16:20:51 +03:00
bol-van 7119ffce54 zapret-lib: dissect_url accept # and ? chars as domain end 2026-09-16 16:14:48 +03:00
bol-van 2efdb70b1c nfqws2: make autolists nonempty even if loading failed 2026-09-15 21:51:18 +03:00
bol-van 692139aeea zapret-lib: dissect quic tvb values that can't fit into 52-bit mantissa into raw strings 2026-09-15 18:58:21 +03:00
bol-van 6b6c63e338 update docs 2026-09-15 15:21:18 +03:00
bol-van 5fab86344a nfqws2: do not make autohostlists nonempty if no lists were reloaded 2026-09-15 15:02:28 +03:00
bol-van c1e8e7c0d9 nfqws2: dp_match_data optimize struct members 2026-09-15 12:13:17 +03:00
bol-van d9fde580e6 nfqws2: dp_match_data optimize struct members 2026-09-15 12:12:34 +03:00
bol-van 5ebfa6c0dc nfqws2: move dp_match() args to struct 2026-09-15 12:09:54 +03:00
bol-van a1bca5a85e update docs 2026-09-07 21:18:32 +03:00
bol-van 3122b66190 nfqws2: optimize profile search. fast checks first 2026-09-07 21:03:48 +03:00
bol-van 0b8182d24a zapret-lib: fix length checking for keyshare extension in tls_dissect_ext 2026-09-02 10:49:58 +03:00
bol-van 8b50962e66 nfqws2: --filter-mark 2026-09-02 10:36:00 +03:00
bol-van a7ff6410d6 update docs 2026-09-02 09:35:10 +03:00
bol-van 6e186a3b38 nfqws2: pad tcp options with 0x00 in reconstruct. strip 0x00 ending options in dissect. 2026-09-02 09:18:27 +03:00
bol-van 36355d46e3 nfqws2: fix exit on HUP 2026-08-28 09:04:12 +03:00
bol-van 2f8948d0eb nfqws2:dont break on eol tcpopt in dissect 2026-08-25 08:07:53 +03:00
bol-van 68514ed871 blind patch fix 2026-08-24 21:43:31 +03:00
bol-van 204e95ad0e nfqws22: pad tcp opts with zero 2026-08-24 20:41:26 +03:00
zaveshaa 3c93f925be docs: fix doubled word in manual.en.md 2026-08-23 21:11:10 +03:00
bol-van 07c69bf132 blockcheck2: fix script crash on bad strategies from the file in custom/10-list.sh 2026-08-23 19:50:13 +03:00
bol-van 2080981cea zapret-lib: minor optimize 2026-08-23 15:58:54 +03:00
bol-van c4e4fff4a0 zapret-lib: minor optimize 2026-08-23 15:58:02 +03:00
bol-van 1c5d49699c AI and manual fixes 2026-08-23 12:07:59 +03:00
bol-van a0be7cbb40 update docs 2026-08-20 18:55:40 +03:00
bol-van e263849b1b update docs 2026-08-20 18:53:41 +03:00
bol-van 46010c4a42 nfqws2: negate ssid/nlm filters 2026-08-20 18:45:49 +03:00
bol-van b08ae78778 update docs 2026-08-19 10:06:17 +03:00
bol-van 49e2d438d8 github, builder_linux: lua 5.5.1 2026-08-19 10:03:56 +03:00
bol-van a8d24607a5 blockcheck2: --wf-dup-check not needed anymore 2026-08-15 16:54:32 +03:00
bol-van 5e84833269 blockcheck2: optimize 2026-08-15 15:42:52 +03:00
bol-van fbeb124cf8 blockcheck2: use --wf-raw-filter in Windows instead of --ipset to limit target IP addresses 2026-08-15 12:08:09 +03:00
0-x-0-0 3f87539d7c Fix compiler/static analyzer warnings 2026-08-08 14:54:40 +03:00
bol-van 032651deeb update docs 2026-08-02 07:01:51 +03:00
bol-van bdad7d7be1 update docs 2026-08-01 14:11:01 +03:00
bol-van 86bcc7db16 update docs 2026-08-01 09:58:52 +03:00
bol-van 569297c623 update docs 2026-07-31 21:54:19 +03:00
bol-van 2c21faa80e github: remove high_entropy_va patch for windivert.dll, but leave dynamic_base 2026-07-31 10:31:30 +03:00
bol-van 90097be91a Merge pull request #280 from goodrussian666/master
Fixed the double-free reports in [nfqws.c (line 1118)
2026-07-31 09:53:21 +03:00
bol-van c61706cc47 update docs 2026-07-31 09:48:37 +03:00
bol-van 5f2807e0c5 builder_linux: zlib_ng optional support 2026-07-31 09:48:12 +03:00
bol-van dd85b1f852 nfqws2: remove file_mod_time() 2026-07-31 09:03:33 +03:00
bol-van b93b84791d zapret-antidpi: remove debug output 2026-07-30 22:05:03 +03:00
bol-van 4668664ee7 update docs 2026-07-30 19:02:40 +03:00
bol-van 78bbd0ee51 update docs 2026-07-30 19:01:43 +03:00
bol-van e185d5ce60 nfqws2: check for left space during udp and icmp reconstruct 2026-07-30 17:36:04 +03:00
bol-van d55fe738eb zapret-lib: include func instance in desync_timer_name 2026-07-30 17:24:29 +03:00
bol-van a8399a59fe nfqws2: do not allow more than 2 args in conntrack_feed() 2026-07-30 17:18:52 +03:00
bol-van 37e5a8a880 nfqws2: fix hostlist and ipset load if modtime is unixtime 0 2026-07-30 17:16:16 +03:00
bol-van 3b465d9981 nfqws2: stop execution plan in C code if a blob is unavailable 2026-07-30 16:34:06 +03:00
Your Name 11bc8f4fd1 Fixed the double-free reports in [nfqws.c (line 1118)
blob_collection_add() now occurs only after name/file validation and duplicate-name checks. This prevents exit_clean() from traversing a partially registered blob while name is being freed locally.

nfq2/nfqws.c:1140:4: error: Memory pointed to by 'name' is freed twice. [doubleFree]
nfq2/nfqws.c:1148:5: error: Memory pointed to by 'name' is freed twice. [doubleFree]
nfq2/nfqws.c:1159:3: error: Memory pointed to by 'name' is freed twice. [doubleFree]
nfq2/nfqws.c:1164:3: error: Memory pointed to by 'name' is freed twice. [doubleFree]
2026-07-29 20:35:31 +02:00
bol-van 80bb86e833 blockcheck2: unify messages 2026-07-23 13:46:31 +03:00
bol-van d7e6e3fb60 blockcheck2: disable tls1.3 and quic tests in batch mode if curl does not support them 2026-07-23 13:45:17 +03:00
bol-van ea047535b9 blockcheck2: revert cygstart method as it brings more problems than profit 2026-07-23 12:50:57 +03:00
bol-van a3202599aa blockcheck2: optimize signal trap/untrap 2026-07-22 21:26:05 +03:00
bol-van b8f142253b blockcheck2: wait for cygwin pid in case of signal 2026-07-22 21:13:48 +03:00
bol-van ccb6e9b534 blockcheck2: wait up to 2 seconds for the winws2 pidfile to appear 2026-07-22 20:21:36 +03:00
bol-van 3d4828d61f blockcheck2: graceful shutdown on QUIT signal 2026-07-22 13:13:34 +03:00
bol-van 0020a8de6a blockcheck2: graceful shutdown on TERM signal 2026-07-22 13:10:27 +03:00
bol-van 6f63dbc3a8 blockcheck2: use kill -f in cygwin to speed up kill 2026-07-22 09:49:43 +03:00
bol-van bb042f734e update docs 2026-07-22 08:31:39 +03:00
bol-van e6b22f3868 blockcheck2: remove spaces in assignment 2026-07-21 20:09:41 +03:00
bol-van c73af75881 blockcheck2: use instead of in killwait 2026-07-21 20:01:27 +03:00
bol-van 889d120235 blockcheck2: fix non-working 'wait' after cygstart 2026-07-21 19:53:41 +03:00
41 changed files with 1113 additions and 494 deletions

No files matched your search

+2 -3
View File
@@ -82,7 +82,7 @@ jobs:
LDFLAGS: ${{ matrix.env.LDFLAGS != '' && matrix.env.LDFLAGS || null }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
LUA_VER: 5.5
LUA_RELEASE: 5.5.0
LUA_RELEASE: 5.5.1
LUAJIT_VER: 2.1
LUAJIT_RELEASE: 2.1-20250826
LUAJIT_LUAVER: 5.1
@@ -482,8 +482,7 @@ jobs:
cp -a -t output psmisc/psmisc-*.src/psmisc-*/inst/usr/bin/killall.exe /usr/bin/cygwin1.dll &&
wget -O WinDivert.zip https://github.com/basil00/WinDivert/releases/download/v2.2.2/WinDivert-2.2.2-A.zip &&
unzip -j WinDivert.zip "*/${DIR}/WinDivert.dll" "*/${DIR}/WinDivert${BITS}.sys" -d output &&
( [ "$BITS" = 64 ] && rebase -b 0x205c00000 output/WinDivert.dll || true ) &&
peflags --dynamicbase=true --high-entropy-va=true output/WinDivert.dll &&
peflags --dynamicbase=true output/WinDivert.dll &&
zip zapret2-win-${{ matrix.arch }}.zip -j output/*
- name: Upload artifacts
+22 -1
View File
@@ -3,6 +3,11 @@ LIST_HTTPS_TLS12="${LIST_HTTPS_TLS12:-$TESTDIR/list_https_tls12.txt}"
LIST_HTTPS_TLS13="${LIST_HTTPS_TLS13:-$TESTDIR/list_https_tls13.txt}"
LIST_QUIC="${LIST_QUIC:-$TESTDIR/list_quic.txt}"
emptyf()
{
:
}
check_list()
{
# $1 - test function
@@ -19,7 +24,23 @@ check_list()
""|\#*) continue ;;
esac
line=$(echo "$line" | tr -d "\r\n")
eval pktws_curl_test_update "$1" "$2" $line && ok=1
# dry run eval in subshell. can fail because of unescaped chars or something else
if (eval emptyf $line) 2>/dev/null; then
# real run in the current shell. can modify vars
eval pktws_curl_test_update "$1" "$2" $line && ok=1
else
# $line contains characters that are meaningful to nfqws2/lua-desync
# syntax (e.g. "<" in --out-range=s1<d1, or "(" and "'" in a
# --lua-init=...=tls_mod(...,'rnd') expression) but aren't valid
# shell syntax on their own, so the dry run above rejected it.
# Fall back to a plain (non-eval) split: $line is only word-split
# on whitespace here, nothing in it is re-parsed as shell syntax,
# so these can be used exactly as blockcheck2 itself prints them,
# with no manual escaping needed.
pktws_curl_test_update "$1" "$2" $line && ok=1
echo >&2 'WARNING: this strategy fails eval (may be unescaped special characters). using fallback space separated method'
echo >&2 "WARNING: exact line : $line"
fi
done < "$3"
[ "$ok" = 1 ]
+2 -2
View File
@@ -2,11 +2,11 @@
Скопируйте эту директорию под другим именем в blockcheck2.d, отредактируйте list файлы, впишите туда свои стратегии.
В диалоге blockcheck2.sh выберите тест с названием вашей директории.
Можно комментировать строки символом '#' в начале строки.
Параметры со спец символами типа "<" должны быть эскейпнуты по правилам shell.
Параметры со спец символами типа "<" больше не обязательно эскейпить - если строка не разбирается как shell, она пробуется заново буквально. Эскейпинг по правилам shell по-прежнему работает и пробуется первым.
Альтернативный путь до файлов стратегий можно задать переменными LIST_HTTP, LIST_HTTPS_TLS12, LIST_HTTPS_TLS13, LIST_QUIC.
This is simple strategy tester from a file.
Copy this folder, write your strategies into list files and select your test in blockcheck2 dialog.
Lines can be commented using the '#' symbol at the line start.
Parameters with special symbols like "<" must be escaped.
Parameters with special symbols like "<" no longer need escaping - a line that doesn't parse as shell is retried literally instead of failing. Escaping by shell rules still works too and is tried first.
Strategy list files paths can be overriden in env variables : LIST_HTTP, LIST_HTTPS_TLS12, LIST_HTTPS_TLS13, LIST_QUIC.
+9
View File
@@ -41,7 +41,9 @@ pktws_check_http()
pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=fake_http:@"$FAKE_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS && ok=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS --payload=empty "--out-range=<s1" --lua-desync=send:$TCP_MD5 && ok=1
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
for ttl in $attls; do
for ff in $fake 0x00000000; do
@@ -52,6 +54,7 @@ pktws_check_http()
}
done
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ $ok = 0 -a "$SCANLEVEL" != force ] && need_fake=1
@@ -64,9 +67,13 @@ pktws_fake_https_vary_()
local ok_any=0 testf=$1 domain="$2" fooling="$3" pre="$4" post="$5"
shift; shift; shift
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=multisplit:blob=$fake:$fooling:pos=2:nodrop:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid,padencap:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 ] && ok=1
}
@@ -115,11 +122,13 @@ pktws_check_https_tls()
done
for fooling in $FOOLINGS_TCP; do
pktws_fake_https_vary $testf $domain "$fooling" "$pre"
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
for ttl in $attls; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ $ok = 0 -a "$SCANLEVEL" != force ] && need_fake=1
+6
View File
@@ -35,6 +35,7 @@ pktws_check_faked()
[ "$SCANLEVEL" = force ] || break
}
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$ok" = 1 ] && break
done
@@ -43,7 +44,9 @@ pktws_check_faked()
pktws_curl_test_update $testf $domain ${FAKED_PATTERN:+--blob=faked_pat:@"$FAKED_PATTERN" }$pre $PAYLOAD --lua-desync=$splitf:${FAKED_PATTERN:+pattern=faked_pat:}pos=$split:$fooling && ok=1
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && pktws_curl_test_update $testf $domain ${FAKED_PATTERN:+--blob=faked_pat:@"$FAKED_PATTERN" }$pre $PAYLOAD --lua-desync=$splitf:${FAKED_PATTERN:+pattern=faked_pat:}pos=$split:$fooling:repeats=$FAKE_REPEATS --payload=empty --out-range="<s1" --lua-desync=send:$TCP_MD5 && ok=1
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
for ttl in $attls; do
for split in $splits; do
@@ -53,10 +56,13 @@ pktws_check_faked()
[ "$SCANLEVEL" = force ] || break
}
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ $ok = 0 -a "$SCANLEVEL" != force ] && eval need_$splitf=1
[ $ok = 1 ] && ok_any=1
[ "$SCANLEVEL" = veryquick -a "$ok_any" = 1 ] && break
done
[ "$ok_any" = 1 ]
}
+8
View File
@@ -7,11 +7,17 @@ pktws_hostfake_vary_()
for disorder in '' 'disorder_after:'; do
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake1:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake2:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake1:midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake2:midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
done
[ "$ok_any" = 1 ] && ok=1
}
@@ -51,11 +57,13 @@ pktws_check_hostfake()
done
for fooling in $FOOLINGS_TCP; do
pktws_hostfake_vary $testf $domain "$fooling" "$pre"
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
for ttl in $attls; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_hostfake_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ $ok = 0 -a "$SCANLEVEL" != force ] && need_hostfakesplit=1
[ $ok = 1 ]
+10
View File
@@ -70,9 +70,13 @@ pktws_fake_https_vary_()
local ok_any=0 testf=$1 domain="$2" fooling="$3" pre="$4" post="$5"
shift; shift; shift
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=multisplit:blob=$fake:$fooling:pos=2:nodrop:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid,padencap:repeats=$FAKE_REPEATS --lua-desync=$splitf:pos=$split $post && ok_any=1
[ "$ok_any" = 1 ] && ok=1
}
@@ -122,22 +126,28 @@ pktws_check_https_tls()
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_ttl=$ttl" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
for split in $splits; do
pktws_fake_https_vary $testf $domain "$fooling" "$pre"
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
for ttl in $attls; do
for split in $splits; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$ok" = 1 ] && ok_any=1
[ "$SCANLEVEL" = veryquick -a "$ok_any" = 1 ] && break
done
[ "$ok_any" = 1 ]
}
+16
View File
@@ -38,6 +38,7 @@ pktws_check_http()
}
done
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$ok" = 1 ] && break
done
@@ -48,7 +49,9 @@ pktws_check_http()
# duplicate SYN with MD5
contains "$fooling" tcp_md5 && pktws_curl_test_update $testf $domain ${FAKE_HTTP:+--blob=$fake:@"$FAKE_HTTP" }${FAKED_PATTERN_HTTP:+--blob=faked_pat:@"$FAKED_PATTERN_HTTP" }$PAYLOAD --lua-desync=fake:blob=$ff:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTP:+pattern=faked_pat:}pos=$split:$fooling:repeats=$FAKE_REPEATS --payload=empty "--out-range=<s1" --lua-desync=send:$TCP_MD5 && ok=1
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
for ttl in $attls; do
for split in $splits; do
@@ -60,8 +63,11 @@ pktws_check_http()
}
done
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
}
@@ -70,9 +76,13 @@ pktws_fake_https_vary_()
local ok_any=0 testf=$1 domain="$2" fooling="$3" pre="$4" post="$5"
shift; shift; shift
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS:+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS:+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=0x00000000:$fooling:repeats=$FAKE_REPEATS --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS:+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=multisplit:blob=$fake:$fooling:pos=2:nodrop:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS:+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
[ "$ok_any" = 1 -a "$SCANLEVEL" = veryquick ] && { ok=1; return 0; }
pktws_curl_test_update $testf $domain ${FAKE_HTTPS:+--blob=$fake:@"$FAKE_HTTPS" }${FAKED_PATTERN_HTTPS:+--blob=faked_pat:@"$FAKED_PATTERN_HTTPS" }$pre $PAYLOAD --lua-desync=fake:blob=$fake:$fooling:tls_mod=rnd,dupsid,padencap:repeats=$FAKE_REPEATS --lua-desync=$splitf:${FAKED_PATTERN_HTTPS:+pattern=faked_pat:}pos=$split:$fooling $post && ok_any=1
[ "$ok_any" = 1 ] && ok=1
@@ -123,22 +133,28 @@ pktws_check_https_tls()
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_ttl=$ttl" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$ok" = 1 ] && break
done
for fooling in $FOOLINGS_TCP; do
for split in $splits; do
pktws_fake_https_vary $testf $domain "$fooling" "$pre"
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
for ttl in $attls; do
for split in $splits; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
pktws_fake_https_vary $testf $domain "ip${IPVV}_autottl=-$ttl,3-20" "$pre" "$f" && [ "$SCANLEVEL" != force ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$ok" = 1 ] && ok_any=1
[ "$SCANLEVEL" = veryquick -a "$ok_any" = 1 ] && break
done
[ "$ok_any" = 1 ]
}
@@ -7,11 +7,17 @@ pktws_hostfake_vary_()
for disorder in '' 'disorder_after:'; do
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}$fooling:repeats=$FAKE_REPEATS $post && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" = veryquick ] && return 0
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake1:$fooling:repeats=$FAKE_REPEATS $post && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" = veryquick ] && return 0
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake2:$fooling:repeats=$FAKE_REPEATS $post && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" = veryquick ] && return 0
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" = veryquick ] && return 0
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake1:midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" = veryquick ] && return 0
pktws_curl_test_update $testf $domain $pre ${FAKE:+--blob=$fake:@"$FAKE" }$PAYLOAD --lua-desync=fake:blob=$fake:$fooling:repeats=$FAKE_REPEATS --lua-desync=hostfakesplit:${HOSTFAKE:+host=${HOSTFAKE}:}${disorder}nofake2:midhost=midsld:$fooling:repeats=$FAKE_REPEATS $post && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" = veryquick ] && return 0
done
}
pktws_hostfake_vary()
@@ -48,6 +54,7 @@ pktws_check_hostfake()
done
for fooling in $FOOLINGS_TCP; do
pktws_hostfake_vary $testf $domain "$fooling" "$pre" && ok=1
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
for ttl in $attls; do
for f in '' "--payload=empty --out-range=s1<d1 --lua-desync=pktmod:ip${IPVV}_ttl=1"; do
@@ -56,6 +63,7 @@ pktws_check_hostfake()
[ "$SCANLEVEL" = force ] || break
}
done
[ "$SCANLEVEL" = veryquick -a "$ok" = 1 ] && break
done
[ "$ok" = 1 ]
}
+234 -63
View File
@@ -34,9 +34,12 @@ DVTWS2=${DVTWS2:-${ZAPRET_BASE}/nfq2/dvtws2}
WINWS2=${WINWS2:-${ZAPRET_BASE}/nfq2/winws2}
MDIG=${MDIG:-${ZAPRET_BASE}/mdig/mdig}
DESYNC_MARK=0x10000000
PACE=${PACE:-0}
CURL_MAX_TIME=${CURL_MAX_TIME:-2}
CURL_MAX_TIME_QUIC=${CURL_MAX_TIME_QUIC:-$CURL_MAX_TIME}
CURL_MAX_TIME_DOH=${CURL_MAX_TIME_DOH:-2}
CURL_PAD=${CURL_PAD:-0}
PAD_MAX_HEADER=${PAD_MAX_HEADER:-4000}
USER_AGENT=${USER_AGENT:-Mozilla}
HTTP_PORT=${HTTP_PORT:-80}
HTTPS_PORT=${HTTPS_PORT:-443}
@@ -49,13 +52,19 @@ IPFW_RULE_NUM=${IPFW_RULE_NUM:-$(($$ % $IPFW_RULE_MAX + 1))}
IPFW_DIVERT_PORT=${IPFW_DIVERT_PORT:-$(($$ % 64536 + 1000))}
QNUM=${QNUM:-$(($$ % 64536 + 1000))}
IPSET_FILE=/tmp/blockcheck_ipset_$$.txt
PARALLEL_OUT=/tmp/zapret_parallel_$$
HDRTEMP=/tmp/zapret-hdr-$$
NFT_TABLE=blockcheck$$
IPT_OUT_CHAIN=blockcheck_output_$$
IPT_IN_CHAIN=blockcheck_input_$$
IPT_COMMENT="-m comment --comment blockcheck_$$"
PARALLEL_OUT_TEMPLATE=/tmp/zapret2_parallel_
PARALLEL_OUT=$PARALLEL_OUT_TEMPLATE$$
HDRTEMP_TEMPLATE=/tmp/zapret2-hdr-
HDRTEMP=$HDRTEMP_TEMPLATE$$
CURL_PAD_FILE_TEMPLATE=/tmp/zapret2-curlpad-
NFT_TABLE_TEMPLATE=blockcheck2-
NFT_TABLE=$NFT_TABLE_TEMPLATE$$
IPT_OUT_CHAIN_TEMPLATE=blockcheck2_output_
IPT_OUT_CHAIN=$IPT_OUT_CHAIN_TEMPLATE$$
IPT_IN_CHAIN_TEMPLATE=blockcheck2_input_
IPT_IN_CHAIN=$IPT_IN_CHAIN_TEMPLATE$$
IPT_COMMENT="-m comment --comment blockcheck2_$$"
DNSCHECK_DNS=${DNSCHECK_DNS:-8.8.8.8 1.1.1.1 77.88.8.1}
DNSCHECK_DOM=${DNSCHECK_DOM:-pornhub.com ej.ru rutracker.org www.torproject.org bbc.com}
@@ -64,17 +73,67 @@ DNSCHECK_DIG1=/tmp/dig1.txt
DNSCHECK_DIG2=/tmp/dig2.txt
DNSCHECK_DIGS=/tmp/digs.txt
GUARD_TIMER="timer_set('exit_guard',function(name,data) io.stderr:write('exit_guard\n'); os.exit(3000); end,20000,true)"
unset PF_STATUS
PF_RULES_SAVE=/tmp/pf-zapret-save.conf
PF_RULES_SAVE=/tmp/pf-zapret2-save.conf
unset ALL_PROXY
apply_header_padfile()
{
local n=1 left size f pid
# A normal exit already removes this run's own pad file (see
# cleanup() below), which covers Ctrl+C, a closed terminal, and
# a plain kill. kill -9 and crashes bypass that, as they would
# for any script's cleanup, so sweep any such leftover from
# a previous run before making this run's own - only once its PID
# is confirmed no longer running, so a genuinely still-running
# concurrent instance (different PID, its own file) is never
# touched
for f in $CURL_PAD_FILE_TEMPLATE*; do
[ -e "$f" ] || continue
pid=${f##*-}
case "$pid" in ''|*[!0-9]*) continue ;; esac
kill -0 "$pid" 2>/dev/null || rm -f "$f"
done
if [ "$CURL_PAD" -gt 0 ] 2>/dev/null; then
left=$CURL_PAD
CURL_PAD_FILE=$CURL_PAD_FILE_TEMPLATE$$
rm -f "$CURL_PAD_FILE"
touch "$CURL_PAD_FILE"
while [ "$left" -ge 14 ]; do
left=$(($left-14))
if [ "$left" -gt "$PAD_MAX_HEADER" ]; then
size="$PAD_MAX_HEADER"
else
size=$left
fi
left=$(($left-$size))
# if the next header would be too small add the remainder to the current header
[ "$left" -lt 14 ] && size=$(($size+$left))
printf 'X-Pad-%04d: ' $n >> "$CURL_PAD_FILE"
printf "%${size}s" | tr ' ' 'A' >> "$CURL_PAD_FILE"
# curl internally adds 0D0A line ending even if it's 0A in the file
echo >> "$CURL_PAD_FILE"
n=$(($n+1))
done
CURL_OPT="${CURL_OPT}${CURL_OPT:+ }-H @$CURL_PAD_FILE"
fi
}
killwait()
{
# $1 - signal (-9, -2, ...)
# $2 - pid
kill $1 $2
local KILL=kill
# avoid internal bash kill in cygwin to support -f
[ "$UNAME" = "CYGWIN" ] && KILL=/bin/kill
$KILL $1 $2
# suppress job kill message
wait $2 2>/dev/null
}
@@ -161,6 +220,7 @@ cleanup()
pf_clean
;;
esac
[ -n "$CURL_PAD_FILE" ] && rm -f "$CURL_PAD_FILE"
}
IPT()
@@ -634,8 +694,13 @@ curl_with_subst_ip()
local connect_to="--connect-to $1::$ip${2:+:$2}" arg
shift ; shift ; shift;
[ "$CURL_VERBOSE" = 1 ] && arg="-v"
[ "$CURL_CMD" = 1 ] && echo $CURL ${arg:+$arg }$connect_to "$@"
ALL_PROXY="$ALL_PROXY" "$CURL" ${arg:+$arg }$connect_to "$@"
[ "$CURL_CMD" = 1 ] && echo $CURL -w "%{time_total}" ${arg:+$arg }$connect_to "$@"
local tooks
tooks=$(ALL_PROXY="$ALL_PROXY" "$CURL" -w "%{time_total}" ${arg:+$arg }$connect_to "$@")
local ret=$?
local tookms=$(awk "BEGIN {print int($tooks * 1000)}")
[ "$PACE" -gt "$tookms" ] && msleep $(($PACE - $tookms))
return "$ret"
}
curl_with_dig()
{
@@ -819,7 +884,7 @@ pktws_ipt_prepare()
# $2 - port
# $3 - ip list
local ip
local ip v=
case "$FWTYPE" in
iptables)
@@ -839,10 +904,12 @@ pktws_ipt_prepare()
opf_prepare_dvtws $1 $2 "$3"
;;
windivert)
[ "$IPV" = 6 ] && v="v6"
WF="--wf-l3=ipv${IPV} --wf-${1}-out=$2"
rm -f "$IPSET_FILE"
WFRAWF=
for ip in $3; do
echo $ip >>"$IPSET_FILE"
[ -n "$WFRAWF" ] && WFRAWF="$WFRAWF or "
WFRAWF="${WFRAWF}ip${v}.DstAddr=$ip or ip${v}.SrcAddr=$ip"
done
;;
@@ -871,8 +938,7 @@ pktws_ipt_unprepare()
pf_restore
;;
windivert)
unset WF
rm -f "$IPSET_FILE"
unset WF WFRAWF
;;
esac
}
@@ -940,43 +1006,70 @@ pktws_ipt_unprepare_udp()
pktws_ipt_unprepare udp $1
}
pktws_start()
pktws_ipt_sweep()
{
local pidfile
case "$UNAME" in
Linux)
"$NFQWS2" --uid $WS_UID:$WS_GID --fwmark=$DESYNC_MARK --qnum=$QNUM --lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
PID=$!
# give some time to initialize
minsleep
# remove leftovers after hard kill/crash
local f pid IPTABLES
case "$FWTYPE" in
nftables)
nft list tables | grep '^table inet blockcheck2-' | while IFS= read -r f; do
pid=${f##*-}
case "$pid" in ''|*[!0-9]*) continue ;; esac
kill -0 "$pid" 2>/dev/null || nft delete $f
done
;;
FreeBSD|OpenBSD)
"$DVTWS2" --port=$IPFW_DIVERT_PORT --lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
PID=$!
# give some time to initialize
minsleep
;;
CYGWIN)
pidfile="/tmp/blockcheck_cygpid_$$"
# avoid fork
# allow multiple PKTWS instances with the same wf filter but different ipset
# some methods require empty acks
cygstart --hide "$WINWS2" --pidfile="$pidfile" --wf-dup-check=0 --wf-tcp-empty=1 $WF --ipset="$IPSET_FILE" --lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
# give some time to initialize
minsleep
if [ -f "$pidfile" ]; then
read PID <"$pidfile"
rm -f "$pidfile"
else
echo "pktws failed to initialize within specified time !!"
fi
iptables)
for IPTABLES in iptables ip6tables; do
$IPTABLES -t mangle -S | grep -o -e "INPUT -j $IPT_IN_CHAIN_TEMPLATE[0-9]\+" -e "OUTPUT -j $IPT_OUT_CHAIN_TEMPLATE[0-9]\+" | while IFD= read -r f; do
pid=${f##*_}
case "$pid" in ''|*[!0-9]*) continue ;; esac
kill -0 "$pid" 2>/dev/null || $IPTABLES -t mangle -D $f
done
$IPTABLES -t mangle -S | grep -o -e "$IPT_IN_CHAIN_TEMPLATE[0-9]\+" -e "$IPT_OUT_CHAIN_TEMPLATE[0-9]\+" | sort -u | while IFD= read -r f; do
pid=${f##*_}
case "$pid" in ''|*[!0-9]*) continue ;; esac
kill -0 "$pid" 2>/dev/null || {
$IPTABLES -t mangle -F $f
$IPTABLES -t mangle -X $f
}
done
done
;;
esac
}
pktws_start()
{
case "$UNAME" in
Linux)
"$NFQWS2" --uid $WS_UID:$WS_GID --fwmark=$DESYNC_MARK --qnum=$QNUM --lua-init="$GUARD_TIMER" --lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
;;
FreeBSD|OpenBSD)
"$DVTWS2" --port=$IPFW_DIVERT_PORT --lua-init="$GUARD_TIMER" --lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
;;
CYGWIN)
# some methods require empty acks
# use raw filter to filter target ip addresses in-kernel to allow multiple blockchecks running at the same time
# use guard timer to kill unmanaged pktws instances
"$WINWS2" --wf-tcp-empty=1 $WF --wf-raw-filter="$WFRAWF" \
--lua-init="$GUARD_TIMER" \
--lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
;;
esac
PID=$!
# give some time to initialize
minsleep
}
ws_kill()
{
local sig
[ -z "$PID" ] || {
killwait -9 $PID 2>/dev/null
sig=-9
# use fast kill using TerminateProcess() in cygwin
[ "$UNAME" = "CYGWIN" ] && sig=-f
killwait $sig $PID 2>/dev/null
PID=
}
}
@@ -1040,7 +1133,7 @@ curl_test()
[ $REPEATS -gt 1 ] && echo 'AVAILABLE'
else
code=$?
[ "$SCANLEVEL" = quick ] && break
[ "$SCANLEVEL" = quick -o "$SCANLEVEL" = veryquick ] && break
fi
done
fi
@@ -1186,6 +1279,29 @@ result_intersection_print()
n=$(($n + 1))
done
}
result_coverage_print()
{
# Lists every distinct successful strategy together with how many of the
# DOMAINS_COUNT domains/targets it worked for, most-covered first.
# Unlike result_intersection_print, a strategy doesn't need 100% coverage
# to be listed here - useful when DOMAINS is a set of largely independent
# targets (e.g. one IP per ASN/provider) where no single strategy is
# expected to satisfy every one of them, and the question is which
# strategy gets closest.
local n=0 hash hashvar hashcountvar ct val
while : ; do
eval hash=\"\$RES_$n\"
[ -n "$hash" ] || break
hashvar=RESHASH_${hash}
hashcountvar=${hashvar}_COUNTER
eval ct=\"\$$hashcountvar\"
eval val=\"\$$hashvar\"
printf '%s %s\n' "$ct" "$val"
n=$(($n + 1))
done | sort -rn | while IFS=' ' read -r ct rest; do
echo "$ct/$DOMAINS_COUNT : $rest"
done
}
report_strategy()
{
# $1 - test function
@@ -1590,11 +1706,13 @@ ask_params()
echo "TLS 1.3 only strategy is better than nothing."
ask_yes_no_var ENABLE_HTTPS_TLS13 "check https tls 1.3"
}
else
echo
echo "installed curl version does not support TLS 1.3 . tests disabled."
fi
}
[ "$ENABLE_HTTPS_TLS13" = 1 -a -z "$TLS13" ] && {
echo
echo "WARNING ! installed curl version does not support TLS 1.3 . tests disabled."
ENABLE_HTTPS_TLS13=0
}
[ -n "$ENABLE_HTTP3" ] || {
ENABLE_HTTP3=0
@@ -1605,11 +1723,13 @@ ask_params()
echo "make sure target domain(s) support QUIC or result will be negative in any case"
ask_yes_no_var ENABLE_HTTP3 "check http3 QUIC"
}
else
echo
echo "installed curl version does not support http3 QUIC. tests disabled."
fi
}
[ "$ENABLE_HTTP3" = 1 -a -z "$HTTP3" ] && {
echo
echo "WARNING ! installed curl version does not support http3 QUIC. tests disabled."
ENABLE_HTTP3=0
}
[ -n "$REPEATS" ] || {
[ "$BATCH" = 1 ] || {
@@ -1638,10 +1758,11 @@ ask_params()
SCANLEVEL=standard
[ "$BATCH" = 1 ] || {
echo
echo quick - in multi-attempt mode skip further attempts after first failure
echo standard - do investigation what works on your DPI
echo force - scan maximum despite of result
ask_list SCANLEVEL "quick standard force" "$SCANLEVEL"
echo veryquick - same as quick, and also stop testing more variations of a strategy family once one already succeeds
echo quick - in multi-attempt mode skip further attempts after first failure
echo standard - do investigation what works on your DPI
echo force - scan maximum despite of result
ask_list SCANLEVEL "veryquick quick standard force" "$SCANLEVEL"
}
}
@@ -1840,6 +1961,49 @@ check_dns()
return $r
}
sweep_stale_pidfiles()
{
# $1 - fixed filename prefix immediately preceding the pid
#
# A normal exit already removes this run's own PARALLEL_OUT/HDRTEMP
# files (unprepare_all(), further down), which covers Ctrl+C,
# a closed terminal, and a plain kill. kill -9 and crashes bypass
# that, as they would for any script's cleanup, and can leave
# stale files behind indefinitely, since nothing about a later run
# gives it any reason to know about a different PID's leftovers.
# Removes a matching file only once its PID (the run that created
# it) is confirmed no longer running, so a genuinely still-running
# concurrent instance - a different PID, its own files - is never
# touched.
local f pid prefix="$1"
for f in "$prefix"*; do
[ -e "$f" ] || continue
pid=${f#"$prefix"}
pid=${pid%%[!0-9]*}
[ -n "$pid" ] || continue
kill -0 "$pid" 2>/dev/null || rm -f "$f"
done
}
sweep_leftovers()
{
sweep_stale_pidfiles $PARALLEL_OUT_TEMPLATE
sweep_stale_pidfiles $HDRTEMP_TEMPLATE
pktws_ipt_sweep
}
block_signals()
{
# prevent signal function reenter
trap '' QUIT TERM HUP PIPE INT
}
untrap()
{
trap - QUIT TERM HUP PIPE INT
}
unprepare_all()
{
# make sure we are not in a middle state that impacts connectivity
@@ -1855,18 +2019,23 @@ unprepare_all()
}
sigint()
{
block_signals
echo
echo terminating...
unprepare_all
# exitp can wait for the user input. restore default signal behavior without trap
untrap
exitp 1
}
sigint_cleanup()
{
block_signals
cleanup
exit 1
}
sigsilent()
{
block_signals
# must not write anything here to stdout
unprepare_all
exit 1
@@ -1877,19 +2046,20 @@ fix_sbin_path
check_system
check_already
[ "$UNAME" != CYGWIN -a "$SKIP_PKTWS" != 1 ] && require_root
sweep_leftovers
check_prerequisites
trap sigint_cleanup INT
check_dns
check_virt
ask_params
apply_header_padfile
trap - INT
PID=
NREPORT=
unset WF
trap sigint INT
trap sigsilent PIPE
trap sigsilent HUP
trap sigsilent PIPE HUP TERM QUIT
for dom in $DOMAINS; do
for IPV in $IPVS; do
configure_ip_version
@@ -1903,9 +2073,7 @@ for dom in $DOMAINS; do
[ "$ENABLE_HTTP3" = 1 ] && check_domain_http3 $dom
done
done
trap - HUP
trap - PIPE
trap - INT
untrap
cleanup
@@ -1917,10 +2085,13 @@ report_print
echo \* COMMON
result_intersection_print
echo
echo \* COVERAGE
result_coverage_print
echo
[ "$SCANLEVEL" = force ] || {
echo "blockcheck optimizes test sequence. To save time some strategies can be skipped if their test is considered useless."
echo "That's why COMMON intersection can miss strategies that would work for all domains."
echo "Use \"force\" scan level to test all strategies and generate trustable intersection."
echo "That's why COMMON intersection can miss strategies that would work for all domains, and COVERAGE counts can undercount."
echo "Use \"force\" scan level to test all strategies and generate trustable results."
echo "Current scan level was \"$SCANLEVEL\"".
}
}
+39
View File
@@ -314,3 +314,42 @@ v0.9.4.3
* nfqws2: limit delayed packet queue to 64 packets (anti-ddos)
* init.d: add udp ports range 19294-19344 to discord media
* blockcheck2: use cygstart in windows to avoid fork problems (without cygwin rebase)
1.0.4
* blockcheck2: revert cygstart method as it brings more problems than profit
* nfqws2: stop execution plan in C code if a blob is unavailable
* nfqws2: desync_timer_name includes now instance name
* builder_linux: optional build with zlib-ng : set ZLIB_NG=1 variable
* github: remove high_entropy_va patch for windivert.dll, but leave dynamic_base
1.0.5
* blockcheck2: use --wf-raw-filter in Windows instead of --ipset to limit target IP addresses
* github, builder_linux: lua 5.5.1
* nfqws2: negate SSID/NLM filters
* nfqws2: desync function can explicitly return nil as a synonym of VERDICT_PASS
* nfqws2: pad tcp options with 0x00 in reconstruct. strip 0x00 ending options in dissect.
* nfqws2: --filter-mark
* zapret-lib: luaexec now passes returned verdict code
* blockcheck2: fix script crash on bad strategies from the file in custom/10-list.sh
1.0.5.1
* nfqws2: optimize profile search. fast checks first
1.0.5.2
* nfqws2: more profile search optimizations
1.0.6
* zapret-lib: dissect quic tvb values that can't fit into 52-bit mantissa into raw strings
* zapret-lib, nfqws2: treat ? and # characters as domain end in URL
* blockcheck2: in 10-list.sh try space separated logic as fallback if eval fails (useful for copy-paste from blockcheck2)
* blockcheck2: CURL_PAD - add junk headers to increase request size and trigger 16-kb block without large downloaded data
* blockcheck2: in addition to COMMON print COVERAGE section with how many domains worked for each strategy
* blockcheck2: sweep old temp files after hard kill or crash. sweep ip/nf tables in linux
* blockcheck2: pktws guard timer in all OS
* blockcheck2: request PACE-ing
* blockcheck2: veryquick scan level
-11
View File
@@ -1,11 +0,0 @@
Here listed all api breaking changes.
When something changes capable of breaking things NFQWS2_COMPAT_VER increases.
v2
* removed "stun_binding_req" specialized payload. replaced with common "stun" - any stun packets, not only binding request.
every LUA relying on desync.l7payload should be revised.
nfqws2 --payload option and init.d custom scripts must be updated.
v3
* restructured desync.track. pass positions in desync.track.pos.{client,server,direct,reverse}
code relying on conntrack counters and sequence numbers must be rewritten
+2 -2
View File
@@ -1,11 +1,11 @@
* debian,ubuntu :
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev libmnl-dev libsystemd-dev libluajit2-5.1-dev
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev libmnl-dev libsystemd-dev libluajit-5.1-dev
make -C /opt/zapret2 systemd
* fedora, rhel, centos, alma
dnf add make gcc zlib-devel libcap-devel libnetfilter_queue-devel libmnl-devel systemd-devel luajit-devel
dnf install make gcc zlib-ng-compat-devel libcap-devel libnetfilter_queue-devel libmnl-devel systemd-devel luajit-devel
make -C /opt/zapret2 systemd
* linux static :
+11 -5
View File
@@ -16,8 +16,10 @@ dl_deps()
curl $CURL_OPT -Lo - https://www.netfilter.org/pub/libmnl/libmnl-1.0.5.tar.bz2 | tar -xj || exit 5
exists_dir libnetfilter_queue-* ||
curl $CURL_OPT -Lo - https://www.netfilter.org/pub/libnetfilter_queue/libnetfilter_queue-1.0.5.tar.bz2 | tar -xj || exit 5
exists_dir zlib-* ||
curl $CURL_OPT -Lo - https://zlib.net/fossils/zlib-1.3.2.tar.gz | tar -xz || exit 5
exists_dir zlib-ng-${ZLIB_NG_VER} ||
curl $CURL_OPT -Lo - https://github.com/zlib-ng/zlib-ng/archive/refs/tags/${ZLIB_NG_VER}.tar.gz | tar -xz || exit 5
exists_dir zlib-${ZLIB_VER} ||
curl $CURL_OPT -Lo - https://zlib.net/fossils/zlib-${ZLIB_VER}.tar.gz | tar -xz || exit 5
exists_dir luajit2-* ||
curl $CURL_OPT -Lo - https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz | tar -xz || exit 5
exists_dir lua-* ||
@@ -42,11 +44,15 @@ build_netlink()
build_zlib()
{
(
cd zlib-*
if [ "$ZLIB_NG" = 1 ]; then
cd zlib-ng-${ZLIB_NG_VER}
else
cd zlib-${ZLIB_VER}
fi
[ -f "Makefile" ] && make clean
CFLAGS="$OPTIMIZE $MINSIZE $CFLAGS" \
LDFLAGS="$LDMINSIZE $LDFLAGS" \
./configure --prefix= --static
./configure --prefix= --static --zlib-compat
make install -j$nproc DESTDIR=$STAGING_DIR
)
}
@@ -107,8 +113,8 @@ for t in $TGT; do
CFLAGS="$CFLAGS $CFLAGS_PIC"
pushd "$DEPS"
install_h_files
build_netlink
build_zlib
build_netlink
build_lua
build_luajit_for_target $t
popd
+3 -1
View File
@@ -16,8 +16,10 @@ LDFLAGS="-lgcc_eh $LDFLAGS"
# PIE does not work for arm32 and all mips
PIE=${PIE:-0}
HOSTCC=${HOSTCC:-cc}
ZLIB_VER=${ZLIB_VER:-1.3.2}
ZLIB_NG_VER=${ZLIB_NG_VER:-2.3.3}
LUA_VER=${LUA_VER:-5.5}
LUA_RELEASE=${LUA_RELEASE:-5.5.0}
LUA_RELEASE=${LUA_RELEASE:-5.5.1}
LUAJIT_VER=${LUAJIT_VER:-2.1}
LUAJIT_RELEASE=${LUAJIT_RELEASE:-2.1-20250826}
LUAJIT_LUA_VER=${LUAJIT_LUA_VER:-5.1}
+86 -51
View File
@@ -84,6 +84,7 @@
- [standard rawsend](#standard-rawsend)
- [Dissection and reconstruction](#dissection-and-reconstruction)
- [dissect](#dissect)
- [dissect_hdr](#dissect_hdr)
- [reconstruct_dissect](#reconstruct_dissect)
- [reconstruct_hdr](#reconstruct_hdr)
- [csum_fix](#csum_fix)
@@ -695,8 +696,8 @@ MULTI-STRATEGY:
LUA PACKET PASS MODE:
--payload=type[,type] ; in-profile filter: payload filter for subsequent instances within the profile
--out-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>] ; in-profile filter: conntrack counter range for subsequent instances within the profile - outgoing direction
--in-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>] ; in-profile filter: conntrack counter range for subsequent instances within the profile - incoming direction
--out-range=<mode>[<int>][(-|<)<mode>[<int>]] ; in-profile filter: conntrack counter range for subsequent instances within the profile - outgoing direction
--in-range=<mode>[<int>][(-|<)<mode>[<int>]] ; in-profile filter: conntrack counter range for subsequent instances within the profile - incoming direction
LUA DESYNC ACTION:
--lua-desync=<function>[:param1=val1[:param2=val2]] ; call a LUA instance with the specified parameters during profile processing if in-profile filter conditions are met
@@ -711,7 +712,9 @@ Specific parameters for nfqws2:
--bind-fix4 ; fix for the issue where generated packets on Linux exit from the wrong interface when using PBR (IPv4)
--bind-fix6 ; same as above for IPv6
--fwmark=<int|0xHEX> ; mark bit to prevent looping. default = 0x40000000
--filter-mark=mark[/mask] ; profile filter: filter by packet mark. mark and mask can be decimal or 0xHEX
--filter-ssid=ssid1[,ssid2,ssid3,...] ; profile filter: WiFi network name (SSID)
--filter-ssid-neg=[0|1] ; profile filter: negate SSID filter
```
Specific parameters for dvtws2:
@@ -744,7 +747,9 @@ Specific parameters for winws2:
--wf-save=<filename> ; save the final complete WinDivert filter to a file
LOGICAL NETWORK FILTER:
--ssid-filter=ssid1[,ssid2,ssid3,...] ; list of WiFi networks; interception is enabled only when connected to these, otherwise it is disabled.
--ssid-filter-neg=[0|1] ; negate WiFi network filter
--nlm-filter=net1[,net2,net3,...] ; list of Network List Manager networks; interception is enabled only when connected to these, otherwise it is disabled.
--nlm-filter-neg=[0|1] ; negate NLM filter
--nlm-list[=all] ; display a list of connected NLM networks. "all" shows all NLM networks.
```
@@ -915,6 +920,8 @@ Another way to address this-and not just for Wi-Fi-is by using the Network List
An NLM network is the result of the system detecting a connection to a specific network. You might connect to a router via Wi-Fi or Ethernet, but it will be recognized as the same network. To distinguish between networks, the system typically looks at the gateway's MAC address. NLM technology is interesting and useful, but unfortunately, adequate management tools were only available in Windows 7. In newer systems, you have to dig into PowerShell or the Registry to manually assign connections to the correct GUIDs if the system categorizes them incorrectly. Alternatively, you can simply use the list of GUIDs automatically assigned by the system.
SSID/NLM filters can be negated in both systems.
## Server mode
Certain types of manipulations can be performed not only from the client side but also from the server side. `nfqws2` was designed to fully support both inbound and outbound traffic, on both the client and server sides.
@@ -1024,7 +1031,7 @@ On the Lua side, dangerous functions are removed: `os.execute`, `io.popen`, `pac
## Calling Lua code
Lua code is invoked in two stages:
Lua code is invoked in 3 stages:
1. Once at program startup via `--lua-init=code|@file`. If the parameter value starts with `@`, a file is executed; otherwise, the value is treated as Lua code. Gzip file compression is supported. The program first checks for "file", then "file.gz".
2. During profile processing via `--lua-desync=function_name:arg1[=val1]:arg2[=val2]:argN[=valN]`.
@@ -1033,6 +1040,7 @@ Two types of automatic substitutions are implemented on the C side:
`%var` substitutes the value of the variable `desync.var` or `var` if the former is missing.
`#var` substitutes the length of the variable `desync.var` or `var` if the former is missing.
Colons and the `%`, `#` symbols at the beginning can be escaped with a backslash `\`.
3. During [timer](#timers) processing.
Both `--lua-init` and `--lua-desync` can be used multiple times. Execution follows the exact order in which they are specified.
@@ -1048,12 +1056,16 @@ A blob is a binary data block of any size that can be loaded into a Lua variable
Direct file operations from Lua code are not recommended unless absolutely necessary. Lua code runs with restricted privileges; intended operations might fail or behave inconsistently across different operating systems and environments. Blob loading occurs before entering the sandbox, providing a higher chance of success.
Since a blob's name is a Lua variable name, you cannot name it arbitrarily. You must follow Lua naming conventions.
For example, a variable name cannot start with a digit, contain non-alphanumeric characters (other than `_`), or use reserved words.
Names are case-sensitive.
### In-profile filters
These come in three types: `--payload`, `--in-range`, and `--out-range`. Filter values remain active from the moment they are specified until the next override.
- `--payload=type1[,type2][,type3]...` accepts a comma-separated list of known [payloads](#protocol-detection), "all", or "known". The default is `--payload=all`.
- `--(in-range|out-range)=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>]` sets conntrack counter ranges for inbound and outbound directions. The default is `--in-range=x`, `--out-range=a`.
- `--(in-range|out-range)=<mode>[<int>][(-|<)<mode>[<int>]]` sets conntrack counter ranges for inbound and outbound directions. The default is `--in-range=x`, `--out-range=a`.
Ranges are specified in the following formats: `mX-mY`, `mX<mY`, `-mY`, `<mY`, `mX-`, where `m` is the counter mode, `X` is the lower bound, and `Y` is the upper bound. Modes `x` and `a` are specified as a single letter without a range or counter value. The `-` sign indicates an inclusive upper bound, while `<` indicates an exclusive one.
@@ -2144,6 +2156,20 @@ Returns a table representing the dissection of the `raw_ip` packet.
This is the same operation that occurs automatically before desync functions are called based on the profile.
Those functions receive an already prepared dissect.
#### dissect_hdr
```
function dissect_tcphdr(tcp)
function dissect_udphdr(udp)
function dissect_icmphdr(icmp)
function dissect_iphdr(ip)
function dissect_ip6hdr(ip6)
```
Dissection of individual raw headers.
Returns a table representing the dissected header.
For TCP options, all 0x00 options (padding) at the end are trimmed.
#### reconstruct_dissect
```
@@ -2181,6 +2207,7 @@ Reconstructs the corresponding raw headers from the dissect tables. Returns the
- IPv6 reconstruction utilizes `reconstruct_opts`, specifically `ip6_preserve_next` and `ip6_last_proto`.
- The IP header checksum is calculated automatically since it does not depend on any other components.
- TCP, UDP and ICMP checksums are not calculated automatically because they depend on other components.
- TCP options are padded with 0x00 to a multiple of 4 bytes.
#### csum_fix
@@ -2223,7 +2250,7 @@ If conntrack is disabled or the packet is not valid tcp or udp, nil is returned.
function get_source_ip(target)
```
Get source IP (raw string) that would be used for connection the the target IP.
Get source IP (raw string) that would be used for connection to the target IP.
Returns nil if destination is unreachable.
```
@@ -2487,6 +2514,8 @@ The code can address the `desync` table-it is temporarily assigned to a global v
Example: `--lua-desync=luaexec:code="desync.rnd=brandom(math.random(5,10))"`
Verdict can be returned using "return" statement.
### pass
```
@@ -2590,10 +2619,10 @@ Performs a linear search in table `a` for value `v`. `array_field_search` assume
function in_list(s, v)
```
### find_next_line
Checks whether string `v` is included in a comma-separated list of strings `s`. For example, `abc` is included in the list `xyz,abc,12345`.
### find_next_line
```
function find_next_line(s, pos)
```
@@ -3435,7 +3464,7 @@ The `ipfrag_options` contain only two standard parameters. The rest are handled
| Field | Description |
| :-------------- | :------------------------------------------------------------------------------------------------------------------ |
| ipfrag | Name of the fragmenter function. If not specified, `ipfrag2` is used. The fragmenter returns an array of dissects (fragments). |
| ipfrag | Name of the fragmenter function. If specified with no value, `ipfrag2` is used. The fragmenter returns an array of dissects (fragments). |
| ipfrag_disorder | Send fragments in reverse order. |
| ipfrag_pos_udp | (ipfrag2 fragmenter) UDP fragmentation position. Must be a multiple of 8; defaults to 8. |
| ipfrag_pos_tcp | (ipfrag2 fragmenter) TCP fragmentation position. Must be a multiple of 8; defaults to 32. |
@@ -3617,9 +3646,9 @@ May not be unique.
function desync_timer_name(desync)
```
Construct timer name that includes dis_timer_name result + conntrack packet number.
Construct timer name that includes instance name, dis_timer_name result, conntrack packet number.
If track is absent random characters are added to the end.
This name can be considered unique per desync and can be used as an oneshot timer name.
This name can be considered unique per desync+instance and can be used as an oneshot timer name.
## Orchestration
@@ -4177,7 +4206,7 @@ function hostfakesplit(ctx, desync)
- arg: host - template for [fake host generation](#genhost) - random.template
- arg: midhost - [marker](#markers) for an additional split of the segment containing the real host.
- arg: disorder_after - [marker](#markers) for an additional split of the final real part and sending segments in reverse order.
- arg: nofake, nofake2 - skip sending specific fake packets.
- arg: nofake1, nofake2 - skip sending specific fake packets.
- arg: blob - replace the current payload with the specified [blob](#passing-blobs).
- arg: optional - abort the operation if the blob is specified but missing.
- arg: nodrop - skip issuing a VERDICT_DROP.
@@ -4392,7 +4421,7 @@ function automate_failure_counter(hrec, crec, fails, maxtime)
- hrec - [host storage](#automate_host_record)
- crec - [stream storage](#automate_conn_record)
- fails - the target number of failures.
- maxtime - the time in seconds after which, since the last failure, the next failure restarts the count from scratch.
- time - the time in seconds after which, since the last failure, the next failure restarts the count from scratch.
Returns `true` if the counter reaches the `fails` value. The counter is reset upon returning `true`.
@@ -4600,7 +4629,7 @@ This is a basic signature detector. If the C code does not recognize the protoco
#### cond_tcp_has_ts
```
function cond_tcp_ts(desync)
function cond_tcp_has_ts(desync)
```
Returns `true` if the dissect is tcp and has tcp timestamp option.
@@ -4766,6 +4795,7 @@ Parallel mode is [supported](#shell-variables). In this mode, each attempt is ex
- **standard** - uses a test algorithm that excludes strategies deemed irrelevant based on previous successes or other criteria. In the case of multiple attempts, testing does not stop upon failure. The success rate and curl errors can also provide useful information for situational analysis.
- **quick** - same as standard, but when using multiple attempts, testing stops after the first failure.
- **veryquick** - same as quick, and also stops testing further variations of a strategy family (extra TCP foolings, autottl deltas, TTL values, and, where applicable, multisplit/fakedsplit positions and split-mechanisms) as soon as any combination has already succeeded for the current target, instead of continuing through the rest for completeness. Only affects the fake-based strategy families (25-fake.sh, 30-faked.sh, 35-hostfake.sh, 50-fake-multi.sh, 55-fake-faked.sh, 60-fake-hostfake.sh); can make those noticeably faster to test when an early candidate already succeeds, at the cost of not necessarily finding every strategy that would also have succeeded (success here means curl got through - it says nothing about how that strategy will actually do in a real browser).
- **force** - tests as extensively as possible, regardless of previous test results.
### Supported protocols
@@ -4899,6 +4929,8 @@ MULTIDISORDER=multidisorder_legacy - replace multidisorder with the version from
NOTEST_BASIC_HTTP=1 - disable 10-http-basic.sh tests
NOTEST_MISC_HTTP=1 - disable http 15-misc.sh tests
NOTEST_MISC_HTTPS=1 - disable https 15-misc.sh tests
NOTEST_OOB_HTTP=1 - disable http 17-oob.sh tests
NOTEST_OOB_HTTPS=1 - disable https 17-oob.sh tests
NOTEST_MULTI_HTTP=1 - disable http 20-multi.sh tests
NOTEST_MULTI_HTTPS=1 - disable https 20-multi.sh tests
NOTEST_SEQOVL_HTTP=1 - disable http 23-seqovl.sh tests
@@ -4911,6 +4943,7 @@ NOTEST_FAKED_HTTP=1 - disable http 25-faked.sh tests
NOTEST_FAKED_HTTPS=1 - disable https 25-faked.sh tests
NOTEST_HOSTFAKE_HTTP=1 - disable http 35-hostfake.sh tests
NOTEST_HOSTFAKE_HTTPS=1 - disable https 35-hostfake.sh tests
HOSTFAKE=host - specify hostname for hostfakesplit
NOTEST_FAKE_MULTI_HTTP=1 - disable http 50-fake-multi.sh tests
NOTEST_FAKE_MULTI_HTTPS=1 - disable https 50-fake-multi.sh tests
NOTEST_FAKE_FAKED_HTTP=1 - disable http 55-fake-faked.sh tests
@@ -4924,53 +4957,55 @@ NOTEST_QUIC=1 - disable 90-quic.sh tests
A simple tester that uses strategy lists from files. Strategies must be on separate lines; line breaks within a single strategy are not allowed. Separate lists are used for different protocols: `list_http.txt`, `list_https_tls12.txt`, `list_https_tls13.txt`, and `list_quic.sh`. These files support comments starting with `#`.
When defining parameters, keep in mind that they will be interpreted as shell arguments. Special characters must be escaped according to shell rules.
For example, leaving `<` unquoted across the entire parameter or incorrectly quoting the `--luaexec=code=print("abc")` parameter will result in an error. If your Lua code uses strings, it is best to enclose them in single quotes and wrap the entire parameter in double quotes.
When defining parameters, keep in mind that they will first be tried as shell arguments. Special characters (`<`, `(`, `'`, and similar) are shell syntax and would normally need escaping - for example, leaving `<` unquoted across the entire parameter, or incorrectly quoting the `--luaexec=code=print("abc")` parameter, would previously result in an error. If a line fails this way now, it's automatically retried as a plain, unparsed argument list instead (split only on whitespace, nothing re-interpreted), so strategies containing these characters work whether or not you escape them yourself - copy one straight from `blockcheck2`'s own output and it will just work. Escaping by hand (single quotes around Lua strings, the whole parameter wrapped in double quotes) is still supported and still takes priority when it succeeds, for anyone who wants shell-side expansion inside a list line.
Note that `blockcheck2` will output strategy parameters without escaping.
The recommended way to use this is to copy it into its own subdirectory within `blockcheck2.d` and populate the `.txt` files with your tests. Then, select your custom test name from the dialog.
## Summary
At the end of the test, all successful strategies are displayed for each domain and each IP protocol version. If more than one domain was tested, the intersection of successful strategies (those that worked for all) is also provided. However, this intersection can only be fully relied upon when using `SCANLEVEL=force`. Otherwise, strategies that might have worked for subsequent domains might not have been tested for the first one.
At the end of the test, all successful strategies are displayed for each domain and each IP protocol version. If more than one domain was tested, the intersection of successful strategies (those that worked for all, labeled COMMON) is also provided, together with a COVERAGE listing of every successful strategy and how many of the domains it worked for, most-covered first - useful when testing a set of largely independent targets where no single strategy is expected to satisfy all of them. However, COMMON and the COVERAGE counts can only be fully relied upon when using `SCANLEVEL=force`. Otherwise, strategies that might have worked for subsequent domains might not have been tested for the first one.
## Shell variables
```
CURL - path to the curl executable
CURL_MAX_TIME - curl timeout in seconds
CURL_MAX_TIME_QUIC - curl timeout for QUIC. If not set, CURL_MAX_TIME is used
CURL_MAX_TIME_DOH - curl timeout for DoH servers
CURL_CMD=1 - display curl commands
CURL_OPT - additional curl options. `-k` to ignore certificates, `-v` for verbose protocol output
CURL_HTTPS_GET=1 - use the GET method instead of HEAD for HTTPS
DOMAINS - space-separated list of domains or domains with URIs to test
TEST - name of the test
IPVS=4|6|46 - IP protocol versions to test
ENABLE_HTTP=0|1 - enable plain HTTP test
ENABLE_HTTPS_TLS12=0|1 - enable HTTPS TLS 1.2 test
ENABLE_HTTPS_TLS13=0|1 - enable HTTPS TLS 1.3 test
ENABLE_HTTP3=0|1 - enable QUIC test
REPEATS - number of test attempts
PARALLEL=0|1 - enable parallel attempts. May overwhelm the site due to aggressive requests and lead to incorrect results
SCANLEVEL=quick|standard|force - scanning level
BATCH=1 - batch mode (no prompts or waiting for console input)
HTTP_PORT, HTTPS_PORT, QUIC_PORT - port numbers for the respective protocols
SKIP_DNSCHECK=1 - skip DNS check
SKIP_IPBLOCK=1 - skip port or IP blocking tests
PKTWS_EXTRA_POST - additional nfqws/dvtws/winws parameters added after the main strategy
PKTWS_EXTRA_POST_1 .. PKTWS_EXTRA_POST_9 - separate additional parameters that contain spaces
PKTWS_EXTRA_PRE - additional nfqws/dvtws/winws parameters added before the main strategy
PKTWS_EXTRA_PRE_1 .. PKTWS_EXTRA_PRE_9 - separate additional parameters that contain spaces
DNSCHECK_DNS - space-separated list of third-party DNS servers for DNS spoofing tests
DNSCHECK_DOM - space-separated list of domains for DNS spoofing tests
SECURE_DNS=0|1 - force enable or disable DoH
DOH_SERVERS - space-separated list of DoH URLs for automatic selection of a working server
DOH_SERVER - specific DoH URL (disables auto-selection)
UNBLOCKED_DOM - an unblocked domain used for IP block tests
SIMULATE=1 - enable simulation mode for debugging script logic. Disables real curl requests and replaces them with random results.
SIM_SUCCESS_RATE=<percent> - simulation success probability as a percentage
```
| Variable | Description |
| :------- | :---------- |
| CURL | path to the curl executable |
| CURL_MAX_TIME | curl timeout in seconds |
| CURL_MAX_TIME_QUIC | curl timeout for QUIC. If not set, CURL_MAX_TIME is used |
| CURL_MAX_TIME_DOH | curl timeout for DoH servers |
| CURL_CMD=1 | display curl commands |
| CURL_OPT | additional curl options. `-k` to ignore certificates, `-v` for verbose protocol output |
| CURL_HTTPS_GET=1 | use the GET method instead of HEAD for HTTPS |
| CURL_PAD | pad every curl request with a junk headers of this many bytes. Full length taken into account along with header name, data field and EOL. Useful for testing a "16 KB"-style whitelist block against a target with no large page of its own to download, since that block triggers on total bytes transferred rather than on how much of it was a real response. Independent of CURL_HTTPS_GET; use whichever fits the target |
| PAD_MAX_HEADER | maximum length in bytes of the data field of one header for CURL_PAD padding. the length can be exceeded by no more than 14 characters for the last header, if the last header together with the name and EOL could be less than 14 characters |
| PACE | minimum time in msec for a single curl query. allows to slow down hammering to avoid server side ban |
| DOMAINS | space-separated list of domains or domains with URIs to test |
| TEST | name of the test |
| IPVS=4\|6\|46 | IP protocol versions to test |
| ENABLE_HTTP=0\|1 | enable plain HTTP test |
| ENABLE_HTTPS_TLS12=0\|1 | enable HTTPS TLS 1.2 test |
| ENABLE_HTTPS_TLS13=0\|1 | enable HTTPS TLS 1.3 test |
| ENABLE_HTTP3=0\|1 | enable QUIC test |
| REPEATS | number of test attempts |
| PARALLEL=0|1 | enable\ parallel\ attempts. May overwhelm the site due to aggressive requests and lead to incorrect results |
| SCANLEVEL=quick\|standard\|force | scanning level |
| BATCH=1 | batch mode (no prompts or waiting for console input) |
| HTTP_PORT, HTTPS_PORT, QUIC_PORT | port numbers for the respective protocols |
| SKIP_DNSCHECK=1 | skip DNS check |
| SKIP_IPBLOCK=1 | skip port or IP blocking tests |
| PKTWS_EXTRA_POST | additional nfqws/dvtws/winws parameters added after the main strategy |
| PKTWS_EXTRA_POST_1 .. PKTWS_EXTRA_POST_9 | separate additional parameters that contain spaces |
| PKTWS_EXTRA_PRE | additional nfqws/dvtws/winws parameters added before the main strategy |
| PKTWS_EXTRA_PRE_1 .. PKTWS_EXTRA_PRE_9 | separate additional parameters that contain spaces |
| DNSCHECK_DNS | space-separated list of third-party DNS servers for DNS spoofing tests |
| DNSCHECK_DOM | space-separated list of domains for DNS spoofing tests |
| SECURE_DNS=0\|1 | force enable or disable DoH |
| DOH_SERVERS | space-separated list of DoH URLs for automatic selection of a working server |
| DOH_SERVER | specific DoH URL (disables auto-selection) |
| UNBLOCKED_DOM | an unblocked domain used for IP block tests |
| SIMULATE=1 | enable simulation mode for debugging script logic. Disables real curl requests and replaces them with random results. |
| SIM_SUCCESS_RATE=\<percent\> | simulation success probability as a percentage |
## Why it won't open
+86 -51
View File
@@ -81,6 +81,7 @@
- [standard rawsend](#standard-rawsend)
- [Диссекция и реконструкция](#диссекция-и-реконструкция)
- [dissect](#dissect)
- [dissect_hdr](#dissect_hdr)
- [reconstruct_dissect](#reconstruct_dissect)
- [reconstruct_hdr](#reconstruct_hdr)
- [csum_fix](#csum_fix)
@@ -326,6 +327,7 @@ zapret2 является пакетным манипулятором, основ
Если система не удовлетворяет указанным требованиям - возможна самостоятельная "доприкрутка" к системе.
macOS не поддерживается по причине отсутствия подходящего средства перехвата и управления пакетами. Стандартное для BSD средство ipdivert было убрано из ядра производителем.
Политика apple - все закрывать, ужесточать, вырезать низкоуровневые интерфейсы, вводить требования подписи. Из-за этого реализация собственного подходящего средства перехвата пакетов крайне затруднена.
# Схема обработки трафика
@@ -754,8 +756,8 @@ MULTI-STRATEGY:
LUA PACKET PASS MODE:
--payload=type[,type] ; внутрипрофильный фильтр : фильтр пейлоада для последующих инстансов внутри профиля
--out-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>] ; внутрипрофильный фильтр : диапазон счетчиков conntrack для последующих инстансов внутри профиля - исходящее направление
--in-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>] ; внутрипрофильный фильтр : диапазон счетчиков conntrack для последующих инстансов внутри профиля - входящее направление
--out-range=<mode>[<int>][(-|<)<mode>[<int>]] ; внутрипрофильный фильтр : диапазон счетчиков conntrack для последующих инстансов внутри профиля - исходящее направление
--in-range=<mode>[<int>][(-|<)<mode>[<int>]] ; внутрипрофильный фильтр : диапазон счетчиков conntrack для последующих инстансов внутри профиля - входящее направление
LUA DESYNC ACTION:
--lua-desync=<function>[:param1=val1[:param2=val2]] ; при обработке профиля вызвать LUA инстанс с указанными параметрами, если соблюдены условия внутрипрофильных фильтров
@@ -770,7 +772,9 @@ LUA DESYNC ACTION:
--bind-fix4 ; лечение проблемы ухода генерированных пакетов на Linux с неверного интерфейса при использовании PBR (ipv4)
--bind-fix6 ; аналогично для ipv6
--fwmark=<int|0xHEX> ; бит в mark для предотвращения зацикливания. default = 0x40000000
--filter-mark=mark[/mask] ; фильтр профиля : фильтрация по mark пакета. mark и mask могут быть десятичные или 0xHEX
--filter-ssid=ssid1[,ssid2,ssid3,...] ; фильтр профиля : имя wifi сети
--filter-ssid-neg=[0|1] ; фильтр профиля : инверсия фильтра wifi сети
```
Специфические параметры dvtws2 :
@@ -790,7 +794,7 @@ LUA DESYNC ACTION:
--wf-tcp-out=[~]port1[-port2] ; WinDivert конструктор : tcp порты или диапазоны портов для перехвата по исходящему направлению. список через запятую
--wf-udp-in=[~]port1[-port2] ; WinDivert конструктор : udp порты или диапазоны портов для перехвата по входящему направлению. список через запятую
--wf-udp-out=[~]port1[-port2] ; WinDivert конструктор : udp порты или диапазоны портов для перехвата по исходящему направлению. список через запятую
--wf-tcp-empty=[~]port1[-port2] ; WinDivert конструктор : перехватывать пустые tcp пакеты ACK. по умолчанию - нет.
--wf-tcp-empty=[0|1] ; WinDivert конструктор : перехватывать пустые tcp пакеты ACK. по умолчанию - нет.
--wf-icmp-in=type[:code] ; WinDivert конструктор : icmp type и code по входящему направлению. если code не задан - любой code. список через запятую
--wf-icmp-out=type[:code] ; WinDivert конструктор : icmp type и code по исходящему направлению. если code не задан - любой code. список через запятую
--wf-ipp-in=type[:code] ; WinDivert конструктор : номера raw ip протоколов по входящему направлению. список через запятую
@@ -803,7 +807,9 @@ LUA DESYNC ACTION:
--wf-save=<filename> ; сохранить полный итоговый WinDivert фильтр в файл
LOGICAL NETWORK FILTER:
--ssid-filter=ssid1[,ssid2,ssid3,...] ; список сетей wifi, при наличии подключения к которым перехват включается, а иначе не включается.
--ssid-filter-neg=[0|1] ; инверсия фильтра wifi сетей
--nlm-filter=net1[,net2,net3,...] ; список сетей Network List Manager, при наличии подключения к которым перехват включается, а иначе не включается.
--nlm-filter-neg=[0|1] ; инверсия NLM фильтра
--nlm-list[=all] ; вывести список подключенных NLM сетей. all - список всех NLM сетей
```
@@ -991,6 +997,8 @@ nfqws2 <глобальные_параметры>
В остальных системах нужно копаться в powershell или лезть в реестр, чтобы раскидать подключения по нужным GUID,
если вдруг они раскидались системой неправильно. Но можно и не бороться, а просто внести список GUID, назначенных системой автоматически.
В обеих системах есть параметры, позволяющие инвертировать проверку.
## Серверный режим
Некоторые виды воздействий можно осуществлять не только со стороны клиента, но и сервера.
@@ -1158,7 +1166,7 @@ Lua код вызывается в 3 местах.
`%var` подставляет значение переменной `desync.var` или `var`, если первая отсутствует.
`#var` подставляет длину переменной `desync.var` или `var`, если первая отсутствует.
Двоеточия и знаки `%`, `#` в начале могут быть эскейпнуты через `\`.
3. При обработке таймеров
3. При обработке [таймеров](#таймеры)
И `--lua-init`, и `--lua-desync` может быть несколько. Выполнение производится строго в порядке указания.
@@ -1176,13 +1184,17 @@ Lua код вызывается в 3 местах.
Lua код выполняется с ограниченными правами, задуманное может не получиться или не работать на разных ОС в разных условиях.
Загрузка blob происходит до вхождения в песочницу, поэтому шансов на успех больше.
Поскольку имя блоба - это имя переменной Lua, нельзя назвать блоб как попало - должна соблюдаться конвенция имен Lua.
Например, нельзя начинать переменную с цифры, использовать не буквенно-цифровые символы, кроме `_`, или зарезервированные слова.
Имена чувствительны к регистру букв.
### Внутрипрофильные фильтры
Они бывают трех видов - `--payload`, `--in-range`, `--out-range`.
Значения фильтров действуют с момента их указания до следующего переопределения.
- `--payload=type1[,type2][,type3]...` принимает список известных пейлоадов через запятую, "all" или "known". [Список известных пейлоадов](#распознавание-протоколов). По умолчанию `--payload=all`.
- `--(in-range|out-range)=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>]` задает диапазоны счетчиков conntrack по входящему и исходящему направлениям. По умолчанию `--in-range=x`, `--out-range=a`.
- `--(in-range|out-range)=<mode>[<int>][(-|<)<mode>[<int>]]` задает диапазоны счетчиков conntrack по входящему и исходящему направлениям. По умолчанию `--in-range=x`, `--out-range=a`.
Диапазоны задаются в формах : `mX-mY`, `mX<mY`, `-mY`, `<mY`, `mX-`, где m - режим счетчика, X - нижнее значение, Y - верхнее значение.
Режимы `x` и `a` задаются без диапазона и значения счетчика - единственной буквой. Знак `-` означает включающую верхнюю границу, `<` - исключающую.
@@ -2297,6 +2309,20 @@ function dissect(raw_ip)
Это та же самая операция, что происходит автоматически до вызова desync функций по профилю.
Они получают уже готовый диссект.
#### dissect_hdr
```
function dissect_tcphdr(tcp)
function dissect_udphdr(udp)
function dissect_icmphdr(icmp)
function dissect_iphdr(ip)
function dissect_ip6hdr(ip6)
```
Диссекция отдельных raw заголовков.
Возвращает таблицу - диссект заголовка.
В tcp options обрезаются все опции 0x00 с конца (padding).
#### reconstruct_dissect
```
@@ -2335,6 +2361,7 @@ function reconstruct_ip6hdr(ip6 [,reconstruct_opts])
- реконструкция ip6 задействует reconstruct_opts. из них берется ip6_preserve_next и ip6_last_proto.
- чексумма ip header считается автоматически, поскольку ни от чего больше не зависит
- чексуммы tcp, udp и icmp не считаются, поскольку зависят от других компонент
- tcp options дополняются 0x00 до кратности 4 байтам
#### csum_fix
@@ -2647,6 +2674,8 @@ function luaexec(ctx, desync)
Пример : `--lua-desync=luaexec:code="desync.rnd=brandom(math.random(5,10))"`
Функция может возвращать вердикт через return.
### pass
```
@@ -2755,10 +2784,10 @@ function array_field_search(a, f, v)
function in_list(s, v)
```
### find_next_line
Включена ли строка v в список строк через запятую s. Например, `abc` включено в список `xyz,abc,12345`.
### find_next_line
```
function find_next_line(s, pos)
```
@@ -3612,7 +3641,7 @@ tcp_ts_up дублирует старое поведение - двигает ti
| Поле | Описание |
| :-------------- | :--------------------------------------------------------------------------------------------------------------- |
| ipfrag | имя функции фрагментатора. если нет, использовать ipfrag2. фрагментатор возвращает массив диссектов - фрагментов |
| ipfrag | имя функции фрагментатора. если имя пустое, использовать ipfrag2. фрагментатор возвращает массив диссектов - фрагментов |
| ipfrag_disorder | отправить фрагменты в обратном порядке |
| ipfrag_pos_udp | (фрагментатор ipfrag2) позиция фрагментации udp. должна быть кратна 8, по умолчанию 8 |
| ipfrag_pos_tcp | (фрагментатор ipfrag2) позиция фрагментации tcp. должна быть кратна 8, по умолчанию 32 |
@@ -3796,9 +3825,9 @@ function dis_timer_name(dis)
function desync_timer_name(desync)
```
Конструирует имя таймера, включающее в себя результат dis_timer_name + номер пакета в conntrack по прямому направлению.
Конструирует имя таймера, включающее в себя имя инстанса, результат dis_timer_name, номер пакета в conntrack по прямому направлению.
Если track отсутствует, добавляются случайные символы.
Имя таймера можно считать уникальным на desync, его можно использовать как oneshot timer name.
Имя таймера можно считать уникальным на desync+instance, его можно использовать как oneshot timer name.
## Оркестрация
@@ -4368,7 +4397,7 @@ function hostfakesplit(ctx, desync)
- arg: host - шаблон для [генерации фейкового хоста](#genhost) - random.template
- arg: midhost - [маркер](#маркеры) для дополнительного разреза сегмента с реальным хостом
- arg: disorder_after - [маркер](#маркеры) для дополнительного разреза заключительной реальной части и отправки сегментов в обратном порядке
- arg: nofake, nofake2 - отказ от отсылки отдельных фейков
- arg: nofake1, nofake2 - отказ от отсылки отдельных фейков
- arg: blob - заменить текущий пейлоад на указанный [blob](#передача-блобов)
- arg: optional - отказ от операции, если blob задан, но отсутствует
- arg: nodrop - отказ от вынесения VERDICT_DROP
@@ -4611,7 +4640,7 @@ function automate_failure_check(desync, hrec, crec)
- arg: success_detector - имя функции детектора удач. если не задано, используется standard_success_detector.
- arg: failure_detector - имя функции детектора неудач. если не задано, используется standard_failure_detector.
- arg: fails - целевое значение счетчика неудач. По умолчанию - 3.
- arg: maxtime - максимальное время в секундах между неудачами, после которого счетчик сбрасывается. По умолчанию - 60 сек.
- arg: time - максимальное время в секундах между неудачами, после которого счетчик сбрасывается. По умолчанию - 60 сек.
Функция выполняет ведение счетчика неудач, вызывая детекторы удачи и неудачи. Возвращает true, если счетчик достиг целевого значения. При этом счетчик сбрасывается автоматически.
@@ -4789,7 +4818,7 @@ function cond_payload_str(desync)
#### cond_tcp_has_ts
```
function cond_tcp_ts(desync)
function cond_tcp_has_ts(desync)
```
Возвращает true, если диссект является tcp и присутствует timestamp tcp option.
@@ -4954,6 +4983,7 @@ blockcheck2 способен определить подменяется ли DN
- standard - использовать алгоритм теста, позволяющий исключить тестирование неактуальных по его мнению стратегий в зависимости от успешности предыдущих или по каким-то другим критериям. В случае множественных попыток тестирование не прекращается при неудаче. Процент успеха и ошибки curl тоже могут служить полезной информации при анализе ситуации.
- quick - то же самое, что и standard, но при использовании множественных попыток прекращение тестирования после первой неудачи
- veryquick - то же самое, что и quick, а также прекращение перебора оставшихся вариантов внутри семейства стратегий (доп. TCP foolings, autottl-дельты, TTL, а где применимо - позиции multisplit/fakedsplit и сами split-механизмы), как только для текущей цели уже нашлась хоть одна успешная комбинация, вместо перебора всех вариантов до конца. Затрагивает только семейства на основе fake (25-fake.sh, 30-faked.sh, 35-hostfake.sh, 50-fake-multi.sh, 55-fake-faked.sh, 60-fake-hostfake.sh); может заметно ускорить тестирование этих семейств, если успешная комбинация находится рано, ценой того, что не все стратегии, которые тоже могли бы оказаться успешными, будут найдены (успех здесь значит, что curl прошел - это ничего не говорит о том, как эта стратегия поведет себя в реальном браузере)
- force - тестировать максимально много без учета результата предыдущих тестов
### Поддерживаемые протоколы
@@ -5087,6 +5117,8 @@ MULTIDISORDER=multidisorder_legacy - заменить multidisorder на вар
NOTEST_BASIC_HTTP=1 - отмена тестов 10-http-basic.sh
NOTEST_MISC_HTTP=1 - отмена тестов http 15-misc.sh
NOTEST_MISC_HTTPS=1 - отмена тестов https 15-misc.sh
NOTEST_OOB_HTTP=1 - отмена тестов http 17-oob.sh
NOTEST_OOB_HTTPS=1 - отмена тестов https 17-oob.sh
NOTEST_MULTI_HTTP=1 - отмена тестов http 20-multi.sh
NOTEST_MULTI_HTTPS=1 - отмена тестов https 20-multi.sh
NOTEST_SEQOVL_HTTP=1 - отмена тестов http 23-seqovl.sh
@@ -5099,6 +5131,7 @@ NOTEST_FAKED_HTTP=1 - отмена тестов http 25-faked.sh
NOTEST_FAKED_HTTPS=1 - отмена тестов https 25-faked.sh
NOTEST_HOSTFAKE_HTTP=1 - отмена тестов http 35-hostfake.sh
NOTEST_HOSTFAKE_HTTPS=1 - отмена тестов https 35-hostfake.sh
HOSTFAKE=host - указание конкретного имени хоста для hostfakesplit
NOTEST_FAKE_MULTI_HTTP=1 - отмена тестов http 50-fake-multi.sh
NOTEST_FAKE_MULTI_HTTPS=1 - отмена тестов https 50-fake-multi.sh
NOTEST_FAKE_FAKED_HTTP=1 - отмена тестов http 55-fake-faked.sh
@@ -5112,53 +5145,55 @@ NOTEST_QUIC=1 - отмена тестов 90-quic.sh
Простой тестировщик по списку стратегий из файлов. Стратегии должны быть на отдельных строчках, переносы строки не допускаются. Используются отдельные списки для протоколов - `list_http.txt`, `list_https_tls12.txt`, `list_https_tls13.txt`, `list_quic.sh`. В файлах поддерживаются комментарии, начинающиеся на `#`.
При записи параметров следует учитывать, что они будут интерпретироваться как параметры shell. Специальные символы нужно экранировать по правилам shell.
Если вы оставите "<" без кавычек на всем параметре или возьмете в кавычки параметр `--luaexec=code=print("abc")`, будет ошибка. Если в lua коде используются строки - их стоит заключать в одинарные кавычки, а вокруг параметра - двойные.
При записи параметров следует учитывать, что сначала они будут опробованы как параметры shell. Специальные символы (`<`, `(`, `'` и подобные) являются синтаксисом shell и обычно требуют экранирования - например, раньше "<" без кавычек на всем параметре или неверно взятый в кавычки параметр `--luaexec=code=print("abc")` приводили к ошибке. Теперь, если строка не проходит таким образом, она автоматически пробуется заново как обычный, неразбираемый список аргументов (разбивается только по пробелам, ничего не переинтерпретируется) - поэтому стратегии с такими символами работают вне зависимости от того, экранировали вы их сами или нет, и строку можно скопировать прямо из вывода `blockcheck2` без правок. Ручное экранирование (одинарные кавычки вокруг lua-строк, весь параметр в двойных) по-прежнему поддерживается и имеет приоритет, если срабатывает - для тех, кому нужно shell-раскрытие внутри строки списка.
blockcheck2 будет выдавать параметры стратегий без экранирования.
Самый правильный способ применения - скопировать в свою поддиректорию внутри `blockcheck2.d` и заполнить txt файлы с тестами. Выбрать свое имя теста в диалоге.
## Summary
В конце теста выдаются все успешные стратегии по каждому домену и каждой версии ip протокола. Если тестировалось более одного домена, дополнительно выдается пересечение успешных стратегий. Однако, ему можно полностью доверять только при использовании SCANLEVEL=force. Иначе возможна ситуация, когда для первого домена не стали тестировать стратегии, которые подошли бы для последующих.
В конце теста выдаются все успешные стратегии по каждому домену и каждой версии ip протокола. Если тестировалось более одного домена, дополнительно выдается пересечение успешных стратегий (те, что сработали для всех, блок COMMON), а также список COVERAGE - каждая успешная стратегия и число доменов, для которых она сработала, по убыванию - полезно, когда цели в DOMAINS во многом независимы друг от друга и ни одна стратегия не обязана подходить для всех сразу. Однако, и COMMON, и счетчики в COVERAGE можно полностью доверять только при использовании SCANLEVEL=force. Иначе возможна ситуация, когда для первого домена не стали тестировать стратегии, которые подошли бы для последующих.
## Shell переменные
```
CURL - замена программы curl
CURL_MAX_TIME - время таймаута curl в секундах
CURL_MAX_TIME_QUIC - время таймаута curl для quic. если не задано, используется значение CURL_MAX_TIME
CURL_MAX_TIME_DOH - время таймаута curl для DoH серверов
CURL_CMD=1 - показывать команды curl
CURL_OPT - дополнительные параметры curl. `-k` - игнор сертификатов. `-v` - подробный вывод протокола
CURL_HTTPS_GET=1 - использовать метод GET вместо HEAD для https
DOMAINS - список тестируемых доменов или доменов с URI через пробел
TEST - имя теста
IPVS=4|6|46 - тестируемые версии ip протокола
ENABLE_HTTP=0|1 - включить тест plain http
ENABLE_HTTPS_TLS12=0|1 - включить тест https TLS 1.2
ENABLE_HTTPS_TLS13=0|1 - включить тест https TLS 1.3
ENABLE_HTTP3=0|1 - включить тест QUIC
REPEATS - количество попыток тестирования
PARALLEL=0|1 - включить параллельные попытки. может обидеть сайт из-за долбежки и привести к неверному результату
SCANLEVEL=quick|standard|force - уровень сканирования
BATCH=1 - пакетный режим без вопросов и ожидания ввода в консоли
HTTP_PORT, HTTPS_PORT, QUIC_PORT - номера портов для соответствующих протоколов
SKIP_DNSCHECK=1 - отказ от проверки DNS
SKIP_IPBLOCK=1 - отказ от тестов блокировки по порту или IP
PKTWS_EXTRA_POST - дополнительные параметры nfqws/dvtws/winws, указываемые после основной стратегии
PKTWS_EXTRA_POST_1 .. PKTWS_EXTRA_POST_9 - отдельно дополнительные параметры, содержащие пробелы
PKTWS_EXTRA_PRE - дополнительные параметры для nfqws/dvtws/winws, указываемые перед основной стратегией
PKTWS_EXTRA_PRE_1 .. PKTWS_EXTRA_PRE_9 - отдельно дополнительные параметры, содержащие пробелы
DNSCHECK_DNS - список сторонних DNS через пробел для тестирования подмены DNS
DNSCHECK_DOM - список доменов через пробел для тестирования подмены DNS
SECURE_DNS=0|1 - принудительно выключить или включить DoH
DOH_SERVERS - список URL DoH через пробел для автоматического выбора работающего сервера
DOH_SERVER - конкретный DoH URL, отказ от поиска
UNBLOCKED_DOM - незаблокированный домен, который используется для тестов IP block
SIMULATE=1 - включить режим симуляции для отладки логики скрипта. отключаются реальные запросы через curl, заменяются рандомным результатом.
SIM_SUCCESS_RATE=<percent> - вероятность успеха симуляции в процентах
```
| Переменная | Назначение |
| :--------- | :--------- |
| CURL| замена программы curl |
| CURL_MAX_TIME | время таймаута curl в секундах |
| CURL_MAX_TIME_QUIC | время таймаута curl для quic. если не задано, используется значение CURL_MAX_TIME |
| CURL_MAX_TIME_DOH | время таймаута curl для DoH серверов |
| CURL_CMD=1 | показывать команды curl |
| CURL_OPT | дополнительные параметры curl. `-k` - игнор сертификатов. `-v` - подробный вывод протокола |
| CURL_HTTPS_GET=1 | использовать метод GET вместо HEAD для https |
| CURL_PAD | добавлять к каждому запросу curl мусорные заголовки длиной не более PAD_MAX_HEADER байт (4000 по умолчанию) на указанное число байт. Учитывается полная длина вместе с названием хедера, полем данных и EOL. Полезно для проверки блока в стиле "16 кб" на ресурсе без своей большой страницы для скачивания, поскольку этот блок срабатывает по суммарному числу переданных байт, а не по тому, сколько из них было настоящим ответом. Независимо от CURL_HTTPS_GET, используйте то, что подходит для конкретной цели |
| PAD_MAX_HEADER | максимальная длина в байтах поля данных одного хедера для паддинга CURL_PAD. длина может быть превышена на не болеее, чем 14 символов, для последнего хедера, если последний хедер вместе с названием и EOL мог бы получиться менее 14 символов |
| PACE | минимальное время выполнения одного curl запроса в мсек. позволяет притормозить долбежку, чтобы не обидеть сайт |
| DOMAINS | список тестируемых доменов или доменов с URI через пробел |
| TEST | имя теста |
| IPVS=4\|6\|46 | тестируемые версии ip протокола |
| ENABLE_HTTP=0\|1 | включить тест plain http |
| ENABLE_HTTPS_TLS12=0\|1 | включить тест https TLS 1.2 |
| ENABLE_HTTPS_TLS13=0\|1 | включить тест https TLS 1.3 |
| ENABLE_HTTP3=0\|1 | включить тест QUIC |
| REPEATS | количество попыток тестирования |
| PARALLEL=0\|1 | включить параллельные попытки. может обидеть сайт из-за долбежки и привести к неверному результату |
| SCANLEVEL=quick\|standard\|force | уровень сканирования |
| BATCH=1 | пакетный режим без вопросов и ожидания ввода в консоли |
| HTTP_PORT, HTTPS_PORT, QUIC_PORT | номера портов для соответствующих протоколов |
| SKIP_DNSCHECK=1 | отказ от проверки DNS |
| SKIP_IPBLOCK=1 | отказ от тестов блокировки по порту или IP |
| PKTWS_EXTRA_POST | дополнительные параметры nfqws/dvtws/winws, указываемые после основной стратегии |
| PKTWS_EXTRA_POST_1 .. PKTWS_EXTRA_POST_9 | отдельно дополнительные параметры, содержащие пробелы |
| PKTWS_EXTRA_PRE | дополнительные параметры для nfqws/dvtws/winws, указываемые перед основной стратегией |
| PKTWS_EXTRA_PRE_1 .. PKTWS_EXTRA_PRE_9 | отдельно дополнительные параметры, содержащие пробелы |
| DNSCHECK_DNS | список сторонних DNS через пробел для тестирования подмены DNS |
| DNSCHECK_DOM | список доменов через пробел для тестирования подмены DNS |
| SECURE_DNS=0\|1 | принудительно выключить или включить DoH |
| DOH_SERVERS | список URL DoH через пробел для автоматического выбора работающего сервера |
| DOH_SERVER | конкретный DoH URL, отказ от поиска |
| UNBLOCKED_DOM | незаблокированный домен, который используется для тестов IP block |
| SIMULATE=1 | включить режим симуляции для отладки логики скрипта. отключаются реальные запросы через curl, заменяются рандомным результатом. |
| SIM_SUCCESS_RATE=\<percent\> | вероятность успеха симуляции в процентах |
## Почему не открывается
+13 -2
View File
@@ -52,11 +52,16 @@ Lua код получает от C кода структурированное
Имеется библиотека хелперов, написанных на Lua, а так же готовая библиотека программ атаки на DPI (стратегий), реализующая функции *nfqws1* в расширенном варианте
и с большей гибкостью.
Вы всегда сможете взять и дописать что-то свое. В этом и есть смысл, чтобы борьбой с DPI смог заняться любой, кто разбирается в пакетах.
Вы всегда сможете взять и дописать что-то свое. От мелкой частной кустомизации, которую бессмысленно зашивать намертво в C код, до полноценной desync функции.
В этом и есть смысл, чтобы борьбой с DPI смог заняться любой, кто разбирается в пакетах.
Мог "потыкать" его, проверить свои идеи. А потом поделиться с друзьями своим решением "одного клика".
zapret2 - инструмент для таких энтузиастов. Но это не готовое решение для чайников. Проект не ставит себе целью сделать все простым для всех.
Автор считает, что это невозможно в принципе по обьективным причинам.
Борьбой с DPI возможности zapret2 не ограничиваются. По сути это мультиплатформенный пакетный программируемый манипулятор.
Вы можете использовать его для генерации произвольного IPv4/IPv6 трафика, обфускации или шифрования имеющегося, построения нестандартных протоколов обмена через IP сети,
реализации собственных firewall или DPI систем.
## С чего начать
@@ -425,7 +430,13 @@ nfqws2 \
### Не только лишь автономный обман DPI
Рабочий тестовый пример icmp обфускатора udp от винды к серверу на vps.
Для теста используем wireguard. Ничего в конфигах менять не надо - wireguard будет думать, что он работает по udp, но на самом деле он преобразуется в пинги icmp, которые могут проходить NAT. Размер пакетов не изменяется, потому проблемы MTU нет.
Для теста используем wireguard. Ничего в конфигах менять не надо - wireguard будет думать, что он работает по udp, но на самом деле он преобразуется в пинги icmp, которые могут проходить NAT.
Если NAT не меняет поле Identifier в icmp (типично для Linux NAT), можно использовать легкий stateless вариант, когда размер пакета не меняется, и он с гарантией влезает в MTU.
Провайдерский CGNAT меняет Identifier. Для его поддержки существует параметр udp2icmp `cgnat`. Клиент увеличит размеры исходящих пакетов на 2 байта, поэтому нужно предварительно
уменьшить MTU интерфейса wireguard на 2, если он выбран "под завязку". В режиме cgnat серверный конец будет вести базу измененных идентификаторов и сохранять их при ответе.
Обычно на CGNAT время жизни icmp identifier небольшое - 30 секунд, но может быть еще меньше, и освежаться может только исходящими пакетами. Поэтому нужен частый keepalive со стороны клиента.
Если этого не сделать, будет обрыв до момента, когда wireguard клиент что-либо пошлет. Некоторые операторы троттлят icmp, поэтому могут быть проблемы со скоростью.
Будем загонять исходящие с клиента в icmp type 8 (echo request) code 199 , исходящие с сервера в icmp type 0 (echo reply) code 199.
Код у обоих концов делаем одинаковый, иначе NAT не соотнесет. Без NAT можно коды делать разными для клиента и сервера.
+9 -7
View File
@@ -1,11 +1,10 @@
CC ?= cc
OPTIMIZE ?= -Os
CFLAGS_PIC = -fPIC
CFLAGS += -std=gnu99 $(OPTIMIZE) -flto=auto $(CFLAGS_PIC)
CFLAGS += -std=gnu99 $(OPTIMIZE)
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_WIN = -static
LDFLAGS_PIE = -pie
LDFLAGS += $(LDFLAGS_PIE)
LIBS =
LIBS_WIN = -lws2_32
@@ -14,24 +13,27 @@ SRC_FILES = ip2net.c qsort.c
all: ip2net
ip2net: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
systemd: ip2net
android: ip2net
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
mac: $(SRC_FILES)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2neta $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2netx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o ip2neta $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
$(CC) $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o ip2netx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
strip ip2neta ip2netx
lipo -create -output ip2net ip2netx ip2neta
rm -f ip2netx ip2neta
win: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o ip2net $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o ip2net.exe $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
win-arm64-cross:
clang -s $(CFLAGS) $(CFLAGS_WIN) --sysroot=/clangarm64 --target=aarch64-w64-mingw32 -B/clang64/bin -rtlib=compiler-rt -o mdig.exe $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
clean:
rm -f ip2net *.o
+3 -3
View File
@@ -95,7 +95,7 @@ function send(ctx, desync)
apply_fooling(desync, dis)
apply_ip_id(desync, dis, nil, "none")
if desync.arg.delay then
local tname = "send_"..desync_timer_name(desync)
local tname = desync_timer_name(desync)
timer_set(tname, "send_timer_delayed", tonumber(desync.arg.delay), true, {dis = dis, opts = desync_opts(desync)})
else
-- it uses rawsend, reconstruct and ipfrag options
@@ -589,7 +589,7 @@ end
-- algorithm is not 100% the same as in nfqws1. multi-segment queries can produce different segment ordering.
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
-- arg : pos=<postmarker list> . position marker list. example : "1,host,midsld+1,-10"
-- arg : seqovl=N . decrease seq number of the second segment in the original order by N and fill N bytes with pattern (default - all zero). N must be less than the first split pos.
-- arg : seqovl=<posmarker> . decrease seq number of the second segment in the original order by N and fill N bytes with pattern (default - all zero). N is resolved marker position and must be less than the first split pos.
-- arg : seqovl_pattern=<blob> . override pattern
-- arg : blob=<blob> - use this data instead of reasm_data
-- arg : optional - skip if blob is absent. use zero pattern if seqovl_pattern blob is absent
@@ -643,7 +643,7 @@ end
-- nfqws1 : "--dpi-desync=multidisorder". segment ordering is the same as in nfqws1
-- standard args : direction, payload, fooling, ip_id, rawsend, reconstruct, ipfrag
-- arg : pos=<postmarker list> . position marker list. example : "1,host,midsld+1,-10"
-- arg : seqovl=N . decrease seq number of the second segment in the original order by N and fill N bytes with pattern (default - all zero). N must be less than the first split pos.
-- arg : seqovl=<posmarker> . decrease seq number of the second segment in the original order by N and fill N bytes with pattern (default - all zero). N is resolved marker position and must be less than the first split pos.
-- arg : seqovl_pattern=<blob> . override pattern
-- arg : optional - use zero pattern if seqovl_pattern blob is absent
function multidisorder_legacy(ctx, desync)
+5 -5
View File
@@ -301,9 +301,10 @@ end
-- each orchestrated instance must have strategy=N arg, where N starts from 1 and increment without gaps
-- if 'final' arg is present in an orchestrated instance it stops rotation
-- arg: fails=N - failture count threshold. default is 3
-- arg: time=<sec> - if last failure happened earlier than `maxtime` seconds ago - reset failure counter. default is 60.
-- arg: time=<sec> - if last failure happened earlier than `time` seconds ago - reset failure counter. default is 60.
-- arg: success_detector - success detector function name
-- arg: failure_detector - failure detector function name
-- arg: key - a string - table name inside autostate table. to allow multiple orchestrator instances to use single host storage
-- arg: hostkey - hostkey generator function name
-- args for failure detector - see standard_failure_detector or your own detector
-- args for success detector - see standard_success_detector or your own detector
@@ -409,7 +410,7 @@ end
function cond_tcp_has_ts(desync)
return desync.dis.tcp and find_tcp_option(desync.dis.tcp.options, TCP_KIND_TS)
end
-- exec lua code in "code" arg and return it's result
-- exec lua code in "cond_code" arg and return it's result
function cond_lua(desync)
if not desync.arg.cond_code then
error("cond_lua: no 'cond_code' parameter")
@@ -420,7 +421,6 @@ function cond_lua(desync)
_G[fname], err = load(desync.arg.cond_code, fname)
if not _G[fname] then
error(err)
return
end
end
-- allow dynamic cond_code to access desync
@@ -463,8 +463,8 @@ function condition(ctx, desync)
return replay_execution_plan(desync)
end
-- execute further desync instances.
-- each instance may have "cond" and "cond_neg" args.
-- "cond" - condition function. "neg" - invert condition function result
-- each instance must have "cond" arg and may optionally have "cond_neg" arg.
-- "cond" - condition function. "cond_neg" - invert condition function result
-- arg: instances - how many instances execute conditionally. all if not defined
function per_instance_condition(ctx, desync)
orchestrate(ctx, desync)
+136 -110
View File
@@ -24,15 +24,16 @@ function luaexec(ctx, desync)
_G[fname], err = load(desync.arg.code, fname)
if not _G[fname] then
error(err)
return
end
end
-- allow dynamic code to access desync
_G.desync = desync
local res, err = pcall(_G[fname])
local res, ret = pcall(_G[fname])
_G.desync = nil
if not res then
error(err);
error(ret)
else
return ret or VERDICT_PASS -- older nfqws2 failed if desync function returned explicit nil
end
end
@@ -550,7 +551,7 @@ function blob(desync, name, def)
-- use global var if no field in dissect table
blob = _G[name]
if not blob then
error("blob '"..name.."' unavailable")
error("blob '"..name.."' unavailable")
end
end
blob = tostring(blob)
@@ -808,7 +809,7 @@ function dis_timer_name(dis)
return table.concat({ntop(dis_ipsrc(dis)),"->",ntop(dis_ipdst(dis)),"_",dis_l4_name(dis),"_",dis_l4_ports(dis)})
end
function desync_timer_name(desync)
local name = dis_timer_name(desync.dis)
local name = desync.func_instance.."_"..dis_timer_name(desync.dis)
if desync.track then
name = name.."_n"..desync.track.pos.direct.pcounter
else
@@ -917,7 +918,7 @@ end
-- tcp_seq=N - add N to tcp.th_seq
-- tcp_ack=N - add N to tcp.th_ack
-- tcp_ts=N - add N to timestamp value
-- tcp_md5[=hex] - add MD5 header with optional 16-byte data. all zero by default.
-- tcp_md5[=hex] - add MD5 header with optional 16-byte data. random by default.
-- tcp_flags_set=<list> - set tcp flags in comma separated list
-- tcp_flags_unset=<list> - unset tcp flags in comma separated list
-- tcp_ts_up - move timestamp tcp option to the top if it's present. this allows linux not to accept badack segments without badseq. this is very strange discovery but it works.
@@ -1289,7 +1290,7 @@ function rawsend_payload_segmented(desync, payload, seq, options)
end
-- check if desync.outgoing comply with arg.dir or def if it's not present or "out" of they are not present both. dir can be "in","out","any"
-- check if desync.outgoing complies with arg.dir or def if it's not present or "out" if they are not present both. dir can be "in","out","any"
function direction_check(desync, def)
local dir = desync.arg.dir or def or "out"
return desync.outgoing and dir~="in" or not desync.outgoing and dir~="out"
@@ -1313,7 +1314,7 @@ function payload_match_filter(l7payload, l7payload_filter, def)
local pl = neg and string.sub(argpl,2) or argpl
return neg ~= (in_list(pl, "all") or in_list(pl, l7payload) or in_list(pl, "known") and l7payload~="unknown" and l7payload~="empty")
end
-- check if desync payload type comply with payload type list in arg.payload
-- check if desync payload type complies with payload type list in arg.payload
-- if arg.payload is not present - check for known payload - not empty and not unknown (nfqws1 behavior without "--desync-any-protocol" option)
-- if arg.payload is prefixed with '~' - it means negation
function payload_check(desync, def)
@@ -1475,7 +1476,7 @@ end
-- option : ipfrag_pos_udp - udp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 8
-- option : ipfrag_pos_tcp - tcp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 32
-- option : ipfrag_pos_icmp - icmp frag position. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 8
-- option : ipfrag_pos - icmp frag position for other L4. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 32
-- option : ipfrag_pos - frag position for other L4. ipv4 : starting from L4 header. ipv6: starting from fragmentable part. must be multiple of 8. default 32
-- option : ipfrag_next - next protocol field in ipv6 fragment extenstion header of the second fragment. same as first by default.
function ipfrag2(dis, ipfrag_options)
local function frag_idx(exthdr)
@@ -1601,13 +1602,17 @@ function tls_client_hello_mod(tls, options)
DLOG("tls_client_hello_mod: could not dissect tls")
return
end
if not tdis.handshake or not tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT] then
if not tdis.handshake or not tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT] or not tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT].dis then
DLOG("tls_client_hello_mod: handshake not dissected")
return
end
local idx_sni
if options.sni_snt or options.sni_del_ext or options.sni_del or options.sni_first or options.sni_last then
idx_sni = array_field_search(tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT].dis.ext, "type", TLS_EXT_SERVER_NAME)
if tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT].dis.ext then
idx_sni = array_field_search(tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT].dis.ext, "type", TLS_EXT_SERVER_NAME)
else
tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT].dis.ext={}
end
if not idx_sni then
DLOG("tls_client_hello_mod: no SNI extension. adding")
table.insert(tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT].dis.ext, 1, { type = TLS_EXT_SERVER_NAME, dis = { list = {} } } )
@@ -1858,7 +1863,7 @@ function dissect_url(url)
creds = string.sub(url,p1,pend-1)
p1 = pend+1
end
pstart,pend = string.find(url,"/",p1,true)
pstart,pend = string.find(url,"[/?#]",p1)
if pend then
if pend==pb then
uri = string.sub(url,pb)
@@ -2276,21 +2281,32 @@ function quic_tvb(data, offset)
return bitand(u32(data,offset),0x3FFFFFFF), 4
elseif size==3 then
if (offset+7)>#data then return end
-- only lua 5.3+ can handle this. others can't store 64-bit integers
return bitand(u32(data,offset),0x3FFFFFFF) * 0x100000000 + u32(data,offset+4), 8
-- only lua 5.3+ has 64-bit integer type. others can't store 64-bit integers. return raw string if the value cannot fit.
local msb = bitand(u32(data,offset),0x3FFFFFFF)
-- lua 5.3+ has math.type function, older lua and luajit - don't
if math.type==nil and msb>0x1FFFFF then
return string.sub(data,offset,offset+7), 8
else
return msb * 0x100000000 + u32(data,offset+4), 8
end
end
end
-- quic-style tvb reconstruct
function bquic_tvb(v)
if v<0x40 then
return bu8(v)
elseif v<0x4000 then
return bu16(v + 0x4000)
elseif v<0x40000000 then
return bu32(v + 0x80000000)
elseif v<0x4000000000000000 then
-- only lua 5.3+ can handle 64-bit int !
return bu32(divint(v, 0x100000000) + 0xC0000000) .. bu32(v % 0x100000000)
if type(v)=="string" then
-- raw string. do not do anything, just return it
return v
else
if v<0x40 then
return bu8(v)
elseif v<0x4000 then
return bu16(v + 0x4000)
elseif v<0x40000000 then
return bu32(v + 0x80000000)
elseif v<0x4000000000000000 then
-- only lua 5.3+ can handle 64-bit int !
return bu32(divint(v, 0x100000000) + 0xC0000000) .. bu32(v % 0x100000000)
end
end
end
@@ -2385,7 +2401,7 @@ function tls_dissect_ext(ext)
left, off = len16_header()
if not left then return end
dis.list = {}
while left>=1 do
while left>=4 do
len = u16(ext.data, off + 2)
if (len+4)>left then return end
dis.list[#dis.list+1] = { group = u16(ext.data, off) , kex = string.sub(ext.data, off+4, off+4+len-1) }
@@ -2414,7 +2430,7 @@ function tls_dissect_ext(ext)
off = off + size
left = left - size
len, size = quic_tvb(ext.data, off)
if not len then return end
if type(len)~="number" then return end
off = off + size
left = left - size
if len > left then return end
@@ -2431,93 +2447,102 @@ end
-- dissect client/server hello. leave 'data' as is for others
function tls_dissect_handshake(handshake, partialOK)
if is_tls_hello(handshake.data, 1, partialOK) then
local hlen = tls_handshake_len(handshake.data, 1)
if hlen > #handshake.data then
if not partialOK then return false end
hlen = #handshake.data
end
local typ = tls_handshake_type(handshake.data, 1)
handshake.dis = { type = typ , ver = u16(handshake.data, 5), name = tostring(TLS_HANDSHAKE_TYPE_NAMES[typ]) }
-- random
if hlen<36 then return partialOK end
handshake.dis.random = string.sub(handshake.data, 7, 38)
-- session_id
if hlen<39 then return partialOK end
local len = u8(handshake.data, 39)
local left = hlen-39-len
if left<0 then return partialOK end
handshake.dis.session_id = string.sub(handshake.data, 40, 40+len-1)
local off = 40+len
-- cipher suite(s)
if left<2 then return partialOK end
if handshake.dis.type==TLS_HANDSHAKE_TYPE_CLIENT then
-- client hello - array
len = u16(handshake.data, off)
if left<(2+len) then return partialOK end
handshake.dis.cipher_suites={}
for i=1,(len/2) do
handshake.dis.cipher_suites[i] = u16(handshake.data, off + i*2)
local function tls_dissect_handshake_(handshake, partialOK)
handshake.dis = nil
if is_tls_hello(handshake.data, 1, partialOK) then
local hlen = tls_handshake_len(handshake.data, 1)
if hlen > #handshake.data then
if not partialOK then return false end
hlen = #handshake.data
end
off = off + 2 + len
left = left - 2 - len
else
-- server hello - single
handshake.dis.cipher_suite = u16(handshake.data, off)
local typ = tls_handshake_type(handshake.data, 1)
handshake.dis = { type = typ , ver = u16(handshake.data, 5), name = tostring(TLS_HANDSHAKE_TYPE_NAMES[typ]) }
-- random
if hlen<38 then return partialOK end
handshake.dis.random = string.sub(handshake.data, 7, 38)
-- session_id
if hlen<39 then return partialOK end
local len = u8(handshake.data, 39)
local left = hlen-39-len
if left<0 then return partialOK end
handshake.dis.session_id = string.sub(handshake.data, 40, 40+len-1)
local off = 40+len
-- cipher suite(s)
if left<2 then return partialOK end
if handshake.dis.type==TLS_HANDSHAKE_TYPE_CLIENT then
-- client hello - array
len = u16(handshake.data, off)
if left<(2+len) then return partialOK end
handshake.dis.cipher_suites={}
for i=1,(len/2) do
handshake.dis.cipher_suites[i] = u16(handshake.data, off + i*2)
end
off = off + 2 + len
left = left - 2 - len
else
-- server hello - single
handshake.dis.cipher_suite = u16(handshake.data, off)
off = off + 2
left = left - 2
end
-- compression method(s)
if left<1 then return partialOK end
if handshake.dis.type==1 then
-- client hello - array
len = u8(handshake.data, off)
if left<(1+len) then return partialOK end
handshake.dis.compression_methods={}
for i=1,len do
handshake.dis.compression_methods[i] = u8(handshake.data, off + i)
end
off = off + 1 + len
left = left - 1 - len
else
-- server hello - single
handshake.dis.compression_method = u8(handshake.data, off)
off = off + 1
left = left - 1
end
-- tls extensions
if left<2 then return partialOK end
local extlen = u16(handshake.data, off)
if left<(2+extlen) and not partialOK then return false end
off = off + 2
left = left - 2
end
if left>extlen then left=extlen end
-- compression method(s)
if left<1 then return partialOK end
if handshake.dis.type==1 then
-- client hello - array
len = u8(handshake.data, off)
if left<(1+len) then return partialOK end
handshake.dis.compression_methods={}
for i=1,len do
handshake.dis.compression_methods[i] = u8(handshake.data, off + i)
end
off = off + 1 + len
left = left - 1 - len
else
-- server hello - single
handshake.dis.compression_method = u8(handshake.data, off)
off = off + 1
left = left - 1
end
-- tls extensions
if left<2 then return partialOK end
local extlen = u16(handshake.data, off)
if left<(2+extlen) and not partialOK then return end
off = off + 2
left = left - 2
if left>extlen then left=extlen end
handshake.dis.ext = {}
while left>=4 do
len = u16(handshake.data, off + 2)
if len>(left-4) then
if partialOK then
break
else
return
handshake.dis.ext = {}
while left>=4 do
len = u16(handshake.data, off + 2)
if len>(left-4) then
if partialOK then
break
else
return false
end
end
local typ = u16(handshake.data, off)
handshake.dis.ext[#handshake.dis.ext+1] = { type = typ, name = tostring(TLS_HELLO_EXT_NAMES[typ]), data = string.sub(handshake.data, off+4, off+4+len-1) }
tls_dissect_ext(handshake.dis.ext[#handshake.dis.ext])
left = left - 4 - len
off = off + 4 + len
end
local typ = u16(handshake.data, off)
handshake.dis.ext[#handshake.dis.ext+1] = { type = typ, name = tostring(TLS_HELLO_EXT_NAMES[typ]), data = string.sub(handshake.data, off+4, off+4+len-1) }
tls_dissect_ext(handshake.dis.ext[#handshake.dis.ext])
left = left - 4 - len
off = off + 4 + len
end
return true
return true
end
return false
end
return false
local b = tls_dissect_handshake_(handshake,partialOK)
if not b then
-- clear partially filled dissect if failed
handshake.dis = nil
end
return b
end
-- convert tls blob tls dissect
@@ -2649,8 +2674,10 @@ function tls_reconstruct_handshake(handshake)
end
exts = exts .. bu16(handshake.dis.ext[i].type) .. bu16(#handshake.dis.ext[i].data) .. handshake.dis.ext[i].data
end
handshake.data = bu8(handshake.type) .. bu24(#header + 2 + #exts) .. header .. bu16(#exts) .. exts
else
handshake.data = bu8(handshake.type) .. bu24(#header) .. header
end
handshake.data = bu8(handshake.type) .. bu24(#header + 2 + #exts) .. header .. bu16(#exts) .. exts
end
end
@@ -2701,11 +2728,10 @@ function tls_reconstruct(tdis)
if not rec.data then return nil end
end
tls = barray(tdis.rec, function(a) return (#a.data > 0) and (bu8(a.type) .. bu16(a.ver) .. bu16(#a.data) .. a.data) or "" end)
elseif tdis.handshake and #tdis.handshake==1 then
-- without record layer
for k,handshake in pairs(tdis.handshake) do
tls = handshake.data
break
elseif tdis.handshake then
local i = next(tdis.handshake)
if i~=nil then
tls = tdis.handshake[i].data
end
end
+70 -5
View File
@@ -173,6 +173,8 @@ end
-- arg : scode - server icmp code
-- arg : dataxor - blob to xor udp payload
-- arg : server=[0|1] - override server mode. by default use "--server" nfqws2 parameter
-- arg : cgnat - do not rely on identifier - cgnat in most cases changes it. expand udp packet size by 2 (may exceed MTU !)
-- arg : id_lifetime - mapped identifiers lifetime in seconds
function udp2icmp(ctx, desync)
local dataxor
local bserver
@@ -221,6 +223,25 @@ function udp2icmp(ctx, desync)
desync.dis.payload = bxor(desync.dis.payload, pattern(dataxor,1,#desync.dis.payload))
end
end
local function map_purge()
if udp2icmp_map then
local last = udp2icmp_map.purge or 0
local now = os.time()
local timeout = desync.arg.id_lifetime and tonumber(desync.arg.id_lifetime) or 60
if last~=now then
for k,v in pairs(udp2icmp_map) do
if type(v)=='table' and v.t then
if now>=(v.t + timeout) then
udp2icmp_map[k] = nil
end
end
end
udp2icmp_map.purge=now
end
end
end
map_purge()
if desync.arg.dataxor then
dataxor = blob(desync,desync.arg.dataxor)
@@ -234,19 +255,63 @@ function udp2icmp(ctx, desync)
if b_debug then -- save some cpu
DLOG("udp2icmp: udp => icmp sport="..desync.dis.udp.uh_sport.." dport="..desync.dis.udp.uh_dport.." size="..#desync.dis.payload)
end
local id = bitxor(desync.dis.udp.uh_sport,desync.dis.udp.uh_dport)
if desync.arg.cgnat then
if bserver then
-- extract original icmp identifier from the map. it must match original or NAT will not deliver
local map_id = (desync.dis.ip6 and desync.dis.ip6.ip6_dst or desync.dis.ip.ip_dst)..
(desync.dis.ip6 and desync.dis.ip6.ip6_src or desync.dis.ip.ip_src)..
bu16(desync.dis.udp.uh_dport)..bu16(desync.dis.udp.uh_sport)
if udp2icmp_map then
local id_ = udp2icmp_map[map_id]
if id_ then
id=id_.id
if b_debug then DLOG(string.format("udp2icmp: found mapped icmp identifier %04X",id)) end
id_.t=os.time() -- update time
else
DLOG("udp2icmp: mapped icmp identifier NOT found !")
end
end
else
-- cgnat changes identifier. save it in the end of the packet, expand packet size by 2
desync.dis.payload = desync.dis.payload .. bu16(id)
DLOG("udp2icmp: expanding udp size to "..#desync.dis.payload)
end
end
desync.dis.icmp = {
icmp_type = ictype(true),
icmp_code = iccode(true),
icmp_data = u32(
bu16(bitxor(desync.dis.udp.uh_sport,desync.dis.udp.uh_dport))..
(bserver and bu16(desync.dis.udp.uh_sport) or bu16(desync.dis.udp.uh_dport)))
icmp_data = u32(bu16(id)..(bserver and bu16(desync.dis.udp.uh_sport) or bu16(desync.dis.udp.uh_dport)))
}
desync.dis.udp = nil
fix_ip_proto(desync.dis)
return VERDICT_MODIFY
elseif desync.dis.icmp and desync.dis.icmp.icmp_type==ictype(false) and desync.dis.icmp.icmp_code==iccode(false) then
local pl = bitand(desync.dis.icmp.icmp_data,0xFFFF)
local pm = bitxor(bitrshift(desync.dis.icmp.icmp_data,16),pl)
local pm
local id = bitrshift(desync.dis.icmp.icmp_data,16)
if bserver and desync.arg.cgnat then
if #desync.dis.payload<2 then
DLOG("udp2icmp: udp is less than 2 bytes and cgnat is on ! doing nothing")
return VERDICT_PASS
end
if not udp2icmp_map then udp2icmp_map={} end
pm = bitxor(u16(desync.dis.payload,#desync.dis.payload-1),pl) -- use right 2 bytes
desync.dis.payload = string.sub(desync.dis.payload,1,-3) -- shrink right 2 bytes
DLOG("udp2icmp: shrinking udp size to "..#desync.dis.payload)
-- keep original icmp identifier indexed by ip/port tuple
local map_id = (desync.dis.ip6 and desync.dis.ip6.ip6_src or desync.dis.ip.ip_src)..
(desync.dis.ip6 and desync.dis.ip6.ip6_dst or desync.dis.ip.ip_dst)..
bu16(pm)..bu16(pl)
udp2icmp_map[map_id] = { id=id, t=os.time() }
if b_debug then DLOG(string.format("udp2icmp: stored mapped icmp identifier %04X",id)) end
else
pm = bitxor(id,pl)
end
desync.dis.udp = {
uh_sport = bserver and pm or pl,
uh_dport = bserver and pl or pm,
@@ -429,7 +494,7 @@ function synhide(ctx, desync)
if fl==TH_SYN then
-- client sent
local ttl = tonumber(desync.dis.ip and desync.arg.ghost or desync.arg.ghost6)
local ttl = tonumber(desync.dis.ip and desync.arg.ghost or (desync.dis.ip6 and desync.arg.ghost6))
if ttl then
DLOG("synhide: punch NAT hole with ttl="..ttl)
local dis = deepcopy(desync.dis)
+4 -7
View File
@@ -536,8 +536,7 @@ function test_dissect()
{ kind = 1 },
{ kind = 0xE0, data = brandom(math.random(1,10)) },
{ kind = 1 },
{ kind = 0xE1, data = brandom(math.random(1,10)) },
{ kind = 0 }
{ kind = 0xE1, data = brandom(math.random(1,10)) }
}
},
payload = brandom(math.random(0, 20))
@@ -704,8 +703,7 @@ function test_csum()
{ kind = 1 },
{ kind = 0xE0, data = brandom(math.random(1,10)) },
{ kind = 1 },
{ kind = 0xE1, data = brandom(math.random(1,10)) },
{ kind = 0 }
{ kind = 0xE1, data = brandom(math.random(1,10)) }
}
}
tcpb = reconstruct_tcphdr(tcp)
@@ -721,9 +719,8 @@ function test_csum()
bu8(tcp.options[1].kind)..
bu8(tcp.options[2].kind)..bu8(2 + #tcp.options[2].data)..tcp.options[2].data ..
bu8(tcp.options[3].kind)..
bu8(tcp.options[4].kind)..bu8(2 + #tcp.options[4].data)..tcp.options[4].data ..
bu8(tcp.options[5].kind)
raw = raw .. string.rep(bu8(TCP_KIND_NOOP), bitand(4-bitand(#raw,3),3))
bu8(tcp.options[4].kind)..bu8(2 + #tcp.options[4].data)..tcp.options[4].data
raw = raw .. string.rep(bu8(0), bitand(4-bitand(#raw,3),3))
print( raw==tcpb and "TCP RECONSTRUCT OK" or "TCP RECONSTRUCT FAILED" )
test_assert(raw==tcpb)
+11 -9
View File
@@ -1,38 +1,40 @@
CC ?= cc
OPTIMIZE ?= -Os
CFLAGS_PIC = -fPIC
CFLAGS += -std=gnu99 $(OPTIMIZE) $(CFLAGS_PIC)
CFLAGS += -std=gnu99 $(OPTIMIZE)
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_WIN = -static
LDFLAGS_PIE = -pie
LDFLAGS += $(LDFLAGS_PIE)
LIBS = -lpthread
LIBS_ANDROID =
LIBS_WIN = -lws2_32
LIBS_WIN = -lws2_32 -lpthread
SRC_FILES = *.c
all: mdig
mdig: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
systemd: mdig
android: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS_ANDROID) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) -o mdig $(SRC_FILES) $(LIBS_ANDROID) $(LDFLAGS) $(LDFLAGS_PIE)
bsd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS) $(LDFLAGS_PIE)
mac: $(SRC_FILES)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o mdiga $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o mdigx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS)
$(CC) $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o mdiga $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS) $(LDFLAGS_PIE)
$(CC) $(CFLAGS) $(CFLAGS_PIC) $(CFLAGS_BSD) -o mdigx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS) $(LDFLAGS_PIE)
strip mdiga mdigx
lipo -create -output mdig mdigx mdiga
rm -f mdigx mdiga
win: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o mdig $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o mdig.exe $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
win-arm64-cross:
clang -s $(CFLAGS) $(CFLAGS_WIN) --sysroot=/clangarm64 --target=aarch64-w64-mingw32 -B/clang64/bin -rtlib=compiler-rt -o mdig.exe $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS)
clean:
rm -f mdig *.o
+4 -4
View File
@@ -72,13 +72,13 @@ static uint16_t do_csum(const uint8_t *buff, size_t len)
}
if (len & 4)
{
result += *(uint32_t *)buff;
result += *(const uint32_t *)buff;
buff += 4;
}
}
if (len & 2)
{
result += *(uint16_t *)buff;
result += *(const uint16_t *)buff;
buff += 2;
}
}
@@ -115,8 +115,8 @@ void ip4_fix_checksum(struct ip *ip)
uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8_t proto, uint16_t sum)
{
uint64_t a = (uint64_t)sum + htonl(len + proto) +
*(uint32_t *)saddr + *((uint32_t *)saddr + 1) + *((uint32_t *)saddr + 2) + *((uint32_t *)saddr + 3) +
*(uint32_t *)daddr + *((uint32_t *)daddr + 1) + *((uint32_t *)daddr + 2) + *((uint32_t *)daddr + 3);
*(const uint32_t *)saddr + *((const uint32_t *)saddr + 1) + *((const uint32_t *)saddr + 2) + *((const uint32_t *)saddr + 3) +
*(const uint32_t *)daddr + *((const uint32_t *)daddr + 1) + *((const uint32_t *)daddr + 2) + *((const uint32_t *)daddr + 3);
return ~from64to16(a);
}
+1 -10
View File
@@ -146,7 +146,7 @@ static void ConntrackApplyPos(t_ctrack *t, bool bReverse, const struct dissect *
{
// scale and mss only valid in syn packets
scale = tcp_find_scale_factor(dis->tcp);
if (scale != SCALE_NONE) direct->scale = scale;
if (scale != SCALE_NONE) direct->scale = scale<14 ? scale : 14; // RFC7323 hard limit
direct->mss = tcp_find_mss(dis->tcp);
}
else
@@ -395,15 +395,6 @@ bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start)
reasm->seq = seq_start;
return true;
}
bool ReasmResize(t_reassemble *reasm, size_t new_size)
{
uint8_t *p = realloc(reasm->packet, new_size);
if (!p) return false;
reasm->packet = p;
reasm->size = new_size;
if (reasm->size_present > new_size) reasm->size_present = new_size;
return true;
}
#define REASM_MAX_NEG 0x100000
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len)
{
+16 -8
View File
@@ -377,8 +377,9 @@ bool proto_check_ipv4(const uint8_t *data, size_t len)
{
if (len < sizeof(struct ip)) return false;
if (((struct ip*)data)->ip_v!=4) return false;
uint16_t iplen = ntohs(((struct ip*)data)->ip_len);
uint8_t off = ((struct ip*)data)->ip_hl << 2;
return off>=sizeof(struct ip) && len>=off;
return off>=sizeof(struct ip) && iplen>=off && len>=off;
}
bool proto_check_ipv4_payload(const uint8_t *data, size_t len)
{
@@ -559,7 +560,7 @@ void proto_dissect_l3l4(const uint8_t *data, size_t len, struct dissect *dis, bo
dis->data_pkt = data;
dis->len_pkt = len;
uint16_t iplen;
size_t iplen;
if (proto_check_ipv4(data, len) && (no_payload_check || proto_check_ipv4_payload(data, len)))
{
@@ -1111,6 +1112,7 @@ bool win_sandbox(void)
static HANDLE w_filter = NULL;
static OVERLAPPED ovl = { .hEvent = NULL };
static const struct str_list_head *wlan_filter_ssid = NULL, *nlm_filter_net = NULL;
static bool wlan_filter_ssid_neg = false, nlm_filter_net_neg = false;
static DWORD logical_net_filter_tick=0;
INetworkListManager* pNetworkListManager=NULL;
@@ -1230,12 +1232,15 @@ t_WlanQueryInterface f_WlanQueryInterface = NULL;
t_WlanFreeMemory f_WlanFreeMemory = NULL;
HMODULE hdll_wlanapi = NULL;
bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter)
bool win_dark_init(const struct str_list_head *ssid_filter, bool ssid_filter_neg, const struct str_list_head *nlm_filter, bool nlm_filter_neg)
{
win_dark_deinit();
if (LIST_EMPTY(ssid_filter)) ssid_filter=NULL;
if (LIST_EMPTY(nlm_filter)) nlm_filter=NULL;
wlan_filter_ssid_neg = ssid_filter_neg;
nlm_filter_net_neg = nlm_filter_neg;
if (nlm_filter)
{
if (SUCCEEDED(w_win32_error = CoInitialize(NULL)))
@@ -1288,6 +1293,7 @@ void win_dark_deinit(void)
}
if (nlm_filter_net) CoUninitialize();
wlan_filter_ssid = nlm_filter_net = NULL;
wlan_filter_ssid_neg = nlm_filter_net_neg = false;
if (hdll_wlanapi)
{
FreeLibrary(hdll_wlanapi);
@@ -1525,16 +1531,18 @@ found:
goto ex;
}
bool logical_net_filter_match(void)
{
return wlan_filter_match(wlan_filter_ssid) && nlm_filter_match(nlm_filter_net);
}
bool logical_net_filter_present(void)
{
return (wlan_filter_ssid && !LIST_EMPTY(wlan_filter_ssid)) || (nlm_filter_net && !LIST_EMPTY(nlm_filter_net));
}
bool logical_net_filter_match(void)
{
return (wlan_filter_match(wlan_filter_ssid) ^ wlan_filter_ssid_neg) &&
(nlm_filter_match(nlm_filter_net) ^ nlm_filter_net_neg);
}
static bool logical_net_filter_match_rate_limited(void)
+1 -1
View File
@@ -129,7 +129,7 @@ extern uint32_t w_win32_error;
bool ensure_dir_access(const char *filename);
bool prepare_low_appdata();
bool win_sandbox(void);
bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter);
bool win_dark_init(const struct str_list_head *ssid_filter, bool ssid_filter_neg, const struct str_list_head *nlm_filter, bool nlm_filter_neg);
void win_dark_deinit(void);
bool logical_net_filter_present(void);
bool logical_net_filter_match(void);
+119 -72
View File
@@ -201,60 +201,80 @@ static void packet_debug(bool replay, const struct dissect *dis)
}
}
// ipr,ipr6 - reverse ip - ip of the other side of communication
struct dp_match_data
{
const struct in_addr *ip;
const struct in6_addr *ip6;
const struct in_addr *ipr;
const struct in6_addr *ipr6;
const char *hostname;
const char *ssid;
bool *bCheckDone, *bCheckResult, *bExcluded;
t_l7proto l7proto;
uint32_t fwmark;
uint16_t port;
uint8_t icmp_type, icmp_code;
bool bNoSubdom;
uint8_t l3proto;
};
static bool dp_match(
struct desync_profile *dp,
uint8_t l3proto,
const struct in_addr *ip, const struct in6_addr *ip6,
const struct in_addr *ipr, const struct in6_addr *ipr6,
uint16_t port, uint8_t icmp_type, uint8_t icmp_code,
const char *hostname, bool bNoSubdom, t_l7proto l7proto, const char *ssid,
bool *bCheckDone, bool *bCheckResult, bool *bExcluded)
const struct dp_match_data *match)
{
bool bHostlistsEmpty;
if (bCheckDone) *bCheckDone = false;
if (match->bCheckDone) *match->bCheckDone = false;
if (!HostlistsReloadCheckForProfile(dp)) return false;
// fast checks first
if ((ip && !dp->filter_ipv4) || (ip6 && !dp->filter_ipv6))
// L3 filter does not match
return false;
switch(l3proto)
{
case IPPROTO_TCP:
if (!port_filters_match(&dp->pf_tcp, port)) return false;
break;
case IPPROTO_UDP:
if (!port_filters_match(&dp->pf_udp, port)) return false;
break;
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
if (!icmp_filters_match(&dp->icf, icmp_type, icmp_code)) return false;
break;
default:
if (!ipp_filters_match(&dp->ipf, l3proto)) return false;
}
if (!l7_proto_match(l7proto, dp->filter_l7))
// L7 filter does not match
return false;
#ifdef HAS_FILTER_SSID
if (!LIST_EMPTY(&dp->filter_ssid) && !strlist_search(&dp->filter_ssid, ssid))
#ifdef __linux__
if ((match->fwmark & dp->filter_mark_mask)!=dp->filter_mark)
return false;
#endif
if ((match->ip && !dp->filter_ipv4) || (match->ip6 && !dp->filter_ipv6))
// L3 filter does not match
return false;
if (!l7_proto_match(match->l7proto, dp->filter_l7))
// L7 filter does not match
return false;
switch(match->l3proto)
{
case IPPROTO_TCP:
if (!port_filters_match(&dp->pf_tcp, match->port)) return false;
break;
case IPPROTO_UDP:
if (!port_filters_match(&dp->pf_udp, match->port)) return false;
break;
case IPPROTO_ICMP:
case IPPROTO_ICMPV6:
if (!icmp_filters_match(&dp->icf, match->icmp_type, match->icmp_code)) return false;
break;
default:
if (!ipp_filters_match(&dp->ipf, match->l3proto)) return false;
}
#ifdef HAS_FILTER_SSID
if (!LIST_EMPTY(&dp->filter_ssid) && (!strlist_search(&dp->filter_ssid, match->ssid) ^ dp->filter_ssid_neg))
return false;
#endif
if (!HostlistsReloadCheckForProfile(dp)) return false;
bHostlistsEmpty = PROFILE_HOSTLISTS_EMPTY(dp);
if (!dp->hostlist_auto && !hostname && !bHostlistsEmpty)
if (!dp->hostlist_auto && !match->hostname && !bHostlistsEmpty)
// avoid cpu consuming ipset check. profile cannot win if regular hostlists are present without auto hostlist and hostname is unknown.
return false;
if (!IpsetCheck(dp, ip, ip6, ipr, ipr6))
if (!IpsetCheck(dp, match->ip, match->ip6, match->ipr, match->ipr6))
// target ip does not match
return false;
// autohostlist profile matching l3/l4/l7 filter always win if we have a hostname. no matter it matches or not.
if (dp->hostlist_auto && hostname)
if (dp->hostlist_auto && match->hostname)
{
DLOG("autohostlist profile %u (%s) wins because hostname is known\n",dp->n,dp->name);
return true;
@@ -266,12 +286,12 @@ static bool dp_match(
else
{
// if hostlists are present profile matches only if hostname is known and satisfy profile hostlists
if (hostname)
if (match->hostname)
{
if (bCheckDone) *bCheckDone = true;
if (match->bCheckDone) *match->bCheckDone = true;
bool b;
b = HostlistCheck(dp, hostname, bNoSubdom, bExcluded, true);
if (bCheckResult) *bCheckResult = b;
b = HostlistCheck(dp, match->hostname, match->bNoSubdom, match->bExcluded, true);
if (match->bCheckResult) *match->bCheckResult = b;
return b;
}
}
@@ -283,10 +303,19 @@ static struct desync_profile *dp_find(
const struct in_addr *ip, const struct in6_addr *ip6,
const struct in_addr *ipr, const struct in6_addr *ipr6,
uint16_t port, uint8_t icmp_type, uint8_t icmp_code,
const char *hostname, bool bNoSubdom, t_l7proto l7proto, const char *ssid,
const char *hostname, bool bNoSubdom, t_l7proto l7proto,
const char *ssid, uint32_t fwmark,
bool *bCheckDone, bool *bCheckResult, bool *bExcluded)
{
struct desync_profile_list *dpl;
struct dp_match_data match = {
.l3proto=l3proto,
.ip=ip, .ip6=ip6, .ipr=ipr, .ipr6=ipr6,
.port=port, .icmp_type=icmp_type, .icmp_code=icmp_code,
.hostname=hostname, .bNoSubdom=bNoSubdom, .l7proto=l7proto,
.ssid=ssid, .fwmark=fwmark,
.bCheckDone=bCheckDone, .bCheckResult=bCheckResult, .bExcluded=bExcluded
};
if (params.debug)
{
char s[INET6_ADDRSTRLEN];
@@ -295,17 +324,17 @@ static struct desync_profile *dp_find(
{
char sr[INET6_ADDRSTRLEN];
ntopa46(ipr, ipr6, sr, sizeof(sr));
DLOG("desync profile search for %s ip1=%s ip2=%s port=%u icmp=%u:%u l7proto=%s ssid='%s' hostname='%s'\n",
proto_name(l3proto), s, sr, port, icmp_type, icmp_code, l7proto_str(l7proto), ssid ? ssid : "", hostname ? hostname : "");
DLOG("desync profile search for %s ip1=%s ip2=%s port=%u icmp=%u:%u l7proto=%s ssid='%s' mark=0x%08X hostname='%s'\n",
proto_name(l3proto), s, sr, port, icmp_type, icmp_code, l7proto_str(l7proto), ssid ? ssid : "", fwmark, hostname ? hostname : "");
}
else
DLOG("desync profile search for %s ip=%s port=%u icmp=%u:%u l7proto=%s ssid='%s' hostname='%s'\n",
proto_name(l3proto), s, port, icmp_type, icmp_code, l7proto_str(l7proto), ssid ? ssid : "", hostname ? hostname : "");
DLOG("desync profile search for %s ip=%s port=%u icmp=%u:%u l7proto=%s ssid='%s' mark=0x%08X hostname='%s'\n",
proto_name(l3proto), s, port, icmp_type, icmp_code, l7proto_str(l7proto), ssid ? ssid : "", fwmark, hostname ? hostname : "");
}
if (bCheckDone) *bCheckDone = false;
LIST_FOREACH(dpl, head, next)
{
if (dp_match(&dpl->dp, l3proto, ip, ip6, ipr, ipr6, port, icmp_type, icmp_code, hostname, bNoSubdom, l7proto, ssid, bCheckDone, bCheckResult, bExcluded))
if (dp_match(&dpl->dp, &match))
{
DLOG("desync profile %u (%s) matches\n", dpl->dp.n, PROFILE_NAME(&dpl->dp));
return &dpl->dp;
@@ -614,8 +643,8 @@ static uint8_t ct_new_postnat_fix(const t_ctrack *ctrack, const struct dissect *
// if used in postnat chain, dropping initial packet will cause conntrack connection teardown
// so we need to workaround this.
// SYN and SYN,ACK checks are for conntrack-less mode
if (ctrack && (params.server ? ctrack->pos.server.pcounter : ctrack->pos.client.pcounter) == 1 ||
!ctrack && dis->tcp && (tcp_syn_segment(dis->tcp) || tcp_synack_segment(dis->tcp)))
if ((ctrack && (params.server ? ctrack->pos.server.pcounter : ctrack->pos.client.pcounter) == 1) ||
(!ctrack && dis->tcp && (tcp_syn_segment(dis->tcp) || tcp_synack_segment(dis->tcp))))
{
if (dis->len_pkt > *len_mod_pkt)
DLOG_ERR("linux postnat conntrack workaround cannot be applied\n");
@@ -687,7 +716,7 @@ static bool check_pos_to(const t_ctrack_position *pos, const struct packet_range
if (pos)
{
ps = pos_get(pos, range->to.mode);
return (ps < range->to.pos) || !range->upper_cutoff && (ps == range->to.pos);
return (ps < range->to.pos) || (!range->upper_cutoff && (ps == range->to.pos));
}
else
return false;
@@ -814,20 +843,26 @@ static bool desync_get_result(uint8_t *verdict)
DLOG_ERR("desync function returned more than one result : %d\n", rescount);
goto err;
}
// absence of the return value or nil mean VERDICT_PASS
if (rescount)
{
if (!lua_isinteger(params.L, -1))
if (lua_isnil(params.L, -1))
*verdict = VERDICT_PASS;
else
{
DLOG_ERR("desync function returned non-int result\n");
goto err;
if (!lua_isinteger(params.L, -1))
{
DLOG_ERR("desync function returned non-int result\n");
goto err;
}
lua_Integer lv = lua_tointeger(params.L, -1);
if (lv & ~VERDICT_MASK_VALID_LUA)
{
DLOG_ERR("desync function returned bad int result\n");
goto err;
}
*verdict = (uint8_t)lv;
}
lua_Integer lv = lua_tointeger(params.L, -1);
if (lv & ~VERDICT_MASK_VALID_LUA)
{
DLOG_ERR("desync function returned bad int result\n");
goto err;
}
*verdict = (uint8_t)lv;
}
else
*verdict = VERDICT_PASS; // default result if function returns nothing
@@ -994,7 +1029,6 @@ static uint8_t desync(
lua_pushf_global(params.L, "tcp_mss", "DEFAULT_MSS");
}
ref_arg = luaL_ref(params.L, LUA_REGISTRYINDEX);
ctx->func_n = 1;
LIST_FOREACH(func, &dp->lua_desync, next)
{
@@ -1027,7 +1061,12 @@ static uint8_t desync(
}
lua_rawgeti(params.L, LUA_REGISTRYINDEX, params.ref_desync_ctx);
lua_rawgeti(params.L, LUA_REGISTRYINDEX, ref_arg);
lua_pushf_args(params.L, &func->args, -1, true);
if (!lua_pushf_args(params.L, &func->args, -1, true))
{
// some % or # blobs cannot be resolved
lua_pop(params.L, 3);
goto err;
}
lua_pushf_str(params.L, "func", func->func);
lua_pushf_int(params.L, "func_n", ctx->func_n);
lua_pushf_str(params.L, "func_instance", instance);
@@ -1178,6 +1217,7 @@ struct play_state
t_l7proto l7proto;
t_l7payload l7payload;
const char *ssid;
uint32_t fwmark;
bool bReverse, bReverseFixed, bHaveHost;
};
static bool play_prolog(
@@ -1185,7 +1225,8 @@ static bool play_prolog(
const struct dissect *dis,
const t_ctrack_positions *tpos,
bool bReplay,
const char *ifin, const char *ifout)
const char *ifin, const char *ifout,
uint32_t fwmark)
{
ps->verdict = VERDICT_PASS;
@@ -1200,6 +1241,7 @@ static bool play_prolog(
ps->l7proto = L7_UNKNOWN;
ps->l7payload = dis->len_payload ? L7P_UNKNOWN : L7P_EMPTY;
ps->ssid = NULL;
ps->fwmark = fwmark;
const char *ifname;
@@ -1223,7 +1265,7 @@ static bool play_prolog(
DLOG("using cached desync profile %u (%s)\n", ps->dp->n, PROFILE_NAME(ps->dp));
else if (!ps->ctrack_replay->dp_search_complete)
{
ps->dp = ps->ctrack_replay->dp = dp_find(&params.desync_profiles, dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, ps->ctrack_replay->hostname, ps->ctrack_replay->hostname_is_ip, ps->l7proto, ps->ssid, NULL, NULL, NULL);
ps->dp = ps->ctrack_replay->dp = dp_find(&params.desync_profiles, dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, ps->ctrack_replay->hostname, ps->ctrack_replay->hostname_is_ip, ps->l7proto, ps->ssid, ps->fwmark, NULL, NULL, NULL);
ps->ctrack_replay->dp_search_complete = true;
}
if (!ps->dp)
@@ -1271,7 +1313,7 @@ static bool play_prolog(
DLOG_ERR("strdup(host): out of memory\n");
}
}
ps->dp = dp_find(&params.desync_profiles, dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, hostname, hostname_is_ip, ps->l7proto, ps->ssid, NULL, NULL, NULL);
ps->dp = dp_find(&params.desync_profiles, dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF, hostname, hostname_is_ip, ps->l7proto, ps->ssid, ps->fwmark, NULL, NULL, NULL);
if (ps->ctrack)
{
ps->ctrack->dp = ps->dp;
@@ -1359,7 +1401,7 @@ static bool dp_rediscovery(struct play_state *ps)
ps->dp = dp_find(&params.desync_profiles, ps->dis->proto, ps->sdip4, ps->sdip6, NULL, NULL, ps->sdport, 0xFF, 0xFF,
ps->ctrack_replay ? ps->ctrack_replay->hostname : ps->bHaveHost ? ps->host : NULL,
ps->ctrack_replay ? ps->ctrack_replay->hostname_is_ip : bHostIsIp,
ps->l7proto, ps->ssid,
ps->l7proto, ps->ssid, ps->fwmark,
&bCheckDone, &bCheckResult, &bCheckExcluded);
if (ps->ctrack_replay)
{
@@ -1437,7 +1479,7 @@ static uint8_t dpi_desync_tcp_packet_play(
{
struct play_state ps;
if (!play_prolog(&ps, dis, tpos, !!replay_piece_count, ifin, ifout))
if (!play_prolog(&ps, dis, tpos, !!replay_piece_count, ifin, ifout, fwmark))
return ps.verdict;
uint32_t desync_fwmark = fwmark | params.desync_fwmark;
@@ -1585,9 +1627,7 @@ static uint8_t dpi_desync_tcp_packet_play(
{
// likely exceeded packet limit or unlikely out of memory
DLOG_ERR("rawpacket_queue failed !\n");
reasm_client_cancel(ps.ctrack);
ps.l7payload = L7P_UNKNOWN; // middle packet may be not L7P_TLS_CLIENT_HELLO
goto rediscover;
goto reasm_cancel_rediscover;
}
if (ReasmIsFull(&ps.ctrack->reasm_client))
{
@@ -1619,6 +1659,13 @@ pass:
pass_reasm_cancel:
reasm_client_cancel(ps.ctrack);
goto pass;
reasm_cancel_rediscover:
reasm_client_cancel(ps.ctrack);
rdata_payload = dis->data_payload;
rlen_payload = dis->len_payload;
ps.l7payload = L7P_UNKNOWN; // could be middle packet
goto rediscover;
}
// return : true - should continue, false - should stop with verdict
@@ -1796,7 +1843,7 @@ static uint8_t dpi_desync_udp_packet_play(
{
struct play_state ps;
if (!play_prolog(&ps, dis, tpos, !!replay_piece_count, ifin, ifout))
if (!play_prolog(&ps, dis, tpos, !!replay_piece_count, ifin, ifout, fwmark))
return ps.verdict;
uint32_t desync_fwmark = fwmark | params.desync_fwmark;
@@ -2058,7 +2105,7 @@ static uint8_t dpi_desync_icmp_packet(
dis->ip ? &dis->ip->ip_src : NULL, dis->ip6 ? &dis->ip6->ip6_src : NULL,
0, dis->icmp->icmp_type, dis->icmp->icmp_code,
hostname, hostname_is_ip,
L7_UNKNOWN, ssid, NULL, NULL, NULL);
L7_UNKNOWN, ssid, fwmark, NULL, NULL, NULL);
if (!dp)
{
DLOG("matching desync profile not found\n");
@@ -2124,7 +2171,7 @@ static uint8_t dpi_desync_ip_packet(
dis->ip ? &dis->ip->ip_src : NULL, dis->ip6 ? &dis->ip6->ip6_src : NULL,
0, 0xFF, 0xFF,
hostname, hostname_is_ip,
L7_UNKNOWN, ssid, NULL, NULL, NULL);
L7_UNKNOWN, ssid, fwmark, NULL, NULL, NULL);
if (!dp)
{
DLOG("matching desync profile not found\n");
+4 -9
View File
@@ -35,7 +35,7 @@ int unique_ssize_t(ssize_t *pu, int ct) UNIQ_SORT
static int cmp_size_t(const void * a, const void * b)
{
return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b;
return *(const size_t*)a < *(const size_t*)b ? -1 : *(const size_t*)a > *(const size_t*)b;
}
void qsort_size_t(size_t *array, int ct)
{
@@ -43,7 +43,7 @@ void qsort_size_t(size_t *array, int ct)
}
static int cmp_ssize_t(const void * a, const void * b)
{
return *(ssize_t*)a < *(ssize_t*)b ? -1 : *(ssize_t*)a > *(ssize_t*)b;
return *(const ssize_t*)a < *(const ssize_t*)b ? -1 : *(const ssize_t*)a > *(const ssize_t*)b;
}
void qsort_ssize_t(ssize_t *array, int ct)
{
@@ -424,14 +424,9 @@ bool file_size(const char *filename, off_t *size)
{
struct stat st;
if (stat(filename, &st) == -1) return false;
*size = st.st_size;
if (size) *size = st.st_size;
return true;
}
time_t file_mod_time(const char *filename)
{
struct stat st;
return stat(filename, &st) == -1 ? 0 : st.st_mtime;
}
bool file_mod_signature(const char *filename, file_mod_sig *ms)
{
struct stat st;
@@ -440,7 +435,7 @@ bool file_mod_signature(const char *filename, file_mod_sig *ms)
FILE_MOD_RESET(ms);
return false;
}
ms->mod_time = st.st_mtime;
ms->mod_time = st.st_mtim;
ms->size = st.st_size;
return true;
}
+2 -3
View File
@@ -82,13 +82,12 @@ int fprint_localtime(FILE *F);
typedef struct
{
time_t mod_time;
struct timespec mod_time;
off_t size;
} file_mod_sig;
#define FILE_MOD_COMPARE(ms1,ms2) (((ms1)->mod_time==(ms2)->mod_time) && ((ms1)->size==(ms2)->size))
#define FILE_MOD_COMPARE(ms1,ms2) (((ms1)->mod_time.tv_sec==(ms2)->mod_time.tv_sec) && ((ms1)->mod_time.tv_nsec==(ms2)->mod_time.tv_nsec) && ((ms1)->size==(ms2)->size))
#define FILE_MOD_RESET(ms) memset(ms,0,sizeof(file_mod_sig))
bool file_mod_signature(const char *filename, file_mod_sig *ms);
time_t file_mod_time(const char *filename);
bool file_size(const char *filename, off_t *size);
bool file_open_test(const char *filename, int flags);
+14 -8
View File
@@ -115,7 +115,8 @@ bool AppendHostList(hostlist_pool **hostlist, const char *filename)
return true;
}
static bool LoadHostList(struct hostlist_file *hfile)
// bChanged is set to true if the list was changed and not set otherwise
static bool LoadHostList(struct hostlist_file *hfile, bool *bChanged)
{
if (hfile->filename)
{
@@ -133,6 +134,7 @@ static bool LoadHostList(struct hostlist_file *hfile)
DLOG_PERROR("file_open_test");
goto unchanged;
}
if (bChanged) *bChanged=true;
// don't want to keep backup copy in memory - it will require *2 RAM. Problem on low-ram devices. It's better to fail hostlist read than have OOM.
// if a file can be opened there're few chances it can't be read. fs corruption, disk error, deleted or made inaccessible between 2 syscals ?
// it's all hypotetically possible but very unlikely. but OOM is much more real problem on an embedded device if list is large enough
@@ -149,14 +151,14 @@ unchanged:
DLOG_ERR("cannot access hostlist file '%s'. in-memory content remains unchanged.\n",hfile->filename);
return true;
}
static bool LoadHostLists(struct hostlist_files_head *list)
static bool LoadHostLists(struct hostlist_files_head *list, bool *bChanged)
{
bool bres=true;
struct hostlist_file *hfile;
LIST_FOREACH(hfile, list, next)
{
if (!LoadHostList(hfile))
if (!LoadHostList(hfile, bChanged))
// at least one failed
bres=false;
}
@@ -181,9 +183,10 @@ static void MakeAutolistsNonEmpty()
bool LoadAllHostLists()
{
if (!LoadHostLists(&params.hostlists))
bool bChanged=false;
if (!LoadHostLists(&params.hostlists, &bChanged))
return false;
MakeAutolistsNonEmpty();
if (bChanged) MakeAutolistsNonEmpty();
return true;
}
@@ -227,12 +230,15 @@ static bool SearchHostList(hostlist_pool *hostlist, const char *host, bool no_ma
static bool HostlistsReloadCheck(const struct hostlist_collection_head *hostlists)
{
struct hostlist_item *item;
bool bChanged=false;
LIST_FOREACH(item, hostlists, next)
{
if (!LoadHostList(item->hfile))
if (!LoadHostList(item->hfile, &bChanged))
return false;
}
MakeAutolistsNonEmpty();
// this is relatively heavy function. walks through all the profiles. do not walk if no hostlists were releaded
if (bChanged) MakeAutolistsNonEmpty();
return true;
}
bool HostlistsReloadCheckForProfile(const struct desync_profile *dp)
@@ -309,7 +315,7 @@ 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))
if (filename && !file_size(filename,NULL))
{
DLOG_ERR("cannot access hostlist file '%s'\n",filename);
return NULL;
+1 -1
View File
@@ -310,7 +310,7 @@ static struct ipset_file *RegisterIpset_(struct ipset_files_head *ipsets, struct
}
struct ipset_file *RegisterIpset(struct desync_profile *dp, bool bExclude, const char *filename)
{
if (filename && !file_mod_time(filename))
if (filename && !file_size(filename,NULL))
{
DLOG_ERR("cannot access ipset file '%s'\n",filename);
return NULL;
+77 -20
View File
@@ -1036,7 +1036,8 @@ static int luacall_execution_plan(lua_State *L)
lua_pushinteger(L, n - ctx->func_n);
lua_createtable(L, 0, 7);
lua_pushf_args(L,&func->args, -1, false);
lua_pushf_args(L,&func->args, -1, false); // cannot return false if subst_prefix=false
lua_pushf_str(L,"func", func->func);
lua_pushf_int(L,"func_n", n);
lua_pushf_str(L,"func_instance", instance);
@@ -1240,21 +1241,33 @@ void lua_pushf_global(lua_State *L, const char *field, const char *global)
lua_rawset(L,-3);
}
void lua_push_blob(lua_State *L, int idx_desync, const char *blob)
bool lua_push_blob(lua_State *L, int idx_desync, const char *blob)
{
lua_getfield(L, idx_desync, blob);
if (lua_type(L,-1)==LUA_TNIL)
{
lua_pop(L,1);
lua_getglobal(L, blob);
if (lua_type(L,-1)==LUA_TNIL)
{
lua_pop(L,1);
DLOG_ERR("blob '%s' unavailable\n", blob);
return false;
}
}
lua_tostring(L,-1);
return true;
}
void lua_pushf_blob(lua_State *L, int idx_desync, const char *field, const char *blob)
bool lua_pushf_blob(lua_State *L, int idx_desync, const char *field, const char *blob)
{
lua_pushstring(L, field);
lua_push_blob(L, idx_desync, blob);
if (!lua_push_blob(L, idx_desync, blob))
{
lua_pop(L,1);
return false;
}
lua_rawset(L,-3);
return true;
}
void lua_push_ipaddr(lua_State *L, const struct sockaddr *sa)
@@ -1293,8 +1306,34 @@ void lua_pushf_tcphdr_options(lua_State *L, const struct tcphdr *tcp, size_t len
uint8_t *t = (uint8_t*)(tcp+1);
uint8_t *end = (uint8_t*)tcp + (tcp->th_off<<2);
uint8_t opt;
uint8_t opt, *ee;
if ((end-(uint8_t*)tcp) > len) end=(uint8_t*)tcp + len;
// strip ending 0x00 options (padding)
ee = end;
while(t<end)
{
opt = *t;
if (opt==TCP_KIND_END)
{
// set end marker only once
if (ee==end) ee=t;
}
else
// not 0x00 (END) ? clear end marker. ENDs inside other options must be dissected
ee=end;
// skip to the next option
if (opt==TCP_KIND_NOOP || opt==TCP_KIND_END)
t++;
else
{
if ((t+1)>=end || t[1]<2 || (t+t[1])>end) break;
t+=t[1];
}
}
end = ee;
t = (uint8_t*)(tcp+1);
lua_Integer idx=1;
while(t<end)
{
@@ -1316,7 +1355,6 @@ void lua_pushf_tcphdr_options(lua_State *L, const struct tcphdr *tcp, size_t len
t+=t[1];
}
lua_rawset(L,-3);
if (opt==TCP_KIND_END) break;
idx++;
}
@@ -1747,7 +1785,7 @@ void lua_pushf_ctrack(lua_State *L, const t_ctrack *ctrack, const t_ctrack_posit
LUA_STACK_GUARD_LEAVE(L, 0)
}
void lua_pushf_args(lua_State *L, const struct str2_list_head *args, int idx_desync, bool subst_prefix)
bool lua_pushf_args(lua_State *L, const struct str2_list_head *args, int idx_desync, bool subst_prefix)
{
// var=val - pass val string
// var=%val - subst 'val' blob
@@ -1774,10 +1812,20 @@ void lua_pushf_args(lua_State *L, const struct str2_list_head *args, int idx_des
// escape char
lua_pushf_str(L, var, val+1);
else if (val[0]=='%')
lua_pushf_blob(L, idx_desync, var, val+1);
{
if (!lua_pushf_blob(L, idx_desync, var, val+1))
{
lua_pop(L,2);
goto err;
}
}
else if (val[0]=='#')
{
lua_push_blob(L, idx_desync, val+1);
if (!lua_push_blob(L, idx_desync, val+1))
{
lua_pop(L,2);
goto err;
}
lua_Integer len = lua_rawlen(L, -1);
lua_pop(L,1);
lua_pushstring(L, var);
@@ -1794,6 +1842,10 @@ void lua_pushf_args(lua_State *L, const struct str2_list_head *args, int idx_des
lua_rawset(L,-3);
LUA_STACK_GUARD_LEAVE(L, 0)
return true;
err:
LUA_STACK_GUARD_LEAVE(L, 0)
return false;
}
void lua_pushf_pos(lua_State *L, const char *name, const struct packet_pos *pos)
{
@@ -2130,7 +2182,7 @@ static bool lua_reconstruct_tcphdr_options(lua_State *L, int idx, struct tcphdr
lua_Integer idx=0;
uint8_t *p, *data = (uint8_t*)(tcp+1);
size_t l, left;
uint8_t kind;
uint8_t kind, pad;
left = *len - filled;
if (left>40) left=40; // max size of tcp options
@@ -2158,9 +2210,6 @@ static bool lua_reconstruct_tcphdr_options(lua_State *L, int idx, struct tcphdr
switch(kind)
{
case TCP_KIND_END:
*data = kind; data++; left--; filled++;
lua_pop(L, 1);
goto end;
case TCP_KIND_NOOP:
*data = kind; data++; left--; filled++;
break;
@@ -2181,12 +2230,10 @@ static bool lua_reconstruct_tcphdr_options(lua_State *L, int idx, struct tcphdr
lua_pop(L, 1);
}
}
end:
while(filled & 3)
{
if (!left) goto err;
*data = TCP_KIND_NOOP; data++; left--; filled++;
}
pad = (4-(filled & 3)) & 3;
if (left<pad) goto err;
memset(data,0,pad);
filled+=pad;
}
tcp->th_off = filled>>2;
@@ -2498,6 +2545,11 @@ bool lua_reconstruct_dissect(lua_State *L, int idx, uint8_t *buf, size_t *len, b
if (lua_type(L,-1)==LUA_TTABLE)
{
l = sizeof(struct udphdr);
if (left<l)
{
DLOG_ERR("reconstruct_dissect: no space for udp header\n");
goto err;
}
udp = (struct udphdr*)data;
if (!lua_reconstruct_udphdr(L, -1, udp))
{
@@ -2512,6 +2564,11 @@ bool lua_reconstruct_dissect(lua_State *L, int idx, uint8_t *buf, size_t *len, b
if (lua_type(L,-1)==LUA_TTABLE)
{
l = sizeof(struct icmp46);
if (left<l)
{
DLOG_ERR("reconstruct_dissect: no space for icmp header\n");
goto err;
}
icmp = (struct icmp46*)data;
if (!lua_reconstruct_icmphdr(L, -1, icmp))
{
@@ -2976,7 +3033,7 @@ static int luacall_rawsend_dissect(lua_State *L)
static int luacall_conntrack_feed(lua_State *L)
{
// conntrack_feed(dissect/raw_packet[, reconstruct_opts]) return track,bOutgoing
lua_check_argc_range(L,"conntrack_feed",1,3);
lua_check_argc_range(L,"conntrack_feed",1,2);
LUA_STACK_GUARD_ENTER(L)
+3 -3
View File
@@ -82,8 +82,8 @@ void lua_pushf_lud(lua_State *L, const char *field, void *p);
void lua_pushf_table(lua_State *L, const char *field);
void lua_pushi_table(lua_State *L, lua_Integer idx);
void lua_push_blob(lua_State *L, int idx_desync, const char *blob);
void lua_pushf_blob(lua_State *L, int idx_desync, const char *field, const char *blob);
bool lua_push_blob(lua_State *L, int idx_desync, const char *blob);
bool lua_pushf_blob(lua_State *L, int idx_desync, const char *field, const char *blob);
void lua_push_ipaddr(lua_State *L, const struct sockaddr *sa);
void lua_pushf_ipaddr(lua_State *L, const char *field, const struct sockaddr *sa);
@@ -103,7 +103,7 @@ void lua_push_dissect(lua_State *L, const struct dissect *dis);
void lua_pushf_dissect(lua_State *L, const struct dissect *dis);
void lua_push_ctrack(lua_State *L, const t_ctrack *ctrack, const t_ctrack_positions *tpos, bool bIncoming);
void lua_pushf_ctrack(lua_State *L, const t_ctrack *ctrack, const t_ctrack_positions *tpos, bool bIncoming);
void lua_pushf_args(lua_State *L, const struct str2_list_head *args, int idx_desync, bool subst_prefix);
bool lua_pushf_args(lua_State *L, const struct str2_list_head *args, int idx_desync, bool subst_prefix);
void lua_pushf_pos(lua_State *L, const char *name, const struct packet_pos *pos);
void lua_pushf_range(lua_State *L, const char *name, const struct packet_range *range);
void lua_pushf_global(lua_State *L, const char *field, const char *global);
+57 -10
View File
@@ -529,7 +529,7 @@ static int nfq_main(void)
fd = nfq_fd(h);
bt_next = bt = 0;
do
for(;;)
{
if (bQuit) goto quit;
for(;;)
@@ -594,9 +594,8 @@ static int nfq_main(void)
DLOG_ERR("recv: recv=%zd errno %d\n", rd, e);
errno = e;
DLOG_PERROR("recv");
// do not fail on ENOBUFS
} while (e == ENOBUFS);
if (e!=ENOBUFS) break;
}
err:
res=1;
goto ex;
@@ -876,7 +875,7 @@ static int win_main(void)
return ERROR_TOO_MANY_OPEN_FILES; // code 4 = The system cannot open the file
}
if (!win_dark_init(&params.ssid_filter, &params.nlm_filter))
if (!win_dark_init(&params.ssid_filter, params.ssid_filter_neg, &params.nlm_filter, params.nlm_filter_neg))
{
DLOG_ERR("win_dark_init failed. win32 error %u (0x%08X)\n", w_win32_error, w_win32_error);
res=w_win32_error; goto ex;
@@ -1117,7 +1116,7 @@ static char* item_name(char **str)
static struct blob_item *load_blob_to_collection(const char *filename, struct blob_collection_head *blobs, size_t max_size, size_t size_reserve)
{
struct blob_item *blob = blob_collection_add(blobs);
struct blob_item *blob;
uint8_t *p;
char *name;
@@ -1159,6 +1158,7 @@ static struct blob_item *load_blob_to_collection(const char *filename, struct bl
free(name);
exit_clean(1);
}
blob = blob_collection_add(blobs);
if (!blob || (!(blob->data = malloc(max_size + size_reserve))))
{
free(name);
@@ -1864,7 +1864,9 @@ static void exithelp(void)
" --wf-save=<filename>\t\t\t\t\t; save windivert filter string to a file and exit\n"
"\nLOGICAL NETWORK FILTER:\n"
" --ssid-filter=ssid1[,ssid2,ssid3,...]\t\t\t; enable winws2 only if any of specified wifi SSIDs connected\n"
" --ssid-filter-neg=[0|1]\t\t\t\t; invert SSID filter\n"
" --nlm-filter=net1[,net2,net3,...]\t\t\t; enable winws2 only if any of specified NLM network is connected. names and GUIDs are accepted.\n"
" --nlm-filter-neg=[0|1]\t\t\t\t\t; invert NLM filter\n"
" --nlm-list[=all]\t\t\t\t\t; list Network List Manager (NLM) networks. connected only or all.\n"
#endif
"\nDESYNC ENGINE INIT:\n"
@@ -1887,6 +1889,10 @@ static void exithelp(void)
" --filter-l7=proto[,proto]\t\t\t\t; L6-L7 protocol filter : %s\n"
#ifdef HAS_FILTER_SSID
" --filter-ssid=ssid1[,ssid2,ssid3,...]\t\t\t; per profile wifi SSID filter\n"
" --filter-ssid-neg=[0|1]\t\t\t\t; invert SSID filter\n"
#endif
#ifdef __linux__
" --filter-mark=mark[/mask]\t\t\t\t; filter by fwmark. mark and mask can be decimal or 0xHEX\n"
#endif
" --ipset=<filename>\t\t\t\t\t; ipset include filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)\n"
" --ipset-ip=<ip_list>\t\t\t\t\t; comma separated fixed subnet list\n"
@@ -1908,8 +1914,8 @@ static void exithelp(void)
" --hostlist-auto-debug=<logfile>\t\t\t; debug auto hostlist positives (global parameter)\n"
"\nLUA PACKET PASS MODE:\n"
" --payload=type[,type]\t\t\t\t\t; set payload types following LUA functions should process : %s\n"
" --out-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>]\t; set outgoing packet range for following LUA functions. '-' - include end pos, '<' - not include. prefix meaning : n - packet number, d - data packet number, s - relative sequence, p - data position relative sequence, b - byte count, x - never, a - always\n"
" --in-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>]\t; set incoming packet range for following LUA functions. '-' - include end pos, '<' - not include. prefix meaning : n - packet number, d - data packet number, s - relative sequence, p - data position relative sequence, b - byte count, x - never, a - always\n"
" --out-range=<mode>[<int>][(-|<)<mode>[<int>]]\t\t; set outgoing packet range for following LUA functions. '-' - include end pos, '<' - not include. mode meaning : n - packet number, d - data packet number, s - relative sequence, p - data position relative sequence, b - byte count, x - never, a - always\n"
" --in-range=<mode>[<int>][(-|<)<mode>[<int>]]\t\t; set incoming packet range for following LUA functions. '-' - include end pos, '<' - not include. mode meaning : n - packet number, d - data packet number, s - relative sequence, p - data position relative sequence, b - byte count, x - never, a - always\n"
"\nLUA DESYNC ACTION:\n"
" --lua-desync=<functon>[:param1=val1[:param2=val2]]\t; call LUA function when packet received\n",
#if defined(__linux__) || defined(SO_USER_COOKIE)
@@ -2039,6 +2045,10 @@ enum opt_indices {
IDX_FILTER_L7,
#ifdef HAS_FILTER_SSID
IDX_FILTER_SSID,
IDX_FILTER_SSID_NEG,
#endif
#ifdef __linux__
IDX_FILTER_MARK,
#endif
IDX_IPSET,
IDX_IPSET_IP,
@@ -2070,7 +2080,9 @@ enum opt_indices {
IDX_WF_DUP_CHECK,
IDX_WF_SAVE,
IDX_SSID_FILTER,
IDX_SSID_FILTER_NEG,
IDX_NLM_FILTER,
IDX_NLM_FILTER_NEG,
IDX_NLM_LIST,
#endif
IDX_LAST
@@ -2141,6 +2153,10 @@ static const struct option long_options[] = {
[IDX_FILTER_L7] = {"filter-l7", required_argument, 0, 0},
#ifdef HAS_FILTER_SSID
[IDX_FILTER_SSID] = {"filter-ssid", required_argument, 0, 0},
[IDX_FILTER_SSID_NEG] = {"filter-ssid-neg", optional_argument, 0, 0},
#endif
#ifdef __linux__
[IDX_FILTER_MARK] = {"filter-mark", required_argument, 0, 0},
#endif
[IDX_IPSET] = {"ipset", required_argument, 0, 0},
[IDX_IPSET_IP] = {"ipset-ip", required_argument, 0, 0},
@@ -2172,7 +2188,9 @@ static const struct option long_options[] = {
[IDX_WF_SAVE] = {"wf-save", required_argument, 0, 0},
[IDX_WF_DUP_CHECK] = {"wf-dup-check", optional_argument, 0, 0},
[IDX_SSID_FILTER] = {"ssid-filter", required_argument, 0, 0},
[IDX_SSID_FILTER_NEG] = {"ssid-filter-neg", optional_argument, 0, 0},
[IDX_NLM_FILTER] = {"nlm-filter", required_argument, 0, 0},
[IDX_NLM_FILTER_NEG] = {"nlm-filter-neg", optional_argument, 0, 0},
[IDX_NLM_LIST] = {"nlm-list", optional_argument, 0, 0},
#endif
[IDX_LAST] = {NULL, 0, NULL, 0},
@@ -2218,7 +2236,7 @@ static void WinSetIcon(void)
#endif
#endif
enum {WF_TCP_IN, WF_UDP_IN, WF_TCP_OUT, WF_UDP_OUT, WF_ICMP_IN, WF_ICMP_OUT, WF_IPP_IN, WF_IPP_OUT, WF_RAW, WF_RAWF_PART, WF_RAWF_FILTER, GLOBAL_SSID_FILTER, GLOBAL_NLM_FILTER, WF_RAWF, WF_COUNT} t_wf_index;
enum {WF_TCP_IN, WF_UDP_IN, WF_TCP_OUT, WF_UDP_OUT, WF_ICMP_IN, WF_ICMP_OUT, WF_IPP_IN, WF_IPP_OUT, WF_RAW, WF_RAWF_PART, WF_RAWF_FILTER, GLOBAL_SSID_FILTER, GLOBAL_SSID_FILTER_NEG, GLOBAL_NLM_FILTER, GLOBAL_NLM_FILTER_NEG, WF_RAWF, WF_COUNT} t_wf_index;
int main(int argc, char **argv)
{
#ifdef __CYGWIN__
@@ -2513,7 +2531,7 @@ int main(int argc, char **argv)
#endif
#if defined(__linux__) || defined(SO_USER_COOKIE)
params.desync_fwmark = 0;
if (sscanf(optarg, "0x%X", &params.desync_fwmark) <= 0) sscanf(optarg, "%u", &params.desync_fwmark);
if (sscanf(optarg, "0x%X", &params.desync_fwmark)!=1) sscanf(optarg, "%u", &params.desync_fwmark);
if (!params.desync_fwmark)
{
DLOG_ERR("fwmark/sockarg should be decimal or 0xHEX and should not be zero\n");
@@ -2842,7 +2860,30 @@ int main(int argc, char **argv)
}
params.filter_ssid_present = true;
break;
case IDX_FILTER_SSID_NEG:
dp->filter_ssid_neg = !optarg || atoi(optarg);
break;
#endif
#ifdef __linux__
case IDX_FILTER_MARK:
{
char *smask = strchr(optarg,'/');
if (smask) *smask++=0;
if (sscanf(optarg, "0x%X", &dp->filter_mark)!=1 && sscanf(optarg, "%u", &dp->filter_mark)!=1) goto fmark_err;
if (smask)
{
if (sscanf(smask, "0x%X", &dp->filter_mark_mask)!=1 && sscanf(smask, "%u", &dp->filter_mark_mask)!=1) goto fmark_err;
}
else
// no mask - use the whole 32-bit value
dp->filter_mark_mask=0xFFFFFFFF;
break;
fmark_err:
DLOG_ERR("filter mark or mask format error\n");
exit_clean(1);
}
#endif
case IDX_IPSET:
if (bSkip) break;
if (!RegisterIpset(dp, false, optarg))
@@ -3079,6 +3120,9 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case IDX_SSID_FILTER_NEG:
hash_wf[GLOBAL_SSID_FILTER_NEG] = params.ssid_filter_neg = !optarg || atoi(optarg);
break;
case IDX_NLM_FILTER:
hash_wf[GLOBAL_NLM_FILTER] = hash_jen(optarg, strlen(optarg));
if (!parse_strlist(optarg, &params.nlm_filter))
@@ -3087,6 +3131,9 @@ int main(int argc, char **argv)
exit_clean(1);
}
break;
case IDX_NLM_FILTER_NEG:
hash_wf[GLOBAL_NLM_FILTER_NEG] = params.nlm_filter_neg = !optarg || atoi(optarg);
break;
case IDX_NLM_LIST:
if (!nlm_list(optarg && !strcmp(optarg, "all")))
{
+5 -1
View File
@@ -72,6 +72,10 @@ struct desync_profile
// annot use global filter because it's not possible to bind multiple instances to a single queue
// it's possible to run multiple winws2 instances on the same windivert filter, but it's not the case for linux
struct str_list_head filter_ssid;
bool filter_ssid_neg;
#endif
#ifdef __linux__
uint32_t filter_mark,filter_mark_mask;
#endif
// list of pointers to ipsets
@@ -96,7 +100,6 @@ struct desync_profile
bool b_hostlist_auto_retrans_maxseq, b_hostlist_auto_incoming_maxseq, b_hostlist_auto_retrans_reset;
bool b_hostlist_auto_udp_out, b_hostlist_auto_udp_in;
bool b_filter_l3, b_filter_l7;
};
#define PROFILE_NAME(dp) ((dp)->name ? (dp)->name : "noname")
@@ -149,6 +152,7 @@ struct params_s
#ifdef __CYGWIN__
struct str_list_head ssid_filter,nlm_filter;
bool ssid_filter_neg, nlm_filter_neg;
struct str_list_head wf_raw_part;
char *windivert_filter;
+3 -3
View File
@@ -47,7 +47,7 @@ t_l7proto l7proto_from_name(const char *name)
}
bool l7_proto_match(t_l7proto l7proto, uint64_t filter_l7)
{
return filter_l7==L7_ALL || (filter_l7 & (1ULL<<l7proto)) || (filter_l7 & (1ULL<<L7_KNOWN)) && l7proto>L7_KNOWN && l7proto<L7_LAST;
return (filter_l7==L7_ALL) || (filter_l7 & (1ULL<<l7proto)) || (filter_l7 & (1ULL<<L7_KNOWN)) && (l7proto>L7_KNOWN) && (l7proto<L7_LAST);
}
static const char *l7payload_name[] = {
@@ -76,7 +76,7 @@ const char *l7payload_str(t_l7payload l7)
}
bool l7_payload_match(t_l7payload l7payload, uint64_t filter_l7p)
{
return filter_l7p==L7P_ALL || (filter_l7p & (1ULL<<l7payload)) || (filter_l7p & (1ULL<<L7P_KNOWN)) && l7payload>L7P_KNOWN && l7payload<L7P_LAST;
return (filter_l7p==L7P_ALL) || (filter_l7p & (1ULL<<l7payload)) || (filter_l7p & (1ULL<<L7P_KNOWN)) && (l7payload>L7P_KNOWN) && (l7payload<L7P_LAST);
}
bool l7_payload_str_list(uint64_t l7p, char *buf, size_t size)
{
@@ -385,7 +385,7 @@ bool HttpReplyLooksLikeDPIRedirect(const uint8_t *data, size_t len, const char *
// somethinkg like : censor.net/badpage.php?reason=denied&source=RKN
for(p=redirect_host; *p && *p!='/' ; p++);
for(p=redirect_host; *p && *p!='/' && *p!='?' && *p!='#' ; p++);
*p=0;
if (!*redirect_host) return false;
+10 -2
View File
@@ -14,6 +14,7 @@
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <linux/securebits.h>
#include <linux/seccomp.h>
#include <linux/filter.h>
// __X32_SYSCALL_BIT defined in linux/unistd.h
@@ -266,8 +267,15 @@ bool droproot(uid_t uid, const char *user, const gid_t *gid, int gid_count)
#ifdef __linux__
if (prctl(PR_SET_KEEPCAPS, 1L))
{
DLOG_PERROR("prctl(PR_SET_KEEPCAPS)");
return false;
int e=errno;
// if SECBIT_NO_SETUID_FIXUP is set PR_SET_KEEPCAPS is not necessary
int bits = prctl(PR_GET_SECUREBITS);
if (bits<0 || !(bits & SECBIT_NO_SETUID_FIXUP))
{
errno=e;
DLOG_PERROR("prctl(PR_SET_KEEPCAPS)");
return false;
}
}
#endif
if (user)
+1 -1
View File
@@ -103,7 +103,7 @@ again:
{
if (bt >= elem->bt_next)
{
if (name = strdup(elem->str))
if ((name = strdup(elem->str)))
{
n = elem->n;