small-package/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua

362 lines
10 KiB
Lua

local api = require "luci.passwall2.api"
local appname = "passwall2"
local uci = api.uci
local sys = api.sys
local fs = api.fs
local datatypes = api.datatypes
local TMP = {}
local function tinsert(table_name, val)
if table_name and type(table_name) == "table" then
if not TMP[table_name] then
TMP[table_name] = {}
end
if TMP[table_name][val] then
return false
end
table.insert(table_name, val)
TMP[table_name][val] = true
return true
end
return false
end
local function backup_servers()
local DNSMASQ_DNS = uci:get("dhcp", "@dnsmasq[0]", "server")
if DNSMASQ_DNS and #DNSMASQ_DNS > 0 then
uci:set(appname, "@global[0]", "dnsmasq_servers", DNSMASQ_DNS)
api.uci_save(uci, appname, true)
end
end
local function restore_servers()
local dns_table = {}
local DNSMASQ_DNS = uci:get("dhcp", "@dnsmasq[0]", "server")
if DNSMASQ_DNS and #DNSMASQ_DNS > 0 then
for k, v in ipairs(DNSMASQ_DNS) do
tinsert(dns_table, v)
end
end
local OLD_SERVER = uci:get(appname, "@global[0]", "dnsmasq_servers")
if OLD_SERVER and #OLD_SERVER > 0 then
for k, v in ipairs(OLD_SERVER) do
tinsert(dns_table, v)
end
uci:delete(appname, "@global[0]", "dnsmasq_servers")
api.uci_save(uci, appname, true)
end
if dns_table and #dns_table > 0 then
uci:set_list("dhcp", "@dnsmasq[0]", "server", dns_table)
api.uci_save(uci, "dhcp", true)
end
end
function stretch()
local dnsmasq_server = uci:get("dhcp", "@dnsmasq[0]", "server")
local dnsmasq_noresolv = uci:get("dhcp", "@dnsmasq[0]", "noresolv")
local _flag
if dnsmasq_server and #dnsmasq_server > 0 then
for k, v in ipairs(dnsmasq_server) do
if not v:find("/") then
_flag = true
end
end
end
if not _flag and dnsmasq_noresolv == "1" then
uci:delete("dhcp", "@dnsmasq[0]", "noresolv")
local RESOLVFILE = "/tmp/resolv.conf.d/resolv.conf.auto"
local file = io.open(RESOLVFILE, "r")
if not file then
RESOLVFILE = "/tmp/resolv.conf.auto"
else
local size = file:seek("end")
file:close()
if size == 0 then
RESOLVFILE = "/tmp/resolv.conf.auto"
end
end
uci:set("dhcp", "@dnsmasq[0]", "resolvfile", RESOLVFILE)
api.uci_save(uci, "dhcp", true)
end
end
function restart(var)
local LOG = var["-LOG"]
sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
if LOG == "1" then
api.log("重启 dnsmasq 服务")
end
end
function logic_restart(var)
local LOG = var["-LOG"]
local DEFAULT_DNS = api.get_cache_var("DEFAULT_DNS")
if DEFAULT_DNS then
backup_servers()
--sys.call("sed -i '/list server/d' /etc/config/dhcp >/dev/null 2>&1")
local dns_table = {}
local dnsmasq_server = uci:get("dhcp", "@dnsmasq[0]", "server")
if dnsmasq_server and #dnsmasq_server > 0 then
for k, v in ipairs(dnsmasq_server) do
if v:find("/") then
tinsert(dns_table, v)
end
end
uci:set_list("dhcp", "@dnsmasq[0]", "server", dns_table)
api.uci_save(uci, "dhcp", true)
end
sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
restore_servers()
else
sys.call("/etc/init.d/dnsmasq restart >/dev/null 2>&1")
end
if LOG == "1" then
api.log("重启 dnsmasq 服务")
end
end
function copy_instance(var)
local LISTEN_PORT = var["-LISTEN_PORT"]
local TMP_DNSMASQ_PATH = var["-TMP_DNSMASQ_PATH"]
local conf_lines = {}
local DEFAULT_DNSMASQ_CFGID = sys.exec("echo -n $(uci -q show dhcp.@dnsmasq[0] | awk 'NR==1 {split($0, conf, /[.=]/); print conf[2]}')")
for line in io.lines("/tmp/etc/dnsmasq.conf." .. DEFAULT_DNSMASQ_CFGID) do
local filter
if line:find("passwall2") then filter = true end
if line:find("ubus") then filter = true end
if line:find("dhcp") then filter = true end
if line:find("server=") == 1 then filter = true end
if line:find("port=") == 1 then filter = true end
if line:find("conf%-dir=") == 1 then
filter = true
if TMP_DNSMASQ_PATH then
local tmp_path = line:sub(1 + #"conf-dir=")
sys.call(string.format("cp -r %s/* %s/ 2>/dev/null", tmp_path, TMP_DNSMASQ_PATH))
end
end
if line:find("address=") == 1 or (line:find("server=") == 1 and line:find("/")) then filter = nil end
if not filter then
tinsert(conf_lines, line)
end
end
tinsert(conf_lines, "port=" .. LISTEN_PORT)
if TMP_DNSMASQ_PATH then
sys.call("rm -rf " .. TMP_DNSMASQ_PATH .. "/*passwall*")
end
if var["-return"] == "1" then
return conf_lines
end
if #conf_lines > 0 then
local DNSMASQ_CONF = var["-DNSMASQ_CONF"]
local conf_out = io.open(DNSMASQ_CONF, "a")
conf_out:write(table.concat(conf_lines, "\n"))
conf_out:write("\n")
conf_out:close()
end
end
function add_rule(var)
local FLAG = var["-FLAG"]
local TMP_DNSMASQ_PATH = var["-TMP_DNSMASQ_PATH"]
local DNSMASQ_CONF_FILE = var["-DNSMASQ_CONF_FILE"]
local LISTEN_PORT = var["-LISTEN_PORT"]
local DEFAULT_DNS = var["-DEFAULT_DNS"]
local LOCAL_DNS = var["-LOCAL_DNS"]
local TUN_DNS = var["-TUN_DNS"]
local NO_LOGIC_LOG = var["-NO_LOGIC_LOG"]
local NFTFLAG = var["-NFTFLAG"]
local CACHE_PATH = api.CACHE_PATH
local CACHE_FLAG = "dnsmasq_" .. FLAG
local CACHE_DNS_PATH = CACHE_PATH .. "/" .. CACHE_FLAG
local CACHE_TEXT_FILE = CACHE_DNS_PATH .. ".txt"
local list1 = {}
local excluded_domain = {}
local excluded_domain_str = "!"
local function check_dns(domain, dns)
if domain == "" or domain:find("#") then
return false
end
if not dns then
return
end
for k,v in ipairs(list1[domain].dns) do
if dns == v then
return true
end
end
return false
end
local function check_ipset(domain, ipset)
if domain == "" or domain:find("#") then
return false
end
if not ipset then
return
end
for k,v in ipairs(list1[domain].ipsets) do
if ipset == v then
return true
end
end
return false
end
local function set_domain_dns(domain, dns)
if domain == "" or domain:find("#") then
return
end
if not dns then
return
end
if not list1[domain] then
list1[domain] = {
dns = {},
ipsets = {}
}
end
for line in string.gmatch(dns, '[^' .. "," .. ']+') do
if not check_dns(domain, line) then
table.insert(list1[domain].dns, line)
end
end
end
local function set_domain_ipset(domain, ipset)
if domain == "" or domain:find("#") then
return
end
if not ipset then
return
end
if not list1[domain] then
list1[domain] = {
dns = {},
ipsets = {}
}
end
for line in string.gmatch(ipset, '[^' .. "," .. ']+') do
if not check_ipset(domain, line) then
table.insert(list1[domain].ipsets, line)
end
end
end
local cache_text = ""
local nodes_address_md5 = sys.exec("echo -n $(uci show passwall2 | grep '\\.address') | md5sum")
local new_text = TMP_DNSMASQ_PATH .. DNSMASQ_CONF_FILE .. DEFAULT_DNS .. LOCAL_DNS .. TUN_DNS .. nodes_address_md5 .. NFTFLAG
if fs.access(CACHE_TEXT_FILE) then
for line in io.lines(CACHE_TEXT_FILE) do
cache_text = line
end
end
if cache_text ~= new_text then
api.remove(CACHE_DNS_PATH .. "*")
end
local dnsmasq_default_dns = TUN_DNS
local setflag_4= (NFTFLAG == "1") and "4#inet#passwall2#" or ""
local setflag_6= (NFTFLAG == "1") and "6#inet#passwall2#" or ""
if not fs.access(CACHE_DNS_PATH) then
fs.mkdir(CACHE_DNS_PATH)
local fwd_dns
--始终用国内DNS解析节点域名
if true then
fwd_dns = LOCAL_DNS
uci:foreach(appname, "nodes", function(t)
local function process_address(address)
if address == "engage.cloudflareclient.com" then return end
if datatypes.hostname(address) then
set_domain_dns(address, fwd_dns)
set_domain_ipset(address, setflag_4 .. "passwall2_vps," .. setflag_6 .. "passwall2_vps6")
end
end
process_address(t.address)
process_address(t.download_address)
end)
end
if list1 and next(list1) then
local server_out = io.open(CACHE_DNS_PATH .. "/001-server.conf", "a")
local ipset_out = io.open(CACHE_DNS_PATH .. "/ipset.conf", "a")
local set_name = "ipset"
if NFTFLAG == "1" then
set_name = "nftset"
end
for key, value in pairs(list1) do
if value.dns and #value.dns > 0 then
for i, dns in ipairs(value.dns) do
server_out:write(string.format("server=/.%s/%s", key, dns) .. "\n")
end
end
if value.ipsets and #value.ipsets > 0 then
local ipsets_str = ""
for i, ipset in ipairs(value.ipsets) do
ipsets_str = ipsets_str .. ipset .. ","
end
ipsets_str = ipsets_str:sub(1, #ipsets_str - 1)
ipset_out:write(string.format("%s=/.%s/%s", set_name, key, ipsets_str) .. "\n")
end
end
server_out:close()
ipset_out:close()
end
local f_out = io.open(CACHE_TEXT_FILE, "a")
f_out:write(new_text)
f_out:close()
end
api.remove(TMP_DNSMASQ_PATH)
fs.symlink(CACHE_DNS_PATH, TMP_DNSMASQ_PATH)
if DNSMASQ_CONF_FILE ~= "nil" then
local conf_lines = {}
if LISTEN_PORT then
--Copy dnsmasq instance
conf_lines = copy_instance({["-LISTEN_PORT"] = LISTEN_PORT, ["-TMP_DNSMASQ_PATH"] = TMP_DNSMASQ_PATH, ["-return"] = "1"})
else
--Modify the default dnsmasq service
end
tinsert(conf_lines, string.format("conf-dir=%s", TMP_DNSMASQ_PATH))
if dnsmasq_default_dns then
for s in string.gmatch(dnsmasq_default_dns, '[^' .. "," .. ']+') do
tinsert(conf_lines, string.format("server=%s", s))
end
tinsert(conf_lines, "all-servers")
tinsert(conf_lines, "no-poll")
tinsert(conf_lines, "no-resolv")
if FLAG == "default" then
api.set_cache_var("DEFAULT_DNS", DEFAULT_DNS)
end
end
if #conf_lines > 0 then
local conf_out = io.open(DNSMASQ_CONF_FILE, "a")
conf_out:write(table.concat(conf_lines, "\n"))
conf_out:write("\n")
conf_out:close()
end
end
end
_G.stretch = stretch
_G.restart = restart
_G.logic_restart = logic_restart
_G.copy_instance = copy_instance
_G.add_rule = add_rule
if arg[1] then
local func =_G[arg[1]]
if func then
func(api.get_function_args(arg))
end
end