基于linux 实现DNS Client请求

news2024/12/23 19:00:53

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 =十进制256
 	header->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;//queries
	for (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 answers
		len = 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<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<unistd.h>
#include <arpa/inet.h>

#define DNS_SERVER_PORT		53
#define DNS_SERVER_IP		"114.114.114.114"

#define DNS_HOST			0x01
#define DNS_CNAME			0x05

struct 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 =十进制256
 	header->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;//queries
	for (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 answers
		len = 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们后续的协议理解是很有帮助的。上面提供的所有代码可以直接编译运行,执行的时候请检查电脑是否有网络,否则无法正常执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/390497.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux环境开发stm32+vscode编码+gcc-arm编译+openocd烧录

文章目录Linux环境下STM32开发1前言2环境搭建3点灯&#xff1a;脚本方式命令行操作方式具体见参考2vscode将以上命令集成起来4总结Linux环境下STM32开发 1前言 最近在使用Keil开发STM32的时候总感觉代码写起来很很费劲&#xff0c;然后打算用vscode试试&#xff0c;刚开始用的…

js作用域和作用域链

1、局部作用域分为函数作用域和块作用域 1.1、函数作用域: 在函数内部声明的变量只能在函数内部被访问&#xff0c;外部无法直接访问。 总结 1.函数内部声明的变量&#xff0c;在函数外部无法被访问 2.函数的参数也是函数内部的局部变量 3.不同函数内部声明的变量无法互相访…

iscsi windows使用教程与smb das 区别

介绍——为什么不用smb而用iscsi 历史 说到SAN等传统存储设备&#xff0c;我们不得不提到SCSI&#xff0c;SCSI作为外部块设备的连接和传输协议&#xff0c;是最广泛的块设备协议&#xff0c;于1979首次提出&#xff0c;是为小型机研制的一种接口技术&#xff0c;现在已完全普…

taobao.item.joint.img( 商品关联子图 )

&#xffe5;开放平台免费API必须用户授权 关联一张商品图片到num_iid指定的商品中传入的num_iid所对应的商品必须属于当前会话的用户商品图片关联在卖家身份和图片来源上的限制&#xff0c;卖家要是B卖家或订购了多图服务才能关联图片&#xff0c;并且图片要来自于卖家自己的…

汇编指令学习(JMP、JE、JS、JP,JO,JB)

一、JMP无条件跳转不用看标志位&#xff0c;jmp后面跟一个内存地址&#xff0c;直接跳转到该地址jmp 0x0046B994二、JE&#xff08;JZ&#xff09;条件跳转当ZF标致为1的时候发生跳转&#xff0c;为0的时候不跳转&#xff0c;可以双击标志位&#xff0c;进行判断je 0x0046B99F三…

802.11 mac帧

mac帧格式帧格式MAC headFrame Control域Protocol VersionType和SubtypeTo DS和From DSMore FragmentsRetryPower ManagementMore DataProtected FrameOrderDuration/ID域Address域Address1 接收Address2 发送Address3 携带其他信息帮助mac帧传输Sequence Control域管理帧格式定…

vue3:生命周期(onErrorCaptured)

一、背景 当项目如果发生报错&#xff0c;影响程序体验。如果能以捕获的方式得到错误信息&#xff0c;而且还能定位问题&#xff0c;这样就好了&#xff0c;本文介绍onErrorCaptured实现我们想要的效果。 vue2&#xff1a;errorCaptured。使用与vue3同理。 vue3&#xff1a;…

某游戏app sig参数分析

今天要分析的app 叫 dGFwdGFwIDIuMjA= (base64 解码),来一起学习下。 找个视频接口,上来先抓个包,没错今天就是要分析下这个sig参数。 这个app 在高版本上有加固壳,并且还有frida检测(ps:遇到困难不会放弃,以后慢慢研究),这里只是研究sig参数,所以采用低版本了。 把…

ARM uboot 的移植2-从三星官方 uboot 开始移植

一、inand 驱动问题的解决 1、先从现象出发定位问题 (1) 解决问题的第一步&#xff0c;是定位问题。所谓定位问题&#xff0c;就是找到源代码当中导致这个问题的那一句或者那几句代码。有时候解决这个问题需要修改的代码和直接导致这个问题的代码是不同的。我们这里说的定位问…

一文深入分析虚拟机中对象锁实现!

一、前言 编程过程中经常会遇到线程的同步问题&#xff0c;Java 中对同步问题的解决方案比较多&#xff08;synchronized、JUC、原子操作、volatile、条件变量等&#xff09;&#xff0c;其中synchronized 最方便、简单易用&#xff0c;也是java 编程中使用最多的临界区保护方…

接口自动化入门-TestNg

目录1.TestNg介绍2、TestNG安装3、TestNG使用3.1 编写测试用例脚本3.2 创建TestNG.xml文件&#xff08;1&#xff09;创建testng.xml文件&#xff08;2&#xff09;修改testng.xml4、测试报告生成1.TestNg介绍 TestNg是Java中开源的自动化测试框架&#xff0c;灵感来源于Junit…

CSAPP第九章 虚拟内存

理解虚拟内存的原因 本章前部分描述虚拟内存是如何工作的&#xff0c;后一部分描述应用程序如何使用和管理虚拟内存 物理和虚拟寻址 虚拟内存作为缓存的工具 页表 页命中 缺页 虚拟内存作为内存管理的工具 简化链接&#xff0c;简化加载&#xff0c;简化共享&#xff0c;简化…

K8s集群部署

#部署方式有多种&#xff0c;本文采用kubeadm组件的方式来部署K8s集群 安装要求&#xff1a; 至少三台主机内存最少2G&#xff0c;CPU2核集群网络互通可以访问外网禁止swap分区 环境说明: 系统&#xff1a;ubuntu22.04.1 版本信息&#xff1a;kubernetes&#xff1a;1.26.…

HashMap底层的实现原理

目录一、知识点回顾二、HashMap 的 put() 和 get() 的实现2.1 map.put(k, v) 实现原理2.2 map.get(k) 实现原理2.3 为何随机增删、查询效率都很高&#xff1f;2.4 为什么放在 HashMap 集合 key 部分的元素需要重写 equals 方法?2.5 HashMap总结2.6 JDK8 之后&#xff0c;HashM…

由点到面贯穿整个Java泛型理解

泛型概述 Java泛型(generics)是DK5中引入的一个新特性&#xff0c;泛型提供了编译时类型安全监测机制&#xff0c;该机制允许我们在编译时检测到非法的类型数据结构。 泛型的本质就是参数化类型&#xff0c;也就是所操作的数据类型被指定为一个参数。 如我们经常使用的Array…

信息安全与数学基础-笔记-③一次同余方程

知识目录一次同余方程的解中国剩余定理中国剩余定理的应用一次同余方程的解 本文只研究一次同余方程的解。 f(x) 三 0 (mod m)&#xff0c; 若有一个s能够满足该式子&#xff0c;那么该数字就是该式子的解&#xff0c; 在同余方程式中的解一般写成&#xff1a;x三s (mod m) 同…

Git学习入门(2)- 基本命令操作总结

个人博客&#xff1a;我的个人博客&#xff0c;各位大佬来玩1 创建 git仓库1.1 从现有工作目录中初始化新仓库需要到你需要用git管理的项目中输入以下命令&#xff1a;git init便会创建一个空的git项目&#xff0c;并且当前目录下会出现一个名为 .git 的目录&#xff0c; Git 需…

1.SpringSecurity快速入门

*SpringScurity的核心功能: 认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户 授权:经过认证后判断当前用户是否有权限进行某个操作 *第一步:创建springboot工程 *第二步:引入SpringSecurity依赖 *第三步:写controller,访问对应的url:localhos…

常用训练tricks,提升你模型的鲁棒性

目录一、对抗训练FGM(Fast Gradient Method): ICLR2017代码实现二、权值平均1.指数移动平均&#xff08;Exponential Moving Average&#xff0c;EMA&#xff09;为什么EMA会有效&#xff1f;代码实现2. 随机权值平均&#xff08;Stochastic Weight Averaging&#xff0c;SWA&a…

Java Volatile的三大特性

本文通过学习&#xff1a;周阳老师-尚硅谷Java大厂面试题第二季 总结的volatile相关的笔记volatile是Java虚拟机提供的轻量级的同步机制&#xff0c;三大特性为&#xff1a;保证可见性、不保证原子性、禁止指令重排一、保证可见性import java.util.concurrent.TimeUnit;class M…