跳转到正文

DNS 配置参考 ​

dns { ... } 配置段使用 dae 语法,运行时语义如下。

顶层键 ​

键默认值含义
bind省略 / ""可选的独立 DNS 监听器;空值只关闭此监听器。
use_hostfalse可重复的 hosts 来源:true 选择 /etc/hosts;路径选择 OxiDNS 兼容规则文件。
client_subnet省略 / ""可选的 EDNS Client Subnet preset:IPv4、IPv4 CIDR、auto 或 auto(IPv4)。
upstream { ... }default: 'udp://223.5.5.5:53'命名上游服务器。第一个显式 upstream 块会替换内置条目。
routing { ... }无规则;request fallback 为 default;response fallback 为 accept有序的 request 与 response 路由。
ipversion_prefer省略:both4 选择 preferipv4;6 选择 preferipv6;0 是 dae 的无偏好,即 both。honk 无法解析的值保持 both,并输出诊断。
optimistic_cachetrue启用正、负缓存的读取与写入。
optimistic_cache_ttl600 秒固定正应答的缓存和报文 TTL,不适用于 NODATA;0 保留应答 TTL。
optimistic_stale_reply_ttl30 秒serve-stale 正应答使用的 TTL;非零值替换每个非 OPT RR 的 TTL,0 保留缓存中已按策略改写的 wire TTL,而不是权威 TTL。
max_cache_size10000缓存最大条目数,最大 100,000。
fixed_domain_ttl { ... }空按域名覆盖正应答和 NODATA 的 TTL;0 禁止缓存所有响应码的应答,包括负应答。

标量值占据该物理行的剩余部分,只在键后的冒号处分隔,因此裸写的 IPv6 地址和 client_subnet: auto(9.9.9.9) 保持完整。解析器只移除一对包围整个值的匹配引号。已开启的标量引号必须在同一行闭合,否则配置失败。

标量设置中,只有引号外、词法单元开头的 # 才开始注释。use_host: /tmp/a#b 选择字面路径 /tmp/a#b,并产生 legacy-glued-hash 警告;注释请写成 use_host: /tmp/a # comment。重复的 use_host 声明保持来源顺序和既有去重行为。optimistic_cache 的布尔简写 t、y、f、n 仍表示假,但产生 legacy-bool-shorthand 警告;请改用 true 或 false。

独立监听器(bind) ​

独立监听器使用主机网络命名空间中的普通套接字,不设置旁路标记。关闭它不会关闭透明 TCP/UDP 53 端口拦截;透明路径遵循流量规则所有权。

值结果
省略或 ""不创建独立监听器。
数字 IP:port,如 127.0.0.1:1053 或 [::1]:1053UDP 监听器。
udp://host:portUDP 监听器。
tcp://host:portTCP 监听器。
tcp+udp://host:port在同一地址和端口创建 TCP 与 UDP 监听器。

主机名必须带 scheme,例如 udp://localhost:1053。IPv6 字面量必须使用方括号。tcp+udp://:1053 这样的空 host 表示通配地址。每种形式都必须显式给出十进制 u16 端口。端口 0 表示申请临时端口;honk 会记录最终选择的地址。

裸主机名无效。解析器也会拒绝 userinfo、path、query、fragment、反斜杠、IPv6 zone identifier、错误的方括号、不支持的 scheme 和超出范围的端口。使用主机名时,honk 按系统解析顺序尝试地址,选择第一个能让全部请求传输成功绑定的地址。绑定同步执行,必须全部成功;任一绑定失败都会关闭其他已选套接字,并令启动失败。

监听器归进程所有。SIGHUP 重载接受语义等价的不同写法,但主机、端口或传输集合的任何变化都会因需要重启而被拒绝。通配或 LAN 侧绑定会暴露一个无认证的递归解析器;必须用主机防火墙限制来源,绝不能发布到不可信网络。

监听 :53 不会抢先于 LAN 流量策略接收查询:即使 dnsmasq 或 dns.bind 占用该 socket,非 must 的 LAN TCP/UDP53 查询仍进入 honk 透明 DNS。主机 loopback 和显式原生 direct(must) 交付仍可到达该普通监听器;见监听器矩阵。

Hosts 快照(use_host) ​

use_host 可以重复配置。true 会按标准 IP canonical-name aliases... 格式加入 /etc/hosts;false 不加入来源;每个路径会按 OxiDNS 兼容的 matcher IP... 规则文件加载。所有来源按声明顺序各读取一次并合并为一个快照;后面的来源会覆盖前面来源中相同的精确名称或 matcher。重复路径只加载一次。

dae
dns {
    use_host: true
    use_host: 'hosts.txt'
}

绝对路径保持原样。相对路径依次优先使用 global.data_dir 下、/var/share/honk 下和工作目录中的已有副本;缺失路径仍定位到 global.data_dir 下。查询路径只使用生成的不可变快照,不执行文件 I/O。

自定义 matcher 支持 full:example.com(或无前缀的精确名称)、domain:example.com(该名称及标签边界内的所有子域)、keyword:text 和 regexp:pattern。匹配优先级依次为精确、最长 domain 后缀、首个匹配的 regexp、首个匹配的 keyword;重复定义同一 matcher 时,后者替换其地址。Full、domain 和 keyword 名称按 ASCII 大小写不敏感方式处理,并归一化末尾点。Regexp 保持大小写敏感,匹配不带末尾点的小写名称;需要忽略大小写时使用 (?i)。重复地址会去除。

在 ipv4only/ipv6only 的硬地址族过滤之后,已知名称的 IN class A 或 AAAA 查询优先于 request 规则(包括 reject)、缓存查询和上游交换。名称存在但没有所请求地址族时,honk 返回 NOERROR/NODATA,且不查询上游。其他 class 和 qtype 继续走正常管线。Hosts 应答使用 60 秒 TTL,并绕过 honk 的 DNS 缓存。

SIGHUP 会构建新快照。任一来源不可读或自定义规则无效时,启动失败;重载时,新运行时代次会在发布前失败,当前运行时代次继续运行。

EDNS Client Subnet(client_subnet) ​

ECS 会向上游权威服务器暴露近似的客户端网络。只有 CDN 本地性确有需要时才应设置 client_subnet,否则请留空。该设置只作用于命名上游。

  • 显式值:IPv4 地址表示 /32;honk 将 IPv4 CIDR 规范化为网络地址。
  • 自动值:auto 向 1.1.1.1:33434 发送带绕过标记的 UDP 探测;auto(IPv4) 只替换探测目标。路由选出的本地地址为公网地址时,honk 直接使用它。否则,每个 TTL 使用 3 个独立 UDP 流,最多检查 12 个 TTL,将首个公网 ICMP 路由跳点作为 /24。探测不依赖 DNS 或 HTTP 服务。
  • 刷新:启动、SIGHUP 以及路由、链路或地址变化会为替换的 DNS 运行时代次解析一个新的不可变值。有界探测失败时,该代次不生成 ECS。

honk 不会覆盖客户端自带的 ECS,包括 /0。生成的 ECS 遵循以下规则:

  • 显式 -> tag 选择和普通流量路由都完成后,以最终解析出的拨号路径为准。直连尝试(包括 -> direct)可以注入 ECS;使用代理叶节点的尝试不注入。
  • honk 独立判断每次 UDP 地址重试。失败的直连尝试所带 ECS 不会进入后续代理重试。
  • 对于没有 ECS 且发往合格命名上游的查询,honk 在缓存/同键请求合并准入后添加配置的选项,校验上游回显的 ECS,并在缓存或答复前只移除自身注入的状态。
  • 有效前缀参与 DNS 策略身份,因此自动前缀变化前后的应答不会交叉复用。asis 请求保持逐字节不变。

上游 ​

每行使用 name: 'uri',后面可选 -> node-or-group:

dae
upstream {
    default: 'udp://223.5.5.5:53'
    google_doh: 'https://dns.google/dns-query' -> proxy
}

上游声明保留名称的原有拼写。dae 请求和应答动作解析器先去除首尾空白,再调用 Rust to_lowercase(),然后识别关键字或生成上游目标。查找时,转换后的目标必须与声明名称完全一致。建议声明名称使用小写:声明 alidns 配合动作 AliDNS 可以匹配;声明和动作都写成 AliDNS 则不能匹配。旧版结构化目标不做大小写转换。

启动和重载时,完整配置校验会检查生效的请求及应答规则和 fallback 引用的每个上游名称。只解析配置的 API 不执行这项校验。第一个显式 upstream 块会替换内置条目,因此必须保留名为 default 的声明、选择已声明的回退上游,或将请求 fallback 设为 reject、asis。即使请求规则覆盖所有查询,也不能免除 fallback 校验。

URI scheme 与默认值 ​

URI 形式运行时协议默认端口 / path
host[:port] 或 udp://host[:port]UDP;应答带 TC 时改用 TCP 重试53
tcp://host[:port]TCP DNS53
tcp+udp://host[:port]当前解析器将其归一化为上面的 UDP 行为53
tls://host[:port]DNS over TLS(DoT)853
https://host[:port][/path]DNS over HTTPS(DoH,HTTP/2)443,path 为 /dns-query
h3://host[:port][/path] 或 http3://host[:port][/path]DNS over HTTP/3(DoH3)443,path 为 /dns-query
quic://host[:port]DNS over QUIC(DoQ)853

对于基于 TLS 的协议,解析器会从主机名派生 tls_server_name。当证书校验要求 DNS 名称时,IP 字面量 endpoint 需要显式 query 参数:

dae
cloudflare_dot: 'tls://1.1.1.1:853?tls_server_name=cloudflare-dns.com'

该参数会从拨号地址中移除,并覆盖从主机名派生的值。

出站选择 ​

末尾的 -> tag 强制该上游经过指定节点或组。省略时,honk 会解析上游目的地址并应用普通流量的 routing { ... } 规则;该路由仍可选择代理叶节点。旧版同一行写法 name: 'uri' outbound: tag 仍然接受。

只有引号外的 -> 与 outbound: 后缀选择出站。它们在 URI 引号内仍是数据;与旧版拆分结果不同的写法产生 legacy-upstream-separator 警告。引号外词法单元开头的 # 开始行尾注释,行为变化产生 legacy-upstream-comment。注释和出站后缀请放在 URI 引号外。

协议经过选定节点/组
UDP(udp、裸地址、tcp+udp)通过该出站承载为 TCP-DNS。
TCP支持通过出站 TCP stream。
DoT支持;TLS 在出站 TCP stream 上运行。
DoH支持;TLS 与 HTTP/2 在出站 TCP stream 上运行。
DoQ支持经出站的 UDP-capable PacketTransport。
DoH3支持经出站的 UDP-capable PacketTransport。

DNS 路由 ​

routing 包含有序的 request 和 response 规则,首条匹配规则生效。同一个条件内的参数按 OR 组合;用 && 连接的条件按 AND 组合。在条件前加 ! 可将其取反。

在 request 和 response 规则中,匹配器参数内的单引号或双引号保护 ,、)、&&、->、# 和 //。只有引号外词法单元开头的 # 开始注释,前面可以是空格或制表符;紧贴前文的 # 仍是数据。// 不是注释:行尾斜杠注释文本使规则无效,产生 legacy-slash-comment 警告并省略整条规则,请改用 #。qtype('a,aaaa') 仍选择两种类型,并产生 legacy-quoted-list 警告;建议裸写或逐项加引号。

未知、格式错误或不支持的条件会产生带位置的警告,并省略整条规则,不会只丢弃合取中的一个条件。未知 QTYPE 名称产生 invalid-qtype,混合列表和取反条件也不例外;请修正名称或使用数字类型码。显式 qtype() 仍不匹配任何类型。

每条完整调用及其动作必须位于同一物理行。不完整的非空行被省略,并产生带位置的 incomplete-dns-rule;DNS 不合并这些行。完整匹配器后的多余文本产生 trailing-matcher-text,并省略整条规则。词法单元开头和参数位置的引号都必须在同一行闭合。未闭合引号产生一条词法诊断;只有每个块都保留了有效右花括号时才省略该行并继续,否则整个文档失败。被未闭合引号覆盖的右花括号不作为块结束符。

条件 ​

语法范围含义
qname(suffix: example.com)Request 与 response点边界后缀;裸参数也表示后缀。
qname(keyword: ads)Request 与 response子串匹配。
qname(full: api.example.com)Request 与 response精确域名匹配。
qname(regex: ...)Request 与 responseRust 正则表达式匹配。
qname(geosite: cn)Request 与 response匹配由指定 geosite 代码展开的域名。
qtype(a, aaaa, ...)Request 与 response匹配 QTYPE 名称或数字 u16。可识别名称为 A、AAAA、CNAME、MX、TXT、NS、PTR、SOA、SRV、HTTPS、SVCB、ANY 和 *。
sip(192.168.50.1, 100.64.0.0/10, 2001:db8::/32)仅 request将逻辑客户端来源与任一所列 IP 主机地址或 CIDR 匹配。
upstream(name, ...)仅 response匹配生成当前应答的上游。
ip(192.0.2.0/24, geoip: private, ...)仅 response任一应答 IP 属于所列 CIDR 或 GeoIP 集时匹配。

透明 53 端口与 dns.bind 入口的逻辑来源是 socket peer;代表已接纳 TCP/UDP 流执行的 DNS 解析使用该流的客户端地址。内部、bootstrap、prefetch 与 Clash API 查询没有逻辑来源。来源未知时,正向和取反的 sip 条件都为 false,请求路由会继续执行下一条规则或 fallback。response 路由不接受 sip。

DNS sip() 与 ip() 共用 IP/CIDR 解码器。裸 IPv4 和 IPv6 地址分别表示 /32 和 /128;CIDR 中的主机位会被清零,并产生 dns-network-host-bits 警告。无效网络产生带位置的 invalid-dns-network 诊断,并省略整条 dae 规则;通过代码构造的无效路由会在路由器或策略构建时失败。等价网络写法使用相同策略标识,不改变参数顺序或删除重复参数。

Request 动作 ​

动作结果
reject返回空的成功应答。
asis拨向拦截所得的原始 DNS 目的地址。透明查询保留入口 transport;UDP 应答带 TC 时,对同一目的地址改用 TCP 重试。独立查询没有原始目的地址,会失败而不会递归拨回监听器。
上游名查询该命名上游。
fallback: reject|asis|<upstream>无 request 规则匹配时使用的动作;默认使用上游 default。

代表已接纳流执行的带来源查询没有被拦截 DNS 服务器的原始目的地址。若其 request 策略选择 asis,解析会 fail closed,绝不会回退到兼容/default 上游。

原生 direct(must) 绕过不同于 asis 重新查询;见DNS 来源/NAT 与投影限制。

Response 动作 ​

动作结果
accept返回当前应答。
reject将当前应答替换为空的成功应答。
上游名通过该命名上游重新查询,然后再次执行 response 路由。
fallback: accept|reject|<upstream>无 response 规则匹配时使用的判定或命名上游;默认为 accept。

NXDOMAIN 和 SERVFAIL 应答直接返回,不经过应答路由,也不应用其规则或回退动作。

一次 response 遍历的最大重新查询深度为三个上游,其中包括初始上游;第四次交换会被拒绝。重新查询形成环路时也会被拒绝。

旧版转换 ​

兼容性配置保留扁平的 routing.rules 条目(包含 domain 和 upstream)以及命名 fallback。这些是结构化兼容字段,不是当前 dae 语法的附加语句。suffix:、keyword:、full: 和 regex: 前缀选择匹配方式;不带前缀的旧版域名采用 full 精确匹配。

生效的请求路由按以下优先级选择:

  1. 新式 request.rules 非空时,使用新式规则及其回退动作,忽略所有旧版字段。
  2. 否则,旧版 rules 非空时,将其转换为请求规则,并保留旧版回退名称的原有拼写。即使另有新式回退动作,也以旧版为准。
  3. 两组规则都为空时,使用已配置的请求路由。仅当其回退动作为 Upstream("default"),且旧版回退值不是 ""、"upstream" 或 "default" 时,才用旧版值替换。

"upstream" 仅在第三个分支中作为哨兵值被忽略;存在旧版规则时,它是普通的上游名称,必须有对应声明。旧版目标按原样精确匹配,不采用 dae 动作的大小写转换规则。

校验诊断保留原始字段路径:新式规则和回退使用 dns.routing.request.rules[i].action 与 dns.routing.request.fallback;转换后的旧版规则使用 dns.routing.rules[i].upstream;转换或提升的回退使用 dns.routing.fallback。 旧版规则生效时,默认回退 upstream 未声明会报告 missing-dns-fallback,空回退没有匹配声明会报告 empty-dns-fallback,其他未声明目标报告 unknown-dns-upstream。诊断保留字段路径,但不回显目标名称或列出已声明名称。省略旧版回退等同于显式设置 upstream;只要声明了该上游,两者都有效。没有旧版规则时,空值和 upstream 哨兵仍会被忽略。

地址族策略 ​

dae 设置内部策略行为
省略both并发执行符合资格的 A 与 AAAA 工作;不压制任何地址族。
ipversion_prefer: 4preferipv4偏好 IPv4,同时保留 IPv6 回退。
ipversion_prefer: 6preferipv6偏好 IPv6,同时保留 IPv4 回退。

偏好模式下,两个地址族仍可查询。对于非偏好族的 A/AAAA 请求,honk 会让偏好族 sibling query 经过同一管线,并保留调用方的逻辑来源、原始目的地址、入口 profile 以及除 QTYPE 外的 wire profile。偏好族有地址时,非偏好族应答会被压制为 NODATA;偏好族没有地址或 sibling 发生普通可用性失败时,则返回非偏好族应答。包括 carrier Capacity 在内的 typed local packet refusal 会以原始原因终止解析,也不会被过期缓存应答替换。相关缓存未命中时,这会增加一次上游查询。

同一策略也决定上游主机名经 bootstrap 解析后的地址拨号顺序。both 与 preferipv4 先拨 IPv4;preferipv6 先拨 IPv6。TCP、DoT、DoH、DoQ、DoH3 以及经代理承载的 DNS 会在拨号失败后继续尝试后续地址。直连 UDP 会把唯一一次重试优先用于另一地址族,再考虑同族的其他地址,并复用成功的 socket。仅兼容格式可用的 ipv4only 与 ipv6only 会把上游拨号候选限制在对应地址族。

内部 ipv4only 和 ipv6only 模式无法通过 dae 的 ipversion_prefer 语法表达。

缓存与固定 TTL ​

键默认值行为
optimistic_cachetrue启用缓存读取与发布。
optimistic_cache_ttl600覆盖正应答的最小 TTL,用于缓存生命周期和返回的记录 TTL,不适用于 NODATA。0 保留应答 TTL。
optimistic_stale_reply_ttl30 秒serve-stale 正应答使用此 TTL;非零值替换每个非 OPT RR 的 TTL。0 保留缓存中已按策略改写的 wire TTL,而不是权威 TTL;此时 outcome TTL 从该 wire 的 extract_min_ttl 得出,不存在正 TTL 时回退为 60 秒。非零值时,即使不存在正 TTL,outcome TTL 仍为配置值。
max_cache_size10000条目上限,也是缓存唯一的上限:任意大小的应答都只占一个条目。大于 100,000 的值钳制为 100,000,0 钳制为一个条目,两者均会告警。
fixed_domain_ttl { domain: seconds }空先于 optimistic_cache_ttl 应用的按域名覆盖;0 禁止缓存所有响应码的应答,包括 NXDOMAIN 和 SERVFAIL。

Request 路由先于缓存查询执行。缓存与后台刷新的标识使用选中的上游或精确 asis 目的地址,不使用原始客户端来源:选择相同交换 scope 的客户端共享条目,选择不同上游或 asis 目的地址的客户端仍相互隔离。偏好地址族的渲染继续保留来源元数据,因此依赖来源的 sibling 策略不会经 foreground singleflight 泄漏。

设置 optimistic_cache_ttl: 0 且没有 fixed_domain_ttl 覆盖时,NOERROR 正应答使用 answer、authority 和 additional 段中所有非 OPT 记录的最小 TTL。任一记录 TTL 为零时,honk 不缓存该应答,并移除精确缓存槽,避免旧地址或更早的负应答再次返回。非零的配置 TTL 或固定 TTL 仍可覆盖零值。其他失败响应码的 TTL 行为不变。

不保留缓存不等于禁用 DNS 路由投影。已接受但不可缓存的正应答(包括 fixed_domain_ttl: 0)使用 wire 中非 OPT 记录的最小正 TTL 作为投影寿命;这些 TTL 全为零时回退为 60 秒。此规则不会改写应答,也不会使其变得可缓存;缓存命中的剩余寿命与 served-stale 寿命保持不变。

负应答 ​

NXDOMAIN 的缓存时间为 min(SOA TTL, SOA MINIMUM, 300) 秒。缺少 SOA 或 SOA 生命周期为零时不缓存,并移除精确缓存键下已有的正、负缓存,避免旧地址再次作为过期应答返回。前台结果替换当时已有的发布结果;后台刷新只移除开始时读取的版本。fixed_domain_ttl: 0 禁止缓存,但不移除已有条目。除此之外,SERVFAIL 仍使用 SOA 得出的生命周期,缺省为 60 秒,并限制在 1..=300 秒。

此处 NODATA 指没有 answer 记录的 NOERROR 应答(ANCOUNT=0);answer 非空时仍归为正应答,包括仅含 CNAME/DNAME 的应答。NODATA 的完整报文保留在正缓存槽中,生命周期为 min(SOA TTL, SOA MINIMUM, 300) 秒。非零 fixed_domain_ttl 优先于 SOA 和上限,即使缺少 SOA 也生效。没有该覆盖值时,缺少 SOA 或生命周期为零的 NODATA 与 NXDOMAIN 一样:移除精确缓存槽,不保留新应答。

缓存的 NODATA 仍可作为过期应答返回;过期应答改写的是 SOA 记录 TTL,而不是 SOA MINIMUM。对上游 NODATA 应用响应策略 reject 会生成空的 NOERROR 报文;其缓存生命周期按相同的 TTL 规则计算,使用被拒绝应答的 SOA,而非合成报文。后续请求会在该生命周期内复用合成应答。

即使响应策略 reject 将回复替换为合成的 NOERROR,缓存生命周期仍按原始上游应答的分类、响应码和 TTL 选择。被拒绝的 REFUSED 因此保留失败响应码的 TTL 规则;没有配置 TTL 或固定 TTL 覆盖时,被拒绝的 NOERROR 正应答使用原始 TTL。

缓存命中时不会递减报文中的记录 TTL。替换只影响内存,不删除已保存的持久化行。

例如:

dae
fixed_domain_ttl {
    ddns.example.org: 10
    nocache.test: 0
}

每个固定 TTL 必须恰好是一个无符号 32 位十进制标量。接受裸值和带引号的值,包括 0 与 4294967295;新接受的带引号十进制值产生 legacy-ttl-quoting 警告。无效或溢出值产生 invalid-ttl,多余词法单元产生 trailing-value;两种情况都会省略该条目。说明文字放在 # 后,不要紧贴数值。

示例 ​

dae
dns {
    # 省略 bind 可保持独立监听器关闭。
    # bind: 'tcp+udp://:1053'
    use_host: true
    ipversion_prefer: 4

    upstream {
        default: 'udp://223.5.5.5:53'
        cloudflare_dot: 'tls://1.1.1.1:853?tls_server_name=cloudflare-dns.com'
        google_doh: 'https://dns.google/dns-query' -> proxy
    }

    routing {
        request {
            sip(192.168.50.0/24, 100.64.0.0/10) -> google_doh
            qname(geosite: category-ads-all) -> reject
            qname(suffix: cn) -> default
            qtype(https) -> reject
            fallback: default
        }
        response {
            upstream(google_doh) -> accept
            ip(geoip: private) && !qname(geosite: cn) -> google_doh
            fallback: accept
        }
    }

    optimistic_cache: true
    optimistic_cache_ttl: 600
    max_cache_size: 10000
    fixed_domain_ttl {
        ddns.example.org: 10
        nocache.test: 0
    }
}

相关文档 ​


来源:honk 上游文档,采用 GPL-3.0-only 许可证。