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