This commit is contained in:
Alex Yang
2026-04-12 21:40:22 +08:00
parent 7abc2b5339
commit f9e2e5a6bc
52 changed files with 3388 additions and 368846 deletions
@@ -0,0 +1,79 @@
# DNS 查询超时问题分析与修复计划
## 问题现状
- 客户端请求 DNS 解析需要两次才能成功
- 第一次返回 SERVFAIL,第二次才有正确结果
- 错误数量高达 29,594 条
## 已尝试的修复
-`queryTimeout` 从 5ms 修改为 5000ms
- 服务器已重启
## 需要深入调查的问题
### 1. 超时配置的实际生效情况
- 检查配置文件中 queryTimeout 的单位(毫秒还是秒)
- 验证代码中如何解析和使用这个配置值
- 确认配置是否真正被重新加载
### 2. 上游 DNS 服务器可达性
- 测试上游 DNS 服务器 (10.35.10.200, 106.14.121.141) 是否可达
- 检查上游服务器的响应时间
- 验证上游服务器是否支持递归查询
### 3. 网络层面问题
- 检查是否有防火墙规则阻止 DNS 查询
- 验证 UDP 53 端口是否畅通
- 检查是否存在网络延迟或丢包
### 4. DNS 缓存逻辑问题
- 检查缓存是否正常工作
- 验证缓存 TTL 设置是否合理
- 确认缓存命中时是否还会超时
### 5. 代码逻辑问题
- 检查并行查询模式的实现
- 验证超时处理逻辑是否正确
- 查看是否有竞态条件导致的问题
## 调查步骤
### 第一步:检查当前配置和日志
1. 查看当前配置文件中的 queryTimeout 值
2. 检查最新日志中的超时设置
3. 统计错误日志中的错误类型分布
### 第二步:测试上游 DNS 服务器
1. 直接测试上游 DNS 服务器的响应
2. 测量到上游服务器的网络延迟
3. 验证上游服务器是否工作正常
### 第三步:分析代码中的超时处理
1. 查看 forwardDNSRequestWithCache 函数中的超时逻辑
2. 检查 dns.Client 的 Timeout 设置
3. 验证并行查询的超时处理
### 第四步:检查网络配置
1. 检查防火墙规则
2. 测试 DNS 端口连通性
3. 检查路由表和网络接口
## 可能的修复方案
### 方案 A:修复配置单位问题
如果配置单位是秒而不是毫秒,需要将 5000 改为 5
### 方案 B:修复上游服务器配置
如果上游服务器不可达,需要更换可用的 DNS 服务器
### 方案 C:修复代码中的超时逻辑
如果代码中存在超时处理 bug,需要修改相关代码
### 方案 D:优化查询模式
如果并行查询模式有问题,可以切换到 fastest-ip 模式
## 验证标准
1. DNS 查询一次成功,不需要重试
2. 错误率降低到接近 0
3. 平均响应时间合理(<100ms)
4. 日志中不再有大量 SERVFAIL 记录
@@ -0,0 +1,368 @@
# DNS 查询 SERVFAIL 问题分析(已禁用 DNSSEC
## 问题描述
客户端请求 DNS 解析时,需要请求两次才能得到正常结果:
* 第一次请求:返回 SERVFAIL(服务器失败)
* 第二次请求:返回正常解析结果
## 重要信息
**DNSSEC 已禁用** - 因此问题与 DNSSEC 验证无关
## 可能的原因分析
### 1. 缓存初始化时机问题
**现象**:第一次查询时缓存为空,第二次查询命中缓存
**可能原因**
* 缓存加载逻辑可能存在问题
* 第一次查询的响应没有正确缓存
* 缓存键生成或查找逻辑有问题
**相关代码位置**
* `/root/dns-server/dns/cache.go` - `Get``Set` 方法
* `/root/dns-server/dns/server.go` - `handleCacheResponse` 方法
### 2. DNSSEC 验证超时或失败
**现象**:第一次查询触发 DNSSEC 验证,验证超时导致 SERVFAIL;第二次直接使用缓存结果
**可能原因**
* DNSSEC 验证逻辑可能存在超时
* 验证失败后没有正确处理响应
* `verifyDNSSEC` 方法可能阻塞或失败
**相关代码位置**
* `/root/dns-server/dns/server.go` - `verifyDNSSEC` 方法
* 第 1563 行左右的 DNSSEC 验证逻辑
### 3. 上游服务器选择逻辑问题
**现象**:第一次查询时服务器状态统计为空,导致选择了不可用的服务器
**可能原因**
* 服务器健康检查逻辑可能有问题
* 第一次查询时没有正确选择最快的服务器
* 并行查询逻辑可能存在竞态条件
**相关代码位置**
* `/root/dns-server/dns/server.go` - `forwardDNSRequestWithCache` 方法
* 第 1473-1600 行的并行查询逻辑
### 4. 响应处理逻辑问题
**现象**:第一次查询返回的响应被错误地判断为失败
**可能原因**
* 响应代码判断逻辑可能有误
* `hasValidRecords` 判断可能过于严格
* 某些类型的响应被错误地拒绝
**相关代码位置**
* `/root/dns-server/dns/server.go` - 第 871-891 行的响应验证逻辑
### 5. 并发锁竞争问题
**现象**:第一次查询时遇到锁竞争,导致超时返回 SERVFAIL
**可能原因**
* 缓存访问锁可能与其他操作冲突
* 统计信息更新可能导致阻塞
* 日志写入可能阻塞查询流程
**相关代码位置**
* `/root/dns-server/dns/cache.go` - 各种锁操作
* `/root/dns-server/dns/server.go` - 统计更新操作
## 调试建议
### 方案 1:增强日志记录
在关键位置添加详细日志:
* 缓存命中/未命中时
* DNSSEC 验证开始/结束/结果
* 上游服务器选择和响应
* 响应验证过程
### 方案 2:检查时间戳
分析查询日志中的时间戳:
* 第一次查询的实际处理时间
* 是否有超时现象
* DNSSEC 验证耗时
### 方案 3:复现测试
使用 `dig` 命令测试:
```bash
# 清除缓存后测试
dig @127.0.0.1 example.com +norecurse
dig @127.0.0.1 example.com
```
## 根本原因已确定
经过详细代码分析,发现问题出在 **并行请求模式下的响应处理逻辑缺陷**
### 问题流程:
1. **第一次查询**
* 缓存未命中,向上游服务器发起并行查询
* 代码进入并行请求模式(第 1490-1777 行)
* 收到上游返回的响应,`resp.response.Rcode == dns.RcodeSuccess`
* **关键问题**:在 `noDNSSEC` 模式下(第 1572-1588 行),代码直接返回第一个成功响应
* **但是**:没有检查响应是否包含有效记录(Answer/Ns/Extra
* 如果上游返回的是 `Rcode=Success``Answer 为空` 的响应(例如某些 DNS 服务器的截断响应)
* 该响应被直接发送给客户端
*`handleUpstreamRequest` 第 870-891 行,由于 `hasValidRecords` 检查失败(Answer 为空),`response.Rcode` 没有被设置为 `RcodeSuccess`
* 最终客户端收到 SERVFAIL 错误
2. **第二次查询**
* 第一次查询的响应已经被缓存(即使 Answer 为空,只要 Rcode=Success 就会被缓存)
* 直接从缓存返回
* 但此时缓存的可能是完整响应(因为上游第二次返回了完整结果)
* 客户端收到正常解析结果
### 具体代码问题:
**问题 1:并行模式下缺少 hasValidRecords 检查**
`/root/dns-server/dns/server.go` 第 1572-1588 行(并行模式,noDNSSEC 分支):
```go
if noDNSSEC && !bestResponseSent {
// 不验证 DNSSEC 的域名:直接返回第一个成功响应
if fastestResponse == nil || resp.rtt < fastestRtt {
fastestResponse = resp.response
fastestRtt = resp.rtt
fastestServer = resp.server
fastestDnssecServer = dnssecServerForResponse
fastestHasDnssec = false
// 立即发送结果,快速返回(只发送一次)
resultChan <- struct {
response: fastestResponse, // 可能 Answer 为空!
...
}
bestResponseSent = true
}
}
```
**对比**:在串行模式(第 1452-1469 行)中有 `hasValidRecords` 检查:
```go
// 检查响应是否包含有效的记录,如果包含,将 Rcode 设置为成功
hasValidRecords := false
if len(response.Answer) > 0 {
hasValidRecords = true
} else if len(response.Ns) > 0 {
hasValidRecords = true
} else if len(response.Extra) > 0 {
for _, rr := range response.Extra {
if rr.Header().Rrtype != dns.TypeOPT {
hasValidRecords = true
break
}
}
}
if hasValidRecords {
response.Rcode = dns.RcodeSuccess
}
```
**问题 2:Rcode 判断逻辑不准确**
第 1562 行只检查 `resp.response.Rcode == dns.RcodeSuccess`,但有些 DNS 服务器可能返回:
* `Rcode=Success``Answer 为空`(例如需要 TCP 回退时)
* 这种情况下应该继续等待其他服务器的响应,而不是立即返回
## 建议的修复步骤
### 方案 1:在并行模式下添加 hasValidRecords 检查(推荐)
**问题**:并行模式(noDNSSEC 分支)缺少 hasValidRecords 检查
**解决方案**
在并行模式的快速返回逻辑中添加 hasValidRecords 检查,确保只返回包含有效记录的响应
```go
// 修改前
if noDNSSEC && !bestResponseSent {
// 不验证 DNSSEC 的域名:直接返回第一个成功响应
if fastestResponse == nil || resp.rtt < fastestRtt {
fastestResponse = resp.response
fastestRtt = resp.rtt
fastestServer = resp.server
fastestDnssecServer = dnssecServerForResponse
fastestHasDnssec = false
// 立即发送结果,快速返回(只发送一次)
resultChan <- struct {
response: fastestResponse,
...
}
bestResponseSent = true
}
}
// 修改后
if noDNSSEC && !bestResponseSent {
// 不验证 DNSSEC 的域名:返回第一个包含有效记录的成功响应
if fastestResponse == nil || resp.rtt < fastestRtt {
// 检查响应是否包含有效记录
hasValidRecords := false
if len(resp.response.Answer) > 0 {
hasValidRecords = true
} else if len(resp.response.Ns) > 0 {
hasValidRecords = true
} else if len(resp.response.Extra) > 0 {
for _, rr := range resp.response.Extra {
if rr.Header().Rrtype != dns.TypeOPT {
hasValidRecords = true
break
}
}
}
// 只有包含有效记录才返回
if hasValidRecords {
fastestResponse = resp.response
fastestRtt = resp.rtt
fastestServer = resp.server
fastestDnssecServer = dnssecServerForResponse
fastestHasDnssec = false
// 立即发送结果,快速返回(只发送一次)
resultChan <- struct {
response: fastestResponse,
...
}
bestResponseSent = true
}
}
}
```
### 方案 2:改进 Rcode 判断逻辑
**问题**:只检查 Rcode,不检查 Answer 内容
**解决方案**
将 Rcode 检查和 Answer 检查结合起来,确保响应真正有效
```go
// 修改前
if resp.response.Rcode == dns.RcodeSuccess || resp.response.Rcode == dns.RcodeNameError {
// 处理响应
}
// 修改后
// 检查 Rcode 和 Answer 内容
isValidResponse := false
if resp.response.Rcode == dns.RcodeSuccess {
// 成功响应需要包含有效记录
if len(resp.response.Answer) > 0 || len(resp.response.Ns) > 0 {
isValidResponse = true
}
} else if resp.response.Rcode == dns.RcodeNameError {
// NXDOMAIN 响应可以没有 Answer
isValidResponse = true
}
if isValidResponse {
// 处理有效响应
}
```
### 方案 3:增加重试机制
**问题**:第一次返回空 Answer 后没有重试
**解决方案**
检测到空 Answer 时,继续等待其他服务器的响应或重试
```go
// 在并行模式中添加重试逻辑
if len(resp.response.Answer) == 0 && resp.response.Rcode == dns.RcodeSuccess {
// Answer 为空,可能是截断响应,继续等待其他服务器
logger.Debug("响应 Answer 为空,继续等待其他服务器", "domain", domain)
continue // 不立即返回,继续等待
}
```
### 方案 4:调整上游 DNS 服务器配置
**问题**:某些上游 DNS 服务器可能返回截断响应
**解决方案**
1. 检查上游 DNS 服务器配置
2. 尝试更换更可靠的上游 DNS 服务器
3. 增加 UDP 缓冲区大小,避免截断
## 推荐修复顺序
1. **立即修复**:方案 1(在并行模式下添加 hasValidRecords 检查)- 直接解决问题
2. **优化改进**:方案 2(改进 Rcode 判断逻辑)- 提高健壮性
3. **可选优化**:方案 3(增加重试机制)- 进一步提高可靠性
## 需要查看的关键代码
1. `verifyDNSSEC` 方法实现
2. `forwardDNSRequestWithCache` 中的 DNSSEC 验证逻辑
3. `handleUpstreamRequest` 中的响应处理
4. DNSSEC 相关配置和超时设置
@@ -0,0 +1,332 @@
# 域名信息管理项重新设计计划
## 1. 现状分析
### 1.1 现有实现
- **后端实现**`shield/domain_info_manager.go` 实现了域名信息管理器
- **前端实现**`static/js/modules/domain-info.js` 实现了前端管理界面
- **API接口**`http/domain_info_handlers.go` 提供了相关API
- **配置管理**`config/config.go` 定义了配置结构
### 1.2 现有功能
1. **三个列表管理**
- 域名信息列表 (domain-info)
- 威胁域名列表 (threat-database)
- 跟踪器列表 (tracker)
2. **现有API**
- `/api/domain-info` - 获取域名信息列表和统计
- `/api/domain-info/update` - 更新所有域名信息
- `/api/domain-info/update/{type}` - 更新指定类型的域名信息
- `/api/domain-info/query` - 查询单个域名信息
3. **现有前端功能**
- 显示三个列表的详细信息
- 显示各列表的规则数量
- 显示最后更新时间
- 支持更新所有列表和单个列表
### 1.3 问题与不足
1. **数据更新机制**
- 威胁数据库的URL配置错误(使用了src路径而非raw路径)
- 缺少更新状态的实时反馈
2. **前端界面**
- 界面设计较为简单
- 缺少视觉层次感和交互体验
3. **数据管理**
- 缺少数据验证和错误处理
- 缓存机制可以进一步优化
4. **用户功能**
- 缺少用户增加和删除列表的功能
### 1.4 列表文件分析
通过下载和分析列表文件,了解到:
1. **domain-info.json**
- 格式:JSON
- 结构:
- categories: 分类信息(键值对)
- domains: 域名信息(嵌套结构,第一层公司名称,第二层具体域名服务)
- timeUpdated: 更新时间
- 规模:4444行
- 用途:提供域名分类信息
2. **threats-database.csv**
- 格式:CSV
- 结构:包含 type, name, riskLevel, domain 字段
- 规模:73012行(约3.1MB
- 用途:提供威胁域名数据库
3. **trackers.json**
- 格式:JSON
- 结构:
- categories: 分类信息
- timeUpdated: 更新时间
- trackerDomains: 跟踪器域名
- trackers: 跟踪器信息(嵌套结构)
- 规模:25345行
- 用途:提供跟踪器域名信息
### 1.5 嵌套结构处理注意事项
- **domain-info.json**domains字段包含多层嵌套结构,需要递归处理
- **trackers.json**trackers字段包含嵌套结构,需要正确解析
- **数据量级**:威胁数据库较大(7万多行),需要考虑性能优化
## 2. 重新设计方案
### 2.1 后端改进
1. **修复URL配置**
- 修正威胁数据库的URL,使用raw路径
2. **增强更新机制**
- 添加更新状态的实时反馈
- 改进错误处理和日志记录
3. **优化缓存策略**
- 实现更智能的缓存过期机制
- 增加缓存验证
4. **添加API端点**
- `/api/domain-info/status` - 获取更新状态
- `/api/domain-info/refresh` - 强制刷新缓存
- `/api/domain-info/add` - 添加新的域名信息列表
- `/api/domain-info/remove` - 删除域名信息列表
5. **实现用户增删功能**
- 支持用户添加自定义域名信息列表
- 支持用户删除现有列表
- 实现列表配置的持久化存储
6. **嵌套结构处理**
- 优化JSON嵌套结构的解析和处理
- 实现高效的嵌套数据存储和查询
### 2.2 前端改进
1. **界面重新设计**
- 采用卡片式布局,增强视觉层次感
- 添加统计信息概览区域
- 优化表格布局和交互体验
2. **功能增强**
- 添加更新进度指示
- 实现批量操作功能
- 增加数据导出功能
- 添加列表增加和删除功能
- 实现列表编辑功能
3. **响应式设计**
- 优化移动端显示
- 确保在不同设备上的良好体验
4. **数据展示优化**
- 实现嵌套结构的可视化展示
- 支持展开/折叠嵌套数据
### 2.3 数据管理优化
1. **数据验证**
- 增加URL格式验证
- 实现数据完整性检查
- 验证列表类型和格式
2. **错误处理**
- 提供更详细的错误信息
- 实现优雅的错误展示
3. **性能优化**
- 减少不必要的网络请求
- 优化数据加载和渲染
- 实现大型列表的分页加载
- 优化嵌套结构的处理性能
## 3. 具体实现步骤
### 3.1 后端修改
1. **修复配置**
- 修正 `config/config.go` 中的威胁数据库URL
2. **增强域名信息管理器**
-`shield/domain_info_manager.go` 中添加更新状态管理
- 实现更详细的错误处理
- 完善列表增删功能
- 优化嵌套JSON结构的解析
3. **扩展API接口**
-`http/domain_info_handlers.go` 中添加新的API端点
- 实现状态查询、缓存刷新、列表增删功能
4. **实现配置持久化**
- 确保用户添加的列表配置能够持久保存
- 实现配置文件的自动更新
### 3.2 前端修改
1. **重新设计界面**
- 更新 `static/js/modules/domain-info.js` 中的渲染逻辑
- 实现卡片式布局和统计概览
2. **增强交互功能**
- 添加更新进度指示
- 实现批量操作和数据导出
- 添加列表增加和删除功能
- 实现列表编辑表单
3. **优化用户体验**
- 添加加载动画和过渡效果
- 实现更友好的错误提示
- 提供操作确认机制
4. **实现嵌套数据展示**
- 支持展开/折叠嵌套结构
- 优化大型嵌套数据的渲染性能
### 3.3 测试与验证
1. **功能测试**
- 测试所有API端点
- 验证数据更新功能
- 测试列表增删功能
- 测试错误处理
2. **性能测试**
- 测试数据加载速度
- 验证缓存机制
- 测试响应式布局
3. **安全性测试**
- 验证输入验证
- 测试错误处理安全性
## 4. 预期效果
### 4.1 功能改进
- ✅ 正确显示三个列表的数量
- ✅ 支持更新所有/单个列表
- ✅ 显示更新时间
- ✅ 提供更新状态反馈
- ✅ 实现批量操作功能
- ✅ 支持用户增加和删除列表
- ✅ 实现列表编辑功能
- ✅ 正确处理JSON嵌套结构
### 4.2 界面改进
- ✅ 现代化的卡片式布局
- ✅ 清晰的统计信息概览
- ✅ 良好的响应式设计
- ✅ 流畅的交互体验
- ✅ 直观的列表管理界面
- ✅ 嵌套数据的可视化展示
### 4.3 性能改进
- ✅ 优化的数据加载速度
- ✅ 智能的缓存机制
- ✅ 减少网络请求
- ✅ 提高系统稳定性
- ✅ 支持大型列表的高效处理
- ✅ 优化嵌套结构的处理性能
## 5. 风险与应对措施
### 5.1 风险
1. **数据加载失败**
- 远程数据源不可用
- 网络连接问题
2. **性能问题**
- 大量数据导致加载缓慢
- 缓存策略不当
- 嵌套结构处理性能问题
3. **兼容性问题**
- 浏览器兼容性
- 移动设备适配
4. **用户操作风险**
- 误删除重要列表
- 添加无效的列表配置
5. **数据结构风险**
- JSON嵌套结构解析错误
- 数据格式不一致
### 5.2 应对措施
1. **数据加载失败**
- 实现错误重试机制
- 使用缓存数据作为备用
- 提供详细的错误信息
2. **性能问题**
- 实现数据分页加载
- 优化缓存策略
- 使用异步加载
- 实现嵌套结构的懒加载
3. **兼容性问题**
- 使用现代前端技术
- 测试主流浏览器
- 实现响应式设计
4. **用户操作风险**
- 实现操作确认机制
- 提供默认列表恢复功能
- 验证用户输入的有效性
5. **数据结构风险**
- 实现健壮的JSON解析
- 添加数据结构验证
- 提供详细的错误提示
## 6. 实施步骤
1. **准备阶段**
- 代码分析和问题识别
- 设计新的界面和功能
2. **后端开发**
- 修复URL配置
- 扩展API接口
- 实现列表增删功能
- 优化嵌套结构处理
3. **前端开发**
- 重新设计界面
- 实现交互功能
- 添加列表管理功能
- 实现嵌套数据展示
4. **测试阶段**
- 功能测试
- 性能测试
- 兼容性测试
5. **部署阶段**
- 代码审查
- 部署更新
- 监控运行状态
## 7. 结论
通过重新设计域名信息管理项,我们将实现:
1. **功能完善**:确保三个列表的数量显示、更新功能、时间显示,以及用户增删列表的能力,同时正确处理JSON嵌套结构
2. **界面优化**:提供现代化、美观的用户界面,支持嵌套数据的可视化展示
3. **性能提升**:优化数据加载和缓存机制,提高嵌套结构处理性能
4. **用户体验**:提供流畅、直观的操作体验
这些改进将使域名信息管理系统更加实用、可靠和用户友好,满足用户对域名信息管理的各种需求。
@@ -0,0 +1,186 @@
# 域名信息远程列表功能实施计划
## 概述
在 DNS 服务器的屏蔽管理系统中增加三类域名信息的远程异步加载功能,用于增强日志详情 API 和威胁告警 API 的域名信息查询能力。
## 需要添加的远程域名信息
### 1. 域名信息列表
- **URL**: `https://gitea.amazehome.xyz/AMAZEHOME/domain-info/raw/branch/main/domains/domain-info.json`
- **用途**: 用于查询日志详情 API 中的域名信息
- **格式**: JSON
### 2. 威胁告警域名列表
- **URL**: `https://gitea.amazehome.xyz/AMAZEHOME/domain-info/src/branch/main/threats/threats-database.csv`
- **用途**: 用于威胁告警 API 的威胁域名信息
- **格式**: CSV
### 3. 跟踪器域名列表
- **URLs**:
- `https://gitea.amazehome.xyz/AMAZEHOME/domain-info/raw/branch/main/tracker/trackers.json`
- **用途**: 用于查询日志详情 API 中的跟踪器信息
- **格式**: JSON
## 实施步骤
### 步骤 1: 扩展配置文件结构
1.1 在 `config/config.go` 中添加新的配置结构:
- `DomainInfoConfig` - 域名信息配置
- 包含三个远程列表的配置条目(类似 `BlacklistEntry`
- 添加更新间隔、启用状态等配置项
1.2 在 `Config` 结构体中添加 `DomainInfo DomainInfoConfig` 字段
1.3 实现配置解析函数:
- `parseDomainInfoLists()` - 解析域名信息列表配置
- 支持 INI 格式配置
### 步骤 2: 创建域名信息管理器
2.1 创建新文件 `shield/domain_info_manager.go`
2.2 实现 `DomainInfoManager` 结构体:
- 存储域名信息数据(JSON 格式)
- 存储威胁域名数据(CSV 格式)
- 存储跟踪器数据(JSON 格式)
- 缓存管理和过期检查机制
- 异步加载和更新逻辑
2.3 实现核心方法:
- `LoadDomainInfo()` - 加载所有域名信息
- `fetchRemoteDomainInfo()` - 获取远程域名信息
- `fetchThreatDatabase()` - 获取威胁数据库
- `fetchTrackerInfo()` - 获取跟踪器信息
- `shouldUpdateCache()` - 检查缓存是否需要更新
- `GetDomainInfo(domain)` - 查询域名信息
- `GetThreatInfo(domain)` - 查询威胁信息
- `GetTrackerInfo(domain)` - 查询跟踪器信息
- `GetAllDomainInfo()` - 获取所有域名信息(用于 API)
2.4 实现缓存机制:
- 使用内存缓存存储加载的数据
- 定期后台刷新(根据配置的更新间隔)
- 支持手动刷新
### 步骤 3: 实现后端 API
3.1 在 `http/server.go` 中添加新的 API 路由:
- `/api/shield/domain-info` - 获取域名信息列表(GET
- `/api/shield/domain-info/update` - 手动更新所有域名信息(POST)
- `/api/shield/domain-info/{type}/update` - 更新指定类型的域名信息(POST)
- `/api/domain/info` - 查询单个域名的详细信息(GET)
3.2 实现 API 处理函数:
- `handleDomainInfo()` - 处理域名信息列表的获取
- `handleUpdateDomainInfo()` - 处理手动更新请求
- `handleDomainInfoQuery()` - 处理域名信息查询
3.3 在服务器初始化时:
- 创建 `DomainInfoManager` 实例
- 启动后台定期更新任务
### 步骤 4: 实现前端界面
4.1 创建 `static/js/modules/domain-info.js` 模块:
- `loadDomainInfoLists()` - 加载域名信息列表
- `updateDomainInfo(type)` - 更新域名信息
- `renderDomainInfoLists()` - 渲染域名信息列表表格
- `showDomainInfoDetails()` - 显示域名信息详情
4.2 在 `static/js/shield.js` 中添加:
- `loadRemoteDomainInfoLists()` - 异步加载远程域名信息列表
- `updateDomainInfoListsTable()` - 更新域名信息列表 UI
- 添加状态指示器和通知逻辑
4.3 创建前端管理界面 `static/domain-info.html`(或集成到现有 shield.html):
- 域名信息列表管理表格
- 显示各列表的状态、规则数量、最后更新时间
- 手动更新按钮
- 启用/禁用切换
### 步骤 5: 集成到日志详情 API
5.1 修改日志详情查询接口:
- 在返回的日志详情中添加域名信息字段
- 添加跟踪器信息字段
- 如果域名在威胁数据库中,添加威胁告警信息
5.2 优化查询性能:
- 使用缓存避免重复查询
- 批量查询优化
### 步骤 6: 集成到威胁告警 API
6.1 修改威胁告警查询接口:
- 使用远程威胁数据库进行匹配
- 返回威胁类型、风险等级等详细信息
### 步骤 7: 测试和文档
7.1 功能测试:
- 测试远程列表加载
- 测试缓存机制
- 测试 API 接口
- 测试前端界面
7.2 更新文档:
- 更新 README.md
- 添加配置示例
- 更新 API 文档
## 技术要点
### 异步加载机制
- 使用 goroutine 进行后台异步加载
- 使用 channel 或 context 控制加载流程
- 支持并发加载多个远程列表
### 缓存策略
- 内存缓存 + 文件缓存双重机制
- 基于时间的过期策略
- 支持手动刷新和自动刷新
### 错误处理
- 网络错误的重试机制
- 加载失败时的降级处理(使用缓存)
- 详细的错误日志记录
### 性能优化
- 批量处理减少锁竞争
- 使用 RWMutex 优化读写性能
- 延迟加载和按需加载
## 文件清单
### 新增文件
- `shield/domain_info_manager.go` - 域名信息管理器
- `static/js/modules/domain-info.js` - 前端域名信息管理模块
- `static/domain-info.html` - 域名信息管理界面(可选,如集成到现有页面则不需要)
### 修改文件
- `config/config.go` - 添加域名信息配置
- `http/server.go` - 添加 API 路由和处理函数
- `shield/manager.go` - 集成域名信息管理(可选)
- `static/js/shield.js` - 添加前端加载逻辑
- `README.md` - 更新文档
## 配置示例
```ini
[domainInfo]
; 域名信息列表配置
; 格式:name = url,enabled
domainInfoList = https://gitea.amazehome.xyz/AMAZEHOME/domain-info/raw/branch/main/domains/domain-info.json,true
threatDatabase = https://gitea.amazehome.xyz/AMAZEHOME/domain-info/src/branch/main/threats/threats-database.csv,true
trackerList = https://gitea.amazehome.xyz/AMAZEHOME/domain-info/raw/branch/main/tracker/trackers.json,true
; 更新间隔(秒)
updateInterval = 3600
; 是否启用自动更新
enableAutoUpdate = true
```
## 时间估算
- 步骤 1: 配置文件扩展 - 30 分钟
- 步骤 2: 域名信息管理器 - 2 小时
- 步骤 3: 后端 API 实现 - 1 小时
- 步骤 4: 前端界面实现 - 1.5 小时
- 步骤 5-6: API 集成 - 1 小时
- 步骤 7: 测试和文档 - 1 小时
**总计**: 约 7-8 小时
+87
View File
@@ -0,0 +1,87 @@
# DNS服务器间歇性SERVFAIL错误修复计划
## 问题分析
通过分析代码和测试脚本,发现DNS服务器存在间歇性SERVFAIL错误,表现为:
- 第一次查询失败(返回SERVFAIL)
- 第二次查询成功
## 可能的原因
1. **上游服务器连接问题**:上游DNS服务器偶尔响应失败,导致第一次查询时所有服务器都失败
2. **缓存机制问题**:缓存未正确处理失败响应,导致第二次查询时能够从缓存中获取到正确的响应
3. **并行查询逻辑问题**:在parallel模式下,响应处理可能存在竞态条件
4. **超时处理问题**:超时设置不合理或处理不当
## 代码分析
### 关键问题点
1. **forwardDNSRequestWithCache函数**server.go:1191):
- 当所有上游服务器都失败时,可能返回nil响应,导致SERVFAIL
- 并行查询时的响应处理逻辑可能存在问题
2. **handleUpstreamRequest函数**server.go:825):
- 当response为nil时,直接返回SERVFAIL
3. **客户端池管理**
- 使用sync.Pool管理DNS客户端实例,但可能存在并发问题
## 修复方案
### 1. 改进上游请求处理逻辑
- **增加重试机制**:当第一次查询失败时,自动重试一次
- **优化超时设置**:确保超时时间合理,避免过早超时
- **改进错误处理**:当所有上游服务器都失败时,尝试使用备选服务器
### 2. 优化缓存机制
- **缓存失败响应**:暂时缓存失败响应,避免短时间内重复查询失败的域名
- **改进缓存键设计**:确保缓存键能够正确标识不同的查询条件
### 3. 改进并行查询逻辑
- **优化响应处理**:确保并行查询时能够正确处理所有响应
- **增加响应验证**:验证响应的有效性,避免返回无效响应
### 4. 增加监控和日志
- **增加详细日志**:记录每次查询的详细信息,包括上游服务器响应情况
- **增加统计信息**:统计上游服务器的响应成功率,便于问题定位
## 实施步骤
1. **修改forwardDNSRequestWithCache函数**
- 增加重试机制
- 优化错误处理逻辑
- 改进并行查询的响应处理
2. **修改handleUpstreamRequest函数**
- 增加对nil响应的处理
- 优化响应验证
3. **修改缓存机制**
- 增加失败响应的缓存
- 改进缓存键设计
4. **增加监控和日志**
- 增加详细的查询日志
- 增加上游服务器响应统计
5. **测试验证**
- 使用test_servfail.sh脚本测试修复效果
- 进行压力测试,确保修复后系统稳定
## 预期效果
- 消除间歇性SERVFAIL错误
- 提高DNS服务器的稳定性和可靠性
- 减少对上游服务器的重复查询
- 提供更详细的日志和监控信息
## 风险评估
- **性能影响**:增加重试机制可能会增加响应时间,但可以通过合理的超时设置来平衡
- **内存使用**:增加失败响应的缓存可能会增加内存使用,但可以通过设置合理的缓存大小和过期时间来控制
- **兼容性**:修改缓存机制可能会影响现有缓存的使用,但可以通过平滑过渡来避免问题