ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

如何编写一个最小权威服务器:hello-dns之tauth的DNS Tree结构与RFC 1034算法剖析

如何编写一个最小权威服务器:hello-dns之tauth的DNS Tree结构与RFC 1034算法剖析 如何编写一个最小权威服务器hello-dns之tauth的DNS Tree结构与RFC 1034算法剖析【免费下载链接】hello-dnsHello and welcome to DNS!项目地址: https://gitcode.com/gh_mirrors/he/hello-dns想自己动手写一个真正能用的 DNS 权威服务器吗hello-dns是一个专为教学而生的 DNS 项目其中的tauth就是一台功能完整的最小权威服务器。本文带你读懂 tauth 的两大核心忠于 RFC 1034 的 DNS Tree 树形存储结构以及处理每次查询的权威应答算法——用最少的代码把权威服务器究竟怎么作答彻底讲透。一、hello-dns 与 tauth一台会教你 DNS 的权威服务器hello-dns 的名字直白到可爱Hello and welcome to DNS!它由一套可教学的 DNS 库tdns和几个小型名称服务器组成目的是让你边读边懂而不是一上来就被几万个字节的工程吓退。其中最能代表最小权威服务器的就是tauthteaching authoritative server教学权威服务器。它体量很小却把基础 DNS该有的能力一个不落tauth 能力说明 UDP TCP同时支持两种传输协议 AXFR区域传输入站与出站都实现 通配符支持*泛解析 委派delegation / zone cut 完整支持 Glue 记录委派时自动附带 NS 的 A/AAAA✂️ 截断UDP 超长包自动截断️ 压缩DNS name 标签压缩 EDNS / DNSSEC支持缓冲区协商、服务已签名区域 它刻意保持可教学代码里保留了便于理解的写法方便你逐行读懂一次权威应答的全过程。入口非常薄tauth-main.cc 只是解析ip:port参数后调用launchDNSServer。二、为什么先选对DNS Tree存储模型写权威服务器最容易踩的坑往往不在网络层而在存储模型。很多实现一开始用拍脑袋的结构来存 DNS结果在后面对付空非终端、大小写、glue 的 AA 位、DNSSEC 排序等问题时处处被动。tauth 的设计原则一句话就能说清把 DNS 按 RFC 1034 的树来存很多坑就自动不会发生了。当DNS 名字成为一种由 DNS 标签组成、且天生大小写不敏感的基本类型时很多错误从结构上就不可能被写出来。tauth 的核心正是那棵按 RFC 1034 构想的节点树里面装的全是 DNS 原生对象DNS 标签Label与 DNS 名字Name。三、拆解 tauth 的 DNS Tree节点、区与记录树在 tauth 里的三个用途同一棵 DNS Treetauth 用在了三个地方快速定位某个查询最合适的 zone在那个 zone 内部按名字逐层遍历做DNS name 压缩复用节点来生成压缩指针。也就是说tauth 里其实有两层树一棵是zone 树顶层用于查哪个区负责我每个 zone 内部又是一棵名字树用于查这个域名下的记录。每个节点长什么样整棵树由DNSNode节点对象组成。每个节点最多能带三类东西子节点children——永远在用构成树的分支指向一个 zone 的指针——只在zone 树里用挂载的 RRSet按记录类型 key——只有在定位到正确 zone 后才用来产生真正的应答。三个基础类型都定义在 dns-storage.hh 中是理解整棵树的钥匙DNSLabel一个标签比较与相等天然大小写不敏感它自己实现了一个把a–z视作A–Z的比较器DNSName一串标签组成的完整名字继承标签的大小写不敏感DNSNode树节点持有children、rrsetsmapDNSType, RRSet与可选的zone指针。一个真实 zone 长什么样tdns教学文档里用org区举了个经典例子这棵树深度为 4最顶层节点是空名字相对 zone 名org而言.---. --------- -------- / -- \ / | \ .--. .--. .--. 2 ietf | ietg | ... -- -- --- / \ | / \ | .--. ---. .--. 3 ord | | fra | ... -- -- --- | | .--. .--. 4 ns1 | | ns2 -- ---在第 4 层可以找到ns1.ord.ietf.org和ns2.fra.ietf.org。在 DNS 里查任何东西的关键就是沿着树往下走并留意自己路过了哪些节点。以查找www.ietf.org为例假设org区已加载从第 1 层出发这里有 SOA看有没有叫ietf的子节点——有看这个节点上是否挂着 NS 记录例如ietf.org NS ns1.ord.ietf.org一旦发现说明撞上了 zone cut委派边界权威服务器应立即用这些 NS 记录作为 Authority 段作答把委派交出去为了补全报文再去树里找到ns1.ord.ietf.org的 A/AAAA——这就是glue 记录。两个关键方法add 与 find往树上加东西用幂等的addDNSNode::add 会以幂等方式创建/访问名字对应的节点并返回该节点指针顺手把 RRSet 挂上去。它只负责把路修好从不修改已存在的分支。查名字则用一个更聪明的find。它同样返回节点指针但不修改树还额外告诉你三件事哪些标签没匹配上、是否路过了 zone cut、那个 cut 叫什么名字。DNSNode::find 的精髓在于每匹配一个标签就把已匹配部分从name挪到last如果路过一个挂着 NS 记录的节点就把passedZonecut指向它找不到普通子节点时若允许通配符就去找*节点并一次性吃掉所有剩余标签这就是泛解析的合成。正是靠find返回的这些附带信息后面实现 RFC 1034 算法就变得非常直接。四、记录生成器 RRGen新增一种记录只要改一处RRSet 里存的不是裸字节而是生成器generator例如CNAMEGen::make(...)。生成器知道如何把自己的内容翻译进一个 DNS 报文写入器DNSMessageWriter。每实现一种DNSType至少配一个生成器。以 MX 记录为例完整实现见 record-types.cc它只做三件事一个构造函数存下优先级 服务器名两个字段一个make工厂返回智能指针一个toMessage把 16 位优先级 名字按序写进报文。这个设计的妙处在于记录类型多但新增一种只改两处——在DNSType枚举里加一个数值再写一个XGen结构。tauth 还藏了个动态内容的小彩蛋time.tdns.powerdns.org节点挂了一个ClockTXTGen每次应答时实时用strftime生成当前时间的 TXT 记录。名字在树里是静态的内容却可以随心变化——这正是生成器抽象的价值。⏰五、RFC 1034 权威算法tauth 如何作答一次查询tauth.md 反复强调纯权威服务器可以把 RFC 1034 的算法大幅简化。整个过程集中在processQuestion里逻辑像一次先排除、后作答的分支判断步骤判断条件权威服务器怎么做① 定位在 zone 树里find出最合适的 zone 与节点置 AA 位我有权威② 委派路过了 zone cutpassedZonecut非空去掉 AA 位NS 进 Authority再补 glue③ NXDOMAIN有标签没匹配上、且没跨 zone cutrcode 置 NXDOMAINSOA 进 Authority④ 命中节点存在依次看 CNAME / 匹配类型 / 空非终端下面逐步拆解。第 1 步找到最合适的 zone 与节点先在zone 树上find拿到负责本次查询的那个 zone再在该 zone 的名字树上find拿到最匹配的节点。find一边找一边通过出参告诉你是否路过 NS委派。这一步的完整代码在 tauth.cc。第 2 步如果跨过了 zone cut委派一旦passedZonecut非空说明这个子树不归我管见 tauth.cc去掉 AA 位——你并不是在提供权威答案而是在转发委派把 zone cut 上的NS 记录放进 Authority 段调addAdditional为这些 NS 名字补glueA/AAAA。注意一个务实的取舍addAdditional只在 bestzone 内部找 glue不去别的区里翻。解析器本来也不该信任跨区数据这里就少做一点保持简单。第 3 步NXDOMAIN如果find结束时还有标签没匹配上searchname非空、又没有路过 zone cut那这个名字真的不存在见 tauth.cc把 rcode 置为Nxdomain把该 zone 的SOA 记录放进 Authority 段它携带否定应答的缓存 TTL。有个细节如果这次 NXDOMAIN 是在追 CNAME 途中产生的就不再设置 NXDOMAIN rcode遵循 RFC 1034 §4.3.2 step 3.c。第 4 步节点存在——三条岔路节点找到了可能是通配符合成出来的但我们不必在意接下来分三种情况a. 先看 CNAMEtauth.cc CNAME 若存在它是该节点唯一的记录类型。把它放进 Answer 段取出目标名若目标仍在本 zone内就goto loopCNAME用新目标重跑一遍算法最多追 10 次防环。指向别的区的 CNAME 就不追了good luck。b. 名字存在且有匹配类型tauth.cc 把对应 RRSet 放进 Answer 段若查询类型是ANY则把该节点所有类型都放进去。顺带收集 MX 记录里的服务器名留给 Additional 段。c. 名字存在但没有匹配类型——空非终端tauth.cc 这是 DNS 里最刁钻的场景之一some.long.name.powerdns.org会在沿途凭空造出一串节点但中间某层并没有任何记录。得益于我们用的是树 节点模型处理它变得平凡无奇——直接else进去把 SOA 放进 Authority 段即可NOERROR无答案。六、动手构建并运行 tauth 权威服务器tauth 的依赖极轻构建简单见 tdns/Makefile。跟着做几分钟后你就能在自己的机器上跑起一台最小权威服务器克隆项目git clone https://gitcode.com/gh_mirrors/he/hello-dns cd hello-dns/tdns编译产物含tauth、tdig等make启动权威服务器把127.0.0.1换成你本机 IPsudo ./tauth 127.0.0.1:53用自带的 tdig 客户端发一条查询验证./tdig A www.tdns.powerdns.org 127.0.0.1示例区数据由 contents.cc 的loadZones注入里面已经铺好了 SOA、NS、MX、A、AAAA、CNAME、通配符*.nl、*.fr、委派fra与 glue正好能触发上面讲的每一种应答分支。想观察委派试试fra.tdns.powerdns.org想触发空非终端可以查一个只路过的中间名字。七、小结最小权威服务器的心法读完 tauth你会对最小权威服务器有一个非常清晰的骨架选对存储模型按 RFC 1034 建树DNSNode 大小写不敏感的DNSName让很多 bug 在结构层面就不可能发生。一棵树干三件事定位 zone、遍历名字、做 name 压缩。一个find顶半个算法它把匹配到哪、没匹配上哪、是否路过委派一次性告诉你RFC 1034 的四大应答分支委派 / NXDOMAIN / 命中 / 空非终端因此水到渠成。生成器解耦记录类型新增一种记录只改两处动态内容也能轻松塞进去。如果你想系统理解权威服务器到底在答什么tauth 是目前极少能从一棵树讲到一条报文的教学实现。从 dns-storage.hh 读起再到 tauth.cc 的processQuestion你几乎可以用不到一千行代码把权威 DNS 的精髓完整走一遍。【免费下载链接】hello-dnsHello and welcome to DNS!项目地址: https://gitcode.com/gh_mirrors/he/hello-dns创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表