先简单介绍下家庭组网情况,目前主要是使用一台铭凡 MS-01 作为 All in Boom 的硬件载体。MS-01 自带两个 10G SFP 口,大大增加了可玩性。为了摆脱厚重的运营商光猫,我购置了 10G EPON 猫棒(剑桥 XE-99S)供 MS-01 直接拨号上网。MS-01 的另一个 SFP 口通过 DAC 线缆下联兮克的 2.5G 交换机,再外挂多个小米、红米的 BE7000、AX6000 等无线路由设备,构成完整的家庭网络。
为充分利用 MS-01 的性能,我安装了 PVE(Proxmox Virtual Environment)作为其虚拟化平台,内部主要运行以下几个虚拟实例:
作为一个坚定的 Surge 用户,长期以来我并不需要过多折腾家庭 DNS 方案,因为 Surge 已经能很好地处理 DNS 解析和流量管理。但在日常使用过程中,确实遇到了部分家庭设备(Linux、Windows)需要正常访问外网的情况,因此我曾添加了一个 ImmortalWrt 实例来解决这个问题。
然而,ImmortalWrt 的各种插件相对混乱,大多提供整套解决方案,出现问题时不易排查,有时会遇到无法正常访问互联网的情况。于是我开始寻找更简单可控的替代方案,最终找到了性能更好(却并不十分知名)的 DAE。
DAE 在内核层面对流量进行分流,能有效提高直连流量的性能。由于 DAE 使用的不是我熟悉的 Fake IP 模式,出于 DNS 配置的洁癖,我开始尝试搭建无污染的家庭 DNS 服务。
在开始构建 DNS 方案前,我的需求非常明确:
经过一段时间的调研和实践,我最终选择了 AdGuardHome 作为前置广告过滤器,SmartDNS 作为主 DNS 请求服务的组合方案。
虽然几乎所有 DNS 服务器都具备广告域名过滤能力,但 AdGuardHome 作为一个成熟稳定的广告过滤工具,具有以下优势:

目前网上流行的 DNS 服务器主要有两个选择:SmartDNS 和 MosDNS。
| 功能 | SmartDNS | MosDNS |
|---|---|---|
| 简介 | 高性能 DNS 解析加速工具,提供多服务器查询测速最优 IP | 模块化 DNS 解析工具,适用于复杂 DNS 需求 |
| 主要特点 | 支持多服务器并发查询,测速返回最优的结果 | 规则引擎灵活,支持 DNS 路由降级等高级功能 |
SmartDNS 的一个巧妙设计是:当对多个 DNS 服务器并发查询新域名时,它会先返回 TTL=3 秒的最快结果,确保客户端能够第一时间收到回答。同时,它会对所有 DNS 服务器返回的结果进行测速(通过 TCP 或 ICMP),缓存其最优结果。当 3 秒后客户端 DNS 记录过期,再次发起查询时,SmartDNS 就会直接返回缓存里的最优结果。
在短时间试用两者后,考虑到个人需求,我最终选择了 SmartDNS。在我的使用场景下,可能并不需要 DNS 降级等相对复杂的逻辑,而 DNS 测速功能则更为重要。
我个人认为,不必过度关注 DNS 泄漏问题,更应该关注 DNS 解析结果的准确性与稳定性。虽然提及 DNS 泄漏这个话题可能会引发无休止的争论,但我认为:如果第三方真的要分析你的流量,只需嗅探 TLS 数据包就能做到,没必要为了保证 DNS 100% 无泄漏而牺牲 DNS 解析的速度和体验。对我而言,只需确保敏感域名不在国内递归 DNS 服务器进行解析即可。
结合 SmartDNS 的特性和我的实际需求,我的 DNS 请求策略如下:
AdGuardHome 的配置相对简单:
127.0.0.1:153(指向 SmartDNS)我将 SmartDNS 内的 DNS 服务器分成三组:
我使用以下域名列表来区分不同类型的域名:
| 列表 | 说明 | 链接 |
|---|---|---|
| direct-list | 直连列表 | https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/direct-list.txt |
| cn-apple-list | 中国大陆的 apple 域名列表,日常使用苹果设备比较多,单独处理下 | https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/apple-cn.txt |
| cn-cdn-list | 国内 CDN 域名列表 | https://raw.githubusercontent.com/pmkol/easymosdns/main/rules/cdn_domain_list.txt |
| proxy-list | 国外域名列表 | https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/proxy-list.txt |
| gfw-list | 国外敏感域名列表 | https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/gfw.txt |
注意:下载的域名列表文件需要进行处理,以符合 SmartDNS 的规则格式:
full: 替换为 -.domain:regexp: 的行,直接忽略为确保 SmartDNS 的规则保持最新,可以编写定时任务定期更新这些列表文件,并重启 SmartDNS 服务使更新生效。
简单写了一个脚本用于每天拉取最新的规则,处理后如果发现有更新,就重启 SmartDNS。简单配置在 crontab 中,每天更新一次。
这套 DNS 配置方案经过实际使用,能够很好地满足我对家庭网络的需求:干净无广告、响应迅速、解析精准。通过组合使用 AdGuardHome 和 SmartDNS,既保证了广告过滤的效果,又能够智能选择最佳的 DNS 解析结果。
当然,每个人的网络环境和需求不同,可以根据自己的实际情况进行调整和优化。欢迎和我一起讨论~
# Aliyun
server 223.5.5.5 -group direct
# DNSPod
server 119.29.29.29 -group direct
# Cloudflare ZeroTrust
server-https https://xxxx.cloudflare-gateway.com/dns-query -group proxy
server 1.1.1.1 -group global-lite
server 1.0.0.1 -group global-lite
log-level warn
bind :153
cache-file /etc/smartdns/smartdns.cache
cache-persist yes
cache-checkpoint-time 21600
prefetch-domain yes
force-qtype-SOA 65
force-AAAA-SOA yes
dualstack-ip-selection no
tcp-idle-time 300
server 223.5.5.5 -bootstrap-dns
# --- Direct Server 列表 --- #
# Aliyun
server 223.5.5.5 -group direct
# DNSPod
server 119.29.29.29 -group direct
# --- Proxy Server 列表 --- #
server-https https://xxxx.cloudflare-gateway.com/dns-query -group proxy
# --- global-lite Server 列表 --- #
server 1.1.1.1 -group global-lite
server 1.0.0.1 -group global-lite
# --- 域名配置列表 --- #
domain-set -t list -name direct-list -file /etc/smartdns/rules/direct-list.txt
domain-set -t list -name cn-apple-list -file /etc/smartdns/rules/cn-apple-list.txt
domain-set -t list -name cn-cdn-list -file /etc/smartdns/rules/cn-cdn-list.txt
domain-set -t list -name proxy-list -file /etc/smartdns/rules/proxy-list.txt
domain-set -t list -name gfw-list -file /etc/smartdns/rules/gfw-list.txt
# 本地域名只请求 direct,测速并使用最优结果
domain-rules /domain-set:cn-apple-list/ -nameserver direct
domain-rules /domain-set:cn-cdn-list/ -nameserver direct
domain-rules /domain-set:direct-list/ -nameserver direct
# 国外域名只请求 proxy,测速并使用最优结果
domain-rules /domain-set:proxy-list/ -nameserver proxy
# 国外敏感域名不要测速,只请求 proxy;注意顺序,放最后覆盖前面的配置
domain-rules /domain-set:gfw-list/ -nameserver proxy -speed-check-mode none
# 一些调用非常频繁的请求,直接用 global-lite
domain-rules /in-addr.arpa/ -nameserver direct
domain-rules /time.apple.com/ -nameserver global-lite
domain-rules /time.asia.apple.com/ -nameserver global-lite
domain-rules /pool.ntp.org/ -nameserver global-lite
domain-rules /time-ios.apple.com/ -nameserver global-lite
# 兜底:剩下的域名请求 proxy 与 direct,测速并使用最优结果
#!/usr/bin/env python3
import os
import sys
import shutil
import requests
from datetime import datetime
from pathlib import Path
def log(msg):
print(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - {msg}")
def process_url(url, filename):
script_dir = Path(__file__).parent.absolute()
rules_dir = script_dir / "rules"
rules_dir.mkdir(exist_ok=True)
log(f"Downloading URL: {url}")
try:
resp = requests.get(url)
resp.raise_for_status()
except requests.exceptions.RequestException as e:
log(f"Failed to download URL: {url} (Error: {str(e)})")
return False
log("Processing file content...")
processed_lines = []
for line in resp.text.splitlines():
if line.startswith("full:"):
processed_lines.append(line.replace("full:", "-."))
elif line.startswith("domain:"):
processed_lines.append(line.replace("domain:", ""))
elif line.startswith("regexp:"):
continue
else:
processed_lines.append(line)
if not processed_lines:
log("New file is empty. No update needed.")
return False
target_file = rules_dir / filename
new_content = "\n".join(processed_lines) + "\n"
if target_file.exists():
with open(target_file) as f:
old_content = f.read()
if old_content == new_content:
log("No differences found. No update needed.")
return False
log(f"{'Updating' if target_file.exists() else 'Creating'} file: {target_file}")
with open(target_file, "w") as f:
f.write(new_content)
return True
def main():
urls = [
("https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/direct-list.txt", "direct-list.txt"),
("https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/apple-cn.txt", "cn-apple-list.txt"),
("https://raw.githubusercontent.com/pmkol/easymosdns/main/rules/cdn_domain_list.txt", "cn-cdn-list.txt"),
("https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/proxy-list.txt", "proxy-list.txt"),
("https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/gfw.txt", "gfw-list.txt")
]
updated = False
for url, filename in urls:
if process_url(url, filename):
updated = True
print("-" * 18)
if updated:
log("Updates detected. Restart smartdns")
os.system("service smartdns restart")
else:
log("No updates detected.")
if __name__ == "__main__":
main()