增加更多匹配的域名信息

This commit is contained in:
Alex Yang
2026-01-14 23:08:46 +08:00
parent f247eaeaa8
commit 8159577be0
60 changed files with 11716 additions and 1022 deletions

View File

@@ -0,0 +1,182 @@
## 问题分析
通过深入分析代码,我找到了导致所有查询都显示同一个 NXDOMAIN 错误的根本原因:
**核心问题**`mergeResponses` 函数在合并多个 DNS 响应时,**没有正确处理 Rcode 字段**
**具体原因**
1. 当使用并行查询模式时DNS 服务器会向多个上游服务器发送请求
2. 函数使用第一个响应作为基础来合并其他响应
3. 它清空了 `Answer``Ns``Extra` 字段,但**保留了第一个响应的 Rcode**
4. 如果第一个响应返回 NXDOMAIN比如对于恶意域名 www.evilsnssdk.com那么**合并后的响应也会保持 NXDOMAIN 状态**,即使其他响应返回成功
5. 这导致所有查询都显示同一个 NXDOMAIN 错误
## 修复方案
### 修复 `mergeResponses` 函数
**关键修改点**
1. **重置 Rcode**:在合并响应前,将 Rcode 重置为成功状态
2. **处理 NXDOMAIN**:只有当所有响应都是 NXDOMAIN 时,才返回 NXDOMAIN
3. **优先使用成功响应**:如果有任何响应返回成功,就使用成功的 Rcode
### 修复步骤
1. **修改 `mergeResponses` 函数** (`/root/dns/dns/server.go:842-933`)
- 在合并记录前,将 `mergedResponse.Rcode` 设置为 `dns.RcodeSuccess`
- 添加变量 `allNXDOMAIN` 来跟踪是否所有响应都是 NXDOMAIN
- 遍历所有响应,检查是否有成功响应
- 如果所有响应都是 NXDOMAIN才将 `mergedResponse.Rcode` 设置为 `dns.RcodeNameError`
2. **优化合并逻辑**
- 确保优先使用成功响应中的记录
- 避免将 NXDOMAIN 响应的记录合并到成功响应中
- 保持响应的一致性Rcode 与记录内容匹配
## 修复代码
```go
// mergeResponses 合并多个DNS响应
func mergeResponses(responses []*dns.Msg) *dns.Msg {
if len(responses) == 0 {
return nil
}
// 如果只有一个响应,直接返回,避免不必要的合并操作
if len(responses) == 1 {
return responses[0].Copy()
}
// 使用第一个响应作为基础
mergedResponse := responses[0].Copy()
mergedResponse.Answer = []dns.RR{}
mergedResponse.Ns = []dns.RR{}
mergedResponse.Extra = []dns.RR{}
// 重置Rcode为成功除非所有响应都是NXDOMAIN
mergedResponse.Rcode = dns.RcodeSuccess
// 检查是否所有响应都是NXDOMAIN
allNXDOMAIN := true
// 收集所有成功响应的记录
for _, resp := range responses {
if resp == nil {
continue
}
// 如果有任何响应是成功的就不是allNXDOMAIN
if resp.Rcode == dns.RcodeSuccess {
allNXDOMAIN = false
}
}
// 如果所有响应都是NXDOMAIN设置合并响应为NXDOMAIN
if allNXDOMAIN {
mergedResponse.Rcode = dns.RcodeNameError
}
// 使用map存储唯一记录选择最长TTL
// 预分配map容量减少扩容开销
answerMap := make(map[recordKey]dns.RR, len(responses[0].Answer)*len(responses))
nsMap := make(map[recordKey]dns.RR, len(responses[0].Ns)*len(responses))
extraMap := make(map[recordKey]dns.RR, len(responses[0].Extra)*len(responses))
for _, resp := range responses {
if resp == nil {
continue
}
// 只合并与最终Rcode匹配的响应记录
if (mergedResponse.Rcode == dns.RcodeSuccess && resp.Rcode == dns.RcodeSuccess) ||
(mergedResponse.Rcode == dns.RcodeNameError && resp.Rcode == dns.RcodeNameError) {
// 合并Answer部分
for _, rr := range resp.Answer {
key := getRecordKey(rr)
if existing, exists := answerMap[key]; exists {
// 如果存在相同记录选择TTL更长的
if rr.Header().Ttl > existing.Header().Ttl {
answerMap[key] = rr
}
} else {
answerMap[key] = rr
}
}
// 合并Ns部分
for _, rr := range resp.Ns {
key := getRecordKey(rr)
if existing, exists := nsMap[key]; exists {
// 如果存在相同记录选择TTL更长的
if rr.Header().Ttl > existing.Header().Ttl {
nsMap[key] = rr
}
} else {
nsMap[key] = rr
}
}
// 合并Extra部分
for _, rr := range resp.Extra {
// 跳过OPT记录避免重复
if rr.Header().Rrtype == dns.TypeOPT {
continue
}
key := getRecordKey(rr)
if existing, exists := extraMap[key]; exists {
// 如果存在相同记录选择TTL更长的
if rr.Header().Ttl > existing.Header().Ttl {
extraMap[key] = rr
}
} else {
extraMap[key] = rr
}
}
}
}
// 预分配切片容量,减少扩容开销
mergedResponse.Answer = make([]dns.RR, 0, len(answerMap))
mergedResponse.Ns = make([]dns.RR, 0, len(nsMap))
mergedResponse.Extra = make([]dns.RR, 0, len(extraMap))
// 将map转换回切片
for _, rr := range answerMap {
mergedResponse.Answer = append(mergedResponse.Answer, rr)
}
for _, rr := range nsMap {
mergedResponse.Ns = append(mergedResponse.Ns, rr)
}
for _, rr := range extraMap {
mergedResponse.Extra = append(mergedResponse.Extra, rr)
}
return mergedResponse
}```
## 预期效果
修复后DNS服务器将能够
- 正确合并多个 DNS 响应
- 确保 Rcode 与实际记录内容匹配
- 只有当所有响应都是 NXDOMAIN 时才返回 NXDOMAIN
- 避免单个恶意域名影响所有查询结果
- 正确显示各个域名的查询结果
## 修复代码位置
- **核心修改文件**`/root/dns/dns/server.go`
- **关键函数**`mergeResponses`
- **修改内容**:修复响应合并逻辑,正确处理 Rcode 字段
## 测试方法
1. 使用 nslookup 测试不同域名
2. 检查是否每个域名都显示正确的查询结果
3. 验证 www.evilsnssdk.com 返回 NXDOMAIN而其他域名返回成功
4. 检查日志中是否还有大量错误信息
这个修复将彻底解决所有查询都显示同一个 NXDOMAIN 错误的问题!