Files
NeoTachyon/tachyon/files/usr/lib/providers/olcrtc/validator.uc
T
Dushnilin 26431f50d3 feat: per-section MAC/DNS exclusions, boot latency sweep, WDTT/OlcRTC providers, DNS hijack socket collision fix
Features:
- Per-Section MAC Exclusions: nft, generator_routes, section.js, i18n
- Per-Section Routed DNS: connections, generator_routes, nft, validator,
  types.ts, section.js, state.uc, reload.uc
- Boot Latency Sweep: ui.uc latency boot sweep with fixture dispatchers
- WDTT provider: full backend (common, validator, runtime), sing-box
  outbound, diagnostics, component install, updater, i18n, section.js UI
- OlcRTC provider: full backend (common, validator, runtime), sing-box
  outbound, diagnostics, component install, updater, i18n, section.js UI
- WireGuard-over-TURN tunnel (WDTT) and TCP-over-WebRTC tunnel (OlcRTC)
  as sidecar processes with local SOCKS5, routed through sing-box outbound

Bug fixes:
- route.uc: restrict hijack-dns to local DNS inbounds only (dns-in,
  source-dns-in, dns-health-*) — fixes socket collision between sing-box
  and dnsmasq on port 53 via SO_REUSEADDR transparent write-back sockets
- runtime.uc: add Port 53 socket collision diagnostic with auto-fix
  (sing-box restart to clear parasitic UNCONN sockets with Recv-Q)
- runtime.uc: add dnsmasq dns_redirect check with auto-fix — disables
  firewall4 DNAT that causes TProxy to rewrite destination to router :53
- failover.uc: add wdtt/olcrtc to failover candidate actions
- telegram.uc: show WDTT/OlcRTC installed status in /info

Tests: 135/155 shell + 591/591 frontend
2026-09-11 18:04:03 +03:00

204 lines
6.1 KiB
Ucode

let common = require("core.common");
let fs = require("fs");
let as_string = common.as_string;
const OLCRTC_PROVIDERS = [ "jitsi", "telemost", "wbstream" ];
const OLCRTC_TRANSPORTS = [ "datachannel", "vp8channel", "seichannel", "videochannel" ];
function yaml_quote(value) {
value = as_string(value);
if (value == "")
return "''";
if (match(value, /^[a-zA-Z0-9_./:-]+$/) != null)
return value;
return "'" + replace(value, "'", "''") + "'";
}
function parse_uri(value) {
value = trim(as_string(value));
if (substr(value, 0, 9) != "olcrtc://")
return { valid: false, reason: "URI must start with olcrtc://" };
let rest = substr(value, 9);
let dollar_idx = rindex(rest, "$");
let mimo = "";
if (dollar_idx >= 0) {
mimo = substr(rest, dollar_idx + 1);
rest = substr(rest, 0, dollar_idx);
}
let hash_idx = rindex(rest, "#");
let crypto_key = "";
if (hash_idx >= 0) {
crypto_key = substr(rest, hash_idx + 1);
rest = substr(rest, 0, hash_idx);
}
let at_idx = index(rest, "@");
let room_id = "";
let provider_transport = rest;
if (at_idx >= 0) {
provider_transport = substr(rest, 0, at_idx);
room_id = substr(rest, at_idx + 1);
}
let q_idx = index(provider_transport, "?");
let provider = "";
let transport_with_payload = provider_transport;
if (q_idx >= 0) {
provider = substr(provider_transport, 0, q_idx);
transport_with_payload = substr(provider_transport, q_idx + 1);
}
let payload = "";
let transport = transport_with_payload;
let lt_idx = index(transport_with_payload, "<");
let gt_idx = index(transport_with_payload, ">");
if (lt_idx >= 0 && gt_idx > lt_idx) {
transport = substr(transport_with_payload, 0, lt_idx);
payload = substr(transport_with_payload, lt_idx + 1, gt_idx - lt_idx - 1);
}
if (!array_contains(OLCRTC_PROVIDERS, provider))
return { valid: false, reason: "provider must be one of: " + join(", ", OLCRTC_PROVIDERS) };
if (!array_contains(OLCRTC_TRANSPORTS, transport))
return { valid: false, reason: "transport must be one of: " + join(", ", OLCRTC_TRANSPORTS) };
if (room_id == "")
return { valid: false, reason: "room_id is required" };
if (crypto_key != "" && length(crypto_key) != 64)
return { valid: false, reason: "crypto_key must be 64 hex characters" };
return {
valid: true,
provider: provider,
transport: transport,
room_id: room_id,
crypto_key: crypto_key,
mimo: mimo,
payload: payload
};
}
function valid_hex_key(value) {
value = trim(as_string(value));
if (value == "" || value == null)
return true;
return length(value) == 64 && match(value, /^[0-9a-fA-F]+$/) != null;
}
function valid_number(value, min, max) {
value = as_string(value);
if (value == "" || value == null)
return true;
let num = int(value);
if (num == null || isNaN(num))
return false;
return num >= min && num <= max;
}
function valid_url(value) {
value = trim(as_string(value));
if (value == "" || value == null)
return true;
return match(value, /^https?:\/\/[^ \t\r\n]+$/) != null;
}
function valid_host_port(value) {
value = trim(as_string(value));
if (value == "" || value == null)
return true;
return match(value, /^[^ \t\r\n:]+:[0-9]{1,5}$/) != null;
}
function list_values(value) {
if (type(value) == "array")
return value;
value = trim(as_string(value));
if (value == "" || value == null)
return [];
return split(value, " \t\n\r");
}
function validation_result(valid, message, options) {
let result = { valid: valid, message: message || "" };
if (options != null)
for (let key in options)
result[key] = options[key];
return result;
}
function validate_section(section) {
let errors = [];
let provider = as_string(section.provider || "");
if (provider != "" && !array_contains(OLCRTC_PROVIDERS, provider))
errors.push("provider must be one of: " + join(", ", OLCRTC_PROVIDERS));
let transport = as_string(section.transport || "");
if (transport != "" && !array_contains(OLCRTC_TRANSPORTS, transport))
errors.push("transport must be one of: " + join(", ", OLCRTC_TRANSPORTS));
let crypto_key = as_string(section.crypto_key || "");
if (crypto_key != "" && !valid_hex_key(crypto_key))
errors.push("crypto_key must be 64 hex characters");
let socks_port = as_string(section.socks_port || "");
if (socks_port != "" && !valid_number(socks_port, 1, 65535))
errors.push("socks_port must be 1-65535");
let dns_server = as_string(section.dns_server || "");
if (dns_server != "" && !valid_host_port(dns_server))
errors.push("dns_server must be in HOST:PORT format");
let subscription_links = list_values(section.subscription_links);
for (let link in subscription_links) {
link = trim(as_string(link));
if (link == "")
continue;
if (substr(link, 0, 9) == "olcrtc://") {
let parsed = parse_uri(link);
if (!parsed.valid)
errors.push("invalid olcrtc:// URI: " + parsed.reason);
}
else if (!valid_url(link))
errors.push("subscription link must be olcrtc:// URI or HTTP URL: " + link);
}
return errors;
}
function validate_section_json() {
let input = read_stdin();
let data = object_or_empty(json(input));
let errors = validate_section(data);
if (length(errors) > 0)
write_json({ valid: false, message: join("; ", errors) });
else
write_json({ valid: true, message: "" });
return length(errors) == 0;
}
function read_stdin() {
let input = fs.open("/dev/stdin", "r");
if (!input)
return "";
let data = input.read("all");
input.close();
return data == null ? "" : data;
}
function write_json(value) {
printf("%s\n", sprintf("%J", value));
}
return {
validate_section,
validate_section_json,
parse_uri,
valid_hex_key,
providers: OLCRTC_PROVIDERS,
transports: OLCRTC_TRANSPORTS
};