CNAME 與 A 記錄:哪個先來?解析 DNS 記錄順序變更事件
2026-01-14 Sebastiaan Neuteboom
概要
2026年1月8日,一次旨在降低記憶體使用量的更新意外導致全球用戶的 DNS 解析失敗。問題的根源不在於攻擊或中斷,而是我們的 DNS 回應中記錄順序的微妙變化。
時間線
所有時間戳均為協調世界時 (UTC)。
- 2025-12-02:記錄順序改變引入到 1.1.1.1 的程式碼中。
- 2025-12-10:改變發布到我們的測試環境中。
- 2026-01-07 23:48:包含更改的全球版本開始推出。
- 2026-01-08 17:40:版本到達90%的伺服器。
- 2026-01-08 18:19:宣告事件。
- 2026-01-08 18:27:版本回退。
- 2026-01-08 19:55:回退完成,影響結束。
事件詳情
在降低我們緩存實現的記憶體使用量時,我們對 CNAME 記錄順序的變更導致 DNS 解析失敗。這一變更於 2025年12月2日引入,12月10日發布到我們的測試環境,並於 2026年1月7日開始部署。
DNS CNAME 鏈的運作原理
查詢域名如 www.example.com 時,可能會收到 CNAME (規範名稱) 記錄,顯示一個名稱是另一名稱的別名。公共解析器如 1.1.1.1 的工作是跟隨這一別名鏈,直到達到最終回應:
www.example.com → cdn.example.com → server.cdn-provider.com → 198.51.100.1
在 1.1.1.1 遍歷此鏈時,它會緩存每個中間記錄。鏈中每個記錄都有自己的 TTL (存活時間),指示我們可以緩存多長時間。CNAME 鏈中的所有 TTL 不需要相同:
www.example.com → cdn.example.com (TTL: 3600 秒)
cdn.example.com → 198.51.100.1 (TTL: 300 秒)
當 CNAME 鏈中的一個或多個記錄過期時,稱為部分過期。幸運的是,由於部分鏈還在緩存中,我們不需要再次解析整個 CNAME 鏈,只需解析過期部分。
邏輯變更
合併這兩個鏈的程式碼是發生變更的地方。以前,程式碼會創建新列表,插入現有 CNAME 鏈,然後追加新記錄。然而,為了節省一些記憶體分配和複製,程式碼改為將 CNAME 附加到現有的回應列表:
impl PartialChain {
/// 合併記錄到緩存條目以完成緩存記錄。
pub fn fill_cache(&self, entry: &mut CacheEntry) {
entry.answer.extend(self.records); // CNAMEs 最後
}
}
因此,1.1.1.1 返回的回應有時會將 CNAME 記錄顯示在最底部,排在最終解析的回應之後。
為何造成影響
當 DNS 客戶端收到包含 CNAME 鏈的回應時,它們也需要跟隨這一鏈以完成解析。如果某些實現預期 CNAME 記錄首先出現,順序變更可能導致解析失敗。