Leadaxe 提出用 route.default_domain_resolver 对象形式关闭节点域名的乐观 DNS 缓存
在自有仓库 LxBox 的 issue #168(仍 open)中,Leadaxe 提出一条待办规范:节点地址会轮换的提供商(A 记录每分钟变化、旧 IP 很快失效)遇到核心的乐观 DNS 缓存时会把客户端留在失效地址上——TTL 过期后核心仍返回旧应答并在后台刷新;DNS 规则也帮不上忙,因为一旦设置了 domain_resolver,节点服务器域名就直接由该解析器解析、绕过规则(sing-box dns/router.go 的 Lookup,`if options.Transport != nil`),可用的开关是 domain_resolver 的对象形式。他要求新增模板全局变量 dns_resolver_no_stale(bool、默认 true,标题「Never serve stale answers for node hostnames」,提示为 TTL 过期后重新解析节点域名而非拨号到缓存地址),并把 route.default_domain_resolver 改为对象 {"server": "@dns_default_domain_resolver", "disable_optimistic_cache": "@dns_resolver_no_stale"}:默认构建得到 true、关闭勾选得到 false,核心两种形式都接受;不使用 disable_cache,因为那会对每个连接发一次查询。读取该字段的代码都要同时支持字符串与对象,替换解析器时只改 server;UI 在 DNS 选项卡加开关,LX Backup 中该变量可移植,另需 corpus 用例与一个集成测试。该行为只影响地址由域名给出、自己发起连接的组件(带 server 域名的 outbounds、域名地址的 DNS 服务器、endpoints、chain),不涉及客户端目标域名流量。
作者原文@LeadaxeКонтракт 1.1.119,
contract/TASKS_LXBOX.md§116 п. 10, нормаcontract/docs/TEMPLATE_LANG.md§6.9. Синк контракта с лаунчера: developbaf7e9a2. Остальное из §116 (for_each_node,#do/#final,unique/internal) — отдельная задача, здесь не трогать.Зачем
Провайдеры с ротацией адресов узлов (A-запись меняется раз в минуту, старый IP вскоре глохнет) при optimistic DNS-кэше ядра оставляют клиент на мёртвом адресе: после истечения TTL ядро отдаёт старый ответ и обновляет его в фоне. DNS-правила не помогают: при заданном
domain_resolverимя сервера узла резолвится напрямую этим сервером, минуя правила (sing-boxdns/router.go,Lookup:if options.Transport != nil). Рычаг — объектная формаdomain_resolver.Что сделать
- Глобальная переменная шаблона
dns_resolver_no_stale:bool,default_value: "true",wizard_ui: "fix", title «Never serve stale answers for node hostnames», tooltip «Resolve a node's hostname again once its TTL expires instead of dialing a cached address. Keeps nodes alive with providers that rotate addresses.». Объявление рядом сdns_default_domain_resolver.route.default_domain_resolver— объект:json {"server": "@dns_default_domain_resolver", "disable_optimistic_cache": "@dns_resolver_no_stale"}
Сборка по умолчанию даёт{"server": "<тег>", "disable_optimistic_cache": true}, при выключенной галке —false(bool подставляется литералом, ключ не снимается). Ядро принимает обе формы.disable_cacheне использовать: это запрос на каждое соединение.- Чтение в обеих формах. Весь код, который читает
route.default_domain_resolver(сборка, пост-шаги, показ в UI, подмена резолвера при отбраковке DNS-сервера, импорт sing-box конфига), понимает строку и объект; тот, кто подменяет резолвер, меняет толькоserverи не теряет остальные ключи. Искать grep'ом поapp/lib.- UI: переключатель во вкладке DNS рядом с выбором «Default domain resolver», подписи из title/tooltip переменной, перевод в каталог локализации; изменение помечает конфиг изменённым и хранится в переменных состояния как
dns_optimistic.- LX Backup: переменная переносимая (
contract/registry/vars.jsonуже объявляет её portable): экспорт, импорт, слияние по общим правиламvars.- Корпус обязателен:
contract/corpus/template/subst/default_resolver_object_defaultsиdefault_resolver_object_offпроходят вашим движком. Плюс один интеграционный тест сборки: с галкой и без — объект вrouteсtrue/false; после смены резолвера объект сохраняется.- Релиз-ноты и дока по DNS-настройкам шаблона — по строке.
Где это работает
default_domain_resolver— дефолт для «адрес сервера задан доменом» у компонентов, которые сами открывают соединение: outbounds сserver-доменом, DNS-серверы с доменным адресом без своегоdomain_resolver, endpoints (WireGuard по hostname, Tailscale),chain. Доменов назначения в трафике клиента он не касается: те идут через DNS-правила,dns.finalи действиеresolve, и обычный кэш для сайтов остаётся optimistic.Ограничения
Спорное — текстом в DECISIONS лаунчера через сессию лаунчера. Релиз, теги,
main, force-push — только по явной команде владельца. В отчёте: файлы, как разворачиваетсяrouteпри обоих значениях, какие тесты и кейсы прошли, что разошлось с нормой.