feat(core): 改进 DNS 响应构造逻辑以支持扩展错误码和诊断信息

在 writeReply 函数中,增强对上游 EDNS 选项的处理:
- 继承上游的扩展 RCODE 与 EDNS 版本号
- 可选透传 EDE(Extended DNS Errors)记录,保留更多诊断信息
- 明确设置 UDPSize、DO 位及 EDNS 版本转换逻辑

这有助于提升调试能力与协议兼容性,同时保持客户端请求的一致性。
```
This commit is contained in:
2025-10-14 13:55:01 +08:00
parent 05d3be286e
commit bcd0914b2f
2 changed files with 26 additions and 5 deletions

BIN
dot

Binary file not shown.

31
main.go
View File

@@ -392,20 +392,22 @@ func getDOFlag(m *dns.Msg) bool {
* 响应构造:使用客户端请求头构造 reply复制上游内容 * 响应构造:使用客户端请求头构造 reply复制上游内容
******************************************************************/ ******************************************************************/
// writeReply 根据客户端请求构造响应骨架:复制上游 Answer/Ns/非伪Extra // writeReply 根据客户端请求构造响应:复制上游 Answer/Ns/非伪 Extra
// 并按照**客户端请求**重建 OPTUDPSize/DO避免直接采纳上游的 OPT/TSIG。 // 并按客户端请求重建 OPTUDPSize/DO同时继承上游的扩展 RCODE 与 EDNS 版本;
// 可选透传上游的 EDEExtended DNS Errors以保留诊断信息。
func writeReply(w dns.ResponseWriter, req, upstream *dns.Msg) { func writeReply(w dns.ResponseWriter, req, upstream *dns.Msg) {
if upstream == nil { if upstream == nil {
dns.HandleFailed(w, req) dns.HandleFailed(w, req)
return return
} }
out := new(dns.Msg) out := new(dns.Msg)
out.SetReply(req) out.SetReply(req)
out.Authoritative = false out.Authoritative = false
out.RecursionAvailable = upstream.RecursionAvailable out.RecursionAvailable = upstream.RecursionAvailable
out.AuthenticatedData = upstream.AuthenticatedData out.AuthenticatedData = upstream.AuthenticatedData
out.CheckingDisabled = req.CheckingDisabled // 反映客户端 CD 位 out.CheckingDisabled = req.CheckingDisabled // 反映客户端 CD 位
out.Rcode = upstream.Rcode out.Rcode = upstream.Rcode // 主 RCODE低 4 位)
out.Answer = upstream.Answer out.Answer = upstream.Answer
out.Ns = upstream.Ns out.Ns = upstream.Ns
@@ -417,20 +419,39 @@ func writeReply(w dns.ResponseWriter, req, upstream *dns.Msg) {
} }
extras = append(extras, rr) extras = append(extras, rr)
} }
// 基于客户端请求镜像 EDNSUDPSize + DO保持传输一致性
// 基于客户端请求镜像 EDNSUDPSize + DO
if ro := req.IsEdns0(); ro != nil { if ro := req.IsEdns0(); ro != nil {
o := new(dns.OPT) o := new(dns.OPT)
o.Hdr.Name = "." o.Hdr.Name = "."
o.Hdr.Rrtype = dns.TypeOPT o.Hdr.Rrtype = dns.TypeOPT
// 与客户端保持一致的 UDPSize / DO 位
o.SetUDPSize(ro.UDPSize()) o.SetUDPSize(ro.UDPSize())
if ro.Do() { if ro.Do() {
o.SetDo() o.SetDo()
} }
// 如需转发 NSID/COOKIE 等可在此附加;为减少复杂性与缓存污染,建议保守最小集合
// 继承上游的扩展 RCODE 与 EDNS 版本(注意不同版本签名差异,这里显式转换)
if uo := upstream.IsEdns0(); uo != nil {
// 你当前库期望 uint16这里强转若你的库期望 uint8也可改成 uint8(...)
o.SetExtendedRcode(uint16(uo.ExtendedRcode()))
o.SetVersion(uint8(uo.Version()))
// 可选:透传只读的 EDE 诊断信息
for _, opt := range uo.Option {
if ede, ok := opt.(*dns.EDNS0_EDE); ok {
o.Option = append(o.Option, ede)
}
}
}
extras = append(extras, o) extras = append(extras, o)
} }
out.Extra = extras out.Extra = extras
out.Compress = true out.Compress = true
if err := w.WriteMsg(out); err != nil { if err := w.WriteMsg(out); err != nil {
log.Printf("[write] WriteMsg error: %v", err) log.Printf("[write] WriteMsg error: %v", err)
} }