Просмотр исходного кода

luci-app-ssr-plus: Add dnsproxy parse.

zxlhhyccc 6 месяцев назад
Родитель
Сommit
92f1ef8f72

+ 6 - 0
luci-app-ssr-plus/Makefile

@@ -11,6 +11,7 @@ PKG_CONFIG_DEPENDS:= \
 	CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG \
 	CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_DNS2SOCKS \
 	CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_DNS2SOCKS_RUST \
+	CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_DNSPROXY \
 	CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_MosDNS \
 	CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria \
 	CONFIG_PACKAGE_$(PKG_NAME)_INCLUDE_Tuic_Client \
@@ -44,6 +45,7 @@ LUCI_DEPENDS:= \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_ChinaDNS_NG:chinadns-ng \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_DNS2SOCKS:dns2socks \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_DNS2SOCKS_RUST:dns2socks-rust \
+	+PACKAGE_$(PKG_NAME)_INCLUDE_DNSPROXY:dnsproxy \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_MosDNS:mosdns \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_Hysteria:hysteria \
 	+PACKAGE_$(PKG_NAME)_INCLUDE_Tuic_Client:tuic-client \
@@ -130,6 +132,10 @@ config PACKAGE_$(PKG_NAME)_INCLUDE_DNS2SOCKS_RUST
 	bool "Include DNS2socks-Rust"
 	default n
 
+config PACKAGE_$(PKG_NAME)_INCLUDE_DNSPROXY
+	bool "Include DNSproxy"
+	default n
+
 config PACKAGE_$(PKG_NAME)_INCLUDE_MosDNS
 	bool "Include MosDNS"
 	default y if aarch64||arm||i386||x86_64

+ 67 - 2
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/advanced.lua

@@ -1,5 +1,23 @@
 local m, s, o
 local uci = require "luci.model.uci".cursor()
+
+-- 获取 LAN IP 地址
+function lanip()
+	local lan_ip
+	lan_ip = luci.sys.exec("uci -q get network.lan.ipaddr 2>/dev/null | awk -F '/' '{print $1}' | tr -d '\n'")
+
+	if not lan_ip or lan_ip == "" then
+    	lan_ip = luci.sys.exec("ip address show $(uci -q -p /tmp/state get network.lan.ifname || uci -q -p /tmp/state get network.lan.device) | grep -w 'inet' | grep -Eo 'inet [0-9\.]+' | awk '{print $2}' | head -1 | tr -d '\n'")
+	end
+
+	if not lan_ip or lan_ip == "" then
+    	lan_ip = luci.sys.exec("ip addr show | grep -w 'inet' | grep 'global' | grep -Eo 'inet [0-9\.]+' | awk '{print $2}' | head -n 1 | tr -d '\n'")
+	end
+
+	return lan_ip
+end
+
+local lan_ip = lanip()
 local server_table = {}
 local type_table = {}
 local function is_finded(e)
@@ -77,11 +95,18 @@ o.description = translate("Customize Netflix IP Url")
 o:depends("netflix_enable", "1")
 
 o = s:option(ListValue, "shunt_dns_mode", translate("DNS Query Mode For Shunt Mode"))
-o:value("1", translate("Use DNS2SOCKS query and cache"))
-o:value("2", translate("Use DNS2SOCKS-RUST query and cache"))
+if is_finded("dns2socks") then
+	o:value("1", translate("Use DNS2SOCKS query and cache"))
+end
+if is_finded("dns2socks-rust") then
+	o:value("2", translate("Use DNS2SOCKS-RUST query and cache"))
+end
 if is_finded("mosdns") then
 	o:value("3", translate("Use MosDNS query"))
 end
+if is_finded("dnsproxy") then
+	o:value("4", translate("Use DNSPROXY query and cache"))
+end
 o:depends("netflix_enable", "1")
 o.default = 1
 
@@ -117,6 +142,46 @@ o:depends("shunt_dns_mode", "3")
 o.rmempty = false
 o.default = "0"
 
+if is_finded("dnsproxy") then
+	o = s:option(ListValue, "shunt_parse_method", translate("Select DNS parse Mode"))
+	o.description = translate(
+    	"<ul>" ..
+    	"<li>" .. translate("When use DNS list file, please ensure list file exists and is formatted correctly.") .. "</li>" ..
+    	"<li>" .. translate("Tips: Dnsproxy DNS Parse List Path:") ..
+    	" <a href='http://" .. lan_ip .. "/cgi-bin/luci/admin/services/shadowsocksr/control' target='_blank'>" ..
+    	translate("Click here to view or manage the DNS list file") .. "</a>" .. "</li>" ..
+    	"</ul>"
+	)
+	o:value("single_dns", translate("Set Single DNS"))
+	o:value("parse_file", translate("Use DNS List File"))
+	o:depends("shunt_dns_mode", "4")
+	o.rmempty = true
+	o.default = "single_dns"
+
+	o = s:option(Value, "dnsproxy_shunt_forward", translate("Anti-pollution DNS Server"))
+	o:value("sdns://AgUAAAAAAAAABzguOC40LjQgsKKKE4EwvtIbNjGjagI2607EdKSVHowYZtyvD9iPrkkHOC44LjQuNAovZG5zLXF1ZXJ5", translate("Google DNSCrypt SDNS"))
+	o:value("sdns://AgcAAAAAAAAADzE4NS4yMjIuMjIyLjIyMiAOp5Svj-oV-Fz-65-8H2VKHLKJ0egmfEgrdPeAQlUFFA8xODUuMjIyLjIyMi4yMjIKL2Rucy1xdWVyeQ", translate("dns.sb DNSCrypt SDNS"))
+	o:value("sdns://AgMAAAAAAAAADTE0OS4xMTIuMTEyLjkgsBkgdEu7dsmrBT4B4Ht-BQ5HPSD3n3vqQ1-v5DydJC8SZG5zOS5xdWFkOS5uZXQ6NDQzCi9kbnMtcXVlcnk", translate("Quad9 DNSCrypt SDNS"))
+	o:value("sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", translate("AdGuard DNSCrypt SDNS"))
+	o:value("sdns://AgcAAAAAAAAABzEuMC4wLjGgENk8mGSlIfMGXMOlIlCcKvq7AVgcrZxtjon911-ep0cg63Ul-I8NlFj4GplQGb_TTLiczclX57DvMV8Q-JdjgRgSZG5zLmNsb3VkZmxhcmUuY29tCi9kbnMtcXVlcnk", translate("Cloudflare DNSCrypt SDNS"))
+	o:value("sdns://AgcAAAAAAAAADjEwNC4xNi4yNDkuMjQ5ABJjbG91ZGZsYXJlLWRucy5jb20KL2Rucy1xdWVyeQ", translate("cloudflare-dns.com DNSCrypt SDNS"))
+	o:depends("shunt_parse_method", "single_dns")
+	o.description = translate("Custom DNS Server (support: IP:Port or tls://IP:Port or https://IP/dns-query and other format).")
+
+	o = s:option(ListValue, "shunt_upstreams_logic_mode", translate("Defines the upstreams logic mode"))
+	o.description = translate(
+    	"<ul>" ..
+    	"<li>" .. translate("Defines the upstreams logic mode, possible values: load_balance, parallel, fastest_addr (default: load_balance).") .. "</li>" .. "<li>" .. translate("When two or more DNS servers are deployed, enable this function.") .. "</li>" ..
+    	"</ul>"
+	)
+	o:value("load_balance", translate("load_balance"))
+	o:value("parallel", translate("parallel"))
+	o:value("fastest_addr", translate("fastest_addr"))
+	o:depends("shunt_parse_method", "parse_file")
+	o.rmempty = true
+	o.default = "load_balance"
+end
+
 o = s:option(Flag, "apple_optimization", translate("Apple domains optimization"), translate("For Apple domains equipped with Chinese mainland CDN, always responsive to Chinese CDN IP addresses"))
 o.rmempty = false
 o.default = "1"

+ 67 - 2
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/client.lua

@@ -5,6 +5,23 @@
 local m, s, sec, o
 local uci = require "luci.model.uci".cursor()
 
+-- 获取 LAN IP 地址
+function lanip()
+	local lan_ip
+	lan_ip = luci.sys.exec("uci -q get network.lan.ipaddr 2>/dev/null | awk -F '/' '{print $1}' | tr -d '\n'")
+
+	if not lan_ip or lan_ip == "" then
+    	lan_ip = luci.sys.exec("ip address show $(uci -q -p /tmp/state get network.lan.ifname || uci -q -p /tmp/state get network.lan.device) | grep -w 'inet' | grep -Eo 'inet [0-9\.]+' | awk '{print $2}' | head -1 | tr -d '\n'")
+	end
+
+	if not lan_ip or lan_ip == "" then
+    	lan_ip = luci.sys.exec("ip addr show | grep -w 'inet' | grep 'global' | grep -Eo 'inet [0-9\.]+' | awk '{print $2}' | head -n 1 | tr -d '\n'")
+	end
+
+	return lan_ip
+end
+
+local lan_ip = lanip()
 local validation = require "luci.cbi.datatypes"
 local function is_finded(e)
 	return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
@@ -95,11 +112,18 @@ o.default = 1
 
 o = s:option(ListValue, "pdnsd_enable", translate("Resolve Dns Mode"))
 o:value("1", translate("Use DNS2TCP query"))
-o:value("2", translate("Use DNS2SOCKS query and cache"))
-o:value("3", translate("Use DNS2SOCKS-RUST query and cache"))
+if is_finded("dns2socks") then
+	o:value("2", translate("Use DNS2SOCKS query and cache"))
+end
+if is_finded("dns2socks-rust") then
+	o:value("3", translate("Use DNS2SOCKS-RUST query and cache"))
+end
 if is_finded("mosdns") then
 	o:value("4", translate("Use MOSDNS query (Not Support Oversea Mode)"))
 end
+if is_finded("dnsproxy") then
+	o:value("5", translate("Use DNSPROXY query and cache"))
+end
 o:value("0", translate("Use Local DNS Service listen port 5335"))
 o.default = 1
 
@@ -138,6 +162,47 @@ o:depends("pdnsd_enable", "4")
 o.rmempty = false
 o.default = "1"
 
+if is_finded("dnsproxy") then
+	o = s:option(ListValue, "parse_method", translate("Select DNS parse Mode"))
+	o.description = translate(
+    	"<ul>" ..
+    	"<li>" .. translate("When use DNS list file, please ensure list file exists and is formatted correctly.") .. "</li>" ..
+    	"<li>" .. translate("Tips: Dnsproxy DNS Parse List Path:") ..
+    	" <a href='http://" .. lan_ip .. "/cgi-bin/luci/admin/services/shadowsocksr/control' target='_blank'>" ..
+    	translate("Click here to view or manage the DNS list file") .. "</a>" .. "</li>" ..
+    	"</ul>"
+	)
+	o:value("single_dns", translate("Set Single DNS"))
+	o:value("parse_file", translate("Use DNS List File"))
+	o:depends("pdnsd_enable", "5")
+	o.rmempty = true
+	o.default = "single_dns"
+
+	o = s:option(Value, "dnsproxy_tunnel_forward", translate("Anti-pollution DNS Server"))
+	o:value("sdns://AgUAAAAAAAAABzguOC40LjQgsKKKE4EwvtIbNjGjagI2607EdKSVHowYZtyvD9iPrkkHOC44LjQuNAovZG5zLXF1ZXJ5", translate("Google DNSCrypt SDNS"))
+	o:value("sdns://AgcAAAAAAAAADzE4NS4yMjIuMjIyLjIyMiAOp5Svj-oV-Fz-65-8H2VKHLKJ0egmfEgrdPeAQlUFFA8xODUuMjIyLjIyMi4yMjIKL2Rucy1xdWVyeQ", translate("dns.sb DNSCrypt SDNS"))
+	o:value("sdns://AgMAAAAAAAAADTE0OS4xMTIuMTEyLjkgsBkgdEu7dsmrBT4B4Ht-BQ5HPSD3n3vqQ1-v5DydJC8SZG5zOS5xdWFkOS5uZXQ6NDQzCi9kbnMtcXVlcnk", translate("Quad9 DNSCrypt SDNS"))
+	o:value("sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20", translate("AdGuard DNSCrypt SDNS"))
+	o:value("sdns://AgcAAAAAAAAABzEuMC4wLjGgENk8mGSlIfMGXMOlIlCcKvq7AVgcrZxtjon911-ep0cg63Ul-I8NlFj4GplQGb_TTLiczclX57DvMV8Q-JdjgRgSZG5zLmNsb3VkZmxhcmUuY29tCi9kbnMtcXVlcnk", translate("Cloudflare DNSCrypt SDNS"))
+	o:value("sdns://AgcAAAAAAAAADjEwNC4xNi4yNDkuMjQ5ABJjbG91ZGZsYXJlLWRucy5jb20KL2Rucy1xdWVyeQ", translate("cloudflare-dns.com DNSCrypt SDNS"))
+	o:depends("parse_method", "single_dns")
+	o.description = translate("Custom DNS Server (support: IP:Port or tls://IP:Port or https://IP/dns-query and other format).")
+
+	o = s:option(ListValue, "upstreams_logic_mode", translate("Defines the upstreams logic mode"))
+	o.description = translate(
+    	"<ul>" ..
+    	"<li>" .. translate("Defines the upstreams logic mode, possible values: load_balance, parallel, fastest_addr (default: load_balance).") .. "</li>" ..
+    	"<li>" .. translate("When two or more DNS servers are deployed, enable this function.") .. "</li>" ..
+    	"</ul>"
+	)
+	o:value("load_balance", translate("load_balance"))
+	o:value("parallel", translate("parallel"))
+	o:value("fastest_addr", translate("fastest_addr"))
+	o:depends("parse_method", "parse_file")
+	o.rmempty = true
+	o.default = "load_balance"
+end
+
 if is_finded("chinadns-ng") then
 	o = s:option(Value, "chinadns_forward", translate("Domestic DNS Server"))
 	o:value("", translate("Disable ChinaDNS-NG"))

+ 22 - 0
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/control.lua

@@ -3,6 +3,10 @@ require "nixio.fs"
 require "luci.sys"
 local m, s, o
 
+local function is_finded(e)
+	return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
+end
+
 m = Map("shadowsocksr")
 
 s = m:section(TypedSection, "access_control")
@@ -141,6 +145,24 @@ o.remove = function(self, section, value)
 	nixio.fs.writefile(netflixconf, "")
 end
 
+if is_finded("dnsproxy") then
+	s:tab("dnsproxy", translate("Dnsproxy Parse List"))
+	local dnsproxyconf = "/etc/ssrplus/dnsproxy_dns.list"
+	o = s:taboption("dnsproxy", TextValue, "dnsproxyconf", "", "<font style=color:red>" .. translate("Specifically for edit dnsproxy DNS parse files.") .. "</font>")
+	o.rows = 13
+	o.wrap = "off"
+	o.rmempty = true
+	o.cfgvalue = function(self, section)
+		return nixio.fs.readfile(dnsproxyconf) or " "
+	end
+	o.write = function(self, section, value)
+		nixio.fs.writefile(dnsproxyconf, value:gsub("\r\n", "\n"))
+	end
+	o.remove = function(self, section, value)
+		nixio.fs.writefile(dnsproxyconf, "")
+	end
+end
+
 if luci.sys.call('[ -f "/www/luci-static/resources/uci.js" ]') == 0 then
 	m.apply_on_parse = true
 	function m.on_apply(self)

+ 1 - 1
luci-app-ssr-plus/luasrc/model/cbi/shadowsocksr/status.lua

@@ -92,7 +92,7 @@ if Process_list:find("ssr.server") then
 	server_run = 1
 end
 
-if Process_list:find("ssrplus/bin/dns2tcp") or Process_list:find("ssrplus/bin/mosdns") or (Process_list:find("ssrplus.dns") and Process_list:find("dns2socks*.*127.0.0.1.*127.0.0.1.5335")) then
+if Process_list:find("ssrplus/bin/dns2tcp") or Process_list:find("ssrplus/bin/mosdns") or Process_list:find("dnsproxy.*127.0.0.1.*5335") or (Process_list:find("ssrplus.dns") and Process_list:find("dns2socks*.*127.0.0.1.*127.0.0.1.5335")) then
 	pdnsd_run = 1
 end
 

+ 48 - 0
luci-app-ssr-plus/po/zh_Hans/ssr-plus.po

@@ -621,6 +621,39 @@ msgstr "使用 DNS2SOCKS-RUST 查询并缓存"
 msgid "Use MOSDNS query (Not Support Oversea Mode)"
 msgstr "使用 MOSDNS 查询 (不支持海外用户回国模式)"
 
+msgid "Use DNSPROXY query and cache"
+msgstr "使用 DNSPROXY 查询并缓存"
+
+msgid "Select DNS parse Mode"
+msgstr "选择 DNS 解析方式"
+
+msgid "Set Single DNS"
+msgstr "设置单个 DNS"
+
+msgid "Custom DNS Server (support: IP:Port or tls://IP:Port or https://IP/dns-query and other format)."
+msgstr "自定义 DNS 服务器(支持格式:IP:端口、tls://IP:端口、https://IP/dns-query 及其他格式)。"
+
+msgid "Use DNS List File"
+msgstr "使用 DNS 列表文件"
+
+msgid "Defines the upstreams logic mode"
+msgstr "定义上游逻辑模式"
+
+msgid "load_balance"
+msgstr "负载均衡"
+
+msgid "parallel"
+msgstr "并行查询"
+
+msgid "fastest_addr"
+msgstr "最快响应"
+
+msgid "Defines the upstreams logic mode, possible values: load_balance, parallel, fastest_addr (default: load_balance)."
+msgstr "定义上游逻辑模式,可选择值:负载均衡、并行查询、最快响应(默认值:负载均衡)。"
+
+msgid "When two or more DNS servers are deployed, enable this function."
+msgstr "当部署两台或两台以上 DNS 服务器时,需要启用该功能。"
+
 msgid "Disable IPv6 in MOSDNS query mode"
 msgstr "禁止 MOSDNS 返回 IPv6 记录"
 
@@ -642,6 +675,21 @@ msgstr "禁止 MosDNS 返回 IPv6 记录 (分流模式)"
 msgid "DNS Server IP:Port"
 msgstr "DNS 服务器 IP:Port"
 
+msgid "When use DNS list file, please ensure list file exists and is formatted correctly."
+msgstr "当使用 DNS 列表文件时,请确保列表文件存在并且格式正确。"
+
+msgid "Tips: Dnsproxy DNS Parse List Path:"
+msgstr "提示:Dnsproxy 的 DNS 解析列表路径:"
+
+msgid "Click here to view or manage the DNS list file"
+msgstr "点击此处查看或管理 DNS 列表文件"
+
+msgid "Dnsproxy Parse List"
+msgstr "DNSPROXY 解析列表"
+
+msgid "Specifically for edit dnsproxy DNS parse files."
+msgstr "专门用于编辑 DNSPROXY 的 DNS 解析文件。"
+
 msgid "Domestic DNS Server"
 msgstr "国内 DNS 服务器"
 

+ 121 - 2
luci-app-ssr-plus/root/etc/init.d/shadowsocksr

@@ -200,7 +200,12 @@ add_dns_into_ipset() {
 
 start_dns() {
 	local ssrplus_dns="$(uci_get_by_type global pdnsd_enable 0)"
-	local dnsserver="$(uci_get_by_type global tunnel_forward 8.8.4.4:53)"
+	local dnsproxy_dnsserver="$(uci_get_by_type global parse_method)"
+	if [ -n "$dnsproxy_dnsserver" ] && [ "$dnsproxy_dnsserver" != "parse_file" ]; then
+		dnsserver="$(uci_get_by_type global dnsproxy_tunnel_forward 8.8.4.4:53)"
+	else
+		dnsserver="$(uci_get_by_type global tunnel_forward 8.8.4.4:53)"
+	fi
 	local run_mode="$(uci_get_by_type global run_mode)"
 
 	if [ "$ssrplus_dns" != "0" ]; then
@@ -243,6 +248,61 @@ start_dns() {
 			ln_start_bin $(first_type mosdns) mosdns start -c $TMP_PATH/mosdns-config.yaml
 			pdnsd_enable_flag=4
 			;;
+		5)
+			if [ "$dnsproxy_dnsserver" != "parse_file" ]; then
+				ln_start_bin $(first_type dnsproxy) dnsproxy -l 127.0.0.1 -p $tmp_dns_port  -p $dns_port -u $dnsserver --cache --cache-min-ttl=3600
+			else
+				dnsproxy_dnsserver_file="$TMP_PATH/dnsproxy_dns.list"
+				cleaned_file="$TMP_PATH/cleaned_dns.list"
+				temp_file="$TMP_PATH/temp_dns.list"
+				> "$cleaned_file"
+				# 清理输入文件并去重
+				while IFS= read -r line || [ -n "$line" ]; do
+				    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+					[ -z "$line" ] && continue
+					echo "$line" | grep -qE '^#' && continue
+					echo "$line" >> "$cleaned_file"
+				done < "/etc/ssrplus/dnsproxy_dns.list"
+				# 获取清理后文件的MD5
+				cleaned_md5=$(md5sum "$cleaned_file" | awk '{print $1}')
+				if [ ! -f "$dnsproxy_dnsserver_file" ]; then
+				    cp "$cleaned_file" "$dnsproxy_dnsserver_file"
+				else
+				    target_md5=$(md5sum "$dnsproxy_dnsserver_file" | awk '{print $1}')
+					if [ "$cleaned_md5" != "$target_md5" ]; then
+					    > "$temp_file"
+					    # 保留目标文件中也存在于清理文件的记录(去重)
+					    while IFS= read -r line; do
+						    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+						    if grep -qixF "$line" "$cleaned_file" && ! grep -qixF "$line" "$temp_file"; then
+							    echo "$line" >> "$temp_file"
+						    fi
+						done < "$dnsproxy_dnsserver_file"
+						# 添加清理文件中有但目标文件没有的记录(去重)
+						while IFS= read -r line; do
+						    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+							if ! grep -qixF "$line" "$temp_file"; then
+							    echo "$line" >> "$temp_file"
+							fi
+						done < "$cleaned_file"
+						temp_md5=$(md5sum "$temp_file" | awk '{print $1}')
+						if [ "$temp_md5" != "$target_md5" ]; then
+						    mv "$temp_file" "$dnsproxy_dnsserver_file"
+						else
+						    rm -f "$temp_file"
+						fi
+					fi
+				fi
+				rm -f "$cleaned_file"
+
+				if [ -n "$dnsproxy_dnsserver_file" ] && [ -s "$dnsproxy_dnsserver_file" ]; then
+					local upstreams_logic_mode="$(uci_get_by_type global upstreams_logic_mode)"
+					ln_start_bin $(first_type dnsproxy) dnsproxy -l 127.0.0.1 -p $tmp_dns_port  -p $dns_port -u $dnsproxy_dnsserver_file --cache --cache-min-ttl=3600 --upstream-mode=$upstreams_logic_mode
+				fi
+			fi
+			echolog "DNSPROXY query and cache Started!"
+			pdnsd_enable_flag=5
+			;;
 		esac
 
 		if [ "$run_mode" = "router" ]; then
@@ -525,7 +585,12 @@ start_udp() {
 
 shunt_dns_command() {
 	local shunt_dns_mode="$(uci_get_by_type global shunt_dns_mode)"
-	local shunt_dnsserver="$(uci_get_by_type global shunt_dnsserver)"
+	local shunt_dnsproxy_dnsserver="$(uci_get_by_type global shunt_parse_method)"
+	if [ -n "$shunt_dnsproxy_dnsserver" ] && [ "$shunt_dnsproxy_dnsserver" != "parse_file" ]; then
+		shunt_dnsserver="$(uci_get_by_type global dnsproxy_shunt_forward 8.8.4.4:53)"
+	else
+		shunt_dnsserver="$(uci_get_by_type global shunt_dnsserver 8.8.4.4:53)"
+	fi
 	local tmp_port=$1
 	case "$shunt_dns_mode" in
 	1)
@@ -552,6 +617,60 @@ shunt_dns_command() {
 		fi
 		ln_start_bin $(first_type mosdns) mosdns start -c $TMP_PATH/mosdns-config-shunt.yaml
 		;;
+	4)
+		if [ "$shunt_dnsproxy_dnsserver" != "parse_file" ]; then
+			ln_start_bin $(first_type dnsproxy) dnsproxy -l 127.0.0.1 -p $tmp_port -p $tmp_shunt_dns_port -u $shunt_dnsserver --cache --cache-min-ttl=3600
+		else
+			shunt_dnsproxy_dnsserver_file="$TMP_PATH/dnsproxy_dns.list"
+			cleaned_file="$TMP_PATH/cleaned_dns_servers.list"
+			temp_file="$TMP_PATH/temp_dns_servers.list"
+			> "$cleaned_file"
+			# 清理输入文件并去重
+			while IFS= read -r line || [ -n "$line" ]; do
+			    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+				[ -z "$line" ] && continue
+				echo "$line" | grep -qE '^#' && continue
+				echo "$line" >> "$cleaned_file"
+			done < "/etc/ssrplus/dnsproxy_dns.list"
+			# 获取清理后文件的MD5
+			cleaned_md5=$(md5sum "$cleaned_file" | awk '{print $1}')
+			if [ ! -f "$shunt_dnsproxy_dnsserver_file" ]; then
+			    cp "$cleaned_file" "$shunt_dnsproxy_dnsserver_file"
+			else
+			    target_md5=$(md5sum "$shunt_dnsproxy_dnsserver_file" | awk '{print $1}')
+				if [ "$cleaned_md5" != "$target_md5" ]; then
+				    > "$temp_file"
+					# 保留目标文件中也存在于清理文件的记录(去重)
+					while IFS= read -r line; do
+					    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+						if grep -qixF "$line" "$cleaned_file" && ! grep -qixF "$line" "$temp_file"; then
+						    echo "$line" >> "$temp_file"
+						fi
+					done < "$shunt_dnsproxy_dnsserver_file"
+					# 添加清理文件中有但目标文件没有的记录(去重)
+					while IFS= read -r line; do
+					    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+						if ! grep -qixF "$line" "$temp_file"; then
+						    echo "$line" >> "$temp_file"
+						fi
+					done < "$cleaned_file"
+					temp_md5=$(md5sum "$temp_file" | awk '{print $1}')
+					if [ "$temp_md5" != "$target_md5" ]; then
+					    mv "$temp_file" "$shunt_dnsproxy_dnsserver_file"
+					else
+						rm -f "$temp_file"
+					fi
+				fi
+			fi
+			rm -f "$cleaned_file"
+			
+			if [ -n "$shunt_dnsproxy_dnsserver_file" ] && [ -s "$shunt_dnsproxy_dnsserver_file" ]; then
+				local shunt_upstreams_logic_mode="$(uci_get_by_type global shunt_upstreams_logic_mode)"
+				ln_start_bin $(first_type dnsproxy) dnsproxy -l 127.0.0.1 -p $tmp_port -p $tmp_shunt_dns_port -u $shunt_dnsproxy_dnsserver_file --cache --cache-min-ttl=3600 --upstream-mode=$shunt_upstreams_logic_mode
+			fi
+		fi
+		echolog "DNSPROXY shunt query and cache Started!"
+		;;
 	esac
 }
 

+ 12 - 0
luci-app-ssr-plus/root/etc/ssrplus/dnsproxy_dns.list

@@ -0,0 +1,12 @@
+# cloudflare-dns.com
+sdns://AgcAAAAAAAAADjEwNC4xNi4yNDkuMjQ5ABJjbG91ZGZsYXJlLWRucy5jb20KL2Rucy1xdWVyeQ
+# Google
+sdns://AgUAAAAAAAAABzguOC40LjQgsKKKE4EwvtIbNjGjagI2607EdKSVHowYZtyvD9iPrkkHOC44LjQuNAovZG5zLXF1ZXJ5
+# dns.sb
+sdns://AgcAAAAAAAAADzE4NS4yMjIuMjIyLjIyMiAOp5Svj-oV-Fz-65-8H2VKHLKJ0egmfEgrdPeAQlUFFA8xODUuMjIyLjIyMi4yMjIKL2Rucy1xdWVyeQ
+# Quad9
+sdns://AgMAAAAAAAAADTE0OS4xMTIuMTEyLjkgsBkgdEu7dsmrBT4B4Ht-BQ5HPSD3n3vqQ1-v5DydJC8SZG5zOS5xdWFkOS5uZXQ6NDQzCi9kbnMtcXVlcnk
+# AdGuard
+sdns://AQMAAAAAAAAAETk0LjE0MC4xNC4xNDo1NDQzINErR_JS3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczEuYWRndWFyZC5jb20
+# Cloudflare
+sdns://AgcAAAAAAAAABzEuMC4wLjGgENk8mGSlIfMGXMOlIlCcKvq7AVgcrZxtjon911-ep0cg63Ul-I8NlFj4GplQGb_TTLiczclX57DvMV8Q-JdjgRgSZG5zLmNsb3VkZmxhcmUuY29tCi9kbnMtcXVlcnk

+ 62 - 0
luci-app-ssr-plus/root/usr/bin/ssr-monitor

@@ -125,6 +125,68 @@ while [ "1" == "1" ]; do #死循环
 			dnsserver=$(uci_get_by_type global tunnel_forward 8.8.4.4:53)
 			kill -9 $(busybox ps -w | grep $TMP_BIN_PATH/mosdns | grep -v grep | awk '{print $1}') >/dev/null 2>&1
 			ln_start_bin $(first_type mosdns) mosdns start -c /etc/mosdns/config.yaml
+	#dnsproxy
+	elif [ "$pdnsd_process" -eq 5 ]; then
+		icount=$(busybox ps -w | grep -e ssrplus-dns -e "dnsproxy -l 127.0.0.1 -p $tmp_port" | grep -v grep | wc -l)
+		if [ "$icount" -lt 2 ]; then #如果进程挂掉就重启它
+			logger -t "$NAME" "dnsproxy $dnsserver tunnel error.restart!"
+			echolog "dnsproxy $dnsserver tunnel error.restart!"
+			local dnsproxy_dnsserver="$(uci_get_by_type global parse_method)"
+			if [ -n "$dnsproxy_dnsserver" ] && [ "$dnsproxy_dnsserver" != "parse_file" ]; then
+				dnsserver="$(uci_get_by_type global dnsproxy_tunnel_forward 8.8.4.4:53)"
+			fi
+			kill -9 $(busybox ps -w | grep "dnsproxy -l 127.0.0.1 -p $tmp_port" | grep -v grep | awk '{print $1}') >/dev/null 2>&1
+			if [ "$dnsproxy_dnsserver" != "parse_file" ]; then
+				ln_start_bin $(first_type dnsproxy) dnsproxy -l 127.0.0.1 -p $tmp_dns_port  -p $dns_port -u $dnsserver --cache --cache-min-ttl=3600
+			else
+				dnsproxy_dnsserver_file="$TMP_PATH/dnsproxy_dns.list"
+				cleaned_file="$TMP_PATH/cleaned_dns.list"
+				temp_file="$TMP_PATH/temp_dns.list"
+				> "$cleaned_file"
+				# 清理输入文件并去重
+				while IFS= read -r line || [ -n "$line" ]; do
+				    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+					[ -z "$line" ] && continue
+					echo "$line" | grep -qE '^#' && continue
+					echo "$line" >> "$cleaned_file"
+				done < "/etc/ssrplus/dnsproxy_dns.list"
+				# 获取清理后文件的MD5
+				cleaned_md5=$(md5sum "$cleaned_file" | awk '{print $1}')
+				if [ ! -f "$dnsproxy_dnsserver_file" ]; then
+				    cp "$cleaned_file" "$dnsproxy_dnsserver_file"
+				else
+				    target_md5=$(md5sum "$dnsproxy_dnsserver_file" | awk '{print $1}')
+					if [ "$cleaned_md5" != "$target_md5" ]; then
+					    > "$temp_file"
+					    # 保留目标文件中也存在于清理文件的记录(去重)
+					    while IFS= read -r line; do
+						    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+						    if grep -qixF "$line" "$cleaned_file" && ! grep -qixF "$line" "$temp_file"; then
+							    echo "$line" >> "$temp_file"
+						    fi
+						done < "$dnsproxy_dnsserver_file"
+						# 添加清理文件中有但目标文件没有的记录(去重)
+						while IFS= read -r line; do
+						    line=$(echo "$line" | sed -E 's/^[ \t\r]+//; s/[ \t\r]+$//')
+							if ! grep -qixF "$line" "$temp_file"; then
+							    echo "$line" >> "$temp_file"
+							fi
+						done < "$cleaned_file"
+						temp_md5=$(md5sum "$temp_file" | awk '{print $1}')
+						if [ "$temp_md5" != "$target_md5" ]; then
+						    mv "$temp_file" "$dnsproxy_dnsserver_file"
+						else
+						    rm -f "$temp_file"
+						fi
+					fi
+				fi
+				rm -f "$cleaned_file"
+
+				if [ -n "$dnsproxy_dnsserver_file" ] && [ -s "$dnsproxy_dnsserver_file" ]; then
+					local upstreams_logic_mode="$(uci_get_by_type global upstreams_logic_mode)"
+					ln_start_bin $(first_type dnsproxy) dnsproxy -l 127.0.0.1 -p $tmp_dns_port  -p $dns_port -u $dnsproxy_dnsserver_file --cache --cache-min-ttl=3600 --upstream-mode=$upstreams_logic_mode
+				fi
+			fi
 		fi
 	fi
 	#chinadns-ng