Compare commits

...
51 Commits
Author SHA1 Message Date
bol-van b78b52c4cd blockcheck2: use cygstart in windows to avoid fork problems (without cygwin rebase) 2026-07-20 20:42:29 +03:00
bol-van 1d85620abd nfqws2: DLOG_ERR in timer out of memory case 2026-07-20 20:18:21 +03:00
bol-van 4b9b938db2 update changes.txt 2026-07-20 19:55:07 +03:00
bol-van 43a18c7eb7 init.d: extend discord media ports to 19294-19344 2026-07-20 19:53:18 +03:00
bol-van 49c8dfedc9 init.d: extend discord media ports to 19294-19344 2026-07-20 19:51:18 +03:00
bol-van 8a0f53f3cf nfqws2: small AI fixes 2026-07-16 18:33:23 +03:00
bol-van 8d34a4b7a2 nfqws2: limit delayed packet queue to 64 packets (anti-ddos) 2026-07-04 12:37:12 +03:00
bol-van bc6b8c64a4 nfqws2: minor fix in TLSAdvanceToHostInSNI 2026-07-04 11:27:46 +03:00
bol-van c5325fc267 blockcheck fix 2026-06-25 07:01:41 +03:00
bol-van d1b80a4254 builder-linux: curl download retries and timeouts 2026-06-24 14:29:18 +03:00
bol-van 4759d64c27 update docs 2026-06-23 20:18:56 +03:00
bol-van d9acc873fa Merge pull request #195 from goodrussian666/master
cppcheck cleanup
2026-06-23 16:40:01 +00:00
bol-van 4e5efbf2b2 update docs 2026-06-23 19:35:16 +03:00
bol-van b35db6aac5 Merge pull request #249 from loop-uh/master
орфография + фикс ошибок и ссылок
2026-06-23 16:33:22 +00:00
bol-van c54533bbb2 nfqws2: accept string or function in timer_set() 2026-06-22 19:45:43 +03:00
bol-van 8afe88dea7 update docs 2026-06-21 19:08:36 +03:00
bol-van a599b4016b update docs 2026-06-21 19:06:44 +03:00
loop-uh d2c85ec8b7 орфография + фикс ошибок и ссылок 2026-06-20 19:10:46 +03:00
bol-van 15bc21d75b update docs 2026-06-16 16:33:43 +03:00
bol-van 4df9449786 nfqws2: fix 60 msec --lua-gc default to 60 sec 2026-06-16 15:50:39 +03:00
bol-van 9474a64d27 nfqws2: do not call timer search by name if not dirty 2026-06-08 20:28:37 +03:00
bol-van 31904a564e nfqws2: compile fix for bsd and lexra 2026-06-08 13:50:42 +03:00
bol-van fd9896846a nfqws2: lua-gc arg check fix 2026-06-08 13:41:14 +03:00
bol-van 2aa5d9ee37 nfqws2: minor optimize 2026-06-08 09:54:40 +03:00
bol-van da3d28f467 nfqws2: optimize 2026-06-08 08:02:13 +03:00
bol-van 5e2b6d8bde nfqws2: AI fixes 2026-06-07 19:09:27 +03:00
bol-van 08d27bf0a2 install_easy: do not use apk in openwrt if it doesn't have db in /lib/apk/db 2026-06-07 09:06:10 +03:00
bol-van d214ebf2de update docs 2026-06-07 08:39:12 +03:00
bol-van fe8b8df2ae nfqws2,zapret-lib: fix wrong spelling : WRITEABLE -> WRITABLE 2026-06-07 08:34:40 +03:00
bol-van 441d2e3cfc update docs 2026-06-06 21:57:18 +03:00
bol-van b37f17e623 zapret-antidpi: do not VERDICT_DROP in send with delay option 2026-06-06 21:50:47 +03:00
bol-van 7b82744d4d nfqws2: fix unitialized lua_ref field access 2026-06-06 09:43:28 +03:00
bol-van 211867eca5 update docs 2026-06-02 10:47:55 +03:00
bol-van c90e0e9a7f zapret-lib: fix oob urp=0 case in rawsend_dissect_segmented 2026-05-27 16:55:51 +03:00
bol-van fd4716da54 update docs 2026-05-09 22:04:07 +03:00
bol-van 5dcb6c1163 zapret-antidpi: send delayed fooled dissect, not original 2026-05-09 22:00:18 +03:00
bol-van fe112c1f22 update docs 2026-05-07 18:14:20 +03:00
bol-van 850a9920f6 update docs 2026-05-07 18:12:53 +03:00
bol-van dc99547ff0 zapret-lib,zapret-antidpi: delay parameter in 'send' desync function 2026-05-07 17:59:40 +03:00
bol-van 8d08b4f9a4 nfqws2: nfq close ifname sock after queue deinit 2026-05-06 14:46:51 +03:00
bol-van 860b13c86e nfqws2: store bt_next instead of bt_prev 2026-05-06 14:45:49 +03:00
bol-van a154c223f4 update docs 2026-05-05 22:02:09 +03:00
bol-van 7a69d56a4b update docs 2026-04-30 17:00:44 +03:00
bol-van b3be87fd7c update docs 2026-04-30 16:58:18 +03:00
bol-van 62b86c3899 zapret-tests: show current time in timer test 2026-04-30 16:44:28 +03:00
bol-van 7db50c018a nfqws2: timer rework. remove timer-res 2026-04-30 13:12:56 +03:00
bol-van eb35bb34f5 update docs 2026-04-29 10:22:25 +03:00
bol-van d0c9fa9b76 copypaste fixes 2026-04-29 08:36:43 +03:00
bol-van 7441ed7119 update LICENSE year 2026-04-28 13:41:44 +03:00
Your Name 7661f29ede cleanup based on cppcheck
nfq2/darkmagic.c:1965:6: style: Condition '!set_socket_fwmark(sock,fwmark)' is always false [knownConditionTrueFalse]
2026-04-17 07:18:15 +02:00
Your Name ce3c1600da ignore .vscode 2026-04-17 07:17:10 +02:00
34 changed files with 590 additions and 333 deletions

No files matched your search

+1
View File
@@ -11,3 +11,4 @@ ipset/zapret-ip*.txt
ipset/zapret-ip*.gz
ipset/zapret-hosts*.txt
ipset/zapret-hosts*.gz
.vscode/
+19 -5
View File
@@ -942,22 +942,36 @@ pktws_ipt_unprepare_udp()
pktws_start()
{
local pidfile
case "$UNAME" in
Linux)
"$NFQWS2" --uid $WS_UID:$WS_GID --fwmark=$DESYNC_MARK --qnum=$QNUM --lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
PID=$!
# give some time to initialize
minsleep
;;
FreeBSD|OpenBSD)
"$DVTWS2" --port=$IPFW_DIVERT_PORT --lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
PID=$!
# give some time to initialize
minsleep
;;
CYGWIN)
pidfile="/tmp/blockcheck_cygpid_$$"
# avoid fork
# allow multiple PKTWS instances with the same wf filter but different ipset
# some methods require empty acks
"$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 &
cygstart --hide "$WINWS2" --pidfile="$pidfile" --wf-dup-check=0 --wf-tcp-empty=1 $WF --ipset="$IPSET_FILE" --lua-init=@"$ZAPRET_BASE/lua/zapret-lib.lua" --lua-init=@"$ZAPRET_BASE/lua/zapret-antidpi.lua" "$@" >/dev/null &
# give some time to initialize
minsleep
if [ -f "$pidfile" ]; then
read PID <"$pidfile"
rm -f "$pidfile"
else
echo "pktws failed to initialize within specified time !!"
fi
;;
esac
PID=$!
# give some time to initialize
minsleep
}
ws_kill()
{
@@ -1089,7 +1103,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"}}"
}
+2 -1
View File
@@ -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
View File
@@ -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
+26
View File
@@ -288,3 +288,29 @@ v0.9.4.3
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)
+6 -6
View File
@@ -11,17 +11,17 @@ 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
curl $CURL_OPT -Lo - https://www.netfilter.org/pub/libnetfilter_queue/libnetfilter_queue-1.0.5.tar.bz2 | tar -xj || exit 5
exists_dir zlib-* ||
curl -Lo - https://zlib.net/fossils/zlib-1.3.2.tar.gz | tar -xz || exit 5
curl $CURL_OPT -Lo - https://zlib.net/fossils/zlib-1.3.2.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
)
}
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
+2
View File
@@ -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"
+1 -1
View File
@@ -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
)
+33 -20
View File
@@ -162,6 +162,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)
@@ -653,10 +654,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.
@@ -1018,7 +1018,7 @@ 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.
@@ -1607,15 +1607,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.
@@ -1706,8 +1697,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
@@ -2433,8 +2424,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)
@@ -2451,7 +2442,7 @@ function timer_del(name)
Get information about the timer identified by name.
```
function timer_del(name)
function timer_info(name)
```
Returns table in case of success, nil otherwise.
@@ -2460,7 +2451,7 @@ Returns table in case of success, nil otherwise.
| Field | Type | Description |
| :------ | :----- | :------------------ |
| name | string | unique timer name |
| func | string | timer function 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 |
@@ -3304,12 +3295,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
@@ -3613,6 +3604,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 dis_timer_name result + conntrack packet number.
If track is absent random characters are added to the end.
This name can be considered unique per desync and can be used as an oneshot timer name.
## Orchestration
This group includes functions that support the orchestration and shimming processes.
@@ -3777,9 +3785,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
+87 -68
View File
@@ -159,6 +159,7 @@
- [rawsend_payload_segmented](#rawsend_payload_segmented)
- [Стандартные фильтры direction и payload](#стандартные-фильтры-direction-и-payload)
- [Работа с многопакетными пейлоадам](#работа-с-многопакетными-пейлоадам)
- [Помощь с таймерами](#помощь-с-таймерами)
- [Оркестрация](#оркестрация)
- [instance_cutoff_shim](#instance_cutoff_shim)
- [cutoff_shim_check](#cutoff_shim_check)
@@ -712,10 +713,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 отключает периодический вызов.
@@ -856,7 +856,7 @@ nfqws2 <глобальные_параметры>
Если не сработал ни один фильтр, выбирается пустой профиль с номером 0, не предполагающий никаких действий с трафиком.
Все условия, кроме `--filter-l7` и хостлистов, являются однозначными и известными с момента начала обработки потока (с начала соединения).
В начале, как правило, еще неизвестнен протокол потока и имя хоста, выделяемое из сообщений потока.
В начале, как правило, еще неизвестен протокол потока и имя хоста, выделяемое из сообщений потока.
Когда эти величины становятся известны, происходит поиск профиля заново. Если выбирается другой профиль - происходит перескок.
Таких перескоков может быть до двух, поскольку есть только 2 величины, влияющие на выбор, неизвестные с самого начала.
Для протоколов потока tls, http, quic обычно бывает только 1 перескок, поскольку определение протокола и имени хоста
@@ -1139,8 +1139,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.
@@ -1231,7 +1231,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` распространяется на два следующих за ним инстанса.
@@ -1527,7 +1527,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`.
@@ -1744,7 +1744,7 @@ desync.track всегда отсутствует.
Lua код может вызываться и вне привязки к принимаемым данным из сети.
Источником события является время.
Таймер - это обьект nfqws2, идентифицируемый по уникальному имени и позволяющий вызывать
Таймер - это объект nfqws2, идентифицируемый по уникальному имени и позволяющий вызывать
указанную Lua функцию с определенной периодичностью, либо однократно через какое-то время.
Установкой таймера занимается функция [timer_set](#timer_set), удаляет таймер функция [timer_del](#timer_del).
@@ -1753,15 +1753,6 @@ nfqws2 - программа однопоточная, как и движок Lua
Таймеры вызываются между обработкой блоков. Если обработка заняла достаточно много времени, то вызов таймера может быть
не идеальным по времени.
Следует помнить о разрешении таймеров, которое задается в параметре `--timer-res`. По умолчанию - 50 мсек.
Это значит, что проверка необходимости вызова таймера может проводиться не чаще указанного времени.
Если вы сделаете period 70 мсек, таймер будет вызываться "рвано". +70 будет вызван при 100, +140 - при 150, +210 - при 250.
Поэтому период желательно делать кратным разрешению таймеров.
Увеличение разрешения (снижение `--timer-res`) может привести к некоторому увеличению нагрузки на CPU,
поскольку процессор будет просыпаться через каждый указанный период времени для проверки всех таймеров.
Чем больше таймеров, тем выше нагрузка на CPU для проверки.
Максимальное разрешение - 100 проверок в секунду, что соответствует `--timer-res=10`.
Таймеры могут быть полезны для обработки ситуаций отсутствия реакции из сети на отсылаемые пакеты.
Например, вам надо что-то куда-то послать и прочитать ответ. Но оппонент может и не ответить или ответ может не дойти.
Чтобы ваша схема не подвисла в неопределенном состоянии и не оставила мусор в памяти, может помочь таймер.
@@ -1853,8 +1844,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 функции
@@ -1930,7 +1921,7 @@ function bitset(u48, bit_from, bit_to, set)
### Операции с беззнаковыми числами
При операциях с числями без знака всегда важна разрядность. От нее зависит результат.
При операциях с числами без знака всегда важна разрядность. От нее зависит результат.
Поэтому все функции имеют в своем названии разрядность. При передаче аргументов, выходящих за пределы разрядности, вызывается error.
#### uX
@@ -2140,7 +2131,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
@@ -2154,8 +2145,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.
### Системные функции
@@ -2251,7 +2242,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. За вас это сделает реконструктор диссекта.
@@ -2593,8 +2584,8 @@ function timer_set(name, func, period, oneshot, data)
```
* name - уникальное имя таймера. если таймер с таким именем уже есть, он удаляется и замещается новым. При замещении отсчет времени начинается заново.
* func - имя таймер функции (string)
* period - периодичность вызова таймера в миллисекундах (см. [разрешение таймеров](#таймеры))
* func - таймер функция. string или function. в случае string функция ищется по имени в момент вызова timer_set и запоминается по ссылке, а не как строка.
* period - периодичность вызова таймера в миллисекундах
* oneshot - bool признак является ли таймер однократным (true) или периодическим (false)
* data - произвольная переменная, передаваемая [таймер-функции](#прототип-функции-таймера)
@@ -2611,7 +2602,7 @@ function timer_del(name)
Получить информацию о таймере.
```
function timer_del(name)
function timer_info(name)
```
В случае успеха возвращается таблица, в случае неудачи - nil.
@@ -2620,7 +2611,7 @@ function timer_del(name)
| Поле | Тип | Описание |
| :------ | :----- | :------------------ |
| name | string | уникальное имя таймера |
| func | string | имя таймер функции |
| func | function | таймер функция |
| oneshot | bool | true = однократный таймер, false = периодический |
| period | number | период таймера в мсек |
| fires | number | количество выполненных вызовов таймера |
@@ -2921,7 +2912,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>
@@ -3348,7 +3339,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)
@@ -3477,13 +3468,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.
## Чтение и запись файлов
@@ -3538,7 +3529,7 @@ function autottl(incoming_ttl, attl)
```
Механизм autottl служит, чтобы автоматически на базе TTL входящего пакета определять такой TTL,
который либо немного не достает до противопложного конца, либо немного превышает длину пути до него.
который либо немного не достает до противоположного конца, либо немного превышает длину пути до него.
delta - это положительная или отрицательная разница с догадкой о длине пути. min-max - допустимый диапазон.
Если итоговый результат выходит за пределы диапазона, назначаются его крайние значения.
Если delta<0 и результат оказывается равен пути или длиннее, либо если delta>=0 и результат оказывается короче пути, алгоритм уходит в ошибку и выдает nil.
@@ -3564,7 +3555,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 на собственную последовательность, остальные ОС - нет.
@@ -3676,7 +3667,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
@@ -3710,7 +3701,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
@@ -3792,6 +3783,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, его можно использовать как oneshot timer name.
## Оркестрация
В эту группу функций входят функции поддержки процесса оркестрации и прокладки.
@@ -3841,7 +3850,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`.
@@ -3864,7 +3873,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) и существует этот вариант.
@@ -3905,7 +3914,7 @@ function replay_execution_plan(desync, max)
## Стандартные наборы параметров
Многие функции берут стандартные наборы аргументов, классифицируемые по их назначанию.
Многие функции берут стандартные наборы аргументов, классифицируемые по их назначению.
Дополнительные фильтры по направлению и пейлоаду внутри функций anti-dpi сделаны в основном на случай не слишком грамотного написания опций командной строки как дополнительный предохранитель и защита от дурака, чтобы сделать флуд как это было в winws1 с `--dpi-desync-any-protocol` непреднамеренно было непросто.
@@ -3961,10 +3970,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
```
@@ -3998,7 +4016,7 @@ function http_domcase(ctx, desync)
- arg: [standard direction](#standard-direction)
Меняет регистр написания домена внутри заголовка `Host:`. Верхний и нижний регистр чредуется каждый символ : `rUtRaCkEr.oRg`.
Меняет регистр написания домена внутри заголовка `Host:`. Верхний и нижний регистр чередуется каждый символ : `rUtRaCkEr.oRg`.
### http_methodeol
@@ -4032,7 +4050,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 сегментации, поскольку они лишены побочных эффектов в виде замедления скорости и имеют больше возможностей.
@@ -4047,7 +4065,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).
@@ -4217,9 +4235,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.
@@ -4287,10 +4305,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
@@ -4333,7 +4351,7 @@ function fakeddisorder(ctx, desync)
- ipid_opts и rawsend_opts применяются и к фейкам, и к оригиналом. ipfrag_opts не задействуются ни для фейков, ни для оригиналов.
В случае успеха отсылки выносится VERDICT_DROP, если не указано "nodrop".
[blob](#передача-блобов) позволяет заменить текущией пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
[blob](#передача-блобов) позволяет заменить текущий пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
### hostfakesplit
@@ -4373,7 +4391,7 @@ function hostfakesplit(ctx, desync)
- ipid_opts и rawsend_opts применяются и к фейкам, и к оригиналом. ipfrag_opts не задействуются ни для фейков, ни для оригиналов.
В случае успеха отсылки выносится VERDICT_DROP, если не указано "nodrop".
[blob](#передача-блобов) позволяет заменить текущией пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
[blob](#передача-блобов) позволяет заменить текущий пейлоад на произвольный [blob](#передача-блобов), и тем самым отослать любой совместимый пейлоад с тем же разбиением.
### tcpseg
@@ -4553,7 +4571,7 @@ function automate_host_record(desync)
## Обслуживание удач и неудач
Простое, но далеко неточное, обьяснение понятиям "удача" и "неудача" - "сайт открывается" и "сайт не открывается". В логике автоматизации эти состояния абстрактны. Любой процесс может претерпеть удачу или неудачу. Детект состояния удачи или неудачи нужен для подсчета неудач через счетчик. При неудаче счетчик увеличивается, при успехе - сбрасывается, при достижении целевого значения - возвращается признак, по которому внешняя логика может выполнить любое действие. Например, сменить стратегию.
Простое, но далеко неточное, объяснение понятиям "удача" и "неудача" - "сайт открывается" и "сайт не открывается". В логике автоматизации эти состояния абстрактны. Любой процесс может претерпеть удачу или неудачу. Детект состояния удачи или неудачи нужен для подсчета неудач через счетчик. При неудаче счетчик увеличивается, при успехе - сбрасывается, при достижении целевого значения - возвращается признак, по которому внешняя логика может выполнить любое действие. Например, сменить стратегию.
### automate_failure_counter
@@ -4630,7 +4648,7 @@ function standard_failure_detector(desync, crec)
Стандартный детектор неудач.
http редиректом от DPI считается то же самое , что и в [автохослистах](#детектор-неудач-автохостлистов).
http редиректом от DPI считается то же самое , что и в [автохостлистах](#детектор-неудач-автохостлистов).
## Оркестраторы
@@ -4766,7 +4784,7 @@ function cond_payload_str(desync)
- arg: pattern - строка, которая ищется в пейлоаде
Возвращает true, если в desync.dis.payload присутствует подстрока pattern.
Это простейший сигнатурый детектор. Если C код не распознает нужный вам протокол, вы можете написать свой сигнатурный детектор и запускать последующие инстансы под оркестратором condition с вашим детектором в качестве iff.
Это простейший сигнатурный детектор. Если C код не распознает нужный вам протокол, вы можете написать свой сигнатурный детектор и запускать последующие инстансы под оркестратором condition с вашим детектором в качестве iff.
#### cond_tcp_has_ts
@@ -4885,7 +4903,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 скриптами. Это удобно, потому что не нужно думать о путях и каждый раз писать или вставлять откуда-то огромный текст, тем более при наличии национальных символов и пробелов в путях, путаясь в экранировании символов и кодировках.
Возможно последовательное тестирование нескольких доменов. Для этого нужно задать их через пробел.
@@ -5094,7 +5112,7 @@ NOTEST_QUIC=1 - отмена тестов 90-quic.sh
Простой тестировщик по списку стратегий из файлов. Стратегии должны быть на отдельных строчках, переносы строки не допускаются. Используются отдельные списки для протоколов - `list_http.txt`, `list_https_tls12.txt`, `list_https_tls13.txt`, `list_quic.sh`. В файлах поддерживаются комментарии, начинающиеся на `#`.
При записи параметров следует учитывать, что они будут интерпретироваься как параметры shell. Специальные символы нужно экранировать по правилам shell.
При записи параметров следует учитывать, что они будут интерпретироваться как параметры shell. Специальные символы нужно экранировать по правилам shell.
Если вы оставите "<" без кавычек на всем параметре или возьмете в кавычки параметр `--luaexec=code=print("abc")`, будет ошибка. Если в lua коде используются строки - их стоит заключать в одинарные кавычки, а вокруг параметра - двойные.
blockcheck2 будет выдавать параметры стратегий без экранирования.
@@ -5153,14 +5171,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% нажимает на кнопку, и она не срабатывает, для них решение не работает.
На деле же требуется техническое исследование что они там натворили очередной раз. Много ручных тестов для выявление алгоритма их действий и нащупывание обходного решения.
@@ -5220,9 +5239,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.
## Система ведения листов
@@ -5451,7 +5470,7 @@ fw_nfqws_pre()
Что не следует писать в фильтр :
- Проверку стандарных exclude ipset - nozapret, nozapret6.
- Проверку стандартных exclude ipset - nozapret, nozapret6.
- Проверку по [DESYNC_FWMARK](#файл-config).
Будут ли применены правила для каждой версии ip зависит от настроек [config](#файл-config).
@@ -5540,7 +5559,7 @@ nft_fw_nfqws_pre()
Что не следует писать в фильтр :
- Проверку стандарных exclude ipset - nozapret, nozapret6.
- Проверку стандартных exclude ipset - nozapret, nozapret6.
- Проверку по [DESYNC_FWMARK](#файл-config).
Будут ли применены правила для каждой версии ip зависит от настроек [config](#файл-config).
@@ -5657,7 +5676,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
@@ -5678,7 +5697,7 @@ systemctl daemon-reload
systemctl enable zapret2
```
2. Таймер для обновления листов - в случайное время суток каждые 2 дня. Рассчет идет на любую систему, в том числе десктопную, которую могут включать только днем.
2. Таймер для обновления листов - в случайное время суток каждые 2 дня. Расчет идет на любую систему, в том числе десктопную, которую могут включать только днем.
```
cp /opt/zapret2/init.d/systemd/zapret2-list-update.* /lib/systemd/system
@@ -5705,7 +5724,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
@@ -5723,7 +5742,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`
@@ -5766,7 +5785,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.
@@ -5805,7 +5824,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 не поддерживаются.
+11 -11
View File
@@ -9,8 +9,8 @@
VPN. Может использоваться для частичной прозрачной обфускации протоколов.
Проект нацелен прежде всего на маломощные embedded устройства - роутеры, работающие под OpenWrt. Поддерживаются
традиционные Linux-системы, FreeBSD, OpenBSD, Windows. В некоторых случаях возможна самостоятельная прикрутка
решения к различным прошивкам.
традиционные Linux-системы, FreeBSD, OpenBSD, Windows. MacOS не поддерживается и вряд ли будет по техническим причинам.
В некоторых случаях возможна самостоятельная прикрутка решения к различным прошивкам.
[Полный мануал](manual.md)
@@ -102,7 +102,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 +117,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 +150,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 +167,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 +248,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 +269,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
```
@@ -468,7 +468,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
+12 -2
View File
@@ -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 = "send_"..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
+64 -4
View File
@@ -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 !")
@@ -761,6 +761,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 = 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
@@ -1193,7 +1250,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
@@ -1376,9 +1435,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
@@ -1690,6 +1749,7 @@ function writefile(filename, data)
end
end
-- DISSECTORS
function http_dissect_header(header)
+3 -3
View File
@@ -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])
+18 -2
View File
@@ -907,9 +907,13 @@ 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.func="..tinfo.func)
print(" timer_info.period="..tinfo.period)
print(" timer_info.oneshot="..tostring(tinfo.oneshot))
print(" timer_info.fires="..tinfo.fires)
@@ -921,19 +925,30 @@ 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))
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")
@@ -942,6 +957,7 @@ function test_timer(opts)
print("TIMER "..i.." :")
timer_info_print_by_name(name)
end
print_current_time()
print()
end
+1 -1
View File
@@ -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);
+3 -1
View File
@@ -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
+48 -30
View File
@@ -20,6 +20,8 @@
#include "nfqws.h"
#ifdef __CYGWIN__
#include "timer.h"
#include <sys/cygwin.h>
#include <wlanapi.h>
@@ -1627,32 +1629,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, &params.timers_dirty);
bt = boottime_ms();
tmax = *bt_next>bt ? *bt_next-bt : 0;
if (!tmax)
{
*bt_next = TimerPoolRun(&params.timers, &params.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 +1674,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(&params.timers, &params.timers_dirty, bt);
bt = boottime_ms();
tmax = *bt_next>bt ? *bt_next-bt : 0;
}
}
if (!GetOverlappedResult(hFilter,&ovl,&rd,FALSE))
{
@@ -1700,9 +1716,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 +1978,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 +2048,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 +2481,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 +2519,6 @@ bool make_writeable_dir()
}
}
#endif
if (b) setenv("WRITEABLE",wrdir,1);
if (b) setenv("WRITABLE",wrdir,1);
return b;
}
+3 -4
View File
@@ -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;
@@ -135,8 +135,7 @@ 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);
+5 -2
View File
@@ -705,7 +705,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)
{
@@ -1583,7 +1583,10 @@ static uint8_t dpi_desync_tcp_packet_play(
}
else
{
// likely exceeded packet limit or unlikely out of memory
DLOG_ERR("rawpacket_queue failed !\n");
reasm_client_cancel(ps.ctrack);
ps.l7payload = L7P_UNKNOWN; // middle packet may be not L7P_TLS_CLIENT_HELLO
goto rediscover;
}
if (ReasmIsFull(&ps.ctrack->reasm_client))
@@ -2194,7 +2197,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;
+41 -13
View File
@@ -3795,36 +3795,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(&params.timers, timer);
params.timers_dirty = true;
action = "replaced";
}
else
action = "created";
timer = TimerPoolAdd(&params.timers, name, func, period, oneshot);
if (!timer) luaL_error(L,"could not create timer");
timer = TimerPoolAdd(&params.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,24 +3863,30 @@ static int luacall_timer_del(lua_State *L)
{
DLOG("timer: '%s' deleted\n", timer->str);
TimerPoolDel(&params.timers, timer);
params.timers_dirty = true;
}
else
DLOG("timer: '%s' not found\n", name);
lua_pushboolean(L, !!timer);
LUA_STACK_GUARD_RETURN(L,1)
}
static int lua_push_timer_info(lua_State *L, const timer_pool *timer)
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) lua_pushf_str(L, "func", timer->func);
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_del(name)
// timer_info(name)
lua_check_argc(L,"timer_info",1);
LUA_STACK_GUARD_ENTER(L)
@@ -4610,12 +4638,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);
+70 -76
View File
@@ -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, &params.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(&params.timers, 0);
bt_next = TimerPoolRun(&params.timers, &params.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,7 +528,7 @@ static int nfq_main(void)
notify_ready();
fd = nfq_fd(h);
bt_prev=0;
bt_next = bt = 0;
do
{
if (bQuit) goto quit;
@@ -527,6 +536,12 @@ static int nfq_main(void)
{
if (params.timers)
{
if (!bt_next) bt_next = TimerPoolNext(params.timers, &params.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);
@@ -540,6 +555,7 @@ static int nfq_main(void)
}
else
{
bt_next = bt = 0;
if (bQuit) goto quit;
res = 1;
}
@@ -566,21 +582,10 @@ static int nfq_main(void)
if (params.timers)
{
bt = boottime_ms();
dbt = bt-bt_prev;
if (dbt>=params.timer_res)
{
TimerPoolRun(&params.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);
}
if (bt>=bt_next)
bt_next = TimerPoolRun(&params.timers, &params.timers_dirty, bt);
else if (params.timers_dirty)
bt_next = 0;
}
}
if (errno==EINTR)
@@ -602,10 +607,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();
@@ -629,8 +634,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")))
{
@@ -714,16 +719,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, params.timers ? &tv : NULL);
if (params.timers)
{
if (!bt_next) bt_next = TimerPoolNext(params.timers, &params.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");
@@ -810,21 +828,10 @@ static int dvt_main(void)
if (params.timers)
{
bt = boottime_ms();
dbt = bt-bt_prev;
if (dbt>=params.timer_res)
{
TimerPoolRun(&params.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);
}
if (bt>=bt_next)
bt_next = TimerPoolRun(&params.timers, &params.timers_dirty, bt);
else if (params.timers_dirty)
bt_next = 0;
}
}
@@ -846,11 +853,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(&params.timers, bt);
}
static int win_main(void)
{
size_t len, packet_len, left, modlen;
@@ -863,7 +865,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();
@@ -933,11 +935,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)
{
@@ -1839,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"
@@ -1866,7 +1868,7 @@ static void exithelp(void)
" --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"
@@ -1915,7 +1917,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,
@@ -1998,14 +1999,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,
@@ -2104,13 +2104,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},
@@ -2507,14 +2506,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)
@@ -2530,18 +2521,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:
@@ -2568,11 +2559,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:
@@ -3159,14 +3153,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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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(&params->hostlists);
LIST_INIT(&params->ipsets);
+4 -6
View File
@@ -37,8 +37,6 @@
#define IPCACHE_LIFETIME 7200
#define TIMER_RES_DEFAULT 50
#define MAX_GIDS 64
#define MAX_BLOB_SIZE (16*1024)
@@ -192,13 +190,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;
};
+1 -1
View File
@@ -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;
}
+83 -40
View File
@@ -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,18 +39,14 @@ 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;
@@ -58,17 +54,11 @@ struct timer_pool *TimerPoolAdd(timer_pool **pp, const char *str, const char *fu
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);
p->fires++;
DLOG("\ntimer: '%s' function '%s' period %llu oneshot %u fires=%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)
{
@@ -77,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;
}
+6 -4
View File
@@ -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;
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);