基于linux 实现DNS Client请求
创始人
2024-05-29 05:42:24
0

DNS是什么:

DNS是域名系统,Domain Name System的缩写,是一个服务。

作用:

DNS就是把域名解析为IP地址,提供我们上网,我们能够上网最终是找到IP地址。

DNS请求报文格式:

在这里插入图片描述
分别包含

  • Transaction ID:会话标识
  • Flags:标志
  • Questions:问题数量
  • Answer RRs:回答数量
  • Authority RRs:授权数量
  • Additional RRs:附加数量
  • Queries:查询区域

再细分展开Flags与Queries:
Flags:
在这里插入图片描述
其包含:

  • Response: 0 为查询,1 为响应
  • Opcode: 0 表示标准查询,1 表示反向查询,2 表示服务器状态请求
  • Truncated: 表示可截断的
  • Recursion desired: 表示期望递归
  • Z: 保留
  • Non-authenticated data: 是否为未经验证的数据

Queries
在这里插入图片描述
其包含1个问题,问题内容为:

  • Name: m.baidu.com
  • Type: A (Host Address) (1)
  • Class: IN (0x0001)

type数值依据:
在这里插入图片描述

Class(查询类)
通常为 1,是 Internet 数据

构建DNS :

header :

struct header {unsigned short id;			unsigned short flags;		unsigned short questions;	unsigned short answer;		unsigned short authority;unsigned short additional;
};

queries:

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

创建结构体存储ip

struct dns_item {char* ip;

};
header创建:

int create_header(struct header* header) {if (header == NULL)
{ 
return -1;
}memset(header, 0, sizeof(struct header));srandom(time(NULL));header->id = random();header->flags = htons(0x0100);//16位主机字节顺序转化成网络字节序 0000 0001 0000 0000 =十进制256header->questions = htons(1);//0000 0001 转换}

创建Queries

int create_queries(struct queries* question, const char* hostname) {if (question == NULL || hostname == NULL){return -1;}memset(question, 0, sizeof(struct queries));question->name = (char*)malloc(strlen(hostname) + 2);if (question->name == NULL) {return -1;}question->length = strlen(hostname) + 2;question->qtype = htons(1);question->qclass = htons(1);const char delim[2] = ".";char* qname = question->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);
}

将header和Queries组包:

int dns_build_request(struct header* header, struct queries* question, char* request,int rlen) {if (header == NULL || question == NULL || request == NULL){return -1;}int offset = 0;memset(request, 0, rlen);memcpy(request, header, sizeof(struct header));offset = sizeof(struct header);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;
}

DNS回复报文格式:

在这里插入图片描述
下面是Queris的展开
在这里插入图片描述
下面是Answer的展开(没用截图到回复的DNS_HOST类型,只有CNAME,逻辑可以参考有回复IP的):
在这里插入图片描述

服务器回复后的解析包:

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);printf("------问题数querys:%#x------------\n",querys);ptr += 2;int answers = ntohs(*(unsigned short*)ptr);printf("-----回复数answers:%#x------------\n",answers);ptr += 6;//queriesfor (i = 0; i < querys; i++) {while (1) {int flag = (int)ptr[0];ptr += (flag + 1);if (flag == 0) break;}ptr += 4;}char  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++) {//ptr answerslen = 0;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) {printf("--------DNS_CNAME--------\n");ptr += datalen;}else if (type == DNS_HOST) {printf("--------DNS_HOST--------\n");bzero(ip, sizeof(ip));if (datalen == 4) {memcpy(netip, ptr, datalen);inet_ntop(AF_INET, netip, ip, sizeof(ip));list[cnt].ip = (char*)calloc(strlen(ip) + 1, 1);memcpy(list[cnt].ip, ip, strlen(ip));printf("ip address %s\n", list[cnt].ip);cnt++;}ptr += datalen;}}*domains = list;ptr += 2;return cnt;}

socket请求

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));//绑定struct header header = { 0 };dns_create_header(&header);struct queries question = { 0 };dns_create_queries(&question, domain);char request[1024] = { 0 };int length = dns_build_request(&header, &question, request,1024);int slen = sendto(sockfd, request, length, 0, (struct sockaddr*)&servaddr, sizeof(struct sockaddr));char response[1024] = { 0 };struct sockaddr_in addr = { 0 };size_t addr_len = sizeof(struct sockaddr_in);int n = recvfrom(sockfd, response, sizeof(response), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);struct dns_item* dns_domain = NULL;dns_parse_response(response, &dns_domain);free(dns_domain);return 1;
}

main函数(查询百度dns):

int main(void) {dns_client_commit("www.baidu.com");
}

所有的代码

#include
#include
#include
#include
#include
#include
#include #define DNS_SERVER_PORT		53
#define DNS_SERVER_IP		"114.114.114.114"#define DNS_HOST			0x01
#define DNS_CNAME			0x05struct header {unsigned short id;			unsigned short flags;		unsigned short questions;	unsigned short answer;		unsigned short authority;unsigned short additional;
};struct queries {int length;unsigned short qtype;unsigned short qclass;unsigned char* name;
};
struct dns_item {char* ip;int create_header(struct header* header) {if (header == NULL)
{ 
return -1;
}memset(header, 0, sizeof(struct header));srandom(time(NULL));header->id = random();header->flags = htons(0x0100);//16位主机字节顺序转化成网络字节序 0000 0001 0000 0000 =十进制256header->questions = htons(1);//0000 0001 转换}int create_queries(struct queries* question, const char* hostname) {if (question == NULL || hostname == NULL){return -1;}memset(question, 0, sizeof(struct queries));question->name = (char*)malloc(strlen(hostname) + 2);if (question->name == NULL) {return -1;}question->length = strlen(hostname) + 2;question->qtype = htons(1);question->qclass = htons(1);const char delim[2] = ".";char* qname = question->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 header* header, struct queries* question, char* request,int rlen) {if (header == NULL || question == NULL || request == NULL){return -1;}int offset = 0;memset(request, 0, rlen);memcpy(request, header, sizeof(struct header));offset = sizeof(struct header);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 dns_parse_response(char* buffer, struct dns_item** domains) {int i = 0;unsigned char* ptr = buffer;ptr += 4;int querys = ntohs(*(unsigned short*)ptr);printf("------问题数querys:%#x------------\n",querys);ptr += 2;int answers = ntohs(*(unsigned short*)ptr);printf("-----回复数answers:%#x------------\n",answers);ptr += 6;//queriesfor (i = 0; i < querys; i++) {while (1) {int flag = (int)ptr[0];ptr += (flag + 1);if (flag == 0) break;}ptr += 4;}char  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++) {//ptr answerslen = 0;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) {printf("--------DNS_CNAME--------\n");ptr += datalen;}else if (type == DNS_HOST) {printf("--------DNS_HOST--------\n");bzero(ip, sizeof(ip));if (datalen == 4) {memcpy(netip, ptr, datalen);inet_ntop(AF_INET, netip, ip, sizeof(ip));list[cnt].ip = (char*)calloc(strlen(ip) + 1, 1);memcpy(list[cnt].ip, ip, strlen(ip));printf("ip address %s\n", list[cnt].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));//绑定struct header header = { 0 };dns_create_header(&header);struct queries question = { 0 };dns_create_queries(&question, domain);char request[1024] = { 0 };int length = dns_build_request(&header, &question, request,1024);int slen = sendto(sockfd, request, length, 0, (struct sockaddr*)&servaddr, sizeof(struct sockaddr));char response[1024] = { 0 };struct sockaddr_in addr = { 0 };size_t addr_len = sizeof(struct sockaddr_in);int n = recvfrom(sockfd, response, sizeof(response), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);struct dns_item* dns_domain = NULL;dns_parse_response(response, &dns_domain);free(dns_domain);return 1;
}
int main(void) {dns_client_commit("www.baidu.com");
}

结语

属于应用层的编程,对于我i们后续的协议理解是很有帮助的。上面提供的所有代码可以直接编译运行,执行的时候请检查电脑是否有网络,否则无法正常执行。

上一篇:Java开发环境搭配

下一篇:Vue学习[2023]

相关内容

热门资讯

泰山景点导游词 泰山景点导游词  作为一位不辞辛劳的导游,时常会需要准备好导游词,导游词由引言、主体和结语三部分构成...
陕西省简介导游词 陕西省简介导游词  陕西,简称“陕”或“秦”,中华人民共和国省级行政单位之一,省会古都西安。下面是小...
故宫导游词 故宫导游词300字2篇  导游词一  各位朋友,现在我们已经进入故宫,故宫导游词300字2篇。此处是...
天梯山导游词 天梯山导游词天梯山游览区位于邢台市西东牛庄,距市中心22公里,面积18平方公里。这里山势突兀,雄浑险...
淄博市鲁山国家森林公园导游词 淄博市鲁山国家森林公园导游词各位游客:  大家好!  欢迎您到鲁山国家森林公园观光旅游。我是本次活动...
照金香山导游词 照金香山导游词  导语:香山公园位于北京西郊,地势险峻,苍翠连绵,占地188公顷,是一座具有山林特色...
云冈石窟导游词 云冈石窟导游词各位游客大家好,很荣幸能当你们的导游,我姓詹,大家可以叫我詹导游。今天,我们将参观举世...
普陀山风景名胜区导游词 普陀山风景名胜区导游词  出历史名城锦州西北行十余里,有一座群峰险壑逶迤伴绕,飞泉云岫横生妙境的名山...
江西省九江庐山牯岭导游词 江西省九江庐山牯岭导游词  作为一名导游,就有可能用到导游词,导游词不是以一代百、千篇一律的,它必须...
台湾阿里山介绍导游词 台湾阿里山介绍导游词  阿里山,台湾地区地名,是台湾地区的著名旅游风景区,阿里山位于台湾省嘉义市东方...
河南内乡县衙导游词 河南内乡县衙导游词  各位游客,大家好!  内乡县衙开始建于元朝大德八年(公元132019年),距今...
导游词的方法技巧以及 导游词的方法技巧以及范文  作为一名尽职尽责的导游,编写导游词是必不可少的,导游词作为一种解说的文体...
导游词结束语怎么写 导游词结束语怎么写  结束语1  各位朋友几天的行程,还有10分钟就要结束了,在此刻要和大家说再见的...
上海南浦大桥导游词 上海南浦大桥导游词  竣工通车于1991年12月1日的南浦大桥,总长8346米,通航净高46米,5....
贵阳河滨公园导游词 贵阳河滨公园导游词  作为一位无私奉献的导游,总归要编写导游词,一篇完整的导游词,其结构一般包括习惯...
天生三桥导游词 关于天生三桥导游词范文(通用9篇)  作为一位出色的导游人员,有必要进行细致的导游词准备工作,借助导...
西安兵马俑英文导游词 西安兵马俑英文导游词(通用10篇)  作为一名优秀的导游,通常需要准备好一份导游词,一篇完整的导游词...
丽江古城中英文导游词 丽江古城中英文导游词  丽江古城被列入世界文化遗产后,丽江的旅游业达到顶峰,成为世人向往的世外桃源、...
观音山导游词 观音山导游词范文  作为一位杰出的导游,很有必要精心设计一份导游词,导游词具有形象、生动、具有感染力...