1044 lines
30 KiB
Lua
1044 lines
30 KiB
Lua
#!/usr/bin/lua
|
|
local ucursor = require "luci.model.uci"
|
|
local json = require "luci.jsonc"
|
|
local nixiofs = require "nixio.fs"
|
|
|
|
local proxy_section = ucursor:get_first("xray", "general")
|
|
local proxy = ucursor:get_all("xray", proxy_section)
|
|
|
|
local tcp_server_section = arg[1] == nil and proxy.main_server or arg[1]
|
|
local tcp_server = ucursor:get_all("xray", tcp_server_section)
|
|
|
|
local udp_server_section = arg[2] == nil and proxy.tproxy_udp_server or arg[2]
|
|
local udp_server = ucursor:get_all("xray", udp_server_section)
|
|
|
|
local geoip_existence = false
|
|
local geosite_existence = false
|
|
|
|
local xray_data_file_iterator = nixiofs.dir("/usr/share/xray")
|
|
|
|
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 split_ipv4_host_port(val, port_default)
|
|
local found, _, ip, port = val:find("([%d.]+):(%d+)")
|
|
if found == nil then
|
|
return val, tonumber(port_default)
|
|
else
|
|
return ip, tonumber(port)
|
|
end
|
|
end
|
|
|
|
local function direct_outbound()
|
|
return {
|
|
protocol = "freedom",
|
|
tag = "direct",
|
|
settings = {
|
|
domainStrategy = "UseIPv4"
|
|
},
|
|
streamSettings = {
|
|
sockopt = {
|
|
mark = tonumber(proxy.mark)
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
local function blackhole_outbound()
|
|
return {
|
|
tag = "blackhole_outbound",
|
|
protocol = "blackhole"
|
|
}
|
|
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
|
|
}
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
local function stream_grpc(server)
|
|
if (server.transport == "grpc") then
|
|
return {
|
|
serviceName = server.grpc_service_name,
|
|
multiMode = server.grpc_multi_mode == "1",
|
|
initial_windows_size = tonumber(server.grpc_initial_windows_size or 0),
|
|
idle_timeout = server.grpc_health_check == "1" and tonumber(server.grpc_idle_timeout or 10) or nil,
|
|
health_check_timeout = server.grpc_health_check == "1" and tonumber(server.grpc_health_check_timeout or 20) or nil,
|
|
permit_without_stream = server.grpc_health_check == "1" and (server.grpc_permit_without_stream == "1") or nil
|
|
}
|
|
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
|
|
}
|
|
end
|
|
return {
|
|
path = server.ws_path,
|
|
headers = headers
|
|
}
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
local function stream_kcp(server)
|
|
if server.transport == "mkcp" then
|
|
local mkcp_seed = nil
|
|
if server.mkcp_seed ~= "" then
|
|
mkcp_seed = server.mkcp_seed
|
|
end
|
|
return {
|
|
mtu = tonumber(server.mkcp_mtu),
|
|
tti = tonumber(server.mkcp_tti),
|
|
uplinkCapacity = tonumber(server.mkcp_uplink_capacity),
|
|
downlinkCapacity = tonumber(server.mkcp_downlink_capacity),
|
|
congestion = server.mkcp_congestion == "1",
|
|
readBufferSize = tonumber(server.mkcp_read_buffer_size),
|
|
writeBufferSize = tonumber(server.mkcp_write_buffer_size),
|
|
seed = mkcp_seed,
|
|
header = {
|
|
type = server.mkcp_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 tls_settings(server, protocol)
|
|
local result = {
|
|
serverName = server[protocol .. "_tls_host"],
|
|
allowInsecure = server[protocol .. "_tls_insecure"] ~= "0",
|
|
fingerprint = server[protocol .. "_tls_fingerprint"] or "",
|
|
}
|
|
|
|
if server[protocol .. "_tls_alpn"] ~= nil then
|
|
local alpn = {}
|
|
for _, x in ipairs(server[protocol .. "_tls_alpn"]) do
|
|
table.insert(alpn, x)
|
|
end
|
|
result["alpn"] = alpn
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
local function xtls_settings(server, protocol)
|
|
local result = {
|
|
serverName = server[protocol .. "_xtls_host"],
|
|
allowInsecure = server[protocol .. "_xtls_insecure"] ~= "0",
|
|
}
|
|
|
|
if server[protocol .. "_xtls_alpn"] ~= nil then
|
|
local alpn = {}
|
|
for _, x in ipairs(server[protocol .. "_xtls_alpn"]) do
|
|
table.insert(alpn, x)
|
|
end
|
|
result["alpn"] = alpn
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
local function stream_settings(server, protocol, xtls)
|
|
local security = server[protocol .. "_tls"]
|
|
local tlsSettings = nil
|
|
local xtlsSettings = nil
|
|
if security == "tls" then
|
|
tlsSettings = tls_settings(server, protocol)
|
|
elseif security == "xtls" and xtls then
|
|
xtlsSettings = xtls_settings(server, protocol)
|
|
end
|
|
return {
|
|
network = server.transport,
|
|
sockopt = {
|
|
mark = tonumber(proxy.mark),
|
|
domainStrategy = server.domain_strategy or "UseIP"
|
|
},
|
|
security = security,
|
|
tlsSettings = tlsSettings,
|
|
xtlsSettings = xtlsSettings,
|
|
quicSettings = stream_quic(server),
|
|
tcpSettings = stream_tcp(server),
|
|
kcpSettings = stream_kcp(server),
|
|
wsSettings = stream_ws(server),
|
|
grpcSettings = stream_grpc(server),
|
|
httpSettings = stream_h2(server)
|
|
}
|
|
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.shadowsocks_security,
|
|
uot = server.shadowsocks_udp_over_tcp == '1'
|
|
}
|
|
}
|
|
},
|
|
streamSettings = stream_settings(server, "shadowsocks", false)
|
|
}
|
|
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.password,
|
|
alterId = tonumber(server.alter_id),
|
|
security = server.vmess_security
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
streamSettings = stream_settings(server, "vmess", false)
|
|
}
|
|
end
|
|
|
|
local function vless_outbound(server, tag)
|
|
local flow = server.vless_flow
|
|
if server.vless_flow == "none" then
|
|
flow = nil
|
|
end
|
|
return {
|
|
protocol = "vless",
|
|
tag = tag,
|
|
settings = {
|
|
vnext = {
|
|
{
|
|
address = server.server,
|
|
port = tonumber(server.server_port),
|
|
users = {
|
|
{
|
|
id = server.password,
|
|
flow = flow,
|
|
encryption = server.vless_encryption
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
streamSettings = stream_settings(server, "vless", true)
|
|
}
|
|
end
|
|
|
|
local function trojan_outbound(server, tag)
|
|
local flow = server.trojan_flow
|
|
if server.trojan_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 = stream_settings(server, "trojan", true)
|
|
}
|
|
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
|
|
error("unknown outbound server protocol")
|
|
end
|
|
|
|
local function tproxy_tcp_inbound()
|
|
return {
|
|
port = proxy.tproxy_port_tcp,
|
|
protocol = "dokodemo-door",
|
|
tag = "tproxy_tcp_inbound",
|
|
sniffing = proxy.tproxy_sniffing == "1" and {
|
|
enabled = true,
|
|
routeOnly = proxy.route_only == "1",
|
|
destOverride = {"http", "tls"},
|
|
metadataOnly = false
|
|
} or nil,
|
|
settings = {
|
|
network = "tcp",
|
|
followRedirect = true
|
|
},
|
|
streamSettings = {
|
|
sockopt = {
|
|
tproxy = "tproxy",
|
|
mark = tonumber(proxy.mark)
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
local function tproxy_udp_inbound()
|
|
return {
|
|
port = proxy.tproxy_port_udp,
|
|
protocol = "dokodemo-door",
|
|
tag = "tproxy_udp_inbound",
|
|
settings = {
|
|
network = "udp",
|
|
followRedirect = true
|
|
},
|
|
streamSettings = {
|
|
sockopt = {
|
|
tproxy = "tproxy",
|
|
mark = tonumber(proxy.mark)
|
|
}
|
|
}
|
|
}
|
|
end
|
|
|
|
local function http_inbound()
|
|
return {
|
|
port = proxy.http_port,
|
|
protocol = "http",
|
|
tag = "http_inbound",
|
|
settings = {
|
|
allowTransparent = false
|
|
}
|
|
}
|
|
end
|
|
|
|
local function socks_inbound()
|
|
return {
|
|
port = proxy.socks_port,
|
|
protocol = "socks",
|
|
tag = "socks_inbound",
|
|
settings = {
|
|
udp = true
|
|
}
|
|
}
|
|
end
|
|
|
|
local function fallbacks()
|
|
local f = {}
|
|
ucursor:foreach("xray", "fallback", function(s)
|
|
if s.dest ~= nil then
|
|
table.insert(f, {
|
|
dest = s.dest,
|
|
alpn = s.alpn,
|
|
name = s.name,
|
|
xver = s.xver,
|
|
path = s.path
|
|
})
|
|
end
|
|
end)
|
|
table.insert(f, {
|
|
dest = proxy.web_server_address
|
|
})
|
|
return f
|
|
end
|
|
|
|
local function https_trojan_inbound()
|
|
return {
|
|
port = 443,
|
|
protocol = "trojan",
|
|
tag = "https_inbound",
|
|
settings = {
|
|
clients = {
|
|
{
|
|
password = proxy.web_server_password,
|
|
flow = proxy.trojan_tls == "xtls" and proxy.trojan_flow or nil
|
|
}
|
|
},
|
|
fallbacks = fallbacks()
|
|
},
|
|
streamSettings = {
|
|
network = "tcp",
|
|
security = proxy.trojan_tls,
|
|
tlsSettings = proxy.trojan_tls == "tls" and {
|
|
alpn = {
|
|
"http/1.1"
|
|
},
|
|
certificates = {
|
|
{
|
|
certificateFile = proxy.web_server_cert_file,
|
|
keyFile = proxy.web_server_key_file
|
|
}
|
|
}
|
|
} or nil,
|
|
xtlsSettings = proxy.trojan_tls == "xtls" and {
|
|
alpn = {
|
|
"http/1.1"
|
|
},
|
|
certificates = {
|
|
{
|
|
certificateFile = proxy.web_server_cert_file,
|
|
keyFile = proxy.web_server_key_file
|
|
}
|
|
}
|
|
} or nil
|
|
}
|
|
}
|
|
end
|
|
|
|
local function https_vless_inbound()
|
|
return {
|
|
port = 443,
|
|
protocol = "vless",
|
|
tag = "https_inbound",
|
|
settings = {
|
|
clients = {
|
|
{
|
|
id = proxy.web_server_password,
|
|
flow = proxy.vless_tls == "xtls" and proxy.vless_flow or nil
|
|
}
|
|
},
|
|
decryption = "none",
|
|
fallbacks = fallbacks()
|
|
},
|
|
streamSettings = {
|
|
network = "tcp",
|
|
security = proxy.vless_tls,
|
|
tlsSettings = proxy.vless_tls == "tls" and {
|
|
alpn = {
|
|
"http/1.1"
|
|
},
|
|
certificates = {
|
|
{
|
|
certificateFile = proxy.web_server_cert_file,
|
|
keyFile = proxy.web_server_key_file
|
|
}
|
|
}
|
|
} or nil,
|
|
xtlsSettings = proxy.vless_tls == "xtls" and {
|
|
alpn = {
|
|
"http/1.1"
|
|
},
|
|
certificates = {
|
|
{
|
|
certificateFile = proxy.web_server_cert_file,
|
|
keyFile = proxy.web_server_key_file
|
|
}
|
|
}
|
|
} or nil
|
|
}
|
|
}
|
|
end
|
|
|
|
local function https_inbound()
|
|
if proxy.web_server_protocol == "vless" then
|
|
return https_vless_inbound()
|
|
end
|
|
if proxy.web_server_protocol == "trojan" then
|
|
return https_trojan_inbound()
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function dns_server_inbounds()
|
|
local result = {}
|
|
for i = proxy.dns_port, proxy.dns_port + (proxy.dns_count or 0), 1 do
|
|
local default_dns_ip, default_dns_port = split_ipv4_host_port(proxy.default_dns, 53)
|
|
table.insert(result, {
|
|
port = i,
|
|
protocol = "dokodemo-door",
|
|
tag = string.format("dns_server_inbound_%d", i),
|
|
settings = {
|
|
address = default_dns_ip,
|
|
port = default_dns_port,
|
|
network = "tcp,udp"
|
|
}
|
|
})
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function dns_server_tags()
|
|
local result = {}
|
|
for i = proxy.dns_port, proxy.dns_port + (proxy.dns_count or 0), 1 do
|
|
table.insert(result, string.format("dns_server_inbound_%d", i))
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function dns_server_outbound()
|
|
return {
|
|
protocol = "dns",
|
|
streamSettings = {
|
|
sockopt = {
|
|
mark = tonumber(proxy.mark)
|
|
}
|
|
},
|
|
tag = "dns_server_outbound"
|
|
}
|
|
end
|
|
|
|
local function upstream_domain_names()
|
|
local result = {
|
|
tcp_server.server,
|
|
}
|
|
if tcp_server.server ~= udp_server.server then
|
|
table.insert(result, udp_server.server)
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function domain_rules(k)
|
|
if proxy[k] == nil then
|
|
return nil
|
|
end
|
|
|
|
local result = {}
|
|
for _, x in ipairs(proxy[k]) do
|
|
if x:sub(1, 8) == "geosite:" then
|
|
if geosite_existence then
|
|
table.insert(result, x)
|
|
end
|
|
else
|
|
table.insert(result, x)
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function secure_domain_rules()
|
|
return domain_rules("forwarded_domain_rules")
|
|
end
|
|
|
|
local function fast_domain_rules()
|
|
return domain_rules("bypassed_domain_rules")
|
|
end
|
|
|
|
local function blocked_domain_rules()
|
|
return domain_rules("blocked_domain_rules")
|
|
end
|
|
|
|
local function dns_conf()
|
|
local fast_dns_ip, fast_dns_port = split_ipv4_host_port(proxy.fast_dns, 53)
|
|
local default_dns_ip, default_dns_port = split_ipv4_host_port(proxy.default_dns, 53)
|
|
local hosts = nil
|
|
local servers = {
|
|
{
|
|
address = fast_dns_ip,
|
|
port = fast_dns_port,
|
|
domains = upstream_domain_names(),
|
|
},
|
|
{
|
|
address = default_dns_ip,
|
|
port = default_dns_port,
|
|
}
|
|
}
|
|
|
|
if fast_domain_rules() ~= nil then
|
|
table.insert(servers, 2, {
|
|
address = fast_dns_ip,
|
|
port = fast_dns_port,
|
|
domains = fast_domain_rules(),
|
|
})
|
|
end
|
|
|
|
if secure_domain_rules() ~= nil then
|
|
local secure_dns_ip, secure_dns_port = split_ipv4_host_port(proxy.secure_dns, 53)
|
|
table.insert(servers, 2, {
|
|
address = secure_dns_ip,
|
|
port = secure_dns_port,
|
|
domains = secure_domain_rules(),
|
|
})
|
|
end
|
|
|
|
if blocked_domain_rules() ~= nil then
|
|
hosts = {}
|
|
for _, rule in ipairs(blocked_domain_rules()) do
|
|
hosts[rule] = {"127.127.127.127", "100::6c62:636f:656b:2164"} -- blocked!
|
|
end
|
|
end
|
|
|
|
return {
|
|
hosts = hosts,
|
|
servers = servers,
|
|
tag = "dns_conf_inbound",
|
|
queryStrategy = "UseIPv4"
|
|
}
|
|
end
|
|
|
|
local function api_conf()
|
|
if proxy.xray_api == '1' then
|
|
return {
|
|
tag = "api",
|
|
services = {
|
|
"HandlerService",
|
|
"LoggerService",
|
|
"StatsService"
|
|
}
|
|
}
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
local function metrics_conf()
|
|
if proxy.metrics_server_enable == "1" then
|
|
return {
|
|
tag = "metrics",
|
|
}
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function inbounds()
|
|
local i = {
|
|
http_inbound(),
|
|
tproxy_tcp_inbound(),
|
|
tproxy_udp_inbound(),
|
|
socks_inbound(),
|
|
}
|
|
for _, v in ipairs(dns_server_inbounds()) do
|
|
table.insert(i, v)
|
|
end
|
|
if proxy.web_server_enable == "1" then
|
|
table.insert(i, https_inbound())
|
|
end
|
|
if proxy.metrics_server_enable == '1' then
|
|
table.insert(i, {
|
|
listen = "0.0.0.0",
|
|
port = proxy.metrics_server_port or 18888,
|
|
protocol = "dokodemo-door",
|
|
settings = {
|
|
address = "127.0.0.1"
|
|
},
|
|
tag = "metrics"
|
|
})
|
|
end
|
|
if proxy.xray_api == '1' then
|
|
table.insert(i, {
|
|
listen = "127.0.0.1",
|
|
port = 8080,
|
|
protocol = "dokodemo-door",
|
|
settings = {
|
|
address = "127.0.0.1"
|
|
},
|
|
tag = "api"
|
|
})
|
|
end
|
|
return i
|
|
end
|
|
|
|
local function manual_tproxy_outbounds()
|
|
local result = {}
|
|
local i = 0
|
|
ucursor:foreach("xray", "manual_tproxy", function(v)
|
|
i = i + 1
|
|
local tcp_tag = "direct"
|
|
local udp_tag = "direct"
|
|
if v.force_forward == "1" then
|
|
if v.force_forward_server_tcp ~= nil then
|
|
if v.force_forward_server_tcp == proxy.main_server then
|
|
tcp_tag = "tcp_outbound"
|
|
else
|
|
tcp_tag = string.format("manual_tproxy_force_forward_tcp_outbound_%d", i)
|
|
local force_forward_server_tcp = ucursor:get_all("xray", v.force_forward_server_tcp)
|
|
table.insert(result, server_outbound(force_forward_server_tcp, tcp_tag))
|
|
end
|
|
else
|
|
tcp_tag = "tcp_outbound"
|
|
end
|
|
if v.force_forward_server_udp ~= nil then
|
|
if v.force_forward_server_udp == proxy.tproxy_udp_server then
|
|
udp_tag = "udp_outbound"
|
|
else
|
|
udp_tag = string.format("manual_tproxy_force_forward_udp_outbound_%d", i)
|
|
local force_forward_server_udp = ucursor:get_all("xray", v.force_forward_server_udp)
|
|
table.insert(result, server_outbound(force_forward_server_udp, udp_tag))
|
|
end
|
|
else
|
|
udp_tag = "udp_outbound"
|
|
end
|
|
end
|
|
|
|
table.insert(result, {
|
|
protocol = "freedom",
|
|
tag = string.format("manual_tproxy_outbound_tcp_%d", i),
|
|
settings = {
|
|
redirect = string.format("%s:%d", v.dest_addr, v.dest_port),
|
|
domainStrategy = v.domain_strategy or "UseIP"
|
|
},
|
|
proxySettings = {
|
|
tag = tcp_tag
|
|
}
|
|
})
|
|
table.insert(result, {
|
|
protocol = "freedom",
|
|
tag = string.format("manual_tproxy_outbound_udp_%d", i),
|
|
settings = {
|
|
redirect = string.format("%s:%d", v.dest_addr, v.dest_port),
|
|
domainStrategy = v.domain_strategy or "UseIP"
|
|
},
|
|
proxySettings = {
|
|
tag = udp_tag
|
|
}
|
|
})
|
|
end)
|
|
return result
|
|
end
|
|
|
|
local function manual_tproxy_rules()
|
|
local result = {}
|
|
local i = 0
|
|
ucursor:foreach("xray", "manual_tproxy", function(v)
|
|
i = i + 1
|
|
table.insert(result, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_tcp_inbound", "socks_inbound", "https_inbound", "http_inbound"},
|
|
ip = {v.source_addr},
|
|
port = v.source_port,
|
|
outboundTag = string.format("manual_tproxy_outbound_tcp_%d", i)
|
|
})
|
|
table.insert(result, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_udp_inbound"},
|
|
ip = {v.source_addr},
|
|
port = v.source_port,
|
|
outboundTag = string.format("manual_tproxy_outbound_udp_%d", i)
|
|
})
|
|
end)
|
|
return result
|
|
end
|
|
|
|
local function bridges()
|
|
local result = {}
|
|
local i = 0
|
|
ucursor:foreach("xray", "bridge", function(v)
|
|
i = i + 1
|
|
table.insert(result, {
|
|
tag = string.format("bridge_inbound_%d", i),
|
|
domain = v.domain
|
|
})
|
|
end)
|
|
return result
|
|
end
|
|
|
|
local function bridge_outbounds()
|
|
local result = {}
|
|
local i = 0
|
|
ucursor:foreach("xray", "bridge", function(v)
|
|
i = i + 1
|
|
local bridge_server = ucursor:get_all("xray", v.upstream)
|
|
table.insert(result, 1, server_outbound(bridge_server, string.format("bridge_upstream_outbound_%d", i)))
|
|
table.insert(result, 1, {
|
|
tag = string.format("bridge_freedom_outbound_%d", i),
|
|
protocol = "freedom",
|
|
settings = {
|
|
redirect = v.redirect
|
|
}
|
|
})
|
|
end)
|
|
return result
|
|
end
|
|
|
|
local function bridge_rules()
|
|
local result = {}
|
|
local i = 0
|
|
ucursor:foreach("xray", "bridge", function(v)
|
|
i = i + 1
|
|
table.insert(result, {
|
|
type = "field",
|
|
inboundTag = {string.format("bridge_inbound_%d", i)},
|
|
outboundTag = string.format("bridge_freedom_outbound_%d", i)
|
|
})
|
|
table.insert(result, {
|
|
type = "field",
|
|
inboundTag = {string.format("bridge_inbound_%d", i)},
|
|
domain = {string.format("full:%s", v.domain)},
|
|
outboundTag = string.format("bridge_upstream_outbound_%d", i)
|
|
})
|
|
end)
|
|
return result
|
|
end
|
|
|
|
local function rules()
|
|
local result = {
|
|
{
|
|
type = "field",
|
|
inboundTag = {"tproxy_tcp_inbound", "dns_conf_inbound", "socks_inbound", "https_inbound", "http_inbound"},
|
|
outboundTag = "tcp_outbound"
|
|
},
|
|
{
|
|
type = "field",
|
|
inboundTag = {"tproxy_udp_inbound"},
|
|
outboundTag = "udp_outbound"
|
|
},
|
|
{
|
|
type = "field",
|
|
inboundTag = dns_server_tags(),
|
|
outboundTag = "dns_server_outbound"
|
|
},
|
|
{
|
|
type = "field",
|
|
inboundTag = {"api"},
|
|
outboundTag = "api"
|
|
}
|
|
}
|
|
if proxy.metrics_server_enable == "1" then
|
|
table.insert(result, 1, {
|
|
type = "field",
|
|
inboundTag = {"metrics"},
|
|
outboundTag = "metrics"
|
|
})
|
|
end
|
|
if geoip_existence then
|
|
if proxy.geoip_direct_code == nil or proxy.geoip_direct_code == "upgrade" then
|
|
if proxy.geoip_direct_code_list ~= nil then
|
|
local geoip_direct_code_list = {}
|
|
for _, v in ipairs(proxy.geoip_direct_code_list) do
|
|
table.insert(geoip_direct_code_list, "geoip:" .. v)
|
|
end
|
|
table.insert(result, 1, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_tcp_inbound", "tproxy_udp_inbound", "dns_conf_inbound"},
|
|
outboundTag = "direct",
|
|
ip = geoip_direct_code_list
|
|
})
|
|
end
|
|
else
|
|
table.insert(result, 1, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_tcp_inbound", "tproxy_udp_inbound", "dns_conf_inbound"},
|
|
outboundTag = "direct",
|
|
ip = {"geoip:" .. proxy.geoip_direct_code}
|
|
})
|
|
end
|
|
table.insert(result, 1, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_tcp_inbound", "tproxy_udp_inbound", "dns_conf_inbound", "socks_inbound", "https_inbound", "http_inbound"},
|
|
outboundTag = "direct",
|
|
ip = {"geoip:private"}
|
|
})
|
|
end
|
|
if proxy.tproxy_sniffing == "1" then
|
|
if secure_domain_rules() ~= nil then
|
|
table.insert(result, 1, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_udp_inbound"},
|
|
outboundTag = "udp_outbound",
|
|
domain = secure_domain_rules(),
|
|
})
|
|
table.insert(result, 1, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_tcp_inbound", "dns_conf_inbound"},
|
|
outboundTag = "tcp_outbound",
|
|
domain = secure_domain_rules(),
|
|
})
|
|
end
|
|
if blocked_domain_rules() ~= nil then
|
|
table.insert(result, 1, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_tcp_inbound", "tproxy_udp_inbound", "dns_conf_inbound"},
|
|
outboundTag = "blackhole_outbound",
|
|
domain = blocked_domain_rules(),
|
|
})
|
|
end
|
|
table.insert(result, 1, {
|
|
type = "field",
|
|
inboundTag = {"tproxy_tcp_inbound", "tproxy_udp_inbound", "dns_conf_inbound", "https_inbound", "http_inbound"},
|
|
outboundTag = "direct",
|
|
domain = fast_domain_rules()
|
|
})
|
|
end
|
|
for _, v in ipairs(manual_tproxy_rules()) do
|
|
table.insert(result, 1, v)
|
|
end
|
|
for _, v in ipairs(bridge_rules()) do
|
|
table.insert(result, 1, v)
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function outbounds()
|
|
local result = {
|
|
server_outbound(tcp_server, "tcp_outbound"),
|
|
server_outbound(udp_server, "udp_outbound"),
|
|
direct_outbound(),
|
|
dns_server_outbound(),
|
|
blackhole_outbound()
|
|
}
|
|
for _, v in ipairs(manual_tproxy_outbounds()) do
|
|
table.insert(result, v)
|
|
end
|
|
for _, v in ipairs(bridge_outbounds()) do
|
|
table.insert(result, v)
|
|
end
|
|
return result
|
|
end
|
|
|
|
local function policy()
|
|
local stats = proxy.stats == "1"
|
|
return {
|
|
levels = {
|
|
["0"] = {
|
|
handshake = proxy.handshake == nil and 4 or tonumber(proxy.handshake),
|
|
connIdle = proxy.conn_idle == nil and 300 or tonumber(proxy.conn_idle),
|
|
uplinkOnly = proxy.uplink_only == nil and 2 or tonumber(proxy.uplink_only),
|
|
downlinkOnly = proxy.downlink_only == nil and 5 or tonumber(proxy.downlink_only),
|
|
bufferSize = proxy.buffer_size == nil and 4 or tonumber(proxy.buffer_size),
|
|
statsUserUplink = stats,
|
|
statsUserDownlink = stats,
|
|
}
|
|
},
|
|
system = {
|
|
statsInboundUplink = stats,
|
|
statsInboundDownlink = stats,
|
|
statsOutboundUplink = stats,
|
|
statsOutboundDownlink = stats
|
|
}
|
|
}
|
|
end
|
|
|
|
local function logging()
|
|
return {
|
|
access = proxy.access_log == "1" and "" or "none",
|
|
loglevel = proxy.loglevel or "warning",
|
|
dnsLog = proxy.dns_log == "1"
|
|
}
|
|
end
|
|
|
|
local function observatory()
|
|
if proxy.observatory == "1" then
|
|
return {
|
|
subjectSelector = {"tcp_outbound", "udp_outbound", "direct"},
|
|
probeInterval = "1s",
|
|
probeUrl = "http://www.apple.com/library/test/success.html"
|
|
}
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local xray = {
|
|
inbounds = inbounds(),
|
|
outbounds = outbounds(),
|
|
dns = dns_conf(),
|
|
api = api_conf(),
|
|
metrics = metrics_conf(),
|
|
policy = policy(),
|
|
log = logging(),
|
|
stats = proxy.stats == "1" and {
|
|
place = "holder"
|
|
} or nil,
|
|
observatory = observatory(),
|
|
reverse = {
|
|
bridges = bridges()
|
|
},
|
|
routing = {
|
|
domainStrategy = proxy.routing_domain_strategy or "AsIs",
|
|
rules = rules()
|
|
}
|
|
}
|
|
|
|
print(json.stringify(xray, true))
|