Pi-hole 等級的 DNS 廣告攔截器,運行於僅需 2 美元的 ESP32-C3 — 無需 PSRAM。
大家忽略的技巧:您不需要將封鎖清單保留在 RAM 中。將網域儲存為快閃記憶體中的排序 40 位元雜湊值,並進行二分搜尋。超過 140,000 個網域可儲存於約 0.7MB 的快閃記憶體中,並在約 10 毫秒內完成比對,僅使用約 50KB 的 RAM。
大多數 ESP32 DNS 接收器會將封鎖清單(網域字串)載入 RAM,因此需要 PSRAM。此專案改為將固定的 5 位元組(40 位元)雜湊值儲存在快閃記憶體中:
為何是 40 位元?這是針對此快閃記憶體預算的最優選擇。碰撞遵循生日悖論 — 在 141k 個網域時約為 0,在 537k 個網域時約為 1(即一個不幸的網域會被過度封鎖)。降至 32 位元可節省 20% 的快閃記憶體,但在 250k 個網域時會導致約 7 次碰撞;增加到 64 位元則每個網域浪費 3 個位元組來解決您並不存在的問題。
相同的技巧也適用於更大的晶片 — 這並非 C3 的特殊解決方案。在擁有 16MB 的 ESP32-S3 上,這些雜湊值可容納約 2.7M 個網域,相較之下,在 8MB 的 PSRAM 中儲存字串僅能容納約 466k 個網域。快閃記憶體中的雜湊值基本上在所有情況下都優於 PSRAM 中的字串;C3 只是讓這一點更加無可辯駁。
可列印的 C3 SuperMini 外殼:hardware/esp32-c3-supermini-enclosure.stl
只需一個 USB 隨身碟即可開始 — 之後,韌體和封鎖清單都可以透過 Wi-Fi 更新(見下文)。
⚠️ 請使用最新的 PlatformIO — VSCode PlatformIO 擴充功能內建的核心,或在 venv 中執行 pip install -U platformio。發行版/apt 的 platformio 套件(例如 4.3.4)太舊,會因 AttributeError: ... 'resultcallback' 而失敗(問題 #4)。一個點擊式瀏覽器安裝程式即將推出(託管地點待定)。
build_blocklist.py OUT.bin [SOURCE ...] 可以接受任何混合的 URL 和本地檔案,格式如下:
封鎖的網域也會封鎖其子網域。DNS 雜湊清單無法表達的規則(正規表示式、萬用字元、$ 修飾符、視覺化 ## 規則)會被跳過並計數。@@ 規則僅解除封鎖該確切項目 — 它無法從被封鎖的父網域中分割出子網域。如果來源無法下載,建置將停止,而不是默默產生較小的清單(--allow-missing 可覆寫)。
如果無法連線(或您從未設定 secrets.h),它會啟動一個開放存取的接入點 C3-AdBlock-XXXX,並帶有入口網站 — 從手機加入,選擇您的網路,輸入密碼,完成。之後要移至新網路:點擊儀表板上的 Forget WiFi,或在開機時按住 BOOT 按鈕,設定入口網站就會再次出現。(/forgetwifi 現在需要驗證,因此不再是您可以直接造訪的裸 URL — 請參閱下方的安全性。)
http://c3adblock.local 的儀表板可以完成所有操作:
4MB 快閃記憶體權衡:韌體 OTA 需要兩個應用程式槽,這會留下約 1.3MB 的空間用於封鎖清單(最多約 250k 個網域)。激進的 537k「終極」清單僅適用於單一應用程式分割區表(無韌體 OTA)。請在 partitions.csv 中選擇您的權衡。
儀表板的唯讀檢視(/、/stats.json)保持開放,但每個狀態變更的端點都需要 HTTP 基本驗證(來自 secrets.h 的 WEB_USER / WEB_PASS):
網路 OTA(ArduinoOTA,例如 pio run -t upload --upload-port c3adblock.local --upload-protocol espota)需要來自相同檔案的 OTA_PASS。
沒有這些,任何能夠在 LAN 上存取該裝置的人都可以用任意韌體重新刷寫它,或以零憑證重寫封鎖清單 — 鑑於該裝置位於您網路上的每個 DNS 查詢路徑中,這一點值得注意。自訂封鎖網域名稱在呈現於儀表板之前也會進行 HTML 編碼,關閉了儲存型 XSS 路徑,其中透過 /addblock 添加的網域字串(包含標記)否則會在檢視瀏覽器中執行。
這裡的基本驗證是 LAN 信任邊界控制,而非加密。所有內容都是 :80 上的純 HTTP — 此晶片沒有實際的預算來運行 TLS 伺服器。基本驗證憑證是 base64 編碼(未加密),並在每次驗證請求時傳送;任何能夠嗅探您 LAN 流量的人(開放/訪客 Wi-Fi、ARP 欺騙)都可以從線上讀取它們。這可以防範常見情況 — 您網路上的另一個裝置未經憑證存取 API,或瀏覽器標籤頁進行 CSRF — 而非防範路徑上的網路攻擊者。
透過快取基本驗證進行的 CSRF:瀏覽器會自動將快取的 és基本驗證憑證附加到對已驗證來源的任何後續請求 — 包括稍後由同一瀏覽器造訪的完全不相關頁面觸發的請求(例如 <img src="http://c3adblock.local/forgetwifi">,無需 JS)。一旦您登入儀表板一次,任何網頁都可以默默地驅動此 API,無論是誰在您的 LAN 上。每個變更端點現在還需要自訂的 X-Requested-With: c3-adblock 標頭,純粹的 <img> /auto-submitted <form> CSRF 無法附加(只有同源 fetch() 可以,這也是儀表板自己的 JS 所做的)— 這就是為什麼 /forgetwifi 不再是您可以直接造訪的裸 URL;請改用儀表板按鈕。
預設憑證:如果 secrets.h 仍然具有來自 secrets.example.h 的預設值 CHANGE_ME_WEB_PASSWORD / CHANGE_ME_OTA_PASSWORD,則裝置將以公開的「密碼」啟動(它位於此儲存庫的範例檔案中)。韌體會在序列埠上記錄警告,並在儀表板上顯示橫幅提示此情況 — 但它仍會啟動並運行,因此在您完全控制的網路之外信任此裝置之前,請勿跳過在 secrets.h 中設定實際值。
目前範圍外:Wi-Fi 設定入口網站的接入點(C3-AdBlock-XXXX)仍是開放的(未加密),這是設計使然 — 它需要無需密碼即可加入。您在入口網站輸入的實際 Wi-Fi 密碼僅在短暫設定視窗期間,與該本地無線連結一樣安全。
將裝置的 DNS 指向 C3 的 IP,或將其作為主要 DNS 後面的次要解析器新增。測試:
靈感來自 s60sc/ESP32_AdBlocker — 「為封鎖的網域回應 0.0.0.0」的理念。這是一個獨立的從頭開始的實作,專注於針對無 PSRAM 晶片的雜湊值快閃記憶體優化。
Pi-hole 等級的 DNS 廣告攔截器,運行於僅需 2 美元的 ESP32-C3 — 無需 PSRAM:53.7 萬筆網域作為 40 位元 FNV-1a 雜湊值儲存在快閃記憶體中,進行二分搜尋。UDP DNS 接收器 + 網頁儀表板。https://youtube.com/shorts/RaxszOUMi8E?feature=share