From 9122882ea3f842d6875ede079d1dccc64c7a99db Mon Sep 17 00:00:00 2001 From: action Date: Fri, 16 Oct 2026 00:09:36 +0800 Subject: [PATCH] update 2026-10-16 00:09:36 --- .../model/cbi/passwall/client/global.lua | 24 +++++-- .../luasrc/view/passwall/global/footer.htm | 32 +++++++++ .../root/usr/share/passwall/app.sh | 71 +++++++++++++++---- .../share/passwall/helper_smartdns_add.lua | 52 ++++++++------ openlist2/Makefile | 8 +-- 5 files changed, 144 insertions(+), 43 deletions(-) diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua index 5f3aef17..79ebb0f8 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/global.lua @@ -188,9 +188,6 @@ o:value("udp", translatef("Requery DNS By %s", "UDP")) o:value("tcp", translatef("Requery DNS By %s", "TCP")) o:depends({dns_shunt = "dnsmasq"}) o:depends({dns_shunt = "chinadns-ng"}) -if has_smartdns then - o:depends({dns_shunt = "smartdns"}) -end o = s:taboption("DNS", Value, "direct_dns", translate("Direct DNS")) o.datatype = "or(ipaddr,ipaddrport(1))" @@ -202,8 +199,25 @@ o:value("180.184.2.2") o:value("114.114.114.114") o:value("114.114.115.115") o:value("119.28.28.28") -o:depends("direct_dns_mode", "udp") -o:depends("direct_dns_mode", "tcp") +o:depends({dns_shunt = "dnsmasq", direct_dns_mode = "udp"}) +o:depends({dns_shunt = "dnsmasq", direct_dns_mode = "tcp"}) +o:depends({dns_shunt = "chinadns-ng", direct_dns_mode = "udp"}) +o:depends({dns_shunt = "chinadns-ng", direct_dns_mode = "tcp"}) + +if has_smartdns then + o = s:taboption("DNS", DynamicList, "smartdns_direct_dns", translate("Direct DNS")) + o.placeholder = translate("Auto") + o.rmempty = true + o:value("223.5.5.5") + o:value("119.29.29.29") + o:value("tcp://223.5.5.5") + o:value("tcp://114.114.114.114") + o:value("tls://dot.pub,1.12.12.12") + o:value("tls://dns.alidns.com,223.5.5.5") + o:value("https://223.5.5.5/dns-query") + o:value("https://1.12.12.12/dns-query") + o:depends({dns_shunt = "smartdns"}) +end o = s:taboption("DNS", Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6")) o.default = "0" diff --git a/luci-app-passwall/luasrc/view/passwall/global/footer.htm b/luci-app-passwall/luasrc/view/passwall/global/footer.htm index bdacc409..0962d5ee 100644 --- a/luci-app-passwall/luasrc/view/passwall/global/footer.htm +++ b/luci-app-passwall/luasrc/view/passwall/global/footer.htm @@ -229,5 +229,37 @@ local sid = "@global[0]" select.insertAdjacentElement("afterend", logLink); }; })(); + + (function () { + function initSmartdnsDirectAuto() { + const field = document.getElementById("cbi-<%=appname%>-" + global_id + "-smartdns_direct_dns"); + if (!field) return; + + const update = () => { + const list = field.querySelector(".cbi-dynlist"); + if (!list) return; + const hasDns = Array.from(list.querySelectorAll('.item > input[type="hidden"]')) + .some(input => input.value.trim() !== ""); + const text = hasDns ? <%=api.jsonc.stringify(translate("-- Please choose --"))%> : "<%:Auto%>"; + const placeholder = list.querySelector(".add-item .cbi-dropdown > ul > li[placeholder]"); + // 只修改空选项的提示文字,不添加 DNS 条目,也不改变正在输入的内容。 + if (placeholder && placeholder.textContent !== text) { + placeholder.textContent = text; + } + const input = list.querySelector('.add-item > input[type="text"]'); + if (input) input.placeholder = hasDns ? "" : "<%:Auto%>"; + }; + + // 同时处理 LuCI 延迟渲染,以及添加、删除 DNS 后的空列表状态。 + new MutationObserver(update).observe(field, { childList: true, subtree: true }); + update(); + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", initSmartdnsDirectAuto, { once: true }); + } else { + initSmartdnsDirectAuto(); + } + })(); //]]> diff --git a/luci-app-passwall/root/usr/share/passwall/app.sh b/luci-app-passwall/root/usr/share/passwall/app.sh index 6d210bbd..c2ab984b 100755 --- a/luci-app-passwall/root/usr/share/passwall/app.sh +++ b/luci-app-passwall/root/usr/share/passwall/app.sh @@ -658,10 +658,11 @@ start_global() { local _remote_dns_client_ip=$(config_n_get @global[0] remote_dns_client_ip) [ -n "${_remote_dns_client_ip}" ] && _args="${_args} remote_dns_client_ip=${_remote_dns_client_ip}" [ "${DNS_CACHE}" = "0" ] && _args="${_args} dns_cache=0" - resolve_dns_port=${NEXT_DNS_LISTEN_PORT} + resolve_dns_port=$(get_new_port "$NEXT_DNS_LISTEN_PORT" tcp,udp) + [ -n "$resolve_dns_port" ] || return 1 _args="${_args} dns_listen_port=${resolve_dns_port}" - case "$(config_n_get @global[0] direct_dns_mode "auto")" in + case "$(get_direct_dns_mode)" in udp) _args="${_args} direct_dns_udp_server=$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)")" ;; @@ -694,7 +695,7 @@ start_global() { USE_FAKEDNS=1 } _args="${_args} remote_rewrite_ttl=$(config_n_get @global[0] remote_rewrite_ttl)" - NEXT_DNS_LISTEN_PORT=$(expr $NEXT_DNS_LISTEN_PORT + 1) + NEXT_DNS_LISTEN_PORT=$(expr $resolve_dns_port + 1) } _args="${_args} use_proxy_list=$USE_PROXY_LIST use_gfw_list=$USE_GFW_LIST chn_list=$CHN_LIST" run_singbox flag=$_flag node=$NODE redir_port=$REDIR_PORT tcp_proxy_way=$TCP_PROXY_WAY config_file=$config_file log_file=$log_file ${_args} @@ -741,10 +742,11 @@ start_global() { local _remote_dns_client_ip=$(config_n_get @global[0] remote_dns_client_ip) [ -n "${_remote_dns_client_ip}" ] && _args="${_args} remote_dns_client_ip=${_remote_dns_client_ip}" [ "${DNS_CACHE}" = "0" ] && _args="${_args} dns_cache=0" - resolve_dns_port=${NEXT_DNS_LISTEN_PORT} + resolve_dns_port=$(get_new_port "$NEXT_DNS_LISTEN_PORT" tcp,udp) + [ -n "$resolve_dns_port" ] || return 1 _args="${_args} dns_listen_port=${resolve_dns_port}" - case "$(config_n_get @global[0] direct_dns_mode "auto")" in + case "$(get_direct_dns_mode)" in udp) _args="${_args} direct_dns_udp_server=$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)")" ;; @@ -773,7 +775,7 @@ start_global() { resolve_dns_log="${resolve_dns_log} + FakeDNS" USE_FAKEDNS=1 } - NEXT_DNS_LISTEN_PORT=$(expr $NEXT_DNS_LISTEN_PORT + 1) + NEXT_DNS_LISTEN_PORT=$(expr $resolve_dns_port + 1) } _args="${_args} use_proxy_list=$USE_PROXY_LIST use_gfw_list=$USE_GFW_LIST chn_list=$CHN_LIST" run_xray flag=$_flag node=$NODE redir_port=$REDIR_PORT tcp_proxy_way=$TCP_PROXY_WAY config_file=$config_file log_file=$log_file ${_args} @@ -1066,12 +1068,21 @@ stop_crontab() { #echolog "清除定时执行命令。" } +get_direct_dns_mode() { + # SmartDNS 使用独立直连列表,其余 DNS 分流模式继续读取原来的设置。 + if [ "$ENABLED_DEFAULT_ACL" = "1" ] && [ "$DNS_SHUNT" = "smartdns" ]; then + echo auto + else + config_n_get @global[0] direct_dns_mode "auto" + fi +} + start_dns() { echolog "DNS域名解析:" local china_ng_local_dns="${LOCAL_DNS}" local v2ray_local_dns - local direct_dns_mode=$(config_n_get @global[0] direct_dns_mode "auto") + local direct_dns_mode=$(get_direct_dns_mode) #获取访问控制节点所使用的DNS分流模式 local ACL_RULE_DNSMASQ=0 @@ -1267,10 +1278,11 @@ start_dns() { } local smartdns_config="${GLOBAL_ACL_PATH}/smartdns.conf" local subnet_ip=$(config_n_get @global[0] remote_dns_client_ip) - echolog " - SmartDNS(127.0.0.1#${smartdns_listen_port}),直连 DNS:${china_ng_local_dns} -> 分组(direct)" + echolog " - SmartDNS(127.0.0.1#${smartdns_listen_port}),直连 DNS:${SMARTDNS_DIRECT_DNS} -> 分组(direct)" + [ "${SMARTDNS_LOCAL_PORT:-0}" != "0" ] && echolog " - SmartDNS 直连入口:${LOCAL_DNS} -> 分组(direct)" lua "$APP_PATH/helper_smartdns_add.lua" -FLAG "default" \ - -LISTEN_PORT "$smartdns_listen_port" -LOCAL_DNS "$china_ng_local_dns" -DNS_REDIRECT "${DNS_REDIRECT}" \ + -LISTEN_PORT "$smartdns_listen_port" -DIRECT_DNS "$SMARTDNS_DIRECT_DNS" -LOCAL_PORT "${SMARTDNS_LOCAL_PORT:-0}" -DNS_REDIRECT "${DNS_REDIRECT}" \ -REMOTE_PROXY_SERVER "${GLOBAL_SOCKS_server}" -USE_DEFAULT_DNS "${smartdns_default_dns}" \ -REMOTE_DNS "${smartdns_remote_dns}" -DNS_MODE "${DNS_MODE:-socks}" -TUN_DNS "${TUN_DNS}" \ -USE_DIRECT_LIST "${USE_DIRECT_LIST}" -USE_PROXY_LIST "${USE_PROXY_LIST}" -USE_BLOCK_LIST "${USE_BLOCK_LIST}" -USE_GFW_LIST "${USE_GFW_LIST}" -CHN_LIST "${CHN_LIST}" \ @@ -1567,7 +1579,7 @@ acl_app() { _china_ng_listen="127.0.0.1#${chinadns_port},::1#${chinadns_port}" _chinadns_local_dns="${LOCAL_DNS}" - _direct_dns_mode=$(config_n_get @global[0] direct_dns_mode "auto") + _direct_dns_mode=$(get_direct_dns_mode) case "${_direct_dns_mode}" in udp) _chinadns_local_dns=$(normalize_dns "$(config_n_get @global[0] direct_dns 223.5.5.5:53)") @@ -1720,7 +1732,11 @@ start() { fi [ "$ENABLED_DEFAULT_ACL" = 1 ] && { mkdir -p ${GLOBAL_ACL_PATH} - start_global + start_global || { + echolog "全局节点启动失败,停止服务。" + stop + return 1 + } start_dns || { echolog "DNS 启动失败,停止服务。" stop @@ -1856,7 +1872,7 @@ get_config() { REDIRECT_LIST="socks ss-rust ssr sing-box xray naiveproxy hysteria2" TPROXY_LIST="socks ss-rust ssr sing-box xray hysteria2" - NEXT_DNS_LISTEN_PORT=15353 + NEXT_DNS_LISTEN_PORT=$(get_new_port 15353 tcp,udp) TUN_DNS="127.0.0.1#${NEXT_DNS_LISTEN_PORT}" DNS_CACHE=0 DIRECT_DNS_QUERY_STRATEGY="UseIP" @@ -1878,6 +1894,37 @@ get_config() { fi set_cache_var GLOBAL_DNSMASQ_CONF ${DNSMASQ_CONF_DIR}/dnsmasq-${CONFIG}.conf set_cache_var GLOBAL_DNSMASQ_CONF_PATH ${GLOBAL_ACL_PATH}/dnsmasq.d + + SMARTDNS_LOCAL_PORT=0 + SMARTDNS_DIRECT_DNS= + if [ "$ENABLED_DEFAULT_ACL" = "1" ] && [ "$DNS_SHUNT" = "smartdns" ]; then + # 显式列出空白字符,兼容未启用字符类支持的 BusyBox tr。 + SMARTDNS_DIRECT_DNS=$(config_n_get @global[0] smartdns_direct_dns | tr -s ' \t\r\n\v\f' '|' | sed 's/^|//;s/|$//') + [ "$SMARTDNS_DIRECT_DNS" = "nil" ] && SMARTDNS_DIRECT_DNS= + if [ -z "$SMARTDNS_DIRECT_DNS" ]; then + SMARTDNS_DIRECT_DNS="$LOCAL_DNS" + else + local smartdns_single_dns="${SMARTDNS_DIRECT_DNS%%,*}" + local smartdns_protocol=udp + case "$smartdns_single_dns" in + *://*) + smartdns_protocol=$(printf '%s' "${smartdns_single_dns%%://*}" | tr 'A-Z' 'a-z') + smartdns_single_dns="${smartdns_single_dns#*://}" + ;; + esac + if [ "${SMARTDNS_DIRECT_DNS#*|}" = "$SMARTDNS_DIRECT_DNS" ] && [ "$smartdns_protocol" = "udp" ]; then + LOCAL_DNS=$(normalize_dns "$smartdns_single_dns" 53) + local smartdns_host_ip="${SMARTDNS_DIRECT_DNS#*,}" + [ "$smartdns_host_ip" != "$SMARTDNS_DIRECT_DNS" ] && [ -n "$smartdns_host_ip" ] && \ + LOCAL_DNS=$(normalize_dns "$smartdns_host_ip" "${LOCAL_DNS##*#}") + else + SMARTDNS_LOCAL_PORT=${NEXT_DNS_LISTEN_PORT} + NEXT_DNS_LISTEN_PORT=$(expr $NEXT_DNS_LISTEN_PORT + 1) + LOCAL_DNS="127.0.0.1#${SMARTDNS_LOCAL_PORT}" + fi + fi + set_cache_var "LOCAL_DNS" "$LOCAL_DNS" + fi } get_local_dns() { diff --git a/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua b/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua index ed463698..737bc157 100644 --- a/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua +++ b/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua @@ -2,7 +2,8 @@ local api = require "luci.passwall.api" local var = api.get_args(arg) local FLAG = var["-FLAG"] -local LOCAL_DNS = var["-LOCAL_DNS"] +local DIRECT_DNS = var["-DIRECT_DNS"] +local LOCAL_PORT = tonumber(var["-LOCAL_PORT"]) or 0 local REMOTE_PROXY_SERVER = var["-REMOTE_PROXY_SERVER"] local USE_DEFAULT_DNS = var["-USE_DEFAULT_DNS"] local REMOTE_DNS = var["-REMOTE_DNS"] @@ -132,12 +133,14 @@ if data_dir ~= SMARTDNS_DATA_PATH then sys.call("mkdir -p " .. api.util.shellquote(data_dir)) end -local LOCAL_SERVERS = {} -for endpoint in string.gmatch(LOCAL_DNS or "", "[^,]+") do - endpoint = api.trim(endpoint) - local protocol, address = endpoint:match("^(%w+)://(.+)$") - protocol = protocol and protocol:lower() or "udp" - address = address or endpoint +-- 统一生成直连、远程上游,协议和默认端口由 SmartDNS 根据 URL 识别。 +local function make_server_param(endpoint) + local dns = api.split(api.trim(endpoint), ",") + local address = api.trim(dns[1] or "") + if address == "" then return nil end + local protocol, host_address = address:match("^(%w+)://(.+)$") + protocol = protocol and protocol:lower() + address = host_address or address local host, port = address:match("^(.-)#(%d+)$") if host then if host:find(":", 1, true) and not host:find("[", 1, true) then host = "[" .. host .. "]" end @@ -145,18 +148,27 @@ for endpoint in string.gmatch(LOCAL_DNS or "", "[^,]+") do elseif datatypes.ip6addr(address) and not address:find("[", 1, true) then address = "[" .. address .. "]" end - if (protocol == "udp" or protocol == "tcp") and address ~= "" then - table.insert(LOCAL_SERVERS, "server " .. (protocol == "tcp" and "tcp://" or "") .. address) + local server_param = "server " .. (protocol and (protocol .. "://") or "") .. address + if dns[2] and dns[2] ~= "" then + -- 保留 URL 中的域名,固定连接 IP 交给 SmartDNS 处理,避免手动替换影响 SNI 和 HTTP Host。 + server_param = server_param .. " -host-ip " .. api.trim(dns[2]):gsub("^%[(.*)%]$", "%1") + end + return server_param +end + +local DIRECT_SERVERS = {} +for endpoint in string.gmatch(DIRECT_DNS or "", "[^|]+") do + local server_param = make_server_param(endpoint) + if server_param then + -- 复用 PassWall 的直连标记,使所有协议及动态域名上游绕过本机透明代理。 + table.insert(DIRECT_SERVERS, server_param .. " -set-mark 255") end end -if #LOCAL_SERVERS == 0 then +if #DIRECT_SERVERS == 0 then log(" * 注意:SmartDNS 没有可用的直连 DNS 服务器,DNS 将无法正常工作!!!") os.exit(1) end -if REMOTE_DNS then - REMOTE_DNS = REMOTE_DNS:gsub("#", ":") -end local AUTO_GROUP = "auto" local auto_config_lines = {} -- 直连规则显式覆盖远程兜底的缓存、IPv6、测速和双栈优选设置。 @@ -177,13 +189,15 @@ local local_domain = DNS_REDIRECT and api.uci_get("dhcp", "@dnsmasq[0]", "domain config_lines = { tonumber(LISTEN_PORT) ~= 0 and ("bind " .. listen_address) or "", tonumber(LISTEN_PORT) ~= 0 and ("bind-tcp " .. listen_address) or "", + LOCAL_PORT > 0 and ("bind [::]:" .. LOCAL_PORT .. "@lo -group direct -no-rules") or "", + LOCAL_PORT > 0 and ("bind-tcp [::]:" .. LOCAL_PORT .. "@lo -group direct -no-rules") or "", "log-level " .. LOG_LEVEL, "log-num " .. (LOG and "1" or "0"), LOG and string.format("log-file %q", FLAG_PATH .. "/smartdns.log") or "", LOG and ("log-size " .. LOG_SIZE .. "M") or "", string.format("cache-file %q", SMARTDNS_DATA_PATH .. "/smartdns.cache"), string.format("data-dir %q", data_dir), - "server 223.5.5.5 -bootstrap-dns", + "server 223.5.5.5 -bootstrap-dns -set-mark 255", -- Set the suffix before loading hosts/leases so initial records are expanded too. (local_domain and local_domain ~= "") and "local-domain " .. local_domain or "", DNS_REDIRECT and "hosts-file /tmp/hosts/*" or "", @@ -220,7 +234,7 @@ if DNS_REDIRECT then table.insert(config_lines, string.format("odhcpd-lease-file %q", odhcpd_lease)) end end -for _, server_param in ipairs(LOCAL_SERVERS) do +for _, server_param in ipairs(DIRECT_SERVERS) do table.insert(config_lines, server_param .. " -group direct -exclude-default-group") if DEFAULT_DNS_GROUP == AUTO_GROUP then -- 自动组的直连副本仅接受大陆 IP,普通直连组保持默认行为。 @@ -241,13 +255,7 @@ end if DNS_MODE == "socks" then for w in string.gmatch(REMOTE_DNS, '[^|]+') do - -- 协议和默认端口由 SmartDNS 根据原始 URL 识别。 - local dns = api.split(api.trim(w), ",") - local server_param = "server " .. api.trim(dns[1]) - if dns[2] and dns[2] ~= "" then - -- 保留 URL 中的域名,固定连接 IP 交给 SmartDNS 处理,避免手动替换影响 SNI 和 HTTP Host。 - server_param = server_param .. " -host-ip " .. api.trim(dns[2]):gsub("^%[(.*)%]$", "%1") - end + local server_param = make_server_param(w) if not api.is_local_ip(w) then server_param = server_param .. " -proxy " .. proxy_server_name diff --git a/openlist2/Makefile b/openlist2/Makefile index be67779b..436c2ed9 100644 --- a/openlist2/Makefile +++ b/openlist2/Makefile @@ -7,13 +7,13 @@ include $(TOPDIR)/rules.mk PKG_NAME:=openlist2 -PKG_VERSION:=4.2.6 -PKG_WEB_VERSION:=4.2.6 +PKG_VERSION:=4.2.7 +PKG_WEB_VERSION:=4.2.7 PKG_RELEASE:=1 PKG_SOURCE:=openlist-$(PKG_VERSION).tar.gz PKG_SOURCE_URL:=https://codeload.github.com/OpenListTeam/OpenList/tar.gz/v$(PKG_VERSION)? -PKG_HASH:=028694b14ba7368429774f33c1b7eb584b2bf6235fb7d72df371e528a5c6b04c +PKG_HASH:=5d2201596a81e85146eb8aec4028f66aacff65945fef5936193cbd41fc7e68b5 PKG_BUILD_DIR:=$(BUILD_DIR)/OpenList-$(PKG_VERSION) @@ -24,7 +24,7 @@ PKG_MAINTAINER:=sbwml define Download/openlist-frontend FILE:=openlist-frontend-dist-lite-v$(PKG_WEB_VERSION).tar.gz URL:=https://github.com/OpenListTeam/OpenList-Frontend/releases/download/v$(PKG_WEB_VERSION)/ - HASH:=26368756cdbe4de9972118d8abbb5a7e0b913bb1e5685e64a89a6f07bafccab1 + HASH:=6679861e3726305434f5de665a06880bd9a74aeab5ffc4c4150accef00e1c42d endef PKG_BUILD_DEPENDS:=golang/host