- fix(zapret2): add safe_str fallback and avoid default --filter-tcp=443 injection on UDP rules in prepare_strategy_args()
- fix(providers): make individual rule failures non-fatal in start_runtime() so one bad rule does not rollback all running rules
- fix(binaries): dynamic candidate resolution for nfqws and nfqws2 across standard OpenWrt and custom paths (/opt/zapret2/nfq2, /opt/zapret2/nfq, /opt/zapret2, /usr/bin)
- fix(nft): add raw_output notrack chain and mangle_output/forward counter return rules for desync marks (0x40000000, 0x20000000) and musl/nobody skuid (2147483647, 65534) to prevent EPERM rawsend failure on reload
- fix(reload): ensure nft_rebuild is triggered on zapret and zapret2 runtime updates
- fix(singbox): reject mieru outbounds from rule_set download_detour and http_clients dial_detour to prevent fatal startup crash loop
- fix(singbox): detect sing-box 1.14+ and omit deprecated download_detour on remote rule-sets when http_clients is active
- fix(action): pre-seed /etc/config/zapret2 with enabled=0 via core.uci before package install to prevent conflicting upstream daemon startup
- test(ruleset): add verification tests for mieru download_detour and dial_detour rejection
- feat(diagnostics): router-level IP & DNS leak detection
* одновременный опрос прямого WAN сокета (SO_MARK 0x08000000) и локального прокси (127.0.0.1:4534)
* выявление утечки реального публичного IP адреса
* тестирование восходящих DNS-резолверов по протоколу bash.ws и обнаружение DNS провайдера
* CLI команды: tachyon leak_check [--pretty], check_ip_leak, check_dns_leak
* LuCI модальное окно с детализированными таблицами изоляции IP и анализа резолверов
- fix(hosts): устранение ложного предупреждения [warn] No hosts list URLs configured
* запуск HOSTS_UC в lifecycle.uc только при наличии активных списков хостов
* hosts.uc возвращает success:true и код 0 без лишних записей в syslog при отсутствии URL
* добавлена проверка has-hosts-list-update-sources в state.uc
- i18n: полная синхронизация русской локализации в каталогах gettext PO/POT
- bump: версия пакета 1.3.24
- Added get_mixed_port() to core/common.uc (reads sing-box config.json)
- agent_api.uc, api.uc: use common.get_mixed_port()
- warp_generator.uc: use config port instead of hardcoded fallback
- watchdog.uc: use mixed_ports_from_config() for Clash API calls
- runtime.uc: dynamic port in conflict checks and proxy probe
Коммит 3d84d96e добавил constants.TACHYON_COMMIT_SHA в get_system_status()
без соответствующего require(). В результате constants == null и каждый
вызов api.get_system_status() крашил с 'left-hand side expression is null'.
Это ломало буквально все команды Telegram-бота (/menu, /status, и т.д.)
так как каждая из них вызывает view_menu() -> api.get_system_status().
Пустых catch в кодовой базе около полусотни, и они не одинаковые. Большинство
выражает намерение: fs.unlink бросает на ENOENT, а вызывающий говорит «пусть
этого файла не будет», и отсутствующий файл это условие уже выполняет; разбор
json в цикле пропускает мусорную строку и берёт следующую. Такие места теперь
несут пояснение, почему пустое тело здесь и есть правильный ответ.
Семь мест к этому классу не относились: там сбой означал потерю состояния, а
дальше код шёл так, будто всё получилось.
Watchdog писал через пустой catch файл лока перезапуска прокси, шкалу
GOMEMLIMIT после OOM, бэкап конфига и метку keepalive. Не лёгший лок означает,
что следующий тик перезапустит прокси заново, а счётчик rate-limit об этом не
узнает; не записанная метка keepalive заставляет init.d считать watchdog мёртвым
и перезапускать его. Все четыре теперь идут через write_state_file, который
логирует отказ. Метка пишется на каждом тике uloop, поэтому безусловная строка
залила бы syslog именно при переполненной файловой системе — там, где лог нужнее
всего; вместо этого каждый путь сообщает о первом отказе и молчит до следующего
успеха, что заодно делает видимым восстановление.
Контроллер разбирал /etc/sing-box/config.json ради портов http и tproxy и при
неудаче молча брал 4534 и 4530. Watchdog после этого опрашивает не тот порт,
вечно считает прокси нерабочим и лечит его по кругу — при том, что в логе нет
ничего, что связало бы это с конфигом. Разбор вынесен в общую функцию, которая
сообщает о непарсящемся конфиге один раз на процесс.
Telegram молча терял карту callback-токенов: токен уже вшит в клавиатуру к
моменту записи, поэтому провал отдаёт пользователю кнопку, которая на нажатие
ответит «кнопка устарела». Обновление списков молча теряло маркер отложенного
применения nft — списки обновлены на диске и до nftables не доехали никогда,
а строкой выше в лог написано, что маркер сохраняется. urltest молча выкидывал
битое регулярное выражение из конфига пользователя, отчего группа тихо
подхватывала меньше outbound-ов, чем сказано в конфиге.
tests/heal_escalation.sh проверял порядок строк по литералу fs.writefile для
лока и обновлён под новый вызов.
UI (section.js):
- showUrlTestSettingsModal и showPriorityLevelSettingsModal: предзагрузка
кэша outbound-имён до открытия модала — CBINamedSection.render() не
вызывает section.load(), из-за чего DynamicList рендерился без данных
- renderStackedJsonSettingsModal: вызов map.load() перед map.render()
LuCI ACL (luci-app-tachyon.json):
- Добавлены пути /tmp/run/tachyon/... наряду с /var/run/tachyon/...
На OpenWrt /var — симлинк на /tmp; rpcd резолвит симлинки перед
проверкой ACL, поэтому /var/run/... никогда не давал прав чтения
и fs.read() возвращал PermissionError
Postinstall (50_luci-tachyon):
- Добавлен перезапуск rpcd после установки пакета, чтобы новые ACL
вступали в силу без ручного logout/login в LuCI
Telegram-бот (telegram.uc):
- handle_toggle_mac(): блокировка/разблокировка устройств теперь
через правила firewall (fw4), а не через UCI blocked_macs
- /cb команда: разрешение токенизированных коллбэков для oversized
inline-клавиатур (лимит Telegram 64 байта на callback_data)
- /qh_start, /qh_end: интерактивная настройка часов тихого режима
- in_quiet_hours() и daily report: исправлен localtime(time())
вместо clock() — clock() возвращает [сек, мксек], а не tm-структуру
- Уведомления об обновлениях: current_version вместо installed_version
- Описание команды /restart исправлено
Backend (api.uc, state.uc):
- http_probe(): выделена функция с проверкой HTTP-статуса
(curl выходит 0 даже при 4xx/5xx — теперь проверяем код явно)
- run_speedtest(): учёт curl exit status
- Math.random() заменён на clock()[1] (Math недоступен без import)
- current_year(): убрано +1900 — localtime().year уже полный год
- telegram.uc: добавлены таймауты --max-time 35 и --connect-timeout 10 для всех запросов curl,
ограничение длины сообщений до 3900 символов (предотвращение 400 Bad Request от Telegram API),
безопасная обработка callback_query в try...catch с гарантированным вызовом answerCallbackQuery,
защита основного цикла worker от падений при сбоях сети
- api.uc: добавлены таймауты --max-time и --connect-timeout для всех curl вызовов,
устраняющие зависание подпроцессов при опросе Clash API и проверках соединения через прокси
- watchdog.uc: добавлена функция safe_reload_firewall, устранившая ошибки
'left-hand side is not a function' в процессах ai_heal_nftables и ai_heal_qos
Реализовано:
- Создан модуль api.uc для вынесения системной логики, работы с конфигурацией UCI и метриками устройства.
- Выполнена переработка архитектуры бота на основе роутера команд.
- Добавлено расширенное меню с разделами мониторинга статуса и параметров.
- Внедрено отображение статистики подключений, использования ресурсов, списка серверов и задержек.