电子工程师技术服务社区
公告
登录
|
注册
首页
技术问答
厂商活动
正点原子
板卡试用
资源库
下载
文章
社区首页
文章
教你动手写UDP协议栈-DNS报文解析<4>
分 享
扫描二维码分享
教你动手写UDP协议栈-DNS报文解析<4>
TCP
UDP
DNS
Rice嵌入式开发
关注
发布时间: 2020-08-17
丨
阅读: 2065
## 教你动手写UDP协议栈系列文章 | 序号 | 内容 | |-----|------| | 1 | [《教你动手写UDP协议栈-UDP协议栈格式》](https://mp.weixin.qq.com/s/SwiW0hgusYExgo7KdbOyhQ) | | 2 | [《教你动手写UDP协议栈-DHCP报文解析》](https://mp.weixin.qq.com/s/2NlKoSxqQ2EDoinVyx3Flg) | | 3 | [《教你动手写UDP协议栈-OTA上位机》](https://mp.weixin.qq.com/s/My6AKh_BRfgtM6VgL3jTBQ) | | 4 | 《教你动手写UDP协议栈-DNS报文解析》 | ## 背景 - 因特网上的节点通过IP地址唯一标识,并且能通过IP地址来识别参与分布式应用的主机。但对于大多数人来说,这些地址太繁琐而且难以使用和记忆(特别是IPV6地址)。因此互联网支持使用主机名称来识别包括客户机和服务器在内的主机。为了使用如TCP和IP等协议,主机名称可以通过称为域名解析的过程转换成IP地址。 - 在互联网中存在不同形式的名称解析,但是最普遍、最重要的一种是采用分布式数据库系统,即我们熟知的域名系统(DNS),也是这篇文章的主角。 - DNS - 是一个分布式的客户机-服务器网络数据库,TCP/IP应用程序使用它来完成主机名称和IP地址之间的映射,提供电子邮件路由信息、服务命名和其他服务。 - DNS使用TCP和UDP的端口--53。 - DNS - 为了可扩展性,DNS名称是分层的。每一级域名长度的限制是63个字符,域名总长度则不能超过253个字符。 - 下面来介绍DNS报文的格式解析,以及如何将域名转为IP地址的流程。 ## 准 备 工 具 | 工具 | 介绍 | |-----|------| | WireShark | 网络封包分析软件,分析数据包 | | CMD | window 命令行 | ## DNS报文解析 #### 抓包分析 - 打开CMD和WireShark工具。 - 在WireShark中设置过滤信息,我们只抓取DNS报文。 - 在CMD键入ping www.baidu.com,然后查看WireShark的抓包信息。 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/1.png) - 可以看到两包DNS报文,一个是DNS发送报文,一个是DNS接收报文 - 发送报文 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/2.png) - 接收报文 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/3.png) - 发送报文和接收报文格式是不一样的,从上面截图可以看到,接收报文多一个Answers字段。 - DNS可以使用UDP与TCP两种协议。这里我们主要以UDP进行分析。 #### DNS报文字段解析 - DNS报文格式: ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/4.png) - DNS字段格式: - 发送报文 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/5.png) - 接收报文 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/6.png) ##### DNS报文头部 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/7.png) - 字段说明 | 字段 | 说明 | |-----|------| | Transaction ID | 辨别DNS应答报文是哪个请求报文的响应 | | QR | Flags字段,1为响应,0位查询 | | OpCode | Flags字段,查询或响应类型,0为标准,1为反向,2为服务器状态请求 | | AA | Flags字段,授权回答 | | TC | Flags字段,截断,1表示超过512字节并已被截断,0表示没有发送截断 | | RD | Flags字段,是否希望得到递归回答 | | RA | Flags字段,响应报文中为1便是得到递归响应 | | Z | Flags字段,0 | | AD | Flags字段,真是数据 | | CD | Flags字段,禁止校验 | | RCODE | Flags字段,返回码:0-无差错,1-格式错误,2-服务器失效,3-不存在域名,4-查询类型不支持,5-被禁止,6-15保留 | | Questions | Flags字段,查询数 | | Answer | Flags字段,资源记录数 | | Authority | Flags字段,授权资源记录数 | | Additional | Flags字段,额外资源记录数 | - 代码实现 ~~~ C /@@** DNS message header */ PACK_STRUCT_BEGIN struct dns_header { PACK_STRUCT_FIELD(uint16_t id); PACK_STRUCT_FIELD(uint8_t flags1); PACK_STRUCT_FIELD(uint8_t flags2); PACK_STRUCT_FIELD(uint16_t numquestions); PACK_STRUCT_FIELD(uint16_t numanswers); PACK_STRUCT_FIELD(uint16_t numauthrr); PACK_STRUCT_FIELD(uint16_t numextrarr); }PACK_STRUCT_STRUCT; PACK_STRUCT_END ~~~ ##### DNS报文问题字段 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/8.png) - 字段说明 - 查询名称格式: ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/10.png) | 字段 | 说明 | |-----|------| | name | 查询名称,不定长 | | type | 查询类型 | | class | 查询类 | - 代码实现(由于名字是不定长,另作处理) ~~~ C PACK_STRUCT_BEGIN struct dns_query { PACK_STRUCT_FIELD(uint16_t type); PACK_STRUCT_FIELD(uint16_t class); }PACK_STRUCT_STRUCT; PACK_STRUCT_END ~~~ ##### DNS报文应答字段 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/9.png) - 字段说明(此字段只有应答包才有) | 字段 | 说明 | |-----|------| | name | 查询名称,不定长 | | type | 查询类型 | | class | 查询类 | | TTL | 该资源记录的生命周期 | | data length | 资源数据长度 | | address | 返回的IP地址,即域名转换的IP地址 | - 代码实现 ~~~ C struct dns_answer { PACK_STRUCT_FIELD(uint16_t name); PACK_STRUCT_FIELD(uint16_t type); PACK_STRUCT_FIELD(uint16_t class); PACK_STRUCT_FIELD(uint32_t ttl); PACK_STRUCT_FIELD(uint16_t len); PACK_STRUCT_FIELD(struct ip_addr server_ip); }PACK_STRUCT_STRUCT; PACK_STRUCT_END ~~~ ## DNS报文发送实现 - 代码实现 ~~~ C static void dns_packet_output(uint8_t *host_name) { struct dns_header dns_hdr = {0}; struct dns_query dns_qry = {0}; struct dest_device_info dest_info = {0}; uint8_t *dns_packet = NULL; uint8_t *dns_name = NULL; uint16_t query_index = 0; uint16_t label_len = 0; uint16_t dns_name_len = strlen(host_name) + 2; dns_packet = malloc(DNS_HDR_SIZE + dns_name_len + DNS_QUERY_SIZE); dns_name = malloc(strlen(host_name) + 2); if(dns_packet != NULL && dns_name !=NULL) { //打包DNS header memset(&dns_hdr, 0, DNS_HDR_SIZE); dns_hdr.id = mu_htons(TRANSACTION_ID); dns_hdr.flags1 = DNS_FLAG1_RD; dns_hdr.numquestions = mu_htons(1); memcpy(dns_packet, &dns_hdr, DNS_HDR_SIZE); //将域名转换DNS数据包格式 change_to_dns_name(dns_name, host_name); memcpy(dns_packet + DNS_HDR_SIZE, dns_name, dns_name_len); dns_qry.type = mu_htons(DNS_RRTYPE_A); dns_qry.class = mu_htons(DNS_RRCLASS_IN); //打包DNS query memcpy(dns_packet + DNS_HDR_SIZE + dns_name_len, &dns_qry, DNS_QUERY_SIZE); memcpy(&dest_info.dest_mac, get_gw_mac(), MAC_ADDR_SIZE); memcpy(&dest_info.dest_ip, get_dns_server(), IP_ADDR_SIZE); dest_info.src_port = DNS_CLIENT_PORT; dest_info.dest_port = DNS_SERVER_PORT; //通过UDP报文发送 mini_udp_output(&dest_info, dns_packet, (DNS_HDR_SIZE + dns_name_len + DNS_QUERY_SIZE)); } if(dns_packet != NULL) { free(dns_packet); } if(dns_name != NULL) { free(dns_name); } } ~~~ - 验证代码结果,我们通过查询CSDN的IP地址,CSDN的域名:www.csdn.net ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/11.png) - 通过wireshark抓包,可以看到我们DNS报文已发送成功,并且有应答包 ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/12.png) ## DNS报文接收实现 - 代码实现 ~~~ C static void dns_packet_input(void *dns_packet_data) { struct dns_header *dns_hdr = {0}; struct dns_answer *dns_ans = {0}; uint16_t dns_name_len = strlen("www.csdn.net") + 2; uint8_t *server_dns_name = malloc(strlen("www.csdn.net") + 2); if(server_dns_name == NULL) { LOG_E("malloc fail!!\n"); return; } dns_hdr = dns_packet_data; if(dns_hdr->id == mu_ntohs(TRANSACTION_ID) && (dns_hdr->numanswers > 1)) { change_to_dns_name(server_dns_name, "www.csdn.net"); if(strncmp(dns_packet_data + DNS_HDR_SIZE, server_dns_name, dns_name_len) == 0) { dns_ans = dns_packet_data + DNS_HDR_SIZE + dns_name_len + DNS_QUERY_SIZE; printf("CSDN IP: %d:%d:%d:%d \n", dns_ans->server_ip.addr[0], dns_ans->server_ip.addr[1], dns_ans->server_ip.addr[2], dns_ans->server_ip.addr[3]); } } free(server_dns_name); } ~~~ - 通过wireshark抓包的IP与代码捕获的IP一致: ![](https://rice_chen_1.gitee.io/picture/mini_udp_dns/13.png) 关注微信公众号『Rice嵌入式开发技术分享』,后台回复“微信”添加作者微信,备注”入群“,便可邀请进入技术交流群。 ![](https://rice_chen_1.gitee.io/picture/logo/logo_.jpg)
原创作品,未经权利人授权禁止转载。详情见
转载须知
。
举报文章
点赞
(
0
)
Rice嵌入式开发
关注
评论
(0)
登录后可评论,请
登录
或
注册
相关文章推荐
MK-米客方德推出工业级存储卡
Beetle ESP32 C3 蓝牙数据收发
Beetle ESP32 C3 wifi联网获取实时天气信息
开箱测评Beetle ESP32-C3 (RISC-V芯片)模块
正点原子数控电源DP100测评
DP100试用评测-----开箱+初体验
Beetle ESP32 C3环境搭建
【花雕体验】16 使用Beetle ESP32 C3控制8X32位WS2812硬屏之二
X
你的打赏是对原创作者最大的认可
请选择打赏IC币的数量,一经提交无法退回 !
100IC币
500IC币
1000IC币
自定义
IC币
确定
X
提交成功 ! 谢谢您的支持
返回
我要举报该内容理由
×
广告及垃圾信息
抄袭或未经授权
其它举报理由
请输入您举报的理由(50字以内)
取消
提交