diff --git a/luci-app-passwall/root/usr/share/passwall/adblock.sh b/luci-app-passwall/root/usr/share/passwall/adblock.sh index c048dd92..0841ea42 100755 --- a/luci-app-passwall/root/usr/share/passwall/adblock.sh +++ b/luci-app-passwall/root/usr/share/passwall/adblock.sh @@ -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 diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua index ef70a93c..c9420c81 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua @@ -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 diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua index a69ac4fd..ab03ff15 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua @@ -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 ]] diff --git a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua index a76de67a..8a7dad2b 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua @@ -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 diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua index 81304d57..eb2f5871 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua @@ -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), diff --git a/luci-app-passwall2/po/fa/passwall2.po b/luci-app-passwall2/po/fa/passwall2.po index 2d7dab7b..46e41b8d 100644 --- a/luci-app-passwall2/po/fa/passwall2.po +++ b/luci-app-passwall2/po/fa/passwall2.po @@ -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 "پروکسی زنجیره‌ای" diff --git a/luci-app-passwall2/po/ru/passwall2.po b/luci-app-passwall2/po/ru/passwall2.po index fd19b737..5013a339 100644 --- a/luci-app-passwall2/po/ru/passwall2.po +++ b/luci-app-passwall2/po/ru/passwall2.po @@ -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 "Цепочка прокси" diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po index c9d5ec1d..6e06ca35 100644 --- a/luci-app-passwall2/po/zh-cn/passwall2.po +++ b/luci-app-passwall2/po/zh-cn/passwall2.po @@ -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 "链式代理" diff --git a/luci-app-passwall2/po/zh-tw/passwall2.po b/luci-app-passwall2/po/zh-tw/passwall2.po index 9ffae2b5..938d43c0 100644 --- a/luci-app-passwall2/po/zh-tw/passwall2.po +++ b/luci-app-passwall2/po/zh-tw/passwall2.po @@ -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 "鏈式代理" diff --git a/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua b/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua index 9fab4d10..c15d7d37 100644 --- a/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua +++ b/luci-app-passwall2/root/usr/share/passwall2/helper_dnsmasq.lua @@ -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