Compare commits

..

5 Commits

Author SHA1 Message Date
bol-van
45913cc2cd docs: windows.txt change encoding to UTF8 2024-04-26 22:03:53 +03:00
bol-van
92c6ccf38f docs: iptables fix regression 2024-04-26 22:02:23 +03:00
bol-van
1a9a676c94 winws 2024-04-26 21:36:27 +03:00
bol-van
3098727f4c ip2net,mdig: improve makefile for windows 2024-04-26 17:24:42 +03:00
bol-van
147af10b61 tpws: move portfilter code 2024-04-26 17:23:03 +03:00
50 changed files with 1663 additions and 171 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
binaries/win64/ip2net.exe Normal file

Binary file not shown.

BIN
binaries/win64/mdig.exe Normal file

Binary file not shown.

View File

@ -0,0 +1,9 @@
Standalone version in zapret-winws folder !!
From this folder winws can be started only from cygwin shell.
Cygwin refuses to start winws if a copy of cygwin1.dll is present !
How to get win7 and winws compatible version of cygwin :
curl -O https://www.cygwin.com/setup-x86_64.exe
setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215

BIN
binaries/win64/winws.exe Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,2 @@
set WINWS1=--wf-l3=ipv4,ipv6 --wf-tcp=80,443 --dpi-desync=fake,split --dpi-desync-ttl=7 --dpi-desync-fooling=md5sig
schtasks /Create /F /TN winws1 /NP /RU "" /SC onstart /TR "\"%~dp0winws.exe\" %WINWS1%"

View File

@ -0,0 +1,2 @@
schtasks /End /TN winws1
schtasks /Delete /TN winws1 /F

View File

@ -0,0 +1 @@
schtasks /Run /TN winws1

View File

@ -0,0 +1 @@
schtasks /End /TN winws1

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -17,6 +17,7 @@ TPWS_UID=${TPWS_UID:-1}
TPWS_GID=${TPWS_GID:-3003} TPWS_GID=${TPWS_GID:-3003}
NFQWS=${NFQWS:-${ZAPRET_BASE}/nfq/nfqws} NFQWS=${NFQWS:-${ZAPRET_BASE}/nfq/nfqws}
DVTWS=${DVTWS:-${ZAPRET_BASE}/nfq/dvtws} DVTWS=${DVTWS:-${ZAPRET_BASE}/nfq/dvtws}
WINWS=${WINWS:-${ZAPRET_BASE}/nfq/winws}
TPWS=${TPWS:-${ZAPRET_BASE}/tpws/tpws} TPWS=${TPWS:-${ZAPRET_BASE}/tpws/tpws}
MDIG=${MDIG:-${ZAPRET_BASE}/mdig/mdig} MDIG=${MDIG:-${ZAPRET_BASE}/mdig/mdig}
DESYNC_MARK=0x10000000 DESYNC_MARK=0x10000000
@ -191,7 +192,9 @@ mdig_resolve()
{ {
# $1 - ip version 4/6 # $1 - ip version 4/6
# $2 - hostname # $2 - hostname
echo "$2" | "$MDIG" --family=$1 | head -n 1
# windows version of mdig outputs 0D0A line ending. remove 0D.
echo "$2" | "$MDIG" --family=$1 | head -n 1 | tr -d '\r'
} }
check_system() check_system()
@ -229,6 +232,12 @@ check_system()
PKTWSD=dvtws PKTWSD=dvtws
FWTYPE=mpf FWTYPE=mpf
;; ;;
CYGWIN*)
UNAME=CYGWIN
PKTWS="$WINWS"
PKTWSD=winws
FWTYPE=windivert
;;
*) *)
echo $UNAME not supported echo $UNAME not supported
exitp 5 exitp 5
@ -256,7 +265,7 @@ check_prerequisites()
{ {
echo \* checking prerequisites echo \* checking prerequisites
[ "$UNAME" = Darwin -o -x "$PKTWS" ] && [ -x "$TPWS" ] && [ -x "$MDIG" ] || { [ "$UNAME" = Darwin -o -x "$PKTWS" ] && [ "$UNAME" = CYGWIN -o -x "$TPWS" ] && [ -x "$MDIG" ] || {
local target local target
case $UNAME in case $UNAME in
Darwin) Darwin)
@ -323,6 +332,9 @@ check_prerequisites()
[ "$UNAME" = "Darwin" ] && SKIP_PKTWS=1 [ "$UNAME" = "Darwin" ] && SKIP_PKTWS=1
pf_save pf_save
;; ;;
CYGWIN)
SKIP_TPWS=1
;;
esac esac
for prog in $progs; do for prog in $progs; do
@ -446,6 +458,7 @@ curl_with_dig()
return 6 return 6
} }
shift ; shift ; shift shift ; shift ; shift
ALL_PROXY="$ALL_PROXY" curl $connect_to "$@" ALL_PROXY="$ALL_PROXY" curl $connect_to "$@"
} }
@ -558,6 +571,10 @@ pktws_ipt_prepare()
opf) opf)
opf_prepare_dvtws $1 $2 opf_prepare_dvtws $1 $2
;; ;;
windivert)
WF="--wf-l3=ipv${IPV} --wf-${1}=$2"
;;
esac esac
} }
pktws_ipt_unprepare() pktws_ipt_unprepare()
@ -578,6 +595,9 @@ pktws_ipt_unprepare()
opf) opf)
pf_restore pf_restore
;; ;;
windivert)
unset WF
;;
esac esac
} }
@ -636,6 +656,9 @@ pktws_start()
FreeBSD|OpenBSD) FreeBSD|OpenBSD)
"$DVTWS" --port=$IPFW_DIVERT_PORT "$@" >/dev/null & "$DVTWS" --port=$IPFW_DIVERT_PORT "$@" >/dev/null &
;; ;;
CYGWIN)
"$WINWS" $WF "$@" >/dev/null &
;;
esac esac
PID=$! PID=$!
# give some time to initialize # give some time to initialize
@ -711,7 +734,7 @@ pktws_curl_test()
# $1 - test function # $1 - test function
# $2 - domain # $2 - domain
# $3,$4,$5, ... - nfqws/dvtws params # $3,$4,$5, ... - nfqws/dvtws params
echo - checking $PKTWSD $3 $4 $5 $6 $7 $8 $9 echo - checking $PKTWSD ${WF:+$WF }$3 $4 $5 $6 $7 $8 $9
ws_curl_test pktws_start "$@" ws_curl_test pktws_start "$@"
} }
xxxws_curl_test_update() xxxws_curl_test_update()
@ -726,7 +749,7 @@ xxxws_curl_test_update()
shift shift
$xxxf $testf $dom "$@" $xxxf $testf $dom "$@"
code=$? code=$?
[ $code = 0 ] && strategy="${strategy:-$@}" [ $code = 0 ] && strategy="${WF:+$WF }${strategy:-$@}"
return $code return $code
} }
pktws_curl_test_update() pktws_curl_test_update()
@ -1329,6 +1352,10 @@ pingtest()
OpenBSD) OpenBSD)
ping -c 1 -w 1 $1 >/dev/null ping -c 1 -w 1 $1 >/dev/null
;; ;;
CYGWIN)
# cygwin does not have own PING by default. use windows PING.
ping -n 1 -w 1000 $1 >/dev/null
;;
*) *)
ping -c 1 -W 1 $1 >/dev/null ping -c 1 -W 1 $1 >/dev/null
;; ;;
@ -1356,7 +1383,7 @@ lookup4()
# $2 - DNS # $2 - DNS
case "$LOOKUP" in case "$LOOKUP" in
nslookup) nslookup)
nslookup $1 $2 | sed -n '/Name:/,$p' | grep ^Address | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' nslookup $1 $2 2>/dev/null | sed -n '/Name:/,$p' | sed -nre 's/^.*(([0-9]{1,3}\.){3}[0-9]{1,3}).*$/\1/p'
;; ;;
host) host)
host -t A $1 $2 | grep "has address" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' host -t A $1 $2 | grep "has address" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}'
@ -1367,7 +1394,9 @@ check_dns_spoof()
{ {
# $1 - domain # $1 - domain
# $2 - public DNS # $2 - public DNS
echo $1 | "$MDIG" --family=4 >"$DNSCHECK_DIG1"
# windows version of mdig outputs 0D0A line ending. remove 0D.
echo $1 | "$MDIG" --family=4 | tr -d '\r' >"$DNSCHECK_DIG1"
lookup4 $1 $2 >"$DNSCHECK_DIG2" lookup4 $1 $2 >"$DNSCHECK_DIG2"
# check whether system resolver returns anything other than public DNS # check whether system resolver returns anything other than public DNS
grep -qvFf "$DNSCHECK_DIG2" "$DNSCHECK_DIG1" grep -qvFf "$DNSCHECK_DIG2" "$DNSCHECK_DIG1"
@ -1475,7 +1504,7 @@ sigpipe()
fsleep_setup fsleep_setup
fix_sbin_path fix_sbin_path
check_system check_system
require_root [ "$UNAME" = CYGWIN ] || require_root
check_prerequisites check_prerequisites
trap sigint_cleanup INT trap sigint_cleanup INT
check_dns check_dns
@ -1485,6 +1514,7 @@ trap - INT
PID= PID=
NREPORT= NREPORT=
unset WF
trap sigint INT trap sigint INT
trap sigpipe PIPE trap sigpipe PIPE
for dom in $DOMAINS; do for dom in $DOMAINS; do

View File

@ -283,3 +283,7 @@ nfqws: --dpi-desync-start option
nfqws: packet delay for kyber TLS and QUIC nfqws: packet delay for kyber TLS and QUIC
nfqws: --dpi-desync-retrans obsolete nfqws: --dpi-desync-retrans obsolete
nfqws: --qnum is mandatory, no more default queue 0 nfqws: --qnum is mandatory, no more default queue 0
v58
winws

View File

@ -91,7 +91,7 @@ zapret не может пробить блокировку по IP адресу
Сейчас блокираторы ставят на магистральных каналах. В сложных случаях у вас может быть несколько маршрутов Сейчас блокираторы ставят на магистральных каналах. В сложных случаях у вас может быть несколько маршрутов
с различной длиной по ХОПам, с DPI на разных хопах. Приходится преодолевать целый зоопарк DPI, с различной длиной по ХОПам, с DPI на разных хопах. Приходится преодолевать целый зоопарк DPI,
которые еще и включаются в работу хаотичным образом или образом, зависящим от направления (IP сервера). которые еще и включаются в работу хаотичным образом или образом, зависящим от направления (IP сервера).
blockheck не всегда может выдать вам в итогах оптимальную стратегию, которую надо просто переписать в настройки. blockcheck не всегда может выдать вам в итогах оптимальную стратегию, которую надо просто переписать в настройки.
В некоторых случаях надо реально думать что происходит, анализируя результат на разных стратегиях. В некоторых случаях надо реально думать что происходит, анализируя результат на разных стратегиях.
Если вы применяете большой TTL, чтобы достать до магистрала, то не лишним будет добавить дополнительный ограничитель Если вы применяете большой TTL, чтобы достать до магистрала, то не лишним будет добавить дополнительный ограничитель
--dpi-desync-fooling, чтобы не сломать сайты на более коротких дистанциях. --dpi-desync-fooling, чтобы не сломать сайты на более коротких дистанциях.

View File

@ -0,0 +1,95 @@
Специально для тех, кто хочет побыстрее начать, но не хочет слишком углубляться в простыню readme.txt.
Как обычно, компьютерная грамотность ложится полностью на вас.
Вы должны уметь работать с консолью windows и иметь минимальные навыки обращения с командными файлами bat,cmd.
Если грамотность отсутствует и возникает куча "как" на базовых вещах - проходите мимо или ищите помощь в другом месте.
Обход DPI является хакерской методикой. Под этим словом понимается метод, которому сопротивляется окружающая среда,
которому автоматически не гарантирована работоспособность в любых условиях и на любых ресурсах,
требуется настройка под специфические условия у вашего провайдера. Условия могут меняться со временем,
и методика может начинать или переставать работать, может потребоваться повторный анализ ситуации.
Могут обнаруживаться отдельные ресурсы, которые заблокированы иначе, и которые не работают или перестали работать.
Могут и сломаться отдельные незаблокированные ресурсы.
Поэтому очень желательно иметь знания в области сетей, чтобы иметь возможность проанализировать техническую ситуацию.
Не будет лишним иметь обходные каналы проксирования трафика на случай, если обход DPI не помогает.
Будем считать, что у вас есть windows 7 или выше. Задача - обойти блокировки с самой системы.
1) Если у вас windows 7, обновляйте систему. Годами не обновляемая 7-ка может не запускать драйвер windivert.
Поддержка 32-битных x86 windows возможна, но в готовом виде отсутствует.
Поддержка arm64 систем возможна при определенных условиях, но в готовом виде отсутствует.
Читайте docs/windows.txt
Имейте в виду, что антивирусы могут плохо реагировать на windivert. Если это имеет место , используйте исключения.
2) Скачайте zip архив проекта с github, распакуйте его куда-нибудь.
3) Убедитесь, что у вас отключены все средства обхода блокировок, в том числе и сам zapret.
4) Если вы работаете в виртуальной машине, необходимо использовать соединение с сетью в режиме bridge. nat не подходит
5) Выполните установку cygwin. Желательно устанавливать windows 7 совместимую версию, под которой и был собран проект.
Скачайте https://www.cygwin.com/setup-x86_64.exe
Выполните его с параметрами : setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215
Установите cygwin, выбрав в списке пакетов curl.
cygwin нужен только для поиска стратегии обхода блокировки. После этого он не нужен.
6) Запустите cygwin.bat из директории установки cygwin от имени администратора.
В нем нужно пройти в директорию с zapret. Буква диска windows X: отображается как /cygdrive/x.
Используейте команды как в *nix
7) Однократно выполните ./install_bin.sh
8) Запустите blockcheck.sh. blockcheck.sh в начале проверяет DNS. Если выводятся сообщения о подмене адресов, то
первым делом нужно решить эту проблему, иначе ничего не будет работать.
Решение проблемы DNS выходит за рамки проекта. Обычно она решается либо заменой DNS серверов
от провайдера на публичные (1.1.1.1, 8.8.8.8), либо в случае перехвата провайдером обращений
к сторонним серверам - через специальные средства шифрования DNS запросов, такие как dnscrypt, DoT, DoH.
В современных броузерах чаще всего DoH включен по умолчанию, но curl будет использовать обычный DNS.
9) blockcheck позволяет выявить рабочую стратегию обхода блокировок.
Запомните найденные стратегии.
Следует понимать, что blockcheck проверяет доступность только конкретного домена, который вы вводите в начале.
Вероятно, все остальные домены блокированы подобным образом, но не факт.
В большинстве случаев можно обьединить несколько стратегий в одну универсальную, но для этого необходимо понимать
"что там за буковки". Если вы в сетях слабо разбираетесь, это не для вас. В противном случае читайте readme.txt.
zapret не может пробить блокировку по IP адресу
Для проверки нескольких доменов вводите их через пробел.
Сейчас блокираторы ставят на магистральных каналах. В сложных случаях у вас может быть несколько маршрутов
с различной длиной по ХОПам, с DPI на разных хопах. Приходится преодолевать целый зоопарк DPI,
которые еще и включаются в работу хаотичным образом или образом, зависящим от направления (IP сервера).
blockcheck не всегда может выдать вам в итогах оптимальную стратегию, которую надо просто переписать в настройки.
В некоторых случаях надо реально думать что происходит, анализируя результат на разных стратегиях.
Если вы применяете большой TTL, чтобы достать до магистрала, то не лишним будет добавить дополнительный ограничитель
--dpi-desync-fooling, чтобы не сломать сайты на более коротких дистанциях.
md5sig наиболее совместим, но работатет только на linux серверах.
badseq может работать только на https и не работать на http.
Чтобы выяснить какие дополнительные ограничители работают, смотрите результат теста аналогичных стратегий без TTL
с каждым из этих ограничителей.
При использовании autottl следует протестировать как можно больше разных доменов. Эта техника
может на одних провайдерах работать стабильно, на других потребуется выяснить при каких параметрах
она стабильна, на третьих полный хаос, и проще отказаться.
10) Протестируйте найденные стратегии на winws. winws следует брать из binaries/win64/zapret-winws.
11) Обеспечьте удобную загрузку обхода блокировок.
В binaries/win64/zapret-winws есть командные файлы task_*, предназначенные для управления задачами планировщика.
Там следует поменять содержимое переменной WINWS1 на свою стратегию.
Если вы не можете обьединить несколько стратегий для разных протоколов в одну, дублируйте код в каждом из cmd
для поддержки нескольких задач : winws1,winws2,winws3.
После создания задач запустите их. Проверьте, что обход встает после перезагрузки windows.
12) Если ломаются отдельные незаблокированные ресурсы, используйте хост-листы.
Где они будут находиться - решайте сами. Пути прописываются не в формате windows, а формате cygwin.
Например, /cygdrive/c/Users/vasya/zapret.
Если в путях присутствуют национальные символы, кодировку нужно использовать OEM. Для русского языка это 866.
Пути с пробелами нужно брать в кавычки.
Параметры управления хост-листами точно такие же, как в *nix.
Это минимальная инструкция, чтобы соориентироваться с чего начать. Однако, это - не панацея.
В некоторых случаях вы не обойдетесь без знаний и основного "толмуда".
Подробности и полное техническое описание расписаны в readme.txt

View File

@ -9,6 +9,8 @@ blocked by Roskomnadzor), but most others are common.
Mainly OpenWRT targeted but also supports traditional Linux, FreeBSD, OpenBSD, partially MacOS. Mainly OpenWRT targeted but also supports traditional Linux, FreeBSD, OpenBSD, partially MacOS.
Most features are also supported in Windows.
## How it works ## How it works
In the simplest case you are dealing with passive DPI. Passive DPI can read passthrough traffic, In the simplest case you are dealing with passive DPI. Passive DPI can read passthrough traffic,
@ -1158,29 +1160,7 @@ see docs/bsd.eng.md
### Windows (WSL) ### Windows (WSL)
Using WSL (Windows subsystem for Linux) it's possible to run tpws in socks mode under rather new builds of see docs/windows.eng.md
windows 10 and windows server.
Its not required to install any linux distributions as suggested in most articles.
tpws is static binary. It doesn't need a distribution.
Install WSL : `dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all`
Copy binaries/x86_64/tpws_wsl.tgz to the target system.
Run : `wsl --import tpws "%USERPROFILE%\tpws" tpws_wsl.tgz`
Run tpws : `wsl -d tpws --exec /tpws --uid=1 --no-resolve --socks --bind-addr=127.0.0.1 --port=1080 <fooling_options>`
Configure socks as 127.0.0.1:1080 in a browser or another program.
Cleanup : `wsl --unregister tpws`
Tested in windows 10 build 19041 (20.04).
`--oob` , `--mss` and `--disorder` do not work.
RST detection in autohostlist scheme may not work.
WSL may glitch with pipes. `--nosplice` may be required.
NOTICE. There is native windows solution GoodByeDPI. It works on packet level like nfqws.
### Other devices ### Other devices

View File

@ -1,4 +1,4 @@
zapret v.57 zapret v.58
English English
------- -------
@ -16,10 +16,12 @@ For english version refer to docs/readme.eng.txt
Поддерживаются традиционные Linux системы, FreeBSD, OpenBSD, частично MacOS. Поддерживаются традиционные Linux системы, FreeBSD, OpenBSD, частично MacOS.
В некоторых случаях возможна самостоятельная прикрутка решения к различным прошивкам. В некоторых случаях возможна самостоятельная прикрутка решения к различным прошивкам.
Большая часть функционала работает на windows.
Как побыстрее начать Как побыстрее начать
-------------------- --------------------
Читайте docs/quick_start.txt Читайте docs/quick_start.txt для linux и openwrt, docs/quick_start_windows.txt для windows.
Как это работает Как это работает
@ -1559,29 +1561,10 @@ FreeBSD, OpenBSD, MacOS
Описано в docs/bsd.txt Описано в docs/bsd.txt
Windows (WSL) Windows
------------- -------
tpws в режиме socks можно запускать и под более-менее современными билдами windows 10 и windows server Описано в docs/windows.txt
с установленным WSL. Совсем не обязательно устанавливать дистрибутив убунту, как вам напишут почти в каждой
статье про WSL, которую вы найдете в сети. tpws - статический бинарик, ему дистрибутив не нужен.
Установить WSL : dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all
Скопировать на целевую систему binaries/x86_64/tpws_wsl.tgz.
Выполнить : wsl --import tpws "%USERPROFILE%\tpws" tpws_wsl.tgz
Запустить : wsl -d tpws --exec /tpws --uid=1 --no-resolve --socks --bind-addr=127.0.0.1 --port=1080 <параметры_дурения>
Прописать socks 127.0.0.1:1080 в броузер или другую программу.
Удаление : wsl --unregister tpws
Проверено на windows 10 build 19041 (20.04).
Не работают функции --oob и --mss из-за ограничений реализации WSL.
--disorder не работает из-за особенностей tcp/ip стека windows.
Может не срабатывать детект RST в autohostlist.
WSL может глючить с pipes, ломая тем самым splice и приводя к зацикливанию процесса. Может потребоваться --nosplice.
ЗАМЕЧАНИЕ. Под Windows существует нативное решение GoodByeDPI, выполняющее дурение на пакетном уровне (по типу nfqws).
Другие прошивки Другие прошивки

95
docs/windows.eng.md Normal file
View File

@ -0,0 +1,95 @@
### tpws
Using `WSL` (Windows subsystem for Linux) it's possible to run `tpws` in socks mode under rather new builds of
windows 10 and windows server.
Its not required to install any linux distributions as suggested in most articles.
tpws is static binary. It doesn't need a distribution.
Install `WSL` : `dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all`
Copy `binaries/x86_64/tpws_wsl.tgz` to the target system.
Run : `wsl --import tpws "%USERPROFILE%\tpws" tpws_wsl.tgz`
Run tpws : `wsl -d tpws --exec /tpws --uid=1 --no-resolve --socks --bind-addr=127.0.0.1 --port=1080 <fooling_options>`
Configure socks as `127.0.0.1:1080` in a browser or another program.
Cleanup : `wsl --unregister tpws`
Tested in windows 10 build 19041 (20.04).
`--oob` , `--mss` and `--disorder` do not work.
RST detection in autohostlist scheme may not work.
WSL may glitch with splice. `--nosplice` may be required.
### winws
`winws` is `nfqws` version for windows. It's based on `windivert`. Most functions are working.
Large ip filters (ipsets) are not possible. Forwarded traffic and connection sharing are not supported.
Administrator rights are required.
Working with packet filter consists of two parts
1. In-kernel packet selection and passing selected packets to a packet filter in user mode.
In *nix it's done by `iptables`, `nftables`, `pf`, `ipfw`.
2. User mode packet filter processes packets and does DPI bypass magic.
Windows does not have part 1. No `iptables` exist. That's why 3rd party packet redirector is used.
It's called `windivert`. It works starting from `windows 7`. Kernel driver is signed but it may require to disable secure boot
or update windows 7.
Task of `iptables` is done inside `winws` through `windivert` filters. `Windivert` has it's own [filter language](https://reqrypt.org/windivert-doc.html#filter_language).
`winws` can automate filter construction using simple ip version and port filter. Raw filters are also supported.
```
--wf-l3=ipv4|ipv6 ; L3 protocol filter. multiple comma separated values allowed.
--wf-tcp=[~]port1[-port2] ; TCP port filter. ~ means negation. multiple comma separated values allowed.
--wf-udp=[~]port1[-port2] ; UDP port filter. ~ means negation. multiple comma separated values allowed.
--wf-raw=<filter>|@<filename> ; raw windivert filter string or filename
--wf-save=<filename> ; save windivert filter string to a file and exit
```
`--wf-l3`, `--wf-tcp`, `--wf-udp` can take multiple comma separated arguments.
Multiple `winws` processes are allowed. However, it's discouraged to intersect their filters.
Paths are passed in `cygwin` format. Windows drives are mapped to `/cygdrive/x`. For example : `/cygdrive/c/Users/vasya/zapret`.
`Cygwin` shell does not run binaries if their directory has it's own copy of `cygwin1.dll`.
That's why exists separate standalone version in `binaries/win64/zapret-tpws`.
`Cygwin` is required for `blockcheck.sh` support but `winws` itself can be run standalone without cygwin.
How to get `windows 7` and `winws` compatible `cygwin` :
```
curl -O https://www.cygwin.com/setup-x86_64.exe
setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215
```
You must choose to install `curl`. To compile from sources install `gcc-core`,`make`,`zlib-devel`.
`winws` requires `cygwin1.dll`, `windivert.dll`, `windivert64.sys`. You can take them from `binaries/win64/zapret-winws`.
It's possible to build x86 32-bit version but this version is not shipped. You have to build it yourself.
32-bit `windivert` can be downloaded from it's developer github. Required version is 2.2.2.
There's no `arm64` signed `windivert` driver. You can compile it yourself but it will run only with disabled driver signature checks.
### blockcheck
`blockcheck.sh` is written in posix shell and uses some standard posix utilites.
Windows does not have them. To execute `blockcheck.sh` use `cygwin` command prompt run as administrator.
It's not possible to use `WSL`. It's not the same as `cygwin`.
First run once `install_bin.sh` then `blockcheck.sh`.
`Cygwin` is required only for `blockcheck.sh`. Standalone `winws` can be run without it.
### auto start
To start `winws` with windows use windows task scheduler. There are `task_*.cmd` batch files in `binaries/win64/zapret-winws`.
They create, remove, start and stop scheduled task `winws1`. They must be run as administrator.
Edit `task_create.cmd` and write your `winws` parameters to `%WINWS1%` variable. If you need multiple `winws` instances
clone the code in all cmd files to support multiple tasks `winws1,winws2,winws3,...`.
Tasks can also be controlled from GUI `taskschd.msc`.

117
docs/windows.txt Normal file
View File

@ -0,0 +1,117 @@
tpws
----
Запуск tpws возможен только в Linux варианте под WSL.
Нативного варианта под Windows нет, поскольку он использует epoll, которого под windows не существует.
tpws в режиме socks можно запускать под более-менее современными билдами windows 10 и windows server
с установленным WSL. Совсем не обязательно устанавливать дистрибутив убунту, как вам напишут почти в каждой
статье про WSL, которую вы найдете в сети. tpws - статический бинарик, ему дистрибутив не нужен.
Установить WSL : dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all
Скопировать на целевую систему binaries/x86_64/tpws_wsl.tgz.
Выполнить : wsl --import tpws "%USERPROFILE%\tpws" tpws_wsl.tgz
Запустить : wsl -d tpws --exec /tpws --uid=1 --no-resolve --socks --bind-addr=127.0.0.1 --port=1080 <параметры_дурения>
Прописать socks 127.0.0.1:1080 в броузер или другую программу.
Удаление : wsl --unregister tpws
Проверено на windows 10 build 19041 (20.04).
Не работают функции --oob и --mss из-за ограничений реализации WSL.
--disorder не работает из-за особенностей tcp/ip стека windows.
Может не срабатывать детект RST в autohostlist.
WSL может глючить со splice, приводя к зацикливанию процесса. Может потребоваться --nosplice.
winws
-----
Это вариант пакетного фильтра nfqws для Windows, построенный на базе windivert.
Все функции работоспособны, однако функционал ipset отсутствует. Фильтры по большому количеству IP адресов невозможны.
Работа с проходящим трафиком, например в случае "расшаривания" соединения, не проверялась и не гарантируется.
Для работы с windivert требуются права администратора.
Специфические для unix параметры, такие как --uid, --user и тд, исключены. Все остальные параметры аналогичны nfqws и dvtws.
Работа с пакетным фильтром основана на двух действиях.
Первое - выделение перенаправляемого трафика в режиме ядра и передача его пакетному фильтру в user mode.
Второе - собственно обработка перенаправленных пакетов в пакетном фильтре.
В windows отсутствуют встроенные средства для перенаправления трафика, такие как iptables, nftables, pf или ipfw.
Поэтому используется сторонний драйвер ядра windivert. Он работает, начиная с windows 7. На системах с включенным
secure boot могут быть проблемы из-за подписи драйвера. В этом случае отключите secureboot или включите режим testsigning.
На windows 7 требуются обновления. На старых необновленных системах может быть ошибка проверки подписи драйвера.
Требования к патчам точно такие же, как и у GoodbyDPI.
Задача iptables в winws решается внутренними средствами через фильтры windivert.
У windivert существует собственный язык фильтров, похожий на язык фильтров wireshark.
Документация по фильтрам windivert : https://reqrypt.org/windivert-doc.html#filter_language
Чтобы не писать сложные фильтры вручную, предусмотрены различные упрощенные варианты автоматического построения фильтров.
--wf-l3=ipv4|ipv6 ; фильтр L3 протоколов. по умолчанию включены ipv4 и ipv6.
--wf-tcp=[~]port1[-port2] ; фильтр портов для tcp. ~ означает отрицание
--wf-udp=[~]port1[-port2] ; фильтр портов для udp. ~ означает отрицание
--wf-raw=<filter>|@<filename> ; задать напрямую фильтр windivert из параметра или из файла. имени файла предшествует символ @.
--wf-save=<filename> ; сохранить сконструированный фильтр windivert в файл для последующей правки вручную
Параметры --wf-l3, --wf-tcp, --wf-udp могут брать несколько значений через запятую.
Конструктор фильтров автоматически включает входящие tcp пакеты с tcp synack и tcp rst для корректной работы функций
autottl и autohostlist. При включении autohostlist так же перенаправляются пакеты данных с http redirect с кодами 302 и 307.
Всегда добавляется фильтр на исключение не-интернет адресов ipv4 и ipv6.
Для сложных нестандартных сценариев могут потребоваться свои фильтры. Логично будет начать со стандартного шаблона,
сохраненного через --wf-save. Нужно править файл и подсовывать его в параметре --wf-raw. Максимальный размер фильтра - 8 Kb.
Можно запускать несколько процессов winws с разными стратегиями. Однако, не следует делать пересекающиеся фильтры.
В параметрах, берущих пути к файлам, они прописываются не в формате windows, а формате cygwin.
Например, /cygdrive/c/Users/vasya/zapret.
Если в путях присутствуют национальные символы, то при вызове winws из cmd или bat кодировку нужно использовать OEM.
Для русского языка это 866. Пути с пробелами нужно брать в кавычки.
Существует неочевидный момент, каcаемый запуска winws из cygwin шелла. Если в директории, где находится nfqws, находится
копия cygwin1.dll, winws не запустится. Поэтому в binaries/win64 существует директория zapret-winws, содержащая полный
комплект для запуска без cygwin. Его вы и берете для повседневного использования.
Если нужен запуск под cygwin, то следует запускать из binaries/win64. Это нужно для работы blockcheck.
Из cygwin шелла можно посылать winws сигналы через kill точно так же, как в *nix.
Как получить совместимый с windows 7 и winws cygwin :
curl -O https://www.cygwin.com/setup-x86_64.exe
setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215
Следует выбрать установку curl.
Для сборки из исходников требуется gcc-core,make,zlib-devel.
winws требует cygwin1.dll, windivert.dll, windivert64.sys. Их можно взять из binaries/win64/zapret-winws.
Версию для 32-битных x86 windows собрать можно, но такие системы уже уходят в прошлое, поэтому если надо - собирайте сами.
32-битный windivert можно взять с сайта разработчика. Требуется версия 2.2.2.
Для arm64 систем нет подписанного драйвера windivert. Можно самому собрать windivert, но работать он будет только
с отключенной подписью драйверов.
blockcheck
----------
blockcheck.sh написан на posix shell и требует некоторых стандартных утилит posix. В windows, естественно, этого нет.
Потому просто так запустить blockcheck.sh невозможно.
Для этого требуется скачать и установить cygwin так , как описано в предыдущем разделе.
Следует запустить от имени администратора cygwin shell через cygwin.bat.
В нем нужно пройти в директорию с zapret. Буква диска windows X: отображается как /cygdrive/x.
Далее все как в *nix : 1 раз ./install_bin.sh , затем ./blockcheck.sh.
WSL использовать нельзя, это не то же самое.
cygwin для обычной работы winws не нужен. Разве что вы хотите посылать winws SIGHUP для перечитки листов без перезапуска.
автозапуск winws
----------------
Для запуска winws вместе с windows воспользуйтесь планировщиком задач windows.
Удобнее всего создавать задачи и управлять ими через консольную программу schtasks.
В директории binaries/win64/winws подготовлены файлы task_*.cmd .
В них реализовано создание, удаление, старт и стоп одной копии процесса winws с параметрами из переменной %WINWS1%.
Исправьте параметры на нужную вам стратегию. Если для разных фильтров применяется разная стратегия, размножьте код
для задач winws1,winws2,winws3,...
Все батники требуется запускать от имени администратора.
Управлять задачами можно так же из графической программы управления планировщиком taskschd.msc

View File

@ -18,9 +18,9 @@ check_dir()
# ash and dash try to execute invalid executables as a script. they interpret binary garbage with possible negative consequences # ash and dash try to execute invalid executables as a script. they interpret binary garbage with possible negative consequences
# bash and zsh do not do this # bash and zsh do not do this
if exists bash; then if exists bash; then
out=$(echo 0.0.0.0 | bash -c "$exe" 2>/dev/null) out=$(echo 0.0.0.0 | bash -c "\"$exe"\" 2>/dev/null)
elif exists zsh; then elif exists zsh; then
out=$(echo 0.0.0.0 | zsh -c "$exe" 2>/dev/null) out=$(echo 0.0.0.0 | zsh -c "\"$exe\"" 2>/dev/null)
else else
# find does not use its own shell exec # find does not use its own shell exec
# it uses execvp(). in musl libc it does not call shell, in glibc it DOES call /bin/sh # it uses execvp(). in musl libc it does not call shell, in glibc it DOES call /bin/sh
@ -49,15 +49,27 @@ ccp()
} }
UNAME=$(uname) UNAME=$(uname)
if [ "$UNAME" = "Linux" ]; then unset PKTWS
ARCHLIST="my x86_64 x86 aarch64 arm mips64r2-msb mips32r1-lsb mips32r1-msb ppc" case $UNAME in
elif [ "$UNAME" = "Darwin" ]; then Linux)
ARCHLIST="my mac64" ARCHLIST="my x86_64 x86 aarch64 arm mips64r2-msb mips32r1-lsb mips32r1-msb ppc"
elif [ "$UNAME" = "FreeBSD" ]; then PKTWS=nfqws
ARCHLIST="my freebsd-x64" ;;
else Darwin)
ARCHLIST="my" ARCHLIST="my mac64"
fi ;;
FreeBSD)
ARCHLIST="my freebsd-x64"
PKTWS=dvtws
;;
CYGWIN*)
UNAME=CYGWIN
ARCHLIST="win64"
PKTWS=winws
;;
*)
ARCHLIST="my"
esac
if [ "$1" = "getarch" ]; then if [ "$1" = "getarch" ]; then
for arch in $ARCHLIST for arch in $ARCHLIST
@ -77,12 +89,8 @@ else
echo installing binaries ... echo installing binaries ...
ccp $arch/ip2net ip2net ccp $arch/ip2net ip2net
ccp $arch/mdig mdig ccp $arch/mdig mdig
if [ "$UNAME" = "Linux" ]; then [ -n "$PKTWS" ] && ccp $arch/$PKTWS nfq
ccp $arch/nfqws nfq [ "$UNAME" = CYGWIN ] || ccp $arch/tpws tpws
else
ccp $arch/dvtws nfq
fi
ccp $arch/tpws tpws
exit 0 exit 0
else else
echo $arch is NOT OK echo $arch is NOT OK

View File

@ -1,6 +1,7 @@
CC ?= gcc CC ?= gcc
CFLAGS += -std=gnu99 -Wno-logical-op-parentheses -O3 CFLAGS += -std=gnu99 -Wno-logical-op-parentheses -O3
CFLAGS_BSD = -Wno-address-of-packed-member -Wno-switch CFLAGS_BSD = -Wno-address-of-packed-member -Wno-switch
CFLAGS_WIN = -static
LIBS = LIBS =
LIBS_WIN = -lws2_32 LIBS_WIN = -lws2_32
SRC_FILES = ip2net.c qsort.c SRC_FILES = ip2net.c qsort.c
@ -21,7 +22,7 @@ mac: $(SRC_FILES)
rm -f ip2netx ip2neta rm -f ip2netx ip2neta
win: $(SRC_FILES) win: $(SRC_FILES)
$(CC) $(CFLAGS) -o ip2net $(SRC_FILES) $(LDFLAGS) $(LIBS_WIN) $(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o ip2net $(SRC_FILES) $(LDFLAGS) $(LIBS_WIN)
clean: clean:
rm -f ip2net *.o rm -f ip2net *.o

View File

@ -1,6 +1,7 @@
CC ?= gcc CC ?= gcc
CFLAGS += -std=gnu99 -Wno-logical-op-parentheses -O3 CFLAGS += -std=gnu99 -Wno-logical-op-parentheses -O3
CFLAGS_BSD = -Wno-address-of-packed-member -Wno-switch CFLAGS_BSD = -Wno-address-of-packed-member -Wno-switch
CFLAGS_WIN = -static
LIBS = -lpthread LIBS = -lpthread
LIBS_WIN = -lws2_32 LIBS_WIN = -lws2_32
SRC_FILES = *.c SRC_FILES = *.c
@ -21,7 +22,7 @@ mac: $(SRC_FILES)
rm -f mdigx mdiga rm -f mdigx mdiga
win: $(SRC_FILES) win: $(SRC_FILES)
$(CC) $(CFLAGS) -o mdig -static $(SRC_FILES) $(LDFLAGS) $(LIBS_WIN) $(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o mdig $(SRC_FILES) $(LDFLAGS) $(LIBS_WIN)
clean: clean:
rm -f mdig *.o rm -f mdig *.o

View File

@ -2,9 +2,10 @@ CC ?= gcc
CFLAGS += -std=gnu99 -Wno-logical-op-parentheses -O3 CFLAGS += -std=gnu99 -Wno-logical-op-parentheses -O3
CFLAGS_BSD = -Wno-address-of-packed-member -Wno-switch CFLAGS_BSD = -Wno-address-of-packed-member -Wno-switch
CFLAGS_MAC = -mmacosx-version-min=10.8 CFLAGS_MAC = -mmacosx-version-min=10.8
CFLAGS_CYGWIN = -Wno-address-of-packed-member -static
LIBS_LINUX = -lnetfilter_queue -lnfnetlink -lz LIBS_LINUX = -lnetfilter_queue -lnfnetlink -lz
LIBS_BSD = -lz LIBS_BSD = -lz
LIBS_CYGWIN = -lz LIBS_CYGWIN = -lz -Lwindivert -lwindivert
SRC_FILES = *.c crypto/*.c SRC_FILES = *.c crypto/*.c
all: nfqws all: nfqws
@ -23,7 +24,7 @@ mac: $(SRC_FILES)
rm -f dvtwsx dvtwsa rm -f dvtwsx dvtwsa
cygwin: cygwin:
$(CC) -s $(CFLAGS) -o winws $(SRC_FILES) $(LDFLAGS) $(LIBS_CYGWIN) $(CC) -s $(CFLAGS) $(CFLAGS_CYGWIN) -o winws $(SRC_FILES) $(LDFLAGS) $(LIBS_CYGWIN)
clean: clean:
rm -f nfqws dvtws *.o rm -f nfqws dvtws winws.exe *.o

View File

@ -1,6 +1,5 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include "darkmagic.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -8,10 +7,13 @@
#include <arpa/inet.h> #include <arpa/inet.h>
#include <sys/param.h> #include <sys/param.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include "darkmagic.h"
#include "helpers.h" #include "helpers.h"
#include "params.h" #include "params.h"
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment) uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment)
{ {
return htonl(ntohl(netorder_value)+cpuorder_increment); return htonl(ntohl(netorder_value)+cpuorder_increment);
@ -642,6 +644,8 @@ const char *proto_name(uint8_t proto)
return "udp"; return "udp";
case IPPROTO_ICMP: case IPPROTO_ICMP:
return "icmp"; return "icmp";
case IPPROTO_ICMPV6:
return "icmp6";
case IPPROTO_IGMP: case IPPROTO_IGMP:
return "igmp"; return "igmp";
case IPPROTO_ESP: case IPPROTO_ESP:
@ -950,8 +954,133 @@ void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_fac
} }
#ifdef __CYGWIN__
static HANDLE w_filter = NULL, w_event = NULL;
static HANDLE windivert_init_filter(const char *filter, UINT64 flags)
{
LPTSTR errormessage = NULL;
DWORD errorcode = 0;
HANDLE h;
h = WinDivertOpen(filter, WINDIVERT_LAYER_NETWORK, 0, flags);
if (h != INVALID_HANDLE_VALUE)
{
return h;
}
errorcode = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorcode, MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), (LPTSTR)&errormessage, 0, NULL);
fprintf(stderr, "windivert: error opening filter: %s", errormessage);
LocalFree(errormessage);
if (errorcode == 577)
fprintf(stderr,"windivert: try to disable secure boot and install OS patches\n");
return NULL;
}
void rawsend_cleanup(void)
{
if (w_filter)
{
WinDivertClose(w_filter);
w_filter=NULL;
}
if (w_event)
{
CloseHandle(w_event);
w_event=NULL;
}
}
bool windivert_init(const char *filter)
{
rawsend_cleanup();
w_filter = windivert_init_filter(filter, 0);
if (w_filter)
{
w_event = CreateEventW(NULL,FALSE,FALSE,NULL);
if (!w_event)
{
rawsend_cleanup();
return false;
}
return true;
}
return false;
}
static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa)
{
UINT recv_len;
DWORD err;
OVERLAPPED ovl = { .hEvent = w_event };
DWORD rd;
char c;
if (WinDivertRecvEx(hFilter, packet, *len, &recv_len, 0, wa, NULL, &ovl))
{
*len = recv_len;
return true;
}
for(;;)
{
err = GetLastError();
switch(err)
{
case ERROR_IO_PENDING:
// make signals working
while(WaitForSingleObject(w_event,50)==WAIT_TIMEOUT) usleep(0);
if (!GetOverlappedResult(hFilter,&ovl,&rd,TRUE))
continue;
*len = rd;
return true;
case ERROR_INSUFFICIENT_BUFFER:
errno = ENOBUFS;
break;
case ERROR_NO_DATA:
errno = ESHUTDOWN;
break;
default:
errno = EIO;
}
break;
}
return false;
}
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa)
{
return windivert_recv_filter(w_filter,packet,len,wa);
}
static bool windivert_send_filter(HANDLE hFilter, const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa)
{
return WinDivertSend(hFilter,packet,(UINT)len,NULL,wa);
}
bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa)
{
return windivert_send_filter(w_filter,packet,len,wa);
}
bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len)
{
WINDIVERT_ADDRESS wa;
memset(&wa,0,sizeof(wa));
// pseudo interface id IfIdx.SubIfIdx
if (sscanf(ifout,"%u.%u",&wa.Network.IfIdx,&wa.Network.SubIfIdx)!=2)
{
errno = EINVAL;
return false;
}
wa.Outbound=1;
wa.IPChecksum=1;
wa.TCPChecksum=1;
wa.UDPChecksum=1;
wa.IPv6 = (dst->sa_family==AF_INET6);
return windivert_send(data,len,&wa);
}
#else // *nix
static int rawsend_sock4=-1, rawsend_sock6=-1; static int rawsend_sock4=-1, rawsend_sock6=-1;
static bool b_bind_fix4=false, b_bind_fix6=false; static bool b_bind_fix4=false, b_bind_fix6=false;
@ -1264,6 +1393,8 @@ nofix:
return true; return true;
} }
#endif // not CYGWIN
bool rawsend_rp(const struct rawpacket *rp) bool rawsend_rp(const struct rawpacket *rp)
{ {
return rawsend((struct sockaddr*)&rp->dst,rp->fwmark,rp->ifout,rp->packet,rp->len); return rawsend((struct sockaddr*)&rp->dst,rp->fwmark,rp->ifout,rp->packet,rp->len);
@ -1311,23 +1442,35 @@ uint8_t autottl_guess(uint8_t ttl, const autottl *attl)
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr) void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr)
{ {
#ifdef __FreeBSD__ if (!(verdict & VERDICT_NOCSUM))
// FreeBSD tend to pass ipv6 frames with wrong checksum {
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr) // always fix csum for windivert. original can be partial or bad
#else #ifndef __CYGWIN__
// if original packet was tampered earlier it needs checksum fixed #ifdef __FreeBSD__
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY) // FreeBSD tend to pass ipv6 frames with wrong checksum
#endif if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr)
tcp_fix_checksum(tcphdr,transport_len,ip,ip6hdr); #else
// if original packet was tampered earlier it needs checksum fixed
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY)
#endif
#endif
tcp_fix_checksum(tcphdr,transport_len,ip,ip6hdr);
}
} }
void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr) void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr)
{ {
#ifdef __FreeBSD__ if (!(verdict & VERDICT_NOCSUM))
// FreeBSD tend to pass ipv6 frames with wrong checksum {
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr) // always fix csum for windivert. original can be partial or bad
#else #ifndef __CYGWIN__
// if original packet was tampered earlier it needs checksum fixed #ifdef __FreeBSD__
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY) // FreeBSD tend to pass ipv6 frames with wrong checksum
#endif if ((verdict & VERDICT_MASK)==VERDICT_MODIFY || ip6hdr)
udp_fix_checksum(udphdr,transport_len,ip,ip6hdr); #else
// if original packet was tampered earlier it needs checksum fixed
if ((verdict & VERDICT_MASK)==VERDICT_MODIFY)
#endif
#endif
udp_fix_checksum(udphdr,transport_len,ip,ip6hdr);
}
} }

View File

@ -1,15 +1,21 @@
#pragma once #pragma once
#include "checksum.h"
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include <netinet/ip6.h> #include <netinet/ip6.h>
#include <netinet/tcp.h> #include <netinet/tcp.h>
#include <netinet/udp.h> #include <netinet/udp.h>
#include <netinet/in.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/param.h>
#include "checksum.h"
#ifdef __CYGWIN__
#include "windivert/windivert.h"
#endif
#include "packet_queue.h" #include "packet_queue.h"
@ -142,17 +148,26 @@ uint32_t *tcp_find_timestamps(struct tcphdr *tcp);
uint8_t tcp_find_scale_factor(const struct tcphdr *tcp); uint8_t tcp_find_scale_factor(const struct tcphdr *tcp);
bool tcp_has_fastopen(const struct tcphdr *tcp); bool tcp_has_fastopen(const struct tcphdr *tcp);
#ifdef __CYGWIN__
bool windivert_init(const char *filter);
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa);
bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa);
#else
// should pre-do it if dropping privileges. otherwise its not necessary
bool rawsend_preinit(bool bind_fix4, bool bind_fix6);
#endif
// auto creates internal socket and uses it for subsequent calls // auto creates internal socket and uses it for subsequent calls
bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len); bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len);
bool rawsend_rp(const struct rawpacket *rp); bool rawsend_rp(const struct rawpacket *rp);
// return trues if all packets were send successfully // return trues if all packets were send successfully
bool rawsend_queue(struct rawpacket_tailhead *q); bool rawsend_queue(struct rawpacket_tailhead *q);
// should pre-do it if dropping privileges. otherwise its not necessary
bool rawsend_preinit(bool bind_fix4, bool bind_fix6);
// cleans up socket autocreated by rawsend // cleans up socket autocreated by rawsend
void rawsend_cleanup(void); void rawsend_cleanup(void);
#ifdef BSD
int socket_divert(sa_family_t family); int socket_divert(sa_family_t family);
#endif
const char *proto_name(uint8_t proto); const char *proto_name(uint8_t proto);
uint16_t family_from_proto(uint8_t l3proto); uint16_t family_from_proto(uint8_t l3proto);

View File

@ -265,3 +265,35 @@ time_t file_mod_time(const char *filename)
struct stat st; struct stat st;
return stat(filename,&st)==-1 ? 0 : st.st_mtime; return stat(filename,&st)==-1 ? 0 : st.st_mtime;
} }
bool pf_in_range(uint16_t port, const port_filter *pf)
{
return port && ((!pf->from && !pf->to || port>=pf->from && port<=pf->to) ^ pf->neg);
}
bool pf_parse(const char *s, port_filter *pf)
{
unsigned int v1,v2;
if (!s) return false;
if (*s=='~')
{
pf->neg=true;
s++;
}
else
pf->neg=false;
if (sscanf(s,"%u-%u",&v1,&v2)==2)
{
if (!v1 || v1>65535 || v2>65535 || v1>v2) return false;
pf->from=(uint16_t)v1;
pf->to=(uint16_t)v2;
}
else if (sscanf(s,"%u",&v1)==1)
{
if (!v1 || v1>65535) return false;
pf->to=pf->from=(uint16_t)v1;
}
else
return false;
return true;
}

View File

@ -47,3 +47,11 @@ void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize
int fprint_localtime(FILE *F); int fprint_localtime(FILE *F);
time_t file_mod_time(const char *filename); time_t file_mod_time(const char *filename);
typedef struct
{
uint16_t from,to;
bool neg;
} port_filter;
bool pf_in_range(uint16_t port, const port_filter *pf);
bool pf_parse(const char *s, port_filter *pf);

View File

@ -78,6 +78,15 @@ static void onusr2(int sig)
printf("\n"); printf("\n");
} }
static void pre_desync(void)
{
signal(SIGHUP, onhup);
signal(SIGUSR1, onusr1);
signal(SIGUSR2, onusr2);
desync_init();
}
static uint8_t processPacketData(uint32_t *mark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt) static uint8_t processPacketData(uint32_t *mark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt)
{ {
@ -189,17 +198,16 @@ static int nfq_main(void)
if (!rawsend_preinit(params.bind_fix4,params.bind_fix6)) if (!rawsend_preinit(params.bind_fix4,params.bind_fix6))
goto exiterr; goto exiterr;
#ifndef __CYGWIN__
sec_harden(); sec_harden();
if (params.droproot && !droproot(params.uid, params.gid)) if (params.droproot && !droproot(params.uid, params.gid))
goto exiterr; goto exiterr;
print_id(); print_id();
#endif
signal(SIGHUP, onhup); pre_desync();
signal(SIGUSR1, onusr1);
signal(SIGUSR2, onusr2);
desync_init();
fd = nfq_fd(h); fd = nfq_fd(h);
@ -311,10 +319,7 @@ static int dvt_main(void)
goto exiterr; goto exiterr;
print_id(); print_id();
signal(SIGHUP, onhup); pre_desync();
signal(SIGUSR1, onusr1);
desync_init();
for(;;) for(;;)
{ {
@ -385,7 +390,75 @@ exiterr:
return res; return res;
} }
#endif
#elif defined (__CYGWIN__)
static int win_main(const char *windivert_filter)
{
size_t len;
unsigned int id;
uint8_t verdict;
bool bOutbound;
uint8_t packet[16384];
uint32_t mark;
WINDIVERT_ADDRESS wa;
char ifout[22];
if (!windivert_init(windivert_filter))
return 1;
printf("windivert initialized. capture is started.\n");
pre_desync();
for (id=0;;id++)
{
len = sizeof(packet);
if (!windivert_recv(packet, &len, &wa))
{
if (errno==ENOBUFS)
{
DLOG("windivert: ignoring too large packet\n")
continue; // too large packet
}
fprintf(stderr, "windivert: recv failed. errno %d\n", errno);
break;
}
*ifout=0;
snprintf(ifout,sizeof(ifout),"%u.%u",wa.Network.IfIdx, wa.Network.SubIfIdx);
DLOG("packet: id=%u len=%zu outbound=%u IPv6=%u IPChecksum=%u TCPChecksum=%u UDPChecksum=%u IfIdx=%s\n", id, len, wa.Outbound, wa.IPv6, wa.IPChecksum, wa.TCPChecksum, wa.UDPChecksum, ifout)
if (wa.Impostor)
{
DLOG("windivert: skipping impostor packet\n")
continue;
}
if (wa.Loopback)
{
DLOG("windivert: skipping loopback packet\n")
continue;
}
mark=0;
// pseudo interface id IfIdx.SubIfIdx
verdict = processPacketData(&mark, ifout, packet, &len);
switch (verdict & VERDICT_MASK)
{
case VERDICT_PASS:
case VERDICT_MODIFY:
if ((verdict & VERDICT_MASK)==VERDICT_PASS)
DLOG("packet: id=%u reinject unmodified\n", id)
else
DLOG("packet: id=%u reinject modified len=%zu\n", id, len)
if (!windivert_send(packet, len, &wa))
fprintf(stderr,"windivert: reinject of packet id=%u failed\n", id);
break;
default:
DLOG("packet: id=%u drop\n", id);
}
}
return 0;
}
#endif // multiple OS divert handlers
@ -427,16 +500,35 @@ static void exithelp(void)
#endif #endif
" --daemon\t\t\t\t\t; daemonize\n" " --daemon\t\t\t\t\t; daemonize\n"
" --pidfile=<filename>\t\t\t\t; write pid to file\n" " --pidfile=<filename>\t\t\t\t; write pid to file\n"
#ifndef __CYGWIN__
" --user=<username>\t\t\t\t; drop root privs\n" " --user=<username>\t\t\t\t; drop root privs\n"
" --uid=uid[:gid]\t\t\t\t; drop root privs\n" " --uid=uid[:gid]\t\t\t\t; drop root privs\n"
#endif
#ifdef __linux__ #ifdef __linux__
" --bind-fix4\t\t\t\t\t; apply outgoing interface selection fix for generated ipv4 packets\n" " --bind-fix4\t\t\t\t\t; apply outgoing interface selection fix for generated ipv4 packets\n"
" --bind-fix6\t\t\t\t\t; apply outgoing interface selection fix for generated ipv6 packets\n" " --bind-fix6\t\t\t\t\t; apply outgoing interface selection fix for generated ipv6 packets\n"
#endif #endif
" --ctrack-timeouts=S:E:F[:U]\t\t\t; internal conntrack timeouts for TCP SYN, ESTABLISHED, FIN stages, UDP timeout. default %u:%u:%u:%u\n"
#ifdef __CYGWIN__
"\nWINDIVERT FILTER:\n"
" --wf-l3=ipv4|ipv6\t\t\t\t; L3 protocol filter. multiple comma separated values allowed.\n"
" --wf-tcp=[~]port1[-port2]\t\t\t; TCP port filter. ~ means negation. multiple comma separated values allowed.\n"
" --wf-udp=[~]port1[-port2]\t\t\t; UDP port filter. ~ means negation. multiple comma separated values allowed.\n"
" --wf-raw=<filter>|@<filename>\t\t\t; raw windivert filter string or filename\n"
" --wf-save=<filename>\t\t\t\t; save windivert filter string to a file and exit\n"
#endif
"\nHOSTLIST FILTER:\n"
" --hostlist=<filename>\t\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
" --hostlist-exclude=<filename>\t\t\t; do not apply dpi desync to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
" --hostlist-auto=<filename>\t\t\t; detect DPI blocks and build hostlist automatically\n"
" --hostlist-auto-fail-threshold=<int>\t\t; how many failed attempts cause hostname to be added to auto hostlist (default : %d)\n"
" --hostlist-auto-fail-time=<int>\t\t; all failed attemps must be within these seconds (default : %d)\n"
" --hostlist-auto-retrans-threshold=<int>\t; how many request retransmissions cause attempt to fail (default : %d)\n"
" --hostlist-auto-debug=<logfile>\t\t; debug auto hostlist positives\n"
"\nTAMPER:\n"
" --wsize=<window_size>[:<scale_factor>]\t\t; set window size. 0 = do not modify. OBSOLETE !\n" " --wsize=<window_size>[:<scale_factor>]\t\t; set window size. 0 = do not modify. OBSOLETE !\n"
" --wssize=<window_size>[:<scale_factor>]\t; set window size for server. 0 = do not modify. default scale_factor = 0.\n" " --wssize=<window_size>[:<scale_factor>]\t; set window size for server. 0 = do not modify. default scale_factor = 0.\n"
" --wssize-cutoff=[n|d|s]N\t\t\t; apply server wsize only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n" " --wssize-cutoff=[n|d|s]N\t\t\t; apply server wsize only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n"
" --ctrack-timeouts=S:E:F[:U]\t\t\t; internal conntrack timeouts for TCP SYN, ESTABLISHED, FIN stages, UDP timeout. default %u:%u:%u:%u\n"
" --hostcase\t\t\t\t\t; change Host: => host:\n" " --hostcase\t\t\t\t\t; change Host: => host:\n"
" --hostspell\t\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" " --hostspell\t\t\t\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n"
" --hostnospace\t\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n" " --hostnospace\t\t\t\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n"
@ -471,15 +563,9 @@ static void exithelp(void)
" --dpi-desync-udplen-increment=<int>\t\t; increase or decrease udp packet length by N bytes (default %u). negative values decrease length.\n" " --dpi-desync-udplen-increment=<int>\t\t; increase or decrease udp packet length by N bytes (default %u). negative values decrease length.\n"
" --dpi-desync-udplen-pattern=<filename>|0xHEX\t; udp tail fill pattern\n" " --dpi-desync-udplen-pattern=<filename>|0xHEX\t; udp tail fill pattern\n"
" --dpi-desync-start=[n|d|s]N\t\t\t; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N\n" " --dpi-desync-start=[n|d|s]N\t\t\t; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) greater or equal than N\n"
" --dpi-desync-cutoff=[n|d|s]N\t\t\t; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n" " --dpi-desync-cutoff=[n|d|s]N\t\t\t; apply dpi desync only to packet numbers (n, default), data packet numbers (d), relative sequence (s) less than N\n",
" --hostlist=<filename>\t\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
" --hostlist-exclude=<filename>\t\t\t; do not apply dpi desync to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
" --hostlist-auto=<filename>\t\t\t; detect DPI blocks and build hostlist automatically\n"
" --hostlist-auto-fail-threshold=<int>\t\t; how many failed attempts cause hostname to be added to auto hostlist (default : %d)\n"
" --hostlist-auto-fail-time=<int>\t\t; all failed attemps must be within these seconds (default : %d)\n"
" --hostlist-auto-retrans-threshold=<int>\t; how many request retransmissions cause attempt to fail (default : %d)\n"
" --hostlist-auto-debug=<logfile>\t\t; debug auto hostlist positives\n",
CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN, CTRACK_T_UDP, CTRACK_T_SYN, CTRACK_T_EST, CTRACK_T_FIN, CTRACK_T_UDP,
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT, HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT,
#if defined(__linux__) || defined(SO_USER_COOKIE) #if defined(__linux__) || defined(SO_USER_COOKIE)
DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT, DPI_DESYNC_FWMARK_DEFAULT,DPI_DESYNC_FWMARK_DEFAULT,
#endif #endif
@ -488,8 +574,7 @@ static void exithelp(void)
DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_UDP_DEFAULT, DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_UDP_DEFAULT,
DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_TCP_DEFAULT, DPI_DESYNC_MAX_FAKE_LEN, IPFRAG_TCP_DEFAULT,
BADSEQ_INCREMENT_DEFAULT, BADSEQ_ACK_INCREMENT_DEFAULT, BADSEQ_INCREMENT_DEFAULT, BADSEQ_ACK_INCREMENT_DEFAULT,
UDPLEN_INCREMENT_DEFAULT, UDPLEN_INCREMENT_DEFAULT
HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT, HOSTLIST_AUTO_FAIL_TIME_DEFAULT, HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT
); );
exit(1); exit(1);
} }
@ -580,12 +665,151 @@ bool parse_autottl(const char *s, autottl *t)
return true; return true;
} }
#ifdef __CYGWIN__
static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *buf, size_t len)
{
char *e,*p,c,s1[64];
port_filter pf;
int n;
if (len<3) return false;
for (n=0,p=opt,*buf='(',buf[1]=0 ; p ; n++)
{
if ((e = strchr(p,',')))
{
c=*e;
*e=0;
}
if (!pf_parse(p,&pf)) return false;
if (pf.from==pf.to)
snprintf(s1, sizeof(s1), "(%s.%s %s %u)", l4, portname, pf.neg ? "!=" : "==", pf.from);
else
snprintf(s1, sizeof(s1), "(%s.%s %s %u %s %s.%s %s %u)", l4, portname, pf.neg ? "<" : ">=", pf.from, pf.neg ? "or" : "and" , l4, portname, pf.neg ? ">" : "<=", pf.to);
if (n) strncat(buf," or ",len-strlen(buf)-1);
strncat(buf, s1, len-strlen(buf)-1);
if (e)
{
*e++=c;
}
p = e;
}
strncat(buf, ")", len-strlen(buf)-1);
return true;
}
static bool wf_make_l3(char *opt, bool *ipv4, bool *ipv6)
{
char *e,*p,c;
for (p=opt,*ipv4=*ipv6=false ; p ; )
{
if ((e = strchr(p,',')))
{
c=*e;
*e=0;
}
if (!strcmp(p,"ipv4"))
*ipv4 = true;
else if (!strcmp(p,"ipv6"))
*ipv6 = true;
else return false;
if (e)
{
*e++=c;
}
p = e;
}
return true;
}
#define DIVERT_NO_LOCALNETSv4_DST "(" \
"(ip.DstAddr < 127.0.0.1 or ip.DstAddr > 127.255.255.255) and " \
"(ip.DstAddr < 10.0.0.0 or ip.DstAddr > 10.255.255.255) and " \
"(ip.DstAddr < 192.168.0.0 or ip.DstAddr > 192.168.255.255) and " \
"(ip.DstAddr < 172.16.0.0 or ip.DstAddr > 172.31.255.255) and " \
"(ip.DstAddr < 169.254.0.0 or ip.DstAddr > 169.254.255.255))"
#define DIVERT_NO_LOCALNETSv4_SRC "(" \
"(ip.SrcAddr < 127.0.0.1 or ip.SrcAddr > 127.255.255.255) and " \
"(ip.SrcAddr < 10.0.0.0 or ip.SrcAddr > 10.255.255.255) and " \
"(ip.SrcAddr < 192.168.0.0 or ip.SrcAddr > 192.168.255.255) and " \
"(ip.SrcAddr < 172.16.0.0 or ip.SrcAddr > 172.31.255.255) and " \
"(ip.SrcAddr < 169.254.0.0 or ip.SrcAddr > 169.254.255.255))"
#define DIVERT_NO_LOCALNETSv6_DST "(" \
"(ipv6.DstAddr > ::1) and " \
"(ipv6.DstAddr < 2001::0 or ipv6.DstAddr >= 2001:1::0) and " \
"(ipv6.DstAddr < fc00::0 or ipv6.DstAddr >= fe00::0) and " \
"(ipv6.DstAddr < fe80::0 or ipv6.DstAddr >= fec0::0) and " \
"(ipv6.DstAddr < ff00::0 or ipv6.DstAddr >= ffff::0))"
#define DIVERT_NO_LOCALNETSv6_SRC "(" \
"(ipv6.SrcAddr > ::1) and " \
"(ipv6.SrcAddr < 2001::0 or ipv6.SrcAddr >= 2001:1::0) and " \
"(ipv6.SrcAddr < fc00::0 or ipv6.SrcAddr >= fe00::0) and " \
"(ipv6.SrcAddr < fe80::0 or ipv6.SrcAddr >= fec0::0) and " \
"(ipv6.SrcAddr < ff00::0 or ipv6.SrcAddr >= ffff::0))"
#define DIVERT_NO_LOCALNETS_SRC "(" DIVERT_NO_LOCALNETSv4_SRC " or " DIVERT_NO_LOCALNETSv6_SRC ")"
#define DIVERT_NO_LOCALNETS_DST "(" DIVERT_NO_LOCALNETSv4_DST " or " DIVERT_NO_LOCALNETSv6_DST ")"
#define DIVERT_TCP_INBOUNDS "tcp.Ack and tcp.Syn or tcp.Rst or tcp.Fin"
// HTTP/1.? 30(2|7)
#define DIVERT_HTTP_REDIRECT "tcp.PayloadLength>=12 and tcp.Payload32[0]==0x48545450 and tcp.Payload16[2]==0x2F31 and tcp.Payload[6]==0x2E and tcp.Payload16[4]==0x2033 and tcp.Payload[10]==0x30 and (tcp.Payload[11]==0x32 or tcp.Payload[11]==0x37)"
#define DIVERT_PROLOG "!impostor and !loopback"
static bool wf_make_filter(
char *wf, size_t len,
bool ipv4, bool ipv6,
const char *pf_tcp_src, const char *pf_tcp_dst,
const char *pf_udp_src, const char *pf_udp_dst)
{
char pf_src_buf[512],pf_dst_buf[512];
const char *pf_dst;
const char *f_tcpin = *pf_tcp_src ? *params.hostlist_auto_filename ? "(" DIVERT_TCP_INBOUNDS " or (" DIVERT_HTTP_REDIRECT "))" : DIVERT_TCP_INBOUNDS : "";
if (!*pf_tcp_src && !*pf_udp_src) return false;
if (*pf_tcp_src && *pf_udp_src)
{
snprintf(pf_dst_buf,sizeof(pf_dst_buf),"(%s or %s)",pf_tcp_dst,pf_udp_dst);
pf_dst = pf_dst_buf;
}
else
pf_dst = *pf_tcp_dst ? pf_tcp_dst : pf_udp_dst;
snprintf(wf,len,
DIVERT_PROLOG " and%s\n ((outbound and %s%s)\n or\n (inbound and tcp%s%s%s%s%s%s%s))",
ipv4 ? ipv6 ? "" : " ip and" : " ipv6 and",
pf_dst,
ipv4 ? ipv6 ? " and " DIVERT_NO_LOCALNETS_DST : " and " DIVERT_NO_LOCALNETSv4_DST : " and " DIVERT_NO_LOCALNETSv6_DST,
*pf_tcp_src ? "" : " and false",
*f_tcpin ? " and " : "",
*f_tcpin ? f_tcpin : "",
*pf_tcp_src ? " and " : "",
*pf_tcp_src ? pf_tcp_src : "",
*pf_tcp_src ? " and " : "",
*pf_tcp_src ? ipv4 ? ipv6 ? DIVERT_NO_LOCALNETS_SRC : DIVERT_NO_LOCALNETSv4_SRC : DIVERT_NO_LOCALNETSv6_SRC : ""
);
return true;
}
#endif
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int result, v; int result, v;
int option_index = 0; int option_index = 0;
bool daemon = false; bool daemon = false;
char pidfile[256]; char pidfile[256];
#ifdef __CYGWIN__
char windivert_filter[8192], wf_pf_tcp_src[256], wf_pf_tcp_dst[256], wf_pf_udp_src[256], wf_pf_udp_dst[256], wf_save_file[256];
bool wf_ipv4=true, wf_ipv6=true;
*windivert_filter = *wf_pf_tcp_src = *wf_pf_tcp_dst = *wf_pf_udp_src = *wf_pf_udp_dst = *wf_save_file = 0;
#endif
srandom(time(NULL)); srandom(time(NULL));
@ -629,12 +853,14 @@ int main(int argc, char **argv)
LIST_INIT(&params.hostlist_files); LIST_INIT(&params.hostlist_files);
LIST_INIT(&params.hostlist_exclude_files); LIST_INIT(&params.hostlist_exclude_files);
#ifndef __CYGWIN__
if (can_drop_root()) // are we root ? if (can_drop_root()) // are we root ?
{ {
params.uid = params.gid = 0x7FFFFFFF; // default uid:gid params.uid = params.gid = 0x7FFFFFFF; // default uid:gid
params.droproot = true; params.droproot = true;
} }
#endif
const struct option long_options[] = { const struct option long_options[] = {
{"debug",optional_argument,0,0}, // optidx=0 {"debug",optional_argument,0,0}, // optidx=0
@ -647,8 +873,13 @@ int main(int argc, char **argv)
#endif #endif
{"daemon",no_argument,0,0}, // optidx=2 {"daemon",no_argument,0,0}, // optidx=2
{"pidfile",required_argument,0,0}, // optidx=3 {"pidfile",required_argument,0,0}, // optidx=3
#ifndef __CYGWIN__
{"user",required_argument,0,0 }, // optidx=4 {"user",required_argument,0,0 }, // optidx=4
{"uid",required_argument,0,0 }, // optidx=5 {"uid",required_argument,0,0 }, // optidx=5
#else
{"disabled_argument_2",no_argument,0,0}, // optidx=4
{"disabled_argument_3",no_argument,0,0}, // optidx=5
#endif
{"wsize",required_argument,0,0}, // optidx=6 {"wsize",required_argument,0,0}, // optidx=6
{"wssize",required_argument,0,0}, // optidx=7 {"wssize",required_argument,0,0}, // optidx=7
{"wssize-cutoff",required_argument,0,0},// optidx=8 {"wssize-cutoff",required_argument,0,0},// optidx=8
@ -663,7 +894,7 @@ int main(int argc, char **argv)
#elif defined(SO_USER_COOKIE) #elif defined(SO_USER_COOKIE)
{"dpi-desync-sockarg",required_argument,0,0}, // optidx=15 {"dpi-desync-sockarg",required_argument,0,0}, // optidx=15
#else #else
{"disabled_argument_2",no_argument,0,0}, // optidx=15 {"disabled_argument_4",no_argument,0,0}, // optidx=15
#endif #endif
{"dpi-desync-ttl",required_argument,0,0}, // optidx=16 {"dpi-desync-ttl",required_argument,0,0}, // optidx=16
{"dpi-desync-ttl6",required_argument,0,0}, // optidx=17 {"dpi-desync-ttl6",required_argument,0,0}, // optidx=17
@ -701,6 +932,12 @@ int main(int argc, char **argv)
#ifdef __linux__ #ifdef __linux__
{"bind-fix4",no_argument,0,0}, // optidx=48 {"bind-fix4",no_argument,0,0}, // optidx=48
{"bind-fix6",no_argument,0,0}, // optidx=49 {"bind-fix6",no_argument,0,0}, // optidx=49
#elif defined(__CYGWIN__)
{"wf-l3",required_argument,0,0}, // optidx=48
{"wf-tcp",required_argument,0,0}, // optidx=49
{"wf-udp",required_argument,0,0}, // optidx=50
{"wf-raw",required_argument,0,0}, // optidx=51
{"wf-save",required_argument,0,0}, // optidx=52
#endif #endif
{NULL,0,NULL,0} {NULL,0,NULL,0}
}; };
@ -713,6 +950,7 @@ int main(int argc, char **argv)
case 0: /* debug */ case 0: /* debug */
params.debug = !optarg || atoi(optarg); params.debug = !optarg || atoi(optarg);
break; break;
#ifndef __CYGWIN__
case 1: /* qnum or port */ case 1: /* qnum or port */
#ifdef __linux__ #ifdef __linux__
params.qnum = atoi(optarg); params.qnum = atoi(optarg);
@ -733,6 +971,7 @@ int main(int argc, char **argv)
} }
#endif #endif
break; break;
#endif
case 2: /* daemon */ case 2: /* daemon */
daemon = true; daemon = true;
break; break;
@ -740,6 +979,7 @@ int main(int argc, char **argv)
strncpy(pidfile, optarg, sizeof(pidfile)); strncpy(pidfile, optarg, sizeof(pidfile));
pidfile[sizeof(pidfile) - 1] = '\0'; pidfile[sizeof(pidfile) - 1] = '\0';
break; break;
#ifndef __CYGWIN__
case 4: /* user */ case 4: /* user */
{ {
struct passwd *pwd = getpwnam(optarg); struct passwd *pwd = getpwnam(optarg);
@ -762,6 +1002,7 @@ int main(int argc, char **argv)
exit_clean(1); exit_clean(1);
} }
break; break;
#endif
case 6: /* wsize */ case 6: /* wsize */
if (!parse_ws_scale_factor(optarg,&params.wsize,&params.wscale)) if (!parse_ws_scale_factor(optarg,&params.wsize,&params.wscale))
exit_clean(1); exit_clean(1);
@ -847,6 +1088,7 @@ int main(int argc, char **argv)
#endif #endif
} }
break; break;
#ifndef __CYGWIN__
case 15: /* dpi-desync-fwmark/dpi-desync-sockarg */ case 15: /* dpi-desync-fwmark/dpi-desync-sockarg */
#if defined(__linux__) || defined(SO_USER_COOKIE) #if defined(__linux__) || defined(SO_USER_COOKIE)
params.desync_fwmark = 0; params.desync_fwmark = 0;
@ -861,6 +1103,7 @@ int main(int argc, char **argv)
exit_clean(1); exit_clean(1);
#endif #endif
break; break;
#endif
case 16: /* dpi-desync-ttl */ case 16: /* dpi-desync-ttl */
params.desync_ttl = (uint8_t)atoi(optarg); params.desync_ttl = (uint8_t)atoi(optarg);
break; break;
@ -1069,8 +1312,10 @@ int main(int argc, char **argv)
fprintf(stderr, "gzipped auto hostlists are not supported\n"); fprintf(stderr, "gzipped auto hostlists are not supported\n");
exit_clean(1); exit_clean(1);
} }
#ifndef __CYGWIN__
if (params.droproot && chown(optarg, params.uid, -1)) if (params.droproot && chown(optarg, params.uid, -1))
fprintf(stderr, "could not chown %s. auto hostlist file may not be writable after privilege drop\n", optarg); fprintf(stderr, "could not chown %s. auto hostlist file may not be writable after privilege drop\n", optarg);
#endif
} }
if (!strlist_add(&params.hostlist_files, optarg)) if (!strlist_add(&params.hostlist_files, optarg))
{ {
@ -1113,8 +1358,10 @@ int main(int argc, char **argv)
exit_clean(1); exit_clean(1);
} }
fclose(F); fclose(F);
#ifndef __CYGWIN__
if (params.droproot && chown(optarg, params.uid, -1)) if (params.droproot && chown(optarg, params.uid, -1))
fprintf(stderr, "could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", optarg); fprintf(stderr, "could not chown %s. auto hostlist debug log may not be writable after privilege drop\n", optarg);
#endif
strncpy(params.hostlist_auto_debuglog, optarg, sizeof(params.hostlist_auto_debuglog)); strncpy(params.hostlist_auto_debuglog, optarg, sizeof(params.hostlist_auto_debuglog));
params.hostlist_auto_debuglog[sizeof(params.hostlist_auto_debuglog) - 1] = '\0'; params.hostlist_auto_debuglog[sizeof(params.hostlist_auto_debuglog) - 1] = '\0';
} }
@ -1126,6 +1373,48 @@ int main(int argc, char **argv)
case 49: /* bind-fix6 */ case 49: /* bind-fix6 */
params.bind_fix6 = true; params.bind_fix6 = true;
break; break;
#elif defined(__CYGWIN__)
case 48: /* wf-l3 */
if (!wf_make_l3(optarg,&wf_ipv4,&wf_ipv6))
{
fprintf(stderr, "bad value for --wf-l3\n");
exit_clean(1);
}
break;
case 49: /* wf-tcp */
if (!wf_make_pf(optarg,"tcp","SrcPort",wf_pf_tcp_src,sizeof(wf_pf_tcp_src)) ||
!wf_make_pf(optarg,"tcp","DstPort",wf_pf_tcp_dst,sizeof(wf_pf_tcp_dst)))
{
fprintf(stderr, "bad value for --wf-tcp\n");
exit_clean(1);
}
break;
case 50: /* wf-udp */
if (!wf_make_pf(optarg,"udp","SrcPort",wf_pf_udp_src,sizeof(wf_pf_udp_src)) ||
!wf_make_pf(optarg,"udp","DstPort",wf_pf_udp_dst,sizeof(wf_pf_udp_dst)))
{
fprintf(stderr, "bad value for --wf-udp\n");
exit_clean(1);
}
break;
case 51: /* wf-raw */
if (optarg[0]=='@')
{
size_t sz = sizeof(windivert_filter)-1;
load_file_or_exit(optarg+1,windivert_filter,&sz);
windivert_filter[sz] = 0;
}
else
{
strncpy(windivert_filter, optarg, sizeof(windivert_filter));
windivert_filter[sizeof(windivert_filter) - 1] = '\0';
}
break;
case 52: /* wf-save */
strncpy(wf_save_file, optarg, sizeof(wf_save_file));
wf_save_file[sizeof(wf_save_file) - 1] = '\0';
break;
#endif #endif
} }
} }
@ -1142,6 +1431,34 @@ int main(int argc, char **argv)
fprintf(stderr, "Need divert port (--port)\n"); fprintf(stderr, "Need divert port (--port)\n");
exit_clean(1); exit_clean(1);
} }
#elif defined(__CYGWIN__)
if (!*windivert_filter)
{
if (!*wf_pf_tcp_src && !*wf_pf_udp_src)
{
fprintf(stderr, "windivert filter : must specify port filter\n");
exit_clean(1);
}
if (!wf_make_filter(windivert_filter, sizeof(windivert_filter), wf_ipv4, wf_ipv6, wf_pf_tcp_src, wf_pf_tcp_dst, wf_pf_udp_src, wf_pf_udp_dst))
{
fprintf(stderr, "windivert filter : could not make filter\n");
exit_clean(1);
}
}
DLOG("windivert filter size: %zu\nwindivert filter:\n%s\n",strlen(windivert_filter),windivert_filter)
if (*wf_save_file)
{
if (save_file(wf_save_file,windivert_filter,strlen(windivert_filter)))
{
printf("windivert filter: raw filter saved to %s\n", wf_save_file);
exit_clean(0);
}
else
{
fprintf(stderr, "windivert filter: could not save raw filter to %s\n", wf_save_file);
exit_clean(0);
}
}
#endif #endif
// not specified - use desync_ttl value instead // not specified - use desync_ttl value instead
@ -1157,7 +1474,8 @@ int main(int argc, char **argv)
fprintf(stderr, "Include hostlist load failed\n"); fprintf(stderr, "Include hostlist load failed\n");
exit_clean(1); exit_clean(1);
} }
if (*params.hostlist_auto_filename) NonEmptyHostlist(&params.hostlist); if (*params.hostlist_auto_filename)
NonEmptyHostlist(&params.hostlist);
if (!LoadExcludeHostLists()) if (!LoadExcludeHostLists())
{ {
fprintf(stderr, "Exclude hostlist load failed\n"); fprintf(stderr, "Exclude hostlist load failed\n");
@ -1179,6 +1497,8 @@ int main(int argc, char **argv)
result = nfq_main(); result = nfq_main();
#elif defined(BSD) #elif defined(BSD)
result = dvt_main(); result = dvt_main();
#elif defined(__CYGWIN__)
result = win_main(windivert_filter);
#else #else
#error unsupported OS #error unsupported OS
#endif #endif

View File

@ -61,9 +61,12 @@ struct params_s
uint8_t fake_unknown_udp[1472],udplen_pattern[1472],fake_quic[1472],fake_wg[1472],fake_dht[1472]; uint8_t fake_unknown_udp[1472],udplen_pattern[1472],fake_quic[1472],fake_wg[1472],fake_dht[1472];
size_t fake_http_size,fake_tls_size,fake_quic_size,fake_wg_size,fake_dht_size,fake_unknown_size,fake_syndata_size,fake_unknown_udp_size; size_t fake_http_size,fake_tls_size,fake_quic_size,fake_wg_size,fake_dht_size,fake_unknown_size,fake_syndata_size,fake_unknown_udp_size;
int udplen_increment; int udplen_increment;
#ifndef __CYGWIN__
bool droproot; bool droproot;
uid_t uid; uid_t uid;
gid_t gid; gid_t gid;
#endif
strpool *hostlist, *hostlist_exclude; strpool *hostlist, *hostlist_exclude;
struct str_list_head hostlist_files, hostlist_exclude_files; struct str_list_head hostlist_files, hostlist_exclude_files;

View File

@ -270,17 +270,18 @@ bool dropcaps(void)
} }
return true; return true;
} }
#else // __linux__
#endif // __linux__
#ifndef __CYGWIN__
#ifndef __linux__
bool sec_harden(void) bool sec_harden(void)
{ {
// noop // noop
return true; return true;
} }
#endif
#endif // __linux__
bool can_drop_root(void) bool can_drop_root(void)
{ {
@ -329,6 +330,7 @@ void print_id(void)
{ {
int i,N; int i,N;
gid_t g[128]; gid_t g[128];
printf("Running as UID=%u GID=",getuid()); printf("Running as UID=%u GID=",getuid());
N=getgroups(sizeof(g)/sizeof(*g),g); N=getgroups(sizeof(g)/sizeof(*g),g);
if (N>0) if (N>0)
@ -341,6 +343,9 @@ void print_id(void)
printf("%u\n",getgid()); printf("%u\n",getgid());
} }
#endif
void daemonize(void) void daemonize(void)
{ {
int pid; int pid;

View File

@ -52,9 +52,12 @@ bool dropcaps(void);
#endif #endif
#ifndef __CYGWIN__
bool sec_harden(void); bool sec_harden(void);
bool can_drop_root(void); bool can_drop_root(void);
bool droproot(uid_t uid, gid_t gid); bool droproot(uid_t uid, gid_t gid);
void print_id(void); void print_id(void);
#endif
void daemonize(void); void daemonize(void);
bool writepid(const char *filename); bool writepid(const char *filename);

Binary file not shown.

635
nfq/windivert/windivert.h Normal file
View File

@ -0,0 +1,635 @@
// WinDivert 2.2.2. MODDED
/*
* windivert.h
* (C) 2019, all rights reserved,
*
* This file is part of WinDivert.
*
* WinDivert is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* WinDivert is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __WINDIVERT_H
#define __WINDIVERT_H
#ifndef WINDIVERT_KERNEL
#include <windows.h>
#endif /* WINDIVERT_KERNEL */
#ifndef WINDIVERTEXPORT
#define WINDIVERTEXPORT extern __declspec(dllimport)
#endif /* WINDIVERTEXPORT */
#ifdef __MINGW32__
#define __in
#define __in_opt
#define __out
#define __out_opt
#define __inout
#define __inout_opt
#include <stdint.h>
#define INT8 int8_t
#define UINT8 uint8_t
#define INT16 int16_t
#define UINT16 uint16_t
#define INT32 int32_t
#define UINT32 uint32_t
#define INT64 int64_t
#define UINT64 uint64_t
#endif /* __MINGW32__ */
#ifdef __cplusplus
extern "C" {
#endif
/****************************************************************************/
/* WINDIVERT API */
/****************************************************************************/
/*
* WinDivert layers.
*/
typedef enum
{
WINDIVERT_LAYER_NETWORK = 0, /* Network layer. */
WINDIVERT_LAYER_NETWORK_FORWARD = 1,/* Network layer (forwarded packets) */
WINDIVERT_LAYER_FLOW = 2, /* Flow layer. */
WINDIVERT_LAYER_SOCKET = 3, /* Socket layer. */
WINDIVERT_LAYER_REFLECT = 4, /* Reflect layer. */
} WINDIVERT_LAYER, *PWINDIVERT_LAYER;
/*
* WinDivert NETWORK and NETWORK_FORWARD layer data.
*/
typedef struct
{
UINT32 IfIdx; /* Packet's interface index. */
UINT32 SubIfIdx; /* Packet's sub-interface index. */
} WINDIVERT_DATA_NETWORK, *PWINDIVERT_DATA_NETWORK;
/*
* WinDivert FLOW layer data.
*/
typedef struct
{
UINT64 EndpointId; /* Endpoint ID. */
UINT64 ParentEndpointId; /* Parent endpoint ID. */
UINT32 ProcessId; /* Process ID. */
UINT32 LocalAddr[4]; /* Local address. */
UINT32 RemoteAddr[4]; /* Remote address. */
UINT16 LocalPort; /* Local port. */
UINT16 RemotePort; /* Remote port. */
UINT8 Protocol; /* Protocol. */
} WINDIVERT_DATA_FLOW, *PWINDIVERT_DATA_FLOW;
/*
* WinDivert SOCKET layer data.
*/
typedef struct
{
UINT64 EndpointId; /* Endpoint ID. */
UINT64 ParentEndpointId; /* Parent Endpoint ID. */
UINT32 ProcessId; /* Process ID. */
UINT32 LocalAddr[4]; /* Local address. */
UINT32 RemoteAddr[4]; /* Remote address. */
UINT16 LocalPort; /* Local port. */
UINT16 RemotePort; /* Remote port. */
UINT8 Protocol; /* Protocol. */
} WINDIVERT_DATA_SOCKET, *PWINDIVERT_DATA_SOCKET;
/*
* WinDivert REFLECTION layer data.
*/
typedef struct
{
INT64 Timestamp; /* Handle open time. */
UINT32 ProcessId; /* Handle process ID. */
WINDIVERT_LAYER Layer; /* Handle layer. */
UINT64 Flags; /* Handle flags. */
INT16 Priority; /* Handle priority. */
} WINDIVERT_DATA_REFLECT, *PWINDIVERT_DATA_REFLECT;
/*
* WinDivert address.
*/
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4201)
#endif
typedef struct
{
INT64 Timestamp; /* Packet's timestamp. */
UINT32 Layer:8; /* Packet's layer. */
UINT32 Event:8; /* Packet event. */
UINT32 Sniffed:1; /* Packet was sniffed? */
UINT32 Outbound:1; /* Packet is outound? */
UINT32 Loopback:1; /* Packet is loopback? */
UINT32 Impostor:1; /* Packet is impostor? */
UINT32 IPv6:1; /* Packet is IPv6? */
UINT32 IPChecksum:1; /* Packet has valid IPv4 checksum? */
// MODDED : UDPChecksum and TCPChecksum in original version are exchanged
UINT32 UDPChecksum:1; /* Packet has valid UDP checksum? */
UINT32 TCPChecksum:1; /* Packet has valid TCP checksum? */
UINT32 Reserved1:8;
UINT32 Reserved2;
union
{
WINDIVERT_DATA_NETWORK Network; /* Network layer data. */
WINDIVERT_DATA_FLOW Flow; /* Flow layer data. */
WINDIVERT_DATA_SOCKET Socket; /* Socket layer data. */
WINDIVERT_DATA_REFLECT Reflect; /* Reflect layer data. */
UINT8 Reserved3[64];
};
} WINDIVERT_ADDRESS, *PWINDIVERT_ADDRESS;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
/*
* WinDivert events.
*/
typedef enum
{
WINDIVERT_EVENT_NETWORK_PACKET = 0, /* Network packet. */
WINDIVERT_EVENT_FLOW_ESTABLISHED = 1,
/* Flow established. */
WINDIVERT_EVENT_FLOW_DELETED = 2, /* Flow deleted. */
WINDIVERT_EVENT_SOCKET_BIND = 3, /* Socket bind. */
WINDIVERT_EVENT_SOCKET_CONNECT = 4, /* Socket connect. */
WINDIVERT_EVENT_SOCKET_LISTEN = 5, /* Socket listen. */
WINDIVERT_EVENT_SOCKET_ACCEPT = 6, /* Socket accept. */
WINDIVERT_EVENT_SOCKET_CLOSE = 7, /* Socket close. */
WINDIVERT_EVENT_REFLECT_OPEN = 8, /* WinDivert handle opened. */
WINDIVERT_EVENT_REFLECT_CLOSE = 9, /* WinDivert handle closed. */
} WINDIVERT_EVENT, *PWINDIVERT_EVENT;
/*
* WinDivert flags.
*/
#define WINDIVERT_FLAG_SNIFF 0x0001
#define WINDIVERT_FLAG_DROP 0x0002
#define WINDIVERT_FLAG_RECV_ONLY 0x0004
#define WINDIVERT_FLAG_READ_ONLY WINDIVERT_FLAG_RECV_ONLY
#define WINDIVERT_FLAG_SEND_ONLY 0x0008
#define WINDIVERT_FLAG_WRITE_ONLY WINDIVERT_FLAG_SEND_ONLY
#define WINDIVERT_FLAG_NO_INSTALL 0x0010
#define WINDIVERT_FLAG_FRAGMENTS 0x0020
/*
* WinDivert parameters.
*/
typedef enum
{
WINDIVERT_PARAM_QUEUE_LENGTH = 0, /* Packet queue length. */
WINDIVERT_PARAM_QUEUE_TIME = 1, /* Packet queue time. */
WINDIVERT_PARAM_QUEUE_SIZE = 2, /* Packet queue size. */
WINDIVERT_PARAM_VERSION_MAJOR = 3, /* Driver version (major). */
WINDIVERT_PARAM_VERSION_MINOR = 4, /* Driver version (minor). */
} WINDIVERT_PARAM, *PWINDIVERT_PARAM;
#define WINDIVERT_PARAM_MAX WINDIVERT_PARAM_VERSION_MINOR
/*
* WinDivert shutdown parameter.
*/
typedef enum
{
WINDIVERT_SHUTDOWN_RECV = 0x1, /* Shutdown recv. */
WINDIVERT_SHUTDOWN_SEND = 0x2, /* Shutdown send. */
WINDIVERT_SHUTDOWN_BOTH = 0x3, /* Shutdown recv and send. */
} WINDIVERT_SHUTDOWN, *PWINDIVERT_SHUTDOWN;
#define WINDIVERT_SHUTDOWN_MAX WINDIVERT_SHUTDOWN_BOTH
#ifndef WINDIVERT_KERNEL
/*
* Open a WinDivert handle.
*/
WINDIVERTEXPORT HANDLE WinDivertOpen(
__in const char *filter,
__in WINDIVERT_LAYER layer,
__in INT16 priority,
__in UINT64 flags);
/*
* Receive (read) a packet from a WinDivert handle.
*/
WINDIVERTEXPORT BOOL WinDivertRecv(
__in HANDLE handle,
__out_opt VOID *pPacket,
__in UINT packetLen,
__out_opt UINT *pRecvLen,
__out_opt WINDIVERT_ADDRESS *pAddr);
/*
* Receive (read) a packet from a WinDivert handle.
*/
WINDIVERTEXPORT BOOL WinDivertRecvEx(
__in HANDLE handle,
__out_opt VOID *pPacket,
__in UINT packetLen,
__out_opt UINT *pRecvLen,
__in UINT64 flags,
__out WINDIVERT_ADDRESS *pAddr,
__inout_opt UINT *pAddrLen,
__inout_opt LPOVERLAPPED lpOverlapped);
/*
* Send (write/inject) a packet to a WinDivert handle.
*/
WINDIVERTEXPORT BOOL WinDivertSend(
__in HANDLE handle,
__in const VOID *pPacket,
__in UINT packetLen,
__out_opt UINT *pSendLen,
__in const WINDIVERT_ADDRESS *pAddr);
/*
* Send (write/inject) a packet to a WinDivert handle.
*/
WINDIVERTEXPORT BOOL WinDivertSendEx(
__in HANDLE handle,
__in const VOID *pPacket,
__in UINT packetLen,
__out_opt UINT *pSendLen,
__in UINT64 flags,
__in const WINDIVERT_ADDRESS *pAddr,
__in UINT addrLen,
__inout_opt LPOVERLAPPED lpOverlapped);
/*
* Shutdown a WinDivert handle.
*/
WINDIVERTEXPORT BOOL WinDivertShutdown(
__in HANDLE handle,
__in WINDIVERT_SHUTDOWN how);
/*
* Close a WinDivert handle.
*/
WINDIVERTEXPORT BOOL WinDivertClose(
__in HANDLE handle);
/*
* Set a WinDivert handle parameter.
*/
WINDIVERTEXPORT BOOL WinDivertSetParam(
__in HANDLE handle,
__in WINDIVERT_PARAM param,
__in UINT64 value);
/*
* Get a WinDivert handle parameter.
*/
WINDIVERTEXPORT BOOL WinDivertGetParam(
__in HANDLE handle,
__in WINDIVERT_PARAM param,
__out UINT64 *pValue);
#endif /* WINDIVERT_KERNEL */
/*
* WinDivert constants.
*/
#define WINDIVERT_PRIORITY_HIGHEST 30000
#define WINDIVERT_PRIORITY_LOWEST (-WINDIVERT_PRIORITY_HIGHEST)
#define WINDIVERT_PARAM_QUEUE_LENGTH_DEFAULT 4096
#define WINDIVERT_PARAM_QUEUE_LENGTH_MIN 32
#define WINDIVERT_PARAM_QUEUE_LENGTH_MAX 16384
#define WINDIVERT_PARAM_QUEUE_TIME_DEFAULT 2000 /* 2s */
#define WINDIVERT_PARAM_QUEUE_TIME_MIN 100 /* 100ms */
#define WINDIVERT_PARAM_QUEUE_TIME_MAX 16000 /* 16s */
#define WINDIVERT_PARAM_QUEUE_SIZE_DEFAULT 4194304 /* 4MB */
#define WINDIVERT_PARAM_QUEUE_SIZE_MIN 65535 /* 64KB */
#define WINDIVERT_PARAM_QUEUE_SIZE_MAX 33554432 /* 32MB */
#define WINDIVERT_BATCH_MAX 0xFF /* 255 */
#define WINDIVERT_MTU_MAX (40 + 0xFFFF)
/****************************************************************************/
/* WINDIVERT HELPER API */
/****************************************************************************/
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4214)
#endif
/*
* IPv4/IPv6/ICMP/ICMPv6/TCP/UDP header definitions.
*/
typedef struct
{
UINT8 HdrLength:4;
UINT8 Version:4;
UINT8 TOS;
UINT16 Length;
UINT16 Id;
UINT16 FragOff0;
UINT8 TTL;
UINT8 Protocol;
UINT16 Checksum;
UINT32 SrcAddr;
UINT32 DstAddr;
} WINDIVERT_IPHDR, *PWINDIVERT_IPHDR;
#define WINDIVERT_IPHDR_GET_FRAGOFF(hdr) \
(((hdr)->FragOff0) & 0xFF1F)
#define WINDIVERT_IPHDR_GET_MF(hdr) \
((((hdr)->FragOff0) & 0x0020) != 0)
#define WINDIVERT_IPHDR_GET_DF(hdr) \
((((hdr)->FragOff0) & 0x0040) != 0)
#define WINDIVERT_IPHDR_GET_RESERVED(hdr) \
((((hdr)->FragOff0) & 0x0080) != 0)
#define WINDIVERT_IPHDR_SET_FRAGOFF(hdr, val) \
do \
{ \
(hdr)->FragOff0 = (((hdr)->FragOff0) & 0x00E0) | \
((val) & 0xFF1F); \
} \
while (FALSE)
#define WINDIVERT_IPHDR_SET_MF(hdr, val) \
do \
{ \
(hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFFDF) | \
(((val) & 0x0001) << 5); \
} \
while (FALSE)
#define WINDIVERT_IPHDR_SET_DF(hdr, val) \
do \
{ \
(hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFFBF) | \
(((val) & 0x0001) << 6); \
} \
while (FALSE)
#define WINDIVERT_IPHDR_SET_RESERVED(hdr, val) \
do \
{ \
(hdr)->FragOff0 = (((hdr)->FragOff0) & 0xFF7F) | \
(((val) & 0x0001) << 7); \
} \
while (FALSE)
typedef struct
{
UINT8 TrafficClass0:4;
UINT8 Version:4;
UINT8 FlowLabel0:4;
UINT8 TrafficClass1:4;
UINT16 FlowLabel1;
UINT16 Length;
UINT8 NextHdr;
UINT8 HopLimit;
UINT32 SrcAddr[4];
UINT32 DstAddr[4];
} WINDIVERT_IPV6HDR, *PWINDIVERT_IPV6HDR;
#define WINDIVERT_IPV6HDR_GET_TRAFFICCLASS(hdr) \
((((hdr)->TrafficClass0) << 4) | ((hdr)->TrafficClass1))
#define WINDIVERT_IPV6HDR_GET_FLOWLABEL(hdr) \
((((UINT32)(hdr)->FlowLabel0) << 16) | ((UINT32)(hdr)->FlowLabel1))
#define WINDIVERT_IPV6HDR_SET_TRAFFICCLASS(hdr, val) \
do \
{ \
(hdr)->TrafficClass0 = ((UINT8)(val) >> 4); \
(hdr)->TrafficClass1 = (UINT8)(val); \
} \
while (FALSE)
#define WINDIVERT_IPV6HDR_SET_FLOWLABEL(hdr, val) \
do \
{ \
(hdr)->FlowLabel0 = (UINT8)((val) >> 16); \
(hdr)->FlowLabel1 = (UINT16)(val); \
} \
while (FALSE)
typedef struct
{
UINT8 Type;
UINT8 Code;
UINT16 Checksum;
UINT32 Body;
} WINDIVERT_ICMPHDR, *PWINDIVERT_ICMPHDR;
typedef struct
{
UINT8 Type;
UINT8 Code;
UINT16 Checksum;
UINT32 Body;
} WINDIVERT_ICMPV6HDR, *PWINDIVERT_ICMPV6HDR;
typedef struct
{
UINT16 SrcPort;
UINT16 DstPort;
UINT32 SeqNum;
UINT32 AckNum;
UINT16 Reserved1:4;
UINT16 HdrLength:4;
UINT16 Fin:1;
UINT16 Syn:1;
UINT16 Rst:1;
UINT16 Psh:1;
UINT16 Ack:1;
UINT16 Urg:1;
UINT16 Reserved2:2;
UINT16 Window;
UINT16 Checksum;
UINT16 UrgPtr;
} WINDIVERT_TCPHDR, *PWINDIVERT_TCPHDR;
typedef struct
{
UINT16 SrcPort;
UINT16 DstPort;
UINT16 Length;
UINT16 Checksum;
} WINDIVERT_UDPHDR, *PWINDIVERT_UDPHDR;
#ifdef _MSC_VER
#pragma warning(pop)
#endif
/*
* Flags for WinDivertHelperCalcChecksums()
*/
#define WINDIVERT_HELPER_NO_IP_CHECKSUM 1
#define WINDIVERT_HELPER_NO_ICMP_CHECKSUM 2
#define WINDIVERT_HELPER_NO_ICMPV6_CHECKSUM 4
#define WINDIVERT_HELPER_NO_TCP_CHECKSUM 8
#define WINDIVERT_HELPER_NO_UDP_CHECKSUM 16
#ifndef WINDIVERT_KERNEL
/*
* Hash a packet.
*/
WINDIVERTEXPORT UINT64 WinDivertHelperHashPacket(
__in const VOID *pPacket,
__in UINT packetLen,
__in UINT64 seed
#ifdef __cplusplus
= 0
#endif
);
/*
* Parse IPv4/IPv6/ICMP/ICMPv6/TCP/UDP headers from a raw packet.
*/
WINDIVERTEXPORT BOOL WinDivertHelperParsePacket(
__in const VOID *pPacket,
__in UINT packetLen,
__out_opt PWINDIVERT_IPHDR *ppIpHdr,
__out_opt PWINDIVERT_IPV6HDR *ppIpv6Hdr,
__out_opt UINT8 *pProtocol,
__out_opt PWINDIVERT_ICMPHDR *ppIcmpHdr,
__out_opt PWINDIVERT_ICMPV6HDR *ppIcmpv6Hdr,
__out_opt PWINDIVERT_TCPHDR *ppTcpHdr,
__out_opt PWINDIVERT_UDPHDR *ppUdpHdr,
__out_opt PVOID *ppData,
__out_opt UINT *pDataLen,
__out_opt PVOID *ppNext,
__out_opt UINT *pNextLen);
/*
* Parse an IPv4 address.
*/
WINDIVERTEXPORT BOOL WinDivertHelperParseIPv4Address(
__in const char *addrStr,
__out_opt UINT32 *pAddr);
/*
* Parse an IPv6 address.
*/
WINDIVERTEXPORT BOOL WinDivertHelperParseIPv6Address(
__in const char *addrStr,
__out_opt UINT32 *pAddr);
/*
* Format an IPv4 address.
*/
WINDIVERTEXPORT BOOL WinDivertHelperFormatIPv4Address(
__in UINT32 addr,
__out char *buffer,
__in UINT bufLen);
/*
* Format an IPv6 address.
*/
WINDIVERTEXPORT BOOL WinDivertHelperFormatIPv6Address(
__in const UINT32 *pAddr,
__out char *buffer,
__in UINT bufLen);
/*
* Calculate IPv4/IPv6/ICMP/ICMPv6/TCP/UDP checksums.
*/
WINDIVERTEXPORT BOOL WinDivertHelperCalcChecksums(
__inout VOID *pPacket,
__in UINT packetLen,
__out_opt WINDIVERT_ADDRESS *pAddr,
__in UINT64 flags);
/*
* Decrement the TTL/HopLimit.
*/
WINDIVERTEXPORT BOOL WinDivertHelperDecrementTTL(
__inout VOID *pPacket,
__in UINT packetLen);
/*
* Compile the given filter string.
*/
WINDIVERTEXPORT BOOL WinDivertHelperCompileFilter(
__in const char *filter,
__in WINDIVERT_LAYER layer,
__out_opt char *object,
__in UINT objLen,
__out_opt const char **errorStr,
__out_opt UINT *errorPos);
/*
* Evaluate the given filter string.
*/
WINDIVERTEXPORT BOOL WinDivertHelperEvalFilter(
__in const char *filter,
__in const VOID *pPacket,
__in UINT packetLen,
__in const WINDIVERT_ADDRESS *pAddr);
/*
* Format the given filter string.
*/
WINDIVERTEXPORT BOOL WinDivertHelperFormatFilter(
__in const char *filter,
__in WINDIVERT_LAYER layer,
__out char *buffer,
__in UINT bufLen);
/*
* Byte ordering.
*/
WINDIVERTEXPORT UINT16 WinDivertHelperNtohs(
__in UINT16 x);
WINDIVERTEXPORT UINT16 WinDivertHelperHtons(
__in UINT16 x);
WINDIVERTEXPORT UINT32 WinDivertHelperNtohl(
__in UINT32 x);
WINDIVERTEXPORT UINT32 WinDivertHelperHtonl(
__in UINT32 x);
WINDIVERTEXPORT UINT64 WinDivertHelperNtohll(
__in UINT64 x);
WINDIVERTEXPORT UINT64 WinDivertHelperHtonll(
__in UINT64 x);
WINDIVERTEXPORT void WinDivertHelperNtohIPv6Address(
__in const UINT *inAddr,
__out UINT *outAddr);
WINDIVERTEXPORT void WinDivertHelperHtonIPv6Address(
__in const UINT *inAddr,
__out UINT *outAddr);
/*
* Old names to be removed in the next version.
*/
WINDIVERTEXPORT void WinDivertHelperNtohIpv6Address(
__in const UINT *inAddr,
__out UINT *outAddr);
WINDIVERTEXPORT void WinDivertHelperHtonIpv6Address(
__in const UINT *inAddr,
__out UINT *outAddr);
#endif /* WINDIVERT_KERNEL */
#ifdef __cplusplus
}
#endif
#endif /* __WINDIVERT_H */

View File

@ -229,3 +229,35 @@ time_t file_mod_time(const char *filename)
struct stat st; struct stat st;
return stat(filename,&st)==-1 ? 0 : st.st_mtime; return stat(filename,&st)==-1 ? 0 : st.st_mtime;
} }
bool pf_in_range(uint16_t port, const port_filter *pf)
{
return port && ((!pf->from && !pf->to || port>=pf->from && port<=pf->to) ^ pf->neg);
}
bool pf_parse(const char *s, port_filter *pf)
{
unsigned int v1,v2;
if (!s) return false;
if (*s=='~')
{
pf->neg=true;
s++;
}
else
pf->neg=false;
if (sscanf(s,"%u-%u",&v1,&v2)==2)
{
if (!v1 || v1>65535 || v2>65535 || v1>v2) return false;
pf->from=(uint16_t)v1;
pf->to=(uint16_t)v2;
}
else if (sscanf(s,"%u",&v1)==1)
{
if (!v1 || v1>65535) return false;
pf->to=pf->from=(uint16_t)v1;
}
else
return false;
return true;
}

View File

@ -47,3 +47,11 @@ static inline void phton16(uint8_t *p, uint16_t v) {
int fprint_localtime(FILE *F); int fprint_localtime(FILE *F);
time_t file_mod_time(const char *filename); time_t file_mod_time(const char *filename);
typedef struct
{
uint16_t from,to;
bool neg;
} port_filter;
bool pf_in_range(uint16_t port, const port_filter *pf);
bool pf_parse(const char *s, port_filter *pf);

View File

@ -8,6 +8,7 @@
#include <time.h> #include <time.h>
#include "tpws.h" #include "tpws.h"
#include "pools.h" #include "pools.h"
#include "helpers.h"
#define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 3 #define HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT 3
#define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60 #define HOSTLIST_AUTO_FAIL_TIME_DEFAULT 60
@ -25,12 +26,6 @@ struct bind_s
int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll; int bind_wait_ifup,bind_wait_ip,bind_wait_ip_ll;
}; };
typedef struct
{
uint16_t from,to;
bool neg;
} port_filter;
struct params_s struct params_s
{ {
struct bind_s binds[MAX_BINDS]; struct bind_s binds[MAX_BINDS];

View File

@ -1530,37 +1530,3 @@ ex:
if (resolve_pipe[1]) close(resolve_pipe[1]); if (resolve_pipe[1]) close(resolve_pipe[1]);
return retval; return retval;
} }
bool pf_in_range(uint16_t port, const port_filter *pf)
{
return port && ((!pf->from && !pf->to || port>=pf->from && port<=pf->to) ^ pf->neg);
}
bool pf_parse(const char *s, port_filter *pf)
{
unsigned int v1,v2;
if (!s) return false;
if (*s=='~')
{
pf->neg=true;
s++;
}
else
pf->neg=false;
if (sscanf(s,"%u-%u",&v1,&v2)==2)
{
if (!v1 || v1>65535 || v2>65535 || v1>v2) return false;
pf->from=(uint16_t)v1;
pf->to=(uint16_t)v2;
}
else if (sscanf(s,"%u",&v1)==1)
{
if (!v1 || v1>65535) return false;
pf->to=pf->from=(uint16_t)v1;
}
else
return false;
return true;
}

View File

@ -103,6 +103,3 @@ TAILQ_HEAD(tailhead, tproxy_conn);
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf); bool set_socket_buffers(int fd, int rcvbuf, int sndbuf);
bool pf_in_range(uint16_t port, const port_filter *pf);
bool pf_parse(const char *s, port_filter *pf);