DNS协议既可通过UDP也能够以TCP为载体进行传输,所指定的端口编号均为53,不过通常DNS传输过程会选用UDP作为媒介。
关于DNS协议的说明已经呈现,若需详尽探究此协议,请查阅网上的资料:DNS - MDN Web文档术语表:Web相关术语的解释 | MDN
2 DNS域名介绍
DNS域名通常分为以下几类:
根域名服务器位于DNS架构的最高层级,它承担着维护DNS命名空间中根区域(Root Zone)的责任。该服务器的主要功能是进行查询引导,具体表现为将请求定向至各个顶级域(TLD)的权威服务器上。
最高级别域名服务器:承担着解析专有顶级域名的职责,诸如商业性质的.com、非盈利性质的.org、网络服务性质的.net,亦或是国家特有的域名如.cn和.uk。
权威DNS服务器:负责存储并提供特定域名的DNS记录信息
本地DNS服务器,就是电脑在解析域名时的首选服务器,也是备选服务器的设置目标。通常情况下,电脑会优先使用这个服务器。常见的本地DNS服务提供商包括电信、联通、谷歌以及阿里等。
3 DNS查询方式
DNS查询方式分为以下两种:
递归查询是指DNS客户端,例如用户设备或本地域名服务器,向DNS服务器发出的查询要求,DNS服务器承担整个查询任务,然后把最终解析的答案交给客户端。
查询的逐步指引,是DNS服务器提供给用户或请求者的后续步骤,并非直接给出最终解析数据,需要用户自行进行重复查询,才能逐一得到完整信息。
下面两张图则是递归查询和迭代查询的工作流程图。


4 DNS协议的基本工作流程
下面,我们通过PC端正向解析这个案例,来认识一下DNS解析的运作过程。
先去查浏览器的 DNS 记忆库,里面存着网址和实际地址的关联清单。
2)若没有命中,则继续搜索「操作系统的 DNS 缓存」;
如果没有成功匹配,那么操作系统会向「本地域名服务器」传输域名,本地域名服务器会检查自身的 DNS 缓存,若找到匹配项,便会将查询结果反馈回来,务必留意,主机与本地域名服务器之间的查询过程采用的是「递归查询」模式。
本地域名服务器的 DNS 缓存若未找到匹配项,该服务器会向更高级别的域名服务器发起请求,这种查询模式称为「逐级查询」,目的是减轻根域名服务器的负担,确保查询过程不会过度依赖单一服务器,本地与其它域名服务器间的交互均采用这种查询方式
本地域名服务器首先向「根域名服务器」发出查询请求,根域名服务器作为最高级别的服务器,不会直接提供域名对应的IP地址,而是会将顶级域名服务器的地址反馈给本地域名服务器,这样本地域名服务器就获得了查询路径,知道接下来应该向哪里寻求答案。
本地域名服务器接收到这个「顶级域名服务器」的地址信息之后,便开始向其发送查询请求,目的是查询并得到「权限域名服务器」的地址信息
本地域名解析器依据授权域名解析器的位置信息向其发送查询,从而获取该域名所指的 IP 地址
本地域名服务器将查询到的 IP 地址传送给操作系统,并且自身也将该 IP 地址存储在缓存中
系统把网络标识交给网页查看器,并且自身也把网络标识存放在临时存储区
现在,浏览器已经获取到了域名所对应的 IP,同时将这个 IP 存放在了缓存中
配合下图直观理解:

在处理.io域名时,我们只需要向本地域名服务器发出DNS查询信息,随后对收到的DNS应答信息进行解析。
5 DNS报文
DNS报文分为以下五个部分:
报文头部:定义了请求或响应的元信息(如标志、条目数等)。
问题区域:描述了查询的域名和查询类型。
回答区域:包含查询的最终结果(如域名对应的IP地址)。
权威区域:提供权威DNS服务器的信息。
附加区域:包含附加的相关信息(如域名的A记录)。
DNS查询信息包含核心部分和查询部分,其中回应部分、权威部分以及补充部分均未包含内容。
报文头部
ID由16位数字构成,具备唯一性,其作用在于将请求与响应相互对应。
标志:长度固定为十六位,用于指示不同属性,比如查询的性质、是否期望递归等。
:固定长度为16bit,问题区域的条目数,通常为1。
RRs:固定占用16位二进制位,代表响应部分包含的记录项数量,在查询数据包中该值为零。
RRs:长度固定为十六位,代表权威区域内的记录条目数量,在查询消息中该值为零。
RRs:固定占16位二进制位,代表附加部分的记录条目数量,在查询数据包中该值为零。
问题区域
QName:查询的域名(以点分形式存储)。
查询的记录类型指的是特定格式,例如A记录、AAAA记录、MX记录等。
:查询的记录类别,通常为IN(互联网)。
DNS的回应信息结构跟请求信息类似,既有相似的起始部分和询问部分,还增加了提供答案、权威来源和补充数据的部分。
报文头部:同请求报文,但Flags内容有所变化:
QR:1表示响应(查询报文中为0)
RCODE:返回代码,用于说明响应情况,比如数值0代表没有问题,数值3代表找不到域名。
AA:权威回答标志(1表示这是权威服务器返回的响应)。
问题区域:与请求报文一致,用于描述客户端的查询。
查询区域设有查询结果,例如域名关联的IP地址,每项查询均涵盖以下要素:
Name:对应的域名
Type:记录类型(如A、AAAA、CNAME等)。
Class:记录类别(通常为IN)。
TTL:记录的生存时间(秒)。
Rdata:记录的具体值(如IP地址)。
权威区域:提供权威服务器的信息,通常包含NS记录。
附加部分:囊括了权威服务器的A型记录和AAAA型记录
请求报文实例:请求解析域名.io的A记录
| 报文头部 | Transaction ID: 0x8D12 Flags: 0x0100 (标准查询、期望递归) Questions: 1 Answer RRs: 0 Authority RRs: 0 Additional RRs: 0 | 问题区域 | QName:wiznet.io QType: A QClass: IN | 报文原文 | 8D十二零一零零零零一零零零零 那个六十七十六十六六十四十六六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六十六
DNS服务器反馈.io域名的A记录查询结果,其对应的IP地址是183.111.138.249
| 报文头部 | Transaction ID: 0x8D12 Flags: 0x8180 (响应、无错误) Questions: 1 Answer RRs: 1 Authority RRs: 0 Additional RRs: 0 | 问题区域 | QName:wiznet.io QType: A QClass: IN | 回答区域 | Name:wiznet.io Type: A Class: IN TTL: 156 RData: 183.111.138.249 | 报文原文 | 八D十二八十一八十零零零一零零零零零 那个数字序列由若干部分组成,分别是六十七,六十九,七十七,六十四,六十六,一百零四,零二,十九,零零,一,零一,零一。 C0零个零C零个零零个零零个零九C零个零四B七六F八A九F
6 实现过程
接下来,我们看看如何在上实现DNS正向解析。
请注意,本示例需要联网访问,请确认您的设置支持联网功能。
步骤一:注册DNS定时器中断到1s定时器中
/**
简述毫秒定时器中断服务程序
* @param none
* @return none
*/
void TIM3_IRQHandler(void)
{
静态的32位无符号整数tim3_1ms计数器初始值为零,该计数器用于计时。
当检测到TIM3的更新中断标志位时,确认其状态并非未置位
{
tim3_1ms_count++;
if (tim3_1ms_count >= 1000)
{
DHCP_time_handler();
DNS_time_handler();
tim3_1ms_count = 0;
}
清除定时器三的更新中断挂起标志位
}
}
注册DNS定时器中断主要为了DNS超时处理。
dns.h文件里,规定了DNS的等待时限,还有尝试的次数,以及连接所用的端口,和报文标识符等细节。
定义最大域名缓冲区大小为二百五十六个字节,///< maximum size of DNS buffer. */ /* * @brief Maxium length of your queried Domain name * @todo SHOULD BE defined it equal as or greater than your Domain name lenght + null character(1) * @note SHOULD BE careful to stack overflow because it is allocated 1.5 times as MAX_DOMAIN_NAME in stack. */ #define MAX_DOMAIN_NAME 128 // for example "www.google.com" #define MAX_DNS_RETRY 2 ///< Requery Count #define DNS_WAIT_TIME 3 ///< Wait response time. unit 1s. #define IPPORT_DOMAIN 53 ///< DNS server port number #define DNS_MSG_ID 0x1122 ///< ID for DNS message. You can be modifyed it any number
步骤二:进行DNS正向解析处理
在()函数中,我们实现了dns正向解析的过程。
执行域名解析操作,参数为以太网数据包,域名目标,以及来自DNS的IP地址
这个函数接收三个参数,第一个是用于DNS解析的缓存数据,第二个是需要解析的域名,第三个是解析完成后的IP地址。
()函数的内容如下:
/**
域名解析过程概述
该参数指代以太网缓冲区,用以存放数据包
该参数指定需要解析的域名
该参数指定已解析的互联网协议地址
返回值,若操作成功则为零,若操作未成功则为负一
*/
完成域名解析功能,需要输入缓冲区指针,域名字符串指针,以及域名对应的IP地址指针
{
int dns_ok_flag = 0;
int dns_run_flag = 1;
wiz_NetInfo net_info;
字节型变量dns重试次数初始为0
初始化DNS客户端,参数设置为0,数据缓冲区为buf。
获取网络信息到net_info结构体中
while (1)
{
评估DNS运行结果,依据网络信息中的DNS配置,针对指定域名和IP地址执行操作,查看函数返回状态
{
情况为DNS解析失败时,域名已正确获取
{
if (dns_retry_cnt < DNS_RETRY) // Determine whether the parsing is successful or whether the parsing exceeds the number of times
{
dns_retry_cnt++;
}
else
{
printf(" > DNS Failedrn");
dns_ok_flag = -1;
dns_run_flag = 0;
}
break;
}
case DNS_RET_SUCCESS: {
输出转换后的域名,其值为指定IP地址的各个段,具体为第一段数值,第二段数值,第三段数值,第四段数值,并换行显示
dns_ok_flag = 0;
dns_run_flag = 0;
break;
}
}
if (dns_run_flag != 1)
{
return dns_ok_flag;
}
}
}
接下来会去调用DNS核心函数,该函数主要负责打包DNS数据包,发出查询请求,解析返回信息以及处理超时情况,具体操作只需要依据括号内函数给出的结果来执行相应动作即可。
()函数内容如下:
/* DNS CLIENT RUN */
DNS运行时,接收域名参数,查询对应IP,并将结果返回给调用方,整个过程使用指定的IP地址作为通信媒介,最终将解析到的IP信息存储在输出缓冲区中
{
int8_t ret;
struct dhdr dhp;
uint8_t ip[4];
uint16_t len, port;
int8_t ret_check_timeout;
retry_count = 0;
dns_1s_tick = 0;
// Socket open
创建套接字,指定类型为数据报,本地端口为0,本地地址为0,并绑定到指定名称的套接字对象上
#ifdef _DNS_DEBUG_
输出向DNS服务器的查询信息,包括IP地址各段数值,分别为第一个段数值,第二个段数值,第三个段数值,和第四个段数值。
#endif
查询长度通过函数 dns_makequery 计算得出,参数为零,名称为 name,使用 pDNSMSG 作为消息类型,最大缓冲区大小为 MAX_DNS_BUF_SIZE
发送给DNS套接字,数据内容为pDNSMSG,长度为len,目标IP为dns_ip,端口为IPPORT_DOMAIN。
while (1)
{
当获取DNS套接字接收缓冲区长度为getSn_RX_RSR函数返回值时,若该长度大于零,
{
当长度超出最大DNS缓冲区尺寸时,长度将被设定为该最大尺寸。
接收数据报,存入DNS消息缓冲区,记录源地址和端口号,获取长度值,完成数据传输过程
#ifdef _DNS_DEBUG_
接收到来自%d.%d.%d.%d(%d)的DNS消息, 长度为%drn, 其中%d是端口编号
ip[3],port,len);
#endif
计算结果赋值给变量ret,通过解析DNS消息,参数包括dhp指针,pDNSMSG内容,以及从DNS获取的ip地址
break;
}
// Check Timeout
验证超时时间,调用检查DNS超时函数,获取结果赋值给ret_check_timeout变量。
if (ret_check_timeout < 0) {
#ifdef _DNS_DEBUG_
printf(" >DNS服务器没有响应:%d.%d.%d.%d,其中第一段是%d,第二段是%d,第三段是%d,第四段是%d。
#endif
close(DNS_SOCKET);
return 0; // timeout occurred
}
如果返回检查超时等于零,那么
#ifdef _DNS_DEBUG_
printf(" > DNS Timeoutrn");
#endif
发送给DNS套接字,数据为pDNSMSG,长度为len,目标IP为dns_ip,端口为IPPORT_DOMAIN。
}
}
close(DNS_SOCKET);
// Return value
尝试没有达成目标,结果却是顺利完成了预期
return ret;
}
7 运行结果
程序执行初始阶段,系统先检测物理链路状态,接着尝试通过DHCP协议获取网络参数,最终确认DNS服务器已正确解析.io域名对应的IP数值为183.111.138.249,具体情形参照下图所示。
8 总结
本文阐述在硬件上构建 DNS 域名解析功能的技术,说明怎样将 .io 域名转换为具体 IP 地址。说明 DNS 协议核心原理、域名分级体系、查询机制和操作步骤,介绍 DNS 消息格式以及请求和回复消息范例等。演示了该功能的具体构建步骤。
下回会阐释这款芯片如何达成 HTTP 功能,说明向目标站点传输信息的机制和具体操作方法,请大家关注!
该公司为一家不拥有晶圆厂的半导体企业,其成立于 1998 年。该企业所生产的产品涵盖互联网处理器 iMCU™,此款处理器运用了 TOE 技术,该技术指的是 TCP/IP 卸载引擎,并且其核心是具备专利性质的全硬件 TCP/IP 架构。iMCU™ 主要面向多种用途的嵌入式网络设备。
公司遍及全球,合作商家超过七十家,在香港地区、韩国本土以及美国本土均设有分支机构,负责技术协助和商品推广工作。
香港机构负责的范畴涵盖:大洋洲、南亚次大陆、中东地区,以及东亚部分国家,不过朝鲜和日本不在其监管范围内。
审核编辑 黄宇
扫一扫在手机端查看
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。


客服1