fix(singbox): fix p2p isolation rule matching for transport network and port directions
This commit is contained in:
+208
-208
File diff suppressed because it is too large
Load Diff
+210
-210
File diff suppressed because it is too large
Load Diff
@@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: TACHYON\n"
|
"Project-Id-Version: TACHYON\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-09-17 18:04+0300\n"
|
"POT-Creation-Date: 2026-09-17 18:17+0300\n"
|
||||||
"PO-Revision-Date: 2026-09-17 18:04+0300\n"
|
"PO-Revision-Date: 2026-09-17 18:17+0300\n"
|
||||||
"Last-Translator: Dushnilin\n"
|
"Last-Translator: Dushnilin\n"
|
||||||
"Language-Team: none\n"
|
"Language-Team: none\n"
|
||||||
"Language: ru\n"
|
"Language: ru\n"
|
||||||
|
|||||||
@@ -2175,13 +2175,16 @@ function createSettingsContent(section, capabilities) {
|
|||||||
|
|
||||||
const entries = value.split(",");
|
const entries = value.split(",");
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
if (!/^(tcp|udp)(:[0-9]{1,5}(-[0-9]{1,5})?)?$/.test(entry.trim())) {
|
const trimmed = entry.trim();
|
||||||
|
if (!/^((tcp|udp):)?[0-9]{1,5}(-[0-9]{1,5})?$|^(tcp|udp)$/.test(trimmed)) {
|
||||||
return _(
|
return _(
|
||||||
'Each entry must be "proto", "proto:port" or "proto:port-port" (tcp/udp), e.g. tcp:6881,udp:6881-6889',
|
'Each entry must be "proto", "proto:port" or "proto:port-port" (tcp/udp), e.g. tcp:6881,udp:6881-6889',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const portSpec = entry.split(":")[1];
|
const portSpec = trimmed.includes(":")
|
||||||
|
? trimmed.split(":")[1]
|
||||||
|
: (/^[0-9]/.test(trimmed) ? trimmed : null);
|
||||||
if (!portSpec) {
|
if (!portSpec) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: TACHYON\n"
|
"Project-Id-Version: TACHYON\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-09-17 18:04+0300\n"
|
"POT-Creation-Date: 2026-09-17 18:17+0300\n"
|
||||||
"PO-Revision-Date: 2026-09-17 18:04+0300\n"
|
"PO-Revision-Date: 2026-09-17 18:17+0300\n"
|
||||||
"Last-Translator: Dushnilin\n"
|
"Last-Translator: Dushnilin\n"
|
||||||
"Language-Team: none\n"
|
"Language-Team: none\n"
|
||||||
"Language: ru\n"
|
"Language: ru\n"
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -18,45 +18,83 @@ function normalize_port_entry(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// P2P leak protection can be pinned down to the torrent client's own ports so
|
// P2P leak protection can be pinned down to the torrent client's own ports so
|
||||||
// nothing sneaks past the protocol sniffer. Each entry is "proto" or
|
// nothing sneaks past the protocol sniffer. Each entry is:
|
||||||
// "proto:port" or "proto:port-port" (tcp/udp, comma separated). Without a
|
// "port", "port-port", "proto", "proto:port" or "proto:port-port" (tcp/udp, comma separated).
|
||||||
// port the whole protocol is routed direct.
|
// Without a port the whole protocol is routed direct.
|
||||||
function p2p_direct_rules(settings) {
|
function p2p_direct_rules(settings) {
|
||||||
let rules = [];
|
let rules = [];
|
||||||
for (let entry in split(option(settings, "p2p_ports", ""), ",")) {
|
for (let entry in split(option(settings, "p2p_ports", ""), ",")) {
|
||||||
entry = trim(entry);
|
entry = trim(entry);
|
||||||
if (entry == "")
|
if (entry == "")
|
||||||
continue;
|
continue;
|
||||||
let proto = "tcp";
|
let proto = "both";
|
||||||
let port_spec = "";
|
let port_spec = "";
|
||||||
let colon = index(entry, ":");
|
let colon = index(entry, ":");
|
||||||
if (colon >= 0) {
|
if (colon >= 0) {
|
||||||
proto = trim(substr(entry, 0, colon));
|
let p = lc(trim(substr(entry, 0, colon)));
|
||||||
port_spec = trim(substr(entry, colon + 1));
|
if (p == "tcp" || p == "udp") {
|
||||||
|
proto = p;
|
||||||
|
port_spec = trim(substr(entry, colon + 1));
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
proto = entry;
|
let p = lc(entry);
|
||||||
|
if (p == "tcp" || p == "udp") {
|
||||||
|
proto = p;
|
||||||
|
port_spec = "";
|
||||||
|
} else {
|
||||||
|
proto = "both";
|
||||||
|
port_spec = entry;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (proto != "tcp" && proto != "udp")
|
|
||||||
continue;
|
let networks = proto == "both" ? [ "tcp", "udp" ] : [ proto ];
|
||||||
let rule = { protocol: [ proto ], action: "route", outbound: runtime_constants.DIRECT_OUTBOUND_TAG };
|
|
||||||
if (port_spec != "") {
|
if (port_spec != "") {
|
||||||
let dash = index(port_spec, "-");
|
let dash = index(port_spec, "-");
|
||||||
if (dash >= 0) {
|
if (dash >= 0) {
|
||||||
let start = normalize_port_entry(substr(port_spec, 0, dash));
|
let start = normalize_port_entry(substr(port_spec, 0, dash));
|
||||||
let end = normalize_port_entry(substr(port_spec, dash + 1));
|
let end = normalize_port_entry(substr(port_spec, dash + 1));
|
||||||
if (start != null && end != null && start <= end)
|
if (start != null && end != null && start <= end) {
|
||||||
rule.source_port_range = [ sprintf("%d:%d", start, end) ];
|
let range_str = sprintf("%d:%d", start, end);
|
||||||
else
|
push(rules, {
|
||||||
continue;
|
network: networks,
|
||||||
|
source_port_range: [ range_str ],
|
||||||
|
action: "route",
|
||||||
|
outbound: runtime_constants.DIRECT_OUTBOUND_TAG
|
||||||
|
});
|
||||||
|
push(rules, {
|
||||||
|
network: networks,
|
||||||
|
port_range: [ range_str ],
|
||||||
|
action: "route",
|
||||||
|
outbound: runtime_constants.DIRECT_OUTBOUND_TAG
|
||||||
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let port = normalize_port_entry(port_spec);
|
let port = normalize_port_entry(port_spec);
|
||||||
if (port != null)
|
if (port != null) {
|
||||||
rule.source_port = port;
|
push(rules, {
|
||||||
else
|
network: networks,
|
||||||
continue;
|
source_port: port,
|
||||||
|
action: "route",
|
||||||
|
outbound: runtime_constants.DIRECT_OUTBOUND_TAG
|
||||||
|
});
|
||||||
|
push(rules, {
|
||||||
|
network: networks,
|
||||||
|
port: [ port ],
|
||||||
|
action: "route",
|
||||||
|
outbound: runtime_constants.DIRECT_OUTBOUND_TAG
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
push(rules, {
|
||||||
|
network: networks,
|
||||||
|
action: "route",
|
||||||
|
outbound: runtime_constants.DIRECT_OUTBOUND_TAG
|
||||||
|
});
|
||||||
}
|
}
|
||||||
push(rules, rule);
|
|
||||||
}
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
@@ -175,5 +213,7 @@ return {
|
|||||||
config,
|
config,
|
||||||
target,
|
target,
|
||||||
has_resolve_matchers,
|
has_resolve_matchers,
|
||||||
resolve_rule_for_section
|
resolve_rule_for_section,
|
||||||
|
p2p_direct_rules,
|
||||||
|
normalize_port_entry
|
||||||
};
|
};
|
||||||
@@ -653,7 +653,7 @@ cat >"$WORK_DIR/p2p-isolate-fixture.json" <<'JSON'
|
|||||||
"dns_server": "1.1.1.1",
|
"dns_server": "1.1.1.1",
|
||||||
"service_listen_address": "127.0.0.1",
|
"service_listen_address": "127.0.0.1",
|
||||||
"isolate_p2p": "1",
|
"isolate_p2p": "1",
|
||||||
"p2p_ports": "tcp:6881,udp:6881-6889,udp"
|
"p2p_ports": "tcp:6881,udp:6881-6889,udp,51413"
|
||||||
},
|
},
|
||||||
"section": [
|
"section": [
|
||||||
{
|
{
|
||||||
@@ -662,7 +662,8 @@ cat >"$WORK_DIR/p2p-isolate-fixture.json" <<'JSON'
|
|||||||
"enabled": "1",
|
"enabled": "1",
|
||||||
"action": "outbound",
|
"action": "outbound",
|
||||||
"outbound_json": "{\"type\":\"direct\"}",
|
"outbound_json": "{\"type\":\"direct\"}",
|
||||||
"domain_suffix": [ "example.org" ]
|
"domain_suffix": [ "example.org" ],
|
||||||
|
"excluded_protocol": [ "bittorrent" ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1413,9 +1414,13 @@ assert(mwan3_pinned.route.default_interface == "wan2", "mwan3 pinned interface i
|
|||||||
|
|
||||||
let p2p_isolate = cfg("p2p-isolate");
|
let p2p_isolate = cfg("p2p-isolate");
|
||||||
assert(route_rule(p2p_isolate, r => contains(r.protocol, "bittorrent") && r.outbound == "direct-out") != null, "p2p isolation sniffer rule");
|
assert(route_rule(p2p_isolate, r => contains(r.protocol, "bittorrent") && r.outbound == "direct-out") != null, "p2p isolation sniffer rule");
|
||||||
assert(route_rule(p2p_isolate, r => contains(r.protocol, "tcp") && r.source_port == 6881 && r.outbound == "direct-out") != null, "p2p client tcp port rule");
|
assert(route_rule(p2p_isolate, r => contains(r.network, "tcp") && r.source_port == 6881 && r.outbound == "direct-out") != null, "p2p client tcp source port rule");
|
||||||
assert(route_rule(p2p_isolate, r => contains(r.protocol, "udp") && contains(r.source_port_range, "6881:6889") && r.outbound == "direct-out") != null, "p2p client udp port range rule");
|
assert(route_rule(p2p_isolate, r => contains(r.network, "tcp") && contains(r.port, 6881) && r.outbound == "direct-out") != null, "p2p client tcp dest port rule");
|
||||||
assert(route_rule(p2p_isolate, r => contains(r.protocol, "udp") && r.source_port == null && r.source_port_range == null && r.outbound == "direct-out") != null, "p2p protocol-wide direct rule");
|
assert(route_rule(p2p_isolate, r => contains(r.network, "udp") && contains(r.source_port_range, "6881:6889") && r.outbound == "direct-out") != null, "p2p client udp source port range rule");
|
||||||
|
assert(route_rule(p2p_isolate, r => contains(r.network, "udp") && contains(r.port_range, "6881:6889") && r.outbound == "direct-out") != null, "p2p client udp dest port range rule");
|
||||||
|
assert(route_rule(p2p_isolate, r => contains(r.network, "udp") && r.source_port == null && r.port == null && r.source_port_range == null && r.port_range == null && r.outbound == "direct-out") != null, "p2p protocol-wide direct rule");
|
||||||
|
assert(route_rule(p2p_isolate, r => contains(r.network, "tcp") && contains(r.network, "udp") && r.source_port == 51413 && r.outbound == "direct-out") != null, "p2p plain port source rule");
|
||||||
|
assert(route_rule(p2p_isolate, r => contains(r.network, "tcp") && contains(r.network, "udp") && contains(r.port, 51413) && r.outbound == "direct-out") != null, "p2p plain port dest rule");
|
||||||
|
|
||||||
let lists = cfg("domain-ip-rulesets");
|
let lists = cfg("domain-ip-rulesets");
|
||||||
assert(no_internal_fields(lists), "internal runtime fields stripped from generated config");
|
assert(no_internal_fields(lists), "internal runtime fields stripped from generated config");
|
||||||
|
|||||||
Reference in New Issue
Block a user