mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-08 13:14:12 +08:00
🌴 Sync 2026-03-05 23:51:42
This commit is contained in:
1 parent
97c049ed0a
commit
0acff03804
14253 files changed
+3893239
No files matched your search
@@ -0,0 +1,626 @@
|
||||
#!/usr/bin/lua
|
||||
--local ucursor = require "luci.model.uci"
|
||||
local ucursor = require "luci.model.uci".cursor()
|
||||
local json = require "luci.jsonc"
|
||||
local nixiofs = require "nixio.fs"
|
||||
|
||||
local proxy_section = ucursor:get_first("xclient", "global")
|
||||
local proxy = ucursor:get_all("xclient", proxy_section)
|
||||
|
||||
local tcp_server_section = proxy.global_server or nil
|
||||
local tcp_server = ucursor:get_all("xclient", tcp_server_section)
|
||||
|
||||
local udp_server_section = proxy.udp_relay_server or nil
|
||||
local udp_server = ucursor:get_all("xclient", udp_server_section)
|
||||
|
||||
local socks5_server_section = proxy.socks5_server or nil
|
||||
local socks5_server = ucursor:get_all("xclient", socks5_server_section)
|
||||
|
||||
local server_section = arg[1] or nil
|
||||
local node = ucursor:get_all("xclient", server_section)
|
||||
|
||||
local geoip_existence = false
|
||||
local geosite_existence = false
|
||||
|
||||
local xray_data_file_iterator = nixiofs.dir("/usr/share/xclient")
|
||||
|
||||
local proto = arg[2]
|
||||
local local_port = arg[3] or "1234"
|
||||
local socks_port = arg[4] or "1080"
|
||||
local _protocol = arg[5] or "redir"
|
||||
local _local = arg[6] or "0"
|
||||
|
||||
repeat
|
||||
local fn = xray_data_file_iterator()
|
||||
if fn == "geoip.dat" then
|
||||
geoip_existence = true
|
||||
end
|
||||
if fn == "geosite.dat" then
|
||||
geosite_existence = true
|
||||
end
|
||||
until fn == nil
|
||||
|
||||
|
||||
local function direct_outbound()
|
||||
return {
|
||||
protocol = "freedom",
|
||||
settings = {
|
||||
domainStrategy = "UseIP"
|
||||
},
|
||||
tag = "direct"
|
||||
}
|
||||
end
|
||||
|
||||
local function block_outbound()
|
||||
return {
|
||||
protocol = "blackhole",
|
||||
tag = "block",
|
||||
settings = {
|
||||
response = {
|
||||
type = "http"
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function dns_outbound()
|
||||
return {
|
||||
protocol = "dns",
|
||||
address = "1.1.1.1",
|
||||
network = "tcp,udp",
|
||||
port = 53,
|
||||
tag = "dns_outbound"
|
||||
}
|
||||
end
|
||||
|
||||
local function stream_tcp_fake_http_request(server)
|
||||
if server.tcp_guise == "http" then
|
||||
return {
|
||||
version = "1.1",
|
||||
method = "GET",
|
||||
path = server.http_path,
|
||||
headers = {
|
||||
Host = server.http_host,
|
||||
User_Agent = {
|
||||
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
|
||||
},
|
||||
Accept_Encoding = {"gzip, deflate"},
|
||||
Connection = {"keep-alive"},
|
||||
Pragma = "no-cache"
|
||||
}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_tcp_fake_http_response(server)
|
||||
if server.tcp_guise == "http" then
|
||||
return {
|
||||
version = "1.1",
|
||||
status = "200",
|
||||
reason = "OK",
|
||||
headers = {
|
||||
Content_Type = {"application/octet-stream", "video/mpeg"},
|
||||
Transfer_Encoding = {"chunked"},
|
||||
Connection = {"keep-alive"},
|
||||
Pragma = "no-cache"
|
||||
}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_tcp(server)
|
||||
if server.transport == "tcp" then
|
||||
return {
|
||||
header = {
|
||||
type = server.tcp_guise,
|
||||
request = stream_tcp_fake_http_request(server),
|
||||
response = stream_tcp_fake_http_response(server)
|
||||
}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_h2(server)
|
||||
if (server.transport == "h2") then
|
||||
return {
|
||||
path = server.h2_path,
|
||||
host = server.h2_host,
|
||||
read_idle_timeout = tonumber(server.read_idle_timeout) or nil,
|
||||
health_check_timeout = tonumber(server.health_check_timeout) or nil
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_grpc(server)
|
||||
if (server.transport == "grpc") then
|
||||
return {
|
||||
serviceName = server.serviceName or "",
|
||||
multiMode = server.grpc_multi_mode == "1",
|
||||
initial_windows_size = tonumber(server.initial_windows_size) or nil,
|
||||
idle_timeout = tonumber(server.idle_timeout) or nil,
|
||||
health_check_timeout = tonumber(server.health_check_timeout) or nil,
|
||||
permit_without_stream = (server.permit_without_stream == "1") and true or nil
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function stream_kcp(server)
|
||||
if server.transport == "mkcp" then
|
||||
local mkcp_seed = nil
|
||||
if server.seed ~= "" then
|
||||
mkcp_seed = server.seed
|
||||
end
|
||||
return {
|
||||
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),
|
||||
seed = mkcp_seed,
|
||||
header = {
|
||||
type = server.kcp_guise
|
||||
}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_quic(server)
|
||||
if server.transport == "quic" then
|
||||
return {
|
||||
security = server.quic_security,
|
||||
key = server.quic_key,
|
||||
header = {type = server.quic_guise}
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function stream_ws(server)
|
||||
if server.transport == "ws" then
|
||||
local headers = nil
|
||||
if (server.ws_host ~= nil) then
|
||||
headers = {
|
||||
Host = server.ws_host or server.tls_host
|
||||
}
|
||||
end
|
||||
return {
|
||||
path = server.ws_path,
|
||||
headers = headers
|
||||
}
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
local function tls_settings(server)
|
||||
local result = {
|
||||
fingerprint = server.fingerprint,
|
||||
rejectUnknownSni = (server.rejectUnknownSni == "1") and true or false,
|
||||
allowInsecure = (server.insecure == "1") and true or false,
|
||||
serverName = server.tls_host,
|
||||
}
|
||||
if server.alpn ~= nil then
|
||||
local alpn = {}
|
||||
for _, x in ipairs(server.alpn) do
|
||||
table.insert(alpn, x)
|
||||
end
|
||||
result["alpn"] = alpn
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
local function reality_settings(server)
|
||||
local result = {
|
||||
fingerprint = server.fingerprint,
|
||||
shortId = server.shortId,
|
||||
publicKey = server.publicKey,
|
||||
serverName = server.tls_host,
|
||||
spiderX = server.spiderX,
|
||||
}
|
||||
return result
|
||||
end
|
||||
|
||||
local function shadowsocks_outbound(server, tag)
|
||||
return {
|
||||
protocol = "shadowsocks",
|
||||
tag = tag,
|
||||
settings = {
|
||||
servers = {
|
||||
{
|
||||
address = server.server,
|
||||
port = tonumber(server.server_port),
|
||||
password = server.password,
|
||||
method = server.encrypt_method_v2ray_ss
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = {
|
||||
network = server.transport,
|
||||
security = (server.security == 'tls') and "tls" or "none",
|
||||
tlsSettings = (server.security == 'tls') and "tls" and tls_settings(server) or nil,
|
||||
quicSettings = stream_quic(server),
|
||||
kcpSettings = stream_kcp(server),
|
||||
tcpSettings = stream_tcp(server) and server.protocol ~= "shadowsocks" or nil,
|
||||
wsSettings = stream_ws(server),
|
||||
grpcSettings = stream_grpc(server),
|
||||
httpSettings = stream_h2(server)
|
||||
},
|
||||
mux = (server.mux == "1" and server.security == 'tls' or server.security == 'none') and {
|
||||
enabled = true,
|
||||
concurrency = tonumber(server.concurrency)
|
||||
} or nil
|
||||
}
|
||||
end
|
||||
|
||||
local function vmess_outbound(server, tag)
|
||||
return {
|
||||
protocol = "vmess",
|
||||
tag = tag,
|
||||
settings = {
|
||||
vnext = {
|
||||
{
|
||||
address = server.server,
|
||||
port = tonumber(server.server_port),
|
||||
users = {
|
||||
{
|
||||
id = server.vmess_id,
|
||||
alterId = 0,
|
||||
security = server.vmess_encryption
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = {
|
||||
network = server.transport,
|
||||
security = (server.security == 'tls') and "tls" or "none",
|
||||
tlsSettings = (server.security == 'tls') and "tls" and tls_settings(server) or nil,
|
||||
tcpSettings = stream_tcp(server),
|
||||
wsSettings = stream_ws(server),
|
||||
grpcSettings = stream_grpc(server),
|
||||
httpSettings = stream_h2(server),
|
||||
quicSettings = stream_quic(server),
|
||||
kcpSettings = stream_kcp(server),
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function vless_outbound(server, tag)
|
||||
local flow = server.flow
|
||||
if server.flow == "none" then
|
||||
flow = nil
|
||||
end
|
||||
return {
|
||||
protocol = "vless",
|
||||
tag = tag,
|
||||
settings = {
|
||||
vnext = {
|
||||
{
|
||||
address = server.server,
|
||||
port = tonumber(server.server_port),
|
||||
users = {
|
||||
{
|
||||
id = server.vmess_id,
|
||||
flow = flow,
|
||||
encryption = server.vless_encryption
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = {
|
||||
network = server.transport,
|
||||
security = (server.security == 'reality') and "reality" or (server.security == 'tls') and "tls" or "none",
|
||||
tlsSettings = (server.security == 'tls') and "tls" and tls_settings(server) or nil,
|
||||
realitySettings = (server.security == 'reality') and "reality" and reality_settings(server) or nil,
|
||||
tcpSettings = stream_tcp(server),
|
||||
wsSettings = stream_ws(server),
|
||||
grpcSettings = stream_grpc(server),
|
||||
httpSettings = stream_h2(server),
|
||||
quicSettings = stream_quic(server),
|
||||
kcpSettings = stream_kcp(server),
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function trojan_outbound(server, tag)
|
||||
local flow = server.flow
|
||||
if server.flow == "none" then
|
||||
flow = nil
|
||||
end
|
||||
return {
|
||||
protocol = "trojan",
|
||||
tag = tag,
|
||||
settings = {
|
||||
servers = {
|
||||
{
|
||||
address = server.server,
|
||||
port = tonumber(server.server_port),
|
||||
password = server.password,
|
||||
flow = flow,
|
||||
}
|
||||
}
|
||||
},
|
||||
streamSettings = {
|
||||
network = server.transport,
|
||||
security = (server.security == 'tls') and "tls" or "none",
|
||||
tlsSettings = (server.security == 'tls') and "tls" and tls_settings(server) or nil,
|
||||
tcpSettings = stream_tcp(server) and server.protocol ~= "trojan" or nil,
|
||||
wsSettings = stream_ws(server),
|
||||
grpcSettings = stream_grpc(server),
|
||||
httpSettings = stream_h2(server),
|
||||
quicSettings = stream_quic(server),
|
||||
kcpSettings = stream_kcp(server),
|
||||
},
|
||||
mux = (server.mux == "1" and server.security == 'tls' or server.security == 'none') and {
|
||||
enabled = true,
|
||||
concurrency = tonumber(server.concurrency)
|
||||
} or nil
|
||||
}
|
||||
end
|
||||
|
||||
local function server_outbound(server, tag)
|
||||
if server.protocol == "vmess" then
|
||||
return vmess_outbound(server, tag)
|
||||
end
|
||||
if server.protocol == "vless" then
|
||||
return vless_outbound(server, tag)
|
||||
end
|
||||
if server.protocol == "shadowsocks" then
|
||||
return shadowsocks_outbound(server, tag)
|
||||
end
|
||||
if server.protocol == "trojan" then
|
||||
return trojan_outbound(server, tag)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function proxy_inbound()
|
||||
if local_port == "0" then
|
||||
return nil
|
||||
end
|
||||
|
||||
return {
|
||||
port = local_port,
|
||||
protocol = "dokodemo-door",
|
||||
tag = "proxy_inbound",
|
||||
sniffing = {
|
||||
enabled = true,
|
||||
destOverride = {"http", "tls"}
|
||||
} ,
|
||||
settings = {
|
||||
network = proto,
|
||||
followRedirect = true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function socks_inbound()
|
||||
if socks_port == "0" then
|
||||
return nil
|
||||
end
|
||||
return {
|
||||
port = socks_port,
|
||||
protocol = "socks",
|
||||
tag = "socks_inbound",
|
||||
settings = {
|
||||
auth = "noauth",
|
||||
udp = true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local function dns_inbounds()
|
||||
local servers = {}
|
||||
if proxy["dns_servers"] ~= nil then
|
||||
for _, x in ipairs(proxy["dns_servers"]) do
|
||||
table.insert(servers, x)
|
||||
end
|
||||
end
|
||||
local i = 0
|
||||
ucursor:foreach("xclient", "dns", function(v)
|
||||
i = i + 1
|
||||
table.insert(servers, {
|
||||
address = v.address or nil,
|
||||
port = v.port,
|
||||
domains = v.domains or nil,
|
||||
expectIPs = v.expectIPs or nil,
|
||||
skipFallback = v.skipFallback == "1" and true or false
|
||||
})
|
||||
end)
|
||||
return servers
|
||||
end
|
||||
|
||||
local function dns()
|
||||
local result = {}
|
||||
for _, v in ipairs(dns_inbounds()) do
|
||||
table.insert(result, v)
|
||||
end
|
||||
return {
|
||||
server = result,
|
||||
clientIp = proxy.clientIp or nil,
|
||||
queryStrategy = proxy.queryStrategy,
|
||||
disableCache = proxy.disableCache == "1" and true or false,
|
||||
disableFallback = proxy.disableFallback == "1" and true or false,
|
||||
disableFallbackIfMatch = proxy.disableFallbackIfMatch == "1" and true or false,
|
||||
tag = "dns_inbound"
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function inbounds()
|
||||
local i = {}
|
||||
if proxy.global_server ~= "nil" or proxy.udp_server ~= "nil" then
|
||||
i = {
|
||||
proxy_inbound(),
|
||||
}
|
||||
if proxy.socks5_server == "same" or _local == "1" then
|
||||
table.insert(i, socks_inbound())
|
||||
end
|
||||
table.insert(i, {
|
||||
listen = "127.0.0.1",
|
||||
port = 8888,
|
||||
protocol = "dokodemo-door",
|
||||
settings = {
|
||||
address = "127.0.0.1"
|
||||
},
|
||||
tag = "api"
|
||||
})
|
||||
return i
|
||||
end
|
||||
end
|
||||
|
||||
local function api()
|
||||
return {
|
||||
tag = "api",
|
||||
services = {
|
||||
"StatsService"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function routing()
|
||||
local rule = {}
|
||||
local i = 0
|
||||
ucursor:foreach("xclient", "rule", function(v)
|
||||
i = i + 1
|
||||
table.insert(rule, {
|
||||
type = "field",
|
||||
domainMatcher = v.domainMatcher or nil,
|
||||
domain = v.domain or nil,
|
||||
ip = v.ip or nil,
|
||||
port = v.port or nil,
|
||||
sourcePort = v.sourcePort or nil,
|
||||
network = v.network or nil,
|
||||
source = v.source or nil,
|
||||
inboundTag = v.inboundTag or nil,
|
||||
protocol = v.protocol or nil,
|
||||
outboundTag = v.outboundTag or nil,
|
||||
})
|
||||
end)
|
||||
table.insert(rule, {
|
||||
type = "field",
|
||||
inboundTag = {
|
||||
"api"
|
||||
},
|
||||
outboundTag = "api",
|
||||
enabled = true
|
||||
})
|
||||
return rule
|
||||
end
|
||||
|
||||
|
||||
local function routing_rules()
|
||||
local result = {}
|
||||
for _, v in ipairs(routing()) do
|
||||
table.insert(result, v)
|
||||
end
|
||||
return {
|
||||
domainMatcher = proxy.domainMatcher,
|
||||
domainStrategy = proxy.routing_strategy or 'AsIs',
|
||||
rules = result
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function outbounds()
|
||||
local result = {}
|
||||
if arg[7] == "none" then
|
||||
result = {
|
||||
server_outbound(socks5_server, "proxy_outbound"),
|
||||
dns_outbound(),
|
||||
direct_outbound(),
|
||||
block_outbound()
|
||||
}
|
||||
elseif proto == "tcp" then
|
||||
result = {
|
||||
server_outbound(tcp_server, "proxy_outbound"),
|
||||
dns_outbound(),
|
||||
direct_outbound(),
|
||||
block_outbound()
|
||||
}
|
||||
elseif proto == "udp" then
|
||||
result = {
|
||||
server_outbound(udp_server, "proxy_outbound"),
|
||||
dns_outbound(),
|
||||
direct_outbound(),
|
||||
block_outbound()
|
||||
}
|
||||
elseif proto == "tcp,udp" then
|
||||
result = {
|
||||
server_outbound(tcp_server, "proxy_outbound"),
|
||||
dns_outbound(),
|
||||
direct_outbound(),
|
||||
block_outbound()
|
||||
}
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
|
||||
local function logging()
|
||||
return {
|
||||
loglevel = proxy.log_level or "warning",
|
||||
}
|
||||
end
|
||||
|
||||
local function policy()
|
||||
return {
|
||||
system = {
|
||||
statsOutboundUplink = true,
|
||||
statsOutboundDownlink = true
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
local xclient = {
|
||||
log = logging(),
|
||||
dns = dns(),
|
||||
routing = routing_rules(),
|
||||
dns = dns(),
|
||||
api = api(),
|
||||
policy = policy(),
|
||||
routing = routing_rules(),
|
||||
inbounds = inbounds(),
|
||||
outbounds = outbounds()
|
||||
}
|
||||
|
||||
|
||||
local shadowsocks_plugin = {
|
||||
server = node.server,
|
||||
server_port = tonumber(node.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 = node.password,
|
||||
method = node.encrypt_method_v2ray_ss,
|
||||
reuse_port = true,
|
||||
protocol = _protocol,
|
||||
plugin = node.plugin,
|
||||
plugin_opts = node.plugin_opts or nil
|
||||
}
|
||||
|
||||
if node.protocol == "shadowsocks-plugin" then
|
||||
print(json.stringify(shadowsocks_plugin, true))
|
||||
else
|
||||
print(json.stringify(xclient, true))
|
||||
end
|
||||
Reference in new issue
Block a user