【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.mzph.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最新毕业设计选题推荐…