mirror of https://github.com/bol-van/zapret/
175 changed files with 0 additions and 47127 deletions
@ -1,74 +0,0 @@ |
|||||
DIRS := nfq tpws ip2net mdig |
|
||||
DIRS_MAC := tpws ip2net mdig |
|
||||
TGT := binaries/my |
|
||||
|
|
||||
all: clean |
|
||||
@mkdir -p "$(TGT)"; \
|
|
||||
for dir in $(DIRS); do \
|
|
||||
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
|
|
||||
$(MAKE) -C "$$dir" || exit; \
|
|
||||
for exe in "$$dir/"*; do \
|
|
||||
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
|
|
||||
mv -f "$$exe" "${TGT}" ; \
|
|
||||
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
|
|
||||
fi \
|
|
||||
done \
|
|
||||
done |
|
||||
|
|
||||
systemd: clean |
|
||||
@mkdir -p "$(TGT)"; \
|
|
||||
for dir in $(DIRS); do \
|
|
||||
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
|
|
||||
$(MAKE) -C "$$dir" systemd || exit; \
|
|
||||
for exe in "$$dir/"*; do \
|
|
||||
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
|
|
||||
mv -f "$$exe" "${TGT}" ; \
|
|
||||
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
|
|
||||
fi \
|
|
||||
done \
|
|
||||
done |
|
||||
|
|
||||
android: clean |
|
||||
@mkdir -p "$(TGT)"; \
|
|
||||
for dir in $(DIRS); do \
|
|
||||
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
|
|
||||
$(MAKE) -C "$$dir" android || exit; \
|
|
||||
for exe in "$$dir/"*; do \
|
|
||||
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
|
|
||||
mv -f "$$exe" "${TGT}" ; \
|
|
||||
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
|
|
||||
fi \
|
|
||||
done \
|
|
||||
done |
|
||||
|
|
||||
bsd: clean |
|
||||
@mkdir -p "$(TGT)"; \
|
|
||||
for dir in $(DIRS); do \
|
|
||||
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
|
|
||||
$(MAKE) -C "$$dir" bsd || exit; \
|
|
||||
for exe in "$$dir/"*; do \
|
|
||||
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
|
|
||||
mv -f "$$exe" "${TGT}" ; \
|
|
||||
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
|
|
||||
fi \
|
|
||||
done \
|
|
||||
done |
|
||||
|
|
||||
mac: clean |
|
||||
@mkdir -p "$(TGT)"; \
|
|
||||
for dir in $(DIRS_MAC); do \
|
|
||||
find "$$dir" -type f \( -name "*.c" -o -name "*.h" -o -name "*akefile" \) -exec chmod -x {} \; ; \
|
|
||||
$(MAKE) -C "$$dir" mac || exit; \
|
|
||||
for exe in "$$dir/"*; do \
|
|
||||
if [ -f "$$exe" ] && [ -x "$$exe" ]; then \
|
|
||||
mv -f "$$exe" "${TGT}" ; \
|
|
||||
ln -fs "../${TGT}/$$(basename "$$exe")" "$$exe" ; \
|
|
||||
fi \
|
|
||||
done \
|
|
||||
done |
|
||||
|
|
||||
clean: |
|
||||
@[ -d "$(TGT)" ] && rm -rf "$(TGT)" ; \
|
|
||||
for dir in $(DIRS); do \
|
|
||||
$(MAKE) -C "$$dir" clean; \
|
|
||||
done |
|
||||
@ -1,21 +0,0 @@ |
|||||
MIT License |
|
||||
|
|
||||
Copyright (c) 2016-2024 bol-van |
|
||||
|
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|
||||
of this software and associated documentation files (the "Software"), to deal |
|
||||
in the Software without restriction, including without limitation the rights |
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
||||
copies of the Software, and to permit persons to whom the Software is |
|
||||
furnished to do so, subject to the following conditions: |
|
||||
|
|
||||
The above copyright notice and this permission notice shall be included in all |
|
||||
copies or substantial portions of the Software. |
|
||||
|
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
||||
SOFTWARE. |
|
||||
@ -1,627 +0,0 @@ |
|||||
## Table of contents |
|
||||
|
|
||||
- [Table of contents](#table-of-contents) |
|
||||
- [Supported versions](#supported-versions) |
|
||||
- [BSD features](#bsd-features) |
|
||||
- [FreeBSD](#freebsd) |
|
||||
- [`dvtws` quick start](#dvtws-quick-start) |
|
||||
- [PF in FreeBSD](#pf-in-freebsd) |
|
||||
- [`pfsense`](#pfsense) |
|
||||
- [OpenBSD](#openbsd) |
|
||||
- [MacOS](#macos) |
|
||||
- [MacOS easy install](#macos-easy-install) |
|
||||
|
|
||||
## Supported versions |
|
||||
|
|
||||
FreeBSD 11.x+ , OpenBSD 6.x+, partially MacOS Sierra+ |
|
||||
|
|
||||
Older versions may work or not. |
|
||||
|
|
||||
## BSD features |
|
||||
|
|
||||
BSD does not have NFQUEUE. Similar mechanism - divert sockets. In BSD compiling |
|
||||
the source from nfq directory result in `dvtws` binary instead of `nfqws`. |
|
||||
`dvtws` shares most of the code with `nfqws` and offers almost identical |
|
||||
parameters. |
|
||||
|
|
||||
FreeBSD has 3 firewalls: IPFilter, ipfw and Packet Filter (PF). OpenBSD has |
|
||||
only PF. |
|
||||
|
|
||||
To compile sources: |
|
||||
|
|
||||
- FreeBSD: `make` |
|
||||
- OpenBSD: `make bsd` |
|
||||
- MacOS: `make mac` |
|
||||
|
|
||||
Compile all programs: |
|
||||
``` |
|
||||
make -C /opt/zapret |
|
||||
``` |
|
||||
|
|
||||
Divert sockets are internal type sockets in the BSD kernel. They have no |
|
||||
relation to network addresses or network packet exchange. They are identified |
|
||||
by a port number `1..65535`. Its like queue number in NFQUEUE. Traffic can be |
|
||||
diverted to a divert socket using firewall rule. If nobody listens on the |
|
||||
specified divert port packets are dropped. Its similar to NFQUEUE without |
|
||||
`--queue-bypass`. |
|
||||
|
|
||||
`ipset/*.sh` scripts work with ipfw lookup tables if ipfw is present. |
|
||||
|
|
||||
ipfw table is analog to linux `ipset`. Unlike ipsets ipfw tables share v4 an v6 |
|
||||
addresses and subnets. |
|
||||
|
|
||||
- If ipfw is absent scripts check LISTS_RELOAD config variable. |
|
||||
- If its present then scripts execute a command from LISTS_RELOAD. |
|
||||
- If LISTS_RELOAD=- scripts do not load tables even if ipfw exists. |
|
||||
|
|
||||
PF can load ip tables from a file. To use this feature with `ipset/*.sh` scripts disable gzip file creation |
|
||||
using `GZIP_LISTS=0` directive in the `/opt/zapret/config` file. |
|
||||
|
|
||||
BSD kernel doesn't implement splice syscall. tpws uses regular recv/send |
|
||||
operations with data copying to user space. Its slower but not critical. |
|
||||
|
|
||||
`tpws` uses nonblocking sockets with linux specific epoll feature. In BSD systems |
|
||||
epoll is emulated by epoll-shim library on top of kqueue. |
|
||||
|
|
||||
`dvtws` uses some programming HACKs, assumptions and knowledge of discovered |
|
||||
bugs and limitations. BSD systems have many limitations, version specific |
|
||||
features and bugs in low level networking, especially for ipv6. Many years have |
|
||||
passed but BSD code still has 15-20 year artificial limiters in the code. `dvtws` |
|
||||
uses additinal divert socket(s) for layer 3 packet injection if raw sockets do |
|
||||
not allow it. It works for the moment but who knows. Such a usage is not very |
|
||||
documented. |
|
||||
|
|
||||
`mdig` and `ip2net` are fully compatible with BSD. |
|
||||
|
|
||||
|
|
||||
## FreeBSD |
|
||||
|
|
||||
Divert sockets require special kernel module `ipdivert`. |
|
||||
Write the following to config files: |
|
||||
|
|
||||
`/boot/loader.conf` (create if absent): |
|
||||
``` |
|
||||
ipdivert_load="YES" |
|
||||
net.inet.ip.fw.default_to_accept=1 |
|
||||
``` |
|
||||
|
|
||||
`/etc/rc.conf`: |
|
||||
``` |
|
||||
firewall_enable="YES" |
|
||||
firewall_script="/etc/rc.firewall.my" |
|
||||
``` |
|
||||
|
|
||||
`/etc/rc.firewall.my`: |
|
||||
``` |
|
||||
ipfw -q -f flush |
|
||||
``` |
|
||||
|
|
||||
Later you will add ipfw commands to `/etc/rc.firewall.my` to be reapplied after reboot. |
|
||||
You can also run zapret daemons from there. Start them with `--daemon` options, for example |
|
||||
``` |
|
||||
pkill ^dvtws$ |
|
||||
/opt/zapret/nfq/dvtws --port=989 --daemon --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
To restart firewall and daemons run : `/etc/rc.d/ipfw restart` |
|
||||
|
|
||||
Assume `LAN="em1"`, `WAN="em0"`. |
|
||||
|
|
||||
`tpws` transparent mode quick start. |
|
||||
|
|
||||
For all traffic: |
|
||||
``` |
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 fwd 127.0.0.1,988 tcp from me to any 80,443 proto ip4 xmit em0 not uid daemon |
|
||||
ipfw add 100 fwd ::1,988 tcp from me to any 80,443 proto ip6 xmit em0 not uid daemon |
|
||||
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1 |
|
||||
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1 |
|
||||
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 |
|
||||
``` |
|
||||
|
|
||||
Process only table zapret with the exception of table nozapret: |
|
||||
``` |
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443 |
|
||||
ipfw add 100 fwd 127.0.0.1,988 tcp from me to table\(zapret\) 80,443 proto ip4 xmit em0 not uid daemon |
|
||||
ipfw add 100 fwd ::1,988 tcp from me to table\(zapret\) 80,443 proto ip6 xmit em0 not uid daemon |
|
||||
ipfw add 100 allow tcp from any to table\(nozapret\) 80,443 recv em1 |
|
||||
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1 |
|
||||
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1 |
|
||||
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 |
|
||||
``` |
|
||||
|
|
||||
Tables zapret, nozapret, ipban are created by `ipset/*.sh` scripts the same way as in Linux. |
|
||||
Its a good idea to update tables periodically: |
|
||||
``` |
|
||||
crontab -e |
|
||||
``` |
|
||||
|
|
||||
Write the line: |
|
||||
``` |
|
||||
0 12 */2 * * /opt/zapret/ipset/get_config.sh |
|
||||
``` |
|
||||
|
|
||||
When using `ipfw`, `tpws` does not require special permissions for transparent |
|
||||
mode. However without root its not possible to bind to ports less than 1024 and |
|
||||
change UID/GID. Without changing UID tpws will run into recursive loop, and |
|
||||
that's why its necessary to write ipfw rules with the right UID. Redirecting to |
|
||||
ports greater than or equal to 1024 is dangerous. If tpws is not running any |
|
||||
unprivileged process can listen to that port and intercept traffic. |
|
||||
|
|
||||
### `dvtws` quick start |
|
||||
|
|
||||
For all traffic: |
|
||||
``` |
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted not sockarg xmit em0 |
|
||||
# required for autottl mode only |
|
||||
ipfw add 100 divert 989 tcp from any 80,443 to any tcpflags syn,ack in not diverted not sockarg recv em0 |
|
||||
/opt/zapret/nfq/dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
Process only table zapret with the exception of table nozapret: |
|
||||
``` |
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443 |
|
||||
ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted not sockarg xmit em0 |
|
||||
# required for autottl mode only |
|
||||
ipfw add 100 divert 989 tcp from table\(zapret\) 80,443 to any tcpflags syn,ack in not diverted not sockarg recv em0 |
|
||||
/opt/zapret/nfq/dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
Reinjection loop avoidance. FreeBSD artificially ignores sockarg for ipv6 in |
|
||||
the kernel. This limitation is coming from the ipv6 early age. Code is still in |
|
||||
"testing" state. 10-20 years. Everybody forgot about it. `dvtws` sends ipv6 |
|
||||
forged frames using another divert socket (HACK). they can be filtered out |
|
||||
using 'diverted'. ipv4 frames are filtered using 'sockarg'. |
|
||||
|
|
||||
### PF in FreeBSD |
|
||||
|
|
||||
The setup is similar to OpenBSD, but there are important nuances. |
|
||||
1. PF support is disabled by default in FreeBSD. Use parameter `--enable-pf`. |
|
||||
2. It's not possible to redirect to `::1`. Need to redirect to the link-local |
|
||||
address of the incoming interface. Look for fe80:... address in ifconfig and |
|
||||
use it for redirection target. |
|
||||
3. pf.conf syntax is a bit different from OpenBSD. |
|
||||
4. How to set maximum table size : sysctl net.pf.request_maxcount=2000000 |
|
||||
5. `divert-to` is broken. Loop avoidance scheme does not work. |
|
||||
This makes `dvtws` unusable with pf. |
|
||||
Someone posted kernel patch but 14-RELEASE is still broken. |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
rdr pass on em1 inet6 proto tcp to port {80,443} -> fe80::31c:29ff:dee2:1c4d port 988 |
|
||||
rdr pass on em1 inet proto tcp to port {80,443} -> 127.0.0.1 port 988 |
|
||||
``` |
|
||||
|
|
||||
Then: |
|
||||
``` |
|
||||
/opt/zapret/tpws/tpws --port=988 --enable-pf --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force |
|
||||
``` |
|
||||
|
|
||||
Its not clear how to do rdr-to outgoing traffic. I could not make route-to |
|
||||
scheme work. |
|
||||
|
|
||||
|
|
||||
### `pfsense` |
|
||||
|
|
||||
`pfsense` is based on FreeBSD. Binaries from `binaries/freebsd-x64` are |
|
||||
compiled in FreeBSD 11 and should work. Use `install_bin.sh`. pfsense uses pf |
|
||||
firewall which does not support divert. Fortunately ipfw and ipdivert modules |
|
||||
are present and can be kldload-ed. In older versions it's also necessary to |
|
||||
change firewall order using sysctl commands. In newer versions those sysctl |
|
||||
parameters are absent but the system behaves as required without them. |
|
||||
Sometimes pf may limit `dvtws` abilities. It scrubs ip fragments disabling `dvtws` |
|
||||
ipfrag2 desync mode. |
|
||||
|
|
||||
There's autostart script example in `init.d/pfsense`. It should be placed to |
|
||||
`/usr/local/etc/rc.d` and edited. Write your ipfw rules and daemon start |
|
||||
commands. |
|
||||
curl is present by default. You can use it to download `tar.gz` release directly from github. |
|
||||
Or you can copy files using sftp. |
|
||||
|
|
||||
Copy zip with zapret files to `/opt` and unpack there as it's done in other |
|
||||
systems. In this case run `dvtws` as `/opt/zapret/nfq/dvtws`. Or just copy |
|
||||
`dvtws` to `/usr/local/sbin`. As you wish. `ipset` scripts are working, cron is |
|
||||
present. It's possible to renew lists. |
|
||||
|
|
||||
If you dont like poverty of default repos its possible to enable FreeBSD repo. |
|
||||
Change `no` to `yes` in `/usr/local/etc/pkg/repos/FreeBSD.conf` and `/usr/local/etc/pkg/repos/pfSense.conf`. |
|
||||
|
|
||||
`/usr/local/etc/rc.d/zapret.sh` (chmod 755) |
|
||||
``` |
|
||||
#!/bin/sh |
|
||||
|
|
||||
kldload ipfw |
|
||||
kldload ipdivert |
|
||||
|
|
||||
# for older pfsense versions. newer do not have these sysctls |
|
||||
sysctl net.inet.ip.pfil.outbound=ipfw,pf |
|
||||
sysctl net.inet.ip.pfil.inbound=ipfw,pf |
|
||||
sysctl net.inet6.ip6.pfil.outbound=ipfw,pf |
|
||||
sysctl net.inet6.ip6.pfil.inbound=ipfw,pf |
|
||||
|
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted not sockarg xmit em0 |
|
||||
pkill ^dvtws$ |
|
||||
dvtws --daemon --port 989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
|
|
||||
# required for newer pfsense versions (2.6.0 tested) to return ipfw to functional state |
|
||||
pfctl -d ; pfctl -e |
|
||||
``` |
|
||||
|
|
||||
I could not make tpws work from ipfw. Looks like there's some conflict between |
|
||||
two firewalls. Only PF redirection works. PF does not allow to freely add and |
|
||||
delete rules. Only anchors can be reloaded. To make an anchor work it must be |
|
||||
referred from the main ruleset. But its managed by pfsense scripts. |
|
||||
|
|
||||
One possible solution would be to modify `/etc/inc/filter.inc` as follows: |
|
||||
``` |
|
||||
................. |
|
||||
/* MOD */ |
|
||||
$natrules .= "# ZAPRET redirection\n"; |
|
||||
$natrules .= "rdr-anchor \"zapret\"\n"; |
|
||||
|
|
||||
$natrules .= "# TFTP proxy\n"; |
|
||||
$natrules .= "rdr-anchor \"tftp-proxy/*\"\n"; |
|
||||
................. |
|
||||
``` |
|
||||
|
|
||||
Write the anchor code to `/etc/zapret.anchor`: |
|
||||
``` |
|
||||
rdr pass on em1 inet proto tcp to port {80,443} -> 127.0.0.1 port 988 |
|
||||
rdr pass on em1 inet6 proto tcp to port {80,443} -> fe80::20c:29ff:5ae3:4821 port 988 |
|
||||
``` |
|
||||
Replace `fe80::20c:29ff:5ae3:4821` with your link local address of the LAN |
|
||||
interface or remove the line if ipv6 is not needed. |
|
||||
|
|
||||
Autostart `/usr/local/etc/rc.d/zapret.sh`: |
|
||||
``` |
|
||||
pfctl -a zapret -f /etc/zapret.anchor |
|
||||
pkill ^tpws$ |
|
||||
tpws --daemon --port=988 --enable-pf --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force --split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
After reboot check that anchor is created and referred from the main ruleset: |
|
||||
``` |
|
||||
[root@pfSense /]# pfctl -s nat |
|
||||
no nat proto carp all |
|
||||
nat-anchor "natearly/*" all |
|
||||
nat-anchor "natrules/*" all |
|
||||
................... |
|
||||
no rdr proto carp all |
|
||||
rdr-anchor "zapret" all |
|
||||
rdr-anchor "tftp-proxy/*" all |
|
||||
rdr-anchor "miniupnpd" all |
|
||||
[root@pfSense /]# pfctl -s nat -a zapret |
|
||||
rdr pass on em1 inet proto tcp from any to any port = http -> 127.0.0.1 port 988 |
|
||||
rdr pass on em1 inet proto tcp from any to any port = https -> 127.0.0.1 port 988 |
|
||||
rdr pass on em1 inet6 proto tcp from any to any port = http -> fe80::20c:29ff:5ae3:4821 port 988 |
|
||||
rdr pass on em1 inet6 proto tcp from any to any port = https -> fe80::20c:29ff:5ae3:4821 port 988 |
|
||||
``` |
|
||||
|
|
||||
Also there's a way to add redirect in the pfsense UI and start `tpws` from cron using `@reboot` prefix. |
|
||||
This way avoids modification of pfsense code. |
|
||||
|
|
||||
## OpenBSD |
|
||||
|
|
||||
In OpenBSD default `tpws` bind is ipv6 only. To bind to ipv4 specify |
|
||||
`--bind-addr=0.0.0.0`. |
|
||||
|
|
||||
Use `--bind-addr=0.0.0.0 --bind-addr=::` to achieve the same default bind as in |
|
||||
others OSes. |
|
||||
|
|
||||
`tpws` for forwarded traffic only (OLDER OS versions): |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
pass in quick on em1 inet proto tcp to port {80,443} rdr-to 127.0.0.1 port 988 |
|
||||
pass in quick on em1 inet6 proto tcp to port {80,443} rdr-to ::1 port 988 |
|
||||
``` |
|
||||
|
|
||||
Then: |
|
||||
``` |
|
||||
pfctl -f /etc/pf.conf |
|
||||
tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 --enable-pf |
|
||||
``` |
|
||||
|
|
||||
Its not clear how to do rdr-to outgoing traffic. I could not make route-to |
|
||||
scheme work. rdr-to support is done using /dev/pf, that's why transparent mode |
|
||||
requires root. |
|
||||
|
|
||||
`tpws` for forwarded traffic only (NEWER OS versions): |
|
||||
|
|
||||
``` |
|
||||
pass on em1 inet proto tcp to port {80,443} divert-to 127.0.0.1 port 989 |
|
||||
pass on em1 inet6 proto tcp to port {80,443} divert-to ::1 port 989 |
|
||||
``` |
|
||||
|
|
||||
Then: |
|
||||
``` |
|
||||
pfctl -f /etc/pf.conf |
|
||||
tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 |
|
||||
``` |
|
||||
|
|
||||
tpws must be bound exactly to diverted IPs, not `0.0.0.0` or `::`. |
|
||||
|
|
||||
It's also not clear how to divert connections from local system. |
|
||||
|
|
||||
|
|
||||
`dvtws` for all traffic: |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
pass in quick on em0 proto tcp from port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 proto tcp from port {80,443} no state |
|
||||
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 |
|
||||
``` |
|
||||
|
|
||||
Then: |
|
||||
``` |
|
||||
pfctl -f /etc/pf.conf |
|
||||
./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
`dwtws` only for table zapret with the exception of table nozapret : |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
set limit table-entries 2000000 |
|
||||
table <zapret> file "/opt/zapret/ipset/zapret-ip.txt" |
|
||||
table <zapret-user> file "/opt/zapret/ipset/zapret-ip-user.txt" |
|
||||
table <nozapret> file "/opt/zapret/ipset/zapret-ip-exclude.txt" |
|
||||
pass out quick on em0 inet proto tcp to <nozapret> port {80,443} |
|
||||
pass in quick on em0 inet proto tcp from <zapret> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret> port {80,443} no state |
|
||||
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} no state |
|
||||
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989 no state |
|
||||
table <zapret6> file "/opt/zapret/ipset/zapret-ip6.txt" |
|
||||
table <zapret6-user> file "/opt/zapret/ipset/zapret-ip-user6.txt" |
|
||||
table <nozapret6> file "/opt/zapret/ipset/zapret-ip-exclude6.txt" |
|
||||
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443} |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6> port {80,443} no state |
|
||||
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} no state |
|
||||
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989 no state |
|
||||
``` |
|
||||
|
|
||||
Then: |
|
||||
``` |
|
||||
pfctl -f /etc/pf.conf |
|
||||
./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
divert-packet automatically adds the reverse rule. By default also incoming |
|
||||
traffic will be passwed to `dvtws`. This is highly undesired because it is waste |
|
||||
of cpu resources and speed limiter. The trick with "no state" and "in" rules |
|
||||
allows to bypass auto reverse rule. |
|
||||
|
|
||||
`dvtws` in OpenBSD sends all fakes through a divert socket because raw sockets |
|
||||
have critical artificial limitations. Looks like pf automatically prevent |
|
||||
reinsertion of diverted frames. Loop problem does not exist. |
|
||||
|
|
||||
OpenBSD forcibly recomputes tcp checksum after divert. Thats why most likely |
|
||||
dpi-desync-fooling=badsum will not work. `dvtws` will warn if you specify this |
|
||||
parameter. |
|
||||
|
|
||||
`ipset` scripts do not reload PF by default. To enable reload specify command in |
|
||||
`/opt/zapret/config`: |
|
||||
``` |
|
||||
LISTS_RELOAD="pfctl -f /etc/pf.conf" |
|
||||
``` |
|
||||
|
|
||||
Newer `pfctl` versions can reload tables only: |
|
||||
``` |
|
||||
pfctl -Tl -f /etc/pf.conf |
|
||||
``` |
|
||||
|
|
||||
But OpenBSD 6.8 `pfctl` is old enough and does not support that. Newer FreeBSD do. |
|
||||
|
|
||||
Don't forget to disable gzip compression: |
|
||||
``` |
|
||||
GZIP_LISTS=0 |
|
||||
``` |
|
||||
|
|
||||
If some list files do not exist and have references in pf.conf it leads to |
|
||||
error. You need to exclude those tables from pf.conf and referencing them |
|
||||
rules. After configuration is done you can put `ipset` script: |
|
||||
``` |
|
||||
crontab -e |
|
||||
``` |
|
||||
|
|
||||
Then write the line: |
|
||||
``` |
|
||||
0 12 */2 * * /opt/zapret/ipset/get_config.sh |
|
||||
``` |
|
||||
|
|
||||
## MacOS |
|
||||
|
|
||||
Initially, the kernel of this OS was based on BSD. That's why it is still BSD |
|
||||
but a lot was modified by Apple. As usual a mass commercial project priorities |
|
||||
differ from their free counterparts. Apple guys do what they want. |
|
||||
|
|
||||
MacOS used to have ipfw but it was removed later and replaced by PF. It looks |
|
||||
like divert sockets are internally replaced with raw. Its possible to request a |
|
||||
divert socket but it behaves exactly as raw socket with all its BSD inherited + |
|
||||
apple specific bugs and feature. The fact is that divert-packet in |
|
||||
`/etc/pf.conf` does not work. pfctl binary does not contain the word `divert`. |
|
||||
|
|
||||
`dvtws` does compile but is useless. |
|
||||
|
|
||||
After some efforts `tpws` works. Apple has removed some important stuff from |
|
||||
their newer SDKs (DIOCNATLOOK) making them undocumented and unsupported. |
|
||||
|
|
||||
With important definitions copied from an older SDK it was possible to make |
|
||||
transparent mode working again. But this is not guaranteed to work in the |
|
||||
future versions. |
|
||||
|
|
||||
Another MacOS unique feature is root requirement while polling `/dev/pf`. |
|
||||
|
|
||||
By default tpws drops root. Its necessary to specify `--user=root` to stay with |
|
||||
root. |
|
||||
|
|
||||
In other aspects PF behaves very similar to FreeBSD and shares the same pf.conf |
|
||||
syntax. |
|
||||
|
|
||||
In MacOS redirection works both for passthrough and outgoing traffic. Outgoing |
|
||||
redirection requires route-to rule. Because tpws is forced to run as root to |
|
||||
avoid loop its necessary to exempt root from the redirection. That's why DPI |
|
||||
bypass will not work for local requests from root. |
|
||||
|
|
||||
If you do ipv6 routing you have to get rid of "secured" ipv6 address |
|
||||
assignment. |
|
||||
|
|
||||
"secured" addresses are designed to be permanent and not related to the MAC |
|
||||
address. |
|
||||
|
|
||||
And they really are. Except for link-locals. |
|
||||
|
|
||||
If you just reboot the system link-locals will not change. But next day they |
|
||||
will change. |
|
||||
|
|
||||
Not necessary to wait so long. Just change the system time to tomorrow and reboot. |
|
||||
Link-locals will change (at least they change in vmware guest). Looks like its a kernel bug. |
|
||||
Link locals should not change. Its useless and can be harmful. Cant use LL as a gateway. |
|
||||
|
|
||||
The easiest solution is to disable "secured" addresses. |
|
||||
|
|
||||
Outgoing connections prefer randomly generated temporary addressesas like in other systems. |
|
||||
|
|
||||
Put the string `net.inet6.send.opmode=0` to `/etc/sysctl.conf`. If not present |
|
||||
- create it. |
|
||||
|
|
||||
Then reboot the system. |
|
||||
|
|
||||
If you dont like this solution you can assign an additional static ipv6 address |
|
||||
from `fc00::/7` range with `/128` prefix to your LAN interface and use it as |
|
||||
the gateway address. |
|
||||
|
|
||||
`tpws` transparent mode only for outgoing connections. |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
rdr pass on lo0 inet proto tcp from !127.0.0.0/8 to any port {80,443} -> 127.0.0.1 port 988 |
|
||||
rdr pass on lo0 inet6 proto tcp from !::1 to any port {80,443} -> fe80::1 port 988 |
|
||||
pass out route-to (lo0 127.0.0.1) inet proto tcp from any to any port {80,443} user { >root } |
|
||||
pass out route-to (lo0 fe80::1) inet6 proto tcp from any to any port {80,443} user { >root } |
|
||||
``` |
|
||||
|
|
||||
Then: |
|
||||
``` |
|
||||
pfctl -ef /etc/pf.conf |
|
||||
/opt/zapret/tpws/tpws --user=root --port=988 --bind-addr=127.0.0.1 --bind-iface6=lo0 --bind-linklocal=force |
|
||||
``` |
|
||||
|
|
||||
`tpws` transparent mode for both passthrough and outgoing connections. en1 - LAN. |
|
||||
|
|
||||
``` |
|
||||
ifconfig en1 | grep fe80 |
|
||||
inet6 fe80::bbbb:bbbb:bbbb:bbbb%en1 prefixlen 64 scopeid 0x8 |
|
||||
``` |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
rdr pass on en1 inet proto tcp from any to any port {80,443} -> 127.0.0.1 port 988 |
|
||||
rdr pass on en1 inet6 proto tcp from any to any port {80,443} -> fe80::bbbb:bbbb:bbbb:bbbb port 988 |
|
||||
rdr pass on lo0 inet proto tcp from !127.0.0.0/8 to any port {80,443} -> 127.0.0.1 port 988 |
|
||||
rdr pass on lo0 inet6 proto tcp from !::1 to any port {80,443} -> fe80::1 port 988 |
|
||||
pass out route-to (lo0 127.0.0.1) inet proto tcp from any to any port {80,443} user { >root } |
|
||||
pass out route-to (lo0 fe80::1) inet6 proto tcp from any to any port {80,443} user { >root } |
|
||||
``` |
|
||||
|
|
||||
Then: |
|
||||
``` |
|
||||
pfctl -ef /etc/pf.conf |
|
||||
/opt/zapret/tpws/tpws --user=root --port=988 --bind-addr=127.0.0.1 --bind-iface6=lo0 --bind-linklocal=force --bind-iface6=en1 --bind-linklocal=force |
|
||||
``` |
|
||||
|
|
||||
Build from source : `make -C /opt/zapret mac` |
|
||||
|
|
||||
`ipset/*.sh` scripts work. |
|
||||
|
|
||||
|
|
||||
### MacOS easy install |
|
||||
|
|
||||
`install_easy.sh` supports MacOS |
|
||||
|
|
||||
Shipped precompiled binaries are built for 64-bit MacOS with |
|
||||
`-mmacosx-version-min=10.8` option. They should run on all supported MacOS |
|
||||
versions. If no - its easy to build your own. Running `make` automatically |
|
||||
installs developer tools. |
|
||||
|
|
||||
**WARNING**: |
|
||||
**Internet sharing is not supported!** |
|
||||
|
|
||||
Routing is supported but only manually configured through PF. If you enable |
|
||||
internet sharing tpws stops functioning. When you disable internet sharing you |
|
||||
may lose web site access. |
|
||||
|
|
||||
To fix: |
|
||||
``` |
|
||||
pfctl -f /etc/pf.conf |
|
||||
``` |
|
||||
|
|
||||
If you need internet sharing use `tpws` socks mode. |
|
||||
|
|
||||
`launchd` is used for autostart (`/Library/LaunchDaemons/zapret.plist`) |
|
||||
|
|
||||
Control script: `/opt/zapret/init.d/macos/zapret` |
|
||||
|
|
||||
The following commands fork with both tpws and firewall (if `INIT_APPLY_FW=1` in config) |
|
||||
``` |
|
||||
/opt/zapret/init.d/macos/zapret start |
|
||||
/opt/zapret/init.d/macos/zapret stop |
|
||||
/opt/zapret/init.d/macos/zapret restart |
|
||||
``` |
|
||||
|
|
||||
Work with `tpws` only: |
|
||||
``` |
|
||||
/opt/zapret/init.d/macos/zapret start-daemons |
|
||||
/opt/zapret/init.d/macos/zapret stop-daemons |
|
||||
/opt/zapret/init.d/macos/zapret restart-daemons |
|
||||
``` |
|
||||
|
|
||||
Work with PF only: |
|
||||
``` |
|
||||
/opt/zapret/init.d/macos/zapret start-fw |
|
||||
/opt/zapret/init.d/macos/zapret stop-fw |
|
||||
/opt/zapret/init.d/macos/zapret restart-fw |
|
||||
``` |
|
||||
|
|
||||
Reloading PF tables: |
|
||||
``` |
|
||||
/opt/zapret/init.d/macos/zapret reload-fw-tables |
|
||||
``` |
|
||||
|
|
||||
Installer configures `LISTS_RELOAD` in the config so `ipset *.sh` scripts |
|
||||
automatically reload PF tables. Installer creates cron job for `ipset |
|
||||
/get_config.sh`, as in OpenWRT. |
|
||||
|
|
||||
start-fw script automatically patches `/etc/pf.conf` inserting there `zapret` |
|
||||
anchors. Auto patching requires pf.conf with apple anchors preserved. If your |
|
||||
`pf.conf` is highly customized and patching fails you will see the warning. Do |
|
||||
not ignore it. |
|
||||
|
|
||||
In that case you need to manually insert "zapret" anchors to your `pf.conf` |
|
||||
(keeping the right rule type ordering): |
|
||||
``` |
|
||||
rdr-anchor "zapret" |
|
||||
anchor "zapret" |
|
||||
unistall_easy.sh unpatches pf.conf |
|
||||
``` |
|
||||
start-fw creates 3 anchor files in `/etc/pf.anchors` : |
|
||||
zapret,zapret-v4,zapret-v6. |
|
||||
|
|
||||
- Last 2 are referenced by anchor `zapret`. |
|
||||
- Tables `nozapret`,`nozapret6` belong to anchor `zapret`. |
|
||||
- Tables `zapret`,`zapret-user` belong to anchor `zapret-v4`. |
|
||||
- Tables `zapret6`,`apret6-user` belong to anchor `zapret-v6`. |
|
||||
|
|
||||
If an ip version is disabled then corresponding anchor is empty and is not |
|
||||
referenced from the anchor `zapret`. Tables are only created for existing list |
|
||||
files in the `ipset` directory. |
|
||||
@ -1,750 +0,0 @@ |
|||||
# Настройка BSD-подобных систем |
|
||||
|
|
||||
* [Поддерживаемые версии](#поддерживаемые-версии) |
|
||||
* [Особенности BSD систем](#особенности-bsd-систем) |
|
||||
* [Отсутствие nfqueue](#отсутствие-nfqueue) |
|
||||
* [Типы Firewall](#типы-firewall) |
|
||||
* [Сборка](#сборка) |
|
||||
* [Divert сокеты](#divert-сокеты) |
|
||||
* [Lookup Tables](#lookup-tables) |
|
||||
* [Загрузка ip таблиц из файла](#загрузка-ip-таблиц-из-файла) |
|
||||
* [Отсутствие splice](#отсутствие-splice) |
|
||||
* [mdig и ip2net](#mdig-и-ip2net) |
|
||||
* [FreeBSD](#freebsd) |
|
||||
* [Подгрузка ipdivert](#подгрузка-ipdivert) |
|
||||
* [Авто-восстановление правил ipfw и работа в фоне](#авто-восстановление-правил-ipfw-и-работа-в-фоне) |
|
||||
* [tpws в прозрачном режиме](#tpws-в-прозрачном-режиме) |
|
||||
* [Запуск dvtws](#запуск-dvtws) |
|
||||
* [PF в FreeBSD](#pf-в-freebsd) |
|
||||
* [pfsense](#pfsense) |
|
||||
* [OpenBSD](#openbsd) |
|
||||
* [tpws bind на ipv4](#tpws-bind-на-ipv4) |
|
||||
* [tpws для проходящего трафика (старые системы)](#tpws-для-проходящего-трафика-старая-схема-не-работает-в-новых-версиях)) |
|
||||
* [tpws для проходящего трафика (новые системы)](#tpws-для-проходящего-трафика-новые-системы)) |
|
||||
* [Запуск dvtws](#запуск-dvtws) |
|
||||
* [Проблемы с badsum](#проблемы-с-badsum) |
|
||||
* [Особенность отправки fake пакетов](#особенность-отправки-fake-пакетов) |
|
||||
* [Перезагрузка PF таблиц](#перезагрузка-pf-таблиц) |
|
||||
* [MacOS](#macos) |
|
||||
* [Введение](#введение) |
|
||||
* [dvtws бесполезен](#dvtws-бесполезен) |
|
||||
* [tpws](#tpws) |
|
||||
* [Проблема link-local адреса](#проблема-link-local-адреса) |
|
||||
* [Сборка](#сборка) |
|
||||
* [Простая установка](#простая-установка) |
|
||||
* [Вариант Custom](#вариант-custom) |
|
||||
|
|
||||
|
|
||||
## Поддерживаемые версии |
|
||||
**FreeBSD** 11.x+ , **OpenBSD** 6.x+, частично **MacOS Sierra** + |
|
||||
|
|
||||
> [!CAUTION] |
|
||||
> На более старых может собираться, может не собираться, может работать или не |
|
||||
> работать. На **FreeBSD** 10 собирается и работает `dvtws`. С `tpws` есть |
|
||||
> проблемы из-за слишком старой версии компилятора clang. Вероятно, будет |
|
||||
> работать, если обновить компилятор. Возможна прикрутка к последним версиям |
|
||||
> pfsense без веб интерфейса в ручном режиме через консоль. |
|
||||
|
|
||||
|
|
||||
## Особенности BSD систем |
|
||||
|
|
||||
### Отсутствие nfqueue |
|
||||
В **BSD** нет `nfqueue`. Похожий механизм - divert sockets. Из каталога |
|
||||
[`nfq/`](../nfq/) под **BSD** собирается `dvtws` вместо `nfqws`. Он разделяет с |
|
||||
`nfqws` большую часть кода и почти совпадает по параметрам командной строки. |
|
||||
|
|
||||
### Типы Firewall |
|
||||
**FreeBSD** содержит 3 фаервола : **IPFilter**, **ipfw** и **Packet Filter (PF |
|
||||
в дальнейшем)**. **OpenBSD** содержит только **PF**. |
|
||||
|
|
||||
### Сборка |
|
||||
Под **FreeBSD** `tpws` и `dvtws` собираются через `make`. |
|
||||
|
|
||||
Под **OpenBSD**: |
|
||||
```sh |
|
||||
make bsd |
|
||||
``` |
|
||||
|
|
||||
Под **MacOS**: |
|
||||
```sh |
|
||||
make mac |
|
||||
``` |
|
||||
|
|
||||
**FreeBSD** make распознает BSDmakefile, **OpenBSD** и **MacOS** - нет. Поэтому |
|
||||
там используется отдельный target в Makefile. Сборка всех исходников: |
|
||||
```sh |
|
||||
make -C /opt/zapret |
|
||||
``` |
|
||||
|
|
||||
### Divert сокеты |
|
||||
Divert сокет это внутренний тип сокета ядра **BSD**. Он не привязывается ни к |
|
||||
какому сетевому адресу, не участвует в обмене данными через сеть и |
|
||||
идентифицируется по номеру порта `1..65535`. Аналогия с номером очереди |
|
||||
`NFQUEUE`. На divert сокеты заворачивается трафик посредством правил ipfw или |
|
||||
PF. Если в фаерволе есть правило divert, но на divert порту никто не слушает, |
|
||||
то пакеты дропаются. Это поведение аналогично правилам `NFQUEUE` без параметра |
|
||||
`--queue-bypass`. На **FreeBSD** divert сокеты могут быть только ipv4, хотя на |
|
||||
них принимаются и ipv4, и ipv6 фреймы. На **OpenBSD** divert сокеты создаются |
|
||||
отдельно для ipv4 и ipv6 и работают только с одной версией `ip` каждый. На |
|
||||
**MacOS** похоже, что divert сокеты из ядра вырезаны. См подробнее раздел про |
|
||||
**MacOS**. Отсылка в divert сокет работает аналогично отсылке через raw socket |
|
||||
на linux. Передается полностью IP фрейм, начиная с ip загловка. Эти особенности |
|
||||
учитываются в `dvtws`. |
|
||||
|
|
||||
### Lookup Tables |
|
||||
Скрипты [`ipset/*.sh`](../ipset/) при наличии ipfw работают с ipfw lookup |
|
||||
tables. Это прямой аналог ipset. lookup tables не разделены на v4 и v6. Они |
|
||||
могут содержать v4 и v6 адреса и подсети одновременно. Если ipfw отсутствует, |
|
||||
то действие зависит от переменной `LISTS_RELOAD` в config. Если она задана, то |
|
||||
выполняется команда из `LISTS_RELOAD`. В противном случае не делается ничего. |
|
||||
Если `LISTS_RELOAD=-`, то заполнение таблиц отключается даже при наличии ipfw. |
|
||||
|
|
||||
### Загрузка ip таблиц из файла |
|
||||
PF может загружать ip таблицы из файла. Чтобы использовать эту возможность |
|
||||
следует отключить сжатие gzip для листов через параметр файла config: |
|
||||
`GZIP_LISTS=0`. |
|
||||
|
|
||||
### Отсутствие splice |
|
||||
**BSD** не содержит системного вызова splice. `tpws` работает через переброску |
|
||||
данных в user mode в оба конца. Это медленнее, но не критически. Управление |
|
||||
асинхронными сокетами в `tpws` основано на linux-specific механизме epoll. В |
|
||||
**BSD** для его эмуляции используется epoll-shim - прослойка для эмуляции epoll |
|
||||
на базе kqueue. |
|
||||
|
|
||||
### mdig и ip2net |
|
||||
mdig и ip2net полностью работоспособны в **BSD**. В них нет ничего |
|
||||
системо-зависимого. |
|
||||
|
|
||||
|
|
||||
## FreeBSD |
|
||||
|
|
||||
### Подгрузка ipdivert |
|
||||
Divert сокеты требуют специального модуля ядра - `ipdivert`. |
|
||||
|
|
||||
- Поместите следующие строки в `/boot/loader.conf` (создать, если отсутствует): |
|
||||
``` |
|
||||
ipdivert_load="YES" |
|
||||
net.inet.ip.fw.default_to_accept=1 |
|
||||
``` |
|
||||
|
|
||||
`/etc/rc.conf`: |
|
||||
``` |
|
||||
firewall_enable="YES" |
|
||||
firewall_script="/etc/rc.firewall.my" |
|
||||
``` |
|
||||
|
|
||||
`/etc/rc.firewall.my`: |
|
||||
```sh |
|
||||
$ ipfw -q -f flush |
|
||||
``` |
|
||||
|
|
||||
### Авто-восстановление правил ipfw и работа в фоне |
|
||||
В `/etc/rc.firewall.my` можно дописывать правила ipfw, чтобы они |
|
||||
восстанавливались после перезагрузки. Оттуда же можно запускать и демоны |
|
||||
zapret, добавив в параметры `--daemon`. Например так: |
|
||||
```sh |
|
||||
$ pkill ^dvtws$ |
|
||||
$ /opt/zapret/nfq/dvtws --port=989 --daemon --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
Для перезапуска фаервола и демонов достаточно будет сделать: |
|
||||
```sh |
|
||||
$ /etc/rc.d/ipfw restart |
|
||||
``` |
|
||||
|
|
||||
### tpws в прозрачном режиме |
|
||||
Краткая инструкция по запуску `tpws` в прозрачном режиме. |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Предполагается, что интерфейс LAN называется `em1`, WAN - `em0`. |
|
||||
|
|
||||
#### Весь трафик |
|
||||
```sh |
|
||||
$ ipfw delete 100 |
|
||||
$ ipfw add 100 fwd 127.0.0.1,988 tcp from me to any 80,443 proto ip4 xmit em0 not uid daemon |
|
||||
$ ipfw add 100 fwd ::1,988 tcp from me to any 80,443 proto ip6 xmit em0 not uid daemon |
|
||||
$ ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1 |
|
||||
$ ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1 |
|
||||
$ /opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 |
|
||||
``` |
|
||||
|
|
||||
#### Трафик только на таблицу zapret, за исключением таблицы nozapret |
|
||||
|
|
||||
```sh |
|
||||
$ ipfw delete 100 |
|
||||
$ ipfw add 100 allow tcp from me to table\(nozapret\) 80,443 |
|
||||
$ ipfw add 100 fwd 127.0.0.1,988 tcp from me to table\(zapret\) 80,443 proto ip4 xmit em0 not uid daemon |
|
||||
$ ipfw add 100 fwd ::1,988 tcp from me to table\(zapret\) 80,443 proto ip6 xmit em0 not uid daemon |
|
||||
$ ipfw add 100 allow tcp from any to table\(nozapret\) 80,443 recv em1 |
|
||||
$ ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1 |
|
||||
$ ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1 |
|
||||
$ /opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 |
|
||||
``` |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Таблицы zapret, nozapret, ipban создаются скриптами из ipset по аналогии с |
|
||||
> Linux. Обновление скриптов можно забить в cron под root: |
|
||||
> ```sh |
|
||||
> $ crontab -e |
|
||||
> ``` |
|
||||
> |
|
||||
> ``` |
|
||||
> <...> |
|
||||
> 0 12 */2 * * /opt/zapret/ipset/get_config.sh |
|
||||
> ``` |
|
||||
|
|
||||
> [!CAUTION] |
|
||||
> При использовании ipfw `tpws` не требует повышенных привилегий для реализации |
|
||||
> прозрачного режима. Однако, без рута невозможен bind на порты `< 1024` и |
|
||||
> смена UID/GID. Без смены UID будет рекурсия, поэтому правила ipfw нужно |
|
||||
> создавать с учетом UID, под которым работает `tpws`. Переадресация на порты |
|
||||
> `>= 1024` может создать угрозу перехвата трафика непривилегированным |
|
||||
> процессом, если вдруг `tpws` не запущен. |
|
||||
|
|
||||
|
|
||||
### Запуск dvtws |
|
||||
|
|
||||
#### Весь трафик |
|
||||
```sh |
|
||||
$ ipfw delete 100 |
|
||||
$ ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0 |
|
||||
# required for autottl mode only |
|
||||
$ ipfw add 100 divert 989 tcp from any 80,443 to any tcpflags syn,ack in not diverted recv em0 |
|
||||
$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
#### Трафик только на таблицу zapret, за исключением таблицы nozapret |
|
||||
|
|
||||
```sh |
|
||||
$ ipfw delete 100 |
|
||||
$ ipfw add 100 allow tcp from me to table\(nozapret\) 80,443 |
|
||||
$ ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted not sockarg xmit em0 |
|
||||
# required for autottl mode only |
|
||||
$ ipfw add 100 divert 989 tcp from table\(zapret\) 80,443 to any tcpflags syn,ack in not diverted not sockarg recv em0 |
|
||||
$ /opt/zapret/nfq/dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
|
|
||||
### PF в FreeBSD |
|
||||
Настройка аналогична **OpenBSD**, но есть важные нюансы. |
|
||||
|
|
||||
- В **FreeBSD** поддержка PF в `tpws` отключена по умолчанию. Чтобы ее |
|
||||
включить, нужно использовать параметр `--enable-pf`. |
|
||||
- Нельзя сделать ipv6 rdr на `::1`. Нужно делать на link-local адрес входящего |
|
||||
интерфейса. Смотрите через `ifconfig` адрес `fe80:...` и добавляете в правило. |
|
||||
- Синтаксис `pf.conf` немного отличается. Более новая версия PF. |
|
||||
- Лимит на количество элементов таблиц задается так: |
|
||||
```sh |
|
||||
$ sysctl net.pf.request_maxcount=2000000 |
|
||||
``` |
|
||||
- Сломан divert-to. Он работает, но не работает механизм предотвращения |
|
||||
зацикливаний. Кто-то уже написал патч, но в `14-RELEASE` проблема все еще |
|
||||
есть. Следовательно, на данный момент работа `dvtws` через PF невозможна. |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
rdr pass on em1 inet6 proto tcp to port {80,443} -> fe80::31c:29ff:dee2:1c4d port 988 |
|
||||
rdr pass on em1 inet proto tcp to port {80,443} -> 127.0.0.1 port 988 |
|
||||
``` |
|
||||
|
|
||||
```sh |
|
||||
$ /opt/zapret/tpws/tpws --port=988 --enable-pf --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force |
|
||||
``` |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> В PF не выходит делать rdr-to с той же системы, где работает proxy. |
|
||||
> Вариант с route-to не сохраняет мета информацию. Адрес назначения теряется. |
|
||||
> Поэтому этот вариант годится для squid, берущего адрес из протокола прикладного уровня, но не годится для tpws, полагающегося на метаданные ОС. |
|
||||
> Поддержка rdr-to реализована через `/dev/pf`, поэтому прозрачный режим **требует root**. |
|
||||
|
|
||||
|
|
||||
### pfsense |
|
||||
|
|
||||
#### Описание |
|
||||
pfsense основан на **FreeBSD** и использует фаервол PF, имеющий проблемы с |
|
||||
divert. К счастью, модули ipfw и ipdivert присутствуют в поставке последних |
|
||||
версий pfsense. Их можно подгрузить через `kldload`. |
|
||||
|
|
||||
В некоторых более старых версиях pfsense требуется изменить порядок фаерволов |
|
||||
через `sysctl`, сделав ipfw первым. В более новых эти параметры `sysctl` |
|
||||
отсутствуют, но система работает как надо и без них. В некоторых случаях |
|
||||
фаервол PF может ограничивать возможности `dvtws`, в частности в области |
|
||||
фрагментации ip. |
|
||||
|
|
||||
Присутствуют по умолчанию правила scrub для реассемблинга фрагментов. |
|
||||
|
|
||||
Бинарики из [`binaries/freebsd-x64`](../binaries/freebsd-x64) собраны под |
|
||||
**FreeBSD 11**. Они должны работать и на последующих версиях **FreeBSD**, |
|
||||
включая pfsense. Можно пользоваться `install_bin.sh`. |
|
||||
|
|
||||
#### Автозапуск |
|
||||
Пример скрипта автозапуска лежит в [`init.d/pfsense`](../init.d/pfsense). Его |
|
||||
следует поместить в `/usr/local/etc/rc.d` и отредактировать на предмет правил |
|
||||
ipfw и запуска демонов. Есть встроенный редактор `edit` как более приемлемая |
|
||||
альтернатива `vi`. |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Поскольку `git` отсутствует, копировать файлы удобнее всего через `ssh`. |
|
||||
> `curl` присутствует по умолчанию. Можно скопировать zip с файлами zapret и |
|
||||
> распаковать в `/opt`, как это делается на других системах. Тогда `dvtws` |
|
||||
> нужно запускать как `/opt/zapret/nfq/dvtws`. Либо скопировать только `dvtws` |
|
||||
> в `/usr/local/sbin`. Как вам больше нравится. |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Скрипты ipset работают, крон есть. Можно сделать автообновление листов. |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Если вас напрягает бедность имеющегося репозитория, можно включить |
|
||||
> репозиторий от **FreeBSD**, который по умолчанию выключен. |
|
||||
> |
|
||||
> Поменяйте `no` на `yes` в `/usr/local/etc/pkg/repos/FreeBSD.conf` |
|
||||
> |
|
||||
> Можно установить весь привычный софт, включая `git`, чтобы напрямую скачивать |
|
||||
> zapret с github. |
|
||||
|
|
||||
`/usr/local/etc/rc.d/zapret.sh` (chmod `755`): |
|
||||
```sh |
|
||||
#!/bin/sh |
|
||||
|
|
||||
kldload ipfw |
|
||||
kldload ipdivert |
|
||||
|
|
||||
# for older pfsense versions. newer do not have these sysctls |
|
||||
sysctl net.inet.ip.pfil.outbound=ipfw,pf |
|
||||
sysctl net.inet.ip.pfil.inbound=ipfw,pf |
|
||||
sysctl net.inet6.ip6.pfil.outbound=ipfw,pf |
|
||||
sysctl net.inet6.ip6.pfil.inbound=ipfw,pf |
|
||||
|
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0 |
|
||||
pkill ^dvtws$ |
|
||||
dvtws --daemon --port 989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
|
|
||||
# required for newer pfsense versions (2.6.0 tested) to return ipfw to functional state |
|
||||
pfctl -d ; pfctl -e |
|
||||
``` |
|
||||
|
|
||||
#### Проблемы tpws |
|
||||
Что касается `tpws`, то видимо имеется некоторый конфликт двух фаерволов, и |
|
||||
правила fwd в ipfw не работают. Работает перенаправление средствами PF как |
|
||||
описано в разделе по **FreeBSD**. В PF можно изменять правила только целыми |
|
||||
блоками - якорями (anchors). Нельзя просто так добавить или удалить что-то. Но |
|
||||
чтобы какой-то anchor был обработан, на него должна быть ссылка из основного |
|
||||
набора правил. Его трогать нельзя, иначе порушится весь фаервол. Поэтому |
|
||||
придется править код скриптов pfsense. |
|
||||
|
|
||||
1. Поправьте `/etc/inc/filter.inc` следующим образом: |
|
||||
``` |
|
||||
<...> |
|
||||
/* MOD */ |
|
||||
$natrules .= "# ZAPRET redirection\n"; |
|
||||
$natrules .= "rdr-anchor \"zapret\"\n"; |
|
||||
|
|
||||
$natrules .= "# TFTP proxy\n"; |
|
||||
$natrules .= "rdr-anchor \"tftp-proxy/*\"\n"; |
|
||||
<...> |
|
||||
``` |
|
||||
|
|
||||
2. Напишите файл с содержимым anchor-а (например, `/etc/zapret.anchor`): |
|
||||
``` |
|
||||
rdr pass on em1 inet proto tcp to port {80,443} -> 127.0.0.1 port 988 |
|
||||
rdr pass on em1 inet6 proto tcp to port {80,443} -> fe80::20c:29ff:5ae3:4821 port 988 |
|
||||
``` |
|
||||
|
|
||||
`fe80::20c:29ff:5ae3:4821` замените на ваш link local адрес LAN интерфейса, |
|
||||
либо уберите строчку, если ipv6 не нужен. |
|
||||
|
|
||||
3. Добавьте в автозапуск `/usr/local/etc/rc.d/zapret.sh`: |
|
||||
```sh |
|
||||
$ pfctl -a zapret -f /etc/zapret.anchor |
|
||||
$ pkill ^tpws$ |
|
||||
$ tpws --daemon --port=988 --enable-pf --bind-addr=127.0.0.1 --bind-iface6=em1 --bind-linklocal=force --split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
4. После перезагрузки проверьте, что правила создались: |
|
||||
```sh |
|
||||
$ pfctl -s nat |
|
||||
no nat proto carp all |
|
||||
nat-anchor "natearly/*" all |
|
||||
nat-anchor "natrules/*" all |
|
||||
<...> |
|
||||
no rdr proto carp all |
|
||||
rdr-anchor "zapret" all |
|
||||
rdr-anchor "tftp-proxy/*" all |
|
||||
rdr-anchor "miniupnpd" all |
|
||||
|
|
||||
$ pfctl -s nat -a zapret |
|
||||
rdr pass on em1 inet proto tcp from any to any port = http -> 127.0.0.1 port 988 |
|
||||
rdr pass on em1 inet proto tcp from any to any port = https -> 127.0.0.1 port 988 |
|
||||
rdr pass on em1 inet6 proto tcp from any to any port = http -> fe80::20c:29ff:5ae3:4821 port 988 |
|
||||
rdr pass on em1 inet6 proto tcp from any to any port = https -> fe80::20c:29ff:5ae3:4821 port 988 |
|
||||
``` |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Так же есть более элегантный способ запуска `tpws` через @reboot в cron и |
|
||||
> правило перенаправления в UI. Это позволит не редактировать код pfsense. |
|
||||
|
|
||||
|
|
||||
## OpenBSD |
|
||||
|
|
||||
### tpws bind на ipv4 |
|
||||
В `tpws` bind по умолчанию только на ipv6. Для bind на ipv4 нужно указать `--bind-addr=0.0.0.0`. |
|
||||
Используйте `--bind-addr=0.0.0.0 --bind-addr=::` для достижения того же результата, как в других ОС по умолчанию. |
|
||||
Но лучше все же так не делать, а сажать на определенные внутренние адреса или интерфейсы. |
|
||||
|
|
||||
|
|
||||
### tpws для проходящего трафика (старая схема не работает в новых версиях) |
|
||||
|
|
||||
В этом варианте tpws обращается явно к редиректору pf и пытается от него получить оригинальный адрес назначения. |
|
||||
Как показывает практика, это не работает на новых версиях OpenBSD. Возвращается ошибка ioctl. |
|
||||
Последняя проверенная версия, где это работает, - 6.8 . Между 6.8 и 7.4 разработчики сломали этот механизм. |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
pass in quick on em1 inet proto tcp to port {80,443} rdr-to 127.0.0.1 port 988 |
|
||||
pass in quick on em1 inet6 proto tcp to port {80,443} rdr-to ::1 port 988 |
|
||||
``` |
|
||||
|
|
||||
```sh |
|
||||
$ pfctl -f /etc/pf.conf |
|
||||
$ tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 --enable-pf |
|
||||
``` |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> В PF не выходит делать rdr-to с той же системы, где работает proxy. |
|
||||
> Вариант с route-to не сохраняет мета информацию. Адрес назначения теряется. |
|
||||
> Поэтому этот вариант годится для squid, берущего адрес из протокола прикладного уровня, но не годится для tpws, полагающегося на метаданные ОС. |
|
||||
> Поддержка rdr-to реализована через `/dev/pf`, поэтому прозрачный режим **требует root**. |
|
||||
|
|
||||
### tpws для проходящего трафика (новые системы) |
|
||||
|
|
||||
В новых версиях предлагается использовать divert-to вместо rdr-to. |
|
||||
Минимально проверенная версия, где это работает, 7.4. Может работать или не работать на более старых - исследование не проводилось. |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
pass on em1 inet proto tcp to port {80,443} divert-to 127.0.0.1 port 989 |
|
||||
pass on em1 inet6 proto tcp to port {80,443} divert-to ::1 port 989 |
|
||||
``` |
|
||||
|
|
||||
tpws должен иметь бинд на точно такой адрес, который указан в правилах pf. `0.0.0.0` или `::` не работает. |
|
||||
|
|
||||
```sh |
|
||||
$ pfctl -f /etc/pf.conf |
|
||||
$ tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 |
|
||||
``` |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Так же не понятно как делать divert с самой системы, где работает tpws. |
|
||||
|
|
||||
### Запуск dvtws |
|
||||
|
|
||||
#### Весь трафик |
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
pass in quick on em0 proto tcp from port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 proto tcp from port {80,443} no state |
|
||||
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 no state |
|
||||
``` |
|
||||
|
|
||||
```sh |
|
||||
$ pfctl -f /etc/pf.conf |
|
||||
$ ./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
#### Трафик только на таблицу zapret, за исключением таблицы nozapret |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
set limit table-entries 2000000 |
|
||||
table <zapret> file "/opt/zapret/ipset/zapret-ip.txt" |
|
||||
table <zapret-user> file "/opt/zapret/ipset/zapret-ip-user.txt" |
|
||||
table <nozapret> file "/opt/zapret/ipset/zapret-ip-exclude.txt" |
|
||||
pass out quick on em0 inet proto tcp to <nozapret> port {80,443} |
|
||||
pass in quick on em0 inet proto tcp from <zapret> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret> port {80,443} no state |
|
||||
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} no state |
|
||||
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989 no state |
|
||||
table <zapret6> file "/opt/zapret/ipset/zapret-ip6.txt" |
|
||||
table <zapret6-user> file "/opt/zapret/ipset/zapret-ip-user6.txt" |
|
||||
table <nozapret6> file "/opt/zapret/ipset/zapret-ip-exclude6.txt" |
|
||||
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443} |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6> port {80,443} no state |
|
||||
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} no state |
|
||||
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989 no state |
|
||||
``` |
|
||||
|
|
||||
```sh |
|
||||
$ pfctl -f /etc/pf.conf |
|
||||
$ ./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
``` |
|
||||
|
|
||||
|
|
||||
### Проблемы с badsum |
|
||||
**OpenBSD** принудительно пересчитывает tcp checksum после divert, поэтому |
|
||||
скорее всего `dpi-desync-fooling=badsum` у вас не заработает. При использовании |
|
||||
этого параметра `dvtws` предупредит о возможной проблеме. |
|
||||
|
|
||||
|
|
||||
### Особенность отправки fake пакетов |
|
||||
В **OpenBSD** `dvtws` все фейки отсылает через divert socket, поскольку эта |
|
||||
возможность через raw sockets заблокирована. Видимо PF автоматически |
|
||||
предотвращает повторный заворот diverted фреймов, поэтому проблемы зацикливания |
|
||||
нет. |
|
||||
|
|
||||
divert-packet автоматически вносит обратное правило для перенаправления. Трюк с |
|
||||
no state и in правилом позволяет обойти эту проблему, чтобы напрасно не гнать |
|
||||
массивный трафик через `dvtws`. |
|
||||
|
|
||||
|
|
||||
### Перезагрузка PF таблиц |
|
||||
Скрипты из ipset не перезагружают таблицы в PF по умолчанию. |
|
||||
|
|
||||
Чтобы они это делали, добавьте параметр в `/opt/zapret/config`: |
|
||||
``` |
|
||||
LISTS_RELOAD="pfctl -f /etc/pf.conf" |
|
||||
``` |
|
||||
|
|
||||
Более новые версии `pfctl` понимают команду перезагрузить только таблицы. Но это не относится к **OpenBSD**. В новых **FreeBSD** есть. |
|
||||
```sh |
|
||||
$ pfctl -Tl -f /etc/pf.conf |
|
||||
``` |
|
||||
|
|
||||
> [!IMPORTANT] |
|
||||
> Не забудьте выключить сжатие gzip: `GZIP_LISTS=0` |
|
||||
|
|
||||
> [!IMPORTANT] |
|
||||
> Если в вашей конфигурации какого-то файла листа нет, то его необходимо |
|
||||
> исключить из правил PF. Если вдруг листа нет, и он задан в pf.conf, будет |
|
||||
> ошибка перезагрузки фаервола. |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> После настройки обновление листов можно поместить в cron: |
|
||||
> ```sh |
|
||||
> $ crontab -e |
|
||||
> ``` |
|
||||
> |
|
||||
> ``` |
|
||||
> <...> |
|
||||
> 0 12 */2 * * /opt/zapret/ipset/get_config.sh |
|
||||
> ``` |
|
||||
|
|
||||
|
|
||||
## MacOS |
|
||||
|
|
||||
### Введение |
|
||||
Иначально ядро этой ОС "darwin" основывалось на **BSD**, потому в ней много |
|
||||
похожего на другие версии **BSD**. Однако, как и в других массовых коммерческих |
|
||||
проектах, приоритеты смещаются в сторону от оригинала. Яблочники что хотят, то |
|
||||
и творят. |
|
||||
|
|
||||
|
|
||||
### dvtws бесполезен |
|
||||
Раньше был ipfw, потом его убрали, заменили на PF. Есть сомнения, что divert |
|
||||
сокеты в ядре остались. Попытка создать divert socket не выдает ошибок, но |
|
||||
полученный сокет ведет себя точно так же, как raw, со всеми его унаследованными |
|
||||
косяками + еще яблочно специфическими. В PF divert-packet не работает. Простой |
|
||||
grep бинарика `pfctl` показывает, что там нет слова "divert", а в других |
|
||||
версиях **BSD** оно есть. `dvtws` собирается, но совершенно бесполезен. |
|
||||
|
|
||||
|
|
||||
### tpws |
|
||||
`tpws` удалось адаптировать, он работоспособен. Получение адреса назначения для |
|
||||
прозрачного прокси в PF (`DIOCNATLOOK`) убрали из заголовков в новых SDK, |
|
||||
сделав фактически недокументированным. |
|
||||
|
|
||||
В `tpws` перенесены некоторые определения из более старых версий яблочных SDK. |
|
||||
С ними удалось завести прозрачный режим. Однако, что будет в следующих версиях |
|
||||
угадать сложно. Гарантий нет. Еще одной особенностью PF в **MacOS** является |
|
||||
проверка на рута в момент обращения к `/dev/pf`, чего нет в остальных **BSD**. |
|
||||
`tpws` по умолчанию сбрасывает рутовые привилегии. Необходимо явно указать |
|
||||
параметр `--user=root`. В остальном PF себя ведет похоже на **FreeBSD**. |
|
||||
Синтаксис `pf.conf` тот же. |
|
||||
|
|
||||
> [!IMPORTANT] |
|
||||
> На **MacOS** работает редирект как с проходящего трафика, так и с локальной |
|
||||
> системы через route-to. Поскольку `tpws` вынужден работать под root, для |
|
||||
> исключения рекурсии приходится пускать исходящий от root трафик напрямую. |
|
||||
> Отсюда имеем недостаток - **обход DPI для рута работать НЕ будет**. |
|
||||
|
|
||||
#### Работа в прозрачном режиме только для исходящих запросов |
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
rdr pass on lo0 inet proto tcp from !127.0.0.0/8 to any port {80,443} -> 127.0.0.1 port 988 |
|
||||
rdr pass on lo0 inet6 proto tcp from !::1 to any port {80,443} -> fe80::1 port 988 |
|
||||
pass out route-to (lo0 127.0.0.1) inet proto tcp from any to any port {80,443} user { >root } |
|
||||
pass out route-to (lo0 fe80::1) inet6 proto tcp from any to any port {80,443} user { >root } |
|
||||
``` |
|
||||
|
|
||||
```sh |
|
||||
$ pfctl -ef /etc/pf.conf |
|
||||
$ /opt/zapret/tpws/tpws --user=root --port=988 --bind-addr=127.0.0.1 --bind-iface6=lo0 --bind-linklocal=force |
|
||||
``` |
|
||||
|
|
||||
#### Работа в прозрачном режиме |
|
||||
> [!NOTE] |
|
||||
> Предполагается, что имя LAN интерфейса - `en1` |
|
||||
|
|
||||
```sh |
|
||||
$ ifconfig en1 | grep fe80 |
|
||||
inet6 fe80::bbbb:bbbb:bbbb:bbbb%en1 prefixlen 64 scopeid 0x8 |
|
||||
``` |
|
||||
|
|
||||
`/etc/pf.conf`: |
|
||||
``` |
|
||||
rdr pass on en1 inet proto tcp from any to any port {80,443} -> 127.0.0.1 port 988 |
|
||||
rdr pass on en1 inet6 proto tcp from any to any port {80,443} -> fe80::bbbb:bbbb:bbbb:bbbb port 988 |
|
||||
rdr pass on lo0 inet proto tcp from !127.0.0.0/8 to any port {80,443} -> 127.0.0.1 port 988 |
|
||||
rdr pass on lo0 inet6 proto tcp from !::1 to any port {80,443} -> fe80::1 port 988 |
|
||||
pass out route-to (lo0 127.0.0.1) inet proto tcp from any to any port {80,443} user { >root } |
|
||||
pass out route-to (lo0 fe80::1) inet6 proto tcp from any to any port {80,443} user { >root } |
|
||||
``` |
|
||||
|
|
||||
```sh |
|
||||
$ pfctl -ef /etc/pf.conf |
|
||||
$ /opt/zapret/tpws/tpws --user=root --port=988 --bind-addr=127.0.0.1 --bind-iface6=lo0 --bind-linklocal=force --bind-iface6=en1 --bind-linklocal=force |
|
||||
``` |
|
||||
|
|
||||
|
|
||||
### Проблема link-local адреса |
|
||||
Если вы пользуетесь **MaсOS** в качестве ipv6 роутера, то нужно будет |
|
||||
решить вопрос с регулярно изменяемым link-local адресом. С некоторых версий |
|
||||
**MacOS** использует по умолчанию постоянные "secured" ipv6 адреса вместо |
|
||||
генерируемых на базе MAC адреса. |
|
||||
|
|
||||
Все замечательно, но есть одна проблема. Постоянными остаются только global |
|
||||
scope адреса. Link locals периодически меняются. Смена завязана на системное |
|
||||
время. Перезагрузки адрес не меняют, Но если перевести время на день вперед и |
|
||||
перезагрузиться - link local станет другим (по крайней мере в vmware это так). |
|
||||
Информации по вопросу крайне мало, но тянет на баг. Не должен меняться link |
|
||||
local. Скрывать link local не имеет смысла, а динамический link local нельзя |
|
||||
использовать в качестве адреса шлюза. Проще всего отказаться от "secured" |
|
||||
адресов. Для этого поместите строчку `net.inet6.send.opmode=0` в |
|
||||
`/etc/sysctl.conf` и перезагрузите систему. |
|
||||
|
|
||||
Все равно для исходящих соединений будут использоваться temporary адреса, как и |
|
||||
в других системах. Или вам идея не по вкусу, можно прописать дополнительный |
|
||||
статический ipv6 из диапазона маски `fc00::/7` - выберите любой с длиной |
|
||||
префикса `128`. Это можно сделать в системных настройках, создав дополнительный |
|
||||
адаптер на базе того же сетевого интерфейса, отключить в нем ipv4 и вписать |
|
||||
статический ipv6. Он добавится к автоматически настраеваемым. |
|
||||
|
|
||||
|
|
||||
### Сборка |
|
||||
```sh |
|
||||
$ make -C /opt/zapret mac |
|
||||
``` |
|
||||
|
|
||||
|
|
||||
### Простая установка |
|
||||
В **MacOS** поддерживается `install_easy.sh` |
|
||||
|
|
||||
В комплекте идут бинарики, собраные под 64-bit с опцией |
|
||||
`-mmacosx-version-min=10.8`. Они должны работать на всех поддерживаемых версиях |
|
||||
**MacOS**. Если вдруг не работают - можно собрать свои. Developer tools |
|
||||
ставятся автоматом при запуске `make`. |
|
||||
|
|
||||
> [!WARNING] |
|
||||
> Internet sharing средствами системы **не поддерживается**! |
|
||||
> |
|
||||
> Поддерживается только роутер, настроенный своими силами через PF. Если вы |
|
||||
> вдруг включили шаринг, а потом выключили, то доступ к сайтам может пропасть |
|
||||
> совсем. |
|
||||
> |
|
||||
> Лечение: |
|
||||
> ```sh |
|
||||
> $ pfctl -f /etc/pf.conf |
|
||||
> ``` |
|
||||
> |
|
||||
> Если вам нужен шаринг интернета, лучше отказаться от прозрачного режима и |
|
||||
> использовать socks прокси. |
|
||||
|
|
||||
Для автостарта используется launchd (`/Library/LaunchDaemons/zapret.plist`) |
|
||||
Управляющий скрипт : `/opt/zapret/init.d/macos/zapret` |
|
||||
|
|
||||
Следующие команды работают с `tpws` и фаерволом одновременно (если |
|
||||
`INIT_APPLY_FW=1` в config) |
|
||||
|
|
||||
```sh |
|
||||
$ /opt/zapret/init.d/macos/zapret start |
|
||||
$ /opt/zapret/init.d/macos/zapret stop |
|
||||
$ /opt/zapret/init.d/macos/zapret restart |
|
||||
``` |
|
||||
|
|
||||
Работа только с tpws: |
|
||||
```sh |
|
||||
$ /opt/zapret/init.d/macos/zapret start-daemons |
|
||||
$ /opt/zapret/init.d/macos/zapret stop-daemons |
|
||||
$ /opt/zapret/init.d/macos/zapret restart-daemons |
|
||||
``` |
|
||||
|
|
||||
Работа только с PF: |
|
||||
```sh |
|
||||
$ /opt/zapret/init.d/macos/zapret start-fw |
|
||||
$ /opt/zapret/init.d/macos/zapret stop-fw |
|
||||
$ /opt/zapret/init.d/macos/zapret restart-fw |
|
||||
``` |
|
||||
|
|
||||
Перезагрузка всех IP таблиц из файлов: |
|
||||
```sh |
|
||||
$ /opt/zapret/init.d/macos/zapret reload-fw-tables |
|
||||
``` |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Инсталятор настраивает `LISTS_RELOAD` в config, так что скрипты |
|
||||
> [`ipset/*.sh`](../ipset/) автоматически перезагружают IP таблицы в PF. |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Автоматически создается cron job на |
|
||||
> [`ipset/get_config.sh`](../ipset/get_config.sh), по аналогии с openwrt. |
|
||||
|
|
||||
При start-fw скрипт автоматически модицифирует `/etc/pf.conf`, вставляя туда |
|
||||
anchors "zapret". Модификация расчитана на `pf.conf`, в котором сохранены |
|
||||
дефолтные anchors от apple. Если у вас измененный `pf.conf` и модификация не |
|
||||
удалась, об этом будет предупреждение. Не игнорируйте его. В этом случае вам |
|
||||
нужно вставить в свой `pf.conf` (в соответствии с порядком типов правил): |
|
||||
``` |
|
||||
rdr-anchor "zapret" |
|
||||
anchor "zapret" |
|
||||
``` |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> При деинсталяции через `uninstall_easy.sh` модификации `pf.conf` убираются. |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> start-fw создает 3 файла anchors в `/etc/pf.anchors`: `zapret`, `zapret-v4`, |
|
||||
> `zapret-v6`. Последние 2 подключаются из anchor "zapret". |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Таблицы `nozapret` и `nozapret6` принадлежат anchor "zapret". |
|
||||
> |
|
||||
> Таблицы `zapret` и `zapret-user` в anchor "zapret-v4". |
|
||||
> |
|
||||
> Таблицы `zapret6` и `zapret6-user` в anchor "zapret-v6". |
|
||||
> |
|
||||
> Если какая-то версия протокола отключена - соответствующий anchor пустой и не |
|
||||
> упоминается в anchor "zapret". Таблицы и правила создаются только на те |
|
||||
> листы, которые фактически есть в директории ipset. |
|
||||
|
|
||||
|
|
||||
### Вариант Custom |
|
||||
Так же как и в других системах, поддерживаемых в простом инсталяторе, можно |
|
||||
создавать свои custom скрипты. |
|
||||
|
|
||||
Расположение: `/opt/zapret/init.d/macos/custom` |
|
||||
|
|
||||
`zapret_custom_daemons()` получает в `$1`: `0` или `1`. `0` = stop, `1` = start |
|
||||
|
|
||||
custom firewall отличается от linux варианта. Вместо заполнения `iptables` вам |
|
||||
нужно сгенерировать правила для `zapret-v4` и `zapret-v6` anchors и выдать их в |
|
||||
stdout. Это делается в функциях `zapret_custom_firewall_v4()` и |
|
||||
`zapret_custom_firewall_v6()`. Определения таблиц заполняются основным скриптом |
|
||||
\- вам это делать не нужно. Можно ссылаться на таблицы `zapret` и `zapret-user` |
|
||||
в v4, `zapret6` и `zapret6-user`. |
|
||||
|
|
||||
Cм. пример [в файле](../init.d/macos/custom.d.examples/50-extra-tpws). |
|
||||
@ -1,110 +0,0 @@ |
|||||
WAN=em0 LAN=em1 |
|
||||
|
|
||||
FreeBSD IPFW : |
|
||||
|
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 fwd 127.0.0.1,988 tcp from me to any 80,443 proto ip4 xmit em0 not uid daemon |
|
||||
ipfw add 100 fwd ::1,988 tcp from me to any 80,443 proto ip6 xmit em0 not uid daemon |
|
||||
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1 |
|
||||
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1 |
|
||||
|
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443 |
|
||||
ipfw add 100 fwd 127.0.0.1,988 tcp from me to table\(zapret\) 80,443 proto ip4 xmit em0 not uid daemon |
|
||||
ipfw add 100 fwd ::1,988 tcp from me to table\(zapret\) 80,443 proto ip6 xmit em0 not uid daemon |
|
||||
ipfw add 100 allow tcp from any to table\(nozapret\) 80,443 recv em1 |
|
||||
ipfw add 100 fwd 127.0.0.1,988 tcp from any to any 80,443 proto ip4 recv em1 |
|
||||
ipfw add 100 fwd ::1,988 tcp from any to any 80,443 proto ip6 recv em1 |
|
||||
|
|
||||
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 |
|
||||
|
|
||||
|
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted xmit em0 |
|
||||
; required for autottl mode |
|
||||
ipfw add 100 divert 989 tcp from any 80,443 to any tcpflags syn,ack in not diverted recv em0 |
|
||||
; udp |
|
||||
ipfw add 100 divert 989 udp from any to any 443 out not diverted xmit em0 |
|
||||
|
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 allow tcp from me to table\(nozapret\) 80,443 |
|
||||
ipfw add 100 divert 989 tcp from any to table\(zapret\) 80,443 out not diverted xmit em0 |
|
||||
|
|
||||
/opt/zapret/nfq/dvtws --port=989 --debug --dpi-desync=split |
|
||||
|
|
||||
|
|
||||
sample ipfw NAT setup : |
|
||||
|
|
||||
WAN=em0 |
|
||||
LAN=em1 |
|
||||
ipfw -q flush |
|
||||
ipfw -q nat 1 config if $WAN unreg_only reset |
|
||||
ipfw -q add 10 allow ip from any to any via $LAN |
|
||||
ipfw -q add 20 allow ip from any to any via lo0 |
|
||||
ipfw -q add 300 nat 1 ip4 from any to any in recv $WAN |
|
||||
ipfw -q add 301 check-state |
|
||||
ipfw -q add 350 skipto 390 tcp from any to any out xmit $WAN setup keep-state |
|
||||
ipfw -q add 350 skipto 390 udp from any to any out xmit $WAN keep-state |
|
||||
ipfw -q add 360 allow all from any to me in recv $WAN |
|
||||
ipfw -q add 390 nat 1 ip4 from any to any out xmit $WAN |
|
||||
ipfw -q add 10000 allow ip from any to any |
|
||||
|
|
||||
Forwarding : |
|
||||
sysctl net.inet.ip.forwarding=1 |
|
||||
sysctl net.inet6.ip6.forwarding=1 |
|
||||
|
|
||||
|
|
||||
OpenBSD PF : |
|
||||
|
|
||||
(OLD OpenBSD versions) |
|
||||
|
|
||||
; dont know how to rdr-to from local system. doesn't seem to work. only works for routed traffic. |
|
||||
|
|
||||
/etc/pf.conf |
|
||||
pass in quick on em1 inet proto tcp to port {80,443} rdr-to 127.0.0.1 port 988 |
|
||||
pass in quick on em1 inet6 proto tcp to port {80,443} rdr-to ::1 port 988 |
|
||||
pfctl -f /etc/pf.conf |
|
||||
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 --enable-pf |
|
||||
|
|
||||
(NEW OpenBSD versions) |
|
||||
|
|
||||
; dont know how to divert-to from local system |
|
||||
|
|
||||
/etc/pf.conf |
|
||||
pass on em1 inet proto tcp to port {80,443} divert-to 127.0.0.1 port 989 |
|
||||
pass on em1 inet6 proto tcp to port {80,443} divert-to ::1 port 989 |
|
||||
pfctl -f /etc/pf.conf |
|
||||
/opt/zapret/tpws/tpws --port=988 --user=daemon --bind-addr=::1 --bind-addr=127.0.0.1 |
|
||||
|
|
||||
|
|
||||
; dvtws works both for routed and local |
|
||||
|
|
||||
pass in quick on em0 proto tcp from port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 proto tcp from port {80,443} no state |
|
||||
pass out quick on em0 proto tcp to port {80,443} divert-packet port 989 no state |
|
||||
pfctl -f /etc/pf.conf |
|
||||
./dvtws --port=989 --dpi-desync=multisplit --dpi-desync-split-pos=2 |
|
||||
|
|
||||
; dvtws with table limitations : to zapret,zapret6 but not to nozapret,nozapret6 |
|
||||
; reload tables : pfctl -f /etc/pf.conf |
|
||||
set limit table-entries 2000000 |
|
||||
table <zapret> file "/opt/zapret/ipset/zapret-ip.txt" |
|
||||
table <zapret-user> file "/opt/zapret/ipset/zapret-ip-user.txt" |
|
||||
table <nozapret> file "/opt/zapret/ipset/zapret-ip-exclude.txt" |
|
||||
pass out quick on em0 inet proto tcp to <nozapret> port {80,443} |
|
||||
pass in quick on em0 inet proto tcp from <zapret> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret> port {80,443} no state |
|
||||
pass out quick on em0 inet proto tcp to <zapret> port {80,443} divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet proto tcp from <zapret-user> port {80,443} no state |
|
||||
pass out quick on em0 inet proto tcp to <zapret-user> port {80,443} divert-packet port 989 no state |
|
||||
table <zapret6> file "/opt/zapret/ipset/zapret-ip6.txt" |
|
||||
table <zapret6-user> file "/opt/zapret/ipset/zapret-ip-user6.txt" |
|
||||
table <nozapret6> file "/opt/zapret/ipset/zapret-ip-exclude6.txt" |
|
||||
pass out quick on em0 inet6 proto tcp to <nozapret6> port {80,443} |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6> port {80,443} no state |
|
||||
pass out quick on em0 inet6 proto tcp to <zapret6> port {80,443} divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} flags SA/SA divert-packet port 989 no state |
|
||||
pass in quick on em0 inet6 proto tcp from <zapret6-user> port {80,443} no state |
|
||||
pass out quick on em0 inet6 proto tcp to <zapret6-user> port {80,443} divert-packet port 989 no state |
|
||||
@ -1,588 +0,0 @@ |
|||||
v1 |
|
||||
|
|
||||
Initial release |
|
||||
|
|
||||
v2 |
|
||||
|
|
||||
nfqws : command line options change. now using standard getopt. |
|
||||
nfqws : added options for window size changing and "Host:" case change |
|
||||
ISP support : tested on mns.ru and beeline (corbina) |
|
||||
init scripts : rewritten init scripts for simple choise of ISP |
|
||||
create_ipset : now using 'ipset restore', it works much faster |
|
||||
readme : updated. now using UTF-8 charset. |
|
||||
|
|
||||
v3 |
|
||||
|
|
||||
tpws : added transparent proxy (supports TPROXY and DNAT). |
|
||||
can help when ISP tracks whole HTTP session, not only the beginning |
|
||||
ipset : added zapret-hosts-user.txt which contain user defined host names to be resolved |
|
||||
and added to zapret ip list |
|
||||
ISP support : dom.ru support via TPROXY/DNAT |
|
||||
ISP support : successfully tested sknt.ru on 'domru' configuration |
|
||||
other configs will probably also work, but cannot test |
|
||||
compile : openwrt compile howto |
|
||||
|
|
||||
v4 |
|
||||
|
|
||||
tpws : added ability to insert extra space after http method : "GET /" => "GET /" |
|
||||
ISP support : TKT support |
|
||||
|
|
||||
v5 |
|
||||
|
|
||||
nfqws : ipv6 support in nfqws |
|
||||
|
|
||||
v6 |
|
||||
|
|
||||
ipset : added "get_antizapret.sh" |
|
||||
|
|
||||
v7 |
|
||||
|
|
||||
tpws : added ability to insert "." after Host: name |
|
||||
|
|
||||
v8 |
|
||||
|
|
||||
openwrt init : removed hotplug.d/firewall because of race conditions. now only use /etc/firewall.user |
|
||||
|
|
||||
v9 |
|
||||
|
|
||||
ipban : added ipban ipset. place domains banned by ip to zapret-hosts-user-ipban.txt |
|
||||
these IPs must be soxified for both http and https |
|
||||
ISP support : tiera support |
|
||||
ISP support : added DNS filtering to ubuntu and debian scripts |
|
||||
|
|
||||
v10 |
|
||||
|
|
||||
tpws : added split-pos option. split every message at specified position |
|
||||
|
|
||||
v11 |
|
||||
|
|
||||
ipset : scripts optimizations |
|
||||
|
|
||||
v12 |
|
||||
|
|
||||
nfqws : fix wrong tcp checksum calculation if packet length is odd and platform is big-endian |
|
||||
|
|
||||
v13 |
|
||||
|
|
||||
added binaries |
|
||||
|
|
||||
v14 |
|
||||
|
|
||||
change get_antizapret script to work with https://github.com/zapret-info/z-i/raw/master/dump.csv |
|
||||
filter out 192.168.*, 127.*, 10.* from blocked ips |
|
||||
|
|
||||
v15 |
|
||||
|
|
||||
added --hostspell option to nfqws and tpws |
|
||||
ISP support : beeline now catches "host" but other spellings still work |
|
||||
openwrt/LEDE : changed init script to work with procd |
|
||||
tpws, nfqws : minor cosmetic fixes |
|
||||
|
|
||||
v16 |
|
||||
|
|
||||
tpws: split-http-req=method : split inside method name, not after |
|
||||
ISP support : mns.ru changed split pos to 3 (got redirect page with HEAD req : curl -I ej.ru) |
|
||||
|
|
||||
v17 |
|
||||
|
|
||||
ISP support : athome moved from nfqws to tpws because of instability and http request hangs |
|
||||
tpws : added options unixeol,methodeol,hosttab |
|
||||
|
|
||||
v18 |
|
||||
|
|
||||
tpws,nfqws : added hostnospace option |
|
||||
|
|
||||
v19 |
|
||||
|
|
||||
tpws : added hostlist option |
|
||||
|
|
||||
v20 |
|
||||
|
|
||||
added ip2net. ip2net groups ips from iplist into subnets and reduces ipset size twice |
|
||||
|
|
||||
v21 |
|
||||
|
|
||||
added mdig. get_reestr.sh is *real* again |
|
||||
|
|
||||
v22 |
|
||||
|
|
||||
total review of init script logic |
|
||||
dropped support of older debian 7 and ubuntu 12/14 systems |
|
||||
install_bin.sh : auto binaries preparation |
|
||||
docs: readme review. some new topics added, others deleted |
|
||||
docs: VPN setup with policy based routing using wireguard |
|
||||
docs: wireguard modding guide |
|
||||
|
|
||||
v23 |
|
||||
|
|
||||
major init system rewrite |
|
||||
openwrt : separate firewall include /etc/firewall.zapret |
|
||||
install_easy.sh : easy setup on openwrt, debian, ubuntu, centos, fedora, opensuse |
|
||||
|
|
||||
v24 |
|
||||
|
|
||||
separate config from init scripts |
|
||||
gzip support in ipset/*.sh and tpws |
|
||||
|
|
||||
v25 |
|
||||
|
|
||||
init : move to native systemd units |
|
||||
use links to units, init scripts and firewall includes, no more copying |
|
||||
|
|
||||
v26 |
|
||||
|
|
||||
ipv6 support |
|
||||
tpws : advanced bind options |
|
||||
|
|
||||
v27 |
|
||||
|
|
||||
tpws : major connection code rewrite. originally it was derived from not top quality example , with many bugs and potential problems. |
|
||||
next generation connection code uses nonblocking sockets. now its in EXPERIMENTAL state. |
|
||||
|
|
||||
v28 |
|
||||
|
|
||||
tpws : added socks5 support |
|
||||
ipset : major RKN getlist rewrite. added antifilter.network support |
|
||||
|
|
||||
v29 |
|
||||
|
|
||||
nfqws : DPI desync attack |
|
||||
ip exclude system |
|
||||
|
|
||||
v30 |
|
||||
|
|
||||
nfqws : DPI desync attack modes : fake,rst |
|
||||
|
|
||||
v31 |
|
||||
|
|
||||
nfqws : DPI desync attack modes : disorder,disorder2,split,split2. |
|
||||
nfqws : DPI desync fooling mode : badseq. multiple modes supported |
|
||||
|
|
||||
v32 |
|
||||
|
|
||||
tpws : multiple binds |
|
||||
init scripts : run only one instance of tpws in any case |
|
||||
|
|
||||
v33 |
|
||||
|
|
||||
openwrt : flow offloading support |
|
||||
config : MODE refactoring |
|
||||
|
|
||||
v34 |
|
||||
|
|
||||
nfqws : dpi-desync 2 mode combos |
|
||||
nfqws : dpi-desync without parameter no more supported. previously it meant "fake" |
|
||||
nfqws : custom fake http request and tls client hello |
|
||||
|
|
||||
v35 |
|
||||
|
|
||||
limited FreeBSD and OpenBSD support |
|
||||
|
|
||||
v36 |
|
||||
|
|
||||
full FreeBSD and OpenBSD support |
|
||||
|
|
||||
v37 |
|
||||
|
|
||||
limited MacOS support |
|
||||
|
|
||||
v38 |
|
||||
|
|
||||
MacOS easy install |
|
||||
|
|
||||
v39 |
|
||||
|
|
||||
nfqws: conntrack, wssize |
|
||||
|
|
||||
v40 |
|
||||
|
|
||||
init scripts : IFACE_LAN, IFACE_WAN now accept multiple interfaces |
|
||||
init scripts : openwrt uses now OPENWRT_LAN parameter to override incoming interfaces for tpws |
|
||||
|
|
||||
v41 |
|
||||
|
|
||||
install_easy : openrc support |
|
||||
|
|
||||
v42 |
|
||||
|
|
||||
blockcheck.sh |
|
||||
|
|
||||
v43 |
|
||||
|
|
||||
nfqws: UDP desync with conntrack support (any-protocol only for now) |
|
||||
|
|
||||
v44 |
|
||||
|
|
||||
nfqws: ipfrag |
|
||||
|
|
||||
v45 |
|
||||
|
|
||||
nfqws: hop-by-hop ipv6 desync and fooling |
|
||||
|
|
||||
v46 |
|
||||
|
|
||||
big startup script refactoring to support nftables and new openwrt snapshot builds with firewall4 |
|
||||
|
|
||||
v47 |
|
||||
|
|
||||
nfqws: QUIC initial decryption |
|
||||
nfqws: udplen, fakeknown dpi desync modes |
|
||||
|
|
||||
v48 |
|
||||
|
|
||||
nfqws, tpws : multiple --hostlist and --hostlist-exclude support |
|
||||
launch system, ipset : no more list merging. all lists are passed separately to nfqws and tpws |
|
||||
nfqws : udplen fooling supports packet shrinking (negative increment value) |
|
||||
|
|
||||
v49 |
|
||||
|
|
||||
QUIC support integrated to the main system and setup |
|
||||
|
|
||||
v50 |
|
||||
|
|
||||
DHT protocol support. |
|
||||
DPI desync mode 'tamper' for DHT. |
|
||||
HEX string support in addition to binary files. |
|
||||
|
|
||||
v51 |
|
||||
|
|
||||
tpws --tlsrec attack. |
|
||||
|
|
||||
v52 |
|
||||
|
|
||||
autohostlist mode |
|
||||
|
|
||||
v53 |
|
||||
|
|
||||
nfqws: tcp session reassemble for TLS ClientHello |
|
||||
|
|
||||
v54 |
|
||||
|
|
||||
tpws: out of band send when splitting (--oob) |
|
||||
nfqws: autottl |
|
||||
nfqws: datanoack fooling |
|
||||
nftables: use POSTNAT path for tcp redirections to allow NAT-breaking strategies. use additional mark bit DESYNC_MARK_POSTNAT. |
|
||||
|
|
||||
v55 |
|
||||
|
|
||||
tpws: incompatible oob parameter change. it doesn't take oob byte anymore. instead it takes optional protocol filter - http or tls. |
|
||||
the same is done with disorder. oob byte can be specified in parameter --oob-data. |
|
||||
blockcheck: quick mode, strategy order optimizations, QUIC protocol support |
|
||||
nfqws: syndata desync mode |
|
||||
|
|
||||
v56 |
|
||||
|
|
||||
tpws: mss fooling |
|
||||
tpws: multi thread resolver. eliminates blocks related to hostname resolve. |
|
||||
|
|
||||
v57 |
|
||||
|
|
||||
tpws: --nosplice option |
|
||||
nfqws: postnat fixes |
|
||||
nfqws: --dpi-desync-start option |
|
||||
nfqws: packet delay for kyber TLS and QUIC |
|
||||
nfqws: --dpi-desync-retrans obsolete |
|
||||
nfqws: --qnum is mandatory, no more default queue 0 |
|
||||
|
|
||||
v58 |
|
||||
|
|
||||
winws |
|
||||
|
|
||||
v59 |
|
||||
|
|
||||
tpws: --split-tls |
|
||||
tpws: --tlsrec=sniext |
|
||||
nfqws: --dpi-desync-split-http-req, --dpi-desync-split-tls. multi segment TLS support for split. |
|
||||
blockcheck: mdig dns cache |
|
||||
|
|
||||
v60 |
|
||||
|
|
||||
blockcheck: port block test, partial ip block test |
|
||||
nfqws: seqovl split/disorder modes |
|
||||
|
|
||||
v61 |
|
||||
|
|
||||
C code cleanups |
|
||||
dvtws: do not use raw sockets. use divert. |
|
||||
nfqws,tpws: detect TLS 1.2 ClientHello from very old libraries with SSL 3.0 version in record layer |
|
||||
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 |
|
||||
|
|
||||
v63: |
|
||||
|
|
||||
tpws: multi-strategy |
|
||||
|
|
||||
v64: |
|
||||
|
|
||||
blockcheck: warn if dpi bypass software is already running |
|
||||
blockcheck: TPWS_EXTRA, NFQWS_EXTRA |
|
||||
init.d: multiple custom scripts |
|
||||
|
|
||||
v65: |
|
||||
|
|
||||
init.d: dynamic number allocation for dnum,tpws_port,qnum |
|
||||
init.d: FW_EXTRA_PRE, FW_EXTRA_POST |
|
||||
init.d: zapret_custom_firewall_nft_flush |
|
||||
nfqws,tpws: l7proto and client ip:port info in autohostlist debug log |
|
||||
nfqws,tpws: user mode ipset filter support |
|
||||
nfqws,tpws: l7proto filter support |
|
||||
tpws: fixed MSS apply in transparent mode |
|
||||
nfqws: fixed autottl apply if desync profile changed |
|
||||
tpws,nfqws: fixed 100% cpu hang on gzipped list with comments |
|
||||
ipset: get_refilter_ipsum.sh , get_refilter_domain.sh |
|
||||
|
|
||||
v66: |
|
||||
|
|
||||
init.d: rewrite traffic interception and daemon launch parameters in config file. this break compatibility with old versions. |
|
||||
init.d: openwrt-minimal : tpws launch for low storage openwrt devices |
|
||||
|
|
||||
v67: |
|
||||
|
|
||||
mdig: --dns-make-query, --dns-parse-query for side-channel resolving (DoH) |
|
||||
blockcheck: use DoH resolvers if DNS spoof is detected |
|
||||
blockcheck: restring fooling to testing domain's IPs |
|
||||
nfqws,tpws: internal hostlist deduplication to save RAM |
|
||||
nfqws,tpws: hostlist/ipset auto reload on file change. no more HUP. |
|
||||
nfqws,tpws: --filter-tcp, --filter-udp take comma separated port range list |
|
||||
nfqws,tpws: @<config_file> - read config from a file |
|
||||
config: <HOSTLIST_NOAUTO> marker |
|
||||
binaries: remove zapret-winws. add win32. |
|
||||
blockcheck, install_easy.sh: preserve user environment variables during elevation |
|
||||
blockcheck: do not require root if SKIP_PKTWS=1 |
|
||||
|
|
||||
v68: |
|
||||
|
|
||||
docs : move russian version to markdown |
|
||||
nfqws,tpws: use alternate $ sign for $<config_file> |
|
||||
repo: binaries removed from repo. git actions binaries build in releases. |
|
||||
uninstall_easy.sh: offer to remove dependencies in openwrt |
|
||||
install_easy.sh: allow to download lists in autohostlist filter mode |
|
||||
|
|
||||
v69: |
|
||||
|
|
||||
nfqws, tpws: multisplit/multidisorder support. |
|
||||
nfqws: name change split->fakedsplit, disorder->fakeddisorder. compat : old names are synonyms |
|
||||
nfqws: --dpi-desync-split-http-req, --dpi-desync-split-tls deprecated. compat : these parameters add split point to multisplit. |
|
||||
nfqws: --dpi-desync=split2|disorder2 deprecated. compat: they are now synonyms for multisplit/multidisorder |
|
||||
nfqws: cancel seqovl if MTU is exceeded (linux only). cancel seqovl for disorder if seqovl>=first_part_size. |
|
||||
nfqws: fixed splits in multiple TLS segments. |
|
||||
tpws: --split-http-req,--split-tls deprecated. compat : these parameters add split point to multisplit. |
|
||||
tpws: --tlsrec now takes pos markers. compat : old names are converted to pos markers |
|
||||
tpws: --tlsrec-pos deprecated. compat : sets absolute pos marker |
|
||||
nfqws,tpws: chown autohostlist, autohostlist debug log and debug log files after options parse |
|
||||
nfqws,tpws: set EXEDIR env var to use in @config (won't work for stadalone winws without /bin/sh) |
|
||||
dvtws: set random/increasing ip_id value in generated packets |
|
||||
mdig: fixed parsing of DNS reply in windows (stdin is opened as text, not binary) |
|
||||
tpws: support compile for android NDK api level >= 21 (Android 5.0) |
|
||||
tpws: --fix-seg segmentation fixer |
|
||||
repo: build for android NDK api level 21 (Android 5.0) |
|
||||
install_easy: support for APK package manager in openwrt |
|
||||
blockcheck: removed ignore CA question |
|
||||
blockcheck: removed IGNORE_CA, CURL_VERBOSE |
|
||||
blockcheck: added CURL_OPT |
|
||||
blockcheck: new strategies support |
|
||||
blockcheck: test sequence rework |
|
||||
blockcheck: view all working strategies in summary |
|
||||
|
|
||||
v69.1: |
|
||||
|
|
||||
init.d: keenetic udp fix custom |
|
||||
tpws: fixed incorrect hostlist checks |
|
||||
|
|
||||
v69.2: |
|
||||
|
|
||||
nfqws,tpws: --skip |
|
||||
nfqws: --methodeol |
|
||||
init.d: do not use pgrep in sysv for busybox compat |
|
||||
|
|
||||
v69.3 |
|
||||
|
|
||||
nfqws,tpws: fixed ipsets and hostlists |
|
||||
all progs: version numbers for github, build date/time for self built |
|
||||
repo: light release for openwrt and embedded systems |
|
||||
repo: sha256sum |
|
||||
|
|
||||
v69.4 |
|
||||
|
|
||||
nfqws: fakedsplit/fakeddisorder fakes for both split segments |
|
||||
nfqws: --dpi-desync-fakedsplit-pattern |
|
||||
|
|
||||
v69.5 |
|
||||
|
|
||||
nfqws,tpws: --dry-run |
|
||||
install_easy: check tpws and nfqws options validity |
|
||||
|
|
||||
v69.6 |
|
||||
|
|
||||
nfqws: set NETLINK_NO_ENOBUFS to fix possible nfq recv errors |
|
||||
init.d: unify custom scripts for linux |
|
||||
init.d: new custom scripts : 20-fw-extra, 50-wg4all |
|
||||
|
|
||||
v69.7 |
|
||||
|
|
||||
nfqws,tpws: --comment |
|
||||
nfqws: trash flood warning |
|
||||
winws: exclude empty outgoing ack packets in windivert filter |
|
||||
|
|
||||
v69.8 |
|
||||
|
|
||||
winws: accept empty outgoing RST and FIN packets for conntrack needs |
|
||||
repo: lexra build |
|
||||
|
|
||||
v69.9 |
|
||||
|
|
||||
init.d: exclude ipban from tpws redirection |
|
||||
macos: fix install_easy |
|
||||
macos: fix national decimal separator in sleep |
|
||||
ipset: scripts maintenance |
|
||||
|
|
||||
v70 |
|
||||
|
|
||||
blockcheck: override all dialog questions and enable batch mode |
|
||||
blockcheck: parallel attempts |
|
||||
nfqws: weaken wireguard initiation recognition. use len=148 and data[0]=1 signature |
|
||||
nfqws: apply split+seqovl only to the first reasm fragment |
|
||||
install_easy: dnf packager support |
|
||||
nfqws,tpws: hostlist/ipset track not only file mod time but also file size |
|
||||
nfqws,tpws,ipset: return lists reload on HUP |
|
||||
nfqws,blockcheck: --dpi-desync-fake-tls-mod |
|
||||
|
|
||||
v70.1 |
|
||||
|
|
||||
nfqws: --dpi-desync-fake-tls-mod=dupsid |
|
||||
nfqws,tpws: test accessibility of list files after privs drop |
|
||||
nfqws,tpws: --version |
|
||||
|
|
||||
v70.4 |
|
||||
|
|
||||
nfqws,tpws: ^ prefix in hostlist to disable subdomain matches |
|
||||
nfqws,tpws: optional systemd notify support. compile using 'make systemd' |
|
||||
nfqws,tpws: systemd instance templates for nfqws and tpws |
|
||||
nfqws,tpws: separate droproot from dropcaps |
|
||||
tpws: detect WSL 1 and warn about non-working options |
|
||||
|
|
||||
v70.5 |
|
||||
|
|
||||
nfqws: multiple --dpi-desync-fake-xxx |
|
||||
nfqws: support of inter-packet fragmented QUIC CRYPTO |
|
||||
|
|
||||
v70.6 |
|
||||
|
|
||||
nfqws: detect Discord Voice IP discovery packets |
|
||||
nfqws: detect STUN message packets |
|
||||
nfqws: change SNI to specified value tls mod : --dpi-desync-fake-tls-mod sni=<sni> |
|
||||
nfqws: update default TLS ClientHello fake. firefox 136.0.4 finger, no kyber, SNI=microsoft.com |
|
||||
nfqws: multiple mods for multiple TLS fakes |
|
||||
init.d: remove 50-discord |
|
||||
blockcheck: use tpws --fix-seg on linux for multiple splits |
|
||||
|
|
||||
v71 |
|
||||
|
|
||||
nfqws,tpws: debug tls version, alpn, ech |
|
||||
nfqws: --dpi-desync-fake-tls=! means default tls fake |
|
||||
nfqws: --dup* |
|
||||
nfqws: --orig* |
|
||||
nfqws: ipcache of hop count and host names |
|
||||
nfqws: --ctrack-disable |
|
||||
nfqws: --synack-split |
|
||||
nfqws: --autottl=- or --autottl=0:0-0 disable autottl. previous "0" does not work anymore. |
|
||||
tpws: ipcache of host names |
|
||||
nfqws,tpws: set 1024 repeat limit to fakes and dups |
|
||||
nfqws,tpws: do more before daemonize |
|
||||
nfqws,tpws: accept multiple gids in --gid |
|
||||
nfqws,tpws: display "android" in version string if built for android |
|
||||
init.d: remove --ipset parameter prohibition |
|
||||
init.d, blockcheck: drop time exceeded icmp for nfqws-related connections |
|
||||
blockcheck: some dup and orig-ttl mods |
|
||||
blockcheck: PKTWS_EXTRA_PRE |
|
||||
blockcheck: report test function and domain every test |
|
||||
|
|
||||
v71.1 |
|
||||
|
|
||||
nfqws,tpws: much faster ipset implementation. move from hash to avl tree |
|
||||
nfqws,tpws: check list files accessibility with dropped privs in --dry-run mode |
|
||||
nfqws,tpws: --debug=android for NDK builds |
|
||||
nfqws,tpws: use initgroups instead of setgroups if --user specified |
|
||||
nfqws: --filter-ssid (linux-only) |
|
||||
install_easy: stop if running embedded release on traditional linux system (some files missing) |
|
||||
install_bin: add "read elf" arch detection method |
|
||||
binaries: renamed arch dirs in binaries |
|
||||
|
|
||||
v71.1.1 |
|
||||
|
|
||||
nfqws: use wireless ext in case nl80211 does not return SSID |
|
||||
|
|
||||
v71.2 |
|
||||
|
|
||||
nfqws: apply udp desync to replayed packets with non-zero reasm offset (except fake) |
|
||||
blockcheck: display curl version and kernel version |
|
||||
install_bin: stop if no binaries found. display help text. |
|
||||
winws: increase buffers for port filter |
|
||||
tpws: tpws no more opens /dev/pf in OpenBSD by default. requires --enable-pf like in FreeBSD. this is migration from rdr-to to divert-to redirection scheme. |
|
||||
install_easy: warn if --ipset parameter is specified |
|
||||
|
|
||||
v71.3 |
|
||||
|
|
||||
init.d: FILTER_MARK |
|
||||
nfqws: ts fooling |
|
||||
blockcheck: test ts fooling |
|
||||
blockcheck: auto enable tcp timestamps in windows |
|
||||
tpws,nfqws: special handling of IP-like hostnames. strip :port from hostname:port |
|
||||
|
|
||||
v71.4 |
|
||||
|
|
||||
nfqws,tpws: fix possible crashes or high memory usage if hostlist has duplicate hostnames |
|
||||
init.d: custom scripts 50-discord-media, 50-stun4all |
|
||||
init.d: windivert filters for discord media, stun, wireguard |
|
||||
readme: hardware problems description |
|
||||
|
|
||||
v72 |
|
||||
|
|
||||
winws: --wf-raw-part |
|
||||
nfqws: --dpi-desync=hostfakesplit |
|
||||
nfqws: --dpi-desync-fake-tcp-mod=seq |
|
||||
nfqws: --dpi-desync-fake-tls=!+offset , --dpi-desync-fake-xxx=[+offset]@filename |
|
||||
nfqws: --dpi-desync-fakedsplit-mod=altorder for fakedsplit/fakeddisorder |
|
||||
nfqws: --dpi-desync-hostfakesplit-mod=altorder |
|
||||
nfqws: fakedsplit/fakeddisorder normalize fake split pattern offset to reasm offset |
|
||||
nfqws: optimize ipv4 ip_id. apply ip_id on all OS the same way. |
|
||||
blockcheck: print PRETTY_NAME and some OPENWRT_xxx from /etc/os-release |
|
||||
blockcheck: new strategies |
|
||||
blockcheck: curl test simulation : SIMULATE=1 |
|
||||
|
|
||||
v72.1 |
|
||||
|
|
||||
nfqws: --ip-id=seq|seqgroup|rnd|zero |
|
||||
blockcheck: MIN_AUTOTTL_DELTA,MAX_AUTOTTL_DELTA |
|
||||
init.d: 50-quic4all custom |
|
||||
|
|
||||
72.2 |
|
||||
|
|
||||
nfqws: --wssize-forced-cutoff |
|
||||
nfqws: --orig-tcp-flags, --dup-tcp-flags, --dpi-desync-tcp-flags |
|
||||
nfqws: --dup-ip-id |
|
||||
|
|
||||
73.3 |
|
||||
|
|
||||
blockcheck: support URIs |
|
||||
blockcheck: CURL_HTTPS_GET=1 suppresses -I curl option for https (HEAD -> GET) |
|
||||
|
|
||||
73.4 |
|
||||
|
|
||||
blockcheck: fix broken dns cache |
|
||||
|
|
||||
73.5 |
|
||||
|
|
||||
nfqws: fix broken l7proto profile rediscovery |
|
||||
nfqws: backport from nfqws2 nl80211 ssid discovery fix for newer kernels |
|
||||
|
|
||||
73.6 |
|
||||
|
|
||||
ipset: remove zapret-info based scripts because it's gone |
|
||||
blockcheck: fix tpws test regression |
|
||||
@ -1,57 +0,0 @@ |
|||||
How to compile native programs for use in openwrt |
|
||||
------------------------------------------------- |
|
||||
|
|
||||
1) Install required packages to the host system : |
|
||||
|
|
||||
debian,ubuntu : apt install build-essential patch libncurses-dev python3-distutils unzip gawk wget git |
|
||||
fedora: dnf install make patch gcc g++ ncurses-devel git perl |
|
||||
|
|
||||
Other packages may be required on your distribution. Look for the errors. |
|
||||
|
|
||||
2) Download latest SDK for your target platform from https://downloads.openwrt.org |
|
||||
|
|
||||
examples : |
|
||||
|
|
||||
curl -o - https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz | tar -Jxv |
|
||||
cd openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64 |
|
||||
|
|
||||
curl -o - https://downloads.openwrt.org/snapshots/targets/x86/64/openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst | tar --zstd -xv |
|
||||
cd openwrt-sdk-x86-64_gcc-13.3.0_musl.Linux-x86_64 |
|
||||
|
|
||||
3) Install required libs |
|
||||
|
|
||||
./scripts/feeds update base packages |
|
||||
./scripts/feeds install libnetfilter-queue zlib libcap |
|
||||
|
|
||||
4) Prepare openwrt package definitions |
|
||||
|
|
||||
cp -R /opt/zapret/docs/compile/openwrt/. . |
|
||||
cp -R /opt/zapret/tpws package/zapret/tpws |
|
||||
cp -R /opt/zapret/nfq package/zapret/nfqws |
|
||||
cp -R /opt/zapret/mdig package/zapret/mdig |
|
||||
cp -R /opt/zapret/ip2net package/zapret/ip2net |
|
||||
rm -f package/zapret/tpws/tpws/tpws package/zapret/nfqws/nfq/nfqws package/zapret/mdig/mdig/mdig package/zapret/ip2net/ip2net/ip2net |
|
||||
|
|
||||
5) Prepare .config |
|
||||
|
|
||||
make defconfig |
|
||||
|
|
||||
If you only need bins without packages comment 'CONFIG_AUTOREMOVE=y' line in .config |
|
||||
|
|
||||
6) Compile |
|
||||
|
|
||||
dynamic build : make package/{tpws,nfqws,mdig,ip2net}/compile |
|
||||
static build : make CFLAGS=-static package/{tpws,nfqws,mdig,ip2net}/compile |
|
||||
|
|
||||
7) Get result |
|
||||
|
|
||||
executables only : build_dir/target/<progname> |
|
||||
ipk or apk packages : bin/packages/*/base |
|
||||
|
|
||||
8) Installing to openwrt to use with zapret |
|
||||
|
|
||||
zapret with or without binaries should be already installed in /opt/zapret. |
|
||||
Install ipk's or apk's with all compiled progs using opkg or apk. |
|
||||
Bins are placed to /opt/zapret/binaries/my. |
|
||||
Or copy binaries there manually and set chmod 755 to them. |
|
||||
Run install_bin.sh or install_easy.sh. They will use bins in 'my' folder. |
|
||||
@ -1,16 +0,0 @@ |
|||||
debian,ubuntu : |
|
||||
|
|
||||
apt install make gcc zlib1g-dev libcap-dev libnetfilter-queue-dev libmnl-dev libsystemd-dev |
|
||||
make -C /opt/zapret systemd |
|
||||
|
|
||||
FreeBSD : |
|
||||
|
|
||||
make -C /opt/zapret |
|
||||
|
|
||||
OpenBSD : |
|
||||
|
|
||||
make -C /opt/zapret bsd |
|
||||
|
|
||||
MacOS : |
|
||||
|
|
||||
make -C /opt/zapret mac |
|
||||
@ -1,29 +0,0 @@ |
|||||
Windows x64 |
|
||||
|
|
||||
1) Download latest cygwin for windows 7 |
|
||||
|
|
||||
curl -O https://www.cygwin.com/setup-x86_64.exe |
|
||||
setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215 |
|
||||
|
|
||||
2) During setup install packages : make gcc-core zlib-devel |
|
||||
|
|
||||
3) Run Cygwin.bat |
|
||||
|
|
||||
4) cd to %ZAPRET_BASE%/nfq |
|
||||
|
|
||||
cd C:/Users/user/Downloads/zapret/nfq |
|
||||
|
|
||||
5) Compile |
|
||||
|
|
||||
make cygwin64 |
|
||||
|
|
||||
use winws.exe |
|
||||
|
|
||||
6) Take windivert.dll and windivert64.sys here : https://reqrypt.org/download |
|
||||
Choose version 2.2.2 for Windows 10 and 2.2.0 for Windows 7. |
|
||||
|
|
||||
7) Copy cygwin1.dll, winws.exe, windivert.dll and windivert64.sys to one folder. |
|
||||
|
|
||||
8) Run winws.exe from cmd.exe running as administrator. |
|
||||
winws will not run from cygwin shell with cygwin1.dll copy in it's folder. |
|
||||
winws will not run without cygwin1.dll outside of cygwin shell. |
|
||||
@ -1,32 +0,0 @@ |
|||||
#
|
|
||||
|
|
||||
include $(TOPDIR)/rules.mk |
|
||||
|
|
||||
PKG_NAME:=ip2net |
|
||||
PKG_RELEASE:=1 |
|
||||
|
|
||||
include $(INCLUDE_DIR)/package.mk |
|
||||
|
|
||||
define Package/ip2net |
|
||||
SECTION:=net |
|
||||
CATEGORY:=Network |
|
||||
TITLE:=ip2net |
|
||||
SUBMENU:=Zapret |
|
||||
endef |
|
||||
|
|
||||
define Build/Prepare |
|
||||
mkdir -p $(PKG_BUILD_DIR) |
|
||||
$(CP) ./ip2net/* $(PKG_BUILD_DIR)/ |
|
||||
endef |
|
||||
|
|
||||
define Build/Compile |
|
||||
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) |
|
||||
endef |
|
||||
|
|
||||
define Package/ip2net/install |
|
||||
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my |
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/ip2net $(1)/opt/zapret/binaries/my |
|
||||
endef |
|
||||
|
|
||||
$(eval $(call BuildPackage,ip2net)) |
|
||||
|
|
||||
@ -1 +0,0 @@ |
|||||
Copy "ip2net" folder here ! |
|
||||
@ -1,32 +0,0 @@ |
|||||
#
|
|
||||
|
|
||||
include $(TOPDIR)/rules.mk |
|
||||
|
|
||||
PKG_NAME:=mdig |
|
||||
PKG_RELEASE:=1 |
|
||||
|
|
||||
include $(INCLUDE_DIR)/package.mk |
|
||||
|
|
||||
define Package/mdig |
|
||||
SECTION:=net |
|
||||
CATEGORY:=Network |
|
||||
TITLE:=mdig |
|
||||
SUBMENU:=Zapret |
|
||||
endef |
|
||||
|
|
||||
define Build/Prepare |
|
||||
mkdir -p $(PKG_BUILD_DIR) |
|
||||
$(CP) ./mdig/* $(PKG_BUILD_DIR)/ |
|
||||
endef |
|
||||
|
|
||||
define Build/Compile |
|
||||
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) |
|
||||
endef |
|
||||
|
|
||||
define Package/mdig/install |
|
||||
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my |
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/mdig $(1)/opt/zapret/binaries/my |
|
||||
endef |
|
||||
|
|
||||
$(eval $(call BuildPackage,mdig)) |
|
||||
|
|
||||
@ -1 +0,0 @@ |
|||||
Copy "mdig" folder here ! |
|
||||
@ -1,34 +0,0 @@ |
|||||
#
|
|
||||
|
|
||||
include $(TOPDIR)/rules.mk |
|
||||
|
|
||||
PKG_NAME:=nfqws |
|
||||
PKG_RELEASE:=1 |
|
||||
|
|
||||
include $(INCLUDE_DIR)/package.mk |
|
||||
|
|
||||
define Package/nfqws |
|
||||
SECTION:=net |
|
||||
CATEGORY:=Network |
|
||||
TITLE:=nfqws |
|
||||
SUBMENU:=Zapret |
|
||||
DEPENDS:=+libnetfilter-queue +lmnl +libcap +zlib |
|
||||
endef |
|
||||
|
|
||||
define Build/Prepare |
|
||||
mkdir -p $(PKG_BUILD_DIR) |
|
||||
$(CP) ./nfq/* $(PKG_BUILD_DIR)/ |
|
||||
endef |
|
||||
|
|
||||
define Build/Compile |
|
||||
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) |
|
||||
endef |
|
||||
|
|
||||
define Package/nfqws/install |
|
||||
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my |
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/nfqws $(1)/opt/zapret/binaries/my |
|
||||
endef |
|
||||
|
|
||||
$(eval $(call BuildPackage,nfqws)) |
|
||||
|
|
||||
|
|
||||
@ -1 +0,0 @@ |
|||||
Copy "nfq" folder here ! |
|
||||
@ -1,33 +0,0 @@ |
|||||
#
|
|
||||
|
|
||||
include $(TOPDIR)/rules.mk |
|
||||
|
|
||||
PKG_NAME:=tpws |
|
||||
PKG_RELEASE:=1 |
|
||||
|
|
||||
include $(INCLUDE_DIR)/package.mk |
|
||||
|
|
||||
define Package/tpws |
|
||||
SECTION:=net |
|
||||
CATEGORY:=Network |
|
||||
TITLE:=tpws |
|
||||
SUBMENU:=Zapret |
|
||||
DEPENDS:=+zlib +libcap |
|
||||
endef |
|
||||
|
|
||||
define Build/Prepare |
|
||||
mkdir -p $(PKG_BUILD_DIR) |
|
||||
$(CP) ./tpws/* $(PKG_BUILD_DIR)/ |
|
||||
endef |
|
||||
|
|
||||
define Build/Compile |
|
||||
$(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) |
|
||||
endef |
|
||||
|
|
||||
define Package/tpws/install |
|
||||
$(INSTALL_DIR) $(1)/opt/zapret/binaries/my |
|
||||
$(INSTALL_BIN) $(PKG_BUILD_DIR)/tpws $(1)/opt/zapret/binaries/my |
|
||||
endef |
|
||||
|
|
||||
$(eval $(call BuildPackage,tpws)) |
|
||||
|
|
||||
@ -1 +0,0 @@ |
|||||
Copy "tpws" folder here ! |
|
||||
@ -1,63 +0,0 @@ |
|||||
For window size changing : |
|
||||
|
|
||||
iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -j NFQUEUE --queue-num 200 --queue-bypass |
|
||||
iptables -t mangle -I PREROUTING -p tcp --sport 80 --tcp-flags SYN,ACK SYN,ACK -m set --match-set zapret src -j NFQUEUE --queue-num 200 --queue-bypass |
|
||||
|
|
||||
For dpi desync attack : |
|
||||
|
|
||||
iptables -t mangle -I POSTROUTING -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:6 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass |
|
||||
iptables -t mangle -I POSTROUTING -p tcp --dport 443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass |
|
||||
iptables -t mangle -I POSTROUTING -p udp --dport 443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass |
|
||||
|
|
||||
# auto hostlist with avoiding wrong ACK numbers in RST,ACK packets sent by russian DPI |
|
||||
sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1 |
|
||||
iptables -t mangle -I POSTROUTING -p tcp -m multiport --dports 80,443 -m connbytes --connbytes-dir=original --connbytes-mode=packets --connbytes 1:12 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass |
|
||||
iptables -t mangle -I PREROUTING -p tcp -m multiport --sports 80,443 -m connbytes --connbytes-dir=reply --connbytes-mode=packets --connbytes 1:3 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass |
|
||||
|
|
||||
|
|
||||
For TPROXY : |
|
||||
|
|
||||
sysctl -w net.ipv4.ip_forward=1 |
|
||||
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE |
|
||||
|
|
||||
ip -f inet rule add fwmark 1 lookup 100 |
|
||||
ip -f inet route add local default dev lo table 100 |
|
||||
# prevent loop |
|
||||
iptables -t filter -I INPUT -p tcp --dport 988 -j REJECT |
|
||||
iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -j MARK --set-mark 1 |
|
||||
iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 988 |
|
||||
|
|
||||
iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -m set --match-set zapret dst -j MARK --set-mark 1 |
|
||||
iptables -t mangle -A PREROUTING -i eth1 -p tcp --dport 80 -m mark --mark 0x1/0x1 -j TPROXY --tproxy-mark 0x1/0x1 --on-port 988 |
|
||||
|
|
||||
For DNAT : |
|
||||
|
|
||||
# run tpws as user "tpws". its required to avoid loops. |
|
||||
sysctl -w net.ipv4.conf.eth1.route_localnet=1 |
|
||||
iptables -t nat -I PREROUTING -p tcp --dport 80 -j DNAT --to 127.0.0.127:988 |
|
||||
iptables -t nat -I OUTPUT -p tcp --dport 80 -m owner ! --uid-owner tpws -j DNAT --to 127.0.0.127:988 |
|
||||
|
|
||||
|
|
||||
Reset all iptable rules : |
|
||||
|
|
||||
iptables -F |
|
||||
iptables -X |
|
||||
iptables -t nat -F |
|
||||
iptables -t nat -X |
|
||||
iptables -t mangle -F |
|
||||
iptables -t mangle -X |
|
||||
iptables -t raw -F |
|
||||
iptables -t raw -X |
|
||||
|
|
||||
Reset iptable policies : |
|
||||
|
|
||||
iptables -P INPUT ACCEPT |
|
||||
iptables -P FORWARD ACCEPT |
|
||||
iptables -P OUTPUT ACCEPT |
|
||||
iptables -t mangle -P POSTROUTING ACCEPT |
|
||||
iptables -t mangle -P PREROUTING ACCEPT |
|
||||
iptables -t mangle -P INPUT ACCEPT |
|
||||
iptables -t mangle -P FORWARD ACCEPT |
|
||||
iptables -t mangle -P OUTPUT ACCEPT |
|
||||
iptables -t raw -P PREROUTING ACCEPT |
|
||||
iptables -t raw -P OUTPUT ACCEPT |
|
||||
@ -1,32 +0,0 @@ |
|||||
nftables test cheat sheet |
|
||||
simplified rules to test nfqws and tpws |
|
||||
|
|
||||
|
|
||||
For DNAT : |
|
||||
|
|
||||
# run tpws as user "tpws". its required to avoid loops. |
|
||||
|
|
||||
nft delete table inet ztest |
|
||||
nft create table inet ztest |
|
||||
nft add chain inet ztest pre "{type nat hook prerouting priority dstnat;}" |
|
||||
nft add rule inet ztest pre tcp dport "{80,443}" redirect to :988 |
|
||||
nft add chain inet ztest out "{type nat hook output priority -100;}" |
|
||||
nft add rule inet ztest out tcp dport "{80,443}" skuid != tpws redirect to :988 |
|
||||
|
|
||||
|
|
||||
For dpi desync attack : |
|
||||
|
|
||||
nft delete table inet ztest |
|
||||
nft create table inet ztest |
|
||||
nft add chain inet ztest post "{type filter hook postrouting priority mangle;}" |
|
||||
nft add rule inet ztest post meta mark and 0x40000000 == 0 tcp dport "{80,443}" ct original packets 1-6 queue num 200 bypass |
|
||||
nft add rule inet ztest post meta mark and 0x40000000 == 0 udp dport 443 ct original packets 1-6 queue num 200 bypass |
|
||||
|
|
||||
# auto hostlist with avoiding wrong ACK numbers in RST,ACK packets sent by russian DPI |
|
||||
sysctl net.netfilter.nf_conntrack_tcp_be_liberal=1 |
|
||||
nft add chain inet ztest pre "{type filter hook prerouting priority filter;}" |
|
||||
nft add rule inet ztest pre tcp sport "{80,443}" ct reply packets 1-3 queue num 200 bypass |
|
||||
|
|
||||
|
|
||||
show rules : nft list table inet ztest |
|
||||
delete table : nft delete table inet ztest |
|
||||
@ -1,120 +0,0 @@ |
|||||
nftables - это технология, пришедшая на замену iptables. |
|
||||
В ней собрали все, что относилось к различным iptables. А их немало. iptables, ip6tables, ebtables, arptables, ipset. |
|
||||
Весь код из разрозненных, но похожих компонент, собрали в одно целое с единым синтаксисом. |
|
||||
Добавили различные конструкции языка, позволяющие писать правила более лаконично, не повторяя одни и те же команды с небольшими различиями. |
|
||||
На nftables можно сделать почти все, что можно было сделать на iptables. Есть то, что можно сделать на nftables, но нельзя на iptables. |
|
||||
Есть и наоборот. |
|
||||
|
|
||||
К сожалению, не обошлось и без боли. |
|
||||
|
|
||||
Главная боль N1. Очень серьезная, актуальная для openwrt, и решения не видно. |
|
||||
|
|
||||
ipset-ы позволяли загонять пересекающиеся интервалы ip адресов или подсетей. |
|
||||
nftables sets это не позволяют. Любое пересечение вызывает ошибку. |
|
||||
Есть auto-merge, но это работает только в user mode в процессе nft, при условии, что весь блок адресов загоняется одной командой |
|
||||
и нет пересечений с уже имеющимся контентом в set. |
|
||||
Это не было бы критической проблемой, поскольку скрипты zapret и так загоняют ipset целиком. |
|
||||
Проблема в катастрофическом расходе памяти при операции загона больших интервальных листов, то есть с подсетями и диапазонами. |
|
||||
Чтобы загнать 100000 ipv4 записей, едва хватает 300 Mb памяти устройства. |
|
||||
При успехе операции в ядре список столько не занимает, но суть дела это не меняет. |
|
||||
Для традиционных linux систем это не проблема, но почти все роутеры загнутся. |
|
||||
Приемлемого решения не просматривается. |
|
||||
Сделать записи непересекающимися в листах - задача непростая. Потребуется переписать алгоритм auto-merge из nft, |
|
||||
но с пониженным расходом памяти. |
|
||||
Загонять записи по одному отдельными вызовами nft, игнорируя ошибки, займет вечность. |
|
||||
Загонять блоком отдельных команд, игнорируя ошибки, - nft такого не умеет. Похоже, при любой ошибке происходит откат всего скрипта. |
|
||||
К тому же при таком подходе будут неточности в итоговом результате. |
|
||||
Swap позволяет немного сгладить проблему, но лишь незначительно. |
|
||||
Скажем, если вдруг list загоняется без ошибок с 300 Mb памяти и с падением на 256 Mb, то swap спасает. |
|
||||
Если памяти становится 200 Mb, то swap уже не спасет. Все равно вызывается OOM killer, заодно убивая и другие процессы, кроме nft, |
|
||||
а это уже совсем плохо. Может быть убито что-то важное. |
|
||||
|
|
||||
Боль N2, не смертельная, но тоже не айс. |
|
||||
|
|
||||
Какие-то нерациональные алгоритмы разбора таблиц в nft. |
|
||||
Например, есть 1 большой set на 100000 элементов и 1 маленький на 2 элемента. |
|
||||
Чтобы просто пролистать мелкий set или добавить туда еще что-то nft будет мусолить несколько секунд. |
|
||||
Что он делает за это время ? Тащит из ядра огромный блоб, в котором все в куче, и разбирает его, чтобы выделить искомую мелочь ? |
|
||||
В какой-то мере удается это сгладить, обьединяя несколько команд в единый скрипт. |
|
||||
|
|
||||
Боль N3 |
|
||||
|
|
||||
Система nftables построена на виртуальной машине. Правила, которые вы пишите, переводятся в псевдокод VM. |
|
||||
Чтобы потом их показать , nft декомпилирует код и переводит в читаемый язык. |
|
||||
Это довольно сложно, и регулярно случаются баги, связанные с неверным отображением. |
|
||||
|
|
||||
Кроме этого, часто встречаются и баги парсера. |
|
||||
Например, все версии nft вплоть до 1.0.1 имеют баг, который не разрешает названия интерфейсов в кавычках в |
|
||||
определении flowtable. Без кавычек нельзя вставить интерфейсы , имя которых начинается с цифры. |
|
||||
OpenWRT решает эту проблему отдельным патчем в snapshot версии, но на традиционных системах и в openwrt 21.x- его нет. |
|
||||
Почему бы не наплевать на интерфейсы, начинающиеся с цифры ? Потому что для openwrt 6to4-6to4, 6in4-he-net - обычное явление. |
|
||||
На текущий момент этой проблемы в openwrt уже нет, если использовать актуальную версию. |
|
||||
|
|
||||
Но тем не менее, хоть nft и давно перешел отметку 1.0, всякая мелочь, особенно на не совсем стандартных правилах, |
|
||||
регулярно всплывает. Потому чем новее у вас будет версия nft, тем больше там выловлено проблем. |
|
||||
Здесь обновления важны, чтобы потом не мучаться из-за давно исправленного велосипеда. |
|
||||
|
|
||||
Боль N4 |
|
||||
|
|
||||
Невозможно , не копаясь в других таблицах и хуках, ничего не зная об их содержании, предотвратить DROP или REJECT. |
|
||||
Нельзя написать такое правило, которое что-то важное ACCEPTнет, игнорируя остальные хуки во всех таблицах. |
|
||||
Если у вас есть какой-то фаервол, и он что-то дропает, то как от этого отказаться, если надо временно что-то принять ? |
|
||||
Это особенность netfilter, он так работает, но в iptables есть лишь стандартные таблицы с их хуками, куда можно |
|
||||
вставить ACCEPT. Здесь хуков может быть сколько угодно в каких угодно таблицах. |
|
||||
Эта проблема частично ломает кайф от независимого управления таблицами. |
|
||||
|
|
||||
|
|
||||
Плюс N1, главный |
|
||||
|
|
||||
iptables хороши, когда ими пользуется кто-то один. Иначе это проходной двор. |
|
||||
Когда есть система управления фаерволом, то приходится как-то к ней прикручиваться, чтобы не нарушить ее работу |
|
||||
и управлять правилами синхронно. Нужно уметь внести и удалить отдельные правила когда это нужно, не трогая все остальное. |
|
||||
Некоторые системы управления фаерволом вообще не предполагают, чтобы кто-то еще лез в iptables, и это очень сильно портит жизнь. |
|
||||
У iptables есть предопределенный набор хуков netfilter с фиксированным приоритетом. |
|
||||
В nftables хуков можно создать неограниченное количество с выбранным приоритетом, управляя ими независимо в отдельных таблицах. |
|
||||
Система управления фаерволом может работать в одной таблице (fw4 в случае openwrt) и не трогать все остальное. |
|
||||
zapret может работать в другой таблице и не трогать систему управления фаерволом. Они друг другу не мешают. |
|
||||
Это снимает множество боли. |
|
||||
Но есть и исключение. nfset-ы - аналог ipset-ов - нельзя использовать из другой таблицы. Потому если вам нужен ipset, |
|
||||
создаваемый zapret скриптами, вам понадобится писать правила в той же таблице. Но нет никакой необходимости влезать в цепочки zapret. |
|
||||
Создаете свои цепочки и хуки и делаете в них что угодно. |
|
||||
|
|
||||
Плюс N2 |
|
||||
|
|
||||
Возможность выбора приоритета хука позволяет легко решить проблему хаотической и принудительной дефрагментацией L3 ipv6, |
|
||||
без танцев с загрузкой модулей ядра со специальными параметрами или перекомпиляцией nftables-nft. |
|
||||
Это же позволяет перехватить трафик после SNAT/MASQUERADE, что на iptables невозможно. |
|
||||
Атаки на проходящий трафик, ломающие NAT, крайне затруднены на iptables. |
|
||||
|
|
||||
Плюс N3 |
|
||||
|
|
||||
Наличие множеств (anonymous/named sets) позволяет не писать кучу однообразных правил там, где в iptables их пришлось бы написать. |
|
||||
|
|
||||
Плюс N4 |
|
||||
|
|
||||
Если у вас есть nftables, то там наверняка есть уже все или почти все. |
|
||||
Нет кучи разных модулей ядра и .so плагинов для iptables user-mode процесса. |
|
||||
Отдельные модули ядра есть, но их меньше, чем в iptables, и openwrt их делит на меньшее число пакетов, большинство из которых |
|
||||
и так ставятся по умолчанию. user-mode процесс nft и вовсе неделим. EXE-шник + lib. |
|
||||
|
|
||||
Плюс N5 |
|
||||
|
|
||||
Пишут, что nftables работают быстрее. Но это не точно и зависит от много чего. |
|
||||
В целом - чем меньше правил, тем выше скорость. Но в nftables правил можно писать меньше, значит скрость тоже может быть выше. |
|
||||
У разработчиков есть идея перевести backend nftables на BPF, а это наверняка будет существенно быстрее. |
|
||||
|
|
||||
|
|
||||
Выводы |
|
||||
|
|
||||
Без больших листов все почти прекрасно. Но большие ip листы убивают все. Не для домашних это роутеров. |
|
||||
А ipset-ы к nftables не прикрутить. |
|
||||
Зато есть возможность задействовать более продвинутые атаки, конфликтующие с NAT, которые на iptables могут быть невозможны. |
|
||||
Делать нечего. Openwrt отошел от iptables. С этим придется как-то жить. |
|
||||
Поэтому пришлось сделать для openwrt поддержку и iptables, и nftables (только с версии openwrt 21.xx, в более старых будут проблемы). |
|
||||
iptables можно задействовать на любой openwrt версии. |
|
||||
Если используется fw3, применяется старый механизм интеграции в fw3. |
|
||||
Если он не используется, то правилами iptables управляем как в традиционных linux системах - то есть с возможностью |
|
||||
запуска и остановки, а скрипт запуска вносит в том числе и правила iptables. |
|
||||
|
|
||||
На новых openwrt возможно снести nftables и firewall4 и установить firewall3 и iptables. |
|
||||
Если вам никак без больших ip листов на слабой системе, это может быть единственным спасением. |
|
||||
@ -1,274 +0,0 @@ |
|||||
# Быстрая настройка Linux/OpenWrt |
|
||||
|
|
||||
> [!CAUTION] |
|
||||
> Не пишите в issue вопросы типа "как скопировать файл", "как скачать", "как |
|
||||
> запустить" и т.п. То есть все, что касается базовых навыков обращения с ОС |
|
||||
> Linux. Эти вопросы будут закрывать сразу. Если у вас подобные вопросы |
|
||||
> возникают, рекомендую не использовать данный софт или искать помощь где-то в |
|
||||
> другом месте. То же самое могу сказать тем, кто хочет нажать 1 кнопку, чтобы |
|
||||
> все заработало, и совсем не хочет читать и изучать. Увы, такое не подвезли и |
|
||||
> не подвезут. Ищите другие более простые методы обхода. Этот метод **не для |
|
||||
> рядового пользователя**. |
|
||||
|
|
||||
|
|
||||
## Вступление |
|
||||
Специально для тех, кто хочет побыстрее начать, но не хочет слишком углубляться |
|
||||
в простыню [readme.md](readme.md). |
|
||||
|
|
||||
Обход DPI является хакерской методикой. Под этим словом понимается метод, |
|
||||
которому оказывается активное противодействие и поэтому автоматически не |
|
||||
гарантирована работоспособность в любых условиях и на любых ресурсах, требуется |
|
||||
настройка под специфические условия у вашего провайдера. Условия могут меняться |
|
||||
со временем, и методика может начинать или переставать работать, может |
|
||||
потребоваться повторный анализ ситуации. Могут обнаруживаться отдельные |
|
||||
ресурсы, которые заблокированы иначе, и которые не работают или перестали |
|
||||
работать. Могут и сломаться отдельные не заблокированные ресурсы. Поэтому очень |
|
||||
желательно иметь знания в области сетей, чтобы иметь возможность |
|
||||
проанализировать техническую ситуацию. Не будет лишним иметь обходные каналы |
|
||||
проксирования трафика на случай, если обход DPI не помогает. |
|
||||
|
|
||||
Вариант, когда вы нашли стратегию где-то в интернете и пытаетесь ее приспособить к своему случаю - заведомо проблемный. |
|
||||
Нет универсальной таблетки. Везде ситуация разная. В сети гуляют написанные кем-то откровенные глупости, которые тиражируются массово ничего не понимающей публикой. |
|
||||
Такие варианты чаще всего работают нестабильно, только на части ресурсов, только на части провайдеров, не работают вообще или ломают другие ресурсы. В худших случаях еще и устраивают флуд в сети. |
|
||||
Если даже вариант когда-то и работал неплохо, завтра он может перестать, а в сети останется устаревшая информация. |
|
||||
|
|
||||
Будем считать, что у вас есть система на базе традиционного **linux** или |
|
||||
**openwrt**. Если у вас традиционный linux - задача обойти блокировки только на |
|
||||
этой системе, если openwrt - обойти блокировки для подключенных устройств. Это |
|
||||
наиболее распространенный случай. |
|
||||
|
|
||||
|
|
||||
## Настройка |
|
||||
> [!TIP] |
|
||||
> Чтобы процедура установки сработала в штатном режиме на openwrt, нужно |
|
||||
> рассчитывать на свободное место около 1-2 Mb для установки самого zapret и |
|
||||
> необходимых дополнительных пакетов. Если места мало и нет возможности его |
|
||||
> увеличить за счет `extroot`, возможно придется отказаться от варианта простой |
|
||||
> установки и прикручивать в ручном режиме без имеющихся скриптов запуска. |
|
||||
> Можно использовать [облегченный `tpws` вариант](../init.d/openwrt-minimal), |
|
||||
> либо попробовать засунуть требуемые zapret дополнительные пакеты в сжатый |
|
||||
> образ `squashfs` с помощью `image builder` и перешить этим вариантом роутер. |
|
||||
|
|
||||
1. Скачайте последний [tar.gz релиз](https://github.com/bol-van/zapret/releases) в /tmp, распакуйте его, затем удалите архив. |
|
||||
Для openwrt и прошивок используйте вариант `openwrt-embedded`. |
|
||||
Для экономия места в /tmp можно качать через curl в stdout и сразу распаковывать. |
|
||||
Пример под openwrt для версии zapret 71.4 (для других URL отличается) : |
|
||||
``` |
|
||||
$ curl -Lo - https://github.com/bol-van/zapret/releases/download/v71.4/zapret-v71.4-openwrt-embedded.tar.gz | tar -zx |
|
||||
$ wget -O - https://github.com/bol-van/zapret/releases/download/v71.4/zapret-v71.4-openwrt-embedded.tar.gz | tar -zx |
|
||||
``` |
|
||||
Пример под традиционный linux для версии zapret 71.4 (для других URL отличается) : |
|
||||
``` |
|
||||
$ curl -Lo - https://github.com/bol-van/zapret/releases/download/v71.4/zapret-v71.4.tar.gz | tar -zx |
|
||||
$ wget -O - https://github.com/bol-van/zapret/releases/download/v71.4/zapret-v71.4.tar.gz | tar -zx |
|
||||
``` |
|
||||
curl должен быть предварительно установлен. Но он в любом случае понадобится далее. |
|
||||
Вариант с wget будет работать только если установленный wget поддерживает https. |
|
||||
|
|
||||
2. Убедитесь, что у вас отключены все средства обхода блокировок, в том числе и |
|
||||
сам zapret. Гарантированно уберет zapret скрипт `uninstall_easy.sh`. |
|
||||
|
|
||||
3. Если вы работаете в виртуальной машине, необходимо использовать соединение с |
|
||||
сетью в режиме bridge. NAT **не** подходит. |
|
||||
|
|
||||
4. Выполните однократные действия по установке требуемых пакетов в ОС и |
|
||||
настройке исполняемых файлов правильной архитектуры: |
|
||||
```sh |
|
||||
$ install_bin.sh |
|
||||
$ install_prereq.sh |
|
||||
``` |
|
||||
|
|
||||
> Вас могут спросить о типе фаервола (iptables/nftables) и использовании |
|
||||
> ipv6. Это нужно для установки правильных пакетов в ОС, чтобы не |
|
||||
> устанавливать лишнее. |
|
||||
|
|
||||
5. Запустите `blockcheck.sh`. Скрипт вначале проверяет DNS. Если выводятся |
|
||||
сообщения о подмене адресов, то нужно будет решить проблему с DNS. |
|
||||
`blockcheck.sh` перейдет в этом случае на DoH и будет пытаться получить и |
|
||||
использовать реальные IP адреса. Но если вы не настроите решение этой |
|
||||
проблемы, обход будет работать только для тех программ или ОС, которые сами |
|
||||
реализуют механизмы SecureDNS. Для других программ обход работать не будет. |
|
||||
|
|
||||
> Решение проблемы DNS выходит за рамки проекта. Обычно она решается либо |
|
||||
> заменой DNS серверов от провайдера на публичные (`1.1.1.1`, `8.8.8.8`), |
|
||||
> либо в случае перехвата провайдером обращений к сторонним серверам - через |
|
||||
> специальные средства шифрования DNS запросов, такие как `dnscrypt`, `DoT`, |
|
||||
> `DoH`. |
|
||||
> |
|
||||
> Еще один эффективный вариант - использовать ресолвер от yandex |
|
||||
> (`77.88.8.88`) на нестандартном порту `1253`. Многие провайдеры не |
|
||||
> анализируют обращения к DNS на нестандартных портах. |
|
||||
> |
|
||||
> Проверить работает ли этот вариант можно так: |
|
||||
> ```sh |
|
||||
> $ dig -p 53 @77.88.8.88 rutracker.org |
|
||||
> $ dig -p 1253 @77.88.8.88 rutracker.org |
|
||||
> ``` |
|
||||
> |
|
||||
> Если DNS действительно подменяется, и ответ на эти 2 команды разный, |
|
||||
> значит метод вероятно работает. |
|
||||
> |
|
||||
> В openwrt DNS на нестандартном порту можно прописать в `/etc/config/dhcp` |
|
||||
> таким способом: |
|
||||
> |
|
||||
> ``` |
|
||||
> config dnsmasq |
|
||||
> <...> |
|
||||
> list server '77.88.8.88#1253' |
|
||||
> ``` |
|
||||
> |
|
||||
> Если настройки IP и DNS получаются автоматически от провайдера, в |
|
||||
> `/etc/config/network` найдите секцию интерфейса `wan` и сделайте так: |
|
||||
> |
|
||||
> ``` |
|
||||
> config interface 'wan' |
|
||||
> <...> |
|
||||
> option peerdns '0' |
|
||||
> ``` |
|
||||
> |
|
||||
> ```sh |
|
||||
> $ /etc/init.d/network restart |
|
||||
> $ /etc/init.d/dnsmasq restart |
|
||||
> ``` |
|
||||
> |
|
||||
> Если это не подходит, можно перенаправлять обращения на UDP и TCP порты |
|
||||
> `53` вашего DNS сервера на `77.88.8.88:1253` средствами |
|
||||
> `iptables`/`nftables`. В `/etc/resolv.conf` нельзя прописать DNS на |
|
||||
> нестандартном порту. |
|
||||
|
|
||||
6. `blockcheck.sh` позволяет выявить рабочую стратегию обхода блокировок. По |
|
||||
результатам скрипта нужно понять какой вариант будете использовать : `nfqws` |
|
||||
или `tpws` и запомнить найденные стратегии для дальнейшего применения. |
|
||||
|
|
||||
Следует понимать, что скрипт проверяет доступность только конкретного |
|
||||
домена, который вы вводите в начале, конкретной программой curl. |
|
||||
У разных клиентов есть свой фингерпринт. У броузеров один, у curl другой. |
|
||||
Может применяться или не применяться многопакетный TLS с постквантовой криптографией (kyber). |
|
||||
От этого может зависеть работоспособность стратегий. |
|
||||
Обычно остальные домены блокированы подобным образом, **но не факт**. |
|
||||
Бывают специальные блокировки. Некоторые параметры требуют тюнинга под "общий знаменатель". |
|
||||
В большинстве случаев можно объединить несколько стратегий в одну универсальную, и это **крайне |
|
||||
желательно**, но это требует понимания как работают стратегии. zapret **не может |
|
||||
пробить блокировку по IP адресу**. Для проверки нескольких доменов вводите |
|
||||
их через пробел. |
|
||||
|
|
||||
> Сейчас блокираторы ставят на магистральных каналах. В сложных случаях у |
|
||||
> вас может быть несколько маршрутов с различной длиной по ХОПам, с DPI на |
|
||||
> разных хопах. Приходится преодолевать целый зоопарк DPI, которые еще и |
|
||||
> включаются в работу хаотичным образом или образом, зависящим от |
|
||||
> направления (IP сервера). скрипт не всегда может выдать вам в итогах |
|
||||
> оптимальную стратегию, которую надо просто переписать в настройки. В |
|
||||
> некоторых случаях надо реально думать что происходит, анализируя результат |
|
||||
> на разных стратегиях. |
|
||||
> |
|
||||
> Далее, имея понимание что работает на http, https, quic нужно |
|
||||
> сконструировать параметры запуска `tpws` и/или `nfqws` с использованием |
|
||||
> мультистратегии. Как работают мультистратегии описано в [readme.md](./readme.md#множественные-стратегии). |
|
||||
> |
|
||||
> Если кратко, то обычно параметры конструируются так: |
|
||||
> ```sh |
|
||||
> "--filter-udp=443 'параметры для quic' <HOSTLIST_NOAUTO> --new |
|
||||
> --filter-tcp=80,443 'объединенные параметры для http и https' <HOSTLIST>" |
|
||||
> ``` |
|
||||
> |
|
||||
> Или так: |
|
||||
> ```sh |
|
||||
> "--filter-udp=443 'параметры для quic' <HOSTLIST_NOAUTO> --new |
|
||||
> --filter-tcp=80 'параметры для http' <HOSTLIST> --new |
|
||||
> --filter-tcp=443 'параметры для https' <HOSTLIST>" |
|
||||
> ``` |
|
||||
> |
|
||||
> `<HOSTLIST>` и `<HOSTLIST_NOAUTO>` так и пишутся. Их не надо на что-то |
|
||||
> заменять. Это сделают скрипты запуска, если вы выбрали режим фильтрации по |
|
||||
> хостлистам, и уберут в противном случае. Если для какого-то протокола надо |
|
||||
> дурить все без стандартного хостлиста - просто уберите оттуда `<HOSTLIST>` |
|
||||
> и `<HOSTLIST_NOAUTO>`. Можно писать свои параметры `--hostlist` и |
|
||||
> `--hostlist-exclude` для дополнительных хостлистов или в профилях |
|
||||
> специализаций под конкретный ресурс. В последнем случае стандартный |
|
||||
> хостлист там не нужен. Следует избегать указания собственных параметров |
|
||||
> `--hostlist` на листы из директории ipset. Эта логика включена в |
|
||||
> `<HOSTLIST>` и `<HOSTLIST_NOAUTO>`. Отличие `<HOSTLIST_NOAUTO>` в том, что |
|
||||
> стандартный автолист по этому профилю используется как обычный, то есть |
|
||||
> без автоматического добавления доменов. Однако, добавления в других |
|
||||
> профилях автоматически отражаются во всех остальных. |
|
||||
> |
|
||||
> Если стратегии отличаются по версии ip протокола, и вы не можете их |
|
||||
> обьединить, фильтр пишется так: |
|
||||
> ```sh |
|
||||
> "--filter-l3=ipv4 --filter-udp=443 lпараметры для quic ipv4' <HOSTLIST_NOAUTO> --new |
|
||||
> --filter-l3=ipv4 --filter-tcp=80 'параметры для http ipv4' <HOSTLIST> --new |
|
||||
> --filter-l3=ipv4 --filter-tcp=443 'параметры для https ipv4' <HOSTLIST> --new |
|
||||
> --filter-l3=ipv6 --filter-udp=443 'параметры для quic ipv6' <HOSTLIST_NOAUTO> --new |
|
||||
> --filter-l3=ipv6 --filter-tcp=80 'параметры для http ipv6' <HOSTLIST> --new |
|
||||
> --filter-l3=ipv6 --filter-tcp=443 'параметры для https ipv6' <HOSTLIST>" |
|
||||
> ``` |
|
||||
> |
|
||||
> Но здесь совсем "копи-пастный" вариант. Чем больше вы объедините стратегий и |
|
||||
> сократите их общее количество, тем будет лучше. |
|
||||
> |
|
||||
> Если вам не нужно дурение отдельных протоколов, лучше всего будет убрать |
|
||||
> лишние порты из системы перехвата трафика через параметры `TPWS_PORTS`, |
|
||||
> `NFQWS_PORTS_TCP`, `NFQWS_PORTS_UDP` и убрать соответствующие им профили |
|
||||
> мультистратегии. |
|
||||
> |
|
||||
> | Протокол | Порт | Примечание | |
|
||||
> |---|---|---| |
|
||||
> | `tcp` | `80` | `http` соединение | |
|
||||
> | `tcp` | `443` | `https` соединение | |
|
||||
> | `udp` | `443` | `quic` соединение | |
|
||||
> |
|
||||
> Если используются методы нулевой фазы десинхронизации (`--mss`, |
|
||||
> `--wssize`, `--dpi-desync=syndata`) и режим фильтрации `hostlist`, то все |
|
||||
> параметры, относящиеся к этим методам, следует помещать в отдельные |
|
||||
> профили мультистратегии, которые получат управление до определения имени |
|
||||
> хоста. Необходимо понимать алгоритм работы мультистратегий. Самым надежным |
|
||||
> вариантом будет дублирование этих параметров на 2 профиля. Какой-нибудь |
|
||||
> сработает в зависимости от параметра `MODE_FILTER`. |
|
||||
> |
|
||||
> ```sh |
|
||||
> "--filter-tcp=80 'параметры для http' <HOSTLIST> --new |
|
||||
> --filter-tcp=443 'параметры для https' --wssize 1:6 <HOSTLIST> --new |
|
||||
> --filter-tcp=443 --wssize 1:6" |
|
||||
> ``` |
|
||||
> |
|
||||
> В этом примере `wssize` будет применяться всегда к порту tcp `443` вне |
|
||||
> зависимости от параметра `MODE_FILTER`. Хостлист будет игнорироваться, |
|
||||
> если таковой имеется. К http применять `wssize` вредно и бессмысленно. |
|
||||
> |
|
||||
> Иногда требуется дописать к стратегиям свои собственные параметры. |
|
||||
> Например, нужно изменить количество повторов фейков или задать свой фейк. |
|
||||
> Это делается через шелл-переменные `PKTWS_EXTRA`, `TPWS_EXTRA`. |
|
||||
> |
|
||||
> ```PKTWS_EXTRA="--dpi-desync-repeats=10 --dpi-desync-fake-tls=/tmp/tls.bin" ./blockcheck.sh``` |
|
||||
> |
|
||||
> Перебор всех комбинаций может привести к ожиданию неделями, поэтому выбран разумный |
|
||||
> костяк проверки, на который вы можете навешивать свои кустомизации. |
|
||||
|
|
||||
7. Запустите скрипт облегченной установки - `install_easy.sh` Выберите `nfqws` |
|
||||
и/или `tpws`, затем согласитесь на редактирование параметров. Откроется |
|
||||
редактор, куда впишите созданную на предыдущем этапе стратегию. |
|
||||
|
|
||||
8. На все остальные вопросы `install_easy.sh` отвечайте согласно выводимой |
|
||||
аннотации. |
|
||||
|
|
||||
9. Удалите директорию из /tmp, откуда производилась установка. |
|
||||
|
|
||||
## Полное удаление |
|
||||
|
|
||||
1. Прогоните `/opt/zapret/uninstall_easy.sh`. |
|
||||
2. Cогласитесь на удаление зависимостей в openwrt. |
|
||||
3. Удалите каталог `/opt/zapret`. |
|
||||
|
|
||||
## Итог |
|
||||
Это минимальная инструкция, чтобы быстро сориентироваться с чего начать. |
|
||||
Однако, это не гарантированное решение и в некоторых случаях вы не обойдетесь |
|
||||
без знаний и основного "талмуда". Подробности и полное техническое описание |
|
||||
расписаны в [README](readme.md). |
|
||||
|
|
||||
Если ломаются отдельные **не заблокированные** ресурсы, следует вносить их в |
|
||||
исключения, либо пользоваться ограничивающим `ipset` или хост листом. Лучше |
|
||||
подбирать такие стратегии, которые вызывают минимальные поломки. Есть стратегии |
|
||||
довольно безобидные, а есть сильно ломающие, которые подходят только для |
|
||||
точечного пробития отдельных ресурсов, когда ничего лучше нет. Хорошая |
|
||||
стратегия может сильно ломать из-за плохо подобранных ограничителей для фейков |
|
||||
\- ttl, fooling. |
|
||||
@ -1,205 +0,0 @@ |
|||||
# Быстрая настройка Windows |
|
||||
|
|
||||
Специально для тех, кто хочет побыстрее начать, но не хочет слишком углубляться в простыню [readme.md](./readme.md). |
|
||||
> [!CAUTION] |
|
||||
> Как обычно, компьютерная грамотность ложится полностью на вас. |
|
||||
> Вы должны уметь работать с консолью windows и иметь минимальные навыки обращения с командными файлами `bat`, `cmd`. |
|
||||
> Если грамотность отсутствует и возникает куча _"как?"_ на базовых вещах, значит эта программа не для вас. |
|
||||
> Разработчик не будет отвечать на вопросы из серии школы компьютерной грамотности. |
|
||||
> Если вы все-таки хотите продолжать, задавайте вопросы в дискуссиях на github или на форумах. |
|
||||
> Возможно, кто-то вам поможет. Но не надо писать issue на github. Они будут закрываться сразу. |
|
||||
|
|
||||
## Немного разъяснений |
|
||||
|
|
||||
Обход DPI является хакерской методикой. Под этим словом понимается метод, которому оказывается активное противодействие и поэтому |
|
||||
автоматически не гарантирована работоспособность в любых условиях и на любых ресурсах, |
|
||||
требуется настройка под специфические условия у вашего провайдера. Условия могут меняться со временем, |
|
||||
и методика может начинать или переставать работать, может потребоваться повторный анализ ситуации. |
|
||||
Могут обнаруживаться отдельные ресурсы, которые заблокированы иначе, и которые не работают или перестали работать. |
|
||||
Могут и сломаться отдельные незаблокированные ресурсы. |
|
||||
Поэтому очень желательно иметь знания в области сетей, чтобы иметь возможность проанализировать техническую ситуацию. |
|
||||
Не будет лишним иметь обходные каналы проксирования трафика на случай, если обход DPI не помогает. |
|
||||
|
|
||||
Вариант, когда вы нашли стратегию где-то в интернете и пытаетесь ее приспособить к своему случаю - заведомо проблемный. |
|
||||
Нет универсальной таблетки. Везде ситуация разная. В сети гуляют написанные кем-то откровенные глупости, которые тиражируются массово ничего не понимающей публикой. |
|
||||
Такие варианты чаще всего работают нестабильно, только на части ресурсов, только на части провайдеров, не работают вообще или ломают другие ресурсы. В худших случаях еще и устраивают флуд в сети. |
|
||||
Если даже вариант когда-то и работал неплохо, завтра он может перестать, а в сети останется устаревшая информация. |
|
||||
|
|
||||
Особо осторожным нужно быть со сторонними сборками. Там могут быть вирусы. Не в каждой сборке, но уже были замечены скаммеры. |
|
||||
Видео на ютубе как просто обойти блокировку, прилагающийся архив, в котором какая-то ерунда, написанная на питоне, скачивающая зловред. |
|
||||
|
|
||||
Будем считать, что у вас есть windows 7 или выше. Задача - обойти блокировки с самой системы. |
|
||||
|
|
||||
## Я ЧТО-ТО ДЕЛАЛ, НЕ ПОМОГЛО, КАК ТЕПЕРЬ ЭТО УДАЛИТЬ |
|
||||
|
|
||||
Если вы не устанавливали zapret как службу или запланированную задачу (а это требует редактирования cmd файлов), |
|
||||
достаточно закрыть окно с winws и запустить windivert_delete.cmd. |
|
||||
Альтернатива - перезагрузить компьютер. |
|
||||
После чего можно удалить папку с zapret. На этом деинсталляция закончена. |
|
||||
Если же вы устанавливали zapret как службу, то вы наверняка знаете как ее удалить. |
|
||||
|
|
||||
Если вдруг среди того, на что вы нажимали, есть слова "general", "alt", ".bat", "автозапуск", или же есть файлы, которые отсутствуют |
|
||||
в оригинальных репозиториях, то это сборка. Вы не получите ответа как ее удалить от разработчика zapret. |
|
||||
Спрашивайте самих сборщиков. Разработчик не предоставляет простого решения, этим занимаются сборщики, но они и сами отвечают за свои продукты. |
|
||||
|
|
||||
## Настройка |
|
||||
|
|
||||
1. Скачайте и распакуйте архив https://github.com/bol-van/zapret-win-bundle/archive/refs/heads/master.zip. |
|
||||
|
|
||||
2. Если у вас Windows 7 x64, однократно запустите `win7/install_win7.cmd`. Батник заменит файлы windivert на совместимую с Windows 7 версию. |
|
||||
|
|
||||
> Для 32-битных систем Windows нет готового полного варианта. |
|
||||
|
|
||||
> На windows 11 arm64 выполните `arm64/install_arm64.cmd` от имени администратора и перезагрузите компьютер. |
|
||||
> Читайте [docs/windows.md](./windows.md) |
|
||||
> |
|
||||
> Имейте в виду, что антивирусы могут плохо реагировать на windivert. |
|
||||
> cygwin так же имеет внушительный список несовместимостей с антивирусами, хотя современные антивирусы |
|
||||
> более-менее научились с ним дружить. |
|
||||
> Если проблема имеет место , используйте исключения. Если не помогает - отключайте антивирус совсем. |
|
||||
|
|
||||
3. Убедитесь, что у вас отключены все средства обхода блокировок, в том числе и сам zapret. |
|
||||
|
|
||||
4. Если вы работаете в виртуальной машине, необходимо использовать соединение с сетью в режиме bridge. nat не подходит |
|
||||
|
|
||||
5. Запустите `blockcheck\blockcheck.cmd`. blockcheck в начале проверяет **DNS**. |
|
||||
Если выводятся сообщения о подмене адресов, то нужно будет решить проблему с **DNS**. |
|
||||
blockcheck перейдет в этом случае на **DoH** _(DNS over HTTPS)_ и будет пытаться получить и использовать реальные IP адреса. |
|
||||
Но если вы не настроите решение этой проблемы, обход будет работать только для тех программ, |
|
||||
которые сами реализуют механизмы SecureDNS. Для других программ обход работать не будет. |
|
||||
|
|
||||
> Решение проблемы DNS выходит за рамки проекта. Обычно она решается либо заменой DNS серверов |
|
||||
> от провайдера на публичные (`1.1.1.1`, `8.8.8.8`), либо в случае перехвата провайдером обращений |
|
||||
> к сторонним серверам - через специальные средства шифрования DNS запросов, такие как [dnscrypt](https://www.dnscrypt.org/), **DoT** _(DNS over TLS)_, **DoH**. |
|
||||
> В современных броузерах чаще всего DoH включен по умолчанию, но curl будет использовать обычный системный DNS. |
|
||||
> win11 поддерживает системные DoH из коробки. Они не настроены по умолчанию. |
|
||||
> В последних билдах win10 существует неофициальный обходной путь для включения DoH. |
|
||||
> Для остальных систем нужно стороннее решение, работающие по принципу DNS proxy. |
|
||||
> |
|
||||
> Тут все разжевано как и где это включается : https://hackware.ru/?p=13707 |
|
||||
|
|
||||
6. blockcheck позволяет выявить рабочую стратегию обхода блокировок. |
|
||||
Лог скрипта будет сохранен в `blockcheck\blockcheck.log`. |
|
||||
Запомните/перепишите найденные стратегии. |
|
||||
|
|
||||
Следует понимать, что скрипт проверяет доступность только конкретного |
|
||||
домена, который вы вводите в начале, конкретной программой curl. |
|
||||
У разных клиентов есть свой фингерпринт. У броузеров один, у curl другой. |
|
||||
Может применяться или не применяться многопакетный TLS с постквантовой криптографией (kyber). |
|
||||
От этого может зависеть работоспособность стратегий. |
|
||||
Обычно остальные домены блокированы подобным образом, **но не факт**. |
|
||||
Бывают специальные блокировки. Некоторые параметры требуют тюнинга под "общий знаменатель". |
|
||||
В большинстве случаев можно объединить несколько стратегий в одну универсальную, и это **крайне |
|
||||
желательно**, но это требует понимания как работают стратегии. zapret **не может |
|
||||
пробить блокировку по IP адресу**. Для проверки нескольких доменов вводите их через пробел. |
|
||||
|
|
||||
> Сейчас блокираторы ставят на магистральных каналах. В сложных случаях у |
|
||||
> вас может быть несколько маршрутов с различной длиной по ХОПам, с DPI на |
|
||||
> разных хопах. Приходится преодолевать целый зоопарк DPI, которые еще и |
|
||||
> включаются в работу хаотичным образом или образом, зависящим от |
|
||||
> направления (IP сервера). скрипт не всегда может выдать вам в итогах |
|
||||
> оптимальную стратегию, которую надо просто переписать в настройки. В |
|
||||
> некоторых случаях надо реально думать что происходит, анализируя результат |
|
||||
> на разных стратегиях. |
|
||||
> |
|
||||
> Далее, имея понимание что работает на http, https, quic, нужно сконструировать параметры запуска winws |
|
||||
> с использованием мультистратегии. Как работают мультистратегии описано в [readme.md](./readme.md#множественные-стратегии). |
|
||||
> |
|
||||
> Прежде всего вам нужно собрать фильтр перехватываемого трафика. Это делается через параметры |
|
||||
> `--wf-l3`, `--wf-tcp`, `--wf-udp`. |
|
||||
> `--wf-l3` относится к версии ip протокола - ipv4 или ipv6. |
|
||||
> `--wf-tcp` и `--wf-udp` содержат перечень портов или диапазонов портов через запятую. |
|
||||
> |
|
||||
> Пример стандартного фильтра для перехвата http, https, quic : `--wf-tcp=80,443` `--wf-udp=443` |
|
||||
> |
|
||||
> Фильтр должен быть минимально необходимым. Перехват лишнего трафика приведет только к бессмысленному расходованию ресурсов процессора и замедлению интернета. |
|
||||
> |
|
||||
> Если кратко по мультистратегии, то обычно параметры конструируются так : |
|
||||
> ``` |
|
||||
> --filter-udp=443 'параметры для quic' --new |
|
||||
> --filter-tcp=80,443 'обьединенные параметры для http и https' |
|
||||
> ``` |
|
||||
> |
|
||||
> Или так : |
|
||||
> ``` |
|
||||
> --filter-udp=443 'параметры для quic' --new |
|
||||
> --filter-tcp=80 'параметры для http' --new |
|
||||
> --filter-tcp=443 'параметры для https' |
|
||||
> ``` |
|
||||
> |
|
||||
> Если стратегии отличаются по версии ip протокола, и вы не можете их обьединить, фильтр пишется так : |
|
||||
> ``` |
|
||||
> --filter-l3=ipv4 --filter-udp=443 "параметры для quic ipv4" --new |
|
||||
> --filter-l3=ipv4 --filter-tcp=80 'параметры для http ipv4' --new |
|
||||
> --filter-l3=ipv4 --filter-tcp=443 'параметры для https ipv4' --new |
|
||||
> --filter-l3=ipv6 --filter-udp=443 "параметры для quic ipv6" --new |
|
||||
> --filter-l3=ipv6 --filter-tcp=80 "параметры для http ipv6" --new |
|
||||
> --filter-l3=ipv6 --filter-tcp=443 "параметры для https ipv6" |
|
||||
> ``` |
|
||||
> |
|
||||
> Но здесь совсем _"копи-пастный"_ вариант. |
|
||||
> Чем больше вы обьедините стратегий и сократите их общее количество, тем будет лучше. |
|
||||
> |
|
||||
> Если вам не нужно дурение отдельных протоколов, лучше всего будет их убрать из системы перехвата трафика через |
|
||||
> параметры `--wf-*` и убрать соответствующие им профили мультистратегии. |
|
||||
> tcp 80 - http, tcp 443 - https, udp 443 - quic. |
|
||||
> |
|
||||
> Если используются методы нулевой фазы десинхронизации (`--mss`, `--wssize`, `--dpi-desync=syndata`) и фильтрация hostlist, |
|
||||
> то все параметры, относящиеся к этим методам, следует помещать в отдельные профили мультистратегии, которые получат |
|
||||
> управление до определения имени хоста. Необходимо понимать алгоритм работы мультистратегий. |
|
||||
> |
|
||||
> ``` |
|
||||
> --filter-tcp=80 'параметры для http' --new |
|
||||
> --filter-tcp=443 'параметры для https' --hostlist=d:/users/user/temp/list.txt --new |
|
||||
> --filter-tcp=443 --wssize 1:6 |
|
||||
> ``` |
|
||||
> |
|
||||
> autohostlist профиль приоритетен, поэтому wssize нужно писать туда : |
|
||||
> |
|
||||
> ``` |
|
||||
> --filter-tcp=80 'параметры для http' --new |
|
||||
> --filter-tcp=443 'параметры для https' --wssize 1:6 --hostlist-auto=d:/users/user/temp/autolist.txt |
|
||||
> ``` |
|
||||
> |
|
||||
> В этих примерах wssize будет применяться всегда к порту tcp 443, а хостлист будет игнорироваться. |
|
||||
> К http применять wssize вредно и бессмысленно. |
|
||||
> |
|
||||
> Иногда требуется дописать к стратегиям свои собственные параметры. |
|
||||
> Например, нужно изменить количество повторов фейков или задать свой фейк. |
|
||||
> Это делается через шелл-переменные `PKTWS_EXTRA`, `TPWS_EXTRA`. Пользуйтесь шеллом `cygwin/cygwin-admin.cmd`. |
|
||||
> |
|
||||
> ```PKTWS_EXTRA="--dpi-desync-repeats=10 --dpi-desync-fake-tls=/tmp/tls.bin" blockcheck``` |
|
||||
> |
|
||||
> Перебор всех комбинаций может привести к ожиданию неделями, поэтому выбран разумный |
|
||||
> костяк проверки, на который вы можете навешивать свои кустомизации. |
|
||||
|
|
||||
7. Протестируйте найденные стратегии на winws. Winws следует брать из zapret-winws. |
|
||||
Для этого откройте командную строку windows от имени администратора в директории zapret-winws. |
|
||||
Проще всего это сделать через `_CMD_ADMIN.cmd`. Он сам поднимет права и зайдет в нужную директорию. |
|
||||
|
|
||||
8. Обеспечьте удобную загрузку обхода блокировок. |
|
||||
|
|
||||
> Есть 2 варианта. Ручной запуск через ярлык или автоматический при старте системы, вне контекста текущего пользователя. |
|
||||
> Последний вариант разделяется на запуск через планировщик задач и через службы windows. |
|
||||
> |
|
||||
> Если хотите ручной запуск, скопируйте `preset_russia.cmd` в `preset_my.cmd` (`<ваше_название>.cmd`) и адаптируйте его под ваши параметра запуска. |
|
||||
> Потом можно создать ярлык на рабочем столе на `preset_my.cmd`. Не забудьте, что требуется запускать от имени администратора. |
|
||||
> |
|
||||
> Но лучше будет сделать неинтерактивный автоматический запуск вместе с системой. |
|
||||
> В zapret-winws есть командные файлы `task_*`, предназначенные для управления задачами планировщика. |
|
||||
> Там следует поменять содержимое переменной `WINWS1` на свои параметры запуска winws. Пути с пробелами нужно экранировать кавычками с обратным слэшем : `\"`. |
|
||||
> После создания задач запустите их. Проверьте, что обход встает после перезагрузки windows. |
|
||||
> |
|
||||
> Аналогично настраиваются и службы windows. Смотрите `service_*.cmd` |
|
||||
|
|
||||
9. Если ломаются отдельные незаблокированные ресурсы, нужно пользоваться ограничивающим |
|
||||
ipset или хост листом. Читайте основной талмуд [readme.md](./readme.md) ради подробностей. |
|
||||
Но еще лучше будет подбирать такие стратегии, которые ломают минимум. |
|
||||
Есть стратегии довольно безобидные, а есть сильно ломающие, которые подходят только для точечного пробития отдельных ресурсов, |
|
||||
когда ничего лучше нет. Хорошая стратегия может сильно ломать из-за плохо подобранных ограничителей для фейков - ttl, fooling. |
|
||||
|
|
||||
> [!CAUTION] |
|
||||
> Это минимальная инструкция, чтобы соориентироваться с чего начать. Однако, это - не панацея. |
|
||||
> В некоторых случаях вы не обойдетесь без знаний и основного "талмуда". |
|
||||
|
|
||||
Подробности и полное техническое описание расписаны в [readme.md](./readme.md) |
|
||||
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -1,194 +0,0 @@ |
|||||
Данный мануал пишется не как копипастная инструкция, а как помощь уже соображающему. |
|
||||
Если вы не знаете основ сетей, linux, openwrt, а пытаетесь что-то скопипастить отсюда без малейшего |
|
||||
понимания смысла, то маловероятно, что у вас что-то заработает. Не тратье свое время напрасно. |
|
||||
Цель - донести принципы как это настраивается вообще, а не указать какую буковку где вписать. |
|
||||
|
|
||||
|
|
||||
Прозрачный выборочный заворот tcp соединений на роутере через socks |
|
||||
|
|
||||
Tor поддерживает "из коробки" режим transparent proxy. Это можно использовать в теории, но практически - только на роутерах с 128 мб памяти и выше. И тор еще и тормозной. |
|
||||
Другой вариант напрашивается, если у вас есть доступ к какой-нибудь unix системе с SSH, где сайты не блокируются. Например, у вас есть VPS вне России. |
|
||||
Понятийно требуются следующие шаги : |
|
||||
1) Выделять IP, на которые надо проксировать трафик. У нас уже имеется ipset "zapret", технология создания которого отработана. |
|
||||
2) Сделать так, чтобы все время при загрузке системы на некотором порту возникал socks. |
|
||||
3) Установить transparent соксификатор. Redsocks прекрасно подошел на эту роль. |
|
||||
4) Завернуть через iptables или nftables трафик с порта назначения 443 и на ip адреса из ipset/nfset 'zapret' на соксификатор |
|
||||
Тоже самое сделать с ipset/nfset 'ipban' для всех tcp портов. |
|
||||
Буду рассматривать систему на базе openwrt, где уже установлена система обхода dpi "zapret". |
|
||||
Если вам не нужны функции обхода DPI, его можно не включать. Обновление фильтра от этого не зависит. |
|
||||
|
|
||||
|
|
||||
* Сделать так, чтобы все время при загрузке системы на некотором порту возникал socks |
|
||||
|
|
||||
Т.к. дефолтный dropbear клиент не поддерживает создание socks, то для начала придется заменить dropbear ssh client на openssh : пакеты openssh-client и openssh-client-utils. |
|
||||
Устанавливать их нужно с опцией opkg --force-overwrite, поскольку они перепишут ssh клиент от dropbear. |
|
||||
После установки пакетов расслабим неоправданно жестокие права : chmod 755 /etc/ssh. |
|
||||
Следует создать пользователя, под которым будем крутить ssh client. Допустим, это будет 'proxy'. |
|
||||
Сначала установить пакеты shadow-useradd и shadow-su. |
|
||||
------------------ |
|
||||
useradd -s /bin/false -d /home/proxy proxy |
|
||||
mkdir -p /home/proxy |
|
||||
chown proxy:proxy /home/proxy |
|
||||
------------------ |
|
||||
Сгенерируем ключ RSA для доступа к ssh серверу. |
|
||||
------------------ |
|
||||
su -s /bin/ash proxy |
|
||||
cd |
|
||||
mkdir -m 700 .ssh |
|
||||
cd .ssh |
|
||||
ssh-keygen |
|
||||
ls |
|
||||
exit |
|
||||
------------------ |
|
||||
Должны получиться файлы id_rsa и id_rsa.pub. |
|
||||
Строчку из id_rsa.pub следует добавить на ssh сервер в файл $HOME/.ssh/authorized_keys. |
|
||||
Более подробно о доступе к ssh через авторизацию по ключам : https://beget.com/ru/articles/ssh_by_key |
|
||||
Предположим, ваш ssh сервер - vps.mydomain.com, пользователь называется 'proxy'. |
|
||||
Проверить подключение можно так : ssh -N -D 1098 -l proxy vps.mydomain.com. |
|
||||
Сделайте это под пользователем "proxy", поскольку при первом подключении ssh спросит о правильности hostkey. |
|
||||
Соединение может отвалиться в любой момент, поэтому нужно зациклить запуск ssh. |
|
||||
Для этого лучший вариант - использовать procd - упрощенная замена systemd на openwrt версий BB и выше. |
|
||||
--- /etc/init.d/socks_vps --- |
|
||||
#!/bin/sh /etc/rc.common |
|
||||
START=50 |
|
||||
STOP=50 |
|
||||
USE_PROCD=1 |
|
||||
USERNAME=proxy |
|
||||
COMMAND="ssh -N -D 1098 -l proxy vps.mydomain.com" |
|
||||
start_service() { |
|
||||
procd_open_instance |
|
||||
procd_set_param user $USERNAME |
|
||||
procd_set_param respawn 10 10 0 |
|
||||
procd_set_param command $COMMAND |
|
||||
procd_close_instance |
|
||||
} |
|
||||
----------------------------- |
|
||||
Этому файлу нужно дать права : chmod +x /etc/init.d/socks_vps |
|
||||
Запуск : /etc/init.d/socks_vps start |
|
||||
Останов : /etc/init.d/socks_vps stop |
|
||||
Включить автозагрузку : /etc/init.d/socks_vps enable |
|
||||
Проверка : curl -4 --socks5 127.0.0.1:1098 https://rutracker.org |
|
||||
|
|
||||
|
|
||||
* Организовать прозрачную соксификацию |
|
||||
|
|
||||
Установить пакет redsocks. |
|
||||
Конфиг : |
|
||||
-- /etc/redsocks.conf : --- |
|
||||
base { |
|
||||
log_debug = off; |
|
||||
log_info = on; |
|
||||
log = "syslog:local7"; |
|
||||
daemon = on; |
|
||||
user = nobody; |
|
||||
group = nogroup; |
|
||||
redirector = iptables; |
|
||||
} |
|
||||
redsocks { |
|
||||
local_ip = 127.0.0.127; |
|
||||
local_port = 1099; |
|
||||
ip = 127.0.0.1; |
|
||||
port = 1098; |
|
||||
type = socks5; |
|
||||
} |
|
||||
--------------------------- |
|
||||
|
|
||||
После чего перезапускаем : /etc/init.d/redsocks restart |
|
||||
Смотрим появился ли листенер : netstat -tnlp | grep 1099 |
|
||||
|
|
||||
В zapret для перенаправления DNAT на интерфейс lo используется 127.0.0.127. |
|
||||
Ко всем остальным адресам из 127.0.0.0/8 DNAT может быть заблокирован. Читайте readme.txt про route_localnet. |
|
||||
|
|
||||
* Завертывание соединений через iptables |
|
||||
|
|
||||
!! Версии OpenWRT до 21.02 включительно используют iptables + fw3. Более новые перешили на nftables по умолчанию. |
|
||||
!! В новых OpenWRT можно снести firewall4 и nftables, заменив их на firewall3 + iptables |
|
||||
!! Инструкция относится только к openwrt, где используется iptables. |
|
||||
|
|
||||
Будем завертывать любые tcp соединения на ip из ipset "ipban" и https на ip из ipset "zapret", за исключением ip из ipset "nozapret". |
|
||||
|
|
||||
--- /etc/firewall.user ----- |
|
||||
SOXIFIER_PORT=1099 |
|
||||
|
|
||||
. /opt/zapret/init.d/openwrt/functions |
|
||||
|
|
||||
create_ipset no-update |
|
||||
|
|
||||
network_find_wan4_all wan_iface |
|
||||
for ext_iface in $wan_iface; do |
|
||||
network_get_device ext_device $ext_iface |
|
||||
ipt OUTPUT -t nat -o $ext_device -p tcp --dport 443 -m set --match-set zapret dst -m set ! --match-set nozapret dst -j REDIRECT --to-port $SOXIFIER_PORT |
|
||||
ipt OUTPUT -t nat -o $ext_device -p tcp -m set --match-set ipban dst -m set ! --match-set nozapret dst -j REDIRECT --to-port $SOXIFIER_PORT |
|
||||
done |
|
||||
|
|
||||
prepare_route_localnet |
|
||||
|
|
||||
ipt prerouting_lan_rule -t nat -p tcp --dport 443 -m set --match-set zapret dst -m set ! --match-set nozapret -j DNAT --to $TPWS_LOCALHOST4:$SOXIFIER_PORT |
|
||||
ipt prerouting_lan_rule -t nat -p tcp -m set --match-set ipban dst -m set ! --match-set nozapret -j DNAT --to $TPWS_LOCALHOST4:$SOXIFIER_PORT |
|
||||
---------------------------- |
|
||||
|
|
||||
Внести параметр "reload" в указанное место : |
|
||||
--- /etc/config/firewall --- |
|
||||
config include |
|
||||
option path '/etc/firewall.user' |
|
||||
option reload '1' |
|
||||
---------------------------- |
|
||||
|
|
||||
Перезапуск firewall : /etc/init.d/firewall restart |
|
||||
|
|
||||
|
|
||||
* Завертывание соединений через nftables |
|
||||
|
|
||||
!! Только для версий OpenWRT старше 21.02 |
|
||||
|
|
||||
nftables не могут использовать ipset. Вместо ipset существует аналог - nfset. |
|
||||
nfset является частью таблицы nftable и принадлежит только к ней. Адресация nfset из другой nftable невозможна. |
|
||||
Скрипты ipset/* в случае nftables используют nfset-ы в таблице zapret. |
|
||||
Чтобы использовать эти nfset-ы в своих правилах, необходимо синхронизироваться с их созданием и вносить свои цепочки в nftable "zapret". |
|
||||
Для этого существуют хуки - скрипты, вызываемые из zapret на определенных стадиях инициализации фаервола. |
|
||||
|
|
||||
Раскоментируейте в /opt/zapret/config строчку |
|
||||
INIT_FW_POST_UP_HOOK="/etc/firewall.zapret.hook.post_up" |
|
||||
|
|
||||
Создайте файл /etc/firewall.zapret.hook.post_up и присвойте ему chmod 755. |
|
||||
|
|
||||
--- /etc/firewall.zapret.hook.post_up --- |
|
||||
#!/bin/sh |
|
||||
|
|
||||
SOXIFIER_PORT=1099 |
|
||||
|
|
||||
. /opt/zapret/init.d/openwrt/functions |
|
||||
|
|
||||
cat << EOF | nft -f - 2>/dev/null |
|
||||
delete chain inet $ZAPRET_NFT_TABLE my_output |
|
||||
delete chain inet $ZAPRET_NFT_TABLE my_prerouting |
|
||||
EOF |
|
||||
|
|
||||
prepare_route_localnet |
|
||||
|
|
||||
cat << EOF | nft -f - |
|
||||
add chain inet $ZAPRET_NFT_TABLE my_output { type nat hook output priority -102; } |
|
||||
flush chain inet $ZAPRET_NFT_TABLE my_output |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_output oifname @wanif meta l4proto tcp ip daddr @ipban ip daddr != @nozapret dnat to $TPWS_LOCALHOST4:$SOXIFIER_PORT |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_output oifname @wanif tcp dport 443 ip daddr @zapret ip daddr != @nozapret dnat to $TPWS_LOCALHOST4:$SOXIFIER_PORT |
|
||||
|
|
||||
add chain inet $ZAPRET_NFT_TABLE my_prerouting { type nat hook prerouting priority -102; } |
|
||||
flush chain inet $ZAPRET_NFT_TABLE my_prerouting |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_prerouting iifname @lanif meta l4proto tcp ip daddr @ipban ip daddr != @nozapret dnat to $TPWS_LOCALHOST4:$SOXIFIER_PORT |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_prerouting iifname @lanif tcp dport 443 ip daddr @zapret ip daddr != @nozapret dnat to $TPWS_LOCALHOST4:$SOXIFIER_PORT |
|
||||
EOF |
|
||||
---------------------------- |
|
||||
|
|
||||
Перезапуск firewall : /etc/init.d/zapret restart_fw |
|
||||
|
|
||||
|
|
||||
* Проверка |
|
||||
|
|
||||
Все, теперь можно проверять : |
|
||||
/etc/init.d/redsocks stop |
|
||||
curl -4 https://rutracker.org |
|
||||
# должно обломаться с надписью "Connection refused". если не обламывается - значит ip адрес rutracker.org не в ipset, |
|
||||
# либо не сработали правила фаервола. например, из-за не установленных модулей ipt |
|
||||
/etc/init.d/redsocks start |
|
||||
curl -4 https://rutracker.org |
|
||||
# должно выдать страницу |
|
||||
@ -1,181 +0,0 @@ |
|||||
### tpws |
|
||||
|
|
||||
Using `WSL` (Windows subsystem for Linux) it's possible to run `tpws` in socks mode under rather new builds of |
|
||||
windows 10 and windows server. |
|
||||
Its not required to install any linux distributions as suggested in most articles. |
|
||||
tpws is static binary. It doesn't need a distribution. |
|
||||
|
|
||||
Install `WSL` : `dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all` |
|
||||
|
|
||||
From release copy `binaries/linux-x86_64/tpws_wsl.tgz` to the target system. |
|
||||
Run : `wsl --import tpws "%USERPROFILE%\tpws" tpws_wsl.tgz` |
|
||||
|
|
||||
Run tpws : `wsl -d tpws --exec /tpws --uid=1 --no-resolve --socks --bind-addr=127.0.0.1 --port=1080 <fooling_options>` |
|
||||
|
|
||||
Configure socks as `127.0.0.1:1080` in a browser or another program. |
|
||||
|
|
||||
Cleanup : `wsl --unregister tpws` |
|
||||
|
|
||||
Tested in windows 10 build 19041 (20.04) with WSL1. |
|
||||
|
|
||||
`--oob` , `--mss` and `--disorder` do not work. |
|
||||
RST detection in autohostlist scheme may not work. |
|
||||
WSL may glitch with splice. `--nosplice` may be required. |
|
||||
|
|
||||
|
|
||||
### winws |
|
||||
|
|
||||
`winws` is `nfqws` version for windows. It's based on `windivert`. Most functions are working. |
|
||||
Large ip filters (ipsets) are not possible. Forwarded traffic and connection sharing are not supported. |
|
||||
Administrator rights are required. |
|
||||
|
|
||||
Working with packet filter consists of two parts |
|
||||
|
|
||||
1. In-kernel packet selection and passing selected packets to a packet filter in user mode. |
|
||||
In *nix it's done by `iptables`, `nftables`, `pf`, `ipfw`. |
|
||||
2. User mode packet filter processes packets and does DPI bypass magic. |
|
||||
|
|
||||
Windows does not have part 1. No `iptables` exist. That's why 3rd party packet redirector is used. |
|
||||
It's called `windivert`. It works starting from `windows 7`. Kernel driver is signed but it may require to disable secure boot |
|
||||
or update windows 7. Read below for windows 7 windivert signing info. |
|
||||
|
|
||||
Task of `iptables` is done inside `winws` through `windivert` filters. `Windivert` has it's own [filter language](https://reqrypt.org/windivert-doc.html#filter_language). |
|
||||
`winws` can automate filter construction using simple ip version and port filter. Raw filters are also supported. |
|
||||
|
|
||||
``` |
|
||||
--wf-iface=<int>[:<int>] ; numeric network interface and subinterface indexes |
|
||||
--wf-l3=ipv4|ipv6 ; L3 protocol filter. multiple comma separated values allowed. |
|
||||
--wf-tcp=[~]port1[-port2] ; TCP port filter. ~ means negation. multiple comma separated values allowed. |
|
||||
--wf-udp=[~]port1[-port2] ; UDP port filter. ~ means negation. multiple comma separated values allowed. |
|
||||
--wf-raw-part=<filter>|@<filename> ; partial raw windivert filter string or filename |
|
||||
--wf-filter-lan=0|1 ; add excluding filter for non-global IP (default : 1) |
|
||||
--wf-raw=<filter>|@<filename> ; full raw windivert filter string or filename. replaces --wf-tcp,--wf-udp,--wf-raw-part |
|
||||
--wf-save=<filename> ; save windivert filter string to a file and exit |
|
||||
--ssid-filter=ssid1[,ssid2,ssid3,...] ; enable winws only if any of specified wifi SSIDs connected |
|
||||
--nlm-filter=net1[,net2,net3,...] ; enable winws only if any of specified NLM network is connected. names and GUIDs are accepted. |
|
||||
--nlm-list[=all] ; list Network List Manager (NLM) networks. connected only or all. |
|
||||
``` |
|
||||
|
|
||||
`--wf-l3`, `--wf-tcp`, `--wf-udp` can take multiple comma separated arguments. |
|
||||
|
|
||||
Interface indexes can be discovered using this command : `netsh int ip show int` |
|
||||
|
|
||||
If you can't find index this way use `winws --debug` to see index there. Subinterface index is almost always 0 and you can omit it. |
|
||||
|
|
||||
`--wf-raw-part` specifies partial windivert filter. Multiple filter parts are supported. They can also be combined with `--wf-tcp`,`--wf-udp`. |
|
||||
|
|
||||
`--wf-raw` specifies full windivert filter that replaces `--wf-tcp`,`--wf-udp`,`--wf-raw-part`. |
|
||||
|
|
||||
Kernel filtering with windivert language is much more effective than passing massive amount of traffic to winws. Use it if possible to save CPU resources. |
|
||||
|
|
||||
Multiple `winws` processes are allowed. However, it's discouraged to intersect their filters. |
|
||||
|
|
||||
`--ssid-filter` allows to enable `winws` only if specified wifi networks are connected. `winws` auto detects SSID appearance and disappearance. |
|
||||
SSID names must be written in the same case as the system sees them. This option does not analyze routing and does not detect where traffic actually goes. |
|
||||
If multiple connections are available, the only thing that triggers `winws` operation is wifi connection presence. That's why it's a good idea to add also `--wf-iface` filter to not break ethernet, for example. |
|
||||
|
|
||||
`--nlm-filter` is like `--ssid-filter` but works with names or GUIDs from Network List Manager. NLM names are those you see in Control Panel "Network and Sharing Center". |
|
||||
NLM networks are adapter independent. Usually MAC address of the default router is used to distinugish networks. NLM works with any type of adapters : ethernet, wifi, vpn and others. |
|
||||
That's why NLM is more universal than `ssid-filter`. |
|
||||
|
|
||||
`Cygwin` shell does not run binaries if their directory has it's own copy of `cygwin1.dll`. |
|
||||
If you want to run `winws` from `cygwin` delete, rename or move `cygwin1.dll`. |
|
||||
`Cygwin` is required for `blockcheck.sh` support but `winws` itself can be run standalone without cygwin. |
|
||||
|
|
||||
How to get `windows 7` and `winws` compatible `cygwin` : |
|
||||
``` |
|
||||
curl -O https://www.cygwin.com/setup-x86_64.exe |
|
||||
setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215 |
|
||||
``` |
|
||||
You must choose to install `curl`. To compile from sources install `gcc-core`,`make`,`zlib-devel`. |
|
||||
Make from directory `nfq` using `make cygwin64` or `make cygwin32` for 64 and 32 bit versions. |
|
||||
|
|
||||
`winws` requires `cygwin1.dll`, `windivert.dll`, `windivert64.sys` or `windivert32.sys`. |
|
||||
You can take them from `binaries/windows-x86_64` or `binaries/windows-x86`. |
|
||||
|
|
||||
There's no `arm64` signed `windivert` driver and no `cygwin`. |
|
||||
But it's possible to use unsigned driver version in test mode and user mode components with x64 emulation. |
|
||||
x64 emulation requires `windows 11` and not supported in `windows 10`. |
|
||||
|
|
||||
### windows 7 windivert signing |
|
||||
|
|
||||
Requirements for windows driver signing have changed in 2021. |
|
||||
Official free updates of windows 7 ended in 2020. |
|
||||
After 2020 for the years paid updates were available (ESU). |
|
||||
One of the updates from ESU enables signatures used in windivert 2.2.2-A. |
|
||||
There are several options : |
|
||||
|
|
||||
1. Take `windivert64.sys` and `windivert.dll` version `2.2.0-C` or `2.2.0-D` from [here](https://reqrypt.org/download). |
|
||||
Replace these 2 files in every location they are present. |
|
||||
In `zapret-win-bundle` they are in `zapret-winws` и `blockcheck/zapret/nfq` folders. |
|
||||
However this option still requires 10+ year old patch that enables SHA256 signatures. |
|
||||
If you're using win bundle you can simply run `win7\install_win7.cmd` |
|
||||
|
|
||||
3. [Hack ESU](https://hackandpwn.com/windows-7-esu-patching) |
|
||||
|
|
||||
4. Use `UpdatePack7R2` from simplix : https://blog.simplix.info |
|
||||
If you are in Russia or Belarus temporary change region in Control Panel. |
|
||||
|
|
||||
### blockcheck |
|
||||
|
|
||||
`blockcheck.sh` is written in posix shell and uses some standard posix utilites. |
|
||||
Windows does not have them. To execute `blockcheck.sh` use `cygwin` command prompt run as administrator. |
|
||||
It's not possible to use `WSL`. It's not the same as `cygwin`. |
|
||||
First run once `install_bin.sh` then `blockcheck.sh`. |
|
||||
|
|
||||
Backslashes in windows paths shoud be doubled. Or use cygwin path notation. |
|
||||
``` |
|
||||
cd "C:\\Users\\vasya" |
|
||||
cd "C:/Users/vasya" |
|
||||
cd "/cygdrive/c/Users/vasya" |
|
||||
``` |
|
||||
`Cygwin` shell does not run binaries if their directory has it's own copy of `cygwin1.dll`. |
|
||||
If you want to run `winws` from `cygwin` delete, rename or move `cygwin1.dll`. |
|
||||
|
|
||||
`Cygwin` is required only for `blockcheck.sh`. Standalone `winws` can be run without it. |
|
||||
|
|
||||
To simplify things it's advised to use `zapret-win-bundle`. |
|
||||
|
|
||||
### zapret-win-bundle |
|
||||
|
|
||||
To make your life easier there's ready to use [bundle](https://github.com/bol-van/zapret-win-bundle) with `cygwin`,`blockcheck` and `winws`. |
|
||||
|
|
||||
* `/zapret-winws` - standalone version of `winws` for everyday use. does not require any other folders. |
|
||||
* `/zapret-winws/_CMD_ADMIN.cmd` - open `cmd` as administrator in the current folder |
|
||||
* `/blockcheck/blockcheck.cmd` - run `blockcheck` with logging to `blockcheck/blockcheck.log` |
|
||||
* `/cygwin/cygwin.cmd` - run `cygwin` shell as current user |
|
||||
* `/cygwin/cygwin-admin.cmd` - run `cygwin` shell as administrator |
|
||||
|
|
||||
There're aliases in cygwin shell for `winws`,`blockcheck`,`ip2net`,`mdig`. No need to mess with paths. |
|
||||
It's possible to send signals to `winws` using standard unix utilites : `pidof,kill,killall,pgrep,pkill`. |
|
||||
`Cygwin` shares common process list per `cygwin1.dll` copy. If you run a `winws` from `zapret-winws` |
|
||||
you won't be able to `kill` it because this folder contain its own copy of `cygwin1.dll`. |
|
||||
|
|
||||
It's possible to use `cygwin` shell to make `winws` debug log. Use `tee` command like this : |
|
||||
|
|
||||
``` |
|
||||
winws --debug --wf-tcp=80,443 | tee winws.log |
|
||||
unix2dos winws.log |
|
||||
``` |
|
||||
|
|
||||
`winws.log` will be in `cygwin/home/<username>`. `unix2dos` helps with `windows 7` notepad. It's not necessary in `Windows 10` and later. |
|
||||
|
|
||||
Because 32-bit systems are rare nowadays `zapret-win-bundle` exists only for `Windows x64/arm64`. |
|
||||
|
|
||||
### auto start |
|
||||
|
|
||||
To start `winws` with windows use windows task scheduler. There are `task_*.cmd` batch files in `binaries/windows-x86-64/zapret-winws`. |
|
||||
They create, remove, start and stop scheduled task `winws1`. They must be run as administrator. |
|
||||
|
|
||||
Edit `task_create.cmd` and write your `winws` parameters to `%WINWS1%` variable. If you need multiple `winws` instances |
|
||||
clone the code in all cmd files to support multiple tasks `winws1,winws2,winws3,...`. |
|
||||
|
|
||||
Tasks can also be controlled from GUI `taskschd.msc`. |
|
||||
|
|
||||
Also you can use windows services the same way with `service_*.cmd`. |
|
||||
|
|
||||
### Windows Server |
|
||||
|
|
||||
winws is linked against wlanapi.dll which is absent by default. |
|
||||
To solve this problem run power shell as administrator and execute command `Install-WindowsFeature -Name Wireless-Networking`. |
|
||||
Then reboot the system. |
|
||||
@ -1,281 +0,0 @@ |
|||||
# Windows |
|
||||
|
|
||||
## tpws |
|
||||
|
|
||||
Запуск tpws возможен только в Linux варианте под **WSL** _(Windows Subsystem for Linux)_. |
|
||||
Нативного варианта под Windows нет, поскольку он использует epoll, которого под windows не существует. |
|
||||
|
|
||||
tpws в режиме socks можно запускать под более-менее современными билдами windows 10 и windows server |
|
||||
с установленным WSL. Совсем не обязательно устанавливать дистрибутив убунту, как вам напишут почти в каждой |
|
||||
статье про WSL, которую вы найдете в сети. tpws - статический бинарик, ему дистрибутив не нужен. |
|
||||
|
|
||||
Установить WSL : |
|
||||
`dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all` |
|
||||
|
|
||||
Из релиза скопировать на целевую систему `binaries/linux-x86_64/tpws_wsl.tgz`. |
|
||||
|
|
||||
Выполнить : |
|
||||
`wsl --import tpws "%USERPROFILE%\tpws" tpws_wsl.tgz` |
|
||||
|
|
||||
Запустить : |
|
||||
`wsl -d tpws --exec /tpws --uid=1 --no-resolve --socks --bind-addr=127.0.0.1 --port=1080 <параметры_дурения>` |
|
||||
|
|
||||
Прописать socks `127.0.0.1:1080` в браузер или другую программу. |
|
||||
|
|
||||
Удаление : `wsl --unregister tpws` |
|
||||
|
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Проверено на windows 10 build 19041 (20.04) под WSL1. На WSL2 эти команды могут не сработать. |
|
||||
Если у вас есть WSL2, значит у вас есть работающая виртуалка с linux. |
|
||||
Если вы умеете с ней обращаться, tpws на ней запустить возможно без всяких проблем. |
|
||||
|
|
||||
|
|
||||
Возможные проблемы: |
|
||||
- Не работают функции `--oob` и `--mss` из-за ограничений реализации WSL. |
|
||||
`--disorder` не работает из-за особенностей tcp/ip стека windows. |
|
||||
|
|
||||
- Может не срабатывать детект RST в autohostlist. |
|
||||
|
|
||||
- WSL может глючить со splice, приводя к зацикливанию процесса. Может потребоваться `--nosplice`. |
|
||||
|
|
||||
- Не поддерживается tcp user timeout. |
|
||||
Чтобы избавиться от сообщений об ошибке добавляйте : |
|
||||
`--local-tcp-user-timeout=0 --remote-tcp-user-timeout=0`. |
|
||||
Эти сообщения только информативные, на работу они не влияют. |
|
||||
|
|
||||
## winws |
|
||||
|
|
||||
Это вариант пакетного фильтра nfqws для Windows, построенный на базе windivert. |
|
||||
Все функции работоспособны, однако функционал ipset в ядре отсутствует. Он реализован в user mode. Фильтры по большому количеству IP адресов невозможны. |
|
||||
Работа с проходящим трафиком, например в случае "расшаривания" соединения, невозможна. |
|
||||
Для работы с windivert требуются права администратора. |
|
||||
Специфические для unix параметры, такие как `--uid`, `--user` и тд, исключены. Все остальные параметры аналогичны nfqws и dvtws. |
|
||||
|
|
||||
Работа с пакетным фильтром основана на двух действиях : |
|
||||
1) Выделение перенаправляемого трафика в режиме ядра и передача его пакетному фильтру в user mode. |
|
||||
2) Собственно обработка перенаправленных пакетов в пакетном фильтре. |
|
||||
|
|
||||
В windows отсутствуют встроенные средства для перенаправления трафика, такие как _iptables_, _nftables_, _pf_ или _ipfw_. |
|
||||
Поэтому используется сторонний драйвер ядра windivert. Он работает, начиная с windows 7. На системах с включенным |
|
||||
secure boot могут быть проблемы из-за подписи драйвера. В этом случае отключите `secureboot` или включите режим `testsigning`. |
|
||||
На windows 7, вероятно, будут проблемы с загрузкой windivert. Читайте ниже соответствующий раздел. |
|
||||
|
|
||||
Задача _iptables_ в **winws** решается внутренними средствами через фильтры windivert. |
|
||||
У windivert существует собственный язык фильтров, похожий на язык фильтров wireshark. |
|
||||
[Документация по фильтрам windivert.](https://reqrypt.org/windivert-doc.html#filter_language) |
|
||||
Чтобы не писать сложные фильтры вручную, предусмотрены различные упрощенные варианты автоматического построения фильтров. |
|
||||
|
|
||||
``` |
|
||||
--wf-iface=<int>[.<int>] ; числовые индексы интерфейса и суб-интерфейса |
|
||||
--wf-l3=ipv4|ipv6 ; фильтр L3 протоколов. по умолчанию включены ipv4 и ipv6. |
|
||||
--wf-tcp=[~]port1[-port2] ; фильтр портов для tcp. ~ означает отрицание |
|
||||
--wf-udp=[~]port1[-port2] ; фильтр портов для udp. ~ означает отрицание |
|
||||
--wf-raw-part=<filter>|@<filename> ; частичный windivert фильтр из параметра или из файла. имени файла предшествует символ @. может быть множество частей. сочетается с --wf-tcp,--wf-udp. |
|
||||
--wf-filter-lan=0|1 ; отфильтровывать адреса назначения, не являющиеся глобальными inet адресами ipv4 или ipv6. по умолчанию - 1. |
|
||||
--wf-raw=<filter>|@<filename> ; полный windivert фильтр из параметра или из файла. имени файла предшествует символ @. замещает --wf-raw-part,--wf-tcp,--wf-udp. |
|
||||
--wf-save=<filename> ; сохранить сконструированный фильтр windivert в файл для последующей правки вручную |
|
||||
--ssid-filter=ssid1[,ssid2,ssid3,...] ; включать winws только когда подключена любая из указанных wifi сетей |
|
||||
--nlm-filter=net1[,net2,net3,...] ; включать winws только когда подключена любая из указанных сетей NLM |
|
||||
--nlm-list[=all] ; вывести список сетей NLM. по умолчанию только подключенных, all - всех. |
|
||||
``` |
|
||||
|
|
||||
Параметры `--wf-l3`, `--wf-tcp`, `--wf-udp` могут брать несколько значений через запятую. |
|
||||
|
|
||||
Номера интерфейсов можно узнать так : `netsh int ip show int`. |
|
||||
Некоторых типы соединений там не увидеть. В этом случае запускайте **winws** с параметром `--debug` и смотрите IfIdx там. |
|
||||
SubInterface используется windivert, но практически всегда **0**, его можно не указывать. Вероятно, он нужен в редких случаях. |
|
||||
|
|
||||
Конструктор стандартных фильтров автоматически включает входящие tcp пакеты с tcp synack и tcp rst для корректной работы функций |
|
||||
autottl и autohostlist. При включении autohostlist так же перенаправляются пакеты данных с http redirect с кодами 302 и 307. |
|
||||
Если не указаное иное, добавляется фильтр на исключение не-интернет адресов ipv4 и ipv6. |
|
||||
Для сложных нестандартных сценариев могут потребоваться свои фильтры. Полный фильтр --wf-raw замещает все остальное. |
|
||||
Частичные фильтры `--wf-raw-part` совместимы друг с другом и `--wf-tcp` и `--wf-udp`. Они позволяют исключить написание |
|
||||
громоздких полных фильтров, сосредоточившись лишь на добавлении какого-то особенного пейлоада. |
|
||||
`--wf-save` позволяет записать итоговый windivert фильтр в файл. Максимальный размер фильтра - **16 Kb**. |
|
||||
|
|
||||
Фильтрация windivert производится в ядре. Это несравнимо легче по ресурсам, чем перенаправлять пакеты в пространство user mode, |
|
||||
чтобы winws принимал решение. Поэтому пользуйтесь по максимуму возможностями windivert. |
|
||||
Например, если вам нужно дурить wireguard на все порты, вам придется перенаправить все порты на winws. Или же написать windivert фильтр, который отсечет wireguard по содержимому пакета. |
|
||||
Разница в нагрузке на процессор колоссальна. В первом случае - до 100% одного ядра cpu в зависимости от обьема исходящего udp трафика (привет, торрент и uTP), во втором - близко к 0. |
|
||||
Кроме нагрузки на процессор еще можете порезать себе скорость, тк одно ядро не будет справляться с обработкой вашего гигабитного интернета. А на старых ноутах еще и получите самолетный вой системы охлаждения, приводящий к ее износу. |
|
||||
|
|
||||
Можно запускать несколько процессов **winws** с разными стратегиями. Однако, не следует делать пересекающиеся фильтры. |
|
||||
|
|
||||
В `--ssid-filter` можно через запятую задать неограниченное количество имен wifi сетей (**SSID**). Если задана хотя бы одна сеть, |
|
||||
то winws включается только, если подключен указанный **SSID**. Если **SSID** исчезает, winws отключается. Если **SSID** появляется снова, |
|
||||
winws включается. Это нужно, чтобы можно было применять раздельное дурение к каждой отдельной wifi сети. |
|
||||
Названия сетей должны быть написаны в том регистре, в котором их видит система. Сравнение идет с учетом регистра! |
|
||||
При этом нет никаких проверок куда реально идет трафик. Если одновременно подключен, допустим, ethernet, |
|
||||
и трафик идет туда, то дурение включается и выключается просто по факту наличия wifi сети, на которую трафик может и не идти. |
|
||||
И это может сломать дурение на ethernet. Поэтому полезно так же будет добавить фильтр `--wf-iface` на индекс интерфейса wifi адаптера, |
|
||||
чтобы не трогать другой трафик. |
|
||||
|
|
||||
`--nlm-filter` аналогичен `--ssid-filter`, но работает с именами или GUIDами сетей Network List Manager (NLM). |
|
||||
Это те сети, которые вы видите в панели управления в разделе "Центр управления сетями и общим доступом". |
|
||||
Под сетью подразумевается не конкретный адаптер, а именно сетевое окружение конкретного подключения. |
|
||||
Обычно проверяется mac адрес шлюза. К сети можно подключиться через любой адаптер, и она останется той же самой. |
|
||||
Если подключиться, допустим, к разными роутерам по кабелю, то будут разные сети. |
|
||||
А если к одному роутеру через 2 разных сетевых карточки на том же компе - будет одна сеть. |
|
||||
NLM абстрагирует типы сетевых адаптеров. Он работает как с wifi, так и с ethernet и любыми другими. |
|
||||
Поэтому это более универсальный метод, чем **SSID** фильтр. |
|
||||
Однако, есть и неприятная сторона. В windows 7 вы легко могли ткнуть на иконку сети и выбрать тип : private или public. |
|
||||
Там же вы могли посмотреть список сетей и обьединить их. Чтобы, допустим, вы могли подключаться по кабелю и wifi |
|
||||
к одному роутеру, и система эти подключения воспринимала как одну сеть. |
|
||||
В следующих версиях windows они эти возможности сильно порезали. Похоже нет встроенных средств полноценно управлять |
|
||||
network locations в win10/11. Кое-что есть в **powershell**. |
|
||||
Можно поковыряться напрямую в реестре здесь : |
|
||||
`HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList` |
|
||||
Нужно менять ProfileGUID в `Signatures\Unmanaged`. Имена можно поменять в Profiles. |
|
||||
Есть кое-какие сторонние утилиты. Кое-что находится, позволяющее посмотреть и удалить network profiles, но не обьединить. |
|
||||
Факт, что в ms они это сильно испортили. Движок network list все тот же, и он способен на все то, что было в win7. |
|
||||
Можно не бороться с этой проблемой, а просто указывать через запятую те названия сетей или GUIDы, которые выбрала система. |
|
||||
Или если у вас только wifi, то использовать `--ssid-filter`. Там хотя бы есть гарантия, что **SSID** соответствуют реальности, |
|
||||
а система их не назвала как-то по-своему. |
|
||||
|
|
||||
Если в путях присутствуют национальные символы, то при вызове winws из `cmd` или `bat` кодировку нужно использовать **OEM**. |
|
||||
Для русского языка это 866. Пути с пробелами нужно брать в кавычки. |
|
||||
При использовании опции @<config_file> кодировка в файле должна быть **UTF-8** без **BOM mark**. |
|
||||
|
|
||||
Существует неочевидный момент, каcаемый запуска **winws** из cygwin shell\`а. Если в директории, где находится winws, находится |
|
||||
копия `cygwin1.dll`, **winws** не запустится. |
|
||||
Если нужен запуск под cygwin, то следует удалить или переместить `cygwin1.dll` из `binaries/windows-x86_64`. Это нужно для работы blockcheck. |
|
||||
Из cygwin шелла можно посылать winws сигналы через `kill` точно так же, как в `*nix`. |
|
||||
|
|
||||
Как получить совместимый с windows 7 и winws cygwin : |
|
||||
|
|
||||
`curl -O https://www.cygwin.com/setup-x86_64.exe` |
|
||||
|
|
||||
`setup-x86_64.exe --allow-unsupported-windows --no-verify --site http://ctm.crouchingtigerhiddenfruitbat.org/pub/cygwin/circa/64bit/2024/01/30/231215` |
|
||||
|
|
||||
> [!IMPORTANT] |
|
||||
> Следует выбрать установку curl. |
|
||||
|
|
||||
Для сборки из исходников требуется _gcc-core_,_make_,_zlib-devel_. |
|
||||
Собирать из директории nfq командой `make cygwin64` или `make cygwin32` для 64 и 32 битных версий соответственно. |
|
||||
**winws** требует `cygwin1.dll`, `windivert.dll`, `windivert64.sys` или `windivert32.sys`. |
|
||||
Их можно взять из `binaries/win64` и `binaries/win32`. |
|
||||
|
|
||||
Для _arm64_ windows нет подписанного драйвера windivert и нет cygwin. |
|
||||
Однако, эмуляция x64 windows 11 позволяет использовать все, кроме WinDivert64.sys без изменений. |
|
||||
Но при этом надо заменить WinDivert64.sys на неподписанную _arm64_ версию и установить режим testsigning. |
|
||||
|
|
||||
## Windows 7 и windivert |
|
||||
|
|
||||
Требования к подписи драйверов windows изменились в 2021 году. |
|
||||
Официальные бесплатные обновления windows 7 закончились в 2020. |
|
||||
После этого несколько лет продолжали идти платные обновления по программе **ESU**. |
|
||||
Именно в этих **ESU** обновлениях находится обновление ядра windows 7, позволяющиее загрузить драйвер |
|
||||
_windivert 2.2.2-A_, который идет в поставке zapret. |
|
||||
Поэтому варианты следующие : |
|
||||
|
|
||||
1) Взять `windivert64.sys` и `windivert.dll` версии _2.2.0-C_ или _2.2.0-D_ отсюда : https://reqrypt.org/download |
|
||||
и заменить эти 2 файла. |
|
||||
В [zapret-win-bundle](https://github.com/bol-van/zapret-win-bundle) есть отдельных 2 места, где находится **winws** : [_zapret-winws_](https://github.com/bol-van/zapret-win-bundle/tree/master/zapret-winws) и [_blockcheck/zapret/nfq_](https://github.com/bol-van/zapret-win-bundle/tree/master/blockcheck). |
|
||||
Надо менять в обоих местах. |
|
||||
Альтернативный вариант при использовании win bundle - запустить `win7\install_win7.cmd` |
|
||||
|
|
||||
> [!NOTE] |
|
||||
> Этот вариант проверен и должен работать. Тем не менее патч 10 летней давности, который включает SHA256 сигнатуры, все еще необходим. |
|
||||
|
|
||||
2) Взломать **ESU** : |
|
||||
https://hackandpwn.com/windows-7-esu-patching/ |
|
||||
http://www.bifido.net/tweaks-and-scripts/8-extended-security-updates-installer.html |
|
||||
и обновить систему |
|
||||
|
|
||||
3) Использовать готовый патчер-взламыватель ESU - "BypassESU". Его надо искать по названию. |
|
||||
|
|
||||
4) Использовать UpdatePack7R2 от simplix : https://blog.simplix.info |
|
||||
> [!WARNING] |
|
||||
> Но с этим паком есть проблема. Автор из Украины, он очень обиделся на русских. |
|
||||
> Если в панели управления стоит регион RU или BY, появляется неприятный диалог. |
|
||||
> Чтобы эту проблему обойти, можно поставить временно любой другой регион, потом вернуть. |
|
||||
> Так же нет никаких гарантий, что автор не насовал туда какой-то зловредный код. |
|
||||
> Использовать на свой страх и риск. |
|
||||
|
|
||||
Более безопасный вариант - скачать последнюю нормальную довоенную версию : 22.2.10 |
|
||||
https://nnmclub.to/forum/viewtopic.php?t=1530323 |
|
||||
Ее достаточно, чтобы _windivert 2.2.2-A_ заработал на windows 7. |
|
||||
|
|
||||
## blockcheck |
|
||||
|
|
||||
`blockcheck.sh` написан на _posix shell_ и требует некоторых стандартных утилит _posix_. В windows, естественно, этого нет. |
|
||||
Потому просто так запустить `blockcheck.sh` невозможно. |
|
||||
Для этого требуется скачать и установить _cygwin_ так , как описано в предыдущем разделе. |
|
||||
Следует запустить от имени администратора _cygwin shell_ через `cygwin.bat`. |
|
||||
В нем нужно пройти в директорию с zapret. |
|
||||
Обратные слэши путей windows нужно удваивать, менять на прямые слэши, либо использовать отображение на unix path. |
|
||||
Корректные варианты : |
|
||||
- `cd C:\\Users\\vasya` |
|
||||
- `cd C:/Users/vasya` |
|
||||
- `cd /cygdrive/c/Users/vasya` |
|
||||
|
|
||||
Существует неочевидный момент, каcаемый запуска **winws** из _cygwin_ шелла. Если в директории, где находится **winws**, есть копия `cygwin1.dll`, **winws** не запустится. Нужно переименовать файл `cygwin1.dll`. |
|
||||
Далее все как в _*nix_ : 1 раз `./install_bin.sh` , затем `./blockcheck.sh`. |
|
||||
WSL использовать нельзя, это не то же самое. |
|
||||
|
|
||||
_cygwin_ для обычной работы **winws** не нужен. |
|
||||
|
|
||||
Однако, хотя такой способ и работает, использование **winws** сильно облегчает [zapret-win-bundle](https://github.com/bol-van/zapret-win-bundle). |
|
||||
Там нет проблемы с `cygwin.dll`. |
|
||||
|
|
||||
## Zapret-win-bundle |
|
||||
|
|
||||
Можно не возиться с _cygwin_, а взять готовый пакет, включающий в себя _cygwin_ и _blockcheck_ : https://github.com/bol-van/zapret-win-bundle |
|
||||
Там сделан максимум удобств для сосредоточения на самом zapret, исключая возню с установкой _cygwin_, |
|
||||
заходами в директории, запусками под администратором и прочими сугубо техническими моментами, в которых могут быть |
|
||||
ошибки и непонимания, а новичок без базиса знаний может и вовсе запутаться. |
|
||||
|
|
||||
`/zapret-winws` - здесь все, что нужно для запуска winws в повседневном рабочем режиме. остальное не нужно.\ |
|
||||
`/zapret-winws/_CMD_ADMIN.cmd` - получить командную строку cmd в этой директории от имени администратора для тестирования **winws** |
|
||||
с параметрами, вводимыми вручную\ |
|
||||
`/blockcheck/blockcheck.cmd` - достаточно кликнуть по нему, чтобы пошел _blockcheck_ с записью лога в `blockcheck/blockcheck.log`\ |
|
||||
`/cygwin/cygwin.cmd` - запуск среды _cygwin bash_ под текущим пользователем\ |
|
||||
`/cygwin/cygwin-admin.cmd` - запуск среды _cygwin bash_ под администратором |
|
||||
|
|
||||
В среде _cygwin_ уже настроены alias-ы на winws,blockcheck,ip2net,mdig. С путями возиться не нужно! |
|
||||
|
|
||||
> [!TIP] |
|
||||
> Из cygwin можно не только тестировать winws, но и посылать сигналы. |
|
||||
> Доступны команды: |
|
||||
>- `pidof` |
|
||||
>- `kill` |
|
||||
>- `killall` |
|
||||
>- `pgrep` |
|
||||
>- `pkill` |
|
||||
|
|
||||
Но важно понимать, что таким образом не выйдет посылать сигналы **winws**, запущенному из _zapret-winws_, |
|
||||
поскольку там свой `cygwin1.dll`, и они не разделяют общее пространство процессов unix. |
|
||||
_zapret-winws_ - это отдельный комплект для повседневного использования, не требующий что-то еще, но и не связанный со _средой cygwin_. |
|
||||
Специально для посылки сигналов winws в _zapret-winws_ присутствует killall.exe. |
|
||||
|
|
||||
Среду cygwin можно использовать для записи в файл дебаг-лога winws. Для этого пользуйтесь командой tee. |
|
||||
`winws --debug --wf-tcp=80,443 | tee winws.log` |
|
||||
`winws.log` будет в `cygwin/home/<имя_пользователя>` |
|
||||
Если у вас windows 7, то блокнот не поймет переводы строк в стиле unix. Воспользуйтесь командой |
|
||||
`unix2dos winws.log` |
|
||||
|
|
||||
> [!CAUTION] |
|
||||
> Поскольку 32-битные windows мало востребованы, _zapret-win-bundle_ существует только в варианте для windows _x64/arm64_. |
|
||||
|
|
||||
## Автозапуск winws |
|
||||
|
|
||||
Для запуска **winws** вместе с windows есть 2 варианта. Планировщик задач или службы windows. |
|
||||
|
|
||||
Можно создавать задачи и управлять ими через консольную программу schtasks. |
|
||||
В директории `binaries/windows-x86_64/winws` подготовлены файлы `task_*.cmd` . |
|
||||
В них реализовано создание, удаление, старт и стоп одной копии процесса winws с параметрами из переменной `%WINWS1%`. |
|
||||
Исправьте параметры на нужную вам стратегию. Если для разных фильтров применяется разная стратегия, размножьте код |
|
||||
для задач _winws1_,_winws2_,_winws3_,_..._ |
|
||||
|
|
||||
Аналогично настраивается вариант запуска через службы windows. Смотрите `service_*.cmd`. |
|
||||
|
|
||||
Все батники требуется запускать от имени администратора. |
|
||||
|
|
||||
Управлять задачами можно так же из графической программы управления планировщиком `taskschd.msc` |
|
||||
|
|
||||
## Особенности Windows Server |
|
||||
|
|
||||
winws слинкован с wlanapi.dll, который по умолчанию не установлен в windows server. |
|
||||
Для решения этой проблемы запустите power shell под администратором и выполните команду `Install-WindowsFeature -Name Wireless-Networking`. |
|
||||
После чего перезагрузите систему. |
|
||||
@ -1,653 +0,0 @@ |
|||||
Данный мануал пишется не как копипастная инструкция, а как помощь уже соображающему. |
|
||||
Если вы не знаете основ сетей, linux, openwrt, а пытаетесь что-то скопипастить отсюда без малейшего |
|
||||
понимания смысла, то маловероятно, что у вас что-то заработает. Не тратье свое время напрасно. |
|
||||
Цель - донести принципы как это настраивается вообще, а не указать какую буковку где вписать. |
|
||||
|
|
||||
|
|
||||
Есть возможность поднять свой VPN сервер ? Не хотим использовать redsocks ? |
|
||||
Хотим завертывать на VPN только часть трафика ? |
|
||||
Например, из ipset zapret только порт tcp:443, из ipban - весь трафик, не только tcp ? |
|
||||
Да, с VPN такое возможно. |
|
||||
Опишу понятийно как настраивается policy based routing в openwrt на примере wireguard. |
|
||||
Вместо wireguard можно использовать openvpn или любой другой. Но wireguard прекрасен сразу несколькими вещами. |
|
||||
Главная из которых - в разы большая скорость, даже немного превышающая ipsec. |
|
||||
Ведь openvpn основан на tun, а tun - всегда в разы медленнее решения в kernel mode, |
|
||||
и если для PC оно может быть не так актуально, для soho роутеров - более чем. |
|
||||
Wireguard может дать 50 mbps там, где openvpn еле тащит 10. |
|
||||
Но есть и дополнительное требование. Wireguard работает в ядре, значит ядро должно |
|
||||
быть под вашим контролем. vps на базе openvz не подойдет. Нужен xen, kvm, |
|
||||
любой другой вариант, где загружается ваше собственное ядро, а не используется |
|
||||
общее, разделяемое на множество vps. |
|
||||
|
|
||||
Понятийно необходимо выполнить следующие шаги : |
|
||||
1) Поднять vpn сервер. |
|
||||
2) Настроить vpn клиент. Результат этого шага - получение поднятого интерфейса vpn. |
|
||||
Будь то wireguard, openvpn или любой другой тип vpn. |
|
||||
3) Создать такую схему маршрутизации, при которой пакеты, помечаемые особым mark, |
|
||||
попадают на vpn, а остальные идут обычным способом. |
|
||||
4) Создать правила, выставляющие mark для всего трафика, который необходимо рулить на vpn. |
|
||||
Критерии могут быть любые, ограниченные лишь возможностями iptables и вашим воображением. |
|
||||
|
|
||||
Будем считать наш vpn сервер находится на ip 91.15.68.202. |
|
||||
Вешать его будем на udp порт 12345. На этот же порт будем вешать и клиентов. |
|
||||
Сервер работает под debian 9 или выше. Клиент работает под openwrt. |
|
||||
Для vpn отведем подсеть 192.168.254.0/24. |
|
||||
|
|
||||
--- Если нет своего сервера --- |
|
||||
|
|
||||
Но есть конфиг от VPN провайдера или от друга "Васи", который захотел с вами поделиться. |
|
||||
Тогда вам не надо настраивать сервер, задача упрощается. Делается невозможным вариант настройки |
|
||||
без masquerade (см ниже). |
|
||||
Из конфига вытаскиваете приватный ключ своего пира и публичный ключ сервера, ip/host/port сервера, |
|
||||
используете их в настройках openwrt вместо сгенеренных самостоятельно. |
|
||||
|
|
||||
--- Поднятие сервера --- |
|
||||
|
|
||||
Wireguard был включен в ядро linux с версии 5.6. |
|
||||
Если у вас ядро >=5.6, то достаточно установить пакет wireguard-tools. Он содержит user-mode компоненты wireguard. |
|
||||
Посмотрите, возможно в вашем дистрибутиве ядро по умолчанию более старое, но в репозитории |
|
||||
имеются бэкпорты новых версий. Лучше будет обновить ядро из репозитория. |
|
||||
|
|
||||
В репозитории может быть пакет wireguard-dkms. Это автоматизированное средство сборки |
|
||||
wireguard с исходников, в том числе модуль ядра. Можно пользоваться им. |
|
||||
|
|
||||
Иначе вам придется собрать wireguard самому. Ядро должно быть не ниже 3.10. |
|
||||
На сервере должны быть установлены заголовки ядра (linux-headers-...) и компилятор gcc. |
|
||||
|
|
||||
# git clone --depth 1 https://git.zx2c4.com/wireguard-linux-compat |
|
||||
# cd wireguard-linux-compat/src |
|
||||
# make |
|
||||
# strip --strip-debug wireguard.ko |
|
||||
# sudo make install |
|
||||
|
|
||||
wireguard основан на понятии криптороутинга. Каждый пир (сервер - тоже пир) |
|
||||
имеет пару открытый/закрытый ключ. Закрытый ключ остается у пира, |
|
||||
открытый прописывается у его партнера. Каждый пир авторизует другого |
|
||||
по знанию приватного ключа, соответствующего прописанному у него публичному ключу. |
|
||||
Протокол построен таким образом, что на все неправильные udp пакеты не следует ответа. |
|
||||
Не знаешь приватный ключ ? Не смог послать правильный запрос ? Долбись сколько влезет, |
|
||||
я тебе ничего не отвечу. Это защищает от активного пробинга со стороны DPI и просто |
|
||||
экономит ресурсы. |
|
||||
Значит первым делом нужно создать 2 пары ключей : для сервера и для клиента. |
|
||||
wg genkey генерит приватный ключ, wg pubkey получает из него публичный ключ. |
|
||||
|
|
||||
# wg genkey |
|
||||
oAUkmhoREtFQ5D5yZmeHEgYaSWCcLYlKe2jBP7EAGV0= |
|
||||
# echo oAUkmhoREtFQ5D5yZmeHEgYaSWCcLYlKe2jBP7EAGV0= | wg pubkey |
|
||||
bCdDaPYSTBZVO1HTmKD+Tztuf3PbOWGDWfz7Lb1E6C4= |
|
||||
# wg genkey |
|
||||
OKXX0TSlyjJmGt3/yHlHxi0AqjJ0vh+Msne3qEHk0VM= |
|
||||
# echo OKXX0TSlyjJmGt3/yHlHxi0AqjJ0vh+Msne3qEHk0VM= | wg pubkey |
|
||||
EELdA2XzjcKxtriOCPBXMOgxlkgpbRdIyjtc3aIpkxg= |
|
||||
|
|
||||
Пишем конфиг |
|
||||
--/etc/wireguard/wgvps.conf------------------- |
|
||||
[Interface] |
|
||||
PrivateKey = OKXX0TSlyjJmGt3/yHlHxi0AqjJ0vh+Msne3qEHk0VM= |
|
||||
ListenPort = 12345 |
|
||||
|
|
||||
[Peer] |
|
||||
#Endpoint = |
|
||||
PublicKey = bCdDaPYSTBZVO1HTmKD+Tztuf3PbOWGDWfz7Lb1E6C4= |
|
||||
AllowedIPs = 192.168.254.3 |
|
||||
PersistentKeepalive=20 |
|
||||
---------------------------------------------- |
|
||||
|
|
||||
Wireguard - минималистичный vpn. В нем нет никаких средств для автоконфигурации ip. |
|
||||
Все придется прописывать руками. |
|
||||
В wgvps.conf должны быть перечислены все пиры с их публичными ключами, |
|
||||
а так же прописаны допустимые для них ip адреса. |
|
||||
Назначим нашему клиенту 192.168.254.3. Сервер будет иметь ip 192.168.254.1. |
|
||||
Endpoint должен быть прописан хотя бы на одном пире. |
|
||||
Если endpoint настроен для пира, то wireguard будет периодически пытаться к нему подключиться. |
|
||||
В схеме клиент/сервер у сервера можно не прописывать endpoint-ы пиров, что позволит |
|
||||
менять ip и быть за nat. Endpoint пира настраивается динамически после успешной фазы |
|
||||
проверки ключа. |
|
||||
|
|
||||
Включаем маршрутизцию : |
|
||||
# echo net.ipv4.ip_forward = 1 >>/etc/sysctl.conf |
|
||||
# sysctl -p |
|
||||
|
|
||||
Интерфейс конфигурится стандартно для дебианоподобных систем : |
|
||||
|
|
||||
--/etc/network/interfaces.d/wgvps------------- |
|
||||
auto wgvps |
|
||||
iface wgvps inet static |
|
||||
address 192.168.254.1 |
|
||||
netmask 255.255.255.0 |
|
||||
pre-up ip link add $IFACE type wireguard |
|
||||
pre-up wg setconf $IFACE /etc/wireguard/$IFACE.conf |
|
||||
post-up iptables -t nat -A POSTROUTING -o eth0 -s 192.168.254.0/24 -j MASQUERADE |
|
||||
post-up iptables -A FORWARD -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu |
|
||||
post-down iptables -D FORWARD -o eth0 -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu |
|
||||
post-down iptables -t nat -D POSTROUTING -o eth0 -s 192.168.254.0/24 -j MASQUERADE |
|
||||
post-down ip link del $IFACE |
|
||||
---------------------------------------------- |
|
||||
|
|
||||
Поднятие через ifup wgvps, опускание через ifdown wgvps. |
|
||||
При поднятии интерфейса заодно настраивается nat. eth0 здесь означает интерфейс vpn сервера с инетовским ip адресом. |
|
||||
Если у вас какая-то система управления фаерволом, то надо настройку nat прикручивать туда. |
|
||||
Пример написан для простейшего случая, когда никаких ограничений нет, таблицы iptables пустые. |
|
||||
Чтобы посмотреть текущие настройки wireguard, запустите 'wg' без параметров. |
|
||||
|
|
||||
|
|
||||
--- Поднятие клиента --- |
|
||||
|
|
||||
# opkg update |
|
||||
# opkg install wireguard-tools |
|
||||
|
|
||||
Добавляем записи в конфиги. |
|
||||
|
|
||||
--/etc/config/network-------------------------- |
|
||||
config interface 'wgvps' |
|
||||
option proto 'wireguard' |
|
||||
option auto '1' |
|
||||
option private_key 'oAUkmhoREtFQ5D5yZmeHEgYaSWCcLYlKe2jBP7EAGV0=' |
|
||||
option listen_port '12345' |
|
||||
option metric '9' |
|
||||
option mtu '1420' |
|
||||
|
|
||||
config wireguard_wgvps |
|
||||
option public_key 'EELdA2XzjcKxtriOCPBXMOgxlkgpbRdIyjtc3aIpkxg=' |
|
||||
list allowed_ips '0.0.0.0/0' |
|
||||
option endpoint_host '91.15.68.202' |
|
||||
option endpoint_port '12345' |
|
||||
option route_allowed_ips '0' |
|
||||
option persistent_keepalive '20' |
|
||||
|
|
||||
config interface 'wgvps_ip' |
|
||||
option proto 'static' |
|
||||
option ifname '@wgvps' |
|
||||
list ipaddr '192.168.254.3/24' |
|
||||
|
|
||||
config route |
|
||||
option interface 'wgvps' |
|
||||
option target '0.0.0.0/0' |
|
||||
option table '100' |
|
||||
|
|
||||
config rule |
|
||||
option mark '0x800/0x800' |
|
||||
option priority '100' |
|
||||
option lookup '100' |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
--/etc/config/firewall-------------------------- |
|
||||
config zone |
|
||||
option name 'tunvps' |
|
||||
option output 'ACCEPT' |
|
||||
option input 'REJECT' |
|
||||
option masq '1' |
|
||||
option mtu_fix '1' |
|
||||
option forward 'REJECT' |
|
||||
option network 'wgvps wgvps_ip' |
|
||||
|
|
||||
config forwarding |
|
||||
option dest 'tunvps' |
|
||||
option src 'lan' |
|
||||
|
|
||||
config rule |
|
||||
option name 'Allow-ICMP-tunvps' |
|
||||
option src 'tunvps' |
|
||||
option proto 'icmp' |
|
||||
option target 'ACCEPT' |
|
||||
|
|
||||
config rule |
|
||||
option target 'ACCEPT' |
|
||||
option src 'wan' |
|
||||
option proto 'udp' |
|
||||
option family 'ipv4' |
|
||||
option src_port '12345' |
|
||||
option src_ip '91.15.68.202' |
|
||||
option name 'WG-VPS' |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
Что тут было сделано : |
|
||||
*) Настроен интерфейс wireguard. Указан собственный приватный ключ. |
|
||||
*) Настроен пир-партнер с указанием его публичнго ключа и endpoint (ip:port нашего сервера) |
|
||||
такая настройка заставит периодически долбиться на сервер по указанному ip |
|
||||
route_allowed_ip '0' запрещает автоматическое создание маршрута |
|
||||
allowed_ips '0.0.0.0/0' разрешает пакеты с любым адресом источника. |
|
||||
ведь мы собираемся подключаться к любым ip в инете |
|
||||
persistent_keepalive '20' помогает исключить дропание mapping на nat-е, если мы сидим за ним, |
|
||||
да и вообще полезная вещь, чтобы не было подвисших пиров |
|
||||
*) Статическая конфигурация ip интерфейса wgvps. |
|
||||
*) Маршрут default route на wgvps в отдельной таблице маршрутизации с номером 100. Аналог команды ip route add .. table 100 |
|
||||
*) Правило использовать таблицу 100 при выставлении в mark бита 0x800. Аналог команды ip rule. |
|
||||
*) Отдельная зона фаервола для VPN - 'tunvps'. В принципе ее можно не создавать, можете приписать интерфейс к зоне wan. |
|
||||
Но в случае с отдельной зоной можно настроить особые правила на подключения с vpn сервера в сторону клиента. |
|
||||
*) Разрешение форвардинга между локалкой за роутером и wgvps. |
|
||||
*) Разрешение принимать icmp от vpn сервера, включая пинги. ICMP жизненно важны для правильного функционирования ip сети ! |
|
||||
*) И желательно проткнуть дырку в фаерволе, чтобы принимать пакеты wireguard со стороны инетовского ip vpn сервера. |
|
||||
Конечно, оно скорее всего заработает и так, потому что первый пакет пойдет от клиента к серверу и тем самым создаст |
|
||||
запись в conntrack. Все дальнейшие пакеты в обе стороны подпадут под состояние ESTABLISHED и будут пропущены. |
|
||||
Запись будет поддерживаться за счет периодических запросов keep alive. Но если вы вдруг уберете keep alive или |
|
||||
выставите таймаут, превышающий udp таймаут в conntrack, то могут начаться ошибки, висы и переподключения. |
|
||||
Если же в фаерволе проткнута дырка, то пакеты от сервера не будут заблокированы ни при каких обстоятельствах. |
|
||||
|
|
||||
# /etc/init.d/firewall restart |
|
||||
# ifup wgvps |
|
||||
# ifconfig wgvps |
|
||||
# ping 192.168.254.1 |
|
||||
|
|
||||
Если все хорошо, должны ходить пинги. |
|
||||
С сервера не помешает : |
|
||||
# ping 192.168.254.3 |
|
||||
|
|
||||
|
|
||||
--- Подготовка zapret --- |
|
||||
|
|
||||
Выполните install_easy.sh. Он настроит режим обхода DPI. Если обход DPI не нужен - не включайте tpws и nfqws. |
|
||||
Так же инсталятор заресолвит домены из ipset/zapret-hosts-user-ipban.txt и внесет крон-джоб для периодического обновления ip. |
|
||||
|
|
||||
Если вы используете в своих правилах ipset zapret, то он ресолвится и обновляется только, если выбран режим фильтрации обхода DPI по ipset. |
|
||||
По сути он вам нужен исключительно, если обход DPI не помогает. Например, удается как-то пробить http, но не удается пробить https. |
|
||||
И при этом вы хотите, чтобы на VPN направлялись только ip из скачанного ip листа, в добавок к заресолвленному ipset/zapret-hosts-user.txt. |
|
||||
Именно этот случай и рассмотрен в данном примере. Если это не так, то убирайте правила с портом 443 из нижеприведенных правил iptables/nftables. |
|
||||
Если не хотите ограничиваться листом, и хотите направлять все на порт 443, то уберите фильтры из правил iptables/nftables, |
|
||||
связанные с ipset/nfset "zapret". |
|
||||
|
|
||||
Фильтрация по именам доменов (MODE_FILTER=hostlist) невозможна средствами iptables/nftables. Она производится исключительно в tpws и nfqws |
|
||||
по результатам анализа протокола прикладного уровня, иногда достаточно сложного, связанного с дешифровкой пакета (QUIC). |
|
||||
Скачиваются листы с именами доменов, не ip адресами. ipset/zapret-hosts-user.txt не ресолвится, а используется как hostlist. |
|
||||
Потому вам нельзя расчитывать на ipset zapret. |
|
||||
Тем не менее при выборе этого режима фильтрации , либо вовсе при ее отсутствии (MODE_FILTER=none), ipset/zapret-hosts-user-ipban.txt |
|
||||
все равно ресолвится. Вы всегда можете расчитывать на ipset/nfset "ipban", "nozapret". |
|
||||
|
|
||||
"nozapret" - это ipset/nfset, связанный с системой исключения ip. Сюда загоняется все из ipset/zapret-hosts-user-exclude.txt после ресолвинга. |
|
||||
Его учет крайне желателен, чтобы вдруг из скачанного листа не просочились записи, например, 192.168.0.0/16 и не заставили лезть туда через VPN. |
|
||||
Хотя скрипты получения листов и пытаются отсечь IP локалок, но так будет намного надежнее. |
|
||||
|
|
||||
--- Маркировка трафика --- |
|
||||
|
|
||||
Завернем на vpn все из ipset zapret на tcp:443 и все из ipban. |
|
||||
OUTPUT относится к исходящим с роутера пакетам, PREROUTING - ко всем остальным. |
|
||||
Если с самого роутера ничего заруливать не надо, можно опустить часть, отвечающую за OUTPUT. |
|
||||
|
|
||||
--/etc/firewall.user---------------------------- |
|
||||
. /opt/zapret/init.d/openwrt/functions |
|
||||
|
|
||||
create_ipset no-update |
|
||||
|
|
||||
network_find_wan4_all wan_iface |
|
||||
for ext_iface in $wan_iface; do |
|
||||
network_get_device DEVICE $ext_iface |
|
||||
ipt OUTPUT -t mangle -o $DEVICE -p tcp --dport 443 -m set --match-set zapret dst -m set ! --match-set nozapret dst -j MARK --set-mark 0x800/0x800 |
|
||||
ipt OUTPUT -t mangle -o $DEVICE -m set --match-set ipban dst -m set ! --match-set nozapret dst -j MARK --set-mark 0x800/0x800 |
|
||||
done |
|
||||
|
|
||||
network_get_device DEVICE lan |
|
||||
ipt PREROUTING -t mangle -i $DEVICE -p tcp --dport 443 -m set --match-set zapret dst -m set ! --match-set nozapret dst -j MARK --set-mark 0x800/0x800 |
|
||||
ipt PREROUTING -t mangle -i $DEVICE -m set --match-set ipban dst -m set ! --match-set nozapret dst -j MARK --set-mark 0x800/0x800 |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
# /etc/init.d/firewall restart |
|
||||
|
|
||||
Чтобы правила обновлялись в процессе поднятия интерфейсов в системе, нужно внести параметр "reload" в указанное место : |
|
||||
--- /etc/config/firewall --- |
|
||||
config include |
|
||||
option path '/etc/firewall.user' |
|
||||
option reload '1' |
|
||||
---------------------------- |
|
||||
|
|
||||
|
|
||||
--- Маркировка трафика nftables --- |
|
||||
|
|
||||
В новых openwrt по умолчанию установлен nftables, iptables отсутствует. |
|
||||
Есть вариант снести nftables + fw4 и заменить их на iptables + fw3. |
|
||||
Веб интерфейс luci понимает прозрачно и fw3, и fw4. Однако, при установке iptables и fw3 новые пакеты |
|
||||
будут устанавливаться без сжатия squashfs. Убедитесь, что у вас достаточно места. |
|
||||
Либо сразу настраивайте образ через image builder. |
|
||||
|
|
||||
Фаервол fw4 работает в одноименной nftable - "inet fw4". "inet" означает, что таблица принимает и ipv4, и ipv6. |
|
||||
Поскольку для маркировки трафика используется nfset, принадлежащий таблице zapret, цепочки необходимо помещать в ту же таблицу. |
|
||||
Для синхронизации лучше всего использовать хук |
|
||||
INIT_FW_POST_UP_HOOK="/etc/firewall.zapret.hook.post_up" |
|
||||
Параметр нужно раскоментировать в /opt/zapret/config. Далее надо создать указанный файл и дать ему chmod 755. |
|
||||
|
|
||||
--/etc/firewall.zapret.hook.post_up---------------------------- |
|
||||
#!/bin/sh |
|
||||
|
|
||||
ZAPRET_NFT_TABLE=zapret |
|
||||
|
|
||||
cat << EOF | nft -f - 2>/dev/null |
|
||||
delete chain inet $ZAPRET_NFT_TABLE my_output |
|
||||
delete chain inet $ZAPRET_NFT_TABLE my_prerouting |
|
||||
EOF |
|
||||
|
|
||||
cat << EOF | nft -f - |
|
||||
add chain inet $ZAPRET_NFT_TABLE my_output { type route hook output priority mangle; } |
|
||||
flush chain inet $ZAPRET_NFT_TABLE my_output |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_output oifname @wanif ip daddr @ipban ip daddr != @nozapret meta mark set mark or 0x800 |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_output oifname @wanif tcp dport 443 ip daddr @zapret ip daddr != @nozapret meta mark set mark or 0x800 |
|
||||
|
|
||||
add chain inet $ZAPRET_NFT_TABLE my_prerouting { type filter hook prerouting priority mangle; } |
|
||||
flush chain inet $ZAPRET_NFT_TABLE my_prerouting |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_prerouting iifname @lanif ip daddr @ipban ip daddr != @nozapret meta mark set mark or 0x800 |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_prerouting iifname @lanif tcp dport 443 ip daddr @zapret ip daddr != @nozapret meta mark set mark or 0x800 |
|
||||
EOF |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
# /etc/init.d/zapret restart_fw |
|
||||
|
|
||||
Проверка правил : |
|
||||
# /etc/init.d/zapret list_table |
|
||||
или |
|
||||
# nft -t list table inet zapret |
|
||||
|
|
||||
Должны быть цепочки my_prerouting и my_output. |
|
||||
|
|
||||
Проверка заполнения nfsets : |
|
||||
# nft list set inet zapret zapret |
|
||||
# nft list set inet zapret ipban |
|
||||
# nft list set inet zapret nozapret |
|
||||
|
|
||||
Проверка заполнения множеств lanif, wanif, wanif6, link_local : |
|
||||
# /etc/init.d/zapret list_ifsets |
|
||||
|
|
||||
Должны присутствовать имена интерфейсов во множествах lanif, wanif. |
|
||||
wanif6 заполняется только при включении ipv6. |
|
||||
link_local нужен только для tpws при включении ipv6. |
|
||||
|
|
||||
--- По поводу двойного NAT --- |
|
||||
|
|
||||
В описанной конфигурации nat выполняется дважды : на роутере-клиенте происходит замена адреса источника из LAN |
|
||||
на 192.168.254.3 и на сервере замена 192.168.254.3 на внешний адрес сервера в инете. |
|
||||
Зачем так делать ? Исключительно для простоты настройки. Или на случай, если сервер wireguard не находится под вашим контролем. |
|
||||
Делать для вас нижеописанные настройки никто не будет с вероятностью, близкой к 100%. |
|
||||
Если сервер wireguard - ваш, и вы готовы чуток еще поднапрячься и не хотите двойного nat, |
|
||||
то можете вписать в /etc/config/firewall "masq '0'", на сервер дописать маршрут до вашей подсети lan. |
|
||||
Чтобы не делать это для каждого клиента, можно отвести под всех клиентов диапазон 192.168.0.0-192.168.127.255 |
|
||||
и прописать его одним маршрутом. |
|
||||
|
|
||||
--/etc/network/interfaces.d/wgvps------------- |
|
||||
post-up ip route add dev $IFACE 192.168.0.0/17 |
|
||||
post-down ip route del dev $IFACE 192.168.0.0/17 |
|
||||
---------------------------------------------- |
|
||||
|
|
||||
Так же необходимо указать wireguard дополнительные разрешенные ip для peer : |
|
||||
|
|
||||
--/etc/wireguard/wgvps.conf------------------- |
|
||||
[Peer] |
|
||||
PublicKey = bCdDaPYSTBZVO1HTmKD+Tztuf3PbOWGDWfz7Lb1E6C4= |
|
||||
AllowedIPs = 192.168.254.3, 192.168.2.0/24 |
|
||||
---------------------------------------------- |
|
||||
|
|
||||
Всем клиентам придется назначать различные диапазоны адресов в lan и индивидуально прописывать AllowedIPs |
|
||||
для каждого peer. |
|
||||
|
|
||||
# ifdown wgvps ; ifup wgvps |
|
||||
|
|
||||
На клиенте разрешим форвард icmp, чтобы работал пинг и корректно определялось mtu. |
|
||||
|
|
||||
--/etc/config/firewall-------------------------- |
|
||||
config rule |
|
||||
option name 'Allow-ICMP-tunvps' |
|
||||
option src 'tunvps' |
|
||||
option dest 'lan' |
|
||||
option proto 'icmp' |
|
||||
option target 'ACCEPT' |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
Существуют еще два неочевидных нюанса. |
|
||||
|
|
||||
Первый из них касается пакетов с самого роутера (цепочка OUTPUT). |
|
||||
Адрес источника выбирается по особому алгоритму, если программа явно его не задала, еще до этапа iptables. |
|
||||
Он берется с интерфейса, куда бы пошел пакет при нормальном раскладе. |
|
||||
Обратная маршрутизация с VPN станет невозможной, да и wireguard такие пакеты порежет, поскольку они не вписываются в AllowedIPs. |
|
||||
Никаким мистическим образом автоматом source address не поменяется. |
|
||||
В прошлом варианте настройки проблема решалось через маскарад. Сейчас же маскарада нет. |
|
||||
Потому все же придется его делать в случае, когда пакет изначально направился бы через wan, |
|
||||
а мы его завертываем на VPN. Помечаем такие пакеты марком 0x1000. |
|
||||
Если вам не актуальны исходящие с самого роутера, то можно ничего не менять. |
|
||||
|
|
||||
Другой нюанс связан с обработкой проброшенных на vps портов, соединения по которым приходят как входящие с интерфейса wgvps. |
|
||||
Представьте себе, что вы пробросили порт 2222. Кто-то подключается с адреса 1.2.3.4. Вам приходит пакет SYN 1.2.3.4:51723=>192.168.2.2:2222. |
|
||||
По правилам маршрутизации он пойдет в локалку. 192.168.2.2 его обработает, ответит пакетом ACK 192.168.2.2:2222=>1.2.3.4:51723. |
|
||||
Этот пакет придет на роутер. И куда он дальше пойдет ? Если он не занесен в ipban, то согласно правилам машрутизации |
|
||||
он пойдет по WAN интерфейсу, а не по исходному wgvps. |
|
||||
Чтобы решить эту проблему, необходимо воспользоваться CONNMARK. Существуют 2 отдельных марка : fwmark и connmark. |
|
||||
connmark относится к соединению, fwmark - к пакету. Трэкингом соединений занимается conntrack. |
|
||||
Посмотреть его таблицу можно командой "conntrack -L". Там же найдете connmark : mark=xxxx. |
|
||||
Как только видим приходящий с wgvps пакет с новым соединением, отмечаем его connmark как 0x800/0x800. |
|
||||
При этом fwmark не меняется, иначе бы пакет тут же бы завернулся обратно на wgvps согласно ip rule. |
|
||||
Если к нам приходит пакет с какого-то другого интерфейса, то восстанавливаем его connmark в fwmark по маске 0x800. |
|
||||
И теперь он подпадает под правило ip rule, заворачиваясь на wgvps, что и требовалось. |
|
||||
|
|
||||
Альтернативное решение - использовать на VPSке для проброса портов не только DNAT, но и SNAT/MASQUERADE. Тогда source address |
|
||||
будет заменен на 192.168.254.1. Он по таблице маршрутизации пойдет на wgvps. Но в этом случае клиентские программы, |
|
||||
на которые осуществляется проброс портов, не будут видеть реальный IP подключенца. |
|
||||
|
|
||||
--/etc/firewall.user---------------------------- |
|
||||
. /opt/zapret/init.d/openwrt/functions |
|
||||
|
|
||||
create_ipset no-update |
|
||||
|
|
||||
network_find_wan4_all wan_iface |
|
||||
for ext_iface in $wan_iface; do |
|
||||
network_get_device DEVICE $ext_iface |
|
||||
ipt OUTPUT -t mangle -o $DEVICE -p tcp --dport 443 -m set --match-set zapret dst -m set ! --match-set nozapret dst -j MARK --set-mark 0x800/0x800 |
|
||||
ipt OUTPUT -t mangle -o $DEVICE -m set --match-set ipban dst -m set ! --match-set nozapret dst -j MARK --set-mark 0x800/0x800 |
|
||||
ipt OUTPUT -t mangle -o $DEVICE -j MARK --set-mark 0x1000/0x1000 |
|
||||
done |
|
||||
|
|
||||
network_get_device DEVICE lan |
|
||||
ipt PREROUTING -t mangle -i $DEVICE -p tcp --dport 443 -m set --match-set zapret dst -m set ! --match-set nozapret dst -j MARK --set-mark 0x800/0x800 |
|
||||
ipt PREROUTING -t mangle -i $DEVICE -m set --match-set ipban dst -m set ! --match-set nozapret dst -j MARK --set-mark 0x800/0x800 |
|
||||
|
|
||||
# do masquerade for OUTPUT to ensure correct outgoing address |
|
||||
ipt postrouting_tunvps_rule -t nat -m mark --mark 0x1000/0x1000 -j MASQUERADE |
|
||||
|
|
||||
# incoming from wgvps |
|
||||
network_get_device DEVICE wgvps |
|
||||
ipt PREROUTING -t mangle ! -i $DEVICE -j CONNMARK --restore-mark --nfmask 0x800 --ctmask 0x800 |
|
||||
ipt PREROUTING -t mangle -i $DEVICE -m conntrack --ctstate NEW -j CONNMARK --set-mark 0x800/0x800 |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
# /etc/init.d/firewall restart |
|
||||
|
|
||||
Вариант nftables : |
|
||||
|
|
||||
--/etc/firewall.zapret.hook.post_up---------------------------- |
|
||||
#!/bin/sh |
|
||||
|
|
||||
ZAPRET_NFT_TABLE=zapret |
|
||||
DEVICE=wgvps |
|
||||
|
|
||||
cat << EOF | nft -f - 2>/dev/null |
|
||||
delete chain inet $ZAPRET_NFT_TABLE my_output |
|
||||
delete chain inet $ZAPRET_NFT_TABLE my_prerouting |
|
||||
delete chain inet $ZAPRET_NFT_TABLE my_nat |
|
||||
EOF |
|
||||
|
|
||||
cat << EOF | nft -f - |
|
||||
add chain inet $ZAPRET_NFT_TABLE my_output { type route hook output priority mangle; } |
|
||||
flush chain inet $ZAPRET_NFT_TABLE my_output |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_output oifname @wanif ip daddr @ipban ip daddr != @nozapret meta mark set mark or 0x800 |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_output oifname @wanif tcp dport 443 ip daddr @zapret ip daddr != @nozapret meta mark set mark or 0x800 |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_output oifname @wanif meta mark set mark or 0x1000 |
|
||||
|
|
||||
add chain inet $ZAPRET_NFT_TABLE my_prerouting { type filter hook prerouting priority mangle; } |
|
||||
flush chain inet $ZAPRET_NFT_TABLE my_prerouting |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_prerouting iifname $DEVICE ct state new ct mark set ct mark or 0x800 |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_prerouting iifname != $DEVICE meta mark set ct mark and 0x800 |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_prerouting iifname @lanif ip daddr @ipban ip daddr != @nozapret meta mark set mark or 0x800 |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_prerouting iifname @lanif tcp dport 443 ip daddr @zapret ip daddr != @nozapret meta mark set mark or 0x800 |
|
||||
|
|
||||
add chain inet $ZAPRET_NFT_TABLE my_nat { type nat hook postrouting priority 100 ; } |
|
||||
flush chain inet $ZAPRET_NFT_TABLE my_nat |
|
||||
add rule inet $ZAPRET_NFT_TABLE my_nat oifname $DEVICE mark and 0x1000 == 0x1000 masquerade |
|
||||
EOF |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
# /etc/init.d/zapret restart_fw |
|
||||
|
|
||||
К сожалению, здесь возможности nftables немного хромают. Полноценного эквивалента CONNMARK --restore-mark --nfmask |
|
||||
не существует. Оригинал iptables предполагал копирование одного бита 0x800 из connmark в mark. |
|
||||
Лучшее, что можно сделать в nftables, это копирование одного бита с занулением всех остальных. |
|
||||
Сложные выражения типа "meta mark set mark and ~0x800 or (ct mark and 0x800)" nft не понимает. |
|
||||
Об этом же говорит попытка перевода через iptables-translate. |
|
||||
|
|
||||
Сейчас уже можно с vpn сервера пингануть ip адрес внутри локалки клиента. Пинги должны ходить. |
|
||||
|
|
||||
Отсутствие двойного NAT значительно облегчает проброс портов с внешнего IP vpn сервера в локалку какого-либо клиента. |
|
||||
Для этого надо выполнить 2 действия : добавить разрешение в фаервол на клиенте и сделать dnat на сервере. |
|
||||
Пример форварда портов 5001 и 5201 на 192.168.2.2 : |
|
||||
|
|
||||
--/etc/config/firewall-------------------------- |
|
||||
config rule |
|
||||
option target 'ACCEPT' |
|
||||
option src 'tunvps' |
|
||||
option dest 'lan' |
|
||||
option proto 'tcp udp' |
|
||||
option dest_port '5001 5201' |
|
||||
option dest_ip '192.168.2.2' |
|
||||
option name 'IPERF' |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
# /etc/init.d/firewall restart |
|
||||
# /etc/init.d/zapret restart_fw |
|
||||
|
|
||||
--/etc/network/interfaces.d/wgvps------------- |
|
||||
post-up iptables -t nat -A PREROUTING -i eth0 -p tcp -m multiport --dports 5001,5201 -j DNAT --to-destination 192.168.2.2 |
|
||||
post-up iptables -t nat -A PREROUTING -i eth0 -p udp -m multiport --dports 5001,5201 -j DNAT --to-destination 192.168.2.2 |
|
||||
post-down iptables -t nat -D PREROUTING -i eth0 -p tcp -m multiport --dports 5001,5201 -j DNAT --to-destination 192.168.2.2 |
|
||||
post-down iptables -t nat -D PREROUTING -i eth0 -p udp -m multiport --dports 5001,5201 -j DNAT --to-destination 192.168.2.2 |
|
||||
---------------------------------------------- |
|
||||
|
|
||||
# ifdown wgvps ; ifup wgvps |
|
||||
|
|
||||
Пример приведен для iperf и iperf3, чтобы показать как пробрасывать несколько портов tcp+udp с минимальным количеством команд. |
|
||||
Проброс tcp и udp порта так же необходим для полноценной работы bittorrent клиента, чтобы работали входящие. |
|
||||
|
|
||||
--- Как мне отправлять на vpn весь трафик с bittorrent ? --- |
|
||||
|
|
||||
Можно поступить так : посмотрите порт в настройках torrent клиента, убедитесь, что не поставлено "случайный порт", |
|
||||
добавьте на роутер правило маркировки по порту источника. |
|
||||
Но мне предпочтительно иное решение. На windows есть замечательная возможность |
|
||||
прописать правило установки поля качества обслуживания в заголовках ip пакетов в зависимости от процесса-источника. |
|
||||
Для windows 7/2008R2 необходимо будет установить ключик реестра и перезагрузить комп : |
|
||||
# reg add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\Tcpip\QoS /v "Do not use NLA" /t REG_SZ /d "1" |
|
||||
Редактировать политику можно в : gpedit.msc -> Computer Configuration -> Windows Settings -> Policy-based QoS |
|
||||
На win 10 ключик реестра больше не работает, правила qos в gpedit применяются только для профиля домена. |
|
||||
Необходимо пользоваться командой powershell New-NetQosPolicy. Гуглите хелп по ней. Пример : |
|
||||
# powershell New-NetQosPolicy -Name "torrent" -AppPathNameMatchCondition "qbittorrent.exe" -DSCPAction 1 |
|
||||
Однозначно требуется проверка в wireshark или netmon успешности установки поля dscp. Если там по-прежнему 0x00, |
|
||||
значит что-то не сработало. 0x04 означает DSCP=1 (dscp находится в старших 6 битах). |
|
||||
|
|
||||
На роутере в фаер прописываем правило : |
|
||||
|
|
||||
--/etc/config/firewall-------------------------- |
|
||||
config rule |
|
||||
option target 'MARK' |
|
||||
option src 'lan' |
|
||||
option proto 'all' |
|
||||
option extra '-m dscp --dscp 1' |
|
||||
option name 'route-dscp-1' |
|
||||
option set_mark '0x0800/0x0800' |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
# /etc/init.d/firewall restart |
|
||||
|
|
||||
Теперь все с полем dscp "1" идет на vpn. Клиент сам решает какой трафик ему нужно забрасывать |
|
||||
на vpn, перенастраивать роутер не нужно. |
|
||||
На linux клиенте проще всего будет выставлять dscp в iptables по номеру порта источника : |
|
||||
|
|
||||
--/etc/rc.local--------------------------------- |
|
||||
iptables -A OUTPUT -t mangle -p tcp --sport 23444 -j DSCP --set-dscp 1 |
|
||||
iptables -A OUTPUT -t mangle -p udp --sport 23444 -j DSCP --set-dscp 1 |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
можно привязываться к pid процесса, но тогда нужно перенастраивать iptables при каждом перезапуске |
|
||||
торент клиента, это требует рута, и все становится очень неудобно. |
|
||||
|
|
||||
|
|
||||
--- Автоматизация проброса портов через miniupnd --- |
|
||||
|
|
||||
Да, его тоже можно использовать на vps. Только как всегда есть нюансы. |
|
||||
|
|
||||
miniupnpd поддерживает 3 протокола IGD : upnp,nat-pmp и pcp. |
|
||||
upnp и pcp работают через мультикаст, который не пройдет через wgvps. |
|
||||
nat-pmp работает через посылку специальных сообщений на udp:5351 на default gateway. |
|
||||
Обычно их обслуживает miniupnpd на роутере. При создании lease miniupnpd добавляет |
|
||||
правила для проброса портов в цепочку iptables MINIUPNPD, при потери lease - убирает. |
|
||||
|
|
||||
udp:5351 можно перенаправить на vpn сервер через DNAT, чтобы их обрабатывал miniupnpd там. |
|
||||
Но вы должны иметь однозначный критерий перенаправления. |
|
||||
Если вы решили завернуть на vpn все, то проблем нет. Пробрасываем udp:5351 безусловно. |
|
||||
Если у вас идет перенаправление только с торрент, то необходимо к условию перенаправления |
|
||||
добавить условия, выделяющие torrent трафик из прочего. Или по dscp, или по sport. |
|
||||
Чтобы запросы от остальных программ обрабатывались miniupnpd на роутере. |
|
||||
Если какая-то программа создаст lease не там, где нужно, то входящий трафик до нее не дойдет. |
|
||||
|
|
||||
На роутере стоит запретить протокол upnp, чтобы торрент клиент не удовлетворился запросом, |
|
||||
обслуженным по upnp на роутере, и пытался использовать nat-pmp. |
|
||||
|
|
||||
--/etc/config/upnp-------------------------- |
|
||||
config upnpd 'config' |
|
||||
..... |
|
||||
option enable_upnp '0' |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
/etc/init.d/miniupnpd restart |
|
||||
|
|
||||
Делаем проброс порта на роутере. |
|
||||
Для простоты изложения будем считать, что на vpn у нас завернут весь трафик. |
|
||||
Если это не так, то следует добавить фильтр в "config redirect". |
|
||||
Заодно выделяем диапазон портов для торрент клиентов. |
|
||||
Порт в торент клиенте следует прописать какой-то из этого диапазона. |
|
||||
|
|
||||
------------------------------------------------ |
|
||||
config redirect |
|
||||
option enabled '1' |
|
||||
option target 'DNAT' |
|
||||
option src 'lan' |
|
||||
option dest 'tunvps' |
|
||||
option proto 'udp' |
|
||||
option src_dport '5351' |
|
||||
option dest_ip '192.168.254.1' |
|
||||
option dest_port '5351' |
|
||||
option name 'NAT-PMP' |
|
||||
option reflection '0' |
|
||||
config rule |
|
||||
option enabled '1' |
|
||||
option target 'ACCEPT' |
|
||||
option src 'tunvps' |
|
||||
option dest 'lan' |
|
||||
option name 'tunvps-torrent' |
|
||||
option dest_port '28000-28009' |
|
||||
------------------------------------------------ |
|
||||
|
|
||||
/etc/init.d/firewall reload |
|
||||
|
|
||||
|
|
||||
На сервере : |
|
||||
|
|
||||
apt install miniupnpd |
|
||||
|
|
||||
--- /etc/miniupnpd/miniupnpd.conf -------- |
|
||||
enable_natpmp=yes |
|
||||
enable_upnp=no |
|
||||
lease_file=/var/log/upnp.leases |
|
||||
system_uptime=yes |
|
||||
clean_ruleset_threshold=10 |
|
||||
clean_ruleset_interval=600 |
|
||||
force_igd_desc_v1=no |
|
||||
listening_ip=192.168.254.1/16 |
|
||||
ext_ifname=eth0 |
|
||||
------------------------------------------ |
|
||||
|
|
||||
systemctl restart miniupnpd |
|
||||
|
|
||||
listening_ip прописан именно таким образом, чтобы обозначить диапазон разрешенных IP. |
|
||||
С других IP он не будет обрабатывать запросы на редирект. |
|
||||
В ext_ifname впишите название inet интерфейса на сервере. |
|
||||
|
|
||||
Запускаем торрент клиент. Попутно смотрим в tcpdump весь путь udp:5351 до сервера и обратно. |
|
||||
Смотрим syslog сервера на ругань от miniupnpd. |
|
||||
Если все ок, то можем проверить редиректы : iptables -t nat -nL MINIUPNPD |
|
||||
С какого-нибудь другого хоста (не vpn сервер, не ваше подключение) можно попробовать telnet-нуться на проброшенный порт. |
|
||||
Должно установиться соединение. Или качайте торент и смотрите в пирах флаг "I" (incoming). |
|
||||
Если "I" есть и по ним идет закачка, значит все в порядке. |
|
||||
|
|
||||
ОСОБЕННОСТЬ НОВЫХ DEBIAN : по умолчанию используются iptables-nft. miniupnpd работает с iptables-legacy. |
|
||||
ЛЕЧЕНИЕ : update-alternatives --set iptables /usr/sbin/iptables-legacy |
|
||||
@ -1,35 +0,0 @@ |
|||||
#!/system/bin/busybox sh |
|
||||
|
|
||||
# download hostlist from http(s) (need curl, its absent by default), |
|
||||
# feed it to zapret. save flash write cycles |
|
||||
|
|
||||
u="https://your.host.com/censorship/hoslist.txt" |
|
||||
|
|
||||
SCRIPT=$(readlink -f "$0") |
|
||||
EXEDIR=$(dirname "$SCRIPT") |
|
||||
|
|
||||
d=/data/censorship |
|
||||
[ -d $d ] || mkdir $d |
|
||||
f=$d/hostlist.txt |
|
||||
t=/hostlist.txt |
|
||||
|
|
||||
curl -k --fail --max-time 10 -o "$t" "$u" && { |
|
||||
if [ -s "$t" ]; then |
|
||||
m1=$(md5sum "$t" | cut -d ' ' -f 1) |
|
||||
m2=$(md5sum "$f" | cut -d ' ' -f 1) |
|
||||
echo $m1 $m2 |
|
||||
if [ -z "$m2" ] || [ "$m1" != "$m2" ]; then |
|
||||
echo updating hostlist |
|
||||
cp -f "$t" "$f" |
|
||||
else |
|
||||
echo hostlist was not changed. keeping old copy |
|
||||
fi |
|
||||
else |
|
||||
echo downloaded hostlist is empty. disabling zapret |
|
||||
rm "$f" |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
rm -f "$t" |
|
||||
"$EXEDIR/unzapret" |
|
||||
[ -s "$f" ] && exec "$EXEDIR/zapret" "--hostlist=$f" |
|
||||
@ -1,39 +0,0 @@ |
|||||
#!/system/bin/busybox sh |
|
||||
|
|
||||
# download hostlist from http(s) (need curl, its absent by default), |
|
||||
# resolve to ip list, feed to zapret-ip. save flash write cycles |
|
||||
|
|
||||
u="https://your.host.com/censorship/hoslist.txt" |
|
||||
|
|
||||
SCRIPT=$(readlink -f "$0") |
|
||||
EXEDIR=$(dirname "$SCRIPT") |
|
||||
|
|
||||
d=/data/censorship |
|
||||
[ -d $d ] || mkdir $d |
|
||||
f=$d/hostlist.txt |
|
||||
t=/hostlist.txt |
|
||||
i=/iplist.txt |
|
||||
|
|
||||
curl -k --fail --max-time 10 -o "$t" "$u" && { |
|
||||
if [ -s "$t" ]; then |
|
||||
m1=$(md5sum "$t" | cut -d ' ' -f 1) |
|
||||
m2=$(md5sum "$f" | cut -d ' ' -f 1) |
|
||||
echo $m1 $m2 |
|
||||
if [ -z "$m2" ] || [ "$m1" != "$m2" ]; then |
|
||||
echo updating hostlist |
|
||||
cp -f "$t" "$f" |
|
||||
else |
|
||||
echo hostlist was not changed. keeping old copy |
|
||||
fi |
|
||||
else |
|
||||
echo downloaded hostlist is empty. disabling zapret |
|
||||
rm "$f" |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
rm -f "$t" |
|
||||
"$EXEDIR/unzapret-ip" |
|
||||
[ -s "$f" ] && { |
|
||||
mdig --threads=10 --family=4 <"$f" >"$i" |
|
||||
[ -s "$i" ] && exec "$EXEDIR/zapret-ip" "$i" |
|
||||
} |
|
||||
Binary file not shown.
@ -1,9 +0,0 @@ |
|||||
#!/system/bin/busybox sh |
|
||||
|
|
||||
rule="PREROUTING -t nat -i br0 ! -d 192.168.0.0/16 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port 1" |
|
||||
iptables -C $rule 2>/dev/null && iptables -D $rule |
|
||||
killall tpws |
|
||||
|
|
||||
rule="OUTPUT -t mangle -o wan0 -p tcp -m multiport --dports 80,443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass" |
|
||||
iptables -C $rule 2>/dev/null && iptables -D $rule |
|
||||
killall nfqws |
|
||||
@ -1,11 +0,0 @@ |
|||||
#!/system/bin/busybox sh |
|
||||
|
|
||||
rule="PREROUTING -t nat -i br0 -p tcp -m multiport --dports 80,443 -j tpws" |
|
||||
iptables -C $rule 2>/dev/null && iptables -D $rule |
|
||||
iptables -F tpws -t nat |
|
||||
iptables -X tpws -t nat |
|
||||
killall tpws |
|
||||
|
|
||||
rule="OUTPUT -t mangle -o wan0 -p tcp -m multiport --dports 80,443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass" |
|
||||
iptables -C $rule 2>/dev/null && iptables -D $rule |
|
||||
killall nfqws |
|
||||
@ -1,15 +0,0 @@ |
|||||
#!/system/bin/busybox sh |
|
||||
|
|
||||
# $1 - additional parameters for nfqws |
|
||||
|
|
||||
insmod /online/modules/unfuck_nfqueue.ko 2>/dev/null |
|
||||
|
|
||||
rule="PREROUTING -t nat -i br0 ! -d 192.168.0.0/16 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port 1" |
|
||||
iptables -C $rule 2>/dev/null || iptables -I $rule |
|
||||
|
|
||||
tpws --uid 1:3003 --port=1 --daemon |
|
||||
|
|
||||
rule="OUTPUT -t mangle -o wan0 -p tcp -m multiport --dports 80,443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass" |
|
||||
iptables -C $rule 2>/dev/null || iptables -I $rule |
|
||||
|
|
||||
nfqws --uid 2 --qnum=200 --dpi-desync=disorder --dpi-desync-ttl=8 --dpi-desync-fooling=md5sig --daemon $1 |
|
||||
@ -1,34 +0,0 @@ |
|||||
#!/system/bin/busybox sh |
|
||||
|
|
||||
# $1 - ip list file. create individual rules for tpws redirection. ipset is not available |
|
||||
|
|
||||
[ -z "$1" ] && { |
|
||||
echo need iplist file as parameter |
|
||||
exit 1 |
|
||||
} |
|
||||
|
|
||||
insmod /online/modules/unfuck_nfqueue.ko 2>/dev/null |
|
||||
|
|
||||
tpws --maxconn=1024 --uid 1:3003 --port=1 --daemon |
|
||||
|
|
||||
|
|
||||
REDIR="-j REDIRECT --to-port 1" |
|
||||
|
|
||||
iptables -F tpws -t nat |
|
||||
iptables -X tpws -t nat |
|
||||
iptables -N tpws -t nat |
|
||||
iptables -A tpws -t nat -d 192.168.0.0/16 -j RETURN |
|
||||
|
|
||||
while read ip; do |
|
||||
echo redirecting $ip |
|
||||
iptables -A tpws -t nat -d $ip -p tcp $REDIR |
|
||||
done <"$1" |
|
||||
|
|
||||
|
|
||||
rule="PREROUTING -t nat -i br0 -p tcp -m multiport --dports 80,443 -j tpws" |
|
||||
iptables -C $rule 2>/dev/null || iptables -I $rule |
|
||||
|
|
||||
nfqws --uid 2 --qnum=200 --dpi-desync=disorder --dpi-desync-ttl=8 --dpi-desync-fooling=md5sig --daemon |
|
||||
|
|
||||
rule="OUTPUT -t mangle -o wan0 -p tcp -m multiport --dports 80,443 -m mark ! --mark 0x40000000/0x40000000 -j NFQUEUE --queue-num 200 --queue-bypass" |
|
||||
iptables -C $rule 2>/dev/null || iptables -I $rule |
|
||||
@ -1,30 +0,0 @@ |
|||||
# this custom script runs desync to all IETF QUIC initials |
|
||||
# NOTE: @ih requires nft 1.0.1+ and updated kernel version. it's confirmed to work on 5.15 (openwrt 23) and not work on 5.10 (openwrt 22) |
|
||||
|
|
||||
# can override in config : |
|
||||
NFQWS_OPT_DESYNC_QUIC="${NFQWS_OPT_DESYNC_QUIC:---dpi-desync=fake --dpi-desync-repeats=2}" |
|
||||
|
|
||||
alloc_dnum DNUM_QUIC4ALL |
|
||||
alloc_qnum QNUM_QUIC4ALL |
|
||||
|
|
||||
zapret_custom_daemons() |
|
||||
{ |
|
||||
# $1 - 1 - add, 0 - stop |
|
||||
|
|
||||
local opt="--qnum=$QNUM_QUIC4ALL $NFQWS_OPT_DESYNC_QUIC" |
|
||||
do_nfqws $1 $DNUM_QUIC4ALL "$opt" |
|
||||
} |
|
||||
zapret_custom_firewall() |
|
||||
{ |
|
||||
# $1 - 1 - run, 0 - stop |
|
||||
|
|
||||
local f='-p udp -m u32 --u32' |
|
||||
fw_nfqws_post $1 "$f 0>>22&0x3C@4>>16=264:65535&&0>>22&0x3C@8>>28=0xC&&0>>22&0x3C@9=0x00000001" "$f 44>>16=264:65535&&48>>28=0xC&&49=0x00000001" $QNUM_QUIC4ALL |
|
||||
} |
|
||||
zapret_custom_firewall_nft() |
|
||||
{ |
|
||||
# stop logic is not required |
|
||||
|
|
||||
local f="udp length >= 264 @ih,0,4 0xC @ih,8,32 0x00000001" |
|
||||
nft_fw_nfqws_post "$f" "$f" $QNUM_QUIC4ALL |
|
||||
} |
|
||||
@ -1,29 +0,0 @@ |
|||||
# this script is an example describing how to run tpws on a custom port |
|
||||
|
|
||||
TPWS_OPT_EXTRA=${TPWS_OPT_EXTRA:---split-pos=2} |
|
||||
DPORTS_EXTRA=${DPORTS_EXTRA:-20443,20444,30000-30009} |
|
||||
|
|
||||
alloc_dnum DNUM_EXTRA_TPWS |
|
||||
alloc_tpws_port TPPORT_EXTRA_TPWS |
|
||||
|
|
||||
zapret_custom_daemons() |
|
||||
{ |
|
||||
# $1 - 1 - run, 0 - stop |
|
||||
local opt="--user=root --port=$TPPORT_EXTRA_TPWS" |
|
||||
tpws_apply_binds opt |
|
||||
opt="$opt $TPWS_OPT_EXTRA" |
|
||||
filter_apply_hostlist_target opt |
|
||||
do_daemon $1 $DNUM_EXTRA_TPWS "$TPWS" "$opt" |
|
||||
} |
|
||||
|
|
||||
# custom firewall functions echo rules for zapret-v4 and zapret-v6 anchors |
|
||||
# they come after automated table definitions. so you can use <zapret> <zapret6> <zapret-user> ... |
|
||||
|
|
||||
zapret_custom_firewall_v4() |
|
||||
{ |
|
||||
pf_anchor_zapret_v4_tpws $TPPORT_EXTRA_TPWS $(replace_char - : $DPORTS_EXTRA) |
|
||||
} |
|
||||
zapret_custom_firewall_v6() |
|
||||
{ |
|
||||
pf_anchor_zapret_v6_tpws $TPPORT_EXTRA_TPWS $(replace_char - : $DPORTS_EXTRA) |
|
||||
} |
|
||||
@ -1,195 +0,0 @@ |
|||||
# init script functions library for macos |
|
||||
|
|
||||
ZAPRET_BASE=${ZAPRET_BASE:-/opt/zapret} |
|
||||
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"} |
|
||||
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"} |
|
||||
. "$ZAPRET_CONFIG" |
|
||||
. "$ZAPRET_BASE/common/base.sh" |
|
||||
. "$ZAPRET_BASE/common/pf.sh" |
|
||||
. "$ZAPRET_BASE/common/list.sh" |
|
||||
. "$ZAPRET_BASE/common/custom.sh" |
|
||||
CUSTOM_DIR="$ZAPRET_RW/init.d/macos" |
|
||||
|
|
||||
IPSET_DIR=$ZAPRET_BASE/ipset |
|
||||
. "$IPSET_DIR/def.sh" |
|
||||
|
|
||||
PIDDIR=/var/run |
|
||||
[ -n "$TPPORT" ] || TPPORT=988 |
|
||||
[ -n "$TPPORT_SOCKS" ] || TPPORT=987 |
|
||||
[ -n "$WS_USER" ] || WS_USER=daemon |
|
||||
TPWS_WAIT="--bind-wait-ifup=30 --bind-wait-ip=30" |
|
||||
TPWS_WAIT_SOCKS6="$TPWS_WAIT --bind-wait-ip-linklocal=30" |
|
||||
[ -n "$TPWS" ] || TPWS="$ZAPRET_BASE/tpws/tpws" |
|
||||
|
|
||||
CUSTOM_SCRIPT="$ZAPRET_BASE/init.d/macos/custom" |
|
||||
[ -f "$CUSTOM_SCRIPT" ] && . "$CUSTOM_SCRIPT" |
|
||||
|
|
||||
run_daemon() |
|
||||
{ |
|
||||
# $1 - daemon number : 1,2,3,... |
|
||||
# $2 - daemon |
|
||||
# $3 - daemon args |
|
||||
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile |
|
||||
local DAEMONBASE="$(basename "$2")" |
|
||||
local PIDFILE="$PIDDIR/$DAEMONBASE$1.pid" |
|
||||
local ARGS="--daemon --pidfile=$PIDFILE $3" |
|
||||
[ -f "$PIDFILE" ] && pgrep -qF "$PIDFILE" && { |
|
||||
echo Already running $1: $2 |
|
||||
return 0 |
|
||||
} |
|
||||
echo "Starting daemon $1: $2 $ARGS" |
|
||||
"$2" $ARGS |
|
||||
} |
|
||||
stop_daemon() |
|
||||
{ |
|
||||
# $1 - daemon number : 1,2,3,... |
|
||||
# $2 - daemon |
|
||||
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile |
|
||||
|
|
||||
local PID |
|
||||
local DAEMONBASE="$(basename "$2")" |
|
||||
local PIDFILE="$PIDDIR/$DAEMONBASE$1.pid" |
|
||||
[ -f "$PIDFILE" ] && read PID <"$PIDFILE" |
|
||||
[ -n "$PID" ] && { |
|
||||
echo "Stopping daemon $1: $2 (PID=$PID)" |
|
||||
kill $PID |
|
||||
rm -f "$PIDFILE" |
|
||||
} |
|
||||
return 0 |
|
||||
} |
|
||||
do_daemon() |
|
||||
{ |
|
||||
# $1 - 1 - run, 0 - stop |
|
||||
on_off_function run_daemon stop_daemon "$@" |
|
||||
} |
|
||||
|
|
||||
tpws_apply_binds() |
|
||||
{ |
|
||||
local o |
|
||||
[ "$DISABLE_IPV4" = "1" ] || o="--bind-addr=127.0.0.1" |
|
||||
[ "$DISABLE_IPV6" = "1" ] || { |
|
||||
for i in lo0 $IFACE_LAN; do |
|
||||
o="$o --bind-iface6=$i --bind-linklocal=force $TPWS_WAIT" |
|
||||
done |
|
||||
} |
|
||||
eval $1="\"\$$1 $o\"" |
|
||||
} |
|
||||
tpws_apply_socks_binds() |
|
||||
{ |
|
||||
local o |
|
||||
|
|
||||
[ "$DISABLE_IPV4" = "1" ] || o="--bind-addr=127.0.0.1" |
|
||||
[ "$DISABLE_IPV6" = "1" ] || o="$o --bind-addr=::1" |
|
||||
|
|
||||
for lan in $IFACE_LAN; do |
|
||||
[ "$DISABLE_IPV4" = "1" ] || o="$o --bind-iface4=$lan $TPWS_WAIT" |
|
||||
[ "$DISABLE_IPV6" = "1" ] || o="$o --bind-iface6=$lan --bind-linklocal=unwanted $TPWS_WAIT_SOCKS6" |
|
||||
done |
|
||||
eval $1="\"\$$1 $o\"" |
|
||||
} |
|
||||
|
|
||||
wait_interface_ll() |
|
||||
{ |
|
||||
echo waiting for an ipv6 link local address on $1 ... |
|
||||
"$TPWS" --bind-wait-only --bind-iface6=$1 --bind-linklocal=force $TPWS_WAIT |
|
||||
} |
|
||||
wait_lan_ll() |
|
||||
{ |
|
||||
[ "$DISABLE_IPV6" != "1" ] && { |
|
||||
for lan in $IFACE_LAN; do |
|
||||
wait_interface_ll $lan >&2 || { |
|
||||
echo "wait interface failed on $lan" |
|
||||
return 1 |
|
||||
} |
|
||||
done |
|
||||
} |
|
||||
return 0 |
|
||||
} |
|
||||
get_ipv6_linklocal() |
|
||||
{ |
|
||||
ifconfig $1 | sed -nEe 's/^.*inet6 (fe80:[a-f0-9:]+).*/\1/p' |
|
||||
} |
|
||||
|
|
||||
|
|
||||
zapret_do_firewall() |
|
||||
{ |
|
||||
# $1 - 1 - add, 0 - del |
|
||||
|
|
||||
[ "$1" = 1 -a -n "$INIT_FW_PRE_UP_HOOK" ] && $INIT_FW_PRE_UP_HOOK |
|
||||
[ "$1" = 0 -a -n "$INIT_FW_PRE_DOWN_HOOK" ] && $INIT_FW_PRE_DOWN_HOOK |
|
||||
|
|
||||
if [ "$1" = "1" ] ; then |
|
||||
pf_anchor_root || return 1 |
|
||||
pf_anchors_create |
|
||||
pf_anchors_load || return 1 |
|
||||
pf_enable |
|
||||
else |
|
||||
pf_anchors_clear |
|
||||
fi |
|
||||
|
|
||||
[ "$1" = 1 -a -n "$INIT_FW_POST_UP_HOOK" ] && $INIT_FW_POST_UP_HOOK |
|
||||
[ "$1" = 0 -a -n "$INIT_FW_POST_DOWN_HOOK" ] && $INIT_FW_POST_DOWN_HOOK |
|
||||
|
|
||||
return 0 |
|
||||
} |
|
||||
zapret_apply_firewall() |
|
||||
{ |
|
||||
zapret_do_firewall 1 "$@" |
|
||||
} |
|
||||
zapret_unapply_firewall() |
|
||||
{ |
|
||||
zapret_do_firewall 0 "$@" |
|
||||
} |
|
||||
zapret_restart_firewall() |
|
||||
{ |
|
||||
zapret_unapply_firewall "$@" |
|
||||
zapret_apply_firewall "$@" |
|
||||
} |
|
||||
|
|
||||
|
|
||||
standard_mode_daemons() |
|
||||
{ |
|
||||
local opt |
|
||||
|
|
||||
if [ "$1" = "1" ] && [ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] ; then |
|
||||
echo "both ipv4 and ipv6 are disabled. nothing to do" |
|
||||
else |
|
||||
[ "$TPWS_ENABLE" = 1 ] && check_bad_ws_options $1 "$TPWS_OPT" && { |
|
||||
opt="--user=root --port=$TPPORT" |
|
||||
tpws_apply_binds opt |
|
||||
opt="$opt $TPWS_OPT" |
|
||||
filter_apply_hostlist_target opt |
|
||||
do_daemon $1 1 "$TPWS" "$opt" |
|
||||
} |
|
||||
[ "$TPWS_SOCKS_ENABLE" = 1 ] && { |
|
||||
opt="--socks --user=$WS_USER --port=$TPPORT_SOCKS" |
|
||||
tpws_apply_socks_binds opt |
|
||||
opt="$opt $TPWS_SOCKS_OPT" |
|
||||
filter_apply_hostlist_target opt |
|
||||
do_daemon $1 2 "$TPWS" "$opt" |
|
||||
} |
|
||||
fi |
|
||||
} |
|
||||
|
|
||||
zapret_do_daemons() |
|
||||
{ |
|
||||
# $1 - 1 - run, 0 - stop |
|
||||
|
|
||||
standard_mode_daemons $1 |
|
||||
custom_runner zapret_custom_daemons $1 |
|
||||
|
|
||||
return 0 |
|
||||
} |
|
||||
zapret_run_daemons() |
|
||||
{ |
|
||||
zapret_do_daemons 1 "$@" |
|
||||
} |
|
||||
zapret_stop_daemons() |
|
||||
{ |
|
||||
zapret_do_daemons 0 "$@" |
|
||||
} |
|
||||
zapret_restart_daemons() |
|
||||
{ |
|
||||
zapret_stop_daemons "$@" |
|
||||
zapret_run_daemons "$@" |
|
||||
} |
|
||||
@ -1,51 +0,0 @@ |
|||||
#!/bin/sh |
|
||||
|
|
||||
EXEDIR="$(dirname "$0")" |
|
||||
ZAPRET_BASE="$EXEDIR/../.." |
|
||||
ZAPRET_BASE="$(cd "$ZAPRET_BASE"; pwd)" |
|
||||
|
|
||||
. "$EXEDIR/functions" |
|
||||
|
|
||||
case "$1" in |
|
||||
start) |
|
||||
zapret_run_daemons |
|
||||
[ "$INIT_APPLY_FW" != "1" ] || zapret_apply_firewall |
|
||||
;; |
|
||||
stop) |
|
||||
[ "$INIT_APPLY_FW" != "1" ] || zapret_unapply_firewall |
|
||||
zapret_stop_daemons |
|
||||
;; |
|
||||
restart) |
|
||||
"$0" stop |
|
||||
"$0" start |
|
||||
;; |
|
||||
|
|
||||
start-fw|start_fw) |
|
||||
zapret_apply_firewall |
|
||||
;; |
|
||||
stop-fw|stop_fw) |
|
||||
zapret_unapply_firewall |
|
||||
;; |
|
||||
restart-fw|stop_fw) |
|
||||
zapret_restart_firewall |
|
||||
;; |
|
||||
reload-fw-tables|reload_fw_tables) |
|
||||
pf_table_reload |
|
||||
;; |
|
||||
|
|
||||
start-daemons|start_daemons) |
|
||||
zapret_run_daemons |
|
||||
;; |
|
||||
stop-daemons|stop_daemons) |
|
||||
zapret_stop_daemons |
|
||||
;; |
|
||||
restart-daemons|restart_daemons) |
|
||||
zapret_restart_daemons |
|
||||
;; |
|
||||
|
|
||||
*) |
|
||||
N="$SCRIPT/$NAME" |
|
||||
echo "Usage: $N {start|stop|start-fw|stop-fw|restart-fw|reload-fw-tables|start-daemons|stop-daemons|restart-daemons}" >&2 |
|
||||
exit 1 |
|
||||
;; |
|
||||
esac |
|
||||
@ -1,17 +0,0 @@ |
|||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
|
||||
<plist version="1.0"> |
|
||||
<dict> |
|
||||
<key>Label</key> |
|
||||
<string>zapret</string> |
|
||||
<key>LaunchOnlyOnce</key> |
|
||||
<false/> |
|
||||
<key>ProgramArguments</key> |
|
||||
<array> |
|
||||
<string>/opt/zapret/init.d/macos/zapret</string> |
|
||||
<string>start</string> |
|
||||
</array> |
|
||||
<key>RunAtLoad</key> |
|
||||
<true/> |
|
||||
</dict> |
|
||||
</plist> |
|
||||
@ -1,69 +0,0 @@ |
|||||
#!/sbin/openrc-run |
|
||||
|
|
||||
# zapret openrc to sysv adapter |
|
||||
# on some systems (alpine) for unknown reason non-openrc-run scripts are not started from /etc/init.d |
|
||||
|
|
||||
EXEDIR=$(dirname "$RC_SERVICE") |
|
||||
EXEDIR="$(cd "$EXEDIR"; pwd)" |
|
||||
ZAPRET_BASE="$EXEDIR/../.." |
|
||||
ZAPRET_INIT="$ZAPRET_BASE/init.d/sysv/zapret" |
|
||||
|
|
||||
extra_commands="start_fw stop_fw restart_fw start_daemons stop_daemons restart_daemons reload_ifsets list_ifsets list_table" |
|
||||
description="extra commands :" |
|
||||
description_stop_fw="Stop zapret firewall" |
|
||||
description_start_fw="Start zapret firewall" |
|
||||
description_restart_fw="Restart zapret firewall" |
|
||||
description_reload_ifsets="Reload interface lists (nftables only)" |
|
||||
description_list_ifsets="Display interface lists (nftables only)" |
|
||||
description_list_table="Display zapret nftable (nftables only)" |
|
||||
description_stop_daemons="Stop zapret daemons only" |
|
||||
description_start_daemons="Start zapret daemons only" |
|
||||
description_restart_daemons="Restart zapret firewall only" |
|
||||
|
|
||||
depend() { |
|
||||
rc-service -e networking && need networking |
|
||||
} |
|
||||
start() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" start |
|
||||
} |
|
||||
stop() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" stop |
|
||||
} |
|
||||
start_fw() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" start_fw |
|
||||
} |
|
||||
stop_fw() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" stop_fw |
|
||||
} |
|
||||
restart_fw() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" restart_fw |
|
||||
} |
|
||||
start_daemons() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" start_daemons |
|
||||
} |
|
||||
stop_daemons() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" stop_daemons |
|
||||
} |
|
||||
restart_daemons() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" restart_daemons |
|
||||
} |
|
||||
reload_ifsets() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" reload_ifsets |
|
||||
} |
|
||||
list_ifsets() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" list_ifsets |
|
||||
} |
|
||||
list_table() |
|
||||
{ |
|
||||
"$ZAPRET_INIT" list_table |
|
||||
} |
|
||||
@ -1,54 +0,0 @@ |
|||||
Minimal tpws startup script for low storage openwrt. |
|
||||
|
|
||||
--- openwrt with NFTABLES (22+) |
|
||||
|
|
||||
Make sure you are running openwrt with nftables, not iptables. |
|
||||
No opkg dependencies required ! |
|
||||
|
|
||||
* install : |
|
||||
|
|
||||
Copy everything from tpws directory to the root of the router. |
|
||||
Copy tpws binary for your architecture to /usr/bin/tpws |
|
||||
Set proper access rights : chmod 755 /etc/init.d/tpws /usr/bin/tpws |
|
||||
EDIT /etc/config/tpws |
|
||||
If you don't want ipv6 : edit /etc/nftables.d and comment lines with ipv6 redirect |
|
||||
/etc/init.d/tpws enable |
|
||||
/etc/init.d/tpws start |
|
||||
fw4 restart |
|
||||
|
|
||||
* full uninstall : |
|
||||
|
|
||||
/etc/init.d/tpws disable |
|
||||
/etc/init.d/tpws stop |
|
||||
rm -f /etc/nftables.d/90-tpws.nft /etc/firewall.user /etc/init.d/tpws |
|
||||
fw4 restart |
|
||||
|
|
||||
--- openwrt with IPTABLES (21-) |
|
||||
|
|
||||
Make sure you are running openwrt with iptables, not nftables. |
|
||||
Make sure you do not have anything valuable in /etc/firewall.user. |
|
||||
If you have - do not blindly follow instruction in firewall.user part. |
|
||||
Merge the code instead or setup your own firewall include in /etc/config/firewall. |
|
||||
|
|
||||
opkg update |
|
||||
opkg install iptables-mod-extra |
|
||||
IPV6 ONLY : opkg install ip6tables-mod-nat |
|
||||
|
|
||||
* install : |
|
||||
|
|
||||
Copy everything from tpws directory to the root of the router. |
|
||||
Copy tpws binary for your architecture to /usr/bin/tpws |
|
||||
Set proper access rights : chmod 755 /etc/init.d/tpws /usr/bin/tpws |
|
||||
EDIT /etc/config/tpws |
|
||||
If you don't want ipv6 : edit /etc/firewall.user and set DISABLE_IPV6=1 |
|
||||
/etc/init.d/tpws enable |
|
||||
/etc/init.d/tpws start |
|
||||
fw3 restart |
|
||||
|
|
||||
* full uninstall : |
|
||||
|
|
||||
/etc/init.d/tpws disable |
|
||||
/etc/init.d/tpws stop |
|
||||
rm -f /etc/nftables.d/90-tpws.nft /etc/firewall.user /etc/init.d/tpws |
|
||||
touch /etc/firewall.user |
|
||||
fw3 restart |
|
||||
@ -1,12 +0,0 @@ |
|||||
config global defaults |
|
||||
option user daemon |
|
||||
option tpws /usr/bin/tpws |
|
||||
|
|
||||
config tpws |
|
||||
option port 900 |
|
||||
option opt '--split-pos=2 --oob' |
|
||||
option enabled 1 |
|
||||
config tpws |
|
||||
option port 901 |
|
||||
option opt '--split-tls=sni --disorder' |
|
||||
option enabled 0 |
|
||||
@ -1,49 +0,0 @@ |
|||||
DISABLE_IPV6=0 |
|
||||
TP_PORT=900 |
|
||||
TP_USER=daemon |
|
||||
|
|
||||
EXCLUDE4="10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 169.254.0.0/16 127.0.0.0/8" |
|
||||
EXCLUDE6="fc00::/7 fe80::/10 ::1" |
|
||||
IPTS="iptables ip6tables" |
|
||||
[ "$DISABLE_IPV6" = 1 ] && IPTS=iptables |
|
||||
|
|
||||
exists() |
|
||||
{ |
|
||||
which "$1" >/dev/null 2>/dev/null |
|
||||
} |
|
||||
|
|
||||
ipt() |
|
||||
{ |
|
||||
$IPTABLES -C "$@" >/dev/null 2>/dev/null || $IPTABLES -I "$@" |
|
||||
} |
|
||||
|
|
||||
redirect_port() |
|
||||
{ |
|
||||
ipt tpws -t nat -p tcp --dport $1 -j REDIRECT --to-port $2 |
|
||||
} |
|
||||
|
|
||||
redirect() |
|
||||
{ |
|
||||
redirect_port 80 $TP_PORT |
|
||||
redirect_port 443 $TP_PORT |
|
||||
} |
|
||||
|
|
||||
for IPTABLES in $IPTS; do |
|
||||
$IPTABLES -t nat -N tpws 2>/dev/null |
|
||||
$IPTABLES -t nat -F tpws |
|
||||
redirect |
|
||||
done |
|
||||
|
|
||||
for net in $EXCLUDE4; do |
|
||||
iptables -t nat -I tpws -d $net -j RETURN |
|
||||
done |
|
||||
[ "$DISABLE_IPV6" = 1 ] || { |
|
||||
for net in $EXCLUDE6; do |
|
||||
ip6tables -t nat -I tpws -d $net -j RETURN |
|
||||
done |
|
||||
} |
|
||||
|
|
||||
for IPTABLES in $IPTS; do |
|
||||
ipt PREROUTING -t nat -j tpws |
|
||||
ipt OUTPUT -t nat -m owner ! --uid-owner $TP_USER -j tpws |
|
||||
done |
|
||||
@ -1,34 +0,0 @@ |
|||||
#!/bin/sh /etc/rc.common |
|
||||
|
|
||||
TPWS_DEFAULT=/usr/bin/tpws |
|
||||
TPWS_USER_DEFAULT=daemon |
|
||||
|
|
||||
START=99 |
|
||||
STOP=01 |
|
||||
USE_PROCD=1 |
|
||||
|
|
||||
tpws_instance() |
|
||||
{ |
|
||||
config_get "$@" |
|
||||
|
|
||||
local enabled port opt |
|
||||
|
|
||||
config_get_bool enabled "$1" enabled 0 |
|
||||
[ "$enabled" -eq 1 ] || return 1 |
|
||||
|
|
||||
config_get port "$1" port |
|
||||
config_get opt "$1" opt |
|
||||
|
|
||||
local COMMAND="$TPWS --user=$TPWS_USER --port=$port $opt" |
|
||||
procd_open_instance |
|
||||
procd_set_param command $COMMAND |
|
||||
procd_close_instance |
|
||||
} |
|
||||
|
|
||||
start_service() |
|
||||
{ |
|
||||
config_load tpws |
|
||||
config_get TPWS_USER defaults user $TPWS_USER_DEFAULT |
|
||||
config_get TPWS defaults tpws $TPWS_DEFAULT |
|
||||
config_foreach tpws_instance tpws |
|
||||
} |
|
||||
@ -1,18 +0,0 @@ |
|||||
set tpws_exclude4 { |
|
||||
type ipv4_addr; flags interval; auto-merge; |
|
||||
elements = { 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16,127.0.0.0/8 } |
|
||||
} |
|
||||
set tpws_exclude6 { |
|
||||
type ipv6_addr; flags interval; auto-merge; |
|
||||
elements = { fc00::/7, fe80::/10, ::1 } |
|
||||
} |
|
||||
chain tpws_pre { |
|
||||
type nat hook prerouting priority dstnat; policy accept; |
|
||||
tcp dport {80,443} ip daddr != @tpws_exclude4 redirect to :900 |
|
||||
tcp dport {80,443} ip6 daddr != @tpws_exclude6 redirect to :900 |
|
||||
} |
|
||||
chain tpws_out { |
|
||||
type nat hook output priority -100; policy accept; |
|
||||
tcp dport {80,443} skuid != daemon ip daddr != @tpws_exclude4 redirect to :900 |
|
||||
tcp dport {80,443} skuid != daemon ip6 daddr != @tpws_exclude6 redirect to :900 |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
#!/bin/sh |
|
||||
|
|
||||
# this file should be placed to /usr/local/etc/rc.d and chmod 755 |
|
||||
|
|
||||
# prepare system |
|
||||
|
|
||||
kldload ipfw |
|
||||
kldload ipdivert |
|
||||
|
|
||||
# for older pfsense versions. newer do not have these sysctls |
|
||||
sysctl net.inet.ip.pfil.outbound=ipfw,pf |
|
||||
sysctl net.inet.ip.pfil.inbound=ipfw,pf |
|
||||
sysctl net.inet6.ip6.pfil.outbound=ipfw,pf |
|
||||
sysctl net.inet6.ip6.pfil.inbound=ipfw,pf |
|
||||
|
|
||||
# required for newer pfsense versions (2.6.0 tested) to return ipfw to functional state |
|
||||
pfctl -d ; pfctl -e |
|
||||
|
|
||||
# add ipfw rules and start daemon |
|
||||
|
|
||||
ipfw delete 100 |
|
||||
ipfw add 100 divert 989 tcp from any to any 80,443 out not diverted not sockarg |
|
||||
pkill ^dvtws$ |
|
||||
dvtws --daemon --port 989 --dpi-desync=multisplit |
|
||||
@ -1,2 +0,0 @@ |
|||||
#!/bin/sh |
|
||||
/opt/zapret/init.d/sysv/zapret stop |
|
||||
@ -1,3 +0,0 @@ |
|||||
#!/bin/sh |
|
||||
/opt/zapret/init.d/sysv/zapret start |
|
||||
exec chpst -b zapret sleep infinity |
|
||||
@ -1,2 +0,0 @@ |
|||||
#!/bin/execlineb -P |
|
||||
exec /opt/zapret/init.d/sysv/zapret stop |
|
||||
@ -1 +0,0 @@ |
|||||
oneshot |
|
||||
@ -1,2 +0,0 @@ |
|||||
#!/bin/execlineb -P |
|
||||
exec /opt/zapret/init.d/sysv/zapret start |
|
||||
@ -1,62 +0,0 @@ |
|||||
# Example systemd service unit for nfqws. Adjust for your installation. |
|
||||
|
|
||||
# WARNING ! This unit requires to compile nfqws using `make systemd` |
|
||||
# WARNING ! This makefile target enables special systemd notify support. |
|
||||
|
|
||||
# PREPARE |
|
||||
# install build depends |
|
||||
# make -C /opt/zapret systemd |
|
||||
# cp nfqws@service /lib/systemd/system |
|
||||
# systemctl daemon-reload |
|
||||
|
|
||||
# MANAGE INSTANCE |
|
||||
# prepare /etc/zapret/nfqws1.conf with nfqws parameters |
|
||||
# systemctl start nfqws@nfqws1 |
|
||||
# systemctl status nfqws@nfqws1 |
|
||||
# systemctl restart nfqws@nfqws1 |
|
||||
# systemctl enable nfqws@nfqws1 |
|
||||
# systemctl disable nfqws@nfqws1 |
|
||||
# systemctl stop nfqws@nfqws1 |
|
||||
|
|
||||
# DELETE |
|
||||
# rm /lib/systemd/system/[email protected] |
|
||||
# systemctl daemon-reload |
|
||||
|
|
||||
|
|
||||
[Unit] |
|
||||
After=network.target |
|
||||
|
|
||||
[Service] |
|
||||
Type=notify |
|
||||
Restart=on-failure |
|
||||
|
|
||||
ExecSearchPath=/opt/zapret/binaries/my |
|
||||
ExecStart=nfqws @${CONFIG_DIR}/${INSTANCE}.conf |
|
||||
Environment=CONFIG_DIR=/etc/zapret |
|
||||
Environment=INSTANCE=%i |
|
||||
|
|
||||
RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET6 AF_INET |
|
||||
|
|
||||
LockPersonality=true |
|
||||
MemoryDenyWriteExecute=true |
|
||||
PrivateDevices=true |
|
||||
PrivateMounts=true |
|
||||
PrivateTmp=true |
|
||||
ProcSubset=pid |
|
||||
ProtectClock=true |
|
||||
ProtectControlGroups=true |
|
||||
ProtectHome=true |
|
||||
ProtectHostname=true |
|
||||
ProtectKernelLogs=true |
|
||||
ProtectKernelModules=true |
|
||||
ProtectKernelTunables=true |
|
||||
ProtectProc=invisible |
|
||||
ProtectSystem=full |
|
||||
RemoveIPC=true |
|
||||
RestrictNamespaces=true |
|
||||
RestrictRealtime=true |
|
||||
RestrictSUIDSGID=true |
|
||||
UMask=0077 |
|
||||
|
|
||||
[Install] |
|
||||
WantedBy=multi-user.target |
|
||||
@ -1,61 +0,0 @@ |
|||||
# Example systemd service unit for tpws. Adjust for your installation. |
|
||||
|
|
||||
# WARNING ! This unit requires to compile tpws using `make systemd` |
|
||||
# WARNING ! This makefile target enables special systemd notify support. |
|
||||
|
|
||||
# PREPARE |
|
||||
# install build depends |
|
||||
# make -C /opt/zapret systemd |
|
||||
# cp tpws@service /lib/systemd/system |
|
||||
# systemctl daemon-reload |
|
||||
|
|
||||
# MANAGE INSTANCE |
|
||||
# prepare /etc/zapret/tpws1.conf with tpws parameters |
|
||||
# systemctl start tpws@tpws1 |
|
||||
# systemctl status tpws@tpws1 |
|
||||
# systemctl restart tpws@tpws1 |
|
||||
# systemctl enable tpws@tpws1 |
|
||||
# systemctl disable tpws@tpws1 |
|
||||
# systemctl stop tpws@tpws1 |
|
||||
|
|
||||
# DELETE |
|
||||
# rm /lib/systemd/system/[email protected] |
|
||||
# systemctl daemon-reload |
|
||||
|
|
||||
|
|
||||
[Unit] |
|
||||
After=network.target |
|
||||
|
|
||||
[Service] |
|
||||
Type=notify |
|
||||
Restart=on-failure |
|
||||
|
|
||||
ExecSearchPath=/opt/zapret/binaries/my |
|
||||
ExecStart=tpws @${CONFIG_DIR}/${INSTANCE}.conf |
|
||||
Environment=CONFIG_DIR=/etc/zapret |
|
||||
Environment=INSTANCE=%i |
|
||||
|
|
||||
RestrictAddressFamilies=AF_NETLINK AF_UNIX AF_INET6 AF_INET |
|
||||
|
|
||||
LockPersonality=true |
|
||||
MemoryDenyWriteExecute=true |
|
||||
PrivateDevices=true |
|
||||
PrivateMounts=true |
|
||||
PrivateTmp=true |
|
||||
ProcSubset=pid |
|
||||
ProtectClock=true |
|
||||
ProtectControlGroups=true |
|
||||
ProtectHome=true |
|
||||
ProtectHostname=true |
|
||||
ProtectKernelLogs=true |
|
||||
ProtectKernelModules=true |
|
||||
ProtectProc=invisible |
|
||||
ProtectSystem=full |
|
||||
RemoveIPC=true |
|
||||
RestrictNamespaces=true |
|
||||
RestrictRealtime=true |
|
||||
RestrictSUIDSGID=true |
|
||||
UMask=0077 |
|
||||
|
|
||||
[Install] |
|
||||
WantedBy=multi-user.target |
|
||||
@ -1,13 +0,0 @@ |
|||||
[Unit] |
|
||||
Description=zapret ip/host list update |
|
||||
|
|
||||
[Service] |
|
||||
Restart=no |
|
||||
IgnoreSIGPIPE=no |
|
||||
KillMode=control-group |
|
||||
GuessMainPID=no |
|
||||
RemainAfterExit=no |
|
||||
ExecStart=/opt/zapret/ipset/get_config.sh |
|
||||
|
|
||||
[Install] |
|
||||
WantedBy=multi-user.target |
|
||||
@ -1,11 +0,0 @@ |
|||||
[Unit] |
|
||||
Description=zapret ip/host list update timer |
|
||||
|
|
||||
[Timer] |
|
||||
OnCalendar=*-*-2,4,6,8,10,12,14,16,18,20,22,24,26,28,30 00:00:00 |
|
||||
RandomizedDelaySec=86400 |
|
||||
Persistent=true |
|
||||
Unit=zapret-list-update.service |
|
||||
|
|
||||
[Install] |
|
||||
WantedBy=timers.target |
|
||||
@ -1,17 +0,0 @@ |
|||||
[Unit] |
|
||||
After=network-online.target |
|
||||
Wants=network-online.target |
|
||||
|
|
||||
[Service] |
|
||||
Type=forking |
|
||||
Restart=no |
|
||||
TimeoutSec=30sec |
|
||||
IgnoreSIGPIPE=no |
|
||||
KillMode=none |
|
||||
GuessMainPID=no |
|
||||
RemainAfterExit=no |
|
||||
ExecStart=/opt/zapret/init.d/sysv/zapret start |
|
||||
ExecStop=/opt/zapret/init.d/sysv/zapret stop |
|
||||
|
|
||||
[Install] |
|
||||
WantedBy=multi-user.target |
|
||||
@ -1,271 +0,0 @@ |
|||||
# init script functions library for desktop linux systems |
|
||||
|
|
||||
ZAPRET_BASE=${ZAPRET_BASE:-/opt/zapret} |
|
||||
ZAPRET_RW=${ZAPRET_RW:-"$ZAPRET_BASE"} |
|
||||
ZAPRET_CONFIG=${ZAPRET_CONFIG:-"$ZAPRET_RW/config"} |
|
||||
. "$ZAPRET_CONFIG" |
|
||||
. "$ZAPRET_BASE/common/base.sh" |
|
||||
. "$ZAPRET_BASE/common/fwtype.sh" |
|
||||
. "$ZAPRET_BASE/common/linux_iphelper.sh" |
|
||||
. "$ZAPRET_BASE/common/ipt.sh" |
|
||||
. "$ZAPRET_BASE/common/nft.sh" |
|
||||
. "$ZAPRET_BASE/common/linux_fw.sh" |
|
||||
. "$ZAPRET_BASE/common/linux_daemons.sh" |
|
||||
. "$ZAPRET_BASE/common/list.sh" |
|
||||
. "$ZAPRET_BASE/common/custom.sh" |
|
||||
CUSTOM_DIR="$ZAPRET_RW/init.d/sysv" |
|
||||
|
|
||||
|
|
||||
user_exists() |
|
||||
{ |
|
||||
id -u $1 >/dev/null 2>/dev/null |
|
||||
} |
|
||||
useradd_compat() |
|
||||
{ |
|
||||
# $1 - username |
|
||||
# skip for readonly systems |
|
||||
[ -w "/etc" ] && { |
|
||||
if exists useradd ; then |
|
||||
useradd --no-create-home --system --shell /bin/false $1 |
|
||||
elif is_linked_to_busybox adduser ; then |
|
||||
# some systems may miss nogroup group in /etc/group |
|
||||
# adduser fails if it's absent and no group is specified |
|
||||
addgroup nogroup 2>/dev/null |
|
||||
# busybox has special adduser syntax |
|
||||
adduser -S -H -D $1 |
|
||||
elif exists adduser; then |
|
||||
adduser --no-create-home --system --disabled-login $1 |
|
||||
fi |
|
||||
} |
|
||||
user_exists $1 |
|
||||
} |
|
||||
prepare_user() |
|
||||
{ |
|
||||
# $WS_USER is required to prevent redirection of the traffic originating from TPWS itself |
|
||||
# otherwise infinite loop will occur |
|
||||
# also its good idea not to run tpws as root |
|
||||
user_exists $WS_USER || { |
|
||||
# fallback to daemon if we cant add WS_USER |
|
||||
useradd_compat $WS_USER || { |
|
||||
for user in daemon nobody; do |
|
||||
user_exists $user && { |
|
||||
WS_USER=$user |
|
||||
return 0 |
|
||||
} |
|
||||
done |
|
||||
return 1 |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
# this complex user selection allows to survive in any locked/readonly/minimalistic environment |
|
||||
[ -n "$WS_USER" ] || WS_USER=tpws |
|
||||
if prepare_user; then |
|
||||
USEROPT="--user=$WS_USER" |
|
||||
else |
|
||||
WS_USER=1 |
|
||||
USEROPT="--uid $WS_USER:$WS_USER" |
|
||||
fi |
|
||||
|
|
||||
PIDDIR=/var/run |
|
||||
IPSET_CR="$ZAPRET_BASE/ipset/create_ipset.sh" |
|
||||
|
|
||||
[ -n "$DESYNC_MARK" ] || DESYNC_MARK=0x40000000 |
|
||||
[ -n "$DESYNC_MARK_POSTNAT" ] || DESYNC_MARK_POSTNAT=0x20000000 |
|
||||
|
|
||||
[ -n "$QNUM" ] || QNUM=200 |
|
||||
[ -n "$NFQWS" ] || NFQWS="$ZAPRET_BASE/nfq/nfqws" |
|
||||
NFQWS_OPT_BASE="$USEROPT --dpi-desync-fwmark=$DESYNC_MARK" |
|
||||
|
|
||||
[ -n "$TPPORT" ] || TPPORT=988 |
|
||||
[ -n "$TPPORT_SOCKS" ] || TPPORT_SOCKS=987 |
|
||||
[ -n "$TPWS" ] || TPWS="$ZAPRET_BASE/tpws/tpws" |
|
||||
TPWS_LOCALHOST4=127.0.0.127 |
|
||||
|
|
||||
TPWS_OPT_BASE="$USEROPT" |
|
||||
TPWS_OPT_BASE4="--bind-addr=$TPWS_LOCALHOST4" |
|
||||
TPWS_OPT_BASE6="--bind-addr=::1" |
|
||||
TPWS_WAIT="--bind-wait-ifup=30 --bind-wait-ip=30" |
|
||||
TPWS_WAIT_SOCKS6="$TPWS_WAIT --bind-wait-ip-linklocal=30" |
|
||||
# first wait for lan to ifup, then wait for bind-wait-ip-linklocal seconds for link local address and bind-wait-ip for any ipv6 as the worst case |
|
||||
TPWS_OPT_BASE6_PRE="--bind-linklocal=prefer $TPWS_WAIT --bind-wait-ip-linklocal=3" |
|
||||
|
|
||||
dnat6_target() |
|
||||
{ |
|
||||
_dnat6_target "$@" |
|
||||
} |
|
||||
set_route_localnet() |
|
||||
{ |
|
||||
_set_route_localnet $1 $IFACE_LAN |
|
||||
} |
|
||||
|
|
||||
fw_nfqws_post4() |
|
||||
{ |
|
||||
_fw_nfqws_post4 $1 "$2" $3 "$IFACE_WAN" |
|
||||
} |
|
||||
fw_nfqws_post6() |
|
||||
{ |
|
||||
_fw_nfqws_post6 $1 "$2" $3 "${IFACE_WAN6:-$IFACE_WAN}" |
|
||||
} |
|
||||
fw_nfqws_pre4() |
|
||||
{ |
|
||||
_fw_nfqws_pre4 $1 "$2" $3 "$IFACE_WAN" |
|
||||
} |
|
||||
fw_nfqws_pre6() |
|
||||
{ |
|
||||
_fw_nfqws_pre6 $1 "$2" $3 "${IFACE_WAN6:-$IFACE_WAN}" |
|
||||
} |
|
||||
fw_tpws4() |
|
||||
{ |
|
||||
_fw_tpws4 $1 "$2" $3 "$IFACE_LAN" "$IFACE_WAN" |
|
||||
} |
|
||||
fw_tpws6() |
|
||||
{ |
|
||||
_fw_tpws6 $1 "$2" $3 "$IFACE_LAN" "${IFACE_WAN6:-$IFACE_WAN}" |
|
||||
} |
|
||||
nft_fw_tpws4() |
|
||||
{ |
|
||||
_nft_fw_tpws4 "$1" $2 "$IFACE_WAN" |
|
||||
} |
|
||||
nft_fw_tpws6() |
|
||||
{ |
|
||||
_nft_fw_tpws6 "$1" $2 "$IFACE_LAN" "${IFACE_WAN6:-$IFACE_WAN}" |
|
||||
} |
|
||||
nft_fw_nfqws_post4() |
|
||||
{ |
|
||||
_nft_fw_nfqws_post4 "$1" $2 "$IFACE_WAN" |
|
||||
} |
|
||||
nft_fw_nfqws_post6() |
|
||||
{ |
|
||||
_nft_fw_nfqws_post6 "$1" $2 "${IFACE_WAN6:-$IFACE_WAN}" |
|
||||
} |
|
||||
nft_fw_nfqws_pre4() |
|
||||
{ |
|
||||
_nft_fw_nfqws_pre4 "$1" $2 "$IFACE_WAN" |
|
||||
} |
|
||||
nft_fw_nfqws_pre6() |
|
||||
{ |
|
||||
_nft_fw_nfqws_pre6 "$1" $2 "${IFACE_WAN6:-$IFACE_WAN}" |
|
||||
} |
|
||||
nft_fill_ifsets_overload() |
|
||||
{ |
|
||||
nft_fill_ifsets "$IFACE_LAN" "$IFACE_WAN" "${IFACE_WAN6:-$IFACE_WAN}" |
|
||||
} |
|
||||
|
|
||||
|
|
||||
run_daemon() |
|
||||
{ |
|
||||
# $1 - daemon number : 1,2,3,... |
|
||||
# $2 - daemon |
|
||||
# $3 - daemon args |
|
||||
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile |
|
||||
|
|
||||
local DAEMONBASE="$(basename "$2")" |
|
||||
local PID= PIDFILE=$PIDDIR/$DAEMONBASE$1.pid |
|
||||
echo "Starting daemon $1: $2 $3" |
|
||||
|
|
||||
[ -f "$PIDFILE" ] && { |
|
||||
read PID <"$PIDFILE" |
|
||||
[ -d "/proc/$PID" ] || PID= |
|
||||
} |
|
||||
|
|
||||
if [ -n "$PID" ]; then |
|
||||
echo already running |
|
||||
else |
|
||||
"$2" $3 >/dev/null & |
|
||||
PID=$! |
|
||||
if [ -n "$PID" ]; then |
|
||||
echo $PID >$PIDFILE |
|
||||
else |
|
||||
echo could not start daemon $1 : $2 $3 |
|
||||
false |
|
||||
fi |
|
||||
fi |
|
||||
} |
|
||||
stop_daemon() |
|
||||
{ |
|
||||
# $1 - daemon number : 1,2,3,... |
|
||||
# $2 - daemon |
|
||||
# use $PIDDIR/$DAEMONBASE$1.pid as pidfile |
|
||||
local DAEMONBASE="$(basename "$2")" |
|
||||
local PID PIDFILE=$PIDDIR/$DAEMONBASE$1.pid |
|
||||
echo "Stopping daemon $1: $2" |
|
||||
if [ -f "$PIDFILE" ]; then |
|
||||
read PID <"$PIDFILE" |
|
||||
kill $PID |
|
||||
rm -f "$PIDFILE" |
|
||||
else |
|
||||
echo no pidfile : $PIDFILE |
|
||||
fi |
|
||||
} |
|
||||
do_daemon() |
|
||||
{ |
|
||||
# $1 - 1 - run, 0 - stop |
|
||||
on_off_function run_daemon stop_daemon "$@" |
|
||||
} |
|
||||
|
|
||||
|
|
||||
do_tpws() |
|
||||
{ |
|
||||
# $1 : 1 - run, 0 - stop |
|
||||
# $2 : daemon number |
|
||||
# $3 : daemon args |
|
||||
|
|
||||
[ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] && return 0 |
|
||||
|
|
||||
local OPT="$TPWS_OPT_BASE" |
|
||||
|
|
||||
[ "$DISABLE_IPV4" = "1" ] || OPT="$OPT $TPWS_OPT_BASE4" |
|
||||
[ "$DISABLE_IPV6" = "1" ] || { |
|
||||
OPT="$OPT $TPWS_OPT_BASE6" |
|
||||
for lan in $IFACE_LAN; do |
|
||||
OPT="$OPT --bind-iface6=$lan $TPWS_OPT_BASE6_PRE" |
|
||||
done |
|
||||
} |
|
||||
|
|
||||
do_daemon $1 $2 "$TPWS" "$OPT $3" |
|
||||
} |
|
||||
do_tpws_socks() |
|
||||
{ |
|
||||
# $1 : 1 - run, 0 - stop |
|
||||
# $2 : daemon number |
|
||||
# $3 : daemon args |
|
||||
|
|
||||
[ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] && return 0 |
|
||||
|
|
||||
local opt="$TPWS_OPT_BASE --socks" |
|
||||
|
|
||||
tpws_apply_socks_binds opt |
|
||||
|
|
||||
do_daemon $1 $2 "$TPWS" "$opt $3" |
|
||||
} |
|
||||
|
|
||||
do_nfqws() |
|
||||
{ |
|
||||
# $1 : 1 - run, 0 - stop |
|
||||
# $2 : daemon number |
|
||||
# $3 : daemon args |
|
||||
|
|
||||
do_daemon $1 $2 "$NFQWS" "$NFQWS_OPT_BASE $3" |
|
||||
} |
|
||||
|
|
||||
tpws_apply_socks_binds() |
|
||||
{ |
|
||||
local o |
|
||||
|
|
||||
[ "$DISABLE_IPV4" = "1" ] || o="--bind-addr=127.0.0.1" |
|
||||
[ "$DISABLE_IPV6" = "1" ] || o="$o --bind-addr=::1" |
|
||||
|
|
||||
for lan in $IFACE_LAN; do |
|
||||
[ "$DISABLE_IPV4" = "1" ] || o="$o --bind-iface4=$lan $TPWS_WAIT" |
|
||||
[ "$DISABLE_IPV6" = "1" ] || o="$o --bind-iface6=$lan --bind-linklocal=unwanted $TPWS_WAIT_SOCKS6" |
|
||||
done |
|
||||
eval $1="\"\$$1 $o\"" |
|
||||
} |
|
||||
|
|
||||
|
|
||||
create_ipset() |
|
||||
{ |
|
||||
echo "Creating ip list table (firewall type $FWTYPE)" |
|
||||
"$IPSET_CR" "$@" |
|
||||
} |
|
||||
@ -1,82 +0,0 @@ |
|||||
#!/bin/sh |
|
||||
### BEGIN INIT INFO |
|
||||
# Provides: zapret |
|
||||
# Required-Start: $local_fs $network |
|
||||
# Required-Stop: $local_fs $network |
|
||||
# Default-Start: 2 3 4 5 |
|
||||
# Default-Stop: 0 1 6 |
|
||||
### END INIT INFO |
|
||||
|
|
||||
SCRIPT=$(readlink -f "$0") |
|
||||
EXEDIR=$(dirname "$SCRIPT") |
|
||||
ZAPRET_BASE=$(readlink -f "$EXEDIR/../..") |
|
||||
. "$EXEDIR/functions" |
|
||||
|
|
||||
NAME=zapret |
|
||||
DESC=anti-zapret |
|
||||
|
|
||||
do_start() |
|
||||
{ |
|
||||
zapret_run_daemons |
|
||||
[ "$INIT_APPLY_FW" != "1" ] || { zapret_apply_firewall; } |
|
||||
} |
|
||||
do_stop() |
|
||||
{ |
|
||||
zapret_stop_daemons |
|
||||
[ "$INIT_APPLY_FW" != "1" ] || zapret_unapply_firewall |
|
||||
} |
|
||||
|
|
||||
case "$1" in |
|
||||
start) |
|
||||
do_start |
|
||||
;; |
|
||||
|
|
||||
stop) |
|
||||
do_stop |
|
||||
;; |
|
||||
|
|
||||
restart) |
|
||||
do_stop |
|
||||
do_start |
|
||||
;; |
|
||||
|
|
||||
start-fw|start_fw) |
|
||||
zapret_apply_firewall |
|
||||
;; |
|
||||
stop-fw|stop_fw) |
|
||||
zapret_unapply_firewall |
|
||||
;; |
|
||||
|
|
||||
restart-fw|restart_fw) |
|
||||
zapret_unapply_firewall |
|
||||
zapret_apply_firewall |
|
||||
;; |
|
||||
|
|
||||
start-daemons|start_daemons) |
|
||||
zapret_run_daemons |
|
||||
;; |
|
||||
stop-daemons|stop_daemons) |
|
||||
zapret_stop_daemons |
|
||||
;; |
|
||||
restart-daemons|restart_daemons) |
|
||||
zapret_stop_daemons |
|
||||
zapret_run_daemons |
|
||||
;; |
|
||||
|
|
||||
reload-ifsets|reload_ifsets) |
|
||||
zapret_reload_ifsets |
|
||||
;; |
|
||||
list-ifsets|list_ifsets) |
|
||||
zapret_list_ifsets |
|
||||
;; |
|
||||
list-table|list_table) |
|
||||
zapret_list_table |
|
||||
;; |
|
||||
|
|
||||
*) |
|
||||
echo "Usage: $SCRIPT {start|stop|restart|start-fw|stop-fw|restart-fw|start-daemons|stop-daemons|restart-daemons|reload-ifsets|list-ifsets|list-table}" >&2 |
|
||||
exit 1 |
|
||||
;; |
|
||||
esac |
|
||||
|
|
||||
exit 0 |
|
||||
@ -1,14 +0,0 @@ |
|||||
Цель этих фильтров - отсекать полезную нагрузку в режиме ядра, не насилуя процессор перенаправлением целого потока на winws. |
|
||||
Задействуются через `winws --wf-raw-part=@filename`. Может быть несколько частичных фильтров. Они могут сочетаться с --wf-tcp и --wf-udp. |
|
||||
Однако, язык фильтров windivert не содержит операций с битовыми полями, сдвигов и побитовой логики. |
|
||||
Поэтому фильтры получились более слабыми, способными передавать неправильную нагрузку. |
|
||||
Дофильтрация производится силами winws. |
|
||||
|
|
||||
Описание языка фильтров : https://reqrypt.org/windivert-doc.html#filter_language |
|
||||
Пример инстанса для пробития медиапотоков в discord : `winws --wf-raw-part=@windivert_part.discord_media.txt --wf-raw-part=@windivert_part.stun.txt --filter-l7=stun,discord --dpi-desync=fake` |
|
||||
|
|
||||
|
|
||||
These filters are invoked using `winws --wf-raw-part=@filename`. Multiple filter parts are supported. They can be combined with --wf-tcp and --wf-udp. |
|
||||
Filters are kernel mode and save great amount of CPU. |
|
||||
However windivert cannot filter by bit fields, lacks shift and bitwise logic operations. |
|
||||
Filters are relaxed and can pass wrong payloads. Finer filtering is done by winws. |
|
||||
@ -1,20 +0,0 @@ |
|||||
outbound and ip and |
|
||||
udp.DstPort>=50000 and udp.DstPort<=50099 and |
|
||||
udp.PayloadLength=74 and |
|
||||
udp.Payload32[0]=0x00010046 and |
|
||||
udp.Payload32[2]=0 and |
|
||||
udp.Payload32[3]=0 and |
|
||||
udp.Payload32[4]=0 and |
|
||||
udp.Payload32[5]=0 and |
|
||||
udp.Payload32[6]=0 and |
|
||||
udp.Payload32[7]=0 and |
|
||||
udp.Payload32[8]=0 and |
|
||||
udp.Payload32[9]=0 and |
|
||||
udp.Payload32[10]=0 and |
|
||||
udp.Payload32[11]=0 and |
|
||||
udp.Payload32[12]=0 and |
|
||||
udp.Payload32[13]=0 and |
|
||||
udp.Payload32[14]=0 and |
|
||||
udp.Payload32[15]=0 and |
|
||||
udp.Payload32[16]=0 and |
|
||||
udp.Payload32[17]=0 |
|
||||
@ -1,4 +0,0 @@ |
|||||
outbound and |
|
||||
udp.PayloadLength>=256 and |
|
||||
udp.Payload[0]>=0xC0 and udp.Payload[0]<0xD0 and |
|
||||
udp.Payload[1]=0 and udp.Payload16[1]=0 and udp.Payload[4]=1 |
|
||||
@ -1,3 +0,0 @@ |
|||||
outbound and |
|
||||
udp.PayloadLength>=20 and |
|
||||
udp.Payload32[1]=0x2112A442 and udp.Payload[0]<0x40 |
|
||||
@ -1,3 +0,0 @@ |
|||||
outbound and |
|
||||
udp.PayloadLength=148 and |
|
||||
udp.Payload[0]=0x01 |
|
||||
@ -1,33 +0,0 @@ |
|||||
CC ?= cc |
|
||||
OPTIMIZE ?= -Os |
|
||||
CFLAGS += -std=gnu99 $(OPTIMIZE) -flto=auto |
|
||||
CFLAGS_BSD = -Wno-address-of-packed-member |
|
||||
CFLAGS_WIN = -static |
|
||||
LIBS = |
|
||||
LIBS_WIN = -lws2_32 |
|
||||
SRC_FILES = ip2net.c qsort.c |
|
||||
|
|
||||
all: ip2net |
|
||||
|
|
||||
ip2net: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS) |
|
||||
|
|
||||
systemd: ip2net |
|
||||
|
|
||||
android: ip2net |
|
||||
|
|
||||
bsd: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o ip2net $(SRC_FILES) $(LIBS) $(LDFLAGS) |
|
||||
|
|
||||
mac: $(SRC_FILES) |
|
||||
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2neta $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS) $(LDFLAGS) |
|
||||
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o ip2netx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS) $(LDFLAGS) |
|
||||
strip ip2neta ip2netx |
|
||||
lipo -create -output ip2net ip2netx ip2neta |
|
||||
rm -f ip2netx ip2neta |
|
||||
|
|
||||
win: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o ip2net $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS) |
|
||||
|
|
||||
clean: |
|
||||
rm -f ip2net *.o |
|
||||
@ -1,516 +0,0 @@ |
|||||
// group ipv4/ipv6 list from stdout into subnets
|
|
||||
// each line must contain either ip or ip/bitcount
|
|
||||
// valid ip/bitcount and ip1-ip2 are passed through without modification
|
|
||||
// ips are groupped into subnets
|
|
||||
|
|
||||
// can be compiled in mingw. msvc not supported because of absent getopt
|
|
||||
|
|
||||
#include <stdio.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <stdbool.h> |
|
||||
#include <stdint.h> |
|
||||
#include <string.h> |
|
||||
#ifdef _WIN32 |
|
||||
#undef _WIN32_WINNT |
|
||||
#define _WIN32_WINNT 0x600 |
|
||||
#include <winsock2.h> |
|
||||
#include <ws2ipdef.h> |
|
||||
#include <ws2tcpip.h> |
|
||||
#else |
|
||||
#include <arpa/inet.h> |
|
||||
#include <netinet/in.h> |
|
||||
#include <sys/socket.h> |
|
||||
#endif |
|
||||
#include <getopt.h> |
|
||||
#include "qsort.h" |
|
||||
|
|
||||
#define ALLOC_STEP 16384 |
|
||||
|
|
||||
// minimum subnet fill percent is PCTMULT/PCTDIV (for example 3/4)
|
|
||||
#define DEFAULT_PCTMULT 3 |
|
||||
#define DEFAULT_PCTDIV 4 |
|
||||
// subnet search range in "zero bit count"
|
|
||||
// means search start from /(32-ZCT_MAX) to /(32-ZCT_MIN)
|
|
||||
#define DEFAULT_V4_ZCT_MAX 10 // /22
|
|
||||
#define DEFAULT_V4_ZCT_MIN 2 // /30
|
|
||||
#define DEFAULT_V6_ZCT_MAX 72 // /56
|
|
||||
#define DEFAULT_V6_ZCT_MIN 64 // /64
|
|
||||
// must be no less than N ipv6 in subnet
|
|
||||
#define DEFAULT_V6_THRESHOLD 5 |
|
||||
|
|
||||
static int ucmp(const void * a, const void * b, void *arg) |
|
||||
{ |
|
||||
if (*(uint32_t*)a < *(uint32_t*)b) |
|
||||
return -1; |
|
||||
else if (*(uint32_t*)a > *(uint32_t*)b) |
|
||||
return 1; |
|
||||
else |
|
||||
return 0; |
|
||||
} |
|
||||
static uint32_t mask_from_bitcount(uint32_t zct) |
|
||||
{ |
|
||||
return zct<32 ? ~((1u << zct) - 1) : 0; |
|
||||
} |
|
||||
// make presorted array unique. return number of unique items.
|
|
||||
// 1,1,2,3,3,0,0,0 (ct=8) => 1,2,3,0 (ct=4)
|
|
||||
static uint32_t unique(uint32_t *pu, uint32_t ct) |
|
||||
{ |
|
||||
uint32_t i, j, u; |
|
||||
for (i = j = 0; j < ct; i++) |
|
||||
{ |
|
||||
u = pu[j++]; |
|
||||
for (; j < ct && pu[j] == u; j++); |
|
||||
pu[i] = u; |
|
||||
} |
|
||||
return i; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
#if defined(__GNUC__) && !defined(__llvm__) |
|
||||
__attribute__((optimize ("no-strict-aliasing"))) |
|
||||
#endif |
|
||||
static int cmp6(const void * a, const void * b, void *arg) |
|
||||
{ |
|
||||
// this function is critical for sort performance
|
|
||||
// on big endian systems cpu byte order is equal to network byte order
|
|
||||
// no conversion required. it's possible to improve speed by using big size compares
|
|
||||
// on little endian systems byte conversion also gives better result than byte comparision
|
|
||||
// 64-bit archs often have cpu command to reverse byte order
|
|
||||
// assume that a and b are properly aligned
|
|
||||
|
|
||||
#if defined(__BYTE_ORDER__) && ((__BYTE_ORDER__==__ORDER_BIG_ENDIAN__) || (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__)) |
|
||||
|
|
||||
uint64_t aa,bb; |
|
||||
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ |
|
||||
aa = __builtin_bswap64(((uint64_t*)((struct in6_addr *)a)->s6_addr)[0]); |
|
||||
bb = __builtin_bswap64(((uint64_t*)((struct in6_addr *)b)->s6_addr)[0]); |
|
||||
#else |
|
||||
aa = ((uint64_t*)((struct in6_addr *)a)->s6_addr)[0]; |
|
||||
bb = ((uint64_t*)((struct in6_addr *)b)->s6_addr)[0]; |
|
||||
#endif |
|
||||
if (aa < bb) |
|
||||
return -1; |
|
||||
else if (aa > bb) |
|
||||
return 1; |
|
||||
else |
|
||||
{ |
|
||||
#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ |
|
||||
aa = __builtin_bswap64(((uint64_t*)((struct in6_addr *)a)->s6_addr)[1]); |
|
||||
bb = __builtin_bswap64(((uint64_t*)((struct in6_addr *)b)->s6_addr)[1]); |
|
||||
#else |
|
||||
aa = ((uint64_t*)((struct in6_addr *)a)->s6_addr)[1]; |
|
||||
bb = ((uint64_t*)((struct in6_addr *)b)->s6_addr)[1]; |
|
||||
#endif |
|
||||
return aa < bb ? -1 : aa > bb ? 1 : 0; |
|
||||
} |
|
||||
|
|
||||
#else |
|
||||
// fallback case
|
|
||||
for (uint8_t i = 0; i < sizeof(((struct in6_addr *)0)->s6_addr); i++) |
|
||||
{ |
|
||||
if (((struct in6_addr *)a)->s6_addr[i] < ((struct in6_addr *)b)->s6_addr[i]) |
|
||||
return -1; |
|
||||
else if (((struct in6_addr *)a)->s6_addr[i] > ((struct in6_addr *)b)->s6_addr[i]) |
|
||||
return 1; |
|
||||
} |
|
||||
return 0; |
|
||||
#endif |
|
||||
} |
|
||||
|
|
||||
// make presorted array unique. return number of unique items.
|
|
||||
static uint32_t unique6(struct in6_addr *pu, uint32_t ct) |
|
||||
{ |
|
||||
uint32_t i, j, k; |
|
||||
for (i = j = 0; j < ct; i++) |
|
||||
{ |
|
||||
for (k = j++; j < ct && !memcmp(pu + j, pu + k, sizeof(struct in6_addr)); j++); |
|
||||
pu[i] = pu[k]; |
|
||||
} |
|
||||
return i; |
|
||||
} |
|
||||
static void mask_from_bitcount6_make(uint32_t zct, struct in6_addr *a) |
|
||||
{ |
|
||||
if (zct >= 128) |
|
||||
memset(a->s6_addr,0x00,16); |
|
||||
else |
|
||||
{ |
|
||||
int32_t n = (127 - zct) >> 3; |
|
||||
memset(a->s6_addr,0xFF,n); |
|
||||
memset(a->s6_addr+n,0x00,16-n); |
|
||||
a->s6_addr[n] = ~((1u << (zct & 7)) - 1); |
|
||||
} |
|
||||
} |
|
||||
static struct in6_addr ip6_mask[129]; |
|
||||
static void mask_from_bitcount6_prepare(void) |
|
||||
{ |
|
||||
for (int zct=0;zct<=128;zct++) mask_from_bitcount6_make(zct, ip6_mask+zct); |
|
||||
} |
|
||||
static inline const struct in6_addr *mask_from_bitcount6(uint32_t zct) |
|
||||
{ |
|
||||
return ip6_mask+zct; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/*
|
|
||||
// this is "correct" solution for strict aliasing feature
|
|
||||
// but I don't like this style of coding
|
|
||||
// write what I don't mean to force smart optimizer to do what it's best
|
|
||||
// it produces better code sometimes but not on all compilers/versions/archs
|
|
||||
// sometimes it even generates real memcpy calls (mips32,arm32)
|
|
||||
// so I will not do it
|
|
||||
|
|
||||
static void ip6_and(const struct in6_addr *a, const struct in6_addr *b, struct in6_addr *result) |
|
||||
{ |
|
||||
uint64_t a_addr[2], b_addr[2]; |
|
||||
memcpy(a_addr, a->s6_addr, 16); |
|
||||
memcpy(b_addr, b->s6_addr, 16); |
|
||||
a_addr[0] &= b_addr[0]; |
|
||||
a_addr[1] &= b_addr[1]; |
|
||||
memcpy(result->s6_addr, a_addr, 16); |
|
||||
} |
|
||||
*/ |
|
||||
|
|
||||
// YES, from my point of view C should work as a portable assembler. It must do what I instruct it to do.
|
|
||||
// that's why I disable strict aliasing for this function. I observed gcc can miscompile with O2/O3 setting if inlined and not coded "correct"
|
|
||||
// result = a & b
|
|
||||
// assume that a and b are properly aligned
|
|
||||
#if defined(__GNUC__) && !defined(__llvm__) |
|
||||
__attribute__((optimize ("no-strict-aliasing"))) |
|
||||
#endif |
|
||||
static void ip6_and(const struct in6_addr * restrict a, const struct in6_addr * restrict b, struct in6_addr * restrict result) |
|
||||
{ |
|
||||
#ifdef __SIZEOF_INT128__ |
|
||||
// gcc and clang have 128 bit int types on some 64-bit archs. take some advantage
|
|
||||
*((unsigned __int128*)result->s6_addr) = *((unsigned __int128*)a->s6_addr) & *((unsigned __int128*)b->s6_addr); |
|
||||
#else |
|
||||
((uint64_t*)result->s6_addr)[0] = ((uint64_t*)a->s6_addr)[0] & ((uint64_t*)b->s6_addr)[0]; |
|
||||
((uint64_t*)result->s6_addr)[1] = ((uint64_t*)a->s6_addr)[1] & ((uint64_t*)b->s6_addr)[1]; |
|
||||
#endif |
|
||||
} |
|
||||
|
|
||||
static void rtrim(char *s) |
|
||||
{ |
|
||||
if (s) |
|
||||
for (char *p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'); p--) *p = '\0'; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static struct params_s |
|
||||
{ |
|
||||
bool ipv6; |
|
||||
uint32_t pctmult, pctdiv; // for v4
|
|
||||
uint32_t zct_min, zct_max; // for v4 and v6
|
|
||||
uint32_t v6_threshold; // for v6
|
|
||||
} params; |
|
||||
|
|
||||
|
|
||||
static void exithelp(void) |
|
||||
{ |
|
||||
printf( |
|
||||
" -4\t\t\t\t; ipv4 list (default)\n" |
|
||||
" -6\t\t\t\t; ipv6 list\n" |
|
||||
" --prefix-length=min[-max]\t; consider prefix lengths from 'min' to 'max'. examples : 22-30 (ipv4), 56-64 (ipv6)\n" |
|
||||
" --v4-threshold=mul/div\t\t; ipv4 only : include subnets with more than mul/div ips. example : 3/4\n" |
|
||||
" --v6-threshold=N\t\t; ipv6 only : include subnets with more than N v6 ips. example : 5\n" |
|
||||
); |
|
||||
exit(1); |
|
||||
} |
|
||||
|
|
||||
#define STRINGIFY(x) #x |
|
||||
#define TOSTRING(x) STRINGIFY(x) |
|
||||
#if defined(ZAPRET_GH_VER) || defined (ZAPRET_GH_HASH) |
|
||||
#define PRINT_VER printf("github version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH)) |
|
||||
#else |
|
||||
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__) |
|
||||
#endif |
|
||||
|
|
||||
enum opt_indices { |
|
||||
IDX_HELP, |
|
||||
IDX_H, |
|
||||
IDX_4, |
|
||||
IDX_6, |
|
||||
IDX_PREFIX_LENGTH, |
|
||||
IDX_V4_THRESHOLD, |
|
||||
IDX_V6_THRESHOLD, |
|
||||
IDX_LAST, |
|
||||
}; |
|
||||
|
|
||||
static const struct option long_options[] = { |
|
||||
[IDX_HELP] = {"help", no_argument, 0, 0}, |
|
||||
[IDX_H] = {"h", no_argument, 0, 0}, |
|
||||
[IDX_4] = {"4", no_argument, 0, 0}, |
|
||||
[IDX_6] = {"6", no_argument, 0, 0}, |
|
||||
[IDX_PREFIX_LENGTH] = {"prefix-length", required_argument, 0, 0}, |
|
||||
[IDX_V4_THRESHOLD] = {"v4-threshold", required_argument, 0, 0}, |
|
||||
[IDX_V6_THRESHOLD] = {"v6-threshold", required_argument, 0, 0}, |
|
||||
[IDX_LAST] = {NULL, 0, NULL, 0}, |
|
||||
}; |
|
||||
|
|
||||
static void parse_params(int argc, char *argv[]) |
|
||||
{ |
|
||||
int option_index = 0; |
|
||||
int v, i; |
|
||||
uint32_t plen1=-1, plen2=-1; |
|
||||
|
|
||||
memset(¶ms, 0, sizeof(params)); |
|
||||
params.pctmult = DEFAULT_PCTMULT; |
|
||||
params.pctdiv = DEFAULT_PCTDIV; |
|
||||
params.v6_threshold = DEFAULT_V6_THRESHOLD; |
|
||||
|
|
||||
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) |
|
||||
{ |
|
||||
if (v) exithelp(); |
|
||||
switch (option_index) |
|
||||
{ |
|
||||
case IDX_HELP: |
|
||||
case IDX_H: |
|
||||
PRINT_VER; |
|
||||
exithelp(); |
|
||||
break; |
|
||||
case IDX_4: |
|
||||
params.ipv6 = false; |
|
||||
break; |
|
||||
case IDX_6: |
|
||||
params.ipv6 = true; |
|
||||
break; |
|
||||
case IDX_PREFIX_LENGTH: |
|
||||
i = sscanf(optarg,"%u-%u",&plen1,&plen2); |
|
||||
if (i == 1) plen2 = plen1; |
|
||||
if (i<=0 || plen2<plen1 || !plen1 || !plen2) |
|
||||
{ |
|
||||
fprintf(stderr, "invalid parameter for prefix-length : %s\n", optarg); |
|
||||
exit(1); |
|
||||
} |
|
||||
break; |
|
||||
case IDX_V4_THRESHOLD: |
|
||||
i = sscanf(optarg, "%u/%u", ¶ms.pctmult, ¶ms.pctdiv); |
|
||||
if (i!=2 || params.pctdiv<2 || params.pctmult<1 || params.pctmult>=params.pctdiv) |
|
||||
{ |
|
||||
fprintf(stderr, "invalid parameter for v4-threshold : %s\n", optarg); |
|
||||
exit(1); |
|
||||
} |
|
||||
break; |
|
||||
case IDX_V6_THRESHOLD: |
|
||||
i = sscanf(optarg, "%u", ¶ms.v6_threshold); |
|
||||
if (i != 1 || params.v6_threshold<1) |
|
||||
{ |
|
||||
fprintf(stderr, "invalid parameter for v6-threshold : %s\n", optarg); |
|
||||
exit(1); |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
if (plen1 != -1 && ((!params.ipv6 && (plen1>31 || plen2>31)) || (params.ipv6 && (plen1>127 || plen2>127)))) |
|
||||
{ |
|
||||
fprintf(stderr, "invalid parameter for prefix-length\n"); |
|
||||
exit(1); |
|
||||
} |
|
||||
params.zct_min = params.ipv6 ? plen2==-1 ? DEFAULT_V6_ZCT_MIN : 128-plen2 : plen2==-1 ? DEFAULT_V4_ZCT_MIN : 32-plen2; |
|
||||
params.zct_max = params.ipv6 ? plen1==-1 ? DEFAULT_V6_ZCT_MAX : 128-plen1 : plen1==-1 ? DEFAULT_V4_ZCT_MAX : 32-plen1; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
int main(int argc, char **argv) |
|
||||
{ |
|
||||
char str[256],d; |
|
||||
uint32_t ipct = 0, iplist_size = 0, pos = 0, p, zct, ip_ct, pos_end; |
|
||||
|
|
||||
parse_params(argc, argv); |
|
||||
|
|
||||
if (params.ipv6) // ipv6
|
|
||||
{ |
|
||||
char *s; |
|
||||
struct in6_addr a, *iplist = NULL, *iplist_new; |
|
||||
|
|
||||
while (fgets(str, sizeof(str), stdin)) |
|
||||
{ |
|
||||
rtrim(str); |
|
||||
d = 0; |
|
||||
if ((s = strchr(str, '/')) || (s = strchr(str, '-'))) |
|
||||
{ |
|
||||
d = *s; |
|
||||
*s = '\0'; |
|
||||
} |
|
||||
if (inet_pton(AF_INET6, str, &a)) |
|
||||
{ |
|
||||
if (d=='/') |
|
||||
{ |
|
||||
// we have subnet ip6/y
|
|
||||
// output it as is
|
|
||||
if (sscanf(s + 1, "%u", &zct)==1 && zct!=128) |
|
||||
{ |
|
||||
if (zct<128) printf("%s/%u\n", str, zct); |
|
||||
continue; |
|
||||
} |
|
||||
} |
|
||||
else if (d=='-') |
|
||||
{ |
|
||||
if (inet_pton(AF_INET6, s+1, &a)) printf("%s-%s\n", str, s+1); |
|
||||
continue; |
|
||||
} |
|
||||
if (ipct >= iplist_size) |
|
||||
{ |
|
||||
iplist_size += ALLOC_STEP; |
|
||||
iplist_new = (struct in6_addr*)(iplist ? realloc(iplist, sizeof(*iplist)*iplist_size) : malloc(sizeof(*iplist)*iplist_size)); |
|
||||
if (!iplist_new) |
|
||||
{ |
|
||||
free(iplist); |
|
||||
fprintf(stderr, "out of memory\n"); |
|
||||
return 100; |
|
||||
} |
|
||||
iplist = iplist_new; |
|
||||
} |
|
||||
iplist[ipct++] = a; |
|
||||
} |
|
||||
} |
|
||||
gnu_quicksort(iplist, ipct, sizeof(*iplist), cmp6, NULL); |
|
||||
ipct = unique6(iplist, ipct); |
|
||||
mask_from_bitcount6_prepare(); |
|
||||
|
|
||||
/*
|
|
||||
for(uint32_t i=0;i<ipct;i++) |
|
||||
if (inet_ntop(AF_INET6,iplist+i,str,sizeof(str))) |
|
||||
printf("%s\n",str); |
|
||||
printf("\n"); |
|
||||
*/ |
|
||||
while (pos < ipct) |
|
||||
{ |
|
||||
const struct in6_addr *mask; |
|
||||
struct in6_addr ip_start, ip; |
|
||||
uint32_t ip_ct_best = 0, zct_best = 0; |
|
||||
|
|
||||
pos_end = pos + 1; |
|
||||
// find smallest network with maximum ip coverage with no less than ip6_subnet_threshold addresses
|
|
||||
for (zct = params.zct_max; zct >= params.zct_min; zct--) |
|
||||
{ |
|
||||
mask = mask_from_bitcount6(zct); |
|
||||
ip6_and(iplist + pos, mask, &ip_start); |
|
||||
for (p = pos + 1, ip_ct = 1; p < ipct; p++, ip_ct++) |
|
||||
{ |
|
||||
ip6_and(iplist + p, mask, &ip); |
|
||||
if (memcmp(&ip_start, &ip, sizeof(ip))) |
|
||||
break; |
|
||||
} |
|
||||
if (ip_ct == 1) break; |
|
||||
if (ip_ct >= params.v6_threshold) |
|
||||
{ |
|
||||
// network found. but is there smaller network with the same ip_ct ? dont do carpet bombing if possible, use smaller subnets
|
|
||||
if (!ip_ct_best || ip_ct == ip_ct_best) |
|
||||
{ |
|
||||
ip_ct_best = ip_ct; |
|
||||
zct_best = zct; |
|
||||
pos_end = p; |
|
||||
} |
|
||||
else |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
if (zct_best) |
|
||||
// network was found
|
|
||||
ip6_and(iplist + pos, mask_from_bitcount6(zct_best), &ip_start); |
|
||||
else |
|
||||
ip_start = iplist[pos], pos_end = pos + 1; // network not found, use single ip
|
|
||||
inet_ntop(AF_INET6, &ip_start, str, sizeof(str)); |
|
||||
printf(zct_best ? "%s/%u\n" : "%s\n", str, 128 - zct_best); |
|
||||
|
|
||||
pos = pos_end; |
|
||||
} |
|
||||
|
|
||||
free(iplist); |
|
||||
} |
|
||||
else // ipv4
|
|
||||
{ |
|
||||
uint32_t u1,u2,u3,u4, u11,u22,u33,u44, ip; |
|
||||
uint32_t *iplist = NULL, *iplist_new, i; |
|
||||
|
|
||||
while (fgets(str, sizeof(str), stdin)) |
|
||||
{ |
|
||||
if ((i = sscanf(str, "%u.%u.%u.%u-%u.%u.%u.%u", &u1, &u2, &u3, &u4, &u11, &u22, &u33, &u44)) >= 8 && |
|
||||
!(u1 & 0xFFFFFF00) && !(u2 & 0xFFFFFF00) && !(u3 & 0xFFFFFF00) && !(u4 & 0xFFFFFF00) && |
|
||||
!(u11 & 0xFFFFFF00) && !(u22 & 0xFFFFFF00) && !(u33 & 0xFFFFFF00) && !(u44 & 0xFFFFFF00)) |
|
||||
{ |
|
||||
printf("%u.%u.%u.%u-%u.%u.%u.%u\n", u1, u2, u3, u4, u11, u22, u33, u44); |
|
||||
} |
|
||||
else |
|
||||
if ((i = sscanf(str, "%u.%u.%u.%u/%u", &u1, &u2, &u3, &u4, &zct)) >= 4 && |
|
||||
!(u1 & 0xFFFFFF00) && !(u2 & 0xFFFFFF00) && !(u3 & 0xFFFFFF00) && !(u4 & 0xFFFFFF00)) |
|
||||
{ |
|
||||
if (i == 5 && zct != 32) |
|
||||
{ |
|
||||
// we have subnet x.x.x.x/y
|
|
||||
// output it as is if valid, ignore otherwise
|
|
||||
if (zct < 32) |
|
||||
printf("%u.%u.%u.%u/%u\n", u1, u2, u3, u4, zct); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
ip = u1 << 24 | u2 << 16 | u3 << 8 | u4; |
|
||||
if (ipct >= iplist_size) |
|
||||
{ |
|
||||
iplist_size += ALLOC_STEP; |
|
||||
iplist_new = (uint32_t*)(iplist ? realloc(iplist, sizeof(*iplist)*iplist_size) : malloc(sizeof(*iplist)*iplist_size)); |
|
||||
if (!iplist_new) |
|
||||
{ |
|
||||
free(iplist); |
|
||||
fprintf(stderr, "out of memory\n"); |
|
||||
return 100; |
|
||||
} |
|
||||
iplist = iplist_new; |
|
||||
} |
|
||||
iplist[ipct++] = ip; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
gnu_quicksort(iplist, ipct, sizeof(*iplist), ucmp, NULL); |
|
||||
ipct = unique(iplist, ipct); |
|
||||
|
|
||||
while (pos < ipct) |
|
||||
{ |
|
||||
uint32_t mask, ip_start, ip_end, subnet_ct; |
|
||||
uint32_t ip_ct_best = 0, zct_best = 0; |
|
||||
|
|
||||
// find smallest network with maximum ip coverage with no less than mul/div percent addresses
|
|
||||
for (zct = params.zct_max; zct >= params.zct_min; zct--) |
|
||||
{ |
|
||||
mask = mask_from_bitcount(zct); |
|
||||
ip_start = iplist[pos] & mask; |
|
||||
subnet_ct = ~mask + 1; |
|
||||
if (iplist[pos] > (ip_start + subnet_ct*(params.pctdiv - params.pctmult) / params.pctdiv)) |
|
||||
continue; // ip is higher than (1-PCT). definitely coverage is not enough. skip searching
|
|
||||
ip_end = ip_start | ~mask; |
|
||||
for (p=pos+1, ip_ct=1; p < ipct && iplist[p] <= ip_end; p++) ip_ct++; // count ips within subnet range
|
|
||||
if (ip_ct == 1) break; |
|
||||
if (ip_ct >= (subnet_ct*params.pctmult / params.pctdiv)) |
|
||||
{ |
|
||||
// network found. but is there smaller network with the same ip_ct ? dont do carpet bombing if possible, use smaller subnets
|
|
||||
if (!ip_ct_best || ip_ct == ip_ct_best) |
|
||||
{ |
|
||||
ip_ct_best = ip_ct; |
|
||||
zct_best = zct; |
|
||||
pos_end = p; |
|
||||
} |
|
||||
else |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
if (zct_best) |
|
||||
ip_start = iplist[pos] & mask_from_bitcount(zct_best); |
|
||||
else |
|
||||
ip_start = iplist[pos], pos_end = pos + 1; // network not found, use single ip
|
|
||||
|
|
||||
u1 = ip_start >> 24; |
|
||||
u2 = (ip_start >> 16) & 0xFF; |
|
||||
u3 = (ip_start >> 8) & 0xFF; |
|
||||
u4 = ip_start & 0xFF; |
|
||||
printf(zct_best ? "%u.%u.%u.%u/%u\n" : "%u.%u.%u.%u\n", u1, u2, u3, u4, 32 - zct_best); |
|
||||
|
|
||||
pos = pos_end; |
|
||||
} |
|
||||
|
|
||||
free(iplist); |
|
||||
} |
|
||||
|
|
||||
return 0; |
|
||||
} |
|
||||
@ -1,250 +0,0 @@ |
|||||
/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
|
|
||||
This file is part of the GNU C Library. |
|
||||
Written by Douglas C. Schmidt (schmidt@ics.uci.edu). |
|
||||
|
|
||||
The GNU C Library is free software; you can redistribute it and/or |
|
||||
modify it under the terms of the GNU Lesser General Public |
|
||||
License as published by the Free Software Foundation; either |
|
||||
version 2.1 of the License, or (at your option) any later version. |
|
||||
|
|
||||
The GNU C Library is distributed in the hope that it will be useful, |
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
||||
Lesser General Public License for more details. |
|
||||
|
|
||||
You should have received a copy of the GNU Lesser General Public |
|
||||
License along with the GNU C Library; if not, see |
|
||||
<http://www.gnu.org/licenses/>. */
|
|
||||
|
|
||||
/* If you consider tuning this algorithm, you should consult first:
|
|
||||
Engineering a sort function; Jon Bentley and M. Douglas McIlroy; |
|
||||
Software - Practice and Experience; Vol. 23 (11), 1249-1265, 1993. */ |
|
||||
|
|
||||
//#include <alloca.h>
|
|
||||
#include <limits.h> |
|
||||
#include <stdlib.h> |
|
||||
//#include <string.h>
|
|
||||
#include "qsort.h" |
|
||||
|
|
||||
/* Byte-wise swap two items of size SIZE. */ |
|
||||
#define SWAP(a, b, size) \ |
|
||||
do \ |
|
||||
{ \ |
|
||||
size_t __size = (size); \ |
|
||||
char *__a = (a), *__b = (b); \ |
|
||||
do \ |
|
||||
{ \ |
|
||||
char __tmp = *__a; \ |
|
||||
*__a++ = *__b; \ |
|
||||
*__b++ = __tmp; \ |
|
||||
} while (--__size > 0); \ |
|
||||
} while (0) |
|
||||
|
|
||||
/* Discontinue quicksort algorithm when partition gets below this size.
|
|
||||
This particular magic number was chosen to work best on a Sun 4/260. */ |
|
||||
#define MAX_THRESH 4 |
|
||||
|
|
||||
/* Stack node declarations used to store unfulfilled partition obligations. */ |
|
||||
typedef struct |
|
||||
{ |
|
||||
char *lo; |
|
||||
char *hi; |
|
||||
} stack_node; |
|
||||
|
|
||||
/* The next 4 #defines implement a very fast in-line stack abstraction. */ |
|
||||
/* The stack needs log (total_elements) entries (we could even subtract
|
|
||||
log(MAX_THRESH)). Since total_elements has type size_t, we get as |
|
||||
upper bound for log (total_elements): |
|
||||
bits per byte (CHAR_BIT) * sizeof(size_t). */ |
|
||||
#define STACK_SIZE (CHAR_BIT * sizeof(size_t)) |
|
||||
#define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top)) |
|
||||
#define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi))) |
|
||||
#define STACK_NOT_EMPTY (stack < top) |
|
||||
|
|
||||
|
|
||||
/* Order size using quicksort. This implementation incorporates
|
|
||||
four optimizations discussed in Sedgewick: |
|
||||
|
|
||||
1. Non-recursive, using an explicit stack of pointer that store the |
|
||||
next array partition to sort. To save time, this maximum amount |
|
||||
of space required to store an array of SIZE_MAX is allocated on the |
|
||||
stack. Assuming a 32-bit (64 bit) integer for size_t, this needs |
|
||||
only 32 * sizeof(stack_node) == 256 bytes (for 64 bit: 1024 bytes). |
|
||||
Pretty cheap, actually. |
|
||||
|
|
||||
2. Chose the pivot element using a median-of-three decision tree. |
|
||||
This reduces the probability of selecting a bad pivot value and |
|
||||
eliminates certain extraneous comparisons. |
|
||||
|
|
||||
3. Only quicksorts TOTAL_ELEMS / MAX_THRESH partitions, leaving |
|
||||
insertion sort to order the MAX_THRESH items within each partition. |
|
||||
This is a big win, since insertion sort is faster for small, mostly |
|
||||
sorted array segments. |
|
||||
|
|
||||
4. The larger of the two sub-partitions is always pushed onto the |
|
||||
stack first, with the algorithm then concentrating on the |
|
||||
smaller partition. This *guarantees* no more than log (total_elems) |
|
||||
stack size is needed (actually O(1) in this case)! */ |
|
||||
|
|
||||
void |
|
||||
gnu_quicksort (void *const pbase, size_t total_elems, size_t size, |
|
||||
__gnu_compar_d_fn_t cmp, void *arg) |
|
||||
{ |
|
||||
char *base_ptr = (char *) pbase; |
|
||||
|
|
||||
const size_t max_thresh = MAX_THRESH * size; |
|
||||
|
|
||||
if (total_elems == 0) |
|
||||
/* Avoid lossage with unsigned arithmetic below. */ |
|
||||
return; |
|
||||
|
|
||||
if (total_elems > MAX_THRESH) |
|
||||
{ |
|
||||
char *lo = base_ptr; |
|
||||
char *hi = &lo[size * (total_elems - 1)]; |
|
||||
stack_node stack[STACK_SIZE]; |
|
||||
stack_node *top = stack; |
|
||||
|
|
||||
PUSH (NULL, NULL); |
|
||||
|
|
||||
while (STACK_NOT_EMPTY) |
|
||||
{ |
|
||||
char *left_ptr; |
|
||||
char *right_ptr; |
|
||||
|
|
||||
/* Select median value from among LO, MID, and HI. Rearrange
|
|
||||
LO and HI so the three values are sorted. This lowers the |
|
||||
probability of picking a pathological pivot value and |
|
||||
skips a comparison for both the LEFT_PTR and RIGHT_PTR in |
|
||||
the while loops. */ |
|
||||
|
|
||||
char *mid = lo + size * ((hi - lo) / size >> 1); |
|
||||
|
|
||||
if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) |
|
||||
SWAP (mid, lo, size); |
|
||||
if ((*cmp) ((void *) hi, (void *) mid, arg) < 0) |
|
||||
SWAP (mid, hi, size); |
|
||||
else |
|
||||
goto jump_over; |
|
||||
if ((*cmp) ((void *) mid, (void *) lo, arg) < 0) |
|
||||
SWAP (mid, lo, size); |
|
||||
jump_over:; |
|
||||
|
|
||||
left_ptr = lo + size; |
|
||||
right_ptr = hi - size; |
|
||||
|
|
||||
/* Here's the famous ``collapse the walls'' section of quicksort.
|
|
||||
Gotta like those tight inner loops! They are the main reason |
|
||||
that this algorithm runs much faster than others. */ |
|
||||
do |
|
||||
{ |
|
||||
while ((*cmp) ((void *) left_ptr, (void *) mid, arg) < 0) |
|
||||
left_ptr += size; |
|
||||
|
|
||||
while ((*cmp) ((void *) mid, (void *) right_ptr, arg) < 0) |
|
||||
right_ptr -= size; |
|
||||
|
|
||||
if (left_ptr < right_ptr) |
|
||||
{ |
|
||||
SWAP (left_ptr, right_ptr, size); |
|
||||
if (mid == left_ptr) |
|
||||
mid = right_ptr; |
|
||||
else if (mid == right_ptr) |
|
||||
mid = left_ptr; |
|
||||
left_ptr += size; |
|
||||
right_ptr -= size; |
|
||||
} |
|
||||
else if (left_ptr == right_ptr) |
|
||||
{ |
|
||||
left_ptr += size; |
|
||||
right_ptr -= size; |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
while (left_ptr <= right_ptr); |
|
||||
|
|
||||
/* Set up pointers for next iteration. First determine whether
|
|
||||
left and right partitions are below the threshold size. If so, |
|
||||
ignore one or both. Otherwise, push the larger partition's |
|
||||
bounds on the stack and continue sorting the smaller one. */ |
|
||||
|
|
||||
if ((size_t) (right_ptr - lo) <= max_thresh) |
|
||||
{ |
|
||||
if ((size_t) (hi - left_ptr) <= max_thresh) |
|
||||
/* Ignore both small partitions. */ |
|
||||
POP (lo, hi); |
|
||||
else |
|
||||
/* Ignore small left partition. */ |
|
||||
lo = left_ptr; |
|
||||
} |
|
||||
else if ((size_t) (hi - left_ptr) <= max_thresh) |
|
||||
/* Ignore small right partition. */ |
|
||||
hi = right_ptr; |
|
||||
else if ((right_ptr - lo) > (hi - left_ptr)) |
|
||||
{ |
|
||||
/* Push larger left partition indices. */ |
|
||||
PUSH (lo, right_ptr); |
|
||||
lo = left_ptr; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
/* Push larger right partition indices. */ |
|
||||
PUSH (left_ptr, hi); |
|
||||
hi = right_ptr; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* Once the BASE_PTR array is partially sorted by quicksort the rest
|
|
||||
is completely sorted using insertion sort, since this is efficient |
|
||||
for partitions below MAX_THRESH size. BASE_PTR points to the beginning |
|
||||
of the array to sort, and END_PTR points at the very last element in |
|
||||
the array (*not* one beyond it!). */ |
|
||||
|
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y)) |
|
||||
|
|
||||
{ |
|
||||
char *const end_ptr = &base_ptr[size * (total_elems - 1)]; |
|
||||
char *tmp_ptr = base_ptr; |
|
||||
char *thresh = min(end_ptr, base_ptr + max_thresh); |
|
||||
char *run_ptr; |
|
||||
|
|
||||
/* Find smallest element in first threshold and place it at the
|
|
||||
array's beginning. This is the smallest array element, |
|
||||
and the operation speeds up insertion sort's inner loop. */ |
|
||||
|
|
||||
for (run_ptr = tmp_ptr + size; run_ptr <= thresh; run_ptr += size) |
|
||||
if ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) |
|
||||
tmp_ptr = run_ptr; |
|
||||
|
|
||||
if (tmp_ptr != base_ptr) |
|
||||
SWAP (tmp_ptr, base_ptr, size); |
|
||||
|
|
||||
/* Insertion sort, running from left-hand-side up to right-hand-side. */ |
|
||||
|
|
||||
run_ptr = base_ptr + size; |
|
||||
while ((run_ptr += size) <= end_ptr) |
|
||||
{ |
|
||||
tmp_ptr = run_ptr - size; |
|
||||
while ((*cmp) ((void *) run_ptr, (void *) tmp_ptr, arg) < 0) |
|
||||
tmp_ptr -= size; |
|
||||
|
|
||||
tmp_ptr += size; |
|
||||
if (tmp_ptr != run_ptr) |
|
||||
{ |
|
||||
char *trav; |
|
||||
|
|
||||
trav = run_ptr + size; |
|
||||
while (--trav >= run_ptr) |
|
||||
{ |
|
||||
char c = *trav; |
|
||||
char *hi, *lo; |
|
||||
|
|
||||
for (hi = lo = trav; (lo -= size) >= tmp_ptr; hi = lo) |
|
||||
*hi = *lo; |
|
||||
*hi = c; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
// GNU qsort is 2x faster than musl
|
|
||||
|
|
||||
typedef int (*__gnu_compar_d_fn_t) (const void *, const void *, void *); |
|
||||
void gnu_quicksort (void *const pbase, size_t total_elems, size_t size, __gnu_compar_d_fn_t cmp, void *arg); |
|
||||
@ -1,35 +0,0 @@ |
|||||
CC ?= cc |
|
||||
OPTIMIZE ?= -Os |
|
||||
CFLAGS += -std=gnu99 $(OPTIMIZE) |
|
||||
CFLAGS_BSD = -Wno-address-of-packed-member |
|
||||
CFLAGS_WIN = -static |
|
||||
LIBS = -lpthread |
|
||||
LIBS_ANDROID = |
|
||||
LIBS_WIN = -lws2_32 |
|
||||
SRC_FILES = *.c |
|
||||
|
|
||||
all: mdig |
|
||||
|
|
||||
mdig: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS) |
|
||||
|
|
||||
systemd: mdig |
|
||||
|
|
||||
android: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) -o mdig $(SRC_FILES) $(LIBS_ANDROID) $(LDFLAGS) |
|
||||
|
|
||||
bsd: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o mdig $(SRC_FILES) $(LIBS) $(LDFLAGS) |
|
||||
|
|
||||
mac: $(SRC_FILES) |
|
||||
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o mdiga $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS) |
|
||||
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o mdigx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS) |
|
||||
strip mdiga mdigx |
|
||||
lipo -create -output mdig mdigx mdiga |
|
||||
rm -f mdigx mdiga |
|
||||
|
|
||||
win: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_WIN) -o mdig $(SRC_FILES) $(LIBS_WIN) $(LDFLAGS) |
|
||||
|
|
||||
clean: |
|
||||
rm -f mdig *.o |
|
||||
@ -1,596 +0,0 @@ |
|||||
// multi thread dns resolver
|
|
||||
// domain list <stdin
|
|
||||
// ip list >stdout
|
|
||||
// errors, verbose >stderr
|
|
||||
// transparent for valid ip or ip/subnet of allowed address family
|
|
||||
|
|
||||
#define _GNU_SOURCE |
|
||||
|
|
||||
#include <stdio.h> |
|
||||
#include <stdint.h> |
|
||||
#include <stdarg.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <string.h> |
|
||||
#include <stdbool.h> |
|
||||
#include <pthread.h> |
|
||||
#include <getopt.h> |
|
||||
#ifdef _WIN32 |
|
||||
#undef _WIN32_WINNT |
|
||||
#define _WIN32_WINNT 0x600 |
|
||||
#include <winsock2.h> |
|
||||
#include <ws2ipdef.h> |
|
||||
#include <ws2tcpip.h> |
|
||||
#include <fcntl.h> |
|
||||
#else |
|
||||
#include <unistd.h> |
|
||||
#include <sys/socket.h> |
|
||||
#include <arpa/inet.h> |
|
||||
#include <netinet/in.h> |
|
||||
#include <netdb.h> |
|
||||
#endif |
|
||||
#include <time.h> |
|
||||
|
|
||||
#define RESOLVER_EAGAIN_ATTEMPTS 2 |
|
||||
|
|
||||
static void trimstr(char *s) |
|
||||
{ |
|
||||
char *p; |
|
||||
for (p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r' || *p == ' ' || *p == '\t'); p--) *p = '\0'; |
|
||||
} |
|
||||
|
|
||||
static const char* eai_str(int r) |
|
||||
{ |
|
||||
switch (r) |
|
||||
{ |
|
||||
case EAI_NONAME: |
|
||||
return "EAI_NONAME"; |
|
||||
case EAI_AGAIN: |
|
||||
return "EAI_AGAIN"; |
|
||||
#ifdef EAI_ADDRFAMILY |
|
||||
case EAI_ADDRFAMILY: |
|
||||
return "EAI_ADDRFAMILY"; |
|
||||
#endif |
|
||||
#ifdef EAI_NODATA |
|
||||
case EAI_NODATA: |
|
||||
return "EAI_NODATA"; |
|
||||
#endif |
|
||||
case EAI_BADFLAGS: |
|
||||
return "EAI_BADFLAGS"; |
|
||||
case EAI_FAIL: |
|
||||
return "EAI_FAIL"; |
|
||||
case EAI_MEMORY: |
|
||||
return "EAI_MEMORY"; |
|
||||
case EAI_FAMILY: |
|
||||
return "EAI_FAMILY"; |
|
||||
case EAI_SERVICE: |
|
||||
return "EAI_SERVICE"; |
|
||||
case EAI_SOCKTYPE: |
|
||||
return "EAI_SOCKTYPE"; |
|
||||
#ifdef EAI_SYSTEM |
|
||||
case EAI_SYSTEM: |
|
||||
return "EAI_SYSTEM"; |
|
||||
#endif |
|
||||
default: |
|
||||
return "UNKNOWN"; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
static bool dom_valid(char *dom) |
|
||||
{ |
|
||||
if (!dom || *dom=='.') return false; |
|
||||
for (; *dom; dom++) |
|
||||
if (*dom < 0x20 || (*dom & 0x80) || !(*dom == '.' || *dom == '-' || *dom == '_' || (*dom >= '0' && *dom <= '9') || (*dom >= 'a' && *dom <= 'z') || (*dom >= 'A' && *dom <= 'Z'))) |
|
||||
return false; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static void invalid_domain_beautify(char *dom) |
|
||||
{ |
|
||||
for (int i = 0; *dom && i < 64; i++, dom++) |
|
||||
if (*dom < 0x20 || *dom<0) *dom = '?'; |
|
||||
if (*dom) *dom = 0; |
|
||||
} |
|
||||
|
|
||||
#define FAMILY4 1 |
|
||||
#define FAMILY6 2 |
|
||||
static struct |
|
||||
{ |
|
||||
char verbose; |
|
||||
char family; |
|
||||
int threads; |
|
||||
time_t start_time; |
|
||||
pthread_mutex_t flock; |
|
||||
pthread_mutex_t slock; // stats lock
|
|
||||
int stats_every, stats_ct, stats_ct_ok; // stats
|
|
||||
FILE *F_log_resolved, *F_log_failed; |
|
||||
} glob; |
|
||||
|
|
||||
// get next domain. return 0 if failure
|
|
||||
static char interlocked_get_dom(char *dom, size_t size) |
|
||||
{ |
|
||||
char *s; |
|
||||
pthread_mutex_lock(&glob.flock); |
|
||||
s = fgets(dom, size, stdin); |
|
||||
pthread_mutex_unlock(&glob.flock); |
|
||||
if (!s) return 0; |
|
||||
trimstr(s); |
|
||||
return 1; |
|
||||
} |
|
||||
static void interlocked_fprintf(FILE *stream, const char * format, ...) |
|
||||
{ |
|
||||
if (stream) |
|
||||
{ |
|
||||
va_list args; |
|
||||
va_start(args, format); |
|
||||
pthread_mutex_lock(&glob.flock); |
|
||||
vfprintf(stream, format, args); |
|
||||
pthread_mutex_unlock(&glob.flock); |
|
||||
va_end(args); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#define ELOG(format, ...) interlocked_fprintf(stderr, "[%d] " format "\n", tid, ##__VA_ARGS__) |
|
||||
#define VLOG(format, ...) {if (glob.verbose) ELOG(format, ##__VA_ARGS__);} |
|
||||
|
|
||||
static void print_addrinfo(struct addrinfo *ai) |
|
||||
{ |
|
||||
char str[64]; |
|
||||
while (ai) |
|
||||
{ |
|
||||
switch (ai->ai_family) |
|
||||
{ |
|
||||
case AF_INET: |
|
||||
if (inet_ntop(ai->ai_family, &((struct sockaddr_in*)ai->ai_addr)->sin_addr, str, sizeof(str))) |
|
||||
interlocked_fprintf(stdout, "%s\n", str); |
|
||||
break; |
|
||||
case AF_INET6: |
|
||||
if (inet_ntop(ai->ai_family, &((struct sockaddr_in6*)ai->ai_addr)->sin6_addr, str, sizeof(str))) |
|
||||
interlocked_fprintf(stdout, "%s\n", str); |
|
||||
break; |
|
||||
} |
|
||||
ai = ai->ai_next; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
static void stat_print(int ct, int ct_ok) |
|
||||
{ |
|
||||
if (glob.stats_every > 0) |
|
||||
{ |
|
||||
time_t tm = time(NULL)-glob.start_time; |
|
||||
interlocked_fprintf(stderr, "mdig stats : %02u:%02u:%02u : domains=%d success=%d error=%d\n", (unsigned int)(tm/3600), (unsigned int)((tm/60)%60), (unsigned int)(tm%60), ct, ct_ok, ct - ct_ok); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
static void stat_plus(bool is_ok) |
|
||||
{ |
|
||||
int ct, ct_ok; |
|
||||
if (glob.stats_every > 0) |
|
||||
{ |
|
||||
pthread_mutex_lock(&glob.slock); |
|
||||
ct = ++glob.stats_ct; |
|
||||
ct_ok = glob.stats_ct_ok += is_ok; |
|
||||
pthread_mutex_unlock(&glob.slock); |
|
||||
|
|
||||
if (!(ct % glob.stats_every)) stat_print(ct, ct_ok); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
static uint16_t GetAddrFamily(const char *saddr) |
|
||||
{ |
|
||||
struct in_addr a4; |
|
||||
struct in6_addr a6; |
|
||||
|
|
||||
if (inet_pton(AF_INET, saddr, &a4)) |
|
||||
return AF_INET; |
|
||||
else if (inet_pton(AF_INET6, saddr, &a6)) |
|
||||
return AF_INET6; |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
static void *t_resolver(void *arg) |
|
||||
{ |
|
||||
int tid = (int)(size_t)arg; |
|
||||
int i, r; |
|
||||
char dom[256]; |
|
||||
bool is_ok; |
|
||||
struct addrinfo hints; |
|
||||
struct addrinfo *result; |
|
||||
|
|
||||
VLOG("started"); |
|
||||
|
|
||||
memset(&hints, 0, sizeof(struct addrinfo)); |
|
||||
hints.ai_family = (glob.family == FAMILY4) ? AF_INET : (glob.family == FAMILY6) ? AF_INET6 : AF_UNSPEC; |
|
||||
hints.ai_socktype = SOCK_DGRAM; |
|
||||
|
|
||||
while (interlocked_get_dom(dom, sizeof(dom))) |
|
||||
{ |
|
||||
is_ok = false; |
|
||||
if (*dom) |
|
||||
{ |
|
||||
uint16_t family; |
|
||||
char *s_mask, s_ip[sizeof(dom)]; |
|
||||
|
|
||||
strncpy(s_ip, dom, sizeof(s_ip)); |
|
||||
s_mask = strchr(s_ip, '/'); |
|
||||
if (s_mask) *s_mask++ = 0; |
|
||||
family = GetAddrFamily(s_ip); |
|
||||
if (family) |
|
||||
{ |
|
||||
if ((family == AF_INET && (glob.family & FAMILY4)) || (family == AF_INET6 && (glob.family & FAMILY6))) |
|
||||
{ |
|
||||
unsigned int mask; |
|
||||
bool mask_needed = false; |
|
||||
if (s_mask) |
|
||||
{ |
|
||||
if (sscanf(s_mask, "%u", &mask)==1) |
|
||||
{ |
|
||||
switch (family) |
|
||||
{ |
|
||||
case AF_INET: is_ok = mask <= 32; mask_needed = mask < 32; break; |
|
||||
case AF_INET6: is_ok = mask <= 128; mask_needed = mask < 128; break; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
is_ok = true; |
|
||||
if (is_ok) |
|
||||
interlocked_fprintf(stdout, mask_needed ? "%s/%u\n" : "%s\n", s_ip, mask); |
|
||||
else |
|
||||
VLOG("bad ip/subnet %s", dom); |
|
||||
} |
|
||||
else |
|
||||
VLOG("wrong address family %s", s_ip); |
|
||||
} |
|
||||
else if (dom_valid(dom)) |
|
||||
{ |
|
||||
VLOG("resolving %s", dom); |
|
||||
for (i = 0; i < RESOLVER_EAGAIN_ATTEMPTS; i++) |
|
||||
{ |
|
||||
if ((r = getaddrinfo(dom, NULL, &hints, &result))) |
|
||||
{ |
|
||||
VLOG("failed to resolve %s : result %d (%s)", dom, r, eai_str(r)); |
|
||||
if (r == EAI_AGAIN) continue; // temporary failure. should retry
|
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
print_addrinfo(result); |
|
||||
freeaddrinfo(result); |
|
||||
is_ok = true; |
|
||||
} |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
else if (glob.verbose) |
|
||||
{ |
|
||||
char dom2[sizeof(dom)]; |
|
||||
strcpy(dom2,dom); |
|
||||
invalid_domain_beautify(dom2); |
|
||||
VLOG("invalid domain : %s", dom2); |
|
||||
} |
|
||||
interlocked_fprintf(is_ok ? glob.F_log_resolved : glob.F_log_failed,"%s\n",dom); |
|
||||
} |
|
||||
stat_plus(is_ok); |
|
||||
} |
|
||||
VLOG("ended"); |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
static int run_threads(void) |
|
||||
{ |
|
||||
int i, thread; |
|
||||
pthread_t *t; |
|
||||
|
|
||||
glob.stats_ct = glob.stats_ct_ok = 0; |
|
||||
time(&glob.start_time); |
|
||||
if (pthread_mutex_init(&glob.flock, NULL) != 0) |
|
||||
{ |
|
||||
fprintf(stderr, "mutex init failed\n"); |
|
||||
return 10; |
|
||||
} |
|
||||
if (pthread_mutex_init(&glob.slock, NULL) != 0) |
|
||||
{ |
|
||||
fprintf(stderr, "mutex init failed\n"); |
|
||||
pthread_mutex_destroy(&glob.flock); |
|
||||
return 10; |
|
||||
} |
|
||||
t = (pthread_t*)malloc(sizeof(pthread_t)*glob.threads); |
|
||||
if (!t) |
|
||||
{ |
|
||||
fprintf(stderr, "out of memory\n"); |
|
||||
pthread_mutex_destroy(&glob.slock); |
|
||||
pthread_mutex_destroy(&glob.flock); |
|
||||
return 11; |
|
||||
} |
|
||||
for (thread = 0; thread < glob.threads; thread++) |
|
||||
{ |
|
||||
if (pthread_create(t + thread, NULL, t_resolver, (void*)(size_t)thread)) |
|
||||
{ |
|
||||
interlocked_fprintf(stderr, "failed to create thread #%d\n", thread); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
for (i = 0; i < thread; i++) |
|
||||
{ |
|
||||
pthread_join(t[i], NULL); |
|
||||
} |
|
||||
free(t); |
|
||||
stat_print(glob.stats_ct, glob.stats_ct_ok); |
|
||||
pthread_mutex_destroy(&glob.slock); |
|
||||
pthread_mutex_destroy(&glob.flock); |
|
||||
return thread ? 0 : 12; |
|
||||
} |
|
||||
|
|
||||
// slightly patched musl code
|
|
||||
size_t dns_mk_query_blob(uint8_t op, const char *dname, uint8_t class, uint8_t type, uint8_t *buf, size_t buflen) |
|
||||
{ |
|
||||
int i, j; |
|
||||
uint16_t id; |
|
||||
struct timespec ts; |
|
||||
size_t l = strnlen(dname, 255); |
|
||||
size_t n; |
|
||||
|
|
||||
if (l && dname[l-1]=='.') l--; |
|
||||
if (l && dname[l-1]=='.') return 0; |
|
||||
n = 17+l+!!l; |
|
||||
if (l>253 || buflen<n || op>15u) return 0; |
|
||||
|
|
||||
/* Construct query template - ID will be filled later */ |
|
||||
memset(buf, 0, n); |
|
||||
buf[2] = (op<<3) | 1; |
|
||||
buf[5] = 1; |
|
||||
memcpy((char *)buf+13, dname, l); |
|
||||
for (i=13; buf[i]; i=j+1) |
|
||||
{ |
|
||||
for (j=i; buf[j] && buf[j] != '.'; j++); |
|
||||
if (j-i-1u > 62u) return 0; |
|
||||
buf[i-1] = j-i; |
|
||||
} |
|
||||
buf[i+1] = type; |
|
||||
buf[i+3] = class; |
|
||||
|
|
||||
/* Make a reasonably unpredictable id */ |
|
||||
clock_gettime(CLOCK_REALTIME, &ts); |
|
||||
id = (uint16_t)ts.tv_nsec + (uint16_t)(ts.tv_nsec>>16); |
|
||||
buf[0] = id>>8; |
|
||||
buf[1] = id; |
|
||||
|
|
||||
return n; |
|
||||
} |
|
||||
int dns_make_query(const char *dom, char family) |
|
||||
{ |
|
||||
uint8_t q[280]; |
|
||||
size_t l = dns_mk_query_blob(0, dom, 1, family == FAMILY6 ? 28 : 1, q, sizeof(q)); |
|
||||
if (!l) |
|
||||
{ |
|
||||
fprintf(stderr, "could not make DNS query\n"); |
|
||||
return 1; |
|
||||
} |
|
||||
#ifdef _WIN32 |
|
||||
_setmode(_fileno(stdout), _O_BINARY); |
|
||||
#endif |
|
||||
if (fwrite(q,l,1,stdout)!=1) |
|
||||
{ |
|
||||
fprintf(stderr, "could not write DNS query blob to stdout\n"); |
|
||||
return 10; |
|
||||
} |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
bool dns_parse_print(const uint8_t *a, size_t len) |
|
||||
{ |
|
||||
// check of minimum header length and response flag
|
|
||||
uint16_t k, dlen, qcount = a[4]<<8 | a[5], acount = a[6]<<8 | a[7]; |
|
||||
char s_ip[40]; |
|
||||
|
|
||||
if (len<12 || !(a[2]&0x80)) return false; |
|
||||
a+=12; len-=12; |
|
||||
for(k=0;k<qcount;k++) |
|
||||
{ |
|
||||
while (len && *a) |
|
||||
{ |
|
||||
if ((*a+1)>len) return false; |
|
||||
// skip to next label
|
|
||||
len -= *a+1; a += *a+1; |
|
||||
} |
|
||||
if (len<5) return false; |
|
||||
// skip zero length label, type, class
|
|
||||
a+=5; len-=5; |
|
||||
} |
|
||||
for(k=0;k<acount;k++) |
|
||||
{ |
|
||||
// 11 higher bits indicate pointer
|
|
||||
if (len<12 || (*a & 0xC0)!=0xC0) return false; |
|
||||
dlen = a[10]<<8 | a[11]; |
|
||||
if (len<(dlen+12)) return false; |
|
||||
if (a[4]==0 && a[5]==1 && a[2]==0) // IN class and higher byte of type = 0
|
|
||||
{ |
|
||||
switch(a[3]) |
|
||||
{ |
|
||||
case 1: // A
|
|
||||
if (dlen!=4) break; |
|
||||
if (inet_ntop(AF_INET, a+12, s_ip, sizeof(s_ip))) |
|
||||
printf("%s\n", s_ip); |
|
||||
break; |
|
||||
case 28: // AAAA
|
|
||||
if (dlen!=16) break; |
|
||||
if (inet_ntop(AF_INET6, a+12, s_ip, sizeof(s_ip))) |
|
||||
printf("%s\n", s_ip); |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
len -= 12+dlen; a += 12+dlen; |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
int dns_parse_query() |
|
||||
{ |
|
||||
uint8_t a[8192]; |
|
||||
size_t l; |
|
||||
#ifdef _WIN32 |
|
||||
_setmode(_fileno(stdin), _O_BINARY); |
|
||||
#endif |
|
||||
l = fread(a,1,sizeof(a),stdin); |
|
||||
if (!l || !feof(stdin)) |
|
||||
{ |
|
||||
fprintf(stderr, "could not read DNS reply blob from stdin\n"); |
|
||||
return 10; |
|
||||
} |
|
||||
if (!dns_parse_print(a,l)) |
|
||||
{ |
|
||||
fprintf(stderr, "could not parse DNS reply blob\n"); |
|
||||
return 11; |
|
||||
} |
|
||||
return 0; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static void exithelp(void) |
|
||||
{ |
|
||||
printf( |
|
||||
" --threads=<threads_number>\n" |
|
||||
" --family=<4|6|46>\t\t; ipv4, ipv6, ipv4+ipv6\n" |
|
||||
" --verbose\t\t\t; print query progress to stderr\n" |
|
||||
" --stats=N\t\t\t; print resolve stats to stderr every N domains\n" |
|
||||
" --log-resolved=<file>\t\t; log successfully resolved domains to a file\n" |
|
||||
" --log-failed=<file>\t\t; log failed domains to a file\n" |
|
||||
" --dns-make-query=<domain>\t; output to stdout binary blob with DNS query. use --family to specify ip version.\n" |
|
||||
" --dns-parse-query\t\t; read from stdin binary DNS answer blob and parse it to ipv4/ipv6 addresses\n" |
|
||||
); |
|
||||
exit(1); |
|
||||
} |
|
||||
|
|
||||
#define STRINGIFY(x) #x |
|
||||
#define TOSTRING(x) STRINGIFY(x) |
|
||||
#if defined(ZAPRET_GH_VER) || defined (ZAPRET_GH_HASH) |
|
||||
#define PRINT_VER printf("github version %s (%s)\n\n", TOSTRING(ZAPRET_GH_VER), TOSTRING(ZAPRET_GH_HASH)) |
|
||||
#else |
|
||||
#define PRINT_VER printf("self-built version %s %s\n\n", __DATE__, __TIME__) |
|
||||
#endif |
|
||||
|
|
||||
enum opt_indices { |
|
||||
IDX_HELP, |
|
||||
IDX_THREADS, |
|
||||
IDX_FAMILY, |
|
||||
IDX_VERBOSE, |
|
||||
IDX_STATS, |
|
||||
IDX_LOG_RESOLVED, |
|
||||
IDX_LOG_FAILED, |
|
||||
IDX_DNS_MAKE_QUERY, |
|
||||
IDX_DNS_PARSE_QUERY, |
|
||||
IDX_LAST, |
|
||||
}; |
|
||||
|
|
||||
static const struct option long_options[] = { |
|
||||
[IDX_HELP] = {"help", no_argument, 0, 0}, |
|
||||
[IDX_THREADS] = {"threads", required_argument, 0, 0}, |
|
||||
[IDX_FAMILY] = {"family", required_argument, 0, 0}, |
|
||||
[IDX_VERBOSE] = {"verbose", no_argument, 0, 0}, |
|
||||
[IDX_STATS] = {"stats", required_argument, 0, 0}, |
|
||||
[IDX_LOG_RESOLVED] = {"log-resolved", required_argument, 0, 0}, |
|
||||
[IDX_LOG_FAILED] = {"log-failed", required_argument, 0, 0}, |
|
||||
[IDX_DNS_MAKE_QUERY] = {"dns-make-query", required_argument, 0, 0}, |
|
||||
[IDX_DNS_PARSE_QUERY] = {"dns-parse-query", no_argument, 0, 0}, |
|
||||
[IDX_LAST] = {NULL, 0, NULL, 0}, |
|
||||
}; |
|
||||
|
|
||||
int main(int argc, char **argv) |
|
||||
{ |
|
||||
int r, v, option_index = 0; |
|
||||
char fn1[256],fn2[256]; |
|
||||
char dom[256]; |
|
||||
|
|
||||
memset(&glob, 0, sizeof(glob)); |
|
||||
*fn1 = *fn2 = *dom = 0; |
|
||||
glob.family = FAMILY4; |
|
||||
glob.threads = 1; |
|
||||
while ((v = getopt_long_only(argc, argv, "", long_options, &option_index)) != -1) |
|
||||
{ |
|
||||
if (v) exithelp(); |
|
||||
switch (option_index) |
|
||||
{ |
|
||||
case IDX_HELP: |
|
||||
PRINT_VER; |
|
||||
exithelp(); |
|
||||
break; |
|
||||
case IDX_THREADS: |
|
||||
glob.threads = optarg ? atoi(optarg) : 0; |
|
||||
if (glob.threads <= 0 || glob.threads > 100) |
|
||||
{ |
|
||||
fprintf(stderr, "thread number must be within 1..100\n"); |
|
||||
return 1; |
|
||||
} |
|
||||
break; |
|
||||
case IDX_FAMILY: |
|
||||
if (!strcmp(optarg, "4")) |
|
||||
glob.family = FAMILY4; |
|
||||
else if (!strcmp(optarg, "6")) |
|
||||
glob.family = FAMILY6; |
|
||||
else if (!strcmp(optarg, "46")) |
|
||||
glob.family = FAMILY4 | FAMILY6; |
|
||||
else |
|
||||
{ |
|
||||
fprintf(stderr, "ip family must be 4,6 or 46\n"); |
|
||||
return 1; |
|
||||
} |
|
||||
break; |
|
||||
case IDX_VERBOSE: |
|
||||
glob.verbose = '\1'; |
|
||||
break; |
|
||||
case IDX_STATS: |
|
||||
glob.stats_every = optarg ? atoi(optarg) : 0; |
|
||||
break; |
|
||||
case IDX_LOG_RESOLVED: |
|
||||
strncpy(fn1,optarg,sizeof(fn1)); |
|
||||
fn1[sizeof(fn1)-1] = 0; |
|
||||
break; |
|
||||
case IDX_LOG_FAILED: |
|
||||
strncpy(fn2,optarg,sizeof(fn2)); |
|
||||
fn2[sizeof(fn2)-1] = 0; |
|
||||
break; |
|
||||
case IDX_DNS_MAKE_QUERY: |
|
||||
strncpy(dom,optarg,sizeof(dom)); |
|
||||
dom[sizeof(dom)-1] = 0; |
|
||||
break; |
|
||||
case IDX_DNS_PARSE_QUERY: |
|
||||
return dns_parse_query(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
#ifdef _WIN32 |
|
||||
WSADATA wsaData; |
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData)) |
|
||||
{ |
|
||||
fprintf(stderr,"WSAStartup failed\n"); |
|
||||
return 4; |
|
||||
} |
|
||||
#endif |
|
||||
|
|
||||
if (*dom) return dns_make_query(dom, glob.family); |
|
||||
|
|
||||
if (*fn1) |
|
||||
{ |
|
||||
glob.F_log_resolved = fopen(fn1,"wt"); |
|
||||
if (!glob.F_log_resolved) |
|
||||
{ |
|
||||
fprintf(stderr,"failed to create %s\n",fn1); |
|
||||
r=5; goto ex; |
|
||||
} |
|
||||
} |
|
||||
if (*fn2) |
|
||||
{ |
|
||||
glob.F_log_failed = fopen(fn2,"wt"); |
|
||||
if (!glob.F_log_failed) |
|
||||
{ |
|
||||
fprintf(stderr,"failed to create %s\n",fn2); |
|
||||
r=5; goto ex; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
r = run_threads(); |
|
||||
|
|
||||
ex: |
|
||||
if (glob.F_log_resolved) fclose(glob.F_log_resolved); |
|
||||
if (glob.F_log_failed) fclose(glob.F_log_failed); |
|
||||
|
|
||||
return r; |
|
||||
} |
|
||||
@ -1,13 +0,0 @@ |
|||||
CC ?= cc |
|
||||
OPTIMIZE ?= -Os |
|
||||
CFLAGS += -std=gnu99 -s $(OPTIMIZE) -flto=auto -Wno-address-of-packed-member |
|
||||
LIBS = -lz |
|
||||
SRC_FILES = *.c crypto/*.c |
|
||||
|
|
||||
all: dvtws |
|
||||
|
|
||||
dvtws: $(SRC_FILES) |
|
||||
$(CC) $(CFLAGS) -o dvtws $(SRC_FILES) $(LIBS) $(LDFLAGS) |
|
||||
|
|
||||
clean: |
|
||||
rm -f dvtws |
|
||||
@ -1,46 +0,0 @@ |
|||||
CC ?= cc |
|
||||
OPTIMIZE ?= -Os |
|
||||
CFLAGS += -std=gnu99 $(OPTIMIZE) -flto=auto |
|
||||
CFLAGS_SYSTEMD = -DUSE_SYSTEMD |
|
||||
CFLAGS_BSD = -Wno-address-of-packed-member |
|
||||
CFLAGS_CYGWIN = -Wno-address-of-packed-member -static |
|
||||
LDFLAGS_ANDROID = -llog |
|
||||
LIBS_LINUX = -lz -lnetfilter_queue -lnfnetlink -lmnl |
|
||||
LIBS_SYSTEMD = -lsystemd |
|
||||
LIBS_BSD = -lz |
|
||||
LIBS_CYGWIN = -lz -Lwindows/windivert -Iwindows -lwlanapi -lole32 -loleaut32 |
|
||||
LIBS_CYGWIN32 = -lwindivert32 |
|
||||
LIBS_CYGWIN64 = -lwindivert64 |
|
||||
RES_CYGWIN32 = windows/res/32/winmanifest.o windows/res/32/winicon.o |
|
||||
RES_CYGWIN64 = windows/res/64/winmanifest.o windows/res/64/winicon.o |
|
||||
SRC_FILES = *.c crypto/*.c |
|
||||
|
|
||||
all: nfqws |
|
||||
|
|
||||
nfqws: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LDFLAGS) |
|
||||
|
|
||||
systemd: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_SYSTEMD) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LIBS_SYSTEMD) $(LDFLAGS) |
|
||||
|
|
||||
android: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) -o nfqws $(SRC_FILES) $(LIBS_LINUX) $(LDFLAGS) $(LDFLAGS_ANDROID) |
|
||||
|
|
||||
bsd: $(SRC_FILES) |
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_BSD) -o dvtws $(SRC_FILES) $(LIBS_BSD) $(LDFLAGS) |
|
||||
|
|
||||
mac: $(SRC_FILES) |
|
||||
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o dvtwsa $(SRC_FILES) -target arm64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS) |
|
||||
$(CC) $(CFLAGS) $(CFLAGS_BSD) -o dvtwsx $(SRC_FILES) -target x86_64-apple-macos10.8 $(LIBS_BSD) $(LDFLAGS) |
|
||||
strip dvtwsa dvtwsx |
|
||||
lipo -create -output dvtws dvtwsx dvtwsa |
|
||||
rm -f dvtwsx dvtwsa |
|
||||
|
|
||||
cygwin64: |
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_CYGWIN) -o winws $(SRC_FILES) $(LIBS_CYGWIN) $(LIBS_CYGWIN64) $(RES_CYGWIN64) $(LDFLAGS) |
|
||||
cygwin32: |
|
||||
$(CC) -s $(CFLAGS) $(CFLAGS_CYGWIN) -o winws $(SRC_FILES) $(LIBS_CYGWIN) $(LIBS_CYGWIN32) $(RES_CYGWIN32) $(LDFLAGS) |
|
||||
cygwin: cygwin64 |
|
||||
|
|
||||
clean: |
|
||||
rm -f nfqws dvtws winws.exe |
|
||||
@ -1,159 +0,0 @@ |
|||||
#define _GNU_SOURCE |
|
||||
#include "checksum.h" |
|
||||
#include <netinet/in.h> |
|
||||
|
|
||||
//#define htonll(x) ((1==htonl(1)) ? (x) : ((uint64_t)htonl((x) & 0xFFFFFFFF) << 32) | htonl((x) >> 32))
|
|
||||
//#define ntohll(x) ((1==ntohl(1)) ? (x) : ((uint64_t)ntohl((x) & 0xFFFFFFFF) << 32) | ntohl((x) >> 32))
|
|
||||
|
|
||||
static uint16_t from64to16(uint64_t x) |
|
||||
{ |
|
||||
uint32_t u = (uint32_t)(uint16_t)x + (uint16_t)(x>>16) + (uint16_t)(x>>32) + (uint16_t)(x>>48); |
|
||||
return (uint16_t)u + (uint16_t)(u>>16); |
|
||||
} |
|
||||
|
|
||||
// this function preserves data alignment requirements (otherwise it will be damn slow on mips arch)
|
|
||||
// and uses 64-bit arithmetics to improve speed
|
|
||||
// taken from linux source code
|
|
||||
static uint16_t do_csum(const uint8_t * buff, size_t len) |
|
||||
{ |
|
||||
uint8_t odd; |
|
||||
size_t count; |
|
||||
uint64_t result,w,carry=0; |
|
||||
uint16_t u16; |
|
||||
|
|
||||
if (!len) return 0; |
|
||||
odd = (uint8_t)(1 & (size_t)buff); |
|
||||
if (odd) |
|
||||
{ |
|
||||
// any endian compatible
|
|
||||
u16 = 0; |
|
||||
*((uint8_t*)&u16+1) = *buff; |
|
||||
result = u16; |
|
||||
len--; |
|
||||
buff++; |
|
||||
} |
|
||||
else |
|
||||
result = 0; |
|
||||
count = len >> 1; /* nr of 16-bit words.. */ |
|
||||
if (count) |
|
||||
{ |
|
||||
if (2 & (size_t) buff) |
|
||||
{ |
|
||||
result += *(uint16_t *) buff; |
|
||||
count--; |
|
||||
len -= 2; |
|
||||
buff += 2; |
|
||||
} |
|
||||
count >>= 1; /* nr of 32-bit words.. */ |
|
||||
if (count) |
|
||||
{ |
|
||||
if (4 & (size_t) buff) |
|
||||
{ |
|
||||
result += *(uint32_t *) buff; |
|
||||
count--; |
|
||||
len -= 4; |
|
||||
buff += 4; |
|
||||
} |
|
||||
count >>= 1; /* nr of 64-bit words.. */ |
|
||||
if (count) |
|
||||
{ |
|
||||
do |
|
||||
{ |
|
||||
w = *(uint64_t *) buff; |
|
||||
count--; |
|
||||
buff += 8; |
|
||||
result += carry; |
|
||||
result += w; |
|
||||
carry = (w > result); |
|
||||
} while (count); |
|
||||
result += carry; |
|
||||
result = (result & 0xffffffff) + (result >> 32); |
|
||||
} |
|
||||
if (len & 4) |
|
||||
{ |
|
||||
result += *(uint32_t *) buff; |
|
||||
buff += 4; |
|
||||
} |
|
||||
} |
|
||||
if (len & 2) |
|
||||
{ |
|
||||
result += *(uint16_t *) buff; |
|
||||
buff += 2; |
|
||||
} |
|
||||
} |
|
||||
if (len & 1) |
|
||||
{ |
|
||||
// any endian compatible
|
|
||||
u16 = 0; |
|
||||
*(uint8_t*)&u16 = *buff; |
|
||||
result += u16; |
|
||||
} |
|
||||
u16 = from64to16(result); |
|
||||
if (odd) u16 = ((u16 >> 8) & 0xff) | ((u16 & 0xff) << 8); |
|
||||
return u16; |
|
||||
} |
|
||||
|
|
||||
uint16_t csum_partial(const void *buff, size_t len) |
|
||||
{ |
|
||||
return do_csum(buff,len); |
|
||||
} |
|
||||
|
|
||||
uint16_t csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, size_t len, uint8_t proto, uint16_t sum) |
|
||||
{ |
|
||||
return ~from64to16((uint64_t)saddr + daddr + sum + htonl(len+proto)); |
|
||||
} |
|
||||
|
|
||||
uint16_t ip4_compute_csum(const void *buff, size_t len) |
|
||||
{ |
|
||||
return ~from64to16(do_csum(buff,len)); |
|
||||
} |
|
||||
void ip4_fix_checksum(struct ip *ip) |
|
||||
{ |
|
||||
ip->ip_sum = 0; |
|
||||
ip->ip_sum = ip4_compute_csum(ip, ip->ip_hl<<2); |
|
||||
} |
|
||||
|
|
||||
uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8_t proto, uint16_t sum) |
|
||||
{ |
|
||||
uint64_t a = (uint64_t)sum + htonl(len+proto) + |
|
||||
*(uint32_t*)saddr + *((uint32_t*)saddr+1) + *((uint32_t*)saddr+2) + *((uint32_t*)saddr+3) + |
|
||||
*(uint32_t*)daddr + *((uint32_t*)daddr+1) + *((uint32_t*)daddr+2) + *((uint32_t*)daddr+3); |
|
||||
return ~from64to16(a); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void tcp4_fix_checksum(struct tcphdr *tcp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr) |
|
||||
{ |
|
||||
tcp->th_sum = 0; |
|
||||
tcp->th_sum = csum_tcpudp_magic(src_addr->s_addr,dest_addr->s_addr,len,IPPROTO_TCP,csum_partial(tcp,len)); |
|
||||
} |
|
||||
void tcp6_fix_checksum(struct tcphdr *tcp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr) |
|
||||
{ |
|
||||
tcp->th_sum = 0; |
|
||||
tcp->th_sum = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_TCP,csum_partial(tcp,len)); |
|
||||
} |
|
||||
void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr) |
|
||||
{ |
|
||||
if (ip) |
|
||||
tcp4_fix_checksum(tcp, len, &ip->ip_src, &ip->ip_dst); |
|
||||
else if (ip6hdr) |
|
||||
tcp6_fix_checksum(tcp, len, &ip6hdr->ip6_src, &ip6hdr->ip6_dst); |
|
||||
} |
|
||||
|
|
||||
void udp4_fix_checksum(struct udphdr *udp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr) |
|
||||
{ |
|
||||
udp->uh_sum = 0; |
|
||||
udp->uh_sum = csum_tcpudp_magic(src_addr->s_addr,dest_addr->s_addr,len,IPPROTO_UDP,csum_partial(udp,len)); |
|
||||
} |
|
||||
void udp6_fix_checksum(struct udphdr *udp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr) |
|
||||
{ |
|
||||
udp->uh_sum = 0; |
|
||||
udp->uh_sum = csum_ipv6_magic(src_addr,dest_addr,len,IPPROTO_UDP,csum_partial(udp,len)); |
|
||||
} |
|
||||
void udp_fix_checksum(struct udphdr *udp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr) |
|
||||
{ |
|
||||
if (ip) |
|
||||
udp4_fix_checksum(udp, len, &ip->ip_src, &ip->ip_dst); |
|
||||
else if (ip6hdr) |
|
||||
udp6_fix_checksum(udp, len, &ip6hdr->ip6_src, &ip6hdr->ip6_dst); |
|
||||
} |
|
||||
@ -1,27 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <stddef.h> |
|
||||
#include <stdint.h> |
|
||||
#include <sys/types.h> |
|
||||
#include <netinet/in.h> |
|
||||
|
|
||||
#define __FAVOR_BSD |
|
||||
#include <netinet/ip6.h> |
|
||||
#include <netinet/ip.h> |
|
||||
#include <netinet/tcp.h> |
|
||||
#include <netinet/udp.h> |
|
||||
|
|
||||
uint16_t csum_partial(const void *buff, size_t len); |
|
||||
uint16_t csum_tcpudp_magic(uint32_t saddr, uint32_t daddr, size_t len, uint8_t proto, uint16_t sum); |
|
||||
uint16_t csum_ipv6_magic(const void *saddr, const void *daddr, size_t len, uint8_t proto, uint16_t sum); |
|
||||
|
|
||||
uint16_t ip4_compute_csum(const void *buff, size_t len); |
|
||||
void ip4_fix_checksum(struct ip *ip); |
|
||||
|
|
||||
void tcp4_fix_checksum(struct tcphdr *tcp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr); |
|
||||
void tcp6_fix_checksum(struct tcphdr *tcp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr); |
|
||||
void tcp_fix_checksum(struct tcphdr *tcp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr); |
|
||||
|
|
||||
void udp4_fix_checksum(struct udphdr *udp,size_t len, const struct in_addr *src_addr, const struct in_addr *dest_addr); |
|
||||
void udp6_fix_checksum(struct udphdr *udp,size_t len, const struct in6_addr *src_addr, const struct in6_addr *dest_addr); |
|
||||
void udp_fix_checksum(struct udphdr *udp,size_t len,const struct ip *ip,const struct ip6_hdr *ip6hdr); |
|
||||
@ -1,391 +0,0 @@ |
|||||
#include "conntrack.h" |
|
||||
#include "darkmagic.h" |
|
||||
#include <arpa/inet.h> |
|
||||
#include <stdio.h> |
|
||||
|
|
||||
#undef uthash_nonfatal_oom |
|
||||
#define uthash_nonfatal_oom(elt) ut_oom_recover(elt) |
|
||||
|
|
||||
static bool oom = false; |
|
||||
static void ut_oom_recover(void *elem) |
|
||||
{ |
|
||||
oom = true; |
|
||||
} |
|
||||
|
|
||||
static const char *connstate_s[]={"SYN","ESTABLISHED","FIN"}; |
|
||||
|
|
||||
static void connswap(const t_conn *c, t_conn *c2) |
|
||||
{ |
|
||||
memset(c2,0,sizeof(*c2)); |
|
||||
c2->l3proto = c->l3proto; |
|
||||
c2->l4proto = c->l4proto; |
|
||||
c2->src = c->dst; |
|
||||
c2->dst = c->src; |
|
||||
c2->sport = c->dport; |
|
||||
c2->dport = c->sport; |
|
||||
} |
|
||||
|
|
||||
void ConntrackClearHostname(t_ctrack *track) |
|
||||
{ |
|
||||
free(track->hostname); |
|
||||
track->hostname = NULL; |
|
||||
track->hostname_is_ip = false; |
|
||||
} |
|
||||
static void ConntrackClearTrack(t_ctrack *track) |
|
||||
{ |
|
||||
ConntrackClearHostname(track); |
|
||||
ReasmClear(&track->reasm_orig); |
|
||||
rawpacket_queue_destroy(&track->delayed); |
|
||||
} |
|
||||
|
|
||||
static void ConntrackFreeElem(t_conntrack_pool *elem) |
|
||||
{ |
|
||||
ConntrackClearTrack(&elem->track); |
|
||||
free(elem); |
|
||||
} |
|
||||
|
|
||||
static void ConntrackPoolDestroyPool(t_conntrack_pool **pp) |
|
||||
{ |
|
||||
t_conntrack_pool *elem, *tmp; |
|
||||
HASH_ITER(hh, *pp, elem, tmp) { HASH_DEL(*pp, elem); ConntrackFreeElem(elem); } |
|
||||
} |
|
||||
void ConntrackPoolDestroy(t_conntrack *p) |
|
||||
{ |
|
||||
ConntrackPoolDestroyPool(&p->pool); |
|
||||
} |
|
||||
|
|
||||
void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin, uint32_t timeout_udp) |
|
||||
{ |
|
||||
p->timeout_syn = timeout_syn; |
|
||||
p->timeout_established = timeout_established; |
|
||||
p->timeout_fin = timeout_fin; |
|
||||
p->timeout_udp= timeout_udp; |
|
||||
p->t_purge_interval = purge_interval; |
|
||||
time(&p->t_last_purge); |
|
||||
p->pool = NULL; |
|
||||
} |
|
||||
|
|
||||
void ConntrackExtractConn(t_conn *c, bool bReverse, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr) |
|
||||
{ |
|
||||
memset(c,0,sizeof(*c)); |
|
||||
if (ip) |
|
||||
{ |
|
||||
c->l3proto = IPPROTO_IP; |
|
||||
c->dst.ip = bReverse ? ip->ip_src : ip->ip_dst; |
|
||||
c->src.ip = bReverse ? ip->ip_dst : ip->ip_src; |
|
||||
} |
|
||||
else if (ip6) |
|
||||
{ |
|
||||
c->l3proto = IPPROTO_IPV6; |
|
||||
c->dst.ip6 = bReverse ? ip6->ip6_src : ip6->ip6_dst; |
|
||||
c->src.ip6 = bReverse ? ip6->ip6_dst : ip6->ip6_src; |
|
||||
} |
|
||||
else |
|
||||
c->l3proto = -1; |
|
||||
extract_ports(tcphdr, udphdr, &c->l4proto, bReverse ? &c->dport : &c->sport, bReverse ? &c->sport : &c->dport); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static t_conntrack_pool *ConntrackPoolSearch(t_conntrack_pool *p, const t_conn *c) |
|
||||
{ |
|
||||
t_conntrack_pool *t; |
|
||||
HASH_FIND(hh, p, c, sizeof(*c), t); |
|
||||
return t; |
|
||||
} |
|
||||
|
|
||||
static void ConntrackInitTrack(t_ctrack *t) |
|
||||
{ |
|
||||
memset(t,0,sizeof(*t)); |
|
||||
t->scale_orig = t->scale_reply = SCALE_NONE; |
|
||||
time(&t->t_start); |
|
||||
rawpacket_queue_init(&t->delayed); |
|
||||
} |
|
||||
static void ConntrackReInitTrack(t_ctrack *t) |
|
||||
{ |
|
||||
ConntrackClearTrack(t); |
|
||||
ConntrackInitTrack(t); |
|
||||
} |
|
||||
|
|
||||
static t_conntrack_pool *ConntrackNew(t_conntrack_pool **pp, const t_conn *c) |
|
||||
{ |
|
||||
t_conntrack_pool *ctnew; |
|
||||
if (!(ctnew = malloc(sizeof(*ctnew)))) return NULL; |
|
||||
ctnew->conn = *c; |
|
||||
oom = false; |
|
||||
HASH_ADD(hh, *pp, conn, sizeof(*c), ctnew); |
|
||||
if (oom) { free(ctnew); return NULL; } |
|
||||
ConntrackInitTrack(&ctnew->track); |
|
||||
return ctnew; |
|
||||
} |
|
||||
|
|
||||
// non-tcp packets are passed with tcphdr=NULL but len_payload filled
|
|
||||
static void ConntrackFeedPacket(t_ctrack *t, bool bReverse, const struct tcphdr *tcphdr, uint32_t len_payload) |
|
||||
{ |
|
||||
uint8_t scale; |
|
||||
|
|
||||
if (bReverse) |
|
||||
{ |
|
||||
t->pcounter_reply++; |
|
||||
t->pdcounter_reply+=!!len_payload; |
|
||||
|
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
t->pcounter_orig++; |
|
||||
t->pdcounter_orig+=!!len_payload; |
|
||||
} |
|
||||
|
|
||||
if (tcphdr) |
|
||||
{ |
|
||||
if (tcp_syn_segment(tcphdr)) |
|
||||
{ |
|
||||
if (t->state!=SYN) ConntrackReInitTrack(t); // erase current entry
|
|
||||
t->seq0 = ntohl(tcphdr->th_seq); |
|
||||
} |
|
||||
else if (tcp_synack_segment(tcphdr)) |
|
||||
{ |
|
||||
// ignore SA dups
|
|
||||
uint32_t seq0 = ntohl(tcphdr->th_ack)-1; |
|
||||
if (t->state!=SYN && t->seq0!=seq0) |
|
||||
ConntrackReInitTrack(t); // erase current entry
|
|
||||
if (!t->seq0) t->seq0 = seq0; |
|
||||
t->ack0 = ntohl(tcphdr->th_seq); |
|
||||
} |
|
||||
else if (tcphdr->th_flags & (TH_FIN|TH_RST)) |
|
||||
{ |
|
||||
t->state = FIN; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (t->state==SYN) |
|
||||
{ |
|
||||
t->state=ESTABLISHED; |
|
||||
if (!bReverse && !t->ack0) t->ack0 = ntohl(tcphdr->th_ack)-1; |
|
||||
} |
|
||||
} |
|
||||
scale = tcp_find_scale_factor(tcphdr); |
|
||||
if (bReverse) |
|
||||
{ |
|
||||
t->pos_orig = t->seq_last = ntohl(tcphdr->th_ack); |
|
||||
t->ack_last = ntohl(tcphdr->th_seq); |
|
||||
t->pos_reply = t->ack_last + len_payload; |
|
||||
t->winsize_reply = ntohs(tcphdr->th_win); |
|
||||
if (scale!=SCALE_NONE) t->scale_reply = scale; |
|
||||
|
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
t->seq_last = ntohl(tcphdr->th_seq); |
|
||||
t->pos_orig = t->seq_last + len_payload; |
|
||||
t->pos_reply = t->ack_last = ntohl(tcphdr->th_ack); |
|
||||
t->winsize_orig = ntohs(tcphdr->th_win); |
|
||||
if (scale!=SCALE_NONE) t->scale_orig = scale; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (bReverse) |
|
||||
{ |
|
||||
t->ack_last=t->pos_reply; |
|
||||
t->pos_reply+=len_payload; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
t->seq_last=t->pos_orig; |
|
||||
t->pos_orig+=len_payload; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
time(&t->t_last); |
|
||||
} |
|
||||
|
|
||||
static bool ConntrackPoolDoubleSearchPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse) |
|
||||
{ |
|
||||
t_conn conn,connswp; |
|
||||
t_conntrack_pool *ctr; |
|
||||
|
|
||||
ConntrackExtractConn(&conn,false,ip,ip6,tcphdr,udphdr); |
|
||||
if ((ctr=ConntrackPoolSearch(*pp,&conn))) |
|
||||
{ |
|
||||
if (bReverse) *bReverse = false; |
|
||||
if (ctrack) *ctrack = &ctr->track; |
|
||||
return true; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
connswap(&conn,&connswp); |
|
||||
if ((ctr=ConntrackPoolSearch(*pp,&connswp))) |
|
||||
{ |
|
||||
if (bReverse) *bReverse = true; |
|
||||
if (ctrack) *ctrack = &ctr->track; |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
bool ConntrackPoolDoubleSearch(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse) |
|
||||
{ |
|
||||
return ConntrackPoolDoubleSearchPool(&p->pool, ip, ip6, tcphdr, udphdr, ctrack, bReverse); |
|
||||
} |
|
||||
|
|
||||
static bool ConntrackPoolFeedPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse) |
|
||||
{ |
|
||||
t_conn conn, connswp; |
|
||||
t_conntrack_pool *ctr; |
|
||||
bool b_rev; |
|
||||
|
|
||||
ConntrackExtractConn(&conn,false,ip,ip6,tcphdr,udphdr); |
|
||||
if ((ctr=ConntrackPoolSearch(*pp,&conn))) |
|
||||
{ |
|
||||
ConntrackFeedPacket(&ctr->track, (b_rev=false), tcphdr, len_payload); |
|
||||
goto ok; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
connswap(&conn,&connswp); |
|
||||
if ((ctr=ConntrackPoolSearch(*pp,&connswp))) |
|
||||
{ |
|
||||
ConntrackFeedPacket(&ctr->track, (b_rev=true), tcphdr, len_payload); |
|
||||
goto ok; |
|
||||
} |
|
||||
} |
|
||||
b_rev = tcphdr && tcp_synack_segment(tcphdr); |
|
||||
if ((tcphdr && tcp_syn_segment(tcphdr)) || b_rev || udphdr) |
|
||||
{ |
|
||||
if ((ctr=ConntrackNew(pp, b_rev ? &connswp : &conn))) |
|
||||
{ |
|
||||
ConntrackFeedPacket(&ctr->track, b_rev, tcphdr, len_payload); |
|
||||
goto ok; |
|
||||
} |
|
||||
} |
|
||||
return false; |
|
||||
ok: |
|
||||
if (ctrack) *ctrack = &ctr->track; |
|
||||
if (bReverse) *bReverse = b_rev; |
|
||||
return true; |
|
||||
} |
|
||||
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse) |
|
||||
{ |
|
||||
return ConntrackPoolFeedPool(&p->pool,ip,ip6,tcphdr,udphdr,len_payload,ctrack,bReverse); |
|
||||
} |
|
||||
|
|
||||
static bool ConntrackPoolDropPool(t_conntrack_pool **pp, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr) |
|
||||
{ |
|
||||
t_conn conn, connswp; |
|
||||
t_conntrack_pool *t; |
|
||||
ConntrackExtractConn(&conn,false,ip,ip6,tcphdr,udphdr); |
|
||||
if (!(t=ConntrackPoolSearch(*pp,&conn))) |
|
||||
{ |
|
||||
connswap(&conn,&connswp); |
|
||||
t=ConntrackPoolSearch(*pp,&connswp); |
|
||||
} |
|
||||
if (!t) return false; |
|
||||
HASH_DEL(*pp, t); ConntrackFreeElem(t); |
|
||||
return true; |
|
||||
} |
|
||||
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr) |
|
||||
{ |
|
||||
return ConntrackPoolDropPool(&p->pool,ip,ip6,tcphdr,udphdr); |
|
||||
} |
|
||||
|
|
||||
void ConntrackPoolPurge(t_conntrack *p) |
|
||||
{ |
|
||||
time_t tidle, tnow = time(NULL); |
|
||||
t_conntrack_pool *t, *tmp; |
|
||||
|
|
||||
if ((tnow - p->t_last_purge)>=p->t_purge_interval) |
|
||||
{ |
|
||||
HASH_ITER(hh, p->pool , t, tmp) { |
|
||||
tidle = tnow - t->track.t_last; |
|
||||
if ( t->track.b_cutoff || |
|
||||
(t->conn.l4proto==IPPROTO_TCP && ( |
|
||||
(t->track.state==SYN && tidle>=p->timeout_syn) || |
|
||||
(t->track.state==ESTABLISHED && tidle>=p->timeout_established) || |
|
||||
(t->track.state==FIN && tidle>=p->timeout_fin)) |
|
||||
) || (t->conn.l4proto==IPPROTO_UDP && tidle>=p->timeout_udp) |
|
||||
) |
|
||||
{ |
|
||||
HASH_DEL(p->pool, t); ConntrackFreeElem(t); |
|
||||
} |
|
||||
} |
|
||||
p->t_last_purge = tnow; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
static void taddr2str(uint8_t l3proto, const t_addr *a, char *buf, size_t bufsize) |
|
||||
{ |
|
||||
if (!inet_ntop(family_from_proto(l3proto), a, buf, bufsize) && bufsize) *buf=0; |
|
||||
} |
|
||||
|
|
||||
void ConntrackPoolDump(const t_conntrack *p) |
|
||||
{ |
|
||||
t_conntrack_pool *t, *tmp; |
|
||||
char sa1[40],sa2[40]; |
|
||||
time_t tnow = time(NULL); |
|
||||
HASH_ITER(hh, p->pool, t, tmp) { |
|
||||
taddr2str(t->conn.l3proto, &t->conn.src, sa1, sizeof(sa1)); |
|
||||
taddr2str(t->conn.l3proto, &t->conn.dst, sa2, sizeof(sa2)); |
|
||||
printf("%s [%s]:%u => [%s]:%u : %s : t0=%llu last=t0+%llu now=last+%llu packets_orig=d%llu/n%llu packets_reply=d%llu/n%llu ", |
|
||||
proto_name(t->conn.l4proto), |
|
||||
sa1, t->conn.sport, sa2, t->conn.dport, |
|
||||
t->conn.l4proto==IPPROTO_TCP ? connstate_s[t->track.state] : "-", |
|
||||
(unsigned long long)t->track.t_start, (unsigned long long)(t->track.t_last - t->track.t_start), (unsigned long long)(tnow - t->track.t_last), |
|
||||
(unsigned long long)t->track.pdcounter_orig, (unsigned long long)t->track.pcounter_orig, |
|
||||
(unsigned long long)t->track.pdcounter_reply, (unsigned long long)t->track.pcounter_reply); |
|
||||
if (t->conn.l4proto==IPPROTO_TCP) |
|
||||
printf("seq0=%u rseq=%u pos_orig=%u ack0=%u rack=%u pos_reply=%u wsize_orig=%u:%d wsize_reply=%u:%d", |
|
||||
t->track.seq0, t->track.seq_last - t->track.seq0, t->track.pos_orig - t->track.seq0, |
|
||||
t->track.ack0, t->track.ack_last - t->track.ack0, t->track.pos_reply - t->track.ack0, |
|
||||
t->track.winsize_orig, t->track.scale_orig==SCALE_NONE ? -1 : t->track.scale_orig, |
|
||||
t->track.winsize_reply, t->track.scale_reply==SCALE_NONE ? -1 : t->track.scale_reply); |
|
||||
else |
|
||||
printf("rseq=%u pos_orig=%u rack=%u pos_reply=%u", |
|
||||
t->track.seq_last, t->track.pos_orig, |
|
||||
t->track.ack_last, t->track.pos_reply); |
|
||||
printf(" req_retrans=%u cutoff=%u wss_cutoff=%u desync_cutoff=%u dup_cutoff=%u orig_cutoff=%u hostname=%s l7proto=%s\n", |
|
||||
t->track.req_retrans_counter, t->track.b_cutoff, t->track.b_wssize_cutoff, t->track.b_desync_cutoff, t->track.b_dup_cutoff, t->track.b_orig_mod_cutoff, t->track.hostname, l7proto_str(t->track.l7proto)); |
|
||||
}; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void ReasmClear(t_reassemble *reasm) |
|
||||
{ |
|
||||
free(reasm->packet); |
|
||||
reasm->packet = NULL; |
|
||||
reasm->size = reasm->size_present = 0; |
|
||||
} |
|
||||
bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start) |
|
||||
{ |
|
||||
reasm->packet = malloc(size_requested); |
|
||||
if (!reasm->packet) return false; |
|
||||
reasm->size = size_requested; |
|
||||
reasm->size_present = 0; |
|
||||
reasm->seq = seq_start; |
|
||||
return true; |
|
||||
} |
|
||||
bool ReasmResize(t_reassemble *reasm, size_t new_size) |
|
||||
{ |
|
||||
uint8_t *p = realloc(reasm->packet, new_size); |
|
||||
if (!p) return false; |
|
||||
reasm->packet = p; |
|
||||
reasm->size = new_size; |
|
||||
if (reasm->size_present > new_size) reasm->size_present = new_size; |
|
||||
return true; |
|
||||
} |
|
||||
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len) |
|
||||
{ |
|
||||
if (reasm->seq!=seq) return false; // fail session if out of sequence
|
|
||||
|
|
||||
size_t szcopy; |
|
||||
szcopy = reasm->size - reasm->size_present; |
|
||||
if (len<szcopy) szcopy = len; |
|
||||
memcpy(reasm->packet + reasm->size_present, payload, szcopy); |
|
||||
reasm->size_present += szcopy; |
|
||||
reasm->seq += (uint32_t)szcopy; |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
bool ReasmHasSpace(t_reassemble *reasm, size_t len) |
|
||||
{ |
|
||||
return (reasm->size_present+len)<=reasm->size; |
|
||||
} |
|
||||
@ -1,132 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
|
|
||||
// this conntrack is not bullet-proof
|
|
||||
// its designed to satisfy dpi desync needs only
|
|
||||
|
|
||||
#include <stdbool.h> |
|
||||
#include <stdint.h> |
|
||||
#include <ctype.h> |
|
||||
#include <sys/types.h> |
|
||||
#include <time.h> |
|
||||
#include <netinet/in.h> |
|
||||
|
|
||||
#define __FAVOR_BSD |
|
||||
#include <netinet/ip.h> |
|
||||
#include <netinet/ip6.h> |
|
||||
#include <netinet/tcp.h> |
|
||||
#include <netinet/udp.h> |
|
||||
|
|
||||
#include "packet_queue.h" |
|
||||
#include "protocol.h" |
|
||||
|
|
||||
//#define HASH_BLOOM 20
|
|
||||
#define HASH_NONFATAL_OOM 1 |
|
||||
#undef HASH_FUNCTION |
|
||||
#define HASH_FUNCTION HASH_BER |
|
||||
#include "uthash.h" |
|
||||
|
|
||||
#define RETRANS_COUNTER_STOP ((uint8_t)-1) |
|
||||
|
|
||||
typedef union { |
|
||||
struct in_addr ip; |
|
||||
struct in6_addr ip6; |
|
||||
} t_addr; |
|
||||
typedef struct |
|
||||
{ |
|
||||
t_addr src, dst; |
|
||||
uint16_t sport,dport; |
|
||||
uint8_t l3proto; // IPPROTO_IP, IPPROTO_IPV6
|
|
||||
uint8_t l4proto; // IPPROTO_TCP, IPPROTO_UDP
|
|
||||
} t_conn; |
|
||||
|
|
||||
// this structure helps to reassemble continuous packets streams. it does not support out-of-orders
|
|
||||
typedef struct { |
|
||||
uint8_t *packet; // allocated for size during reassemble request. requestor must know the message size.
|
|
||||
uint32_t seq; // current seq number. if a packet comes with an unexpected seq - it fails reassemble session.
|
|
||||
size_t size; // expected message size. success means that we have received exactly 'size' bytes and have them in 'packet'
|
|
||||
size_t size_present; // how many bytes already stored in 'packet'
|
|
||||
} t_reassemble; |
|
||||
|
|
||||
// SYN - SYN or SYN/ACK received
|
|
||||
// ESTABLISHED - any except SYN or SYN/ACK received
|
|
||||
// FIN - FIN or RST received
|
|
||||
typedef enum {SYN=0, ESTABLISHED, FIN} t_connstate; |
|
||||
|
|
||||
typedef struct |
|
||||
{ |
|
||||
bool bCheckDone, bCheckResult, bCheckExcluded; // hostlist check result cache
|
|
||||
|
|
||||
struct desync_profile *dp; // desync profile cache
|
|
||||
bool dp_search_complete; |
|
||||
|
|
||||
// common state
|
|
||||
time_t t_start, t_last; |
|
||||
uint64_t pcounter_orig, pcounter_reply; // packet counter
|
|
||||
uint64_t pdcounter_orig, pdcounter_reply; // data packet counter (with payload)
|
|
||||
uint32_t pos_orig, pos_reply; // TCP: seq_last+payload, ack_last+payload UDP: sum of all seen payload lenghts including current
|
|
||||
uint32_t seq_last, ack_last; // TCP: last seen seq and ack UDP: sum of all seen payload lenghts NOT including current
|
|
||||
|
|
||||
// tcp only state, not used in udp
|
|
||||
t_connstate state; |
|
||||
uint32_t seq0, ack0; // starting seq and ack
|
|
||||
uint16_t winsize_orig, winsize_reply; // last seen window size
|
|
||||
uint8_t scale_orig, scale_reply; // last seen window scale factor. SCALE_NONE if none
|
|
||||
|
|
||||
uint8_t req_retrans_counter; // number of request retransmissions
|
|
||||
bool req_seq_present,req_seq_finalized,req_seq_abandoned; |
|
||||
uint32_t req_seq_start,req_seq_end; // sequence interval of the request (to track retransmissions)
|
|
||||
|
|
||||
uint8_t incoming_ttl, desync_autottl, orig_autottl, dup_autottl; |
|
||||
bool b_autottl_discovered; |
|
||||
|
|
||||
bool b_cutoff; // mark for deletion
|
|
||||
bool b_wssize_cutoff, b_desync_cutoff, b_dup_cutoff, b_orig_mod_cutoff; |
|
||||
|
|
||||
uint16_t ip_id; |
|
||||
|
|
||||
t_l7proto l7proto; |
|
||||
bool l7proto_discovered; |
|
||||
char *hostname; |
|
||||
bool hostname_is_ip; |
|
||||
bool hostname_discovered; |
|
||||
bool hostname_ah_check; // should perform autohostlist checks
|
|
||||
|
|
||||
t_reassemble reasm_orig; |
|
||||
struct rawpacket_tailhead delayed; |
|
||||
} t_ctrack; |
|
||||
|
|
||||
typedef struct |
|
||||
{ |
|
||||
t_ctrack track; |
|
||||
UT_hash_handle hh; // makes this structure hashable
|
|
||||
t_conn conn; // key
|
|
||||
} t_conntrack_pool; |
|
||||
typedef struct |
|
||||
{ |
|
||||
// inactivity time to purge an entry in each connection state
|
|
||||
uint32_t timeout_syn,timeout_established,timeout_fin,timeout_udp; |
|
||||
time_t t_purge_interval, t_last_purge; |
|
||||
t_conntrack_pool *pool; |
|
||||
} t_conntrack; |
|
||||
|
|
||||
void ConntrackPoolInit(t_conntrack *p, time_t purge_interval, uint32_t timeout_syn, uint32_t timeout_established, uint32_t timeout_fin, uint32_t timeout_udp); |
|
||||
void ConntrackPoolDestroy(t_conntrack *p); |
|
||||
bool ConntrackPoolFeed(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, size_t len_payload, t_ctrack **ctrack, bool *bReverse); |
|
||||
// do not create, do not update. only find existing
|
|
||||
bool ConntrackPoolDoubleSearch(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr, t_ctrack **ctrack, bool *bReverse); |
|
||||
bool ConntrackPoolDrop(t_conntrack *p, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr); |
|
||||
void CaonntrackExtractConn(t_conn *c, bool bReverse, const struct ip *ip, const struct ip6_hdr *ip6, const struct tcphdr *tcphdr, const struct udphdr *udphdr); |
|
||||
void ConntrackPoolDump(const t_conntrack *p); |
|
||||
void ConntrackPoolPurge(t_conntrack *p); |
|
||||
void ConntrackClearHostname(t_ctrack *track); |
|
||||
|
|
||||
bool ReasmInit(t_reassemble *reasm, size_t size_requested, uint32_t seq_start); |
|
||||
bool ReasmResize(t_reassemble *reasm, size_t new_size); |
|
||||
void ReasmClear(t_reassemble *reasm); |
|
||||
// false means reassemble session has failed and we should ReasmClear() it
|
|
||||
bool ReasmFeed(t_reassemble *reasm, uint32_t seq, const void *payload, size_t len); |
|
||||
// check if it has enough space to buffer 'len' bytes
|
|
||||
bool ReasmHasSpace(t_reassemble *reasm, size_t len); |
|
||||
inline static bool ReasmIsEmpty(t_reassemble *reasm) {return !reasm->size;} |
|
||||
inline static bool ReasmIsFull(t_reassemble *reasm) {return !ReasmIsEmpty(reasm) && (reasm->size==reasm->size_present);} |
|
||||
@ -1,15 +0,0 @@ |
|||||
#include "aes-gcm.h" |
|
||||
|
|
||||
int aes_gcm_crypt(int mode, uint8_t *output, const uint8_t *input, size_t input_length, const uint8_t *key, const size_t key_len, const uint8_t *iv, const size_t iv_len, const uint8_t *adata, size_t adata_len, uint8_t *atag, size_t atag_len) |
|
||||
{ |
|
||||
int ret = 0; |
|
||||
gcm_context ctx; |
|
||||
|
|
||||
if (!(ret = gcm_setkey(&ctx, key, (const uint)key_len))) |
|
||||
{ |
|
||||
ret = gcm_crypt_and_tag(&ctx, mode, iv, iv_len, adata, adata_len, input, output, input_length, atag, atag_len); |
|
||||
gcm_zero_ctx(&ctx); |
|
||||
} |
|
||||
|
|
||||
return ret; |
|
||||
} |
|
||||
@ -1,6 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "gcm.h" |
|
||||
|
|
||||
// mode : AES_ENCRYPT, AES_DECRYPT
|
|
||||
int aes_gcm_crypt(int mode, uint8_t *output, const uint8_t *input, size_t input_length, const uint8_t *key, const size_t key_len, const uint8_t *iv, const size_t iv_len, const uint8_t *adata, size_t adata_len, uint8_t *atag, size_t atag_len); |
|
||||
@ -1,483 +0,0 @@ |
|||||
/******************************************************************************
|
|
||||
* |
|
||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL |
|
||||
* |
|
||||
* This is a simple and straightforward implementation of the AES Rijndael |
|
||||
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus |
|
||||
* of this work was correctness & accuracy. It is written in 'C' without any |
|
||||
* particular focus upon optimization or speed. It should be endian (memory |
|
||||
* byte order) neutral since the few places that care are handled explicitly. |
|
||||
* |
|
||||
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com. |
|
||||
* |
|
||||
* It is intended for general purpose use, but was written in support of GRC's |
|
||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client. |
|
||||
* |
|
||||
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
|
|
||||
* |
|
||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE |
|
||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. |
|
||||
* |
|
||||
*******************************************************************************/ |
|
||||
|
|
||||
#include "aes.h" |
|
||||
|
|
||||
static int aes_tables_inited = 0; // run-once flag for performing key
|
|
||||
// expasion table generation (see below)
|
|
||||
/*
|
|
||||
* The following static local tables must be filled-in before the first use of |
|
||||
* the GCM or AES ciphers. They are used for the AES key expansion/scheduling |
|
||||
* and once built are read-only and thread safe. The "gcm_initialize" function |
|
||||
* must be called once during system initialization to populate these arrays |
|
||||
* for subsequent use by the AES key scheduler. If they have not been built |
|
||||
* before attempted use, an error will be returned to the caller. |
|
||||
* |
|
||||
* NOTE: GCM Encryption/Decryption does NOT REQUIRE AES decryption. Since |
|
||||
* GCM uses AES in counter-mode, where the AES cipher output is XORed with |
|
||||
* the GCM input, we ONLY NEED AES encryption. Thus, to save space AES |
|
||||
* decryption is typically disabled by setting AES_DECRYPTION to 0 in aes.h. |
|
||||
*/ |
|
||||
// We always need our forward tables
|
|
||||
static uchar FSb[256]; // Forward substitution box (FSb)
|
|
||||
static uint32_t FT0[256]; // Forward key schedule assembly tables
|
|
||||
static uint32_t FT1[256]; |
|
||||
static uint32_t FT2[256]; |
|
||||
static uint32_t FT3[256]; |
|
||||
|
|
||||
#if AES_DECRYPTION // We ONLY need reverse for decryption
|
|
||||
static uchar RSb[256]; // Reverse substitution box (RSb)
|
|
||||
static uint32_t RT0[256]; // Reverse key schedule assembly tables
|
|
||||
static uint32_t RT1[256]; |
|
||||
static uint32_t RT2[256]; |
|
||||
static uint32_t RT3[256]; |
|
||||
#endif /* AES_DECRYPTION */ |
|
||||
|
|
||||
static uint32_t RCON[10]; // AES round constants
|
|
||||
|
|
||||
/*
|
|
||||
* Platform Endianness Neutralizing Load and Store Macro definitions |
|
||||
* AES wants platform-neutral Little Endian (LE) byte ordering |
|
||||
*/ |
|
||||
#define GET_UINT32_LE(n,b,i) { \ |
|
||||
(n) = ( (uint32_t) (b)[(i) ] ) \ |
|
||||
| ( (uint32_t) (b)[(i) + 1] << 8 ) \ |
|
||||
| ( (uint32_t) (b)[(i) + 2] << 16 ) \ |
|
||||
| ( (uint32_t) (b)[(i) + 3] << 24 ); } |
|
||||
|
|
||||
#define PUT_UINT32_LE(n,b,i) { \ |
|
||||
(b)[(i) ] = (uchar) ( (n) ); \ |
|
||||
(b)[(i) + 1] = (uchar) ( (n) >> 8 ); \ |
|
||||
(b)[(i) + 2] = (uchar) ( (n) >> 16 ); \ |
|
||||
(b)[(i) + 3] = (uchar) ( (n) >> 24 ); } |
|
||||
|
|
||||
/*
|
|
||||
* AES forward and reverse encryption round processing macros |
|
||||
*/ |
|
||||
#define AES_FROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ |
|
||||
{ \ |
|
||||
X0 = *RK++ ^ FT0[ ( Y0 ) & 0xFF ] ^ \ |
|
||||
FT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ |
|
||||
FT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ |
|
||||
FT3[ ( Y3 >> 24 ) & 0xFF ]; \ |
|
||||
\ |
|
||||
X1 = *RK++ ^ FT0[ ( Y1 ) & 0xFF ] ^ \ |
|
||||
FT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ |
|
||||
FT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ |
|
||||
FT3[ ( Y0 >> 24 ) & 0xFF ]; \ |
|
||||
\ |
|
||||
X2 = *RK++ ^ FT0[ ( Y2 ) & 0xFF ] ^ \ |
|
||||
FT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ |
|
||||
FT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ |
|
||||
FT3[ ( Y1 >> 24 ) & 0xFF ]; \ |
|
||||
\ |
|
||||
X3 = *RK++ ^ FT0[ ( Y3 ) & 0xFF ] ^ \ |
|
||||
FT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ |
|
||||
FT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ |
|
||||
FT3[ ( Y2 >> 24 ) & 0xFF ]; \ |
|
||||
} |
|
||||
|
|
||||
#define AES_RROUND(X0,X1,X2,X3,Y0,Y1,Y2,Y3) \ |
|
||||
{ \ |
|
||||
X0 = *RK++ ^ RT0[ ( Y0 ) & 0xFF ] ^ \ |
|
||||
RT1[ ( Y3 >> 8 ) & 0xFF ] ^ \ |
|
||||
RT2[ ( Y2 >> 16 ) & 0xFF ] ^ \ |
|
||||
RT3[ ( Y1 >> 24 ) & 0xFF ]; \ |
|
||||
\ |
|
||||
X1 = *RK++ ^ RT0[ ( Y1 ) & 0xFF ] ^ \ |
|
||||
RT1[ ( Y0 >> 8 ) & 0xFF ] ^ \ |
|
||||
RT2[ ( Y3 >> 16 ) & 0xFF ] ^ \ |
|
||||
RT3[ ( Y2 >> 24 ) & 0xFF ]; \ |
|
||||
\ |
|
||||
X2 = *RK++ ^ RT0[ ( Y2 ) & 0xFF ] ^ \ |
|
||||
RT1[ ( Y1 >> 8 ) & 0xFF ] ^ \ |
|
||||
RT2[ ( Y0 >> 16 ) & 0xFF ] ^ \ |
|
||||
RT3[ ( Y3 >> 24 ) & 0xFF ]; \ |
|
||||
\ |
|
||||
X3 = *RK++ ^ RT0[ ( Y3 ) & 0xFF ] ^ \ |
|
||||
RT1[ ( Y2 >> 8 ) & 0xFF ] ^ \ |
|
||||
RT2[ ( Y1 >> 16 ) & 0xFF ] ^ \ |
|
||||
RT3[ ( Y0 >> 24 ) & 0xFF ]; \ |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* These macros improve the readability of the key |
|
||||
* generation initialization code by collapsing |
|
||||
* repetitive common operations into logical pieces. |
|
||||
*/ |
|
||||
#define ROTL8(x) ( ( x << 8 ) & 0xFFFFFFFF ) | ( x >> 24 ) |
|
||||
#define XTIME(x) ( ( x << 1 ) ^ ( ( x & 0x80 ) ? 0x1B : 0x00 ) ) |
|
||||
#define MUL(x,y) ( ( x && y ) ? pow[(log[x]+log[y]) % 255] : 0 ) |
|
||||
#define MIX(x,y) { y = ( (y << 1) | (y >> 7) ) & 0xFF; x ^= y; } |
|
||||
#define CPY128 { *RK++ = *SK++; *RK++ = *SK++; \ |
|
||||
*RK++ = *SK++; *RK++ = *SK++; } |
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* AES_INIT_KEYGEN_TABLES |
|
||||
* |
|
||||
* Fills the AES key expansion tables allocated above with their static |
|
||||
* data. This is not "per key" data, but static system-wide read-only |
|
||||
* table data. THIS FUNCTION IS NOT THREAD SAFE. It must be called once |
|
||||
* at system initialization to setup the tables for all subsequent use. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
void aes_init_keygen_tables(void) |
|
||||
{ |
|
||||
int i, x, y, z; // general purpose iteration and computation locals
|
|
||||
int pow[256]; |
|
||||
int log[256]; |
|
||||
|
|
||||
if (aes_tables_inited) return; |
|
||||
|
|
||||
// fill the 'pow' and 'log' tables over GF(2^8)
|
|
||||
for (i = 0, x = 1; i < 256; i++) { |
|
||||
pow[i] = x; |
|
||||
log[x] = i; |
|
||||
x = (x ^ XTIME(x)) & 0xFF; |
|
||||
} |
|
||||
// compute the round constants
|
|
||||
for (i = 0, x = 1; i < 10; i++) { |
|
||||
RCON[i] = (uint32_t)x; |
|
||||
x = XTIME(x) & 0xFF; |
|
||||
} |
|
||||
// fill the forward and reverse substitution boxes
|
|
||||
FSb[0x00] = 0x63; |
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||
RSb[0x63] = 0x00; |
|
||||
#endif /* AES_DECRYPTION */ |
|
||||
|
|
||||
for (i = 1; i < 256; i++) { |
|
||||
x = y = pow[255 - log[i]]; |
|
||||
MIX(x, y); |
|
||||
MIX(x, y); |
|
||||
MIX(x, y); |
|
||||
MIX(x, y); |
|
||||
FSb[i] = (uchar)(x ^= 0x63); |
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||
RSb[x] = (uchar)i; |
|
||||
#endif /* AES_DECRYPTION */ |
|
||||
|
|
||||
} |
|
||||
// generate the forward and reverse key expansion tables
|
|
||||
for (i = 0; i < 256; i++) { |
|
||||
x = FSb[i]; |
|
||||
y = XTIME(x) & 0xFF; |
|
||||
z = (y ^ x) & 0xFF; |
|
||||
|
|
||||
FT0[i] = ((uint32_t)y) ^ ((uint32_t)x << 8) ^ |
|
||||
((uint32_t)x << 16) ^ ((uint32_t)z << 24); |
|
||||
|
|
||||
FT1[i] = ROTL8(FT0[i]); |
|
||||
FT2[i] = ROTL8(FT1[i]); |
|
||||
FT3[i] = ROTL8(FT2[i]); |
|
||||
|
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||
x = RSb[i]; |
|
||||
|
|
||||
RT0[i] = ((uint32_t)MUL(0x0E, x)) ^ |
|
||||
((uint32_t)MUL(0x09, x) << 8) ^ |
|
||||
((uint32_t)MUL(0x0D, x) << 16) ^ |
|
||||
((uint32_t)MUL(0x0B, x) << 24); |
|
||||
|
|
||||
RT1[i] = ROTL8(RT0[i]); |
|
||||
RT2[i] = ROTL8(RT1[i]); |
|
||||
RT3[i] = ROTL8(RT2[i]); |
|
||||
#endif /* AES_DECRYPTION */ |
|
||||
} |
|
||||
aes_tables_inited = 1; // flag that the tables have been generated
|
|
||||
} // to permit subsequent use of the AES cipher
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* AES_SET_ENCRYPTION_KEY |
|
||||
* |
|
||||
* This is called by 'aes_setkey' when we're establishing a key for |
|
||||
* subsequent encryption. We give it a pointer to the encryption |
|
||||
* context, a pointer to the key, and the key's length in bytes. |
|
||||
* Valid lengths are: 16, 24 or 32 bytes (128, 192, 256 bits). |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int aes_set_encryption_key(aes_context *ctx, |
|
||||
const uchar *key, |
|
||||
uint keysize) |
|
||||
{ |
|
||||
uint i; // general purpose iteration local
|
|
||||
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
|
|
||||
|
|
||||
for (i = 0; i < (keysize >> 2); i++) { |
|
||||
GET_UINT32_LE(RK[i], key, i << 2); |
|
||||
} |
|
||||
|
|
||||
switch (ctx->rounds) |
|
||||
{ |
|
||||
case 10: |
|
||||
for (i = 0; i < 10; i++, RK += 4) { |
|
||||
RK[4] = RK[0] ^ RCON[i] ^ |
|
||||
((uint32_t)FSb[(RK[3] >> 8) & 0xFF]) ^ |
|
||||
((uint32_t)FSb[(RK[3] >> 16) & 0xFF] << 8) ^ |
|
||||
((uint32_t)FSb[(RK[3] >> 24) & 0xFF] << 16) ^ |
|
||||
((uint32_t)FSb[(RK[3]) & 0xFF] << 24); |
|
||||
|
|
||||
RK[5] = RK[1] ^ RK[4]; |
|
||||
RK[6] = RK[2] ^ RK[5]; |
|
||||
RK[7] = RK[3] ^ RK[6]; |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case 12: |
|
||||
for (i = 0; i < 8; i++, RK += 6) { |
|
||||
RK[6] = RK[0] ^ RCON[i] ^ |
|
||||
((uint32_t)FSb[(RK[5] >> 8) & 0xFF]) ^ |
|
||||
((uint32_t)FSb[(RK[5] >> 16) & 0xFF] << 8) ^ |
|
||||
((uint32_t)FSb[(RK[5] >> 24) & 0xFF] << 16) ^ |
|
||||
((uint32_t)FSb[(RK[5]) & 0xFF] << 24); |
|
||||
|
|
||||
RK[7] = RK[1] ^ RK[6]; |
|
||||
RK[8] = RK[2] ^ RK[7]; |
|
||||
RK[9] = RK[3] ^ RK[8]; |
|
||||
RK[10] = RK[4] ^ RK[9]; |
|
||||
RK[11] = RK[5] ^ RK[10]; |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
case 14: |
|
||||
for (i = 0; i < 7; i++, RK += 8) { |
|
||||
RK[8] = RK[0] ^ RCON[i] ^ |
|
||||
((uint32_t)FSb[(RK[7] >> 8) & 0xFF]) ^ |
|
||||
((uint32_t)FSb[(RK[7] >> 16) & 0xFF] << 8) ^ |
|
||||
((uint32_t)FSb[(RK[7] >> 24) & 0xFF] << 16) ^ |
|
||||
((uint32_t)FSb[(RK[7]) & 0xFF] << 24); |
|
||||
|
|
||||
RK[9] = RK[1] ^ RK[8]; |
|
||||
RK[10] = RK[2] ^ RK[9]; |
|
||||
RK[11] = RK[3] ^ RK[10]; |
|
||||
|
|
||||
RK[12] = RK[4] ^ |
|
||||
((uint32_t)FSb[(RK[11]) & 0xFF]) ^ |
|
||||
((uint32_t)FSb[(RK[11] >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)FSb[(RK[11] >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)FSb[(RK[11] >> 24) & 0xFF] << 24); |
|
||||
|
|
||||
RK[13] = RK[5] ^ RK[12]; |
|
||||
RK[14] = RK[6] ^ RK[13]; |
|
||||
RK[15] = RK[7] ^ RK[14]; |
|
||||
} |
|
||||
break; |
|
||||
|
|
||||
default: |
|
||||
return -1; |
|
||||
} |
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* AES_SET_DECRYPTION_KEY |
|
||||
* |
|
||||
* This is called by 'aes_setkey' when we're establishing a |
|
||||
* key for subsequent decryption. We give it a pointer to |
|
||||
* the encryption context, a pointer to the key, and the key's |
|
||||
* length in bits. Valid lengths are: 128, 192, or 256 bits. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int aes_set_decryption_key(aes_context *ctx, |
|
||||
const uchar *key, |
|
||||
uint keysize) |
|
||||
{ |
|
||||
int i, j; |
|
||||
aes_context cty; // a calling aes context for set_encryption_key
|
|
||||
uint32_t *RK = ctx->rk; // initialize our RoundKey buffer pointer
|
|
||||
uint32_t *SK; |
|
||||
int ret; |
|
||||
|
|
||||
cty.rounds = ctx->rounds; // initialize our local aes context
|
|
||||
cty.rk = cty.buf; // round count and key buf pointer
|
|
||||
|
|
||||
if ((ret = aes_set_encryption_key(&cty, key, keysize)) != 0) |
|
||||
return(ret); |
|
||||
|
|
||||
SK = cty.rk + cty.rounds * 4; |
|
||||
|
|
||||
CPY128 // copy a 128-bit block from *SK to *RK
|
|
||||
|
|
||||
for (i = ctx->rounds - 1, SK -= 8; i > 0; i--, SK -= 8) { |
|
||||
for (j = 0; j < 4; j++, SK++) { |
|
||||
*RK++ = RT0[FSb[(*SK) & 0xFF]] ^ |
|
||||
RT1[FSb[(*SK >> 8) & 0xFF]] ^ |
|
||||
RT2[FSb[(*SK >> 16) & 0xFF]] ^ |
|
||||
RT3[FSb[(*SK >> 24) & 0xFF]]; |
|
||||
} |
|
||||
} |
|
||||
CPY128 // copy a 128-bit block from *SK to *RK
|
|
||||
memset(&cty, 0, sizeof(aes_context)); // clear local aes context
|
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
#endif /* AES_DECRYPTION */ |
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* AES_SETKEY |
|
||||
* |
|
||||
* Invoked to establish the key schedule for subsequent encryption/decryption |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int aes_setkey(aes_context *ctx, // AES context provided by our caller
|
|
||||
int mode, // ENCRYPT or DECRYPT flag
|
|
||||
const uchar *key, // pointer to the key
|
|
||||
uint keysize) // key length in bytes
|
|
||||
{ |
|
||||
// since table initialization is not thread safe, we could either add
|
|
||||
// system-specific mutexes and init the AES key generation tables on
|
|
||||
// demand, or ask the developer to simply call "gcm_initialize" once during
|
|
||||
// application startup before threading begins. That's what we choose.
|
|
||||
if (!aes_tables_inited) return (-1); // fail the call when not inited.
|
|
||||
|
|
||||
ctx->mode = mode; // capture the key type we're creating
|
|
||||
ctx->rk = ctx->buf; // initialize our round key pointer
|
|
||||
|
|
||||
switch (keysize) // set the rounds count based upon the keysize
|
|
||||
{ |
|
||||
case 16: ctx->rounds = 10; break; // 16-byte, 128-bit key
|
|
||||
case 24: ctx->rounds = 12; break; // 24-byte, 192-bit key
|
|
||||
case 32: ctx->rounds = 14; break; // 32-byte, 256-bit key
|
|
||||
default: return(-1); |
|
||||
} |
|
||||
|
|
||||
#if AES_DECRYPTION |
|
||||
if (mode == AES_DECRYPT) // expand our key for encryption or decryption
|
|
||||
return(aes_set_decryption_key(ctx, key, keysize)); |
|
||||
else /* ENCRYPT */ |
|
||||
#endif /* AES_DECRYPTION */ |
|
||||
return(aes_set_encryption_key(ctx, key, keysize)); |
|
||||
} |
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* AES_CIPHER |
|
||||
* |
|
||||
* Perform AES encryption and decryption. |
|
||||
* The AES context will have been setup with the encryption mode |
|
||||
* and all keying information appropriate for the task. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int aes_cipher(aes_context *ctx, |
|
||||
const uchar input[16], |
|
||||
uchar output[16]) |
|
||||
{ |
|
||||
int i; |
|
||||
uint32_t *RK, X0, X1, X2, X3, Y0, Y1, Y2, Y3; // general purpose locals
|
|
||||
|
|
||||
RK = ctx->rk; |
|
||||
|
|
||||
GET_UINT32_LE(X0, input, 0); X0 ^= *RK++; // load our 128-bit
|
|
||||
GET_UINT32_LE(X1, input, 4); X1 ^= *RK++; // input buffer in a storage
|
|
||||
GET_UINT32_LE(X2, input, 8); X2 ^= *RK++; // memory endian-neutral way
|
|
||||
GET_UINT32_LE(X3, input, 12); X3 ^= *RK++; |
|
||||
|
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||
|
|
||||
if (ctx->mode == AES_DECRYPT) |
|
||||
{ |
|
||||
for (i = (ctx->rounds >> 1) - 1; i > 0; i--) |
|
||||
{ |
|
||||
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); |
|
||||
AES_RROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); |
|
||||
} |
|
||||
|
|
||||
AES_RROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); |
|
||||
|
|
||||
X0 = *RK++ ^ \ |
|
||||
((uint32_t)RSb[(Y0) & 0xFF]) ^ |
|
||||
((uint32_t)RSb[(Y3 >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)RSb[(Y2 >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)RSb[(Y1 >> 24) & 0xFF] << 24); |
|
||||
|
|
||||
X1 = *RK++ ^ \ |
|
||||
((uint32_t)RSb[(Y1) & 0xFF]) ^ |
|
||||
((uint32_t)RSb[(Y0 >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)RSb[(Y3 >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)RSb[(Y2 >> 24) & 0xFF] << 24); |
|
||||
|
|
||||
X2 = *RK++ ^ \ |
|
||||
((uint32_t)RSb[(Y2) & 0xFF]) ^ |
|
||||
((uint32_t)RSb[(Y1 >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)RSb[(Y0 >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)RSb[(Y3 >> 24) & 0xFF] << 24); |
|
||||
|
|
||||
X3 = *RK++ ^ \ |
|
||||
((uint32_t)RSb[(Y3) & 0xFF]) ^ |
|
||||
((uint32_t)RSb[(Y2 >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)RSb[(Y1 >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)RSb[(Y0 >> 24) & 0xFF] << 24); |
|
||||
} |
|
||||
else /* ENCRYPT */ |
|
||||
{ |
|
||||
#endif /* AES_DECRYPTION */ |
|
||||
|
|
||||
for (i = (ctx->rounds >> 1) - 1; i > 0; i--) |
|
||||
{ |
|
||||
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); |
|
||||
AES_FROUND(X0, X1, X2, X3, Y0, Y1, Y2, Y3); |
|
||||
} |
|
||||
|
|
||||
AES_FROUND(Y0, Y1, Y2, Y3, X0, X1, X2, X3); |
|
||||
|
|
||||
X0 = *RK++ ^ \ |
|
||||
((uint32_t)FSb[(Y0) & 0xFF]) ^ |
|
||||
((uint32_t)FSb[(Y1 >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)FSb[(Y2 >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)FSb[(Y3 >> 24) & 0xFF] << 24); |
|
||||
|
|
||||
X1 = *RK++ ^ \ |
|
||||
((uint32_t)FSb[(Y1) & 0xFF]) ^ |
|
||||
((uint32_t)FSb[(Y2 >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)FSb[(Y3 >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)FSb[(Y0 >> 24) & 0xFF] << 24); |
|
||||
|
|
||||
X2 = *RK++ ^ \ |
|
||||
((uint32_t)FSb[(Y2) & 0xFF]) ^ |
|
||||
((uint32_t)FSb[(Y3 >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)FSb[(Y0 >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)FSb[(Y1 >> 24) & 0xFF] << 24); |
|
||||
|
|
||||
X3 = *RK++ ^ \ |
|
||||
((uint32_t)FSb[(Y3) & 0xFF]) ^ |
|
||||
((uint32_t)FSb[(Y0 >> 8) & 0xFF] << 8) ^ |
|
||||
((uint32_t)FSb[(Y1 >> 16) & 0xFF] << 16) ^ |
|
||||
((uint32_t)FSb[(Y2 >> 24) & 0xFF] << 24); |
|
||||
|
|
||||
#if AES_DECRYPTION // whether AES decryption is supported
|
|
||||
} |
|
||||
#endif /* AES_DECRYPTION */ |
|
||||
|
|
||||
PUT_UINT32_LE(X0, output, 0); |
|
||||
PUT_UINT32_LE(X1, output, 4); |
|
||||
PUT_UINT32_LE(X2, output, 8); |
|
||||
PUT_UINT32_LE(X3, output, 12); |
|
||||
|
|
||||
return(0); |
|
||||
} |
|
||||
/* end of aes.c */ |
|
||||
@ -1,78 +0,0 @@ |
|||||
/******************************************************************************
|
|
||||
* |
|
||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL |
|
||||
* |
|
||||
* This is a simple and straightforward implementation of the AES Rijndael |
|
||||
* 128-bit block cipher designed by Vincent Rijmen and Joan Daemen. The focus |
|
||||
* of this work was correctness & accuracy. It is written in 'C' without any |
|
||||
* particular focus upon optimization or speed. It should be endian (memory |
|
||||
* byte order) neutral since the few places that care are handled explicitly. |
|
||||
* |
|
||||
* This implementation of Rijndael was created by Steven M. Gibson of GRC.com. |
|
||||
* |
|
||||
* It is intended for general purpose use, but was written in support of GRC's |
|
||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client. |
|
||||
* |
|
||||
* See: http://csrc.nist.gov/archive/aes/rijndael/wsdindex.html
|
|
||||
* |
|
||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE |
|
||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. |
|
||||
* |
|
||||
*******************************************************************************/ |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
/******************************************************************************/ |
|
||||
#define AES_DECRYPTION 0 // whether AES decryption is supported
|
|
||||
/******************************************************************************/ |
|
||||
|
|
||||
#include <string.h> |
|
||||
|
|
||||
#define AES_ENCRYPT 1 // specify whether we're encrypting
|
|
||||
#define AES_DECRYPT 0 // or decrypting
|
|
||||
|
|
||||
#if defined(_MSC_VER) |
|
||||
#include <basetsd.h> |
|
||||
typedef UINT32 uint32_t; |
|
||||
#else |
|
||||
#include <inttypes.h> |
|
||||
#endif |
|
||||
|
|
||||
typedef unsigned char uchar; // add some convienent shorter types
|
|
||||
typedef unsigned int uint; |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* AES_INIT_KEYGEN_TABLES : MUST be called once before any AES use |
|
||||
******************************************************************************/ |
|
||||
void aes_init_keygen_tables(void); |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* AES_CONTEXT : cipher context / holds inter-call data |
|
||||
******************************************************************************/ |
|
||||
typedef struct { |
|
||||
int mode; // 1 for Encryption, 0 for Decryption
|
|
||||
int rounds; // keysize-based rounds count
|
|
||||
uint32_t *rk; // pointer to current round key
|
|
||||
uint32_t buf[68]; // key expansion buffer
|
|
||||
} aes_context; |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* AES_SETKEY : called to expand the key for encryption or decryption |
|
||||
******************************************************************************/ |
|
||||
int aes_setkey(aes_context *ctx, // pointer to context
|
|
||||
int mode, // 1 or 0 for Encrypt/Decrypt
|
|
||||
const uchar *key, // AES input key
|
|
||||
uint keysize); // size in bytes (must be 16, 24, 32 for
|
|
||||
// 128, 192 or 256-bit keys respectively)
|
|
||||
// returns 0 for success
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* AES_CIPHER : called to encrypt or decrypt ONE 128-bit block of data |
|
||||
******************************************************************************/ |
|
||||
int aes_cipher(aes_context *ctx, // pointer to context
|
|
||||
const uchar input[16], // 128-bit block to en/decipher
|
|
||||
uchar output[16]); // 128-bit output result block
|
|
||||
// returns 0 for success
|
|
||||
@ -1,512 +0,0 @@ |
|||||
/******************************************************************************
|
|
||||
* |
|
||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL |
|
||||
* |
|
||||
* This is a simple and straightforward implementation of AES-GCM authenticated |
|
||||
* encryption. The focus of this work was correctness & accuracy. It is written |
|
||||
* in straight 'C' without any particular focus upon optimization or speed. It |
|
||||
* should be endian (memory byte order) neutral since the few places that care |
|
||||
* are handled explicitly. |
|
||||
* |
|
||||
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. |
|
||||
* |
|
||||
* It is intended for general purpose use, but was written in support of GRC's |
|
||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client. |
|
||||
* |
|
||||
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
|
|
||||
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/
|
|
||||
* gcm/gcm-revised-spec.pdf |
|
||||
* |
|
||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE |
|
||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. |
|
||||
* |
|
||||
*******************************************************************************/ |
|
||||
|
|
||||
#include "gcm.h" |
|
||||
#include "aes.h" |
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* ==== IMPLEMENTATION WARNING ==== |
|
||||
* |
|
||||
* This code was developed for use within SQRL's fixed environmnent. Thus, it |
|
||||
* is somewhat less "general purpose" than it would be if it were designed as |
|
||||
* a general purpose AES-GCM library. Specifically, it bothers with almost NO |
|
||||
* error checking on parameter limits, buffer bounds, etc. It assumes that it |
|
||||
* is being invoked by its author or by someone who understands the values it |
|
||||
* expects to receive. Its behavior will be undefined otherwise. |
|
||||
* |
|
||||
* All functions that might fail are defined to return 'ints' to indicate a |
|
||||
* problem. Most do not do so now. But this allows for error propagation out |
|
||||
* of internal functions if robust error checking should ever be desired. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
|
|
||||
/* Calculating the "GHASH"
|
|
||||
* |
|
||||
* There are many ways of calculating the so-called GHASH in software, each with |
|
||||
* a traditional size vs performance tradeoff. The GHASH (Galois field hash) is |
|
||||
* an intriguing construction which takes two 128-bit strings (also the cipher's |
|
||||
* block size and the fundamental operation size for the system) and hashes them |
|
||||
* into a third 128-bit result. |
|
||||
* |
|
||||
* Many implementation solutions have been worked out that use large precomputed |
|
||||
* table lookups in place of more time consuming bit fiddling, and this approach |
|
||||
* can be scaled easily upward or downward as needed to change the time/space |
|
||||
* tradeoff. It's been studied extensively and there's a solid body of theory and |
|
||||
* practice. For example, without using any lookup tables an implementation |
|
||||
* might obtain 119 cycles per byte throughput, whereas using a simple, though |
|
||||
* large, key-specific 64 kbyte 8-bit lookup table the performance jumps to 13 |
|
||||
* cycles per byte. |
|
||||
* |
|
||||
* And Intel's processors have, since 2010, included an instruction which does |
|
||||
* the entire 128x128->128 bit job in just several 64x64->128 bit pieces. |
|
||||
* |
|
||||
* Since SQRL is interactive, and only processing a few 128-bit blocks, I've |
|
||||
* settled upon a relatively slower but appealing small-table compromise which |
|
||||
* folds a bunch of not only time consuming but also bit twiddling into a simple |
|
||||
* 16-entry table which is attributed to Victor Shoup's 1996 work while at |
|
||||
* Bellcore: "On Fast and Provably Secure MessageAuthentication Based on |
|
||||
* Universal Hashing." See: http://www.shoup.net/papers/macs.pdf |
|
||||
* See, also section 4.1 of the "gcm-revised-spec" cited above. |
|
||||
*/ |
|
||||
|
|
||||
/*
|
|
||||
* This 16-entry table of pre-computed constants is used by the |
|
||||
* GHASH multiplier to improve over a strictly table-free but |
|
||||
* significantly slower 128x128 bit multiple within GF(2^128). |
|
||||
*/ |
|
||||
static const uint64_t last4[16] = { |
|
||||
0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0, |
|
||||
0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0 }; |
|
||||
|
|
||||
/*
|
|
||||
* Platform Endianness Neutralizing Load and Store Macro definitions |
|
||||
* GCM wants platform-neutral Big Endian (BE) byte ordering |
|
||||
*/ |
|
||||
#define GET_UINT32_BE(n,b,i) { \ |
|
||||
(n) = ( (uint32_t) (b)[(i) ] << 24 ) \ |
|
||||
| ( (uint32_t) (b)[(i) + 1] << 16 ) \ |
|
||||
| ( (uint32_t) (b)[(i) + 2] << 8 ) \ |
|
||||
| ( (uint32_t) (b)[(i) + 3] ); } |
|
||||
|
|
||||
#define PUT_UINT32_BE(n,b,i) { \ |
|
||||
(b)[(i) ] = (uchar) ( (n) >> 24 ); \ |
|
||||
(b)[(i) + 1] = (uchar) ( (n) >> 16 ); \ |
|
||||
(b)[(i) + 2] = (uchar) ( (n) >> 8 ); \ |
|
||||
(b)[(i) + 3] = (uchar) ( (n) ); } |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_INITIALIZE |
|
||||
* |
|
||||
* Must be called once to initialize the GCM library. |
|
||||
* |
|
||||
* At present, this only calls the AES keygen table generator, which expands |
|
||||
* the AES keying tables for use. This is NOT A THREAD-SAFE function, so it |
|
||||
* MUST be called during system initialization before a multi-threading |
|
||||
* environment is running. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_initialize(void) |
|
||||
{ |
|
||||
aes_init_keygen_tables(); |
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_MULT |
|
||||
* |
|
||||
* Performs a GHASH operation on the 128-bit input vector 'x', setting |
|
||||
* the 128-bit output vector to 'x' times H using our precomputed tables. |
|
||||
* 'x' and 'output' are seen as elements of GCM's GF(2^128) Galois field. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
static void gcm_mult(gcm_context *ctx, // pointer to established context
|
|
||||
const uchar x[16], // pointer to 128-bit input vector
|
|
||||
uchar output[16]) // pointer to 128-bit output vector
|
|
||||
{ |
|
||||
int i; |
|
||||
uchar lo, hi, rem; |
|
||||
uint64_t zh, zl; |
|
||||
|
|
||||
lo = (uchar)(x[15] & 0x0f); |
|
||||
hi = (uchar)(x[15] >> 4); |
|
||||
zh = ctx->HH[lo]; |
|
||||
zl = ctx->HL[lo]; |
|
||||
|
|
||||
for (i = 15; i >= 0; i--) { |
|
||||
lo = (uchar)(x[i] & 0x0f); |
|
||||
hi = (uchar)(x[i] >> 4); |
|
||||
|
|
||||
if (i != 15) { |
|
||||
rem = (uchar)(zl & 0x0f); |
|
||||
zl = (zh << 60) | (zl >> 4); |
|
||||
zh = (zh >> 4); |
|
||||
zh ^= (uint64_t)last4[rem] << 48; |
|
||||
zh ^= ctx->HH[lo]; |
|
||||
zl ^= ctx->HL[lo]; |
|
||||
} |
|
||||
rem = (uchar)(zl & 0x0f); |
|
||||
zl = (zh << 60) | (zl >> 4); |
|
||||
zh = (zh >> 4); |
|
||||
zh ^= (uint64_t)last4[rem] << 48; |
|
||||
zh ^= ctx->HH[hi]; |
|
||||
zl ^= ctx->HL[hi]; |
|
||||
} |
|
||||
PUT_UINT32_BE(zh >> 32, output, 0); |
|
||||
PUT_UINT32_BE(zh, output, 4); |
|
||||
PUT_UINT32_BE(zl >> 32, output, 8); |
|
||||
PUT_UINT32_BE(zl, output, 12); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_SETKEY |
|
||||
* |
|
||||
* This is called to set the AES-GCM key. It initializes the AES key |
|
||||
* and populates the gcm context's pre-calculated HTables. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_setkey(gcm_context *ctx, // pointer to caller-provided gcm context
|
|
||||
const uchar *key, // pointer to the AES encryption key
|
|
||||
const uint keysize) // size in bytes (must be 16, 24, 32 for
|
|
||||
// 128, 192 or 256-bit keys respectively)
|
|
||||
{ |
|
||||
int ret, i, j; |
|
||||
uint64_t hi, lo; |
|
||||
uint64_t vl, vh; |
|
||||
unsigned char h[16]; |
|
||||
|
|
||||
memset(ctx, 0, sizeof(gcm_context)); // zero caller-provided GCM context
|
|
||||
memset(h, 0, 16); // initialize the block to encrypt
|
|
||||
|
|
||||
// encrypt the null 128-bit block to generate a key-based value
|
|
||||
// which is then used to initialize our GHASH lookup tables
|
|
||||
if ((ret = aes_setkey(&ctx->aes_ctx, AES_ENCRYPT, key, keysize)) != 0) |
|
||||
return(ret); |
|
||||
if ((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0) |
|
||||
return(ret); |
|
||||
|
|
||||
GET_UINT32_BE(hi, h, 0); // pack h as two 64-bit ints, big-endian
|
|
||||
GET_UINT32_BE(lo, h, 4); |
|
||||
vh = (uint64_t)hi << 32 | lo; |
|
||||
|
|
||||
GET_UINT32_BE(hi, h, 8); |
|
||||
GET_UINT32_BE(lo, h, 12); |
|
||||
vl = (uint64_t)hi << 32 | lo; |
|
||||
|
|
||||
ctx->HL[8] = vl; // 8 = 1000 corresponds to 1 in GF(2^128)
|
|
||||
ctx->HH[8] = vh; |
|
||||
ctx->HH[0] = 0; // 0 corresponds to 0 in GF(2^128)
|
|
||||
ctx->HL[0] = 0; |
|
||||
|
|
||||
for (i = 4; i > 0; i >>= 1) { |
|
||||
uint32_t T = (uint32_t)(vl & 1) * 0xe1000000U; |
|
||||
vl = (vh << 63) | (vl >> 1); |
|
||||
vh = (vh >> 1) ^ ((uint64_t)T << 32); |
|
||||
ctx->HL[i] = vl; |
|
||||
ctx->HH[i] = vh; |
|
||||
} |
|
||||
for (i = 2; i < 16; i <<= 1) { |
|
||||
uint64_t *HiL = ctx->HL + i, *HiH = ctx->HH + i; |
|
||||
vh = *HiH; |
|
||||
vl = *HiL; |
|
||||
for (j = 1; j < i; j++) { |
|
||||
HiH[j] = vh ^ ctx->HH[j]; |
|
||||
HiL[j] = vl ^ ctx->HL[j]; |
|
||||
} |
|
||||
} |
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM processing occurs four phases: SETKEY, START, UPDATE and FINISH. |
|
||||
* |
|
||||
* SETKEY: |
|
||||
* |
|
||||
* START: Sets the Encryption/Decryption mode. |
|
||||
* Accepts the initialization vector and additional data. |
|
||||
* |
|
||||
* UPDATE: Encrypts or decrypts the plaintext or ciphertext. |
|
||||
* |
|
||||
* FINISH: Performs a final GHASH to generate the authentication tag. |
|
||||
* |
|
||||
****************************************************************************** |
|
||||
* |
|
||||
* GCM_START |
|
||||
* |
|
||||
* Given a user-provided GCM context, this initializes it, sets the encryption |
|
||||
* mode, and preprocesses the initialization vector and additional AEAD data. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
|
|
||||
int mode, // AES_ENCRYPT or AES_DECRYPT
|
|
||||
const uchar *iv, // pointer to initialization vector
|
|
||||
size_t iv_len, // IV length in bytes (should == 12)
|
|
||||
const uchar *add, // ptr to additional AEAD data (NULL if none)
|
|
||||
size_t add_len) // length of additional AEAD data (bytes)
|
|
||||
{ |
|
||||
int ret; // our error return if the AES encrypt fails
|
|
||||
uchar work_buf[16]; // XOR source built from provided IV if len != 16
|
|
||||
const uchar *p; // general purpose array pointer
|
|
||||
size_t use_len; // byte count to process, up to 16 bytes
|
|
||||
size_t i; // local loop iterator
|
|
||||
|
|
||||
// since the context might be reused under the same key
|
|
||||
// we zero the working buffers for this next new process
|
|
||||
memset(ctx->y, 0x00, sizeof(ctx->y)); |
|
||||
memset(ctx->buf, 0x00, sizeof(ctx->buf)); |
|
||||
ctx->len = 0; |
|
||||
ctx->add_len = 0; |
|
||||
|
|
||||
ctx->mode = mode; // set the GCM encryption/decryption mode
|
|
||||
ctx->aes_ctx.mode = AES_ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
|
|
||||
|
|
||||
if (iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV
|
|
||||
memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff
|
|
||||
ctx->y[15] = 1; // start "counting" from 1 (not 0)
|
|
||||
} |
|
||||
else // if we don't have a 12-byte IV, we GHASH whatever we've been given
|
|
||||
{ |
|
||||
memset(work_buf, 0x00, 16); // clear the working buffer
|
|
||||
PUT_UINT32_BE(iv_len * 8, work_buf, 12); // place the IV into buffer
|
|
||||
|
|
||||
p = iv; |
|
||||
while (iv_len > 0) { |
|
||||
use_len = (iv_len < 16) ? iv_len : 16; |
|
||||
for (i = 0; i < use_len; i++) ctx->y[i] ^= p[i]; |
|
||||
gcm_mult(ctx, ctx->y, ctx->y); |
|
||||
iv_len -= use_len; |
|
||||
p += use_len; |
|
||||
} |
|
||||
for (i = 0; i < 16; i++) ctx->y[i] ^= work_buf[i]; |
|
||||
gcm_mult(ctx, ctx->y, ctx->y); |
|
||||
} |
|
||||
|
|
||||
if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ctx->base_ectr)) != 0) |
|
||||
return(ret); |
|
||||
|
|
||||
ctx->add_len = add_len; |
|
||||
p = add; |
|
||||
while (add_len > 0) { |
|
||||
use_len = (add_len < 16) ? add_len : 16; |
|
||||
for (i = 0; i < use_len; i++) ctx->buf[i] ^= p[i]; |
|
||||
gcm_mult(ctx, ctx->buf, ctx->buf); |
|
||||
add_len -= use_len; |
|
||||
p += use_len; |
|
||||
} |
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_UPDATE |
|
||||
* |
|
||||
* This is called once or more to process bulk plaintext or ciphertext data. |
|
||||
* We give this some number of bytes of input and it returns the same number |
|
||||
* of output bytes. If called multiple times (which is fine) all but the final |
|
||||
* invocation MUST be called with length mod 16 == 0. (Only the final call can |
|
||||
* have a partial block length of < 128 bits.) |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
|
|
||||
size_t length, // length, in bytes, of data to process
|
|
||||
const uchar *input, // pointer to source data
|
|
||||
uchar *output) // pointer to destination data
|
|
||||
{ |
|
||||
int ret; // our error return if the AES encrypt fails
|
|
||||
uchar ectr[16]; // counter-mode cipher output for XORing
|
|
||||
size_t use_len; // byte count to process, up to 16 bytes
|
|
||||
size_t i; // local loop iterator
|
|
||||
|
|
||||
ctx->len += length; // bump the GCM context's running length count
|
|
||||
|
|
||||
while (length > 0) { |
|
||||
// clamp the length to process at 16 bytes
|
|
||||
use_len = (length < 16) ? length : 16; |
|
||||
|
|
||||
// increment the context's 128-bit IV||Counter 'y' vector
|
|
||||
for (i = 16; i > 12; i--) if (++ctx->y[i - 1] != 0) break; |
|
||||
|
|
||||
// encrypt the context's 'y' vector under the established key
|
|
||||
if ((ret = aes_cipher(&ctx->aes_ctx, ctx->y, ectr)) != 0) |
|
||||
return(ret); |
|
||||
|
|
||||
// encrypt or decrypt the input to the output
|
|
||||
if (ctx->mode == AES_ENCRYPT) |
|
||||
{ |
|
||||
for (i = 0; i < use_len; i++) { |
|
||||
// XOR the cipher's ouptut vector (ectr) with our input
|
|
||||
output[i] = (uchar)(ectr[i] ^ input[i]); |
|
||||
// now we mix in our data into the authentication hash.
|
|
||||
// if we're ENcrypting we XOR in the post-XOR (output)
|
|
||||
// results, but if we're DEcrypting we XOR in the input
|
|
||||
// data
|
|
||||
ctx->buf[i] ^= output[i]; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
for (i = 0; i < use_len; i++) { |
|
||||
// but if we're DEcrypting we XOR in the input data first,
|
|
||||
// i.e. before saving to ouput data, otherwise if the input
|
|
||||
// and output buffer are the same (inplace decryption) we
|
|
||||
// would not get the correct auth tag
|
|
||||
|
|
||||
ctx->buf[i] ^= input[i]; |
|
||||
|
|
||||
// XOR the cipher's ouptut vector (ectr) with our input
|
|
||||
output[i] = (uchar)(ectr[i] ^ input[i]); |
|
||||
} |
|
||||
} |
|
||||
gcm_mult(ctx, ctx->buf, ctx->buf); // perform a GHASH operation
|
|
||||
|
|
||||
length -= use_len; // drop the remaining byte count to process
|
|
||||
input += use_len; // bump our input pointer forward
|
|
||||
output += use_len; // bump our output pointer forward
|
|
||||
} |
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_FINISH |
|
||||
* |
|
||||
* This is called once after all calls to GCM_UPDATE to finalize the GCM. |
|
||||
* It performs the final GHASH to produce the resulting authentication TAG. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
|
|
||||
uchar *tag, // pointer to buffer which receives the tag
|
|
||||
size_t tag_len) // length, in bytes, of the tag-receiving buf
|
|
||||
{ |
|
||||
uchar work_buf[16]; |
|
||||
uint64_t orig_len = ctx->len * 8; |
|
||||
uint64_t orig_add_len = ctx->add_len * 8; |
|
||||
size_t i; |
|
||||
|
|
||||
if (tag_len != 0) memcpy(tag, ctx->base_ectr, tag_len); |
|
||||
|
|
||||
if (orig_len || orig_add_len) { |
|
||||
memset(work_buf, 0x00, 16); |
|
||||
|
|
||||
PUT_UINT32_BE((orig_add_len >> 32), work_buf, 0); |
|
||||
PUT_UINT32_BE((orig_add_len), work_buf, 4); |
|
||||
PUT_UINT32_BE((orig_len >> 32), work_buf, 8); |
|
||||
PUT_UINT32_BE((orig_len), work_buf, 12); |
|
||||
|
|
||||
for (i = 0; i < 16; i++) ctx->buf[i] ^= work_buf[i]; |
|
||||
gcm_mult(ctx, ctx->buf, ctx->buf); |
|
||||
for (i = 0; i < tag_len; i++) tag[i] ^= ctx->buf[i]; |
|
||||
} |
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_CRYPT_AND_TAG |
|
||||
* |
|
||||
* This either encrypts or decrypts the user-provided data and, either |
|
||||
* way, generates an authentication tag of the requested length. It must be |
|
||||
* called with a GCM context whose key has already been set with GCM_SETKEY. |
|
||||
* |
|
||||
* The user would typically call this explicitly to ENCRYPT a buffer of data |
|
||||
* and optional associated data, and produce its an authentication tag. |
|
||||
* |
|
||||
* To reverse the process the user would typically call the companion |
|
||||
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided |
|
||||
* authentication tag. The GCM_AUTH_DECRYPT function calls this function |
|
||||
* to perform its decryption and tag generation, which it then compares. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_crypt_and_tag( |
|
||||
gcm_context *ctx, // gcm context with key already setup
|
|
||||
int mode, // cipher direction: AES_ENCRYPT or AES_DECRYPT
|
|
||||
const uchar *iv, // pointer to the 12-byte initialization vector
|
|
||||
size_t iv_len, // byte length if the IV. should always be 12
|
|
||||
const uchar *add, // pointer to the non-ciphered additional data
|
|
||||
size_t add_len, // byte length of the additional AEAD data
|
|
||||
const uchar *input, // pointer to the cipher data source
|
|
||||
uchar *output, // pointer to the cipher data destination
|
|
||||
size_t length, // byte length of the cipher data
|
|
||||
uchar *tag, // pointer to the tag to be generated
|
|
||||
size_t tag_len) // byte length of the tag to be generated
|
|
||||
{ /*
|
|
||||
assuming that the caller has already invoked gcm_setkey to |
|
||||
prepare the gcm context with the keying material, we simply |
|
||||
invoke each of the three GCM sub-functions in turn... |
|
||||
*/ |
|
||||
gcm_start(ctx, mode, iv, iv_len, add, add_len); |
|
||||
gcm_update(ctx, length, input, output); |
|
||||
gcm_finish(ctx, tag, tag_len); |
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_AUTH_DECRYPT |
|
||||
* |
|
||||
* This DECRYPTS a user-provided data buffer with optional associated data. |
|
||||
* It then verifies a user-supplied authentication tag against the tag just |
|
||||
* re-created during decryption to verify that the data has not been altered. |
|
||||
* |
|
||||
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption |
|
||||
* and authentication tag generation. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_auth_decrypt( |
|
||||
gcm_context *ctx, // gcm context with key already setup
|
|
||||
const uchar *iv, // pointer to the 12-byte initialization vector
|
|
||||
size_t iv_len, // byte length if the IV. should always be 12
|
|
||||
const uchar *add, // pointer to the non-ciphered additional data
|
|
||||
size_t add_len, // byte length of the additional AEAD data
|
|
||||
const uchar *input, // pointer to the cipher data source
|
|
||||
uchar *output, // pointer to the cipher data destination
|
|
||||
size_t length, // byte length of the cipher data
|
|
||||
const uchar *tag, // pointer to the tag to be authenticated
|
|
||||
size_t tag_len) // byte length of the tag <= 16
|
|
||||
{ |
|
||||
uchar check_tag[16]; // the tag generated and returned by decryption
|
|
||||
int diff; // an ORed flag to detect authentication errors
|
|
||||
size_t i; // our local iterator
|
|
||||
/*
|
|
||||
we use GCM_DECRYPT_AND_TAG (above) to perform our decryption |
|
||||
(which is an identical XORing to reverse the previous one) |
|
||||
and also to re-generate the matching authentication tag |
|
||||
*/ |
|
||||
gcm_crypt_and_tag(ctx, AES_DECRYPT, iv, iv_len, add, add_len, |
|
||||
input, output, length, check_tag, tag_len); |
|
||||
|
|
||||
// now we verify the authentication tag in 'constant time'
|
|
||||
for (diff = 0, i = 0; i < tag_len; i++) |
|
||||
diff |= tag[i] ^ check_tag[i]; |
|
||||
|
|
||||
if (diff != 0) { // see whether any bits differed?
|
|
||||
memset(output, 0, length); // if so... wipe the output data
|
|
||||
return(GCM_AUTH_FAILURE); // return GCM_AUTH_FAILURE
|
|
||||
} |
|
||||
return(0); |
|
||||
} |
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_ZERO_CTX |
|
||||
* |
|
||||
* The GCM context contains both the GCM context and the AES context. |
|
||||
* This includes keying and key-related material which is security- |
|
||||
* sensitive, so it MUST be zeroed after use. This function does that. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
void gcm_zero_ctx(gcm_context *ctx) |
|
||||
{ |
|
||||
// zero the context originally provided to us
|
|
||||
memset(ctx, 0, sizeof(gcm_context)); |
|
||||
} |
|
||||
@ -1,183 +0,0 @@ |
|||||
/******************************************************************************
|
|
||||
* |
|
||||
* THIS SOURCE CODE IS HEREBY PLACED INTO THE PUBLIC DOMAIN FOR THE GOOD OF ALL |
|
||||
* |
|
||||
* This is a simple and straightforward implementation of AES-GCM authenticated |
|
||||
* encryption. The focus of this work was correctness & accuracy. It is written |
|
||||
* in straight 'C' without any particular focus upon optimization or speed. It |
|
||||
* should be endian (memory byte order) neutral since the few places that care |
|
||||
* are handled explicitly. |
|
||||
* |
|
||||
* This implementation of AES-GCM was created by Steven M. Gibson of GRC.com. |
|
||||
* |
|
||||
* It is intended for general purpose use, but was written in support of GRC's |
|
||||
* reference implementation of the SQRL (Secure Quick Reliable Login) client. |
|
||||
* |
|
||||
* See: http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
|
|
||||
* http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/ \ |
|
||||
* gcm/gcm-revised-spec.pdf |
|
||||
* |
|
||||
* NO COPYRIGHT IS CLAIMED IN THIS WORK, HOWEVER, NEITHER IS ANY WARRANTY MADE |
|
||||
* REGARDING ITS FITNESS FOR ANY PARTICULAR PURPOSE. USE IT AT YOUR OWN RISK. |
|
||||
* |
|
||||
*******************************************************************************/ |
|
||||
#pragma once |
|
||||
|
|
||||
#define GCM_AUTH_FAILURE 0x55555555 // authentication failure
|
|
||||
|
|
||||
#include "aes.h" // gcm_context includes aes_context |
|
||||
|
|
||||
#if defined(_MSC_VER) |
|
||||
#include <basetsd.h> |
|
||||
typedef unsigned int size_t;// use the right type for length declarations
|
|
||||
typedef UINT32 uint32_t; |
|
||||
typedef UINT64 uint64_t; |
|
||||
#else |
|
||||
#include <stdint.h> |
|
||||
#endif |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* GCM_CONTEXT : GCM context / holds keytables, instance data, and AES ctx |
|
||||
******************************************************************************/ |
|
||||
typedef struct { |
|
||||
int mode; // cipher direction: encrypt/decrypt
|
|
||||
uint64_t len; // cipher data length processed so far
|
|
||||
uint64_t add_len; // total add data length
|
|
||||
uint64_t HL[16]; // precalculated lo-half HTable
|
|
||||
uint64_t HH[16]; // precalculated hi-half HTable
|
|
||||
uchar base_ectr[16]; // first counter-mode cipher output for tag
|
|
||||
uchar y[16]; // the current cipher-input IV|Counter value
|
|
||||
uchar buf[16]; // buf working value
|
|
||||
aes_context aes_ctx; // cipher context used
|
|
||||
} gcm_context; |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* GCM_CONTEXT : MUST be called once before ANY use of this library |
|
||||
******************************************************************************/ |
|
||||
int gcm_initialize(void); |
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* GCM_SETKEY : sets the GCM (and AES) keying material for use |
|
||||
******************************************************************************/ |
|
||||
int gcm_setkey(gcm_context *ctx, // caller-provided context ptr
|
|
||||
const uchar *key, // pointer to cipher key
|
|
||||
const uint keysize // size in bytes (must be 16, 24, 32 for
|
|
||||
// 128, 192 or 256-bit keys respectively)
|
|
||||
); // returns 0 for success
|
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_CRYPT_AND_TAG |
|
||||
* |
|
||||
* This either encrypts or decrypts the user-provided data and, either |
|
||||
* way, generates an authentication tag of the requested length. It must be |
|
||||
* called with a GCM context whose key has already been set with GCM_SETKEY. |
|
||||
* |
|
||||
* The user would typically call this explicitly to ENCRYPT a buffer of data |
|
||||
* and optional associated data, and produce its an authentication tag. |
|
||||
* |
|
||||
* To reverse the process the user would typically call the companion |
|
||||
* GCM_AUTH_DECRYPT function to decrypt data and verify a user-provided |
|
||||
* authentication tag. The GCM_AUTH_DECRYPT function calls this function |
|
||||
* to perform its decryption and tag generation, which it then compares. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_crypt_and_tag( |
|
||||
gcm_context *ctx, // gcm context with key already setup
|
|
||||
int mode, // cipher direction: ENCRYPT (1) or DECRYPT (0)
|
|
||||
const uchar *iv, // pointer to the 12-byte initialization vector
|
|
||||
size_t iv_len, // byte length if the IV. should always be 12
|
|
||||
const uchar *add, // pointer to the non-ciphered additional data
|
|
||||
size_t add_len, // byte length of the additional AEAD data
|
|
||||
const uchar *input, // pointer to the cipher data source
|
|
||||
uchar *output, // pointer to the cipher data destination
|
|
||||
size_t length, // byte length of the cipher data
|
|
||||
uchar *tag, // pointer to the tag to be generated
|
|
||||
size_t tag_len); // byte length of the tag to be generated
|
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_AUTH_DECRYPT |
|
||||
* |
|
||||
* This DECRYPTS a user-provided data buffer with optional associated data. |
|
||||
* It then verifies a user-supplied authentication tag against the tag just |
|
||||
* re-created during decryption to verify that the data has not been altered. |
|
||||
* |
|
||||
* This function calls GCM_CRYPT_AND_TAG (above) to perform the decryption |
|
||||
* and authentication tag generation. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_auth_decrypt( |
|
||||
gcm_context *ctx, // gcm context with key already setup
|
|
||||
const uchar *iv, // pointer to the 12-byte initialization vector
|
|
||||
size_t iv_len, // byte length if the IV. should always be 12
|
|
||||
const uchar *add, // pointer to the non-ciphered additional data
|
|
||||
size_t add_len, // byte length of the additional AEAD data
|
|
||||
const uchar *input, // pointer to the cipher data source
|
|
||||
uchar *output, // pointer to the cipher data destination
|
|
||||
size_t length, // byte length of the cipher data
|
|
||||
const uchar *tag, // pointer to the tag to be authenticated
|
|
||||
size_t tag_len); // byte length of the tag <= 16
|
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_START |
|
||||
* |
|
||||
* Given a user-provided GCM context, this initializes it, sets the encryption |
|
||||
* mode, and preprocesses the initialization vector and additional AEAD data. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
|
|
||||
int mode, // ENCRYPT (1) or DECRYPT (0)
|
|
||||
const uchar *iv, // pointer to initialization vector
|
|
||||
size_t iv_len, // IV length in bytes (should == 12)
|
|
||||
const uchar *add, // pointer to additional AEAD data (NULL if none)
|
|
||||
size_t add_len); // length of additional AEAD data (bytes)
|
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_UPDATE |
|
||||
* |
|
||||
* This is called once or more to process bulk plaintext or ciphertext data. |
|
||||
* We give this some number of bytes of input and it returns the same number |
|
||||
* of output bytes. If called multiple times (which is fine) all but the final |
|
||||
* invocation MUST be called with length mod 16 == 0. (Only the final call can |
|
||||
* have a partial block length of < 128 bits.) |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
|
|
||||
size_t length, // length, in bytes, of data to process
|
|
||||
const uchar *input, // pointer to source data
|
|
||||
uchar *output); // pointer to destination data
|
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_FINISH |
|
||||
* |
|
||||
* This is called once after all calls to GCM_UPDATE to finalize the GCM. |
|
||||
* It performs the final GHASH to produce the resulting authentication TAG. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
int gcm_finish(gcm_context *ctx, // pointer to user-provided GCM context
|
|
||||
uchar *tag, // ptr to tag buffer - NULL if tag_len = 0
|
|
||||
size_t tag_len); // length, in bytes, of the tag-receiving buf
|
|
||||
|
|
||||
|
|
||||
/******************************************************************************
|
|
||||
* |
|
||||
* GCM_ZERO_CTX |
|
||||
* |
|
||||
* The GCM context contains both the GCM context and the AES context. |
|
||||
* This includes keying and key-related material which is security- |
|
||||
* sensitive, so it MUST be zeroed after use. This function does that. |
|
||||
* |
|
||||
******************************************************************************/ |
|
||||
void gcm_zero_ctx(gcm_context *ctx); |
|
||||
@ -1,337 +0,0 @@ |
|||||
/**************************** hkdf.c ***************************/ |
|
||||
/***************** See RFC 6234 for details. *******************/ |
|
||||
/* Copyright (c) 2011 IETF Trust and the persons identified as */ |
|
||||
/* authors of the code. All rights reserved. */ |
|
||||
/* See sha.h for terms of use and redistribution. */ |
|
||||
|
|
||||
/*
|
|
||||
* Description: |
|
||||
* This file implements the HKDF algorithm (HMAC-based |
|
||||
* Extract-and-Expand Key Derivation Function, RFC 5869), |
|
||||
* expressed in terms of the various SHA algorithms. |
|
||||
*/ |
|
||||
|
|
||||
#include "sha.h" |
|
||||
#include <string.h> |
|
||||
#include <stdlib.h> |
|
||||
|
|
||||
/*
|
|
||||
* hkdf |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will generate keying material using HKDF. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* whichSha: [in] |
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512 |
|
||||
* salt[ ]: [in] |
|
||||
* The optional salt value (a non-secret random value); |
|
||||
* if not provided (salt == NULL), it is set internally |
|
||||
* to a string of HashLen(whichSha) zeros. |
|
||||
* salt_len: [in] |
|
||||
* The length of the salt value. (Ignored if salt == NULL.) |
|
||||
* ikm[ ]: [in] |
|
||||
* Input keying material. |
|
||||
* ikm_len: [in] |
|
||||
* The length of the input keying material. |
|
||||
* info[ ]: [in] |
|
||||
* The optional context and application specific information. |
|
||||
* If info == NULL or a zero-length string, it is ignored. |
|
||||
* info_len: [in] |
|
||||
* The length of the optional context and application specific |
|
||||
* information. (Ignored if info == NULL.) |
|
||||
* okm[ ]: [out] |
|
||||
* Where the HKDF is to be stored. |
|
||||
* okm_len: [in] |
|
||||
* The length of the buffer to hold okm. |
|
||||
* okm_len must be <= 255 * USHABlockSize(whichSha) |
|
||||
* |
|
||||
* Notes: |
|
||||
* Calls hkdfExtract() and hkdfExpand(). |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hkdf(SHAversion whichSha, |
|
||||
const unsigned char *salt, size_t salt_len, |
|
||||
const unsigned char *ikm, size_t ikm_len, |
|
||||
const unsigned char *info, size_t info_len, |
|
||||
uint8_t okm[], size_t okm_len) |
|
||||
{ |
|
||||
uint8_t prk[USHAMaxHashSize]; |
|
||||
return hkdfExtract(whichSha, salt, salt_len, ikm, ikm_len, prk) || |
|
||||
hkdfExpand(whichSha, prk, USHAHashSize(whichSha), info, |
|
||||
info_len, okm, okm_len); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hkdfExtract |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will perform HKDF extraction. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* whichSha: [in] |
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512 |
|
||||
* salt[ ]: [in] |
|
||||
* The optional salt value (a non-secret random value); |
|
||||
* if not provided (salt == NULL), it is set internally |
|
||||
* to a string of HashLen(whichSha) zeros. |
|
||||
* salt_len: [in] |
|
||||
* The length of the salt value. (Ignored if salt == NULL.) |
|
||||
* ikm[ ]: [in] |
|
||||
* Input keying material. |
|
||||
* ikm_len: [in] |
|
||||
* The length of the input keying material. |
|
||||
* prk[ ]: [out] |
|
||||
* Array where the HKDF extraction is to be stored. |
|
||||
* Must be larger than USHAHashSize(whichSha); |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hkdfExtract(SHAversion whichSha, |
|
||||
const unsigned char *salt, size_t salt_len, |
|
||||
const unsigned char *ikm, size_t ikm_len, |
|
||||
uint8_t prk[USHAMaxHashSize]) |
|
||||
{ |
|
||||
unsigned char nullSalt[USHAMaxHashSize]; |
|
||||
if (salt == 0) { |
|
||||
salt = nullSalt; |
|
||||
salt_len = USHAHashSize(whichSha); |
|
||||
memset(nullSalt, '\0', salt_len); |
|
||||
} |
|
||||
else if (salt_len < 0) { |
|
||||
return shaBadParam; |
|
||||
} |
|
||||
return hmac(whichSha, ikm, ikm_len, salt, salt_len, prk); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hkdfExpand |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will perform HKDF expansion. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* whichSha: [in] |
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512 |
|
||||
* prk[ ]: [in] |
|
||||
* The pseudo-random key to be expanded; either obtained |
|
||||
* directly from a cryptographically strong, uniformly |
|
||||
* distributed pseudo-random number generator, or as the |
|
||||
* output from hkdfExtract(). |
|
||||
* prk_len: [in] |
|
||||
* The length of the pseudo-random key in prk; |
|
||||
* should at least be equal to USHAHashSize(whichSHA). |
|
||||
* info[ ]: [in] |
|
||||
* The optional context and application specific information. |
|
||||
* If info == NULL or a zero-length string, it is ignored. |
|
||||
* info_len: [in] |
|
||||
* The length of the optional context and application specific |
|
||||
* information. (Ignored if info == NULL.) |
|
||||
* okm[ ]: [out] |
|
||||
* Where the HKDF is to be stored. |
|
||||
* okm_len: [in] |
|
||||
* The length of the buffer to hold okm. |
|
||||
* okm_len must be <= 255 * USHABlockSize(whichSha) |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hkdfExpand(SHAversion whichSha, const uint8_t prk[], size_t prk_len, |
|
||||
const unsigned char *info, size_t info_len, |
|
||||
uint8_t okm[], size_t okm_len) |
|
||||
{ |
|
||||
size_t hash_len, N; |
|
||||
unsigned char T[USHAMaxHashSize]; |
|
||||
size_t Tlen, where, i; |
|
||||
|
|
||||
if (info == 0) { |
|
||||
info = (const unsigned char *)""; |
|
||||
info_len = 0; |
|
||||
} |
|
||||
else if (info_len < 0) { |
|
||||
return shaBadParam; |
|
||||
} |
|
||||
if (okm_len <= 0) return shaBadParam; |
|
||||
if (!okm) return shaBadParam; |
|
||||
|
|
||||
hash_len = USHAHashSize(whichSha); |
|
||||
if (prk_len < hash_len) return shaBadParam; |
|
||||
N = okm_len / hash_len; |
|
||||
if ((okm_len % hash_len) != 0) N++; |
|
||||
if (N > 255) return shaBadParam; |
|
||||
|
|
||||
Tlen = 0; |
|
||||
where = 0; |
|
||||
for (i = 1; i <= N; i++) { |
|
||||
HMACContext context; |
|
||||
unsigned char c = i; |
|
||||
int ret = hmacReset(&context, whichSha, prk, prk_len) || |
|
||||
hmacInput(&context, T, Tlen) || |
|
||||
hmacInput(&context, info, info_len) || |
|
||||
hmacInput(&context, &c, 1) || |
|
||||
hmacResult(&context, T); |
|
||||
if (ret != shaSuccess) return ret; |
|
||||
memcpy(okm + where, T, |
|
||||
(i != N) ? hash_len : (okm_len - where)); |
|
||||
where += hash_len; |
|
||||
Tlen = hash_len; |
|
||||
} |
|
||||
return shaSuccess; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hkdfReset |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will initialize the hkdfContext in preparation |
|
||||
* for key derivation using the modular HKDF interface for |
|
||||
* arbitrary length inputs. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to reset. |
|
||||
* whichSha: [in] |
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512 |
|
||||
* salt[ ]: [in] |
|
||||
* The optional salt value (a non-secret random value); |
|
||||
* if not provided (salt == NULL), it is set internally |
|
||||
* to a string of HashLen(whichSha) zeros. |
|
||||
* salt_len: [in] |
|
||||
* The length of the salt value. (Ignored if salt == NULL.) |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hkdfReset(HKDFContext *context, enum SHAversion whichSha, |
|
||||
const unsigned char *salt, size_t salt_len) |
|
||||
{ |
|
||||
unsigned char nullSalt[USHAMaxHashSize]; |
|
||||
if (!context) return shaNull; |
|
||||
|
|
||||
context->whichSha = whichSha; |
|
||||
context->hashSize = USHAHashSize(whichSha); |
|
||||
if (salt == 0) { |
|
||||
salt = nullSalt; |
|
||||
salt_len = context->hashSize; |
|
||||
memset(nullSalt, '\0', salt_len); |
|
||||
} |
|
||||
|
|
||||
return hmacReset(&context->hmacContext, whichSha, salt, salt_len); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hkdfInput |
|
||||
* |
|
||||
* Description: |
|
||||
* This function accepts an array of octets as the next portion |
|
||||
* of the input keying material. It may be called multiple times. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The HKDF context to update. |
|
||||
* ikm[ ]: [in] |
|
||||
* An array of octets representing the next portion of |
|
||||
* the input keying material. |
|
||||
* ikm_len: [in] |
|
||||
* The length of ikm. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hkdfInput(HKDFContext *context, const unsigned char *ikm, |
|
||||
size_t ikm_len) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
if (context->Computed) return context->Corrupted = shaStateError; |
|
||||
return hmacInput(&context->hmacContext, ikm, ikm_len); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hkdfFinalBits |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will add in any final bits of the |
|
||||
* input keying material. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The HKDF context to update |
|
||||
* ikm_bits: [in] |
|
||||
* The final bits of the input keying material, in the upper |
|
||||
* portion of the byte. (Use 0b###00000 instead of 0b00000### |
|
||||
* to input the three bits ###.) |
|
||||
* ikm_bit_count: [in] |
|
||||
* The number of bits in message_bits, between 1 and 7. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits, |
|
||||
unsigned int ikm_bit_count) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
if (context->Computed) return context->Corrupted = shaStateError; |
|
||||
return hmacFinalBits(&context->hmacContext, ikm_bits, ikm_bit_count); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hkdfResult |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will finish the HKDF extraction and perform the |
|
||||
* final HKDF expansion. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The HKDF context to use to calculate the HKDF hash. |
|
||||
* prk[ ]: [out] |
|
||||
* An optional location to store the HKDF extraction. |
|
||||
* Either NULL, or pointer to a buffer that must be |
|
||||
* larger than USHAHashSize(whichSha); |
|
||||
* info[ ]: [in] |
|
||||
* The optional context and application specific information. |
|
||||
* If info == NULL or a zero-length string, it is ignored. |
|
||||
* info_len: [in] |
|
||||
* The length of the optional context and application specific |
|
||||
* information. (Ignored if info == NULL.) |
|
||||
* okm[ ]: [out] |
|
||||
* Where the HKDF is to be stored. |
|
||||
* okm_len: [in] |
|
||||
* The length of the buffer to hold okm. |
|
||||
* okm_len must be <= 255 * USHABlockSize(whichSha) |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hkdfResult(HKDFContext *context, |
|
||||
uint8_t prk[USHAMaxHashSize], |
|
||||
const unsigned char *info, size_t info_len, |
|
||||
uint8_t okm[], size_t okm_len) |
|
||||
{ |
|
||||
uint8_t prkbuf[USHAMaxHashSize]; |
|
||||
int ret; |
|
||||
|
|
||||
if (!context) return shaNull; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
if (context->Computed) return context->Corrupted = shaStateError; |
|
||||
if (!okm) return context->Corrupted = shaBadParam; |
|
||||
if (!prk) prk = prkbuf; |
|
||||
|
|
||||
ret = hmacResult(&context->hmacContext, prk) || |
|
||||
hkdfExpand(context->whichSha, prk, context->hashSize, info, |
|
||||
info_len, okm, okm_len); |
|
||||
context->Computed = 1; |
|
||||
return context->Corrupted = ret; |
|
||||
} |
|
||||
|
|
||||
@ -1,250 +0,0 @@ |
|||||
/**************************** hmac.c ***************************/ |
|
||||
/***************** See RFC 6234 for details. *******************/ |
|
||||
/* Copyright (c) 2011 IETF Trust and the persons identified as */ |
|
||||
/* authors of the code. All rights reserved. */ |
|
||||
/* See sha.h for terms of use and redistribution. */ |
|
||||
|
|
||||
/*
|
|
||||
* Description: |
|
||||
* This file implements the HMAC algorithm (Keyed-Hashing for |
|
||||
* Message Authentication, [RFC 2104]), expressed in terms of |
|
||||
* the various SHA algorithms. |
|
||||
*/ |
|
||||
|
|
||||
#include "sha.h" |
|
||||
#include <stddef.h> |
|
||||
|
|
||||
/*
|
|
||||
* hmac |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will compute an HMAC message digest. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* whichSha: [in] |
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512 |
|
||||
* message_array[ ]: [in] |
|
||||
* An array of octets representing the message. |
|
||||
* Note: in RFC 2104, this parameter is known |
|
||||
* as 'text'. |
|
||||
* length: [in] |
|
||||
* The length of the message in message_array. |
|
||||
* key[ ]: [in] |
|
||||
* The secret shared key. |
|
||||
* key_len: [in] |
|
||||
* The length of the secret shared key. |
|
||||
* digest[ ]: [out] |
|
||||
* Where the digest is to be returned. |
|
||||
* NOTE: The length of the digest is determined by |
|
||||
* the value of whichSha. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
|
|
||||
int hmac(SHAversion whichSha, |
|
||||
const unsigned char *message_array, size_t length, |
|
||||
const unsigned char *key, size_t key_len, |
|
||||
uint8_t digest[USHAMaxHashSize]) |
|
||||
{ |
|
||||
HMACContext context; |
|
||||
return hmacReset(&context, whichSha, key, key_len) || |
|
||||
hmacInput(&context, message_array, length) || |
|
||||
hmacResult(&context, digest); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hmacReset |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will initialize the hmacContext in preparation |
|
||||
* for computing a new HMAC message digest. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to reset. |
|
||||
* whichSha: [in] |
|
||||
* One of SHA1, SHA224, SHA256, SHA384, SHA512 |
|
||||
* key[ ]: [in] |
|
||||
* The secret shared key. |
|
||||
* key_len: [in] |
|
||||
* The length of the secret shared key. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hmacReset(HMACContext *context, enum SHAversion whichSha, |
|
||||
const unsigned char *key, size_t key_len) |
|
||||
{ |
|
||||
size_t i, blocksize, hashsize; |
|
||||
int ret; |
|
||||
|
|
||||
/* inner padding - key XORd with ipad */ |
|
||||
unsigned char k_ipad[USHA_Max_Message_Block_Size]; |
|
||||
|
|
||||
/* temporary buffer when keylen > blocksize */ |
|
||||
unsigned char tempkey[USHAMaxHashSize]; |
|
||||
|
|
||||
if (!context) return shaNull; |
|
||||
context->Computed = 0; |
|
||||
context->Corrupted = shaSuccess; |
|
||||
|
|
||||
blocksize = context->blockSize = USHABlockSize(whichSha); |
|
||||
hashsize = context->hashSize = USHAHashSize(whichSha); |
|
||||
context->whichSha = whichSha; |
|
||||
|
|
||||
/*
|
|
||||
* If key is longer than the hash blocksize, |
|
||||
* reset it to key = HASH(key). |
|
||||
*/ |
|
||||
if (key_len > blocksize) { |
|
||||
USHAContext tcontext; |
|
||||
int err = USHAReset(&tcontext, whichSha) || |
|
||||
USHAInput(&tcontext, key, key_len) || |
|
||||
USHAResult(&tcontext, tempkey); |
|
||||
if (err != shaSuccess) return err; |
|
||||
|
|
||||
key = tempkey; |
|
||||
key_len = hashsize; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* The HMAC transform looks like: |
|
||||
* |
|
||||
* SHA(K XOR opad, SHA(K XOR ipad, text)) |
|
||||
* |
|
||||
* where K is an n byte key, 0-padded to a total of blocksize bytes, |
|
||||
* ipad is the byte 0x36 repeated blocksize times, |
|
||||
* opad is the byte 0x5c repeated blocksize times, |
|
||||
* and text is the data being protected. |
|
||||
*/ |
|
||||
|
|
||||
/* store key into the pads, XOR'd with ipad and opad values */ |
|
||||
for (i = 0; i < key_len; i++) { |
|
||||
k_ipad[i] = key[i] ^ 0x36; |
|
||||
context->k_opad[i] = key[i] ^ 0x5c; |
|
||||
} |
|
||||
/* remaining pad bytes are '\0' XOR'd with ipad and opad values */ |
|
||||
for (; i < blocksize; i++) { |
|
||||
k_ipad[i] = 0x36; |
|
||||
context->k_opad[i] = 0x5c; |
|
||||
} |
|
||||
|
|
||||
/* perform inner hash */ |
|
||||
/* init context for 1st pass */ |
|
||||
ret = USHAReset(&context->shaContext, whichSha) || |
|
||||
/* and start with inner pad */ |
|
||||
USHAInput(&context->shaContext, k_ipad, blocksize); |
|
||||
return context->Corrupted = ret; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hmacInput |
|
||||
* |
|
||||
* Description: |
|
||||
* This function accepts an array of octets as the next portion |
|
||||
* of the message. It may be called multiple times. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The HMAC context to update. |
|
||||
* text[ ]: [in] |
|
||||
* An array of octets representing the next portion of |
|
||||
* the message. |
|
||||
* text_len: [in] |
|
||||
* The length of the message in text. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hmacInput(HMACContext *context, const unsigned char *text, |
|
||||
size_t text_len) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
if (context->Computed) return context->Corrupted = shaStateError; |
|
||||
/* then text of datagram */ |
|
||||
return context->Corrupted = |
|
||||
USHAInput(&context->shaContext, text, text_len); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hmacFinalBits |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will add in any final bits of the message. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The HMAC context to update. |
|
||||
* message_bits: [in] |
|
||||
* The final bits of the message, in the upper portion of the |
|
||||
* byte. (Use 0b###00000 instead of 0b00000### to input the |
|
||||
* three bits ###.) |
|
||||
* length: [in] |
|
||||
* The number of bits in message_bits, between 1 and 7. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int hmacFinalBits(HMACContext *context, |
|
||||
uint8_t bits, unsigned int bit_count) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
if (context->Computed) return context->Corrupted = shaStateError; |
|
||||
/* then final bits of datagram */ |
|
||||
return context->Corrupted = |
|
||||
USHAFinalBits(&context->shaContext, bits, bit_count); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* hmacResult |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will return the N-byte message digest into the |
|
||||
* Message_Digest array provided by the caller. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to use to calculate the HMAC hash. |
|
||||
* digest[ ]: [out] |
|
||||
* Where the digest is returned. |
|
||||
* NOTE 2: The length of the hash is determined by the value of |
|
||||
* whichSha that was passed to hmacReset(). |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int hmacResult(HMACContext *context, uint8_t *digest) |
|
||||
{ |
|
||||
int ret; |
|
||||
if (!context) return shaNull; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
if (context->Computed) return context->Corrupted = shaStateError; |
|
||||
|
|
||||
/* finish up 1st pass */ |
|
||||
/* (Use digest here as a temporary buffer.) */ |
|
||||
ret = |
|
||||
USHAResult(&context->shaContext, digest) || |
|
||||
|
|
||||
/* perform outer SHA */ |
|
||||
/* init context for 2nd pass */ |
|
||||
USHAReset(&context->shaContext, context->whichSha) || |
|
||||
|
|
||||
/* start with outer pad */ |
|
||||
USHAInput(&context->shaContext, context->k_opad, |
|
||||
context->blockSize) || |
|
||||
|
|
||||
/* then results of 1st hash */ |
|
||||
USHAInput(&context->shaContext, digest, context->hashSize) || |
|
||||
/* finish up 2nd pass */ |
|
||||
USHAResult(&context->shaContext, digest); |
|
||||
|
|
||||
context->Computed = 1; |
|
||||
return context->Corrupted = ret; |
|
||||
} |
|
||||
@ -1,25 +0,0 @@ |
|||||
/************************ sha-private.h ************************/ |
|
||||
/***************** See RFC 6234 for details. *******************/ |
|
||||
#pragma once |
|
||||
/*
|
|
||||
* These definitions are defined in FIPS 180-3, section 4.1. |
|
||||
* Ch() and Maj() are defined identically in sections 4.1.1, |
|
||||
* 4.1.2, and 4.1.3. |
|
||||
* |
|
||||
* The definitions used in FIPS 180-3 are as follows: |
|
||||
*/ |
|
||||
|
|
||||
#ifndef USE_MODIFIED_MACROS |
|
||||
#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) |
|
||||
#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) |
|
||||
#else /* USE_MODIFIED_MACROS */ |
|
||||
/*
|
|
||||
* The following definitions are equivalent and potentially faster. |
|
||||
*/ |
|
||||
|
|
||||
#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z)) |
|
||||
#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z))) |
|
||||
|
|
||||
#endif /* USE_MODIFIED_MACROS */ |
|
||||
|
|
||||
#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z)) |
|
||||
@ -1,278 +0,0 @@ |
|||||
/**************************** sha.h ****************************/ |
|
||||
/***************** See RFC 6234 for details. *******************/ |
|
||||
/*
|
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as |
|
||||
authors of the code. All rights reserved. |
|
||||
Redistribution and use in source and binary forms, with or |
|
||||
without modification, are permitted provided that the following |
|
||||
conditions are met: |
|
||||
- Redistributions of source code must retain the above |
|
||||
copyright notice, this list of conditions and |
|
||||
the following disclaimer. |
|
||||
- Redistributions in binary form must reproduce the above |
|
||||
copyright notice, this list of conditions and the following |
|
||||
disclaimer in the documentation and/or other materials provided |
|
||||
with the distribution. |
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor |
|
||||
the names of specific contributors, may be used to endorse or |
|
||||
promote products derived from this software without specific |
|
||||
prior written permission. |
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
|
||||
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, |
|
||||
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
||||
*/ |
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
/*
|
|
||||
* Description: |
|
||||
* This file implements the Secure Hash Algorithms |
|
||||
* as defined in the U.S. National Institute of Standards |
|
||||
* and Technology Federal Information Processing Standards |
|
||||
* Publication (FIPS PUB) 180-3 published in October 2008 |
|
||||
* and formerly defined in its predecessors, FIPS PUB 180-1 |
|
||||
* and FIP PUB 180-2. |
|
||||
* |
|
||||
* A combined document showing all algorithms is available at |
|
||||
* http://csrc.nist.gov/publications/fips/
|
|
||||
* fips180-3/fips180-3_final.pdf |
|
||||
* |
|
||||
* The five hashes are defined in these sizes: |
|
||||
* SHA-1 20 byte / 160 bit |
|
||||
* SHA-224 28 byte / 224 bit |
|
||||
* SHA-256 32 byte / 256 bit |
|
||||
* SHA-384 48 byte / 384 bit |
|
||||
* SHA-512 64 byte / 512 bit |
|
||||
* |
|
||||
* Compilation Note: |
|
||||
* These files may be compiled with two options: |
|
||||
* USE_32BIT_ONLY - use 32-bit arithmetic only, for systems |
|
||||
* without 64-bit integers |
|
||||
* |
|
||||
* USE_MODIFIED_MACROS - use alternate form of the SHA_Ch() |
|
||||
* and SHA_Maj() macros that are equivalent |
|
||||
* and potentially faster on many systems |
|
||||
* |
|
||||
*/ |
|
||||
|
|
||||
#include <stdint.h> |
|
||||
#include <stddef.h> |
|
||||
|
|
||||
/*
|
|
||||
* If you do not have the ISO standard stdint.h header file, then you |
|
||||
* must typedef the following: |
|
||||
* name meaning |
|
||||
* uint64_t unsigned 64-bit integer |
|
||||
* uint32_t unsigned 32-bit integer |
|
||||
* uint8_t unsigned 8-bit integer (i.e., unsigned char) |
|
||||
* int_least16_t integer of >= 16 bits |
|
||||
* |
|
||||
* See stdint-example.h |
|
||||
*/ |
|
||||
|
|
||||
#ifndef _SHA_enum_ |
|
||||
#define _SHA_enum_ |
|
||||
/*
|
|
||||
* All SHA functions return one of these values. |
|
||||
*/ |
|
||||
enum { |
|
||||
shaSuccess = 0, |
|
||||
shaNull, /* Null pointer parameter */ |
|
||||
shaInputTooLong, /* input data too long */ |
|
||||
shaStateError, /* called Input after FinalBits or Result */ |
|
||||
shaBadParam /* passed a bad parameter */ |
|
||||
}; |
|
||||
#endif /* _SHA_enum_ */ |
|
||||
|
|
||||
/*
|
|
||||
* These constants hold size information for each of the SHA |
|
||||
* hashing operations |
|
||||
*/ |
|
||||
enum { |
|
||||
SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64, |
|
||||
SHA256_Message_Block_Size = 64, |
|
||||
USHA_Max_Message_Block_Size = SHA256_Message_Block_Size, |
|
||||
|
|
||||
SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32, |
|
||||
USHAMaxHashSize = SHA256HashSize, |
|
||||
|
|
||||
SHA1HashSizeBits = 160, SHA224HashSizeBits = 224, |
|
||||
SHA256HashSizeBits = 256, USHAMaxHashSizeBits = SHA256HashSizeBits |
|
||||
}; |
|
||||
|
|
||||
/*
|
|
||||
* These constants are used in the USHA (Unified SHA) functions. |
|
||||
*/ |
|
||||
typedef enum SHAversion { |
|
||||
SHA224, SHA256 |
|
||||
} SHAversion; |
|
||||
|
|
||||
/*
|
|
||||
* This structure will hold context information for the SHA-256 |
|
||||
* hashing operation. |
|
||||
*/ |
|
||||
typedef struct SHA256Context { |
|
||||
uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */ |
|
||||
|
|
||||
uint32_t Length_High; /* Message length in bits */ |
|
||||
uint32_t Length_Low; /* Message length in bits */ |
|
||||
|
|
||||
int_least16_t Message_Block_Index; /* Message_Block array index */ |
|
||||
/* 512-bit message blocks */ |
|
||||
uint8_t Message_Block[SHA256_Message_Block_Size]; |
|
||||
|
|
||||
int Computed; /* Is the hash computed? */ |
|
||||
int Corrupted; /* Cumulative corruption code */ |
|
||||
} SHA256Context; |
|
||||
|
|
||||
/*
|
|
||||
* This structure will hold context information for the SHA-224 |
|
||||
* hashing operation. It uses the SHA-256 structure for computation. |
|
||||
*/ |
|
||||
typedef struct SHA256Context SHA224Context; |
|
||||
|
|
||||
/*
|
|
||||
* This structure holds context information for all SHA |
|
||||
* hashing operations. |
|
||||
*/ |
|
||||
typedef struct USHAContext { |
|
||||
int whichSha; /* which SHA is being used */ |
|
||||
union { |
|
||||
SHA224Context sha224Context; SHA256Context sha256Context; |
|
||||
} ctx; |
|
||||
|
|
||||
} USHAContext; |
|
||||
|
|
||||
/*
|
|
||||
* This structure will hold context information for the HMAC |
|
||||
* keyed-hashing operation. |
|
||||
*/ |
|
||||
typedef struct HMACContext { |
|
||||
int whichSha; /* which SHA is being used */ |
|
||||
int hashSize; /* hash size of SHA being used */ |
|
||||
int blockSize; /* block size of SHA being used */ |
|
||||
USHAContext shaContext; /* SHA context */ |
|
||||
unsigned char k_opad[USHA_Max_Message_Block_Size]; |
|
||||
/* outer padding - key XORd with opad */ |
|
||||
int Computed; /* Is the MAC computed? */ |
|
||||
int Corrupted; /* Cumulative corruption code */ |
|
||||
|
|
||||
} HMACContext; |
|
||||
|
|
||||
/*
|
|
||||
* This structure will hold context information for the HKDF |
|
||||
* extract-and-expand Key Derivation Functions. |
|
||||
*/ |
|
||||
typedef struct HKDFContext { |
|
||||
int whichSha; /* which SHA is being used */ |
|
||||
HMACContext hmacContext; |
|
||||
int hashSize; /* hash size of SHA being used */ |
|
||||
unsigned char prk[USHAMaxHashSize]; |
|
||||
/* pseudo-random key - output of hkdfInput */ |
|
||||
int Computed; /* Is the key material computed? */ |
|
||||
int Corrupted; /* Cumulative corruption code */ |
|
||||
} HKDFContext; |
|
||||
|
|
||||
/*
|
|
||||
* Function Prototypes |
|
||||
*/ |
|
||||
|
|
||||
|
|
||||
/* SHA-224 */ |
|
||||
int SHA224Reset(SHA224Context *); |
|
||||
int SHA224Input(SHA224Context *, const uint8_t *bytes, |
|
||||
unsigned int bytecount); |
|
||||
int SHA224FinalBits(SHA224Context *, uint8_t bits, |
|
||||
unsigned int bit_count); |
|
||||
int SHA224Result(SHA224Context *, |
|
||||
uint8_t Message_Digest[SHA224HashSize]); |
|
||||
|
|
||||
/* SHA-256 */ |
|
||||
int SHA256Reset(SHA256Context *); |
|
||||
int SHA256Input(SHA256Context *, const uint8_t *bytes, |
|
||||
unsigned int bytecount); |
|
||||
int SHA256FinalBits(SHA256Context *, uint8_t bits, |
|
||||
unsigned int bit_count); |
|
||||
int SHA256Result(SHA256Context *, |
|
||||
uint8_t Message_Digest[SHA256HashSize]); |
|
||||
|
|
||||
/* Unified SHA functions, chosen by whichSha */ |
|
||||
int USHAReset(USHAContext *context, SHAversion whichSha); |
|
||||
int USHAInput(USHAContext *context, |
|
||||
const uint8_t *bytes, unsigned int bytecount); |
|
||||
int USHAFinalBits(USHAContext *context, |
|
||||
uint8_t bits, unsigned int bit_count); |
|
||||
int USHAResult(USHAContext *context, |
|
||||
uint8_t Message_Digest[USHAMaxHashSize]); |
|
||||
int USHABlockSize(enum SHAversion whichSha); |
|
||||
int USHAHashSize(enum SHAversion whichSha); |
|
||||
|
|
||||
/*
|
|
||||
* HMAC Keyed-Hashing for Message Authentication, RFC 2104, |
|
||||
* for all SHAs. |
|
||||
* This interface allows a fixed-length text input to be used. |
|
||||
*/ |
|
||||
int hmac(SHAversion whichSha, /* which SHA algorithm to use */ |
|
||||
const unsigned char *text, /* pointer to data stream */ |
|
||||
size_t text_len, /* length of data stream */ |
|
||||
const unsigned char *key, /* pointer to authentication key */ |
|
||||
size_t key_len, /* length of authentication key */ |
|
||||
uint8_t digest[USHAMaxHashSize]); /* caller digest to fill in */ |
|
||||
|
|
||||
/*
|
|
||||
* HMAC Keyed-Hashing for Message Authentication, RFC 2104, |
|
||||
* for all SHAs. |
|
||||
* This interface allows any length of text input to be used. |
|
||||
*/ |
|
||||
int hmacReset(HMACContext *context, enum SHAversion whichSha, |
|
||||
const unsigned char *key, size_t key_len); |
|
||||
int hmacInput(HMACContext *context, const unsigned char *text, |
|
||||
size_t text_len); |
|
||||
int hmacFinalBits(HMACContext *context, uint8_t bits, |
|
||||
unsigned int bit_count); |
|
||||
int hmacResult(HMACContext *context, |
|
||||
uint8_t digest[USHAMaxHashSize]); |
|
||||
|
|
||||
|
|
||||
/*
|
|
||||
* HKDF HMAC-based Extract-and-Expand Key Derivation Function, |
|
||||
* RFC 5869, for all SHAs. |
|
||||
*/ |
|
||||
int hkdf(SHAversion whichSha, |
|
||||
const unsigned char *salt, size_t salt_len, |
|
||||
const unsigned char *ikm, size_t ikm_len, |
|
||||
const unsigned char *info, size_t info_len, |
|
||||
uint8_t okm[ ], size_t okm_len); |
|
||||
|
|
||||
int hkdfExtract(SHAversion whichSha, const unsigned char *salt, |
|
||||
size_t salt_len, const unsigned char *ikm, |
|
||||
size_t ikm_len, uint8_t prk[USHAMaxHashSize]); |
|
||||
int hkdfExpand(SHAversion whichSha, const uint8_t prk[ ], |
|
||||
size_t prk_len, const unsigned char *info, |
|
||||
size_t info_len, uint8_t okm[ ], size_t okm_len); |
|
||||
|
|
||||
/*
|
|
||||
* HKDF HMAC-based Extract-and-Expand Key Derivation Function, |
|
||||
* RFC 5869, for all SHAs. |
|
||||
* This interface allows any length of text input to be used. |
|
||||
*/ |
|
||||
int hkdfReset(HKDFContext *context, enum SHAversion whichSha, |
|
||||
const unsigned char *salt, size_t salt_len); |
|
||||
int hkdfInput(HKDFContext *context, const unsigned char *ikm, |
|
||||
size_t ikm_len); |
|
||||
int hkdfFinalBits(HKDFContext *context, uint8_t ikm_bits, |
|
||||
unsigned int ikm_bit_count); |
|
||||
int hkdfResult(HKDFContext *context, |
|
||||
uint8_t prk[USHAMaxHashSize], |
|
||||
const unsigned char *info, size_t info_len, |
|
||||
uint8_t okm[USHAMaxHashSize], size_t okm_len); |
|
||||
@ -1,581 +0,0 @@ |
|||||
/************************* sha224-256.c ************************/ |
|
||||
/***************** See RFC 6234 for details. *******************/ |
|
||||
/* Copyright (c) 2011 IETF Trust and the persons identified as */ |
|
||||
/* authors of the code. All rights reserved. */ |
|
||||
/* See sha.h for terms of use and redistribution. */ |
|
||||
|
|
||||
/*
|
|
||||
* Description: |
|
||||
* This file implements the Secure Hash Algorithms SHA-224 and |
|
||||
* SHA-256 as defined in the U.S. National Institute of Standards |
|
||||
* and Technology Federal Information Processing Standards |
|
||||
* Publication (FIPS PUB) 180-3 published in October 2008 |
|
||||
* and formerly defined in its predecessors, FIPS PUB 180-1 |
|
||||
* and FIP PUB 180-2. |
|
||||
* |
|
||||
* A combined document showing all algorithms is available at |
|
||||
* http://csrc.nist.gov/publications/fips/
|
|
||||
* fips180-3/fips180-3_final.pdf |
|
||||
* |
|
||||
* The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit |
|
||||
* message digests for a given data stream. It should take about |
|
||||
* 2**n steps to find a message with the same digest as a given |
|
||||
* message and 2**(n/2) to find any two messages with the same |
|
||||
* digest, when n is the digest size in bits. Therefore, this |
|
||||
* algorithm can serve as a means of providing a |
|
||||
* "fingerprint" for a message. |
|
||||
* |
|
||||
* Portability Issues: |
|
||||
* SHA-224 and SHA-256 are defined in terms of 32-bit "words". |
|
||||
* This code uses <stdint.h> (included via "sha.h") to define 32- |
|
||||
* and 8-bit unsigned integer types. If your C compiler does not |
|
||||
* support 32-bit unsigned integers, this code is not |
|
||||
* appropriate. |
|
||||
* |
|
||||
* Caveats: |
|
||||
* SHA-224 and SHA-256 are designed to work with messages less |
|
||||
* than 2^64 bits long. This implementation uses SHA224/256Input() |
|
||||
* to hash the bits that are a multiple of the size of an 8-bit |
|
||||
* octet, and then optionally uses SHA224/256FinalBits() |
|
||||
* to hash the final few bits of the input. |
|
||||
*/ |
|
||||
|
|
||||
#include "sha.h" |
|
||||
#include "sha-private.h" |
|
||||
|
|
||||
/* Define the SHA shift, rotate left, and rotate right macros */ |
|
||||
#define SHA256_SHR(bits,word) ((word) >> (bits)) |
|
||||
#define SHA256_ROTL(bits,word) \ |
|
||||
(((word) << (bits)) | ((word) >> (32-(bits)))) |
|
||||
#define SHA256_ROTR(bits,word) \ |
|
||||
(((word) >> (bits)) | ((word) << (32-(bits)))) |
|
||||
|
|
||||
/* Define the SHA SIGMA and sigma macros */ |
|
||||
#define SHA256_SIGMA0(word) \ |
|
||||
(SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word)) |
|
||||
#define SHA256_SIGMA1(word) \ |
|
||||
(SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word)) |
|
||||
#define SHA256_sigma0(word) \ |
|
||||
(SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word)) |
|
||||
#define SHA256_sigma1(word) \ |
|
||||
(SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word)) |
|
||||
|
|
||||
/*
|
|
||||
* Add "length" to the length. |
|
||||
* Set Corrupted when overflow has occurred. |
|
||||
*/ |
|
||||
static uint32_t addTemp; |
|
||||
#define SHA224_256AddLength(context, length) \ |
|
||||
(addTemp = (context)->Length_Low, (context)->Corrupted = \ |
|
||||
(((context)->Length_Low += (length)) < addTemp) && \ |
|
||||
(++(context)->Length_High == 0) ? shaInputTooLong : \ |
|
||||
(context)->Corrupted ) |
|
||||
|
|
||||
/* Local Function Prototypes */ |
|
||||
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0); |
|
||||
static void SHA224_256ProcessMessageBlock(SHA256Context *context); |
|
||||
static void SHA224_256Finalize(SHA256Context *context, |
|
||||
uint8_t Pad_Byte); |
|
||||
static void SHA224_256PadMessage(SHA256Context *context, |
|
||||
uint8_t Pad_Byte); |
|
||||
static int SHA224_256ResultN(SHA256Context *context, |
|
||||
uint8_t Message_Digest[ ], int HashSize); |
|
||||
|
|
||||
/* Initial Hash Values: FIPS 180-3 section 5.3.2 */ |
|
||||
static uint32_t SHA224_H0[SHA256HashSize/4] = { |
|
||||
0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, |
|
||||
0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4 |
|
||||
}; |
|
||||
|
|
||||
/* Initial Hash Values: FIPS 180-3 section 5.3.3 */ |
|
||||
static uint32_t SHA256_H0[SHA256HashSize/4] = { |
|
||||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, |
|
||||
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 |
|
||||
}; |
|
||||
|
|
||||
/*
|
|
||||
* SHA224Reset |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will initialize the SHA224Context in preparation |
|
||||
* for computing a new SHA224 message digest. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to reset. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int SHA224Reset(SHA224Context *context) |
|
||||
{ |
|
||||
return SHA224_256Reset(context, SHA224_H0); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA224Input |
|
||||
* |
|
||||
* Description: |
|
||||
* This function accepts an array of octets as the next portion |
|
||||
* of the message. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The SHA context to update. |
|
||||
* message_array[ ]: [in] |
|
||||
* An array of octets representing the next portion of |
|
||||
* the message. |
|
||||
* length: [in] |
|
||||
* The length of the message in message_array. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int SHA224Input(SHA224Context *context, const uint8_t *message_array, |
|
||||
unsigned int length) |
|
||||
{ |
|
||||
return SHA256Input(context, message_array, length); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA224FinalBits |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will add in any final bits of the message. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The SHA context to update. |
|
||||
* message_bits: [in] |
|
||||
* The final bits of the message, in the upper portion of the |
|
||||
* byte. (Use 0b###00000 instead of 0b00000### to input the |
|
||||
* three bits ###.) |
|
||||
* length: [in] |
|
||||
* The number of bits in message_bits, between 1 and 7. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int SHA224FinalBits(SHA224Context *context, |
|
||||
uint8_t message_bits, unsigned int length) |
|
||||
{ |
|
||||
return SHA256FinalBits(context, message_bits, length); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA224Result |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will return the 224-bit message digest |
|
||||
* into the Message_Digest array provided by the caller. |
|
||||
* NOTE: |
|
||||
* The first octet of hash is stored in the element with index 0, |
|
||||
* the last octet of hash in the element with index 27. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to use to calculate the SHA hash. |
|
||||
* Message_Digest[ ]: [out] |
|
||||
* Where the digest is returned. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int SHA224Result(SHA224Context *context, |
|
||||
uint8_t Message_Digest[SHA224HashSize]) |
|
||||
{ |
|
||||
return SHA224_256ResultN(context, Message_Digest, SHA224HashSize); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA256Reset |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will initialize the SHA256Context in preparation |
|
||||
* for computing a new SHA256 message digest. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to reset. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int SHA256Reset(SHA256Context *context) |
|
||||
{ |
|
||||
return SHA224_256Reset(context, SHA256_H0); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA256Input |
|
||||
* |
|
||||
* Description: |
|
||||
* This function accepts an array of octets as the next portion |
|
||||
* of the message. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The SHA context to update. |
|
||||
* message_array[ ]: [in] |
|
||||
* An array of octets representing the next portion of |
|
||||
* the message. |
|
||||
* length: [in] |
|
||||
* The length of the message in message_array. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int SHA256Input(SHA256Context *context, const uint8_t *message_array, |
|
||||
unsigned int length) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
if (!length) return shaSuccess; |
|
||||
if (!message_array) return shaNull; |
|
||||
if (context->Computed) return context->Corrupted = shaStateError; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
|
|
||||
while (length--) { |
|
||||
context->Message_Block[context->Message_Block_Index++] = |
|
||||
*message_array; |
|
||||
|
|
||||
if ((SHA224_256AddLength(context, 8) == shaSuccess) && |
|
||||
(context->Message_Block_Index == SHA256_Message_Block_Size)) |
|
||||
SHA224_256ProcessMessageBlock(context); |
|
||||
|
|
||||
message_array++; |
|
||||
} |
|
||||
|
|
||||
return context->Corrupted; |
|
||||
|
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA256FinalBits |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will add in any final bits of the message. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The SHA context to update. |
|
||||
* message_bits: [in] |
|
||||
* The final bits of the message, in the upper portion of the |
|
||||
* byte. (Use 0b###00000 instead of 0b00000### to input the |
|
||||
* three bits ###.) |
|
||||
* length: [in] |
|
||||
* The number of bits in message_bits, between 1 and 7. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int SHA256FinalBits(SHA256Context *context, |
|
||||
uint8_t message_bits, unsigned int length) |
|
||||
{ |
|
||||
static uint8_t masks[8] = { |
|
||||
/* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80, |
|
||||
/* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0, |
|
||||
/* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8, |
|
||||
/* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE |
|
||||
}; |
|
||||
static uint8_t markbit[8] = { |
|
||||
/* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40, |
|
||||
/* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10, |
|
||||
/* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04, |
|
||||
/* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01 |
|
||||
}; |
|
||||
|
|
||||
if (!context) return shaNull; |
|
||||
if (!length) return shaSuccess; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
if (context->Computed) return context->Corrupted = shaStateError; |
|
||||
if (length >= 8) return context->Corrupted = shaBadParam; |
|
||||
|
|
||||
SHA224_256AddLength(context, length); |
|
||||
SHA224_256Finalize(context, (uint8_t) |
|
||||
((message_bits & masks[length]) | markbit[length])); |
|
||||
|
|
||||
return context->Corrupted; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA256Result |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will return the 256-bit message digest |
|
||||
* into the Message_Digest array provided by the caller. |
|
||||
* NOTE: |
|
||||
* The first octet of hash is stored in the element with index 0, |
|
||||
* the last octet of hash in the element with index 31. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to use to calculate the SHA hash. |
|
||||
* Message_Digest[ ]: [out] |
|
||||
* Where the digest is returned. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int SHA256Result(SHA256Context *context, |
|
||||
uint8_t Message_Digest[SHA256HashSize]) |
|
||||
{ |
|
||||
return SHA224_256ResultN(context, Message_Digest, SHA256HashSize); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA224_256Reset |
|
||||
* |
|
||||
* Description: |
|
||||
* This helper function will initialize the SHA256Context in |
|
||||
* preparation for computing a new SHA-224 or SHA-256 message digest. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to reset. |
|
||||
* H0[ ]: [in] |
|
||||
* The initial hash value array to use. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
static int SHA224_256Reset(SHA256Context *context, uint32_t *H0) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
|
|
||||
context->Length_High = context->Length_Low = 0; |
|
||||
context->Message_Block_Index = 0; |
|
||||
|
|
||||
context->Intermediate_Hash[0] = H0[0]; |
|
||||
context->Intermediate_Hash[1] = H0[1]; |
|
||||
context->Intermediate_Hash[2] = H0[2]; |
|
||||
context->Intermediate_Hash[3] = H0[3]; |
|
||||
context->Intermediate_Hash[4] = H0[4]; |
|
||||
context->Intermediate_Hash[5] = H0[5]; |
|
||||
context->Intermediate_Hash[6] = H0[6]; |
|
||||
context->Intermediate_Hash[7] = H0[7]; |
|
||||
|
|
||||
context->Computed = 0; |
|
||||
context->Corrupted = shaSuccess; |
|
||||
|
|
||||
return shaSuccess; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA224_256ProcessMessageBlock |
|
||||
* |
|
||||
* Description: |
|
||||
* This helper function will process the next 512 bits of the |
|
||||
* message stored in the Message_Block array. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The SHA context to update. |
|
||||
* |
|
||||
* Returns: |
|
||||
* Nothing. |
|
||||
* |
|
||||
* Comments: |
|
||||
* Many of the variable names in this code, especially the |
|
||||
* single character names, were used because those were the |
|
||||
* names used in the Secure Hash Standard. |
|
||||
*/ |
|
||||
static void SHA224_256ProcessMessageBlock(SHA256Context *context) |
|
||||
{ |
|
||||
/* Constants defined in FIPS 180-3, section 4.2.2 */ |
|
||||
static const uint32_t K[64] = { |
|
||||
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, |
|
||||
0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, |
|
||||
0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, |
|
||||
0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, |
|
||||
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, |
|
||||
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, |
|
||||
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, |
|
||||
0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, |
|
||||
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, |
|
||||
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, |
|
||||
0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, |
|
||||
0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, |
|
||||
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 |
|
||||
}; |
|
||||
int t, t4; /* Loop counter */ |
|
||||
uint32_t temp1, temp2; /* Temporary word value */ |
|
||||
uint32_t W[64]; /* Word sequence */ |
|
||||
uint32_t A, B, C, D, E, F, G, H; /* Word buffers */ |
|
||||
|
|
||||
/*
|
|
||||
* Initialize the first 16 words in the array W |
|
||||
*/ |
|
||||
for (t = t4 = 0; t < 16; t++, t4 += 4) |
|
||||
W[t] = (((uint32_t)context->Message_Block[t4]) << 24) | |
|
||||
(((uint32_t)context->Message_Block[t4 + 1]) << 16) | |
|
||||
(((uint32_t)context->Message_Block[t4 + 2]) << 8) | |
|
||||
(((uint32_t)context->Message_Block[t4 + 3])); |
|
||||
for (t = 16; t < 64; t++) |
|
||||
W[t] = SHA256_sigma1(W[t-2]) + W[t-7] + |
|
||||
SHA256_sigma0(W[t-15]) + W[t-16]; |
|
||||
|
|
||||
A = context->Intermediate_Hash[0]; |
|
||||
B = context->Intermediate_Hash[1]; |
|
||||
C = context->Intermediate_Hash[2]; |
|
||||
D = context->Intermediate_Hash[3]; |
|
||||
E = context->Intermediate_Hash[4]; |
|
||||
F = context->Intermediate_Hash[5]; |
|
||||
G = context->Intermediate_Hash[6]; |
|
||||
H = context->Intermediate_Hash[7]; |
|
||||
|
|
||||
for (t = 0; t < 64; t++) { |
|
||||
temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t]; |
|
||||
temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C); |
|
||||
H = G; |
|
||||
G = F; |
|
||||
F = E; |
|
||||
E = D + temp1; |
|
||||
D = C; |
|
||||
C = B; |
|
||||
B = A; |
|
||||
A = temp1 + temp2; |
|
||||
} |
|
||||
|
|
||||
context->Intermediate_Hash[0] += A; |
|
||||
context->Intermediate_Hash[1] += B; |
|
||||
context->Intermediate_Hash[2] += C; |
|
||||
context->Intermediate_Hash[3] += D; |
|
||||
context->Intermediate_Hash[4] += E; |
|
||||
context->Intermediate_Hash[5] += F; |
|
||||
context->Intermediate_Hash[6] += G; |
|
||||
context->Intermediate_Hash[7] += H; |
|
||||
|
|
||||
context->Message_Block_Index = 0; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA224_256Finalize |
|
||||
* |
|
||||
* Description: |
|
||||
* This helper function finishes off the digest calculations. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The SHA context to update. |
|
||||
* Pad_Byte: [in] |
|
||||
* The last byte to add to the message block before the 0-padding |
|
||||
* and length. This will contain the last bits of the message |
|
||||
* followed by another single bit. If the message was an |
|
||||
* exact multiple of 8-bits long, Pad_Byte will be 0x80. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
static void SHA224_256Finalize(SHA256Context *context, |
|
||||
uint8_t Pad_Byte) |
|
||||
{ |
|
||||
int i; |
|
||||
SHA224_256PadMessage(context, Pad_Byte); |
|
||||
/* message may be sensitive, so clear it out */ |
|
||||
for (i = 0; i < SHA256_Message_Block_Size; ++i) |
|
||||
context->Message_Block[i] = 0; |
|
||||
context->Length_High = 0; /* and clear length */ |
|
||||
context->Length_Low = 0; |
|
||||
context->Computed = 1; |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA224_256PadMessage |
|
||||
* |
|
||||
* Description: |
|
||||
* According to the standard, the message must be padded to the next |
|
||||
* even multiple of 512 bits. The first padding bit must be a '1'. |
|
||||
* The last 64 bits represent the length of the original message. |
|
||||
* All bits in between should be 0. This helper function will pad |
|
||||
* the message according to those rules by filling the |
|
||||
* Message_Block array accordingly. When it returns, it can be |
|
||||
* assumed that the message digest has been computed. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to pad. |
|
||||
* Pad_Byte: [in] |
|
||||
* The last byte to add to the message block before the 0-padding |
|
||||
* and length. This will contain the last bits of the message |
|
||||
* followed by another single bit. If the message was an |
|
||||
* exact multiple of 8-bits long, Pad_Byte will be 0x80. |
|
||||
* |
|
||||
* Returns: |
|
||||
* Nothing. |
|
||||
*/ |
|
||||
static void SHA224_256PadMessage(SHA256Context *context, |
|
||||
uint8_t Pad_Byte) |
|
||||
{ |
|
||||
/*
|
|
||||
* Check to see if the current message block is too small to hold |
|
||||
* the initial padding bits and length. If so, we will pad the |
|
||||
* block, process it, and then continue padding into a second |
|
||||
* block. |
|
||||
*/ |
|
||||
if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) { |
|
||||
context->Message_Block[context->Message_Block_Index++] = Pad_Byte; |
|
||||
while (context->Message_Block_Index < SHA256_Message_Block_Size) |
|
||||
context->Message_Block[context->Message_Block_Index++] = 0; |
|
||||
SHA224_256ProcessMessageBlock(context); |
|
||||
} else |
|
||||
context->Message_Block[context->Message_Block_Index++] = Pad_Byte; |
|
||||
|
|
||||
while (context->Message_Block_Index < (SHA256_Message_Block_Size-8)) |
|
||||
context->Message_Block[context->Message_Block_Index++] = 0; |
|
||||
|
|
||||
/*
|
|
||||
* Store the message length as the last 8 octets |
|
||||
*/ |
|
||||
context->Message_Block[56] = (uint8_t)(context->Length_High >> 24); |
|
||||
context->Message_Block[57] = (uint8_t)(context->Length_High >> 16); |
|
||||
context->Message_Block[58] = (uint8_t)(context->Length_High >> 8); |
|
||||
context->Message_Block[59] = (uint8_t)(context->Length_High); |
|
||||
context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24); |
|
||||
context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16); |
|
||||
context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8); |
|
||||
context->Message_Block[63] = (uint8_t)(context->Length_Low); |
|
||||
|
|
||||
SHA224_256ProcessMessageBlock(context); |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* SHA224_256ResultN |
|
||||
* |
|
||||
* Description: |
|
||||
* This helper function will return the 224-bit or 256-bit message |
|
||||
* digest into the Message_Digest array provided by the caller. |
|
||||
* NOTE: |
|
||||
* The first octet of hash is stored in the element with index 0, |
|
||||
* the last octet of hash in the element with index 27/31. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to use to calculate the SHA hash. |
|
||||
* Message_Digest[ ]: [out] |
|
||||
* Where the digest is returned. |
|
||||
* HashSize: [in] |
|
||||
* The size of the hash, either 28 or 32. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
static int SHA224_256ResultN(SHA256Context *context, |
|
||||
uint8_t Message_Digest[ ], int HashSize) |
|
||||
{ |
|
||||
int i; |
|
||||
|
|
||||
if (!context) return shaNull; |
|
||||
if (!Message_Digest) return shaNull; |
|
||||
if (context->Corrupted) return context->Corrupted; |
|
||||
|
|
||||
if (!context->Computed) |
|
||||
SHA224_256Finalize(context, 0x80); |
|
||||
|
|
||||
for (i = 0; i < HashSize; ++i) |
|
||||
Message_Digest[i] = (uint8_t) |
|
||||
(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) )); |
|
||||
|
|
||||
return shaSuccess; |
|
||||
} |
|
||||
|
|
||||
@ -1,191 +0,0 @@ |
|||||
/**************************** usha.c ***************************/ |
|
||||
/***************** See RFC 6234 for details. *******************/ |
|
||||
/* Copyright (c) 2011 IETF Trust and the persons identified as */ |
|
||||
/* authors of the code. All rights reserved. */ |
|
||||
/* See sha.h for terms of use and redistribution. */ |
|
||||
|
|
||||
/*
|
|
||||
* Description: |
|
||||
* This file implements a unified interface to the SHA algorithms. |
|
||||
*/ |
|
||||
|
|
||||
#include "sha.h" |
|
||||
|
|
||||
/*
|
|
||||
* USHAReset |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will initialize the SHA Context in preparation |
|
||||
* for computing a new SHA message digest. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to reset. |
|
||||
* whichSha: [in] |
|
||||
* Selects which SHA reset to call |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int USHAReset(USHAContext *context, enum SHAversion whichSha) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
context->whichSha = whichSha; |
|
||||
switch (whichSha) { |
|
||||
case SHA224: return SHA224Reset((SHA224Context*)&context->ctx); |
|
||||
case SHA256: return SHA256Reset((SHA256Context*)&context->ctx); |
|
||||
default: return shaBadParam; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* USHAInput |
|
||||
* |
|
||||
* Description: |
|
||||
* This function accepts an array of octets as the next portion |
|
||||
* of the message. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The SHA context to update. |
|
||||
* message_array: [in] |
|
||||
* An array of octets representing the next portion of |
|
||||
* the message. |
|
||||
* length: [in] |
|
||||
* The length of the message in message_array. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int USHAInput(USHAContext *context, |
|
||||
const uint8_t *bytes, unsigned int bytecount) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
switch (context->whichSha) { |
|
||||
case SHA224: |
|
||||
return SHA224Input((SHA224Context*)&context->ctx, bytes, |
|
||||
bytecount); |
|
||||
case SHA256: |
|
||||
return SHA256Input((SHA256Context*)&context->ctx, bytes, |
|
||||
bytecount); |
|
||||
default: return shaBadParam; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* USHAFinalBits |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will add in any final bits of the message. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The SHA context to update. |
|
||||
* message_bits: [in] |
|
||||
* The final bits of the message, in the upper portion of the |
|
||||
* byte. (Use 0b###00000 instead of 0b00000### to input the |
|
||||
* three bits ###.) |
|
||||
* length: [in] |
|
||||
* The number of bits in message_bits, between 1 and 7. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
*/ |
|
||||
int USHAFinalBits(USHAContext *context, |
|
||||
uint8_t bits, unsigned int bit_count) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
switch (context->whichSha) { |
|
||||
case SHA224: |
|
||||
return SHA224FinalBits((SHA224Context*)&context->ctx, bits, |
|
||||
bit_count); |
|
||||
case SHA256: |
|
||||
return SHA256FinalBits((SHA256Context*)&context->ctx, bits, |
|
||||
bit_count); |
|
||||
default: return shaBadParam; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* USHAResult |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will return the message digest of the appropriate |
|
||||
* bit size, as returned by USHAHashSizeBits(whichSHA) for the |
|
||||
* 'whichSHA' value used in the preceeding call to USHAReset, |
|
||||
* into the Message_Digest array provided by the caller. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* context: [in/out] |
|
||||
* The context to use to calculate the SHA-1 hash. |
|
||||
* Message_Digest: [out] |
|
||||
* Where the digest is returned. |
|
||||
* |
|
||||
* Returns: |
|
||||
* sha Error Code. |
|
||||
* |
|
||||
*/ |
|
||||
int USHAResult(USHAContext *context, |
|
||||
uint8_t Message_Digest[USHAMaxHashSize]) |
|
||||
{ |
|
||||
if (!context) return shaNull; |
|
||||
switch (context->whichSha) { |
|
||||
case SHA224: |
|
||||
return SHA224Result((SHA224Context*)&context->ctx, |
|
||||
Message_Digest); |
|
||||
case SHA256: |
|
||||
return SHA256Result((SHA256Context*)&context->ctx, |
|
||||
Message_Digest); |
|
||||
default: return shaBadParam; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* USHABlockSize |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will return the blocksize for the given SHA |
|
||||
* algorithm. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* whichSha: |
|
||||
* which SHA algorithm to query |
|
||||
* |
|
||||
* Returns: |
|
||||
* block size |
|
||||
* |
|
||||
*/ |
|
||||
int USHABlockSize(enum SHAversion whichSha) |
|
||||
{ |
|
||||
switch (whichSha) { |
|
||||
case SHA224: return SHA224_Message_Block_Size; |
|
||||
default: |
|
||||
case SHA256: return SHA256_Message_Block_Size; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/*
|
|
||||
* USHAHashSize |
|
||||
* |
|
||||
* Description: |
|
||||
* This function will return the hashsize for the given SHA |
|
||||
* algorithm. |
|
||||
* |
|
||||
* Parameters: |
|
||||
* whichSha: |
|
||||
* which SHA algorithm to query |
|
||||
* |
|
||||
* Returns: |
|
||||
* hash size |
|
||||
* |
|
||||
*/ |
|
||||
int USHAHashSize(enum SHAversion whichSha) |
|
||||
{ |
|
||||
switch (whichSha) { |
|
||||
case SHA224: return SHA224HashSize; |
|
||||
default: |
|
||||
case SHA256: return SHA256HashSize; |
|
||||
} |
|
||||
} |
|
||||
File diff suppressed because it is too large
@ -1,305 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "nfqws.h" |
|
||||
#include "checksum.h" |
|
||||
#include "packet_queue.h" |
|
||||
#include "pools.h" |
|
||||
|
|
||||
#include <stdint.h> |
|
||||
#include <stdbool.h> |
|
||||
#include <sys/types.h> |
|
||||
#include <sys/socket.h> |
|
||||
#include <sys/param.h> |
|
||||
#include <netinet/in.h> |
|
||||
|
|
||||
#define __FAVOR_BSD |
|
||||
#include <netinet/ip.h> |
|
||||
#include <netinet/ip6.h> |
|
||||
#include <netinet/tcp.h> |
|
||||
#include <netinet/udp.h> |
|
||||
|
|
||||
#ifndef IPV6_FREEBIND |
|
||||
#define IPV6_FREEBIND 78 |
|
||||
#endif |
|
||||
|
|
||||
#ifdef __CYGWIN__ |
|
||||
#define INITGUID |
|
||||
#include "windivert/windivert.h" |
|
||||
#endif |
|
||||
|
|
||||
#ifndef IPPROTO_DIVERT |
|
||||
#define IPPROTO_DIVERT 258 |
|
||||
#endif |
|
||||
|
|
||||
#ifndef AF_DIVERT |
|
||||
#define AF_DIVERT 44 /* divert(4) */ |
|
||||
#endif |
|
||||
#ifndef PF_DIVERT |
|
||||
#define PF_DIVERT AF_DIVERT |
|
||||
#endif |
|
||||
|
|
||||
// returns netorder value
|
|
||||
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment); |
|
||||
uint32_t net16_add(uint16_t netorder_value, uint16_t cpuorder_increment); |
|
||||
|
|
||||
#define FOOL_NONE 0x00 |
|
||||
#define FOOL_MD5SIG 0x01 |
|
||||
#define FOOL_BADSUM 0x02 |
|
||||
#define FOOL_TS 0x04 |
|
||||
#define FOOL_BADSEQ 0x08 |
|
||||
#define FOOL_HOPBYHOP 0x10 |
|
||||
#define FOOL_HOPBYHOP2 0x20 |
|
||||
#define FOOL_DESTOPT 0x40 |
|
||||
#define FOOL_IPFRAG1 0x80 |
|
||||
#define FOOL_DATANOACK 0x100 |
|
||||
|
|
||||
#define SCALE_NONE ((uint8_t)-1) |
|
||||
|
|
||||
#define VERDICT_PASS 0 |
|
||||
#define VERDICT_MODIFY 1 |
|
||||
#define VERDICT_DROP 2 |
|
||||
#define VERDICT_MASK 3 |
|
||||
#define VERDICT_NOCSUM 4 |
|
||||
#define VERDICT_GARBAGE 8 |
|
||||
|
|
||||
#define IP4_TOS(ip_header) (ip_header ? ip_header->ip_tos : 0) |
|
||||
#define IP4_IP_ID(ip_header) (ip_header ? ip_header->ip_id : 0) |
|
||||
#define IP6_FLOW(ip6_header) (ip6_header ? ip6_header->ip6_ctlun.ip6_un1.ip6_un1_flow : 0) |
|
||||
|
|
||||
// seq and wsize have network byte order
|
|
||||
bool prepare_tcp_segment4( |
|
||||
const struct sockaddr_in *src, const struct sockaddr_in *dst, |
|
||||
uint16_t tcp_flags, |
|
||||
bool sack, |
|
||||
uint16_t nmss, |
|
||||
uint32_t nseq, uint32_t nack_seq, |
|
||||
uint16_t nwsize, |
|
||||
uint8_t scale_factor, |
|
||||
uint32_t *timestamps, |
|
||||
bool DF, |
|
||||
uint8_t ttl, |
|
||||
uint8_t tos, |
|
||||
uint16_t ip_id, |
|
||||
uint32_t fooling, |
|
||||
uint32_t ts_increment, |
|
||||
uint32_t badseq_increment, |
|
||||
uint32_t badseq_ack_increment, |
|
||||
const void *data, uint16_t len, |
|
||||
uint8_t *buf, size_t *buflen); |
|
||||
bool prepare_tcp_segment6( |
|
||||
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, |
|
||||
uint16_t tcp_flags, |
|
||||
bool sack, |
|
||||
uint16_t nmss, |
|
||||
uint32_t nseq, uint32_t nack_seq, |
|
||||
uint16_t nwsize, |
|
||||
uint8_t scale_factor, |
|
||||
uint32_t *timestamps, |
|
||||
uint8_t ttl, |
|
||||
uint32_t flow_label, |
|
||||
uint32_t fooling, |
|
||||
uint32_t ts_increment, |
|
||||
uint32_t badseq_increment, |
|
||||
uint32_t badseq_ack_increment, |
|
||||
const void *data, uint16_t len, |
|
||||
uint8_t *buf, size_t *buflen); |
|
||||
bool prepare_tcp_segment( |
|
||||
const struct sockaddr *src, const struct sockaddr *dst, |
|
||||
uint16_t tcp_flags, |
|
||||
bool sack, |
|
||||
uint16_t nmss, |
|
||||
uint32_t nseq, uint32_t nack_seq, |
|
||||
uint16_t nwsize, |
|
||||
uint8_t scale_factor, |
|
||||
uint32_t *timestamps, |
|
||||
bool DF, |
|
||||
uint8_t ttl, |
|
||||
uint8_t tos, |
|
||||
uint16_t ip_id, |
|
||||
uint32_t flow_label, |
|
||||
uint32_t fooling, |
|
||||
uint32_t ts_increment, |
|
||||
uint32_t badseq_increment, |
|
||||
uint32_t badseq_ack_increment, |
|
||||
const void *data, uint16_t len, |
|
||||
uint8_t *buf, size_t *buflen); |
|
||||
|
|
||||
|
|
||||
bool prepare_udp_segment4( |
|
||||
const struct sockaddr_in *src, const struct sockaddr_in *dst, |
|
||||
bool DF, |
|
||||
uint8_t ttl, |
|
||||
uint8_t tos, |
|
||||
uint16_t ip_id, |
|
||||
uint32_t fooling, |
|
||||
const uint8_t *padding, size_t padding_size, |
|
||||
int padlen, |
|
||||
const void *data, uint16_t len, |
|
||||
uint8_t *buf, size_t *buflen); |
|
||||
bool prepare_udp_segment6( |
|
||||
const struct sockaddr_in6 *src, const struct sockaddr_in6 *dst, |
|
||||
uint8_t ttl, |
|
||||
uint32_t flow_label, |
|
||||
uint32_t fooling, |
|
||||
const uint8_t *padding, size_t padding_size, |
|
||||
int padlen, |
|
||||
const void *data, uint16_t len, |
|
||||
uint8_t *buf, size_t *buflen); |
|
||||
bool prepare_udp_segment( |
|
||||
const struct sockaddr *src, const struct sockaddr *dst, |
|
||||
bool DF, |
|
||||
uint8_t ttl, |
|
||||
uint8_t tos, |
|
||||
uint16_t ip_id, |
|
||||
uint32_t flow_label, |
|
||||
uint32_t fooling, |
|
||||
const uint8_t *padding, size_t padding_size, |
|
||||
int padlen, |
|
||||
const void *data, uint16_t len, |
|
||||
uint8_t *buf, size_t *buflen); |
|
||||
|
|
||||
bool ip6_insert_simple_hdr(uint8_t type, uint8_t *data_pkt, size_t len_pkt, uint8_t *buf, size_t *buflen); |
|
||||
|
|
||||
// ipv4: ident==-1 - copy ip_id from original ipv4 packet
|
|
||||
bool ip_frag4( |
|
||||
const uint8_t *pkt, size_t pkt_size, |
|
||||
size_t frag_pos, uint32_t ident, |
|
||||
uint8_t *pkt1, size_t *pkt1_size, |
|
||||
uint8_t *pkt2, size_t *pkt2_size); |
|
||||
bool ip_frag6( |
|
||||
const uint8_t *pkt, size_t pkt_size, |
|
||||
size_t frag_pos, uint32_t ident, |
|
||||
uint8_t *pkt1, size_t *pkt1_size, |
|
||||
uint8_t *pkt2, size_t *pkt2_size); |
|
||||
bool ip_frag( |
|
||||
const uint8_t *pkt, size_t pkt_size, |
|
||||
size_t frag_pos, uint32_t ident, |
|
||||
uint8_t *pkt1, size_t *pkt1_size, |
|
||||
uint8_t *pkt2, size_t *pkt2_size); |
|
||||
|
|
||||
bool rewrite_ttl(struct ip *ip, struct ip6_hdr *ip6, uint8_t ttl); |
|
||||
uint16_t get_tcp_flags(const struct tcphdr *tcp); |
|
||||
void apply_tcp_flags(struct tcphdr *tcp, uint16_t fl); |
|
||||
|
|
||||
void extract_ports(const struct tcphdr *tcphdr, const struct udphdr *udphdr, uint8_t *proto, uint16_t *sport, uint16_t *dport); |
|
||||
void extract_endpoints(const struct ip *ip,const struct ip6_hdr *ip6hdr,const struct tcphdr *tcphdr,const struct udphdr *udphdr, struct sockaddr_storage *src, struct sockaddr_storage *dst); |
|
||||
uint8_t *tcp_find_option(struct tcphdr *tcp, uint8_t kind); |
|
||||
uint32_t *tcp_find_timestamps(struct tcphdr *tcp); |
|
||||
uint8_t tcp_find_scale_factor(const struct tcphdr *tcp); |
|
||||
uint16_t tcp_find_mss(struct tcphdr *tcp); |
|
||||
bool tcp_has_sack(struct tcphdr *tcp); |
|
||||
|
|
||||
bool tcp_has_fastopen(const struct tcphdr *tcp); |
|
||||
|
|
||||
bool ip_has_df(const struct ip *ip); |
|
||||
|
|
||||
#ifdef __CYGWIN__ |
|
||||
extern uint32_t w_win32_error; |
|
||||
|
|
||||
bool win_dark_init(const struct str_list_head *ssid_filter, const struct str_list_head *nlm_filter); |
|
||||
bool win_dark_deinit(void); |
|
||||
bool logical_net_filter_match(void); |
|
||||
bool nlm_list(bool bAll); |
|
||||
bool windivert_init(const char *filter); |
|
||||
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa); |
|
||||
bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa); |
|
||||
#else |
|
||||
// should pre-do it if dropping privileges. otherwise its not necessary
|
|
||||
bool rawsend_preinit(bool bind_fix4, bool bind_fix6); |
|
||||
#endif |
|
||||
|
|
||||
// auto creates internal socket and uses it for subsequent calls
|
|
||||
bool rawsend(const struct sockaddr* dst,uint32_t fwmark,const char *ifout,const void *data,size_t len); |
|
||||
bool rawsend_rp(const struct rawpacket *rp); |
|
||||
// return trues if all packets were send successfully
|
|
||||
bool rawsend_queue(struct rawpacket_tailhead *q); |
|
||||
// cleans up socket autocreated by rawsend
|
|
||||
void rawsend_cleanup(void); |
|
||||
|
|
||||
#ifdef BSD |
|
||||
int socket_divert(sa_family_t family); |
|
||||
#endif |
|
||||
|
|
||||
const char *proto_name(uint8_t proto); |
|
||||
uint16_t family_from_proto(uint8_t l3proto); |
|
||||
void print_ip(const struct ip *ip); |
|
||||
void print_ip6hdr(const struct ip6_hdr *ip6hdr, uint8_t proto); |
|
||||
void print_tcphdr(const struct tcphdr *tcphdr); |
|
||||
void print_udphdr(const struct udphdr *udphdr); |
|
||||
void str_ip(char *s, size_t s_len, const struct ip *ip); |
|
||||
void str_ip6hdr(char *s, size_t s_len, const struct ip6_hdr *ip6hdr, uint8_t proto); |
|
||||
void str_srcdst_ip6(char *s, size_t s_len, const void *saddr,const void *daddr); |
|
||||
void str_tcphdr(char *s, size_t s_len, const struct tcphdr *tcphdr); |
|
||||
void str_udphdr(char *s, size_t s_len, const struct udphdr *udphdr); |
|
||||
|
|
||||
bool proto_check_ipv4(const uint8_t *data, size_t len); |
|
||||
void proto_skip_ipv4(uint8_t **data, size_t *len); |
|
||||
bool proto_check_ipv6(const uint8_t *data, size_t len); |
|
||||
void proto_skip_ipv6(uint8_t **data, size_t *len, uint8_t *proto_type, uint8_t **last_header_type); |
|
||||
bool proto_check_tcp(const uint8_t *data, size_t len); |
|
||||
void proto_skip_tcp(uint8_t **data, size_t *len); |
|
||||
bool proto_check_udp(const uint8_t *data, size_t len); |
|
||||
void proto_skip_udp(uint8_t **data, size_t *len); |
|
||||
struct dissect |
|
||||
{ |
|
||||
uint8_t *data_pkt; |
|
||||
size_t len_pkt; |
|
||||
struct ip *ip; |
|
||||
struct ip6_hdr *ip6; |
|
||||
uint8_t proto; |
|
||||
struct tcphdr *tcp; |
|
||||
struct udphdr *udp; |
|
||||
size_t transport_len; |
|
||||
uint8_t *data_payload; |
|
||||
size_t len_payload; |
|
||||
}; |
|
||||
void proto_dissect_l3l4(uint8_t *data, size_t len,struct dissect *dis); |
|
||||
|
|
||||
bool tcp_synack_segment(const struct tcphdr *tcphdr); |
|
||||
bool tcp_syn_segment(const struct tcphdr *tcphdr); |
|
||||
bool tcp_ack_segment(const struct tcphdr *tcphdr); |
|
||||
// scale_factor=SCALE_NONE - do not change
|
|
||||
void tcp_rewrite_wscale(struct tcphdr *tcp, uint8_t scale_factor); |
|
||||
void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_factor); |
|
||||
|
|
||||
typedef struct |
|
||||
{ |
|
||||
int8_t delta; |
|
||||
uint8_t min, max; |
|
||||
} autottl; |
|
||||
#define AUTOTTL_ENABLED(a) ((a).delta || (a).min || (a).max) |
|
||||
|
|
||||
uint8_t hop_count_guess(uint8_t ttl); |
|
||||
uint8_t autottl_eval(uint8_t hop_count, const autottl *attl); |
|
||||
void do_nat(bool bOutbound, struct ip *ip, struct ip6_hdr *ip6, struct tcphdr *tcphdr, struct udphdr *udphdr, const struct sockaddr_in *target4, const struct sockaddr_in6 *target6); |
|
||||
|
|
||||
void verdict_tcp_csum_fix(uint8_t verdict, struct tcphdr *tcphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr); |
|
||||
void verdict_udp_csum_fix(uint8_t verdict, struct udphdr *udphdr, size_t transport_len, struct ip *ip, struct ip6_hdr *ip6hdr); |
|
||||
|
|
||||
void dbgprint_socket_buffers(int fd); |
|
||||
bool set_socket_buffers(int fd, int rcvbuf, int sndbuf); |
|
||||
|
|
||||
|
|
||||
#ifdef HAS_FILTER_SSID |
|
||||
|
|
||||
struct wlan_interface |
|
||||
{ |
|
||||
int ifindex; |
|
||||
char ifname[IFNAMSIZ], ssid[33]; |
|
||||
}; |
|
||||
#define WLAN_INTERFACE_MAX 16 |
|
||||
struct wlan_interface_collection |
|
||||
{ |
|
||||
int count; |
|
||||
struct wlan_interface wlan[WLAN_INTERFACE_MAX]; |
|
||||
}; |
|
||||
|
|
||||
extern struct wlan_interface_collection wlans; |
|
||||
|
|
||||
void wlan_info_deinit(void); |
|
||||
bool wlan_info_init(void); |
|
||||
bool wlan_info_get_rate_limited(void); |
|
||||
const char *wlan_ssid_search_ifname(const char *ifname); |
|
||||
const char *wlan_ssid_search_ifidx(int ifidx); |
|
||||
|
|
||||
#endif |
|
||||
File diff suppressed because it is too large
@ -1,56 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include "darkmagic.h" |
|
||||
|
|
||||
#include <stdint.h> |
|
||||
#include <stdbool.h> |
|
||||
|
|
||||
#define __FAVOR_BSD |
|
||||
#include <netinet/ip.h> |
|
||||
#include <netinet/ip6.h> |
|
||||
#include <netinet/tcp.h> |
|
||||
#include <netinet/in.h> |
|
||||
|
|
||||
#ifdef __linux__ |
|
||||
#define DPI_DESYNC_FWMARK_DEFAULT 0x40000000 |
|
||||
#else |
|
||||
#define DPI_DESYNC_FWMARK_DEFAULT 512 |
|
||||
#endif |
|
||||
|
|
||||
#define DPI_DESYNC_MAX_FAKE_LEN 9216 |
|
||||
|
|
||||
enum dpi_desync_mode { |
|
||||
DESYNC_NONE=0, |
|
||||
DESYNC_INVALID, |
|
||||
DESYNC_FAKE, |
|
||||
DESYNC_FAKE_KNOWN, |
|
||||
DESYNC_RST, |
|
||||
DESYNC_RSTACK, |
|
||||
DESYNC_SYNACK, |
|
||||
DESYNC_SYNDATA, |
|
||||
DESYNC_FAKEDSPLIT, |
|
||||
DESYNC_FAKEDDISORDER, |
|
||||
DESYNC_MULTISPLIT, |
|
||||
DESYNC_MULTIDISORDER, |
|
||||
DESYNC_HOSTFAKESPLIT, |
|
||||
DESYNC_IPFRAG2, |
|
||||
DESYNC_HOPBYHOP, |
|
||||
DESYNC_DESTOPT, |
|
||||
DESYNC_IPFRAG1, |
|
||||
DESYNC_UDPLEN, |
|
||||
DESYNC_TAMPER |
|
||||
}; |
|
||||
|
|
||||
extern const char *fake_http_request_default; |
|
||||
extern const uint8_t fake_tls_clienthello_default[680]; |
|
||||
void randomize_default_tls_payload(uint8_t *p); |
|
||||
|
|
||||
enum dpi_desync_mode desync_mode_from_string(const char *s); |
|
||||
bool desync_valid_zero_stage(enum dpi_desync_mode mode); |
|
||||
bool desync_valid_first_stage(enum dpi_desync_mode mode); |
|
||||
bool desync_only_first_stage(enum dpi_desync_mode mode); |
|
||||
bool desync_valid_second_stage(enum dpi_desync_mode mode); |
|
||||
bool desync_valid_second_stage_tcp(enum dpi_desync_mode mode); |
|
||||
bool desync_valid_second_stage_udp(enum dpi_desync_mode mode); |
|
||||
|
|
||||
uint8_t dpi_desync_packet(uint32_t fwmark, const char *ifin, const char *ifout, uint8_t *data_pkt, size_t *len_pkt); |
|
||||
@ -1,79 +0,0 @@ |
|||||
#include "gzip.h" |
|
||||
#include <stdio.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <string.h> |
|
||||
|
|
||||
#define ZCHUNK 16384 |
|
||||
#define BUFMIN 128 |
|
||||
#define BUFCHUNK (1024*128) |
|
||||
|
|
||||
int z_readfile(FILE *F, char **buf, size_t *size) |
|
||||
{ |
|
||||
z_stream zs; |
|
||||
int r; |
|
||||
unsigned char in[ZCHUNK]; |
|
||||
size_t bufsize; |
|
||||
void *newbuf; |
|
||||
|
|
||||
memset(&zs, 0, sizeof(zs)); |
|
||||
|
|
||||
*buf = NULL; |
|
||||
bufsize = *size = 0; |
|
||||
|
|
||||
r = inflateInit2(&zs, 47); |
|
||||
if (r != Z_OK) return r; |
|
||||
|
|
||||
do |
|
||||
{ |
|
||||
zs.avail_in = fread(in, 1, sizeof(in), F); |
|
||||
if (ferror(F)) |
|
||||
{ |
|
||||
r = Z_ERRNO; |
|
||||
goto zerr; |
|
||||
} |
|
||||
if (!zs.avail_in) break; |
|
||||
zs.next_in = in; |
|
||||
do |
|
||||
{ |
|
||||
if ((bufsize - *size) < BUFMIN) |
|
||||
{ |
|
||||
bufsize += BUFCHUNK; |
|
||||
newbuf = *buf ? realloc(*buf, bufsize) : malloc(bufsize); |
|
||||
if (!newbuf) |
|
||||
{ |
|
||||
r = Z_MEM_ERROR; |
|
||||
goto zerr; |
|
||||
} |
|
||||
*buf = newbuf; |
|
||||
} |
|
||||
zs.avail_out = bufsize - *size; |
|
||||
zs.next_out = (unsigned char*)(*buf + *size); |
|
||||
r = inflate(&zs, Z_NO_FLUSH); |
|
||||
if (r != Z_OK && r != Z_STREAM_END) goto zerr; |
|
||||
*size = bufsize - zs.avail_out; |
|
||||
} while (r == Z_OK && zs.avail_in); |
|
||||
} while (r == Z_OK); |
|
||||
|
|
||||
if (*size < bufsize) |
|
||||
{ |
|
||||
// free extra space
|
|
||||
if ((newbuf = realloc(*buf, *size))) *buf = newbuf; |
|
||||
} |
|
||||
|
|
||||
inflateEnd(&zs); |
|
||||
return Z_OK; |
|
||||
|
|
||||
zerr: |
|
||||
inflateEnd(&zs); |
|
||||
free(*buf); |
|
||||
*buf = NULL; |
|
||||
return r; |
|
||||
} |
|
||||
|
|
||||
bool is_gzip(FILE* F) |
|
||||
{ |
|
||||
unsigned char magic[2]; |
|
||||
bool b = !fseek(F, 0, SEEK_SET) && fread(magic, 1, 2, F) == 2 && magic[0] == 0x1F && magic[1] == 0x8B; |
|
||||
fseek(F, 0, SEEK_SET); |
|
||||
return b; |
|
||||
} |
|
||||
@ -1,8 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <stdio.h> |
|
||||
#include <zlib.h> |
|
||||
#include <stdbool.h> |
|
||||
|
|
||||
int z_readfile(FILE *F,char **buf,size_t *size); |
|
||||
bool is_gzip(FILE* F); |
|
||||
@ -1,550 +0,0 @@ |
|||||
#define _GNU_SOURCE |
|
||||
|
|
||||
#include "helpers.h" |
|
||||
|
|
||||
#include <stdio.h> |
|
||||
#include <string.h> |
|
||||
#include <unistd.h> |
|
||||
#include <stdlib.h> |
|
||||
#include <ctype.h> |
|
||||
#include <sys/stat.h> |
|
||||
#include <libgen.h> |
|
||||
#include <fcntl.h> |
|
||||
|
|
||||
int unique_size_t(size_t *pu, int ct) |
|
||||
{ |
|
||||
size_t i, j, u; |
|
||||
for (i = j = 0; j < ct; i++) |
|
||||
{ |
|
||||
u = pu[j++]; |
|
||||
for (; j < ct && pu[j] == u; j++); |
|
||||
pu[i] = u; |
|
||||
} |
|
||||
return i; |
|
||||
} |
|
||||
static int cmp_size_t(const void * a, const void * b) |
|
||||
{ |
|
||||
return *(size_t*)a < *(size_t*)b ? -1 : *(size_t*)a > *(size_t*)b; |
|
||||
} |
|
||||
void qsort_size_t(size_t *array,size_t ct) |
|
||||
{ |
|
||||
qsort(array,ct,sizeof(*array),cmp_size_t); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void rtrim(char *s) |
|
||||
{ |
|
||||
if (s) |
|
||||
for (char *p = s + strlen(s) - 1; p >= s && (*p == '\n' || *p == '\r'); p--) *p = '\0'; |
|
||||
} |
|
||||
|
|
||||
void replace_char(char *s, char from, char to) |
|
||||
{ |
|
||||
for(;*s;s++) if (*s==from) *s=to; |
|
||||
} |
|
||||
|
|
||||
char *strncasestr(const char *s, const char *find, size_t slen) |
|
||||
{ |
|
||||
char c, sc; |
|
||||
size_t len; |
|
||||
|
|
||||
if ((c = *find++) != '\0') |
|
||||
{ |
|
||||
len = strlen(find); |
|
||||
do |
|
||||
{ |
|
||||
do |
|
||||
{ |
|
||||
if (slen-- < 1 || (sc = *s++) == '\0') return NULL; |
|
||||
} while (toupper(c) != toupper(sc)); |
|
||||
if (len > slen) return NULL; |
|
||||
} while (strncasecmp(s, find, len) != 0); |
|
||||
s--; |
|
||||
} |
|
||||
return (char *)s; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
bool load_file(const char *filename, void *buffer, size_t *buffer_size) |
|
||||
{ |
|
||||
FILE *F; |
|
||||
|
|
||||
F = fopen(filename, "rb"); |
|
||||
if (!F) return false; |
|
||||
|
|
||||
*buffer_size = fread(buffer, 1, *buffer_size, F); |
|
||||
if (ferror(F)) |
|
||||
{ |
|
||||
fclose(F); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
fclose(F); |
|
||||
return true; |
|
||||
} |
|
||||
bool load_file_nonempty(const char *filename, void *buffer, size_t *buffer_size) |
|
||||
{ |
|
||||
bool b = load_file(filename, buffer, buffer_size); |
|
||||
return b && *buffer_size; |
|
||||
} |
|
||||
bool save_file(const char *filename, const void *buffer, size_t buffer_size) |
|
||||
{ |
|
||||
FILE *F; |
|
||||
|
|
||||
F = fopen(filename, "wb"); |
|
||||
if (!F) return false; |
|
||||
|
|
||||
fwrite(buffer, 1, buffer_size, F); |
|
||||
if (ferror(F)) |
|
||||
{ |
|
||||
fclose(F); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
fclose(F); |
|
||||
return true; |
|
||||
} |
|
||||
bool append_to_list_file(const char *filename, const char *s) |
|
||||
{ |
|
||||
FILE *F = fopen(filename,"at"); |
|
||||
if (!F) return false; |
|
||||
bool bOK = fprintf(F,"%s\n",s)>0; |
|
||||
fclose(F); |
|
||||
return bOK; |
|
||||
} |
|
||||
|
|
||||
void expand_bits(void *target, const void *source, unsigned int source_bitlen, unsigned int target_bytelen) |
|
||||
{ |
|
||||
unsigned int target_bitlen = target_bytelen<<3; |
|
||||
unsigned int bitlen = target_bitlen<source_bitlen ? target_bitlen : source_bitlen; |
|
||||
unsigned int bytelen = bitlen>>3; |
|
||||
|
|
||||
if ((target_bytelen-bytelen)>=1) memset(target+bytelen,0,target_bytelen-bytelen); |
|
||||
memcpy(target,source,bytelen); |
|
||||
if ((bitlen &= 7)) ((uint8_t*)target)[bytelen] = ((uint8_t*)source)[bytelen] & (~((1 << (8-bitlen)) - 1)); |
|
||||
} |
|
||||
|
|
||||
// " [fd00::1]" => "fd00::1"
|
|
||||
// "[fd00::1]:8000" => "fd00::1"
|
|
||||
// "127.0.0.1" => "127.0.0.1"
|
|
||||
// " 127.0.0.1:8000" => "127.0.0.1"
|
|
||||
// " vk.com:8000" => "vk.com"
|
|
||||
// return value: true - host is ip addr
|
|
||||
bool strip_host_to_ip(char *host) |
|
||||
{ |
|
||||
size_t l; |
|
||||
char *h,*p; |
|
||||
uint8_t addr[16]; |
|
||||
|
|
||||
for (h = host ; *h==' ' || *h=='\t' ; h++); |
|
||||
l = strlen(h); |
|
||||
if (l>=2) |
|
||||
{ |
|
||||
if (*h=='[') |
|
||||
{ |
|
||||
// ipv6 ?
|
|
||||
for (p=++h ; *p && *p!=']' ; p++); |
|
||||
if (*p==']') |
|
||||
{ |
|
||||
l = p-h; |
|
||||
memmove(host,h,l); |
|
||||
host[l]=0; |
|
||||
return inet_pton(AF_INET6, host, addr)>0; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (inet_pton(AF_INET6, h, addr)>0) |
|
||||
{ |
|
||||
// ipv6 ?
|
|
||||
if (host!=h) |
|
||||
{ |
|
||||
l = strlen(h); |
|
||||
memmove(host,h,l); |
|
||||
host[l]=0; |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// ipv4 ?
|
|
||||
for (p=h ; *p && *p!=':' ; p++); |
|
||||
l = p-h; |
|
||||
if (host!=h) memmove(host,h,l); |
|
||||
host[l]=0; |
|
||||
return inet_pton(AF_INET, host, addr)>0; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
void ntop46(const struct sockaddr *sa, char *str, size_t len) |
|
||||
{ |
|
||||
if (!len) return; |
|
||||
*str = 0; |
|
||||
switch (sa->sa_family) |
|
||||
{ |
|
||||
case AF_INET: |
|
||||
inet_ntop(sa->sa_family, &((struct sockaddr_in*)sa)->sin_addr, str, len); |
|
||||
break; |
|
||||
case AF_INET6: |
|
||||
inet_ntop(sa->sa_family, &((struct sockaddr_in6*)sa)->sin6_addr, str, len); |
|
||||
break; |
|
||||
default: |
|
||||
snprintf(str, len, "UNKNOWN_FAMILY_%d", sa->sa_family); |
|
||||
} |
|
||||
} |
|
||||
void ntop46_port(const struct sockaddr *sa, char *str, size_t len) |
|
||||
{ |
|
||||
char ip[40]; |
|
||||
ntop46(sa, ip, sizeof(ip)); |
|
||||
switch (sa->sa_family) |
|
||||
{ |
|
||||
case AF_INET: |
|
||||
snprintf(str, len, "%s:%u", ip, ntohs(((struct sockaddr_in*)sa)->sin_port)); |
|
||||
break; |
|
||||
case AF_INET6: |
|
||||
snprintf(str, len, "[%s]:%u", ip, ntohs(((struct sockaddr_in6*)sa)->sin6_port)); |
|
||||
break; |
|
||||
default: |
|
||||
snprintf(str, len, "%s", ip); |
|
||||
} |
|
||||
} |
|
||||
void print_sockaddr(const struct sockaddr *sa) |
|
||||
{ |
|
||||
char ip_port[48]; |
|
||||
|
|
||||
ntop46_port(sa, ip_port, sizeof(ip_port)); |
|
||||
printf("%s", ip_port); |
|
||||
} |
|
||||
|
|
||||
bool pton4_port(const char *s, struct sockaddr_in *sa) |
|
||||
{ |
|
||||
char ip[16],*p; |
|
||||
size_t l; |
|
||||
unsigned int u; |
|
||||
|
|
||||
p = strchr(s,':'); |
|
||||
if (!p) return false; |
|
||||
l = p-s; |
|
||||
if (l<7 || l>15) return false; |
|
||||
memcpy(ip,s,l); |
|
||||
ip[l]=0; |
|
||||
p++; |
|
||||
|
|
||||
sa->sin_family = AF_INET; |
|
||||
if (inet_pton(AF_INET,ip,&sa->sin_addr)!=1 || sscanf(p,"%u",&u)!=1 || !u || u>0xFFFF) return false; |
|
||||
sa->sin_port = htons((uint16_t)u); |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
bool pton6_port(const char *s, struct sockaddr_in6 *sa) |
|
||||
{ |
|
||||
char ip[40],*p; |
|
||||
size_t l; |
|
||||
unsigned int u; |
|
||||
|
|
||||
if (*s++!='[') return false; |
|
||||
p = strchr(s,']'); |
|
||||
if (!p || p[1]!=':') return false; |
|
||||
l = p-s; |
|
||||
if (l<2 || l>39) return false; |
|
||||
p+=2; |
|
||||
memcpy(ip,s,l); |
|
||||
ip[l]=0; |
|
||||
|
|
||||
sa->sin6_family = AF_INET6; |
|
||||
if (inet_pton(AF_INET6,ip,&sa->sin6_addr)!=1 || sscanf(p,"%u",&u)!=1 || !u || u>0xFFFF) return false; |
|
||||
sa->sin6_port = htons((uint16_t)u); |
|
||||
sa->sin6_flowinfo = 0; |
|
||||
sa->sin6_scope_id = 0; |
|
||||
|
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
uint16_t saport(const struct sockaddr *sa) |
|
||||
{ |
|
||||
return htons(sa->sa_family==AF_INET ? ((struct sockaddr_in*)sa)->sin_port : |
|
||||
sa->sa_family==AF_INET6 ? ((struct sockaddr_in6*)sa)->sin6_port : 0); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
uint64_t pntoh64(const void *p) |
|
||||
{ |
|
||||
return (uint64_t)*((const uint8_t *)(p)+0) << 56 | |
|
||||
(uint64_t)*((const uint8_t *)(p)+1) << 48 | |
|
||||
(uint64_t)*((const uint8_t *)(p)+2) << 40 | |
|
||||
(uint64_t)*((const uint8_t *)(p)+3) << 32 | |
|
||||
(uint64_t)*((const uint8_t *)(p)+4) << 24 | |
|
||||
(uint64_t)*((const uint8_t *)(p)+5) << 16 | |
|
||||
(uint64_t)*((const uint8_t *)(p)+6) << 8 | |
|
||||
(uint64_t)*((const uint8_t *)(p)+7) << 0; |
|
||||
} |
|
||||
void phton64(uint8_t *p, uint64_t v) |
|
||||
{ |
|
||||
p[0] = (uint8_t)(v >> 56); |
|
||||
p[1] = (uint8_t)(v >> 48); |
|
||||
p[2] = (uint8_t)(v >> 40); |
|
||||
p[3] = (uint8_t)(v >> 32); |
|
||||
p[4] = (uint8_t)(v >> 24); |
|
||||
p[5] = (uint8_t)(v >> 16); |
|
||||
p[6] = (uint8_t)(v >> 8); |
|
||||
p[7] = (uint8_t)(v >> 0); |
|
||||
} |
|
||||
|
|
||||
bool seq_within(uint32_t s, uint32_t s1, uint32_t s2) |
|
||||
{ |
|
||||
return (s2>=s1 && s>=s1 && s<=s2) || (s2<s1 && (s<=s2 || s>=s1)); |
|
||||
} |
|
||||
|
|
||||
bool ipv6_addr_is_zero(const struct in6_addr *a) |
|
||||
{ |
|
||||
return !memcmp(a,"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",16); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
#define INVALID_HEX_DIGIT ((uint8_t)-1) |
|
||||
static inline uint8_t parse_hex_digit(char c) |
|
||||
{ |
|
||||
return (c>='0' && c<='9') ? c-'0' : (c>='a' && c<='f') ? c-'a'+0xA : (c>='A' && c<='F') ? c-'A'+0xA : INVALID_HEX_DIGIT; |
|
||||
} |
|
||||
static inline bool parse_hex_byte(const char *s, uint8_t *pbyte) |
|
||||
{ |
|
||||
uint8_t u,l; |
|
||||
u = parse_hex_digit(s[0]); |
|
||||
l = parse_hex_digit(s[1]); |
|
||||
if (u==INVALID_HEX_DIGIT || l==INVALID_HEX_DIGIT) |
|
||||
{ |
|
||||
*pbyte=0; |
|
||||
return false; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
*pbyte=(u<<4) | l; |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size) |
|
||||
{ |
|
||||
uint8_t *pe = pbuf+*size; |
|
||||
*size=0; |
|
||||
while(pbuf<pe && *s) |
|
||||
{ |
|
||||
if (!parse_hex_byte(s,pbuf)) |
|
||||
return false; |
|
||||
pbuf++; s+=2; (*size)++; |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize,size_t offset) |
|
||||
{ |
|
||||
size_t size; |
|
||||
|
|
||||
if (offset%=patsize) |
|
||||
{ |
|
||||
size = patsize-offset; |
|
||||
size = bufsize>size ? size : bufsize; |
|
||||
memcpy(buf,pattern+offset,size); |
|
||||
buf += size; |
|
||||
bufsize -= size; |
|
||||
} |
|
||||
while (bufsize) |
|
||||
{ |
|
||||
size = bufsize>patsize ? patsize : bufsize; |
|
||||
memcpy(buf,pattern,size); |
|
||||
buf += size; |
|
||||
bufsize -= size; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
int fprint_localtime(FILE *F) |
|
||||
{ |
|
||||
struct tm t; |
|
||||
time_t now; |
|
||||
|
|
||||
time(&now); |
|
||||
localtime_r(&now,&t); |
|
||||
return fprintf(F, "%02d.%02d.%04d %02d:%02d:%02d", t.tm_mday, t.tm_mon + 1, t.tm_year + 1900, t.tm_hour, t.tm_min, t.tm_sec); |
|
||||
} |
|
||||
|
|
||||
time_t file_mod_time(const char *filename) |
|
||||
{ |
|
||||
struct stat st; |
|
||||
return stat(filename,&st)==-1 ? 0 : st.st_mtime; |
|
||||
} |
|
||||
bool file_mod_signature(const char *filename, file_mod_sig *ms) |
|
||||
{ |
|
||||
struct stat st; |
|
||||
if (stat(filename,&st)==-1) |
|
||||
{ |
|
||||
FILE_MOD_RESET(ms); |
|
||||
return false; |
|
||||
} |
|
||||
ms->mod_time=st.st_mtime; |
|
||||
ms->size=st.st_size; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
bool file_open_test(const char *filename, int flags) |
|
||||
{ |
|
||||
int fd = open(filename,flags); |
|
||||
if (fd>=0) |
|
||||
{ |
|
||||
close(fd); |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
bool pf_in_range(uint16_t port, const port_filter *pf) |
|
||||
{ |
|
||||
return port && (((!pf->from && !pf->to) || (port>=pf->from && port<=pf->to)) ^ pf->neg); |
|
||||
} |
|
||||
bool pf_parse(const char *s, port_filter *pf) |
|
||||
{ |
|
||||
unsigned int v1,v2; |
|
||||
char c; |
|
||||
|
|
||||
if (!s) return false; |
|
||||
if (*s=='*' && s[1]==0) |
|
||||
{ |
|
||||
pf->from=1; pf->to=0xFFFF; |
|
||||
return true; |
|
||||
} |
|
||||
if (*s=='~') |
|
||||
{ |
|
||||
pf->neg=true; |
|
||||
s++; |
|
||||
} |
|
||||
else |
|
||||
pf->neg=false; |
|
||||
if (sscanf(s,"%u-%u%c",&v1,&v2,&c)==2) |
|
||||
{ |
|
||||
if (v1>65535 || v2>65535 || v1>v2) return false; |
|
||||
pf->from=(uint16_t)v1; |
|
||||
pf->to=(uint16_t)v2; |
|
||||
} |
|
||||
else if (sscanf(s,"%u%c",&v1,&c)==1) |
|
||||
{ |
|
||||
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) |
|
||||
{ |
|
||||
size_t k,sz16 = sz>>1; |
|
||||
for(k=0;k<sz16;k++) ((uint16_t*)p)[k]=(uint16_t)random(); |
|
||||
if (sz & 1) p[sz-1]=(uint8_t)random(); |
|
||||
} |
|
||||
void fill_random_az(uint8_t *p,size_t sz) |
|
||||
{ |
|
||||
size_t k; |
|
||||
for(k=0;k<sz;k++) p[k] = 'a'+(random() % ('z'-'a')); |
|
||||
} |
|
||||
void fill_random_az09(uint8_t *p,size_t sz) |
|
||||
{ |
|
||||
size_t k; |
|
||||
uint8_t rnd; |
|
||||
for(k=0;k<sz;k++) |
|
||||
{ |
|
||||
rnd = random() % (10 + 'z'-'a'+1); |
|
||||
p[k] = rnd<10 ? rnd+'0' : 'a'+rnd-10; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
void set_console_io_buffering(void) |
|
||||
{ |
|
||||
setvbuf(stdout, NULL, _IOLBF, 0); |
|
||||
setvbuf(stderr, NULL, _IOLBF, 0); |
|
||||
} |
|
||||
|
|
||||
bool set_env_exedir(const char *argv0) |
|
||||
{ |
|
||||
char *s,*d; |
|
||||
bool bOK=false; |
|
||||
if ((s = strdup(argv0))) |
|
||||
{ |
|
||||
if ((d = dirname(s))) |
|
||||
bOK = !setenv("EXEDIR",d,1); |
|
||||
free(s); |
|
||||
} |
|
||||
return bOK; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr) |
|
||||
{ |
|
||||
char s_ip[16]; |
|
||||
*s_ip=0; |
|
||||
inet_ntop(AF_INET, &cidr->addr, s_ip, sizeof(s_ip)); |
|
||||
snprintf(s,s_len,cidr->preflen<32 ? "%s/%u" : "%s", s_ip, cidr->preflen); |
|
||||
} |
|
||||
void print_cidr4(const struct cidr4 *cidr) |
|
||||
{ |
|
||||
char s[19]; |
|
||||
str_cidr4(s,sizeof(s),cidr); |
|
||||
printf("%s",s); |
|
||||
} |
|
||||
void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr) |
|
||||
{ |
|
||||
char s_ip[40]; |
|
||||
*s_ip=0; |
|
||||
inet_ntop(AF_INET6, &cidr->addr, s_ip, sizeof(s_ip)); |
|
||||
snprintf(s,s_len,cidr->preflen<128 ? "%s/%u" : "%s", s_ip, cidr->preflen); |
|
||||
} |
|
||||
void print_cidr6(const struct cidr6 *cidr) |
|
||||
{ |
|
||||
char s[44]; |
|
||||
str_cidr6(s,sizeof(s),cidr); |
|
||||
printf("%s",s); |
|
||||
} |
|
||||
bool parse_cidr4(char *s, struct cidr4 *cidr) |
|
||||
{ |
|
||||
char *p,d; |
|
||||
bool b; |
|
||||
unsigned int plen; |
|
||||
|
|
||||
if ((p = strchr(s, '/'))) |
|
||||
{ |
|
||||
if (sscanf(p + 1, "%u", &plen)!=1 || plen>32) |
|
||||
return false; |
|
||||
cidr->preflen = (uint8_t)plen; |
|
||||
d=*p; *p=0; // backup char
|
|
||||
} |
|
||||
else |
|
||||
cidr->preflen = 32; |
|
||||
b = (inet_pton(AF_INET, s, &cidr->addr)==1); |
|
||||
if (p) *p=d; // restore char
|
|
||||
return b; |
|
||||
} |
|
||||
bool parse_cidr6(char *s, struct cidr6 *cidr) |
|
||||
{ |
|
||||
char *p,d; |
|
||||
bool b; |
|
||||
unsigned int plen; |
|
||||
|
|
||||
if ((p = strchr(s, '/'))) |
|
||||
{ |
|
||||
if (sscanf(p + 1, "%u", &plen)!=1 || plen>128) |
|
||||
return false; |
|
||||
cidr->preflen = (uint8_t)plen; |
|
||||
d=*p; *p=0; // backup char
|
|
||||
} |
|
||||
else |
|
||||
cidr->preflen = 128; |
|
||||
b = (inet_pton(AF_INET6, s, &cidr->addr)==1); |
|
||||
if (p) *p=d; // restore char
|
|
||||
return b; |
|
||||
} |
|
||||
@ -1,120 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <arpa/inet.h> |
|
||||
#include <netinet/in.h> |
|
||||
#include <sys/socket.h> |
|
||||
#include <stddef.h> |
|
||||
#include <stdbool.h> |
|
||||
#include <stdint.h> |
|
||||
#include <stdio.h> |
|
||||
#include <time.h> |
|
||||
|
|
||||
#define UNARY_PLUS(v) (v>0 ? "+" : "") |
|
||||
|
|
||||
// this saves memory. sockaddr_storage is larger than required. it can be 128 bytes. sockaddr_in6 is 28 bytes.
|
|
||||
typedef union |
|
||||
{ |
|
||||
struct sockaddr_in sa4; // size 16
|
|
||||
struct sockaddr_in6 sa6; // size 28
|
|
||||
char _align[32]; // force 16-byte alignment for ip6_and int128 ops
|
|
||||
} sockaddr_in46; |
|
||||
|
|
||||
int unique_size_t(size_t *pu, int ct); |
|
||||
void qsort_size_t(size_t *array,size_t ct); |
|
||||
|
|
||||
void rtrim(char *s); |
|
||||
void replace_char(char *s, char from, char to); |
|
||||
char *strncasestr(const char *s,const char *find, size_t slen); |
|
||||
|
|
||||
bool load_file(const char *filename,void *buffer,size_t *buffer_size); |
|
||||
bool load_file_nonempty(const char *filename,void *buffer,size_t *buffer_size); |
|
||||
bool save_file(const char *filename, const void *buffer, size_t buffer_size); |
|
||||
bool append_to_list_file(const char *filename, const char *s); |
|
||||
|
|
||||
void expand_bits(void *target, const void *source, unsigned int source_bitlen, unsigned int target_bytelen); |
|
||||
|
|
||||
bool strip_host_to_ip(char *host); |
|
||||
|
|
||||
void print_sockaddr(const struct sockaddr *sa); |
|
||||
void ntop46(const struct sockaddr *sa, char *str, size_t len); |
|
||||
void ntop46_port(const struct sockaddr *sa, char *str, size_t len); |
|
||||
bool pton4_port(const char *s, struct sockaddr_in *sa); |
|
||||
bool pton6_port(const char *s, struct sockaddr_in6 *sa); |
|
||||
|
|
||||
uint16_t saport(const struct sockaddr *sa); |
|
||||
|
|
||||
bool seq_within(uint32_t s, uint32_t s1, uint32_t s2); |
|
||||
|
|
||||
uint64_t pntoh64(const void *p); |
|
||||
void phton64(uint8_t *p, uint64_t v); |
|
||||
|
|
||||
bool ipv6_addr_is_zero(const struct in6_addr *a); |
|
||||
|
|
||||
static inline uint16_t pntoh16(const uint8_t *p) { |
|
||||
return ((uint16_t)p[0] << 8) | (uint16_t)p[1]; |
|
||||
} |
|
||||
static inline void phton16(uint8_t *p, uint16_t v) { |
|
||||
p[0] = (uint8_t)(v >> 8); |
|
||||
p[1] = v & 0xFF; |
|
||||
} |
|
||||
static inline uint32_t pntoh24(const uint8_t *p) { |
|
||||
return ((uint32_t)p[0] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[2]; |
|
||||
} |
|
||||
static inline void phton24(uint8_t *p, uint32_t v) { |
|
||||
p[0] = (uint8_t)(v>>16); |
|
||||
p[1] = (uint8_t)(v>>8); |
|
||||
p[2] = (uint8_t)v; |
|
||||
} |
|
||||
static inline uint32_t pntoh32(const uint8_t *p) { |
|
||||
return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3]; |
|
||||
} |
|
||||
|
|
||||
bool parse_hex_str(const char *s, uint8_t *pbuf, size_t *size); |
|
||||
void fill_pattern(uint8_t *buf,size_t bufsize,const void *pattern,size_t patsize,size_t offset); |
|
||||
|
|
||||
int fprint_localtime(FILE *F); |
|
||||
|
|
||||
typedef struct |
|
||||
{ |
|
||||
time_t mod_time; |
|
||||
off_t size; |
|
||||
} file_mod_sig; |
|
||||
#define FILE_MOD_COMPARE(ms1,ms2) (((ms1)->mod_time==(ms2)->mod_time) && ((ms1)->size==(ms2)->size)) |
|
||||
#define FILE_MOD_RESET(ms) memset(ms,0,sizeof(file_mod_sig)) |
|
||||
bool file_mod_signature(const char *filename, file_mod_sig *ms); |
|
||||
time_t file_mod_time(const char *filename); |
|
||||
bool file_open_test(const char *filename, int flags); |
|
||||
|
|
||||
typedef struct |
|
||||
{ |
|
||||
uint16_t from,to; |
|
||||
bool neg; |
|
||||
} port_filter; |
|
||||
bool pf_in_range(uint16_t port, const port_filter *pf); |
|
||||
bool pf_parse(const char *s, port_filter *pf); |
|
||||
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); |
|
||||
|
|
||||
void set_console_io_buffering(void); |
|
||||
bool set_env_exedir(const char *argv0); |
|
||||
|
|
||||
|
|
||||
struct cidr4 |
|
||||
{ |
|
||||
struct in_addr addr; |
|
||||
uint8_t preflen; |
|
||||
}; |
|
||||
struct cidr6 |
|
||||
{ |
|
||||
struct in6_addr addr; |
|
||||
uint8_t preflen; |
|
||||
}; |
|
||||
void str_cidr4(char *s, size_t s_len, const struct cidr4 *cidr); |
|
||||
void print_cidr4(const struct cidr4 *cidr); |
|
||||
void str_cidr6(char *s, size_t s_len, const struct cidr6 *cidr); |
|
||||
void print_cidr6(const struct cidr6 *cidr); |
|
||||
bool parse_cidr4(char *s, struct cidr4 *cidr); |
|
||||
bool parse_cidr6(char *s, struct cidr6 *cidr); |
|
||||
@ -1,340 +0,0 @@ |
|||||
#include <stdio.h> |
|
||||
#include "hostlist.h" |
|
||||
#include "gzip.h" |
|
||||
#include "helpers.h" |
|
||||
|
|
||||
// inplace tolower() and add to pool
|
|
||||
static bool addpool(hostlist_pool **hostlist, char **s, const char *end, int *ct) |
|
||||
{ |
|
||||
char *p=*s; |
|
||||
|
|
||||
// comment line
|
|
||||
if ( *p == '#' || *p == ';' || *p == '/' || *p == '\r' || *p == '\n') |
|
||||
{ |
|
||||
// advance until eol
|
|
||||
for (; p<end && *p && *p!='\r' && *p != '\n'; p++); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
// advance until eol lowering all chars
|
|
||||
uint32_t flags = 0; |
|
||||
if (*p=='^') |
|
||||
{ |
|
||||
p = ++(*s); |
|
||||
flags |= HOSTLIST_POOL_FLAG_STRICT_MATCH; |
|
||||
} |
|
||||
for (; p<end && *p && *p!='\r' && *p != '\n'; p++) *p=tolower(*p); |
|
||||
if (!HostlistPoolAddStrLen(hostlist, *s, p-*s, flags)) |
|
||||
{ |
|
||||
HostlistPoolDestroy(hostlist); |
|
||||
*hostlist = NULL; |
|
||||
return false; |
|
||||
} |
|
||||
if (ct) (*ct)++; |
|
||||
} |
|
||||
// advance to the next line
|
|
||||
for (; p<end && (!*p || *p=='\r' || *p=='\n') ; p++); |
|
||||
*s = p; |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
bool AppendHostlistItem(hostlist_pool **hostlist, char *s) |
|
||||
{ |
|
||||
return addpool(hostlist,&s,s+strlen(s),NULL); |
|
||||
} |
|
||||
|
|
||||
bool AppendHostList(hostlist_pool **hostlist, const char *filename) |
|
||||
{ |
|
||||
char *p, *e, s[256], *zbuf; |
|
||||
size_t zsize; |
|
||||
int ct = 0; |
|
||||
FILE *F; |
|
||||
int r; |
|
||||
|
|
||||
DLOG_CONDUP("Loading hostlist %s\n",filename); |
|
||||
|
|
||||
if (!(F = fopen(filename, "rb"))) |
|
||||
{ |
|
||||
DLOG_ERR("Could not open %s\n", filename); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
if (is_gzip(F)) |
|
||||
{ |
|
||||
r = z_readfile(F,&zbuf,&zsize); |
|
||||
fclose(F); |
|
||||
if (r==Z_OK) |
|
||||
{ |
|
||||
DLOG_CONDUP("zlib compression detected. uncompressed size : %zu\n", zsize); |
|
||||
|
|
||||
p = zbuf; |
|
||||
e = zbuf + zsize; |
|
||||
while(p<e) |
|
||||
{ |
|
||||
if (!addpool(hostlist,&p,e,&ct)) |
|
||||
{ |
|
||||
DLOG_ERR("Not enough memory to store host list : %s\n", filename); |
|
||||
free(zbuf); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
free(zbuf); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
DLOG_ERR("zlib decompression failed : result %d\n",r); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
DLOG_CONDUP("loading plain text list\n"); |
|
||||
|
|
||||
while (fgets(s, sizeof(s), F)) |
|
||||
{ |
|
||||
p = s; |
|
||||
if (!addpool(hostlist,&p,p+strlen(p),&ct)) |
|
||||
{ |
|
||||
DLOG_ERR("Not enough memory to store host list : %s\n", filename); |
|
||||
fclose(F); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
fclose(F); |
|
||||
} |
|
||||
|
|
||||
DLOG_CONDUP("Loaded %d hosts from %s\n", ct, filename); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
static bool LoadHostList(struct hostlist_file *hfile) |
|
||||
{ |
|
||||
if (hfile->filename) |
|
||||
{ |
|
||||
file_mod_sig fsig; |
|
||||
if (!file_mod_signature(hfile->filename, &fsig)) |
|
||||
{ |
|
||||
// stat() error
|
|
||||
DLOG_PERROR("file_mod_signature"); |
|
||||
DLOG_ERR("cannot access hostlist file '%s'. in-memory content remains unchanged.\n",hfile->filename); |
|
||||
return true; |
|
||||
} |
|
||||
if (FILE_MOD_COMPARE(&hfile->mod_sig,&fsig)) return true; // up to date
|
|
||||
HostlistPoolDestroy(&hfile->hostlist); |
|
||||
if (!AppendHostList(&hfile->hostlist, hfile->filename)) |
|
||||
{ |
|
||||
HostlistPoolDestroy(&hfile->hostlist); |
|
||||
return false; |
|
||||
} |
|
||||
hfile->mod_sig=fsig; |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
static bool LoadHostLists(struct hostlist_files_head *list) |
|
||||
{ |
|
||||
bool bres=true; |
|
||||
struct hostlist_file *hfile; |
|
||||
|
|
||||
LIST_FOREACH(hfile, list, next) |
|
||||
{ |
|
||||
if (!LoadHostList(hfile)) |
|
||||
// at least one failed
|
|
||||
bres=false; |
|
||||
} |
|
||||
return bres; |
|
||||
} |
|
||||
|
|
||||
bool NonEmptyHostlist(hostlist_pool **hostlist) |
|
||||
{ |
|
||||
// add impossible hostname if the list is empty
|
|
||||
return *hostlist ? true : HostlistPoolAddStrLen(hostlist, "@&()", 4, 0); |
|
||||
} |
|
||||
|
|
||||
static void MakeAutolistsNonEmpty() |
|
||||
{ |
|
||||
struct desync_profile_list *dpl; |
|
||||
LIST_FOREACH(dpl, ¶ms.desync_profiles, next) |
|
||||
{ |
|
||||
if (dpl->dp.hostlist_auto) |
|
||||
NonEmptyHostlist(&dpl->dp.hostlist_auto->hostlist); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
bool LoadAllHostLists() |
|
||||
{ |
|
||||
if (!LoadHostLists(¶ms.hostlists)) |
|
||||
return false; |
|
||||
MakeAutolistsNonEmpty(); |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
|
|
||||
static bool SearchHostList(hostlist_pool *hostlist, const char *host, bool no_match_subdomains) |
|
||||
{ |
|
||||
if (hostlist) |
|
||||
{ |
|
||||
const char *p = host; |
|
||||
const struct hostlist_pool *hp; |
|
||||
bool bHostFull=true; |
|
||||
while (p) |
|
||||
{ |
|
||||
DLOG("hostlist check for %s : ", p); |
|
||||
hp = HostlistPoolGetStr(hostlist, p); |
|
||||
if (hp) |
|
||||
{ |
|
||||
if ((hp->flags & HOSTLIST_POOL_FLAG_STRICT_MATCH) && !bHostFull) |
|
||||
{ |
|
||||
DLOG("negative : strict_mismatch : %s != %s\n", p, host); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
DLOG("positive\n"); |
|
||||
return true; |
|
||||
} |
|
||||
} |
|
||||
else |
|
||||
DLOG("negative\n"); |
|
||||
if (no_match_subdomains) break; |
|
||||
p = strchr(p, '.'); |
|
||||
if (p) p++; |
|
||||
bHostFull = false; |
|
||||
} |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static bool HostlistsReloadCheck(const struct hostlist_collection_head *hostlists) |
|
||||
{ |
|
||||
struct hostlist_item *item; |
|
||||
LIST_FOREACH(item, hostlists, next) |
|
||||
{ |
|
||||
if (!LoadHostList(item->hfile)) |
|
||||
return false; |
|
||||
} |
|
||||
MakeAutolistsNonEmpty(); |
|
||||
return true; |
|
||||
} |
|
||||
bool HostlistsReloadCheckForProfile(const struct desync_profile *dp) |
|
||||
{ |
|
||||
return HostlistsReloadCheck(&dp->hl_collection) && HostlistsReloadCheck(&dp->hl_collection_exclude); |
|
||||
} |
|
||||
// return : true = apply fooling, false = do not apply
|
|
||||
static bool HostlistCheck_(const struct hostlist_collection_head *hostlists, const struct hostlist_collection_head *hostlists_exclude, const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck) |
|
||||
{ |
|
||||
struct hostlist_item *item; |
|
||||
|
|
||||
if (excluded) *excluded = false; |
|
||||
|
|
||||
if (!bSkipReloadCheck) |
|
||||
if (!HostlistsReloadCheck(hostlists) || !HostlistsReloadCheck(hostlists_exclude)) |
|
||||
return false; |
|
||||
|
|
||||
LIST_FOREACH(item, hostlists_exclude, next) |
|
||||
{ |
|
||||
DLOG("[%s] exclude ", item->hfile->filename ? item->hfile->filename : "fixed"); |
|
||||
if (SearchHostList(item->hfile->hostlist, host, no_match_subdomains)) |
|
||||
{ |
|
||||
if (excluded) *excluded = true; |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
// old behavior compat: all include lists are empty means check passes
|
|
||||
if (!hostlist_collection_is_empty(hostlists)) |
|
||||
{ |
|
||||
LIST_FOREACH(item, hostlists, next) |
|
||||
{ |
|
||||
DLOG("[%s] include ", item->hfile->filename ? item->hfile->filename : "fixed"); |
|
||||
if (SearchHostList(item->hfile->hostlist, host, no_match_subdomains)) |
|
||||
return true; |
|
||||
} |
|
||||
return false; |
|
||||
} |
|
||||
return true; |
|
||||
} |
|
||||
|
|
||||
|
|
||||
// return : true = apply fooling, false = do not apply
|
|
||||
bool HostlistCheck(const struct desync_profile *dp, const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck) |
|
||||
{ |
|
||||
DLOG("* hostlist check for profile %d\n",dp->n); |
|
||||
return HostlistCheck_(&dp->hl_collection, &dp->hl_collection_exclude, host, no_match_subdomains, excluded, bSkipReloadCheck); |
|
||||
} |
|
||||
|
|
||||
|
|
||||
static struct hostlist_file *RegisterHostlist_(struct hostlist_files_head *hostlists, struct hostlist_collection_head *hl_collection, const char *filename) |
|
||||
{ |
|
||||
struct hostlist_file *hfile; |
|
||||
|
|
||||
if (filename) |
|
||||
{ |
|
||||
if (!(hfile=hostlist_files_search(hostlists, filename))) |
|
||||
if (!(hfile=hostlist_files_add(hostlists, filename))) |
|
||||
return NULL; |
|
||||
if (!hostlist_collection_search(hl_collection, filename)) |
|
||||
if (!hostlist_collection_add(hl_collection, hfile)) |
|
||||
return NULL; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
if (!(hfile=hostlist_files_add(hostlists, NULL))) |
|
||||
return NULL; |
|
||||
if (!hostlist_collection_add(hl_collection, hfile)) |
|
||||
return NULL; |
|
||||
} |
|
||||
|
|
||||
return hfile; |
|
||||
} |
|
||||
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename) |
|
||||
{ |
|
||||
/*
|
|
||||
if (filename && !file_mod_time(filename)) |
|
||||
{ |
|
||||
DLOG_ERR("cannot access hostlist file '%s'\n",filename); |
|
||||
return NULL; |
|
||||
} |
|
||||
*/ |
|
||||
return RegisterHostlist_( |
|
||||
¶ms.hostlists, |
|
||||
bExclude ? &dp->hl_collection_exclude : &dp->hl_collection, |
|
||||
filename); |
|
||||
} |
|
||||
|
|
||||
void HostlistsDebug() |
|
||||
{ |
|
||||
if (!params.debug) return; |
|
||||
|
|
||||
struct hostlist_file *hfile; |
|
||||
struct desync_profile_list *dpl; |
|
||||
struct hostlist_item *hl_item; |
|
||||
|
|
||||
LIST_FOREACH(hfile, ¶ms.hostlists, next) |
|
||||
{ |
|
||||
if (hfile->filename) |
|
||||
DLOG("hostlist file %s%s\n",hfile->filename,hfile->hostlist ? "" : " (empty)"); |
|
||||
else |
|
||||
DLOG("hostlist fixed%s\n",hfile->hostlist ? "" : " (empty)"); |
|
||||
} |
|
||||
|
|
||||
LIST_FOREACH(dpl, ¶ms.desync_profiles, next) |
|
||||
{ |
|
||||
LIST_FOREACH(hl_item, &dpl->dp.hl_collection, next) |
|
||||
if (hl_item->hfile!=dpl->dp.hostlist_auto) |
|
||||
{ |
|
||||
if (hl_item->hfile->filename) |
|
||||
DLOG("profile %d include hostlist %s%s\n",dpl->dp.n, hl_item->hfile->filename,hl_item->hfile->hostlist ? "" : " (empty)"); |
|
||||
else |
|
||||
DLOG("profile %d include fixed hostlist%s\n",dpl->dp.n, hl_item->hfile->hostlist ? "" : " (empty)"); |
|
||||
} |
|
||||
LIST_FOREACH(hl_item, &dpl->dp.hl_collection_exclude, next) |
|
||||
{ |
|
||||
if (hl_item->hfile->filename) |
|
||||
DLOG("profile %d exclude hostlist %s%s\n",dpl->dp.n,hl_item->hfile->filename,hl_item->hfile->hostlist ? "" : " (empty)"); |
|
||||
else |
|
||||
DLOG("profile %d exclude fixed hostlist%s\n",dpl->dp.n,hl_item->hfile->hostlist ? "" : " (empty)"); |
|
||||
} |
|
||||
if (dpl->dp.hostlist_auto) |
|
||||
DLOG("profile %d auto hostlist %s%s\n",dpl->dp.n,dpl->dp.hostlist_auto->filename,dpl->dp.hostlist_auto->hostlist ? "" : " (empty)"); |
|
||||
} |
|
||||
} |
|
||||
@ -1,17 +0,0 @@ |
|||||
#pragma once |
|
||||
|
|
||||
#include <stdbool.h> |
|
||||
#include "pools.h" |
|
||||
#include "params.h" |
|
||||
|
|
||||
bool AppendHostlistItem(hostlist_pool **hostlist, char *s); |
|
||||
bool AppendHostList(hostlist_pool **hostlist, const char *filename); |
|
||||
bool LoadAllHostLists(); |
|
||||
bool NonEmptyHostlist(hostlist_pool **hostlist); |
|
||||
// return : true = apply fooling, false = do not apply
|
|
||||
bool HostlistCheck(const struct desync_profile *dp,const char *host, bool no_match_subdomains, bool *excluded, bool bSkipReloadCheck); |
|
||||
struct hostlist_file *RegisterHostlist(struct desync_profile *dp, bool bExclude, const char *filename); |
|
||||
bool HostlistsReloadCheckForProfile(const struct desync_profile *dp); |
|
||||
void HostlistsDebug(); |
|
||||
|
|
||||
#define ResetAllHostlistsModTime() hostlist_files_reset_modtime(¶ms.hostlists) |
|
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue