From 11f83e3ddda037d946f8c2bcdbcd58443539be1d Mon Sep 17 00:00:00 2001 From: gSpot Date: Sun, 20 Jan 2019 04:54:52 +0300 Subject: [PATCH] fixes & improvements --- opt/etc/tor/torrc | 6 +- opt/usr/bin/ruab.az-rbl.all.lua | 507 ++++++++++++++++++++------------ opt/usr/bin/ruab.az-rbl.all.sh | 159 +++++----- opt/usr/bin/ruab.az.fqdn.sh | 76 ++--- opt/usr/bin/ruantiblock.sh | 378 +++++++++--------------- 5 files changed, 585 insertions(+), 541 deletions(-) diff --git a/opt/etc/tor/torrc b/opt/etc/tor/torrc index e876b6a..ebd916f 100644 --- a/opt/etc/tor/torrc +++ b/opt/etc/tor/torrc @@ -8,4 +8,8 @@ TransPort 127.0.0.1:9040 DNSPort 9053 DNSListenAddress 127.0.0.1 #SOCKSPort 192.168.1.1:9050 # Адрес tor socks proxy в LAN для подключения браузера и пр. -ExcludeExitNodes {ru} # Исключение российских исходящих нодов +GeoIPFile /opt/share/tor/geoip +GeoIPv6File /opt/share/tor/geoip6 +ExcludeExitNodes {RU},{UA},{BY},{KZ},{MD},{TM},{UZ},{AM},{KG} +ExitPolicy reject *:* +ExitPolicy reject6 *:* diff --git a/opt/usr/bin/ruab.az-rbl.all.lua b/opt/usr/bin/ruab.az-rbl.all.lua index fc979f4..01801a5 100755 --- a/opt/usr/bin/ruab.az-rbl.all.lua +++ b/opt/usr/bin/ruab.az-rbl.all.lua @@ -5,21 +5,31 @@ http://api.antizapret.info/all.php?type=csv http://api.antizapret.info/group.php?data=ip http://api.antizapret.info/group.php?data=domain - http://reestr.rublacklist.net/api/current + http://api.reserve-rbl.ru/api/current - Исходный скрипт из статьи: https://habrahabr.ru/post/270657 --]] -local config = { - dataDir = "/opt/var/ruantiblock", - wgetCmd = "wget -q -O -", - blSource = "antizapret", -- antizapret или rublacklist - blockMode = "hybrid", -- ip, fqdn, hybrid - groupBySld = 16, -- количество поддоменов после которого в список вносится весь домен второго уровня целиком - excludeEntries = { -- записи, исключаемые из итоговых файлов (ip, FQDN, CIDR) - ["youtube.com"] = true +local NAME = "ruantiblock" + +------------------------------ Settings -------------------------------- + +local Config = { + -- Тип обновления списка блокировок (antizapret, rublacklist) + BL_UPDATE_MODE = "antizapret", + -- Режим обхода блокировок: ip (если провайдер блокирует по ip), hybrid (если провайдер использует DPI, подмену DNS и пр.), fqdn (если провайдер использует DPI, подмену DNS и пр.) + BLOCK_MODE = "hybrid", + -- Перенаправлять DNS-запросы на альтернативный DNS-сервер для заблокированных FQDN (или в tor если провайдер блокирует сторонние DNS-серверы) (0 - off, 1 - on) + ALT_NSLOOKUP = 1, + -- Альтернативный DNS-сервер ($ONION_DNS_ADDR в ruantiblock.sh (tor), 8.8.8.8 и др.). Если провайдер не блокирует сторонние DNS-запросы, то оптимальнее будет использовать для заблокированных сайтов, например, 8.8.8.8, а не резолвить через tor... + ALT_DNS_ADDR = "8.8.8.8", + -- Преобразование кириллических доменов в punycode (0 - off, 1 - on) + USE_IDN = 0, + -- Записи (ip, CIDR, FQDN) исключаемые из списка блокировки + EXCLUDE_ENTRIES = { + ["youtube.com"] = true, }, - neverGroupSld = { + -- SLD не подлежащие оптимизации + OPT_EXCLUDE_SLD = { ["livejournal.com"] = true, ["facebook.com"] = true, ["vk.com"] = true, @@ -33,38 +43,48 @@ local config = { ["co.uk"] = true, ["amazonaws.com"] = true, }, - neverGroupMasks = {}, -- { "^%a%a%a?.%a%a$" }, -- не распространять на org.ru, net.ua и аналогичные - stripWww = true, - idnCmd = "idn", -- внешняя утилита idn - convertIdn = false, - idnType = "standalone", -- standalone или lua. Тип idn: внешняя утилита или библиотека lua-idn - altNsLookup = true, -- отправлять DNS запросы заблокированных доменов через альтернативный DNS - blMinimumEntries = 1000, -- костыль если список получился короче, значит что-то пошло не так и конфиги не обновляем - ipsetDns = "ruantiblock-dnsmasq", - ipsetIp = "ruantiblock-ip-tmp", - ipsetCidr = "ruantiblock-cidr-tmp", - altDnsAddr = "8.8.8.8", - bllistUrl1_0="http://api.antizapret.info/all.php?type=csv", - bllistUrl1_1="http://api.antizapret.info/group.php?data=ip", - bllistUrl1_2="http://api.antizapret.info/group.php?data=domain", - bllistUrl2="http://reestr.rublacklist.net/api/current", - --bllistUrl2="http://api.reserve-rbl.ru/api/current", -} -config.dnsmasqConfigPath = config.dataDir .. "/ruantiblock.dnsmasq" -config.ipsetConfigPath = config.dataDir .. "/ruantiblock.ip" -config.updateStatusPath = config.dataDir .. "/update_status" - -local ipPattern = "%d+%.%d+%.%d+%.%d+" -local cidrPattern = ipPattern .. "[/]%d[%d]?" -local fqdnPattern = "[a-z0-9_%-%.]-[a-z0-9_%-]+%.[a-z0-9%-%.]" -local blTables = { - ["ipCount"] = 0, - ["cidrCount"] = 0, - ["fqdn"] = {}, - ["sdCount"] = {}, - ["ip"] = {}, - ["cidr"] = {}, + -- Не оптимизировать SLD попадающие под выражения из таблицы + OPT_EXCLUDE_MASKS = {}, -- { "^%a%a%a?.%a%a$" }, + -- Лимит для субдоменов. При достижении, в конфиг dnsmasq будет добавлен весь домен 2-го ур-ня вместо множества субдоменов + SD_LIMIT = 16, + -- В случае если из источника получено менее указанного кол-ва записей, то обновления списков не происходит + BLLIST_MIN_ENTRS = 1000, + -- Обрезка www[0-9]. в FQDN (0 - off, 1 - on) + STRIP_WWW = 1, + -- Тип idn: внешняя утилита или библиотека lua-idn (standalone, lua) + IDN_TYPE = "standalone", + -- Внешняя утилита idn + IDN_CMD = "idn", + WGET_CMD = "wget -q -O -", + DATA_DIR = "/opt/var/" .. NAME, + IPSET_DNSMASQ = NAME .. "-dnsmasq", + IPSET_IP = NAME .. "-ip-tmp", + IPSET_CIDR = NAME .. "-cidr-tmp", + -- Источники блэклиста + AZ_ALL_URL = "https://api.antizapret.info/all.php?type=csv", + AZ_IP_URL = "http://api.antizapret.info/group.php?data=ip", + AZ_FQDN_URL = "http://api.antizapret.info/group.php?data=domain", + --RBL_ALL_URL = "http://reestr.rublacklist.net/api/current", + RBL_ALL_URL = "https://api.reserve-rbl.ru/api/current", + httpSendHeadersTable = { + --["User-Agent"] = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.5) Gecko/20100101 Firefox/52.5", + }, } +Config.__index = Config +Config.__call = true +Config.DNSMASQ_DATA_FILE = Config.DATA_DIR .. "/" .. NAME .. ".dnsmasq" +Config.IP_DATA_FILE = Config.DATA_DIR .. "/" .. NAME .. ".ip" +Config.UPDATE_STATUS_FILE = Config.DATA_DIR .. "/update_status" + +local function remapBool(val) + return (val ~= 0 and val ~= false and val ~= nil) and true or false +end + +Config.ALT_NSLOOKUP = remapBool(Config.ALT_NSLOOKUP) +Config.USE_IDN = remapBool(Config.USE_IDN) +Config.STRIP_WWW = remapBool(Config.STRIP_WWW) + +-- Import packages local function prequire(package) local result = pcall(require, package) @@ -75,8 +95,8 @@ local function prequire(package) end local idn = prequire("idn") -if config.convertIdn and config.idnType == "lua" and not idn then - error("You need to install idn.lua (github.com/haste/lua-idn) or use standalone idn tool... Otherwise 'config.convertIdn' must be set to 'false'") +if Config.USE_IDN and Config.IDN_TYPE == "lua" and not idn then + error("You need to install idn.lua (github.com/haste/lua-idn) or use standalone idn tool... Otherwise 'Config.USE_IDN' must be set to 'false'") end local http = prequire("socket.http") @@ -85,148 +105,113 @@ if not ltn12 then error("You need to install ltn12...") end -local function hex2unicode(code) - local n = tonumber(code, 16) - if n < 128 then - return string.char(n) - elseif n < 2048 then - return string.char(192 + ((n - (n % 64)) / 64), 128 + (n % 64)) +------------------------------ Classes -------------------------------- + +-- Constructor + +function Class(super, t) + local function instanceConstructor(cls, t) + local instance = t or {} + setmetatable(instance, cls) + instance.__class = cls + return instance + end + local class = t or {} + if not super then + local mt = {__call = instanceConstructor} + mt.__index = mt + setmetatable(class, mt) + elseif type(super) == "table" and super.__index and super.__call then + setmetatable(class, super) + class.__super = super else - return string.char(224 + ((n - (n % 4096)) / 4096), 128 + (((n % 4096) - (n % 64)) / 64), 128 + (n % 64)) + error("Argument error! Incorrect object of a 'super'") end + class.__index = class + class.__call = instanceConstructor + return class end -local function convertToPunycode(input) +-- Mixin class + +local BlackListParser = Class(Config, { + ipPattern = "%d+%.%d+%.%d+%.%d+", + cidrPattern = "%d+%.%d+%.%d+%.%d+/%d%d?", + fqdnPattern = "[a-z0-9_%.%-]-[a-z0-9_%-]+%.[a-z0-9%.%-]", + url = "http://127.0.0.1", + recordsSeparator = "\n", +}) + +function BlackListParser:new(t) + -- extended instance constructor + local instance = self(t) + instance.url = instance["url"] or self.url + instance.recordsSeparator = instance["recordsSeparator"] or self.recordsSeparator + instance.ipCount = 0 + instance.cidrCount = 0 + instance.fqdn = {} + instance.sdCount = {} + instance.ip = {} + instance.cidr = {} + return instance +end + +function BlackListParser:convertToPunycode(input) local output - if config["idnType"] == "lua" and idn then + if self.IDN_TYPE == "lua" and idn then output = idn.encode(input) - elseif config["idnType"] == "standalone" then - local idnHandler = assert(io.popen(config.idnCmd .. " \"" .. input .. "\"", "r")) + elseif self.IDN_TYPE == "standalone" then + local idnHandler = assert(io.popen(self.IDN_CMD .. " \"" .. input .. "\"", "r")) output = idnHandler:read("*l") idnHandler:close() else - error("idnType should be either 'lua' or 'standalone'") + error("Config.IDN_TYPE should be either 'lua' or 'standalone'") end return (output) end -local function chunkBuffer(recordsSeparator) - local recordsSeparator = recordsSeparator - local buff = "" - local retValue = "" - local lastChunk - return function(chunk) - if lastChunk then - return nil - end - if chunk then - buff = buff .. chunk - local lastRsPosition = select(2, buff:find("^.*" .. recordsSeparator)) - if lastRsPosition then - retValue = buff:sub(1, lastRsPosition) - buff = buff:sub((lastRsPosition + 1), -1) - else - retValue = "" - end - else - retValue = buff - lastChunk = true - end - return (retValue) - end -end - -local function fillIpTable(val, tname) - if not config.excludeEntries[val] and not blTables[tname][val] then - blTables[tname][val] = true +function BlackListParser:fillIpTable(val, tname) + if not self.EXCLUDE_ENTRIES[val] and not self[tname][val] then + self[tname][val] = true local counter = tname .. "Count" - blTables[counter] = blTables[counter] + 1 + self[counter] = self[counter] + 1 end end -local function fillDomainTables(val) - if config["stripWww"] then val = val:gsub("^www%.", "") end - local subDomain, secondLevelDomain = val:match("^([a-z0-9_%-%.]-)([a-z0-9_%-]+%.[a-z0-9%-]+)$") +function BlackListParser:fillDomainTables(val) + if self.STRIP_WWW then val = val:gsub("^www[0-9]?%.", "") end + local subDomain, secondLevelDomain = val:match("^([a-z0-9_%.%-]-)([a-z0-9_%-]+%.[a-z0-9%-]+)$") if secondLevelDomain then - blTables.fqdn[val] = secondLevelDomain - blTables.sdCount[secondLevelDomain] = (blTables.sdCount[secondLevelDomain] or 0) + 1 + self.fqdn[val] = secondLevelDomain + self.sdCount[secondLevelDomain] = (self.sdCount[secondLevelDomain] or 0) + 1 end end -local function parseIpString(val) +function BlackListParser:parseIpString(val) if val and val ~= "" then - for ipEntry in val:gmatch("[0-9%./]+") do - if ipEntry:match("^" .. ipPattern .. "$") then - fillIpTable(ipEntry, "ip") - elseif ipEntry:match("^" .. cidrPattern .. "$") then - fillIpTable(ipEntry, "cidr") - end - end - end -end - -local function ipSink(chunk) - if chunk and chunk ~= "" then - local ipStringPattern = (config.blSource == "antizapret") and "(.-)\n" or "([^;]+);[^;]-;[^;]-;[^;]-;[^;]-;[^;]-\\n" - for ipString in chunk:gmatch(ipStringPattern) do - parseIpString(ipString) - end - end - return true -end - -local function azFqdnSink(chunk) - if chunk and chunk ~= "" then - --chunk = chunk:gsub("&", "") - local entryPattern = (config.blockMode == "fqdn") and "((.-))\n" or "[^;]+;[^;]-;([^;]-);([^;]+)\n" - for fqdnStr, ipStr in chunk:gmatch(entryPattern) do - if #fqdnStr > 0 and not fqdnStr:match("^" .. ipPattern .. "$") then - fqdnStr = fqdnStr:gsub("*%.", ""):gsub("%.$", ""):lower() - if fqdnStr:match("^" .. fqdnPattern .. "+$") then - fillDomainTables(fqdnStr) - elseif config["convertIdn"] and fqdnStr:match("^[^\\/&%?]-[^\\/&%?%.]+%.[^\\/&%?%.]+%.?$") then - fqdnStr = convertToPunycode(fqdnStr) - fillDomainTables(fqdnStr) - end - elseif (config.blockMode == "hybrid" or fqdnStr:match("^" .. ipPattern .. "$")) and #ipStr > 0 then - parseIpString(ipStr) + for ipEntry in val:gmatch("[0-9][0-9%./]+[0-9]") do + if ipEntry:match("^" .. self.ipPattern .. "$") then + self:fillIpTable(ipEntry, "ip") + elseif ipEntry:match("^" .. self.cidrPattern .. "$") then + self:fillIpTable(ipEntry, "cidr") end end end - return true end -local function rblFqdnSink(chunk) - if chunk and chunk ~= "" then - --chunk = chunk:gsub("&", "") - for ipStr, fqdnStr in chunk:gmatch("([^;]+);([^;]-);[^;]-;[^;]-;[^;]-;[^;]-\\n") do - if #fqdnStr > 0 and not fqdnStr:match("^" .. ipPattern .. "$") then - fqdnStr = fqdnStr:gsub("*%.", ""):gsub("%.$", ""):lower() - if config["convertIdn"] then - fqdnStr = fqdnStr:gsub("\\u(%x%x%x%x)", function(s) return convertToPunycode(hex2unicode(s)) end) - end - if fqdnStr:match("^" .. fqdnPattern .. "+$") then - fillDomainTables(fqdnStr) - end - elseif (config.blockMode == "hybrid" or fqdnStr:match("^" .. ipPattern .. "$")) and #ipStr > 0 then - parseIpString(ipStr) - end - end - end - return true +function BlackListParser:sink() + -- Must be reload in the subclass + error("Method BlackListParser:sink() must be reload in the subclass!") end -local function compactDomainList(fqdnList, subdomainsCount) +function BlackListParser:compactDomainList(fqdnList, sdCountList) local domainTable = {} local numEntries = 0 - if config.groupBySld and config.groupBySld > 0 then - for sld in pairs(subdomainsCount) do - if config.neverGroupSld[sld] then - subdomainsCount[sld] = 0 - end - for _, pattern in ipairs(config.neverGroupMasks) do + if self.OPT_EXCLUDE_MASKS and #self.OPT_EXCLUDE_MASKS > 0 then + for sld in pairs(sdCountList) do + for _, pattern in ipairs(self.OPT_EXCLUDE_MASKS) do if sld:find(pattern) then - subdomainsCount[sld] = 0 + sdCountList[sld] = 0 break end end @@ -234,8 +219,8 @@ local function compactDomainList(fqdnList, subdomainsCount) end for fqdn, sld in pairs(fqdnList) do if not fqdnList[sld] or fqdn == sld then - local keyValue = ((config.groupBySld and config.groupBySld > 0) and subdomainsCount[sld] > config.groupBySld) and sld or fqdn - if not config.excludeEntries[keyValue] and not domainTable[keyValue] then + local keyValue = ((self.SD_LIMIT and self.SD_LIMIT > 0 and not self.OPT_EXCLUDE_SLD[sld]) and sdCountList[sld] >= self.SD_LIMIT) and sld or fqdn + if not self.EXCLUDE_ENTRIES[keyValue] and not domainTable[keyValue] then domainTable[keyValue] = true numEntries = numEntries + 1 end @@ -244,18 +229,18 @@ local function compactDomainList(fqdnList, subdomainsCount) return domainTable, numEntries end -local function generateDnsmasqConfig(configPath, domainList) +function BlackListParser:generateDnsmasqConfig(configPath, domainList) local configFile = assert(io.open(configPath, "w"), "Could not open dnsmasq config") for fqdn in pairs(domainList) do - if config.altNsLookup then - configFile:write(string.format("server=/%s/%s\n", fqdn, config.altDnsAddr)) + if self.ALT_NSLOOKUP then + configFile:write(string.format("server=/%s/%s\n", fqdn, self.ALT_DNS_ADDR)) end - configFile:write(string.format("ipset=/%s/%s\n", fqdn, config.ipsetDns)) + configFile:write(string.format("ipset=/%s/%s\n", fqdn, self.IPSET_DNSMASQ)) end configFile:close() end -local function generateIpsetConfig(configPath, t) +function BlackListParser:generateIpsetConfig(configPath, t) local configFile = assert(io.open(configPath, "w"), "Could not open ipset config") for k, v in pairs(t) do for ipaddr in pairs(v) do @@ -265,47 +250,185 @@ local function generateIpsetConfig(configPath, t) configFile:close() end -local retVal, retCode, url, rs, sink +function BlackListParser:chunkBuffer() + local buff = "" + local retValue = "" + local lastChunk + return function(chunk) + if lastChunk then + return nil + end + if chunk then + buff = buff .. chunk + local lastRsPosition = select(2, buff:find("^.*" .. self.recordsSeparator)) + if lastRsPosition then + retValue = buff:sub(1, lastRsPosition) + buff = buff:sub((lastRsPosition + 1), -1) + else + retValue = "" + end + else + retValue = buff + lastChunk = true + end + return (retValue) + end +end + +function BlackListParser:getHttpData(url) + local retVal + local output = ltn12.sink.chain(self:chunkBuffer(), self:sink()) + if http then + retVal, retCode, retHeaders = http.request{ url = url, sink = output, headers = self.httpSendHeadersTable } + --[[ + for k, v in pairs(retHeaders) do + print(k, v) + end + --]] + if not retVal or retCode ~= 200 then + retVal = nil + print(string.format("Connection error! (%s) URL: %s", retCode, url)) + end + else + retVal = ltn12.pump.all(ltn12.source.file(io.popen(self.WGET_CMD .. " \"" .. url .. "\"", "r")), output) + end + return (retVal == 1) and true or false +end -if config.blSource == "antizapret" then - sink = (config.blockMode == "fqdn" or config.blockMode == "hybrid") and azFqdnSink or ipSink - url = (config.blockMode == "fqdn") and config.bllistUrl1_2 or ((config.blockMode == "hybrid") and config.bllistUrl1_0 or config.bllistUrl1_1) - rs = "\n" -elseif config.blSource == "rublacklist" then - sink = (config.blockMode == "fqdn" or config.blockMode == "hybrid") and rblFqdnSink or ipSink - url = config.bllistUrl2 - rs = "\\n" -else - error("Blacklist source should be either 'rublacklist' or 'antizapret'") +function BlackListParser:run() + local domainTable + local recordsNum = 0 + local returnCode = 0 + if self:getHttpData(self.url) then + domainTable, recordsNum = self:compactDomainList(self.fqdn, self.sdCount) + if (recordsNum + self.ipCount + self.cidrCount) > self.BLLIST_MIN_ENTRS then + self:generateDnsmasqConfig(self.DNSMASQ_DATA_FILE, domainTable) + self:generateIpsetConfig(self.IP_DATA_FILE, { [self.IPSET_IP] = self.ip, [self.IPSET_CIDR] = self.cidr }) + returnCode = 0 + else + returnCode = 2 + end + else + returnCode = 1 + end + -- update_status + local updateStatusFile = assert(io.open(self.UPDATE_STATUS_FILE, "w"), "Could not open 'update_status' file") + updateStatusFile:write(string.format("%d %d %d", self.ipCount, self.cidrCount, recordsNum)) + updateStatusFile:close() + return returnCode end -local output = ltn12.sink.chain(chunkBuffer(rs), sink) +-- Subclasses -if http then - retVal, retCode = http.request{ url = url, sink = output } -else - retVal, retCode = ltn12.pump.all(ltn12.source.file(io.popen(config.wgetCmd .. " " .. url, "r")), output) +function ipSink(self) + return function(chunk) + if chunk and chunk ~= "" then + for ipString in chunk:gmatch(self.ipStringPattern) do + self:parseIpString(ipString) + end + end + return true + end end -local domainTable, recordsNum -local returnCode = 0 + -- antizapret.info -if retVal == 1 and (retCode == 200 or not http) then - domainTable, recordsNum = compactDomainList(blTables.fqdn, blTables.sdCount) - if (recordsNum + blTables.ipCount + blTables.cidrCount) > config.blMinimumEntries then - generateDnsmasqConfig(config.dnsmasqConfigPath, domainTable) - generateIpsetConfig(config.ipsetConfigPath, { [config.ipsetIp] = blTables.ip, [config.ipsetCidr] = blTables.cidr }) - returnCode = 0 +local Az = Class(BlackListParser, { + url = Config.AZ_ALL_URL, + recordsSeparator = "\n", + ipStringPattern = "(.-)\n", +}) + +function Az:sink() + local entryPattern = (self.BLOCK_MODE == "fqdn") and "((.-))" .. self.recordsSeparator or ";([^;]-);([^;]-)" .. self.recordsSeparator + return function(chunk) + if chunk and chunk ~= "" then + for fqdnStr, ipStr in chunk:gmatch(entryPattern) do + if #fqdnStr > 0 and not fqdnStr:match("^" .. self.ipPattern .. "$") then + fqdnStr = fqdnStr:gsub("*%.", ""):gsub("%.$", ""):lower() + if fqdnStr:match("^" .. self.fqdnPattern .. "+$") then + self:fillDomainTables(fqdnStr) + elseif self.USE_IDN and fqdnStr:match("^[^\\/&%?]-[^\\/&%?%.]+%.[^\\/&%?%.]+%.?$") then + fqdnStr = self:convertToPunycode(fqdnStr) + self:fillDomainTables(fqdnStr) + end + elseif (self.BLOCK_MODE == "hybrid" or fqdnStr:match("^" .. self.ipPattern .. "$")) and #ipStr > 0 then + self:parseIpString(ipStr) + end + end + end + return true + end +end + +local AzFqdn = Class(Az, { + url = Config.AZ_FQDN_URL +}) + +local AzIp = Class(Az, { + url = Config.AZ_IP_URL, + sink = ipSink, +}) + + -- rublacklist.net + +local Rbl = Class(BlackListParser, { + url = Config.RBL_ALL_URL, + recordsSeparator = "\\n", + ipStringPattern = "([0-9%./ |]+);.-\\n", + unicodeHexPattern = "\\u(%x%x%x%x)", +}) + +function Rbl:hex2unicode(code) + local n = tonumber(code, 16) + if n < 128 then + return string.char(n) + elseif n < 2048 then + return string.char(192 + ((n - (n % 64)) / 64), 128 + (n % 64)) else - returnCode = 2 + return string.char(224 + ((n - (n % 4096)) / 4096), 128 + (((n % 4096) - (n % 64)) / 64), 128 + (n % 64)) + end +end + +function Rbl:sink() + return function(chunk) + if chunk and chunk ~= "" then + for ipStr, fqdnStr in chunk:gmatch("([^;]-);([^;]-);.-" .. self.recordsSeparator) do + if #fqdnStr > 0 and not fqdnStr:match("^" .. self.ipPattern .. "$") then + fqdnStr = fqdnStr:gsub("*%.", ""):gsub("%.$", ""):lower() + if self.USE_IDN and fqdnStr:match(self.unicodeHexPattern) then + fqdnStr = self:convertToPunycode(fqdnStr:gsub(self.unicodeHexPattern, function(s) return self:hex2unicode(s) end)) + end + if fqdnStr:match("^" .. self.fqdnPattern .. "+$") then + self:fillDomainTables(fqdnStr) + end + elseif (self.BLOCK_MODE == "hybrid" or fqdnStr:match("^" .. self.ipPattern .. "$")) and #ipStr > 0 then + self:parseIpString(ipStr) + end + end + end + return true end -else - returnCode = 1 end --- запись в update_status -local updateStatusFile = assert(io.open(config.updateStatusPath, "w"), "Could not open 'update_status' file") -updateStatusFile:write(string.format("%d %d %d", blTables.ipCount, blTables.cidrCount, recordsNum)) -updateStatusFile:close() +local RblIp = Class(Rbl, { + sink = ipSink +}) + +------------------------------ Run section -------------------------------- + +local ctxTable = { + ["ip"] = {["antizapret"] = AzIp, ["rublacklist"] = RblIp}, + ["fqdn"] = {["antizapret"] = AzFqdn, ["rublacklist"] = Rbl}, + ["hybrid"] = {["antizapret"] = Az, ["rublacklist"] = Rbl}, +} + +local returnCode = 1 +local ctx = ctxTable[Config.BLOCK_MODE] and ctxTable[Config.BLOCK_MODE][Config.BL_UPDATE_MODE] +if ctx then + returnCode = ctx:new():run() +else + error("Wrong configuration! (Config.BLOCK_MODE or Config.BL_UPDATE_MODE)") +end os.exit(returnCode) diff --git a/opt/usr/bin/ruab.az-rbl.all.sh b/opt/usr/bin/ruab.az-rbl.all.sh index 8d1a8e1..ffbbcab 100755 --- a/opt/usr/bin/ruab.az-rbl.all.sh +++ b/opt/usr/bin/ruab.az-rbl.all.sh @@ -6,35 +6,36 @@ # # Модуль поддерживает следующие источники: # http://api.antizapret.info/group.php?data=ip +# http://api.antizapret.info/group.php?data=domain # http://api.antizapret.info/all.php?type=csv -# http://reestr.rublacklist.net/api/current +# http://api.reserve-rbl.ru/api/current # ######################################################################## ############################## Settings ################################ -### Альтернативный DNS-сервер ($ONION_DNS_ADDR (tor), 8.8.8.8 и др.). Если провайдер не блокирует сторонние DNS-запросы, то оптимальнее будет использовать для заблокированных сайтов, например, 8.8.8.8, а не резолвить через tor... -export ALT_DNS_ADDR="8.8.8.8" +### Тип обновления списка блокировок (antizapret, rublacklist) +export BL_UPDATE_MODE="antizapret" +### Режим обхода блокировок: ip (если провайдер блокирует по ip), hybrid (если провайдер использует DPI, подмену DNS и пр.), fqdn (если провайдер использует DPI, подмену DNS и пр.) +export BLOCK_MODE="hybrid" ### Перенаправлять DNS-запросы на альтернативный DNS-сервер для заблокированных FQDN (или в tor если провайдер блокирует сторонние DNS-серверы) (0 - off, 1 - on) export ALT_NSLOOKUP=1 -### В случае если из источника получено менее указанного кол-ва записей, то обновления списков не происходит -export BLLIST_MIN_ENTRS=1000 -### Обрезка www. в FQDN -export STRIP_WWW=1 +### Альтернативный DNS-сервер ($ONION_DNS_ADDR в ruantiblock.sh (tor), 8.8.8.8 и др.). Если провайдер не блокирует сторонние DNS-запросы, то оптимальнее будет использовать для заблокированных сайтов, например, 8.8.8.8, а не резолвить через tor... +export ALT_DNS_ADDR="8.8.8.8" +### Преобразование кириллических доменов в punycode (0 - off, 1 - on) +export USE_IDN=0 ### Записи (ip, CIDR, FQDN) исключаемые из списка блокировки (через пробел) export EXCLUDE_ENTRIES="youtube.com" ### 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" -### Не оптимизировать домены 3-го ур-ня вида: subdomain.xx(x).xx (.msk.ru .net.ru .org.ru .net.ua .com.ua .org.ua .co.uk и т.п.) (0 - off, 1 - on) +### Не оптимизировать SLD содержащие поддомены типа subdomain.xx(x).xx (.msk.ru .net.ru .org.ru .net.ua .com.ua .org.ua .co.uk и т.п.) (0 - off, 1 - on) export OPT_EXCLUDE_3LD_REGEXP=0 -### Лимит для субдоменов. При превышении, в список ${NAME}.dnsmasq будет добавлен весь домен 2-го ур-ня, вместо множества субдоменов +### Лимит для субдоменов. При достижении, в конфиг dnsmasq будет добавлен весь домен 2-го ур-ня вместо множества субдоменов export SD_LIMIT=16 -### Преобразование кириллических доменов в punycode -export USE_IDN=0 -### Тип обновления списка блокировок: 1 - antizapret.info, 2 - rublacklist.net -export BL_UPDATE_MODE=1 -### Режим обхода блокировок: 1 - ip (если провайдер блокирует по ip), 2 - hybrid (если провайдер использует DPI, подмену DNS и пр.) -export BLOCK_MODE=2 +### В случае если из источника получено менее указанного кол-ва записей, то обновления списков не происходит +export BLLIST_MIN_ENTRS=1000 +### Обрезка www[0-9]. в FQDN (0 - off, 1 - on) +export STRIP_WWW=1 ############################ Configuration ############################# @@ -42,79 +43,74 @@ export PATH="${PATH}:/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/opt/usr/b export NAME="ruantiblock" export LANG="en_US.UTF-8" ### Необходим gawk. Ибо "облегчённый" mawk, похоже, не справляется с огромным кол-вом обрабатываемых записей и крашится с ошибками... -AWKCMD="awk" -WGETCMD=`which wget` +AWK_CMD="awk" +WGET_CMD=`which wget` if [ $? -ne 0 ]; then echo " Error! Wget doesn't exists" >&2 exit 1 fi WGET_PARAMS="-q -O -" -IDNCMD=`which idn` +IDN_CMD=`which idn` if [ $USE_IDN = "1" -a $? -ne 0 ]; then echo " Idn doesn't exists" >&2 USE_IDN=0 fi DATA_DIR="/opt/var/${NAME}" -export DNSMASQ_DATA="${DATA_DIR}/${NAME}.dnsmasq" -export DNSMASQ_DATA_TMP="${DNSMASQ_DATA}.tmp" -export IP_DATA="${DATA_DIR}/${NAME}.ip" -export IP_DATA_TMP="${IP_DATA}.tmp" +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 UPDATE_STATUS_FILE="${DATA_DIR}/update_status" -### Источники списка блокировок -BLLIST_URL1_BASE="http://api.antizapret.info" -BLLIST_URL1_IP="${BLLIST_URL1_BASE}/group.php?data=ip" -BLLIST_URL1_ALL="${BLLIST_URL1_BASE}/all.php?type=csv" -BLLIST_URL2="http://reestr.rublacklist.net/api/current" -#BLLIST_URL2="http://api.reserve-rbl.ru/api/current" +### Источники блэклиста +AZ_ALL_URL="http://api.antizapret.info/all.php?type=csv" +AZ_IP_URL="http://api.antizapret.info/group.php?data=ip" +AZ_FQDN_URL="http://api.antizapret.info/group.php?data=domain" +#RBL_ALL_URL="http://reestr.rublacklist.net/api/current" +RBL_ALL_URL="http://api.reserve-rbl.ru/api/current" ############################## Functions ############################### DlRun () { - - $WGETCMD $WGET_PARAMS "$1" - + $WGET_CMD $WGET_PARAMS "$1" + if [ $? -ne 0 ]; then + echo "Connection error (${1})" >&2 + exit 1 + fi } GetAntizapret () { - local _url - case $BLOCK_MODE in - 2) - _url="$BLLIST_URL1_ALL" + "fqdn") + _url="$AZ_FQDN_URL" + ;; + "hybrid") + _url="$AZ_ALL_URL" ;; *) - _url="$BLLIST_URL1_IP" + _url="$AZ_IP_URL" ;; esac - DlRun "$_url" - } GetRublacklist () { - - DlRun "$BLLIST_URL2" - + DlRun "$RBL_ALL_URL" } MakeDataFiles () { - local _return_code - - $AWKCMD -F ";" -v IDNCMD="$IDNCMD" ' + $AWK_CMD -F ";" -v IDN_CMD="$IDN_CMD" ' BEGIN { ### Массивы из констант с исключениями makeConstArray(ENVIRON["EXCLUDE_ENTRIES"], ex_entrs_array, " "); makeConstArray(ENVIRON["OPT_EXCLUDE_SLD"], ex_sld_array, " "); total_ip=0; total_cidr=0; total_fqdn=0; ### Определение разделителя записей (строк) - if(ENVIRON["BL_UPDATE_MODE"] == "2") + if(ENVIRON["BL_UPDATE_MODE"] == "rublacklist") RS="\134"; else RS="\n"; @@ -135,13 +131,18 @@ MakeDataFiles () { }; ### Получение SLD из доменов низших уровней function getSld(val) { - return substr(val, match(val, /[a-z0-9-]+[.][a-z0-9-]+$/)); + return substr(val, match(val, /[a-z0-9_-]+[.][a-z0-9-]+$/)); }; - ### Запись в $DNSMASQ_DATA + ### Запись в $DNSMASQ_DATA_FILE function writeDNSData(val) { if(ENVIRON["ALT_NSLOOKUP"] == 1) - printf "server=/%s/%s\n", val, ENVIRON["ALT_DNS_ADDR"] > ENVIRON["DNSMASQ_DATA"]; - printf "ipset=/%s/%s\n", val, ENVIRON["IPSET_DNSMASQ"] > ENVIRON["DNSMASQ_DATA"]; + printf "server=/%s/%s\n", val, ENVIRON["ALT_DNS_ADDR"] > ENVIRON["DNSMASQ_DATA_FILE"]; + printf "ipset=/%s/%s\n", val, ENVIRON["IPSET_DNSMASQ"] > ENVIRON["DNSMASQ_DATA_FILE"]; + }; + ### Запись в $IP_DATA_FILE + function writeIpsetEntries(array, set, _i) { + for(_i in array) + printf "add %s %s\n", set, _i > ENVIRON["IP_DATA_FILE"]; }; ### Обработка ip и CIDR function checkIp(val, _val_array, _i) { @@ -161,12 +162,13 @@ MakeDataFiles () { }; ### Обработка FQDN function checkFQDN(val, _sld, _call_idn) { + sub(/[.]$/, "", val); sub(/^[\052][.]/, "", val); - if(ENVIRON["STRIP_WWW"] == "1") sub(/^www[.]/, "", val); + if(ENVIRON["STRIP_WWW"] == "1") sub(/^www[0-9]?[.]/, "", val); if(val in ex_entrs_array) next; if(ENVIRON["USE_IDN"] == "1" && val !~ /^[a-z0-9._-]+[.]([a-z]{2,}|xn--[a-z0-9]+)$/ && val ~ /^([a-z0-9.-])*[^a-zA-Z.]+[.]([a-z]|[^a-z]){2,}$/) { - ### Кириллические FQDN кодируются $IDNCMD в punycode ($AWKCMD вызывает $IDNCMD с параметром val, в отдельном экземпляре /bin/sh, далее STDOUT $IDNCMD функцей getline помещается в val) - _call_idn=IDNCMD" "val; + ### Кириллические FQDN кодируются $IDN_CMD в punycode ($AWK_CMD вызывает $IDN_CMD с параметром val, в отдельном экземпляре /bin/sh, далее STDOUT $IDN_CMD функцей getline помещается в val) + _call_idn=IDN_CMD" "val; _call_idn | getline val; close(_call_idn); } @@ -192,36 +194,41 @@ MakeDataFiles () { }; }; }; - ### Запись в $IP_DATA - function writeIpsetEntries(array, set, _i) { - for(_i in array) - printf "add %s %s\n", set, _i > ENVIRON["IP_DATA"]; - }; - (ENVIRON["BL_UPDATE_MODE"] != "2") || ($0 ~ /^n/) { + (ENVIRON["BL_UPDATE_MODE"] != "rublacklist") || ($0 ~ /^n/) { ip_string=""; fqdn_string=""; - gsub("&", "", $0) split($0, string_array, ";") - if(ENVIRON["BL_UPDATE_MODE"] == 2) { + if(ENVIRON["BL_UPDATE_MODE"] == "rublacklist") { ip_string=string_array[1]; fqdn_string=string_array[2]; gsub(/[ n]/, "", ip_string); } else { - if(ENVIRON["BLOCK_MODE"] == "2") { - ip_string=string_array[4]; - fqdn_string=string_array[3]; + if(ENVIRON["BLOCK_MODE"] == "hybrid") { + ip_string=string_array[length(string_array)]; + fqdn_string=string_array[length(string_array)-1]; + } + else if(ENVIRON["BLOCK_MODE"] == "fqdn") { + fqdn_string=string_array[1]; } else ip_string=string_array[1]; }; - ### В случае, если запись реестра не содержит FQDN, то, не смотря на $BLOCK_MODE=2, в $IP_DATA добавляются найденные в записи ip и CIDR-подсети (после проверки на повторы) - if(ENVIRON["BLOCK_MODE"] == "2") { + ### В случае, если запись реестра не содержит FQDN, то при $BLOCK_MODE="hybrid" в $IP_DATA_FILE добавляются найденные в записи ip и CIDR-подсети (после проверки на повторы) + if(ENVIRON["BLOCK_MODE"] == "hybrid") { if(length(fqdn_string) > 0 && fqdn_string !~ /^[0-9]{1,3}([.][0-9]{1,3}){3}$/) { - sub(/[.]$/, "", fqdn_string); checkFQDN(fqdn_string); } - else if(length(ip_string) > 0) { + else if(length(ip_string) > 0) checkIp(ip_string); + } + else if(ENVIRON["BLOCK_MODE"] == "fqdn") { + if(length(fqdn_string) > 0) { + if(fqdn_string ~ /^[0-9]{1,3}([.][0-9]{1,3}){3}$/) { + checkIp(fqdn_string); + } + else { + checkFQDN(fqdn_string); + }; }; } else if(length(ip_string) > 0) { @@ -236,14 +243,14 @@ MakeDataFiles () { if((total_ip + total_cidr + total_fqdn) < ENVIRON["BLLIST_MIN_ENTRS"]) exit_code=2; else { - ### Запись в $IP_DATA ip-адресов и подсетей CIDR - system("rm -f \"" ENVIRON["IP_DATA"] "\""); + ### Запись в $IP_DATA_FILE ip-адресов и подсетей CIDR + system("rm -f \"" ENVIRON["IP_DATA_FILE"] "\""); writeIpsetEntries(total_ip_array, ENVIRON["IPSET_IP_TMP"]); writeIpsetEntries(total_cidr_array, ENVIRON["IPSET_CIDR_TMP"]); - ### Оптимизация отобранных FQDN и запись в $DNSMASQ_DATA - system("rm -f \"" ENVIRON["DNSMASQ_DATA"] "\""); - if(ENVIRON["BLOCK_MODE"] == "2") { - ### Чистка sld_array[] от тех SLD, которые встречались при обработке менее $SD_LIMIT (остаются только достигнувшие $SD_LIMIT) и добавление их в $DNSMASQ_DATA (вместо исключаемых далее субдоменов достигнувших $SD_LIMIT) + ### Оптимизация отобранных FQDN и запись в $DNSMASQ_DATA_FILE + system("rm -f \"" ENVIRON["DNSMASQ_DATA_FILE"] "\""); + if(ENVIRON["BLOCK_MODE"] == "hybrid" || ENVIRON["BLOCK_MODE"] == "fqdn") { + ### Чистка sld_array[] от тех SLD, которые встречались при обработке менее $SD_LIMIT (остаются только достигнувшие $SD_LIMIT) и добавление их в $DNSMASQ_DATA_FILE (вместо исключаемых далее субдоменов достигнувших $SD_LIMIT) if(ENVIRON["SD_LIMIT"] > 1) { for(j in sld_array) { if(sld_array[j] < ENVIRON["SD_LIMIT"]) @@ -254,7 +261,7 @@ MakeDataFiles () { }; }; }; - ### Запись из total_fqdn_array[] в $DNSMASQ_DATA с исключением всех SLD присутствующих в sld_array[] и их субдоменов (если ENVIRON["SD_LIMIT"] > 1) + ### Запись из total_fqdn_array[] в $DNSMASQ_DATA_FILE с исключением всех SLD присутствующих в sld_array[] и их субдоменов (если ENVIRON["SD_LIMIT"] > 1) for(k in total_fqdn_array) { if(ENVIRON["SD_LIMIT"] > 1 && total_fqdn_array[k] in sld_array) continue; @@ -269,15 +276,13 @@ MakeDataFiles () { printf "%s %s %s", total_ip, total_cidr, output_fqdn > ENVIRON["UPDATE_STATUS_FILE"]; exit exit_code; }' - return $? - } ############################# Run section ############################## case $BL_UPDATE_MODE in - 2) + "rublacklist") GetRublacklist | MakeDataFiles ;; *) diff --git a/opt/usr/bin/ruab.az.fqdn.sh b/opt/usr/bin/ruab.az.fqdn.sh index c7f3a45..7707416 100755 --- a/opt/usr/bin/ruab.az.fqdn.sh +++ b/opt/usr/bin/ruab.az.fqdn.sh @@ -10,24 +10,24 @@ ############################## Settings ################################ -### Альтернативный DNS-сервер ($ONION_DNS_ADDR (tor), 8.8.8.8 и др.). Если провайдер не блокирует сторонние DNS-запросы, то оптимальнее будет использовать для заблокированных сайтов, например, 8.8.8.8, а не резолвить через tor... -export ALT_DNS_ADDR="8.8.8.8" ### Перенаправлять DNS-запросы на альтернативный DNS-сервер для заблокированных FQDN (или в tor если провайдер блокирует сторонние DNS-серверы) (0 - off, 1 - on) export ALT_NSLOOKUP=1 -### В случае если из источника получено менее указанного кол-ва записей, то обновления списков не происходит -export BLLIST_MIN_ENTRS=1000 -### Обрезка www. в FQDN -export STRIP_WWW=1 +### Альтернативный DNS-сервер ($ONION_DNS_ADDR в ruantiblock.sh (tor), 8.8.8.8 и др.). Если провайдер не блокирует сторонние DNS-запросы, то оптимальнее будет использовать для заблокированных сайтов, например, 8.8.8.8, а не резолвить через tor... +export ALT_DNS_ADDR="8.8.8.8" +### Преобразование кириллических доменов в punycode (0 - off, 1 - on) +export USE_IDN=0 ### Записи (ip, FQDN) исключаемые из списка блокировки (через пробел) export EXCLUDE_ENTRIES="youtube.com" ### 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" -### Не оптимизировать домены 3-го ур-ня вида: subdomain.xx(x).xx (.msk.ru .net.ru .org.ru .net.ua .com.ua .org.ua .co.uk и т.п.) (0 - off, 1 - on) +### Не оптимизировать SLD содержащие поддомены типа subdomain.xx(x).xx (.msk.ru .net.ru .org.ru .net.ua .com.ua .org.ua .co.uk и т.п.) (0 - off, 1 - on) export OPT_EXCLUDE_3LD_REGEXP=0 -### Лимит для субдоменов. При превышении, в список ${NAME}.dnsmasq будет добавлен весь домен 2-го ур-ня, вместо множества субдоменов +### Лимит для субдоменов. При достижении, в конфиг dnsmasq будет добавлен весь домен 2-го ур-ня вместо множества субдоменов export SD_LIMIT=16 -### Преобразование кириллических доменов в punycode -export USE_IDN=0 +### В случае если из источника получено менее указанного кол-ва записей, то обновления списков не происходит +export BLLIST_MIN_ENTRS=1000 +### Обрезка www[0-9]. в FQDN (0 - off, 1 - on) +export STRIP_WWW=1 ############################ Configuration ############################# @@ -35,31 +35,31 @@ export PATH="${PATH}:/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/opt/usr/b export NAME="ruantiblock" export LANG="en_US.UTF-8" ### Необходим gawk. Ибо "облегчённый" mawk, похоже, не справляется с огромным кол-вом обрабатываемых записей и крашится с ошибками... -AWKCMD="awk" -WGETCMD=`which wget` +AWK_CMD="awk" +WGET_CMD=`which wget` if [ $? -ne 0 ]; then echo " Error! Wget doesn't exists" >&2 exit 1 fi WGET_PARAMS="-q -O -" -IDNCMD=`which idn` +IDN_CMD=`which idn` if [ $USE_IDN = "1" -a $? -ne 0 ]; then echo " Idn doesn't exists" >&2 USE_IDN=0 fi DATA_DIR="/opt/var/${NAME}" -export DNSMASQ_DATA="${DATA_DIR}/${NAME}.dnsmasq" -export IP_DATA="${DATA_DIR}/${NAME}.ip" +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_DNSMASQ="${NAME}-dnsmasq" export UPDATE_STATUS_FILE="${DATA_DIR}/update_status" -### Источник списка блокировок -BLLIST_URL="http://api.antizapret.info/group.php?data=domain" +### Источник блэклиста +AZ_FQDN_URL="http://api.antizapret.info/group.php?data=domain" ############################# Run section ############################## -$WGETCMD $WGET_PARAMS "$BLLIST_URL" | $AWKCMD -v IDNCMD="$IDNCMD" ' +$WGET_CMD $WGET_PARAMS "$AZ_FQDN_URL" | $AWK_CMD -v IDN_CMD="$IDN_CMD" ' BEGIN { ### Массивы из констант с исключениями makeConstArray(ENVIRON["EXCLUDE_ENTRIES"], ex_entrs_array, " "); @@ -74,18 +74,18 @@ $WGETCMD $WGET_PARAMS "$BLLIST_URL" | $AWKCMD -v IDNCMD="$IDNCMD" ' }; ### Получение SLD из доменов низших уровней function getSld(val) { - return substr(val, match(val, /[a-z0-9-]+[.][a-z0-9-]+$/)); + return substr(val, match(val, /[a-z0-9_-]+[.][a-z0-9-]+$/)); }; - ### Запись в $DNSMASQ_DATA + ### Запись в $DNSMASQ_DATA_FILE function writeDNSData(val) { if(ENVIRON["ALT_NSLOOKUP"] == 1) - printf "server=/%s/%s\n", val, ENVIRON["ALT_DNS_ADDR"] > ENVIRON["DNSMASQ_DATA"]; - printf "ipset=/%s/%s\n", val, ENVIRON["IPSET_DNSMASQ"] > ENVIRON["DNSMASQ_DATA"]; + printf "server=/%s/%s\n", val, ENVIRON["ALT_DNS_ADDR"] > ENVIRON["DNSMASQ_DATA_FILE"]; + printf "ipset=/%s/%s\n", val, ENVIRON["IPSET_DNSMASQ"] > ENVIRON["DNSMASQ_DATA_FILE"]; }; - ### Запись в $IP_DATA + ### Запись в $IP_DATA_FILE function writeIpsetEntries(array, set, _i) { for(_i in array) - printf "add %s %s\n", set, _i > ENVIRON["IP_DATA"]; + printf "add %s %s\n", set, _i > ENVIRON["IP_DATA_FILE"]; }; ### Обработка ip и CIDR function checkIp(val, array2, counter) { @@ -97,17 +97,18 @@ $WGETCMD $WGET_PARAMS "$BLLIST_URL" | $AWKCMD -v IDNCMD="$IDNCMD" ' }; ### Обработка FQDN function checkFQDN(val, array, cyr, _sld, _call_idn) { + sub(/[.]$/, "", val); sub(/^[\052][.]/, "", val); - if(ENVIRON["STRIP_WWW"] == "1") sub(/^www[.]/, "", val); + if(ENVIRON["STRIP_WWW"] == "1") sub(/^www[0-9]?[.]/, "", val); if(val in ex_entrs_array) next; if(cyr == 1) { - ### Кириллические FQDN кодируются $IDNCMD в punycode ($AWKCMD вызывает $IDNCMD с параметром val, в отдельном экземпляре /bin/sh, далее STDOUT $IDNCMD функцей getline помещается в val) - _call_idn=IDNCMD" "val; + ### Кириллические FQDN кодируются $IDN_CMD в punycode ($AWK_CMD вызывает $IDN_CMD с параметром val, в отдельном экземпляре /bin/sh, далее STDOUT $IDN_CMD функцей getline помещается в val) + _call_idn=IDN_CMD" "val; _call_idn | getline val; close(_call_idn); } ### Проверка на отсутствие лишних символов и повторы - if(val ~ /^[a-z0-9.-]+$/) { + if(val ~ /^[a-z0-9._-]+[.]([a-z]{2,}|xn--[a-z0-9]+)$/) { ### SLD из FQDN _sld=getSld(val); ### Каждому SLD задается предельный лимит, чтобы далее исключить из очистки при сравнении с $SD_LIMIT @@ -129,15 +130,15 @@ $WGETCMD $WGET_PARAMS "$BLLIST_URL" | $AWKCMD -v IDNCMD="$IDNCMD" ' }; { ### Отбор ip - if($0 ~ /^[0-9]{1,3}([.][0-9]{1,3}){3}?$/) + if($0 ~ /^[0-9]{1,3}([.][0-9]{1,3}){3}$/) total_ip=checkIp($0, total_ip_array, total_ip); ### Отбор FQDN - else if($0 ~ /^[a-z0-9.\052-]+[.]([a-z]{2,}|xn--[a-z0-9]+)$/) { + else if($0 ~ /^[a-z0-9.\052_-]+[.]([a-z]{2,}|xn--[a-z0-9]+)[.]?$/) { checkFQDN($0, total_fqdn_array, 0); total_fqdn++; } ### Отбор кириллических FQDN - else if(ENVIRON["USE_IDN"] == "1" && $0 ~ /^[^a-zA-Z.]+[.]([a-z]|[^a-z]){2,}$/) { + else if(ENVIRON["USE_IDN"] == "1" && $0 ~ /^([a-z0-9.-])*[^a-zA-Z.]+[.]([a-z]|[^a-z]){2,}$/) { checkFQDN($0, total_fqdn_array, 1); total_fqdn++; }; @@ -148,12 +149,12 @@ $WGETCMD $WGET_PARAMS "$BLLIST_URL" | $AWKCMD -v IDNCMD="$IDNCMD" ' if((total_ip + total_fqdn) < ENVIRON["BLLIST_MIN_ENTRS"]) exit_code=2; else { - ### Запись в $IP_DATA - system("rm -f \"" ENVIRON["IP_DATA"] "\""); + ### Запись в $IP_DATA_FILE + system("rm -f \"" ENVIRON["IP_DATA_FILE"] "\""); writeIpsetEntries(total_ip_array, ENVIRON["IPSET_IP_TMP"]); - ### Оптимизация отобранных FQDN и запись в $DNSMASQ_DATA - system("rm -f \"" ENVIRON["DNSMASQ_DATA"] "\""); - ### Чистка sld_array[] от тех SLD, которые встречались при обработке менее $SD_LIMIT (остаются только достигнувшие $SD_LIMIT) и добавление их в $DNSMASQ_DATA (вместо исключаемых далее субдоменов достигнувших $SD_LIMIT) + ### Оптимизация отобранных FQDN и запись в $DNSMASQ_DATA_FILE + system("rm -f \"" ENVIRON["DNSMASQ_DATA_FILE"] "\""); + ### Чистка sld_array[] от тех SLD, которые встречались при обработке менее $SD_LIMIT (остаются только достигнувшие $SD_LIMIT) и добавление их в $DNSMASQ_DATA_FILE (вместо исключаемых далее субдоменов достигнувших $SD_LIMIT) if(ENVIRON["SD_LIMIT"] > 1) { for(j in sld_array) { if(sld_array[j] < ENVIRON["SD_LIMIT"]) @@ -164,9 +165,8 @@ $WGETCMD $WGET_PARAMS "$BLLIST_URL" | $AWKCMD -v IDNCMD="$IDNCMD" ' }; }; }; - ### Запись из total_fqdn_array[] в $DNSMASQ_DATA с исключением всех SLD присутствующих в sld_array[] и их субдоменов (если ENVIRON["SD_LIMIT"] > 1) + ### Запись из total_fqdn_array[] в $DNSMASQ_DATA_FILE с исключением всех SLD присутствующих в sld_array[] и их субдоменов (если ENVIRON["SD_LIMIT"] > 1) for(k in total_fqdn_array) { - #print(total_fqdn_array[k]) if(ENVIRON["SD_LIMIT"] > 1 && total_fqdn_array[k] in sld_array) continue; else { diff --git a/opt/usr/bin/ruantiblock.sh b/opt/usr/bin/ruantiblock.sh index 60af026..195b360 100755 --- a/opt/usr/bin/ruantiblock.sh +++ b/opt/usr/bin/ruantiblock.sh @@ -13,54 +13,61 @@ ############################## Settings ################################ -### Входящий сетевой интерфейс для правил iptables -IF_IN="br0" -### WAN интерфейс -IF_WAN="ppp0" -### Максимальное кол-во элементов списка ipset (по умол.: 65536, на данный момент уже не хватает для полного списка ip...) -IPSET_MAXELEM=150000 -### Таймаут для записей в сете $IPSET_DNSMASQ -IPSET_DNSMASQ_TIMEOUT=900 +### Режим обработки пакетов в правилах iptables (1 - Tor, 2 - VPN) +PROXY_MODE=1 +### Применять правила проксификации для трафика локальных сервисов роутера (0 - off, 1 - on) +PROXY_LOCAL_CLIENTS=1 ### Порт транспарентного proxy tor (параметр TransPort в torrc) TOR_TRANS_PORT=9040 ### DNS-сервер для резолвинга в домене .onion (tor) export ONION_DNS_ADDR="127.0.0.1#9053" +### Html-страница с инфо о текущем статусе (0 - off, 1 - on) +HTML_INFO=1 ### Запись событий в syslog (0 - off, 1 - on) export USE_LOGGER=1 -### Режим обработки пакетов в правилах iptables (1 - Tor, 2 - VPN) -PROXY_MODE=1 -### Применять правила проксификации для трафика локальных сервисов роутера (0 - off, 1 - on) -PROXY_LOCAL_CLIENTS=1 -### --set-mark для отбора пакетов в VPN туннель -VPN_PKTS_MARK=1 -### VPN интерфейс для правил iptables -IF_VPN="tun0" ### Режим полного прокси при старте скрипта (0 - off, 1 - on). Если 1, то весь трафик всегда идёт через прокси. Все пакеты попадающие в цепочку $IPT_CHAIN попадают в tor или VPN, за исключением сетей из $TOTAL_PROXY_EXCLUDE_NETS. Списки блокировок не используются для фильтрации. Работает только при PROXY_LOCAL_CLIENTS=0 DEF_TOTAL_PROXY=0 ### Трафик в заданные сети идет напрямую, не попадая в tor или VPN, в режиме total-proxy TOTAL_PROXY_EXCLUDE_NETS="10.0.0.0/8 172.16.0.0/12 192.168.0.0/16" -### Html-страница с инфо о текущем статусе (0 - off, 1 - on) -USE_HTML_STATUS=1 ### Добавление в список блокировок пользовательских записей из файла $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=1 +### Входящий сетевой интерфейс для правил iptables +IF_IN="br0" +### WAN интерфейс +IF_WAN="ppp0" +### VPN интерфейс для правил iptables +IF_VPN="tun0" +### --set-mark для отбора пакетов в VPN туннель +VPN_PKTS_MARK=1 +### Максимальное кол-во элементов списка ipset (по умол.: 65536, на данный момент уже не хватает для полного списка ip...) +IPSET_MAXELEM=150000 +### Таймаут для записей в сете $IPSET_DNSMASQ +IPSET_DNSMASQ_TIMEOUT=900 ############################ Configuration ############################# export PATH="${PATH}:/bin:/sbin:/usr/bin:/usr/sbin:/opt/bin:/opt/sbin:/opt/usr/bin:/opt/usr/sbin" export NAME="ruantiblock" export LANG="en_US.UTF-8" -AWKCMD="awk" -IPTCMD="iptables" -IPCMD="ip" -IPSETCMD=`which ipset` + +### Модули для получения и обработки блэклиста +MODULES_DIR="/opt/usr/bin" +BLLIST_MODULE_CMD="lua ${MODULES_DIR}/ruab.az-rbl.all.lua" +#BLLIST_MODULE_CMD="${MODULES_DIR}/ruab.az.fqdn.sh" +#BLLIST_MODULE_CMD="${MODULES_DIR}/ruab.az-rbl.all.sh" + +AWK_CMD="awk" +IPT_CMD="iptables" +IP_CMD="ip" +IPSET_CMD=`which ipset` if [ $? -ne 0 ]; then echo " Error! Ipset doesn't exists" >&2 exit 1 fi -LOGGERCMD=`which logger` +LOGGER_CMD=`which logger` if [ $USE_LOGGER = "1" -a $? -ne 0 ]; then echo " Logger doesn't exists" >&2 USE_LOGGER=0 @@ -89,11 +96,6 @@ HTML_BORDER_COLOR="#B5B5B5" HTML_MAIN_FONT_COLOR="#333333" ### Пользовательские записи USER_ENTRIES_FILE="${DATA_DIR}/user_entries" -### Модули для получения и обработки блэклиста -MODULES_DIR="/opt/usr/bin" -BLLIST_MODULE_CMD="lua ${MODULES_DIR}/ruab.az-rbl.all.lua" -#BLLIST_MODULE_CMD="${MODULES_DIR}/ruab.az.fqdn.sh" -#BLLIST_MODULE_CMD="${MODULES_DIR}/ruab.az-rbl.all.sh" ########################### Iptables config ############################ @@ -103,7 +105,7 @@ IPT_IPSET_MSET="-m set --match-set" ### Проксификация трафика локальных клиентов IPT_OUTPUT_FIRST_RULE="-j ${IPT_CHAIN}" -WAN_IP=`$IPCMD addr list dev $IF_WAN | $AWKCMD '/inet/{sub("/[0-9]{1,2}$", "", $2); print $2}'` +WAN_IP=`$IP_CMD addr list dev $IF_WAN | $AWK_CMD '/inet/{sub("/[0-9]{1,2}$", "", $2); print $2}'` VPN_NAT_RULE="-m mark --mark ${VPN_PKTS_MARK} -s ${WAN_IP} -o ${IF_VPN} -j MASQUERADE" ### Tor конфигурация @@ -126,7 +128,6 @@ IPT_TP_RULE="-m set ! --match-set ${IPSET_TOTAL_PROXY} ${IPT_IPSET_TARGET}" ############################## Functions ############################### Help () { - cat << EOF Usage: `basename $0` start|stop|destroy|restart|update|force-update|data-files|total-proxy-on|total-proxy-off|renew-ipt|status|status-html|--help start : Start @@ -140,7 +141,7 @@ cat << EOF total-proxy-off : Total-proxy mode off renew-ipt : Renew iptables configuration status : Status & some info - status-html : Update html-status (if USE_HTML_STATUS=1) + html-info : Update html infopage (if HTML_INFO=1) -h|--help : This message Examples: `basename $0` start @@ -153,79 +154,60 @@ cat << EOF `basename $0` total-proxy-on `basename $0` total-proxy-off `basename $0` status - `basename $0` status-html + `basename $0` html-info EOF - } MakeLogRecord () { - - [ $USE_LOGGER = "1" ] && $LOGGERCMD $LOGGER_PARAMS $1 - + [ $USE_LOGGER = "1" ] && $LOGGER_CMD $LOGGER_PARAMS $1 } DnsmasqRestart () { - eval `echo "$DNSMASQ_RESTART_CMD"` - } IsIpsetExists () { - - $IPSETCMD list "$1" &> /dev/null + $IPSET_CMD list "$1" &> /dev/null return $? - } FlushIpSets () { - local _set - for _set in "$@" do - IsIpsetExists "$_set" && $IPSETCMD flush "$_set" + IsIpsetExists "$_set" && $IPSET_CMD flush "$_set" done - } DestroyIpsets () { - local _set - for _set in "$@" do - IsIpsetExists "$_set" && $IPSETCMD destroy "$_set" + IsIpsetExists "$_set" && $IPSET_CMD destroy "$_set" done - } FillTotalProxySet () { - local _entry - for _entry in $TOTAL_PROXY_EXCLUDE_NETS do - $IPSETCMD add "$IPSET_TOTAL_PROXY" "$_entry" + $IPSET_CMD add "$IPSET_TOTAL_PROXY" "$_entry" done - } TotalProxyOn () { - if [ "$PROXY_LOCAL_CLIENTS" != "1" ]; then - $IPTCMD -t "$IPT_TABLE" -I "$IPT_CHAIN" 1 $IPT_TP_RULE + $IPT_CMD -t "$IPT_TABLE" -I "$IPT_CHAIN" 1 $IPT_TP_RULE if [ $? -eq 0 ]; then echo " ${IPSET_TOTAL_PROXY} enabled" MakeLogRecord "${IPSET_TOTAL_PROXY} enabled" fi fi - } TotalProxyOff () { - if [ "$PROXY_LOCAL_CLIENTS" != "1" ]; then - $IPTCMD -t "$IPT_TABLE" -D "$IPT_CHAIN" $IPT_TP_RULE + $IPT_CMD -t "$IPT_TABLE" -D "$IPT_CHAIN" $IPT_TP_RULE if [ $? -ne 0 ]; then echo " ${IPSET_TOTAL_PROXY} is already disabled" >&2 else @@ -233,122 +215,92 @@ TotalProxyOff () { MakeLogRecord "${IPSET_TOTAL_PROXY} disabled" fi fi - } AddIptRules () { - local _set - - $IPTCMD -t "$IPT_TABLE" -N "$IPT_CHAIN" - $IPTCMD -t "$IPT_TABLE" -I "$IPT_FIRST_CHAIN" 1 $IPT_FIRST_CHAIN_RULE - + $IPT_CMD -t "$IPT_TABLE" -N "$IPT_CHAIN" + $IPT_CMD -t "$IPT_TABLE" -I "$IPT_FIRST_CHAIN" 1 $IPT_FIRST_CHAIN_RULE ### Проксификация трафика локальных клиентов if [ "$PROXY_LOCAL_CLIENTS" = "1" ]; then - $IPTCMD -t "$IPT_TABLE" -I OUTPUT 1 $IPT_OUTPUT_FIRST_RULE + $IPT_CMD -t "$IPT_TABLE" -I OUTPUT 1 $IPT_OUTPUT_FIRST_RULE if [ "$PROXY_MODE" = "2" -a -n "$WAN_IP" ]; then - $IPTCMD -t nat -A POSTROUTING $VPN_NAT_RULE + $IPT_CMD -t nat -A POSTROUTING $VPN_NAT_RULE fi fi - for _set in $IPT_IPSETS do - $IPTCMD -t "$IPT_TABLE" -A "$IPT_CHAIN" $IPT_IPSET_MSET "$_set" $IPT_IPSET_TARGET + $IPT_CMD -t "$IPT_TABLE" -A "$IPT_CHAIN" $IPT_IPSET_MSET "$_set" $IPT_IPSET_TARGET done - if [ "$DEF_TOTAL_PROXY" = "1" ]; then TotalProxyOff &> /dev/null TotalProxyOn fi - } RemIptRules () { - - $IPTCMD -t "$IPT_TABLE" -F "$IPT_CHAIN" - $IPTCMD -t "$IPT_TABLE" -D "$IPT_FIRST_CHAIN" $IPT_FIRST_CHAIN_RULE - + $IPT_CMD -t "$IPT_TABLE" -F "$IPT_CHAIN" + $IPT_CMD -t "$IPT_TABLE" -D "$IPT_FIRST_CHAIN" $IPT_FIRST_CHAIN_RULE ### Проксификация трафика локальных клиентов if [ "$PROXY_LOCAL_CLIENTS" = "1" ]; then - $IPTCMD -t "$IPT_TABLE" -D OUTPUT $IPT_OUTPUT_FIRST_RULE + $IPT_CMD -t "$IPT_TABLE" -D OUTPUT $IPT_OUTPUT_FIRST_RULE if [ "$PROXY_MODE" = "2" -a -n "$WAN_IP" ]; then - $IPTCMD -t nat -D POSTROUTING $VPN_NAT_RULE + $IPT_CMD -t nat -D POSTROUTING $VPN_NAT_RULE fi fi - - $IPTCMD -t "$IPT_TABLE" -X "$IPT_CHAIN" - + $IPT_CMD -t "$IPT_TABLE" -X "$IPT_CHAIN" } SetNetConfig () { - local _set - ### Создание списков ipset. Проверка на наличие списка с таким же именем, если нет, то создается новый - for _set in "$IPSET_TOTAL_PROXY" "$IPSET_CIDR_TMP" "$IPSET_CIDR" do - IsIpsetExists "$_set" || $IPSETCMD create "$_set" hash:net maxelem $IPSET_MAXELEM + 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" || $IPSETCMD create "$_set" hash:ip maxelem $IPSET_MAXELEM + IsIpsetExists "$_set" || $IPSET_CMD create "$_set" hash:ip maxelem $IPSET_MAXELEM done - - IsIpsetExists "$IPSET_DNSMASQ" || $IPSETCMD create "$IPSET_DNSMASQ" hash:ip maxelem $IPSET_MAXELEM timeout $IPSET_DNSMASQ_TIMEOUT - + IsIpsetExists "$IPSET_DNSMASQ" || $IPSET_CMD create "$IPSET_DNSMASQ" hash:ip maxelem $IPSET_MAXELEM timeout $IPSET_DNSMASQ_TIMEOUT FillTotalProxySet AddIptRules - } DropNetConfig () { - RemIptRules FlushIpSets "$IPSET_CIDR_TMP" "$IPSET_CIDR" "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_DNSMASQ" "$IPSET_ONION" "$IPSET_TOTAL_PROXY" - } FillIpsets () { - local _set - ### Заполнение списков ipset $IPSET_IP и $IPSET_CIDR. Сначала restore загружает во временные списки, а затем swap из временных добавляет в основные - if [ -f "$IP_DATA" ]; 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" | $IPSETCMD restore && { $IPSETCMD swap "$IPSET_IP_TMP" "$IPSET_IP"; $IPSETCMD swap "$IPSET_CIDR_TMP" "$IPSET_CIDR"; } - + cat "$IP_DATA" | $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" else echo " Error! Ipset wasn't updated" >&2 MakeLogRecord "Error! Ipset wasn't updated" fi - fi - } PreStartCheck () { - [ -d "$DATA_DIR" ] || mkdir -p "$DATA_DIR" + [ "$HTML_INFO" = "1" -a ! -d "$HTML_DIR" ] && mkdir -p "$HTML_DIR" ### Костыль для старта dnsmasq [ -e "$DNSMASQ_DATA" ] || printf "\n" > "$DNSMASQ_DATA" - } AddUserEntries () { - if [ "$ADD_USER_ENTRIES" = "1" ]; then - if [ -f "$USER_ENTRIES_FILE" -a -s "$USER_ENTRIES_FILE" ]; then - - $AWKCMD 'BEGIN { + $AWK_CMD 'BEGIN { while((getline ip_string 0){ split(ip_string, ip_string_arr, " "); ip_data_array[ip_string_arr[3]]=""; @@ -376,28 +328,19 @@ AddUserEntries () { 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 _update_string - PreStartCheck echo "$$" > "$UPDATE_PID_FILE" - eval `echo "$BLLIST_MODULE_CMD"` _return_code=$? - AddUserEntries - if [ $_return_code -eq 0 ]; then - - _update_string=`$AWKCMD '{printf "%s ip, %s CIDR and %s FQDN entries added\n", $1, $2, $3; exit}' "$UPDATE_STATUS_FILE"` + _update_string=`$AWK_CMD '{printf "Received entries: IP: %s, CIDR: %s, FQDN: %s\n", $1, $2, $3; exit}' "$UPDATE_STATUS_FILE"` ### STDOUT echo " ${_update_string}" MakeLogRecord "${_update_string}" @@ -408,30 +351,22 @@ GetDataFiles () { ### Запись для .onion в $DNSMASQ_DATA printf "server=/onion/%s\nipset=/onion/%s\n" "${ONION_DNS_ADDR}" "${IPSET_ONION}" >> "$DNSMASQ_DATA" fi - fi - printf " `date +%d.%m.%Y-%H:%M`\n" >> "$UPDATE_STATUS_FILE" rm -f "$UPDATE_PID_FILE" - return $_return_code - } Update () { - local _return_code=0 - 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}..." GetDataFiles - case $? in 0) echo " Blacklist updated" @@ -451,50 +386,74 @@ Update () { _return_code=1 ;; esac - fi - return $_return_code - } Start () { - echo " ${NAME} ${1}..." MakeLogRecord "${1}..." DropNetConfig &> /dev/null SetNetConfig PreStartCheck FillIpsets - } Stop () { - echo " ${NAME} ${1}..." MakeLogRecord "${1}..." DropNetConfig &> /dev/null - } RenewIpt () { - if [ -f "$INIT_SCRIPT" ]; then RemIptRules &> /dev/null AddIptRules &> /dev/null fi - } Status () { - local _set - local _call_iptables="${IPTCMD} -t ${IPT_TABLE} -v -L ${IPT_CHAIN}" - - [ "$1" = "html" -a "$USE_HTML_STATUS" != "1" ] && return 0 - - if [ "$1" = "html" ]; then + local _call_iptables="${IPT_CMD} -t ${IPT_TABLE} -v -L ${IPT_CHAIN}" + $_call_iptables &> /dev/null + if [ $? -eq 0 ]; then + printf "\n \033[1m${NAME} status\033[m: \033[1;32mActive\033[m\n\n PROXY_MODE: ${PROXY_MODE}\n DEF_TOTAL_PROXY: ${DEF_TOTAL_PROXY}\n BLLIST_MODULE_CMD: ${BLLIST_MODULE_CMD}\n" + [ -f "$UPDATE_STATUS_FILE" ] && $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" + + printf "\n \033[4mIptables rules\033[m:\n\n" + $_call_iptables | $AWK_CMD ' + { + if(NR > 2) { + match_set=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? "\033[1;31m"ENVIRON["IPSET_TOTAL_PROXY"]" (Enabled!)\033[m" : $11; + match_set_html=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? ENVIRON["IPSET_TOTAL_PROXY"]" (Enabled!)" : $11; + match_set_html_class=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? " red" : ""; + 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[1mOff\033[m\n\n" + exit 2 + fi +} +HtmlInfo () { + local _set + local _call_iptables="${IPT_CMD} -t ${IPT_TABLE} -v -L ${IPT_CHAIN}" + if [ "$HTML_INFO" = "1" -a -d "$HTML_DIR" ]; then cat << EOF > $HTML_OUTPUT @@ -514,108 +473,60 @@ span.info_label { display: block; padding: 0px 0px 10px 0px }
EOF - - fi - - $_call_iptables &> /dev/null - - if [ $? -eq 0 ]; then - - if [ "$1" = "html" ]; then + $_call_iptables &> /dev/null + if [ $? -eq 0 ]; then printf "
\n\ - \n\ - \n\ - \n\ - \n\ - \n" >> "$HTML_OUTPUT" - else - printf "\n \033[1m${NAME} status\033[m: \033[1;32mActive\033[m\n\n PROXY_MODE: ${PROXY_MODE}\n DEF_TOTAL_PROXY: ${DEF_TOTAL_PROXY}\n BLLIST_MODULE_CMD: ${BLLIST_MODULE_CMD}\n" - fi - - [ -f "$UPDATE_STATUS_FILE" ] && $AWKCMD -v TYPE="$1" '{ - update_string=(NF < 4) ? "No data" : $4" (ip: "$1" | CIDR: "$2" | FQDN: "$3")"; - if(TYPE == "html") - printf "\n", update_string >> ENVIRON["HTML_OUTPUT"]; - else - printf "\n Last blacklist update: %s\n", update_string; - }' "$UPDATE_STATUS_FILE" - - if [ "$1" = "html" ]; then + \n\ + \n\ + \n\ + \n\ + \n" >> "$HTML_OUTPUT" + [ -f "$UPDATE_STATUS_FILE" ] && $AWK_CMD '{ + update_string=(NF < 4) ? "No data" : $4" (ip: "$1" | CIDR: "$2" | FQDN: "$3")"; + printf "\n", update_string >> ENVIRON["HTML_OUTPUT"]; + }' "$UPDATE_STATUS_FILE" printf "
Last status update at:`date`
${NAME} status:Active
PROXY_MODE:${PROXY_MODE}
DEF_TOTAL_PROXY:${DEF_TOTAL_PROXY}
BLLIST_MODULE_CMD:${BLLIST_MODULE_CMD}
Last blacklist update:%s
Last status update at:`date`
${NAME} status:Active
PROXY_MODE:${PROXY_MODE}
DEF_TOTAL_PROXY:${DEF_TOTAL_PROXY}
BLLIST_MODULE_CMD:${BLLIST_MODULE_CMD}
Last blacklist update:%s
Iptables rules:" >> "$HTML_OUTPUT" - else - printf "\n \033[4mIptables rules\033[m:\n\n" - fi - - $_call_iptables | $AWKCMD -v TYPE="$1" ' - BEGIN { - if(TYPE == "html") + $_call_iptables | $AWK_CMD ' + BEGIN { printf "%s", "" >> ENVIRON["HTML_OUTPUT"]; - } - { - if(NR > 2) { - match_set=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? "\033[1;31m"ENVIRON["IPSET_TOTAL_PROXY"]" (Enabled!)\033[m" : $11; - match_set_html=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? ENVIRON["IPSET_TOTAL_PROXY"]" (Enabled!)" : $11; - match_set_html_class=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? " red" : ""; - if(TYPE == "html") + } + { + if(NR > 2) { + match_set=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? "\033[1;31m"ENVIRON["IPSET_TOTAL_PROXY"]" (Enabled!)\033[m" : $11; + match_set_html=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? ENVIRON["IPSET_TOTAL_PROXY"]" (Enabled!)" : $11; + match_set_html_class=(NR == 3 && $0 ~ ENVIRON["IPSET_TOTAL_PROXY"]) ? " red" : ""; printf "\n", match_set_html_class, match_set_html, $2 >> ENVIRON["HTML_OUTPUT"]; - else - printf " Match-set: %s\n Bytes: %s\n\n", match_set, $2; - }; - } - END { - if(TYPE == "html") + }; + } + END { printf "%s", "
Match-setBytes
%s%s
" >> ENVIRON["HTML_OUTPUT"]; - }' - - if [ "$1" = "html" ]; then + }' printf "%s" "
Ip sets:\ - \ - " >> "$HTML_OUTPUT" - else - printf " \033[4mIp sets\033[m:\n\n" - fi - - for _set in "$IPSET_TOTAL_PROXY" "$IPSET_CIDR_TMP" "$IPSET_CIDR" "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_DNSMASQ" "$IPSET_ONION" - do - $IPSETCMD list "$_set" -terse | $AWKCMD -F ":" -v TYPE="$1" ' - BEGIN { - if(TYPE == "html") +
NameSize in memoryNumber of entries
\ + " >> "$HTML_OUTPUT" + 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 ":" ' + BEGIN { printf "%s", "" >> ENVIRON["HTML_OUTPUT"]; - } - { - if($1 ~ /^(Name|Size in memory|Number of entries)/) { - if(TYPE == "html") { + } + { + if($1 ~ /^(Name|Size in memory|Number of entries)/) { align=($1 ~ /^Name/) ? "left" : "center"; printf "\n", align, $2 >> ENVIRON["HTML_OUTPUT"]; - } - else { - printf " %s: %s\n", $1, $2; - if($1 ~ /^Number of entries/) printf "\n"; }; - }; - } - END { - if(TYPE == "html") + } + END { printf "%s", "" >> ENVIRON["HTML_OUTPUT"]; - }' - done - - [ "$1" = "html" ] && printf "
NameSize in memoryNumber of entries
%s
" >> "$HTML_OUTPUT" - - else - - if [ "$1" = "html" ]; then - printf "
${NAME}status:Off
\n" >> "$HTML_OUTPUT" + }' + done + printf "
" >> "$HTML_OUTPUT" else - printf "\n \033[1m${NAME} status\033[m: \033[1mOff\033[m\n\n" + printf "
${NAME} status:Off
\n" >> "$HTML_OUTPUT" + exit 2 fi - - exit 2 - + printf "\n" >> "$HTML_OUTPUT" fi - - [ "$1" = "html" ] && printf "\n" >> "$HTML_OUTPUT" - } ############################# Run section ############################## @@ -623,24 +534,25 @@ EOF case "$1" in start|restart) Start "$1" - Status html + HtmlInfo ;; stop) Stop "$1" - Status html + HtmlInfo ;; destroy) Stop "$1" DestroyIpsets "$IPSET_TOTAL_PROXY" "$IPSET_CIDR_TMP" "$IPSET_CIDR" "$IPSET_IP_TMP" "$IPSET_IP" "$IPSET_DNSMASQ" "$IPSET_ONION" - Status html + HtmlInfo ;; renew-ipt) ### Костыль для post_iptables_script.sh RenewIpt + HtmlInfo ;; update|force-update) Update "$1" - Status html + HtmlInfo ;; data-files) if [ -e "$UPDATE_PID_FILE" ] && [ "$1" != "force-update" ]; then @@ -653,17 +565,17 @@ case "$1" in total-proxy-on) TotalProxyOff &> /dev/null TotalProxyOn - Status html + HtmlInfo ;; total-proxy-off) TotalProxyOff - Status html + HtmlInfo ;; status) Status ;; - status-html) - Status html + html-info) + HtmlInfo ;; -h|--help|help) Help