删除 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 中。
|
这个插件用于将 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 格式)
|
- 自动创建网络前缀(CIDR 格式)
|
||||||
- 支持地址超时设置
|
- 支持地址超时设置
|
||||||
- 支持添加注释
|
- 支持添加注释
|
||||||
@ -62,12 +80,27 @@ host:port:username:password:use_tls:timeout:address_list4:address_list6:mask4:ma
|
|||||||
timeout: 10
|
timeout: 10
|
||||||
address_list4: "my_list4"
|
address_list4: "my_list4"
|
||||||
address_list6: "my_list6"
|
address_list6: "my_list6"
|
||||||
mask4: 24
|
mask4: 32 # 默认32,确保每个IP单独添加
|
||||||
mask6: 32
|
mask6: 128 # 默认128,确保每个IP单独添加
|
||||||
comment: "from_dns"
|
comment: "from_dns"
|
||||||
timeout_addr: 3600
|
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 配置中使用
|
### 1. 在 mosdns 配置中使用
|
||||||
@ -109,10 +142,21 @@ servers:
|
|||||||
## 工作原理
|
## 工作原理
|
||||||
|
|
||||||
1. **DNS 查询处理**:当 mosdns 收到 DNS 查询并返回响应时,插件被触发
|
1. **DNS 查询处理**:当 mosdns 收到 DNS 查询并返回响应时,插件被触发
|
||||||
2. **IP 提取**:从 DNS 响应的 A 记录(IPv4)和 AAAA 记录(IPv6)中提取 IP 地址
|
2. **所有IP提取**:从 DNS 响应的所有 A 记录(IPv4)和 AAAA 记录(IPv6)中提取所有IP地址
|
||||||
3. **网络前缀创建**:根据配置的掩码创建 CIDR 格式的网络前缀
|
3. **数量限制**:根据 `max_ips` 配置限制处理的IP数量(可选)
|
||||||
4. **重复检查**:检查地址列表中是否已存在该地址
|
4. **网络前缀创建**:根据配置的掩码创建 CIDR 格式的网络前缀
|
||||||
5. **地址添加**:通过 MikroTik API 将地址添加到指定的 address list 中
|
- 默认 `/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 名称
|
AddressList4 string `yaml:"address_list4"` // IPv4 address list 名称
|
||||||
AddressList6 string `yaml:"address_list6"` // IPv6 address list 名称
|
AddressList6 string `yaml:"address_list6"` // IPv6 address list 名称
|
||||||
Mask4 int `yaml:"mask4"` // IPv4 掩码,默认 24
|
Mask4 int `yaml:"mask4"` // IPv4 掩码,默认 32(单个IP)
|
||||||
Mask6 int `yaml:"mask6"` // IPv6 掩码,默认 32
|
Mask6 int `yaml:"mask6"` // IPv6 掩码,默认 128(单个IP)
|
||||||
Comment string `yaml:"comment"` // 添加的地址的注释
|
Comment string `yaml:"comment"` // 添加的地址的注释
|
||||||
TimeoutAddr int `yaml:"timeout_addr"` // 地址超时时间(秒),0 表示永久
|
TimeoutAddr int `yaml:"timeout_addr"` // 地址超时时间(秒),0 表示永久
|
||||||
CacheTTL int `yaml:"cache_ttl"` // 缓存 TTL(秒),默认 3600(1小时)
|
CacheTTL int `yaml:"cache_ttl"` // 缓存 TTL(秒),默认 3600(1小时)
|
||||||
VerifyAdd bool `yaml:"verify_add"` // 是否在添加后验证地址确实存在,默认 false
|
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)
|
var _ sequence.Executable = (*mikrotikAddressListPlugin)(nil)
|
||||||
@ -69,13 +71,15 @@ func QuickSetup(_ sequence.BQ, s string) (any, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
args := &Args{
|
args := &Args{
|
||||||
Host: parts[0],
|
Host: parts[0],
|
||||||
Username: parts[2],
|
Username: parts[2],
|
||||||
Password: parts[3],
|
Password: parts[3],
|
||||||
UseTLS: false,
|
UseTLS: false,
|
||||||
Timeout: 10,
|
Timeout: 10,
|
||||||
Mask4: 24,
|
Mask4: 32, // 默认单个IP,确保所有IP都被添加
|
||||||
Mask6: 32,
|
Mask6: 128, // 默认单个IP,确保所有IP都被添加
|
||||||
|
AddAllIPs: true, // 默认添加所有IP
|
||||||
|
MaxIPs: 0, // 默认无限制
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析端口
|
// 解析端口
|
||||||
|
|||||||
@ -65,11 +65,12 @@ type mikrotikAddressListPlugin struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newMikrotikAddressListPlugin(args *Args) (*mikrotikAddressListPlugin, error) {
|
func newMikrotikAddressListPlugin(args *Args) (*mikrotikAddressListPlugin, error) {
|
||||||
|
// 设置默认值,优化为支持所有IP地址写入
|
||||||
if args.Mask4 == 0 {
|
if args.Mask4 == 0 {
|
||||||
args.Mask4 = 24
|
args.Mask4 = 32 // 默认单个IP掩码,确保每个IP都被单独添加
|
||||||
}
|
}
|
||||||
if args.Mask6 == 0 {
|
if args.Mask6 == 0 {
|
||||||
args.Mask6 = 32
|
args.Mask6 = 128 // 默认单个IP掩码,确保每个IP都被单独添加
|
||||||
}
|
}
|
||||||
if args.Port == 0 {
|
if args.Port == 0 {
|
||||||
args.Port = 8728
|
args.Port = 8728
|
||||||
@ -77,6 +78,10 @@ func newMikrotikAddressListPlugin(args *Args) (*mikrotikAddressListPlugin, error
|
|||||||
if args.Timeout == 0 {
|
if args.Timeout == 0 {
|
||||||
args.Timeout = 10
|
args.Timeout = 10
|
||||||
}
|
}
|
||||||
|
// 默认启用添加所有IP功能
|
||||||
|
if !args.AddAllIPs {
|
||||||
|
args.AddAllIPs = true
|
||||||
|
}
|
||||||
|
|
||||||
// 构建连接地址
|
// 构建连接地址
|
||||||
addr := fmt.Sprintf("%s:%d", args.Host, args.Port)
|
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 {
|
func (p *mikrotikAddressListPlugin) addToAddressList(r *dns.Msg, domain string) error {
|
||||||
p.log.Debug("starting to process DNS response",
|
p.log.Debug("starting to process DNS response",
|
||||||
zap.String("configured_address_list4", p.args.AddressList4),
|
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 {
|
for i := range r.Answer {
|
||||||
switch rr := r.Answer[i].(type) {
|
switch rr := r.Answer[i].(type) {
|
||||||
case *dns.A:
|
case *dns.A:
|
||||||
@ -321,70 +337,193 @@ func (p *mikrotikAddressListPlugin) addToAddressList(r *dns.Msg, domain string)
|
|||||||
p.log.Error("invalid A record", zap.String("ip", rr.A.String()))
|
p.log.Error("invalid A record", zap.String("ip", rr.A.String()))
|
||||||
continue // 跳过无效记录,不中断处理
|
continue // 跳过无效记录,不中断处理
|
||||||
}
|
}
|
||||||
addresses = append(addresses, addr)
|
ipv4Addresses = append(ipv4Addresses, addr)
|
||||||
p.log.Debug("queued A record for processing",
|
p.log.Debug("queued A record for processing",
|
||||||
zap.String("ip", addr.String()),
|
zap.String("ip", addr.String()),
|
||||||
zap.String("address_list4", p.args.AddressList4))
|
zap.String("address_list4", p.args.AddressList4))
|
||||||
|
|
||||||
case *dns.AAAA:
|
case *dns.AAAA:
|
||||||
// 跳过 IPv6 记录
|
if len(p.args.AddressList6) == 0 {
|
||||||
p.log.Debug("skipping AAAA record (IPv6 not supported)")
|
p.log.Debug("skipping AAAA record, no IPv6 address list configured")
|
||||||
continue
|
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:
|
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
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(addresses) == 0 {
|
// 应用IP数量限制
|
||||||
p.log.Debug("no IPv4 addresses to process")
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 立即记录并启动异步处理,不等待任何操作
|
// 立即记录并启动异步处理,不等待任何操作
|
||||||
p.log.Debug("queuing addresses for async processing",
|
p.log.Info("queuing addresses for async processing",
|
||||||
zap.Int("address_count", len(addresses)),
|
zap.Int("ipv4_count", len(ipv4Addresses)),
|
||||||
|
zap.Int("ipv6_count", len(ipv6Addresses)),
|
||||||
|
zap.Int("total_count", totalAddresses),
|
||||||
zap.String("domain", domain))
|
zap.String("domain", domain))
|
||||||
|
|
||||||
// 异步处理所有地址,包括工作池调整和批量处理
|
// 异步处理IPv4地址
|
||||||
go func(addrs []netip.Addr, listName string, mask int, domainName string) {
|
if len(ipv4Addresses) > 0 && len(p.args.AddressList4) > 0 {
|
||||||
// 在异步线程中调整工作池大小
|
go func(addrs []netip.Addr, listName string, mask int, domainName string) {
|
||||||
p.adjustWorkerPoolSize(len(addrs))
|
// 在异步线程中调整工作池大小
|
||||||
|
p.adjustWorkerPoolSize(len(addrs))
|
||||||
|
|
||||||
// 启动批量处理
|
// 启动批量处理
|
||||||
if err := p.batchAddAddresses(addrs, listName, mask, domainName); err != nil {
|
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()
|
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("processed_count", len(addrs)),
|
||||||
zap.Int("cache_total", total),
|
zap.Int("cache_total", total),
|
||||||
zap.Int("cache_valid", valid),
|
zap.Int("cache_valid", valid),
|
||||||
zap.String("domain", domainName))
|
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
|
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 {
|
func (p *mikrotikAddressListPlugin) addAddressToMikrotik(addr netip.Addr, listName string, mask int, domain string) error {
|
||||||
p.log.Debug("addAddressToMikrotik called",
|
p.log.Debug("addAddressToMikrotik called",
|
||||||
zap.String("addr", addr.String()),
|
zap.String("addr", addr.String()),
|
||||||
zap.String("listName", listName),
|
zap.String("listName", listName),
|
||||||
zap.Int("mask", mask))
|
zap.Int("mask", mask))
|
||||||
|
|
||||||
// 构建 CIDR 格式的地址,将 IP 转换为网段地址
|
// 构建 CIDR 格式的地址
|
||||||
|
// 为了支持多个IP地址写入,我们有两种选择:
|
||||||
|
// 1. 写入具体的IP地址(/32 for IPv4, /128 for IPv6)
|
||||||
|
// 2. 写入网段地址(使用配置的掩码)
|
||||||
|
// 根据用户需求,这里优化为支持两种模式
|
||||||
var cidrAddr string
|
var cidrAddr string
|
||||||
if addr.Is4() {
|
if addr.Is4() {
|
||||||
// 将 IPv4 地址转换为网段地址(主机位清零)
|
if p.args.Mask4 == 32 {
|
||||||
networkAddr := netip.PrefixFrom(addr, p.args.Mask4).Addr()
|
// 如果掩码是32,直接使用IP地址,这样每个IP都会被单独添加
|
||||||
cidrAddr = networkAddr.String() + "/" + strconv.Itoa(p.args.Mask4)
|
cidrAddr = addr.String() + "/32"
|
||||||
|
} else {
|
||||||
|
// 使用网段地址,将多个IP归并到同一网段
|
||||||
|
networkAddr := netip.PrefixFrom(addr, p.args.Mask4).Addr()
|
||||||
|
cidrAddr = networkAddr.String() + "/" + strconv.Itoa(p.args.Mask4)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 将 IPv6 地址转换为网段地址(主机位清零)
|
if p.args.Mask6 == 128 {
|
||||||
networkAddr := netip.PrefixFrom(addr, p.args.Mask6).Addr()
|
// 如果掩码是128,直接使用IP地址,这样每个IP都会被单独添加
|
||||||
cidrAddr = networkAddr.String() + "/" + strconv.Itoa(p.args.Mask6)
|
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))
|
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",
|
p.log.Info("adding address to MikroTik",
|
||||||
|
zap.String("original_ip", addr.String()),
|
||||||
zap.String("cidr", cidrAddr),
|
zap.String("cidr", cidrAddr),
|
||||||
zap.String("list", listName),
|
zap.String("list", listName),
|
||||||
zap.String("domain", domain),
|
zap.String("domain", domain),
|
||||||
@ -429,9 +569,9 @@ func (p *mikrotikAddressListPlugin) addAddressToMikrotik(addr netip.Addr, listNa
|
|||||||
p.log.Debug("Add to list: ", zap.Strings("params", params))
|
p.log.Debug("Add to list: ", zap.Strings("params", params))
|
||||||
|
|
||||||
// 发送到 RouterOS,优化重试机制以减少延迟
|
// 发送到 RouterOS,优化重试机制以减少延迟
|
||||||
maxRetries := 2 // 减少重试次数
|
maxRetries := 2 // 减少重试次数
|
||||||
backoffDuration := 50 * time.Millisecond // 减少退避时间
|
backoffDuration := 50 * time.Millisecond // 减少退避时间
|
||||||
var err error // 声明 err 变量
|
var err error // 声明 err 变量
|
||||||
|
|
||||||
for i := 0; i < maxRetries; i++ {
|
for i := 0; i < maxRetries; i++ {
|
||||||
// 使用读锁保护连接访问
|
// 使用读锁保护连接访问
|
||||||
@ -705,9 +845,10 @@ func (p *mikrotikAddressListPlugin) processBatchVerification() {
|
|||||||
case task := <-p.verifyQueue:
|
case task := <-p.verifyQueue:
|
||||||
tasks = append(tasks, task)
|
tasks = append(tasks, task)
|
||||||
default:
|
default:
|
||||||
break
|
goto exitLoop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
exitLoop:
|
||||||
|
|
||||||
if len(tasks) == 0 {
|
if len(tasks) == 0 {
|
||||||
return
|
return
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user