zapret/blockcheck.sh

662 lines
15 KiB
Bash
Raw Normal View History

2021-12-08 15:20:44 +03:00
#!/bin/sh
EXEDIR="$(dirname "$0")"
EXEDIR="$(cd "$EXEDIR"; pwd)"
ZAPRET_BASE="$EXEDIR"
[ -n "$QNUM" ] || QNUM=59780
[ -n "$TPPORT" ] || TPPORT=993
[ -n "$TPWS_UID" ] || TPWS_UID=1
2021-12-08 15:20:44 +03:00
[ -n "$NFQWS" ] || NFQWS="$ZAPRET_BASE/nfq/nfqws"
[ -n "$TPWS" ] || TPWS="$ZAPRET_BASE/tpws/tpws"
2021-12-08 15:20:44 +03:00
[ -n "$MDIG" ] || MDIG="$ZAPRET_BASE/mdig/mdig"
[ -n "$DESYNC_MARK" ] || DESYNC_MARK=0x40000000
DOMAIN=rutracker.org
CURL_MAX_TIME=5
MIN_TTL=1
MAX_TTL=12
HDRTEMP=/tmp/zapret-hdr.txt
ECHON="echo -n"
DNSCHECK_DNS="8.8.8.8 1.1.1.1 77.88.8.8"
DNSCHECK_DOM="pornhub.com putinhuylo.com rutracker.org nnmclub.to protonmail.com"
DNSCHECK_DIG1=/tmp/dig1.txt
DNSCHECK_DIG2=/tmp/dig2.txt
DNSCHECK_DIGS=/tmp/digs.txt
exists()
{
which $1 >/dev/null 2>/dev/null
}
killwait()
{
# $1 - signal (-9, -2, ...)
# $2 - pid
kill $1 $2
# suppress job kill message
wait $2 2>/dev/null
}
exitp()
{
local A
echo
echo press enter to continue
read A
exit $1
}
read_yes_no()
{
# $1 - default (Y/N)
local A
read A
[ -z "$A" ] || ([ "$A" != "Y" ] && [ "$A" != "y" ] && [ "$A" != "N" ] && [ "$A" != "n" ]) && A=$1
[ "$A" = "Y" ] || [ "$A" = "y" ] || [ "$A" = "1" ]
}
ask_yes_no()
{
# $1 - default (Y/N or 0/1)
# $2 - text
local DEFAULT=$1
[ "$1" = "1" ] && DEFAULT=Y
[ "$1" = "0" ] && DEFAULT=N
[ -z "$DEFAULT" ] && DEFAULT=N
$ECHON "$2 (default : $DEFAULT) (Y/N) ? "
read_yes_no $DEFAULT
}
ask_yes_no_var()
{
# $1 - variable name for answer : 0/1
# $2 - text
local DEFAULT
eval DEFAULT="\$$1"
if ask_yes_no "$DEFAULT" "$2"; then
eval $1=1
else
eval $1=0
fi
}
require_root()
{
echo \* checking privileges
[ $(id -u) -ne "0" ] && {
echo root is required
exists sudo && exec sudo "$0"
exists su && exec su -c "$0"
echo su or sudo not found
exitp 2
}
}
IPT()
{
$IPTABLES -C "$@" >/dev/null 2>/dev/null || $IPTABLES -I "$@"
}
IPT_DEL()
{
$IPTABLES -C "$@" >/dev/null 2>/dev/null && $IPTABLES -D "$@"
}
check_system()
{
echo \* checking system
local UNAME=$(uname)
[ "$UNAME" = "Linux" ] || {
2021-12-09 20:13:38 +03:00
echo $UNAME not supported
2021-12-08 15:20:44 +03:00
exitp 5
}
}
check_prerequisites()
{
echo \* checking prerequisites
[ -x "$NFQWS" ] && [ -x "$TPWS" ] && [ -x "$MDIG" ] || {
echo $NFQWS or $MDIG or $TPWS is not available. run $ZAPRET_BASE/install_bin.sh
2021-12-08 15:20:44 +03:00
exitp 6
}
for prog in iptables ip6tables curl; do
exists $prog || {
echo $prog does not exist. please install
exitp 6
}
done
}
2021-12-10 11:18:25 +03:00
curl_supports_tls13()
{
curl --tlsv1.3 -Is -o /dev/null http://$LOCALHOST_IPT:65535 2>/dev/null
# return code 2 = init failed. likely bad command line options
[ $? != 2 ]
}
2021-12-08 15:20:44 +03:00
hdrfile_http_code()
{
# $1 - hdr file
sed -nre '1,1 s/^HTTP\/1\.[0,1] ([0-9]+) .*$/\1/p' "$1"
}
hdrfile_location()
{
# $1 - hdr file
# some DPIs return CRLF line ending
2021-12-08 21:08:03 +03:00
tr -d '\015' <"$1" | sed -nre 's/^[Ll][Oo][Cc][Aa][Tt][Ii][Oo][Nn]:[ \t]*([^ \t]*)[ \t]*$/\1/p'
2021-12-08 15:20:44 +03:00
}
curl_test_http()
{
# $1 - ip version : 4/6
# $2 - domain name
local code loc
curl -${1}SsD "$HDRTEMP" --max-time $CURL_MAX_TIME $CURL_OPT "http://$2" -o /dev/null 2>&1 || {
code=$?
rm -f "$HDRTEMP"
return $code
}
code=$(hdrfile_http_code "$HDRTEMP")
[ "$code" = 301 -o "$code" = 302 -o "$code" = 307 -o "$code" = 308 ] && {
loc=$(hdrfile_location "$HDRTEMP")
echo "$loc" | grep -qE "^https?://.*$2(/|$)" ||
echo "$loc" | grep -vqE '^https?://' || {
2021-12-08 15:20:44 +03:00
echo suspicious redirection to : $loc
rm -f "$HDRTEMP"
return 254
}
}
rm -f "$HDRTEMP"
return 0
}
2021-12-10 11:18:25 +03:00
curl_test_https_tls12()
2021-12-08 15:20:44 +03:00
{
# $1 - ip version : 4/6
# $2 - domain name
# prevent using QUIC if available in curl
2021-12-10 11:18:25 +03:00
# do not use tls 1.3 to make sure server certificate is not encrypted
curl -${1}Ss --max-time $CURL_MAX_TIME $CURL_OPT --http1.1 --tls-max 1.2 "https://$2" -o /dev/null 2>&1
}
curl_test_https_tls13()
{
# $1 - ip version : 4/6
# $2 - domain name
# prevent using QUIC if available in curl
# force TLS1.3 mode
curl -${1}Ss --max-time $CURL_MAX_TIME $CURL_OPT --http1.1 --tlsv1.3 "https://$2" -o /dev/null 2>&1
2021-12-08 15:20:44 +03:00
}
nfqws_ipt_prepare()
{
# $1 - port
IPT POSTROUTING -t mangle -p tcp --dport $1 -m mark ! --mark $DESYNC_MARK/$DESYNC_MARK -j NFQUEUE --queue-num $QNUM
}
nfqws_ipt_unprepare()
{
# $1 - port
IPT_DEL POSTROUTING -t mangle -p tcp --dport $1 -m mark ! --mark $DESYNC_MARK/$DESYNC_MARK -j NFQUEUE --queue-num $QNUM
}
tpws_ipt_prepare()
{
# $1 - port
IPT OUTPUT -t nat -p tcp --dport $1 -m owner ! --uid-owner $TPWS_UID -j DNAT --to $LOCALHOST_IPT:$TPPORT
}
tpws_ipt_unprepare()
{
# $1 - port
IPT_DEL OUTPUT -t nat -p tcp --dport $1 -m owner ! --uid-owner $TPWS_UID -j DNAT --to $LOCALHOST_IPT:$TPPORT
}
2021-12-08 15:20:44 +03:00
nfqws_start()
{
"$NFQWS" --dpi-desync-fwmark=$DESYNC_MARK --qnum=$QNUM "$@" >/dev/null &
2021-12-09 20:13:38 +03:00
PID=$!
2021-12-08 15:20:44 +03:00
}
tpws_start()
{
"$TPWS" --uid $TPWS_UID:$TPWS_UID --bind-addr=$LOCALHOST --port=$TPPORT "$@" >/dev/null &
2021-12-09 20:13:38 +03:00
PID=$!
# give some time to initialize
sleep 1
}
2021-12-10 11:18:25 +03:00
ws_kill()
{
[ -z "$PID" ] || {
killwait -9 $PID 2>/dev/null
PID=
}
}
2021-12-08 15:20:44 +03:00
curl_test()
{
# $1 - test function
# $2 - domain
$1 $IPV $2 && {
echo '!!!!! AVAILABLE !!!!!'
return 0
}
local code=$?
if [ $code = 254 ]; then
echo UNAVAILABLE
else
echo UNAVAILABLE code=$code
fi
return $code
}
ws_curl_test()
2021-12-08 15:20:44 +03:00
{
# $1 - ws start function
# $2 - test function
# $3 - domain
# $4,$5,$6, ... - ws params
2021-12-09 20:13:38 +03:00
local code ws_start=$1 testf=$2 dom=$3
shift
2021-12-08 15:20:44 +03:00
shift
shift
$ws_start "$@"
# let some time for tpws to initialize
2021-12-08 15:20:44 +03:00
curl_test $testf $dom
code=$?
2021-12-10 11:18:25 +03:00
ws_kill
2021-12-08 15:20:44 +03:00
return $code
}
tpws_curl_test()
{
# $1 - test function
# $2 - domain
# $3,$4,$5, ... - tpws params
echo - checking tpws $3 $4 $5 $6 $7 $8 $9
ws_curl_test tpws_start "$@"
}
nfqws_curl_test()
{
# $1 - test function
# $2 - domain
# $3,$4,$5, ... - nfqws params
echo - checking nfqws $3 $4 $5 $6 $7 $8 $9
ws_curl_test nfqws_start "$@"
}
nfqws_check_domain_bypass()
2021-12-08 15:20:44 +03:00
{
# $1 - test function
2021-12-09 16:38:02 +03:00
# $2 - encrypted test : 1/0
# $3 - domain
local strategy tests='fake' ttls s sec="$2" found
2021-12-08 15:20:44 +03:00
2021-12-09 16:38:02 +03:00
[ "$sec" = 0 ] && {
for s in '--hostcase' '--hostspell=hoSt' '--hostnospace' '--domcase'; do
2021-12-09 16:54:12 +03:00
nfqws_curl_test $1 $3 $s && strategy="${strategy:-$s}"
done
2021-12-09 16:38:02 +03:00
}
2021-12-08 15:20:44 +03:00
2021-12-09 16:38:02 +03:00
s="--dpi-desync=split2"
if nfqws_curl_test $1 $3 $s; then
2021-12-09 16:50:31 +03:00
strategy="${strategy:-$s}"
2021-12-08 15:20:44 +03:00
else
tests="$tests split fake,split2 fake,split"
2021-12-09 16:38:02 +03:00
[ "$sec" = 0 ] && {
s="$s --hostcase"
2021-12-09 16:50:31 +03:00
nfqws_curl_test $1 $3 $s && strategy="${strategy:-$s}"
2021-12-09 16:38:02 +03:00
}
for pos in 1 2 4 5 10 50 100; do
s="--dpi-desync=split2 --dpi-desync-split-pos=$pos"
if nfqws_curl_test $1 $3 $s; then
strategy="${strategy:-$s}"
break
else
[ "$sec" = 0 ] && {
s="$s --hostcase"
nfqws_curl_test $1 $3 $s && strategy="${strategy:-$s}"
}
fi
done
2021-12-08 15:20:44 +03:00
fi
2021-12-09 16:38:02 +03:00
s="--dpi-desync=disorder2"
if nfqws_curl_test $1 $3 $s; then
strategy="${strategy:-$s}"
2021-12-08 15:20:44 +03:00
else
tests="$tests disorder fake,disorder2 fake,disorder"
2021-12-08 15:20:44 +03:00
fi
2021-12-08 16:30:21 +03:00
ttls=$(seq -s ' ' $MIN_TTL $MAX_TTL)
2021-12-08 15:20:44 +03:00
for desync in $tests; do
2021-12-09 16:38:02 +03:00
found=0
2021-12-08 15:20:44 +03:00
for ttl in $ttls; do
2021-12-09 16:38:02 +03:00
s="--dpi-desync=$desync --dpi-desync-ttl=$ttl"
nfqws_curl_test $1 $3 $s && {
found=1
strategy="${strategy:-$s}"
2021-12-08 15:20:44 +03:00
break
}
done
2021-12-09 16:38:02 +03:00
[ "$sec" = 1 ] && [ "$found" = 0 ] && {
for ttl in $ttls; do
s="--dpi-desync=$desync --dpi-desync-ttl=$ttl --wssize 1:6"
nfqws_curl_test $1 $3 $s && {
found=1
strategy="${strategy:-$s}"
break
}
done
}
2021-12-09 16:50:31 +03:00
for fooling in badsum md5sig badseq; do
s="--dpi-desync=$desync --dpi-desync-fooling=$fooling"
if nfqws_curl_test $1 $3 $s ; then
strategy="${strategy:-$s}"
[ "$fooling" = "md5sig" ] && echo 'WARNING ! although md5sig fooling worked it will not work on all sites. it typically works only on linux servers.'
else
[ "$sec" = 1 ] && {
s="$s --wssize 1:6"
nfqws_curl_test $1 $3 $s && {
strategy="${strategy:-$s}"
[ "$fooling" = "md5sig" ] && echo 'WARNING ! although md5sig fooling worked it will not work on all sites. it typically works only on linux servers.'
}
}
fi
done
2021-12-08 15:20:44 +03:00
done
2021-12-10 11:18:25 +03:00
[ "$sec" = 1 ] && {
s="--wssize 1:6"
nfqws_curl_test $1 $3 $s && strategy="${strategy:-$s}"
}
2021-12-08 15:20:44 +03:00
echo
if [ -n "$strategy" ]; then
echo "!!!!! working strategy found : nfqws $strategy !!!!!"
return 0
else
echo 'working strategy not found'
return 1
fi
}
tpws_check_domain_bypass()
{
# $1 - test function
# $2 - encrypted test : 1/0
# $3 - domain
local s strategy sec="$2"
if [ "$sec" = 0 ]; then
for s in '--hostcase' '--hostspell=hoSt' '--split-http-req=method' '--split-http-req=method --hostcase' '--split-http-req=host' '--split-http-req=host --hostcase' \
'--hostdot' '--hosttab' '--hostnospace' '--methodspace' '--methodeol' '--unixeol' \
'--hostpad=1024' '--hostpad=2048' '--hostpad=4096' '--hostpad=8192' '--hostpad=16384'; do
tpws_curl_test $1 $3 $s && strategy="${strategy:-$s}"
done
else
for pos in 1 2 3 4 5 10 50 100; do
s="--split-pos=$pos"
tpws_curl_test $1 $3 $s && {
strategy="${strategy:-$s}"
break
}
done
fi
echo
if [ -n "$strategy" ]; then
echo "!!!!! working strategy found : tpws $strategy !!!!!"
return 0
else
echo 'working strategy not found'
return 1
fi
}
2021-12-08 15:20:44 +03:00
check_domain()
{
# $1 - test function
# $2 - port
2021-12-09 16:38:02 +03:00
# $3 - encrypted test : 1/0
# $4 - domain
2021-12-08 15:20:44 +03:00
2021-12-08 21:23:06 +03:00
local code
2021-12-08 15:20:44 +03:00
echo
2021-12-09 16:38:02 +03:00
echo \* $1 $4
2021-12-08 15:20:44 +03:00
# in case was interrupted before
nfqws_ipt_unprepare $2
tpws_ipt_unprepare $2
2021-12-10 11:18:25 +03:00
ws_kill
2021-12-08 15:20:44 +03:00
echo "- checking without DPI bypass"
2021-12-09 16:38:02 +03:00
curl_test $1 $4 && return
2021-12-08 21:23:06 +03:00
code=$?
for c in 1 2 3 4 6 27 ; do
[ $code = $c ] && return
done
2021-12-08 15:20:44 +03:00
echo preparing tpws redirection
tpws_ipt_prepare $2
tpws_check_domain_bypass $1 $3 $4
echo clearing tpws redirection
tpws_ipt_unprepare $2
echo
2021-12-08 15:20:44 +03:00
echo preparing nfqws redirection
nfqws_ipt_prepare $2
nfqws_check_domain_bypass $1 $3 $4
2021-12-08 15:20:44 +03:00
echo clearing nfqws redirection
nfqws_ipt_unprepare $2
}
check_domain_http()
{
# $1 - domain
2021-12-09 16:38:02 +03:00
check_domain curl_test_http 80 0 $1
2021-12-08 15:20:44 +03:00
}
2021-12-10 11:18:25 +03:00
check_domain_https_tls12()
{
# $1 - domain
check_domain curl_test_https_tls12 443 1 $1
}
check_domain_https_tls13()
2021-12-08 15:20:44 +03:00
{
# $1 - domain
2021-12-10 11:18:25 +03:00
check_domain curl_test_https_tls13 443 1 $1
2021-12-08 15:20:44 +03:00
}
configure_ip_version()
{
if [ "$IPV" = 6 ]; then
2021-12-09 20:24:41 +03:00
IPTABLES=ip6tables
LOCALHOST=::1
LOCALHOST_IPT=[::1]
else
IPTABLES=iptables
LOCALHOST=127.0.0.1
LOCALHOST_IPT=127.0.0.1
fi
}
2021-12-08 15:20:44 +03:00
ask_params()
{
echo
echo NOTE ! this test should be run with zapret or any other bypass software disabled, without VPN
2021-12-09 16:38:02 +03:00
$ECHON "test this domain (default: $DOMAIN) : "
2021-12-08 15:20:44 +03:00
local dom
read dom
[ -n "$dom" ] && DOMAIN=$dom
2021-12-09 16:38:02 +03:00
$ECHON "ip protocol version - 4 or 6 (default: 4) : "
2021-12-08 15:20:44 +03:00
read IPV
[ -n "$IPV" ] || IPV=4
[ "$IPV" = 4 -o "$IPV" = 6 ] || {
echo invalid ip version. should be 4 or 6.
exitp 1
}
configure_ip_version
2021-12-08 15:20:44 +03:00
ENABLE_HTTP=1
ask_yes_no_var ENABLE_HTTP "check http"
2021-12-10 11:18:25 +03:00
ENABLE_HTTPS_TLS12=1
ask_yes_no_var ENABLE_HTTPS_TLS12 "check https tls 1.2"
ENABLE_HTTPS_TLS13=0
if curl_supports_tls13; then
echo
echo "TLS 1.3 is the new standard for encrypted communications over TCP"
echo "its the most important feature for DPI bypass is encrypted server TLS ClientHello"
echo "more and more sites enable TLS 1.3 but still there're many sites with only TLS 1.2 support"
2021-12-10 11:22:42 +03:00
echo "with TLS 1.3 more DPI bypass strategies can work but they may not apply to all sites"
2021-12-10 11:18:25 +03:00
echo "if a strategy works with TLS 1.2 it will also work with TLS 1.3"
echo "if nothing works with TLS 1.2 this test may find TLS1.3 only strategies"
echo "make sure that $DOMAIN supports TLS 1.3 otherwise all test will return an error"
ask_yes_no_var ENABLE_HTTPS_TLS13 "check https tls 1.3"
else
echo "installed curl version does not support TLS 1.3 . tests disabled."
fi
2021-12-08 15:20:44 +03:00
IGNORE_CA=0
CURL_OPT=
2021-12-10 11:18:25 +03:00
[ $ENABLE_HTTPS_TLS13 = 1 -o $ENABLE_HTTPS_TLS12 = 1 ] && {
echo
2021-12-08 15:20:44 +03:00
echo on limited systems like openwrt CA certificates might not be installed to preserve space
echo in such a case curl cannot verify server certificate and you should either install ca-bundle or disable verification
echo however disabling verification will break https check if ISP does MitM attack and substitutes server certificate
ask_yes_no_var IGNORE_CA "do not verify server certificate"
[ "$IGNORE_CA" = 1 ] && CURL_OPT=-k
}
}
pingtest()
{
ping -c 1 -W 1 $1 >/dev/null
}
dnstest()
{
# $1 - dns server. empty for system resolver
nslookup w3.org $1 >/dev/null 2>/dev/null
}
find_working_public_dns()
{
for dns in $DNSCHECK_DNS; do
pingtest $dns && dnstest $dns && {
PUBDNS=$dns
return 0
}
done
return 1
}
check_dns_spoof()
{
# $1 - domain
# $2 - public DNS
echo $1 | "$EXEDIR/mdig/mdig" --family=4 >"$DNSCHECK_DIG1"
nslookup $1 $2 | sed -n '/Name:/,$p' | grep ^Address | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' >"$DNSCHECK_DIG2"
# check whether system resolver returns anything other than public DNS
grep -qvFf "$DNSCHECK_DIG2" "$DNSCHECK_DIG1"
}
check_dns_cleanup()
{
rm -f "$DNSCHECK_DIG1" "$DNSCHECK_DIG2" "$DNSCHECK_DIGS" 2>/dev/null
}
check_dns()
{
local C1 C2
echo \* checking DNS
[ -f "$DNSCHECK_DIGS" ] && rm -f "$DNSCHECK_DIGS"
dnstest || {
echo -- DNS is not working. It's either misconfigured or blocked or you don't have inet access.
return 1
}
echo system DNS is working
if find_working_public_dns ; then
echo comparing system resolver to public DNS : $PUBDNS
for dom in $DNSCHECK_DOM; do
if check_dns_spoof $dom $PUBDNS ; then
echo $dom : MISMATCH
echo -- system resolver :
cat "$DNSCHECK_DIG1"
echo -- $PUBDNS :
cat "$DNSCHECK_DIG2"
check_dns_cleanup
echo -- POSSIBLE DNS HIJACK DETECTED. ZAPRET WILL NOT HELP YOU IN CASE DNS IS SPOOFED !!!
echo -- DNS CHANGE OR DNSCRYPT MAY BE REQUIRED
return 1
else
echo $dom : OK
cat "$DNSCHECK_DIG1" >>"$DNSCHECK_DIGS"
fi
done
else
echo no working public DNS was found. looks like public DNS blocked.
for dom in $DNSCHECK_DOM; do echo $dom; done | "$EXEDIR/mdig/mdig" --threads=10 --family=4 >"$DNSCHECK_DIGS"
fi
echo checking resolved IP uniqueness for : $DNSCHECK_DOM
echo censor\'s DNS can return equal result for multiple blocked domains.
C1=$(wc -l <"$DNSCHECK_DIGS")
C2=$(sort -u "$DNSCHECK_DIGS" | wc -l)
[ "$C1" -eq 0 ] &&
{
echo -- DNS is not working. It's either misconfigured or blocked or you don't have inet access.
check_dns_cleanup
return 1
}
[ "$C1" = "$C2" ] ||
{
echo system dns resolver has returned equal IPs for some domains checked above \($C1 total, $C2 unique\)
echo non-unique IPs :
sort "$DNSCHECK_DIGS" | uniq -d
echo -- POSSIBLE DNS HIJACK DETECTED. ZAPRET WILL NOT HELP YOU IN CASE DNS IS SPOOFED !!!
echo -- DNSCRYPT MAY BE REQUIRED
check_dns_cleanup
return 1
}
echo all resolved IPs are unique
echo -- DNS looks good
echo -- NOTE this check is Russia targeted. In your country other domains may be blocked.
check_dns_cleanup
return 0
}
sigint()
{
# make sure we are not in a middle state that impacts connectivity
echo
echo terminating...
[ -n "$IPV" ] && {
tpws_ipt_unprepare 80
tpws_ipt_unprepare 443
nfqws_ipt_unprepare 80
nfqws_ipt_unprepare 443
}
2021-12-10 11:18:25 +03:00
ws_kill
exitp 1
2021-12-08 15:20:44 +03:00
}
check_system
check_prerequisites
require_root
check_dns
ask_params
2021-12-10 11:18:25 +03:00
PID=
2021-12-10 11:24:15 +03:00
trap 'sigint' 2
2021-12-08 15:20:44 +03:00
[ "$ENABLE_HTTP" = 1 ] && check_domain_http $DOMAIN
2021-12-10 11:18:25 +03:00
[ "$ENABLE_HTTPS_TLS12" = 1 ] && check_domain_https_tls12 $DOMAIN
[ "$ENABLE_HTTPS_TLS13" = 1 ] && check_domain_https_tls13 $DOMAIN
trap - 2
2021-12-08 21:12:41 +03:00
exitp 0