tianchaoshangguo 2020-06-08
域名(Domain Name),是由一串用点分隔的名字组成的互联网上某台计算机或某组计算机的标识,它的目的是为了方便人们更简单便捷地访问互联网上的服务。在实际的系统实现中,域名通过DNS(Domain Name System)系统转化为服务器的IP地址,以方便机器通过IP进行寻址和通信。上述行为,我们称之为域名解析
网络通讯大部分是基于TCP/IP的,而TCP/IP是基于IP地址的,所以计算机在网络上进行通讯时只能识别如“202.96.134.133”之类的IP地址,而不能认识域名。我们无法记住10个以上IP地址的网站,所以我们访问网站时,更多的是在浏览器地址栏中输入域名,就能看到所需要的页面,这是因为有一个叫“DNS服务器”的计算机自动把我们的域名“翻译”成了相应的IP地址,然后调出IP地址所对应的网页
作为一次网络通信最前置的环节,域名解析的重要性不言而喻。在传统的基于浏览器的网站访问场景下,域名解析环节由浏览器内核实现,网站开发者无需关心域名解析的细节。But there are always two sides to every coin,一旦域名解析环节发生异常,开发者面对这样的黑盒架构就会显得束手无策,一个很典型的例子即域名劫持问题
进入移动互联网时代,大量的应用基于C/S架构构建。相较于传统的面向浏览器的Web App,C/S架构的应用赋予了我们非常大的软件定制空间,开发者甚至可以渗透到整个应用的底层网络实现当中,域名解析环节的优化因此变为了可能
域名系统(英文:Domain Name System,缩写:DNS)是一种组织成域层次结构的计算机和网络服务命名系统,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。DNS就是这样的一位“翻译官”,它的基本工作原理可用下图来表示。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网
DNS解析是分布式存储的,比如根域名服务器ROOT DNS,只存储260个顶级域名的DNS服务器的ip地址。顶级域名服务器如.com的DNS服务器,存储的则是一些一级域名的权威DNS服务器地址(如suning.com,qq.com,163.com的DNS)。而suning.com的权威DNS存储的才是具体的主机记录(如A记录,cname记录,txt记录)
DNS系统一般采用树状结构进行组织,以ru.wikipedia.org
为例,org
为顶级域名,wikipedia
为二级域名,ru
为三级域名,域名树状组织结构如下图所示
根域名服务器(ROOT)
根服务器主要用来管理互联网的主目录,全世界IPv4根服务器只有13台(这13台IPv4根域名服务器名字分别为“A”至“M”),1个为主根服务器在美国。其余12个均为辅根服务器,其中9个在美国,欧洲2个,位于英国和瑞典,亚洲1个位于日本。根服务器中有经美国政府批准的260个左右的互联网后缀(如.com、.net、.cn等)
顶级域名服务器
负责解析本身顶级域名下一级域名对应的权威DNS服务器地址
localDNS 本地DNS
一般是运营商的dns,主要作用是代理用户进行迭代解析
本地host记录
这个优先级最高可以在自己电脑自定义域名的解析记录,如果本机有就不会再往上迭代。PC的host(C:\Windows\System32\drivers\etc\hosts)
权威DNS即最终决定域名解析结果的服务器,开发者可以在权威DNS上配置、变更、删除具体域名的对应解析结果信息,阿里云云解析即权威DNS服务提供商
递归DNS又称为Local DNS,它没有域名解析结果的决定权,但代理了用户向权威DNS获取域名解析结果的过程。递归DNS上有缓存模块,当目标域名存在缓存解析结果并且TTL未过期时(每个域名都有TTL时间,即有效生存时间,若域名解析结果缓存的时间超过TTL,需要重新向权威DNS获取解析结果),递归DNS会返回缓存结果,否则,递归DNS会一级一级地查询各个层级域名的权威DNS直至获取最终完整域名的解析结果
公共DNS是递归DNS的一种特例,它是一种全网开放的递归DNS服务,而传统的递归DNS信息一般由运营商分发给用户。一个比较典型的公共DNS即Google的8.8.8.8
,我们可以通过在操作系统配置文件中配置公共DNS来代替Local DNS完成域名解析流程
在实际的使用过程中,我们通常不需要手工指定自己的Local DNS地址。运营商会通过DHCP协议在系统网络初始化阶段将Local DNS地址分配给我们的计算机。当我们需要使用公共DNS服务时,我们就必须手工指定这些服务的地址。以Linux为例,我们可以通过在‘/etc/resolv.conf‘中添加Local DNS地址项来改变本机Local DNS的地址
主机向本地域名服务器的查询一般都是采用递归查询。所谓递归查询就是:如果主机所询问的本地域名服务器不知道被查询的域名的IP地址,那么本地域名服务器就以DNS客户的身份,向其它根域名服务器继续发出查询请求报文(即替主机继续查询),而不是让主机自己进行下一步查询。因此,递归查询返回的查询结果或者是所要查询的IP地址,或者是报错,表示无法查询到所需的IP地址
本地域名服务器向根域名服务器的查询的迭代查询。迭代查询的特点:当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP地址,要么告诉本地服务器:“你下一步应当向哪一个域名服务器进行查询”。然后让本地服务器进行后续的查询。根域名服务器通常是把自己知道的顶级域名服务器的IP地址告诉本地域名服务器,让本地域名服务器再向顶级域名服务器查询。顶级域名服务器在收到本地域名服务器的查询请求后,要么给出所要查询的IP地址,要么告诉本地服务器下一步应当向哪一个权限域名服务器进行查询。最后,知道了所要解析的IP地址或报错,然后把这个结果返回给发起查询的主机
A记录
A代表的是Address,用来指定域名对应的IP地址,比如将map.baidu.com指定到180.97.34.157,将zhidao.baidu.com指定到180.149.131.245,A记录允许将多个域名解析到一个IP地址,但不允许将一个域名解析到多个IP地址上
MX记录
MX代表的是Mail Exchage,就是可以将某个域名下的邮件服务器指向自己的Mail Server,如baidu.com域名的A记录IP地址是180.97.34.157,如果将MX记录设置为180.97.34.154,即的邮件路由,那么DNS会将邮件发送到180.97.34.154所在的服务器,而正常web请求仍然会解析到A记录的IP地址180.97.34.157
CNAME记录
CNAME指的就是Canonical Name,也就是别名解析,可以将指定的域名解析到其他域名上,而其他域名就是指定域名的别名,整个解析过程称为别名解析。比如将baidu.com解析到itlemon.cn,将csdn.net解析到itlemon.cn,那么itlemon.cn就是baidu.com和CSDN.net的别名
NS记录
就是为某个域名指定了特定的DNS
服务器去解析
TXT记录
为某个主机名或者域名设置特定的说明,比如为itlemon.cn设置的的TXT记录为“Lemon的技术笔记”,这个TXT记录为itlemon.cn的说明
检查浏览器缓存中是否缓存过该域名对应的IP地址
用户通过浏览器浏览过某网站之后,浏览器就会自动缓存该网站域名对应的IP
地址,当用户再次访问的时候,浏览器就会从缓存中查找该域名对应的IP地址,因为缓存不仅是有大小限制,而且还有时间限制(域名被缓存的时间通过TTL
属性来设置),所以存在域名对应的IP
找不到的情况。当浏览器从缓存中找到了该网站域名对应的IP
地址,那么整个DNS
解析过程结束,如果没有找到,将进行下一步骤。对于IP
的缓存时间问题,不宜设置太长的缓存时间,时间太长,如果域名对应的IP
发生变化,那么用户将在一段时间内无法正常访问到网站,如果太短,那么又造成频繁解析域名
如果在浏览器缓存中没有找到IP,那么将继续查找本机系统是否缓存过IP
如果第一个步骤没有完成对域名的解析过程,那么浏览器会去系统缓存中查找系统是否缓存过这个域名对应的IP
地址,也可以理解为系统自己也具备域名解析的基本能力。在Windows
系统中,可以通过设置hosts
文件来将域名手动绑定到某IP
上,hosts
文件位置在C:\Windows\System32\drivers\etc\hosts
。对于普通用户,并不推荐自己手动绑定域名和IP
,对于开发者来说,通过绑定域名和IP
,可以轻松切换环境,可以从测试环境切换到开发环境,方便开发和测试。在XP
系统中,黑客常常修改他的电脑的hosts
文件,将用户常常访问的域名绑定到他指定的IP
上,从而实现了本地DNS
解析,导致这些域名被劫持。在Linux
或者Mac
系统中,hosts
文件在/etc/hosts
,修改该文件也可以实现同样的目的
前两步都是在本机上完成的,所以没有在上面示例图上展示出来,从第三步开始,才正在地向远程DNS服务器发起解析域名的请求
向本地域名解析服务系统发起域名解析的请求
如果在本机上无法完成域名的解析,那么系统只能请求本地域名解析服务系统进行解析,本地域名系统LDNS
一般都是本地区的域名服务器,比如你连接的校园网,那么域名解析系统就在你的校园机房里,如果你连接的是电信、移动或者联通的网络,那么本地域名解析服务器就在本地区,由各自的运营商来提供服务。对于本地DNS
服务器地址,Windows
系统使用命令ipconfig
就可以查看,在Linux
和Mac
系统下,直接使用命令cat /etc/resolv.conf
来查看LDNS
服务地址。LDNS
一般都缓存了大部分的域名解析的结果,当然缓存时间也受域名失效时间控制,大部分的解析工作到这里就差不多已经结束了,LDNS
负责了大部分的解析工作
向根域名解析服务器发起域名解析请求
本地DNS
域名解析器还没有完成解析的话,那么本地域名解析服务器将向根域名服务器发起解析请求
根域名服务器返回gTLD域名解析服务器地址
本地DNS
域名解析向根域名服务器发起解析请求,根域名服务器返回的是所查域的通用顶级域(Generic top-level domain,gTLD
)地址,常见的通用顶级域有.com
、.cn
、.org
、.edu
等
向gTLD服务器发起解析请求
本地域名解析服务器向gTLD服务器发起请求
gTLD服务器接收请求并返回Name Server服务器
gTLD
服务器接收本地域名服务器发起的请求,并根据需要解析的域名,找到该域名对应的Name Server
域名服务器,通常情况下,这个Name Server
服务器就是你注册的域名服务器,那么你注册的域名的服务商的服务器将承担起域名解析的任务
Name Server服务器返回IP地址给本地服务器
Name Server
服务器查找域名对应的IP
地址,将IP
地址连同TTL
值返回给本地域名服务器
本地域名服务器缓存解析结果
本地域名服务器缓存解析后的结果,缓存时间由TTL
时间来控制
返回解析结果给用户
解析结果将直接返回给用户,用户系统将缓存该IP
地址,缓存时间由TTL
来控制,至此,解析过程结束
现在的DNS主要还是通过UDP来通讯,大小不超过1500个字节,结构如下:
+--+--+--+--+--+--+--+ | Header | +--+--+--+--+--+--+--+ | Question | +--+--+--+--+--+--+--+ | Answer | +--+--+--+--+--+--+--+ | Authority | +--+--+--+--+--+--+--+ | Additional | +--+--+--+--+--+--+--+
按格式来区分的话,可以分为三部分:头部Header,查询部分Question,应答部分:Answer,Authority,Additional
各字段作用
ID: 2个字节(16bit),标识字段,客户端会解析服务器返回的DNS应答报文,获取ID值与请求报文设置的ID值做比较,如果相同,则认为是同一个DNS会话
FLAGS: 2个字节(16bit)的标志字段。包含以下属性
QR: 0表示查询报文,1表示响应报文
opcode: 通常值为0(标准查询),其他值为1(反向查询)和2(服务器状态请求),[3,15]保留值
AA: 表示授权回答(authoritative answer)– 这个比特位在应答的时候才有意义,指出给出应答的服务器是查询域名的授权解析服务器
TC: 表示可截断的(truncated)–用来指出报文比允许的长度还要长,导致被截断
RD: 表示期望递归(Recursion Desired) – 这个比特位被请求设置,应答的时候使用的相同的值返回。如果设置了RD,就建议域名服务器进行递归解析,递归查询的支持是可选的
RA: 表示支持递归(Recursion Available) – 这个比特位在应答中设置或取消,用来代表服务器是否支持递归查询
Z : 保留值,暂未使用
RCODE: 应答码(Response code) - 这4个比特位在应答报文中设置,代表的含义如下
0 : 没有错误
1 : 报文格式错误(Format error) - 服务器不能理解请求的报文
2 : 服务器失败(Server failure) - 因为服务器的原因导致没办法处理这个请求
3 : 名字错误(Name Error) - 只有对授权域名解析服务器有意义,指出解析的域名不存在
4 : 没有实现(Not Implemented) - 域名服务器不支持查询类型
5 : 拒绝(Refused) - 服务器由于设置的策略拒绝给出应答.比如,服务器不希望对某些请求者给出应答,或者服务器不希望进行某些操作(比如区域传送zone transfer)
[6,15] : 保留值,暂未使用
QDCOUNT: 无符号16bit整数表示报文请求段中的问题记录数
ANCOUNT: 无符号16bit整数表示报文回答段中的回答记录数
NSCOUNT: 无符号16bit整数表示报文授权段中的授权记录数
ARCOUNT: 无符号16bit整数表示报文附加段中的附加记录数
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ... | | QNAME | | ... | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QTYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QCLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
QNAME 8bit为单位表示的查询名(广泛的说就是:域名)
QTYPE 无符号16bit整数表示查询的协议类型
QCLASS 无符号16bit整数表示查询的类,比如,IN代表Internet
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NAME | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | CLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TTL | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDLENGTH | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDATA | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
NAME 资源记录包含的域名
TYPE 表示DNS协议的类型
CLASS 表示RDATA的类
TTL 4字节无符号整数表示资源记录可以缓存的时间。0代表只能被传输,但是不能被缓存
RDLENGTH 2个字节无符号整数表示RDATA的长度
RDATA 不定长字符串来表示记录,格式根TYPE和CLASS有关。比如,TYPE是A,CLASS 是 IN,那么RDATA就是一个4个字节的ARPA网络地址
域名劫持一直是困扰许多开发者的问题之一,其表现即域名A应该返回的DNS解析结果IP1被恶意替换为了IP2,导致A的访问失败或访问了一个不安全的站点。下面我们一起看看几种常见的域名劫持的场景
一种可能的域名劫持方式即黑客侵入了宽带路由器并对终端用户的Local DNS进行篡改,指向黑客自己伪造的Local DNS,进而通过控制Local DNS的逻辑返回错误的IP信息进行域名劫持。另一方面,由于DNS解析主要是基于UDP协议,除了上述攻击行为外,攻击者还可以监听终端用户的域名解析请求,并在Local DNS返回正确结果之前将伪造的DNS解析响应传递给终端用户,进而控制终端用户的域名访问行为
上述攻击行为的影响面相对比较有限,另一种我们最常碰到的域名劫持现象是缓存污染。我们知道在接收到域名解析请求时,Local DNS首先会查找缓存,如果缓存命中就会直接返回缓存结果,不再进行递归DNS查询。这时候如果Local DNS针对部分域名的缓存进行更改,比如将缓存结果指向第三方的广告页,就会导致用户的访问请求被引导到这些广告页地址上
对比第一种攻击,这类缓存污染往往能带来更明显的群体伤害,比如某个省份某个运营商的用户群可能因为该地区Local DNS的缓存污染而导致访问服务异常。这类缓存污染行为往往是间歇性、局部性发生的,没有明显的规律,导致开发者很难对其进行量化、评估、预防
除了域名劫持问题,基于传统Local DNS的域名解析还会带来域名调度精准性的问题。对于类似CDN域名访问这类需要按地域、运营商进行智能解析调度的场景,精准调度的诉求是十分强烈的
关于调度不精准的原因,我们主要可以从两个方面来探究一下。第一个常见的问题即解析转发
部分Local DNS供应商为了降低运营成本,会将请求到自己节点的域名解析请求转发给其他供应商的Local DNS节点,如上图所示。假如用户请求解析一个CDN域名cdn.aliyun.com
,用户分配到的Local DNS A为了节省成本,把该次请求转发给了另一运营商的Local DNS B,权威DNS在进行域名解析时会根据Local DNS的IP信息进行智能调度,即权威DNS会根据Local DNS B的IP78.29.29.1
进行调度,分配与78.29.29.1
相同运营商并且地理位置最近的CDN节点78.29.29.2
,然而这个CDN节点对于终端135.35.35.1
而言并不是最优的CDN节点,他们分属不同的运营商,并且地理位置上可能相隔很远。这类解析转发行为会严重影响域名解析的精准性并对用户业务访问延迟带来影响
除了解析转发对调度精准性带来的影响外,Local DNS的布署情况同样影响着域名智能解析的精准性
如上图所示,部分运营商Local DNS的布点受成本因素制约分布并不均匀,比如在东部地区部署比较密集,但在西部地区部署比较稀疏。这时候当一位西藏的用户准备访问CDN节点时,我们预期他应该会被调度到西藏的CDN节点A上以实现就近接入和访问加速。但由于Local DNS的资源有限,西部地区的终端用户被统一调度到青海的Local DNS B上,这时候权威DNS根据Local DNS B的IP进行CDN域名的智能解析,并将青海的CDN节点B返回给西藏用户,导致用户的网络访问延迟上升。另一种我们实际发现的情况是Local DNS的分配甚至并非遵循就近原则,比如有实际案例显示西藏的用户甚至被分配了北京的Local DNS节点C,导致西藏的用户在进行CDN资源访问时被调度到了北京的CDN节点C上,类似的由于调度精度的缺失带来的访问体验的影响是非常严重的
部分业务场景下开发者对域名解析结果变更的生效时间非常敏感(这部分变更操作是开发者在权威DNS上完成的),比如当业务服务器受到攻击时,我们需要最快速地将业务IP切换到另一组集群上,这样的诉求在传统域名解析体系下是无法完成的
Local DNS的部署是由各个地区的各个运营商独立部署的,因此各个Local DNS的服务质量参差不齐。在对域名解析缓存的处理上,各个独立节点的实现策略也有区别,比如部分节点为了节省开支忽略了域名解析结果的TTL时间限制,导致用户在权威DNS变更的解析结果全网生效的周期非常漫长,这类延迟生效可能直接导致用户业务访问的异常