0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Go的 DNS Lookup 的接口语义

Linux爱好者 来源:Linux爱好者 作者:Linux爱好者 2022-10-25 16:28 次阅读

背景分享

遇到过这么一个问题,有童鞋的 Go 程序用 DNS 解析做服务发现(内网用的 CoreDNS 做的域名解析服务器)。比如,内网有个服务域名,对应 7 个后端节点。为了做服务发现,故障的剔除等服务,在 Client 端对一个给定的域名调用 Go 标准库的 Resolver.LookupHost 方法来解析 ip 列表。如果解析得到的 ip 列表有变化,那么在 Client 内对相应的对后端节点的链接做创建和销毁。

addrs,err:=resolver.LookupHost(ctx,/*某服务域名A*/)

//addrs的结果会变化,一会返回6个ip,一会返回7个ip

就是这么一个典型的服务发现的应用场景,还是精准踩坑。那什么坑?

坑就是:解析得到的 ip 列表反复变化,导致反复创、删连接和对应的结构体。让人误以为 DNS 的后端节点一直在故障,从而导致一系列的问题。

还遇到另一个有趣的问题:同一份业务代码,Go 1.15 编译的版本总会频繁截断成 6 个 ip ,Go 1.16 以上的版本则非常稳定,一直返回 7 个 ip ? 这又是为啥呢。

这个问题很简单,但其实也很隐蔽。因为很少人会这么用,也很少人会注意到这个问题。

Go 的 DNS Lookup 的接口语义

先看下 Go 标准库的接口语义,看下 Resolver.LookupHost 在 Go 的注释怎么说的。文件在 Go 的标准库 net/lookup.go :

//LookupHostlooksupthegivenhostusingthelocalresolver.
//Itreturnsasliceofthathost'saddresses.
func(r*Resolver)LookupHost(ctxcontext.Context,hoststring)(addrs[]string,errerror){
//...
}

LookupHost 查询一个给定的域名,返回值是一个地址列表。注意:它并没有保证,要返回该域名的所有 ip 列表。 所以啊,这本来就是用法不对,Go 的接口没声明说要返回全部的 ip 。哪怕有域名对应有 100 个 ip ,这个接口只返回 1 个也是对的。

Go 1.15 和 Go 1.16之上的区别 ?

域名对应 7 个 ip ,同一份解析代码, Go 1.15 编译的程序时而返回 6 个?但 Go 1.16 之上的版本编译则总是 7 个,感觉非常稳定。为什么呢?

笔者还真翻了一下 Go 1.15 和 Go 1.16 的区别,DNS 解析的代码几乎一致,只在 dnsPacketRoundTrip 函数中,改了一个 buffer 的大小。

Go 1.15 是这样的( 文件:src/net/dnsclient_unix.go ):

funcdnsPacketRoundTrip(cConn,iduint16,querydnsmessage.Question,b[]byte)(dnsmessage.Parser,dnsmessage.Header,error){
//发送请求
if_,err:=c.Write(b);err!=nil{
}

//创建一个装响应包的buffer
b=make([]byte,512)//seeRFC1035
for{
//读取dns响应
n,err:=c.Read(b)
//...
returnp,h,nil
}
}

Go 1.16 是这样的( 文件:src/net/dnsclient_unix.go ):

const(
//MaximumDNSpacketsize.
//Valuetakenfromhttps://dnsflagday.net/2020/.
maxDNSPacketSize=1232
)

funcdnsPacketRoundTrip(cConn,iduint16,querydnsmessage.Question,b[]byte)(dnsmessage.Parser,dnsmessage.Header,error){
//发送请求
if_,err:=c.Write(b);err!=nil{
}

//创建一个装响应包的buffer
b=make([]byte,maxDNSPacketSize)
for{
//读取dns响应
n,err:=c.Read(b)
//...
returnp,h,nil
}
}

函数逻辑是发送请求给 DNS Server ,并等待它的响应。两个版本完全一致,只有 buffer 的大小不一样,Go 1.16 之上用了 1232 这个大小。请注意,这个大小其实是有讲究的,这个值是在尽量避免 IP 包分片又能尽量多装数据而拍的一个值。详细看 DNS FLAG DAY 2020[1] 。

这就是 Go 1.15 ,Go 1.16 版本在内网域名解析中的差异。DNS 服务端虽然发了 7 个 ip 过来,但是 Go 1.15 编译的版本用 512 个字节 buffer 装不下,只解析到 6 个有效 ip,Go 1.16 版本则好点,客户端用的 1232 个字节的 buffer 大一点,差别就在这个地方。

这里有个细节提一下:

DNS 的协议,Message 的 Header 有四个字段 QDCOUNT,ANCOUNT,NSCOUNT,ARCOUNT,是指明了数据包里各个 Record 有多少个,Answer 有多少个的。但是在协议实现的时候,往往不依赖于这几个字段,因为它们可能被伪造攻击。所以解析的 ip 列表都是按照实际解析结果来的,解析到多少个就多少个,而不是 Header 里声明了多少个。

//Qdcount,Ancount,Nscount,Arcountcan'tbetrusted,astheyare
//attackercontrolled.

简单说下 DNS

DNS 协议默认使用 UDP 协议作为其传输层协议。UDP 的数据包是有限制的,DNS 的消息大小也是有限制的,基本大小限制为 512 字节,长消息会被截断并且设置标记位。

所以,DNS 协议本身就从来没承诺过,给你解析完整的 IP 列表。它这个名字对应的 IP 而已,至于全不全,它从没承诺过。

本文并不详细介绍 DNS 协议的原理。详细见 RFC 1035[2] 和相关的文档。为了突破数据包大小,或者其他的限制,也有扩展协议 EDNS ,可以参考 RFC 6891[3] 。

总之,用 DNS 解析 ip 这种方式来替代 consul 这种服务发现。感觉还是有欠缺的,或者说它的使用场景是不一样的。

总结

Go 默认的解析方式其实有两种,一种是 cgo 的方式调用 libc 库的函数去解析,一种是 Go 自己的实现。本文讨论的是默认的 Go 的方式。

DNS 做服务发现好像并不合适,和 consul 等组件不同。它有自己特定的协议约束着,如果一定要用 DNS 来做服务发现,那么请千万要注意本文提到的知识点。

DNS 解析的 ip 列表,并不承诺它是全的。如果业务想用来做服务发现和剔除的功能,请千万牢记。

DNS 服务端和客户端的行为配合缺一不可,服务端会不会发全部?客户端能不能收全部?各种差异都会导致解析出来的 ip 可能不一样。

审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    7639

    浏览量

    148495
  • 服务器
    +关注

    关注

    12

    文章

    8116

    浏览量

    82518
  • 编译
    +关注

    关注

    0

    文章

    615

    浏览量

    32392

原文标题:DNS 做服务发现,是坑吗 ?

文章出处:【微信号:LinuxHub,微信公众号:Linux爱好者】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    基于OWL属性特征的语义检索研究

    【来源】:《电子设计工程》2010年02期【摘要】:在文献检索中,概念的语义相似度计算直接影响查准率和查全率指标。将本体描述语言OWL(Web Ontology Language)的属性特征有机结合
    发表于 04-24 09:48

    什么是域名DNS

    什么是域名DNS为什么要注册DNS呢,申请了DNS后,客户可以自己为域名作解析,或增设子域名.客户申请DNS时,建议客户一次性申请两个。 在国际域名网网站上注册域名,一般都是
    发表于 07-19 00:13

    语义机器人

    人工智能将迎来语义理解新时代。打破了传统人工智能在语言交互方面反射式的应答方式,成功地通过独创的中文语义理解算法,让计算机可以准确理解语言环境,进行上下文处理、口语处理、省略处理。该平台可用于构建
    发表于 03-10 16:52

    使用JavaScript代码在Rapid板子上实现DNS解析域名得到IP地址操作分享!

    function key_cb(name){print(name);dns.lookup(“bbs.o2ee.com”, dns_cb);}key.on(key_cb);function loop
    发表于 08-15 04:17

    为什么编译时有Warning说这个函数有定义未声明?

    在LWIP1.3里面dns.c里面有定义了一个static u32_t dns_lookup(const char *name){。。。。}并且在dns.h里面进行了声明static u32_t
    发表于 10-30 22:29

    DNS错误

    嗨,我试着使用DNS,但是总是会遇到DNS错误。我总是收到“DNS错误发生”消息。使用TCP/IP栈v5.41我有什么问题吗?问候彼得
    发表于 04-20 06:14

    什么是DNS

    什么是DNS  英文缩写: DNS 中文译名: 域名系统 分  类: IP与多媒体 解  释: 遍布于因特网的数据库。它
    发表于 02-22 17:25 1053次阅读

    DNS是什么DNS原理入门指南资料概述

    DNS 是互联网核心协议之一。不管是上网浏览,还是编程开发,都需要了解一点它的知识。本文详细介绍DNS的原理,以及如何运用工具软件观察它的运作。我的目标是,读完此文后,你就能完全理解DNS
    的头像 发表于 02-03 08:58 5022次阅读

    DNS服务器和DNS服务器地址是什么

    今日就来同大家分享什么是DNS服务器地址,DNS服务器地址怎么找,帮助大家更了解DNS服务器。首先,我们一起来聊聊什么是DNS服务器,什么是DNS
    的头像 发表于 03-30 15:57 7887次阅读

    DNS污染是是什么,DNS污染怎么解决

    DNS污染是指有意或无意进行的域名服务器分组,将域名指向错误的IP地址。 什么是DNS污染? DNS污染有哪些处理方法 一般而言,网站通常在Internet上具有可靠的域名服务器,但是为了减少
    发表于 04-19 09:43 1w次阅读

    Tcp-DNS-proxy TCP DNS代理

    Tcp-DNS-proxy.zip
    发表于 04-29 10:44 2次下载
    Tcp-<b class='flag-5'>DNS</b>-proxy TCP <b class='flag-5'>DNS</b>代理

    DNS基本概述

    与 HTTP、FTP 和 SMTP 一样,DNS 协议也是一种应用层的协议,DNS 使用客户-服务器模式运行在通信的端系统之间,在通信的端系统之间通过 UDP 运输层协议来传送 DNS 报文。
    的头像 发表于 05-25 10:49 2421次阅读

    dns是什么意思 dns作用是什么介绍

    dns是什么意思 dns作用是什么介绍
    发表于 10-17 14:44 0次下载

    介绍时序分析的基本概念lookup table

    今天要介绍的时序分析基本概念是lookup table。中文全称时序查找表。
    的头像 发表于 07-03 14:30 757次阅读
    介绍时序分析的基本概念<b class='flag-5'>lookup</b> table

    DNS服务器是什么?有哪些类型?

    每当我们单击网站上的链接时,我们的计算机都会搜索其DNS信息。DNS是互联网地址的数据库。在互联网上搜索信息时,我们会提交DNS请求。DNS只是与IP相对应的名称列表,计算机通过IP进
    的头像 发表于 08-14 17:40 1437次阅读