抛弃MosDNS套娃分流 AdGuardDNS分流又快又好
也算是用了三年软路由了,从一开始的Smartdns到MosDNS,一直在DNS泄露的问题上弯弯绕绕,浪费了许多时间。尤其是使用MosDNS→AdGuardHome方案后,抖音、哔哩哔哩经常性出现加载卡顿的问题。经过排除,确定了卡顿是由于MosDNS分流所导致的一些CDN地址无法解析,随决定抛弃MosDNS,完全转入AGH分流的怀抱。
个人网络环境
家里的网络环境很简单,运营商光纤接入→光猫→路由→旁路由,路由器拨号,光猫仅负责光电转换;旁路由的意义一方面是方便我用网,另一方面是补全家用网络环境下的一些功能。
分流思路
同MosDNS的分流思路,但是由于AGH在匹配规则方面与MosDNS有所出入,因此是无法直接引用MosDNS的分流文件来进行使用的,另一方面是规则会定期更新的,因此手动组合难度较大,所以是需要本地脚本进行处理的。
分流规则使用的Loyalsoldier发布的规则列表。
配置流程
脚本运行目录是在 /root/dnsrules
下的,在根目录创建名为 rules2agh.sh
的文件,并将下面的文件内容贴入。
#!/usr/bin/env bash
# rules2agh.sh (one-domain-per-line)
# - 源:Loyalsoldier/v2ray-rules-dat(raw + jsDelivr 双源)
# - 输出:
# 1) /root/dnsrules/adguard.upstreams.txt (AdGuard Home upstream_dns_file)
# 2) /root/dnsrules/adguard.blocklist.txt (Adblock 语法 ||domain^)
# - 特性:逐域一行,不打包;过滤超长/非法域名;日志走 stderr
# 依赖:bash、curl、sed、awk、sort、uniq
set -Eeuo pipefail
BASE_DIR="/root/dnsrules"
WORK_DIR="$BASE_DIR/work"
UPSTREAM_FILE="$BASE_DIR/adguard.upstreams.txt"
BLOCKLIST_FILE="$BASE_DIR/adguard.blocklist.txt"
LOG_PREFIX="[rules2agh]"
mkdir -p "$WORK_DIR"
# === 上游设置 ===
DIRECT_UPSTREAM="tcp://223.5.5.5" # 国内
PROXY_UPSTREAM="tls://1.1.1.1" # 国外
REVERSE_UPSTREAM="$DIRECT_UPSTREAM" # 反向解析走国内
# --- 源清单 ---
SRC=(
"direct-list|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/direct-list.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/direct-list.txt"
"proxy-list|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/proxy-list.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/proxy-list.txt"
"reject-list|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/reject-list.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/reject-list.txt"
"china-list|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/china-list.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/china-list.txt"
"apple-cn|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/apple-cn.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/apple-cn.txt"
"google-cn|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/google-cn.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/google-cn.txt"
"gfw|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/gfw.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/gfw.txt"
"win-spy|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/win-spy.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/win-spy.txt"
"win-update|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/win-update.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/win-update.txt"
"win-extra|https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/win-extra.txt|https://cdn.jsdelivr.net/gh/Loyalsoldier/v2ray-rules-dat@release/win-extra.txt"
)
log() { echo "[$(date +'%F %T')] $LOG_PREFIX $*" >&2; }
fetch_one() {
local tag="$1"; shift
local dst="$WORK_DIR/$tag.raw"
local ok=0
for url in "$@"; do
log "GET $url"
if curl -fsSL -L --connect-timeout 5 --max-time 25 --retry 2 -o "$dst" "$url"; then
ok=1; break
fi
done
[[ $ok -eq 1 ]] || { log "下载失败:$tag"; return 1; }
printf '%s\n' "$dst"
}
normalize_domains_file() {
local in="$1" out="$2"
awk '
{ gsub(/\r$/, "", $0); line=$0; }
{ sub(/#.*/, "", line); gsub(/^[ \t]+|[ \t]+$/, "", line); }
line=="" { next }
/^domain:/ { print substr(line,8); next }
/^full:/ { print substr(line,6); next }
/^(regexp:|keyword:|include:|exception:|attr:)/ { next }
{ gsub(/^\|\|/, "", line); gsub(/\^$/, "", line); print line; }
' "$in" \
| sed -E 's/^\*\.//; s/^\.+//' \
| sed -E 's/[^0-9A-Za-z._-]//g' \
| sed '/^[._-]*$/d' \
| sed 's/\.$//' \
| tr 'A-Z' 'a-z' \
| sort -u > "$out"
}
merge_sets() { cat "$@" | sort -u; }
filter_too_long_or_invalid() {
local in="$1" out="$2"
awk 'length($0)>0 && length($0)<=240 {print}' "$in" | sort -u > "$out"
}
write_one_per_line() {
local domains_file="$1" upstream="$2"
while IFS= read -r d || [[ -n "$d" ]]; do
[[ -z "$d" ]] && continue
printf "[/%s/]%s\n" "$d" "$upstream"
done < "$domains_file"
}
make_blocklist() {
awk '{print "||"$0"^"}' "$1"
}
main() {
log "下载源列表..."
declare -A RAW
for item in "${SRC[@]}"; do
IFS='|' read -r tag url1 url2 <<< "$item"
RAW["$tag"]="$(fetch_one "$tag" "$url1" "$url2")"
done
log "规范化各列表..."
declare -A NORM
for tag in "${!RAW[@]}"; do
normalize_domains_file "${RAW[$tag]}" "$WORK_DIR/$tag.norm"
filter_too_long_or_invalid "$WORK_DIR/$tag.norm" "$WORK_DIR/$tag.good"
NORM["$tag"]="$WORK_DIR/$tag.good"
log "$tag: $(wc -l < "${NORM[$tag]}" | tr -d ' ') 条"
done
# --- 集合定义 ---
merge_sets "${NORM[direct-list]}" "${NORM[china-list]}" "${NORM[apple-cn]}" "${NORM[google-cn]}" "${NORM[win-update]}" \
> "$WORK_DIR/DIRECT.all"
merge_sets "${NORM[proxy-list]}" "${NORM[gfw]}" \
> "$WORK_DIR/PROXY.all"
merge_sets "${NORM[reject-list]}" "${NORM[win-spy]}" "${NORM[win-extra]}" \
> "$WORK_DIR/BLOCK.all"
log "生成 upstream 文件:$UPSTREAM_FILE"
{
echo "# Generated by rules2agh(one-domain-per-line) on $(date -Is)"
echo "# 默认上游(未匹配域名) -> $PROXY_UPSTREAM"
echo "$PROXY_UPSTREAM"
echo
echo "# 可选:反向解析 -> 直连上游"
echo "[/in-addr.arpa/]$REVERSE_UPSTREAM"
echo "[/ip6.arpa/]$REVERSE_UPSTREAM"
echo
echo "# === DIRECT -> $DIRECT_UPSTREAM ==="
write_one_per_line "$WORK_DIR/DIRECT.all" "$DIRECT_UPSTREAM"
echo
echo "# === PROXY -> $PROXY_UPSTREAM ==="
write_one_per_line "$WORK_DIR/PROXY.all" "$PROXY_UPSTREAM"
} > "$UPSTREAM_FILE"
log "生成 blocklist:$BLOCKLIST_FILE"
{
echo "! Generated by rules2agh on $(date -Is)"
echo "! 拦截集合:reject + win-spy + win-extra"
make_blocklist "$WORK_DIR/BLOCK.all"
} > "$BLOCKLIST_FILE"
log "完成。统计:"
echo " DIRECT: $(wc -l < "$WORK_DIR/DIRECT.all")" >&2
echo " PROXY : $(wc -l < "$WORK_DIR/PROXY.all")" >&2
echo " BLOCK : $(wc -l < "$WORK_DIR/BLOCK.all")" >&2
/etc/init.d/AdGuardHome restart
}
main "$@"
该脚本引用了L大的以下列表,并在脚本中一一对应,可以根据自己需求添加或删减对应部分。
直连域名列表 direct-list.txt
——DIRECT直连——direct-list
代理域名列表 proxy-list.txt
——代理PROXY——proxy-list
广告域名列表 reject-list.txt
——BLOCK阻断——reject-list
@felixonmars/dnsmasq-china-list 仓库收集的在中国大陆可直连的域名列表 china-list.txt
——DIRECT直连——china-list
Apple 在中国大陆可直连的域名列表 apple-cn.txt
——DIRECT直连——apple-cn
Google 在中国大陆可直连的域名列表 google-cn.txt
——DIRECT直连——google-cn
GFWList 域名列表 gfw.txt
——代理PROXY——gfw
Windows 操作系统使用的隐私跟踪域名列表 win-spy.txt
——BLOCK阻断——win-spy
Windows 操作系统使用的系统升级域名列表 win-update.txt
——DIRECT直连——win-update
Windows 操作系统使用的附加隐私跟踪域名列表 win-extra.txt
——BLOCK阻断——win-extra
由于 mapfile -t CN_DOMAINS < <(parse_category_recursive "$CN_LIST" | sed 's/\r$//')
语法问题, dash
不支持< <(...)
此类进程替换语法,因此运行时需要使用 bash
运行。
#赋权
chmod +x /root/dnsrules/geosite2agh.sh
#运行
bash /root/dnsrules/geosite2agh.sh
不出意外的话会返回以下内容并重启AGH服务
root@iStoreOS:~# bash /root/dnsrules/rules2agh.sh
[2025-08-15 22:51:00] [rules2agh] 下载源列表...
[2025-08-15 22:51:00] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/direct-list.txt
[2025-08-15 22:51:02] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/proxy-list.txt
[2025-08-15 22:51:04] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/reject-list.txt
[2025-08-15 22:51:06] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/china-list.txt
[2025-08-15 22:51:08] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/apple-cn.txt
[2025-08-15 22:51:09] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/google-cn.txt
[2025-08-15 22:51:10] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/gfw.txt
[2025-08-15 22:51:11] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/win-spy.txt
[2025-08-15 22:51:12] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/win-update.txt
[2025-08-15 22:51:14] [rules2agh] GET https://raw.githubusercontent.com/Loyalsoldier/v2ray-rules-dat/release/win-extra.txt
[2025-08-15 22:51:15] [rules2agh] 规范化各列表...
[2025-08-15 22:51:15] [rules2agh] win-update: 538 条
[2025-08-15 22:51:15] [rules2agh] google-cn: 142 条
[2025-08-15 22:51:15] [rules2agh] gfw: 5882 条
[2025-08-15 22:51:15] [rules2agh] proxy-list: 31163 条
[2025-08-15 22:51:15] [rules2agh] apple-cn: 167 条
[2025-08-15 22:51:17] [rules2agh] china-list: 118747 条
[2025-08-15 22:51:17] [rules2agh] win-extra: 399 条
[2025-08-15 22:51:19] [rules2agh] direct-list: 119387 条
[2025-08-15 22:51:21] [rules2agh] reject-list: 131727 条
[2025-08-15 22:51:21] [rules2agh] win-spy: 347 条
[2025-08-15 22:51:22] [rules2agh] 生成 upstream 文件:/root/dnsrules/adguard.upstreams.txt
[2025-08-15 22:51:25] [rules2agh] 生成 blocklist:/root/dnsrules/adguard.blocklist.txt
[2025-08-15 22:51:25] [rules2agh] 完成。统计:
DIRECT: 120445
PROXY : 31348
BLOCK : 132453
warn ip6tables nat mod is needed
AdGuardHome service disabled
workdir is a tmpfs filesystem
AdGuardHome service enabled
luci enable switch=1
如果出现错误,优先考虑是否是当前系统缺少依赖,如果出现某一条依赖导致的报错,可以安装依赖后再次运行。
成功运行后,可以看到根目录有了 adguard.blocklist.txt
和 adguard.upstreams.txt
两个文件,接下来就需要将 /root/dnsrules/adguard.upstreams.txt
写入 AdGuardHome.yaml
的 dns.upstream_dns_file
位置。如果你是使用iStore一键安装的AGH,那么只需要在UI界面的“服务——AdGuard Home——手动设置”中找到 dns.upstream_dns_file
,将文件路径添加在后面,再次运行脚本或者ssh /etc/init.d/AdGuardHome restart
重启AGH即可调用该规则分流。
此外,该脚本会基于L大 “广告域名列表 reject-list.txt” 的规则在根目录生成一个 adguard.blocklist.txt
文件,该文件可以在AGH的DNS黑名单中被加载使用,也可以酌情考虑是否使用该广告拒绝列表。
最后,在Openwrt的Crontab中加入自动执行该脚本的计划任务。 0 12 * * * /root/dnsrules/rules2agh.sh >> /root/dnsrules/work/update.log 2>&1
/etc/init.d/cron enable
/etc/init.d/cron restart
保存,并重启cron服务。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。