esp32-c3-adblock:$2芯片承载537k域名的DNS广告拦截器
给家庭网络用的ESP32-C3 DNS拦截器,把域名哈希放进flash而不是RAM。
GitHub M-Abozaid/esp32-c3-adblock 更新 2026-10-06 分支 main 星标 1.3K 分叉 120
C++ Python C DNS广告拦截 ESP32-C3 PlatformIO FNV-1a 家庭网络

🧭 决策指南

适合,如果你

  • 你有4MB ESP32-C3 SuperMini,想在无PSRAM设备上运行约100k默认blocklist。
    README 的 Hardware 和 Build & flash (PlatformIO) 章节:C3 SuperMini、4 MB flash、no PSRAM;默认列表约100k entries。
  • 你需要把DNS拦截器放进路由器USB口,并接受约10ms的哈希查找延迟。
    README 的 Hardware 和 Why this is interesting 章节:USB-A → USB-C dongle可接路由器;lookup约10 ms。
  • 你希望首次USB刷写后,通过c3adblock.local维护blocklist和firmware。
    README 的 Build & flash (PlatformIO) 和 Over-the-air updates 章节:之后firmware与blocklist都可通过WiFi更新。

不适合,如果你

  • 你必须同时使用537k ultimate列表和firmware OTA,而硬件只有4MB flash。
    README 的 Over-the-air updates 章节:OTA需要两个app slots,blocklist约250k上限;537k列表只能使用single-app partition table。
  • 你的威胁模型包含可监听LAN流量的攻击者,且需要加密管理接口。
    README 的 Security 章节:所有流量是plain HTTP on :80,Basic Auth凭据以base64发送,不是加密。
  • 你要把设备直接暴露给不可信网络,或依赖默认CHANGE_ME凭据运行。
    README 的 Security 章节:默认凭据公开;项目明确说明Basic Auth只是LAN trust boundary control。
  • 你只有旧版apt PlatformIO,例如4.3.4。
    README 的 Build & flash (PlatformIO) 章节:distro/apt platformio package,例如4.3.4,会因AttributeError: ... 'resultcallback'失败。

前置条件

  • ESP32-C3 board,tested on a C3 SuperMini,4 MB flash,no PSRAM needed
  • Stable USB source,例如phone charger或router's USB port
  • Current PlatformIO;README指出apt PlatformIO 4.3.4过旧
  • 先配置src/secrets.h中的WEB_USER、WEB_PASS和OTA_PASS
  • 首次USB刷写需要pio run -t upload和pio run -t uploadfs

第一步命令(README 原文)

cp src/secrets.example.h src/secrets.h

要注意

  • 便宜或松动的USB-C→A适配器可能在WiFi发射时造成brownout。
    README 的 Hardware 章节:cheap/loose USB-C→A adapters can brown out the radio during WiFi transmit。
  • 未修改secrets.h会继续启动,但串口告警并显示公开密码横幅。
    README 的 Security 章节:CHANGE_ME_WEB_PASSWORD和CHANGE_ME_OTA_PASSWORD会触发warning,但firmware仍会运行。
  • WiFi setup portal的C3-AdBlock-XXXX接入点仍是开放且未加密。
    README 的 Security 章节:setup portal access point is still open (unencrypted) by design。
  • 自定义/addblock域名虽做HTML转义,网络OTA仍需OTA_PASS。
    README 的 Security 章节:custom names are HTML-escaped;ArduinoOTA requires OTA_PASS。

替代方案

  • 传统ESP32 + PSRAM DNS sinkhole:当你需要把domain strings直接放进RAM,或可接受约$8的ESP32 + PSRAM硬件时更合适。
    README 的 Why this is interesting 章节

材料未说明

  • README未给出高并发DNS查询量或长时间运行下的吞吐上限。
  • README未说明上游resolver的默认地址、超时和失败重试策略。
  • README未提供537k ultimate列表在不同分区表下的完整刷写命令。
  • README省略了Enclosure、Gotchas和Done / how it could grow章节的具体内容。
  • README未给出C3 SuperMini之外各ESP32-C3板卡的逐型号兼容性清单。

💡 深度解析

6
视情况 我需要在家庭 LAN 上启用 Web dashboard、blocklist 上传和固件 OTA;如果设备仍使用 HTTP Basic Auth 和开放配置 AP,这个项目的安全边界是否足够?
适合读者: 把 DNS 设备放在可信家庭 LAN、需要通过 Web 面板上传 blocklist 和 OTA 固件的嵌入式维护者

视情况,适合受信任的家庭 LAN,不适合公网、开放 WiFi 或存在可监听者的网络。

  • README Security 章节要求所有状态变更接口使用 WEB_USER/WEB_PASS,网络 OTA 使用 OTA_PASS,并要求自定义 X-Requested-With: c3-adblock 请求头来降低常见 CSRF 风险。
  • 但管理界面完全是 HTTP :80,Basic Auth 只是 Base64;README 明确说开放/访客 WiFi、ARP spoofing 或其他链路监听者可以读取凭据。
  • 未替换 CHANGE_ME_WEB_PASSWORD / CHANGE_ME_OTA_PASSWORD 时,仓库示例中的公开占位值仍可登录;配置 AP C3-AdBlock-XXXX 也按设计保持开放且未加密。
  • 所以该方案的边界是“可信局域网防普通误操作”,不是 TLS 或公网管理安全控制。
  • README「Security」:状态变更接口需要 HTTP Basic Auth;OTA 需要 `OTA_PASS`;需要 `X-Requested-With: c3-adblock`
  • README「Security」:Everything is plain HTTP on :80;Basic Auth credentials are base64
  • README「Security」:默认占位密码公开;WiFi setup portal 的 AP 仍为 open(unencrypted)
材料未说明:README 未说明是否可以通过外部反向代理为 dashboard 提供 TLS;README 未给出 Web/OTA 认证失败次数限制、审计日志或账户锁定机制
适合 我需要在无 PSRAM 的 ESP32-C3 上处理 141k 到 537k 域名,同时控制 RAM 和查询延迟;40 位 FNV-1a 哈希加 Flash 二分查找是否适合?
适合读者: 研究低内存索引结构、要在无 PSRAM 的 ESP32-C3 上维护 141k 至 537k 域名列表的嵌入式算法开发者

适合,因为它直接针对无 PSRAM 的 RAM 和 Flash 约束设计,但必须接受哈希碰撞导致误拦截的概率。

  • README 说明域名被转换为排序后的 40 位哈希并存入 Flash,141k+ 域名约占 0.7 MB,运行时约 50 KB RAM。
  • 查询时计算 FNV-1a,并检查父级后缀,再对 Flash 哈希表二分查找;README 给出的典型代价约为 18 次 Flash 读取、包含 WiFi RTT 约 10 ms。
  • README 的碰撞估计是 141k 域名约为 0 次,537k 域名约为 1 次;因此 40 位是存储规模、查询效率与误拦截风险之间的折中,而不是无碰撞保证。
  • 未来的 bucketed prefix index 仍标为未完成,目标是把约 18 次读取降到 1–2 次,当前实现不能把该收益当作现状。
  • README「Why this is interesting」:sorted 40-bit hashes in flash、~50 KB RAM、141,000+ domains fit in ~0.7 MB
  • README「Why 40 bits?」:141k 约 0 次碰撞,537k 约 1 次碰撞
  • README「Done / how it could grow」:Bucketed prefix index ⬜,约 18 次读取目标降至 1–2 次
dig @<c3-ip> doubleclick.net   # -> 0.0.0.0  (blocked)
材料未说明:README 未说明哈希碰撞发生时如何定位具体原始域名并撤销误拦截;README 未给出不同 WiFi 信号、上游 DNS 和查询并发下的完整延迟分布
不适合 我想让家庭和小型办公室里的客户端都通过 ESP32-C3 做 DNS,并依靠它拦截广告;如果设备短暂断电或客户端使用 DoH/DoT,这个方案还能满足约束吗?
适合读者: 维护家庭或小型办公室网络、希望把 ESP32-C3 作为唯一 DNS 服务器并拦截浏览器与智能家居流量的网络管理员

不适合把它作为唯一、不可绕过的 DNS 基础设施,因为设备离线会影响解析,而加密 DNS 能绕过它。

  • 项目洞察明确指出 ESP32 是单点设备,断电、WiFi 中断、固件异常或 Flash 损坏都可能影响依赖它的 DNS;作为唯一 DNS 时需要回退方案。
  • README 的 Use it 只要求客户端把 DNS 指向设备,或把它作为 secondary resolver,并不提供 DHCP 自动下发 DNS;DHCP server 仍是待实现功能。
  • 项目洞察说明 DoH/DoT、硬编码 IP 或备用 DNS 会绕过基于 UDP DNS 的拦截;README 的实现路径也是命中后 sinkhole、未命中后转发。
  • 因此它更适合家庭、小型办公室和实验网络中的辅助解析器,而不是强制所有终端经过的企业级 DNS 控制点。
  • README「Done / how it could grow」:Act as the DHCP server 仍为 ⬜
  • README「Use it」:Point a device's DNS at the C3's IP, or add it as a secondary resolver
  • 项目洞察 usage_limitations:DoH/DoT、硬编码 IP、缓存或备用 DNS 可绕过拦截;ESP32 是单点设备
dig @<c3-ip> doubleclick.net   # -> 0.0.0.0  (blocked)
材料未说明:README 未说明设备离线时客户端是否自动切换到 secondary resolver;README 未提供针对 DoH/DoT、IPv6 DNS 或硬编码 DNS 的网络侧阻断机制
不适合 我只有 4 MB Flash、无 PSRAM 的 ESP32-C3 SuperMini,既想加载约 537k 域名,又想保留固件 OTA,这个项目适合吗?
适合读者: 使用 4 MB Flash、无 PSRAM 的 ESP32-C3 SuperMini,想在家庭网络中部署 537k 域名拦截并保留 WiFi OTA 的嵌入式开发者

不适合同时满足这两个目标,因为 4 MB Flash 的分区空间无法同时容纳最大列表和标准 OTA 布局。

  • 项目确实针对无 PSRAM 的 ESP32-C3,40 位哈希把约 537k 域名放进 Flash,运行时约使用 50 KB RAM。
  • 但项目洞察说明:启用双应用分区进行固件 OTA 后,blocklist 可用空间约 1.3 MB,容量约限制在 25 万域名;约 537k 域名需要单应用分区。
  • 因此你需要在覆盖规模与远程固件维护之间取舍:保留 OTA 就接受较小列表,选择大列表则要保留 USB 恢复路径。

README 没有给出每种 PlatformIO 分区表的完整配置内容,不能据此判断自定义分区还能精确容纳多少域名。

  • README「Why this is interesting」:537,000 domains、40-bit hashes、~50 KB RAM
  • 项目洞察 common_pitfalls:4MB Flash 启用双应用 OTA 后约 1.3MB blocklist 空间、约 25 万域名
  • 项目洞察 best_practices:最大 blocklist 需要选择更大分区并保留 USB 恢复路径
材料未说明:README 未提供完整分区表和不同分区方案的精确 blocklist 上限;README 未说明 537k 列表在具体固件版本中的实际二进制文件大小
适合 我只有约 2 美元的无 PSRAM ESP32-C3,并希望直接插在路由器 USB 口上做家庭广告拦截;这个项目的部署体验是否比树莓派方案更合适?
适合读者: 熟悉 PlatformIO 的 DIY 爱好者,只有约 2 美元的 ESP32-C3、稳定 USB 供电和家庭路由器 USB 口,想避免树莓派或独立服务器

适合追求低成本、小体积和无需独立服务器的家庭部署,但它仍要求你具备 PlatformIO、USB 烧录和路由器 DNS 配置能力。

  • README 明确定位为运行在约 2 美元 ESP32-C3、无需 PSRAM 的 Pi-hole-style DNS ad-blocker;USB-A 转 USB-C dongle 可直接插入多数路由器的备用 USB 口。
  • 无法连接 WiFi 或未配置 secrets.h 时,设备会启动 C3-AdBlock-XXXX 开放 AP 和 captive portal,首次联网不需要重新烧录。
  • 项目包含 Web dashboard、统计、手动封禁、自定义域名、blocklist 更新和 firmware OTA,维护链路比每次拆机 USB 烧录更短。
  • 代价是部署并非即插即用:README 仍要求把客户端 DNS 指向设备,而且稳定供电很重要;廉价或松动的 USB-C 转接头可能在 WiFi 发射时导致 brownout 重启。
  • README 开头:Pi-hole-style DNS ad-blocker、$2 ESP32-C3、no PSRAM required
  • README「Hardware」:USB-A → USB-C dongle 可直接插入多数路由器 USB 口;稳定 USB source
  • README「WiFi setup」:无法连接时启动 `C3-AdBlock-XXXX` captive portal
  • README「Done / how it could grow」:Web dashboard、OTA、scheduled remote blocklist pulls 已完成
dig @<c3-ip> doubleclick.net   # -> 0.0.0.0  (blocked)
材料未说明:README 省略的 Build & flash 章节未提供完整的从克隆仓库到首次烧录的命令序列;README 未量化设备的长期功耗、路由器 USB 兼容性和断电恢复时间
视情况 我手上的不是主要测试目标 ESP32-C3,而是 4 MB Flash 的 Classic ESP32 DevKit/WROOM;我能否直接用这个项目,并把它作为家庭网络的 DNS?
适合读者: 熟悉 Arduino 和 PlatformIO、要把现有 Classic ESP32 DevKit / WROOM 4 MB 板改造成 DNS sinkhole 的嵌入式开发者

视情况,项目提供 Classic ESP32 构建支持,但主要测试目标仍是 ESP32-C3,且家庭 DNS 还取决于客户端是否真的把查询发给它。

  • README 的 Hardware 章节明确写有 Classic ESP32(DevKit / WROOM,4 MB)也能构建,并给出 pio run -e esp32dev -t upload;同时注明这是社区贡献、仅 compile-tested,C3 才是 tested target。
  • 架构支持命中域名返回 0.0.0.0,未命中请求转发到上游解析器,因此具备 DNS sinkhole 和普通解析的闭环。
  • 使用时需要把客户端 DNS 指向 C3/ESP32 IP,或把设备作为主 DNS 后的 secondary resolver;DoH/DoT、硬编码 DNS 或备用解析路径可能绕过拦截。

README 没有说明 Classic ESP32 的实际查询延迟、最大列表规模或长期稳定性。

  • README「Hardware」:Classic ESP32(DevKit / WROOM, 4 MB)也能构建;`pio run -e esp32dev -t upload`;C3 是 tested target
  • README「Use it」:Point a device's DNS at the C3's IP, or add it as a secondary resolver
  • README 顶部数据流:命中返回 0.0.0.0,未命中转发到 upstream resolver
pio run -e esp32dev -t upload
材料未说明:README 未给出 Classic ESP32 的实测 RAM、Flash blocklist 上限和吞吐数据;README 未说明 IPv6 DNS、路由器 DHCP 下发和客户端备用 DNS 的完整行为

✨ 核心亮点

  • ESP32-C3无PSRAM也能运行537k域名列表
  • 40-bit FNV-1a哈希占用约50KB RAM
  • flash二分查找约10ms完成DNS匹配
  • 4MB闪存开启OTA后列表上限约250k

🔧 工程化

  • ESP32-C3通过UDP DNS sinkhole拦截域名并转发未命中查询
  • PlatformIO可刷写固件、blocklist.bin并通过WiFi OTA更新
  • http://c3adblock.local提供DNS统计、列表上传和Firmware OTA

⚠️ 风险

  • HTTP Basic Auth仅在明文HTTP :80上保护接口,不提供TLS
  • 4MB闪存的双应用分区会把blocklist限制到约250k
  • 40-bit哈希在537k域名规模预计出现1次碰撞并可能误拦截
  • secrets.h保留CHANGE_ME密码会让LAN接口使用公开凭据

👥 适合谁?

  • 拥有4MB ESP32-C3 SuperMini并使用PlatformIO的家庭网络开发者
  • 需要无PSRAM设备承载约100k默认列表的嵌入式开发者
  • 希望用DNS secondary resolver部署轻量拦截器的网络用户