mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-07-27 10:31:38 +08:00
930 lines
32 KiB
Lua
Executable File
930 lines
32 KiB
Lua
Executable File
#!/usr/bin/lua
|
||
|
||
require "luci.sys"
|
||
local ucursor = require "luci.model.uci".cursor()
|
||
local json = require "luci.jsonc"
|
||
|
||
local server_section = arg[1]
|
||
local proto = arg[2] or "tcp"
|
||
local local_port = arg[3] or "0"
|
||
local socks_port = arg[4] or "0"
|
||
|
||
local chain = arg[5] or "0"
|
||
local chain_local_port = string.split(chain, "/")[2] or "0"
|
||
|
||
local server = ucursor:get_all("shadowsocksr", server_section)
|
||
local socks_server = ucursor:get_all("shadowsocksr", "@socks5_proxy[0]") or {}
|
||
local xray_fragment = ucursor:get_all("shadowsocksr", "@global_xray_fragment[0]") or {}
|
||
local xray_noise = ucursor:get_all("shadowsocksr", "@xray_noise_packets[0]") or {}
|
||
local outbound_settings = nil
|
||
local xray_version = nil
|
||
local xray_version_val = 0
|
||
|
||
local node_id = server_section
|
||
local remarks = server.alias or ""
|
||
local b64decode = nixio.bin.b64decode
|
||
|
||
local function base64Decode(text)
|
||
local raw = text
|
||
if not text or text == "" then
|
||
return ''
|
||
end
|
||
text = text:gsub("%z", "")
|
||
text = text:gsub("_", "/")
|
||
text = text:gsub("-", "+")
|
||
local mod4 = #text % 4
|
||
text = text .. string.sub('====', mod4 + 1)
|
||
local result = b64decode(text)
|
||
if result then
|
||
return result:gsub("%z", "")
|
||
else
|
||
return raw
|
||
end
|
||
end
|
||
|
||
local function cleanEmptyTables(t)
|
||
if type(t) ~= "table" then return nil end
|
||
for k, v in pairs(t) do
|
||
if type(v) == "table" then
|
||
t[k] = cleanEmptyTables(v)
|
||
end
|
||
end
|
||
return next(t) and t or nil
|
||
end
|
||
|
||
-- 确保正确判断程序是否存在
|
||
local function is_finded(e)
|
||
return luci.sys.exec(string.format('type -t -p "%s" 2>/dev/null', e)) ~= ""
|
||
end
|
||
|
||
-- 获取 Xray 版本号
|
||
if is_finded("xray") then
|
||
local version = luci.sys.exec("xray version 2>&1")
|
||
if version and version ~= "" then
|
||
xray_version = version:match("Xray%s+([%d%.]+)")
|
||
end
|
||
end
|
||
|
||
-- 将 Xray 版本号转换为数字
|
||
if xray_version and xray_version ~= "" then
|
||
local major, minor, patch =
|
||
xray_version:match("(%d+)%.?(%d*)%.?(%d*)")
|
||
|
||
major = tonumber(major) or 0
|
||
minor = tonumber(minor) or 0
|
||
patch = tonumber(patch) or 0
|
||
|
||
xray_version_val = major * 10000 + minor * 100 + patch
|
||
end
|
||
|
||
function vmess_vless()
|
||
outbound_settings = {
|
||
vnext = {
|
||
{
|
||
address = server.server,
|
||
port = tonumber(server.server_port),
|
||
users = {
|
||
{
|
||
id = server.vmess_id,
|
||
alterId = (server.v2ray_protocol == "vmess" or not server.v2ray_protocol) and tonumber(server.alter_id) or nil,
|
||
security = (server.v2ray_protocol == "vmess" or not server.v2ray_protocol) and server.security or nil,
|
||
testpre = (server.v2ray_protocol == "vless" or not server.v2ray_protocol) and tonumber(server.preconns) or nil,
|
||
encryption = (server.v2ray_protocol == "vless" or (not server.v2ray_protocol and server.vless_encryption)) and (server.vless_encryption or "none") or nil,
|
||
flow = (server.v2ray_protocol == "vless" and (server.xtls == "1" or server.tls == "1" or server.reality == "1"
|
||
or (server.vless_encryption and server.vless_encryption ~= "" and server.vless_encryption ~= "none")) and (
|
||
server.transport == "raw" or server.transport == "tcp" or server.transport == "xhttp" or server.transport == "splithttp") and (
|
||
server.tls_flow and server.tls_flow ~= "none")) and server.tls_flow or nil
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
end
|
||
function trojan_shadowsocks()
|
||
outbound_settings = {
|
||
servers = {
|
||
{
|
||
address = server.server,
|
||
port = tonumber(server.server_port),
|
||
password = server.password,
|
||
method = ((server.v2ray_protocol == "shadowsocks") and server.encrypt_method_ss) or nil,
|
||
uot = (server.v2ray_protocol == "shadowsocks") and (server.uot == '1') or nil,
|
||
ivCheck = (server.v2ray_protocol == "shadowsocks") and (server.ivCheck == '1') or nil,
|
||
}
|
||
}
|
||
}
|
||
end
|
||
function socks_http()
|
||
outbound_settings = {
|
||
version = server.socks_ver or nil,
|
||
servers = {
|
||
{
|
||
address = server.server,
|
||
port = tonumber(server.server_port),
|
||
users = (server.auth_enable == "1") and {
|
||
{
|
||
user = server.username,
|
||
pass = server.password
|
||
}
|
||
} or nil
|
||
}
|
||
}
|
||
}
|
||
end
|
||
function wireguard()
|
||
-- 处理 reserved 字段,支持逗号分隔的数字或 Base64 编码
|
||
local reserved = nil
|
||
if server.reserved then
|
||
local bytes = {}
|
||
if not server.reserved:match("[^%d,]+") then
|
||
-- 纯数字和逗号,解析为数字列表
|
||
server.reserved:gsub("%d+", function(b)
|
||
bytes[#bytes + 1] = tonumber(b)
|
||
end)
|
||
else
|
||
-- Base64 编码的二进制数据
|
||
local result = base64Decode(server.reserved)
|
||
for i = 1, #result do
|
||
bytes[i] = result:byte(i)
|
||
end
|
||
end
|
||
reserved = #bytes > 0 and bytes or nil
|
||
end
|
||
|
||
outbound_settings = {
|
||
secretKey = server.private_key,
|
||
address = server.local_addresses,
|
||
peers = {
|
||
{
|
||
publicKey = server.peer_pubkey,
|
||
preSharedKey = server.preshared_key,
|
||
endpoint = server.server .. ":" .. server.server_port,
|
||
keepAlive = tonumber(server.keepaliveperiod),
|
||
allowedIPs = (server.allowedips) or nil,
|
||
}
|
||
},
|
||
noKernelTun = (server.kernelmode == "1") and true or false,
|
||
reserved = reserved,
|
||
mtu = tonumber(server.mtu)
|
||
}
|
||
end
|
||
function xray_hysteria2()
|
||
outbound_settings = {
|
||
version = (server.v2ray_protocol == "hysteria2") and 2 or nil,
|
||
address = server.server,
|
||
port = tonumber(server.server_port)
|
||
}
|
||
end
|
||
local outbound = {}
|
||
function outbound:new(o)
|
||
o = o or {}
|
||
setmetatable(o, self)
|
||
self.__index = self
|
||
return o
|
||
end
|
||
function outbound:handleIndex(index)
|
||
local switch = {
|
||
vmess = function()
|
||
vmess_vless()
|
||
end,
|
||
vless = function()
|
||
vmess_vless()
|
||
end,
|
||
trojan = function()
|
||
trojan_shadowsocks()
|
||
end,
|
||
shadowsocks = function()
|
||
trojan_shadowsocks()
|
||
end,
|
||
socks = function()
|
||
socks_http()
|
||
end,
|
||
http = function()
|
||
socks_http()
|
||
end,
|
||
wireguard = function()
|
||
wireguard()
|
||
end,
|
||
hysteria2 = function()
|
||
xray_hysteria2()
|
||
end
|
||
}
|
||
if switch[index] then
|
||
switch[index]()
|
||
end
|
||
end
|
||
local settings = outbound:new()
|
||
settings:handleIndex(server.v2ray_protocol)
|
||
local Xray = {
|
||
log = {
|
||
-- error = "/var/ssrplus.log",
|
||
loglevel = "warning"
|
||
},
|
||
|
||
-- 初始化 inbounds 表
|
||
inbounds = {},
|
||
|
||
-- 初始化 outbounds 表
|
||
outbounds = {},
|
||
}
|
||
-- 传入连接
|
||
-- 添加 dokodemo-door 配置,如果 local_port 不为 0
|
||
if local_port ~= "0" then
|
||
table.insert(Xray.inbounds, {
|
||
-- listening
|
||
port = tonumber(local_port),
|
||
protocol = "dokodemo-door",
|
||
settings = {network = proto, followRedirect = true},
|
||
sniffing = {
|
||
enabled = true,
|
||
destOverride = {"http", "tls", "quic"},
|
||
metadataOnly = false,
|
||
domainsExcluded = {
|
||
"courier.push.apple.com",
|
||
"rbsxbxp-mim.vivox.com",
|
||
"rbsxbxp.www.vivox.com",
|
||
"rbsxbxp-ws.vivox.com",
|
||
"rbspsxp.www.vivox.com",
|
||
"rbspsxp-mim.vivox.com",
|
||
"rbspsxp-ws.vivox.com",
|
||
"rbswxp.www.vivox.com",
|
||
"rbswxp-mim.vivox.com",
|
||
"disp-rbspsp-5-1.vivox.com",
|
||
"disp-rbsxbp-5-1.vivox.com",
|
||
"proxy.rbsxbp.vivox.com",
|
||
"proxy.rbspsp.vivox.com",
|
||
"proxy.rbswp.vivox.com",
|
||
"rbswp.vivox.com",
|
||
"rbsxbp.vivox.com",
|
||
"rbspsp.vivox.com",
|
||
"rbspsp.www.vivox.com",
|
||
"rbswp.www.vivox.com",
|
||
"rbsxbp.www.vivox.com",
|
||
"rbsxbxp.vivox.com",
|
||
"rbspsxp.vivox.com",
|
||
"rbswxp.vivox.com",
|
||
"Mijia Cloud",
|
||
"dlg.io.mi.com"
|
||
}
|
||
}
|
||
})
|
||
end
|
||
|
||
-- 开启 socks 代理
|
||
-- 检查是否启用 socks 代理
|
||
if proto and proto:find("tcp") and socks_port ~= "0" then
|
||
table.insert(Xray.inbounds, {
|
||
-- socks
|
||
protocol = "socks",
|
||
port = tonumber(socks_port),
|
||
settings = {
|
||
auth = socks_server.socks5_auth or "noauth",
|
||
udp = true,
|
||
mixed = ((socks_server.socks5_mixed == '1') and true or false) or (socks_server.server == 'same') and nil,
|
||
accounts = (socks_server.server ~= "same" and (socks_server.socks5_auth and socks_server.socks5_auth ~= "noauth")) and {
|
||
{
|
||
user = socks_server.socks5_user,
|
||
pass = socks_server.socks5_pass
|
||
}
|
||
} or nil
|
||
} or nil
|
||
})
|
||
end
|
||
|
||
-- 传出连接
|
||
Xray.outbounds = {
|
||
{
|
||
protocol = (server.v2ray_protocol == "hysteria2") and "hysteria" or server.v2ray_protocol,
|
||
settings = outbound_settings,
|
||
-- 底层传输配置
|
||
streamSettings = (server.v2ray_protocol ~= "wireguard") and {
|
||
network = (server.v2ray_protocol == "hysteria2") and "hysteria" or (server.transport or "raw"),
|
||
security = (server.xtls == '1') and "xtls" or (server.tls == '1') and "tls" or (server.reality == '1') and "reality" or nil,
|
||
tlsSettings = (server.tls == '1') and {
|
||
-- tls
|
||
alpn = (server.tls_alpn and server.tls_alpn ~= "") and (function()
|
||
local alpn = {}
|
||
string.gsub(server.tls_alpn, '[^,]+', function(w)
|
||
table.insert(alpn, w)
|
||
end)
|
||
if #alpn > 0 then
|
||
return alpn
|
||
else
|
||
return nil
|
||
end
|
||
end)() or nil,
|
||
fingerprint = server.fingerprint,
|
||
allowInsecure = (function()
|
||
if server.tls_CertSha and server.tls_CertSha ~= "" then return nil end
|
||
if os.date("%Y.%m.%d") < "2026.06.01" then
|
||
return server.insecure == "1"
|
||
end
|
||
return nil
|
||
end)(),
|
||
serverName = server.tls_host,
|
||
certificates = server.certificate and {
|
||
usage = "verify",
|
||
certificateFile = server.certpath
|
||
} or nil,
|
||
pinnedPeerCertSha256 = (function()
|
||
if xray_version_val < 260131 then return nil end
|
||
if not server.tls_CertSha then return "" end
|
||
return server.tls_CertSha
|
||
end)(),
|
||
verifyPeerCertByName = (function()
|
||
if xray_version_val < 260131 then return nil end
|
||
if not server.tls_CertByName then return "" end
|
||
return server.tls_CertByName
|
||
end)(),
|
||
echConfigList = (server.enable_ech == "1") and server.ech_config or nil,
|
||
echForceQuery = (server.enable_ech == "1") and (server.ech_ForceQuery or "none") or nil
|
||
} or nil,
|
||
xtlsSettings = (server.xtls == '1') and server.tls_host and {
|
||
-- xtls
|
||
allowInsecure = (server.insecure == "1") and true or nil,
|
||
serverName = server.tls_host,
|
||
minVersion = "1.3"
|
||
} or nil,
|
||
realitySettings = (server.reality == '1') and {
|
||
publicKey = server.reality_publickey,
|
||
shortId = server.reality_shortid or "",
|
||
spiderX = server.reality_spiderx or "",
|
||
fingerprint = server.fingerprint,
|
||
mldsa65Verify = (server.enable_mldsa65verify == '1') and server.reality_mldsa65verify or nil,
|
||
serverName = server.tls_host
|
||
} or nil,
|
||
rawSettings = ((server.transport == "raw" or server.transport == "tcp") and (server.tcp_guise and server.tcp_guise ~= "none")) and {
|
||
-- tcp
|
||
header = {
|
||
type = server.tcp_guise,
|
||
request = (server.tcp_guise == "http") and {
|
||
path = server.http_path and (function()
|
||
local t, r = server.http_path, {}
|
||
for _, v in ipairs(t) do
|
||
r[#r + 1] = (v == "" and "/" or v)
|
||
end
|
||
return r
|
||
end)() or {"/"},
|
||
headers = (server.http_path or server.user_agent) and {
|
||
Host = server.http_path,
|
||
["User-Agent"] = server.user_agent and {server.user_agent} or nil
|
||
} or nil
|
||
} or nil
|
||
}
|
||
} or nil,
|
||
kcpSettings = (server.transport == "kcp") and {
|
||
-- kcp
|
||
mtu = tonumber(server.mtu),
|
||
tti = tonumber(server.tti),
|
||
uplinkCapacity = tonumber(server.uplink_capacity),
|
||
downlinkCapacity = tonumber(server.downlink_capacity),
|
||
congestion = (server.congestion == "1") and true or false,
|
||
readBufferSize = tonumber(server.read_buffer_size),
|
||
writeBufferSize = tonumber(server.write_buffer_size)
|
||
} or nil,
|
||
wsSettings = (server.transport == "ws") and (server.ws_path or server.ws_host or server.tls_host) and {
|
||
-- ws
|
||
host = server.ws_host or server.tls_host or nil,
|
||
path = server.ws_path or "/",
|
||
headers = server.user_agent and {
|
||
["User-Agent"] = server.user_agent
|
||
} or nil,
|
||
maxEarlyData = tonumber(server.ws_ed) or nil,
|
||
earlyDataHeaderName = server.ws_ed_header or nil,
|
||
heartbeatPeriod = tonumber(server.ws_heartbeatPeriod) or nil
|
||
} or nil,
|
||
httpupgradeSettings = (server.transport == "httpupgrade") and {
|
||
-- httpupgrade
|
||
host = (server.httpupgrade_host or server.tls_host) or nil,
|
||
path = server.httpupgrade_path or "",
|
||
headers = server.user_agent and {
|
||
["User-Agent"] = server.user_agent
|
||
} or nil
|
||
} or nil,
|
||
xhttpSettings = (server.transport == "xhttp" or server.transport == "splithttp") and {
|
||
-- xhttp
|
||
mode = server.xhttp_mode or "auto",
|
||
host = (server.xhttp_host or server.tls_host) or nil,
|
||
path = server.xhttp_path or "/",
|
||
extra = (function()
|
||
local extra = {}
|
||
-- 解析 xhttp_extra(Base64 编码的 JSON)
|
||
if (server.enable_xhttp_extra == "1" and server.xhttp_extra) then
|
||
local ok, parsed = pcall(json.parse, base64Decode(server.xhttp_extra))
|
||
if ok and type(parsed) == "table" then
|
||
extra = parsed.extra or parsed -- 取 "extra" 节,若无则整个 parsed
|
||
end
|
||
end
|
||
-- 处理 User-Agent
|
||
if server.user_agent and server.user_agent ~= "" then
|
||
extra.headers = extra.headers or {}
|
||
if not extra.headers["User-Agent"] and not extra.headers["user-agent"] then
|
||
extra.headers["User-Agent"] = server.user_agent
|
||
end
|
||
end
|
||
-- 递归清理空表(如空 headers 会被删除)
|
||
return cleanEmptyTables(extra)
|
||
end)()
|
||
} or nil,
|
||
httpSettings = (server.transport == "h2") and {
|
||
-- h2
|
||
path = server.h2_path or "",
|
||
host = {server.h2_host} or nil,
|
||
read_idle_timeout = tonumber(server.read_idle_timeout) or nil,
|
||
health_check_timeout = tonumber(server.health_check_timeout) or nil
|
||
} or nil,
|
||
quicSettings = (server.transport == "quic") and {
|
||
-- quic
|
||
security = server.quic_security,
|
||
key = server.quic_key,
|
||
header = {type = server.quic_guise}
|
||
} or nil,
|
||
grpcSettings = (server.transport == "grpc") and {
|
||
-- grpc
|
||
serviceName = (server.serviceName and server.serviceName ~= "") and server.serviceName or nil,
|
||
multiMode = (server.grpc_mode == "multi") and true or nil,
|
||
idle_timeout = server.idle_timeout and (tonumber(server.idle_timeout) < 10 and 10 or tonumber(server.idle_timeout)) or nil,
|
||
health_check_timeout = server.health_check_timeout and tonumber(server.health_check_timeout) or nil,
|
||
permit_without_stream = (server.permit_without_stream == "1") and true or nil,
|
||
initial_windows_size = server.initial_windows_size and tonumber(server.initial_windows_size) or nil,
|
||
user_agent = server.user_agent
|
||
} or nil,
|
||
hysteriaSettings = (server.v2ray_protocol == "hysteria2") and {
|
||
-- hysteria2
|
||
version = 2,
|
||
auth = server.hy2_auth,
|
||
congestion = server.hy2_tcpcongestion or nil,
|
||
up = tonumber(server.uplink_capacity) and tonumber(server.uplink_capacity) .. " mbps" or nil,
|
||
down = tonumber(server.downlink_capacity) and tonumber(server.downlink_capacity) .. " mbps" or nil,
|
||
udphop = (server.flag_port_hopping == "1") and {
|
||
port = string.gsub(server.port_range, ":", "-"),
|
||
interval = (function()
|
||
local v = tonumber((server.hopinterval or "30"):match("^%d+"))
|
||
return (v and v >= 5) and v or 30
|
||
end)()
|
||
} or nil,
|
||
initStreamReceiveWindow = (server.flag_quicparam == "1" and server.initstreamreceivewindow) and tonumber(server.initstreamreceivewindow) or nil,
|
||
maxStreamReceiveWindow = (server.flag_quicparam == "1" and server.maxstreamreceivewindow) and tonumber(server.maxstreamreceivewindow) or nil,
|
||
initConnectionReceiveWindow = (server.flag_quicparam == "1" and server.initconnreceivewindow) and tonumber(server.initconnreceivewindow) or nil,
|
||
maxConnectionReceiveWindow = (server.flag_quicparam == "1" and server.maxconnreceivewindow) and tonumber(server.maxconnreceivewindow) or nil,
|
||
maxIdleTimeout = (server.flag_quicparam == "1" and (function()
|
||
local timeoutStr = tostring(server.maxidletimeout or "")
|
||
local timeout = tonumber(timeoutStr:match("^%d+"))
|
||
if timeout and timeout >= 4 and timeout <= 120 then
|
||
return timeout
|
||
end
|
||
end)()) or 30,
|
||
keepAlivePeriod = (server.flag_quicparam == "1" and server.keepaliveperiod) and tonumber(server.keepaliveperiod) or nil,
|
||
disablePathMTUDiscovery = (server.flag_quicparam == "1" and tostring(server.disablepathmtudiscovery) == "1") and true or nil
|
||
} or nil,
|
||
finalmask = (function()
|
||
local finalmask
|
||
if server.transport == "kcp" then
|
||
local map = {none = "none", srtp = "header-srtp", utp = "header-utp", ["wechat-video"] = "header-wechat",
|
||
dtls = "header-dtls", wireguard = "header-wireguard", dns = "header-dns"}
|
||
local udp = {}
|
||
if server.kcp_guise and server.kcp_guise ~= "none" then
|
||
local g = { type = map[server.kcp_guise] }
|
||
if server.kcp_guise == "dns" and server.kcp_domain and server.kcp_domain ~= "" then
|
||
g.settings = { domain = server.kcp_domain }
|
||
end
|
||
udp[#udp+1] = g
|
||
end
|
||
local c = { type = (server.seed and server.seed ~= "") and "mkcp-aes128gcm" or "mkcp-original" }
|
||
if server.seed and server.seed ~= "" then
|
||
c.settings = { password = server.seed }
|
||
end
|
||
udp[#udp+1] = c
|
||
finalmask = { udp = udp }
|
||
elseif (server.flag_obfs == "1" and (server.v2ray_protocol == "hysteria2" and server.obfs_type and server.obfs_type ~= "")) then
|
||
finalmask = {
|
||
udp = {{
|
||
type = server.obfs_type,
|
||
settings = server.salamander and {
|
||
password = server.salamander
|
||
} or nil
|
||
}}
|
||
}
|
||
end
|
||
if server.finalmask and server.finalmask ~= "" then
|
||
local ok, fm = pcall(json.parse, base64Decode(server.finalmask))
|
||
if ok and type(fm) == "table" then
|
||
finalmask = fm
|
||
end
|
||
end
|
||
return cleanEmptyTables(finalmask)
|
||
end)(),
|
||
sockopt = {
|
||
mark = 250,
|
||
tcpFastOpen = (function()
|
||
if server.transport == "xhttp" then
|
||
return (server.fast_open == "1") and true or false
|
||
elseif server.v2ray_protocol == "hysteria2" then
|
||
return (server.fast_open == "1") and true or nil
|
||
else
|
||
return nil
|
||
end
|
||
end)(), -- XHTTP Tcp Fast Open
|
||
tcpMptcp = (server.mptcp == "1") and true or nil, -- MPTCP
|
||
Penetrate = (server.mptcp == "1") and true or nil, -- Penetrate MPTCP
|
||
tcpcongestion = server.custom_tcpcongestion, -- 连接服务器节点的 TCP 拥塞控制算法
|
||
dialerProxy = (xray_fragment.fragment == "1" or xray_fragment.noise == "1") and
|
||
((remarks ~= nil and remarks ~= "") and (node_id .. "." .. remarks) or node_id) or nil
|
||
}
|
||
} or nil,
|
||
mux = (server.v2ray_protocol ~= "hysteria2" and server.v2ray_protocol ~= "wireguard") and {
|
||
-- mux
|
||
enabled = (server.mux == "1"), -- Mux
|
||
concurrency = (server.mux == "1" and (tonumber(server.concurrency) or -1)) or nil, -- TCP 最大并发连接数
|
||
xudpConcurrency = (server.mux == "1" and (tonumber(server.xudpConcurrency) or 16)) or nil, -- UDP 最大并发连接数
|
||
xudpProxyUDP443 = (server.mux == "1" and (server.xudpProxyUDP443 or "reject")) or nil -- 对被代理的 UDP/443 流量处理方式
|
||
} or nil
|
||
}
|
||
}
|
||
|
||
-- 添加带有 fragment 设置的 dialerproxy 配置
|
||
if xray_fragment.fragment ~= "0" or (xray_fragment.noise ~= "0" and xray_noise.enabled ~= "0") then
|
||
table.insert(Xray.outbounds, {
|
||
protocol = "freedom",
|
||
tag = (remarks ~= nil and remarks ~= "") and (node_id .. "." .. remarks) or node_id,
|
||
settings = {
|
||
domainStrategy = (xray_fragment.noise == "1" and xray_noise.enabled == "1") and xray_noise.domainStrategy,
|
||
fragment = (xray_fragment.fragment == "1") and {
|
||
packets = (xray_fragment.fragment_packets and xray_fragment.fragment_packets ~= "") and xray_fragment.fragment_packets or nil,
|
||
length = (xray_fragment.fragment_length and xray_fragment.fragment_length ~= "") and xray_fragment.fragment_length or nil,
|
||
interval = (xray_fragment.fragment_interval and xray_fragment.fragment_interval ~= "") and xray_fragment.fragment_interval or nil
|
||
} or nil,
|
||
noises = (xray_fragment.noise == "1" and xray_noise.enabled == "1") and {
|
||
{
|
||
type = xray_noise.type,
|
||
packet = xray_noise.packet,
|
||
delay = xray_noise.delay:find("-") and xray_noise.delay or tonumber(xray_noise.delay)
|
||
}
|
||
} or nil
|
||
},
|
||
streamSettings = {
|
||
sockopt = {
|
||
mark = 250,
|
||
tcpFastOpen = (function()
|
||
if server.transport == "xhttp" then
|
||
return (server.fast_open == "1") and true or false
|
||
elseif server.v2ray_protocol == "hysteria2" then
|
||
return (server.fast_open == "1") and true or nil
|
||
else
|
||
return nil
|
||
end
|
||
end)(), -- XHTTP Tcp Fast Open
|
||
tcpMptcp = (server.mptcp == "1") and true or nil, -- MPTCP
|
||
Penetrate = (server.mptcp == "1") and true or nil, -- Penetrate MPTCP
|
||
tcpcongestion = server.custom_tcpcongestion -- 连接服务器节点的 TCP 拥塞控制算法
|
||
}
|
||
}
|
||
})
|
||
end
|
||
|
||
local cipher = "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:AES128-SHA:AES256-SHA:DES-CBC3-SHA"
|
||
local cipher13 = "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384"
|
||
local trojan = {
|
||
log_level = 3,
|
||
run_type = (proto == "nat" or proto == "tcp") and "nat" or "client",
|
||
local_addr = "0.0.0.0",
|
||
local_port = tonumber(local_port),
|
||
remote_addr = server.server,
|
||
remote_port = tonumber(server.server_port),
|
||
udp_timeout = 60,
|
||
-- 传入连接
|
||
password = {server.password},
|
||
-- 传出连接
|
||
ssl = {
|
||
verify = (server.insecure == "0") and true or false,
|
||
verify_hostname = (server.tls == "1") and true or false,
|
||
cert = (server.certificate) and server.certpath or nil,
|
||
cipher = cipher,
|
||
cipher_tls13 = cipher13,
|
||
sni = server.tls_host,
|
||
alpn = (server.tls == "1") and (function()
|
||
local alpn = {}
|
||
if server.tls_alpn and server.tls_alpn ~= "" then
|
||
string.gsub(server.tls_alpn, '[^,]+', function(w)
|
||
table.insert(alpn, w)
|
||
end)
|
||
end
|
||
if #alpn > 0 then
|
||
return alpn
|
||
else
|
||
return nil
|
||
end
|
||
end)() or {"h2", "http/1.1"},
|
||
curve = "",
|
||
reuse_session = true,
|
||
session_ticket = (server.tls_sessionTicket == "1") and true or false
|
||
},
|
||
tcp = {
|
||
-- tcp
|
||
no_delay = true,
|
||
keep_alive = true,
|
||
reuse_port = true,
|
||
fast_open = (server.fast_open == "1") and true or false,
|
||
fast_open_qlen = 20
|
||
}
|
||
}
|
||
local naiveproxy = {
|
||
proxy = (server.username and server.password and server.server and server.server_port) and "https://" .. server.username .. ":" .. server.password .. "@" .. server.server .. ":" .. server.server_port,
|
||
listen = (proto == "redir") and "redir" .. "://0.0.0.0:" .. tonumber(local_port) or "socks" .. "://0.0.0.0:" .. tonumber(local_port),
|
||
["insecure-concurrency"] = tonumber(server.concurrency) or 1
|
||
}
|
||
local ss = {
|
||
server = (server.kcp_enable == "1") and "127.0.0.1" or server.server,
|
||
server_port = tonumber(server.server_port),
|
||
local_address = "0.0.0.0",
|
||
local_port = tonumber(local_port),
|
||
mode = (proto == "tcp,udp") and "tcp_and_udp" or (proto .. "_only"),
|
||
password = server.password,
|
||
method = server.encrypt_method_ss,
|
||
timeout = tonumber(server.timeout),
|
||
fast_open = (server.fast_open == "1") and true or false,
|
||
reuse_port = true
|
||
}
|
||
local hysteria2 = {
|
||
server = (
|
||
server.server_port and
|
||
(
|
||
server.port_range and
|
||
(server.server .. ":" .. server.server_port .. "," .. string.gsub(server.port_range, ":", "-"))
|
||
or
|
||
(server.server .. ":" .. server.server_port)
|
||
)
|
||
or
|
||
(
|
||
server.port_range and
|
||
server.server .. ":" .. string.gsub(server.port_range, ":", "-")
|
||
or
|
||
server.server and server.server .. ":443"
|
||
)
|
||
),
|
||
bandwidth = (server.uplink_capacity or server.downlink_capacity) and {
|
||
up = tonumber(server.uplink_capacity) and tonumber(server.uplink_capacity) .. " mbps" or nil,
|
||
down = tonumber(server.downlink_capacity) and tonumber(server.downlink_capacity) .. " mbps" or nil
|
||
} or nil,
|
||
socks5 = (proto:find("tcp") and tonumber(socks_port) and tonumber(socks_port) ~= 0) and {
|
||
listen = "0.0.0.0:" .. tonumber(socks_port),
|
||
disableUDP = false
|
||
} or nil,
|
||
transport = server.transport_protocol and {
|
||
type = server.transport_protocol or "udp",
|
||
udp = (server.port_range and (server.hopinterval) and {
|
||
hopInterval = (server.port_range and (tonumber(server.hopinterval) .. "s") or nil)
|
||
} or nil)
|
||
} or nil,
|
||
--[[
|
||
tcpTProxy = (proto:find("tcp") and local_port ~= "0") and {
|
||
listen = "0.0.0.0:" .. tonumber(local_port)
|
||
} or nil,
|
||
]]--
|
||
tcpRedirect = (proto:find("tcp") and local_port ~= "0") and {
|
||
listen = "0.0.0.0:" .. tonumber(local_port)
|
||
} or nil,
|
||
udpTProxy = (proto:find("udp") and local_port ~= "0") and {
|
||
listen = "0.0.0.0:" .. tonumber(local_port)
|
||
} or nil,
|
||
obfs = (server.flag_obfs == "1") and {
|
||
type = server.obfs_type,
|
||
salamander = { password = server.salamander }
|
||
} or nil,
|
||
quic = (server.flag_quicparam == "1" ) and {
|
||
initStreamReceiveWindow = (server.initstreamreceivewindow and server.initstreamreceivewindow or nil),
|
||
maxStreamReceiveWindow = (server.maxstreamreceivewindow and server.maxstreamreceivewindow or nil),
|
||
initConnReceiveWindow = (server.initconnreceivewindow and server.initconnreceivewindow or nil),
|
||
maxConnReceiveWindow = (server.maxconnreceivewindow and server.maxconnreceivewindow or nil),
|
||
maxIdleTimeout = (tonumber(server.maxidletimeout) and tonumber(server.maxidletimeout) .. "s" or nil),
|
||
keepAlivePeriod = (tonumber(server.keepaliveperiod) and tonumber(server.keepaliveperiod) .. "s" or nil),
|
||
disablePathMTUDiscovery = (server.disablepathmtudiscovery == "1") and true or false
|
||
} or nil,
|
||
auth = server.hy2_auth,
|
||
tls = (server.tls_host and server.tls_host ~= "") and {
|
||
sni = server.tls_host,
|
||
alpn = (server.tls_alpn and server.tls_alpn ~= "") and (function()
|
||
local alpn = {}
|
||
string.gsub(server.tls_alpn, '[^,]+', function(w)
|
||
table.insert(alpn, w)
|
||
end)
|
||
if #alpn > 0 then
|
||
return alpn
|
||
else
|
||
return nil
|
||
end
|
||
end)() or nil,
|
||
--sni = server.tls_host or (server.tls_host and server.tls_alpn) or nil,
|
||
insecure = (server.insecure == "1") and true or false,
|
||
pinSHA256 = server.pinsha256 or nil
|
||
} or {
|
||
sni = server.server,
|
||
alpn = (server.type == "hysteria2") and (function()
|
||
local alpn = {}
|
||
if server.tls_alpn and server.tls_alpn ~= "" then
|
||
string.gsub(server.tls_alpn, '[^,]+', function(w)
|
||
table.insert(alpn, w)
|
||
end)
|
||
end
|
||
if #alpn > 0 then
|
||
return alpn
|
||
else
|
||
return nil
|
||
end
|
||
end)() or nil,
|
||
insecure = (server.insecure == "1") and true or false,
|
||
pinSHA256 = server.pinsha256 or nil
|
||
},
|
||
fast_open = (server.fast_open == "1") and true or false,
|
||
lazy = (server.lazy_mode == "1") and true or false
|
||
}
|
||
local shadowtls = {
|
||
client = {
|
||
server_addr = server.server_port and server.server .. ":" .. server.server_port or nil,
|
||
listen = "127.0.0.1:" .. tonumber(local_port),
|
||
tls_names = server.shadowtls_sni,
|
||
password = server.password
|
||
},
|
||
v3 = (server.shadowtls_protocol == "v3") and true or false,
|
||
disable_nodelay = (server.disable_nodelay == "1") and true or false,
|
||
fastopen = (server.fastopen == "1") and true or false,
|
||
strict = (server.strict == "1") and true or false
|
||
}
|
||
local chain_sslocal = {
|
||
locals = local_port ~= "0" and {
|
||
{
|
||
local_address = "0.0.0.0",
|
||
local_port = (chain_local_port == "0" and tonumber(server.local_port) or tonumber(chain_local_port)),
|
||
mode = (proto:find("tcp,udp") and "tcp_and_udp") or proto .. "_only",
|
||
protocol = "redir",
|
||
tcp_redir = "redirect",
|
||
--tcp_redir = "tproxy",
|
||
udp_redir = "tproxy"
|
||
},
|
||
socks_port ~= "0" and {
|
||
protocol = "socks",
|
||
local_address = "0.0.0.0",
|
||
local_port = tonumber(socks_port)
|
||
} or nil
|
||
} or {{
|
||
protocol = "socks",
|
||
local_address = "0.0.0.0",
|
||
local_port = tonumber(socks_port)
|
||
}},
|
||
servers = {
|
||
{
|
||
server = "127.0.0.1",
|
||
server_port = (tonumber(local_port) == 0 and tonumber(chain_local_port) or tonumber(local_port)),
|
||
method = server.sslocal_method,
|
||
password = server.sslocal_password
|
||
}
|
||
}
|
||
}
|
||
local chain_vmess = {
|
||
inbounds = (local_port ~= "0") and {
|
||
{
|
||
port = (chain_local_port == "0" and tonumber(server.local_port) or tonumber(chain_local_port)),
|
||
protocol = "dokodemo-door",
|
||
settings = {
|
||
network = proto,
|
||
followRedirect = true
|
||
},
|
||
streamSettings = {
|
||
sockopt = {tproxy = "redirect"}
|
||
},
|
||
sniffing = {
|
||
enable = true,
|
||
destOverride = {"http","tls"}
|
||
}
|
||
},
|
||
(proto:find("tcp") and socks_port ~= "0") and {
|
||
protocol = "socks",
|
||
port = tonumber(socks_port)
|
||
} or nil
|
||
} or { protocol = "socks",port = tonumber(socks_port) },
|
||
outbound = {
|
||
protocol = "vmess",
|
||
settings = {
|
||
vnext = {{
|
||
address = "127.0.0.1",
|
||
port = (tonumber(local_port) == 0 and tonumber(chain_local_port) or tonumber(local_port)),
|
||
users = {{
|
||
id = (server.vmess_uuid),
|
||
security = server.vmess_method,
|
||
level = 0
|
||
}}
|
||
}}
|
||
}
|
||
}
|
||
}
|
||
local tuic = {
|
||
relay = {
|
||
server = server.server_port and server.server .. ":" .. server.server_port,
|
||
ip = server.tuic_ip,
|
||
uuid = server.tuic_uuid,
|
||
password = server.tuic_passwd,
|
||
certificates = server.certificate and { server.certpath } or nil,
|
||
udp_relay_mode = server.udp_relay_mode,
|
||
congestion_control = server.congestion_control,
|
||
heartbeat = server.heartbeat and tonumber(server.heartbeat) .. "s" or nil,
|
||
timeout = server.timeout and tonumber(server.timeout) .. "s" or nil,
|
||
gc_interval = server.gc_interval and tonumber(server.gc_interval) .. "s" or nil,
|
||
gc_lifetime = server.gc_lifetime and tonumber(server.gc_lifetime) .. "s" or nil,
|
||
alpn = (server.tuic_alpn and server.tuic_alpn ~= "") and (function()
|
||
local alpn = {}
|
||
string.gsub(server.tuic_alpn, '[^,]+', function(w)
|
||
table.insert(alpn, w)
|
||
end)
|
||
if #alpn > 0 then
|
||
return alpn
|
||
else
|
||
return nil
|
||
end
|
||
end)() or nil,
|
||
ipstack_prefer = (server.tuic_dual_stack == "1") and server.ipstack_prefer or nil,
|
||
skip_cert_verify = (server.insecure == "1" or server.insecure == true or server.insecure == "true"),
|
||
disable_sni = (server.disable_sni == "1") and true or false,
|
||
zero_rtt_handshake = (server.zero_rtt_handshake == "1") and true or false,
|
||
send_window = tonumber(server.send_window),
|
||
receive_window = tonumber(server.receive_window)
|
||
},
|
||
["local"] = {
|
||
server = tonumber(socks_port) and "[::]:" .. (socks_port == "0" and local_port or tonumber(socks_port)),
|
||
dual_stack = (server.tuic_dual_stack == "1") and true or nil,
|
||
max_packet_size = tonumber(server.tuic_max_package_size)
|
||
}
|
||
}
|
||
local config = {}
|
||
function config:new(o)
|
||
o = o or {}
|
||
setmetatable(o, self)
|
||
self.__index = self
|
||
return o
|
||
end
|
||
function config:handleIndex(index)
|
||
local switch = {
|
||
ss = function()
|
||
ss.protocol = socks_port
|
||
if server.enable_plugin == "1" and server.plugin and server.plugin ~= "none" then
|
||
if server.plugin == "custom" then
|
||
ss.plugin = server.custom_plugin
|
||
else
|
||
ss.plugin = server.plugin
|
||
end
|
||
ss.plugin_opts = server.plugin_opts or nil
|
||
end
|
||
print(json.stringify(ss, 1))
|
||
end,
|
||
ssr = function()
|
||
ss.protocol = server.protocol
|
||
ss.protocol_param = server.protocol_param
|
||
ss.method = server.encrypt_method
|
||
ss.obfs = server.obfs
|
||
ss.obfs_param = server.obfs_param
|
||
print(json.stringify(ss, 1))
|
||
end,
|
||
v2ray = function()
|
||
print(json.stringify(Xray, 1))
|
||
end,
|
||
trojan = function()
|
||
print(json.stringify(trojan, 1))
|
||
end,
|
||
naiveproxy = function()
|
||
print(json.stringify(naiveproxy, 1))
|
||
end,
|
||
hysteria2 = function()
|
||
print(json.stringify(hysteria2, 1))
|
||
end,
|
||
shadowtls = function()
|
||
local chain_switch = {
|
||
sslocal = function()
|
||
if (chain:find("chain")) then
|
||
print(json.stringify(chain_sslocal, 1))
|
||
else
|
||
print(json.stringify(shadowtls, 1))
|
||
end
|
||
end,
|
||
vmess = function()
|
||
if (chain:find("chain")) then
|
||
print(json.stringify(chain_vmess, 1))
|
||
else
|
||
print(json.stringify(shadowtls, 1))
|
||
end
|
||
end
|
||
}
|
||
local ChainType = server.chain_type
|
||
if chain_switch[ChainType] then
|
||
chain_switch[ChainType]()
|
||
end
|
||
end,
|
||
tuic = function()
|
||
print(json.stringify(tuic, 1))
|
||
end
|
||
}
|
||
if switch[index] then
|
||
switch[index]()
|
||
end
|
||
end
|
||
local f = config:new()
|
||
f:handleIndex(server.type)
|