small-package/luci-app-passwall2/luasrc/passwall2/util_xray.lua

1824 lines
53 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module("luci.passwall2.util_xray", package.seeall)
local api = require "luci.passwall2.api"
local uci = api.uci
local sys = api.sys
local jsonc = api.jsonc
local appname = api.appname
local fs = api.fs
local CACHE_PATH = api.CACHE_PATH
local new_port
local function get_new_port()
if new_port then
new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port %s tcp)", appname, new_port + 1)))
else
new_port = tonumber(sys.exec(string.format("echo -n $(/usr/share/%s/app.sh get_new_port auto tcp)", appname)))
end
return new_port
end
local function get_domain_excluded()
local path = string.format("/usr/share/%s/domains_excluded", appname)
local content = fs.readfile(path)
if not content then return nil end
local hosts = {}
string.gsub(content, '[^' .. "\n" .. ']+', function(w)
local s = w:gsub("^%s*(.-)%s*$", "%1") -- Trim
if s == "" then return end
if s:find("#") and s:find("#") == 1 then return end
if not s:find("#") or s:find("#") ~= 1 then table.insert(hosts, s) end
end)
if #hosts == 0 then hosts = nil end
return hosts
end
function gen_outbound(flag, node, tag, proxy_table)
local result = nil
if node and node ~= "nil" then
local node_id = node[".name"]
if tag == nil then
tag = node_id
end
local proxy = 0
local proxy_tag = "nil"
local fragment = nil
if proxy_table ~= nil and type(proxy_table) == "table" then
proxy = proxy_table.proxy or 0
proxy_tag = proxy_table.tag or "nil"
fragment = proxy_table.fragment or nil
end
if node.type == "Xray" then
if node.flow == "xtls-rprx-vision" then
else
proxy = 0
if proxy_tag ~= "nil" then
node.proxySettings = {
tag = proxy_tag,
transportLayer = true
}
end
end
end
if node.type ~= "Xray" then
local relay_port = node.port
new_port = get_new_port()
local config_file = string.format("%s_%s_%s.json", flag, tag, new_port)
if tag and node_id and tag ~= node_id then
config_file = string.format("%s_%s_%s_%s.json", flag, tag, node_id, new_port)
end
sys.call(string.format('/usr/share/%s/app.sh run_socks "%s"> /dev/null',
appname,
string.format("flag=%s node=%s bind=%s socks_port=%s config_file=%s relay_port=%s",
new_port, --flag
node_id, --node
"127.0.0.1", --bind
new_port, --socks port
config_file, --config file
(proxy == 1 and relay_port) and tostring(relay_port) or "" --relay port
)
)
)
node = {}
node.protocol = "socks"
node.transport = "tcp"
node.address = "127.0.0.1"
node.port = new_port
node.stream_security = "none"
end
if node.type == "Xray" then
if node.tls and node.tls == "1" then
node.stream_security = "tls"
if node.reality and node.reality == "1" then
node.stream_security = "reality"
end
end
end
if node.protocol == "wireguard" and node.wireguard_reserved then
local bytes = {}
if not node.wireguard_reserved:match("[^%d,]+") then
node.wireguard_reserved:gsub("%d+", function(b)
bytes[#bytes + 1] = tonumber(b)
end)
else
local result = api.bin.b64decode(node.wireguard_reserved)
for i = 1, #result do
bytes[i] = result:byte(i)
end
end
node.wireguard_reserved = #bytes > 0 and bytes or nil
end
result = {
_flag_tag = node_id,
_flag_proxy = proxy,
_flag_proxy_tag = proxy_tag,
tag = tag,
proxySettings = node.proxySettings or nil,
protocol = node.protocol,
mux = {
enabled = (node.mux == "1" or node.xmux == "1") and true or false,
concurrency = (node.mux == "1" and ((node.mux_concurrency) and tonumber(node.mux_concurrency) or 8)) or ((node.xmux == "1") and -1) or nil,
xudpConcurrency = (node.xmux == "1" and ((node.xudp_concurrency) and tonumber(node.xudp_concurrency) or 8)) or nil
} or nil,
-- 底层传输配置
streamSettings = (node.streamSettings or node.protocol == "vmess" or node.protocol == "vless" or node.protocol == "socks" or node.protocol == "shadowsocks" or node.protocol == "trojan") and {
sockopt = {
mark = 255,
tcpMptcp = (node.tcpMptcp == "1") and true or nil,
tcpNoDelay = (node.tcpNoDelay == "1") and true or nil,
dialerProxy = fragment and "fragment" or nil
},
network = node.transport,
security = node.stream_security,
tlsSettings = (node.stream_security == "tls") and {
serverName = node.tls_serverName,
allowInsecure = (node.tls_allowInsecure == "1") and true or false,
fingerprint = (node.type == "Xray" and node.utls == "1" and node.fingerprint and node.fingerprint ~= "") and node.fingerprint or nil
} or nil,
realitySettings = (node.stream_security == "reality") and {
serverName = node.tls_serverName,
publicKey = node.reality_publicKey,
shortId = node.reality_shortId or "",
spiderX = node.reality_spiderX or "/",
fingerprint = (node.type == "Xray" and node.fingerprint and node.fingerprint ~= "") and node.fingerprint or "chrome"
} or nil,
tcpSettings = (node.transport == "tcp" and node.protocol ~= "socks") and {
header = {
type = node.tcp_guise or "none",
request = (node.tcp_guise == "http") and {
path = node.tcp_guise_http_path or {"/"},
headers = {
Host = node.tcp_guise_http_host or {}
}
} or nil
}
} or nil,
kcpSettings = (node.transport == "mkcp") and {
mtu = tonumber(node.mkcp_mtu),
tti = tonumber(node.mkcp_tti),
uplinkCapacity = tonumber(node.mkcp_uplinkCapacity),
downlinkCapacity = tonumber(node.mkcp_downlinkCapacity),
congestion = (node.mkcp_congestion == "1") and true or false,
readBufferSize = tonumber(node.mkcp_readBufferSize),
writeBufferSize = tonumber(node.mkcp_writeBufferSize),
seed = (node.mkcp_seed and node.mkcp_seed ~= "") and node.mkcp_seed or nil,
header = {type = node.mkcp_guise}
} or nil,
wsSettings = (node.transport == "ws") and {
path = node.ws_path or "/",
headers = (node.ws_host ~= nil) and
{Host = node.ws_host} or nil,
maxEarlyData = tonumber(node.ws_maxEarlyData) or nil,
earlyDataHeaderName = (node.ws_earlyDataHeaderName) and node.ws_earlyDataHeaderName or nil
} or nil,
httpSettings = (node.transport == "h2") and {
path = node.h2_path or "/",
host = node.h2_host,
read_idle_timeout = tonumber(node.h2_read_idle_timeout) or nil,
health_check_timeout = tonumber(node.h2_health_check_timeout) or nil
} or nil,
dsSettings = (node.transport == "ds") and
{path = node.ds_path} or nil,
quicSettings = (node.transport == "quic") and {
security = node.quic_security,
key = node.quic_key,
header = {type = node.quic_guise}
} or nil,
grpcSettings = (node.transport == "grpc") and {
serviceName = node.grpc_serviceName,
multiMode = (node.grpc_mode == "multi") and true or nil,
idle_timeout = tonumber(node.grpc_idle_timeout) or nil,
health_check_timeout = tonumber(node.grpc_health_check_timeout) or nil,
permit_without_stream = (node.grpc_permit_without_stream == "1") and true or nil,
initial_windows_size = tonumber(node.grpc_initial_windows_size) or nil
} or nil,
httpupgradeSettings = (node.transport == "httpupgrade") and {
path = node.httpupgrade_path or "/",
host = node.httpupgrade_host
} or nil,
} or nil,
settings = {
vnext = (node.protocol == "vmess" or node.protocol == "vless") and {
{
address = node.address,
port = tonumber(node.port),
users = {
{
id = node.uuid,
level = 0,
security = (node.protocol == "vmess") and node.security or nil,
encryption = node.encryption or "none",
flow = (node.protocol == "vless" and node.tls == '1' and node.flow) and node.flow or nil
}
}
}
} or nil,
servers = (node.protocol == "socks" or node.protocol == "http" or node.protocol == "shadowsocks" or node.protocol == "trojan") and {
{
address = node.address,
port = tonumber(node.port),
method = node.method or nil,
ivCheck = (node.protocol == "shadowsocks") and node.iv_check == "1" or nil,
uot = (node.protocol == "shadowsocks") and node.uot == "1" or nil,
password = node.password or "",
users = (node.username and node.password) and {
{
user = node.username,
pass = node.password
}
} or nil
}
} or nil,
address = (node.protocol == "wireguard" and node.wireguard_local_address) and node.wireguard_local_address or nil,
secretKey = (node.protocol == "wireguard") and node.wireguard_secret_key or nil,
peers = (node.protocol == "wireguard") and {
{
publicKey = node.wireguard_public_key,
endpoint = node.address .. ":" .. node.port,
preSharedKey = node.wireguard_preSharedKey,
keepAlive = node.wireguard_keepAlive and tonumber(node.wireguard_keepAlive) or nil
}
} or nil,
mtu = (node.protocol == "wireguard" and node.wireguard_mtu) and tonumber(node.wireguard_mtu) or nil,
reserved = (node.protocol == "wireguard" and node.wireguard_reserved) and node.wireguard_reserved or nil
}
}
if node.protocol == "wireguard" then
result.settings.kernelMode = false
end
local alpn = {}
if node.alpn and node.alpn ~= "default" then
string.gsub(node.alpn, '[^' .. "," .. ']+', function(w)
table.insert(alpn, w)
end)
end
if alpn and #alpn > 0 then
if result.streamSettings.tlsSettings then
result.streamSettings.tlsSettings.alpn = alpn
end
end
end
return result
end
function gen_config_server(node)
local settings = nil
local routing = nil
local outbounds = {
{protocol = "freedom", tag = "direct"}, {protocol = "blackhole", tag = "blocked"}
}
if node.protocol == "vmess" or node.protocol == "vless" then
if node.uuid then
local clients = {}
for i = 1, #node.uuid do
clients[i] = {
id = node.uuid[i],
flow = ("vless" == node.protocol and "1" == node.tls and node.flow) and node.flow or nil
}
end
settings = {
clients = clients,
decryption = node.decryption or "none"
}
end
elseif node.protocol == "socks" then
settings = {
udp = ("1" == node.udp_forward) and true or false,
auth = ("1" == node.auth) and "password" or "noauth",
accounts = ("1" == node.auth) and {
{
user = node.username,
pass = node.password
}
} or nil
}
elseif node.protocol == "http" then
settings = {
allowTransparent = false,
accounts = ("1" == node.auth) and {
{
user = node.username,
pass = node.password
}
} or nil
}
node.transport = "tcp"
node.tcp_guise = "none"
elseif node.protocol == "shadowsocks" then
settings = {
method = node.method,
password = node.password,
ivCheck = ("1" == node.iv_check) and true or false,
network = node.ss_network or "TCP,UDP"
}
elseif node.protocol == "trojan" then
if node.uuid then
local clients = {}
for i = 1, #node.uuid do
clients[i] = {
password = node.uuid[i]
}
end
settings = {
clients = clients
}
end
elseif node.protocol == "dokodemo-door" then
settings = {
network = node.d_protocol,
address = node.d_address,
port = tonumber(node.d_port)
}
end
if node.fallback and node.fallback == "1" then
local fallbacks = {}
for i = 1, #node.fallback_list do
local fallbackStr = node.fallback_list[i]
if fallbackStr then
local tmp = {}
string.gsub(fallbackStr, '[^' .. "," .. ']+', function(w)
table.insert(tmp, w)
end)
local dest = tmp[1] or ""
local path = tmp[2]
if dest:find("%.") then
else
dest = tonumber(dest)
end
fallbacks[i] = {
path = path,
dest = dest,
xver = 1
}
end
end
settings.fallbacks = fallbacks
end
routing = {
domainStrategy = "IPOnDemand",
rules = {
{
type = "field",
ip = {"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"},
outboundTag = (node.accept_lan == nil or node.accept_lan == "0") and "blocked" or "direct"
}
}
}
if node.outbound_node and node.outbound_node ~= "nil" then
local outbound = nil
if node.outbound_node == "_iface" and node.outbound_node_iface then
outbound = {
protocol = "freedom",
tag = "outbound",
streamSettings = {
sockopt = {
mark = 255,
interface = node.outbound_node_iface
}
}
}
sys.call("mkdir -p /tmp/etc/passwall2/iface && touch /tmp/etc/passwall2/iface/" .. node.outbound_node_iface)
else
local outbound_node_t = uci:get_all("passwall2", node.outbound_node)
if node.outbound_node == "_socks" or node.outbound_node == "_http" then
outbound_node_t = {
type = node.type,
protocol = node.outbound_node:gsub("_", ""),
transport = "tcp",
address = node.outbound_node_address,
port = node.outbound_node_port,
username = (node.outbound_node_username and node.outbound_node_username ~= "") and node.outbound_node_username or nil,
password = (node.outbound_node_password and node.outbound_node_password ~= "") and node.outbound_node_password or nil,
}
end
outbound = require("luci.passwall2.util_xray").gen_outbound(nil, outbound_node_t, "outbound")
end
if outbound then
table.insert(outbounds, 1, outbound)
end
end
local config = {
log = {
loglevel = ("1" == node.log) and node.loglevel or "none"
},
-- 传入连接
inbounds = {
{
listen = (node.bind_local == "1") and "127.0.0.1" or nil,
port = tonumber(node.port),
protocol = node.protocol,
settings = settings,
streamSettings = {
network = node.transport,
security = "none",
tlsSettings = ("1" == node.tls) and {
disableSystemRoot = false,
certificates = {
{
certificateFile = node.tls_certificateFile,
keyFile = node.tls_keyFile
}
}
} or nil,
tcpSettings = (node.transport == "tcp") and {
header = {
type = node.tcp_guise,
request = (node.tcp_guise == "http") and {
path = node.tcp_guise_http_path or {"/"},
headers = {
Host = node.tcp_guise_http_host or {}
}
} or nil
}
} or nil,
kcpSettings = (node.transport == "mkcp") and {
mtu = tonumber(node.mkcp_mtu),
tti = tonumber(node.mkcp_tti),
uplinkCapacity = tonumber(node.mkcp_uplinkCapacity),
downlinkCapacity = tonumber(node.mkcp_downlinkCapacity),
congestion = (node.mkcp_congestion == "1") and true or false,
readBufferSize = tonumber(node.mkcp_readBufferSize),
writeBufferSize = tonumber(node.mkcp_writeBufferSize),
seed = (node.mkcp_seed and node.mkcp_seed ~= "") and node.mkcp_seed or nil,
header = {type = node.mkcp_guise}
} or nil,
wsSettings = (node.transport == "ws") and {
headers = (node.ws_host) and {Host = node.ws_host} or nil,
path = node.ws_path
} or nil,
httpSettings = (node.transport == "h2") and {
path = node.h2_path, host = node.h2_host
} or nil,
dsSettings = (node.transport == "ds") and {
path = node.ds_path
} or nil,
quicSettings = (node.transport == "quic") and {
security = node.quic_security,
key = node.quic_key,
header = {type = node.quic_guise}
} or nil,
grpcSettings = (node.transport == "grpc") and {
serviceName = node.grpc_serviceName
} or nil,
httpupgradeSettings = (node.transport == "httpupgrade") and {
path = node.httpupgrade_path or "/",
host = node.httpupgrade_host
} or nil,
sockopt = {
acceptProxyProtocol = (node.acceptProxyProtocol and node.acceptProxyProtocol == "1") and true or false
}
}
}
},
-- 传出连接
outbounds = outbounds,
routing = routing
}
local alpn = {}
if node.alpn then
string.gsub(node.alpn, '[^' .. "," .. ']+', function(w)
table.insert(alpn, w)
end)
end
if alpn and #alpn > 0 then
if config.inbounds[1].streamSettings.tlsSettings then
config.inbounds[1].streamSettings.tlsSettings.alpn = alpn
end
end
if "1" == node.tls then
config.inbounds[1].streamSettings.security = "tls"
if "1" == node.reality then
config.inbounds[1].streamSettings.tlsSettings = nil
config.inbounds[1].streamSettings.security = "reality"
config.inbounds[1].streamSettings.realitySettings = {
show = false,
dest = node.reality_dest,
serverNames = {
node.reality_serverNames
},
privateKey = node.reality_private_key,
shortIds = node.reality_shortId or ""
} or nil
end
end
return config
end
function gen_config(var)
local flag = var["-flag"]
local loglevel = var["-loglevel"] or "warning"
local node_id = var["-node"]
local server_host = var["-server_host"]
local server_port = var["-server_port"]
local tcp_proxy_way = var["-tcp_proxy_way"]
local redir_port = var["-redir_port"]
local local_socks_address = var["-local_socks_address"] or "0.0.0.0"
local local_socks_port = var["-local_socks_port"]
local local_socks_username = var["-local_socks_username"]
local local_socks_password = var["-local_socks_password"]
local local_http_address = var["-local_http_address"] or "0.0.0.0"
local local_http_port = var["-local_http_port"]
local local_http_username = var["-local_http_username"]
local local_http_password = var["-local_http_password"]
local dns_listen_port = var["-dns_listen_port"]
local direct_dns_udp_server = var["-direct_dns_udp_server"]
local direct_dns_udp_port = var["-direct_dns_udp_port"]
local direct_dns_query_strategy = var["-direct_dns_query_strategy"]
local direct_ipset = var["-direct_ipset"]
local direct_nftset = var["-direct_nftset"]
local remote_dns_udp_server = var["-remote_dns_udp_server"]
local remote_dns_udp_port = var["-remote_dns_udp_port"]
local remote_dns_tcp_server = var["-remote_dns_tcp_server"]
local remote_dns_tcp_port = var["-remote_dns_tcp_port"]
local remote_dns_doh_url = var["-remote_dns_doh_url"]
local remote_dns_doh_host = var["-remote_dns_doh_host"]
local remote_dns_doh_ip = var["-remote_dns_doh_ip"]
local remote_dns_doh_port = var["-remote_dns_doh_port"]
local remote_dns_fake = var["-remote_dns_fake"]
local remote_dns_query_strategy = var["-remote_dns_query_strategy"]
local remote_dns_detour = var["-remote_dns_detour"]
local dns_cache = var["-dns_cache"]
local dns_domain_rules = {}
local dns = nil
local fakedns = nil
local inbounds = {}
local outbounds = {}
local routing = nil
local observatory = nil
local CACHE_TEXT_FILE = CACHE_PATH .. "/cache_" .. flag .. ".txt"
local xray_settings = uci:get_all(appname, "@global_xray[0]") or {}
local nodes = {}
if node_id then
local node = uci:get_all(appname, node_id)
if node then
nodes[node_id] = node
end
end
if local_socks_port then
local inbound = {
tag = "socks-in",
listen = local_socks_address,
port = tonumber(local_socks_port),
protocol = "socks",
settings = {auth = "noauth", udp = true},
sniffing = {enabled = true, destOverride = {"http", "tls", "quic"}}
}
if local_socks_username and local_socks_password and local_socks_username ~= "" and local_socks_password ~= "" then
inbound.settings.auth = "password"
inbound.settings.accounts = {
{
user = local_socks_username,
pass = local_socks_password
}
}
end
table.insert(inbounds, inbound)
end
if local_http_port then
local inbound = {
listen = local_http_address,
port = tonumber(local_http_port),
protocol = "http",
settings = {allowTransparent = false}
}
if local_http_username and local_http_password and local_http_username ~= "" and local_http_password ~= "" then
inbound.settings.accounts = {
{
user = local_http_username,
pass = local_http_password
}
}
end
table.insert(inbounds, inbound)
end
if redir_port then
local inbound = {
port = tonumber(redir_port),
protocol = "dokodemo-door",
settings = {network = "tcp,udp", followRedirect = true},
streamSettings = {sockopt = {tproxy = "tproxy"}},
sniffing = {
enabled = xray_settings.sniffing == "1" and true or false,
destOverride = {"http", "tls", "quic", (remote_dns_fake) and "fakedns"},
metadataOnly = false,
routeOnly = (xray_settings.sniffing == "1" and xray_settings.route_only == "1") and true or nil,
domainsExcluded = (xray_settings.sniffing == "1" and xray_settings.route_only == "0") and get_domain_excluded() or nil
}
}
local tcp_inbound = api.clone(inbound)
tcp_inbound.tag = "tcp_redir"
tcp_inbound.settings.network = "tcp"
tcp_inbound.streamSettings.sockopt.tproxy = tcp_proxy_way
table.insert(inbounds, tcp_inbound)
local udp_inbound = api.clone(inbound)
udp_inbound.tag = "udp_redir"
udp_inbound.settings.network = "udp"
table.insert(inbounds, udp_inbound)
end
local function get_balancer_tag(_node_id)
return "balancer-" .. _node_id
end
local function gen_balancer(_node, loopbackTag)
local blc_nodes = _node.balancing_node
local length = #blc_nodes
local valid_nodes = {}
for i = 1, length do
local blc_node_id = blc_nodes[i]
local blc_node_tag = "blc-" .. blc_node_id
local is_new_blc_node = true
for _, outbound in ipairs(outbounds) do
if outbound.tag == blc_node_tag then
is_new_blc_node = false
valid_nodes[#valid_nodes + 1] = blc_node_tag
break
end
end
if is_new_blc_node then
local blc_node = uci:get_all(appname, blc_node_id)
local outbound = gen_outbound(flag, blc_node, blc_node_tag, { fragment = xray_settings.fragment == "1" or nil })
if outbound then
table.insert(outbounds, outbound)
valid_nodes[#valid_nodes + 1] = blc_node_tag
end
end
end
local balancer, rule
if #valid_nodes > 0 then
local balancerTag = get_balancer_tag(_node[".name"])
balancer = {
tag = balancerTag,
selector = valid_nodes,
strategy = { type = _node.balancingStrategy or "random" }
}
if _node.balancingStrategy == "leastPing" or _node.balancingStrategy == "leastLoad" then
if not observatory then
observatory = {
subjectSelector = { "blc-" },
probeUrl = _node.useCustomProbeUrl and _node.probeUrl or nil,
probeInterval = _node.probeInterval or "1m",
enableConcurrency = (nodes[node_id] and nodes[node_id].type == "Xray") and true or nil --这里只判断顶层节点(分流总节点/单独的负载均衡节点)类型为Xray就可以启用并发
}
end
end
if loopbackTag and loopbackTag ~= "" then
local inboundTag = loopbackTag .. "-in"
table.insert(outbounds, {
protocol = "loopback",
tag = loopbackTag,
settings = { inboundTag = inboundTag }
})
rule = {
type = "field",
inboundTag = { inboundTag },
balancerTag = balancerTag
}
end
end
return balancer, rule
end
local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
if not node or not outbound or not outbounds_table then return nil end
local default_outTag = outbound.tag
if node.to_node then
local to_node = uci:get_all(appname, node.to_node)
if to_node then
local to_outbound = gen_outbound(nil, to_node)
if to_outbound then
if shunt_rule_name then
to_outbound.tag = outbound.tag
outbound.tag = node[".name"]
else
to_outbound.tag = outbound.tag .. " -> " .. to_outbound.tag
end
to_outbound.proxySettings = {
tag = outbound.tag,
transportLayer = true
}
table.insert(outbounds_table, to_outbound)
default_outTag = to_outbound.tag
end
end
end
return default_outTag
end
for k, v in pairs(nodes) do
if server_host and server_port then
v.address = server_host
v.port = server_port
end
local node = v
if node.protocol == "_shunt" then
local rules = {}
local balancers = {}
local preproxy_enabled = node.preproxy_enabled == "1"
local preproxy_tag = "main"
local preproxy_node_id = node["main_node"]
local preproxy_node = preproxy_enabled and preproxy_node_id and uci:get_all(appname, preproxy_node_id) or nil
local preproxy_is_balancer
if preproxy_node_id and preproxy_node_id:find("Socks_") then
local socks_id = preproxy_node_id:sub(1 + #"Socks_")
local socks_node = uci:get_all(appname, socks_id) or nil
if socks_node then
local _node = {
type = "Xray",
protocol = "socks",
address = "127.0.0.1",
port = socks_node.port,
transport = "tcp",
stream_security = "none"
}
local preproxy_outbound = gen_outbound(flag, _node, preproxy_tag)
if preproxy_outbound then
table.insert(outbounds, preproxy_outbound)
else
preproxy_enabled = false
end
end
elseif preproxy_node and api.is_normal_node(preproxy_node) then
local preproxy_outbound = gen_outbound(flag, preproxy_node, preproxy_tag, { fragment = xray_settings.fragment == "1" or nil })
if preproxy_outbound then
set_outbound_detour(preproxy_node, preproxy_outbound, outbounds, preproxy_tag)
table.insert(outbounds, preproxy_outbound)
else
preproxy_enabled = false
end
elseif preproxy_node and preproxy_node.protocol == "_balancing" then
preproxy_is_balancer = true
local preproxy_balancer, preproxy_rule = gen_balancer(preproxy_node, preproxy_tag)
if preproxy_balancer and preproxy_rule then
table.insert(balancers, preproxy_balancer)
table.insert(rules, preproxy_rule)
else
preproxy_enabled = false
end
end
local function gen_shunt_node(rule_name, _node_id)
if not rule_name then return nil, nil end
if not _node_id then _node_id = node[rule_name] or "nil" end
local rule_outboundTag
local rule_balancerTag
if _node_id == "_direct" then
rule_outboundTag = "direct"
elseif _node_id == "_blackhole" then
rule_outboundTag = "blackhole"
elseif _node_id == "_default" and rule_name ~= "default" then
rule_outboundTag = "default"
elseif _node_id:find("Socks_") then
local socks_id = _node_id:sub(1 + #"Socks_")
local socks_node = uci:get_all(appname, socks_id) or nil
if socks_node then
local _node = {
type = "Xray",
protocol = "socks",
address = "127.0.0.1",
port = socks_node.port,
transport = "tcp",
stream_security = "none"
}
local _outbound = gen_outbound(flag, _node, rule_name)
if _outbound then
table.insert(outbounds, _outbound)
rule_outboundTag = rule_name
end
end
elseif _node_id ~= "nil" then
local _node = uci:get_all(appname, _node_id)
if not _node then return nil, nil end
if api.is_normal_node(_node) then
local proxy = preproxy_enabled and node[rule_name .. "_proxy_tag"] == preproxy_tag and _node_id ~= preproxy_node_id
if proxy and preproxy_is_balancer then
local blc_nodes = preproxy_node.balancing_node
for _, blc_node_id in ipairs(blc_nodes) do
if _node_id == blc_node_id then
proxy = false
break
end
end
end
local copied_outbound
for index, value in ipairs(outbounds) do
if value["_flag_tag"] == _node_id and value["_flag_proxy_tag"] == preproxy_tag then
copied_outbound = api.clone(value)
break
end
end
if copied_outbound then
copied_outbound.tag = rule_name
table.insert(outbounds, copied_outbound)
rule_outboundTag = rule_name
else
if proxy then
local pre_proxy = nil
if _node.type ~= "Xray" then
pre_proxy = true
end
if _node.type == "Xray" and _node.flow == "xtls-rprx-vision" then
pre_proxy = true
end
if pre_proxy then
new_port = get_new_port()
table.insert(inbounds, {
tag = "proxy_" .. rule_name,
listen = "127.0.0.1",
port = new_port,
protocol = "dokodemo-door",
settings = {network = "tcp,udp", address = _node.address, port = tonumber(_node.port)}
})
if _node.tls_serverName == nil then
_node.tls_serverName = _node.address
end
_node.address = "127.0.0.1"
_node.port = new_port
table.insert(rules, 1, {
type = "field",
inboundTag = {"proxy_" .. rule_name},
outboundTag = is_balancing_proxy and nil or preproxy_tag,
balancerTag = is_balancing_proxy and get_balancer_tag(proxy_node_id) or nil
})
end
end
local proxy_table = {
proxy = proxy and 1 or 0,
tag = proxy and preproxy_tag or nil
}
if xray_settings.fragment == "1" and not proxy_table.tag then
proxy_table.fragment = true
end
local _outbound = gen_outbound(flag, _node, rule_name, proxy_table)
if _outbound then
set_outbound_detour(_node, _outbound, outbounds, rule_name)
table.insert(outbounds, _outbound)
if proxy then preproxy_used = true end
rule_outboundTag = rule_name
end
end
elseif _node.protocol == "_balancing" then
local is_new_balancer = true
for _, v in ipairs(balancers) do
if v["_flag_tag"] == _node_id then
is_new_balancer = false
rule_balancerTag = v.tag
break
end
end
if is_new_balancer then
local balancer = gen_balancer(_node)
if balancer then
table.insert(balancers, balancer)
rule_balancerTag = balancer.tag
end
end
elseif _node.protocol == "_iface" then
if _node.iface then
local _outbound = {
protocol = "freedom",
tag = rule_name,
streamSettings = {
sockopt = {
mark = 255,
interface = _node.iface
}
}
}
table.insert(outbounds, _outbound)
rule_outboundTag = rule_name
sys.call("touch /tmp/etc/passwall2/iface/" .. _node.iface)
end
end
end
return rule_outboundTag, rule_balancerTag
end
--default_node
local default_node_id = node.default_node or "_direct"
local default_outboundTag, default_balancerTag = gen_shunt_node("default", default_node_id)
--shunt rule
uci:foreach(appname, "shunt_rules", function(e)
local outboundTag, balancerTag = gen_shunt_node(e[".name"])
if outboundTag or balancerTag and e.remarks then
if outboundTag == "default" then
outboundTag = default_outboundTag
balancerTag = default_balancerTag
end
local protocols = nil
if e["protocol"] and e["protocol"] ~= "" then
protocols = {}
string.gsub(e["protocol"], '[^' .. " " .. ']+', function(w)
table.insert(protocols, w)
end)
end
local inboundTag = nil
if e["inbound"] and e["inbound"] ~= "" then
inboundTag = {}
if e["inbound"]:find("tproxy") then
if redir_port then
table.insert(inboundTag, "tcp_redir")
table.insert(inboundTag, "udp_redir")
end
end
if e["inbound"]:find("socks") then
if local_socks_port then
table.insert(inboundTag, "socks-in")
end
end
end
local domains = nil
if e.domain_list then
local domain_table = {
shunt_rule_name = e[".name"],
outboundTag = outboundTag,
domain = {},
}
domains = {}
string.gsub(e.domain_list, '[^' .. "\r\n" .. ']+', function(w)
table.insert(domains, w)
table.insert(domain_table.domain, w)
end)
if outboundTag and outboundTag ~= "nil" then
table.insert(dns_domain_rules, api.clone(domain_table))
end
end
local ip = nil
if e.ip_list then
ip = {}
string.gsub(e.ip_list, '[^' .. "\r\n" .. ']+', function(w)
table.insert(ip, w)
end)
end
local source = nil
if e.source then
source = {}
string.gsub(e.source, '[^' .. " " .. ']+', function(w)
table.insert(source, w)
end)
end
local rule = {
_flag = e.remarks,
type = "field",
inboundTag = inboundTag,
outboundTag = outboundTag,
balancerTag = balancerTag,
network = e["network"] or "tcp,udp",
source = source,
sourcePort = e["sourcePort"] ~= "" and e["sourcePort"] or nil,
port = e["port"] ~= "" and e["port"] or nil,
protocol = protocols
}
if domains then
local _rule = api.clone(rule)
_rule["_flag"] = _rule["_flag"] .. "_domains"
_rule.domains = domains
table.insert(rules, _rule)
end
if ip then
local _rule = api.clone(rule)
_rule["_flag"] = _rule["_flag"] .. "_ip"
_rule.ip = ip
table.insert(rules, _rule)
end
if not domains and not ip then
table.insert(rules, rule)
end
end
end)
if default_outboundTag or default_balancerTag then
table.insert(rules, {
_flag = "default",
type = "field",
outboundTag = default_outboundTag,
balancerTag = default_balancerTag,
network = "tcp,udp"
})
end
routing = {
domainStrategy = node.domainStrategy or "AsIs",
domainMatcher = node.domainMatcher or "hybrid",
balancers = #balancers > 0 and balancers or nil,
rules = rules
}
elseif node.protocol == "_balancing" then
if node.balancing_node then
local balancer = gen_balancer(node)
routing = {
balancers = { balancer },
rules = {
{ type = "field", network = "tcp,udp", balancerTag = balancer.tag }
}
}
end
elseif node.protocol == "_iface" then
if node.iface then
local outbound = {
protocol = "freedom",
tag = "outbound",
streamSettings = {
sockopt = {
mark = 255,
interface = node.iface
}
}
}
table.insert(outbounds, outbound)
sys.call("touch /tmp/etc/passwall2/iface/" .. node.iface)
end
else
local outbound = gen_outbound(flag, node, nil, { fragment = xray_settings.fragment == "1" or nil })
if outbound then
local default_outTag = set_outbound_detour(node, outbound, outbounds)
table.insert(outbounds, outbound)
routing = {
domainStrategy = "AsIs",
domainMatcher = "hybrid",
rules = {}
}
table.insert(routing.rules, {
_flag = "default",
type = "field",
outboundTag = default_outTag,
network = "tcp,udp"
})
end
end
end
if dns_listen_port then
local rules = {}
local _remote_dns_proto = "tcp"
if not routing then
routing = {
domainStrategy = "IPOnDemand",
rules = {}
}
end
dns = {
tag = "dns-in1",
hosts = {},
disableCache = (dns_cache and dns_cache == "0") and true or false,
disableFallback = true,
disableFallbackIfMatch = true,
servers = {},
queryStrategy = "UseIP"
}
local dns_host = ""
if flag == "global" then
dns_host = uci:get(appname, "@global[0]", "dns_hosts") or ""
else
flag = flag:gsub("acl_", "")
local dns_hosts_mode = uci:get(appname, flag, "dns_hosts_mode") or "default"
if dns_hosts_mode == "default" then
dns_host = uci:get(appname, "@global[0]", "dns_hosts") or ""
elseif dns_hosts_mode == "disable" then
dns_host = ""
elseif dns_hosts_mode == "custom" then
dns_host = uci:get(appname, flag, "dns_hosts") or ""
end
end
if #dns_host > 0 then
string.gsub(dns_host, '[^' .. "\r\n" .. ']+', function(w)
local host = sys.exec(string.format("echo -n $(echo %s | awk -F ' ' '{print $1}')", w))
local key = sys.exec(string.format("echo -n $(echo %s | awk -F ' ' '{print $2}')", w))
if host ~= "" and key ~= "" then
dns.hosts[host] = key
end
end)
end
local _remote_dns = {
_flag = "remote",
queryStrategy = (remote_dns_query_strategy and remote_dns_query_strategy ~= "") and remote_dns_query_strategy or "UseIPv4"
}
if remote_dns_udp_server then
_remote_dns.address = remote_dns_udp_server
_remote_dns.port = tonumber(remote_dns_udp_port) or 53
_remote_dns_proto = "udp"
end
if remote_dns_tcp_server then
_remote_dns.address = "tcp://" .. remote_dns_tcp_server
_remote_dns.port = tonumber(remote_dns_tcp_port) or 53
_remote_dns_proto = "tcp"
end
if remote_dns_doh_url and remote_dns_doh_host then
if remote_dns_doh_ip and remote_dns_doh_host ~= remote_dns_doh_ip and not api.is_ip(remote_dns_doh_host) then
dns.hosts[remote_dns_doh_host] = remote_dns_doh_ip
end
_remote_dns.address = remote_dns_doh_url
_remote_dns.port = tonumber(remote_dns_doh_port) or 443
end
if _remote_dns.address then
table.insert(dns.servers, _remote_dns)
if remote_dns_detour == "direct" then
table.insert(routing.rules, 1, {
type = "field",
ip = {
_remote_dns.address
},
port = _remote_dns.port,
network = _remote_dns_proto,
outboundTag = "direct"
})
end
end
local _remote_fakedns = nil
if remote_dns_fake then
fakedns = {}
local fakedns4 = {
ipPool = "198.18.0.0/16",
poolSize = 65535
}
local fakedns6 = {
ipPool = "fc00::/18",
poolSize = 65535
}
if remote_dns_query_strategy == "UseIP" then
table.insert(fakedns, fakedns4)
table.insert(fakedns, fakedns6)
elseif remote_dns_query_strategy == "UseIPv4" then
table.insert(fakedns, fakedns4)
elseif remote_dns_query_strategy == "UseIPv6" then
table.insert(fakedns, fakedns6)
end
_remote_fakedns = {
_flag = "remote_fakedns",
address = "fakedns",
}
table.insert(dns.servers, _remote_fakedns)
end
local _direct_dns = nil
if direct_dns_udp_server then
local domain = {}
local nodes_domain_text = sys.exec('uci show passwall2 | grep ".address=" | cut -d "\'" -f 2 | grep "[a-zA-Z]$" | sort -u')
string.gsub(nodes_domain_text, '[^' .. "\r\n" .. ']+', function(w)
table.insert(domain, "full:" .. w)
end)
if #domain > 0 then
table.insert(dns_domain_rules, 1, {
outboundTag = "direct",
domain = domain
})
end
_direct_dns = {
_flag = "direct",
address = direct_dns_udp_server,
port = tonumber(direct_dns_udp_port) or 53,
queryStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP",
}
table.insert(routing.rules, 1, {
type = "field",
ip = {
direct_dns_udp_server
},
port = tonumber(direct_dns_udp_port) or 53,
network = "udp",
outboundTag = "direct"
})
table.insert(dns.servers, _direct_dns)
end
if dns_listen_port then
table.insert(inbounds, {
listen = "127.0.0.1",
port = tonumber(dns_listen_port),
protocol = "dokodemo-door",
tag = "dns-in",
settings = {
address = "0.0.0.0",
network = "tcp,udp"
}
})
local direct_type_dns = {
settings = {
address = direct_dns_udp_server,
port = tonumber(direct_dns_udp_port) or 53,
network = "udp",
nonIPQuery = "skip"
},
proxySettings = {
tag = "direct"
}
}
local remote_type_dns = {
settings = {
address = remote_dns_udp_server,
port = tonumber(remote_dns_udp_port) or 53,
network = _remote_dns_proto or "tcp",
nonIPQuery = "drop"
}
}
local type_dns = direct_type_dns
table.insert(outbounds, {
tag = "dns-out",
protocol = "dns",
proxySettings = type_dns.proxySettings,
settings = type_dns.settings
})
table.insert(routing.rules, 1, {
type = "field",
inboundTag = {
"dns-in"
},
outboundTag = "dns-out"
})
end
local default_dns_flag = "remote"
if node_id and redir_port then
local node = uci:get_all(appname, node_id)
if node.protocol == "_shunt" then
if node.default_node == "_direct" then
default_dns_flag = "direct"
end
end
end
if dns.servers and #dns.servers > 0 then
local dns_servers = nil
for index, value in ipairs(dns.servers) do
if not dns_servers and value["_flag"] == default_dns_flag then
if value["_flag"] == "remote" and remote_dns_fake then
value["_flag"] = "default"
break
end
dns_servers = {
_flag = "default",
address = value.address,
port = value.port,
queryStrategy = value.queryStrategy
}
break
end
end
if dns_servers then
table.insert(dns.servers, 1, dns_servers)
end
--按分流顺序DNS
if dns_domain_rules and #dns_domain_rules > 0 then
for index, value in ipairs(dns_domain_rules) do
if value.outboundTag and value.domain then
local dns_server = nil
if value.outboundTag == "direct" then
dns_server = api.clone(_direct_dns)
else
if remote_dns_fake then
dns_server = api.clone(_remote_fakedns)
else
dns_server = api.clone(_remote_dns)
end
end
dns_server.domains = value.domain
if value.shunt_rule_name then
dns_server["_flag"] = value.shunt_rule_name
end
if dns_server then
table.insert(dns.servers, dns_server)
end
end
end
end
for i = #dns.servers, 1, -1 do
local v = dns.servers[i]
if v["_flag"] ~= "default" then
if not v.domains or #v.domains == 0 then
table.remove(dns.servers, i)
end
end
end
end
local default_rule_index = #routing.rules > 0 and #routing.rules or 1
for index, value in ipairs(routing.rules) do
if value["_flag"] == "default" then
default_rule_index = index
break
end
end
for index, value in ipairs(rules) do
local t = rules[#rules + 1 - index]
table.insert(routing.rules, default_rule_index, t)
end
local dns_hosts_len = 0
for key, value in pairs(dns.hosts) do
dns_hosts_len = dns_hosts_len + 1
end
if dns_hosts_len == 0 then
dns.hosts = nil
end
local content = flag .. node_id .. jsonc.stringify(routing.rules)
if api.cacheFileCompareToLogic(CACHE_TEXT_FILE, content) == false then
--clear ipset/nftset
if direct_ipset then
string.gsub(direct_ipset, '[^' .. "," .. ']+', function(w)
sys.call("ipset -q -F " .. w)
end)
end
if direct_nftset then
string.gsub(direct_nftset, '[^' .. "," .. ']+', function(w)
local split = api.split(w, "#")
if #split > 3 then
local ip_type = split[1]
local family = split[2]
local table_name = split[3]
local set_name = split[4]
sys.call(string.format("nft flush set %s %s %s 2>/dev/null", family, table_name, set_name))
end
end)
end
end
end
if inbounds or outbounds then
local config = {
log = {
--access = string.format("/tmp/etc/%s/%s_access.log", appname, "global"),
--error = string.format("/tmp/etc/%s/%s_error.log", appname, "global"),
--dnsLog = true,
loglevel = loglevel
},
-- DNS
dns = dns,
fakedns = fakedns,
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 连接观测
observatory = observatory,
-- 路由
routing = routing,
-- 本地策略
policy = {
levels = {
[0] = {
-- handshake = 4,
-- connIdle = 300,
-- uplinkOnly = 2,
-- downlinkOnly = 5,
bufferSize = xray_settings.buffer_size and tonumber(xray_settings.buffer_size) or nil,
statsUserUplink = false,
statsUserDownlink = false
}
},
-- system = {
-- statsInboundUplink = false,
-- statsInboundDownlink = false
-- }
}
}
if xray_settings.fragment == "1" then
table.insert(outbounds, {
protocol = "freedom",
tag = "fragment",
settings = {
domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP",
fragments = {
packets = (xray_settings.fragment_packets and xray_settings.fragment_packets ~= "") and xray_settings.fragment_packets,
length = (xray_settings.fragment_length and xray_settings.fragment_length ~= "") and xray_settings.fragment_length,
interval = (xray_settings.fragment_interval and xray_settings.fragment_interval ~= "") and xray_settings.fragment_interval
}
},
streamSettings = {
sockopt = {
mark = 255,
tcpNoDelay = true
}
}
})
end
table.insert(outbounds, {
protocol = "freedom",
tag = "direct",
settings = {
domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP"
},
streamSettings = {
sockopt = {
mark = 255
}
}
})
table.insert(outbounds, {
protocol = "blackhole",
tag = "blackhole"
})
return jsonc.stringify(config, 1)
end
end
function gen_proto_config(var)
local local_socks_address = var["-local_socks_address"] or "0.0.0.0"
local local_socks_port = var["-local_socks_port"]
local local_socks_username = var["-local_socks_username"]
local local_socks_password = var["-local_socks_password"]
local local_http_address = var["-local_http_address"] or "0.0.0.0"
local local_http_port = var["-local_http_port"]
local local_http_username = var["-local_http_username"]
local local_http_password = var["-local_http_password"]
local server_proto = var["-server_proto"]
local server_address = var["-server_address"]
local server_port = var["-server_port"]
local server_username = var["-server_username"]
local server_password = var["-server_password"]
local inbounds = {}
local outbounds = {}
local routing = nil
if local_socks_address and local_socks_port then
local inbound = {
listen = local_socks_address,
port = tonumber(local_socks_port),
protocol = "socks",
settings = {
udp = true,
auth = "noauth"
}
}
if local_socks_username and local_socks_password and local_socks_username ~= "" and local_socks_password ~= "" then
inbound.settings.auth = "password"
inbound.settings.accounts = {
{
user = local_socks_username,
pass = local_socks_password
}
}
end
table.insert(inbounds, inbound)
end
if local_http_address and local_http_port then
local inbound = {
listen = local_http_address,
port = tonumber(local_http_port),
protocol = "http",
settings = {
allowTransparent = false
}
}
if local_http_username and local_http_password and local_http_username ~= "" and local_http_password ~= "" then
inbound.settings.accounts = {
{
user = local_http_username,
pass = local_http_password
}
}
end
table.insert(inbounds, inbound)
end
if server_proto ~= "nil" and server_address ~= "nil" and server_port ~= "nil" then
local outbound = {
protocol = server_proto,
streamSettings = {
network = "tcp",
security = "none"
},
settings = {
servers = {
{
address = server_address,
port = tonumber(server_port),
users = (server_username and server_password) and {
{
user = server_username,
pass = server_password
}
} or nil
}
}
}
}
if outbound then table.insert(outbounds, outbound) end
end
-- 额外传出连接
table.insert(outbounds, {
protocol = "freedom", tag = "direct", settings = {keep = ""}
})
local config = {
log = {
loglevel = "warning"
},
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 路由
routing = routing
}
return jsonc.stringify(config, 1)
end
function gen_dns_config(var)
local dns_listen_port = var["-dns_listen_port"]
local dns_out_tag = var["-dns_out_tag"]
local dns_client_ip = var["-dns_client_ip"]
local direct_dns_udp_server = var["-direct_dns_udp_server"]
local direct_dns_udp_port = var["-direct_dns_udp_port"]
local direct_dns_tcp_server = var["-direct_dns_tcp_server"]
local direct_dns_tcp_port = var["-direct_dns_tcp_port"]
local direct_dns_doh_url = var["-direct_dns_doh_url"]
local direct_dns_doh_host = var["-direct_dns_doh_host"]
local direct_dns_doh_ip = var["-direct_dns_doh_ip"]
local direct_dns_doh_port = var["-direct_dns_doh_port"]
local direct_dns_query_strategy = var["-direct_dns_query_strategy"]
local remote_dns_udp_server = var["-remote_dns_udp_server"]
local remote_dns_udp_port = var["-remote_dns_udp_port"]
local remote_dns_tcp_server = var["-remote_dns_tcp_server"]
local remote_dns_tcp_port = var["-remote_dns_tcp_port"]
local remote_dns_doh_url = var["-remote_dns_doh_url"]
local remote_dns_doh_host = var["-remote_dns_doh_host"]
local remote_dns_doh_ip = var["-remote_dns_doh_ip"]
local remote_dns_doh_port = var["-remote_dns_doh_port"]
local remote_dns_query_strategy = var["-remote_dns_query_strategy"]
local remote_dns_detour = var["-remote_dns_detour"]
local remote_dns_outbound_socks_address = var["-remote_dns_outbound_socks_address"]
local remote_dns_outbound_socks_port = var["-remote_dns_outbound_socks_port"]
local dns_cache = var["-dns_cache"]
local loglevel = var["-loglevel"] or "warning"
local inbounds = {}
local outbounds = {}
local dns = nil
local routing = nil
if dns_listen_port then
routing = {
domainStrategy = "IPOnDemand",
rules = {}
}
dns = {
tag = "dns-in1",
hosts = {},
disableCache = (dns_cache == "1") and false or true,
disableFallback = true,
disableFallbackIfMatch = true,
servers = {},
clientIp = (dns_client_ip and dns_client_ip ~= "") and dns_client_ip or nil,
}
local other_type_dns_proto, other_type_dns_server, other_type_dns_port
if dns_out_tag == "remote" then
dns.queryStrategy = (remote_dns_query_strategy and remote_dns_query_strategy ~= "") and remote_dns_query_strategy or "UseIPv4"
if remote_dns_detour == "direct" then
dns_out_tag = "direct"
table.insert(outbounds, 1, {
tag = dns_out_tag,
protocol = "freedom",
settings = {
domainStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP"
},
streamSettings = {
sockopt = {
mark = 255
}
}
})
else
if remote_dns_outbound_socks_address and remote_dns_outbound_socks_port then
table.insert(outbounds, 1, {
tag = dns_out_tag,
protocol = "socks",
streamSettings = {
network = "tcp",
security = "none"
},
settings = {
servers = {
{
address = remote_dns_outbound_socks_address,
port = tonumber(remote_dns_outbound_socks_port)
}
}
}
})
end
end
local _remote_dns = {
_flag = "remote"
}
if remote_dns_udp_server then
_remote_dns.address = remote_dns_udp_server
_remote_dns.port = tonumber(remote_dns_udp_port) or 53
other_type_dns_proto = "udp"
other_type_dns_server = remote_dns_udp_server
other_type_dns_port = _remote_dns.port
end
if remote_dns_tcp_server then
_remote_dns.address = "tcp://" .. remote_dns_tcp_server
_remote_dns.port = tonumber(remote_dns_tcp_port) or 53
other_type_dns_proto = "tcp"
other_type_dns_server = remote_dns_tcp_server
other_type_dns_port = _remote_dns.port
end
if remote_dns_doh_url and remote_dns_doh_host then
if remote_dns_doh_ip and remote_dns_doh_host ~= remote_dns_doh_ip and not api.is_ip(remote_dns_doh_host) then
dns.hosts[remote_dns_doh_host] = remote_dns_doh_ip
end
_remote_dns.address = remote_dns_doh_url
_remote_dns.port = tonumber(remote_dns_doh_port) or 443
end
table.insert(dns.servers, _remote_dns)
elseif dns_out_tag == "direct" then
dns.queryStrategy = (direct_dns_query_strategy and direct_dns_query_strategy ~= "") and direct_dns_query_strategy or "UseIP"
table.insert(outbounds, 1, {
tag = dns_out_tag,
protocol = "freedom",
settings = {
domainStrategy = dns.queryStrategy
},
streamSettings = {
sockopt = {
mark = 255
}
}
})
local _direct_dns = {
_flag = "direct"
}
if direct_dns_udp_server then
_direct_dns.address = direct_dns_udp_server
_direct_dns.port = tonumber(direct_dns_udp_port) or 53
table.insert(routing.rules, 1, {
type = "field",
ip = {
direct_dns_udp_server
},
port = tonumber(direct_dns_udp_port) or 53,
network = "udp",
outboundTag = "direct"
})
other_type_dns_proto = "udp"
other_type_dns_server = direct_dns_udp_server
other_type_dns_port = _direct_dns.port
end
if direct_dns_tcp_server then
_direct_dns.address = "tcp+local://" .. direct_dns_tcp_server
_direct_dns.port = tonumber(direct_dns_tcp_port) or 53
other_type_dns_proto = "tcp"
other_type_dns_server = direct_dns_tcp_server
other_type_dns_port = _direct_dns.port
end
if direct_dns_doh_url and direct_dns_doh_host then
if direct_dns_doh_ip and direct_dns_doh_host ~= direct_dns_doh_ip and not api.is_ip(direct_dns_doh_host) then
dns.hosts[direct_dns_doh_host] = direct_dns_doh_ip
end
_direct_dns.address = direct_dns_doh_url:gsub("https://", "https+local://")
_direct_dns.port = tonumber(direct_dns_doh_port) or 443
end
table.insert(dns.servers, _direct_dns)
end
local dns_hosts_len = 0
for key, value in pairs(dns.hosts) do
dns_hosts_len = dns_hosts_len + 1
end
if dns_hosts_len == 0 then
dns.hosts = nil
end
table.insert(inbounds, {
listen = "127.0.0.1",
port = tonumber(dns_listen_port),
protocol = "dokodemo-door",
tag = "dns-in",
settings = {
address = other_type_dns_server or "1.1.1.1",
port = other_type_dns_port or 53,
network = "tcp,udp"
}
})
table.insert(outbounds, {
tag = "dns-out",
protocol = "dns",
proxySettings = {
tag = dns_out_tag
},
settings = {
address = other_type_dns_server or "1.1.1.1",
port = other_type_dns_port or 53,
network = other_type_dns_proto or "tcp",
nonIPQuery = "drop"
}
})
table.insert(routing.rules, 1, {
type = "field",
inboundTag = {
"dns-in"
},
outboundTag = "dns-out"
})
table.insert(routing.rules, {
type = "field",
inboundTag = {
"dns-in1"
},
outboundTag = dns_out_tag
})
end
if inbounds or outbounds then
local config = {
log = {
--dnsLog = true,
loglevel = loglevel
},
-- DNS
dns = dns,
-- 传入连接
inbounds = inbounds,
-- 传出连接
outbounds = outbounds,
-- 路由
routing = routing
}
return jsonc.stringify(config, 1)
end
end
_G.gen_config = gen_config
_G.gen_proto_config = gen_proto_config
_G.gen_dns_config = gen_dns_config
if arg[1] then
local func =_G[arg[1]]
if func then
print(func(api.get_function_args(arg)))
end
end