Files
NeoTachyon/tachyon/files/usr/lib/config/rule.uc
T

339 lines
10 KiB
Ucode

#!/usr/bin/env ucode
let ip = require("core.ip");
let domain_config = require("config.domain");
function as_string(value) {
return value == null ? "" : "" + value;
}
function strip_list_comment(line) {
line = replace(as_string(line), /[[:space:]]*\/\/.*$/, "");
return replace(line, /[[:space:]]*#.*$/, "");
}
function text_list_values(value, separator_mode) {
let result = [];
separator_mode = as_string(separator_mode);
for (let line in split(as_string(value), "\n")) {
line = strip_list_comment(line);
line = separator_mode == "comma-space"
? replace(line, /[ ,]/g, "\n")
: replace(line, /,/g, "\n");
for (let item in split(line, "\n")) {
item = trim(replace(item, /\r/g, ""));
if (item != "")
push(result, item);
}
}
return result;
}
function whitespace_fields(value) {
let result = [];
for (let item in split(trim(as_string(value)), /[ \t\r\n]+/))
if (item != "")
push(result, item);
return result;
}
function strip_repeated_domain_prefix(prefix, body) {
let marker = prefix + ":";
while (domain_config.ascii_lower(substr(body, 0, length(marker))) == marker)
body = trim(substr(body, length(marker)));
return body;
}
function prefixed_domain_kind_value(value) {
value = trim(as_string(value));
if (value == "")
return null;
let colon = index(value, ":");
let prefix = "";
let body = value;
if (colon > 0) {
let candidate = domain_config.ascii_lower(substr(value, 0, colon));
if (candidate == "full" || candidate == "keyword" || candidate == "regex") {
prefix = candidate;
body = substr(value, colon + 1);
}
else {
return null;
}
}
body = trim(body);
if (prefix != "")
body = strip_repeated_domain_prefix(prefix, body);
if (body == "")
return null;
if (prefix == "") {
let normalized = domain_config.suffix_to_ascii(body);
return normalized == null ? null : { kind: "domain_suffix", value: normalized };
}
if (prefix == "full") {
let normalized = domain_config.suffix_to_ascii(body);
return normalized == null ? null : { kind: "domain", value: normalized };
}
if (prefix == "keyword") {
let normalized = domain_config.keyword_to_ascii(body);
return normalized == null ? null : { kind: "domain_keyword", value: normalized };
}
let normalized = domain_config.regex_to_ascii(body);
return normalized == null ? null : { kind: "domain_regex", value: normalized };
}
function prefixed_domain_value(value, requested_kind) {
let normalized = prefixed_domain_kind_value(value);
requested_kind = as_string(requested_kind);
return normalized != null && normalized.kind == requested_kind ? normalized.value : null;
}
function domain_value_for_key(value, key) {
key = as_string(key);
if (key == "domain" || key == "domain_suffix")
return domain_config.suffix_to_ascii(value);
if (key == "domain_keyword")
return domain_config.keyword_to_ascii(value);
if (key == "domain_regex")
return domain_config.regex_to_ascii(value);
return null;
}
function normalize_domain_subnet_value(value, kind) {
kind = as_string(kind);
if (kind == "domains")
return domain_config.suffix_to_ascii(value);
if (kind == "subnets")
return ip.valid_ip_or_cidr(value) ? value : null;
exit(1);
}
function filter_domain_subnet_values(values, kind) {
let result = [];
kind = as_string(kind);
if (kind != "domains" && kind != "subnets")
exit(1);
for (let value in values) {
let normalized = normalize_domain_subnet_value(value, kind);
if (normalized != null)
push(result, normalized);
}
return result;
}
function condition_text_csv_value(value, kind) {
kind = as_string(kind);
if (kind == "domains")
return join(",", filter_domain_subnet_values(text_list_values(value, "comma-space"), "domains"));
if (kind == "subnets")
return join(",", filter_domain_subnet_values(text_list_values(value, "comma-space"), "subnets"));
return join(",", text_list_values(value, "comma"));
}
function legacy_condition_csv_value(kind, text_mode, conditions_text_mode, text_value, list_value) {
if (int(text_mode || 0) == 1 || int(conditions_text_mode || 0) == 1)
return condition_text_csv_value(text_value, kind);
list_value = as_string(list_value);
if (list_value != "")
return kind == "subnets"
? condition_text_csv_value(list_value, kind)
: replace(list_value, / /g, ",");
text_value = as_string(text_value);
if (text_value != "")
return condition_text_csv_value(text_value, kind);
return replace(list_value, / /g, ",");
}
function combined_domain_csv_value(value, requested_kind) {
let result = [];
for (let item in split(as_string(value), ",")) {
let normalized = prefixed_domain_value(item, requested_kind);
if (normalized != null)
push(result, normalized);
}
return join(",", result);
}
function combined_domain_text_csv_value(value, requested_kind) {
let result = [];
for (let item in text_list_values(value, "comma-space")) {
let normalized = prefixed_domain_value(item, requested_kind);
if (normalized != null)
push(result, normalized);
}
return join(",", result);
}
function concat_csv_values(first, second) {
first = as_string(first);
second = as_string(second);
if (first != "" && second != "")
return first + "," + second;
return first + second;
}
function combined_condition_csv_value(requested_key, legacy_kind, text_mode, conditions_text_mode, text_value, list_value, combined_text_value, combined_list_value) {
let combined_text_values = combined_domain_text_csv_value(combined_text_value, requested_key);
let combined_list_values = combined_domain_csv_value(replace(as_string(combined_list_value), / /g, ","), requested_key);
let combined_values = concat_csv_values(combined_text_values, combined_list_values);
if (requested_key == "domain_suffix")
return combined_values;
let legacy_values = legacy_condition_csv_value(legacy_kind, text_mode, conditions_text_mode, text_value, list_value);
return concat_csv_values(combined_values, legacy_values);
}
function rule_condition_csv_value(key, kind, text_mode, conditions_text_mode, text_value, list_value, combined_text_value, combined_list_value) {
if (key == "domain" || key == "domain_suffix" || key == "domain_keyword" || key == "domain_regex")
return combined_condition_csv_value(key, kind, text_mode, conditions_text_mode, text_value, list_value, combined_text_value, combined_list_value);
return legacy_condition_csv_value(kind, text_mode, conditions_text_mode, text_value, list_value);
}
function normalize_port_number_value(value) {
value = trim(as_string(value));
if (value == "" || match(value, /^[0-9]+$/) == null)
return null;
let number = int(value);
return number >= 1 && number <= 65535 ? number : null;
}
function normalize_port_condition_value(value) {
value = trim(as_string(value));
if (value == "")
return null;
let dash = index(value, "-");
if (dash < 0) {
let number = normalize_port_number_value(value);
return number == null ? null : as_string(number);
}
let start = normalize_port_number_value(substr(value, 0, dash));
let end = normalize_port_number_value(substr(value, dash + 1));
if (start == null || end == null || start > end)
return null;
return start == end ? as_string(start) : sprintf("%d-%d", start, end);
}
function normalize_port_range_value(value) {
value = trim(as_string(value));
let dash = index(value, "-");
if (dash < 0)
return null;
let start = normalize_port_number_value(substr(value, 0, dash));
let end = normalize_port_number_value(substr(value, dash + 1));
if (start == null || end == null || start > end)
return null;
return sprintf("%d:%d", start, end);
}
function append_unique_port(result, seen, item) {
let normalized = normalize_port_condition_value(item);
if (normalized == null || seen[normalized])
return;
seen[normalized] = true;
push(result, normalized);
}
function rule_ports_csv_value(list_values, text_value) {
let result = [];
let seen = {};
let normalized_list = replace(as_string(list_values), /[[:space:]]+/g, "\n");
for (let item in split(normalized_list, "\n"))
append_unique_port(result, seen, item);
for (let item in text_list_values(text_value, "comma-space"))
append_unique_port(result, seen, item);
return join(",", result);
}
let community_subnet_lists = {
twitter: true,
meta: true,
telegram: true,
cloudflare: true,
hetzner: true,
ovh: true,
digitalocean: true,
cloudfront: true,
discord: true,
roblox: true
};
function community_service_has_subnet_list(value) {
return community_subnet_lists[as_string(value)] == true;
}
function filter_community_subnet_lists_value(value) {
let result = [];
for (let item in whitespace_fields(value))
if (community_service_has_subnet_list(item))
push(result, item);
return join(" ", result);
}
function has_community_subnet_list(value) {
for (let item in whitespace_fields(value))
if (community_service_has_subnet_list(item))
return true;
return false;
}
return {
text_list_values,
normalize_port_number_value,
normalize_port_condition_value,
normalize_port_range_value,
combined_domain_csv_value,
combined_domain_text_csv_value,
prefixed_domain_kind_value,
prefixed_domain_value,
domain_value_for_key,
normalize_domain_subnet_value,
legacy_condition_csv_value,
rule_condition_csv_value,
rule_ports_csv_value,
community_service_has_subnet_list,
filter_community_subnet_lists_value,
has_community_subnet_list
};