api = require "luci.passwall.api" datatypes = api.datatypes local fs = api.fs has_singbox = api.finded_com("sing-box") has_xray = api.finded_com("xray") local has_gfwlist = fs.access("/usr/share/passwall/rules/gfwlist") local has_chnlist = fs.access("/usr/share/passwall/rules/chnlist") local has_chnroute = fs.access("/usr/share/passwall/rules/chnroute") api.set_default_cbi() m = Map() m:appendTemplate("/cbi/nodes_listvalue_com") local nodes_table = {} for _, e in ipairs(api.get_valid_nodes()) do nodes_table[#nodes_table + 1] = e end local normal_list = {} local balancing_list = {} local urltest_list = {} local shunt_list = {} local iface_list = {} for _, v in pairs(nodes_table) do if v.node_type == "normal" then normal_list[#normal_list + 1] = v end if v.protocol and v.protocol == "_balancing" then balancing_list[#balancing_list + 1] = v end if v.protocol and v.protocol == "_urltest" then urltest_list[#urltest_list + 1] = v end if v.protocol and v.protocol == "_shunt" then shunt_list[#shunt_list + 1] = v end if v.protocol and v.protocol == "_iface" then iface_list[#iface_list + 1] = v end end local socks_list = {} local socks_server = "127.0.0.1" .. ":" .. (m:get("@global[0]", "node_socks_port") or "1070") local socks_table = {} socks_table[#socks_table + 1] = { id = socks_server, remark = socks_server .. " - " .. translate("Proxy Node") } m:foreach("socks", function(s) if s.enabled == "1" and s.node then local id, remark for k, n in pairs(nodes_table) do if (s.node == n.id) then remark = n["remark"]; break end end id = "127.0.0.1" .. ":" .. s.port socks_table[#socks_table + 1] = { id = id, remark = id .. " - " .. (remark or translate("Misconfigured")) } if has_singbox or has_xray then socks_list[#socks_list + 1] = { id = s[".name"], remark = translate("Socks Config") .. " " .. string.format("[%s %s]", s.port, translate("Port")), group = "Socks" } end end end) local doh_validate = function(self, value, t) value = value:gsub("%s+", "") if value ~= "" then 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) and not datatypes.ipmask6(v) then flag = 1 end end end if flag == 0 then return value end end return nil, translatef("%s request address","DoH") .. " " .. translate("Format must be:") .. " URL,IP" end m:appendTemplate("/global/status") s = m:section(NamedSection, "@global[0]", "global") s:tab("Main", translate("Main")) -- [[ Global Settings ]]-- o = s:taboption("Main", Flag, "enabled", translate("Main switch")) o.rmempty = false ---- Node o = s:taboption("Main", ListValue, "node", "" .. translate("Proxy Node") .. "") o.template = m:template_path("/cbi/nodes_listvalue") o:value("", translate("Close")) o.group = {""} current_node_id = m:get(s.section, "node") local node_value = s.fields["node"]:formvalue(s.section) if node_value then current_node_id = node_value end current_node = current_node_id and m:get(current_node_id) or {} -- Shunt Start if (has_singbox or has_xray) and #nodes_table > 0 then if #normal_list > 0 or #iface_list > 0 then if current_node.protocol == "_shunt" then local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall/client/include/shunt_options.lua") setfenv(shunt_lua, getfenv(1))(m, s, { node = current_node, verify_option = s.fields["node"], tab = "Shunt", tab_desc = translate("Shunt Rule"), }) end else local tips = s:taboption("Main", DummyValue, "tips", " ") tips.rawhtml = true tips.cfgvalue = function(t, n) return string.format('%s', translate("There are no available nodes, please add or subscribe nodes first.")) end tips:depends("_node", "1") for k, v in pairs(shunt_list) do tips:depends("node", v.id) end for k, v in pairs(balancing_list) do tips:depends("node", v.id) end end end o = s:taboption("Main", Value, "node_socks_port", translate("Node") .. " Socks " .. translate("Listen Port")) o.default = 1070 o.placeholder = 1070 o.datatype = "range(1,65535)" o:depends("_node", "1") --[[ if has_singbox or has_xray then o = s:taboption("Main", Value, "node_http_port", translate("Node") .. " HTTP " .. translate("Listen Port") .. " " .. translate("0 is not use")) o.default = 0 o.datatype = "port" end ]]-- o = s:taboption("Main", Flag, "node_socks_bind_local", translate("Node") .. " Socks " .. translate("Bind Local"), translate("When selected, it can only be accessed localhost.")) o.default = "1" o:depends("_node", "1") o = s:taboption("Main", HideValue, "node_save_before", "") o.value = current_node[".name"] o.cbid = function(self, section) return "node_save_before" end o = s:taboption("Main", HideValue, "_node", "") o:depends({ node = "", ['!reverse'] = true }) -- Node → DNS Depends Settings o = s:taboption("Main", HideValue, "_node_sel_shunt", "") o:depends({ node = "__always__" }) o = s:taboption("Main", HideValue, "_node_sel_other", "") o:depends({ _node_sel_shunt = "1", ['!reverse'] = true }) -- [[ DNS Settings ]]-- s:tab("DNS", translate("DNS")) o = s:taboption("DNS", ListValue, "dns_shunt", "DNS " .. translate("Shunt")) o:value("dnsmasq", "Dnsmasq") o:value("chinadns-ng", translate("ChinaDNS-NG (recommended)")) o.write = function(self, section, value) local old = m:get(section, self.option) if old and old ~= value then m:set(section, "flush_set", "1") end if value ~= "smartdns" then m:del(section, "group_domestic") end return ListValue.write(self, section, value) end if api.is_finded("smartdns") then o:value("smartdns", "SmartDNS") o = s:taboption("DNS", Value, "group_domestic", translate("Domestic group name")) o.placeholder = "local" o.rmempty = false o:depends("dns_shunt", "smartdns") o.description = translate("You only need to configure domestic DNS packets in SmartDNS, and fill in the domestic DNS group name here.") o.validate = function(self, value, section) value = api.trim(value) if value == "" then return nil, translatef("%s cannot be empty.", "SmartDNS " .. translate("Domestic group name")) end return value end end o = s:taboption("DNS", ListValue, "direct_dns_mode", translate("Direct DNS") .. " " .. translate("Request protocol")) o:value("", translate("Auto")) 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"}) o = s:taboption("DNS", Value, "direct_dns", translate("Direct DNS")) o.datatype = "or(ipaddr,ipaddrport(1))" o.default = "223.5.5.5" o:value("223.5.5.5") o:value("223.6.6.6") o:value("180.184.1.1") 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 = s:taboption("DNS", Flag, "filter_proxy_ipv6", translate("Filter Proxy Host IPv6")) o.default = "0" -- 分流时dns过滤模式保存逻辑 function dns_mode_save(section) local f = s.fields["node"] local id_val = f and f:formvalue(section) or "" if id_val == "" then return end for _, v in pairs(shunt_list) do if v.id == id_val then local type_val = v.type if type_val and (type_val == "Xray" or type_val == "sing-box") then local dns_shunt_val = s.fields["dns_shunt"]:formvalue(section) local dns_mode_val = (dns_shunt_val ~= "smartdns") and "dns_mode" or "smartdns_dns_mode" local current_val = m:get(section, dns_mode_val) or "" local new_val = (type_val == "Xray") and "xray" or "sing-box" if current_val ~= new_val then m:set(section, dns_mode_val, new_val) m:del(section, (dns_mode_val == "dns_mode") and "smartdns_dns_mode" or "dns_mode") end local dns_field = s.fields[type_val == "Xray" and "xray_dns_mode" or "singbox_dns_mode"] local v2ray_dns_mode = dns_field and dns_field:formvalue(section) if v2ray_dns_mode and m:get(section, "v2ray_dns_mode") ~= v2ray_dns_mode then m:set(section, "v2ray_dns_mode", v2ray_dns_mode) end break end end end end ---- DNS Forward Mode o = s:taboption("DNS", ListValue, "dns_mode", translate("Filter Mode")) o.default = "tcp" o:value("udp", translatef("Requery DNS By %s", "UDP")) o:value("tcp", translatef("Requery DNS By %s", "TCP")) if api.is_finded("dns2socks") then o:value("dns2socks", "dns2socks") end if has_singbox then o:value("sing-box", "Sing-Box") end if has_xray then o:value("xray", "Xray") end o:depends({ dns_shunt = "chinadns-ng", _node_sel_other = "1" }) o:depends({ dns_shunt = "dnsmasq", _node_sel_other = "1" }) o.write = function(self, section, value) if value ~= "sing-box" and value ~= "xray" then m:del(section, "v2ray_dns_mode") end return ListValue.write(self, section, value) end o.remove = function(self, section) local f = s.fields["smartdns_dns_mode"] if f and f:formvalue(section) then return m:del(section, self.option) end dns_mode_save(section) end ---- SmartDNS Forward Mode if api.is_finded("smartdns") then o = s:taboption("DNS", ListValue, "smartdns_dns_mode", translate("Filter Mode")) o:value("socks", "Socks") if has_singbox then o:value("sing-box", "Sing-Box") end if has_xray then o:value("xray", "Xray") end o:depends({ dns_shunt = "smartdns", _node_sel_other = "1" }) o.write = function(self, section, value) if value == "socks" then m:del(section, "v2ray_dns_mode") end return ListValue.write(self, section, value) end o.remove = function(self, section) local f = s.fields["dns_mode"] if f and f:formvalue(section) then return m:del(section, self.option) end dns_mode_save(section) end o = s:taboption("DNS", DynamicList, "smartdns_remote_dns", translate("Remote DNS")) o:value("tcp://1.1.1.1") o:value("tcp://8.8.4.4") o:value("tcp://8.8.8.8") o:value("tcp://9.9.9.9") o:value("tcp://208.67.222.222") o:value("tls://1.1.1.1") o:value("tls://8.8.4.4") o:value("tls://8.8.8.8") o:value("tls://9.9.9.9") o:value("tls://208.67.222.222") o:value("https://1.1.1.1/dns-query") o:value("https://8.8.4.4/dns-query") o:value("https://8.8.8.8/dns-query") o:value("https://9.9.9.9/dns-query") o:value("https://208.67.222.222/dns-query") o:value("https://dns.adguard.com/dns-query,94.140.14.14") o:value("https://doh.libredns.gr/dns-query,116.202.176.26") o:value("https://doh.libredns.gr/ads,116.202.176.26") o:depends({ dns_shunt = "smartdns", smartdns_dns_mode = "socks" }) o.cfgvalue = function(self, section) return m:get(section, self.option) or {"tcp://1.1.1.1"} end end o = s:taboption("DNS", ListValue, "xray_dns_mode", translate("Remote DNS") .. " " .. translate("Request protocol")) o.default = "tcp" o:value("tcp", "TCP") o:value("udp", "UDP") o:value("doh", "DoH") o:depends("dns_mode", "xray") o:depends("smartdns_dns_mode", "xray") o.cfgvalue = function(self, section) return m:get(section, "v2ray_dns_mode") end o.write = function(self, section, value) local f = s.fields["smartdns_dns_mode"] if s.fields["dns_mode"]:formvalue(section) == "xray" or (f and f:formvalue(section) == "xray") then return m:set(section, "v2ray_dns_mode", value) end end o = s:taboption("DNS", ListValue, "singbox_dns_mode", translate("Remote DNS") .. " " .. translate("Request protocol")) o.default = "tcp" o:value("tcp", "TCP") o:value("udp", "UDP") o:value("doh", "DoH") o:value("http3", "HTTP3(DoH3)") o:value("tls", "TLS(DoT)") o:value("quic", "QUIC(DoQ)") o:depends("dns_mode", "sing-box") o:depends("smartdns_dns_mode", "sing-box") o.cfgvalue = function(self, section) return m:get(section, "v2ray_dns_mode") end o.write = function(self, section, value) local f = s.fields["smartdns_dns_mode"] if s.fields["dns_mode"]:formvalue(section) == "sing-box" or (f and f:formvalue(section) == "sing-box") then return m:set(section, "v2ray_dns_mode", value) end 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.remark) 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(1))" 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)") o:value("149.112.112.112", "149.112.112.112 (Quad9)") o:value("208.67.220.220", "208.67.220.220 (OpenDNS)") o:value("208.67.222.222", "208.67.222.222 (OpenDNS)") o:depends({dns_mode = "dns2socks"}) o:depends({dns_mode = "tcp"}) o:depends({dns_mode = "udp"}) o:depends({xray_dns_mode = "udp"}) o:depends({xray_dns_mode = "tcp"}) o:depends({singbox_dns_mode = "udp"}) o:depends({singbox_dns_mode = "tcp"}) o:depends({singbox_dns_mode = "tls"}) o:depends({singbox_dns_mode = "quic"}) ---- DoH o = s:taboption("DNS", Value, "remote_dns_doh", translate("Remote DNS DoH")) o.description = translate("Format: URL[,IP] (optional IP to map the domain in the URL)") o.default = o.keylist[1] o:value("https://1.1.1.1/dns-query", "1.1.1.1 (CloudFlare)") o:value("https://1.1.1.2/dns-query", "1.1.1.2 (CloudFlare-Security)") o:value("https://8.8.4.4/dns-query", "8.8.4.4 (Google)") o:value("https://8.8.8.8/dns-query", "8.8.8.8 (Google)") o:value("https://9.9.9.9/dns-query", "9.9.9.9 (Quad9)") o:value("https://149.112.112.112/dns-query", "149.112.112.112 (Quad9)") o:value("https://208.67.222.222/dns-query", "208.67.222.222 (OpenDNS)") o:value("https://dns.adguard.com/dns-query,94.140.14.14", "94.140.14.14 (AdGuard)") o:value("https://doh.libredns.gr/dns-query,116.202.176.26", "116.202.176.26 (LibreDNS)") o:value("https://doh.libredns.gr/ads,116.202.176.26", "116.202.176.26 (LibreDNS-NoAds)") o.validate = doh_validate o:depends({xray_dns_mode = "doh"}) o:depends({singbox_dns_mode = "doh"}) o:depends({singbox_dns_mode = "http3"}) o = s:taboption("DNS", Value, "remote_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({dns_mode = "sing-box"}) o:depends({dns_mode = "xray"}) o:depends("dns_shunt", "smartdns") o:depends("_node_sel_shunt", "1") o = s:taboption("DNS", Flag, "remote_fakedns", "FakeDNS", translate("Use FakeDNS work in the domain that proxy.")) o.default = "0" o:depends({dns_mode = "sing-box", dns_shunt = "dnsmasq"}) o:depends({dns_mode = "sing-box", dns_shunt = "chinadns-ng"}) o:depends({smartdns_dns_mode = "sing-box", dns_shunt = "smartdns"}) o:depends({dns_mode = "xray", dns_shunt = "dnsmasq"}) o:depends({dns_mode = "xray", dns_shunt = "chinadns-ng"}) o:depends({smartdns_dns_mode = "xray", dns_shunt = "smartdns"}) --o:depends("_node_sel_shunt", "1") o.validate = function(self, value, t) if value and value == "1" then local _dns_mode = s.fields["dns_mode"]:formvalue(t) if not _dns_mode and s.fields["smartdns_dns_mode"] then _dns_mode = s.fields["smartdns_dns_mode"]:formvalue(t) end local _node = s.fields["node"]:formvalue(t) if _dns_mode and _node then if (m:get(_node, "type") or ""):lower() ~= _dns_mode and not _node:find("socks_") then return nil, translatef("Node must be '%s' type to use FakeDNS.", _dns_mode) end end end return value end o = s:taboption("DNS", Value, "remote_rewrite_ttl", translate("Remote DNS") .. " TTL") o.datatype = "min(1)" o.default = "30" o:depends({dns_mode = "sing-box", dns_shunt = "dnsmasq"}) o:depends({dns_mode = "sing-box", dns_shunt = "chinadns-ng"}) o:depends({smartdns_dns_mode = "sing-box", dns_shunt = "smartdns"}) o = s:taboption("DNS", ListValue, "chinadns_ng_default_tag", translate("Default DNS")) o.default = "none" o:value("gfw", translate("Remote DNS")) o:value("chn", translate("Direct DNS")) o:value("none", translate("Smart, Do not accept no-ip reply from Direct DNS")) o:value("none_noip", translate("Smart, Accept no-ip reply from Direct DNS")) local desc = "" o:depends({dns_shunt = "chinadns-ng", tcp_proxy_mode = "proxy", chn_list = "direct"}) o = s:taboption("DNS", ListValue, "use_default_dns", translate("Default DNS")) o.default = "direct" o:value("remote", translate("Remote DNS")) o:value("direct", translate("Direct DNS")) o.description = desc .. "" o:depends({dns_shunt = "dnsmasq", tcp_proxy_mode = "proxy", chn_list = "direct"}) if api.is_finded("smartdns") then o:depends({dns_shunt = "smartdns", tcp_proxy_mode = "proxy", chn_list = "direct"}) end o = s:taboption("DNS", Flag, "force_https_soa", translate("Force HTTPS SOA"), translate("Force queries with qtype 65 to respond with an SOA record.")) o.default = "0" o:depends({dns_shunt = "chinadns-ng"}) if api.is_finded("smartdns") then o:depends({dns_shunt = "smartdns"}) end o = s:taboption("DNS", Flag, "dns_redirect", translate("DNS Redirect"), translate("Force special DNS server to need proxy devices.")) o.default = "1" o.rmempty = false local prefer_nft = m:get("@global_forwarding[0]", "prefer_nft") == "1" local set_title = api.i18n.translate(prefer_nft and "Clear NFTSET on Reboot" or "Clear IPSET on Reboot") o = s:taboption("DNS", Flag, "flush_set_on_reboot", set_title, translate("Clear IPSET/NFTSET on service reboot. This may increase reboot time.")) o.default = "0" set_title = api.i18n.translate(prefer_nft and "Clear NFTSET" or "Clear IPSET") o = s:taboption("DNS", DummyValue, "clear_ipset", set_title, translate("Try this feature if the rule modification does not take effect.")) o.rawhtml = true function o.cfgvalue(self, section) return string.format( [[]], api.url("flush_set") .. "?redirect=1&reload=1", set_title) end s:tab("Proxy", translate("Mode")) o = s:taboption("Proxy", Flag, "use_direct_list", translatef("Use %s", translate("Direct List"))) o.default = "1" o = s:taboption("Proxy", Flag, "use_proxy_list", translatef("Use %s", translate("Proxy List"))) o.default = "1" o = s:taboption("Proxy", Flag, "use_block_list", translatef("Use %s", translate("Block List"))) o.default = "1" if has_gfwlist then o = s:taboption("Proxy", Flag, "use_gfw_list", translatef("Use %s", translate("GFW List"))) o.default = "1" end if has_chnlist or has_chnroute then o = s:taboption("Proxy", ListValue, "chn_list", translate("China List")) o:value("0", translate("Close(Not use)")) o:value("direct", translate("Direct Connection")) o:value("proxy", translate("Proxy")) o.default = "direct" end ---- TCP Default Proxy Mode o = s:taboption("Proxy", ListValue, "tcp_proxy_mode", "TCP " .. translate("Default Proxy Mode")) o:value("disable", translate("No Proxy")) o:value("proxy", translate("Proxy")) o.default = "proxy" ---- UDP Default Proxy Mode o = s:taboption("Proxy", ListValue, "udp_proxy_mode", "UDP " .. translate("Default Proxy Mode")) o:value("disable", translate("No Proxy")) o:value("proxy", translate("Proxy")) o.default = "proxy" o = s:taboption("Proxy", DummyValue, "switch_mode", "") o.template = m:template_path("/global/proxy") ---- Check the transparent proxy component local handle = io.popen("lsmod") local mods = "" if handle then mods = handle:read("*a") or "" handle:close() end if (mods:find("REDIRECT") and mods:find("TPROXY")) or (mods:find("nft_redir") and mods:find("nft_tproxy")) then o = s:taboption("Proxy", Flag, "localhost_proxy", translate("Localhost Proxy"), translate("When selected, localhost can transparent proxy.")) o.default = "1" o.rmempty = false o = s:taboption("Proxy", Flag, "client_proxy", translate("Client Proxy"), translate("When selected, devices in LAN can transparent proxy. Otherwise, it will not be proxy. But you can still use access control to allow the designated device to proxy.")) o.default = "1" o.rmempty = false else local html = string.format([[
%s
]], translate("Missing components, transparent proxy is unavailable.")) o = s:taboption("Proxy", DummyValue, "localhost_proxy", translate("Localhost Proxy")) o.rawhtml = true function o.cfgvalue(self, section) return html end o = s:taboption("Proxy", DummyValue, "client_proxy", translate("Client Proxy")) o.rawhtml = true function o.cfgvalue(self, section) return html end end o = s:taboption("Proxy", DummyValue, "_proxy_tips", " ") o.rawhtml = true o.cfgvalue = function(t, n) return string.format('%s', api.url("acl"), translate("Want different devices to use different proxy modes/ports/nodes? Please use access control.")) end s:tab("log", translate("Log")) o = s:taboption("log", Flag, "log_node", translate("Enable Node Log")) o.default = "0" o.rmempty = false o = s:taboption("log", ListValue, "loglevel", translate("Log Level")) o.default = "warn" o:value("debug", "Debug") o:value("info", "Info") o:value("warn", "Warning") o:value("error", "Error") o = s:taboption("log", DummyValue, "_node_log", translate("Log File")) o.rawhtml = true o.cfgvalue = function(t, n) local log_file = api.TMP_PATH .. "/acl/default/global.log" local log_url = api.url("get_redir_log") .. "?id=default" local s = "%s  " % log_file if api.fs.access(log_file) then local btn = string.format( '', translate("View Log"), log_url ) s = s .. btn end return s end o:depends("log_node", "1") o = s:taboption("log", Flag, "log_chinadns_ng", translate("Enable") .. " ChinaDNS-NG " .. translate("Log")) o.default = "0" o.rmempty = false o:depends("dns_shunt", "chinadns-ng") o = s:taboption("log", DummyValue, "_chinadns_ng_log", translate("Log File")) o.rawhtml = true o.cfgvalue = function(t, n) local log_file = api.TMP_PATH .. "/acl/default/chinadns_ng.log" local log_url = api.url("get_chinadns_log") .. "?flag=default" local s = "%s  " % log_file if api.fs.access(log_file) then local btn = string.format( '', translate("View Log"), log_url ) s = s .. btn end return s end o:depends("log_chinadns_ng", "1") o = s:taboption("log", DummyValue, "_log_tips", " ") o.rawhtml = true o.cfgvalue = function(t, n) return string.format('%s', translate("It is recommended to disable logging during regular use to reduce system overhead.")) end s:tab("faq", "FAQ") o = s:taboption("faq", DummyValue, "") o.template = m:template_path("/global/faq") s:tab("maintain", translate("Maintain")) o = s:taboption("maintain", DummyValue, "") o.template = m:template_path("/global/backup") m:appendTemplate("/include/node_change", { verify_option = s.fields["node"], shunt_list = api.jsonc.stringify(shunt_list) }) -- [[ Socks Server ]]-- o = s:taboption("Main", Flag, "socks_enabled", "Socks " .. translate("Main switch")) o.rmempty = false s2 = m:section(TypedSection, "socks", translate("Socks Config")) s2.template = "cbi/tblsection" s2.sortable = true s2.anonymous = true s2.addremove = true s2.extedit = api.url("socks_config", "%s") function s2.create(e, t) local uid = "socks_" .. api.gen_random_char(5) local n = 1 m:foreach("socks", function(s) if s[".name"] == section then return false end n = n + 1 end) TypedSection.create(e, uid) m:set(uid, "port", 1080 + n) m:set(uid, "http_port", 0) luci.http.redirect(e.extedit:format(uid)) end function s2.remove(e, t) local new_node = "" local node0 = m:get("@nodes[0]") or nil if node0 then new_node = node0[".name"] end if (m:get("@global[0]", "node") or "") == t then m:set('@global[0]', "node", new_node) end m:foreach("acl_rule", function(s) if s["node"] and s["node"] == t then m:set(s[".name"], "node", "default") end end) m:foreach("nodes", function(s) local list_name = s["urltest_node"] and "urltest_node" or (s["balancing_node"] and "balancing_node") if list_name then local nodes = m.uci:get_list(api.c_config, s[".name"], list_name) if nodes then local changed = false local new_nodes = {} for _, node in ipairs(nodes) do if node ~= t then table.insert(new_nodes, node) else changed = true end end if changed then api.uci_set_c(s[".name"], list_name, new_nodes) end end end if s["fallback_node"] == t then m:del(s[".name"], "fallback_node") end end) TypedSection.remove(e, t) end ---- Enable o = s2:option(Flag, "enabled", translate("Enable")) o.default = 1 o.rmempty = false o = s2:option(ListValue, "node", translate("Socks Node")) o.template = m:template_path("/cbi/nodes_listvalue") o.group = {} o = s2:option(DummyValue, "now_node", translate("Current Node")) o.rawhtml = true o.cfgvalue = function(_, n) local current_node = api.get_cache_var(n) if current_node then local node = m:get(current_node) if node then return (api.get_node_remarks(node) or ""):gsub("(:)%[", "%1
[") end end end o = s2:option(DummyValue, "port_status", translate("Port Status")) o.rawhtml = true o.cfgvalue = function(t, n) local socks = m:get(n, "port") or "0" local http = m:get(n, "http_port") or "0" return string.format([[
SOCKS: %s
HTTP: %s
]], n, socks, http) end local o_node = s.fields["node"] local o_socks = s2.fields["node"] for k, v in pairs(socks_list) do o_node:value(v.id, v["remark"]) o_node.group[#o_node.group+1] = (v.group and v.group ~= "") and v.group or translate("default") end for k, v in pairs(nodes_table) do if #normal_list == 0 and #iface_list == 0 then break end if v.protocol and v.protocol == "_shunt" then if has_singbox or has_xray then o_node:value(v.id, v["remark"]) o_node.group[#o_node.group+1] = (v.group and v.group ~= "") and v.group or translate("default") s.fields["_node_sel_shunt"]:depends({ node = v.id }) if m:get(v.id, "type") == "Xray" then s.fields["xray_dns_mode"]:depends({ node = v.id }) else s.fields["singbox_dns_mode"]:depends({ node = v.id }) s.fields["remote_rewrite_ttl"]:depends({ node = v.id }) end end else o_node:value(v.id, v["remark"]) o_node.group[#o_node.group+1] = (v.group and v.group ~= "") and v.group or translate("default") end if v.type == "Socks" then if has_singbox or has_xray then o_socks:value(v.id, v["remark"]) o_socks.group[#o_socks.group+1] = (v.group and v.group ~= "") and v.group or translate("default") end else o_socks:value(v.id, v["remark"]) o_socks.group[#o_socks.group+1] = (v.group and v.group ~= "") and v.group or translate("default") end end m:appendTemplate("/global/footer") m:appendTemplate("/cbi/sortable", {sectiontype = s2.sectiontype}) return api.return_map(m)