diff --git a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua index 4c5ce1c2f..09372825a 100644 --- a/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua +++ b/luci-app-passwall/luasrc/model/cbi/passwall/client/node_subscribe_config.lua @@ -175,12 +175,18 @@ o.default = 2 o:depends("week_update", "8") o.rmempty = true +o = s:option(ListValue, "access_mode", translate("Subscribe URL Access Method")) +o.default = "" +o:value("", translate("Auto")) +o:value("direct", translate("Direct Connection")) +o:value("proxy", translate("Proxy")) + o = s:option(Value, "user_agent", translate("User-Agent")) o.default = "v2rayN/9.99" o:value("curl", "Curl") o:value("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Linux") o:value("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0", "Edge for Windows") o:value("Passwall/OpenWrt", "PassWall") -o:value("v2rayN/9.99", "V2rayN") +o:value("v2rayN/9.99", "v2rayN") return m diff --git a/luci-app-passwall/luasrc/passwall/api.lua b/luci-app-passwall/luasrc/passwall/api.lua index 61565e9e9..9acf8aaef 100644 --- a/luci-app-passwall/luasrc/passwall/api.lua +++ b/luci-app-passwall/luasrc/passwall/api.lua @@ -63,6 +63,29 @@ function base64Decode(text) end end +--提取URL中的域名和端口(no ip) +function get_domain_port_from_url(url) + local scheme, domain, port = string.match(url, "^(https?)://([%w%.%-]+):?(%d*)") + if not domain then + scheme, domain, port = string.match(url, "^(https?)://(%b[])([^:/]*)/?") + end + if not domain then return nil, nil end + if domain:sub(1, 1) == "[" then domain = domain:sub(2, -2) end + port = port ~= "" and tonumber(port) or (scheme == "https" and 443 or 80) + if datatypes.ipaddr(domain) or datatypes.ip6addr(domain) then return nil, nil end + return domain, port +end + +--解析域名 +function domainToIPv4(domain, dns) + local Dns = dns or "223.5.5.5" + local IPs = luci.sys.exec('nslookup %s %s | awk \'/^Name:/{getline; if ($1 == "Address:") print $2}\'' % { domain, Dns }) + for IP in string.gmatch(IPs, "%S+") do + if not datatypes.ip6addr(IP) then return IP end + end + return nil +end + function curl_base(url, file, args) if not args then args = {} end if file then @@ -92,6 +115,28 @@ function curl_logic(url, file, args) return return_code, result end +function curl_direct(url, file, args) + --直连访问 + if not args then args = {} end + local tmp_args = clone(args) + local domain, port = get_domain_port_from_url(url) + if domain then + local ip = domainToIPv4(domain) + if ip then + tmp_args[#tmp_args + 1] = "--resolve " .. domain .. ":" .. port .. ":" .. ip + end + end + return curl_base(url, file, tmp_args) +end + +function curl_auto(url, file, args) + local return_code, result = curl_proxy(url, file, args) + if not return_code or return_code ~= 0 then + return_code, result = curl_direct(url, file, args) + end + return return_code, result +end + function url(...) local url = string.format("admin/services/%s", appname) local args = { ... } diff --git a/luci-app-passwall/po/zh-cn/passwall.po b/luci-app-passwall/po/zh-cn/passwall.po index d15e8206a..12c1c894e 100644 --- a/luci-app-passwall/po/zh-cn/passwall.po +++ b/luci-app-passwall/po/zh-cn/passwall.po @@ -1009,6 +1009,9 @@ msgstr "订阅备注(机场)" msgid "Subscribe URL" msgstr "订阅网址" +msgid "Subscribe URL Access Method" +msgstr "订阅网址访问方式" + msgid "Please input the subscription url first, save and submit before manual subscription." msgstr "请输入订阅网址保存应用后再手动订阅。" diff --git a/luci-app-passwall/root/usr/share/passwall/subscribe.lua b/luci-app-passwall/root/usr/share/passwall/subscribe.lua index 1dbc3c224..49b5fc2b5 100755 --- a/luci-app-passwall/root/usr/share/passwall/subscribe.lua +++ b/luci-app-passwall/root/usr/share/passwall/subscribe.lua @@ -1216,12 +1216,19 @@ local function processData(szType, content, add_mode, add_from) return result end -local function curl(url, file, ua) +local function curl(url, file, ua, mode) local curl_args = api.clone(api.curl_args) if ua and ua ~= "" and ua ~= "curl" then table.insert(curl_args, '--user-agent "' .. ua .. '"') end - local return_code, result = api.curl_logic(url, file, curl_args) + local return_code + if mode == "direct" then + return_code = api.curl_direct(url, file, curl_args) + elseif mode == "proxy" then + return_code = api.curl_proxy(url, file, curl_args) + else + return_code = api.curl_auto(url, file, curl_args) + end return return_code end @@ -1610,8 +1617,10 @@ local execute = function() domain_strategy_node = domain_strategy_default end local ua = value.user_agent - log('正在订阅:【' .. remark .. '】' .. url) - local raw = curl(url, "/tmp/" .. cfgid, ua) + local access_mode = value.access_mode + local result = (not access_mode) and "自动" or (access_mode == "direct" and "直连访问" or (access_mode == "proxy" and "通过代理" or "自动")) + log('正在订阅:【' .. remark .. '】' .. url .. ' [' .. result .. ']') + local raw = curl(url, "/tmp/" .. cfgid, ua, access_mode) if raw == 0 then local f = io.open("/tmp/" .. cfgid, "r") local stdout = f:read("*all")