UDP协议——DNS解析

1 篇文章 0 订阅
1 篇文章 0 订阅

1.DNS服务器

2.DNS协议

2.1报文格式

2.2报文头部(Header)

Transaction ID

会话标识(2 字节):是 DNS 报文的 ID 标识,对于请求报文和其对应的应答报文,这个字段
是相同的,通过它可以区分 DNS 应答报文是哪个请求的响应。(注意请求和响应报文次字段相同)。

Flags(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 各自
表示后面的四个区域的数目。Questions 表示查询问题区域节的数量,Answers 表示回答区
域的数量,Authoritative namesversers 表示授权区域的数量,Additional recoreds 表示
附加区域的数量

2.3报文正文(content)

Queries

查询名:长度不固定,且不使用填充字节,一般该字段表示的就是需要查询的域名(如果是
反向查询,则为 IP,反向查询即由 IP 地址反查域名),一般的格式如下图所示。

特别注意:上述域名是0voice.com,只有顶级域名和二级域名,报文正文中查询名的填写形式

60voice3com0,注意必须以0结尾!

如果域名是www.0voice.com,书写形式为3www60voice3com0。这种形式填写的原因是底层的数据结构用的是多叉哈夫曼树!

查询类型
类型 助记符 说明
1 A 由域名获得 IPv4 地址
2 NS 查询域名服务器
5 CNAME 查询规范名称
6 SOA 开始授权
11 WKS 熟知服务
12 PTR 把 IP 地址转换成域名
13 HINFO 主机信息
15 MX 邮件交换
28 AAAA 由域名获得 IPv6 地址
252 AXFR 传送整个区的请求
255 ANY 对所有记录的请求


查询类
通常为 1,表明是 Internet 数据

3.C语言实现DNS请求

3.1头部结构体的定义

struct dns_header
{
    unsigned short ID;
    unsigned short Flags;
    unsigned short Questions;
    unsigned short AnswerRRs;
    unsigned short AuthorityRRs;
    unsigned short AdditionalRRs;
};

3.2报文内容结构体定义

struct dns_content
{
    int length;
    unsigned short qtype;
    unsigned short qclass;
    char* name;
};

3.3dns头部内容填充

int dns_create_header(struct dns_header* header)
{
    if (header == NULL) return -1;
	memset(header, 0, sizeof(struct dns_header));
    srand(time(NULL));
	//ID可以任意填充
    header->ID=rand()%1000+1;
	//注意转换为网络字节序,相应字段意思可以参看上面DNS协议各个字段含义
    header->Flags=ntohs(0x0100);
    header->Questions=ntohs(1);
    return 0;
}

3.4dns正文内容填充

//填充dns正文内容
int dns_create_content(struct dns_content* content,const char* hostname)
{
    content->length = strlen(hostname) + 2;
	content->qtype = htons(1); 
	content->qclass = htons(1);

	//填充name时,注意文件规范化name内容
    //比如www.baidu.com--> 3www5baidu3com0

	const char delim[2] = ".";
	char *qname = content->qname;
	
	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); //0voice.com ,  com

	}

	free(hostname_dup);
}

3.5dns数据包合并

int dns_build_request(struct dns_header *header, struct dns_content *content, char *request, int rlen) {
 
	if (header == NULL || content == NULL || request == NULL) return -1;
	memset(request, 0, rlen);
 
	// header --> request
	
	memcpy(request, header, sizeof(struct dns_header));
	int offset = sizeof(struct dns_header);
 
	// content --> request
	memcpy(request+offset, content->name, content->length);
	offset += content->length;
 
	memcpy(request+offset, &content->qtype, sizeof(content->qtype));
	offset += sizeof(content->qtype);
 
	memcpy(request+offset, &content->qclass, sizeof(content->qclass));
	offset += sizeof(content->qclass);
 
	return offset;
 
}

3.6完整代码

#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>


#define DNS_SERVER_PORT		53
#define DNS_SERVER_IP		"114.114.114.114"

//dns头部
struct dns_header
{
    unsigned short ID;
    unsigned short Flags;
    unsigned short Questions;
    unsigned short AnswerRRs;
    unsigned short AuthorityRRs;
    unsigned short AdditionalRRs;
};

//dns内容
struct dns_content
{
    int length;
    unsigned short qtype;
    unsigned short qclass;
    char* name;
};

//dns头部填充
int dns_create_header(struct dns_header* header)
{
    if (header == NULL) return -1;
	memset(header, 0, sizeof(struct dns_header));
    srand(time(NULL));
	//ID可以任意填充
    header->ID=rand()%100+1;
	//注意转换为网络字节序,相应字段意思可以参看上面DNS协议各个字段含义
    header->Flags=ntohs(0x0100);
    header->Questions=ntohs(1);
    return 0;
}


//填充dns正文内容
//参数2是域名 eg. www.baidu.com
int dns_create_content(struct dns_content* content,const char* hostname)
{
    if (content == NULL || hostname == NULL) return -1;
	memset(content, 0, sizeof(struct dns_content));

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

    content->length = strlen(hostname) + 2;
	content->qtype = htons(1); 
	content->qclass = htons(1);
 
	//填充name时,注意文件规范化name内容
    //比如www.baidu.com--> 3www5baidu3com0
    //以下是格式化算法
	const char delim[2] = ".";
	char *qname = content->name;
	
	char *hostname_dup = strdup(hostname); 
	char *token = strtok(hostname_dup, delim); 
 
	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_content *content, char *request, int rlen) {
 
	if (header == NULL || content == NULL || request == NULL) return -1;
	memset(request, 0, rlen);
 
	// header --> request
	
	memcpy(request, header, sizeof(struct dns_header));
	int offset = sizeof(struct dns_header);
 
	// content --> request
	memcpy(request+offset, content->name, content->length);
	offset += content->length;
 
	memcpy(request+offset, &content->qtype, sizeof(content->qtype));
	offset += sizeof(content->qtype);
 
	memcpy(request+offset, &content->qclass, sizeof(content->qclass));
	offset += sizeof(content->qclass);
 
	return offset;
 
}

void dns_request_comment(const char* hostname)
{
    struct dns_header header;
    struct dns_content content;
    struct sockaddr_in addr;
    char request[128]={0};
    int request_len=0;

    dns_create_header(&header);
    dns_create_content(&content,hostname);
    request_len=dns_build_request(&header,&content,request,128);


    int sockfd=socket(AF_INET,SOCK_DGRAM,0);
    addr.sin_family=AF_INET;
    addr.sin_addr.s_addr=inet_addr(DNS_SERVER_IP);
    addr.sin_port=htons(DNS_SERVER_PORT);

    sendto(sockfd,request,request_len,0,(struct sockaddr *)&addr,(unsigned int)sizeof(struct sockaddr));
   
    //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("%s\r\n", response);

}



int main(int argv,char* argc[])
{
    //if(argv<2)return -1;

    dns_request_comment("www.baidu.com");

}

tip:在linux环境下编程,向dns服务器(顶级服务器144.144.144.144:53)发送数据,并返回数据

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值