OpenWrt:dnsmasq 因 IDN(非 ASCII 域名)配置语法错误导致 LAN DNS 崩溃的排障记录
说明:本文仅讨论 OpenWrt 上 dnsmasq 的配置兼容性与故障排查,不涉及也不提供任何绕过网络访问限制的使用指导。
关键词:OpenWrt / dnsmasq / IDN / punycode / crash loop / /tmp/dnsmasq.d
结论先行:某些组件会自动生成 dnsmasq 片段配置;当片段里出现 中文域名(IDN,非 ASCII) 时,可能触发 dnsmasq 语法错误 → 启动失败 → LAN 侧 DNS 解析整体不可用,用户侧观感就是“全网像断网”。
1 分钟快速定位(遇到“启用某组件后全网像断网”先看这个)
- 先确认是不是 DNS 服务本身挂了:
logread -e dnsmasq
- 如果你看到类似关键词,基本就是 dnsmasq 在 crash loop:
error at line ... of /tmp/dnsmasq.d/...FAILED to start updnsmasq in a crash loop
- 直接打开日志里提示的那个文件路径,检查是否出现非 ASCII/中文域名行:
# 把下面路径替换成 logread 里显示的实际路径
sed -n '1,120p' /tmp/dnsmasq.d/xxx.conf
- 临时删掉/过滤掉“非 ASCII 域名”那几行后,重启 dnsmasq:
/etc/init.d/dnsmasq restart
logread -e dnsmasq
目录
- 一、问题背景
- 二、现象与初步判断
- 三、关键突破:dnsmasq crash loop
- 四、定位崩溃源头:自动生成的 dnsmasq 片段
- 五、根因分析:IDN(中文域名)触发 dnsmasq 语法错误
- 六、紧急止血:先让 dnsmasq 活过来
- 七、长期方案:输入域名全部转为 ASCII(punycode)或做过滤
- 八、复发预防清单
- 九、排障命令速查表
一、问题背景
OpenWrt 上常见的网络能力(例如:域名策略、按域名打标、基于 ipset/规则集的分流等)经常会借助 dnsmasq 的片段配置实现:
- 在
/tmp/dnsmasq.d/下动态生成额外配置 - dnsmasq 启动时会加载这些片段
这类“自动生成配置”的机制很方便,但也带来一个典型风险:生成内容不符合 dnsmasq 语法 → dnsmasq 直接起不来。
二、现象与初步判断
1)现象
启用某个“会生成 dnsmasq 片段配置”的组件后,局域网内出现:
- 解析域名失败(大部分网站/服务都打不开)
- 终端表现像“断网”,但物理链路、DHCP 可能仍是好的
- 路由器页面/组件页面可能提示 “DNS 异常”
2)初步判断
如果你确认:
- WAN 拨号/上网正常(路由器本身能 ping 通 IP)
- 但“所有依赖域名的访问”都失败
优先把排障焦点放在 LAN DNS(dnsmasq)是否正常运行,而不是先去改网络出口或其它组件参数。
三、关键突破:dnsmasq crash loop
查看 dnsmasq 日志:
logread -e dnsmasq
如果反复出现:
error at line ... of /tmp/dnsmasq.d/...FAILED to start updnsmasq in a crash loop
关键结论:dnsmasq 没有成功启动。
在 OpenWrt 的默认架构下:
- dnsmasq 通常承担 LAN 侧 DNS(以及常见的 DHCP/DNS 缓存能力)
- dnsmasq 一旦挂掉 ⇒ 整个局域网域名解析“归零”
- 用户侧体验就会非常像“全网断网”
四、定位崩溃源头:自动生成的 dnsmasq 片段
日志里一般会指出具体文件,比如:
/tmp/dnsmasq.d/<某目录>/<某文件>.conf- 或者
/tmp/dnsmasq.d/<某文件>.conf
直接打开对应文件,查看报错行附近内容:
# 示例:查看前 120 行,必要时扩大范围
sed -n '1,120p' /tmp/dnsmasq.d/xxx/001-server.conf
常见会看到类似规则(示例仅为说明语法形态):
server=/.example.com/127.0.0.1#5353
ipset=/.example.com/some_set
五、根因分析:IDN(中文域名)触发 dnsmasq 语法错误
当自动生成的片段里出现 非 ASCII 域名(例如中文域名),就可能触发 dnsmasq 语法错误,导致启动失败。
示例(仅示意问题,不建议这样写):
server=/.例子.测试/127.0.0.1#5353
ipset=/.例子.测试/some_set
为什么会出错
- dnsmasq 的域名匹配规则在很多场景下要求域名为 ASCII(或以 punycode:
xn--...形式表达 IDN) - 直接把中文域名“裸写进配置片段”,部分版本/编译选项下会被解析为非法字符,进而报语法错误
- 一旦有 任意一个加载的片段配置语法错误,dnsmasq 可能直接拒绝启动
六、紧急止血:先让 dnsmasq 活过来
目标:先恢复 LAN DNS,避免“全网像断网”。
1)快速定位“非 ASCII 行”
用 awk 找出包含非 ASCII 字符的行(BusyBox 一般可用):
awk '($0 !~ /^[[:print:][:space:]]*$/) || ($0 ~ /[^\x00-\x7F]/) {print NR ":" $0}' /tmp/dnsmasq.d/xxx/001-server.conf
或直接粗暴搜索(如果系统 grep 支持):
grep -n '[^ -~]' /tmp/dnsmasq.d/xxx/001-server.conf
2)临时过滤掉包含非 ASCII 的行(不改其它内容)
建议先备份一份:
cp /tmp/dnsmasq.d/xxx/001-server.conf /tmp/001-server.conf.bak
然后过滤(保留纯 ASCII 行):
awk '/^[\x00-\x7F]*$/' /tmp/dnsmasq.d/xxx/001-server.conf > /tmp/001-server.conf.fixed
mv /tmp/001-server.conf.fixed /tmp/dnsmasq.d/xxx/001-server.conf
对其它同类片段文件同样处理(以日志里点名的文件为准)。
3)重启 dnsmasq 并验证
/etc/init.d/dnsmasq restart
logread -e dnsmasq
看到类似 Connected to system UBus / started 之类信息,通常说明已恢复。
七、长期方案:输入域名全部转为 ASCII(punycode)或做过滤
临时改 /tmp/... 的文件通常会复发,因为这些文件经常是运行时重新生成的。
更稳的思路有两条:
方案 A:源头治理(推荐)
确保“会被写入 dnsmasq 片段”的域名列表 全部为 ASCII / punycode。
你可以在 PC 上把 IDN 转 punycode(举例):
例子.测试→xn--fsqu00a.xn--0zwm56d(示例)
然后把组件侧的域名源(规则/列表/自定义域名等)统一替换成 punycode 形式。
方案 B:生成后过滤(折中)
如果你无法控制源头(例如某些列表来自外部、或组件每次都会自动写入),可以在“dnsmasq 启动前/网络事件后”做一次过滤,保证 dnsmasq 永远不因非 ASCII 行而启动失败。
示例:过滤某目录下的片段(仅作思路演示)
注意:你需要把
TARGET_DIR换成你设备上实际的生成目录(从logread -e dnsmasq获取)。
TARGET_DIR="/tmp/dnsmasq.d/xxx"
for f in "$TARGET_DIR"/*.conf; do
[ -f "$f" ] || continue
awk '/^[\x00-\x7F]*$/' "$f" > "$f.ascii" && mv "$f.ascii" "$f"
done
/etc/init.d/dnsmasq restart
八、复发预防清单
- 每次“像断网”先看
logread -e dnsmasq,确认是不是 crash loop - 以日志点名的
/tmp/dnsmasq.d/...文件为准定位问题片段 - 检查片段里是否出现 IDN / 非 ASCII
- 长期把域名源统一为 punycode(xn--...)
- 如无法源头治理,用“生成后过滤”确保 dnsmasq 可启动
- 任何自动化脚本都建议先备份、并限定作用范围(只动特定目录/特定文件)
九、排障命令速查表
dnsmasq 日志
logread -e dnsmasq
查看报错文件内容(以日志提示路径为准)
sed -n '1,120p' /tmp/dnsmasq.d/xxx/xxx.conf
找出非 ASCII 行
grep -n '[^ -~]' /tmp/dnsmasq.d/xxx/xxx.conf
# 或(更通用)
awk '($0 ~ /[^\x00-\x7F]/) {print NR ":" $0}' /tmp/dnsmasq.d/xxx/xxx.conf
仅保留 ASCII 行(临时止血)
awk '/^[\x00-\x7F]*$/' /tmp/dnsmasq.d/xxx/xxx.conf > /tmp/xxx.fixed
mv /tmp/xxx.fixed /tmp/dnsmasq.d/xxx/xxx.conf
重启 dnsmasq
/etc/init.d/dnsmasq restart
评论区