From 03460033692858d512fccac8a33afdc3a9113a45 Mon Sep 17 00:00:00 2001 From: xiaorouji <60100640+xiaorouji@users.noreply.github.com> Date: Fri, 25 Aug 2023 00:16:42 +0800 Subject: [PATCH] luci: optimization && modular --- .../cbi/passwall2/client/node_config.lua | 1026 +---------------- .../model/cbi/passwall2/client/type/brook.lua | 65 ++ .../cbi/passwall2/client/type/hysteria.lua | 115 ++ .../model/cbi/passwall2/client/type/naive.lua | 60 + .../model/cbi/passwall2/client/type/ray.lua | 739 ++++++++++++ .../cbi/passwall2/client/type/ss-rust.lua | 82 ++ .../model/cbi/passwall2/client/type/ss.lua | 83 ++ .../model/cbi/passwall2/client/type/ssr.lua | 94 ++ .../model/cbi/passwall2/client/type/tuic.lua | 158 +++ .../model/cbi/passwall2/server/type/brook.lua | 65 ++ .../cbi/passwall2/server/type/hysteria.lua | 137 +++ .../model/cbi/passwall2/server/type/ray.lua | 536 +++++++++ .../cbi/passwall2/server/type/ss-rust.lua | 71 ++ .../model/cbi/passwall2/server/type/ss.lua | 74 ++ .../model/cbi/passwall2/server/type/ssr.lua | 98 ++ .../model/cbi/passwall2/server/user.lua | 623 +--------- .../passwall2/node_list/link_share_man.htm | 502 ++++---- 17 files changed, 2699 insertions(+), 1829 deletions(-) create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/brook.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/naive.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss-rust.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ssr.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/tuic.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/brook.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss.lua create mode 100644 luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua index e99a77354..2cb20ec64 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/node_config.lua @@ -1,62 +1,13 @@ local api = require "luci.passwall2.api" local appname = api.appname local uci = api.uci +local fs = require "nixio.fs" +local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/client/type/" if not arg[1] or not uci:get(appname, arg[1]) then luci.http.redirect(api.url("node_list")) end -local ss_encrypt_method_list = { - "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", - "aes-192-ctr", "aes-256-ctr", "bf-cfb", "salsa20", "chacha20", "chacha20-ietf", - "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", - "xchacha20-ietf-poly1305" -} - -local ss_rust_encrypt_method_list = { - "plain", "none", - "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", - "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305" -} - -local ssr_encrypt_method_list = { - "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", - "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", - "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", - "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20", - "chacha20-ietf" -} - -local ssr_protocol_list = { - "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple", - "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5", - "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c", - "auth_chain_d", "auth_chain_e", "auth_chain_f" -} -local ssr_obfs_list = { - "plain", "http_simple", "http_post", "random_head", "tls_simple", - "tls1.0_session_auth", "tls1.2_ticket_auth" -} - -local v_ss_encrypt_method_list = { - "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305" -} - -local x_ss_encrypt_method_list = { - "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" -} - -local security_list = {"none", "auto", "aes-128-gcm", "chacha20-poly1305", "zero"} - -local header_type_list = { - "none", "srtp", "utp", "wechat-video", "dtls", "wireguard" -} -local encrypt_methods_ss_aead = { - "chacha20-ietf-poly1305", - "aes-128-gcm", - "aes-256-gcm", -} - m = Map(appname, translate("Node Config")) m.redirect = api.url() @@ -64,967 +15,26 @@ s = m:section(NamedSection, arg[1], "nodes", "") s.addremove = false s.dynamic = false -share = s:option(DummyValue, "passwall2", " ") -share.rawhtml = true -share.template = "passwall2/node_list/link_share_man" -share.value = arg[1] +o = s:option(DummyValue, "passwall2", " ") +o.rawhtml = true +o.template = "passwall2/node_list/link_share_man" +o.value = arg[1] -remarks = s:option(Value, "remarks", translate("Node Remarks")) -remarks.default = translate("Remarks") -remarks.rmempty = false +o = s:option(Value, "remarks", translate("Node Remarks")) +o.default = translate("Remarks") +o.rmempty = false -type = s:option(ListValue, "type", translate("Type")) -if api.is_finded("ss-redir") then - type:value("SS", translate("Shadowsocks Libev")) -end -if api.is_finded("sslocal") then - type:value("SS-Rust", translate("Shadowsocks Rust")) -end -if api.is_finded("ssr-redir") then - type:value("SSR", translate("ShadowsocksR Libev")) -end -if api.is_finded("v2ray") then - type:value("V2ray", translate("V2ray")) -end -if api.is_finded("xray") then - type:value("Xray", translate("Xray")) -end -if api.is_finded("brook") then - type:value("Brook", translate("Brook")) -end -if api.is_finded("naive") then - type:value("Naiveproxy", translate("NaiveProxy")) -end -if api.is_finded("hysteria") then - type:value("Hysteria", translate("Hysteria")) -end -if api.is_finded("tuic-client") then - type:value("TUIC", translate("TUIC")) +o = s:option(ListValue, "type", translate("Type")) + +local type_table = {} +for filename in fs.dir(types_dir) do + table.insert(type_table, filename) end +table.sort(type_table) -protocol = s:option(ListValue, "protocol", translate("Protocol")) -protocol:value("vmess", translate("Vmess")) -protocol:value("vless", translate("VLESS")) -protocol:value("http", translate("HTTP")) -protocol:value("socks", translate("Socks")) -protocol:value("shadowsocks", translate("Shadowsocks")) -protocol:value("trojan", translate("Trojan")) -protocol:value("wireguard", translate("WireGuard")) -protocol:value("_balancing", translate("Balancing")) -protocol:value("_shunt", translate("Shunt")) -protocol:value("_iface", translate("Custom Interface") .. " (Only Support Xray)") -protocol:depends("type", "V2ray") -protocol:depends("type", "Xray") - - -iface = s:option(Value, "iface", translate("Interface")) -iface.default = "eth1" -iface:depends("protocol", "_iface") - -local nodes_table = {} -local balancers_table = {} -local iface_table = {} -for k, e in ipairs(api.get_valid_nodes()) do - if e.node_type == "normal" then - nodes_table[#nodes_table + 1] = { - id = e[".name"], - remarks = e["remark"] - } - end - if e.protocol == "_balancing" then - balancers_table[#balancers_table + 1] = { - id = e[".name"], - remarks = e["remark"] - } - end - if e.protocol == "_iface" then - iface_table[#iface_table + 1] = { - id = e[".name"], - remarks = e["remark"] - } - end -end - --- 负载均衡列表 -local balancing_node = s:option(DynamicList, "balancing_node", translate("Load balancing node list"), translate("Load balancing node list, document")) -for k, v in pairs(nodes_table) do balancing_node:value(v.id, v.remarks) end -balancing_node:depends("protocol", "_balancing") - -local balancingStrategy = s:option(ListValue, "balancingStrategy", translate("Balancing Strategy")) -balancingStrategy:depends("protocol", "_balancing") -balancingStrategy:value("random") -balancingStrategy:value("leastPing") -balancingStrategy.default = "random" --- 探测地址 -local useCustomProbeUrl = s:option(Flag, "useCustomProbeUrl", translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL.")) -useCustomProbeUrl:depends("balancingStrategy", "leastPing") -local probeUrl = s:option(Value, "probeUrl", translate("Probe URL")) -probeUrl:depends("useCustomProbeUrl", true) -probeUrl.default = "https://www.google.com/generate_204" -probeUrl.description = translate("The URL used to detect the connection status.") --- 探测间隔 -local probeInterval = s:option(Value, "probeInterval", translate("Probe Interval")) -probeInterval:depends("balancingStrategy", "leastPing") -probeInterval.default = "1m" -probeInterval.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are ns, us, ms, s, m, h, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.") - --- 分流 -if #nodes_table > 0 then - o = s:option(Flag, "preproxy_enabled", translate("Preproxy")) - o:depends("protocol", "_shunt") - o = s:option(Value, "main_node", string.format('%s', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including Default) has a separate switch that controls whether this rule uses the pre-proxy or not.")) - o:depends("preproxy_enabled", "1") - for k, v in pairs(balancers_table) do - o:value(v.id, v.remarks) - end - for k, v in pairs(iface_table) do - o:value(v.id, v.remarks) - end - for k, v in pairs(nodes_table) do - o:value(v.id, v.remarks) - end - if #o.keylist > 0 then - o.default = o.keylist[1] - end -end -uci:foreach(appname, "shunt_rules", function(e) - if e[".name"] and e.remarks then - o = s:option(Value, e[".name"], string.format('* %s', api.url("shunt_rules", e[".name"]), e.remarks)) - o.default = "nil" - o:value("nil", translate("Close")) - o:value("_default", translate("Default")) - o:value("_direct", translate("Direct Connection")) - o:value("_blackhole", translate("Blackhole")) - o:depends("protocol", "_shunt") - - if #nodes_table > 0 then - for k, v in pairs(balancers_table) do - o:value(v.id, v.remarks) - end - for k, v in pairs(iface_table) do - o:value(v.id, v.remarks) - end - local pt = s:option(ListValue, e[".name"] .. "_proxy_tag", string.format('* %s', e.remarks .. " " .. translate("Preproxy"))) - pt:value("nil", translate("Close")) - pt:value("main", translate("Preproxy Node")) - pt.default = "nil" - for k, v in pairs(nodes_table) do - o:value(v.id, v.remarks) - pt:depends({ preproxy_enabled = "1", [e[".name"]] = v.id }) - end - end - end -end) - -shunt_tips = s:option(DummyValue, "shunt_tips", " ") -shunt_tips.rawhtml = true -shunt_tips.cfgvalue = function(t, n) - return string.format('%s', translate("No shunt rules? Click me to go to add.")) -end -shunt_tips:depends("protocol", "_shunt") - -local default_node = s:option(Value, "default_node", string.format('* %s', translate("Default"))) -default_node:depends("protocol", "_shunt") -default_node.default = "_direct" -default_node:value("_direct", translate("Direct Connection")) -default_node:value("_blackhole", translate("Blackhole")) - -if #nodes_table > 0 then - for k, v in pairs(balancers_table) do - default_node:value(v.id, v.remarks) - end - for k, v in pairs(iface_table) do - default_node:value(v.id, v.remarks) - end - local dpt = s:option(ListValue, "default_proxy_tag", string.format('* %s', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node.")) - dpt:value("nil", translate("Close")) - dpt:value("main", translate("Preproxy Node")) - dpt.default = "nil" - for k, v in pairs(nodes_table) do - default_node:value(v.id, v.remarks) - dpt:depends({ preproxy_enabled = "1", default_node = v.id }) - end -end - -domainStrategy = s:option(ListValue, "domainStrategy", translate("Domain Strategy")) -domainStrategy:value("AsIs") -domainStrategy:value("IPIfNonMatch") -domainStrategy:value("IPOnDemand") -domainStrategy.default = "IPOnDemand" -domainStrategy.description = "
" -domainStrategy:depends("protocol", "_shunt") - -domainMatcher = s:option(ListValue, "domainMatcher", translate("Domain matcher")) -domainMatcher:value("hybrid") -domainMatcher:value("linear") -domainMatcher:depends("protocol", "_shunt") - - --- Brook协议 -brook_protocol = s:option(ListValue, "brook_protocol", translate("Protocol")) -brook_protocol:value("client", translate("Brook")) -brook_protocol:value("wsclient", translate("WebSocket")) -brook_protocol:depends("type", "Brook") -function brook_protocol.cfgvalue(self, section) - return m:get(section, "protocol") -end -function brook_protocol.write(self, section, value) - m:set(section, "protocol", value) -end - -brook_tls = s:option(Flag, "brook_tls", translate("Use TLS")) -brook_tls:depends("brook_protocol", "wsclient") - --- Naiveproxy协议 -naiveproxy_protocol = s:option(ListValue, "naiveproxy_protocol", translate("Protocol")) -naiveproxy_protocol:value("https", translate("HTTPS")) -naiveproxy_protocol:value("quic", translate("QUIC")) -naiveproxy_protocol:depends("type", "Naiveproxy") -function naiveproxy_protocol.cfgvalue(self, section) - return m:get(section, "protocol") -end -function naiveproxy_protocol.write(self, section, value) - m:set(section, "protocol", value) -end - -address = s:option(Value, "address", translate("Address (Support Domain Name)")) -address.rmempty = false -address:depends("type", "SS") -address:depends("type", "SS-Rust") -address:depends("type", "SSR") -address:depends("type", "Brook") -address:depends("type", "Naiveproxy") -address:depends("type", "Hysteria") -address:depends("type", "TUIC") -address:depends({ type = "V2ray", protocol = "vmess" }) -address:depends({ type = "V2ray", protocol = "vless" }) -address:depends({ type = "V2ray", protocol = "http" }) -address:depends({ type = "V2ray", protocol = "socks" }) -address:depends({ type = "V2ray", protocol = "shadowsocks" }) -address:depends({ type = "V2ray", protocol = "trojan" }) -address:depends({ type = "Xray", protocol = "vmess" }) -address:depends({ type = "Xray", protocol = "vless" }) -address:depends({ type = "Xray", protocol = "http" }) -address:depends({ type = "Xray", protocol = "socks" }) -address:depends({ type = "Xray", protocol = "shadowsocks" }) -address:depends({ type = "Xray", protocol = "trojan" }) -address:depends({ type = "Xray", protocol = "wireguard" }) - ---[[ -use_ipv6 = s:option(Flag, "use_ipv6", translate("Use IPv6")) -use_ipv6.default = 0 -use_ipv6:depends("type", "SS") -use_ipv6:depends("type", "SS-Rust") -use_ipv6:depends("type", "SSR") -use_ipv6:depends("type", "Brook") -use_ipv6:depends("type", "Hysteria") -use_ipv6:depends({ type = "V2ray", protocol = "vmess" }) -use_ipv6:depends({ type = "V2ray", protocol = "vless" }) -use_ipv6:depends({ type = "V2ray", protocol = "http" }) -use_ipv6:depends({ type = "V2ray", protocol = "socks" }) -use_ipv6:depends({ type = "V2ray", protocol = "shadowsocks" }) -use_ipv6:depends({ type = "V2ray", protocol = "trojan" }) -use_ipv6:depends({ type = "Xray", protocol = "vmess" }) -use_ipv6:depends({ type = "Xray", protocol = "vless" }) -use_ipv6:depends({ type = "Xray", protocol = "http" }) -use_ipv6:depends({ type = "Xray", protocol = "socks" }) -use_ipv6:depends({ type = "Xray", protocol = "shadowsocks" }) -use_ipv6:depends({ type = "Xray", protocol = "trojan" }) ---]] - -port = s:option(Value, "port", translate("Port")) -port.datatype = "port" -port.rmempty = false -port:depends("type", "SS") -port:depends("type", "SS-Rust") -port:depends("type", "SSR") -port:depends("type", "Brook") -port:depends("type", "Naiveproxy") -port:depends("type", "Hysteria") -port:depends("type", "TUIC") -port:depends({ type = "V2ray", protocol = "vmess" }) -port:depends({ type = "V2ray", protocol = "vless" }) -port:depends({ type = "V2ray", protocol = "http" }) -port:depends({ type = "V2ray", protocol = "socks" }) -port:depends({ type = "V2ray", protocol = "shadowsocks" }) -port:depends({ type = "V2ray", protocol = "trojan" }) -port:depends({ type = "Xray", protocol = "vmess" }) -port:depends({ type = "Xray", protocol = "vless" }) -port:depends({ type = "Xray", protocol = "http" }) -port:depends({ type = "Xray", protocol = "socks" }) -port:depends({ type = "Xray", protocol = "shadowsocks" }) -port:depends({ type = "Xray", protocol = "trojan" }) -port:depends({ type = "Xray", protocol = "wireguard" }) - -hysteria_hop = s:option(Value, "hysteria_hop", translate("Additional ports for hysteria hop")) -hysteria_hop:depends("type", "Hysteria") - -username = s:option(Value, "username", translate("Username")) -username:depends("type", "Naiveproxy") -username:depends({ type = "V2ray", protocol = "http" }) -username:depends({ type = "V2ray", protocol = "socks" }) -username:depends({ type = "Xray", protocol = "http" }) -username:depends({ type = "Xray", protocol = "socks" }) - -password = s:option(Value, "password", translate("Password")) -password.password = true -password:depends("type", "SS") -password:depends("type", "SS-Rust") -password:depends("type", "SSR") -password:depends("type", "Brook") -password:depends("type", "Naiveproxy") -password:depends({ type = "V2ray", protocol = "http" }) -password:depends({ type = "V2ray", protocol = "socks" }) -password:depends({ type = "V2ray", protocol = "shadowsocks" }) -password:depends({ type = "V2ray", protocol = "trojan" }) -password:depends({ type = "Xray", protocol = "http" }) -password:depends({ type = "Xray", protocol = "socks" }) -password:depends({ type = "Xray", protocol = "shadowsocks" }) -password:depends({ type = "Xray", protocol = "trojan" }) - -hysteria_protocol = s:option(ListValue, "hysteria_protocol", translate("Protocol")) -hysteria_protocol:value("udp", "UDP") -hysteria_protocol:value("faketcp", "faketcp") -hysteria_protocol:value("wechat-video", "wechat-video") -hysteria_protocol:depends("type", "Hysteria") -function hysteria_protocol.cfgvalue(self, section) - return m:get(section, "protocol") -end -function hysteria_protocol.write(self, section, value) - m:set(section, "protocol", value) -end - -hysteria_obfs = s:option(Value, "hysteria_obfs", translate("Obfs Password")) -hysteria_obfs:depends("type", "Hysteria") - -hysteria_auth_type = s:option(ListValue, "hysteria_auth_type", translate("Auth Type")) -hysteria_auth_type:value("disable", translate("Disable")) -hysteria_auth_type:value("string", translate("STRING")) -hysteria_auth_type:value("base64", translate("BASE64")) -hysteria_auth_type:depends("type", "Hysteria") - -hysteria_auth_password = s:option(Value, "hysteria_auth_password", translate("Auth Password")) -hysteria_auth_password.password = true -hysteria_auth_password:depends("hysteria_auth_type", "string") -hysteria_auth_password:depends("hysteria_auth_type", "base64") - -hysteria_alpn = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN")) -hysteria_alpn:depends("type", "Hysteria") - -ss_encrypt_method = s:option(Value, "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") -function ss_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function ss_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -ss_rust_encrypt_method = s:option(Value, "ss_rust_encrypt_method", translate("Encrypt Method")) -for a, t in ipairs(ss_rust_encrypt_method_list) do ss_rust_encrypt_method:value(t) end -ss_rust_encrypt_method:depends("type", "SS-Rust") -function ss_rust_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function ss_rust_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -ssr_encrypt_method = s:option(Value, "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") -function ssr_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function ssr_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -security = s:option(ListValue, "security", translate("Encrypt Method")) -for a, t in ipairs(security_list) do security:value(t) end -security:depends({ type = "V2ray", protocol = "vmess" }) -security:depends({ type = "Xray", protocol = "vmess" }) - -encryption = s:option(Value, "encryption", translate("Encrypt Method")) -encryption.default = "none" -encryption:value("none") -encryption:depends({ type = "V2ray", protocol = "vless" }) -encryption:depends({ type = "Xray", protocol = "vless" }) - -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({ type = "V2ray", protocol = "shadowsocks" }) -function v_ss_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function v_ss_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -x_ss_encrypt_method = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method")) -for a, t in ipairs(x_ss_encrypt_method_list) do x_ss_encrypt_method:value(t) end -x_ss_encrypt_method:depends({ type = "Xray", protocol = "shadowsocks" }) -function x_ss_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function x_ss_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -iv_check = s:option(Flag, "iv_check", translate("IV Check")) -iv_check:depends({ type = "V2ray", protocol = "shadowsocks" }) -iv_check:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "aes-128-gcm" }) -iv_check:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "aes-256-gcm" }) -iv_check:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "chacha20-poly1305" }) -iv_check:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "xchacha20-poly1305" }) - -uot = s:option(Flag, "uot", translate("UDP over TCP"), translate("Need Xray-core or sing-box as server side.")) -uot:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-aes-128-gcm" }) -uot:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-aes-256-gcm" }) -uot:depends({ type = "Xray", protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-chacha20-poly1305" }) - -ssr_protocol = s:option(Value, "ssr_protocol", translate("Protocol")) -for a, t in ipairs(ssr_protocol_list) do ssr_protocol:value(t) end -ssr_protocol:depends("type", "SSR") -function ssr_protocol.cfgvalue(self, section) - return m:get(section, "protocol") -end -function ssr_protocol.write(self, section, value) - m:set(section, "protocol", value) -end - -protocol_param = s:option(Value, "protocol_param", translate("Protocol_param")) -protocol_param:depends("type", "SSR") - -obfs = s:option(Value, "obfs", translate("Obfs")) -for a, t in ipairs(ssr_obfs_list) do obfs:value(t) end -obfs:depends("type", "SSR") - -obfs_param = s:option(Value, "obfs_param", translate("Obfs_param")) -obfs_param:depends("type", "SSR") - -timeout = s:option(Value, "timeout", translate("Connection Timeout")) -timeout.datatype = "uinteger" -timeout.default = 300 -timeout:depends("type", "SS") -timeout:depends("type", "SS-Rust") -timeout:depends("type", "SSR") - -tcp_fast_open = s:option(ListValue, "tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required")) -tcp_fast_open:value("false") -tcp_fast_open:value("true") -tcp_fast_open:depends("type", "SS") -tcp_fast_open:depends("type", "SS-Rust") -tcp_fast_open:depends("type", "SSR") - -fast_open = s:option(Flag, "fast_open", translate("Fast Open")) -fast_open.default = "0" -fast_open:depends("type", "Hysteria") - -ss_plugin = s:option(ListValue, "ss_plugin", translate("plugin")) -ss_plugin:value("none", translate("none")) -if api.is_finded("xray-plugin") then ss_plugin:value("xray-plugin") end -if api.is_finded("v2ray-plugin") then ss_plugin:value("v2ray-plugin") end -if api.is_finded("obfs-local") then ss_plugin:value("obfs-local") end -ss_plugin:depends("type", "SS") -ss_plugin:depends("type", "SS-Rust") -function ss_plugin.cfgvalue(self, section) - return m:get(section, "plugin") -end -function ss_plugin.write(self, section, value) - m:set(section, "plugin", value) -end - -ss_plugin_opts = s:option(Value, "ss_plugin_opts", translate("opts")) -ss_plugin_opts:depends("ss_plugin", "xray-plugin") -ss_plugin_opts:depends("ss_plugin", "v2ray-plugin") -ss_plugin_opts:depends("ss_plugin", "obfs-local") -function ss_plugin_opts.cfgvalue(self, section) - return m:get(section, "plugin_opts") -end -function ss_plugin_opts.write(self, section, value) - m:set(section, "plugin_opts", value) -end - -uuid = s:option(Value, "uuid", translate("ID")) -uuid.password = true -uuid:depends({ type = "V2ray", protocol = "vmess" }) -uuid:depends({ type = "V2ray", protocol = "vless" }) -uuid:depends({ type = "Xray", protocol = "vmess" }) -uuid:depends({ type = "Xray", protocol = "vless" }) -uuid:depends({ type = "TUIC"}) - -tls = s:option(Flag, "tls", translate("TLS")) -tls.default = 0 -tls:depends({ type = "V2ray", protocol = "vmess" }) -tls:depends({ type = "V2ray", protocol = "vless" }) -tls:depends({ type = "V2ray", protocol = "socks" }) -tls:depends({ type = "V2ray", protocol = "trojan" }) -tls:depends({ type = "V2ray", protocol = "shadowsocks" }) -tls:depends({ type = "Xray", protocol = "vmess" }) -tls:depends({ type = "Xray", protocol = "vless" }) -tls:depends({ type = "Xray", protocol = "socks" }) -tls:depends({ type = "Xray", protocol = "trojan" }) -tls:depends({ type = "Xray", protocol = "shadowsocks" }) - -tlsflow = s:option(Value, "tlsflow", translate("flow")) -tlsflow.default = "" -tlsflow:value("", translate("Disable")) -tlsflow:value("xtls-rprx-vision") -tlsflow:value("xtls-rprx-vision-udp443") -tlsflow:depends({ type = "Xray", protocol = "vless", tls = true, transport = "tcp" }) - -reality = s:option(Flag, "reality", translate("REALITY"), translate("Only recommend to use with VLESS-TCP-XTLS-Vision.")) -reality.default = 0 -reality:depends({ type = "Xray", tls = true, transport = "tcp" }) -reality:depends({ type = "Xray", tls = true, transport = "h2" }) -reality:depends({ type = "Xray", tls = true, transport = "grpc" }) - -alpn = s:option(ListValue, "alpn", translate("alpn")) -alpn.default = "default" -alpn:value("default", translate("Default")) -alpn:value("h2,http/1.1") -alpn:value("h2") -alpn:value("http/1.1") -alpn:depends({ type = "V2ray", tls = true }) -alpn:depends({ type = "Xray", tls = true, reality = false }) - -tls_serverName = s:option(Value, "tls_serverName", translate("Domain")) -tls_serverName:depends("tls", true) -tls_serverName:depends("type", "Hysteria") - -tls_allowInsecure = s:option(Flag, "tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) -tls_allowInsecure.default = "0" -tls_allowInsecure:depends({ tls = true, reality = false }) -tls_allowInsecure:depends("type", "Hysteria") - -xray_fingerprint = s:option(Value, "xray_fingerprint", translate("Finger Print"), translate("Avoid using randomized, unless you have to.")) -xray_fingerprint:value("", translate("Disable")) -xray_fingerprint:value("chrome") -xray_fingerprint:value("firefox") -xray_fingerprint:value("safari") -xray_fingerprint:value("ios") ---xray_fingerprint:value("android") -xray_fingerprint:value("edge") ---xray_fingerprint:value("360") -xray_fingerprint:value("qq") -xray_fingerprint:value("random") -xray_fingerprint:value("randomized") -xray_fingerprint.default = "" -xray_fingerprint:depends({ type = "Xray", tls = true, reality = false }) -function xray_fingerprint.cfgvalue(self, section) - return m:get(section, "fingerprint") -end -function xray_fingerprint.write(self, section, value) - m:set(section, "fingerprint", value) -end -function xray_fingerprint.remove(self, section) - m:del(section, "fingerprint") -end - - --- [[ REALITY部分 ]] -- -reality_publicKey = s:option(Value, "reality_publicKey", translate("Public Key")) -reality_publicKey:depends({ type = "Xray", tls = true, reality = true }) - -reality_shortId = s:option(Value, "reality_shortId", translate("Short Id")) -reality_shortId:depends({ type = "Xray", tls = true, reality = true }) - -reality_spiderX = s:option(Value, "reality_spiderX", translate("Spider X")) -reality_spiderX.placeholder = "/" -reality_spiderX:depends({ type = "Xray", tls = true, reality = true }) - -reality_fingerprint = s:option(Value, "reality_fingerprint", translate("Finger Print"), translate("Avoid using randomized, unless you have to.")) -reality_fingerprint:value("chrome") -reality_fingerprint:value("firefox") -reality_fingerprint:value("safari") -reality_fingerprint:value("ios") ---reality_fingerprint:value("android") -reality_fingerprint:value("edge") ---reality_fingerprint:value("360") -reality_fingerprint:value("qq") -reality_fingerprint:value("random") -reality_fingerprint:value("randomized") -reality_fingerprint.default = "chrome" -reality_fingerprint:depends({ type = "Xray", tls = true, reality = true }) -function reality_fingerprint.cfgvalue(self, section) - return m:get(section, "fingerprint") -end -function reality_fingerprint.write(self, section, value) - m:set(section, "fingerprint", value) -end - -transport = s:option(ListValue, "transport", translate("Transport")) -transport:value("tcp", "TCP") -transport:value("mkcp", "mKCP") -transport:value("ws", "WebSocket") -transport:value("h2", "HTTP/2") -transport:value("ds", "DomainSocket") -transport:value("quic", "QUIC") -transport:value("grpc", "gRPC") -transport:depends({ type = "V2ray", protocol = "vmess" }) -transport:depends({ type = "V2ray", protocol = "vless" }) -transport:depends({ type = "V2ray", protocol = "socks" }) -transport:depends({ type = "V2ray", protocol = "shadowsocks" }) -transport:depends({ type = "V2ray", protocol = "trojan" }) -transport:depends({ type = "Xray", protocol = "vmess" }) -transport:depends({ type = "Xray", protocol = "vless" }) -transport:depends({ type = "Xray", protocol = "socks" }) -transport:depends({ type = "Xray", protocol = "shadowsocks" }) -transport:depends({ type = "Xray", protocol = "trojan" }) - ---[[ -ss_transport = s:option(ListValue, "ss_transport", translate("Transport")) -ss_transport:value("ws", "WebSocket") -ss_transport:value("h2", "HTTP/2") -ss_transport:value("h2+ws", "HTTP/2 & WebSocket") -ss_transport:depends({ type = "V2ray", protocol = "shadowsocks" }) -ss_transport:depends({ type = "Xray", protocol = "shadowsocks" }) -]]-- - -wireguard_public_key = s:option(Value, "wireguard_public_key", translate("Public Key")) -wireguard_public_key:depends({ type = "Xray", protocol = "wireguard" }) - -wireguard_secret_key = s:option(Value, "wireguard_secret_key", translate("Private Key")) -wireguard_secret_key:depends({ type = "Xray", protocol = "wireguard" }) - -wireguard_preSharedKey = s:option(Value, "wireguard_preSharedKey", translate("Pre shared key")) -wireguard_preSharedKey:depends({ type = "Xray", protocol = "wireguard" }) - -wireguard_local_address = s:option(DynamicList, "wireguard_local_address", translate("Local Address")) -wireguard_local_address:depends({ type = "Xray", protocol = "wireguard" }) - -wireguard_mtu = s:option(Value, "wireguard_mtu", translate("MTU")) -wireguard_mtu.default = "1420" -wireguard_mtu:depends({ type = "Xray", protocol = "wireguard" }) - -if api.compare_versions(api.get_app_version("xray"), ">=", "1.8.0") then - wireguard_reserved = s:option(Value, "wireguard_reserved", translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings.")) - wireguard_reserved:depends({ type = "Xray", protocol = "wireguard" }) -end - -wireguard_keepAlive = s:option(Value, "wireguard_keepAlive", translate("Keep Alive")) -wireguard_keepAlive.default = "0" -wireguard_keepAlive:depends({ type = "Xray", protocol = "wireguard" }) - --- [[ TCP部分 ]]-- - --- TCP伪装 -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:depends("tcp_guise", "http") - --- HTTP路径 -tcp_guise_http_path = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path")) -tcp_guise_http_path.placeholder = "/" -tcp_guise_http_path:depends("tcp_guise", "http") - --- [[ mKCP部分 ]]-- - -mkcp_guise = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
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") - -mkcp_mtu = s:option(Value, "mkcp_mtu", translate("KCP MTU")) -mkcp_mtu.default = "1350" -mkcp_mtu:depends("transport", "mkcp") - -mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI")) -mkcp_tti.default = "20" -mkcp_tti:depends("transport", "mkcp") - -mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity")) -mkcp_uplinkCapacity.default = "5" -mkcp_uplinkCapacity:depends("transport", "mkcp") - -mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity")) -mkcp_downlinkCapacity.default = "20" -mkcp_downlinkCapacity:depends("transport", "mkcp") - -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.default = "1" -mkcp_readBufferSize:depends("transport", "mkcp") - -mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize")) -mkcp_writeBufferSize.default = "1" -mkcp_writeBufferSize:depends("transport", "mkcp") - -mkcp_seed = s:option(Value, "mkcp_seed", translate("KCP Seed")) -mkcp_seed: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") - -ws_path = s:option(Value, "ws_path", translate("WebSocket Path")) -ws_path.placeholder = "/" -ws_path:depends("transport", "ws") -ws_path:depends("ss_transport", "ws") -ws_path:depends({ type = "Brook", brook_protocol = "wsclient" }) - -ws_enableEarlyData = s:option(Flag, "ws_enableEarlyData", translate("Enable early data")) -ws_enableEarlyData:depends({ type = "V2ray", transport = "ws" }) - -ws_maxEarlyData = s:option(Value, "ws_maxEarlyData", translate("Early data length")) -ws_maxEarlyData.default = "1024" -ws_maxEarlyData:depends("ws_enableEarlyData", true) - -ws_earlyDataHeaderName = s:option(Value, "ws_earlyDataHeaderName", translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol")) -ws_earlyDataHeaderName:depends("ws_enableEarlyData", true) - --- [[ HTTP/2部分 ]]-- -h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host")) -h2_host:depends("transport", "h2") -h2_host:depends("ss_transport", "h2") - -h2_path = s:option(Value, "h2_path", translate("HTTP/2 Path")) -h2_path.placeholder = "/" -h2_path:depends("transport", "h2") -h2_path:depends("ss_transport", "h2") - -h2_health_check = s:option(Flag, "h2_health_check", translate("Health check")) -h2_health_check:depends({ type = "Xray", transport = "h2"}) - -h2_read_idle_timeout = s:option(Value, "h2_read_idle_timeout", translate("Idle timeout")) -h2_read_idle_timeout.default = "10" -h2_read_idle_timeout:depends("h2_health_check", true) - -h2_health_check_timeout = s:option(Value, "h2_health_check_timeout", translate("Health check timeout")) -h2_health_check_timeout.default = "15" -h2_health_check_timeout:depends("h2_health_check", true) - --- [[ DomainSocket部分 ]]-- -ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running.")) -ds_path:depends("transport", "ds") - --- [[ QUIC部分 ]]-- -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:depends("transport", "quic") - -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") - --- [[ gRPC部分 ]]-- -grpc_serviceName = s:option(Value, "grpc_serviceName", "ServiceName") -grpc_serviceName:depends("transport", "grpc") - -grpc_mode = s:option(ListValue, "grpc_mode", "gRPC " .. translate("Transfer mode")) -grpc_mode:value("gun") -grpc_mode:value("multi") -grpc_mode:depends({ type = "Xray", transport = "grpc"}) - -grpc_health_check = s:option(Flag, "grpc_health_check", translate("Health check")) -grpc_health_check:depends({ type = "Xray", transport = "grpc"}) - -grpc_idle_timeout = s:option(Value, "grpc_idle_timeout", translate("Idle timeout")) -grpc_idle_timeout.default = "10" -grpc_idle_timeout:depends("grpc_health_check", true) - -grpc_health_check_timeout = s:option(Value, "grpc_health_check_timeout", translate("Health check timeout")) -grpc_health_check_timeout.default = "20" -grpc_health_check_timeout:depends("grpc_health_check", true) - -grpc_permit_without_stream = s:option(Flag, "grpc_permit_without_stream", translate("Permit without stream")) -grpc_permit_without_stream.default = "0" -grpc_permit_without_stream:depends("grpc_health_check", true) - -grpc_initial_windows_size = s:option(Value, "grpc_initial_windows_size", translate("Initial Windows Size")) -grpc_initial_windows_size.default = "0" -grpc_initial_windows_size:depends({ type = "Xray", transport = "grpc"}) - --- [[ Mux ]]-- -mux = s:option(Flag, "mux", translate("Mux")) -mux:depends({ type = "V2ray", protocol = "vmess" }) -mux:depends({ type = "V2ray", protocol = "vless" }) -mux:depends({ type = "V2ray", protocol = "http" }) -mux:depends({ type = "V2ray", protocol = "socks" }) -mux:depends({ type = "V2ray", protocol = "shadowsocks" }) -mux:depends({ type = "V2ray", protocol = "trojan" }) -mux:depends({ type = "Xray", protocol = "vmess" }) -mux:depends({ type = "Xray", protocol = "vless", tlsflow = "" }) -mux:depends({ type = "Xray", protocol = "http" }) -mux:depends({ type = "Xray", protocol = "socks" }) -mux:depends({ type = "Xray", protocol = "shadowsocks" }) -mux:depends({ type = "Xray", protocol = "trojan" }) - --- [[ XUDP Mux ]]-- -xmux = s:option(Flag, "xmux", translate("Mux")) -xmux.default = 1 -xmux:depends({ type = "Xray", protocol = "vless", tlsflow = "xtls-rprx-vision" }) -xmux:depends({ type = "Xray", protocol = "vless", tlsflow = "xtls-rprx-vision-udp443" }) - -mux_concurrency = s:option(Value, "mux_concurrency", translate("Mux concurrency")) -mux_concurrency.default = 8 -mux_concurrency:depends("mux", true) -mux_concurrency:depends("smux", true) - -xudp_concurrency = s:option(Value, "xudp_concurrency", translate("XUDP Mux concurrency")) -xudp_concurrency.default = 8 -xudp_concurrency:depends("xmux", true) - -smux_idle_timeout = s:option(Value, "smux_idle_timeout", translate("Mux idle timeout")) -smux_idle_timeout.default = 60 -smux_idle_timeout:depends("smux", true) - -hysteria_up_mbps = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps")) -hysteria_up_mbps.default = "10" -hysteria_up_mbps:depends("type", "Hysteria") - -hysteria_down_mbps = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps")) -hysteria_down_mbps.default = "50" -hysteria_down_mbps:depends("type", "Hysteria") - -hysteria_recv_window_conn = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window")) -hysteria_recv_window_conn:depends("type", "Hysteria") - -hysteria_recv_window = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window")) -hysteria_recv_window:depends("type", "Hysteria") - -hysteria_handshake_timeout = s:option(Value, "hysteria_handshake_timeout", translate("Handshake Timeout")) -hysteria_handshake_timeout:depends("type", "Hysteria") - -hysteria_idle_timeout = s:option(Value, "hysteria_idle_timeout", translate("Idle Timeout")) -hysteria_idle_timeout:depends("type", "Hysteria") - -hysteria_hop_interval = s:option(Value, "hysteria_hop_interval", translate("Hop Interval")) -hysteria_hop_interval:depends("type", "Hysteria") - -hysteria_disable_mtu_discovery = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection")) -hysteria_disable_mtu_discovery:depends("type", "Hysteria") - -hysteria_lazy_start = s:option(Flag, "hysteria_lazy_start", translate("Lazy Start")) -hysteria_lazy_start:depends("type", "Hysteria") - --- [[ TUIC ]] --- Tuic Password for remote server connect -tuic_passwd = s:option(Value, "tuic_password", translate("TUIC User Password For Connect Remote Server")) -tuic_passwd.password = true -tuic_passwd.rmempty = true -tuic_passwd.default = "" -tuic_passwd:depends("type", "TUIC") - ---[[ --- Tuic username for local socks connect -tuic_passwd = s:option(Value, "tuic_socks_username", translate("TUIC UserName For Local Socks")) -tuic_passwd.rmempty = true -tuic_passwd.default = "" -tuic_passwd:depends("type", "TUIC") --- Tuic Password for local socks connect -tuic_passwd = s:option(Value, "tuic_socks_password", translate("TUIC Password For Local Socks")) -tuic_passwd.password = true -tuic_passwd.rmempty = true -tuic_passwd.default = "" -tuic_passwd:depends("type", "TUIC") ---]] - -tuic_ip = s:option(Value, "tuic_ip", translate("Set the TUIC proxy server ip address")) -tuic_ip:depends("type", "TUIC") -tuic_ip.datatype = "ipaddr" -tuic_ip.rmempty = true - -tuic_udp_relay_mode = s:option(ListValue, "tuic_udp_relay_mode", translate("UDP relay mode")) -tuic_udp_relay_mode:depends("type", "TUIC") -tuic_udp_relay_mode:value("native", translate("native")) -tuic_udp_relay_mode:value("quic", translate("QUIC")) -tuic_udp_relay_mode.default = "native" -tuic_udp_relay_mode.rmempty = true - -tuic_congestion_control = s:option(ListValue, "tuic_congestion_control", translate("Congestion control algorithm")) -tuic_congestion_control:depends("type", "TUIC") -tuic_congestion_control:value("bbr", translate("BBR")) -tuic_congestion_control:value("cubic", translate("CUBIC")) -tuic_congestion_control:value("new_reno", translate("New Reno")) -tuic_congestion_control.default = "cubic" -tuic_congestion_control.rmempty = true - -tuic_heartbeat = s:option(Value, "tuic_heartbeat", translate("Heartbeat interval(second)")) -tuic_heartbeat:depends("type", "TUIC") -tuic_heartbeat.datatype = "uinteger" -tuic_heartbeat.default = "3" -tuic_heartbeat.rmempty = true - -tuic_timeout = s:option(Value, "tuic_timeout", translate("Timeout for establishing a connection to server(second)")) -tuic_timeout:depends("type", "TUIC") -tuic_timeout.datatype = "uinteger" -tuic_timeout.default = "8" -tuic_timeout.rmempty = true - -tuic_gc_interval = s:option(Value, "tuic_gc_interval", translate("Garbage collection interval(second)")) -tuic_gc_interval:depends("type", "TUIC") -tuic_gc_interval.datatype = "uinteger" -tuic_gc_interval.default = "3" -tuic_gc_interval.rmempty = true - -tuic_gc_lifetime = s:option(Value, "tuic_gc_lifetime", translate("Garbage collection lifetime(second)")) -tuic_gc_lifetime:depends("type", "TUIC") -tuic_gc_lifetime.datatype = "uinteger" -tuic_gc_lifetime.default = "15" -tuic_gc_lifetime.rmempty = true - -tuic_send_window = s:option(Value, "tuic_send_window", translate("TUIC send window")) -tuic_send_window.datatype = "uinteger" -tuic_send_window:depends("type", "TUIC") -tuic_send_window.default = 20971520 -tuic_send_window.rmempty = true - -tuic_receive_window = s:option(Value, "tuic_receive_window", translate("TUIC receive window")) -tuic_receive_window.datatype = "uinteger" -tuic_receive_window:depends("type", "TUIC") -tuic_receive_window.default = 10485760 -tuic_receive_window.rmempty = true - -tuic_max_package_size = s:option(Value, "tuic_max_package_size", translate("TUIC Maximum packet size the socks5 server can receive from external, in bytes")) -tuic_max_package_size.datatype = "uinteger" -tuic_max_package_size:depends("type", "TUIC") -tuic_max_package_size.default = 1500 -tuic_max_package_size.rmempty = true - ---Tuic settings for the local inbound socks5 server -tuic_dual_stack = s:option(Flag, "tuic_dual_stack", translate("Set if the listening socket should be dual-stack")) -tuic_dual_stack:depends("type", "TUIC") -tuic_dual_stack.default = 0 -tuic_dual_stack.rmempty = true - -tuic_disable_sni = s:option(Flag, "tuic_disable_sni", translate("Disable SNI")) -tuic_disable_sni:depends("type", "TUIC") -tuic_disable_sni.default = 0 -tuic_disable_sni.rmempty = true - -tuic_zero_rtt_handshake = s:option(Flag, "tuic_zero_rtt_handshake", translate("Enable 0-RTT QUIC handshake")) -tuic_zero_rtt_handshake:depends("type", "TUIC") -tuic_zero_rtt_handshake.default = 0 -tuic_zero_rtt_handshake.rmempty = true - -tuic_tls_alpn = s:option(DynamicList, "tuic_tls_alpn", translate("TLS ALPN")) -tuic_tls_alpn:depends({ type = "TUIC"}) -tuic_tls_alpn.rmempty = true - -protocol.validate = function(self, value) - if value == "_shunt" or value == "_balancing" then - address.rmempty = true - port.rmempty = true - end - return value +for index, value in ipairs(type_table) do + local p_func = loadfile(types_dir .. value) + setfenv(p_func, getfenv(1))(m, s) end return m diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/brook.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/brook.lua new file mode 100644 index 000000000..ad5a42a33 --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/brook.lua @@ -0,0 +1,65 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("brook") then + return +end + +local option_prefix = "brook_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +-- [[ Brook ]] + +s.fields["type"]:value("Brook", translate("Brook")) + +o = s:option(ListValue, "brook_protocol", translate("Protocol")) +o:value("client", translate("Brook")) +o:value("wsclient", translate("WebSocket")) + +o = s:option(Value, "brook_address", translate("Address (Support Domain Name)")) + +o = s:option(Value, "brook_port", translate("Port")) +o.datatype = "port" + +o = s:option(Value, "brook_ws_path", translate("WebSocket Path")) +o.placeholder = "/" +o:depends({ brook_protocol = "wsclient" }) + +o = s:option(Flag, "brook_tls", translate("Use TLS")) +o:depends({ brook_protocol = "wsclient" }) + +o = s:option(Value, "brook_password", translate("Password")) +o.password = true + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "Brook" + end + else + s.fields[key]:depends({ type = "Brook" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria.lua new file mode 100644 index 000000000..e1ed3bd9d --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/hysteria.lua @@ -0,0 +1,115 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("hysteria") then + return +end + +local option_prefix = "hysteria_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +-- [[ Hysteria ]] + +s.fields["type"]:value("Hysteria", translate("Hysteria")) + +o = s:option(ListValue, "hysteria_protocol", translate("Protocol")) +o:value("udp", "UDP") +o:value("faketcp", "faketcp") +o:value("wechat-video", "wechat-video") + +o = s:option(Value, "hysteria_address", translate("Address (Support Domain Name)")) + +o = s:option(Value, "hysteria_port", translate("Port")) +o.datatype = "port" + +o = s:option(Value, "hysteria_hop", translate("Additional ports for hysteria hop")) +o:depends("type", "Hysteria") +o.not_rewrite = true + +o = s:option(Value, "hysteria_obfs", translate("Obfs Password")) +o.not_rewrite = true + +o = s:option(ListValue, "hysteria_auth_type", translate("Auth Type")) +o:value("disable", translate("Disable")) +o:value("string", translate("STRING")) +o:value("base64", translate("BASE64")) +o.not_rewrite = true + +o = s:option(Value, "hysteria_auth_password", translate("Auth Password")) +o.password = true +o:depends({ hysteria_auth_type = "string"}) +o:depends({ hysteria_auth_type = "base64"}) +o.not_rewrite = true + +o = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN")) +o.not_rewrite = true + +o = s:option(Flag, "hysteria_fast_open", translate("Fast Open")) +o.default = "0" + +o = s:option(Value, "hysteria_tls_serverName", translate("Domain")) + +o = s:option(Flag, "hysteria_tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) +o.default = "0" + +o = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps")) +o.default = "10" +o.not_rewrite = true + +o = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps")) +o.default = "50" +o.not_rewrite = true + +o = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window")) +o.not_rewrite = true + +o = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window")) +o.not_rewrite = true + +o = s:option(Value, "hysteria_handshake_timeout", translate("Handshake Timeout")) +o.not_rewrite = true + +o = s:option(Value, "hysteria_idle_timeout", translate("Idle Timeout")) +o.not_rewrite = true + +o = s:option(Value, "hysteria_hop_interval", translate("Hop Interval")) +o.not_rewrite = true + +o = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection")) +o.not_rewrite = true + +o = s:option(Flag, "hysteria_lazy_start", translate("Lazy Start")) +o.not_rewrite = true + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "Hysteria" + end + else + s.fields[key]:depends({ type = "Hysteria" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/naive.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/naive.lua new file mode 100644 index 000000000..3390eb270 --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/naive.lua @@ -0,0 +1,60 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("naive") then + return +end + +local option_prefix = "naive_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +-- [[ Naive ]] + +s.fields["type"]:value("Naiveproxy", translate("NaiveProxy")) + +o = s:option(ListValue, "naive_protocol", translate("Protocol")) +o:value("https", translate("HTTPS")) +o:value("quic", translate("QUIC")) + +o = s:option(Value, "naive_address", translate("Address (Support Domain Name)")) + +o = s:option(Value, "naive_port", translate("Port")) +o.datatype = "port" + +o = s:option(Value, "naive_username", translate("Username")) + +o = s:option(Value, "naive_password", translate("Password")) +o.password = true + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "Naiveproxy" + end + else + s.fields[key]:depends({ type = "Naiveproxy" }) + end + end +end 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 new file mode 100644 index 000000000..a0654433a --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ray.lua @@ -0,0 +1,739 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("xray") and not api.is_finded("v2ray") then + return +end + +local appname = api.appname +local uci = api.uci + +local option_prefix = "xray_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end +local function rm_prefix_remove(self, section, value) + if self.option:find(option_prefix) == 1 then + m:del(section, self.option:sub(1 + #option_prefix)) + end +end + +local function add_xray_depends(o, field, value) + local deps = { type = "Xray" } + if field then + if type(field) == "string" then + deps[field] = value + else + for key, value in pairs(field) do + deps[key] = value + end + end + end + o:depends(deps) +end + +local function add_v2ray_depends(o, field, value) + local deps = { type = "V2ray" } + if field then + if type(field) == "string" then + deps[field] = value + else + for key, value in pairs(field) do + deps[key] = value + end + end + end + o:depends(deps) +end + +local v_ss_encrypt_method_list = { + "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305" +} + +local x_ss_encrypt_method_list = { + "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" +} + +local security_list = { "none", "auto", "aes-128-gcm", "chacha20-poly1305", "zero" } + +local header_type_list = { + "none", "srtp", "utp", "wechat-video", "dtls", "wireguard" +} + +-- [[ Xray ]] + +if api.is_finded("xray") then + s.fields["type"]:value("Xray", translate("Xray")) +end +if api.is_finded("v2ray") then + s.fields["type"]:value("V2ray", translate("V2ray")) +end + +o = s:option(ListValue, "xray_protocol", translate("Protocol")) +o:value("vmess", translate("Vmess")) +o:value("vless", translate("VLESS")) +o:value("http", translate("HTTP")) +o:value("socks", translate("Socks")) +o:value("shadowsocks", translate("Shadowsocks")) +o:value("trojan", translate("Trojan")) +o:value("wireguard", translate("WireGuard")) +o:value("_balancing", translate("Balancing")) +o:value("_shunt", translate("Shunt")) +o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)") +add_xray_depends(o) +add_v2ray_depends(o) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_iface", translate("Interface")) +o.default = "eth1" +add_xray_depends(o, { xray_protocol = "_iface" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +local nodes_table = {} +local balancers_table = {} +local iface_table = {} +for k, e in ipairs(api.get_valid_nodes()) do + if e.node_type == "normal" then + nodes_table[#nodes_table + 1] = { + id = e[".name"], + remarks = e["remark"] + } + end + if e.protocol == "_balancing" then + balancers_table[#balancers_table + 1] = { + id = e[".name"], + remarks = e["remark"] + } + end + if e.protocol == "_iface" then + iface_table[#iface_table + 1] = { + id = e[".name"], + remarks = e["remark"] + } + end +end + +-- 负载均衡列表 +local o = s:option(DynamicList, "xray_balancing_node", translate("Load balancing node list"), translate("Load balancing node list, document")) +add_xray_depends(o, { xray_protocol = "_balancing" }) +add_v2ray_depends(o, { xray_protocol = "_balancing" }) +for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +local o = s:option(ListValue, "xray_balancingStrategy", translate("Balancing Strategy")) +add_xray_depends(o, { xray_protocol = "_balancing" }) +add_v2ray_depends(o, { xray_protocol = "_balancing" }) +o:value("random") +o:value("leastPing") +o.default = "random" +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +-- 探测地址 +local o = s:option(Flag, "xray_useCustomProbeUrl", translate("Use Custome Probe URL"), translate("By default the built-in probe URL will be used, enable this option to use a custom probe URL.")) +add_xray_depends(o, { xray_balancingStrategy = "leastPing" }) +add_v2ray_depends(o, { xray_balancingStrategy = "leastPing" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +local o = s:option(Value, "xray_probeUrl", translate("Probe URL")) +add_xray_depends(o, { xray_useCustomProbeUrl = true }) +add_v2ray_depends(o, { xray_useCustomProbeUrl = true }) +o.default = "https://www.google.com/generate_204" +o.description = translate("The URL used to detect the connection status.") +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +-- 探测间隔 +local o = s:option(Value, "xray_probeInterval", translate("Probe Interval")) +add_xray_depends(o, { xray_balancingStrategy = "leastPing" }) +add_v2ray_depends(o, { xray_balancingStrategy = "leastPing" }) +o.default = "1m" +o.description = translate("The interval between initiating probes. Every time this time elapses, a server status check is performed on a server. The time format is numbers + units, such as '10s', '2h45m', and the supported time units are ns, us, ms, s, m, h, which correspond to nanoseconds, microseconds, milliseconds, seconds, minutes, and hours, respectively.") +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +-- [[ 分流模块 ]] +if #nodes_table > 0 then + o = s:option(Flag, "preproxy_enabled", translate("Preproxy")) + add_xray_depends(o, { xray_protocol = "_shunt" }) + add_v2ray_depends(o, { xray_protocol = "_shunt" }) + o = s:option(Value, "main_node", string.format('%s', translate("Preproxy Node")), translate("Set the node to be used as a pre-proxy. Each rule (including Default) has a separate switch that controls whether this rule uses the pre-proxy or not.")) + add_xray_depends(o, { xray_protocol = "_shunt", preproxy_enabled = true }) + add_v2ray_depends(o, { xray_protocol = "_shunt", preproxy_enabled = true }) + for k, v in pairs(balancers_table) do + o:value(v.id, v.remarks) + end + for k, v in pairs(iface_table) do + o:value(v.id, v.remarks) + end + for k, v in pairs(nodes_table) do + o:value(v.id, v.remarks) + end + o.default = "nil" +end +uci:foreach(appname, "shunt_rules", function(e) + if e[".name"] and e.remarks then + o = s:option(Value, e[".name"], string.format('* %s', api.url("shunt_rules", e[".name"]), e.remarks)) + o:value("nil", translate("Close")) + o:value("_default", translate("Default")) + o:value("_direct", translate("Direct Connection")) + o:value("_blackhole", translate("Blackhole")) + add_xray_depends(o, { xray_protocol = "_shunt" }) + add_v2ray_depends(o, { xray_protocol = "_shunt" }) + + if #nodes_table > 0 then + for k, v in pairs(balancers_table) do + o:value(v.id, v.remarks) + end + for k, v in pairs(iface_table) do + o:value(v.id, v.remarks) + end + local pt = s:option(ListValue, e[".name"] .. "_proxy_tag", string.format('* %s', e.remarks .. " " .. translate("Preproxy"))) + pt:value("nil", translate("Close")) + pt:value("main", translate("Preproxy Node")) + pt.default = "nil" + for k, v in pairs(nodes_table) do + o:value(v.id, v.remarks) + add_xray_depends(pt, { xray_protocol = "_shunt", preproxy_enabled = "1", [e[".name"]] = v.id }) + add_v2ray_depends(pt, { xray_protocol = "_shunt", preproxy_enabled = "1", [e[".name"]] = v.id }) + end + end + end +end) + +o = s:option(DummyValue, "shunt_tips", " ") +o.rawhtml = true +o.cfgvalue = function(t, n) + return string.format('%s', translate("No shunt rules? Click me to go to add.")) +end +add_xray_depends(o, { xray_protocol = "_shunt" }) + +local o = s:option(Value, "default_node", string.format('* %s', translate("Default"))) +add_xray_depends(o, { xray_protocol = "_shunt" }) +o:value("_direct", translate("Direct Connection")) +o:value("_blackhole", translate("Blackhole")) + +if #nodes_table > 0 then + for k, v in pairs(balancers_table) do + o:value(v.id, v.remarks) + end + for k, v in pairs(iface_table) do + o:value(v.id, v.remarks) + end + local dpt = s:option(ListValue, "default_proxy_tag", string.format('* %s', translate("Default Preproxy")), translate("When using, localhost will connect this node first and then use this node to connect the default node.")) + dpt:value("nil", translate("Close")) + dpt:value("main", translate("Preproxy Node")) + dpt.default = "nil" + for k, v in pairs(nodes_table) do + o:value(v.id, v.remarks) + add_xray_depends(dpt, { xray_protocol = "_shunt", preproxy_enabled = "1", default_node = v.id }) + end +end + +o = s:option(ListValue, "domainStrategy", translate("Domain Strategy")) +o:value("AsIs") +o:value("IPIfNonMatch") +o:value("IPOnDemand") +o.default = "IPOnDemand" +o.description = "
" +add_xray_depends(o, { xray_protocol = "_shunt" }) + +o = s:option(ListValue, "domainMatcher", translate("Domain matcher")) +o:value("hybrid") +o:value("linear") +add_xray_depends(o, { xray_protocol = "_shunt" }) + +-- [[ 分流模块 End ]] + +o = s:option(Value, "xray_address", translate("Address (Support Domain Name)")) +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless" }) +add_xray_depends(o, { xray_protocol = "http" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "trojan" }) +add_xray_depends(o, { xray_protocol = "wireguard" }) +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "http" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_port", translate("Port")) +o.datatype = "port" +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless" }) +add_xray_depends(o, { xray_protocol = "http" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "trojan" }) +add_xray_depends(o, { xray_protocol = "wireguard" }) +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "http" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_username", translate("Username")) +add_xray_depends(o, { xray_protocol = "http" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "http" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_password", translate("Password")) +o.password = true +add_xray_depends(o, { xray_protocol = "http" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "trojan" }) +add_v2ray_depends(o, { xray_protocol = "http" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(ListValue, "security", translate("Encrypt Method")) +for a, t in ipairs(security_list) do o:value(t) end +add_xray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vmess" }) + +o = s:option(Value, "encryption", translate("Encrypt Method")) +o.default = "none" +o:value("none") +add_xray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) + +o = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method")) +for a, t in ipairs(v_ss_encrypt_method_list) do o:value(t) end +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +function o.cfgvalue(self, section) + return m:get(section, "method") +end +function o.write(self, section, value) + m:set(section, "method", value) +end + +o = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method")) +for a, t in ipairs(x_ss_encrypt_method_list) do o:value(t) end +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +function o.cfgvalue(self, section) + return m:get(section, "method") +end +function o.write(self, section, value) + m:set(section, "method", value) +end + +o = s:option(Flag, "iv_check", translate("IV Check")) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "aes-128-gcm" }) +add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "aes-256-gcm" }) +add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "chacha20-poly1305" }) +add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "xchacha20-poly1305" }) + +o = s:option(Flag, "uot", translate("UDP over TCP"), translate("Need Xray-core or sing-box as server side.")) +add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-aes-128-gcm" }) +add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-aes-256-gcm" }) +add_xray_depends(o, { xray_protocol = "shadowsocks", x_ss_encrypt_method = "2022-blake3-chacha20-poly1305" }) + +o = s:option(Value, "xray_uuid", translate("ID")) +o.password = true +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Flag, "xray_tls", translate("TLS")) +o.default = 0 +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "trojan" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_tlsflow", translate("flow")) +o.default = "" +o:value("", translate("Disable")) +o:value("xtls-rprx-vision") +o:value("xtls-rprx-vision-udp443") +add_xray_depends(o, { xray_protocol = "vless", xray_tls = true, transport = "tcp" }) + +o = s:option(Flag, "reality", translate("REALITY"), translate("Only recommend to use with VLESS-TCP-XTLS-Vision.")) +o.default = 0 +add_xray_depends(o, { xray_tls = true, transport = "tcp" }) +add_xray_depends(o, { xray_tls = true, transport = "h2" }) +add_xray_depends(o, { xray_tls = true, transport = "grpc" }) + +o = s:option(ListValue, "alpn", translate("alpn")) +o.default = "default" +o:value("default", translate("Default")) +o:value("h2,http/1.1") +o:value("h2") +o:value("http/1.1") +add_xray_depends(o, { xray_tls = true, reality = false }) +add_v2ray_depends(o, { xray_tls = true }) + +-- o = s:option(Value, "minversion", translate("minversion")) +-- o.default = "1.3" +-- o:value("1.3") +-- add_xray_depends(o, { xray_tls = true }) +-- add_v2ray_depends(o, { xray_tls = true }) + +o = s:option(Value, "xray_tls_serverName", translate("Domain")) +add_xray_depends(o, { xray_tls = true }) +add_v2ray_depends(o, { xray_tls = true }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Flag, "xray_tls_allowInsecure", translate("allowInsecure"), translate("Whether unsafe connections are allowed. When checked, Certificate validation will be skipped.")) +o.default = "0" +add_xray_depends(o, { xray_tls = true, reality = false }) +add_v2ray_depends(o, { xray_tls = true }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_fingerprint", translate("Finger Print"), translate("Avoid using randomized, unless you have to.")) +o:value("", translate("Disable")) +o:value("chrome") +o:value("firefox") +o:value("safari") +o:value("ios") +-- o:value("android") +o:value("edge") +-- o:value("360") +o:value("qq") +o:value("random") +o:value("randomized") +o.default = "" +add_xray_depends(o, { xray_tls = true, reality = false }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write +o.remove = rm_prefix_remove + +-- [[ REALITY部分 ]] -- +o = s:option(Value, "reality_publicKey", translate("Public Key")) +add_xray_depends(o, { xray_tls = true, reality = true }) + +o = s:option(Value, "reality_shortId", translate("Short Id")) +add_xray_depends(o, { xray_tls = true, reality = true }) + +o = s:option(Value, "reality_spiderX", translate("Spider X")) +o.placeholder = "/" +add_xray_depends(o, { xray_tls = true, reality = true }) + +o = s:option(Value, "reality_fingerprint", translate("Finger Print"), translate("Avoid using randomized, unless you have to.")) +o:value("chrome") +o:value("firefox") +o:value("safari") +o:value("ios") +-- o:value("android") +o:value("edge") +-- o:value("360") +o:value("qq") +o:value("random") +o:value("randomized") +o.default = "chrome" +add_xray_depends(o, { xray_tls = true, reality = true }) +function o.cfgvalue(self, section) + return m:get(section, "fingerprint") +end +function o.write(self, section, value) + m:set(section, "fingerprint", value) +end + +o = s:option(ListValue, "transport", translate("Transport")) +o:value("tcp", "TCP") +o:value("mkcp", "mKCP") +o:value("ws", "WebSocket") +o:value("h2", "HTTP/2") +o:value("ds", "DomainSocket") +o:value("quic", "QUIC") +o:value("grpc", "gRPC") +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "trojan" }) +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) + +--[[ +o = s:option(ListValue, "ss_transport", translate("Transport")) +o:value("ws", "WebSocket") +o:value("h2", "HTTP/2") +o:value("h2+ws", "HTTP/2 & WebSocket") +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +]]-- + +o = s:option(Value, "wireguard_public_key", translate("Public Key")) +add_xray_depends(o, { xray_protocol = "wireguard" }) + +o = s:option(Value, "wireguard_secret_key", translate("Private Key")) +add_xray_depends(o, { xray_protocol = "wireguard" }) + +o = s:option(Value, "wireguard_preSharedKey", translate("Pre shared key")) +add_xray_depends(o, { xray_protocol = "wireguard" }) + +o = s:option(DynamicList, "wireguard_local_address", translate("Local Address")) +add_xray_depends(o, { xray_protocol = "wireguard" }) + +o = s:option(Value, "wireguard_mtu", translate("MTU")) +o.default = "1420" +add_xray_depends(o, { xray_protocol = "wireguard" }) + +if api.compare_versions(api.get_app_version("xray"), ">=", "1.8.0") then + o = s:option(Value, "wireguard_reserved", translate("Reserved"), translate("Decimal numbers separated by \",\" or Base64-encoded strings.")) + add_xray_depends(o, { xray_protocol = "wireguard" }) +end + +o = s:option(Value, "wireguard_keepAlive", translate("Keep Alive")) +o.default = "0" +add_xray_depends(o, { xray_protocol = "wireguard" }) + +-- [[ TCP部分 ]]-- + +-- TCP伪装 +o = s:option(ListValue, "tcp_guise", translate("Camouflage Type")) +o:value("none", "none") +o:value("http", "http") +add_xray_depends(o, { transport = "tcp" }) +add_v2ray_depends(o, { transport = "tcp" }) + +-- HTTP域名 +o = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host")) +add_xray_depends(o, { tcp_guise = "http" }) +add_v2ray_depends(o, { tcp_guise = "http" }) + +-- HTTP路径 +o = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path")) +o.placeholder = "/" +add_xray_depends(o, { tcp_guise = "http" }) +add_v2ray_depends(o, { tcp_guise = "http" }) + +-- [[ mKCP部分 ]]-- + +o = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)')) +for a, t in ipairs(header_type_list) do o:value(t) end +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_mtu", translate("KCP MTU")) +o.default = "1350" +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_tti", translate("KCP TTI")) +o.default = "20" +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity")) +o.default = "5" +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity")) +o.default = "20" +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +o = s:option(Flag, "mkcp_congestion", translate("KCP Congestion")) +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize")) +o.default = "1" +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize")) +o.default = "1" +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_seed", translate("KCP Seed")) +add_xray_depends(o, { transport = "mkcp" }) +add_v2ray_depends(o, { transport = "mkcp" }) + +-- [[ WebSocket部分 ]]-- +o = s:option(Value, "xray_ws_host", translate("WebSocket Host")) +add_xray_depends(o, { transport = "ws" }) +add_xray_depends(o, { ss_transport = "ws" }) +add_v2ray_depends(o, { transport = "ws" }) +add_v2ray_depends(o, { ss_transport = "ws" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_ws_path", translate("WebSocket Path")) +o.placeholder = "/" +add_xray_depends(o, { transport = "ws" }) +add_xray_depends(o, { ss_transport = "ws" }) +add_v2ray_depends(o, { transport = "ws" }) +add_v2ray_depends(o, { ss_transport = "ws" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Flag, "ws_enableEarlyData", translate("Enable early data")) +add_v2ray_depends(o, { transport = "ws" }) + +o = s:option(Value, "ws_maxEarlyData", translate("Early data length")) +o.default = "1024" +add_v2ray_depends(o, { ws_enableEarlyData = true }) + +o = s:option(Value, "ws_earlyDataHeaderName", translate("Early data header name"), translate("Recommended value: Sec-WebSocket-Protocol")) +add_v2ray_depends(o, { ws_enableEarlyData = true }) + +-- [[ HTTP/2部分 ]]-- +o = s:option(Value, "xray_h2_host", translate("HTTP/2 Host")) +add_xray_depends(o, { transport = "h2" }) +add_xray_depends(o, { ss_transport = "h2" }) +add_v2ray_depends(o, { transport = "h2" }) +add_v2ray_depends(o, { ss_transport = "h2" }) + +o = s:option(Value, "xray_h2_path", translate("HTTP/2 Path")) +o.placeholder = "/" +add_xray_depends(o, { transport = "h2" }) +add_xray_depends(o, { ss_transport = "h2" }) +add_v2ray_depends(o, { transport = "h2" }) +add_v2ray_depends(o, { ss_transport = "h2" }) + +o = s:option(Flag, "h2_health_check", translate("Health check")) +add_xray_depends(o, { transport = "h2" }) + +o = s:option(Value, "h2_read_idle_timeout", translate("Idle timeout")) +o.default = "10" +add_xray_depends(o, { h2_health_check = true }) + +o = s:option(Value, "h2_health_check_timeout", translate("Health check timeout")) +o.default = "15" +add_xray_depends(o, { h2_health_check = true }) + +-- [[ DomainSocket部分 ]]-- +o = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running.")) +add_xray_depends(o, { transport = "ds" }) +add_v2ray_depends(o, { transport = "ds" }) + +-- [[ QUIC部分 ]]-- +o = s:option(ListValue, "quic_security", translate("Encrypt Method")) +o:value("none") +o:value("aes-128-gcm") +o:value("chacha20-poly1305") +add_xray_depends(o, { transport = "quic" }) +add_v2ray_depends(o, { transport = "quic" }) + +o = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key")) +add_xray_depends(o, { transport = "quic" }) +add_v2ray_depends(o, { transport = "quic" }) + +o = s:option(ListValue, "quic_guise", translate("Camouflage Type")) +for a, t in ipairs(header_type_list) do o:value(t) end +add_xray_depends(o, { transport = "quic" }) +add_v2ray_depends(o, { transport = "quic" }) + +-- [[ gRPC部分 ]]-- +o = s:option(Value, "grpc_serviceName", "ServiceName") +add_xray_depends(o, { transport = "grpc" }) +add_v2ray_depends(o, { transport = "grpc" }) + +o = s:option(ListValue, "grpc_mode", "gRPC " .. translate("Transfer mode")) +o:value("gun") +o:value("multi") +add_xray_depends(o, { transport = "grpc" }) + +o = s:option(Flag, "grpc_health_check", translate("Health check")) +add_xray_depends(o, { transport = "grpc" }) + +o = s:option(Value, "grpc_idle_timeout", translate("Idle timeout")) +o.default = "10" +add_xray_depends(o, { grpc_health_check = true }) + +o = s:option(Value, "grpc_health_check_timeout", translate("Health check timeout")) +o.default = "20" +add_xray_depends(o, { grpc_health_check = true }) + +o = s:option(Flag, "grpc_permit_without_stream", translate("Permit without stream")) +o.default = "0" +add_xray_depends(o, { grpc_health_check = true }) + +o = s:option(Value, "grpc_initial_windows_size", translate("Initial Windows Size")) +o.default = "0" +add_xray_depends(o, { transport = "grpc" }) + +-- [[ Mux ]]-- +o = s:option(Flag, "mux", translate("Mux")) +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "http" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless", xray_tlsflow = "" }) +add_xray_depends(o, { xray_protocol = "http" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "trojan" }) + +o = s:option(Value, "mux_concurrency", translate("Mux concurrency")) +o.default = 8 +add_xray_depends(o, { mux = true }) +add_v2ray_depends(o, { mux = true }) + +-- [[ XUDP Mux ]]-- +o = s:option(Flag, "xmux", translate("xMux")) +o.default = 1 +add_xray_depends(o, { xray_protocol = "vless", xray_tlsflow = "xtls-rprx-vision" }) +add_xray_depends(o, { xray_protocol = "vless", xray_tlsflow = "xtls-rprx-vision-udp443" }) + +o = s:option(Value, "xudp_concurrency", translate("XUDP Mux concurrency")) +o.default = 8 +add_xray_depends(o, { xmux = true }) + +s.fields["xray_protocol"].validate = function(self, value) + if value == "_shunt" or value == "_balancing" then + s.fields["xray_address"].rmempty = true + s.fields["xray_port"].rmempty = true + end + return value +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss-rust.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss-rust.lua new file mode 100644 index 000000000..fc40f7b6a --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss-rust.lua @@ -0,0 +1,82 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("sslocal") then + return +end + +local option_prefix = "ssrust_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +local ssrust_encrypt_method_list = { + "plain", "none", + "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", + "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305" +} + +-- [[ Shadowsocks Rust ]] + +s.fields["type"]:value("SS-Rust", translate("Shadowsocks Rust")) + +o = s:option(Value, "ssrust_address", translate("Address (Support Domain Name)")) + +o = s:option(Value, "ssrust_port", translate("Port")) +o.datatype = "port" + +o = s:option(Value, "ssrust_password", translate("Password")) +o.password = true + +o = s:option(Value, "ssrust_method", translate("Encrypt Method")) +for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end + +o = s:option(Value, "ssrust_timeout", translate("Connection Timeout")) +o.datatype = "uinteger" +o.default = 300 + +o = s:option(ListValue, "ssrust_tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required")) +o:value("false") +o:value("true") + +o = s:option(ListValue, "ssrust_plugin", translate("plugin")) +o:value("none", translate("none")) +if api.is_finded("xray-plugin") then o:value("xray-plugin") end +if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end +if api.is_finded("obfs-local") then o:value("obfs-local") end + +o = s:option(Value, "ssrust_plugin_opts", translate("opts")) +o:depends({ ssrust_plugin = "xray-plugin"}) +o:depends({ ssrust_plugin = "v2ray-plugin"}) +o:depends({ ssrust_plugin = "obfs-local"}) + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "SS-Rust" + end + else + s.fields[key]:depends({ type = "SS-Rust" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss.lua new file mode 100644 index 000000000..8dbead41b --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ss.lua @@ -0,0 +1,83 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("ss-redir") then + return +end + +local option_prefix = "ss_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +local ss_encrypt_method_list = { + "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", + "aes-192-ctr", "aes-256-ctr", "bf-cfb", "salsa20", "chacha20", "chacha20-ietf", + "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", + "xchacha20-ietf-poly1305" +} + +-- [[ Shadowsocks Libev ]] + +s.fields["type"]:value("SS", translate("Shadowsocks Libev")) + +o = s:option(Value, "ss_address", translate("Address (Support Domain Name)")) + +o = s:option(Value, "ss_port", translate("Port")) +o.datatype = "port" + +o = s:option(Value, "ss_password", translate("Password")) +o.password = true + +o = s:option(Value, "ss_method", translate("Encrypt Method")) +for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end + +o = s:option(Value, "ss_timeout", translate("Connection Timeout")) +o.datatype = "uinteger" +o.default = 300 + +o = s:option(ListValue, "ss_tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required")) +o:value("false") +o:value("true") + +o = s:option(ListValue, "ss_plugin", translate("plugin")) +o:value("none", translate("none")) +if api.is_finded("xray-plugin") then o:value("xray-plugin") end +if api.is_finded("v2ray-plugin") then o:value("v2ray-plugin") end +if api.is_finded("obfs-local") then o:value("obfs-local") end + +o = s:option(Value, "ss_plugin_opts", translate("opts")) +o:depends({ ss_plugin = "xray-plugin"}) +o:depends({ ss_plugin = "v2ray-plugin"}) +o:depends({ ss_plugin = "obfs-local"}) + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "SS" + end + else + s.fields[key]:depends({ type = "SS" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ssr.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ssr.lua new file mode 100644 index 000000000..0a675255e --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/ssr.lua @@ -0,0 +1,94 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("ssr-redir") then + return +end + +local option_prefix = "ssr_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +local ssr_encrypt_method_list = { + "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", + "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", + "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", + "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20", + "chacha20-ietf" +} + +local ssr_protocol_list = { + "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple", + "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5", + "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c", + "auth_chain_d", "auth_chain_e", "auth_chain_f" +} +local ssr_obfs_list = { + "plain", "http_simple", "http_post", "random_head", "tls_simple", + "tls1.0_session_auth", "tls1.2_ticket_auth" +} + +-- [[ ShadowsocksR Libev ]] + +s.fields["type"]:value("SSR", translate("ShadowsocksR Libev")) + +o = s:option(Value, "ssr_address", translate("Address (Support Domain Name)")) + +o = s:option(Value, "ssr_port", translate("Port")) +o.datatype = "port" + +o = s:option(Value, "ssr_password", translate("Password")) +o.password = true + +o = s:option(ListValue, "ssr_method", translate("Encrypt Method")) +for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end + +o = s:option(ListValue, "ssr_protocol", translate("Protocol")) +for a, t in ipairs(ssr_protocol_list) do o:value(t) end + +o = s:option(Value, "ssr_protocol_param", translate("Protocol_param")) + +o = s:option(ListValue, "ssr_obfs", translate("Obfs")) +for a, t in ipairs(ssr_obfs_list) do o:value(t) end + +o = s:option(Value, "ssr_obfs_param", translate("Obfs_param")) + +o = s:option(Value, "ssr_timeout", translate("Connection Timeout")) +o.datatype = "uinteger" +o.default = 300 + +o = s:option(ListValue, "ssr_tcp_fast_open", "TCP " .. translate("Fast Open"), translate("Need node support required")) +o:value("false") +o:value("true") + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "SSR" + end + else + s.fields[key]:depends({ type = "SSR" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/tuic.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/tuic.lua new file mode 100644 index 000000000..8dcd30c48 --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/client/type/tuic.lua @@ -0,0 +1,158 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("tuic-client") then + return +end + +local option_prefix = "tuic_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +-- [[ TUIC ]] + +s.fields["type"]:value("TUIC", translate("TUIC")) + +o = s:option(Value, "tuic_address", translate("Address (Support Domain Name)")) + +o = s:option(Value, "tuic_port", translate("Port")) +o.datatype = "port" + +o = s:option(Value, "tuic_uuid", translate("ID")) +o.password = true + +-- Tuic Password for remote server connect +o = s:option(Value, "tuic_password", translate("TUIC User Password For Connect Remote Server")) +o.password = true +o.rmempty = true +o.default = "" +o.not_rewrite = true + +--[[ +-- Tuic username for local socks connect +o = s:option(Value, "tuic_socks_username", translate("TUIC UserName For Local Socks")) +o.rmempty = true +o.default = "" +o.not_rewrite = true + +-- Tuic Password for local socks connect +o = s:option(Value, "tuic_socks_password", translate("TUIC Password For Local Socks")) +o.password = true +o.rmempty = true +o.default = "" +o.not_rewrite = true +--]] + +o = s:option(Value, "tuic_ip", translate("Set the TUIC proxy server ip address")) +o.datatype = "ipaddr" +o.rmempty = true +o.not_rewrite = true + +o = s:option(ListValue, "tuic_udp_relay_mode", translate("UDP relay mode")) +o:value("native", translate("native")) +o:value("quic", translate("QUIC")) +o.default = "native" +o.rmempty = true +o.not_rewrite = true + +o = s:option(ListValue, "tuic_congestion_control", translate("Congestion control algorithm")) +o:value("bbr", translate("BBR")) +o:value("cubic", translate("CUBIC")) +o:value("new_reno", translate("New Reno")) +o.default = "cubic" +o.rmempty = true +o.not_rewrite = true + +o = s:option(Value, "tuic_heartbeat", translate("Heartbeat interval(second)")) +o.datatype = "uinteger" +o.default = "3" +o.rmempty = true +o.not_rewrite = true + +o = s:option(Value, "tuic_timeout", translate("Timeout for establishing a connection to server(second)")) +o.datatype = "uinteger" +o.default = "8" +o.rmempty = true +o.not_rewrite = true + +o = s:option(Value, "tuic_gc_interval", translate("Garbage collection interval(second)")) +o.datatype = "uinteger" +o.default = "3" +o.rmempty = true +o.not_rewrite = true + +o = s:option(Value, "tuic_gc_lifetime", translate("Garbage collection lifetime(second)")) +o.datatype = "uinteger" +o.default = "15" +o.rmempty = true +o.not_rewrite = true + +o = s:option(Value, "tuic_send_window", translate("TUIC send window")) +o.datatype = "uinteger" +o.default = 20971520 +o.rmempty = true +o.not_rewrite = true + +o = s:option(Value, "tuic_receive_window", translate("TUIC receive window")) +o.datatype = "uinteger" +o.default = 10485760 +o.rmempty = true +o.not_rewrite = true + +o = s:option(Value, "tuic_max_package_size", translate("TUIC Maximum packet size the socks5 server can receive from external, in bytes")) +o.datatype = "uinteger" +o.default = 1500 +o.rmempty = true +o.not_rewrite = true + +--Tuic settings for the local inbound socks5 server +o = s:option(Flag, "tuic_dual_stack", translate("Set if the listening socket should be dual-stack")) +o.default = 0 +o.rmempty = true +o.not_rewrite = true + +o = s:option(Flag, "tuic_disable_sni", translate("Disable SNI")) +o.default = 0 +o.rmempty = true +o.not_rewrite = true + +o = s:option(Flag, "tuic_zero_rtt_handshake", translate("Enable 0-RTT QUIC handshake")) +o.default = 0 +o.rmempty = true +o.not_rewrite = true + +o = s:option(DynamicList, "tuic_tls_alpn", translate("TLS ALPN")) +o.rmempty = true +o.not_rewrite = true + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "TUIC" + end + else + s.fields[key]:depends({ type = "TUIC" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/brook.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/brook.lua new file mode 100644 index 000000000..e59f29726 --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/brook.lua @@ -0,0 +1,65 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("brook") then + return +end + +local option_prefix = "brook_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +-- [[ Brook ]] + +s.fields["type"]:value("Brook", translate("Brook")) + +o = s:option(Value, "brook_port", translate("Listen Port")) +o.datatype = "port" + +o = s:option(ListValue, "brook_protocol", translate("Protocol")) +o:value("server", "Brook") +o:value("wsserver", "WebSocket") + +--o = s:option(Flag, "brook_tls", translate("Use TLS")) +--o:depends({ brook_protocol = "wsserver" }) + +o = s:option(Value, "brook_password", translate("Password")) +o.password = true + +o = s:option(Value, "brook_ws_path", translate("WebSocket Path")) +o:depends({ brook_protocol = "wsserver" }) + +o = s:option(Flag, "brook_log", translate("Log")) +o.default = "1" + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "Brook" + end + else + s.fields[key]:depends({ type = "Brook" }) + end + end +end \ No newline at end of file diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria.lua new file mode 100644 index 000000000..80aa42053 --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/hysteria.lua @@ -0,0 +1,137 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("hysteria") then + return +end + +local option_prefix = "hysteria_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +-- [[ Hysteria ]] + +s.fields["type"]:value("Hysteria", translate("Hysteria")) + +o = s:option(Value, "hysteria_port", translate("Listen Port")) +o.datatype = "port" + +o = s:option(ListValue, "hysteria_protocol", translate("Protocol")) +o:value("udp", "UDP") +o:value("faketcp", "faketcp") +o:value("wechat-video", "wechat-video") + +o = s:option(Value, "hysteria_obfs", translate("Obfs Password")) +o.not_rewrite = true + +o = s:option(ListValue, "hysteria_auth_type", translate("Auth Type")) +o:value("disable", translate("Disable")) +o:value("string", translate("STRING")) +o.not_rewrite = true + +o = s:option(Value, "hysteria_auth_password", translate("Auth Password")) +o.password = true +o:depends({ hysteria_auth_type = "string" }) +o.not_rewrite = true + +o = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN")) +o.not_rewrite = true + +o = s:option(Flag, "hysteria_udp", translate("UDP")) +o.default = "1" +o.not_rewrite = true + +o = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps")) +o.default = "10" +o.not_rewrite = true + +o = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps")) +o.default = "50" +o.not_rewrite = true + +o = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window")) +o.not_rewrite = true + +o = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window")) +o.not_rewrite = true + +o = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection")) +o.not_rewrite = true + +o = s:option(Flag, "hysteria_tls", translate("TLS")) +o.default = 0 +o.validate = function(self, value, t) + if value then + if value == "1" then + local ca = s.fields["hysteria_tls_certificateFile"]:formvalue(t) or "" + local key = s.fields["hysteria_tls_keyFile"]:formvalue(t) or "" + if ca == "" or key == "" then + return nil, translate("Public key and Private key path can not be empty!") + end + end + return value + end +end + +o = s:option(FileUpload, "hysteria_tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") +o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem" +o:depends({ hysteria_tls = true }) +o.validate = function(self, value, t) + if value and value ~= "" then + if not nixio.fs.access(value) then + return nil, translate("Can't find this file!") + else + return value + end + end + return nil +end + +o = s:option(FileUpload, "hysteria_tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key") +o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key" +o:depends({ hysteria_tls = true }) +o.validate = function(self, value, t) + if value and value ~= "" then + if not nixio.fs.access(value) then + return nil, translate("Can't find this file!") + else + return value + end + end + return nil +end + +o = s:option(Flag, "hysteria_log", translate("Log")) +o.default = "1" + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "Hysteria" + end + else + s.fields[key]:depends({ type = "Hysteria" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua new file mode 100644 index 000000000..3de50fd64 --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ray.lua @@ -0,0 +1,536 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("xray") and not api.is_finded("v2ray")then + return +end + +local option_prefix = "xray_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +local function add_xray_depends(o, field, value) + local deps = { type = "Xray" } + if field then + if type(field) == "string" then + deps[field] = value + else + for key, value in pairs(field) do + deps[key] = value + end + end + end + o:depends(deps) +end + +local function add_v2ray_depends(o, field, value) + local deps = { type = "V2ray" } + if field then + if type(field) == "string" then + deps[field] = value + else + for key, value in pairs(field) do + deps[key] = value + end + end + end + o:depends(deps) +end + +local v_ss_encrypt_method_list = { + "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305" +} + +local x_ss_encrypt_method_list = { + "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" +} + +local header_type_list = { + "none", "srtp", "utp", "wechat-video", "dtls", "wireguard" +} + +-- [[ Xray ]] + +if api.is_finded("v2ray") then + s.fields["type"]:value("V2ray", translate("V2ray")) +end +if api.is_finded("xray") then + s.fields["type"]:value("Xray", translate("Xray")) +end + +o = s:option(ListValue, "xray_protocol", translate("Protocol")) +o:value("vmess", "Vmess") +o:value("vless", "VLESS") +o:value("http", "HTTP") +o:value("socks", "Socks") +o:value("shadowsocks", "Shadowsocks") +o:value("trojan", "Trojan") +o:value("dokodemo-door", "dokodemo-door") +add_xray_depends(o) +add_v2ray_depends(o) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_port", translate("Listen Port")) +o.datatype = "port" +add_xray_depends(o) +add_v2ray_depends(o) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Flag, "xray_auth", translate("Auth")) +o.validate = function(self, value, t) + if value and value == "1" then + local user_v = s.fields["xray_username"]:formvalue(t) or "" + local pass_v = s.fields["xray_password"]:formvalue(t) or "" + if user_v == "" or pass_v == "" then + return nil, translate("Username and Password must be used together!") + end + end + return value +end +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "http" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "http" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_username", translate("Username")) +add_xray_depends(o, { xray_auth = true }) +add_v2ray_depends(o, { xray_auth = true }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_password", translate("Password")) +o.password = true +add_xray_depends(o, { xray_auth = true }) +add_v2ray_depends(o, { xray_auth = true }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(ListValue, "d_protocol", translate("Destination protocol")) +o:value("tcp", "TCP") +o:value("udp", "UDP") +o:value("tcp,udp", "TCP,UDP") +add_v2ray_depends(o, { xray_protocol = "dokodemo-door" }) +add_xray_depends(o, { xray_protocol = "dokodemo-door" }) + +o = s:option(Value, "d_address", translate("Destination address")) +add_v2ray_depends(o, { xray_protocol = "dokodemo-door" }) +add_xray_depends(o, { xray_protocol = "dokodemo-door" }) + +o = s:option(Value, "d_port", translate("Destination port")) +o.datatype = "port" +add_v2ray_depends(o, { xray_protocol = "dokodemo-door" }) +add_xray_depends(o, { xray_protocol = "dokodemo-door" }) + +o = s:option(Value, "decryption", translate("Encrypt Method")) +o.default = "none" +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_xray_depends(o, { xray_protocol = "vless" }) + +o = s:option(ListValue, "v_ss_encrypt_method", translate("Encrypt Method")) +for a, t in ipairs(v_ss_encrypt_method_list) do o:value(t) end +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +function o.cfgvalue(self, section) + return m:get(section, "method") +end +function o.write(self, section, value) + m:set(section, "method", value) +end + +o = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method")) +for a, t in ipairs(x_ss_encrypt_method_list) do o:value(t) end +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +function o.cfgvalue(self, section) + return m:get(section, "method") +end +function o.write(self, section, value) + m:set(section, "method", value) +end + +o = s:option(Flag, "iv_check", translate("IV Check")) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) + +o = s:option(ListValue, "ss_network", translate("Transport")) +o.default = "tcp,udp" +o:value("tcp", "TCP") +o:value("udp", "UDP") +o:value("tcp,udp", "TCP,UDP") +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) + +o = s:option(Flag, "udp_forward", translate("UDP Forward")) +o.default = "1" +o.rmempty = false +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "socks" }) + +o = s:option(DynamicList, "xray_uuid", translate("ID") .. "/" .. translate("Password")) +for i = 1, 3 do + o:value(api.gen_uuid(1)) +end +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless" }) +add_xray_depends(o, { xray_protocol = "trojan" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Flag, "xray_tls", translate("TLS")) +o.default = 0 +o.validate = function(self, value, t) + if value then + if value == "1" then + local ca = s.fields["xray_tls_certificateFile"]:formvalue(t) or "" + local key = s.fields["xray_tls_keyFile"]:formvalue(t) or "" + if ca == "" or key == "" then + return nil, translate("Public key and Private key path can not be empty!") + end + end + return value + end +end +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "trojan" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "tlsflow", translate("flow")) +o.default = "" +o:value("", translate("Disable")) +o:value("xtls-rprx-vision") +o:value("xtls-rprx-vision-udp443") +add_xray_depends(o, { xray_protocol = "vless", xray_tls = true }) + +o = s:option(ListValue, "alpn", translate("alpn")) +o.default = "h2,http/1.1" +o:value("h2,http/1.1") +o:value("h2") +o:value("http/1.1") +add_v2ray_depends(o, { xray_tls = true }) +add_xray_depends(o, { xray_tls = true }) + +-- o = s:option(Value, "minversion", translate("minversion")) +-- o.default = "1.3" +-- o:value("1.3") +--add_v2ray_depends(o, { xray_tls = true }) +--add_xray_depends(o, { xray_tls = true }) + +-- [[ TLS部分 ]] -- + +o = s:option(FileUpload, "xray_tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") +o.default = m:get(s.section, "tls_certificateFile") or "/etc/config/ssl/" .. arg[1] .. ".pem" +add_v2ray_depends(o, { xray_tls = true }) +add_xray_depends(o, { xray_tls = true }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write +o.validate = function(self, value, t) + if value and value ~= "" then + if not nixio.fs.access(value) then + return nil, translate("Can't find this file!") + else + return value + end + end + return nil +end + +o = s:option(FileUpload, "xray_tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key") +o.default = m:get(s.section, "tls_keyFile") or "/etc/config/ssl/" .. arg[1] .. ".key" +add_v2ray_depends(o, { xray_tls = true }) +add_xray_depends(o, { xray_tls = true }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write +o.validate = function(self, value, t) + if value and value ~= "" then + if not nixio.fs.access(value) then + return nil, translate("Can't find this file!") + else + return value + end + end + return nil +end + +o = s:option(ListValue, "transport", translate("Transport")) +o:value("tcp", "TCP") +o:value("mkcp", "mKCP") +o:value("ws", "WebSocket") +o:value("h2", "HTTP/2") +o:value("ds", "DomainSocket") +o:value("quic", "QUIC") +o:value("grpc", "gRPC") +add_v2ray_depends(o, { xray_protocol = "vmess" }) +add_v2ray_depends(o, { xray_protocol = "vless" }) +add_v2ray_depends(o, { xray_protocol = "socks" }) +add_v2ray_depends(o, { xray_protocol = "shadowsocks" }) +add_v2ray_depends(o, { xray_protocol = "trojan" }) +add_xray_depends(o, { xray_protocol = "vmess" }) +add_xray_depends(o, { xray_protocol = "vless" }) +add_xray_depends(o, { xray_protocol = "socks" }) +add_xray_depends(o, { xray_protocol = "shadowsocks" }) +add_xray_depends(o, { xray_protocol = "trojan" }) + +-- [[ WebSocket部分 ]]-- + +o = s:option(Value, "xray_ws_host", translate("WebSocket Host")) +add_v2ray_depends(o, { transport = "ws" }) +add_xray_depends(o, { transport = "ws" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_ws_path", translate("WebSocket Path")) +add_v2ray_depends(o, { transport = "ws" }) +add_xray_depends(o, { transport = "ws" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +-- [[ HTTP/2部分 ]]-- + +o = s:option(Value, "xray_h2_host", translate("HTTP/2 Host")) +add_v2ray_depends(o, { transport = "h2" }) +add_xray_depends(o, { transport = "h2" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(Value, "xray_h2_path", translate("HTTP/2 Path")) +add_v2ray_depends(o, { transport = "h2" }) +add_xray_depends(o, { transport = "h2" }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +-- [[ TCP部分 ]]-- + +-- TCP伪装 +o = s:option(ListValue, "tcp_guise", translate("Camouflage Type")) +o:value("none", "none") +o:value("http", "http") +add_v2ray_depends(o, { transport = "tcp" }) +add_xray_depends(o, { transport = "tcp" }) + +-- HTTP域名 +o = s:option(DynamicList, "tcp_guise_http_host", translate("HTTP Host")) +add_v2ray_depends(o, { tcp_guise = "http" }) +add_xray_depends(o, { tcp_guise = "http" }) + +-- HTTP路径 +o = s:option(DynamicList, "tcp_guise_http_path", translate("HTTP Path")) +add_v2ray_depends(o, { tcp_guise = "http" }) +add_xray_depends(o, { tcp_guise = "http" }) + +-- [[ mKCP部分 ]]-- + +o = s:option(ListValue, "mkcp_guise", translate("Camouflage Type"), translate('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
wireguard: disguised as a WireGuard packet. (not really WireGuard protocol)')) +for a, t in ipairs(header_type_list) do o:value(t) end +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_mtu", translate("KCP MTU")) +o.default = "1350" +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_tti", translate("KCP TTI")) +o.default = "20" +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity")) +o.default = "5" +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity")) +o.default = "20" +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +o = s:option(Flag, "mkcp_congestion", translate("KCP Congestion")) +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_readBufferSize", translate("KCP readBufferSize")) +o.default = "1" +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize")) +o.default = "1" +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +o = s:option(Value, "mkcp_seed", translate("KCP Seed")) +add_v2ray_depends(o, { transport = "mkcp" }) +add_xray_depends(o, { transport = "mkcp" }) + +-- [[ DomainSocket部分 ]]-- + +o = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running.")) +add_v2ray_depends(o, { transport = "ds" }) +add_xray_depends(o, { transport = "ds" }) + +-- [[ QUIC部分 ]]-- +o = s:option(ListValue, "quic_security", translate("Encrypt Method")) +o:value("none") +o:value("aes-128-gcm") +o:value("chacha20-poly1305") +add_v2ray_depends(o, { transport = "quic" }) +add_xray_depends(o, { transport = "quic" }) + +o = s:option(Value, "quic_key", translate("Encrypt Method") .. translate("Key")) +add_v2ray_depends(o, { transport = "quic" }) +add_xray_depends(o, { transport = "quic" }) + +o = s:option(ListValue, "quic_guise", translate("Camouflage Type")) +for a, t in ipairs(header_type_list) do o:value(t) end +add_v2ray_depends(o, { transport = "quic" }) +add_xray_depends(o, { transport = "quic" }) + +-- [[ gRPC部分 ]]-- +o = s:option(Value, "grpc_serviceName", "ServiceName") +add_v2ray_depends(o, { transport = "grpc" }) +add_xray_depends(o, { transport = "grpc" }) + +o = s:option(Flag, "acceptProxyProtocol", translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used.")) +add_v2ray_depends(o, { transport = "tcp" }) +add_v2ray_depends(o, { transport = "ws" }) +add_xray_depends(o, { transport = "tcp" }) +add_xray_depends(o, { transport = "ws" }) + +-- [[ Fallback部分 ]]-- +o = s:option(Flag, "fallback", translate("Fallback")) +add_v2ray_depends(o, { xray_protocol = "vless", transport = "tcp" }) +add_v2ray_depends(o, { xray_protocol = "trojan", transport = "tcp" }) +add_xray_depends(o, { xray_protocol = "vless", transport = "tcp" }) +add_xray_depends(o, { xray_protocol = "trojan", transport = "tcp" }) + +--[[ +o = s:option(Value, "fallback_alpn", "Fallback alpn") +add_v2ray_depends(o, { fallback = true }) +add_xray_depends(o, { fallback = true }) + +o = s:option(Value, "fallback_path", "Fallback path") +add_v2ray_depends(o, { fallback = true }) +add_xray_depends(o, { fallback = true }) + +o = s:option(Value, "fallback_dest", "Fallback dest") +add_v2ray_depends(o, { fallback = true }) +add_xray_depends(o, { fallback = true }) + +o = s:option(Value, "fallback_xver", "Fallback xver") +o.default = 0 +add_v2ray_depends(o, { fallback = true }) +add_xray_depends(o, { fallback = true }) +]]-- + +o = s:option(DynamicList, "fallback_list", "Fallback", translate("dest,path")) +add_v2ray_depends(o, { fallback = true }) +add_xray_depends(o, { fallback = true }) + +o = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed locally, It is recommended to turn on when using reverse proxies or be fallback.")) +o.default = "0" +add_v2ray_depends(o) +add_xray_depends(o) + +o = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!")) +o.default = "0" +add_v2ray_depends(o) +add_xray_depends(o) + +local nodes_table = {} +for k, e in ipairs(api.get_valid_nodes()) do + if e.node_type == "normal" and (e.type == "V2ray" or e.type == "Xray") then + nodes_table[#nodes_table + 1] = { + id = e[".name"], + remarks = e["remark"] + } + end +end + +o = s:option(ListValue, "outbound_node", translate("outbound node")) +o:value("nil", translate("Close")) +o:value("_socks", translate("Custom Socks")) +o:value("_http", translate("Custom HTTP")) +o:value("_iface", translate("Custom Interface") .. " (Only Support Xray)") +for k, v in pairs(nodes_table) do o:value(v.id, v.remarks) end +o.default = "nil" +add_v2ray_depends(o) +add_xray_depends(o) + +o = s:option(Value, "outbound_node_address", translate("Address (Support Domain Name)")) +add_v2ray_depends(o, { outbound_node = "_socks"}) +add_v2ray_depends(o, { outbound_node = "_http"}) +add_xray_depends(o, { outbound_node = "_socks"}) +add_xray_depends(o, { outbound_node = "_http"}) + +o = s:option(Value, "outbound_node_port", translate("Port")) +o.datatype = "port" +add_v2ray_depends(o, { outbound_node = "_socks"}) +add_v2ray_depends(o, { outbound_node = "_http"}) +add_xray_depends(o, { outbound_node = "_socks"}) +add_xray_depends(o, { outbound_node = "_http"}) + +o = s:option(Value, "outbound_node_username", translate("Username")) +add_v2ray_depends(o, { outbound_node = "_socks"}) +add_v2ray_depends(o, { outbound_node = "_http"}) +add_xray_depends(o, { outbound_node = "_socks"}) +add_xray_depends(o, { outbound_node = "_http"}) + +o = s:option(Value, "outbound_node_password", translate("Password")) +o.password = true +add_v2ray_depends(o, { outbound_node = "_socks"}) +add_v2ray_depends(o, { outbound_node = "_http"}) +add_xray_depends(o, { outbound_node = "_socks"}) +add_xray_depends(o, { outbound_node = "_http"}) + +o = s:option(Value, "outbound_node_iface", translate("Interface")) +o.default = "eth1" +add_v2ray_depends(o, { outbound_node = "_iface"}) +add_xray_depends(o, { outbound_node = "_iface"}) + +o = s:option(Flag, "xray_log", translate("Log")) +o.default = "1" +add_v2ray_depends(o) +add_xray_depends(o) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write + +o = s:option(ListValue, "xray_loglevel", translate("Log Level")) +o.default = "warning" +o:value("debug") +o:value("info") +o:value("warning") +o:value("error") +add_v2ray_depends(o, { xray_log = true }) +add_xray_depends(o, { xray_log = true }) +o.cfgvalue = rm_prefix_cfgvalue +o.write = rm_prefix_write diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua new file mode 100644 index 000000000..1d3fc6aeb --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss-rust.lua @@ -0,0 +1,71 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("ssserver") then + return +end + +local option_prefix = "ssrust_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +local ssrust_encrypt_method_list = { + "plain", "none", + "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", + "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305" +} + +-- [[ Shadowsocks Rust ]] + +s.fields["type"]:value("SS-Rust", translate("Shadowsocks Rust")) + +o = s:option(Value, "ssrust_port", translate("Listen Port")) +o.datatype = "port" + +o = s:option(Value, "ssrust_password", translate("Password")) +o.password = true + +o = s:option(ListValue, "ssrust_method", translate("Encrypt Method")) +for a, t in ipairs(ssrust_encrypt_method_list) do o:value(t) end + +o = s:option(Value, "ssrust_timeout", translate("Connection Timeout")) +o.datatype = "uinteger" +o.default = 300 + +o = s:option(Flag, "ssrust_tcp_fast_open", "TCP " .. translate("Fast Open")) +o.default = "0" + +o = s:option(Flag, "ssrust_log", translate("Log")) +o.default = "1" + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "SS-Rust" + end + else + s.fields[key]:depends({ type = "SS-Rust" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss.lua new file mode 100644 index 000000000..72790ba64 --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ss.lua @@ -0,0 +1,74 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("ss-server") then + return +end + +local option_prefix = "ss_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +local ss_encrypt_method_list = { + "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", + "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", + "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20", + "chacha20-ietf", -- aead + "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", + "xchacha20-ietf-poly1305" +} + +-- [[ Shadowsocks ]] + +s.fields["type"]:value("SS", translate("Shadowsocks")) + +o = s:option(Value, "ss_port", translate("Listen Port")) +o.datatype = "port" + +o = s:option(Value, "ss_password", translate("Password")) +o.password = true + +o = s:option(ListValue, "ss_method", translate("Encrypt Method")) +for a, t in ipairs(ss_encrypt_method_list) do o:value(t) end + +o = s:option(Value, "ss_timeout", translate("Connection Timeout")) +o.datatype = "uinteger" +o.default = 300 + +o = s:option(Flag, "ss_tcp_fast_open", "TCP " .. translate("Fast Open")) +o.default = "0" + +o = s:option(Flag, "ss_log", translate("Log")) +o.default = "1" + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "SS" + end + else + s.fields[key]:depends({ type = "SS" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua new file mode 100644 index 000000000..f053bfa3a --- /dev/null +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/type/ssr.lua @@ -0,0 +1,98 @@ +local m, s = ... + +local api = require "luci.passwall2.api" + +if not api.is_finded("ssr-server") then + return +end + +local option_prefix = "ssr_" + +local function option_name(name) + return option_prefix .. name +end + +local function rm_prefix_cfgvalue(self, section) + if self.option:find(option_prefix) == 1 then + return m:get(section, self.option:sub(1 + #option_prefix)) + end +end +local function rm_prefix_write(self, section, value) + if self.option:find(option_prefix) == 1 then + m:set(section, self.option:sub(1 + #option_prefix), value) + end +end + +local ssr_encrypt_method_list = { + "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", + "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", + "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", + "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20", + "chacha20-ietf" +} + +local ssr_protocol_list = { + "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple", + "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5", + "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c", + "auth_chain_d", "auth_chain_e", "auth_chain_f" +} +local ssr_obfs_list = { + "plain", "http_simple", "http_post", "random_head", "tls_simple", + "tls1.0_session_auth", "tls1.2_ticket_auth" +} + +-- [[ ShadowsocksR ]] + +s.fields["type"]:value("SSR", translate("ShadowsocksR")) + +o = s:option(Value, "ssr_port", translate("Listen Port")) +o.datatype = "port" + +o = s:option(Value, "ssr_password", translate("Password")) +o.password = true + +o = s:option(ListValue, "ssr_method", translate("Encrypt Method")) +for a, t in ipairs(ssr_encrypt_method_list) do o:value(t) end + +o = s:option(ListValue, "ssr_protocol", translate("Protocol")) +for a, t in ipairs(ssr_protocol_list) do o:value(t) end + +o = s:option(Value, "ssr_protocol_param", translate("Protocol_param")) + +o = s:option(ListValue, "ssr_obfs", translate("Obfs")) +for a, t in ipairs(ssr_obfs_list) do o:value(t) end + +o = s:option(Value, "ssr_obfs_param", translate("Obfs_param")) + +o = s:option(Value, "ssr_timeout", translate("Connection Timeout")) +o.datatype = "uinteger" +o.default = 300 + +o = s:option(Flag, "ssr_tcp_fast_open", "TCP " .. translate("Fast Open")) +o.default = "0" + +o = s:option(Flag, "ssr_udp_forward", translate("UDP Forward")) +o.default = "1" +o.rmempty = false + +o = s:option(Flag, "ssr_log", translate("Log")) +o.default = "1" + +for key, value in pairs(s.fields) do + if key:find(option_prefix) == 1 then + if not s.fields[key].not_rewrite then + s.fields[key].cfgvalue = rm_prefix_cfgvalue + s.fields[key].write = rm_prefix_write + end + + local deps = s.fields[key].deps + if #deps > 0 then + for index, value in ipairs(deps) do + deps[index]["type"] = "SSR" + end + else + s.fields[key]:depends({ type = "SSR" }) + end + end +end diff --git a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua index 1936256c4..7c31ef33d 100644 --- a/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua +++ b/luci-app-passwall2/luasrc/model/cbi/passwall2/server/user.lua @@ -1,56 +1,6 @@ local api = require "luci.passwall2.api" - -local ss_encrypt_method_list = { - "rc4-md5", "aes-128-cfb", "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", - "aes-192-ctr", "aes-256-ctr", "bf-cfb", "camellia-128-cfb", - "camellia-192-cfb", "camellia-256-cfb", "salsa20", "chacha20", - "chacha20-ietf", -- aead - "aes-128-gcm", "aes-192-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", - "xchacha20-ietf-poly1305" -} - -local ss_rust_encrypt_method_list = { - "plain", "none", - "aes-128-gcm", "aes-256-gcm", "chacha20-ietf-poly1305", - "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305" -} - -local ssr_encrypt_method_list = { - "none", "table", "rc2-cfb", "rc4", "rc4-md5", "rc4-md5-6", "aes-128-cfb", - "aes-192-cfb", "aes-256-cfb", "aes-128-ctr", "aes-192-ctr", "aes-256-ctr", - "bf-cfb", "camellia-128-cfb", "camellia-192-cfb", "camellia-256-cfb", - "cast5-cfb", "des-cfb", "idea-cfb", "seed-cfb", "salsa20", "chacha20", - "chacha20-ietf" -} - -local ssr_protocol_list = { - "origin", "verify_simple", "verify_deflate", "verify_sha1", "auth_simple", - "auth_sha1", "auth_sha1_v2", "auth_sha1_v4", "auth_aes128_md5", - "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c", - "auth_chain_d", "auth_chain_e", "auth_chain_f" -} -local ssr_obfs_list = { - "plain", "http_simple", "http_post", "random_head", "tls_simple", - "tls1.0_session_auth", "tls1.2_ticket_auth" -} - -local v_ss_encrypt_method_list = { - "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305" -} - -local x_ss_encrypt_method_list = { - "aes-128-gcm", "aes-256-gcm", "chacha20-poly1305", "xchacha20-poly1305", "2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha20-poly1305" -} - -local header_type_list = { - "none", "srtp", "utp", "wechat-video", "dtls", "wireguard" -} - -local encrypt_methods_ss_aead = { - "chacha20-ietf-poly1305", - "aes-128-gcm", - "aes-256-gcm", -} +local fs = require "nixio.fs" +local types_dir = "/usr/lib/lua/luci/model/cbi/passwall2/server/type/" m = Map("passwall2_server", translate("Server Config")) m.redirect = api.url("server") @@ -59,562 +9,25 @@ s = m:section(NamedSection, arg[1], "user", "") s.addremove = false s.dynamic = false -enable = s:option(Flag, "enable", translate("Enable")) -enable.default = "1" -enable.rmempty = false +o = s:option(Flag, "enable", translate("Enable")) +o.default = "1" +o.rmempty = false -remarks = s:option(Value, "remarks", translate("Remarks")) -remarks.default = translate("Remarks") -remarks.rmempty = false +o = s:option(Value, "remarks", translate("Remarks")) +o.default = translate("Remarks") +o.rmempty = false -type = s:option(ListValue, "type", translate("Type")) -if api.is_finded("ss-server") then - type:value("SS", translate("Shadowsocks")) +o = s:option(ListValue, "type", translate("Type")) + +local type_table = {} +for filename in fs.dir(types_dir) do + table.insert(type_table, filename) end -if api.is_finded("ssserver") then - type:value("SS-Rust", translate("Shadowsocks Rust")) +table.sort(type_table) + +for index, value in ipairs(type_table) do + local p_func = loadfile(types_dir .. value) + setfenv(p_func, getfenv(1))(m, s) end -if api.is_finded("ssr-server") then - type:value("SSR", translate("ShadowsocksR")) -end -if api.is_finded("v2ray") then - type:value("V2ray", translate("V2ray")) -end -if api.is_finded("xray") then - type:value("Xray", translate("Xray")) -end -if api.is_finded("brook") then - type:value("Brook", translate("Brook")) -end -if api.is_finded("hysteria") then - type:value("Hysteria", translate("Hysteria")) -end - -protocol = s:option(ListValue, "protocol", translate("Protocol")) -protocol:value("vmess", "Vmess") -protocol:value("vless", "VLESS") -protocol:value("http", "HTTP") -protocol:value("socks", "Socks") -protocol:value("shadowsocks", "Shadowsocks") -protocol:value("trojan", "Trojan") -protocol:value("dokodemo-door", "dokodemo-door") -protocol:depends("type", "V2ray") -protocol:depends("type", "Xray") - --- Brook协议 -brook_protocol = s:option(ListValue, "brook_protocol", translate("Protocol")) -brook_protocol:value("server", "Brook") -brook_protocol:value("wsserver", "WebSocket") -brook_protocol:depends("type", "Brook") -function brook_protocol.cfgvalue(self, section) - return m:get(section, "protocol") -end -function brook_protocol.write(self, section, value) - m:set(section, "protocol", value) -end - ---brook_tls = s:option(Flag, "brook_tls", translate("Use TLS")) ---brook_tls:depends("brook_protocol", "wsserver") - -port = s:option(Value, "port", translate("Listen Port")) -port.datatype = "port" -port.rmempty = false - -auth = s:option(Flag, "auth", translate("Auth")) -auth.validate = function(self, value, t) - if value and value == "1" then - local user_v = username:formvalue(t) or "" - local pass_v = password:formvalue(t) or "" - if user_v == "" or pass_v == "" then - return nil, translate("Username and Password must be used together!") - end - end - return value -end -auth:depends({ type = "V2ray", protocol = "socks" }) -auth:depends({ type = "V2ray", protocol = "http" }) -auth:depends({ type = "Xray", protocol = "socks" }) -auth:depends({ type = "Xray", protocol = "http" }) - -username = s:option(Value, "username", translate("Username")) -username:depends("auth", true) - -password = s:option(Value, "password", translate("Password")) -password.password = true -password:depends("auth", true) -password:depends("type", "SS") -password:depends("type", "SS-Rust") -password:depends("type", "SSR") -password:depends("type", "Brook") -password:depends({ type = "V2ray", protocol = "shadowsocks" }) -password:depends({ type = "Xray", protocol = "shadowsocks" }) - -d_protocol = s:option(ListValue, "d_protocol", translate("Destination protocol")) -d_protocol:value("tcp", "TCP") -d_protocol:value("udp", "UDP") -d_protocol:value("tcp,udp", "TCP,UDP") -d_protocol:depends({ type = "V2ray", protocol = "dokodemo-door" }) -d_protocol:depends({ type = "Xray", protocol = "dokodemo-door" }) - -d_address = s:option(Value, "d_address", translate("Destination address")) -d_address:depends({ type = "V2ray", protocol = "dokodemo-door" }) -d_address:depends({ type = "Xray", protocol = "dokodemo-door" }) - -d_port = s:option(Value, "d_port", translate("Destination port")) -d_port.datatype = "port" -d_port:depends({ type = "V2ray", protocol = "dokodemo-door" }) -d_port:depends({ type = "Xray", protocol = "dokodemo-door" }) - -decryption = s:option(Value, "decryption", translate("Encrypt Method")) -decryption.default = "none" -decryption:depends({ type = "V2ray", protocol = "vless" }) -decryption:depends({ type = "Xray", protocol = "vless" }) - -hysteria_protocol = s:option(ListValue, "hysteria_protocol", translate("Protocol")) -hysteria_protocol:value("udp", "UDP") -hysteria_protocol:value("faketcp", "faketcp") -hysteria_protocol:value("wechat-video", "wechat-video") -hysteria_protocol:depends("type", "Hysteria") -function hysteria_protocol.cfgvalue(self, section) - return m:get(section, "protocol") -end -function hysteria_protocol.write(self, section, value) - m:set(section, "protocol", value) -end - -hysteria_obfs = s:option(Value, "hysteria_obfs", translate("Obfs Password")) -hysteria_obfs:depends("type", "Hysteria") - -hysteria_auth_type = s:option(ListValue, "hysteria_auth_type", translate("Auth Type")) -hysteria_auth_type:value("disable", translate("Disable")) -hysteria_auth_type:value("string", translate("STRING")) -hysteria_auth_type:depends("type", "Hysteria") - -hysteria_auth_password = s:option(Value, "hysteria_auth_password", translate("Auth Password")) -hysteria_auth_password.password = true -hysteria_auth_password:depends("hysteria_auth_type", "string") - -hysteria_alpn = s:option(Value, "hysteria_alpn", translate("QUIC TLS ALPN")) -hysteria_alpn:depends("type", "Hysteria") - -hysteria_udp = s:option(Flag, "hysteria_udp", translate("UDP")) -hysteria_udp.default = "1" -hysteria_udp:depends("type", "Hysteria") - -hysteria_up_mbps = s:option(Value, "hysteria_up_mbps", translate("Max upload Mbps")) -hysteria_up_mbps.default = "10" -hysteria_up_mbps:depends("type", "Hysteria") - -hysteria_down_mbps = s:option(Value, "hysteria_down_mbps", translate("Max download Mbps")) -hysteria_down_mbps.default = "50" -hysteria_down_mbps:depends("type", "Hysteria") - -hysteria_recv_window_conn = s:option(Value, "hysteria_recv_window_conn", translate("QUIC stream receive window")) -hysteria_recv_window_conn:depends("type", "Hysteria") - -hysteria_recv_window = s:option(Value, "hysteria_recv_window", translate("QUIC connection receive window")) -hysteria_recv_window:depends("type", "Hysteria") - -hysteria_disable_mtu_discovery = s:option(Flag, "hysteria_disable_mtu_discovery", translate("Disable MTU detection")) -hysteria_disable_mtu_discovery:depends("type", "Hysteria") - -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") -function ss_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function ss_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -ss_rust_encrypt_method = s:option(ListValue, "ss_rust_encrypt_method", translate("Encrypt Method")) -for a, t in ipairs(ss_rust_encrypt_method_list) do ss_rust_encrypt_method:value(t) end -ss_rust_encrypt_method:depends("type", "SS-Rust") -function ss_rust_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function ss_rust_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -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") -function ssr_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function ssr_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -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({ type = "V2ray", protocol = "shadowsocks" }) -function v_ss_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function v_ss_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -x_ss_encrypt_method = s:option(ListValue, "x_ss_encrypt_method", translate("Encrypt Method")) -for a, t in ipairs(x_ss_encrypt_method_list) do x_ss_encrypt_method:value(t) end -x_ss_encrypt_method:depends({ type = "Xray", protocol = "shadowsocks" }) -function x_ss_encrypt_method.cfgvalue(self, section) - return m:get(section, "method") -end -function x_ss_encrypt_method.write(self, section, value) - m:set(section, "method", value) -end - -iv_check = s:option(Flag, "iv_check", translate("IV Check")) -iv_check:depends({ type = "V2ray", protocol = "shadowsocks" }) -iv_check:depends({ type = "Xray", protocol = "shadowsocks" }) - -ss_network = s:option(ListValue, "ss_network", translate("Transport")) -ss_network.default = "tcp,udp" -ss_network:value("tcp", "TCP") -ss_network:value("udp", "UDP") -ss_network:value("tcp,udp", "TCP,UDP") -ss_network:depends({ type = "V2ray", protocol = "shadowsocks" }) -ss_network:depends({ type = "Xray", protocol = "shadowsocks" }) - -ssr_protocol = s:option(ListValue, "ssr_protocol", translate("Protocol")) -for a, t in ipairs(ssr_protocol_list) do ssr_protocol:value(t) end -ssr_protocol:depends("type", "SSR") -function ssr_protocol.cfgvalue(self, section) - return m:get(section, "protocol") -end -function ssr_protocol.write(self, section, value) - m:set(section, "protocol", value) -end - -protocol_param = s:option(Value, "protocol_param", translate("Protocol_param")) -protocol_param:depends("type", "SSR") - -obfs = s:option(ListValue, "obfs", translate("Obfs")) -for a, t in ipairs(ssr_obfs_list) do obfs:value(t) end -obfs:depends("type", "SSR") - -obfs_param = s:option(Value, "obfs_param", translate("Obfs_param")) -obfs_param:depends("type", "SSR") - -timeout = s:option(Value, "timeout", translate("Connection Timeout")) -timeout.datatype = "uinteger" -timeout.default = 300 -timeout:depends("type", "SS") -timeout:depends("type", "SS-Rust") -timeout:depends("type", "SSR") - -udp_forward = s:option(Flag, "udp_forward", translate("UDP Forward")) -udp_forward.default = "1" -udp_forward.rmempty = false -udp_forward:depends("type", "SSR") -udp_forward:depends({ type = "V2ray", protocol = "socks" }) -udp_forward:depends({ type = "Xray", protocol = "socks" }) - -uuid = s:option(DynamicList, "uuid", translate("ID") .. "/" .. translate("Password")) -for i = 1, 3 do - uuid:value(api.gen_uuid(1)) -end -uuid:depends({ type = "V2ray", protocol = "vmess" }) -uuid:depends({ type = "V2ray", protocol = "vless" }) -uuid:depends({ type = "V2ray", protocol = "trojan" }) -uuid:depends({ type = "Xray", protocol = "vmess" }) -uuid:depends({ type = "Xray", protocol = "vless" }) -uuid:depends({ type = "Xray", protocol = "trojan" }) - -tls = s:option(Flag, "tls", translate("TLS")) -tls.default = 0 -tls.validate = function(self, value, t) - if value then - local type = type:formvalue(t) or "" - if value == "1" then - local ca = tls_certificateFile:formvalue(t) or "" - local key = tls_keyFile:formvalue(t) or "" - if ca == "" or key == "" then - return nil, translate("Public key and Private key path can not be empty!") - end - end - return value - end -end -tls:depends({ type = "V2ray", protocol = "vmess" }) -tls:depends({ type = "V2ray", protocol = "vless" }) -tls:depends({ type = "V2ray", protocol = "socks" }) -tls:depends({ type = "V2ray", protocol = "shadowsocks" }) -tls:depends({ type = "V2ray", protocol = "trojan" }) -tls:depends({ type = "Xray", protocol = "vmess" }) -tls:depends({ type = "Xray", protocol = "vless" }) -tls:depends({ type = "Xray", protocol = "socks" }) -tls:depends({ type = "Xray", protocol = "shadowsocks" }) -tls:depends({ type = "Xray", protocol = "trojan" }) - -alpn = s:option(ListValue, "alpn", translate("alpn")) -alpn.default = "h2,http/1.1" -alpn:value("h2,http/1.1") -alpn:value("h2") -alpn:value("http/1.1") -alpn:depends({ type = "V2ray", tls = true }) -alpn:depends({ type = "Xray", tls = true }) - --- [[ TLS部分 ]] -- - -tls_certificateFile = s:option(FileUpload, "tls_certificateFile", translate("Public key absolute path"), translate("as:") .. "/etc/ssl/fullchain.pem") -tls_certificateFile.validate = function(self, value, t) - if value and value ~= "" then - if not nixio.fs.access(value) then - return nil, translate("Can't find this file!") - else - return value - end - end - return nil -end -tls_certificateFile.default = "/etc/config/ssl/" .. arg[1] .. ".pem" -tls_certificateFile:depends("tls", true) -tls_certificateFile:depends("type", "Hysteria") - -tls_keyFile = s:option(FileUpload, "tls_keyFile", translate("Private key absolute path"), translate("as:") .. "/etc/ssl/private.key") -tls_keyFile.validate = function(self, value, t) - if value and value ~= "" then - if not nixio.fs.access(value) then - return nil, translate("Can't find this file!") - else - return value - end - end - return nil -end -tls_keyFile.default = "/etc/config/ssl/" .. arg[1] .. ".key" -tls_keyFile:depends("tls", true) -tls_keyFile:depends("type", "Hysteria") - -transport = s:option(ListValue, "transport", translate("Transport")) -transport:value("tcp", "TCP") -transport:value("mkcp", "mKCP") -transport:value("ws", "WebSocket") -transport:value("h2", "HTTP/2") -transport:value("ds", "DomainSocket") -transport:value("quic", "QUIC") -transport:value("grpc", "gRPC") -transport:depends({ type = "V2ray", protocol = "vmess" }) -transport:depends({ type = "V2ray", protocol = "vless" }) -transport:depends({ type = "V2ray", protocol = "socks" }) -transport:depends({ type = "V2ray", protocol = "shadowsocks" }) -transport:depends({ type = "V2ray", protocol = "trojan" }) -transport:depends({ type = "Xray", protocol = "vmess" }) -transport:depends({ type = "Xray", protocol = "vless" }) -transport:depends({ type = "Xray", protocol = "socks" }) -transport:depends({ type = "Xray", protocol = "shadowsocks" }) -transport:depends({ type = "Xray", protocol = "trojan" }) - --- [[ WebSocket部分 ]]-- - -ws_host = s:option(Value, "ws_host", translate("WebSocket Host")) -ws_host:depends("transport", "ws") -ws_host:depends("ss_transport", "ws") - -ws_path = s:option(Value, "ws_path", translate("WebSocket Path")) -ws_path:depends("transport", "ws") -ws_path:depends("ss_transport", "ws") -ws_path:depends({ type = "Brook", brook_protocol = "wsserver" }) - --- [[ HTTP/2部分 ]]-- - -h2_host = s:option(Value, "h2_host", translate("HTTP/2 Host")) -h2_host:depends("transport", "h2") -h2_host:depends("ss_transport", "h2") - -h2_path = s:option(Value, "h2_path", translate("HTTP/2 Path")) -h2_path:depends("transport", "h2") -h2_path:depends("ss_transport", "h2") - --- [[ TCP部分 ]]-- - --- TCP伪装 -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:depends("tcp_guise", "http") - --- HTTP路径 -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('
none: default, no masquerade, data sent is packets with no characteristics.
srtp: disguised as an SRTP packet, it will be recognized as video call data (such as FaceTime).
utp: packets disguised as uTP will be recognized as bittorrent downloaded data.
wechat-video: packets disguised as WeChat video calls.
dtls: disguised as DTLS 1.2 packet.
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") - -mkcp_mtu = s:option(Value, "mkcp_mtu", translate("KCP MTU")) -mkcp_mtu.default = "1350" -mkcp_mtu:depends("transport", "mkcp") - -mkcp_tti = s:option(Value, "mkcp_tti", translate("KCP TTI")) -mkcp_tti.default = "20" -mkcp_tti:depends("transport", "mkcp") - -mkcp_uplinkCapacity = s:option(Value, "mkcp_uplinkCapacity", translate("KCP uplinkCapacity")) -mkcp_uplinkCapacity.default = "5" -mkcp_uplinkCapacity:depends("transport", "mkcp") - -mkcp_downlinkCapacity = s:option(Value, "mkcp_downlinkCapacity", translate("KCP downlinkCapacity")) -mkcp_downlinkCapacity.default = "20" -mkcp_downlinkCapacity:depends("transport", "mkcp") - -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.default = "1" -mkcp_readBufferSize:depends("transport", "mkcp") - -mkcp_writeBufferSize = s:option(Value, "mkcp_writeBufferSize", translate("KCP writeBufferSize")) -mkcp_writeBufferSize.default = "1" -mkcp_writeBufferSize:depends("transport", "mkcp") - -mkcp_seed = s:option(Value, "mkcp_seed", translate("KCP Seed")) -mkcp_seed:depends("transport", "mkcp") - --- [[ DomainSocket部分 ]]-- - -ds_path = s:option(Value, "ds_path", "Path", translate("A legal file path. This file must not exist before running.")) -ds_path:depends("transport", "ds") - --- [[ QUIC部分 ]]-- -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:depends("transport", "quic") - -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") - --- [[ gRPC部分 ]]-- -grpc_serviceName = s:option(Value, "grpc_serviceName", "ServiceName") -grpc_serviceName:depends("transport", "grpc") - -acceptProxyProtocol = s:option(Flag, "acceptProxyProtocol", translate("acceptProxyProtocol"), translate("Whether to receive PROXY protocol, when this node want to be fallback or forwarded by proxy, it must be enable, otherwise it cannot be used.")) -acceptProxyProtocol:depends({ type = "V2ray", transport = "tcp" }) -acceptProxyProtocol:depends({ type = "V2ray", transport = "ws" }) -acceptProxyProtocol:depends({ type = "Xray", transport = "tcp" }) -acceptProxyProtocol:depends({ type = "Xray", transport = "ws" }) - --- [[ Fallback部分 ]]-- -fallback = s:option(Flag, "fallback", translate("Fallback")) -fallback:depends({ type = "V2ray", protocol = "vless", transport = "tcp" }) -fallback:depends({ type = "V2ray", protocol = "trojan", transport = "tcp" }) -fallback:depends({ type = "Xray", protocol = "vless", transport = "tcp" }) -fallback:depends({ type = "Xray", protocol = "trojan", transport = "tcp" }) - ---[[ -fallback_alpn = s:option(Value, "fallback_alpn", "Fallback alpn") -fallback_alpn:depends("fallback", true) - -fallback_path = s:option(Value, "fallback_path", "Fallback path") -fallback_path:depends("fallback", true) - -fallback_dest = s:option(Value, "fallback_dest", "Fallback dest") -fallback_dest:depends("fallback", true) - -fallback_xver = s:option(Value, "fallback_xver", "Fallback xver") -fallback_xver.default = 0 -fallback_xver:depends("fallback", true) -]]-- - -fallback_list = s:option(DynamicList, "fallback_list", "Fallback", translate("dest,path")) -fallback_list:depends("fallback", true) - -tcp_fast_open = s:option(Flag, "tcp_fast_open", translate("TCP Fast Open")) -tcp_fast_open.default = "0" -tcp_fast_open:depends("type", "SS") -tcp_fast_open:depends("type", "SS-Rust") -tcp_fast_open:depends("type", "SSR") - -remote_address = s:option(Value, "remote_address", translate("Remote Address")) -remote_address.default = "127.0.0.1" -remote_address:depends("remote_enable", 1) - -remote_port = s:option(Value, "remote_port", translate("Remote Port")) -remote_port.datatype = "port" -remote_port.default = "80" -remote_port:depends("remote_enable", 1) - -bind_local = s:option(Flag, "bind_local", translate("Bind Local"), translate("When selected, it can only be accessed locally, It is recommended to turn on when using reverse proxies or be fallback.")) -bind_local.default = "0" -bind_local:depends("type", "V2ray") -bind_local:depends("type", "Xray") - -accept_lan = s:option(Flag, "accept_lan", translate("Accept LAN Access"), translate("When selected, it can accessed lan , this will not be safe!")) -accept_lan.default = "0" -accept_lan:depends("type", "V2ray") -accept_lan:depends("type", "Xray") - -local nodes_table = {} -for k, e in ipairs(api.get_valid_nodes()) do - if e.node_type == "normal" and (e.type == "V2ray" or e.type == "Xray") then - nodes_table[#nodes_table + 1] = { - id = e[".name"], - remarks = e["remark"] - } - end -end - -outbound_node = s:option(ListValue, "outbound_node", translate("outbound node")) -outbound_node:value("nil", translate("Close")) -outbound_node:value("_socks", translate("Custom Socks")) -outbound_node:value("_http", translate("Custom HTTP")) -outbound_node:value("_iface", translate("Custom Interface") .. " (Only Support Xray)") -for k, v in pairs(nodes_table) do outbound_node:value(v.id, v.remarks) end -outbound_node.default = "nil" -outbound_node:depends("type", "V2ray") -outbound_node:depends("type", "Xray") - -outbound_node_address = s:option(Value, "outbound_node_address", translate("Address (Support Domain Name)")) -outbound_node_address:depends("outbound_node", "_socks") -outbound_node_address:depends("outbound_node", "_http") - -outbound_node_port = s:option(Value, "outbound_node_port", translate("Port")) -outbound_node_port.datatype = "port" -outbound_node_port:depends("outbound_node", "_socks") -outbound_node_port:depends("outbound_node", "_http") - -outbound_node_username = s:option(Value, "outbound_node_username", translate("Username")) -outbound_node_username:depends("outbound_node", "_socks") -outbound_node_username:depends("outbound_node", "_http") - -outbound_node_password = s:option(Value, "outbound_node_password", translate("Password")) -outbound_node_password.password = true -outbound_node_password:depends("outbound_node", "_socks") -outbound_node_password:depends("outbound_node", "_http") - -outbound_node_iface = s:option(Value, "outbound_node_iface", translate("Interface")) -outbound_node_iface.default = "eth1" -outbound_node_iface:depends("outbound_node", "_iface") - -log = s:option(Flag, "log", translate("Log")) -log.default = "1" -log.rmempty = false - -loglevel = s:option(ListValue, "loglevel", translate("Log Level")) -loglevel.default = "warning" -loglevel:value("debug") -loglevel:value("info") -loglevel:value("warning") -loglevel:value("error") -loglevel:depends({ type = "V2ray", log = true }) -loglevel:depends({ type = "Xray", log = true }) return m 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 18e93bfbf..dccf2429a 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 @@ -8,23 +8,23 @@ local has_xray = api.is_finded("xray") 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 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 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, "/"); @@ -34,11 +34,11 @@ local has_xray = api.is_finded("xray") 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, "/"); @@ -48,13 +48,14 @@ local has_xray = api.is_finded("xray") str = padright(str, l, "="); return atob(str); } - + function dictvalue(d, key) { var v = d[key]; - if (typeof(v) === 'undefined' || v === '') + if (typeof (v) === 'undefined' || v === '') return ''; return b64decsafe(v); } + function parseNodeUrl(url) { let protocol = url.substring(0, url.indexOf("://")) + ":" let str = "http" + url.substring(url.indexOf("://")) @@ -73,8 +74,8 @@ local has_xray = api.is_finded("xray") function buildUrl(btn, urlname, sid) { var opt = { base: "cbid.passwall2", - client : true, - get: function(opt) { + client: true, + get: function (opt) { var id = this.base + "." + opt; var obj = document.getElementsByName(id)[0] || document.getElementsByClassName(id)[0] || document.getElementById(id) if (obj) { @@ -83,7 +84,7 @@ local has_xray = api.is_finded("xray") return null; } }, - getlist: function(opt) { + getlist: function (opt) { var id = this.base + "." + opt; var objs = document.getElementsByName(id) || document.getElementsByClassName(id); var ret = []; @@ -96,7 +97,7 @@ local has_xray = api.is_finded("xray") } return ret; }, - query: function(param, src, default_value, tval = "1", fval = "0") { + query: function (param, src, default_value, tval = "1", fval = "0") { var ret = "&" + param + "="; var obj = this.get(src); if (obj) { @@ -112,7 +113,7 @@ local has_xray = api.is_finded("xray") return "" } } - + var s = document.getElementById(urlname + "-status"); if (!s) { alert("Never"); @@ -122,53 +123,103 @@ local has_xray = api.is_finded("xray") opt.client = urlname.indexOf("server") === -1; var v_type = opt.get("type").value; var v_alias = opt.get("remarks"); + + var dom_prefix = null + var protocol = "" + if (v_type === "SS") { + dom_prefix = "ss_" + protocol = "ss" + } else if (v_type === "SS-Rust") { + dom_prefix = "ssrust_" + protocol = "ss" + } else if (v_type === "SSR") { + dom_prefix = "ssr_" + protocol = "ssr" + } else if (v_type === "Brook") { + dom_prefix = "brook_" + protocol = "brook" + } else if (v_type === "Hysteria") { + dom_prefix = "hysteria_" + protocol = "hysteria" + } else if (v_type === "V2ray" || v_type === "Xray") { + dom_prefix = "xray_" + } var _address = "" - try { - var v_server = opt.get("address"); - const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; - if (ipv6Regex.test(v_server.value)) { - _address = "[" + v_server.value + "]" - } else { - _address = v_server.value + if (dom_prefix && dom_prefix != null) { + try { + var v_server = opt.get(dom_prefix + "address"); + const ipv6Regex = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$/; + if (ipv6Regex.test(v_server.value)) { + _address = "[" + v_server.value + "]" + } else { + _address = v_server.value + } + } catch (e) { } - } catch(e) { } var url = null; if (v_type === "SS") { - var v_port = opt.get("port"); - var v_method = opt.get("ss_encrypt_method"); - var v_password = opt.get("password"); - + var v_port = opt.get(dom_prefix + "port"); + var v_method = opt.get(dom_prefix + "method"); + var v_password = opt.get(dom_prefix + "password"); + url = b64encsafe(v_method.value + ":" + v_password.value) + "@" + _address + ":" + v_port.value + "/?"; - + var params = ""; - var v_plugin = opt.get("ss_plugin").value; + var v_plugin = opt.get(dom_prefix + "plugin").value; if (v_plugin && v_plugin != "none") { if (v_plugin == "simple-obfs" || v_plugin == "obfs-local") { v_plugin = "obfs-local"; } - var v_plugin_opts = opt.get("ss_plugin_opts").value; + var v_plugin_opts = opt.get(dom_prefix + "plugin_opts").value; if (v_plugin_opts && v_plugin_opts != "") { - v_plugin += encodeURI(";" + v_plugin_opts); + v_plugin += ";" + v_plugin_opts; } - params += "&plugin=" + encodeURI(v_plugin); + params += "&plugin=" + encodeURIComponent(v_plugin); } params += "&group=" - params += "#" + encodeURI(v_alias.value); + params += "#" + encodeURIComponent(v_alias.value); + if (params[0] == "&") { + params = params.substring(1); + } + url += params; + } else if (v_type === "SS-Rust") { + var v_port = opt.get(dom_prefix + "port"); + var v_method = opt.get(dom_prefix + "method"); + var v_password = opt.get(dom_prefix + "password"); + + url = btoa(v_method.value + ":" + v_password.value) + "@" + + _address + ":" + + v_port.value + "/?"; + + var params = ""; + var v_plugin = opt.get(dom_prefix + "plugin").value; + if (v_plugin && v_plugin != "none") { + if (v_plugin == "simple-obfs" || v_plugin == "obfs-local") { + v_plugin = "obfs-local"; + } + var v_plugin_opts = opt.get(dom_prefix + "plugin_opts").value; + if (v_plugin_opts && v_plugin_opts != "") { + v_plugin += ";" + v_plugin_opts; + } + params += "&plugin=" + encodeURIComponent(v_plugin); + } + params += "&group=" + params += "#" + encodeURIComponent(v_alias.value); if (params[0] == "&") { params = params.substring(1); } url += params; } else if (v_type === "SSR") { - 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 v_port = opt.get(dom_prefix + "port"); + var v_protocol = opt.get(dom_prefix + "protocol"); + var v_method = opt.get(dom_prefix + "method"); + var v_obfs = opt.get(dom_prefix + "obfs"); + var v_password = opt.get(dom_prefix + "password"); + var v_obfs_param = opt.get(dom_prefix + "obfs_param"); + var v_protocol_param = opt.get(dom_prefix + "protocol_param"); var ssr_str = _address + ":" + v_port.value + ":" + v_protocol.value + ":" + @@ -179,23 +230,23 @@ local has_xray = api.is_finded("xray") "&protoparam=" + b64encsafe(v_protocol_param.value) + "&remarks=" + b64encutf8safe(v_alias.value); url = b64encsafe(ssr_str); - } else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "vmess") { - v_type = "vmess"; + } else if ((v_type === "V2ray" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "vmess") { + protocol = "vmess"; var info = {}; info.v = "2"; info.ps = v_alias.value; - info.add = opt.get("address").value; + info.add = opt.get(dom_prefix + "address").value; //info.add = _address; - info.port = opt.get("port").value; - info.id = opt.get("uuid").value; - + info.port = opt.get(dom_prefix + "port").value; + info.id = opt.get(dom_prefix + "uuid").value; + var v_transport = opt.get("transport").value; if (v_transport === "ws") { - info.host = opt.get("ws_host").value; - info.path = opt.get("ws_path").value; + info.host = opt.get(dom_prefix + "ws_host").value; + info.path = opt.get(dom_prefix + "ws_path").value; } else if (v_transport === "h2") { - info.host = opt.get("h2_host").value; - info.path = opt.get("h2_path").value; + info.host = opt.get(dom_prefix + "h2_host").value; + info.path = opt.get(dom_prefix + "h2_path").value; } else if (v_transport === "tcp") { info.type = opt.get("tcp_guise").value; if (info.type === "http") { @@ -216,31 +267,31 @@ local has_xray = api.is_finded("xray") info.path = encodeURI(info.path); } info.net = v_transport; - + info.security = opt.get("security").value || "auto"; - if (opt.get("tls").checked) { + if (opt.get(dom_prefix + "tls").checked) { var v_security = "tls"; info.tls = "tls"; - info.sni = opt.get("tls_serverName").value; + info.sni = opt.get(dom_prefix + "tls_serverName").value; } url = b64EncodeUnicode(JSON.stringify(info)); - } else if ((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "vless") { - v_type = "vless"; - var v_password = opt.get("uuid"); - var v_port = opt.get("port"); + } else if ((v_type === "V2ray" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "vless") { + protocol = "vless"; + var v_password = opt.get(dom_prefix + "uuid"); + var v_port = opt.get(dom_prefix + "port"); url = encodeURIComponent(v_password.value) + "@" + _address + ":" + v_port.value + "?"; - + var params = ""; var v_transport = opt.get("transport").value; if (v_transport === "ws") { - params += opt.query("host", "ws_host"); - params += opt.query("path", "ws_path"); + params += opt.query("host", dom_prefix + "ws_host"); + params += opt.query("path", dom_prefix + "ws_path"); } else if (v_transport === "h2") { v_transport = "http"; - params += opt.query("host", "h2_host"); - params += opt.query("path", "h2_path"); + params += opt.query("host", dom_prefix + "h2_host"); + params += opt.query("path", dom_prefix + "h2_path"); } else if (v_transport === "tcp") { params += opt.query("headerType", "tcp_guise"); params += opt.query("host", "tcp_guise_http_host"); @@ -253,21 +304,21 @@ local has_xray = api.is_finded("xray") params += opt.query("key", "quic_key"); params += opt.query("quicSecurity", "quic_security"); } else if (v_transport === "grpc") { - //不知道是用path还是serviceName,这里先这样吧 params += opt.query("path", "grpc_serviceName"); params += opt.query("serviceName", "grpc_serviceName"); params += opt.query("mode", "grpc_mode"); } params += "&type=" + v_transport; - + params += opt.query("encryption", "encryption"); - if (opt.get("tls").checked) { + + if (opt.get(dom_prefix + "tls").checked) { var v_security = "tls"; - if (opt.get("xray_fingerprint") && opt.get("xray_fingerprint").value != "") { - let v_fp = opt.get("xray_fingerprint").value; + if (opt.get(dom_prefix + "fingerprint") && opt.get(dom_prefix + "fingerprint").value != "") { + let v_fp = opt.get(dom_prefix + "fingerprint").value; params += "&fp=" + v_fp; } - if(opt.get("reality") && opt.get("reality").checked) { + if (opt.get("reality") && opt.get("reality").checked) { v_security = "reality"; if (opt.get("reality_fingerprint") && opt.get("reality_fingerprint").value != "") { let v_fp = opt.get("reality_fingerprint").value; @@ -277,12 +328,12 @@ local has_xray = api.is_finded("xray") params += opt.query("sid", "reality_shortId"); params += opt.query("spx", "reality_spiderX"); } - if (opt.get("tlsflow") && opt.get("tlsflow").value) { - let v_flow = opt.get("tlsflow").value; + if (opt.get(dom_prefix + "tlsflow") && opt.get(dom_prefix + "tlsflow").value) { + let v_flow = opt.get(dom_prefix + "tlsflow").value; params += "&flow=" + v_flow; } params += "&security=" + v_security; - params += opt.query("sni", "tls_serverName"); + params += opt.query("sni", dom_prefix + "tls_serverName"); } params += "#" + encodeURI(v_alias.value); @@ -290,20 +341,18 @@ local has_xray = api.is_finded("xray") params = params.substring(1); } url += params; - } else if (((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "trojan")) { - if (((v_type === "V2ray" || v_type === "Xray") && opt.get("protocol").value === "trojan")) { - v_type = "trojan"; - } - var v_password = opt.get("password"); - var v_port = opt.get("port"); + } else if (((v_type === "V2ray" || v_type === "Xray") && opt.get(dom_prefix + "protocol").value === "trojan")) { + protocol = "trojan"; + var v_password = opt.get(dom_prefix + "password"); + var v_port = opt.get(dom_prefix + "port"); url = encodeURIComponent(v_password.value) + "@" + _address + ":" + v_port.value + "/?"; var params = ""; - if (opt.get("tls").checked) { - params += opt.query("sni", "tls_serverName"); + if (opt.get(dom_prefix + "tls").checked) { + params += opt.query("sni", dom_prefix + "tls_serverName"); params += "&tls=1" - params += opt.query("allowinsecure", "tls_allowInsecure"); + params += opt.query("allowinsecure", dom_prefix + "tls_allowInsecure"); } params += "#" + encodeURI(v_alias.value); if (params[0] == "&") { @@ -313,21 +362,21 @@ local has_xray = api.is_finded("xray") } else if (v_type === "Brook") { var url = ""; var params = "?"; - var v_protocol = opt.get("brook_protocol"); - var v_port = opt.get("port"); - var v_password = opt.get("password"); + var v_protocol = opt.get(dom_prefix + "protocol"); + var v_port = opt.get(dom_prefix + "port"); + var v_password = opt.get(dom_prefix + "password"); var b_protocol_value = v_protocol.value.split('client').join('server'); - + var url_protocol = b_protocol_value; - params += opt.query("password", "password"); + params += opt.query("password", dom_prefix + "password"); if (b_protocol_value == "wsserver") { var server = ''; var prefix = "ws://"; - if (opt.get("brook_tls").checked) { + if (opt.get(dom_prefix + "tls").checked) { prefix = "wss://"; url_protocol = 'wssserver'; } - var v_path = opt.get("ws_path"); + var v_path = opt.get(dom_prefix + "ws_path"); var v_path_value = v_path.value || '/ws'; if (v_path_value.length > 1 && v_path_value.indexOf('/') < 0) { v_path_value = '/' + v_path_value; @@ -339,16 +388,16 @@ local has_xray = api.is_finded("xray") url += url_protocol; url += params; } else if (v_type === "Hysteria") { - var v_port = opt.get("port"); + var v_port = opt.get(dom_prefix + "port"); var params = ""; - params += opt.query("protocol", "hysteria_protocol"); - params += opt.query("auth", "hysteria_auth_password"); - params += opt.query("peer", "tls_serverName"); - params += opt.query("insecure", "tls_allowInsecure"); - params += opt.query("upmbps", "hysteria_up_mbps", 1000); - params += opt.query("downmbps", "hysteria_down_mbps", 1000); - params += opt.query("alpn", "hysteria_alpn"); - params += opt.query("obfsParam", "hysteria_obfs"); + params += opt.query("protocol", dom_prefix + "protocol"); + params += opt.query("auth", dom_prefix + "auth_password"); + params += opt.query("peer", dom_prefix + "tls_serverName"); + params += opt.query("insecure", dom_prefix + "tls_allowInsecure"); + params += opt.query("upmbps", dom_prefix + "up_mbps", 1000); + params += opt.query("downmbps", dom_prefix + "down_mbps", 1000); + params += opt.query("alpn", dom_prefix + "alpn"); + params += opt.query("obfsParam", dom_prefix + "obfs"); var url = _address + ":" + v_port.value + "?" + @@ -356,9 +405,9 @@ local has_xray = api.is_finded("xray") "#" + encodeURI(v_alias.value); } if (url) { - url = v_type.toLowerCase() + "://" + url; + url = protocol.toLowerCase() + "://" + url; var textarea = document.createElement("textarea"); - textarea.textContent = url; + textarea.textContent = url; textarea.style.position = "fixed"; document.body.appendChild(textarea); textarea.select(); @@ -376,12 +425,12 @@ local has_xray = api.is_finded("xray") } return false; } - + function fromUrl(btn, urlname, sid) { var opt = { base: 'cbid.passwall2', - client : true, - get: function(opt) { + client: true, + get: function (opt) { var obj; var id = this.base + '.' + opt; obj = document.getElementsByName(id)[0] || document.getElementById(id); @@ -399,7 +448,7 @@ local has_xray = api.is_finded("xray") return null; } }, - set: function(opt, val) { + set: function (opt, val) { var obj; obj = this.get(opt); if (obj) { @@ -412,7 +461,7 @@ local has_xray = api.is_finded("xray") if (obj.combobox) { obj.combobox.value = val; } - + var list = obj.getElementsByTagName("li"); if (list.length > 0) { for (var i = 0; i < list.length; i++) { @@ -430,22 +479,22 @@ local has_xray = api.is_finded("xray") input.value = val; } else { var input = document.createElement("input"); - input.setAttribute("type", "hidden") ; - input.setAttribute("name", obj.id) ; - input.setAttribute("value", val) ; + input.setAttribute("type", "hidden"); + input.setAttribute("name", obj.id); + input.setAttribute("value", val); obj.appendChild(input); } } } try { obj.dispatchEvent(event); - } catch(err) { + } catch (err) { } } else { //alert('<%:Faltal on set option, please help in debug: %>' + opt + ' = ' + val); } }, - setlist: function(opt, vlist) { + setlist: function (opt, vlist) { var id = this.base + "." + opt; var objs = document.getElementsByName(id) || document.getElementsByClassName(id); if (objs) { @@ -459,7 +508,7 @@ local has_xray = api.is_finded("xray") } } } - + var s = document.getElementById(urlname + '-status'); if (!s) { alert("Never"); @@ -475,7 +524,11 @@ local has_xray = api.is_finded("xray") var ssu = ssrurl.split('://'); var event = document.createEvent("HTMLEvents"); event.initEvent("change", true, true); + + var dom_prefix = null + if (ssu[0] === "ssr") { + dom_prefix = "ssr_" //var b64c = ssu[1].match(/([A-Za-z0-9_-]+)/); var sstr = b64decsafe(ssu[1]); var ploc = sstr.indexOf("/?"); @@ -500,18 +553,20 @@ local has_xray = api.is_finded("xray") } } opt.set('type', "SSR"); - 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')); + opt.set(dom_prefix + 'address', ssm[1]); + opt.set(dom_prefix + 'port', ssm[2]); + opt.set(dom_prefix + 'protocol', ssm[3]); + opt.set(dom_prefix + 'method', ssm[4]); + opt.set(dom_prefix + 'obfs', ssm[5]); + opt.set(dom_prefix + 'password', b64decsafe(ssm[6])); + opt.set(dom_prefix + 'obfs_param', dictvalue(pdict, 'obfsparam')); + opt.set(dom_prefix + 'protocol_param', dictvalue(pdict, 'protoparam')); var rem = pdict['remarks']; - if (typeof(rem) !== 'undefined' && rem !== '' && rem.length > 0) + if (typeof (rem) !== 'undefined' && rem !== '' && rem.length > 0) opt.set('remarks', b64decutf8safe(rem)); - } else if (ssu[0] === "ss") { + } + if (ssu[0] === "ss") { + dom_prefix = "ss_" var url0 = "", param = ""; var sipIndex = ssu[1].indexOf("@"); var ploc = ssu[1].indexOf("#"); @@ -534,21 +589,26 @@ local has_xray = api.is_finded("xray") var pluginIndex = pluginInfo.indexOf(";"); var pluginNameInfo = pluginInfo.substr(0, pluginIndex); plugin = pluginNameInfo.substr(pluginNameInfo.indexOf("=") + 1) - pluginOpts = pluginInfo.substr(pluginIndex + 1); + pluginOpts = pluginInfo.substr(pluginIndex + 1).split("&")[0]; } var userInfoSplitIndex = userInfo.indexOf(":"); if (userInfoSplitIndex !== -1) { method = userInfo.substr(0, userInfoSplitIndex); password = userInfo.substr(userInfoSplitIndex + 1); } - opt.set('type', "SS"); - opt.set('address', server); - opt.set('port', port); - opt.set('password', password || ""); - opt.set('ss_encrypt_method', method || ""); - opt.set('ss_plugin', plugin || "none"); + if (["2022-blake3-aes-128-gcm", "2022-blake3-aes-256-gcm", "2022-blake3-chacha8-poly1305", "2022-blake3-chacha20-poly1305"].includes(method)) { + opt.set('type', "SS-Rust"); + dom_prefix = "ssrust_" + } else { + opt.set('type', "SS"); + } + opt.set(dom_prefix + 'address', server); + opt.set(dom_prefix + 'port', port); + opt.set(dom_prefix + 'password', password || ""); + opt.set(dom_prefix + 'method', method || ""); + opt.set(dom_prefix + 'plugin', plugin || "none"); if (plugin && plugin != "none") { - opt.set('ss_plugin_opts', pluginOpts || ""); + opt.set(dom_prefix + 'plugin_opts', pluginOpts || ""); } if (param !== undefined) { opt.set('remarks', decodeURI(param)); @@ -559,17 +619,19 @@ local has_xray = api.is_finded("xray") opt.set('type', "SS"); 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', "none"); - //opt.set('ss_plugin_opts', ""); + opt.set(dom_prefix + 'address', part2[0]); + opt.set(dom_prefix + 'port', part2[1]); + opt.set(dom_prefix + 'password', part1[1]); + opt.set(dom_prefix + 'method', part1[0]); + opt.set(dom_prefix + 'plugin', "none"); + //opt.set(dom_prefix + 'plugin_opts', ""); if (param !== undefined) { opt.set('remarks', decodeURI(param)); } } - } else if (ssu[0] === "trojan") { + } + if (ssu[0] === "trojan") { + dom_prefix = "xray_" var m = parseNodeUrl(ssrurl); var password = m.passwd; if (password === "") { @@ -592,21 +654,20 @@ local has_xray = api.is_finded("xray") <% elseif has_xray then %> opt.set('type', "Xray"); <% end %> - opt.set('protocol', "trojan"); - opt.set('address', m.hostname); - opt.set('port', m.port || "443"); - opt.set('password', decodeURIComponent(password)); - var tls = true; - opt.set('tls', tls); - if (tls) { - opt.set('tls_serverName', queryParam.peer || queryParam.sni || ''); - opt.set('tls_allowInsecure', queryParam.allowinsecure === '1'); - } - opt.set('mux', queryParam.mux === '1'); + opt.set(dom_prefix + 'protocol', "trojan"); + opt.set(dom_prefix + 'address', m.hostname); + opt.set(dom_prefix + 'port', m.port || "443"); + opt.set(dom_prefix + 'password', decodeURIComponent(password)); + opt.set(dom_prefix + 'tls', true); + opt.set(dom_prefix + 'tls_serverName', queryParam.peer || queryParam.sni || ''); + opt.set(dom_prefix + 'tls_allowInsecure', queryParam.allowinsecure === '1'); + opt.set(dom_prefix + 'mux', queryParam.mux === '1'); if (m.hash) { opt.set('remarks', decodeURI(m.hash.substr(1))); } - } else if (ssu[0] === "vmess") { + } + if (ssu[0] === "vmess") { + dom_prefix = "xray_" var sstr = b64DecodeUnicode(ssu[1]); var ploc = sstr.indexOf("/?"); <% if has_v2ray then %> @@ -614,7 +675,7 @@ local has_xray = api.is_finded("xray") <% elseif has_xray then %> opt.set('type', "Xray"); <% end %> - opt.set('protocol', "vmess"); + opt.set(dom_prefix + 'protocol', "vmess"); var url0, param = ""; if (ploc > 0) { url0 = sstr.substr(0, ploc); @@ -622,16 +683,16 @@ local has_xray = api.is_finded("xray") } var ssm = JSON.parse(sstr); opt.set('remarks', ssm.ps); - opt.set('address', ssm.add); - opt.set('port', ssm.port); - opt.set('uuid', ssm.id); - opt.set('tls', ssm.tls === "tls"); + opt.set(dom_prefix + 'address', ssm.add); + opt.set(dom_prefix + 'port', ssm.port); + opt.set(dom_prefix + 'uuid', ssm.id); + opt.set(dom_prefix + 'tls', ssm.tls === "tls"); if (ssm.tls === "tls") { var tls_serverName = ssm.host; if (ssm.sni) { tls_serverName = ssm.sni } - opt.set('tls_serverName', tls_serverName); + opt.set(dom_prefix + 'tls_serverName', tls_serverName); } ssm.net = ssm.net.toLowerCase(); if (ssm.net === "kcp" || ssm.net === "mkcp") @@ -644,11 +705,11 @@ local has_xray = api.is_finded("xray") 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); + opt.set(dom_prefix + 'ws_host', ssm.host); + opt.set(dom_prefix + 'ws_path', ssm.path); } else if (ssm.net === "h2") { - opt.set('h2_host', ssm.host); - opt.set('h2_path', ssm.path); + opt.set(dom_prefix + 'h2_host', ssm.host); + opt.set(dom_prefix + 'h2_path', ssm.path); } else if (ssm.net === "quic") { opt.set('quic_security', ssm.securty); opt.set('quic_key', ssm.key); @@ -657,22 +718,24 @@ local has_xray = api.is_finded("xray") } else if (ssm.net === "grpc") { opt.set('grpc_serviceName', ssm.path); } - } else if (ssu[0] === "vless") { + } + if (ssu[0] === "vless") { + dom_prefix = "xray_" <% if has_xray then %> opt.set('type', "Xray"); <% elseif has_v2ray then %> opt.set('type', "V2ray"); <% end %> - opt.set('protocol', "vless"); + opt.set(dom_prefix + 'protocol', "vless"); var m = parseNodeUrl(ssrurl); var password = m.passwd; if (password === "") { s.innerHTML = "<%:Invalid Share URL Format%>"; return false; } - opt.set('uuid', password); - opt.set('address', m.hostname); - opt.set('port', m.port || "443"); + opt.set(dom_prefix + 'uuid', password); + opt.set(dom_prefix + 'address', m.hostname); + opt.set(dom_prefix + 'port', m.port || "443"); var queryParam = {}; if (m.search.length > 1) { var query = m.search.split('?'); @@ -684,35 +747,36 @@ local has_xray = api.is_finded("xray") queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || ''); } } - + opt.set('encryption', queryParam.encryption); if (queryParam.security) { if (queryParam.security == "tls") { - opt.set('tls', true); + opt.set(dom_prefix + 'tls', true); opt.set('reality', false) - opt.set('tlsflow', queryParam.flow || ''); - opt.set('tls_serverName', queryParam.sni || ''); - opt.set('tls_allowInsecure', true); + opt.set(dom_prefix + 'tlsflow', queryParam.flow || ''); + opt.set(dom_prefix + 'tls_serverName', queryParam.sni || ''); + opt.set(dom_prefix + 'tls_allowInsecure', true); if (queryParam.allowinsecure === '0') { - opt.set('tls_allowInsecure', false); + opt.set(dom_prefix + 'tls_allowInsecure', false); } if (queryParam.fp && queryParam.fp.trim() != "") { - opt.set('xray_fingerprint', queryParam.fp); + opt.set(dom_prefix + 'fingerprint', queryParam.fp); } } - } - if (queryParam.security == "reality") { - opt.set('tls', true); - opt.set('reality', true) - opt.set('tlsflow', queryParam.flow || ''); - opt.set('tls_serverName', queryParam.sni || ''); - if (queryParam.fp && queryParam.fp.trim() != "") { - opt.set('reality_fingerprint', queryParam.fp); + if (queryParam.security == "reality") { + opt.set(dom_prefix + 'tls', true); + opt.set('reality', true) + opt.set(dom_prefix + 'tlsflow', queryParam.flow || ''); + opt.set(dom_prefix + 'tls_serverName', queryParam.sni || ''); + if (queryParam.fp && queryParam.fp.trim() != "") { + opt.set('reality_fingerprint', queryParam.fp); + } + opt.set('reality_publicKey', queryParam.pbk || ''); + opt.set('reality_shortId', queryParam.sid || ''); + opt.set('reality_spiderX', queryParam.spx || ''); } - opt.set('reality_publicKey', queryParam.pbk || ''); - opt.set('reality_shortId', queryParam.sid || ''); - opt.set('reality_spiderX', queryParam.spx || ''); + } queryParam.type = queryParam.type.toLowerCase(); @@ -728,11 +792,11 @@ local has_xray = api.is_finded("xray") opt.set('tcp_guise_http_path', queryParam.path || ""); } } else if (queryParam.type === "ws") { - opt.set('ws_host', queryParam.host || ""); - opt.set('ws_path', queryParam.path || ""); + opt.set(dom_prefix + 'ws_host', queryParam.host || ""); + opt.set(dom_prefix + 'ws_path', queryParam.path || ""); } else if (queryParam.type === "h2" || queryParam.type === "http") { - opt.set('h2_host', queryParam.host || ""); - opt.set('h2_path', queryParam.path || ""); + opt.set(dom_prefix + 'h2_host', queryParam.host || ""); + opt.set(dom_prefix + 'h2_path', queryParam.path || ""); } else if (queryParam.type === "quic") { opt.set('quic_guise', queryParam.headerType || "none"); opt.set('quic_security', queryParam.quicSecurity); @@ -743,17 +807,19 @@ local has_xray = api.is_finded("xray") opt.set('grpc_serviceName', (queryParam.serviceName || queryParam.path) || ""); opt.set('grpc_mode', queryParam.mode); } - + if (m.hash) { opt.set('remarks', decodeURI(m.hash.substr(1))); } - } else if (ssu[0] === "brook") { + } + if (ssu[0] === "brook") { + dom_prefix = "brook_" var stype = "Brook"; var m = parseNodeUrl(ssrurl); - + var from_protocol = m.host; var protocol = from_protocol.split('server').join('client'); - + var queryParam = {}; if (m.search.length > 1) { var query = m.search.split('?'); @@ -765,28 +831,28 @@ local has_xray = api.is_finded("xray") queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || ''); } } - + var password = queryParam.password; if (password === "") { s.innerHTML = "<%:Invalid Share URL Format%>"; return false; } - + opt.set('type', stype); - opt.set('brook_protocol', protocol); - opt.set('password', password); - + opt.set(dom_prefix + 'protocol', protocol); + opt.set(dom_prefix + 'password', password); + if (protocol == 'wsclient' || protocol == 'wssclient') { - opt.set('brook_protocol', 'wsclient'); + opt.set(dom_prefix + 'protocol', 'wsclient'); var wsserver = queryParam[from_protocol].split('://'); wsserver = wsserver[1].split('/'); var path = wsserver[1] && '/' + wsserver[1] || '/ws'; var server = wsserver[0].split(':'); - opt.set('address', server[0]); - opt.set('port', server[1]); - opt.set('ws_path', path); + opt.set(dom_prefix + 'address', server[0]); + opt.set(dom_prefix + 'port', server[1]); + opt.set(dom_prefix + 'ws_path', path); if (protocol == 'wssclient') { - opt.set('brook_tls', true); + opt.set(dom_prefix + 'tls', true); } } else { var server = queryParam[from_protocol].split(':'); @@ -794,14 +860,16 @@ local has_xray = api.is_finded("xray") s.innerHTML = "<%:Invalid Share URL Format%>"; return false; } - opt.set('address', server[0]); - opt.set('port', server[1]); + opt.set(dom_prefix + 'address', server[0]); + opt.set(dom_prefix + 'port', server[1]); } - + if (m.hash) { opt.set('remarks', decodeURI(m.hash.substr(1))); } - } else if (ssu[0] === "hysteria") { + } + if (ssu[0] === "hysteria") { + dom_prefix = "hysteria_" var stype = "Hysteria"; var m = parseNodeUrl(ssrurl); var queryParam = {}; @@ -815,31 +883,33 @@ local has_xray = api.is_finded("xray") queryParam[decodeURIComponent(params[0])] = decodeURIComponent(params[1] || ''); } } - opt.set('address', m.hostname); - opt.set('port', m.port || "443"); opt.set('type', stype); - opt.set('hysteria_protocol', queryParam.protocol); - opt.set('hysteria_obfs', queryParam.obfsParam); - opt.set('hysteria_auth_type', "string"); - opt.set('hysteria_auth_password', queryParam.auth); - opt.set('tls_serverName', queryParam.peer); + opt.set(dom_prefix + 'address', m.hostname); + opt.set(dom_prefix + 'port', m.port || "443"); + opt.set(dom_prefix + 'protocol', queryParam.protocol); + opt.set(dom_prefix + 'obfs', queryParam.obfsParam); + opt.set(dom_prefix + 'auth_type', "string"); + opt.set(dom_prefix + 'auth_password', queryParam.auth); + opt.set(dom_prefix + 'tls_serverName', queryParam.peer); if (queryParam.insecure && queryParam.insecure == "1") { - opt.set('tls_allowInsecure', true); + opt.set(dom_prefix + 'tls_allowInsecure', true); } - opt.set('hysteria_alpn', queryParam.alpn); - opt.set('hysteria_up_mbps', queryParam.upmbps); - opt.set('hysteria_down_mbps', queryParam.downmbps); + opt.set(dom_prefix + 'alpn', queryParam.alpn); + opt.set(dom_prefix + 'up_mbps', queryParam.upmbps); + opt.set(dom_prefix + 'down_mbps', queryParam.downmbps); if (m.hash) { opt.set('remarks', decodeURI(m.hash.substr(1))); } + } + if (dom_prefix && dom_prefix != null) { + if (opt.get(dom_prefix + 'port').value) { + opt.get(dom_prefix + 'port').focus(); + opt.get(dom_prefix + 'port').blur(); + } } else { s.innerHTML = "<%:Invalid Share URL Format%>: " + ssu[0]; return false; } - if (opt.get('port') && opt.get('port').value) { - opt.get('port').focus(); - opt.get('port').blur(); - } s.innerHTML = "<%:Import Finished %>"; return false; }