Template
1
0
mirror of https://github.com/bol-van/zapret2.git synced 2026-03-16 14:58:17 +00:00

44 Commits

Author SHA1 Message Date
bol-van
7c320c8d57 update docs 2026-01-12 10:13:26 +03:00
bol-van
b18f0770c8 update docs 2026-01-12 10:12:35 +03:00
bol-van
f7fc845014 update docs 2026-01-12 10:11:10 +03:00
bol-van
2c1a885a07 update docs 2026-01-12 10:10:01 +03:00
bol-van
9eb308d84c update docs 2026-01-12 10:09:02 +03:00
bol-van
3e724c3810 update docs 2026-01-12 10:03:35 +03:00
bol-van
c179d55d88 nfqws2: harden wireguard detector 2026-01-12 09:34:56 +03:00
bol-van
3f1af1441e nfqws2: check quit flag outside of EINTR context 2026-01-12 09:08:19 +03:00
bol-van
4c1b2b65f3 nfqws2: gracefully shutdown on SIGINT and SIGTERM 2026-01-11 21:01:18 +03:00
bol-van
918258413f nfqws2: fix tls reasm logic 2026-01-11 19:41:13 +03:00
bol-van
e6206c5a5f nfqws2: fix tls reasm 2026-01-11 18:20:29 +03:00
bol-van
f93c6de772 nfqws2: --payload-disable 2026-01-11 17:25:53 +03:00
bol-van
5a7e2b1ca2 nfqws2: alternative representation of payload filter in execution_plan item 2026-01-11 16:20:45 +03:00
bol-van
ca8104c72a zapret-lib: remove bitable, use barray 2026-01-11 15:36:10 +03:00
bol-van
3aad1f9ed9 nfqws2: optimize ctx userdata 2026-01-11 14:10:42 +03:00
bol-van
fd288d5e7d nfqws2: move ctx from lightuserdata to userdata. prevents crashes on specific ARM cpus 2026-01-11 13:58:31 +03:00
bol-van
349fe3f7d7 update docs 2026-01-11 12:32:44 +03:00
bol-van
4554b7c15b zapret-lib, zapret-antidpi: use numeric indexes in http dissects 2026-01-11 12:29:53 +03:00
bol-van
0b595ae3a8 AI inspired fixes 2026-01-11 11:39:37 +03:00
bol-van
3e69e1b8c1 zapret-lib: bitable 2026-01-11 11:21:12 +03:00
bol-van
02b895910b dvtws2: openbsd compile fix 2026-01-11 11:04:09 +03:00
bol-van
b2a53e9c64 nfqws2: AI inspired fixes 2026-01-11 10:56:24 +03:00
bol-van
a626cfce8a update docs 2026-01-11 09:36:47 +03:00
bol-van
ebcbfc37ba update docs 2026-01-10 20:14:44 +03:00
bol-van
33d3c94b68 update docs 2026-01-10 20:12:45 +03:00
bol-van
d55dbb7717 update docs 2026-01-10 20:05:21 +03:00
bol-van
cb82be9eab update docs 2026-01-10 20:04:01 +03:00
bol-van
024d36acc4 update docs 2026-01-10 20:02:06 +03:00
bol-van
08c6151a4c update docs 2026-01-10 19:56:22 +03:00
bol-van
520317dc3c update docs 2026-01-10 19:24:02 +03:00
bol-van
6bc0bf1b97 AI inspired fixes 2026-01-10 18:54:26 +03:00
bol-van
d18fec9053 update docs 2026-01-10 16:47:40 +03:00
bol-van
e60e5a0578 update docs 2026-01-10 16:46:44 +03:00
bol-van
84576a7039 update docs 2026-01-10 16:44:07 +03:00
bol-van
7957a0a425 update docs 2026-01-10 16:42:58 +03:00
bol-van
7ba4110416 update docs 2026-01-10 16:35:54 +03:00
bol-van
4babaef6a8 update docs 2026-01-10 16:32:54 +03:00
bol-van
872e37d160 update docs 2026-01-10 16:28:48 +03:00
bol-van
a8219f4897 update docs 2026-01-10 16:25:24 +03:00
bol-van
36267b7e9b update docs 2026-01-10 16:22:35 +03:00
bol-van
99a7f06976 eng manual 2026-01-10 15:42:42 +03:00
bol-van
3617b8934f blockcheck2: 23-seqovl fix 2026-01-10 14:58:38 +03:00
bol-van
8e6387a6df config.default add MDIG comments 2026-01-09 13:14:36 +03:00
bol-van
3bc0e8e350 mdig: EAGAIN->EAI_AGAIN help text 2026-01-09 12:38:06 +03:00
29 changed files with 5610 additions and 335 deletions

View File

@@ -47,14 +47,14 @@ pktws_seqovl_tests_tls()
ok=0
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=tcpseg:pos=0,-1:seqovl=1 --lua-desync=drop && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$rnd_mod $pre $PAYLOAD --lua-desync=tcpseg:pos=0,-1:seqovl=#$pat:seqovl_pattern=$pat --lua-desync=drop && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$pre $PAYLOAD $padencap_mod --lua-desync=tcpseg:pos=0,-1:seqovl=#pat:seqovl_pattern=pat --lua-desync=drop && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$pre $PAYLOAD $padencap_mod --lua-desync=tcpseg:pos=0,-1:seqovl=#pat:seqovl_pattern=$pat --lua-desync=drop && ok=1
ok_any=$ok
ok=0
for split in 10 10,sniext+1 10,sniext+4 10,midsld; do
pktws_curl_test_update $testf $domain $pre $PAYLOAD --lua-desync=multisplit:pos=$split:seqovl=1 && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$rnd_mod $pre $PAYLOAD --lua-desync=multisplit:pos=$split:seqovl=#$pat:seqovl_pattern=$pat && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$pre $PAYLOAD $padencap_mod --lua-desync=multisplit:pos=$split:seqovl=#pat:seqovl_pattern=pat && ok=1
pktws_curl_test_update $testf $domain ${SEQOVL_PATTERN_HTTPS:+--blob=$pat:@"$SEQOVL_PATTERN_HTTPS" }$pre $PAYLOAD $padencap_mod --lua-desync=multisplit:pos=$split:seqovl=#pat:seqovl_pattern=$pat && ok=1
[ "$ok" = 1 -a "$SCANLEVEL" != force ] && break
done
for split in '1 2' 'sniext sniext+1' 'sniext+3 sniext+4' 'midsld-1 midsld' '1 2,midsld'; do

View File

@@ -366,7 +366,7 @@ random()
local r rs
setup_random
if [ -c /dev/urandom ]; then
read rs </dev/urandom
rs=$(dd if=/dev/urandom count=1 bs=16 2>/dev/null | hexdump -e '1 "%02x"')
else
rs="$RANDOM$RANDOM$(date)"
fi

View File

@@ -522,11 +522,6 @@ install_openwrt_firewall()
{
echo \* installing firewall script $1
[ -n "MODE" ] || {
echo should specify MODE in $ZAPRET_CONFIG
exitp 7
}
echo "linking : $FW_SCRIPT_SRC => $OPENWRT_FW_INCLUDE"
ln -fs "$FW_SCRIPT_SRC" "$OPENWRT_FW_INCLUDE"

View File

@@ -41,7 +41,7 @@ ipt6_add_del()
}
ipt6a_add_del()
{
on_off_function ipt6 ipt6a_del "$@"
on_off_function ipt6a ipt6_del "$@"
}
is_ipt_flow_offload_avail()

View File

@@ -41,7 +41,9 @@ AUTOHOSTLIST_DEBUGLOG=0
# number of parallel threads for domain list resolves
MDIG_THREADS=30
# EAI_AGAIN retries
MDIG_EAGAIN=10
# delay between EAI_AGAIN retries (ms)
MDIG_EAGAIN_DELAY=500
# ipset/*.sh can compress large lists

View File

@@ -169,3 +169,12 @@ v0.8.1
* nfqws2: ignore trailing spaces and tabs in hostlists and ipsets. "host.com " or "1.2.3.4 " are ok now
* init.d: 99-lan-filter custom script
* mdig: --eagain, --eagain-delay
0.8.4
* winws2: fix loopback large packets processing (up to 64K)
* zapret-lib, zapret-antidpi: use numeric indexes in http dissects
* nfqws2: move ctx from lightuserdata to userdata. prevents crashes on specific ARM cpus
* nfqws2: alternative representation of payload filter in execution_plan item
* nfqws2: --payload-disable
* nfqws2: gracefully shutdown on SIGINT and SIGTERM

5098
docs/manual.en.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -14,6 +14,7 @@
- [nfqws2](#nfqws2)
- [Общие принципы задания параметров](#общие-принципы-задания-параметров)
- [Полный список опций](#полный-список-опций)
- [Распознавание протоколов](#распознавание-протоколов)
- [Использование множественных профилей](#использование-множественных-профилей)
- [Шаблоны профилей](#шаблоны-профилей)
- [Фильтрация по листам](#фильтрация-по-листам)
@@ -668,10 +669,11 @@ nfqws2 использует стандартный парсер getopt_long_only
--pidfile=<filename> ; запись PID в файл
--ctrack-timeouts=S:E:F[:U] ; таймауты conntrack для стадий tcp SYN, ESTABLISHED, FIN и для udp
--ctrack-disable=[0|1] ; 1 отключает conntrack
--payload-disable=[type[,type]] ; отключить распознавание указанных типов пейлоадов. без аргумента - отключить все.
--server=[0|1] ; серверный режим. для обслуживания listener-ов меняются многие аспекты выбора направления и ip/port источника/приемника
--ipcache-lifetime=<int> ; время жизни записей кэша IP в секундах. 0 - без ограничений.
--ipcache-hostname=[0|1] ; 1 или отсутствие аргумента включают кэширование имен хостов для применения в стратегиях нулевой фазы
--reasm-disable=[proto[,proto]] ; отключить сборку фрагментов для списка пейлоадов : tls_client_hello quic_initial . без аргумента - отключить reasm для всего.
--reasm-disable=[type[,type]] ; отключить сборку фрагментов для списка пейлоадов : tls_client_hello quic_initial . без аргумента - отключить reasm для всего.
DESYNC ENGINE INIT:
--writeable[=<dir_name>] ; создать директорию для Lua с разрешением записи и поместить путь к ней в переменную env "WRITEABLE" (только одна директория)
@@ -689,7 +691,7 @@ MULTI-STRATEGY:
--filter-l3=ipv4|ipv6 ; фильтр профиля : версия ip протокола
--filter-tcp=[~]port1[-port2]|* ; фильтр профиля : порты tcp или диапазоны портов через запятую
--filter-udp=[~]port1[-port2]|* ; фильтр профиля : порты udp или диапазоны портов через запятую
--filter-l7=proto[,proto] ; фильтр профиля : список протоколов потока. полный список доступен в help тексте программы.
--filter-l7=proto[,proto] ; фильтр профиля : список протоколов потока
--ipset=<filename> ; фильтр профиля : включающий список ip адресов или подсетей из файла. может быть смешанным ipv4+ipv6.
--ipset-ip=<ip_list> ; фильтр профиля : включающий фиксированный список ip адресов или подсетей через запятую
--ipset-exclude=<filename> ; фильтр профиля : исключающий список ip адресов или подсетей из файла. может быть смешанным ipv4+ipv6.
@@ -710,7 +712,7 @@ MULTI-STRATEGY:
--hostlist-auto-debug=<logfile> ; дебаг лог автолиста
LUA PACKET PASS MODE:
--payload=type[,type] ; внутрипрофильный фильтр : фильтр пейлоада для последующих инстансов внутри профиля. список пейлоадов доступен в help тексте программы.
--payload=type[,type] ; внутрипрофильный фильтр : фильтр пейлоада для последующих инстансов внутри профиля
--out-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>] ; внутрипрофильный фильтр : диапазон счетчиков conntrack для последующих инстансов внутри профиля - исходящее направление
--in-range=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>] ; внутрипрофильный фильтр : диапазон счетчиков conntrack для последующих инстансов внутри профиля - входящее направление
@@ -758,6 +760,30 @@ LOGICAL NETWORK FILTER:
--nlm-list[=all] ; вывести список подключенных NLM сетей. all - список всех NLM сетей
```
## Распознавание протоколов
nfqws2 сигнатурно распознает типы пейлоадов отдельно взятых пакетов или групп пакетов.
Все пустые пакеты имеют пейлоад empty, неизвестные - unknown.
Протокол потока присваивается после получение первого известного пейлоада и остается с потоком до конца его существования.
При этом последующие пейлоады могут иметь как известный тип, так и неизвестный.
В фильтрах по пейлоаду и протоколу потока доступны специальные значения - all и known. All означает любой, known - не empty и не unknown.
Таблица распознаваемых типов пейлоада и протоколов потока
| Протокол потока | L4 | Пейлоады |
| :-------------- | :-- | :------- |
| http | tcp | http_req<br>http_reply |
| tls | tcp | tls_client_hello<br>tls_server_hello |
| xmpp | tcp | xmpp_stream<br>xmpp_starttls<br>xmpp_proceed<br>xmpp_features |
| mtproto | tcp | mtproto_initial |
| quic | udp | quic_initial |
| wireguard | udp | wireguard_initiation<br>wireguard_response<br>wireguard_cookie<br>wireguard_keepalive |
| dht | udp | dht |
| discord | udp | discord_ip_discovery |
| stun | udp | stun |
| dns | udp | dns_query<br>dns_response |
| dtls | udp | dtls_client_hello<br>dtls_server_hello |
## Использование множественных профилей
Профили существуют, чтобы в зависимости от указанных условий фильтра выбрать ту или иную стратегию воздействия на трафик.
@@ -1085,7 +1111,7 @@ Lua код выполняется с ограниченными правами,
Они бывают трех видов - `--payload`, `--in-range`, `--out-range`.
Значения фильтров действуют с момента их указания до следующего переопределения.
- `--payload=type1[,type2][,type2]...` принимает список известных пейлоадов через зяпятую, "all" или "known". Список известных пейлоадов доступен в help тексте nfqws2. По умолчанию `--payload=all`.
- `--payload=type1[,type2][,type2]...` принимает список известных пейлоадов через зяпятую, "all" или "known". [Список известных пейлоадов](#распознавание-протоколов). По умолчанию `--payload=all`.
- `--(in-range|out-range)=[(n|a|d|s|p)<int>](-|<)[(n|a|d|s|p)<int>]` задает диапазоны счетчиков conntrack по входящему и исходящему направлениям. По умолчанию `--in-range=x`, `--out-range=a`.
Диапазоны задаются в формах : `mX-mY`, `mX<mY`, `-mY`, `<mY`, `mX-`, где m - режим счетчика, X - нижнее значение, Y - верхнее значение.
@@ -1417,8 +1443,8 @@ desync
| replay_piece | number | номер проигрываемой части | нумерация с 1 |
| replay_piece_count | number | количество проигрываемых частей | |
| replay_piece_last | bool | последняя проигрываемая часть | |
| l7payload | string | тип пейлоада текущего пакета или группы пакетов. список возможных в help тексте nfqws2 | если неизвестно - unknown |
| l7proto | string | тип протокола потока | если неизвестно - unknown |
| l7payload | string | тип [пейлоада](#распознавание-протоколов) текущего пакета или группы пакетов | если неизвестно - unknown |
| l7proto | string | тип [протокола потока](#распознавание-протоколов) | если неизвестно - unknown |
| reasm_data | string | результат сборки многопакетного сообщения, либо сам пейлоад, если сборки не было | пока применяется только для tcp |
| reasm_offset | string | смещение текущего переигрываемого пакета в сборке | пока применяется только для tcp |
| decrypt_data | string | результат сборки и дешифровки пейлоада или пейлоадов нескольких пакетов | применяется для quic |
@@ -1554,7 +1580,7 @@ ipv6 extension headers и tcp options представляются в форме
| Поле | Тип | Описание | Примечание |
| :------------- | :----- | :-------------------------------------------------------------- | :----------------------------------------------- |
| incoming_ttl | number | ttl/hl первого входящего пакета по потоку | может не быть, если не определено |
| l7proto | string | протокол потока. список возможных доступен в help тексте nfqws2 | есть всегда. если неизвестно - unknown |
| l7proto | string | [протокол потока](#распознавание-протоколов) | есть всегда. если неизвестно - unknown |
| hostname | string | имя хоста. определяется на основе анализа L6/L7 протоколов | появляется только после определения |
| hostname_is_ip | bool | является ли hostname ip адресом | только если есть hostname |
| lua_state | table | таблица для хранения состояния, привязанного к потоку | есть всегда, передается с каждым пакетом потока |
@@ -2212,7 +2238,8 @@ function execution_plan(ctx)
| func_n | number | номер инстанса внутри профиля |
| func_instance | string | название инстанса | производная имени функции, номера инстанса и номера профиля |
| range | table | эффективный диапазон [счетчиков](#внутрипрофильные-фильтры) `--in-range` или `--out-range` в зависимости от текущего направления |
| payload_filter | string | эффективный `--payload-filter` . список названий пейлоадов через запятую |
| payload | table | эффективный `--payload-filter` . таблица с индексами - названиями типа пейлоада |
| payload_filter | string | эффективный `--payload-filter` . список названий пейлоадов через запятую (иное представление payload) |
**range**
@@ -2520,7 +2547,9 @@ function http_reconstruct_req(hdis, unixeol)
Разборка HTTP запроса или ответа http. http представляет собой многострочный текст.
Разборка представляет собой таблицу с вложенными подтаблицами.
В заголовках выдаются позиции начала и конца названия заголовка и самого значения.
Названия полей в таблице headers соответствуют названию заголовков в нижнем регисте. Все позиции - внутри строки http.
Все позиции - внутри строки http.
Для нахождения хедеров по названию используйте [array_field_search](#array_search) по полю "header_low", которое содержит название хедера в нижнем регистре.
Реконструктор http запроса берет таблицу-разбор и воссоздает raw string. Параметр unixeol заменяет стандартный для http перевод сктроки 0D0A на 0A. Это нестандарт и ломает все сервера, кроме nginx.
@@ -2530,9 +2559,11 @@ function http_reconstruct_req(hdis, unixeol)
.uri
string /test_uri
.headers
.content-length
.1
.header
string Content-Length
.header_low
string content-length
.value
string 330
.pos_start
@@ -2543,9 +2574,11 @@ function http_reconstruct_req(hdis, unixeol)
number 56
.pos_value_start
number 59
.host
.2
.header
string Host
.header_low
string host
.value
string testhost.com
.pos_start
@@ -2566,26 +2599,30 @@ function http_reconstruct_req(hdis, unixeol)
.code
number 200
.headers
.content-type
.1
.pos_header_end
number 28
.pos_value_start
number 31
.header
string Content-Type
.header_low
string content-type
.value
string text/html
.pos_start
number 17
.pos_end
number 39
.content-length
.2
.pos_header_end
number 54
.pos_value_start
number 57
.header
string Content-Length
.header_low
string content-length
.value
string 650
.pos_start
@@ -2928,7 +2965,7 @@ function tls_reconstruct(tdis)
Для некоторых элементов заполняется поле "name". Оно служит для удобства визуального анализа и больше ни для чего не используется.
Первичными являются поля type.
Для нахождения значений в списках используйте [функции поиска в массивах](#служебные-функции).
Для нахождения значений в списках используйте [функции поиска в массивах](#array_search).
Множество констант, связанных с TLS, определено в `zapret-lib.lua`. Прежде чем писать фиксированные значения, посмотрите нет ли нужной константы.
@@ -3319,7 +3356,8 @@ function payload_match_filter(l7payload, l7payload_filter, def)
function payload_check(desync, def)
```
Функции работают со строкой - списком пейлоадов через запятую. Список известных типов пейлоада можно получить из help текста nfqws2. Все пустые пакеты имеют пейлоад empty, неизвестные - unknown. Особые значения - all и known. all означает любой пейлоад, known - не unknown и не empty. Префикс `~` в начале означает логическую инверсию - несоответствие списку.
Функции работают со строкой - списком пейлоадов через запятую. Особые значения - all и known. all означает любой пейлоад, known - не unknown и не empty. Префикс `~` в начале означает логическую инверсию - несоответствие списку.
См. [распознавание протоколов](#распознавание-протоколов).
- payload_match_filter проверяет соответствие l7payload списку l7payload_filter или def, если l7payload_filter=nil. Если оба nil - список берется как "known".
- payload_check вызывает `payload_match_filter(desync.l7payload, desync.arg.payload, def)`
@@ -3468,7 +3506,7 @@ nfqws2 ничего не знает о том, что нужно `--lua-desync`
### standard payload
Фильтр по пейлоаду берет список типов пейлоада. Список известных типов пейлоада можно получить из help текста nfqws2. Все пустые пакеты имеют пейлоад empty, неизвестные - unknown. Особые значения - all и known. all означает любой пейлоад, known - не unknown и не empty.
Фильтр по пейлоаду берет список [типов пейлоада](#распознавание-протоколов).
**standard payload**
@@ -3550,6 +3588,7 @@ function http_methodeol(ctx, desync)
- arg: [standard direction](#standard-direction)
Вставляет '\r\n' перед методом, отрезая 2 последних символа из содержимого заголовка `User-Agent:`. Работает только на nginx, остальные сервера ломает.
Если используется совместно с другими функциями http тамперинга, должен идти последним.
### http_unixeol
@@ -3966,7 +4005,7 @@ function udplen(ctx, desync)
- arg: pattern_offset - начальное смещение внутри pattern
- payload фильтр по умолчанию - "known"
Функция увеличивает или уменьшает длину L4 пейлоада udp. При уменьшении часть информации обрезается и теряется, при увеличении - заполняется pattern. Сегментация udp невозможна - при превышении MTU или PMTU пакет не дойдет до адресата. Ошибка в случае превышения MTU будет только на Linux, остальные системы молча не отправят пакет (windivert и ipdivert не имеют средств обнаружения ошибки).
Функция увеличивает или уменьшает длину L4 пейлоада udp. При уменьшении часть информации обрезается и теряется, при увеличении - заполняется pattern. Сегментация udp невозможна - при превышении MTU или PMTU ОС выполнит фрагментацию на уровне IP. Ошибка в случае превышения MTU будет только на Linux, остальные системы молча не отправят пакет (windivert и ipdivert не имеют средств обнаружения ошибки).
### dht_dn
@@ -4048,7 +4087,7 @@ function standard_hostkey(desync)
### automate_host_record
```
function automate_conn_record(desync)
function automate_host_record(desync)
```
- arg: key - ключ внутри глобальной таблицы autostate. если не задано, в качестве ключа используется имя текущего инстанса
@@ -4326,8 +4365,8 @@ ip2net фильтрует входные данные, выкидывая неп
```
--family=<4|6|46> ; выбор семейства IP адресов : ipv4, ipv6, ipv4+ipv6
--threads=<threads_number> ; количество потоков. по умолчанию 1.
--eagain=<eagain_retries> ; количество попыток повтора после EAGAIN. по умолчанию 10
--eagain-delay=<ms> ; время ожидания в мсек между повторами по EAGAIN. по умолчанию 500.
--eagain=<eagain_retries> ; количество попыток повтора после EAI_AGAIN. по умолчанию 10
--eagain-delay=<ms> ; время ожидания в мсек между повторами по EAI_AGAIN. по умолчанию 500.
--verbose ; дебаг-лог на консоль
--stats=N ; выводить статистику каждые N доменов
--log-resolved=<file> ; сохранять успешно отресолвленные домены в файл
@@ -4668,8 +4707,8 @@ nfqws2 может работать и самостоятельно без скр
| IPSET_HOOK | скрипт, который получает имя ipset в $1, выдает в stdout список ip, и они добавляются в ipset |
| IP2NET_OPT4<br>IP2NET_OPT6 | настройки ip2net для скриптов получения ip листов |
| MDIG_THREADS | количество потоков mdig. используется при ресолвинге хостлистов |
| MDIG_EAGAIN | количество попыток при получении EAGAIN |
| MDIG_EAGAIN_DELAY | задержка в мсек между попытками при получении EAGAIN |
| MDIG_EAGAIN | количество попыток при получении EAI_AGAIN |
| MDIG_EAGAIN_DELAY | задержка в мсек между попытками при получении EAI_AGAIN |
| AUTOHOSTLIST_INCOMING_MAXSEQ<br>AUTOHOSTLIST_RETRANS_MAXSEQ<br>AUTOHOSTLIST_RETRANS_THRESHOLD<br>AUTOHOSTLIST_RETRANS_RESET<br>AUTOHOSTLIST_FAIL_THRESHOLD<br>AUTOHOSTLIST_FAIL_TIME<br>AUTOHOSTLIST_UDP_IN<br>AUTOHOSTLIST_UDP_OUT | параметры [автохостлистов](#детектор-неудач-автохостлистов) |
| AUTOHOSTLIST_DEBUGLOG | включение autohostlist debug log. лог пишется в `ipset/zapret-hosts-auto-debug.log` |
| GZIP_LISTS | применять ли сжатие gzip для генерируемых хост и ip листов |

View File

@@ -1,3 +1,7 @@
## English
[Manual](manual.en.md)
## Зачем это нужно
Автономное средство противодействия DPI, которое не требует подключения каких-либо сторонних серверов. Может помочь
@@ -11,11 +15,13 @@ VPN. Может использоваться для частичной проз
[Полный мануал](manual.md)
## Поддержать разработчика
## Поддержать разработчика. Donations
Если вы считаете проект полезным и желаете поддержать разработку, направляйте ваши пожертвования на следующие адреса криптокошельков :
USDT `0x3d52Ce15B7Be734c53fc9526ECbAB8267b63d66E` (предпочительно сеть ERC-20)
If you find this project useful and wish to donate here are crypto wallets :
USDT `0x3d52Ce15B7Be734c53fc9526ECbAB8267b63d66E` (предпочительно сеть ERC-20. ERC-20 preferred)
BTC `bc1qhqew3mrvp47uk2vevt5sctp7p2x9m7m5kkchve`

View File

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

View File

@@ -1,3 +1,3 @@
udp.PayloadLength=148 and udp.Payload[0]=0x01 or
udp.PayloadLength=92 and udp.Payload[0]=0x02 or
udp.PayloadLength=64 and udp.Payload[0]=0x03
udp.PayloadLength=148 and udp.Payload32[0]=0x01000000 or
udp.PayloadLength=92 and udp.Payload32[0]=0x02000000 or
udp.PayloadLength=64 and udp.Payload32[0]=0x03000000

View File

@@ -149,12 +149,17 @@ function http_hostcase(ctx, desync)
error("http_hostcase: invalid host spelling '"..spell.."'")
else
local hdis = http_dissect_req(desync.dis.payload)
if hdis.headers.host then
DLOG("http_hostcase: 'Host:' => '"..spell.."'")
desync.dis.payload = string.sub(desync.dis.payload,1,hdis.headers.host.pos_start-1)..spell..string.sub(desync.dis.payload,hdis.headers.host.pos_header_end+1)
return VERDICT_MODIFY
if hdis then
local idx_host = array_field_search(hdis.headers, "header_low", "host")
if idx_host then
DLOG("http_hostcase: 'Host:' => '"..spell.."'")
desync.dis.payload = string.sub(desync.dis.payload,1,hdis.headers[idx_host].pos_start-1)..spell..string.sub(desync.dis.payload,hdis.headers[idx_host].pos_header_end+1)
return VERDICT_MODIFY
else
DLOG("http_hostcase: 'Host:' header not found")
end
else
DLOG("http_hostcase: 'Host:' header not found")
DLOG("http_hostcase: http dissect error")
end
end
end
@@ -162,6 +167,7 @@ end
-- nfqws1 : "--methodeol"
-- standard args : direction
-- NOTE : if using with other http tampering methodeol should be the last !
function http_methodeol(ctx, desync)
if not desync.dis.tcp then
instance_cutoff_shim(ctx, desync)
@@ -170,17 +176,22 @@ function http_methodeol(ctx, desync)
direction_cutoff_opposite(ctx, desync)
if desync.l7payload=="http_req" and direction_check(desync) then
local hdis = http_dissect_req(desync.dis.payload)
local ua = hdis.headers["user-agent"]
if ua then
if (ua.pos_end - ua.pos_value_start) < 2 then
DLOG("http_methodeol: 'User-Agent:' header is too short")
if hdis then
local idx_ua = array_field_search(hdis.headers, "header_low", "user-agent")
if idx_ua then
local ua = hdis.headers[idx_ua]
if (ua.pos_end - ua.pos_value_start) < 2 then
DLOG("http_methodeol: 'User-Agent:' header is too short")
else
DLOG("http_methodeol: applied")
desync.dis.payload="\r\n"..string.sub(desync.dis.payload,1,ua.pos_end-2)..(string.sub(desync.dis.payload,ua.pos_end+1) or "");
return VERDICT_MODIFY
end
else
DLOG("http_methodeol: applied")
desync.dis.payload="\r\n"..string.sub(desync.dis.payload,1,ua.pos_end-2)..(string.sub(desync.dis.payload,ua.pos_end+1) or "");
return VERDICT_MODIFY
DLOG("http_methodeol: 'User-Agent:' header not found")
end
else
DLOG("http_methodeol: 'User-Agent:' header not found")
DLOG("http_methodeol: http dissect error")
end
end
end
@@ -197,10 +208,11 @@ function http_unixeol(ctx, desync)
if desync.l7payload=="http_req" and direction_check(desync) then
local hdis = http_dissect_req(desync.dis.payload)
if hdis then
if hdis.headers["user-agent"] then
local idx_ua = array_field_search(hdis.headers, "header_low", "user-agent")
if idx_ua then
local http = http_reconstruct_req(hdis, true)
if #http < #desync.dis.payload then
hdis.headers["user-agent"].value = hdis.headers["user-agent"].value .. string.rep(" ", #desync.dis.payload - #http)
hdis.headers[idx_ua].value = hdis.headers[idx_ua].value .. string.rep(" ", #desync.dis.payload - #http)
end
local http = http_reconstruct_req(hdis, true)
if #http==#desync.dis.payload then
@@ -211,7 +223,7 @@ function http_unixeol(ctx, desync)
DLOG("http_unixeol: reconstruct differs in size from original: "..#http.."!="..#desync.dis.payload)
end
else
DLOG("http_unixeol: user-agent header absent")
DLOG("http_unixeol: 'User-Agent:' header absent")
end
else
DLOG("http_unixeol: could not dissect http")

View File

@@ -569,7 +569,6 @@ function array_search(a, v)
return k
end
end
return nil
end
-- linear search array a for a[index].f==v. return index
function array_field_search(a, f, v)
@@ -578,7 +577,6 @@ function array_field_search(a, f, v)
return k
end
end
return nil
end
-- find pos of the next eol and pos of the next non-eol character after eol
@@ -1558,7 +1556,7 @@ function http_dissect_headers(http, pos)
end
header,value,pos_endheader,pos_startvalue = http_dissect_header(header)
if header then
headers[string.lower(header)] = { header = header, value = value, pos_start = pos, pos_end = eol, pos_header_end = pos+pos_endheader-1, pos_value_start = pos+pos_startvalue-1 }
headers[#headers+1] = { header_low = string.lower(header), header = header, value = value, pos_start = pos, pos_end = eol, pos_header_end = pos+pos_endheader-1, pos_value_start = pos+pos_startvalue-1 }
end
pos=pnext
end
@@ -1609,11 +1607,16 @@ function http_dissect_reply(http)
code = tonumber(string.sub(http,10,pos-1))
if not code then return nil end
pos = find_next_line(http,pos)
return { code = code, headers = http_dissect_headers(http,pos) }
local hdis = { code = code }
hdis.headers, hdis.pos_headers_end = http_dissect_headers(http,pos)
if hdis.pos_headers_end then
hdis.body = string.sub(http, hdis.pos_headers_end)
end
return hdis
end
function http_reconstruct_headers(headers, unixeol)
local eol = unixeol and "\n" or "\r\n"
return headers and btable(headers, function(a) return a.header..": "..a.value..eol end) or ""
return headers and barray(headers, function(a) return a.header..": "..a.value..eol end) or ""
end
function http_reconstruct_req(hdis, unixeol)
local eol = unixeol and "\n" or "\r\n"

View File

@@ -455,8 +455,8 @@ static void exithelp(void)
printf(
" --family=<4|6|46>\t\t; ipv4, ipv6, ipv4+ipv6\n"
" --threads=<threads_number>\n"
" --eagain=<eagain_retries>\t; how many times to retry if EAGAIN received. default %u\n"
" --eagain-delay=<ms>\t\t; time in msec to wait between EAGAIN attempts. default %u\n"
" --eagain=<eagain_retries>\t; how many times to retry if EAI_AGAIN received. default %u\n"
" --eagain-delay=<ms>\t\t; time in msec to wait between EAI_AGAIN attempts. default %u\n"
" --verbose\t\t\t; print query progress to stderr\n"
" --stats=N\t\t\t; print resolve stats to stderr every N domains\n"
" --log-resolved=<file>\t\t; log successfully resolved domains to a file\n"

View File

@@ -622,7 +622,7 @@ BOOL LowMandatoryLevel(void)
label_low.Label.Sid = (PSID)buf1;
InitializeSid(label_low.Label.Sid, &label_authority, 1);
label_low.Label.Attributes = 0;
label_low.Label.Attributes = SE_GROUP_INTEGRITY;
*GetSidSubAuthority(label_low.Label.Sid, 0) = SECURITY_MANDATORY_LOW_RID;
// S-1-16-12288 : Mandatory Label\High Mandatory Level
@@ -892,7 +892,7 @@ bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_lis
wlan_filter_ssid = ssid_filter;
return true;
}
bool win_dark_deinit(void)
void win_dark_deinit(void)
{
if (pNetworkListManager)
{
@@ -997,11 +997,12 @@ bool nlm_list(bool bAll)
}
else
bRet = false;
CoUninitialize();
}
else
bRet = false;
CoUninitialize();
return bRet;
}
@@ -1171,8 +1172,11 @@ static HANDLE windivert_init_filter(const char *filter, UINT64 flags)
FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, w_win32_error, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), (LPSTR)&errormessage, 0, NULL);
DLOG_ERR("windivert: error opening filter: %s", errormessage);
LocalFree(errormessage);
if (errormessage)
{
DLOG_ERR("windivert: error opening filter: %s", errormessage);
LocalFree(errormessage);
}
if (w_win32_error == ERROR_INVALID_IMAGE_HASH)
DLOG_ERR("windivert: try to disable secure boot and install OS patches\n");

View File

@@ -94,7 +94,7 @@ bool ensure_dir_access(const char *filename);
bool prepare_low_appdata();
bool win_sandbox(void);
bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter);
bool win_dark_deinit(void);
void win_dark_deinit(void);
bool logical_net_filter_present(void);
bool logical_net_filter_match(void);
bool nlm_list(bool bAll);

View File

@@ -30,7 +30,7 @@ static void protocol_probe(t_protocol_probe *probe, int probe_count, const uint8
{
for (int i = 0; i < probe_count; i++)
{
if ((!probe[i].l7match || *l7proto==probe[i].l7) && probe[i].check(data_payload, len_payload))
if (!l7_payload_match(probe[i].l7p, params.payload_disable) && (!probe[i].l7match || *l7proto==probe[i].l7) && probe[i].check(data_payload, len_payload))
{
*l7payload = probe[i].l7p;
if (*l7proto == L7_UNKNOWN)
@@ -753,7 +753,6 @@ static uint8_t desync(
struct func_list *func;
int ref_arg = LUA_NOREF, status;
bool b, b_cutoff_all, b_unwanted_payload;
t_lua_desync_context ctx = { .magic = 0, .dp = dp, .ctrack = ctrack, .dis = dis, .cancel = false, .incoming = bIncoming };
const char *sDirection = bIncoming ? "in" : "out";
struct packet_range *range;
size_t l;
@@ -782,13 +781,24 @@ static uint8_t desync(
if (LIST_FIRST(&dp->lua_desync))
{
lua_rawgeti(params.L, LUA_REGISTRYINDEX, params.ref_desync_ctx);
t_lua_desync_context *ctx = (t_lua_desync_context *)luaL_checkudata(params.L, 1, "desync_ctx");
// this is singleton stored in the registry. safe to pop
lua_pop(params.L,1);
ctx->dp = dp;
ctx->ctrack = ctrack;
ctx->dis = dis;
ctx->cancel = false;
ctx->incoming = bIncoming;
b_cutoff_all = b_unwanted_payload = true;
ctx.func_n = 1;
ctx->func_n = 1;
LIST_FOREACH(func, &dp->lua_desync, next)
{
ctx.func = func->func;
desync_instance(func->func, dp->n, ctx.func_n, instance, sizeof(instance));
ctx.instance = instance;
ctx->func = func->func;
desync_instance(func->func, dp->n, ctx->func_n, instance, sizeof(instance));
ctx->instance = instance;
range = bIncoming ? &func->range_in : &func->range_out;
if (b_unwanted_payload)
@@ -796,7 +806,7 @@ static uint8_t desync(
if (b_cutoff_all)
{
if (lua_instance_cutoff_check(params.L, &ctx, bIncoming))
if (lua_instance_cutoff_check(params.L, ctx, bIncoming))
DLOG("* lua '%s' : voluntary cutoff\n", instance);
else if (check_pos_cutoff(pos, range))
{
@@ -814,7 +824,7 @@ static uint8_t desync(
else
b_cutoff_all = false;
}
ctx.func_n++;
ctx->func_n++;
}
if (b_cutoff_all)
{
@@ -867,14 +877,14 @@ static uint8_t desync(
}
ref_arg = luaL_ref(params.L, LUA_REGISTRYINDEX);
ctx.func_n = 1;
ctx->func_n = 1;
LIST_FOREACH(func, &dp->lua_desync, next)
{
ctx.func = func->func;
desync_instance(func->func, dp->n, ctx.func_n, instance, sizeof(instance));
ctx.instance = instance;
ctx->func = func->func;
desync_instance(func->func, dp->n, ctx->func_n, instance, sizeof(instance));
ctx->instance = instance;
if (!lua_instance_cutoff_check(params.L, &ctx, bIncoming))
if (!lua_instance_cutoff_check(params.L, ctx, bIncoming))
{
range = bIncoming ? &func->range_in : &func->range_out;
if (check_pos_range(pos, range))
@@ -897,19 +907,17 @@ static uint8_t desync(
DLOG_ERR("desync function '%s' does not exist\n", func->func);
goto err;
}
lua_pushlightuserdata(params.L, &ctx);
lua_rawgeti(params.L, LUA_REGISTRYINDEX, params.ref_desync_ctx);
lua_rawgeti(params.L, LUA_REGISTRYINDEX, ref_arg);
lua_pushf_args(params.L, &func->args, -1, true);
lua_pushf_str(params.L, "func", func->func);
lua_pushf_int(params.L, "func_n", ctx.func_n);
lua_pushf_int(params.L, "func_n", ctx->func_n);
lua_pushf_str(params.L, "func_instance", instance);
// lua should not store and access ctx outside of this call
// if this happens make our best to prevent access to bad memory
// this is not crash-proof but better than nothing
ctx.magic = MAGIC_CTX; // mark struct as valid
// prevent use of desync ctx object outside of function call
ctx->valid = true;
status = lua_pcall(params.L, 2, LUA_MULTRET, 0);
ctx.magic = 0; // mark struct as invalid
ctx->valid = false;
if (status)
{
@@ -939,8 +947,8 @@ static uint8_t desync(
range->upper_cutoff ? '<' : '-',
range->to.mode, range->to.pos);
}
if (ctx.cancel) break;
ctx.func_n++;
if (ctx->cancel) break;
ctx->func_n++;
}
}
@@ -1117,7 +1125,7 @@ static uint8_t dpi_desync_tcp_packet_play(
}
}
// in absence of conntrack guess direction by presence of interface names. won't work on BSD
bReverseFixed = ctrack ? (bReverse ^ params.server) : (bReverse = ifin && ifin && (!ifout || !*ifout));
bReverseFixed = ctrack ? (bReverse ^ params.server) : (bReverse = ifin && *ifin && (!ifout || !*ifout));
setup_direction(dis, bReverseFixed, &src, &dst, &sdip4, &sdip6, &sdport);
ifname = bReverse ? ifin : ifout;
#ifdef HAS_FILTER_SSID
@@ -1272,19 +1280,32 @@ static uint8_t dpi_desync_tcp_packet_play(
process_retrans_fail(ctrack, dis, (struct sockaddr*)&src, ifin);
if (IsHttp(rdata_payload, rlen_payload))
// do not detect payload if reasm is in progress
if (!ctrack_replay || ReasmIsEmpty(&ctrack_replay->reasm_client))
{
DLOG("packet contains HTTP request\n");
l7payload = L7P_HTTP_REQ;
if (l7proto == L7_UNKNOWN)
t_protocol_probe testers[] = {
{L7P_TLS_CLIENT_HELLO,L7_TLS,IsTLSClientHelloPartial},
{L7P_HTTP_REQ,L7_HTTP,IsHttp,false},
{L7P_XMPP_STREAM,L7_XMPP,IsXMPPStream,false},
{L7P_XMPP_STARTTLS,L7_XMPP,IsXMPPStartTLS,false}
};
protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload);
if (l7payload==L7P_UNKNOWN)
{
l7proto = L7_HTTP;
if (ctrack && ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto;
// this is special type. detection requires AES and can be successful only for the first data packet. no reason to AES every packet
if (ctrack && (ctrack->pos.client.seq_last - ctrack->pos.client.seq0)==1)
{
t_protocol_probe testers[] = {
{L7P_MTPROTO_INITIAL,L7_MTPROTO,IsMTProto}
};
protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload);
}
}
}
// we do not reassemble http
reasm_client_cancel(ctrack);
if (l7payload==L7P_HTTP_REQ)
{
bHaveHost = HttpExtractHost(rdata_payload, rlen_payload, host, sizeof(host));
if (!bHaveHost)
{
@@ -1292,21 +1313,17 @@ static uint8_t dpi_desync_tcp_packet_play(
goto pass;
}
}
else if (IsTLSClientHello(rdata_payload, rlen_payload, TLS_PARTIALS_ENABLE))
else if (l7payload==L7P_TLS_CLIENT_HELLO || l7proto==L7_TLS && l7payload==L7P_UNKNOWN && ctrack_replay && !ReasmIsEmpty(&ctrack_replay->reasm_client))
{
bool bReqFull = IsTLSRecordFull(rdata_payload, rlen_payload);
DLOG(bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n");
l7payload = L7P_TLS_CLIENT_HELLO;
if (l7proto == L7_UNKNOWN)
{
l7proto = L7_TLS;
if (ctrack && ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto;
}
bool bReqFull = IsTLSRecordFull(rdata_payload, rlen_payload);
DLOG(bReqFull ? "TLS client hello is FULL\n" : "TLS client hello is PARTIAL\n");
if (bReqFull) TLSDebug(rdata_payload, rlen_payload);
bHaveHost = TLSHelloExtractHost(rdata_payload, rlen_payload, host, sizeof(host), TLS_PARTIALS_ENABLE);
if (ctrack && !(params.reasm_payload_disable && l7_payload_match(l7payload, params.reasm_payload_disable)))
bHaveHost = TLSHelloExtractHost(rdata_payload, rlen_payload, host, sizeof(host), true);
if (ctrack && !l7_payload_match(l7payload, params.reasm_payload_disable))
{
// do not reasm retransmissions
if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_client) && !is_retransmission(&ctrack->pos.client))
@@ -1336,26 +1353,6 @@ static uint8_t dpi_desync_tcp_packet_play(
}
}
}
else if (ctrack && (ctrack->pos.client.seq_last - ctrack->pos.client.seq0)==1 && IsMTProto(dis->data_payload, dis->len_payload))
{
DLOG("packet contains telegram mtproto2 initial\n");
// mtproto detection requires aes. react only on the first tcp data packet. do not detect if ctrack unavailable.
l7payload = L7P_MTPROTO_INITIAL;
if (l7proto == L7_UNKNOWN)
{
l7proto = L7_MTPROTO;
if (ctrack->l7proto == L7_UNKNOWN) ctrack->l7proto = l7proto;
}
}
else
{
t_protocol_probe testers[] = {
{L7P_XMPP_STREAM,L7_XMPP,IsXMPPStream,false},
{L7P_XMPP_STARTTLS,L7_XMPP,IsXMPPStartTLS,false}
};
protocol_probe(testers, sizeof(testers) / sizeof(*testers), dis->data_payload, dis->len_payload, ctrack, &l7proto, &l7payload);
}
}
if (bHaveHost)
@@ -1663,7 +1660,7 @@ static uint8_t dpi_desync_udp_packet_play(
}
}
// in absence of conntrack guess direction by presence of interface names. won't work on BSD
bReverseFixed = ctrack ? (bReverse ^ params.server) : (bReverse = ifin && ifin && (!ifout || !*ifout));
bReverseFixed = ctrack ? (bReverse ^ params.server) : (bReverse = ifin && *ifin && (!ifout || !*ifout));
setup_direction(dis, bReverseFixed, &src, &dst, &sdip4, &sdip6, &sdport);
ifname = bReverse ? ifin : ifout;
@@ -1756,7 +1753,7 @@ static uint8_t dpi_desync_udp_packet_play(
}
if (pclean)
{
bool reasm_disable = params.reasm_payload_disable && l7_payload_match(l7payload, params.reasm_payload_disable);
bool reasm_disable = l7_payload_match(l7payload, params.reasm_payload_disable);
if (ctrack && !reasm_disable && !ReasmIsEmpty(&ctrack->reasm_client))
{
if (ReasmHasSpace(&ctrack->reasm_client, clean_len))
@@ -1818,7 +1815,7 @@ static uint8_t dpi_desync_udp_packet_play(
{
data_decrypt = defrag + hello_offset;
len_decrypt = hello_len;
bHaveHost = TLSHelloExtractHostFromHandshake(data_decrypt, len_decrypt, host, sizeof(host), TLS_PARTIALS_ENABLE);
bHaveHost = TLSHelloExtractHostFromHandshake(data_decrypt, len_decrypt, host, sizeof(host), true);
}
else
{

View File

@@ -31,7 +31,12 @@ int z_readfile(FILE *F, char **buf, size_t *size, size_t extra_alloc)
r = Z_ERRNO;
goto zerr;
}
if (!zs.avail_in) break;
if (!zs.avail_in)
{
// file is not full
r = Z_DATA_ERROR;
goto zerr;
}
zs.next_in = in;
do
{

View File

@@ -396,16 +396,16 @@ void phton64(uint8_t *p, uint64_t v)
p[7] = (uint8_t)v;
}
uint16_t swap16(uint16_t u)
uint16_t bswap16(uint16_t u)
{
// __builtin_bswap16 is absent in ancient lexra gcc 4.6
return (u>>8) | ((u&0xFF)<<8);
}
uint32_t swap24(uint32_t u)
uint32_t bswap24(uint32_t u)
{
return (u>>16) & 0xFF | u & 0xFF00 | (u<<16) & 0xFF0000;
}
uint64_t swap48(uint64_t u)
uint64_t bswap48(uint64_t u)
{
return ((u & 0xFF0000000000) >> 40) | ((u & 0xFF00000000) >> 24) | ((u & 0xFF000000) >> 8) | ((u & 0xFF0000) << 8) | ((u & 0xFF00) << 24) | ((u & 0xFF) << 40);
}

View File

@@ -66,9 +66,9 @@ void phton48(uint8_t *p, uint64_t v);
uint64_t pntoh64(const uint8_t *p);
void phton64(uint8_t *p, uint64_t v);
uint16_t swap16(uint16_t u);
uint32_t swap24(uint32_t u);
uint64_t swap48(uint64_t u);
uint16_t bswap16(uint16_t u);
uint32_t bswap24(uint32_t u);
uint64_t bswap48(uint64_t u);
bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size);
char hex_digit(uint8_t v);

View File

@@ -79,7 +79,7 @@ bool AppendHostList(hostlist_pool **hostlist, const char *filename)
}
else
{
DLOG_ERR("zlib decompression failed : result %d\n",r);
DLOG_ERR("zlib decompression failed : result %d\n", r);
return false;
}
}

View File

@@ -4,7 +4,6 @@
#include "helpers.h"
// inplace tolower() and add to pool
static bool addpool(ipset *ips, char **s, const char *end, int *ct)
{
char *p, cidr[128];

View File

@@ -279,7 +279,7 @@ static int luacall_swap16(lua_State *L)
int64_t i = (int64_t)luaL_checklint(L,1);
if (i>0xFFFF || i<-(int64_t)0xFFFF) luaL_error(L, "out of range");
uint16_t u = (uint16_t)i;
lua_pushinteger(L,swap16(u));
lua_pushinteger(L,bswap16(u));
return 1;
}
static int luacall_swap24(lua_State *L)
@@ -289,7 +289,7 @@ static int luacall_swap24(lua_State *L)
int64_t i =(int64_t)luaL_checklint(L,1);
if (i>0xFFFFFF || i<-(int64_t)0xFFFFFF) luaL_error(L, "out of range");
uint32_t u = (uint32_t)i;
lua_pushlint(L,swap24(u));
lua_pushlint(L,bswap24(u));
return 1;
}
static int luacall_swap32(lua_State *L)
@@ -309,7 +309,7 @@ static int luacall_swap48(lua_State *L)
int64_t i =(int64_t)luaL_checklint(L,1);
if (i>0xFFFFFFFFFFFF || i<-(int64_t)0xFFFFFFFFFFFF) luaL_error(L, "out of range");
uint64_t u = (uint64_t)i;
lua_pushlint(L, swap48(u));
lua_pushlint(L, bswap48(u));
return 1;
}
static int lua_uxadd(lua_State *L, int64_t max)
@@ -769,20 +769,41 @@ static int luacall_clock_gettime(lua_State *L)
LUA_STACK_GUARD_RETURN(L,2)
}
static void lua_mt_init_desync_ctx(lua_State *L)
{
luaL_newmetatable(L, "desync_ctx");
lua_pop(L, 1);
}
static t_lua_desync_context *lua_desync_ctx(lua_State *L)
{
if (lua_isnil(L,1))
luaL_error(L, "missing ctx");
if (!lua_islightuserdata(L,1))
luaL_error(L, "bad ctx - invalid data type");
t_lua_desync_context *ctx = lua_touserdata(L,1);
// ensure it's really ctx. LUA could pass us any lightuserdata pointer
if (ctx->magic!=MAGIC_CTX)
luaL_error(L, "bad ctx - magic bytes invalid");
if (lua_isnil(L,1)) luaL_error(L, "missing ctx");
t_lua_desync_context *ctx = (t_lua_desync_context *)luaL_checkudata(L, 1, "desync_ctx");
if (!ctx->valid) luaL_error(L, "ctx is invalid");
return ctx;
}
static void lua_desync_ctx_create(lua_State *L)
{
if (!params.ref_desync_ctx)
{
LUA_STACK_GUARD_ENTER(L)
t_lua_desync_context *ctx = (t_lua_desync_context *)lua_newuserdata(L, sizeof(t_lua_desync_context));
memset(ctx, 0, sizeof(*ctx));
luaL_getmetatable(L, "desync_ctx");
lua_setmetatable(L, -2);
params.ref_desync_ctx = luaL_ref(params.L, LUA_REGISTRYINDEX);
LUA_STACK_GUARD_LEAVE(L,0)
}
}
static void lua_desync_ctx_destroy(lua_State *L)
{
if (params.ref_desync_ctx)
{
luaL_unref(L, LUA_REGISTRYINDEX, params.ref_desync_ctx);
params.ref_desync_ctx = 0;
}
}
static int luacall_instance_cutoff(lua_State *L)
{
@@ -911,24 +932,55 @@ static int luacall_execution_plan(lua_State *L)
lua_newtable(L);
struct func_list *func;
char instance[256], pls[2048];
char instance[256], plsl[2048];
struct packet_range *range;
unsigned int n=1;
t_l7payload pl;
const char *pls;
LIST_FOREACH(func, &ctx->dp->lua_desync, next)
{
if (n > ctx->func_n)
{
desync_instance(func->func, ctx->dp->n, n, instance, sizeof(instance));
range = ctx->incoming ? &func->range_in : &func->range_out;
lua_pushinteger(L, n - ctx->func_n);
lua_createtable(L, 0, 6);
lua_createtable(L, 0, 7);
lua_pushf_args(L,&func->args, -1, false);
lua_pushf_str(L,"func", func->func);
lua_pushf_int(L,"func_n", ctx->func_n);
lua_pushf_str(L,"func_instance", instance);
lua_pushf_range(L,"range", range);
if (l7_payload_str_list(func->payload_type, pls, sizeof(pls)))
lua_pushf_str(L,"payload_filter", pls);
lua_pushstring(L, "payload");
lua_newtable(L);
if (func->payload_type==L7P_ALL)
{
lua_pushliteral(L,"all");
lua_pushboolean(L,true);
lua_rawset(L,-3);
}
else
{
for (pl=0 ; pl<L7P_LAST ; pl++)
{
if (func->payload_type & (1<<pl))
{
if ((pls = l7payload_str(pl)))
{
lua_pushstring(L,pls);
lua_pushboolean(L,true);
lua_rawset(L,-3);
}
}
}
}
lua_rawset(L,-3);
if (l7_payload_str_list(func->payload_type, plsl, sizeof(plsl)))
lua_pushf_str(L,"payload_filter", plsl);
else
lua_pushf_nil(L,"payload_filter");
@@ -2159,8 +2211,9 @@ static int luacall_reconstruct_dissect(lua_State *L)
LUA_STACK_GUARD_ENTER(L)
size_t l;
uint8_t buf[RECONSTRUCT_MAX_SIZE];
size_t l = sizeof(buf);
l = sizeof(buf);
bool ip6_preserve_next, badsum;
lua_reconstruct_extract_options(L, 2, &badsum, &ip6_preserve_next, NULL);
@@ -2412,13 +2465,14 @@ static int luacall_rawsend_dissect(lua_State *L)
LUA_STACK_GUARD_ENTER(L)
uint8_t buf[RECONSTRUCT_MAX_SIZE];
size_t len=sizeof(buf);
size_t len;
const char *ifout;
int repeats;
uint32_t fwmark;
sockaddr_in46 sa;
bool b, badsum, ip6_preserve_next;
uint8_t buf[RECONSTRUCT_MAX_SIZE];
len = sizeof(buf);
luaL_checktype(L,1,LUA_TTABLE);
lua_rawsend_extract_options(L,2, &repeats, &fwmark, &ifout);
@@ -2844,14 +2898,19 @@ zerr:
// ----------------------------------------
void lua_cleanup(lua_State *L)
{
lua_desync_ctx_destroy(L);
// conntrack holds lua state. must clear it before lua shoudown
ConntrackPoolDestroy(&params.conntrack);
}
void lua_shutdown()
{
if (params.L)
{
DLOG("LUA SHUTDOWN\n");
// conntrack holds lua state. must clear it before lua shoudown
ConntrackPoolDestroy(&params.conntrack);
lua_cleanup(params.L);
lua_close(params.L);
params.L=NULL;
}
@@ -3031,7 +3090,7 @@ static int luaL_doZfile(lua_State *L, const char *filename)
return r;
}
else
return luaL_dofile(L, filename);
return luaL_dofile(L, fname);
}
static bool lua_init_scripts(void)
@@ -3394,6 +3453,8 @@ static void lua_init_functions(void)
static void lua_init_mt()
{
lua_mt_init_zstream(params.L);
lua_mt_init_desync_ctx(params.L);
lua_desync_ctx_create(params.L);
}
bool lua_init(void)
@@ -3401,6 +3462,9 @@ bool lua_init(void)
DLOG("\nLUA INIT\n");
if (!lua_basic_init()) return false;
LUA_STACK_GUARD_ENTER(params.L)
lua_sec_harden();
lua_init_blobs();
lua_init_const();
@@ -3409,10 +3473,11 @@ bool lua_init(void)
if (!lua_init_scripts()) goto err;
if (!lua_desync_functions_exist()) goto err;
LUA_STACK_GUARD_LEAVE(params.L,0)
DLOG("LUA INIT DONE\n\n");
return true;
err:
LUA_STACK_GUARD_LEAVE(params.L,0)
lua_shutdown();
return false;
}

View File

@@ -101,15 +101,14 @@ bool lua_reconstruct_tcphdr(lua_State *L, int idx, struct tcphdr *tcp, size_t *l
bool lua_reconstruct_udphdr(lua_State *L, int idx, struct udphdr *udp);
bool lua_reconstruct_dissect(lua_State *L, int idx, uint8_t *buf, size_t *len, bool badsum, bool ip6_preserve_next);
#define MAGIC_CTX 0xE73DC935
typedef struct {
uint32_t magic;
unsigned int func_n;
const char *func, *instance;
const struct desync_profile *dp;
const struct dissect *dis;
t_ctrack *ctrack;
bool incoming,cancel;
bool incoming, cancel;
bool valid;
} t_lua_desync_context;
bool lua_instance_cutoff_check(lua_State *L, const t_lua_desync_context *ctx, bool bIn);

View File

@@ -52,9 +52,7 @@
struct params_s params;
static volatile sig_atomic_t bReload = false;
#ifdef __CYGWIN__
bool bQuit = false;
#endif
static void onhup(int sig)
{
@@ -102,12 +100,37 @@ static void onusr2(int sig)
ipcachePrint(&params.ipcache);
printf("\n");
}
static void onint(int sig)
{
const char *msg = "INT received !\n";
size_t wr = write(1, msg, strlen(msg));
bQuit = true;
}
static void onterm(int sig)
{
const char *msg = "TERM received !\n";
size_t wr = write(1, msg, strlen(msg));
bQuit = true;
}
static void pre_desync(void)
{
signal(SIGHUP, onhup);
signal(SIGUSR1, onusr1);
signal(SIGUSR2, onusr2);
struct sigaction sa;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = NULL;
sa.sa_flags = 0;
sa.sa_handler = onhup;
sigaction(SIGHUP, &sa, NULL);
sa.sa_handler = onusr1;
sigaction(SIGUSR1, &sa, NULL);
sa.sa_handler = onusr2;
sigaction(SIGUSR2, &sa, NULL);
sa.sa_handler = onint;
sigaction(SIGINT, &sa, NULL);
sa.sa_handler = onterm;
sigaction(SIGTERM, &sa, NULL);
}
@@ -156,13 +179,14 @@ static int nfq_cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, struct nfq_da
uint8_t *data;
uint32_t ifidx_out, ifidx_in;
char ifout[IFNAMSIZ], ifin[IFNAMSIZ];
uint8_t mod[RECONSTRUCT_MAX_SIZE];
size_t modlen;
uint32_t mark;
uint8_t mod[RECONSTRUCT_MAX_SIZE];
ph = nfq_get_msg_packet_hdr(nfa);
id = ph ? ntohl(ph->packet_id) : 0;
uint32_t mark = nfq_get_nfmark(nfa);
mark = nfq_get_nfmark(nfa);
ilen = nfq_get_payload(nfa, &data);
ifidx_out = nfq_get_outdev(nfa);
@@ -282,12 +306,12 @@ static void notify_ready(void)
static int nfq_main(void)
{
uint8_t buf[RECONSTRUCT_MAX_SIZE] __attribute__((aligned));
struct nfq_handle *h = NULL;
struct nfq_q_handle *qh = NULL;
int res, fd, e;
ssize_t rd;
FILE *Fpid = NULL;
uint8_t buf[RECONSTRUCT_MAX_SIZE] __attribute__((aligned));
if (*params.pidfile && !(Fpid = fopen(params.pidfile, "w")))
{
@@ -348,6 +372,7 @@ static int nfq_main(void)
{
while ((rd = recv(fd, buf, sizeof(buf), 0)) >= 0)
{
if (bQuit) goto quit;
ReloadCheck();
lua_do_gc();
#ifdef HAS_FILTER_SSID
@@ -363,6 +388,11 @@ static int nfq_main(void)
else
DLOG("recv from nfq returned 0 !\n");
}
if (errno==EINTR)
{
if (bQuit) goto quit;
continue;
}
e = errno;
DLOG_ERR("recv: recv=%zd errno %d\n", rd, e);
errno = e;
@@ -370,6 +400,7 @@ static int nfq_main(void)
// do not fail on ENOBUFS
} while (e == ENOBUFS);
exok:
res=0;
ex:
nfq_deinit(&h, &qh);
@@ -383,13 +414,15 @@ err:
if (Fpid) fclose(Fpid);
res=1;
goto ex;
quit:
DLOG_CONDUP("quit requested\n");
goto exok;
}
#elif defined(BSD)
static int dvt_main(void)
{
uint8_t buf[RECONSTRUCT_MAX_SIZE] __attribute__((aligned));
struct sockaddr_storage sa_from;
int fd[2] = { -1,-1 }; // 4,6
int i, r, res = 1, fdct = 1, fdmax;
@@ -398,6 +431,9 @@ static int dvt_main(void)
ssize_t rd, wr;
fd_set fdset;
FILE *Fpid = NULL;
struct sockaddr_in bp4;
struct sockaddr_in6 bp6;
uint8_t buf[RECONSTRUCT_MAX_SIZE] __attribute__((aligned));
if (*params.pidfile && !(Fpid = fopen(params.pidfile, "w")))
{
@@ -405,49 +441,42 @@ static int dvt_main(void)
return 1;
}
bp4.sin_family = AF_INET;
bp4.sin_port = htons(params.port);
bp4.sin_addr.s_addr = INADDR_ANY;
DLOG_CONDUP("creating divert4 socket\n");
fd[0] = socket_divert(AF_INET);
if (fd[0] == -1) {
DLOG_PERROR("socket (DIVERT4)");
goto exiterr;
}
DLOG_CONDUP("binding divert4 socket\n");
if (bind(fd[0], (struct sockaddr*)&bp4, sizeof(bp4)) < 0)
{
struct sockaddr_in bp4;
bp4.sin_family = AF_INET;
bp4.sin_port = htons(params.port);
bp4.sin_addr.s_addr = INADDR_ANY;
DLOG_CONDUP("creating divert4 socket\n");
fd[0] = socket_divert(AF_INET);
if (fd[0] == -1) {
DLOG_PERROR("socket (DIVERT4)");
goto exiterr;
}
DLOG_CONDUP("binding divert4 socket\n");
if (bind(fd[0], (struct sockaddr*)&bp4, sizeof(bp4)) < 0)
{
DLOG_PERROR("bind (DIVERT4)");
goto exiterr;
}
DLOG_PERROR("bind (DIVERT4)");
goto exiterr;
}
#ifdef __OpenBSD__
{
// in OpenBSD must use separate divert sockets for ipv4 and ipv6
struct sockaddr_in6 bp6;
memset(&bp6, 0, sizeof(bp6));
bp6.sin6_family = AF_INET6;
bp6.sin6_port = htons(params.port);
// in OpenBSD must use separate divert sockets for ipv4 and ipv6
memset(&bp6, 0, sizeof(bp6));
bp6.sin6_family = AF_INET6;
bp6.sin6_port = htons(params.port);
DLOG_CONDUP("creating divert6 socket\n");
fd[1] = socket_divert(AF_INET6);
if (fd[1] == -1) {
DLOG_PERROR("socket (DIVERT6)");
goto exiterr;
}
DLOG_CONDUP("binding divert6 socket\n");
if (bind(fd[1], (struct sockaddr*)&bp6, sizeof(bp6)) < 0)
{
DLOG_PERROR("bind (DIVERT6)");
goto exiterr;
}
fdct++;
DLOG_CONDUP("creating divert6 socket\n");
fd[1] = socket_divert(AF_INET6);
if (fd[1] == -1) {
DLOG_PERROR("socket (DIVERT6)");
goto exiterr;
}
DLOG_CONDUP("binding divert6 socket\n");
if (bind(fd[1], (struct sockaddr*)&bp6, sizeof(bp6)) < 0)
{
DLOG_PERROR("bind (DIVERT6)");
goto exiterr;
}
fdct++;
#endif
fdmax = (fd[0] > fd[1] ? fd[0] : fd[1]) + 1;
@@ -487,13 +516,14 @@ static int dvt_main(void)
FD_ZERO(&fdset);
for (i = 0; i < fdct; i++) FD_SET(fd[i], &fdset);
r = select(fdmax, &fdset, NULL, NULL, NULL);
if (bQuit)
{
DLOG_CONDUP("quit requested\n");
goto exitok;
}
if (r == -1)
{
if (errno == EINTR)
{
// a signal received
continue;
}
if (errno == EINTR) continue;
DLOG_PERROR("select");
goto exiterr;
}
@@ -571,6 +601,7 @@ static int dvt_main(void)
}
}
exitok:
res = 0;
exiterr:
if (Fpid) fclose(Fpid);
@@ -590,11 +621,11 @@ static int win_main()
unsigned int id;
uint8_t verdict;
bool bOutbound;
uint8_t packet[RECONSTRUCT_MAX_SIZE];
uint32_t mark;
WINDIVERT_ADDRESS wa;
char ifname[IFNAMSIZ];
int res=0;
uint8_t packet[RECONSTRUCT_MAX_SIZE];
if (params.daemon) daemonize();
@@ -621,7 +652,7 @@ static int win_main()
{
if (bQuit)
{
DLOG("QUIT requested\n");
DLOG("quit requested\n");
goto ex;
}
usleep(500000);
@@ -666,7 +697,7 @@ static int win_main()
}
else if (errno == EINTR)
{
DLOG("QUIT requested\n");
DLOG("quit requested\n");
goto ex;
}
DLOG_ERR("windivert: recv failed. errno %d\n", errno);
@@ -1340,7 +1371,7 @@ static void exithelp(void)
*all_payloads=0;
for (t_l7payload pl=0 ; pl<L7P_LAST; pl++)
{
if (pl) strncat(all_payloads, " ", sizeof(all_payloads)-1-1);
if (pl) strncat(all_payloads, " ", sizeof(all_payloads)-strlen(all_payloads)-1);
strncat(all_payloads, l7payload_str(pl), sizeof(all_payloads)-strlen(all_payloads)-1);
}
*all_protos=0;
@@ -1382,10 +1413,11 @@ static void exithelp(void)
#endif
" --ctrack-timeouts=S:E:F[:U]\t\t\t\t; internal conntrack timeouts for TCP SYN, ESTABLISHED, FIN stages, UDP timeout. default %u:%u:%u:%u\n"
" --ctrack-disable=[0|1]\t\t\t\t\t; 1 or no argument disables conntrack\n"
" --payload-disable=[type[,type]]\t\t\t; do not discover these payload types. for available payload types see '--payload'. disable all if no argument.\n"
" --server=[0|1]\t\t\t\t\t\t; change multiple aspects of src/dst ip/port handling for incoming connections\n"
" --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=[proto[,proto]]\t\t\t; disable reasm for these L7 payloads : tls_client_hello quic_initial . if no argument - disable all reasm.\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"
#ifdef __CYGWIN__
"\nWINDIVERT FILTER:\n"
" --wf-iface=<int>[.<int>]\t\t\t\t; numeric network interface and subinterface indexes\n"
@@ -1527,6 +1559,7 @@ enum opt_indices {
#endif
IDX_CTRACK_TIMEOUTS,
IDX_CTRACK_DISABLE,
IDX_PAYLOAD_DISABLE,
IDX_SERVER,
IDX_IPCACHE_LIFETIME,
IDX_IPCACHE_HOSTNAME,
@@ -1620,6 +1653,7 @@ static const struct option long_options[] = {
#endif
[IDX_CTRACK_TIMEOUTS] = {"ctrack-timeouts", required_argument, 0, 0},
[IDX_CTRACK_DISABLE] = {"ctrack-disable", optional_argument, 0, 0},
[IDX_PAYLOAD_DISABLE] = {"payload-disable", optional_argument, 0, 0},
[IDX_SERVER] = {"server", optional_argument, 0, 0},
[IDX_IPCACHE_LIFETIME] = {"ipcache-lifetime", required_argument, 0, 0},
[IDX_IPCACHE_HOSTNAME] = {"ipcache-hostname", optional_argument, 0, 0},
@@ -1949,6 +1983,18 @@ int main(int argc, char **argv)
case IDX_IPCACHE_HOSTNAME:
params.cache_hostname = !optarg || atoi(optarg);
break;
case IDX_PAYLOAD_DISABLE:
if (optarg)
{
if (!parse_l7p_list(optarg, &params.payload_disable))
{
DLOG_ERR("Invalid payload filter : %s\n", optarg);
exit_clean(1);
}
}
else
params.payload_disable = L7P_ALL;
break;
case IDX_REASM_DISABLE:
if (optarg)
{
@@ -1959,7 +2005,7 @@ int main(int argc, char **argv)
}
}
else
params.reasm_payload_disable = 0xFFFFFFFFFFFFFFFF;
params.reasm_payload_disable = L7P_ALL;
break;
#if defined(__linux__)
case IDX_FWMARK:
@@ -2640,7 +2686,8 @@ int main(int argc, char **argv)
HANDLE hMutexArg;
{
char mutex_name[128];
snprintf(mutex_name, sizeof(mutex_name), "Global\\winws2_arg_%u_%u_%u_%u_%u_%u_%u_%u_%u_%u", hash_wf_tcp_in, hash_wf_udp_in, hash_wf_tcp_out, hash_wf_udp_out, hash_wf_raw, hash_wf_raw_part, hash_ssid_filter, hash_nlm_filter, IfIdx, SubIfIdx, wf_ipv4, wf_ipv6);
snprintf(mutex_name, sizeof(mutex_name), "Global\\winws2_arg_%u_%u_%u_%u_%u_%u_%u_%u_%u_%u_%u_%u",
hash_wf_tcp_in, hash_wf_udp_in, hash_wf_tcp_out, hash_wf_udp_out, hash_wf_raw, hash_wf_raw_part, hash_ssid_filter, hash_nlm_filter, IfIdx, SubIfIdx, wf_ipv4, wf_ipv6);
hMutexArg = CreateMutexA(NULL, TRUE, mutex_name);
if (hMutexArg && GetLastError() == ERROR_ALREADY_EXISTS)

View File

@@ -498,6 +498,7 @@ void cleanup_params(struct params_s *params)
ipcacheDestroy(&params->ipcache);
blob_collection_destroy(&params->blobs);
strlist_destroy(&params->lua_init_scripts);
#ifdef __CYGWIN__
strlist_destroy(&params->ssid_filter);
strlist_destroy(&params->nlm_filter);
@@ -531,6 +532,8 @@ void init_params(struct params_s *params)
LIST_INIT(&params->blobs);
LIST_INIT(&params->lua_init_scripts);
params->reasm_payload_disable = params->payload_disable = 1<<L7P_NONE;
#ifdef __CYGWIN__
LIST_INIT(&params->ssid_filter);
LIST_INIT(&params->nlm_filter);

View File

@@ -21,8 +21,6 @@
#include <wordexp.h>
#endif
#define TLS_PARTIALS_ENABLE true
#define RAW_SNDBUF (64*1024) // in bytes
#define Q_MAXLEN 1024 // in packets
@@ -45,7 +43,7 @@
// this MSS is used for ipv6 in windows and linux
#define DEFAULT_MSS 1220
#define RECONSTRUCT_MAX_SIZE 16384
#define RECONSTRUCT_MAX_SIZE 65536
#define LUA_GC_INTERVAL 60
@@ -176,12 +174,14 @@ struct params_s
unsigned int ipcache_lifetime;
ip_cache ipcache;
uint64_t reasm_payload_disable;
uint64_t payload_disable;
struct str_list_head lua_init_scripts;
bool writeable_dir_enable;
char writeable_dir[PATH_MAX];
int lua_gc;
int ref_desync_ctx; // desync ctx userdata registry ref
lua_State *L;
};

View File

@@ -675,7 +675,7 @@ ssize_t TLSPos(t_marker posmarker, int16_t pos, const uint8_t *data, size_t sz)
case PM_HOST_MIDSLD:
case PM_HOST_ENDSLD:
case PM_SNI_EXT:
if (TLSFindExt(data,sz,0,&ext,&elen,TLS_PARTIALS_ENABLE))
if (TLSFindExt(data,sz,0,&ext,&elen,true))
{
if (posmarker==PM_SNI_EXT)
{
@@ -989,24 +989,18 @@ bool IsQUICCryptoHello(const uint8_t *data, size_t len, size_t *hello_offset, si
uint8_t QUICDraftVersion(uint32_t version)
{
/* IETF Draft versions */
if ((version >> 8) == 0xff0000) {
if ((version >> 8) == 0xff0000)
return (uint8_t)version;
}
/* Facebook mvfst, based on draft -22. */
if (version == 0xfaceb001) {
if (version == 0xfaceb001)
return 22;
}
/* Facebook mvfst, based on draft -27. */
if (version == 0xfaceb002 || version == 0xfaceb00e) {
if (version == 0xfaceb002 || version == 0xfaceb00e)
return 27;
}
/* GQUIC Q050, T050 and T051: they are not really based on any drafts,
* but we must return a sensible value */
if (version == 0x51303530 ||
version == 0x54303530 ||
version == 0x54303531) {
if (version == 0x51303530 || version == 0x54303530 || version == 0x54303531)
return 27;
}
/* https://tools.ietf.org/html/draft-ietf-quic-transport-32#section-15
"Versions that follow the pattern 0x?a?a?a?a are reserved for use in
forcing version negotiation to be exercised"
@@ -1014,19 +1008,17 @@ uint8_t QUICDraftVersion(uint32_t version)
used to select a proper salt (which depends on the version itself), but
we don't have a real version here! Let's hope that we need to handle
only latest drafts... */
if ((version & 0x0F0F0F0F) == 0x0a0a0a0a) {
if ((version & 0x0F0F0F0F) == 0x0a0a0a0a)
return 29;
}
/* QUIC (final?) constants for v1 are defined in draft-33, but draft-34 is the
final draft version */
if (version == 0x00000001) {
if (version == 0x00000001)
return 34;
}
/* QUIC Version 2 */
/* TODO: for the time being use 100 as a number for V2 and let see how v2 drafts evolve */
if (version == 0x709A50C4) {
if ((version == 0x709A50C4) || (version == 0x6b3343cf))
return 100;
}
return 0;
}
@@ -1036,7 +1028,7 @@ static bool is_quic_draft_max(uint32_t draft_version, uint8_t max_version)
}
static bool is_quic_v2(uint32_t version)
{
return version == 0x6b3343cf;
return (version == 0x709A50C4) || (version == 0x6b3343cf);
}
static bool quic_hkdf_expand_label(const uint8_t *secret, uint8_t secret_len, const char *label, uint8_t *out, size_t out_len)
@@ -1386,24 +1378,24 @@ bool IsDNSResponse(const uint8_t *data, size_t len)
}
bool IsWireguardHandshakeInitiation(const uint8_t *data, size_t len)
{
return len==148 && data[0]==1;
return len==148 && pntoh32(data)==0x01000000;
}
bool IsWireguardHandshakeResponse(const uint8_t *data, size_t len)
{
return len==92 && data[0]==2;
return len==92 && pntoh32(data)==0x02000000;
}
bool IsWireguardHandshakeCookie(const uint8_t *data, size_t len)
{
return len==64 && data[0]==3;
return len==64 && pntoh32(data)==0x03000000;
}
bool IsWireguardData(const uint8_t *data, size_t len)
{
// 16 bytes wg header + min 20 bytes for ipv4 encrypted header + 16 byte auth tag
return len>=52 && data[0]==4;
return len>=52 && pntoh32(data)==0x04000000;
}
bool IsWireguardKeepalive(const uint8_t *data, size_t len)
{
return len==32 && data[0]==4;
return len==32 && pntoh32(data)==0x04000000;
}
bool IsDht(const uint8_t *data, size_t len)
{

View File

@@ -20,7 +20,7 @@ typedef enum {
L7_XMPP,
L7_DNS,
L7_MTPROTO,
L7_LAST, L7_INVALID=L7_LAST
L7_LAST, L7_INVALID=L7_LAST, L7_NONE=L7_LAST
} t_l7proto;
const char *l7proto_str(t_l7proto l7);
t_l7proto l7proto_from_name(const char *name);
@@ -53,7 +53,7 @@ typedef enum {
L7P_DNS_QUERY,
L7P_DNS_RESPONSE,
L7P_MTPROTO_INITIAL,
L7P_LAST, L7P_INVALID=L7P_LAST
L7P_LAST, L7P_INVALID=L7P_LAST, L7P_NONE=L7P_LAST
} t_l7payload;
t_l7payload l7payload_from_name(const char *name);
const char *l7payload_str(t_l7payload l7);