diff --git a/binaries/aarch64/nfqws b/binaries/aarch64/nfqws index e4c3af5..90fbbcc 100755 Binary files a/binaries/aarch64/nfqws and b/binaries/aarch64/nfqws differ diff --git a/binaries/arm/nfqws b/binaries/arm/nfqws index 02ad3db..59bbf08 100755 Binary files a/binaries/arm/nfqws and b/binaries/arm/nfqws differ diff --git a/binaries/freebsd-x64/dvtws b/binaries/freebsd-x64/dvtws index cce0452..5a8d087 100755 Binary files a/binaries/freebsd-x64/dvtws and b/binaries/freebsd-x64/dvtws differ diff --git a/binaries/mips32r1-lsb/nfqws b/binaries/mips32r1-lsb/nfqws index f2e3a3b..13bcf23 100755 Binary files a/binaries/mips32r1-lsb/nfqws and b/binaries/mips32r1-lsb/nfqws differ diff --git a/binaries/mips32r1-msb/nfqws b/binaries/mips32r1-msb/nfqws index 68addf9..bb5b6f4 100755 Binary files a/binaries/mips32r1-msb/nfqws and b/binaries/mips32r1-msb/nfqws differ diff --git a/binaries/mips64r2-msb/nfqws b/binaries/mips64r2-msb/nfqws index 4b76197..5d351d3 100755 Binary files a/binaries/mips64r2-msb/nfqws and b/binaries/mips64r2-msb/nfqws differ diff --git a/binaries/ppc/nfqws b/binaries/ppc/nfqws index aa585be..3a3ffd7 100755 Binary files a/binaries/ppc/nfqws and b/binaries/ppc/nfqws differ diff --git a/binaries/win64/winws.exe b/binaries/win64/winws.exe index 785b137..9984d50 100644 Binary files a/binaries/win64/winws.exe and b/binaries/win64/winws.exe differ diff --git a/binaries/win64/zapret-winws/autohostlist.txt b/binaries/win64/zapret-winws/autohostlist.txt deleted file mode 100644 index e69de29..0000000 diff --git a/binaries/win64/zapret-winws/list-youtube.txt b/binaries/win64/zapret-winws/list-youtube.txt new file mode 100644 index 0000000..e37b016 --- /dev/null +++ b/binaries/win64/zapret-winws/list-youtube.txt @@ -0,0 +1,3 @@ +googlevideo.com +youtubei.googleapis.com +i.ytimg.com diff --git a/binaries/win64/zapret-winws/preset_russia.cmd b/binaries/win64/zapret-winws/preset_russia.cmd index a6089b6..06928ea 100644 --- a/binaries/win64/zapret-winws/preset_russia.cmd +++ b/binaries/win64/zapret-winws/preset_russia.cmd @@ -1,2 +1,2 @@ -start "zapret: http,https" /min "%~dp0winws.exe" --wf-tcp=80,443 --dpi-desync=fake,disorder2 --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig -start "zapret: quic" /min "%~dp0winws.exe" --wf-udp=443 --dpi-desync=fake --dpi-desync-repeats=11 \ No newline at end of file +start "zapret: http,https,quic" /min "%~dp0winws.exe" --wf-tcp=80,443 --wf-udp=443 --filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=11 --new --filter-tcp=80 --dpi-desync=fake,split2 --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --new --filter-tcp=443 --hostlist="%~dp0list-youtube.txt" --dpi-desync=fake,split2 --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --dpi-desync-fake-tls="%~dp0tls_clienthello_www_google_com.bin" --new --dpi-desync=fake,disorder2 --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig + diff --git a/binaries/win64/zapret-winws/preset_russia_autohostlist.cmd b/binaries/win64/zapret-winws/preset_russia_autohostlist.cmd index 0d0450e..dd9fd2b 100644 --- a/binaries/win64/zapret-winws/preset_russia_autohostlist.cmd +++ b/binaries/win64/zapret-winws/preset_russia_autohostlist.cmd @@ -1,2 +1 @@ -start "zapret: http,https,autohostlist" /min "%~dp0winws.exe" --wf-tcp=80,443 --dpi-desync=fake,disorder2 --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --hostlist-auto="%~dp0autohostlist.txt" -start "zapret: quic,autohostlist" /min "%~dp0winws.exe" /min --wf-udp=443 --dpi-desync=fake --dpi-desync-repeats=11 --hostlist-auto="%~dp0autohostlist.txt" \ No newline at end of file +start "zapret: http,https,quic" /min "%~dp0winws.exe" --wf-tcp=80,443 --wf-udp=443 --filter-udp=443 --dpi-desync=fake --dpi-desync-repeats=11 --new --filter-tcp=80 --dpi-desync=fake,split2 --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --hostlist-auto="%~dp0autohostlist.txt" --new --filter-tcp=443 --hostlist="%~dp0list-youtube.txt" --dpi-desync=fake,split2 --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --dpi-desync-fake-tls="%~dp0tls_clienthello_www_google_com.bin" --new --dpi-desync=fake,disorder2 --dpi-desync-autottl=2 --dpi-desync-fooling=md5sig --hostlist-auto="%~dp0autohostlist.txt" diff --git a/binaries/win64/zapret-winws/tls_clienthello_www_google_com.bin b/binaries/win64/zapret-winws/tls_clienthello_www_google_com.bin new file mode 100644 index 0000000..c740462 Binary files /dev/null and b/binaries/win64/zapret-winws/tls_clienthello_www_google_com.bin differ diff --git a/binaries/win64/zapret-winws/winws.exe b/binaries/win64/zapret-winws/winws.exe index 785b137..9984d50 100644 Binary files a/binaries/win64/zapret-winws/winws.exe and b/binaries/win64/zapret-winws/winws.exe differ diff --git a/binaries/x86/nfqws b/binaries/x86/nfqws index b97171f..e4bbcb3 100755 Binary files a/binaries/x86/nfqws and b/binaries/x86/nfqws differ diff --git a/binaries/x86_64/nfqws b/binaries/x86_64/nfqws index 86a8784..b819db3 100755 Binary files a/binaries/x86_64/nfqws and b/binaries/x86_64/nfqws differ diff --git a/docs/changes.txt b/docs/changes.txt index 797ed92..c36070a 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -308,3 +308,8 @@ nfqws,tpws: detect TLS 1.2 ClientHello from very old libraries with SSL 3.0 vers nfqws,tpws: debug log to file and syslog tpws: --connect-bind-addr option tpws: log local endpoint (including source port number) for remote leg + +v62: + +tpws: connection close logic rewrite. tcp user timeout parameters for local and remote leg. +nfqws: multi-strategy diff --git a/docs/quick_start.txt b/docs/quick_start.txt index 39dc2bd..931b74d 100644 --- a/docs/quick_start.txt +++ b/docs/quick_start.txt @@ -95,7 +95,7 @@ blockcheck не всегда может выдать вам в итогах оп В некоторых случаях надо реально думать что происходит, анализируя результат на разных стратегиях. Если вы применяете большой TTL, чтобы достать до магистрала, то не лишним будет добавить дополнительный ограничитель --dpi-desync-fooling, чтобы не сломать сайты на более коротких дистанциях. -md5sig наиболее совместим, но работает только на linux серверах. +md5sig наиболее совместим, но работатет только на linux серверах. badseq может работать только на https и не работать на http. badsum и вовсе перестал работать на многих провайдерах с некоторых пор, видимо включили проверку чексумм на DPI. Чтобы выяснить какие дополнительные ограничители работают, смотрите результат теста аналогичных стратегий без TTL diff --git a/docs/readme.eng.md b/docs/readme.eng.md index d754a14..e5b581e 100644 --- a/docs/readme.eng.md +++ b/docs/readme.eng.md @@ -206,6 +206,10 @@ nfqws takes the following parameters: --hostlist-auto-fail-time= ; all failed attemps must be within these seconds (default : 60) --hostlist-auto-retrans-threshold= ; how many request retransmissions cause attempt to fail (default : 3) --hostlist-auto-debug= ; debug auto hostlist positives + --new ; begin new strategy + --filter-l3=ipv4|ipv6 ; L3 protocol filter. multiple comma separated values allowed. + --filter-tcp=[~]port1[-port2] ; TCP port filter. ~ means negation. setting tcp and not setting udp filter denies udp. + --filter-udp=[~]port1[-port2] ; UDP port filter. ~ means negation. setting udp and not setting tcp filter denies tcp. ``` The manipulation parameters can be combined in any way. @@ -565,6 +569,29 @@ nfqws sees packets with internal network source address. If fragmented NAT does This results in attempt to send packets to internet with internal IP address. You need to use nftables instead with hook priority 101 or higher. +### multiple strategies + +`nfqws` can apply different strategies to different requests. It's done with multiple desync profiles. +Profiles are delimited by the `--new` parameter. First profile is created automatically and does not require `--new`. +Each profile has a filter. By default it's empty and profile matches any packet. +Filter can have hard parameters : ip version and tcp/udp port range. +Hard parameters are always identified unambiguously even on zero-phase when hostname is unknown yet. +Hostlist can also act as a filter. They can be combined with hard parameters. +When a packet comes profiles are matched from the first to the last until first filter condition match. +Hard filter is matched first. If it does not match verification goes to the next profile. +If a profile matches hard filter and has autohostlist it's selected immediately. +If a profile matches hard filter and has normal hostlist(s) and hostname is unknown yet verification goes to the next profile. +Otherwise profile hostlist(s) are checked for the hostname. If it matches profile is selected. +Otherwise verification goes to the next profile. + +It's possible that before getting hostname connection is served by one profile and after +hostname is revealed it's switched to another profile. +If you use 0-phase desync methods think carefully what can happen during strategy switch. +Use `--debug` logging to understand better what `nfqws` does. + +IMPORTANT : multiple strategies exist only for the case when it's not possible to combine all to one strategy. +Copy-pasting blockcheck results of different websites to multiple strategies lead to the mess. +This way you may never unblock all resources and only confuse yourself. ## tpws diff --git a/docs/readme.txt b/docs/readme.txt index 7b5edaf..733293b 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -1,4 +1,4 @@ -zapret v.61 +zapret v.62 English ------- @@ -40,7 +40,7 @@ For english version refer to docs/readme.eng.txt Активный DPI ставится в разрез провода и может дропать пакеты по любым критериям, в том числе распознавать TCP потоки и блокировать любые пакеты, принадлежащие потоку. -Как не допустить срабатывания триггера запрета ? Послать то, на что DPI не рассчитывает +Как не допустить срабатывания триггера запрета ? Послать то, на что DPI не расчитывает и что ломает ему алгоритм распознавания запросов и их блокировки. Некоторые DPI не могут распознать http запрос, если он разделен на TCP сегменты. @@ -146,7 +146,7 @@ iptables -t mangle -I POSTROUTING -o <внешний_интерфейс> -p tcp iptables -t mangle -I PREROUTING -i <внешний_интерфейс> -p tcp --sport 80 -m connbytes --connbytes-dir=reply --connbytes-mode=packets --connbytes 1:6 -m set --match-set zapret src -j NFQUEUE --queue-num 200 --queue-bypass -Получаемые пакеты будут фильтроваться по входящему интерфейсу, порту и IP источника, то есть наоборот прямому правилу. +Получаемые пакеты будут фильтровться по входящему интерфейсу, порту и IP источника, то есть наоборот прямому правилу. Некоторые техники, ломающие NAT, не всегда можно реализовать через iptables. Требуются nftables. @@ -211,7 +211,7 @@ NFQUEUE работает без изменений. операционной системы, фрагментация отпадает сразу как средство обхода. Squid правильный, он все найдет как надо, обманывать его бесполезно. НО. Заворачивать на squid могут позволить себе лишь небольшие провайдеры, поскольку это очень ресурсоемко. -Большие компании обычно используют DPI, который рассчитан на гораздо большую пропускную способность. +Большие компании обычно используют DPI, который расчитан на гораздо большую пропускную способность. Может применяться комбинированный подход, когда на DPI заворачивают только IP из "плохого" списка, и дальше уже DPI решает пропускать или нет. Так можно снизить нагрузку на DPI в десятки, если не сотни раз, а следовательно не покупать очень дорогие решения, обойдясь чем-то существенно более дешевым. @@ -259,7 +259,7 @@ nfqws --dpi-desync-badack-increment= ; инкремент ack sequence number для badseq. по умолчанию -66000 --dpi-desync-any-protocol=0|1 ; 0(default)=работать только по http request и tls clienthello 1=по всем непустым пакетам данных --dpi-desync-fake-http=|0xHEX ; файл, содержащий фейковый http запрос для dpi-desync=fake, на замену стандартному www.iana.org - --dpi-desync-fake-tls=|0xHEX ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному + --dpi-desync-fake-tls=|0xHEX ; файл, содержащий фейковый tls clienthello для dpi-desync=fake, на замену стандартному www.iana.org --dpi-desync-fake-unknown=|0xHEX ; файл, содержащий фейковый пейлоад неизвестного протокола для dpi-desync=fake, на замену стандартным нулям 256 байт --dpi-desync-fake-syndata=|0xHEX ; файл, содержащий фейковый пейлоад пакета SYN для режима десинхронизации syndata --dpi-desync-fake-quic=|0xHEX ; файл, содержащий фейковый QUIC Initial @@ -269,13 +269,18 @@ nfqws --dpi-desync-udplen-pattern=|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= ; применять дурение только к хостам из листа. может быть множество листов, они объединяются. пустой обший лист = его отсутствие - --hostlist-exclude= ; не применять дурение к хостам из листа. может быть множество листов, они объединяются + --hostlist= ; применять дурение только к хостам из листа. может быть множество листов, они обьединяются. пустой обший лист = его отсутствие + --hostlist-exclude= ; не применять дурение к хостам из листа. может быть множество листов, они обьединяются --hostlist-auto= ; обнаруживать автоматически блокировки и заполнять автоматический hostlist (требует перенаправления входящего трафика) --hostlist-auto-fail-threshold= ; сколько раз нужно обнаружить ситуацию, похожую на блокировку, чтобы добавить хост в лист (по умолчанию: 3) --hostlist-auto-fail-time= ; все эти ситуации должны быть в пределах указанного количества секунд (по умолчанию: 60) --hostlist-auto-retrans-threshold= ; сколько ретрансмиссий запроса считать блокировкой (по умолчанию: 3) --hostlist-auto-debug= ; лог положительных решений по autohostlist. позволяет разобраться почему там появляются хосты. + --new ; начало новой стратегии + --filter-l3=ipv4|ipv6 ; фильтр версии ip для текущей стратегии + --filter-tcp=[~]port1[-port2] ; фильтр портов tcp для текущей стратегии. ~ означает инверсию. установка фильтра tcp и неустановка фильтра udp запрещает udp. + --filter-udp=[~]port1[-port2] ; фильтр портов udp для текущей стратегии. ~ означает инверсию. установка фильтра udp и неустановка фильтра tcp запрещает udp. + Параметры манипуляции могут сочетаться в любых комбинациях. @@ -358,13 +363,14 @@ fakeknown отличается от fake тем, что применяется сервера. Берутся базовые значения TTL 64,128,255, смотрится входящий пакет (да, требуется направить первый входящий пакет на nfqws !). Вычисляется длина пути, отнимается delta (1 по умолчанию). Если TTL вне диапазона (min,max - 3,20 по умолчанию), - то берутся значения min,max, чтобы вписаться в диапазон. Если при этом полученный TTL больше длины пути, + то берутся значения min,max, чтобы вписаться в диапазон. Если при этом полученый TTL больше длины пути, то автоматизм не сработал и берутся фиксированные значения TTL для атаки. Техника позволяет решить вопрос, когда вся сеть перегорожена шлагбаумами (DPI, ТСПУ) везде где только можно, включая магистралов. Но потенциально может давать сбои. - Например, при асимметрии входящего и исходящего канала до конкретного сервера. + Например, при ассиметрии входящего и исходящего канала до конкретного сервера. На каких-то провайдерах эта техника будет работать неплохо, на других доставит больше проблем, чем пользы. Где-то может потребоваться тюнинг параметров. Лучше использовать с дополнительным ограничителем. + Не рекомендуется для BSD систем, поскольку там нельзя ограничить количество входящих пакетов через connbytes. Режимы дурения могут сочетаться в любых комбинациях. --dpi-desync-fooling берет множество значений через запятую. @@ -590,7 +596,7 @@ chrome рандомизирует фингерпринт TLS. SNI может о udplen увеличивает размер udp пакета на указанное в --dpi-desync-udplen-increment количество байтов. Паддинг заполняется нулями по умолчанию, но можно задать свой паттерн. Предназначено для обмана DPI, ориентирующегося на размеры пакетов. -Может сработать, если пользовательский протокол не привязан жестко к размеру udp пейлоада. +Может сработать, если пользовательсткий протокол не привязан жестко к размеру udp пейлоада. Режим tamper означает модификацию пакетов известных протоколов особенным для протокола образом. На текущий момент работает только с DHT. Поддерживается определение пакетов QUIC Initial с расшифровкой содержимого и имени хоста, то есть параметр @@ -654,6 +660,32 @@ options ip6table_raw raw_before_defrag=1 Видимо единственный рабочий метод - отказаться от iptables и использовать nftables. Хук должен быть с приоритетом 101 или выше. +МНОЖЕСТВЕННЫЕ СТРАТЕГИИ +nfqws способен по-разному реагировать на различные запросы и применять разные стратегии дурения. +Это реализовано посредством поддержки множества профилей дурения. +Профили разделяются в командной строке параметром --new. Первый профиль создается автоматически. +Для него не нужно --new. Каждый профиль имеет фильтр. По умолчанию он пуст, то есть профиль удовлетворяет +любым условиям. +Фильтр может содержать жесткие параметры : версия ip протокола или порты tcp/udp. +Они всегда однозначно идентифицируются даже на нулевой фазе десинхронизации, когда еще хост неизвестен. +В качестве фильтра могут выступать и хост-листы. Они могут сочетаться с жесткими параметрами. +При поступлении запроса идет проверка профилей в порядке от первого до последнего до +достижения первого совпадения с фильтром. +Жесткие параметры фильтра сверяются первыми. При несовпадении идет сразу же переход к следующему профилю. +Если какой-то профиль удовлетворяет жесткому фильтру и содержит авто-хостлист, он выбирается сразу. +Если профиль удовлетворяет жесткому фильтру, для него задан хостлист, и у нас еще нет имени хоста, +идет переход к следующему профилю. В противном случае идет проверка по хостлистам этого профиля. +Если имя хоста удовлетворяет листам, выбирается этот профиль. Иначе идет переход к следующему. +Может так случиться, что до получения имени хоста соединение идет по одному профилю, а при получении +хоста профиль меняется на лету. Поэтому если у вас есть параметры дурения нулевой фазы, тщательно +продумывайте что может произойти при переключении стратегии. Смотрите debug log, чтобы лучше +понять что делает nfqws. + +ВАЖНО : множественные стратегии создавались только для случаев, когда невозможно обьединить +имеющиеся стратегии для разных ресурсов. Копирование стратегий из blockcheck для разных сайтов +во множество профилей без понимания как они работают приведет к нагромождению параметров, которые все равно +не покроют все возможные заблокированные ресурсы. Вы только увязните в этой каше. + tpws ----- @@ -669,7 +701,7 @@ tpws - это transparent proxy. --bind-linklocal=no|unwanted|prefer|force ; no : биндаться только на global ipv6 ; unwanted (default) : предпочтительно global, если нет - LL - ; prefer : предпочтительно LL, если нет - global + ; prefer : предпочительно LL, если нет - global ; force : биндаться только на LL --bind-iface4= ; слушать на первом ipv4 интерфейса iface --bind-iface6= ; слушать на первом ipv6 интерфейса iface @@ -692,7 +724,7 @@ tpws - это transparent proxy. --maxfiles= ; макс количество файловых дескрипторов (setrlimit). мин требование (X*connections+16), где X=6 в tcp proxy mode, X=4 в режиме тамперинга. ; стоит сделать запас с коэффициентом как минимум 1.5. по умолчанию maxfiles (X*connections)*1.5+16 --max-orphan-time=; если вы запускаете через tpws торрент-клиент с множеством раздач, он пытается установить очень много исходящих соединений, - ; большая часть из которых отваливается по таймауту (юзера сидят за NAT, firewall, ...) + ; большая часть из которых отваливается по таймату (юзера сидят за NAT, firewall, ...) ; установление соединения в linux может длиться очень долго. локальный конец отвалился, перед этим послав блок данных, ; tpws ждет подключения удаленного конца, чтобы отослать ему этот блок, и зависает надолго. ; настройка позволяет сбрасывать такие подключения через N секунд, теряя блок данных. по умолчанию 5 сек. 0 означает отключить функцию @@ -734,9 +766,9 @@ tpws - это transparent proxy. ; список читается 1 раз при старте и хранится в памяти в виде иерархической структуры для быстрого поиска. ; по сигналу HUP список будет перечитан при следующем принятом соединении ; список может быть запакован в gzip. формат автоматически распознается и разжимается - ; списков может быть множество, они объединяются. пустой общий лист = его отсутствие + ; списков может быть множество, они обьединяются. пустой общий лист = его отсутствие ; хосты извлекаются из Host: хедера обычных http запросов и из SNI в TLS ClientHello. - --hostlist-exclude= ; не применять дурение к доменам из листа. может быть множество листов, они объединяются + --hostlist-exclude= ; не применять дурение к доменам из листа. может быть множество листов, они обьединяются --hostlist-auto= ; обнаруживать автоматически блокировки и заполнять автоматический hostlist (требует перенаправления входящего трафика) --hostlist-auto-fail-threshold= ; сколько раз нужно обнаружить ситуацию, похожую на блокировку, чтобы добавить хост в лист (по умолчанию: 3) --hostlist-auto-fail-time= ; все эти ситуации должны быть в пределах указанного количества секунд (по умолчанию: 60) @@ -853,7 +885,7 @@ tpws поддерживает эту возможность асинхронно структуры данных. В случае отсутствия SNI разбиение отменяется. --tlsrec-pos режет на указанной позиции. Если длина блока данных TLS меньше указанной позиции, режем на позиции 1. Параметр сочетается с --split-pos. В этом случае происходит сначала разделение на уровне TLS record layer, потом на уровне TCP. -Самая изощрённая атака --tlsrec, --split-pos и --disorder вместе. +Самая изорщенная атака --tlsrec, --split-pos и --disorder вместе. --tlsrec ломает значительное количество сайтов. Криптобиблиотеки (openssl, ...) на оконечных http серверах без проблем принимают разделенные tls сегменты, но мидлбоксы - не всегда. К мидлбоксам можно отнести CDN или системы ddos-защиты. Поэтому применение --tlsrec без ограничителей вряд ли целесообразно. @@ -962,7 +994,7 @@ ipset, это длительная операция на больших лист Список РКН уже достиг внушительных размеров в сотни тысяч IP адресов. Поэтому для оптимизации ipset применяется утилита ip2net. Она берет список отдельных IP адресов и пытается интеллектуально создать из него подсети для сокращения -количества адресов. ip2net отсекает неправильные записи в листах, гарантируя отсутствие ошибок при их загрузке. +количества адресов. ip2net отсекает неправильные записи в листах, гарантируя осутствие ошибок при их загрузке. ip2net написан на языке C, поскольку операция ресурсоемкая. Иные способы роутер может не потянуть. Можно внести список доменов в ipset/zapret-hosts-user-ipban.txt. Их ip адреса будут помещены @@ -1038,7 +1070,7 @@ ip2net фильтрует входные данные, выкидывая неп Альтернативой ipset является использование tpws или nfqws со списком доменов. Оба демона принимают неограниченное количество листов include (--hostlist) и exclude (--hostlist-exclude). -Все листы одного типа объединяются, и таким образом остаются только 2 листа. +Все листы одного типа обьединяются, и таким образом остаются только 2 листа. Прежде всего проверяется exclude list. При вхождении в него происходит отказ от дурения. Далее при наличии include list проверяется домен на вхождение в него. При невхождении в список отказ от дурения. Пустой список приравнивается к его отсутствию. @@ -1196,7 +1228,7 @@ force дает максимум проверок даже в случаях, к из списка. ПРОВЕРКА НА ЧАСТИЧНЫЙ IP block -Под частичным блоком подразумевается ситуация, когда коннект на порты есть, но по определенному транспортному +Под частичным блоком подразумевается ситуация, когда конект на порты есть, но по определенному транспортному или прикладному протоколу всегда идет реакция DPI вне зависимости от запрашиваемого домена. Эта проверка так же не выдаст автоматического вердикта/решения, потому что может быть очень много вариаций. Вместо этого анализ происходящего возложен на самого пользователя или тех, кто будет читать лог. @@ -1208,7 +1240,7 @@ force дает максимум проверок даже в случаях, к Ошибка сертификата может говорить как о реакции DPI с MiTM атакой (подмена сертификата), так и о том, что принимающий сервер неблокированного домена все равно принимает ваш TLS handshake с чужим доменом, пытаясь при этом выдать сертификат без запрошенного домена. Требуется дополнительный анализ. -Если на заблокированный домен есть реакция на всех IP адресах, значит есть блокировка по домену. +Если на заблокированный домен есть реакция на всех IP адресах, значит есть блокировака по домену. Если на неблокированный домен есть реакция на IP адресах блокированного домена, значит имеет место блок по IP. Соответственно, если есть и то, и другое, значит есть и блок по IP, и блок по домену. Неблокированный домен первым делом проверяется на доступность на оригинальном адресе. @@ -1360,14 +1392,14 @@ NFQWS_OPT_DESYNC_QUIC6="--dpi-desync=hopbyhop" Если NFQWS_OPT_DESYNC_QUIC6 не задано, то берется NFQWS_OPT_DESYNC_QUIC. Настройка системы управления выборочным traffic offload (только если поддерживается) -donttouch : выборочное управление отключено, используется системная настройка, простой инсталлятор выключает системную настройку, если она не совместима с выбранным режимом -none : выборочное управление отключено, простой инсталлятор выключает системную настройку -software : выборочное управление включено в режиме software, простой инсталлятор выключает системную настройку -hardware : выборочное управление включено в режиме hardware, простой инсталлятор выключает системную настройку +donttouch : выборочное управление отключено, используется системная настройка, простой инсталятор выключает системную настройку, если она не совместима с выбранным режимом +none : выборочное управление отключено, простой инсталятор выключает системную настройку +software : выборочное управление включено в режиме software, простой инсталятор выключает системную настройку +hardware : выборочное управление включено в режиме hardware, простой инсталятор выключает системную настройку FLOWOFFLOAD=donttouch -Параметр GETLIST указывает инсталлятору install_easy.sh какой скрипт дергать +Параметр GETLIST указывает инсталятору install_easy.sh какой скрипт дергать для обновления списка заблокированных ip или хостов. Он же вызывается через get_config.sh из запланированных заданий (crontab или systemd timer). Поместите сюда название скрипта, который будете использовать для обновления листов. @@ -1542,16 +1574,16 @@ tpws к http и nfqws к https. При этом поддерживаются у install_easy.sh автоматизирует ручные варианты процедур установки (см manual_setup.txt). Он поддерживает OpenWRT, linux системы на базе systemd или openrc и MacOS. -Для более гибкой настройки перед запуском инсталлятора следует выполнить раздел "Выбор параметров". +Для более гибкой настройки перед запуском инсталятора следует выполнить раздел "Выбор параметров". -Если система запуска поддерживается, но используется не поддерживаемый инсталлятором менеджер пакетов -или названия пакетов не соответствуют прописанным в инсталлятор, пакеты нужно установить вручную. +Если система запуска поддерживается, но используется не поддерживаемый инсталятором менеджер пакетов +или названия пакетов не соответствуют прописанным в инсталятор, пакеты нужно установить вручную. Всегда требуется curl. ipset - только для режима iptables, для nftables - не нужен. Для совсем обрезанных дистрибутивов (alpine) требуется отдельно установить iptables и ip6tables, либо nftables. -В комплекте идут статические бинарники для большинства архитектур. Какой-то из них подойдет -с вероятностью 99%. Но если у вас экзотическая система, инсталлятор попробует собрать бинарники сам +В комплекте идут статические бинарики для большинства архитектур. Какой-то из них подойдет +с вероятностью 99%. Но если у вас экзотическая система, инсталятор попробует собрать бинарики сам через make. Для этого нужны gcc, make и необходимые -dev пакеты. Можно форсировать режим компиляции следующим вызовом : @@ -1559,8 +1591,8 @@ install_easy.sh автоматизирует ручные варианты пр Под openwrt все уже сразу готово для использования системы в качестве роутера. Имена интерфейсов WAN и LAN известны из настроек системы. -Под другими системами роутер вы настраиваете самостоятельно. инсталлятор в это не вмешивается. -инсталлятор в зависимости от выбранного режима может спросить LAN и WAN интерфейсы. +Под другими системами роутер вы настраиваете самостоятельно. Инсталятор в это не вмешивается. +Инсталятор в зависимости от выбранного режима может спросить LAN и WAN интерфейсы. Нужно понимать, что заворот проходящего трафика на tpws в прозрачном режиме происходит до выполнения маршрутизации, следовательно возможна фильтрация по LAN и невозможна по WAN. Решение о завороте на tpws локального исходящего трафика принимается после выполнения маршрутизации, @@ -1568,7 +1600,7 @@ install_easy.sh автоматизирует ручные варианты пр Заворот на nfqws происходит всегда после маршрутизации, поэтому к нему применима только фильтрация по WAN. Возможность прохождения трафика в том или ином направлении настраивается вами в процессе конфигурации роутера. -Деинсталляция выполняется через uninstall_easy.sh +Деинсталяция выполняется через uninstall_easy.sh Простая установка на openwrt @@ -1585,7 +1617,7 @@ install_easy.sh автоматизирует ручные варианты пр После успешной установки можно удалить zapret из tmp для освобождения RAM : rm -r /tmp/zapret -Для более гибкой настройки перед запуском инсталлятора следует выполнить раздел "Выбор параметров". +Для более гибкой настройки перед запуском инсталятора следует выполнить раздел "Выбор параметров". Система простой инсталяции заточена на любое умышленное или неумышленное изменение прав доступа на файлы. Устойчива к репаку под windows. После копирования в /opt права будут принудительно восстановлены. @@ -1653,7 +1685,7 @@ echo nameserver 1.1.1.1 >/etc/resolv.conf который как правило присутствует по умолчанию. В ядре android нет nftables. ls -la $(which iptables) Линк должен указывать на legacy вариант. -Если нет, значит устанавливайте нужные пакеты вашего дистрибутива, и убеждайтесь в правильности ссылок. +Если нет, значит устанавливайте нужные пакеты вашего дестрибутива, и убеждайтесь в правильности ссылок. iptables -S Так можно проверить, что ваш iptables увидел то, что туда насовал android. iptables-nft выдаст ошибку. Далее качаем zapret в /opt/zapret. Обычные действия с install_prereq.sh, install_bin.sh, blockcheck.sh. @@ -1676,7 +1708,7 @@ Wifi сеть - обычно wlan0. Устройства типа E3372, E8372, E5770 разделяют общую идеологию построения системы. Имеются 2 вычислительных ядра. Одно ядро выполняет vxworks, другое - linux. -На 4pda имеются модифицированные прошивки с telnet и adb. Их и нужно использовать. +На 4pda имеются модицифированные прошивки с telnet и adb. Их и нужно использовать. Дальнейшие утверждения проверены на E8372. На других может быть аналогично или похоже. Присутствуют дополнительные аппаратные блоки для offload-а сетевых функций. @@ -1691,7 +1723,7 @@ nfqueue поломан. можно собрать фиксящий модуль С помощью этих исходников умельцы могут собрать модуль unfuck_nfqueue.ko. После его применения NFQUEUE и nfqws для arm работают нормально. -Чтобы избежать проблемы с offload-ом при использовании nfqws, следует комбинировать tpws в режиме tcp proxy и nfqws. +Чтобы избежать проблемы с offload-ом при использвании nfqws, следует комбинировать tpws в режиме tcp proxy и nfqws. Правила NFQUEUE пишутся для цепочки OUTPUT. connbytes придется опускать, поскольку модуля в ядре нет. Но это не смертельно. @@ -1722,7 +1754,7 @@ connbytes придется опускать, поскольку модуля в на небольшое количество хостов. Некоторые наброски скриптов присутствуют в files/huawei. Не готовое решение ! Смотрите, изучайте, приспосабливайте. -Здесь можно скачать готовые полезные статические бинарники для arm, включая curl : https://github.com/bol-van/bins +Здесь можно скачать готовые полезные статические бинарики для arm, включая curl : https://github.com/bol-van/bins FreeBSD, OpenBSD, MacOS @@ -1741,7 +1773,7 @@ Windows --------------- Для статических бинариков не имеет значения на чем они запущены : PC, android, приставка, роутер, любой другой девайс. -Подойдет любая прошивка, дистрибутив linux. Статические бинарники запустятся на всем. +Подойдет любая прошивка, дистрибутив linux. Статические бинарики запустятся на всем. Им нужно только ядро с необходимыми опциями сборки или модулями. Но кроме бинариков в проекте используются еще и скрипты, в которых задействуются некоторые стандартные программы. @@ -1827,7 +1859,7 @@ VPN провайдер предоставляет _простую_ и _дост Возможен китайский расклад, при котором DPI выявляет vpn протоколы и динамически банит IP серверов, предоставляющих нелицензированный VPN. Но имея знания, голову, вы всегда можете обфусцировать vpn трафик или применить другие типы VPN, более устойчивые к анализу на DPI или просто менее широкоизвестные, -а следовательно с меньшей вероятностью обнаруживаемые регулятором. +а следовательно с меньшей вероятностью обнаруживамые регулятором. У вас есть свобода делать на вашем VPS все что вы захотите, адаптируясь к новым условиям. Да, это потребует знаний. Вам выбирать учиться и держать ситуацию под контролем, когда вам ничего запретить не могут, или покориться системе. diff --git a/nfq/conntrack.h b/nfq/conntrack.h index 6b05da3..e37a616 100644 --- a/nfq/conntrack.h +++ b/nfq/conntrack.h @@ -55,6 +55,10 @@ typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate; typedef enum {UNKNOWN=0, HTTP, TLS, QUIC, WIREGUARD, DHT} t_l7proto; typedef struct { + struct desync_profile *dp; // desync profile cache + bool dp_search_complete; + bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache + // common state time_t t_start, t_last; uint64_t pcounter_orig, pcounter_reply; // packet counter diff --git a/nfq/desync.c b/nfq/desync.c index 43e3c4c..6dda27d 100644 --- a/nfq/desync.c +++ b/nfq/desync.c @@ -145,11 +145,63 @@ enum dpi_desync_mode desync_mode_from_string(const char *s) return DESYNC_INVALID; } +static bool dp_match_l3l4(struct desync_profile *dp, bool ipv6, uint16_t tcp_port, uint16_t udp_port) +{ + return \ + ((!ipv6 && dp->filter_ipv4) || (ipv6 && dp->filter_ipv6)) && + (!tcp_port || pf_in_range(tcp_port,&dp->pf_tcp)) && + (!udp_port || pf_in_range(udp_port,&dp->pf_udp)); +} +static bool dp_match( + struct desync_profile *dp, bool ipv6, uint16_t tcp_port, uint16_t udp_port, const char *hostname, + bool *bCheckDone, bool *bCheckResult, bool *bExcluded) +{ + if (bCheckDone) *bCheckDone = false; + if (dp_match_l3l4(dp,ipv6,tcp_port,udp_port)) + { + // autohostlist profile matching l3/l4 filter always win + if (*dp->hostlist_auto_filename) return true; + + if (dp->hostlist || dp->hostlist_exclude) + { + // without known hostname first profile matching l3/l4 filter and without hostlist filter wins + if (hostname) + { + if (bCheckDone) *bCheckDone = true; + bool b; + b = HostlistCheck(dp, hostname, bExcluded); + if (bCheckResult) *bCheckResult = b; + return b; + } + } + else + // profile without hostlist filter wins + return true; + } + return false; +} +static struct desync_profile *dp_find( + struct desync_profile_list_head *head, bool ipv6, uint16_t tcp_port, uint16_t udp_port, const char *hostname, + bool *bCheckDone, bool *bCheckResult, bool *bExcluded) +{ + struct desync_profile_list *dpl; + DLOG("desync profile search for hostname='%s' ipv6=%u tcp_port=%u udp_port=%u\n", hostname ? hostname : "", ipv6, tcp_port, udp_port); + LIST_FOREACH(dpl, head, next) + { + if (dp_match(&dpl->dp,ipv6,tcp_port,udp_port,hostname,bCheckDone,bCheckResult,bExcluded)) + { + DLOG("desync profile %d matches\n",dpl->dp.n); + return &dpl->dp; + } + } + DLOG("desync profile not found\n"); + return NULL; +} // auto creates internal socket and uses it for subsequent calls -static bool rawsend_rep(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len) +static bool rawsend_rep(int repeats, const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len) { - for (int i=0;idp) { if (proto==IPPROTO_TCP) - ctrack->b_wssize_cutoff |= cutoff_test(ctrack, params.wssize_cutoff, params.wssize_cutoff_mode); - ctrack->b_desync_cutoff |= cutoff_test(ctrack, params.desync_cutoff, params.desync_cutoff_mode); + ctrack->b_wssize_cutoff |= cutoff_test(ctrack, ctrack->dp->wssize_cutoff, ctrack->dp->wssize_cutoff_mode); + ctrack->b_desync_cutoff |= cutoff_test(ctrack, ctrack->dp->desync_cutoff, ctrack->dp->desync_cutoff_mode); + // in MULTI STRATEGY concept conntrack entry holds desync profile + // we do not want to remove conntrack entries ASAP anymore + + /* // we do not need conntrack entry anymore if all cutoff conditions are either not defined or reached // do not drop udp entry because it will be recreated when next packet arrives if (proto==IPPROTO_TCP) ctrack->b_cutoff |= \ - (!params.wssize || ctrack->b_wssize_cutoff) && - (!params.desync_cutoff || ctrack->b_desync_cutoff) && + (!ctrack->dp->wssize || ctrack->b_wssize_cutoff) && + (!ctrack->dp->desync_cutoff || ctrack->b_desync_cutoff) && (!ctrack->hostname_ah_check || ctrack->req_retrans_counter==RETRANS_COUNTER_STOP) && ReasmIsEmpty(&ctrack->reasm_orig); + */ } } static void wssize_cutoff(t_ctrack *ctrack) @@ -198,7 +255,7 @@ static void wssize_cutoff(t_ctrack *ctrack) } static void forced_wssize_cutoff(t_ctrack *ctrack) { - if (ctrack && params.wssize && !ctrack->b_wssize_cutoff) + if (ctrack && ctrack->dp && ctrack->dp->wssize && !ctrack->b_wssize_cutoff) { DLOG("forced wssize-cutoff\n"); wssize_cutoff(ctrack); @@ -214,16 +271,16 @@ static void ctrack_stop_retrans_counter(t_ctrack *ctrack) } } -static void auto_hostlist_reset_fail_counter(const char *hostname) +static void auto_hostlist_reset_fail_counter(struct desync_profile *dp, const char *hostname) { if (hostname) { hostfail_pool *fail_counter; - fail_counter = HostFailPoolFind(params.hostlist_auto_fail_counters, hostname); + fail_counter = HostFailPoolFind(dp->hostlist_auto_fail_counters, hostname); if (fail_counter) { - HostFailPoolDel(¶ms.hostlist_auto_fail_counters, fail_counter); + HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter); DLOG("auto hostlist : %s : fail counter reset. website is working.\n", hostname); HOSTLIST_DEBUGLOG_APPEND("%s : fail counter reset. website is working.", hostname); } @@ -233,7 +290,7 @@ static void auto_hostlist_reset_fail_counter(const char *hostname) // return true if retrans trigger fires static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int threshold) { - if (ctrack && ctrack->hostname_ah_check && ctrack->req_retrans_counter!=RETRANS_COUNTER_STOP) + if (ctrack && ctrack->dp && ctrack->hostname_ah_check && ctrack->req_retrans_counter!=RETRANS_COUNTER_STOP) { if (l4proto==IPPROTO_TCP) { @@ -243,7 +300,7 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho { DLOG("req retrans : tcp seq %u not within the req range %u-%u. stop tracking.\n", ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end); ctrack_stop_retrans_counter(ctrack); - auto_hostlist_reset_fail_counter(ctrack->hostname); + auto_hostlist_reset_fail_counter(ctrack->dp, ctrack->hostname); return false; } } @@ -258,14 +315,14 @@ static bool auto_hostlist_retrans(t_ctrack *ctrack, uint8_t l4proto, int thresho } return false; } -static void auto_hostlist_failed(const char *hostname) +static void auto_hostlist_failed(struct desync_profile *dp, const char *hostname) { hostfail_pool *fail_counter; - fail_counter = HostFailPoolFind(params.hostlist_auto_fail_counters, hostname); + fail_counter = HostFailPoolFind(dp->hostlist_auto_fail_counters, hostname); if (!fail_counter) { - fail_counter = HostFailPoolAdd(¶ms.hostlist_auto_fail_counters, hostname, params.hostlist_auto_fail_time); + fail_counter = HostFailPoolAdd(&dp->hostlist_auto_fail_counters, hostname, dp->hostlist_auto_fail_time); if (!fail_counter) { fprintf(stderr, "HostFailPoolAdd: out of memory\n"); @@ -273,30 +330,30 @@ static void auto_hostlist_failed(const char *hostname) } } fail_counter->counter++; - DLOG("auto hostlist : %s : fail counter %d/%d\n", hostname, fail_counter->counter, params.hostlist_auto_fail_threshold); - HOSTLIST_DEBUGLOG_APPEND("%s : fail counter %d/%d", hostname, fail_counter->counter, params.hostlist_auto_fail_threshold); - if (fail_counter->counter >= params.hostlist_auto_fail_threshold) + DLOG("auto hostlist : %s : fail counter %d/%d\n", hostname, fail_counter->counter, dp->hostlist_auto_fail_threshold); + HOSTLIST_DEBUGLOG_APPEND("%s : fail counter %d/%d", hostname, fail_counter->counter, dp->hostlist_auto_fail_threshold); + if (fail_counter->counter >= dp->hostlist_auto_fail_threshold) { DLOG("auto hostlist : fail threshold reached. about to add %s to auto hostlist\n", hostname); - HostFailPoolDel(¶ms.hostlist_auto_fail_counters, fail_counter); + HostFailPoolDel(&dp->hostlist_auto_fail_counters, fail_counter); DLOG("auto hostlist : rechecking %s to avoid duplicates\n", hostname); bool bExcluded=false; - if (!HostlistCheck(hostname, &bExcluded) && !bExcluded) + if (!HostlistCheck(dp, hostname, &bExcluded) && !bExcluded) { DLOG("auto hostlist : adding %s\n", hostname); HOSTLIST_DEBUGLOG_APPEND("%s : adding", hostname); - if (!StrPoolAddStr(¶ms.hostlist, hostname)) + if (!StrPoolAddStr(&dp->hostlist, hostname)) { fprintf(stderr, "StrPoolAddStr out of memory\n"); return; } - if (!append_to_list_file(params.hostlist_auto_filename, hostname)) + if (!append_to_list_file(dp->hostlist_auto_filename, hostname)) { DLOG_PERROR("write to auto hostlist:"); return; } - params.hostlist_auto_mod_time = file_mod_time(params.hostlist_auto_filename); + dp->hostlist_auto_mod_time = file_mod_time(dp->hostlist_auto_filename); } else { @@ -308,10 +365,10 @@ static void auto_hostlist_failed(const char *hostname) static void process_retrans_fail(t_ctrack *ctrack, uint8_t proto) { - if (ctrack && ctrack->hostname && auto_hostlist_retrans(ctrack, proto, params.hostlist_auto_retrans_threshold)) + if (ctrack && ctrack->dp && ctrack->hostname && auto_hostlist_retrans(ctrack, proto, ctrack->dp->hostlist_auto_retrans_threshold)) { HOSTLIST_DEBUGLOG_APPEND("%s : tcp retrans threshold reached", ctrack->hostname); - auto_hostlist_failed(ctrack->hostname); + auto_hostlist_failed(ctrack->dp, ctrack->hostname); } } @@ -443,47 +500,50 @@ static uint8_t ct_new_postnat_fix_udp(const t_ctrack *ctrack, struct ip *ip, str } -static bool check_desync_interval(const t_ctrack *ctrack) +static bool check_desync_interval(const struct desync_profile *dp, const t_ctrack *ctrack) { - if (params.desync_start) + if (dp) { - if (ctrack) + if (dp->desync_start) { - if (!cutoff_test(ctrack, params.desync_start, params.desync_start_mode)) + if (ctrack) { - DLOG("desync-start not reached (mode %c): %llu/%u . not desyncing\n", params.desync_start_mode, (unsigned long long)cutoff_get_limit(ctrack,params.desync_start_mode), params.desync_start); + if (!cutoff_test(ctrack, dp->desync_start, dp->desync_start_mode)) + { + DLOG("desync-start not reached (mode %c): %llu/%u . not desyncing\n", dp->desync_start_mode, (unsigned long long)cutoff_get_limit(ctrack,dp->desync_start_mode), dp->desync_start); + return false; + } + DLOG("desync-start reached (mode %c): %llu/%u\n", dp->desync_start_mode, (unsigned long long)cutoff_get_limit(ctrack,dp->desync_start_mode), dp->desync_start); + } + else + { + DLOG("not desyncing. desync-start is set but conntrack entry is missing\n"); return false; } - DLOG("desync-start reached (mode %c): %llu/%u\n", params.desync_start_mode, (unsigned long long)cutoff_get_limit(ctrack,params.desync_start_mode), params.desync_start); } - else + if (dp->desync_cutoff) { - DLOG("not desyncing. desync-start is set but conntrack entry is missing\n"); - return false; - } - } - if (params.desync_cutoff) - { - if (ctrack) - { - if (ctrack->b_desync_cutoff) + if (ctrack) { - DLOG("desync-cutoff reached (mode %c): %llu/%u . not desyncing\n", params.desync_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,params.desync_cutoff_mode), params.desync_cutoff); + if (ctrack->b_desync_cutoff) + { + DLOG("desync-cutoff reached (mode %c): %llu/%u . not desyncing\n", dp->desync_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,dp->desync_cutoff_mode), dp->desync_cutoff); + return false; + } + DLOG("desync-cutoff not reached (mode %c): %llu/%u\n", dp->desync_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,dp->desync_cutoff_mode), dp->desync_cutoff); + } + else + { + DLOG("not desyncing. desync-cutoff is set but conntrack entry is missing\n"); return false; } - DLOG("desync-cutoff not reached (mode %c): %llu/%u\n", params.desync_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,params.desync_cutoff_mode), params.desync_cutoff); - } - else - { - DLOG("not desyncing. desync-cutoff is set but conntrack entry is missing\n"); - return false; } } return true; } -static bool process_desync_interval(t_ctrack *ctrack) +static bool process_desync_interval(const struct desync_profile *dp, t_ctrack *ctrack) { - if (check_desync_interval(ctrack)) + if (check_desync_interval(dp, ctrack)) return true; else { @@ -514,6 +574,7 @@ static size_t pos_normalize(size_t split_pos, size_t reasm_offset, size_t len_pa return split_pos; } + static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint32_t fwmark, const char *ifout, uint8_t *data_pkt, size_t *len_pkt, struct ip *ip, struct ip6_hdr *ip6hdr, struct tcphdr *tcphdr, size_t transport_len, uint8_t *data_payload, size_t len_payload) { uint8_t verdict=VERDICT_PASS; @@ -521,6 +582,8 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint // additional safety check if (!!ip == !!ip6hdr) return verdict; + struct desync_profile *dp = NULL; + t_ctrack *ctrack=NULL, *ctrack_replay=NULL; bool bReverse=false; @@ -529,10 +592,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint size_t pkt1_len, pkt2_len; uint8_t ttl_orig,ttl_fake,flags_orig,scale_factor; uint32_t *timestamps; + t_l7proto l7proto = UNKNOWN; ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim; uint32_t desync_fwmark = fwmark | params.desync_fwmark; - + if (replay) { // in replay mode conntrack_replay is not NULL and ctrack is NULL @@ -540,6 +604,21 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint //ConntrackPoolDump(¶ms.conntrack); if (!ConntrackPoolDoubleSearch(¶ms.conntrack, ip, ip6hdr, tcphdr, NULL, &ctrack_replay, &bReverse) || bReverse) return verdict; + + dp = ctrack_replay->dp; + if (dp) + DLOG("using cached desync profile %d\n",dp->n); + else if (!ctrack_replay->dp_search_complete) + { + dp = ctrack_replay->dp = dp_find(¶ms.desync_profiles, !!ip6hdr, ntohs(bReverse ? tcphdr->th_sport : tcphdr->th_dport), 0, ctrack_replay->hostname, NULL, NULL, NULL); + ctrack_replay->dp_search_complete = true; + } + + if (!dp) + { + DLOG("matching desync profile not found\n"); + return verdict; + } } else { @@ -548,24 +627,42 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint ConntrackPoolPurge(¶ms.conntrack); if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, tcphdr, NULL, len_payload, &ctrack, &bReverse)) { + dp = ctrack->dp; ctrack_replay = ctrack; maybe_cutoff(ctrack, IPPROTO_TCP); } - HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters); + if (dp) + DLOG("using cached desync profile %d\n",dp->n); + else if (!ctrack || !ctrack->dp_search_complete) + { + dp = dp_find(¶ms.desync_profiles, !!ip6hdr, ntohs(bReverse ? tcphdr->th_sport : tcphdr->th_dport), 0, ctrack ? ctrack->hostname : NULL, NULL, NULL, NULL); + if (ctrack) + { + ctrack->dp = dp; + ctrack->dp_search_complete = true; + } + } + if (!dp) + { + DLOG("matching desync profile not found\n"); + return verdict; + } + + HostFailPoolPurgeRateLimited(&dp->hostlist_auto_fail_counters); //ConntrackPoolDump(¶ms.conntrack); - if (params.wsize && tcp_synack_segment(tcphdr)) + if (dp->wsize && tcp_synack_segment(tcphdr)) { - tcp_rewrite_winsize(tcphdr, params.wsize, params.wscale); + tcp_rewrite_winsize(tcphdr, dp->wsize, dp->wscale); verdict=VERDICT_MODIFY; } - + if (bReverse) { if (ctrack && !ctrack->autottl && ctrack->pcounter_reply==1) { - autottl *attl = ip ? ¶ms.desync_autottl : ¶ms.desync_autottl6; + autottl *attl = ip ? &dp->desync_autottl : &dp->desync_autottl6; if (AUTOTTL_ENABLED(*attl)) { ctrack->autottl = autottl_guess(ttl_orig, attl); @@ -609,10 +706,10 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } if (bFail) - auto_hostlist_failed(ctrack->hostname); + auto_hostlist_failed(dp, ctrack->hostname); else if (len_payload) - auto_hostlist_reset_fail_counter(ctrack->hostname); + auto_hostlist_reset_fail_counter(dp, ctrack->hostname); if (tcphdr->th_flags & TH_RST) ConntrackClearHostname(ctrack); // do not react to further dup RSTs } @@ -620,19 +717,18 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint return verdict; // nothing to do. do not waste cpu } - - if (params.wssize) + if (dp->wssize) { if (ctrack) { if (ctrack->b_wssize_cutoff) { - DLOG("not changing wssize. wssize-cutoff reached\n"); + DLOG("wssize-cutoff reached (mode %c): %llu/%u . not changing wssize.\n", dp->wssize_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,dp->wssize_cutoff_mode), dp->wssize_cutoff); } else { - if (params.wssize_cutoff) DLOG("wssize-cutoff not reached (mode %c): %llu/%u\n", params.wssize_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,params.wssize_cutoff_mode), params.wssize_cutoff); - tcp_rewrite_winsize(tcphdr, params.wssize, params.wsscale); + if (dp->wssize_cutoff) DLOG("wssize-cutoff not reached (mode %c): %llu/%u\n", dp->wssize_cutoff_mode, (unsigned long long)cutoff_get_limit(ctrack,dp->wssize_cutoff_mode), dp->wssize_cutoff); + tcp_rewrite_winsize(tcphdr, dp->wssize, dp->wsscale); verdict=VERDICT_MODIFY; } } @@ -643,29 +739,28 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } // !replay - ttl_fake = (ctrack_replay && ctrack_replay->autottl) ? ctrack_replay->autottl : (ip6hdr ? (params.desync_ttl6 ? params.desync_ttl6 : ttl_orig) : (params.desync_ttl ? params.desync_ttl : ttl_orig)); + ttl_fake = (ctrack_replay && ctrack_replay->autottl) ? ctrack_replay->autottl : (ip6hdr ? (dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig) : (dp->desync_ttl ? dp->desync_ttl : ttl_orig)); flags_orig = *((uint8_t*)tcphdr+13); scale_factor = tcp_find_scale_factor(tcphdr); timestamps = tcp_find_timestamps(tcphdr); - extract_endpoints(ip, ip6hdr, tcphdr, NULL, &src, &dst); if (!replay) { if (tcp_syn_segment(tcphdr)) { - switch (params.desync_mode0) + switch (dp->desync_mode0) { case DESYNC_SYNACK: pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_SYN|TH_ACK, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, NULL, 0, pkt1, &pkt1_len)) { return verdict; } DLOG("sending fake SYNACK\n"); - if (!rawsend_rep((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; break; case DESYNC_SYNDATA: @@ -682,13 +777,13 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,0,0,0, params.fake_syndata,params.fake_syndata_size, pkt1,&pkt1_len)) + ttl_orig,0,0,0, dp->fake_syndata,dp->fake_syndata_size, pkt1,&pkt1_len)) { return verdict; } DLOG("sending SYN with fake data : "); - hexdump_limited_dlog(params.fake_syndata,params.fake_syndata_size,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + hexdump_limited_dlog(dp->fake_syndata,dp->fake_syndata_size,PKTDATA_MAXDUMP); DLOG("\n"); + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; verdict = ct_new_postnat_fix_tcp(ctrack, ip, ip6hdr, tcphdr); break; @@ -700,11 +795,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } // start and cutoff limiters - if (!process_desync_interval(ctrack)) return verdict; + if (!process_desync_interval(dp, ctrack)) return verdict; } // !replay - if (!params.wssize && params.desync_mode==DESYNC_NONE && !params.hostcase && !params.hostnospace && !params.domcase && !*params.hostlist_auto_filename) return verdict; // nothing to do. do not waste cpu - if (!(tcphdr->th_flags & TH_SYN) && len_payload) { const uint8_t *fake; @@ -712,7 +805,6 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint char host[256]; bool bHaveHost=false; bool bIsHttp; - bool bKnownProtocol = false; uint8_t *p, *phost; const uint8_t *rdata_payload = data_payload; size_t rlen_payload = len_payload; @@ -734,22 +826,18 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint if ((bIsHttp = IsHttp(rdata_payload,rlen_payload))) { DLOG("packet contains HTTP request\n"); - if (ctrack && !ctrack->l7proto) ctrack->l7proto = HTTP; + l7proto = HTTP; + if (ctrack && !ctrack->l7proto) ctrack->l7proto = l7proto; // we do not reassemble http reasm_orig_cancel(ctrack); - forced_wssize_cutoff(ctrack); - fake = params.fake_http; - fake_size = params.fake_http_size; - if (params.hostlist || params.hostlist_exclude) + + bHaveHost=HttpExtractHost(rdata_payload,rlen_payload,host,sizeof(host)); + if (!bHaveHost) { - bHaveHost=HttpExtractHost(rdata_payload,rlen_payload,host,sizeof(host)); - if (!bHaveHost) - { - DLOG("not applying tampering to HTTP without Host:\n"); - return verdict; - } + DLOG("not applying tampering to HTTP without Host:\n"); + return verdict; } if (ctrack) { @@ -762,19 +850,18 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint DLOG("req retrans : tcp seq interval %u-%u\n",ctrack->req_seq_start,ctrack->req_seq_end); } } - split_pos = HttpPos(params.desync_split_http_req, params.desync_split_pos, rdata_payload, rlen_payload); - bKnownProtocol = true; } else if (IsTLSClientHello(rdata_payload,rlen_payload,TLS_PARTIALS_ENABLE)) { bool bReqFull = IsTLSRecordFull(rdata_payload,rlen_payload); DLOG(bReqFull ? "packet contains full TLS ClientHello\n" : "packet contains partial TLS ClientHello\n"); + l7proto = TLS; bHaveHost=TLSHelloExtractHost(rdata_payload,rlen_payload,host,sizeof(host),TLS_PARTIALS_ENABLE); if (ctrack) { - if (!ctrack->l7proto) ctrack->l7proto = TLS; + if (!ctrack->l7proto) ctrack->l7proto = l7proto; // do not reasm retransmissions if (!bReqFull && ReasmIsEmpty(&ctrack->reasm_orig) && !ctrack->req_seq_abandoned && !(ctrack->req_seq_finalized && seq_within(ctrack->seq_last, ctrack->req_seq_start, ctrack->req_seq_end))) @@ -825,20 +912,13 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } - if (params.desync_skip_nosni && !bHaveHost) + if (dp->desync_skip_nosni && !bHaveHost) { DLOG("not applying tampering to TLS ClientHello without hostname in the SNI\n"); reasm_orig_cancel(ctrack); return verdict; } - - fake = params.fake_tls; - fake_size = params.fake_tls_size; - split_pos = TLSPos(params.desync_split_tls, params.desync_split_pos, rdata_payload, rlen_payload, 0); - bKnownProtocol = true; } - else - split_pos=params.desync_split_pos; reasm_orig_cancel(ctrack); rdata_payload=NULL; @@ -852,22 +932,50 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint if (bHaveHost) { + bool bCheckDone=false, bCheckResult=false, bCheckExcluded=false; DLOG("hostname: %s\n",host); - if (params.hostlist || params.hostlist_exclude) + if (ctrack_replay) { - bool bBypass; - if (HostlistCheck(host, &bBypass)) + if (!ctrack_replay->hostname) + { + ctrack_replay->hostname=strdup(host); + if (!ctrack_replay->hostname) + { + DLOG_ERR("hostname dup : out of memory"); + return verdict; + } + DLOG("we have hostname now. searching desync profile again.\n"); + struct desync_profile *dp_prev = dp; + dp = ctrack_replay->dp = dp_find(¶ms.desync_profiles, !!ip6hdr, ntohs(bReverse ? tcphdr->th_sport : tcphdr->th_dport), 0, ctrack_replay->hostname, &ctrack_replay->bCheckDone, &ctrack_replay->bCheckResult, &ctrack_replay->bCheckExcluded); + ctrack_replay->dp_search_complete = true; + if (!dp) return verdict; + if (dp!=dp_prev) + { + DLOG("desync profile changed by revealed hostname !\n"); + // re-evaluate start/cutoff limiters + if (!replay) + { + maybe_cutoff(ctrack, IPPROTO_TCP); + if (!process_desync_interval(dp, ctrack)) return verdict; + } + } + } + bCheckDone = ctrack_replay->bCheckDone; + bCheckResult = ctrack_replay->bCheckResult; + bCheckExcluded = ctrack_replay->bCheckExcluded; + } + if (dp->hostlist || dp->hostlist_exclude) + { + if (!bCheckDone) + bCheckResult = HostlistCheck(dp, host, &bCheckExcluded); + if (bCheckResult) ctrack_stop_retrans_counter(ctrack_replay); else { if (ctrack_replay) { - ctrack_replay->hostname_ah_check = *params.hostlist_auto_filename && !bBypass; - if (ctrack_replay->hostname_ah_check) - { - if (!ctrack_replay->hostname) ctrack_replay->hostname=strdup(host); - } - else + ctrack_replay->hostname_ah_check = *dp->hostlist_auto_filename && !bCheckExcluded; + if (!ctrack_replay->hostname_ah_check) ctrack_stop_retrans_counter(ctrack_replay); } DLOG("not applying tampering to this request\n"); @@ -875,24 +983,44 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } } - - if (!bKnownProtocol) + + if (l7proto==UNKNOWN) { - if (!params.desync_any_proto) return verdict; + if (!dp->desync_any_proto) return verdict; DLOG("applying tampering to unknown protocol\n"); - fake = params.fake_unknown; - fake_size = params.fake_unknown_size; } - if (bIsHttp && (params.hostcase || params.hostnospace || params.domcase) && (phost = (uint8_t*)memmem(data_payload, len_payload, "\r\nHost: ", 8))) + // desync profile may have changed after hostname was revealed + switch(l7proto) { - if (params.hostcase) + case HTTP: + fake = dp->fake_http; + fake_size = dp->fake_http_size; + split_pos = HttpPos(dp->desync_split_http_req, dp->desync_split_pos, rdata_payload, rlen_payload); + break; + case TLS: + fake = dp->fake_tls; + fake_size = dp->fake_tls_size; + split_pos = TLSPos(dp->desync_split_tls, dp->desync_split_pos, rdata_payload, rlen_payload, 0); + break; + default: + fake = dp->fake_unknown; + fake_size = dp->fake_unknown_size; + split_pos=dp->desync_split_pos; + break; + } + ttl_fake = (ctrack_replay && ctrack_replay->autottl) ? ctrack_replay->autottl : (ip6hdr ? (dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig) : (dp->desync_ttl ? dp->desync_ttl : ttl_orig)); + + + if (bIsHttp && (dp->hostcase || dp->hostnospace || dp->domcase) && (phost = (uint8_t*)memmem(data_payload, len_payload, "\r\nHost: ", 8))) + { + if (dp->hostcase) { - DLOG("modifying Host: => %c%c%c%c:\n", params.hostspell[0], params.hostspell[1], params.hostspell[2], params.hostspell[3]); - memcpy(phost + 2, params.hostspell, 4); + DLOG("modifying Host: => %c%c%c%c:\n", dp->hostspell[0], dp->hostspell[1], dp->hostspell[2], dp->hostspell[3]); + memcpy(phost + 2, dp->hostspell, 4); verdict=VERDICT_MODIFY; } - if (params.domcase) + if (dp->domcase) { DLOG("mixing domain case\n"); for (p = phost+7; p < (data_payload + len_payload) && *p != '\r' && *p != '\n'; p++) @@ -900,7 +1028,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint verdict=VERDICT_MODIFY; } uint8_t *pua; - if (params.hostnospace && + if (dp->hostnospace && (pua = (uint8_t*)memmem(data_payload, len_payload, "\r\nUser-Agent: ", 14)) && (pua = (uint8_t*)memmem(pua + 1, len_payload - (pua - data_payload) - 1, "\r\n", 2))) { @@ -919,7 +1047,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint } } - if (params.desync_mode==DESYNC_NONE) return verdict; + if (dp->desync_mode==DESYNC_NONE) return verdict; if (params.debug) { @@ -932,10 +1060,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint if (!split_pos || split_pos>rlen_payload) split_pos=1; split_pos=pos_normalize(split_pos,reasm_offset,len_payload); - enum dpi_desync_mode desync_mode = params.desync_mode; + enum dpi_desync_mode desync_mode = dp->desync_mode; uint32_t fooling_orig = FOOL_NONE; bool b; - pkt1_len = sizeof(pkt1); b = false; switch(desync_mode) @@ -943,19 +1070,19 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint case DESYNC_FAKE_KNOWN: if (reasm_offset) { - desync_mode = params.desync_mode2; + desync_mode = dp->desync_mode2; break; } - if (!bKnownProtocol) + if (l7proto==UNKNOWN) { DLOG("not applying fake because of unknown protocol\n"); - desync_mode = params.desync_mode2; + desync_mode = dp->desync_mode2; break; } case DESYNC_FAKE: if (reasm_offset) break; if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, fake, fake_size, pkt1, &pkt1_len)) { return verdict; @@ -968,7 +1095,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint case DESYNC_RSTACK: if (reasm_offset) break; if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, TH_RST | (desync_mode==DESYNC_RSTACK ? TH_ACK:0), tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, NULL, 0, pkt1, &pkt1_len)) { return verdict; @@ -980,7 +1107,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint case DESYNC_DESTOPT: case DESYNC_IPFRAG1: fooling_orig = (desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; - desync_mode = params.desync_mode2; + desync_mode = dp->desync_mode2; if (ip6hdr && (desync_mode==DESYNC_NONE || !desync_valid_second_stage_tcp(desync_mode) || (!split_pos && (desync_mode==DESYNC_SPLIT || desync_mode==DESYNC_SPLIT2 || desync_mode==DESYNC_DISORDER || desync_mode==DESYNC_DISORDER2)))) { @@ -1003,9 +1130,9 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint if (b) { - if (!rawsend_rep((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; - if (params.desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(params.desync_mode2)) + if (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_tcp(dp->desync_mode2)) { DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", *len_pkt, len_payload); verdict_tcp_csum_fix(verdict, tcphdr, transport_len, ip, ip6hdr); @@ -1013,7 +1140,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint return verdict; return VERDICT_DROP; } - desync_mode = params.desync_mode2; + desync_mode = dp->desync_mode2; } pkt1_len = sizeof(pkt1); @@ -1026,7 +1153,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint uint8_t fakeseg[DPI_DESYNC_MAX_FAKE_LEN+100], *seg; size_t seg_len; - if (params.desync_seqovl>=split_pos) + if (dp->desync_seqovl>=split_pos) { DLOG("seqovl>=split_pos. desync is not possible.\n"); return verdict; @@ -1034,16 +1161,16 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint if (split_posdesync_seqovl) { - seg_len = len_payload-split_pos+params.desync_seqovl; + seg_len = len_payload-split_pos+dp->desync_seqovl; if (seg_len>sizeof(fakeseg)) { DLOG("seqovl is too large\n"); return verdict; } - fill_pattern(fakeseg,params.desync_seqovl,params.seqovl_pattern,sizeof(params.seqovl_pattern)); - memcpy(fakeseg+params.desync_seqovl,data_payload+split_pos,len_payload-split_pos); + fill_pattern(fakeseg,dp->desync_seqovl,dp->seqovl_pattern,sizeof(dp->seqovl_pattern)); + memcpy(fakeseg+dp->desync_seqovl,data_payload+split_pos,len_payload-split_pos); seg = fakeseg; } else @@ -1052,11 +1179,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint seg_len = len_payload-split_pos; } - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(net32_add(tcphdr->th_seq,split_pos),-params.desync_seqovl), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,fooling_orig,params.desync_badseq_increment,params.desync_badseq_ack_increment, + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(net32_add(tcphdr->th_seq,split_pos),-dp->desync_seqovl), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, + ttl_orig,fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, seg, seg_len, pkt1, &pkt1_len)) return verdict; - DLOG("sending 2nd out-of-order tcp segment %zu-%zu len=%zu seqovl=%u : ",split_pos,len_payload-1, len_payload-split_pos, params.desync_seqovl); + DLOG("sending 2nd out-of-order tcp segment %zu-%zu len=%zu seqovl=%u : ",split_pos,len_payload-1, len_payload-split_pos, dp->desync_seqovl); hexdump_limited_dlog(seg,seg_len,PKTDATA_MAXDUMP); DLOG("\n"); if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; @@ -1067,18 +1194,18 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint { seg_len = sizeof(fakeseg); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, zeropkt, split_pos, fakeseg, &seg_len)) return verdict; DLOG("sending fake(1) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep((struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, seg_len)) + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, seg_len)) return verdict; } pkt1_len = sizeof(pkt1); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,fooling_orig,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, data_payload, split_pos, pkt1, &pkt1_len)) return verdict; DLOG("sending 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); @@ -1090,7 +1217,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint { DLOG("sending fake(2) 1st out-of-order tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep((struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, seg_len)) + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, seg_len)) return verdict; } @@ -1108,25 +1235,25 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint { fakeseg_len = sizeof(fakeseg); if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, tcphdr->th_seq, tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_fake,params.desync_fooling_mode,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_fake,dp->desync_fooling_mode,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, zeropkt, split_pos, fakeseg, &fakeseg_len)) return verdict; DLOG("sending fake(1) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep((struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, fakeseg_len)) + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, fakeseg_len)) return verdict; } - if (params.desync_seqovl) + if (dp->desync_seqovl) { - seg_len = split_pos+params.desync_seqovl; + seg_len = split_pos+dp->desync_seqovl; if (seg_len>sizeof(ovlseg)) { DLOG("seqovl is too large"); return verdict; } - fill_pattern(ovlseg,params.desync_seqovl,params.seqovl_pattern,sizeof(params.seqovl_pattern)); - memcpy(ovlseg+params.desync_seqovl,data_payload,split_pos); + fill_pattern(ovlseg,dp->desync_seqovl,dp->seqovl_pattern,sizeof(dp->seqovl_pattern)); + memcpy(ovlseg+dp->desync_seqovl,data_payload,split_pos); seg = ovlseg; } else @@ -1135,11 +1262,11 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint seg_len = split_pos; } - if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(tcphdr->th_seq,-params.desync_seqovl), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,fooling_orig,params.desync_badseq_increment,params.desync_badseq_ack_increment, + if (!prepare_tcp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, flags_orig, net32_add(tcphdr->th_seq,-dp->desync_seqovl), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, + ttl_orig,fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, seg, seg_len, pkt1, &pkt1_len)) return verdict; - DLOG("sending 1st tcp segment 0-%zu len=%zu seqovl=%u : ",split_pos-1, split_pos, params.desync_seqovl); + DLOG("sending 1st tcp segment 0-%zu len=%zu seqovl=%u : ",split_pos-1, split_pos, dp->desync_seqovl); hexdump_limited_dlog(seg,seg_len,PKTDATA_MAXDUMP); DLOG("\n"); if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; @@ -1148,14 +1275,14 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint { DLOG("sending fake(2) 1st tcp segment 0-%zu len=%zu : ",split_pos-1, split_pos); hexdump_limited_dlog(zeropkt,split_pos,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep((struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, fakeseg_len)) + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , fakeseg, fakeseg_len)) return verdict; } if (split_posth_seq,split_pos), tcphdr->th_ack, tcphdr->th_win, scale_factor, timestamps, - ttl_orig,fooling_orig,params.desync_badseq_increment,params.desync_badseq_ack_increment, + ttl_orig,fooling_orig,dp->desync_badseq_increment,dp->desync_badseq_ack_increment, data_payload+split_pos, len_payload-split_pos, pkt1, &pkt1_len)) return verdict; DLOG("sending 2nd tcp segment %zu-%zu len=%zu : ",split_pos,len_payload-1, len_payload-split_pos); @@ -1175,7 +1302,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint uint8_t pkt3[DPI_DESYNC_MAX_FAKE_LEN+100], *pkt_orig; size_t pkt_orig_len; - size_t ipfrag_pos = (params.desync_ipfrag_pos_tcp && params.desync_ipfrag_pos_tcpdesync_ipfrag_pos_tcp && dp->desync_ipfrag_pos_tcpdesync_ipfrag_pos_tcp : 24; uint32_t ident = ip ? ip->ip_id ? ip->ip_id : htons(1+random()%0xFFFF) : htonl(1+random()%0xFFFFFFFF); pkt1_len = sizeof(pkt1); @@ -1222,7 +1349,7 @@ static uint8_t dpi_desync_tcp_packet_play(bool replay, size_t reasm_offset, uint static bool quic_reasm_cancel(t_ctrack *ctrack, const char *reason) { reasm_orig_cancel(ctrack); - if (params.desync_any_proto) + if (ctrack && ctrack->dp && ctrack->dp->desync_any_proto) { DLOG("%s. applying tampering because desync_any_proto is set\n",reason); return true; @@ -1244,6 +1371,8 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint // no need to desync middle packets in reasm session if (reasm_offset) return verdict; + struct desync_profile *dp = NULL; + t_ctrack *ctrack=NULL, *ctrack_replay=NULL; bool bReverse=false; @@ -1251,7 +1380,8 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint uint8_t pkt1[DPI_DESYNC_MAX_FAKE_LEN+100], pkt2[DPI_DESYNC_MAX_FAKE_LEN+100]; size_t pkt1_len, pkt2_len; uint8_t ttl_orig,ttl_fake; - + t_l7proto l7proto = UNKNOWN; + if (replay) { // in replay mode conntrack_replay is not NULL and ctrack is NULL @@ -1259,6 +1389,20 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint //ConntrackPoolDump(¶ms.conntrack); if (!ConntrackPoolDoubleSearch(¶ms.conntrack, ip, ip6hdr, NULL, udphdr, &ctrack_replay, &bReverse) || bReverse) return verdict; + + dp = ctrack_replay->dp; + if (dp) + DLOG("using cached desync profile %d\n",dp->n); + else if (!ctrack_replay->dp_search_complete) + { + dp = ctrack_replay->dp = dp_find(¶ms.desync_profiles, !!ip6hdr, 0, ntohs(bReverse ? udphdr->uh_sport : udphdr->uh_dport), ctrack_replay->hostname, NULL, NULL, NULL); + ctrack_replay->dp_search_complete = true; + } + if (!dp) + { + DLOG("matching desync profile not found\n"); + return verdict; + } } else { @@ -1267,24 +1411,39 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint ConntrackPoolPurge(¶ms.conntrack); if (ConntrackPoolFeed(¶ms.conntrack, ip, ip6hdr, NULL, udphdr, len_payload, &ctrack, &bReverse)) { + dp = ctrack->dp; ctrack_replay = ctrack; maybe_cutoff(ctrack, IPPROTO_UDP); } - HostFailPoolPurgeRateLimited(¶ms.hostlist_auto_fail_counters); + if (dp) + DLOG("using cached desync profile %d\n",dp->n); + else if (!ctrack || !ctrack->dp_search_complete) + { + dp = dp_find(¶ms.desync_profiles, !!ip6hdr, 0, ntohs(bReverse ? udphdr->uh_sport : udphdr->uh_dport), ctrack ? ctrack->hostname : NULL, NULL, NULL, NULL); + if (ctrack) + { + ctrack->dp = dp; + ctrack->dp_search_complete = true; + } + } + if (!dp) + { + DLOG("matching desync profile not found\n"); + return verdict; + } + + HostFailPoolPurgeRateLimited(&dp->hostlist_auto_fail_counters); //ConntrackPoolDump(¶ms.conntrack); } if (bReverse) return verdict; // nothing to do. do not waste cpu - if (params.desync_mode==DESYNC_NONE && !*params.hostlist_auto_filename) return verdict; // do not waste cpu - // start and cutoff limiters - if (!replay && !process_desync_interval(ctrack)) return verdict; + if (!replay && !process_desync_interval(dp, ctrack)) return verdict; uint32_t desync_fwmark = fwmark | params.desync_fwmark; ttl_orig = ip ? ip->ip_ttl : ip6hdr->ip6_ctlun.ip6_un1.ip6_un1_hlim; - if (ip6hdr) ttl_fake = params.desync_ttl6 ? params.desync_ttl6 : ttl_orig; - else ttl_fake = params.desync_ttl ? params.desync_ttl : ttl_orig; + ttl_fake = ip6hdr ? dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig : dp->desync_ttl ? dp->desync_ttl : ttl_orig; extract_endpoints(ip, ip6hdr, NULL, udphdr, &src, &dst); if (len_payload) @@ -1294,12 +1453,12 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint bool b; char host[256]; bool bHaveHost=false; - bool bKnownProtocol=false; if (IsQUICInitial(data_payload,len_payload)) { DLOG("packet contains QUIC initial\n"); - if (ctrack && !ctrack->l7proto) ctrack->l7proto = QUIC; + l7proto = QUIC; + if (ctrack && !ctrack->l7proto) ctrack->l7proto = l7proto; uint8_t clean[16384], *pclean; size_t clean_len; @@ -1377,7 +1536,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint if (bIsHello) { bHaveHost = TLSHelloExtractHostFromHandshake(defrag + hello_offset, hello_len, host, sizeof(host), TLS_PARTIALS_ENABLE); - if (!bHaveHost && params.desync_skip_nosni) + if (!bHaveHost && dp->desync_skip_nosni) { reasm_orig_cancel(ctrack); DLOG("not applying tampering to QUIC ClientHello without hostname in the SNI\n"); @@ -1400,10 +1559,6 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint // decrypt failed if (!quic_reasm_cancel(ctrack,"QUIC initial decryption failed")) return verdict; } - - fake = params.fake_quic; - fake_size = params.fake_quic_size; - bKnownProtocol = true; } else // not QUIC initial { @@ -1415,39 +1570,66 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint if (IsWireguardHandshakeInitiation(data_payload,len_payload)) { DLOG("packet contains wireguard handshake initiation\n"); - if (ctrack && !ctrack->l7proto) ctrack->l7proto = WIREGUARD; - fake = params.fake_wg; - fake_size = params.fake_wg_size; - bKnownProtocol = true; + l7proto = WIREGUARD; + if (ctrack && !ctrack->l7proto) ctrack->l7proto = l7proto; } else if (IsDhtD1(data_payload,len_payload)) { DLOG("packet contains DHT d1...e\n"); - if (ctrack && !ctrack->l7proto) ctrack->l7proto = DHT; - fake = params.fake_dht; - fake_size = params.fake_dht_size; - bKnownProtocol = true; + l7proto = DHT; + if (ctrack && !ctrack->l7proto) ctrack->l7proto = l7proto; } else { - if (!params.desync_any_proto) return verdict; + if (!dp->desync_any_proto) return verdict; DLOG("applying tampering to unknown protocol\n"); - fake = params.fake_unknown_udp; - fake_size = params.fake_unknown_udp_size; } } if (bHaveHost) { + bool bCheckDone=false, bCheckResult=false, bCheckExcluded=false; DLOG("hostname: %s\n",host); - if (params.hostlist || params.hostlist_exclude) + if (ctrack_replay) { - bool bBypass; - if (!HostlistCheck(host, &bBypass)) + if (!ctrack_replay->hostname) + { + ctrack_replay->hostname=strdup(host); + if (!ctrack_replay->hostname) + { + DLOG_ERR("hostname dup : out of memory"); + return verdict; + } + DLOG("we have hostname now. searching desync profile again.\n"); + struct desync_profile *dp_prev = dp; + dp = ctrack_replay->dp = dp_find(¶ms.desync_profiles, !!ip6hdr, 0, ntohs(bReverse ? udphdr->uh_sport : udphdr->uh_dport), ctrack_replay->hostname, &ctrack_replay->bCheckDone, &ctrack_replay->bCheckResult, &ctrack_replay->bCheckExcluded); + ctrack_replay->dp_search_complete = true; + if (!dp) return verdict; + if (dp!=dp_prev) + { + DLOG("desync profile changed by reavealed hostname !\n"); + // re-evaluate start/cutoff limiters + if (!replay) + { + maybe_cutoff(ctrack, IPPROTO_UDP); + if (!process_desync_interval(dp, ctrack)) return verdict; + } + } + } + bCheckDone = ctrack_replay->bCheckDone; + bCheckResult = ctrack_replay->bCheckResult; + bCheckExcluded = ctrack_replay->bCheckExcluded; + } + if (dp->hostlist || dp->hostlist_exclude) + { + bool bCheckExcluded; + if (!bCheckDone) + bCheckResult = HostlistCheck(dp, host, &bCheckExcluded); + if (!bCheckResult) { if (ctrack_replay) { - ctrack_replay->hostname_ah_check = *params.hostlist_auto_filename && !bBypass; + ctrack_replay->hostname_ah_check = *dp->hostlist_auto_filename && !bCheckExcluded; if (ctrack_replay->hostname_ah_check) { // first request is not retrans @@ -1463,7 +1645,29 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint } } - enum dpi_desync_mode desync_mode = params.desync_mode; + // desync profile may have changed after hostname was revealed + switch(l7proto) + { + case QUIC: + fake = dp->fake_quic; + fake_size = dp->fake_quic_size; + break; + case WIREGUARD: + fake = dp->fake_wg; + fake_size = dp->fake_wg_size; + break; + case DHT: + fake = dp->fake_dht; + fake_size = dp->fake_dht_size; + break; + default: + fake = dp->fake_unknown_udp; + fake_size = dp->fake_unknown_udp_size; + break; + } + ttl_fake = ip6hdr ? dp->desync_ttl6 ? dp->desync_ttl6 : ttl_orig : dp->desync_ttl ? dp->desync_ttl : ttl_orig; + + enum dpi_desync_mode desync_mode = dp->desync_mode; uint32_t fooling_orig = FOOL_NONE; if (params.debug) @@ -1479,18 +1683,18 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint switch(desync_mode) { case DESYNC_FAKE_KNOWN: - if (!bKnownProtocol) + if (l7proto==UNKNOWN) { DLOG("not applying fake because of unknown protocol\n"); - desync_mode = params.desync_mode2; + desync_mode = dp->desync_mode2; break; } case DESYNC_FAKE: - if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, params.desync_fooling_mode, NULL, 0, 0, fake, fake_size, pkt1, &pkt1_len)) + if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_fake, dp->desync_fooling_mode, NULL, 0, 0, fake, fake_size, pkt1, &pkt1_len)) return verdict; DLOG("sending fake request : "); hexdump_limited_dlog(fake,fake_size,PKTDATA_MAXDUMP); DLOG("\n"); - if (!rawsend_rep((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) + if (!rawsend_rep(dp->desync_repeats,(struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; b = true; break; @@ -1498,7 +1702,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint case DESYNC_DESTOPT: case DESYNC_IPFRAG1: fooling_orig = (desync_mode==DESYNC_HOPBYHOP) ? FOOL_HOPBYHOP : (desync_mode==DESYNC_DESTOPT) ? FOOL_DESTOPT : FOOL_IPFRAG1; - if (ip6hdr && (params.desync_mode2==DESYNC_NONE || !desync_valid_second_stage_udp(params.desync_mode2))) + if (ip6hdr && (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_udp(dp->desync_mode2))) { if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_orig,fooling_orig,NULL,0,0, @@ -1512,7 +1716,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint // this mode is final, no other options available return ct_new_postnat_fix_udp(ctrack, ip, ip6hdr, udphdr, len_pkt); } - desync_mode = params.desync_mode2; + desync_mode = dp->desync_mode2; break; default: pkt1_len=0; @@ -1521,7 +1725,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint if (b) { - if (params.desync_mode2==DESYNC_NONE || !desync_valid_second_stage_udp(params.desync_mode2)) + if (dp->desync_mode2==DESYNC_NONE || !desync_valid_second_stage_udp(dp->desync_mode2)) { DLOG("reinjecting original packet. len=%zu len_payload=%zu\n", *len_pkt, len_payload); verdict_udp_csum_fix(verdict, udphdr, transport_len, ip, ip6hdr); @@ -1529,19 +1733,19 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint return verdict; return ct_new_postnat_fix_udp(ctrack, ip, ip6hdr, udphdr, len_pkt); } - desync_mode = params.desync_mode2; + desync_mode = dp->desync_mode2; } switch(desync_mode) { case DESYNC_UDPLEN: pkt1_len = sizeof(pkt1); - if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_orig,fooling_orig, params.udplen_pattern, sizeof(params.udplen_pattern), params.udplen_increment, data_payload, len_payload, pkt1, &pkt1_len)) + if (!prepare_udp_segment((struct sockaddr *)&src, (struct sockaddr *)&dst, ttl_orig,fooling_orig, dp->udplen_pattern, sizeof(dp->udplen_pattern), dp->udplen_increment, data_payload, len_payload, pkt1, &pkt1_len)) { DLOG("could not construct packet with modified length. too large ?\n"); return verdict; } - DLOG("resending original packet with increased by %d length\n", params.udplen_increment); + DLOG("resending original packet with increased by %d length\n", dp->udplen_increment); if (!rawsend((struct sockaddr *)&dst, desync_fwmark, ifout , pkt1, pkt1_len)) return verdict; return ct_new_postnat_fix_udp(ctrack, ip, ip6hdr, udphdr, len_pkt); @@ -1583,7 +1787,7 @@ static uint8_t dpi_desync_udp_packet_play(bool replay, size_t reasm_offset, uint uint8_t pkt3[DPI_DESYNC_MAX_FAKE_LEN+100], *pkt_orig; size_t pkt_orig_len; - size_t ipfrag_pos = (params.desync_ipfrag_pos_udp && params.desync_ipfrag_pos_udpdesync_ipfrag_pos_udp && dp->desync_ipfrag_pos_udpdesync_ipfrag_pos_udp : sizeof(struct udphdr); // freebsd do not set ip.id uint32_t ident = ip ? ip->ip_id ? ip->ip_id : htons(1+random()%0xFFFF) : htonl(1+random()%0xFFFFFFFF); diff --git a/nfq/helpers.c b/nfq/helpers.c index 0ff9b4e..a1e3fa0 100644 --- a/nfq/helpers.c +++ b/nfq/helpers.c @@ -1,12 +1,13 @@ #define _GNU_SOURCE #include "helpers.h" + #include #include #include #include #include - +#include #include "params.h" @@ -320,7 +321,7 @@ bool pf_parse(const char *s, port_filter *pf) unsigned int v1,v2; if (!s) return false; - if (*s=='~') + if (*s=='~') { pf->neg=true; s++; @@ -329,19 +330,25 @@ bool pf_parse(const char *s, port_filter *pf) pf->neg=false; if (sscanf(s,"%u-%u",&v1,&v2)==2) { - if (!v1 || v1>65535 || v2>65535 || v1>v2) return false; + if (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; + if (v1>65535) return false; pf->to=pf->from=(uint16_t)v1; } else return false; + // deny all case + if (!pf->from && !pf->to) pf->neg=true; return true; } +bool pf_is_empty(const port_filter *pf) +{ + return !pf->neg && !pf->from && !pf->to; +} void fill_random_bytes(uint8_t *p,size_t sz) { @@ -364,3 +371,16 @@ void fill_random_az09(uint8_t *p,size_t sz) p[k] = rnd<10 ? rnd+'0' : 'a'+rnd-10; } } + +bool cd_to_exe_dir(const char *argv0) +{ + char *s,*d; + bool bOK=false; + if ((s = strdup(argv0))) + { + if ((d = dirname(s))) + bOK = !chdir(d); + free(s); + } + return bOK; +} diff --git a/nfq/helpers.h b/nfq/helpers.h index 2ac6584..15222d4 100644 --- a/nfq/helpers.h +++ b/nfq/helpers.h @@ -57,7 +57,10 @@ typedef struct } port_filter; 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 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); \ No newline at end of file +void fill_random_az09(uint8_t *p,size_t sz); + +bool cd_to_exe_dir(const char *argv0); diff --git a/nfq/hostlist.c b/nfq/hostlist.c index d1a03b2..03ed441 100644 --- a/nfq/hostlist.c +++ b/nfq/hostlist.c @@ -1,7 +1,6 @@ #include #include "hostlist.h" #include "gzip.h" -#include "params.h" #include "helpers.h" // inplace tolower() and add to pool @@ -154,36 +153,53 @@ static bool HostlistCheck_(strpool *hostlist, strpool *hostlist_exclude, const c return true; } -// return : true = apply fooling, false = do not apply -bool HostlistCheck(const char *host, bool *excluded) +static bool LoadIncludeHostListsForProfile(struct desync_profile *dp) { - if (*params.hostlist_auto_filename) + if (!LoadHostLists(&dp->hostlist, &dp->hostlist_files)) + return false; + if (*dp->hostlist_auto_filename) { - time_t t = file_mod_time(params.hostlist_auto_filename); - if (t!=params.hostlist_auto_mod_time) + dp->hostlist_auto_mod_time = file_mod_time(dp->hostlist_auto_filename); + NonEmptyHostlist(&dp->hostlist); + } + return true; +} + +// return : true = apply fooling, false = do not apply +bool HostlistCheck(struct desync_profile *dp, const char *host, bool *excluded) +{ + DLOG("* Hostlist check for profile %d\n",dp->n); + if (*dp->hostlist_auto_filename) + { + time_t t = file_mod_time(dp->hostlist_auto_filename); + if (t!=dp->hostlist_auto_mod_time) { - DLOG_CONDUP("Autohostlist was modified by another process. Reloading include hostslist.\n"); - if (!LoadIncludeHostLists()) + DLOG_CONDUP("Autohostlist '%s' from profile %d was modified. Reloading include hostlists for this profile.\n",dp->hostlist_auto_filename, dp->n); + if (!LoadIncludeHostListsForProfile(dp)) { // what will we do without hostlist ?? sure, gonna die exit(1); } - params.hostlist_auto_mod_time = t; - NonEmptyHostlist(¶ms.hostlist); + dp->hostlist_auto_mod_time = t; + NonEmptyHostlist(&dp->hostlist); } } - return HostlistCheck_(params.hostlist, params.hostlist_exclude, host, excluded); + return HostlistCheck_(dp->hostlist, dp->hostlist_exclude, host, excluded); } bool LoadIncludeHostLists() { - if (!LoadHostLists(¶ms.hostlist, ¶ms.hostlist_files)) - return false; - if (*params.hostlist_auto_filename) - params.hostlist_auto_mod_time = file_mod_time(params.hostlist_auto_filename); + struct desync_profile_list *dpl; + LIST_FOREACH(dpl, ¶ms.desync_profiles, next) + if (!LoadIncludeHostListsForProfile(&dpl->dp)) + return false; return true; } bool LoadExcludeHostLists() { - return LoadHostLists(¶ms.hostlist_exclude, ¶ms.hostlist_exclude_files); + struct desync_profile_list *dpl; + LIST_FOREACH(dpl, ¶ms.desync_profiles, next) + if (!LoadHostLists(&dpl->dp.hostlist_exclude, &dpl->dp.hostlist_exclude_files)) + return false; + return true; } diff --git a/nfq/hostlist.h b/nfq/hostlist.h index ba0e34b..0bdb94a 100644 --- a/nfq/hostlist.h +++ b/nfq/hostlist.h @@ -2,6 +2,7 @@ #include #include "pools.h" +#include "params.h" bool AppendHostList(strpool **hostlist, char *filename); bool LoadHostLists(strpool **hostlist, struct str_list_head *file_list); @@ -10,4 +11,4 @@ bool LoadExcludeHostLists(); bool NonEmptyHostlist(strpool **hostlist); bool SearchHostList(strpool *hostlist, const char *host); // return : true = apply fooling, false = do not apply -bool HostlistCheck(const char *host, bool *excluded); \ No newline at end of file +bool HostlistCheck(struct desync_profile *dp,const char *host, bool *excluded); \ No newline at end of file diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 3d1beef..2de6a84 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -54,8 +54,7 @@ static bool bHup = false; static void onhup(int sig) { printf("HUP received !\n"); - if (params.hostlist || params.hostlist_exclude) - printf("Will reload hostlist on next request\n"); + printf("Will reload hostlist on next request (if any)\n"); bHup = true; } // should be called in normal execution @@ -81,7 +80,16 @@ static void onusr1(int sig) static void onusr2(int sig) { printf("\nHOSTFAIL POOL DUMP\n"); - HostFailPoolDump(params.hostlist_auto_fail_counters); + + struct desync_profile_list *dpl; + int n=0; + LIST_FOREACH(dpl, ¶ms.desync_profiles, next) + { + printf("\nDESYNC PROFILE %d\n",n); + HostFailPoolDump(dpl->dp.hostlist_auto_fail_counters); + n++; + } + printf("\n"); } @@ -563,11 +571,8 @@ static void cleanup_params(void) { ConntrackPoolDestroy(¶ms.conntrack); - strlist_destroy(¶ms.hostlist_files); - strlist_destroy(¶ms.hostlist_exclude_files); - StrPoolDestroy(¶ms.hostlist_exclude); - StrPoolDestroy(¶ms.hostlist); - HostFailPoolDestroy(¶ms.hostlist_auto_fail_counters); + dp_list_destroy(¶ms.desync_profiles); + #ifdef __CYGWIN__ strlist_destroy(¶ms.ssid_filter); strlist_destroy(¶ms.nlm_filter); @@ -588,7 +593,7 @@ static bool parse_badseq_increment(const char *opt, uint32_t *value) { if (((opt[0]=='0' && opt[1]=='x') || (opt[0]=='-' && opt[1]=='0' && opt[2]=='x')) && sscanf(opt+2+(opt[0]=='-'), "%X", (int32_t*)value)>0) { - if (opt[0]=='-') params.desync_badseq_increment = -params.desync_badseq_increment; + if (opt[0]=='-') *value = -*value; return true; } else @@ -644,6 +649,32 @@ bool parse_autottl(const char *s, autottl *t) 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; +} #ifdef __CYGWIN__ static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *buf, size_t len) { @@ -678,32 +709,6 @@ static bool wf_make_pf(char *opt, const char *l4, const char *portname, char *bu 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 " \ @@ -750,7 +755,7 @@ static bool wf_make_filter( { char pf_dst_buf[512],iface[64]; 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 : ""; + const char *f_tcpin = *pf_tcp_src ? dp_list_have_autohostlist(¶ms.desync_profiles) ? "(" DIVERT_TCP_INBOUNDS " or (" DIVERT_HTTP_REDIRECT "))" : DIVERT_TCP_INBOUNDS : ""; snprintf(iface,sizeof(iface)," ifIdx=%u and subIfIdx=%u and",IfIdx,SubIfIdx); @@ -823,6 +828,11 @@ static void exithelp(void) " --nlm-filter=net1[,net2,net3,...]\t\t; enable winws only if any of specified NLM network is connected. names and GUIDs are accepted.\n" " --nlm-list[=all]\t\t\t\t; list Network List Manager (NLM) networks. connected only or all.\n" #endif + "\nMULTI-STRATEGY:\n" + " --new\t\t\t\t\t\t; begin new strategy\n" + " --filter-l3=ipv4|ipv6\t\t\t\t; L3 protocol filter. multiple comma separated values allowed.\n" + " --filter-tcp=[~]port1[-port2]\t\t\t; TCP port filter. ~ means negation. setting tcp and not setting udp filter denies udp.\n" + " --filter-udp=[~]port1[-port2]\t\t\t; UDP port filter. ~ means negation. setting udp and not setting tcp filter denies tcp.\n" "\nHOSTLIST FILTER:\n" " --hostlist=\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=\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" @@ -917,6 +927,8 @@ bool parse_tlspos(const char *s, enum tlspos *pos) int main(int argc, char **argv) { + cd_to_exe_dir(argv[0]); + #ifdef __CYGWIN__ if (service_run(argc, argv)) { @@ -935,50 +947,33 @@ int main(int argc, char **argv) unsigned int hash_wf_tcp=0,hash_wf_udp=0,hash_wf_raw=0,hash_ssid_filter=0,hash_nlm_filter=0; *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)); memset(¶ms, 0, sizeof(params)); - memcpy(params.hostspell, "host", 4); // default hostspell *pidfile = 0; + struct desync_profile_list *dpl; + struct desync_profile *dp; + int desync_profile_count=0; + if (!(dpl = dp_list_add(¶ms.desync_profiles))) + { + DLOG_ERR("desync_profile_add: out of memory\n"); + exit_clean(1); + } + dp = &dpl->dp; + dp->n = ++desync_profile_count; + #ifdef __linux__ params.qnum = -1; +#elif defined(BSD) + params.port = 0; #endif params.desync_fwmark = DPI_DESYNC_FWMARK_DEFAULT; - params.desync_skip_nosni = true; - params.desync_split_pos = 2; - params.desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT; - params.desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT; - params.desync_repeats = 1; - params.fake_tls_size = sizeof(fake_tls_clienthello_default); - memcpy(params.fake_tls,fake_tls_clienthello_default,params.fake_tls_size); - randomize_default_tls_payload(params.fake_tls); - params.fake_http_size = strlen(fake_http_request_default); - memcpy(params.fake_http,fake_http_request_default,params.fake_http_size); - params.fake_quic_size = 620; // must be 601+ for TSPU hack - params.fake_quic[0] = 0x40; // russian TSPU QUIC short header fake - params.fake_wg_size = 64; - params.fake_dht_size = 64; - params.fake_unknown_size = 256; - params.fake_syndata_size = 16; - params.fake_unknown_udp_size = 64; - params.wscale=-1; // default - dont change scale factor (client) params.ctrack_t_syn = CTRACK_T_SYN; params.ctrack_t_est = CTRACK_T_EST; params.ctrack_t_fin = CTRACK_T_FIN; params.ctrack_t_udp = CTRACK_T_UDP; - params.desync_ttl6 = 0xFF; // unused - params.desync_badseq_increment = BADSEQ_INCREMENT_DEFAULT; - params.desync_badseq_ack_increment = BADSEQ_ACK_INCREMENT_DEFAULT; - params.wssize_cutoff_mode = params.desync_start_mode = params.desync_cutoff_mode = 'n'; // packet number by default - params.udplen_increment = UDPLEN_INCREMENT_DEFAULT; - params.hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT; - params.hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT; - params.hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT; - - LIST_INIT(¶ms.hostlist_files); - LIST_INIT(¶ms.hostlist_exclude_files); #ifdef __CYGWIN__ LIST_INIT(¶ms.ssid_filter); @@ -1061,20 +1056,23 @@ int main(int argc, char **argv) {"hostlist-auto-fail-time",required_argument,0,0}, // optidx=49 {"hostlist-auto-retrans-threshold",required_argument,0,0}, // optidx=50 {"hostlist-auto-debug",required_argument,0,0}, // optidx=51 - + {"new",no_argument,0,0}, // optidx=52 + {"filter-l3",required_argument,0,0}, // optidx=53 + {"filter-tcp",required_argument,0,0}, // optidx=54 + {"filter-udp",required_argument,0,0}, // optidx=55 #ifdef __linux__ - {"bind-fix4",no_argument,0,0}, // optidx=52 - {"bind-fix6",no_argument,0,0}, // optidx=53 + {"bind-fix4",no_argument,0,0}, // optidx=56 + {"bind-fix6",no_argument,0,0}, // optidx=57 #elif defined(__CYGWIN__) - {"wf-iface",required_argument,0,0}, // optidx=52 - {"wf-l3",required_argument,0,0}, // optidx=53 - {"wf-tcp",required_argument,0,0}, // optidx=54 - {"wf-udp",required_argument,0,0}, // optidx=55 - {"wf-raw",required_argument,0,0}, // optidx=56 - {"wf-save",required_argument,0,0}, // optidx=57 - {"ssid-filter",required_argument,0,0}, // optidx=58 - {"nlm-filter",required_argument,0,0}, // optidx=59 - {"nlm-list",optional_argument,0,0}, // optidx=60 + {"wf-iface",required_argument,0,0}, // optidx=56 + {"wf-l3",required_argument,0,0}, // optidx=57 + {"wf-tcp",required_argument,0,0}, // optidx=58 + {"wf-udp",required_argument,0,0}, // optidx=59 + {"wf-raw",required_argument,0,0}, // optidx=60 + {"wf-save",required_argument,0,0}, // optidx=61 + {"ssid-filter",required_argument,0,0}, // optidx=62 + {"nlm-filter",required_argument,0,0}, // optidx=63 + {"nlm-list",optional_argument,0,0}, // optidx=64 #endif {NULL,0,NULL,0} }; @@ -1176,15 +1174,15 @@ int main(int argc, char **argv) break; #endif case 6: /* wsize */ - if (!parse_ws_scale_factor(optarg,¶ms.wsize,¶ms.wscale)) + if (!parse_ws_scale_factor(optarg,&dp->wsize,&dp->wscale)) exit_clean(1); break; case 7: /* wssize */ - if (!parse_ws_scale_factor(optarg,¶ms.wssize,¶ms.wsscale)) + if (!parse_ws_scale_factor(optarg,&dp->wssize,&dp->wsscale)) exit_clean(1); break; case 8: /* wssize-cutoff */ - if (!parse_cutoff(optarg, ¶ms.wssize_cutoff, ¶ms.wssize_cutoff_mode)) + if (!parse_cutoff(optarg, &dp->wssize_cutoff, &dp->wssize_cutoff_mode)) { DLOG_ERR("invalid wssize-cutoff value\n"); exit_clean(1); @@ -1198,7 +1196,7 @@ int main(int argc, char **argv) } break; case 10: /* hostcase */ - params.hostcase = true; + dp->hostcase = true; break; case 11: /* hostspell */ if (strlen(optarg) != 4) @@ -1206,14 +1204,14 @@ int main(int argc, char **argv) DLOG_ERR("hostspell must be exactly 4 chars long\n"); exit_clean(1); } - params.hostcase = true; - memcpy(params.hostspell, optarg, 4); + dp->hostcase = true; + memcpy(dp->hostspell, optarg, 4); break; case 12: /* hostnospace */ - params.hostnospace = true; + dp->hostnospace = true; break; case 13: /* domcase */ - params.domcase = true; + dp->domcase = true; break; case 14: /* dpi-desync */ { @@ -1223,8 +1221,8 @@ int main(int argc, char **argv) mode3 = mode2 ? strchr(mode2,',') : NULL; if (mode3) *mode3++=0; - params.desync_mode0 = desync_mode_from_string(mode); - if (desync_valid_zero_stage(params.desync_mode0)) + dp->desync_mode0 = desync_mode_from_string(mode); + if (desync_valid_zero_stage(dp->desync_mode0)) { mode = mode2; mode2 = mode3; @@ -1232,11 +1230,11 @@ int main(int argc, char **argv) } else { - params.desync_mode0 = DESYNC_NONE; + dp->desync_mode0 = DESYNC_NONE; } - params.desync_mode = desync_mode_from_string(mode); - params.desync_mode2 = desync_mode_from_string(mode2); - if (params.desync_mode0==DESYNC_INVALID || params.desync_mode==DESYNC_INVALID || params.desync_mode2==DESYNC_INVALID) + dp->desync_mode = desync_mode_from_string(mode); + dp->desync_mode2 = desync_mode_from_string(mode2); + if (dp->desync_mode0==DESYNC_INVALID || dp->desync_mode==DESYNC_INVALID || dp->desync_mode2==DESYNC_INVALID) { DLOG_ERR("invalid dpi-desync mode\n"); exit_clean(1); @@ -1246,13 +1244,13 @@ int main(int argc, char **argv) DLOG_ERR("invalid desync combo : %s+%s+%s\n",mode,mode2,mode3); exit_clean(1); } - if (params.desync_mode2 && (desync_only_first_stage(params.desync_mode) || !(desync_valid_first_stage(params.desync_mode) && desync_valid_second_stage(params.desync_mode2)))) + if (dp->desync_mode2 && (desync_only_first_stage(dp->desync_mode) || !(desync_valid_first_stage(dp->desync_mode) && desync_valid_second_stage(dp->desync_mode2)))) { DLOG_ERR("invalid desync combo : %s+%s\n", mode,mode2); exit_clean(1); } #if defined(__OpenBSD__) - if (params.desync_mode==DESYNC_IPFRAG2 || params.desync_mode2==DESYNC_IPFRAG2) + if (dp->desync_mode==DESYNC_IPFRAG2 || dp->desync_mode2==DESYNC_IPFRAG2) { DLOG_ERR("OpenBSD has checksum issues with fragmented packets. ipfrag disabled.\n"); exit_clean(1); @@ -1277,20 +1275,20 @@ int main(int argc, char **argv) break; #endif case 16: /* dpi-desync-ttl */ - params.desync_ttl = (uint8_t)atoi(optarg); + dp->desync_ttl = (uint8_t)atoi(optarg); break; case 17: /* dpi-desync-ttl6 */ - params.desync_ttl6 = (uint8_t)atoi(optarg); + dp->desync_ttl6 = (uint8_t)atoi(optarg); break; case 18: /* dpi-desync-autottl */ - if (!parse_autottl(optarg, ¶ms.desync_autottl)) + if (!parse_autottl(optarg, &dp->desync_autottl)) { DLOG_ERR("dpi-desync-autottl value error\n"); exit_clean(1); } break; case 19: /* dpi-desync-autottl6 */ - if (!parse_autottl(optarg, ¶ms.desync_autottl6)) + if (!parse_autottl(optarg, &dp->desync_autottl6)) { DLOG_ERR("dpi-desync-autottl6 value error\n"); exit_clean(1); @@ -1304,24 +1302,24 @@ int main(int argc, char **argv) e = strchr(p,','); if (e) *e++=0; if (!strcmp(p,"md5sig")) - params.desync_fooling_mode |= FOOL_MD5SIG; + dp->desync_fooling_mode |= FOOL_MD5SIG; else if (!strcmp(p,"ts")) - params.desync_fooling_mode |= FOOL_TS; + dp->desync_fooling_mode |= FOOL_TS; else if (!strcmp(p,"badsum")) { #ifdef __OpenBSD__ DLOG_CONDUP("\nWARNING !!! OpenBSD may forcibly recompute tcp/udp checksums !!! In this case badsum fooling will not work.\nYou should check tcp checksum correctness in tcpdump manually before using badsum.\n\n"); #endif - params.desync_fooling_mode |= FOOL_BADSUM; + dp->desync_fooling_mode |= FOOL_BADSUM; } else if (!strcmp(p,"badseq")) - params.desync_fooling_mode |= FOOL_BADSEQ; + dp->desync_fooling_mode |= FOOL_BADSEQ; else if (!strcmp(p,"datanoack")) - params.desync_fooling_mode |= FOOL_DATANOACK; + dp->desync_fooling_mode |= FOOL_DATANOACK; else if (!strcmp(p,"hopbyhop")) - params.desync_fooling_mode |= FOOL_HOPBYHOP; + dp->desync_fooling_mode |= FOOL_HOPBYHOP; else if (!strcmp(p,"hopbyhop2")) - params.desync_fooling_mode |= FOOL_HOPBYHOP2; + dp->desync_fooling_mode |= FOOL_HOPBYHOP2; else if (strcmp(p,"none")) { DLOG_ERR("dpi-desync-fooling allowed values : none,md5sig,ts,badseq,badsum,datanoack,hopbyhop,hopbyhop2\n"); @@ -1332,38 +1330,38 @@ int main(int argc, char **argv) } break; case 21: /* dpi-desync-repeats */ - if (sscanf(optarg,"%u",¶ms.desync_repeats)<1 || !params.desync_repeats || params.desync_repeats>20) + if (sscanf(optarg,"%u",&dp->desync_repeats)<1 || !dp->desync_repeats || dp->desync_repeats>20) { DLOG_ERR("dpi-desync-repeats must be within 1..20\n"); exit_clean(1); } break; case 22: /* dpi-desync-skip-nosni */ - params.desync_skip_nosni = !optarg || atoi(optarg); + dp->desync_skip_nosni = !optarg || atoi(optarg); break; case 23: /* dpi-desync-split-pos */ - if (sscanf(optarg,"%u",¶ms.desync_split_pos)<1 || params.desync_split_pos<1) + if (sscanf(optarg,"%u",&dp->desync_split_pos)<1 || dp->desync_split_pos<1) { DLOG_ERR("dpi-desync-split-pos is not valid\n"); exit_clean(1); } break; case 24: /* dpi-desync-split-http-req */ - if (!parse_httpreqpos(optarg, ¶ms.desync_split_http_req)) + if (!parse_httpreqpos(optarg, &dp->desync_split_http_req)) { DLOG_ERR("Invalid argument for dpi-desync-split-http-req\n"); exit_clean(1); } break; case 25: /* dpi-desync-split-tls */ - if (!parse_tlspos(optarg, ¶ms.desync_split_tls)) + if (!parse_tlspos(optarg, &dp->desync_split_tls)) { DLOG_ERR("Invalid argument for dpi-desync-split-tls\n"); exit_clean(1); } break; case 26: /* dpi-desync-split-seqovl */ - if (sscanf(optarg,"%u",¶ms.desync_seqovl)<1) + if (sscanf(optarg,"%u",&dp->desync_seqovl)<1) { DLOG_ERR("dpi-desync-split-seqovl is not valid\n"); exit_clean(1); @@ -1371,87 +1369,87 @@ int main(int argc, char **argv) break; case 27: /* dpi-desync-split-seqovl-pattern */ { - char buf[sizeof(params.seqovl_pattern)]; + char buf[sizeof(dp->seqovl_pattern)]; size_t sz=sizeof(buf); load_file_or_exit(optarg,buf,&sz); - fill_pattern(params.seqovl_pattern,sizeof(params.seqovl_pattern),buf,sz); + fill_pattern(dp->seqovl_pattern,sizeof(dp->seqovl_pattern),buf,sz); } break; case 28: /* dpi-desync-ipfrag-pos-tcp */ - if (sscanf(optarg,"%u",¶ms.desync_ipfrag_pos_tcp)<1 || params.desync_ipfrag_pos_tcp<1 || params.desync_ipfrag_pos_tcp>DPI_DESYNC_MAX_FAKE_LEN) + if (sscanf(optarg,"%u",&dp->desync_ipfrag_pos_tcp)<1 || dp->desync_ipfrag_pos_tcp<1 || dp->desync_ipfrag_pos_tcp>DPI_DESYNC_MAX_FAKE_LEN) { DLOG_ERR("dpi-desync-ipfrag-pos-tcp must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN); exit_clean(1); } - if (params.desync_ipfrag_pos_tcp & 7) + if (dp->desync_ipfrag_pos_tcp & 7) { DLOG_ERR("dpi-desync-ipfrag-pos-tcp must be multiple of 8\n"); exit_clean(1); } break; case 29: /* dpi-desync-ipfrag-pos-udp */ - if (sscanf(optarg,"%u",¶ms.desync_ipfrag_pos_udp)<1 || params.desync_ipfrag_pos_udp<1 || params.desync_ipfrag_pos_udp>DPI_DESYNC_MAX_FAKE_LEN) + if (sscanf(optarg,"%u",&dp->desync_ipfrag_pos_udp)<1 || dp->desync_ipfrag_pos_udp<1 || dp->desync_ipfrag_pos_udp>DPI_DESYNC_MAX_FAKE_LEN) { DLOG_ERR("dpi-desync-ipfrag-pos-udp must be within 1..%u range\n",DPI_DESYNC_MAX_FAKE_LEN); exit_clean(1); } - if (params.desync_ipfrag_pos_udp & 7) + if (dp->desync_ipfrag_pos_udp & 7) { DLOG_ERR("dpi-desync-ipfrag-pos-udp must be multiple of 8\n"); exit_clean(1); } break; case 30: /* dpi-desync-badseq-increments */ - if (!parse_badseq_increment(optarg,¶ms.desync_badseq_increment)) + if (!parse_badseq_increment(optarg,&dp->desync_badseq_increment)) { DLOG_ERR("dpi-desync-badseq-increment should be signed decimal or signed 0xHEX\n"); exit_clean(1); } break; case 31: /* dpi-desync-badack-increment */ - if (!parse_badseq_increment(optarg,¶ms.desync_badseq_ack_increment)) + if (!parse_badseq_increment(optarg,&dp->desync_badseq_ack_increment)) { DLOG_ERR("dpi-desync-badack-increment should be signed decimal or signed 0xHEX\n"); exit_clean(1); } break; case 32: /* dpi-desync-any-protocol */ - params.desync_any_proto = !optarg || atoi(optarg); + dp->desync_any_proto = !optarg || atoi(optarg); break; case 33: /* dpi-desync-fake-http */ - params.fake_http_size = sizeof(params.fake_http); - load_file_or_exit(optarg,params.fake_http,¶ms.fake_http_size); + dp->fake_http_size = sizeof(dp->fake_http); + load_file_or_exit(optarg,dp->fake_http,&dp->fake_http_size); break; case 34: /* dpi-desync-fake-tls */ - params.fake_tls_size = sizeof(params.fake_tls); - load_file_or_exit(optarg,params.fake_tls,¶ms.fake_tls_size); + dp->fake_tls_size = sizeof(dp->fake_tls); + load_file_or_exit(optarg,dp->fake_tls,&dp->fake_tls_size); break; case 35: /* dpi-desync-fake-unknown */ - params.fake_unknown_size = sizeof(params.fake_unknown); - load_file_or_exit(optarg,params.fake_unknown,¶ms.fake_unknown_size); + dp->fake_unknown_size = sizeof(dp->fake_unknown); + load_file_or_exit(optarg,dp->fake_unknown,&dp->fake_unknown_size); break; case 36: /* dpi-desync-fake-syndata */ - params.fake_syndata_size = sizeof(params.fake_syndata); - load_file_or_exit(optarg,params.fake_syndata,¶ms.fake_syndata_size); + dp->fake_syndata_size = sizeof(dp->fake_syndata); + load_file_or_exit(optarg,dp->fake_syndata,&dp->fake_syndata_size); break; case 37: /* dpi-desync-fake-quic */ - params.fake_quic_size = sizeof(params.fake_quic); - load_file_or_exit(optarg,params.fake_quic,¶ms.fake_quic_size); + dp->fake_quic_size = sizeof(dp->fake_quic); + load_file_or_exit(optarg,dp->fake_quic,&dp->fake_quic_size); break; case 38: /* dpi-desync-fake-wireguard */ - params.fake_wg_size = sizeof(params.fake_wg); - load_file_or_exit(optarg,params.fake_wg,¶ms.fake_wg_size); + dp->fake_wg_size = sizeof(dp->fake_wg); + load_file_or_exit(optarg,dp->fake_wg,&dp->fake_wg_size); break; case 39: /* dpi-desync-fake-dht */ - params.fake_dht_size = sizeof(params.fake_dht); - load_file_or_exit(optarg,params.fake_dht,¶ms.fake_dht_size); + dp->fake_dht_size = sizeof(dp->fake_dht); + load_file_or_exit(optarg,dp->fake_dht,&dp->fake_dht_size); break; case 40: /* dpi-desync-fake-unknown-udp */ - params.fake_unknown_udp_size = sizeof(params.fake_unknown_udp); - load_file_or_exit(optarg,params.fake_unknown_udp,¶ms.fake_unknown_udp_size); + dp->fake_unknown_udp_size = sizeof(dp->fake_unknown_udp); + load_file_or_exit(optarg,dp->fake_unknown_udp,&dp->fake_unknown_udp_size); break; case 41: /* dpi-desync-udplen-increment */ - if (sscanf(optarg,"%d",¶ms.udplen_increment)<1 || params.udplen_increment>0x7FFF || params.udplen_increment<-0x8000) + if (sscanf(optarg,"%d",&dp->udplen_increment)<1 || dp->udplen_increment>0x7FFF || dp->udplen_increment<-0x8000) { DLOG_ERR("dpi-desync-udplen-increment must be integer within -32768..32767 range\n"); exit_clean(1); @@ -1459,44 +1457,44 @@ int main(int argc, char **argv) break; case 42: /* dpi-desync-udplen-pattern */ { - char buf[sizeof(params.udplen_pattern)]; + char buf[sizeof(dp->udplen_pattern)]; size_t sz=sizeof(buf); load_file_or_exit(optarg,buf,&sz); - fill_pattern(params.udplen_pattern,sizeof(params.udplen_pattern),buf,sz); + fill_pattern(dp->udplen_pattern,sizeof(dp->udplen_pattern),buf,sz); } break; case 43: /* desync-cutoff */ - if (!parse_cutoff(optarg, ¶ms.desync_cutoff, ¶ms.desync_cutoff_mode)) + if (!parse_cutoff(optarg, &dp->desync_cutoff, &dp->desync_cutoff_mode)) { DLOG_ERR("invalid desync-cutoff value\n"); exit_clean(1); } break; case 44: /* desync-start */ - if (!parse_cutoff(optarg, ¶ms.desync_start, ¶ms.desync_start_mode)) + if (!parse_cutoff(optarg, &dp->desync_start, &dp->desync_start_mode)) { DLOG_ERR("invalid desync-start value\n"); exit_clean(1); } break; case 45: /* hostlist */ - if (!strlist_add(¶ms.hostlist_files, optarg)) + if (!strlist_add(&dp->hostlist_files, optarg)) { DLOG_ERR("strlist_add failed\n"); exit_clean(1); } break; case 46: /* hostlist-exclude */ - if (!strlist_add(¶ms.hostlist_exclude_files, optarg)) + if (!strlist_add(&dp->hostlist_exclude_files, optarg)) { DLOG_ERR("strlist_add failed\n"); exit_clean(1); } break; case 47: /* hostlist-auto */ - if (*params.hostlist_auto_filename) + if (*dp->hostlist_auto_filename) { - DLOG_ERR("only one auto hostlist is supported\n"); + DLOG_ERR("only one auto hostlist per profile is supported\n"); exit_clean(1); } { @@ -1518,33 +1516,33 @@ int main(int argc, char **argv) DLOG_ERR("could not chown %s. auto hostlist file may not be writable after privilege drop\n", optarg); #endif } - if (!strlist_add(¶ms.hostlist_files, optarg)) + if (!strlist_add(&dp->hostlist_files, optarg)) { DLOG_ERR("strlist_add failed\n"); exit_clean(1); } - strncpy(params.hostlist_auto_filename, optarg, sizeof(params.hostlist_auto_filename)); - params.hostlist_auto_filename[sizeof(params.hostlist_auto_filename) - 1] = '\0'; + strncpy(dp->hostlist_auto_filename, optarg, sizeof(dp->hostlist_auto_filename)); + dp->hostlist_auto_filename[sizeof(dp->hostlist_auto_filename) - 1] = '\0'; break; case 48: /* hostlist-auto-fail-threshold */ - params.hostlist_auto_fail_threshold = (uint8_t)atoi(optarg); - if (params.hostlist_auto_fail_threshold<1 || params.hostlist_auto_fail_threshold>20) + dp->hostlist_auto_fail_threshold = (uint8_t)atoi(optarg); + if (dp->hostlist_auto_fail_threshold<1 || dp->hostlist_auto_fail_threshold>20) { DLOG_ERR("auto hostlist fail threshold must be within 1..20\n"); exit_clean(1); } break; case 49: /* hostlist-auto-fail-time */ - params.hostlist_auto_fail_time = (uint8_t)atoi(optarg); - if (params.hostlist_auto_fail_time<1) + dp->hostlist_auto_fail_time = (uint8_t)atoi(optarg); + if (dp->hostlist_auto_fail_time<1) { DLOG_ERR("auto hostlist fail time is not valid\n"); exit_clean(1); } break; case 50: /* hostlist-auto-retrans-threshold */ - params.hostlist_auto_retrans_threshold = (uint8_t)atoi(optarg); - if (params.hostlist_auto_retrans_threshold<2 || params.hostlist_auto_retrans_threshold>10) + dp->hostlist_auto_retrans_threshold = (uint8_t)atoi(optarg); + if (dp->hostlist_auto_retrans_threshold<2 || dp->hostlist_auto_retrans_threshold>10) { DLOG_ERR("auto hostlist fail threshold must be within 2..10\n"); exit_clean(1); @@ -1567,29 +1565,65 @@ int main(int argc, char **argv) params.hostlist_auto_debuglog[sizeof(params.hostlist_auto_debuglog) - 1] = '\0'; } break; + + case 52: /* new */ + if (!(dpl = dp_list_add(¶ms.desync_profiles))) + { + DLOG_ERR("desync_profile_add: out of memory\n"); + exit_clean(1); + } + dp = &dpl->dp; + dp->n = ++desync_profile_count; + break; + case 53: /* filter-l3 */ + if (!wf_make_l3(optarg,&dp->filter_ipv4,&dp->filter_ipv6)) + { + DLOG_ERR("bad value for --filter-l3\n"); + exit_clean(1); + } + break; + case 54: /* filter-tcp */ + if (!pf_parse(optarg,&dp->pf_tcp)) + { + DLOG_ERR("Invalid port filter : %s\n",optarg); + exit_clean(1); + } + // deny udp if not set + if (pf_is_empty(&dp->pf_udp)) dp->pf_udp.neg=true; + break; + case 55: /* filter-udp */ + if (!pf_parse(optarg,&dp->pf_udp)) + { + DLOG_ERR("Invalid port filter : %s\n",optarg); + exit_clean(1); + } + // deny tcp if not set + if (pf_is_empty(&dp->pf_tcp)) dp->pf_tcp.neg=true; + break; + #ifdef __linux__ - case 52: /* bind-fix4 */ + case 56: /* bind-fix4 */ params.bind_fix4 = true; break; - case 53: /* bind-fix6 */ + case 57: /* bind-fix6 */ params.bind_fix6 = true; break; #elif defined(__CYGWIN__) - case 52: /* wf-iface */ + case 56: /* wf-iface */ if (!sscanf(optarg,"%u.%u",&IfIdx,&SubIfIdx)) { DLOG_ERR("bad value for --wf-iface\n"); exit_clean(1); } break; - case 53: /* wf-l3 */ + case 57: /* wf-l3 */ if (!wf_make_l3(optarg,&wf_ipv4,&wf_ipv6)) { DLOG_ERR("bad value for --wf-l3\n"); exit_clean(1); } break; - case 54: /* wf-tcp */ + case 58: /* wf-tcp */ hash_wf_tcp=hash_jen(optarg,strlen(optarg)); 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))) @@ -1598,7 +1632,7 @@ int main(int argc, char **argv) exit_clean(1); } break; - case 55: /* wf-udp */ + case 59: /* wf-udp */ hash_wf_udp=hash_jen(optarg,strlen(optarg)); 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))) @@ -1607,7 +1641,7 @@ int main(int argc, char **argv) exit_clean(1); } break; - case 56: /* wf-raw */ + case 60: /* wf-raw */ hash_wf_raw=hash_jen(optarg,strlen(optarg)); if (optarg[0]=='@') { @@ -1621,11 +1655,11 @@ int main(int argc, char **argv) windivert_filter[sizeof(windivert_filter) - 1] = '\0'; } break; - case 57: /* wf-save */ + case 61: /* wf-save */ strncpy(wf_save_file, optarg, sizeof(wf_save_file)); wf_save_file[sizeof(wf_save_file) - 1] = '\0'; break; - case 58: /* ssid-filter */ + case 62: /* ssid-filter */ hash_ssid_filter=hash_jen(optarg,strlen(optarg)); { char *e,*p = optarg; @@ -1643,7 +1677,7 @@ int main(int argc, char **argv) } } break; - case 59: /* nlm-filter */ + case 63: /* nlm-filter */ hash_nlm_filter=hash_jen(optarg,strlen(optarg)); { char *e,*p = optarg; @@ -1661,7 +1695,7 @@ int main(int argc, char **argv) } } break; - case 60: /* nlm-list */ + case 64: /* nlm-list */ if (!nlm_list(optarg && !strcmp(optarg,"all"))) { DLOG_ERR("could not get list of NLM networks\n"); @@ -1729,29 +1763,35 @@ int main(int argc, char **argv) } #endif - // not specified - use desync_ttl value instead - if (params.desync_ttl6 == 0xFF) params.desync_ttl6=params.desync_ttl; - if (!AUTOTTL_ENABLED(params.desync_autottl6)) params.desync_autottl6 = params.desync_autottl; - if (AUTOTTL_ENABLED(params.desync_autottl)) - DLOG("autottl ipv4 %u:%u-%u\n",params.desync_autottl.delta,params.desync_autottl.min,params.desync_autottl.max); - if (AUTOTTL_ENABLED(params.desync_autottl6)) - DLOG("autottl ipv6 %u:%u-%u\n",params.desync_autottl6.delta,params.desync_autottl6.min,params.desync_autottl6.max); - if (params.desync_split_tls==tlspos_none && params.desync_split_pos) params.desync_split_tls=tlspos_pos; - if (params.desync_split_http_req==httpreqpos_none && params.desync_split_pos) params.desync_split_http_req=httpreqpos_pos; + DLOG_CONDUP("we have %d desync profile(s)\n",desync_profile_count); + + v=0; + LIST_FOREACH(dpl, ¶ms.desync_profiles, next) + { + dp = &dpl->dp; + // not specified - use desync_ttl value instead + if (dp->desync_ttl6 == 0xFF) dp->desync_ttl6=dp->desync_ttl; + if (!AUTOTTL_ENABLED(dp->desync_autottl6)) dp->desync_autottl6 = dp->desync_autottl; + if (AUTOTTL_ENABLED(dp->desync_autottl)) + DLOG("[profile %d] autottl ipv4 %u:%u-%u\n",v,dp->desync_autottl.delta,dp->desync_autottl.min,dp->desync_autottl.max); + if (AUTOTTL_ENABLED(dp->desync_autottl6)) + DLOG("[profile %d] autottl ipv6 %u:%u-%u\n",v,dp->desync_autottl6.delta,dp->desync_autottl6.min,dp->desync_autottl6.max); + if (dp->desync_split_tls==tlspos_none && dp->desync_split_pos) dp->desync_split_tls=tlspos_pos; + if (dp->desync_split_http_req==httpreqpos_none && dp->desync_split_pos) dp->desync_split_http_req=httpreqpos_pos; + v++; + } if (!LoadIncludeHostLists()) { - DLOG_ERR("Include hostlist load failed\n"); + DLOG_ERR("Include hostlists load failed\n"); exit_clean(1); } - if (*params.hostlist_auto_filename) - NonEmptyHostlist(¶ms.hostlist); if (!LoadExcludeHostLists()) { - DLOG_ERR("Exclude hostlist load failed\n"); + DLOG_ERR("Exclude hostlists load failed\n"); exit_clean(1); } - + if (daemon) daemonize(); if (*pidfile && !writepid(pidfile)) diff --git a/nfq/params.c b/nfq/params.c index 5e29a92..e6ad407 100644 --- a/nfq/params.c +++ b/nfq/params.c @@ -1,8 +1,12 @@ #include "params.h" + #include #include #include +#include "pools.h" +#include "desync.h" + #ifdef BSD const char *progname = "dvtws"; #elif defined(__CYGWIN__) @@ -149,3 +153,80 @@ int HOSTLIST_DEBUGLOG_APPEND(const char *format, ...) else return 0; } + + +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; + + LIST_INIT(&entry->dp.hostlist_files); + LIST_INIT(&entry->dp.hostlist_exclude_files); + memcpy(entry->dp.hostspell, "host", 4); // default hostspell + entry->dp.desync_skip_nosni = true; + entry->dp.desync_split_pos = 2; + entry->dp.desync_ipfrag_pos_udp = IPFRAG_UDP_DEFAULT; + entry->dp.desync_ipfrag_pos_tcp = IPFRAG_TCP_DEFAULT; + entry->dp.desync_repeats = 1; + entry->dp.fake_tls_size = sizeof(fake_tls_clienthello_default); + memcpy(entry->dp.fake_tls,fake_tls_clienthello_default,entry->dp.fake_tls_size); + randomize_default_tls_payload(entry->dp.fake_tls); + entry->dp.fake_http_size = strlen(fake_http_request_default); + memcpy(entry->dp.fake_http,fake_http_request_default,entry->dp.fake_http_size); + entry->dp.fake_quic_size = 620; // must be 601+ for TSPU hack + entry->dp.fake_quic[0] = 0x40; // russian TSPU QUIC short header fake + entry->dp.fake_wg_size = 64; + entry->dp.fake_dht_size = 64; + entry->dp.fake_unknown_size = 256; + entry->dp.fake_syndata_size = 16; + entry->dp.fake_unknown_udp_size = 64; + entry->dp.wscale=-1; // default - dont change scale factor (client) + entry->dp.desync_ttl6 = 0xFF; // unused + entry->dp.desync_badseq_increment = BADSEQ_INCREMENT_DEFAULT; + entry->dp.desync_badseq_ack_increment = BADSEQ_ACK_INCREMENT_DEFAULT; + entry->dp.wssize_cutoff_mode = entry->dp.desync_start_mode = entry->dp.desync_cutoff_mode = 'n'; // packet number by default + entry->dp.udplen_increment = UDPLEN_INCREMENT_DEFAULT; + entry->dp.hostlist_auto_fail_threshold = HOSTLIST_AUTO_FAIL_THRESHOLD_DEFAULT; + entry->dp.hostlist_auto_fail_time = HOSTLIST_AUTO_FAIL_TIME_DEFAULT; + entry->dp.hostlist_auto_retrans_threshold = HOSTLIST_AUTO_RETRANS_THRESHOLD_DEFAULT; + entry->dp.filter_ipv4 = entry->dp.filter_ipv6 = true; + + // add to the tail + struct desync_profile_list *dpn,*dpl=LIST_FIRST(¶ms.desync_profiles); + if (dpl) + { + while ((dpn=LIST_NEXT(dpl,next))) dpl = dpn; + LIST_INSERT_AFTER(dpl, entry, next); + } + else + LIST_INSERT_HEAD(¶ms.desync_profiles, entry, next); + + return entry; +} +static void dp_entry_destroy(struct desync_profile_list *entry) +{ + strlist_destroy(&entry->dp.hostlist_files); + strlist_destroy(&entry->dp.hostlist_exclude_files); + StrPoolDestroy(&entry->dp.hostlist_exclude); + StrPoolDestroy(&entry->dp.hostlist); + HostFailPoolDestroy(&entry->dp.hostlist_auto_fail_counters); + free(entry); +} +void dp_list_destroy(struct desync_profile_list_head *head) +{ + struct desync_profile_list *entry; + while ((entry = LIST_FIRST(head))) + { + LIST_REMOVE(entry, next); + dp_entry_destroy(entry); + } +} +bool dp_list_have_autohostlist(struct desync_profile_list_head *head) +{ + struct desync_profile_list *dpl; + LIST_FOREACH(dpl, head, next) + if (*dpl->dp.hostlist_auto_filename) + return true; + return false; +} + diff --git a/nfq/params.h b/nfq/params.h index 70ed34e..b9dd395 100644 --- a/nfq/params.h +++ b/nfq/params.h @@ -4,6 +4,7 @@ #include "conntrack.h" #include "desync.h" #include "protocol.h" +#include "helpers.h" #include #include @@ -12,6 +13,7 @@ #include #include #include +#include #define TLS_PARTIALS_ENABLE true @@ -35,22 +37,15 @@ enum log_target { LOG_TARGET_CONSOLE=0, LOG_TARGET_FILE, LOG_TARGET_SYSLOG }; -struct params_s +struct desync_profile { - enum log_target debug_target; - char debug_logfile[PATH_MAX]; - bool debug; + int n; // number of the profile uint16_t wsize,wssize; uint8_t wscale,wsscale; char wssize_cutoff_mode; // n - packets, d - data packets, s - relative sequence unsigned int wssize_cutoff; -#ifdef __linux__ - int qnum; -#elif defined(BSD) - uint16_t port; // divert port -#endif - char bind_fix4,bind_fix6; + bool hostcase, hostnospace, domcase; char hostspell[4]; enum dpi_desync_mode desync_mode0,desync_mode,desync_mode2; @@ -63,13 +58,47 @@ struct params_s uint8_t desync_ttl, desync_ttl6; autottl desync_autottl, desync_autottl6; uint32_t desync_fooling_mode; - uint32_t desync_fwmark; // unused in BSD uint32_t desync_badseq_increment, desync_badseq_ack_increment; uint8_t fake_http[1460],fake_tls[1460],fake_unknown[1460],fake_syndata[1460],seqovl_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_tls_size,fake_quic_size,fake_wg_size,fake_dht_size,fake_unknown_size,fake_syndata_size,fake_unknown_udp_size; int udplen_increment; + bool filter_ipv4,filter_ipv6; + port_filter pf_tcp,pf_udp; + strpool *hostlist, *hostlist_exclude; + struct str_list_head hostlist_files, hostlist_exclude_files; + char hostlist_auto_filename[PATH_MAX]; + int hostlist_auto_fail_threshold, hostlist_auto_fail_time, hostlist_auto_retrans_threshold; + time_t hostlist_auto_mod_time; + hostfail_pool *hostlist_auto_fail_counters; +}; + +struct desync_profile_list { + struct desync_profile dp; + LIST_ENTRY(desync_profile_list) next; +}; +LIST_HEAD(desync_profile_list_head, desync_profile_list); +struct desync_profile_list *dp_list_add(struct desync_profile_list_head *head); +void dp_list_destroy(struct desync_profile_list_head *head); +bool dp_list_have_autohostlist(struct desync_profile_list_head *head); + +struct params_s +{ + enum log_target debug_target; + char debug_logfile[PATH_MAX]; + bool debug; + +#ifdef __linux__ + int qnum; +#elif defined(BSD) + uint16_t port; // divert port +#endif + char bind_fix4,bind_fix6; + uint32_t desync_fwmark; // unused in BSD + + struct desync_profile_list_head desync_profiles; + #ifdef __CYGWIN__ struct str_list_head ssid_filter,nlm_filter; #else @@ -78,13 +107,8 @@ struct params_s gid_t gid; #endif - strpool *hostlist, *hostlist_exclude; - struct str_list_head hostlist_files, hostlist_exclude_files; - char hostlist_auto_filename[PATH_MAX], hostlist_auto_debuglog[PATH_MAX]; - int hostlist_auto_fail_threshold, hostlist_auto_fail_time, hostlist_auto_retrans_threshold; - time_t hostlist_auto_mod_time; - hostfail_pool *hostlist_auto_fail_counters; - + char hostlist_auto_debuglog[PATH_MAX]; + unsigned int ctrack_t_syn, ctrack_t_est, ctrack_t_fin, ctrack_t_udp; t_conntrack conntrack; }; diff --git a/nfq/pools.h b/nfq/pools.h index ab58968..154d541 100644 --- a/nfq/pools.h +++ b/nfq/pools.h @@ -26,7 +26,6 @@ struct str_list { }; LIST_HEAD(str_list_head, str_list); - typedef struct hostfail_pool { char *str; /* key */ int counter; /* value */