luci-app-passwall: sync with upstream source

This commit is contained in:
CN_SZTL 2020-07-19 17:27:49 +08:00
parent 95923f589c
commit 45e68bf527
No known key found for this signature in database
GPG Key ID: 6850B6345C862176
10 changed files with 807 additions and 155 deletions

View File

@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=3.9
PKG_RELEASE:=18
PKG_RELEASE:=19
PKG_DATE:=20200717
PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)

View File

@ -37,6 +37,9 @@ function index()
entry({"admin", "vpn", "passwall", "server_user_status"}, call("server_user_status")).leaf = true
entry({"admin", "vpn", "passwall", "server_get_log"}, call("server_get_log")).leaf = true
entry({"admin", "vpn", "passwall", "server_clear_log"}, call("server_clear_log")).leaf = true
entry({"admin", "vpn", "passwall", "link_append_temp"}, call("link_append_temp")).leaf = true
entry({"admin", "vpn", "passwall", "link_load_temp"}, call("link_load_temp")).leaf = true
entry({"admin", "vpn", "passwall", "link_clear_temp"}, call("link_clear_temp")).leaf = true
entry({"admin", "vpn", "passwall", "link_add_node"}, call("link_add_node")).leaf = true
entry({"admin", "vpn", "passwall", "get_log"}, call("get_log")).leaf = true
entry({"admin", "vpn", "passwall", "clear_log"}, call("clear_log")).leaf = true
@ -82,9 +85,38 @@ function hide_menu()
luci.http.redirect(luci.dispatcher.build_url("admin", "status", "overview"))
end
function link_add_node()
function link_append_temp()
local link = luci.http.formvalue("link")
luci.sys.call('rm -f /tmp/links.conf && echo "' .. link .. '" >> /tmp/links.conf')
local lfile = "/tmp/links.conf"
local ret, ldata="empty", {}
luci.sys.call('touch ' .. lfile .. ' && echo \'' .. link .. '\' >> ' .. lfile)
ret = luci.sys.exec([[awk -F'://' 'BEGIN{ all=0 } /.{2,9}:\/\/.{4,}$/ {gsub(/:\/\/.*$/,""); arr[$0]++; all++ } END { for(typ in arr) { printf("%s: %d, ", typ, arr[typ]) }; printf("\ntotal: %d", all) }' ]] .. lfile)
luci.http.prepare_content("application/json")
luci.http.write_json({counter = ret})
end
function link_load_temp()
local lfile = "/tmp/links.conf"
local ret, ldata="empty", {}
ldata[#ldata+1] = nixio.fs.readfile(lfile) or "_nofile_"
if ldata[1] == "" then
ldata[1] = "_nodata_"
else
ret = luci.sys.exec([[awk -F'://' 'BEGIN{ all=0 } /.{2,9}:\/\/.{4,}$/ {gsub(/:\/\/.*$/,""); arr[$0]++; all++ } END { for(typ in arr) { printf("%s: %d, ", typ, arr[typ]) }; printf("\ntotal: %d", all) }' ]] .. lfile)
end
luci.http.prepare_content("application/json")
luci.http.write_json({counter = ret, data = ldata})
end
function link_clear_temp()
local lfile = "/tmp/links.conf"
luci.sys.call('cat /dev/null > ' .. lfile)
end
function link_add_node()
local lfile = "/tmp/links.conf"
local link = luci.http.formvalue("link")
luci.sys.call('echo \'' .. link .. '\' >> ' .. lfile)
luci.sys.call("lua /usr/share/passwall/subscribe.lua add log")
end

View File

@ -48,7 +48,7 @@ local function gen_outbound(node, tag)
concurrency = (node.mux_concurrency) and tonumber(node.mux_concurrency) or 8
},
-- 底层传输配置
streamSettings = (node.protocol == "vmess") and {
streamSettings = (node.protocol == "vmess" or node.protocol == "socks" or node.protocol == "shadowsocks") and {
network = node.transport,
security = node.stream_security,
tlsSettings = (node.stream_security == "tls") and {
@ -56,8 +56,7 @@ local function gen_outbound(node, tag)
serverName = node.tls_serverName,
allowInsecure = (node.tls_allowInsecure == "1") and true or false
} or nil,
tcpSettings = (node.transport == "tcp" and
node.protocol ~= "socks") and {
tcpSettings = (node.transport == "tcp" and node.protocol ~= "socks") and {
header = {
type = node.tcp_guise,
request = {

View File

@ -10,8 +10,7 @@ local function get_customed_path(e)
end
local function is_finded(e)
return luci.sys.exec("find /usr/*bin %s -iname %s -type f" %
{get_customed_path(e), e}) ~= "" and true or false
return luci.sys.exec("find /usr/*bin %s -iname %s -type f" % {get_customed_path(e), e}) ~= "" and true or false
end
local function is_installed(e) return ipkg.installed(e) end
@ -73,13 +72,17 @@ s = m:section(NamedSection, arg[1], "nodes", "")
s.addremove = false
s.dynamic = false
share = s:option(DummyValue, "share_url", translate("Share Current"))
share.rawhtml = true
share.template = "passwall/node_list/link_share_man"
share.value = arg[1]
remarks = s:option(Value, "remarks", translate("Node Remarks"))
remarks.default = translate("Remarks")
remarks.rmempty = false
type = s:option(ListValue, "type", translate("Type"))
if ((is_installed("redsocks2") or is_finded("redsocks2")) or
(is_installed("ipt2socks") or is_finded("ipt2socks"))) then
if is_installed("ipt2socks") or is_finded("ipt2socks") then
type:value("Socks", translate("Socks"))
end
if is_finded("ss-redir") then
@ -145,8 +148,7 @@ for k, v in pairs(nodes_table) do default_node:value(v.id, v.remarks) end
default_node:depends("protocol", "_shunt")
-- Brook协议
brook_protocol = s:option(ListValue, "brook_protocol",
translate("Brook Protocol"))
brook_protocol = s:option(ListValue, "brook_protocol", translate("Brook Protocol"))
brook_protocol:value("client", translate("Brook"))
brook_protocol:value("wsclient", translate("WebSocket"))
brook_protocol:depends("type", "Brook")
@ -213,28 +215,23 @@ password:depends("protocol", "http")
password:depends("protocol", "socks")
password:depends("protocol", "shadowsocks")
ss_encrypt_method = s:option(ListValue, "ss_encrypt_method",
translate("Encrypt Method"))
ss_encrypt_method = s:option(ListValue, "ss_encrypt_method", translate("Encrypt Method"))
for a, t in ipairs(ss_encrypt_method_list) do ss_encrypt_method:value(t) end
ss_encrypt_method:depends("type", "SS")
ssr_encrypt_method = s:option(ListValue, "ssr_encrypt_method",
translate("Encrypt Method"))
ssr_encrypt_method = s:option(ListValue, "ssr_encrypt_method", translate("Encrypt Method"))
for a, t in ipairs(ssr_encrypt_method_list) do ssr_encrypt_method:value(t) end
ssr_encrypt_method:depends("type", "SSR")
security = s:option(ListValue, "security",
translate("Encrypt Method"))
security = s:option(ListValue, "security", translate("Encrypt Method"))
for a, t in ipairs(security_list) do security:value(t) end
security:depends("protocol", "vmess")
v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method",
translate("Encrypt Method"))
v_ss_encrypt_method = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method"))
for a, t in ipairs(v_ss_encrypt_method_list) do v_ss_encrypt_method:value(t) end
v_ss_encrypt_method:depends("protocol", "shadowsocks")
ss_ota = s:option(Flag, "ss_ota", translate("OTA"), translate(
"When OTA is enabled, V2Ray will reject connections that are not OTA enabled. This option is invalid when using AEAD encryption."))
ss_ota = s:option(Flag, "ss_ota", translate("OTA"), translate("When OTA is enabled, V2Ray will reject connections that are not OTA enabled. This option is invalid when using AEAD encryption."))
ss_ota.default = "0"
ss_ota:depends("protocol", "shadowsocks")
@ -258,8 +255,7 @@ timeout.default = 300
timeout:depends("type", "SS")
timeout:depends("type", "SSR")
tcp_fast_open = s:option(ListValue, "tcp_fast_open", translate("TCP Fast Open"),
translate("Need node support required"))
tcp_fast_open = s:option(ListValue, "tcp_fast_open", translate("TCP Fast Open"), translate("Need node support required"))
tcp_fast_open:value("false")
tcp_fast_open:value("true")
tcp_fast_open:depends("type", "SS")
@ -278,9 +274,7 @@ ss_plugin_opts:depends("ss_plugin", "v2ray-plugin")
ss_plugin_opts:depends("ss_plugin", "obfs-local")
use_kcp = s:option(Flag, "use_kcp", translate("Use Kcptun"),
"<span style='color:red'>" .. translate(
"Please confirm whether the Kcptun is installed. If not, please go to Rule Update download installation.") ..
"</span>")
"<span style='color:red'>" .. translate("Please confirm whether the Kcptun is installed. If not, please go to Rule Update download installation.") .. "</span>")
use_kcp.default = 0
use_kcp:depends("type", "SS")
use_kcp:depends("type", "SSR")
@ -294,34 +288,27 @@ kcp_port = s:option(Value, "kcp_port", translate("Kcptun Port"))
kcp_port.datatype = "port"
kcp_port:depends("use_kcp", "1")
kcp_opts = s:option(TextValue, "kcp_opts", translate("Kcptun Config"),
translate(
"--crypt aes192 --key abc123 --mtu 1350 --sndwnd 128 --rcvwnd 1024 --mode fast"))
kcp_opts.placeholder =
"--crypt aes192 --key abc123 --mtu 1350 --sndwnd 128 --rcvwnd 1024 --mode fast"
kcp_opts = s:option(TextValue, "kcp_opts", translate("Kcptun Config"), translate("--crypt aes192 --key abc123 --mtu 1350 --sndwnd 128 --rcvwnd 1024 --mode fast"))
kcp_opts.placeholder = "--crypt aes192 --key abc123 --mtu 1350 --sndwnd 128 --rcvwnd 1024 --mode fast"
kcp_opts:depends("use_kcp", "1")
vmess_id = s:option(Value, "vmess_id", translate("ID"))
vmess_id.password = true
vmess_id:depends("protocol", "vmess")
alter_id = s:option(Value, "alter_id",
translate("Alter ID"))
alter_id = s:option(Value, "alter_id", translate("Alter ID"))
alter_id:depends("protocol", "vmess")
vmess_level =
s:option(Value, "vmess_level", translate("User Level"))
vmess_level = s:option(Value, "vmess_level", translate("User Level"))
vmess_level.default = 1
vmess_level:depends("protocol", "vmess")
stream_security = s:option(ListValue, "stream_security",
translate("Transport Layer Encryption"),
translate(
'Whether or not transport layer encryption is enabled, the supported options are "none" for unencrypted (default) and "TLS" for using TLS.'))
stream_security = s:option(ListValue, "stream_security", translate("Transport Layer Encryption"), translate('Whether or not transport layer encryption is enabled, the supported options are "none" for unencrypted (default) and "TLS" for using TLS.'))
stream_security:value("none", "none")
stream_security:value("tls", "tls")
stream_security.default = "tls"
stream_security:depends("protocol", "vmess")
stream_security:depends("protocol", "socks")
stream_security:depends("protocol", "shadowsocks")
stream_security:depends("type", "Trojan")
stream_security:depends("type", "Trojan-Go")
@ -333,13 +320,11 @@ stream_security.validate = function(self, value)
end
-- [[ TLS部分 ]] --
tls_sessionTicket = s:option(Flag, "tls_sessionTicket", translate("Session Ticket"))
tls_sessionTicket.default = "0"
tls_sessionTicket:depends("stream_security", "tls")
-- [[ Trojan TLS ]]--
trojan_force_fp = s:option(ListValue, "fingerprint", translate("Finger Print"))
for a, t in ipairs(force_fp) do trojan_force_fp:value(t) end
trojan_force_fp.default = "firefox"
@ -348,13 +333,11 @@ trojan_force_fp:depends({ type = "Trojan-Go", stream_security = "tls" })
tls_serverName = s:option(Value, "tls_serverName", translate("Domain"))
tls_serverName:depends("stream_security", "tls")
tls_allowInsecure = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate(
"Whether unsafe connections are allowed. When checked, V2Ray does not check the validity of the TLS certificate provided by the remote host."))
tls_allowInsecure = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, V2Ray does not check the validity of the TLS certificate provided by the remote host."))
tls_allowInsecure.default = "0"
tls_allowInsecure:depends("stream_security", "tls")
-- [[ Trojan Cert ]]--
trojan_cert_path = s:option(Value, "trojan_cert_path", translate("Trojan Cert Path"))
trojan_cert_path.default = ""
trojan_cert_path:depends({ stream_security = "tls", tls_allowInsecure = false })
@ -397,12 +380,13 @@ transport:value("h2", "HTTP/2")
transport:value("ds", "DomainSocket")
transport:value("quic", "QUIC")
transport:depends("protocol", "vmess")
transport:depends("protocol", "socks")
transport:depends("protocol", "shadowsocks")
--[[
ss_transport = s:option(ListValue, "ss_transport", translate("Transport"))
ss_transport:value("ws", "WebSocket")
ss_transport:value("h2", "HTTP/2")
ss_transport:value("ws", "WebSocket")
ss_transport:value("h2+ws", "HTTP/2 & WebSocket")
ss_transport:depends("protocol", "shadowsocks")
]]--
@ -410,27 +394,22 @@ ss_transport:depends("protocol", "shadowsocks")
-- [[ TCP部分 ]]--
-- TCP伪装
tcp_guise = s:option(ListValue, "tcp_guise",
translate("Camouflage Type"))
tcp_guise = s:option(ListValue, "tcp_guise", translate("Camouflage Type"))
tcp_guise:value("none", "none")
tcp_guise:value("http", "http")
tcp_guise:depends("transport", "tcp")
-- HTTP域名
tcp_guise_http_host = s:option(DynamicList, "tcp_guise_http_host",
translate("HTTP Host"))
tcp_guise_http_host = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host"))
tcp_guise_http_host:depends("tcp_guise", "http")
-- HTTP路径
tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path",
translate("HTTP Path"))
tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path"))
tcp_guise_http_path:depends("tcp_guise", "http")
-- [[ mKCP部分 ]]--
mkcp_guise = s:option(ListValue, "mkcp_guise",
translate("Camouflage Type"), translate(
'<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
mkcp_guise = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('<br />none: default, no masquerade, data sent is packets with no characteristics.<br />srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).<br />utp: packets disguised as uTP will be recognized as bittorrent downloaded data.<br />wechat-video: packets disguised as WeChat video calls.<br />dtls: disguised as DTLS 1.2 packet.<br />wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)'))
for a, t in ipairs(header_type_list) do mkcp_guise:value(t) end
mkcp_guise:depends("transport", "mkcp")
@ -440,28 +419,22 @@ mkcp_mtu:depends("transport", "mkcp")
mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI"))
mkcp_tti:depends("transport", "mkcp")
mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity",
translate("KCP uplinkCapacity"))
mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity"))
mkcp_uplinkCapacity:depends("transport", "mkcp")
mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity",
translate("KCP downlinkCapacity"))
mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity"))
mkcp_downlinkCapacity:depends("transport", "mkcp")
mkcp_congestion = s:option(Flag, "mkcp_congestion",
translate("KCP Congestion"))
mkcp_congestion = s:option(Flag, "mkcp_congestion", translate("KCP Congestion"))
mkcp_congestion:depends("transport", "mkcp")
mkcp_readBufferSize = s:option(Value, "mkcp_readBufferSize",
translate("KCP readBufferSize"))
mkcp_readBufferSize = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize"))
mkcp_readBufferSize:depends("transport", "mkcp")
mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize",
translate("KCP writeBufferSize"))
mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize"))
mkcp_writeBufferSize:depends("transport", "mkcp")
-- [[ WebSocket部分 ]]--
ws_host = s:option(Value, "ws_host", translate("WebSocket Host"))
ws_host:depends("transport", "ws")
ws_host:depends("ss_transport", "ws")
@ -475,7 +448,6 @@ ws_path:depends("trojan_transport", "h2+ws")
ws_path:depends("trojan_transport", "ws")
-- [[ HTTP/2部分 ]]--
h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host"))
h2_host:depends("transport", "h2")
h2_host:depends("ss_transport", "h2")
@ -489,30 +461,24 @@ h2_path:depends("trojan_transport", "h2+ws")
h2_path:depends("trojan_transport", "h2")
-- [[ DomainSocket部分 ]]--
ds_path = s:option(Value, "ds_path", "Path", translate(
"A legal file path. This file must not exist before running V2Ray."))
ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running V2Ray."))
ds_path:depends("transport", "ds")
-- [[ QUIC部分 ]]--
quic_security = s:option(ListValue, "quic_security",
translate("Encrypt Method"))
quic_security = s:option(ListValue, "quic_security", translate("Encrypt Method"))
quic_security:value("none")
quic_security:value("aes-128-gcm")
quic_security:value("chacha20-poly1305")
quic_security:depends("transport", "quic")
quic_key = s:option(Value, "quic_key",
translate("Encrypt Method") .. translate("Key"))
quic_key = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key"))
quic_key:depends("transport", "quic")
quic_guise = s:option(ListValue, "quic_guise",
translate("Camouflage Type"))
quic_guise = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
for a, t in ipairs(header_type_list) do quic_guise:value(t) end
quic_guise:depends("transport", "quic")
-- [[ Trojan-Go Shadowsocks2 ]] --
ss_aead = s:option(Flag, "ss_aead", translate("Shadowsocks2"))
ss_aead:depends("type", "Trojan-Go")
ss_aead.default = "0"
@ -540,33 +506,24 @@ mux_concurrency:depends("mux", "1")
-- [[ 当作为TCP节点时是否同时开启socks代理 ]]--
--[[
tcp_socks = s:option(Flag, "tcp_socks", translate("TCP Open Socks"),
translate(
"When using this TCP node, whether to open the socks proxy at the same time"))
tcp_socks = s:option(Flag, "tcp_socks", translate("TCP Open Socks"), translate("When using this TCP node, whether to open the socks proxy at the same time"))
tcp_socks.default = 0
tcp_socks:depends("type", "V2ray")
tcp_socks_port = s:option(Value, "tcp_socks_port",
"Socks " .. translate("Port"),
translate("Do not conflict with other ports"))
tcp_socks_port = s:option(Value, "tcp_socks_port", "Socks " .. translate("Port"), translate("Do not conflict with other ports"))
tcp_socks_port.datatype = "port"
tcp_socks_port.default = 1080
tcp_socks_port:depends("tcp_socks", "1")
tcp_socks_auth = s:option(ListValue, "tcp_socks_auth",
translate("Socks for authentication"),
translate(
'Socks protocol authentication, support anonymous and password.'))
tcp_socks_auth = s:option(ListValue, "tcp_socks_auth", translate("Socks for authentication"), translate('Socks protocol authentication, support anonymous and password.'))
tcp_socks_auth:value("noauth", translate("anonymous"))
tcp_socks_auth:value("password", translate("User Password"))
tcp_socks_auth:depends("tcp_socks", "1")
tcp_socks_auth_username = s:option(Value, "tcp_socks_auth_username",
"Socks " .. translate("Username"))
tcp_socks_auth_username = s:option(Value, "tcp_socks_auth_username", "Socks " .. translate("Username"))
tcp_socks_auth_username:depends("tcp_socks_auth", "password")
tcp_socks_auth_password = s:option(Value, "tcp_socks_auth_password",
"Socks " .. translate("Password"))
tcp_socks_auth_password = s:option(Value, "tcp_socks_auth_password", "Socks " .. translate("Password"))
tcp_socks_auth_password:depends("tcp_socks_auth", "password")
--]]

View File

@ -30,11 +30,6 @@ function gen_config(user)
udp_timeout = 60,
disable_http_check = true,
tcp = {
mux = (user.mux == "1") and {
enabled = true,
concurrency = tonumber(user.mux_concurrency),
idle_timeout = 60,
} or nil,
transport_plugin = user.stream_security == "none" and user.trojan_transport == "original" and {
enabled = user.plugin_type ~= nil,
type = user.plugin_type or "plaintext",

View File

@ -14,7 +14,7 @@ function gen_config(user)
for i = 1, #user.vmess_id do
clients[i] = {
id = user.vmess_id[i],
level = tonumber(user.vmess_level),
level = tonumber(user.level),
alterId = tonumber(user.alter_id)
}
end
@ -42,12 +42,21 @@ function gen_config(user)
}
elseif user.protocol == "shadowsocks" then
settings = {
method = user.ss_encrypt_method,
method = user.v_ss_encrypt_method,
password = user.password,
level = tonumber(user.vmess_level) or 1,
level = tonumber(user.level) or 1,
network = user.ss_network or "TCP,UDP",
ota = (user.ss_ota == '1') and true or false
}
elseif user.protocol == "mtproto" then
settings = {
users = {
{
level = tonumber(user.level) or 1,
secret = (user.password == nil) and "" or user.password
}
}
}
end
if user.accept_lan == nil or user.accept_lan == "0" then
@ -74,7 +83,7 @@ function gen_config(user)
concurrency = (node.mux_concurrency) and tonumber(node.mux_concurrency) or 8
},
-- 底层传输配置
streamSettings = (node.protocol == "vmess") and {
streamSettings = {
network = node.transport,
security = node.stream_security,
tlsSettings = (node.stream_security == "tls") and {
@ -122,7 +131,7 @@ function gen_config(user)
key = node.quic_key,
header = {type = node.quic_guise}
} or nil
} or nil,
},
settings = {
vnext = (node.protocol == "vmess") and {
{
@ -132,7 +141,7 @@ function gen_config(user)
{
id = node.vmess_id,
alterId = tonumber(node.alter_id),
level = tonumber(node.vmess_level),
level = tonumber(node.level),
security = node.security
}
}
@ -162,7 +171,7 @@ function gen_config(user)
local config = {
log = {
-- error = "/var/log/v2ray.log",
-- error = "/var/etc/passwall_server/log/" .. user[".name"] .. ".log",
loglevel = "warning"
},
-- 传入连接
@ -171,14 +180,12 @@ function gen_config(user)
listen = (user.bind_local == "1") and "127.0.0.1" or nil,
port = tonumber(user.port),
protocol = user.protocol,
-- 底层传输配置
settings = settings,
streamSettings = (user.protocol == "vmess") and {
streamSettings = {
network = user.transport,
security = (user.stream_security == 'tls') and "tls" or "none",
tlsSettings = (user.stream_security == 'tls') and {
disableSessionResumption = user.sessionTicket ~= "1" and true or false,
-- serverName = (user.tls_serverName),
allowInsecure = false,
disableSystemRoot = false,
certificates = {
@ -209,22 +216,22 @@ function gen_config(user)
writeBufferSize = tonumber(user.mkcp_writeBufferSize),
header = {type = user.mkcp_guise}
} or nil,
wsSettings = (user.transport == "ws") and
{
headers = (user.ws_host) and {Host = user.ws_host} or
nil,
path = user.ws_path
} or nil,
httpSettings = (user.transport == "h2") and
{path = user.h2_path, host = user.h2_host} or nil,
dsSettings = (user.transport == "ds") and
{path = user.ds_path} or nil,
wsSettings = (user.transport == "ws") and {
headers = (user.ws_host) and {Host = user.ws_host} or nil,
path = user.ws_path
} or nil,
httpSettings = (user.transport == "h2") and {
path = user.h2_path, host = user.h2_host
} or nil,
dsSettings = (user.transport == "ds") and {
path = user.ds_path
} or nil,
quicSettings = (user.transport == "quic") and {
security = user.quic_security,
key = user.quic_key,
header = {type = user.quic_guise}
} or nil
} or nil
}
}
},
-- 传出连接

View File

@ -82,17 +82,17 @@ if is_installed("trojan-go") or is_finded("trojan-go") then
end
protocol = s:option(ListValue, "protocol", translate("Protocol"))
protocol:value("vmess", translate("Vmess"))
protocol:value("http", translate("HTTP"))
protocol:value("socks", translate("Socks"))
protocol:value("shadowsocks", translate("Shadowsocks"))
protocol:value("vmess", "Vmess")
protocol:value("http", "HTTP")
protocol:value("socks", "Socks")
protocol:value("shadowsocks", "Shadowsocks")
protocol:value("mtproto", "MTProto")
protocol:depends("type", "V2ray")
-- Brook协议
brook_protocol = s:option(ListValue, "brook_protocol",
translate("Brook Protocol"))
brook_protocol:value("server", translate("Brook"))
brook_protocol:value("wsserver", translate("WebSocket"))
brook_protocol = s:option(ListValue, "brook_protocol", translate("Brook Protocol"))
brook_protocol:value("server", "Brook")
brook_protocol:value("wsserver", "WebSocket")
brook_protocol:depends("type", "Brook")
brook_tls = s:option(Flag, "brook_tls", translate("Use TLS"))
@ -101,14 +101,6 @@ brook_tls:depends("brook_protocol", "wsserver")
port = s:option(Value, "port", translate("Port"))
port.datatype = "port"
port.rmempty = false
port:depends("type", "SSR")
port:depends({ type = "V2ray", protocol = "vmess" })
port:depends({ type = "V2ray", protocol = "http" })
port:depends({ type = "V2ray", protocol = "socks" })
port:depends({ type = "V2ray", protocol = "shadowsocks" })
port:depends("type", "Brook")
port:depends("type", "Trojan")
port:depends("type", "Trojan-Go")
username = s:option(Value, "username", translate("Username"))
username:depends("protocol", "http")
@ -123,6 +115,7 @@ password:depends("type", "Trojan-Go")
password:depends({ type = "V2ray", protocol = "http" })
password:depends({ type = "V2ray", protocol = "socks" })
password:depends({ type = "V2ray", protocol = "shadowsocks" })
password:depends({ type = "V2ray", protocol = "mtproto" })
ssr_encrypt_method = s:option(ListValue, "ssr_encrypt_method", translate("Encrypt Method"))
for a, t in ipairs(ssr_encrypt_method_list) do ssr_encrypt_method:value(t) end
@ -185,21 +178,20 @@ alter_id = s:option(Value, "alter_id", translate("Alter ID"))
alter_id.default = 16
alter_id:depends({ type = "V2ray", protocol = "vmess" })
vmess_level = s:option(Value, "vmess_level", translate("User Level"))
vmess_level.default = 1
vmess_level:depends({ type = "V2ray", protocol = "vmess" })
vmess_level:depends({ type = "V2ray", protocol = "shadowsocks" })
level = s:option(Value, "level", translate("User Level"))
level.default = 1
level:depends({ type = "V2ray", protocol = "vmess" })
level:depends({ type = "V2ray", protocol = "shadowsocks" })
level:depends({ type = "V2ray", protocol = "mtproto" })
stream_security = s:option(ListValue, "stream_security",
translate("Transport Layer Encryption"),
translate(
'Whether or not transport layer encryption is enabled, the supported options are "none" for unencrypted (default) and "TLS" for using TLS.'))
stream_security = s:option(ListValue, "stream_security", translate("Transport Layer Encryption"), translate('Whether or not transport layer encryption is enabled, the supported options are "none" for unencrypted (default) and "TLS" for using TLS.'))
stream_security:value("none", "none")
stream_security:value("tls", "tls")
stream_security.default = "tls"
stream_security:depends({ type = "V2ray", protocol = "vmess", transport = "ws" })
stream_security:depends({ type = "V2ray", protocol = "vmess", transport = "h2" })
stream_security:depends("protocol", "shadowsocks")
stream_security:depends({ type = "V2ray", protocol = "socks" })
stream_security:depends({ type = "V2ray", protocol = "shadowsocks" })
stream_security:depends("type", "Trojan-Go")
-- [[ TLS部分 ]] --
@ -221,7 +213,9 @@ transport:value("ws", "WebSocket")
transport:value("h2", "HTTP/2")
transport:value("ds", "DomainSocket")
transport:value("quic", "QUIC")
transport:depends("protocol", "vmess")
stream_security:depends({ type = "V2ray", protocol = "vmess" })
stream_security:depends({ type = "V2ray", protocol = "socks" })
stream_security:depends({ type = "V2ray", protocol = "shadowsocks" })
trojan_transport = s:option(ListValue, "trojan_transport", translate("Transport"))
trojan_transport:value("original", "Original")
@ -343,7 +337,7 @@ quic_guise = s:option(ListValue, "quic_guise", translate("Camouflage Type"))
for a, t in ipairs(header_type_list) do quic_guise:value(t) end
quic_guise:depends("transport", "quic")
remote_enable = s:option(Flag, "remote_enable", translate("Enable Remote"),translate("You can forward to Nginx/Caddy/V2ray WebSocket and more."))
remote_enable = s:option(Flag, "remote_enable", translate("Enable Remote"), translate("You can forward to Nginx/Caddy/V2ray WebSocket and more."))
remote_enable.default = "1"
remote_enable.rmempty = false
remote_enable:depends("type", "Trojan")
@ -372,14 +366,6 @@ ss_aead_pwd.password = true
ss_aead_pwd.rmempty = false
ss_aead_pwd:depends("ss_aead", "1")
-- [[ Mux ]]--
mux = s:option(Flag, "mux", translate("Mux"))
mux_concurrency = s:option(Value, "mux_concurrency",
translate("Mux Concurrency"))
mux_concurrency.default = 8
mux_concurrency:depends("mux", "1")
local nodes_table = {}
uci:foreach("passwall", "nodes", function(e)
if e.type and e.type == "V2ray" and e.remarks and e.address and e.port then

View File

@ -23,6 +23,41 @@ local dsp = require "luci.dispatcher"
<script type="text/javascript">
//<![CDATA[
function link_clear_temp() {
if (confirm('<%:Are you sure to clear all cached links?%>') == true){
XHR.get('<%=dsp.build_url("admin/vpn/passwall/link_clear_temp")%>', null,
function(x, data) {
if(x && x.status == 200) {
window.location.href = '<%=dsp.build_url("admin/vpn/passwall/node_list")%>';
}
else {
alert("<%:Error%>");
}
});
}
}
function link_load_temp() {
var inputer = document.getElementById("nodes_link");
XHR.get('<%=dsp.build_url("admin/vpn/passwall/link_load_temp")%>', null,
function(x, data) {
if(x.readyState === 4 && x.status === 200) {
try {
res = JSON.parse(x.responseText);
}
catch(e) {
res = null;
}
if(res !== null && typeof(res) === "object") {
alert(res.counter);
if(res.data) inputer.value = res.data;
}
} else {
alert("<%:Error%>");
}
});
}
function ajax_add_node(link) {
if (link) {
XHR.get('<%=dsp.build_url("admin/vpn/passwall/link_add_node")%>', {
@ -51,7 +86,7 @@ local dsp = require "luci.dispatcher"
function add_node() {
var nodes_link = document.getElementById("nodes_link").value;
if (nodes_link.trim() != "") {
if (nodes_link.indexOf("ss://") == 0 || nodes_link.indexOf("ssr://") == 0 || nodes_link.indexOf("vmess://") == 0 || nodes_link.indexOf("trojan://") == 0) {
if (nodes_link.indexOf("ss://") == 0 || nodes_link.indexOf("ssr://") == 0 || nodes_link.indexOf("vmess://") == 0 || nodes_link.indexOf("trojan://") == 0 || nodes_link.indexOf("trojan-go://") == 0) {
ajax_add_node(nodes_link);
}
else {
@ -88,9 +123,10 @@ local dsp = require "luci.dispatcher"
</div>
</div>
<div class="cbi-value">
<label class="cbi-value-title"></label>
<div class="cbi-value-field">
<input class="cbi-button cbi-button-add" type="button" onclick="add_node()" value="<%:Add%>" />
<input class="cbi-button cbi-button-apply" type="button" onclick="link_load_temp()" value="<%:Load %><%:Cached%>" />
<input class="cbi-button cbi-button-refresh" type="button" onclick="link_clear_temp()" value="<%:Clear %><%:Cached%>" />
<input class="cbi-button cbi-button-remove" type="button" onclick="close_add_link_div()" value="<%:Close%>" />
</div>
</div>

View File

@ -0,0 +1,604 @@
<%+cbi/valueheader%>
<%
local dsp = require "luci.dispatcher"
-%>
<script type="text/javascript">//<![CDATA[
function padright(str, cnt, pad) {
return str + Array(cnt + 1).join(pad);
}
function b64EncodeUnicode(str) {
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
function b64encutf8safe(str) {
return b64EncodeUnicode(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '');
}
function b64DecodeUnicode(str) {
return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
}
function b64decutf8safe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return b64DecodeUnicode(str);
}
function b64encsafe(str) {
return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, '')
}
function b64decsafe(str) {
var l;
str = str.replace(/-/g, "+").replace(/_/g, "/");
l = str.length;
l = (4 - l % 4) % 4;
if (l)
str = padright(str, l, "=");
return atob(str);
}
function dictvalue(d, key) {
var v = d[key];
if (typeof(v) === 'undefined' || v === '')
return '';
return b64decsafe(v);
}
function parseNodeUrl(url) {
var m = url.match(/^(([^:\/?#]+:)?(?:\/\/((?:([^\/?#:]*)([^\/?#:]*)@)?([^\/?#:]*)(?::([^\/?#:]*))?)))?([^?#]*)(\?[^#]*)?(#.*)?$/),
r = {
hash: m[10] || "", // #asd
host: m[3] || "", // localhost:257
hostname: m[6] || "", // localhost
href: m[0] || "", // http://username:password@localhost:257/deploy/?asd=asd#asd
origin: m[1] || "", // http://username:password@localhost:257
pathname: m[8] || (m[1] ? "/" : ""), // /deploy/
port: m[7] || "", // 257
protocol: m[2] || "", // http:
search: m[9] || "", // ?asd=asd
passwd: m[4] || "", // username
removed: m[5] || "" // password
};
if (r.protocol.length === 2) {
r.protocol = "file:///" + r.protocol.toUpperCase();
r.origin = r.protocol + "//" + r.host;
}
r.href = r.origin + r.pathname + r.search + r.hash;
return m && r;
}
function link_append_temp(link) {
XHR.get('<%=dsp.build_url("admin/vpn/passwall/link_append_temp")%>', {
'link': link
},
function(x, data) {
if(x.readyState === 4 && x.status === 200) {
try {
res = JSON.parse(x.responseText);
}
catch(e) {
res = null;
}
if(res !== null && typeof(res) === "object") {
alert(res.counter);
}
} else {
alert("<%:Error%>");
}
});
}
function buildUrl(btn, urlname, sid) {
var opt = {
base: "cbid.passwall",
fallback: "dummy",
get: function(opt) {
var id = this.base + "." + opt;
var obj = document.getElementsByName(id) || document.getElementById(id);
if (obj) {
if (obj.length === 1) obj = obj[0];
return obj;
} else {
alert("<%:Faltal on get option, please help in debug: %>" + opt);
return document.getElementById(this.fallback);
}
},
getlist: function(opt) {
var id = this.base + "." + opt;
var objs = document.getElementsByName(id) || document.getElementsByClassName(id);
var ret = [];
if (objs) {
for (var i = 0; i < objs.length; i++) {
ret[i] = objs[i].value;
}
} else {
alert("<%:Faltal on get option, please help in debug: %>" + opt);
}
return ret;
},
query: function(param, src, tval = "1", fval = "0") {
var ret = "&" + param + "=";
var obj = this.get(src);
if (obj) {
if (obj.type === "checkbox") {
return ret + (obj.checked === true ? tval : fval);
} else {
return ret + encodeURIComponent(obj.value);
}
}
return ""
}
}
var s = document.getElementById(urlname + "-status");
if (!s) {
alert("Never");
return false;
}
opt.base = "cbid.passwall." + sid;
opt.fallback = urlname + "-dummy";
var v_type = opt.get("type").value;
var v_alias = opt.get("remarks");
var url = null;
if (v_type === "SSR") {
var v_server = opt.get("address");
var v_port = opt.get("port");
var v_protocol = opt.get("ssr_protocol");
var v_method = opt.get("ssr_encrypt_method");
var v_obfs = opt.get("obfs");
var v_password = opt.get("password");
var v_obfs_param = opt.get("obfs_param");
var v_protocol_param = opt.get("protocol_param");
var ssr_str = v_server.value + ":" +
v_port.value + ":" +
v_protocol.value + ":" +
v_method.value + ":" +
v_obfs.value + ":" +
b64encsafe(v_password.value) +
"/?obfsparam=" + b64encsafe(v_obfs_param.value) +
"&protoparam=" + b64encsafe(v_protocol_param.value) +
"&remarks=" + b64encutf8safe(v_alias.value);
url = b64encsafe(ssr_str);
} else if (v_type === "Trojan" || v_type === "Trojan-Go") {
var v_password = opt.get("password");
var v_server = opt.get("address");
var v_port = opt.get("port");
url = encodeURIComponent(v_password.value) +
"@" + v_server.value +
":" + v_port.value + "/?";
if (opt.get("stream_security").value === "tls") {
url += "tls=1";
url += opt.query("sni", "tls_serverName");
url += opt.query("allowinsecure", "tls_allowInsecure");
} else if (v_type === "Trojan-Go") {
url += "tls=0";
}
if (v_type === "Trojan-Go") {
if (opt.get("stream_security").value === "none" && opt.get("trojan_transport").value === "original") {
var plugin = {};
plugin.type = opt.get("plugin_type").value;
if (plugin.type !== "plaintext") {
plugin.command = opt.get("plugin_cmd").value;
plugin.option = opt.get("plugin_option").value;
plugin.arg = opt.getlist("plugin_arg");
}
url += "&plugin=" + encodeURIComponent(JSON.stringify(plugin));
}
url += opt.query("type", "trojan_transport");
var ws = (opt.get("trojan_transport").value.indexOf("ws") !== -1);
var h2 = (opt.get("trojan_transport").value.indexOf("h2") !== -1);
if (ws) {
url += opt.query("host", "ws_host");
url += opt.query("path", "ws_path");
} else if (h2) {
url += opt.query("host", "h2_host");
url += opt.query("path", "h2_path");
}
var enc = "none";
if (opt.get("ss_aead").checked === true) {
enc = "ss;" +
opt.get("ss_aead_method").value +
":" + opt.get("ss_aead_pwd").value;
}
url += "&encryption=" + encodeURIComponent(enc);
url += opt.query("mux", "mux");
}
url += "#" + encodeURI(v_alias.value);
}
if (url) {
url = v_type.toLowerCase() + "://" + url;
link_append_temp(url);
var textarea = document.createElement("textarea");
textarea.textContent = url;
textarea.style.position = "fixed";
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand("copy"); // Security exception may be thrown by some browsers.
s.innerHTML = "<font color='green'><%:Share to clipboard successfully, copy to batch mode still: %></font>";
} catch (ex) {
s.innerHTML = "<font color='red'><%:Unable share to clipboard, copy to batch mode still: %></font>";
} finally {
document.body.removeChild(textarea);
}
} else {
alert("<%:Not a supported scheme:%> " + v_type);
}
return false;
}
function fromUrl(btn, urlname, sid) {
var opt = {
base: 'cbid.passwall',
fallback: 'dummy',
get: function(opt) {
var obj;
var id = this.base + '.' + opt;
obj = document.getElementsByName(id)[0] || document.getElementById(id);
if (obj) {
return obj;
} else {
alert('<%:Faltal on get option, please help in debug: %>' + opt);
return document.getElementById(this.fallback);
}
},
set: function(opt, val) {
var obj;
obj = this.get(opt)
if (obj) {
if (obj.type === 'checkbox') {
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
obj.checked = val;
obj.dispatchEvent(event);
} else {
obj.value = val;
}
} else {
alert('<%:Faltal on set option, please help in debug: %>' + opt + ' = ' + val);
}
},
setlist: function(opt, vlist) {
var id = this.base + "." + opt;
var objs = document.getElementsByName(id) || document.getElementsByClassName(id);
if (objs) {
var values = "";
for (var i = 0; i < vlist.length; i++) {
values += vlist[i] + ", ";
}
alert("Manually input the option:\n" + opt + "s:\n[" + values + "]");
} else {
alert("<%:Faltal on set option, please help in debug: %>" + opt);
}
}
}
var s = document.getElementById(urlname + '-status');
if (!s) {
alert("Never");
return false;
}
opt.base = 'cbid.passwall.' + sid
opt.fallback = urlname + '-dummy';
var ssrurl = prompt('<%:Paste Share URL Here%>', '');
if (ssrurl === null || ssrurl === "") {
s.innerHTML = "<font color='red'><%:User cancelled%></font>";
return false;
}
s.innerHTML = "";
var ssu = ssrurl.split('://');
var event = document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
if (ssu[0] === "ssr") {
//var b64c = ssu[1].match(/([A-Za-z0-9_-]+)/);
var sstr = b64decsafe(ssu[1]);
var ploc = sstr.indexOf("/?");
var url0 = "", param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
console.log(param);
var ssm = url0.match(/^(.+):([^:]+):([^:]*):([^:]+):([^:]*):([^:]+)/);
if (!ssm || ssm.length < 7) {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
return false;
}
var pdict = {};
if (param.length > 2) {
var a = param.split('&');
for (var i = 0; i < a.length; i++) {
var b = a[i].split('=');
pdict[decodeURIComponent(b[0])] = decodeURIComponent(b[1] || '');
}
}
opt.set('type', "SSR");
opt.get('type').dispatchEvent(event);
opt.set('address', ssm[1]);
opt.set('port', ssm[2]);
opt.set('ssr_protocol', ssm[3]);
opt.set('ssr_encrypt_method', ssm[4]);
opt.set('obfs', ssm[5]);
opt.set('password', b64decsafe(ssm[6]));
opt.set('obfs_param', dictvalue(pdict, 'obfsparam'));
opt.set('protocol_param', dictvalue(pdict, 'protoparam'));
var rem = pdict['remarks'];
if (typeof(rem) !== 'undefined' && rem !== '' && rem.length > 0)
opt.set('remark', b64decutf8safe(rem));
} else if (ssu[0] === "ss") {
var url0 = "", param = "";
var sipIndex = ssu[1].indexOf("@");
var ploc = ssu[1].indexOf("#");
if (ploc > 0) {
url0 = ssu[1].substr(0, ploc);
param = ssu[1].substr(ploc + 1);
} else {
url0 = ssu[1];
}
console.log(param);
if (sipIndex !== -1) {
// SIP002
var userInfo = b64decsafe(url0.substr(0, sipIndex));
var temp = url0.substr(sipIndex + 1).split("/?");
var serverInfo = temp[0].split(":");
var server = serverInfo[0];
var port = serverInfo[1];
var method, password, plugin, pluginOpts;
if (temp[1]) {
var pluginInfo = decodeURIComponent(temp[1]);
var pluginIndex = pluginInfo.indexOf(";");
var pluginNameInfo = pluginInfo.substr(0, pluginIndex);
plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1)
pluginOpts = pluginInfo.substr(pluginIndex + 1);
}
var userInfoSplitIndex = userInfo.indexOf(":");
if (userInfoSplitIndex !== -1) {
method = userInfo.substr(0, userInfoSplitIndex);
password = userInfo.substr(userInfoSplitIndex + 1);
}
opt.set('type', "SS");
opt.get('type').dispatchEvent(event);
opt.set('address', server);
opt.set('port', port);
opt.set('password', password || "");
opt.set('ss_encrypt_method', method || "");
opt.set('ss_plugin', plugin || "");
opt.set('ss_plugin_opts', pluginOpts || "");
if (param !== undefined) {
opt.set('remark', decodeURI(param));
}
} else {
var sstr = b64decsafe(url0);
var team = sstr.split('@');
opt.set('type', "SS");
opt.get('type').dispatchEvent(event);
var part1 = team[0].split(':');
var part2 = team[1].split(':');
opt.set('address', part2[0]);
opt.set('port', part2[1]);
opt.set('password', part1[1]);
opt.set('ss_encrypt_method', part1[0]);
opt.set('ss_plugin', "");
opt.set('ss_plugin_opts', "");
if (param !== undefined) {
opt.set('remark', decodeURI(param));
}
}
} else if (ssu[0] === "trojan") {
var stype = "Trojan";
var m = parseNodeUrl(ssrurl);
console.log(m.search);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
return false;
}
var queryParam = {};
if (m.search.length > 1) {
var query = m.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]).toLowerCase()] = decodeURIComponent(params[1] || '');
}
}
if (queryParam.mux || queryParam.ws || queryParam.h2 || queryParam.ss || queryParam.plugin) {
stype = "Trojan-Go";
}
opt.set('type', stype);
opt.get('type').dispatchEvent(event);
opt.set('address', m.hostname);
opt.set('port', m.port || "443");
opt.set('password', decodeURIComponent(password));
var tls = true;
if (stype === "Trojan-Go") {
tls = queryParam.plugin === undefined;
}
if (tls === false) { alert("TODO: plugin params for trojan-go."); }
opt.set('stream_security', tls ? "tls" : "none");
opt.get('stream_security').dispatchEvent(event);
if (tls) {
opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
opt.set('tls_allowInsecure', queryParam.allowinsecure === '1');
}
var tran = 'original';
var ws = queryParam.type.indexOf('ws') !== -1;
var h2 = queryParam.type.indexOf('h2') !== -1;
if (ws && h2) {
tran = 'h2+ws'
} else {
if (ws) tran = 'ws';
if (h2) tran = 'h2';
}
opt.set('trojan_transport', 'tran');
opt.get('trojan_transport').dispatchEvent(event);
if (ws) {
opt.set('ws_host', queryParam.wshost || '');
opt.set('ws_path', queryParam.wspath || '/');
}
if (h2) {
opt.set('h2_host', queryParam.h2host || '');
opt.set('h2_path', queryParam.h2path || '/');
}
var ss = queryParam.ss === '1';
opt.set('ss_aead', ss);
if (ss) {
opt.set('ss_aead_method', queryParam.ssmethod.toUpperCase() || '');
opt.set('ss_aead_pwd', queryParam.sspasswd || '');
}
opt.set('mux', queryParam.mux === '1');
if (m.hash) {
opt.set('remark', decodeURI(m.hash.substr(1)));
}
} else if (ssu[0] === "trojan-go") {
var m = parseNodeUrl(ssrurl);
console.log(m.search);
var password = m.passwd;
if (password === "") {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>";
return false;
}
var queryParam = {};
if (m.search.length > 1) {
var query = m.search.split('?');
var queryParams = query[1];
var queryArray = queryParams.split('&');
for (i = 0; i < queryArray.length; i++) {
var params = queryArray[i].split('=');
queryParam[decodeURIComponent(params[0]).toLowerCase()] = decodeURIComponent(params[1] || '');
}
}
opt.set('type', 'Trojan-Go');
opt.get('type').dispatchEvent(event);
opt.set('address', m.hostname);
opt.set('port', m.port || "443");
opt.set('password', decodeURIComponent(password));
opt.set('stream_security', (queryParam.tls && queryParam.tls === '1') ? 'tls' : 'none');
opt.get('stream_security').dispatchEvent(event);
var plugin = queryParam.plugin !== undefined;
if (plugin) {
opt.set('trojan_transport', 'original');
opt.get('trojan_transport').dispatchEvent(event);
var plugin = JSON.parse(queryParam.plugin);
if (plugin) {
opt.set('plugin_type', plugin.type);
opt.get('plugin_type').dispatchEvent(event);
if (plugin.type !== "plaintext") {
opt.set('plugin_cmd', plugin.command);
opt.set('plugin_option', plugin.option);
opt.setlist('plugin_arg', plugin.arg);
}
} else
alert(queryParam.plugin);
}
var tls = !plugin && queryParam.tls === '1';
opt.set('stream_security', tls ? "tls" : "none");
opt.get('stream_security').dispatchEvent(event);
if (tls) {
opt.set('tls_serverName', queryParam.peer || queryParam.sni || '');
opt.set('tls_allowInsecure', queryParam.allowinsecure === '1');
}
var tran = 'original';
var or = queryParam.type === undefined || queryParam.type === 'original';
var ws = queryParam.type.indexOf('ws') !== -1;
var h2 = queryParam.type.indexOf('h2') !== -1;
if (ws && h2) {
tran = 'h2+ws'
} else {
if (ws) tran = 'ws';
if (h2) tran = 'h2';
}
opt.set('trojan_transport', tran);
opt.get('trojan_transport').dispatchEvent(event);
if (ws) {
opt.set('ws_host', queryParam.host || '');
opt.set('ws_path', queryParam.path || '/');
}
if (h2){
opt.set('h2_host', queryParam.host || '');
opt.set('h2_path', queryParam.path || '/');
}
var enc = {};
var ss = false;
if (queryParam.encryption) {
var r = queryParam.encryption.match(/^(ss);([^;:]*)[;:](.*)$/),
enc = {type: r[1], method: r[2], password: r[3]};
}
ss = enc.type === 'ss';
opt.set('ss_aead', ss);
if (ss) {
opt.set('ss_aead_method', enc.method.toUpperCase() || '');
opt.set('ss_aead_pwd', enc.password || '');
}
opt.set('mux', queryParam.mux === '1');
if (m.hash) {
opt.set('remark', decodeURI(m.hash));
}
} else if (ssu[0] === "vmess") {
var sstr = b64DecodeUnicode(ssu[1]);
var ploc = sstr.indexOf("/?");
opt.set('type', "V2ray");
opt.get('type').dispatchEvent(event);
var url0, param = "";
if (ploc > 0) {
url0 = sstr.substr(0, ploc);
param = sstr.substr(ploc + 2);
}
var ssm = JSON.parse(sstr);
opt.set('remark', ssm.ps);
opt.set('address', ssm.add);
opt.set('port', ssm.port);
opt.set('alter_id', ssm.aid);
opt.set('vmess_id', ssm.id);
opt.set('stream_security', ssm.tls === "tls" ? "tls" : "none");
opt.get('stream_security').dispatchEvent(event);
if (ssm.tls === "tls") {
opt.set('tls_serverName', ssm.host);
}
opt.set('transport', ssm.net);
opt.get('transport').dispatchEvent(event);
if (ssm.net === "tcp") {
opt.set('tcp_guise', (ssm.host && ssm.path) ? "http" : "none");
opt.get('tcp_guise').dispatchEvent(event);
if (ssm.host && ssm.path) {
opt.set('tcp_guise_http_host', ssm.host);
opt.set('tcp_guise_http_path', ssm.path);
}
} else if (ssm.net === "ws") {
opt.set('ws_host', ssm.host);
opt.set('ws_path', ssm.path);
} else if (ssm.net === "h2") {
opt.set('h2_host', ssm.host);
opt.set('h2_path', ssm.path);
} else if (ssm.net === "quic") {
opt.set('quic_security', ssm.securty);
opt.set('quic_key', ssm.key);
} else if (ssm.net === "kcp") {
opt.set('mkcp_guise', ssm.type);
opt.get('mkcp_guise').dispatchEvent(event);
}
} else {
s.innerHTML = "<font color='red'><%:Invalid Share URL Format%></font>: " + ssu[0];
return false;
}
s.innerHTML = "<font color='green'><%:Import Finished %></font>";
return false;
}
//]]></script>
<input type="test" class="hidden" id='<%=self.option%>-dummy' value="-dummy-" />
<input type="button" class="cbi-button cbi-button-apply" value='<%:From Share URL%>' onclick="return fromUrl(this, '<%=self.option%>', '<%=self.value%>')" />
<input type="button" class="cbi-button cbi-button-apply" value='<%:Build Share URL%>' onclick="return buildUrl(this, '<%=self.option%>', '<%=self.value%>')" />
<span id="<%=self.option%>-status"></span>
<%+cbi/valuefooter%>

View File

@ -370,6 +370,42 @@ msgstr "负载均衡节点列表"
msgid "Load balancing node list, <a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>document</a>"
msgstr "负载均衡节点列表,<a target='_blank' href='https://toutyrater.github.io/routing/balance2.html'>文档原理</a>"
msgid "Share Current"
msgstr "所见即所得"
msgid "From Share URL"
msgstr "导入配置信息"
msgid "Build Share URL"
msgstr "导出配置信息"
msgid "Import Finished"
msgstr "导入完成:"
msgid "Not a supported scheme:"
msgstr "不支持这种样式的:"
msgid "Invalid Share URL Format"
msgstr "无效的分享信息"
msgid "User cancelled"
msgstr "用户已取消"
msgid "Paste Share URL Here"
msgstr "在此处粘贴分享信息"
msgid "Unable share to clipboard, copy to batch mode still:"
msgstr "无法分享网址到剪贴板,复制到批量导入临时输入:"
msgid "Share to clipboard successfully, copy to batch mode still:"
msgstr "成功复制分享网址到剪贴板和批量导入临时输入:"
msgid "Faltal on get option, please help in debug:"
msgstr "代码错误,请协助捉虫:"
msgid "Faltal on set option, please help in debug:"
msgstr "代码错误,请协助捉虫:"
msgid "Address"
msgstr "地址"
@ -386,7 +422,7 @@ msgid "Finger Print"
msgstr "指纹伪造"
msgid "Shadowsocks2"
msgstr "双重加密"
msgstr "二次加密"
msgid "Encrypt Method"
msgstr "加密"