Linux: C语言发起 DNS 查询报文

本文目录

  • 使用 getaddrinfo()
  • 手动构造 DNS 查询报文
      • DNS 查询部分(Question Section)
        • QNAME (查询的域名)
        • QTYPE (查询类型)
        • QCLASS (查询类)
        • Answer Section (答案部分)
  • C语言代码发起 DNS 查询报文

使用 getaddrinfo()

getaddrinfo() 是一个高层的接口,它可以用来处理 DNS 查询、地址解析等任务。使用这个接口非常简单,不需要手动构建 DNS 查询报文。

// dnsReq.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>void dnsRequests(char *hostname) {struct addrinfo hints, *res;int status;// 设置 hintsmemset(&hints, 0, sizeof(hints));hints.ai_family = AF_INET;  // IPv4 地址hints.ai_socktype = SOCK_STREAM; // TCP 连接// 获取解析结果if ((status = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));return; // 出现错误时直接返回}// 打印解析到的 IP 地址char ipstr[INET_ADDRSTRLEN];struct sockaddr_in *ipv4 = (struct sockaddr_in *)res->ai_addr;inet_ntop(res->ai_family, &ipv4->sin_addr, ipstr, sizeof(ipstr));printf("IP address of %s: %s\n", hostname, ipstr);// 释放地址信息freeaddrinfo(res);
}int main(int argc, char* argv[]) {if (argc < 2) {fprintf(stderr, "Usage: %s <hostname>...\n", argv[0]);return 1; // 如果没有提供主机名,打印帮助信息并退出}for (int i = 1; i < argc; i++) {  // 从 argv[1] 开始dnsRequests(argv[i]);}return 0;
}

编译:gcc -o dnsReq dnsReq.c
运行:./dnsReq hostname_1 hostname_2 hostname_3 ... ...
请添加图片描述

手动构造 DNS 查询报文

DNS 查询和回答报文具有相同的格式。

请添加图片描述
DNS 报文首部占 12 个字节。

  • 前 16 比特标识,事务 ID,用于标识请求和响应之间的匹配。客户端和服务器在发送和接收查询时会使用该 ID。
  • Flags :标志字段,指示查询类型、响应状态等。
    请添加图片描述
    • QR:1 位,表示查询(0)还是响应(1)。
    • Opcode:4 位,表示查询的类型(标准查询、反向查询等)。
      • 0:标准查询(Standard query)。这是最常见的查询类型,用于获取指定域名的IP地址或其他DNS记录。
      • 1:反向查询(Inverse query)。这种查询类型通常用于根据IP地址获取对应的域名,与标准查询相反。
      • 2:服务器状态请求(Status request)。这种查询类型用于请求DNS服务器的状态信息,通常用于诊断或监控目的。
    • AA:1 位,表示是否为权威答案(1 表示是)。
    • TC:1 位,表示响应是否被截断(通常用于 TCP 响应)。
    • RD:1 位,表示是否要求递归查询(1 表示要求)。
    • RA:1 位,表示是否支持递归查询(1 表示支持)。
    • zero:3 位,保留,通常为 0。
    • rCode:4 位,响应码,表示查询的状态(成功、无数据等)。
      • 0:无差错。表示DNS查询成功完成,没有遇到任何问题。
      • 1:格式差错(Format error)。这表示服务器不能理解请求的报文格式,可能是由于报文格式不正确或存在无法识别的字段。
      • 2:域名服务器失败(Server failure)。这表示由于服务器的原因导致无法处理请求,可能是由于服务器内部错误或资源不足等原因。
      • 3:名字错误(Name Error)。这表示解析的域名不存在,只有对授权域名解析服务器有意义。它指出客户端请求的域名在DNS系统中不存在或无法找到。
      • 4:查询类型不支持(Not Implemented)。这表示域名服务器不支持客户端请求的查询类型。
      • 5:拒绝(Refused)。这表示服务器由于设置的策略拒绝给出应答,例如服务器不希望对某些请求者给出应答。
  • Number of questions:表示查询中的问题数目,通常为 1,表示只查询一个域名的记录。
  • Number of anwser RRs:表示响应中答案的数量。在查询报文中,这个字段一般为 0。
  • Number of anthority RRs:表示响应中权威记录的数量。
  • Number of additional RRs:表示响应中附加记录的数量。

DNS 查询部分(Question Section)

查询部分包含一个或多个问题,通常情况下一个 DNS 查询只包含一个问题。该部分包括域名和查询的类型。每个问题由以下字段组成:

字段描述长度(字节)
QNAME查询的域名,格式为标签(label)加点(‘.’)。可变
QTYPE查询类型,表示想要查询的记录类型(如 A、MX)。2
QCLASS查询类,表示查询的类,通常为 IN(Internet)。2
QNAME (查询的域名)

QNAME 是一个以点(.)分隔的标签(label)格式的域名。例如,如果查询 www.example.comQNAME 就是由标签 wwwexamplecom 组成的。

每个标签之前有一个字节表示该标签的长度(例如 “www” 对应的标签长度为 3,“example” 长度为 7)。最终,域名以一个 0x00 字节结束,表示域名的结尾。

QTYPE (查询类型)

QTYPE 是一个 16 位的字段,表示查询的记录类型。常见的查询类型有:

类型描述代码
AIPv4 地址1
AAAAIPv6 地址28
MX邮件交换记录15
CNAME别名记录5
NS域名服务器记录2
PTR反向查询记录12
QCLASS (查询类)

QCLASS 是一个 16 位的字段,表示查询的类。通常查询类为 IN(互联网),其值为 1。

类别描述代码
INInternet(互联网)1
CHChaos(用于 Chaosnet)3
HSHesiod(Hesiod 系统)4

请添加图片描述

Answer Section (答案部分)

答案部分包含 DNS 响应的资源记录。每个资源记录由以下字段组成:

字段描述长度(字节)
NAME域名(通常是指向查询的域名或子域名)。可变
TYPE记录类型(如 A 记录、MX 记录)。2
CLASS类别(通常是 IN)。2
TTL生存时间(Time to Live),表示记录在缓存中的有效时间(单位是秒)。4
RDLENGTH数据的长度。2
RDATA记录数据,存储具体的信息(如 IP 地址)。可变

DNS answer section:TYPE 类型字段有以下常用值:

  1. A记录(Address Record)

    • TYPE字段值为1。
    • 表示将域名映射到IPv4地址。
  2. NS记录(Name Server Record)

    • TYPE字段值为2。
    • 指定域名服务器负责特定区域的权威信息。
  3. CNAME记录(Canonical Name Record)

    • TYPE字段值为5。
    • 提供域名的规范别名,通常用于将一个域名重定向到另一个域名。
  4. PTR记录(Pointer Record)

    • TYPE字段值为12。
    • 用于反向DNS查询,将IP地址映射到域名。
  5. MX记录(Mail Exchange Record)

    • TYPE字段值为15。
    • 指定邮件服务器的优先级和域名,用于邮件路由。
  6. AAAA记录

    • TYPE字段值为28。
    • 类似于A记录,但用于将域名映射到IPv6地址。
  7. SRV记录

    • TYPE字段值为33。
    • 指定提供特定服务的服务器的位置,包括主机名、端口号和优先级等信息。
  8. TXT记录

    • TYPE字段值为16。
    • 提供任意文本信息,通常用于存储元数据或说明性信息。
  9. NAPTR记录(Naming Authority Pointer Record)

    • TYPE字段值为35。
    • 用于基于正则表达式的域名到URI的映射。
  10. TLSA记录

    • TYPE字段值为863。
    • 用于存储DNSSEC(域名系统安全扩展)的TLS证书关联信息。

请添加图片描述

C语言代码发起 DNS 查询报文

// dnsReq.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>#define DNS_SERVER "8.8.8.8" // Google's public DNS server
#define DNS_PORT 53 // DNS uses port 53// 生成随机的 16 位事务 ID
unsigned short generate_random_id() {srand(time(NULL));  // 设置随机数种子(基于当前时间)return (unsigned short)(rand() % 65536);  // 生成 0 到 65535 的随机数
}// DNS 头部结构体
struct DNSHeader {unsigned short id; // Transaction IDunsigned short flags; // DNS flagsunsigned short qdcount; // Number of questionsunsigned short ancount; // Number of answersunsigned short nscount; // Number of authority recordsunsigned short arcount; // Number of additional records
};// DNS 查询部分
struct DNSQuestion {unsigned short qtype; // Query type (A, MX, etc.)unsigned short qclass; // Query class (IN, etc.)
};// 构建 DNS 查询报文
void build_dns_query(char *query, const char *hostname, int pos) {char *label;for (label = strtok(strdup(hostname), "."); label != NULL; label = strtok(NULL, ".")) {query[pos++] = strlen(label);strcpy(query + pos, label);pos += *(query + pos - 1);}query[pos++] = 0;struct DNSQuestion question = { htons(1), htons(1) };memcpy(query + pos, &question, sizeof(question));
}int main(int argc, char* argv[]) {char query[512] = { 0 };// 设置 DNS 请求头unsigned short id = generate_random_id();printf("%x\n", id);struct DNSHeader header = { htons(id), htons(0x0180), htons(1), htons(0), htons(0), htons(0) };memcpy(query, &header, sizeof(header));build_dns_query(query, "baidu.com", sizeof(header));int sockfd;struct sockaddr_in server_addr;sockfd = socket(AF_INET, SOCK_DGRAM, 0);   // UDPif (sockfd < 0) {perror("Socket creation failed");return 1;}memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_port = htons(DNS_PORT);server_addr.sin_addr.s_addr = inet_addr(DNS_SERVER);if (sendto(sockfd, query, sizeof(query), 0, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("Sendto failed");close(sockfd);return 1;}char buffer[512];socklen_t len = sizeof(server_addr);int n = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&server_addr, &len);if (n < 0) {perror("Recvfrom failed");close(sockfd);return 1;}printf("Received %d bytes from DNS server\n", n);FILE *file = fopen("output.bin", "wb");if (file == NULL) {perror("Failed to open file");return EXIT_FAILURE;}size_t written = fwrite(buffer, sizeof(unsigned char), n, file);if (written != n) {perror("Failed to write complete data to file");fclose(file);return EXIT_FAILURE;}// 关闭文件fclose(file);close(sockfd);return 0;
}

环境在 Linux 发送版 Ubuntu 下,编译器使用 gcc,

编译命令: gcc -o dnsReq dnsReq.c
运行:命令行下 ./dnsReq./ 表示当前目录,dnsReq 编译后的可执行文件。

运行以上代码:构造了一个 DNS 查询报文(查询 baidu.com),并使用 socket 套接字编程连接谷歌公共 DNS 服务器 8.8.8.853 端口,发送了该 DNS 查询报文,将得到的响应以二进制的形式写入文件 output.bin 中。

一下是对该二进制文件的分析:

2136 8180 0001 0002 0000 0000 0562 6169  !6...........bai
6475 0363 6f6d 0000 0100 01c0 0c00 0100  du.com..........
0100 0000 9000 046e f244 42c0 0c00 0100  .......n.DB.....
0100 0000 9000 0427 9c42 0a              .......'.B.
  • 2136 ID
  • 8180 Flags,意思是: 响应报文、递归查询、支持递归查询、响应成功。
  • 0001 Number of questions :1个查询。
  • 0002 Number of answers:2个应答。
  • 0000 Number of anthority RRs :0
  • 0000 Number of additional RRs :0
  • 0562 6169 6475 0363 6f6d 0000 0100 01 代码中构造的 DNS 查询,请见代码。
  • c0 0c00 0100 0100 0000 9000 046e f244 42 第一条应答:
    • NAME:c0 0c 表示域名的指针,前两个 bit 为 11,后面14个 bit 的值为 12,表示域名在整个应答报文中的偏移量,偏移量的下标从 0 开始,也就是第 12 个字节处,正好指向 baidu.com
    • TYPE:00 01 表示 A 记录。
    • CLASS:00 01 表示 IN (Internet 互联网)
    • TTL:00 0000 90 生存时间,16*9 秒。
    • RDLENGTH:00 04 数据长度 4 个字节。
    • RDDATA:6e f244 42 4个字节的数据,写成点分十进制就是 110.242.68.66
  • c0 0c00 0100 0100 0000 9000 0427 9c42 0a 第二条应答:数据部分写成点分十进制就是 39.156.66.10

验证:
请添加图片描述

DNS 应答报文如果最后多出了一个 0a 字节,则表示换行,表示应答报文的结束。

拿到二进制数据,自己手动去翻译应答报文实属麻烦,写代码解析 DNS 应答报文的任务,就交给屏幕前正在阅读此文的你。

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

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

相关文章

【Pytorch】神经网络介绍|激活函数|使用pytorch搭建方法

神经网络 神经网络介绍 概念 神经网络 人工神经网络ANN 也称神经网络NN 是一种模仿生物神经网络结构和功能的计算模型人脑可以看作是一个生物神经网络,由众多神经元连接而成,神经网络可以看作是模拟生物神经元的过程 输入层 input Layer: 输入x的那一层 输出层 output Laye…

【HarmonyOS NEXT】实战——登录页面

【HarmonyOS NEXT】实战——登录页面 在本文中&#xff0c;我们将深入探讨如何使用HarmonyOS NEXT来实现一个功能完备的登录页面。通过这个实战案例&#xff0c;你将结合页面布局、数据本地化存储、网络请求等多方面了解到HarmonyOS NEXT在构建现代应用时的强大能力和灵活性。…

iscc2023

iscc 还没想好名字的塔防游戏 就是那句话首字母&#xff0c;加上玩游戏通关后有提示就是后面的字母 Flask中的pin值计算 先f12&#xff0c;看到base64到路由/getusername 输入app.py&#xff0c;得到路由/crawler 进入后发现是一个计算&#xff0c;写一个python脚本 impor…

力扣-Mysql-3328-查找每个州的城市 II(中等)

一、题目来源 3328. 查找每个州的城市 II - 力扣&#xff08;LeetCode&#xff09; 二、数据表结构 表&#xff1a;cities ---------------------- | Column Name | Type | ---------------------- | state | varchar | | city | varchar | ----------------…

Vue2:组件

Vue2&#xff1a;组件 非单文件组件定义注册使用 单文件组件 组件是Vue中最核心的内容&#xff0c;在编写页面时&#xff0c;将整个页面视为一个个组件&#xff0c;再把组件拼接起来&#xff0c;这样每个组件之间相互独立&#xff0c;有自己的结构样式&#xff0c;使页面编写思…

力扣 LeetCode 28. 找出字符串中第一个匹配项的下标(Day4:字符串)

解题思路&#xff1a; KMP算法 需要先求得最长相等前后缀&#xff0c;并记录在next数组中&#xff0c;也就是前缀表&#xff0c;前缀表是用来回退的&#xff0c;它记录了模式串与主串(文本串)不匹配的时候&#xff0c;模式串应该从哪里开始重新匹配。 next[ j - 1 ] 记录了 …

计算机网络 (1)互联网的组成

一、互联网的边缘部分 互联网的边缘部分由所有连接在互联网上的主机组成&#xff0c;这些主机又称为端系统&#xff08;end system&#xff09;。端系统可以是各种类型的计算机设备&#xff0c;如个人电脑、智能手机、网络摄像头等&#xff0c;也可以是大型计算机或服务器。端系…

智慧军营安防方案

1. 引言 智慧安防方案集成高清视频监控、智能分析与大数据管理&#xff0c;打造全方位安全防护体系。通过先进技术&#xff0c;提升预警与应急响应能力&#xff0c;确保安全无死角。 2. 视频监控技术 采用高清摄像设备与智能识别算法&#xff0c;实现全景监控与细节跟踪&#…

ABAP开发学习——ST05 ABAP SQL跟踪工具

操作步骤 第一步使用ST05之前&#xff0c;将要查的程序停留想要看的操作的前一步&#xff0c;这里想看到取数操作&#xff0c;所以停留在选择界面 第二步进入ST05 选择SQL Trace 然后激活 第三步去执行程序 第四步ST05取消激活 第五步查看操作 选完时间直接执行

AtCoder ABC378 A-D题解

比赛链接:ABC378 比较简单的一次 ABC。 Problem A: Code #include <bits/stdc.h> using namespace std; int main(){cin>>A[1]>>A[2]>>A[3]>>A[4];sort(A1,A5);if(A[1]A[2] && A[3]A[4])cout<<2<<endl;else{if(A[1]A[2]…

Windows上安装专业版IDEA2024并激活

1、IDEA官方下载 搜索IDEA官网点击进入&#xff0c;点击Download&#xff08;目前这个激活脚本只能激活2024.1.7&#xff0c;2024.2.x的版本都不能激活&#xff0c;2024.1.7版本已上传资源&#xff09;&#xff0c;如图&#xff1a; 2、开始安装 1&#xff09;、双击下载的.…

表达式求值问题(中缀转后缀,对后缀求值)详解

目录 实验题目 理解中缀和后缀表达式 问题分析 1转化为中缀表达式 2计算后缀表达式 完整代码 运行结果 实验题目 实验题目&#xff1a;表达式求值问题。这里限定的表达式求值问题是&#xff1a; 用户输入一个包含“”、“-”、“*”、“/”、正整数和圆括号的合法数学表…

AD22怎么按照板子形状铺铜

如何按照板子形状来铺铜&#xff1f; 选择铺铜管理器 选择板外形 我这里图里VCC没画就选择VCC&#xff0c; 你选什么层&#xff0c;就勾什么层 死铜移除勾选 效果如下&#xff1a;

【视觉SLAM】2-三维空间刚体运动的数学表示

读书笔记&#xff1a;学习空间变换的三种数学表达形式。 文章目录 1. 旋转矩阵1.1 向量运算1.2 坐标系空间变换1.3 变换矩阵与齐次坐标 2. 旋转向量和欧拉角2.1 旋转向量2.2 欧拉角 3. 四元数 1. 旋转矩阵 1.1 向量运算 对于三维空间中的两个向量 a , b ∈ R 3 a,b \in \R^3 …

研发费用资本化的意义

1.更真实地反映企业价值&#xff1a;研发费用是企业为创造未来经济利益而进行的投资&#xff0c;通过将其资本化并作为无形资产计入资产负债表&#xff0c;可以更真实地反映企业的资产总额和长期投资价值。这有助于投资者、债权人和其他利益相关者更准确地评估企业的财务状况、…

Ubuntu24.04安装Anaconda3+Pycharm

一、引言 重装系统已经过去一段时间了&#xff0c;现在安装一下 Anaconda 和 Pycharm。 参考连接&#xff1a; Ubuntu中安装Anaconda3和Pycharm 及其环境搭建Ubuntu18.04安装Pycharm教程ubuntu系统安装Anaconda及Pycharm在移动硬盘上搭建Ubuntu24.04深度学习环境&#xff08;…

稀疏矩阵(Sparse Matrix)及其存储格式详解

稀疏矩阵&#xff08;Sparse Matrix&#xff09;是线性代数和计算机科学中的一个重要概念&#xff0c;广泛应用于科学计算、工程模拟、图像处理、机器学习等多个领域。与稠密矩阵&#xff08;Dense Matrix&#xff09;相比&#xff0c;稀疏矩阵大部分元素为零&#xff0c;仅有少…

操作系统:页表中的页表项

操作系统&#xff1a;页表中的页表项 页表是操作系统用于跟踪进程使用的虚拟地址与系统内存中相应物理地址之间映射的数据结构。 页表项&#xff08;Page Table Entry&#xff0c;PTE&#xff09;是页表中的一个条目&#xff0c;用于存储有关特定内存页的信息。每个页表项包含…

Docker部署Kafka SASL_SSL认证,并集成到Spring Boot

1&#xff0c;创建证书和密钥 需要openssl环境&#xff0c;如果是Window下&#xff0c;下载openssl Win32/Win64 OpenSSL Installer for Windows - Shining Light Productions 还需要keytool环境&#xff0c;此环境是在jdk环境下 本案例所使用的账号密码均为&#xff1a; ka…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《基于改进容积卡尔曼滤波的含光伏配电网动态状态估计》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…