Skip to content

Commit fc17f19

Browse files
authored
luci: add naiveproxy support of sing-box
1 parent 6eaac0d commit fc17f19

File tree

5 files changed

+186
-1
lines changed

5 files changed

+186
-1
lines changed

luci-app-passwall/luasrc/model/cbi/passwall/client/type/sing-box.lua

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ if singbox_tags:find("with_quic") then
6565
end
6666
o:value("anytls", "AnyTLS")
6767
o:value("ssh", "SSH")
68+
if singbox_tags:find("with_naive_outbound") then
69+
o:value("naive", "NaïveProxy")
70+
end
6871
o:value("_urltest", translate("URLTest"))
6972
o:value("_shunt", translate("Shunt"))
7073
o:value("_iface", translate("Custom Interface"))
@@ -224,6 +227,7 @@ o = s:option(Value, _n("username"), translate("Username"))
224227
o:depends({ [_n("protocol")] = "http" })
225228
o:depends({ [_n("protocol")] = "socks" })
226229
o:depends({ [_n("protocol")] = "ssh" })
230+
o:depends({ [_n("protocol")] = "naive" })
227231

228232
o = s:option(Value, _n("password"), translate("Password"))
229233
o.password = true
@@ -234,6 +238,7 @@ o:depends({ [_n("protocol")] = "trojan" })
234238
o:depends({ [_n("protocol")] = "tuic" })
235239
o:depends({ [_n("protocol")] = "anytls" })
236240
o:depends({ [_n("protocol")] = "ssh" })
241+
o:depends({ [_n("protocol")] = "naive" })
237242

238243
o = s:option(ListValue, _n("security"), translate("Encrypt Method"))
239244
for a, t in ipairs(security_list) do o:value(t) end
@@ -248,6 +253,7 @@ o:depends({ [_n("protocol")] = "shadowsocks" })
248253
o = s:option(Flag, _n("uot"), translate("UDP over TCP"))
249254
o:depends({ [_n("protocol")] = "socks" })
250255
o:depends({ [_n("protocol")] = "shadowsocks" })
256+
o:depends({ [_n("protocol")] = "naive" })
251257

252258
o = s:option(Value, _n("alter_id"), "Alter ID")
253259
o.datatype = "uinteger"
@@ -399,6 +405,26 @@ o = s:option(Value, _n("ssh_client_version"), translate("Client Version"), trans
399405
o:depends({ [_n("protocol")] = "ssh" })
400406
-- [[ SSH config end ]] --
401407

408+
-- [[ naive start ]] --
409+
o = s:option(Value, _n("naive_insecure_concurrency"), translate("Concurrent Tunnels"))
410+
o.datatype = "uinteger"
411+
o.placeholder = "0"
412+
o.default = "0"
413+
o:depends({ [_n("protocol")] = "naive" })
414+
415+
o = s:option(Flag, _n("naive_quic"), translate("QUIC"))
416+
o.default = 0
417+
o:depends({ [_n("protocol")] = "naive" })
418+
419+
o = s:option(ListValue, _n("naive_congestion_control"), translate("Congestion control algorithm"))
420+
o.default = "bbr"
421+
o:value("bbr", translate("BBR"))
422+
o:value("bbr2", translate("BBRv2"))
423+
o:value("cubic", translate("CUBIC"))
424+
o:value("reno", translate("New Reno"))
425+
o:depends({ [_n("naive_quic")] = "1" })
426+
-- [[ naive end ]] --
427+
402428
o = s:option(Flag, _n("tls"), translate("TLS"))
403429
o.default = 0
404430
o:depends({ [_n("protocol")] = "vmess" })
@@ -432,6 +458,7 @@ o:depends({ [_n("tls")] = true })
432458
o:depends({ [_n("protocol")] = "hysteria"})
433459
o:depends({ [_n("protocol")] = "tuic" })
434460
o:depends({ [_n("protocol")] = "hysteria2" })
461+
o:depends({ [_n("protocol")] = "naive" })
435462

436463
o = s:option(Flag, _n("tls_allowInsecure"), translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped."))
437464
o.default = "0"
@@ -446,6 +473,7 @@ o:depends({ [_n("tls")] = true, [_n("flow")] = "", [_n("reality")] = false })
446473
o:depends({ [_n("protocol")] = "tuic" })
447474
o:depends({ [_n("protocol")] = "hysteria" })
448475
o:depends({ [_n("protocol")] = "hysteria2" })
476+
o:depends({ [_n("protocol")] = "naive" })
449477

450478
o = s:option(TextValue, _n("ech_config"), translate("ECH Config"))
451479
o.default = ""
@@ -622,6 +650,7 @@ o:depends({ [_n("tcp_guise")] = "http" })
622650
o:depends({ [_n("transport")] = "http" })
623651
o:depends({ [_n("transport")] = "ws" })
624652
o:depends({ [_n("transport")] = "httpupgrade" })
653+
o:depends({ [_n("protocol")] = "naive" })
625654

626655
-- [[ Mux ]]--
627656
o = s:option(Flag, _n("mux"), translate("Mux"))

luci-app-passwall/luasrc/passwall/util_sing-box.lua

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ function gen_outbound(flag, node, tag, proxy_table)
154154
}
155155

156156
local tls = nil
157-
if node.protocol == "hysteria" or node.protocol == "hysteria2" or node.protocol == "tuic" then
157+
if node.protocol == "hysteria" or node.protocol == "hysteria2" or node.protocol == "tuic" or node.protocol == "naive" then
158158
node.tls = "1"
159159
end
160160
if node.tls == "1" then
@@ -489,6 +489,23 @@ function gen_outbound(flag, node, tag, proxy_table)
489489
}
490490
end
491491

492+
if node.protocol == "naive" then
493+
protocol_table = {
494+
username = (node.username and node.username ~= "") and node.username or "",
495+
password = (node.password and node.password ~= "") and node.password or "",
496+
insecure_concurrency = tonumber(node.naive_insecure_concurrency or 0) > 0 and tonumber(node.naive_insecure_concurrency) or 0,
497+
udp_over_tcp = node.uot == "1" and {
498+
enabled = true,
499+
version = 2
500+
} or false,
501+
extra_headers = node.user_agent and {
502+
["User-Agent"] = node.user_agent
503+
} or nil,
504+
quic = node.naive_quic == "1" and true or false,
505+
quic_congestion_control = (node.naive_quic == "1" and node.naive_congestion_control) and node.naive_congestion_control or nil
506+
}
507+
end
508+
492509
if protocol_table then
493510
for key, value in pairs(protocol_table) do
494511
result[key] = value

luci-app-passwall/luasrc/view/passwall/node_config/link_share_man.htm

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,31 @@
679679
params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure");
680680
}
681681

682+
params += "#" + encodeURI(v_alias.value);
683+
if (params[0] == "&") {
684+
params = params.substring(1);
685+
}
686+
url += params;
687+
} else if (v_type === "sing-box" && opt.get(dom_prefix + "protocol").value === "naive") {
688+
protocol = "naive+https";
689+
var v_username = opt.get(dom_prefix + "username");
690+
var v_password = opt.get(dom_prefix + "password");
691+
var v_port = opt.get(dom_prefix + "port");
692+
url = encodeURIComponent(v_username.value) +
693+
":" + encodeURIComponent(v_password.value) +
694+
"@" + _address +
695+
":" + v_port.value + "?";
696+
697+
var params = "security=tls";
698+
params += opt.query("sni", dom_prefix + "tls_serverName");
699+
params += opt.query("insecure-concurrency", dom_prefix + "naive_insecure_concurrency");
700+
params += opt.query("ech", dom_prefix + "ech_config");
701+
params += opt.query("uot", dom_prefix + "uot");
702+
if (opt.get(dom_prefix + "naive_quic")?.checked) {
703+
protocol = "naive+quic";
704+
params += opt.query("congestion_control", dom_prefix + "naive_congestion_control");
705+
}
706+
682707
params += "#" + encodeURI(v_alias.value);
683708
if (params[0] == "&") {
684709
params = params.substring(1);
@@ -1729,6 +1754,55 @@
17291754
opt.set('remarks', decodeURIComponent(m.hash.substr(1)));
17301755
}
17311756
}
1757+
if (ssu[0] === "naive+https" || ssu[0] === "naive+quic") {
1758+
if (has_singbox) {
1759+
dom_prefix = "singbox_"
1760+
opt.set('type', "sing-box");
1761+
}
1762+
opt.set(dom_prefix + 'protocol', "naive");
1763+
var _parsedUrl = new URL("http://" + ssu[1]);
1764+
var username = _parsedUrl.username;
1765+
var password = _parsedUrl.password;
1766+
var hostname = _parsedUrl.hostname;
1767+
var port = _parsedUrl.port;
1768+
var search = _parsedUrl.search;
1769+
var hash = _parsedUrl.hash;
1770+
if (!username || !password) { //某些链接会把username和password之间的:进行编码
1771+
const decoded = decodeURIComponent(username || password || "");
1772+
const i = decoded.indexOf(":");
1773+
if (i > -1) {
1774+
username = decoded.slice(0, i);
1775+
password = decoded.slice(i + 1);
1776+
}
1777+
}
1778+
opt.set(dom_prefix + 'username', decodeURIComponent(username));
1779+
opt.set(dom_prefix + 'password', decodeURIComponent(password));
1780+
opt.set(dom_prefix + 'address', unbracketIP(hostname));
1781+
opt.set(dom_prefix + 'port', port || "443");
1782+
var queryParam = {};
1783+
if (search.length > 1) {
1784+
var query = search.split('?')
1785+
var queryParams = query[1];
1786+
var queryArray = queryParams.split('&');
1787+
var params;
1788+
for (i = 0; i < queryArray.length; i++) {
1789+
params = queryArray[i].split('=');
1790+
queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || '');
1791+
}
1792+
}
1793+
opt.set(dom_prefix + 'naive_insecure_concurrency', queryParam['insecure-concurrency'] || '0');
1794+
opt.set(dom_prefix + 'uot', (queryParam.uot ?? '0') === '1');
1795+
opt.set(dom_prefix + 'tls_serverName', queryParam.sni || '');
1796+
opt.set(dom_prefix + 'ech', !!queryParam.ech);
1797+
opt.set(dom_prefix + 'ech_config', queryParam.ech || '');
1798+
if (ssu[0] === "naive+quic") {
1799+
opt.set(dom_prefix + 'naive_quic', true);
1800+
opt.set(dom_prefix + 'naive_congestion_control', queryParam.congestion_control || 'bbr');
1801+
}
1802+
if (hash) {
1803+
opt.set('remarks', decodeURIComponent(hash.substr(1)));
1804+
}
1805+
}
17321806
if (dom_prefix && dom_prefix != null) {
17331807
if (opt.get(dom_prefix + 'port').value) {
17341808
opt.get(dom_prefix + 'port').focus();

luci-app-passwall/po/zh-cn/passwall.po

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2107,3 +2107,6 @@ msgstr "当前使用的 %s 节点"
21072107

21082108
msgid "Search nodes..."
21092109
msgstr "搜索节点…"
2110+
2111+
msgid "Concurrent Tunnels"
2112+
msgstr "并发隧道连接数"

luci-app-passwall/root/usr/share/passwall/subscribe.lua

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1561,6 +1561,68 @@ local function processData(szType, content, add_mode, group)
15611561
return nil
15621562
end
15631563
end
1564+
elseif szType == 'naive+https' or szType == 'naive+quic' then
1565+
if has_singbox then
1566+
result.type = 'sing-box'
1567+
result.protocol = "naive"
1568+
else
1569+
log("跳过 NaïveProxy 节点,因未安装 NaïveProxy 核心程序 Sing-box。")
1570+
return nil
1571+
end
1572+
1573+
local alias = ""
1574+
if content:find("#") then
1575+
local idx_sp = content:find("#")
1576+
alias = content:sub(idx_sp + 1, -1)
1577+
content = content:sub(0, idx_sp - 1)
1578+
end
1579+
result.remarks = UrlDecode(alias)
1580+
local Info = content
1581+
if content:find("@", 1, true) then
1582+
local contents = split(content, "@")
1583+
local auth = contents[1] or ""
1584+
local idx = auth:find(":", 1, true)
1585+
if not idx then --修正某些链接会把username和password之间的:进行编码
1586+
auth = UrlDecode(auth)
1587+
idx = auth:find(":", 1, true)
1588+
end
1589+
if idx then
1590+
result.username = UrlDecode(auth:sub(1, idx - 1))
1591+
result.password = UrlDecode(auth:sub(idx + 1))
1592+
end
1593+
Info = (contents[2] or ""):gsub("/%?", "?")
1594+
end
1595+
local query = split(Info, "%?")
1596+
local host_port = query[1]
1597+
local params = {}
1598+
for _, v in pairs(split(query[2], '&')) do
1599+
local t = split(v, '=')
1600+
if #t > 1 then
1601+
params[string.lower(t[1])] = UrlDecode(t[2])
1602+
end
1603+
end
1604+
if host_port:find(":") then
1605+
local sp = split(host_port, ":")
1606+
result.port = sp[#sp]
1607+
if api.is_ipv6addrport(host_port) then
1608+
result.address = api.get_ipv6_only(host_port)
1609+
else
1610+
result.address = sp[1]
1611+
end
1612+
else
1613+
result.address = host_port
1614+
end
1615+
result.tls_serverName = params.sni
1616+
result.uot = params.uot
1617+
result.naive_insecure_concurrency = params["insecure-concurrency"] or "0"
1618+
if params.ech and params.ech ~= "" then
1619+
result.ech = "1"
1620+
result.ech_config = params.ech
1621+
end
1622+
if szType == "naive+quic" then
1623+
result.naive_quic = "1"
1624+
result.naive_congestion_control = params.congestion_control or "bbr"
1625+
end
15641626
else
15651627
log('暂时不支持' .. szType .. "类型的节点订阅,跳过此节点。")
15661628
return nil

0 commit comments

Comments
 (0)