feat(fuzzer): мульти-таргетные сьюты, конструктор паттернов, кастомные стратегии и минималистичный UI

This commit is contained in:
Dushnilin
2026-08-27 13:54:51 +03:00
parent 7ddb7f6d4d
commit b000d3024a
12 changed files with 6507 additions and 3424 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+249 -54
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: TACHYON\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-27 07:39+0300\n"
"PO-Revision-Date: 2026-08-27 07:39+0300\n"
"POT-Creation-Date: 2026-08-27 13:54+0300\n"
"PO-Revision-Date: 2026-08-27 13:54+0300\n"
"Last-Translator: Dushnilin\n"
"Language-Team: none\n"
"Language: ru\n"
@@ -35,6 +35,9 @@ msgstr "+ Добавить уровень"
msgid "+ Add priority"
msgstr "+ Добавить приоритет"
msgid "+ Add Strategy"
msgstr ""
msgid "+ Add URLTest"
msgstr "+ Добавить URLTest"
@@ -59,6 +62,12 @@ msgstr "⚠️ Если все основные DNS завершатся с ош
msgid "⚡ DPI Strategy Fuzzer"
msgstr "⚡ DPI Strategy Fuzzer"
msgid "⚡ Fast (>1MB/s)"
msgstr ""
msgid "⚡ QUIC / HTTP/3 (UDP 443)"
msgstr ""
msgid "⚡ Quick Benchmark (Presets ~12-14)"
msgstr "⚡ Быстрый бенчмарк (Пресеты ~12-14)"
@@ -68,6 +77,12 @@ msgstr "⚡ Подборщик стратегий и авто-тюнер"
msgid "✅ Benchmark completed! Optimal strategy identified."
msgstr "✅ Бенчмарк завершен! Оптимальная стратегия определена."
msgid "✅ Working only"
msgstr ""
msgid "✈️ Telegram Suite (Web + Bot API)"
msgstr ""
msgid "✔ Online"
msgstr ""
@@ -80,11 +95,32 @@ msgstr ""
msgid "✘ Stopped"
msgstr "✘ Остановлено"
msgid "🏆 Apply Best Strategy"
msgstr "🏆 Применить лучшую стратегию"
msgid "🌐 Custom Target URL..."
msgstr ""
msgid "🔍 Combinatorial Deep Fuzzing (~40-60)"
msgstr "🔍 Комбинаторный глубокий перебор (~40-60)"
msgid "🎬 YouTube Suite (Web + 4K Stream)"
msgstr ""
msgid "🏆 Apply Best Match"
msgstr ""
msgid "🏴‍☠️ RuTracker Suite"
msgstr ""
msgid "💬 Discord Suite (API + WSS + CDN)"
msgstr ""
msgid "💾 Save Custom Patterns"
msgstr ""
msgid "📸 Instagram / Meta (Web + CDN)"
msgstr ""
msgid "🔄 Reset to Factory Defaults"
msgstr ""
msgid "🔍 Combinatorial Deep Fuzzing (~40-80+)"
msgstr ""
msgid "🔑 Generate"
msgstr "🔑 Сгенерировать"
@@ -95,15 +131,15 @@ msgstr "🧠 Сгенерировать через ИИ"
msgid "🧠 Synthesizing..."
msgstr "🧠 Генерация..."
msgid "🚀 Run Benchmark Again"
msgstr "🚀 Запустить бенчмарк снова"
msgid "🚀 Start Benchmark"
msgstr "🚀 Начать бенчмарк"
msgid "🛑 Stop Benchmark"
msgstr "🛑 Остановить бенчмарк"
msgid "🛠️ My Custom Strategies Only"
msgstr ""
msgid "Accept external proxy connections and route them with sing-box."
msgstr "Принимать внешние прокси-подключения и маршрутизировать их через sing-box."
@@ -119,6 +155,9 @@ msgstr "Действие не выполнено"
msgid "Action Mode"
msgstr "Режим действия"
msgid "Actions"
msgstr ""
msgid "Active"
msgstr "Активные"
@@ -155,6 +194,9 @@ msgstr "Добавить серверный инбаунд"
msgid "Add at least one DNS server"
msgstr "Добавьте как минимум один DNS-сервер"
msgid "Add Custom DPI Strategy"
msgstr ""
msgid "Add prefix to nodes"
msgstr "Добавить префикс к узлам"
@@ -245,8 +287,11 @@ msgstr "Ошибка ИИ-доктора"
msgid "AI Doctor Language"
msgstr "Язык ИИ-доктора"
msgid "AI DPI Engineer & Strategy Synthesizer (RAG-Powered)"
msgstr "ИИ-инженер DPI и генератор стратегий (на базе RAG)"
msgid "AI Doctor RAG"
msgstr ""
msgid "AI DPI Engineer & Strategy Synthesizer"
msgstr ""
msgid "AI Model"
msgstr "Модель ИИ"
@@ -386,6 +431,9 @@ msgstr "Применение исправления"
msgid "Applying..."
msgstr "Применение..."
msgid "Arguments"
msgstr ""
msgid "Ask Tachyon AI Assistant a question..."
msgstr "Задайте вопрос ИИ-ассистенту..."
@@ -404,6 +452,9 @@ msgstr "Auth Digest"
msgid "Auto"
msgstr "Авто"
msgid "Auto Modes (--auto)"
msgstr ""
msgid "Auto refresh"
msgstr "Автообновление"
@@ -431,9 +482,6 @@ msgstr "Автолечение сетевого стека"
msgid "Auto-update Hosts Lists"
msgstr "Автообновление списков Hosts"
msgid "Automated DPI Strategy Fuzzer & Auto-Tuner"
msgstr "Автоматический подборщик и оптимизатор стратегий DPI"
msgid "Automatic component update checks"
msgstr "Автоматическая проверка обновлений компонентов"
@@ -452,6 +500,9 @@ msgstr "Автоматически создать аккаунт Cloudflare WARP
msgid "Automatically increase check intervals when healthy (to 5 min) and decrease on problems (to 120s)."
msgstr "Автоматически увеличивать интервалы проверки при нормальном состоянии (до 5 мин) и уменьшать при проблемах (до 120с)."
msgid "Automatically runs a diagnostic probe against the target service, queries the built-in DPI Knowledge Base, and synthesizes 3-5 custom bypass strategies tailored to your ISP."
msgstr ""
msgid "Automatically select the fastest node in the current level"
msgstr "Автовыбор самого быстрого узла уровня"
@@ -482,15 +533,15 @@ msgstr "Начало интервала расписания (например,
msgid "Belarus (by)"
msgstr "Беларусь (by)"
msgid "Benchmark & Test"
msgstr ""
msgid "Benchmark stopped"
msgstr "Бенчмарк остановлен"
msgid "Benchmarking in progress..."
msgstr "Выполняется бенчмарк..."
msgid "Benchmarks a suite of packet desynchronization strategies in an isolated sandbox without disrupting active traffic, ranking them by latency, TTFB, and throughput."
msgstr "Тестирует набор стратегий десинхронизации пакетов в изолированном сэндбоксе без влияния на активный трафик и ранжирует их по задержке, TTFB и скорости."
msgid "BitTorrent / P2P"
msgstr "BitTorrent / P2P"
@@ -782,6 +833,9 @@ msgstr "Токен доступа Cloudflare WARP API"
msgid "Comma-separated list of Telegram user IDs authorized to control the bot."
msgstr "Список Telegram user ID через запятую, авторизованных для управления ботом."
msgid "Command-line arguments string..."
msgstr ""
msgid "Community Lists"
msgstr "Community-списки"
@@ -908,6 +962,9 @@ msgstr "Пользовательский заголовок для инициа
msgid "Custom header for handshake response"
msgstr "Пользовательский заголовок для ответа рукопожатия"
msgid "Custom interface/section"
msgstr ""
msgid "Custom OpenAI-Compatible API"
msgstr "Пользовательский OpenAI-совместимый API"
@@ -917,8 +974,8 @@ msgstr "Пользовательский URL эндпоинта OpenAI-совм
msgid "Custom outbound configurations in JSON format"
msgstr "Пользовательские конфигурации outbound в формате JSON"
msgid "Custom Target URL..."
msgstr "Свой URL для теста..."
msgid "Custom strategy added!"
msgstr ""
msgid "Daily Quota"
msgstr "Дневная квота"
@@ -941,9 +998,6 @@ msgstr "По умолчанию (авто)"
msgid "Default (Direct WAN / Default mixed proxy)"
msgstr "По умолчанию (WAN / стандартный смешанный прокси)"
msgid "default (service mixed proxy)"
msgstr "по умолчанию (сервисный смешанный прокси)"
msgid "Default address for checking server availability and latency. URLTest uses its own address."
msgstr "Адрес по умолчанию для проверки доступности и задержки сервера. URLTest использует свой собственный адрес."
@@ -1031,8 +1085,8 @@ msgstr "Отключено"
msgid "Disabling interception is incompatible with action=zapret2 because Tachyon sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Tachyon направляет совпавший трафик через NFQUEUE."
msgid "Discord (Gateway / API / Voice)"
msgstr "Discord (Шлюз / API / Голосовые каналы)"
msgid "Disorder Values (-d)"
msgstr ""
msgid "Display name"
msgstr "Отображаемое имя"
@@ -1481,6 +1535,9 @@ msgstr "Не удалось создать отчёт об ошибке"
msgid "Failed to generate WARP config:"
msgstr "Не удалось сгенерировать конфигурацию WARP:"
msgid "Failed to get fuzzer patterns"
msgstr ""
msgid "Failed to get fuzzer status"
msgstr ""
@@ -1505,6 +1562,9 @@ msgstr "Не удалось прочитать лог операции"
msgid "Failed to receive response from AI service."
msgstr "Не удалось получить ответ от ИИ-сервиса."
msgid "Failed to reset fuzzer patterns"
msgstr ""
msgid "Failed to reset settings"
msgstr "Не удалось сбросить настройки"
@@ -1526,6 +1586,12 @@ msgstr "Ошибка сохранения IP устройств"
msgid "Failed to save DNS Turbo Cache"
msgstr "Не удалось сохранить DNS Turbo Cache"
msgid "Failed to save fuzzer patterns"
msgstr ""
msgid "Failed to save patterns"
msgstr ""
msgid "Failed to save Smart Detect settings"
msgstr "Ошибка сохранения настроек Smart Detect"
@@ -1601,6 +1667,9 @@ msgstr "Фильтр по сервису:"
msgid "Filter the servers that will be displayed on the dashboard."
msgstr "Фильтровать серверы, отображаемые на дашборде."
msgid "Filter:"
msgstr ""
msgid "Finland (fi)"
msgstr "Финляндия (fi)"
@@ -1622,6 +1691,12 @@ msgstr "Исправлено"
msgid "Flush Conntrack Table"
msgstr "Очистить таблицу соед. (conntrack)"
msgid "Fooling Methods (dpi-desync-fooling)"
msgstr ""
msgid "Fooling Methods (fooling)"
msgstr ""
msgid "Force immediate download and refresh of all remote lists and rule sets"
msgstr "Принудительно скачать и обновить все удаленные списки и наборы правил"
@@ -1925,9 +2000,6 @@ msgstr "Полезный груз init (I1)"
msgid "Init timeout (Itime)"
msgstr "Таймаут init (Itime)"
msgid "Instagram / Meta (TLS 1.3)"
msgstr "Instagram / Meta (TLS 1.3)"
msgid "Install"
msgstr "Установить"
@@ -2180,6 +2252,9 @@ msgstr "Некорректный VMess URL: ошибка разбора"
msgid "IP Addresses (IP CIDR)"
msgstr "IP-адреса (IP CIDR)"
msgid "IP Frag Bounds (--ip-frag)"
msgstr ""
msgid "IPs"
msgstr "IP"
@@ -2282,9 +2357,6 @@ msgstr "Путь к ключу"
msgid "Keyword must not contain spaces or commas"
msgstr "Ключевое слово не должно содержать пробелов или запятых"
msgid "Knowledge Base + Live Probe Context"
msgstr "База знаний + Контекст сетевого зонда"
msgid "LAN Devices"
msgstr "Устройства LAN"
@@ -2492,6 +2564,12 @@ msgstr "Должен быть список числовых ID через зап
msgid "Must be a number in the range of 0 - 10000"
msgstr "Должно быть числом в диапазоне 0 - 10000"
msgid "My Strategies"
msgstr ""
msgid "Name"
msgstr ""
msgid "Name displayed on the dashboard"
msgstr "Имя, отображаемое на дашборде"
@@ -2555,8 +2633,8 @@ msgstr "Активные устройства в локальной сети н
msgid "No active routing sections found."
msgstr "Активные секции маршрутизации не найдены."
msgid "No benchmark results yet. Select engine and click \"Start Benchmark\" or \"Synthesize with AI\"."
msgstr "Нет результатов бенчмарка. Выберите движок и нажмите «Начать бенчмарк» или «Сгенерировать через ИИ»."
msgid "No benchmark results yet. Configure options and click \"Start Benchmark\"."
msgstr ""
msgid "No check targets found in the configuration."
msgstr "Цели для проверки не найдены в конфигурации."
@@ -2570,6 +2648,9 @@ msgstr "Конфликт не обнаружен"
msgid "No Custom Rules Matched"
msgstr "Нет совпадений с пользовательскими правилами"
msgid "No custom strategies added yet."
msgstr ""
msgid "No DHCP clients detected"
msgstr "DHCP-клиенты не обнаружены"
@@ -2612,6 +2693,9 @@ msgstr "Секции не выбраны"
msgid "No snapshots yet"
msgstr "Снапшотов пока нет"
msgid "No strategies match the active filter."
msgstr ""
msgid "Node list is empty"
msgstr "Список узлов пуст"
@@ -2723,6 +2807,9 @@ msgstr "Только серверы с одним из выбранных тра
msgid "Only the specified servers."
msgstr "Только указанные серверы."
msgid "OOB Values (-o)"
msgstr ""
msgid "Open"
msgstr "Открыть"
@@ -2768,12 +2855,12 @@ msgstr "Параметр \"%s\" содержит недопустимое зна
msgid "Optional — leave empty for default"
msgstr "Опционально — оставьте пустым для значения по умолчанию"
msgid "Optional context (e.g. \"Rostelecom, YouTube 4K buffering is slow\")..."
msgstr ""
msgid "Optional for local LLM"
msgstr "Опционально для локальных LLM"
msgid "Optional notes for AI (e.g. \"Rostelecom, YouTube 4K stream is slow\")..."
msgstr "Дополнительные заметки для ИИ (напр. «Ростелеком, тормозит 4K на YouTube»)..."
msgid "Optional tls-auth or tls-crypt key"
msgstr "Опциональный ключ tls-auth или tls-crypt"
@@ -2837,6 +2924,21 @@ msgstr "Путь должен содержать как минимум одну
msgid "Path must end with cache.db"
msgstr "Путь должен заканчиваться на cache.db"
msgid "Pattern Builder"
msgstr ""
msgid "Patterns reset to defaults!"
msgstr ""
msgid "Patterns reset to factory defaults"
msgstr ""
msgid "Patterns saved successfully"
msgstr ""
msgid "Patterns saved successfully!"
msgstr ""
msgid "Pause updates"
msgstr "Приостановить обновление"
@@ -2888,6 +2990,9 @@ msgstr "Закрепить на дашборде"
msgid "Pin P2P isolation to the torrent client's own ports, comma separated. Each entry is \"proto\", \"proto:port\" or \"proto:port-port\" (tcp/udp). Without a port the whole protocol is routed direct."
msgstr "Закрепить изоляцию P2P за портами торрент-клиента, через запятую. Каждая запись — \"протокол\", \"протокол:порт\" или \"протокол:порт-порт\" (tcp/udp). Без порта весь протокол уходит напрямую."
msgid "Please specify strategy name and arguments"
msgstr ""
msgid "Polling Interval (seconds)"
msgstr "Интервал опроса (секунды)"
@@ -3014,6 +3119,9 @@ msgstr "QR-код слишком большой"
msgid "QUIC (HTTP/3)"
msgstr "QUIC (HTTP/3)"
msgid "RAG Knowledge Base + Live ISP Probe Context"
msgstr ""
msgid "RDP"
msgstr "RDP"
@@ -3290,18 +3398,15 @@ msgstr "Запустить восстановление"
msgid "Running"
msgstr "Запущен"
msgid "Running benchmarks..."
msgstr ""
msgid "Running diagnostics…"
msgstr "Выполняется диагностика…"
msgid "Running initial strategy probe..."
msgstr "Запуск первого зонда стратегии..."
msgid "Russia (ru)"
msgstr "Россия (ru)"
msgid "RuTracker (HTTP/HTTPS)"
msgstr "RuTracker (HTTP/HTTPS)"
msgid "Sat"
msgstr "Сб"
@@ -3365,8 +3470,8 @@ msgstr "Действие секции"
msgid "Section name"
msgstr "Имя секции"
msgid "Section name for WARP registration proxy. Leave empty to use the default service mixed proxy."
msgstr "Секция для прокси регистрации WARP. Оставьте пустым для использования сервисного смешанного прокси по умолчанию."
msgid "Section or tunnel interface used for Cloudflare WARP API registration. Supports Connection, WireGuard, AmneziaWG, and Interface sections. Leave empty to use direct WAN / default mixed proxy."
msgstr ""
msgid "Section Rules Content"
msgstr "Содержимое правил секции"
@@ -3503,6 +3608,9 @@ msgstr "Отправлять уведомление в Telegram при пере
msgid "Send a Telegram notification when proxy subscriptions are successfully updated."
msgstr "Отправлять уведомление в Telegram при успешном обновлении подписок прокси."
msgid "Sequence Overlaps (seqovl)"
msgstr ""
msgid "Server"
msgstr "Сервер"
@@ -3722,6 +3830,18 @@ msgstr "Укажите разные файлы"
msgid "Speed"
msgstr "Скорость"
msgid "Split Modes (dpi-desync)"
msgstr ""
msgid "Split Positions (dpi-desync-split-pos)"
msgstr ""
msgid "Split Positions (pos)"
msgstr ""
msgid "Split Values (-s)"
msgstr ""
msgid "SSH"
msgstr "SSH"
@@ -3788,6 +3908,9 @@ msgstr ""
msgid "Status"
msgstr "Статус"
msgid "Status / Suite"
msgstr ""
msgid "Stop"
msgstr "Стоп"
@@ -3815,6 +3938,12 @@ msgstr "Стратегия и параметры"
msgid "Strategy benchmark started"
msgstr "Бенчмарк стратегий запущен"
msgid "Strategy deleted"
msgstr ""
msgid "Strategy Name"
msgstr ""
msgid "Strict ECH + DoQ DNS Enforcer"
msgstr "Строгий ECH + DoQ DNS Enforcer"
@@ -3965,8 +4094,11 @@ msgstr "Целевые устройства (IP / MAC)"
msgid "Target Scope"
msgstr "Область действия"
msgid "Target Service"
msgstr "Целевой сервис"
msgid "Target Service / Suite"
msgstr ""
msgid "TCP Window Sizes (wsize)"
msgstr ""
msgid "Telegram Bot"
msgstr "Telegram Бот"
@@ -3977,9 +4109,6 @@ msgstr "Токен Telegram-бота"
msgid "Telegram Channel"
msgstr "Telegram канал"
msgid "Telegram Web"
msgstr "Telegram Web"
msgid "Test Connection"
msgstr "Проверить подключение"
@@ -4106,6 +4235,9 @@ msgstr "Ключ TLS Auth/Crypt"
msgid "TLS SNI"
msgstr "TLS SNI"
msgid "TLS-Rec Bounds (--tlsrec)"
msgstr ""
msgid "Token is required when the bot is enabled"
msgstr "Токен обязателен, когда бот включён"
@@ -4187,6 +4319,12 @@ msgstr "TTL должен быть положительным числом"
msgid "TTL value cannot be empty"
msgstr "Значение TTL не может быть пустым"
msgid "TTL Values (dpi-desync-ttl)"
msgstr ""
msgid "TTL Values (ttl)"
msgstr ""
msgid "Tue"
msgstr "Вт"
@@ -4421,6 +4559,9 @@ msgstr "Использовать exit-node тейлнета для LAN"
msgid "Used by"
msgstr "Используется"
msgid "User custom strategy"
msgstr ""
msgid "User-Agent"
msgstr "User-Agent"
@@ -4550,12 +4691,6 @@ msgstr "Да"
msgid "You can select Output Network Interface, by default autodetect"
msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию автоопределение"
msgid "YouTube (GoogleVideo 4K stream)"
msgstr "YouTube (4K видеопоток GoogleVideo)"
msgid "YouTube Web (Interface)"
msgstr "YouTube Web (Интерфейс)"
msgid "Zapret hostlist templates are not supported here because Tachyon does not expand them for per-rule NFQWS strategies."
msgstr "Шаблоны hostlist Zapret не поддерживаются здесь, потому что Tachyon не расширяет их для стратегий NFQWS по правилам."
@@ -4604,6 +4739,66 @@ msgstr "sing-box outbound для Zapret2 настроен"
msgid "Zapret2 sing-box outbound is not configured"
msgstr "sing-box outbound для Zapret2 не настроен"
msgid "🏆 Apply Best Strategy"
msgstr "🏆 Применить лучшую стратегию"
msgid "🔍 Combinatorial Deep Fuzzing (~40-60)"
msgstr "🔍 Комбинаторный глубокий перебор (~40-60)"
msgid "🚀 Run Benchmark Again"
msgstr "🚀 Запустить бенчмарк снова"
msgid "AI DPI Engineer & Strategy Synthesizer (RAG-Powered)"
msgstr "ИИ-инженер DPI и генератор стратегий (на базе RAG)"
msgid "Automated DPI Strategy Fuzzer & Auto-Tuner"
msgstr "Автоматический подборщик и оптимизатор стратегий DPI"
msgid "Benchmarks a suite of packet desynchronization strategies in an isolated sandbox without disrupting active traffic, ranking them by latency, TTFB, and throughput."
msgstr "Тестирует набор стратегий десинхронизации пакетов в изолированном сэндбоксе без влияния на активный трафик и ранжирует их по задержке, TTFB и скорости."
msgid "Custom Target URL..."
msgstr "Свой URL для теста..."
msgid "default (service mixed proxy)"
msgstr "по умолчанию (сервисный смешанный прокси)"
msgid "Discord (Gateway / API / Voice)"
msgstr "Discord (Шлюз / API / Голосовые каналы)"
msgid "Instagram / Meta (TLS 1.3)"
msgstr "Instagram / Meta (TLS 1.3)"
msgid "Knowledge Base + Live Probe Context"
msgstr "База знаний + Контекст сетевого зонда"
msgid "No benchmark results yet. Select engine and click \"Start Benchmark\" or \"Synthesize with AI\"."
msgstr "Нет результатов бенчмарка. Выберите движок и нажмите «Начать бенчмарк» или «Сгенерировать через ИИ»."
msgid "Optional notes for AI (e.g. \"Rostelecom, YouTube 4K stream is slow\")..."
msgstr "Дополнительные заметки для ИИ (напр. «Ростелеком, тормозит 4K на YouTube»)..."
msgid "Running initial strategy probe..."
msgstr "Запуск первого зонда стратегии..."
msgid "RuTracker (HTTP/HTTPS)"
msgstr "RuTracker (HTTP/HTTPS)"
msgid "Section name for WARP registration proxy. Leave empty to use the default service mixed proxy."
msgstr "Секция для прокси регистрации WARP. Оставьте пустым для использования сервисного смешанного прокси по умолчанию."
msgid "Target Service"
msgstr "Целевой сервис"
msgid "Telegram Web"
msgstr "Telegram Web"
msgid "YouTube (GoogleVideo 4K stream)"
msgstr "YouTube (4K видеопоток GoogleVideo)"
msgid "YouTube Web (Interface)"
msgstr "YouTube Web (Интерфейс)"
msgid "No benchmark results yet. Select engine and click \"Start Benchmark\"."
msgstr "Нет результатов бенчмарка. Выберите движок и нажмите «Начать бенчмарк»."
@@ -1246,4 +1246,110 @@ export const TachyonShellMethods = {
_('Failed to synthesize AI strategies'),
};
},
getFuzzerPatterns: async (): Promise<
Tachyon.MethodResponse<{ patterns: Tachyon.FuzzerPatternsConfig }>
> => {
const response = await executeShellCommand({
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.FUZZER_GET_PATTERNS],
timeout: 8000,
});
let parsed: {
success: boolean;
patterns: Tachyon.FuzzerPatternsConfig;
} | null = null;
try {
parsed = JSON.parse(response.stdout?.trim() || '{}');
} catch {
parsed = null;
}
if ((response.code ?? 1) === 0 && parsed && parsed.success) {
return {
success: true,
data: parsed,
};
}
return {
success: false,
error: response.stderr || _('Failed to get fuzzer patterns'),
};
},
saveFuzzerPatterns: async (
patterns: Tachyon.FuzzerPatternsConfig,
): Promise<Tachyon.MethodResponse<{ message: string }>> => {
const response = await executeShellCommand({
command: '/usr/bin/tachyon',
args: [
Tachyon.AvailableMethods.FUZZER_SAVE_PATTERNS,
JSON.stringify(patterns),
],
timeout: 10000,
});
let parsed: { success: boolean; message?: string; error?: string } | null =
null;
try {
parsed = JSON.parse(response.stdout?.trim() || '{}');
} catch {
parsed = null;
}
if ((response.code ?? 1) === 0 && parsed && parsed.success) {
return {
success: true,
data: { message: parsed.message || _('Patterns saved successfully') },
};
}
return {
success: false,
error:
parsed?.error || response.stderr || _('Failed to save fuzzer patterns'),
};
},
resetFuzzerPatterns: async (): Promise<
Tachyon.MethodResponse<{
patterns: Tachyon.FuzzerPatternsConfig;
message: string;
}>
> => {
const response = await executeShellCommand({
command: '/usr/bin/tachyon',
args: [Tachyon.AvailableMethods.FUZZER_RESET_PATTERNS],
timeout: 8000,
});
let parsed: {
success: boolean;
message?: string;
patterns?: Tachyon.FuzzerPatternsConfig;
error?: string;
} | null = null;
try {
parsed = JSON.parse(response.stdout?.trim() || '{}');
} catch {
parsed = null;
}
if ((response.code ?? 1) === 0 && parsed && parsed.success) {
return {
success: true,
data: {
message: parsed.message || _('Patterns reset to factory defaults'),
patterns: parsed.patterns as Tachyon.FuzzerPatternsConfig,
},
};
}
return {
success: false,
error:
parsed?.error ||
response.stderr ||
_('Failed to reset fuzzer patterns'),
};
},
};
@@ -182,4 +182,72 @@ describe('TachyonShellMethods Fuzzer Actions', () => {
expect(res.data.strategies).toHaveLength(1);
}
});
it('gets fuzzer patterns configuration', async () => {
mocks.executeShellCommand.mockResolvedValueOnce({
stdout: JSON.stringify({
success: true,
patterns: {
zapret2: { splits: ['1', '2', 'midsld'] },
zapret: { splits: ['1', '2'] },
byedpi: { splits: ['1'] },
custom_strategies: [],
},
}),
stderr: '',
code: 0,
});
const res = await TachyonShellMethods.getFuzzerPatterns();
expect(res.success).toBe(true);
if (res.success) {
expect(res.data.patterns.zapret2.splits).toEqual(['1', '2', 'midsld']);
}
});
it('saves fuzzer patterns configuration', async () => {
mocks.executeShellCommand.mockResolvedValueOnce({
stdout: JSON.stringify({
success: true,
message: 'Patterns saved successfully',
}),
stderr: '',
code: 0,
});
const patternsPayload: any = {
zapret2: { splits: ['1', '2', 'midsld'] },
zapret: { splits: ['1'] },
byedpi: { splits: ['1'] },
custom_strategies: [],
};
const res = await TachyonShellMethods.saveFuzzerPatterns(patternsPayload);
expect(res.success).toBe(true);
expect(mocks.executeShellCommand).toHaveBeenCalledWith(
expect.objectContaining({
args: ['fuzzer_save_patterns', JSON.stringify(patternsPayload)],
}),
);
});
it('resets fuzzer patterns configuration', async () => {
mocks.executeShellCommand.mockResolvedValueOnce({
stdout: JSON.stringify({
success: true,
message: 'Patterns reset to factory defaults',
patterns: {
zapret2: { splits: ['1', '2', '3', 'midsld'] },
},
}),
stderr: '',
code: 0,
});
const res = await TachyonShellMethods.resetFuzzerPatterns();
expect(res.success).toBe(true);
if (res.success) {
expect(res.data.message).toBe('Patterns reset to factory defaults');
}
});
});
File diff suppressed because it is too large Load Diff
+68 -1
View File
@@ -104,6 +104,9 @@ export namespace Tachyon {
FUZZER_APPLY = 'fuzzer_apply',
FUZZER_STRATEGIES = 'fuzzer_strategies',
FUZZER_AI_SYNTHESIZE = 'fuzzer_ai_synthesize',
FUZZER_GET_PATTERNS = 'fuzzer_get_patterns',
FUZZER_SAVE_PATTERNS = 'fuzzer_save_patterns',
FUZZER_RESET_PATTERNS = 'fuzzer_reset_patterns',
}
export enum AvailableClashAPIMethods {
@@ -831,15 +834,21 @@ export namespace Tachyon {
export type FuzzerEngine = 'zapret2' | 'zapret' | 'byedpi' | 'all';
export type FuzzerTarget =
| 'youtube_suite'
| 'youtube'
| 'youtube_web'
| 'discord_suite'
| 'discord'
| 'instagram_suite'
| 'instagram'
| 'rutracker_suite'
| 'rutracker'
| 'telegram_suite'
| 'telegram'
| 'quic_http3'
| 'custom';
export type FuzzerMode = 'presets' | 'combinatorial';
export type FuzzerMode = 'presets' | 'combinatorial' | 'custom';
export interface FuzzerStrategyItem {
id: string;
@@ -851,6 +860,17 @@ export namespace Tachyon {
export type FuzzerStrategyDefinition = FuzzerStrategyItem;
export interface FuzzerSubProbeResult {
target_name?: string;
url?: string;
http_code: number;
handshake_ms: number;
ttfb_ms: number;
speed_kbps: number;
success: boolean;
error?: string;
}
export interface FuzzerStrategyResult {
id: string;
name: string;
@@ -865,6 +885,7 @@ export namespace Tachyon {
score: number;
error?: string;
badge?: string;
sub_probes?: FuzzerSubProbeResult[];
}
export interface FuzzerState {
@@ -911,4 +932,50 @@ export namespace Tachyon {
strategies?: FuzzerStrategyItem[];
error?: string;
}
export interface FuzzerCustomStrategy {
id?: string;
name: string;
engine: 'zapret2' | 'zapret' | 'byedpi';
args: string;
description?: string;
}
export interface FuzzerPatternsConfig {
zapret2: {
splits: string[];
foolings: string[];
ttls: number[];
seqovls: string[];
wsizes: string[];
payloads: string[];
};
zapret: {
splits: string[];
foolings: string[];
ttls: number[];
split_modes: string[];
};
byedpi: {
splits: string[];
disorders: string[];
ttls: number[];
oobs: string[];
autos: string[];
tlsrecs: string[];
ipfrags: string[];
};
custom_strategies: FuzzerCustomStrategy[];
}
export interface FuzzerTargetSuiteItem {
name: string;
url: string;
weight: number;
}
export interface FuzzerTargetSuite {
name: string;
urls: FuzzerTargetSuiteItem[];
}
}
File diff suppressed because it is too large Load Diff
+249 -54
View File
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: TACHYON\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-08-27 07:39+0300\n"
"PO-Revision-Date: 2026-08-27 07:39+0300\n"
"POT-Creation-Date: 2026-08-27 13:54+0300\n"
"PO-Revision-Date: 2026-08-27 13:54+0300\n"
"Last-Translator: Dushnilin\n"
"Language-Team: none\n"
"Language: ru\n"
@@ -35,6 +35,9 @@ msgstr "+ Добавить уровень"
msgid "+ Add priority"
msgstr "+ Добавить приоритет"
msgid "+ Add Strategy"
msgstr ""
msgid "+ Add URLTest"
msgstr "+ Добавить URLTest"
@@ -59,6 +62,12 @@ msgstr "⚠️ Если все основные DNS завершатся с ош
msgid "⚡ DPI Strategy Fuzzer"
msgstr "⚡ DPI Strategy Fuzzer"
msgid "⚡ Fast (>1MB/s)"
msgstr ""
msgid "⚡ QUIC / HTTP/3 (UDP 443)"
msgstr ""
msgid "⚡ Quick Benchmark (Presets ~12-14)"
msgstr "⚡ Быстрый бенчмарк (Пресеты ~12-14)"
@@ -68,6 +77,12 @@ msgstr "⚡ Подборщик стратегий и авто-тюнер"
msgid "✅ Benchmark completed! Optimal strategy identified."
msgstr "✅ Бенчмарк завершен! Оптимальная стратегия определена."
msgid "✅ Working only"
msgstr ""
msgid "✈️ Telegram Suite (Web + Bot API)"
msgstr ""
msgid "✔ Online"
msgstr ""
@@ -80,11 +95,32 @@ msgstr ""
msgid "✘ Stopped"
msgstr "✘ Остановлено"
msgid "🏆 Apply Best Strategy"
msgstr "🏆 Применить лучшую стратегию"
msgid "🌐 Custom Target URL..."
msgstr ""
msgid "🔍 Combinatorial Deep Fuzzing (~40-60)"
msgstr "🔍 Комбинаторный глубокий перебор (~40-60)"
msgid "🎬 YouTube Suite (Web + 4K Stream)"
msgstr ""
msgid "🏆 Apply Best Match"
msgstr ""
msgid "🏴‍☠️ RuTracker Suite"
msgstr ""
msgid "💬 Discord Suite (API + WSS + CDN)"
msgstr ""
msgid "💾 Save Custom Patterns"
msgstr ""
msgid "📸 Instagram / Meta (Web + CDN)"
msgstr ""
msgid "🔄 Reset to Factory Defaults"
msgstr ""
msgid "🔍 Combinatorial Deep Fuzzing (~40-80+)"
msgstr ""
msgid "🔑 Generate"
msgstr "🔑 Сгенерировать"
@@ -95,15 +131,15 @@ msgstr "🧠 Сгенерировать через ИИ"
msgid "🧠 Synthesizing..."
msgstr "🧠 Генерация..."
msgid "🚀 Run Benchmark Again"
msgstr "🚀 Запустить бенчмарк снова"
msgid "🚀 Start Benchmark"
msgstr "🚀 Начать бенчмарк"
msgid "🛑 Stop Benchmark"
msgstr "🛑 Остановить бенчмарк"
msgid "🛠️ My Custom Strategies Only"
msgstr ""
msgid "Accept external proxy connections and route them with sing-box."
msgstr "Принимать внешние прокси-подключения и маршрутизировать их через sing-box."
@@ -119,6 +155,9 @@ msgstr "Действие не выполнено"
msgid "Action Mode"
msgstr "Режим действия"
msgid "Actions"
msgstr ""
msgid "Active"
msgstr "Активные"
@@ -155,6 +194,9 @@ msgstr "Добавить серверный инбаунд"
msgid "Add at least one DNS server"
msgstr "Добавьте как минимум один DNS-сервер"
msgid "Add Custom DPI Strategy"
msgstr ""
msgid "Add prefix to nodes"
msgstr "Добавить префикс к узлам"
@@ -245,8 +287,11 @@ msgstr "Ошибка ИИ-доктора"
msgid "AI Doctor Language"
msgstr "Язык ИИ-доктора"
msgid "AI DPI Engineer & Strategy Synthesizer (RAG-Powered)"
msgstr "ИИ-инженер DPI и генератор стратегий (на базе RAG)"
msgid "AI Doctor RAG"
msgstr ""
msgid "AI DPI Engineer & Strategy Synthesizer"
msgstr ""
msgid "AI Model"
msgstr "Модель ИИ"
@@ -386,6 +431,9 @@ msgstr "Применение исправления"
msgid "Applying..."
msgstr "Применение..."
msgid "Arguments"
msgstr ""
msgid "Ask Tachyon AI Assistant a question..."
msgstr "Задайте вопрос ИИ-ассистенту..."
@@ -404,6 +452,9 @@ msgstr "Auth Digest"
msgid "Auto"
msgstr "Авто"
msgid "Auto Modes (--auto)"
msgstr ""
msgid "Auto refresh"
msgstr "Автообновление"
@@ -431,9 +482,6 @@ msgstr "Автолечение сетевого стека"
msgid "Auto-update Hosts Lists"
msgstr "Автообновление списков Hosts"
msgid "Automated DPI Strategy Fuzzer & Auto-Tuner"
msgstr "Автоматический подборщик и оптимизатор стратегий DPI"
msgid "Automatic component update checks"
msgstr "Автоматическая проверка обновлений компонентов"
@@ -452,6 +500,9 @@ msgstr "Автоматически создать аккаунт Cloudflare WARP
msgid "Automatically increase check intervals when healthy (to 5 min) and decrease on problems (to 120s)."
msgstr "Автоматически увеличивать интервалы проверки при нормальном состоянии (до 5 мин) и уменьшать при проблемах (до 120с)."
msgid "Automatically runs a diagnostic probe against the target service, queries the built-in DPI Knowledge Base, and synthesizes 3-5 custom bypass strategies tailored to your ISP."
msgstr ""
msgid "Automatically select the fastest node in the current level"
msgstr "Автовыбор самого быстрого узла уровня"
@@ -482,15 +533,15 @@ msgstr "Начало интервала расписания (например,
msgid "Belarus (by)"
msgstr "Беларусь (by)"
msgid "Benchmark & Test"
msgstr ""
msgid "Benchmark stopped"
msgstr "Бенчмарк остановлен"
msgid "Benchmarking in progress..."
msgstr "Выполняется бенчмарк..."
msgid "Benchmarks a suite of packet desynchronization strategies in an isolated sandbox without disrupting active traffic, ranking them by latency, TTFB, and throughput."
msgstr "Тестирует набор стратегий десинхронизации пакетов в изолированном сэндбоксе без влияния на активный трафик и ранжирует их по задержке, TTFB и скорости."
msgid "BitTorrent / P2P"
msgstr "BitTorrent / P2P"
@@ -782,6 +833,9 @@ msgstr "Токен доступа Cloudflare WARP API"
msgid "Comma-separated list of Telegram user IDs authorized to control the bot."
msgstr "Список Telegram user ID через запятую, авторизованных для управления ботом."
msgid "Command-line arguments string..."
msgstr ""
msgid "Community Lists"
msgstr "Community-списки"
@@ -908,6 +962,9 @@ msgstr "Пользовательский заголовок для инициа
msgid "Custom header for handshake response"
msgstr "Пользовательский заголовок для ответа рукопожатия"
msgid "Custom interface/section"
msgstr ""
msgid "Custom OpenAI-Compatible API"
msgstr "Пользовательский OpenAI-совместимый API"
@@ -917,8 +974,8 @@ msgstr "Пользовательский URL эндпоинта OpenAI-совм
msgid "Custom outbound configurations in JSON format"
msgstr "Пользовательские конфигурации outbound в формате JSON"
msgid "Custom Target URL..."
msgstr "Свой URL для теста..."
msgid "Custom strategy added!"
msgstr ""
msgid "Daily Quota"
msgstr "Дневная квота"
@@ -941,9 +998,6 @@ msgstr "По умолчанию (авто)"
msgid "Default (Direct WAN / Default mixed proxy)"
msgstr "По умолчанию (WAN / стандартный смешанный прокси)"
msgid "default (service mixed proxy)"
msgstr "по умолчанию (сервисный смешанный прокси)"
msgid "Default address for checking server availability and latency. URLTest uses its own address."
msgstr "Адрес по умолчанию для проверки доступности и задержки сервера. URLTest использует свой собственный адрес."
@@ -1031,8 +1085,8 @@ msgstr "Отключено"
msgid "Disabling interception is incompatible with action=zapret2 because Tachyon sends matched traffic through NFQUEUE."
msgstr "Отключение перехвата несовместимо с action=zapret2, потому что Tachyon направляет совпавший трафик через NFQUEUE."
msgid "Discord (Gateway / API / Voice)"
msgstr "Discord (Шлюз / API / Голосовые каналы)"
msgid "Disorder Values (-d)"
msgstr ""
msgid "Display name"
msgstr "Отображаемое имя"
@@ -1481,6 +1535,9 @@ msgstr "Не удалось создать отчёт об ошибке"
msgid "Failed to generate WARP config:"
msgstr "Не удалось сгенерировать конфигурацию WARP:"
msgid "Failed to get fuzzer patterns"
msgstr ""
msgid "Failed to get fuzzer status"
msgstr ""
@@ -1505,6 +1562,9 @@ msgstr "Не удалось прочитать лог операции"
msgid "Failed to receive response from AI service."
msgstr "Не удалось получить ответ от ИИ-сервиса."
msgid "Failed to reset fuzzer patterns"
msgstr ""
msgid "Failed to reset settings"
msgstr "Не удалось сбросить настройки"
@@ -1526,6 +1586,12 @@ msgstr "Ошибка сохранения IP устройств"
msgid "Failed to save DNS Turbo Cache"
msgstr "Не удалось сохранить DNS Turbo Cache"
msgid "Failed to save fuzzer patterns"
msgstr ""
msgid "Failed to save patterns"
msgstr ""
msgid "Failed to save Smart Detect settings"
msgstr "Ошибка сохранения настроек Smart Detect"
@@ -1601,6 +1667,9 @@ msgstr "Фильтр по сервису:"
msgid "Filter the servers that will be displayed on the dashboard."
msgstr "Фильтровать серверы, отображаемые на дашборде."
msgid "Filter:"
msgstr ""
msgid "Finland (fi)"
msgstr "Финляндия (fi)"
@@ -1622,6 +1691,12 @@ msgstr "Исправлено"
msgid "Flush Conntrack Table"
msgstr "Очистить таблицу соед. (conntrack)"
msgid "Fooling Methods (dpi-desync-fooling)"
msgstr ""
msgid "Fooling Methods (fooling)"
msgstr ""
msgid "Force immediate download and refresh of all remote lists and rule sets"
msgstr "Принудительно скачать и обновить все удаленные списки и наборы правил"
@@ -1925,9 +2000,6 @@ msgstr "Полезный груз init (I1)"
msgid "Init timeout (Itime)"
msgstr "Таймаут init (Itime)"
msgid "Instagram / Meta (TLS 1.3)"
msgstr "Instagram / Meta (TLS 1.3)"
msgid "Install"
msgstr "Установить"
@@ -2180,6 +2252,9 @@ msgstr "Некорректный VMess URL: ошибка разбора"
msgid "IP Addresses (IP CIDR)"
msgstr "IP-адреса (IP CIDR)"
msgid "IP Frag Bounds (--ip-frag)"
msgstr ""
msgid "IPs"
msgstr "IP"
@@ -2282,9 +2357,6 @@ msgstr "Путь к ключу"
msgid "Keyword must not contain spaces or commas"
msgstr "Ключевое слово не должно содержать пробелов или запятых"
msgid "Knowledge Base + Live Probe Context"
msgstr "База знаний + Контекст сетевого зонда"
msgid "LAN Devices"
msgstr "Устройства LAN"
@@ -2492,6 +2564,12 @@ msgstr "Должен быть список числовых ID через зап
msgid "Must be a number in the range of 0 - 10000"
msgstr "Должно быть числом в диапазоне 0 - 10000"
msgid "My Strategies"
msgstr ""
msgid "Name"
msgstr ""
msgid "Name displayed on the dashboard"
msgstr "Имя, отображаемое на дашборде"
@@ -2555,8 +2633,8 @@ msgstr "Активные устройства в локальной сети н
msgid "No active routing sections found."
msgstr "Активные секции маршрутизации не найдены."
msgid "No benchmark results yet. Select engine and click \"Start Benchmark\" or \"Synthesize with AI\"."
msgstr "Нет результатов бенчмарка. Выберите движок и нажмите «Начать бенчмарк» или «Сгенерировать через ИИ»."
msgid "No benchmark results yet. Configure options and click \"Start Benchmark\"."
msgstr ""
msgid "No check targets found in the configuration."
msgstr "Цели для проверки не найдены в конфигурации."
@@ -2570,6 +2648,9 @@ msgstr "Конфликт не обнаружен"
msgid "No Custom Rules Matched"
msgstr "Нет совпадений с пользовательскими правилами"
msgid "No custom strategies added yet."
msgstr ""
msgid "No DHCP clients detected"
msgstr "DHCP-клиенты не обнаружены"
@@ -2612,6 +2693,9 @@ msgstr "Секции не выбраны"
msgid "No snapshots yet"
msgstr "Снапшотов пока нет"
msgid "No strategies match the active filter."
msgstr ""
msgid "Node list is empty"
msgstr "Список узлов пуст"
@@ -2723,6 +2807,9 @@ msgstr "Только серверы с одним из выбранных тра
msgid "Only the specified servers."
msgstr "Только указанные серверы."
msgid "OOB Values (-o)"
msgstr ""
msgid "Open"
msgstr "Открыть"
@@ -2768,12 +2855,12 @@ msgstr "Параметр \"%s\" содержит недопустимое зна
msgid "Optional — leave empty for default"
msgstr "Опционально — оставьте пустым для значения по умолчанию"
msgid "Optional context (e.g. \"Rostelecom, YouTube 4K buffering is slow\")..."
msgstr ""
msgid "Optional for local LLM"
msgstr "Опционально для локальных LLM"
msgid "Optional notes for AI (e.g. \"Rostelecom, YouTube 4K stream is slow\")..."
msgstr "Дополнительные заметки для ИИ (напр. «Ростелеком, тормозит 4K на YouTube»)..."
msgid "Optional tls-auth or tls-crypt key"
msgstr "Опциональный ключ tls-auth или tls-crypt"
@@ -2837,6 +2924,21 @@ msgstr "Путь должен содержать как минимум одну
msgid "Path must end with cache.db"
msgstr "Путь должен заканчиваться на cache.db"
msgid "Pattern Builder"
msgstr ""
msgid "Patterns reset to defaults!"
msgstr ""
msgid "Patterns reset to factory defaults"
msgstr ""
msgid "Patterns saved successfully"
msgstr ""
msgid "Patterns saved successfully!"
msgstr ""
msgid "Pause updates"
msgstr "Приостановить обновление"
@@ -2888,6 +2990,9 @@ msgstr "Закрепить на дашборде"
msgid "Pin P2P isolation to the torrent client's own ports, comma separated. Each entry is \"proto\", \"proto:port\" or \"proto:port-port\" (tcp/udp). Without a port the whole protocol is routed direct."
msgstr "Закрепить изоляцию P2P за портами торрент-клиента, через запятую. Каждая запись — \"протокол\", \"протокол:порт\" или \"протокол:порт-порт\" (tcp/udp). Без порта весь протокол уходит напрямую."
msgid "Please specify strategy name and arguments"
msgstr ""
msgid "Polling Interval (seconds)"
msgstr "Интервал опроса (секунды)"
@@ -3014,6 +3119,9 @@ msgstr "QR-код слишком большой"
msgid "QUIC (HTTP/3)"
msgstr "QUIC (HTTP/3)"
msgid "RAG Knowledge Base + Live ISP Probe Context"
msgstr ""
msgid "RDP"
msgstr "RDP"
@@ -3290,18 +3398,15 @@ msgstr "Запустить восстановление"
msgid "Running"
msgstr "Запущен"
msgid "Running benchmarks..."
msgstr ""
msgid "Running diagnostics…"
msgstr "Выполняется диагностика…"
msgid "Running initial strategy probe..."
msgstr "Запуск первого зонда стратегии..."
msgid "Russia (ru)"
msgstr "Россия (ru)"
msgid "RuTracker (HTTP/HTTPS)"
msgstr "RuTracker (HTTP/HTTPS)"
msgid "Sat"
msgstr "Сб"
@@ -3365,8 +3470,8 @@ msgstr "Действие секции"
msgid "Section name"
msgstr "Имя секции"
msgid "Section name for WARP registration proxy. Leave empty to use the default service mixed proxy."
msgstr "Секция для прокси регистрации WARP. Оставьте пустым для использования сервисного смешанного прокси по умолчанию."
msgid "Section or tunnel interface used for Cloudflare WARP API registration. Supports Connection, WireGuard, AmneziaWG, and Interface sections. Leave empty to use direct WAN / default mixed proxy."
msgstr ""
msgid "Section Rules Content"
msgstr "Содержимое правил секции"
@@ -3503,6 +3608,9 @@ msgstr "Отправлять уведомление в Telegram при пере
msgid "Send a Telegram notification when proxy subscriptions are successfully updated."
msgstr "Отправлять уведомление в Telegram при успешном обновлении подписок прокси."
msgid "Sequence Overlaps (seqovl)"
msgstr ""
msgid "Server"
msgstr "Сервер"
@@ -3722,6 +3830,18 @@ msgstr "Укажите разные файлы"
msgid "Speed"
msgstr "Скорость"
msgid "Split Modes (dpi-desync)"
msgstr ""
msgid "Split Positions (dpi-desync-split-pos)"
msgstr ""
msgid "Split Positions (pos)"
msgstr ""
msgid "Split Values (-s)"
msgstr ""
msgid "SSH"
msgstr "SSH"
@@ -3788,6 +3908,9 @@ msgstr ""
msgid "Status"
msgstr "Статус"
msgid "Status / Suite"
msgstr ""
msgid "Stop"
msgstr "Стоп"
@@ -3815,6 +3938,12 @@ msgstr "Стратегия и параметры"
msgid "Strategy benchmark started"
msgstr "Бенчмарк стратегий запущен"
msgid "Strategy deleted"
msgstr ""
msgid "Strategy Name"
msgstr ""
msgid "Strict ECH + DoQ DNS Enforcer"
msgstr "Строгий ECH + DoQ DNS Enforcer"
@@ -3965,8 +4094,11 @@ msgstr "Целевые устройства (IP / MAC)"
msgid "Target Scope"
msgstr "Область действия"
msgid "Target Service"
msgstr "Целевой сервис"
msgid "Target Service / Suite"
msgstr ""
msgid "TCP Window Sizes (wsize)"
msgstr ""
msgid "Telegram Bot"
msgstr "Telegram Бот"
@@ -3977,9 +4109,6 @@ msgstr "Токен Telegram-бота"
msgid "Telegram Channel"
msgstr "Telegram канал"
msgid "Telegram Web"
msgstr "Telegram Web"
msgid "Test Connection"
msgstr "Проверить подключение"
@@ -4106,6 +4235,9 @@ msgstr "Ключ TLS Auth/Crypt"
msgid "TLS SNI"
msgstr "TLS SNI"
msgid "TLS-Rec Bounds (--tlsrec)"
msgstr ""
msgid "Token is required when the bot is enabled"
msgstr "Токен обязателен, когда бот включён"
@@ -4187,6 +4319,12 @@ msgstr "TTL должен быть положительным числом"
msgid "TTL value cannot be empty"
msgstr "Значение TTL не может быть пустым"
msgid "TTL Values (dpi-desync-ttl)"
msgstr ""
msgid "TTL Values (ttl)"
msgstr ""
msgid "Tue"
msgstr "Вт"
@@ -4421,6 +4559,9 @@ msgstr "Использовать exit-node тейлнета для LAN"
msgid "Used by"
msgstr "Используется"
msgid "User custom strategy"
msgstr ""
msgid "User-Agent"
msgstr "User-Agent"
@@ -4550,12 +4691,6 @@ msgstr "Да"
msgid "You can select Output Network Interface, by default autodetect"
msgstr "Вы можете выбрать выходной сетевой интерфейс, по умолчанию автоопределение"
msgid "YouTube (GoogleVideo 4K stream)"
msgstr "YouTube (4K видеопоток GoogleVideo)"
msgid "YouTube Web (Interface)"
msgstr "YouTube Web (Интерфейс)"
msgid "Zapret hostlist templates are not supported here because Tachyon does not expand them for per-rule NFQWS strategies."
msgstr "Шаблоны hostlist Zapret не поддерживаются здесь, потому что Tachyon не расширяет их для стратегий NFQWS по правилам."
@@ -4604,6 +4739,66 @@ msgstr "sing-box outbound для Zapret2 настроен"
msgid "Zapret2 sing-box outbound is not configured"
msgstr "sing-box outbound для Zapret2 не настроен"
msgid "🏆 Apply Best Strategy"
msgstr "🏆 Применить лучшую стратегию"
msgid "🔍 Combinatorial Deep Fuzzing (~40-60)"
msgstr "🔍 Комбинаторный глубокий перебор (~40-60)"
msgid "🚀 Run Benchmark Again"
msgstr "🚀 Запустить бенчмарк снова"
msgid "AI DPI Engineer & Strategy Synthesizer (RAG-Powered)"
msgstr "ИИ-инженер DPI и генератор стратегий (на базе RAG)"
msgid "Automated DPI Strategy Fuzzer & Auto-Tuner"
msgstr "Автоматический подборщик и оптимизатор стратегий DPI"
msgid "Benchmarks a suite of packet desynchronization strategies in an isolated sandbox without disrupting active traffic, ranking them by latency, TTFB, and throughput."
msgstr "Тестирует набор стратегий десинхронизации пакетов в изолированном сэндбоксе без влияния на активный трафик и ранжирует их по задержке, TTFB и скорости."
msgid "Custom Target URL..."
msgstr "Свой URL для теста..."
msgid "default (service mixed proxy)"
msgstr "по умолчанию (сервисный смешанный прокси)"
msgid "Discord (Gateway / API / Voice)"
msgstr "Discord (Шлюз / API / Голосовые каналы)"
msgid "Instagram / Meta (TLS 1.3)"
msgstr "Instagram / Meta (TLS 1.3)"
msgid "Knowledge Base + Live Probe Context"
msgstr "База знаний + Контекст сетевого зонда"
msgid "No benchmark results yet. Select engine and click \"Start Benchmark\" or \"Synthesize with AI\"."
msgstr "Нет результатов бенчмарка. Выберите движок и нажмите «Начать бенчмарк» или «Сгенерировать через ИИ»."
msgid "Optional notes for AI (e.g. \"Rostelecom, YouTube 4K stream is slow\")..."
msgstr "Дополнительные заметки для ИИ (напр. «Ростелеком, тормозит 4K на YouTube»)..."
msgid "Running initial strategy probe..."
msgstr "Запуск первого зонда стратегии..."
msgid "RuTracker (HTTP/HTTPS)"
msgstr "RuTracker (HTTP/HTTPS)"
msgid "Section name for WARP registration proxy. Leave empty to use the default service mixed proxy."
msgstr "Секция для прокси регистрации WARP. Оставьте пустым для использования сервисного смешанного прокси по умолчанию."
msgid "Target Service"
msgstr "Целевой сервис"
msgid "Telegram Web"
msgstr "Telegram Web"
msgid "YouTube (GoogleVideo 4K stream)"
msgstr "YouTube (4K видеопоток GoogleVideo)"
msgid "YouTube Web (Interface)"
msgstr "YouTube Web (Интерфейс)"
msgid "No benchmark results yet. Select engine and click \"Start Benchmark\"."
msgstr "Нет результатов бенчмарка. Выберите движок и нажмите «Начать бенчмарк»."
File diff suppressed because it is too large Load Diff
+3
View File
@@ -265,6 +265,9 @@ function command_spec(command) {
fuzzer_strategies: [ "diagnostics/fuzzer.uc", "strategies", 1 ],
fuzzer_generate: [ "diagnostics/fuzzer.uc", "generate", 2 ],
fuzzer_ai_synthesize: [ "diagnostics/fuzzer.uc", "ai_synthesize", 4 ],
fuzzer_get_patterns: [ "diagnostics/fuzzer.uc", "get_patterns", 0 ],
fuzzer_save_patterns: [ "diagnostics/fuzzer.uc", "save_patterns", 1 ],
fuzzer_reset_patterns: [ "diagnostics/fuzzer.uc", "reset_patterns", 0 ],
generate_warp: [ "service/warp_generator.uc", "", 1 ],
package_prerm: [ "service/package.uc", "prerm", 1 ],
package_postinst: [ "service/package.uc", "postinst", 0 ],
+386 -69
View File
@@ -84,14 +84,117 @@ function validate_strategy_args(engine, args_val) {
return true;
}
// Target definitions
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: "https://web.telegram.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)
@@ -385,6 +488,8 @@ const STRATEGIES_BYEDPI = [
];
function generate_combinatorial_zapret2() {
let cfg = get_patterns_config();
let p = cfg.zapret2 || DEFAULT_PATTERNS.zapret2;
let list = [];
let seen = {};
@@ -394,7 +499,7 @@ function generate_combinatorial_zapret2() {
if (!validate_strategy_args("zapret2", args)) return;
seen[args] = true;
push(list, {
id: sprintf("z2_gen_%d", length(list) + 1),
id: sprintf("z2_comb_%d", length(list) + 1),
name: name,
engine: "zapret2",
args: args,
@@ -404,24 +509,41 @@ function generate_combinatorial_zapret2() {
for (let s in STRATEGIES_ZAPRET2) add(s.name, s.args, s.description);
let positions = ["1", "midsld", "sniext+4", "1,midsld", "1,sniext+2"];
let foolings = ["badseq", "md5sig", "badack"];
let ttls = [3, 4, 5, 8];
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");
}
}
}
for (let pos in positions) {
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");
add(sprintf("Multisplit + SeqOvl (pos=%s, %s)", pos, fooling),
sprintf("--lua-desync=multisplit:pos=%s:seqovl=1:fooling=%s", pos, fooling),
"Multisplit with sequence overlap");
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 ["badseq", "md5sig", "badack"]) {
for (let pos in ["1", "midsld", "1,midsld"]) {
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");
@@ -436,6 +558,8 @@ function generate_combinatorial_zapret2() {
}
function generate_combinatorial_zapret() {
let cfg = get_patterns_config();
let p = cfg.zapret || DEFAULT_PATTERNS.zapret;
let list = [];
let seen = {};
@@ -455,10 +579,18 @@ function generate_combinatorial_zapret() {
for (let s in STRATEGIES_ZAPRET) add(s.name, s.args, s.description);
let modes = ["split2", "disorder2", "fake,split2", "fake,disorder2"];
let positions = ["1", "2", "midsld"];
let foolings = ["badseq", "md5sig", "badack"];
let ttls = [4, 6, 8];
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) {
@@ -485,6 +617,8 @@ function generate_combinatorial_zapret() {
}
function generate_combinatorial_byedpi() {
let cfg = get_patterns_config();
let p = cfg.byedpi || DEFAULT_PATTERNS.byedpi;
let list = [];
let seen = {};
@@ -504,11 +638,21 @@ function generate_combinatorial_byedpi() {
for (let s in STRATEGIES_BYEDPI) add(s.name, s.args, s.description);
let splits = ["1", "2", "1+sniext", "midsld"];
let disorders = ["1", "2"];
let ttls = [3, 4, 6, 8];
let oobs = ["1", "2"];
let autos = ["t,r,a,s", "r,s", "t,a"];
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) {
@@ -536,12 +680,16 @@ function generate_combinatorial_byedpi() {
}
for (let s in splits) {
add(sprintf("TLS-Rec (1+sniext) + Split=%s", s),
sprintf("--tlsrec 1+sniext --split %s", s),
"TLS record boundary fragmentation");
add(sprintf("IP-Frag (24) + Split=%s", s),
sprintf("--ip-frag 24 --split %s", s),
"IP packet fragmentation");
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;
@@ -550,17 +698,62 @@ function generate_combinatorial_byedpi() {
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;
}
}
if (engine == "zapret2") return STRATEGIES_ZAPRET2;
if (engine == "zapret") return STRATEGIES_ZAPRET;
if (engine == "byedpi") return STRATEGIES_BYEDPI;
return [];
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) {
@@ -569,6 +762,19 @@ function resolve_target_url(target_key, 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);
}
@@ -578,6 +784,17 @@ function save_fuzzer_state(state) {
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 || ""));
@@ -599,7 +816,7 @@ function query_llm(provider, api_key, custom_url, prompt_text, model_override) {
let pipe = fs.popen(cmd, "r");
let output = pipe ? pipe.read("all") : "";
if (pipe) pipe.close();
let parsed = common.json_parse(output);
let parsed = safe_json_parse(output);
if (parsed && parsed.content && type(parsed.content) == "array" && length(parsed.content) > 0) {
return parsed.content[0].text;
}
@@ -644,7 +861,7 @@ function query_llm(provider, api_key, custom_url, prompt_text, model_override) {
let pipe = fs.popen(cmd, "r");
let output = pipe ? pipe.read("all") : "";
if (pipe) pipe.close();
let parsed = common.json_parse(output);
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) {
@@ -657,18 +874,18 @@ function query_llm(provider, api_key, custom_url, prompt_text, model_override) {
function parse_llm_json(raw_text) {
raw_text = trim(as_string(raw_text));
if (raw_text == "") return null;
let direct = common.json_parse(raw_text);
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 = common.json_parse(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 = common.json_parse(m[0]);
let parsed = safe_json_parse(m[0]);
if (parsed && type(parsed) == "object") return parsed;
}
return null;
@@ -682,11 +899,11 @@ function synthesize_ai_strategies(engine, target, custom_url, user_prompt) {
}
engine = lc(as_string(engine || "zapret2"));
target = trim(as_string(target || "youtube"));
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_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);
@@ -800,7 +1017,7 @@ function get_fuzzer_state() {
running: false,
job_id: null,
engine: "zapret2",
target: "youtube",
target: "youtube_suite",
progress_pct: 0,
current_index: 0,
total_strategies: 0,
@@ -834,14 +1051,24 @@ function cleanup_temp_daemons() {
}
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;
}
@@ -862,6 +1089,7 @@ function parse_curl_output(output, result) {
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;
@@ -871,9 +1099,12 @@ function parse_curl_output(output, result) {
return result;
}
function run_probe(engine, args_str, target_url) {
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,
@@ -881,10 +1112,12 @@ function run_probe(engine, args_str, target_url) {
ttfb_ms: 0,
speed_kbps: 0,
score: 0,
error: ""
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();
@@ -898,19 +1131,55 @@ function run_probe(engine, args_str, target_url) {
system(common.background_command_with_pid(spawn_cmd, ">/dev/null", ">" + shell_quote(pid_path)));
system("sleep 0.15");
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_url)
);
let passed_count = 0;
let sum_handshake = 0;
let sum_ttfb = 0;
let max_speed = 0;
let last_http = 0;
let pipe = fs.popen(curl_cmd, "r");
let output = pipe ? pipe.read("all") : "";
if (pipe) pipe.close();
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();
return parse_curl_output(output, result);
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") {
@@ -933,30 +1202,71 @@ function run_probe(engine, args_str, target_url) {
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");
// Setup temporary isolated nftables queue for probe with high priority (-200)
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));
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));
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));
}
// Execute probe with direct curl
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_url)
);
let passed_count = 0;
let sum_handshake = 0;
let sum_ttfb = 0;
let max_speed = 0;
let last_http = 0;
let pipe = fs.popen(curl_cmd, "r");
let output = pipe ? pipe.read("all") : "";
if (pipe) pipe.close();
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();
return parse_curl_output(output, result);
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;
@@ -1007,7 +1317,7 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
state.progress_pct = int(((i) / double(total)) * 100.0);
save_fuzzer_state(state);
let probe = run_probe(strat.engine || engine, strat.args, target_url);
let probe = run_probe(strat.engine || engine, strat.args, target, custom_url);
let item_result = {
id: strat.id || sprintf("strat_%d", i + 1),
@@ -1023,6 +1333,7 @@ function run_fuzzer_worker(engine, target, custom_url, rule_section, custom_file
speed_kbps: probe.speed_kbps,
score: probe.score,
error: probe.error,
sub_probes: probe.sub_probes || [],
badge: ""
};
@@ -1082,7 +1393,7 @@ function start_fuzzer(engine, target, custom_url, rule_section, custom_file, mod
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"),
shell_quote(target || "youtube_suite"),
shell_quote(custom_url || ""),
shell_quote(rule_section || ""),
shell_quote(custom_file || ""),
@@ -1091,7 +1402,7 @@ function start_fuzzer(engine, target, custom_url, rule_section, custom_file, mod
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", mode: mode || "presets" }));
print(sprintf("%J\n", { success: true, job_id, engine: engine || "zapret2", target: target || "youtube_suite", mode: mode || "presets" }));
}
function stop_fuzzer() {
@@ -1128,7 +1439,6 @@ function apply_strategy(engine, args_val, target_rule) {
let uci = uci_core.cursor();
let applied = false;
// If target rule section is specified, update that rule
if (target_rule != "" && target_rule != "global") {
uci.set(CONFIG_NAME, target_rule, "action", engine);
if (engine == "zapret2")
@@ -1139,7 +1449,6 @@ function apply_strategy(engine, args_val, target_rule) {
uci.set(CONFIG_NAME, target_rule, "byedpi_cmd_opts", args_val);
applied = true;
} else {
// Apply as provider global setting
let provider_sec = engine;
let sec_obj = uci.get_all(CONFIG_NAME, provider_sec);
if (sec_obj == null) {
@@ -1156,8 +1465,6 @@ function apply_strategy(engine, args_val, target_rule) {
}
uci.commit(CONFIG_NAME);
// Mark reload pending
system(common.background_command("tachyon reload"));
print(sprintf("%J\n", {
@@ -1181,6 +1488,13 @@ if (op == "start") {
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") {
@@ -1189,11 +1503,14 @@ if (op == "start") {
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|ai_synthesize|worker] ...\n");
warn("Usage: fuzzer.uc [start|status|stop|apply|strategies|generate|get_patterns|save_patterns|reset_patterns|ai_synthesize|worker] ...\n");
exit(1);
}