mirror of
https://github.com/gSpotx2f/ruantiblock_openwrt.git
synced 2026-05-14 06:30:59 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
## Ruantiblock
|
||||
|
||||
|
||||
Обход блокировок в OpenWrt с помощью Tor или VPN.
|
||||
|
||||
|
||||
Подробно об установке и настройке: [https://github.com/gSpotx2f/ruantiblock_openwrt/wiki](https://github.com/gSpotx2f/ruantiblock_openwrt/wiki)
|
||||
|
||||
___________________
|
||||
Executable
+441
@@ -0,0 +1,441 @@
|
||||
#!/bin/sh
|
||||
|
||||
PREFIX=""
|
||||
TOR_USER="tor"
|
||||
|
||||
PROXY_MODE=1
|
||||
RAM_CONFIG=0
|
||||
LUA_MODULE=1
|
||||
LUCI_APP=1
|
||||
|
||||
### ruantiblock
|
||||
URL_CONFIG="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/ruantiblock/ruantiblock.conf"
|
||||
URL_FQDN_FILTER="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/ruantiblock/fqdn_filter"
|
||||
URL_CONFIG_SCRIPT="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/ruantiblock/scripts/config_script"
|
||||
URL_INFO_OUTPUT="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/ruantiblock/scripts/info_output"
|
||||
URL_IPT_FUNCTIONS="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/ruantiblock/scripts/ipt_functions"
|
||||
URL_START_SCRIPT="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/ruantiblock/scripts/start_script"
|
||||
URL_UCI_CONFIG="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/config/ruantiblock"
|
||||
URL_INIT_SCRIPT="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/init.d/ruantiblock"
|
||||
URL_MAIN_SCRIPT="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/usr/bin/ruantiblock"
|
||||
URL_VPN_UP="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock/files/etc/hotplug.d/iface/40-ruantiblock"
|
||||
### tor
|
||||
URL_TORRC="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/tor/etc/tor/torrc"
|
||||
### ruantiblock-mod-lua
|
||||
URL_PARSER_LUA="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock-mod-lua/files/usr/bin/ruab_parser.lua"
|
||||
URL_LUA_IPTOOL="https://raw.githubusercontent.com/gSpotx2f/iptool-lua/master/5.1/iptool.lua"
|
||||
URL_LUA_SUM_IP="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/ruantiblock-mod-lua/files/usr/lib/lua/ruab_sum_ip.lua"
|
||||
URL_LUA_IDN="https://raw.githubusercontent.com/haste/lua-idn/master/idn.lua"
|
||||
### luci-app-ruantiblock
|
||||
URL_LUCI_CONTROLLER="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/luasrc/controller/ruantiblock.lua"
|
||||
URL_LUCI_MENU="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/root/usr/share/luci/menu.d/luci-app-ruantiblock.json"
|
||||
URL_LUCI_RPCD_ACL="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/root/usr/share/rpcd/acl.d/luci-app-ruantiblock.json"
|
||||
URL_LUCI_JS_CRON="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/cron.js"
|
||||
URL_LUCI_JS_INFO="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/info.js"
|
||||
URL_LUCI_JS_SERVICE="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/service.js"
|
||||
URL_LUCI_JS_SETTINGS="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/settings.js"
|
||||
URL_LUCI_JS_TOOLS="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/tools.js"
|
||||
URL_LUCI_JS_LOG="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/htdocs/luci-static/resources/view/ruantiblock/log.js"
|
||||
URL_LUCI_JS_STATUS="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/luci-app-ruantiblock/htdocs/luci-static/resources/view/status/include/80_ruantiblock.js"
|
||||
URL_LUCI_I18N_RU="https://raw.githubusercontent.com/gSpotx2f/ruantiblock_openwrt/master/translations/ruantiblock.ru.lmo"
|
||||
|
||||
RUAB_CFG_DIR="${PREFIX}/etc/ruantiblock"
|
||||
EXEC_DIR="${PREFIX}/usr/bin"
|
||||
LUCI_ROOT="${PREFIX}/usr/lib/lua/luci"
|
||||
HTDOCS_VIEW="${PREFIX}/www/luci-static/resources/view"
|
||||
HTDOCS_RUAB="${HTDOCS_VIEW}/ruantiblock"
|
||||
BACKUP_DIR="${RUAB_CFG_DIR}/autoinstall.bak"
|
||||
DATA_DIR="${RUAB_CFG_DIR}/var"
|
||||
DATA_DIR_RAM="/var/ruantiblock"
|
||||
RC_LOCAL="/etc/rc.local"
|
||||
|
||||
### ruantiblock
|
||||
FILE_CONFIG="${RUAB_CFG_DIR}/ruantiblock.conf"
|
||||
FILE_FQDN_FILTER="${RUAB_CFG_DIR}/fqdn_filter"
|
||||
FILE_IP_FILTER="${RUAB_CFG_DIR}/ip_filter"
|
||||
FILE_USER_ENTRIES="${RUAB_CFG_DIR}/user_entries"
|
||||
FILE_CONFIG_SCRIPT="${RUAB_CFG_DIR}/scripts/config_script"
|
||||
FILE_INFO_OUTPUT="${RUAB_CFG_DIR}/scripts/info_output"
|
||||
FILE_IPT_FUNCTIONS="${RUAB_CFG_DIR}/scripts/ipt_functions"
|
||||
FILE_START_SCRIPT="${RUAB_CFG_DIR}/scripts/start_script"
|
||||
FILE_UCI_CONFIG="${PREFIX}/etc/config/ruantiblock"
|
||||
FILE_INIT_SCRIPT="${PREFIX}/etc/init.d/ruantiblock"
|
||||
FILE_MAIN_SCRIPT="${EXEC_DIR}/ruantiblock"
|
||||
FILE_VPN_UP="${PREFIX}/etc/hotplug.d/iface/40-ruantiblock"
|
||||
### tor
|
||||
FILE_TORRC="${PREFIX}/etc/tor/torrc"
|
||||
### ruantiblock-mod-lua
|
||||
FILE_PARSER_LUA="${EXEC_DIR}/ruab_parser.lua"
|
||||
FILE_LUA_IPTOOL="${PREFIX}/usr/lib/lua/iptool.lua"
|
||||
FILE_LUA_SUM_IP="${PREFIX}/usr/lib/lua/ruab_sum_ip.lua"
|
||||
FILE_LUA_IDN="${PREFIX}/usr/lib/lua/idn.lua"
|
||||
### luci-app-ruantiblock
|
||||
FILE_LUCI_CONTROLLER="${LUCI_ROOT}/controller/ruantiblock.lua"
|
||||
FILE_LUCI_I18N_RU="${LUCI_ROOT}/i18n/ruantiblock.ru.lmo"
|
||||
FILE_LUCI_MENU="${PREFIX}/usr/share/luci/menu.d/luci-app-ruantiblock.json"
|
||||
FILE_LUCI_RPCD_ACL="${PREFIX}/usr/share/rpcd/acl.d/luci-app-ruantiblock.json"
|
||||
FILE_LUCI_JS_CRON="${HTDOCS_RUAB}/cron.js"
|
||||
FILE_LUCI_JS_INFO="${HTDOCS_RUAB}/info.js"
|
||||
FILE_LUCI_JS_SERVICE="${HTDOCS_RUAB}/service.js"
|
||||
FILE_LUCI_JS_SETTINGS="${HTDOCS_RUAB}/settings.js"
|
||||
FILE_LUCI_JS_TOOLS="${HTDOCS_RUAB}/tools.js"
|
||||
FILE_LUCI_JS_LOG="${HTDOCS_RUAB}/log.js"
|
||||
FILE_LUCI_JS_STATUS="${HTDOCS_VIEW}/status/include/80_ruantiblock.js"
|
||||
|
||||
AWK_CMD="awk"
|
||||
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 "
|
||||
OPKG_CMD=`which opkg`
|
||||
if [ $? -ne 0 ]; then
|
||||
echo " Error! opkg doesn't exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
UCI_CMD=`which uci`
|
||||
if [ $? -ne 0 ]; then
|
||||
echo " Error! uci doesn't exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
InstallPackages() {
|
||||
local _pkg
|
||||
for _pkg in $@
|
||||
do
|
||||
if [ -z "`$OPKG_CMD list-installed $_pkg`" ]; then
|
||||
$OPKG_CMD install $_pkg
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error during installation of the package (${_pkg})" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
FileExists() {
|
||||
test -e "$1"
|
||||
}
|
||||
|
||||
MakeDir() {
|
||||
[ -d "$1" ] || mkdir -p "$1"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Error! Can't create directory (${1})" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
ChmodExec() {
|
||||
chmod 755 "$1"
|
||||
}
|
||||
|
||||
UpdatePackagesList() {
|
||||
$OPKG_CMD update
|
||||
}
|
||||
|
||||
DlFile() {
|
||||
local _dir _file
|
||||
if [ -n "$2" ]; then
|
||||
_dir=`dirname "$2"`
|
||||
MakeDir "$_dir"
|
||||
_file="$2"
|
||||
else
|
||||
_file="-"
|
||||
fi
|
||||
$WGET_CMD $WGET_PARAMS "$_file" "$1"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Connection error (${1})" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "Downloading ${1}"
|
||||
}
|
||||
|
||||
BackupFile() {
|
||||
[ -e "$1" ] && cp -f "$1" "${1}.bak.`date +%s`"
|
||||
}
|
||||
|
||||
BackupCurrentConfig() {
|
||||
local _file
|
||||
MakeDir "$BACKUP_DIR"
|
||||
for _file in "$FILE_CONFIG" "$FILE_FQDN_FILTER" "$FILE_IP_FILTER" "$FILE_USER_ENTRIES" "$FILE_UCI_CONFIG" "$FILE_TORRC"
|
||||
do
|
||||
[ -e "$_file" ] && cp -f "$_file" "${BACKUP_DIR}/`basename ${_file}`"
|
||||
done
|
||||
}
|
||||
|
||||
InstallBaseConfig() {
|
||||
InstallPackages "ipset" "kmod-ipt-ipset" "dnsmasq-full"
|
||||
DlFile "$URL_CONFIG" "$FILE_CONFIG"
|
||||
DlFile "$URL_FQDN_FILTER" "$FILE_FQDN_FILTER"
|
||||
DlFile "$URL_CONFIG_SCRIPT" "$FILE_CONFIG_SCRIPT"
|
||||
DlFile "$URL_INFO_OUTPUT" "$FILE_INFO_OUTPUT"
|
||||
DlFile "$URL_IPT_FUNCTIONS" "$FILE_IPT_FUNCTIONS"
|
||||
DlFile "$URL_START_SCRIPT" "$FILE_START_SCRIPT" && ChmodExec "$FILE_START_SCRIPT"
|
||||
DlFile "$URL_UCI_CONFIG" "$FILE_UCI_CONFIG"
|
||||
DlFile "$URL_INIT_SCRIPT" "$FILE_INIT_SCRIPT" && ChmodExec "$FILE_INIT_SCRIPT"
|
||||
DlFile "$URL_MAIN_SCRIPT" "$FILE_MAIN_SCRIPT" && ChmodExec "$FILE_MAIN_SCRIPT"
|
||||
DlFile "$URL_VPN_UP" "$FILE_VPN_UP" && ChmodExec "$FILE_VPN_UP"
|
||||
}
|
||||
|
||||
InstallVPNConfig() {
|
||||
local _if_vpn
|
||||
$UCI_CMD set ruantiblock.config.proxy_mode="2"
|
||||
_if_vpn=`$UCI_CMD get network.VPN.ifname`
|
||||
if [ -z "$_if_vpn" ]; then
|
||||
_if_vpn="tun0"
|
||||
fi
|
||||
$UCI_CMD set ruantiblock.config.if_vpn="$_if_vpn"
|
||||
$UCI_CMD commit
|
||||
}
|
||||
|
||||
TorrcSettings() {
|
||||
local _lan_ip=`$UCI_CMD get network.lan.ipaddr | $AWK_CMD -F "/" '{print $1}'`
|
||||
if [ -z "$_lan_ip" ]; then
|
||||
_lan_ip="0.0.0.0"
|
||||
fi
|
||||
$AWK_CMD -v lan_ip="$_lan_ip" -v TOR_USER="$TOR_USER" '{
|
||||
if($0 ~ /^([#]?TransPort|[#]?TransListenAddress|[#]?SOCKSPort)/ && $0 !~ "127.0.0.1") sub(/([0-9]{1,3}.){3}[0-9]{1,3}/, lan_ip, $0);
|
||||
else if($0 ~ /^User/) $2 = TOR_USER;
|
||||
print $0;
|
||||
}' "$FILE_TORRC" > "${FILE_TORRC}.tmp" && mv -f "${FILE_TORRC}.tmp" "$FILE_TORRC"
|
||||
}
|
||||
|
||||
InstallTorConfig() {
|
||||
local _if_lan
|
||||
InstallPackages "tor" "tor-geoip"
|
||||
BackupFile "$FILE_TORRC"
|
||||
DlFile "$URL_TORRC" "$FILE_TORRC"
|
||||
TorrcSettings
|
||||
$UCI_CMD set ruantiblock.config.proxy_mode="1"
|
||||
_if_lan=`$UCI_CMD get network.lan.ifname`
|
||||
if [ -z "$_if_lan" ]; then
|
||||
_if_lan="eth0"
|
||||
fi
|
||||
$UCI_CMD set ruantiblock.config.if_lan="$_if_lan"
|
||||
$UCI_CMD commit
|
||||
}
|
||||
|
||||
RamConfigPrepare() {
|
||||
$AWK_CMD -v DATA_DIR_RAM="$DATA_DIR_RAM" '{
|
||||
sub(/^DATA_DIR=.*$/, "DATA_DIR=\"" DATA_DIR_RAM "\"");
|
||||
print $0;
|
||||
}' "$FILE_CONFIG" > "${FILE_CONFIG}.tmp" && mv -f "${FILE_CONFIG}.tmp" "$FILE_CONFIG"
|
||||
$AWK_CMD -v FILE_MAIN_SCRIPT="$FILE_MAIN_SCRIPT" '{
|
||||
if($0 ~ /^exit 0/) next;
|
||||
print $0;
|
||||
} END { print FILE_MAIN_SCRIPT " update\nexit 0" }' "$RC_LOCAL" > "${RC_LOCAL}.tmp" && mv -f "${RC_LOCAL}.tmp" "$RC_LOCAL"
|
||||
}
|
||||
|
||||
InstallLuaModule() {
|
||||
InstallPackages "lua" "luasocket" "luasec" "luabitop"
|
||||
DlFile "$URL_PARSER_LUA" "$FILE_PARSER_LUA" && ChmodExec "$FILE_PARSER_LUA"
|
||||
DlFile "$URL_LUA_IPTOOL" "$FILE_LUA_IPTOOL"
|
||||
DlFile "$URL_LUA_SUM_IP" "$FILE_LUA_SUM_IP"
|
||||
FileExists "$FILE_LUA_IDN" || DlFile "$URL_LUA_IDN" "$FILE_LUA_IDN"
|
||||
$UCI_CMD set ruantiblock.config.bllist_module="/usr/bin/ruab_parser.lua"
|
||||
$UCI_CMD commit
|
||||
}
|
||||
|
||||
InstallLuciApp() {
|
||||
InstallPackages "luci-mod-rpc" "rpcd-mod-luci" "uhttpd-mod-ubus"
|
||||
DlFile "$URL_LUCI_CONTROLLER" "$FILE_LUCI_CONTROLLER"
|
||||
DlFile "$URL_LUCI_I18N_RU" "$FILE_LUCI_I18N_RU"
|
||||
DlFile "$URL_LUCI_MENU" "$FILE_LUCI_MENU"
|
||||
DlFile "$URL_LUCI_RPCD_ACL" "$FILE_LUCI_RPCD_ACL"
|
||||
DlFile "$URL_LUCI_JS_CRON" "$FILE_LUCI_JS_CRON"
|
||||
DlFile "$URL_LUCI_JS_INFO" "$FILE_LUCI_JS_INFO"
|
||||
DlFile "$URL_LUCI_JS_SERVICE" "$FILE_LUCI_JS_SERVICE"
|
||||
DlFile "$URL_LUCI_JS_SETTINGS" "$FILE_LUCI_JS_SETTINGS"
|
||||
DlFile "$URL_LUCI_JS_TOOLS" "$FILE_LUCI_JS_TOOLS"
|
||||
DlFile "$URL_LUCI_JS_LOG" "$FILE_LUCI_JS_LOG"
|
||||
DlFile "$URL_LUCI_JS_STATUS" "$FILE_LUCI_JS_STATUS"
|
||||
rm -f /tmp/luci-modulecache/*
|
||||
rm -f /tmp/luci-indexcache
|
||||
/etc/init.d/rpcd restart
|
||||
/etc/init.d/uhttpd restart
|
||||
}
|
||||
|
||||
RunAtStartup() {
|
||||
$FILE_INIT_SCRIPT enable
|
||||
}
|
||||
|
||||
AppStop() {
|
||||
FileExists "$FILE_MAIN_SCRIPT" && $FILE_MAIN_SCRIPT destroy
|
||||
}
|
||||
|
||||
AppStart() {
|
||||
modprobe ip_set > /dev/null
|
||||
modprobe ip_set_hash_ip > /dev/null
|
||||
modprobe ip_set_hash_net > /dev/null
|
||||
modprobe ip_set_list_set > /dev/null
|
||||
modprobe xt_set > /dev/null
|
||||
$FILE_INIT_SCRIPT start
|
||||
}
|
||||
|
||||
SetCronTask() {
|
||||
echo "0 3 */3 * * ${FILE_MAIN_SCRIPT} update" >> /etc/crontabs/root
|
||||
/etc/init.d/cron restart 2> /dev/null
|
||||
/etc/init.d/cron enable
|
||||
}
|
||||
|
||||
Reboot() {
|
||||
reboot
|
||||
}
|
||||
|
||||
PrintBold() {
|
||||
printf "\033[1m - ${1}\033[0m\n"
|
||||
}
|
||||
|
||||
InputError () {
|
||||
printf "\033[1;31m Wrong input! Try again...\033[m\n"; $1
|
||||
}
|
||||
|
||||
ConfirmProxyMode() {
|
||||
local _reply
|
||||
printf " Select configuration [1: Tor | 2: VPN] (default: 1, quit: q) > "
|
||||
read _reply
|
||||
case $_reply in
|
||||
1|"")
|
||||
PROXY_MODE=1
|
||||
break
|
||||
;;
|
||||
2)
|
||||
PROXY_MODE=2
|
||||
break
|
||||
;;
|
||||
q|Q)
|
||||
printf "Bye...\n"; exit 0
|
||||
;;
|
||||
*)
|
||||
InputError ConfirmProxyMode
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ConfirmRamConfig() {
|
||||
local _reply
|
||||
printf " Would you like to set the RAM-configuration? [y|n] (default: n, quit: q) > "
|
||||
read _reply
|
||||
case $_reply in
|
||||
y|Y)
|
||||
RAM_CONFIG=1
|
||||
break
|
||||
;;
|
||||
n|N|"")
|
||||
RAM_CONFIG=0
|
||||
break
|
||||
;;
|
||||
q|Q)
|
||||
printf "Bye...\n"; exit 0
|
||||
;;
|
||||
*)
|
||||
InputError ConfirmRamConfig
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ConfirmLuaModule() {
|
||||
local _reply
|
||||
printf " Would you like to install the lua module? [y|n] (default: y, quit: q) > "
|
||||
read _reply
|
||||
case $_reply in
|
||||
y|Y|"")
|
||||
LUA_MODULE=1
|
||||
break
|
||||
;;
|
||||
n|N)
|
||||
LUA_MODULE=0
|
||||
break
|
||||
;;
|
||||
q|Q)
|
||||
printf "Bye...\n"; exit 0
|
||||
;;
|
||||
*)
|
||||
InputError ConfirmLuaModule
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ConfirmLuciApp() {
|
||||
local _reply
|
||||
printf " Would you like to install the LuCI application? [y|n] (default: y, quit: q) > "
|
||||
read _reply
|
||||
case $_reply in
|
||||
y|Y|"")
|
||||
LUCI_APP=1
|
||||
break
|
||||
;;
|
||||
n|N)
|
||||
LUCI_APP=0
|
||||
break
|
||||
;;
|
||||
q|Q)
|
||||
printf "Bye...\n"; exit 0
|
||||
;;
|
||||
*)
|
||||
InputError ConfirmLuciApp
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ConfirmProcessing() {
|
||||
local _reply
|
||||
printf " Next, the installation will begin... Continue? [y|n] (default: y, quit: q) > "
|
||||
read _reply
|
||||
case $_reply in
|
||||
y|Y|"")
|
||||
break
|
||||
;;
|
||||
n|N|q|Q)
|
||||
printf "Bye...\n"; exit 0
|
||||
;;
|
||||
*)
|
||||
InputError ConfirmLuciApp
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ConfirmProxyMode
|
||||
ConfirmRamConfig
|
||||
ConfirmLuciApp
|
||||
ConfirmProcessing
|
||||
|
||||
AppStop
|
||||
PrintBold "Updating packages list..."
|
||||
UpdatePackagesList
|
||||
PrintBold "Saving current configuration..."
|
||||
BackupCurrentConfig
|
||||
PrintBold "Installing basic configuration..."
|
||||
InstallBaseConfig
|
||||
|
||||
if [ $PROXY_MODE = 2 ]; then
|
||||
PrintBold "Installing VPN configuration..."
|
||||
InstallVPNConfig
|
||||
else
|
||||
PrintBold "Installing Tor configuration..."
|
||||
InstallTorConfig
|
||||
if `/etc/init.d/tor enabled`; then
|
||||
/etc/init.d/tor restart
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $RAM_CONFIG = 1 ]; then
|
||||
PrintBold "Setting the RAM-configuration..."
|
||||
RamConfigPrepare
|
||||
fi
|
||||
|
||||
if [ $LUA_MODULE = 1 ]; then
|
||||
PrintBold "Installing lua module..."
|
||||
InstallLuaModule
|
||||
fi
|
||||
|
||||
if [ $LUCI_APP = 1 ]; then
|
||||
PrintBold "Installing luci app..."
|
||||
InstallLuciApp
|
||||
fi
|
||||
|
||||
RunAtStartup
|
||||
SetCronTask
|
||||
|
||||
exit 0
|
||||
Executable
+161
@@ -0,0 +1,161 @@
|
||||
#!/bin/sh
|
||||
|
||||
PREFIX=""
|
||||
|
||||
RUAB_CFG_DIR="${PREFIX}/etc/ruantiblock"
|
||||
EXEC_DIR="${PREFIX}/usr/bin"
|
||||
LUCI_ROOT="${PREFIX}/usr/lib/lua/luci"
|
||||
HTDOCS_VIEW="${PREFIX}/www/luci-static/resources/view"
|
||||
HTDOCS_RUAB="${HTDOCS_VIEW}/ruantiblock"
|
||||
CRONTAB_FILE="/etc/crontabs/root"
|
||||
DATA_DIR="${RUAB_CFG_DIR}/var"
|
||||
DATA_DIR_RAM="/var/ruantiblock"
|
||||
RC_LOCAL="/etc/rc.local"
|
||||
DNSMASQ_CONF_LINK="/tmp/dnsmasq.d/ruantiblock.conf"
|
||||
|
||||
### ruantiblock
|
||||
FILE_CONFIG="${RUAB_CFG_DIR}/ruantiblock.conf"
|
||||
FILE_FQDN_FILTER="${RUAB_CFG_DIR}/fqdn_filter"
|
||||
FILE_IP_FILTER="${RUAB_CFG_DIR}/ip_filter"
|
||||
FILE_USER_ENTRIES="${RUAB_CFG_DIR}/user_entries"
|
||||
FILE_CONFIG_SCRIPT="${RUAB_CFG_DIR}/scripts/config_script"
|
||||
FILE_INFO_OUTPUT="${RUAB_CFG_DIR}/scripts/info_output"
|
||||
FILE_IPT_FUNCTIONS="${RUAB_CFG_DIR}/scripts/ipt_functions"
|
||||
FILE_START_SCRIPT="${RUAB_CFG_DIR}/scripts/start_script"
|
||||
FILE_STOP_SCRIPT="${RUAB_CFG_DIR}/scripts/stop_script"
|
||||
FILE_UCI_CONFIG="${PREFIX}/etc/config/ruantiblock"
|
||||
FILE_INIT_SCRIPT="${PREFIX}/etc/init.d/ruantiblock"
|
||||
FILE_MAIN_SCRIPT="${EXEC_DIR}/ruantiblock"
|
||||
FILE_VPN_UP="${PREFIX}/etc/hotplug.d/iface/40-ruantiblock"
|
||||
### tor
|
||||
FILE_TORRC="${PREFIX}/etc/tor/torrc"
|
||||
### ruantiblock-mod-lua
|
||||
FILE_PARSER_LUA="${EXEC_DIR}/ruab_parser.lua"
|
||||
FILE_LUA_IPTOOL="${PREFIX}/usr/lib/lua/iptool.lua"
|
||||
FILE_LUA_SUM_IP="${PREFIX}/usr/lib/lua/ruab_sum_ip.lua"
|
||||
FILE_LUA_IDN="${PREFIX}/usr/lib/lua/idn.lua"
|
||||
### ruantiblock-mod-py
|
||||
FILE_PARSER_PY="${EXEC_DIR}/ruab_parser.py"
|
||||
FILE_PY_SUM_IP="${PREFIX}/usr/lib/python3.7/ruab_sum_ip.py"
|
||||
### luci-app-ruantiblock
|
||||
FILE_LUCI_CONTROLLER="${LUCI_ROOT}/controller/ruantiblock.lua"
|
||||
FILE_LUCI_I18N_RU="${LUCI_ROOT}/i18n/ruantiblock.ru.lmo"
|
||||
FILE_LUCI_MENU="${PREFIX}/usr/share/luci/menu.d/luci-app-ruantiblock.json"
|
||||
FILE_LUCI_RPCD_ACL="${PREFIX}/usr/share/rpcd/acl.d/luci-app-ruantiblock.json"
|
||||
FILE_LUCI_JS_CRON="${HTDOCS_RUAB}/cron.js"
|
||||
FILE_LUCI_JS_INFO="${HTDOCS_RUAB}/info.js"
|
||||
FILE_LUCI_JS_SERVICE="${HTDOCS_RUAB}/service.js"
|
||||
FILE_LUCI_JS_SETTINGS="${HTDOCS_RUAB}/settings.js"
|
||||
FILE_LUCI_JS_TOOLS="${HTDOCS_RUAB}/tools.js"
|
||||
FILE_LUCI_JS_LOG="${HTDOCS_RUAB}/log.js"
|
||||
FILE_LUCI_JS_STATUS="${HTDOCS_VIEW}/status/include/80_ruantiblock.js"
|
||||
|
||||
AWK_CMD="awk"
|
||||
|
||||
FileExists() {
|
||||
test -e "$1"
|
||||
}
|
||||
|
||||
RemoveFile() {
|
||||
if [ -e "$1" ]; then
|
||||
echo "Removing ${1}"
|
||||
rm -f "$1"
|
||||
fi
|
||||
}
|
||||
|
||||
RestoreTorConfig() {
|
||||
[ -e "${FILE_TORRC}.bak" ] && mv -f "${FILE_TORRC}.bak" "$FILE_TORRC"
|
||||
if `/etc/init.d/tor enabled`; then
|
||||
/etc/init.d/tor restart
|
||||
fi
|
||||
}
|
||||
|
||||
RemoveAppFiles() {
|
||||
RestoreTorConfig
|
||||
rm -rf "$DATA_DIR"
|
||||
rm -rf "$DATA_DIR_RAM"
|
||||
RemoveFile "$FILE_VPN_UP"
|
||||
RemoveFile "$FILE_CONFIG_SCRIPT"
|
||||
RemoveFile "$FILE_INFO_OUTPUT"
|
||||
RemoveFile "$FILE_IPT_FUNCTIONS"
|
||||
RemoveFile "$FILE_START_SCRIPT"
|
||||
RemoveFile "$FILE_STOP_SCRIPT"
|
||||
rmdir "${RUAB_CFG_DIR}/scripts" 2> /dev/null
|
||||
RemoveFile "$FILE_UCI_CONFIG"
|
||||
RemoveFile "$FILE_INIT_SCRIPT"
|
||||
RemoveFile "$FILE_MAIN_SCRIPT"
|
||||
RemoveFile "$FILE_SEARCH_SCRIPT"
|
||||
RemoveFile "$FILE_PARSER_LUA"
|
||||
RemoveFile "$FILE_LUA_IPTOOL"
|
||||
RemoveFile "$FILE_LUA_SUM_IP"
|
||||
RemoveFile "$FILE_PARSER_PY"
|
||||
RemoveFile "$FILE_PY_SUM_IP"
|
||||
RemoveFile "$FILE_LUCI_CONTROLLER"
|
||||
RemoveFile "$FILE_LUCI_I18N_RU"
|
||||
RemoveFile "$FILE_LUCI_MENU"
|
||||
RemoveFile "$FILE_LUCI_RPCD_ACL"
|
||||
RemoveFile "$FILE_LUCI_JS_CRON"
|
||||
RemoveFile "$FILE_LUCI_JS_INFO"
|
||||
RemoveFile "$FILE_LUCI_JS_SERVICE"
|
||||
RemoveFile "$FILE_LUCI_JS_SETTINGS"
|
||||
RemoveFile "$FILE_LUCI_JS_TOOLS"
|
||||
RemoveFile "$FILE_LUCI_JS_LOG"
|
||||
RemoveFile "$FILE_LUCI_JS_STATUS"
|
||||
rmdir "$HTDOCS_RUAB" 2> /dev/null
|
||||
rm -f /tmp/luci-modulecache/*
|
||||
rm -f /tmp/luci-indexcache
|
||||
/etc/init.d/rpcd restart
|
||||
/etc/init.d/uhttpd restart
|
||||
}
|
||||
|
||||
AppStop() {
|
||||
rm -f $DNSMASQ_CONF_LINK
|
||||
FileExists "$FILE_MAIN_SCRIPT" && $FILE_MAIN_SCRIPT destroy
|
||||
}
|
||||
|
||||
DisableStartup() {
|
||||
FileExists "$FILE_INIT_SCRIPT" && $FILE_INIT_SCRIPT disable
|
||||
}
|
||||
|
||||
RemoveCronTask() {
|
||||
$AWK_CMD -v FILE_MAIN_SCRIPT="$FILE_MAIN_SCRIPT" '$0 !~ FILE_MAIN_SCRIPT {
|
||||
print $0;
|
||||
}' "$CRONTAB_FILE" > "${CRONTAB_FILE}.tmp" && mv -f "${CRONTAB_FILE}.tmp" "$CRONTAB_FILE"
|
||||
/etc/init.d/cron restart
|
||||
}
|
||||
|
||||
RemoveRcLocalEntry() {
|
||||
$AWK_CMD -v FILE_MAIN_SCRIPT="$FILE_MAIN_SCRIPT" '$0 !~ FILE_MAIN_SCRIPT {
|
||||
print $0;
|
||||
}' "$RC_LOCAL" > "${RC_LOCAL}.tmp" && mv -f "${RC_LOCAL}.tmp" "$RC_LOCAL"
|
||||
}
|
||||
|
||||
InputError () {
|
||||
printf "\033[1;31m Wrong input! Try again...\033[m\n"; $1
|
||||
}
|
||||
|
||||
ConfirmRemove() {
|
||||
local _reply
|
||||
printf " Files will be removed... Continue? [y|n] (default: y, quit: q) > "
|
||||
read _reply
|
||||
case $_reply in
|
||||
y|Y|"")
|
||||
break
|
||||
;;
|
||||
n|N|q|Q)
|
||||
printf "Bye...\n"; exit 0
|
||||
;;
|
||||
*)
|
||||
InputError ConfirmRemove
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
ConfirmRemove
|
||||
AppStop
|
||||
DisableStartup
|
||||
RemoveCronTask
|
||||
RemoveRcLocalEntry
|
||||
RemoveAppFiles
|
||||
|
||||
exit 0
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 76 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
@@ -0,0 +1,220 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require view.ruantiblock.tools as tools';
|
||||
|
||||
let crontab_regexp = new RegExp(`^(\\*?\\/?(\\d){0,2}\\s){5}${tools.exec_path} update(\n)?`, 'gm');
|
||||
let current_crontab_content;
|
||||
|
||||
function to_dd(n){
|
||||
return String(n).replace(/^(\d)$/, "0$1");
|
||||
};
|
||||
|
||||
function cron_status_string(s) {
|
||||
return s || _('No Shedule');
|
||||
}
|
||||
|
||||
function pick_cron_task(content) {
|
||||
if(!content){
|
||||
return;
|
||||
};
|
||||
let current_tasks = content.match(crontab_regexp) || [];
|
||||
return current_tasks.join('');
|
||||
};
|
||||
|
||||
function set_cron_status(value) {
|
||||
document.getElementById('cron_status').value = cron_status_string(value);
|
||||
document.getElementById("btn_cron_del").style.visibility = (value) ? 'visible' : 'hidden';
|
||||
}
|
||||
|
||||
function write_cron_file() {
|
||||
let btn_cron_add = document.getElementById('btn_cron_add');
|
||||
let btn_cron_del = document.getElementById('btn_cron_del');
|
||||
|
||||
if(!current_crontab_content) {
|
||||
ui.addNotification(null, E('p', _('No changes to save.')));
|
||||
btn_cron_add.disabled = false;
|
||||
return;
|
||||
};
|
||||
|
||||
return fs.write(tools.crontab_file, current_crontab_content).then(rc => {
|
||||
ui.addNotification(null, E('p',_('Changes have been saved.')), 'info');
|
||||
set_cron_status(pick_cron_task(current_crontab_content));
|
||||
}).then(() => {
|
||||
return fs.exec('/etc/init.d/cron', [ 'enabled' ]).then(res => {
|
||||
if(res.code !== 0) {
|
||||
return fs.exec('/etc/init.d/cron', [ 'enable' ]);
|
||||
};
|
||||
}).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s<br />[ %s ]'.format(e.message, '/etc/init.d/cron')));
|
||||
});
|
||||
}).finally(() => {
|
||||
return fs.exec('/etc/init.d/cron', [ 'restart' ]).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s<br />[ %s ]'.format(e.message, '/etc/init.d/cron')));
|
||||
});
|
||||
}).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to save the changes')
|
||||
+ ': %s<br />[ %s ]'.format(
|
||||
e.message, tools.crontab_file
|
||||
)));
|
||||
});
|
||||
}
|
||||
|
||||
function del_cron_schedule(ev) {
|
||||
if(current_crontab_content) {
|
||||
current_crontab_content = current_crontab_content.replace(crontab_regexp, "");
|
||||
};
|
||||
return write_cron_file();
|
||||
};
|
||||
|
||||
function set_cron_schedule(ev) {
|
||||
let hour_interval = document.getElementById('cron_hour_interval').value;
|
||||
let day_interval = document.getElementById('cron_day_interval').value;
|
||||
let hour = document.getElementById('cron_hour').value;
|
||||
let min = document.getElementById('cron_min').value;
|
||||
let task_string = '%s %s %s * * %s update\n'.format(
|
||||
min,
|
||||
(!hour_interval) ? hour : (hour_interval == "1") ? '*' : '*/' + hour_interval,
|
||||
(hour_interval || day_interval == "1") ? '*' : '*/' + day_interval,
|
||||
tools.exec_path
|
||||
);
|
||||
if(current_crontab_content) {
|
||||
current_crontab_content = current_crontab_content.replace(crontab_regexp, "") + task_string;
|
||||
};
|
||||
return write_cron_file();
|
||||
};
|
||||
|
||||
function onchange_hour_interval(e) {
|
||||
let value = e.target.value;
|
||||
let bool = (value != '');
|
||||
let cron_hour = document.getElementById('cron_hour');
|
||||
let cron_day_interval = document.getElementById('cron_day_interval');
|
||||
cron_hour.disabled = bool;
|
||||
cron_day_interval.disabled = bool;
|
||||
|
||||
// For luci-theme-material
|
||||
if(bool) {
|
||||
cron_hour.style.opacity = '50%';
|
||||
cron_day_interval.style.opacity = '50%';
|
||||
} else {
|
||||
cron_hour.style.opacity = '100%';
|
||||
cron_day_interval.style.opacity = '100%';
|
||||
};
|
||||
}
|
||||
|
||||
return L.view.extend({
|
||||
load: function() {
|
||||
return fs.read(tools.crontab_file).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to read the contents')
|
||||
+ ': %s<br />[ %s ]'.format(
|
||||
e.message, tools.crontab_file
|
||||
)));
|
||||
});
|
||||
},
|
||||
|
||||
render: function(content) {
|
||||
current_crontab_content = content;
|
||||
let current_task = pick_cron_task(content);
|
||||
|
||||
let cron_status = E('textarea', {
|
||||
'id': 'cron_status',
|
||||
'name': 'cron_status',
|
||||
'style': 'width:30em; padding:5px 10px 5px 10px !important; vertical-align:middle; resize:none !important;',
|
||||
'readonly': 'readonly',
|
||||
'wrap': 'off',
|
||||
'rows': 2,
|
||||
}, cron_status_string(current_task));
|
||||
|
||||
let btn_cron_del = E('button', {
|
||||
'class': 'cbi-button btn cbi-button-reset',
|
||||
'id': 'btn_cron_del',
|
||||
'name': 'btn_cron_del',
|
||||
}, _('Reset'));
|
||||
btn_cron_del.onclick = ui.createHandlerFn(this, del_cron_schedule);
|
||||
btn_cron_del.style.visibility = (current_task) ? 'visible' : 'hidden';
|
||||
|
||||
let status_header = E('div', { 'class': 'cbi-section-node' }, [
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title', 'for': 'cron_status' },
|
||||
_('Current schedule')),
|
||||
E('div', { 'class': 'cbi-value-field' }, [ cron_status, ' ', btn_cron_del ]),
|
||||
])
|
||||
]);
|
||||
|
||||
let layout = E('div', { 'class': 'cbi-section-node' });
|
||||
|
||||
function layout_append(elem, title, descr) {
|
||||
descr = (descr) ? E('div', { 'class': 'cbi-value-description' }, descr) : '';
|
||||
layout.append(
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title', 'for': elem.id || null },
|
||||
title),
|
||||
E('div', { 'class': 'cbi-value-field' }, [ elem, descr ]),
|
||||
])
|
||||
)
|
||||
};
|
||||
|
||||
layout_append(E('b', {}, _('Interval')));
|
||||
|
||||
let cron_hour_interval = E('select',
|
||||
{ 'id': 'cron_hour_interval', 'style': 'width:60px !important; min-width:60px !important' }, [
|
||||
E('option', { 'value': '' }, ''),
|
||||
E('option', { 'value': '1' }, '∗')
|
||||
]);
|
||||
for(let i = 2; i <= 12 ; i += 2) {
|
||||
cron_hour_interval.append(E('option', { 'value': String(i) }, '∗/' + i));
|
||||
};
|
||||
layout_append(cron_hour_interval, _('Hour'));
|
||||
cron_hour_interval.onchange = onchange_hour_interval;
|
||||
|
||||
let cron_day_interval = E('select',
|
||||
{ 'id': 'cron_day_interval', 'style': 'width:60px !important; min-width:60px !important' },
|
||||
E('option', { 'value': '1' }, '∗')
|
||||
);
|
||||
for(let i = 2; i < 8 ; i++) {
|
||||
cron_day_interval.append(E('option', { 'value': String(i) }, '∗/' + i));
|
||||
};
|
||||
cron_day_interval.append(E('option', { 'value': '14' }, '∗/14'));
|
||||
cron_day_interval.append(E('option', { 'value': '28' }, '∗/28'));
|
||||
layout_append(cron_day_interval, _('Day'));
|
||||
|
||||
layout_append(E('b', {}, _('Time')));
|
||||
|
||||
let cron_hour = E('select',
|
||||
{ 'id': 'cron_hour', 'style': 'width:60px !important; min-width:60px !important' });
|
||||
for(let i = 0; i < 24 ; i++) {
|
||||
cron_hour.append(E('option', { 'value': String(i) }, to_dd(i)));
|
||||
};
|
||||
layout_append(cron_hour, _('Hour'));
|
||||
|
||||
let cron_min = E('select',
|
||||
{ 'id': 'cron_min', 'style': 'width:60px !important; min-width:60px !important' });
|
||||
for(let i = 0; i < 60 ; i++) {
|
||||
cron_min.append(E('option', { 'value': String(i) }, to_dd(i)));
|
||||
};
|
||||
layout_append(cron_min, _('Minute'));
|
||||
|
||||
let btn_cron_add = E('button', {
|
||||
'class': 'btn cbi-button-save',
|
||||
'id': 'btn_cron_add',
|
||||
'name': 'btn_cron_add'
|
||||
}, _('Set'));
|
||||
btn_cron_add.onclick = ui.createHandlerFn(this, set_cron_schedule);
|
||||
layout_append(btn_cron_add);
|
||||
|
||||
return E([
|
||||
E('h2',
|
||||
{ 'class': 'fade-in' }, _('Ruantiblock') + ' - ' + _('Blacklist updates') + ' (cron)'),
|
||||
E('div', { 'class': 'cbi-section-descr fade-in' }),
|
||||
E('div', { 'class': 'cbi-section fade-in' }, status_header),
|
||||
E('div', { 'class': 'cbi-section fade-in' }, layout),
|
||||
]);
|
||||
|
||||
},
|
||||
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
});
|
||||
@@ -0,0 +1,219 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require view.ruantiblock.tools as tools';
|
||||
|
||||
return L.view.extend({
|
||||
poll_info: function() {
|
||||
return fs.exec_direct(tools.exec_path, [ 'html-info' ], 'json').catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s<br />[ %s ]'.format(e.message, tools.exec_path)
|
||||
));
|
||||
L.Poll.stop();
|
||||
}).then(data => {
|
||||
if(!data) {
|
||||
return;
|
||||
};
|
||||
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(err) {};
|
||||
|
||||
if(data.status === 'enabled') {
|
||||
let date = document.getElementById('last_blacklist_update.date');
|
||||
|
||||
if(data.last_blacklist_update.status) {
|
||||
if(date) {
|
||||
date.textContent = data.last_blacklist_update.date;
|
||||
};
|
||||
|
||||
let ip = document.getElementById('last_blacklist_update.ip');
|
||||
if(ip) {
|
||||
ip.textContent = data.last_blacklist_update.ip;
|
||||
};
|
||||
|
||||
let cidr = document.getElementById('last_blacklist_update.cidr');
|
||||
if(cidr) {
|
||||
cidr.textContent = data.last_blacklist_update.cidr;
|
||||
};
|
||||
|
||||
let fqdn = document.getElementById('last_blacklist_update.fqdn');
|
||||
if(fqdn) {
|
||||
fqdn.textContent = data.last_blacklist_update.fqdn;
|
||||
};
|
||||
} else {
|
||||
if(date) {
|
||||
date.textContent = _('No data');
|
||||
};
|
||||
};
|
||||
|
||||
if(data.iptables) {
|
||||
for(let [k, v] of Object.entries(data.iptables)) {
|
||||
if(k === '_dummy') continue;
|
||||
|
||||
let elem = document.getElementById('iptables.' + k);
|
||||
if(elem) {
|
||||
elem.textContent = v;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if(data.ipset) {
|
||||
for(let [k, v] of Object.entries(data.ipset)) {
|
||||
if(k === '_dummy') continue;
|
||||
|
||||
let elem0 = document.getElementById('ipset.' + k + '.' + '0');
|
||||
let elem1 = document.getElementById('ipset.' + k + '.' + '1');
|
||||
if(elem0 && elem1) {
|
||||
elem0.textContent = v[0];
|
||||
elem1.textContent = v[1];
|
||||
};
|
||||
};
|
||||
};
|
||||
} else {
|
||||
if(L.Poll.active()) {
|
||||
L.Poll.stop();
|
||||
};
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
load: function() {
|
||||
return fs.exec_direct(tools.exec_path, [ 'html-info' ], 'json').catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s<br />[ %s ]'.format(e.message, tools.exec_path)
|
||||
));
|
||||
})
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
if(!data) {
|
||||
return;
|
||||
};
|
||||
|
||||
try {
|
||||
data = JSON.parse(data);
|
||||
} catch(err) {};
|
||||
|
||||
let update_status = null,
|
||||
iptables = null,
|
||||
ipset = null;
|
||||
if(data) {
|
||||
if(data.status === 'enabled') {
|
||||
update_status = E('div', { 'class': 'table' });
|
||||
|
||||
if(data.last_blacklist_update.status) {
|
||||
update_status.append(
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'width': '33%' },
|
||||
_('Last blacklist update')),
|
||||
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.date' },
|
||||
data.last_blacklist_update.date),
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'width': '33%' }, 'IP'),
|
||||
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.ip' },
|
||||
data.last_blacklist_update.ip),
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'width': '33%' }, 'CIDR'),
|
||||
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.cidr' },
|
||||
data.last_blacklist_update.cidr),
|
||||
]),
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'width': '33%' }, 'FQDN'),
|
||||
E('div', { 'class': 'td left', 'id': 'last_blacklist_update.fqdn' },
|
||||
data.last_blacklist_update.fqdn),
|
||||
])
|
||||
);
|
||||
} else {
|
||||
update_status.append(
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'width': '33%' },
|
||||
_('Last blacklist update')),
|
||||
E('div', { 'class': 'td left' }, _('No data')),
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
if(data.iptables) {
|
||||
let table_iptables = E('div', { 'class': 'table' }, [
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', { 'class': 'th left', 'width': '33%' },
|
||||
_('Match-set')),
|
||||
E('div', { 'class': 'th left' }, _('Bytes')),
|
||||
]),
|
||||
]);
|
||||
|
||||
for(let [k, v] of Object.entries(data.iptables)) {
|
||||
if(k === '_dummy') continue;
|
||||
|
||||
table_iptables.append(
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'width': '33%' },
|
||||
k),
|
||||
E('div', { 'class': 'td left', 'id': 'iptables.' + k },
|
||||
v),
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
iptables = E([
|
||||
E('h3', {}, _('Iptables rules')),
|
||||
table_iptables,
|
||||
]);
|
||||
};
|
||||
|
||||
if(data.ipset) {
|
||||
let table_ipset = E('div', { 'class': 'table' },
|
||||
E('div', { 'class': 'tr table-titles' }, [
|
||||
E('div', { 'class': 'th left', 'width': '33%' }, _('Name')),
|
||||
E('div', { 'class': 'th left' }, _('Size in memory')),
|
||||
E('div', { 'class': 'th left' }, _('Number of entries')),
|
||||
])
|
||||
);
|
||||
|
||||
for(let [k, v] of Object.entries(data.ipset)) {
|
||||
if(k === '_dummy') continue;
|
||||
|
||||
table_ipset.append(
|
||||
E('div', { 'class': 'tr' }, [
|
||||
E('div', { 'class': 'td left', 'width': '33%' }, k),
|
||||
E('div', { 'class': 'td left', 'id': 'ipset.' + k + '.' + '0' },
|
||||
v[0]),
|
||||
E('div', { 'class': 'td left', 'id': 'ipset.' + k + '.' + '1' },
|
||||
v[1]),
|
||||
])
|
||||
);
|
||||
};
|
||||
|
||||
ipset = E([
|
||||
E('h3', {}, _('Ipset')),
|
||||
table_ipset,
|
||||
]);
|
||||
};
|
||||
|
||||
L.Poll.add(this.poll_info);
|
||||
} else {
|
||||
update_status = E('em', {}, _('Status') + ' : ' + _('disabled'));
|
||||
};
|
||||
};
|
||||
return E([
|
||||
E('h2', { 'class': 'fade-in' }, _('Ruantiblock') + ' - ' + _('Statistics')),
|
||||
E('div', { 'class': 'cbi-section-descr fade-in' }),
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' }, update_status)
|
||||
),
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' }, iptables)
|
||||
),
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' }, ipset)
|
||||
),
|
||||
]);
|
||||
},
|
||||
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
});
|
||||
@@ -0,0 +1,201 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require ui';
|
||||
'require view.ruantiblock.tools as tools';
|
||||
|
||||
let log_regexp = new RegExp(`^.*(user\\.notice ${tools.app_name}).*$`, 'gm');
|
||||
|
||||
return L.view.extend({
|
||||
tail_default: 25,
|
||||
|
||||
parse_log_data: function(logdata) {
|
||||
return logdata.trim().match(log_regexp);
|
||||
},
|
||||
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.stat('/sbin/logread'), null),
|
||||
L.resolveDefault(fs.stat('/usr/sbin/logread'), null),
|
||||
]).then(stat => {
|
||||
let logger = (stat[0]) ? stat[0].path : (stat[1]) ? stat[1].path : null;
|
||||
|
||||
if(logger) {
|
||||
return fs.exec_direct(logger, [ '-e', tools.app_name ]).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s<br />[ %s ]'.format(e.message, logger)
|
||||
));
|
||||
return '';
|
||||
});
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
render: function(logdata) {
|
||||
let nav_btns_top = '120px';
|
||||
let loglines = this.parse_log_data(logdata);
|
||||
|
||||
let log_textarea = E('textarea', {
|
||||
'id': 'syslog',
|
||||
'class': 'cbi-input-textarea',
|
||||
'style': 'width:100% !important; padding: 0 0 0 45px; font-size:12px',
|
||||
'readonly': 'readonly',
|
||||
'wrap': 'off',
|
||||
'rows': this.tail_default,
|
||||
'spellcheck': 'false',
|
||||
}, [ loglines.slice(-this.tail_default).join('\n') ]);
|
||||
|
||||
let tail_value = E('input', {
|
||||
'id': 'tail_value',
|
||||
'name': 'tail_value',
|
||||
'type': 'text',
|
||||
'form': 'log_form',
|
||||
'class': 'cbi-input-text',
|
||||
'style': 'width:4em !important; min-width:4em !important',
|
||||
'maxlength': 5,
|
||||
});
|
||||
tail_value.value = this.tail_default;
|
||||
ui.addValidator(tail_value, 'uinteger', true);
|
||||
|
||||
let log_filter = E('input', {
|
||||
'id': 'log_filter',
|
||||
'name': 'log_filter',
|
||||
'type': 'text',
|
||||
'form': 'log_form',
|
||||
'class': 'cbi-input-text',
|
||||
'style': 'margin-left:1em !important; width:16em !important; min-width:16em !important',
|
||||
'placeholder': _('Message filter'),
|
||||
'data-tooltip': _('Filter messages with a regexp'),
|
||||
});
|
||||
|
||||
let log_form_submit_btn = E('input', {
|
||||
'type': 'submit',
|
||||
'form': 'log_form',
|
||||
'class': 'cbi-button btn',
|
||||
'style': 'margin-left:1em !important; vertical-align:middle',
|
||||
'value': _('Apply'),
|
||||
'click': ev => ev.target.blur(),
|
||||
});
|
||||
|
||||
function set_log_tail(c_arr) {
|
||||
let tail_num_val = tail_value.value;
|
||||
if(tail_num_val && tail_num_val > 0 && c_arr) {
|
||||
return c_arr.slice(-tail_num_val);
|
||||
};
|
||||
return c_arr;
|
||||
}
|
||||
|
||||
function set_log_filter(c_arr) {
|
||||
let f_pattern = log_filter.value;
|
||||
if(!f_pattern) {
|
||||
return c_arr;
|
||||
};
|
||||
let f_arr = [];
|
||||
try {
|
||||
f_arr = c_arr.filter(s => new RegExp(f_pattern.toLowerCase()).test(s.toLowerCase()));
|
||||
} catch(err) {
|
||||
if(err.name === 'SyntaxError') {
|
||||
ui.addNotification(null,
|
||||
E('p', {}, _('Wrong regular expression') + ': ' + err.message));
|
||||
return c_arr;
|
||||
} else {
|
||||
throw err;
|
||||
};
|
||||
};
|
||||
if(f_arr.length === 0) {
|
||||
f_arr.push(_('No matches...'));
|
||||
};
|
||||
return f_arr;
|
||||
}
|
||||
|
||||
return E([
|
||||
E('h2', { 'id': 'log_title', 'class': 'fade-in' }, _('Ruantiblock') + ' - ' + _('Log')),
|
||||
E('div', { 'class': 'cbi-section-descr fade-in' }),
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' },
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title', 'for': 'tail_value' },
|
||||
_('Show only the last messages')),
|
||||
E('div', { 'class': 'cbi-value-field' }, [
|
||||
tail_value,
|
||||
E('input', {
|
||||
'type': 'button',
|
||||
'form': 'log_form',
|
||||
'class': 'cbi-button btn cbi-button-reset',
|
||||
'value': 'Χ',
|
||||
'click': ev => {
|
||||
tail_value.value = null;
|
||||
log_form_submit_btn.click();
|
||||
ev.target.blur();
|
||||
},
|
||||
|
||||
}),
|
||||
log_filter,
|
||||
E('input', {
|
||||
'type': 'button',
|
||||
'form': 'log_form',
|
||||
'class': 'cbi-button btn cbi-button-reset',
|
||||
'value': 'Χ',
|
||||
'click': ev => {
|
||||
log_filter.value = null;
|
||||
log_form_submit_btn.click();
|
||||
ev.target.blur();
|
||||
},
|
||||
}),
|
||||
log_form_submit_btn,
|
||||
E('form', {
|
||||
'id': 'log_form',
|
||||
'name': 'log_form',
|
||||
'style': 'display:inline-block; margin-left:1em !important',
|
||||
'submit': ui.createHandlerFn(this, function(ev) {
|
||||
ev.preventDefault();
|
||||
let form_elems = Array.from(document.forms.log_form.elements);
|
||||
form_elems.forEach(e => e.disabled = true);
|
||||
|
||||
return this.load().then(logdata => {
|
||||
let loglines = set_log_filter(set_log_tail(
|
||||
this.parse_log_data(logdata)));
|
||||
log_textarea.value = loglines.join('\n');
|
||||
}).finally(() => {
|
||||
form_elems.forEach(e => e.disabled = false);
|
||||
});
|
||||
}),
|
||||
}, E('span', {}, ' ')),
|
||||
]),
|
||||
])
|
||||
)
|
||||
),
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
E('div', { 'class': 'cbi-section-node' },
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('div', { 'style': 'position:fixed' }, [
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'style': 'position:relative; display:block; margin:0 !important; left:1px; top:'
|
||||
+ nav_btns_top,
|
||||
'click': ev => {
|
||||
log_textarea.scrollTop = 0;
|
||||
ev.target.blur();
|
||||
},
|
||||
}, '↑'),
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'style': 'position:relative; display:block; margin:0 !important; margin-top:1px !important; left:1px; top:'
|
||||
+ nav_btns_top,
|
||||
'click': ev => {
|
||||
log_textarea.scrollTop = log_textarea.scrollHeight;
|
||||
ev.target.blur();
|
||||
},
|
||||
}, '↓'),
|
||||
]),
|
||||
log_textarea,
|
||||
])
|
||||
)
|
||||
),
|
||||
]);
|
||||
},
|
||||
|
||||
handleSaveApply: null,
|
||||
handleSave: null,
|
||||
handleReset: null,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,347 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require uci';
|
||||
'require ui';
|
||||
'require view.ruantiblock.tools as tools';
|
||||
|
||||
const btn_style_neutral = 'btn'
|
||||
const btn_style_action = 'btn cbi-button-action';
|
||||
const btn_style_save = 'btn cbi-button-save important';
|
||||
const btn_style_reset = 'btn cbi-button-reset important';
|
||||
const btn_style_warning = 'btn cbi-button-negative important'
|
||||
let status_token_value;
|
||||
|
||||
function disable_buttons(bool, btn, elems=[]) {
|
||||
let btn_start = elems[1] || document.getElementById("btn_start");
|
||||
let btn_destroy = elems[5] || document.getElementById("btn_destroy");
|
||||
let btn_enable = elems[2] || document.getElementById("btn_enable");
|
||||
let btn_update = elems[4] || document.getElementById("btn_update");
|
||||
let btn_tp = elems[3] || document.getElementById("btn_tp");
|
||||
|
||||
btn_start.disabled = bool;
|
||||
btn_update.disabled = bool;
|
||||
btn_destroy.disabled = bool;
|
||||
if(btn === btn_update) {
|
||||
btn_enable.disabled = false;
|
||||
} else {
|
||||
btn_enable.disabled = bool;
|
||||
};
|
||||
if(btn_tp) {
|
||||
btn_tp.disabled = bool
|
||||
};
|
||||
}
|
||||
|
||||
function get_app_status() {
|
||||
return Promise.all([
|
||||
fs.exec(tools.exec_path, [ 'raw-status' ]),
|
||||
fs.exec(tools.exec_path, [ 'total-proxy-status' ]),
|
||||
fs.exec(tools.exec_path, [ 'vpn-route-status' ]),
|
||||
fs.exec(tools.init_path, [ 'enabled' ]),
|
||||
L.resolveDefault(fs.read(tools.token_file), 0),
|
||||
uci.load(tools.app_name),
|
||||
]).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s<br />[ %s | %s | %s ]'.format(
|
||||
e.message, tools.exec_path, tools.init_path, 'uci.ruantiblock'
|
||||
)));
|
||||
});
|
||||
}
|
||||
|
||||
function set_app_status(status_array, elems=[], force_app_code) {
|
||||
let section = uci.get(tools.app_name, 'config');
|
||||
if(!status_array || typeof(section) !== 'object') {
|
||||
(elems[0] || document.getElementById("status")).innerHTML = tools.make_status_string(1);
|
||||
ui.addNotification(null, E('p', _('Unable to read the contents')
|
||||
+ ': set_app_status()'));
|
||||
disable_buttons(true, null, elems);
|
||||
return;
|
||||
};
|
||||
|
||||
let app_status_code = (force_app_code) ? force_app_code : status_array[0].code;
|
||||
let tp_status_code = status_array[1].code;
|
||||
let vpn_route_status_code = status_array[2].code;
|
||||
let enabled_flag = status_array[3].code;
|
||||
|
||||
let proxy_local_clients = section.proxy_local_clients;
|
||||
let proxy_mode = section.proxy_mode;
|
||||
let bllist_mode = section.bllist_mode;
|
||||
let bllist_module = section.bllist_module;
|
||||
let bllist_source = section.bllist_source;
|
||||
|
||||
let btn_enable = elems[2] || document.getElementById('btn_enable');
|
||||
if(enabled_flag == 0) {
|
||||
btn_enable.onclick = ui.createHandlerFn(this, button_action, 'disable');
|
||||
btn_enable.textContent = _('Disable');
|
||||
btn_enable.className = btn_style_reset;
|
||||
} else {
|
||||
btn_enable.onclick = ui.createHandlerFn(this, button_action, 'enable');
|
||||
btn_enable.textContent = _('Enable');
|
||||
btn_enable.className = btn_style_save;
|
||||
};
|
||||
|
||||
let btn_tp = elems[3] || document.getElementById('btn_tp');
|
||||
if(btn_tp) {
|
||||
if(tp_status_code == 0) {
|
||||
btn_tp.onclick = ui.createHandlerFn(this, button_action, 'total-proxy-off');
|
||||
btn_tp.textContent = _('Disable');
|
||||
btn_tp.className = btn_style_reset;
|
||||
} else {
|
||||
btn_tp.onclick = ui.createHandlerFn(this, button_action, 'total-proxy-on');
|
||||
btn_tp.textContent = _('Enable');
|
||||
btn_tp.className = btn_style_save;
|
||||
};
|
||||
};
|
||||
|
||||
let btn_start = elems[1] || document.getElementById("btn_start");
|
||||
let btn_update = elems[4] || document.getElementById("btn_update");
|
||||
let btn_destroy = elems[5] || document.getElementById("btn_destroy");
|
||||
|
||||
function btn_start_state_on() {
|
||||
btn_start.onclick = ui.createHandlerFn(this, button_action, 'stop');
|
||||
btn_start.textContent = _('Disable');
|
||||
btn_start.className = btn_style_reset;
|
||||
}
|
||||
|
||||
function btn_start_state_off() {
|
||||
btn_start.onclick = ui.createHandlerFn(this, button_action, 'start');
|
||||
btn_start.textContent = _('Enable');
|
||||
btn_start.className = btn_style_action;
|
||||
}
|
||||
|
||||
if(app_status_code == 0) {
|
||||
disable_buttons(false, null, elems);
|
||||
btn_start_state_on();
|
||||
btn_destroy.disabled = false;
|
||||
btn_update.disabled = false;
|
||||
if(btn_tp) {
|
||||
btn_tp.disabled = false;
|
||||
};
|
||||
}
|
||||
else if(app_status_code == 2) {
|
||||
disable_buttons(false, null, elems);
|
||||
btn_start_state_off();
|
||||
btn_update.disabled = true;
|
||||
if(btn_tp) {
|
||||
btn_tp.disabled = true;
|
||||
};
|
||||
}
|
||||
else if(app_status_code == 3) {
|
||||
btn_start_state_off();
|
||||
disable_buttons(true, btn_start, elems);
|
||||
}
|
||||
else if(app_status_code == 4) {
|
||||
btn_start_state_on();
|
||||
disable_buttons(true, btn_update, elems);
|
||||
}
|
||||
else {
|
||||
ui.addNotification(null, E('p', _('Error')
|
||||
+ ' %s: return code = %s'.format(tools.exec_path, app_status_code)));
|
||||
disable_buttons(true, null, elems);
|
||||
};
|
||||
|
||||
(elems[0] || document.getElementById("status")).innerHTML = tools.make_status_string(
|
||||
app_status_code,
|
||||
proxy_mode,
|
||||
bllist_mode,
|
||||
bllist_module,
|
||||
bllist_source,
|
||||
tp_status_code,
|
||||
vpn_route_status_code);
|
||||
|
||||
if(!L.Poll.active()) {
|
||||
L.Poll.start();
|
||||
};
|
||||
}
|
||||
|
||||
function button_action(action) {
|
||||
let btn,
|
||||
cmd = tools.exec_path;
|
||||
|
||||
switch(action) {
|
||||
case 'start':
|
||||
case 'stop':
|
||||
btn = document.getElementById('btn_start');
|
||||
break;
|
||||
case 'destroy':
|
||||
btn = document.getElementById('btn_destroy');
|
||||
break;
|
||||
case 'update':
|
||||
btn = document.getElementById('btn_update');
|
||||
break;
|
||||
case 'enable':
|
||||
case 'disable':
|
||||
btn = document.getElementById('btn_enable');
|
||||
cmd = tools.init_path;
|
||||
break;
|
||||
case 'total-proxy-on':
|
||||
case 'total-proxy-off':
|
||||
btn = document.getElementById('btn_tp');
|
||||
break;
|
||||
}
|
||||
|
||||
disable_buttons(true, btn);
|
||||
L.Poll.stop();
|
||||
|
||||
if(action === 'update') {
|
||||
get_app_status().then(status_array => {
|
||||
set_app_status(status_array, [], 4);
|
||||
});
|
||||
};
|
||||
|
||||
return fs.exec_direct(cmd, [ action ]).then(res => {
|
||||
return get_app_status().then(
|
||||
(status_array) => {
|
||||
set_app_status(status_array);
|
||||
ui.hideModal();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return L.view.extend({
|
||||
poll_status: function() {
|
||||
return fs.read(tools.token_file).then(v => {
|
||||
v = tools.normalize_value(v);
|
||||
if(v != status_token_value) {
|
||||
get_app_status().then(set_app_status);
|
||||
}
|
||||
status_token_value = v;
|
||||
}).catch(e => {
|
||||
status_token_value = 0;
|
||||
});
|
||||
},
|
||||
|
||||
dialog_destroy: function(ev) {
|
||||
ev.target.blur();
|
||||
let cancel_button = E('button', {
|
||||
'class': btn_style_neutral,
|
||||
'click': ui.hideModal,
|
||||
}, _('Cancel'));
|
||||
|
||||
let shutdown_btn = E('button', {
|
||||
'class': btn_style_warning,
|
||||
}, _('Shutdown'));
|
||||
shutdown_btn.onclick = ui.createHandlerFn(this, function() {
|
||||
cancel_button.disabled = true;
|
||||
return button_action('destroy');
|
||||
});
|
||||
|
||||
ui.showModal(_('Shutdown'), [
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('p', _('The service will be disabled and all blacklist data will be deleted. Continue?')),
|
||||
]),
|
||||
E('div', { 'class': 'right' }, [
|
||||
shutdown_btn,
|
||||
' ',
|
||||
cancel_button,
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
load: function() {
|
||||
return get_app_status();
|
||||
},
|
||||
|
||||
render: function(status_array) {
|
||||
if(!status_array) {
|
||||
return;
|
||||
};
|
||||
|
||||
let section = uci.get(tools.app_name, 'config');
|
||||
let proxy_local_clients = (typeof(section) === 'object') ? section.proxy_local_clients : null;
|
||||
status_token_value = (Array.isArray(status_array)) ? tools.normalize_value(status_array[4]) : null;
|
||||
|
||||
document.head.append(E('style', {'type': 'text/css'}, tools.css));
|
||||
|
||||
let status_string = E('div', {
|
||||
'id': 'status',
|
||||
'name': 'status',
|
||||
'class': 'cbi-section-node',
|
||||
});
|
||||
|
||||
let layout = E('div', { 'class': 'cbi-section-node' });
|
||||
|
||||
function layout_append(elem, title, descr) {
|
||||
descr = (descr) ? E('div', { 'class': 'cbi-value-description' }, descr) : '';
|
||||
layout.append(
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title' }, title),
|
||||
E('div', { 'class': 'cbi-value-field' }, [ elem, descr ]),
|
||||
])
|
||||
)
|
||||
};
|
||||
|
||||
let btn_start = E('button', {
|
||||
'id': 'btn_start',
|
||||
'name': 'btn_start',
|
||||
'class': btn_style_action,
|
||||
}, _('Enable'));
|
||||
layout_append(btn_start, _('Service'));
|
||||
|
||||
let btn_enable = E('button', {
|
||||
'id': 'btn_enable',
|
||||
'name': 'btn_enable',
|
||||
'class': btn_style_save,
|
||||
}, _('Enable'));
|
||||
layout_append(btn_enable, _('Run at startup'));
|
||||
|
||||
let btn_tp = E('button', {
|
||||
'id': 'btn_tp',
|
||||
'name': 'btn_tp',
|
||||
'class': btn_style_save,
|
||||
}, _('Enable'));
|
||||
if(proxy_local_clients == '0') {
|
||||
layout_append(btn_tp, _('Total-proxy'),
|
||||
_('All traffic goes through the proxy without applying rules'));
|
||||
};
|
||||
|
||||
let btn_update = E('button', {
|
||||
'id': 'btn_update',
|
||||
'name': 'btn_update',
|
||||
'class': btn_style_action,
|
||||
}, _('Update'));
|
||||
btn_update.onclick = ui.createHandlerFn(this, () => { button_action('update') });
|
||||
layout_append(btn_update, _('Update blacklist'));
|
||||
|
||||
let btn_destroy = E('button', {
|
||||
'id': 'btn_destroy',
|
||||
'name': 'btn_destroy',
|
||||
'class': btn_style_reset,
|
||||
}, _('Shutdown'));
|
||||
btn_destroy.onclick = this.dialog_destroy;
|
||||
|
||||
layout_append(btn_destroy, _('Shutdown'),
|
||||
_('Complete service shutdown, as well as deleting ipsets and blacklist data'));
|
||||
|
||||
set_app_status(status_array, [
|
||||
status_string,
|
||||
btn_start,
|
||||
btn_enable,
|
||||
btn_tp,
|
||||
btn_update,
|
||||
btn_destroy,
|
||||
]);
|
||||
|
||||
L.Poll.add(this.poll_status);
|
||||
|
||||
return E([
|
||||
E('h2', { 'class': 'fade-in' }, _('Ruantiblock')),
|
||||
E('div', { 'class': 'cbi-section-descr fade-in' },
|
||||
E('a', {
|
||||
'href': 'https://github.com/gSpotx2f/ruantiblock_openwrt/wiki',
|
||||
'target': '_blank' },
|
||||
'https://github.com/gSpotx2f/ruantiblock_openwrt/wiki')
|
||||
),
|
||||
E('div', { 'class': 'cbi-section fade-in' }, [
|
||||
status_string,
|
||||
E('hr'),
|
||||
]),
|
||||
E('div', { 'class': 'cbi-section fade-in' },
|
||||
layout
|
||||
),
|
||||
]);
|
||||
},
|
||||
|
||||
handleSave: null,
|
||||
handleSaveApply: null,
|
||||
handleReset: null,
|
||||
});
|
||||
@@ -0,0 +1,397 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require uci';
|
||||
'require form';
|
||||
'require ui';
|
||||
'require tools.widgets as widgets';
|
||||
'require view.ruantiblock.tools as tools';
|
||||
|
||||
let available_parsers = [];
|
||||
|
||||
function depends(elem, key, array, empty=true) {
|
||||
if(empty && array.length === 0) {
|
||||
elem.depends(key, '_dummy');
|
||||
} else {
|
||||
array.forEach(e => elem.depends(key, e));
|
||||
};
|
||||
};
|
||||
|
||||
function depends_bllist_module(elem) {
|
||||
depends(elem, 'bllist_module', available_parsers);
|
||||
};
|
||||
|
||||
function validate_ip_port(section, value) {
|
||||
return (/^$|^([0-9]{1,3}\.){3}[0-9]{1,3}(#[\d]{2,5})?$/.test(value)) ? true : _('Expecting:')
|
||||
+ ` ${_('One of the following:')}\n - ${_('valid IP address')}\n - ${_('valid address#port')}\n`;
|
||||
};
|
||||
|
||||
let CBIBlockTitle = form.DummyValue.extend({
|
||||
string: null,
|
||||
|
||||
renderWidget: function(section_id, option_index, cfgvalue) {
|
||||
this.title = this.description = null;
|
||||
return E([
|
||||
E('label', { 'class': 'cbi-value-title' }),
|
||||
E('div', { 'class': 'cbi-value-field' },
|
||||
E('b', {}, this.string)
|
||||
),
|
||||
]);
|
||||
},
|
||||
});
|
||||
|
||||
let ip_filter_edit = new tools.file_edit_dialog(
|
||||
tools.ip_filter_file,
|
||||
_('IP filter'),
|
||||
_('Patterns can be strings or regular expressions. Each pattern in a separate line, the symbol <code>#</code> in the first position of the line - comments on the line.<br />Examples (dot is a special character):') + '<br /><code>128[.]199[.]0[.]0/16<br />34[.]217[.]90[.]52<br />162[.]13[.]190[.]</code>'
|
||||
);
|
||||
|
||||
let fqdn_filter_edit = new tools.file_edit_dialog(
|
||||
tools.fqdn_filter_file,
|
||||
_('FQDN filter'),
|
||||
_('Patterns can be strings or regular expressions. Each pattern in a separate line, the symbol <code>#</code> in the first position of the line - comments on the line.<br />Examples:') + '<br /><code>poker<br />[ck]?a[sz]ino?<br />[vw]ulkan<br />slots?</code>'
|
||||
);
|
||||
|
||||
let user_entries_edit = new tools.file_edit_dialog(
|
||||
tools.user_entries_file,
|
||||
_('User entries'),
|
||||
_('One entry (IP, CIDR or FQDN) per line. In the FQDN records, you can specify the DNS server for resolving this domain (separated by a space). You can also comment on lines (<code>#</code> is the first character of a line).<br />Examples:') + '<br /><code>#comment<br />domain.net<br />sub.domain.com 8.8.8.8<br />sub.domain.com 8.8.8.8#53<br />74.125.131.19<br />74.125.0.0/16</code>'
|
||||
);
|
||||
|
||||
let torrc_edit = new tools.file_edit_dialog(
|
||||
tools.torrc_file,
|
||||
_('Tor configuration file'),
|
||||
null,
|
||||
function(rc) {
|
||||
return fs.exec('/etc/init.d/tor', [ 'enabled' ]).then(res => {
|
||||
if(res.code === 0) {
|
||||
return fs.exec('/etc/init.d/tor', [ 'restart' ]);
|
||||
};
|
||||
}).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to execute or read contents')
|
||||
+ ': %s<br />[ %s ]'.format(e.message, '/etc/init.d/tor')));
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
return L.view.extend({
|
||||
app_status_code: null,
|
||||
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
L.resolveDefault(fs.exec(tools.exec_path, [ 'raw-status' ]), 1),
|
||||
fs.list(tools.parsers_dir),
|
||||
uci.load('network'),
|
||||
]).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to read the contents')
|
||||
+ ': %s<br />[ %s ]'.format(
|
||||
e.message, tools.parsers_dir
|
||||
)));
|
||||
});
|
||||
},
|
||||
|
||||
render: function(data) {
|
||||
if(!data) {
|
||||
return;
|
||||
};
|
||||
this.app_status_code = data[0].code;
|
||||
let p_dir_arr = data[1];
|
||||
let lan_iface = uci.get('network', 'lan', 'ifname') || 'eth0';
|
||||
let vpn_iface = uci.get('network', 'VPN', 'ifname') || 'tun0';
|
||||
|
||||
if(p_dir_arr) {
|
||||
p_dir_arr.forEach(e => {
|
||||
let fname = e.name;
|
||||
if(fname.startsWith('ruab_parser')) {
|
||||
available_parsers.push(tools.parsers_dir + '/' + fname);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
let m, s, o;
|
||||
|
||||
m = new form.Map(tools.app_name, _('Ruantiblock') + ' - ' + _('Settings'));
|
||||
|
||||
s = m.section(form.NamedSection, 'config');
|
||||
s.anonymous = true;
|
||||
s.addremove = false;
|
||||
|
||||
/* Main settings tab */
|
||||
|
||||
s.tab('main_settings', _('Main settings'));
|
||||
|
||||
// PROXY_MODE
|
||||
if(this.app_status_code == 1 || this.app_status_code == 2) {
|
||||
o = s.taboption('main_settings', form.ListValue, 'proxy_mode',
|
||||
_('Proxy mode'));
|
||||
o.value('1', 'Tor');
|
||||
o.value('2', 'VPN');
|
||||
};
|
||||
|
||||
// PROXY_LOCAL_CLIENTS
|
||||
let proxy_local_clients = s.taboption('main_settings', form.Flag, 'proxy_local_clients',
|
||||
_("Apply proxy rules to router application traffic"));
|
||||
proxy_local_clients.rmempty = false;
|
||||
proxy_local_clients.default = proxy_local_clients.enabled;
|
||||
|
||||
// USE_LOGGER
|
||||
o = s.taboption('main_settings', form.Flag, 'use_logger',
|
||||
_('Logging events'));
|
||||
o.rmempty = false;
|
||||
o.default = 1;
|
||||
|
||||
// DEF_TOTAL_PROXY
|
||||
o = s.taboption('main_settings', form.Flag, 'def_total_proxy',
|
||||
_("Enable the 'total-proxy' option at startup"));
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
o.depends('proxy_local_clients', '0');
|
||||
|
||||
// IPSET_CLEAR_SETS
|
||||
o = s.taboption('main_settings', form.Flag, 'ipset_clear_sets',
|
||||
_('Clean up ipsets before updating blacklist'));
|
||||
o.description = _('Reduces RAM consumption during update');
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
|
||||
|
||||
if(this.app_status_code == 1 || this.app_status_code == 2) {
|
||||
/* Tor tab */
|
||||
|
||||
s.tab('tor_settings', _('Tor mode'));
|
||||
|
||||
// IF_LAN
|
||||
o = s.taboption('tor_settings', widgets.DeviceSelect, 'if_lan',
|
||||
_('LAN interface'));
|
||||
o.multiple = false;
|
||||
o.noaliases = true;
|
||||
o.rmempty = false;
|
||||
o.default = lan_iface;
|
||||
|
||||
// TOR_TRANS_PORT
|
||||
o = s.taboption('tor_settings', form.Value, 'tor_trans_port',
|
||||
_('Transparent proxy port for iptables rules'));
|
||||
o.rmempty = false;
|
||||
o.datatype = "port";
|
||||
o.default = '9040';
|
||||
|
||||
// ONION_DNS_ADDR
|
||||
o = s.taboption('tor_settings', form.Value, 'onion_dns_addr',
|
||||
_("Optional DNS resolver for '.onion' zone"), '<code>ipaddress#port</code>');
|
||||
o.rmempty = false;
|
||||
o.default = '127.0.0.1#9053';
|
||||
o.validate = validate_ip_port;
|
||||
|
||||
// Torrc edit dialog
|
||||
o = s.taboption('tor_settings', form.Button, '_torrc_btn',
|
||||
_('Tor configuration file'));
|
||||
o.onclick = () => torrc_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
|
||||
|
||||
/* VPN tab */
|
||||
|
||||
s.tab('vpn_settings', _('VPN mode'));
|
||||
|
||||
// IF_VPN
|
||||
o = s.taboption('vpn_settings', widgets.DeviceSelect, 'if_vpn',
|
||||
_('VPN interface'));
|
||||
o.multiple = false;
|
||||
o.noaliases = true;
|
||||
o.rmempty = false;
|
||||
o.default = vpn_iface;
|
||||
};
|
||||
|
||||
|
||||
/* Parser settings tab */
|
||||
|
||||
s.tab('parser_settings', _('Blacklist settings'));
|
||||
|
||||
// BLLIST_MODULE
|
||||
let bllist_module = s.taboption('parser_settings', form.ListValue,
|
||||
'bllist_module', _('Blacklist module'));
|
||||
bllist_module.value("", _("user entries only"));
|
||||
available_parsers.forEach(e => bllist_module.value(e));
|
||||
|
||||
// BLLIST_MODE
|
||||
let bllist_mode = s.taboption('parser_settings', form.ListValue,
|
||||
'bllist_mode', _('Module operation mode'));
|
||||
bllist_mode.value('ip');
|
||||
bllist_mode.value('fqdn');
|
||||
depends_bllist_module(bllist_mode);
|
||||
|
||||
// BLLIST_SOURCE
|
||||
let bllist_source = s.taboption('parser_settings', form.ListValue,
|
||||
'bllist_source', _('Blacklist source'));
|
||||
bllist_source.description = _("Options") + ':';
|
||||
for(let [k, v] of Object.entries(tools.blacklist_sources)) {
|
||||
bllist_source.value(k);
|
||||
bllist_source.description += `<br />${k} - <a href="${v}" target="_blank">${v}</a>`;
|
||||
};
|
||||
depends_bllist_module(bllist_source);
|
||||
|
||||
o = s.taboption('parser_settings', CBIBlockTitle, '_dummy_ip');
|
||||
o.string = _('IP configuration') + ':';
|
||||
depends_bllist_module(o);
|
||||
|
||||
// IP_LIMIT
|
||||
o = s.taboption('parser_settings', form.Value, 'ip_limit', _("IP limit"));
|
||||
o.description = _("The number of IP addresses in the subnet, upon reaching which the entire '/24' subnet is added to the list");
|
||||
o.datatype = 'uinteger';
|
||||
o.default = '0';
|
||||
depends_bllist_module(o);
|
||||
|
||||
// OPT_EXCLUDE_NETS
|
||||
o = s.taboption('parser_settings', form.DynamicList, 'opt_exclude_nets');
|
||||
o.title = _('IP subnet patterns (/24) that are excluded from optimization');
|
||||
o.description = _('ex:') + ' <code>192.168.1.</code>';
|
||||
o.placeholder = _('ex:') + ' 192.168.1.';
|
||||
o.default = '';
|
||||
depends_bllist_module(o);
|
||||
|
||||
// SUMMARIZE_IP
|
||||
o = s.taboption('parser_settings', form.Flag, 'summarize_ip',
|
||||
_("Summarize IP ranges"));
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
depends_bllist_module(o);
|
||||
|
||||
// SUMMARIZE_CIDR
|
||||
o = s.taboption('parser_settings', form.Flag, 'summarize_cidr',
|
||||
_("Summarize '/24' networks"));
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
depends_bllist_module(o);
|
||||
|
||||
o = s.taboption('parser_settings', CBIBlockTitle, '_dummy_fqdn');
|
||||
o.string = _('FQDN configuration') + ':';
|
||||
depends_bllist_module(o);
|
||||
|
||||
// SD_LIMIT
|
||||
o = s.taboption('parser_settings', form.Value, 'sd_limit',
|
||||
_("Subdomains limit"));
|
||||
o.description = _('The number of subdomains in the domain, upon reaching which the entire 2nd level domain is added to the list');
|
||||
o.datatype = 'uinteger';
|
||||
o.default = '16';
|
||||
depends_bllist_module(o);
|
||||
|
||||
// OPT_EXCLUDE_SLD
|
||||
o = s.taboption('parser_settings', form.DynamicList, 'opt_exclude_sld',
|
||||
_('2nd level domains that are excluded from optimization'));
|
||||
o.datatype = "hostname";
|
||||
o.default = [
|
||||
'livejournal.com',
|
||||
'facebook.com',
|
||||
'vk.com',
|
||||
'blog.jp',
|
||||
'msk.ru',
|
||||
'net.ru',
|
||||
'org.ru',
|
||||
'net.ua',
|
||||
'com.ua',
|
||||
'org.ua',
|
||||
'co.uk',
|
||||
'amazonaws.com',
|
||||
];
|
||||
depends_bllist_module(o);
|
||||
|
||||
// USE_IDN
|
||||
o = s.taboption('parser_settings', form.Flag, 'use_idn',
|
||||
_("Convert cyrillic domains to punycode"));
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
depends_bllist_module(o);
|
||||
|
||||
// ALT_NSLOOKUP
|
||||
o = s.taboption('parser_settings', form.Flag, 'alt_nslookup',
|
||||
_('Use optional DNS resolver'));
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
depends_bllist_module(o);
|
||||
|
||||
// ALT_DNS_ADDR
|
||||
o = s.taboption('parser_settings', form.Value, 'alt_dns_addr',
|
||||
_("Optional DNS resolver"), '<code>ipaddress[#port]</code>');
|
||||
o.rmempty = false;
|
||||
o.depends('alt_nslookup', '1');
|
||||
o.validate = validate_ip_port;
|
||||
o.default = '8.8.8.8';
|
||||
|
||||
|
||||
/* Entries filters tab */
|
||||
|
||||
s.tab('entries_filter_tab', _('Entries filters'));
|
||||
|
||||
// IP_FILTER
|
||||
o = s.taboption('entries_filter_tab', form.Flag, 'ip_filter',
|
||||
_("Enable IP filter"));
|
||||
o.description = _('Exclude IP addresses from blacklist by IP filter patterns');
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
depends_bllist_module(o);
|
||||
|
||||
// IP_FILTER edit dialog
|
||||
o = s.taboption('entries_filter_tab', form.Button, '_ip_filter_btn',
|
||||
_("IP filter"));
|
||||
o.onclick = () => ip_filter_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
depends_bllist_module(o);
|
||||
|
||||
// FQDN_FILTER
|
||||
o = s.taboption('entries_filter_tab', form.Flag, 'fqdn_filter',
|
||||
_("Enable FQDN filter"));
|
||||
o.description = _('Exclude domains from blacklist by FQDN filter patterns');
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
depends_bllist_module(o);
|
||||
|
||||
// FQDN_FILTER edit dialog
|
||||
o = s.taboption('entries_filter_tab', form.Button, '_fqdn_filter_btn',
|
||||
_("FQDN filter"));
|
||||
o.onclick = () => fqdn_filter_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
depends_bllist_module(o);
|
||||
|
||||
|
||||
/* User entries tab */
|
||||
|
||||
s.tab('user_entries_tab', _('User entries'));
|
||||
|
||||
// ADD_USER_ENTRIES
|
||||
o = s.taboption('user_entries_tab', form.Flag, 'add_user_entries',
|
||||
_('Enable'), _("Add user entries to the blacklist when updating"));
|
||||
o.rmempty = false;
|
||||
o.default = 0;
|
||||
depends_bllist_module(o);
|
||||
|
||||
// USER_ENTRIES_DNS
|
||||
o = s.taboption('user_entries_tab', form.Value, 'user_entries_dns',
|
||||
_("DNS server that is used for FQDN entries"), '<code>ipaddress[#port]</code>');
|
||||
o.validate = validate_ip_port;
|
||||
|
||||
// USER_ENTRIES edit dialog
|
||||
o = s.taboption('user_entries_tab', form.Button, '_user_entries_btn',
|
||||
_('User entries'));
|
||||
o.onclick = () => user_entries_edit.show();
|
||||
o.inputtitle = _('Edit');
|
||||
o.inputstyle = 'edit btn';
|
||||
|
||||
|
||||
let map_promise = m.render();
|
||||
map_promise.then(node => node.classList.add('fade-in'));
|
||||
|
||||
return map_promise;
|
||||
},
|
||||
|
||||
handleSaveApply: function(ev, mode) {
|
||||
return this.handleSave(ev).then(() => {
|
||||
ui.changes.apply(mode == '0');
|
||||
|
||||
if(this.app_status_code != 1 && this.app_status_code != 2) {
|
||||
window.setTimeout(() => fs.exec(tools.init_path, [ 'restart' ]), 3000);
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,249 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require ui';
|
||||
|
||||
return L.Class.extend({
|
||||
app_name: 'ruantiblock',
|
||||
exec_path: '/usr/bin/ruantiblock',
|
||||
init_path: '/etc/init.d/ruantiblock',
|
||||
token_file: '/var/run/ruantiblock.token',
|
||||
parsers_dir: '/usr/bin',
|
||||
torrc_file: '/etc/tor/torrc',
|
||||
user_entries_file: '/etc/ruantiblock/user_entries',
|
||||
fqdn_filter_file: '/etc/ruantiblock/fqdn_filter',
|
||||
ip_filter_file: '/etc/ruantiblock/ip_filter',
|
||||
crontab_file: '/etc/crontabs/root',
|
||||
info_label_starting: '<span class="label-status starting">' + _('Starting') + '</span>',
|
||||
info_label_running: '<span class="label-status running">' + _('Enabled') + '</span>',
|
||||
info_label_updating: '<span class="label-status updating">' + _('Updating') + '</span>',
|
||||
info_label_stopped: '<span class="label-status stopped">' + _('Disabled') + '</span>',
|
||||
info_label_error: '<span class="label-status error">' + _('Error') + '</span>',
|
||||
|
||||
blacklist_sources: {
|
||||
'rublacklist': 'https://rublacklist.net',
|
||||
'zapret-info': 'https://github.com/zapret-info/z-i',
|
||||
'antifilter': 'https://antifilter.download',
|
||||
},
|
||||
|
||||
css: `
|
||||
.label-status {
|
||||
display: inline;
|
||||
margin: 0px 2px 0px 0 !important;
|
||||
padding: 1px 4px 2px 4px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
font-weight: bold;
|
||||
color: #fff !important;
|
||||
}
|
||||
.starting {
|
||||
background-color: #a7b668 !important;
|
||||
}
|
||||
.running {
|
||||
background-color: #2ea256 !important;
|
||||
}
|
||||
.updating {
|
||||
background-color: #1e82ff !important;
|
||||
}
|
||||
.stopped {
|
||||
background-color: #acacac !important;
|
||||
}
|
||||
.error {
|
||||
background-color: #ff4e54 !important;
|
||||
}
|
||||
.total-proxy {
|
||||
background-color: #ffb937 !important;
|
||||
}`,
|
||||
|
||||
normalize_value: function(v) {
|
||||
return (v && typeof(v) === 'string') ? v.trim().replace(/\r?\n/g, '') : v;
|
||||
},
|
||||
|
||||
make_status_string: function(
|
||||
app_status_code,
|
||||
proxy_mode,
|
||||
bllist_mode,
|
||||
bllist_module,
|
||||
bllist_source,
|
||||
tp_status_code,
|
||||
vpn_route_status_code) {
|
||||
let app_status_label;
|
||||
let spinning = '';
|
||||
|
||||
switch(app_status_code) {
|
||||
case 0:
|
||||
app_status_label = this.info_label_running;
|
||||
break;
|
||||
case 2:
|
||||
app_status_label = this.info_label_stopped;
|
||||
break;
|
||||
case 3:
|
||||
app_status_label = this.info_label_starting;
|
||||
spinning = ' spinning';
|
||||
break;
|
||||
case 4:
|
||||
app_status_label = this.info_label_updating;
|
||||
spinning = ' spinning';
|
||||
break;
|
||||
default:
|
||||
app_status_label = this.info_label_error;
|
||||
return `<div class="table">
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:33%">
|
||||
${_('Status')}
|
||||
</div>
|
||||
<div class="td left">
|
||||
${app_status_label}
|
||||
</div>
|
||||
</div>
|
||||
</div>`
|
||||
};
|
||||
|
||||
return `<div class="table">
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:33%%">
|
||||
${_('Status')}
|
||||
</div>
|
||||
<div class="td left%s">
|
||||
%s %s %s
|
||||
</div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:33%%">
|
||||
${_('Proxy mode')}
|
||||
</div>
|
||||
<div class="td left">
|
||||
%s
|
||||
</div>
|
||||
</div>
|
||||
<div class="tr">
|
||||
<div class="td left" style="width:33%%">
|
||||
${_('Blacklist update mode')}
|
||||
</div>
|
||||
<div class="td left">
|
||||
%s
|
||||
</div>
|
||||
</div>
|
||||
%s
|
||||
</div>
|
||||
`.format(
|
||||
spinning,
|
||||
app_status_label,
|
||||
(tp_status_code == 0) ? '<span class="label-status total-proxy">'
|
||||
+ _('Total-proxy is on') + '</span>' : '',
|
||||
(app_status_code != 2 && proxy_mode == 2 && vpn_route_status_code != 0)
|
||||
? '<span class="label-status error">'
|
||||
+ _('VPN routing error! Need restart') + '</span>' : '',
|
||||
(proxy_mode == 1) ? 'Tor' : 'VPN',
|
||||
(!bllist_module || bllist_module === '') ? _('user entries only') : bllist_mode,
|
||||
(!bllist_module || bllist_module === '') ? '' : `<div class="tr">
|
||||
<div class="td left" style="width:33%%">
|
||||
${_('Blacklist source')}
|
||||
</div>
|
||||
<div class="td left">
|
||||
<span style="cursor:help; border-bottom:1px dotted" data-tooltip="${this.blacklist_sources[bllist_source]}">
|
||||
${bllist_source}
|
||||
</span>
|
||||
</div>
|
||||
</div>`
|
||||
);
|
||||
},
|
||||
|
||||
file_edit_dialog: L.Class.extend({
|
||||
__init__: function(file, title, description, callback, file_exists=false) {
|
||||
this.file = file;
|
||||
this.title = title;
|
||||
this.description = description;
|
||||
this.callback = callback;
|
||||
this.file_exists = file_exists;
|
||||
},
|
||||
|
||||
load: function() {
|
||||
return fs.read(this.file);
|
||||
},
|
||||
|
||||
render: function(content) {
|
||||
ui.showModal(this.title, [
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('div', { 'class': 'cbi-section-descr' }, this.description),
|
||||
E('div', { 'class': 'cbi-section' },
|
||||
E('p', {},
|
||||
E('textarea', {
|
||||
'id': 'widget.modal_content',
|
||||
'class': 'cbi-input-textarea',
|
||||
'style': 'width:100% !important',
|
||||
'rows': 10,
|
||||
'wrap': 'off',
|
||||
'spellcheck': 'false',
|
||||
},
|
||||
content || '')
|
||||
)
|
||||
),
|
||||
]),
|
||||
E('div', { 'class': 'right' }, [
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.hideModal,
|
||||
}, _('Dismiss')),
|
||||
' ',
|
||||
E('button', {
|
||||
'id': 'btn_save',
|
||||
'class': 'btn cbi-button-positive important',
|
||||
'click': ui.createHandlerFn(this, this.handleSave),
|
||||
}, _('Save')),
|
||||
]),
|
||||
]);
|
||||
},
|
||||
|
||||
handleSave: function(ev) {
|
||||
let textarea = document.getElementById('widget.modal_content');
|
||||
let value = textarea.value.trim().replace(/\r\n/g, '\n') + '\n';
|
||||
|
||||
return fs.write(this.file, value).then(async rc => {
|
||||
textarea.value = value;
|
||||
ui.addNotification(null, E('p', _('Contents have been saved.')),
|
||||
'info');
|
||||
if(this.callback) {
|
||||
return this.callback(rc);
|
||||
};
|
||||
}).catch(e => {
|
||||
ui.addNotification(null, E('p', _('Unable to save the contents')
|
||||
+ ': %s'.format(e.message)));
|
||||
}).finally(() => {
|
||||
ui.hideModal();
|
||||
});
|
||||
},
|
||||
|
||||
error: function(e) {
|
||||
if(!this.file_exists && e instanceof Error && e.name === 'NotFoundError') {
|
||||
return this.render();
|
||||
} else {
|
||||
ui.showModal(this.title, [
|
||||
E('div', { 'class': 'cbi-section' },
|
||||
E('p', {}, _('Unable to read the contents')
|
||||
+ ': %s'.format(e.message))
|
||||
),
|
||||
E('div', { 'class': 'right' },
|
||||
E('button', {
|
||||
'class': 'btn',
|
||||
'click': ui.hideModal,
|
||||
}, _('Dismiss'))
|
||||
),
|
||||
]);
|
||||
};
|
||||
},
|
||||
|
||||
show: function() {
|
||||
ui.showModal(null,
|
||||
E('p', { 'class': 'spinning' }, _('Loading'))
|
||||
);
|
||||
this.load().then(content => {
|
||||
ui.hideModal();
|
||||
return this.render(content);
|
||||
}).catch(e => {
|
||||
ui.hideModal();
|
||||
return this.error(e);
|
||||
})
|
||||
},
|
||||
}),
|
||||
});
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
'require fs';
|
||||
'require uci';
|
||||
'require view.ruantiblock.tools as tools';
|
||||
|
||||
return L.Class.extend({
|
||||
title: _('Ruantiblock'),
|
||||
|
||||
load: function() {
|
||||
return Promise.all([
|
||||
fs.exec(tools.exec_path, [ 'raw-status' ]),
|
||||
fs.exec(tools.exec_path, [ 'total-proxy-status' ]),
|
||||
fs.exec(tools.exec_path, [ 'vpn-route-status' ]),
|
||||
uci.load('ruantiblock'),
|
||||
]).catch(e => {});
|
||||
},
|
||||
|
||||
render: function(status_array) {
|
||||
if(!status_array) {
|
||||
return E('em', _('Error') + ': ' + _('Unable to execute or read contents'));
|
||||
};
|
||||
|
||||
let app_status_code = status_array[0].code;
|
||||
let tp_status_code = status_array[1].code;
|
||||
let vpn_route_status_code = status_array[2].code;
|
||||
|
||||
let section = uci.get('ruantiblock', 'config');
|
||||
let proxy_local_clients, proxy_mode, bllist_mode, bllist_module, bllist_source;
|
||||
|
||||
if(typeof(section) === 'object') {
|
||||
proxy_local_clients = section.proxy_local_clients;
|
||||
proxy_mode = section.proxy_mode;
|
||||
bllist_mode = section.bllist_mode;
|
||||
bllist_module = section.bllist_module;
|
||||
bllist_source = section.bllist_source;
|
||||
} else {
|
||||
return _('Error');
|
||||
};
|
||||
|
||||
document.head.append(E('style', { 'type': 'text/css' }, tools.css));
|
||||
|
||||
return E('div', { 'class': 'cbi-section' }).innerHTML = tools.make_status_string(
|
||||
app_status_code,
|
||||
proxy_mode,
|
||||
bllist_mode,
|
||||
bllist_module,
|
||||
bllist_source,
|
||||
tp_status_code,
|
||||
vpn_route_status_code);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
|
||||
module('luci.controller.ruantiblock', package.seeall)
|
||||
|
||||
function index()
|
||||
if nixio.fs.access('/etc/config/ruantiblock') and nixio.fs.access('/usr/bin/ruantiblock') then
|
||||
entry({'admin', 'services', 'ruantiblock'}, firstchild(), _('Ruantiblock'), 60).acl_depends = { 'luci-app-ruantiblock' }
|
||||
entry({'admin', 'services', 'ruantiblock', 'service'}, view('ruantiblock/service'), _('Service'), 10)
|
||||
entry({'admin', 'services', 'ruantiblock', 'settings'}, view('ruantiblock/settings'), _('Settings'), 20)
|
||||
entry({'admin', 'services', 'ruantiblock', 'cron'}, view('ruantiblock/cron'), _('Blacklist updates'), 30)
|
||||
entry({'admin', 'services', 'ruantiblock', 'info'}, view('ruantiblock/info'), _('Statistics'), 40)
|
||||
entry({'admin', 'services', 'ruantiblock', 'log'}, view('ruantiblock/log'), _('Log'), 50)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,373 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ru\n"
|
||||
"X-Generator: Poedit 2.0.6\n"
|
||||
|
||||
msgid "valid IP address"
|
||||
msgstr "верный IP-адрес"
|
||||
|
||||
msgid "valid address#port"
|
||||
msgstr "верный IP-адрес#порт"
|
||||
|
||||
msgid "2nd level domains that are excluded from optimization"
|
||||
msgstr "Домены 2-го уровня не подлежащие оптимизации"
|
||||
|
||||
msgid "Add user entries to the blacklist when updating"
|
||||
msgstr "Добавлять записи пользователя в блэклист при обновлении"
|
||||
|
||||
msgid "All traffic goes through the proxy without applying rules"
|
||||
msgstr "Весь трафик отправляется в прокси, без применения правил"
|
||||
|
||||
msgid "Apply"
|
||||
msgstr "Применить"
|
||||
|
||||
msgid "Apply proxy rules to router application traffic"
|
||||
msgstr "Применять правила прокси к трафику приложений роутера"
|
||||
|
||||
msgid "Blacklist module"
|
||||
msgstr "Модуль блэклиста"
|
||||
|
||||
msgid "Blacklist settings"
|
||||
msgstr "Настройки блэклиста"
|
||||
|
||||
msgid "Blacklist source"
|
||||
msgstr "Источник блэклиста"
|
||||
|
||||
msgid "Blacklist update mode"
|
||||
msgstr "Режим обновления блэклиста"
|
||||
|
||||
msgid "Blacklist updates"
|
||||
msgstr "Обновления блэклиста"
|
||||
|
||||
msgid "Bytes"
|
||||
msgstr "Байты"
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr "Отмена"
|
||||
|
||||
msgid "Changes have been saved."
|
||||
msgstr "Изменения сохранены."
|
||||
|
||||
msgid "Clean up ipsets before updating blacklist"
|
||||
msgstr "Очищать ipset'ы перед обновлением блэклиста"
|
||||
|
||||
msgid ""
|
||||
"Complete service shutdown, as well as deleting ipsets and blacklist data"
|
||||
msgstr ""
|
||||
"Полное выключение службы, а также удаление ipset'ов и данных блэклиста"
|
||||
|
||||
msgid "Contents have been saved."
|
||||
msgstr "Содержимое сохранено."
|
||||
|
||||
msgid "Convert cyrillic domains to punycode"
|
||||
msgstr "Конвертировать кириллические домены в punycode"
|
||||
|
||||
msgid "Current schedule"
|
||||
msgstr "Текущее расписание"
|
||||
|
||||
msgid "DNS server that is used for FQDN entries"
|
||||
msgstr "DNS сервер для FQDN записей"
|
||||
|
||||
msgid "Day"
|
||||
msgstr "День"
|
||||
|
||||
msgid "Disable"
|
||||
msgstr "Отключить"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Отключено"
|
||||
|
||||
msgid "Dismiss"
|
||||
msgstr "Закрыть"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Изменить"
|
||||
|
||||
msgid "Enable"
|
||||
msgstr "Включить"
|
||||
|
||||
msgid "Enable FQDN filter"
|
||||
msgstr "Включить фильтр FQDN"
|
||||
|
||||
msgid "Enable IP filter"
|
||||
msgstr "Включить фильтр IP"
|
||||
|
||||
msgid "Enable the 'total-proxy' option at startup"
|
||||
msgstr "Включать 'total-proxy' при старте"
|
||||
|
||||
msgid "Enabled"
|
||||
msgstr "Включено"
|
||||
|
||||
msgid "Entries filters"
|
||||
msgstr "Фильтры записей"
|
||||
|
||||
msgid "Error"
|
||||
msgstr "Ошибка"
|
||||
|
||||
msgid "Exclude domains from blacklist by FQDN filter patterns"
|
||||
msgstr "Исключение доменов из блэклиста по шаблонам фильтра FQDN"
|
||||
|
||||
msgid "Exclude IP addresses from blacklist by IP filter patterns"
|
||||
msgstr "Исключение IP адресов из блэклиста по шаблонам фильтра IP"
|
||||
|
||||
msgid "Expecting:"
|
||||
msgstr "Ожидается:"
|
||||
|
||||
msgid "Filter messages with a regexp"
|
||||
msgstr "Фильтровать сообщения с помощью регулярного выражения"
|
||||
|
||||
msgid "FQDN configuration"
|
||||
msgstr "Конфигурация FQDN"
|
||||
|
||||
msgid "FQDN filter"
|
||||
msgstr "Фильтр FQDN"
|
||||
|
||||
msgid "Hour"
|
||||
msgstr "Час"
|
||||
|
||||
msgid "Interval"
|
||||
msgstr "Интервал"
|
||||
|
||||
msgid "IP configuration"
|
||||
msgstr "Конфигурация IP"
|
||||
|
||||
msgid "IP filter"
|
||||
msgstr "Фильтр IP"
|
||||
|
||||
msgid "IP limit"
|
||||
msgstr "Лимит IP адресов"
|
||||
|
||||
msgid "IP subnet patterns (/24) that are excluded from optimization"
|
||||
msgstr "Шаблоны IP подсетей (/24) не подлежащих оптимизации"
|
||||
|
||||
msgid "Ipset"
|
||||
msgstr "Ipset"
|
||||
|
||||
msgid "Iptables rules"
|
||||
msgstr "Правила iptables"
|
||||
|
||||
msgid "LAN interface"
|
||||
msgstr "LAN интерфейс"
|
||||
|
||||
msgid "Last blacklist update"
|
||||
msgstr "Последнее обновление блэклиста"
|
||||
|
||||
msgid "Loading"
|
||||
msgstr "Загрузка"
|
||||
|
||||
msgid "Log"
|
||||
msgstr "Лог"
|
||||
|
||||
msgid "Logging events"
|
||||
msgstr "Записывать события в лог"
|
||||
|
||||
msgid "Main settings"
|
||||
msgstr "Основные настройки"
|
||||
|
||||
msgid "Match-set"
|
||||
msgstr "Правило"
|
||||
|
||||
msgid "Message filter"
|
||||
msgstr "Фильтр сообщений"
|
||||
|
||||
msgid "Minute"
|
||||
msgstr "Минута"
|
||||
|
||||
msgid "Module operation mode"
|
||||
msgstr "Режим работы модуля"
|
||||
|
||||
msgid "Name"
|
||||
msgstr "Имя"
|
||||
|
||||
msgid "No changes to save."
|
||||
msgstr "Нет изменений для сохранения."
|
||||
|
||||
msgid "No data"
|
||||
msgstr "Нет данных"
|
||||
|
||||
msgid "No matches..."
|
||||
msgstr "Нет совпадений..."
|
||||
|
||||
msgid "No Shedule"
|
||||
msgstr "Нет расписания"
|
||||
|
||||
msgid "Number of entries"
|
||||
msgstr "Кол-во записей"
|
||||
|
||||
msgid ""
|
||||
"One entry (IP, CIDR or FQDN) per line. In the FQDN records, you can specify "
|
||||
"the DNS server for resolving this domain (separated by a space). You can "
|
||||
"also comment on lines (<code>#</code> is the first character of a line).<br /"
|
||||
">Examples:"
|
||||
msgstr ""
|
||||
"Одна запись (IP, CIDR или FQDN) на строку. В записях FQDN можно задать DNS-"
|
||||
"сервер для разрешения данного домена (через пробел). Также можно "
|
||||
"комментировать строки (<code>#</code> - первый символ строки).<br />Примеры:"
|
||||
|
||||
msgid "One of the following:"
|
||||
msgstr "Одно из следующих значений:"
|
||||
|
||||
msgid "Only messages that include the specified string will be displayed"
|
||||
msgstr "Будут показаны только сообщения включающие указанную строку"
|
||||
|
||||
msgid "Optional DNS resolver for '.onion' zone"
|
||||
msgstr "Дополнительный DNS резолвер для '.onion' зоны"
|
||||
|
||||
msgid "Optional DNS resolver"
|
||||
msgstr "Альтернативный DNS резолвер"
|
||||
|
||||
msgid "Options"
|
||||
msgstr "Опции"
|
||||
|
||||
msgid ""
|
||||
"Patterns can be strings or regular expressions. Each pattern in a separate "
|
||||
"line, the symbol <code>#</code> in the first position of the line - comments "
|
||||
"on the line.<br />Examples (dot is a special character):"
|
||||
msgstr ""
|
||||
"Шаблоны могут быть строками или регулярными выражениями. Каждый шаблон в "
|
||||
"отдельной строке, символ <code>#</code> в первой позиции строки - "
|
||||
"комментирует строку.<br />Примеры (точка - спецсимвол):"
|
||||
|
||||
msgid ""
|
||||
"Patterns can be strings or regular expressions. Each pattern in a separate "
|
||||
"line, the symbol <code>#</code> in the first position of the line - comments "
|
||||
"on the line.<br />Examples:"
|
||||
msgstr ""
|
||||
"Шаблоны могут быть строками или регулярными выражениями. Каждый шаблон в "
|
||||
"отдельной строке, символ <code>#</code> в первой позиции строки - "
|
||||
"комментирует строку.<br />Примеры:"
|
||||
|
||||
msgid "Proxy mode"
|
||||
msgstr "Режим прокси"
|
||||
|
||||
msgid "Reduces RAM consumption during update"
|
||||
msgstr "Уменьшает потребление опреративной памяти при обновлении"
|
||||
|
||||
msgid "Reset"
|
||||
msgstr "Сбросить"
|
||||
|
||||
msgid "Run at startup"
|
||||
msgstr "Запуск при старте системы"
|
||||
|
||||
msgid "Service"
|
||||
msgstr "Служба"
|
||||
|
||||
msgid "Set"
|
||||
msgstr "Установить"
|
||||
|
||||
msgid "Settings"
|
||||
msgstr "Настройки"
|
||||
|
||||
msgid "Show only the last messages"
|
||||
msgstr "Показать только последние сообщения"
|
||||
|
||||
msgid "Shutdown"
|
||||
msgstr "Выключение"
|
||||
|
||||
msgid "Size in memory"
|
||||
msgstr "Размер в памяти"
|
||||
|
||||
msgid "Starting"
|
||||
msgstr "Запускается"
|
||||
|
||||
msgid "Statistics"
|
||||
msgstr "Статистика"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "Статус"
|
||||
|
||||
msgid "Subdomains limit"
|
||||
msgstr "Лимит субдоменов"
|
||||
|
||||
msgid "Summarize '/24' networks"
|
||||
msgstr "Суммировать '/24' подсети"
|
||||
|
||||
msgid "Summarize IP ranges"
|
||||
msgstr "Суммировать IP диапазоны"
|
||||
|
||||
msgid ""
|
||||
"The number of IP addresses in the subnet, upon reaching which the entire "
|
||||
"'/24' subnet is added to the list"
|
||||
msgstr ""
|
||||
"Количество IP адресов подсети, при достижении которого в список добавляется "
|
||||
"вся '/24' подсеть"
|
||||
|
||||
msgid ""
|
||||
"The number of subdomains in the domain, upon reaching which the entire 2nd "
|
||||
"level domain is added to the list"
|
||||
msgstr ""
|
||||
"Количество субдоменов при достижении которого в список добавляется весь "
|
||||
"домен 2-го уровня"
|
||||
|
||||
msgid ""
|
||||
"The service will be disabled and all blacklist data will be deleted. "
|
||||
"Continue?"
|
||||
msgstr ""
|
||||
"Служба будет выключена и все данные блэклиста будут удалены. Продолжить?"
|
||||
|
||||
msgid "Time"
|
||||
msgstr "Время"
|
||||
|
||||
msgid "Tor configuration file"
|
||||
msgstr "Конфигурационный файл Tor"
|
||||
|
||||
msgid "Tor mode"
|
||||
msgstr "Режим Tor"
|
||||
|
||||
msgid "Total-proxy is on"
|
||||
msgstr "Total-proxy включен"
|
||||
|
||||
msgid "Transparent proxy port for iptables rules"
|
||||
msgstr "Порт прозрачного прокси для правил iptables"
|
||||
|
||||
msgid "Unable to execute or read contents"
|
||||
msgstr "Невозможно выполнить или прочитать содержимое"
|
||||
|
||||
msgid "Unable to read the contents"
|
||||
msgstr "Невозможно прочитать содержимое"
|
||||
|
||||
msgid "Unable to save the changes"
|
||||
msgstr "Невозможно сохранить изменения"
|
||||
|
||||
msgid "Unable to save the contents"
|
||||
msgstr "Невозможно сохранить содержимое"
|
||||
|
||||
msgid "Update"
|
||||
msgstr "Обновить"
|
||||
|
||||
msgid "Update blacklist"
|
||||
msgstr "Обновить блэклист"
|
||||
|
||||
msgid "Updating"
|
||||
msgstr "Обновление"
|
||||
|
||||
msgid "Use optional DNS resolver"
|
||||
msgstr "Использовать альтернативный DNS резолвер"
|
||||
|
||||
msgid "User entries"
|
||||
msgstr "Записи пользователя"
|
||||
|
||||
msgid "VPN interface"
|
||||
msgstr "VPN интерфейс"
|
||||
|
||||
msgid "VPN mode"
|
||||
msgstr "Режим VPN"
|
||||
|
||||
msgid "VPN routing error! Need restart"
|
||||
msgstr "Ошибка маршрутизации VPN! Необходим перезапуск"
|
||||
|
||||
msgid "ex:"
|
||||
msgstr "прим:"
|
||||
|
||||
msgid "user entries only"
|
||||
msgstr "только записи пользователя"
|
||||
@@ -0,0 +1,343 @@
|
||||
msgid ""
|
||||
msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
|
||||
msgid "valid IP address"
|
||||
msgstr ""
|
||||
|
||||
msgid "valid address#port"
|
||||
msgstr ""
|
||||
|
||||
msgid "2nd level domains that are excluded from optimization"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add user entries to the blacklist when updating"
|
||||
msgstr ""
|
||||
|
||||
msgid "All traffic goes through the proxy without applying rules"
|
||||
msgstr ""
|
||||
|
||||
msgid "Apply"
|
||||
msgstr ""
|
||||
|
||||
msgid "Apply proxy rules to router application traffic"
|
||||
msgstr ""
|
||||
|
||||
msgid "Blacklist module"
|
||||
msgstr ""
|
||||
|
||||
msgid "Blacklist settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Blacklist source"
|
||||
msgstr ""
|
||||
|
||||
msgid "Blacklist update mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Blacklist updates"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bytes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cancel"
|
||||
msgstr ""
|
||||
|
||||
msgid "Changes have been saved."
|
||||
msgstr ""
|
||||
|
||||
msgid "Clean up ipsets before updating blacklist"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Complete service shutdown, as well as deleting ipsets and blacklist data"
|
||||
msgstr ""
|
||||
|
||||
msgid "Contents have been saved."
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert cyrillic domains to punycode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Current schedule"
|
||||
msgstr ""
|
||||
|
||||
msgid "DNS server that is used for FQDN entries"
|
||||
msgstr ""
|
||||
|
||||
msgid "Day"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dismiss"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable FQDN filter"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable ip filter"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable the 'total-proxy' option at startup"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "Entries filters"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
msgid "Exclude domains from blacklist by FQDN filter patterns"
|
||||
msgstr ""
|
||||
|
||||
msgid "Exclude IP addresses from blacklist by IP filter patterns"
|
||||
msgstr ""
|
||||
|
||||
msgid "Expecting:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Filter messages with a regexp"
|
||||
msgstr ""
|
||||
|
||||
msgid "FQDN configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "FQDN filter"
|
||||
msgstr ""
|
||||
|
||||
msgid "Hour"
|
||||
msgstr ""
|
||||
|
||||
msgid "Interval"
|
||||
msgstr ""
|
||||
|
||||
msgid "IP configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "IP filter"
|
||||
msgstr ""
|
||||
|
||||
msgid "IP limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "IP subnet patterns (/24) that are excluded from optimization"
|
||||
msgstr ""
|
||||
|
||||
msgid "Ipset"
|
||||
msgstr ""
|
||||
|
||||
msgid "Iptables rules"
|
||||
msgstr ""
|
||||
|
||||
msgid "LAN interface"
|
||||
msgstr ""
|
||||
|
||||
msgid "Last blacklist update"
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading"
|
||||
msgstr ""
|
||||
|
||||
msgid "Log"
|
||||
msgstr ""
|
||||
|
||||
msgid "Logging events"
|
||||
msgstr ""
|
||||
|
||||
msgid "Main settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Match-set"
|
||||
msgstr ""
|
||||
|
||||
msgid "Message filter"
|
||||
msgstr ""
|
||||
|
||||
msgid "Minute"
|
||||
msgstr ""
|
||||
|
||||
msgid "Module operation mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
|
||||
msgid "No changes to save."
|
||||
msgstr ""
|
||||
|
||||
msgid "No data"
|
||||
msgstr ""
|
||||
|
||||
msgid "No matches..."
|
||||
msgstr ""
|
||||
|
||||
msgid "No Shedule"
|
||||
msgstr ""
|
||||
|
||||
msgid "Number of entries"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"One entry (IP, CIDR or FQDN) per line. In the FQDN records, you can specify "
|
||||
"the DNS server for resolving this domain (separated by a space). You can "
|
||||
"also comment on lines (<code>#</code> is the first character of a line).<br /"
|
||||
">Examples:"
|
||||
msgstr ""
|
||||
|
||||
msgid "One of the following:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Optional DNS resolver for '.onion' zone"
|
||||
msgstr ""
|
||||
|
||||
msgid "Optional DNS resolver"
|
||||
msgstr ""
|
||||
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Patterns can be strings or regular expressions. Each pattern in a separate "
|
||||
"line, the symbol <code>#</code> in the first position of the line - comments "
|
||||
"on the line.<br />Examples (dot is a special character):"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Patterns can be strings or regular expressions. Each pattern in a separate "
|
||||
"line, the symbol <code>#</code> in the first position of the line - comments "
|
||||
"on the line.<br />Examples:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Proxy mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reduces RAM consumption during update"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reset"
|
||||
msgstr ""
|
||||
|
||||
msgid "Run at startup"
|
||||
msgstr ""
|
||||
|
||||
msgid "Service"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set"
|
||||
msgstr ""
|
||||
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show only the last messages"
|
||||
msgstr ""
|
||||
|
||||
msgid "Shutdown"
|
||||
msgstr ""
|
||||
|
||||
msgid "Size in memory"
|
||||
msgstr ""
|
||||
|
||||
msgid "Starting"
|
||||
msgstr ""
|
||||
|
||||
msgid "Statistics"
|
||||
msgstr ""
|
||||
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Subdomains limit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Summarize '/24' networks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Summarize IP ranges"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The number of IP addresses in the subnet, upon reaching which the entire "
|
||||
"'/24' subnet is added to the list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The number of subdomains in the domain, upon reaching which the entire 2nd "
|
||||
"level domain is added to the list"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The service will be disabled and all blacklist data will be deleted. "
|
||||
"Continue?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Time"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tor configuration file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tor mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "Total-proxy is on"
|
||||
msgstr ""
|
||||
|
||||
msgid "Transparent proxy port for iptables rules"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to execute or read contents"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to read the contents"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to save the changes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Unable to save the contents"
|
||||
msgstr ""
|
||||
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
|
||||
msgid "Update blacklist"
|
||||
msgstr ""
|
||||
|
||||
msgid "Updating"
|
||||
msgstr ""
|
||||
|
||||
msgid "Use optional DNS resolver"
|
||||
msgstr ""
|
||||
|
||||
msgid "User entries"
|
||||
msgstr ""
|
||||
|
||||
msgid "VPN interface"
|
||||
msgstr ""
|
||||
|
||||
msgid "VPN mode"
|
||||
msgstr ""
|
||||
|
||||
msgid "VPN routing error! Need restart"
|
||||
msgstr ""
|
||||
|
||||
msgid "ex:"
|
||||
msgstr ""
|
||||
|
||||
msgid "user entries only"
|
||||
msgstr ""
|
||||
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"admin/services/ruantiblock": {
|
||||
"title": "Ruantiblock",
|
||||
"order": 60,
|
||||
"action": {
|
||||
"type": "alias",
|
||||
"path": "admin/services/ruantiblock/service"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [ "luci-app-ruantiblock" ],
|
||||
"fs": {
|
||||
"/usr/bin/ruantiblock": "executable",
|
||||
"/etc/init.d/ruantiblock": "executable"
|
||||
},
|
||||
"uci": { "ruantiblock": true }
|
||||
}
|
||||
},
|
||||
|
||||
"admin/services/ruantiblock/service": {
|
||||
"title": "Service",
|
||||
"order": 10,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ruantiblock/service"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/services/ruantiblock/settings": {
|
||||
"title": "Settings",
|
||||
"order": 20,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ruantiblock/settings"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/services/ruantiblock/cron": {
|
||||
"title": "Blacklist updates",
|
||||
"order": 30,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ruantiblock/cron"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/services/ruantiblock/info": {
|
||||
"title": "Statistics",
|
||||
"order": 40,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ruantiblock/info"
|
||||
}
|
||||
},
|
||||
|
||||
"admin/services/ruantiblock/log": {
|
||||
"title": "Log",
|
||||
"order": 50,
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "ruantiblock/log"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"luci-app-ruantiblock": {
|
||||
"description": "Grant access to ruantiblock procedures",
|
||||
"read": {
|
||||
"cgi-io": [ "exec" ],
|
||||
"file": {
|
||||
"/usr/bin": [ "list" ],
|
||||
"/etc/ruantiblock/fqdn_filter": [ "read" ],
|
||||
"/etc/ruantiblock/ip_filter": [ "read" ],
|
||||
"/etc/ruantiblock/user_entries": [ "read" ],
|
||||
"/var/run/ruantiblock.token": [ "read" ],
|
||||
"/etc/tor/torrc": [ "read" ],
|
||||
"/etc/crontabs/root": [ "read" ],
|
||||
"/usr/bin/ruantiblock": [ "exec" ],
|
||||
"/etc/init.d/ruantiblock": [ "exec" ],
|
||||
"/etc/init.d/tor enabled": [ "exec" ],
|
||||
"/etc/init.d/tor restart": [ "exec" ],
|
||||
"/etc/init.d/cron enabled": [ "exec" ],
|
||||
"/etc/init.d/cron enable": [ "exec" ],
|
||||
"/etc/init.d/cron restart": [ "exec" ],
|
||||
"/sbin/logread -e ruantiblock": [ "exec" ],
|
||||
"/usr/sbin/logread -e ruantiblock": [ "exec" ]
|
||||
},
|
||||
"uci": [ "network", "ruantiblock" ]
|
||||
},
|
||||
"write": {
|
||||
"file": {
|
||||
"/etc/ruantiblock/fqdn_filter": [ "write" ],
|
||||
"/etc/ruantiblock/ip_filter": [ "write" ],
|
||||
"/etc/ruantiblock/user_entries": [ "write" ],
|
||||
"/etc/tor/torrc": [ "write" ],
|
||||
"/etc/crontabs/root": [ "write" ]
|
||||
},
|
||||
"uci": [ "ruantiblock" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
+591
@@ -0,0 +1,591 @@
|
||||
#!/usr/bin/env lua
|
||||
|
||||
--[[
|
||||
(с) 2020 gSpot (https://github.com/gSpotx2f/ruantiblock_openwrt)
|
||||
|
||||
lua == 5.1
|
||||
--]]
|
||||
|
||||
-------------------------- Class constructor -------------------------
|
||||
|
||||
local function Class(super, t)
|
||||
local class = t or {}
|
||||
local function instance_constructor(cls, t)
|
||||
local instance = t or {}
|
||||
setmetatable(instance, cls)
|
||||
instance.__class = cls
|
||||
return instance
|
||||
end
|
||||
if not super then
|
||||
local mt = {__call = instance_constructor}
|
||||
mt.__index = mt
|
||||
setmetatable(class, mt)
|
||||
elseif type(super) == "table" and super.__index and super.__call then
|
||||
setmetatable(class, super)
|
||||
class.__super = super
|
||||
else
|
||||
error("Argument error! Incorrect object of a 'super'")
|
||||
end
|
||||
class.__index = class
|
||||
class.__call = instance_constructor
|
||||
return class
|
||||
end
|
||||
|
||||
------------------------------ Settings ------------------------------
|
||||
|
||||
local Config = Class(nil, {
|
||||
environ_table = {
|
||||
["EXEC_DIR"] = true,
|
||||
["BLLIST_SOURCE"] = true,
|
||||
["BLLIST_MODE"] = true,
|
||||
["ALT_NSLOOKUP"] = true,
|
||||
["ALT_DNS_ADDR"] = true,
|
||||
["USE_IDN"] = true,
|
||||
["OPT_EXCLUDE_SLD"] = true,
|
||||
["OPT_EXCLUDE_MASKS"] = true,
|
||||
["FQDN_FILTER"] = true,
|
||||
["FQDN_FILTER_FILE"] = true,
|
||||
["IP_FILTER"] = true,
|
||||
["IP_FILTER_FILE"] = true,
|
||||
["SD_LIMIT"] = true,
|
||||
["IP_LIMIT"] = true,
|
||||
["OPT_EXCLUDE_NETS"] = true,
|
||||
["BLLIST_MIN_ENTRS"] = true,
|
||||
["STRIP_WWW"] = true,
|
||||
["DATA_DIR"] = true,
|
||||
["IPSET_DNSMASQ"] = true,
|
||||
["IPSET_IP_TMP"] = true,
|
||||
["IPSET_CIDR_TMP"] = true,
|
||||
["DNSMASQ_DATA_FILE"] = true,
|
||||
["IP_DATA_FILE"] = true,
|
||||
["UPDATE_STATUS_FILE"] = true,
|
||||
["RBL_ALL_URL"] = true,
|
||||
["RBL_IP_URL"] = true,
|
||||
["ZI_ALL_URL"] = true,
|
||||
["AF_IP_URL"] = true,
|
||||
["AF_FQDN_URL"] = true,
|
||||
["AZ_ENCODING"] = true,
|
||||
["RBL_ENCODING"] = true,
|
||||
["ZI_ENCODING"] = true,
|
||||
["AF_ENCODING"] = true,
|
||||
["SUMMARIZE_IP"] = true,
|
||||
["SUMMARIZE_CIDR"] = true,
|
||||
},
|
||||
FQDN_FILTER_PATTERNS = {},
|
||||
IP_FILTER_PATTERNS = {},
|
||||
-- iconv type: standalone iconv or lua-iconv (standalone, lua)
|
||||
ICONV_TYPE = "standalone",
|
||||
-- standalone iconv
|
||||
ICONV_CMD = "iconv",
|
||||
WGET_CMD = "wget --no-check-certificate -q -O -",
|
||||
encoding = "UTF-8",
|
||||
site_encoding = "",
|
||||
http_send_headers = {
|
||||
--["User-Agent"] = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0",
|
||||
},
|
||||
})
|
||||
Config.wget_user_agent = (Config.http_send_headers["User-Agent"]) and ' -U "' .. Config.http_send_headers["User-Agent"] .. '"' or ''
|
||||
|
||||
-- Load external config
|
||||
|
||||
function Config:load_config(t)
|
||||
local config_arrays = {
|
||||
["OPT_EXCLUDE_SLD"] = true,
|
||||
["OPT_EXCLUDE_NETS"] = true,
|
||||
}
|
||||
for k, v in pairs(t) do
|
||||
if config_arrays[k] then
|
||||
local value_table = {}
|
||||
for v in v:gmatch('[^" ]+') do
|
||||
value_table[v] = true
|
||||
end
|
||||
self[k] = value_table
|
||||
else
|
||||
self[k] = v:match("^[0-9.]+$") and tonumber(v) or v:gsub('"', '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Config:load_environ_config()
|
||||
local cfg_table = {}
|
||||
for var in pairs(self.environ_table) do
|
||||
val = os.getenv(var)
|
||||
if val then
|
||||
cfg_table[var] = val
|
||||
end
|
||||
end
|
||||
self:load_config(cfg_table)
|
||||
end
|
||||
|
||||
Config:load_environ_config()
|
||||
|
||||
local function remap_bool(val)
|
||||
return (val ~= 0 and val ~= false and val ~= nil) and true or false
|
||||
end
|
||||
|
||||
Config.ALT_NSLOOKUP = remap_bool(Config.ALT_NSLOOKUP)
|
||||
Config.USE_IDN = remap_bool(Config.USE_IDN)
|
||||
Config.STRIP_WWW = remap_bool(Config.STRIP_WWW)
|
||||
Config.FQDN_FILTER = remap_bool(Config.FQDN_FILTER)
|
||||
Config.IP_FILTER = remap_bool(Config.IP_FILTER)
|
||||
Config.SUMMARIZE_IP = remap_bool(Config.SUMMARIZE_IP)
|
||||
Config.SUMMARIZE_CIDR = remap_bool(Config.SUMMARIZE_CIDR)
|
||||
|
||||
-- Load filters
|
||||
|
||||
function Config:load_filter_files()
|
||||
function load_file(file, t)
|
||||
local file_handler = io.open(file, "r")
|
||||
if file_handler then
|
||||
for line in file_handler:lines() do
|
||||
if #line > 0 and line:match("^[^#]") then
|
||||
t[line] = true
|
||||
end
|
||||
end
|
||||
file_handler:close()
|
||||
end
|
||||
end
|
||||
if self.FQDN_FILTER then
|
||||
load_file(self.FQDN_FILTER_FILE, self.FQDN_FILTER_PATTERNS)
|
||||
end
|
||||
if self.IP_FILTER then
|
||||
load_file(self.IP_FILTER_FILE, self.IP_FILTER_PATTERNS)
|
||||
end
|
||||
end
|
||||
|
||||
Config:load_filter_files()
|
||||
|
||||
-- Import packages
|
||||
|
||||
local function prequire(package)
|
||||
local ret_val, pkg = pcall(require, package)
|
||||
return ret_val and pkg
|
||||
end
|
||||
|
||||
local http = prequire("socket.http")
|
||||
local https = prequire("ssl.https")
|
||||
local ltn12 = prequire("ltn12")
|
||||
if not ltn12 then
|
||||
error("You need to install luasocket or ltn12...")
|
||||
end
|
||||
|
||||
local idn = prequire("idn")
|
||||
if Config.USE_IDN and not idn then
|
||||
error("You need to install idn.lua (github.com/haste/lua-idn) or 'USE_IDN' must be set to '0'")
|
||||
end
|
||||
local iconv = prequire("iconv")
|
||||
|
||||
local si, it
|
||||
if prequire("bit") then
|
||||
it = prequire("iptool")
|
||||
if it then
|
||||
si = prequire("ruab_sum_ip")
|
||||
end
|
||||
end
|
||||
if not si then
|
||||
Config.SUMMARIZE_CIDR = false
|
||||
Config.SUMMARIZE_IP = false
|
||||
end
|
||||
|
||||
-- Check iconv
|
||||
|
||||
if Config.ICONV_TYPE == "standalone" then
|
||||
local handler = io.popen("which " .. Config.ICONV_CMD)
|
||||
local ret_val = handler:read("*l")
|
||||
handler:close()
|
||||
if not ret_val then
|
||||
Config.ICONV_CMD = nil
|
||||
end
|
||||
elseif Config.ICONV_TYPE == "lua" then
|
||||
else
|
||||
error("Config.ICONV_TYPE should be either 'lua' or 'standalone'")
|
||||
end
|
||||
|
||||
------------------------------ Classes -------------------------------
|
||||
|
||||
local BlackListParser = Class(Config, {
|
||||
ip_pattern = "%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?",
|
||||
cidr_pattern = "%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?/%d%d?",
|
||||
fqdn_pattern = "[a-z0-9_%.%-]-[a-z0-9_%-]+%.[a-z0-9%.%-]",
|
||||
url = "http://127.0.0.1",
|
||||
records_separator = "\n",
|
||||
ips_separator = " | ",
|
||||
})
|
||||
|
||||
function BlackListParser:new(t)
|
||||
-- extended instance constructor
|
||||
local instance = self(t)
|
||||
instance.url = instance["url"] or self.url
|
||||
instance.records_separator = instance["records_separator"] or self.records_separator
|
||||
instance.ips_separator = instance["ips_separator"] or self.ips_separator
|
||||
instance.site_encoding = instance["site_encoding"] or self.site_encoding
|
||||
instance.ip_records_count = 0
|
||||
instance.ip_count = 0
|
||||
instance.ip_subnet_table = {}
|
||||
instance.cidr_count = 0
|
||||
instance.fqdn_table = {}
|
||||
instance.fqdn_count = 0
|
||||
instance.sld_table = {}
|
||||
instance.fqdn_records_count = 0
|
||||
instance.ip_table = {}
|
||||
instance.cidr_table = {}
|
||||
instance.iconv_handler = iconv and iconv.open(instance.encoding, instance.site_encoding) or nil
|
||||
return instance
|
||||
end
|
||||
|
||||
function BlackListParser:convert_encoding(input)
|
||||
local output
|
||||
if self.ICONV_TYPE == "lua" and self.iconv_handler then
|
||||
output = self.iconv_handler:iconv(input)
|
||||
elseif self.ICONV_TYPE == "standalone" and self.ICONV_CMD then
|
||||
local iconv_handler = assert(io.popen('printf \'' .. input .. '\' | ' .. self.ICONV_CMD .. ' -f "' .. self.site_encoding .. '" -t "' .. self.encoding .. '"', 'r'))
|
||||
output = iconv_handler:read("*a")
|
||||
iconv_handler:close()
|
||||
end
|
||||
return (output)
|
||||
end
|
||||
|
||||
function BlackListParser:convert_to_punycode(input)
|
||||
if self.site_encoding and self.site_encoding ~= "" then
|
||||
input = self:convert_encoding(input)
|
||||
end
|
||||
return input and (idn.encode(input))
|
||||
end
|
||||
|
||||
function BlackListParser:check_filter(str, filter_patterns)
|
||||
if filter_patterns and str then
|
||||
for pattern in pairs(filter_patterns) do
|
||||
if str:match(pattern) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function BlackListParser:get_subnet(ip)
|
||||
return ip:match("^(%d+%.%d+%.%d+%.)%d+$")
|
||||
end
|
||||
|
||||
function BlackListParser:fill_ip_tables(val)
|
||||
if val and val ~= "" then
|
||||
for ip_entry in val:gmatch(self.ip_pattern .. "/?%d?%d?") do
|
||||
if not self.IP_FILTER or (self.IP_FILTER and not self:check_filter(ip_entry, self.IP_FILTER_PATTERNS)) then
|
||||
if ip_entry:match("^" .. self.ip_pattern .. "$") and not self.ip_table[ip_entry] then
|
||||
local subnet = self:get_subnet(ip_entry)
|
||||
if subnet and (self.OPT_EXCLUDE_NETS[subnet] or ((not self.IP_LIMIT or self.IP_LIMIT == 0) or (not self.ip_subnet_table[subnet] or self.ip_subnet_table[subnet] < self.IP_LIMIT))) then
|
||||
self.ip_table[ip_entry] = subnet
|
||||
self.ip_subnet_table[subnet] = (self.ip_subnet_table[subnet] or 0) + 1
|
||||
self.ip_count = self.ip_count + 1
|
||||
end
|
||||
elseif ip_entry:match("^" .. self.cidr_pattern .. "$") and not self.cidr_table[ip_entry] then
|
||||
self.cidr_table[ip_entry] = true
|
||||
self.cidr_count = self.cidr_count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function BlackListParser:get_sld(fqdn)
|
||||
return fqdn:match("^[a-z0-9_%.%-]-([a-z0-9_%-]+%.[a-z0-9%-]+)$")
|
||||
end
|
||||
|
||||
function BlackListParser:fill_domain_tables(val)
|
||||
val = val:gsub("%*%.", ""):gsub("%.$", ""):lower()
|
||||
if self.STRIP_WWW then
|
||||
val = val:gsub("^www[0-9]?%.", "")
|
||||
end
|
||||
if not self.FQDN_FILTER or (self.FQDN_FILTER and not self:check_filter(val, self.FQDN_FILTER_PATTERNS)) then
|
||||
if val:match("^" .. self.fqdn_pattern .. "+$") then
|
||||
elseif self.USE_IDN and val:match("^[^\\/&%?]-[^\\/&%?%.]+%.[^\\/&%?%.]+%.?$") then
|
||||
val = self:convert_to_punycode(val)
|
||||
if not val then
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
local sld = self:get_sld(val)
|
||||
if sld and (self.OPT_EXCLUDE_SLD[sld] or ((not self.SD_LIMIT or self.SD_LIMIT == 0) or (not self.sld_table[sld] or self.sld_table[sld] < self.SD_LIMIT))) then
|
||||
self.fqdn_table[val] = sld
|
||||
self.sld_table[sld] = (self.sld_table[sld] or 0) + 1
|
||||
self.fqdn_count = self.fqdn_count + 1
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function BlackListParser:sink()
|
||||
-- Must be reload in the subclass
|
||||
error("Method BlackListParser:sink() must be reload in the subclass!")
|
||||
end
|
||||
|
||||
function BlackListParser:optimize_ip_table()
|
||||
local optimized_table = {}
|
||||
for ipaddr, subnet in pairs(self.ip_table) do
|
||||
if self.ip_subnet_table[subnet] then
|
||||
if (self.IP_LIMIT and self.IP_LIMIT > 0 and not self.OPT_EXCLUDE_NETS[subnet]) and self.ip_subnet_table[subnet] >= self.IP_LIMIT then
|
||||
self.cidr_table[string.format("%s0/24", subnet)] = true
|
||||
self.ip_subnet_table[subnet] = nil
|
||||
self.cidr_count = self.cidr_count + 1
|
||||
else
|
||||
optimized_table[ipaddr] = true
|
||||
self.ip_records_count = self.ip_records_count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
self.ip_table = optimized_table
|
||||
end
|
||||
|
||||
|
||||
function BlackListParser:optimize_fqdn_table()
|
||||
local optimized_table = {}
|
||||
if self.OPT_EXCLUDE_MASKS and #self.OPT_EXCLUDE_MASKS > 0 then
|
||||
for sld in pairs(self.sld_table) do
|
||||
for _, pattern in ipairs(self.OPT_EXCLUDE_MASKS) do
|
||||
if sld:find(pattern) then
|
||||
self.sld_table[sld] = 0
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
for fqdn, sld in pairs(self.fqdn_table) do
|
||||
local key_value = fqdn
|
||||
if (not self.fqdn_table[sld] or fqdn == sld) and self.sld_table[sld] then
|
||||
if (self.SD_LIMIT and self.SD_LIMIT > 0 and not self.OPT_EXCLUDE_SLD[sld]) and self.sld_table[sld] >= self.SD_LIMIT then
|
||||
key_value = sld
|
||||
self.sld_table[sld] = nil
|
||||
end
|
||||
optimized_table[key_value] = true
|
||||
self.fqdn_records_count = self.fqdn_records_count + 1
|
||||
end
|
||||
end
|
||||
self.fqdn_table = optimized_table
|
||||
end
|
||||
|
||||
function BlackListParser:group_ip_ranges()
|
||||
for i in si.summarize_ip_ranges(self.ip_table, true) do
|
||||
self.cidr_table[string.format("%s/%s", it.int_to_ip(i[1]), i[2])] = true
|
||||
end
|
||||
end
|
||||
|
||||
function BlackListParser:group_cidr_ranges()
|
||||
for i in si.summarize_nets(self.cidr_table, true) do
|
||||
self.cidr_table[string.format("%s/%s", it.int_to_ip(i[1]), i[2])] = true
|
||||
end
|
||||
end
|
||||
|
||||
function BlackListParser:write_ipset_config()
|
||||
local file_handler = assert(io.open(self.IP_DATA_FILE, "w"), "Could not open ipset config")
|
||||
local i = 0
|
||||
for ipaddr in pairs(self.ip_table) do
|
||||
file_handler:write(string.format("add %s %s\n", self.IPSET_IP_TMP, ipaddr))
|
||||
i = i + 1
|
||||
end
|
||||
self.ip_records_count = i
|
||||
local c = 0
|
||||
for cidr in pairs(self.cidr_table) do
|
||||
file_handler:write(string.format("add %s %s\n", self.IPSET_CIDR_TMP, cidr))
|
||||
c = c + 1
|
||||
end
|
||||
self.cidr_count = c
|
||||
file_handler:close()
|
||||
end
|
||||
|
||||
function BlackListParser:write_dnsmasq_config()
|
||||
local file_handler = assert(io.open(self.DNSMASQ_DATA_FILE, "w"), "Could not open dnsmasq config")
|
||||
for fqdn in pairs(self.fqdn_table) do
|
||||
if self.ALT_NSLOOKUP then
|
||||
file_handler:write(string.format("server=/%s/%s\n", fqdn, self.ALT_DNS_ADDR))
|
||||
end
|
||||
file_handler:write(string.format("ipset=/%s/%s\n", fqdn, self.IPSET_DNSMASQ))
|
||||
end
|
||||
file_handler:close()
|
||||
end
|
||||
|
||||
function BlackListParser:write_update_status()
|
||||
local file_handler = assert(io.open(self.UPDATE_STATUS_FILE, "w"), "Could not open 'update_status' file")
|
||||
file_handler:write(string.format("%d %d %d", self.ip_records_count, self.cidr_count, self.fqdn_records_count))
|
||||
file_handler:close()
|
||||
end
|
||||
|
||||
function BlackListParser:chunk_buffer()
|
||||
local buff = ""
|
||||
local ret_value = ""
|
||||
local last_chunk
|
||||
return function(chunk)
|
||||
if last_chunk then
|
||||
return nil
|
||||
end
|
||||
if chunk then
|
||||
buff = buff .. chunk
|
||||
local last_rs_position = select(2, buff:find("^.*" .. self.records_separator))
|
||||
if last_rs_position then
|
||||
ret_value = buff:sub(1, last_rs_position)
|
||||
buff = buff:sub((last_rs_position + 1), -1)
|
||||
else
|
||||
ret_value = ""
|
||||
end
|
||||
else
|
||||
ret_value = buff
|
||||
last_chunk = true
|
||||
end
|
||||
return (ret_value)
|
||||
end
|
||||
end
|
||||
|
||||
function BlackListParser:get_http_data(url)
|
||||
local ret_val, ret_code, ret_headers
|
||||
local http_module = url:match("^https") and https or http
|
||||
if http_module then
|
||||
local http_sink = ltn12.sink.chain(self:chunk_buffer(), self:sink())
|
||||
ret_val, ret_code, ret_headers = http_module.request{url = url, sink = http_sink, headers = self.http_send_headers}
|
||||
if not ret_val or ret_code ~= 200 then
|
||||
ret_val = nil
|
||||
print(string.format("Connection error! (%s) URL: %s", ret_code, url))
|
||||
end
|
||||
end
|
||||
if not ret_val then
|
||||
local wget_sink = ltn12.sink.chain(self:chunk_buffer(), self:sink())
|
||||
ret_val = ltn12.pump.all(ltn12.source.file(io.popen(self.WGET_CMD .. self.wget_user_agent .. ' "' .. url .. '"', 'r')), wget_sink)
|
||||
end
|
||||
return (ret_val == 1) and true or false
|
||||
end
|
||||
|
||||
function BlackListParser:run()
|
||||
local return_code = 0
|
||||
if self:get_http_data(self.url) then
|
||||
if (self.fqdn_count + self.ip_count + self.cidr_count) > self.BLLIST_MIN_ENTRS then
|
||||
self:optimize_fqdn_table()
|
||||
self:optimize_ip_table()
|
||||
if self.SUMMARIZE_IP then
|
||||
self:group_ip_ranges()
|
||||
end
|
||||
if self.SUMMARIZE_CIDR then
|
||||
self:group_cidr_ranges()
|
||||
end
|
||||
self:write_ipset_config()
|
||||
self:write_dnsmasq_config()
|
||||
return_code = 0
|
||||
else
|
||||
return_code = 2
|
||||
end
|
||||
else
|
||||
return_code = 1
|
||||
end
|
||||
self:write_update_status()
|
||||
return return_code
|
||||
end
|
||||
|
||||
-- Subclasses
|
||||
|
||||
local function ip_sink(self)
|
||||
return function(chunk)
|
||||
if chunk and chunk ~= "" then
|
||||
for ip_string in chunk:gmatch(self.ip_string_pattern) do
|
||||
self:fill_ip_tables(ip_string)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function fqdn_sink_func(self, ip_str, fqdn_str)
|
||||
if #fqdn_str > 0 and not fqdn_str:match("^" .. self.ip_pattern .. "$") then
|
||||
if self:fill_domain_tables(fqdn_str) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
self:fill_ip_tables(ip_str)
|
||||
end
|
||||
|
||||
-- rublacklist.net
|
||||
|
||||
local Rbl = Class(BlackListParser, {
|
||||
url = Config.RBL_ALL_URL,
|
||||
ips_separator = ", ",
|
||||
ip_string_pattern = "([a-f0-9/.:]+),?\n?",
|
||||
})
|
||||
|
||||
function Rbl:sink()
|
||||
return function(chunk)
|
||||
if chunk and chunk ~= "" then
|
||||
for ip_str, fqdn_str in chunk:gmatch("%[([a-f0-9/.:', ]+)%],([^,]-),.-" .. self.records_separator) do
|
||||
fqdn_sink_func(self, ip_str, fqdn_str)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local RblIp = Class(Rbl, {
|
||||
url = Config.RBL_IP_URL,
|
||||
sink = ip_sink,
|
||||
})
|
||||
|
||||
-- zapret-info
|
||||
|
||||
local Zi = Class(BlackListParser, {
|
||||
url = Config.ZI_ALL_URL,
|
||||
ip_string_pattern = "([a-f0-9%.:/ |]+);.-\n",
|
||||
site_encoding = Config.ZI_ENCODING,
|
||||
})
|
||||
|
||||
function Zi:sink()
|
||||
return function(chunk)
|
||||
if chunk and chunk ~= "" then
|
||||
for ip_str, fqdn_str in chunk:gmatch("([^;]-);([^;]-);.-" .. self.records_separator) do
|
||||
fqdn_sink_func(self, ip_str, fqdn_str)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local ZiIp = Class(Zi, {
|
||||
sink = ip_sink,
|
||||
})
|
||||
|
||||
-- antifilter
|
||||
|
||||
local Af = Class(BlackListParser, {
|
||||
url = Config.AF_FQDN_URL,
|
||||
ip_string_pattern = "(.-)\n",
|
||||
})
|
||||
|
||||
function Af:sink()
|
||||
local entry_pattern = "((.-))" .. self.records_separator
|
||||
return function(chunk)
|
||||
if chunk and chunk ~= "" then
|
||||
for fqdn_str, ip_str in chunk:gmatch(entry_pattern) do
|
||||
fqdn_sink_func(self, ip_str, fqdn_str)
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local AfIp = Class(Af, {
|
||||
url = Config.AF_IP_URL,
|
||||
sink = ip_sink,
|
||||
BLLIST_MIN_ENTRS = 100,
|
||||
})
|
||||
|
||||
----------------------------- Main section ------------------------------
|
||||
|
||||
local ctx_table = {
|
||||
["ip"] = {["rublacklist"] = RblIp, ["zapret-info"] = ZiIp, ["antifilter"] = AfIp},
|
||||
["fqdn"] = {["rublacklist"] = Rbl, ["zapret-info"] = Zi, ["antifilter"] = Af},
|
||||
}
|
||||
|
||||
local return_code = 1
|
||||
local ctx = ctx_table[Config.BLLIST_MODE] and ctx_table[Config.BLLIST_MODE][Config.BLLIST_SOURCE]
|
||||
if ctx then
|
||||
return_code = ctx:new():run()
|
||||
else
|
||||
error("Wrong configuration! (Config.BLLIST_MODE or Config.BLLIST_SOURCE)")
|
||||
end
|
||||
|
||||
os.exit(return_code)
|
||||
@@ -0,0 +1,207 @@
|
||||
--[[
|
||||
(с) 2020 gSpot (https://github.com/gSpotx2f/ruantiblock_openwrt)
|
||||
--]]
|
||||
|
||||
local it = require("iptool")
|
||||
|
||||
HOSTS_LIMIT = 0
|
||||
NETS_LIMIT = 0
|
||||
|
||||
local function sort_ip_list(t)
|
||||
local t2 = {}
|
||||
for k in pairs(t) do
|
||||
t2[#t2 + 1] = k
|
||||
end
|
||||
table.sort(t2, function(a, b) return it.ip_to_int(a) < it.ip_to_int(b) end)
|
||||
return t2
|
||||
end
|
||||
|
||||
local function group_ip_ranges(ip_list, raw_list)
|
||||
local function remove_items(start, stop)
|
||||
for i = start, stop do
|
||||
if raw_list[i] then
|
||||
raw_list[i] = nil
|
||||
return
|
||||
end
|
||||
local item = it.int_to_ip(i)
|
||||
if raw_list[item] then
|
||||
raw_list[it.int_to_ip(i)] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local start, stop, last_call
|
||||
local hosts = 1
|
||||
local i = 0
|
||||
return function()
|
||||
local ret_val
|
||||
local ip
|
||||
repeat
|
||||
i = i + 1
|
||||
ip = ip_list[i]
|
||||
if ip then
|
||||
local ip_dec = it.ip_to_int(ip)
|
||||
if stop and (stop + 1) == ip_dec then
|
||||
hosts = hosts + 1
|
||||
else
|
||||
if hosts > 1 and hosts >= HOSTS_LIMIT then
|
||||
if raw_list then
|
||||
remove_items(start, stop)
|
||||
end
|
||||
ret_val = {[1] = start, [2] = stop}
|
||||
start = ip_dec
|
||||
stop = ip_dec
|
||||
hosts = 1
|
||||
break
|
||||
end
|
||||
start = ip_dec
|
||||
end
|
||||
stop = ip_dec
|
||||
elseif not last_call then
|
||||
if hosts > 1 and hosts >= HOSTS_LIMIT then
|
||||
if raw_list then
|
||||
remove_items(start, stop)
|
||||
end
|
||||
ret_val = {[1] = start, [2] = stop}
|
||||
last_call = true
|
||||
break
|
||||
end
|
||||
end
|
||||
until not ip
|
||||
return ret_val
|
||||
end
|
||||
end
|
||||
|
||||
local function sort_net_list(t)
|
||||
local t2 = {}
|
||||
for k, v in pairs(t) do
|
||||
local ip, pref = it.get_network_addr(k)
|
||||
t2[#t2 + 1] = {[1] = ip, [2] = pref}
|
||||
end
|
||||
table.sort(t2, function(a, b) return a[1] < b[1] end)
|
||||
return t2
|
||||
end
|
||||
|
||||
local function group_nets(cidr_list, raw_list)
|
||||
local function remove_items(start, stop)
|
||||
for i = start, stop, 256 do
|
||||
local item = it.int_to_ip(i) .. "/24"
|
||||
if raw_list[item] then
|
||||
raw_list[item] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local start, stop, last_call, curr_supernet
|
||||
local nets = 1
|
||||
local i = 0
|
||||
return function()
|
||||
local ret_val
|
||||
local cidr
|
||||
repeat
|
||||
i = i + 1
|
||||
cidr = cidr_list[i]
|
||||
if cidr then
|
||||
local network_address, prefixlen
|
||||
if type(cidr) == "string" then
|
||||
network_address, prefixlen = it.get_network_addr(cidr)
|
||||
elseif type(cidr) == "table" then
|
||||
network_address, prefixlen = cidr[1], cidr[2]
|
||||
end
|
||||
if prefixlen == 24 then
|
||||
local supernet = it.get_supernet({[1] = network_address, [2] = prefixlen}, 16)
|
||||
if stop and supernet == curr_supernet and (stop + 256) == network_address then
|
||||
nets = nets + 1
|
||||
else
|
||||
if nets > 1 and nets >= NETS_LIMIT then
|
||||
if raw_list then
|
||||
remove_items(start, stop)
|
||||
end
|
||||
ret_val = {[1] = start, [2] = stop + 255}
|
||||
start = network_address
|
||||
stop = network_address
|
||||
nets = 1
|
||||
curr_supernet = supernet
|
||||
break
|
||||
end
|
||||
start = network_address
|
||||
curr_supernet = supernet
|
||||
end
|
||||
stop = network_address
|
||||
end
|
||||
elseif not last_call then
|
||||
if nets > 1 and nets >= NETS_LIMIT then
|
||||
if raw_list then
|
||||
remove_items(start, stop)
|
||||
end
|
||||
ret_val = {[1] = start, [2] = stop + 255}
|
||||
last_call = true
|
||||
break
|
||||
end
|
||||
end
|
||||
until not cidr
|
||||
return ret_val
|
||||
end
|
||||
end
|
||||
|
||||
local function summarize_ranges(ip_iter)
|
||||
local s_range_iter
|
||||
return function()
|
||||
-- lua >= 5.2
|
||||
--::check_prefix::
|
||||
if s_range_iter then
|
||||
repeat
|
||||
local ip_t = s_range_iter()
|
||||
if ip_t then
|
||||
return ip_t
|
||||
end
|
||||
until not ip_t
|
||||
end
|
||||
local ip_range = ip_iter()
|
||||
if ip_range then
|
||||
s_range_iter = it.summarize_address_range(ip_range[1], ip_range[2])
|
||||
if s_range_iter then
|
||||
-- lua >= 5.2
|
||||
--goto check_prefix
|
||||
-- lua < 5.2
|
||||
repeat
|
||||
local ip_t = s_range_iter()
|
||||
if ip_t then
|
||||
return ip_t
|
||||
end
|
||||
until not ip_t
|
||||
--
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function summarize_ip_ranges(ip_list, modify_raw_list)
|
||||
local summ_iter = summarize_ranges(group_ip_ranges(sort_ip_list(ip_list),
|
||||
modify_raw_list and ip_list)
|
||||
)
|
||||
return function()
|
||||
repeat
|
||||
local ip_t = summ_iter()
|
||||
if ip_t and ip_t[2] == 32 then
|
||||
if modify_raw_list then
|
||||
ip_list[it.int_to_ip(ip_t[1])] = true
|
||||
end
|
||||
else
|
||||
return ip_t
|
||||
end
|
||||
until not ip_t
|
||||
end
|
||||
end
|
||||
|
||||
function summarize_nets(cidr_list, modify_raw_list)
|
||||
return summarize_ranges(group_nets(sort_net_list(cidr_list),
|
||||
modify_raw_list and cidr_list))
|
||||
end
|
||||
|
||||
return {
|
||||
summarize_ip_ranges = summarize_ip_ranges,
|
||||
summarize_nets = summarize_nets,
|
||||
}
|
||||
+495
@@ -0,0 +1,495 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
(с) 2020 gSpot (https://github.com/gSpotx2f/ruantiblock_openwrt)
|
||||
|
||||
Python >= 3.6
|
||||
"""
|
||||
|
||||
from contextlib import contextmanager
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import ssl
|
||||
import sys
|
||||
from urllib import request
|
||||
from ruab_sum_ip import summarize_ip_ranges, summarize_nets
|
||||
|
||||
|
||||
class Config:
|
||||
environ_list = [
|
||||
"BLLIST_SOURCE",
|
||||
"BLLIST_MODE",
|
||||
"ALT_NSLOOKUP",
|
||||
"ALT_DNS_ADDR",
|
||||
"USE_IDN",
|
||||
"OPT_EXCLUDE_SLD",
|
||||
"OPT_EXCLUDE_MASKS",
|
||||
"FQDN_FILTER",
|
||||
"FQDN_FILTER_FILE",
|
||||
"IP_FILTER",
|
||||
"IP_FILTER_FILE",
|
||||
"SD_LIMIT",
|
||||
"IP_LIMIT",
|
||||
"OPT_EXCLUDE_NETS",
|
||||
"BLLIST_MIN_ENTRS",
|
||||
"STRIP_WWW",
|
||||
"DATA_DIR",
|
||||
"IPSET_DNSMASQ",
|
||||
"IPSET_IP_TMP",
|
||||
"IPSET_CIDR_TMP",
|
||||
"DNSMASQ_DATA_FILE",
|
||||
"IP_DATA_FILE",
|
||||
"UPDATE_STATUS_FILE",
|
||||
"RBL_ALL_URL",
|
||||
"RBL_IP_URL",
|
||||
"ZI_ALL_URL",
|
||||
"AF_IP_URL",
|
||||
"AF_FQDN_URL",
|
||||
"AZ_ENCODING",
|
||||
"RBL_ENCODING",
|
||||
"ZI_ENCODING",
|
||||
"AF_ENCODING",
|
||||
"SUMMARIZE_IP",
|
||||
"SUMMARIZE_CIDR",
|
||||
]
|
||||
FQDN_FILTER_PATTERNS = set()
|
||||
IP_FILTER_PATTERNS = set()
|
||||
|
||||
@classmethod
|
||||
def _load_config(cls, cfg_dict):
|
||||
|
||||
def normalize_string(string):
|
||||
return re.sub('"', '', string)
|
||||
|
||||
config_arrays = {
|
||||
"OPT_EXCLUDE_SLD",
|
||||
"OPT_EXCLUDE_NETS",
|
||||
}
|
||||
try:
|
||||
for k, v in cfg_dict.items():
|
||||
if k in config_arrays:
|
||||
value = {normalize_string(i) for i in v.split(" ")}
|
||||
else:
|
||||
try:
|
||||
value = int(v)
|
||||
except ValueError:
|
||||
value = normalize_string(v)
|
||||
setattr(cls, k, value)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def load_environ_config(cls):
|
||||
cls._load_config({
|
||||
k: v for k, v in os.environ.items()
|
||||
if k in cls.environ_list
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def _load_filter(cls, file_path, filter_patterns):
|
||||
try:
|
||||
with open(file_path, "rt") as file_handler:
|
||||
for line in file_handler:
|
||||
if line and re.match("[^#]", line):
|
||||
filter_patterns.add(line.strip())
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def load_fqdn_filter(cls, file_path=None):
|
||||
if cls.FQDN_FILTER:
|
||||
cls._load_filter(file_path or cls.FQDN_FILTER_FILE, cls.FQDN_FILTER_PATTERNS)
|
||||
|
||||
@classmethod
|
||||
def load_ip_filter(cls, file_path=None):
|
||||
if cls.IP_FILTER:
|
||||
cls._load_filter(file_path or cls.IP_FILTER_FILE, cls.IP_FILTER_PATTERNS)
|
||||
|
||||
|
||||
class ParserError(Exception):
|
||||
def __init__(self, reason=None):
|
||||
super().__init__(reason)
|
||||
self.reason = reason
|
||||
|
||||
def __str__(self):
|
||||
return self.reason
|
||||
|
||||
|
||||
class FieldValueError(ParserError):
|
||||
pass
|
||||
|
||||
|
||||
class BlackListParser(Config):
|
||||
def __init__(self):
|
||||
self.ip_pattern = re.compile("(([0-9]{1,3}[.]){3})[0-9]{1,3}")
|
||||
self.cidr_pattern = re.compile("([0-9]{1,3}[.]){3}[0-9]{1,3}/[0-9]{1,2}")
|
||||
self.fqdn_pattern = re.compile(
|
||||
"([а-яёa-z0-9_.*-]*?)([а-яёa-z0-9_-]+[.][а-яёa-z0-9-]+)",
|
||||
re.U)
|
||||
self.www_pattern = re.compile("^www[0-9]?[.]")
|
||||
self.cyr_pattern = re.compile("[а-яё]", re.U)
|
||||
self.fqdn_set = {}
|
||||
self.sld_dict = {}
|
||||
self.ip_set = {}
|
||||
self.ip_subnet_dict = {}
|
||||
self.cidr_set = set()
|
||||
self.cidr_count = 0
|
||||
self.ip_count = 0
|
||||
self.output_fqdn_count = 0
|
||||
self.ssl_unverified = False
|
||||
self.send_headers_dict = {
|
||||
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0",
|
||||
}
|
||||
### Proxies (ex.: self.proxies = {"http": "http://192.168.0.1:8080", "https": "http://192.168.0.1:8080"})
|
||||
self.proxies = None
|
||||
self.connect_timeout = None
|
||||
self.data_chunk = 2048
|
||||
self.url = "http://127.0.0.1"
|
||||
self.records_separator = "\n"
|
||||
self.fields_separator = ";"
|
||||
self.ips_separator = " | "
|
||||
self.default_site_encoding = "utf-8"
|
||||
self.site_encoding = self.default_site_encoding
|
||||
|
||||
@staticmethod
|
||||
def _compile_filter_patterns(filters_seq):
|
||||
return {
|
||||
re.compile(i, re.U)
|
||||
for i in filters_seq
|
||||
if i and type(i) == str
|
||||
}
|
||||
|
||||
@contextmanager
|
||||
def _make_connection(self,
|
||||
url,
|
||||
method="GET",
|
||||
postData=None,
|
||||
send_headers_dict=None,
|
||||
timeout=None):
|
||||
conn_object = http_code = received_headers = None
|
||||
req_object = request.Request(url,
|
||||
data=postData,
|
||||
headers=send_headers_dict,
|
||||
method=method)
|
||||
opener_args = [request.ProxyHandler(self.proxies)]
|
||||
if self.ssl_unverified:
|
||||
opener_args.append(request.HTTPSHandler(context=ssl._create_unverified_context()))
|
||||
try:
|
||||
conn_object = request.build_opener(*opener_args).open(
|
||||
req_object,
|
||||
timeout=(
|
||||
timeout if type(timeout) == int else socket._GLOBAL_DEFAULT_TIMEOUT
|
||||
)
|
||||
)
|
||||
http_code, received_headers = conn_object.status, conn_object.getheaders()
|
||||
except Exception as exception_object:
|
||||
print(f" Connection error! {exception_object} ( {url} )",
|
||||
file=sys.stderr)
|
||||
try:
|
||||
yield (conn_object, http_code, received_headers)
|
||||
except Exception as exception_object:
|
||||
raise ParserError(f"Parser error! {exception_object} ( {self.url} )")
|
||||
finally:
|
||||
if conn_object:
|
||||
conn_object.close()
|
||||
|
||||
def _download_data(self):
|
||||
with self._make_connection(
|
||||
self.url,
|
||||
send_headers_dict=self.send_headers_dict,
|
||||
timeout=self.connect_timeout
|
||||
) as conn_params:
|
||||
conn_object, http_code, _ = conn_params
|
||||
if http_code == 200:
|
||||
while True:
|
||||
chunk = conn_object.read(self.data_chunk)
|
||||
yield (chunk or None)
|
||||
if not chunk:
|
||||
break
|
||||
|
||||
def _align_chunk(self):
|
||||
rest = bytes()
|
||||
for chunk in self._download_data():
|
||||
if chunk is None:
|
||||
yield rest
|
||||
continue
|
||||
data, _, rest = (rest + chunk).rpartition(self.records_separator)
|
||||
yield data
|
||||
|
||||
def _split_entries(self):
|
||||
for chunk in self._align_chunk():
|
||||
for entry in chunk.split(self.records_separator):
|
||||
try:
|
||||
yield entry.decode(
|
||||
self.site_encoding or self.default_site_encoding)
|
||||
except UnicodeError:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _check_filter(string, filter_patterns):
|
||||
if filter_patterns and string:
|
||||
for pattern in filter_patterns:
|
||||
if pattern and pattern.search(string):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _get_subnet(self, ip_addr):
|
||||
regexp_obj = self.ip_pattern.fullmatch(ip_addr)
|
||||
return regexp_obj.group(1) if regexp_obj else None
|
||||
|
||||
def ip_field_processing(self, string):
|
||||
for i in string.split(self.ips_separator):
|
||||
if self.IP_FILTER and self._check_filter(i, self.IP_FILTER_PATTERNS):
|
||||
continue
|
||||
if self.ip_pattern.fullmatch(i) and i not in self.ip_set:
|
||||
subnet = self._get_subnet(i)
|
||||
if subnet in self.OPT_EXCLUDE_NETS or (
|
||||
not self.IP_LIMIT or (
|
||||
subnet not in self.ip_subnet_dict or self.ip_subnet_dict[subnet] < self.IP_LIMIT
|
||||
)
|
||||
):
|
||||
self.ip_set[i] = subnet
|
||||
self.ip_subnet_dict[subnet] = (self.ip_subnet_dict.get(subnet) or 0) + 1
|
||||
elif self.cidr_pattern.fullmatch(i) and i not in self.cidr_set:
|
||||
self.cidr_set.add(i)
|
||||
|
||||
def _convert_to_punycode(self, string):
|
||||
if self.cyr_pattern.search(string):
|
||||
if self.USE_IDN:
|
||||
try:
|
||||
string = string.encode("idna").decode(
|
||||
self.site_encoding or self.default_site_encoding)
|
||||
except UnicodeError:
|
||||
pass
|
||||
else:
|
||||
raise FieldValueError()
|
||||
return string
|
||||
|
||||
def _get_sld(self, fqdn):
|
||||
regexp_obj = self.fqdn_pattern.fullmatch(fqdn)
|
||||
return regexp_obj.group(2) if regexp_obj else None
|
||||
|
||||
def fqdn_field_processing(self, string):
|
||||
if self.ip_pattern.fullmatch(string):
|
||||
raise FieldValueError()
|
||||
string = string.strip("*.").lower()
|
||||
if self.STRIP_WWW:
|
||||
string = self.www_pattern.sub("", string)
|
||||
if not self.FQDN_FILTER or (
|
||||
self.FQDN_FILTER and not self._check_filter(string, self.FQDN_FILTER_PATTERNS)
|
||||
):
|
||||
if self.fqdn_pattern.fullmatch(string):
|
||||
string = self._convert_to_punycode(string)
|
||||
sld = self._get_sld(string)
|
||||
if sld in self.OPT_EXCLUDE_SLD or (
|
||||
not self.SD_LIMIT or (
|
||||
sld not in self.sld_dict or self.sld_dict[sld] < self.SD_LIMIT
|
||||
)
|
||||
):
|
||||
self.sld_dict[sld] = (self.sld_dict.get(sld) or 0) + 1
|
||||
self.fqdn_set[string] = sld
|
||||
else:
|
||||
raise FieldValueError()
|
||||
|
||||
def parser_func(self):
|
||||
"""
|
||||
Must be reload in the subclass
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def _check_sld_masks(self, sld):
|
||||
if self.OPT_EXCLUDE_MASKS:
|
||||
for pattern in self.OPT_EXCLUDE_MASKS:
|
||||
if re.fullmatch(pattern, sld):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _optimize_fqdn_set(self):
|
||||
optimized_set = set()
|
||||
for fqdn, sld in self.fqdn_set.items():
|
||||
if sld and (fqdn == sld or sld not in self.fqdn_set) and self.sld_dict.get(sld):
|
||||
if (not self._check_sld_masks(sld) and (
|
||||
self.SD_LIMIT and sld not in self.OPT_EXCLUDE_SLD
|
||||
)) and (self.sld_dict[sld] >= self.SD_LIMIT):
|
||||
record_value = sld
|
||||
del(self.sld_dict[sld])
|
||||
else:
|
||||
record_value = fqdn
|
||||
optimized_set.add(record_value)
|
||||
self.output_fqdn_count += 1
|
||||
self.fqdn_set = optimized_set
|
||||
|
||||
def _optimize_ip_set(self):
|
||||
optimized_set = set()
|
||||
for ip_addr, subnet in self.ip_set.items():
|
||||
if subnet in self.ip_subnet_dict:
|
||||
if subnet not in self.OPT_EXCLUDE_NETS and (
|
||||
self.IP_LIMIT and self.ip_subnet_dict[subnet] >= self.IP_LIMIT
|
||||
):
|
||||
self.cidr_set.add(f"{subnet}0/24")
|
||||
del(self.ip_subnet_dict[subnet])
|
||||
else:
|
||||
optimized_set.add(ip_addr)
|
||||
self.ip_count += 1
|
||||
self.ip_set = optimized_set
|
||||
|
||||
def _group_ip_ranges(self):
|
||||
if self.SUMMARIZE_IP:
|
||||
for i in summarize_ip_ranges(self.ip_set, True):
|
||||
self.cidr_set.add(i.with_prefixlen)
|
||||
self.ip_count = len(self.ip_set)
|
||||
|
||||
def _group_cidr_ranges(self):
|
||||
if self.SUMMARIZE_CIDR:
|
||||
for i in summarize_nets(self.cidr_set):
|
||||
self.cidr_set.add(i.with_prefixlen)
|
||||
self.cidr_count = len(self.cidr_set)
|
||||
|
||||
def run(self):
|
||||
ret_value = 1
|
||||
self.FQDN_FILTER_PATTERNS = self._compile_filter_patterns(self.FQDN_FILTER_PATTERNS)
|
||||
self.IP_FILTER_PATTERNS = self._compile_filter_patterns(self.IP_FILTER_PATTERNS)
|
||||
self.records_separator = bytes(self.records_separator, "utf-8")
|
||||
self.parser_func()
|
||||
if (len(self.ip_set) + len(self.cidr_set) + len(self.fqdn_set)) >= self.BLLIST_MIN_ENTRS:
|
||||
self._optimize_fqdn_set()
|
||||
self._optimize_ip_set()
|
||||
self._group_ip_ranges()
|
||||
self._group_cidr_ranges()
|
||||
ret_value = 0
|
||||
else:
|
||||
ret_value = 2
|
||||
return ret_value
|
||||
|
||||
|
||||
class RblFQDN(BlackListParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.url = self.RBL_ALL_URL
|
||||
self.fields_separator = "],"
|
||||
self.ips_separator = ","
|
||||
|
||||
def parser_func(self):
|
||||
for entry in self._split_entries():
|
||||
entry_list = entry.partition(self.fields_separator)
|
||||
ip_string = re.sub(r"[' \]\[]", "", entry_list[0])
|
||||
fqdn_string = re.sub(",.*$", "", entry_list[2])
|
||||
if fqdn_string:
|
||||
try:
|
||||
self.fqdn_field_processing(fqdn_string)
|
||||
except FieldValueError:
|
||||
self.ip_field_processing(ip_string)
|
||||
else:
|
||||
self.ip_field_processing(ip_string)
|
||||
|
||||
|
||||
class RblIp(BlackListParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.url = self.RBL_IP_URL
|
||||
|
||||
def parser_func(self):
|
||||
for entry in self._split_entries():
|
||||
self.ip_field_processing(entry.rstrip(","))
|
||||
|
||||
|
||||
class ZiFQDN(BlackListParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.url = self.ZI_ALL_URL
|
||||
self.site_encoding = self.ZI_ENCODING
|
||||
|
||||
def parser_func(self):
|
||||
for entry in self._split_entries():
|
||||
entry_list = entry.split(self.fields_separator)
|
||||
try:
|
||||
if entry_list[1]:
|
||||
try:
|
||||
self.fqdn_field_processing(entry_list[1])
|
||||
except FieldValueError:
|
||||
self.ip_field_processing(entry_list[0])
|
||||
else:
|
||||
self.ip_field_processing(entry_list[0])
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
|
||||
class ZiIp(ZiFQDN):
|
||||
def parser_func(self):
|
||||
for entry in self._split_entries():
|
||||
entry_list = entry.split(self.fields_separator)
|
||||
self.ip_field_processing(entry_list[0])
|
||||
|
||||
|
||||
class AfFQDN(BlackListParser):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.url = self.AF_FQDN_URL
|
||||
|
||||
def parser_func(self):
|
||||
for entry in self._split_entries():
|
||||
try:
|
||||
self.fqdn_field_processing(entry)
|
||||
except FieldValueError:
|
||||
self.ip_field_processing(entry)
|
||||
|
||||
|
||||
class AfIp(BlackListParser):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.url = self.AF_IP_URL
|
||||
self.BLLIST_MIN_ENTRS = 100
|
||||
|
||||
def parser_func(self):
|
||||
for entry in self._split_entries():
|
||||
self.ip_field_processing(entry)
|
||||
|
||||
|
||||
class WriteConfigFiles(Config):
|
||||
def __init__(self):
|
||||
self.write_buffer = -1
|
||||
|
||||
def write_ipset_config(self, ip_set, cidr_set):
|
||||
with open(self.IP_DATA_FILE, "wt", buffering=self.write_buffer) as file_handler:
|
||||
for i in ip_set:
|
||||
file_handler.write(f"add {self.IPSET_IP_TMP} {i}\n")
|
||||
for i in cidr_set:
|
||||
file_handler.write(f"add {self.IPSET_CIDR_TMP} {i}\n")
|
||||
|
||||
def write_dnsmasq_config(self, fqdn_set):
|
||||
with open(self.DNSMASQ_DATA_FILE, "wt", buffering=self.write_buffer) as file_handler:
|
||||
for fqdn in fqdn_set:
|
||||
file_handler.write(
|
||||
f"server=/{fqdn}/{self.ALT_DNS_ADDR}\nipset=/{fqdn}/{self.IPSET_DNSMASQ}\n"
|
||||
if self.ALT_NSLOOKUP else
|
||||
f"ipset=/{fqdn}/{self.IPSET_DNSMASQ}\n")
|
||||
|
||||
def write_update_status_file(self, ip_count, cidr_count, output_fqdn_count):
|
||||
with open(self.UPDATE_STATUS_FILE, "wt") as file_handler:
|
||||
file_handler.write(
|
||||
f"{ip_count} {cidr_count} {output_fqdn_count}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Config.load_environ_config()
|
||||
Config.load_fqdn_filter()
|
||||
Config.load_ip_filter()
|
||||
ctx_dict = {
|
||||
"ip": {"rublacklist": RblIp, "zapret-info": ZiIp, "antifilter": AfIp},
|
||||
"fqdn": {"rublacklist": RblFQDN, "zapret-info": ZiFQDN, "antifilter": AfFQDN},
|
||||
}
|
||||
write_cfg_obj = WriteConfigFiles()
|
||||
try:
|
||||
ctx = ctx_dict[Config.BLLIST_MODE][Config.BLLIST_SOURCE]()
|
||||
except KeyError:
|
||||
print("Wrong configuration! (Config.BLLIST_MODE or Config.BLLIST_SOURCE)",
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
ret_code = ctx.run()
|
||||
if ret_code == 0:
|
||||
write_cfg_obj.write_dnsmasq_config(ctx.fqdn_set)
|
||||
write_cfg_obj.write_ipset_config(ctx.ip_set, ctx.cidr_set)
|
||||
write_cfg_obj.write_update_status_file(ctx.ip_count, ctx.cidr_count, ctx.output_fqdn_count)
|
||||
sys.exit(ret_code)
|
||||
@@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# (с) 2020 gSpot (https://github.com/gSpotx2f/ruantiblock_openwrt)
|
||||
|
||||
from ipaddress import IPv4Address, IPv4Network, summarize_address_range
|
||||
|
||||
HOSTS_LIMIT = 0
|
||||
NETS_LIMIT = 0
|
||||
|
||||
|
||||
def _sort_ip_func(e: str) -> IPv4Address:
|
||||
return IPv4Address(e)
|
||||
|
||||
|
||||
def _group_ip_ranges(ip_list: list, raw_list=None) -> tuple:
|
||||
def remove_items(start, end):
|
||||
for ip in range(int(start), int(end) + 1):
|
||||
raw_list.remove(str(IPv4Address(ip)))
|
||||
|
||||
start = end = None
|
||||
hosts = 1
|
||||
for ip in ip_list:
|
||||
ip_obj = IPv4Address(ip)
|
||||
if end and (end + 1) == ip_obj:
|
||||
hosts += 1
|
||||
else:
|
||||
if hosts > 1 and hosts >= HOSTS_LIMIT:
|
||||
if raw_list:
|
||||
remove_items(start, end)
|
||||
yield start, end
|
||||
start = ip_obj
|
||||
hosts = 1
|
||||
end = ip_obj
|
||||
else:
|
||||
if hosts > 1 and hosts >= HOSTS_LIMIT:
|
||||
if raw_list:
|
||||
remove_items(start, end)
|
||||
yield start, end
|
||||
|
||||
|
||||
def summarize_ip_ranges(ip_list: (list, set), modify_raw_list=False) -> IPv4Network:
|
||||
for s, e in _group_ip_ranges(sorted(ip_list, key=_sort_ip_func),
|
||||
modify_raw_list and ip_list):
|
||||
for i in summarize_address_range(s, e):
|
||||
if i.prefixlen == 32:
|
||||
if modify_raw_list:
|
||||
if type(ip_list) == set:
|
||||
ip_list.add(i.network_address)
|
||||
else:
|
||||
ip_list.append(i.network_address)
|
||||
else:
|
||||
yield i
|
||||
|
||||
|
||||
def _sort_net_func(e: str) -> IPv4Network:
|
||||
return IPv4Network(e)
|
||||
|
||||
|
||||
def _group_nets(cidr_list: list, raw_list=None) -> IPv4Network:
|
||||
|
||||
def remove_items(start, end):
|
||||
for ip in range(int(start), int(end) + 1, 256):
|
||||
raw_list.remove(str(IPv4Address(ip)) + "/24")
|
||||
|
||||
start = end = curr_super_net = None
|
||||
nets = 1
|
||||
for net in cidr_list:
|
||||
net_obj = IPv4Network(net)
|
||||
prefix_len = net_obj.prefixlen
|
||||
if prefix_len == 24:
|
||||
address = net_obj.network_address
|
||||
super_net = net_obj.supernet(new_prefix=16)
|
||||
if end and super_net == curr_super_net and (end + 256) == address:
|
||||
nets += 1
|
||||
else:
|
||||
if nets > 1 and nets >= NETS_LIMIT:
|
||||
if raw_list:
|
||||
remove_items(start, end)
|
||||
yield summarize_address_range(IPv4Address(start), IPv4Address(end + 255))
|
||||
start = address
|
||||
curr_super_net = super_net
|
||||
nets = 1
|
||||
end = address
|
||||
else:
|
||||
if nets > 1 and nets >= NETS_LIMIT:
|
||||
if raw_list:
|
||||
remove_items(start, end)
|
||||
yield summarize_address_range(IPv4Address(start), IPv4Address(end + 255))
|
||||
|
||||
|
||||
def summarize_nets(cidr_list: (list, set)) -> IPv4Network:
|
||||
for i in _group_nets(sorted(cidr_list, key=_sort_net_func), cidr_list):
|
||||
for j in i:
|
||||
yield j
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
config main 'config'
|
||||
option proxy_mode '1'
|
||||
option proxy_local_clients '1'
|
||||
option ipset_clear_sets '0'
|
||||
option if_lan 'eth0'
|
||||
option if_vpn 'tun0'
|
||||
option tor_trans_port '9040'
|
||||
option onion_dns_addr '127.0.0.1#9053'
|
||||
option add_user_entries '0'
|
||||
option use_logger '1'
|
||||
option def_total_proxy '0'
|
||||
option bllist_source 'rublacklist'
|
||||
option bllist_mode 'ip'
|
||||
option ip_limit '0'
|
||||
option summarize_ip '0'
|
||||
option summarize_cidr '0'
|
||||
option ip_filter '0'
|
||||
option sd_limit '16'
|
||||
list opt_exclude_sld 'livejournal.com'
|
||||
list opt_exclude_sld 'facebook.com'
|
||||
list opt_exclude_sld 'vk.com'
|
||||
list opt_exclude_sld 'blog.jp'
|
||||
list opt_exclude_sld 'msk.ru'
|
||||
list opt_exclude_sld 'net.ru'
|
||||
list opt_exclude_sld 'org.ru'
|
||||
list opt_exclude_sld 'net.ua'
|
||||
list opt_exclude_sld 'com.ua'
|
||||
list opt_exclude_sld 'org.ua'
|
||||
list opt_exclude_sld 'co.uk'
|
||||
list opt_exclude_sld 'amazonaws.com'
|
||||
option fqdn_filter '0'
|
||||
option use_idn '0'
|
||||
option alt_nslookup '0'
|
||||
option alt_dns_addr '8.8.8.8'
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
|
||||
IF_VPN=`uci get ruantiblock.config.if_vpn`
|
||||
PROXY_MODE=`uci get ruantiblock.config.proxy_mode`
|
||||
|
||||
if [ "$ACTION" = "ifup" ] && [ "$PROXY_MODE" = "2" ] && [ "$DEVICE" = "$IF_VPN" ]; then
|
||||
[ `/usr/bin/ruantiblock raw-status` -ne 2 ] && /usr/bin/ruantiblock reload
|
||||
fi
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh /etc/rc.common
|
||||
|
||||
START=92
|
||||
STOP=01
|
||||
|
||||
RUAB="/usr/bin/ruantiblock"
|
||||
|
||||
start() {
|
||||
$RUAB start
|
||||
}
|
||||
|
||||
stop() {
|
||||
$RUAB stop
|
||||
}
|
||||
|
||||
restart() {
|
||||
$RUAB restart
|
||||
}
|
||||
|
||||
reload() {
|
||||
$RUAB reload
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
^youtube[.]com
|
||||
parimatch
|
||||
paripartners
|
||||
marathon
|
||||
pm[-][0-9]
|
||||
fbmetrix
|
||||
[ck]?a[sz]ino?
|
||||
[vw]ulkan
|
||||
slots?
|
||||
nar[ck]
|
||||
st[au]f
|
||||
al[ck]o
|
||||
[cs]pravk
|
||||
bets?
|
||||
igr[ova]+
|
||||
olimp
|
||||
poker
|
||||
leon
|
||||
jackpot
|
||||
spin
|
||||
loto
|
||||
bcity
|
||||
stavka
|
||||
lotery
|
||||
fortuna
|
||||
blackja
|
||||
azart
|
||||
eldorado
|
||||
play
|
||||
game
|
||||
777
|
||||
bonus
|
||||
admiral
|
||||
winner
|
||||
cash
|
||||
market
|
||||
kupit
|
||||
space
|
||||
drug
|
||||
farma
|
||||
apteka
|
||||
drop
|
||||
rasta
|
||||
smok
|
||||
semen
|
||||
seed
|
||||
steroid
|
||||
diplom
|
||||
medic
|
||||
prostitutk
|
||||
individualk
|
||||
dosug
|
||||
putan
|
||||
@@ -0,0 +1,110 @@
|
||||
|
||||
### Настройки ruantiblock ###
|
||||
|
||||
### Директория данных (генерируемые конфиги dnsmasq, ipset и пр.)
|
||||
DATA_DIR="/etc/ruantiblock/var"
|
||||
### Директория исполняемых скриптов
|
||||
EXEC_DIR="/usr/bin"
|
||||
### Команда для перезапуска dnsmasq
|
||||
DNSMASQ_RESTART_CMD="/etc/init.d/dnsmasq restart"
|
||||
### Директория для html-страницы статуса (не используется в OpenWrt)
|
||||
HTML_DIR="/www"
|
||||
|
||||
### Режим обработки пакетов в правилах iptables (1 - Tor, 2 - VPN)
|
||||
PROXY_MODE=1
|
||||
### Применять правила проксификации для трафика локальных сервисов роутера (0 - off, 1 - on)
|
||||
PROXY_LOCAL_CLIENTS=1
|
||||
### Удаление записей из основных сетов перед началом заполнения временных сетов при обновлении (для освобождения оперативной памяти перед заполнением сетов) (0 - off, 1 - on)
|
||||
IPSET_CLEAR_SETS=0
|
||||
### Входящий сетевой интерфейс для правил iptables
|
||||
IF_LAN="eth0"
|
||||
### VPN интерфейс для правил маршрутизации
|
||||
IF_VPN="tun0"
|
||||
### Порт транспарентного proxy Tor (параметр TransPort в torrc)
|
||||
TOR_TRANS_PORT=9040
|
||||
### DNS-сервер для резолвинга в домене .onion (Tor)
|
||||
ONION_DNS_ADDR="127.0.0.1#9053"
|
||||
### Добавление в список блокировок пользовательских записей из файла $USER_ENTRIES_FILE (0 - off, 1 - on)
|
||||
### В $DATA_DIR можно создать текстовый файл user_entries с записями IP, CIDR или FQDN (одна на строку). Эти записи будут добавлены в список блокировок
|
||||
### В записях FQDN можно задать DNS-сервер для разрешения данного домена, через пробел (прим.: domain.com 8.8.8.8)
|
||||
### Можно комментировать строки (#)
|
||||
ADD_USER_ENTRIES=0
|
||||
### DNS-сервер для пользовательских записей (пустая строка - без DNS-сервера). Можно с портом: 8.8.8.8#53. Если в записи указан свой DNS-сервер - он имеет приоритет
|
||||
USER_ENTRIES_DNS=""
|
||||
### Файл пользовательских записей
|
||||
USER_ENTRIES_FILE="/etc/ruantiblock/user_entries"
|
||||
### Запись событий в syslog (0 - off, 1 - on)
|
||||
USE_LOGGER=1
|
||||
### Режим полного прокси при старте скрипта (0 - off, 1 - on). Если 1, то весь трафик всегда идёт через прокси. Все пакеты попадающие в цепочку $IPT_CHAIN попадают в tor или VPN, за исключением сетей из $TOTAL_PROXY_EXCLUDE_NETS. Списки блокировок не используются для фильтрации. Работает только при PROXY_LOCAL_CLIENTS=0
|
||||
DEF_TOTAL_PROXY=0
|
||||
### Html-страница с инфо о текущем статусе (0 - off, 1 - on) (не используется в OpenWrt)
|
||||
HTML_INFO=0
|
||||
### --set-mark для отбора пакетов в VPN туннель
|
||||
VPN_PKTS_MARK=8
|
||||
### Максимальное кол-во элементов списка ipset
|
||||
IPSET_MAXELEM=2000000
|
||||
### Таймаут для записей в сете $IPSET_DNSMASQ
|
||||
IPSET_DNSMASQ_TIMEOUT=3600
|
||||
### Кол-во попыток обновления блэклиста (в случае неудачи)
|
||||
MODULE_RUN_ATTEMPTS=3
|
||||
### Таймаут между попытками обновления
|
||||
MODULE_RUN_TIMEOUT=60
|
||||
### Модули для получения и обработки блэклиста
|
||||
BLLIST_MODULE=""
|
||||
#BLLIST_MODULE="/usr/bin/ruab_parser.lua"
|
||||
#BLLIST_MODULE="/usr/bin/ruab_parser.py"
|
||||
|
||||
### Настройки модулей-парсеров ###
|
||||
|
||||
### Источник для обновления списка блокировок (rublacklist, zapret-info, antifilter)
|
||||
BLLIST_SOURCE="rublacklist"
|
||||
### Режим обхода блокировок: ip, fqdn
|
||||
BLLIST_MODE="ip"
|
||||
### В случае если из источника получено менее указанного кол-ва записей, то обновления списков не происходит
|
||||
BLLIST_MIN_ENTRS=30000
|
||||
|
||||
### Лимит ip адресов. При достижении, в конфиг ipset будет добавлена вся подсеть /24 вместо множества ip-адресов пренадлежащих этой сети (0 - off)
|
||||
IP_LIMIT=0
|
||||
### Подсети класса C (/24). IP адреса из этих подсетей не группируются при оптимизации (записи д.б. в виде: 68.183.221. 149.154.162. и пр.). Прим.: OPT_EXCLUDE_NETS="68.183.221. 149.154.162."
|
||||
OPT_EXCLUDE_NETS=""
|
||||
### Группировать идущие подряд IP адреса в подсетях /24 в диапазоны CIDR
|
||||
SUMMARIZE_IP=0
|
||||
### Группировать идущие подряд подсети /24 в диапазоны CIDR
|
||||
SUMMARIZE_CIDR=0
|
||||
### Фильтрация записей блэклиста по шаблонам из файла IP_FILTER_FILE. Записи (IP, CIDR) попадающие под шаблоны исключаются из кофига ipset (0 - off, 1 - on)
|
||||
IP_FILTER=0
|
||||
### Файл с шаблонами ip для опции FQDN_FILTER (каждый шаблон в отдельной строке. # в первом символе строки - комментирует строку)
|
||||
IP_FILTER_FILE="/etc/ruantiblock/ip_filter"
|
||||
|
||||
### Лимит для субдоменов. При достижении, в конфиг dnsmasq будет добавлен весь домен 2-го ур-ня вместо множества субдоменов (0 - off)
|
||||
SD_LIMIT=16
|
||||
### SLD не подлежащие оптимизации (через пробел)
|
||||
OPT_EXCLUDE_SLD="livejournal.com facebook.com vk.com blog.jp msk.ru net.ru org.ru net.ua com.ua org.ua co.uk amazonaws.com"
|
||||
### Не оптимизировать SLD попадающие под выражения (через пробел) ("[.][a-z]{2,3}[.][a-z]{2}$")
|
||||
OPT_EXCLUDE_MASKS=""
|
||||
### Фильтрация записей блэклиста по шаблонам из файла FQDN_FILTER_FILE. Записи (FQDN) попадающие под шаблоны исключаются из кофига dnsmasq (0 - off, 1 - on)
|
||||
FQDN_FILTER=0
|
||||
### Файл с шаблонами FQDN для опции FQDN_FILTER (каждый шаблон в отдельной строке. # в первом символе строки - комментирует строку)
|
||||
FQDN_FILTER_FILE="/etc/ruantiblock/fqdn_filter"
|
||||
### Обрезка www[0-9]. в FQDN (0 - off, 1 - on)
|
||||
STRIP_WWW=1
|
||||
### Преобразование кириллических доменов в punycode (0 - off, 1 - on)
|
||||
USE_IDN=0
|
||||
### Перенаправлять DNS-запросы на альтернативный DNS-сервер для заблокированных FQDN (0 - off, 1 - on)
|
||||
ALT_NSLOOKUP=0
|
||||
### Альтернативный DNS-сервер
|
||||
ALT_DNS_ADDR="8.8.8.8"
|
||||
|
||||
### Источники блэклиста
|
||||
RBL_ALL_URL="https://reestr.rublacklist.net/api/v2/current/csv"
|
||||
#RBL_ALL_URL="https://api.reserve-rbl.ru/api/v2/current/csv"
|
||||
RBL_IP_URL="https://reestr.rublacklist.net/api/v2/ips/csv"
|
||||
#RBL_IP_URL="https://api.reserve-rbl.ru/api/v2/ips/csv"
|
||||
ZI_ALL_URL="https://raw.githubusercontent.com/zapret-info/z-i/master/dump.csv"
|
||||
#ZI_ALL_URL="https://sourceforge.net/p/z-i/code-0/HEAD/tree/dump.csv?format=raw"
|
||||
AF_IP_URL="https://antifilter.download/list/allyouneed.lst"
|
||||
AF_FQDN_URL="https://antifilter.download/list/domains.lst"
|
||||
AZ_ENCODING=""
|
||||
RBL_ENCODING=""
|
||||
ZI_ENCODING="CP1251"
|
||||
AF_ENCODING=""
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
AWK_CMD="awk"
|
||||
UCI_SECTION="ruantiblock.config"
|
||||
UCI_VARS="proxy_mode proxy_local_clients ipset_clear_sets if_lan if_vpn tor_trans_port onion_dns_addr add_user_entries user_entries_dns use_logger def_total_proxy bllist_module bllist_source bllist_mode ip_limit opt_exclude_nets summarize_ip summarize_cidr ip_filter sd_limit opt_exclude_sld fqdn_filter use_idn alt_nslookup alt_dns_addr"
|
||||
|
||||
eval `uci show "$UCI_SECTION" | $AWK_CMD -F "=" -v UCI_VARS="$UCI_VARS" '
|
||||
BEGIN {
|
||||
split(UCI_VARS, split_array, " ");
|
||||
for(i in split_array)
|
||||
vars_array[split_array[i]]="";
|
||||
}
|
||||
{
|
||||
sub(/^.*[.]/, "", $1);
|
||||
gsub(/["\047]/, "", $2);
|
||||
if($1 in vars_array) {
|
||||
print toupper($1) "=\"" $2 "\"";
|
||||
delete vars_array[$1];
|
||||
};
|
||||
}
|
||||
END {
|
||||
if(length(vars_array) > 0) {
|
||||
for(i in vars_array)
|
||||
print toupper(i) "=\"""\"";
|
||||
};
|
||||
}'`
|
||||
@@ -0,0 +1,44 @@
|
||||
Info() {
|
||||
local _set
|
||||
if CheckStatus; then
|
||||
printf "{\"status\":\"enabled\",\"last_blacklist_update\":{"
|
||||
if [ -f "$UPDATE_STATUS_FILE" ]; then
|
||||
$AWK_CMD '{
|
||||
if(NF < 4)
|
||||
print "\"status\":false";
|
||||
else
|
||||
print "\"status\":true,\"date\":\""$4"\",\"ip\":\""$1"\",\"cidr\":\""$2"\",\"fqdn\":\""$3"\"";
|
||||
}' "$UPDATE_STATUS_FILE"
|
||||
else
|
||||
printf "\"status\":false"
|
||||
fi
|
||||
printf "},"
|
||||
IptListChain | $AWK_CMD '
|
||||
BEGIN {
|
||||
printf "\"iptables\":{"
|
||||
}
|
||||
{
|
||||
if(NR > 2)
|
||||
printf "\""(($10 == "!") ? $12 : $11)"\":\""$2"\",";
|
||||
}
|
||||
END {
|
||||
printf "\"_dummy\":false},";
|
||||
}'
|
||||
printf "\"ipset\":{";
|
||||
for _set in "$IPSET_TOTAL_PROXY" "$IPSET_CIDR_TMP" "$IPSET_CIDR" "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_DNSMASQ" "$IPSET_ONION"
|
||||
do
|
||||
$IPSET_CMD list "$_set" -terse | $AWK_CMD -F ": " '
|
||||
{
|
||||
if($1 ~ /^Name/)
|
||||
printf "\""$2"\":[";
|
||||
else if($1 ~ /^Size in memory/)
|
||||
printf "\""$2"\",";
|
||||
else if($1 ~ /^Number of entries/)
|
||||
printf "\""$2"\"],";
|
||||
}'
|
||||
done
|
||||
printf "\"_dummy\":false}}"
|
||||
else
|
||||
printf "{\"status\": \"disabled\"}"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
|
||||
IP_CMD="ip"
|
||||
IPT_CMD="iptables"
|
||||
IPT_CHAIN="$NAME"
|
||||
IPT_FIRST_CHAIN="PREROUTING"
|
||||
VPN_ROUTE_TABLE=99
|
||||
|
||||
### Tor
|
||||
IPT_TABLE="nat"
|
||||
IPT_FIRST_CHAIN_RULE="-i ${IF_LAN} -j ${IPT_CHAIN}"
|
||||
IPT_IPSET_TARGET="dst -p tcp -j REDIRECT --to-ports ${TOR_TRANS_PORT}"
|
||||
IPT_IPSETS="${IPSET_ONION} ${IPSET_CIDR} ${IPSET_IP} ${IPSET_DNSMASQ}"
|
||||
|
||||
if [ "$PROXY_MODE" = "2" ]; then
|
||||
### VPN
|
||||
IPT_TABLE="mangle"
|
||||
IPT_FIRST_CHAIN_RULE="-j ${IPT_CHAIN}"
|
||||
IPT_IPSET_TARGET="dst,src -j MARK --set-mark ${VPN_PKTS_MARK}"
|
||||
IPT_IPSETS="${IPSET_CIDR} ${IPSET_IP} ${IPSET_DNSMASQ}"
|
||||
fi
|
||||
|
||||
IptCmdWrapper() {
|
||||
local _i=0 _attempts=10 _return_code=1
|
||||
while [ $_i -lt $_attempts ]
|
||||
do
|
||||
if $*; then
|
||||
_return_code=$?
|
||||
break
|
||||
fi
|
||||
_i=`expr $_i + 1`
|
||||
done
|
||||
return $_return_code
|
||||
}
|
||||
|
||||
IptVpnRouteAdd() {
|
||||
VPN_IP=`$IP_CMD addr list dev $IF_VPN 2> /dev/null | $AWK_CMD '/inet/{sub("/[0-9]{1,2}$", "", $2); print $2; exit}'`
|
||||
if [ -n "$VPN_IP" ]; then
|
||||
echo 0 > /proc/sys/net/ipv4/conf/$IF_VPN/rp_filter
|
||||
IptVpnRouteDel 2> /dev/null
|
||||
$IP_CMD rule add fwmark $VPN_PKTS_MARK table $VPN_ROUTE_TABLE priority 1000
|
||||
$IP_CMD route add default via $VPN_IP table $VPN_ROUTE_TABLE
|
||||
fi
|
||||
}
|
||||
|
||||
IptVpnRouteDel() {
|
||||
$IP_CMD route flush table $VPN_ROUTE_TABLE
|
||||
$IP_CMD rule del table $VPN_ROUTE_TABLE
|
||||
}
|
||||
|
||||
IptVpnRouteStatus() {
|
||||
[ -n "`$IP_CMD route show table $VPN_ROUTE_TABLE 2> /dev/null`" ] && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
IptMainAdd() {
|
||||
local _set
|
||||
$IPT_CMD -t "$IPT_TABLE" -N "$IPT_CHAIN"
|
||||
$IPT_CMD -t "$IPT_TABLE" -I "$IPT_FIRST_CHAIN" 1 $IPT_FIRST_CHAIN_RULE
|
||||
for _set in $IPT_IPSETS
|
||||
do
|
||||
IptCmdWrapper $IPT_CMD -t "$IPT_TABLE" -A "$IPT_CHAIN" -m set --match-set "$_set" $IPT_IPSET_TARGET
|
||||
done
|
||||
if [ "$PROXY_MODE" = "2" ]; then
|
||||
IptVpnRouteAdd
|
||||
fi
|
||||
}
|
||||
|
||||
IptMainDel() {
|
||||
IptCmdWrapper $IPT_CMD -t "$IPT_TABLE" -D "$IPT_FIRST_CHAIN" $IPT_FIRST_CHAIN_RULE
|
||||
$IPT_CMD -t "$IPT_TABLE" -F "$IPT_CHAIN"
|
||||
$IPT_CMD -t "$IPT_TABLE" -X "$IPT_CHAIN"
|
||||
if [ "$PROXY_MODE" = "2" ]; then
|
||||
IptVpnRouteDel 2> /dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
IPT_TP_RULE="-m set ! --match-set ${IPSET_TOTAL_PROXY} ${IPT_IPSET_TARGET}"
|
||||
|
||||
IptTotalProxyDel() {
|
||||
IptCmdWrapper $IPT_CMD -t "$IPT_TABLE" -D "$IPT_CHAIN" $IPT_TP_RULE
|
||||
}
|
||||
|
||||
IptTotalProxyAdd() {
|
||||
$IPT_CMD -t "$IPT_TABLE" -I "$IPT_CHAIN" 1 $IPT_TP_RULE
|
||||
}
|
||||
|
||||
IptTotalProxyStatus() {
|
||||
$IPT_CMD -t "$IPT_TABLE" -L "$IPT_CHAIN" 2> /dev/null | $AWK_CMD -v IPSET_TOTAL_PROXY="$IPSET_TOTAL_PROXY" -v RET_CODE=1 '$0 ~ IPSET_TOTAL_PROXY {RET_CODE=0} END {exit RET_CODE}'
|
||||
return $?
|
||||
}
|
||||
|
||||
IPT_OUTPUT_FIRST_RULE="-j ${IPT_CHAIN}"
|
||||
|
||||
IptLocalClientsAdd() {
|
||||
$IPT_CMD -t "$IPT_TABLE" -I OUTPUT 1 $IPT_OUTPUT_FIRST_RULE
|
||||
}
|
||||
|
||||
IptLocalClientsDel() {
|
||||
IptCmdWrapper $IPT_CMD -t "$IPT_TABLE" -D OUTPUT $IPT_OUTPUT_FIRST_RULE
|
||||
}
|
||||
|
||||
IptListChain() {
|
||||
$IPT_CMD -t "$IPT_TABLE" -v -L "$IPT_CHAIN"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
_link="/tmp/dnsmasq.d/ruantiblock.conf"
|
||||
ln -fs $DNSMASQ_DATA_FILE $_link
|
||||
eval `echo "$DNSMASQ_RESTART_CMD"`
|
||||
Executable
+713
@@ -0,0 +1,713 @@
|
||||
#!/bin/sh
|
||||
|
||||
########################################################################
|
||||
#
|
||||
# Ruantiblock
|
||||
# (с) 2020 gSpot (https://github.com/gSpotx2f/ruantiblock_openwrt)
|
||||
#
|
||||
########################################################################
|
||||
|
||||
export NAME="ruantiblock"
|
||||
export LANG="en_US.UTF-8"
|
||||
export LANGUAGE="en"
|
||||
|
||||
#################### Platform-specific settings ########################
|
||||
|
||||
CONFIG_DIR="/etc/${NAME}"
|
||||
CONFIG_FILE="${CONFIG_DIR}/${NAME}.conf"
|
||||
export DATA_DIR="${CONFIG_DIR}/var"
|
||||
export EXEC_DIR="/usr/bin"
|
||||
### Команда для перезапуска dnsmasq
|
||||
export DNSMASQ_RESTART_CMD="/etc/init.d/dnsmasq restart"
|
||||
### Директория для html-страницы статуса (не используется в OpenWrt)
|
||||
export HTML_DIR="/www"
|
||||
|
||||
########################## Default Settings ############################
|
||||
|
||||
### Режим обработки пакетов в правилах iptables (1 - Tor, 2 - VPN)
|
||||
export PROXY_MODE=1
|
||||
### Применять правила проксификации для трафика локальных сервисов роутера (0 - off, 1 - on)
|
||||
export PROXY_LOCAL_CLIENTS=1
|
||||
### Удаление записей из основных сетов перед началом заполнения временных сетов при обновлении (для освобождения оперативной памяти перед заполнением сетов) (0 - off, 1 - on)
|
||||
export IPSET_CLEAR_SETS=0
|
||||
### Входящий сетевой интерфейс для правил iptables
|
||||
export IF_LAN="eth0"
|
||||
### VPN интерфейс для правил iptables
|
||||
export IF_VPN="tun0"
|
||||
### Порт транспарентного proxy Tor (параметр TransPort в torrc)
|
||||
export TOR_TRANS_PORT=9040
|
||||
### DNS-сервер для резолвинга в домене .onion (Tor)
|
||||
export ONION_DNS_ADDR="127.0.0.1#9053"
|
||||
### Добавление в список блокировок пользовательских записей из файла $USER_ENTRIES_FILE (0 - off, 1 - on)
|
||||
### В $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"
|
||||
### Запись событий в syslog (0 - off, 1 - on)
|
||||
export USE_LOGGER=1
|
||||
### Режим полного прокси при старте скрипта (0 - off, 1 - on). Если 1, то весь трафик всегда идёт через прокси. Все пакеты попадающие в цепочку $IPT_CHAIN попадают в tor или VPN, за исключением сетей из $TOTAL_PROXY_EXCLUDE_NETS. Списки блокировок не используются для фильтрации. Работает только при PROXY_LOCAL_CLIENTS=0
|
||||
export DEF_TOTAL_PROXY=0
|
||||
### Трафик в заданные сети идет напрямую, не попадая в Tor или VPN, в режиме total-proxy
|
||||
export TOTAL_PROXY_EXCLUDE_NETS="10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 100.64.0.0/10"
|
||||
### Html-страница с инфо о текущем статусе (0 - off, 1 - on) (не используется в OpenWrt)
|
||||
export HTML_INFO=0
|
||||
### --set-mark для отбора пакетов в VPN туннель
|
||||
export VPN_PKTS_MARK=8
|
||||
### Максимальное кол-во элементов списка ipset
|
||||
export IPSET_MAXELEM=2000000
|
||||
### Таймаут для записей в сете $IPSET_DNSMASQ
|
||||
export IPSET_DNSMASQ_TIMEOUT=3600
|
||||
### Кол-во попыток обновления блэклиста (в случае неудачи)
|
||||
export MODULE_RUN_ATTEMPTS=3
|
||||
### Таймаут между попытками обновления
|
||||
export MODULE_RUN_TIMEOUT=60
|
||||
### Модули для получения и обработки блэклиста
|
||||
export BLLIST_MODULE=""
|
||||
#export BLLIST_MODULE="${EXEC_DIR}/ruab_parser.lua"
|
||||
#export BLLIST_MODULE="${EXEC_DIR}/ruab_parser.py"
|
||||
|
||||
##############################
|
||||
|
||||
### Источник для обновления списка блокировок (rublacklist, zapret-info, antifilter)
|
||||
export BLLIST_SOURCE="rublacklist"
|
||||
### Режим обхода блокировок: ip, fqdn
|
||||
export BLLIST_MODE="ip"
|
||||
### В случае если из источника получено менее указанного кол-ва записей, то обновления списков не происходит
|
||||
export BLLIST_MIN_ENTRS=30000
|
||||
|
||||
### Лимит IP адресов. При достижении, в конфиг ipset будет добавлена вся подсеть /24 вместо множества IP адресов пренадлежащих этой сети (0 - off)
|
||||
export IP_LIMIT=0
|
||||
### Подсети класса C (/24). IP адреса из этих подсетей не группируются при оптимизации (записи д.б. в виде: 68.183.221. 149.154.162. и пр.). Прим.: OPT_EXCLUDE_NETS="68.183.221. 149.154.162."
|
||||
export OPT_EXCLUDE_NETS=""
|
||||
### Группировать идущие подряд IP адреса в подсетях /24 в диапазоны CIDR
|
||||
export SUMMARIZE_IP=0
|
||||
### Группировать идущие подряд подсети /24 в диапазоны CIDR
|
||||
export SUMMARIZE_CIDR=0
|
||||
### Фильтрация записей блэклиста по шаблонам из файла IP_FILTER_FILE. Записи (IP, CIDR) попадающие под шаблоны исключаются из кофига ipset (0 - off, 1 - on)
|
||||
export IP_FILTER=0
|
||||
### Файл с шаблонами IP для опции FQDN_FILTER (каждый шаблон в отдельной строке. # в первом символе строки - комментирует строку)
|
||||
export IP_FILTER_FILE="${CONFIG_DIR}/ip_filter"
|
||||
|
||||
### Лимит для субдоменов. При достижении, в конфиг dnsmasq будет добавлен весь домен 2-го ур-ня вместо множества субдоменов (0 - off)
|
||||
export SD_LIMIT=16
|
||||
### SLD не подлежащие оптимизации (через пробел)
|
||||
export OPT_EXCLUDE_SLD="livejournal.com facebook.com vk.com blog.jp msk.ru net.ru org.ru net.ua com.ua org.ua co.uk amazonaws.com"
|
||||
### Не оптимизировать SLD попадающие под выражения (через пробел) ("[.][a-z]{2,3}[.][a-z]{2}$")
|
||||
export OPT_EXCLUDE_MASKS=""
|
||||
### Фильтрация записей блэклиста по шаблонам из файла ENTRIES_FILTER_FILE. Записи (FQDN) попадающие под шаблоны исключаются из кофига dnsmasq (0 - off, 1 - on)
|
||||
export FQDN_FILTER=0
|
||||
### Файл с шаблонами FQDN для опции FQDN_FILTER (каждый шаблон в отдельной строке. # в первом символе строки - комментирует строку)
|
||||
export FQDN_FILTER_FILE="${CONFIG_DIR}/fqdn_filter"
|
||||
### Обрезка www[0-9]. в FQDN (0 - off, 1 - on)
|
||||
export STRIP_WWW=1
|
||||
### Преобразование кириллических доменов в punycode (0 - off, 1 - on)
|
||||
export USE_IDN=0
|
||||
### Перенаправлять DNS-запросы на альтернативный DNS-сервер для заблокированных FQDN (0 - off, 1 - on)
|
||||
export ALT_NSLOOKUP=0
|
||||
### Альтернативный DNS-сервер
|
||||
export ALT_DNS_ADDR="8.8.8.8"
|
||||
|
||||
### Источники блэклиста
|
||||
export RBL_ALL_URL="https://api.reserve-rbl.ru/api/v2/current/csv"
|
||||
export RBL_IP_URL="https://api.reserve-rbl.ru/api/v2/ips/csv"
|
||||
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 RBL_ENCODING=""
|
||||
export ZI_ENCODING="CP1251"
|
||||
export AF_ENCODING=""
|
||||
|
||||
############################ Configuration #############################
|
||||
|
||||
[ -f "$CONFIG_FILE" ] && . "$CONFIG_FILE"
|
||||
|
||||
CONFIG_SCRIPT="${CONFIG_DIR}/scripts/config_script"
|
||||
START_SCRIPT="${CONFIG_DIR}/scripts/start_script"
|
||||
STOP_SCRIPT="${CONFIG_DIR}/scripts/stop_script"
|
||||
|
||||
[ -f "$CONFIG_SCRIPT" ] && . "$CONFIG_SCRIPT"
|
||||
|
||||
AWK_CMD="awk"
|
||||
IPSET_CMD=`which ipset`
|
||||
if [ $? -ne 0 ]; then
|
||||
echo " Error! Ipset doesn't exists" >&2
|
||||
exit 1
|
||||
fi
|
||||
LOGGER_CMD=`which logger`
|
||||
if [ $USE_LOGGER = "1" -a $? -ne 0 ]; then
|
||||
echo " Logger doesn't exists" >&2
|
||||
USE_LOGGER=0
|
||||
fi
|
||||
LOGGER_PARAMS="-t `basename $0`[${$}] -p user.notice"
|
||||
export DNSMASQ_DATA_FILE="${DATA_DIR}/${NAME}.dnsmasq"
|
||||
export IP_DATA_FILE="${DATA_DIR}/${NAME}.ip"
|
||||
export IPSET_IP="${NAME}-ip"
|
||||
export IPSET_IP_TMP="${IPSET_IP}-tmp"
|
||||
export IPSET_CIDR="${NAME}-cidr"
|
||||
export IPSET_CIDR_TMP="${IPSET_CIDR}-tmp"
|
||||
export IPSET_DNSMASQ="${NAME}-dnsmasq"
|
||||
export IPSET_ONION="onion"
|
||||
export IPSET_TOTAL_PROXY="total-proxy"
|
||||
export UPDATE_STATUS_FILE="${DATA_DIR}/update_status"
|
||||
UPDATE_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"
|
||||
IPT_FUNCTIONS="${CONFIG_DIR}/scripts/ipt_functions"
|
||||
INFO_OUTPUT_FUNCTION="${CONFIG_DIR}/scripts/info_output"
|
||||
|
||||
######################### External functions ###########################
|
||||
|
||||
. "$IPT_FUNCTIONS"
|
||||
if [ -f "$INFO_OUTPUT_FUNCTION" ]; then
|
||||
. "$INFO_OUTPUT_FUNCTION"
|
||||
else
|
||||
HTML_INFO=0
|
||||
fi
|
||||
|
||||
############################## Functions ###############################
|
||||
|
||||
Help() {
|
||||
cat << EOF
|
||||
Usage: `basename $0` start|force-start|stop|destroy|restart|update|force-update|data-files|total-proxy-on|total-proxy-off|renew-ipt|status|status-html|--help
|
||||
start : Start
|
||||
force-start :
|
||||
stop : Stop
|
||||
destroy : Stop + destroy ipsets and clear all data files
|
||||
restart : Restart
|
||||
reload : Renew iptables configuration
|
||||
update : Update blacklist
|
||||
force-update : Force update blacklist
|
||||
data-files : Create ${IP_DATA_FILE} & ${DNSMASQ_DATA_FILE} (without network functions)
|
||||
total-proxy-on : Turn on total-proxy mode
|
||||
total-proxy-off : Turn off total-proxy mode
|
||||
total-proxy-status : total-proxy status
|
||||
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 : This message
|
||||
Examples:
|
||||
`basename $0` start
|
||||
`basename $0` force-start
|
||||
`basename $0` stop
|
||||
`basename $0` destroy
|
||||
`basename $0` restart
|
||||
`basename $0` update
|
||||
`basename $0` force-update
|
||||
`basename $0` data-files
|
||||
`basename $0` total-proxy-on
|
||||
`basename $0` total-proxy-off
|
||||
`basename $0` total-proxy-status
|
||||
`basename $0` status
|
||||
`basename $0` html-info
|
||||
EOF
|
||||
}
|
||||
|
||||
MakeLogRecord() {
|
||||
[ $USE_LOGGER = "1" ] && $LOGGER_CMD $LOGGER_PARAMS $1
|
||||
}
|
||||
|
||||
DnsmasqRestart() {
|
||||
eval `echo "$DNSMASQ_RESTART_CMD"`
|
||||
}
|
||||
|
||||
IsIpsetExists() {
|
||||
$IPSET_CMD list "$1" -terse &> /dev/null
|
||||
return $?
|
||||
}
|
||||
|
||||
FlushIpSets() {
|
||||
local _set
|
||||
for _set in "$@"
|
||||
do
|
||||
IsIpsetExists "$_set" && $IPSET_CMD flush "$_set"
|
||||
done
|
||||
}
|
||||
|
||||
DestroyIpsets() {
|
||||
local _set
|
||||
for _set in "$@"
|
||||
do
|
||||
IsIpsetExists "$_set" && $IPSET_CMD destroy "$_set"
|
||||
done
|
||||
}
|
||||
|
||||
FillTotalProxySet() {
|
||||
local _entry
|
||||
for _entry in $TOTAL_PROXY_EXCLUDE_NETS
|
||||
do
|
||||
$IPSET_CMD add "$IPSET_TOTAL_PROXY" "$_entry"
|
||||
done
|
||||
}
|
||||
|
||||
TotalProxyOn() {
|
||||
if [ "$PROXY_LOCAL_CLIENTS" != "1" ]; then
|
||||
IptTotalProxyDel &> /dev/null
|
||||
IptTotalProxyAdd
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " ${IPSET_TOTAL_PROXY} enabled"
|
||||
MakeLogRecord "${IPSET_TOTAL_PROXY} enabled"
|
||||
fi
|
||||
MakeToken
|
||||
fi
|
||||
}
|
||||
|
||||
TotalProxyOff() {
|
||||
if [ "$PROXY_LOCAL_CLIENTS" != "1" ]; then
|
||||
IptTotalProxyDel &> /dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
echo " ${IPSET_TOTAL_PROXY} is already disabled" >&2
|
||||
else
|
||||
echo " ${IPSET_TOTAL_PROXY} disabled"
|
||||
MakeLogRecord "${IPSET_TOTAL_PROXY} disabled"
|
||||
fi
|
||||
MakeToken
|
||||
fi
|
||||
}
|
||||
|
||||
TotalProxyStatus() {
|
||||
IptTotalProxyStatus
|
||||
return $?
|
||||
}
|
||||
|
||||
AddIptRules() {
|
||||
IptMainAdd
|
||||
if [ "$PROXY_LOCAL_CLIENTS" = "1" ]; then
|
||||
IptLocalClientsAdd
|
||||
fi
|
||||
if [ "$DEF_TOTAL_PROXY" = "1" ]; then
|
||||
TotalProxyOn
|
||||
fi
|
||||
}
|
||||
|
||||
DelIptRules() {
|
||||
IptLocalClientsDel
|
||||
IptMainDel
|
||||
}
|
||||
|
||||
SetNetConfig() {
|
||||
local _set
|
||||
for _set in "$IPSET_TOTAL_PROXY" "$IPSET_CIDR_TMP" "$IPSET_CIDR"
|
||||
do
|
||||
IsIpsetExists "$_set" || $IPSET_CMD create "$_set" hash:net maxelem $IPSET_MAXELEM
|
||||
done
|
||||
for _set in "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_ONION"
|
||||
do
|
||||
IsIpsetExists "$_set" || $IPSET_CMD create "$_set" hash:ip maxelem $IPSET_MAXELEM
|
||||
done
|
||||
IsIpsetExists "$IPSET_DNSMASQ" || $IPSET_CMD create "$IPSET_DNSMASQ" hash:ip maxelem $IPSET_MAXELEM timeout $IPSET_DNSMASQ_TIMEOUT
|
||||
FillTotalProxySet
|
||||
AddIptRules
|
||||
}
|
||||
|
||||
DropNetConfig() {
|
||||
DelIptRules
|
||||
FlushIpSets "$IPSET_CIDR_TMP" "$IPSET_CIDR" "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_DNSMASQ" "$IPSET_ONION" "$IPSET_TOTAL_PROXY"
|
||||
}
|
||||
|
||||
FillIpsets() {
|
||||
local _set
|
||||
if [ -f "$IP_DATA_FILE" ]; then
|
||||
echo " Filling ipsets..."
|
||||
FlushIpSets "$IPSET_IP_TMP" "$IPSET_CIDR_TMP"
|
||||
IsIpsetExists "$IPSET_IP_TMP" && IsIpsetExists "$IPSET_CIDR_TMP" && IsIpsetExists "$IPSET_IP" && IsIpsetExists "$IPSET_CIDR" &&\
|
||||
cat "$IP_DATA_FILE" | $IPSET_CMD restore && { $IPSET_CMD swap "$IPSET_IP_TMP" "$IPSET_IP"; $IPSET_CMD swap "$IPSET_CIDR_TMP" "$IPSET_CIDR"; }
|
||||
if [ $? -eq 0 ]; then
|
||||
echo " Ok"
|
||||
FlushIpSets "$IPSET_IP_TMP" "$IPSET_CIDR_TMP"
|
||||
else
|
||||
echo " Error! Ipset wasn't updated" >&2
|
||||
MakeLogRecord "Error! Ipset wasn't updated"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
ClearDataFiles() {
|
||||
if [ -d "$DATA_DIR" ]; then
|
||||
printf "" > "$DNSMASQ_DATA_FILE"
|
||||
printf "" > "$IP_DATA_FILE"
|
||||
printf "0 0 0" > "$UPDATE_STATUS_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
CheckStatus() {
|
||||
local _set _ipset_return=0 _return_code=1
|
||||
if [ "$1" = "ipsets" ]; then
|
||||
for _set in "$IPSET_TOTAL_PROXY" "$IPSET_CIDR_TMP" "$IPSET_CIDR" "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_DNSMASQ" "$IPSET_ONION"
|
||||
do
|
||||
IsIpsetExists "$_set"
|
||||
_ipset_return=$?
|
||||
[ $_ipset_return -ne 0 ] && break
|
||||
done
|
||||
fi
|
||||
if IptListChain &> /dev/null && [ $_ipset_return -eq 0 ]; then
|
||||
_return_code=0
|
||||
fi
|
||||
return $_return_code
|
||||
}
|
||||
|
||||
PreStartCheck() {
|
||||
[ -d "$DATA_DIR" ] || mkdir -p "$DATA_DIR"
|
||||
[ "$HTML_INFO" = "1" -a ! -d "$HTML_DIR" ] && mkdir -p "$HTML_DIR"
|
||||
[ -e "$DNSMASQ_DATA_FILE" ] || printf "\n" > "$DNSMASQ_DATA_FILE"
|
||||
}
|
||||
|
||||
AddUserEntries() {
|
||||
if [ "$ADD_USER_ENTRIES" = "1" ]; then
|
||||
if [ -f "$USER_ENTRIES_FILE" -a -s "$USER_ENTRIES_FILE" ]; then
|
||||
$AWK_CMD 'BEGIN {
|
||||
null="";
|
||||
while((getline ip_string <ENVIRON["IP_DATA_FILE"]) > 0) {
|
||||
split(ip_string, ip_string_arr, " ");
|
||||
ip_data_array[ip_string_arr[3]]=null;
|
||||
};
|
||||
close(ENVIRON["IP_DATA_FILE"]);
|
||||
while((getline fqdn_string <ENVIRON["DNSMASQ_DATA_FILE"]) > 0) {
|
||||
split(fqdn_string, fqdn_string_arr, "/");
|
||||
fqdn_data_array[fqdn_string_arr[2]]=null;
|
||||
};
|
||||
close(ENVIRON["DNSMASQ_DATA_FILE"]);
|
||||
}
|
||||
function writeIpsetEntries(val, set) {
|
||||
printf "add %s %s\n", set, val >> ENVIRON["IP_DATA_FILE"];
|
||||
};
|
||||
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 "ipset=/%s/%s\n", val, ENVIRON["IPSET_DNSMASQ"] >> ENVIRON["DNSMASQ_DATA_FILE"];
|
||||
};
|
||||
($0 !~ /^([\040\011]*$|#)/) {
|
||||
if($0 ~ /^[0-9]{1,3}([.][0-9]{1,3}){3}$/ && !($0 in ip_data_array))
|
||||
writeIpsetEntries($0, ENVIRON["IPSET_IP_TMP"]);
|
||||
else if($0 ~ /^[0-9]{1,3}([.][0-9]{1,3}){3}[\057][0-9]{1,2}$/ && !($0 in ip_data_array))
|
||||
writeIpsetEntries($0, ENVIRON["IPSET_CIDR_TMP"]);
|
||||
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})?)?$/ && !($1 in fqdn_data_array))
|
||||
writeDNSData($1, $2);
|
||||
}' "$USER_ENTRIES_FILE"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
GetDataFiles() {
|
||||
local _return_code=1 _attempt=1 _update_string
|
||||
PreStartCheck
|
||||
echo "$$" > "$UPDATE_PID_FILE"
|
||||
if [ -n "$BLLIST_MODULE" ]; then
|
||||
while :
|
||||
do
|
||||
$BLLIST_MODULE
|
||||
_return_code=$?
|
||||
[ $_return_code -eq 0 ] && break
|
||||
echo " Module run attempt ${_attempt}: failed [${BLLIST_MODULE}]"
|
||||
MakeLogRecord "Module run attempt ${_attempt}: failed [${BLLIST_MODULE}]"
|
||||
_attempt=`expr $_attempt + 1`
|
||||
[ $_attempt -gt $MODULE_RUN_ATTEMPTS ] && break
|
||||
sleep $MODULE_RUN_TIMEOUT
|
||||
done
|
||||
AddUserEntries
|
||||
if [ $_return_code -eq 0 ]; then
|
||||
_update_string=`$AWK_CMD '{
|
||||
printf "Received entries: %s\n", (NF < 3) ? "No data" : "IP: "$1", CIDR: "$2", FQDN: "$3;
|
||||
exit;
|
||||
}' "$UPDATE_STATUS_FILE"`
|
||||
echo " ${_update_string}"
|
||||
MakeLogRecord "${_update_string}"
|
||||
printf " `date +%d.%m.%Y-%H:%M`\n" >> "$UPDATE_STATUS_FILE"
|
||||
fi
|
||||
else
|
||||
ClearDataFiles
|
||||
ADD_USER_ENTRIES=1
|
||||
AddUserEntries
|
||||
_return_code=0
|
||||
fi
|
||||
if [ "$PROXY_MODE" = "2" ]; then
|
||||
printf "\n" >> "$DNSMASQ_DATA_FILE"
|
||||
else
|
||||
printf "server=/onion/%s\nipset=/onion/%s\n" "${ONION_DNS_ADDR}" "${IPSET_ONION}" >> "$DNSMASQ_DATA_FILE"
|
||||
fi
|
||||
rm -f "$UPDATE_PID_FILE"
|
||||
return $_return_code
|
||||
}
|
||||
|
||||
MakeToken() {
|
||||
date +%s > "$TOKEN_FILE"
|
||||
}
|
||||
|
||||
Update() {
|
||||
local _return_code=0
|
||||
if CheckStatus ipsets; then
|
||||
:
|
||||
else
|
||||
echo " ${NAME} ${1} - Error! ${NAME} does not running or another error has occurred" >&2
|
||||
return 1
|
||||
fi
|
||||
MakeToken
|
||||
if [ -e "$UPDATE_PID_FILE" ] && [ "$1" != "force-update" ]; then
|
||||
echo " ${NAME} ${1} - Error! Another instance of update is already running" >&2
|
||||
MakeLogRecord "${1} - Error! Another instance of update is already running"
|
||||
_return_code=2
|
||||
else
|
||||
echo " ${NAME} ${1}..."
|
||||
MakeLogRecord "${1}..."
|
||||
if [ "$IPSET_CLEAR_SETS" = "1" ]; then
|
||||
FlushIpSets "$IPSET_IP" "$IPSET_CIDR" "$IPSET_DNSMASQ"
|
||||
fi
|
||||
GetDataFiles
|
||||
case $? in
|
||||
0)
|
||||
echo " Blacklist updated"
|
||||
MakeLogRecord "Blacklist updated"
|
||||
;;
|
||||
2)
|
||||
echo " Error! Blacklist update error" >&2
|
||||
MakeLogRecord "Error! Blacklist update error"
|
||||
_return_code=1
|
||||
;;
|
||||
*)
|
||||
echo " Module error! [${BLLIST_MODULE}]" >&2
|
||||
MakeLogRecord "Module error! [${BLLIST_MODULE}]"
|
||||
_return_code=1
|
||||
;;
|
||||
esac
|
||||
FlushIpSets "$IPSET_DNSMASQ"
|
||||
FillIpsets
|
||||
_return_code=$?
|
||||
DnsmasqRestart
|
||||
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 "${1}..."
|
||||
DropNetConfig &> /dev/null
|
||||
SetNetConfig
|
||||
PreStartCheck
|
||||
FillIpsets
|
||||
_return_code=$?
|
||||
### 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 "${1}..."
|
||||
DropNetConfig &> /dev/null
|
||||
_return_code=$?
|
||||
### 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=`expr $_i + 1`
|
||||
sleep 1
|
||||
done
|
||||
echo " ${NAME} reload..."
|
||||
DelIptRules &> /dev/null
|
||||
AddIptRules &> /dev/null
|
||||
MakeToken
|
||||
}
|
||||
|
||||
Status() {
|
||||
local _set
|
||||
if CheckStatus; then
|
||||
printf "\n \033[1m${NAME} status\033[m: \033[1;32mEnabled\033[m\n\n PROXY_MODE: ${PROXY_MODE}\n DEF_TOTAL_PROXY: ${DEF_TOTAL_PROXY}\n PROXY_LOCAL_CLIENTS: ${PROXY_LOCAL_CLIENTS}\n BLLIST_MODULE: ${BLLIST_MODULE}\n"
|
||||
if [ -f "$UPDATE_STATUS_FILE" ]; then
|
||||
$AWK_CMD '{
|
||||
update_string=(NF < 4) ? "No data" : $4" (IP: "$1" | CIDR: "$2" | FQDN: "$3")";
|
||||
printf "\n Last blacklist update: %s\n", update_string;
|
||||
}' "$UPDATE_STATUS_FILE"
|
||||
else
|
||||
printf "\n Last blacklist update: No data\n"
|
||||
fi
|
||||
if [ "$PROXY_MODE" = "2" ] && ! IptVpnRouteStatus; then
|
||||
printf "\n \033[1;31mVPN ROUTING ERROR! (NEED THE RESTART)\033[m\n"
|
||||
fi
|
||||
printf "\n \033[4mIptables rules\033[m:\n\n"
|
||||
IptListChain | $AWK_CMD '
|
||||
{
|
||||
if(NR > 2) {
|
||||
match_set=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? "\033[1;33m"ENVIRON["IPSET_TOTAL_PROXY"]" (Enabled!)\033[m" : $11;
|
||||
printf " Match-set: %s\n Bytes: %s\n\n", match_set, $2;
|
||||
};
|
||||
}'
|
||||
printf " \033[4mIp sets\033[m:\n\n"
|
||||
for _set in "$IPSET_TOTAL_PROXY" "$IPSET_CIDR_TMP" "$IPSET_CIDR" "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_DNSMASQ" "$IPSET_ONION"
|
||||
do
|
||||
$IPSET_CMD list "$_set" -terse | $AWK_CMD -F ":" '
|
||||
{
|
||||
if($1 ~ /^(Name|Size in memory|Number of entries)/) {
|
||||
printf " %s: %s\n", $1, $2;
|
||||
if($1 ~ /^Number of entries/) printf "\n";
|
||||
};
|
||||
}'
|
||||
done
|
||||
else
|
||||
printf "\n \033[1m${NAME} status\033[m: \033[1mDisabled\033[m\n\n"
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
StatusOutput() {
|
||||
if [ "$HTML_INFO" = "1" -a -d "$HTML_DIR" ]; then
|
||||
Info
|
||||
fi
|
||||
}
|
||||
|
||||
############################ Main section ##############################
|
||||
|
||||
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
|
||||
DestroyIpsets "$IPSET_TOTAL_PROXY" "$IPSET_CIDR_TMP" "$IPSET_CIDR" "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_DNSMASQ" "$IPSET_ONION"
|
||||
ClearDataFiles
|
||||
return_code=$?
|
||||
rm -f "$UPDATE_PID_FILE" "$START_PID_FILE"
|
||||
DnsmasqRestart
|
||||
StatusOutput
|
||||
;;
|
||||
update|force-update)
|
||||
Update "$1"
|
||||
return_code=$?
|
||||
StatusOutput
|
||||
;;
|
||||
data-files)
|
||||
if [ -e "$UPDATE_PID_FILE" ] && [ "$1" != "force-update" ]; then
|
||||
echo " ${NAME} - Error! Another instance of update is already running" >&2
|
||||
exit 2
|
||||
else
|
||||
GetDataFiles
|
||||
return_code=$?
|
||||
fi
|
||||
;;
|
||||
total-proxy-on)
|
||||
if ! CheckStatus; then
|
||||
echo " ${NAME} does not running" >&2
|
||||
return_code=1
|
||||
else
|
||||
TotalProxyOn
|
||||
return_code=$?
|
||||
fi
|
||||
StatusOutput
|
||||
;;
|
||||
total-proxy-off)
|
||||
if ! CheckStatus; then
|
||||
echo " ${NAME} does not running" >&2
|
||||
return_code=1
|
||||
else
|
||||
TotalProxyOff
|
||||
return_code=$?
|
||||
fi
|
||||
StatusOutput
|
||||
;;
|
||||
total-proxy-status)
|
||||
TotalProxyStatus
|
||||
return_code=$?
|
||||
echo $return_code
|
||||
;;
|
||||
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 "$UPDATE_PID_FILE" ]; then
|
||||
return_code=4
|
||||
echo 4
|
||||
else
|
||||
echo 0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
return_code=2
|
||||
echo 2
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
vpn-route-status)
|
||||
IptVpnRouteStatus
|
||||
return_code=$?
|
||||
echo $return_code
|
||||
;;
|
||||
html-info)
|
||||
Info
|
||||
return_code=$?
|
||||
;;
|
||||
-h|--help|help)
|
||||
Help
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
Help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
exit $return_code;
|
||||
@@ -0,0 +1,15 @@
|
||||
User admin
|
||||
PidFile /var/run/tor.pid
|
||||
DataDirectory /var/lib/tor
|
||||
VirtualAddrNetwork 10.254.0.0/16
|
||||
AutomapHostsOnResolve 1
|
||||
TransPort 192.168.1.1:9040
|
||||
TransPort 127.0.0.1:9040
|
||||
DNSPort 9053
|
||||
DNSListenAddress 127.0.0.1
|
||||
#SOCKSPort 192.168.1.1:9050 # Tor socks-proxy
|
||||
GeoIPFile /usr/share/tor/geoip
|
||||
GeoIPv6File /usr/share/tor/geoip6
|
||||
ExcludeExitNodes {RU},{UA},{BY},{KZ},{MD},{TM},{UZ},{AM},{KG}
|
||||
ExitPolicy reject *:*
|
||||
ExitPolicy reject6 *:*
|
||||
Binary file not shown.
Reference in New Issue
Block a user