DNS请求流程和报文解析

DNS

域名系统(英文: Domain Name System, 缩写: DNS)是互联网的一项服务,它作为将域名和 IP 地址相互映射的一个分布式数据库,能够使人更方便地访问互联网。DNS)的作用是将人类可读的域名(如,www.example.com) 转换为机器可读的 IP 地址 (如,192.0.2.44)。

域名结构

internet在命名时采用了层次结构的命名方法。每个域名都是一个标号序列(labels),用字母,数字,连接符组成。它用点号分割成一个个标号,每个标号可以看成是一个个的层级,级别低的写在左边,级别高的写在右边,域名服务主要基于UDP实现。

顶级域  ----    com   edu     gov     net
第二级域----    baidu    163
第三级域----    www     gitlab

域名解析过程

在有了域名结构之后需要有机器对其进行解析,而不同层级的根据高低级别由根域名服务器,顶级域名服务器,权限域名服务器,本地域名服务器。域名解析过程分为两大步骤:第一个步骤是本机向本地域名服务器发出一个DNS请求报文,报文携带要查询的域名,第二个步骤是本地域名服务器向本机回应一个DNS响应报文,包含域名对应的IP地址。
以百度为例具体流程描述如下:

  1. 主机 192.168.11.123 先向本地域名服务器 192.168.1.2 进行递归查询
  2. 本地域名服务器采用迭代查询,向一个根域名服务器进行查询
  3. 根域名服务器告诉本地域名服务器,下一次应该查询的顶级域名服务器 baidu.com 的IP 地址
  4. 本地域名服务器向顶级域名服务器 baidu.com 进行查询
  5. 顶级域名服务器 .com 告诉本地域名服务器,下一步查询权限服务器 www.baidu.com的 IP 地址
  6. 本地域名服务器向权限服务器 www.baidu.com 进行查询
  7. 权限服务器 www.baidu.com 告诉本地域名服务器所查询的主机的 IP 地址
  8. 本地域名服务器最后把查询结果告诉 122.152.222.180

递归查询:本机向本地域名服务器发出一次查询请求,就静待最终的结果。如果本地域名服务器无法解析,自己会以 DNS 客户机的身份向其它域名服务器查询,直到得到最
终的 IP 地址告诉本机

迭代查询:本地域名服务器向根域名服务器查询,根域名服务器告诉它下一步到哪里去查询,然后它再去查,每次它都是以客户机的身份去各个服务器查询

协议报文格式

请添加图片描述
报文分为头部和正文:

头部

会话标识(2 字节):是 DNS 报文的 ID 标识,请求和应答是相同的。

标志(2字节):
请添加图片描述

QR(1bit)查询/响应标志,0 为查询,1 为响应
opcode(4bit)0 表示标准查询,1 表示反向查询,2 表示服务器状态请求
AA(1bit)表示授权回答
TC(1bit)表示可截断的
RD(1bit)表示期望递归
RA(1bit)表示可用递归
rcode(4bit)表示返回码,0 表示没有差错,3 表示名字差错,2 表示服务器错误(Server Failure)
数量字段(总共 8 字节):Questions、Answer RRs、Authority RRs、Additional RRs 各自表示后面的四个区域的数目。

正文

Queries
请添加图片描述

查询名:长度不固定,不填充字节,一般表示反向查询,即由IP地址反查域名格式如下

5baidu3com0
5表示后面域名的长度,3同理,最后必须为0

查询类型:
请添加图片描述

查询类:通常为1,表明internet数据
原纪录(RR):包括回答区域,授权区域和附加区域
请添加图片描述

c实现DNS请求


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define DNS_SERVER_PORT		53
#define DNS_SERVER_IP		"114.114.114.114"

#define DNS_HOST			0x01
#define DNS_CNAME			0x05

struct dns_header{

	unsigned short id;
	unsigned short flags;

	unsigned short questions;
	unsigned short answer;

	unsigned short authority;
	unsigned short additional;
	
};

struct dns_question{

	int length;
	unsigned short qtype;
	unsigned short qclass;
	unsigned char *name;

};

struct dns_item{

	char *domain;
	char *ip;

};

//client send to dns server

int dns_create_header(struct dns_header *header){

	if(header == NULL) return -1;
	memset(header,0,sizeof(struct dns_header));

	//random
	srandom(time(NULL));
	header->id = random();

	header->flags = htons(0x0100);;
	header->questions = htons(1);

	return 0;
	
}

//hostname: www.baidu.com
//www
//baidu
//com

//name: 3www5baidu3com0

int dns_creat_questions(struct dns_question *question, const char *hostname){

	if(question == NULL || hostname==NULL) return -1;
	memset(question,0,sizeof(struct dns_question));

	question->name =(char *)malloc(strlen(hostname) + 2);
	if(question->name == NULL){
		return -2;
	}
	
	question->length = strlen(hostname) + 2;

	question->qtype = htons(1);
	question->qclass = htons(1);

	//name
	const char delim[2] = ".";
	char *qname = question->name;
	
	char *hostname_dup = strdup(hostname);	//strdup --> malloc
	char *token = strtok(hostname_dup,delim);	//www.0voice.com

	while(token != NULL){

		size_t len = strlen(token);

		*qname = len;
		qname++;

		strncpy(qname, token, len+1);
		qname += len;

		token = strtok(NULL, delim);
	}

	free(hostname_dup);
	
}

//

int dns_build_request(struct dns_header *header, struct dns_question *question, char *request, int rlen){

	if(header == NULL || question == NULL || request == NULL) return -1;
	memset(request, 0, rlen);

	//header --> server	
	memcpy(request,header, sizeof(struct dns_header));
	int offset = sizeof(struct dns_header);
		
	//question -->request
	memcpy(request+offset, question->name, question->length);
	offset += question->length;

	memcpy(request+offset, &question->qtype,sizeof(question->qtype));
	offset += sizeof(question->qtype);

	memcpy(request+offset, &question->qclass,sizeof(question->qclass));
	offset += sizeof(question->qclass);
	
	return offset;

}

static int is_pointer(int in) {
	return ((in & 0xC0) == 0xC0);
}

static void dns_parse_name(unsigned char *chunk, unsigned char *ptr,char *out,int *len){

	int flag = 0, n = 0, alen = 0;
	char *pos = out + (*len);

	while (1) {

		flag = (int)ptr[0];
		if (flag == 0) break;

		if (is_pointer(flag)) {
			
			n = (int)ptr[1];
			ptr = chunk + n;
			dns_parse_name(chunk, ptr, out, len);
			break;
			
		} else {

			ptr ++;
			memcpy(pos, ptr, flag);
			pos += flag;
			ptr += flag;

			*len += flag;
			if ((int)ptr[0] != 0) {
				memcpy(pos, ".", 1);
				pos += 1;
				(*len) += 1;
			}
		}
	
	}


}
static int dns_parse_response(char *buffer, struct dns_item **domains){

	int i = 0;
	unsigned char *ptr = buffer;

	ptr += 4;
	int querys = ntohs(*(unsigned short*)ptr);

	ptr += 2;
	int answers = ntohs(*(unsigned short*)ptr);

	ptr += 6;
	for(i = 0;i< querys; i ++){
	while(i){
		int flag = (int)ptr[0];
		ptr += (flag + 1);

		if(flag = 0) break;

	}
	ptr += 4;	

	}

	char cname[128], aname[128], ip[20], netip[4];
	int len, type, ttl, datalen;

	int cnt = 0;
	struct dns_item *list = (struct dns_item*)calloc(answers, sizeof(struct dns_item));
	if(list == NULL){
		return -1;
	}

	for(i = 0; i<answers; i++){

		bzero(aname, sizeof(aname));
		len = 0;

		dns_parse_name(buffer, ptr, aname, &len);
		ptr += 2;

		type = htons(*(unsigned short*)ptr);
		ptr += 4;

		ttl = htons(*(unsigned short*)ptr);
		ptr += 4;

		datalen = ntohs(*(unsigned short*)ptr);
		ptr += 2;

		if (type == DNS_CNAME) {

			bzero(cname, sizeof(cname));
			len = 0;
			dns_parse_name(buffer, ptr, cname, &len);
			ptr += datalen;
			
		} else if (type == DNS_HOST) {

			bzero(ip, sizeof(ip));

			if (datalen == 4) {
				memcpy(netip, ptr, datalen);
				inet_ntop(AF_INET , netip , ip , sizeof(struct sockaddr));

				printf("%s has address %s\n" , aname, ip);
				printf("\tTime to live: %d minutes , %d seconds\n", ttl / 60, ttl % 60);

				list[cnt].domain = (char *)calloc(strlen(aname) + 1, 1);
				memcpy(list[cnt].domain, aname, strlen(aname));
				
				list[cnt].ip = (char *)calloc(strlen(ip) + 1, 1);
				memcpy(list[cnt].ip, ip, strlen(ip));
				
				cnt ++;
			}
			
			ptr += datalen;
		}
	}

	*domains = list;
	ptr += 2;

	return cnt;
	

}


int dns_client_commit(const char *domain){

	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sockfd < 0){
		return -1;
	}

	struct sockaddr_in servaddr = {0};
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(DNS_SERVER_PORT);
	servaddr.sin_addr.s_addr = inet_addr(DNS_SERVER_IP);

	int ret = connect(sockfd,(struct sockaddr*)&servaddr,sizeof(servaddr));
	printf("connect: %d\n", ret);
	
	struct dns_header header = {0};
	dns_create_header(&header);

	struct dns_question question = {0};	
	dns_creat_questions(&question, domain);

	char request[1024] = {0};
	int length = dns_build_request(&header, &question, request, 1024);

	//requst
	int slen = sendto(sockfd, request, length, 0, (struct sockaddr*)&servaddr, sizeof(struct sockaddr));
	printf("requst: %d\n", slen);
	//recvfrom
	char response[1024] = {0};
	struct sockaddr_in addr;
	size_t addr_len = sizeof(struct sockaddr_in);

	int n = recvfrom(sockfd, response, sizeof(response), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
	printf("recvfrom: %d\n", n);

	struct dns_item *dns_domain = NULL;
	dns_parse_response(response,&dns_domain);
	free(dns_domain);

	return n;

}

int main(int argc,char *argv[]){

	if(argc < 2) return -1;

	dns_client_commit(argv[1]);

}


_in);

	int n = recvfrom(sockfd, response, sizeof(response), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
	printf("recvfrom: %d\n", n);

	struct dns_item *dns_domain = NULL;
	dns_parse_response(response,&dns_domain);
	free(dns_domain);

	return n;

}

int main(int argc,char *argv[]){

	if(argc < 2) return -1;

	dns_client_commit(argv[1]);

}


  • 1
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值