local api = require "luci.passwall.api"
local appname = api.appname
local uci = api.uci
local datatypes = api.datatypes
local has_v2ray = api.is_finded("v2ray")
local has_xray = api.is_finded("xray")
local has_chnlist = api.fs.access("/usr/share/passwall/rules/chnlist")
m = Map(appname)
local nodes_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
nodes_table[#nodes_table + 1] = e
end
local tcp_socks_server = "127.0.0.1" .. ":" .. (uci:get(appname, "@global[0]", "tcp_node_socks_port") or "1070")
local socks_table = {}
socks_table[#socks_table + 1] = {
id = tcp_socks_server,
remarks = tcp_socks_server .. " - " .. translate("TCP Node")
}
uci:foreach(appname, "socks", function(s)
if s.enabled == "1" and s.node then
local id, remarks
for k, n in pairs(nodes_table) do
if (s.node == n.id) then
remarks = n["remark"]; break
end
end
id = "127.0.0.1" .. ":" .. s.port
socks_table[#socks_table + 1] = {
id = id,
remarks = id .. " - " .. (remarks or translate("Misconfigured"))
}
end
end)
local doh_validate = function(self, value, t)
if value ~= "" then
value = api.trim(value)
local flag = 0
local util = require "luci.util"
local val = util.split(value, ",")
local url = val[1]
val[1] = nil
for i = 1, #val do
local v = val[i]
if v then
if not datatypes.ipmask4(v) then
flag = 1
end
end
end
if flag == 0 then
return value
end
end
return nil, translate("DoH request address") .. " " .. translate("Format must be:") .. " URL,IP"
end
local redir_mode_validate = function(self, value, t)
local tcp_proxy_mode_v = tcp_proxy_mode:formvalue(t) or ""
local udp_proxy_mode_v = udp_proxy_mode:formvalue(t) or ""
local localhost_tcp_proxy_mode_v = localhost_tcp_proxy_mode:formvalue(t) or ""
local localhost_udp_proxy_mode_v = localhost_udp_proxy_mode:formvalue(t) or ""
local s = tcp_proxy_mode_v .. udp_proxy_mode_v .. localhost_tcp_proxy_mode_v .. localhost_udp_proxy_mode_v
if s:find("returnhome") then
if s:find("chnroute") or s:find("gfwlist") then
return nil, translate("China list or gfwlist cannot be used together with outside China list!")
end
end
return value
end
m:append(Template(appname .. "/global/status"))
s = m:section(TypedSection, "global")
s.anonymous = true
s.addremove = false
s:tab("Main", translate("Main"))
-- [[ Global Settings ]]--
o = s:taboption("Main", Flag, "enabled", translate("Main switch"))
o.rmempty = false
---- TCP Node
tcp_node = s:taboption("Main", ListValue, "tcp_node", "" .. translate("TCP Node") .. "")
tcp_node.description = ""
local current_node = luci.sys.exec(string.format("[ -f '/tmp/etc/%s/id/TCP' ] && echo -n $(cat /tmp/etc/%s/id/TCP)", appname, appname))
if current_node and current_node ~= "" and current_node ~= "nil" then
local n = uci:get_all(appname, current_node)
if n then
if tonumber(m:get("@auto_switch[0]", "enable") or 0) == 1 then
local remarks = api.get_full_node_remarks(n)
local url = api.url("node_config", current_node)
tcp_node.description = tcp_node.description .. translatef("Current node: %s", string.format('%s', url, remarks)) .. "
"
end
end
end
tcp_node:value("nil", translate("Close"))
---- UDP Node
udp_node = s:taboption("Main", ListValue, "udp_node", "" .. translate("UDP Node") .. "")
udp_node:value("nil", translate("Close"))
udp_node:value("tcp", translate("Same as the tcp node"))
-- 分流
if (has_v2ray or has_xray) and #nodes_table > 0 then
local normal_list = {}
local shunt_list = {}
for k, v in pairs(nodes_table) do
if v.node_type == "normal" or v.protocol == "_balancing" then
normal_list[#normal_list + 1] = v
end
if v.protocol and v.protocol == "_shunt" then
shunt_list[#shunt_list + 1] = v
end
end
local function get_cfgvalue(shunt_node_id, rule_id)
return function(self, section)
return m:get(shunt_node_id, rule_id) or "nil"
end
end
local function get_write(shunt_node_id, rule_id)
return function(self, section, value)
m:set(shunt_node_id, rule_id, value)
end
end
for k, v in pairs(shunt_list) do
local vid = v.id:sub(1, 8)
o = s:taboption("Main", ListValue, vid .. "-main_node", string.format('%s', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including Default
) has a separate switch that controls whether this rule uses the pre-proxy or not."))
o:depends("tcp_node", v.id)
o:value("nil", translate("Close"))
for k1, v1 in pairs(normal_list) do
o:value(v1.id, v1.remark)
end
o.cfgvalue = get_cfgvalue(v.id, "main_node")
o.write = get_write(v.id, "main_node")
local dialerProxy = s:taboption("Main", Flag, vid .. "-dialerProxy", translate("dialerProxy"))
dialerProxy.default = "0"
if v.type == "Xray" then
dialerProxy:depends("tcp_node", v.id)
else --主设置界面没有type判断,只能判断本分流节点类型是Xray就添加对本分流节点的依赖,但不是的话就没有依赖,会全部显示,所以添加一个不存在的依赖以达到隐藏的目的
dialerProxy:depends("tcp_node", "xray_shunt")
end
dialerProxy.cfgvalue = get_cfgvalue(v.id, "dialerProxy")
dialerProxy.write = get_write(v.id, "dialerProxy")
dialerProxy.rmempty = false
uci:foreach(appname, "shunt_rules", function(e)
local id = e[".name"]
local node_option = vid .. "-" .. id .. "_node"
if id and e.remarks then
o = s:taboption("Main", ListValue, node_option, string.format('* %s', api.url("shunt_rules", id), e.remarks))
o:depends("tcp_node", v.id)
o:value("nil", translate("Close"))
o:value("_default", translate("Default"))
o:value("_direct", translate("Direct Connection"))
o:value("_blackhole", translate("Blackhole"))
local pt = s:taboption("Main", ListValue, vid .. "-".. id .. "_proxy_tag", string.format('* %s', e.remarks .. " " .. translate("Preproxy")))
pt:value("nil", translate("Close"))
pt:value("main", translate("Preproxy Node"))
pt.default = "nil"
for k1, v1 in pairs(normal_list) do
o:value(v1.id, v1.remark)
if v1.protocol ~= "_balancing" then
pt:depends(node_option, v1.id)
end
end
o.cfgvalue = get_cfgvalue(v.id, id)
o.write = get_write(v.id, id)
pt.cfgvalue = get_cfgvalue(v.id, id .. "_proxy_tag")
pt.write = get_write(v.id, id .. "_proxy_tag")
end
end)
local id = "default_node"
o = s:taboption("Main", ListValue, vid .. "-" .. id, string.format('* %s', translate("Default")))
o:depends("tcp_node", v.id)
o:value("_direct", translate("Direct Connection"))
o:value("_blackhole", translate("Blackhole"))
for k1, v1 in pairs(normal_list) do
o:value(v1.id, v1["remark"])
end
o.cfgvalue = get_cfgvalue(v.id, id)
o.write = get_write(v.id, id)
local id = "default_proxy_tag"
o = s:taboption("Main", ListValue, vid .. "-" .. id, string.format('* %s', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node."))
for k1, v1 in pairs(normal_list) do
if v1.protocol ~= "_balancing" then
o:depends(vid .. "-default_node", v1.id)
end
end
o:value("nil", translate("Close"))
o:value("main", translate("Preproxy Node"))
o.cfgvalue = get_cfgvalue(v.id, id)
o.write = get_write(v.id, id)
end
end
tcp_node_socks_port = s:taboption("Main", Value, "tcp_node_socks_port", translate("TCP Node") .. " Socks " .. translate("Listen Port"))
tcp_node_socks_port.default = 1070
tcp_node_socks_port.datatype = "port"
--[[
if has_v2ray or has_xray then
tcp_node_http_port = s:taboption("Main", Value, "tcp_node_http_port", translate("TCP Node") .. " HTTP " .. translate("Listen Port") .. " " .. translate("0 is not use"))
tcp_node_http_port.default = 0
tcp_node_http_port.datatype = "port"
end
]]--
s:tab("DNS", translate("DNS"))
o = s:taboption("DNS", Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6"), translate("Experimental feature."))
o.default = "0"
---- DNS Forward Mode
dns_mode = s:taboption("DNS", ListValue, "dns_mode", translate("Filter Mode"))
dns_mode.rmempty = false
dns_mode:reset_values()
if api.is_finded("dns2tcp") then
dns_mode:value("dns2tcp", translatef("Requery DNS By %s", "TCP"))
end
if api.is_finded("dns2socks") then
dns_mode:value("dns2socks", "dns2socks")
end
if has_xray then
dns_mode:value("xray", "Xray")
end
dns_mode:value("udp", translatef("Requery DNS By %s", "UDP"))
o = s:taboption("DNS", ListValue, "v2ray_dns_mode", " ")
o:value("tcp", "TCP")
o:value("doh", "DoH")
o:value("fakedns", "FakeDNS")
o:depends("dns_mode", "xray")
o.validate = function(self, value, t)
if value == "fakedns" then
local _dns_mode = dns_mode:formvalue(t)
local _tcp_node = tcp_node:formvalue(t)
if m:get(_tcp_node, "type"):lower() ~= _dns_mode then
return nil, translatef("TCP node must be '%s' type to use FakeDNS.", _dns_mode)
end
end
return value
end
o = s:taboption("DNS", Value, "socks_server", translate("Socks Server"), translate("Make sure socks service is available on this address."))
for k, v in pairs(socks_table) do o:value(v.id, v.remarks) end
o.default = socks_table[1].id
o.validate = function(self, value, t)
if not datatypes.ipaddrport(value) then
return nil, translate("Socks Server") .. " " .. translate("Not valid IP format, please re-enter!")
end
return value
end
o:depends({dns_mode = "dns2socks"})
---- DNS Forward
o = s:taboption("DNS", Value, "remote_dns", translate("Remote DNS"))
o.datatype = "or(ipaddr,ipaddrport)"
o.default = "1.1.1.1"
o:value("1.1.1.1", "1.1.1.1 (CloudFlare)")
o:value("1.1.1.2", "1.1.1.2 (CloudFlare-Security)")
o:value("8.8.4.4", "8.8.4.4 (Google)")
o:value("8.8.8.8", "8.8.8.8 (Google)")
o:value("9.9.9.9", "9.9.9.9 (Quad9-Recommended)")
o:value("208.67.220.220", "208.67.220.220 (OpenDNS)")
o:value("208.67.222.222", "208.67.222.222 (OpenDNS)")
o:value("127.0.0.1:5335", "127.0.0.1:5335 (MosDNS)")
o:depends({dns_mode = "dns2socks"})
o:depends({dns_mode = "dns2tcp"})
o:depends({dns_mode = "udp"})
o:depends({v2ray_dns_mode = "tcp"})
---- DoH
o = s:taboption("DNS", Value, "remote_dns_doh", translate("Remote DNS DoH"))
o.default = "https://1.1.1.1/dns-query"
o:value("https://1.1.1.1/dns-query", "CloudFlare")
o:value("https://1.1.1.2/dns-query", "CloudFlare-Security")
o:value("https://8.8.4.4/dns-query", "Google 8844")
o:value("https://8.8.8.8/dns-query", "Google 8888")
o:value("https://9.9.9.9/dns-query", "Quad9-Recommended")
o:value("https://208.67.222.222/dns-query", "OpenDNS")
o:value("https://dns.adguard.com/dns-query,176.103.130.130", "AdGuard")
o:value("https://doh.libredns.gr/dns-query,116.202.176.26", "LibreDNS")
o:value("https://doh.libredns.gr/ads,116.202.176.26", "LibreDNS (No Ads)")
o.validate = doh_validate
o:depends("v2ray_dns_mode", "doh")
o = s:taboption("DNS", Value, "dns_client_ip", translate("EDNS Client Subnet"))
o.description = translate("Notify the DNS server when the DNS query is notified, the location of the client (cannot be a private IP address).") .. "
" ..
translate("This feature requires the DNS server to support the Edns Client Subnet (RFC7871).")
o.datatype = "ipaddr"
o:depends("v2ray_dns_mode", "tcp")
o:depends("v2ray_dns_mode", "doh")
o = s:taboption("DNS", Flag, "dns_cache", translate("Cache Resolved"))
o.default = "1"
o:depends({dns_mode = "dns2socks"})
o:depends({dns_mode = "xray", v2ray_dns_mode = "tcp"})
o:depends({dns_mode = "xray", v2ray_dns_mode = "doh"})
o.rmempty = false
if api.is_finded("chinadns-ng") then
o = s:taboption("DNS", Flag, "chinadns_ng", translate("ChinaDNS-NG"), translate("The effect is better, but will increase the memory."))
o.default = "0"
o:depends({dns_mode = "dns2socks"})
o:depends({dns_mode = "dns2tcp"})
o:depends({dns_mode = "xray", v2ray_dns_mode = "tcp"})
o:depends({dns_mode = "xray", v2ray_dns_mode = "doh"})
o:depends({dns_mode = "udp"})
end
if has_chnlist then
when_chnroute_default_dns = s:taboption("DNS", ListValue, "when_chnroute_default_dns", translate("When using the chnroute list the default DNS"))
when_chnroute_default_dns.default = "direct"
when_chnroute_default_dns:value("remote", translate("Remote DNS"))
when_chnroute_default_dns:value("direct", translate("Direct DNS"))
when_chnroute_default_dns.description = "