Files
ruantiblock_openwrt/ruantiblock/files/usr/bin/ruantiblock
T
2023-12-24 16:13:41 +03:00

938 lines
38 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
########################################################################
#
# Ruantiblock
# (с) 2023 gSpot (https://github.com/gSpotx2f/ruantiblock_openwrt)
#
########################################################################
export NAME="ruantiblock"
export APP_EXEC="$0"
export APP_NAME="`basename $0`"
export LANG="en_US.UTF-8"
export LANGUAGE="en"
#################### Platform-specific settings ########################
CONFIG_DIR="/etc/${NAME}"
CONFIG_FILE="${CONFIG_DIR}/${NAME}.conf"
SCRIPTS_DIR="/usr/share/${NAME}"
export DATA_DIR="${CONFIG_DIR}/var"
export MODULES_DIR="/usr/libexec/${NAME}"
### Дополнительный конфиг dnsmasq с FQDN записями блэклиста
export DNSMASQ_DATA_FILE="/var/dnsmasq.d/02-${NAME}.dnsmasq"
export DNSMASQ_DATA_FILE_BYPASS="/var/dnsmasq.d/01-${NAME}_bypass.dnsmasq"
### Команда для перезапуска dnsmasq
export DNSMASQ_RESTART_CMD="/etc/init.d/dnsmasq restart"
### Директория для html-страницы статуса (не используется в OpenWrt)
export HTML_DIR="/www"
########################## Default Settings ############################
### Режим обработки пакетов в правилах nftables (1 - Tor, 2 - VPN, 3 - Прозрачный прокси)
export PROXY_MODE=1
### Применять правила проксификации для трафика локальных сервисов роутера (0 - выкл, 1 - вкл)
export PROXY_LOCAL_CLIENTS=0
### Удаление записей сетов перед началом обновления (для освобождения оперативной памяти перед обновлением сетов) (0 - выкл, 1 - вкл)
export NFTSET_CLEAR_SETS=0
### Режим фильтра хостов которым разрешено обходить блокировки (0 - выкл., 1 - только адреса из списка, 2 - любые адреса кроме присутствующих в списке)
export ALLOWED_HOSTS_MODE=0
### Список IP адресов хостов для фильтра, через пробел (прим.: 192.168.0.10 192.168.0.15)
export ALLOWED_HOSTS_LIST=""
### Порт прозрачного прокси Tor (параметр TransPort в torrc)
export TOR_TRANS_PORT=9040
### DNS-сервер для резолвинга в домене .onion (Tor)
export ONION_DNS_ADDR="127.0.0.1#9053"
### VPN интерфейс для правил маршрутизации
export IF_VPN="tun0"
### IP адрес шлюза для VPN конфигурации. Если не задан, используется адрес VPN интерфейса (или адрес пира для протоколов PPP)
export VPN_GW_IP=""
### Метка для отбора пакетов в VPN туннель
export VPN_PKTS_MARK=8
### Таблица маршрутизации для отправки пакетов в VPN туннель
export VPN_ROUTE_TABLE_ID=99
### Приоритет правила отбора пакетов при маршрутизации в VPN-интерфейс
export VPN_RULE_PRIO=1000
### Способ добавления в таблицу маршрутизации правила для отправки пакетов в VPN туннель (0 - hotplug.d, 1 - скрипт ruab_route_check)
export VPN_ROUTE_CHECK=0
### TCP порт прокси в режиме прозрачного прокси
export T_PROXY_PORT_TCP=1100
### UDP порт прокси в режиме прозрачного прокси
export T_PROXY_PORT_UDP=1100
### Отправлять в прозрачный прокси UDP-трафик (0 - выкл, 1 - вкл)
export T_PROXY_ALLOW_UDP=0
### Запись событий в syslog (0 - выкл, 1 - вкл)
export ENABLE_LOGGING=1
### Html-страница с инфо о текущем статусе (0 - выкл, 1 - вкл) (не используется в OpenWrt)
export ENABLE_HTML_INFO=0
### Максимальное кол-во элементов списка nftables
export NFTSET_MAXELEM_CIDR=65535
export NFTSET_MAXELEM_IP=1000000
export NFTSET_MAXELEM_DNSMASQ=65535
export NFTSET_MAXELEM_BYPASS_IP=65535
export NFTSET_MAXELEM_BYPASS_FQDN=65535
### Политика отбора элементов в сетах nftables. "performance" - производительность и большее потребление RAM. "memory" - хуже производительность и меньше потребление RAM
export NFTSET_POLICY_CIDR="memory"
export NFTSET_POLICY_IP="memory"
export NFTSET_POLICY_DNSMASQ="performance"
### Таймаут для записей в сете $NFTSET_DNSMASQ
export NFTSET_DNSMASQ_TIMEOUT="3h"
### Динамическое обновление таймаута записей в сете $NFTSET_DNSMASQ (0 - выкл, 1 - вкл)
export NFTSET_DNSMASQ_TIMEOUT_UPDATE=1
### Приоритет правила отбора пакетов nftables для конфигупации Tor или прозрачного прокси
export NFT_PRIO_NAT="dstnat - 10"
### Приоритет правила отбора пакетов nftables для трафика локальных клиентов в конфигупации Tor или прозрачного прокси
export NFT_PRIO_NAT_LOCAL="filter - 10"
### Приоритет правила отбора пакетов nftables для VPN-конфигурации
export NFT_PRIO_ROUTE="mangle + 10"
### Приоритет правила отбора пакетов nftables для трафика локальных клиентов в VPN-конфигурации
export NFT_PRIO_ROUTE_LOCAL="mangle + 10"
### Добавление в список блокировок пользовательских записей из файла $USER_ENTRIES_FILE (0 - выкл, 1 - вкл)
### В $DATA_DIR можно создать текстовый файл user_entries с записями IP, CIDR или FQDN (одна на строку). Эти записи будут добавлены в список блокировок
### В записях FQDN можно задать DNS-сервер для разрешения данного домена, через пробел (прим.: domain.com 8.8.8.8)
### Можно комментировать строки (#)
export ADD_USER_ENTRIES=0
### DNS-сервер для пользовательских записей (пустая строка - без DNS-сервера). Можно с портом: 8.8.8.8#53. Если в записи указан свой DNS-сервер - он имеет приоритет
export USER_ENTRIES_DNS=""
### Файл пользовательских записей
export USER_ENTRIES_FILE="${CONFIG_DIR}/user_entries"
### Режим списка IP адресов исключаемых из обхода блокировок (0 - выкл, 1 - вкл)
export BYPASS_MODE=0
### DNS-сервер для исключаемых записей (пустая строка - без DNS-сервера). Можно с портом: 8.8.8.8#53. Если в записи указан свой DNS-сервер - он имеет приоритет
export BYPASS_ENTRIES_DNS=""
### Файл исключаемых записей
export BYPASS_ENTRIES_FILE="${CONFIG_DIR}/bypass_entries"
### Включение режима полного прокси (0 - выкл, 1 - вкл)
export ENABLE_FPROXY=0
### Список IP адресов хостов для режима полного прокси, через пробел (прим.: 192.168.0.10 192.168.0.15)
export FPROXY_LIST=""
### Список приватных сетей для режима полного прокси, через пробел
export FPROXY_PRIVATE_NETS="192.168.0.0/16 172.16.0.0/12 10.0.0.0/8"
### Скачивать блэклисты через прокси
export ENABLE_BLLIST_PROXY=0
### Список хостов источников блэклиста
export BLLIST_HOSTS="reestr.rublacklist.net raw.githubusercontent.com antifilter.download"
### Кол-во попыток обновления блэклиста (в случае неудачи)
export MODULE_RUN_ATTEMPTS=3
### Таймаут между попытками обновления
export MODULE_RUN_TIMEOUT=60
### Модули для получения и обработки блэклиста
export BLLIST_MODULE=""
#export BLLIST_MODULE="${MODULES_DIR}/ruab_parser.lua"
#export BLLIST_MODULE="${MODULES_DIR}/ruab_parser.py"
##############################
### Режим обхода блокировок: ruantiblock-fqdn, ruantiblock-ip, zapret-info-fqdn, zapret-info-ip, rublacklist-fqdn, rublacklist-ip, antifilter-ip
export BLLIST_PRESET=""
### В случае если из источника получено менее указанного кол-ва записей, то обновления списков не происходит
export BLLIST_MIN_ENTRIES=3000
### Лимит IP адресов. При достижении, в конфиг ipset будет добавлена вся подсеть /24 вместо множества IP адресов пренадлежащих этой сети (0 - выкл)
export BLLIST_IP_LIMIT=0
### Подсети класса C (/24). IP адреса из этих подсетей не группируются при оптимизации (записи д.б. в виде: 68.183.221. 149.154.162. и пр.). Прим.: "68.183.221. 149.154.162."
export BLLIST_GR_EXCLUDED_NETS=""
### Группировать идущие подряд IP адреса в подсетях /24 в диапазоны CIDR
export BLLIST_SUMMARIZE_IP=0
### Группировать идущие подряд подсети /24 в диапазоны CIDR
export BLLIST_SUMMARIZE_CIDR=0
### Фильтрация записей блэклиста по шаблонам из файла BLLIST_IP_FILTER_FILE. Записи (IP, CIDR) попадающие под шаблоны исключаются из кофига ipset (0 - выкл, 1 - вкл)
export BLLIST_IP_FILTER=0
### Тип фильтра IP (0 - все записи, кроме совпадающих с шаблонами; 1 - только записи, совпадающие с шаблонами)
export BLLIST_IP_FILTER_TYPE=0
### Файл с шаблонами IP для опции BLLIST_IP_FILTER (каждый шаблон в отдельной строке. # в первом символе строки - комментирует строку)
export BLLIST_IP_FILTER_FILE="${CONFIG_DIR}/ip_filter"
### Лимит субдоменов для группировки. При достижении, в конфиг dnsmasq будет добавлен весь домен 2-го ур-ня вместо множества субдоменов (0 - выкл)
export BLLIST_SD_LIMIT=0
### SLD не подлежащие группировке при оптимизации (через пробел)
export BLLIST_GR_EXCLUDED_SLD=""
### Не группировать SLD попадающие под выражения (через пробел)
export BLLIST_GR_EXCLUDED_MASKS=""
### Фильтрация записей блэклиста по шаблонам из файла ENTRIES_FILTER_FILE. Записи (FQDN) попадающие под шаблоны исключаются из кофига dnsmasq (0 - выкл, 1 - вкл)
export BLLIST_FQDN_FILTER=0
### Тип фильтра FQDN (0 - все записи, кроме совпадающих с шаблонами; 1 - только записи, совпадающие с шаблонами)
export BLLIST_FQDN_FILTER_TYPE=0
### Файл с шаблонами FQDN для опции BLLIST_FQDN_FILTER (каждый шаблон в отдельной строке. # в первом символе строки - комментирует строку)
export BLLIST_FQDN_FILTER_FILE="${CONFIG_DIR}/fqdn_filter"
### Обрезка www[0-9]. в FQDN (0 - выкл, 1 - вкл)
export BLLIST_STRIP_WWW=1
### Преобразование кириллических доменов в punycode (0 - выкл, 1 - вкл)
export BLLIST_ENABLE_IDN=0
### Перенаправлять DNS-запросы на альтернативный DNS-сервер для заблокированных FQDN (0 - выкл, 1 - вкл)
export BLLIST_ALT_NSLOOKUP=0
### Альтернативный DNS-сервер
export BLLIST_ALT_DNS_ADDR="8.8.8.8"
### Источники блэклиста
export RBL_ALL_URL="https://reestr.rublacklist.net/api/v3/snapshot/"
export RBL_IP_URL="https://reestr.rublacklist.net/api/v3/ips/"
export ZI_ALL_URL="https://raw.githubusercontent.com/zapret-info/z-i/master/dump.csv"
export AF_IP_URL="https://antifilter.download/list/allyouneed.lst"
export AF_FQDN_URL="https://antifilter.download/list/domains.lst"
export RA_IP_IPSET_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_blacklist/master/blacklist-1.1/ip/ruantiblock.ip"
export RA_IP_DMASK_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_blacklist/master/blacklist-1.1/ip/ruantiblock.dnsmasq"
export RA_IP_STAT_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_blacklist/master/blacklist-1.1/ip/update_status"
export RA_FQDN_IPSET_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_blacklist/master/blacklist-1.1/fqdn/ruantiblock.ip"
export RA_FQDN_DMASK_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_blacklist/master/blacklist-1.1/fqdn/ruantiblock.dnsmasq"
export RA_FQDN_STAT_URL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_blacklist/master/blacklist-1.1/fqdn/update_status"
export RBL_ENCODING=""
export ZI_ENCODING="CP1251"
export AF_ENCODING=""
export RA_ENCODING=""
############################ Configuration #############################
### External config
[ -f "$CONFIG_FILE" ] && . "$CONFIG_FILE"
CONFIG_SCRIPT="${SCRIPTS_DIR}/config_script"
START_SCRIPT="${SCRIPTS_DIR}/start_script"
STOP_SCRIPT="${SCRIPTS_DIR}/stop_script"
### Config script
[ -f "$CONFIG_SCRIPT" ] && . "$CONFIG_SCRIPT"
### Utilities
AWK_CMD="awk"
NFT_CMD=`which nft`
if [ $? -ne 0 ]; then
echo " Error! Nftables doesn't exists" >&2
exit 1
fi
LOGGER_CMD=`which logger`
if [ $ENABLE_LOGGING = "1" -a $? -ne 0 ]; then
echo " Logger doesn't exists" >&2
ENABLE_LOGGING=0
fi
LOGGER_PARAMS="-t ${APP_NAME}"
WGET_CMD=`which wget`
if [ $? -ne 0 ]; then
echo " Error! Wget doesn't exists" >&2
exit 1
fi
WGET_PARAMS="--no-check-certificate -q -O"
NSLOOKUP_CMD=`which nslookup`
if [ $? -ne 0 ]; then
echo " Error! Nslookup doesn't exists" >&2
exit 1
fi
export IP_CMD="ip"
if [ $? -ne 0 ]; then
echo " Error! Iproute2 doesn't exists" >&2
exit 1
fi
ROUTE_CHECK_EXEC="${MODULES_DIR}/ruab_route_check"
export IP_DATA_FILE="${DATA_DIR}/${NAME}.ip"
export NFT_TABLE="ip r"
export NFT_TABLE_DNSMASQ="4#ip#r"
export NFTSET_ALLOWED_HOSTS="allowed_ip"
export NFTSET_BYPASS_IP="bi"
export NFTSET_BYPASS_FQDN="bd"
export NFTSET_FPROXY="fproxy"
export NFTSET_FPROXY_PRIVATE="fproxy_private"
export NFTSET_BLLIST_PROXY="bllist_proxy"
export NFTSET_ONION="onion"
export NFTSET_CIDR="c"
export NFTSET_IP="i"
export NFTSET_DNSMASQ="d"
export NFTSET_ALLOWED_HOSTS_TYPE="ipv4_addr"
export NFTSET_BYPASS_IP_TYPE="ipv4_addr"
export NFTSET_BYPASS_FQDN_TYPE="ipv4_addr"
export NFTSET_FPROXY_TYPE="ipv4_addr"
export NFTSET_FPROXY_PRIVATE_TYPE="ipv4_addr"
export NFTSET_BLLIST_PROXY_TYPE="ipv4_addr"
export NFTSET_CIDR_TYPE="ipv4_addr"
export NFTSET_IP_TYPE="ipv4_addr"
export NFTSET_DNSMASQ_TYPE="ipv4_addr"
export NFTSET_CIDR_CFG="set ${NFTSET_CIDR} {type ${NFTSET_CIDR_TYPE};size ${NFTSET_MAXELEM_CIDR};policy ${NFTSET_POLICY_CIDR};flags interval;auto-merge;"
export NFTSET_IP_CFG="set ${NFTSET_IP} {type ${NFTSET_IP_TYPE};size ${NFTSET_MAXELEM_IP};policy ${NFTSET_POLICY_IP};flags dynamic;"
export NFTSET_BYPASS_IP_CFG="set ${NFTSET_BYPASS_IP} {type ${NFTSET_BYPASS_IP_TYPE};size ${NFTSET_MAXELEM_BYPASS_IP};policy ${NFTSET_POLICY_CIDR};flags interval;auto-merge;"
export UPDATE_STATUS_FILE="${DATA_DIR}/update_status"
U_PID_FILE="/var/run/${NAME}_update.pid"
START_PID_FILE="/var/run/${NAME}_start.pid"
TOKEN_FILE="/var/run/${NAME}.token"
export HTML_OUTPUT="${HTML_DIR}/${NAME}.html"
NFT_FUNCTIONS="${SCRIPTS_DIR}/nft_functions"
INFO_OUTPUT_FUNCTION="${SCRIPTS_DIR}/info_output"
######################### External functions ###########################
. "$NFT_FUNCTIONS"
if [ -f "$INFO_OUTPUT_FUNCTION" ]; then
. "$INFO_OUTPUT_FUNCTION"
else
ENABLE_HTML_INFO=0
fi
############################## Functions ###############################
Help() {
cat << EOF
Usage: ${APP_NAME} start|force-start|stop|destroy|restart|reload|update|force-update|data-files|status|raw-status|html-info|help
start : Start
force-start : Removing the PID-file before running
stop : Stop
destroy : Stop, remove nft table and clear all data files
restart : Restart
reload : Renew nftables configuration
update : Update blacklist
force-update : Force update blacklist
data-files : Create ${IP_DATA_FILE}, ${DNSMASQ_DATA_FILE}, ${DNSMASQ_DATA_FILE_BYPASS} (without network functions)
status : Status & some info
raw-status : Return code: 0 - enabled, 1 - error, 2 - disabled, 3 - starting, 4 - updating
html-info : Return the html-info output
-h|--help|help : This message
Examples:
${APP_NAME} start
${APP_NAME} force-start
${APP_NAME} stop
${APP_NAME} destroy
${APP_NAME} restart
${APP_NAME} reload
${APP_NAME} update
${APP_NAME} force-update
${APP_NAME} data-files
${APP_NAME} status
${APP_NAME} raw-status
${APP_NAME} html-info
EOF
}
MakeLogRecord() {
if [ $ENABLE_LOGGING = "1" ]; then
$LOGGER_CMD $LOGGER_PARAMS -p "user.${1}" "$2"
fi
}
Download() {
$WGET_CMD $WGET_PARAMS "$1" "$2"
if [ $? -ne 0 ]; then
echo "Blacklist downloading failed! Connection error (${2})" >&2
MakeLogRecord "err" "Blacklist downloading failed! Connection error (${2})"
return 1
fi
}
DownloadRuabBlacklist() {
case "$1" in
"ip")
Download "$IP_DATA_FILE" "$RA_IP_IPSET_URL" && Download "$DNSMASQ_DATA_FILE" "$RA_IP_DMASK_URL" && Download "$UPDATE_STATUS_FILE" "$RA_IP_STAT_URL"
;;
"fqdn")
Download "$DNSMASQ_DATA_FILE" "$RA_FQDN_DMASK_URL" && Download "$IP_DATA_FILE" "$RA_FQDN_IPSET_URL" && Download "$UPDATE_STATUS_FILE" "$RA_FQDN_STAT_URL"
;;
*)
echo "Blacklist configuration error (${1})" >&2
MakeLogRecord "err" "Blacklist configuration error (${1})"
exit 1
;;
esac
}
RestartDnsmasq() {
eval `echo "$DNSMASQ_RESTART_CMD"`
}
FlushNftSets() {
local _set
for _set in "$@"
do
$NFT_CMD flush set $NFT_TABLE "$_set" &> /dev/null
done
}
FormatNftSetElemsList() {
printf "$1" | $AWK_CMD '{gsub(/[ ]+/, ",", $0); printf $0;}'
}
AddNftSets() {
local _allowed_hosts _bypass_ips _fproxy_hosts _fproxy_private
$NFT_CMD add set $NFT_TABLE "$NFTSET_CIDR" { type "$NFTSET_CIDR_TYPE"\; size $NFTSET_MAXELEM_CIDR\; policy "$NFTSET_POLICY_CIDR"\; flags interval\; auto-merge\; }
$NFT_CMD add set $NFT_TABLE "$NFTSET_IP" { type "$NFTSET_IP_TYPE"\; size $NFTSET_MAXELEM_IP\; policy "$NFTSET_POLICY_IP"\; flags dynamic\; }
$NFT_CMD add set $NFT_TABLE "$NFTSET_DNSMASQ" { type "$NFTSET_DNSMASQ_TYPE"\; size $NFTSET_MAXELEM_DNSMASQ\; policy "$NFTSET_POLICY_DNSMASQ"\; flags dynamic,timeout\; timeout "$NFTSET_DNSMASQ_TIMEOUT"\; }
$NFT_CMD add set $NFT_TABLE "$NFTSET_ONION" { type "$NFTSET_DNSMASQ_TYPE"\; size $NFTSET_MAXELEM_DNSMASQ\; policy "$NFTSET_POLICY_DNSMASQ"\; flags dynamic,timeout\; timeout "$NFTSET_DNSMASQ_TIMEOUT"\; }
$NFT_CMD add set $NFT_TABLE "$NFTSET_ALLOWED_HOSTS" { type "$NFTSET_ALLOWED_HOSTS_TYPE"\; policy "$NFTSET_POLICY_CIDR"\; flags interval\; auto-merge\; }
_allowed_hosts=`FormatNftSetElemsList "$ALLOWED_HOSTS_LIST"`
if [ -n "$_allowed_hosts" ]; then
$NFT_CMD add element $NFT_TABLE "$NFTSET_ALLOWED_HOSTS" { "$_allowed_hosts" }
fi
$NFT_CMD add set $NFT_TABLE "$NFTSET_BYPASS_IP" { type "$NFTSET_BYPASS_IP_TYPE"\; size $NFTSET_MAXELEM_BYPASS_IP\; policy "$NFTSET_POLICY_CIDR"\; flags interval\; auto-merge\; }
$NFT_CMD add set $NFT_TABLE "$NFTSET_BYPASS_FQDN" { type "$NFTSET_BYPASS_FQDN_TYPE"\; size $NFTSET_MAXELEM_BYPASS_FQDN\; policy "$NFTSET_POLICY_DNSMASQ"\; flags dynamic,timeout\; timeout "$NFTSET_DNSMASQ_TIMEOUT"\; }
$NFT_CMD add set $NFT_TABLE "$NFTSET_FPROXY" { type "$NFTSET_FPROXY_TYPE"\; policy "$NFTSET_POLICY_CIDR"\; flags interval\; auto-merge\; }
_fproxy_hosts=`FormatNftSetElemsList "$FPROXY_LIST"`
if [ -n "$_fproxy_hosts" ]; then
$NFT_CMD add element $NFT_TABLE "$NFTSET_FPROXY" { "$_fproxy_hosts" }
fi
$NFT_CMD add set $NFT_TABLE "$NFTSET_FPROXY_PRIVATE" { type "$NFTSET_FPROXY_PRIVATE_TYPE"\; policy "$NFTSET_POLICY_CIDR"\; flags interval\; auto-merge\; }
_fproxy_private=`FormatNftSetElemsList "$FPROXY_PRIVATE_NETS"`
if [ -n "$_fproxy_private" ]; then
$NFT_CMD add element $NFT_TABLE "$NFTSET_FPROXY_PRIVATE" { "$_fproxy_private" }
fi
$NFT_CMD add set $NFT_TABLE "$NFTSET_BLLIST_PROXY" { type "$NFTSET_BLLIST_PROXY_TYPE"\; policy "$NFTSET_POLICY_IP"\; flags dynamic\; }
}
UpdateBllistProxySet() {
local _ip_string=""
FlushNftSets "$NFTSET_BLLIST_PROXY"
for host in $BLLIST_HOSTS
do
if printf "$host" | $AWK_CMD '{exit ($0 ~ /^([0-9]{1,3}.){3}[0-9]{1,3}$/) ? 0 : 1}'; then
_ip_string="${_ip_string}${host} "
else
_ip_string="${_ip_string}`$NSLOOKUP_CMD $host | $AWK_CMD '/^Address: ([0-9]{1,3}.){3}[0-9]{1,3}$/ {printf $2" "}'`"
fi
done
_ip_string=`FormatNftSetElemsList "$_ip_string"`
if [ -n "$_ip_string" ]; then
$NFT_CMD add element $NFT_TABLE "$NFTSET_BLLIST_PROXY" { "$_ip_string" }
fi
}
UpdateBllistSets() {
local _return_code=0
if [ -f "$IP_DATA_FILE" ]; then
echo " Updating nft sets..."
$NFT_CMD -f "$IP_DATA_FILE"
_return_code=$?
if [ $_return_code -eq 0 ]; then
echo " Ok"
else
echo " Error! Nft set wasn't updated" >&2
MakeLogRecord "err" "Error! Nft set wasn't updated"
fi
fi
return $_return_code
}
AddNftRules() {
NftMainAdd
}
DeleteNftRules() {
NftMainDelete
}
SetNetConfig() {
$NFT_CMD add table $NFT_TABLE
AddNftSets
AddNftRules
}
DropNetConfig() {
DeleteNftRules
FlushNftSets "$NFTSET_ALLOWED_HOSTS" "$NFTSET_FPROXY" "$NFTSET_FPROXY_PRIVATE" "$NFTSET_BLLIST_PROXY" "$NFTSET_BYPASS_IP" "$NFTSET_BYPASS_FQDN" "$NFTSET_CIDR" "$NFTSET_IP" "$NFTSET_DNSMASQ" "$NFTSET_ONION"
}
DestroyNetConfig() {
$NFT_CMD flush table $NFT_TABLE &> /dev/null
$NFT_CMD delete table $NFT_TABLE &> /dev/null
}
ClearDataFiles() {
if [ -d "$DATA_DIR" ]; then
printf "" > "$DNSMASQ_DATA_FILE"
printf "" > "$DNSMASQ_DATA_FILE_BYPASS"
printf "" > "$IP_DATA_FILE"
printf "0 0 0" > "$UPDATE_STATUS_FILE"
fi
}
CheckStatus() {
NftReturnStatus
return $?
}
PreStartCheck() {
[ -d "$DATA_DIR" ] || mkdir -p "$DATA_DIR"
[ "$ENABLE_HTML_INFO" = "1" -a ! -d "$HTML_DIR" ] && mkdir -p "$HTML_DIR"
### Костыль для старта dnsmasq
[ -e "$DNSMASQ_DATA_FILE" ] || printf "" > "$DNSMASQ_DATA_FILE"
[ -e "$DNSMASQ_DATA_FILE_BYPASS" ] || printf "" > "$DNSMASQ_DATA_FILE_BYPASS"
}
AddUserEntries() {
if [ "$ADD_USER_ENTRIES" = "1" ]; then
if [ -f "$USER_ENTRIES_FILE" ]; then
$AWK_CMD 'BEGIN {
null = "";
ip_array[0] = null;
cidr_array[0] = null;
fqdn_array[0] = null;
}
function writeIpList(array, _str) {
_str = "";
for(i in array) {
_str = _str i ",";
};
return _str;
};
function writeDNSData(val, dns) {
if(length(dns) == 0 && length(ENVIRON["USER_ENTRIES_DNS"]) > 0) {
dns = ENVIRON["USER_ENTRIES_DNS"];
};
if(length(dns) > 0) {
printf "server=/%s/%s\n", val, dns >> ENVIRON["DNSMASQ_DATA_FILE"];
};
printf "nftset=/%s/%s#%s\n", val, ENVIRON["NFT_TABLE_DNSMASQ"], ENVIRON["NFTSET_DNSMASQ"] >> ENVIRON["DNSMASQ_DATA_FILE"];
};
function writeFqdnEntries() {
delete fqdn_array[0];
for(i in fqdn_array) {
split(fqdn_array[i], a, " ");
writeDNSData(a[1], a[2]);
};
};
($0 !~ /^([\040\011]*$|#)/) {
if($0 ~ /^[0-9]{1,3}([.][0-9]{1,3}){3}$/) {
ip_array[$0] = null;
}
else if($0 ~ /^[0-9]{1,3}([.][0-9]{1,3}){3}[\057][0-9]{1,2}$/) {
cidr_array[$0] = null;
}
else if($0 ~ /^[a-z0-9.\052-]+[.]([a-z]{2,}|xn--[a-z0-9]+)([ ][0-9]{1,3}([.][0-9]{1,3}){3}([#][0-9]{2,5})?)?$/) {
fqdn_array[length(fqdn_array)] = $1 " " $2;
};
}
END {
printf "table %s {\n%s", ENVIRON["NFT_TABLE"], ENVIRON["NFTSET_CIDR_CFG"] >> ENVIRON["IP_DATA_FILE"];
delete cidr_array[0];
if(length(cidr_array) > 0) {
printf "elements={%s};", writeIpList(cidr_array) >> ENVIRON["IP_DATA_FILE"];
};
printf "}\n%s", ENVIRON["NFTSET_IP_CFG"] >> ENVIRON["IP_DATA_FILE"];
delete ip_array[0];
if(length(ip_array) > 0) {
printf "elements={%s};", writeIpList(ip_array) >> ENVIRON["IP_DATA_FILE"];
};
printf "}\n}\n" >> ENVIRON["IP_DATA_FILE"];
writeFqdnEntries();
}' "$USER_ENTRIES_FILE"
fi
fi
}
AddBypassEntries() {
printf "" > "$DNSMASQ_DATA_FILE_BYPASS"
FlushNftSets "$NFTSET_BYPASS_IP" "$NFTSET_BYPASS_FQDN"
if [ "$BYPASS_MODE" = "1" ]; then
if [ -f "$BYPASS_ENTRIES_FILE" ]; then
$AWK_CMD 'BEGIN {
null = "";
ip_array[0] = null;
fqdn_array[0] = null;
}
function writeIpList(array, _str) {
_str = "";
for(i in array) {
_str = _str i ",";
};
return _str;
};
function writeDNSData(val, dns) {
if(length(dns) == 0 && length(ENVIRON["BYPASS_ENTRIES_DNS"]) > 0) {
dns = ENVIRON["BYPASS_ENTRIES_DNS"];
};
if(length(dns) > 0) {
printf "server=/%s/%s\n", val, dns >> ENVIRON["DNSMASQ_DATA_FILE_BYPASS"];
};
printf "nftset=/%s/%s#%s\n", val, ENVIRON["NFT_TABLE_DNSMASQ"], ENVIRON["NFTSET_BYPASS_FQDN"] >> ENVIRON["DNSMASQ_DATA_FILE_BYPASS"];
};
function writeFqdnEntries() {
delete fqdn_array[0];
for(i in fqdn_array) {
split(fqdn_array[i], a, " ");
writeDNSData(a[1], a[2]);
};
};
($0 !~ /^([\040\011]*$|#)/) {
if($0 ~ /^[0-9]{1,3}([.][0-9]{1,3}){3}([\057][0-9]{1,2})?$/) {
ip_array[$0] = null;
}
else if($0 ~ /^[a-z0-9.\052-]+[.]([a-z]{2,}|xn--[a-z0-9]+)([ ][0-9]{1,3}([.][0-9]{1,3}){3}([#][0-9]{2,5})?)?$/) {
fqdn_array[length(fqdn_array)] = $1 " " $2;
};
}
END {
printf "table %s {\n%s", ENVIRON["NFT_TABLE"], ENVIRON["NFTSET_BYPASS_IP_CFG"] >> ENVIRON["IP_DATA_FILE"];
delete ip_array[0];
if(length(ip_array) > 0) {
printf "elements={%s};", writeIpList(ip_array) >> ENVIRON["IP_DATA_FILE"];
};
printf "}\n}\n" >> ENVIRON["IP_DATA_FILE"];
writeFqdnEntries();
}' "$BYPASS_ENTRIES_FILE"
fi
fi
}
ToggleUPIDFile() {
if [ "$1" = "del" ]; then
rm -f "$U_PID_FILE"
else
echo "$$" > "$U_PID_FILE"
fi
}
GetDataFiles() {
local _return_code=1 _attempt=1 _update_string
PreStartCheck
if [ -n "$BLLIST_PRESET" -a -n "$BLLIST_MODULE" ]; then
while :
do
if [ "$ENABLE_BLLIST_PROXY" = "1" ]; then
UpdateBllistProxySet
fi
$BLLIST_MODULE
_return_code=$?
[ $_return_code -eq 0 ] && break
### STDOUT
echo " Module run attempt ${_attempt}: failed [${BLLIST_MODULE}]" >&2
MakeLogRecord "err" "Module run attempt ${_attempt}: failed [${BLLIST_MODULE}]"
_attempt=$(($_attempt + 1))
[ $_attempt -gt $MODULE_RUN_ATTEMPTS ] && break
sleep $MODULE_RUN_TIMEOUT
done
if [ "$ENABLE_BLLIST_PROXY" = "1" ]; then
FlushNftSets "$NFTSET_BLLIST_PROXY"
fi
AddUserEntries
AddBypassEntries
if [ $_return_code -eq 0 ]; then
_update_string=`$AWK_CMD '{
printf "Received entries: %s\n", (NF < 3) ? "No data" : "CIDR: "$1", IP: "$2", FQDN: "$3;
exit;
}' "$UPDATE_STATUS_FILE"`
### STDOUT
echo " ${_update_string}"
MakeLogRecord "notice" "${_update_string}"
printf " `date +%d.%m.%Y-%H:%M`\n" >> "$UPDATE_STATUS_FILE"
fi
elif [ -z "$BLLIST_PRESET" -a -z "$BLLIST_MODULE" ]; then
ClearDataFiles
ADD_USER_ENTRIES=1
AddUserEntries
AddBypassEntries
_return_code=0
else
_return_code=2
return $_return_code
fi
if [ $_return_code -eq 0 ]; then
if [ "$PROXY_MODE" = "2" -o "$PROXY_MODE" = "3" ]; then
printf "" >> "$DNSMASQ_DATA_FILE"
else
### Запись для .onion в $DNSMASQ_DATA_FILE
printf "server=/onion/%s\nnftset=/onion/%s#%s\n" "$ONION_DNS_ADDR" "$NFT_TABLE_DNSMASQ" "$NFTSET_ONION" >> "$DNSMASQ_DATA_FILE"
fi
fi
return $_return_code
}
MakeToken() {
date +%s > "$TOKEN_FILE"
}
Update() {
local _return_code=0
if CheckStatus; then
:
else
echo " ${NAME} ${1} - Error! ${NAME} does not running or another error has occurred" >&2
return 1
fi
MakeToken
if [ -e "$U_PID_FILE" ] && [ "$1" != "force-update" ]; then
echo " ${NAME} ${1} - Error! Another instance of update is already running" >&2
MakeLogRecord "err" "${1} - Error! Another instance of update is already running"
_return_code=2
else
ToggleUPIDFile add
echo " ${NAME} ${1}..."
MakeLogRecord "notice" "${1}..."
if [ "$NFTSET_CLEAR_SETS" = "1" ]; then
FlushNftSets "$NFTSET_CIDR" "$NFTSET_IP" "$NFTSET_DNSMASQ"
elif [ -z "$BLLIST_PRESET" -a -z "$BLLIST_MODULE" ]; then
FlushNftSets "$NFTSET_IP" "$NFTSET_CIDR"
fi
GetDataFiles
case $? in
0)
echo " Blacklist updated"
MakeLogRecord "notice" "Blacklist updated"
;;
2)
echo " Error! Blacklist update error" >&2
MakeLogRecord "err" "Error! Blacklist update error"
_return_code=1
;;
*)
echo " Module error! [${BLLIST_MODULE}]" >&2
MakeLogRecord "err" "Module error! [${BLLIST_MODULE}]"
_return_code=1
;;
esac
FlushNftSets "$NFTSET_DNSMASQ" "$NFTSET_ONION"
UpdateBllistSets
_return_code=$?
RestartDnsmasq
ToggleUPIDFile del
fi
MakeToken
return $_return_code
}
Start() {
local _return_code=1
if [ -e "$START_PID_FILE" ]; then
echo " ${NAME} is currently starting..." >&2
return 1
else
echo "$$" > "$START_PID_FILE"
fi
MakeToken
if CheckStatus; then
echo " ${NAME} is already running" >&2
_return_code=1
else
echo " ${NAME} ${1}..."
MakeLogRecord "info" "${1}..."
DropNetConfig &> /dev/null
SetNetConfig
PreStartCheck
UpdateBllistSets
_return_code=$?
if [ "$PROXY_MODE" = "2" -a "$VPN_ROUTE_CHECK" = "1" -a -x "$ROUTE_CHECK_EXEC" ]; then
$ROUTE_CHECK_EXEC start &> /dev/null &
fi
### Start-script
[ -x "$START_SCRIPT" ] && $START_SCRIPT > /dev/null 2>&1 &
fi
rm -f "$START_PID_FILE"
MakeToken
return $_return_code
}
Stop() {
local _return_code=1
if CheckStatus; then
MakeToken
echo " ${NAME} ${1}..."
MakeLogRecord "info" "${1}..."
DropNetConfig &> /dev/null
_return_code=$?
if [ -x "$ROUTE_CHECK_EXEC" ]; then
$ROUTE_CHECK_EXEC stop &> /dev/null
fi
### Stop-script
[ -x "$STOP_SCRIPT" ] && $STOP_SCRIPT > /dev/null 2>&1 &
MakeToken
else
echo " ${NAME} does not running" >&2
fi
return $_return_code
}
Reload() {
local _i=0 _attempts=60
MakeToken
while [ -e "$START_PID_FILE" ]
do
if [ $_i -ge $_attempts ]; then
return 1
fi
_i=$(($_i + 1))
sleep 1
done
echo " ${NAME} reload..."
DeleteNftRules &> /dev/null
AddNftRules &> /dev/null
MakeToken
}
Status() {
local _update_status _vpn_error
if [ -f "$UPDATE_STATUS_FILE" ]; then
_update_status=`$AWK_CMD '{
update_string=(NF < 4) ? "No data" : $4" (CIDR: "$1" | IP: "$2" | FQDN: "$3")";
printf "Last blacklist update: %s", update_string;
}' "$UPDATE_STATUS_FILE"`
else
_update_status="Last blacklist update: No data"
fi
if [ "$PROXY_MODE" = "2" ] && ! NftVpnRouteStatus; then
_vpn_error="\033[1;31mVPN ROUTING ERROR! (NEED THE RESTART)\033[m"
fi
NftListBllistChain 2> /dev/null | $AWK_CMD -v UPDATE_STATUS="$_update_status" -v VPN_ERROR="$_vpn_error" '
BEGIN {
rules_str = "";
nftset = "";
bytes = "";
}
/@/ {
if(match($0, /@[^ ]+/) != 0) {
nftset = substr($0, RSTART+1, RLENGTH-1);
if(match($0, /bytes [^ ]+/) != 0) {
bytes = substr($0, RSTART+6, RLENGTH-6);
};
rules_str = rules_str " Match-set: " nftset "\n Bytes: " bytes "\n\n";
};
}
END {
if(NR == 0) {
printf "\n \033[1m" ENVIRON["NAME"] " status\033[m: \033[1mDisabled\033[m\n\n";
exit 2;
};
printf "\n \033[1m" ENVIRON["NAME"] " status\033[m: \033[1;32mEnabled\033[m\n\n PROXY_MODE: " ENVIRON["PROXY_MODE"] "\n PROXY_LOCAL_CLIENTS: " ENVIRON["PROXY_LOCAL_CLIENTS"] "\n BLLIST_PRESET: " ENVIRON["BLLIST_PRESET"] "\n BLLIST_MODULE: " ENVIRON["BLLIST_MODULE"] "\n";
printf "\n "UPDATE_STATUS"\n";
if(length(VPN_ERROR) > 0) {
printf "\n "VPN_ERROR"\n";
};
printf "\n \033[4mNftables rules\033[m:\n\n";
printf rules_str;
}'
}
StatusOutput() {
if [ "$ENABLE_HTML_INFO" = "1" -a -d "$HTML_DIR" ]; then
Info
fi
}
############################ Main section ##############################
### Blacklist source and mode
case "$BLLIST_PRESET" in
zapret-info-ip)
### Источник для обновления списка блокировок (zapret-info, rublacklist, antifilter, ruantiblock)
export BLLIST_SOURCE="zapret-info"
### Режим обхода блокировок: ip, fqdn
export BLLIST_MODE="ip"
;;
zapret-info-fqdn)
export BLLIST_SOURCE="zapret-info"
export BLLIST_MODE="fqdn"
;;
rublacklist-ip)
export BLLIST_SOURCE="rublacklist"
export BLLIST_MODE="ip"
;;
rublacklist-fqdn)
export BLLIST_SOURCE="rublacklist"
export BLLIST_MODE="fqdn"
;;
antifilter-ip)
export BLLIST_SOURCE="antifilter"
export BLLIST_MODE="ip"
;;
ruantiblock-ip)
export BLLIST_SOURCE="ruantiblock"
export BLLIST_MODE="ip"
BLLIST_MODULE="DownloadRuabBlacklist $BLLIST_MODE"
;;
ruantiblock-fqdn)
export BLLIST_SOURCE="ruantiblock"
export BLLIST_MODE="fqdn"
BLLIST_MODULE="DownloadRuabBlacklist $BLLIST_MODE"
;;
*)
export BLLIST_SOURCE=""
export BLLIST_MODE=""
;;
esac
return_code=1
case "$1" in
start|force-start)
[ "$1" == "force-start" ] && rm -f "$START_PID_FILE"
Start "$1"
return_code=$?
StatusOutput
;;
stop)
Stop "$1"
return_code=$?
StatusOutput
;;
restart)
Stop "stop"
Start "start"
return_code=$?
StatusOutput
;;
reload)
Reload
return_code=$?
StatusOutput
;;
destroy)
Stop "$1" &> /dev/null
DestroyNetConfig
ClearDataFiles
return_code=$?
ToggleUPIDFile del
RestartDnsmasq
StatusOutput
;;
update|force-update)
Update "$1"
return_code=$?
StatusOutput
;;
data-files)
if [ -e "$U_PID_FILE" ] && [ "$1" != "force-update" ]; then
echo " ${NAME} - Error! Another instance of update is already running" >&2
exit 2
else
GetDataFiles
return_code=$?
fi
;;
status)
Status
return_code=$?
;;
raw-status)
CheckStatus
return_code=$?
case $return_code in
0)
if [ -e "$START_PID_FILE" ]; then
return_code=3
echo 3
elif [ -e "$U_PID_FILE" ]; then
return_code=4
echo 4
else
echo 0
fi
;;
*)
return_code=2
echo 2
;;
esac
;;
vpn-route-status)
NftVpnRouteStatus
return_code=$?
echo $return_code
;;
html-info)
Info
return_code=$?
;;
-h|--help|help)
Help
exit 0
;;
*)
Help
exit 1
;;
esac
exit $return_code;