mirror of
https://github.com/bol-van/zapret2.git
synced 2026-10-29 22:19:22 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8283f4cd76 | ||
|
|
019bbe845f | ||
|
|
9679d58499 | ||
|
|
ca7ed6a986 | ||
|
|
c460884213 | ||
|
|
3570f193d5 | ||
|
|
8be19e079d | ||
|
|
4a33055448 | ||
|
|
221d7dff24 | ||
|
|
f94b562482 | ||
|
|
6ec7ee67ce | ||
|
|
98757082b8 | ||
|
|
5786448c93 | ||
|
|
a0028b9895 | ||
|
|
486626d28f | ||
|
|
f8d08b8436 | ||
|
|
a4515beb32 | ||
|
|
299f833f39 | ||
|
|
a0c928be45 | ||
|
|
f256fea2d8 | ||
|
|
3a6f75cd3c | ||
|
|
44fbf1b18e | ||
|
|
a1adf7b868 | ||
|
|
ca950d838a | ||
|
|
873642573e | ||
|
|
e41e4f18c7 | ||
|
|
03ce20156a | ||
|
|
00f5aaa36d | ||
|
|
1e329918dc | ||
|
|
8a398236ac | ||
|
|
074c981a37 | ||
|
|
03c139f34a | ||
|
|
cdcb8d4711 | ||
|
|
970adb5d55 | ||
|
|
6535202a9a | ||
|
|
9bff218b9a | ||
|
|
3e3b902902 | ||
|
|
0f65389571 | ||
|
|
ec25da2277 | ||
|
|
0d2227caf6 | ||
|
|
eb42084b30 | ||
|
|
3b6f0cc345 | ||
|
|
28b6fb853e | ||
|
|
bf3b30af9c | ||
|
|
34a95e8a41 | ||
|
|
bf64220ab8 | ||
|
|
adf7791367 | ||
|
|
1ea7d10f6f | ||
|
|
859c24d32b | ||
|
|
5df635efdb | ||
|
|
7119ffce54 | ||
|
|
2efdb70b1c | ||
|
|
692139aeea | ||
|
|
6b6c63e338 | ||
|
|
5fab86344a | ||
|
|
c1e8e7c0d9 | ||
|
|
d9fde580e6 | ||
|
|
5ebfa6c0dc | ||
|
|
a1bca5a85e | ||
|
|
3122b66190 | ||
|
|
0b8182d24a | ||
|
|
8b50962e66 | ||
|
|
a7ff6410d6 | ||
|
|
6e186a3b38 | ||
|
|
36355d46e3 | ||
|
|
2f8948d0eb | ||
|
|
68514ed871 | ||
|
|
204e95ad0e | ||
|
|
3c93f925be | ||
|
|
07c69bf132 | ||
|
|
2080981cea | ||
|
|
c4e4fff4a0 | ||
|
|
1c5d49699c | ||
|
|
a0be7cbb40 | ||
|
|
e263849b1b | ||
|
|
46010c4a42 | ||
|
|
b08ae78778 | ||
|
|
49e2d438d8 | ||
|
|
a8d24607a5 | ||
|
|
5e84833269 | ||
|
|
fbeb124cf8 | ||
|
|
3f87539d7c | ||
|
|
032651deeb | ||
|
|
bdad7d7be1 | ||
|
|
86bcc7db16 | ||
|
|
569297c623 | ||
|
|
2c21faa80e | ||
|
|
90097be91a | ||
|
|
c61706cc47 | ||
|
|
5f2807e0c5 | ||
|
|
dd85b1f852 | ||
|
|
b93b84791d | ||
|
|
4668664ee7 | ||
|
|
78bbd0ee51 | ||
|
|
e185d5ce60 | ||
|
|
d55fe738eb | ||
|
|
a8399a59fe | ||
|
|
37e5a8a880 | ||
|
|
3b465d9981 | ||
|
|
11bc8f4fd1 | ||
|
|
80bb86e833 | ||
|
|
d7e6e3fb60 | ||
|
|
ea047535b9 | ||
|
|
a3202599aa | ||
|
|
b8f142253b | ||
|
|
ccb6e9b534 | ||
|
|
3d4828d61f | ||
|
|
0020a8de6a | ||
|
|
6f63dbc3a8 | ||
|
|
bb042f734e | ||
|
|
e6b22f3868 | ||
|
|
c73af75881 | ||
|
|
889d120235 | ||
|
|
b78b52c4cd | ||
|
|
1d85620abd | ||
|
|
4b9b938db2 | ||
|
|
43a18c7eb7 | ||
|
|
49c8dfedc9 | ||
|
|
8a0f53f3cf | ||
|
|
8d34a4b7a2 | ||
|
|
bc6b8c64a4 | ||
|
|
c5325fc267 | ||
|
|
d1b80a4254 | ||
|
|
4759d64c27 | ||
|
|
d9acc873fa | ||
|
|
4e5efbf2b2 | ||
|
|
b35db6aac5 | ||
|
|
c54533bbb2 | ||
|
|
8afe88dea7 | ||
|
|
a599b4016b | ||
|
|
d2c85ec8b7 | ||
|
|
15bc21d75b | ||
|
|
4df9449786 | ||
|
|
9474a64d27 | ||
|
|
31904a564e | ||
|
|
fd9896846a | ||
|
|
2aa5d9ee37 | ||
|
|
da3d28f467 | ||
|
|
5e2b6d8bde | ||
|
|
08d27bf0a2 | ||
|
|
d214ebf2de | ||
|
|
fe8b8df2ae | ||
|
|
441d2e3cfc | ||
|
|
b37f17e623 | ||
|
|
7b82744d4d | ||
|
|
211867eca5 | ||
|
|
c90e0e9a7f | ||
|
|
fd4716da54 | ||
|
|
5dcb6c1163 | ||
|
|
fe112c1f22 | ||
|
|
850a9920f6 | ||
|
|
dc99547ff0 | ||
|
|
8d08b4f9a4 | ||
|
|
860b13c86e | ||
|
|
a154c223f4 | ||
|
|
7a69d56a4b | ||
|
|
b3be87fd7c | ||
|
|
62b86c3899 | ||
|
|
7db50c018a | ||
|
|
eb35bb34f5 | ||
|
|
d0c9fa9b76 | ||
|
|
7441ed7119 | ||
|
|
bbaeeb73fe | ||
|
|
b1afa7079f | ||
|
|
01a4ee63d4 | ||
|
|
5aefa8610b | ||
|
|
aa226e6e7a | ||
|
|
65bb7ac639 | ||
|
|
62dcf80867 | ||
|
|
7661f29ede | ||
|
|
ce3c1600da | ||
|
|
640a73d25c | ||
|
|
787e5244ad | ||
|
|
d42aea6a9f | ||
|
|
e0b48c54db | ||
|
|
0f63e09d64 | ||
|
|
bca26682e7 | ||
|
|
ee24e84b85 | ||
|
|
9584e29189 | ||
|
|
d4aa4cc5b1 |
No files matched your search
@@ -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
|
||||
|
||||
@@ -11,3 +11,4 @@ ipset/zapret-ip*.txt
|
||||
ipset/zapret-ip*.gz
|
||||
ipset/zapret-hosts*.txt
|
||||
ipset/zapret-hosts*.gz
|
||||
.vscode/
|
||||
@@ -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,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.
|
||||
@@ -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
|
||||
|
||||
@@ -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 ]
|
||||
}
|
||||
|
||||
@@ -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 ]
|
||||
|
||||
@@ -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 ]
|
||||
}
|
||||
|
||||
@@ -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 ]
|
||||
}
|
||||
|
||||
+225
-40
@@ -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,29 +1006,70 @@ pktws_ipt_unprepare_udp()
|
||||
pktws_ipt_unprepare udp $1
|
||||
}
|
||||
|
||||
pktws_ipt_sweep()
|
||||
{
|
||||
# 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
|
||||
;;
|
||||
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=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
|
||||
"$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=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
|
||||
"$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)
|
||||
# allow multiple PKTWS instances with the same wf filter but different ipset
|
||||
# some methods require empty acks
|
||||
"$WINWS2" --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 &
|
||||
# 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=
|
||||
}
|
||||
}
|
||||
@@ -1026,7 +1133,7 @@ curl_test()
|
||||
[ $REPEATS -gt 1 ] && echo 'AVAILABLE'
|
||||
else
|
||||
code=$?
|
||||
[ "$SCANLEVEL" = quick ] && break
|
||||
[ "$SCANLEVEL" = quick -o "$SCANLEVEL" = veryquick ] && break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@@ -1089,7 +1196,7 @@ pktws_curl_test()
|
||||
}
|
||||
strategy_append_extra_pktws()
|
||||
{
|
||||
strategy="${strategy:+${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }$strategy${PKTWS_EXTRA_POST:+ $PKTWS_EXTRA_POST}${PKTWS_EXTRA_1:+ "$PKTWS_EXTRA_POST_1"}${PKTWS_EXTRA_POST_2:+ "$PKTWS_EXTRA_POST_2"}${PKTWS_EXTRA_POST_3:+ "$PKTWS_EXTRA_POST_3"}${PKTWS_EXTRA_POST_4:+ "$PKTWS_EXTRA_POST_4"}${PKTWS_EXTRA_POST_5:+ "$PKTWS_EXTRA_POST_5"}${PKTWS_EXTRA_POST_6:+ "$PKTWS_EXTRA_POST_6"}${PKTWS_EXTRA_POST_7:+ "$PKTWS_EXTRA_POST_7"}${PKTWS_EXTRA_POST_8:+ "$PKTWS_EXTRA_POST_8"}${PKTWS_EXTRA_POST_9:+ "$PKTWS_EXTRA_POST_9"}}"
|
||||
strategy="${strategy:+${PKTWS_EXTRA_PRE:+$PKTWS_EXTRA_PRE }${PKTWS_EXTRA_PRE_1:+"$PKTWS_EXTRA_PRE_1" }${PKTWS_EXTRA_PRE_2:+"$PKTWS_EXTRA_PRE_2" }${PKTWS_EXTRA_PRE_3:+"$PKTWS_EXTRA_PRE_3" }${PKTWS_EXTRA_PRE_4:+"$PKTWS_EXTRA_PRE_4" }${PKTWS_EXTRA_PRE_5:+"$PKTWS_EXTRA_PRE_5" }${PKTWS_EXTRA_PRE_6:+"$PKTWS_EXTRA_PRE_6" }${PKTWS_EXTRA_PRE_7:+"$PKTWS_EXTRA_PRE_7" }${PKTWS_EXTRA_PRE_8:+"$PKTWS_EXTRA_PRE_8" }${PKTWS_EXTRA_PRE_9:+"$PKTWS_EXTRA_PRE_9" }$strategy${PKTWS_EXTRA_POST:+ $PKTWS_EXTRA_POST}${PKTWS_EXTRA_POST_1:+ "$PKTWS_EXTRA_POST_1"}${PKTWS_EXTRA_POST_2:+ "$PKTWS_EXTRA_POST_2"}${PKTWS_EXTRA_POST_3:+ "$PKTWS_EXTRA_POST_3"}${PKTWS_EXTRA_POST_4:+ "$PKTWS_EXTRA_POST_4"}${PKTWS_EXTRA_POST_5:+ "$PKTWS_EXTRA_POST_5"}${PKTWS_EXTRA_POST_6:+ "$PKTWS_EXTRA_POST_6"}${PKTWS_EXTRA_POST_7:+ "$PKTWS_EXTRA_POST_7"}${PKTWS_EXTRA_POST_8:+ "$PKTWS_EXTRA_POST_8"}${PKTWS_EXTRA_POST_9:+ "$PKTWS_EXTRA_POST_9"}}"
|
||||
}
|
||||
|
||||
|
||||
@@ -1172,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
|
||||
@@ -1576,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
|
||||
@@ -1591,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 ] || {
|
||||
@@ -1624,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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1826,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
|
||||
@@ -1841,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
|
||||
@@ -1863,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
|
||||
@@ -1889,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
|
||||
|
||||
@@ -1903,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\"".
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -212,7 +212,8 @@ check_system()
|
||||
OPENWRT_PACKAGER=opkg
|
||||
OPENWRT_PACKAGER_INSTALL="opkg install"
|
||||
OPENWRT_PACKAGER_UPDATE="opkg update"
|
||||
exists apk && {
|
||||
# do not use apk if it does not have database
|
||||
[ -d /lib/apk/db ] && exists apk && {
|
||||
OPENWRT_PACKAGER=apk
|
||||
OPENWRT_PACKAGER_INSTALL="apk add"
|
||||
OPENWRT_PACKAGER_UPDATE=
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2016-2025 bol-van
|
||||
Copyright (c) 2016-2026 bol-van
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -284,3 +284,72 @@ v0.9.4.3
|
||||
0.9.5
|
||||
|
||||
* nfqws2: timers
|
||||
|
||||
0.9.5.1
|
||||
|
||||
* nfqws2: timer_info, timer_enum
|
||||
|
||||
0.9.5.2
|
||||
|
||||
* nfqws2: rewrite time counting algorithm, remove --timer-res
|
||||
|
||||
1.0
|
||||
|
||||
* zapret-lib,zapret-antidpi: delay parameter in "send" desync function
|
||||
* zapret-lib: fix oob urp=b case in rawsend_dissect_segmented
|
||||
* nfqws2, zapret-lib: (LUA_COMPAT_VER=6) fix wrong spelling : WRITEABLE -> WRITABLE
|
||||
|
||||
1.0.1
|
||||
|
||||
* install_easy: do not use apk in openwrt if it doesn't have db in /lib/apk/db
|
||||
* nfqws2: fix UAF if a timer is deleted in timer function
|
||||
|
||||
1.0.2
|
||||
|
||||
* nfqws2: fix 60 msec lua-gc default to 60 sec
|
||||
|
||||
1.0.3
|
||||
|
||||
* nfqws2: accept string or function in timer_set(). remember function as a function, not as a string.
|
||||
* 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
|
||||
@@ -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
|
||||
@@ -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,17 +11,19 @@ dl_deps()
|
||||
(
|
||||
cd "$DEPS"
|
||||
exists_dir libnfnetlink-* ||
|
||||
curl -Lo - https://www.netfilter.org/pub/libnfnetlink/libnfnetlink-1.0.2.tar.bz2 | tar -xj || exit 5
|
||||
curl $CURL_OPT -Lo - https://www.netfilter.org/pub/libnfnetlink/libnfnetlink-1.0.2.tar.bz2 | tar -xj || exit 5
|
||||
exists_dir libmnl-* ||
|
||||
curl -Lo - https://www.netfilter.org/pub/libmnl/libmnl-1.0.5.tar.bz2 | tar -xj || exit 5
|
||||
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 -Lo - https://www.netfilter.org/pub/libnetfilter_queue/libnetfilter_queue-1.0.5.tar.bz2 | tar -xj || exit 5
|
||||
exists_dir zlib-* ||
|
||||
curl -Lo - https://zlib.net/fossils/zlib-1.3.1.tar.gz | tar -xz || exit 5
|
||||
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-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 -Lo - https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz | tar -xz || exit 5
|
||||
curl $CURL_OPT -Lo - https://github.com/openresty/luajit2/archive/refs/tags/v${LUAJIT_RELEASE}.tar.gz | tar -xz || exit 5
|
||||
exists_dir lua-* ||
|
||||
curl -Lo - https://www.lua.org/ftp/lua-${LUA_RELEASE}.tar.gz | tar -xz || exit 5
|
||||
curl $CURL_OPT -Lo - https://www.lua.org/ftp/lua-${LUA_RELEASE}.tar.gz | tar -xz || exit 5
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,7 @@ dl_zapret1()
|
||||
fi
|
||||
(
|
||||
cd "$ZBASE"
|
||||
curl -Lo /tmp/zapret.zip "$ZURL"
|
||||
curl $CURL_OPT -Lo /tmp/zapret.zip "$ZURL"
|
||||
unzip /tmp/zapret.zip
|
||||
rm /tmp/zapret.zip
|
||||
mv zapret-${BRANCH} $ZDIR
|
||||
|
||||
@@ -22,7 +22,7 @@ dl_zapret2()
|
||||
fi
|
||||
(
|
||||
cd "$ZBASE"
|
||||
curl -Lo /tmp/zapret2.zip "$ZURL"
|
||||
curl $CURL_OPT -Lo /tmp/zapret2.zip "$ZURL"
|
||||
unzip /tmp/zapret2.zip
|
||||
rm /tmp/zapret2.zip
|
||||
mv zapret2-${BRANCH} $ZDIR
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
EXEDIR="$(dirname "$0")"
|
||||
EXEDIR="$(cd "$EXEDIR"; pwd)"
|
||||
|
||||
CURL_OPT="--connect-timeout 5 --max-time 120 --retry 5 --retry-delay 30"
|
||||
|
||||
TOOLCHAINS="$EXEDIR/toolchain"
|
||||
DEPS="$EXEDIR/deps"
|
||||
STAGE="$EXEDIR/staging"
|
||||
@@ -14,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}
|
||||
|
||||
@@ -17,6 +17,6 @@ ask_target 1
|
||||
cd "$TOOLCHAINS"
|
||||
for t in $TGT; do
|
||||
[ -d "$t" ] && rm -r "$t"
|
||||
curl -Lo - "${BASEURL}/${t}.tar.xz" | tar -Jx
|
||||
curl $CURL_OPT -Lo - "${BASEURL}/${t}.tar.xz" | tar -Jx
|
||||
done
|
||||
)
|
||||
+153
-70
@@ -34,7 +34,7 @@
|
||||
- [Passing blobs](#passing-blobs)
|
||||
- [In-profile filters](#in-profile-filters)
|
||||
- [Typical instance invocation scheme within a profile](#typical-instance-invocation-scheme-within-a-profile)
|
||||
- [Lua function prototype](#lua-function-prototype)
|
||||
- [Lua desync function prototype](#lua-desync-function-prototype)
|
||||
- [Structure of the desync table](#structure-of-the-desync-table)
|
||||
- [Dissect structure](#dissect-structure)
|
||||
- [Handling multi-packet payloads](#handling-multi-packet-payloads)
|
||||
@@ -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)
|
||||
@@ -104,6 +105,8 @@
|
||||
- [Timer control](#timer-control)
|
||||
- [timer_set](#timer_set)
|
||||
- [timer_del](#timer_del)
|
||||
- [timer_info](#timer_info)
|
||||
- [timer_enum](#timer_enum)
|
||||
- [zapret-lib.lua base function library](#zapret-liblua-base-function-library)
|
||||
- [Base desync functions](#base-desync-functions)
|
||||
- [luaexec](#luaexec)
|
||||
@@ -160,6 +163,7 @@
|
||||
- [rawsend_payload_segmented](#rawsend_payload_segmented)
|
||||
- [Standard direction and payload filters](#standard-direction-and-payload-filters)
|
||||
- [Working with multi-packet payloads](#working-with-multi-packet-payloads)
|
||||
- [Timer helpers](#timer-helpers)
|
||||
- [Orchestration](#orchestration)
|
||||
- [instance_cutoff_shim](#instance_cutoff_shim)
|
||||
- [cutoff_shim_check](#cutoff_shim_check)
|
||||
@@ -348,7 +352,7 @@ Once a profile is selected, what constitutes its action-oriented logic? Actions
|
||||
|
||||
[in-profile filters](#in-profile-filters) are also available. There are three types: the `--payload` filter (a list of payloads accepted by the instance) and two range filters, `--in-range` and `--out-range`, which define the specific byte range within the stream that the instance should process. Once defined, in-profile filters apply to all subsequent instances until they are redefined. The primary purpose of these filters is to minimize relatively slow Lua calls by offloading as much decision-making as possible to the C-side code.
|
||||
|
||||
When a packet reaches a Lua instance, the function receives two [parameters](#lua-function-prototype): `ctx` and `desync`. `ctx` provides a context for interacting with specific C-side functions. The [`desync`](#structure-of-the-desync-table) parameter is a table containing various attributes of the packet being processed. Most notably, it includes the [dissect](#dissect-structure) (the `dis` subtable) and information from the conntrack entry (the [track subtable](#the-track-table-structure)). Numerous other parameters can be inspected by executing [`var_debug(desync)`](#var_debug) or by using the pre-built [`pktdebug`](#pktdebug) instance.
|
||||
When a packet reaches a Lua instance, the function receives two [parameters](#lua-desync-function-prototype): `ctx` and `desync`. `ctx` provides a context for interacting with specific C-side functions. The [`desync`](#structure-of-the-desync-table) parameter is a table containing various attributes of the packet being processed. Most notably, it includes the [dissect](#dissect-structure) (the `dis` subtable) and information from the conntrack entry (the [track subtable](#the-track-table-structure)). Numerous other parameters can be inspected by executing [`var_debug(desync)`](#var_debug) or by using the pre-built [`pktdebug`](#pktdebug) instance.
|
||||
|
||||
During the [replay](#handling-multi-packet-payloads) of delayed packets, the Lua instance receives details such as the part number, the total number of parts in the original message, the current part's position, and [reasm](#handling-multi-packet-payloads) or [decrypt](#handling-multi-packet-payloads) data if available.
|
||||
|
||||
@@ -651,10 +655,9 @@ General parameters for all versions - nfqws2, dvtws2, winws2.
|
||||
--ipcache-lifetime=<int> ; IP cache entry lifetime in seconds. 0 - unlimited.
|
||||
--ipcache-hostname=[0|1] ; 1 or no argument enables hostname caching for use in zero-phase strategies
|
||||
--reasm-disable=[type[,type]] ; disable fragment reassembly for a list of payloads: tls_client_hello quic_initial. without arguments - disable reasm for everything.
|
||||
--timer-res=msec ; timer resolution in msec. default - 50
|
||||
|
||||
DESYNC ENGINE INIT:
|
||||
--writeable[=<dir_name>] ; create a directory for Lua with write permissions and store its path in the "WRITEABLE" env variable (only one directory)
|
||||
--writable[=<dir_name>] ; create a directory for Lua with write permissions and store its path in the "WRITABLE" env variable (only one directory)
|
||||
--blob=<item_name>:[+ofs]@<filename>|0xHEX ; load a binary file or hex string into the Lua variable <item_name>. +ofs specifies the offset from the start of the file
|
||||
--lua-init=@<filename>|<lua_text> ; execute Lua code from a string or file once at startup. supports gzipped files. automatically checks for "<filename>.gz"
|
||||
--lua-gc=<int> ; Lua garbage collector invocation interval in seconds. 0 disables periodic calls.
|
||||
@@ -693,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
|
||||
@@ -709,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:
|
||||
@@ -742,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.
|
||||
```
|
||||
|
||||
@@ -913,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.
|
||||
@@ -1016,13 +1025,13 @@ Windows:
|
||||
- All `Se*` privileges are irrevocably removed from the token, except for `SeChangeNotifyPrivilege`.
|
||||
- A Job object is used to prohibit the creation of child processes and restrict desktop interaction - clipboard access, changing desktop settings, changing display settings, etc.
|
||||
|
||||
There is a simple way to pass a writable directory to the Lua code using the `--writeable[=<dirname>]` parameter. `nfqws2` creates the directory and assigns permissions so that the Lua code can write files there, then passes the directory name in the `WRITEABLE` environment variable. If `dirname` is not specified, a directory is created within `%USERPROFILE%/AppData/LocalLow` on Windows.
|
||||
There is a simple way to pass a writable directory to the Lua code using the `--writable[=<dirname>]` parameter. `nfqws2` creates the directory and assigns permissions so that the Lua code can write files there, then passes the directory name in the `WRITABLE` environment variable. If `dirname` is not specified, a directory is created within `%USERPROFILE%/AppData/LocalLow` on Windows.
|
||||
|
||||
On the Lua side, dangerous functions are removed: `os.execute`, `io.popen`, `package.loadlib`, and the `debug` module. On GitHub, `nfqws2` executables are built with a version of LuaJIT that excludes FFI.
|
||||
|
||||
## 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]`.
|
||||
@@ -1031,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.
|
||||
|
||||
@@ -1046,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.
|
||||
|
||||
@@ -1097,7 +1111,7 @@ The specific mechanics of these functions are less important here; the focus is
|
||||
- The `--payload` directive applies to the two instances following it.
|
||||
- The line `--lua-desync=fake:blob=fake_default_tls:badsum:strategy=1` calls the `fake` function with three arguments: `blob`, `badsum`, and `strategy`. The value for the `badsum` argument is an empty string.
|
||||
|
||||
## Lua function prototype
|
||||
## Lua desync function prototype
|
||||
|
||||
A standard Lua function uses the following prototype:
|
||||
|
||||
@@ -1605,15 +1619,6 @@ nfqws2 is a single-threaded program, like the Lua engine. Timers are called in t
|
||||
In Linux and Windows, packets are received in blocks, not one at a time. A block is processed until all remaining packets are exhausted.
|
||||
Timers are called between block processing. If processing takes a significant amount of time, the timer call may not be perfectly timed.
|
||||
|
||||
It's important to remember the timer resolution specified with the `--timer-res` parameter. The default is 50 ms.
|
||||
This means that the timer call check can be performed no more frequently than the specified interval.
|
||||
If you set the period to 70 ms, the timer will be called staggered. +70 will be called at 100, +140 at 150, and +210 at 250.
|
||||
Therefore, it's advisable to set the period to a multiple of the timer resolution.
|
||||
Increasing the resolution (decreasing `--timer-res`) may slightly increase CPU load,
|
||||
since the processor will wake up every specified period to check all timers.
|
||||
The more timers, the higher CPU load for checking.
|
||||
The maximum resolution is 100 checks per second, which corresponds to `--timer-res=10`.
|
||||
|
||||
Timers can be useful for handling unreplied packets.
|
||||
For example, you need to send something somewhere and read the response. But the other party might not respond or the response might not arrive.
|
||||
To prevent your system from hanging in an undefined state and leaving garbage in memory, a timer can help.
|
||||
@@ -1621,6 +1626,10 @@ Using timers, desync functions, and send functions, you can build a full-fledged
|
||||
The system should be designed asynchronously, using a state machine. Direct sleep delays is not the option, as they break the queue-based traffic processing scheme.
|
||||
While you're waiting, everything else will hang.
|
||||
|
||||
In the `--intercept=0` mode, if there are timers, nfqws2 doesn't exit immediately, but calls the timers until there are none left.
|
||||
After this, the process terminates.
|
||||
|
||||
|
||||
### Timer function prototype
|
||||
|
||||
```
|
||||
@@ -1700,8 +1709,8 @@ Before executing `--lua-init`, the C code sets up base constants, blobs, and C f
|
||||
|
||||
| env | Purpose |
|
||||
| :-------- |:---------- |
|
||||
| WRITEABLE | A directory writable by Lua. Corresponds to the `--writeable` option. |
|
||||
| APPDATALOW | (Windows only) The AppData location for the low mandatory level. This is also writable, but using `--writeable` is preferred for cross-platform compatibility. |
|
||||
| WRITABLE | A directory writable by Lua. Corresponds to the `--writable` option. |
|
||||
| APPDATALOW | (Windows only) The AppData location for the low mandatory level. This is also writable, but using `--writable` is preferred for cross-platform compatibility. |
|
||||
|
||||
## C functions
|
||||
|
||||
@@ -2147,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
|
||||
|
||||
```
|
||||
@@ -2184,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
|
||||
|
||||
@@ -2226,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.
|
||||
|
||||
```
|
||||
@@ -2427,8 +2451,8 @@ function timer_set(name, func, period, oneshot, data)
|
||||
```
|
||||
|
||||
* name - a unique timer name. If a timer with this name already exists, it is deleted and replaced with a new one. When replaced, the countdown restarts.
|
||||
* func - the timer function name (string)
|
||||
* period - the timer call frequency in milliseconds (see [timer resolution](#timers))
|
||||
* func - the timer function. string or function. if it's a string function is resolved during timer_set call and remembered as a function, not as a string.
|
||||
* period - the timer call frequency in milliseconds
|
||||
* oneshot - a bool indicating whether the timer is single-shot (true) or periodic (false)
|
||||
* data - an arbitrary variable passed to the [timer-function](#timer-function-prototype)
|
||||
|
||||
@@ -2440,6 +2464,35 @@ Delete the timer.
|
||||
function timer_del(name)
|
||||
```
|
||||
|
||||
#### timer_info
|
||||
|
||||
Get information about the timer identified by name.
|
||||
|
||||
```
|
||||
function timer_info(name)
|
||||
```
|
||||
|
||||
Returns table in case of success, nil otherwise.
|
||||
|
||||
|
||||
| Field | Type | Description |
|
||||
| :------ | :----- | :------------------ |
|
||||
| name | string | unique timer name |
|
||||
| func | function | timer function |
|
||||
| oneshot | bool | true = single shot timer, false = periodic timer |
|
||||
| period | number | timer period in msec |
|
||||
| fires | number | number of timer calls made |
|
||||
|
||||
|
||||
#### timer_enum
|
||||
|
||||
Get an array of unique names of all existing timers.
|
||||
|
||||
```
|
||||
function timer_enum()
|
||||
```
|
||||
|
||||
|
||||
|
||||
# zapret-lib.lua base function library
|
||||
|
||||
@@ -2461,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
|
||||
|
||||
```
|
||||
@@ -2564,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)
|
||||
```
|
||||
@@ -3269,12 +3324,12 @@ function host_or_ip(desync)
|
||||
```
|
||||
function is_absolute_path(path)
|
||||
function append_path(path, file)
|
||||
function writeable_file_name(filename)
|
||||
function writeble_file_name(filename)
|
||||
```
|
||||
|
||||
- `is_absolute_path` returns true if the path starts from the root. It accounts for CYGWIN path specifics.
|
||||
- `append_path` appends a file or directory name `file` to `path`, using '/' as a separator.
|
||||
- `writeable_file_name` returns `filename` if it contains an absolute path or if the `WRITEABLE` environment variable is not set. Otherwise, it retrieves the path from the `WRITEABLE` environment variable and appends the `filename` using `append_path`.
|
||||
- `writeble_file_name` returns `filename` if it contains an absolute path or if the `WRITABLE` environment variable is not set. Otherwise, it retrieves the path from the `WRITABLE` environment variable and appends the `filename` using `append_path`.
|
||||
|
||||
## Reading and writing Files
|
||||
|
||||
@@ -3409,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. |
|
||||
@@ -3578,6 +3633,23 @@ function replay_drop(desync)
|
||||
|
||||
These functions work correctly with both [replays](#handling-multi-packet-payloads) and regular dissects. For regular dissects, `replay_first` is always true, `replay_drop_set` does not change the flag, and `replay_drop` is always false.
|
||||
|
||||
## Timer helpers
|
||||
|
||||
```
|
||||
function dis_timer_name(dis)
|
||||
```
|
||||
|
||||
Construct timer name based on ip src and dst addresses, l4 protocol name, port numbers or icmp codes.
|
||||
May not be unique.
|
||||
|
||||
```
|
||||
function desync_timer_name(desync)
|
||||
```
|
||||
|
||||
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+instance and can be used as an oneshot timer name.
|
||||
|
||||
## Orchestration
|
||||
|
||||
This group includes functions that support the orchestration and shimming processes.
|
||||
@@ -3742,9 +3814,14 @@ function send(ctx, desync)
|
||||
- arg: [standard ipfrag](#standard-ipfrag)
|
||||
- arg: [standard reconstruct](#standard-reconstruct)
|
||||
- arg: [standard rawsend](#standard-rawsend)
|
||||
- arg: delay - packet send delay in msec
|
||||
- Default `ip_id` mode is `none`.
|
||||
|
||||
Sends the current dissect with optional modifications applied.
|
||||
If delay is specified packet data and send options are remembered. After specified time packet is sent out.
|
||||
|
||||
The function returns VERDICT_PASS - the sending of the current dissect is not canceled. If you want to cancel, use the [drop](#drop) instance.
|
||||
|
||||
|
||||
### pktmod
|
||||
|
||||
@@ -4129,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.
|
||||
@@ -4344,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`.
|
||||
|
||||
@@ -4552,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.
|
||||
@@ -4718,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
|
||||
@@ -4851,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
|
||||
@@ -4863,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
|
||||
@@ -4876,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
|
||||
|
||||
|
||||
+206
-117
@@ -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)
|
||||
@@ -101,6 +102,8 @@
|
||||
- [Управление таймерами](#управление-таймерами)
|
||||
- [timer_set](#timer_set)
|
||||
- [timer_del](#timer_del)
|
||||
- [timer_info](#timer_info)
|
||||
- [timer_enum](#timer_enum)
|
||||
- [Библиотека базовых функций zapret-lib.lua](#библиотека-базовых-функций-zapret-liblua)
|
||||
- [Базовые desync функции](#базовые-desync-функции)
|
||||
- [luaexec](#luaexec)
|
||||
@@ -157,6 +160,7 @@
|
||||
- [rawsend_payload_segmented](#rawsend_payload_segmented)
|
||||
- [Стандартные фильтры direction и payload](#стандартные-фильтры-direction-и-payload)
|
||||
- [Работа с многопакетными пейлоадам](#работа-с-многопакетными-пейлоадам)
|
||||
- [Помощь с таймерами](#помощь-с-таймерами)
|
||||
- [Оркестрация](#оркестрация)
|
||||
- [instance_cutoff_shim](#instance_cutoff_shim)
|
||||
- [cutoff_shim_check](#cutoff_shim_check)
|
||||
@@ -323,6 +327,7 @@ zapret2 является пакетным манипулятором, основ
|
||||
Если система не удовлетворяет указанным требованиям - возможна самостоятельная "доприкрутка" к системе.
|
||||
|
||||
macOS не поддерживается по причине отсутствия подходящего средства перехвата и управления пакетами. Стандартное для BSD средство ipdivert было убрано из ядра производителем.
|
||||
Политика apple - все закрывать, ужесточать, вырезать низкоуровневые интерфейсы, вводить требования подписи. Из-за этого реализация собственного подходящего средства перехвата пакетов крайне затруднена.
|
||||
|
||||
# Схема обработки трафика
|
||||
|
||||
@@ -369,7 +374,7 @@ conntrack отслеживает логическое направление п
|
||||
позволяющих задать диапазон позиций внутри потока, который интересен для инстанса. Внутрипрофильные фильтры после их определения действуют на все последующие инстансы
|
||||
до их переопределения. Главный смысл наличия внутрипрофильных фильтров - сократить число относительно медленных вызовов Lua , принимая максимум решений на стороне C кода.
|
||||
|
||||
Пакет пришел в Lua инстанс. Функция имеет 2 [параметра](#прототип-lua-функции) - ctx и desync. ctx - это контекст для связи с некоторыми функциями на стороне C кода.
|
||||
Пакет пришел в Lua инстанс. Функция имеет 2 [параметра](#прототип-lua-desync-функции) - ctx и desync. ctx - это контекст для связи с некоторыми функциями на стороне C кода.
|
||||
[desync](#структура-таблицы-desync) - таблица, содержащая множество параметров обрабатываемого пакета. Прежде всего это [диссект](#структура-диссекта) - подтаблица `dis`.
|
||||
Информация из записи conntrack - [подтаблица track](#структура-track). Еще целый ряд параметров, который можно увидеть, выполнив [var_debug(desync)](#var_debug) или просто вызвав готовый инстанс [pktdebug](#pktdebug).
|
||||
|
||||
@@ -710,10 +715,9 @@ nfqws2 использует стандартный парсер getopt_long_only
|
||||
--ipcache-lifetime=<int> ; время жизни записей кэша IP в секундах. 0 - без ограничений.
|
||||
--ipcache-hostname=[0|1] ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы
|
||||
--reasm-disable=[type[,type]] ; отключить сборку фрагментов для списка пейлоадов : tls_client_hello quic_initial . без аргумента - отключить reasm для всего.
|
||||
--timer-res=msec ; разрешение таймеров в мс. по умолчанию - 50
|
||||
|
||||
DESYNC ENGINE INIT:
|
||||
--writeable[=<dir_name>] ; создать директорию для Lua с разрешением записи и поместить путь к ней в переменную env "WRITEABLE" (только одна директория)
|
||||
--writable[=<dir_name>] ; создать директорию для Lua с разрешением записи и поместить путь к ней в переменную env "WRITABLE" (только одна директория)
|
||||
--blob=<item_name>:[+ofs]@<filename>|0xHEX ; загрузить бинарный файл или hex строку в переменную Lua <item_name>. +ofs задает смещение от начала файла
|
||||
--lua-init=@<filename>|<lua_text> ; однократно при старте выполнить Lua код из строки или из файла. поддерживаются сжатые gzip файлы. автоматически проверяется "<filename>.gz"
|
||||
--lua-gc=<int> ; интервал вызова сборщика мусора Lua в секундах. 0 отключает периодический вызов.
|
||||
@@ -752,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 инстанс с указанными параметрами, если соблюдены условия внутрипрофильных фильтров
|
||||
@@ -768,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 :
|
||||
@@ -788,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 протоколов по входящему направлению. список через запятую
|
||||
@@ -801,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 сетей
|
||||
```
|
||||
|
||||
@@ -854,7 +862,7 @@ nfqws2 <глобальные_параметры>
|
||||
Если не сработал ни один фильтр, выбирается пустой профиль с номером 0, не предполагающий никаких действий с трафиком.
|
||||
|
||||
Все условия, кроме `--filter-l7` и хостлистов, являются однозначными и известными с момента начала обработки потока (с начала соединения).
|
||||
В начале, как правило, еще неизвестнен протокол потока и имя хоста, выделяемое из сообщений потока.
|
||||
В начале, как правило, еще неизвестен протокол потока и имя хоста, выделяемое из сообщений потока.
|
||||
Когда эти величины становятся известны, происходит поиск профиля заново. Если выбирается другой профиль - происходит перескок.
|
||||
Таких перескоков может быть до двух, поскольку есть только 2 величины, влияющие на выбор, неизвестные с самого начала.
|
||||
Для протоколов потока tls, http, quic обычно бывает только 1 перескок, поскольку определение протокола и имени хоста
|
||||
@@ -989,6 +997,8 @@ nfqws2 <глобальные_параметры>
|
||||
В остальных системах нужно копаться в powershell или лезть в реестр, чтобы раскидать подключения по нужным GUID,
|
||||
если вдруг они раскидались системой неправильно. Но можно и не бороться, а просто внести список GUID, назначенных системой автоматически.
|
||||
|
||||
В обеих системах есть параметры, позволяющие инвертировать проверку.
|
||||
|
||||
## Серверный режим
|
||||
|
||||
Некоторые виды воздействий можно осуществлять не только со стороны клиента, но и сервера.
|
||||
@@ -1137,8 +1147,8 @@ Windows :
|
||||
- Безвозвратно убираются все Se* привилегии из токена, кроме SeChangeNotifyPrivilege.
|
||||
- С помощью Job запрещается создание дочерних процессов и ограничивается взаимодействие с десктопом - clipboard, change desktop, change display settings и тд
|
||||
|
||||
Есть простой способ передать Lua коду каталог, доступный на запись - параметр `--writeable[=<dirname>]`.
|
||||
nfqws2 создает каталог, назначает на него такие права, чтобы Lua код смог писать туда файлы, передает имя директории в переменной env `WRITEABLE`.
|
||||
Есть простой способ передать Lua коду каталог, доступный на запись - параметр `--writable[=<dirname>]`.
|
||||
nfqws2 создает каталог, назначает на него такие права, чтобы Lua код смог писать туда файлы, передает имя директории в переменной env `WRITABLE`.
|
||||
Если dirname не задан, на Windows создается каталог внутри `%USERPROFILE%/AppData/LocalLow`
|
||||
|
||||
Со стороны Lua убираются опасные функции - os.execute, io.popen, package.loadlib и модуль debug.
|
||||
@@ -1156,7 +1166,7 @@ Lua код вызывается в 3 местах.
|
||||
`%var` подставляет значение переменной `desync.var` или `var`, если первая отсутствует.
|
||||
`#var` подставляет длину переменной `desync.var` или `var`, если первая отсутствует.
|
||||
Двоеточия и знаки `%`, `#` в начале могут быть эскейпнуты через `\`.
|
||||
3. При обработке таймеров
|
||||
3. При обработке [таймеров](#таймеры)
|
||||
|
||||
И `--lua-init`, и `--lua-desync` может быть несколько. Выполнение производится строго в порядке указания.
|
||||
|
||||
@@ -1174,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` задаются без диапазона и значения счетчика - единственной буквой. Знак `-` означает включающую верхнюю границу, `<` - исключающую.
|
||||
@@ -1229,7 +1243,7 @@ nfqws2 следит за превышением верхней границы с
|
||||
- `--out-range` указан, чтобы отсечь поток от Lua по исходящему направлению после relative sequence (32768+1460) - для экономии процессора.
|
||||
Такое значение выбрано из-за специфики функции circular - значение s32768 используется в детекторе успеха как порог срабатывания по умолчанию,
|
||||
1460 - максимально возможная длина данных в tcp пакете. На Linux может быть не нужно, если используется фильтр connbytes.
|
||||
- сircular для своей работы требует начальные входящие пакеты потока, а по умолчанию они отключены.
|
||||
- circular для своей работы требует начальные входящие пакеты потока, а по умолчанию они отключены.
|
||||
Поэтому включаем вплоть до позиции relative sequence 5556. По умолчанию детектор успеха реагирует на s4096. Добавлен еще 1 пакет макс 1460 байт.
|
||||
- Остальные инстансы не нуждаются во входящем трафике. Снова отключаем. Действие `--in-range=x` распространяется до конца профиля.
|
||||
- Действие `--payload` распространяется на два следующих за ним инстанса.
|
||||
@@ -1525,7 +1539,7 @@ desync
|
||||
|
||||
Диссект включает в себя поля ip, ip6, tcp, udp.
|
||||
ip присутствует в случае ipv4, ip6 - в случае ipv6.
|
||||
По наличиню ip или ip6 можно определить версию ip протокола.
|
||||
По наличию ip или ip6 можно определить версию ip протокола.
|
||||
По наличию tcp или udp можно определять L4 протокол.
|
||||
|
||||
Сами таблицы хедеров копируют названия полей C структур из `netinet/{ip,ip6,tcp,udp}.h`.
|
||||
@@ -1742,7 +1756,7 @@ desync.track всегда отсутствует.
|
||||
|
||||
Lua код может вызываться и вне привязки к принимаемым данным из сети.
|
||||
Источником события является время.
|
||||
Таймер - это обьект nfqws2, идентифицируемый по уникальному имени и позволяющий вызывать
|
||||
Таймер - это объект nfqws2, идентифицируемый по уникальному имени и позволяющий вызывать
|
||||
указанную Lua функцию с определенной периодичностью, либо однократно через какое-то время.
|
||||
Установкой таймера занимается функция [timer_set](#timer_set), удаляет таймер функция [timer_del](#timer_del).
|
||||
|
||||
@@ -1751,22 +1765,18 @@ nfqws2 - программа однопоточная, как и движок Lua
|
||||
Таймеры вызываются между обработкой блоков. Если обработка заняла достаточно много времени, то вызов таймера может быть
|
||||
не идеальным по времени.
|
||||
|
||||
Следует помнить о разрешении таймеров, которое задается в параметре `--timer-res`. По умолчанию - 50 мсек.
|
||||
Это значит, что проверка необходимости вызова таймера может проводиться не чаще указанного времени.
|
||||
Если вы сделаете period 70 мсек, таймер будет вызываться "рвано". +70 будет вызван при 100, +140 - при 150, +210 - при 250.
|
||||
Поэтому период желательно делать кратным разрешению таймеров.
|
||||
Увеличение разрешения (снижение `--timer-res`) может привести к некоторому увеличению нагрузки на CPU,
|
||||
поскольку процессор будет просыпаться через каждый указанный период времени для проверки всех таймеров.
|
||||
Чем больше таймеров, тем выше нагрузка на CPU для проверки.
|
||||
Максимальное разрешение - 100 проверок в секунду, что соответствует `--timer-res=10`.
|
||||
|
||||
Таймеры могут быть полезны для обработке ситуаций отсутствия реакции из сети на отсылаемые пакеты.
|
||||
Таймеры могут быть полезны для обработки ситуаций отсутствия реакции из сети на отсылаемые пакеты.
|
||||
Например, вам надо что-то куда-то послать и прочитать ответ. Но оппонент может и не ответить или ответ может не дойти.
|
||||
Чтобы ваша схема не подвисла в неопределенном состоянии и не оставила мусор в памяти, может помочь таймер.
|
||||
С помощью таймеров, desync функций и функций отсылки можно построить полноценную машину состояний - вплоть
|
||||
до собственной реализации tcp или иной системы гарантированной доставки.
|
||||
Проектировать схему нужно асинхронно в стиле конечного автомата. Прямые задержки sleep в коде не предусмотрены, поскольку ломают
|
||||
схему обработки трафика через очередь. Пока вы ждете - остальное повиснет.
|
||||
Другое возможное применение - различного рода задержки трафика. Запомнить, дропнуть, отослать через какое-то время.
|
||||
Без таймеров отсылать все придется сразу без задержки.
|
||||
|
||||
В режиме `--intercept=0` при наличии таймеров nfqws2 не выходит сразу, а исправно вызывает таймеры, пока не останется ни одного.
|
||||
После этого процесс завершается.
|
||||
|
||||
### Прототип функции таймера
|
||||
|
||||
@@ -1846,8 +1856,8 @@ data задается при запуске таймера через [timer_set
|
||||
|
||||
| env | Назначение |
|
||||
| :-------- |:---------- |
|
||||
| WRITEABLE | Директория, доступная на запись Lua. Результат опции `--writeable` |
|
||||
| APPDATALOW | (только Windows) Расположение AppData для low mandatory level. Сюда тоже можно записывать, но предпочтительно использовать `--writeable` для кросс-платформенности. |
|
||||
| WRITABLE | Директория, доступная на запись Lua. Результат опции `--writable` |
|
||||
| APPDATALOW | (только Windows) Расположение AppData для low mandatory level. Сюда тоже можно записывать, но предпочтительно использовать `--writable` для кросс-платформенности. |
|
||||
|
||||
## C функции
|
||||
|
||||
@@ -1923,7 +1933,7 @@ function bitset(u48, bit_from, bit_to, set)
|
||||
|
||||
### Операции с беззнаковыми числами
|
||||
|
||||
При операциях с числями без знака всегда важна разрядность. От нее зависит результат.
|
||||
При операциях с числами без знака всегда важна разрядность. От нее зависит результат.
|
||||
Поэтому все функции имеют в своем названии разрядность. При передаче аргументов, выходящих за пределы разрядности, вызывается error.
|
||||
|
||||
#### uX
|
||||
@@ -2133,7 +2143,7 @@ function gunzip_inflate(zstream, compressed_data[, expected_uncompressed_chunk_s
|
||||
|
||||
* gunzip_init создает и возвращает контекст gzip потока для последующих вызовов других функций. Значение windowBits см. в документации по zlib (по умолчанию 47).
|
||||
* gunzip_end освобождает контекст gzip. может быть освобожден сборщиком мусора, но лучше вызывать явно.
|
||||
* gunzip_inflate разжимает очередную часть зипованных данных. Данные можно скармливать частями. Разжатые части конкатенируются для получения полных данных. Возвращается 2 аргумента : расжатые данные и bool признак конца gzip. В случае испорченных данных или при нехватке памяти возвращается nil.
|
||||
* gunzip_inflate разжимает очередную часть зипованных данных. Данные можно скармливать частями. Разжатые части конкатенируются для получения полных данных. Возвращается 2 аргумента : разжатые данные и bool признак конца gzip. В случае испорченных данных или при нехватке памяти возвращается nil.
|
||||
* expected_uncompressed_chunk_size - необязательный параметр для оптимизации выделения памяти под разжимаемые данные. Если буфера не хватает, вызываются realloc, копирующие блоки памяти и влияющие на производительность. Размер следует выбирать согласно ожидаемой степени сжатия с небольшим запасом. По умолчанию - четырехкратный размер compressed_data.
|
||||
|
||||
#### gzip
|
||||
@@ -2147,8 +2157,8 @@ function gzip_deflate(zstream, uncompressed_data[, expected_compressed_chunk_siz
|
||||
* gzip_init создает и возвращает контекст gzip потока для последующих вызовов других функций. Значение windowBits см. в документации по zlib (по умолчанию 31). level - уровень сжатия от 1 до 9 (по умолчанию 9), memlevel - допустимый уровень использования памяти от 1 до 8 (по умолчанию 8).
|
||||
* gzip_end освобождает контекст gzip. может быть освобожден сборщиком мусора, но лучше вызывать явно.
|
||||
* вместо явного вызова gzip_end в Lua 5.5+ для контекста можно использовать to-be-closed variable
|
||||
* gzip_deflate cжимает очередную часть данных. Данные можно скармливать частями. Cжатые части конкатенируются для получения полных данных. Для финализации потока по окончанию скармливания данных функция должна быть вызвана с uncompressed_data=nil или uncompressed_data="". Возвращается 2 аргумента : сжатые данные и bool признак конца gzip. При ошибках gzip или нехватке памяти возвращается nil.
|
||||
* expected_compressed_chunk_size - необязательный параметр для оптимизации выделения памяти под cжимаемые данные. Если буфера не хватает, вызываются realloc, копирующие блоки памяти и влияющие на производительность. Размер следует выбирать согласно ожидаемой степени сжатия с небольшим запасом. По умолчанию - половина размера uncompressed_data.
|
||||
* gzip_deflate сжимает очередную часть данных. Данные можно скармливать частями. Сжатые части конкатенируются для получения полных данных. Для финализации потока по окончанию скармливания данных функция должна быть вызвана с uncompressed_data=nil или uncompressed_data="". Возвращается 2 аргумента : сжатые данные и bool признак конца gzip. При ошибках gzip или нехватке памяти возвращается nil.
|
||||
* expected_compressed_chunk_size - необязательный параметр для оптимизации выделения памяти под сжимаемые данные. Если буфера не хватает, вызываются realloc, копирующие блоки памяти и влияющие на производительность. Размер следует выбирать согласно ожидаемой степени сжатия с небольшим запасом. По умолчанию - половина размера uncompressed_data.
|
||||
|
||||
### Системные функции
|
||||
|
||||
@@ -2244,7 +2254,7 @@ function timegm(tm)
|
||||
|
||||
При сборке ipv6 по умолчанию цепочка ip протоколов в exthdr собирается автоматически.
|
||||
У каждого exthdr есть поле type, поэтому понятно что вписывать в предыдущий exthdr,
|
||||
либо в основной ip6 хедер. next протокол последнего exthdr устанавливатеся как IPPROTO_TCP,
|
||||
либо в основной ip6 хедер. next протокол последнего exthdr устанавливается как IPPROTO_TCP,
|
||||
IPPROTO_UDP, IPPROTO_ICMP, IPPROTO_ICMPV6 в зависимости от наличия в диссекте таблиц tcp, udp, icmp, ip6.
|
||||
В большинстве случаев это удобно, поскольку вам при вставлении exthdr не надо реконструировать
|
||||
всю цепочку next протоколов и не надо заполнять поля next. За вас это сделает реконструктор диссекта.
|
||||
@@ -2299,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
|
||||
|
||||
```
|
||||
@@ -2337,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
|
||||
|
||||
@@ -2586,19 +2611,47 @@ function timer_set(name, func, period, oneshot, data)
|
||||
```
|
||||
|
||||
* name - уникальное имя таймера. если таймер с таким именем уже есть, он удаляется и замещается новым. При замещении отсчет времени начинается заново.
|
||||
* func - имя таймер функции (string)
|
||||
* period - периодичность вызова таймера в миллисекундах (см. [разрешение таймеров](#таймеры))
|
||||
* func - таймер функция. string или function. в случае string функция ищется по имени в момент вызова timer_set и запоминается по ссылке, а не как строка.
|
||||
* period - периодичность вызова таймера в миллисекундах
|
||||
* oneshot - bool признак является ли таймер однократным (true) или периодическим (false)
|
||||
* data - произвольная переменная, передаваемая [таймер-функции](#прототип-функции-таймера)
|
||||
|
||||
#### timer_del
|
||||
|
||||
Удалить таймер.
|
||||
Удалить таймер. Текущая функция не прерывается, но последующих вызовов не будет.
|
||||
|
||||
```
|
||||
function timer_del(name)
|
||||
```
|
||||
|
||||
#### timer_info
|
||||
|
||||
Получить информацию о таймере.
|
||||
|
||||
```
|
||||
function timer_info(name)
|
||||
```
|
||||
|
||||
В случае успеха возвращается таблица, в случае неудачи - nil.
|
||||
|
||||
|
||||
| Поле | Тип | Описание |
|
||||
| :------ | :----- | :------------------ |
|
||||
| name | string | уникальное имя таймера |
|
||||
| func | function | таймер функция |
|
||||
| oneshot | bool | true = однократный таймер, false = периодический |
|
||||
| period | number | период таймера в мсек |
|
||||
| fires | number | количество выполненных вызовов таймера |
|
||||
|
||||
|
||||
#### timer_enum
|
||||
|
||||
Получить массив уникальных имен всех существующих таймеров.
|
||||
|
||||
```
|
||||
function timer_enum()
|
||||
```
|
||||
|
||||
|
||||
# Библиотека базовых функций zapret-lib.lua
|
||||
|
||||
@@ -2621,6 +2674,8 @@ function luaexec(ctx, desync)
|
||||
|
||||
Пример : `--lua-desync=luaexec:code="desync.rnd=brandom(math.random(5,10))"`
|
||||
|
||||
Функция может возвращать вердикт через return.
|
||||
|
||||
### pass
|
||||
|
||||
```
|
||||
@@ -2729,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)
|
||||
```
|
||||
@@ -2886,7 +2941,7 @@ function http_reconstruct_req(hdis[, unixeol])
|
||||
|
||||
Для нахождения хедеров по названию используйте [array_field_search](#array_search) по полю "header_low", которое содержит название хедера в нижнем регистре.
|
||||
|
||||
Реконструктор http запроса берет таблицу-разбор и воссоздает raw string. Параметр unixeol заменяет стандартный для http перевод сктроки 0D0A на 0A. Это нестандарт и ломает все сервера, кроме nginx.
|
||||
Реконструктор http запроса берет таблицу-разбор и воссоздает raw string. Параметр unixeol заменяет стандартный для http перевод строки 0D0A на 0A. Это нестандарт и ломает все сервера, кроме nginx.
|
||||
|
||||
<details>
|
||||
<summary><b>Пример разборки http запроса `http://testhost.com/testuri`</b></summary>
|
||||
@@ -3313,7 +3368,7 @@ extensions и другие списки индексируются по номе
|
||||
Можно заполнить только raw data field. Если нет подтаблицы dis - при реконструкции будет взято оно.
|
||||
Если есть dis, то он должен быть заполнен корректно согласно рассматриваемому элементу данных.
|
||||
|
||||
Следущий пример кода ищет SNI extension в диссекте tdis, а в случае отсутствия добавляет в начало. Затем добавляется домен "example.com".
|
||||
Следующий пример кода ищет SNI extension в диссекте tdis, а в случае отсутствия добавляет в начало. Затем добавляется домен "example.com".
|
||||
|
||||
```
|
||||
local idx_sni = array_field_search(tdis.handshake[TLS_HANDSHAKE_TYPE_CLIENT].dis.ext, "type", TLS_EXT_SERVER_NAME)
|
||||
@@ -3442,13 +3497,13 @@ function host_or_ip(desync)
|
||||
```
|
||||
function is_absolute_path(path)
|
||||
function append_path(path,file)
|
||||
function writeable_file_name(filename)
|
||||
function writable_file_name(filename)
|
||||
```
|
||||
|
||||
- is_absolute_path возвращает true, если путь path начинается с корня. Учитываются особенности путей CYGWIN.
|
||||
- append_path дописывает имя файла или каталога file к пути path, разделяя их знаком '/'
|
||||
- writeable_file_name возвращает filename, если filename содержит абсолютный путь или env `WRITEABLE` отсутствует.
|
||||
Иначе берется путь из env `WRITEABLE` и к нему дописывается имя файла filename через append_path.
|
||||
- writable_file_name возвращает filename, если filename содержит абсолютный путь или env `WRITABLE` отсутствует.
|
||||
Иначе берется путь из env `WRITABLE` и к нему дописывается имя файла filename через append_path.
|
||||
|
||||
## Чтение и запись файлов
|
||||
|
||||
@@ -3503,7 +3558,7 @@ function autottl(incoming_ttl, attl)
|
||||
```
|
||||
|
||||
Механизм autottl служит, чтобы автоматически на базе TTL входящего пакета определять такой TTL,
|
||||
который либо немного не достает до противопложного конца, либо немного превышает длину пути до него.
|
||||
который либо немного не достает до противоположного конца, либо немного превышает длину пути до него.
|
||||
delta - это положительная или отрицательная разница с догадкой о длине пути. min-max - допустимый диапазон.
|
||||
Если итоговый результат выходит за пределы диапазона, назначаются его крайние значения.
|
||||
Если delta<0 и результат оказывается равен пути или длиннее, либо если delta>=0 и результат оказывается короче пути, алгоритм уходит в ошибку и выдает nil.
|
||||
@@ -3529,7 +3584,7 @@ delta - это положительная или отрицательная ра
|
||||
| ip_id | режим назначения ip_id : seq, rnd, zero, none<br>seq - последовательное<br>rnd - случайное<br>zero - ноль<br>none - не менять |
|
||||
| ip_id_conn | запоминать последнее сгенерированное значение seq и начинать с него в следующем пакете. не работает без desync.track |
|
||||
|
||||
Из seq далеко не всегда получится ожидаемая и преемлемая последовательность.
|
||||
Из seq далеко не всегда получится ожидаемая и приемлемая последовательность.
|
||||
ip_id применяется только в ряде функций, оно не применяется ко всем проходящим пакетам автоматически.
|
||||
Поскольку ОС не следит за измененными ip_id, в пакетах, которые не трогали, оно может начать идти заново.
|
||||
Windows заменяет нулевые ip_id на собственную последовательность, остальные ОС - нет.
|
||||
@@ -3586,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 |
|
||||
@@ -3641,7 +3696,7 @@ ipfrag2 реализует эту возможность на двух фраг
|
||||
function wsize_rewrite(dis, arg)
|
||||
```
|
||||
|
||||
Переписать в диссекте dis tcp.th_win и scale factor в tcp оцпии, если она присутствует. Увеличение scaling factor блокируется.
|
||||
Переписать в диссекте dis tcp.th_win и scale factor в tcp опции, если она присутствует. Увеличение scaling factor блокируется.
|
||||
|
||||
- arg: wsize - window size
|
||||
- arg: scale - scale factor
|
||||
@@ -3675,7 +3730,7 @@ function ip2ifname(ip)
|
||||
## Отсылка
|
||||
|
||||
Следующие функции могут брать несколько блоков описанных выше опций, каждый из которых представлен полем параметра options.
|
||||
Во всех функциях используется [options.reconstruct](#standard-reconstruct) и (options.rawsend)[#standard-rawsend). Они соответствуют формату параметров C функции [rawsend_dissect](#rawsend).
|
||||
Во всех функциях используется [options.reconstruct](#standard-reconstruct) и [options.rawsend](#standard-rawsend). Они соответствуют формату параметров C функции [rawsend_dissect](#rawsend).
|
||||
|
||||
### rawsend_dissect_ipfrag
|
||||
|
||||
@@ -3757,6 +3812,24 @@ function replay_drop(desync)
|
||||
|
||||
Функции работают корректно как с [реплеем](#особенности-приема-многопакетных-пейлоадов), так и обычными диссектами. Для обычных диссектов replay_first всегда true, replay_drop_set не меняет признак, replay_drop всегда false.
|
||||
|
||||
## Помощь с таймерами
|
||||
|
||||
```
|
||||
function dis_timer_name(dis)
|
||||
```
|
||||
|
||||
Конструирует имя таймера, включающее в себя ip адреса src,dst, имя протокола l4, номера портов или icmp коды.
|
||||
Имя таймера может не быть уникальным.
|
||||
|
||||
```
|
||||
function desync_timer_name(desync)
|
||||
```
|
||||
|
||||
Конструирует имя таймера, включающее в себя имя инстанса, результат dis_timer_name, номер пакета в conntrack по прямому направлению.
|
||||
Если track отсутствует, добавляются случайные символы.
|
||||
Имя таймера можно считать уникальным на desync+instance, его можно использовать как oneshot timer name.
|
||||
|
||||
|
||||
## Оркестрация
|
||||
|
||||
В эту группу функций входят функции поддержки процесса оркестрации и прокладки.
|
||||
@@ -3806,7 +3879,7 @@ function apply_execution_plan(desync, instance)
|
||||
|
||||
Копирует в desync идентификацию инстанса и его аргументы из элемента [execution plan](#execution_plan) `instance`,
|
||||
тем самым воссоздает состояние desync, как если бы `instance` был вызван напрямую C кодом.
|
||||
За одним исключением : apply_arg_prefix не применяется, поскольку может содержать несуществующие блоб, существование которого зависит от условного выполнения предыдущих истансов.
|
||||
За одним исключением : apply_arg_prefix не применяется, поскольку может содержать несуществующие блоб, существование которого зависит от условного выполнения предыдущих инстансов.
|
||||
|
||||
[execution plan](#execution_plan) выдается C функцией `execution_plan()` как массив, элементами которого являются `instance`.
|
||||
|
||||
@@ -3829,7 +3902,7 @@ function plan_instance_execute_preapplied(desync, verdict, instance)
|
||||
При совпадении условий непосредственно перед вызовом выполняет apply_arg_prefix.
|
||||
Возвращает агрегацию verdict и вердикта `instance`.
|
||||
|
||||
Вариант "preapplied" не выполняет apply_execution_plan, позволяя это сделат вызывающему коду.
|
||||
Вариант "preapplied" не выполняет apply_execution_plan, позволяя это сделать вызывающему коду.
|
||||
Иногда для принятия решения вызывать ли instance требуется таблица desync, настроенная на вызываемый инстанс.
|
||||
Чтобы не делать apply дважды (там копирование desync.arg) и существует этот вариант.
|
||||
|
||||
@@ -3870,7 +3943,7 @@ function replay_execution_plan(desync, max)
|
||||
|
||||
## Стандартные наборы параметров
|
||||
|
||||
Многие функции берут стандартные наборы аргументов, классифицируемые по их назначанию.
|
||||
Многие функции берут стандартные наборы аргументов, классифицируемые по их назначению.
|
||||
|
||||
Дополнительные фильтры по направлению и пейлоаду внутри функций anti-dpi сделаны в основном на случай не слишком грамотного написания опций командной строки как дополнительный предохранитель и защита от дурака, чтобы сделать флуд как это было в winws1 с `--dpi-desync-any-protocol` непреднамеренно было непросто.
|
||||
|
||||
@@ -3926,10 +3999,19 @@ function send(ctx, desync)
|
||||
- arg: [standard ipfrag](#standard-ipfrag)
|
||||
- arg: [standard reconstruct](#standard-reconstruct)
|
||||
- arg: [standard rawsend](#standard-rawsend)
|
||||
- arg: delay - задержка отправки в мсек
|
||||
- режим ip_id по умолчанию - none
|
||||
|
||||
Отсылает текущий диссект c опциональным применением модификаций.
|
||||
|
||||
При наличии параметра delay данные пакета и опции отправки запоминаются. Спустя указанное время происходит отправка.
|
||||
Функция отсылки с delay реализована тривиальным образом через таймер с именем, уникальным для диссекта.
|
||||
Множественные send с delay не вызывают дополнительную отправку, вместо этого замещается предыдущая отправка.
|
||||
Если вам нужно задерживать несколько вариаций (например, результат tcp сегментации) и нужна очередь для отправки в строгом порядке,
|
||||
то вам нужно писать свою функцию - send не подойдет.
|
||||
|
||||
Функция выносит VERDICT_PASS - отсылка текущего диссекта не отменяется. Если нужно отменить - используйте инстанс [drop](#drop).
|
||||
|
||||
### pktmod
|
||||
|
||||
```
|
||||
@@ -3963,7 +4045,7 @@ function http_domcase(ctx, desync)
|
||||
|
||||
- arg: [standard direction](#standard-direction)
|
||||
|
||||
Меняет регистр написания домена внутри заголовка `Host:`. Верхний и нижний регистр чредуется каждый символ : `rUtRaCkEr.oRg`.
|
||||
Меняет регистр написания домена внутри заголовка `Host:`. Верхний и нижний регистр чередуется каждый символ : `rUtRaCkEr.oRg`.
|
||||
|
||||
### http_methodeol
|
||||
|
||||
@@ -3997,7 +4079,7 @@ function wsize(ctx, desync)
|
||||
- arg: wsize - размер tcp окна
|
||||
- arg: scale - scaling фактор. заменяется в tcp option, если есть. только уменьшение, увеличение блокируется
|
||||
|
||||
Меняет tcp.th_win и/или scaling factor в tcp оцпии в tcp пакете SYN,ACK, после чего выполняет [instance cutoff](#instance_cutoff). Если изменение выполнено - выставляет VERDICT_MODIFY.
|
||||
Меняет tcp.th_win и/или scaling factor в tcp опции в tcp пакете SYN,ACK, после чего выполняет [instance cutoff](#instance_cutoff). Если изменение выполнено - выставляет VERDICT_MODIFY.
|
||||
|
||||
Цель техники - подменить window size со стороны клиента или сервера, чтобы в ответ на это клиент отослал следующий пакет частями, поскольку целиком он не влезает в window size. Может приводить к замедлению. Устаревшая техника, рекомендуется применять только с сервера в крайнем случае для работы по клиентам, которые не делают ничего для обхода блокировки. С клиента лучше применять техники tcp сегментации, поскольку они лишены побочных эффектов в виде замедления скорости и имеют больше возможностей.
|
||||
|
||||
@@ -4012,7 +4094,7 @@ function wssize(ctx, desync)
|
||||
- arg: scale - scaling фактор. заменяется в tcp option, если есть. только уменьшение, увеличение блокируется
|
||||
- arg: forced_cutoff - список типов пейлоадов через запятую, при получении которых выполняется [instance cutoff](#instance_cutoff). Если нужно применять wssize бесконечно, можно указать forced_cutoff=no, то есть несуществующий тип пейлоада, который не придет никогда.
|
||||
|
||||
Меняет tcp.th_win и/или scaling factor в tcp оцпии во всех tcp пакетах потока по направлению до достижения условия "cutoff".
|
||||
Меняет tcp.th_win и/или scaling factor в tcp опции во всех tcp пакетах потока по направлению до достижения условия "cutoff".
|
||||
Если изменение выполнено - выставляет VERDICT_MODIFY.
|
||||
"cutoff" наступает при получении любого пакета с данными, если аргумент forced_cutoff не задан, или при получении одного из указанных в аргументе forced_cutoff пейлоадов.
|
||||
В этом случае выполняется [instance cutoff](#instance_cutoff).
|
||||
@@ -4182,9 +4264,9 @@ function multidisorder(ctx, desync)
|
||||
|
||||
Аналогично [multisplit](#multisplit), но отправка сегментов производится в обратном порядке - с последнего по первый.
|
||||
|
||||
Техника seqovl в данном случае работает иначе. Она применятся ко второму в оригинальной очередности (предпоследнему отсылаемому) сегменту. seqovl может быть маркером. Например, можно сделать разрез по midsld, а seqovl сделать "midsld-1". seqovl обязательно должен быть меньше первого сегмента в оригинальной очередности (последнего отсылаемого), иначе эта ситуация распознается и seqovl отменяется.
|
||||
Техника seqovl в данном случае работает иначе. Она применяется ко второму в оригинальной очередности (предпоследнему отсылаемому) сегменту. seqovl может быть маркером. Например, можно сделать разрез по midsld, а seqovl сделать "midsld-1". seqovl обязательно должен быть меньше первого сегмента в оригинальной очередности (последнего отсылаемого), иначе эта ситуация распознается и seqovl отменяется.
|
||||
|
||||
Смысл seqovl в варианте disorder - в переписывании буфера сокета на принимающем конце. tcp сокет выдает данные в приложение последовательно, в порядке их оригинальной передачи. Если сначала приходит сегмент "спереди", не образующий с уже принятыми данными непрерывной последовательности, происходит задержка информации в буфере без выдачи ее приложению. Если далее приходит перекрывающийся по sequence сегмент, то информация из него переписыват уже имеющуюся в буфере. Так ведут себя все системы, кроме Windows, поэтому на Windows серверах эта техника не работает. Windows сохраняет старую информацию.
|
||||
Смысл seqovl в варианте disorder - в переписывании буфера сокета на принимающем конце. tcp сокет выдает данные в приложение последовательно, в порядке их оригинальной передачи. Если сначала приходит сегмент "спереди", не образующий с уже принятыми данными непрерывной последовательности, происходит задержка информации в буфере без выдачи ее приложению. Если далее приходит перекрывающийся по sequence сегмент, то информация из него переписывает уже имеющуюся в буфере. Так ведут себя все системы, кроме Windows, поэтому на Windows серверах эта техника не работает. Windows сохраняет старую информацию.
|
||||
|
||||
К предпоследнему отсылаемому сегменту (2-му по оригинальной очередности) приписывается слева seqovl_pattern размером seqovl (результат ресолвинга маркера), а tcp.th_seq уменьшается на seqovl.
|
||||
|
||||
@@ -4252,10 +4334,10 @@ function fakedsplit(ctx, desync)
|
||||
|
||||
- К оригиналам применяется только fooling_opts.tcp_ts_up. reconstruct_opts не применяются.
|
||||
- К фейкам применяются fooling_opts и reconstruct_opts в полном объеме.
|
||||
- ipid_opts и rawsend_opts применяются и к фейкам, и к оригиналом. ipfrag_opts не задействуются ни для фейков, ни для оригиналов.
|
||||
- ipid_opts и rawsend_opts применяются и к фейкам, и к оригиналам. ipfrag_opts не задействуются ни для фейков, ни для оригиналов.
|
||||
|
||||
В случае успеха отсылки выносится VERDICT_DROP, если не указано "nodrop".
|
||||
[blob](#передача-блобов) позволяет заменить текущией пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
|
||||
[blob](#передача-блобов) позволяет заменить текущий пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
|
||||
|
||||
### fakeddisorder
|
||||
|
||||
@@ -4298,7 +4380,7 @@ function fakeddisorder(ctx, desync)
|
||||
- ipid_opts и rawsend_opts применяются и к фейкам, и к оригиналом. ipfrag_opts не задействуются ни для фейков, ни для оригиналов.
|
||||
|
||||
В случае успеха отсылки выносится VERDICT_DROP, если не указано "nodrop".
|
||||
[blob](#передача-блобов) позволяет заменить текущией пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
|
||||
[blob](#передача-блобов) позволяет заменить текущий пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
|
||||
|
||||
### hostfakesplit
|
||||
|
||||
@@ -4315,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
|
||||
@@ -4338,7 +4420,7 @@ function hostfakesplit(ctx, desync)
|
||||
- ipid_opts и rawsend_opts применяются и к фейкам, и к оригиналом. ipfrag_opts не задействуются ни для фейков, ни для оригиналов.
|
||||
|
||||
В случае успеха отсылки выносится VERDICT_DROP, если не указано "nodrop".
|
||||
[blob](#передача-блобов) позволяет заменить текущией пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
|
||||
[blob](#передача-блобов) позволяет заменить текущий пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
|
||||
|
||||
### tcpseg
|
||||
|
||||
@@ -4518,7 +4600,7 @@ function automate_host_record(desync)
|
||||
|
||||
## Обслуживание удач и неудач
|
||||
|
||||
Простое, но далеко неточное, обьяснение понятиям "удача" и "неудача" - "сайт открывается" и "сайт не открывается". В логике автоматизации эти состояния абстрактны. Любой процесс может претерпеть удачу или неудачу. Детект состояния удачи или неудачи нужен для подсчета неудач через счетчик. При неудаче счетчик увеличивается, при успехе - сбрасывается, при достижении целевого значения - возвращается признак, по которому внешняя логика может выполнить любое действие. Например, сменить стратегию.
|
||||
Простое, но далеко неточное, объяснение понятиям "удача" и "неудача" - "сайт открывается" и "сайт не открывается". В логике автоматизации эти состояния абстрактны. Любой процесс может претерпеть удачу или неудачу. Детект состояния удачи или неудачи нужен для подсчета неудач через счетчик. При неудаче счетчик увеличивается, при успехе - сбрасывается, при достижении целевого значения - возвращается признак, по которому внешняя логика может выполнить любое действие. Например, сменить стратегию.
|
||||
|
||||
### automate_failure_counter
|
||||
|
||||
@@ -4558,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, если счетчик достиг целевого значения. При этом счетчик сбрасывается автоматически.
|
||||
|
||||
@@ -4595,7 +4677,7 @@ function standard_failure_detector(desync, crec)
|
||||
|
||||
Стандартный детектор неудач.
|
||||
|
||||
http редиректом от DPI считается то же самое , что и в [автохослистах](#детектор-неудач-автохостлистов).
|
||||
http редиректом от DPI считается то же самое , что и в [автохостлистах](#детектор-неудач-автохостлистов).
|
||||
|
||||
## Оркестраторы
|
||||
|
||||
@@ -4731,12 +4813,12 @@ function cond_payload_str(desync)
|
||||
- arg: pattern - строка, которая ищется в пейлоаде
|
||||
|
||||
Возвращает true, если в desync.dis.payload присутствует подстрока pattern.
|
||||
Это простейший сигнатурый детектор. Если C код не распознает нужный вам протокол, вы можете написать свой сигнатурный детектор и запускать последующие инстансы под оркестратором condition с вашим детектором в качестве iff.
|
||||
Это простейший сигнатурный детектор. Если C код не распознает нужный вам протокол, вы можете написать свой сигнатурный детектор и запускать последующие инстансы под оркестратором condition с вашим детектором в качестве iff.
|
||||
|
||||
#### cond_tcp_has_ts
|
||||
|
||||
```
|
||||
function cond_tcp_ts(desync)
|
||||
function cond_tcp_has_ts(desync)
|
||||
```
|
||||
|
||||
Возвращает true, если диссект является tcp и присутствует timestamp tcp option.
|
||||
@@ -4850,7 +4932,7 @@ blockcheck2 по умолчанию работает в интерактивно
|
||||
|
||||
`/opt/zapret2/blockcheck2.sh | tee /tmp/blockcheck2.log`
|
||||
|
||||
В [win bundle](https://github.com/bol-van/zapret-win-bundle) можно пользоваться cygwin prompt (`cygwin/cygwin-admin.cmd`). Там уже созданы alias-ы для запуска blockcheck из первой версии zapret, blockcheck2, winws, winws2 с подключенными стандартными Lua скриптами. Это удобно, потому что не нужно думать о путях и каждый раз писать или вставлять откуда-то огромный текст, тем боллее при наличии национальных символов и пробелов в путях, путаясь в экранировании символов и кодировках.
|
||||
В [win bundle](https://github.com/bol-van/zapret-win-bundle) можно пользоваться cygwin prompt (`cygwin/cygwin-admin.cmd`). Там уже созданы alias-ы для запуска blockcheck из первой версии zapret, blockcheck2, winws, winws2 с подключенными стандартными Lua скриптами. Это удобно, потому что не нужно думать о путях и каждый раз писать или вставлять откуда-то огромный текст, тем более при наличии национальных символов и пробелов в путях, путаясь в экранировании символов и кодировках.
|
||||
|
||||
Возможно последовательное тестирование нескольких доменов. Для этого нужно задать их через пробел.
|
||||
|
||||
@@ -4901,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 - тестировать максимально много без учета результата предыдущих тестов
|
||||
|
||||
### Поддерживаемые протоколы
|
||||
@@ -5034,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
|
||||
@@ -5046,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
|
||||
@@ -5059,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\> | вероятность успеха симуляции в процентах |
|
||||
|
||||
## Почему не открывается
|
||||
|
||||
@@ -5118,14 +5206,15 @@ blockcheck проверяет доступность одного отдельн
|
||||
3. Цель броузера - открыть сайт как можно быстрее, не нагружая пользователя техническими терминами, которые для него неотличимы от китайского языка. Поэтому он лезет на сайт по разным протоколам так, чтобы страница открылась быстрее. Может скакать между ipv4 и ipv6, между tls и quic. Уже 4 комбинации. А ведь для каждого из них отдельный тест блокчека. Все ли они пробились одной стратегией ? Корректно ли вы перенесли эти стратегии в рабочий конфиг ? Правильно ли их объединили ?
|
||||
4. Отдельный важный параметр - kyber. Постквантовая криптография, которая однопакетный запрос TLS/QUIC превращает в 2 или 3 пакетный. Само по себе фактор при обходе DPI. Современные броузеры обычно используют kyber. curl - зависит от его старости и от старости криптобиблиотеки, с которой он слинкован. OpenSSL 3.5.0 выдает kyber, старее - нет. LibreSSL или mbedTLS не выдают kyber. Сейчас. А завтра выдадут, потому что тренд идет в ту сторону.
|
||||
5. Блокираторы ничем не брезгуют. Бывает они даже привязываются к фингерпринту клиента. Что это такое ? Наличие/порядок tls extensions прежде всего, характерный для броузера или curl. Никак нельзя забанить IP адреса, но нужно устранить очередной новый VPN клиент ? Смотрят его handshake, находят уникальные особенности и рубят по ним. Заодно отрубается что-то еще. Но им на это уже плевать.
|
||||
6. ECH - технология зашифрованной передачи SNI, чтобы блокираторы не видели к какому ресурсу обращаются. Технология отличная, но, к сожалению, запоздавшая. Точка, когда технология уже окончательно вьелась в стандарты де-факто, не была пройдена. Поэтому могут банить как по самому наличию ECH, так и по внешнему SNI-заглушке. На cloudflare это "cloudflare-ech.com". curl дергает сайт без ECH, а броузер может с ним - вот и разница.
|
||||
6. ECH - технология зашифрованной передачи SNI, чтобы блокираторы не видели к какому ресурсу обращаются. Технология отличная, но, к сожалению, запоздавшая. Точка, когда технология уже окончательно въелась в стандарты де-факто, не была пройдена. Поэтому могут банить как по самому наличию ECH, так и по внешнему SNI-заглушке. На cloudflare это "cloudflare-ech.com". curl дергает сайт без ECH, а броузер может с ним - вот и разница.
|
||||
7. Версия TLS протокола. По умолчанию blockcheck2 тестирует TLS 1.2 как самый тяжелый для обхода вариант. Броузер скорее всего полезет по TLS 1.3. Были случаи, когда блокираторы намеренно банили протокол TLS 1.3, потому что его использует и требует популярный метод обхода через VLESS-REALITY. Уровень пофигизма на сопутствующий ущерб уже настолько высок, что поломка множества легальных ресурсов их не останавливает.
|
||||
8. Знаменитый "16 кб" блок. Как проверить 16 кб блок ? Дернуть curl какой-то URI, на котором сайт выдаст достаточно длинную страницу. Если загрузка виснет посередине - это оно. По умолчанию blockcheck2 на https использует HEAD, чтобы не мучать сервер и экономить трафик - все равно под https ничего не видно. Это можно поменять через [CURL_HTTPS_GET=1](#shell-переменные). Но так вы скорее всего начнете получать сплошные "UNAVAILABLE". Стандартный вариант дает стратегии, которые будут работать при обходе 16кб блока, поэтому более информативен. Почему же они это делают ? Поставлена задача загнать всех под российскую юрисдикцию, а для этого гнобят крупные хостинги - cloudfare, hetzner, akamai, aws и другие. CDN часто используются для скрытого проброса прокси или VPN. Им надо оставить несколько важных сайтов. hp.com - откуда еще драйвера для LaserJet качать ? А hp на одном из таких хостингов. Получается белый список. Следовательно, вам нужен пейлоад, который ему удовлетворяет. Но беда в том, что блокчек , как и сам zapret, - всего лишь инструмент. Он не включает в себя готовых рецептов. Вам самим нужно искать чем пробивать конкретные хостинги. Сейчас это так, а завтра будет еще что-то другое. Автор не будет гнаться за ситуацией, не будет оперативно менять блокчек, чтобы он вам выдавал готовые copy+paste рецепты. Вам нужно самим смотреть на ситуацию и искать решение. Сегодня белые списки в стиле "16 кб" пробиваются или белым SNI, или каким-то другим белым типом протокола, не TLS. Завтра будет что-то другое. [Стандартный чекер](#тест-standard) берет целый список переменных для кустомизации сканирования. Есть возможность подсунуть [что-то до и что-то после](#shell-переменные) стратегии. Наконец, свет клином не сошелся на блокчеке. Иногда удобнее вручную проверять.
|
||||
8. Знаменитый "16 кб" блок. Как проверить 16 кб блок ? Дернуть curl какой-то URI, на котором сайт выдаст достаточно длинную страницу. Если загрузка виснет посередине - это оно. По умолчанию blockcheck2 на https использует HEAD, чтобы не мучать сервер и экономить трафик - все равно под https ничего не видно. Это можно поменять через [CURL_HTTPS_GET=1](#shell-переменные). Но так вы скорее всего начнете получать сплошные "UNAVAILABLE". Стандартный вариант дает стратегии, которые будут работать при обходе 16кб блока, поэтому более информативен. Почему же они это делают ? Поставлена задача загнать всех под российскую юрисдикцию, а для этого гнобят крупные хостинги - cloudflare, hetzner, akamai, aws и другие. CDN часто используются для скрытого проброса прокси или VPN. Им надо оставить несколько важных сайтов. hp.com - откуда еще драйвера для LaserJet качать ? А hp на одном из таких хостингов. Получается белый список. Следовательно, вам нужен пейлоад, который ему удовлетворяет. Но беда в том, что блокчек , как и сам zapret, - всего лишь инструмент. Он не включает в себя готовых рецептов. Вам самим нужно искать чем пробивать конкретные хостинги. Сейчас это так, а завтра будет еще что-то другое. Автор не будет гнаться за ситуацией, не будет оперативно менять блокчек, чтобы он вам выдавал готовые copy+paste рецепты. Вам нужно самим смотреть на ситуацию и искать решение. Сегодня белые списки в стиле "16 кб" пробиваются или белым SNI, или каким-то другим белым типом протокола, не TLS. Завтра будет что-то другое. [Стандартный чекер](#тест-standard) берет целый список переменных для кастомизации сканирования. Есть возможность подсунуть [что-то до и что-то после](#shell-переменные) стратегии. Наконец, свет клином не сошелся на блокчеке. Иногда удобнее вручную проверять.
|
||||
9. В продолжение предыдущего пункта - широко развилась практика вводить особые правила на диапазонах IP адресов. Поэтому вы можете найти стратегию, работающую для большинства ресурсов, но на некоторых она работать не будет.
|
||||
10. Ходит информация о поведении DPI в стиле "наказать шалуна". DPI видит попытки его задурить и на некоторое время блокирует доступ по IP. Раз-два прошло, потом никакие конекты не проходят. Если IP динамический, помогает переподключение к линии провайдера, но лишь до новой попытки. Другой вариант - отправка udp пакета на триггерный IP (например, через торрент клиент) и последующий блок некоторых IP диапазонов. Автор не сталкивался с подобными блокировками, но сами динамические блокировки - это тот путь, которым прошел Китай. В России тоже можно ожидать.
|
||||
11. Нестабильность стратегии. Балансировка на провайдерах - явление частое. Трафик может идти то через один DPI, то через другой, и стратегия может работать через раз. blockcheck2 по умолчанию тестирует только одну попытку. Если есть сомнения в стабильности - следует увеличить количество попыток минимум до 5. Можно использовать [PARALLEL=1](#shell-переменные), но это может привести к срабатыванию защиты самого ресурса от долбежки.
|
||||
11. Если вы пробиваете не https, а что-то под него мимикрирующее (прокси телеграм, например), могут применяться статистические методы. Они не могут отсечь по сигнатуре, но характер пакетов не похож на https, а похож на VPN. По некоторой информации для экономии ресурса поведенческий анализ включается только на определенных диапазонах "подозрительных" IP. В случае срабатывания триггера соединение может зависать не сразу, а в процессе. Дальше следует наказание плохого мальчика в виде бана IP на 2 минуты, при попытках продолжать плохое поведение - на 10 минут с пролонгацией бана, если активность не прекращается. Программа долбится, продлевая бан, и пока ее не закроете и не посидите в углу нужное время, ничего вы не получите.
|
||||
12. Нестабильность стратегии. Балансировка на провайдерах - явление частое. Трафик может идти то через один DPI, то через другой, и стратегия может работать через раз. blockcheck2 по умолчанию тестирует только одну попытку. Если есть сомнения в стабильности - следует увеличить количество попыток минимум до 5. Можно использовать [PARALLEL=1](#shell-переменные), но это может привести к срабатыванию защиты самого ресурса от долбежки.
|
||||
|
||||
Гланая цель блокираторов - устранить массовое нарушение их блокировок.
|
||||
Главная цель блокираторов - устранить массовое нарушение их блокировок.
|
||||
Если 99% нажимает на кнопку, и она не срабатывает, для них решение не работает.
|
||||
На деле же требуется техническое исследование что они там натворили очередной раз. Много ручных тестов для выявление алгоритма их действий и нащупывание обходного решения.
|
||||
|
||||
@@ -5185,9 +5274,9 @@ nfqws2 может работать и самостоятельно без скр
|
||||
- netifd интерфейсы - это то, что видно в `/etc/config/network` или в Luci, и что берет команда ifstatus. Не являются интерфейсами Linux. Интерфейсы Linux указываются в параметре device в определении интерфейса и видны по команде `ip link`. Например, интерфейс netifd - "lan", интерфейс Linux - "br-lan". В OPENWRT_LAN надо вписывать "lan", а не "br-lan", иначе это работать не будет.
|
||||
- Указание LAN интерфейсов нужно только для flow offloading в nftables, больше ни для чего не используется.
|
||||
- Параметры командной строки `NFQWS2_OPT` включают только стратегию. Стандартные Lua файлы, служебные параметры типа `--qnum` или `--user` добавляются автоматически. Можно добавлять свои `--blob` или `--lua-init`.
|
||||
- `NFQWS2_OPT` берет маркеры `<HOSTLIST>` и `<HOSTLIST_NOAUTO>`. Это подстановка стандартных листов. Маркеры замещаются на параметры `--hostlist` и `--hostlist-auto` в зависимости от режима `MODE_FILTER`. `<HOSTLIST_NOAUTO>` добавляет автохослист как обычный лист без авто. В параметры вписываются только те файлы стандартных листов, которые по факту есть. Маркеры могут вписываться в разные профили, и только вы можете знать куда их вписывать , а куда нет, исходя из логики ваших стратегий.
|
||||
- `NFQWS2_OPT` берет маркеры `<HOSTLIST>` и `<HOSTLIST_NOAUTO>`. Это подстановка стандартных листов. Маркеры замещаются на параметры `--hostlist` и `--hostlist-auto` в зависимости от режима `MODE_FILTER`. `<HOSTLIST_NOAUTO>` добавляет автохостлист как обычный лист без авто. В параметры вписываются только те файлы стандартных листов, которые по факту есть. Маркеры могут вписываться в разные профили, и только вы можете знать куда их вписывать , а куда нет, исходя из логики ваших стратегий.
|
||||
- Прямое указание параметров в `NFQWS2_OPT` типа `--hostlist=/opt/zapret2/ipset/zapret-hosts-user.txt` крайне не приветствуется, поскольку ломает логику `MODE_FILTER` и скриптов получения листов - их результат может не быть учтен.
|
||||
- Класть свои файлы в `/opt/zapret2` чревато тем, что их снесет инсталятор при обновлении zapret2. Используете расположение вне каталога zapret2.
|
||||
- Класть свои файлы в `/opt/zapret2` чревато тем, что их снесет инсталлятор при обновлении zapret2. Используете расположение вне каталога zapret2.
|
||||
|
||||
## Система ведения листов
|
||||
|
||||
@@ -5416,7 +5505,7 @@ fw_nfqws_pre()
|
||||
|
||||
Что не следует писать в фильтр :
|
||||
|
||||
- Проверку стандарных exclude ipset - nozapret, nozapret6.
|
||||
- Проверку стандартных exclude ipset - nozapret, nozapret6.
|
||||
- Проверку по [DESYNC_FWMARK](#файл-config).
|
||||
|
||||
Будут ли применены правила для каждой версии ip зависит от настроек [config](#файл-config).
|
||||
@@ -5505,7 +5594,7 @@ nft_fw_nfqws_pre()
|
||||
|
||||
Что не следует писать в фильтр :
|
||||
|
||||
- Проверку стандарных exclude ipset - nozapret, nozapret6.
|
||||
- Проверку стандартных exclude ipset - nozapret, nozapret6.
|
||||
- Проверку по [DESYNC_FWMARK](#файл-config).
|
||||
|
||||
Будут ли применены правила для каждой версии ip зависит от настроек [config](#файл-config).
|
||||
@@ -5622,7 +5711,7 @@ uci set firewall.@include[-1].reload=1
|
||||
uci commit
|
||||
```
|
||||
|
||||
4. Обновление листов - cron job на вызов `/opt/zapret2/ipset/get_config.sh` ночью в случайное время каждые 2 дня - для обновления листов. Рассчет идет на роутер, работающий круглосуточно.
|
||||
4. Обновление листов - cron job на вызов `/opt/zapret2/ipset/get_config.sh` ночью в случайное время каждые 2 дня - для обновления листов. Расчёт идет на роутер, работающий круглосуточно.
|
||||
|
||||
### Шпаргалка OpenWRT
|
||||
|
||||
@@ -5643,7 +5732,7 @@ systemctl daemon-reload
|
||||
systemctl enable zapret2
|
||||
```
|
||||
|
||||
2. Таймер для обновления листов - в случайное время суток каждые 2 дня. Рассчет идет на любую систему, в том числе десктопную, которую могут включать только днем.
|
||||
2. Таймер для обновления листов - в случайное время суток каждые 2 дня. Расчет идет на любую систему, в том числе десктопную, которую могут включать только днем.
|
||||
|
||||
```
|
||||
cp /opt/zapret2/init.d/systemd/zapret2-list-update.* /lib/systemd/system
|
||||
@@ -5670,7 +5759,7 @@ ln -s /opt/zapret2/init.d/openrc/zapret2 /etc/init.d
|
||||
rc-update add zapret2
|
||||
```
|
||||
|
||||
2. Обновление листов - cron job на вызов `/opt/zapret2/ipset/get_config.sh` днем в случайное время каждые 2 дня. Рассчет идет на любую систему, в том числе десктопную, которую могут включать только днем.
|
||||
2. Обновление листов - cron job на вызов `/opt/zapret2/ipset/get_config.sh` днем в случайное время каждые 2 дня. Расчет идет на любую систему, в том числе десктопную, которую могут включать только днем.
|
||||
|
||||
### Шпаргалка openrc
|
||||
|
||||
@@ -5688,7 +5777,7 @@ rc-update add zapret2
|
||||
1. `cp /opt/zapret2/init.d/systemd/nfqws2\@.service /lib/systemd/system`
|
||||
2. `systemctl daemon-reload`
|
||||
3. `mkdir /etc/zapret2`
|
||||
4. Создаете в `/etc/zapret2` текстовый файл с параметрами коммандной строки nfqws2. Файл называется INSTANCE.conf, где INSTANCE - название инстанса, которое вы придумываете сами.
|
||||
4. Создаете в `/etc/zapret2` текстовый файл с параметрами командной строки nfqws2. Файл называется INSTANCE.conf, где INSTANCE - название инстанса, которое вы придумываете сами.
|
||||
5. Автозапуск : `systemctl enable nfqws2@INSTANCE`
|
||||
6. Запуск : `systemctl start nfqws2@INSTANCE`
|
||||
7. Останов : `systemctl stop nfqws2@INSTANCE`
|
||||
@@ -5731,7 +5820,7 @@ entware содержит репозиторий user-mode компонент, к
|
||||
Перед запуском скрипта путь к дополнительным программам должен быть помещен в PATH.
|
||||
|
||||
Обратите внимание, что entware патчен на предмет замены стандартных путей файлов типа /etc/passwd на /opt/etc/passwd.
|
||||
Статические бинарники zapret собраны без учета этой особенности, поэтому оцпия --user может не работать - юзер ищется в /etc/passwd,
|
||||
Статические бинарники zapret собраны без учета этой особенности, поэтому опция --user может не работать - юзер ищется в /etc/passwd,
|
||||
который находится на r/o разделе, в том время как adduser добавляет юзеров в /opt/etc/passwd.
|
||||
Поэтому может понадобиться раскомментировать в [config](#файл-config) WS_USER и указать юзера, который есть в /etc/passwd.
|
||||
|
||||
@@ -5770,7 +5859,7 @@ cygwin эмулирует общее пространство PID процесс
|
||||
Чтобы иметь возможность послать сигнал, нужно, чтобы winws2 и программа для посылки сигнала (kill, killall) запускались с одним cygwin1.dll.
|
||||
|
||||
Можно использовать отдельно установленную среду cygwin, а можно скачать готовый [zapret-win-bundle](https://github.com/bol-van/zapret-win-bundle), где уже
|
||||
присутствует минимальный комплект cygwin. cygwin prompt настроен с aliases на запуск blockcheck, blockcheck2, winws, winws2, winws2 со станадртными Lua скриптами,
|
||||
присутствует минимальный комплект cygwin. cygwin prompt настроен с aliases на запуск blockcheck, blockcheck2, winws, winws2, winws2 со стандартными Lua скриптами,
|
||||
чтобы не возиться с путями, а сосредоточиться на главном.
|
||||
|
||||
32-битные версии Windows в zapret-win-bundle не поддерживаются.
|
||||
|
||||
+24
-13
@@ -9,8 +9,8 @@
|
||||
VPN. Может использоваться для частичной прозрачной обфускации протоколов.
|
||||
|
||||
Проект нацелен прежде всего на маломощные embedded устройства - роутеры, работающие под OpenWrt. Поддерживаются
|
||||
традиционные Linux-системы, FreeBSD, OpenBSD, Windows. В некоторых случаях возможна самостоятельная прикрутка
|
||||
решения к различным прошивкам.
|
||||
традиционные Linux-системы, FreeBSD, OpenBSD, Windows. MacOS не поддерживается и вряд ли будет по техническим причинам.
|
||||
В некоторых случаях возможна самостоятельная прикрутка решения к различным прошивкам.
|
||||
|
||||
[Полный мануал](manual.md)
|
||||
|
||||
@@ -52,11 +52,16 @@ Lua код получает от C кода структурированное
|
||||
Имеется библиотека хелперов, написанных на Lua, а так же готовая библиотека программ атаки на DPI (стратегий), реализующая функции *nfqws1* в расширенном варианте
|
||||
и с большей гибкостью.
|
||||
|
||||
Вы всегда сможете взять и дописать что-то свое. В этом и есть смысл, чтобы борьбой с DPI смог заняться любой, кто разбирается в пакетах.
|
||||
Вы всегда сможете взять и дописать что-то свое. От мелкой частной кустомизации, которую бессмысленно зашивать намертво в C код, до полноценной desync функции.
|
||||
В этом и есть смысл, чтобы борьбой с DPI смог заняться любой, кто разбирается в пакетах.
|
||||
Мог "потыкать" его, проверить свои идеи. А потом поделиться с друзьями своим решением "одного клика".
|
||||
zapret2 - инструмент для таких энтузиастов. Но это не готовое решение для чайников. Проект не ставит себе целью сделать все простым для всех.
|
||||
Автор считает, что это невозможно в принципе по обьективным причинам.
|
||||
|
||||
Борьбой с DPI возможности zapret2 не ограничиваются. По сути это мультиплатформенный пакетный программируемый манипулятор.
|
||||
Вы можете использовать его для генерации произвольного IPv4/IPv6 трафика, обфускации или шифрования имеющегося, построения нестандартных протоколов обмена через IP сети,
|
||||
реализации собственных firewall или DPI систем.
|
||||
|
||||
|
||||
## С чего начать
|
||||
|
||||
@@ -102,7 +107,7 @@ windivert вообще не отслеживает соединения. Поэ
|
||||
Дальше под рутом нужно запустить *nfqws2* с параметрами командной строки. Они строятся примерно следующим образом :
|
||||
|
||||
```
|
||||
nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua \
|
||||
nfqws2 --qnum=200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua \
|
||||
--filter-tcp=80,443 --filter-l7=tls,http \
|
||||
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_md5:tls_mod=rnd,rndsni,dupsid \
|
||||
--payload=http_req --lua-desync=fake:blob=fake_default_http:tcp_md5 \
|
||||
@@ -117,7 +122,7 @@ nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.
|
||||
После двоеточия и через двоеточия следуют параметры для данной функции в формате `param[=value]`. В примере реализована стратегия
|
||||
|
||||
```
|
||||
nfqws --qnum 200 --debug \
|
||||
nfqws --qnum=200 --debug \
|
||||
--filter-tcp=80,443 --filter-l7=tls,http \
|
||||
--dpi-desync=fake,multisplit --dpi-desync-fooling=md5sig --dpi-desync-split-pos=1,midsld \
|
||||
--dpi-desync-split-seqovl=5 --dpi-desync-split-seqovl-pattern=0x1603030000 \
|
||||
@@ -150,7 +155,7 @@ nfqws --qnum 200 --debug \
|
||||
В *nfqws1* они падали на профиль с автолистом.
|
||||
|
||||
```
|
||||
nfqws2 --qnum 200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua \
|
||||
nfqws2 --qnum=200 --debug --lua-init=@zapret-lib.lua --lua-init=@zapret-antidpi.lua \
|
||||
--filter-tcp=80 --filter-l7=http --hostlist=mylist1.txt --lua-desync=multisplit --new \
|
||||
--filter-tcp=80 --filter-l7=http --hostlist-exclude=mylist2.txt --lua-desync=fake:blob=0x00000000:ip_ttl=5:ip6_ttl=3 --lua-desync=multidisorder:pos=5,endhost-1 --new \
|
||||
--filter-tcp=443 --filter-l7=tls --hostlist=mylist1.txt --lua-desync=multidisorder
|
||||
@@ -167,7 +172,7 @@ range задается как `mX-mY`, `mX<mY`, `-mY`, `<mY`, `mX-`.
|
||||
Есть и режимы, не требущие числа - `a` - всегда, `x` - никогда.
|
||||
Если разделителем указан знак '-' - конечная позиция включительна, а если '<' - не включительна.
|
||||
|
||||
Установка по умолчанию `--in-range=x --out-range=a --payload all`. То есть отсекаются все входящие пакеты и берутся все исходящие.
|
||||
Установка по умолчанию `--in-range=x --out-range=a --payload=all`. То есть отсекаются все входящие пакеты и берутся все исходящие.
|
||||
|
||||
`--in-range`, `--out-range` и `--payload` фильтры могут присутствовать множественно в одном профиле.
|
||||
Их действие распространяется на все последующие `--lua-desync` функции до следующего фильтра того же типа или до конца профиля.
|
||||
@@ -248,7 +253,7 @@ nfqws \
|
||||
nfqws2 \
|
||||
--blob=tls_google:@tls_clienthello_google_com.bin \
|
||||
--filter-l7=tls \
|
||||
--payload tls_client_hello,http_req \
|
||||
--payload=tls_client_hello,http_req \
|
||||
--lua-desync=fakedsplit:pattern=tls_google:pos=method+2:nofake1:ip_ttl=1:ip6_ttl=1:ip_autottl=-1,3-20:ip6_autottl=-1,3-20
|
||||
```
|
||||
|
||||
@@ -269,19 +274,19 @@ nfqws2 --lua-desync=wssize:wsize=1:scale=6 --lua-desync=syndata --lua-desync=mul
|
||||
|
||||
```
|
||||
nfqws1 \
|
||||
--filter-l7 tls \
|
||||
--filter-l7=tls \
|
||||
--dpi-desync=fake --dpi-desync-fooling=datanoack --dpi-desync-fake-tls=! \
|
||||
--dpi-desync-fake-tls-mod=rnd,rndsni,dupsid
|
||||
|
||||
nfqws2
|
||||
--filter-l7 tls \
|
||||
--filter-l7=tls \
|
||||
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_flags_unset=ack:tls_mod=rnd,rndsni,dupsid,padencap \
|
||||
--payload=empty --out-range="s1<d1" --lua-desync=pktmod:ip_ttl=1:ip6_ttl=1
|
||||
|
||||
|
||||
nfqws2 \
|
||||
--lua-init="fake_default_tls=tls_mod(fake_default_tls,'rnd,rndsni')" \
|
||||
--filter-l7 tls \
|
||||
--filter-l7=tls \
|
||||
--payload=tls_client_hello --lua-desync=fake:blob=fake_default_tls:tcp_flags_unset=ack:tls_mod=dupsid,padencap \
|
||||
--payload=empty --out-range="s1<d1" --lua-desync=pktmod:ip_ttl=1:ip6_ttl=1
|
||||
```
|
||||
@@ -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 можно коды делать разными для клиента и сервера.
|
||||
@@ -468,7 +479,7 @@ table ip ztest {
|
||||
```
|
||||
|
||||
```
|
||||
nfqws2 --qnum 200 --server
|
||||
nfqws2 --qnum=200 --server
|
||||
--lua-init=@/opt/zapret2/lua/zapret-lib.lua
|
||||
--lua-init=@/opt/zapret2/lua/zapret-obfs.lua
|
||||
--in-range=a
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
# can override in config :
|
||||
NFQWS_OPT_DESYNC_DISCORD_MEDIA="${NFQWS_OPT_DESYNC_DISCORD_MEDIA:---payload=discord_ip_discovery --lua-desync=fake:blob=0x00000000000000000000000000000000:repeats=2}"
|
||||
DISCORD_MEDIA_PORT_RANGE="${DISCORD_MEDIA_PORT_RANGE:-50000-50099}"
|
||||
DISCORD_MEDIA_PORT_RANGE="${DISCORD_MEDIA_PORT_RANGE:-50000-50099,19294-19344}"
|
||||
|
||||
alloc_dnum DNUM_DISCORD_MEDIA
|
||||
alloc_qnum QNUM_DISCORD_MEDIA
|
||||
@@ -21,7 +21,7 @@ zapret_custom_firewall()
|
||||
|
||||
local DISABLE_IPV6=1
|
||||
local port_range=$(replace_char - : $DISCORD_MEDIA_PORT_RANGE)
|
||||
local f="-p udp --dport $port_range -m u32 --u32"
|
||||
local f="-p udp -m multiport --dports $port_range -m u32 --u32"
|
||||
# this is simplified test to skip writing monstrous rule. instead of checking 64 bytes for zeroes only check 2 dwords for zero
|
||||
fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=0x52&&0>>22&0x3C@8=0x00010046&&0>>22&0x3C@16=0&&0>>22&0x3C@76=0" '' $QNUM_DISCORD_MEDIA
|
||||
}
|
||||
@@ -30,6 +30,6 @@ zapret_custom_firewall_nft()
|
||||
# stop logic is not required
|
||||
|
||||
local DISABLE_IPV6=1
|
||||
local f="udp dport $DISCORD_MEDIA_PORT_RANGE udp length == 82 @ih,0,32 0x00010046 @ih,64,128 0x00000000000000000000000000000000 @ih,192,128 0x00000000000000000000000000000000 @ih,320,128 0x00000000000000000000000000000000 @ih,448,128 0x00000000000000000000000000000000"
|
||||
local f="udp dport {$DISCORD_MEDIA_PORT_RANGE} udp length == 82 @ih,0,32 0x00010046 @ih,64,128 0x00000000000000000000000000000000 @ih,192,128 0x00000000000000000000000000000000 @ih,320,128 0x00000000000000000000000000000000 @ih,448,128 0x00000000000000000000000000000000"
|
||||
nft_fw_nfqws_post "$f" '' $QNUM_DISCORD_MEDIA
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
outbound and ip and
|
||||
udp.DstPort>=50000 and udp.DstPort<=50099 and
|
||||
(udp.DstPort>=50000 and udp.DstPort<=50099 or udp.DstPort>=19294 and udp.DstPort<=19344) and
|
||||
udp.PayloadLength=74 and
|
||||
udp.Payload32[0]=0x00010046 and
|
||||
udp.Payload32[2]=0 and
|
||||
|
||||
+9
-7
@@ -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
|
||||
+14
-4
@@ -86,6 +86,7 @@ end
|
||||
|
||||
-- nfqws1 : "--dup"
|
||||
-- standard args : direction, fooling, ip_id, ipfrag, rawsend, reconstruct
|
||||
-- arg : delay=<msec> - drop and send delayed
|
||||
function send(ctx, desync)
|
||||
direction_cutoff_opposite(ctx, desync, "any")
|
||||
if direction_check(desync, "any") then
|
||||
@@ -93,10 +94,19 @@ function send(ctx, desync)
|
||||
local dis = deepcopy(desync.dis)
|
||||
apply_fooling(desync, dis)
|
||||
apply_ip_id(desync, dis, nil, "none")
|
||||
-- it uses rawsend, reconstruct and ipfrag options
|
||||
rawsend_dissect_ipfrag(dis, desync_opts(desync))
|
||||
if desync.arg.delay then
|
||||
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
|
||||
rawsend_dissect_ipfrag(dis, desync_opts(desync))
|
||||
end
|
||||
end
|
||||
end
|
||||
function send_timer_delayed(name, data)
|
||||
-- oneshot timer, auto deletes
|
||||
rawsend_dissect_ipfrag(data.dis, data.opts)
|
||||
end
|
||||
|
||||
-- nfqws1 : "--orig"
|
||||
-- apply modification to current packet
|
||||
@@ -579,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
|
||||
@@ -633,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
@@ -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)
|
||||
|
||||
+199
-113
@@ -1,4 +1,4 @@
|
||||
NFQWS2_COMPAT_VER_REQUIRED=5
|
||||
NFQWS2_COMPAT_VER_REQUIRED=6
|
||||
|
||||
if NFQWS2_COMPAT_VER~=NFQWS2_COMPAT_VER_REQUIRED then
|
||||
error("Incompatible NFQWS2_COMPAT_VER. Use pktws and lua scripts from the same release !")
|
||||
@@ -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)
|
||||
@@ -761,6 +762,63 @@ function fix_ip6_next(ip6, last_proto)
|
||||
end
|
||||
end
|
||||
|
||||
function dis_ipsrc(dis)
|
||||
if dis.ip then
|
||||
return dis.ip.ip_src
|
||||
elseif dis.ip6 then
|
||||
return dis.ip6.ip6_src
|
||||
end
|
||||
end
|
||||
function dis_ipdst(dis)
|
||||
if dis.ip then
|
||||
return dis.ip.ip_dst
|
||||
elseif dis.ip6 then
|
||||
return dis.ip6.ip6_dst
|
||||
end
|
||||
end
|
||||
|
||||
function dis_l4_name(dis)
|
||||
local l4
|
||||
if dis.tcp then
|
||||
l4="tcp"
|
||||
elseif dis.udp then
|
||||
l4="udp"
|
||||
elseif dis.icmp then
|
||||
l4="icmp"
|
||||
else
|
||||
l4="raw"..ip_proto_l3(dis)
|
||||
end
|
||||
return l4
|
||||
end
|
||||
function dis_l4_ports(dis)
|
||||
local p1,p2
|
||||
if dis.tcp then
|
||||
p1=dis.tcp.th_sport
|
||||
p2=dis.tcp.th_dport
|
||||
elseif dis.udp then
|
||||
p1=dis.udp.uh_sport
|
||||
p2=dis.udp.uh_dport
|
||||
elseif dis.icmp then
|
||||
p1=dis.icmp.icmp_type
|
||||
p2=dis.icmp.icmp_code
|
||||
end
|
||||
return p1 and (p1..":"..p2) or "?"
|
||||
end
|
||||
|
||||
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 = desync.func_instance.."_"..dis_timer_name(desync.dis)
|
||||
if desync.track then
|
||||
name = name.."_n"..desync.track.pos.direct.pcounter
|
||||
else
|
||||
name = name.."_"..brandom_az09(10)
|
||||
end
|
||||
return name
|
||||
end
|
||||
|
||||
|
||||
-- reverses ip addresses, ports and seq/ack
|
||||
function dis_reverse(dis)
|
||||
if dis.ip then
|
||||
@@ -860,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.
|
||||
@@ -1193,7 +1251,9 @@ function rawsend_dissect_segmented(desync, dis, mss, options)
|
||||
len = #payload - pos + 1
|
||||
if len > max_data then len = max_data end
|
||||
if oob then
|
||||
if urp>=pos and urp<(pos+len)then
|
||||
if urp==0 and pos==1 then
|
||||
discopy.tcp.th_flags = bitor(discopy.tcp.th_flags, TH_URG)
|
||||
elseif urp>=pos and urp<(pos+len) then
|
||||
discopy.tcp.th_flags = bitor(discopy.tcp.th_flags, TH_URG)
|
||||
discopy.tcp.th_urp = urp-pos+1
|
||||
else
|
||||
@@ -1230,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"
|
||||
@@ -1254,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)
|
||||
@@ -1376,9 +1436,9 @@ end
|
||||
function append_path(path,file)
|
||||
return string.sub(path,#path,#path)=='/' and path..file or path.."/"..file
|
||||
end
|
||||
function writeable_file_name(filename)
|
||||
function writable_file_name(filename)
|
||||
if is_absolute_path(filename) then return filename end
|
||||
local writedir = os.getenv("WRITEABLE")
|
||||
local writedir = os.getenv("WRITABLE")
|
||||
if not writedir then return filename end
|
||||
return append_path(writedir, filename)
|
||||
end
|
||||
@@ -1416,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)
|
||||
@@ -1542,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 = {} } } )
|
||||
@@ -1690,6 +1754,7 @@ function writefile(filename, data)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- DISSECTORS
|
||||
|
||||
function http_dissect_header(header)
|
||||
@@ -1798,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)
|
||||
@@ -2216,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
|
||||
|
||||
@@ -2325,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) }
|
||||
@@ -2354,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
|
||||
@@ -2371,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
|
||||
@@ -2589,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
|
||||
|
||||
@@ -2641,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
@@ -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)
|
||||
|
||||
+3
-3
@@ -15,8 +15,8 @@ function pcap_write(file, raw)
|
||||
pcap_write_packet(file, raw)
|
||||
end
|
||||
|
||||
-- test case : --writeable=zdir --in-range=a --lua-desync=pcap:file=test.pcap
|
||||
-- arg : file=<filename> - file for storing pcap data. if --writeable is specified and filename is relative - append filename to writeable path
|
||||
-- test case : --writable=zdir --in-range=a --lua-desync=pcap:file=test.pcap
|
||||
-- arg : file=<filename> - file for storing pcap data. if --writable is specified and filename is relative - append filename to writable path
|
||||
-- arg : keep - do not overwrite file, append packets to existing
|
||||
function pcap(ctx, desync)
|
||||
if not desync.arg.file or #desync.arg.file==0 then
|
||||
@@ -24,7 +24,7 @@ function pcap(ctx, desync)
|
||||
end
|
||||
local fn_cache_name = desync.func_instance.."_fn"
|
||||
if not _G[fn_cache_name] then
|
||||
_G[fn_cache_name] = writeable_file_name(desync.arg.file)
|
||||
_G[fn_cache_name] = writable_file_name(desync.arg.file)
|
||||
if not desync.arg.keep then
|
||||
-- overwrite file
|
||||
os.remove(_G[fn_cache_name])
|
||||
|
||||
+44
-9
@@ -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)
|
||||
|
||||
@@ -907,20 +904,58 @@ function test_ifaddrs(opts)
|
||||
end
|
||||
end
|
||||
|
||||
function print_current_time()
|
||||
local sec,nsec = clock_gettime()
|
||||
local t = sec + nsec/1000000000;
|
||||
print("time: "..t)
|
||||
end
|
||||
function timer_info_print(tinfo)
|
||||
print(" timer_info.name="..tinfo.name)
|
||||
print(" timer_info.period="..tinfo.period)
|
||||
print(" timer_info.oneshot="..tostring(tinfo.oneshot))
|
||||
print(" timer_info.fires="..tinfo.fires)
|
||||
end
|
||||
function timer_info_print_by_name(name)
|
||||
local tinfo = timer_info(name)
|
||||
timer_info_print(tinfo)
|
||||
end
|
||||
|
||||
function timer1(name, data)
|
||||
print("timer "..name.." fired. data="..tostring(data))
|
||||
print_current_time()
|
||||
timer_info_print_by_name(name)
|
||||
end
|
||||
function timer2(name, data)
|
||||
data.n = data.n+1
|
||||
print("timer "..name.." fired. data.n="..tostring(data.n))
|
||||
if data.n>=3 then
|
||||
print_current_time()
|
||||
timer_info_print_by_name(name)
|
||||
if data.n>=4 then
|
||||
timer_del(name)
|
||||
end
|
||||
end
|
||||
function test_timer(opts)
|
||||
timer_set("t1","timer1",500,true,"sample_data");
|
||||
-- anonymous function
|
||||
timer_set("t0",function(name,data)
|
||||
print("timer "..name.." fired. anonymous timer function")
|
||||
print_current_time()
|
||||
end,
|
||||
100,true)
|
||||
|
||||
-- pass function as function
|
||||
timer_set("t1",timer1,500,true,"sample_data");
|
||||
local tbl = {n=0}
|
||||
-- pass function through string name
|
||||
timer_set("t2","timer2",700,false,tbl);
|
||||
|
||||
print("* timers\n")
|
||||
local timers=timer_enum()
|
||||
for i,name in ipairs(timers) do
|
||||
print("TIMER "..i.." :")
|
||||
timer_info_print_by_name(name)
|
||||
end
|
||||
print_current_time()
|
||||
print()
|
||||
end
|
||||
|
||||
|
||||
|
||||
+11
-9
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+2
-11
@@ -106,7 +106,7 @@ static void ConntrackInitTrack(t_ctrack *t)
|
||||
t->l7proto = L7_UNKNOWN;
|
||||
t->reasm_client_payload = L7P_UNKNOWN;
|
||||
t->pos.client.scale = t->pos.server.scale = 0;
|
||||
rawpacket_queue_init(&t->delayed);
|
||||
rawpacket_queue_init(&t->delayed, RAW_PACKET_QUEUE_DELAYED_MAX);
|
||||
lua_newtable(params.L);
|
||||
t->lua_state = luaL_ref(params.L, LUA_REGISTRYINDEX);
|
||||
lua_newtable(params.L);
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
+3
-1
@@ -29,6 +29,8 @@
|
||||
|
||||
#define RETRANS_COUNTER_STOP ((uint8_t)-1)
|
||||
|
||||
#define RAW_PACKET_QUEUE_DELAYED_MAX 64
|
||||
|
||||
typedef union {
|
||||
struct in_addr ip;
|
||||
struct in6_addr ip6;
|
||||
@@ -82,7 +84,7 @@ typedef struct
|
||||
|
||||
t_l7payload reasm_client_payload; // payload type being reassembled
|
||||
t_reassemble reasm_client;
|
||||
struct rawpacket_tailhead delayed;
|
||||
struct rawpacket_queue delayed;
|
||||
} t_ctrack;
|
||||
|
||||
typedef struct
|
||||
|
||||
+64
-38
@@ -20,6 +20,8 @@
|
||||
#include "nfqws.h"
|
||||
|
||||
#ifdef __CYGWIN__
|
||||
#include "timer.h"
|
||||
|
||||
#include <sys/cygwin.h>
|
||||
|
||||
#include <wlanapi.h>
|
||||
@@ -375,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)
|
||||
{
|
||||
@@ -557,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)))
|
||||
{
|
||||
@@ -1109,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;
|
||||
|
||||
@@ -1228,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)))
|
||||
@@ -1286,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);
|
||||
@@ -1523,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)
|
||||
@@ -1627,32 +1637,35 @@ static bool windivert_recv_exit(void)
|
||||
}
|
||||
return false;
|
||||
}
|
||||
static DWORD win_timer_check(t_timer_callback timer_callback, uint64_t *bt_prev)
|
||||
{
|
||||
uint64_t bt,dbt;
|
||||
|
||||
bt = boottime_ms();
|
||||
dbt = bt-*bt_prev;
|
||||
if (dbt>=params.timer_res)
|
||||
{
|
||||
timer_callback(bt);
|
||||
|
||||
*bt_prev = bt;
|
||||
return params.timer_res;
|
||||
}
|
||||
else
|
||||
return params.timer_res-(int)dbt;
|
||||
}
|
||||
|
||||
static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev)
|
||||
static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, uint64_t *bt_next)
|
||||
{
|
||||
UINT recv_len;
|
||||
DWORD rd,twait,tmax;
|
||||
DWORD rd,twait;
|
||||
uint64_t tmax;
|
||||
unsigned int wac;
|
||||
uint64_t bt;
|
||||
|
||||
if (windivert_recv_exit()) return false;
|
||||
if (windivert_recv_exit())
|
||||
return false;
|
||||
|
||||
tmax = win_timer_check(timer_callback, bt_prev);
|
||||
if (params.timers)
|
||||
{
|
||||
if (!*bt_next || params.timers_dirty) *bt_next = TimerPoolNext(params.timers, ¶ms.timers_dirty);
|
||||
bt = boottime_ms();
|
||||
tmax = *bt_next>bt ? *bt_next-bt : 0;
|
||||
if (!tmax)
|
||||
{
|
||||
*bt_next = TimerPoolRun(¶ms.timers, ¶ms.timers_dirty, bt);
|
||||
bt = boottime_ms();
|
||||
tmax = *bt_next>bt ? *bt_next-bt : 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*bt_next = 0;
|
||||
tmax = 0;
|
||||
}
|
||||
|
||||
wac = *wa_count * sizeof(WINDIVERT_ADDRESS);
|
||||
if (WinDivertRecvEx(hFilter, packet, *len, &recv_len, 0, wa, &wac, &ovl))
|
||||
@@ -1669,13 +1682,24 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len,
|
||||
// need to check for signals periodically
|
||||
for(;;)
|
||||
{
|
||||
twait = tmax>50 ? 50 : tmax;
|
||||
tmax -= twait;
|
||||
if (params.timers)
|
||||
{
|
||||
twait = tmax>50 ? 50 : (DWORD)tmax;
|
||||
tmax -= twait;
|
||||
}
|
||||
else
|
||||
twait = 50;
|
||||
// make signals working
|
||||
if (WaitForSingleObject(ovl.hEvent,twait)!=WAIT_TIMEOUT) break;
|
||||
if (windivert_recv_exit())
|
||||
return false;
|
||||
if (!tmax) tmax = win_timer_check(timer_callback, bt_prev);
|
||||
if (params.timers && !tmax)
|
||||
{
|
||||
bt = boottime_ms();
|
||||
*bt_next = TimerPoolRun(¶ms.timers, ¶ms.timers_dirty, bt);
|
||||
bt = boottime_ms();
|
||||
tmax = *bt_next>bt ? *bt_next-bt : 0;
|
||||
}
|
||||
}
|
||||
if (!GetOverlappedResult(hFilter,&ovl,&rd,FALSE))
|
||||
{
|
||||
@@ -1700,9 +1724,9 @@ cancel:
|
||||
GetOverlappedResult(hFilter, &ovl, &rd, TRUE);
|
||||
return false;
|
||||
}
|
||||
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev)
|
||||
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, uint64_t *bt_next)
|
||||
{
|
||||
return windivert_recv_filter(w_filter,packet,len,wa,wa_count,timer_callback,bt_prev);
|
||||
return windivert_recv_filter(w_filter,packet,len,wa,wa_count,bt_next);
|
||||
}
|
||||
|
||||
static bool windivert_send_filter(HANDLE hFilter, const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa)
|
||||
@@ -1962,7 +1986,9 @@ bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const
|
||||
ssize_t bytes;
|
||||
int sock=rawsend_socket(dst->sa_family);
|
||||
if (sock==-1) return false;
|
||||
#if defined(BSD) || defined(__linux__)
|
||||
if (!set_socket_fwmark(sock,fwmark)) return false;
|
||||
#endif
|
||||
int salen = dst->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6);
|
||||
struct sockaddr_storage dst2;
|
||||
memcpy(&dst2,dst,salen);
|
||||
@@ -2030,7 +2056,7 @@ bool rawsend_rp(const struct rawpacket *rp)
|
||||
{
|
||||
return rawsend((struct sockaddr*)&rp->dst,rp->fwmark,rp->ifout,rp->packet,rp->len);
|
||||
}
|
||||
bool rawsend_queue(struct rawpacket_tailhead *q)
|
||||
bool rawsend_queue(struct rawpacket_queue *q)
|
||||
{
|
||||
struct rawpacket *rp;
|
||||
bool b;
|
||||
@@ -2463,11 +2489,11 @@ bool set_socket_buffers(int fd, int rcvbuf, int sndbuf)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool make_writeable_dir()
|
||||
bool make_writable_dir()
|
||||
{
|
||||
char wdir[PATH_MAX], *wrdir;
|
||||
if (*params.writeable_dir)
|
||||
wrdir = params.writeable_dir;
|
||||
if (*params.writable_dir)
|
||||
wrdir = params.writable_dir;
|
||||
else
|
||||
{
|
||||
#ifdef __CYGWIN__
|
||||
@@ -2501,6 +2527,6 @@ bool make_writeable_dir()
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (b) setenv("WRITEABLE",wrdir,1);
|
||||
if (b) setenv("WRITABLE",wrdir,1);
|
||||
return b;
|
||||
}
|
||||
+4
-5
@@ -121,7 +121,7 @@ bool tcp_synack_segment(const struct tcphdr *tcphdr);
|
||||
bool tcp_syn_segment(const struct tcphdr *tcphdr);
|
||||
|
||||
|
||||
bool make_writeable_dir();
|
||||
bool make_writable_dir();
|
||||
bool ensure_file_access(const char *filename);
|
||||
#ifdef __CYGWIN__
|
||||
extern uint32_t w_win32_error;
|
||||
@@ -129,14 +129,13 @@ 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);
|
||||
bool nlm_list(bool bAll);
|
||||
bool windivert_init(const char *filter);
|
||||
typedef void (*t_timer_callback)(uint64_t bt);
|
||||
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, t_timer_callback timer_callback, uint64_t *bt_prev);
|
||||
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa, unsigned int *wa_count, uint64_t *bt_next);
|
||||
bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa);
|
||||
#else
|
||||
#define ensure_dir_access(dir) ensure_file_access(dir)
|
||||
@@ -148,7 +147,7 @@ bool rawsend_preinit(bool bind_fix4, bool bind_fix6);
|
||||
bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len);
|
||||
bool rawsend_rp(const struct rawpacket *rp);
|
||||
// return trues if all packets were send successfully
|
||||
bool rawsend_queue(struct rawpacket_tailhead *q);
|
||||
bool rawsend_queue(struct rawpacket_queue *q);
|
||||
// cleans up socket autocreated by rawsend
|
||||
void rawsend_cleanup(void);
|
||||
bool rawsend_rep(int repeats, const struct sockaddr* dst, uint32_t fwmark, const char *ifout, const void *data, size_t len);
|
||||
|
||||
+124
-75
@@ -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;
|
||||
@@ -705,7 +734,7 @@ static bool check_pos_range(const t_ctrack_position *pos, const struct packet_ra
|
||||
}
|
||||
|
||||
|
||||
static bool replay_queue(struct rawpacket_tailhead *q);
|
||||
static bool replay_queue(struct rawpacket_queue *q);
|
||||
|
||||
static bool ipcache_put_hostname(const struct in_addr *a4, const struct in6_addr *a6, const char *hostname, bool hostname_is_ip)
|
||||
{
|
||||
@@ -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;
|
||||
|
||||
@@ -1212,8 +1254,7 @@ static bool play_prolog(
|
||||
return false;
|
||||
ps->bReverseFixed = ps->bReverse ^ params.server;
|
||||
setup_direction(dis, ps->bReverseFixed, &ps->src, &ps->dst, &ps->sdip4, &ps->sdip6, &ps->sdport);
|
||||
|
||||
ifname = ps->bReverse ? ifin : ifout;
|
||||
ifname = ps->bReverseFixed ? ifin : ifout;
|
||||
#ifdef HAS_FILTER_SSID
|
||||
ps->ssid = wlan_ssid_search_ifname(ifname);
|
||||
if (ps->ssid) DLOG("found ssid for %s : %s\n", ifname, ps->ssid);
|
||||
@@ -1224,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(¶ms.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(¶ms.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)
|
||||
@@ -1249,7 +1290,7 @@ static bool play_prolog(
|
||||
// in absence of conntrack guess direction by presence of interface names. won't work on BSD
|
||||
ps->bReverseFixed = ps->ctrack ? (ps->bReverse ^ params.server) : (ps->bReverse = ifin && *ifin && (!ifout || !*ifout));
|
||||
setup_direction(dis, ps->bReverseFixed, &ps->src, &ps->dst, &ps->sdip4, &ps->sdip6, &ps->sdport);
|
||||
ifname = ps->bReverse ? ifin : ifout;
|
||||
ifname = ps->bReverseFixed ? ifin : ifout;
|
||||
#ifdef HAS_FILTER_SSID
|
||||
ps->ssid = wlan_ssid_search_ifname(ifname);
|
||||
if (ps->ssid) DLOG("found ssid for %s : %s\n", ifname, ps->ssid);
|
||||
@@ -1272,7 +1313,7 @@ static bool play_prolog(
|
||||
DLOG_ERR("strdup(host): out of memory\n");
|
||||
}
|
||||
}
|
||||
ps->dp = dp_find(¶ms.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(¶ms.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;
|
||||
@@ -1360,7 +1401,7 @@ static bool dp_rediscovery(struct play_state *ps)
|
||||
ps->dp = dp_find(¶ms.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)
|
||||
{
|
||||
@@ -1438,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;
|
||||
@@ -1584,8 +1625,9 @@ static uint8_t dpi_desync_tcp_packet_play(
|
||||
}
|
||||
else
|
||||
{
|
||||
// likely exceeded packet limit or unlikely out of memory
|
||||
DLOG_ERR("rawpacket_queue failed !\n");
|
||||
goto rediscover;
|
||||
goto reasm_cancel_rediscover;
|
||||
}
|
||||
if (ReasmIsFull(&ps.ctrack->reasm_client))
|
||||
{
|
||||
@@ -1617,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
|
||||
@@ -1794,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;
|
||||
@@ -2056,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");
|
||||
@@ -2122,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");
|
||||
@@ -2195,7 +2244,7 @@ uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifin, const char *ifout,
|
||||
|
||||
|
||||
|
||||
static bool replay_queue(struct rawpacket_tailhead *q)
|
||||
static bool replay_queue(struct rawpacket_queue *q)
|
||||
{
|
||||
struct rawpacket *rp;
|
||||
size_t offset;
|
||||
|
||||
+4
-9
@@ -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
@@ -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
@@ -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(¶ms.hostlists))
|
||||
bool bChanged=false;
|
||||
if (!LoadHostLists(¶ms.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
@@ -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;
|
||||
|
||||
+161
-33
@@ -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)
|
||||
{
|
||||
@@ -2083,7 +2135,7 @@ bool lua_reconstruct_iphdr(lua_State *L, int idx, struct ip *ip, size_t *len)
|
||||
{
|
||||
if (lopt>40 || ((sizeof(struct ip) + ((lopt+3)&~3)) > *len)) goto err;
|
||||
memcpy(ip+1,p,lopt);
|
||||
memset(((uint8_t*)ip) + sizeof(struct ip) + lopt, 0, (4-lopt&3)&3);
|
||||
memset(((uint8_t*)ip) + sizeof(struct ip) + lopt, 0, (4-(lopt&3))&3);
|
||||
lopt = (lopt+3) & ~3;
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -3795,36 +3852,58 @@ static int luacall_timer_set(lua_State *L)
|
||||
LUA_STACK_GUARD_ENTER(L)
|
||||
|
||||
int argc=lua_gettop(L);
|
||||
|
||||
const char *name = luaL_checkstring(L,1);
|
||||
const char *func = luaL_checkstring(L,2);
|
||||
lua_Integer period = luaL_checkinteger(L,3);
|
||||
if (period<10) luaL_error(L,"invalid timer period. must be >=10 ms");
|
||||
|
||||
bool oneshot = argc>=4 && lua_toboolean(L,4);
|
||||
|
||||
lua_getglobal(L, func);
|
||||
bool is_f = lua_isfunction(L, -1);
|
||||
lua_pop(L, 1);
|
||||
if (!is_f) luaL_error(L, "timer function '%s' does not exist", func);
|
||||
const char *func;
|
||||
switch(lua_type(L,2))
|
||||
{
|
||||
case LUA_TSTRING:
|
||||
func = lua_tostring(L,2);
|
||||
lua_getglobal(L, func);
|
||||
if (!lua_isfunction(L, -1))
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
luaL_error(L, "timer function '%s' does not exist", func);
|
||||
}
|
||||
break;
|
||||
case LUA_TFUNCTION:
|
||||
lua_pushvalue(L, 2);
|
||||
break;
|
||||
default:
|
||||
luaL_error(L,"invalid timer function type");
|
||||
}
|
||||
|
||||
const char *action;
|
||||
timer_pool *timer = TimerPoolSearch(params.timers, name);
|
||||
if (timer)
|
||||
{
|
||||
TimerPoolDel(¶ms.timers, timer);
|
||||
params.timers_dirty = true;
|
||||
action = "replaced";
|
||||
}
|
||||
else
|
||||
action = "created";
|
||||
|
||||
timer = TimerPoolAdd(¶ms.timers, name, func, period, oneshot);
|
||||
if (!timer) luaL_error(L,"could not create timer");
|
||||
timer = TimerPoolAdd(¶ms.timers, name, period, oneshot);
|
||||
if (!timer)
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
luaL_error(L,"could not create timer");
|
||||
}
|
||||
params.timers_dirty = true;
|
||||
timer->func_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
if (argc>=5)
|
||||
{
|
||||
lua_pushvalue(L, 5);
|
||||
timer->lua_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
DLOG("timer: '%s' %s. function '%s' period %llu oneshot %u\n", timer->str, action, timer->func, timer->period, timer->oneshot);
|
||||
DLOG("timer: '%s' %s. period %llu oneshot %u\n", timer->str, action, timer->period, timer->oneshot);
|
||||
|
||||
LUA_STACK_GUARD_RETURN(L,0)
|
||||
}
|
||||
@@ -3841,12 +3920,59 @@ static int luacall_timer_del(lua_State *L)
|
||||
{
|
||||
DLOG("timer: '%s' deleted\n", timer->str);
|
||||
TimerPoolDel(¶ms.timers, timer);
|
||||
params.timers_dirty = true;
|
||||
}
|
||||
else
|
||||
DLOG("timer: '%s' not found\n", timer->str);
|
||||
DLOG("timer: '%s' not found\n", name);
|
||||
lua_pushboolean(L, !!timer);
|
||||
LUA_STACK_GUARD_RETURN(L,1)
|
||||
}
|
||||
static void lua_push_timer_info(lua_State *L, const timer_pool *timer)
|
||||
{
|
||||
lua_newtable(L);
|
||||
if (timer->str) lua_pushf_str(L, "name", timer->str);
|
||||
if (timer->func_ref!=LUA_NOREF)
|
||||
{
|
||||
lua_pushliteral(L, "func");
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, timer->func_ref);
|
||||
lua_rawset(L,-3);
|
||||
}
|
||||
lua_pushf_lint(L, "period", timer->period);
|
||||
lua_pushf_bool(L, "oneshot", timer->oneshot);
|
||||
lua_pushf_lint(L, "fires", timer->fires);
|
||||
}
|
||||
static int luacall_timer_info(lua_State *L)
|
||||
{
|
||||
// timer_info(name)
|
||||
lua_check_argc(L,"timer_info",1);
|
||||
|
||||
LUA_STACK_GUARD_ENTER(L)
|
||||
|
||||
const char *name = luaL_checkstring(L,1);
|
||||
const timer_pool *timer = TimerPoolSearch(params.timers, name);
|
||||
if (timer)
|
||||
lua_push_timer_info(L, timer);
|
||||
else
|
||||
lua_pushnil(L);
|
||||
|
||||
LUA_STACK_GUARD_RETURN(L,1)
|
||||
}
|
||||
static int luacall_timer_enum(lua_State *L)
|
||||
{
|
||||
// timer_enum()
|
||||
lua_check_argc(L,"timer_enum",0);
|
||||
|
||||
LUA_STACK_GUARD_ENTER(L)
|
||||
|
||||
lua_Integer n=1;
|
||||
timer_pool *timer, *tmp, *p;
|
||||
|
||||
lua_newtable(L);
|
||||
HASH_ITER(hh, params.timers, timer, tmp) lua_pushi_str(L, n++, timer->str);
|
||||
|
||||
LUA_STACK_GUARD_RETURN(L,1)
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
@@ -4505,7 +4631,9 @@ static void lua_init_functions(void)
|
||||
|
||||
// timers
|
||||
{"timer_set",luacall_timer_set},
|
||||
{"timer_del",luacall_timer_del}
|
||||
{"timer_del",luacall_timer_del},
|
||||
{"timer_info",luacall_timer_info},
|
||||
{"timer_enum",luacall_timer_enum}
|
||||
};
|
||||
for(int i=0;i<(sizeof(lfunc)/sizeof(*lfunc));i++)
|
||||
lua_register(params.L,lfunc[i].name,lfunc[i].f);
|
||||
@@ -4567,12 +4695,12 @@ void lua_dlog_error(void)
|
||||
}
|
||||
|
||||
|
||||
static time_t gc_time=0;
|
||||
static uint64_t gc_time=0;
|
||||
void lua_do_gc(void)
|
||||
{
|
||||
if (params.lua_gc)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
uint64_t now = boottime_ms();
|
||||
if ((now - gc_time) >= params.lua_gc)
|
||||
{
|
||||
int kb1 = lua_gc(params.L, LUA_GCCOUNT, 0);
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
+150
-98
@@ -242,20 +242,29 @@ static int write_pidfile(FILE **Fpid)
|
||||
|
||||
void NoInterceptLoop(void)
|
||||
{
|
||||
useconds_t usec = params.timer_res * 1000;
|
||||
uint64_t bt, bt_next, bt_delta;
|
||||
struct timespec ts;
|
||||
|
||||
if (params.timers)
|
||||
{
|
||||
DLOG("processing timers\n");
|
||||
|
||||
bt_next = TimerPoolNext(params.timers, ¶ms.timers_dirty);
|
||||
while(params.timers)
|
||||
{
|
||||
if (bQuit) goto quit;
|
||||
usleep(usec);
|
||||
if (bQuit) goto quit;
|
||||
bt = boottime_ms();
|
||||
if (bt_next>bt)
|
||||
{
|
||||
bt_delta = bt_next - bt;
|
||||
ts.tv_sec = (time_t)(bt_delta/1000U);
|
||||
ts.tv_nsec = bt_delta%1000U*1000000U;
|
||||
nanosleep(&ts,NULL);
|
||||
if (bQuit) goto quit;
|
||||
}
|
||||
ReloadCheck();
|
||||
lua_do_gc();
|
||||
TimerPoolRun(¶ms.timers, 0);
|
||||
bt_next = TimerPoolRun(¶ms.timers, ¶ms.timers_dirty, 0);
|
||||
}
|
||||
}
|
||||
return;
|
||||
@@ -442,8 +451,8 @@ static int nfq_main(void)
|
||||
uint8_t *buf=NULL, *mod=NULL;
|
||||
struct nfq_cb_data cbdata = { .sock = -1, .mod = NULL };
|
||||
fd_set fdset;
|
||||
struct timeval tv = {.tv_sec = params.timer_res/1000, .tv_usec = params.timer_res%1000*1000};
|
||||
unsigned int bt,bt_prev,dbt;
|
||||
struct timeval tv;
|
||||
uint64_t bt,dbt,bt_next;
|
||||
|
||||
if (*params.pidfile && !(Fpid = fopen(params.pidfile, "w")))
|
||||
{
|
||||
@@ -519,21 +528,36 @@ static int nfq_main(void)
|
||||
notify_ready();
|
||||
|
||||
fd = nfq_fd(h);
|
||||
bt_prev=0;
|
||||
do
|
||||
bt_next = bt = 0;
|
||||
for(;;)
|
||||
{
|
||||
if (bQuit) goto quit;
|
||||
for(;;)
|
||||
{
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(fd, &fdset);
|
||||
res = select(fd+1, &fdset, NULL, NULL, &tv);
|
||||
if (bQuit) goto quit;
|
||||
if (res == -1)
|
||||
if (params.timers)
|
||||
{
|
||||
if (errno == EINTR) continue;
|
||||
DLOG_PERROR("select");
|
||||
goto err;
|
||||
if (!bt_next) bt_next = TimerPoolNext(params.timers, ¶ms.timers_dirty);
|
||||
bt = boottime_ms();
|
||||
dbt = bt_next>bt ? bt_next-bt : 0;
|
||||
tv.tv_sec = (time_t)(dbt/1000);
|
||||
tv.tv_usec = (suseconds_t)(dbt%1000*1000);
|
||||
|
||||
FD_ZERO(&fdset);
|
||||
FD_SET(fd, &fdset);
|
||||
res = select(fd+1, &fdset, NULL, NULL, &tv);
|
||||
if (bQuit) goto quit;
|
||||
if (res == -1)
|
||||
{
|
||||
if (errno == EINTR) continue;
|
||||
DLOG_PERROR("select");
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bt_next = bt = 0;
|
||||
if (bQuit) goto quit;
|
||||
res = 1;
|
||||
}
|
||||
lua_do_gc();
|
||||
ReloadCheck();
|
||||
@@ -555,21 +579,13 @@ static int nfq_main(void)
|
||||
if (res<0) DLOG_ERR("nfq_handle_packet result %d, errno %d : %s\n", res, errno, strerror(errno));
|
||||
}
|
||||
|
||||
bt = boottime_ms();
|
||||
dbt = bt-bt_prev;
|
||||
if (dbt>=params.timer_res)
|
||||
if (params.timers)
|
||||
{
|
||||
TimerPoolRun(¶ms.timers, bt);
|
||||
|
||||
bt_prev = bt;
|
||||
tv.tv_sec = params.timer_res/1000;
|
||||
tv.tv_usec = params.timer_res%1000*1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbt = params.timer_res-dbt;
|
||||
tv.tv_sec = (time_t)(dbt/1000);
|
||||
tv.tv_usec = (suseconds_t)(dbt%1000*1000);
|
||||
bt = boottime_ms();
|
||||
if (bt>=bt_next)
|
||||
bt_next = TimerPoolRun(¶ms.timers, ¶ms.timers_dirty, bt);
|
||||
else if (params.timers_dirty)
|
||||
bt_next = 0;
|
||||
}
|
||||
}
|
||||
if (errno==EINTR)
|
||||
@@ -578,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;
|
||||
@@ -591,10 +606,10 @@ exok:
|
||||
res=0;
|
||||
ex:
|
||||
if (Fpid) fclose(Fpid);
|
||||
if (cbdata.sock>=0) close(cbdata.sock);
|
||||
free(cbdata.mod);
|
||||
free(buf);
|
||||
nfq_deinit(&h, &qh);
|
||||
if (cbdata.sock>=0) close(cbdata.sock);
|
||||
lua_shutdown();
|
||||
#ifdef HAS_FILTER_SSID
|
||||
wlan_info_deinit();
|
||||
@@ -618,8 +633,8 @@ static int dvt_main(void)
|
||||
struct sockaddr_in6 bp6;
|
||||
uint8_t buf[RECONSTRUCT_MAX_SIZE] __attribute__((aligned));
|
||||
fd_set fdset;
|
||||
struct timeval tv = {.tv_sec = params.timer_res/1000, .tv_usec = params.timer_res%1000*1000};
|
||||
unsigned int bt,bt_prev,dbt;
|
||||
struct timeval tv;
|
||||
uint64_t bt,bt_next,dbt;
|
||||
|
||||
if (*params.pidfile && !(Fpid = fopen(params.pidfile, "w")))
|
||||
{
|
||||
@@ -703,16 +718,29 @@ static int dvt_main(void)
|
||||
if (params.daemon) daemonize();
|
||||
if (!write_pidfile(&Fpid)) goto exiterr;
|
||||
|
||||
for (bt_prev=0;;)
|
||||
for (bt=bt_next=0;;)
|
||||
{
|
||||
if (bQuit)
|
||||
{
|
||||
DLOG_CONDUP("quit requested\n");
|
||||
goto exitok;
|
||||
}
|
||||
|
||||
FD_ZERO(&fdset);
|
||||
for (i = 0; i < fdct; i++) FD_SET(fd[i], &fdset);
|
||||
r = select(fdmax, &fdset, NULL, NULL, &tv);
|
||||
|
||||
if (params.timers)
|
||||
{
|
||||
if (!bt_next) bt_next = TimerPoolNext(params.timers, ¶ms.timers_dirty);
|
||||
bt = boottime_ms();
|
||||
dbt = bt_next>bt ? bt_next-bt : 0;
|
||||
tv.tv_sec = (time_t)(dbt/1000);
|
||||
tv.tv_usec = (suseconds_t)(dbt%1000*1000);
|
||||
r = select(fdmax, &fdset, NULL, NULL, &tv);
|
||||
}
|
||||
else
|
||||
r = select(fdmax, &fdset, NULL, NULL, NULL);
|
||||
|
||||
if (bQuit)
|
||||
{
|
||||
DLOG_CONDUP("quit requested\n");
|
||||
@@ -796,21 +824,13 @@ static int dvt_main(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
bt = boottime_ms();
|
||||
dbt = bt-bt_prev;
|
||||
if (dbt>=params.timer_res)
|
||||
if (params.timers)
|
||||
{
|
||||
TimerPoolRun(¶ms.timers, bt);
|
||||
|
||||
bt_prev = bt;
|
||||
tv.tv_sec = params.timer_res/1000;
|
||||
tv.tv_usec = params.timer_res%1000*1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
dbt = params.timer_res-dbt;
|
||||
tv.tv_sec = (time_t)(dbt/1000);
|
||||
tv.tv_usec = (suseconds_t)(dbt%1000*1000);
|
||||
bt = boottime_ms();
|
||||
if (bt>=bt_next)
|
||||
bt_next = TimerPoolRun(¶ms.timers, ¶ms.timers_dirty, bt);
|
||||
else if (params.timers_dirty)
|
||||
bt_next = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,11 +852,6 @@ exiterr:
|
||||
// do not make it less than 65536 - loopback packets can be up to 64K
|
||||
#define WINDIVERT_PACKET_BUF_SIZE 196608 // 3*64K, 128*1500=192000
|
||||
|
||||
static void win_timer_callback(uint64_t bt)
|
||||
{
|
||||
TimerPoolRun(¶ms.timers, bt);
|
||||
}
|
||||
|
||||
static int win_main(void)
|
||||
{
|
||||
size_t len, packet_len, left, modlen;
|
||||
@@ -849,7 +864,7 @@ static int win_main(void)
|
||||
WINDIVERT_ADDRESS wa[WINDIVERT_BULK_MAX];
|
||||
uint8_t *packets = NULL, *packet, *mod=NULL;
|
||||
unsigned int n,wa_count;
|
||||
uint64_t bt_prev = 0;
|
||||
uint64_t bt_next;
|
||||
|
||||
// windows emulated fork logic does not cover objects outside of cygwin world. have to daemonize before inits
|
||||
if (params.daemon) daemonize();
|
||||
@@ -860,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(¶ms.ssid_filter, ¶ms.nlm_filter))
|
||||
if (!win_dark_init(¶ms.ssid_filter, params.ssid_filter_neg, ¶ms.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;
|
||||
@@ -919,11 +934,12 @@ static int win_main(void)
|
||||
goto ex;
|
||||
}
|
||||
|
||||
bt_next = 0;
|
||||
for (id = 0;;)
|
||||
{
|
||||
len = WINDIVERT_PACKET_BUF_SIZE;
|
||||
wa_count = WINDIVERT_BULK_MAX;
|
||||
if (!windivert_recv(packets, &len, wa, &wa_count, win_timer_callback, &bt_prev))
|
||||
if (!windivert_recv(packets, &len, wa, &wa_count, &bt_next))
|
||||
{
|
||||
if (errno == ENOBUFS)
|
||||
{
|
||||
@@ -1100,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;
|
||||
|
||||
@@ -1142,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);
|
||||
@@ -1201,12 +1218,9 @@ static bool parse_uid(char *opt, uid_t *uid, gid_t *gid, int *gid_count, int max
|
||||
c = *e;
|
||||
*e = 0;
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
if (sscanf(p, "%u", &u) != 1) return false;
|
||||
if (*gid_count >= max_gids) return false;
|
||||
gid[(*gid_count)++] = (gid_t)u;
|
||||
}
|
||||
if (sscanf(p, "%u", &u) != 1) return false;
|
||||
if (*gid_count >= max_gids) return false;
|
||||
gid[(*gid_count)++] = (gid_t)u;
|
||||
if (e) *e++ = c;
|
||||
p = e;
|
||||
}
|
||||
@@ -1828,7 +1842,6 @@ static void exithelp(void)
|
||||
" --ipcache-lifetime=<int>\t\t\t\t; time in seconds to keep cached hop count and domain name (default %u). 0 = no expiration\n"
|
||||
" --ipcache-hostname=[0|1]\t\t\t\t; 1 or no argument enables ip->hostname caching\n"
|
||||
" --reasm-disable=[type[,type]]\t\t\t\t; disable reasm for these L7 payloads : tls_client_hello quic_initial . if no argument - disable all reasm.\n"
|
||||
" --timer-res=msec\t\t\t\t\t; Lua timer resolution. default %d ms\n"
|
||||
#ifdef __CYGWIN__
|
||||
"\nWINDIVERT FILTER:\n"
|
||||
" --wf-iface=<int>[.<int>]\t\t\t\t; numeric network interface and subinterface indexes\n"
|
||||
@@ -1851,11 +1864,13 @@ 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"
|
||||
" --writeable[=<dir_name>]\t\t\t\t; create writeable dir for LUA scripts and pass it in WRITEABLE env variable (only one dir possible)\n"
|
||||
" --writable[=<dir_name>]\t\t\t\t; create writable dir for LUA scripts and pass it in WRITABLE env variable (only one dir possible)\n"
|
||||
" --blob=<item_name>:[+ofs]@<filename>|0xHEX\t\t; load blob to LUA var <item_name>\n"
|
||||
" --lua-init=@<filename>|<lua_text>\t\t\t; load LUA program from a file or string. if multiple parameters present order of execution is preserved. gzipped files are supported.\n"
|
||||
" --lua-gc=<int>\t\t\t\t\t\t; forced garbage collection every N sec. default %u sec. triggers only when a packet arrives. 0 = disable.\n"
|
||||
@@ -1874,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"
|
||||
@@ -1895,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)
|
||||
@@ -1904,7 +1923,6 @@ static void exithelp(void)
|
||||
#endif
|
||||
CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN, CTRACK_T_UDP,
|
||||
IPCACHE_LIFETIME,
|
||||
TIMER_RES_DEFAULT,
|
||||
LUA_GC_INTERVAL,
|
||||
all_protos,
|
||||
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT,
|
||||
@@ -1987,14 +2005,13 @@ enum opt_indices {
|
||||
IDX_IPCACHE_LIFETIME,
|
||||
IDX_IPCACHE_HOSTNAME,
|
||||
IDX_REASM_DISABLE,
|
||||
IDX_TIMER_RES,
|
||||
#ifdef __linux__
|
||||
IDX_FWMARK,
|
||||
#elif defined(SO_USER_COOKIE)
|
||||
IDX_SOCKARG,
|
||||
#endif
|
||||
|
||||
IDX_WRITEABLE,
|
||||
IDX_WRITABLE,
|
||||
|
||||
IDX_BLOB,
|
||||
IDX_LUA_INIT,
|
||||
@@ -2028,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,
|
||||
@@ -2059,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
|
||||
@@ -2093,13 +2116,12 @@ static const struct option long_options[] = {
|
||||
[IDX_IPCACHE_LIFETIME] = {"ipcache-lifetime", required_argument, 0, 0},
|
||||
[IDX_IPCACHE_HOSTNAME] = {"ipcache-hostname", optional_argument, 0, 0},
|
||||
[IDX_REASM_DISABLE] = {"reasm-disable", optional_argument, 0, 0},
|
||||
[IDX_TIMER_RES] = {"timer-res", required_argument, 0, 0},
|
||||
#ifdef __linux__
|
||||
[IDX_FWMARK] = {"fwmark", required_argument, 0, 0},
|
||||
#elif defined(SO_USER_COOKIE)
|
||||
[IDX_SOCKARG] = {"sockarg", required_argument, 0, 0},
|
||||
#endif
|
||||
[IDX_WRITEABLE] = {"writeable", optional_argument, 0, 0},
|
||||
[IDX_WRITABLE] = {"writable", optional_argument, 0, 0},
|
||||
[IDX_BLOB] = {"blob", required_argument, 0, 0},
|
||||
[IDX_LUA_INIT] = {"lua-init", required_argument, 0, 0},
|
||||
[IDX_LUA_GC] = {"lua-gc", required_argument, 0, 0},
|
||||
@@ -2131,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},
|
||||
@@ -2162,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},
|
||||
@@ -2208,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__
|
||||
@@ -2496,14 +2524,6 @@ int main(int argc, char **argv)
|
||||
else
|
||||
params.reasm_payload_disable = L7P_ALL;
|
||||
break;
|
||||
case IDX_TIMER_RES:
|
||||
params.timer_res = atoi(optarg);
|
||||
if (params.timer_res<10)
|
||||
{
|
||||
DLOG_ERR("Invalid timer resolution. must be >=10 ms\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
break;
|
||||
#if defined(__linux__)
|
||||
case IDX_FWMARK:
|
||||
#elif defined(SO_USER_COOKIE)
|
||||
@@ -2511,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", ¶ms.desync_fwmark) <= 0) sscanf(optarg, "%u", ¶ms.desync_fwmark);
|
||||
if (sscanf(optarg, "0x%X", ¶ms.desync_fwmark)!=1) sscanf(optarg, "%u", ¶ms.desync_fwmark);
|
||||
if (!params.desync_fwmark)
|
||||
{
|
||||
DLOG_ERR("fwmark/sockarg should be decimal or 0xHEX and should not be zero\n");
|
||||
@@ -2519,18 +2539,18 @@ int main(int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case IDX_WRITEABLE:
|
||||
params.writeable_dir_enable = true;
|
||||
case IDX_WRITABLE:
|
||||
params.writable_dir_enable = true;
|
||||
if (optarg)
|
||||
{
|
||||
if (!realpath_any(optarg, params.writeable_dir))
|
||||
if (!realpath_any(optarg, params.writable_dir))
|
||||
{
|
||||
DLOG_ERR("bad file '%s'\n",optarg);
|
||||
exit_clean(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
*params.writeable_dir = 0;
|
||||
*params.writable_dir = 0;
|
||||
break;
|
||||
|
||||
case IDX_BLOB:
|
||||
@@ -2557,11 +2577,14 @@ int main(int argc, char **argv)
|
||||
}
|
||||
break;
|
||||
case IDX_LUA_GC:
|
||||
params.lua_gc = atoi(optarg);
|
||||
if (params.lua_gc<0)
|
||||
{
|
||||
DLOG_ERR("lua-gc must be >=0\n");
|
||||
exit_clean(1);
|
||||
int i = atoi(optarg);
|
||||
if (i<0)
|
||||
{
|
||||
DLOG_ERR("lua-gc must be >=0\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
params.lua_gc = i*1000; // in msec
|
||||
}
|
||||
break;
|
||||
case IDX_HOSTLIST:
|
||||
@@ -2837,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))
|
||||
@@ -3074,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, ¶ms.nlm_filter))
|
||||
@@ -3082,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")))
|
||||
{
|
||||
@@ -3148,14 +3200,14 @@ int main(int argc, char **argv)
|
||||
DLOG_CONDUP("we have %u user defined desync profile(s) and default low priority profile 0\n", desync_profile_count);
|
||||
DLOG_CONDUP("we have %u user defined desync template(s)\n", desync_template_count);
|
||||
|
||||
if (params.writeable_dir_enable)
|
||||
if (params.writable_dir_enable)
|
||||
{
|
||||
if (!make_writeable_dir())
|
||||
if (!make_writable_dir())
|
||||
{
|
||||
DLOG_ERR("could not make writeable dir for LUA\n");
|
||||
DLOG_ERR("could not make writable dir for LUA\n");
|
||||
exit_clean(1);
|
||||
}
|
||||
DLOG("LUA writeable dir : %s\n", getenv("WRITEABLE"));
|
||||
DLOG("LUA writable dir : %s\n", getenv("WRITABLE"));
|
||||
}
|
||||
#ifndef __CYGWIN__
|
||||
if (params.droproot)
|
||||
|
||||
+1
-1
@@ -10,4 +10,4 @@ extern volatile sig_atomic_t bQuit;
|
||||
int main(int argc, char *argv[]);
|
||||
|
||||
// when something changes that can break LUA compatibility this version should be increased
|
||||
#define LUA_COMPAT_VER 5
|
||||
#define LUA_COMPAT_VER 6
|
||||
+19
-16
@@ -4,37 +4,40 @@
|
||||
|
||||
#include "packet_queue.h"
|
||||
|
||||
void rawpacket_queue_init(struct rawpacket_tailhead *q)
|
||||
void rawpacket_queue_init(struct rawpacket_queue *q, unsigned int max_packets)
|
||||
{
|
||||
TAILQ_INIT(q);
|
||||
TAILQ_INIT(&q->q);
|
||||
q->max_packets = max_packets;
|
||||
}
|
||||
void rawpacket_free(struct rawpacket *rp)
|
||||
{
|
||||
if (rp) free(rp->packet);
|
||||
free(rp);
|
||||
}
|
||||
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q)
|
||||
struct rawpacket *rawpacket_dequeue(struct rawpacket_queue *q)
|
||||
{
|
||||
struct rawpacket *rp;
|
||||
rp = TAILQ_FIRST(q);
|
||||
if (rp) TAILQ_REMOVE(q, rp, next);
|
||||
rp = TAILQ_FIRST(&q->q);
|
||||
if (rp) TAILQ_REMOVE(&q->q, rp, next);
|
||||
return rp;
|
||||
}
|
||||
void rawpacket_queue_destroy(struct rawpacket_tailhead *q)
|
||||
void rawpacket_queue_destroy(struct rawpacket_queue *q)
|
||||
{
|
||||
struct rawpacket *rp;
|
||||
while((rp = rawpacket_dequeue(q))) rawpacket_free(rp);
|
||||
}
|
||||
|
||||
struct rawpacket *rawpacket_queue(
|
||||
struct rawpacket_tailhead *q,
|
||||
struct rawpacket_queue *q,
|
||||
const struct sockaddr_storage* dst,
|
||||
uint32_t fwmark_orig,uint32_t fwmark,
|
||||
const char *ifin,const char *ifout,
|
||||
const void *data,size_t len,size_t len_payload,
|
||||
uint32_t fwmark_orig, uint32_t fwmark,
|
||||
const char *ifin, const char *ifout,
|
||||
const void *data, size_t len, size_t len_payload,
|
||||
const t_ctrack_positions *tpos,
|
||||
bool server_side)
|
||||
{
|
||||
if (q->max_packets && rawpacket_queue_count(q)>=q->max_packets) return NULL;
|
||||
|
||||
struct rawpacket *rp = malloc(sizeof(struct rawpacket));
|
||||
if (!rp) return NULL;
|
||||
|
||||
@@ -44,7 +47,7 @@ struct rawpacket *rawpacket_queue(
|
||||
free(rp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
rp->dst = *dst;
|
||||
rp->fwmark_orig = fwmark_orig;
|
||||
rp->fwmark = fwmark;
|
||||
@@ -70,19 +73,19 @@ struct rawpacket *rawpacket_queue(
|
||||
rp->tpos_present = false;
|
||||
rp->server_side = server_side;
|
||||
|
||||
TAILQ_INSERT_TAIL(q, rp, next);
|
||||
TAILQ_INSERT_TAIL(&q->q, rp, next);
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q)
|
||||
unsigned int rawpacket_queue_count(const struct rawpacket_queue *q)
|
||||
{
|
||||
const struct rawpacket *rp;
|
||||
unsigned int ct=0;
|
||||
TAILQ_FOREACH(rp, q, next) ct++;
|
||||
TAILQ_FOREACH(rp, &q->q, next) ct++;
|
||||
return ct;
|
||||
}
|
||||
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q)
|
||||
bool rawpacket_queue_empty(const struct rawpacket_queue *q)
|
||||
{
|
||||
return !TAILQ_FIRST(q);
|
||||
return !TAILQ_FIRST(&q->q);
|
||||
}
|
||||
+12
-6
@@ -23,10 +23,16 @@ struct rawpacket
|
||||
};
|
||||
TAILQ_HEAD(rawpacket_tailhead, rawpacket);
|
||||
|
||||
void rawpacket_queue_init(struct rawpacket_tailhead *q);
|
||||
void rawpacket_queue_destroy(struct rawpacket_tailhead *q);
|
||||
bool rawpacket_queue_empty(const struct rawpacket_tailhead *q);
|
||||
unsigned int rawpacket_queue_count(const struct rawpacket_tailhead *q);
|
||||
struct rawpacket *rawpacket_queue(struct rawpacket_tailhead *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_positions *tpos,bool server_side);
|
||||
struct rawpacket *rawpacket_dequeue(struct rawpacket_tailhead *q);
|
||||
struct rawpacket_queue
|
||||
{
|
||||
struct rawpacket_tailhead q;
|
||||
unsigned int max_packets;
|
||||
};
|
||||
|
||||
void rawpacket_queue_init(struct rawpacket_queue *q, unsigned int max_packets);
|
||||
void rawpacket_queue_destroy(struct rawpacket_queue *q);
|
||||
bool rawpacket_queue_empty(const struct rawpacket_queue *q);
|
||||
unsigned int rawpacket_queue_count(const struct rawpacket_queue *q);
|
||||
struct rawpacket *rawpacket_queue(struct rawpacket_queue *q,const struct sockaddr_storage* dst,uint32_t fwmark_orig,uint32_t fwmark,const char *ifin,const char *ifout,const void *data,size_t len,size_t len_payload,const t_ctrack_positions *tpos,bool server_side);
|
||||
struct rawpacket *rawpacket_dequeue(struct rawpacket_queue *q);
|
||||
void rawpacket_free(struct rawpacket *rp);
|
||||
+1
-2
@@ -588,8 +588,7 @@ void init_params(struct params_s *params)
|
||||
params->ctrack_t_fin = CTRACK_T_FIN;
|
||||
params->ctrack_t_udp = CTRACK_T_UDP;
|
||||
params->ipcache_lifetime = IPCACHE_LIFETIME;
|
||||
params->timer_res = TIMER_RES_DEFAULT;
|
||||
params->lua_gc = LUA_GC_INTERVAL;
|
||||
params->lua_gc = LUA_GC_INTERVAL*1000;
|
||||
|
||||
LIST_INIT(¶ms->hostlists);
|
||||
LIST_INIT(¶ms->ipsets);
|
||||
|
||||
+9
-7
@@ -37,8 +37,6 @@
|
||||
|
||||
#define IPCACHE_LIFETIME 7200
|
||||
|
||||
#define TIMER_RES_DEFAULT 50
|
||||
|
||||
#define MAX_GIDS 64
|
||||
|
||||
#define MAX_BLOB_SIZE (16*1024)
|
||||
@@ -74,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
|
||||
@@ -98,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")
|
||||
|
||||
@@ -151,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;
|
||||
@@ -192,13 +194,13 @@ struct params_s
|
||||
uint64_t payload_disable;
|
||||
|
||||
struct str_list_head lua_init_scripts;
|
||||
bool writeable_dir_enable;
|
||||
char writeable_dir[PATH_MAX];
|
||||
bool writable_dir_enable;
|
||||
char writable_dir[PATH_MAX];
|
||||
|
||||
int timer_res; // timer resolution in msec
|
||||
timer_pool *timers;
|
||||
bool timers_dirty; // changed something in timers
|
||||
|
||||
int lua_gc;
|
||||
uint64_t lua_gc;
|
||||
int ref_desync_ctx; // desync ctx userdata registry ref
|
||||
lua_State *L;
|
||||
};
|
||||
|
||||
+4
-4
@@ -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;
|
||||
|
||||
@@ -651,7 +651,7 @@ bool TLSAdvanceToHostInSNI(const uint8_t **ext, size_t *elen, size_t *slen)
|
||||
if (*elen < 5 || (*ext)[2] != 0) return false;
|
||||
uint16_t nll = pntoh16(*ext);
|
||||
*slen = pntoh16(*ext + 3);
|
||||
if (nll<(*slen+3) || *slen > *elen-5) return false;
|
||||
if (nll<(*slen+3) || nll>(*elen-2)) return false;
|
||||
*ext += 5; *elen -= 5;
|
||||
return true;
|
||||
}
|
||||
|
||||
+10
-2
@@ -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)
|
||||
|
||||
+85
-40
@@ -19,8 +19,8 @@ static unsigned int timer_n=0;
|
||||
static void TimerPoolDestroyItem(timer_pool *elem)
|
||||
{
|
||||
free(elem->str);
|
||||
free(elem->func);
|
||||
luaL_unref(params.L, LUA_REGISTRYINDEX, elem->lua_ref);
|
||||
luaL_unref(params.L, LUA_REGISTRYINDEX, elem->func_ref);
|
||||
}
|
||||
void TimerPoolDel(timer_pool **pp, timer_pool *p)
|
||||
{
|
||||
@@ -39,34 +39,26 @@ struct timer_pool *TimerPoolSearch(timer_pool *p, const char *str)
|
||||
HASH_FIND_STR(p, str, elem_find);
|
||||
return elem_find;
|
||||
}
|
||||
struct timer_pool *TimerPoolAdd(timer_pool **pp, const char *str, const char *func, uint64_t period, bool oneshot)
|
||||
struct timer_pool *TimerPoolAdd(timer_pool **pp, const char *str, uint64_t period, bool oneshot)
|
||||
{
|
||||
ADD_STR_POOL(timer_pool, pp, str, strlen(str))
|
||||
if (!(elem->func = strdup(func)))
|
||||
{
|
||||
TimerPoolDel(pp,elem);
|
||||
return NULL;
|
||||
}
|
||||
elem->lua_ref = LUA_NOREF;
|
||||
elem->func_ref = LUA_NOREF;
|
||||
elem->period = period;
|
||||
elem->oneshot = oneshot;
|
||||
elem->lua_ref = LUA_NOREF;
|
||||
elem->bt_prev = boottime_ms();
|
||||
elem->bt_next = boottime_ms() + elem->period;
|
||||
elem->n = ++timer_n;
|
||||
elem->fires = 0;
|
||||
return elem;
|
||||
}
|
||||
|
||||
static bool TimerPoolRunTimer(timer_pool *p)
|
||||
{
|
||||
lua_getglobal(params.L, p->func);
|
||||
if (!lua_isfunction(params.L, -1))
|
||||
{
|
||||
lua_pop(params.L, 1);
|
||||
DLOG_ERR("timer: '%s' function '%s' does not exist\n",p->str,p->func);
|
||||
return false;
|
||||
}
|
||||
lua_rawgeti(params.L, LUA_REGISTRYINDEX, p->func_ref);
|
||||
lua_pushstring(params.L, p->str);
|
||||
lua_rawgeti(params.L, LUA_REGISTRYINDEX, p->lua_ref);
|
||||
DLOG("\ntimer: '%s' function '%s' period %llu oneshot %u\n",p->str,p->func,p->period,p->oneshot);
|
||||
p->fires++;
|
||||
DLOG("\ntimer: '%s' period %llu oneshot %u fires=%u\n",p->str,p->period,p->oneshot,p->fires);
|
||||
int status = lua_pcall(params.L, 2, 0, 0);
|
||||
if (status)
|
||||
{
|
||||
@@ -75,45 +67,98 @@ static bool TimerPoolRunTimer(timer_pool *p)
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void TimerPoolRun(timer_pool **pp, uint64_t bt)
|
||||
uint64_t TimerPoolNext(const timer_pool *p, bool *dirty)
|
||||
{
|
||||
if (!pp) return; // no timers
|
||||
*dirty = false;
|
||||
if (!p) return 0;
|
||||
|
||||
if (!bt) bt = boottime_ms();
|
||||
const timer_pool *elem, *tmp;
|
||||
uint64_t mintime=0x7FFFFFFFFFFFFFFF;
|
||||
|
||||
HASH_ITER(hh, p, elem, tmp)
|
||||
{
|
||||
if (elem->bt_next < mintime) mintime = elem->bt_next;
|
||||
}
|
||||
return mintime;
|
||||
}
|
||||
uint64_t TimerPoolRun(timer_pool **pp, bool *dirty, uint64_t bt)
|
||||
{
|
||||
if (!*pp)
|
||||
{
|
||||
*dirty = false;
|
||||
return 0; // no timers
|
||||
}
|
||||
|
||||
timer_pool *elem, *tmp, *p;
|
||||
char *name;
|
||||
const char *del;
|
||||
unsigned int n;
|
||||
uint64_t mintime;
|
||||
|
||||
if (!bt) bt = boottime_ms();
|
||||
again:
|
||||
mintime=0x7FFFFFFFFFFFFFFF;
|
||||
*dirty = false;
|
||||
HASH_ITER(hh, *pp, elem, tmp)
|
||||
{
|
||||
if (bt >= (elem->bt_prev + elem->period))
|
||||
if (bt >= elem->bt_next)
|
||||
{
|
||||
if (!(name = strdup(elem->str)))
|
||||
return;
|
||||
n = elem->n;
|
||||
|
||||
del = NULL;
|
||||
if (!TimerPoolRunTimer(elem))
|
||||
del = "timer: '%s' deleted because of error\n";
|
||||
|
||||
// timer function could delete the timer or recreate with the same name
|
||||
p = TimerPoolSearch(*pp, name);
|
||||
if (p==elem && p->n==n)
|
||||
if ((name = strdup(elem->str)))
|
||||
{
|
||||
if (!del && elem->oneshot)
|
||||
del = "timer: '%s' deleted because of oneshot\n";
|
||||
// elem is valid, not deleted and not recreated
|
||||
if (del)
|
||||
n = elem->n;
|
||||
|
||||
del = NULL;
|
||||
if (!TimerPoolRunTimer(elem))
|
||||
del = "timer: '%s' deleted because of error\n";
|
||||
|
||||
if (*dirty)
|
||||
{
|
||||
DLOG(del,name);
|
||||
// timer function could delete the timer or recreate with the same name
|
||||
p = TimerPoolSearch(*pp, name);
|
||||
if (p!=elem || p->n!=n) elem = NULL; // timer deleted or recreated with the same name (but another 'n')
|
||||
}
|
||||
if (elem)
|
||||
{
|
||||
if (!del && elem->oneshot)
|
||||
del = "timer: '%s' deleted because of oneshot\n";
|
||||
// elem is valid, not deleted and not recreated
|
||||
if (del)
|
||||
{
|
||||
DLOG(del,name);
|
||||
TimerPoolDel(pp, elem);
|
||||
elem = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// in case process was freezed (--ssid-filter/nlm-filter stop in winws for example)
|
||||
// do not use previous activation time or timer can hit multiple times
|
||||
// use current time instead to prevent bulk activation
|
||||
elem->bt_next = bt + elem->period;
|
||||
}
|
||||
}
|
||||
|
||||
free(name);
|
||||
|
||||
if (*dirty)
|
||||
goto again; // they may have deleted or created any number of timers, HASH_ITER may fail or access invalid pointers - restart
|
||||
}
|
||||
else // unlikely
|
||||
{
|
||||
if (elem->oneshot)
|
||||
{
|
||||
DLOG_ERR("timer: '%s' deleted because of out of memory\n", elem->str);
|
||||
TimerPoolDel(pp, elem);
|
||||
elem = NULL;
|
||||
}
|
||||
else
|
||||
elem->bt_prev = bt;
|
||||
{
|
||||
DLOG_ERR("timer: '%s' skipped because of out of memory\n", elem->str);
|
||||
elem->bt_next = bt + elem->period;
|
||||
}
|
||||
}
|
||||
|
||||
free(name);
|
||||
}
|
||||
if (elem && (elem->bt_next < mintime)) mintime = elem->bt_next;
|
||||
}
|
||||
if (!*pp) return 0; // no timers
|
||||
return mintime;
|
||||
}
|
||||
+7
-5
@@ -7,17 +7,19 @@
|
||||
|
||||
typedef struct timer_pool {
|
||||
char *str; /* key */
|
||||
char *func;
|
||||
int func_ref;
|
||||
uint64_t period;
|
||||
bool oneshot;
|
||||
int lua_ref;
|
||||
uint64_t bt_prev;
|
||||
unsigned int n;
|
||||
uint64_t bt_next; /* next activation time */
|
||||
unsigned int n, fires;
|
||||
UT_hash_handle hh; /* makes this structure hashable */
|
||||
} timer_pool;
|
||||
|
||||
void TimerPoolDestroy(timer_pool **pp);
|
||||
struct timer_pool *TimerPoolSearch(timer_pool *p, const char *str);
|
||||
struct timer_pool *TimerPoolAdd(timer_pool **pp, const char *str, const char *func, uint64_t period, bool oneshot);
|
||||
struct timer_pool *TimerPoolAdd(timer_pool **pp, const char *str, uint64_t period, bool oneshot);
|
||||
void TimerPoolDel(timer_pool **pp, timer_pool *p);
|
||||
void TimerPoolRun(timer_pool **pp, uint64_t bt);
|
||||
// return next timer fire time
|
||||
uint64_t TimerPoolRun(timer_pool **pp, bool *dirty, uint64_t bt);
|
||||
uint64_t TimerPoolNext(const timer_pool *p, bool *dirty);
|
||||
Reference in new issue
Block a user