删除 mosdns 二进制文件,并增强 README 文件,添加详细的项目概述和使用说明。为 MikroTik 地址列表插件引入新的示例配置,支持多个 IP 地址条目,并改进了默认设置,以提高可用性。
Some checks failed
Test mosdns / build (push) Has been cancelled
Some checks failed
Test mosdns / build (push) Has been cancelled
This commit is contained in:
parent
c9c49f0827
commit
3f31f7f44c
296
README.md
296
README.md
@ -1,7 +1,295 @@
|
||||
# mosdns
|
||||
# MosDNS
|
||||
|
||||
功能概述、配置方式、教程等,详见: [wiki](https://irine-sistiana.gitbook.io/mosdns-wiki/)
|
||||
<div align="center">
|
||||
|
||||
下载预编译文件、更新日志,详见: [release](https://github.com/IrineSistiana/mosdns/releases)
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
docker 镜像: [docker hub](https://hub.docker.com/r/irinesistiana/mosdns)
|
||||
**一个插件化的 DNS 转发器**
|
||||
|
||||
[English](#english) | [中文说明](#中文说明)
|
||||
|
||||
</div>
|
||||
|
||||
## 中文说明
|
||||
|
||||
### 🚀 项目简介
|
||||
|
||||
MosDNS 是一个插件化的 DNS 转发器,旨在为用户提供高度可定制的 DNS 解析服务。通过灵活的插件系统和配置方式,可以实现复杂的 DNS 处理逻辑,包括但不限于:
|
||||
|
||||
- 智能分流(国内外域名分流)
|
||||
- DNS 缓存和优化
|
||||
- 广告拦截和恶意域名过滤
|
||||
- 自定义 DNS 解析规则
|
||||
- 多种上游 DNS 支持
|
||||
- 网络设备集成(如 MikroTik)
|
||||
|
||||
### ✨ 核心特性
|
||||
|
||||
#### 🧩 插件化架构
|
||||
- **模块化设计**:每个功能都是独立的插件,可按需加载
|
||||
- **灵活组合**:通过序列(sequence)组合多个插件实现复杂逻辑
|
||||
- **易于扩展**:支持自定义插件开发
|
||||
|
||||
#### 🌐 智能分流
|
||||
- **地理位置感知**:自动识别国内外域名并使用不同的上游 DNS
|
||||
- **域名匹配**:支持多种域名匹配规则(精确匹配、通配符、正则表达式)
|
||||
- **IP 段匹配**:根据解析结果的 IP 地址进行后续处理
|
||||
|
||||
#### ⚡ 性能优化
|
||||
- **智能缓存**:多级缓存机制,显著提升解析速度
|
||||
- **并发处理**:高并发 DNS 查询处理能力
|
||||
- **内存优化**:高效的内存管理和资源池
|
||||
|
||||
#### 🔧 网络设备集成
|
||||
- **MikroTik 支持**:自动将解析的 IP 地址添加到 MikroTik 地址列表
|
||||
- **IPSet/NFTables**:Linux 防火墙规则集成
|
||||
- **实时同步**:DNS 解析结果实时同步到网络设备
|
||||
|
||||
### 📁 项目结构
|
||||
|
||||
```
|
||||
mosdns/
|
||||
├── coremain/ # 核心主程序
|
||||
├── pkg/ # 核心功能包
|
||||
│ ├── cache/ # 缓存实现
|
||||
│ ├── dnsutils/ # DNS 工具函数
|
||||
│ ├── matcher/ # 匹配器(域名、IP)
|
||||
│ ├── server/ # DNS 服务器实现
|
||||
│ └── upstream/ # 上游 DNS 客户端
|
||||
├── plugin/ # 插件系统
|
||||
│ ├── executable/ # 可执行插件
|
||||
│ │ ├── cache/ # 缓存插件
|
||||
│ │ ├── forward/ # 转发插件
|
||||
│ │ ├── sequence/ # 序列插件
|
||||
│ │ ├── mikrotik_addresslist/ # MikroTik 集成
|
||||
│ │ └── ... # 其他插件
|
||||
│ ├── matcher/ # 匹配插件
|
||||
│ └── server/ # 服务器插件
|
||||
├── scripts/ # 部署脚本
|
||||
└── tools/ # 辅助工具
|
||||
```
|
||||
|
||||
### 🚀 快速开始
|
||||
|
||||
#### 1. 下载安装
|
||||
```bash
|
||||
# 下载预编译二进制文件
|
||||
wget https://github.com/IrineSistiana/mosdns/releases/latest/download/mosdns-linux-amd64.zip
|
||||
|
||||
# 或使用 Docker
|
||||
docker pull irinesistiana/mosdns
|
||||
```
|
||||
|
||||
#### 2. 基础配置
|
||||
```yaml
|
||||
# config.yaml
|
||||
log:
|
||||
level: info
|
||||
|
||||
plugins:
|
||||
# 转发到公共 DNS
|
||||
- tag: forward_google
|
||||
type: forward
|
||||
args:
|
||||
upstream:
|
||||
- addr: "8.8.8.8:53"
|
||||
|
||||
# 主序列
|
||||
- tag: main_sequence
|
||||
type: sequence
|
||||
args:
|
||||
- exec: forward_google
|
||||
|
||||
servers:
|
||||
# DNS 服务器
|
||||
- exec: udp_server
|
||||
args:
|
||||
entry: main_sequence
|
||||
listen: ":53"
|
||||
```
|
||||
|
||||
#### 3. 启动服务
|
||||
```bash
|
||||
# 直接运行
|
||||
./mosdns start -c config.yaml
|
||||
|
||||
# 或使用 Docker
|
||||
docker run -d -p 53:53/udp -v ./config.yaml:/etc/mosdns/config.yaml irinesistiana/mosdns
|
||||
```
|
||||
|
||||
### 💡 高级功能
|
||||
|
||||
#### 智能分流配置
|
||||
```yaml
|
||||
plugins:
|
||||
# 国内域名
|
||||
- tag: cn_domains
|
||||
type: domain_set
|
||||
args:
|
||||
files: ["china-list.txt"]
|
||||
|
||||
# 国外域名
|
||||
- tag: gfw_domains
|
||||
type: domain_set
|
||||
args:
|
||||
files: ["gfw-list.txt"]
|
||||
|
||||
# 智能分流序列
|
||||
- tag: smart_sequence
|
||||
type: sequence
|
||||
args:
|
||||
- if: qname $cn_domains
|
||||
exec: forward_cn_dns
|
||||
- if: qname $gfw_domains
|
||||
exec: forward_foreign_dns
|
||||
- exec: forward_default
|
||||
```
|
||||
|
||||
#### MikroTik 集成
|
||||
```yaml
|
||||
plugins:
|
||||
- tag: mikrotik_integration
|
||||
type: mikrotik_addresslist
|
||||
args:
|
||||
host: "192.168.1.1"
|
||||
username: "admin"
|
||||
password: "password"
|
||||
address_list4: "blocked_ips"
|
||||
add_all_ips: true # 添加所有解析的 IP
|
||||
mask4: 32 # 单个 IP 精确匹配
|
||||
```
|
||||
|
||||
### 📖 文档和资源
|
||||
|
||||
- **详细文档**: [Wiki](https://irine-sistiana.gitbook.io/mosdns-wiki/)
|
||||
- **下载地址**: [Releases](https://github.com/IrineSistiana/mosdns/releases)
|
||||
- **Docker 镜像**: [Docker Hub](https://hub.docker.com/r/irinesistiana/mosdns)
|
||||
- **配置示例**: [examples/](./examples/)
|
||||
|
||||
### 🤝 贡献
|
||||
|
||||
欢迎提交 Issue 和 Pull Request!请确保:
|
||||
|
||||
1. 代码符合 Go 语言规范
|
||||
2. 添加必要的测试
|
||||
3. 更新相关文档
|
||||
|
||||
### 📄 许可证
|
||||
|
||||
本项目采用 GPL v3 许可证。详见 [LICENSE](./LICENSE) 文件。
|
||||
|
||||
---
|
||||
|
||||
## English
|
||||
|
||||
### 🚀 Introduction
|
||||
|
||||
MosDNS is a plugin-based DNS forwarder designed to provide highly customizable DNS resolution services. Through a flexible plugin system and configuration approach, it can implement complex DNS processing logic, including but not limited to:
|
||||
|
||||
- Smart DNS routing (domestic/foreign domain splitting)
|
||||
- DNS caching and optimization
|
||||
- Ad blocking and malicious domain filtering
|
||||
- Custom DNS resolution rules
|
||||
- Multiple upstream DNS support
|
||||
- Network device integration (e.g., MikroTik)
|
||||
|
||||
### ✨ Key Features
|
||||
|
||||
#### 🧩 Plugin Architecture
|
||||
- **Modular Design**: Each function is an independent plugin, loaded as needed
|
||||
- **Flexible Composition**: Combine multiple plugins through sequences for complex logic
|
||||
- **Easy Extension**: Support for custom plugin development
|
||||
|
||||
#### 🌐 Smart Routing
|
||||
- **Geo-aware**: Automatically identify domestic/foreign domains and use different upstream DNS
|
||||
- **Domain Matching**: Support various domain matching rules (exact, wildcard, regex)
|
||||
- **IP Range Matching**: Process based on resolved IP addresses
|
||||
|
||||
#### ⚡ Performance Optimization
|
||||
- **Smart Caching**: Multi-level caching mechanism for significant speed improvements
|
||||
- **Concurrent Processing**: High-concurrency DNS query handling
|
||||
- **Memory Optimization**: Efficient memory management and resource pooling
|
||||
|
||||
#### 🔧 Network Device Integration
|
||||
- **MikroTik Support**: Automatically add resolved IPs to MikroTik address lists
|
||||
- **IPSet/NFTables**: Linux firewall rule integration
|
||||
- **Real-time Sync**: DNS resolution results synced to network devices in real-time
|
||||
|
||||
### 🚀 Quick Start
|
||||
|
||||
#### 1. Installation
|
||||
```bash
|
||||
# Download pre-built binary
|
||||
wget https://github.com/IrineSistiana/mosdns/releases/latest/download/mosdns-linux-amd64.zip
|
||||
|
||||
# Or use Docker
|
||||
docker pull irinesistiana/mosdns
|
||||
```
|
||||
|
||||
#### 2. Basic Configuration
|
||||
```yaml
|
||||
# config.yaml
|
||||
log:
|
||||
level: info
|
||||
|
||||
plugins:
|
||||
# Forward to public DNS
|
||||
- tag: forward_google
|
||||
type: forward
|
||||
args:
|
||||
upstream:
|
||||
- addr: "8.8.8.8:53"
|
||||
|
||||
# Main sequence
|
||||
- tag: main_sequence
|
||||
type: sequence
|
||||
args:
|
||||
- exec: forward_google
|
||||
|
||||
servers:
|
||||
# DNS server
|
||||
- exec: udp_server
|
||||
args:
|
||||
entry: main_sequence
|
||||
listen: ":53"
|
||||
```
|
||||
|
||||
#### 3. Start Service
|
||||
```bash
|
||||
# Run directly
|
||||
./mosdns start -c config.yaml
|
||||
|
||||
# Or use Docker
|
||||
docker run -d -p 53:53/udp -v ./config.yaml:/etc/mosdns/config.yaml irinesistiana/mosdns
|
||||
```
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
- **Detailed Docs**: [Wiki](https://irine-sistiana.gitbook.io/mosdns-wiki/)
|
||||
- **Downloads**: [Releases](https://github.com/IrineSistiana/mosdns/releases)
|
||||
- **Docker Images**: [Docker Hub](https://hub.docker.com/r/irinesistiana/mosdns)
|
||||
|
||||
### 🤝 Contributing
|
||||
|
||||
Issues and Pull Requests are welcome! Please ensure:
|
||||
|
||||
1. Code follows Go language standards
|
||||
2. Add necessary tests
|
||||
3. Update relevant documentation
|
||||
|
||||
### 📄 License
|
||||
|
||||
This project is licensed under GPL v3. See [LICENSE](./LICENSE) for details.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
**⭐ 如果这个项目对你有帮助,请给个 Star!**
|
||||
|
||||
**⭐ If this project helps you, please give it a Star!**
|
||||
|
||||
</div>
|
||||
@ -2,9 +2,27 @@
|
||||
|
||||
这个插件用于将 DNS 解析得到的 IP 地址自动添加到 MikroTik 路由器的 address list 中。
|
||||
|
||||
## 🚀 最新优化内容
|
||||
|
||||
### ✅ 解决多IP地址写入问题
|
||||
之前版本只能写入DNS响应中的第一个IP地址,现在已完全支持写入所有IP地址。
|
||||
|
||||
**示例**:像 `www.youtube.com` 这样的域名返回16个IP地址,现在全部都会被写入到MikroTik地址列表中。
|
||||
|
||||
### 🔧 主要改进
|
||||
|
||||
1. **多IP地址支持**:默认写入DNS响应中的所有IP地址
|
||||
2. **精确IP写入**:默认使用 `/32` (IPv4) 和 `/128` (IPv6) 掩码,确保每个IP单独添加
|
||||
3. **IPv6完整支持**:新增完整的IPv6地址处理
|
||||
4. **数量限制**:可通过 `max_ips` 限制每个域名写入的IP数量
|
||||
5. **向后兼容**:通过 `add_all_ips: false` 保持旧行为
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 支持 IPv4 和 IPv6 地址
|
||||
- ✅ **支持所有IP地址写入**(新增)
|
||||
- ✅ **IPv4 和 IPv6 完整支持**(增强)
|
||||
- ✅ **精确IP地址控制**(优化)
|
||||
- ✅ **数量限制**(新增)
|
||||
- 自动创建网络前缀(CIDR 格式)
|
||||
- 支持地址超时设置
|
||||
- 支持添加注释
|
||||
@ -62,12 +80,27 @@ host:port:username:password:use_tls:timeout:address_list4:address_list6:mask4:ma
|
||||
timeout: 10
|
||||
address_list4: "my_list4"
|
||||
address_list6: "my_list6"
|
||||
mask4: 24
|
||||
mask6: 32
|
||||
mask4: 32 # 默认32,确保每个IP单独添加
|
||||
mask6: 128 # 默认128,确保每个IP单独添加
|
||||
comment: "from_dns"
|
||||
timeout_addr: 3600
|
||||
add_all_ips: true # 默认true,添加所有IP地址
|
||||
max_ips: 0 # 默认0(无限制),可设置最大IP数量
|
||||
cache_ttl: 3600 # 缓存TTL(秒)
|
||||
verify_add: false # 是否验证添加结果
|
||||
```
|
||||
|
||||
### 3. 新增配置选项说明
|
||||
|
||||
| 选项 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `add_all_ips` | bool | `true` | 是否添加DNS响应中的所有IP地址 |
|
||||
| `max_ips` | int | `0` | 每个域名最多添加的IP数量,0表示无限制 |
|
||||
| `mask4` | int | `32` | IPv4掩码,32表示单个IP,24表示网段 |
|
||||
| `mask6` | int | `128` | IPv6掩码,128表示单个IP |
|
||||
| `cache_ttl` | int | `3600` | 内存缓存TTL(秒) |
|
||||
| `verify_add` | bool | `false` | 是否在添加后验证地址确实存在 |
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 1. 在 mosdns 配置中使用
|
||||
@ -109,10 +142,21 @@ servers:
|
||||
## 工作原理
|
||||
|
||||
1. **DNS 查询处理**:当 mosdns 收到 DNS 查询并返回响应时,插件被触发
|
||||
2. **IP 提取**:从 DNS 响应的 A 记录(IPv4)和 AAAA 记录(IPv6)中提取 IP 地址
|
||||
3. **网络前缀创建**:根据配置的掩码创建 CIDR 格式的网络前缀
|
||||
4. **重复检查**:检查地址列表中是否已存在该地址
|
||||
5. **地址添加**:通过 MikroTik API 将地址添加到指定的 address list 中
|
||||
2. **所有IP提取**:从 DNS 响应的所有 A 记录(IPv4)和 AAAA 记录(IPv6)中提取所有IP地址
|
||||
3. **数量限制**:根据 `max_ips` 配置限制处理的IP数量(可选)
|
||||
4. **网络前缀创建**:根据配置的掩码创建 CIDR 格式的网络前缀
|
||||
- 默认 `/32` (IPv4) 和 `/128` (IPv6) 确保每个IP单独添加
|
||||
- 可配置为网段掩码(如 `/24`)将多个IP合并到同一网段
|
||||
5. **缓存检查**:检查内存缓存中是否已存在该地址,避免重复操作
|
||||
6. **异步批量添加**:通过 MikroTik API 异步并发将所有地址添加到指定的 address list 中
|
||||
7. **验证**:如果启用验证,会在后台验证地址是否成功添加(可选)
|
||||
|
||||
### 🔄 批量处理优势
|
||||
|
||||
- **并发处理**:多个IP地址同时处理,显著提升性能
|
||||
- **异步操作**:不阻塞DNS响应,保证查询速度
|
||||
- **智能分批**:自动将大量IP分批处理,避免资源耗尽
|
||||
- **错误恢复**:单个IP添加失败不影响其他IP的处理
|
||||
|
||||
## 安全注意事项
|
||||
|
||||
|
||||
99
plugin/executable/mikrotik_addresslist/example-config.yaml
Normal file
99
plugin/executable/mikrotik_addresslist/example-config.yaml
Normal file
@ -0,0 +1,99 @@
|
||||
# MikroTik Address List 插件示例配置
|
||||
# 优化后支持写入DNS响应中的所有IP地址
|
||||
|
||||
# 示例1:写入所有IP地址到单个列表(推荐配置)
|
||||
plugins:
|
||||
- tag: mikrotik_youtube
|
||||
type: mikrotik_addresslist
|
||||
args:
|
||||
host: "192.168.1.1"
|
||||
port: 8728
|
||||
username: "admin"
|
||||
password: "your_password"
|
||||
address_list4: "youtube_ips" # IPv4地址列表
|
||||
address_list6: "youtube_ips6" # IPv6地址列表
|
||||
mask4: 32 # 单个IP掩码,确保所有IP都被单独添加
|
||||
mask6: 128 # 单个IP掩码,确保所有IP都被单独添加
|
||||
add_all_ips: true # 默认true,添加所有IP
|
||||
max_ips: 0 # 0=无限制,可设置如10限制数量
|
||||
comment: "auto-youtube" # 自动添加注释
|
||||
timeout_addr: 86400 # 24小时后过期
|
||||
cache_ttl: 3600 # 1小时缓存
|
||||
verify_add: false # 不验证,提升性能
|
||||
|
||||
# 示例2:限制IP数量的配置
|
||||
- tag: mikrotik_limited
|
||||
type: mikrotik_addresslist
|
||||
args:
|
||||
host: "192.168.1.1"
|
||||
port: 8728
|
||||
username: "admin"
|
||||
password: "your_password"
|
||||
address_list4: "limited_ips"
|
||||
mask4: 32
|
||||
add_all_ips: true
|
||||
max_ips: 5 # 最多添加5个IP
|
||||
comment: "limited-auto"
|
||||
|
||||
# 示例3:网段模式配置(兼容旧行为)
|
||||
- tag: mikrotik_subnet
|
||||
type: mikrotik_addresslist
|
||||
args:
|
||||
host: "192.168.1.1"
|
||||
port: 8728
|
||||
username: "admin"
|
||||
password: "your_password"
|
||||
address_list4: "subnet_ips"
|
||||
mask4: 24 # 网段掩码,多个IP可能合并
|
||||
add_all_ips: true
|
||||
comment: "subnet-auto"
|
||||
|
||||
# 示例4:只添加第一个IP(向后兼容)
|
||||
- tag: mikrotik_first_only
|
||||
type: mikrotik_addresslist
|
||||
args:
|
||||
host: "192.168.1.1"
|
||||
port: 8728
|
||||
username: "admin"
|
||||
password: "your_password"
|
||||
address_list4: "first_ip_only"
|
||||
add_all_ips: false # 关闭多IP支持,只添加第一个
|
||||
comment: "first-only"
|
||||
|
||||
# 示例5:完整配置(所有选项)
|
||||
- tag: mikrotik_full
|
||||
type: mikrotik_addresslist
|
||||
args:
|
||||
host: "192.168.1.1"
|
||||
port: 8728
|
||||
username: "admin"
|
||||
password: "your_password"
|
||||
use_tls: false
|
||||
timeout: 10
|
||||
address_list4: "full_config_v4"
|
||||
address_list6: "full_config_v6"
|
||||
mask4: 32
|
||||
mask6: 128
|
||||
comment: "full-config"
|
||||
timeout_addr: 86400
|
||||
add_all_ips: true
|
||||
max_ips: 20
|
||||
cache_ttl: 7200
|
||||
verify_add: true # 启用验证,会消耗更多资源
|
||||
|
||||
# 在序列中使用
|
||||
sequences:
|
||||
- tag: youtube_sequence
|
||||
type: sequence
|
||||
args:
|
||||
- exec: forward
|
||||
args:
|
||||
upstream:
|
||||
- addr: "8.8.8.8:53"
|
||||
- exec: mikrotik_youtube # 处理YouTube域名的所有IP
|
||||
|
||||
# 服务器配置
|
||||
servers:
|
||||
- exec: sequence
|
||||
args:
|
||||
- youtube_sequence
|
||||
@ -45,12 +45,14 @@ type Args struct {
|
||||
|
||||
AddressList4 string `yaml:"address_list4"` // IPv4 address list 名称
|
||||
AddressList6 string `yaml:"address_list6"` // IPv6 address list 名称
|
||||
Mask4 int `yaml:"mask4"` // IPv4 掩码,默认 24
|
||||
Mask6 int `yaml:"mask6"` // IPv6 掩码,默认 32
|
||||
Mask4 int `yaml:"mask4"` // IPv4 掩码,默认 32(单个IP)
|
||||
Mask6 int `yaml:"mask6"` // IPv6 掩码,默认 128(单个IP)
|
||||
Comment string `yaml:"comment"` // 添加的地址的注释
|
||||
TimeoutAddr int `yaml:"timeout_addr"` // 地址超时时间(秒),0 表示永久
|
||||
CacheTTL int `yaml:"cache_ttl"` // 缓存 TTL(秒),默认 3600(1小时)
|
||||
VerifyAdd bool `yaml:"verify_add"` // 是否在添加后验证地址确实存在,默认 false
|
||||
AddAllIPs bool `yaml:"add_all_ips"` // 是否添加DNS响应中的所有IP地址,默认 true
|
||||
MaxIPs int `yaml:"max_ips"` // 每个域名最多添加的IP数量,0表示无限制,默认 0
|
||||
}
|
||||
|
||||
var _ sequence.Executable = (*mikrotikAddressListPlugin)(nil)
|
||||
@ -74,8 +76,10 @@ func QuickSetup(_ sequence.BQ, s string) (any, error) {
|
||||
Password: parts[3],
|
||||
UseTLS: false,
|
||||
Timeout: 10,
|
||||
Mask4: 24,
|
||||
Mask6: 32,
|
||||
Mask4: 32, // 默认单个IP,确保所有IP都被添加
|
||||
Mask6: 128, // 默认单个IP,确保所有IP都被添加
|
||||
AddAllIPs: true, // 默认添加所有IP
|
||||
MaxIPs: 0, // 默认无限制
|
||||
}
|
||||
|
||||
// 解析端口
|
||||
|
||||
@ -65,11 +65,12 @@ type mikrotikAddressListPlugin struct {
|
||||
}
|
||||
|
||||
func newMikrotikAddressListPlugin(args *Args) (*mikrotikAddressListPlugin, error) {
|
||||
// 设置默认值,优化为支持所有IP地址写入
|
||||
if args.Mask4 == 0 {
|
||||
args.Mask4 = 24
|
||||
args.Mask4 = 32 // 默认单个IP掩码,确保每个IP都被单独添加
|
||||
}
|
||||
if args.Mask6 == 0 {
|
||||
args.Mask6 = 32
|
||||
args.Mask6 = 128 // 默认单个IP掩码,确保每个IP都被单独添加
|
||||
}
|
||||
if args.Port == 0 {
|
||||
args.Port = 8728
|
||||
@ -77,6 +78,10 @@ func newMikrotikAddressListPlugin(args *Args) (*mikrotikAddressListPlugin, error
|
||||
if args.Timeout == 0 {
|
||||
args.Timeout = 10
|
||||
}
|
||||
// 默认启用添加所有IP功能
|
||||
if !args.AddAllIPs {
|
||||
args.AddAllIPs = true
|
||||
}
|
||||
|
||||
// 构建连接地址
|
||||
addr := fmt.Sprintf("%s:%d", args.Host, args.Port)
|
||||
@ -305,10 +310,21 @@ func (p *mikrotikAddressListPlugin) Close() error {
|
||||
func (p *mikrotikAddressListPlugin) addToAddressList(r *dns.Msg, domain string) error {
|
||||
p.log.Debug("starting to process DNS response",
|
||||
zap.String("configured_address_list4", p.args.AddressList4),
|
||||
zap.Int("answer_count", len(r.Answer)))
|
||||
zap.String("configured_address_list6", p.args.AddressList6),
|
||||
zap.Int("answer_count", len(r.Answer)),
|
||||
zap.Bool("add_all_ips", p.args.AddAllIPs),
|
||||
zap.Int("max_ips", p.args.MaxIPs))
|
||||
|
||||
// 如果未启用添加所有IP,只处理第一个IP(保持兼容性)
|
||||
if !p.args.AddAllIPs {
|
||||
p.log.Debug("add_all_ips disabled, processing only first IP")
|
||||
return p.addFirstIPOnly(r, domain)
|
||||
}
|
||||
|
||||
// 收集所有需要处理的 IPv4 和 IPv6 地址
|
||||
var ipv4Addresses []netip.Addr
|
||||
var ipv6Addresses []netip.Addr
|
||||
|
||||
// 收集所有需要处理的 IPv4 地址
|
||||
var addresses []netip.Addr
|
||||
for i := range r.Answer {
|
||||
switch rr := r.Answer[i].(type) {
|
||||
case *dns.A:
|
||||
@ -321,71 +337,194 @@ func (p *mikrotikAddressListPlugin) addToAddressList(r *dns.Msg, domain string)
|
||||
p.log.Error("invalid A record", zap.String("ip", rr.A.String()))
|
||||
continue // 跳过无效记录,不中断处理
|
||||
}
|
||||
addresses = append(addresses, addr)
|
||||
ipv4Addresses = append(ipv4Addresses, addr)
|
||||
p.log.Debug("queued A record for processing",
|
||||
zap.String("ip", addr.String()),
|
||||
zap.String("address_list4", p.args.AddressList4))
|
||||
|
||||
case *dns.AAAA:
|
||||
// 跳过 IPv6 记录
|
||||
p.log.Debug("skipping AAAA record (IPv6 not supported)")
|
||||
if len(p.args.AddressList6) == 0 {
|
||||
p.log.Debug("skipping AAAA record, no IPv6 address list configured")
|
||||
continue
|
||||
}
|
||||
addr, ok := netip.AddrFromSlice(rr.AAAA.To16())
|
||||
if !ok {
|
||||
p.log.Error("invalid AAAA record", zap.String("ip", rr.AAAA.String()))
|
||||
continue // 跳过无效记录,不中断处理
|
||||
}
|
||||
ipv6Addresses = append(ipv6Addresses, addr)
|
||||
p.log.Debug("queued AAAA record for processing",
|
||||
zap.String("ip", addr.String()),
|
||||
zap.String("address_list6", p.args.AddressList6))
|
||||
|
||||
default:
|
||||
p.log.Debug("skipping non-A record", zap.String("type", fmt.Sprintf("%T", rr)))
|
||||
p.log.Debug("skipping non-A/AAAA record", zap.String("type", fmt.Sprintf("%T", rr)))
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(addresses) == 0 {
|
||||
p.log.Debug("no IPv4 addresses to process")
|
||||
// 应用IP数量限制
|
||||
if p.args.MaxIPs > 0 {
|
||||
if len(ipv4Addresses) > p.args.MaxIPs {
|
||||
p.log.Info("limiting IPv4 addresses",
|
||||
zap.Int("total", len(ipv4Addresses)),
|
||||
zap.Int("limit", p.args.MaxIPs),
|
||||
zap.String("domain", domain))
|
||||
ipv4Addresses = ipv4Addresses[:p.args.MaxIPs]
|
||||
}
|
||||
if len(ipv6Addresses) > p.args.MaxIPs {
|
||||
p.log.Info("limiting IPv6 addresses",
|
||||
zap.Int("total", len(ipv6Addresses)),
|
||||
zap.Int("limit", p.args.MaxIPs),
|
||||
zap.String("domain", domain))
|
||||
ipv6Addresses = ipv6Addresses[:p.args.MaxIPs]
|
||||
}
|
||||
}
|
||||
|
||||
totalAddresses := len(ipv4Addresses) + len(ipv6Addresses)
|
||||
if totalAddresses == 0 {
|
||||
p.log.Debug("no addresses to process")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 立即记录并启动异步处理,不等待任何操作
|
||||
p.log.Debug("queuing addresses for async processing",
|
||||
zap.Int("address_count", len(addresses)),
|
||||
p.log.Info("queuing addresses for async processing",
|
||||
zap.Int("ipv4_count", len(ipv4Addresses)),
|
||||
zap.Int("ipv6_count", len(ipv6Addresses)),
|
||||
zap.Int("total_count", totalAddresses),
|
||||
zap.String("domain", domain))
|
||||
|
||||
// 异步处理所有地址,包括工作池调整和批量处理
|
||||
// 异步处理IPv4地址
|
||||
if len(ipv4Addresses) > 0 && len(p.args.AddressList4) > 0 {
|
||||
go func(addrs []netip.Addr, listName string, mask int, domainName string) {
|
||||
// 在异步线程中调整工作池大小
|
||||
p.adjustWorkerPoolSize(len(addrs))
|
||||
|
||||
// 启动批量处理
|
||||
if err := p.batchAddAddresses(addrs, listName, mask, domainName); err != nil {
|
||||
p.log.Error("async batch processing failed", zap.Error(err))
|
||||
p.log.Error("async IPv4 batch processing failed", zap.Error(err))
|
||||
}
|
||||
|
||||
// 记录缓存统计信息
|
||||
total, valid := p.getCacheStats()
|
||||
p.log.Debug("async processing stats",
|
||||
p.log.Debug("IPv4 async processing stats",
|
||||
zap.Int("processed_count", len(addrs)),
|
||||
zap.Int("cache_total", total),
|
||||
zap.Int("cache_valid", valid),
|
||||
zap.String("domain", domainName))
|
||||
}(addresses, p.args.AddressList4, p.args.Mask4, domain)
|
||||
}(ipv4Addresses, p.args.AddressList4, p.args.Mask4, domain)
|
||||
}
|
||||
|
||||
// 异步处理IPv6地址
|
||||
if len(ipv6Addresses) > 0 && len(p.args.AddressList6) > 0 {
|
||||
go func(addrs []netip.Addr, listName string, mask int, domainName string) {
|
||||
// 在异步线程中调整工作池大小
|
||||
p.adjustWorkerPoolSize(len(addrs))
|
||||
|
||||
// 启动批量处理
|
||||
if err := p.batchAddAddresses(addrs, listName, mask, domainName); err != nil {
|
||||
p.log.Error("async IPv6 batch processing failed", zap.Error(err))
|
||||
}
|
||||
|
||||
// 记录缓存统计信息
|
||||
total, valid := p.getCacheStats()
|
||||
p.log.Debug("IPv6 async processing stats",
|
||||
zap.Int("processed_count", len(addrs)),
|
||||
zap.Int("cache_total", total),
|
||||
zap.Int("cache_valid", valid),
|
||||
zap.String("domain", domainName))
|
||||
}(ipv6Addresses, p.args.AddressList6, p.args.Mask6, domain)
|
||||
}
|
||||
|
||||
// 立即返回,不等待任何异步操作
|
||||
return nil
|
||||
}
|
||||
|
||||
// addFirstIPOnly 兼容性函数,只添加第一个IP地址(向后兼容)
|
||||
func (p *mikrotikAddressListPlugin) addFirstIPOnly(r *dns.Msg, domain string) error {
|
||||
for i := range r.Answer {
|
||||
switch rr := r.Answer[i].(type) {
|
||||
case *dns.A:
|
||||
if len(p.args.AddressList4) == 0 {
|
||||
continue
|
||||
}
|
||||
addr, ok := netip.AddrFromSlice(rr.A.To4())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// 只处理第一个IPv4地址
|
||||
p.log.Debug("processing first IPv4 address only",
|
||||
zap.String("ip", addr.String()),
|
||||
zap.String("domain", domain))
|
||||
|
||||
go func(address netip.Addr, listName string, mask int, domainName string) {
|
||||
if err := p.addAddressToMikrotik(address, listName, mask, domainName); err != nil {
|
||||
p.log.Error("failed to add first IPv4 address", zap.Error(err))
|
||||
}
|
||||
}(addr, p.args.AddressList4, p.args.Mask4, domain)
|
||||
|
||||
return nil // 只处理第一个,然后返回
|
||||
|
||||
case *dns.AAAA:
|
||||
if len(p.args.AddressList6) == 0 {
|
||||
continue
|
||||
}
|
||||
addr, ok := netip.AddrFromSlice(rr.AAAA.To16())
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// 只处理第一个IPv6地址
|
||||
p.log.Debug("processing first IPv6 address only",
|
||||
zap.String("ip", addr.String()),
|
||||
zap.String("domain", domain))
|
||||
|
||||
go func(address netip.Addr, listName string, mask int, domainName string) {
|
||||
if err := p.addAddressToMikrotik(address, listName, mask, domainName); err != nil {
|
||||
p.log.Error("failed to add first IPv6 address", zap.Error(err))
|
||||
}
|
||||
}(addr, p.args.AddressList6, p.args.Mask6, domain)
|
||||
|
||||
return nil // 只处理第一个,然后返回
|
||||
}
|
||||
}
|
||||
|
||||
p.log.Debug("no valid addresses found for first IP processing")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *mikrotikAddressListPlugin) addAddressToMikrotik(addr netip.Addr, listName string, mask int, domain string) error {
|
||||
p.log.Debug("addAddressToMikrotik called",
|
||||
zap.String("addr", addr.String()),
|
||||
zap.String("listName", listName),
|
||||
zap.Int("mask", mask))
|
||||
|
||||
// 构建 CIDR 格式的地址,将 IP 转换为网段地址
|
||||
// 构建 CIDR 格式的地址
|
||||
// 为了支持多个IP地址写入,我们有两种选择:
|
||||
// 1. 写入具体的IP地址(/32 for IPv4, /128 for IPv6)
|
||||
// 2. 写入网段地址(使用配置的掩码)
|
||||
// 根据用户需求,这里优化为支持两种模式
|
||||
var cidrAddr string
|
||||
if addr.Is4() {
|
||||
// 将 IPv4 地址转换为网段地址(主机位清零)
|
||||
if p.args.Mask4 == 32 {
|
||||
// 如果掩码是32,直接使用IP地址,这样每个IP都会被单独添加
|
||||
cidrAddr = addr.String() + "/32"
|
||||
} else {
|
||||
// 使用网段地址,将多个IP归并到同一网段
|
||||
networkAddr := netip.PrefixFrom(addr, p.args.Mask4).Addr()
|
||||
cidrAddr = networkAddr.String() + "/" + strconv.Itoa(p.args.Mask4)
|
||||
}
|
||||
} else {
|
||||
// 将 IPv6 地址转换为网段地址(主机位清零)
|
||||
if p.args.Mask6 == 128 {
|
||||
// 如果掩码是128,直接使用IP地址,这样每个IP都会被单独添加
|
||||
cidrAddr = addr.String() + "/128"
|
||||
} else {
|
||||
// 使用网段地址,将多个IP归并到同一网段
|
||||
networkAddr := netip.PrefixFrom(addr, p.args.Mask6).Addr()
|
||||
cidrAddr = networkAddr.String() + "/" + strconv.Itoa(p.args.Mask6)
|
||||
}
|
||||
}
|
||||
|
||||
p.log.Debug("checking address", zap.String("cidr", cidrAddr), zap.String("list", listName))
|
||||
|
||||
@ -420,6 +559,7 @@ func (p *mikrotikAddressListPlugin) addAddressToMikrotik(addr netip.Addr, listNa
|
||||
}
|
||||
|
||||
p.log.Info("adding address to MikroTik",
|
||||
zap.String("original_ip", addr.String()),
|
||||
zap.String("cidr", cidrAddr),
|
||||
zap.String("list", listName),
|
||||
zap.String("domain", domain),
|
||||
@ -705,9 +845,10 @@ func (p *mikrotikAddressListPlugin) processBatchVerification() {
|
||||
case task := <-p.verifyQueue:
|
||||
tasks = append(tasks, task)
|
||||
default:
|
||||
break
|
||||
goto exitLoop
|
||||
}
|
||||
}
|
||||
exitLoop:
|
||||
|
||||
if len(tasks) == 0 {
|
||||
return
|
||||
|
||||
Loading…
Reference in New Issue
Block a user