diff --git a/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/luci-app-passwall/root/usr/share/passwall/subscribe.lua index 4229d607d..0847dcbe7 100755 --- a/luci-app-passwall/root/usr/share/passwall/subscribe.lua +++ b/luci-app-passwall/root/usr/share/passwall/subscribe.lua @@ -515,6 +515,7 @@ local function processData(szType, content, add_mode, add_from) --ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888#Example3 --ss://2022-blake3-aes-256-gcm:YctPZ6U7xPPcU%2Bgp3u%2B0tx%2FtRizJN9K8y%2BuKlW2qjlI%3D@192.168.100.1:8888/?plugin=v2ray-plugin%3Bserver#Example3 --ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTp0ZXN0@xxxxxx.com:443?type=ws&path=%2Ftestpath&host=xxxxxx.com&security=tls&fp=&alpn=h3%2Ch2%2Chttp%2F1.1&sni=xxxxxx.com#test-1%40ss + --ss://Y2hhY2hhMjAtaWV0Zi1wb2x5MTMwNTp4eHh4eHhAeHh4eC54eHh4eC5jb206NTYwMDE#Hong%20Kong-01 local idx_sp = 0 local alias = "" @@ -549,7 +550,7 @@ local function processData(szType, content, add_mode, add_from) info = info:sub(1, find_index - 1) end - local hostInfo = split(UrlDecode(info), "@") + local hostInfo = split(base64Decode(info), "@") if hostInfo and #hostInfo > 0 then local host_port = hostInfo[#hostInfo] -- [2001:4860:4860::8888]:443 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 07b3dfd83..cc3654602 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/global.lua @@ -244,6 +244,16 @@ node_socks_bind_local:depends({ node = "nil", ["!reverse"] = true }) s:tab("DNS", translate("DNS")) +o = s:taboption("DNS", ListValue, "direct_dns_query_strategy", translate("Direct Query Strategy")) +o.default = "UseIP" +o:value("UseIP") +o:value("UseIPv4") +o:value("UseIPv6") + +o = s:taboption("DNS", Flag, "write_ipset_direct", translate("Direct DNS result write to IPSet"), translate("Perform the matching direct domain name rules into IP to IPSet/NFTSet, and then connect directly (not entering the core). Maybe conflict with some special circumstances.")) +o.default = "1" +o.rmempty = false + o = s:taboption("DNS", ListValue, "remote_dns_protocol", translate("Remote DNS Protocol")) o:value("tcp", "TCP") o:value("doh", "DoH") @@ -315,10 +325,6 @@ o.remove = function(self, section) end end -o = s:taboption("DNS", Flag, "write_ipset_direct", translate("Direct DNS result write to IPSet"), translate("Perform the matching direct domain name rules into IP to IPSet/NFTSet, and then connect directly (not entering the core). Maybe conflict with some special circumstances.")) -o.default = "1" -o.rmempty = false - o = s:taboption("DNS", Button, "clear_ipset", translate("Clear IPSet"), translate("Try this feature if the rule modification does not take effect.")) o.inputstyle = "remove" function o.write(e, e) @@ -331,6 +337,8 @@ for k, v in pairs(nodes_table) do s.fields["remote_dns_client_ip"]:depends({ node = v.id, remote_dns_protocol = "tcp" }) s.fields["remote_dns_client_ip"]:depends({ node = v.id, remote_dns_protocol = "doh" }) s.fields["dns_hosts"]:depends({ node = v.id }) + elseif v.type == "sing-box" then + s.fields["direct_dns_query_strategy"]:depends({ node = v.id }) end end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/sing-box.lua index 887857e9b..cc0f31f49 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 @@ -334,7 +334,15 @@ if singbox_tags:find("with_quic") then o.default = "3" o:depends({ [option_name("protocol")] = "tuic" }) - o = s:option(Value, option_name("tuic_alpn"), translate("QUIC TLS ALPN")) + o = s:option(ListValue, option_name("tuic_alpn"), translate("QUIC TLS ALPN")) + o.default = "default" + o:value("default", translate("Default")) + o:value("h3") + o:value("h2") + o:value("h3,h2") + o:value("http/1.1") + o:value("h2,http/1.1") + o:value("h3,h2,http/1.1") o:depends({ [option_name("protocol")] = "tuic" }) end diff --git a/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm b/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm index 56c433595..8297c5968 100644 --- a/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm +++ b/luci-app-passwall2/luasrc/view/passwall2/node_list/link_share_man.htm @@ -413,6 +413,27 @@ local api = require "luci.passwall2.api" if (v_password) { url = encodeURIComponent(v_password.value) + "@" + url } + } else if (v_type === "sing-box" && opt.get(dom_prefix + "protocol").value === "tuic") { + protocol = "tuic"; + var v_username = opt.get(dom_prefix + "uuid"); + var v_password = opt.get(dom_prefix + "password"); + var v_port = opt.get(dom_prefix + "port"); + url = encodeURIComponent(v_username.value) + + ":" + encodeURIComponent(v_password.value) + + "@" + _address + + ":" + v_port.value + "?"; + + var params = ""; + params += opt.query("sni", dom_prefix + "tls_serverName"); + params += opt.query("alpn", dom_prefix + "tuic_alpn"); + params += opt.query("congestion_control", dom_prefix + "tuic_congestion_control"); + params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure"); + + params += "#" + encodeURI(v_alias.value); + if (params[0] == "&") { + params = params.substring(1); + } + url += params; } if (url) { url = protocol.toLowerCase() + "://" + url; @@ -980,6 +1001,45 @@ local api = require "luci.passwall2.api" opt.set('remarks', decodeURI(m.hash.substr(1))); } } + if (ssu[0] === "tuic") { + if (has_singbox) { + dom_prefix = "singbox_" + opt.set('type', "sing-box"); + } + opt.set(dom_prefix + 'protocol', "tuic"); + var _parsedUrl = new URL("http://" + ssu[1]); + var username = _parsedUrl.username; + var password = _parsedUrl.password; + var hostname = _parsedUrl.hostname; + var port = _parsedUrl.port; + var search = _parsedUrl.search; + var hash = _parsedUrl.hash; + opt.set(dom_prefix + 'uuid', decodeURIComponent(username)); + opt.set(dom_prefix + 'password', decodeURIComponent(password)); + opt.set(dom_prefix + 'address', hostname); + opt.set(dom_prefix + 'port', port || "443"); + var queryParam = {}; + if (search.length > 1) { + var query = search.split('?') + var queryParams = query[1]; + var queryArray = queryParams.split('&'); + var params; + for (i = 0; i < queryArray.length; i++) { + params = queryArray[i].split('='); + queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || ''); + } + } + opt.set(dom_prefix + 'tuic_congestion_control', queryParam.congestion_control || 'cubic'); + opt.set(dom_prefix + 'tuic_alpn', queryParam.alpn || 'default'); + opt.set(dom_prefix + 'tls_serverName', queryParam.sni || ''); + opt.set(dom_prefix + 'tls_allowInsecure', true); + if (queryParam.allowinsecure === '0') { + opt.set(dom_prefix + 'tls_allowInsecure', false); + } + if (hash) { + opt.set('remarks', decodeURIComponent(hash.substr(1))); + } + } if (dom_prefix && dom_prefix != null) { if (opt.get(dom_prefix + 'port').value) { opt.get(dom_prefix + 'port').focus(); diff --git a/luci-app-passwall2/root/usr/share/passwall2/app.sh b/luci-app-passwall2/root/usr/share/passwall2/app.sh index 92c98d128..3b41dbc8d 100644 --- a/luci-app-passwall2/root/usr/share/passwall2/app.sh +++ b/luci-app-passwall2/root/usr/share/passwall2/app.sh @@ -286,7 +286,7 @@ lua_api() { run_xray() { local flag node redir_port socks_address socks_port socks_username socks_password http_address http_port http_username http_password - local dns_listen_port remote_dns_protocol remote_dns_udp_server remote_dns_tcp_server remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy dns_cache write_ipset_direct + local dns_listen_port direct_dns_query_strategy remote_dns_protocol remote_dns_udp_server remote_dns_tcp_server remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy dns_cache write_ipset_direct local loglevel log_file config_file local _extra_param="" eval_set_val $@ @@ -394,7 +394,7 @@ run_xray() { run_singbox() { local flag node redir_port socks_address socks_port socks_username socks_password http_address http_port http_username http_password - local dns_listen_port remote_dns_protocol remote_dns_udp_server remote_dns_tcp_server remote_dns_doh remote_dns_detour remote_fakedns remote_dns_query_strategy dns_cache write_ipset_direct + local dns_listen_port direct_dns_query_strategy remote_dns_protocol remote_dns_udp_server remote_dns_tcp_server remote_dns_doh remote_dns_detour remote_fakedns remote_dns_query_strategy dns_cache write_ipset_direct local loglevel log_file config_file local _extra_param="" eval_set_val $@ @@ -449,7 +449,7 @@ run_singbox() { [ -n "${direct_ipset}" ] && _extra_param="${_extra_param} -direct_ipset ${direct_ipset}" [ -n "${direct_nftset}" ] && _extra_param="${_extra_param} -direct_nftset ${direct_nftset}" } - _extra_param="${_extra_param} -direct_dns_udp_port ${DIRECT_DNS_UDP_PORT} -direct_dns_udp_server ${DIRECT_DNS_UDP_SERVER} -direct_dns_query_strategy UseIP" + _extra_param="${_extra_param} -direct_dns_udp_port ${DIRECT_DNS_UDP_PORT} -direct_dns_udp_server ${DIRECT_DNS_UDP_SERVER} -direct_dns_query_strategy ${direct_dns_query_strategy}" case "$remote_dns_protocol" in udp) @@ -656,7 +656,7 @@ run_global() { PROXY_IPV6_UDP=1 fi V2RAY_ARGS="flag=global node=$NODE redir_port=$REDIR_PORT" - V2RAY_ARGS="${V2RAY_ARGS} dns_listen_port=${TUN_DNS_PORT} remote_dns_query_strategy=${REMOTE_DNS_QUERY_STRATEGY} dns_cache=${DNS_CACHE}" + V2RAY_ARGS="${V2RAY_ARGS} dns_listen_port=${TUN_DNS_PORT} direct_dns_query_strategy=${DIRECT_DNS_QUERY_STRATEGY} remote_dns_query_strategy=${REMOTE_DNS_QUERY_STRATEGY} dns_cache=${DNS_CACHE}" local msg="${TUN_DNS} (直连DNS:${AUTO_DNS}" [ -n "$REMOTE_DNS_PROTOCOL" ] && { @@ -946,7 +946,7 @@ acl_app() { dnsmasq_port=11400 for item in $items; do index=$(expr $index + 1) - local enabled sid remarks sources node remote_dns_protocol remote_dns remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy + local enabled sid remarks sources node direct_dns_query_strategy remote_dns_protocol remote_dns remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy local _ip _mac _iprange _ipset _ip_or_mac rule_list config_file sid=$(uci -q show "${CONFIG}.${item}" | grep "=acl_rule" | awk -F '=' '{print $1}' | awk -F '.' '{print $2}') eval $(uci -q show "${CONFIG}.${item}" | cut -d'.' -sf 3-) @@ -975,6 +975,7 @@ acl_app() { tcp_proxy_mode="global" udp_proxy_mode="global" node=${node:-default} + direct_dns_query_strategy=${direct_dns_query_strategy:-UseIP} remote_dns_protocol=${remote_dns_protocol:-tcp} remote_dns=${remote_dns:-1.1.1.1} [ "$remote_dns_protocol" = "doh" ] && remote_dns=${remote_dns_doh:-https://1.1.1.1/dns-query} @@ -1009,7 +1010,7 @@ acl_app() { elif [ "${type}" = "sing-box" ] && [ -n "${SINGBOX_BIN}" ]; then run_func="run_singbox" fi - ${run_func} flag=acl_$sid node=$node redir_port=$redir_port socks_address=127.0.0.1 socks_port=$acl_socks_port dns_listen_port=${dns_port} direct_dns_query_strategy=UseIP remote_dns_protocol=${remote_dns_protocol} remote_dns_tcp_server=${remote_dns} remote_dns_udp_server=${remote_dns} remote_dns_doh="${remote_dns}" remote_dns_client_ip=${remote_dns_client_ip} remote_dns_detour=${remote_dns_detour} remote_fakedns=${remote_fakedns} remote_dns_query_strategy=${remote_dns_query_strategy} write_ipset_direct=${write_ipset_direct} config_file=${config_file} + ${run_func} flag=acl_$sid node=$node redir_port=$redir_port socks_address=127.0.0.1 socks_port=$acl_socks_port dns_listen_port=${dns_port} direct_dns_query_strategy=${direct_dns_query_strategy} remote_dns_protocol=${remote_dns_protocol} remote_dns_tcp_server=${remote_dns} remote_dns_udp_server=${remote_dns} remote_dns_doh="${remote_dns}" remote_dns_client_ip=${remote_dns_client_ip} remote_dns_detour=${remote_dns_detour} remote_fakedns=${remote_fakedns} remote_dns_query_strategy=${remote_dns_query_strategy} write_ipset_direct=${write_ipset_direct} config_file=${config_file} fi dnsmasq_port=$(get_new_port $(expr $dnsmasq_port + 1)) redirect_dns_port=$dnsmasq_port @@ -1040,7 +1041,7 @@ acl_app() { echo "${redir_port}" > $TMP_ACL_PATH/$sid/var_port } [ -n "$redirect_dns_port" ] && echo "${redirect_dns_port}" > $TMP_ACL_PATH/$sid/var_redirect_dns_port - unset enabled sid remarks sources node remote_dns_protocol remote_dns remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy + unset enabled sid remarks sources node direct_dns_query_strategy remote_dns_protocol remote_dns remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy unset _ip _mac _iprange _ipset _ip_or_mac rule_list config_file unset redirect_dns_port done @@ -1147,6 +1148,7 @@ TCP_PROXY_MODE="global" UDP_PROXY_MODE="global" LOCALHOST_PROXY=$(config_t_get global localhost_proxy '1') CLIENT_PROXY=$(config_t_get global client_proxy '1') +DIRECT_DNS_QUERY_STRATEGY=$(config_t_get global direct_dns_query_strategy UseIP) REMOTE_DNS_PROTOCOL=$(config_t_get global remote_dns_protocol tcp) REMOTE_DNS_DETOUR=$(config_t_get global remote_dns_detour remote) REMOTE_DNS=$(config_t_get global remote_dns 1.1.1.1:53 | sed 's/#/:/g' | sed -E 's/\:([^:]+)$/#\1/g') diff --git a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua index 0391c6f90..58018462c 100755 --- a/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua +++ b/luci-app-passwall2/root/usr/share/passwall2/subscribe.lua @@ -480,6 +480,10 @@ local function processData(szType, content, add_mode, add_from) if info.net == 'grpc' then result.grpc_serviceName = info.path end + if info.net == 'splithttp' then + result.splithttp_host = info.host + result.splithttp_path = info.path + end if not info.security then result.security = "auto" end if info.tls == "tls" or info.tls == "1" then result.tls = "1" @@ -488,6 +492,11 @@ local function processData(szType, content, add_mode, add_from) else result.tls = "0" end + + if result.type == "sing-box" and (result.transport == "mkcp" or result.transport == "splithttp") then + log("跳过节点:" .. result.remarks ..",因Sing-Box不支持" .. szType .. "协议的" .. result.transport .. "传输方式,需更换Xray。") + return nil + end elseif szType == "ss" then result.type = "SS" @@ -737,10 +746,19 @@ local function processData(szType, content, add_mode, add_from) if params.serviceName then result.grpc_serviceName = params.serviceName end result.grpc_mode = params.mode end + if params.type == 'splithttp' then + result.splithttp_host = params.host + result.splithttp_path = params.path + end result.encryption = params.encryption or "none" result.flow = params.flow or nil + + if result.type == "sing-box" and (result.transport == "mkcp" or result.transport == "splithttp") then + log("跳过节点:" .. result.remarks ..",因Sing-Box不支持" .. szType .. "协议的" .. result.transport .. "传输方式,需更换Xray。") + return nil + end end elseif szType == "ssd" then result.type = "SS" @@ -884,6 +902,11 @@ local function processData(szType, content, add_mode, add_from) result.port = port result.tls_allowInsecure = allowInsecure_default and "1" or "0" + + if result.type == "sing-box" and (result.transport == "mkcp" or result.transport == "splithttp") then + log("跳过节点:" .. result.remarks ..",因Sing-Box不支持" .. szType .. "协议的" .. result.transport .. "传输方式,需更换Xray。") + return nil + end end elseif szType == 'hysteria' then local alias = "" @@ -991,6 +1014,59 @@ local function processData(szType, content, add_mode, add_from) result.hysteria2_obfs_password = params["obfs-password"] end end + elseif szType == 'tuic' then + local alias = "" + if content:find("#") then + local idx_sp = content:find("#") + alias = content:sub(idx_sp + 1, -1) + content = content:sub(0, idx_sp - 1) + end + result.remarks = UrlDecode(alias) + local Info = content + if content:find("@") then + local contents = split(content, "@") + if contents[1]:find(":") then + local userinfo = split(contents[1], ":") + result.uuid = UrlDecode(userinfo[1]) + result.password = UrlDecode(userinfo[2]) + end + Info = (contents[2] or ""):gsub("/%?", "?") + end + local query = split(Info, "?") + local host_port = query[1] + local params = {} + for _, v in pairs(split(query[2], '&')) do + local t = split(v, '=') + if #t > 1 then + params[string.lower(t[1])] = UrlDecode(t[2]) + end + end + if host_port:find(":") then + local sp = split(host_port, ":") + result.port = sp[#sp] + if api.is_ipv6addrport(host_port) then + result.address = api.get_ipv6_only(host_port) + else + result.address = sp[1] + end + else + result.address = host_port + end + result.tls_serverName = params.sni + result.tuic_alpn = params.alpn or "default" + result.tuic_congestion_control = params.congestion_control or "cubic" + if params.allowinsecure then + if params.allowinsecure == "1" or params.allowinsecure == "0" then + result.tls_allowInsecure = params.allowinsecure + else + result.tls_allowInsecure = string.lower(params.allowinsecure) == "true" and "1" or "0" + end + --log(result.remarks .. ' 使用节点AllowInsecure设定: '.. result.tls_allowInsecure) + else + result.tls_allowInsecure = allowInsecure_default and "1" or "0" + end + result.type = 'sing-box' + result.protocol = "tuic" else log('暂时不支持' .. szType .. "类型的节点订阅,跳过此节点。") return nil