mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-03 06:13:56 +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
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
main() {
|
||||
case "$1" in
|
||||
list)
|
||||
echo '{"statsquery":{},"statssys":{},"restartlogger":{}}'
|
||||
;;
|
||||
call)
|
||||
shift
|
||||
xray api $@
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"admin/services/xray_core": {
|
||||
"title": "Xray",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "xray/core"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [
|
||||
"luci-app-xray"
|
||||
],
|
||||
"uci": {
|
||||
"xray_core": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"admin/services/xray_preview": {
|
||||
"title": "Xray (preview)",
|
||||
"action": {
|
||||
"type": "view",
|
||||
"path": "xray/preview"
|
||||
},
|
||||
"depends": {
|
||||
"acl": [
|
||||
"luci-app-xray"
|
||||
],
|
||||
"uci": {
|
||||
"xray_core": {
|
||||
"@general[0]": {
|
||||
"preview_or_deprecated": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
include "/var/etc/xray/*.nft"
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"luci-app-xray": {
|
||||
"description": "Grant access to xray configurations",
|
||||
"read": {
|
||||
"uci": [
|
||||
"xray_core"
|
||||
]
|
||||
},
|
||||
"write": {
|
||||
"uci": [
|
||||
"xray_core"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
"use strict";
|
||||
|
||||
import { cursor } from "uci";
|
||||
|
||||
export function load_config() {
|
||||
const uci = cursor();
|
||||
uci.load("xray_core");
|
||||
return uci.get_all("xray_core") || {};
|
||||
};
|
||||
@@ -0,0 +1,225 @@
|
||||
"use strict";
|
||||
|
||||
import { reality_outbound_settings, tls_outbound_settings } from "./tls.mjs";
|
||||
|
||||
function stream_tcp_fake_http_request(server) {
|
||||
if (server["tcp_guise"] === "http") {
|
||||
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/53.0.2785.143 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"
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_tcp_fake_http_response(server) {
|
||||
if (server["tcp_guise"] === "http") {
|
||||
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"
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_tcp(server) {
|
||||
if (server["transport"] === "tcp") {
|
||||
return {
|
||||
header: {
|
||||
type: server["tcp_guise"],
|
||||
request: stream_tcp_fake_http_request(server),
|
||||
response: stream_tcp_fake_http_response(server)
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_h2(server) {
|
||||
if (server["transport"] === "h2") {
|
||||
return {
|
||||
path: server["h2_path"],
|
||||
host: server["h2_host"],
|
||||
read_idle_timeout: server["h2_health_check"] === "1" ? int(server["h2_read_idle_timeout"] || 10) : null,
|
||||
health_check_timeout: server["h2_health_check"] === "1" ? int(server["h2_health_check_timeout"] || 20) : null,
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_grpc(server) {
|
||||
if (server["transport"] === "grpc") {
|
||||
return {
|
||||
serviceName: server["grpc_service_name"],
|
||||
multiMode: server["grpc_multi_mode"] === "1",
|
||||
initial_windows_size: int(server["grpc_initial_windows_size"] || 0),
|
||||
idle_timeout: server["grpc_health_check"] === "1" ? int(server["grpc_idle_timeout"] || 10) : null,
|
||||
health_check_timeout: server["grpc_health_check"] === "1" ? int(server["grpc_health_check_timeout"] || 20) : null,
|
||||
permit_without_stream: server["grpc_health_check"] === "1" ? (server["grpc_permit_without_stream"] === "1") : null
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_ws(server) {
|
||||
if (server["transport"] === "ws") {
|
||||
let headers = null;
|
||||
if (server["ws_host"] != null) {
|
||||
headers = {
|
||||
Host: server["ws_host"]
|
||||
};
|
||||
}
|
||||
return {
|
||||
path: server["ws_path"],
|
||||
headers: headers
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_kcp(server) {
|
||||
if (server["transport"] === "mkcp") {
|
||||
let mkcp_seed = null;
|
||||
if (server["mkcp_seed"] != "") {
|
||||
mkcp_seed = server["mkcp_seed"];
|
||||
}
|
||||
return {
|
||||
mtu: int(server["mkcp_mtu"] || 1350),
|
||||
tti: int(server["mkcp_tti"] || 50),
|
||||
uplinkCapacity: int(server["mkcp_uplink_capacity"] || 5),
|
||||
downlinkCapacity: int(server["mkcp_downlink_capacity"] || 20),
|
||||
congestion: server["mkcp_congestion"] === "1",
|
||||
readBufferSize: int(server["mkcp_read_buffer_size"] || 2),
|
||||
writeBufferSize: int(server["mkcp_write_buffer_size"] || 2),
|
||||
seed: mkcp_seed,
|
||||
header: {
|
||||
type: server["mkcp_guise"] || "none"
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_quic(server) {
|
||||
if (server["transport"] === "quic") {
|
||||
return {
|
||||
security: server["quic_security"],
|
||||
key: server["quic_key"],
|
||||
header: {
|
||||
type: server["quic_guise"]
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_splithttp(server) {
|
||||
if (server["transport"] === "splithttp") {
|
||||
return {
|
||||
path: server["splithttp_path"],
|
||||
host: server["splithttp_host"],
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_httpupgrade(server) {
|
||||
if (server["transport"] === "httpupgrade") {
|
||||
return {
|
||||
path: server["httpupgrade_path"],
|
||||
host: server["httpupgrade_host"],
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function stream_hysteria(server) {
|
||||
if (server["transport"] === "hysteria") {
|
||||
let result = {
|
||||
version: 2,
|
||||
auth: server["password"],
|
||||
};
|
||||
let hysteria_up = server["hysteria_up_mbps"] || "";
|
||||
if (hysteria_up !== "") {
|
||||
result["up"] = `${hysteria_up}mbps`;
|
||||
}
|
||||
let hysteria_down = server["hysteria_down_mbps"] || "";
|
||||
if (hysteria_down !== "") {
|
||||
result["down"] = `${hysteria_down}mbps`;
|
||||
}
|
||||
let udphop_port = server["hysteria_udphop_port"] || "";
|
||||
if (udphop_port !== "") {
|
||||
result["udphop"] = {
|
||||
port: udphop_port,
|
||||
interval: int(server["hysteria_udphop_interval"] || 30)
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function port_array(i) {
|
||||
if (type(i) === 'array') {
|
||||
return map(i, v => int(v));
|
||||
}
|
||||
return [int(i)];
|
||||
};
|
||||
|
||||
export function stream_settings(server, protocol, tag) {
|
||||
const security = server[protocol + "_tls"];
|
||||
let tlsSettings = null;
|
||||
let realitySettings = null;
|
||||
if (security === "tls") {
|
||||
tlsSettings = tls_outbound_settings(server, protocol);
|
||||
} else if (security === "reality") {
|
||||
realitySettings = reality_outbound_settings(server, protocol);
|
||||
}
|
||||
|
||||
let dialer_proxy = null;
|
||||
let dialer_proxy_tag = null;
|
||||
if (server["dialer_proxy"] != null && server["dialer_proxy"] != "disabled") {
|
||||
dialer_proxy = server["dialer_proxy"];
|
||||
dialer_proxy_tag = tag + `@dialer_proxy:${dialer_proxy}`;
|
||||
}
|
||||
return {
|
||||
stream_settings: {
|
||||
network: server["transport"],
|
||||
sockopt: {
|
||||
domainStrategy: server["domain_strategy"] || "UseIP",
|
||||
dialerProxy: dialer_proxy_tag
|
||||
},
|
||||
security: security,
|
||||
tlsSettings: tlsSettings,
|
||||
realitySettings: realitySettings,
|
||||
quicSettings: stream_quic(server),
|
||||
tcpSettings: stream_tcp(server),
|
||||
kcpSettings: stream_kcp(server),
|
||||
wsSettings: stream_ws(server),
|
||||
grpcSettings: stream_grpc(server),
|
||||
httpSettings: stream_h2(server),
|
||||
splithttpSettings: stream_splithttp(server),
|
||||
httpupgradeSettings: stream_httpupgrade(server),
|
||||
hysteriaSettings: stream_hysteria(server)
|
||||
},
|
||||
dialer_proxy: dialer_proxy
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
|
||||
export function fallbacks(proxy, config) {
|
||||
const fallback = filter(keys(config), k => config[k][".type"] == "fallback") || [];
|
||||
let f = [];
|
||||
for (let key in fallback) {
|
||||
const s = config[key];
|
||||
if (s["dest"] != null) {
|
||||
push(f, {
|
||||
dest: s["dest"],
|
||||
alpn: s["alpn"],
|
||||
name: s["name"],
|
||||
xver: s["xver"],
|
||||
path: s["path"]
|
||||
});
|
||||
}
|
||||
}
|
||||
push(f, {
|
||||
dest: proxy["web_server_address"]
|
||||
});
|
||||
return f;
|
||||
};
|
||||
|
||||
export function tls_outbound_settings(server, protocol) {
|
||||
let result = {
|
||||
serverName: server[protocol + "_tls_host"],
|
||||
allowInsecure: server[protocol + "_tls_insecure"] != "0",
|
||||
fingerprint: server[protocol + "_tls_fingerprint"] || ""
|
||||
};
|
||||
|
||||
if (server[protocol + "_tls_alpn"] != null) {
|
||||
result["alpn"] = server[protocol + "_tls_alpn"];
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export function tls_inbound_settings(proxy, protocol_name) {
|
||||
let wscert = proxy[protocol_name + "_tls_cert_file"];
|
||||
if (wscert == null) {
|
||||
wscert = proxy["web_server_cert_file"];
|
||||
}
|
||||
let wskey = proxy[protocol_name + "_tls_key_file"];
|
||||
if (wskey == null) {
|
||||
wskey = proxy["web_server_key_file"];
|
||||
}
|
||||
return {
|
||||
alpn: [
|
||||
"http/1.1"
|
||||
],
|
||||
certificates: [
|
||||
{
|
||||
certificateFile: wscert,
|
||||
keyFile: wskey
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
export function reality_outbound_settings(server, protocol) {
|
||||
let result = {
|
||||
show: server[protocol + "_reality_show"] === "1",
|
||||
fingerprint: server[protocol + "_reality_fingerprint"],
|
||||
serverName: server[protocol + "_reality_server_name"],
|
||||
publicKey: server[protocol + "_reality_public_key"],
|
||||
shortId: server[protocol + "_reality_short_id"],
|
||||
spiderX: server[protocol + "_reality_spider_x"],
|
||||
};
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export function reality_inbound_settings(proxy, protocol_name) {
|
||||
return {
|
||||
show: proxy[protocol_name + "_reality_show"],
|
||||
dest: proxy[protocol_name + "_reality_dest"],
|
||||
xver: proxy[protocol_name + "_reality_xver"],
|
||||
serverNames: proxy[protocol_name + "_reality_server_names"],
|
||||
privateKey: proxy[protocol_name + "_reality_private_key"],
|
||||
minClientVer: proxy[protocol_name + "_reality_min_client_ver"],
|
||||
maxClientVer: proxy[protocol_name + "_reality_max_client_ver"],
|
||||
maxTimeDiff: proxy[protocol_name + "_reality_max_time_diff"],
|
||||
shortIds: proxy[protocol_name + "_reality_short_ids"],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/ucode
|
||||
"use strict";
|
||||
|
||||
import { open, popen, stat } from "fs";
|
||||
import { connect } from "ubus";
|
||||
|
||||
function network_dump() {
|
||||
const ubus = connect();
|
||||
if (ubus) {
|
||||
const result = ubus.call("network.interface", "dump");
|
||||
ubus.disconnect();
|
||||
return result;
|
||||
}
|
||||
return {
|
||||
"interface": []
|
||||
};
|
||||
}
|
||||
|
||||
function get_default_gateway(dump) {
|
||||
let dgs = {};
|
||||
for (let i in dump["interface"] || []) {
|
||||
for (let j in i["route"] || []) {
|
||||
if (j["target"] == "0.0.0.0") {
|
||||
dgs[j["nexthop"]] = true;
|
||||
if (j["source"] != "0.0.0.0/0") {
|
||||
dgs[j["source"]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return keys(dgs);
|
||||
}
|
||||
|
||||
function get_prefix_delegate(dump) {
|
||||
let pds = {};
|
||||
for (let i in dump["interface"] || []) {
|
||||
for (let j in i["ipv6-prefix"] || []) {
|
||||
if (j["assigned"]) {
|
||||
pds[`${j["address"]}/${j["mask"]}`] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return keys(pds);
|
||||
}
|
||||
|
||||
function gen_tp_spec_dv4_dg(dg) {
|
||||
if (stat("/usr/share/xray/ignore_tp_spec_def_gw")) {
|
||||
return "";
|
||||
}
|
||||
if (length(dg) > 0) {
|
||||
return `set tp_spec_dv4_dg {
|
||||
type ipv4_addr
|
||||
size 16
|
||||
flags interval
|
||||
elements = { ${join(", ", dg)} }
|
||||
}\n`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function gen_tp_spec_dv6_dg(pd) {
|
||||
if (length(pd) > 0) {
|
||||
return `set tp_spec_dv6_dg {
|
||||
type ipv6_addr
|
||||
size 16
|
||||
flags interval
|
||||
elements = { ${join(", ", pd)} }
|
||||
}\n`;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function generate_include(rule_dg, rule_pd, file_path) {
|
||||
const handle = open(file_path, "w");
|
||||
handle.write(rule_dg);
|
||||
handle.write(rule_pd);
|
||||
handle.flush();
|
||||
handle.close();
|
||||
}
|
||||
|
||||
function update_nft(rule_dg, rule_pd) {
|
||||
const handle = popen("nft -f -", "w");
|
||||
handle.write(`table inet fw4 {
|
||||
${rule_dg}
|
||||
${rule_pd}
|
||||
}`);
|
||||
handle.flush();
|
||||
handle.close();
|
||||
}
|
||||
|
||||
function restart_dnsmasq_if_necessary() {
|
||||
if (stat("/usr/share/xray/restart_dnsmasq_on_iface_change")) {
|
||||
system("service dnsmasq restart");
|
||||
}
|
||||
}
|
||||
|
||||
const dump = network_dump();
|
||||
const dg = get_default_gateway(dump);
|
||||
const pd = get_prefix_delegate(dump);
|
||||
const log = join(", ", [...dg, ...pd]);
|
||||
if (log == "") {
|
||||
print("default gateway not available, please wait for interface ready");
|
||||
} else {
|
||||
print(`default gateway available at ${log}\n`);
|
||||
const rule_dg = gen_tp_spec_dv4_dg(dg);
|
||||
const rule_pd = gen_tp_spec_dv6_dg(pd);
|
||||
update_nft(rule_dg, rule_pd);
|
||||
generate_include(rule_dg, rule_pd, "/var/etc/xray/02_default_gateway_include.nft");
|
||||
}
|
||||
restart_dnsmasq_if_necessary();
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/utpl
|
||||
{%
|
||||
"use strict";
|
||||
import { load_config } from "./common/config.mjs";
|
||||
const config = load_config();
|
||||
const general = config[filter(keys(config), k => config[k][".type"] == "general")[0]];
|
||||
const dns_port = int(general["dns_port"] || 5300);
|
||||
const dns_count = int(general["dns_count"] || 3);
|
||||
const manual_tproxy = filter(keys(config), k => config[k][".type"] == "manual_tproxy") || [];
|
||||
%}
|
||||
# Generated dnsmasq configurations by luci-app-xray
|
||||
strict-order
|
||||
server=/#/127.0.0.1#{{ dns_port }}
|
||||
{% for (let i = dns_port; i <= dns_port + dns_count; i++): %}
|
||||
server=127.0.0.1#{{ i }}
|
||||
{% endfor %}
|
||||
{% for (let i in manual_tproxy): %}
|
||||
{% if (config[i]["rebind_domain_ok"] == "1"): %}
|
||||
{% for (let j in config[i]["domain_names"]): %}
|
||||
rebind-domain-ok={{ j }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if (general["blocked_to_loopback"] != "1"): %}
|
||||
bogus-nxdomain=127.127.127.127
|
||||
bogus-nxdomain=100::6c62:636f:656b:2164
|
||||
{% endif %}
|
||||
@@ -0,0 +1,46 @@
|
||||
"use strict";
|
||||
|
||||
import { server_outbound } from "./outbound.mjs";
|
||||
|
||||
export function bridges(bridge) {
|
||||
let result = [];
|
||||
for (let v in bridge) {
|
||||
push(result, {
|
||||
tag: sprintf("bridge_inbound:%s", v[".name"]),
|
||||
domain: v["domain"]
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function bridge_outbounds(config, bridge) {
|
||||
let result = [];
|
||||
for (let v in bridge) {
|
||||
const bridge_server = config[v["upstream"]];
|
||||
push(result, {
|
||||
tag: sprintf("bridge_freedom_outbound:%s", v[".name"]),
|
||||
protocol: "freedom",
|
||||
settings: {
|
||||
redirect: v["redirect"]
|
||||
}
|
||||
}, ...server_outbound(bridge_server, sprintf("bridge_upstream_outbound:%s", v[".name"]), config));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function bridge_rules(bridge) {
|
||||
let result = [];
|
||||
for (let v in bridge) {
|
||||
push(result, {
|
||||
type: "field",
|
||||
inboundTag: [sprintf("bridge_inbound:%s", v[".name"])],
|
||||
domain: [sprintf("full:%s", v["domain"])],
|
||||
outboundTag: sprintf("bridge_upstream_outbound:%s", v[".name"])
|
||||
}, {
|
||||
type: "field",
|
||||
inboundTag: [sprintf("bridge_inbound:%s", v[".name"])],
|
||||
outboundTag: sprintf("bridge_freedom_outbound:%s", v[".name"])
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,289 @@
|
||||
"use strict";
|
||||
|
||||
import { access } from "fs";
|
||||
import { fake_dns_domains } from "./fake_dns.mjs";
|
||||
import { direct_outbound } from "./outbound.mjs";
|
||||
|
||||
const fallback_fast_dns = "223.5.5.5:53";
|
||||
const fallback_secure_dns = "8.8.8.8:53";
|
||||
const fallback_default_dns = "1.1.1.1:53";
|
||||
const geoip_existence = access("/usr/share/xray/geoip.dat") || false;
|
||||
const geosite_existence = access("/usr/share/xray/geosite.dat") || false;
|
||||
|
||||
function parse_ip_port(val, port_default) {
|
||||
const split_dot = split(val, ".");
|
||||
if (length(split_dot) > 1) {
|
||||
const split_ipv4 = split(val, ":");
|
||||
return {
|
||||
ip: split_ipv4[0],
|
||||
port: int(split_ipv4[1])
|
||||
};
|
||||
}
|
||||
const split_ipv6_port = split(val, "]:");
|
||||
if (length(split_ipv6_port) == 2) {
|
||||
return {
|
||||
ip: ltrim(split_ipv6_port[0], "["),
|
||||
port: int(split_ipv6_port[1]),
|
||||
};
|
||||
}
|
||||
return {
|
||||
ip: val,
|
||||
port: port_default
|
||||
};
|
||||
}
|
||||
|
||||
function format_dns(method, val) {
|
||||
const parsed = parse_ip_port(val, 53);
|
||||
if (method === "udp") {
|
||||
return {
|
||||
address: parsed["ip"],
|
||||
port: parsed["port"]
|
||||
};
|
||||
}
|
||||
let url_suffix = "";
|
||||
if (substr(method, 0, 5) === "https") {
|
||||
url_suffix = "/dns-query";
|
||||
}
|
||||
return {
|
||||
address: `${method}://${val}${url_suffix}`
|
||||
};
|
||||
}
|
||||
|
||||
function domain_rules(proxy, k) {
|
||||
if (proxy[k] === null) {
|
||||
return [];
|
||||
}
|
||||
return filter(proxy[k], function (x) {
|
||||
if (substr(x, 0, 8) === "geosite:") {
|
||||
return geosite_existence;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
export function secure_domain_rules(proxy) {
|
||||
return domain_rules(proxy, "forwarded_domain_rules");
|
||||
};
|
||||
|
||||
export function fast_domain_rules(proxy) {
|
||||
return domain_rules(proxy, "bypassed_domain_rules");
|
||||
};
|
||||
|
||||
export function blocked_domain_rules(proxy) {
|
||||
return domain_rules(proxy, "blocked_domain_rules");
|
||||
};
|
||||
|
||||
export function dns_server_inbounds(proxy) {
|
||||
let result = [];
|
||||
const dns_port = int(proxy["dns_port"] || 5300);
|
||||
const dns_count = int(proxy["dns_count"] || 3);
|
||||
const default_dns = format_dns("udp", proxy["default_dns"] || fallback_default_dns);
|
||||
for (let i = dns_port; i <= dns_port + dns_count; i++) {
|
||||
push(result, {
|
||||
port: i,
|
||||
protocol: "dokodemo-door",
|
||||
tag: sprintf("dns_server_inbound:%d", i),
|
||||
settings: {
|
||||
address: default_dns["address"],
|
||||
port: default_dns["port"],
|
||||
network: "tcp,udp"
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function dns_rules(proxy, tcp_hijack_inbound_tags, udp_hijack_inbound_tags) {
|
||||
const dns_port = int(proxy["dns_port"] || 5300);
|
||||
const dns_count = int(proxy["dns_count"] || 3);
|
||||
const fast_dns = parse_ip_port(proxy["fast_dns"] || fallback_fast_dns, 53);
|
||||
const secure_dns = parse_ip_port(proxy["secure_dns"] || fallback_secure_dns, 53);
|
||||
let dns_server_tags = [];
|
||||
for (let i = dns_port; i <= dns_port + dns_count; i++) {
|
||||
push(dns_server_tags, sprintf("dns_server_inbound:%d", i));
|
||||
}
|
||||
let result = [
|
||||
{
|
||||
type: "field",
|
||||
inboundTag: ["dns_conf_inbound"],
|
||||
ip: [fast_dns["ip"]],
|
||||
port: `${fast_dns["port"]}`,
|
||||
outboundTag: "dynamic_direct"
|
||||
},
|
||||
{
|
||||
type: "field",
|
||||
inboundTag: ["dns_conf_inbound"],
|
||||
ip: [secure_dns["ip"]],
|
||||
port: `${secure_dns["port"]}`,
|
||||
balancerTag: "udp_outbound_v4"
|
||||
},
|
||||
{
|
||||
type: "field",
|
||||
inboundTag: dns_server_tags,
|
||||
outboundTag: "dns_server_outbound"
|
||||
},
|
||||
];
|
||||
if (proxy.dns_tcp_hijack) {
|
||||
push(result, {
|
||||
type: "field",
|
||||
port: "53",
|
||||
inboundTag: tcp_hijack_inbound_tags,
|
||||
outboundTag: "dns_tcp_hijack_outbound"
|
||||
});
|
||||
}
|
||||
if (proxy.dns_udp_hijack) {
|
||||
push(result, {
|
||||
type: "field",
|
||||
port: "53",
|
||||
inboundTag: udp_hijack_inbound_tags,
|
||||
outboundTag: "dns_udp_hijack_outbound"
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function dns_server_outbounds(proxy) {
|
||||
let result = [
|
||||
{
|
||||
protocol: "dns",
|
||||
settings: {
|
||||
nonIPQuery: "skip"
|
||||
},
|
||||
streamSettings: {
|
||||
sockopt: {
|
||||
mark: 254
|
||||
}
|
||||
},
|
||||
tag: "dns_server_outbound"
|
||||
}
|
||||
];
|
||||
if (proxy.dns_tcp_hijack) {
|
||||
push(result, direct_outbound("dns_tcp_hijack_outbound", proxy.dns_tcp_hijack, false));
|
||||
}
|
||||
if (proxy.dns_udp_hijack) {
|
||||
push(result, direct_outbound("dns_udp_hijack_outbound", proxy.dns_udp_hijack, false));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function dns_conf(proxy, config, manual_tproxy, fakedns) {
|
||||
const fast_dns_object = format_dns("udp", proxy["fast_dns"] || fallback_fast_dns);
|
||||
const default_dns_object = format_dns("udp", proxy["default_dns"] || fallback_default_dns);
|
||||
|
||||
let domain_names_set = {};
|
||||
let domain_extra_options = {};
|
||||
|
||||
for (let server in filter(values(config), i => i[".type"] === "servers")) {
|
||||
if (iptoarr(server["server"])) {
|
||||
continue;
|
||||
}
|
||||
if (server["domain_resolve_dns"]) {
|
||||
domain_extra_options[server["server"]] = `${server["domain_resolve_dns_method"] || "udp"};${server["domain_resolve_dns"]};${join(",", server["domain_resolve_expect_ips"] || [])}`;
|
||||
} else {
|
||||
domain_names_set[`domain:${server["server"]}`] = true;
|
||||
}
|
||||
}
|
||||
|
||||
let resolve_merged = {};
|
||||
for (let k in keys(domain_extra_options)) {
|
||||
const v = domain_extra_options[k];
|
||||
let original = resolve_merged[v] || [];
|
||||
push(original, `domain:${k}`);
|
||||
resolve_merged[v] = original;
|
||||
}
|
||||
|
||||
let servers = [
|
||||
...fake_dns_domains(fakedns),
|
||||
...map(keys(resolve_merged), function (k) {
|
||||
const dns_split = split(k, ";");
|
||||
const resolve_dns_object = format_dns(dns_split[0], dns_split[1]);
|
||||
let result = {
|
||||
address: resolve_dns_object["address"],
|
||||
port: resolve_dns_object["port"],
|
||||
domains: uniq(resolve_merged[k]),
|
||||
skipFallback: true,
|
||||
};
|
||||
if (length(dns_split[2]) > 0) {
|
||||
const expect_ips = filter(split(dns_split[2], ",") || [], function (i) {
|
||||
if (!geoip_existence) {
|
||||
if (substr(i, 0, 6) === "geoip:") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
result["expectIPs"] = expect_ips;
|
||||
}
|
||||
return result;
|
||||
}),
|
||||
default_dns_object,
|
||||
{
|
||||
address: fast_dns_object["address"],
|
||||
port: fast_dns_object["port"],
|
||||
domains: [...keys(domain_names_set), ...fast_domain_rules(proxy)],
|
||||
skipFallback: true,
|
||||
expectIPs: function () {
|
||||
let ips = [];
|
||||
if (proxy["align_fast_dns_to_geoip_direct"] === "1") {
|
||||
if (geoip_existence) {
|
||||
const geoip_direct_code_list = map(proxy["geoip_direct_code_list"] || [], v => index(v, ":") > 0 ? v : `geoip:${v}`);
|
||||
const geoip_direct_code_list_v6 = map(proxy["geoip_direct_code_list_v6"] || [], v => index(v, ":") > 0 ? v : `geoip:${v}`);
|
||||
push(ips, ...geoip_direct_code_list, ...geoip_direct_code_list_v6);
|
||||
}
|
||||
}
|
||||
if (length(ips) > 0) {
|
||||
return uniq(ips);
|
||||
}
|
||||
return null;
|
||||
}(),
|
||||
},
|
||||
];
|
||||
|
||||
if (length(secure_domain_rules(proxy)) > 0) {
|
||||
const secure_dns_object = format_dns("udp", proxy["secure_dns"] || fallback_secure_dns);
|
||||
push(servers, {
|
||||
address: secure_dns_object["address"],
|
||||
port: secure_dns_object["port"],
|
||||
domains: secure_domain_rules(proxy),
|
||||
});
|
||||
}
|
||||
|
||||
let hosts = {};
|
||||
if (length(blocked_domain_rules(proxy)) > 0) {
|
||||
for (let rule in (blocked_domain_rules(proxy))) {
|
||||
hosts[rule] = ["127.127.127.127", "100::6c62:636f:656b:2164"]; // blocked!
|
||||
}
|
||||
}
|
||||
|
||||
for (let v in manual_tproxy) {
|
||||
if (v.domain_names != null) {
|
||||
for (let d in v.domain_names) {
|
||||
if (index(v.source_addr, ":") === -1) {
|
||||
hosts[d] = [v.source_addr];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hosts: hosts,
|
||||
servers: servers,
|
||||
tag: "dns_conf_inbound",
|
||||
queryStrategy: "UseIP"
|
||||
};
|
||||
};
|
||||
|
||||
export function dns_direct_servers(config) {
|
||||
let result = [];
|
||||
for (let server in filter(values(config), i => i[".type"] === "servers")) {
|
||||
if (iptoarr(server["server"])) {
|
||||
continue;
|
||||
}
|
||||
if (server["domain_resolve_dns"]) {
|
||||
if (index(server["domain_resolve_dns_method"], "local") > 1) {
|
||||
push(result, parse_ip_port(server["domain_resolve_dns"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
|
||||
import { balancer } from "./system.mjs";
|
||||
|
||||
export function fake_dns_domains(fakedns) {
|
||||
let domains = [];
|
||||
for (let f in fakedns) {
|
||||
push(domains, ...f["fake_dns_domain_names"]);
|
||||
}
|
||||
if (length(domains) === 0) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
{
|
||||
"address": "fakedns",
|
||||
"domains": domains,
|
||||
"skipFallback": true
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
export function fake_dns_rules(fakedns) {
|
||||
let result = [];
|
||||
for (let f in fakedns) {
|
||||
push(result, {
|
||||
type: "field",
|
||||
inboundTag: ["tproxy_tcp_inbound_f4", "tproxy_tcp_inbound_f6"],
|
||||
domain: f["fake_dns_domain_names"],
|
||||
balancerTag: `fake_dns_balancer:${f[".name"]}@tcp_balancer`
|
||||
}, {
|
||||
type: "field",
|
||||
inboundTag: ["tproxy_udp_inbound_f4", "tproxy_udp_inbound_f6"],
|
||||
domain: f["fake_dns_domain_names"],
|
||||
balancerTag: `fake_dns_balancer:${f[".name"]}@udp_balancer`
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function fake_dns_balancers(fakedns) {
|
||||
let result = [];
|
||||
for (let f in fakedns) {
|
||||
push(result, {
|
||||
"tag": `fake_dns_balancer:${f[".name"]}@tcp_balancer`,
|
||||
"selector": balancer(f, "fake_dns_forward_server_tcp", `fake_dns_tcp:${f[".name"]}`),
|
||||
"strategy": {
|
||||
"type": f["fake_dns_balancer_strategy"] || "random"
|
||||
}
|
||||
}, {
|
||||
"tag": `fake_dns_balancer:${f[".name"]}@udp_balancer`,
|
||||
"selector": balancer(f, "fake_dns_forward_server_udp", `fake_dns_udp:${f[".name"]}`),
|
||||
"strategy": {
|
||||
"type": f["fake_dns_balancer_strategy"] || "random"
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function fake_dns_conf(proxy) {
|
||||
return [
|
||||
{
|
||||
"ipPool": proxy.pool_v4 || "198.18.0.0/15",
|
||||
"poolSize": int(proxy.pool_v4_size) || 65535
|
||||
},
|
||||
{
|
||||
"ipPool": proxy.pool_v6 || "fc00::/18",
|
||||
"poolSize": int(proxy.pool_v6_size) || 65535
|
||||
}
|
||||
];
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
"use strict";
|
||||
|
||||
import { https_trojan_inbound } from "../protocol/trojan.mjs";
|
||||
import { https_vless_inbound } from "../protocol/vless.mjs";
|
||||
import { balancer } from "./system.mjs";
|
||||
|
||||
export function dokodemo_inbound(listen, port, tag, sniffing, sniffing_route_only, sniffing_dest_override, sniffing_metadata_only, network, tproxy, timeout) {
|
||||
let result = {
|
||||
port: int(port),
|
||||
protocol: "dokodemo-door",
|
||||
tag: tag,
|
||||
sniffing: sniffing == "1" ? {
|
||||
enabled: true,
|
||||
routeOnly: sniffing_route_only == "1",
|
||||
destOverride: sniffing_dest_override,
|
||||
metadataOnly: sniffing_metadata_only == "1"
|
||||
} : null,
|
||||
settings: {
|
||||
network: network,
|
||||
followRedirect: true,
|
||||
timeout: int(timeout || 300),
|
||||
},
|
||||
streamSettings: {
|
||||
sockopt: {
|
||||
tproxy: tproxy
|
||||
}
|
||||
}
|
||||
};
|
||||
if (listen) {
|
||||
result["listen"] = listen;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function http_inbound(addr, port, tag, username, password) {
|
||||
let accounts = null;
|
||||
if (username && password) {
|
||||
accounts = [
|
||||
{
|
||||
"user": username,
|
||||
"pass": password
|
||||
}
|
||||
];
|
||||
}
|
||||
return {
|
||||
listen: addr || "0.0.0.0",
|
||||
port: port,
|
||||
protocol: "http",
|
||||
tag: tag,
|
||||
settings: {
|
||||
accounts: accounts,
|
||||
allowTransparent: false
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export function socks_inbound(addr, port, tag, username, password) {
|
||||
let auth = "noauth";
|
||||
let accounts = null;
|
||||
if (username && password) {
|
||||
auth = "password";
|
||||
accounts = [
|
||||
{
|
||||
"user": username,
|
||||
"pass": password
|
||||
}
|
||||
];
|
||||
}
|
||||
return {
|
||||
listen: addr || "0.0.0.0",
|
||||
port: port,
|
||||
protocol: "socks",
|
||||
tag: tag,
|
||||
settings: {
|
||||
auth: auth,
|
||||
accounts: accounts,
|
||||
udp: true
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export function https_inbound(proxy, config) {
|
||||
if (proxy["web_server_protocol"] == "vless") {
|
||||
return https_vless_inbound(proxy, config);
|
||||
}
|
||||
if (proxy["web_server_protocol"] == "trojan") {
|
||||
return https_trojan_inbound(proxy, config);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export function extra_inbounds(proxy, extra_inbound) {
|
||||
let result = [];
|
||||
for (let v in extra_inbound) {
|
||||
const tag = `extra_inbound:${v[".name"]}`;
|
||||
if (v["inbound_type"] == "http") {
|
||||
push(result, http_inbound(v["inbound_addr"] || "0.0.0.0", v["inbound_port"], tag, v["inbound_username"], v["inbound_password"]));
|
||||
} else if (v["inbound_type"] == "socks5") {
|
||||
push(result, socks_inbound(v["inbound_addr"] || "0.0.0.0", v["inbound_port"], tag, v["inbound_username"], v["inbound_password"]));
|
||||
} else if (v["inbound_type"] == "tproxy_tcp") {
|
||||
push(result, dokodemo_inbound(v["inbound_addr"] || "0.0.0.0", v["inbound_port"], tag, proxy["tproxy_sniffing"], proxy["route_only"], ["http", "tls"], "0", "tcp", "tproxy"));
|
||||
} else if (v["inbound_type"] == "tproxy_udp") {
|
||||
push(result, dokodemo_inbound(v["inbound_addr"] || "0.0.0.0", v["inbound_port"], tag, proxy["tproxy_sniffing"], proxy["route_only"], ["quic"], "0", "udp", "tproxy"));
|
||||
} else {
|
||||
die(`unknown inbound type ${v["inbound_type"]}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function extra_inbound_rules(extra_inbound) {
|
||||
let result = [];
|
||||
for (let v in extra_inbound) {
|
||||
if (v["specify_outbound"] == "1") {
|
||||
push(result, {
|
||||
type: "field",
|
||||
inboundTag: [`extra_inbound:${v[".name"]}`],
|
||||
balancerTag: `extra_inbound_outbound:${v[".name"]}`
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function extra_inbound_balancers(extra_inbound) {
|
||||
let result = [];
|
||||
for (let e in extra_inbound) {
|
||||
if (e["specify_outbound"] == "1") {
|
||||
push(result, {
|
||||
"tag": `extra_inbound_outbound:${e[".name"]}`,
|
||||
"selector": balancer(e, "destination", `extra_inbound:${e[".name"]}`),
|
||||
"strategy": {
|
||||
"type": e["balancer_strategy"] || "random"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function extra_inbound_global(extra_inbound) {
|
||||
const global_tags = filter(extra_inbound, v => v["specify_outbound"] != "1");
|
||||
return {
|
||||
"tproxy_tcp": map(filter(global_tags, v => v["inbound_type"] == "tproxy_tcp"), v => `extra_inbound_${v[".name"]}`),
|
||||
"tproxy_udp": map(filter(global_tags, v => v["inbound_type"] == "tproxy_udp"), v => `extra_inbound_${v[".name"]}`),
|
||||
"http": map(filter(global_tags, v => v["inbound_type"] == "http"), v => `extra_inbound_${v[".name"]}`),
|
||||
"socks5": map(filter(global_tags, v => v["inbound_type"] == "socks5"), v => `extra_inbound_${v[".name"]}`),
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
"use strict";
|
||||
|
||||
import { server_outbound } from "./outbound.mjs";
|
||||
|
||||
export function manual_tproxy_outbounds(config, manual_tproxy) {
|
||||
let result = [];
|
||||
for (let v in manual_tproxy) {
|
||||
let tcp_tag = "direct";
|
||||
if (v["force_forward_tcp"] === "1") {
|
||||
if (v["force_forward_server_tcp"] != null) {
|
||||
tcp_tag = `manual_tproxy:${v[".name"]}@tcp_outbound@force_forward:${v["force_forward_server_tcp"]}`;
|
||||
const force_forward_server_tcp = config[v["force_forward_server_tcp"]];
|
||||
push(result, ...server_outbound(force_forward_server_tcp, tcp_tag, config));
|
||||
}
|
||||
}
|
||||
push(result, {
|
||||
protocol: "freedom",
|
||||
tag: sprintf("manual_tproxy:%s@tcp_outbound", v[".name"]),
|
||||
settings: {
|
||||
redirect: sprintf("%s:%d", v["dest_addr"] || "", v["dest_port"] || 0),
|
||||
domainStrategy: "AsIs"
|
||||
},
|
||||
proxySettings: {
|
||||
tag: tcp_tag
|
||||
}
|
||||
});
|
||||
|
||||
let udp_tag = "direct";
|
||||
if (v["force_forward_udp"] === "1") {
|
||||
if (v["force_forward_server_udp"] != null) {
|
||||
udp_tag = `manual_tproxy:${v[".name"]}@udp_outbound@force_forward:${v["force_forward_server_udp"]}`;
|
||||
const force_forward_server_udp = config[v["force_forward_server_udp"]];
|
||||
push(result, ...server_outbound(force_forward_server_udp, udp_tag, config));
|
||||
}
|
||||
}
|
||||
push(result, {
|
||||
protocol: "freedom",
|
||||
tag: sprintf("manual_tproxy:%s@udp_outbound", v[".name"]),
|
||||
settings: {
|
||||
redirect: sprintf("%s:%d", v["dest_addr"] || "", v["dest_port"] || 0),
|
||||
domainStrategy: "AsIs"
|
||||
},
|
||||
proxySettings: {
|
||||
tag: udp_tag
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function manual_tproxy_outbound_tags(manual_tproxy) {
|
||||
let result = [];
|
||||
for (let v in manual_tproxy) {
|
||||
if (v["force_forward_tcp"] === "1") {
|
||||
push(result, `manual_tproxy:${v[".name"]}@tcp_outbound@force_forward:${v["force_forward_server_tcp"]}`);
|
||||
}
|
||||
if (v["force_forward_udp"] === "1") {
|
||||
push(result, `manual_tproxy:${v[".name"]}@udp_outbound@force_forward:${v["force_forward_server_udp"]}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export function manual_tproxy_rules(manual_tproxy) {
|
||||
let result = [];
|
||||
for (let v in manual_tproxy) {
|
||||
splice(result, 0, 0, {
|
||||
type: "field",
|
||||
inboundTag: ["tproxy_tcp_inbound_v4", "tproxy_tcp_inbound_v6", "socks_inbound", "https_inbound", "http_inbound"],
|
||||
ip: [v["source_addr"]],
|
||||
port: v["source_port"],
|
||||
outboundTag: sprintf("manual_tproxy:%s@tcp_outbound", v[".name"])
|
||||
});
|
||||
splice(result, 0, 0, {
|
||||
type: "field",
|
||||
inboundTag: ["tproxy_udp_inbound_v4", "tproxy_udp_inbound_v6"],
|
||||
ip: [v["source_addr"]],
|
||||
port: v["source_port"],
|
||||
outboundTag: sprintf("manual_tproxy:%s@udp_outbound", v[".name"])
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,100 @@
|
||||
"use strict";
|
||||
|
||||
import { http_outbound } from "../protocol/http.mjs";
|
||||
import { shadowsocks_outbound } from "../protocol/shadowsocks.mjs";
|
||||
import { socks_outbound } from "../protocol/socks.mjs";
|
||||
import { trojan_outbound } from "../protocol/trojan.mjs";
|
||||
import { vless_outbound } from "../protocol/vless.mjs";
|
||||
import { vmess_outbound } from "../protocol/vmess.mjs";
|
||||
import { hysteria_outbound } from "../protocol/hysteria.mjs";
|
||||
|
||||
const direct_mark = 252;
|
||||
const outbound_mark = 253;
|
||||
|
||||
function override_custom_config_recursive(x, y) {
|
||||
if (type(x) != "object" || type(y) != "object") {
|
||||
return y;
|
||||
}
|
||||
for (let k in y) {
|
||||
x[k] = override_custom_config_recursive(x[k], y[k]);
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function server_outbound_recursive(t, server, tag, config) {
|
||||
let outbound_result = null;
|
||||
if (server["protocol"] == "vmess") {
|
||||
outbound_result = vmess_outbound(server, tag);
|
||||
} else if (server["protocol"] == "vless") {
|
||||
outbound_result = vless_outbound(server, tag);
|
||||
} else if (server["protocol"] == "shadowsocks") {
|
||||
outbound_result = shadowsocks_outbound(server, tag);
|
||||
} else if (server["protocol"] == "trojan") {
|
||||
outbound_result = trojan_outbound(server, tag);
|
||||
} else if (server["protocol"] == "http") {
|
||||
outbound_result = http_outbound(server, tag);
|
||||
} else if (server["protocol"] == "socks") {
|
||||
outbound_result = socks_outbound(server, tag);
|
||||
} else if (server["protocol"] == "hysteria") {
|
||||
outbound_result = hysteria_outbound(server, tag);
|
||||
}
|
||||
if (outbound_result == null) {
|
||||
die(`unknown outbound server protocol ${server["protocol"]}`);
|
||||
}
|
||||
let outbound = outbound_result["outbound"];
|
||||
const custom_config_outbound_string = server["custom_config"];
|
||||
|
||||
if (custom_config_outbound_string != null && custom_config_outbound_string != "") {
|
||||
const custom_config_outbound = json(custom_config_outbound_string);
|
||||
for (let k in custom_config_outbound) {
|
||||
outbound[k] = override_custom_config_recursive(outbound[k], custom_config_outbound[k]);
|
||||
}
|
||||
}
|
||||
outbound["tag"] = tag;
|
||||
if (type(outbound["streamSettings"]) != "object") {
|
||||
outbound["streamSettings"] = {};
|
||||
}
|
||||
if (type(outbound["streamSettings"]["sockopt"]) != "object") {
|
||||
outbound["streamSettings"]["sockopt"] = {};
|
||||
}
|
||||
outbound["streamSettings"]["sockopt"]["mark"] = outbound_mark;
|
||||
|
||||
const dialer_proxy = outbound_result["dialer_proxy"];
|
||||
const result = [...t, outbound];
|
||||
|
||||
if (dialer_proxy != null) {
|
||||
const dialer_proxy_section = config[dialer_proxy];
|
||||
return server_outbound_recursive(result, dialer_proxy_section, `${tag}@dialer_proxy:${dialer_proxy}`, config);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function direct_outbound(tag, redirect, enable_dynamic_direct) {
|
||||
return {
|
||||
protocol: "freedom",
|
||||
tag: tag,
|
||||
settings: {
|
||||
domainStrategy: "UseIPv4",
|
||||
redirect: redirect || ""
|
||||
},
|
||||
streamSettings: {
|
||||
sockopt: {
|
||||
mark: enable_dynamic_direct ? direct_mark : outbound_mark,
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export function blackhole_outbound() {
|
||||
return {
|
||||
tag: "blackhole_outbound",
|
||||
protocol: "blackhole"
|
||||
};
|
||||
};
|
||||
|
||||
export function server_outbound(server, tag, config) {
|
||||
if (server == null) {
|
||||
return [direct_outbound(tag, null, false)];
|
||||
}
|
||||
return server_outbound_recursive([], server, tag, config);
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
"use strict";
|
||||
|
||||
export function balancer(ref, x, prefix) {
|
||||
const v = ref[x] || [];
|
||||
if (length(v) == 0) {
|
||||
return ["direct"];
|
||||
}
|
||||
return map(v, (k) => `${prefix}@balancer_outbound:${k}`);
|
||||
};
|
||||
|
||||
export function api_conf(proxy) {
|
||||
if (proxy["xray_api"] == '1') {
|
||||
return {
|
||||
tag: "api",
|
||||
services: [
|
||||
"HandlerService",
|
||||
"LoggerService",
|
||||
"StatsService"
|
||||
]
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export function metrics_conf(proxy) {
|
||||
if (proxy["metrics_server_enable"] == "1") {
|
||||
return {
|
||||
tag: "metrics"
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export function policy(proxy) {
|
||||
const stats = proxy["stats"] == "1";
|
||||
return {
|
||||
levels: {
|
||||
"0": {
|
||||
handshake: int(proxy["handshake"] || 4),
|
||||
connIdle: int(proxy["conn_idle"] || 300),
|
||||
uplinkOnly: int(proxy["uplink_only"] || 2),
|
||||
downlinkOnly: int(proxy["downlink_only"] || 5),
|
||||
bufferSize: int(proxy["buffer_size"] || 4),
|
||||
statsUserUplink: stats,
|
||||
statsUserDownlink: stats,
|
||||
}
|
||||
},
|
||||
system: {
|
||||
statsInboundUplink: stats,
|
||||
statsInboundDownlink: stats,
|
||||
statsOutboundUplink: stats,
|
||||
statsOutboundDownlink: stats
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export function logging(proxy) {
|
||||
return {
|
||||
access: proxy["access_log"] == "1" ? "" : "none",
|
||||
loglevel: proxy["loglevel"] || "warning",
|
||||
dnsLog: proxy["dns_log"] == "1"
|
||||
};
|
||||
};
|
||||
|
||||
export function system_route_rules(proxy) {
|
||||
let result = [];
|
||||
if (proxy["xray_api"] == '1') {
|
||||
push(result, {
|
||||
type: "field",
|
||||
inboundTag: ["api"],
|
||||
outboundTag: "api"
|
||||
});
|
||||
}
|
||||
if (proxy["metrics_server_enable"] == "1") {
|
||||
push(result, {
|
||||
type: "field",
|
||||
inboundTag: ["metrics"],
|
||||
outboundTag: "metrics"
|
||||
});
|
||||
}
|
||||
return result;
|
||||
};
|
||||
@@ -0,0 +1,618 @@
|
||||
#!/usr/bin/utpl
|
||||
{%
|
||||
"use strict";
|
||||
import { stat } from "fs";
|
||||
import { load_config } from "./common/config.mjs";
|
||||
import { dns_direct_servers } from "./feature/dns.mjs";
|
||||
const ignore_tp_spec_def_gw = stat("/usr/share/xray/ignore_tp_spec_def_gw");
|
||||
const config = load_config();
|
||||
const general = config[filter(keys(config), k => config[k][".type"] == "general")[0]];
|
||||
const general_mark = general.mark || 255;
|
||||
const tcp4_enabled = length(general.tcp_balancer_v4 || []) > 0;
|
||||
const udp4_enabled = length(general.udp_balancer_v4 || []) > 0;
|
||||
const tcp6_enabled = length(general.tcp_balancer_v6 || []) > 0;
|
||||
const udp6_enabled = length(general.udp_balancer_v6 || []) > 0;
|
||||
const uids_direct = uniq(general.uids_direct || []);
|
||||
const gids_direct = uniq(general.gids_direct || []);
|
||||
let wan_bp_ips = uniq(general.wan_bp_ips || []);
|
||||
let wan_fw_ips = uniq(general.wan_fw_ips || []);
|
||||
let wan_bp_dns = dns_direct_servers(config);
|
||||
const wan_bp_ips_v4 = filter(wan_bp_ips, v => index(v, ":") == -1);
|
||||
const wan_bp_ips_v6 = filter(wan_bp_ips, v => index(v, ":") != -1);
|
||||
const wan_fw_ips_v4 = filter(wan_fw_ips, v => index(v, ":") == -1);
|
||||
const wan_fw_ips_v6 = filter(wan_fw_ips, v => index(v, ":") != -1);
|
||||
const wan_bp_dns_v4 = filter(wan_bp_dns, v => index(v.ip, ":") == -1);
|
||||
const wan_bp_dns_v6 = filter(wan_bp_dns, v => index(v.ip, ":") != -1);
|
||||
const transparent_default_port_policy = general.transparent_default_port_policy || "forwarded";
|
||||
const wan_fw_tcp_ports = general.wan_fw_tcp_ports || [];
|
||||
const wan_fw_udp_ports = general.wan_fw_udp_ports || [];
|
||||
const wan_bp_tcp_ports = general.wan_bp_tcp_ports || [];
|
||||
const wan_bp_udp_ports = general.wan_bp_udp_ports || [];
|
||||
const counter = function () {
|
||||
if (general.fw4_counter == "1") {
|
||||
return "counter packets 0 bytes 0";
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
const firewall_priority = function () {
|
||||
if (general.firewall_priority == null) {
|
||||
return "+10";
|
||||
}
|
||||
if (int(general.firewall_priority) > 0) {
|
||||
return sprintf("+%s", general.firewall_priority);
|
||||
};
|
||||
return sprintf("%s", general.firewall_priority);
|
||||
}();
|
||||
const fakedns = map(filter(keys(config), k => config[k][".type"] == "fakedns") || [], k => config[k]);
|
||||
const manual_tproxy = filter(keys(config), k => config[k][".type"] == "manual_tproxy") || [];
|
||||
const manual_tproxy_source_ips = filter(map(manual_tproxy, k => config[k]["source_addr"]) || [], v => index(v, ":") == -1);
|
||||
|
||||
const tp_spec_sm4_tp = uniq(map(filter(keys(config), k => config[k][".type"] == "lan_hosts" && config[k].access_control_strategy_v4 == "tproxy"), k => config[k].macaddr) || []);
|
||||
const tp_spec_sm6_tp = uniq(map(filter(keys(config), k => config[k][".type"] == "lan_hosts" && config[k].access_control_strategy_v6 == "tproxy"), k => config[k].macaddr) || []);
|
||||
const tp_spec_sm4_bp = uniq(map(filter(keys(config), k => config[k][".type"] == "lan_hosts" && config[k].access_control_strategy_v4 == "bypass"), k => config[k].macaddr) || []);
|
||||
const tp_spec_sm6_bp = uniq(map(filter(keys(config), k => config[k][".type"] == "lan_hosts" && config[k].access_control_strategy_v6 == "bypass"), k => config[k].macaddr) || []);
|
||||
const tp_spec_sm4_fw = map(filter(keys(config), k => config[k][".type"] == "lan_hosts" && config[k].access_control_strategy_v4 == "forward"), k => config[k]);
|
||||
const tp_spec_sm6_fw = map(filter(keys(config), k => config[k][".type"] == "lan_hosts" && config[k].access_control_strategy_v6 == "forward"), k => config[k]);
|
||||
|
||||
const used_extra_inbound = uniq([
|
||||
...map(tp_spec_sm4_fw, k => k["access_control_forward_tcp_v4"]),
|
||||
...map(tp_spec_sm4_fw, k => k["access_control_forward_udp_v4"]),
|
||||
...map(tp_spec_sm6_fw, k => k["access_control_forward_tcp_v6"]),
|
||||
...map(tp_spec_sm6_fw, k => k["access_control_forward_udp_v6"]),
|
||||
]);
|
||||
let extra_inbound_tcp_v4_map = {};
|
||||
let extra_inbound_udp_v4_map = {};
|
||||
let extra_inbound_tcp_v6_map = {};
|
||||
let extra_inbound_udp_v6_map = {};
|
||||
for (let i in used_extra_inbound) {
|
||||
let tcp_v4_items = [];
|
||||
let udp_v4_items = [];
|
||||
let tcp_v6_items = [];
|
||||
let udp_v6_items = [];
|
||||
for (let j in tp_spec_sm4_fw) {
|
||||
if (j["access_control_forward_tcp_v4"] == i) {
|
||||
push(tcp_v4_items, j["macaddr"]);
|
||||
}
|
||||
if (j["access_control_forward_udp_v4"] == i) {
|
||||
push(udp_v4_items, j["macaddr"]);
|
||||
}
|
||||
}
|
||||
if (length(tcp_v4_items) > 0) {
|
||||
extra_inbound_tcp_v4_map[i] = tcp_v4_items;
|
||||
}
|
||||
if (length(udp_v4_items) > 0) {
|
||||
extra_inbound_udp_v4_map[i] = udp_v4_items;
|
||||
}
|
||||
for (let j in tp_spec_sm6_fw) {
|
||||
if (j["access_control_forward_tcp_v6"] == i) {
|
||||
push(tcp_v6_items, j["macaddr"]);
|
||||
}
|
||||
if (j["access_control_forward_udp_v6"] == i) {
|
||||
push(udp_v6_items, j["macaddr"]);
|
||||
}
|
||||
}
|
||||
if (length(tcp_v6_items) > 0) {
|
||||
extra_inbound_tcp_v6_map[i] = tcp_v6_items;
|
||||
}
|
||||
if (length(udp_v6_items) > 0) {
|
||||
extra_inbound_udp_v6_map[i] = udp_v6_items;
|
||||
}
|
||||
}
|
||||
const ttl_override = int(general.ttl_override);
|
||||
const hop_limit_override = int(general.hop_limit_override);
|
||||
const ttl_hop_limit_match = int(general.ttl_hop_limit_match);
|
||||
const dynamic_direct_timeout = general.dynamic_direct_timeout || 2233;
|
||||
const dynamic_direct_tcp4 = function () {
|
||||
if (general.dynamic_direct_tcp4 == "1") {
|
||||
return `update @tp_spec_dv4_dd { ip daddr timeout ${dynamic_direct_timeout}s } update @tp_spec_dv4_dd { ip saddr timeout ${dynamic_direct_timeout}s }`;
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
const dynamic_direct_tcp6 = function () {
|
||||
if (general.dynamic_direct_tcp6 == "1") {
|
||||
return `update @tp_spec_dv6_dd { ip6 daddr timeout ${dynamic_direct_timeout}s } update @tp_spec_dv6_dd { ip6 saddr timeout ${dynamic_direct_timeout}s }`;
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
const dynamic_direct_udp4 = function () {
|
||||
if (general.dynamic_direct_udp4 == "1") {
|
||||
return `update @tp_spec_dv4_dd { ip daddr timeout ${dynamic_direct_timeout}s } update @tp_spec_dv4_dd { ip saddr timeout ${dynamic_direct_timeout}s }`;
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
const dynamic_direct_udp6 = function () {
|
||||
if (general.dynamic_direct_udp6 == "1") {
|
||||
return `update @tp_spec_dv6_dd { ip6 daddr timeout ${dynamic_direct_timeout}s } update @tp_spec_dv6_dd { ip6 saddr timeout ${dynamic_direct_timeout}s }`;
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
const dynamic_direct_conntrack_tcp = function () {
|
||||
if (general.dynamic_direct_tcp4 == "1" || general.dynamic_direct_tcp6 == "1") {
|
||||
return "update @tp_spec_ctt_dd { meta nfproto . tcp sport timeout 30s }";
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
const dynamic_direct_conntrack_udp = function () {
|
||||
if (general.dynamic_direct_udp4 == "1" || general.dynamic_direct_udp6 == "1") {
|
||||
return "update @tp_spec_ctu_dd { meta nfproto . udp sport timeout 30s }";
|
||||
}
|
||||
return "";
|
||||
}();
|
||||
%}
|
||||
|
||||
{% if (length(wan_bp_dns_v4) > 0): %}
|
||||
set tp_spec_dp4_bp {
|
||||
typeof ip daddr . udp dport
|
||||
size {{ length(wan_bp_dns_v4) * 2 + 1 }}
|
||||
flags interval
|
||||
elements = {
|
||||
{{ join(", ", uniq(map(wan_bp_dns_v4, v => `${v.ip} . ${v.port}`))) }}
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (length(wan_bp_dns_v6) > 0): %}
|
||||
set tp_spec_dp6_bp {
|
||||
typeof ip6 daddr . udp dport
|
||||
size {{ length(wan_bp_dns_v6) * 2 + 1 }}
|
||||
flags interval
|
||||
elements = {
|
||||
{{ join(", ", uniq(map(wan_bp_dns_v6, v => `${v.ip} . ${v.port}`))) }}
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
set tp_spec_dv4_sp {
|
||||
type ipv4_addr
|
||||
size 32
|
||||
flags interval
|
||||
elements = { 0.0.0.0/8, 10.0.0.0/8,
|
||||
100.64.0.0/10, 127.0.0.0/8,
|
||||
169.254.0.0/16, 172.16.0.0/12,
|
||||
192.0.0.0/24, 192.52.193.0/24,
|
||||
192.168.0.0/16, 224.0.0.0/3 }
|
||||
}
|
||||
|
||||
set tp_spec_dv6_sp {
|
||||
type ipv6_addr
|
||||
size 32
|
||||
flags interval
|
||||
elements = { ::,
|
||||
::1,
|
||||
::ffff:0.0.0.0/96,
|
||||
::ffff:0:0:0/96,
|
||||
64:ff9b::/96,
|
||||
100::/64,
|
||||
2001::/32,
|
||||
2001:20::/28,
|
||||
2001:db8::/32,
|
||||
2002::/16,
|
||||
fc00::/7,
|
||||
fe80::/10,
|
||||
ff00::/8 }
|
||||
}
|
||||
|
||||
{% if (length(tp_spec_sm4_bp) > 0): %}
|
||||
set tp_spec_sm4_bp {
|
||||
type ether_addr
|
||||
size {{ length(tp_spec_sm4_bp) * 2 + 1 }}
|
||||
elements = { {{ join(", ", tp_spec_sm4_bp) }} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (length(tp_spec_sm6_bp) > 0): %}
|
||||
set tp_spec_sm6_bp {
|
||||
type ether_addr
|
||||
size {{ length(tp_spec_sm6_bp) * 2 + 1 }}
|
||||
elements = { {{ join(", ", tp_spec_sm6_bp) }} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (length(tp_spec_sm4_tp) > 0): %}
|
||||
set tp_spec_sm4_tp {
|
||||
type ether_addr
|
||||
size {{ length(tp_spec_sm4_tp) * 2 + 1 }}
|
||||
elements = { {{ join(", ", tp_spec_sm4_tp) }} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (length(tp_spec_sm6_tp) > 0): %}
|
||||
set tp_spec_sm6_tp {
|
||||
type ether_addr
|
||||
size {{ length(tp_spec_sm6_tp) * 2 + 1 }}
|
||||
elements = { {{ join(", ", tp_spec_sm6_tp) }} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% for (let i in extra_inbound_tcp_v4_map): %}
|
||||
set tp_spec_sm4_ft_{{ i }} {
|
||||
type ether_addr
|
||||
size {{ length(extra_inbound_tcp_v4_map) * 2 + 1 }}
|
||||
elements = { {{ join(", ", extra_inbound_tcp_v4_map[i]) }} }
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% for (let i in extra_inbound_udp_v4_map): %}
|
||||
set tp_spec_sm4_fu_{{ i }} {
|
||||
type ether_addr
|
||||
size {{ length(extra_inbound_udp_v4_map) * 2 + 1 }}
|
||||
elements = { {{ join(", ", extra_inbound_udp_v4_map[i]) }} }
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% for (let i in extra_inbound_tcp_v6_map): %}
|
||||
set tp_spec_sm6_ft_{{ i }} {
|
||||
type ether_addr
|
||||
size {{ length(extra_inbound_tcp_v6_map) * 2 + 1 }}
|
||||
elements = { {{ join(", ", extra_inbound_tcp_v6_map[i]) }} }
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% for (let i in extra_inbound_udp_v6_map): %}
|
||||
set tp_spec_sm6_fu_{{ i }} {
|
||||
type ether_addr
|
||||
size {{ length(extra_inbound_udp_v6_map) * 2 + 1 }}
|
||||
elements = { {{ join(", ", extra_inbound_udp_v6_map[i]) }} }
|
||||
}
|
||||
{% endfor %}
|
||||
|
||||
{% if (length(manual_tproxy_source_ips) > 0): %}
|
||||
set tp_spec_dv4_mt {
|
||||
type ipv4_addr
|
||||
size {{ length(manual_tproxy_source_ips) * 2 + 1 }}
|
||||
elements = { {{ join(", ", manual_tproxy_source_ips) }} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (length(wan_bp_ips_v4) > 0): %}
|
||||
set tp_spec_dv4_bp {
|
||||
type ipv4_addr
|
||||
size {{ length(wan_bp_ips_v4) * 2 + 1 }}
|
||||
flags interval
|
||||
elements = { {{ join(", ", wan_bp_ips_v4)}} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (length(wan_bp_ips_v6) > 0): %}
|
||||
set tp_spec_dv6_bp {
|
||||
type ipv6_addr
|
||||
size {{ length(wan_bp_ips_v6) * 2 + 1 }}
|
||||
flags interval
|
||||
elements = { {{ join(", ", wan_bp_ips_v6)}} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (length(wan_fw_ips_v4) > 0): %}
|
||||
set tp_spec_dv4_fw {
|
||||
type ipv4_addr
|
||||
size {{ length(wan_fw_ips_v4) * 2 + 1 }}
|
||||
flags interval
|
||||
elements = { {{ join(", ", wan_fw_ips_v4)}} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (length(wan_fw_ips_v6) > 0): %}
|
||||
set tp_spec_dv6_fw {
|
||||
type ipv6_addr
|
||||
size {{ length(wan_fw_ips_v6) * 2 + 1 }}
|
||||
flags interval
|
||||
elements = { {{ join(", ", wan_fw_ips_v6)}} }
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (ignore_tp_spec_def_gw == null): %}
|
||||
set tp_spec_dv4_dg {
|
||||
type ipv4_addr
|
||||
size 16
|
||||
flags interval
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
set tp_spec_dv6_dg {
|
||||
type ipv6_addr
|
||||
size 16
|
||||
flags interval
|
||||
}
|
||||
|
||||
{% if (general.dynamic_direct_tcp4 == "1" || general.dynamic_direct_udp4 == "1"): %}
|
||||
set tp_spec_dv4_dd {
|
||||
type ipv4_addr
|
||||
size 65536
|
||||
flags timeout
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (general.dynamic_direct_tcp6 == "1" || general.dynamic_direct_udp6 == "1"): %}
|
||||
set tp_spec_dv6_dd {
|
||||
type ipv6_addr
|
||||
size 65536
|
||||
flags timeout
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (dynamic_direct_conntrack_tcp != ""): %}
|
||||
set tp_spec_ctt_dd {
|
||||
typeof meta nfproto . tcp sport
|
||||
size 65536
|
||||
flags timeout
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if (dynamic_direct_conntrack_udp != ""): %}
|
||||
set tp_spec_ctu_dd {
|
||||
typeof meta nfproto . udp sport
|
||||
size 65536
|
||||
flags timeout
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
chain xray_transparent_proxy {
|
||||
type filter hook prerouting priority filter {{ firewall_priority }}; policy accept;
|
||||
mark 0x000000fb {{ counter }} goto tp_spec_wan_fw
|
||||
ip protocol tcp {{ counter }} accept
|
||||
ip protocol udp {{ counter }} accept
|
||||
ip6 nexthdr tcp {{ counter }} accept
|
||||
ip6 nexthdr udp {{ counter }} accept
|
||||
{{ counter }} accept
|
||||
}
|
||||
|
||||
chain tp_spec_wan_fw {
|
||||
{% if (length(fakedns) > 0): %}
|
||||
ip protocol tcp ip daddr {{ general.pool_v4 || "198.18.0.0/15" }} {{ counter }} tproxy ip to :{{ general.tproxy_port_tcp_f4 || 1086 }} accept
|
||||
ip protocol udp ip daddr {{ general.pool_v4 || "198.18.0.0/15" }} {{ counter }} tproxy ip to :{{ general.tproxy_port_udp_f4 || 1088 }} accept
|
||||
ip6 nexthdr tcp ip6 daddr {{ general.pool_v6 || "fc00::/18" }} {{ counter }} tproxy ip6 to :{{ general.tproxy_port_tcp_f6 || 1087 }} accept
|
||||
ip6 nexthdr udp ip6 daddr {{ general.pool_v6 || "fc00::/18" }} {{ counter }} tproxy ip6 to :{{ general.tproxy_port_udp_f6 || 1089 }} accept
|
||||
{% endif %}
|
||||
{% if (length(manual_tproxy_source_ips) > 0): %}
|
||||
ip protocol tcp ip daddr @tp_spec_dv4_mt {{ counter }} tproxy ip to :{{ general.tproxy_port_tcp_v4 || 1082 }} accept
|
||||
ip protocol udp ip daddr @tp_spec_dv4_mt {{ counter }} tproxy ip to :{{ general.tproxy_port_udp_v4 || 1084 }} accept
|
||||
{% endif %}
|
||||
{% for (let i in extra_inbound_tcp_v4_map): %}
|
||||
ip protocol tcp ether saddr @tp_spec_sm4_ft_{{ i }} {{ counter }} tproxy ip to :{{ config[i].inbound_port }} accept
|
||||
{% endfor %}
|
||||
{% for (let i in extra_inbound_udp_v4_map): %}
|
||||
ip protocol udp ether saddr @tp_spec_sm4_fu_{{ i }} {{ counter }} tproxy ip to :{{ config[i].inbound_port }} accept
|
||||
{% endfor %}
|
||||
{% for (let i in extra_inbound_tcp_v6_map): %}
|
||||
ip6 nexthdr tcp ether saddr @tp_spec_sm6_ft_{{ i }} {{ counter }} tproxy ip6 to :{{ config[i].inbound_port }} accept
|
||||
{% endfor %}
|
||||
{% for (let i in extra_inbound_udp_v6_map): %}
|
||||
ip6 nexthdr udp ether saddr @tp_spec_sm6_fu_{{ i }} {{ counter }} tproxy ip6 to :{{ config[i].inbound_port }} accept
|
||||
{% endfor %}
|
||||
{% if (tcp4_enabled): %}
|
||||
ip protocol tcp {{ counter }} tproxy ip to :{{ general.tproxy_port_tcp_v4 || 1082 }} accept
|
||||
{% else %}
|
||||
ip protocol tcp {{ counter }} meta mark set {{ sprintf("0x%08x", general_mark) }} accept
|
||||
{% endif %}
|
||||
{% if (udp4_enabled): %}
|
||||
ip protocol udp {{ counter }} tproxy ip to :{{ general.tproxy_port_udp_v4 || 1084 }} accept
|
||||
{% else %}
|
||||
ip protocol udp {{ counter }} meta mark set {{ sprintf("0x%08x", general_mark) }} accept
|
||||
{% endif %}
|
||||
{% if (tcp6_enabled): %}
|
||||
ip6 nexthdr tcp {{ counter }} tproxy ip6 to :{{ general.tproxy_port_tcp_v6 || 1083 }} accept
|
||||
{% else %}
|
||||
ip6 nexthdr tcp {{ counter }} meta mark set {{ sprintf("0x%08x", general_mark) }} accept
|
||||
{% endif %}
|
||||
{% if (udp6_enabled): %}
|
||||
ip6 nexthdr udp {{ counter }} tproxy ip6 to :{{ general.tproxy_port_udp_v6 || 1085 }} accept
|
||||
{% else %}
|
||||
ip6 nexthdr udp {{ counter }} meta mark set {{ sprintf("0x%08x", general_mark) }} accept
|
||||
{% endif %}
|
||||
{{ counter }} accept
|
||||
}
|
||||
|
||||
chain xray_prerouting {
|
||||
type filter hook prerouting priority mangle {{ firewall_priority }}; policy accept;
|
||||
{% if (length(general.ttl_override_bypass_ports) > 0): %}
|
||||
tcp dport { {{ join(', ', general.ttl_override_bypass_ports) }} } {{ counter }} meta l4proto tcp accept
|
||||
udp dport { {{ join(', ', general.ttl_override_bypass_ports) }} } {{ counter }} meta l4proto udp accept
|
||||
{% endif %}
|
||||
{% if (ttl_override > 0): %}
|
||||
ip ttl {{ ttl_hop_limit_match }} {{ counter }} ip ttl set {{ ttl_override }}
|
||||
{% endif %}
|
||||
{% if (hop_limit_override > 0): %}
|
||||
ip6 hoplimit {{ ttl_hop_limit_match }} {{ counter }} ip6 hoplimit set {{ hop_limit_override }}
|
||||
{% endif %}
|
||||
{% if (general.dynamic_direct_tcp4 == "1"): %}
|
||||
ip protocol tcp ct mark 0x000000fa {{ counter }} {{ dynamic_direct_tcp4 }} accept comment "Xray dynamic direct TCP4"
|
||||
{% endif %}
|
||||
{% if (general.dynamic_direct_udp4 == "1"): %}
|
||||
ip protocol udp ct mark 0x000000fa {{ counter }} {{ dynamic_direct_udp4 }} accept comment "Xray dynamic direct UDP4"
|
||||
{% endif %}
|
||||
{% if (general.dynamic_direct_tcp6 == "1"): %}
|
||||
ip6 nexthdr tcp ct mark 0x000000fa {{ counter }} {{ dynamic_direct_tcp6 }} accept comment "Xray dynamic direct TCP6"
|
||||
{% endif %}
|
||||
{% if (general.dynamic_direct_udp6 == "1"): %}
|
||||
ip6 nexthdr udp ct mark 0x000000fa {{ counter }} {{ dynamic_direct_udp6 }} accept comment "Xray dynamic direct UDP6"
|
||||
{% endif %}
|
||||
mark 0x000000fb {{ counter }} accept comment "Xray remarked from output"
|
||||
{{ counter }} jump tp_spec_lan_mf comment "Xray FakeDNS / manual transparent proxy"
|
||||
{% if (length(general.bypass_ifaces_v4 || []) > 0): %}
|
||||
ip protocol tcp iifname { "{{ join('", "', general.bypass_ifaces_v4) }}" } {{ counter }} accept
|
||||
ip protocol udp iifname { "{{ join('", "', general.bypass_ifaces_v4) }}" } {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (length(general.bypass_ifaces_v6 || []) > 0): %}
|
||||
ip6 nexthdr tcp iifname { "{{ join('", "', general.bypass_ifaces_v6) }}" } {{ counter }} accept
|
||||
ip6 nexthdr udp iifname { "{{ join('", "', general.bypass_ifaces_v6) }}" } {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (length(tp_spec_sm4_bp) > 0): %}
|
||||
ip protocol tcp ether saddr @tp_spec_sm4_bp {{ counter }} accept
|
||||
ip protocol udp ether saddr @tp_spec_sm4_bp {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (length(tp_spec_sm6_bp) > 0): %}
|
||||
ip6 nexthdr tcp ether saddr @tp_spec_sm6_bp {{ counter }} accept
|
||||
ip6 nexthdr udp ether saddr @tp_spec_sm6_bp {{ counter }} accept
|
||||
{% endif %}
|
||||
{% for (let i in extra_inbound_tcp_v4_map): %}
|
||||
ip protocol tcp ether saddr @tp_spec_sm4_ft_{{ i }} {{ counter }} goto tp_spec_lan_ac
|
||||
{% endfor %}
|
||||
{% for (let i in extra_inbound_udp_v4_map): %}
|
||||
ip protocol udp ether saddr @tp_spec_sm4_fu_{{ i }} {{ counter }} goto tp_spec_lan_ac
|
||||
{% endfor %}
|
||||
{% for (let i in extra_inbound_tcp_v6_map): %}
|
||||
ip6 nexthdr tcp ether saddr @tp_spec_sm6_ft_{{ i }} {{ counter }} goto tp_spec_lan_ac
|
||||
{% endfor %}
|
||||
{% for (let i in extra_inbound_udp_v6_map): %}
|
||||
ip6 nexthdr udp ether saddr @tp_spec_sm6_fu_{{ i }} {{ counter }} goto tp_spec_lan_ac
|
||||
{% endfor %}
|
||||
{% if (length(tp_spec_sm4_tp) > 0): %}
|
||||
ip protocol tcp ether saddr @tp_spec_sm4_tp {{ counter }} goto tp_spec_lan_ac
|
||||
ip protocol udp ether saddr @tp_spec_sm4_tp {{ counter }} goto tp_spec_lan_ac
|
||||
{% endif %}
|
||||
{% if (length(tp_spec_sm6_tp) > 0): %}
|
||||
ip6 nexthdr tcp ether saddr @tp_spec_sm6_tp {{ counter }} goto tp_spec_lan_ac
|
||||
ip6 nexthdr udp ether saddr @tp_spec_sm6_tp {{ counter }} goto tp_spec_lan_ac
|
||||
{% endif %}
|
||||
{% if (length(general.tproxy_ifaces_v4 || []) > 0): %}
|
||||
ip protocol tcp iifname { "{{ join('", "', general.tproxy_ifaces_v4) }}" } {{ counter }} goto tp_spec_lan_ac
|
||||
ip protocol udp iifname { "{{ join('", "', general.tproxy_ifaces_v4) }}" } {{ counter }} goto tp_spec_lan_ac
|
||||
{% endif %}
|
||||
{% if (length(general.tproxy_ifaces_v6 || []) > 0): %}
|
||||
ip6 nexthdr tcp iifname { "{{ join('", "', general.tproxy_ifaces_v6) }}" } {{ counter }} goto tp_spec_lan_ac
|
||||
ip6 nexthdr udp iifname { "{{ join('", "', general.tproxy_ifaces_v6) }}" } {{ counter }} goto tp_spec_lan_ac
|
||||
{% endif %}
|
||||
ip protocol tcp {{ counter }} accept
|
||||
ip protocol udp {{ counter }} accept
|
||||
ip6 nexthdr tcp {{ counter }} accept
|
||||
ip6 nexthdr udp {{ counter }} accept
|
||||
{{ counter }} accept
|
||||
}
|
||||
|
||||
chain xray_output {
|
||||
type route hook output priority mangle {{ firewall_priority }}; policy accept;
|
||||
{% if (length(uids_direct) > 0): %}
|
||||
meta skuid { {{ join(", ", uids_direct) }} } {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (length(gids_direct) > 0): %}
|
||||
meta skgid { {{ join(", ", gids_direct) }} } {{ counter }} accept
|
||||
{% endif %}
|
||||
ip protocol tcp {{ counter }} goto tp_spec_wan_ac
|
||||
ip protocol udp {{ counter }} goto tp_spec_wan_ac
|
||||
ip6 nexthdr tcp {{ counter }} goto tp_spec_wan_ac
|
||||
ip6 nexthdr udp {{ counter }} goto tp_spec_wan_ac
|
||||
{{ counter }} accept
|
||||
}
|
||||
|
||||
chain tp_spec_wan_ac {
|
||||
ip protocol tcp mark 0x000000fc {{ counter }} {{ dynamic_direct_tcp4 }} accept comment "Xray direct outbound TCP4"
|
||||
ip protocol udp mark 0x000000fc {{ counter }} {{ dynamic_direct_udp4 }} accept comment "Xray direct outbound UDP4"
|
||||
ip6 nexthdr tcp mark 0x000000fc {{ counter }} {{ dynamic_direct_tcp6 }} accept comment "Xray direct outbound TCP6"
|
||||
ip6 nexthdr udp mark 0x000000fc {{ counter }} {{ dynamic_direct_udp6 }} accept comment "Xray direct outbound UDP6"
|
||||
meta mark 0x000000fd {{ counter }} accept comment "Xray transparent proxy outbound"
|
||||
meta mark 0x000000fe {{ counter }} accept comment "Xray non-IP DNS query outbound"
|
||||
meta mark {{ sprintf("0x%08x", general_mark) }} {{ counter }} accept comment "Xray specified mark {{ general_mark }} outbound"
|
||||
{{ counter }} jump tp_spec_lan_mf
|
||||
{% if (!tcp4_enabled): %}
|
||||
ip protocol tcp {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (!udp4_enabled): %}
|
||||
ip protocol udp {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (!tcp6_enabled): %}
|
||||
ip6 nexthdr tcp {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (!udp6_enabled): %}
|
||||
ip6 nexthdr udp {{ counter }} accept
|
||||
{% endif %}
|
||||
{{ counter }} goto tp_spec_lan_ac
|
||||
}
|
||||
|
||||
chain tp_spec_lan_mf {
|
||||
{% if (length(fakedns) > 0): %}
|
||||
ip protocol tcp ip daddr {{ general.pool_v4 || "198.18.0.0/15" }} {{ counter }} goto tp_spec_lan_fw comment "Xray FakeDNS IPv4 Pool TCP"
|
||||
ip protocol udp ip daddr {{ general.pool_v4 || "198.18.0.0/15" }} {{ counter }} goto tp_spec_lan_fw comment "Xray FakeDNS IPv4 Pool UDP"
|
||||
ip6 nexthdr tcp ip6 daddr {{ general.pool_v6 || "fc00::/18" }} {{ counter }} goto tp_spec_lan_fw comment "Xray FakeDNS IPv6 Pool TCP"
|
||||
ip6 nexthdr udp ip6 daddr {{ general.pool_v6 || "fc00::/18" }} {{ counter }} goto tp_spec_lan_fw comment "Xray FakeDNS IPv6 Pool UDP"
|
||||
{% endif %}
|
||||
{% if (length(manual_tproxy_source_ips) > 0): %}
|
||||
ip protocol tcp ip daddr @tp_spec_dv4_mt {{ counter }} goto tp_spec_lan_fw comment "Xray manual transparent proxy TCP"
|
||||
ip protocol udp ip daddr @tp_spec_dv4_mt {{ counter }} goto tp_spec_lan_fw comment "Xray manual transparent proxy UDP"
|
||||
{% endif %}
|
||||
{% if (length(wan_bp_dns_v4) > 0): %}
|
||||
ip daddr . tcp dport @tp_spec_dp4_bp {{ counter }} accept
|
||||
ip daddr . udp dport @tp_spec_dp4_bp {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (length(wan_bp_dns_v6) > 0): %}
|
||||
ip6 daddr . tcp dport @tp_spec_dp6_bp {{ counter }} accept
|
||||
ip6 daddr . udp dport @tp_spec_dp6_bp {{ counter }} accept
|
||||
{% endif %}
|
||||
{{ counter }} return
|
||||
}
|
||||
|
||||
chain tp_spec_lan_ac {
|
||||
{% if (length(wan_fw_ips_v4) > 0): %}
|
||||
ip daddr @tp_spec_dv4_fw {{ counter }} goto tp_spec_lan_fw
|
||||
{% endif %}
|
||||
{% if (length(wan_fw_ips_v6) > 0): %}
|
||||
ip6 daddr @tp_spec_dv6_fw {{ counter }} goto tp_spec_lan_fw
|
||||
{% endif %}
|
||||
{% if (ignore_tp_spec_def_gw == null): %}
|
||||
ip daddr @tp_spec_dv4_dg {{ counter }} accept
|
||||
{% endif %}
|
||||
ip6 daddr @tp_spec_dv6_dg {{ counter }} accept
|
||||
{% if (length(wan_bp_ips_v4) > 0): %}
|
||||
ip daddr @tp_spec_dv4_bp {{ counter }} accept
|
||||
{% endif %}
|
||||
{% if (length(wan_bp_ips_v6) > 0): %}
|
||||
ip6 daddr @tp_spec_dv6_bp {{ counter }} accept
|
||||
{% endif %}
|
||||
ip daddr @tp_spec_dv4_sp {{ counter }} accept
|
||||
ip6 daddr @tp_spec_dv6_sp {{ counter }} accept
|
||||
{{ counter }} goto tp_spec_lan_re
|
||||
}
|
||||
|
||||
chain tp_spec_lan_re {
|
||||
{% if (transparent_default_port_policy == "bypassed"): %}
|
||||
{% if (length(wan_fw_tcp_ports) > 0): %}
|
||||
tcp dport { {{ join(", ", wan_fw_tcp_ports) }} } {{ counter }} meta l4proto tcp goto tp_spec_lan_fw
|
||||
{% endif %}
|
||||
{% if (length(wan_fw_udp_ports) > 0): %}
|
||||
udp dport { {{ join(", ", wan_fw_udp_ports) }} } {{ counter }} meta l4proto udp goto tp_spec_lan_fw
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if (length(wan_bp_tcp_ports) > 0): %}
|
||||
tcp dport { {{ join(", ", wan_bp_tcp_ports) }} } {{ counter }} meta l4proto tcp accept
|
||||
{% endif %}
|
||||
{% if (length(wan_bp_udp_ports) > 0): %}
|
||||
udp dport { {{ join(", ", wan_bp_udp_ports) }} } {{ counter }} meta l4proto udp accept
|
||||
{% endif %}
|
||||
meta l4proto tcp {{ counter }} goto tp_spec_lan_dd
|
||||
meta l4proto udp {{ counter }} goto tp_spec_lan_dd
|
||||
{% endif %}
|
||||
{{ counter }} accept
|
||||
}
|
||||
|
||||
chain tp_spec_lan_dd {
|
||||
{% if (general.dynamic_direct_tcp4 == "1"): %}
|
||||
ip protocol tcp ip daddr @tp_spec_dv4_dd {{ counter }} goto tp_spec_lan_ct comment "Xray dynamic direct TCP4"
|
||||
{% endif %}
|
||||
{% if (general.dynamic_direct_udp4 == "1"): %}
|
||||
ip protocol udp ip daddr @tp_spec_dv4_dd {{ counter }} goto tp_spec_lan_ct comment "Xray dynamic direct UDP4"
|
||||
{% endif %}
|
||||
{% if (general.dynamic_direct_tcp6 == "1"): %}
|
||||
ip6 nexthdr tcp ip6 daddr @tp_spec_dv6_dd {{ counter }} goto tp_spec_lan_ct comment "Xray dynamic direct TCP6"
|
||||
{% endif %}
|
||||
{% if (general.dynamic_direct_udp6 == "1"): %}
|
||||
ip6 nexthdr udp ip6 daddr @tp_spec_dv6_dd {{ counter }} goto tp_spec_lan_ct comment "Xray dynamic direct UDP6"
|
||||
{% endif %}
|
||||
meta l4proto tcp {{ counter }} {{ dynamic_direct_conntrack_tcp }} goto tp_spec_lan_fw
|
||||
meta l4proto udp {{ counter }} {{ dynamic_direct_conntrack_udp }} goto tp_spec_lan_fw
|
||||
{{ counter }} accept
|
||||
}
|
||||
|
||||
{% if (dynamic_direct_conntrack_tcp != "" || dynamic_direct_conntrack_udp != ""): %}
|
||||
chain tp_spec_lan_ct {
|
||||
{% if (dynamic_direct_conntrack_tcp != ""): %}
|
||||
meta nfproto . tcp sport @tp_spec_ctt_dd {{ counter }} meta l4proto tcp goto tp_spec_lan_fw
|
||||
{% endif %}
|
||||
{% if (dynamic_direct_conntrack_udp != ""): %}
|
||||
meta nfproto . udp sport @tp_spec_ctu_dd {{ counter }} meta l4proto udp goto tp_spec_lan_fw
|
||||
{% endif %}
|
||||
{{ counter }} ct mark set 0x000000fa accept comment "Xray dynamic direct successful registration"
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
chain tp_spec_lan_fw {
|
||||
{{ counter }} mark set 0x000000fb accept
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
#!/usr/bin/ucode
|
||||
"use strict";
|
||||
|
||||
import { access } from "fs";
|
||||
import { load_config } from "./common/config.mjs";
|
||||
import { bridge_outbounds, bridge_rules, bridges } from "./feature/bridge.mjs";
|
||||
import { blocked_domain_rules, dns_conf, dns_rules, dns_server_inbounds, dns_server_outbounds, fast_domain_rules, secure_domain_rules } from "./feature/dns.mjs";
|
||||
import { fake_dns_balancers, fake_dns_conf, fake_dns_rules } from "./feature/fake_dns.mjs";
|
||||
import { dokodemo_inbound, extra_inbound_balancers, extra_inbound_global, extra_inbound_rules, extra_inbounds, http_inbound, https_inbound, socks_inbound } from "./feature/inbound.mjs";
|
||||
import { manual_tproxy_outbound_tags, manual_tproxy_outbounds, manual_tproxy_rules } from "./feature/manual_tproxy.mjs";
|
||||
import { blackhole_outbound, direct_outbound, server_outbound } from "./feature/outbound.mjs";
|
||||
import { api_conf, balancer, logging, metrics_conf, policy, system_route_rules } from "./feature/system.mjs";
|
||||
|
||||
function inbounds(proxy, config, extra_inbound) {
|
||||
const tproxy_sniffing = proxy["tproxy_sniffing"];
|
||||
const route_only = proxy["route_only"];
|
||||
const conn_idle = proxy["conn_idle"];
|
||||
|
||||
let i = [
|
||||
socks_inbound("0.0.0.0", proxy["socks_port"] || 1080, "socks_inbound"),
|
||||
http_inbound("0.0.0.0", proxy["http_port"] || 1081, "http_inbound"),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_tcp_v4"] || 1082, "tproxy_tcp_inbound_v4", tproxy_sniffing, route_only, ["http", "tls"], "0", "tcp", "tproxy", conn_idle),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_tcp_v6"] || 1083, "tproxy_tcp_inbound_v6", tproxy_sniffing, route_only, ["http", "tls"], "0", "tcp", "tproxy", conn_idle),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_v4"] || 1084, "tproxy_udp_inbound_v4", tproxy_sniffing, route_only, ["quic"], "0", "udp", "tproxy", conn_idle),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_v6"] || 1085, "tproxy_udp_inbound_v6", tproxy_sniffing, route_only, ["quic"], "0", "udp", "tproxy", conn_idle),
|
||||
...extra_inbounds(proxy, extra_inbound),
|
||||
...dns_server_inbounds(proxy),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_tcp_f4"] || 1086, "tproxy_tcp_inbound_f4", "1", "0", ["fakedns"], "1", "tcp", "tproxy", conn_idle),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_tcp_f6"] || 1087, "tproxy_tcp_inbound_f6", "1", "0", ["fakedns"], "1", "tcp", "tproxy", conn_idle),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_f4"] || 1088, "tproxy_udp_inbound_f4", "1", "0", ["fakedns"], "1", "udp", "tproxy", conn_idle),
|
||||
dokodemo_inbound("0.0.0.0", proxy["tproxy_port_udp_f6"] || 1089, "tproxy_udp_inbound_f6", "1", "0", ["fakedns"], "1", "udp", "tproxy", conn_idle),
|
||||
];
|
||||
if (proxy["web_server_enable"] == "1") {
|
||||
push(i, https_inbound(proxy, config));
|
||||
}
|
||||
if (proxy["metrics_server_enable"] == '1') {
|
||||
push(i, {
|
||||
listen: "0.0.0.0",
|
||||
port: int(proxy["metrics_server_port"]) || 18888,
|
||||
protocol: "dokodemo-door",
|
||||
settings: {
|
||||
address: "127.0.0.1"
|
||||
},
|
||||
tag: "metrics"
|
||||
});
|
||||
}
|
||||
if (proxy["xray_api"] == '1') {
|
||||
push(i, {
|
||||
listen: "127.0.0.1",
|
||||
port: 8080,
|
||||
protocol: "dokodemo-door",
|
||||
settings: {
|
||||
address: "127.0.0.1"
|
||||
},
|
||||
tag: "api"
|
||||
});
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
function outbounds(proxy, config, manual_tproxy, bridge, extra_inbound, fakedns) {
|
||||
let result = [
|
||||
blackhole_outbound(),
|
||||
direct_outbound("direct", null, false),
|
||||
direct_outbound("dynamic_direct", null, true),
|
||||
...dns_server_outbounds(proxy),
|
||||
...manual_tproxy_outbounds(config, manual_tproxy),
|
||||
...bridge_outbounds(config, bridge)
|
||||
];
|
||||
let outbound_balancers_all = {};
|
||||
for (let b in ["tcp_balancer_v4", "udp_balancer_v4", "tcp_balancer_v6", "udp_balancer_v6"]) {
|
||||
for (let i in balancer(proxy, b, b)) {
|
||||
if (i != "direct") {
|
||||
outbound_balancers_all[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let e in extra_inbound) {
|
||||
if (e["specify_outbound"] == "1") {
|
||||
for (let i in balancer(e, "destination", `extra_inbound:${e[".name"]}`)) {
|
||||
if (i != "direct") {
|
||||
outbound_balancers_all[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let f in fakedns) {
|
||||
for (let i in balancer(f, "fake_dns_forward_server_tcp", `fake_dns_tcp:${f[".name"]}`)) {
|
||||
if (i != "direct") {
|
||||
outbound_balancers_all[i] = true;
|
||||
}
|
||||
}
|
||||
for (let i in balancer(f, "fake_dns_forward_server_udp", `fake_dns_udp:${f[".name"]}`)) {
|
||||
if (i != "direct") {
|
||||
outbound_balancers_all[i] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i in keys(outbound_balancers_all)) {
|
||||
push(result, ...server_outbound(config[substr(i, -9)], i, config));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function rules(proxy, bridge, manual_tproxy, extra_inbound, fakedns) {
|
||||
const geoip_existence = access("/usr/share/xray/geoip.dat") || false;
|
||||
const tproxy_tcp_inbound_v4_tags = ["tproxy_tcp_inbound_v4"];
|
||||
const tproxy_udp_inbound_v4_tags = ["tproxy_udp_inbound_v4"];
|
||||
const tproxy_tcp_inbound_v6_tags = ["tproxy_tcp_inbound_v6"];
|
||||
const tproxy_udp_inbound_v6_tags = ["tproxy_udp_inbound_v6"];
|
||||
const extra_inbound_global_tags = extra_inbound_global();
|
||||
const extra_inbound_global_tcp_tags = extra_inbound_global_tags["tproxy_tcp"] || [];
|
||||
const extra_inbound_global_udp_tags = extra_inbound_global_tags["tproxy_udp"] || [];
|
||||
const extra_inbound_global_http_tags = extra_inbound_global_tags["http"] || [];
|
||||
const extra_inbound_global_socks5_tags = extra_inbound_global_tags["socks5"] || [];
|
||||
const built_in_tcp_inbounds = [...tproxy_tcp_inbound_v4_tags, ...extra_inbound_global_tcp_tags, ...extra_inbound_global_http_tags, ...extra_inbound_global_socks5_tags, "socks_inbound", "https_inbound", "http_inbound"];
|
||||
const built_in_udp_inbounds = [...tproxy_udp_inbound_v4_tags, ...extra_inbound_global_udp_tags, "dns_conf_inbound"];
|
||||
let result = [
|
||||
...fake_dns_rules(fakedns),
|
||||
...manual_tproxy_rules(manual_tproxy),
|
||||
...extra_inbound_rules(extra_inbound),
|
||||
...system_route_rules(proxy),
|
||||
...bridge_rules(bridge),
|
||||
...dns_rules(proxy, [...tproxy_tcp_inbound_v6_tags, ...tproxy_tcp_inbound_v4_tags, ...extra_inbound_global_tcp_tags], [...tproxy_udp_inbound_v6_tags, ...tproxy_udp_inbound_v4_tags, ...extra_inbound_global_udp_tags]),
|
||||
...function () {
|
||||
let direct_rules = [];
|
||||
if (geoip_existence) {
|
||||
const geoip_direct_code_list = map(proxy["geoip_direct_code_list"] || [], v => index(v, ":") > 0 ? v : `geoip:${v}`);
|
||||
if (length(geoip_direct_code_list) > 0) {
|
||||
push(direct_rules, {
|
||||
type: "field",
|
||||
inboundTag: [...built_in_tcp_inbounds, ...built_in_udp_inbounds],
|
||||
outboundTag: "dynamic_direct",
|
||||
ip: geoip_direct_code_list
|
||||
});
|
||||
}
|
||||
const geoip_direct_code_list_v6 = map(proxy["geoip_direct_code_list_v6"] || [], v => index(v, ":") > 0 ? v : `geoip:${v}`);
|
||||
if (length(geoip_direct_code_list_v6) > 0) {
|
||||
push(direct_rules, {
|
||||
type: "field",
|
||||
inboundTag: [...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags],
|
||||
outboundTag: "dynamic_direct",
|
||||
ip: geoip_direct_code_list_v6
|
||||
});
|
||||
}
|
||||
push(direct_rules, {
|
||||
type: "field",
|
||||
inboundTag: [...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...built_in_tcp_inbounds, ...built_in_udp_inbounds],
|
||||
outboundTag: "direct",
|
||||
ip: ["geoip:private"]
|
||||
});
|
||||
}
|
||||
return direct_rules;
|
||||
}(),
|
||||
{
|
||||
type: "field",
|
||||
inboundTag: tproxy_tcp_inbound_v6_tags,
|
||||
balancerTag: "tcp_outbound_v6"
|
||||
},
|
||||
{
|
||||
type: "field",
|
||||
inboundTag: tproxy_udp_inbound_v6_tags,
|
||||
balancerTag: "udp_outbound_v6"
|
||||
},
|
||||
{
|
||||
type: "field",
|
||||
inboundTag: built_in_tcp_inbounds,
|
||||
balancerTag: "tcp_outbound_v4"
|
||||
},
|
||||
{
|
||||
type: "field",
|
||||
inboundTag: built_in_udp_inbounds,
|
||||
balancerTag: "udp_outbound_v4"
|
||||
},
|
||||
];
|
||||
if (proxy["tproxy_sniffing"] == "1") {
|
||||
if (length(secure_domain_rules(proxy)) > 0) {
|
||||
splice(result, 0, 0, {
|
||||
type: "field",
|
||||
inboundTag: [...tproxy_tcp_inbound_v4_tags, ...extra_inbound_global_tcp_tags],
|
||||
balancerTag: "tcp_outbound_v4",
|
||||
domain: secure_domain_rules(proxy),
|
||||
}, {
|
||||
type: "field",
|
||||
inboundTag: [...tproxy_udp_inbound_v4_tags, ...extra_inbound_global_udp_tags],
|
||||
balancerTag: "udp_outbound_v4",
|
||||
domain: secure_domain_rules(proxy),
|
||||
}, {
|
||||
type: "field",
|
||||
inboundTag: [...tproxy_tcp_inbound_v6_tags],
|
||||
balancerTag: "tcp_outbound_v6",
|
||||
domain: secure_domain_rules(proxy),
|
||||
}, {
|
||||
type: "field",
|
||||
inboundTag: [...tproxy_udp_inbound_v6_tags],
|
||||
balancerTag: "udp_outbound_v6",
|
||||
domain: secure_domain_rules(proxy),
|
||||
});
|
||||
}
|
||||
if (length(blocked_domain_rules(proxy)) > 0) {
|
||||
splice(result, 0, 0, {
|
||||
type: "field",
|
||||
inboundTag: [...tproxy_tcp_inbound_v4_tags, ...tproxy_udp_inbound_v4_tags, ...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...extra_inbound_global_tcp_tags, ...extra_inbound_global_udp_tags],
|
||||
outboundTag: "blackhole_outbound",
|
||||
domain: blocked_domain_rules(proxy),
|
||||
});
|
||||
}
|
||||
splice(result, 0, 0, {
|
||||
type: "field",
|
||||
inboundTag: [...tproxy_tcp_inbound_v4_tags, ...tproxy_udp_inbound_v4_tags, ...tproxy_tcp_inbound_v6_tags, ...tproxy_udp_inbound_v6_tags, ...extra_inbound_global_tcp_tags, ...extra_inbound_global_udp_tags],
|
||||
outboundTag: "direct",
|
||||
domain: fast_domain_rules(proxy)
|
||||
});
|
||||
if (proxy["direct_bittorrent"] == "1") {
|
||||
splice(result, 0, 0, {
|
||||
type: "field",
|
||||
outboundTag: "direct",
|
||||
protocol: ["bittorrent"]
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function balancers(proxy, extra_inbound, fakedns) {
|
||||
const general_balancer_strategy = proxy["general_balancer_strategy"] || "random";
|
||||
const built_in_outbounds = ["tcp_outbound_v4", "udp_outbound_v4", "tcp_outbound_v6", "udp_outbound_v6"];
|
||||
const built_in_balancers = ["tcp_balancer_v4", "udp_balancer_v4", "tcp_balancer_v6", "udp_balancer_v6"];
|
||||
return [
|
||||
...map(built_in_balancers, function (balancer_tag, index) {
|
||||
return {
|
||||
"tag": built_in_outbounds[index],
|
||||
"selector": balancer(proxy, balancer_tag, balancer_tag),
|
||||
"strategy": {
|
||||
"type": general_balancer_strategy
|
||||
}
|
||||
};
|
||||
}),
|
||||
...extra_inbound_balancers(extra_inbound),
|
||||
...fake_dns_balancers(fakedns),
|
||||
];
|
||||
};
|
||||
|
||||
function observatory(proxy, manual_tproxy) {
|
||||
if (proxy["observatory"] == "1") {
|
||||
return {
|
||||
subjectSelector: ["tcp_balancer_v4@balancer_outbound", "udp_balancer_v4@balancer_outbound", "tcp_balancer_v6@balancer_outbound", "udp_balancer_v6@balancer_outbound", "extra_inbound", "fake_dns", "direct", ...manual_tproxy_outbound_tags(manual_tproxy)],
|
||||
probeInterval: "100ms",
|
||||
probeUrl: "http://www.apple.com/library/test/success.html"
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function gen_config() {
|
||||
const config = load_config();
|
||||
const bridge = filter(values(config), v => v[".type"] == "bridge") || [];
|
||||
const fakedns = filter(values(config), v => v[".type"] == "fakedns") || [];
|
||||
const extra_inbound = filter(values(config), v => v[".type"] == "extra_inbound") || [];
|
||||
const manual_tproxy = filter(values(config), v => v[".type"] == "manual_tproxy") || [];
|
||||
|
||||
const general = filter(values(config), k => k[".type"] == "general")[0] || {};
|
||||
const custom_configuration_hook = loadstring(general["custom_configuration_hook"] || "return i => i;")();
|
||||
return custom_configuration_hook({
|
||||
inbounds: inbounds(general, config, extra_inbound),
|
||||
outbounds: outbounds(general, config, manual_tproxy, bridge, extra_inbound, fakedns),
|
||||
dns: dns_conf(general, config, manual_tproxy, fakedns),
|
||||
fakedns: fake_dns_conf(general),
|
||||
api: api_conf(general),
|
||||
metrics: metrics_conf(general),
|
||||
policy: policy(general),
|
||||
log: logging(general),
|
||||
stats: general["stats"] == "1" ? {
|
||||
place: "holder"
|
||||
} : null,
|
||||
observatory: observatory(general, manual_tproxy),
|
||||
reverse: {
|
||||
bridges: bridges(bridge)
|
||||
},
|
||||
routing: {
|
||||
domainStrategy: general["routing_domain_strategy"] || "AsIs",
|
||||
rules: rules(general, bridge, manual_tproxy, extra_inbound, fakedns),
|
||||
balancers: balancers(general, extra_inbound, fakedns)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
printf("%.4J", gen_config());
|
||||
@@ -0,0 +1 @@
|
||||
Ignore TP_SPEC_DEF_GW (nftables only). TP_SPEC_DEF_GW is only useful when you have publicly routable IPv4 address on your router.
|
||||
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
|
||||
import { port_array, stream_settings } from "../common/stream.mjs";
|
||||
|
||||
export function http_outbound(server, tag) {
|
||||
const stream_settings_object = stream_settings(server, "http", tag);
|
||||
const stream_settings_result = stream_settings_object["stream_settings"];
|
||||
const dialer_proxy = stream_settings_object["dialer_proxy"];
|
||||
let users = null;
|
||||
if (server["username"] && server["password"]) {
|
||||
users = [
|
||||
{
|
||||
user: server["username"],
|
||||
pass: server["password"],
|
||||
}
|
||||
];
|
||||
}
|
||||
return {
|
||||
outbound: {
|
||||
protocol: "http",
|
||||
tag: tag,
|
||||
settings: {
|
||||
servers: map(port_array(server["server_port"]), function (v) {
|
||||
return {
|
||||
address: server["server"],
|
||||
port: v,
|
||||
users: users
|
||||
};
|
||||
})
|
||||
},
|
||||
streamSettings: stream_settings_result
|
||||
},
|
||||
dialer_proxy: dialer_proxy
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
"use strict";
|
||||
|
||||
import { port_array, stream_settings } from "../common/stream.mjs";
|
||||
|
||||
export function hysteria_outbound(server, tag) {
|
||||
const stream_settings_object = stream_settings(server, "hysteria", tag);
|
||||
const stream_settings_result = stream_settings_object["stream_settings"];
|
||||
const dialer_proxy = stream_settings_object["dialer_proxy"];
|
||||
return {
|
||||
outbound: {
|
||||
protocol: "hysteria",
|
||||
tag: tag,
|
||||
settings: {
|
||||
version: 2,
|
||||
address: server["server"],
|
||||
port: port_array(server["server_port"])[0],
|
||||
},
|
||||
streamSettings: stream_settings_result
|
||||
},
|
||||
dialer_proxy: dialer_proxy
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
"use strict";
|
||||
|
||||
import { port_array, stream_settings } from "../common/stream.mjs";
|
||||
|
||||
export function shadowsocks_outbound(server, tag) {
|
||||
const stream_settings_object = stream_settings(server, "shadowsocks", tag);
|
||||
const stream_settings_result = stream_settings_object["stream_settings"];
|
||||
const dialer_proxy = stream_settings_object["dialer_proxy"];
|
||||
return {
|
||||
outbound: {
|
||||
protocol: "shadowsocks",
|
||||
tag: tag,
|
||||
settings: {
|
||||
servers: map(port_array(server["server_port"]), function (v) {
|
||||
return {
|
||||
address: server["server"],
|
||||
port: v,
|
||||
email: server["username"],
|
||||
password: server["password"],
|
||||
method: server["shadowsocks_security"],
|
||||
uot: server["shadowsocks_udp_over_tcp"] == '1'
|
||||
};
|
||||
})
|
||||
},
|
||||
streamSettings: stream_settings_result
|
||||
},
|
||||
dialer_proxy: dialer_proxy
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
|
||||
import { port_array, stream_settings } from "../common/stream.mjs";
|
||||
|
||||
export function socks_outbound(server, tag) {
|
||||
const stream_settings_object = stream_settings(server, "socks", tag);
|
||||
const stream_settings_result = stream_settings_object["stream_settings"];
|
||||
const dialer_proxy = stream_settings_object["dialer_proxy"];
|
||||
let users = null;
|
||||
if (server["username"] && server["password"]) {
|
||||
users = [
|
||||
{
|
||||
user: server["username"],
|
||||
pass: server["password"],
|
||||
}
|
||||
];
|
||||
}
|
||||
return {
|
||||
outbound: {
|
||||
protocol: "socks",
|
||||
tag: tag,
|
||||
settings: {
|
||||
servers: map(port_array(server["server_port"]), function (v) {
|
||||
return {
|
||||
address: server["server"],
|
||||
port: v,
|
||||
users: users
|
||||
};
|
||||
})
|
||||
},
|
||||
streamSettings: stream_settings_result
|
||||
},
|
||||
dialer_proxy: dialer_proxy
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
"use strict";
|
||||
|
||||
import { port_array, stream_settings } from "../common/stream.mjs";
|
||||
import { fallbacks, tls_inbound_settings } from "../common/tls.mjs";
|
||||
|
||||
function trojan_inbound_user(k) {
|
||||
return {
|
||||
email: k,
|
||||
password: k,
|
||||
};
|
||||
}
|
||||
|
||||
export function trojan_outbound(server, tag) {
|
||||
const stream_settings_object = stream_settings(server, "trojan", tag);
|
||||
const stream_settings_result = stream_settings_object["stream_settings"];
|
||||
const dialer_proxy = stream_settings_object["dialer_proxy"];
|
||||
return {
|
||||
outbound: {
|
||||
protocol: "trojan",
|
||||
tag: tag,
|
||||
settings: {
|
||||
servers: map(port_array(server["server_port"]), function (v) {
|
||||
return {
|
||||
address: server["server"],
|
||||
port: v,
|
||||
email: server["username"],
|
||||
password: server["password"]
|
||||
};
|
||||
})
|
||||
},
|
||||
streamSettings: stream_settings_result
|
||||
},
|
||||
dialer_proxy: dialer_proxy
|
||||
};
|
||||
};
|
||||
|
||||
export function https_trojan_inbound(proxy, config) {
|
||||
return {
|
||||
port: proxy["web_server_port"] || 443,
|
||||
protocol: "trojan",
|
||||
tag: "https_inbound",
|
||||
settings: {
|
||||
clients: map(proxy["web_server_password"], trojan_inbound_user),
|
||||
fallbacks: fallbacks(proxy, config)
|
||||
},
|
||||
streamSettings: {
|
||||
network: "tcp",
|
||||
security: proxy["trojan_tls"],
|
||||
tlsSettings: proxy["trojan_tls"] == "tls" ? tls_inbound_settings(proxy, "trojan") : null
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
"use strict";
|
||||
|
||||
import { port_array, stream_settings } from "../common/stream.mjs";
|
||||
import { fallbacks, reality_inbound_settings, tls_inbound_settings } from "../common/tls.mjs";
|
||||
|
||||
function vless_inbound_user(k, flow) {
|
||||
return {
|
||||
id: k,
|
||||
email: k,
|
||||
flow: flow,
|
||||
};
|
||||
}
|
||||
|
||||
export function vless_outbound(server, tag) {
|
||||
let flow = null;
|
||||
if (server["vless_tls"] == "tls") {
|
||||
flow = server["vless_flow_tls"];
|
||||
} else if (server["vless_tls"] == "reality") {
|
||||
flow = server["vless_flow_reality"];
|
||||
}
|
||||
if (flow == "none") {
|
||||
flow = null;
|
||||
}
|
||||
const stream_settings_object = stream_settings(server, "vless", tag);
|
||||
const stream_settings_result = stream_settings_object["stream_settings"];
|
||||
const dialer_proxy = stream_settings_object["dialer_proxy"];
|
||||
return {
|
||||
outbound: {
|
||||
protocol: "vless",
|
||||
tag: tag,
|
||||
settings: {
|
||||
vnext: map(port_array(server["server_port"]), function (v) {
|
||||
return {
|
||||
address: server["server"],
|
||||
port: v,
|
||||
users: [
|
||||
{
|
||||
email: server["username"],
|
||||
id: server["password"],
|
||||
flow: flow,
|
||||
encryption: server["vless_encryption"] || "none"
|
||||
}
|
||||
]
|
||||
};
|
||||
})
|
||||
},
|
||||
streamSettings: stream_settings_result
|
||||
},
|
||||
dialer_proxy: dialer_proxy
|
||||
};
|
||||
};
|
||||
|
||||
export function https_vless_inbound(proxy, config) {
|
||||
let flow = null;
|
||||
if (proxy["vless_tls"] == "tls") {
|
||||
flow = proxy["vless_flow_tls"];
|
||||
} else if (proxy["vless_tls"] == "reality") {
|
||||
flow = proxy["vless_flow_reality"];
|
||||
}
|
||||
if (flow == "none") {
|
||||
flow = null;
|
||||
}
|
||||
return {
|
||||
port: proxy["web_server_port"] || 443,
|
||||
protocol: "vless",
|
||||
tag: "https_inbound",
|
||||
settings: {
|
||||
clients: map(proxy["web_server_password"], k => vless_inbound_user(k, flow)),
|
||||
decryption: proxy["vless_decryption"] || "none",
|
||||
fallbacks: fallbacks(proxy, config)
|
||||
},
|
||||
streamSettings: {
|
||||
network: "tcp",
|
||||
security: proxy["vless_tls"],
|
||||
tlsSettings: proxy["vless_tls"] == "tls" ? tls_inbound_settings(proxy, "vless") : null,
|
||||
realitySettings: proxy["vless_tls"] == "reality" ? reality_inbound_settings(proxy, "vless") : null,
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
"use strict";
|
||||
|
||||
import { port_array, stream_settings } from "../common/stream.mjs";
|
||||
|
||||
export function vmess_outbound(server, tag) {
|
||||
const stream_settings_object = stream_settings(server, "vmess", tag);
|
||||
const stream_settings_result = stream_settings_object["stream_settings"];
|
||||
const dialer_proxy = stream_settings_object["dialer_proxy"];
|
||||
return {
|
||||
outbound: {
|
||||
protocol: "vmess",
|
||||
tag: tag,
|
||||
settings: {
|
||||
vnext: map(port_array(server["server_port"]), function (v) {
|
||||
return {
|
||||
address: server["server"],
|
||||
port: v,
|
||||
users: [
|
||||
{
|
||||
email: server["username"],
|
||||
id: server["password"],
|
||||
alterId: int(server["alter_id"]),
|
||||
security: server["vmess_security"]
|
||||
}
|
||||
]
|
||||
};
|
||||
})
|
||||
},
|
||||
streamSettings: stream_settings_result
|
||||
},
|
||||
dialer_proxy: dialer_proxy
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
If you are using dnsmasq v2.87, keep this file.
|
||||
See https://thekelleys.org.uk/dnsmasq/CHANGELOG for fixes in version 2.88 about the bug introduced in version 2.87, which could result in DNS servers being removed from the configuration when reloading server configuration.
|
||||
@@ -0,0 +1 @@
|
||||
300000000 333333333
|
||||
@@ -0,0 +1 @@
|
||||
44444444 55555555
|
||||
@@ -0,0 +1 @@
|
||||
8192 16384
|
||||
Reference in new issue
Block a user