esp32-c3-adblock 自定义上游 DNS:一行编译参数切换全网解析出口
【免费下载链接】esp32-c3-adblockPi-hole-class DNS ad-blocker on a $2 ESP32-C3 (no PSRAM): 537k domains as 40-bit FNV-1a hashes in flash, binary-searched. UDP DNS sinkhole + web dashboard. https://youtube.com/shorts/RaxszOUMi8E?feature=share项目地址: https://gitcode.com/GitHub_Trending/es/esp32-c3-adblock
esp32-c3-adblock 是一个跑在约 2 美元 ESP32-C3 上的 Pi-hole 式 DNS 广告拦截器。本文给出它自定义上游 DNS 配置的完整做法:在 platformio.ini 里加一行定义,重新烧录后,整网的解析出口就从默认的 Quad9 换成你指定的任意解析器。
一行改动换解析器:速览
- 改哪里:platformio.ini 中
[env:c3]的build_flags,追加一行-DUPSTREAM_IP=1, 1, 1, 1即指向 Cloudflare。 - 为什么生效:src/main.cpp 里的默认上游(9.9.9.9:53)被
#ifndef包着,编译时定义了同名宏就会自动覆盖默认值,源码保持原样。 - 怎么生效:
pio run -t upload走 USB 烧录,或把固件传到 Web 控制台做 OTA 升级。 - 怎么确认:两条
dig命令——广告域名应返回0.0.0.0,正常域名应返回真实 IP。 - 副作用:无。拦截发生在转发之前,新上游只经手没被拦下的查询。
原理速读:先拦截,后转发
每条 DNS 查询先经过 C3 本地这一关:把域名(连同各级父级后缀逐一)算成 FNV-1a 40 位哈希,再在 Flash 里存的约 53.7 万个哈希上做二分查找。命中就立刻回0.0.0.0掐掉;未命中才把查询转给UPSTREAM_IP指定的解析器,并把它的应答原样传回客户端。
DNS 查询进入 │ ├── 域名命中 Flash 哈希表 ──▶ 直接回 0.0.0.0(拦截,上游完全不参与) │ └── 未命中 ──▶ 转发给上游解析器 ──▶ 把上游应答传回客户端 UPSTREAM_IP : UPSTREAM_PORT把 C3 理解成前台:广告流量在门口就被拒之门外,只有"正常访客"才会被转交给指定的接待方——也就是上游。因此上游的速度和政策决定了全网的浏览体验,而拦截效果则与它无关。
上游解析器怎么选:四个常见候选
| 解析器 | 地址 | 取向 | 适合谁 |
|---|---|---|---|
| Quad9(项目默认) | 9.9.9.9 | 偏安全,自带 DGA 恶意域名与 DNS 隧道封堵 | 默认就多一层恶意域名过滤 |
| Cloudflare | 1.1.1.1 | 速度与隐私见长,不记录客户端 IP | 要响应快、日志负担轻 |
| Cloudflare 家庭版 | 1.1.1.2 | 在 1.1.1.1 之上加成人内容过滤 | 有青少年的家庭 |
| 8.8.8.8 | 全球节点多,延迟普遍低 | 网络环境多变、求稳 |
⚠️ 共同前提:上游地址必须能被 C3 直接访问。家用宽带下,上述公共解析器均可直连,不涉及端口转发。
实操:修改 UPSTREAM_IP 并烧录固件
给 build_flags 追加一行编译定义
- 改什么:一个
-DUPSTREAM_IP宏。 - 在哪改:platformio.ini 的
[env:c3]段。 - 改完预期:编译通过,
.pio/build/c3/下生成新的firmware.bin。
实际改动只有一行,其余保持原样:
[env:c3] board = esp32-c3-devkitm-1 build_flags = -DARDUINO_USB_MODE=1 -DARDUINO_USB_CDC_ON_BOOT=1 -DUPSTREAM_IP=1, 1, 1, 1 ; 上游指向 Cloudflare想切回 Quad9 就写9, 9, 9, 9;上游跑在非标准端口(如 5353)时再加一行-DUPSTREAM_PORT=5353——端口默认是 53,多数情况不用写。
两条烧录路径:USB 直刷与控制台 OTA
- 做什么:把新固件写进 C3。
- 在哪做:项目根目录执行命令,或 Web 控制台(http://c3adblock.local)的Firmware → OTA update区域。
- 做完预期:设备重启后回到控制台界面,随后的 dig 查询已走新上游。
USB 路径,在项目根目录执行:
pio run -t uploadOTA 路径适合设备已在线、不想拔线的场景:打开控制台,输入 Web 密码,把刚构建出的.pio/build/c3/firmware.bin拖进Firmware → OTA update区域,设备校验通过后自行重启进入新镜像。
用两条 dig 命令确认切换
在能访问 C3 的机器上依次执行(把 IP 换成 C3 实际地址):
dig @<c3-ip> doubleclick.net # 预期 0.0.0.0:广告被拦下 dig @<c3-ip> github.com # 预期真实 IP:由新上游转发解析第一条命中哈希表,根本不会触及上游;第二条能返回真实地址,才说明转发链路确实走了 1.1.1.1。顺手刷新控制台,观察 allowed(放行)计数在涨,是转发存活的旁证。
验证与排错:预期输出和异常对照
| 观察到的现象 | 可能原因 | 排查方向 |
|---|---|---|
doubleclick.net返回真实 IP 而非 0.0.0.0 | 拦截列表为空或损坏(换列表期间设备会 fail-open,全部直转) | 看控制台域名计数是否为 0,重新上传blocklist.bin |
github.com超时无应答 | 上游不可达或端口写错,转发 1 秒超时后放弃 | 确认 C3 到上游的网络;核对-DUPSTREAM_PORT设置 |
github.com返回 0.0.0.0 | 该域名本身也在拦截列表,或客户端缓存了旧应答 | 换其他域名再测;清掉客户端 DNS 缓存 |
dig @<c3-ip>连不上 | C3 掉线或 IP 变了 | 从路由器或控制台查当前 IP,确认设备在网 |
若以上都不像,接串口看启动日志(pio device monitor),里面会打印 WiFi 状态和拦截列表域名数,定位最快。
延伸:配网、列表更新与相关文件
上游换了,下面三套机制照常运转:
- 拦截与转发解耦:命中哈希表的查询在门口就被拒,到不了上游;转发链路还会丢弃过期应答、严格按事务 ID 匹配,防止多个客户端的应答串包。
- WiFi 配网保留:首次连不上网时设备放出热点
C3-AdBlock-XXXX,手机连上后在强制门户选网、输密码即可;之后换网络点控制台的Forget WiFi,或上电按住 BOOT 键重新进配网,不用重刷。 - 拦截列表走独立链路:tools/build_blocklist.py 生成
blocklist.bin,经控制台Blocklist → Upload上传,或配置Remote auto-update让设备按小时定期自行拉取,与上游 DNS 互不干涉。
想往下看的话:主逻辑在 src/main.cpp(上游定义就在文件开头),仪表盘界面在 src/page.h,完整文档与构建细节见 README.md。
整网的解析出口,说到底就是这一个宏——编译参数落定之后,剩下的交给控制台和 OTA 就好。
【免费下载链接】esp32-c3-adblockPi-hole-class DNS ad-blocker on a $2 ESP32-C3 (no PSRAM): 537k domains as 40-bit FNV-1a hashes in flash, binary-searched. UDP DNS sinkhole + web dashboard. https://youtube.com/shorts/RaxszOUMi8E?feature=share项目地址: https://gitcode.com/GitHub_Trending/es/esp32-c3-adblock
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考