[ -n "$ZAPRET_BASE" ] || ZAPRET_BASE=/opt/zapret IPSET_DIR=$ZAPRET_BASE/ipset . "$IPSET_DIR/def.sh" HOSTLIST="$ZHOSTLIST.gz" [ -f "$HOSTLIST" ] || HOSTLIST="$ZHOSTLIST" [ -f "$HOSTLIST" ] || HOSTLIST="$ZUSERLIST" PIDDIR=/var/run [ -n "$TPPORT" ] || TPPORT=988 [ -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" PF_MAIN="/etc/pf.conf" PF_ANCHOR_DIR=/etc/pf.anchors PF_ANCHOR_ZAPRET="$PF_ANCHOR_DIR/zapret" PF_ANCHOR_ZAPRET_V4="$PF_ANCHOR_DIR/zapret-v4" PF_ANCHOR_ZAPRET_V6="$PF_ANCHOR_DIR/zapret-v6" CUSTOM_SCRIPT="$ZAPRET_BASE/init.d/macos/custom" [ -f "$CUSTOM_SCRIPT" ] && . "$CUSTOM_SCRIPT" existf() { type "$1" >/dev/null 2>/dev/null } on_off_function() { # $1 : function name on # $2 : function name off # $3 : 0 - off, 1 - on local F="$1" [ "$3" = "1" ] || F="$2" shift shift shift "$F" "$@" } 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 "$@" } filter_apply_hostlist_target() { # $1 - var name of tpws or nfqws params [ "$MODE_FILTER" = "hostlist" ] && eval $1="\"\$$1 --hostlist=$HOSTLIST\"" } 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' } pf_anchor_root_reload() { echo reloading PF root anchor pfctl -qf "$PF_MAIN" } pf_anchor_root() { local patch [ -f "$PF_MAIN" ] && { grep -q '^rdr-anchor "zapret"$' "$PF_MAIN" || { echo patching rdr-anchor in $PF_MAIN patch=1 sed -i '' -e '/^rdr-anchor "com\.apple\/\*"$/i \ rdr-anchor "zapret" ' $PF_MAIN } grep -q '^anchor "zapret"$' "$PF_MAIN" || { echo patching anchor in $PF_MAIN patch=1 sed -i '' -e '/^anchor "com\.apple\/\*"$/i \ anchor "zapret" ' $PF_MAIN } grep -q "^set limit table-entries" "$PF_MAIN" || { echo patching table-entries limit patch=1 sed -i '' -e '/^scrub-anchor "com\.apple\/\*"$/i \ set limit table-entries 5000000 ' $PF_MAIN } grep -q '^anchor "zapret"$' "$PF_MAIN" && grep -q '^rdr-anchor "zapret"$' "$PF_MAIN" && grep -q '^set limit table-entries' "$PF_MAIN" && { if [ -n "$patch" ]; then echo successfully patched $PF_MAIN pf_anchor_root_reload else echo successfully checked zapret anchors in $PF_MAIN fi return 0 } } echo ---------------------------------- echo Automatic $PF_MAIN patching failed. You must apply root anchors manually in your PF config. echo rdr-anchor \"zapret\" echo anchor \"zapret\" echo ---------------------------------- return 1 } pf_anchor_root_del() { sed -i '' -e '/^anchor "zapret"$/d' -e '/^rdr-anchor "zapret"$/d' -e '/^set limit table-entries/d' "$PF_MAIN" } pf_anchor_zapret() { [ "$DISABLE_IPV4" = "1" ] || { if [ -f "$ZIPLIST_EXCLUDE" ]; then echo "table persist file \"$ZIPLIST_EXCLUDE\"" else echo "table persist" fi } [ "$DISABLE_IPV6" = "1" ] || { if [ -f "$ZIPLIST_EXCLUDE6" ]; then echo "table persist file \"$ZIPLIST_EXCLUDE6\"" else echo "table persist" fi } [ "$DISABLE_IPV4" = "1" ] || echo "rdr-anchor \"/zapret-v4\" inet to !" [ "$DISABLE_IPV6" = "1" ] || echo "rdr-anchor \"/zapret-v6\" inet6 to !" [ "$DISABLE_IPV4" = "1" ] || echo "anchor \"/zapret-v4\" inet to !" [ "$DISABLE_IPV6" = "1" ] || echo "anchor \"/zapret-v6\" inet6 to !" } pf_anchor_zapret_tables() { # $1 - variable to receive applied table names # $2/$3 $4/$5 ... table_name/table_file local tblv=$1 local _tbl shift [ "$MODE_FILTER" = "ipset" ] && { while [ -n "$1" ] && [ -n "$2" ] ; do [ -f "$2" ] && { echo "table <$1> file \"$2\"" _tbl="$_tbl<$1> " } shift shift done } [ -n "$_tbl" ] || _tbl="any" eval $tblv="\"\$_tbl\"" } pf_anchor_port_target() { if [ "$MODE_HTTP" = "1" ] && [ "$MODE_HTTPS" = "1" ]; then echo "{80,443}" elif [ "$MODE_HTTPS" = "1" ]; then echo "443" elif [ "$MODE_HTTP" = "1" ]; then echo "80" fi } pf_anchor_zapret_v4_tpws() { # $1 - port local rule port=$(pf_anchor_port_target) for lan in $IFACE_LAN; do for t in $tbl; do echo "rdr on $lan inet proto tcp from any to $t port $port -> 127.0.0.1 port $1" done done echo "rdr on lo0 inet proto tcp from !127.0.0.0/8 to any port $port -> 127.0.0.1 port $1" for t in $tbl; do rule="route-to (lo0 127.0.0.1) inet proto tcp from !127.0.0.0/8 to $t port $port user { >root }" if [ -n "$IFACE_WAN" ] ; then for wan in $IFACE_WAN; do echo "pass out on $wan $rule" done else echo "pass out $rule" fi done } pf_anchor_zapret_v4() { local tbl port [ "$DISABLE_IPV4" = "1" ] || { case $MODE in tpws) [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ] && return pf_anchor_zapret_tables tbl zapret-user "$ZIPLIST_USER" zapret "$ZIPLIST" pf_anchor_zapret_v4_tpws $TPPORT ;; custom) pf_anchor_zapret_tables tbl zapret-user "$ZIPLIST_USER" zapret "$ZIPLIST" existf zapret_custom_firewall_v4 && zapret_custom_firewall_v4 ;; esac } } pf_anchor_zapret_v6_tpws() { # $1 - port local LL_LAN rule port=$(pf_anchor_port_target) # LAN link local is only for router for lan in $IFACE_LAN; do LL_LAN=$(get_ipv6_linklocal $lan) [ -n "$LL_LAN" ] && { for t in $tbl; do echo "rdr on $lan inet6 proto tcp from any to $t port $port -> $LL_LAN port $1" done } done echo "rdr on lo0 inet6 proto tcp from !::1 to any port $port -> fe80::1 port $1" for t in $tbl; do rule="route-to (lo0 fe80::1) inet6 proto tcp from !::1 to $t port $port user { >root }" if [ -n "$IFACE_WAN" ] ; then for wan in $IFACE_WAN; do echo "pass out on $wan $rule" done else echo "pass out $rule" fi done } pf_anchor_zapret_v6() { local tbl port [ "$DISABLE_IPV6" = "1" ] || { case $MODE in tpws) [ ! "$MODE_HTTP" = "1" ] && [ ! "$MODE_HTTPS" = "1" ] && return pf_anchor_zapret_tables tbl zapret6-user "$ZIPLIST_USER6" zapret6 "$ZIPLIST6" pf_anchor_zapret_v6_tpws $TPPORT ;; custom) pf_anchor_zapret_tables tbl zapret6-user "$ZIPLIST_USER6" zapret6 "$ZIPLIST6" existf zapret_custom_firewall_v6 && zapret_custom_firewall_v6 ;; esac } } pf_anchors_create() { wait_lan_ll pf_anchor_zapret >"$PF_ANCHOR_ZAPRET" pf_anchor_zapret_v4 >"$PF_ANCHOR_ZAPRET_V4" pf_anchor_zapret_v6 >"$PF_ANCHOR_ZAPRET_V6" } pf_anchors_del() { rm -f "$PF_ANCHOR_ZAPRET" "$PF_ANCHOR_ZAPRET_V4" "$PF_ANCHOR_ZAPRET_V6" } pf_anchors_load() { echo loading zapret anchor from "$PF_ANCHOR_ZAPRET" pfctl -qa zapret -f "$PF_ANCHOR_ZAPRET" || { echo error loading zapret anchor return 1 } if [ "$DISABLE_IPV4" = "1" ]; then echo clearing zapret-v4 anchor pfctl -qa zapret-v4 -F all 2>/dev/null else echo loading zapret-v4 anchor from "$PF_ANCHOR_ZAPRET_V4" pfctl -qa zapret-v4 -f "$PF_ANCHOR_ZAPRET_V4" || { echo error loading zapret-v4 anchor return 1 } fi if [ "$DISABLE_IPV6" = "1" ]; then echo clearing zapret-v6 anchor pfctl -qa zapret-v6 -F all 2>/dev/null else echo loading zapret-v6 anchor from "$PF_ANCHOR_ZAPRET_V6" pfctl -qa zapret-v6 -f "$PF_ANCHOR_ZAPRET_V6" || { echo error loading zapret-v6 anchor return 1 } fi echo successfully loaded PF anchors return 0 } pf_anchors_clear() { echo clearing zapret anchors pfctl -qa zapret-v4 -F all 2>/dev/null pfctl -qa zapret-v6 -F all 2>/dev/null pfctl -qa zapret -F all 2>/dev/null } pf_enable() { echo enabling PF pfctl -qe } pf_table_reload() { echo reloading zapret tables [ "$DISABLE_IPV4" = "1" ] || pfctl -qTl -a zapret-v4 -f "$PF_ANCHOR_ZAPRET_V4" [ "$DISABLE_IPV6" = "1" ] || pfctl -qTl -a zapret-v6 -f "$PF_ANCHOR_ZAPRET_V6" pfctl -qTl -a zapret -f "$PF_ANCHOR_ZAPRET" } zapret_do_firewall() { # $1 - 1 - add, 0 - del case "${MODE_OVERRIDE:-$MODE}" in tpws|filter|custom) if [ "$1" = "1" ] ; then pf_anchor_root || return 1 pf_anchors_create pf_anchors_load || return 1 pf_enable else pf_anchors_clear fi ;; tpws-socks) ;; *) echo "unsupported MODE=$MODE" return 1 ;; esac 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 "$@" } zapret_do_daemons() { # $1 - 1 - run, 0 - stop local opt case "${MODE_OVERRIDE:-$MODE}" in tpws) [ "$1" = "1" ] && [ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] && { echo "both ipv4 and ipv6 are disabled. nothing to do" return 0 } # MacOS requires root. kernel hardcoded requirement for /dev/pf ioctls opt="--user=root --port=$TPPORT" filter_apply_hostlist_target opt tpws_apply_binds opt opt="$opt $TPWS_OPT" do_daemon $1 1 "$TPWS" "$opt" ;; tpws-socks) [ "$1" = "1" ] && [ "$DISABLE_IPV4" = "1" ] && [ "$DISABLE_IPV6" = "1" ] && { echo "both ipv4 and ipv6 are disabled. nothing to do" return 0 } opt="--socks --user=$WS_USER --port=$TPPORT" tpws_apply_socks_binds opt filter_apply_hostlist_target opt opt="$opt $TPWS_OPT" do_daemon $1 1 "$TPWS" "$opt" ;; filter) ;; custom) existf zapret_custom_daemons && zapret_custom_daemons $1 ;; *) echo "unsupported MODE=$MODE" return 1 ;; esac 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 "$@" }