所有代码都有claude编写 未提供产物,需要自行从源码构建 所有的功能都是以自己的需求为准,想用但觉得不好用欢迎pr或fork
简介
自动 DNS 代理服务器,支持域名分类、GeoIP 路由、ECS、多级缓存和自动回退。
特性
- 自动路由 - 对未分类的域名自动进行分类,记录
- GeoIP/ASN 匹配 - IP 验证和地理位置判断
- CNAME 链缓存 - RR 级别缓存,支持 CNAME 链部分命中
- ECS 支持 - EDNS Client Subnet,可按组配置
- 并发回退 - proxy_ecs_fallback 策略并发查询多个上游,自动选择最优结果
- 多级缓存 - DNS 缓存和域名分类缓存,支持 Redis 和内存两种后端
- 代理支持 - 上游 DNS 和文件下载支持 SOCKS5 代理
- 自动更新 - 定时更新域名分类和 GeoIP 数据库
- 高性能 - Singleflight 去重,连接池复用
核心

域名分类
域名分类存储在 dlc.dat(来自 v2ray/domain-list-community),支持三种匹配方式:
- 完整匹配 -
example.com只匹配example.com - 域名匹配 -
domain:example.com匹配example.com和所有子域名 - 关键字匹配 -
keyword:google匹配包含google的所有域名
分类缓存支持两种模式:
- 按需查询(默认)- 查询时从 dlc.dat 读取,结果缓存到 Redis/内存
- 预加载(
-load模式)- 启动时将所有分类加载到 Redis
查询策略
每个域名分类对应一个查询策略,策略指定:
- 上游组 - 使用哪个 upstream_group
- 策略选项 - disable_cache, disable_https, ecs, expected_ips, fallback_group 等
特殊策略
block - 阻断域名,返回指定类型的响应:
nxdomain- NXDOMAIN(域名不存在)noerror- NOERROR(空响应)0.0.0.0- 返回 0.0.0.0
proxy_ecs_fallback - 并发查询策略(未分类域名的默认策略):
- 并发查询
proxy_ecs和proxy组 - 检查
proxy_ecs结果的 IP 是否匹配fallback.rule(GeoIP 规则) - 如果匹配,查询
direct组并返回 - 否则返回
proxy或proxy_ecs结果 - 自动将域名分类为
direct_site或proxy_site
IP 验证与回退
策略可以配置 expected_ips(GeoIP 规则数组),验证上游返回的 IP:
query_policy:
- name: "cn_site"
group: "proxy"
options:
expected_ips: ["geoip:cn"] # 期望返回国内 IP
fallback_group: "direct" # 如果不是国内 IP,使用 direct 组重查验证逻辑:
- 所有返回的 IP 必须匹配 expected_ips 中的任一规则
- 如果验证失败且配置了
fallback_group,使用该组重新查询(最终结果,不再验证) - 如果没有配置
fallback_group,回退到proxy_ecs_fallback策略
GeoIP 规则
支持的规则格式:
geoip:cn- 国家代码(ISO 3166-1 alpha-2)geoip:private- 私有 IP(10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)asn:13335- ASN 号(自治系统号)
CNAME 链缓存
DNS 缓存按 RR 记录级别存储(不是完整的 DNS 消息),支持 CNAME 链部分命中:
示例: a.com -> b.com -> c.com -> 1.2.3.4
-
第一次查询
a.com,缓存所有 RR:a.com CNAME b.comb.com CNAME c.comc.com A 1.2.3.4
-
c.com A记录过期后再次查询a.com:- 从缓存返回
a.com CNAME b.com和b.com CNAME c.com - 只查询上游
c.com A - 合并结果返回
- 从缓存返回
ECS(EDNS Client Subnet)
ECS 在上游组级别配置:
upstream_group:
proxy_ecs:
nameservers: ["https://dns.google/dns-query"]
ecs_ip: "8.8.8.8" # 固定 ECS IP
proxy_ecs_auto:
nameservers: ["https://dns.google/dns-query"]
ecs_ip: "" # 使用全局默认 ECS(如果启用)
ecs:
enable: true # 全局 ECS 开关
default_ipv4: "8.8.8.8" # 默认 IPv4 ECS
default_ipv6: "2001:4860:4860::8888"
ipv4_prefix: 24 # ECS 前缀长度
ipv6_prefix: 48缓存
DNS 缓存
RR 级别缓存,每条记录独立存储,包含:
- RR 记录内容
- 原始 TTL 和存储时间
- Rcode, AD, RA 标志
支持后端:
- Redis - 跨进程共享,持久化
- Memory - 进程内存,重启丢失
最大 TTL 固定为 24 小时。
域名分类缓存
存储域名到分类的映射(如 google.com -> proxy_site)。
支持后端:
- Redis - TTL 可配置(默认 1 天)
- Memory - 无 TTL 限制
代理支持
支持通过 SOCKS5 代理进行:
- 上游 DNS 查询 - DoH (DNS-over-HTTPS) 和 TCP 协议
- 文件下载 - dlc.dat, Country.mmdb, GeoLite2-ASN.mmdb
每个上游组可以指定不同的 outbound:
upstream_group:
direct:
nameservers: ["223.5.5.5"]
outbound: "direct" # 不使用代理
proxy:
nameservers: ["https://dns.google/dns-query"]
outbound: "proxy" # 使用 SOCKS5 代理
outbound:
- tag: "proxy"
type: "socks5"
enable: true
server: "127.0.0.1"
port: 1080
username: "" # 可选
password: "" # 可选
- tag: "file_download" # 文件下载专用代理
type: "socks5"
enable: true
server: "127.0.0.1"
port: 1080自用配置
# Optimized DNS Server Configuration
# Based on best practices from sing-box, Xray-core, mihomo
# Server Configuration
server:
port: 10053
protocol: udp # or: tcp, both
bind: 0.0.0.0 # Listen address
# Bootstrap DNS for resolving nameserver hostnames
bootstrap:
nameservers:
- 223.5.5.5
- 119.29.29.29
# Upstream DNS Groups
upstream_group:
# Foreign DNS through proxy (no ECS for privacy)
proxy:
nameservers:
- https://1.1.1.1/dns-query
- https://8.8.8.8/dns-query
outbound: hk
# Foreign DNS through proxy with ECS (for CDN optimization)
proxy_ecs:
nameservers:
- https://8.8.8.8/dns-query
- https://8.8.4.4/dns-query
outbound: hk
# Foreign DNS through proxy with ECS (for CDN optimization)
proxy_us:
nameservers:
- https://1.1.1.1/dns-query
- https://8.8.8.8/dns-query
outbound: us
# Domestic DNS (direct connection)
direct:
nameservers:
- 240e:f:a::6
- 61.134.1.4
- 218.30.19.40
- 240e:f:a00b::6
- 223.5.5.5
- 119.29.29.29
outbound: direct
# Outbound (SOCKS5 Proxies)
outbound:
- tag: direct
type: direct # Built-in, no proxy
- tag: file_download
type: socks5
enable: true
server: 10.115.15.1
port: 17890
username: hk
password: hk
- tag: hk
type: socks5
enable: true
server: 10.115.15.1
port: 17890
username: hk
password: hk
- tag: us
type: socks5
enable: true
server: 10.115.15.1
port: 17891
username: us
password: us
# ECS (EDNS Client Subnet) Global Configuration
ecs:
enable: true
default_ipv4: 113.132.219.0/24
default_ipv6: 240e:358:a07:94c2:d589:ebf2:9d2:be0e/64
ipv4_prefix: 24 # Send /24 prefix (hides last octet for privacy)
ipv6_prefix: 64 # Send /64 prefix
# Cache Configuration
cache:
dns_cache:
enable: true
clear: false # IMPORTANT: Don't clear in production
type: redis # or: memory
category_cache:
enable: true
clear: false
type: redis
ttl: 604800 # 7 days (categories rarely change)
# Redis Configuration
redis:
server: 10.115.15.25
port: 6379
database: 8
password: "violet-dns-secure-2024"
max_retries: 3
pool_size: 10
# Domain Categorization Policy
category_policy:
preload:
file: 'https://github.com/v2fly/domain-list-community/releases/latest/download/dlc.dat'
domain_group:
ads_site:
- category-ads-all
ai_site:
- category-ai-chat-!cn
game_domain:
- category-games
proxy_site:
- google
direct_site:
# Query Routing Policy (matched top-down)
query_policy:
# Block ads and trackers
- name: ads_site
group: block
options:
block_type: nxdomain # or: noerror, 0.0.0.0
block_ttl: 60 # Block record TTL in seconds
# Game domains (low latency needed) - name: game_domain
group: direct
options:
strategy: prefer_ipv4
- name: ai_site
group: proxy_us
options:
strategy: prefer_ipv4
disable_https: true
# Foreign sites through proxy
- name: proxy_site
group: proxy
options:
strategy: prefer_ipv4
expected_ips: # Only accept non-CN IPs
- geoip:!cn
- geoip:private
fallback_group: direct
# Domestic sites direct
- name: direct_site
group: direct
options:
strategy: prefer_ipv4
expected_ips:
- geoip:cn
# Unknown domains: intelligent fallback
- name: unknown
group: proxy_ecs_fallback
options:
strategy: prefer_ipv4
auto_categorize: true # Update category_cache based on result
# Fallback Configuration (for proxy_ecs_fallback policy)
fallback:
geoip: 'https://raw.githubusercontent.com/Loyalsoldier/geoip/release/GeoLite2-Country.mmdb'
asn: 'https://raw.githubusercontent.com/Loyalsoldier/geoip/release/GeoLite2-ASN.mmdb'
update: '0 0 7 * * *' # Update at 3 AM daily
strategy: race # Race both proxy_ecs and proxy
rule:
- geoip:cn
- geoip:private
- asn:4134 # China Telecom
- asn:4837 # China Unicom
- asn:9808 # China Mobile
# If IP matches rule → use direct, else use proxy
# Logging Configuration
log:
level: info # 日志级别: debug, info, warn, error
format: json # 日志格式: json (结构化), text (带颜色的文本)
output: stdout # 输出位置: stdout (控制台), file (默认violet-dns.log), 或自定义路径
max_size: 100 # 单个日志文件最大大小 (MB),默认 100MB max_age: 7 # 日志文件保留天数,默认 7 天
max_backups: 10 # 保留的旧日志文件数量,默认 10 个
compress: true # 是否压缩旧日志文件 (gzip) total_size_limit: 500 # 所有日志文件总大小限制 (MB),超过后自动删除最旧的文件构建
未提供产物,需要自行从源码构建
Linux
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go buildOpenWRT
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -trimpath部署
Systemd 服务
[Unit]
Description=violet-dns
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/violet-dns -d /etc/violet-dns
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target使用建议
预先导入category_policy中定义的网站到redis中 此处不需要定义cn和非cn网站,等待后续自动分配即可(因为此种分流必然是不完整且有错误的不如不导入) 只需要定义需要单独分流的域名即可
其余的配置看配文件足够简单明了
violet-dns -d /var/run/violet-dns -load