【golang】DNS 資源記錄(RR)接口

Go 中 miekg/dns 包對 DNS 資源記錄(RR)接口 的定義:

type RR interface {Header() *RR_HeaderString() stringcopy() RRlen(off int, compression map[string]struct{}) intpack(...)unpack(...)parse(...)isDuplicate(r2 RR) bool
}

這個接口定義了一個資源記錄(RR)應具備的核心功能。


每個方法的作用

方法名作用
Header()返回資源記錄的頭部(RR_Header),包含名稱、TTL、類型等元數據。
String()以字符串格式返回記錄,常用于調試或輸出為 zone 文件格式。
copy()返回該 RR 的深拷貝。
len(...)返回該記錄的字節長度(壓縮或未壓縮形式)。
pack(...)把記錄轉換為 DNS 報文(二進制 wire 格式)的一部分。
unpack(...)從 wire 格式解析出該記錄內容。
parse(...)從 zone 文件解析一個 RR。
isDuplicate(...)判斷兩個 RR 是否是重復的。

? 實戰演示:自定義并使用 RR 接口對象

我們用 dns.NewRR() 來創建一個 RR 實例,然后使用其中的一些方法。

package mainimport ("fmt""github.com/miekg/dns"
)func main() {// 創建一個 RR(類型是 A)rr, err := dns.NewRR("example.com. 3600 IN A 93.184.216.34")if err != nil {panic(err)}// 查看 RR 的字符串表示fmt.Println("RR String():", rr.String())// 獲取 RR 的頭部header := rr.Header()fmt.Printf("Name: %s\nType: %d\nClass: %d\nTTL: %d\n",header.Name, header.Rrtype, header.Class, header.Ttl)// 拷貝這個 RRrrCopy := rr.Copy()fmt.Println("Copy equals original?", rrCopy.String() == rr.String())
}

輸出示例:

RR String(): example.com.	3600	IN	A	93.184.216.34
Name: example.com.
Type: 1
Class: 1
TTL: 3600
Copy equals original? true

🔍 RR_Header 是什么?

資源記錄都有一個頭部,稱為 RR_Header

type RR_Header struct {Name     stringRrtype   uint16Class    uint16Ttl      uint32Rdlength uint16 // 自動填充
}

這個頭部定義了一個資源記錄的“元信息”,類似于HTTP報文中的Header部分。


🚨 高階用法:自定義打包/解析

方法如 pack, unpack, parse, len 等主要用于 DNS 底層協議處理(比如實現一個 DNS 服務器或手動構建報文)。通常你不需要自己實現這些接口,因為 miekg/dns 提供了所有常見類型的內置支持(如 A, MX, TXT, CNAME)。


如何自定義一個實現了 RR 接口的結構體

如何解析自定義類型記錄

非常好,這是一個高級又非常實用的用法——自定義 DNS RR 類型并實現 dns.RR 接口

你可以通過實現 RR 接口中的所有方法,來定義自己的 DNS 資源記錄類型(比如:一個私有記錄 TYPE65534)。這在你實現某些自定義協議或擴展 DNS 時非常有用。


🛠 示例目標

我們自定義一個記錄類型:XINFO(私有類型碼:65534),記錄中包含一個文本字符串。

Zone 格式示意:

custom.example.com. 3600 IN XINFO "my custom info"

? 第一步:定義結構體

package mainimport ("fmt""strings""github.com/miekg/dns"
)// 自定義 RR 類型 XINFO
type XINFO struct {Hdr  dns.RR_HeaderInfo string
}// 實現 dns.RR 接口func (x *XINFO) Header() *dns.RR_Header { return &x.Hdr }func (x *XINFO) String() string {return fmt.Sprintf("%s\t%d\t%s\tXINFO\t\"%s\"",x.Hdr.Name, x.Hdr.Ttl, dns.ClassToString[x.Hdr.Class], x.Info)
}func (x *XINFO) copy() dns.RR {return &XINFO{Hdr:  x.Hdr,Info: x.Info,}
}func (x *XINFO) len(off int, compression map[string]struct{}) int {// 2 bytes for TXT length prefix + contentreturn x.Hdr.Len(off, compression) + 1 + len(x.Info)
}func (x *XINFO) pack(msg []byte, off int, compression dns.CompressionMap, compress bool) (int, error) {off, err := x.Hdr.Pack(msg, off, compression, compress)if err != nil {return off, err}msg[off] = byte(len(x.Info)) // 單段 TXT 記錄格式off++copy(msg[off:], x.Info)return off + len(x.Info), nil
}func (x *XINFO) unpack(msg []byte, off int) (int, error) {length := int(msg[off])off++x.Info = string(msg[off : off+length])return off + length, nil
}func (x *XINFO) parse(c *dns.Zlexer, origin string) *dns.ParseError {tok, _ := c.Next()if tok.Value == "" {return &dns.ParseError{Err: "no value for XINFO"}}x.Info = strings.Trim(tok.Value, `"`)return nil
}func (x *XINFO) isDuplicate(r2 dns.RR) bool {xx, ok := r2.(*XINFO)if !ok {return false}return x.Info == xx.Info
}

? 第二步:注冊自定義 RR 類型

func init() {const TypeXINFO = 65534dns.TypeToString[TypeXINFO] = "XINFO"dns.StringToType["XINFO"] = TypeXINFO// 注冊解析器dns.TypeToRR[TypeXINFO] = func() dns.RR {return new(XINFO)}
}

? 第三步:解析記錄字符串

func main() {rr, err := dns.NewRR("custom.example.com. 3600 IN XINFO \"my custom info\"")if err != nil {panic(err)}fmt.Println(rr.String())// 類型斷言并訪問 Info 字段if xinfo, ok := rr.(*XINFO); ok {fmt.Println("Info:", xinfo.Info)}
}

🧾 輸出示例:

custom.example.com.	3600	IN	XINFO	"my custom info"
Info: my custom info

? 小結

實現自定義 RR 類型,你需要:

  1. 定義結構體,包含 dns.RR_Header 和你自定義的數據字段。
  2. 實現 dns.RR 接口 的所有方法。
  3. 注冊你的類型碼、名字與工廠函數(dns.TypeToRR[...]
  4. 使用 dns.NewRR(...)dns.UnpackRR(...) 正常解析。

如果你還想讓這個類型支持 JSON、Base64 編碼,或序列化成 wire 格式,也可以在基礎之上繼續拓展。

用這個自定義類型發送或接收真實 DNS 查詢

當然可以!我們來展示如何通過 Go 的 miekg/dns 包,使用你自定義的 RR 類型進行 DNS 查詢或響應。由于 DNS 查詢通常是客戶端發起請求、服務端響應,我們分別說明:


📡 一、作為服務端:響應帶自定義記錄的 DNS 查詢

這是最常見的用法:你運行一個 DNS 服務,并在某個請求域名時,返回你定義的 XINFO 類型記錄。

? 服務端示例

package mainimport ("fmt""log""net""github.com/miekg/dns"
)// init: 注冊自定義類型 XINFO(65534)
func init() {const TypeXINFO = 65534dns.TypeToString[TypeXINFO] = "XINFO"dns.StringToType["XINFO"] = TypeXINFOdns.TypeToRR[TypeXINFO] = func() dns.RR {return new(XINFO)}
}// 定義自定義 RR
type XINFO struct {Hdr  dns.RR_HeaderInfo string
}func (x *XINFO) Header() *dns.RR_Header                 { return &x.Hdr }
func (x *XINFO) String() string                         { return fmt.Sprintf("%s\t%d\tIN\tXINFO\t\"%s\"", x.Hdr.Name, x.Hdr.Ttl, x.Info) }
func (x *XINFO) copy() dns.RR                           { cp := *x; return &cp }
func (x *XINFO) len(off int, c map[string]struct{}) int { return x.Hdr.Len(off, c) + 1 + len(x.Info) }
func (x *XINFO) pack(buf []byte, off int, c dns.CompressionMap, compress bool) (int, error) {off, err := x.Hdr.Pack(buf, off, c, compress)if err != nil {return off, err}buf[off] = byte(len(x.Info))copy(buf[off+1:], x.Info)return off + 1 + len(x.Info), nil
}
func (x *XINFO) unpack(msg []byte, off int) (int, error) {length := int(msg[off])off++x.Info = string(msg[off : off+length])return off + length, nil
}
func (x *XINFO) parse(c *dns.Zlexer, origin string) *dns.ParseError {tok, _ := c.Next()x.Info = tok.Valuereturn nil
}
func (x *XINFO) isDuplicate(r2 dns.RR) bool {xx, ok := r2.(*XINFO)return ok && xx.Info == x.Info
}// DNS 請求處理函數
func handleDNSRequest(w dns.ResponseWriter, r *dns.Msg) {msg := new(dns.Msg)msg.SetReply(r)for _, q := range r.Question {if q.Name == "custom.example.com." && q.Qtype == 65534 {rr := &XINFO{Hdr: dns.RR_Header{Name:   q.Name,Rrtype: 65534,Class:  dns.ClassINET,Ttl:    60,},Info: "Hello from custom XINFO!",}msg.Answer = append(msg.Answer, rr)}}w.WriteMsg(msg)
}func main() {server := &dns.Server{Addr: ":5353", Net: "udp"}dns.HandleFunc(".", handleDNSRequest)fmt.Println("DNS server listening on UDP :5353")if err := server.ListenAndServe(); err != nil {log.Fatal(err)}
}

🧪 測試方法:模擬查詢

你可以寫個測試客戶端,發送一個請求到這個服務:

package mainimport ("fmt""github.com/miekg/dns"
)func main() {c := new(dns.Client)m := new(dns.Msg)m.SetQuestion("custom.example.com.", 65534)resp, _, err := c.Exchange(m, "127.0.0.1:5353")if err != nil {panic(err)}for _, ans := range resp.Answer {fmt.Println("Response:", ans.String())}
}

🧾 輸出(示例):

DNS server listening on UDP :5353
Response: custom.example.com.	60	IN	XINFO	"Hello from custom XINFO!"

🔐 注意事項

  • 自定義類型不會被標準 DNS 服務器識別。類型號 65534 被保留為私有用途(RFC 6895)。
  • 如果你希望在生產中用自定義 RR,建議配合專用客戶端或私有 DNS 協議。

? 總結

你現在知道了:

  1. 如何定義自定義的 DNS RR 類型(如 XINFO)。
  2. 如何注冊并使用 miekg/dns 支持這個類型。
  3. 如何啟動一個支持自定義記錄的 DNS 服務端。
  4. 如何用 Go 客戶端去解析自定義記錄。

使用 dig 命令行工具測試

dig 命令行工具測試你自定義的 DNS 服務器和自定義的 RR 類型(比如我們示例中的 XINFO)。


1. 前提條件

  • 你的自定義 DNS 服務器已經運行在本地,比如監聽在 UDP 端口 5353(代碼示例中的端口)。
  • 服務器可以正確響應你的自定義 RR 查詢(類型號 65534,名字是 XINFO)。

2. 使用 dig 查詢自定義類型

dig 默認識別標準的DNS類型,如 A、MX、TXT 等。要查詢一個自定義類型,需要使用類型號查詢。

假設你的自定義類型碼是 65534,查詢命令如下:

dig @127.0.0.1 -p 5353 custom.example.com. TYPE65534 +noall +answer

解釋:

  • @127.0.0.1:指定向本地服務器查詢。
  • -p 5353:指定端口(你的服務器端口)。
  • custom.example.com.:查詢的域名。
  • TYPE65534:查詢類型為編號 65534(你的自定義類型)。
  • +noall +answer:只顯示響應中的 answer 部分,簡潔輸出。

3. 輸出示例

如果服務器返回正常,你會看到類似:

custom.example.com. 60 IN TYPE65534 "Hello from custom XINFO!"

這里:

  • TYPE65534 是 dig 無法識別的類型,所以用 TYPE 加類型號顯示。
  • 后面跟著你自定義記錄的字符串內容。

4. 如果想讓 dig 顯示類型名字而不是 TYPE65534?

默認情況下,dig 不認識私有類型的名字,所以顯示類型號是正常的。

你可以通過修改 dig 的類型映射文件或者使用標準類型測試。一般情況下,直接用 TYPE<number> 查詢自定義類型即可。


5. 額外提示

  • dig 支持 UDP 和 TCP,默認是 UDP。
  • 如果你的 DNS 服務器支持 TCP,可以用 +tcp 參數。
  • 你還可以用 +trace+dnssec 等參數調試 DNS。

6. 總結示范

dig @127.0.0.1 -p 5353 custom.example.com. TYPE65534 +noall +answer

運行這個命令,你就能測試自定義 DNS 服務器對自定義 RR 的響應。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/80006.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80006.shtml
英文地址,請注明出處:http://en.pswp.cn/web/80006.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

16.2 VDMA視頻轉發實驗之模擬源

文章目錄 1 實驗任務2 系統框圖3 硬件設計3.1 IP核配置3.2 注意事項3.3 自定義IP核源碼 4 軟件設計4.1 注意事項4.2 工程源碼4.2.1 main.c文件 1 實驗任務 基于14.1&#xff0c;相較于16.1&#xff0c;使用自定義IP核vid_gen_motion替換Xilinx TPG IP核。 2 系統框圖 基于14…

深度學習之用CelebA_Spoof數據集搭建一個活體檢測-訓練好的模型用MNN來推理

一、模型轉換準備 首先確保已完成PyTorch到ONNX的轉換&#xff1a;深度學習之用CelebA_Spoof數據集搭建活體檢測系統&#xff1a;模型驗證與測試。這里有將PyTorch到ONNX格式的模型轉換。 二、ONNX轉MNN 使用MNN轉換工具進行格式轉換&#xff1a;具體的編譯過程可以參考MNN的…

JVM學習專題(一)類加載器與雙親委派

目錄 1、JVM加載運行全過程梳理 2、JVM Hotspot底層 3、war包、jar包如何加載 4、類加載器 我們來查看一下getLauncher&#xff1a; 1.我們先查看getExtClassLoader() 2、再來看看getAppClassLoader(extcl) 5、雙親委派機制 1.職責明確&#xff0c;路徑隔離?&#xff…

部署安裝gitlab-ce-17.9.7-ce.0.el8.x86_64.rpm

目錄 ?編輯 實驗環境 所需軟件 實驗開始 安裝部署gitlab171.配置清華源倉庫&#xff08;版本高的系統無需做&#xff09;vim /etc/yum.repos.d/gitlab-ce.repo 2.提前下載包dnf localinstall gitlab-ce-17.9.7-ce.0.el8.x86_64.rpm --rocklinux 3.修改配…

使用LoRA微調Qwen2.5-VL-7B-Instruct完成電氣主接線圖識別

使用LoRA微調Qwen2.5-VL-7B-Instruct完成電氣主接線圖識別 動機 任務適配需求 Qwen2.5-VL在視覺理解方面表現優異&#xff0c;但電氣主接線圖識別需要特定領域的結構化輸出能力&#xff08;如設備參數提取、拓撲關系解析&#xff09;。微調可增強模型對專業符號&#xff08;如…

系統集成項目管理工程師學習筆記

第九章 項目管理概論 1、項目基本要素 項目基礎 項目是為創造獨特的產品、服務或成果而進行的臨時性工作。 項目具有臨時性、獨特性、漸進明細的特點。項目的“臨時性”是指項目只有明確的起點和終點。“臨時性”并一定意味著項目的持續時間短。 項目可宣告結束的情況&…

Secs/Gem第七講(基于secs4net項目的ChatGpt介紹)

好的&#xff0c;那我們現在進入&#xff1a; 第七講&#xff1a;掉電重連后&#xff0c;為什么設備不再上報事件&#xff1f;——持久化與自動恢復的系統設計 關鍵詞&#xff1a;掉電恢復、狀態重建、初始化流程、SecsMessage 緩存機制、自動重連、事件再注冊 本講目標 你將理…

室內定位:熱門研究方向與未解難題深度解析

I. 引言:對普適性室內定位的持續探索 A. 室內定位在現代應用中的重要性 室內定位系統(IPS)正迅速成為眾多應用領域的基石技術,其重要性源于現代社會人們約70%至90%的時間在室內度過的事實 1。這些應用橫跨多個行業,包括應急響應 1、智能建筑與智慧城市 6、醫療健康(如病…

Android學習總結之Glide自定義三級緩存(實戰篇)

一、為什么需要三級緩存 內存緩存&#xff08;Memory Cache&#xff09; 內存緩存旨在快速顯示剛瀏覽過的圖片&#xff0c;例如在滑動列表時來回切換的圖片。在 Glide 中&#xff0c;內存緩存使用 LruCache 算法&#xff08;最近最少使用&#xff09;&#xff0c;能自動清理長…

Linux的文件查找與壓縮

查找文件 find命令 # 命令&#xff1a;find 路徑范圍 選項1 選項1的值 \[選項2 選項2 的值…]# 作用&#xff1a;用于查找文檔&#xff08;其選項有55 個之多&#xff09;# 選項&#xff1a;# -name&#xff1a;按照文檔名稱進行搜索&#xff08;支持模糊搜索&#xff0c;\* &…

python處理異常,JSON

異常處理 #異常處理 # 在連接MySQL數據庫的過程中&#xff0c;如果不能有效地處理異常&#xff0c;則異常信息過于復雜&#xff0c;對用戶不友好&#xff0c;暴露過多的敏感信息 # 所以&#xff0c;在真實的生產環境中&#xff0c; 程序必須有效地處理和控制異常&#xff0c;按…

線程的兩種實現方式

線程的兩種實現方式——內核支持線程&#xff08;kernal Supported Thread, KST&#xff09;&#xff0c; 用戶級線程&#xff08;User Level Thread, ULT&#xff09; 1. 內核支持線程 顧名思義&#xff0c;內核支持線程即為在內核支持下的那些線程&#xff0c;它們的創建&am…

vue3基礎學習(上) [簡單標簽] (vscode)

目錄 1. Vue簡介 2. 創建Vue應用 2.1 下載JS文件 2.2 引用JS文件 2.3 調用Vue方法?編輯 2.4 運行一下試試: 2.5 代碼如下 3.模塊化開發模式 3.1 Live Server插件 3.2 運行 4. 常用的標簽 4.1 reactive 4.1.1 運行結果 4.1.2 代碼: 4.2 ref 4.2.1 運行結果 4.2.2…

自定義分區器-基礎

什么是分區 在 Spark 里&#xff0c;彈性分布式數據集&#xff08;RDD&#xff09;是核心的數據抽象&#xff0c;它是不可變的、可分區的、里面的元素并行計算的集合。 在 Spark 中&#xff0c;分區是指將數據集按照一定的規則劃分成多個較小的子集&#xff0c;每個子集可以獨立…

深入解析HTTP協議演進:從1.0到3.0的全面對比

HTTP協議作為互聯網的基礎協議&#xff0c;經歷了多個版本的迭代演進。本文將詳細解析HTTP 1.0、HTTP 1.1、HTTP/2和HTTP/3的核心特性與區別&#xff0c;幫助開發者深入理解網絡協議的發展脈絡。 一、HTTP 1.0&#xff1a;互聯網的奠基者 核心特點&#xff1a; 短連接模式&am…

基于windows環境Oracle主備切換之后OGG同步進程恢復

基于windows環境Oracle主備切換之后OGG同步進程恢復 場景&#xff1a;db1是主庫&#xff0c;db2是備庫&#xff0c;ogg從db2備庫抽取數據同步到目標數據庫 db1 - db2(ADG) – ogg – targetdb 場景&#xff1a;db2是主庫&#xff0c;db1是備庫&#xff0c;ogg從db1備庫抽取數…

微服務,服務粒度多少合適

項目服務化好處 復用性&#xff0c;消除代碼拷貝專注性&#xff0c;防止復雜性擴散解耦合&#xff0c;消除公共庫耦合高質量&#xff0c;SQL穩定性有保障易擴展&#xff0c;消除數據庫解耦合高效率&#xff0c;調用方研發效率提升 微服務拆分實現策略 統一服務層一個子業務一…

【工奧閥門科技有限公司】簽約智橙PLM

近日&#xff0c;工奧閥門科技有限公司正式簽約了智橙泵閥行業版PLM。 忠于質量&#xff0c;臻于服務&#xff0c;精于研發 工奧閥門科技有限公司&#xff08;以下簡稱工奧閥門&#xff09;坐落于浙江永嘉&#xff0c;是一家集設計、開發、生產、銷售、安裝、服務為一體的閥門…

2025-5-15Vue3快速上手

1、setup和選項式API之間的關系 (1)vue2中的data,methods可以與vue3的setup共存 &#xff08;2&#xff09;vue2中的data可以用this讀取setup中的數據&#xff0c;但是反過來不行&#xff0c;因為setup中的this是undefined &#xff08;3&#xff09;不建議vue2和vue3的語法混用…

基于智能推薦的就業平臺的設計與實現(招聘系統)(SpringBoot Thymeleaf)+文檔

&#x1f497;博主介紹&#x1f497;&#xff1a;?在職Java研發工程師、專注于程序設計、源碼分享、技術交流、專注于Java技術領域和畢業設計? 溫馨提示&#xff1a;文末有 CSDN 平臺官方提供的老師 Wechat / QQ 名片 :) Java精品實戰案例《700套》 2025最新畢業設計選題推薦…