DNS域名解析C代码

上篇讲到,要访问如www.baidu.com网站,要先知道www.baidu.com的IP地址。

那么如何根据域名获取IP地址呢?我们来看一下C语言代码

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <winsock.h>

#pragma comment(lib,"ws2_32.lib")

//定义DNS数据       ID       tag       numq      numa       numa1    numa2
char DNS_DATA[]={0x12,0x34,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,
0x03,0x77,0x77,0x77,0x06,0x66,0x75,0x68,0x6f,0x6d,0x65,0x03,0x6e,0x65,0x74,0x00,//03 www 06 fucome 03 net 00
0x00,0x01,0x00,0x01};//前两字节为1表示A记录; 后两字节为1表示DNS_HDR+域名+DNS_QER


int main(int argc,char* argv[])
{
	WORD socketVersion = MAKEWORD(2,2);
    WSADATA wsaData; 
    if(WSAStartup(socketVersion, &wsaData) != 0)
    {
  		printf("error");      
		return 0;
    }
	//创建UDP通信套接字
    SOCKET sclient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    SOCKADDR_IN sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(53);//DNS远程端口固定为53
    sin.sin_addr.S_un.S_addr = inet_addr("114.114.114.114");//DNS服务器IP地址,可以填网关地址如(192.168.0.1)
    
	//发送域名数据
    sendto(sclient, DNS_DATA, sizeof(DNS_DATA), 0, (SOCKADDR *)&sin, sizeof(sin));
 
    char recvData[255];     
    int ret = recv(sclient, recvData, 255, 0);//等待接收数据
    if(ret > 0)//收到大于0的数据
    {
  		for(int i=0;i<ret;i++)//以16进制格式打印出来
		{
			printf("%02X ",(unsigned char)recvData[i]);
		}      
    }
	 
    closesocket(sclient);
    WSACleanup();
    return 0;
}

返回的结果为:
12 34 81 80 00 01 00 01 00 00 00 00 03 77 77 77 06 66 75 68 6F 6D 65 03 6E 65 74
 00 00 01 00 01 C0 0C 00 01 00 01 00 00 00 F2 00 04 73 1C 5D C9
其中73 lC 5D C9就是为www.fuhome.net的IP地址:115.28.93.201
 

参考资料:龙剑奋斗的博客

C 语言实现 DNS 域名解析的过程如下: 1. 定义 DNS 查询报文 DNS 查询报文包含以下字段: - 标识符(ID):用于标识该查询和响应,通常是一个随机数。 - 标志(Flags):用于指定该查询的类型和设置。 - 问题数(QDCOUNT):用于指定查询报文中包含的问题数。 - 回答数(ANCOUNT):用于指定响应报文中包含的回答数。 - 授权数(NSCOUNT):用于指定响应报文中包含的授权数。 - 附加数(ARCOUNT):用于指定响应报文中包含的附加数。 - 问题(Question):用于指定查询的域名和类型。 - 回答(Answer):用于返回查询结果。 - 授权(Authority):用于指定授权域名服务器。 - 附加(Additional):用于指定其他信息,例如 DNS 服务器的 IP 地址。 2. 构造 DNS 查询报文 构造 DNS 查询报文的过程如下: - 定义一个 DNS 查询报文结构体,包含上述字段。 - 将域名转换成 DNS 查询格式,即将每个标签的长度和内容以字节方式存储,并在末尾添加一个 0 字节。 - 将查询报文头部填充好,包括标识符、标志、问题数等字段。 - 将查询报文中的问题字段填充好,包括域名和查询类型(通常为 A 记录)。 - 将查询报文序列化成字节数组。 3. 发送 DNS 查询报文 使用套接字 API 发送 DNS 查询报文到 DNS 服务器,例如: ```c #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int sockfd; struct sockaddr_in servaddr; sockfd = socket(AF_INET, SOCK_DGRAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(53); // DNS 服务器端口号 inet_pton(AF_INET, "8.8.8.8", &servaddr.sin_addr); // DNS 服务器 IP 地址 sendto(sockfd, query, query_len, 0, (struct sockaddr *) &servaddr, sizeof(servaddr)); ``` 其中,query 表示序列化后的 DNS 查询报文,query_len 表示报文长度。 4. 接收 DNS 响应报文 使用套接字 API 接收 DNS 响应报文,例如: ```c char response[1024]; int response_len; struct sockaddr_in servaddr; socklen_t servlen = sizeof(servaddr); response_len = recvfrom(sockfd, response, sizeof(response), 0, (struct sockaddr *) &servaddr, &servlen); ``` 其中,response 表示接收到的 DNS 响应报文,response_len 表示报文长度,servaddr 表示 DNS 服务器的地址。 5. 解析 DNS 响应报文 将接收到的 DNS 响应报文解析成可读的格式,例如: ```c struct dns_header *header = (struct dns_header *) response; char *question = response + sizeof(struct dns_header); char *answer = question + strlen(question) + 1; ``` 其中,dns_header 表示 DNS 报文头结构体,question 表示查询问题部分,answer 表示查询回答部分。 6. 提取 DNS 查询结果 从 DNS 响应报文中提取查询结果,例如: ```c struct dns_answer *ans = (struct dns_answer *) answer; char *ip = inet_ntoa(ans->ip); ``` 其中,dns_answer 表示 DNS 回答结构体,ip 表示查询到的 IP 地址。 注意,以上代码仅为示例,实际实现中还需要考虑更多细节和异常情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值