diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua index 38c96631..b0b84851 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/rule.lua @@ -89,9 +89,8 @@ if has_xray or has_singbox then o.rmempty = false o.description = "" function o.write(self, section, value) local old = m:get(section, self.option) or "0" diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po index 7137cdf4..9f29337b 100644 --- a/luci-app-passwall/po/zh-cn/passwall.po +++ b/luci-app-passwall/po/zh-cn/passwall.po @@ -1085,15 +1085,12 @@ msgstr "启用此选项后手动更新时,即使没有新版本也会从现有 G msgid "Enable Geo Data Parsing" msgstr "开启 Geo 数据解析" -msgid "Analyzes and preloads GeoIP/Geosite data to enhance the shunt performance of Sing-box/Xray." -msgstr "分析和预加载 GeoIP/Geosite 数据,以增强 Sing-box/Xray 的分流效果。" +msgid "Parses and preloads GeoIP/Geosite data to improve Sing-box/Xray routing performance." +msgstr "解析和预加载 GeoIP/Geosite 数据,以增强 Sing-box/Xray 的分流效果。" msgid "Once enabled, the rule list can support GeoIP/Geosite rules." msgstr "启用后,规则列表可以支持 GeoIP/Geosite 规则。" -msgid "Note: Increases resource usage; Geosite analysis is only supported in ChinaDNS-NG and SmartDNS modes." -msgstr "注:会增加一些系统资源的开销,仅在 ChinaDNS-NG 和 SmartDNS 模式下支持分析 Geosite 。" - msgid "Shunt Rule" msgstr "分流规则" diff --git a/luci-app-passwall/root/usr/share/passwall/helper_chinadns_add.lua b/luci-app-passwall/root/usr/share/passwall/helper_chinadns_add.lua index 6ad220ee..04d7aab0 100644 --- a/luci-app-passwall/root/usr/share/passwall/helper_chinadns_add.lua +++ b/luci-app-passwall/root/usr/share/passwall/helper_chinadns_add.lua @@ -227,7 +227,7 @@ if USE_DIRECT_LIST == "1" and not fs.access(file_direct_host) then geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line else line = api.get_std_domain(line) - if line ~= "" and not line:find("#") then + if line ~= "" and not line:find("#") and not line:find(":") then insert_unique(direct_domain, line, lookup_direct_domain) end end @@ -277,7 +277,7 @@ if USE_PROXY_LIST == "1" and not fs.access(file_proxy_host) then geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line else line = api.get_std_domain(line) - if line ~= "" and not line:find("#") then + if line ~= "" and not line:find("#") and not line:find(":") then insert_unique(proxy_domain, line, lookup_proxy_domain) end end @@ -398,7 +398,7 @@ if IS_SHUNT_NODE then local domain_list = s.domain_list or "" for line in string.gmatch(domain_list, "[^\r\n]+") do - if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") then + if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") and not line:find("rule-set:") and not line:find("rs:") then if line:find("geosite:") then line = string.match(line, ":([^:]+)$") if _node_id == "_direct" then diff --git a/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua b/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua index b96d27fe..7c916ae9 100644 --- a/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua +++ b/luci-app-passwall/root/usr/share/passwall/helper_dnsmasq.lua @@ -181,10 +181,15 @@ function add_rule(var) local CACHE_TEXT_FILE = CACHE_DNS_PATH .. ".txt" local USE_CHINADNS_NG = "0" local IS_SHUNT_NODE = uci:get(appname, TCP_NODE, "protocol") == "_shunt" + local USE_GEOVIEW = uci:get(appname, "@global_rules[0]", "enable_geoview") local list1 = {} local excluded_domain = {} + if not api.is_finded("geoview") then + USE_GEOVIEW = "0" + end + local function log(...) if NO_LOGIC_LOG == "1" then return @@ -301,6 +306,21 @@ function add_rule(var) return false end + local function foreach_geosite(list_arg, callback) + local geosite_path = uci:get(appname, "@global_rules[0]", "v2ray_location_asset") or "/usr/share/v2ray/" + geosite_path = geosite_path:match("^(.*)/") .. "/geosite.dat" + if not fs.access(geosite_path) then return end + local bin = api.finded_com("geoview") + if not (bin and list_arg) then return end + local cmd = string.format("%q -type geosite -action extract -input %q -list %q -lowmem=true", bin, geosite_path, list_arg) + local pipe = io.popen(cmd) + if not pipe then return end + for line in pipe:lines() do + if line ~= "" then callback(line) end + end + pipe:close() + end + local cache_text = "" local nodes_address_md5 = sys.exec("echo -n $(uci show passwall | grep '\\.address') | md5sum") local new_rules = sys.exec("echo -n $(find /usr/share/passwall/rules -type f | xargs md5sum)") @@ -347,16 +367,28 @@ function add_rule(var) --屏蔽列表 if USE_CHINADNS_NG == "0" and USE_BLOCK_LIST == "1" then + local geosite_arg = "" local f = io.open("/usr/share/passwall/rules/block_host") if f then for line in f:lines() do - line = api.get_std_domain(line) - if line ~= "" and not line:find("#") and not line:find(":") then - set_domain_address(line, "") + if not line:find("#") and line:find("geosite:") then + line = string.match(line, ":([^:]+)$") + geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line + else + line = api.get_std_domain(line) + if line ~= "" and not line:find("#") and not line:find(":") then + set_domain_address(line, "") + end end end f:close() end + if USE_GEOVIEW == "1" and geosite_arg ~= "" then + foreach_geosite(geosite_arg, function(line) + set_domain_address(line, "") + end) + log(" - 解析[屏蔽列表] Geosite 到屏蔽域名表(blocklist)完成") + end end local fwd_dns @@ -400,88 +432,124 @@ function add_rule(var) --直连(白名单)列表 if USE_DIRECT_LIST == "1" then - if fs.access("/usr/share/passwall/rules/direct_host") then - fwd_dns = LOCAL_DNS - if USE_CHINADNS_NG == "1" then - fwd_dns = nil - end - if fwd_dns then - local sets = { - setflag_4 .. "psw_white", - setflag_6 .. "psw_white6" - } - --始终用国内DNS解析直连(白名单)列表 - for line in io.lines("/usr/share/passwall/rules/direct_host") do - line = api.get_std_domain(line) - if line ~= "" and not line:find("#") and not line:find(":") then - add_excluded_domain(line) - set_domain_dns(line, fwd_dns) - set_domain_ipset(line, table.concat(sets, ",")) + fwd_dns = LOCAL_DNS + if USE_CHINADNS_NG == "1" then + fwd_dns = nil + end + if fwd_dns then + local sets = { + setflag_4 .. "psw_white", + setflag_6 .. "psw_white6" + } + --始终用国内DNS解析直连(白名单)列表 + local geosite_arg = "" + local f = io.open("/usr/share/passwall/rules/direct_host") + if f then + for line in f:lines() do + if not line:find("#") and line:find("geosite:") then + line = string.match(line, ":([^:]+)$") + geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line + else + line = api.get_std_domain(line) + if line ~= "" and not line:find("#") and not line:find(":") then + add_excluded_domain(line) + set_domain_dns(line, fwd_dns) + set_domain_ipset(line, table.concat(sets, ",")) + end end end + f:close() log(string.format(" - 域名白名单(whitelist):%s", fwd_dns or "默认")) end + if USE_GEOVIEW == "1" and geosite_arg ~= "" then + foreach_geosite(geosite_arg, function(line) + add_excluded_domain(line) + set_domain_dns(line, fwd_dns) + set_domain_ipset(line, table.concat(sets, ",")) + end) + log(" - 解析[直连列表] Geosite 到域名白名单(whitelist)完成") + end end end --代理(黑名单)列表 if USE_PROXY_LIST == "1" then - if fs.access("/usr/share/passwall/rules/proxy_host") then - fwd_dns = TUN_DNS - if USE_CHINADNS_NG == "1" then - fwd_dns = nil + fwd_dns = TUN_DNS + if USE_CHINADNS_NG == "1" then + fwd_dns = nil + end + if fwd_dns then + local set_name = "psw_black" + local set6_name = "psw_black6" + if FLAG ~= "default" then + set_name = "psw_" .. FLAG .. "_black" + set6_name = "psw_" .. FLAG .. "_black6" end - if fwd_dns then - local set_name = "psw_black" - local set6_name = "psw_black6" - if FLAG ~= "default" then - set_name = "psw_" .. FLAG .. "_black" - set6_name = "psw_" .. FLAG .. "_black6" - end - local sets = { - setflag_4 .. set_name - } - if NO_PROXY_IPV6 ~= "1" then - table.insert(sets, setflag_6 .. set6_name) - end - --始终使用远程DNS解析代理(黑名单)列表 - for line in io.lines("/usr/share/passwall/rules/proxy_host") do - line = api.get_std_domain(line) - if line ~= "" and not line:find("#") and not line:find(":") then - add_excluded_domain(line) - if NO_PROXY_IPV6 == "1" then - set_domain_address(line, "::") + local sets = { + setflag_4 .. set_name + } + if NO_PROXY_IPV6 ~= "1" then + table.insert(sets, setflag_6 .. set6_name) + end + --始终使用远程DNS解析代理(黑名单)列表 + local geosite_arg = "" + local f = io.open("/usr/share/passwall/rules/proxy_host") + if f then + for line in f:lines() do + if not line:find("#") and line:find("geosite:") then + line = string.match(line, ":([^:]+)$") + geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line + else + line = api.get_std_domain(line) + if line ~= "" and not line:find("#") and not line:find(":") then + add_excluded_domain(line) + if NO_PROXY_IPV6 == "1" then + set_domain_address(line, "::") + end + set_domain_dns(line, fwd_dns) + set_domain_ipset(line, table.concat(sets, ",")) end - set_domain_dns(line, fwd_dns) - set_domain_ipset(line, table.concat(sets, ",")) end end + f:close() log(string.format(" - 代理域名表(blacklist):%s", fwd_dns or "默认")) end + if USE_GEOVIEW == "1" and geosite_arg ~= "" then + foreach_geosite(geosite_arg, function(line) + add_excluded_domain(line) + if NO_PROXY_IPV6 == "1" then + set_domain_address(line, "::") + end + set_domain_dns(line, fwd_dns) + set_domain_ipset(line, table.concat(sets, ",")) + end) + log(" - 解析[代理列表] Geosite 到代理域名表(blacklist)完成") + end end end --GFW列表 if USE_GFW_LIST == "1" then - if fs.access("/usr/share/passwall/rules/gfwlist") then - fwd_dns = TUN_DNS - if USE_CHINADNS_NG == "1" then - fwd_dns = nil + fwd_dns = TUN_DNS + if USE_CHINADNS_NG == "1" then + fwd_dns = nil + end + if fwd_dns then + local set_name = "psw_gfw" + local set6_name = "psw_gfw6" + if FLAG ~= "default" then + set_name = "psw_" .. FLAG .. "_gfw" + set6_name = "psw_" .. FLAG .. "_gfw6" end - if fwd_dns then - local set_name = "psw_gfw" - local set6_name = "psw_gfw6" - if FLAG ~= "default" then - set_name = "psw_" .. FLAG .. "_gfw" - set6_name = "psw_" .. FLAG .. "_gfw6" - end - local sets = { - setflag_4 .. set_name - } - if NO_PROXY_IPV6 ~= "1" then - table.insert(sets, setflag_6 .. set6_name) - end - for line in io.lines("/usr/share/passwall/rules/gfwlist") do + local sets = { + setflag_4 .. set_name + } + if NO_PROXY_IPV6 ~= "1" then + table.insert(sets, setflag_6 .. set6_name) + end + local f = io.open("/usr/share/passwall/rules/gfwlist") + if f then + for line in f:lines() do if line ~= "" and not line:find("#") and not check_excluded_domain(line) then if NO_PROXY_IPV6 == "1" then set_domain_address(line, "::") @@ -494,6 +562,7 @@ function add_rule(var) set_domain_ipset(line, table.concat(sets, ",")) end end + f:close() log(string.format(" - 防火墙域名表(gfwlist):%s", fwd_dns or "默认")) end end @@ -501,30 +570,31 @@ function add_rule(var) --中国列表 if CHN_LIST ~= "0" then - if fs.access("/usr/share/passwall/rules/chnlist") then + fwd_dns = nil + if CHN_LIST == "direct" then + fwd_dns = LOCAL_DNS + end + if CHN_LIST == "proxy" then + fwd_dns = TUN_DNS + end + if USE_CHINADNS_NG == "1" then fwd_dns = nil - if CHN_LIST == "direct" then - fwd_dns = LOCAL_DNS - end + end + if fwd_dns then + local sets = { + setflag_4 .. "psw_chn", + setflag_6 .. "psw_chn6" + } if CHN_LIST == "proxy" then - fwd_dns = TUN_DNS - end - if USE_CHINADNS_NG == "1" then - fwd_dns = nil - end - if fwd_dns then - local sets = { - setflag_4 .. "psw_chn", - setflag_6 .. "psw_chn6" - } - if CHN_LIST == "proxy" then - if NO_PROXY_IPV6 == "1" then - sets = { - setflag_4 .. "psw_chn" - } - end + if NO_PROXY_IPV6 == "1" then + sets = { + setflag_4 .. "psw_chn" + } end - for line in io.lines("/usr/share/passwall/rules/chnlist") do + end + local f = io.open("/usr/share/passwall/rules/chnlist") + if f then + for line in f:lines() do if line ~= "" and not line:find("#") and not check_excluded_domain(line) then if CHN_LIST == "proxy" and NO_PROXY_IPV6 == "1" then set_domain_address(line, "::") @@ -537,6 +607,7 @@ function add_rule(var) set_domain_ipset(line, table.concat(sets, ",")) end end + f:close() log(string.format(" - 中国域名表(chnroute):%s", fwd_dns or "默认")) end end @@ -590,21 +661,40 @@ function add_rule(var) end local domain_list = s.domain_list or "" + local geosite_arg = "" for line in string.gmatch(domain_list, "[^\r\n]+") do - if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("geosite:") and not line:find("ext:") then - if line:find("domain:") or line:find("full:") then + if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") and not line:find("rule-set:") and not line:find("rs:") then + if line:find("geosite:") then line = string.match(line, ":([^:]+)$") - end - line = api.get_std_domain(line) - add_excluded_domain(line) + geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line + else + if line:find("domain:") or line:find("full:") then + line = string.match(line, ":([^:]+)$") + end + line = api.get_std_domain(line) + add_excluded_domain(line) + if no_ipv6 then + set_domain_address(line, "::") + end + set_domain_dns(line, fwd_dns) + set_domain_ipset(line, table.concat(sets, ",")) + end + end + end + + if USE_GFW_LIST == "1" and CHN_LIST == "0" and USE_GEOVIEW == "1" and geosite_arg ~= "" then --仅GFW模式解析geosite + foreach_geosite(geosite_arg, function(line) + add_excluded_domain(line) if no_ipv6 then set_domain_address(line, "::") end set_domain_dns(line, fwd_dns) set_domain_ipset(line, table.concat(sets, ",")) - end + end) + log(string.format(" - 解析分流规则(%s) Geosite 完成", s.remarks)) end + if _node_id ~= "_direct" then log(string.format(" - Sing-Box/Xray分流规则(%s):%s", s.remarks, fwd_dns or "默认")) end diff --git a/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua b/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua index 4011fc09..2d046099 100644 --- a/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua +++ b/luci-app-passwall/root/usr/share/passwall/helper_smartdns_add.lua @@ -388,7 +388,7 @@ if USE_DIRECT_LIST == "1" and not fs.access(file_direct_host) then geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line else line = api.get_std_domain(line) - if line ~= "" and not line:find("#") then + if line ~= "" and not line:find("#") and not line:find(":") then insert_unique(direct_domain, line, lookup_direct_domain) end end @@ -440,7 +440,7 @@ if USE_PROXY_LIST == "1" and not fs.access(file_proxy_host) then geosite_arg = geosite_arg .. (geosite_arg ~= "" and "," or "") .. line else line = api.get_std_domain(line) - if line ~= "" and not line:find("#") then + if line ~= "" and not line:find("#") and not line:find(":") then insert_unique(proxy_domain, line, lookup_proxy_domain) end end @@ -566,7 +566,7 @@ if IS_SHUNT_NODE then local domain_list = s.domain_list or "" for line in string.gmatch(domain_list, "[^\r\n]+") do - if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") then + if line ~= "" and not line:find("#") and not line:find("regexp:") and not line:find("ext:") and not line:find("rule-set:") and not line:find("rs:") then if line:find("geosite:") then line = string.match(line, ":([^:]+)$") if _node_id == "_direct" then diff --git a/luci-app-passwall2/Makefile b/luci-app-passwall2/Makefile index 6a80b3bb..25e88ebb 100644 --- a/luci-app-passwall2/Makefile +++ b/luci-app-passwall2/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=luci-app-passwall2 -PKG_VERSION:=26.7.16 +PKG_VERSION:=26.8.7 PKG_RELEASE:=1 PKG_PO_VERSION:=$(PKG_VERSION) diff --git a/luci-app-passwall2/htdocs/luci-static/resources/view/passwall2/func.js b/luci-app-passwall2/htdocs/luci-static/resources/view/passwall2/func.js new file mode 100644 index 00000000..252e8feb --- /dev/null +++ b/luci-app-passwall2/htdocs/luci-static/resources/view/passwall2/func.js @@ -0,0 +1,45 @@ +function arraysEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) return false; + } + return true; +} + +function waitForElement(selector, callback) { + const el = document.querySelector(selector); + if (el) return callback(el); + const observer = new MutationObserver(() => { + const el = document.querySelector(selector); + if (el) { + observer.disconnect(); + callback(el); + } + }); + observer.observe(document.body, { childList: true, subtree: true }); +} + +function get_current_url() { + return window.location.origin + window.location.pathname; +} + +function getOption(config, section, opt) { + let obj; + const id = `cbid.${config}.${section}.${opt}`; + obj = document.getElementsByName(id)[0] || document.getElementById(id); + if (obj) { + const combobox = document.getElementById('cbi.combobox.' + id); + if (combobox) { + obj.combobox = combobox; + } + const div = document.getElementById(id); + if (div && div.getElementsByTagName("li").length > 0) { + obj = div; + } + return obj; + } else { + return null; + } +} diff --git a/luci-app-passwall2/luasrc/controller/passwall2.lua b/luci-app-passwall2/luasrc/controller/passwall2.lua index 9a9b1c35..13af6535 100644 --- a/luci-app-passwall2/luasrc/controller/passwall2.lua +++ b/luci-app-passwall2/luasrc/controller/passwall2.lua @@ -79,8 +79,8 @@ function index() entry({"admin", "services", appname, "connect_status"}, call("connect_status")).leaf = true entry({"admin", "services", appname, "ping_node"}, call("ping_node")).leaf = true entry({"admin", "services", appname, "urltest_node"}, call("urltest_node")).leaf = true + entry({"admin", "services", appname, "update_config"}, call("update_config")).leaf = true entry({"admin", "services", appname, "add_node"}, call("add_node")).leaf = true - entry({"admin", "services", appname, "update_node"}, call("update_node")).leaf = true entry({"admin", "services", appname, "set_node"}, call("set_node")).leaf = true entry({"admin", "services", appname, "copy_node"}, call("copy_node")).leaf = true entry({"admin", "services", appname, "clear_all_nodes"}, call("clear_all_nodes")).leaf = true @@ -96,6 +96,10 @@ function index() entry({"admin", "services", appname, "subscribe_manual"}, call("subscribe_manual")).leaf = true entry({"admin", "services", appname, "subscribe_manual_all"}, call("subscribe_manual_all")).leaf = true entry({"admin", "services", appname, "flush_set"}, call("flush_set")).leaf = true + entry({"admin", "services", appname, "get_shunt_rules"}, call("get_shunt_rules")).leaf = true + entry({"admin", "services", appname, "add_shunt_rule"}, call("add_shunt_rule")).leaf = true + entry({"admin", "services", appname, "delete_select_shunt_rules"}, call("delete_select_shunt_rules")).leaf = true + entry({"admin", "services", appname, "save_shunt_rule_order"}, call("save_shunt_rule_order")).leaf = true --[[Components update]] entry({"admin", "services", appname, "check_passwall2"}, call("app_check")).leaf = true @@ -369,6 +373,23 @@ function urltest_node() http_write_json(e) end +function update_config() + local id = http.formvalue("id") -- Node id + local data = http.formvalue("data") -- json new Data + if id and data then + local data_t = jsonParse(data) or {} + if next(data_t) then + for k, v in pairs(data_t) do + uci:set(appname, id, k, v) + end + api.uci_save(uci, appname) + http_write_json_ok() + return + end + end + http_write_json_error() +end + function add_node() local redirect = http.formvalue("redirect") @@ -391,23 +412,6 @@ function add_node() end end -function update_node() - local id = http.formvalue("id") -- Node id - local data = http.formvalue("data") -- json new Data - if id and data then - local data_t = jsonParse(data) or {} - if next(data_t) then - for k, v in pairs(data_t) do - uci:set(appname, id, k, v) - end - api.uci_save(uci, appname) - http_write_json_ok() - return - end - end - http_write_json_error() -end - function set_node() local type = http.formvalue("type") local config = http.formvalue("config") @@ -968,3 +972,87 @@ function fetch_certsha256() local data = api.fetch_cert_sha256(address, port, sni, 5) http_write_json(data ~= "" and { code = 1, data = data } or { code = 0 }) end + +function get_shunt_rules() + local id = http.formvalue("id") + local result = {} + + if id then + result = uci:get_all(appname, id) + else + local default_items = {} + local other_items = {} + uci:foreach(appname, "shunt_rules", function(t) + if not t.group or t.group == "" then + default_items[#default_items + 1] = t + else + other_items[#other_items + 1] = t + end + end) + for i = 1, #default_items do result[#result + 1] = default_items[i] end + for i = 1, #other_items do result[#result + 1] = other_items[i] end + end + http_write_json(result) +end + +function add_shunt_rule() + local add_name = http.formvalue("add_name") + local redirect = http.formvalue("redirect") + + local uuid = add_name + if add_name then + local has = uci:get(appname, uuid) + if has then + http_write_json_error({ message = "This ID already exists." }) + return + end + else + uuid = api.gen_short_uuid() + end + uci:section(appname, "shunt_rules", uuid) + + local group = http.formvalue("group") + if group and group ~= "default" then + uci:set(appname, uuid, "group", group) + end + + if redirect == "1" then + api.uci_save(uci, appname) + http.redirect(api.url("shunt_rules", uuid)) + else + api.uci_save(uci, appname) + http_write_json_ok({uuid = uuid, redirect_url = api.url("shunt_rules", uuid)}) + end +end + +function delete_select_shunt_rules() + local ids = http.formvalue("ids") + local redirect = http.formvalue("redirect") + string.gsub(ids, '[^' .. "," .. ']+', function(w) + uci:foreach(appname, "nodes", function(s) + if s["protocol"] and s["protocol"] == "_shunt" then + uci:delete(appname, s[".name"], w) + end + end) + uci:delete(appname, w) + end) + if redirect == "1" then + api.uci_save(uci, appname) + http.redirect(api.url("rule")) + else + api.uci_save(uci, appname, true, true) + end +end + +function save_shunt_rule_order() + local ids = http.formvalue("ids") or "" + local new_order = {} + for id in ids:gmatch("([^,]+)") do + new_order[#new_order + 1] = id + end + for idx, name in ipairs(new_order) do + luci.sys.call(string.format("uci -q reorder %s.%s=%d", appname, name, idx - 1)) + end + api.sh_uci_commit(appname) + http_write_json({ status = "ok" }) +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua index 7e0e4a82..6f506950 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua @@ -101,6 +101,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then if current_node.protocol == "_shunt" then local shunt_lua = loadfile("/usr/lib/lua/luci/model/cbi/passwall2/client/include/shunt_options.lua") setfenv(shunt_lua, getfenv(1))(m, s, { + s_cfgid = s:cfgsections()[1], node_id = current_node_id, node = current_node, socks_list = socks_list, diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/include/shunt_options.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/include/shunt_options.lua index e1218026..3852aac1 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/include/shunt_options.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/include/shunt_options.lua @@ -4,9 +4,17 @@ if not data.node_id or not data.node then return end +local s_cfgid = data.s_cfgid local current_node_id = data.node_id local node_list = data.node_list or api.get_node_list() +local groups = {} +m.uci:foreach(appname, "shunt_rules", function(s) + if s.group and s.group ~= "" then + groups[s.group] = true + end +end) + local function get_cfgvalue() return function(self, section) return m:get(current_node_id, self.option) @@ -106,15 +114,27 @@ if api.is_finded("geoview") then .. "" end +shunt_group = add_option(ListValue, "shunt_group", translate("Group Name")) +shunt_group:value("", translate("default")) +for k, v in pairs(groups) do + shunt_group:value(k) +end + +local shunt_group_val = m:get(current_node_id, "shunt_group") or "" +shunt_group_val = shunt_group_val:lower() local shunt_rules = {} m.uci:foreach(appname, "shunt_rules", function(e) - e.id = e[".name"] - e.remarks = e.remarks or e[".name"] - e["_node_option"] = e[".name"] - e["_node_default"] = "" - e["_fakedns_option"] = e[".name"] .. "_fakedns" - e["_proxy_tag_option"] = e[".name"] .. "_proxy_tag" - table.insert(shunt_rules, e) + local group = e.group or "" + group = group:lower() + if group == shunt_group_val then + e.id = e[".name"] + e.remarks = e.remarks or e[".name"] + e["_node_option"] = e[".name"] + e["_node_default"] = "" + e["_fakedns_option"] = e[".name"] .. "_fakedns" + e["_proxy_tag_option"] = e[".name"] .. "_proxy_tag" + table.insert(shunt_rules, e) + end end) table.insert(shunt_rules, { id = ".default", @@ -199,6 +219,8 @@ end local footer = Template(appname .. "/include/shunt_options") footer.api = api +footer.config = m.config footer.id = current_node_id +footer.s_cfgid = s_cfgid or current_node_id footer.normal_list = api.jsonc.stringify(node_list.normal_list) m:append(footer) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua index 6ffdd44a..3a05c465 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe.lua @@ -135,6 +135,9 @@ s.template = "cbi/tblsection" s.extedit = api.url("node_subscribe_config", "%s") function s.create(e, t) local id = TypedSection.create(e, t) + uci:set(appname, id, "hysteria_up_mbps", "100") + uci:set(appname, id, "hysteria_down_mbps", "100") + api.uci_save(uci, appname) luci.http.redirect(e.extedit:format(id)) end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua index 198e19bc..0ea1e64e 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_subscribe_config.lua @@ -223,11 +223,9 @@ if #hysteria2_type > 0 then o = s:option(Value, "hysteria_up_mbps", "Hy/Hy2 " .. translate("Max upload Mbps")) o.datatype = "uinteger" - o.default = "100" o = s:option(Value, "hysteria_down_mbps", "Hy/Hy2 " .. translate("Max download Mbps")) o.datatype = "uinteger" - o.default = "100" end o = s:option(Flag, "boot_update", translate("Update Once on Boot"), translate("Updates the subscription the first time runs automatically after each system boot.")) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua index e0b65f42..7e2d3a7a 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/rule.lua @@ -88,31 +88,6 @@ end s:append(Template(appname .. "/rule/rule_version")) -local cfgname = "shunt_rules" -s = m:section(TypedSection, cfgname, "Sing-Box/Xray " .. translate("Shunt Rule"), "" .. translate("Please note attention to the priority, the higher the order, the higher the priority.") .. "") -s.template = "cbi/tblsection" -s.anonymous = false -s.addremove = true -s.sortable = true -s.extedit = api.url("shunt_rules", "%s") -function s.create(e, t) - TypedSection.create(e, t) - luci.http.redirect(e.extedit:format(t)) -end -function s.remove(e, t) - m.uci:foreach(appname, "nodes", function(s) - if s["protocol"] and s["protocol"] == "_shunt" then - m:del(s[".name"], t) - end - end) - TypedSection.remove(e, t) -end - -o = s:option(DummyValue, "remarks", translate("Remarks")) - -local sortable = Template(appname .. "/cbi/sortable") -sortable.api = api -sortable.target_cfgname = cfgname -m:append(sortable) +s:append(Template(appname .. "/rule/shunt_rule_list")) return api.return_map(m) diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua index f7035984..ebeab5ab 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/shunt_rules.lua @@ -45,6 +45,19 @@ function clean_text(text) :gsub("[ \t]*\n[ \t]*", "\n") end +local remarks_lookup = {} +local groups = {} +m.uci:foreach(appname, "shunt_rules", function(s) + if s[".name"] ~= arg[1] then + if s.remarks then + remarks_lookup[s.remarks] = s[".name"] + end + if s.group and s.group ~= "" then + groups[s.group] = true + end + end +end) + s = m:section(NamedSection, arg[1], "shunt_rules", "") s.addremove = false s.dynamic = false @@ -57,9 +70,34 @@ remarks.validate = function(self, value, section) if value == "" then return nil, translate("Remark cannot be empty.") end + if remarks_lookup[value] then + return nil, translate("This remark already exists, please change a new remark.") + end return value end +o = s:option(Value, "group", translate("Group Name")) +o.default = "" +o:value("", translate("default")) +for k, v in pairs(groups) do + o:value(k) +end +o.write = function(self, section, value) + value = api.trim(value) + local lower = value:lower() + + if lower == "" or lower == "default" then + return m:del(section, self.option) + end + + for _, v in ipairs(self.keylist or {}) do + if v:lower() == lower then + return m:set(section, self.option, v) + end + end + m:set(section, self.option, value) +end + protocol = s:option(MultiValue, "protocol", translate("Protocol")) protocol:value("http") protocol:value("tls") diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua index 2cbeb54d..bc3c4946 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria2.lua @@ -109,10 +109,12 @@ o.rewrite_option = o.option o = s:option(Value, _n("recv_window_conn"), translate("QUIC connection receive window")) o.rewrite_option = o.option -o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)") +o = s:option(Value, _n("idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)") +o.datatype = "range(4,120)" o.rewrite_option = o.option -o = s:option(Value, _n("keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)") +o = s:option(Value, _n("keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)") +o.datatype = "range(2,60)" o.rewrite_option = o.option o = s:option(Flag, _n("disable_mtu_discovery"), translate("Disable MTU detection")) 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 12e44eb6..728ab2fc 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 @@ -394,10 +394,12 @@ o:depends({ [_n("protocol")] = "hysteria2" }) o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o:depends({ [_n("protocol")] = "hysteria2" }) -o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)") +o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)") +o.datatype = "range(4,120)" o:depends({ [_n("protocol")] = "hysteria2"}) -o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)") +o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)") +o.datatype = "range(2,60)" o:depends({ [_n("protocol")] = "hysteria2"}) o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection")) @@ -803,6 +805,9 @@ o:value("", translate("Auto")) o:value("UseIPv4", translate("IPv4 Only")) o:value("UseIPv6", translate("IPv6 Only")) +o = s:option(Flag, _n("happy_eyeballs"), translate("Enable Happy Eyeballs"), translate("Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection.")) +o.default = 0 + local protocols = s.fields[_n("protocol")].keylist if #protocols > 0 then for i, v in ipairs(protocols) do @@ -814,7 +819,11 @@ if #protocols > 0 then s.fields[_n("address")]:depends(depends_condition) s.fields[_n("port")]:depends(depends_condition) s.fields[_n("domain_resolver")]:depends(depends_condition) - s.fields[_n("domain_strategy")]:depends(depends_condition) + s.fields[_n("happy_eyeballs")]:depends(depends_condition) + + local strategy_depends = api.clone(depends_condition) + strategy_depends[_n("happy_eyeballs")] = false + s.fields[_n("domain_strategy")]:depends(strategy_depends) if v ~= "hysteria2" then s.fields[_n("tcp_fast_open")]:depends({ [_n("protocol")] = v }) 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 8dde7edd..138922bc 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 @@ -457,10 +457,12 @@ if singbox_tags:find("with_quic") then o = s:option(Value, _n("hysteria2_down_mbps"), translate("Max download Mbps")) o:depends({ [_n("protocol")] = "hysteria2" }) - o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Example:") .. "30s (4s~120s)") + o = s:option(Value, _n("hysteria2_idle_timeout"), translate("Idle Timeout"), translate("Units:seconds") .. " (4~120)") + o.datatype = "range(4,120)" o:depends({ [_n("protocol")] = "hysteria2"}) - o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Example:") .. "10s (2s~60s)") + o = s:option(Value, _n("hysteria2_keep_alive_period"), translate("QUIC KeepAlive interval"), translate("Units:seconds") .. " (2~60)") + o.datatype = "range(2,60)" o:depends({ [_n("protocol")] = "hysteria2"}) o = s:option(Flag, _n("hysteria2_disable_mtu_discovery"), translate("Disable MTU detection")) @@ -629,6 +631,10 @@ if singbox_tags:find("with_utls") then o:depends({ [_n("reality")] = true }) end +o = s:option(Flag, _n("anytls_disable_reuse"), translate("Disable TLS Reuse")) +o.default = 0 +o:depends({ [_n("protocol")] = "anytls" }) + o = s:option(ListValue, _n("transport"), translate("Transport")) o:value("tcp", "TCP") o:value("http", "HTTP") diff --git a/luci-app-passwall2/luasrc/passwall2/api.lua b/luci-app-passwall2/luasrc/passwall2/api.lua index 18a13dec..3ec184d5 100644 --- a/luci-app-passwall2/luasrc/passwall2/api.lua +++ b/luci-app-passwall2/luasrc/passwall2/api.lua @@ -1170,7 +1170,7 @@ function to_extract(app_name, file, subfix) exec("/bin/rm", {"-f", file}) return { code = 1, - error = i18n.translate("Not installed %s, Can't unzip!" % { tools_name }) + error = i18n.translatef("Not installed %s, Can't unzip!", tools_name) } end end @@ -1383,8 +1383,6 @@ function set_apply_on_parse(map) if old then old(self) end map:set("@global[0]", "timestamp", os.time()) end - local cbi = require "luci.cbi" - map:append(cbi.Template(appname .. "/cbi/optimize_cbi_ui")) end end end diff --git a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua index f25afa17..312787f3 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_sing-box.lua @@ -647,6 +647,8 @@ function gen_outbound(flag, node, tag, proxy_table) idle_session_check_interval = "30s", idle_session_timeout = "30s", min_idle_session = 5, + disable_reuse = (node.anytls_disable_reuse == "1") and true or nil, + client_metadata = api.compare_versions(local_version, ">=", "1.13.16") and "anytls/0.0.13" or nil, tls = tls } end @@ -1599,6 +1601,9 @@ function gen_config(var) --shunt rule uci:foreach(appname, "shunt_rules", function(e) + if node["shunt_group"] ~= e.group then + return + end local outboundTag = gen_shunt_node(e[".name"]) if outboundTag and e.remarks then if outboundTag == "default" then diff --git a/luci-app-passwall2/luasrc/passwall2/util_xray.lua b/luci-app-passwall2/luasrc/passwall2/util_xray.lua index e8ba1589..5a953edd 100644 --- a/luci-app-passwall2/luasrc/passwall2/util_xray.lua +++ b/luci-app-passwall2/luasrc/passwall2/util_xray.lua @@ -170,9 +170,15 @@ function gen_outbound(flag, node, tag, proxy_table) 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 + tcpMptcp = (node.tcpMptcp == "1") and true or nil, + happyEyeballs = (node.happy_eyeballs == "1") and { + TryDelayMs = 250, + PrioritizeIPv6 = false, + Interleave = 1, + MaxConcurrentTry = 4 + } or nil }, - network = node.transport, + [(api.compare_versions(xray_version, "<", "26.7.11")) and "network" or "method"] = node.transport, -- Todo: Remove version check and "network" security = node.stream_security, tlsSettings = (node.stream_security == "tls") and { serverName = node.tls_serverName, @@ -659,7 +665,7 @@ function gen_config_server(node) protocol = node.protocol, settings = settings, streamSettings = { - network = node.transport, + [(api.compare_versions(xray_version, "<", "26.7.11")) and "network" or "method"] = node.transport, -- Todo: Remove version check and "network" security = "none", tlsSettings = ("1" == node.tls) and { disableSystemRoot = false, @@ -1073,6 +1079,10 @@ function gen_config(var) if outboundTag then valid_nodes[#valid_nodes + 1] = outboundTag end + -- Check if balancing node duplicates fallback node + if _node.fallback_node == blc_node_id then + _node.fallback_node = nil + end end end if #valid_nodes == 0 then return nil end @@ -1392,6 +1402,9 @@ function gen_config(var) --shunt rule uci:foreach(appname, "shunt_rules", function(e) + if node["shunt_group"] ~= e.group then + return + end local outboundTag = gen_shunt_node(e[".name"]) if outboundTag and e.remarks then if outboundTag == "default" then diff --git a/luci-app-passwall2/luasrc/view/passwall2/cbi/footer.htm b/luci-app-passwall2/luasrc/view/passwall2/cbi/footer.htm index 46a25d3e..54777e11 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/cbi/footer.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/cbi/footer.htm @@ -1 +1,18 @@ + \ No newline at end of file diff --git a/luci-app-passwall2/luasrc/view/passwall2/cbi/header.htm b/luci-app-passwall2/luasrc/view/passwall2/cbi/header.htm index 5d284d90..a2a716bb 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/cbi/header.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/cbi/header.htm @@ -1,2 +1,24 @@ + + + diff --git a/luci-app-passwall2/luasrc/view/passwall2/cbi/optimize_cbi_ui.htm b/luci-app-passwall2/luasrc/view/passwall2/cbi/optimize_cbi_ui.htm deleted file mode 100644 index 3fccf13c..00000000 --- a/luci-app-passwall2/luasrc/view/passwall2/cbi/optimize_cbi_ui.htm +++ /dev/null @@ -1,22 +0,0 @@ - diff --git a/luci-app-passwall2/luasrc/view/passwall2/global/footer.htm b/luci-app-passwall2/luasrc/view/passwall2/global/footer.htm index e1ba5c58..661c1bd9 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/global/footer.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/global/footer.htm @@ -207,7 +207,7 @@ local appname = api.appname let new_val = el.target.value const new_hasItem = shunt_list.find(element => element.id == new_val); if (new_hasItem) { - XHR.get('<%=api.url("update_node")%>', { + XHR.get('<%=api.url("update_config")%>', { id: "<%=self.global_cfgid%>", data: JSON.stringify({ node: new_val diff --git a/luci-app-passwall2/luasrc/view/passwall2/include/shunt_options.htm b/luci-app-passwall2/luasrc/view/passwall2/include/shunt_options.htm index 9dcc05c7..83b3c4f3 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/include/shunt_options.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/include/shunt_options.htm @@ -53,6 +53,22 @@ } } } + document.addEventListener("DOMContentLoaded", function () { + waitForElement('select[name*="<%=self.config%>"][name*="shunt_group"]', function(el) { + let o_val = el.value; + el.addEventListener("change", () => { + el.blur(); + if (o_val != el.value) { + let save = true; + if (save) { + update_config("<%=self.id%>", { + shunt_group: getOption("<%=self.config%>", "<%=self.s_cfgid%>", "shunt_group").value, + }, get_current_url()); + } + } + }); + }); + }); document.addEventListener("DOMContentLoaded", () => setTimeout(() => { refresh_depends(); const table_dom = document.getElementById("cbi-passwall2-shunt_option_list"); diff --git a/luci-app-passwall2/luasrc/view/passwall2/node_config/footer.htm b/luci-app-passwall2/luasrc/view/passwall2/node_config/footer.htm index feefc4dd..b2cfd367 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/node_config/footer.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/node_config/footer.htm @@ -1,8 +1,8 @@ -<% -local api = self.api --%> + + + +<% if api.is_js_luci() then -%> + +<%- end %> + + + + + + + + +
+ Sing-Box/Xray <%:Shunt Rule%> +
<%:Please note attention to the priority, the higher the order, the higher the priority.%>
+
+
+ + diff --git a/luci-app-passwall2/po/fa/passwall2.po b/luci-app-passwall2/po/fa/passwall2.po index cd8e2625..7e8f93a7 100644 --- a/luci-app-passwall2/po/fa/passwall2.po +++ b/luci-app-passwall2/po/fa/passwall2.po @@ -1750,6 +1750,12 @@ msgstr "حل مشکل DNS دامنه" msgid "If the node address is a domain name, this DNS will be used for resolution." msgstr "اگر آدرس گره یک نام دامنه باشد، از این DNS برای حل مسئله استفاده خواهد شد." +msgid "Enable Happy Eyeballs" +msgstr "فعال کردن چشم‌های خوشحال" + +msgid "Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection." +msgstr "IPv4 و IPv6 را همزمان امتحان می‌کند؛ به‌طور خودکار از اتصال سریع‌تر استفاده می‌کند." + msgid "Supports only Xray or Sing-box node types." msgstr "فقط گره‌های نوع Xray یا Sing-box پشتیبانی می‌شوند." @@ -2413,3 +2419,9 @@ msgstr "اندازه بسته Gecko (حداکثر)" msgid "valid time (hh:mm)" msgstr "زمان معتبر (ساعت:میلی‌متر)" + +msgid "Disable TLS Reuse" +msgstr "غیرفعال کردن استفاده مجدد از TLS" + +msgid "Not Set" +msgstr "تنظیم نشده" diff --git a/luci-app-passwall2/po/ru/passwall2.po b/luci-app-passwall2/po/ru/passwall2.po index 04797312..d9be19cb 100644 --- a/luci-app-passwall2/po/ru/passwall2.po +++ b/luci-app-passwall2/po/ru/passwall2.po @@ -1751,6 +1751,12 @@ msgstr "Разрешение DNS-запросов для домена" msgid "If the node address is a domain name, this DNS will be used for resolution." msgstr "Если адрес узла является доменным именем, для разрешения будет использоваться этот DNS-сервер." +msgid "Enable Happy Eyeballs" +msgstr "Включить счастливые глаза" + +msgid "Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection." +msgstr "Одновременно пытается использовать IPv4 и IPv6; автоматически выбирает более быстрое соединение." + msgid "Supports only Xray or Sing-box node types." msgstr "Поддерживаются только узлы типа Xray или Sing-box." @@ -2411,3 +2417,9 @@ msgstr "Размер упаковки Gecko (макс)" msgid "valid time (hh:mm)" msgstr "Время действия (чч:мм)" + +msgid "Disable TLS Reuse" +msgstr "Отключить повторное использование TLS" + +msgid "Not Set" +msgstr "Не задано" diff --git a/luci-app-passwall2/po/zh-cn/passwall2.po b/luci-app-passwall2/po/zh-cn/passwall2.po index baad1ec2..0eaa07f2 100644 --- a/luci-app-passwall2/po/zh-cn/passwall2.po +++ b/luci-app-passwall2/po/zh-cn/passwall2.po @@ -1736,6 +1736,12 @@ msgstr "域名 DNS 解析" msgid "If the node address is a domain name, this DNS will be used for resolution." msgstr "如果节点地址是域名,则将使用此 DNS 进行解析。" +msgid "Enable Happy Eyeballs" +msgstr "启用 Happy Eyeballs" + +msgid "Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection." +msgstr "同时尝试 IPv4 和 IPv6 连接,自动使用更快的连接。" + msgid "Supports only Xray or Sing-box node types." msgstr "仅支持 Xray 或 Sing-box 类型节点。" @@ -2399,3 +2405,9 @@ msgstr "Gecko 包大小(最大)" msgid "valid time (hh:mm)" msgstr "有效时间(hh:mm)" + +msgid "Disable TLS Reuse" +msgstr "禁用 TLS 连接复用" + +msgid "Not Set" +msgstr "未设置" diff --git a/luci-app-passwall2/po/zh-tw/passwall2.po b/luci-app-passwall2/po/zh-tw/passwall2.po index c7b79250..0a32d41e 100644 --- a/luci-app-passwall2/po/zh-tw/passwall2.po +++ b/luci-app-passwall2/po/zh-tw/passwall2.po @@ -1742,6 +1742,12 @@ msgstr "域名 DNS 解析" msgid "If the node address is a domain name, this DNS will be used for resolution." msgstr "如果節點地址是域名,則將使用此 DNS 進行解析。" +msgid "Enable Happy Eyeballs" +msgstr "啓用 Happy Eyeballs" + +msgid "Attempts IPv4 and IPv6 simultaneously; automatically uses the faster connection." +msgstr "同時嘗試 IPv4 和 IPv6 鏈接,自動使用更快的鏈接。" + msgid "Supports only Xray or Sing-box node types." msgstr "僅支援 Xray 或 Sing-box 類型節點。" @@ -2405,3 +2411,9 @@ msgstr "Gecko 包大小(最大)" msgid "valid time (hh:mm)" msgstr "有效時間(hh:mm)" + +msgid "Disable TLS Reuse" +msgstr "禁用 TLS 連接復用" + +msgid "Not Set" +msgstr "未設置" diff --git a/luci-app-passwall2/root/usr/share/passwall2/0_default_config b/luci-app-passwall2/root/usr/share/passwall2/0_default_config index 16aa35f0..d6ce2fe5 100644 --- a/luci-app-passwall2/root/usr/share/passwall2/0_default_config +++ b/luci-app-passwall2/root/usr/share/passwall2/0_default_config @@ -59,51 +59,6 @@ config global_subscribe config global_singbox -config shunt_rules 'China' - option remarks 'China' - option network 'tcp,udp' - option domain_list 'geosite:cn' - option ip_list 'geoip:cn' - -config shunt_rules 'Iran' - option remarks 'Iran' - option network 'tcp,udp' - option domain_list 'geosite:ir -#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-ir.srs' - option ip_list 'geoip:ir -#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ir.srs' - -config shunt_rules 'Russia_Block' - option remarks 'Russia_Block' - option network 'tcp,udp' - option domain_list 'geosite:ru-blocked -#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geosite/geosite-ru-blocked.srs' - option ip_list 'geoip:ru-blocked -#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru-blocked.srs' - -config shunt_rules 'Russia' - option remarks 'Russia' - option network 'tcp,udp' - option ip_list 'geoip:ru -#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru.srs' - -config shunt_rules 'PrivateIP' - option remarks 'PrivateIP' - option network 'tcp,udp' - option ip_list '10.0.0.0/8 -127.0.0.0/8 -198.51.100.0/24 -192.0.0.0/24 -169.254.0.0/16 -224.0.0.0/3 -192.168.0.0/16 -192.88.99.0/24 -100.64.0.0/10 -172.16.0.0/12 -0.0.0.0/8 -192.0.2.0/24 -203.0.113.0/24' - config nodes 'examplenode' option remarks 'Example' option type 'Xray' @@ -126,3 +81,151 @@ config nodes 'rulenode' option domainMatcher 'hybrid' option write_ipset_direct '1' option enable_geoview_ip '1' + option shunt_group 'CN' + +config shunt_rules 'DirectFront' + option remarks 'DirectFront' + option network 'tcp,udp' + option domain_list 'domain:ip233.cn' + option group 'CN' + +config shunt_rules 'ProxyFront' + option remarks 'ProxyFront' + option network 'tcp,udp' + option domain_list 'domain:www.gstatic.com +domain:ssl.gstatic.com +domain:fonts.gstatic.com' + option group 'CN' + +config shunt_rules 'DirectGame' + option remarks 'DirectGame' + option domain_list '# steam直连域名获取国内CDN走国内线路下载 +cm.steampowered.com +steamserver.net + +# steam国内CDN华为云 +steampipe.steamcontent.tnkjmec.com +# steam国内CDN白山云 +st.dl.eccdnx.com +st.dl.bscstorage.net +st.dl.pinyuncloud.com +# steam国内CDN新流云(原金山云)(支持ipv6) +dl.steam.clngaa.com +# steam国内CDN网宿 +cdn.mileweb.cs.steampowered.com.8686c.com +cdn-ws.content.steamchina.com +# steam国内CDN腾讯云 (蒸汽中国独占) +cdn-qc.content.steamchina.com +# steam国内CDN阿里云(支持ipv6) +cdn-ali.content.steamchina.com +xz.pphimalayanrt.com +lv.queniujq.cn +alibaba.cdn.steampipe.steamcontent.com + +# 国内游戏geosite域名 +geosite:category-games@cn' + option ip_list '# steam直连IP +45.121.184.0/24 +103.10.124.0/23 +103.28.54.0/24 +146.66.152.0/24 +146.66.155.0/24 +153.254.86.0/24 +155.133.224.0/22 +155.133.230.0/24 +155.133.232.0/23 +155.133.234.0/24 +155.133.236.0/22 +155.133.240.0/23 +155.133.244.0/23 +155.133.246.0/24 +155.133.248.0/21 +162.254.192.0/21 +185.25.182.0/23 +190.217.32.0/22 +192.69.96.0/22 +205.196.6.0/24 +208.64.200.0/22 +208.78.164.0/22 +205.185.194.0/24' + option network 'tcp,udp' + option group 'CN' + +config shunt_rules 'ProxyGame' + option remarks 'ProxyGame' + option domain_list '# steam 商店/客服/聊天/网页布局/API/二维码/Google云同步 代理URL +steamcommunity.com +www.steamcommunity.com +store.steampowered.com +checkout.steampowered.com +api.steampowered.com +help.steampowered.com +login.steampowered.com +store.akamai.steamstatic.com +steambroadcast.akamaized.net +steamvideo-a.akamaihd.net +steamusercontent-a.akamaihd.net +steamstore-a.akamaihd.net +steamcommunity-a.akamaihd.net +steamcdn-a.akamaihd.net +steamuserimages-a.akamaihd.net +community.akamai.steamstatic.com +avatars.akamai.steamstatic.com +community.steamstatic.com +cdn.akamai.steamstatic.com +avatars.steamstatic.com +shared.akamai.steamstatic.com +clan.akamai.steamstatic.com +cdn.cloudflare.steamstatic.com +community.cloudflare.steamstatic.com +store.cloudflare.steamstatic.com +avatars.cloudflare.steamstatic.com +clan.cloudflare.steamstatic.com +shared.cloudflare.steamstatic.com +steam-chat.com +steamcloud-ugc.storage.googleapis.com +steamcloud-eu-ams.storage.googleapis.com +steamcloud-eu-fra.storage.googleapis.com +steamcloud-finland.storage.googleapis.com +steamcloud-saopaulo.storage.googleapis.com +steamcloud-singapore.storage.googleapis.com +steamcloud-sydney.storage.googleapis.com +steamcloud-taiwan.storage.googleapis.com +steamcloud-eu.storage.googleapis.com +domain:blizzard.com +geosite:category-games' + option network 'tcp,udp' + option group 'CN' + +config shunt_rules 'CN' + option remarks 'CN' + option domain_list 'geosite:cn' + option ip_list 'geoip:cn' + option network 'tcp,udp' + option group 'CN' + +config shunt_rules 'Iran' + option remarks 'Iran' + option network 'tcp,udp' + option domain_list 'geosite:ir +#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geosite-ir.srs' + option ip_list 'geoip:ir +#rule-set:remote:https://raw.githubusercontent.com/Chocolate4U/Iran-sing-box-rules/rule-set/geoip-ir.srs' + option group 'IR' + +config shunt_rules 'Russia_Block' + option remarks 'Russia_Block' + option network 'tcp,udp' + option domain_list 'geosite:ru-blocked +#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geosite/geosite-ru-blocked.srs' + option ip_list 'geoip:ru-blocked +#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru-blocked.srs' + option group 'RU' + +config shunt_rules 'Russia' + option remarks 'Russia' + option network 'tcp,udp' + option ip_list 'geoip:ru +#rule-set:remote:https://raw.githubusercontent.com/runetfreedom/russia-v2ray-rules-dat/refs/heads/release/sing-box/rule-set-geoip/geoip-ru.srs' + option group 'RU' + diff --git a/luci-app-passwall2/root/usr/share/passwall2/iptables.sh b/luci-app-passwall2/root/usr/share/passwall2/iptables.sh index 52be6721..17255734 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/iptables.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/iptables.sh @@ -189,7 +189,9 @@ gen_shunt_list() { local default_outbound="redirect" [ "$default_node" = "_direct" ] && default_outbound="direct" local shunt_ids=$(uci show $CONFIG | grep "=shunt_rules" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}') + local shunt_group=$(config_n_get $node shunt_group) for shunt_id in $shunt_ids; do + [ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue local shunt_node=$(config_n_get ${node} "${shunt_id}") [ -n "$shunt_node" ] && { local ipset_v4="psw2_${node}_${shunt_id}" @@ -702,15 +704,13 @@ add_firewall_rule() { fi $ipt_n -N PSW2 - $ipt_n -A PSW2 $(dst $IPSET_DIRECT) -j RETURN $ipt_n -A PSW2 $(dst $IPSET_VPS) -j RETURN $ipt_n -A PSW2 $(comment "WAN_IP_RETURN") $(dst $IPSET_WAN) -j RETURN - [ "$accept_icmp" = "1" ] && insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p icmp -j PSW2" - [ -z "${is_tproxy}" ] && insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW2" + [ "$accept_icmp" = "1" ] && insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "$(dst $IPSET_DIRECT !) -p icmp -j PSW2" + [ -z "${is_tproxy}" ] && insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "$(dst $IPSET_DIRECT !) -p tcp -j PSW2" $ipt_n -N PSW2_OUTPUT - $ipt_n -A PSW2_OUTPUT $(dst $IPSET_DIRECT) -j RETURN $ipt_n -A PSW2_OUTPUT $(dst $IPSET_VPS) -j RETURN $ipt_n -A PSW2_OUTPUT -m mark --mark 255 -j RETURN @@ -722,10 +722,6 @@ add_firewall_rule() { $ipt_n -I PREROUTING -m set --match-set $IPSET_DIRECT src -j PSW2_DNS fi - $ipt_m -N PSW2_DIVERT - $ipt_m -A PSW2_DIVERT -j MARK --set-mark ${FWMARK} - $ipt_m -A PSW2_DIVERT -j ACCEPT - $ipt_m -N PSW2_RULE $ipt_m -A PSW2_RULE -j CONNMARK --restore-mark $ipt_m -A PSW2_RULE -m mark --mark ${FWMARK} -j RETURN @@ -734,17 +730,16 @@ add_firewall_rule() { $ipt_m -A PSW2_RULE -j CONNMARK --save-mark $ipt_m -N PSW2 - $ipt_m -A PSW2 $(dst $IPSET_DIRECT) -j RETURN + # Socket Only TCP, UDP Invalid. + $ipt_m -A PSW2 -p tcp -m socket -j MARK --set-mark ${FWMARK} + $ipt_m -A PSW2 -p tcp -m socket -j ACCEPT $ipt_m -A PSW2 $(dst $IPSET_VPS) -j RETURN $ipt_m -A PSW2 $(comment "WAN_IP_RETURN") $(dst $IPSET_WAN) -j RETURN $ipt_m -A PSW2 -m conntrack --ctdir REPLY -j RETURN - insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW2" - # Only TCP, UDP Invalid. - insert_rule_before "$ipt_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT" + insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "$(dst $IPSET_DIRECT !) -j PSW2" $ipt_m -N PSW2_OUTPUT - $ipt_m -A PSW2_OUTPUT $(dst $IPSET_DIRECT) -j RETURN $ipt_m -A PSW2_OUTPUT $(dst $IPSET_VPS) -j RETURN $ipt_m -A PSW2_OUTPUT -m conntrack --ctdir REPLY -j RETURN $ipt_m -A PSW2_OUTPUT -m mark --mark 255 -j RETURN @@ -754,12 +749,10 @@ add_firewall_rule() { [ "$accept_icmpv6" = "1" ] && { $ip6t_n -N PSW2 - $ip6t_n -A PSW2 $(dst $IPSET_DIRECT6) -j RETURN $ip6t_n -A PSW2 $(dst $IPSET_VPS6) -j RETURN - $ip6t_n -A PREROUTING -p ipv6-icmp -j PSW2 + $ip6t_n -A PREROUTING $(dst $IPSET_DIRECT6 !) -p ipv6-icmp -j PSW2 $ip6t_n -N PSW2_OUTPUT - $ip6t_n -A PSW2_OUTPUT $(dst $IPSET_DIRECT6) -j RETURN $ip6t_n -A PSW2_OUTPUT $(dst $IPSET_VPS6) -j RETURN $ip6t_n -A PSW2_OUTPUT -m mark --mark 255 -j RETURN } @@ -772,10 +765,6 @@ add_firewall_rule() { $ip6t_n -I PREROUTING -m set --match-set $IPSET_DIRECT6 src -j PSW2_DNS fi - $ip6t_m -N PSW2_DIVERT - $ip6t_m -A PSW2_DIVERT -j MARK --set-mark ${FWMARK} - $ip6t_m -A PSW2_DIVERT -j ACCEPT - $ip6t_m -N PSW2_RULE $ip6t_m -A PSW2_RULE -j CONNMARK --restore-mark $ip6t_m -A PSW2_RULE -m mark --mark ${FWMARK} -j RETURN @@ -784,18 +773,17 @@ add_firewall_rule() { $ip6t_m -A PSW2_RULE -j CONNMARK --save-mark $ip6t_m -N PSW2 - $ip6t_m -A PSW2 $(dst $IPSET_DIRECT6) -j RETURN + # Socket Only TCP, UDP Invalid. + $ip6t_m -A PSW2 -p tcp -m socket -j MARK --set-mark ${FWMARK} + $ip6t_m -A PSW2 -p tcp -m socket -j ACCEPT $ip6t_m -A PSW2 $(dst $IPSET_VPS6) -j RETURN $ip6t_m -A PSW2 $(comment "WAN6_IP_RETURN") $(dst $IPSET_WAN6) -j RETURN $ip6t_m -A PSW2 -m conntrack --ctdir REPLY -j RETURN - insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW2" - # Only TCP, UDP Invalid. - insert_rule_before "$ip6t_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT" + insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "$(dst $IPSET_DIRECT6 !) -j PSW2" $ip6t_m -N PSW2_OUTPUT $ip6t_m -A PSW2_OUTPUT -m mark --mark 255 -j RETURN - $ip6t_m -A PSW2_OUTPUT $(dst $IPSET_DIRECT6) -j RETURN $ip6t_m -A PSW2_OUTPUT $(dst $IPSET_VPS6) -j RETURN $ip6t_m -A PSW2_OUTPUT -m conntrack --ctdir REPLY -j RETURN @@ -860,14 +848,14 @@ add_firewall_rule() { # Loading local router proxy TCP if [ -n "$NODE" ] && [ "$TCP_LOCALHOST_PROXY" = "1" ]; then [ "$accept_icmp" = "1" ] && { - $ipt_n -A OUTPUT -p icmp -j PSW2_OUTPUT + $ipt_n -A OUTPUT $(dst $IPSET_DIRECT !) -p icmp -j PSW2_OUTPUT $ipt_n -A PSW2_OUTPUT -p icmp -d $FAKE_IP $(REDIRECT) add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_n -A PSW2_OUTPUT -p icmp" "$(REDIRECT)" $ipt_n -A PSW2_OUTPUT -p icmp $(REDIRECT) } [ "$accept_icmpv6" = "1" ] && { - $ip6t_n -A OUTPUT -p ipv6-icmp -j PSW2_OUTPUT + $ip6t_n -A OUTPUT $(dst $IPSET_DIRECT6 !) -p ipv6-icmp -j PSW2_OUTPUT $ip6t_n -A PSW2_OUTPUT -p ipv6-icmp -d $FAKE_IP_6 $(REDIRECT) add_shunt_t_rule "${SHUNT_LIST6}" "$ip6t_n -A PSW2_OUTPUT -p ipv6-icmp" "$(REDIRECT)" $ip6t_n -A PSW2_OUTPUT -p ipv6-icmp $(REDIRECT) @@ -882,11 +870,11 @@ add_firewall_rule() { $ipt_tmp -A PSW2_OUTPUT -p tcp -d $FAKE_IP ${ipt_j} add_shunt_t_rule "${SHUNT_LIST4}" "$ipt_tmp -A PSW2_OUTPUT -p tcp" "${ipt_j}" $TCP_REDIR_PORTS add_port_rules "$ipt_tmp -A PSW2_OUTPUT -p tcp" $TCP_REDIR_PORTS "${ipt_j}" - [ -z "${is_tproxy}" ] && $ipt_n -A OUTPUT -p tcp -j PSW2_OUTPUT + [ -z "${is_tproxy}" ] && $ipt_n -A OUTPUT $(dst $IPSET_DIRECT !) -p tcp -j PSW2_OUTPUT [ -n "${is_tproxy}" ] && { $ipt_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo $(REDIRECT $REDIR_PORT TPROXY) $ipt_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo -j RETURN - insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p tcp -j PSW2_OUTPUT" + insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) $(dst $IPSET_DIRECT !) -p tcp -j PSW2_OUTPUT" } [ "$PROXY_IPV6" == "1" ] && { @@ -895,7 +883,7 @@ add_firewall_rule() { add_port_rules "$ip6t_m -A PSW2_OUTPUT -p tcp" $TCP_REDIR_PORTS "-j PSW2_RULE" $ip6t_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo $(REDIRECT $REDIR_PORT TPROXY) $ip6t_m -A PSW2 $(comment "${comment_l}") -p tcp -i lo -j RETURN - insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p tcp -j PSW2_OUTPUT" + insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) $(dst $IPSET_DIRECT6 !) -p tcp -j PSW2_OUTPUT" } [ -d "${TMP_IFACE_PATH}" ] && { @@ -913,7 +901,7 @@ add_firewall_rule() { add_port_rules "$ipt_m -A PSW2_OUTPUT -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE" $ipt_m -A PSW2 $(comment "${comment_l}") -p udp -i lo $(REDIRECT $REDIR_PORT TPROXY) $ipt_m -A PSW2 $(comment "${comment_l}") -p udp -i lo -j RETURN - insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p udp -j PSW2_OUTPUT" + insert_rule_before "$ipt_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) $(dst $IPSET_DIRECT !) -p udp -j PSW2_OUTPUT" [ "$PROXY_IPV6" == "1" ] && { $ip6t_m -A PSW2_OUTPUT -p udp -d $FAKE_IP_6 -j PSW2_RULE @@ -921,7 +909,7 @@ add_firewall_rule() { add_port_rules "$ip6t_m -A PSW2_OUTPUT -p udp" $UDP_REDIR_PORTS "-j PSW2_RULE" $ip6t_m -A PSW2 $(comment "${comment_l}") -p udp -i lo $(REDIRECT $REDIR_PORT TPROXY) $ip6t_m -A PSW2 $(comment "${comment_l}") -p udp -i lo -j RETURN - insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -p udp -j PSW2_OUTPUT" + insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) $(dst $IPSET_DIRECT6 !) -p udp -j PSW2_OUTPUT" } [ -d "${TMP_IFACE_PATH}" ] && { @@ -938,8 +926,10 @@ add_firewall_rule() { $ip6t_m -I OUTPUT $(comment "mangle-OUTPUT-PSW2") -o lo -j RETURN insert_rule_before "$ip6t_m" "OUTPUT" "mwan3" "$(comment mangle-OUTPUT-PSW2) -m mark --mark ${FWMARK} -j RETURN" - $ipt_m -A PSW2 -p udp --dport 53 -j RETURN - $ip6t_m -A PSW2 -p udp --dport 53 -j RETURN + $ipt_m -A PSW2 -p tcp --dport 53 -j ACCEPT + $ipt_m -A PSW2 -p udp --dport 53 -j ACCEPT + $ip6t_m -A PSW2 -p tcp --dport 53 -j ACCEPT + $ip6t_m -A PSW2 -p udp --dport 53 -j ACCEPT } load_acl @@ -970,7 +960,7 @@ del_firewall_rule() { $ipt -D $chain $index 2>/dev/null done done - for chain in "PSW2" "PSW2_OUTPUT" "PSW2_DIVERT" "PSW2_DNS" "PSW2_RULE"; do + for chain in "PSW2" "PSW2_OUTPUT" "PSW2_DNS" "PSW2_RULE"; do $ipt -F $chain 2>/dev/null $ipt -X $chain 2>/dev/null done @@ -1004,7 +994,7 @@ gen_include() { [ -z "${_ipt}" ] && return echo "*$2" - ${_ipt}-save -t $2 | grep "PSW2" | grep -v "\-j PSW2$" | grep -v "socket \-j PSW2_DIVERT$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/" + ${_ipt}-save -t $2 | grep "PSW2" | grep -v "\-j PSW2$" | sed -e "s/^-A \(OUTPUT\|PREROUTING\)/-I \1 1/" echo 'COMMIT' } local __ipt="" @@ -1017,11 +1007,10 @@ gen_include() { $(extract_rules 4 mangle) EOT - [ "$accept_icmp" = "1" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p icmp -j PSW2") - [ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "-p tcp -j PSW2") + [ "$accept_icmp" = "1" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "$(dst $IPSET_DIRECT !) -p icmp -j PSW2") + [ -z "${is_tproxy}" ] && \$(${MY_PATH} insert_rule_after "$ipt_n" "PREROUTING" "prerouting_rule" "$(dst $IPSET_DIRECT !) -p tcp -j PSW2") - \$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "-j PSW2") - \$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT") + \$(${MY_PATH} insert_rule_before "$ipt_m" "PREROUTING" "mwan3" "$(dst $IPSET_DIRECT !) -j PSW2") EOF ) } @@ -1035,10 +1024,9 @@ gen_include() { $(extract_rules 6 mangle) EOT - [ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING -p ipv6-icmp -j PSW2 + [ "$accept_icmpv6" = "1" ] && $ip6t_n -A PREROUTING $(dst $IPSET_DIRECT6 !) -p ipv6-icmp -j PSW2 - \$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "-j PSW2") - \$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "PSW2" "-p tcp -m socket -j PSW2_DIVERT") + \$(${MY_PATH} insert_rule_before "$ip6t_m" "PREROUTING" "mwan3" "$(dst $IPSET_DIRECT6 !) -j PSW2") EOF ) } diff --git a/luci-app-passwall2/root/usr/share/passwall2/nftables.sh b/luci-app-passwall2/root/usr/share/passwall2/nftables.sh index 7993209a..3fc0e355 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/nftables.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/nftables.sh @@ -231,7 +231,9 @@ gen_shunt_list() { local default_outbound="redirect" [ "$default_node" = "_direct" ] && default_outbound="direct" local shunt_ids=$(uci show $CONFIG | grep "=shunt_rules" | awk -F '.' '{print $2}' | awk -F '=' '{print $1}') + local shunt_group=$(config_n_get $node shunt_group) for shunt_id in $shunt_ids; do + [ "${shunt_group}" != "$(config_n_get ${shunt_id} group)" ] && continue local shunt_node=$(config_n_get ${node} "${shunt_id}") [ -n "$shunt_node" ] && { local nftset_v4="psw2_${node}_${shunt_id}" @@ -700,13 +702,13 @@ add_firewall_rule() { for ip in $(cat /usr/share/passwall2/direct_ip | tr -s "\r\n" "\n" | grep -v "^#" | sed -e "/^$/d"); do if [[ "$ip" == *::* ]]; then - echo "$ip" | insert_nftset $NFTSET_DIRECT6 0 + echo "$ip" | insert_nftset $NFTSET_DIRECT6 "-1" elif [[ "$ip" == "geoip:"* ]]; then local _geoip_code=$(echo $ip | awk -F ':' '{print $2}') - get_geoip $_geoip_code ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_DIRECT "0" - get_geoip $_geoip_code ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_DIRECT6 "0" + get_geoip $_geoip_code ipv4 | grep -E "(\.((2(5[0-5]|[0-4][0-9]))|[0-1]?[0-9]{1,2})){3}" | insert_nftset $NFTSET_DIRECT "-1" + get_geoip $_geoip_code ipv6 | grep -E "([A-Fa-f0-9]{1,4}::?){1,7}[A-Fa-f0-9]{1,4}" | insert_nftset $NFTSET_DIRECT6 "-1" else - echo "$ip" | insert_nftset $NFTSET_DIRECT 0 + echo "$ip" | insert_nftset $NFTSET_DIRECT "-1" fi done @@ -726,14 +728,14 @@ add_firewall_rule() { update_wan_sets [ -n "$ISP_DNS" ] && { - echo "$ISP_DNS" | insert_nftset $NFTSET_DIRECT 0 + echo "$ISP_DNS" | insert_nftset $NFTSET_DIRECT "-1" for ispip in $ISP_DNS; do log_i18n 1 "$(i18n "Add ISP %s DNS to the whitelist: %s" "IPv4" "${ispip}")" done } [ -n "$ISP_DNS6" ] && { - echo $ISP_DNS6 | insert_nftset $NFTSET_DIRECT6 0 + echo $ISP_DNS6 | insert_nftset $NFTSET_DIRECT6 "-1" for ispip6 in $ISP_DNS6; do log_i18n 1 "$(i18n "Add ISP %s DNS to the whitelist: %s" "IPv6" "${ispip6}")" done @@ -762,11 +764,6 @@ add_firewall_rule() { nft_output_chain="PSW2_OUTPUT_MANGLE" fi - nft "add chain $NFTABLE_NAME PSW2_DIVERT" - nft "flush chain $NFTABLE_NAME PSW2_DIVERT" - # Only TCP, UDP Invalid. - nft "add rule $NFTABLE_NAME PSW2_DIVERT meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept" - nft "add chain $NFTABLE_NAME PSW2_DNS" nft "flush chain $NFTABLE_NAME PSW2_DNS" if [ $(config_t_get global dns_redirect "1") = "0" ]; then @@ -790,33 +787,29 @@ add_firewall_rule() { #ipv4 tproxy mode and udp nft "add chain $NFTABLE_NAME PSW2_MANGLE" nft "flush chain $NFTABLE_NAME PSW2_MANGLE" - nft "add rule $NFTABLE_NAME PSW2_MANGLE ip daddr @$NFTSET_DIRECT counter return" nft "add rule $NFTABLE_NAME PSW2_MANGLE ip daddr @$NFTSET_VPS counter return" nft "add rule $NFTABLE_NAME PSW2_MANGLE ct direction reply counter return" - nft "add chain $NFTABLE_NAME PSW2_OUTPUT_MANGLE" nft "flush chain $NFTABLE_NAME PSW2_OUTPUT_MANGLE" - nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip daddr @$NFTSET_DIRECT counter return" nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip daddr @$NFTSET_VPS counter return" nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ct direction reply counter return" nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE meta mark 255 counter return" # jump chains - nft "add rule $NFTABLE_NAME mangle_prerouting counter jump PSW2_DIVERT" - nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol udp counter jump PSW2_MANGLE" - [ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME mangle_prerouting ip protocol tcp counter jump PSW2_MANGLE" + # Only TCP, UDP Invalid. + nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto ipv4 meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept" + nft "add rule $NFTABLE_NAME mangle_prerouting ip daddr != @$NFTSET_DIRECT ip protocol udp counter jump PSW2_MANGLE" + [ -n "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME mangle_prerouting ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_MANGLE" #ipv4 tcp redirect mode [ -z "${is_tproxy}" ] && { nft "add chain $NFTABLE_NAME PSW2_NAT" nft "flush chain $NFTABLE_NAME PSW2_NAT" - nft "add rule $NFTABLE_NAME PSW2_NAT ip daddr @$NFTSET_DIRECT counter return" nft "add rule $NFTABLE_NAME PSW2_NAT ip daddr @$NFTSET_VPS counter return" - nft "add rule $NFTABLE_NAME dstnat ip protocol tcp counter jump PSW2_NAT" + nft "add rule $NFTABLE_NAME dstnat ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_NAT" nft "add chain $NFTABLE_NAME PSW2_OUTPUT_NAT" nft "flush chain $NFTABLE_NAME PSW2_OUTPUT_NAT" - nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT ip daddr @$NFTSET_DIRECT counter return" nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT ip daddr @$NFTSET_VPS counter return" nft "add rule $NFTABLE_NAME PSW2_OUTPUT_NAT meta mark 255 counter return" } @@ -825,16 +818,16 @@ add_firewall_rule() { if [ "$accept_icmp" = "1" ]; then nft "add chain $NFTABLE_NAME PSW2_ICMP_REDIRECT" nft "flush chain $NFTABLE_NAME PSW2_ICMP_REDIRECT" - nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip daddr @$NFTSET_DIRECT counter return" nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip daddr @$NFTSET_VPS counter return" [ "$accept_icmpv6" = "1" ] && { - nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip6 daddr @$NFTSET_DIRECT6 counter return" nft "add rule $NFTABLE_NAME PSW2_ICMP_REDIRECT ip6 daddr @$NFTSET_VPS6 counter return" } - nft "add rule $NFTABLE_NAME dstnat meta l4proto {icmp,icmpv6} counter jump PSW2_ICMP_REDIRECT" - nft "add rule $NFTABLE_NAME nat_output meta l4proto {icmp,icmpv6} counter jump PSW2_ICMP_REDIRECT" + nft "add rule $NFTABLE_NAME dstnat ip daddr != @$NFTSET_DIRECT meta l4proto icmp counter jump PSW2_ICMP_REDIRECT" + nft "add rule $NFTABLE_NAME dstnat ip6 daddr != @$NFTSET_DIRECT6 meta l4proto icmpv6 counter jump PSW2_ICMP_REDIRECT" + nft "add rule $NFTABLE_NAME nat_output ip daddr != @$NFTSET_DIRECT meta l4proto icmp counter jump PSW2_ICMP_REDIRECT" + nft "add rule $NFTABLE_NAME nat_output ip6 daddr != @$NFTSET_DIRECT6 meta l4proto icmpv6 counter jump PSW2_ICMP_REDIRECT" fi #ipv4 wan_ip @@ -847,13 +840,11 @@ add_firewall_rule() { #ipv6 tproxy mode and udp nft "add chain $NFTABLE_NAME PSW2_MANGLE_V6" nft "flush chain $NFTABLE_NAME PSW2_MANGLE_V6" - nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ip6 daddr @$NFTSET_DIRECT6 counter return" nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return" nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ct direction reply counter return" nft "add chain $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6" nft "flush chain $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6" - nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_DIRECT6 counter return" nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ip6 daddr @$NFTSET_VPS6 counter return" nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 ct direction reply counter return" nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta mark 255 counter return" @@ -874,8 +865,10 @@ add_firewall_rule() { # jump chains [ "$PROXY_IPV6" == "1" ] && { - nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto {ipv6} counter jump PSW2_MANGLE_V6" - nft "add rule $NFTABLE_NAME mangle_output meta nfproto {ipv6} counter jump PSW2_OUTPUT_MANGLE_V6 comment \"PSW2_OUTPUT_MANGLE\"" + # Only TCP, UDP Invalid. + nft "add rule $NFTABLE_NAME mangle_prerouting meta nfproto ipv6 meta l4proto tcp socket transparent 1 mark set ${FWMARK} counter accept" + nft "add rule $NFTABLE_NAME mangle_prerouting ip6 daddr != @$NFTSET_DIRECT6 meta nfproto {ipv6} counter jump PSW2_MANGLE_V6" + nft "add rule $NFTABLE_NAME mangle_output ip6 daddr != @$NFTSET_DIRECT6 meta nfproto {ipv6} counter jump PSW2_OUTPUT_MANGLE_V6 comment \"PSW2_OUTPUT_MANGLE\"" nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 ip6 daddr @$NFTSET_WAN6 counter return comment \"WAN6_IP_RETURN\"" @@ -948,11 +941,11 @@ add_firewall_rule() { nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp ip daddr $FAKE_IP ${nft_j}" add_shunt_t_rule "${SHUNT_LIST4}" "nft add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ip daddr" "${nft_j}" nft "add rule $NFTABLE_NAME $nft_chain ip protocol tcp $(factor $TCP_REDIR_PORTS "tcp dport") ${nft_j}" - [ -z "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME nat_output ip protocol tcp counter jump PSW2_OUTPUT_NAT" + [ -z "${is_tproxy}" ] && nft "add rule $NFTABLE_NAME nat_output ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_OUTPUT_NAT" [ -n "${is_tproxy}" ] && { nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp iif lo $(REDIRECT $REDIR_PORT TPROXY4) comment \"${comment_l}\"" nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp iif lo counter return comment \"${comment_l}\"" - nft "add rule $NFTABLE_NAME mangle_output ip protocol tcp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\"" + nft "add rule $NFTABLE_NAME mangle_output ip daddr != @$NFTSET_DIRECT ip protocol tcp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\"" } [ "$PROXY_IPV6" == "1" ] && { @@ -978,7 +971,7 @@ add_firewall_rule() { nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE ip protocol udp $(factor $UDP_REDIR_PORTS "udp dport") counter jump PSW2_RULE" nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp iif lo $(REDIRECT $REDIR_PORT TPROXY4) comment \"${comment_l}\"" nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp iif lo counter return comment \"${comment_l}\"" - nft "add rule $NFTABLE_NAME mangle_output ip protocol udp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\"" + nft "add rule $NFTABLE_NAME mangle_output ip daddr != @$NFTSET_DIRECT ip protocol udp counter jump PSW2_OUTPUT_MANGLE comment \"PSW2_OUTPUT_MANGLE\"" [ "$PROXY_IPV6" == "1" ] && { nft "add rule $NFTABLE_NAME PSW2_OUTPUT_MANGLE_V6 meta l4proto udp ip6 daddr $FAKE_IP_6 jump PSW2_RULE" @@ -996,11 +989,13 @@ add_firewall_rule() { } fi - nft "add rule $NFTABLE_NAME mangle_output oif lo counter return comment \"PSW2_OUTPUT_MANGLE\"" - nft "add rule $NFTABLE_NAME mangle_output meta mark ${FWMARK} counter return comment \"PSW2_OUTPUT_MANGLE\"" + nft "add rule $NFTABLE_NAME mangle_output ip daddr != @$NFTSET_DIRECT oif lo counter return comment \"PSW2_OUTPUT_MANGLE\"" + nft "add rule $NFTABLE_NAME mangle_output ip daddr != @$NFTSET_DIRECT meta mark ${FWMARK} counter return comment \"PSW2_OUTPUT_MANGLE\"" - nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp udp dport 53 counter return" - nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp udp dport 53 counter return" + nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol tcp tcp dport 53 counter accept" + nft "add rule $NFTABLE_NAME PSW2_MANGLE ip protocol udp udp dport 53 counter accept" + nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto tcp tcp dport 53 counter accept" + nft "add rule $NFTABLE_NAME PSW2_MANGLE_V6 meta l4proto udp udp dport 53 counter accept" } load_acl diff --git a/luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh b/luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh index 54b8b23f..36fda212 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/socks_auto_switch.sh @@ -126,7 +126,11 @@ test_auto_switch() { # If the current node is not found, or if the current node is the last node, then take the first node. [ -z "$new_node" ] && new_node="$first_node" local msg2="$(i18n "next backup node")" - [ "$now_node" = "$main_node" ] && msg2="$(i18n "backup node")" + if [ "$new_node" = "$main_node" ]; then + msg2="$(i18n "main node")" + else + [ "$now_node" = "$main_node" ] && msg2="$(i18n "backup node")" + fi msg="$(i18n "switch to %s test detect!" "${msg2}")" else # When there is only one backup node, poll with the primary node. @@ -138,11 +142,6 @@ test_auto_switch() { log_i18n 0 "Socks switch detection: %s 【%s:[%s]】 abnormal, %s" "${id}" "$(config_n_get $now_node type)" "$(config_n_get $now_node remarks)" "${msg}" test_node ${new_node} if [ $? -eq 0 ]; then -# [ "$restore_switch" = "0" ] && { -# uci set $CONFIG.${id}.node=$new_node -# [ -z "$(echo $b_nodes | grep $main_node)" ] && uci add_list $CONFIG.${id}.autoswitch_backup_node=$main_node -# uci commit $CONFIG -# } check_process log_i18n 0 "Socks switch detection: %s 【%s:[%s]】 normal, switch to this node!" "${id}" "$(config_n_get $new_node type)" "$(config_n_get $new_node remarks)" $APP_FILE socks_node_switch flag=${id} new_node=${new_node} @@ -171,6 +170,10 @@ start() { backup_node_num=$(printf "%s\n" "$backup_node" | wc -w) if [ "$backup_node_num" -eq 1 ]; then [ "$main_node" = "$backup_node" ] && return + elif [ "$backup_node_num" -gt 1 ]; then + [ "$restore_switch" != "1" ] && { + [ -z "$(echo $backup_node | grep -F "$main_node")" ] && backup_node="${backup_node} ${main_node}" + } fi else return diff --git a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua index c5f81cbf..56464045 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua +++ b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua @@ -768,6 +768,9 @@ local function parseClashNode(node, add_mode, group, sub_cfg) result.reality_publicKey = node["reality-opts"]["public-key"] result.reality_shortId = node["reality-opts"]["short-id"] end + if node["disable-reuse"] then + result.anytls_disable_reuse = "1" + end end if not result.remarks or result.remarks == "" then if result.address and result.port then @@ -1865,7 +1868,7 @@ local function processData(szType, content, add_mode, group, sub_cfg) end if params.security == "tls" or params.security == "reality" then result.tls = "1" - result.tls_serverName = params.sni + result.tls_serverName = params.sni or params.peer result.alpn = params.alpn if params.fp and params.fp ~= "" then result.utls = "1"