尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SimpleDNS 源码学习笔记:几百行 C 搞懂 DNS 报文

SimpleDNS 源码学习笔记:几百行 C 搞懂 DNS 报文 学完 socket 网络编程之后写过 HTTP、FTP 这类文本协议往往还想接触一种「二进制报文」的典型协议。整理了一个很小的项目SimpleDNS——用 C 写的教学用 DNS 服务器核心基本就在一个main.c里大约七八百行量级专门用来理解 DNS 查询与应答是怎么拼出来的。1. 项目概览SimpleDNS 适合作为网络编程练手项目主要因为体量很小单文件就能看完主流程单线程 UDP模型干净没有多余框架支持常见的A / AAAA / TXT查询自己做报文解码、编码协议细节暴露得很直接一句话总结代码量很小协议味道很正适合专门学 DNS 报文。官方也写明这是教学用途对非法报文没有完整防护不要当生产 DNS 用。2. DNS 基础DNS 和 HTTP/FTP 最大的不同传输层常用UDPSimpleDNS 也是 UDP报文是二进制结构不是一行一行的文本命令典型交互客户端发 Query服务器回 Response同一条报文结构靠标志位区分一份 DNS 报文大致分几段Header12 字节事务 ID、QR/Opcode/RCODE、各段计数Question问什么名字、什么类型A/AAAA…Answer / Authority / Additional资源记录RR域名在报文里不是foo.bar.com明文点分而是标签长度编码例如3foo3bar3com0和 FTP「控制连接 数据连接」不同DNS 通常一次请求一次响应就在同一个 UDP 包来回里完成。3. 源码阅读顺序与模块分工仓库几乎只有一个核心源文件阅读顺序可以按函数走main—— UDPsocket/bind循环recvfrom/sendtodecode_msg/decode_header—— 解析查询包decode_domain_name—— 域名标签解码resolve_query—— 根据问题生成答案 RRget_A_Record/get_AAAA_Record/get_TXT_Record—— 「区数据」encode_msg/encode_header/encode_resource_records—— 打包响应逻辑模块可以这样理解部分作用类型与掩码常量QR、Opcode、RCODE、记录类型A1, AAAA28, TXT16…struct Message一次完整 DNS 消息的内存表示struct Question问题段struct ResourceRecord资源记录答案等get16bits/put16bits…网络字节序读写小工具decode_*二进制 → 结构体resolve_query查本地映射填 answersencode_*结构体 → 二进制默认监听9000端口不必抢 53方便普通用户调试。4. 核心结构主循环四步main里的服务循环非常清晰recvfrom收到 UDP 查询decode_msg解成Messageresolve_query填答案encode_msg再sendto回客户端可概括为收包 → 解码 → 解析/作答 → 编码 → 发包这和文本协议服务器的「读一行 → 分发 → 回写」是同一类骨架只是 DNS 把「一行文本」换成了「二进制报文」。5. 报文解码从二进制到结构体Headerdecode_header先读 12 字节头id事务标识响应要带回flags拆出 QR、Opcode、AA、TC、RD、RA、RCODEqdCount/anCount/nsCount/arCount后面各段有多少条Question对每个问题decode_domain_name把标签编码还原成foo.bar.com再读qType、qClassSimpleDNS 期望收到的是「纯查询」答案段计数为 0否则直接拒绝解析——教学实现逻辑直白。6. 作答resolve_queryresolve_query会把消息改成响应qr 1并设置权威等标志遍历每个Question按类型调用本地函数A →get_A_RecordAAAA →get_AAAA_RecordTXT →get_TXT_Record成功则挂到answers链表并增加anCount不支持的类型给NotImplemented默认写死示例foo.bar.com→ IPv4192.168.1.1同域名 → IPv6fe80::1TXT →abcdefg想加域名改这几个get_*_Record即可非常适合边改边用dig验证。7. 报文编码从结构体回到二进制响应路径encode_header写回 ID、标志、各计数原样编码 QuestionDNS 响应通常带回问题encode_resource_records写 Answer名字、type/class/ttl、rd_length、RDATAA 记录 RDATA 是 4 字节地址AAAA 是 16 字节TXT 先长度再文本。跟读时建议对照 Wireshark 或dig输出看字段是否对得上。8. 和 Tinyhttpd / Tinyftp 的对比TinyhttpdTinyftpSimpleDNS协议HTTP 文本FTP 文本命令DNS 二进制传输TCPTCP双连接UDP体量约几百行约三千行约五百行级单文件核心难点请求行/头、CGIPORT/PASV、进程模型报文编解码、域名格式系列建议顺序Tinyhttpd → Tinyftp → SimpleDNS前两者练文本协议与连接模型这篇补上二进制协议。9. 编译与运行备忘gitclone https://github.com/mwarning/SimpleDNS.gitcdSimpleDNSmake./main另开终端测试dig127.0.0.1-p9000foo.bar.com Adig127.0.0.1-p9000foo.bar.com AAAA注意Debian/Ubuntu 可用dnsutils包提供的dig仅建议本机/内网学习教学实现输入校验不强勿对公网开放10. 小结通过阅读 SimpleDNS可以把这些点串起来DNS 报文 Header / Question / Answer 的基本结构域名标签编码与解码A / AAAA / TXT 资源记录如何写入响应UDP 服务器「解码—处理—编码」的标准骨架有了这篇再去看缓存、递归、DNS 中继就不会觉得报文层是黑盒了。项目链接GitHubhttps://github.com/mwarning/SimpleDNS
返回列表