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)发送数据,并返回数据