🎨 Sync 2026-04-09 04:06:07

This commit is contained in:
github-actions[bot] 2026-04-09 04:06:07 +08:00
parent 4b808ed317
commit 538030270f
10 changed files with 199 additions and 135 deletions

View File

@ -50,13 +50,14 @@ process_url() {
# 去除首尾空格
gsub(/^[[:space:]]+|[[:space:]]+$/, "")
# 跳过注释和空行
if ($0 ~ /^#/ || $0 ~ /^!/ || $0 == "") next
if ($0 ~ /^[#!]/ || $0 ~ /^\[Adblock/ || $0 == "") next
# 前20行探测格式
if (sample_count < 20) {
sample_count++
if (fmt == "" && $0 ~ /^address=\/[^\/]+\//) fmt = "dnsmasq"
if (fmt == "" && $0 ~ /^DOMAIN-SUFFIX,/) fmt = "clash"
if (fmt == "" && $0 ~ /^\|\|[a-zA-Z0-9]/) fmt = "adguard"
if (fmt == "" && $0 ~ /^(0\.0\.0\.0|127\.0\.0\.1)[[:space:]]/)
fmt = "hosts"
if (fmt == "" && $0 ~ /^[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)+$/)
@ -77,9 +78,22 @@ process_url() {
# 去掉行尾可能的注释
gsub(/#.*/, "", domain)
gsub(/[[:space:]]/, "", domain)
if (domain != "" && domain != "localhost")
if (domain ~ /^[a-zA-Z0-9]([a-zA-Z0-9.\-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$/)
print domain
}
} else if (fmt == "adguard") {
if ($0 ~ /^\|\|/) {
line = $0
# 去掉开头 ||
sub(/^\|\|/, "", line)
# 去掉 ^ 及之后所有内容(^$third-party 等修饰符)
sub(/\^.*/, "", line)
# 去掉可能残留的 $ 修饰符
sub(/\$.*/, "", line)
# 校验结果是否为合法域名
if (line ~ /^[a-zA-Z0-9]([a-zA-Z0-9.\-]*[a-zA-Z0-9])?\.[a-zA-Z]{2,}$/)
print line
}
} else if (fmt == "plain") {
if ($0 ~ /^[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]*[a-zA-Z0-9])?)+$/)
print

View File

@ -238,16 +238,6 @@ o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port"))
o.datatype = "port"
local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then
for index, value in ipairs(protocols) do
if not value:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = value })
s.fields[_n("port")]:depends({ [_n("protocol")] = value })
end
end
end
o = s:option(Value, _n("uuid"), translate("ID"))
o.password = true
o:depends({ [_n("protocol")] = "vmess" })
@ -728,10 +718,39 @@ o.datatype = "uinteger"
o.placeholder = 0
o:depends({ [_n("protocol")] = "vless" })
for i, v in ipairs(s.fields[_n("protocol")].keylist) do
if not v:find("^_") and v ~= "hysteria2" then
s.fields[_n("tcp_fast_open")]:depends({ [_n("protocol")] = v })
s.fields[_n("tcpMptcp")]:depends({ [_n("protocol")] = v })
o = s:option(ListValue, _n("domain_resolver"), translate("Domain DNS Resolve"), translate("If the node address is a domain name, this DNS will be used for resolution."))
o:value("", translate("Auto"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o = s:option(Value, _n("domain_resolver_dns"), "DNS")
o.datatype = "or(ipaddr,ipaddrport)"
o:value("114.114.114.114")
o:value("223.5.5.5:53")
o.default = o.keylist[1]
o:depends({ [_n("domain_resolver")] = "tcp" })
o:depends({ [_n("domain_resolver")] = "udp" })
o = s:option(ListValue, _n("domain_strategy"), translate("Domain Strategy"), translate("If is domain name, The requested domain name will be resolved to IP before connect."))
o.default = ""
o:value("", translate("Auto"))
o:value("UseIPv4", translate("IPv4 Only"))
o:value("UseIPv6", translate("IPv6 Only"))
local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then
for i, v in ipairs(protocols) do
if not v:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = v })
s.fields[_n("port")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v })
if v ~= "hysteria2" then
s.fields[_n("tcp_fast_open")]:depends({ [_n("protocol")] = v })
s.fields[_n("tcpMptcp")]:depends({ [_n("protocol")] = v })
end
end
end
end
end

View File

@ -209,16 +209,6 @@ o = s:option(Value, _n("address"), translate("Address (Support Domain Name)"))
o = s:option(Value, _n("port"), translate("Port"))
o.datatype = "port"
local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then
for index, value in ipairs(protocols) do
if not value:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = value })
s.fields[_n("port")]:depends({ [_n("protocol")] = value })
end
end
end
o = s:option(Value, _n("uuid"), translate("ID"))
o.password = true
o:depends({ [_n("protocol")] = "vmess" })
@ -786,6 +776,19 @@ o:value("v2ray-plugin")
o = s:option(Value, _n("plugin_opts"), translate("opts"))
o:depends({ [_n("plugin_enabled")] = true })
o = s:option(ListValue, _n("domain_resolver"), translate("Domain DNS Resolve"), translate("If the node address is a domain name, this DNS will be used for resolution."))
o:value("", translate("Auto"))
o:value("tcp", "TCP")
o:value("udp", "UDP")
o = s:option(Value, _n("domain_resolver_dns"), "DNS")
o.datatype = "or(ipaddr,ipaddrport)"
o:value("114.114.114.114")
o:value("223.5.5.5:53")
o.default = o.keylist[1]
o:depends({ [_n("domain_resolver")] = "tcp" })
o:depends({ [_n("domain_resolver")] = "udp" })
o = s:option(ListValue, _n("domain_strategy"), translate("Domain Strategy"), translate("If is domain name, The requested domain name will be resolved to IP before connect."))
o.default = ""
o:value("", translate("Auto"))
@ -793,18 +796,18 @@ o:value("prefer_ipv4", translate("Prefer IPv4"))
o:value("prefer_ipv6", translate("Prefer IPv6"))
o:value("ipv4_only", translate("IPv4 Only"))
o:value("ipv6_only", translate("IPv6 Only"))
o:depends({ [_n("protocol")] = "socks" })
o:depends({ [_n("protocol")] = "http" })
o:depends({ [_n("protocol")] = "shadowsocks" })
o:depends({ [_n("protocol")] = "shadowsocksr" })
o:depends({ [_n("protocol")] = "vmess" })
o:depends({ [_n("protocol")] = "trojan" })
o:depends({ [_n("protocol")] = "wireguard" })
o:depends({ [_n("protocol")] = "hysteria" })
o:depends({ [_n("protocol")] = "vless" })
o:depends({ [_n("protocol")] = "tuic" })
o:depends({ [_n("protocol")] = "hysteria2" })
o:depends({ [_n("protocol")] = "anytls" })
local protocols = s.fields[_n("protocol")].keylist
if #protocols > 0 then
for i, v in ipairs(protocols) do
if not v:find("^_") then
s.fields[_n("address")]:depends({ [_n("protocol")] = v })
s.fields[_n("port")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_resolver")]:depends({ [_n("protocol")] = v })
s.fields[_n("domain_strategy")]:depends({ [_n("protocol")] = v })
end
end
end
end
-- [[ Normal single node End ]]

View File

@ -12,6 +12,10 @@ local ech_domain = {}
local local_version = api.get_app_version("sing-box"):match("[^v]+")
local version_ge_1_13_0 = api.compare_versions(local_version, ">=", "1.13.0")
local GLOBAL = {
DNS_SERVER = {}
}
local GEO_VAR = {
OK = nil,
DIR = nil,
@ -159,6 +163,29 @@ function gen_outbound(flag, node, tag, proxy_table)
detour = node.detour,
}
if api.datatypes.hostname(node.address) and (node.domain_resolver == "tcp" or node.domain_resolver == "udp") and node.domain_resolver_dns then
local dns_tag = node_id .. "_dns"
local dns_proto = node.domain_resolver
local server_address = node.domain_resolver_dns
local server_port = 53
local split = api.split(server_address, ":")
if #split > 1 then
server_address = split[1]
server_port = tonumber(split[#split])
end
GLOBAL.DNS_SERVER[node_id] = {
server = {
tag = dns_tag,
type = dns_proto,
server = server_address,
server_port = server_port,
detour = "direct"
},
domain = node.address
}
result.domain_resolver.server = dns_tag
end
local tls = nil
if node.protocol == "hysteria" or node.protocol == "hysteria2" or node.protocol == "tuic" or node.protocol == "naive" then
node.tls = "1"
@ -1695,6 +1722,15 @@ function gen_config(var)
reverse_mapping = true, -- After responding to a DNS query, a reverse mapping of the IP address is stored to provide the domain name for routing purposes.
}
for i, v in pairs(GLOBAL.DNS_SERVER) do
table.insert(dns.servers, v.server)
table.insert(dns.rules, {
action = "route",
server = v.server.tag,
domain = v.domain,
})
end
table.insert(dns.servers, {
type = "local",
tag = "local"
@ -1855,42 +1891,6 @@ function gen_config(var)
})
end
local dnsmasq_server_domain = api.get_dnsmasq_server_domain()
if next(dnsmasq_server_domain) then
local new_dns_servers = {}
for domain, v in pairs(dnsmasq_server_domain) do
if not new_dns_servers[v.dnsmasq_dns] then
new_dns_servers[v.dnsmasq_dns] = {
server = {
tag = v.dnsmasq_dns,
type = "udp",
server = v.server,
server_port = v.port,
detour = "direct",
},
rule = {
action = "route",
domain_suffix = {},
server = v.dnsmasq_dns,
strategy = direct_strategy,
},
}
end
table.insert(new_dns_servers[v.dnsmasq_dns].rule.domain_suffix, domain)
end
for k, v in pairs(new_dns_servers) do
table.insert(dns.servers, v.server)
table.insert(dns.rules, 1, v.rule)
table.insert(route.rules, 1, {
action = "route",
network = v.server.type,
ip_cidr = v.server.server,
port = v.server.server_port,
outbound = "direct",
})
end
end
local default_dns_flag = "remote"
if node_id and redir_port then
local node = get_node_by_id(node_id)
@ -2083,7 +2083,10 @@ function gen_config(var)
if not value["_flag_proxy_tag"] and not value.detour and value["_id"] and value.server and value.server_port and not no_run then
sys.call(string.format("echo '%s' >> %s", value["_id"], api.TMP_PATH .. "/direct_node_list"))
end
if value.detour then
if not value.detour and value.type ~= "direct" then
value.detour = "direct"
end
if value.server and not api.datatypes.hostname(value.server) then
value.domain_resolver = nil
end
for k, v in pairs(config.outbounds[index]) do

View File

@ -7,6 +7,10 @@ local appname = api.appname
local fs = api.fs
local CACHE_PATH = api.CACHE_PATH
local GLOBAL = {
DNS_SERVER = {}
}
local xray_version = api.get_app_version("xray")
local function get_domain_excluded()
@ -130,6 +134,7 @@ function gen_outbound(flag, node, tag, proxy_table)
streamSettings = (node.streamSettings or node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan" or node.protocol == "hysteria") and {
sockopt = {
mark = 255,
domainStrategy = node.domain_strategy or "UseIP",
tcpFastOpen = (node.tcp_fast_open == "1") and true or nil,
tcpMptcp = (node.tcpMptcp == "1") and true or nil
},
@ -394,6 +399,29 @@ function gen_outbound(flag, node, tag, proxy_table)
end
end
if api.datatypes.hostname(node.address) and (node.domain_resolver == "tcp" or node.domain_resolver == "udp") and node.domain_resolver_dns then
local dns_tag = node_id .. "_dns"
local dns_proto = node.domain_resolver
local server_address = node.domain_resolver_dns
local server_port = 53
local split = api.split(server_address, ":")
if #split > 1 then
server_address = split[1]
server_port = tonumber(split[#split])
end
local config_address = server_address
if dns_proto == "tcp" then
config_address = dns_proto .. "://" .. server_address .. ":" .. server_port
end
GLOBAL.DNS_SERVER[node_id] = {
tag = dns_tag,
queryStrategy = node.domain_strategy or "UseIP",
address = config_address,
port = server_port,
domains = {"full:" .. node.address}
}
end
end
return result
end
@ -1386,6 +1414,19 @@ function gen_config(var)
end
end
local dns_servers = {}
local direct_dns_tag = "dns-in-direct"
local remote_dns_tag = "dns-in-remote"
local remote_fakedns_tag = "dns-in-remote-fakedns"
local default_dns_tag = "dns-in-default"
for i, v in pairs(GLOBAL.DNS_SERVER) do
table.insert(dns_servers, {
server = v,
outboundTag = "direct"
})
end
dns = {
tag = "dns-global",
hosts = {},
@ -1396,8 +1437,6 @@ function gen_config(var)
queryStrategy = "UseIP"
}
local dns_servers = {}
table.insert(dns_servers, {
server = {
tag = "local",
@ -1406,11 +1445,6 @@ function gen_config(var)
})
if dns_listen_port then
local direct_dns_tag = "dns-in-direct"
local remote_dns_tag = "dns-in-remote"
local remote_fakedns_tag = "dns-in-remote-fakedns"
local default_dns_tag = "dns-in-default"
local _remote_dns_proto = "tcp"
if not routing then
@ -1538,39 +1572,6 @@ function gen_config(var)
})
end
end
local dnsmasq_server_domain = api.get_dnsmasq_server_domain()
if next(dnsmasq_server_domain) then
local new_dns_servers = {}
for domain, v in pairs(dnsmasq_server_domain) do
if not new_dns_servers[v.dnsmasq_dns] then
new_dns_servers[v.dnsmasq_dns] = {
server = {
tag = v.dnsmasq_dns,
address = v.server,
port = v.port,
queryStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP"
},
domain = {}
}
end
table.insert(new_dns_servers[v.dnsmasq_dns].domain, domain)
end
for k, v in pairs(new_dns_servers) do
table.insert(dns_domain_rules, 1, {
shunt_rule_name = k,
outboundTag = "direct",
dns_server = v.server,
domain = v.domain
})
table.insert(routing.rules, 1, {
network = "udp",
ip = v.server.address,
port = v.server.port,
outboundTag = "direct"
})
end
end
if dns_listen_port then
table.insert(inbounds, {
@ -1686,25 +1687,6 @@ function gen_config(var)
end
end
end
for i = #dns_servers, 1, -1 do
local value = dns_servers[i]
if value.server.tag ~= direct_dns_tag and value.server.tag ~= remote_dns_tag then
-- DNS rule must be at the front, prevents being matched by rules.
if value.outboundTag and value.server.address ~= "fakedns" then
table.insert(routing.rules, 1, {
inboundTag = {
value.server.tag
},
outboundTag = value.outboundTag,
})
end
if (value.server.domains and #value.server.domains > 0) or value.server.tag == default_dns_tag then
-- Only keep default DNS server or has domains DNS server.
table.insert(dns.servers, 1, value.server)
end
end
end
end
local default_rule_index = nil
@ -1759,6 +1741,25 @@ function gen_config(var)
dns.hosts = nil
end
for i = #dns_servers, 1, -1 do
local value = dns_servers[i]
if value.server.tag ~= direct_dns_tag and value.server.tag ~= remote_dns_tag then
-- DNS rule must be at the front, prevents being matched by rules.
if value.outboundTag and value.server.address ~= "fakedns" then
table.insert(routing.rules, 1, {
inboundTag = {
value.server.tag
},
outboundTag = value.outboundTag,
})
end
if (value.server.domains and #value.server.domains > 0) or value.server.tag == default_dns_tag then
-- Only keep default DNS server or has domains DNS server.
table.insert(dns.servers, 1, value.server)
end
end
end
if redir_port then
local inbound = {
port = tonumber(redir_port),

View File

@ -1764,6 +1764,12 @@ msgstr "تاخیر (میلی‌ثانیه)"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "اگر نام دامنه است، نام دامنه درخواست شده قبل از اتصال به IP حل می‌شود."
msgid "Domain DNS Resolve"
msgstr "حل مشکل DNS دامنه"
msgid "If the node address is a domain name, this DNS will be used for resolution."
msgstr "اگر آدرس گره یک نام دامنه باشد، از این DNS برای حل مسئله استفاده خواهد شد."
msgid "Chain Proxy"
msgstr "پروکسی زنجیره‌ای"

View File

@ -1765,6 +1765,12 @@ msgstr "Задержка (мс)"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "Если указано доменное имя, оно будет разрешено в IP-адрес перед подключением."
msgid "Domain DNS Resolve"
msgstr "Разрешение DNS-запросов для домена"
msgid "If the node address is a domain name, this DNS will be used for resolution."
msgstr "Если адрес узла является доменным именем, для разрешения будет использоваться этот DNS-сервер."
msgid "Chain Proxy"
msgstr "Цепочка прокси"

View File

@ -1756,6 +1756,12 @@ msgstr "延迟ms"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "如果是域名,域名将在请求发出之前解析为 IP。"
msgid "Domain DNS Resolve"
msgstr "域名 DNS 解析"
msgid "If the node address is a domain name, this DNS will be used for resolution."
msgstr "如果节点地址是域名,则将使用此 DNS 进行解析。"
msgid "Chain Proxy"
msgstr "链式代理"

View File

@ -1756,6 +1756,12 @@ msgstr "延迟ms"
msgid "If is domain name, The requested domain name will be resolved to IP before connect."
msgstr "如果是域名,域名將在請求發出之前解析為 IP。"
msgid "Domain DNS Resolve"
msgstr "域名 DNS 解析"
msgid "If the node address is a domain name, this DNS will be used for resolution."
msgstr "如果節點地址是域名,則將使用此 DNS 進行解析。"
msgid "Chain Proxy"
msgstr "鏈式代理"

View File

@ -272,7 +272,7 @@ function add_rule(var)
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_dns(address, fwd_dns)
set_domain_ipset(address, setflag_4 .. "passwall2_vps," .. setflag_6 .. "passwall2_vps6")
end
end