Files
NeoTachyon/tachyon/files/usr/lib/diagnostics/fuzzer.uc
T

1517 lines
56 KiB
Ucode

#!/usr/bin/env ucode
let fs = require("fs");
let common = require("core.common");
let uci_core = require("core.uci");
let rag = require("diagnostics.rag");
let as_string = common.as_string;
let read_json_file = common.read_json_file;
let write_json_file = common.write_json_file;
let command_from_args = common.command_from_args;
let command_output = common.command_output;
let command_status = common.command_status;
let command_success = common.command_success;
let command_success_from_args = common.command_success_from_args;
let shell_quote = common.shell_quote;
let object_or_empty = common.object_or_empty;
let array_or_empty = common.array_or_empty;
const CONFIG_NAME = getenv("TACHYON_CONFIG_NAME") || "tachyon";
const STATE_DIR = getenv("TACHYON_FUZZER_STATE_DIR") || "/var/run/tachyon";
const STATE_FILE = STATE_DIR + "/fuzzer-state.json";
const PID_FILE = STATE_DIR + "/fuzzer-worker.pid";
const BYEDPI_PORT = 11089;
const NFQUEUE_QNUM_ZAPRET = 298;
const NFQUEUE_QNUM_ZAPRET2 = 299;
const FUZZER_FWMARK = "0x40000000";
function resolve_binary(paths) {
for (let p in paths) {
if (p && fs.stat(p) != null)
return p;
}
return null;
}
function get_zapret2_bin() {
return resolve_binary([
getenv("ZAPRET2_NFQWS2_BIN"),
"/opt/zapret2/nfq2/nfqws2",
"/opt/zapret2/nfqws2",
"/usr/bin/nfqws2"
]);
}
function get_zapret_bin() {
return resolve_binary([
getenv("ZAPRET_NFQWS_BIN"),
"/opt/zapret/nfq/nfqws",
"/opt/zapret/nfqws",
"/usr/bin/nfqws"
]);
}
function get_byedpi_bin() {
return resolve_binary([
getenv("BYEDPI_BIN"),
"/opt/byedpi/ciadpi",
"/usr/bin/ciadpi"
]);
}
function validate_strategy_args(engine, args_val) {
args_val = trim(as_string(args_val));
if (args_val == "") return false;
engine = lc(as_string(engine));
try {
if (engine == "zapret2") {
let val = require("providers.zapret2.validator");
let res = val.validate_strategy("nfqws2", args_val, "");
return res ? res.valid == true : true;
} else if (engine == "zapret") {
let val = require("providers.zapret.validator");
let res = val.validate_strategy("nfqws", args_val, "");
return res ? res.valid == true : true;
} else if (engine == "byedpi") {
let val = require("providers.byedpi.validator");
let res = val.validate_strategy(args_val, "");
return res ? res.valid == true : true;
}
} catch (e) {
return true;
}
return true;
}
const PATTERNS_FILE = "/etc/tachyon/fuzzer_patterns.json";
const DEFAULT_PATTERNS = {
zapret2: {
splits: [ "1", "2", "3", "midsld", "sniext+4", "1,midsld", "1,sniext+2" ],
foolings: [ "badseq", "md5sig", "badack", "datanoack", "fakeddrop" ],
ttls: [ 2, 3, 4, 5, 6, 8 ],
seqovls: [ "1", "2" ],
wsizes: [ "1" ],
payloads: [ "tls_client_hello", "http_req", "quic_initial" ]
},
zapret: {
splits: [ "1", "2", "midsld", "sniext+4", "1,midsld" ],
foolings: [ "badseq", "md5sig", "badack", "datanoack" ],
ttls: [ 2, 3, 4, 6, 8 ],
split_modes: [ "split2", "disorder2", "fake,split2", "fake,disorder2" ]
},
byedpi: {
splits: [ "1", "2", "1+sniext", "midsld" ],
disorders: [ "1", "2" ],
ttls: [ 2, 3, 4, 6, 8 ],
oobs: [ "1", "2" ],
autos: [ "t,r,a,s", "r,s", "t,a" ],
tlsrecs: [ "1+sniext" ],
ipfrags: [ "24" ]
},
custom_strategies: []
};
function get_patterns_config() {
let custom = read_json_file(PATTERNS_FILE);
if (custom && type(custom) == "object") {
return {
zapret2: custom.zapret2 || DEFAULT_PATTERNS.zapret2,
zapret: custom.zapret || DEFAULT_PATTERNS.zapret,
byedpi: custom.byedpi || DEFAULT_PATTERNS.byedpi,
custom_strategies: custom.custom_strategies || []
};
}
return DEFAULT_PATTERNS;
}
function save_patterns_config(cfg_obj) {
if (!cfg_obj || type(cfg_obj) != "object") {
print(sprintf("%J\n", { success: false, error: "Invalid patterns configuration object" }));
return;
}
common.ensure_dir("/etc/tachyon");
write_json_file(PATTERNS_FILE, cfg_obj);
print(sprintf("%J\n", { success: true, message: "Patterns configuration saved successfully" }));
}
function reset_patterns_config() {
try { fs.unlink(PATTERNS_FILE); } catch(e) {}
print(sprintf("%J\n", { success: true, message: "Patterns configuration reset to factory defaults", patterns: DEFAULT_PATTERNS }));
}
// Target Suites & Definitions
const TARGET_SUITES = {
youtube_suite: {
name: "YouTube Full Suite (Web + Static CDN + 4K Stream)",
urls: [
{ name: "Web Interface", url: "https://www.youtube.com", weight: 30 },
{ name: "Static Assets (i.ytimg)", url: "https://i.ytimg.com/generate_204", weight: 20 },
{ name: "GoogleVideo Stream Chunk", url: "https://rr1---sn-4g5ednss.googlevideo.com/generate_204", weight: 50 }
]
},
discord_suite: {
name: "Discord Full Suite (API + WSS Gateway + CDN)",
urls: [
{ name: "API Gateway", url: "https://discord.com/api/v9/gateway", weight: 40 },
{ name: "Media Attachments CDN", url: "https://media.discordapp.net/", weight: 30 },
{ name: "Global Assets CDN", url: "https://cdn.discordapp.com/generate_204", weight: 30 }
]
},
instagram_suite: {
name: "Instagram / Meta Suite (Web + Static CDN)",
urls: [
{ name: "Web Interface", url: "https://www.instagram.com", weight: 50 },
{ name: "CDN Static Assets", url: "https://static.cdninstagram.com/rsrc.php/v3/y4/r/dukNmT7A9dd.webp", weight: 50 }
]
},
telegram_suite: {
name: "Telegram Suite (Web + API)",
urls: [
{ name: "Web App", url: "https://web.telegram.org", weight: 50 },
{ name: "Bot API", url: "https://api.telegram.org", weight: 50 }
]
},
rutracker_suite: {
name: "RuTracker Suite (HTTP / HTTPS)",
urls: [
{ name: "Main Portal", url: "https://rutracker.org", weight: 60 },
{ name: "CDN Static Logo", url: "https://static.rutracker.cc/logo/logo-3.png", weight: 40 }
]
}
};
const TARGET_URLS = {
youtube_suite: "https://www.youtube.com",
youtube: "https://www.youtube.com",
youtube_web: "https://www.youtube.com",
discord_suite: "https://discord.com/api/v9/gateway",
discord: "https://discord.com/api/v9/gateway",
instagram_suite: "https://www.instagram.com",
instagram: "https://www.instagram.com",
rutracker_suite: "https://rutracker.org",
rutracker: "https://rutracker.org",
telegram_suite: "https://web.telegram.org",
telegram: "https://web.telegram.org",
quic_http3: "https://www.google.com"
};
// Strategy Matrices (Expanded Production Suite: 38 strategies across engines)
const STRATEGIES_ZAPRET2 = [
{
id: "z2_fake_ttl4_badseq",
name: "Fake (TTL=4, BadSeq) + Multisplit",
engine: "zapret2",
args: "--lua-desync=fake:ttl=4:fooling=badseq --lua-desync=multisplit:pos=1,midsld",
description: "Low-TTL fake ClientHello with badseq fooling and multisplit segmentation."
},
{
id: "z2_fake_ttl5_md5sig",
name: "Fake (TTL=5, MD5Sig) + Multisplit",
engine: "zapret2",
args: "--lua-desync=fake:ttl=5:fooling=md5sig --lua-desync=multisplit:pos=1,midsld",
description: "MD5Sig TCP option drops packet at DPI while reaching end server."
},
{
id: "z2_fake_ttl4_badack",
name: "Fake (TTL=4, BadACK) + Multisplit",
engine: "zapret2",
args: "--lua-desync=fake:ttl=4:fooling=badack --lua-desync=multisplit:pos=1,sniext+2",
description: "BadACK fooling invalidates packet in DPI state tracking."
},
{
id: "z2_fake_ttl3_md5sig",
name: "Fake (TTL=3, MD5Sig) + Multisplit",
engine: "zapret2",
args: "--lua-desync=fake:ttl=3:fooling=md5sig --lua-desync=multisplit:pos=1,midsld",
description: "Aggressive low-TTL MD5Sig injection for close TSPU hops."
},
{
id: "z2_yt_multisplit_midsld",
name: "YouTube 4K Multisplit + MidSLD",
engine: "zapret2",
args: "--lua-desync=multisplit:pos=1,midsld:seqovl=1:fooling=badseq",
description: "Optimized for GoogleVideo 4K chunk streams and TSPU TLS desync."
},
{
id: "z2_yt_multisplit_sniext",
name: "SNI Extension Split + Badseq",
engine: "zapret2",
args: "--lua-desync=multisplit:pos=1,sniext+4:seqovl=1:fooling=badseq",
description: "Splits deep into SNI extensions to fool next-gen DPI signatures."
},
{
id: "z2_fake_badseq_mid",
name: "Fake Packet (TTL=8) + MidSLD Split",
engine: "zapret2",
args: "--lua-desync=fake:ttl=8:fooling=badseq --lua-desync=multisplit:pos=midsld",
description: "Injects fake ClientHello before segmented payload."
},
{
id: "z2_wsize_multisplit",
name: "Window Size Clamp (wsize=1)",
engine: "zapret2",
args: "--lua-desync=multisplit:pos=1,midsld:wsize=1:fooling=badseq",
description: "Forces single-byte TCP window segments to evade reassembly."
},
{
id: "z2_wsize_seqovl_combo",
name: "Window Clamp (wsize=1) + SeqOvl",
engine: "zapret2",
args: "--lua-desync=multisplit:pos=1,midsld:wsize=1:seqovl=1:fooling=badseq",
description: "Combines 1-byte window clamp with sequence overlap."
},
{
id: "z2_aggressive_combo",
name: "Aggressive Triple-Split + SeqOvl 2",
engine: "zapret2",
args: "--lua-desync=multisplit:pos=1,midsld,sniext+2:seqovl=2:fooling=badseq",
description: "High-entropy triple fragmentation for heavily filtered regions."
},
{
id: "z2_split2_pos1",
name: "Classic Split2 (pos=1)",
engine: "zapret2",
args: "--lua-desync=split2:pos=1:fooling=badseq",
description: "Standard 2-fragment desync for compatibility."
},
{
id: "z2_disorder2_pos1",
name: "Classic Disorder2 (pos=1)",
engine: "zapret2",
args: "--lua-desync=disorder2:pos=1:fooling=badseq",
description: "Sends out-of-order segment with badseq fooling."
},
{
id: "z2_fake_datanoack",
name: "Fake (TTL=8, DataNoAck) + Split2",
engine: "zapret2",
args: "--lua-desync=fake:ttl=8:fooling=datanoack --lua-desync=split2:pos=1",
description: "DataNoAck fooling confuses stateful DPI without triggering ACK RST."
},
{
id: "z2_multiprofile_discord",
name: "Discord Multi-Profile (TCP + UDP)",
engine: "zapret2",
args: "--filter-tcp=443 --lua-desync=multisplit:pos=1,midsld:seqovl=1 --new --filter-udp=50000-65535 --lua-desync=fake:ttl=8",
description: "Combined profile: multisplit for HTTPS/API and fake UDP for Discord Voice."
},
{
id: "z2_discord_udp",
name: "Discord Voice UDP Desync",
engine: "zapret2",
args: "--filter-udp=50000-65535 --lua-desync=fake:ttl=8",
description: "UDP fake packet desync for Discord RTC and Voice channels."
},
{
id: "z2_quic_http3_udp",
name: "QUIC / HTTP3 UDP Fake Desync",
engine: "zapret2",
args: "--filter-udp=443 --lua-desync=fake:ttl=8",
description: "UDP fake desync for QUIC/HTTP3 protocol bypass."
}
];
const STRATEGIES_ZAPRET = [
{
id: "z1_split2_pos2",
name: "Standard Split2 (pos=2)",
engine: "zapret",
args: "--dpi-desync=split2 --dpi-desync-split-pos=2",
description: "Base HTTP/TLS split inside SNI header."
},
{
id: "z1_split2_pos1",
name: "Standard Split2 (pos=1)",
engine: "zapret",
args: "--dpi-desync=split2 --dpi-desync-split-pos=1",
description: "1-byte TLS ClientHello split."
},
{
id: "z1_disorder2_badseq",
name: "Disorder2 + BadSeq (pos=1)",
engine: "zapret",
args: "--dpi-desync=disorder2 --dpi-desync-split-pos=1 --dpi-desync-fooling=badseq",
description: "Sends out-of-order packets with invalid TCP sequence fooling."
},
{
id: "z1_fake_split2_ttl8",
name: "Fake SNI + Split2 (TTL=8)",
engine: "zapret",
args: "--dpi-desync=fake,split2 --dpi-desync-split-pos=1 --dpi-desync-ttl=8 --dpi-desync-fooling=badseq",
description: "Sends TTL=8 fake packet followed by segmented ClientHello."
},
{
id: "z1_fake_split2_ttl6",
name: "Fake SNI + Split2 (TTL=6)",
engine: "zapret",
args: "--dpi-desync=fake,split2 --dpi-desync-split-pos=1 --dpi-desync-ttl=6 --dpi-desync-fooling=badseq",
description: "Sends TTL=6 fake packet for closer TSPU hops."
},
{
id: "z1_fake_split2_ttl4",
name: "Fake SNI + Split2 (TTL=4)",
engine: "zapret",
args: "--dpi-desync=fake,split2 --dpi-desync-split-pos=1 --dpi-desync-ttl=4 --dpi-desync-fooling=badseq",
description: "Low TTL=4 fake packet for nearest TSPU filters."
},
{
id: "z1_fake_disorder2_ttl8",
name: "Fake SNI + Disorder2 (TTL=8)",
engine: "zapret",
args: "--dpi-desync=fake,disorder2 --dpi-desync-split-pos=1 --dpi-desync-ttl=8 --dpi-desync-fooling=badseq",
description: "Sends fake packet and disorders real segments."
},
{
id: "z1_seqovl_split2_1",
name: "Sequence Overlap (SeqOvl=1)",
engine: "zapret",
args: "--dpi-desync=split2 --dpi-desync-split-seqovl=1 --dpi-desync-fooling=badseq",
description: "1-byte overlapping TCP payload to confuse stateful DPI."
},
{
id: "z1_seqovl_split2_2",
name: "Sequence Overlap (SeqOvl=2)",
engine: "zapret",
args: "--dpi-desync=split2 --dpi-desync-split-seqovl=2 --dpi-desync-fooling=badseq",
description: "2-byte overlapping TCP payload for aggressive DPI desync."
},
{
id: "z1_md5sig_disorder",
name: "MD5Sig Fooling + Disorder (TTL=6)",
engine: "zapret",
args: "--dpi-desync=fake,disorder2 --dpi-desync-fooling=md5sig --dpi-desync-ttl=6",
description: "Injects TCP MD5 signature option to trigger DPI packet drop."
},
{
id: "z1_badack_disorder",
name: "BadACK Fooling + Disorder (TTL=8)",
engine: "zapret",
args: "--dpi-desync=fake,disorder2 --dpi-desync-fooling=badack --dpi-desync-ttl=8",
description: "Injects BadACK sequence to break TCP state tracking."
},
{
id: "z1_fake_midsld_split",
name: "Fake + MidSLD Split (TTL=8)",
engine: "zapret",
args: "--dpi-desync=fake,split2 --dpi-desync-split-pos=midsld --dpi-desync-ttl=8 --dpi-desync-fooling=badseq",
description: "Splits in middle of domain name with fake injection."
}
];
const STRATEGIES_BYEDPI = [
{
id: "bd_auto_tr_d2",
name: "ByeDPI Auto (t,r,a,s) + Disorder",
engine: "byedpi",
args: "-o 2 --auto=t,r,a,s -d 2",
description: "Adaptive ByeDPI auto-mode with disorder and OOB."
},
{
id: "bd_auto_tr_s1",
name: "ByeDPI Auto (t,r,a,s) + Split",
engine: "byedpi",
args: "-o 1 --auto=t,r,a,s -s 1",
description: "Adaptive ByeDPI auto-mode with 1-byte split."
},
{
id: "bd_disorder_fake_ttl8",
name: "Disorder + Fake (TTL=8)",
engine: "byedpi",
args: "--split 1 --disorder 1 --fake -1 --ttl 8",
description: "1-byte split with reverse disorder and fake handshake packet."
},
{
id: "bd_disorder_fake_ttl6",
name: "Disorder + Fake (TTL=6)",
engine: "byedpi",
args: "--split 1 --disorder 1 --fake -1 --ttl 6",
description: "1-byte split with fake TTL=6 for intermediate hops."
},
{
id: "bd_disorder_fake_ttl4",
name: "Disorder + Fake (TTL=4)",
engine: "byedpi",
args: "--split 1 --disorder 1 --fake -1 --ttl 4",
description: "1-byte split with low fake TTL=4."
},
{
id: "bd_midsld_fake_frag_t6",
name: "SNI Extension + Fake (TTL=6)",
engine: "byedpi",
args: "-s 1+sniext -f -1 -t 6",
description: "SNI extension split with low-TTL fake payload."
},
{
id: "bd_midsld_fake_frag_t8",
name: "SNI Extension + Fake (TTL=8)",
engine: "byedpi",
args: "-s 1+sniext -f -1 -t 8",
description: "SNI extension split with fake TTL=8."
},
{
id: "bd_tls_sni_split2",
name: "TLS SNI Split + Disorder (pos=2)",
engine: "byedpi",
args: "--split 2 --disorder 2",
description: "Direct TLS SNI offset split with out-of-order delivery."
},
{
id: "bd_tls_sni_split1",
name: "TLS SNI Split + Disorder (pos=1)",
engine: "byedpi",
args: "--split 1 --disorder 1",
description: "1-byte TLS ClientHello split with disorder."
},
{
id: "bd_tlsrec_sniext",
name: "TLS Record Split (1+sniext)",
engine: "byedpi",
args: "--tlsrec 1+sniext --split 1",
description: "Fragments TLS Record header before SNI extension."
},
{
id: "bd_ip_frag_24",
name: "IP Fragmentation (24 bytes)",
engine: "byedpi",
args: "--ip-frag 24 --split 1",
description: "Network layer IP fragmentation on 24-byte boundary."
},
{
id: "bd_fake_sniext_disorder",
name: "Aggressive Fake (TTL=8) + SNIExt",
engine: "byedpi",
args: "--fake -1 --ttl 8 --split 1+sniext --disorder 1",
description: "Fake handshake with SNI extension split and disorder."
}
];
function generate_combinatorial_zapret2() {
let cfg = get_patterns_config();
let p = cfg.zapret2 || DEFAULT_PATTERNS.zapret2;
let list = [];
let seen = {};
let add = function(name, args, desc) {
args = trim(as_string(args));
if (args == "" || seen[args]) return;
if (!validate_strategy_args("zapret2", args)) return;
seen[args] = true;
push(list, {
id: sprintf("z2_comb_%d", length(list) + 1),
name: name,
engine: "zapret2",
args: args,
description: desc
});
};
for (let s in STRATEGIES_ZAPRET2) add(s.name, s.args, s.description);
if (cfg.custom_strategies && length(cfg.custom_strategies) > 0) {
for (let cs in cfg.custom_strategies) {
if (cs && cs.engine == "zapret2" && cs.args) {
add(cs.name || "Custom Zapret v2", cs.args, cs.description || "User custom strategy");
}
}
}
let splits = p.splits || [ "1", "2", "3", "midsld", "sniext+4", "1,midsld", "1,sniext+2" ];
let foolings = p.foolings || [ "badseq", "md5sig", "badack", "datanoack", "fakeddrop" ];
let ttls = p.ttls || [ 2, 3, 4, 5, 6, 8 ];
let seqovls = p.seqovls || [ "1", "2" ];
let wsizes = p.wsizes || [ "1" ];
for (let pos in splits) {
for (let fooling in foolings) {
add(sprintf("Multisplit (pos=%s, %s)", pos, fooling),
sprintf("--lua-desync=multisplit:pos=%s:fooling=%s", pos, fooling),
"Multisplit position and fooling method");
for (let sq in seqovls) {
add(sprintf("Multisplit + SeqOvl %s (pos=%s, %s)", sq, pos, fooling),
sprintf("--lua-desync=multisplit:pos=%s:seqovl=%s:fooling=%s", pos, sq, fooling),
"Multisplit with sequence overlap");
}
for (let w in wsizes) {
add(sprintf("Multisplit + Window %s (pos=%s, %s)", w, pos, fooling),
sprintf("--lua-desync=multisplit:pos=%s:wsize=%s:fooling=%s", pos, w, fooling),
"Multisplit with TCP window size clamping");
}
}
}
for (let ttl in ttls) {
for (let fooling in foolings) {
for (let pos in splits) {
add(sprintf("Fake (TTL=%d, %s) + Multisplit (pos=%s)", ttl, fooling, pos),
sprintf("--lua-desync=fake:ttl=%d:fooling=%s --lua-desync=multisplit:pos=%s", ttl, fooling, pos),
"Low-TTL fake injection followed by multisplit payload");
add(sprintf("Fake (TTL=%d, %s) + Split2 (pos=%s)", ttl, fooling, pos),
sprintf("--lua-desync=fake:ttl=%d:fooling=%s --lua-desync=split2:pos=%s", ttl, fooling, pos),
"Low-TTL fake injection followed by 2-part split");
}
}
}
return list;
}
function generate_combinatorial_zapret() {
let cfg = get_patterns_config();
let p = cfg.zapret || DEFAULT_PATTERNS.zapret;
let list = [];
let seen = {};
let add = function(name, args, desc) {
args = trim(as_string(args));
if (args == "" || seen[args]) return;
if (!validate_strategy_args("zapret", args)) return;
seen[args] = true;
push(list, {
id: sprintf("z1_gen_%d", length(list) + 1),
name: name,
engine: "zapret",
args: args,
description: desc
});
};
for (let s in STRATEGIES_ZAPRET) add(s.name, s.args, s.description);
if (cfg.custom_strategies && length(cfg.custom_strategies) > 0) {
for (let cs in cfg.custom_strategies) {
if (cs && cs.engine == "zapret" && cs.args) {
add(cs.name || "Custom Zapret v1", cs.args, cs.description || "User custom strategy");
}
}
}
let modes = p.split_modes || [ "split2", "disorder2", "fake,split2", "fake,disorder2" ];
let positions = p.splits || [ "1", "2", "midsld" ];
let foolings = p.foolings || [ "badseq", "md5sig", "badack", "datanoack" ];
let ttls = p.ttls || [ 2, 3, 4, 6, 8 ];
for (let mode in modes) {
for (let pos in positions) {
for (let fooling in foolings) {
if (index(mode, "fake") >= 0) {
for (let ttl in ttls) {
add(sprintf("%s (pos=%s, TTL=%d, %s)", mode, pos, ttl, fooling),
sprintf("--dpi-desync=%s --dpi-desync-split-pos=%s --dpi-desync-ttl=%d --dpi-desync-fooling=%s", mode, pos, ttl, fooling),
"Fake desync with split pos and fooling");
}
} else {
add(sprintf("%s (pos=%s, %s)", mode, pos, fooling),
sprintf("--dpi-desync=%s --dpi-desync-split-pos=%s --dpi-desync-fooling=%s", mode, pos, fooling),
"Desync with split pos and fooling");
add(sprintf("%s + SeqOvl (pos=%s, %s)", mode, pos, fooling),
sprintf("--dpi-desync=%s --dpi-desync-split-pos=%s --dpi-desync-split-seqovl=1 --dpi-desync-fooling=%s", mode, pos, fooling),
"Desync with sequence overlap");
}
}
}
}
return list;
}
function generate_combinatorial_byedpi() {
let cfg = get_patterns_config();
let p = cfg.byedpi || DEFAULT_PATTERNS.byedpi;
let list = [];
let seen = {};
let add = function(name, args, desc) {
args = trim(as_string(args));
if (args == "" || seen[args]) return;
if (!validate_strategy_args("byedpi", args)) return;
seen[args] = true;
push(list, {
id: sprintf("bd_gen_%d", length(list) + 1),
name: name,
engine: "byedpi",
args: args,
description: desc
});
};
for (let s in STRATEGIES_BYEDPI) add(s.name, s.args, s.description);
if (cfg.custom_strategies && length(cfg.custom_strategies) > 0) {
for (let cs in cfg.custom_strategies) {
if (cs && cs.engine == "byedpi" && cs.args) {
add(cs.name || "Custom ByeDPI", cs.args, cs.description || "User custom strategy");
}
}
}
let splits = p.splits || [ "1", "2", "1+sniext", "midsld" ];
let disorders = p.disorders || [ "1", "2" ];
let ttls = p.ttls || [ 2, 3, 4, 6, 8 ];
let oobs = p.oobs || [ "1", "2" ];
let autos = p.autos || [ "t,r,a,s", "r,s", "t,a" ];
let tlsrecs = p.tlsrecs || [ "1+sniext" ];
let ipfrags = p.ipfrags || [ "24" ];
for (let a in autos) {
for (let o in oobs) {
for (let d in disorders) {
add(sprintf("Auto (%s) + OOB=%s + Disorder=%s", a, o, d),
sprintf("-o %s --auto=%s -d %s", o, a, d),
"Adaptive auto mode with OOB and disorder");
}
for (let s in splits) {
add(sprintf("Auto (%s) + OOB=%s + Split=%s", a, o, s),
sprintf("-o %s --auto=%s -s %s", o, a, s),
"Adaptive auto mode with OOB and split");
}
}
}
for (let ttl in ttls) {
for (let s in splits) {
for (let d in disorders) {
add(sprintf("Split=%s + Disorder=%s + Fake (TTL=%d)", s, d, ttl),
sprintf("--split %s --disorder %s --fake -1 --ttl %d", s, d, ttl),
"Fake injection with split and disorder");
}
}
}
for (let s in splits) {
for (let tr in tlsrecs) {
add(sprintf("TLS-Rec (%s) + Split=%s", tr, s),
sprintf("--tlsrec %s --split %s", tr, s),
"TLS record boundary fragmentation");
}
for (let ipf in ipfrags) {
add(sprintf("IP-Frag (%s) + Split=%s", ipf, s),
sprintf("--ip-frag %s --split %s", ipf, s),
"IP packet fragmentation");
}
}
return list;
}
function get_strategies_for_engine(engine, mode) {
engine = lc(as_string(engine));
mode = lc(trim(as_string(mode || "presets")));
let cfg = get_patterns_config();
if (mode == "custom" || mode == "user") {
let custom_list = [];
for (let cs in cfg.custom_strategies) {
if (cs && (engine == "all" || cs.engine == engine) && cs.args) {
push(custom_list, {
id: cs.id || sprintf("custom_%d", length(custom_list) + 1),
name: cs.name || "Custom Strategy",
engine: cs.engine || engine,
args: cs.args,
description: cs.description || ""
});
}
}
return custom_list;
}
if (mode == "combinatorial" || mode == "deep_fuzz" || mode == "deep") {
if (engine == "zapret2") return generate_combinatorial_zapret2();
if (engine == "zapret") return generate_combinatorial_zapret();
if (engine == "byedpi") return generate_combinatorial_byedpi();
if (engine == "all") {
let combined = [];
for (let s in generate_combinatorial_zapret2()) push(combined, s);
for (let s in generate_combinatorial_zapret()) push(combined, s);
for (let s in generate_combinatorial_byedpi()) push(combined, s);
return combined;
}
}
let base = [];
if (engine == "zapret2") base = STRATEGIES_ZAPRET2;
else if (engine == "zapret") base = STRATEGIES_ZAPRET;
else if (engine == "byedpi") base = STRATEGIES_BYEDPI;
else if (engine == "all") {
for (let s in STRATEGIES_ZAPRET2) push(base, s);
for (let s in STRATEGIES_ZAPRET) push(base, s);
for (let s in STRATEGIES_BYEDPI) push(base, s);
}
let result = [];
for (let s in base) push(result, s);
for (let cs in cfg.custom_strategies) {
if (cs && (engine == "all" || cs.engine == engine) && cs.args) {
push(result, {
id: cs.id || sprintf("custom_%d", length(result) + 1),
name: cs.name || "Custom Strategy",
engine: cs.engine || engine,
args: cs.args,
description: cs.description || "User custom strategy"
});
}
}
return result;
}
function resolve_target_url(target_key, custom_url) {
if (custom_url && custom_url != "")
return custom_url;
return TARGET_URLS[target_key] || TARGET_URLS.youtube;
}
function resolve_target_urls_list(target_key, custom_url) {
if (custom_url && custom_url != "") {
return [ { name: "Custom Target", url: custom_url, weight: 100 } ];
}
target_key = as_string(target_key || "youtube_suite");
let suite = TARGET_SUITES[target_key];
if (suite && suite.urls && length(suite.urls) > 0) {
return suite.urls;
}
let single = TARGET_URLS[target_key] || TARGET_URLS.youtube;
return [ { name: target_key, url: single, weight: 100 } ];
}
function ensure_state_dir() {
common.ensure_dir(STATE_DIR);
}
function save_fuzzer_state(state) {
ensure_state_dir();
common.write_json_file(STATE_FILE, state);
}
function safe_json_parse(str) {
if (!str || str == "") return null;
let obj = null;
try {
obj = json(str);
} catch (e) {
obj = null;
}
return obj;
}
function query_llm(provider, api_key, custom_url, prompt_text, model_override) {
provider = lc(trim(as_string(provider || "openai")));
model_override = trim(as_string(model_override || ""));
if (provider == "anthropic" || provider == "claude") {
let api_url = "https://api.anthropic.com/v1/messages";
let model = model_override != "" ? model_override : "claude-3-5-haiku-20241022";
let body = {
model,
max_tokens: 1000,
messages: [{ role: "user", content: prompt_text }]
};
let cmd = sprintf(
"curl -s -m 35 --connect-timeout 10 -X POST -H 'x-api-key: %s' -H 'anthropic-version: 2023-06-01' -H 'content-type: application/json' -d %s %s 2>/dev/null",
shell_quote(api_key),
shell_quote(sprintf("%J", body)),
shell_quote(api_url)
);
let pipe = fs.popen(cmd, "r");
let output = pipe ? pipe.read("all") : "";
if (pipe) pipe.close();
let parsed = safe_json_parse(output);
if (parsed && parsed.content && type(parsed.content) == "array" && length(parsed.content) > 0) {
return parsed.content[0].text;
}
return null;
}
let base_url = "https://api.openai.com/v1";
let default_model = "gpt-4o-mini";
if (provider == "deepseek") {
base_url = "https://api.deepseek.com/v1";
default_model = "deepseek-chat";
} else if (provider == "openrouter") {
base_url = "https://openrouter.ai/api/v1";
default_model = "deepseek/deepseek-chat";
} else if (provider == "ollama") {
base_url = custom_url && custom_url != "" ? custom_url : "http://127.0.0.1:11434/v1";
default_model = "llama3.2";
} else if (provider == "lmstudio" || provider == "custom") {
base_url = custom_url && custom_url != "" ? custom_url : "http://127.0.0.1:1234/v1";
default_model = "local-model";
}
base_url = replace(base_url, /\/+$/, "");
let api_url = base_url + "/chat/completions";
let model = model_override != "" ? model_override : default_model;
let body = {
model,
messages: [
{ role: "system", content: "You are a network censorship and DPI bypass expert. Always return responses formatted strictly as requested." },
{ role: "user", content: prompt_text }
],
temperature: 0.3
};
let auth_header = api_key != "" ? sprintf("-H 'Authorization: Bearer %s'", api_key) : "";
let cmd = sprintf(
"curl -s -m 35 --connect-timeout 10 -X POST %s -H 'Content-Type: application/json' -d %s %s 2>/dev/null",
auth_header,
shell_quote(sprintf("%J", body)),
shell_quote(api_url)
);
let pipe = fs.popen(cmd, "r");
let output = pipe ? pipe.read("all") : "";
if (pipe) pipe.close();
let parsed = safe_json_parse(output);
if (parsed && parsed.choices && type(parsed.choices) == "array" && length(parsed.choices) > 0) {
let msg = parsed.choices[0].message;
if (msg && msg.content) {
return msg.content;
}
}
return null;
}
function parse_llm_json(raw_text) {
raw_text = trim(as_string(raw_text));
if (raw_text == "") return null;
let direct = safe_json_parse(raw_text);
if (direct && type(direct) == "object") return direct;
let m = match(raw_text, /```json\s*([\s\S]*?)\s*```/);
if (m && m[1]) {
let parsed = safe_json_parse(m[1]);
if (parsed && type(parsed) == "object") return parsed;
}
m = match(raw_text, /\{[\s\S]*\}/);
if (m && m[0]) {
let parsed = safe_json_parse(m[0]);
if (parsed && type(parsed) == "object") return parsed;
}
return null;
}
function synthesize_ai_strategies(engine, target, custom_url, user_prompt) {
let current = get_fuzzer_state();
if (current.running) {
print(sprintf("%J\n", { success: false, error: "Fuzzer is currently running a benchmark" }));
return;
}
engine = lc(as_string(engine || "zapret2"));
target = trim(as_string(target || "youtube_suite"));
user_prompt = trim(as_string(user_prompt || ""));
let target_url = resolve_target_url(target, custom_url);
let baseline = run_probe(engine, "", target, custom_url);
let query_text = sprintf("%s %s %s", engine, target, user_prompt);
let rag_docs = rag.retrieve(query_text, 4);
let uci = uci_core.cursor();
let ai_sec = uci.get_all(CONFIG_NAME, "ai") || {};
let provider = ai_sec.provider || "openai";
let api_key = ai_sec.api_key || "";
let ai_custom_url = ai_sec.custom_url || "";
let model_override = ai_sec.model || "";
let prompt = sprintf(
"You are an expert DPI Bypass Engineer specializing in OpenWrt, Zapret, Zapret2 (nfqws2), and ByeDPI (ciadpi).\n" +
"We need to bypass censorship / TSPU blocking for target service '%s' (%s) using engine '%s'.\n\n" +
"LIVE PROBE DIAGNOSTICS:\n" +
"- Direct HTTP Code: %d\n" +
"- Connect Time: %d ms\n" +
"- TTFB: %d ms\n" +
"- Probe Error: %s\n" +
"- User Notes / ISP Context: %s\n\n" +
"TECHNICAL KNOWLEDGE BASE FRAGMENTS:\n%s\n\n" +
"TASK:\n" +
"1. Analyze why this target is blocked or throttled.\n" +
"2. Formulate 3 to 5 highly effective, syntactically valid DPI desync strategies for '%s'.\n" +
"3. Output MUST be strictly valid JSON matching this schema:\n" +
"{\n" +
' "analysis": "Brief 1-2 sentence diagnosis of the blocking pattern",\n' +
' "strategies": [\n' +
' {\n' +
' "id": "ai_strat_1",\n' +
' "name": "Human-readable descriptive strategy name",\n' +
' "args": "Exact command-line arguments string for the engine",\n' +
' "description": "Why this combination should bypass the block"\n' +
' }\n' +
' ]\n' +
"}\n\n" +
"RULES FOR STRATEGY ARGS:\n" +
"- For zapret2: use valid options like '--lua-desync=multisplit:pos=1,midsld:seqovl=1:fooling=badseq' or '--lua-desync=fake:ttl=4:fooling=badseq --lua-desync=multisplit:pos=1,midsld'. DO NOT include binary name.\n" +
"- For zapret: use valid options like '--dpi-desync=fake,split2 --dpi-desync-split-pos=1,midsld --dpi-desync-fooling=badseq --dpi-desync-ttl=4'. DO NOT include binary name.\n" +
"- For byedpi: use valid options like '-s 1 -d 1 --auto=t,r,s -o 1'. DO NOT include binary name.\n\n" +
"JSON OUTPUT:",
target, target_url, engine,
baseline.http_code, baseline.handshake_ms, baseline.ttfb_ms,
baseline.error != "" ? baseline.error : "none",
user_prompt != "" ? user_prompt : "None provided",
rag_docs,
engine
);
let raw_reply = query_llm(provider, api_key, ai_custom_url, prompt, model_override);
if (!raw_reply) {
print(sprintf("%J\n", {
success: false,
error: "Failed to receive response from AI provider. Check API key and network connectivity."
}));
return;
}
let parsed_json = parse_llm_json(raw_reply);
if (!parsed_json || !parsed_json.strategies || type(parsed_json.strategies) != "array" || length(parsed_json.strategies) == 0) {
print(sprintf("%J\n", {
success: false,
error: "AI returned non-JSON or invalid format",
raw_response: raw_reply
}));
return;
}
let valid_strategies = [];
for (let i = 0; i < length(parsed_json.strategies); i++) {
let st = parsed_json.strategies[i];
if (st && st.args && validate_strategy_args(engine, st.args)) {
push(valid_strategies, {
id: st.id || sprintf("ai_strat_%d", i + 1),
name: st.name || sprintf("AI Strategy %d", i + 1),
engine,
args: trim(st.args),
description: st.description || ""
});
}
}
if (length(valid_strategies) == 0) {
print(sprintf("%J\n", {
success: false,
error: "All AI strategies failed syntax validation for engine " + engine,
raw_strategies: parsed_json.strategies
}));
return;
}
let custom_file = STATE_DIR + "/fuzzer_ai_strategies.json";
ensure_state_dir();
common.write_json_file(custom_file, valid_strategies);
print(sprintf("%J\n", {
success: true,
engine,
target,
target_url,
analysis: parsed_json.analysis || "AI strategy synthesis complete",
strategies: valid_strategies,
custom_file
}));
}
function get_fuzzer_state() {
let state = common.read_json_file(STATE_FILE);
if (!state || type(state) != "object") {
return {
running: false,
job_id: null,
engine: "zapret2",
target: "youtube_suite",
progress_pct: 0,
current_index: 0,
total_strategies: 0,
current_strategy: null,
results: [],
best_strategy: null,
error: null,
started_at: 0,
finished_at: 0
};
}
return state;
}
function kill_pid_file(path) {
let pid_str = fs.readfile(path);
if (pid_str) {
let pid = trim(as_string(pid_str));
if (pid != "") {
system(sprintf("kill %s >/dev/null 2>&1 || kill -9 %s >/dev/null 2>&1", pid, pid));
}
try { fs.unlink(path); } catch (e) {}
}
}
function cleanup_temp_daemons() {
kill_pid_file(STATE_DIR + "/fuzzer_byedpi.pid");
kill_pid_file(STATE_DIR + "/fuzzer_zapret.pid");
kill_pid_file(STATE_DIR + "/fuzzer_zapret2.pid");
system("nft delete table inet tachyon_fuzzer 2>/dev/null");
}
function parse_curl_output(output, result) {
result = result || {};
output = trim(as_string(output));
if (output == "") {
result.success = false;
result.http_code = 0;
result.handshake_ms = 0;
result.ttfb_ms = 0;
result.speed_kbps = 0;
result.score = 0;
result.error = "Probe timeout or connection refused";
return result;
}
let parts = split(output, "\t");
if (length(parts) < 4) {
result.success = false;
result.http_code = 0;
result.score = 0;
result.error = "Malformed probe metrics output";
return result;
}
let http_code = int(parts[0]);
let appconnect = double(parts[1]);
let starttransfer = double(parts[2]);
let speed_bytes = double(parts[3]);
result.http_code = http_code;
result.handshake_ms = int(appconnect * 1000.0);
result.ttfb_ms = int(starttransfer * 1000.0);
result.speed_kbps = int(speed_bytes / 1024.0);
if (http_code >= 200 && http_code < 400) {
result.success = true;
let base_score = 100;
let latency_score = max(0, 1000 - result.ttfb_ms);
let speed_score = int(result.speed_kbps / 10.0);
result.score = base_score + latency_score + speed_score;
result.error = "";
} else {
result.success = false;
result.score = 0;
result.error = sprintf("HTTP Status %d", http_code);
}
return result;
}
function run_probe(engine, args_str, target_key, custom_url) {
cleanup_temp_daemons();
let urls_list = resolve_target_urls_list(target_key, custom_url);
let total_urls = length(urls_list);
let result = {
success: false,
http_code: 0,
handshake_ms: 0,
ttfb_ms: 0,
speed_kbps: 0,
score: 0,
error: "",
sub_probes: []
};
engine = lc(as_string(engine));
let is_udp = index(args_str, "--filter-udp") >= 0 || index(args_str, "--dpi-desync-any-protocol") >= 0 || target_key == "quic_http3";
if (engine == "byedpi") {
let bin = get_byedpi_bin();
if (!bin) {
result.error = "ByeDPI binary not found";
return result;
}
let pid_path = STATE_DIR + "/fuzzer_byedpi.pid";
let spawn_cmd = sprintf("cd /tmp && %s -i 127.0.0.1 -p %d %s", bin, BYEDPI_PORT, args_str);
system(common.background_command_with_pid(spawn_cmd, ">/dev/null", ">" + shell_quote(pid_path)));
system("sleep 0.15");
let passed_count = 0;
let sum_handshake = 0;
let sum_ttfb = 0;
let max_speed = 0;
let last_http = 0;
for (let target_item in urls_list) {
let curl_cmd = sprintf(
"curl -x socks5h://127.0.0.1:%d -so /dev/null -w '%%{http_code}\\t%%{time_appconnect}\\t%%{time_starttransfer}\\t%%{speed_download}' -L --connect-timeout 3 --max-time 4 %s 2>/dev/null",
BYEDPI_PORT,
shell_quote(target_item.url)
);
let pipe = fs.popen(curl_cmd, "r");
let output = pipe ? pipe.read("all") : "";
if (pipe) pipe.close();
let single_res = parse_curl_output(output, {});
single_res.target_name = target_item.name;
single_res.url = target_item.url;
push(result.sub_probes, single_res);
if (single_res.success) {
passed_count++;
sum_handshake += single_res.handshake_ms;
sum_ttfb += single_res.ttfb_ms;
if (single_res.speed_kbps > max_speed) max_speed = single_res.speed_kbps;
last_http = single_res.http_code;
} else if (last_http == 0) {
last_http = single_res.http_code;
}
}
cleanup_temp_daemons();
if (passed_count == total_urls) {
result.success = true;
result.http_code = last_http > 0 ? last_http : 200;
result.handshake_ms = int(sum_handshake / double(total_urls));
result.ttfb_ms = int(sum_ttfb / double(total_urls));
result.speed_kbps = max_speed;
result.score = 100 + max(0, 1000 - result.ttfb_ms) + int(result.speed_kbps / 10.0);
} else {
result.success = false;
result.http_code = last_http;
result.score = 0;
result.error = sprintf("Failed %d of %d endpoints", total_urls - passed_count, total_urls);
}
return result;
}
if (engine == "zapret" || engine == "zapret2") {
let is_z2 = engine == "zapret2";
let bin = is_z2 ? get_zapret2_bin() : get_zapret_bin();
let qnum = is_z2 ? NFQUEUE_QNUM_ZAPRET2 : NFQUEUE_QNUM_ZAPRET;
let pid_path = is_z2 ? (STATE_DIR + "/fuzzer_zapret2.pid") : (STATE_DIR + "/fuzzer_zapret.pid");
if (!bin) {
result.error = (is_z2 ? "Zapret v2" : "Zapret v1") + " binary not found";
return result;
}
let lua_init_flags = "";
if (is_z2 && fs.stat("/opt/zapret2/lua/zapret-lib.lua") != null && index(args_str, "--lua-init") < 0) {
lua_init_flags = "--lua-init=@/opt/zapret2/lua/zapret-lib.lua --lua-init=@/opt/zapret2/lua/zapret-antidpi.lua --lua-init=@/opt/zapret2/lua/zapret-auto.lua ";
}
let filter_prefix = "";
if (is_z2 && index(args_str, "--filter-tcp") < 0 && index(args_str, "--filter-l7") < 0) {
filter_prefix = "--filter-tcp=443 --filter-l7=tls --payload=tls_client_hello ";
}
if (is_z2 && is_udp && index(args_str, "--filter-udp") < 0) {
filter_prefix += "--filter-udp=443 --payload=quic_initial ";
}
let spawn_cmd = sprintf("cd /tmp && %s --qnum=%d --fwmark=%s %s%s%s --pidfile=%s --daemon", bin, qnum, FUZZER_FWMARK, lua_init_flags, filter_prefix, args_str, pid_path);
system(common.background_command(spawn_cmd));
system("sleep 0.15");
system("nft add table inet tachyon_fuzzer 2>/dev/null");
system("nft 'add chain inet tachyon_fuzzer output { type filter hook output priority -200 ; }' 2>/dev/null");
system(sprintf("nft add rule inet tachyon_fuzzer output meta mark %s counter return 2>/dev/null", FUZZER_FWMARK));
if (is_udp) {
system(sprintf("nft 'add rule inet tachyon_fuzzer output meta l4proto { tcp, udp } th dport { 80, 443, 50000-65535 } counter queue num %d bypass' 2>/dev/null", qnum));
} else {
system(sprintf("nft 'add rule inet tachyon_fuzzer output meta l4proto tcp tcp dport { 80, 443 } counter queue num %d bypass' 2>/dev/null", qnum));
}
let passed_count = 0;
let sum_handshake = 0;
let sum_ttfb = 0;
let max_speed = 0;
let last_http = 0;
for (let target_item in urls_list) {
let curl_cmd = sprintf(
"curl -so /dev/null -w '%%{http_code}\\t%%{time_appconnect}\\t%%{time_starttransfer}\\t%%{speed_download}' -L --connect-timeout 3 --max-time 4 %s 2>/dev/null",
shell_quote(target_item.url)
);
let pipe = fs.popen(curl_cmd, "r");
let output = pipe ? pipe.read("all") : "";
if (pipe) pipe.close();
let single_res = parse_curl_output(output, {});
single_res.target_name = target_item.name;
single_res.url = target_item.url;
push(result.sub_probes, single_res);
if (single_res.success) {
passed_count++;
sum_handshake += single_res.handshake_ms;
sum_ttfb += single_res.ttfb_ms;
if (single_res.speed_kbps > max_speed) max_speed = single_res.speed_kbps;
last_http = single_res.http_code;
} else if (last_http == 0) {
last_http = single_res.http_code;
}
}
cleanup_temp_daemons();
if (passed_count == total_urls) {
result.success = true;
result.http_code = last_http > 0 ? last_http : 200;
result.handshake_ms = int(sum_handshake / double(total_urls));
result.ttfb_ms = int(sum_ttfb / double(total_urls));
result.speed_kbps = max_speed;
result.score = 100 + max(0, 1000 - result.ttfb_ms) + int(result.speed_kbps / 10.0);
} else {
result.success = false;
result.http_code = last_http;
result.score = 0;
result.error = sprintf("Failed %d of %d endpoints", total_urls - passed_count, total_urls);
}
return result;
}
result.error = "Unknown engine: " + engine;
return result;
}
function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file, mode) {
let target_url = resolve_target_url(target, custom_url);
let strategies = null;
if (custom_file && custom_file != "" && fs.stat(custom_file) != null) {
strategies = common.read_json_file(custom_file);
}
if (!strategies || type(strategies) != "array" || length(strategies) == 0) {
strategies = get_strategies_for_engine(engine, mode);
}
let total = length(strategies);
let state = {
running: true,
job_id: sprintf("fuzz_%d", clock()[0]),
engine,
target,
target_url,
mode: as_string(mode || "presets"),
rule_section: as_string(rule_section),
custom_file: as_string(custom_file || ""),
progress_pct: 0,
current_index: 0,
total_strategies: total,
current_strategy: null,
results: [],
best_strategy: null,
error: null,
started_at: clock()[0],
finished_at: 0
};
save_fuzzer_state(state);
try {
let highest_score = -1;
let best = null;
for (let i = 0; i < total; i++) {
let strat = strategies[i];
state.current_index = i + 1;
state.current_strategy = strat;
state.progress_pct = int(((i) / double(total)) * 100.0);
save_fuzzer_state(state);
let probe = run_probe(strat.engine || engine, strat.args, target, custom_url);
let item_result = {
id: strat.id || sprintf("strat_%d", i + 1),
name: strat.name || sprintf("Strategy %d", i + 1),
engine: strat.engine || engine,
args: strat.args,
description: strat.description || "",
rationale: strat.rationale || "",
success: probe.success,
http_code: probe.http_code,
handshake_ms: probe.handshake_ms,
ttfb_ms: probe.ttfb_ms,
speed_kbps: probe.speed_kbps,
score: probe.score,
error: probe.error,
sub_probes: probe.sub_probes || [],
badge: ""
};
if (item_result.score > highest_score && item_result.success) {
highest_score = item_result.score;
best = item_result;
}
push(state.results, item_result);
state.progress_pct = int(((i + 1) / double(total)) * 100.0);
save_fuzzer_state(state);
}
// Assign badges
if (best) {
best.badge = "🏆 Best Match";
state.best_strategy = best;
}
// Mark fastest and most stable
let min_ttfb = 999999;
let fastest = null;
for (let r in state.results) {
if (r.success && r.ttfb_ms > 0 && r.ttfb_ms < min_ttfb) {
min_ttfb = r.ttfb_ms;
fastest = r;
}
}
if (fastest && fastest.id != (best ? best.id : "")) {
fastest.badge = "⚡ Ultra Fast";
}
state.running = false;
state.current_strategy = null;
state.finished_at = clock()[0];
save_fuzzer_state(state);
} catch (err) {
state.running = false;
state.error = as_string(err);
state.finished_at = clock()[0];
save_fuzzer_state(state);
}
cleanup_temp_daemons();
}
function start_fuzzer(engine, target, custom_url, rule_section, custom_file, mode) {
let current = get_fuzzer_state();
if (current.running) {
print(sprintf("%J\n", { success: false, error: "Fuzzer is already running", job_id: current.job_id }));
return;
}
ensure_state_dir();
let job_id = sprintf("fuzz_%d", clock()[0]);
let cmd = sprintf(
"ucode -L /usr/lib/tachyon /usr/lib/tachyon/diagnostics/fuzzer.uc worker %s %s %s %s %s %s",
shell_quote(engine || "zapret2"),
shell_quote(target || "youtube_suite"),
shell_quote(custom_url || ""),
shell_quote(rule_section || ""),
shell_quote(custom_file || ""),
shell_quote(mode || "presets")
);
system(common.background_command_with_pid(cmd, ">/dev/null", ">" + shell_quote(PID_FILE)));
print(sprintf("%J\n", { success: true, job_id, engine: engine || "zapret2", target: target || "youtube_suite", mode: mode || "presets" }));
}
function stop_fuzzer() {
kill_pid_file(PID_FILE);
cleanup_temp_daemons();
let state = get_fuzzer_state();
state.running = false;
state.error = "Stopped by user";
state.finished_at = clock()[0];
save_fuzzer_state(state);
print(sprintf("%J\n", { success: true, message: "Fuzzer stopped" }));
}
function get_available_engines() {
return {
zapret2: get_zapret2_bin() != null,
zapret: get_zapret_bin() != null,
byedpi: get_byedpi_bin() != null
};
}
function apply_strategy(engine, args_val, target_rule) {
engine = lc(as_string(engine));
args_val = trim(as_string(args_val));
target_rule = trim(as_string(target_rule));
if (args_val == "") {
print(sprintf("%J\n", { success: false, error: "Empty strategy arguments" }));
return;
}
let uci = uci_core.cursor();
let applied = false;
if (target_rule != "" && target_rule != "global") {
uci.set(CONFIG_NAME, target_rule, "action", engine);
if (engine == "zapret2")
uci.set(CONFIG_NAME, target_rule, "nfqws2_opt", args_val);
else if (engine == "zapret")
uci.set(CONFIG_NAME, target_rule, "nfqws_opt", args_val);
else if (engine == "byedpi")
uci.set(CONFIG_NAME, target_rule, "byedpi_cmd_opts", args_val);
applied = true;
} else {
let provider_sec = engine;
let sec_obj = uci.get_all(CONFIG_NAME, provider_sec);
if (sec_obj == null) {
uci.set(CONFIG_NAME, provider_sec, "provider");
}
uci.set(CONFIG_NAME, provider_sec, "enabled", "1");
if (engine == "zapret2")
uci.set(CONFIG_NAME, provider_sec, "nfqws2_opt", args_val);
else if (engine == "zapret")
uci.set(CONFIG_NAME, provider_sec, "nfqws_opt", args_val);
else if (engine == "byedpi")
uci.set(CONFIG_NAME, provider_sec, "byedpi_cmd_opts", args_val);
applied = true;
}
uci.commit(CONFIG_NAME);
system(common.background_command("tachyon reload"));
print(sprintf("%J\n", {
success: true,
engine,
applied_to: target_rule != "" ? target_rule : "global",
args: args_val
}));
}
// CLI Dispatcher
let op = ARGV[0] || "status";
if (op == "start") {
start_fuzzer(ARGV[1], ARGV[2], ARGV[3], ARGV[4], ARGV[5], ARGV[6]);
} else if (op == "worker") {
run_fuzzer_worker(ARGV[1], ARGV[2], ARGV[3], ARGV[4], ARGV[5], ARGV[6]);
} else if (op == "status") {
print(sprintf("%J\n", get_fuzzer_state()));
} else if (op == "stop") {
stop_fuzzer();
} else if (op == "apply") {
apply_strategy(ARGV[1], ARGV[2], ARGV[3]);
} else if (op == "get_patterns" || op == "patterns") {
print(sprintf("%J\n", { success: true, patterns: get_patterns_config() }));
} else if (op == "save_patterns") {
let cfg = safe_json_parse(ARGV[1]);
save_patterns_config(cfg);
} else if (op == "reset_patterns") {
reset_patterns_config();
} else if (op == "ai_synthesize" || op == "synthesize") {
synthesize_ai_strategies(ARGV[1], ARGV[2], ARGV[3], ARGV[4]);
} else if (op == "generate" || op == "strategies_generate") {
print(sprintf("%J\n", get_strategies_for_engine(ARGV[1], ARGV[2] || "combinatorial")));
} else if (op == "strategies") {
let strat_mode = ARGV[1] || "presets";
print(sprintf("%J\n", {
available_engines: get_available_engines(),
target_suites: TARGET_SUITES,
patterns: get_patterns_config(),
zapret2: get_strategies_for_engine("zapret2", strat_mode),
zapret: get_strategies_for_engine("zapret", strat_mode),
byedpi: get_strategies_for_engine("byedpi", strat_mode)
}));
} else {
warn("Usage: fuzzer.uc [start|status|stop|apply|strategies|generate|get_patterns|save_patterns|reset_patterns|ai_synthesize|worker] ...\n");
exit(1);
}