From 4ae7d07a078e2c6854d28a8823d322146387fc57 Mon Sep 17 00:00:00 2001 From: gitea-action Date: Thu, 19 Dec 2024 16:30:22 +0800 Subject: [PATCH] luci-app-passwall2: sync upstream last commit: https://github.com/xiaorouji/openwrt-passwall2/commit/cf379edf660f506bce24e361c06f3ce0f3450e99 --- luci-app-passwall2/Makefile | 4 +- luci-app-passwall2/luasrc/passwall2/api.lua | 22 ++ .../root/usr/share/passwall2/app.sh | 68 +--- .../usr/share/passwall2/helper_dnsmasq.lua | 355 ++++++++++++++++++ 4 files changed, 393 insertions(+), 56 deletions(-) create mode 100644 luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index 29fe81722..2892f69f3 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -5,8 +5,8 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 -PKG_VERSION:=24.12.16 -PKG_RELEASE:=2 +PKG_VERSION:=24.12.19 +PKG_RELEASE:=1 PKG_CONFIG_DEPENDS:= \ CONFIG_PACKAGE_$(PKG_NAME)_Iptables_Transparent_Proxy \ diff --git a/luci-app-passwall2/luasrc/passwall2/api.lua b/luci-app-passwall2/luasrc/passwall2/api.lua index 7a1b85a40..a5939ed7f 100644 --- a/luci-app-passwall2/luasrc/passwall2/api.lua +++ b/luci-app-passwall2/luasrc/passwall2/api.lua @@ -207,6 +207,28 @@ function repeat_exist(table, value) return false end +function remove(...) + for index, value in ipairs({...}) do + if value and #value > 0 and value ~= "/" then + sys.call(string.format("rm -rf %s", value)) + end + end +end + +function is_install(package) + if package and #package > 0 then + local file_path = "/usr/lib/opkg/info" + local file_ext = ".control" + local has = sys.call("[ -d " .. file_path .. " ]") + if has == 0 then + file_path = "/lib/apk/packages" + file_ext = ".list" + end + return sys.call(string.format('[ -s "%s/%s%s" ]', file_path, package, file_ext)) == 0 + end + return false +end + function get_args(arg) local var = {} for i, arg_k in pairs(arg) do diff --git a/luci-app-passwall2/root/usr/share/passwall2/app.sh b/luci-app-passwall2/root/usr/share/passwall2/app.sh index 3ebe1e57e..3ea4c2fde 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/app.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/app.sh @@ -991,73 +991,33 @@ start_haproxy() { [ "$(config_t_get global_haproxy balancing_enable 0)" != "1" ] && return haproxy_path=$TMP_PATH/haproxy haproxy_conf="config.cfg" - lua $APP_PATH/haproxy.lua -path ${haproxy_path} -conf ${haproxy_conf} -dns ${LOCAL_DNS} + lua $APP_PATH/haproxy.lua -path ${haproxy_path} -conf ${haproxy_conf} -dns ${LOCAL_DNS:-${AUTO_DNS}} ln_run "$(first_type haproxy)" haproxy "/dev/null" -f "${haproxy_path}/${haproxy_conf}" } -run_ipset_dns_server() { - if [ -n "$(first_type chinadns-ng)" ]; then - run_ipset_chinadns_ng $@ - else - run_ipset_dnsmasq $@ - fi -} - -gen_dnsmasq_items() { - local dnss settype setnames outf ipsetoutf - eval_set_val $@ - - awk -v dnss="${dnss}" -v settype="${settype}" -v setnames="${setnames}" -v outf="${outf}" -v ipsetoutf="${ipsetoutf}" ' - BEGIN { - if(outf == "") outf="/dev/stdout"; - if(ipsetoutf == "") ipsetoutf=outf; - split(dnss, dns, ","); setdns=length(dns)>0; setlist=length(setnames)>0; - if(setdns) for(i in dns) if(length(dns[i])==0) delete dns[i]; - fail=1; - } - ! /^$/&&!/^#/ { - fail=0 - if(setdns) for(i in dns) printf("server=/.%s/%s\n", $0, dns[i]) >>outf; - if(setlist) printf("%s=/.%s/%s\n", settype, $0, setnames) >>ipsetoutf; - } - END {fflush(outf); close(outf); fflush(ipsetoutf); close(ipsetoutf); exit(fail);} - ' -} - run_copy_dnsmasq() { local flag listen_port tun_dns eval_set_val $@ local dnsmasq_conf=$TMP_ACL_PATH/$flag/dnsmasq.conf local dnsmasq_conf_path=$TMP_ACL_PATH/$flag/dnsmasq.d mkdir -p $dnsmasq_conf_path - [ -s "/tmp/etc/dnsmasq.conf.${DEFAULT_DNSMASQ_CFGID}" ] && { - cp -r /tmp/etc/dnsmasq.conf.${DEFAULT_DNSMASQ_CFGID} $dnsmasq_conf - sed -i "/passwall2/d" $dnsmasq_conf - sed -i "/ubus/d" $dnsmasq_conf - sed -i "/dhcp/d" $dnsmasq_conf - sed -i "/port=/d" $dnsmasq_conf - sed -i "/server=/d" $dnsmasq_conf - } - local set_type="ipset" - [ "${nftflag}" = "1" ] && { - set_type="nftset" - local setflag_4="4#inet#passwall2#" - local setflag_6="6#inet#passwall2#" - } - cat <<-EOF >> $dnsmasq_conf - port=${listen_port} - conf-dir=${dnsmasq_conf_path} - server=${tun_dns} - no-poll - no-resolv - EOF - awk '!seen[$0]++' $dnsmasq_conf > /tmp/dnsmasq.tmp && mv /tmp/dnsmasq.tmp $dnsmasq_conf - node_servers=$(uci show "${CONFIG}" | grep -E "(.address=|.download_address=)" | cut -d "'" -f 2) - hosts_foreach "node_servers" host_from_url | grep '[a-zA-Z]$' | sort -u | grep -v "engage.cloudflareclient.com" | gen_dnsmasq_items settype="${set_type}" setnames="${setflag_4}passwall2_vpslist,${setflag_6}passwall2_vpslist6" dnss="${LOCAL_DNS:-${AUTO_DNS}}" outf="${dnsmasq_conf_path}/10-vpslist_host.conf" ipsetoutf="${dnsmasq_conf_path}/ipset.conf" + lua $APP_PATH/helper_dnsmasq.lua copy_instance -LISTEN_PORT ${listen_port} -DNSMASQ_CONF ${dnsmasq_conf} + lua $APP_PATH/helper_dnsmasq.lua add_rule -FLAG "${flag}" -TMP_DNSMASQ_PATH ${dnsmasq_conf_path} -DNSMASQ_CONF_FILE ${dnsmasq_conf} \ + -DEFAULT_DNS ${AUTO_DNS} -LOCAL_DNS ${LOCAL_DNS:-${AUTO_DNS}} -TUN_DNS ${tun_dns} \ + -NFTFLAG ${nftflag:-0} \ + -NO_LOGIC_LOG ${NO_LOGIC_LOG:-0} ln_run "$(first_type dnsmasq)" "dnsmasq_${flag}" "/dev/null" -C $dnsmasq_conf -x $TMP_ACL_PATH/$flag/dnsmasq.pid set_cache_var "ACL_${flag}_dns_port" "${listen_port}" } +run_ipset_dns_server() { + if [ -n "$(first_type chinadns-ng)" ]; then + run_ipset_chinadns_ng $@ + else + run_ipset_dnsmasq $@ + fi +} + run_ipset_chinadns_ng() { local listen_port server_dns ipset nftset config_file eval_set_val $@ diff --git a/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua b/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua new file mode 100644 index 000000000..9e93b68ef --- /dev/null +++ b/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua @@ -0,0 +1,355 @@ +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) + uci:commit(appname) + 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") + uci:commit(appname) + end + if dns_table and #dns_table > 0 then + uci:set_list("dhcp", "@dnsmasq[0]", "server", dns_table) + uci:commit("dhcp") + 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) + uci:commit("dhcp") + 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 + if dns_table and #dns_table > 0 then + uci:set_list("dhcp", "@dnsmasq[0]", "server", dns_table) + uci:commit("dhcp") + end + 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 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("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 var["-return_table"] == "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_vpslist," .. setflag_6 .. "passwall2_vpslist6") + 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 + + if api.is_install("procd\\-ujail") then + fs.copyr(CACHE_DNS_PATH, TMP_DNSMASQ_PATH) + else + api.remove(TMP_DNSMASQ_PATH) + fs.symlink(CACHE_DNS_PATH, TMP_DNSMASQ_PATH) + end + + if DNSMASQ_CONF_FILE ~= "nil" then + local conf_lines = {} + if LISTEN_PORT then + --Copy dnsmasq instance + conf_lines = copy_instance({["-LISTEN_PORT"] = LISTEN_PORT, ["-return_table"] = "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: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