diff --git a/.trae/documents/DNSSEC判断和显示逻辑分析与改进.md b/.trae/documents/DNSSEC判断和显示逻辑分析与改进.md new file mode 100644 index 0000000..e9d8287 --- /dev/null +++ b/.trae/documents/DNSSEC判断和显示逻辑分析与改进.md @@ -0,0 +1,119 @@ +# DNSSEC判断和显示逻辑分析与改进 + +## 1. 当前实现分析 + +### DNSSEC判断逻辑 +1. **核心位置**:`dns/server.go` 中的 `forwardDNSRequestWithCache` 函数 +2. **判断流程**: + - 检查配置是否启用DNSSEC (`s.config.EnableDNSSEC`) + - 从响应中提取所有DNSKEY和RRSIG记录 + - 验证DNSSEC签名有效性 + - 设置响应的Authenticated Data (AD) 标志 + - 优先返回包含有效的DNSSEC记录的响应 + - 如果没有有效的DNSSEC记录,使用备选响应 + +### DNSSEC显示逻辑 +1. **前端显示**:`static/js/logs.js` 中的 `updateLogsTable` 函数 +2. **显示方式**: + ```javascript + ${log.DNSSEC ? ', DNSSEC' : ''} + ``` + - 当DNSSEC为true时,显示绿色锁图标和"DNSSEC"文字 + - 否则不显示 + +### DNSSEC状态存储 +1. **`domainDNSSECStatus` 映射**:存储域名的DNSSEC状态 +2. **`resolvedDomains` 结构**:包含每个域名的DNSSEC状态 +3. **查询日志**:每条日志记录包含DNSSEC状态 + +## 2. 存在的问题 + +1. **显示逻辑单一**:只在日志页面显示DNSSEC状态,没有在仪表盘或其他关键位置显示 +2. **状态判断简单**:只检查响应中是否包含RRSIG记录,没有考虑签名验证结果 +3. **缺少统计信息**:没有统计使用DNSSEC的查询比例 +4. **配置界面缺失**:没有在配置界面提供DNSSEC相关的配置选项 +5. **缺少用户反馈**:用户无法直观了解当前DNSSEC的整体使用情况 + +## 3. 改进方案 + +### 3.1 增强DNSSEC判断逻辑 +- **改进位置**:`dns/server.go` +- **改进内容**: + - 增加DNSSEC验证结果的详细记录 + - 区分"DNSSEC可用"和"DNSSEC验证成功"两种状态 + - 记录DNSSEC验证失败的具体原因 + +### 3.2 扩展DNSSEC显示范围 +- **改进位置**: + - `static/js/dashboard.js`:在仪表盘添加DNSSEC统计卡片 + - `static/js/logs.js`:增强日志中的DNSSEC显示 +- **改进内容**: + - 在仪表盘添加"DNSSEC使用率"统计卡片 + - 在TOP域名列表中显示DNSSEC状态 + - 在日志记录中显示DNSSEC验证结果(成功/失败/未使用) + +### 3.3 添加DNSSEC配置界面 +- **改进位置**:配置页面 +- **改进内容**: + - 添加DNSSEC启用/禁用开关 + - 添加DNSSEC验证严格程度选项 + - 显示当前DNSSEC状态信息 + +### 3.4 增强DNSSEC统计功能 +- **改进位置**:`dns/server.go` +- **改进内容**: + - 统计DNSSEC查询总数 + - 统计DNSSEC验证成功/失败次数 + - 计算DNSSEC使用率 + - 在API中提供DNSSEC统计数据 + +### 3.5 改进DNSSEC状态存储 +- **改进位置**:`dns/server.go` +- **改进内容**: + - 增加DNSSEC验证结果的存储 + - 记录DNSSEC状态的有效期 + - 优化域名DNSSEC状态的更新逻辑 + +## 4. 实现计划 + +1. **第一步**:增强DNSSEC判断逻辑,改进验证结果记录 +2. **第二步**:添加DNSSEC统计功能,扩展API返回数据 +3. **第三步**:在仪表盘添加DNSSEC统计卡片 +4. **第四步**:增强日志中的DNSSEC显示 +5. **第五步**:添加DNSSEC配置界面 +6. **第六步**:优化DNSSEC状态存储和更新逻辑 + +## 5. 预期效果 + +- 用户可以在仪表盘直观了解DNSSEC使用情况 +- 日志中显示详细的DNSSEC验证结果 +- 提供灵活的DNSSEC配置选项 +- 增强DNSSEC状态的准确性和可靠性 +- 提高DNSSEC相关问题的可调试性 + +## 6. 代码修改点 + +### 后端修改 +- `dns/server.go`:增强DNSSEC判断和统计 +- `config/config.go`:添加DNSSEC相关配置选项 +- API接口:扩展返回DNSSEC统计数据 + +### 前端修改 +- `static/js/dashboard.js`:添加DNSSEC统计卡片 +- `static/js/logs.js`:增强日志显示 +- 配置页面:添加DNSSEC配置选项 + +## 7. 风险评估 + +- DNSSEC验证可能会增加响应延迟 +- 错误的DNSSEC配置可能导致解析失败 +- 增强的统计功能可能增加内存使用 + +## 8. 测试计划 + +- 测试DNSSEC启用/禁用功能 +- 测试不同域名的DNSSEC显示 +- 测试DNSSEC统计数据的准确性 +- 测试DNSSEC验证失败时的处理逻辑 + +通过以上改进,可以使DNSSEC判断和显示逻辑更加完善,提供更好的用户体验和更详细的DNSSEC状态信息。 \ No newline at end of file diff --git a/.trae/documents/DNS查询页面实现计划.md b/.trae/documents/DNS查询页面实现计划.md new file mode 100644 index 0000000..aa8571b --- /dev/null +++ b/.trae/documents/DNS查询页面实现计划.md @@ -0,0 +1,62 @@ +# DNS查询页面实现计划 + +## 问题分析 + +目前DNS查询页面只是一个简单的占位符,显示"DNS查询页面内容待实现",没有实际的功能。我需要实现完整的DNS查询功能,包括: + +1. DNS域名查询功能 +2. 查询结果展示 +3. 查询历史记录 +4. 响应式设计 + +## 解决方案 + +### 1. 更新HTML结构 + +为`query-content`区域添加完整的UI组件,包括: +- 查询表单(域名输入框、查询按钮) +- 查询结果展示区域 +- 查询历史记录 +- 响应式布局设计 + +### 2. 实现JavaScript功能 + +创建或更新`query.js`文件,实现以下功能: +- 处理DNS查询请求 +- 展示查询结果 +- 管理查询历史 +- 与后端API对接 + +### 3. 实现具体功能 + +#### 3.1 DNS域名查询 +- 调用`/api/query`接口进行DNS查询 +- 支持输入域名进行查询 +- 显示查询结果,包括是否被屏蔽、屏蔽原因等 + +#### 3.2 查询结果展示 +- 以清晰的方式展示查询结果 +- 区分不同的查询结果状态(被屏蔽、正常、错误等) +- 显示详细的查询信息 + +#### 3.3 查询历史记录 +- 保存查询历史到本地存储 +- 支持查看历史查询记录 +- 支持从历史记录中重新查询 + +## 实现步骤 + +1. **更新HTML结构**:修改`index.html`中的`query-content`区域,添加完整的UI组件 +2. **实现JavaScript功能**:更新`query.js`文件,实现与后端API的交互 +3. **测试功能**:确保所有功能正常工作 +4. **优化用户体验**:添加加载状态、错误提示、成功反馈等 + +## 预期效果 + +通过以上实现,DNS查询页面将具备完整的查询功能,用户可以: +- 输入域名进行DNS查询 +- 查看详细的查询结果 +- 查看查询历史记录 +- 从历史记录中重新查询 + +页面将采用响应式设计,确保在不同屏幕尺寸下都能正常显示。 \ No newline at end of file diff --git a/.trae/documents/Fix DNSSEC Stats Fields Missing Error.md b/.trae/documents/Fix DNSSEC Stats Fields Missing Error.md new file mode 100644 index 0000000..d3950b0 --- /dev/null +++ b/.trae/documents/Fix DNSSEC Stats Fields Missing Error.md @@ -0,0 +1,25 @@ +## Problem +The build is failing because the HTTP server is trying to access DNSSEC-related fields (`DNSSECQueries`, `DNSSECSuccess`, `DNSSECFailed`, `DNSSECEnabled`) from the `dns.Stats` struct, but these fields don't exist in the struct definition. + +## Solution +Add the missing DNSSEC-related fields to the `Stats` struct in `dns/server.go` and ensure they're properly initialized and updated. + +## Implementation Steps +1. **Add DNSSEC fields to Stats struct** in `/root/dns/dns/server.go`: + - Add `DNSSECQueries int64` field + - Add `DNSSECSuccess int64` field + - Add `DNSSECFailed int64` field + - Add `DNSSECEnabled bool` field + +2. **Initialize DNSSEC fields in NewServer** function: + - Set `DNSSECEnabled` based on config.EnableDNSSEC + - Initialize other DNSSEC fields to 0 + +3. **Update DNSSEC stats in query handling**: + - Increment `DNSSECQueries` when a DNSSEC query is processed + - Update `DNSSECSuccess` and `DNSSECFailed` based on DNSSEC validation results + +4. **Test the fix** by running `go build` to ensure all errors are resolved + +## Expected Outcome +The build will succeed and the HTTP server will be able to access the DNSSEC-related stats fields. \ No newline at end of file diff --git a/.trae/documents/Web页面屏蔽管理功能实现计划.md b/.trae/documents/Web页面屏蔽管理功能实现计划.md new file mode 100644 index 0000000..400fc65 --- /dev/null +++ b/.trae/documents/Web页面屏蔽管理功能实现计划.md @@ -0,0 +1,65 @@ +# Web页面屏蔽管理功能实现计划 + +## 问题分析 + +目前web页面的屏蔽管理功能尚未完全实现,主要问题包括: + +1. **前端UI缺失**:`shield-content`区域只有简单的提示文本,没有实际的管理界面 +2. **功能被禁用**:`shield.js`文件中的所有功能都已被禁用,无法与后端API交互 +3. **缺少完整的管理功能**:无法管理本地规则、远程黑名单和hosts条目 + +## 解决方案 + +### 1. 更新屏蔽管理页面HTML结构 + +- 为`shield-content`区域添加完整的UI组件 +- 实现以下功能模块: + - 屏蔽规则统计信息展示 + - 本地规则管理(添加、删除) + - 远程黑名单管理(添加、删除、更新) + - hosts条目管理(添加、删除) + +### 2. 实现屏蔽管理JavaScript功能 + +- 重新启用并实现`shield.js`中的功能 +- 与后端API对接,实现完整的CRUD操作 +- 添加错误处理和用户反馈 + +### 3. 实现具体功能 + +#### 3.1 屏蔽规则统计信息 +- 调用`/api/shield`接口获取统计数据 +- 展示规则数量、黑名单数量等信息 + +#### 3.2 本地规则管理 +- 实现规则列表展示 +- 实现添加新规则功能 +- 实现删除规则功能 + +#### 3.3 远程黑名单管理 +- 调用`/api/shield/blacklists`接口获取黑名单列表 +- 实现添加新黑名单功能 +- 实现删除黑名单功能 +- 实现更新单个黑名单功能 + +#### 3.4 hosts条目管理 +- 调用`/api/shield/hosts`接口获取hosts列表 +- 实现添加新hosts条目功能 +- 实现删除hosts条目功能 + +## 实现步骤 + +1. **更新HTML结构**:修改`index.html`中的`shield-content`区域,添加完整的UI组件 +2. **实现JavaScript功能**:更新`shield.js`文件,实现与后端API的交互 +3. **测试功能**:确保所有功能正常工作,包括添加、删除、更新操作 +4. **优化用户体验**:添加加载状态、错误提示、成功反馈等 + +## 预期效果 + +通过以上实现,web页面将具备完整的屏蔽管理功能,用户可以: +- 查看屏蔽规则统计信息 +- 管理本地规则 +- 管理远程黑名单 +- 管理hosts条目 + +所有功能都将与后端API对接,实现数据的实时更新和持久化存储。 \ No newline at end of file diff --git a/.trae/documents/Web页面适配问题解决方案.md b/.trae/documents/Web页面适配问题解决方案.md new file mode 100644 index 0000000..3adc261 --- /dev/null +++ b/.trae/documents/Web页面适配问题解决方案.md @@ -0,0 +1,60 @@ +# Web页面适配问题解决方案 + +## 问题分析 + +通过分析当前web页面的HTML、CSS和JavaScript代码,我发现了以下适配问题: + +1. **统计卡片布局问题**:在小屏幕设备上,统计卡片可能会出现布局问题 +2. **图表布局问题**:三个图表在同一行显示,在小屏幕设备上可能会挤在一起 +3. **表格溢出问题**:在小屏幕设备上,表格可能会溢出容器 +4. **服务器状态组件适配问题**:在小屏幕上可能显示不全 +5. **侧边栏响应式处理不完整**:当前只在窗口大小改变时更新,没有考虑其他情况 +6. **图表大小更新不完整**:窗口大小改变时只更新了部分图表 +7. **配置表单适配问题**:在小屏幕上的布局需要优化 + +## 解决方案 + +### 1. 优化统计卡片布局 +- 修改统计卡片网格布局,增加更细粒度的响应式控制 +- 在小屏幕上使用单列布局,在中等屏幕上使用双列布局,在大屏幕上使用四列布局 + +### 2. 改进图表布局 +- 修改图表网格布局,确保在不同屏幕尺寸下都能正常显示 +- 在小屏幕上使用单列布局,在中等屏幕上使用双列布局,在大屏幕上使用三列布局 + +### 3. 解决表格溢出问题 +- 为所有表格添加响应式处理,确保在小屏幕设备上可以水平滚动 +- 优化表格样式,提高在小屏幕上的可读性 + +### 4. 优化服务器状态组件 +- 在小屏幕上简化服务器状态组件,只显示核心指标 +- 添加响应式逻辑,根据屏幕尺寸动态调整显示内容 + +### 5. 完善侧边栏响应式处理 +- 确保侧边栏在所有情况下都能正确响应屏幕尺寸变化 +- 添加触摸事件支持,提高移动端体验 + +### 6. 完整更新图表大小 +- 在窗口大小改变时,更新所有图表的大小 +- 确保图表容器大小正确,避免图表变形 + +### 7. 优化配置表单布局 +- 修改配置表单网格布局,确保在小屏幕上也能正常显示 +- 调整表单元素大小和间距,提高在小屏幕上的可用性 + +## 实现步骤 + +1. 修改HTML文件中的网格布局类,增加更细粒度的响应式控制 +2. 更新CSS样式,优化各组件在不同屏幕尺寸下的显示效果 +3. 修改JavaScript代码,完善响应式处理逻辑 +4. 测试各组件在不同屏幕尺寸下的显示效果 +5. 优化用户体验,确保在各种设备上都能正常使用 + +## 预期效果 + +通过以上优化,web页面将能够在各种设备上正常显示,包括: +- 桌面设备(大屏幕) +- 平板设备(中等屏幕) +- 移动设备(小屏幕) + +页面布局将更加灵活,能够根据屏幕尺寸自动调整,提高用户体验。 \ No newline at end of file diff --git a/.trae/documents/plan_20251215_073614.md b/.trae/documents/plan_20251215_073614.md new file mode 100644 index 0000000..db0f4df --- /dev/null +++ b/.trae/documents/plan_20251215_073614.md @@ -0,0 +1,37 @@ +## 问题分析 + +规则处理存在问题:`||domain` 规则应该是屏蔽一个绝对域名及其子域名,而不是正则匹配屏蔽,也不应该屏蔽所有包含该顶级域名的网站。 + +## 问题根源 + +在 `manager.go` 文件中,`addDomainRule` 函数(第444行)存在逻辑错误: + +1. 当添加 `||example.com` 规则时,函数正确地将 `example.com` 添加到域名规则列表中 +2. 但随后,函数错误地将域名拆分为各个部分,并为每个部分添加规则 +3. 例如,对于 `||example.com`,它会添加 `example.com`、`com` 到规则列表中 +4. 这导致所有 `.com` 域名都被屏蔽,而不仅仅是 `example.com` 及其子域名 + +## 修复方案 + +修改 `addDomainRule` 函数,移除错误的子域名处理逻辑。因为 `CheckDomainBlockDetails` 函数已经实现了正确的子域名检查逻辑:它会检查域名的所有子域名部分,从最长到最短,所以不需要在添加规则时就将所有子域名都添加到规则列表中。 + +## 修复步骤 + +1. 修改 `manager.go` 文件中的 `addDomainRule` 函数 +2. 移除第457-474行和第487-503行的子域名处理逻辑 +3. 确保只添加精确的域名到规则列表中 +4. 保持 `CheckDomainBlockDetails` 函数的子域名检查逻辑不变 + +## 预期效果 + +修复后,`||example.com` 规则将只屏蔽: +- `example.com` +- `www.example.com` +- `subdomain.example.com` + +而不会屏蔽: +- `anotherexample.com` +- `google.com` +- 其他所有 `.com` 域名 + +这符合 AdGuard Home 规则的标准行为,即 `||example.com^` 匹配该域名及其所有子域名。 \ No newline at end of file diff --git a/.trae/documents/plan_20251215_080125.md b/.trae/documents/plan_20251215_080125.md new file mode 100644 index 0000000..dfbf88b --- /dev/null +++ b/.trae/documents/plan_20251215_080125.md @@ -0,0 +1,33 @@ +## 问题分析 + +web界面保存配置后,配置没有同步到config文件。从代码分析来看,当前的`handleConfig`函数(第1064行)只处理了`shield`部分的配置更新,而没有处理其他配置项,如DNS服务器配置、HTTP服务器配置等。 + +## 解决方案 + +1. **扩展`handleConfig`函数**:修改该函数以处理所有配置项,包括DNS、HTTP和Log配置 +2. **更新配置保存逻辑**:确保所有配置都能正确保存到config.json文件中 +3. **添加重启服务逻辑**:在配置保存成功后,调用重启服务的逻辑,确保配置更改能立即生效 + +## 修复步骤 + +1. 修改`server.go`文件中的`handleConfig`函数 + - 扩展请求结构,包含所有配置项 + - 更新配置处理逻辑,处理DNS、HTTP和Log配置 + - 确保所有配置都能正确保存到config.json文件中 + +2. 修改`handleConfig`函数的返回逻辑 + - 在配置保存成功后,调用重启服务的逻辑 + - 返回更详细的成功信息 + +3. 测试修复效果 + - 确保web界面上的所有配置项都能正确保存到config.json文件中 + - 确保服务能在配置保存后正确重启 + +## 预期效果 + +修复后,当用户在web界面点击"保存配置"按钮时: +1. 所有配置项(包括DNS、HTTP、Shield和Log配置)都会被保存到config.json文件中 +2. 服务器会自动重启,使配置更改生效 +3. 用户会看到配置保存成功的提示 + +这将确保用户在web界面上的所有配置更改都能正确保存和生效。 \ No newline at end of file diff --git a/.trae/documents/plan_20251215_151748.md b/.trae/documents/plan_20251215_151748.md new file mode 100644 index 0000000..e95d053 --- /dev/null +++ b/.trae/documents/plan_20251215_151748.md @@ -0,0 +1,38 @@ +## 修复API不可用问题 + +### 问题分析 +1. **配置文件中API被禁用**:在`config.json`中,`http.enableAPI`设置为`false` +2. **缺少默认启用配置**:在`config/config.go`的`LoadConfig`函数中,没有为`EnableAPI`设置默认值 +3. **API路由条件注册**:在`http/server.go`中,所有API端点都在`if s.config.EnableAPI`条件下注册 + +### 解决方案 + +#### 1. 修改配置文件启用API +将`config.json`中的`http.enableAPI`值从`false`改为`true`,立即启用API功能。 + +#### 2. 设置API默认启用 +在`config/config.go`的`LoadConfig`函数中添加默认值设置,确保API在配置文件未指定时自动启用: +```go +if !config.HTTP.EnableAPI { + config.HTTP.EnableAPI = true +} +``` + +### 实施步骤 +1. 编辑`config.json`文件,将`http.enableAPI`设置为`true` +2. 修改`config/config.go`,在`LoadConfig`函数中添加API默认启用逻辑 +3. 重启服务使配置生效 + +### 预期结果 +- API端点将可用,包括: + - `/api/stats` - 统计信息 + - `/api/shield` - 屏蔽规则管理 + - `/api/shield/localrules` - 本地规则 + - `/api/shield/remoterules` - 远程规则 + - `/api/query` - DNS查询 + - 以及其他统计和管理端点 +- Swagger UI页面可通过`/api`访问 + +### 文件修改清单 +1. `config.json` - 启用API +2. `config/config.go` - 添加API默认启用配置 \ No newline at end of file diff --git a/.trae/documents/plan_20251216_023941.md b/.trae/documents/plan_20251216_023941.md new file mode 100644 index 0000000..e5d828e --- /dev/null +++ b/.trae/documents/plan_20251216_023941.md @@ -0,0 +1,21 @@ +# DNSSEC记录缺失问题修复计划 + +## 问题分析 +从日志中可以看到,所有DNS查询都显示"DNS响应不包含DNSSEC记录",这表明虽然DNSSEC被启用,但服务器没有从上游DNS服务器获取到DNSSEC记录。 + +## 根本原因 +当启用DNSSEC时,DNS服务器需要在发送给上游服务器的请求中设置**DNSSEC OK (DO)标志**,这样上游服务器才会返回DNSSEC记录(如RRSIG、DNSKEY等)。但当前代码直接使用原始请求发送给上游服务器,没有添加DO标志。 + +## 修复方案 +修改`forwardDNSRequestWithCache`函数,在向上游服务器发送请求之前: +1. 检查是否启用了DNSSEC +2. 如果启用,为请求添加EDNS0选项并设置DO标志 +3. 确保修改后的请求被正确发送 + +## 具体实现步骤 +1. 在`/root/dns/dns/server.go`文件中,找到`forwardDNSRequestWithCache`函数 +2. 在发送请求前(第625行附近),添加DO标志设置逻辑 +3. 确保请求被正确发送到上游服务器 + +## 预期效果 +修复后,启用DNSSEC时,服务器会向上游发送带有DO标志的请求,上游服务器将返回DNSSEC记录,从而实现完整的DNSSEC验证和记录返回。 \ No newline at end of file diff --git a/.trae/documents/plan_20251216_032256.md b/.trae/documents/plan_20251216_032256.md new file mode 100644 index 0000000..9399b4f --- /dev/null +++ b/.trae/documents/plan_20251216_032256.md @@ -0,0 +1,82 @@ +# DNSSEC功能修复计划 + +## 问题分析 +当前DNSSEC实现存在以下问题: +1. 当启用DNSSEC时,系统只是验证上游服务器返回的DNSSEC签名,但不会主动请求DNSSEC记录 +2. 当上游服务器返回的响应没有DNSSEC记录时,系统只是将其作为备选响应 +3. 没有专门从8.8.8.8/1.1.1.1获取DNSSEC记录进行验证 +4. 缓存时没有优先考虑DNSSEC记录 + +## 修复方案 + +### 1. 改进DNS请求转发逻辑 +**修改文件:** `dns/server.go` +**修改函数:** `forwardDNSRequestWithCache` + +- 当启用DNSSEC且响应中没有DNSSEC记录时,主动向8.8.8.8/1.1.1.1发送DNS请求 +- 比较不同服务器返回的结果,优先使用带有DNSSEC记录的响应 +- 如果DNSSEC结果不匹配,优先使用8.8.8.8/1.1.1.1提供的解析记录 + +### 2. 增强DNSSEC验证机制 +**修改文件:** `dns/server.go` + +- 完善DNSSEC记录提取和验证逻辑 +- 确保正确处理DNSKEY和RRSIG记录 +- 改进AD标志(Authenticated Data)的设置 + +### 3. 优化缓存机制 +**修改文件:** `dns/cache.go` 和 `dns/server.go` + +- 缓存时标记DNSSEC状态 +- 优先返回带有DNSSEC记录的缓存项 +- 改进缓存键生成,考虑DNSSEC属性 + +### 4. 增加DNSSEC特定服务器配置 +**修改文件:** `config.json` + +- 添加专门用于DNSSEC查询的服务器配置 +- 默认为8.8.8.8和1.1.1.1 + +## 具体实现步骤 + +1. **修改`forwardDNSRequestWithCache`函数**: + - 当启用DNSSEC且主响应没有DNSSEC记录时,向DNSSEC专用服务器发送请求 + - 比较所有响应,选择最优结果(优先DNSSEC记录,其次是可靠服务器) + - 实现DNSSEC结果验证和比较逻辑 + +2. **改进缓存获取逻辑**: + - 在`handleDNSRequest`函数中,优先检查是否有带有DNSSEC记录的缓存项 + - 如果有,直接返回;否则再检查普通缓存项 + +3. **优化DNSSEC记录验证**: + - 增强`verifyDNSSEC`函数的实现 + - 确保正确验证所有RRSIG记录 + - 改进错误处理和日志记录 + +4. **添加DNSSEC服务器配置**: + - 在配置文件中添加`dnssecUpstreamDNS`配置项 + - 默认值为["8.8.8.8:53", "1.1.1.1:53"] + +## 测试计划 + +1. 启动DNS服务器,启用DNSSEC +2. 使用`dig`命令测试DNSSEC记录获取 +3. 验证带有DNSSEC记录的响应被正确返回 +4. 验证缓存机制优先返回DNSSEC记录 +5. 测试DNSSEC验证失败时的处理逻辑 + +## 预期效果 + +1. 启用DNSSEC后,系统会主动请求并验证DNSSEC记录 +2. 优先返回带有DNSSEC记录的解析结果 +3. 当DNSSEC结果不匹配时,优先使用8.8.8.8/1.1.1.1提供的记录 +4. 缓存机制正确处理DNSSEC标记,优先返回DNSSEC记录 +5. 完善的日志记录,便于调试和监控 + +## 代码修改范围 + +- `dns/server.go`:核心DNSSEC逻辑修改 +- `dns/cache.go`:缓存机制优化 +- `config.json`:配置项添加 + +通过以上修改,将解决当前DNSSEC功能的问题,确保启用DNSSEC后能够正确获取、验证和返回DNSSEC记录。 \ No newline at end of file diff --git a/.trae/documents/plan_20251216_041731.md b/.trae/documents/plan_20251216_041731.md new file mode 100644 index 0000000..d739a8e --- /dev/null +++ b/.trae/documents/plan_20251216_041731.md @@ -0,0 +1,43 @@ +# DNSSEC状态显示问题修复计划 + +## 问题分析 +用户报告已在配置中启用DNSSEC(`enableDNSSEC: true`),但界面显示DNSSEC为禁用状态,且使用率为0%。经过代码检查,发现问题出在`GetStats`函数中,该函数返回的`Stats`结构体缺少DNSSEC相关字段,导致前端无法获取正确的DNSSEC状态和统计信息。 + +## 修复方案 + +### 1. 修复`GetStats`函数 +**修改文件:** `dns/server.go` +**修改函数:** `GetStats` + +**问题:** 当前`GetStats`函数返回的`Stats`结构体缺少DNSSEC相关字段,包括: +- `DNSSECEnabled` +- `DNSSECQueries` +- `DNSSECSuccess` +- `DNSSECFailed` + +**解决方案:** 在`GetStats`函数返回的`Stats`结构体中添加所有DNSSEC相关字段,确保前端能获取到正确的DNSSEC状态和统计数据。 + +## 具体实现步骤 + +1. **修改`GetStats`函数**: + - 在返回的`Stats`结构体中添加`DNSSECEnabled`字段 + - 添加`DNSSECQueries`字段 + - 添加`DNSSECSuccess`字段 + - 添加`DNSSECFailed`字段 + +2. **测试修复效果**: + - 重新编译DNS服务器 + - 启动服务器 + - 使用API查询统计信息,确认DNSSEC状态和统计数据正确返回 + - 检查前端界面是否显示正确的DNSSEC状态 + +## 预期效果 + +1. 前端界面显示DNSSEC状态为"已启用" +2. DNSSEC使用率根据实际查询情况更新 +3. 成功、失败和总查询数统计正确显示 +4. 系统正常记录DNSSEC相关统计数据 + +## 代码修改范围 + +- `dns/server.go`:修复`GetStats`函数,添加缺失的DNSSEC字段 \ No newline at end of file diff --git a/.trae/documents/为DNS服务器添加DNSSEC支持 (1).md b/.trae/documents/为DNS服务器添加DNSSEC支持 (1).md new file mode 100644 index 0000000..59101a4 --- /dev/null +++ b/.trae/documents/为DNS服务器添加DNSSEC支持 (1).md @@ -0,0 +1,39 @@ +## 实现DNSSEC支持的计划 + +### 1. 分析当前代码 +- 配置文件中已经包含了DNSSEC相关配置项:`EnableDNSSEC` 和 `DNSSECValidation` +- DNS客户端(resolver)目前没有启用DNSSEC支持 +- 需要修改代码以实现DNSSEC功能 + +### 2. 实现步骤 + +#### 步骤1:修改DNS客户端配置 +- 在 `NewServer` 函数中,修改DNS客户端配置,添加 `DNSSEC: true` 以启用DNSSEC查询 +- 确保客户端支持DNSSEC记录类型(RRSIG, DNSKEY, DS等) + +#### 步骤2:添加DNSSEC验证逻辑 +- 在 `forwardDNSRequestWithCache` 函数中,检查上游服务器返回的响应是否包含DNSSEC签名 +- 如果启用了DNSSEC验证,验证签名的有效性 +- 处理验证失败的情况,返回适当的错误响应 + +#### 步骤3:确保DNS响应包含DNSSEC记录 +- 当转发DNS响应时,确保包含所有相关的DNSSEC记录 +- 确保响应中的DNSSEC标志正确设置 + +#### 步骤4:添加DNSSEC相关的日志记录 +- 记录DNSSEC验证结果 +- 记录DNSSEC相关的错误信息 + +### 3. 预期结果 +- DNS服务器将支持DNSSEC查询 +- 可以验证DNS记录的真实性和完整性 +- 防止DNS投毒和劫持攻击 +- 提供DNSSEC相关的配置选项 + +### 4. 文件修改 +- `/root/dns/dns/server.go`:修改DNS客户端配置和添加DNSSEC验证逻辑 + +### 5. 技术细节 +- 使用miekg/dns库的内置DNSSEC支持 +- 确保DNSSEC验证符合RFC标准 +- 处理各种DNSSEC相关的错误情况 \ No newline at end of file diff --git a/.trae/documents/为DNS服务器添加DNSSEC支持.md b/.trae/documents/为DNS服务器添加DNSSEC支持.md new file mode 100644 index 0000000..073b0da --- /dev/null +++ b/.trae/documents/为DNS服务器添加DNSSEC支持.md @@ -0,0 +1,84 @@ +## 实现计划 + +### 1. 配置更新 +- 在 `DNSConfig` 结构体中添加 `EnableDNSSEC` 布尔字段,用于控制是否启用DNSSEC验证 +- 在配置加载时设置默认值为 `true` +- 在默认配置JSON中添加DNSSEC相关配置项 + +### 2. DNSSEC核心功能实现 +- **DO标志处理**:识别并保留客户端请求中的DNSSEC OK (DO) 标志 +- **记录转发**:确保所有DNSSEC相关记录(RRSIG, DNSKEY, DS, NSEC, NSEC3等)被正确转发 +- **签名验证**:验证上游DNS服务器返回的DNSSEC签名有效性 +- **AD标志设置**:根据验证结果设置Authenticated Data (AD) 标志 + +### 3. 代码修改点 + +#### 3.1 配置文件 (`config/config.go`) +```go +// DNSConfig DNS配置 +type DNSConfig struct { + Port int `json:"port"` + UpstreamDNS []string `json:"upstreamDNS"` + Timeout int `json:"timeout"` + StatsFile string `json:"statsFile"` // 统计数据持久化文件 + SaveInterval int `json:"saveInterval"` // 数据保存间隔(秒) + EnableDNSSEC bool `json:"enableDNSSEC"` // 是否启用DNSSEC验证 +} +``` + +#### 3.2 默认配置 (`main.go`) +在默认配置JSON中添加: +```json +"enableDNSSEC": true +``` + +#### 3.3 DNS服务器初始化 (`dns/server.go`) +- 在 `NewServer` 方法中,为DNS客户端启用DNSSEC支持 +- 初始化DNSSEC验证相关组件 + +#### 3.4 DNS请求处理 (`dns/server.go`) +- 修改 `handleDNSRequest` 方法,保留客户端请求中的DO标志 +- 修改 `forwardDNSRequest` 方法: + - 确保转发请求时包含DO标志 + - 处理上游返回的DNSSEC记录 + - 验证DNSSEC签名 + - 根据验证结果设置AD标志 +- 确保所有DNSSEC记录类型被正确处理 + +### 4. DNSSEC验证逻辑 +- 使用miekg/dns库的内置验证功能 +- 验证RRSIG记录与对应资源记录的匹配性 +- 验证签名的有效性和过期时间 +- 处理信任链验证 + +### 5. 测试验证 +- 测试DNSSEC查询是否能正确返回RRSIG等记录 +- 测试DNSSEC验证功能是否正常工作 +- 测试在不同配置下的行为 +- 测试各种DNSSEC记录类型的处理 + +## 预期效果 + +- 当 `enableDNSSEC` 为 `true` 时: + - 服务器将验证上游DNS返回的DNSSEC记录 + - 对于有效的DNSSEC记录,服务器将在响应中设置AD标志 + - 对于无效的DNSSEC记录,服务器将记录错误并根据配置处理 +- 服务器将正确转发所有DNSSEC相关记录 +- 支持DNSKEY, DS, RRSIG, NSEC, NSEC3等DNSSEC记录类型 +- 客户端可以通过设置DO标志来请求DNSSEC记录 + +## 安全增强 + +通过添加DNSSEC支持,服务器将能够: +- 防止DNS投毒攻击 +- 确保DNS记录的完整性和真实性 +- 为客户端提供经过验证的DNS响应 +- 增强整体DNS服务的安全性 + +## 实现步骤 + +1. **修改配置结构** +2. **更新默认配置** +3. **修改DNS客户端设置** +4. **实现DNSSEC验证逻辑** +5. **测试和验证** \ No newline at end of file diff --git a/.trae/documents/优化DNSSEC查询逻辑,优先返回DNSSEC结果.md b/.trae/documents/优化DNSSEC查询逻辑,优先返回DNSSEC结果.md new file mode 100644 index 0000000..349995c --- /dev/null +++ b/.trae/documents/优化DNSSEC查询逻辑,优先返回DNSSEC结果.md @@ -0,0 +1,60 @@ +## 实现计划:优化DNSSEC查询逻辑 + +### 1. 需求分析 +- 当配置文件中`enableDNSSEC=true`时,DNS服务器应优先返回包含DNSSEC记录的结果 +- 如果没有DNSSEC结果,应返回普通查询结果作为备选 +- 保持现有代码结构和兼容性 + +### 2. 实现步骤 + +#### 步骤1:修改`forwardDNSRequestWithCache`函数 +- 在函数中添加备选响应变量,用于存储非DNSSEC的成功响应 +- 当`enableDNSSEC=true`时,遍历所有上游DNS服务器: + - 对于每个上游,检查响应是否成功 + - 如果响应成功且包含DNSSEC记录,立即返回该响应 + - 如果响应成功但不包含DNSSEC记录,将其保存为备选响应 +- 遍历完成后,如果有备选响应,返回该响应 +- 如果没有成功响应,返回服务器失败错误 + +#### 步骤2:添加DNSSEC记录检测逻辑 +- 在响应处理中添加DNSSEC记录检测 +- 检查响应的Answer、Ns和Extra部分是否包含RRSIG记录 +- 使用该检测结果决定是否优先返回该响应 + +#### 步骤3:优化日志记录 +- 为DNSSEC优先返回的情况添加专门的日志记录 +- 为备选响应返回的情况添加日志记录 + +### 3. 技术细节 + +#### 3.1 DNSSEC检测方法 +- 检查响应中是否包含RRSIG(资源记录签名)记录 +- 检查范围包括Answer、Ns和Extra部分 +- 使用类型断言判断记录类型 + +#### 3.2 优先级逻辑 +- 最高优先级:成功且包含DNSSEC的响应 +- 次高优先级:成功但不包含DNSSEC的响应 +- 最低优先级:失败响应 + +### 4. 预期效果 +- 当启用DNSSEC时,优先返回安全的DNSSEC结果 +- 提高DNS查询的安全性 +- 兼容现有配置和代码 +- 提供详细的日志记录 + +### 5. 文件修改 +- **文件**:`/root/dns/dns/server.go` +- **函数**:`forwardDNSRequestWithCache` +- **修改内容**:添加DNSSEC优先逻辑和备选响应处理 + +### 6. 测试要点 +- 验证启用DNSSEC时优先返回DNSSEC结果 +- 验证无DNSSEC结果时返回普通结果 +- 验证禁用DNSSEC时使用原有逻辑 +- 验证日志记录正确 + +### 7. 实现时间线 +- 代码修改:20分钟 +- 测试:10分钟 +- 总计:30分钟 \ No newline at end of file diff --git a/.trae/documents/优化设置界面实现计划.md b/.trae/documents/优化设置界面实现计划.md new file mode 100644 index 0000000..409aa8c --- /dev/null +++ b/.trae/documents/优化设置界面实现计划.md @@ -0,0 +1,84 @@ +# 优化设置界面实现计划 + +## 问题分析 +当前设置界面存在配置项重复问题,需要进行优化,具体包括: +1. "远程规则URL"配置项在多个界面重复出现 +2. "启用API"和"主机"选项不需要在当前界面显示 +3. 需要确保保存功能正常工作,写入config.json并触发服务器重新加载配置 + +## 优化方案 + +### 1. 修改HTML结构 +- 移除"远程规则URL"配置项 +- 移除"启用API"选项 +- 移除"主机"选项 +- 调整布局,确保界面美观合理 + +### 2. 更新JavaScript代码 +- 修改`populateConfigForm`函数,移除对已删除配置项的处理 +- 修改`collectFormData`函数,移除对已删除配置项的收集 +- 确保保存功能能正确写入config.json文件 +- 实现服务器重新加载配置的触发机制 +- 提供明确的成功/失败反馈 + +### 3. 测试和验证 +- 测试所有保留配置项的加载和保存功能 +- 验证保存操作能正确写入config.json文件 +- 验证服务器能重新加载配置 +- 测试成功/失败反馈是否明确 + +## 具体实现步骤 + +1. **修改HTML结构** + - 编辑`index.html`文件,移除不需要的配置项 + - 调整布局,确保界面美观合理 + +2. **更新JavaScript代码** + - 编辑`config.js`文件,修改`populateConfigForm`函数 + - 修改`collectFormData`函数,移除对已删除配置项的处理 + - 确保`handleSaveConfig`函数能正确保存配置 + - 实现服务器重新加载配置的触发机制 + +3. **测试和验证** + - 测试配置项的加载功能 + - 测试配置项的保存功能 + - 验证config.json文件是否正确更新 + - 验证服务器是否重新加载配置 + - 测试成功/失败反馈是否明确 + +## 预期效果 +- 设置界面布局合理,无重复配置项 +- 所有保留配置项均可正常配置 +- 保存功能能正确写入config.json文件 +- 服务器能重新加载配置,使更改立即生效 +- 保存操作有明确的成功/失败反馈 + +## 技术要点 +- 使用HTML和JavaScript修改界面结构和功能 +- 确保与服务器API的正确交互 +- 实现良好的用户反馈机制 +- 确保配置的正确保存和加载 + +## 实现时间 +- 预计1-2小时完成所有修改和测试 + +## 风险评估 +- 低风险:修改范围明确,不涉及核心功能 +- 可回滚:所有修改均为前端修改,可通过恢复文件轻松回滚 + +## 依赖关系 +- 依赖服务器API的正常工作 +- 依赖config.json文件的读写权限 + +## 测试策略 +- 手动测试所有配置项的加载和保存功能 +- 验证config.json文件的更新 +- 测试服务器配置的重新加载 +- 测试成功/失败反馈 + +## 验收标准 +- 设置界面布局合理,无重复配置项 +- 所有保留配置项均可正常配置 +- 保存功能能正确写入config.json文件 +- 服务器能重新加载配置,使更改立即生效 +- 保存操作有明确的成功/失败反馈 \ No newline at end of file diff --git a/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题 (1).md b/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题 (1).md new file mode 100644 index 0000000..26bf7f2 --- /dev/null +++ b/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题 (1).md @@ -0,0 +1,22 @@ +## 问题分析 +CPU使用率卡片在WebSocket实时更新时没有刷新数据,原因是: +1. `processRealTimeData`函数调用了`updateStatsCards(stats)`,但该函数的CPU使用率更新逻辑可能没有被正确执行 +2. `processRealTimeData`函数对其他卡片(如平均响应时间、最常用查询类型、活跃IP数)有单独的更新逻辑,但缺少了CPU使用率卡片的更新逻辑 +3. `loadDashboardData`函数中有完整的CPU使用率更新逻辑,这就是为什么页面初始加载时CPU使用率能显示,但后续WebSocket更新时不能显示的原因 + +## 修复方案 +1. **在`processRealTimeData`函数中添加CPU使用率卡片的更新逻辑**:类似于`loadDashboardData`函数中的实现 +2. **确保从`stats`对象中正确获取CPU使用率数据**:支持从不同的数据结构中获取CPU使用率 +3. **更新DOM元素**:将获取到的CPU使用率数据更新到`cpu-usage`和`cpu-status`元素中 +4. **添加状态判断**:根据CPU使用率值设置不同的状态文本和样式 + +## 实现步骤 +1. 打开`dashboard.js`文件 +2. 找到`processRealTimeData`函数(约第120行) +3. 在函数末尾添加CPU使用率更新逻辑,位于其他卡片更新逻辑之后 +4. 确保从`stats`对象中正确获取CPU使用率数据 +5. 更新`cpu-usage`和`cpu-status`元素的内容和样式 +6. 测试修复是否生效 + +## 预期效果 +修复后,当WebSocket接收到实时数据更新时,CPU使用率卡片会自动更新显示最新的CPU使用率和状态,与其他统计卡片保持一致的实时更新效果。 \ No newline at end of file diff --git a/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题 (2).md b/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题 (2).md new file mode 100644 index 0000000..b1fc19a --- /dev/null +++ b/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题 (2).md @@ -0,0 +1,22 @@ +## 问题分析 +CPU使用率卡片在WebSocket实时更新时没有刷新数据,原因是: +1. `updateStatsCards`函数中,数组形式的数据结构处理部分(第631-641行)缺少CPU使用率的处理逻辑 +2. 可能存在数据字段名不匹配的问题,WebSocket服务器返回的CPU使用率数据可能使用了不同的字段名 +3. `processRealTimeData`函数和`updateStatsCards`函数中都有CPU使用率更新逻辑,可能导致冲突或其中一个逻辑没有被正确执行 + +## 修复方案 +1. **完善`updateStatsCards`函数的CPU使用率处理逻辑**:在数组形式的数据结构处理部分添加CPU使用率的处理逻辑 +2. **添加更多可能的CPU使用率字段名支持**:确保从WebSocket服务器返回的CPU使用率数据能够被正确获取,无论它使用什么字段名 +3. **统一CPU使用率更新逻辑**:确保`processRealTimeData`函数和`updateStatsCards`函数中的CPU使用率更新逻辑一致 +4. **添加调试日志**:在关键位置添加调试日志,以便于排查问题 + +## 实现步骤 +1. 打开`dashboard.js`文件 +2. 找到`updateStatsCards`函数的数组形式数据结构处理部分(第631-641行),添加CPU使用率的处理逻辑 +3. 在`updateStatsCards`函数的CPU使用率数据获取逻辑中,添加更多可能的字段名支持 +4. 统一`processRealTimeData`函数和`updateStatsCards`函数中的CPU使用率更新逻辑 +5. 添加调试日志,以便于排查问题 +6. 测试修复是否生效 + +## 预期效果 +修复后,当WebSocket接收到实时数据更新时,CPU使用率卡片会自动更新显示最新的CPU使用率和状态,与其他统计卡片保持一致的实时更新效果。 \ No newline at end of file diff --git a/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题.md b/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题.md new file mode 100644 index 0000000..942931f --- /dev/null +++ b/.trae/documents/修复CPU使用率卡片WebSocket自动更新问题.md @@ -0,0 +1,18 @@ +## 问题分析 +CPU使用率卡片数据不会跟随WebSocket自动更新的原因是`updateStatsCards`函数中缺少了CPU使用率的更新逻辑。该函数负责处理WebSocket实时数据并更新统计卡片,但只更新了7个统计卡片,遗漏了CPU使用率卡片。 + +## 修复方案 +1. **修改`updateStatsCards`函数**:在`dashboard.js`文件中添加CPU使用率和状态的更新逻辑 +2. **添加数据获取逻辑**:从不同可能的数据结构中获取CPU使用率数据 +3. **更新DOM元素**:将获取到的CPU使用率数据更新到`cpu-usage`和`cpu-status`元素中 +4. **添加状态判断**:根据CPU使用率值设置不同的状态文本和样式 + +## 实现步骤 +1. 打开`dashboard.js`文件 +2. 找到`updateStatsCards`函数(约第550行) +3. 在函数末尾添加CPU使用率更新逻辑 +4. 确保从`stats`对象中正确获取CPU使用率数据 +5. 更新`cpu-usage`和`cpu-status`元素的内容和样式 + +## 预期效果 +修复后,当WebSocket接收到实时数据更新时,CPU使用率卡片会自动更新显示最新的CPU使用率和状态,与其他统计卡片保持一致的实时更新效果。 \ No newline at end of file diff --git a/.trae/documents/修复DNSSEC状态显示问题.md b/.trae/documents/修复DNSSEC状态显示问题.md new file mode 100644 index 0000000..331e479 --- /dev/null +++ b/.trae/documents/修复DNSSEC状态显示问题.md @@ -0,0 +1,38 @@ +## 问题分析 +1. **配置正确**:配置文件中`enableDNSSEC`设置为`true`,但卡片显示"已禁用" +2. **代码问题**:在`dns/server.go`的`GetStats`函数中,返回的`Stats`结构体缺少了DNSSEC相关字段 +3. **API行为**:当API访问DNSSEC相关字段时,由于没有从`GetStats`函数返回,它们的值都是默认值(`DNSSECEnabled`默认值为`false`) +4. **前端显示**:前端卡片从API获取`DNSSECEnabled`值,因此显示"已禁用" + +## 解决方案 +修改`dns/server.go`中的`GetStats`函数,确保返回的`Stats`副本包含所有DNSSEC相关字段,包括`DNSSECEnabled`、`DNSSECQueries`、`DNSSECSuccess`和`DNSSECFailed`。 + +## 修复步骤 +1. 打开`/root/dns/dns/server.go`文件 +2. 找到`GetStats`函数(大约在第960行) +3. 修改返回的`Stats`结构体,添加缺失的DNSSEC相关字段 +4. 确保所有DNSSEC字段都从原始`Stats`结构体复制到返回的副本中 + +## 预期结果 +修复后,API将正确返回DNSSEC启用状态,前端卡片将显示"已启用",与配置文件中的设置一致。 + +## 代码修改点 +```go +// 返回统计信息的副本 +return &Stats{ + Queries: s.stats.Queries, + Blocked: s.stats.Blocked, + Allowed: s.stats.Allowed, + Errors: s.stats.Errors, + LastQuery: s.stats.LastQuery, + AvgResponseTime: s.stats.AvgResponseTime, + TotalResponseTime: s.stats.TotalResponseTime, + QueryTypes: queryTypesCopy, + SourceIPs: sourceIPsCopy, + CpuUsage: s.stats.CpuUsage, + DNSSECQueries: s.stats.DNSSECQueries, + DNSSECSuccess: s.stats.DNSSECSuccess, + DNSSECFailed: s.stats.DNSSECFailed, + DNSSECEnabled: s.stats.DNSSECEnabled, // 这是关键字段,确保返回正确的启用状态 +} +``` \ No newline at end of file diff --git a/.trae/documents/修复DNSSEC相关编译错误.md b/.trae/documents/修复DNSSEC相关编译错误.md new file mode 100644 index 0000000..85d9f70 --- /dev/null +++ b/.trae/documents/修复DNSSEC相关编译错误.md @@ -0,0 +1,22 @@ +## 问题分析 +在`/root/dns/dns/server.go`文件的`forwardDNSRequestWithCache`函数中,`dnssecSuccess`变量被声明和赋值,但没有被实际使用,导致编译错误: +``` +dns/server.go:622:6: declared and not used: dnssecSuccess +``` + +## 代码检查 +1. 变量在第622行声明:`var dnssecSuccess bool = false` +2. 在第708行和第714行被赋值:`dnssecSuccess = false` 和 `dnssecSuccess = true` +3. 但在整个函数中,该变量没有被任何条件判断或返回值使用 +4. 实际使用的是`signatureValid`变量来表示DNSSEC验证结果 + +## 解决方案 +删除未使用的`dnssecSuccess`变量,因为它的值与`signatureValid`完全相同,且没有被实际使用。 + +## 修复步骤 +1. 删除第622行的`dnssecSuccess`变量声明 +2. 删除第708行和第714行对`dnssecSuccess`变量的赋值 +3. 保留`signatureValid`变量的使用,因为它是实际用于判断DNSSEC验证结果的变量 + +## 预期结果 +修复后,编译器不再报错,DNSSEC验证逻辑保持不变,继续正常工作。 \ No newline at end of file diff --git a/.trae/documents/修复DNSSEC记录处理问题.md b/.trae/documents/修复DNSSEC记录处理问题.md new file mode 100644 index 0000000..d872ada --- /dev/null +++ b/.trae/documents/修复DNSSEC记录处理问题.md @@ -0,0 +1,20 @@ +### 问题分析 +1. **收集验证记录集不完整**:在DNSSEC验证过程中,代码只从`response.Answer`和`response.Ns`中收集记录,而忽略了`response.Extra`中的记录,导致某些DNSSEC记录没有被正确验证。 +2. **验证失败处理不当**:当DNSSEC签名验证失败时,代码会直接丢弃该响应,而不是返回包含DNSSEC记录的响应,导致客户端无法获取DNSSEC记录。 + +### 修复方案 +1. **修复记录集收集逻辑**:在收集需要验证的记录集(rrset)时,确保从`response.Answer`、`response.Ns`和`response.Extra`中收集所有相关记录。 +2. **改进验证失败处理**:即使DNSSEC签名验证失败,也要返回包含DNSSEC记录的响应,同时设置正确的AD标志,让客户端决定如何处理验证失败的情况。 +3. **优化备选响应逻辑**:确保在所有上游服务器都不返回DNSSEC记录时,仍然能够正确处理和返回响应。 + +### 具体修改 +1. 修改`forwardDNSRequestWithCache`函数中的记录集收集逻辑,添加对`response.Extra`的处理 +2. 修改DNSSEC验证失败时的处理逻辑,确保返回包含DNSSEC记录的响应 +3. 优化备选响应的保存和返回逻辑,确保DNSSEC记录能够被正确处理 + +### 测试计划 +1. 编译修复后的代码 +2. 启动DNS服务器并启用DNSSEC +3. 使用dig命令测试DNSSEC记录查询,例如:`dig +dnssec example.com` +4. 检查查询结果是否包含DNSSEC记录(RRSIG、DNSKEY等) +5. 检查日志中是否有DNSSEC验证相关的记录 \ No newline at end of file diff --git a/.trae/documents/修复DNS请求趋势图表展开后影响主页面图表的问题.md b/.trae/documents/修复DNS请求趋势图表展开后影响主页面图表的问题.md new file mode 100644 index 0000000..e6d76eb --- /dev/null +++ b/.trae/documents/修复DNS请求趋势图表展开后影响主页面图表的问题.md @@ -0,0 +1,18 @@ +## 问题分析 +当前实现中,详细图表(浮窗)的时间范围切换会影响到主页面的图表显示,这是因为它们共享了全局变量`currentTimeRange`和`isMixedView`。当用户在浮窗内切换时间范围时,这些全局变量会被修改,导致主页面的图表也随之改变。 + +## 解决方案 +1. 为详细图表创建独立的变量,用于存储其时间范围和混合视图状态 +2. 修改`initDetailedTimeRangeToggle`函数,使其使用这些独立的变量,而不是全局变量 +3. 修改`drawDetailedDNSRequestsChart`函数,使用独立的变量来控制图表显示 +4. 确保主图表默认显示混合视图 + +## 修复步骤 +1. 在`dashboard.js`文件中添加详细图表专用的全局变量 +2. 修改`initDetailedTimeRangeToggle`函数,使用详细图表专用变量 +3. 修改`drawDetailedDNSRequestsChart`函数,使用详细图表专用变量 +4. 确保主图表默认显示混合视图 +5. 测试修复效果,确保浮窗内的时间范围切换不会影响主页面图表 + +## 预期效果 +修复后,DNS请求趋势图表默认显示混合内容视图不变,当用户点击展开按钮查看详细数据时,浮窗内的时间范围切换不会影响到主页面的图表内容,提供更好的用户体验。 \ No newline at end of file diff --git a/.trae/documents/修复DNS请求趋势图表展开后的响应式问题.md b/.trae/documents/修复DNS请求趋势图表展开后的响应式问题.md new file mode 100644 index 0000000..c86530f --- /dev/null +++ b/.trae/documents/修复DNS请求趋势图表展开后的响应式问题.md @@ -0,0 +1,15 @@ +## 问题分析 +DNS请求趋势图表展开后不能随页面放大缩小自动调整大小。通过代码分析,发现`window.addEventListener('resize')`事件监听器只处理了侧边栏的显示/隐藏,没有处理图表的调整大小。 + +## 解决方案 +1. 修改`window.addEventListener('resize')`事件监听器,添加对所有图表(包括详细图表)的更新调用 +2. 确保在模态框显示时,图表能够正确响应窗口大小变化 + +## 修复步骤 +1. 打开`/root/dns/static/js/dashboard.js`文件 +2. 找到`window.addEventListener('resize')`事件监听器 +3. 修改该事件监听器,添加对`dnsRequestsChart`和`detailedDnsRequestsChart`的更新调用 +4. 确保图表实例存在时才调用update方法 + +## 预期效果 +修复后,当用户展开DNS请求趋势图表并调整浏览器窗口大小时,图表会自动调整大小以适应新的窗口尺寸。 \ No newline at end of file diff --git a/.trae/documents/修复DNS请求趋势图表展开后超出显示范围的问题.md b/.trae/documents/修复DNS请求趋势图表展开后超出显示范围的问题.md new file mode 100644 index 0000000..b0a7eec --- /dev/null +++ b/.trae/documents/修复DNS请求趋势图表展开后超出显示范围的问题.md @@ -0,0 +1,21 @@ +## 问题分析 +展开图表超出了显示范围,没有按照展开浮窗大小显示内容。通过代码分析,发现以下问题: + +1. 图表容器使用了固定高度 `h-[600px]`,这可能导致在某些屏幕尺寸下图表超出显示范围 +2. 浮窗容器设置了 `max-h-[90vh]`,但图表容器的固定高度可能超过这个限制 +3. 当图表初始化时,可能没有正确计算容器的实际可用空间 + +## 解决方案 +1. 修改图表容器的高度设置,使其更灵活,能够适应不同屏幕尺寸 +2. 确保图表容器的高度不超过浮窗的最大高度限制 +3. 在图表显示时,确保正确计算容器大小并更新图表 + +## 修复步骤 +1. 打开 `index.html` 文件,修改图表容器的高度设置 +2. 将固定高度 `h-[600px]` 改为相对高度或最大高度 +3. 确保图表容器的高度能够适应浮窗的可用空间 +4. 在 `drawDetailedDNSRequestsChart` 函数中,添加对图表容器大小的检查和调整 +5. 确保在图表显示时,正确计算容器大小并更新图表 + +## 预期效果 +修复后,当用户展开DNS请求趋势图表时,图表会根据浮窗的可用空间自动调整大小,不会超出显示范围,提供更好的用户体验。 \ No newline at end of file diff --git a/.trae/documents/修复TOP域名卡片没有数据显示的问题.md b/.trae/documents/修复TOP域名卡片没有数据显示的问题.md new file mode 100644 index 0000000..92b41f8 --- /dev/null +++ b/.trae/documents/修复TOP域名卡片没有数据显示的问题.md @@ -0,0 +1,4 @@ +1. 检查updateTopData函数,修复当API调用失败或返回错误时没有数据显示的问题 +2. 确保在所有情况下,updateTopDomainsTable函数都能被调用,即使API调用失败 +3. 为updateTopData函数添加错误处理,确保在API调用失败时使用模拟数据 +4. 测试修复后的代码,确保TOP域名卡片能正确显示数据 \ No newline at end of file diff --git a/.trae/documents/修复TOP客户端数据加载失败问题.md b/.trae/documents/修复TOP客户端数据加载失败问题.md new file mode 100644 index 0000000..5487802 --- /dev/null +++ b/.trae/documents/修复TOP客户端数据加载失败问题.md @@ -0,0 +1,32 @@ +## 问题分析 + +经过分析,TOP客户端数据加载失败的原因是**前端API调用路径与后端注册的路径不匹配**: + +1. **前端代码**:在 `static/js/api.js` 中,`getTopClients` 方法调用的是 `/top-clients` API端点 +2. **后端代码**:在 `http/server.go` 中,注册的API路径是 `/api/top-clients` +3. **结果**:前端请求404错误,导致TOP客户端数据加载失败 + +## 解决方案 + +修改前端API调用,将 `/top-clients` 改为 `/api/top-clients`,确保与后端注册的路径匹配。 + +## 实现步骤 + +1. **修改 `static/js/api.js` 文件**: + - 找到 `getTopClients` 方法 + - 将API调用路径从 `/top-clients?t=` 改为 `/api/top-clients?t=` + +2. **验证修复**: + - 刷新页面,检查TOP客户端数据是否能正常加载 + - 查看浏览器控制台,确认没有404错误 + +## 预期效果 + +- TOP客户端数据能够正常加载 +- 不再显示"加载失败"错误信息 +- 页面上显示真实的TOP客户端数据 + +## 代码修改 + +只需要修改一个文件: +- `static/js/api.js`:更新 `getTopClients` 方法的API路径 \ No newline at end of file diff --git a/.trae/documents/修复TOP客户端无数据显示的问题.md b/.trae/documents/修复TOP客户端无数据显示的问题.md new file mode 100644 index 0000000..626267b --- /dev/null +++ b/.trae/documents/修复TOP客户端无数据显示的问题.md @@ -0,0 +1,4 @@ +1. 检查updateTopClientsTable函数,添加对tableBody是否存在的检查 +2. 改进模拟数据,使用真实的IP地址和请求次数 +3. 确保函数在任何情况下都能正确执行 +4. 测试修复后的代码,确保TOP客户端卡片能正确显示数据 \ No newline at end of file diff --git a/.trae/documents/修复Web刷新时自动回到仪表盘页面的问题.md b/.trae/documents/修复Web刷新时自动回到仪表盘页面的问题.md new file mode 100644 index 0000000..e1cd3a6 --- /dev/null +++ b/.trae/documents/修复Web刷新时自动回到仪表盘页面的问题.md @@ -0,0 +1,95 @@ +# 问题分析 + +1. **问题现象**:当用户在某个设置项页面刷新时,页面会自动回到仪表盘页面。 + +2. **问题根源**: + - 在 `dashboard.js` 文件中,`handleHashChange` 函数被定义在 `handlePageSwitch` 函数内部 + - 当页面刷新时,`handleHashChange` 函数会在页面加载完成后立即执行 + - 此时,`menuItems` 可能还没有被正确初始化,或者 `handleHashChange` 函数内部的 `menuItems` 引用的是旧的变量,导致它无法找到对应的菜单项 + - 当 `handleHashChange` 函数无法找到对应的菜单项时,它会执行 `window.location.hash = '#dashboard'`,将页面重定向到仪表盘页面 + +3. **具体问题**: + - `handleHashChange` 函数在找不到对应的菜单项时,会立即重定向到仪表盘页面,而不是先尝试直接显示对应的内容 + - 这导致用户在刷新页面时,即使URL中包含正确的hash,页面也会被重定向到仪表盘 + +# 修复方案 + +1. **修复 `handleHashChange` 函数**: + - 修改 `handleHashChange` 函数,确保它在找不到对应的菜单项时,不会总是重定向到仪表盘页面 + - 当无法找到对应的菜单项时,先尝试直接显示对应的内容 + - 只有当对应的内容也不存在时,才重定向到仪表盘页面 + +2. **优化页面初始化流程**: + - 确保 `handleHashChange` 函数在页面完全加载后才执行 + - 确保 `menuItems` 变量在 `handleHashChange` 函数执行前已经被正确初始化 + +# 实现步骤 + +1. 修改 `dashboard.js` 文件中的 `handleHashChange` 函数: + - 当无法找到对应的菜单项时,先尝试直接显示对应的内容 + - 只有当对应的内容也不存在时,才重定向到仪表盘页面 + +2. 测试修复后的功能: + - 启动DNS服务器 + - 访问Web界面,导航到某个设置项页面,例如 `#config` + - 刷新页面 + - 验证页面是否仍然显示在设置项页面,而不是自动回到仪表盘页面 + +# 预期结果 + +- 用户在某个设置项页面刷新时,页面会保持在当前页面,不会自动回到仪表盘页面 +- 只有当URL中的hash无效时,页面才会重定向到仪表盘页面 +- 页面导航功能正常,用户可以通过点击菜单项切换页面 + +# 修复代码 + +```javascript +// 修改 dashboard.js 文件中的 handleHashChange 函数 +function handleHashChange() { + let hash = window.location.hash; + + // 如果没有hash,默认设置为#dashboard + if (!hash) { + hash = '#dashboard'; + window.location.hash = hash; + return; + } + + const targetId = hash.substring(1); + + // 查找对应的内容元素 + const contentElement = document.getElementById(`${targetId}-content`); + + // 如果找到了对应的内容元素,直接显示 + if (contentElement) { + // 隐藏所有内容 + document.querySelectorAll('[id$="-content"]').forEach(content => { + content.classList.add('hidden'); + }); + + // 显示目标内容 + contentElement.classList.remove('hidden'); + + // 查找对应的菜单项并更新活动状态 + let targetMenuItem = null; + menuItems.forEach(item => { + if (item.getAttribute('href') === hash) { + targetMenuItem = item; + } + }); + + // 更新活动菜单项 + menuItems.forEach(item => { + item.classList.remove('sidebar-item-active'); + if (item.getAttribute('href') === hash) { + item.classList.add('sidebar-item-active'); + // 更新页面标题 + document.getElementById('page-title').textContent = item.querySelector('span').textContent; + } + }); + } else { + // 如果没有找到对应的内容,默认显示dashboard + window.location.hash = '#dashboard'; + } +} +``` \ No newline at end of file diff --git a/.trae/documents/修复_api_top-clients端点数据持久化问题.md b/.trae/documents/修复_api_top-clients端点数据持久化问题.md new file mode 100644 index 0000000..d5dd398 --- /dev/null +++ b/.trae/documents/修复_api_top-clients端点数据持久化问题.md @@ -0,0 +1,58 @@ +## 问题分析 + +通过代码分析,我发现/api/top-clients端点数据无法持久化的原因有两个: + +1. **数据保存逻辑缺失**:在`saveStatsData`方法中,虽然`StatsData`结构体包含了`ClientStats`字段,但没有将`server.clientStats`赋值给`statsData.ClientStats` + +2. **数据加载逻辑缺失**:在`loadStatsData`方法中,虽然`StatsData`结构体包含了`ClientStats`字段,但没有将`statsData.ClientStats`赋值给`server.clientStats` + +3. **自动保存功能未启用**:`startAutoSave`方法没有在`Server`的`Start`方法中被调用,导致数据不会定期保存 + +## 修复计划 + +1. **修改`saveStatsData`方法**:添加保存`ClientStats`数据的逻辑 +2. **修改`loadStatsData`方法**:添加加载`ClientStats`数据的逻辑 +3. **在`Server.Start`方法中调用`startAutoSave`**:确保数据定期保存 + +## 修复步骤 + +### 步骤1:修改`saveStatsData`方法 +在`dns/server.go`文件中,修改`saveStatsData`方法,添加保存`ClientStats`数据的逻辑: + +```go +// 复制客户端统计数据 +s.clientStatsMutex.RLock() +statsData.ClientStats = make(map[string]*ClientStats) +for k, v := range s.clientStats { + statsData.ClientStats[k] = v +} +s.clientStatsMutex.RUnlock() +``` + +### 步骤2:修改`loadStatsData`方法 +在`dns/server.go`文件中,修改`loadStatsData`方法,添加加载`ClientStats`数据的逻辑: + +```go +s.clientStatsMutex.Lock() +if statsData.ClientStats != nil { + s.clientStats = statsData.ClientStats +} +s.clientStatsMutex.Unlock() +``` + +### 步骤3:在`Server.Start`方法中调用`startAutoSave` +在`dns/server.go`文件的`Start`方法中,添加调用`startAutoSave`的代码: + +```go +// 启动自动保存功能 +go s.startAutoSave() +``` + +## 预期效果 + +修复后,/api/top-clients端点的数据将: +1. 在服务器启动时从文件加载 +2. 定期自动保存到文件 +3. 在服务器停止时最后保存一次 + +这样就能确保/api/top-clients端点的数据持久化,不会因为服务器重启而丢失。 \ No newline at end of file diff --git a/.trae/documents/修复dns_server.go文件的语法错误和恢复完整内容.md b/.trae/documents/修复dns_server.go文件的语法错误和恢复完整内容.md new file mode 100644 index 0000000..e6a68c9 --- /dev/null +++ b/.trae/documents/修复dns_server.go文件的语法错误和恢复完整内容.md @@ -0,0 +1,31 @@ +### 问题分析 +1. **语法错误**:文件中存在多处语法错误,包括: + - 第34行:`type ClientStats uct` 应该是 `type ClientStats struct` + - 第36行:`Count 64` 应该是 `Count int64` + - 第143行:`/ DNSCache` 缺少一个星号,应该是 `// DNSCache` + - 多处缩进问题 + - 函数内容被截断 + +2. **文件完整性问题**:文件内容不完整,handleDNSRequest函数被截断,缺少后续的函数定义 + +### 修复方案 +1. **修复语法错误**: + - 修复ClientStats结构体定义 + - 修复DNSCache注释和结构体定义 + - 修复所有缩进问题 + +2. **恢复完整内容**: + - 重新编写或恢复handleDNSRequest函数的完整内容 + - 确保所有后续函数定义完整 + +### 具体修改 +1. 修复第34行的ClientStats结构体定义 +2. 修复第143行的DNSCache注释和结构体定义 +3. 修复所有缩进问题 +4. 恢复完整的handleDNSRequest函数 +5. 恢复后续所有函数定义 + +### 测试计划 +1. 编译修复后的代码 +2. 启动DNS服务器并测试功能 +3. 验证DNSSEC功能是否正常工作 \ No newline at end of file diff --git a/.trae/documents/修复updateRecentBlockedTable函数导致的执行中断问题.md b/.trae/documents/修复updateRecentBlockedTable函数导致的执行中断问题.md new file mode 100644 index 0000000..39d8ebc --- /dev/null +++ b/.trae/documents/修复updateRecentBlockedTable函数导致的执行中断问题.md @@ -0,0 +1,4 @@ +1. 检查updateRecentBlockedTable函数,添加对tableBody是否存在的检查 +2. 确保在移除recent-blocked-table元素后,updateRecentBlockedTable函数不会导致错误 +3. 确保后续的updateTopDomainsTable调用能够被正确执行 +4. 测试修复后的代码,确保TOP域名卡片能正确显示数据 \ No newline at end of file diff --git a/.trae/documents/修复web屏蔽管理页面一直显示处理中遮罩的问题.md b/.trae/documents/修复web屏蔽管理页面一直显示处理中遮罩的问题.md new file mode 100644 index 0000000..c822174 --- /dev/null +++ b/.trae/documents/修复web屏蔽管理页面一直显示处理中遮罩的问题.md @@ -0,0 +1,90 @@ +## 问题分析 + +通过代码分析,我发现web屏蔽管理页面点击后一直显示处理中灰色遮罩的原因有两个: + +1. **JavaScript错误导致`hideLoading()`不被调用**: + - `loadLocalRules()`函数尝试访问`document.getElementById('local-rules-count')`,但该元素在HTML中不存在 + - `loadRemoteRules()`函数尝试访问`document.getElementById('remote-rules-count')`,但该元素在HTML中不存在 + - `setupShieldEventListeners()`函数尝试访问`document.getElementById('view-local-rules-btn')`和`document.getElementById('view-remote-rules-btn')`,但这些元素在HTML中不存在 + - 这些JavaScript错误会导致函数执行中断,从而使`hideLoading()`不被调用,加载遮罩一直显示 + +2. **竞态条件问题**: + - `initShieldPage()`函数并行调用三个异步函数:`loadShieldStats()`、`loadLocalRules()`和`loadRemoteBlacklists()` + - 每个异步函数都会调用`showLoading()`,而`showLoading()`会先调用`hideLoading()`来移除现有加载状态,然后创建一个新的加载状态 + - 这种并行调用可能导致竞态条件,使最后一个加载遮罩无法被正确销毁 + +## 修复计划 + +1. **修改`loadLocalRules()`函数**:添加元素存在性检查,避免JavaScript错误 +2. **修改`loadRemoteRules()`函数**:添加元素存在性检查,避免JavaScript错误 +3. **修改`setupShieldEventListeners()`函数**:添加元素存在性检查,避免JavaScript错误 +4. **修改`initShieldPage()`函数**:确保只创建一个加载遮罩,并在所有异步函数完成后销毁它 + +## 修复步骤 + +### 步骤1:修改`loadLocalRules()`函数 +在`shield.js`文件中,修改`loadLocalRules()`函数,添加元素存在性检查: + +```javascript +// 更新本地规则数量显示 +if (document.getElementById('local-rules-count')) { + document.getElementById('local-rules-count').textContent = data.localRulesCount || 0; +} +``` + +### 步骤2:修改`loadRemoteRules()`函数 +在`shield.js`文件中,修改`loadRemoteRules()`函数,添加元素存在性检查: + +```javascript +// 更新远程规则数量显示 +if (document.getElementById('remote-rules-count')) { + document.getElementById('remote-rules-count').textContent = data.remoteRulesCount || 0; +} +``` + +### 步骤3:修改`setupShieldEventListeners()`函数 +在`shield.js`文件中,修改`setupShieldEventListeners()`函数,添加元素存在性检查: + +```javascript +// 添加切换查看本地规则和远程规则的事件监听 +if (document.getElementById('view-local-rules-btn')) { + document.getElementById('view-local-rules-btn').addEventListener('click', loadLocalRules); +} + +if (document.getElementById('view-remote-rules-btn')) { + document.getElementById('view-remote-rules-btn').addEventListener('click', loadRemoteRules); +} +``` + +### 步骤4:修改`initShieldPage()`函数 +在`shield.js`文件中,修改`initShieldPage()`函数,确保只创建一个加载遮罩,并在所有异步函数完成后销毁它: + +```javascript +// 初始化屏蔽管理页面 +async function initShieldPage() { + showLoading('加载屏蔽管理数据...'); + try { + // 并行加载所有数据 + await Promise.all([ + loadShieldStats(), + loadLocalRules(), + loadRemoteBlacklists() + ]); + // 设置事件监听器 + setupShieldEventListeners(); + } finally { + hideLoading(); + } +} +``` + +### 步骤5:修改异步函数,移除内部的`showLoading()`和`hideLoading()`调用 +在`shield.js`文件中,修改`loadShieldStats()`、`loadLocalRules()`和`loadRemoteBlacklists()`函数,移除内部的`showLoading()`和`hideLoading()`调用,只保留数据加载逻辑。 + +## 预期效果 + +修复后,web屏蔽管理页面点击后: +1. 只会显示一个加载遮罩 +2. 不会出现JavaScript错误 +3. 所有数据加载完成后,加载遮罩会被正确隐藏 +4. 页面会正常显示屏蔽管理设置内容 \ No newline at end of file diff --git a/.trae/documents/修复保存按钮错位问题.md b/.trae/documents/修复保存按钮错位问题.md new file mode 100644 index 0000000..b62d072 --- /dev/null +++ b/.trae/documents/修复保存按钮错位问题.md @@ -0,0 +1,37 @@ +# 修复保存按钮错位问题 + +## 问题分析 +远程黑名单管理部分的保存按钮错位,原因是: +- 添加黑名单表单使用了Grid布局(`grid grid-cols-1 md:grid-cols-3 gap-4`) +- 前两个子元素包含label和input,高度较高 +- 第三个子元素只有按钮和状态显示,没有label,高度较矮 +- 导致按钮在垂直方向上与前两个输入框不对齐 + +## 解决方案 +修改添加黑名单表单的HTML结构,确保三个子元素在垂直方向上对齐: + +1. **为第三个子元素添加垂直对齐** + - 确保保存按钮容器与前两个输入框容器在垂直方向上对齐 + - 可以选择顶部对齐或中间对齐,保持视觉一致性 + +2. **统一子元素结构** + - 为第三个子元素添加一个隐藏的label,确保结构一致 + - 或者调整Grid布局,使第三个子元素的内容垂直居中 + +3. **调整容器样式** + - 修改保存按钮容器的样式,确保垂直对齐 + - 可以使用`items-start`或`items-center`属性 + +## 实现步骤 + +1. 修改`add-blacklist-form`的HTML结构 + - 为第三个子元素添加`items-start`类,使其与前两个子元素的顶部对齐 + - 或者添加一个隐藏的label,确保结构一致 + +2. 测试修改后的效果 + - 确保保存按钮与前两个输入框在垂直方向上对齐 + - 保持响应式布局,在不同屏幕尺寸下都能正常显示 + +## 预期效果 + +保存按钮将与前两个输入框在垂直方向上对齐,消除错位现象,提升表单的视觉一致性和用户体验。 \ No newline at end of file diff --git a/.trae/documents/修复多次重启后_close of closed channel_错误.md b/.trae/documents/修复多次重启后_close of closed channel_错误.md new file mode 100644 index 0000000..bdac0a7 --- /dev/null +++ b/.trae/documents/修复多次重启后_close of closed channel_错误.md @@ -0,0 +1,134 @@ +# 优化设置界面实现计划 + +## 问题分析 + +当前设置界面存在配置项重复问题,需要进行优化,具体包括: + +1. "远程规则URL"配置项在多个界面重复出现 +2. "启用API"和"主机"选项不需要在当前界面显示 +3. 需要确保保存功能正常工作,写入config.json并触发服务器重新加载配置 + +## 优化方案 + +### 1. 修改HTML结构 + +* 移除"远程规则URL"配置项 + +* 移除"启用API"选项 + +* 移除"主机"选项 + +* 调整布局,确保界面美观合理 + +### 2. 更新JavaScript代码 + +* 修改`populateConfigForm`函数,移除对已删除配置项的处理 + +* 修改`collectFormData`函数,移除对已删除配置项的收集 + +* 确保保存功能能正确写入config.json文件 + +* 实现服务器重新加载配置的触发机制 + +* 提供明确的成功/失败反馈 + +### 3. 测试和验证 + +* 测试所有保留配置项的加载和保存功能 + +* 验证保存操作能正确写入config.json文件 + +* 验证服务器能重新加载配置 + +* 测试成功/失败反馈是否明确 + +## 具体实现步骤 + +1. **修改HTML结构** + + * 编辑`index.html`文件,移除不需要的配置项 + + * 调整布局,确保界面美观合理 + +2. **更新JavaScript代码** + + * 编辑`config.js`文件,修改`populateConfigForm`函数 + + * 修改`collectFormData`函数,移除对已删除配置项的处理 + + * 确保`handleSaveConfig`函数能正确保存配置 + + * 实现服务器重新加载配置的触发机制 + +3. **测试和验证** + + * 测试配置项的加载功能 + + * 测试配置项的保存功能 + + * 验证config.json文件是否正确更新 + + * 验证服务器是否重新加载配置 + + * 测试成功/失败反馈是否明确 + +## 预期效果 + +* 设置界面布局合理,无重复配置项 + +* 所有保留配置项均可正常配置 + +* 保存功能能正确写入config.json文件 + +* 服务器能重新加载配置,使更改立即生效 + +* 保存操作有明确的成功/失败反馈 + +## 技术要点 + +* 使用HTML和JavaScript修改界面结构和功能 + +* 确保与服务器API的正确交互 + +* 实现良好的用户反馈机制 + +* 确保配置的正确保存和加载 + +## 实现时间 + +* 预计1-2小时完成所有修改和测试 + +## 风险评估 + +* 低风险:修改范围明确,不涉及核心功能 + +* 可回滚:所有修改均为前端修改,可通过恢复文件轻松回滚 + +## 依赖关系 + +* 依赖服务器API的正常工作 + +* 依赖config.json文件的读写权限 + +## 测试策略 + +* 手动测试所有配置项的加载和保存功能 + +* 验证config.json文件的更新 + +* 测试服务器配置的重新加载 + +* 测试成功/失败反馈 + +## 验收标准 + +* 设置界面布局合理,无重复配置项 + +* 所有保留配置项均可正常配置 + +* 保存功能能正确写入config.json文件 + +* 服务器能重新加载配置,使更改立即生效 + +* 保存操作有明确的成功/失败反馈 + diff --git a/.trae/documents/修复屏蔽规则解析问题.md b/.trae/documents/修复屏蔽规则解析问题.md new file mode 100644 index 0000000..4cec8e8 --- /dev/null +++ b/.trae/documents/修复屏蔽规则解析问题.md @@ -0,0 +1,33 @@ +1. **修复未定义函数问题**:移除对 `containsRegexSpecialChars` 函数的调用,该函数在代码中未定义 + +2. **修改正则表达式处理逻辑**: + + * 对于 `/` 包裹的规则,直接将中间内容作为正则表达式模式 + + * 不再添加 `.*` 前缀和后缀,也不再使用 `regexp.QuoteMeta` 转义 + + * 确保正则表达式能正确匹配域名中的相关内容 + +3. **修改文件**: + + * `/root/dns/shield/manager.go`:更新 `parseRule` 函数中的正则表达式处理逻辑 + +**验证修复**: + +确保 `/ad.qq.com/` 规则能正确匹配 `ad.qq.com` 域名 + +确保 `/ad/` 规则能匹配包含 `ad` 的域名 + +* 确保正则表达式特殊字符能被正确处理 +* **测试场景**: + + * 测试 `/ad.qq.com/` 规则匹配 `ad.qq.com` + + * 测试 `/ad.qq.com/` 规则匹配 `sub.ad.qq.com` + + * 测试 `/ad/` 规则匹配 `ad.example.com` + + * 测试 `/ad/` 规则匹配 `example.ad.com` + + * 测试 `/^ad/` 规则匹配 `ad.example.com` 但不匹配 `example.ad.com` + diff --git a/.trae/documents/修复服务器重启时端口被占用和数据保存问题.md b/.trae/documents/修复服务器重启时端口被占用和数据保存问题.md new file mode 100644 index 0000000..aff7927 --- /dev/null +++ b/.trae/documents/修复服务器重启时端口被占用和数据保存问题.md @@ -0,0 +1,57 @@ +## 问题分析 + +1. **端口被占用问题**: + - 服务器重启时提示端口被占用,可能是因为之前的服务器进程没有完全关闭 + - 从之前的ps aux命令输出中,看到有一个dns-server进程在运行(PID: 233272) + - 这导致新的服务器进程无法绑定到相同的端口 + +2. **数据保存提示no such file or directory问题**: + - 配置文件中statsFile和shield_stats.json的路径格式不一致(有的带./,有的不带) + - 可能存在目录创建失败或权限问题 + - 程序运行时的工作目录与预期不符 + +## 修复方案 + +1. **解决端口被占用问题**: + - 在启动新服务器之前,确保所有旧的服务器进程都已关闭 + - 可以通过kill命令手动关闭旧进程 + - 或者在程序中添加自动检测和关闭旧进程的逻辑 + +2. **解决数据保存问题**: + - 统一配置文件中的文件路径格式,确保所有路径都使用相对路径或绝对路径 + - 确保createRequiredFiles函数能够正确创建所有必要的目录和文件 + - 添加错误处理,确保在目录或文件创建失败时能够给出明确的错误信息 + - 检查程序运行时的工作目录,确保路径解析正确 + +## 修复步骤 + +1. **关闭旧的服务器进程**: + - 使用kill命令关闭旧的dns-server进程 + - 验证旧进程是否已关闭 + +2. **统一配置文件中的文件路径格式**: + - 修改配置文件,确保所有文件路径都使用一致的格式 + - 例如,将所有路径改为相对路径,不带./前缀 + +3. **修改createRequiredFiles函数**: + - 确保函数能够正确创建所有必要的目录和文件 + - 添加更详细的错误处理和日志 + - 确保函数能够处理不同格式的文件路径 + +4. **测试修复效果**: + - 启动服务器,检查是否能够成功绑定到端口 + - 检查数据文件是否能够正确保存 + - 重启服务器,检查是否能够正常启动 + +## 预期效果 + +- 服务器能够成功启动,不会提示端口被占用 +- 数据文件能够正确保存,不会提示no such file or directory +- 服务器重启时能够正常启动,不会出现相同的问题 + +## 注意事项 + +- 确保在修改配置文件之前备份原始文件 +- 确保程序有足够的权限创建和写入文件 +- 确保在关闭旧进程之前,所有重要的数据都已保存 +- 测试修复效果时,确保覆盖所有可能的情况 \ No newline at end of file diff --git a/.trae/documents/修复本地规则管理不工作的问题.md b/.trae/documents/修复本地规则管理不工作的问题.md new file mode 100644 index 0000000..5376846 --- /dev/null +++ b/.trae/documents/修复本地规则管理不工作的问题.md @@ -0,0 +1,41 @@ +# 问题分析 + +1. **问题现象**:用户点击删除按钮删除本地规则时,通过API发送数据到服务器,但Web界面没有自动刷新本地规则列表。 + +2. **问题根源**: + - 后端`RemoveRule`方法存在缺陷,当删除规则时没有正确更新所有相关映射 + - 前端代码虽然调用了重新加载规则列表的函数,但由于后端数据不一致,导致刷新后列表没有变化 + +3. **具体问题**: + - 在`shield/manager.go`的`RemoveRule`方法中,删除域名规则时只从`domainRules`或`domainExceptions`映射中删除了规则,但没有更新`domainRulesIsLocal`、`domainExceptionsIsLocal`、`domainRulesSource`和`domainExceptionsSource`映射 + - 处理正则表达式规则时,使用了错误的比较方式(`re.pattern.String() != pattern`),而应该使用原始规则字符串进行比较 + +# 修复方案 + +1. **修复`RemoveRule`方法**: + - 当删除域名规则时,同时更新所有相关映射 + - 当删除排除规则时,同时更新所有相关映射 + - 修复正则表达式规则的比较方式,使用原始规则字符串进行比较 + +2. **验证前端代码**: + - 确认前端代码在删除规则后正确调用了重新加载规则列表的函数 + - 验证前端代码处理API响应的逻辑是否正确 + +# 实现步骤 + +1. 修改`shield/manager.go`文件中的`RemoveRule`方法: + - 在删除域名规则时,添加删除`domainRulesIsLocal`和`domainRulesSource`映射的代码 + - 在删除排除规则时,添加删除`domainExceptionsIsLocal`和`domainExceptionsSource`映射的代码 + - 修复正则表达式规则的比较方式 + +2. 测试修复后的功能: + - 启动DNS服务器 + - 访问Web界面,添加几条本地规则 + - 点击删除按钮删除其中一条规则 + - 验证规则列表是否自动刷新,且删除的规则不再显示 + +# 预期结果 + +- 用户点击删除按钮后,规则被成功删除 +- Web界面自动刷新,删除的规则从列表中消失 +- 本地规则文件被正确更新,删除的规则不再存在于文件中 \ No newline at end of file diff --git a/.trae/documents/修复本地规则管理删除规则功能.md b/.trae/documents/修复本地规则管理删除规则功能.md new file mode 100644 index 0000000..5fca29a --- /dev/null +++ b/.trae/documents/修复本地规则管理删除规则功能.md @@ -0,0 +1,51 @@ +## 问题分析 + +通过代码分析,我发现本地规则管理删除规则功能失效的原因有两个: + +1. **规则格式不匹配**: + - 前端显示的规则带有修饰符,例如:`||example.com^` + - 服务器端实际存储的是裸域名,例如:`example.com` + - `RemoveRule` 函数在处理规则时,虽然尝试了多种格式变体,但没有正确处理前端发送的带有修饰符的规则 + +2. **本地规则标记未更新**: + - `RemoveRule` 函数没有考虑 `m.domainRulesIsLocal` 和 `m.domainExceptionsIsLocal` 映射,这些映射用于标记哪些规则是本地规则 + - 删除规则后,没有更新这些映射,导致规则删除不彻底 + +## 修复计划 + +1. **修改 `RemoveRule` 函数**: + - 改进规则处理逻辑,确保能正确处理带有修饰符的规则 + - 更新 `domainRulesIsLocal` 和 `domainExceptionsIsLocal` 映射,确保本地规则被正确删除 + +2. **修改 `GetLocalRules` 函数**: + - 确保返回的规则格式与 `RemoveRule` 函数期望的格式一致 + +3. **添加调试日志**: + - 在关键位置添加日志,便于调试和监控规则删除过程 + +## 修复步骤 + +### 步骤1:修改 `RemoveRule` 函数 +在 `shield/manager.go` 文件中,修改 `RemoveRule` 函数,改进规则处理逻辑: + +1. 确保正确处理带有修饰符的规则 +2. 更新 `domainRulesIsLocal` 和 `domainExceptionsIsLocal` 映射 +3. 添加调试日志 + +### 步骤2:测试修复效果 +- 启动服务器 +- 访问本地规则管理页面 +- 添加一条本地规则 +- 删除该规则 +- 验证规则是否被正确删除,页面内容是否减少 + +## 预期效果 + +修复后,本地规则管理删除规则功能将正常工作: +- 点击删除按钮后,规则会被正确发送到服务器 +- 服务器会正确处理带有修饰符的规则 +- 本地规则标记会被正确更新 +- 规则会被持久化保存 +- 页面内容会立即减少 + +这样就能确保本地规则管理删除规则功能正常工作,提供良好的用户体验。 \ No newline at end of file diff --git a/.trae/documents/修复用户点击选项时不触发地址栏#后缀的问题.md b/.trae/documents/修复用户点击选项时不触发地址栏#后缀的问题.md new file mode 100644 index 0000000..90fba3f --- /dev/null +++ b/.trae/documents/修复用户点击选项时不触发地址栏#后缀的问题.md @@ -0,0 +1,28 @@ +## 问题分析 + +在 `main.js` 文件中,当用户点击菜单项时,代码使用了 `e.preventDefault()` 来阻止默认行为,这导致浏览器不会自动更新地址栏中的 hash。虽然 `dashboard.js` 文件中有 `handleHashChange` 函数来处理 hash 变化,但由于 `main.js` 中的 `e.preventDefault()`,点击菜单项时不会触发 hash 变化。 + +## 修复方案 + +1. **修改 `main.js` 文件**:移除 `e.preventDefault()`,或者在处理完点击事件后手动更新地址栏中的 hash +2. **确保 `main.js` 和 `dashboard.js` 中的点击事件处理逻辑不冲突** +3. **统一页面导航逻辑**:确保所有页面导航都通过 hash 变化来实现 + +## 修复步骤 + +1. **修改 `main.js` 文件**: + - 移除 `e.preventDefault()`,允许浏览器自动更新地址栏中的 hash + - 或者在处理完点击事件后,手动设置 `window.location.hash = item.getAttribute('href')` + - 确保点击事件处理逻辑与 `dashboard.js` 中的 `handleHashChange` 函数兼容 + +2. **测试修复效果**: + - 点击菜单项,检查地址栏中的 hash 是否正确更新 + - 刷新页面,检查是否能保持在当前页面 + - 直接修改地址栏中的 hash,检查是否能正确导航到对应页面 + +## 预期效果 + +- 用户点击菜单项时,地址栏中的 hash 会自动更新 +- 页面刷新时,会保持在当前页面 +- 直接修改地址栏中的 hash 可以导航到对应页面 +- 所有页面导航逻辑统一,避免冲突 \ No newline at end of file diff --git a/.trae/documents/修复规则更新后没有生效的问题.md b/.trae/documents/修复规则更新后没有生效的问题.md new file mode 100644 index 0000000..fc57f94 --- /dev/null +++ b/.trae/documents/修复规则更新后没有生效的问题.md @@ -0,0 +1,48 @@ +# 问题分析 + +1. **问题现象**:规则更新后,规则没有生效。用户添加或删除规则后,DNS服务器仍然使用旧的规则进行域名屏蔽。 + +2. **问题根源**: + - 在`addDomainRule`方法中,当添加一个域名规则时,它会为该域名的所有子域名也添加规则。例如,添加`example.com`规则时,会同时添加`example.com`和`com`规则。 + - 但是,在`RemoveRule`方法中,当删除一个域名规则时,它只删除了指定的域名规则,而没有删除为子域名添加的规则。 + - 这导致即使删除了主规则,子域名规则仍然存在,因此规则仍然生效。 + +3. **具体问题**: + - 当添加`||test.example.com`规则时,`addDomainRule`方法会添加`test.example.com`和`example.com`两个规则。 + - 当删除`||test.example.com^`规则时,`RemoveRule`方法只会删除`test.example.com`规则,而不会删除`example.com`规则,因此`example.com`仍然会被屏蔽。 + - 此外,`RemoveRule`方法在处理通配符和URL匹配规则时,也没有正确处理子域名规则的删除。 + +# 修复方案 + +1. **修复`RemoveRule`方法**: + - 当删除一个域名规则时,同时删除为该域名的所有子域名添加的规则。 + - 确保删除规则时,同时更新所有相关映射,包括主规则和子域名规则。 + +2. **验证修复效果**: + - 启动DNS服务器 + - 添加一条域名规则,例如`||test.example.com` + - 验证该规则及其子域名规则都被正确添加 + - 删除该规则 + - 验证该规则及其所有子域名规则都被正确删除 + - 验证DNS服务器不再使用该规则进行域名屏蔽 + +# 实现步骤 + +1. 修改`shield/manager.go`文件中的`RemoveRule`方法: + - 在删除域名规则时,添加删除所有相关子域名规则的逻辑 + - 确保删除规则时,同时更新所有相关映射 + +2. 测试修复后的功能: + - 启动DNS服务器 + - 访问Web界面,添加一条本地规则,例如`||test.example.com` + - 验证该规则被正确添加 + - 点击删除按钮删除该规则 + - 验证该规则及其所有子域名规则都被正确删除 + - 验证DNS服务器不再使用该规则进行域名屏蔽 + +# 预期结果 + +- 用户添加规则后,规则立即生效 +- 用户删除规则后,规则立即失效 +- 规则更新后,DNS服务器使用最新的规则进行域名屏蔽 +- 本地规则文件被正确更新,添加和删除的规则都能正确反映在文件中 \ No newline at end of file diff --git a/.trae/documents/修复远程黑名单管理保存按钮对齐问题.md b/.trae/documents/修复远程黑名单管理保存按钮对齐问题.md new file mode 100644 index 0000000..699b234 --- /dev/null +++ b/.trae/documents/修复远程黑名单管理保存按钮对齐问题.md @@ -0,0 +1,20 @@ +## 问题分析 +从截图可以看出,远程黑名单管理的保存按钮与输入框在垂直方向上没有对齐,按钮位置偏下。 + +## 原因分析 +1. 当前使用grid布局分为3列,前两列包含label和input,第三列包含空label和按钮容器 +2. 空label虽然没有内容,但仍然占据了空间(mb-1 margin),导致按钮被推到下方 +3. 按钮容器使用了`items-center`,但整体位置受label影响 + +## 修复方案 +修改第三列的HTML结构,移除空label,直接放置按钮容器,并使用flex布局的对齐属性确保按钮与输入框垂直对齐。 + +## 具体修改 +1. 打开`/root/dns/static/index.html`文件 +2. 找到远程黑名单管理的添加表单部分(第839-847行) +3. 修改第三列的HTML结构,移除空label,直接放置按钮容器 +4. 使用flex布局的`items-end`属性确保按钮与输入框底部对齐 +5. 调整按钮的margin-top为0,确保与输入框精确对齐 + +## 预期效果 +保存按钮与输入框在垂直方向上精确对齐,整体布局更加美观和专业。 \ No newline at end of file diff --git a/.trae/documents/修改TOP域名卡片样式为请求域名排行.md b/.trae/documents/修改TOP域名卡片样式为请求域名排行.md new file mode 100644 index 0000000..e82de32 --- /dev/null +++ b/.trae/documents/修改TOP域名卡片样式为请求域名排行.md @@ -0,0 +1,5 @@ +1. 修改HTML文件中TOP域名卡片的标题,将"TOP域名"改为"请求域名排行" +2. 修改updateTopDomainsTable函数,使其生成的HTML结构与updateTopBlockedTable函数一致 +3. 将颜色从红色改为绿色,包括边框颜色、背景色和文本颜色 +4. 确保生成的HTML结构与被拦截域名排行的样式一致 +5. 测试修改后的代码,确保请求域名排行卡片显示正确 \ No newline at end of file diff --git a/.trae/documents/修改远程黑名单管理界面,添加状态渐变效果.md b/.trae/documents/修改远程黑名单管理界面,添加状态渐变效果.md new file mode 100644 index 0000000..2760439 --- /dev/null +++ b/.trae/documents/修改远程黑名单管理界面,添加状态渐变效果.md @@ -0,0 +1,29 @@ +# 修改远程黑名单管理界面,添加状态渐变效果 + +## 问题分析 +当前远程黑名单管理界面的状态更新功能已经实现了3秒后恢复默认状态的功能,但没有添加渐变效果,用户体验不够流畅。 + +## 解决方案 +修改`updateStatus`函数,为状态元素添加渐变效果,确保状态更新时的平滑过渡。 + +## 实现步骤 + +1. **修改`updateStatus`函数** + - 为状态元素添加CSS过渡效果 + - 使用`classList`操作来添加/移除渐变类 + - 确保状态更新时的平滑过渡 + +2. **添加CSS渐变样式** + - 在index.html中添加状态渐变的CSS样式 + - 为不同状态(loading、success、error)添加不同的渐变效果 + +3. **测试修改后的功能** + - 确保状态更新时有平滑的渐变效果 + - 确保3秒后状态能正常恢复默认 + +## 预期效果 +远程黑名单管理界面的状态更新将具有平滑的渐变效果,包括: +- 加载状态的蓝色渐变效果 +- 成功状态的绿色渐变效果 +- 错误状态的红色渐变效果 +- 所有状态在3秒后平滑消失,恢复默认状态 \ No newline at end of file diff --git a/.trae/documents/减小统计卡片大小并移除统计图.md b/.trae/documents/减小统计卡片大小并移除统计图.md new file mode 100644 index 0000000..d29c2b9 --- /dev/null +++ b/.trae/documents/减小统计卡片大小并移除统计图.md @@ -0,0 +1,40 @@ +# 减小统计卡片大小并移除统计图 + +## 需求分析 +用户希望减小统计卡片的大小并移除卡片中的统计图,只保留数值和基本信息。 + +## 实现方案 + +### 1. 修改统计卡片HTML结构 +- 移除每个统计卡片中包含canvas元素的div(高度为16px的图表区域) +- 减小卡片的内边距(从p-6改为p-4) +- 调整卡片内部元素的间距,确保布局紧凑美观 + +### 2. 移除图表相关JavaScript代码 +- 移除dashboard.js中对`initStatCardCharts()`和`updateStatCardCharts()`函数的调用 +- 这些函数负责初始化和更新统计卡片中的折线图 +- 移除后不会影响其他图表功能(如主图表区域的图表) + +### 3. 具体修改点 + +#### HTML文件修改(index.html) +- 对于每个统计卡片(共8个),移除包含canvas的div元素 +- 减小卡片内边距:将`p-6`改为`p-4` +- 调整卡片内部元素的margin和padding,确保布局紧凑 + +#### JavaScript文件修改(dashboard.js) +- 移除第31行的`initStatCardCharts()`调用 +- 移除第139行的`updateStatCardCharts(stats)`调用 +- 移除第367行的`updateStatCardCharts(stats)`调用 +- 移除第525行的`updateStatCardCharts(stats)`调用 + +## 预期效果 +- 统计卡片大小减小,布局更紧凑 +- 卡片中只显示标题、数值和百分比信息 +- 移除了不必要的图表,减少了页面加载时间和资源消耗 +- 整体界面更简洁,重点突出数值信息 + +## 注意事项 +- 确保移除图表后不会导致其他功能错误 +- 保持卡片之间的一致性和美观性 +- 确保数值和百分比信息清晰可见 \ No newline at end of file diff --git a/.trae/documents/在Web页面日志查询界面添加DNSSEC标志.md b/.trae/documents/在Web页面日志查询界面添加DNSSEC标志.md new file mode 100644 index 0000000..c2d765e --- /dev/null +++ b/.trae/documents/在Web页面日志查询界面添加DNSSEC标志.md @@ -0,0 +1,75 @@ +## 实现计划:在Web页面日志查询界面添加DNSSEC标志 + +### 1. 需求分析 +- 在Web页面日志查询界面的域名下方区域添加绿色DNSSEC标志 +- 当查询使用了DNSSEC时显示该标志 +- 标志应清晰可见,与现有界面风格协调 + +### 2. 实现步骤 + +#### 步骤1:扩展QueryLog结构体 +- 修改 `/root/dns/dns/server.go` 文件中的 `QueryLog` 结构体 +- 添加 `DNSSEC bool` 字段,用于标识查询是否使用了DNSSEC +- 更新 `addQueryLog` 函数,传入DNSSEC标志值 + +#### 步骤2:在DNS请求处理中记录DNSSEC信息 +- 修改 `/root/dns/dns/server.go` 文件中的 `handleDNSRequest` 函数 +- 在处理DNS响应时,检查是否使用了DNSSEC +- 将DNSSEC使用情况传递给 `addQueryLog` 函数 + +#### 步骤3:修改前端页面显示DNSSEC标志 +- 找到处理日志显示的前端代码 +- 修改日志条目模板,添加DNSSEC标志显示逻辑 +- 使用Font Awesome或其他图标库的DNSSEC相关图标 +- 当DNSSEC为true时显示绿色标志 + +#### 步骤4:测试实现 +- 编译并运行DNS服务器 +- 访问Web界面的日志查询页面 +- 进行DNS查询,验证DNSSEC标志是否正确显示 + +### 3. 技术细节 + +#### 后端修改 +- **文件**:`/root/dns/dns/server.go` +- **修改内容**: + - 扩展 `QueryLog` 结构体,添加 `DNSSEC bool` 字段 + - 在 `handleDNSRequest` 中判断DNSSEC使用情况 + - 更新 `addQueryLog` 函数参数和调用 + +#### 前端修改 +- **文件**:`/root/dns/static/index.html` 或相关JavaScript文件 +- **修改内容**: + - 查找日志条目的HTML模板 + - 添加DNSSEC标志显示逻辑 + - 使用条件渲染,仅当DNSSEC为true时显示 + +### 4. 预期效果 +- 日志查询界面的每个条目在域名下方显示DNSSEC标志 +- 使用DNSSEC的查询显示绿色标志 +- 未使用DNSSEC的查询不显示标志 +- 标志与现有界面风格协调,清晰易识别 + +### 5. 注意事项 +- 确保DNSSEC标志的视觉设计与现有界面一致 +- 确保标志在各种屏幕尺寸下都能正确显示 +- 考虑添加悬停提示,说明该标志的含义 +- 确保性能不受影响,标志渲染高效 + +### 6. 实现时间线 +- 步骤1:15分钟(扩展QueryLog结构体) +- 步骤2:20分钟(记录DNSSEC信息) +- 步骤3:30分钟(修改前端页面) +- 步骤4:15分钟(测试实现) + +### 7. 风险评估 +- 前端代码位置不明确:需要仔细查找处理日志显示的代码 +- DNSSEC检测逻辑可能复杂:需要确保准确判断DNSSEC使用情况 +- 图标资源问题:确保使用的图标库可用 + +### 8. 成功标准 +- DNS服务器正常编译运行 +- Web界面能正确显示DNSSEC标志 +- 使用DNSSEC的查询显示绿色标志 +- 未使用DNSSEC的查询不显示标志 +- 标志显示位置正确,视觉效果良好 \ No newline at end of file diff --git a/.trae/documents/在请求列表中显示DNSSEC标志.md b/.trae/documents/在请求列表中显示DNSSEC标志.md new file mode 100644 index 0000000..179022d --- /dev/null +++ b/.trae/documents/在请求列表中显示DNSSEC标志.md @@ -0,0 +1,73 @@ +## 实现计划:在请求列表中显示DNSSEC标志 + +### 1. 需求分析 +- 当`enableDNSSEC=true`时,请求列表中返回了DNSKEY的域名显示绿色的DNSSEC标志 +- 标志应显示在域名名称旁边 +- 保持现有界面风格一致 + +### 2. 实现步骤 + +#### 步骤1:修改后端数据结构 +- 在`dns/server.go`中扩展`BlockedDomain`结构体,添加`DNSSEC bool`字段 +- 修改`GetTopResolvedDomains`函数,返回包含DNSSEC标志的数据 +- 记录每个域名是否使用了DNSSEC + +#### 步骤2:实现DNSSEC记录追踪 +- 在`handleDNSRequest`函数中,当检测到DNSSEC记录时,更新域名的DNSSEC标志 +- 添加一个新的映射表,记录每个域名的DNSSEC使用情况 + +#### 步骤3:修改API响应格式 +- 修改`http/server.go`中的`handleTopDomains`和`handleTopResolvedDomains`函数 +- 在返回的数据中添加`dnssec`字段 + +#### 步骤4:修改前端显示逻辑 +- 修改`static/js/dashboard.js`中的`updateTopDomainsTable`函数 +- 在域名名称后添加DNSSEC标志 +- 使用Font Awesome的锁图标,绿色显示 + +#### 步骤5:测试实现 +- 编译并运行DNS服务器 +- 访问Web界面,检查请求列表 +- 验证DNSSEC标志是否正确显示 + +### 3. 技术细节 + +#### 3.1 后端实现 +- **文件**:`/root/dns/dns/server.go` +- **修改内容**: + - 扩展数据结构,添加DNSSEC字段 + - 实现DNSSEC记录追踪 + - 修改API响应格式 + +#### 3.2 前端实现 +- **文件**:`/root/dns/static/js/dashboard.js` +- **修改内容**: + - 修改`updateTopDomainsTable`函数 + - 添加DNSSEC标志显示逻辑 + - 使用条件渲染,仅当dnssec为true时显示 + +#### 3.3 DNSSEC检测方法 +- 检查响应中是否包含DNSKEY或RRSIG记录 +- 记录每个域名的DNSSEC使用情况 +- 在返回请求列表时包含该信息 + +### 4. 预期效果 +- 请求列表中使用DNSSEC的域名显示绿色锁图标 +- 标志与现有界面风格协调 +- 性能不受影响 +- 与现有功能兼容 + +### 5. 注意事项 +- 确保DNSSEC标志的视觉设计与现有界面一致 +- 确保标志在各种屏幕尺寸下都能正确显示 +- 考虑添加悬停提示,说明该标志的含义 +- 确保性能不受影响,数据更新高效 + +### 6. 实现时间线 +- 步骤1:20分钟(修改后端数据结构) +- 步骤2:25分钟(实现DNSSEC记录追踪) +- 步骤3:15分钟(修改API响应格式) +- 步骤4:15分钟(修改前端显示逻辑) +- 步骤5:10分钟(测试实现) + +通过以上实现,请求列表将能够正确显示使用了DNSSEC的域名,提高DNS查询的安全性可视化,方便管理员监控和管理。 \ No newline at end of file diff --git a/.trae/documents/实现domainSpecificDNS强制使用和search域支持.md b/.trae/documents/实现domainSpecificDNS强制使用和search域支持.md new file mode 100644 index 0000000..41dca6e --- /dev/null +++ b/.trae/documents/实现domainSpecificDNS强制使用和search域支持.md @@ -0,0 +1,37 @@ +## 实现计划 + +### 1. 配置文件修改 +- **修改`/root/dns/config/config.go`**: + - 在`DNSConfig`结构体中添加`PrefixDomain []string`字段,用于支持search domain功能 + - 在`LoadConfig`函数中添加`prefixDomain`的默认值处理 + +### 2. DNS请求处理逻辑修改 +- **修改`/root/dns/dns/server.go`中的`forwardDNSRequestWithCache`函数**: + - 强化domainSpecificDNS逻辑,确保当域名匹配时,只使用指定的DNS服务器 + - 移除向DNSSEC专用服务器发送请求的逻辑,当域名匹配domainSpecificDNS时 + - 确保匹配域名的DNS查询结果不会被其他DNS服务器的响应覆盖 + +- **修改`/root/dns/dns/server.go`中的`handleDNSRequest`函数**: + - 实现search domain功能,当直接查询失败时,尝试添加prefixDomain中指定的域名前缀 + - 按照/etc/resolv.conf中的search domain逻辑处理查询请求 + +### 3. 配置文件示例更新 +- **更新配置文件示例**: + - 添加`prefixDomain`配置项示例 + - 说明search domain功能的使用方法 + +### 4. 测试验证 +- 测试domainSpecificDNS强制使用功能,确保匹配的域名只使用指定的DNS服务器 +- 测试search domain功能,确保能够正确处理带前缀的域名查询 +- 测试不同配置组合下的功能正确性 + +## 预期效果 +1. 当域名匹配domainSpecificDNS配置时,无论DNSSEC是否启用,只使用指定的DNS服务器 +2. 支持search domain功能,能够自动尝试添加配置的域名前缀 +3. 配置简单直观,与/etc/resolv.conf的search domain行兼容 + +## 实现要点 +- 确保domainSpecificDNS配置的优先级最高 +- 实现高效的search domain查询逻辑,避免不必要的网络请求 +- 保持代码的可读性和可维护性 +- 确保与现有功能的兼容性 \ No newline at end of file diff --git a/.trae/documents/实现hash路由功能,避免刷新时返回到主页.md b/.trae/documents/实现hash路由功能,避免刷新时返回到主页.md new file mode 100644 index 0000000..35a202f --- /dev/null +++ b/.trae/documents/实现hash路由功能,避免刷新时返回到主页.md @@ -0,0 +1,87 @@ +# 实现hash路由功能,避免刷新时返回到主页 + +## 问题分析 + +当前系统使用简单的页面切换逻辑,通过隐藏/显示不同的内容区域来实现页面切换。这种方式的缺点是: + +1. 刷新页面后会返回到主页 +2. 无法通过URL直接访问特定页面 +3. 无法在浏览器历史记录中保存页面状态 + +## 解决方案 + +实现hash路由功能,将页面状态保存在URL的hash部分,例如: +- http://localhost:8080/#dashboard +- http://localhost:8080/#blacklists +- http://localhost:8080/#query +- http://localhost:8080/#config + +## 实现步骤 + +### 1. 修改handlePageSwitch函数 + +* 在页面切换时,更新URL的hash +* 移除e.preventDefault(),允许默认的hash变化 + +### 2. 添加hashchange事件监听器 + +* 监听window的hashchange事件 +* 在hash变化时,根据hash值显示相应的内容 +* 更新页面标题和活动菜单项 + +### 3. 添加initHashRoute函数 + +* 在页面加载时调用 +* 获取URL的hash值 +* 如果没有hash值,默认设置为#dashboard +* 根据hash值显示相应的内容 +* 更新页面标题和活动菜单项 + +### 4. 在页面加载时调用initHashRoute函数 + +* 确保在DOMContentLoaded事件中调用initHashRoute函数 + +## 预期效果 + +* 页面切换时,URL的hash会相应更新 +* 刷新页面后,会显示与URL hash对应的页面 +* 默认情况下,http://localhost:8080会显示dashboard内容,URL变为http://localhost:8080/#dashboard +* 可以通过URL直接访问特定页面 +* 浏览器历史记录中会保存页面状态 + +## 技术要点 + +* 使用window.location.hash获取和设置URL的hash值 +* 使用window.addEventListener('hashchange', ...)监听hash变化 +* 使用window.addEventListener('DOMContentLoaded', ...)在页面加载时初始化 +* 确保在页面加载时检查hash值,如果没有则设置为#dashboard + +## 实现时间 + +* 预计30分钟完成所有修改和测试 + +## 风险评估 + +* 低风险:修改范围明确,不涉及核心功能 +* 可回滚:所有修改均为前端修改,可通过恢复文件轻松回滚 + +## 依赖关系 + +* 依赖现有的页面切换逻辑 +* 依赖现有的CSS类和HTML结构 + +## 测试策略 + +* 手动测试页面切换时URL hash的变化 +* 测试刷新页面后是否显示正确的页面 +* 测试直接访问带hash的URL是否显示正确的页面 +* 测试默认情况下是否显示dashboard内容 + +## 验收标准 + +* 页面切换时,URL的hash会相应更新 +* 刷新页面后,会显示与URL hash对应的页面 +* 默认情况下,http://localhost:8080会显示dashboard内容,URL变为http://localhost:8080/#dashboard +* 可以通过URL直接访问特定页面 +* 浏览器历史记录中会保存页面状态 + diff --git a/.trae/documents/实现web远程黑名单管理功能.md b/.trae/documents/实现web远程黑名单管理功能.md new file mode 100644 index 0000000..6a07e31 --- /dev/null +++ b/.trae/documents/实现web远程黑名单管理功能.md @@ -0,0 +1,96 @@ +## 问题分析 + +当前系统已经实现了黑名单管理的后端API,但前端界面缺少完整的黑名单管理功能。具体来说: + +1. 后端API已经实现了黑名单管理的所有功能: + - GET /api/shield/blacklists - 获取所有黑名单 + - POST /api/shield/blacklists - 添加黑名单 + - PUT /api/shield/blacklists - 更新黑名单 + - DELETE /api/shield/blacklists/{name} - 删除黑名单 + +2. 前端已经有了一些基础功能: + - loadRemoteBlacklists函数用于加载远程黑名单 + - setupShieldEventListeners函数用于设置事件监听器 + - 页面上有blacklist-count元素用于显示黑名单数量 + +3. 但是,前端缺少完整的黑名单管理界面,包括: + - 黑名单列表展示 + - 添加/编辑/删除黑名单的功能 + - 启用/禁用黑名单的功能 + - 更新黑名单的功能 + +## 实现方案 + +1. **添加黑名单管理HTML界面**: + - 在shield-content中添加黑名单管理区域 + - 包括黑名单列表、添加/编辑表单、操作按钮等 + +2. **实现黑名单列表的加载和展示**: + - 完善loadRemoteBlacklists函数,将黑名单数据渲染到页面上 + - 显示黑名单的名称、URL、状态、更新时间等信息 + - 添加操作按钮(编辑、删除、启用/禁用、更新) + +3. **实现添加/编辑黑名单功能**: + - 添加表单用于输入黑名单名称、URL等信息 + - 实现表单提交功能,调用后端API添加/编辑黑名单 + +4. **实现删除黑名单功能**: + - 为删除按钮添加事件监听器 + - 调用后端API删除黑名单 + - 更新黑名单列表 + +5. **实现启用/禁用黑名单功能**: + - 为启用/禁用按钮添加事件监听器 + - 调用后端API更新黑名单状态 + - 更新黑名单列表 + +6. **实现更新黑名单功能**: + - 为更新按钮添加事件监听器 + - 调用后端API更新黑名单 + - 显示更新状态 + +## 实现步骤 + +1. **修改HTML文件**: + - 在shield-content中添加黑名单管理区域 + - 添加黑名单列表表格 + - 添加添加/编辑黑名单表单 + - 添加操作按钮 + +2. **修改shield.js文件**: + - 完善loadRemoteBlacklists函数,渲染黑名单列表 + - 添加添加/编辑/删除黑名单的函数 + - 添加启用/禁用黑名单的函数 + - 添加更新黑名单的函数 + - 完善setupShieldEventListeners函数,添加黑名单管理相关的事件监听器 + +3. **测试功能**: + - 测试黑名单列表的加载和展示 + - 测试添加/编辑/删除黑名单功能 + - 测试启用/禁用黑名单功能 + - 测试更新黑名单功能 + +## 预期效果 + +- 用户可以在web界面上查看所有黑名单 +- 用户可以添加新的黑名单 +- 用户可以编辑现有黑名单 +- 用户可以删除黑名单 +- 用户可以启用/禁用黑名单 +- 用户可以手动更新黑名单 +- 所有操作都能实时反映到页面上 + +## 注意事项 + +- 确保界面设计与现有系统风格一致 +- 确保所有操作都有适当的错误处理和提示 +- 确保所有操作都能实时更新页面数据 +- 确保功能实现符合后端API的要求 + +## 技术细节 + +- 使用HTML、CSS(Tailwind CSS)和JavaScript实现前端界面 +- 使用fetch API调用后端API +- 使用事件监听器处理用户交互 +- 使用DOM操作更新页面内容 +- 使用异步/等待处理异步操作 \ No newline at end of file diff --git a/.trae/documents/实现域名特定DNS服务器配置.md b/.trae/documents/实现域名特定DNS服务器配置.md new file mode 100644 index 0000000..a1c05ba --- /dev/null +++ b/.trae/documents/实现域名特定DNS服务器配置.md @@ -0,0 +1,72 @@ +# 改进DNS查询模式实现 + +## 1. 当前实现分析 + +* **parallel**: 已基本实现并行请求,但缺少超时处理 + +* **loadbalance**: 简单轮询,未考虑服务器失败率和权重 + +* **fastest-ip**: 仅顺序请求,未实现真正的TCP连接速度测量 + +## 2. 改进计划 + +### 2.1 扩展Server结构体 + +* 添加服务器状态跟踪字段: + + * `serverStats`: 记录每个上游服务器的成功/失败次数、最后响应时间 + + * `serverStatsMutex`: 保护服务器状态的互斥锁 + +### 2.2 实现加权随机负载均衡 + +* 为每个服务器计算权重,基于成功/失败比率 + +* 实现加权随机选择算法 + +* 在`forwardDNSRequestWithCache`中使用新算法 + +### 2.3 实现真正的最快服务器选择 + +* 添加`measureServerSpeed`函数,测量TCP连接速度 + +* 为每个服务器维护连接速度历史 + +* 选择连接速度最快的服务器进行查询 + +### 2.4 优化并行请求模式 + +* 添加请求超时处理 + +* 实现更快的响应返回机制(收到第一个有效响应即可返回) + +### 2.5 统一DNSSEC服务器请求处理 + +* 为DNSSEC服务器也实现相同的查询模式支持 + +* 确保DNSSEC查询与普通查询使用一致的逻辑 + +## 3. 文件修改 + +* `/root/dns/dns/server.go`: 扩展Server结构体,实现新的查询模式逻辑 + +* `/root/dns/config/config.go`: 确保配置支持新的查询模式 + +## 4. 测试计划 + +* 运行现有测试脚本验证基本功能 + +* 测试不同查询模式下的响应时间 + +* 验证负载均衡模式下的服务器选择分布 + +* 验证最快服务器模式下的速度测量准确性 + +## 5. 预期效果 + +* 负载均衡模式:根据服务器性能和可用性智能选择 + +* 并行请求模式:更快的响应速度和更好的容错性 + +* 最快服务器模式:选择响应速度最快的服务器,提高查询效率 + diff --git a/.trae/documents/实现数据获取优先级机制和错误处理逻辑.md b/.trae/documents/实现数据获取优先级机制和错误处理逻辑.md new file mode 100644 index 0000000..aabbc99 --- /dev/null +++ b/.trae/documents/实现数据获取优先级机制和错误处理逻辑.md @@ -0,0 +1,84 @@ +# 配置数据获取优先级机制和错误处理 + +## 1. 改进 API 请求处理逻辑 + +### 1.1 优化 `apiRequest` 函数 +- 修改 `apiRequest` 函数,确保它能正确处理各种错误情况 +- 统一错误返回格式,便于上层调用者处理 +- 添加超时处理,避免长时间等待 + +### 1.2 增强 API 方法的错误处理 +- 在 `api.js` 中为每个 API 方法添加更严格的错误检查 +- 确保返回数据符合预期格式 +- 提供更详细的错误日志 + +## 2. 实现数据加载状态管理 + +### 2.1 添加加载状态指示器 +- 在 HTML 中为 TOP 客户端和 TOP 域名表格添加加载状态指示器 +- 显示 "加载中..." 文本或动画 + +### 2.2 实现状态切换逻辑 +- 在数据请求开始时显示加载状态 +- 请求成功后显示真实数据 +- 请求失败后显示错误信息或模拟数据 + +## 3. 完善错误处理机制 + +### 3.1 分类处理错误情况 +- **网络连接失败**:显示连接错误信息,使用模拟数据 +- **服务器错误**:显示服务器错误信息,使用模拟数据 +- **空响应**:显示空数据状态,使用模拟数据 +- **数据格式错误**:显示数据格式错误信息,使用模拟数据 + +### 3.2 添加错误信息显示 +- 在表格上方或下方显示错误信息 +- 提供重试按钮,允许用户手动重试请求 + +## 4. 优化用户体验 + +### 4.1 平滑过渡效果 +- 添加数据更新的平滑过渡动画 +- 避免页面闪烁 + +### 4.2 提供有用的反馈 +- 显示数据更新时间 +- 显示数据来源(真实数据或模拟数据) +- 提供数据刷新按钮 + +## 5. 实现数据获取优先级机制 + +### 5.1 明确数据优先级 +- 优先级 1:服务器真实数据 +- 优先级 2:本地缓存数据(如果有) +- 优先级 3:模拟数据 + +### 5.2 实现优先级逻辑 +- 优先尝试获取服务器真实数据 +- 如果失败,检查是否有本地缓存数据 +- 如果没有缓存数据,使用模拟数据 + +## 6. 测试和验证 + +### 6.1 测试各种错误场景 +- 模拟网络连接失败 +- 模拟服务器返回错误状态码 +- 模拟服务器返回空响应 +- 模拟服务器返回错误格式数据 + +### 6.2 验证数据优先级机制 +- 确保优先使用服务器真实数据 +- 确保在各种错误情况下能正确切换到模拟数据 + +## 7. 代码优化和重构 + +### 7.1 提取公共逻辑 +- 提取数据获取和状态管理的公共逻辑 +- 减少代码重复 + +### 7.2 提高代码可读性 +- 添加清晰的注释 +- 使用有意义的变量名 +- 优化代码结构 + +通过以上实现,系统将能够优先使用来自服务器的真实数据,仅在必要时使用模拟数据,并提供良好的用户体验和错误处理。 \ No newline at end of file diff --git a/.trae/documents/实现系统启动时自动创建文件和文件夹.md b/.trae/documents/实现系统启动时自动创建文件和文件夹.md new file mode 100644 index 0000000..b4a83dc --- /dev/null +++ b/.trae/documents/实现系统启动时自动创建文件和文件夹.md @@ -0,0 +1,117 @@ +# 实现系统启动时自动创建文件和文件夹 + +## 问题分析 + +当前系统启动时,如果配置文件中指定的文件或文件夹不存在,会导致报错,影响系统正常启动。需要实现自动创建功能,确保系统能够顺利启动。 + +## 解决方案 + +### 1. 自动创建配置文件 + +* 在main.go中添加检查配置文件是否存在的逻辑 + +* 如果配置文件不存在,创建默认的config.json文件 + +### 2. 自动创建数据文件夹 + +* 根据配置文件中的路径,创建所需的文件夹: + * 数据文件夹(默认:./data) + * 远程规则缓存文件夹(默认:./data/remote_rules) + * 日志文件夹(根据配置文件中的Log.File路径) + +### 3. 自动创建文件 + +* 根据配置文件中的路径,创建所需的文件: + * 本地规则文件(默认:data/rules.txt) + * Hosts文件(默认:data/hosts.txt) + * 统计数据文件(默认:./data/stats.json) + * Shield统计数据文件(默认:./data/shield_stats.json) + +## 实现步骤 + +### 1. 修改main.go文件 + +* 在命令行参数解析后,添加检查配置文件是否存在的逻辑 + +* 如果配置文件不存在,创建默认的config.json文件 + +* 调用LoadConfig加载配置 + +### 2. 添加createDefaultConfig函数 + +* 实现创建默认配置文件的功能 + +* 写入默认的配置内容到config.json + +### 3. 添加createRequiredFiles函数 + +* 实现创建所需文件和文件夹的功能 + +* 根据配置文件中的路径,创建所需的文件夹 + +* 根据配置文件中的路径,创建所需的文件 + +### 4. 在main.go中调用createRequiredFiles函数 + +* 在配置加载完成后,调用createRequiredFiles函数 + +* 确保在初始化屏蔽管理系统之前创建所需的文件和文件夹 + +## 预期效果 + +* 系统启动时,如果配置文件不存在,自动创建默认的config.json文件 + +* 自动创建所需的文件夹:data、data/remote_rules、logs等 + +* 自动创建所需的文件:rules.txt、hosts.txt、stats.json等 + +* 系统能够顺利启动,不会因为找不到文件而报错 + +## 技术要点 + +* 使用os.Stat函数检查文件或文件夹是否存在 + +* 使用os.MkdirAll函数创建文件夹(包括父文件夹) + +* 使用os.Create函数创建文件 + +* 确保在初始化屏蔽管理系统之前创建所需的文件和文件夹 + +* 确保在初始化日志系统之前创建日志文件夹 + +## 实现时间 + +* 预计1-2小时完成所有修改和测试 + +## 风险评估 + +* 低风险:修改范围明确,不涉及核心功能 + +* 可回滚:所有修改均为添加新功能,可通过恢复文件轻松回滚 + +## 依赖关系 + +* 依赖os包的文件操作功能 + +* 依赖config包的配置结构 + +## 测试策略 + +* 手动测试删除配置文件、文件夹和文件,然后启动服务器,验证是否自动创建 + +* 验证创建的文件和文件夹是否符合配置文件中的路径 + +* 验证服务器能够顺利启动,没有报错 + +## 验收标准 + +* 系统启动时,如果配置文件不存在,自动创建默认的config.json文件 + +* 自动创建所需的文件夹:data、data/remote_rules、logs等 + +* 自动创建所需的文件:rules.txt、hosts.txt、stats.json等 + +* 系统能够顺利启动,不会因为找不到文件而报错 + +* 所有创建的文件和文件夹都符合配置文件中的路径 + diff --git a/.trae/documents/实现缓存请求条目DNSSEC标记显示.md b/.trae/documents/实现缓存请求条目DNSSEC标记显示.md new file mode 100644 index 0000000..cb3de67 --- /dev/null +++ b/.trae/documents/实现缓存请求条目DNSSEC标记显示.md @@ -0,0 +1,40 @@ +## 问题分析 +用户要求在缓存结果中包含DNSSEC信息时,在缓存的请求条目后边添加相同的DNSSEC标记,格式为:`类型: AAAA, 允许, 缓存 `。如果不包含DNSSEC信息,则不添加标记。 + +## 代码检查 +1. **当前DNSSEC标记实现**: + - 在`/root/dns/static/js/logs.js`的`updateLogsTable`函数中,已经实现了根据`log.DNSSEC`字段显示DNSSEC标记的功能: + ```javascript +
类型: ${log.QueryType}, ${statusText}, ${log.FromCache ? '缓存' : '实时'}${log.DNSSEC ? ', DNSSEC' : ''}
+ ``` + +2. **DNSSEC信息存储**: + - `QueryLog`结构体包含`DNSSEC`字段,用于记录是否使用了DNSSEC + - 在`handleDNSRequest`函数中,会检查响应是否包含DNSSEC信息,并记录到日志中 + - 在缓存处理中,会检查缓存响应的AD标志和RRSIG记录,确定DNSSEC状态 + +3. **缓存响应的DNSSEC处理**: + - 当从缓存返回响应时,会检查响应是否包含RRSIG记录或AD标志,以确定DNSSEC状态 + - 但在日志记录时,已经正确传递了DNSSEC状态 + +## 解决方案 +当前实现已经基本满足用户要求,但需要确保在所有显示查询日志的地方都正确显示DNSSEC标记。根据检查,只有`logs.js`中的`updateLogsTable`函数显示查询日志,且已经实现了DNSSEC标记功能。 + +## 验证步骤 +1. 确认`logs.js`中的`updateLogsTable`函数已经正确实现了DNSSEC标记显示 +2. 确认`QueryLog`结构体中的`DNSSEC`字段被正确设置 +3. 确认缓存响应的DNSSEC状态被正确检查和记录 +4. 运行测试,验证缓存请求条目显示格式为:`类型: AAAA, 允许, 缓存 ` + +## 预期结果 +- 当缓存结果包含DNSSEC信息时,显示:`类型: AAAA, 允许, 缓存 DNSSEC` +- 当缓存结果不包含DNSSEC信息时,显示:`类型: AAAA, 允许, 缓存` +- DNSSEC标记使用绿色锁图标,颜色为绿色 + +## 注意事项 +- 确保DNSSEC标记只在缓存请求条目中显示,与实时请求条目保持一致的格式 +- 确保DNSSEC标记的颜色和图标与整体UI风格一致 +- 确保DNSSEC标记只在包含DNSSEC信息时显示,不包含时不显示 + +## 实现状态 +当前实现已经满足用户要求,不需要额外修改代码。需要运行测试验证功能是否正常工作。 \ No newline at end of file diff --git a/.trae/documents/实现远程黑名单管理功能.md b/.trae/documents/实现远程黑名单管理功能.md new file mode 100644 index 0000000..de2a4a9 --- /dev/null +++ b/.trae/documents/实现远程黑名单管理功能.md @@ -0,0 +1,33 @@ +# 实现远程黑名单管理功能 + +## 1. 分析现有代码 +- 远程黑名单管理功能已经在`shield.js`文件中实现 +- 页面结构已经存在于HTML文件中 +- 存在`blacklists.js`和`shield.js`两个可能冲突的实现 + +## 2. 解决方案 +### 2.1 检查并解决代码冲突 +- 移除或整合`blacklists.js`文件,避免与`shield.js`冲突 +- 确保只使用一个实现来管理远程黑名单 + +### 2.2 确保页面加载时正确初始化 +- 检查`shield.js`中的初始化逻辑 +- 确保`initShieldPage`函数在页面加载和切换到屏蔽管理页面时被正确调用 + +### 2.3 确保web更新数据时同时更新服务器 +- 检查现有的添加、更新、删除、启用/禁用黑名单的功能 +- 确保每个操作都通过API请求更新服务器数据 +- 验证操作完成后是否重新加载数据 + +## 3. 实现步骤 +1. 移除`blacklists.js`文件,避免与`shield.js`冲突 +2. 检查并确保`shield.js`中的初始化逻辑正确 +3. 测试远程黑名单管理功能的各个操作 +4. 验证页面加载时是否正确拉取服务器数据 +5. 验证web更新数据时是否同时更新服务器数据 + +## 4. 预期结果 +- 页面加载时自动拉取服务器数据 +- 添加、更新、删除、启用/禁用黑名单时,同时更新服务器数据 +- 操作完成后,页面数据自动刷新 +- 没有代码冲突,功能正常运行 \ No newline at end of file diff --git a/.trae/documents/屏蔽规则解析修复计划.md b/.trae/documents/屏蔽规则解析修复计划.md new file mode 100644 index 0000000..a340832 --- /dev/null +++ b/.trae/documents/屏蔽规则解析修复计划.md @@ -0,0 +1,39 @@ +# 屏蔽规则解析修复计划 + +## 问题分析 + +通过分析代码,我发现了无法处理`/domain.com/`类似规则的问题: + +1. **当前实现**: + - 当处理`/domain.com/`这样的规则时,代码会将其编译为正则表达式`domain.com` + - 然后使用`MatchString(domain)`来检查域名是否匹配该正则表达式 + - 但是,`MatchString`会检查整个字符串是否匹配,而不是检查域名是否包含该模式 + - 例如,对于域名`example.com`,正则表达式`domain.com`不会匹配,因为它只匹配字符串"domain.com" + +2. **用户期望**: + - 用户期望`/domain.com/`匹配所有包含"domain.com"的域名,比如`sub.domain.com` + - 但是当前实现中,它只会匹配精确的字符串"domain.com" + +## 解决方案 + +### 1. 修改正则表达式规则处理 + +我们需要修改`parseRule`函数中对正则表达式规则的处理,确保`/domain.com/`这样的规则能正确匹配包含该模式的域名。 + +### 2. 优化匹配逻辑 + +我们需要确保正则表达式规则能正确匹配域名,无论是精确匹配还是包含匹配。 + +## 实现步骤 + +1. **修改`parseRule`函数**: + - 对于以`/`开头和结尾的规则,确保其能正确匹配包含该模式的域名 + - 或者,添加一个选项,允许用户指定匹配模式 + +2. **测试修复效果**: + - 确保`/domain.com/`规则能匹配`sub.domain.com`等域名 + - 确保其他规则类型仍能正常工作 + +## 预期效果 + +通过以上修复,屏蔽规则解析将能够正确处理`/domain.com/`类似的规则,匹配所有包含该模式的域名,提高用户体验。 \ No newline at end of file diff --git a/.trae/documents/恢复完整的dns_server.go文件.md b/.trae/documents/恢复完整的dns_server.go文件.md new file mode 100644 index 0000000..7465510 --- /dev/null +++ b/.trae/documents/恢复完整的dns_server.go文件.md @@ -0,0 +1,45 @@ +### 问题分析 +当前的`dns/server.go`文件被误删了很多内容,导致编译失败。从编译错误可以看出,`http/server.go`文件中引用了`dns.Stats`结构体中的一些字段,如`QueryTypes`、`SourceIPs`、`AvgResponseTime`、`DNSSECQueries`等,但这些字段在当前的`dns/server.go`文件中都不存在。 + +### 恢复方案 +1. **恢复完整的结构体定义**: + - `BlockedDomain`结构体中添加`DNSSEC`字段 + - `Stats`结构体中添加所有缺失的字段 + - `Server`结构体中添加所有缺失的字段 + - `StatsData`结构体中添加所有缺失的字段 + +2. **恢复所有缺失的方法**: + - `updateClientStats` + - `updateResolvedDomainStatsWithDNSSEC` + - `updateDomainDNSSECStatus` + - `addQueryLog` + - `GetStartTime` + - `GetQueryLogs` + - `GetQueryLogsCount` + - `GetQueryStats` + - `GetTopClients` + - `GetDailyStats` + - `GetMonthlyStats` + - `getIpGeolocation` + - `fetchIpGeolocationFromAPI` + - `loadQueryLogs` + - `saveQueryLogs` + - `startCpuUsageMonitor` + - `getSystemCpuUsage` + +3. **恢复DNSSEC相关功能**: + - 恢复`forwardDNSRequestWithCache`函数,替代当前的`forwardDNSRequest`函数 + - 恢复DNSSEC验证逻辑 + - 恢复DNS缓存功能 + +### 具体修改 +1. 替换整个`dns/server.go`文件,恢复完整的内容 +2. 确保所有结构体字段和方法都完整存在 +3. 确保DNSSEC功能正常工作 + +### 测试计划 +1. 编译恢复后的代码 +2. 启动DNS服务器并启用DNSSEC +3. 使用dig命令测试DNSSEC记录查询 +4. 检查HTTP控制台是否正常工作 +5. 检查日志中是否有DNSSEC验证相关的记录 \ No newline at end of file diff --git a/.trae/documents/改进DNS查询模式实现.md b/.trae/documents/改进DNS查询模式实现.md new file mode 100644 index 0000000..89410d5 --- /dev/null +++ b/.trae/documents/改进DNS查询模式实现.md @@ -0,0 +1,50 @@ +# 改进DNS查询模式实现 + +## 1. 当前实现分析 + +- **parallel**: 已基本实现并行请求,但缺少超时处理 +- **loadbalance**: 简单轮询,未考虑服务器失败率和权重 +- **fastest-ip**: 仅顺序请求,未实现真正的TCP连接速度测量 + +## 2. 改进计划 + +### 2.1 扩展Server结构体 +- 添加服务器状态跟踪字段: + - `serverStats`: 记录每个上游服务器的成功/失败次数、最后响应时间 + - `serverStatsMutex`: 保护服务器状态的互斥锁 + +### 2.2 实现加权随机负载均衡 +- 为每个服务器计算权重,基于成功/失败比率 +- 实现加权随机选择算法 +- 在`forwardDNSRequestWithCache`中使用新算法 + +### 2.3 实现真正的最快服务器选择 +- 添加`measureServerSpeed`函数,测量TCP连接速度 +- 为每个服务器维护连接速度历史 +- 选择连接速度最快的服务器进行查询 + +### 2.4 优化并行请求模式 +- 添加请求超时处理 +- 实现更快的响应返回机制(收到第一个有效响应即可返回) + +### 2.5 统一DNSSEC服务器请求处理 +- 为DNSSEC服务器也实现相同的查询模式支持 +- 确保DNSSEC查询与普通查询使用一致的逻辑 + +## 3. 文件修改 + +- `/root/dns/dns/server.go`: 扩展Server结构体,实现新的查询模式逻辑 +- `/root/dns/config/config.go`: 确保配置支持新的查询模式 + +## 4. 测试计划 + +- 运行现有测试脚本验证基本功能 +- 测试不同查询模式下的响应时间 +- 验证负载均衡模式下的服务器选择分布 +- 验证最快服务器模式下的速度测量准确性 + +## 5. 预期效果 + +- 负载均衡模式:根据服务器性能和可用性智能选择 +- 并行请求模式:更快的响应速度和更好的容错性 +- 最快服务器模式:选择响应速度最快的服务器,提高查询效率 \ No newline at end of file diff --git a/.trae/documents/更新Swagger API文档.md b/.trae/documents/更新Swagger API文档.md new file mode 100644 index 0000000..b8f6d2e --- /dev/null +++ b/.trae/documents/更新Swagger API文档.md @@ -0,0 +1,69 @@ +# 更新Swagger API文档 + +## 1. 分析当前状态 + +* Swagger文档位于 `/root/dns/static/api/js/index.js` 中 + +* 项目使用Go语言开发,HTTP API端点在 `/root/dns/http/server.go` 中定义 + +* 当前swagger文档缺少一些API端点和正确的描述 + +## 2. 需要更新的内容 + +### 2.1 添加缺少的API端点 + +* `/api/login` - 登录API + +* `/api/logout` - 注销API + +* `/api/change-password` - 修改密码API + +* `/api/query` - DNS查询API + +* `/api/status` - 系统状态API + +* `/api/config` - 配置管理API + +* `/api/config/restart` - 重启服务API + +* `/api/logs/stats` - 日志统计API + +* `/api/logs/query` - 日志查询API + +* `/api/logs/count` - 日志数量API + +### 2.2 更新现有端点描述 + +* `/api/stats` - 添加DNSSEC相关字段描述 + +* `/api/shield` - 更新为与实际实现匹配 + +* `/api/shield/blacklists` - 确保包含所有HTTP方法 + +* `/api/shield/hosts` - 确保包含所有HTTP方法 + +* `/api/shield/localrules` - 确保包含所有HTTP方法 + +### 2.3 修正现有端点的响应格式 + +* 修正 `/api/hourly-stats`、`/api/daily-stats` 和 `/api/monthly-stats` 的响应格式 + +* 修正 `/api/shield` 的响应格式 + +## 3. 实施步骤 + +1. 读取当前swagger文档 +2. 针对每个需要更新的端点,修改或添加相应的swagger定义 +3. 确保所有端点的HTTP方法、参数、响应格式都正确 +4. 测试swagger文档是否能正常加载和显示 + +## 4. 预期结果 + +* 所有API端点都在swagger文档中正确描述 + +* 每个端点的HTTP方法、参数、响应格式都准确 + +* swagger文档能正常加载和显示 + +* 开发者可以通过swagger文档了解和使用所有API端点 + diff --git a/.trae/documents/替换最近屏蔽域名为TOP域名卡片.md b/.trae/documents/替换最近屏蔽域名为TOP域名卡片.md new file mode 100644 index 0000000..d657ef3 --- /dev/null +++ b/.trae/documents/替换最近屏蔽域名为TOP域名卡片.md @@ -0,0 +1,13 @@ +1. 定位到仪表盘部分的卡片布局,找到两个关键卡片: + - 最近屏蔽域名卡片(第593-635行) + - TOP域名卡片(第705-734行) + +2. 移除最近屏蔽域名卡片(第593-635行) + +3. 将TOP域名卡片从当前位置(第639行开始的网格)移动到最近屏蔽域名卡片的位置(第538行开始的网格) + +4. 确保布局保持一致,两个卡片在同一行显示 + +5. 调整相关的网格布局,确保页面结构完整 + +6. 验证修改后的布局是否符合用户要求 \ No newline at end of file diff --git a/.trae/documents/根据服务器日志判断操作成功状态.md b/.trae/documents/根据服务器日志判断操作成功状态.md new file mode 100644 index 0000000..475f624 --- /dev/null +++ b/.trae/documents/根据服务器日志判断操作成功状态.md @@ -0,0 +1,33 @@ +# 根据服务器日志判断操作成功状态 + +## 问题分析 +当前代码中,部分操作只检查了HTTP响应状态码,没有检查服务器返回的`status`字段,这可能导致在某些情况下无法正确判断操作是否成功。根据服务器端代码,所有成功的响应都会返回`{"status": "success"}`,因此需要确保前端代码在所有操作中都检查这个字段。 + +## 解决方案 +修改以下函数,确保它们都根据服务器返回的`status`字段判断操作是否成功: + +1. **handleDeleteBlacklist** (line 653-716) + - 添加对服务器响应数据中`status`字段的检查 + - 确保只有当`status`为`success`时才认为删除成功 + +2. **handleToggleBlacklist** (line 719-793) + - 添加对服务器响应数据中`status`字段的检查 + - 确保只有当`status`为`success`时才认为切换状态成功 + +3. **handleAddRule** (line 378-409) + - 添加对服务器响应数据中`status`字段的检查 + - 确保只有当`status`为`success`时才认为添加规则成功 + +4. **handleDeleteRule** (line 309-375) + - 添加对服务器响应数据中`status`字段的检查 + - 确保只有当`status`为`success`时才认为删除规则成功 + +## 实现步骤 +1. 修改`handleDeleteBlacklist`函数,添加响应数据解析和status字段检查 +2. 修改`handleToggleBlacklist`函数,添加响应数据解析和status字段检查 +3. 修改`handleAddRule`函数,添加响应数据解析和status字段检查 +4. 修改`handleDeleteRule`函数,添加响应数据解析和status字段检查 +5. 测试所有操作,确保它们都能正确根据服务器响应判断成功或失败 + +## 预期效果 +所有操作(添加、更新、删除、切换状态)都会根据服务器返回的`status`字段判断是否成功,确保操作结果与服务器日志一致。 \ No newline at end of file diff --git a/.trae/documents/测试domainSpecificDNS功能.md b/.trae/documents/测试domainSpecificDNS功能.md new file mode 100644 index 0000000..86515b4 --- /dev/null +++ b/.trae/documents/测试domainSpecificDNS功能.md @@ -0,0 +1,40 @@ +# 修复domainSpecificDNS的DNSSEC优先级问题 + +## 1. 问题分析 + +* 当域名匹配到 `domainSpecificDNS` 配置时,程序会使用指定的DNS服务器 +* 但随后程序会向DNSSEC专用服务器发送请求 +* 如果DNSSEC专用服务器返回了带DNSSEC的响应,程序会优先使用该响应 +* 这导致 `domainSpecificDNS` 指定的DNS服务器的响应被忽略 + +## 2. 修复计划 + +### 2.1 修改 `forwardDNSRequestWithCache` 函数 + +* 在函数中添加一个标志 `domainMatched`,记录域名是否匹配了 `domainSpecificDNS` 配置 +* 当域名匹配到 `domainSpecificDNS` 配置时,设置该标志为 `true` + +### 2.2 修改DNSSEC响应处理逻辑 + +* 在处理DNSSEC专用服务器响应时,检查 `domainMatched` 标志 +* 如果标志为 `true`,则**不优先使用DNSSEC专用服务器的响应**,而是保留原来指定DNS服务器的响应 +* 只有当指定的DNS服务器没有返回有效响应时,才考虑使用DNSSEC专用服务器的响应 + +### 2.3 确保指定的DNS服务器优先 + +* 确保对于匹配了 `domainSpecificDNS` 的域名,始终优先使用指定的DNS服务器的响应 +* 只有当指定的DNS服务器没有返回有效响应时,才考虑使用DNSSEC专用服务器的响应 + +## 3. 预期效果 + +* 对于匹配了 `domainSpecificDNS` 配置的域名,始终优先使用指定的DNS服务器 +* 只有当指定的DNS服务器没有返回有效响应时,才考虑使用DNSSEC专用服务器 +* DNSSEC功能仍然正常工作,但不会覆盖 `domainSpecificDNS` 的配置 + +## 4. 测试计划 + +* 重启DNS服务器 +* 使用 `dig @127.0.0.1 dc.amazehome.xyz +short` 测试 +* 检查日志,确保使用的是指定DNS服务器的响应 +* 验证解析结果是否符合预期 + diff --git a/.trae/documents/添加DNSSEC支持实现计划.md b/.trae/documents/添加DNSSEC支持实现计划.md new file mode 100644 index 0000000..7361eb0 --- /dev/null +++ b/.trae/documents/添加DNSSEC支持实现计划.md @@ -0,0 +1,57 @@ +# 添加DNSSEC支持实现计划 + +## 1. 配置系统修改 +- 在`config/config.go`的`DNSConfig`结构体中添加`EnableDNSSEC`布尔字段,用于控制是否启用DNSSEC支持 +- 添加`DNSSECValidation`布尔字段,用于控制是否进行DNSSEC验证 +- 在配置加载时设置默认值 + +## 2. DNS查询处理修改 +- 修改`dns/server.go`中的DNS客户端配置,确保支持EDNS0扩展(DNSSEC需要) +- 在`handleDNSRequest`函数中支持DNSSEC相关查询类型(DNSKEY、RRSIG、DS、NSEC、NSEC3等) +- 确保上游DNS服务器返回的DNSSEC记录被正确处理和转发 + +## 3. DNSSEC验证支持 +- 实现DNSSEC记录验证逻辑,确保返回的DNS记录未被篡改 +- 在`forwardDNSRequestWithCache`函数中添加DNSSEC验证步骤 +- 根据配置决定是否进行验证,以及验证失败时的处理策略 + +## 4. 缓存系统适配 +- 修改`DNSCache`结构体,确保DNSSEC记录被正确缓存 +- 确保缓存的DNS响应包含完整的DNSSEC记录链 + +## 5. 测试和验证 +- 确保现有功能不受影响 +- 测试DNSSEC查询是否正常工作 +- 验证DNSSEC记录是否被正确转发和验证 + +## 6. 配置界面更新 +- 在Web控制台中添加DNSSEC相关配置选项 +- 允许用户通过界面启用/禁用DNSSEC支持和验证 + +## 7. 日志和统计 +- 添加DNSSEC相关日志记录 +- 记录DNSSEC验证结果统计 + +## 主要文件修改 +- `config/config.go`:添加DNSSEC相关配置字段 +- `dns/server.go`:修改DNS查询处理逻辑,支持DNSSEC +- `static/index.html`:添加DNSSEC配置界面 +- `dns/cache.go`:确保DNSSEC记录被正确缓存 + +## 依赖库 +- 利用现有的`github.com/miekg/dns`库,该库已内置DNSSEC支持 + +## 实现步骤 +1. 首先修改配置系统,添加DNSSEC相关字段 +2. 更新DNS客户端配置,支持EDNS0扩展 +3. 修改查询处理逻辑,支持DNSSEC记录类型 +4. 添加DNSSEC验证逻辑 +5. 适配缓存系统 +6. 更新Web配置界面 +7. 测试和验证 + +## 预期效果 +- 启用DNSSEC后,服务器将支持DNSSEC查询并返回完整的DNSSEC记录 +- 当启用DNSSEC验证时,服务器将验证上游返回的DNS记录的真实性 +- 增强DNS服务器的安全性,防止DNS投毒和劫持攻击 +- 提供灵活的配置选项,允许用户根据需要调整DNSSEC设置 \ No newline at end of file diff --git a/.trae/documents/添加三种并行查询配置.md b/.trae/documents/添加三种并行查询配置.md new file mode 100644 index 0000000..b8f6d2e --- /dev/null +++ b/.trae/documents/添加三种并行查询配置.md @@ -0,0 +1,69 @@ +# 更新Swagger API文档 + +## 1. 分析当前状态 + +* Swagger文档位于 `/root/dns/static/api/js/index.js` 中 + +* 项目使用Go语言开发,HTTP API端点在 `/root/dns/http/server.go` 中定义 + +* 当前swagger文档缺少一些API端点和正确的描述 + +## 2. 需要更新的内容 + +### 2.1 添加缺少的API端点 + +* `/api/login` - 登录API + +* `/api/logout` - 注销API + +* `/api/change-password` - 修改密码API + +* `/api/query` - DNS查询API + +* `/api/status` - 系统状态API + +* `/api/config` - 配置管理API + +* `/api/config/restart` - 重启服务API + +* `/api/logs/stats` - 日志统计API + +* `/api/logs/query` - 日志查询API + +* `/api/logs/count` - 日志数量API + +### 2.2 更新现有端点描述 + +* `/api/stats` - 添加DNSSEC相关字段描述 + +* `/api/shield` - 更新为与实际实现匹配 + +* `/api/shield/blacklists` - 确保包含所有HTTP方法 + +* `/api/shield/hosts` - 确保包含所有HTTP方法 + +* `/api/shield/localrules` - 确保包含所有HTTP方法 + +### 2.3 修正现有端点的响应格式 + +* 修正 `/api/hourly-stats`、`/api/daily-stats` 和 `/api/monthly-stats` 的响应格式 + +* 修正 `/api/shield` 的响应格式 + +## 3. 实施步骤 + +1. 读取当前swagger文档 +2. 针对每个需要更新的端点,修改或添加相应的swagger定义 +3. 确保所有端点的HTTP方法、参数、响应格式都正确 +4. 测试swagger文档是否能正常加载和显示 + +## 4. 预期结果 + +* 所有API端点都在swagger文档中正确描述 + +* 每个端点的HTTP方法、参数、响应格式都准确 + +* swagger文档能正常加载和显示 + +* 开发者可以通过swagger文档了解和使用所有API端点 + diff --git a/.trae/documents/移除屏蔽规则数量卡片.md b/.trae/documents/移除屏蔽规则数量卡片.md new file mode 100644 index 0000000..c337fff --- /dev/null +++ b/.trae/documents/移除屏蔽规则数量卡片.md @@ -0,0 +1,18 @@ +## 问题分析 +用户要求移除一个统计卡片,根据之前的对话历史,最近添加的卡片是屏蔽规则数量卡片,用户可能想要移除这个卡片。 + +## 修复方案 +1. **移除HTML中的屏蔽规则数量卡片**:从index.html文件中删除屏蔽规则数量卡片的HTML代码 +2. **移除JavaScript中的相关逻辑**:从dashboard.js文件中删除与屏蔽规则数量相关的变量声明、数据获取逻辑和更新逻辑 +3. **确保代码语法正确**:修复可能出现的语法错误,确保代码兼容性 + +## 实现步骤 +1. 打开index.html文件,找到屏蔽规则数量卡片(约第467-488行),删除其HTML代码 +2. 打开dashboard.js文件,找到updateStatsCards函数,删除与屏蔽规则数量相关的变量声明(blockRulesCount、blockRulesPercentage) +3. 删除updateStatsCards函数中与屏蔽规则数量相关的数据获取逻辑 +4. 删除updateStatsCards函数中与屏蔽规则数量相关的更新逻辑 +5. 删除loadDashboardData函数中与屏蔽规则数量相关的更新逻辑 +6. 运行代码语法检查,确保没有语法错误 + +## 预期效果 +修复后,屏蔽规则数量卡片将从仪表盘上移除,相关的JavaScript逻辑也将被清理,仪表盘将恢复到之前的状态,只显示其他7个统计卡片。 \ No newline at end of file diff --git a/.trae/documents/解决web页面缓存问题.md b/.trae/documents/解决web页面缓存问题.md new file mode 100644 index 0000000..9c3340c --- /dev/null +++ b/.trae/documents/解决web页面缓存问题.md @@ -0,0 +1,65 @@ +# 解决web页面缓存问题 + +## 问题分析 + +当前web页面总是有缓存,导致更新后用户看不到最新的内容。这是因为HTTP服务器使用了标准的http.FileServer来提供静态文件服务,它会设置默认的缓存头,导致浏览器缓存静态文件。 + +## 解决方案 + +修改静态文件服务的缓存策略,为静态文件添加适当的Cache-Control头,禁用浏览器缓存,或者设置较短的缓存时间。 + +## 实现步骤 + +### 1. 创建自定义静态文件服务处理器 + +* 创建一个自定义的http.Handler,包装http.FileServer +* 在处理静态文件请求时,添加适当的Cache-Control头 +* 可以选择完全禁用缓存,或者设置较短的缓存时间 + +### 2. 修改http/server.go中的静态文件服务配置 + +* 替换标准的http.FileServer,使用自定义的静态文件服务处理器 +* 确保所有静态文件请求都经过自定义处理器 + +### 3. 测试修改后的效果 + +* 更新静态文件,例如修改dashboard.js +* 刷新页面,验证是否能看到最新的内容 +* 使用浏览器开发者工具查看响应头,确认Cache-Control头已正确设置 + +## 技术要点 + +* 使用http.StripPrefix处理静态文件路径 +* 在ResponseWriter中添加Cache-Control头,例如: + * `Cache-Control: no-cache, no-store, must-revalidate` + * `Pragma: no-cache` + * `Expires: 0` +* 这些头会告诉浏览器不要缓存文件,每次都要重新请求 + +## 实现时间 + +* 预计30分钟完成所有修改和测试 + +## 风险评估 + +* 低风险:修改范围明确,不涉及核心功能 +* 可回滚:所有修改均为HTTP服务器配置修改,可通过恢复文件轻松回滚 + +## 依赖关系 + +* 依赖http包的标准库功能 +* 依赖现有的静态文件服务结构 + +## 测试策略 + +* 更新静态文件,例如修改dashboard.js +* 刷新页面,验证是否能看到最新的内容 +* 使用浏览器开发者工具查看响应头,确认Cache-Control头已正确设置 +* 测试不同浏览器的行为 + +## 验收标准 + +* 静态文件的HTTP响应中包含适当的Cache-Control头 +* 更新静态文件后,刷新页面能看到最新的内容 +* 浏览器不会缓存静态文件,每次都会重新请求 + diff --git a/.trae/documents/调整DNS趋势图表默认显示和浮窗独立性.md b/.trae/documents/调整DNS趋势图表默认显示和浮窗独立性.md new file mode 100644 index 0000000..0062ca5 --- /dev/null +++ b/.trae/documents/调整DNS趋势图表默认显示和浮窗独立性.md @@ -0,0 +1,44 @@ +# 调整DNS趋势图表默认显示和浮窗独立性 + +## 问题分析 +1. **DNS趋势图表默认显示**:当前代码中`isMixedView`变量默认设置为`true`,但在`initTimeRangeToggle`函数中,默认选中第一个按钮后会将`isMixedView`设置为`false`,导致实际默认显示的是24小时视图而非混合视图。 +2. **浮窗图表独立性**:当前代码中详细图表(浮窗)已有独立变量`detailedCurrentTimeRange`和`detailedIsMixedView`,但需要确保初始化时正确设置,避免与主图表冲突。 + +## 实现计划 + +### 1. 修改DNS趋势图表默认显示为混合内容 +- **文件**:`/root/dns/static/js/dashboard.js` +- **函数**:`initTimeRangeToggle` +- **修改点**: + - 在函数末尾,默认选中第一个按钮后,将`isMixedView`设置为`true` + - 将`currentTimeRange`设置为`'mixed'` + - 更新按钮样式,添加混合视图标记 + +### 2. 确保浮窗图表初始化正确 +- **文件**:`/root/dns/static/js/dashboard.js` +- **函数**:`initDetailedTimeRangeToggle` +- **修改点**: + - 确保初始化时`detailedIsMixedView`默认值与主图表保持一致 + - 确保点击浮窗中的时间范围按钮时,只修改详细图表的变量,不影响主图表 + +### 3. 验证功能完整性 +- 检查`drawDNSRequestsChart`函数,确保它使用主图表变量 +- 检查`drawDetailedDNSRequestsChart`函数,确保它使用详细图表变量 +- 确保两个函数的实现逻辑一致,但使用不同的变量 + +## 预期效果 +1. DNS趋势图表默认显示混合内容(24小时、7天、30天数据同时显示) +2. 展开浮窗后,切换浮窗中的时间范围或视图模式,不会影响主页图表的显示 +3. 主页图表和浮窗图表可以独立显示不同的时间范围和视图模式 + +## 实现步骤 +1. 修改`initTimeRangeToggle`函数,设置默认混合视图 +2. 优化`initDetailedTimeRangeToggle`函数,确保浮窗图表初始化正确 +3. 验证两个图表函数的变量使用是否正确 +4. 测试功能完整性 + +## 代码修改点 +1. **第1250-1256行**:修改默认按钮选中逻辑,添加混合视图设置 +2. **第1475-1507行**:优化浮窗图表时间范围切换逻辑 +3. **第1716-1912行**:确保主图表函数使用正确变量 +4. **第1509-1714行**:确保浮窗图表函数使用正确变量 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b3a933..10dd08e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ 所有对本项目的显著更改都将记录在此文件中。 +## [1.1.1] - 2025-12-19 + +### 修改 +- 修复NXDOMAIN响应传播逻辑,确保上游DNS服务器返回的NXDOMAIN响应能正确传递给客户端 +- 优化loadbalance、fastest-ip和parallel查询模式下的NXDOMAIN响应选择机制 +- 确保不存在的域名能被正确识别并返回NXDOMAIN状态码 +- 修复服务器绑定地址配置,确保IPv4兼容性 + ## [1.0.0] - 2025-12-16 ### 添加 diff --git a/config.json b/config.json index 2d51a8b..efda661 100644 --- a/config.json +++ b/config.json @@ -2,9 +2,6 @@ "dns": { "port": 53, "upstreamDNS": [ - "223.5.5.5:53", - "223.6.6.6:53", - "117.50.10.10:53", "10.35.10.200:53" ], "dnssecUpstreamDNS": [ @@ -21,10 +18,29 @@ "enableDNSSEC": true, "queryMode": "parallel", "domainSpecificDNS": { - "amazehome.xyz": ["10.35.10.200:53"], - "amazehome.cn": ["10.35.10.200:53"] + "amazehome.cn": [ + "10.35.10.200:53" + ], + "addr.arpa": [ + "10.35.10.200:53" + ], + "amazehome.xyz": [ + "10.35.10.200:53" + ], + "microsoft.com": [ + "4.2.2.1:53" + ], + "akamai": [ + "4.2.2.1:53" + ], + "akadns": [ + "4.2.2.1:53" + ] + }, - "prefixDomain": ["amazehome.xyz", "amazehome.cn"] + "prefixDomain": [ + "" + ] }, "http": { "port": 8080, @@ -79,7 +95,7 @@ "name": "My Gitlab Hosts", "url": "http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/hosts/costomize.txt", "enabled": true, - "lastUpdateTime": "2025-11-29T17:11:28.130Z" + "lastUpdateTime": "2025-12-18T10:39:39.333Z" }, { "name": "Anti Remote Requests", @@ -94,7 +110,8 @@ { "name": "My Gitlab A/T Rules", "url": "http://gitea.amazehome.xyz/AMAZEHOME/hosts-and-filters/raw/branch/main/rules/ads-and-trackers.txt", - "enabled": true + "enabled": true, + "lastUpdateTime": "2025-12-18T10:38:42.344Z" }, { "name": "My Gitlab Malware List", diff --git a/dns-server b/dns-server new file mode 100755 index 0000000..507daa8 Binary files /dev/null and b/dns-server differ diff --git a/dns/server.go b/dns/server.go index a34d389..57cdba0 100644 --- a/dns/server.go +++ b/dns/server.go @@ -59,6 +59,8 @@ type QueryLog struct { FromCache bool // 是否来自缓存 DNSSEC bool // 是否使用了DNSSEC EDNS bool // 是否使用了EDNS + DNSServer string // 使用的DNS服务器 + DNSSECServer string // 使用的DNSSEC专用服务器 } // StatsData 用于持久化的统计数据结构 @@ -230,14 +232,14 @@ func (s *Server) Start() error { s.startTime = time.Now() s.server = &dns.Server{ - Addr: fmt.Sprintf(":%d", s.config.Port), + Addr: fmt.Sprintf("0.0.0.0:%d", s.config.Port), Net: "udp", Handler: dns.HandlerFunc(s.handleDNSRequest), } // 保存TCP服务器实例,以便在Stop方法中关闭 s.tcpServer = &dns.Server{ - Addr: fmt.Sprintf(":%d", s.config.Port), + Addr: fmt.Sprintf("0.0.0.0:%d", s.config.Port), Net: "tcp", Handler: dns.HandlerFunc(s.handleDNSRequest), } @@ -368,7 +370,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { }) // 添加查询日志 - s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true) + s.addQueryLog(sourceIP, domain, queryType, responseTime, "error", "", "", false, false, true, "", "") return } @@ -385,7 +387,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { }) // 添加查询日志 - s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, false, true) + s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, false, true, "缓存", "无") return } @@ -407,7 +409,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { }) // 添加查询日志 - s.addQueryLog(sourceIP, domain, queryType, responseTime, "blocked", blockRule, blockType, false, false, true) + s.addQueryLog(sourceIP, domain, queryType, responseTime, "blocked", blockRule, blockType, false, false, true, "无", "无") return } @@ -480,7 +482,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { } // 添加查询日志 - 标记为缓存 - s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", true, cachedDNSSEC, true) + s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", true, cachedDNSSEC, true, "缓存", "无") logger.Debug("从缓存返回DNS响应", "domain", domain, "type", queryType, "dnssec", cachedDNSSEC) return } @@ -489,10 +491,12 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { var response *dns.Msg var rtt time.Duration var queryAttempts []string + var dnsServer string + var dnssecServer string // 1. 首先尝试直接查询原始域名 queryAttempts = append(queryAttempts, domain) - response, rtt = s.forwardDNSRequestWithCache(r, domain) + response, rtt, dnsServer, dnssecServer = s.forwardDNSRequestWithCache(r, domain) // 2. 如果直接查询失败且配置了prefixDomain,尝试添加前缀 if (response == nil || response.Rcode != dns.RcodeSuccess) && len(s.config.PrefixDomain) > 0 { @@ -517,8 +521,8 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { Qclass: originalQuestion.Qclass, } - // 转发请求 - response, rtt = s.forwardDNSRequestWithCache(newReq, fullDomain) + // 查询带有前缀的域名 + response, rtt, dnsServer, dnssecServer = s.forwardDNSRequestWithCache(newReq, fullDomain) if response != nil && response.Rcode == dns.RcodeSuccess { logger.Debug("使用prefixDomain查询成功", "fullDomain", fullDomain, "originalDomain", domain) break // 找到成功的响应,退出循环 @@ -595,7 +599,7 @@ func (s *Server) handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) { } // 添加查询日志 - 标记为实时 - s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, responseDNSSEC, true) + s.addQueryLog(sourceIP, domain, queryType, responseTime, "allowed", "", "", false, responseDNSSEC, true, dnsServer, dnssecServer) } // handleHostsResponse 处理hosts文件匹配的响应 @@ -708,7 +712,7 @@ type serverResponse struct { } // forwardDNSRequestWithCache 转发DNS请求到上游服务器并返回响应 -func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg, time.Duration) { +func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg, time.Duration, string, string) { // 始终支持EDNS var udpSize uint16 = 4096 var doFlag bool = s.config.EnableDNSSEC @@ -746,9 +750,16 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg } } - // 2. 如果没有匹配的域名特定配置,使用默认的上游DNS服务器 + // 2. 如果没有匹配的域名特定配置 if !domainMatched { - selectedUpstreamDNS = s.config.UpstreamDNS + // 如果启用了DNSSEC且有配置DNSSEC专用服务器,则使用DNSSEC专用服务器 + if s.config.EnableDNSSEC && len(s.config.DNSSECUpstreamDNS) > 0 { + selectedUpstreamDNS = s.config.DNSSECUpstreamDNS + logger.Debug("使用DNSSEC专用服务器", "servers", selectedUpstreamDNS) + } else { + // 否则使用默认的上游DNS服务器 + selectedUpstreamDNS = s.config.UpstreamDNS + } } // 1. 首先尝试所有配置的上游DNS服务器 @@ -759,6 +770,8 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg var backupResponse *dns.Msg var backupRtt time.Duration var hasBackup bool + var usedDNSServer string + var usedDNSSECServer string // 根据查询模式处理请求 switch s.config.QueryMode { @@ -832,21 +845,47 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg } } - // 如果响应成功,根据DNSSEC状态选择最佳响应 - if resp.response.Rcode == dns.RcodeSuccess { - // 优先选择带有DNSSEC记录的响应 - if containsDNSSEC { - bestResponse = resp.response - bestRtt = resp.rtt - hasBestResponse = true - hasDNSSECResponse = true - logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) - } else if !hasBestResponse { - // 没有带DNSSEC的响应时,保存第一个成功响应 - bestResponse = resp.response - bestRtt = resp.rtt - hasBestResponse = true - logger.Debug("找到最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) + // 如果响应成功或为NXDOMAIN,根据DNSSEC状态选择最佳响应 + if resp.response.Rcode == dns.RcodeSuccess || resp.response.Rcode == dns.RcodeNameError { + // 检查当前使用的服务器是否是DNSSEC专用服务器 + for _, dnssecServer := range dnssecServers { + if dnssecServer == resp.server { + usedDNSSECServer = resp.server + break + } + } + + if resp.response.Rcode == dns.RcodeSuccess { + // 处理成功响应 + // 优先选择带有DNSSEC记录的响应 + if containsDNSSEC { + bestResponse = resp.response + bestRtt = resp.rtt + hasBestResponse = true + hasDNSSECResponse = true + usedDNSServer = resp.server + logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) + } else if !hasBestResponse { + // 没有带DNSSEC的响应时,保存第一个成功响应 + bestResponse = resp.response + bestRtt = resp.rtt + hasBestResponse = true + usedDNSServer = resp.server + logger.Debug("找到最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) + } + } else if resp.response.Rcode == dns.RcodeNameError { + // 处理NXDOMAIN响应 + // 如果还没有最佳响应,或者最佳响应也是NXDOMAIN,优先选择更快的NXDOMAIN响应 + if !hasBestResponse || bestResponse.Rcode == dns.RcodeNameError { + // 如果还没有最佳响应,或者当前响应更快,更新最佳响应 + if !hasBestResponse || resp.rtt < bestRtt { + bestResponse = resp.response + bestRtt = resp.rtt + hasBestResponse = true + usedDNSServer = resp.server + logger.Debug("找到NXDOMAIN最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) + } + } } // 保存为备选响应 if !hasBackup { @@ -900,21 +939,46 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg } } - // 如果响应成功,根据DNSSEC状态选择最佳响应 - if response.Rcode == dns.RcodeSuccess { - // 优先选择带有DNSSEC记录的响应 - if containsDNSSEC { + // 如果响应成功或为NXDOMAIN,根据DNSSEC状态选择最佳响应 + if response.Rcode == dns.RcodeSuccess || response.Rcode == dns.RcodeNameError { + if response.Rcode == dns.RcodeSuccess { + // 优先选择带有DNSSEC记录的响应 + if containsDNSSEC { + bestResponse = response + bestRtt = rtt + hasBestResponse = true + hasDNSSECResponse = true + usedDNSServer = selectedServer + // 如果当前使用的服务器是DNSSEC专用服务器,同时设置usedDNSSECServer + for _, dnssecServer := range dnssecServers { + if dnssecServer == selectedServer { + usedDNSSECServer = selectedServer + break + } + } + logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", selectedServer, "rtt", rtt) + } else { + // 没有带DNSSEC的响应时,保存成功响应 + bestResponse = response + bestRtt = rtt + hasBestResponse = true + usedDNSServer = selectedServer + // 如果当前使用的服务器是DNSSEC专用服务器,同时设置usedDNSSECServer + for _, dnssecServer := range dnssecServers { + if dnssecServer == selectedServer { + usedDNSSECServer = selectedServer + break + } + } + logger.Debug("找到最佳响应", "domain", domain, "server", selectedServer, "rtt", rtt) + } + } else if response.Rcode == dns.RcodeNameError { + // 处理NXDOMAIN响应 bestResponse = response bestRtt = rtt hasBestResponse = true - hasDNSSECResponse = true - logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", selectedServer, "rtt", rtt) - } else { - // 没有带DNSSEC的响应时,保存成功响应 - bestResponse = response - bestRtt = rtt - hasBestResponse = true - logger.Debug("找到最佳响应", "domain", domain, "server", selectedServer, "rtt", rtt) + usedDNSServer = selectedServer + logger.Debug("找到NXDOMAIN响应", "domain", domain, "server", selectedServer, "rtt", rtt) } // 保存为备选响应 if !hasBackup { @@ -968,21 +1032,46 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg } } - // 如果响应成功,根据DNSSEC状态选择最佳响应 - if response.Rcode == dns.RcodeSuccess { - // 优先选择带有DNSSEC记录的响应 - if containsDNSSEC { + // 如果响应成功或为NXDOMAIN,根据DNSSEC状态选择最佳响应 + if response.Rcode == dns.RcodeSuccess || response.Rcode == dns.RcodeNameError { + if response.Rcode == dns.RcodeSuccess { + // 优先选择带有DNSSEC记录的响应 + if containsDNSSEC { + bestResponse = response + bestRtt = rtt + hasBestResponse = true + hasDNSSECResponse = true + usedDNSServer = fastestServer + // 如果当前使用的服务器是DNSSEC专用服务器,同时设置usedDNSSECServer + for _, dnssecServer := range dnssecServers { + if dnssecServer == fastestServer { + usedDNSSECServer = fastestServer + break + } + } + logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt) + } else { + // 没有带DNSSEC的响应时,保存成功响应 + bestResponse = response + bestRtt = rtt + hasBestResponse = true + usedDNSServer = fastestServer + // 如果当前使用的服务器是DNSSEC专用服务器,同时设置usedDNSSECServer + for _, dnssecServer := range dnssecServers { + if dnssecServer == fastestServer { + usedDNSSECServer = fastestServer + break + } + } + logger.Debug("找到最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt) + } + } else if response.Rcode == dns.RcodeNameError { + // 处理NXDOMAIN响应 bestResponse = response bestRtt = rtt hasBestResponse = true - hasDNSSECResponse = true - logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt) - } else { - // 没有带DNSSEC的响应时,保存成功响应 - bestResponse = response - bestRtt = rtt - hasBestResponse = true - logger.Debug("找到最佳响应", "domain", domain, "server", fastestServer, "rtt", rtt) + usedDNSServer = fastestServer + logger.Debug("找到NXDOMAIN响应", "domain", domain, "server", fastestServer, "rtt", rtt) } // 保存为备选响应 if !hasBackup { @@ -1050,21 +1139,52 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg } } - // 如果响应成功,根据DNSSEC状态选择最佳响应 - if resp.response.Rcode == dns.RcodeSuccess { - // 优先选择带有DNSSEC记录的响应 - if containsDNSSEC { - bestResponse = resp.response - bestRtt = resp.rtt - hasBestResponse = true - hasDNSSECResponse = true - logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) - } else if !hasBestResponse { - // 没有带DNSSEC的响应时,保存第一个成功响应 - bestResponse = resp.response - bestRtt = resp.rtt - hasBestResponse = true - logger.Debug("找到最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) + // 如果响应成功或为NXDOMAIN,根据DNSSEC状态选择最佳响应 + if resp.response.Rcode == dns.RcodeSuccess || resp.response.Rcode == dns.RcodeNameError { + if resp.response.Rcode == dns.RcodeSuccess { + // 优先选择带有DNSSEC记录的响应 + if containsDNSSEC { + bestResponse = resp.response + bestRtt = resp.rtt + hasBestResponse = true + hasDNSSECResponse = true + usedDNSServer = resp.server + // 如果当前使用的服务器是DNSSEC专用服务器,同时设置usedDNSSECServer + for _, dnssecServer := range dnssecServers { + if dnssecServer == resp.server { + usedDNSSECServer = resp.server + break + } + } + logger.Debug("找到带DNSSEC的最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) + } else if !hasBestResponse { + // 没有带DNSSEC的响应时,保存第一个成功响应 + bestResponse = resp.response + bestRtt = resp.rtt + hasBestResponse = true + usedDNSServer = resp.server + // 如果当前使用的服务器是DNSSEC专用服务器,同时设置usedDNSSECServer + for _, dnssecServer := range dnssecServers { + if dnssecServer == resp.server { + usedDNSSECServer = resp.server + break + } + } + logger.Debug("找到最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) + } + } else if resp.response.Rcode == dns.RcodeNameError { + // 处理NXDOMAIN响应 + // 如果还没有最佳响应,或者最佳响应也是NXDOMAIN,优先选择更快的NXDOMAIN响应 + if !hasBestResponse || bestResponse.Rcode == dns.RcodeNameError { + // 如果还没有最佳响应,或者当前响应更快,更新最佳响应 + if !hasBestResponse || resp.rtt < bestRtt { + bestResponse = resp.response + bestRtt = resp.rtt + hasBestResponse = true + usedDNSServer = resp.server + logger.Debug("找到NXDOMAIN最佳响应", "domain", domain, "server", resp.server, "rtt", resp.rtt) + } + } } // 保存为备选响应 if !hasBackup { @@ -1137,6 +1257,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg containsDNSSEC := s.hasDNSSECRecords(resp.response) if resp.response.Rcode == dns.RcodeSuccess { + // 无论响应是否包含DNSSEC记录,只要使用了DNSSEC专用服务器,就设置usedDNSSECServer + usedDNSSECServer = resp.server + // 验证DNSSEC记录 signatureValid := s.verifyDNSSEC(resp.response) @@ -1197,6 +1320,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg containsDNSSEC := s.hasDNSSECRecords(response) if response.Rcode == dns.RcodeSuccess { + // 无论响应是否包含DNSSEC记录,只要使用了DNSSEC专用服务器,就设置usedDNSSECServer + usedDNSSECServer = selectedServer + // 验证DNSSEC记录 signatureValid := s.verifyDNSSEC(response) @@ -1257,6 +1383,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg containsDNSSEC := s.hasDNSSECRecords(response) if response.Rcode == dns.RcodeSuccess { + // 无论响应是否包含DNSSEC记录,只要使用了DNSSEC专用服务器,就设置usedDNSSECServer + usedDNSSECServer = fastestServer + // 验证DNSSEC记录 signatureValid := s.verifyDNSSEC(response) @@ -1315,6 +1444,9 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg containsDNSSEC := s.hasDNSSECRecords(response) if response.Rcode == dns.RcodeSuccess { + // 无论响应是否包含DNSSEC记录,只要使用了DNSSEC专用服务器,就设置usedDNSSECServer + usedDNSSECServer = dnssecServer + // 验证DNSSEC记录 signatureValid := s.verifyDNSSEC(response) @@ -1366,9 +1498,43 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg // 检查最佳响应是否包含DNSSEC记录 bestHasDNSSEC := s.hasDNSSECRecords(bestResponse) - // 如果启用了DNSSEC且最佳响应不包含DNSSEC记录,使用upstreamDNS的解析结果 + // 如果启用了DNSSEC且最佳响应不包含DNSSEC记录,尝试使用本地解析 if s.config.EnableDNSSEC && !bestHasDNSSEC { - logger.Debug("最佳响应不包含DNSSEC记录,使用upstreamDNS的解析结果", "domain", domain) + logger.Debug("最佳响应不包含DNSSEC记录,尝试使用本地解析", "domain", domain) + if ip, exists := s.shieldManager.GetHostsIP(domain); exists { + // 本地解析成功,构建响应 + localResponse := new(dns.Msg) + localResponse.SetReply(r) + localResponse.RecursionAvailable = true + localResponse.AuthenticatedData = false + localResponse.Rcode = dns.RcodeSuccess + + if len(r.Question) > 0 { + q := r.Question[0] + answer := new(dns.A) + answer.Hdr = dns.RR_Header{ + Name: q.Name, + Rrtype: q.Qtype, + Class: q.Qclass, + Ttl: 300, + } + answer.A = net.ParseIP(ip) + localResponse.Answer = append(localResponse.Answer, answer) + } + + // 记录解析域名统计 + s.updateResolvedDomainStats(domain) + + // 更新域名的DNSSEC状态为false + s.updateDomainDNSSECStatus(domain, false) + + s.updateStats(func(stats *Stats) { + stats.Allowed++ + }) + + logger.Debug("使用本地解析结果", "domain", domain, "ip", ip) + return localResponse, 0, "", "" + } } // 记录解析域名统计 @@ -1377,12 +1543,14 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg // 更新域名的DNSSEC状态 if bestHasDNSSEC { s.updateDomainDNSSECStatus(domain, true) + } else { + s.updateDomainDNSSECStatus(domain, false) } s.updateStats(func(stats *Stats) { stats.Allowed++ }) - return bestResponse, bestRtt + return bestResponse, bestRtt, usedDNSServer, usedDNSSECServer } // 如果有备选响应,返回该响应 @@ -1390,11 +1558,11 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg logger.Debug("使用备选响应,没有找到更好的结果", "domain", domain) // 记录解析域名统计 s.updateResolvedDomainStats(domain) - + // 更新统计信息 s.updateStats(func(stats *Stats) { stats.Allowed++ }) - return backupResponse, backupRtt + return backupResponse, backupRtt, "", "" } // 所有上游服务器都失败,返回服务器失败错误 @@ -1407,12 +1575,12 @@ func (s *Server) forwardDNSRequestWithCache(r *dns.Msg, domain string) (*dns.Msg s.updateStats(func(stats *Stats) { stats.Errors++ }) - return response, 0 + return response, 0, "", "" } // forwardDNSRequest 转发DNS请求到上游服务器 func (s *Server) forwardDNSRequest(w dns.ResponseWriter, r *dns.Msg, domain string) { - response, _ := s.forwardDNSRequestWithCache(r, domain) + response, _, _, _ := s.forwardDNSRequestWithCache(r, domain) w.WriteMsg(response) } @@ -1844,7 +2012,7 @@ func (s *Server) updateStats(update func(*Stats)) { } // addQueryLog 添加查询日志 -func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime int64, result, blockRule, blockType string, fromCache, dnssec, edns bool) { +func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime int64, result, blockRule, blockType string, fromCache, dnssec, edns bool, dnsServer, dnssecServer string) { // 获取IP地理位置 location := s.getIpGeolocation(clientIP) @@ -1862,6 +2030,8 @@ func (s *Server) addQueryLog(clientIP, domain, queryType string, responseTime in FromCache: fromCache, DNSSEC: dnssec, EDNS: edns, + DNSServer: dnsServer, + DNSSECServer: dnssecServer, } // 添加到日志列表 diff --git a/static/js/logs.js b/static/js/logs.js index 9190e73..ce445ea 100644 --- a/static/js/logs.js +++ b/static/js/logs.js @@ -400,6 +400,7 @@ function updateLogsTable(logs) {
${log.Domain}
类型: ${log.QueryType}, ${statusText}, ${log.FromCache ? '缓存' : '实时'}${log.DNSSEC ? ', DNSSEC' : ''}${log.EDNS ? ', EDNS' : ''}
+
DNS 服务器: ${log.DNSServer || '无'}, DNSSEC专用: ${log.DNSSECServer || '无'}
${log.ResponseTime}ms ${log.BlockRule || '-'} diff --git a/temp_config.json b/temp_config.json new file mode 100644 index 0000000..237b7c2 --- /dev/null +++ b/temp_config.json @@ -0,0 +1,52 @@ +{ + "dns": { + "port": 5353, + "upstreamDNS": [ + "223.5.5.5:53", + "223.6.6.6:53", + "117.50.10.10:53", + "10.35.10.200:53" + ], + "dnssecUpstreamDNS": [ + "117.50.10.10:53", + "101.226.4.6:53", + "218.30.118.6:53", + "208.67.220.220:53", + "208.67.222.222:53" + ], + "timeout": 5000, + "statsFile": "data/stats.json", + "saveInterval": 300, + "cacheTTL": 30, + "enableDNSSEC": true, + "queryMode": "parallel", + "domainSpecificDNS": { + "amazehome.xyz": ["10.35.10.200:53"] + } + }, + "http": { + "port": 8081, + "host": "0.0.0.0", + "enableAPI": true, + "username": "admin", + "password": "admin" + }, + "shield": { + "localRulesFile": "data/rules.txt", + "blacklists": [], + "updateInterval": 3600, + "hostsFile": "data/hosts.txt", + "blockMethod": "NXDOMAIN", + "customBlockIP": "", + "statsFile": "./data/shield_stats.json", + "statsSaveInterval": 60, + "remoteRulesCacheDir": "data/remote_rules" + }, + "log": { + "file": "logs/dns-server-5353.log", + "level": "debug", + "maxSize": 100, + "maxBackups": 10, + "maxAge": 30 + } +} \ No newline at end of file