44 Commits
v70.3 ... v70.5

Author SHA1 Message Date
bol-van
a2ffa3455d nfqws: minor beautify text 2025-03-24 11:20:51 +03:00
bol-van
60b97dbed0 nfqws: remove debug printfs 2025-03-24 11:14:38 +03:00
bol-van
e56e4f5f35 update changes 2025-03-24 10:32:02 +03:00
bol-van
5305ea83c8 fakes: GGC kyber with inter-packet CRYPTO frag 2025-03-24 09:44:50 +03:00
bol-van
14b3dd459b nfqws: define reasm buffer sizes 2025-03-24 09:34:37 +03:00
bol-van
66fda2c33d nfqws: support QUIC multi packet CRYPTO fragmentation 2025-03-23 23:29:16 +03:00
bol-van
77df43b9cb nfqws: minor optimize 2025-03-22 13:03:31 +03:00
bol-van
85f2b37c88 update docs 2025-03-21 21:00:47 +03:00
bol-van
e2d600fcc6 update docs 2025-03-21 20:58:53 +03:00
bol-van
37eda0ad98 nfqws: mod skipped DLOG_ERR -> DLOG 2025-03-21 19:40:25 +03:00
bol-van
770be21e1c nfqws: fix custom tls fake fallback logic 2025-03-21 19:09:37 +03:00
bol-van
1b880d42f9 nfqws,tpws: missing va_end 2025-03-21 17:33:57 +03:00
bol-van
6387315c0b nfqws: multiple fakes 2025-03-21 17:12:36 +03:00
bol-van
3d4b395bfe ignore windivert files in nfq 2025-03-21 14:23:19 +03:00
bol-van
55950ed7d0 remove bad files 2025-03-21 14:22:28 +03:00
bol-van
f2b0341484 blockcheck: add dupsid to tls-mod 2025-03-20 18:14:35 +03:00
bol-van
b2d89c5d22 blockcheck: warn MTU overflow with md5sig 2025-03-17 09:39:33 +03:00
bol-van
778b611f86 readme: md5sig with fakedsplit/fakeddisorder warning 2025-03-16 17:06:24 +03:00
bol-van
ffaf91c251 nfqws: debug packet length in sendto() 2025-03-16 15:56:47 +03:00
bol-van
326b42fafd nfqws: debug packet length in sendto() 2025-03-16 15:52:50 +03:00
bol-van
94d4238af2 update changes.txt 2025-03-14 11:41:52 +03:00
bol-van
15e22fa1bd tpws: detect WSL 1 and warn about non-working options 2025-03-14 11:38:52 +03:00
bol-van
bd8decddc5 nfqws,tpws: separate droproot from dropcaps 2025-03-13 21:54:28 +03:00
bol-van
2db1ebafe3 tpws systemd unit fix comment 2025-03-12 18:14:43 +03:00
bol-van
33bcf6f7b4 systemd improve cheat sheet 2025-03-12 17:47:17 +03:00
bol-van
f037f1acb2 update docs 2025-03-12 17:45:19 +03:00
bol-van
cdd9b32b27 update docs 2025-03-12 17:44:04 +03:00
bol-van
7934125c09 init.d: systemd unit examples for tpws and nfqws 2025-03-12 17:27:55 +03:00
bol-van
6493d55977 tpws: move systemd notify deeper 2025-03-12 14:45:52 +03:00
bol-van
cafbb17e70 install_easy: make systemd if systemd detected 2025-03-12 14:32:00 +03:00
bol-van
9ac73f7d2f readme.eng: hostlist ^ note 2025-03-12 13:07:29 +03:00
bol-van
08a6e8e069 nfqws,tpws : rename function 2025-03-12 13:03:33 +03:00
bol-van
644a934099 nfqws.service : add special compile warning 2025-03-12 12:58:36 +03:00
bol-van
0eec445af0 Merge pull request #1237 from tie/systemd
nfqws: add support for systemd readiness notifications
2025-03-12 12:50:02 +03:00
bol-van
b8acc1b979 Revert "Revert "Revert "nfqws,tpws: fflush stdin,stdout"""
This reverts commit 123eb057ae.
2025-03-12 12:49:06 +03:00
bol-van
123eb057ae Revert "Revert "nfqws,tpws: fflush stdin,stdout""
This reverts commit 56d06456fb.
2025-03-12 12:48:32 +03:00
bol-van
56d06456fb Revert "nfqws,tpws: fflush stdin,stdout"
This reverts commit a6efe05aa6.
2025-03-12 12:48:18 +03:00
bol-van
a6efe05aa6 nfqws,tpws: fflush stdin,stdout 2025-03-12 12:13:53 +03:00
Ivan Trubach
a1d29b0c3a nfqws,tpws: always use line buffering for console IO
Forces stdout and stderr to always use line buffering. Note that glibc
does automatically flush on newline iff connected to a terminal, but
that is not the case when running under systemd. See also
https://www.gnu.org/software/libc/manual/html_node/Buffering-Concepts.html
https://www.gnu.org/software/libc/manual/html_node/Controlling-Buffering.html
2025-03-12 12:00:28 +03:00
Ivan Trubach
756603338b nfqws,tpws: add support for systemd readiness notifications 2025-03-12 11:39:03 +03:00
bol-van
8b73e2ea8e update docs 2025-03-06 16:07:44 +03:00
bol-van
2a0e952153 update docs 2025-03-06 16:06:55 +03:00
bol-van
1065202349 nfqws,tpws: ^ prefix in hostlist disables subdomain matches 2025-03-06 15:13:40 +03:00
bol-van
307d38f6af readme: update ver 2025-02-26 10:04:26 +03:00
40 changed files with 993 additions and 341 deletions

1
.gitignore vendored
View File

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

View File

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

View File

@@ -1145,7 +1145,10 @@ test_has_fake()
warn_fool()
{
case "$1" in
md5sig) echo 'WARNING ! although md5sig fooling worked it will not work on all sites. it typically works only on linux servers.' ;;
md5sig) echo 'WARNING ! although md5sig fooling worked it will not work on all sites. it typically works only on linux servers.'
[ "$2" = "fakedsplit" -o "$2" = "fakeddisorder" ] && \
echo "WARNING ! fakedsplit/fakeddisorder with md5sig fooling and low split position causes MTU overflow with multi-segment TLS (kyber)"
;;
datanoack) echo 'WARNING ! although datanoack fooling worked it may break NAT and may only work with external IP. Additionally it may require nftables to work correctly.' ;;
esac
}
@@ -1165,7 +1168,7 @@ pktws_curl_test_update_vary()
[ "$sec" = 0 ] || proto=tls
test_has_fake $desync && {
zerofake="--dpi-desync-fake-$proto=0x00000000"
[ "$sec" = 0 ] || tlsmod="--dpi-desync-fake-tls-mod=rnd,rndsni,padencap"
[ "$sec" = 0 ] || tlsmod="--dpi-desync-fake-tls-mod=rnd,dupsid,rndsni,padencap"
}
if test_has_fakedsplit $desync ; then
splits="method+2 midsld"
@@ -1272,7 +1275,7 @@ pktws_check_domain_http_bypass_()
[ "$IPV" = 6 ] && f="$f hopbyhop hopbyhop2"
for fooling in $f; do
pktws_curl_test_update_vary $1 $2 $3 $desync --dpi-desync-fooling=$fooling $e && {
warn_fool $fooling
warn_fool $fooling $desync
[ "$SCANLEVEL" = quick ] && return
need_wssize=0
}

View File

@@ -453,8 +453,21 @@ nfqws,tpws: hostlist/ipset track not only file mod time but also file size
nfqws,tpws,ipset: return lists reload on HUP
nfqws,blockcheck: --dpi-desync-fake-tls-mod
v70.3
v70.1
nfqws: --dpi-desync-fake-tls-mod=dupsid
nfqws,tpws: test accessibility of list files after privs drop
nfqws,tpws: --version
v70.4
nfqws,tpws: ^ prefix in hostlist to disable subdomain matches
nfqws,tpws: optional systemd notify support. compile using 'make systemd'
nfqws,tpws: systemd instance templates for nfqws and tpws
nfqws,tpws: separate droproot from dropcaps
tpws: detect WSL 1 and warn about non-working options
v70.5
nfqws: multiple --dpi-desync-fake-xxx
nfqws: support of inter-packet fragmented QUIC CRYPTO

View File

@@ -1,4 +1,4 @@
# zapret v70.1
# zapret v70.5
# SCAMMER WARNING
@@ -185,9 +185,9 @@ nfqws takes the following parameters:
--dpi-desync-udplen-pattern=<filename>|0xHEX ; udp tail fill pattern
--dpi-desync-start=[n|d|s]N ; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N
--dpi-desync-cutoff=[n|d|s]N ; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N
--hostlist=<filename> ; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)
--hostlist=<filename> ; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply if not prefixed with `^`, gzip supported, multiple hostlists allowed)
--hostlist-domains=<domain_list> ; comma separated fixed domain list
--hostlist-exclude=<filename> ; do not apply dpi desync to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)
--hostlist-exclude=<filename> ; do not apply dpi desync to the listed hosts (one host per line, subdomains auto apply if not prefixed with `^`, gzip supported, multiple hostlists allowed)
--hostlist-exclude-domains=<domain_list> ; comma separated fixed domain list
--hostlist-auto=<filename> ; detect DPI blocks and build hostlist automatically
--hostlist-auto-fail-threshold=<int> ; how many failed attempts cause hostname to be added to auto hostlist (default : 3)
@@ -219,7 +219,9 @@ There're attacks based on TCP sequence numbers. Methods can be combined in many
Fakes are separate generated by nfqws packets carrying false information for DPI. They must either not reach the server or be rejected by it. Otherwise TCP connection or data stream would be broken. There're multiple ways to solve this task.
* **md5sig** does not work on all servers
* **md5sig** does not work on all servers. It typically works only on Linux servers. MD5 tcp option requires additional space in TCP header
and can cause MTU overflow during fakedsplit/fakeddisorder on low positions when multisegment query (TLS kyber) is transmitted.
`nfqws` cannot redistribute data between original TCP segments. The error displayed is 'message too long'.
* **badsum** doesn't work if your device is behind NAT which does not pass invalid packets.
The most common Linux NAT router configuration does not pass them. Most home routers are Linux based.
The default sysctl configuration `net.netfilter.nf_conntrack_checksum=1` causes contrack to verify tcp and udp checksums
@@ -263,6 +265,12 @@ Fakes are separate generated by nfqws packets carrying false information for DPI
`--dpi-desync-fooling` takes multiple comma separated values.
Multiple parameters `--dpi-desync-fake-???` are supported except for the `--dpi-desync-fake-syndata`.
Fakes are sent in the specified order. `--dpi-desync-repeats` resends each fake.
Resulting order would be : `fake1 fake1 fake1 fake2 fake2 fake2 fake3 fake3 fake3 .....`
### FAKE mods
**nfqws** has built-in TLS fake. It can be customized with `--dpi-desync-fake-tls` option.
@@ -280,6 +288,10 @@ It's possible to use TLS Client Hello with any fingerprint and any SNI.
By default if custom fake is not defined `rnd,rndsni,dupsid` mods are applied. If defined - `none`.
This behaviour is compatible with previous versions with addition of `dupsid`.
If TLS mod is enabled and there're multiple TLS fakes, all valid TLS Client Hello fakes are modified.
If there's no TLS Client Hello program exits with error.
### TCP segmentation
* `multisplit`. split request at specified in `--dpi-desync-split-pos` positions
@@ -703,9 +715,9 @@ tpws is transparent proxy.
--ipset-exclude=<filename> ; ipset exclude filter (one ip/CIDR per line, ipv4 and ipv6 accepted, gzip supported, multiple ipsets allowed)
--ipset-exclude-ip=<ip_list> ; comma separated fixed subnet list
--hostlist=<filename> ; only act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)
--hostlist=<filename> ; only act on hosts in the list (one host per line, subdomains auto apply if not prefixed with '^', gzip supported, multiple hostlists allowed)
--hostlist-domains=<domain_list> ; comma separated fixed domain list
--hostlist-exclude=<filename> ; do not act on hosts in the list (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)
--hostlist-exclude=<filename> ; do not act on hosts in the list (one host per line, subdomains auto apply if not prefixed with '^', gzip supported, multiple hostlists allowed)
--hostlist-exclude-domains=<domain_list> ; comma separated fixed domain list
--hostlist-auto=<filename> ; detect DPI blocks and build hostlist automatically
--hostlist-auto-fail-threshold=<int> ; how many failed attempts cause hostname to be added to auto hostlist (default : 3)
@@ -984,6 +996,7 @@ If all include lists are empty it works like no include lists exist at all.
If you need "all except" mode you dont have to delete zapret-hosts-users.txt. Just make it empty.
Subdomains auto apply. For example, "ru" in the list affects "*.ru" .
`^` prefix symbol disables subdomain match.
**tpws** and **nfqws** automatically reload lists if their modification time or file size is changed.
HUP signal forcibly reloads all lists.

View File

@@ -1,4 +1,4 @@
# zapret v70.1
# zapret v70.5
# ВНИМАНИЕ, остерегайтесь мошенников
@@ -57,6 +57,7 @@ zapret является свободным и open source.
- [Прикручивание к системе управления фаерволом или своей системе запуска](#прикручивание-к-системе-управления-фаерволом-или-своей-системе-запуска)
- [Вариант custom](#вариант-custom)
- [Простая установка](#простая-установка)
- [Установка под systemd](#установка-под-systemd)
- [Простая установка на openwrt](#простая-установка-на-openwrt)
- [Установка на openwrt в режиме острой нехватки места на диске](#установка-на-openwrt-в-режиме-острой-нехватки-места-на-диске)
- [Android](#android)
@@ -205,7 +206,7 @@ dvtws, собираемый из тех же исходников (см. [док
--dpi-desync-udplen-pattern=<filename>|0xHEX ; чем добивать udp пакет в режиме udplen. по умолчанию - нули
--dpi-desync-start=[n|d|s]N ; применять dpi desync только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру больше или равно N
--dpi-desync-cutoff=[n|d|s]N ; применять dpi desync только в исходящих пакетах (n), пакетах данных (d), относительных sequence (s) по номеру меньше N
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются.
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются, если хост не начинается с '^'.
; в файле должен быть хост на каждой строке.
; список читается при старте и хранится в памяти в виде иерархической структуры для быстрого поиска.
; при изменении времени модификации файла он перечитывается автоматически по необходимости
@@ -260,6 +261,10 @@ dvtws, собираемый из тех же исходников (см. [док
Есть ряд методов для решения этой задачи.
* `md5sig` добавляет TCP опцию **MD5 signature**. Работает не на всех серверах. Пакеты с md5 обычно отбрасывают только linux.
Требуется значительное увеличение длины tcp пакета, чтобы вместить tcp option. При обработке многосегментных запросов (TLS Kyber)
первый пакет идет полный под MTU. При fakedsplit/fakeddisorder на небольших позициях отдельные tcp сегменты достаточно велики, чтобы внедрение
md5 tcp option вызвало переполнение MTU и ошибку отправки "message too long". `nfqws` не умеет перераспределять данные между tcp сегментами,
поэтому надо или отказываться от kyber, или увеличивать сплит-позицию, или отказываться от fakedsplit/fakeddisorder.
* `badsum` портит контрольную сумму TCP. Не сработает, если ваше устройство за NAT, который не пропускает пакеты с инвалидной суммой. Наиболее
распространенная настройка NAT роутера в Linux их не пропускает. На Linux построено большинство домашних роутеров.
Непропускание обеспечивается так : настройка ядра sysctl по умолчанию
@@ -280,7 +285,7 @@ dvtws, собираемый из тех же исходников (см. [док
Такие пакеты будут наверняка отброшены принимающим узлом, но так же и DPI, если он ориентируется на sequence
numbers. По умолчанию смещение seq выбирается -10000. Практика показала, что некоторые DPI не пропускают seq вне
определенного окна. Однако, такое небольшое смещение может вызвать проблемы при существенной потоковой передаче и
потере пакетов. Если вы используете `--dpi-desync-any-protocol`, может понадобится установить badseq increment
потере пакетов. Если вы используете `--dpi-desync-any-protocol`, может понадобиться установить badseq increment
0x80000000. Это обеспечит надежную гарантию, что поддельный пакет не вклинится в tcp window на сервере. Так же было
замечено, что badseq ломает логику некоторых DPI при анализе http, вызывая зависание соединения. Причем на тех же DPI
TLS с badseq работает нормально.
@@ -295,7 +300,7 @@ dvtws, собираемый из тех же исходников (см. [док
* `hopbyhop` относится только к ipv6. Добавляется ipv6 extenstion header `hop-by-hop options`. В варианте `hopbyhop2`
добавляются 2 хедера, что является нарушением стандарта и гарантированно отбрасывается стеком протоколов во всех ОС.
Один хедер hop-by-hop принимается всеми ОС, однако на некоторых каналах/провайдерах такие пакеты могут фильтроваться и
не доходить. Расчет идет на то, что DPI проанализирует пакет с hop-by-hop, но он либо не дойдет до адресата всилу
не доходить. Расчет идет на то, что DPI проанализирует пакет с hop-by-hop, но он либо не дойдет до адресата в силу
фильтров провайдера, либо будет отброшен сервером, потому что хедера два.
* `datanoack` высылает фейки со снятым tcp флагом ACK. Сервера такое не принимают, а DPI может принять. Эта техника
может ломать NAT и не всегда работает с iptables, если используется masquerade, даже с локальной системы (почти всегда
@@ -315,6 +320,10 @@ dvtws, собираемый из тех же исходников (см. [док
Режимы дурения могут сочетаться в любых комбинациях. `--dpi-desync-fooling` берет множество значений через запятую.
Возможно задание множества фейков через повторение парамеров `--dpi-desync-fake-???`, кроме `--dpi-desync-fake-syndata`.
Фейки будут отосланы в указанном порядке. `--dpi-desync-repeats` повторяет каждый отосланный фейк.
Итоговый порядок будет такой : `fake1 fake1 fake1 fake2 fake2 fake2 fake3 fake3 fake3 .....`
### МОДИФИКАЦИЯ ФЕЙКОВ
В nfqws зашит базовый вариант фейка для TLS. Его можно переопределить опцией `--dpi-desync-fake-tls`.
@@ -334,6 +343,9 @@ dvtws, собираемый из тех же исходников (см. [док
По умолчанию если не задан собственный фейк для TLS используются модификации `rnd,rndsni,dupsid`. Если фейк задан, используется `none`.
Это соответствует поведению программы более старых версий с добавлением функции `dupsid`.
Если задан режим модификации и имеется множество TLS фейков, модифицируются все фейки, являющиеся TLS Client Hello.
Если нет ни одного TLS Client Hello фейка, программа завершается с ошибкой.
### TCP СЕГМЕНТАЦИЯ
* `multisplit`. нарезаем запрос на указанных в `--dpi-desync-split-pos` позициях.
@@ -346,6 +358,9 @@ dvtws, собираемый из тех же исходников (см. [док
Размеры фейков соответствуют длинам отсылаемых частей.
Цель этих режимов - максимально усложнить выявление оригинальных данных среди фейков.
Использование `fakedsplit` или `fakeddisorder` на TLS kyber с md5sig fooling может привести к ошибкам "message too long", если позиция сплита мала,
поскольку будет превышение MTU из-за md5 tcp option.
Для определения позиций нарезки используются маркеры.
* **Абсолютный положительный маркер** - числовое смещение внутри пакета или группы пакетов от начала.
@@ -530,8 +545,8 @@ window size итоговый размер окна стал максимальн
### РЕАССЕМБЛИНГ
nfqws поддерживает реассемблинг некоторых видов запросов.
На текущий момент это TLS и QUIC ClientHello. Они бывает длинными, если в chrome включить пост-квантовую
криптографию tls-kyber, и занимают как правило 2 или 3 пакета. kyber включен по умолчанию, начиная с chromium 124.
На текущий момент это TLS и QUIC ClientHello. Они бывают длинными, если в chrome включить пост-квантовую
криптографию tls-kyber, и занимают, как правило, 2 или 3 пакета. kyber включен по умолчанию, начиная с chromium 124.
chrome рандомизирует фингерпринт TLS. SNI может оказаться как в начале, так и в конце, то есть
попасть в любой пакет. stateful DPI обычно реассемблирует запрос целиком, и только потом
принимает решение о блокировке.
@@ -620,7 +635,7 @@ options ip6table_raw raw_before_defrag=1
При использовании iptables и NAT, похоже, что нет способа прицепить обработчик очереди после NAT.
Пакет попадает в nfqws с source адресом внутренней сети, затем фрагментируется и уже не обрабатывается NAT.
Так и уходит во внешюю сеть с src ip 192.168.x.x. Следовательно, метод не срабатывает.
Так и уходит во внешнюю сеть с src ip 192.168.x.x. Следовательно, метод не срабатывает.
Видимо единственный рабочий метод - отказаться от iptables и использовать nftables.
Хук должен быть с приоритетом 101 или выше.
@@ -644,7 +659,7 @@ L7 протокол становится известен обычно посл
Если имя хоста удовлетворяет листам, выбирается этот профиль. Иначе идет переход к следующему.
Может так случиться, что до получения имени хоста или узнавания L7 протокола соединение идет по одному профилю,
а при выяснении этих параметров профиль меняется на лету. Это может произойти даже дважды - при выяснении L7
и имени хоста. Чаще всего это выяснение совмещается в одно действие, поскольку по одному пакету как правило узнается и L7, и хост.
и имени хоста. Чаще всего это выяснение совмещается в одно действие, поскольку по одному пакету, как правило, узнается и L7, и хост.
Поэтому если у вас есть параметры дурения нулевой фазы, тщательно продумывайте что может произойти при переключении стратегии.
Смотрите debug log, чтобы лучше понять что делает nfqws.
Нумерация профилей идет с 1 до N. Последним в цепочке создается пустой профиль с номером 0.
@@ -658,7 +673,7 @@ L7 протокол становится известен обычно посл
> [!IMPORTANT]
> user-mode реализация ipset создавалась не как удобная замена *nix версии, реализованной в ядре.
> Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без подержки ipset в ядре.
> Вариант в ядре работает гораздо эффективнее. Это создавалось для систем без подд3ержки ipset в ядре.
> Конкретно - Windows и ядра Linux, собранные без nftables и ipset модулей ядра. Например, в android нет ipset.
### IPTABLES ДЛЯ NFQWS
@@ -678,7 +693,7 @@ iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp
```
mark нужен, чтобы сгенерированный поддельный пакет не попал опять к нам на обработку. nfqws выставляет fwmark при его отсылке.
хотя nfqws способен самостоятельно различать помеченные пакеты, фильтр в iptables по mark нужен при использовании connbytes,
Хотя nfqws способен самостоятельно различать помеченные пакеты, фильтр в iptables по mark нужен при использовании connbytes,
чтобы не допустить изменения порядка следования пакетов. Процессинг очереди - процесс отложенный.
Если ядро имеет пакеты на отсылку вне очереди - оно их отправляет незамедлительно.
Изменение правильного порядка следования пакетов при десинхронизации ломает всю идею.
@@ -691,7 +706,7 @@ mark нужен, чтобы сгенерированный поддельный
* 3 - стандартная ситуация приема одного пакета запроса
* 4-6 - на случай ретрансмиссии или запроса длиной в несколько пакетов (TLSClientHello с kyber, например)
Для режима autottl необходимо перенаправление входящего `SYN,ACK` пакета или первого пакета соединения (что обычно есть тоже самое).
Для режима autottl необходимо перенаправление входящего `SYN,ACK` пакета или первого пакета соединения (что обычно есть то же самое).
Для режима autohostlist необходимы входящие RST и http redirect.
Можно построить фильтр на tcp flags для выделения `SYN,ACK` и модуле u32 для поиска характерных паттернов http redirect,
но проще использовать connbytes для выделения нескольких начальных входящих пакетов.
@@ -855,7 +870,7 @@ tpws - это transparent proxy.
--mss=<int> ; установить MSS для клиента. может заставить сервер разбивать ответы, но существенно снижает скорость
--tamper-start=[n]<pos> ; начинать дурение только с указанной байтовой позиции или номера блока исходяшего потока (считается позиция начала принятого блока)
--tamper-cutoff=[n]<pos> ; закончить дурение на указанной байтовой позиции или номере блока исходящего потока (считается позиция начала принятого блока)
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются.
--hostlist=<filename> ; действовать только над доменами, входящими в список из filename. поддомены автоматически учитываются, если хост не начинается с '^'.
; в файле должен быть хост на каждой строке.
; список читается при старте и хранится в памяти в виде иерархической структуры для быстрого поиска.
; при изменении времени модификации файла он перечитывается автоматически по необходимости
@@ -892,7 +907,7 @@ tpws, как и nfqws, поддерживает множественную се
Однако, если в момент send уже имеется неотосланный буфер, то ОС присоединит данные к нему,
никакой отсылки отдельным пакетом не будет. Но в этом случае и так нет никакой гарантии,
что какой-то блок сообщения пойдет в начале пакета, на что собственно и заточены DPI.
Разбиение будет производится согласно MSS, который зависит от MTU исходящего интерфейса.
Разбиение будет производиться согласно MSS, который зависит от MTU исходящего интерфейса.
Таким образом DPI, смотрящие в начало поля данных TCP пакета, будут поломаны в любом случае.
Протокол http относится к запрос-ответным протоколам. Новое сообщение посылается только тогда,
когда сервер получил запрос и полностью вернул ответ. Значит запрос фактически был не только отослан,
@@ -1080,7 +1095,7 @@ route_localnet :
динамически вписывать в команду. В любом случае требуются дополнительные усилия. Использование route_localnet тоже имеет
потенциальные проблемы с безопасностью. Вы делаете доступным все, что висит на `127.0.0.0/8` для локальной подсети <
внутренний_интерфейс>. Службы обычно привязываются к `127.0.0.1`, поэтому можно средствами iptables запретить входящие
на `127.0.0.1` не с интерфейса lo, либо повесить tpws на любой другой IP из из `127.0.0.0/8`, например на `127.0.0.127`,
на `127.0.0.1` не с интерфейса lo, либо повесить tpws на любой другой IP из `127.0.0.0/8`, например на `127.0.0.127`,
и разрешить входящие не с lo только на этот IP.
```
@@ -1322,10 +1337,11 @@ LISTS_RELOAD=- отключает перезагрузку листов.
При режимах фильтрации `MODE_FILTER=hostlist` или `MODE_FILTER=autohostlist` система запуска передает **nfqws** или **tpws** все листы, файлы которых присутствуют.
Передача происходит через замену маркеров `<HOSTLIST>` и `<HOSTLIST_NOAUTO>` на реальные параметры `--hostlist`, `--hostlist-exclude`, `--hostlist-auto`.
Если вдруг листы include присутствуют, но все они пустые, то работа аналогична отсутствию include листа.
Файл есть, но не смотря на это дурится все, кроме exclude.
Файл есть, но несмотря на это дурится все, кроме exclude.
Если вам нужен именно такой режим - не обязательно удалять `zapret-hosts-users.txt`. Достаточно сделать его пустым.
Поддомены учитываются автоматически. Например, строчка "ru" вносит в список "*.ru". Строчка "*.ru" в списке не сработает.
Можно использовать символ `^` в начале хоста, чтобы отказаться от автоматического учета поддоменов.
Список доменов РКН может быть получен скриптами
```
@@ -1359,7 +1375,7 @@ tpws и nfqws решают нужно ли применять дурение в
Крайне рекомендовано использовать ограничитель `connbytes`, чтобы **nfqws** не обрабатывал гигабайты.
По этой же причине не рекомендуется использование режима на BSD системах. Там нет фильтра `connbytes`.
На linux системах при использовании nfqws и фильтра connbytes может понадобится :
На linux системах при использовании nfqws и фильтра connbytes может понадобиться :
`sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1`
Было замечено, что некоторые DPI в России возвращают RST с неверным ACK. Это принимается tcp/ip стеком
linux, но через раз приобретает статус INVALID в conntrack. Поэтому правила с `connbytes` срабатывают
@@ -1374,7 +1390,7 @@ linux, но через раз приобретает статус INVALID в con
свое клиенту. Применяется нечасто, поскольку броузеры на такое ругаются.
**nfqws** и **tpws** могут сечь варианты 1-3, 4 они не распознают.
Всилу специфики работы с отдельными пакетами или с TCP каналом tpws и nfqws распознают эти ситуации
В силу специфики работы с отдельными пакетами или с TCP каналом tpws и nfqws распознают эти ситуации
по-разному.
Что считается ситуацией, похожей на блокировку :
1) **nfqws** Несколько ретрансмиссий первого запроса в TCP сеансе, в котором имеется host.
@@ -1400,7 +1416,7 @@ linux, но через раз приобретает статус INVALID в con
Если сайт не ведет себя как заблокированный, значит обход применен не будет.
В противном случае терять все равно нечего.
Однако, могут быть временные сбои сервера, приводящие к ситуации, аналогичной блокировке.
Могут происходит ложные срабатывания. Если такое произошло, стратегия может начать ломать
Могут происходить ложные срабатывания. Если такое произошло, стратегия может начать ломать
незаблокированный сайт. Эту ситуацию, увы, придется вам контролировать вручную.
Заносите такие домены в `ipset/zapret-hosts-user-exclude.txt`, чтобы избежать повторения.
Чтобы впоследствии разобраться почему домен был занесен в лист, можно включить `autohostlist debug log`.
@@ -1747,7 +1763,7 @@ DISABLE_IPV6=1
```
Количество потоков для многопоточного DNS ресолвера mdig (1..100).
Чем их больше, тем быстрее, но не обидется ли на долбежку ваш DNS сервер?\
Чем их больше, тем быстрее, но не обидится ли на долбежку ваш DNS сервер?\
`MDIG_THREADS=30`
Место для хранения временных файлов. При скачивании огромных реестров в `/tmp` места может не хватить.
@@ -1901,7 +1917,7 @@ nfset-ы принадлежат только одной таблице, след
custom скрипты - это маленькие shell программы, управляющие нестандартными режимами применения zapret
или частными случаями, которые не могут быть интегрированы в основную часть без загромождения и замусоривания кода.
Для применеия custom следует помещать файлы в следующие директории в зависимости от вашей системы:
Для применения custom следует помещать файлы в следующие директории в зависимости от вашей системы:
```
/opt/zapret/init.d/sysv/custom.d
/opt/zapret/init.d/openwrt/custom.d
@@ -1991,7 +2007,7 @@ zapret_custom_firewall_nft поднимает правила nftables.
Под OpenWrt все уже сразу готово для использования системы в качестве роутера.
Имена интерфейсов WAN и LAN известны из настроек системы.
Под другими системами роутер вы настраиваете самостоятельно. Инсталлятор в это не вмешивается.
инсталлятор в зависимости от выбранного режима может спросить LAN и WAN интерфейсы.
Инсталлятор в зависимости от выбранного режима может спросить LAN и WAN интерфейсы.
Нужно понимать, что заворот проходящего трафика на **tpws** в прозрачном режиме происходит до выполнения маршрутизации,
следовательно возможна фильтрация по LAN и невозможна по WAN.
Решение о завороте на **tpws** локального исходящего трафика принимается после выполнения маршрутизации,
@@ -2001,6 +2017,15 @@ zapret_custom_firewall_nft поднимает правила nftables.
Деинсталляция выполняется через `uninstall_easy.sh`. После выполнения деинсталляции можно удалить каталог `/opt/zapret`.
## Установка под systemd
Если вам нравится systemd и хочется максимально под него заточиться, можно отказаться от скриптов запуска zapret
и поднимать инстансы `tpws` и `nfqws` как отдельные юниты systemd. При этом вам придется вручную написать правила iptables/nftables
и каким-то образом их поднимать. Например, написать дополнительный systemd unit для этого.
Так же требуется собрать бинарники особым образом через `make systemd`.
В комплекте zapret есть шаблоны `init.d/systemd/{nfqws@.service,tpws@.service}`.
Краткий перечень команд для их использования приведен в комментариях в этих файлах.
## Простая установка на openwrt
@@ -2172,7 +2197,7 @@ Wifi сеть - обычно `wlan0`.
tpws работает обычным образом.
`nfqueue` поломан, можно собрать фиксящий модуль https://github.com/im-0/unfuck-nfqueue-on-e3372h,
`nfqueue` поломан, можно собрать фиксящий модуль https://github.com/im-0/unfuck-nfqueue-on-e3372h,
используя исходники с huawei open source. Исходники содержат тулчейн и полусобирающееся,
неактуальное ядро. Конфиг можно взять с рабочего модема из `/proc/config.gz`.
С помощью этих исходников умельцы могут собрать модуль `unfuck_nfqueue.ko`.
@@ -2225,10 +2250,10 @@ curl: (7) Failed to connect to www.ru port 80: Host is unreachable
## Другие прошивки
Для статических бинариков не имеет значения на чем они запущены: PC, android, приставка, роутер, любой другой девайс.
Для статических бинарников не имеет значения на чем они запущены: PC, android, приставка, роутер, любой другой девайс.
Подойдет любая прошивка, дистрибутив linux. Статические бинарники запустятся на всем.
Им нужно только ядро с необходимыми опциями сборки или модулями.
Но кроме бинариков в проекте используются еще и скрипты, в которых задействуются некоторые
Но кроме бинарников в проекте используются еще и скрипты, в которых задействуются некоторые
стандартные программы.
Основные причины почему нельзя просто так взять и установить эту систему на что угодно:
@@ -2266,14 +2291,14 @@ entware содержит репозиторий user-mode компонент, к
одробное описание настроек для других прошивок выходит за рамки данного проекта._
OpenWrt является одной из немногих относительно полноценных linux систем для embedded devices.
Она характеризуется следующими вещами, которые и послужили основой выбора именно этой прошивк:
Она характеризуется следующими вещами, которые и послужили основой выбора именно этой прошивки:
* полный root доступ к девайсу через shell. на заводских прошивках чаще всего отсутствует, на многих альтернативных есть
* корень r/w. это практически уникальная особенность OpenWrt. заводские и большинство альтернативных прошивок
построены на базе squashfs root (r/o), а конфигурация хранится в специально отформатированной области
встроенной памяти, называемой nvram. не имеющие r/w корня системы сильно кастрированы. они не имеют
возможности доустановки ПО из репозитория без специальных вывертов и заточены в основном
на чуть более продвинутого, чем обычно, пользователя и управление имеющимся функционалом через веб интерфейс,
но функционал фиксированно ограничен. альтернативные прошивки как правило могут монтировать r/w раздел
но функционал фиксированно ограничен. альтернативные прошивки, как правило, могут монтировать r/w раздел
в какую-то область файловой системы, заводские обычно могут монтировать лишь флэшки, подключенные к USB,
и не факт, что есть поддержка unix файловых системы. может быть поддержка только fat и ntfs.
* возможность выноса корневой файловой системы на внешний носитель (extroot) или создания на нем оверлея (overlay)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,10 @@
CC ?= gcc
CFLAGS += -std=gnu99 -Os -flto=auto
CFLAGS_SYSTEMD = -DUSE_SYSTEMD
CFLAGS_BSD = -Wno-address-of-packed-member
CFLAGS_CYGWIN = -Wno-address-of-packed-member -static
LIBS_LINUX = -lnetfilter_queue -lnfnetlink -lz
LIBS_SYSTEMD = -lsystemd
LIBS_BSD = -lz
LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32
LIBS_CYGWIN32 = -lwindivert32
@@ -16,6 +18,9 @@ all: nfqws
nfqws: $(SRC_FILES)
$(CC) -s $(CFLAGS) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LDFLAGS)
systemd: $(SRC_FILES)
$(CC) -s $(CFLAGS) $(CFLAGS_SYSTEMD) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LIBS_SYSTEMD) $(LDFLAGS)
android: nfqws
bsd: $(SRC_FILES)

View File

@@ -1747,7 +1747,9 @@ nofix:
bytes = sendto(sock, data, len, 0, (struct sockaddr*)&dst2, salen);
if (bytes==-1)
{
DLOG_PERROR("rawsend: sendto");
char s[40];
snprintf(s,sizeof(s),"rawsend: sendto (%zu)",len);
DLOG_PERROR(s);
return false;
}
return true;

View File

@@ -66,6 +66,9 @@ const uint8_t fake_tls_clienthello_default[648] = {
#define PKTDATA_MAXDUMP 32
#define IP_MAXDUMP 80
#define TCP_MAX_REASM 16384
#define UDP_MAX_REASM 16384
bool desync_valid_zero_stage(enum dpi_desync_mode mode)
{
return mode==DESYNC_SYNACK || mode==DESYNC_SYNDATA;
@@ -347,7 +350,7 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
{
DLOG("auto hostlist (profile %d) : adding %s to %s\n", dp->n, hostname, dp->hostlist_auto->filename);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : client %s : proto %s : adding to %s", hostname, dp->n, client_ip_port, l7proto_str(l7proto), dp->hostlist_auto->filename);
if (!StrPoolAddStr(&dp->hostlist_auto->hostlist, hostname))
if (!HostlistPoolAddStr(&dp->hostlist_auto->hostlist, hostname, 0))
{
DLOG_ERR("StrPoolAddStr out of memory\n");
return;
@@ -606,45 +609,48 @@ static uint16_t IP4_IP_ID_FIX(const struct ip *ip)
// fake_mod buffer must at least sizeof(desync_profile->fake_tls)
// size does not change
// return : true - altered, false - not altered
static bool runtime_tls_mod(const struct desync_profile *dp, uint8_t *fake_mod, const uint8_t *payload, size_t payload_len)
static bool runtime_tls_mod(int fake_n,const struct fake_tls_mod_cache *modcache, uint8_t fake_tls_mod, const uint8_t *fake_data, size_t fake_data_size, const uint8_t *payload, size_t payload_len, uint8_t *fake_mod)
{
bool b=false;
if (dp->fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
if (modcache) // it's filled only if it's TLS
{
size_t sz_rec = pntoh16(dp->fake_tls+3) + payload_len;
size_t sz_handshake = pntoh24(dp->fake_tls+6) + payload_len;
size_t sz_ext = pntoh16(dp->fake_tls+dp->fake_tls_extlen_offset) + payload_len;
size_t sz_pad = pntoh16(dp->fake_tls+dp->fake_tls_padlen_offset) + payload_len;
if ((sz_rec & ~0xFFFF) || (sz_handshake & ~0xFFFFFF) || (sz_ext & ~0xFFFF) || (sz_pad & ~0xFFFF))
DLOG("cannot apply padencap tls mod. length overflow.\n");
else
if (fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
{
memcpy(fake_mod,dp->fake_tls,dp->fake_tls_size);
phton16(fake_mod+3,(uint16_t)sz_rec);
phton24(fake_mod+6,(uint32_t)sz_handshake);
phton16(fake_mod+dp->fake_tls_extlen_offset,(uint16_t)sz_ext);
phton16(fake_mod+dp->fake_tls_padlen_offset,(uint16_t)sz_pad);
size_t sz_rec = pntoh16(fake_data+3) + payload_len;
size_t sz_handshake = pntoh24(fake_data+6) + payload_len;
size_t sz_ext = pntoh16(fake_data+modcache->extlen_offset) + payload_len;
size_t sz_pad = pntoh16(fake_data+modcache->padlen_offset) + payload_len;
if ((sz_rec & ~0xFFFF) || (sz_handshake & ~0xFFFFFF) || (sz_ext & ~0xFFFF) || (sz_pad & ~0xFFFF))
DLOG("fake[%d] cannot apply padencap tls mod. length overflow.\n", fake_n);
else
{
memcpy(fake_mod,fake_data,fake_data_size);
phton16(fake_mod+3,(uint16_t)sz_rec);
phton24(fake_mod+6,(uint32_t)sz_handshake);
phton16(fake_mod+modcache->extlen_offset,(uint16_t)sz_ext);
phton16(fake_mod+modcache->padlen_offset,(uint16_t)sz_pad);
b=true;
}
}
if (fake_tls_mod & FAKE_TLS_MOD_RND)
{
if (!b) memcpy(fake_mod,fake_data,fake_data_size);
fill_random_bytes(fake_mod+11,32); // random
fill_random_bytes(fake_mod+44,fake_mod[43]); // session id
b=true;
}
}
if (dp->fake_tls_mod & FAKE_TLS_MOD_RND)
{
if (!b) memcpy(fake_mod,dp->fake_tls,dp->fake_tls_size);
fill_random_bytes(fake_mod+11,32); // random
fill_random_bytes(fake_mod+44,fake_mod[43]); // session id
b=true;
}
if (dp->fake_tls_mod & FAKE_TLS_MOD_DUP_SID)
{
if (dp->fake_tls[43]!=payload[43])
DLOG("cannot apply dupsid tls mod. fake and orig session id length mismatch.\n");
else if (payload_len<(44+payload[43]))
DLOG("cannot apply dupsid tls mod. data payload is not valid.\n");
else
if (fake_tls_mod & FAKE_TLS_MOD_DUP_SID)
{
if (!b) memcpy(fake_mod,dp->fake_tls,dp->fake_tls_size);
memcpy(fake_mod+44,payload+44,fake_mod[43]); // session id
b=true;
if (fake_data[43]!=payload[43])
DLOG("fake[%d] cannot apply dupsid tls mod. fake and orig session id length mismatch.\n",fake_n);
else if (payload_len<(44+payload[43]))
DLOG("fake[%d] cannot apply dupsid tls mod. data payload is not valid.\n",fake_n);
else
{
if (!b) memcpy(fake_mod,fake_data,fake_data_size);
memcpy(fake_mod+44,payload+44,fake_mod[43]); // session id
b=true;
}
}
}
return b;
@@ -880,8 +886,8 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
if (!(dis->tcp->th_flags & TH_SYN) && dis->len_payload)
{
const uint8_t *fake;
size_t fake_size;
struct blob_collection_head *fake;
char host[256];
bool bHaveHost=false;
uint8_t *p, *phost=NULL;
@@ -893,7 +899,6 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
int i;
uint16_t ip_id;
t_l7proto l7proto = UNKNOWN;
uint8_t fake_mod[sizeof(dp->fake_tls)];
if (replay)
{
@@ -952,7 +957,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
!(ctrack->req_seq_finalized && seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end)))
{
// do not reconstruct unexpected large payload (they are feeding garbage ?)
if (!reasm_orig_start(ctrack,IPPROTO_TCP,TLSRecordLen(dis->data_payload),16384,dis->data_payload,dis->len_payload))
if (!reasm_orig_start(ctrack,IPPROTO_TCP,TLSRecordLen(dis->data_payload),TCP_MAX_REASM,dis->data_payload,dis->len_payload))
{
reasm_orig_cancel(ctrack);
return verdict;
@@ -1183,16 +1188,13 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
switch(l7proto)
{
case HTTP:
fake = dp->fake_http;
fake_size = dp->fake_http_size;
fake = &dp->fake_http;
break;
case TLS:
fake = runtime_tls_mod(dp,fake_mod,rdata_payload,rlen_payload) ? fake_mod : dp->fake_tls;
fake_size = dp->fake_tls_size;
fake = &dp->fake_tls;
break;
default:
fake = dp->fake_unknown;
fake_size = dp->fake_unknown_size;
fake = &dp->fake_unknown;
break;
}
if (dp->desync_mode==DESYNC_MULTISPLIT || dp->desync_mode==DESYNC_MULTIDISORDER || dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER)
@@ -1273,13 +1275,8 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
else
seqovl_pos = 0;
// we do not need reasm buffer anymore
reasm_orig_cancel(ctrack);
rdata_payload=NULL;
uint32_t fooling_orig = FOOL_NONE;
bool bFake = false;
pkt1_len = sizeof(pkt1);
switch(dp->desync_mode)
{
case DESYNC_FAKE_KNOWN:
@@ -1291,28 +1288,69 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
}
case DESYNC_FAKE:
if (reasm_offset) break;
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
ttl_fake,IP4_TOS(dis->ip),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6),
dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
fake, fake_size, pkt1, &pkt1_len))
{
return verdict;
struct blob_item *fake_item;
uint8_t *fake_data;
uint8_t fake_data_buf[FAKE_MAX_TCP];
int n=0;
ip_id = IP4_IP_ID_FIX(dis->ip);
LIST_FOREACH(fake_item, fake, next)
{
n++;
switch(l7proto)
{
case TLS:
if ((fake_item->size <= sizeof(fake_data_buf)) &&
runtime_tls_mod(n,(struct fake_tls_mod_cache *)fake_item->extra, dp->fake_tls_mod, fake_item->data, fake_item->size, rdata_payload, rlen_payload, fake_data_buf))
{
fake_data = fake_data_buf;
break;
}
default:
fake_data = fake_item->data;
}
pkt1_len = sizeof(pkt1);
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
ttl_fake,IP4_TOS(dis->ip),ip_id,IP6_FLOW(dis->ip6),
dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
fake_data, fake_item->size, pkt1, &pkt1_len))
{
reasm_orig_cancel(ctrack);
return verdict;
}
DLOG("sending fake[%d] : ", n);
hexdump_limited_dlog(fake_data,fake_item->size,PKTDATA_MAXDUMP); DLOG("\n");
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
{
reasm_orig_cancel(ctrack);
return verdict;
}
ip_id=IP4_IP_ID_NEXT(ip_id);
}
}
DLOG("sending fake : ");
hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n");
bFake = true;
break;
case DESYNC_RST:
case DESYNC_RSTACK:
if (reasm_offset) break;
pkt1_len = sizeof(pkt1);
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (dp->desync_mode==DESYNC_RSTACK ? TH_ACK:0), dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
ttl_fake,IP4_TOS(dis->ip),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6),
dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment,
NULL, 0, pkt1, &pkt1_len))
{
reasm_orig_cancel(ctrack);
return verdict;
}
DLOG("sending fake RST/RSTACK\n");
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
{
reasm_orig_cancel(ctrack);
return verdict;
}
bFake = true;
break;
case DESYNC_HOPBYHOP:
@@ -1323,8 +1361,12 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
(!split_pos && (dp->desync_mode2==DESYNC_FAKEDSPLIT || dp->desync_mode2==DESYNC_FAKEDDISORDER)) ||
(!multisplit_count && (dp->desync_mode2==DESYNC_MULTISPLIT || dp->desync_mode2==DESYNC_MULTIDISORDER))))
{
reasm_orig_cancel(ctrack);
rdata_payload=NULL;
pkt1_len = sizeof(pkt1);
if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, dis->tcp->th_seq, dis->tcp->th_ack, dis->tcp->th_win, scale_factor, timestamps,
ttl_orig,IP4_TOS(dis->ip),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6),
ttl_orig,0,0,IP6_FLOW(dis->ip6),
fooling_orig,0,0,
dis->data_payload, dis->len_payload, pkt1, &pkt1_len))
{
@@ -1341,11 +1383,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint
break;
}
if (bFake)
{
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
return verdict;
}
// we do not need reasm buffer anymore
reasm_orig_cancel(ctrack);
rdata_payload=NULL;
enum dpi_desync_mode desync_mode = dp->desync_mode2==DESYNC_NONE ? dp->desync_mode : dp->desync_mode2;
switch(desync_mode)
@@ -1875,8 +1915,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
if (dis->len_payload)
{
const uint8_t *fake;
size_t fake_size;
struct blob_collection_head *fake;
char host[256];
bool bHaveHost=false;
uint16_t ip_id;
@@ -1917,29 +1956,82 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
return verdict; // cannot be first packet
}
}
uint8_t defrag[16384];
uint8_t defrag[UDP_MAX_REASM];
size_t hello_offset, hello_len, defrag_len = sizeof(defrag);
if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len))
bool bFull;
if (QUICDefragCrypto(pclean,clean_len,defrag,&defrag_len,&bFull))
{
bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
DLOG(bIsHello ? bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n" : "packet does not contain TLS ClientHello\n");
if (ctrack)
if (bFull)
{
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
DLOG("QUIC initial contains CRYPTO with full fragment coverage\n");
bool bIsHello = IsQUICCryptoHello(defrag, defrag_len, &hello_offset, &hello_len);
bool bReqFull = bIsHello ? IsTLSHandshakeFull(defrag+hello_offset,hello_len) : false;
DLOG(bIsHello ? bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n" : "packet does not contain TLS ClientHello\n");
if (ctrack)
{
// preallocate max buffer to avoid reallocs that cause memory copy
if (!reasm_orig_start(ctrack,IPPROTO_UDP,16384,16384,clean,clean_len))
if (bIsHello && !bReqFull && ReasmIsEmpty(&ctrack->reasm_orig))
{
// preallocate max buffer to avoid reallocs that cause memory copy
if (!reasm_orig_start(ctrack,IPPROTO_UDP,UDP_MAX_REASM,UDP_MAX_REASM,clean,clean_len))
{
reasm_orig_cancel(ctrack);
return verdict;
}
}
if (!ReasmIsEmpty(&ctrack->reasm_orig))
{
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
{
DLOG("DELAY desync until reasm is complete (#%u)\n", rawpacket_queue_count(&ctrack->delayed));
}
else
{
DLOG_ERR("rawpacket_queue failed !\n");
reasm_orig_cancel(ctrack);
return verdict;
}
if (bReqFull)
{
replay_queue(&ctrack->delayed);
reasm_orig_fin(ctrack);
}
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
}
}
if (bIsHello)
{
bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE);
if (!bHaveHost && dp->desync_skip_nosni)
{
reasm_orig_cancel(ctrack);
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n");
return verdict;
}
}
if (!ReasmIsEmpty(&ctrack->reasm_orig))
else
{
if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict;
}
}
else
{
DLOG("QUIC initial contains CRYPTO with partial fragment coverage\n");
if (ctrack)
{
if (ReasmIsEmpty(&ctrack->reasm_orig))
{
// preallocate max buffer to avoid reallocs that cause memory copy
if (!reasm_orig_start(ctrack,IPPROTO_UDP,UDP_MAX_REASM,UDP_MAX_REASM,clean,clean_len))
{
reasm_orig_cancel(ctrack);
return verdict;
}
}
verdict_udp_csum_fix(verdict, dis->udp, dis->transport_len, dis->ip, dis->ip6);
if (rawpacket_queue(&ctrack->delayed, &dst, desync_fwmark, ifout, dis->data_pkt, dis->len_pkt, dis->len_payload))
{
@@ -1951,28 +2043,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
reasm_orig_cancel(ctrack);
return verdict;
}
if (bReqFull)
{
replay_queue(&ctrack->delayed);
reasm_orig_fin(ctrack);
}
return ct_new_postnat_fix_udp(ctrack, dis->ip, dis->ip6, dis->udp, &dis->len_pkt);
}
}
if (bIsHello)
{
bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE);
if (!bHaveHost && dp->desync_skip_nosni)
{
reasm_orig_cancel(ctrack);
DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n");
return verdict;
}
}
else
{
if (!quic_reasm_cancel(ctrack,"QUIC initial without ClientHello")) return verdict;
if (!quic_reasm_cancel(ctrack,"QUIC initial fragmented CRYPTO")) return verdict;
}
}
else
@@ -1991,7 +2064,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
{
// received payload without host. it means we are out of the request retransmission phase. stop counter
ctrack_stop_retrans_counter(ctrack);
reasm_orig_cancel(ctrack);
if (IsWireguardHandshakeInitiation(dis->data_payload,dis->len_payload))
@@ -2110,20 +2183,16 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
switch(l7proto)
{
case QUIC:
fake = dp->fake_quic;
fake_size = dp->fake_quic_size;
fake = &dp->fake_quic;
break;
case WIREGUARD:
fake = dp->fake_wg;
fake_size = dp->fake_wg_size;
fake = &dp->fake_wg;
break;
case DHT:
fake = dp->fake_dht;
fake_size = dp->fake_dht_size;
fake = &dp->fake_dht;
break;
default:
fake = dp->fake_unknown_udp;
fake_size = dp->fake_unknown_udp_size;
fake = &dp->fake_unknown_udp;
break;
}
@@ -2140,7 +2209,6 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
}
bool bFake = false;
pkt1_len = sizeof(pkt1);
switch(dp->desync_mode)
{
case DESYNC_FAKE_KNOWN:
@@ -2150,12 +2218,30 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
break;
}
case DESYNC_FAKE:
if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, IP4_TOS(dis->ip),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6), dp->desync_fooling_mode, NULL, 0, 0, fake, fake_size, pkt1, &pkt1_len))
return verdict;
DLOG("sending fake : ");
hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n");
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
return verdict;
{
struct blob_item *fake_item;
int n=0;
ip_id = IP4_IP_ID_FIX(dis->ip);
LIST_FOREACH(fake_item, fake, next)
{
n++;
pkt1_len = sizeof(pkt1);
if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst,
ttl_fake, IP4_TOS(dis->ip),ip_id,IP6_FLOW(dis->ip6),
dp->desync_fooling_mode, NULL, 0, 0,
fake_item->data, fake_item->size, pkt1, &pkt1_len))
{
return verdict;
}
DLOG("sending fake[%d] : ", n);
hexdump_limited_dlog(fake_item->data,fake_item->size,PKTDATA_MAXDUMP); DLOG("\n");
if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len))
return verdict;
ip_id=IP4_IP_ID_NEXT(ip_id);
}
}
bFake = true;
break;
case DESYNC_HOPBYHOP:
@@ -2164,9 +2250,9 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint
fooling_orig = (dp->desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (dp->desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1;
if (dis->ip6 && (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_udp(dp->desync_mode2)))
{
pkt1_len = sizeof(pkt1);
if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst,
ttl_orig,IP4_TOS(dis->ip),IP4_IP_ID_FIX(dis->ip),IP6_FLOW(dis->ip6),
fooling_orig,NULL,0,0,
ttl_orig,0,0,IP6_FLOW(dis->ip6),fooling_orig,NULL,0,0,
dis->data_payload, dis->len_payload, pkt1, &pkt1_len))
{
return verdict;

View File

@@ -391,6 +391,12 @@ void fill_random_az09(uint8_t *p,size_t sz)
}
}
void set_console_io_buffering(void)
{
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(stderr, NULL, _IOLBF, 0);
}
bool set_env_exedir(const char *argv0)
{
char *s,*d;

View File

@@ -92,6 +92,7 @@ void fill_random_bytes(uint8_t *p,size_t sz);
void fill_random_az(uint8_t *p,size_t sz);
void fill_random_az09(uint8_t *p,size_t sz);
void set_console_io_buffering(void);
bool set_env_exedir(const char *argv0);

View File

@@ -4,7 +4,7 @@
#include "helpers.h"
// inplace tolower() and add to pool
static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
static bool addpool(hostlist_pool **hostlist, char **s, const char *end, int *ct)
{
char *p=*s;
@@ -17,10 +17,16 @@ static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
else
{
// advance until eol lowering all chars
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!StrPoolAddStrLen(hostlist, *s, p-*s))
uint32_t flags = 0;
if (*p=='^')
{
StrPoolDestroy(hostlist);
p = ++(*s);
flags |= HOSTLIST_POOL_FLAG_STRICT_MATCH;
}
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!HostlistPoolAddStrLen(hostlist, *s, p-*s, flags))
{
HostlistPoolDestroy(hostlist);
*hostlist = NULL;
return false;
}
@@ -32,12 +38,12 @@ static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
return true;
}
bool AppendHostlistItem(strpool **hostlist, char *s)
bool AppendHostlistItem(hostlist_pool **hostlist, char *s)
{
return addpool(hostlist,&s,s+strlen(s),NULL);
}
bool AppendHostList(strpool **hostlist, const char *filename)
bool AppendHostList(hostlist_pool **hostlist, const char *filename)
{
char *p, *e, s[256], *zbuf;
size_t zsize;
@@ -114,10 +120,10 @@ static bool LoadHostList(struct hostlist_file *hfile)
return true;
}
if (FILE_MOD_COMPARE(&hfile->mod_sig,&fsig)) return true; // up to date
StrPoolDestroy(&hfile->hostlist);
HostlistPoolDestroy(&hfile->hostlist);
if (!AppendHostList(&hfile->hostlist, hfile->filename))
{
StrPoolDestroy(&hfile->hostlist);
HostlistPoolDestroy(&hfile->hostlist);
return false;
}
hfile->mod_sig=fsig;
@@ -138,10 +144,10 @@ static bool LoadHostLists(struct hostlist_files_head *list)
return bres;
}
bool NonEmptyHostlist(strpool **hostlist)
bool NonEmptyHostlist(hostlist_pool **hostlist)
{
// add impossible hostname if the list is empty
return *hostlist ? true : StrPoolAddStrLen(hostlist, "@&()", 4);
return *hostlist ? true : HostlistPoolAddStrLen(hostlist, "@&()", 4, 0);
}
static void MakeAutolistsNonEmpty()
@@ -164,19 +170,34 @@ bool LoadAllHostLists()
static bool SearchHostList(strpool *hostlist, const char *host)
static bool SearchHostList(hostlist_pool *hostlist, const char *host)
{
if (hostlist)
{
const char *p = host;
bool bInHostList;
const struct hostlist_pool *hp;
bool bHostFull=true;
while (p)
{
bInHostList = StrPoolCheckStr(hostlist, p);
DLOG("hostlist check for %s : %s\n", p, bInHostList ? "positive" : "negative");
if (bInHostList) return true;
DLOG("hostlist check for %s : ", p);
hp = HostlistPoolGetStr(hostlist, p);
if (hp)
{
if ((hp->flags & HOSTLIST_POOL_FLAG_STRICT_MATCH) && !bHostFull)
{
DLOG("negative : strict_mismatch : %s != %s\n", p, host);
}
else
{
DLOG("positive\n");
return true;
}
}
else
DLOG("negative\n");
p = strchr(p, '.');
if (p) p++;
bHostFull = false;
}
}
return false;

View File

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

View File

@@ -35,6 +35,10 @@
#include "win.h"
#endif
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#ifdef __linux__
#include <libnetfilter_queue/libnetfilter_queue.h>
#define NF_DROP 0
@@ -271,6 +275,15 @@ exiterr:
return false;
}
static void notify_ready(void)
{
#ifdef USE_SYSTEMD
int r = sd_notify(0, "READY=1");
if (r < 0)
DLOG_ERR("sd_notify: %s\n", strerror(-r));
#endif
}
static int nfq_main(void)
{
uint8_t buf[16384] __attribute__((aligned));
@@ -280,7 +293,7 @@ static int nfq_main(void)
ssize_t rd;
sec_harden();
if (params.droproot && !droproot(params.uid, params.gid))
if (params.droproot && !droproot(params.uid, params.gid) || !dropcaps())
return 1;
print_id();
if (params.droproot && !test_list_files())
@@ -291,6 +304,8 @@ static int nfq_main(void)
if (!nfq_init(&h,&qh))
return 1;
notify_ready();
fd = nfq_fd(h);
do
{
@@ -484,7 +499,6 @@ static int win_main(const char *windivert_filter)
if (!logical_net_filter_match())
{
DLOG_CONDUP("logical network is not present. waiting it to appear.\n");
fflush(stdout);
do
{
if (bQuit)
@@ -497,7 +511,6 @@ static int win_main(const char *windivert_filter)
}
while (!logical_net_filter_match());
DLOG_CONDUP("logical network now present\n");
fflush(stdout);
}
if (!windivert_init(windivert_filter))
@@ -508,10 +521,6 @@ static int win_main(const char *windivert_filter)
DLOG_CONDUP("windivert initialized. capture is started.\n");
// cygwin auto flush fails when piping
fflush(stdout);
fflush(stderr);
for (id=0;;id++)
{
len = sizeof(packet);
@@ -574,10 +583,6 @@ static int win_main(const char *windivert_filter)
default:
DLOG("packet: id=%u drop\n", id);
}
// cygwin auto flush fails when piping
fflush(stdout);
fflush(stderr);
}
}
win_dark_deinit();
@@ -901,7 +906,7 @@ static bool parse_split_pos_list(char *opt, struct proto_pos *splits, int splits
return true;
}
static bool parse_domain_list(char *opt, strpool **pp)
static bool parse_domain_list(char *opt, hostlist_pool **pp)
{
char *e,*p,c;
@@ -1002,74 +1007,67 @@ static void SplitDebug(void)
}
static const char * tld[]={"com","org","net","edu","gov","biz"};
static void onetime_tls_mod(struct desync_profile *dp)
static bool onetime_tls_mod_blob(int profile_n, int fake_n, uint8_t fake_tls_mod, uint8_t *fake_tls, size_t *fake_tls_size, size_t fake_tls_buf_size, struct fake_tls_mod_cache *modcache)
{
const uint8_t *ext;
size_t extlen, slen;
if (dp->n && !(dp->fake_tls_mod & (FAKE_TLS_MOD_SET|FAKE_TLS_MOD_CUSTOM_FAKE)))
dp->fake_tls_mod |= FAKE_TLS_MOD_RND|FAKE_TLS_MOD_RND_SNI|FAKE_TLS_MOD_DUP_SID; // old behavior compat + dup_sid
if (!(dp->fake_tls_mod & ~FAKE_TLS_MOD_SAVE_MASK))
return; // nothing to do
if (!IsTLSClientHello(dp->fake_tls,dp->fake_tls_size,false) || (dp->fake_tls_size<(44+dp->fake_tls[43]))) // has session id ?
modcache->extlen_offset = modcache->padlen_offset = 0;
if (fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
{
DLOG_ERR("profile %d tls mod set but tls fake structure invalid\n", dp->n);
exit_clean(1);
}
if (dp->fake_tls_mod & FAKE_TLS_MOD_PADENCAP)
{
if (!TLSFindExtLen(dp->fake_tls,dp->fake_tls_size,&dp->fake_tls_extlen_offset))
if (!TLSFindExtLen(fake_tls,*fake_tls_size,&modcache->extlen_offset))
{
DLOG_ERR("profile %d padencap set but tls fake structure invalid\n", dp->n);
exit_clean(1);
DLOG_ERR("profile %d fake[%d] padencap set but tls fake structure invalid\n", profile_n, fake_n);
return false;
}
DLOG("profile %d fake tls extensions length offset : %zu\n", dp->n, dp->fake_tls_extlen_offset);
if (TLSFindExt(dp->fake_tls,dp->fake_tls_size,21,&ext,&extlen,false))
DLOG("profile %d fake[%d] tls extensions length offset : %zu\n", profile_n, fake_n, modcache->extlen_offset);
if (TLSFindExt(fake_tls,*fake_tls_size,21,&ext,&extlen,false))
{
if ((ext-dp->fake_tls+extlen)!=dp->fake_tls_size)
if ((ext-fake_tls+extlen)!=*fake_tls_size)
{
DLOG_ERR("profile %d fake tls padding ext is present but it's not at the end. padding ext offset %zu, padding ext size %zu, fake size %zu\n", dp->n, ext-dp->fake_tls, extlen, dp->fake_tls_size);
exit_clean(1);
DLOG_ERR("profile %d fake[%d] tls padding ext is present but it's not at the end. padding ext offset %zu, padding ext size %zu, fake size %zu\n", profile_n, fake_n, ext-fake_tls, extlen, *fake_tls_size);
return false;
}
dp->fake_tls_padlen_offset = ext-dp->fake_tls-2;
DLOG("profile %d fake tls padding ext is present, padding length offset %zu\n", dp->n, dp->fake_tls_padlen_offset);
modcache->padlen_offset = ext-fake_tls-2;
DLOG("profile %d fake[%d] tls padding ext is present, padding length offset %zu\n", profile_n, fake_n, modcache->padlen_offset);
}
else
{
if ((dp->fake_tls_size+4)>sizeof(dp->fake_tls))
if ((*fake_tls_size+4)>fake_tls_buf_size)
{
DLOG_ERR("profile %d fake tls padding is absent and there's not space to add it\n", dp->n);
exit_clean(1);
DLOG_ERR("profile %d fake[%d] tls padding is absent and there's no space to add it\n", profile_n, fake_n);
return false;
}
phton16(dp->fake_tls+dp->fake_tls_size,21);
dp->fake_tls_size+=2;
dp->fake_tls_padlen_offset=dp->fake_tls_size;
phton16(dp->fake_tls+dp->fake_tls_size,0);
dp->fake_tls_size+=2;
phton16(dp->fake_tls+dp->fake_tls_extlen_offset,pntoh16(dp->fake_tls+dp->fake_tls_extlen_offset)+4);
phton16(dp->fake_tls+3,pntoh16(dp->fake_tls+3)+4); // increase tls record len
phton24(dp->fake_tls+6,pntoh24(dp->fake_tls+6)+4); // increase tls handshake len
DLOG("profile %d fake tls padding is absent. added. padding ledgth offset %zu\n", dp->n, dp->fake_tls_padlen_offset);
phton16(fake_tls+*fake_tls_size,21);
*fake_tls_size+=2;
modcache->padlen_offset=*fake_tls_size;
phton16(fake_tls+*fake_tls_size,0);
*fake_tls_size+=2;
phton16(fake_tls+modcache->extlen_offset,pntoh16(fake_tls+modcache->extlen_offset)+4);
phton16(fake_tls+3,pntoh16(fake_tls+3)+4); // increase tls record len
phton24(fake_tls+6,pntoh24(fake_tls+6)+4); // increase tls handshake len
DLOG("profile %d fake[%d] tls padding is absent. added. padding length offset %zu\n", profile_n, fake_n, modcache->padlen_offset);
}
}
if (dp->fake_tls_mod & FAKE_TLS_MOD_RND_SNI)
if (fake_tls_mod & FAKE_TLS_MOD_RND_SNI)
{
if (!TLSFindExt(dp->fake_tls,dp->fake_tls_size,0,&ext,&extlen,false))
if (!TLSFindExt(fake_tls,*fake_tls_size,0,&ext,&extlen,false))
{
DLOG_ERR("profile %d rndsni set but tls fake does not have SNI\n", dp->n);
exit_clean(1);
DLOG_ERR("profile %d fake[%d] rndsni set but tls fake does not have SNI\n", profile_n, fake_n);
return false;
}
if (!TLSAdvanceToHostInSNI(&ext,&extlen,&slen))
{
DLOG_ERR("profile %d rndsni set but tls fake has invalid SNI structure\n", dp->n);
exit_clean(1);
DLOG_ERR("profile %d fake[%d] rndsni set but tls fake has invalid SNI structure\n", profile_n, fake_n);
return false;
}
if (!slen)
{
DLOG_ERR("profile %d rndsni set but tls fake has zero sized SNI\n", dp->n);
exit_clean(1);
DLOG_ERR("profile %d fake[%d] rndsni set but tls fake has zero sized SNI\n", profile_n, fake_n);
return false;
}
uint8_t *sni = dp->fake_tls + (ext - dp->fake_tls);
uint8_t *sni = fake_tls + (ext - fake_tls);
char *s1=NULL, *s2=NULL;
if (params.debug)
@@ -1095,11 +1093,66 @@ static void onetime_tls_mod(struct desync_profile *dp)
if (s1 && (s2 = malloc(slen+1)))
{
memcpy(s2,sni,slen); s2[slen]=0;
DLOG("profile %d generated random SNI : %s -> %s\n",dp->n,s1,s2);
DLOG("profile %d fake[%d] generated random SNI : %s -> %s\n",profile_n,fake_n,s1,s2);
}
free(s1); free(s2);
}
}
return true;
}
static bool onetime_tls_mod(struct desync_profile *dp)
{
if (dp->n && !(dp->fake_tls_mod & (FAKE_TLS_MOD_SET|FAKE_TLS_MOD_CUSTOM_FAKE)))
dp->fake_tls_mod |= FAKE_TLS_MOD_RND|FAKE_TLS_MOD_RND_SNI|FAKE_TLS_MOD_DUP_SID; // old behavior compat + dup_sid
if (!(dp->fake_tls_mod & ~FAKE_TLS_MOD_SAVE_MASK))
return true; // nothing to do
struct blob_item *fake_tls;
int n=0;
bool bMod=false;
LIST_FOREACH(fake_tls, &dp->fake_tls, next)
{
++n;
if (!IsTLSClientHello(fake_tls->data,fake_tls->size,false) || (fake_tls->size < (44+fake_tls->data[43]))) // has session id ?
{
DLOG("profile %d fake[%d] tls mod set but tls fake structure invalid. mod skipped.\n", dp->n, n);
continue;
}
bMod = true;
if (!fake_tls->extra)
{
fake_tls->extra = malloc(sizeof(struct fake_tls_mod_cache));
if (!fake_tls->extra) return false;
}
if (!onetime_tls_mod_blob(dp->n,n,dp->fake_tls_mod,fake_tls->data,&fake_tls->size,fake_tls->size_buf,(struct fake_tls_mod_cache*)fake_tls->extra))
return false;
}
if (!bMod)
DLOG_ERR("profile %d tls fake list does not have any valid TLS ClientHello\n", dp->n);
return bMod;
}
static void load_blob_to_collection(const char *filename, struct blob_collection_head *blobs, size_t max_size, size_t size_reserve)
{
struct blob_item *blob = blob_collection_add(blobs);
uint8_t *p;
if (!blob || (!(blob->data = malloc(max_size+size_reserve))))
{
DLOG_ERR("out of memory\n");
exit_clean(1);
}
blob->size = max_size;
load_file_or_exit(filename,blob->data,&blob->size);
p = realloc(blob->data,blob->size+size_reserve);
if (!p)
{
DLOG_ERR("out of memory\n");
exit_clean(1);
}
blob->data = p;
blob->size_buf = blob->size+size_reserve;
}
@@ -1409,6 +1462,7 @@ void check_dp(const struct desync_profile *dp)
int main(int argc, char **argv)
{
set_console_io_buffering();
set_env_exedir(argv[0]);
#ifdef __CYGWIN__
@@ -1986,12 +2040,10 @@ int main(int argc, char **argv)
dp->desync_any_proto = !optarg || atoi(optarg);
break;
case 38: /* dpi-desync-fake-http */
dp->fake_http_size = sizeof(dp->fake_http);
load_file_or_exit(optarg,dp->fake_http,&dp->fake_http_size);
load_blob_to_collection(optarg, &dp->fake_http, FAKE_MAX_TCP,0);
break;
case 39: /* dpi-desync-fake-tls */
dp->fake_tls_size = sizeof(dp->fake_tls);
load_file_or_exit(optarg,dp->fake_tls,&dp->fake_tls_size);
load_blob_to_collection(optarg, &dp->fake_tls, FAKE_MAX_TCP,4);
dp->fake_tls_mod |= FAKE_TLS_MOD_CUSTOM_FAKE;
break;
case 40: /* dpi-desync-fake-tls-mod */
@@ -2002,28 +2054,23 @@ int main(int argc, char **argv)
}
break;
case 41: /* dpi-desync-fake-unknown */
dp->fake_unknown_size = sizeof(dp->fake_unknown);
load_file_or_exit(optarg,dp->fake_unknown,&dp->fake_unknown_size);
load_blob_to_collection(optarg, &dp->fake_unknown, FAKE_MAX_TCP, 0);
break;
case 42: /* dpi-desync-fake-syndata */
dp->fake_syndata_size = sizeof(dp->fake_syndata);
load_file_or_exit(optarg,dp->fake_syndata,&dp->fake_syndata_size);
break;
case 43: /* dpi-desync-fake-quic */
dp->fake_quic_size = sizeof(dp->fake_quic);
load_file_or_exit(optarg,dp->fake_quic,&dp->fake_quic_size);
load_blob_to_collection(optarg, &dp->fake_quic, FAKE_MAX_UDP, 0);
break;
case 44: /* dpi-desync-fake-wireguard */
dp->fake_wg_size = sizeof(dp->fake_wg);
load_file_or_exit(optarg,dp->fake_wg,&dp->fake_wg_size);
load_blob_to_collection(optarg, &dp->fake_wg, FAKE_MAX_UDP, 0);
break;
case 45: /* dpi-desync-fake-dht */
dp->fake_dht_size = sizeof(dp->fake_dht);
load_file_or_exit(optarg,dp->fake_dht,&dp->fake_dht_size);
load_blob_to_collection(optarg, &dp->fake_dht, FAKE_MAX_UDP, 0);
break;
case 46: /* dpi-desync-fake-unknown-udp */
dp->fake_unknown_udp_size = sizeof(dp->fake_unknown_udp);
load_file_or_exit(optarg,dp->fake_unknown_udp,&dp->fake_unknown_udp_size);
load_blob_to_collection(optarg, &dp->fake_unknown_udp, FAKE_MAX_UDP, 0);
break;
case 47: /* dpi-desync-udplen-increment */
if (sscanf(optarg,"%d",&dp->udplen_increment)<1 || dp->udplen_increment>0x7FFF || dp->udplen_increment<-0x8000)
@@ -2469,7 +2516,16 @@ int main(int argc, char **argv)
if (AUTOTTL_ENABLED(dp->desync_autottl6))
DLOG("profile %d autottl ipv6 %u:%u-%u\n",dp->n,dp->desync_autottl6.delta,dp->desync_autottl6.min,dp->desync_autottl6.max);
split_compat(dp);
onetime_tls_mod(dp);
if (!dp_fake_defaults(dp))
{
DLOG_ERR("could not fill fake defaults\n");
exit_clean(1);
}
if (!onetime_tls_mod(dp))
{
DLOG_ERR("could not mod tls\n");
exit_clean(1);
}
#ifndef __CYGWIN__
if (params.droproot && dp->hostlist_auto && chown(dp->hostlist_auto->filename, params.uid, -1))
DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename);

View File

@@ -65,6 +65,7 @@ static int DLOG_VA(const char *format, int syslog_priority, bool condup, va_list
{
va_copy(args2,args);
DLOG_CON(format,syslog_priority,args2);
va_end(args2);
}
if (params.debug)
{
@@ -184,18 +185,8 @@ void dp_init(struct desync_profile *dp)
dp->desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT;
dp->desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT;
dp->desync_repeats = 1;
dp->fake_tls_size = sizeof(fake_tls_clienthello_default);
memcpy(dp->fake_tls,fake_tls_clienthello_default,dp->fake_tls_size);
dp->fake_tls_mod = 0;
dp->fake_http_size = strlen(fake_http_request_default);
memcpy(dp->fake_http,fake_http_request_default,dp->fake_http_size);
dp->fake_quic_size = 620; // must be 601+ for TSPU hack
dp->fake_quic[0] = 0x40; // russian TSPU QUIC short header fake
dp->fake_wg_size = 64;
dp->fake_dht_size = 64;
dp->fake_unknown_size = 256;
dp->fake_syndata_size = 16;
dp->fake_unknown_udp_size = 64;
dp->wscale=-1; // default - dont change scale factor (client)
dp->desync_ttl6 = 0xFF; // unused
dp->desync_badseq_increment = BADSEQ_INCREMENT_DEFAULT;
@@ -207,11 +198,55 @@ void dp_init(struct desync_profile *dp)
dp->hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT;
dp->filter_ipv4 = dp->filter_ipv6 = true;
}
bool dp_fake_defaults(struct desync_profile *dp)
{
struct blob_item *item;
if (blob_collection_empty(&dp->fake_http))
if (!blob_collection_add_blob(&dp->fake_http,fake_http_request_default,strlen(fake_http_request_default),0))
return false;
if (blob_collection_empty(&dp->fake_tls))
{
if (!blob_collection_add_blob(&dp->fake_tls,fake_tls_clienthello_default,sizeof(fake_tls_clienthello_default),4))
return false;
}
if (blob_collection_empty(&dp->fake_unknown))
{
if (!(item=blob_collection_add_blob(&dp->fake_unknown,NULL,256,0)))
return false;
memset(item->data,0,item->size);
}
if (blob_collection_empty(&dp->fake_quic))
{
if (!(item=blob_collection_add_blob(&dp->fake_quic,NULL,620,0)))
return false;
memset(item->data,0,item->size);
item->data[0] = 0x40;
}
if (blob_collection_empty(&dp->fake_wg))
{
if (!(item=blob_collection_add_blob(&dp->fake_wg,NULL,64,0)))
return false;
memset(item->data,0,item->size);
}
if (blob_collection_empty(&dp->fake_dht))
{
if (!(item=blob_collection_add_blob(&dp->fake_dht,NULL,64,0)))
return false;
memset(item->data,0,item->size);
}
if (blob_collection_empty(&dp->fake_unknown_udp))
{
if (!(item=blob_collection_add_blob(&dp->fake_unknown_udp,NULL,64,0)))
return false;
memset(item->data,0,item->size);
}
return true;
}
struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head)
{
struct desync_profile_list *entry = calloc(1,sizeof(struct desync_profile_list));
if (!entry) return NULL;
dp_init(&entry->dp);
// add to the tail
@@ -234,6 +269,13 @@ static void dp_clear_dynamic(struct desync_profile *dp)
ipset_collection_destroy(&dp->ips_collection_exclude);
port_filters_destroy(&dp->pf_tcp);
port_filters_destroy(&dp->pf_udp);
blob_collection_destroy(&dp->fake_http);
blob_collection_destroy(&dp->fake_tls);
blob_collection_destroy(&dp->fake_unknown);
blob_collection_destroy(&dp->fake_unknown_udp);
blob_collection_destroy(&dp->fake_quic);
blob_collection_destroy(&dp->fake_wg);
blob_collection_destroy(&dp->fake_dht);
HostFailPoolDestroy(&dp->hostlist_auto_fail_counters);
}
void dp_clear(struct desync_profile *dp)

View File

@@ -46,8 +46,16 @@
#define FAKE_TLS_MOD_RND_SNI 0x40
#define FAKE_TLS_MOD_PADENCAP 0x80
#define FAKE_MAX_TCP 1460
#define FAKE_MAX_UDP 1472
enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG };
struct fake_tls_mod_cache
{
size_t extlen_offset, padlen_offset;
};
struct desync_profile
{
int n; // number of the profile
@@ -74,12 +82,12 @@ struct desync_profile
autottl desync_autottl, desync_autottl6;
uint32_t desync_fooling_mode;
uint32_t desync_badseq_increment, desync_badseq_ack_increment;
uint8_t fake_http[1460],fake_unknown[1460],fake_syndata[1460],seqovl_pattern[1460],fsplit_pattern[1460];
uint8_t fake_unknown_udp[1472],udplen_pattern[1472],fake_quic[1472],fake_wg[1472],fake_dht[1472];
size_t fake_http_size,fake_quic_size,fake_wg_size,fake_dht_size,fake_unknown_size,fake_syndata_size,fake_unknown_udp_size;
uint8_t fake_tls[1460],fake_tls_mod;
size_t fake_tls_size, fake_tls_extlen_offset, fake_tls_padlen_offset;
struct blob_collection_head fake_http,fake_tls,fake_unknown,fake_unknown_udp,fake_quic,fake_wg,fake_dht;
uint8_t fake_syndata[FAKE_MAX_TCP],seqovl_pattern[FAKE_MAX_TCP],fsplit_pattern[FAKE_MAX_TCP],udplen_pattern[FAKE_MAX_UDP];
size_t fake_syndata_size;
uint8_t fake_tls_mod;
int udplen_increment;
@@ -113,6 +121,7 @@ void dp_entry_destroy(struct desync_profile_list *entry);
void dp_list_destroy(struct desync_profile_list_head *head);
bool dp_list_have_autohostlist(struct desync_profile_list_head *head);
void dp_init(struct desync_profile *dp);
bool dp_fake_defaults(struct desync_profile *dp);
void dp_clear(struct desync_profile *dp);
struct params_s

View File

@@ -31,6 +31,9 @@
free(elem); \
return false; \
}
#define ADD_HOSTLIST_POOL(etype, ppool, keystr, keystr_len, flg) \
ADD_STR_POOL(etype,ppool,keystr,keystr_len); \
elem->flags = flg;
#undef uthash_nonfatal_oom
@@ -42,27 +45,31 @@ static void ut_oom_recover(void *elem)
}
// for not zero terminated strings
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
bool HostlistPoolAddStrLen(hostlist_pool **pp, const char *s, size_t slen, uint32_t flags)
{
ADD_STR_POOL(strpool, pp, s, slen)
ADD_HOSTLIST_POOL(hostlist_pool, pp, s, slen, flags)
return true;
}
// for zero terminated strings
bool StrPoolAddStr(strpool **pp, const char *s)
bool HostlistPoolAddStr(hostlist_pool **pp, const char *s, uint32_t flags)
{
return StrPoolAddStrLen(pp, s, strlen(s));
return HostlistPoolAddStrLen(pp, s, strlen(s), flags);
}
bool StrPoolCheckStr(strpool *p, const char *s)
hostlist_pool *HostlistPoolGetStr(hostlist_pool *p, const char *s)
{
strpool *elem;
hostlist_pool *elem;
HASH_FIND_STR(p, s, elem);
return elem != NULL;
return elem;
}
bool HostlistPoolCheckStr(hostlist_pool *p, const char *s)
{
return !!HostlistPoolGetStr(p,s);
}
void StrPoolDestroy(strpool **pp)
void HostlistPoolDestroy(hostlist_pool **pp)
{
DESTROY_STR_POOL(strpool, pp)
DESTROY_STR_POOL(hostlist_pool, pp)
}
@@ -178,7 +185,7 @@ struct hostlist_file *hostlist_files_add(struct hostlist_files_head *head, const
static void hostlist_files_entry_destroy(struct hostlist_file *entry)
{
free(entry->filename);
StrPoolDestroy(&entry->hostlist);
HostlistPoolDestroy(&entry->hostlist);
free(entry);
}
void hostlist_files_destroy(struct hostlist_files_head *head)
@@ -510,3 +517,64 @@ bool port_filters_deny_if_empty(struct port_filters_head *head)
if (LIST_FIRST(head)) return true;
return pf_parse("0",&pf) && port_filter_add(head,&pf);
}
struct blob_item *blob_collection_add(struct blob_collection_head *head)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
if (entry)
{
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
{
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
LIST_INSERT_AFTER(iteml, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
}
return entry;
}
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve)
{
struct blob_item *entry = calloc(1,sizeof(struct blob_item));
if (!entry) return NULL;
if (!(entry->data = malloc(size+size_reserve)))
{
free(entry);
return NULL;
}
if (data) memcpy(entry->data,data,size);
entry->size = size;
entry->size_buf = size+size_reserve;
// insert to the end
struct blob_item *itemc,*iteml=LIST_FIRST(head);
if (iteml)
{
while ((itemc=LIST_NEXT(iteml,next))) iteml = itemc;
LIST_INSERT_AFTER(iteml, entry, next);
}
else
LIST_INSERT_HEAD(head, entry, next);
return entry;
}
void blob_collection_destroy(struct blob_collection_head *head)
{
struct blob_item *entry;
while ((entry = LIST_FIRST(head)))
{
LIST_REMOVE(entry, next);
free(entry->extra);
free(entry->data);
free(entry);
}
}
bool blob_collection_empty(const struct blob_collection_head *head)
{
return !LIST_FIRST(head);
}

View File

@@ -12,15 +12,18 @@
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
typedef struct strpool {
char *str; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} strpool;
#define HOSTLIST_POOL_FLAG_STRICT_MATCH 1
void StrPoolDestroy(strpool **pp);
bool StrPoolAddStr(strpool **pp,const char *s);
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
bool StrPoolCheckStr(strpool *p,const char *s);
typedef struct hostlist_pool {
char *str; /* key */
uint32_t flags; /* custom data */
UT_hash_handle hh; /* makes this structure hashable */
} hostlist_pool;
void HostlistPoolDestroy(hostlist_pool **pp);
bool HostlistPoolAddStr(hostlist_pool **pp, const char *s, uint32_t flags);
bool HostlistPoolAddStrLen(hostlist_pool **pp, const char *s, size_t slen, uint32_t flags);
hostlist_pool *HostlistPoolGetStr(hostlist_pool *p, const char *s);
struct str_list {
char *str;
@@ -29,10 +32,10 @@ struct str_list {
LIST_HEAD(str_list_head, str_list);
typedef struct hostfail_pool {
char *str; /* key */
int counter; /* value */
time_t expire; /* when to expire record (unixtime) */
UT_hash_handle hh; /* makes this structure hashable */
char *str; /* key */
int counter; /* value */
time_t expire; /* when to expire record (unixtime) */
UT_hash_handle hh; /* makes this structure hashable */
} hostfail_pool;
void HostFailPoolDestroy(hostfail_pool **pp);
@@ -51,7 +54,7 @@ void strlist_destroy(struct str_list_head *head);
struct hostlist_file {
char *filename;
file_mod_sig mod_sig;
strpool *hostlist;
hostlist_pool *hostlist;
LIST_ENTRY(hostlist_file) next;
};
LIST_HEAD(hostlist_files_head, hostlist_file);
@@ -143,3 +146,17 @@ bool port_filter_add(struct port_filters_head *head, const port_filter *pf);
void port_filters_destroy(struct port_filters_head *head);
bool port_filters_in_range(const struct port_filters_head *head, uint16_t port);
bool port_filters_deny_if_empty(struct port_filters_head *head);
struct blob_item {
uint8_t *data; // main data blob
size_t size; // main data blob size
size_t size_buf;// main data blob allocated size
void *extra; // any data without size
LIST_ENTRY(blob_item) next;
};
LIST_HEAD(blob_collection_head, blob_item);
struct blob_item *blob_collection_add(struct blob_collection_head *head);
struct blob_item *blob_collection_add_blob(struct blob_collection_head *head, const void *data, size_t size, size_t size_reserve);
void blob_collection_destroy(struct blob_collection_head *head);
bool blob_collection_empty(const struct blob_collection_head *head);

View File

@@ -844,7 +844,16 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
return !memcmp(data + pn_offset + pkn_len + cryptlen, atag, 16);
}
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len)
struct range64
{
uint64_t offset,len;
};
#define MAX_DEFRAG_PIECES 128
static int cmp_range64(const void * a, const void * b)
{
return (((struct range64*)a)->offset < ((struct range64*)b)->offset) ? -1 : (((struct range64*)a)->offset > ((struct range64*)b)->offset) ? 1 : 0;
}
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull)
{
// Crypto frame can be split into multiple chunks
// chromium randomly splits it and pads with zero/one bytes to force support the standard
@@ -853,13 +862,15 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
if (*defrag_len<10) return false;
uint8_t *defrag_data = defrag+10;
size_t defrag_data_len = *defrag_len-10;
uint8_t ft;
uint64_t offset,sz,szmax=0,zeropos=0,pos=0;
bool found=false;
struct range64 ranges[MAX_DEFRAG_PIECES];
int i,range=0;
while(pos<clean_len)
{
// frame type
ft = clean[pos];
pos++;
if (ft>1) // 00 - padding, 01 - ping
@@ -867,6 +878,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
if (ft!=6) return false; // dont want to know all possible frame type formats
if (pos>=clean_len) return false;
if (range>=MAX_DEFRAG_PIECES) return false;
if ((pos+tvb_get_size(clean[pos])>=clean_len)) return false;
pos += tvb_get_varint(clean+pos, &offset);
@@ -875,7 +887,7 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
pos += tvb_get_varint(clean+pos, &sz);
if ((pos+sz)>clean_len) return false;
if ((offset+sz)>defrag_data_len) return false;
if ((offset+sz)>defrag_data_len) return false; // defrag buf overflow
if (zeropos < offset)
// make sure no uninitialized gaps exist in case of not full fragment coverage
memset(defrag_data+zeropos,0,offset-zeropos);
@@ -886,6 +898,10 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
found=true;
pos+=sz;
ranges[range].offset = offset;
ranges[range].len = sz;
range++;
}
}
if (found)
@@ -897,6 +913,23 @@ bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,siz
phton64(defrag+2,szmax);
defrag[2] |= 0xC0; // 64 bit value
*defrag_len = (size_t)(szmax+10);
qsort(ranges, range, sizeof(*ranges), cmp_range64);
//for(i=0 ; i<range ; i++)
// printf("RANGE %zu len %zu\n",ranges[i].offset,ranges[i].len);
for(i=0,offset=0,*bFull=true ; i<range ; i++)
{
if (ranges[i].offset!=offset)
{
*bFull = false;
break;
}
offset += ranges[i].len;
}
//printf("bFull=%u\n",*bFull);
}
return found;
}

View File

@@ -87,5 +87,6 @@ uint8_t QUICDraftVersion(uint32_t version);
bool QUICExtractDCID(const uint8_t *data, size_t len, quic_cid_t *cid);
bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, size_t *clean_len);
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len);
// returns true if crypto frames were found . bFull = true if crypto frame fragments have full coverage
bool QUICDefragCrypto(const uint8_t *clean,size_t clean_len, uint8_t *defrag,size_t *defrag_len, bool *bFull);
//bool QUICExtractHostFromInitial(const uint8_t *data, size_t data_len, char *host, size_t len_host, bool *bDecryptOK, bool *bIsCryptoHello);

View File

@@ -287,7 +287,7 @@ bool can_drop_root(void)
{
#ifdef __linux__
// has some caps
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID)|(1<<CAP_SETPCAP));
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID));
#else
// effective root
return !geteuid();
@@ -319,11 +319,7 @@ bool droproot(uid_t uid, gid_t gid)
DLOG_PERROR("setuid");
return false;
}
#ifdef __linux__
return dropcaps();
#else
return true;
#endif
}
void print_id(void)

View File

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

View File

@@ -78,6 +78,13 @@ char *strncasestr(const char *s, const char *find, size_t slen)
return (char *)s;
}
bool str_ends_with(const char *s, const char *suffix)
{
size_t slen = strlen(s);
size_t suffix_len = strlen(suffix);
return suffix_len <= slen && !strcmp(s + slen - suffix_len, suffix);
}
bool load_file(const char *filename, void *buffer, size_t *buffer_size)
{
FILE *F;
@@ -383,6 +390,11 @@ bool pf_is_empty(const port_filter *pf)
return !pf->neg && !pf->from && !pf->to;
}
void set_console_io_buffering(void)
{
setvbuf(stdout, NULL, _IOLBF, 0);
setvbuf(stderr, NULL, _IOLBF, 0);
}
bool set_env_exedir(const char *argv0)
{
@@ -560,4 +572,20 @@ bool socket_wait_notsent(int sfd, unsigned int delay_ms, unsigned int *wasted_ms
}
return false;
}
int is_wsl(void)
{
struct utsname buf;
if (uname(&buf) != 0)
return -1;
if (strcmp(buf.sysname, "Linux") != 0)
return 0;
if (str_ends_with(buf.release, "microsoft-standard-WSL2"))
return 2;
if (str_ends_with(buf.release, "-Microsoft"))
return 1;
return 0;
}
#endif

View File

@@ -7,6 +7,7 @@
#include <netdb.h>
#include <stdio.h>
#include <time.h>
#include <sys/utsname.h>
// this saves memory. sockaddr_storage is larger than required. it can be 128 bytes. sockaddr_in6 is 28 bytes.
typedef union
@@ -22,6 +23,8 @@ void rtrim(char *s);
void replace_char(char *s, char from, char to);
char *strncasestr(const char *s,const char *find, size_t slen);
bool str_ends_with(const char *s, const char *suffix);
bool load_file(const char *filename,void *buffer,size_t *buffer_size);
bool append_to_list_file(const char *filename, const char *s);
@@ -82,6 +85,7 @@ bool pf_in_range(uint16_t port, const port_filter *pf);
bool pf_parse(const char *s, port_filter *pf);
bool pf_is_empty(const port_filter *pf);
void set_console_io_buffering(void);
bool set_env_exedir(const char *argv0);
#ifndef IN_LOOPBACK
@@ -132,4 +136,6 @@ void msleep(unsigned int ms);
bool socket_supports_notsent();
bool socket_has_notsent(int sfd);
bool socket_wait_notsent(int sfd, unsigned int delay_ms, unsigned int *wasted_ms);
int is_wsl();
#endif

View File

@@ -4,7 +4,7 @@
#include "helpers.h"
// inplace tolower() and add to pool
static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
static bool addpool(hostlist_pool **hostlist, char **s, const char *end, int *ct)
{
char *p=*s;
@@ -17,10 +17,16 @@ static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
else
{
// advance until eol lowering all chars
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!StrPoolAddStrLen(hostlist, *s, p-*s))
uint32_t flags = 0;
if (*p=='^')
{
StrPoolDestroy(hostlist);
p = ++(*s);
flags |= HOSTLIST_POOL_FLAG_STRICT_MATCH;
}
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p);
if (!HostlistPoolAddStrLen(hostlist, *s, p-*s, flags))
{
HostlistPoolDestroy(hostlist);
*hostlist = NULL;
return false;
}
@@ -32,12 +38,12 @@ static bool addpool(strpool **hostlist, char **s, const char *end, int *ct)
return true;
}
bool AppendHostlistItem(strpool **hostlist, char *s)
bool AppendHostlistItem(hostlist_pool **hostlist, char *s)
{
return addpool(hostlist,&s,s+strlen(s),NULL);
}
bool AppendHostList(strpool **hostlist, const char *filename)
bool AppendHostList(hostlist_pool **hostlist, const char *filename)
{
char *p, *e, s[256], *zbuf;
size_t zsize;
@@ -114,10 +120,10 @@ static bool LoadHostList(struct hostlist_file *hfile)
return true;
}
if (FILE_MOD_COMPARE(&hfile->mod_sig,&fsig)) return true; // up to date
StrPoolDestroy(&hfile->hostlist);
HostlistPoolDestroy(&hfile->hostlist);
if (!AppendHostList(&hfile->hostlist, hfile->filename))
{
StrPoolDestroy(&hfile->hostlist);
HostlistPoolDestroy(&hfile->hostlist);
return false;
}
hfile->mod_sig=fsig;
@@ -138,10 +144,10 @@ static bool LoadHostLists(struct hostlist_files_head *list)
return bres;
}
bool NonEmptyHostlist(strpool **hostlist)
bool NonEmptyHostlist(hostlist_pool **hostlist)
{
// add impossible hostname if the list is empty
return *hostlist ? true : StrPoolAddStrLen(hostlist, "@&()", 4);
return *hostlist ? true : HostlistPoolAddStrLen(hostlist, "@&()", 4, 0);
}
static void MakeAutolistsNonEmpty()
@@ -164,19 +170,34 @@ bool LoadAllHostLists()
static bool SearchHostList(strpool *hostlist, const char *host)
static bool SearchHostList(hostlist_pool *hostlist, const char *host)
{
if (hostlist)
{
const char *p = host;
bool bInHostList;
const struct hostlist_pool *hp;
bool bHostFull=true;
while (p)
{
bInHostList = StrPoolCheckStr(hostlist, p);
VPRINT("hostlist check for %s : %s\n", p, bInHostList ? "positive" : "negative");
if (bInHostList) return true;
VPRINT("hostlist check for %s : ", p);
hp = HostlistPoolGetStr(hostlist, p);
if (hp)
{
if ((hp->flags & HOSTLIST_POOL_FLAG_STRICT_MATCH) && !bHostFull)
{
VPRINT("negative : strict_mismatch : %s != %s\n", p, host);
}
else
{
VPRINT("positive\n");
return true;
}
}
else
VPRINT("negative\n");
p = strchr(p, '.');
if (p) p++;
bHostFull = false;
}
}
return false;

View File

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

View File

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

View File

@@ -31,6 +31,9 @@
free(elem); \
return false; \
}
#define ADD_HOSTLIST_POOL(etype, ppool, keystr, keystr_len, flg) \
ADD_STR_POOL(etype,ppool,keystr,keystr_len); \
elem->flags = flg;
#undef uthash_nonfatal_oom
@@ -42,27 +45,31 @@ static void ut_oom_recover(void *elem)
}
// for not zero terminated strings
bool StrPoolAddStrLen(strpool **pp, const char *s, size_t slen)
bool HostlistPoolAddStrLen(hostlist_pool **pp, const char *s, size_t slen, uint32_t flags)
{
ADD_STR_POOL(strpool, pp, s, slen)
ADD_HOSTLIST_POOL(hostlist_pool, pp, s, slen, flags)
return true;
}
// for zero terminated strings
bool StrPoolAddStr(strpool **pp, const char *s)
bool HostlistPoolAddStr(hostlist_pool **pp, const char *s, uint32_t flags)
{
return StrPoolAddStrLen(pp, s, strlen(s));
return HostlistPoolAddStrLen(pp, s, strlen(s), flags);
}
bool StrPoolCheckStr(strpool *p, const char *s)
hostlist_pool *HostlistPoolGetStr(hostlist_pool *p, const char *s)
{
strpool *elem;
hostlist_pool *elem;
HASH_FIND_STR(p, s, elem);
return elem != NULL;
return elem;
}
bool HostlistPoolCheckStr(hostlist_pool *p, const char *s)
{
return !!HostlistPoolGetStr(p,s);
}
void StrPoolDestroy(strpool **pp)
void HostlistPoolDestroy(hostlist_pool **pp)
{
DESTROY_STR_POOL(strpool, pp)
DESTROY_STR_POOL(hostlist_pool, pp)
}
@@ -178,7 +185,7 @@ struct hostlist_file *hostlist_files_add(struct hostlist_files_head *head, const
static void hostlist_files_entry_destroy(struct hostlist_file *entry)
{
free(entry->filename);
StrPoolDestroy(&entry->hostlist);
HostlistPoolDestroy(&entry->hostlist);
free(entry);
}
void hostlist_files_destroy(struct hostlist_files_head *head)

View File

@@ -12,15 +12,18 @@
#define HASH_FUNCTION HASH_BER
#include "uthash.h"
typedef struct strpool {
char *str; /* key */
UT_hash_handle hh; /* makes this structure hashable */
} strpool;
#define HOSTLIST_POOL_FLAG_STRICT_MATCH 1
void StrPoolDestroy(strpool **pp);
bool StrPoolAddStr(strpool **pp,const char *s);
bool StrPoolAddStrLen(strpool **pp,const char *s,size_t slen);
bool StrPoolCheckStr(strpool *p,const char *s);
typedef struct hostlist_pool {
char *str; /* key */
uint32_t flags; /* custom data */
UT_hash_handle hh; /* makes this structure hashable */
} hostlist_pool;
void HostlistPoolDestroy(hostlist_pool **pp);
bool HostlistPoolAddStr(hostlist_pool **pp, const char *s, uint32_t flags);
bool HostlistPoolAddStrLen(hostlist_pool **pp, const char *s, size_t slen, uint32_t flags);
hostlist_pool *HostlistPoolGetStr(hostlist_pool *p, const char *s);
struct str_list {
char *str;
@@ -29,10 +32,10 @@ struct str_list {
LIST_HEAD(str_list_head, str_list);
typedef struct hostfail_pool {
char *str; /* key */
int counter; /* value */
time_t expire; /* when to expire record (unixtime) */
UT_hash_handle hh; /* makes this structure hashable */
char *str; /* key */
int counter; /* value */
time_t expire; /* when to expire record (unixtime) */
UT_hash_handle hh; /* makes this structure hashable */
} hostfail_pool;
void HostFailPoolDestroy(hostfail_pool **pp);
@@ -51,7 +54,7 @@ void strlist_destroy(struct str_list_head *head);
struct hostlist_file {
char *filename;
file_mod_sig mod_sig;
strpool *hostlist;
hostlist_pool *hostlist;
LIST_ENTRY(hostlist_file) next;
};
LIST_HEAD(hostlist_files_head, hostlist_file);

View File

@@ -263,7 +263,7 @@ bool can_drop_root(void)
{
#ifdef __linux__
// has some caps
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID)|(1<<CAP_SETPCAP));
return checkpcap((1<<CAP_SETUID)|(1<<CAP_SETGID));
#else
// effective root
return !geteuid();
@@ -295,11 +295,7 @@ bool droproot(uid_t uid, gid_t gid)
DLOG_PERROR("setuid");
return false;
}
#ifdef __linux__
return dropcaps();
#else
return true;
#endif
}
void print_id(void)

View File

@@ -433,7 +433,7 @@ static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname
{
VPRINT("auto hostlist (profile %d) : adding %s to %s\n", dp->n, hostname, dp->hostlist_auto->filename);
HOSTLIST_DEBUGLOG_APPEND("%s : profile %d : client %s : proto %s : adding to %s", hostname, dp->n, client_ip_port, l7proto_str(l7proto), dp->hostlist_auto->filename);
if (!StrPoolAddStr(&dp->hostlist_auto->hostlist, hostname))
if (!HostlistPoolAddStr(&dp->hostlist_auto->hostlist, hostname, 0))
{
DLOG_ERR("StrPoolAddStr out of memory\n");
return;

View File

@@ -121,7 +121,6 @@ static bool test_list_files()
struct hostlist_file *hfile;
struct ipset_file *ifile;
printf("1\n");
LIST_FOREACH(hfile, &params.hostlists, next)
if (hfile->filename && !file_open_test(hfile->filename, O_RDONLY))
{
@@ -136,7 +135,6 @@ printf("1\n");
DLOG_ERR("cannot access ipset file '%s'\n",ifile->filename);
return false;
}
printf("2\n");
return true;
}
@@ -535,7 +533,7 @@ static bool parse_pf_list(char *opt, struct port_filters_head *pfl)
return true;
}
static bool parse_domain_list(char *opt, strpool **pp)
static bool parse_domain_list(char *opt, hostlist_pool **pp)
{
char *e,*p,c;
@@ -1476,7 +1474,7 @@ void parse_params(int argc, char *argv[])
params.binds_last=0; // default bind to all
}
if (!params.resolver_threads) params.resolver_threads = 5 + params.maxconn/50;
VPRINT("adding low-priority default empty desync profile\n");
// add default empty profile
if (!(dpl = dp_list_add(&params.desync_profiles)))
@@ -1492,6 +1490,10 @@ void parse_params(int argc, char *argv[])
fprintf(stderr, "could not chown %s. log file may not be writable after privilege drop\n", params.debug_logfile);
if (params.droproot && *params.hostlist_auto_debuglog && chown(params.hostlist_auto_debuglog, params.uid, -1))
DLOG_ERR("could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", params.hostlist_auto_debuglog);
#ifdef __linux__
bool bHasMSS=false, bHasOOB=false, bHasDisorder=false;
#endif
LIST_FOREACH(dpl, &params.desync_profiles, next)
{
dp = &dpl->dp;
@@ -1502,7 +1504,22 @@ void parse_params(int argc, char *argv[])
}
if (params.droproot && dp->hostlist_auto && chown(dp->hostlist_auto->filename, params.uid, -1))
DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", dp->hostlist_auto->filename);
#ifdef __linux__
if (dp->mss) bHasMSS=true;
if (dp->oob || dp->oob_http || dp->oob_tls) bHasOOB=true;
if (dp->disorder || dp->disorder_http || dp->disorder_tls) bHasDisorder=true;
#endif
}
#ifdef __linux__
if (is_wsl()==1)
{
if (!params.nosplice) DLOG_CONDUP("WARNING ! WSL1 may have problems with splice. Consider using `--nosplice`.\n");
if (bHasMSS) DLOG_CONDUP("WARNING ! WSL1 does not support MSS socket option. MSS will likely fail.\n");
if (bHasOOB) DLOG_CONDUP("WARNING ! WSL1 does not support OOB. OOB will likely fail.\n");
if (bHasDisorder) DLOG_CONDUP("WARNING ! Windows retransmits whole TCP segment. Disorder will not function properly.\n");
fflush(stdout);
}
#endif
if (!LoadAllHostLists())
{
@@ -1696,6 +1713,7 @@ int main(int argc, char *argv[])
struct salisten_s list[MAX_BINDS];
char ip_port[48];
set_console_io_buffering();
set_env_exedir(argv[0]);
srand(time(NULL));
mask_from_preflen6_prepare();
@@ -1948,6 +1966,10 @@ int main(int argc, char *argv[])
sec_harden();
if (params.droproot && !droproot(params.uid,params.gid))
goto exiterr;
#ifdef __linux__
if (!dropcaps())
goto exiterr;
#endif
print_id();
if (params.droproot && !test_list_files())
goto exiterr;

View File

@@ -16,6 +16,10 @@
#include <fcntl.h>
#include <netdb.h>
#ifdef USE_SYSTEMD
#include <systemd/sd-daemon.h>
#endif
#include "tpws.h"
#include "tpws_conn.h"
#include "redirect.h"
@@ -25,6 +29,15 @@
#include "hostlist.h"
#include "linux_compat.h"
static void notify_ready(void)
{
#ifdef USE_SYSTEMD
int r = sd_notify(0, "READY=1");
if (r < 0)
DLOG_ERR("sd_notify: %s\n", strerror(-r));
#endif
}
// keep separate legs counter. counting every time thousands of legs can consume cpu
static int legs_local, legs_remote;
/*
@@ -1542,6 +1555,8 @@ int event_loop(const int *listen_fd, size_t listen_fd_ct)
VPRINT("initialized multi threaded resolver with %d threads\n",resolver_thread_count());
}
notify_ready();
for(;;)
{
ReloadCheck();
@@ -1755,8 +1770,6 @@ int event_loop(const int *listen_fd, size_t listen_fd_ct)
// at least one leg was removed. recount legs
print_legs();
}
fflush(stderr); fflush(stdout); // for console messages
}
ex: