第七章 W55MH32 DNS解析域名示例

描述

单芯片解决方案,开启全新体验——W55MH32 高性能以太网单片机

W55MH32是WIZnet重磅推出的高性能以太网单片机,它为用户带来前所未有的集成化体验。这颗芯片将强大的组件集于一身,具体来说,一颗W55MH32内置高性能Arm® Cortex-M3核心,其主频最高可达216MHz;配备1024KB FLASH与96KB SRAM,满足存储与数据处理需求;集成TOE引擎,包含WIZnet全硬件TCP/IP协议栈、内置MAC以及PHY,拥有独立的32KB以太网收发缓存,可供8个独立硬件socket使用。如此配置,真正实现了All-in-One解决方案,为开发者提供极大便利。

在封装规格上,W55MH32 提供了两种选择:QFN100和QFN68。

W55MH32L采用QFN100封装版本,尺寸为12x12mm,其资源丰富,专为各种复杂工控场景设计。它拥有66个GPIO、3个ADC、12通道DMA、17个定时器、2个I2C、5个串口、2个SPI接口(其中1个带I2S接口复用)、1个CAN、1个USB2.0以及1个SDIO接口。如此丰富的外设资源,能够轻松应对工业控制中多样化的连接需求,无论是与各类传感器、执行器的通信,还是对复杂工业协议的支持,都能游刃有余,成为复杂工控领域的理想选择。 同系列还有QFN68封装的W55MH32Q版本,该版本体积更小,仅为8x8mm,成本低,适合集成度高的网关模组等场景,软件使用方法一致。更多信息和资料请进入http://www.w5500.com/网站或者私信获取。

此外,本W55MH32支持硬件加密算法单元,WIZnet还推出TOE+SSL应用,涵盖TCP SSL、HTTP SSL以及 MQTT SSL等,为网络通信安全再添保障。

为助力开发者快速上手与深入开发,基于W55MH32L这颗芯片,WIZnet精心打造了配套开发板。开发板集成WIZ-Link芯片,借助一根USB C口数据线,就能轻松实现调试、下载以及串口打印日志等功能。开发板将所有外设全部引出,拓展功能也大幅提升,便于开发者全面评估芯片性能。

若您想获取芯片和开发板的更多详细信息,包括产品特性、技术参数以及价格等,欢迎访问官方网页:http://www.w5500.com/,我们期待与您共同探索W55MH32的无限可能。

以太网

第七章 W55MH32 DNS解析域名示例

本篇文章,我们将详细介绍如何在W55MH32芯片上面实现DNS域名解析功能。并通过实战例程,为大家讲解如何将wiznet.io的域名解析为实际IP地址,供大家参考。

该例程用到的其他网络协议,例如DHCP,请参考相关章节。有关W55MH32的初始化过程,也请参考相关章节,这里将不再赘述。

1 DNS协议简介

在学习DNS协议之前,我们先区分一下IP地址和域名这两个概念:

IP地址:一长串能够唯一地标记网络上地计算机的数字。

域名:又称网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时对计算机的定位标识,例如:wiznet.io。

如何理解域名和网址的概念,可以这么理解,网址里面包含域名。举个例子:https://wiznet.io/Products就是一个网址,而wiznet.io就是域名。

因为 IP 地址具有不方便记忆并且不能显示地址组织的名称和性质等缺点,所以设计出了域名,并通过域名解析协议(DNS,Domain Name System)来将域名和 IP 地址相互映射,使人能够更方便地访问互联网,而不用去记住能够被机器直接读取的 IP 地址数串。将域名映射成 IP 地址称为DNS正向解析,将 IP 地址映射成域名称为DNS反向解析。

DNS协议可以使用UDP或者TCP进行传输,使用的端口号都为53,但大多数情况下DNS都是用UDP进行传输。

以上是DNS协议的简介,如想深入了解该协议,请参考mozilla网站上的介绍: DNS - MDN Web 文档术语表:Web 相关术语的定义 | MDN

2 DNS域名介绍

DNS域名通常分为以下几类:

根域名服务器:根域名服务器是DNS系统的顶层,负责管理整个DNS命名空间的根区(Root Zone)。它主要用于引导查询,指向顶级域(TLD)的权威服务器。

顶级域名服务器:负责特定顶级域(如.com、.org、.net)或国家/地区代码顶级域(ccTLD,如.cn、.uk)的解析。

权威DNS服务器:负责存储并提供特定域名的DNS记录信息

本地DNS服务器:本地域名服务器是电脑解析时的默认域名服务器,即电脑中设置的首选 DNS 服务器和备选 DNS 服务器。常见的有电信、联通、谷歌、阿里等的本地 DNS 服务。

3 DNS查询方式

DNS查询方式分为以下两种:

递归查询:指由DNS客户端(如用户设备或本地域名服务器)向DNS服务器发起的查询请求,DNS服务器负责全程完成查询过程,并将最终的解析结果返回给客户端。

迭代查询:指DNS服务器返回给客户端或请求者的下一步建议,而不是直接返回最终结果,由客户端自行完成多次查询,逐步获取解析结果。

下面两张图则是递归查询和迭代查询的工作流程图。

以太网以太网

4 DNS协议的基本工作流程

接下来,我们以PC端正向解析www.baidu.com为例,了解下DNS解析的工作流程。

1)首先搜索「浏览器的 DNS 缓存」,缓存中维护一张域名与 IP 地址的对应表;

2)若没有命中,则继续搜索「操作系统的 DNS 缓存」

3)若仍然没有命中,则操作系统将域名发送至「本地域名服务器」,本地域名服务器查询自己的 DNS 缓存,查找成功则返回结果(注意:主机和本地域名服务器之间的查询方式是「递归查询」);

4)若本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行查询,通过以下方式进行「迭代查询」(注意:本地域名服务器和其他域名服务器之间的查询方式是迭代查询,防止根域名服务器压力过大):

首先本地域名服务器向「根域名服务器」发起请求,根域名服务器是最高层次的,它并不会直接指明这个域名对应的 IP 地址,而是返回顶级域名服务器的地址,也就是说给本地域名服务器指明一条道路,让他去这里寻找答案。

本地域名服务器拿到这个「顶级域名服务器」的地址后,就向其发起请求,获取「权限域名服务器」的地址

本地域名服务器根据权限域名服务器的地址向其发起请求,最终得到该域名对应的 IP 地址

5)本地域名服务器将得到的 IP 地址返回给操作系统,同时自己将 IP 地址缓存起来

6)操作系统将 IP 地址返回给浏览器,同时自己也将 IP 地址缓存起来

7)至此,浏览器就得到了域名对应的 IP 地址,并将 IP 地址缓存起来

配合下图直观理解:

以太网

在W55MH32上使用DNS正向解析wiznet.io域名时,我们只需要向本地域名服务器发送DNS请求报文,然后解析DNS响应报文即可。

5 DNS报文

DNS报文分为以下五个部分:

报文头部:定义了请求或响应的元信息(如标志、条目数等)。

问题区域:描述了查询的域名和查询类型。

回答区域:包含查询的最终结果(如域名对应的IP地址)。

权威区域:提供权威DNS服务器的信息。

附加区域:包含附加的相关信息(如域名的A记录)。

DNS请求报文主要由报文头部和问题区域组成,回答区域、权威区域和附加区域为空。

报文头部

Transaction ID:固定长度为16bit,唯一标识符,用于匹配请求和响应。

Flags:固定长度为16bit,标志位(例如查询类型、递归期望等)。

Questions:固定长度为16bit,问题区域的条目数,通常为1。

Answer RRs:固定长度为16bit,回答区域的条目数,查询报文中为0。

Authority RRs:固定长度为16bit,权威区域的条目数,查询报文中为0。

Additional RRs:固定长度为16bit,附加区域的条目数,查询报文中为0。

问题区域

QName:查询的域名(以点分形式存储)。

QType:查询的记录类型(如A记录、AAAA记录、MX记录等)。

QClass:查询的记录类别,通常为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记录。

请求报文实例:请求解析域名wiznet.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 12 01 00 00 01 00 00 00 00 00 00
06 77 69 7A 6E 65 74 02 69 64 00 00 01 00 01

 

响应报文实例:DNS服务器返回wiznet.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

| 报文原文 |
8D 12 81 80 00 01 00 01 00 00 00 00
06 77 69 7A 6E 65 74 02 69 6F 00 00 01 00 01
C0 0C 00 01 00 01 00 00 00 9C 00 04 B7 6F 8A F9

 

6 实现过程

接下来,我们看看如何在W55MH32上实现DNS正向解析。

注意:因为本示例需要访问互联网,请确保W55MH32的配置能够访问互联网。

步骤一:注册DNS定时器中断到1s定时器中

 

/**
* @brief   1ms timer IRQ Handler
* @param   none
* @return  none
*/
void TIM3_IRQHandler(void)
{
   static uint32_t tim3_1ms_count = 0;
   if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
   {
       tim3_1ms_count++;
       if (tim3_1ms_count >= 1000)
       {
           DHCP_time_handler();
           DNS_time_handler();
           tim3_1ms_count = 0;
       }
       TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
   }
}

 

注册DNS定时器中断主要为了DNS超时处理。

在dns.h文件中,定义了DNS超时时间、重试次数、端口号和消息ID等内容:

 

#define MAX_DNS_BUF_SIZE 256  ///< 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正向解析处理

在do_dns()函数中,我们实现了dns正向解析的过程。

 

do_dns(ethernet_buf, dns_name, ip_fromdns);

 

这个函数的三个传参分别为DNS解析所需缓存,带解析域名,解析后的IP地址。

do_dns()函数的内容如下:

 

/**
* @brief   DNS domain name resolution
* @param   ethernet_buff: ethernet buffer
* @param   domain_name:Domain name to be resolved
* @param   domain_ip:Resolved Internet Protocol Address
* @return  0:success;-1:failed
*/
int do_dns(uint8_t *buf, uint8_t *domain_name, uint8_t *domain_ip)
{
   int         dns_ok_flag  = 0;
   int         dns_run_flag = 1;
   wiz_NetInfo net_info;
   uint8_t     dns_retry_cnt = 0;
   DNS_init(0, buf); // DNS client init
   wizchip_getnetinfo(&net_info);
   while (1)
   {
       switch (DNS_run(net_info.dns, domain_name, domain_ip)) // Read the DNS_run return value
       {
       case DNS_RET_FAIL:                                     // The DNS domain name is successfully resolved
       {
           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: {
           printf(" > Translated %s to %d.%d.%d.%drn", domain_name, domain_ip[0], domain_ip[1], domain_ip[2], domain_ip[3]);
           dns_ok_flag  = 0;
           dns_run_flag = 0;
           break;
       }
       }
       if (dns_run_flag != 1)
       {
           return dns_ok_flag;
       }
   }
}

 

然后是在DNS主循环中运行DNS执行函数DNS_run,它的主要作用是进行DNS组包,发送请求,响应内容解析以及超时处理,这里只需要根据DNS_run()函数的返回值进行相应处理即可。

DNS_run()函数内容如下:

 

  /* DNS CLIENT RUN */
int8_t DNS_run(uint8_t * dns_ip, uint8_t * name, uint8_t * ip_from_dns)
{
 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
 socket(DNS_SOCKET, Sn_MR_UDP, 0, 0);
#ifdef _DNS_DEBUG_
 printf(" > DNS Query to DNS Server : %d.%d.%d.%drn", dns_ip[0], dns_ip[1], dns_ip[2], dns_ip[3]);
#endif
 len = dns_makequery(0, (char *)name, pDNSMSG, MAX_DNS_BUF_SIZE);
 sendto(DNS_SOCKET, pDNSMSG, len, dns_ip, IPPORT_DOMAIN);
 while (1)
 {
   if ((len = getSn_RX_RSR(DNS_SOCKET)) > 0)
   {
     if (len > MAX_DNS_BUF_SIZE) len = MAX_DNS_BUF_SIZE;
     len = recvfrom(DNS_SOCKET, pDNSMSG, len, ip, &port);
     #ifdef _DNS_DEBUG_
       printf(" > Receive DNS message from %d.%d.%d.%d(%d). len = %drn", ip[0], ip[1], ip[2], 
       ip[3],port,len);
     #endif
       ret = parseDNSMSG(&dhp, pDNSMSG, ip_from_dns);
     break;
   }
   // Check Timeout
   ret_check_timeout = check_DNS_timeout();
   if (ret_check_timeout < 0) {
#ifdef _DNS_DEBUG_
 printf(" > DNS Server is not responding : %d.%d.%d.%drn", dns_ip[0], dns_ip[1], dns_ip[2], dns_ip[3]);
#endif
     
close(DNS_SOCKET);
     return 0; // timeout occurred
   }
   else if (ret_check_timeout == 0) {
#ifdef _DNS_DEBUG_
     printf(" > DNS Timeoutrn");
#endif
     sendto(DNS_SOCKET, pDNSMSG, len, dns_ip, IPPORT_DOMAIN);
   }
 }
 close(DNS_SOCKET);
 // Return value
 // 0 > :  failed / 1 - success
 return ret;
}

 

7 运行结果

烧录例程运行后,首先进行了PHY链路检测,然后是DHCP获取网络地址结果,最后是DNS成功解析出wiznet.io的IP地址为183.111.138.249,如下图所示:

8 总结

本文介绍在 W55MH32 芯片上实现 DNS 域名解析功能的方法,讲解如何将 wiznet.io 域名解析为实际 IP 地址。阐述了 DNS 协议发热概念、域名分类、查询方式和工作流程,介绍了 DNS 报文结构及请求、响应报文实例等。展示在W55MH32上的实现过程。

下一篇将讲解在该芯片上实现 HTTP Client 功能,介绍向指定网站提交数据的原理和实现步骤。敬请期待!

WIZnet 是一家无晶圆厂半导体公司,成立于 1998 年。产品包括互联网处理器 iMCU™,它采用 TOE(TCP/IP 卸载引擎)技术,基于独特的专利全硬连线 TCP/IP。iMCU™ 面向各种应用中的嵌入式互联网设备。

WIZnet 在全球拥有 70 多家分销商,在香港、韩国、美国设有办事处,提供技术支持和产品营销。

香港办事处管理的区域包括:澳大利亚、印度、土耳其、亚洲(韩国和日本除外)。

审核编辑 黄宇

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分