本文共 4886 字,大约阅读时间需要 16 分钟。
回想一下,当我们想访问谷歌的时候,通常输入域名(网址):https://www.google.com,其实这就是一个域名。
DNS 解析过程涉及将主机名(例如 https://www.google.com)转换为计算机友好的 IP 地址(例如 172.217.27.142)。Internet 上的每个设备都被分配了一个 IP 地址,必须有该地址才能找到相应的 Internet 设备 。就像使用街道地址来查找特定住所一样。
我们今天主要做的就是解析DNS协议中的域名。
域名系统(DNS)
IP地址是计算机识别Internet上其他计算机的方式。IP地址并不是特别友好。谁想记住一个地址为 34.25.19.8?或者更长的IPV6地址:2401:8954:3291:26a4:7830:8c12:78ce:95c1?
该域名系统(DNS)给了我们人类一种简单的方法来找出我们想要去访问的互联网。我们只需输入“ www.google.com”之类的域名。
一个域名是人类友好的地址,一个网站,东西很容易让我们记住。
DNS传输协议
DNS使用端口53上的用户数据报协议(UDP)来服务DNS查询。首选UDP协议,因为它速度快且开销低。DNS查询是来自DNS客户端的单个UDP请求,然后是来自服务器的单个UDP答复。
/* 用于DNS的端口。 */#define DEFAULT_DNS_PORT_RANGE "53"#define DEFAULT_DNS_TCP_PORT_RANGE "53,5353" /*包括mDNS */#define SCTP_PORT_DNS 53#define UDP_PORT_MDNS 5353#define UDP_PORT_LLMNR 5355#define TCP_PORT_DNS_TLS 853#define UDP_PORT_DNS_DTLS 853
如果DNS响应大于512字节,或者DNS服务器正在管理区域传输之类的任务,则使用传输控制协议(TCP)代替UDP,以实现数据完整性检查。
DNS报文结构
DNS通信通过两种消息发生:查询和响应。DNS查询格式和响应格式都具有以下结构Header部分 包含标识、查询数量、响应数量、和其他资源记录数。
Flags字段 包含1位或4位的部分,指示消息的类型,名称服务器是否权威;查询是否是递归的,请求是否被截断以及状态。 Questions部分包含 要解析的域名和记录类型(A,AAAA,MX,TXT等)。域名中的每个标签都以其长度为前缀。 Answer 具有查询名称的资源记录。
DNS Questions
DNS Questions的格式为:
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+| || QNAME || |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+| QTYPE |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+| QCLASS |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
QNAME以标签序列表示的域名,其中每个标签由一个长度组成八位位组,后跟该八位位组的数量。域名以零长度结尾八位位组,表示根的空标签。
QTYPE一个两个八位字节的代码,用于指定查询的类型。您应该为此项目使用0x0001 ,代表A记录(主机地址)。如果您要完成此项目的研究生版本,您还需要将0x000f用于邮件服务器(MX)记录,并将0x0002用于名称服务器(NS)记录。
QCLASS一个两个八位字节的代码,用于指定查询的类。您应该为此始终使用0x0001项目,代表Internet地址。
Wireshark抓包报文查询格式:
DNS Answers
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+| || || NAME || |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+| TYPE |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+| CLASS |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+| TTL || |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+| RDLENGTH |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|| RDATA || |+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
NAME查询的域名,与Questions中的QNAME格式相同。TYPE包含第一个类型代码的两个八位位组。此字段指定数据的含义RDATA字段。您应该准备好解释类型0x0001 ( A记录)和类型0x0005( CNAME )。
如果您要完成此项目的研究生版本,则还应该准备接受类型0x0002 (名称服务器)和0x000f (邮件服务器)。
CLASS两个八位字节,用于指定RDATA字段中数据的类别。您应该期望0x0001该项目的Internet地址。
TTL可缓存结果的秒数。
RDLENGTH RDATA字段的长度。
RDATA响应的数据。格式取决于TYPE字段
Wireshark抓包报文响应格式:
DNS协议解析主要代码实现:
struct dnshdr { uint16_t id; uint16_t flags; uint16_t quest; uint16_t ans; uint16_t auth; uint16_t add;};struct dnshdr_name { const char *ns_name; int ns_name_len; unsigned int labels;};/* qname_labels_count函数计算'.'的数量,在字符串中加1, 以计算标签数。 */static unsigned int qname_labels_count(const char* name, int name_len){ unsigned int labels = 0; int i = 0; if (name_len > 1) { /* 这不是零长度的名称 */ for (i = 0; i < name_len; i++) { if (name[i] == '.') labels++; } labels++; } return labels;}int get_dns_name(u_char *dns_data, int offset, int max_len, int dns_data_offset, const char **name, int* name_len){ int len; len = expand_dns_name(dns_data, offset, max_len, dns_data_offset, name, name_len); /* Zero-length name means "root server" */ if (**name == '\0' && len <= MIN_DNAME_LEN) { *name=""; *name_len = (int)strlen(*name); return len; } if ((len < MIN_DNAME_LEN) || (len > MIN_DNAME_LEN && *name_len == 0)) { printf("ReportedBoundsError\n"); } return len;}static void dissect_dns(u_char *data_info,int PayloadLen){ int used_bytes = 0; dnshdr_name dns_domain; //printf("DnsHdr 0x%.2X,0x%.2X,0x%.2X,0x%.2X\n",data_info[0],data_info[1],data_info[2],data_info[3]); used_bytes = get_dns_name(data_info, DNS_HDRLEN, 0, 0, &dns_domain.ns_name, &dns_domain.ns_name_len); dns_domain.labels = qname_labels_count(dns_domain.ns_name, dns_domain.ns_name_len); //printf("used_bytes: %d\n",used_bytes); printf("ns_name: %s\n",dns_domain.ns_name); printf("ns_name_len: %d\n",dns_domain.ns_name_len); printf("labels: %d\n",dns_domain.labels); }static void confirm_dns_packet(struct ip *pIp){ int iIpTotalLen = ntohs(pIp->ip_len); int offset = 0; int nFragSeq = 0; struct udphdr* pUdpHdr = (struct udphdr*)((char*)pIp + (pIp->ip_hl<<2)); if (pIp->ip_p == IPPROTO_UDP && (ntohs(pUdpHdr->dest) == DEFAULT_DNS_PORT_RANGE) || (ntohs(pUdpHdr->source) == DEFAULT_DNS_PORT_RANGE) ) { printf("\n"); printf("info udp\n"); int iPayloadLen = iIpTotalLen - (pIp->ip_hl<<2) - 8; printf("UDP Payload Len %d\n", iPayloadLen); u_char *pDnsHdr = (u_char*)(pUdpHdr+1); dissect_dns(pDnsHdr,iPayloadLen); } }
输出结果:
总结
DNS协议可帮助Internet用户和网络设备使用人提供可读的主机名,而不是数字IP地址来发现网站。
对于,深入的理解DNS协议,大家可以看看RFC官方文档。参考:https://www.rfc-editor.org/info/rfc1035
欢迎关注微信公众号【程序猿编码】,欢迎添加本人微信号(17865354792),欢迎进入技术交流群。我们一起学习进步!
转载地址:http://yuiy.baihongyu.com/