Introduce
支持中文域名解析,支持缓存与多请求
遵循RFC1180标准,可被wireshark抓包检测到
本系统包含一个客户端,四个中间多级DNS,一个根DNS,一个本地DNS
难点在于①不用python而使用C语言必须写非常多的底层操作,主要集中在defAndTools库中②DNS请求的逻辑
Project Structure
CODES | EXPLAINATION |
---|---|
defAndTools.h | 此包为主要工具包,涉及网络中的核心底层的操作,是主要难点之一 |
LOCAL_DNS.c | 本地DNS服务器核心逻辑,是本网络中的核心部件,是主要难点之一 |
Client.c | 客户端主逻辑 |
ROOT_DNS.c | 根域名解析服务器的逻辑代码 |
DNS1.c | 第一级域名解析服务器主逻辑 |
DNS2.c | 第二级域名解析服务器主逻辑 |
DNS3.c | 第三级域名解析服务器主逻辑 |
DNS4.c | 第四级域名解析服务器主逻辑 |
localServer.h | 本地DNS服务器的参数设置 |
FILES | EXPLAINATION |
---|---|
localCache.txt | 本地DNS服务器的缓存 |
RRL1.txt | 第一级域名解析服务器的缓存 |
RRL2.txt | 第二级域名解析服务器的缓存 |
RRL3.txt | 第三级域名解析服务器的缓存 |
RRL4.txt | 第四级域名解析服务器的缓存 |
RRroot.txt | 根域名解析服务器的缓存 |
defAndTools
此包为主要工具包,涉及网络中的底层的操作
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <stdlib.h>
#define MAX_SIZE_OF_DOMAIN 100
#define SERVER_PORT 53
#define ROOT_SERVER_IP "127.0.0.3"
//用于储存域名方便处理的全局变量
char domain_temp[MAX_SIZE_OF_DOMAIN];
typedef struct DNS_Header
{
unsigned short id; //16位的消息ID标示一次正常的交互,该ID由消息请求者设置,消息响应者回复请求时带上该ID。
unsigned short tag; //tag要拆,并单独写一个生成tag函数
unsigned short queryNum; //标示请求部分的条目数
unsigned short answerNum;//标示响应部分的资源记录数。如果响应消息中没有记录,则设置为0
unsigned short authorNum;//标示权威部分的域名服务器资源记录数。如果响应消息中没有权威记录,则设置为0
unsigned short addNum; //标示额外部分的资源记录数。
}DNS_HEAD;
typedef struct DNS_Query
{
char *name; //请求的域名。
unsigned short qtype; //记录的类型 [A:0x0001] [NS:0x0002] [CNAME:0x0005] [MX:0x000F]
unsigned short qclass; //请求的资源记录的类型 一般为[IN:0x0001]
}DNS_QUERY;
typedef struct DNS_RR
{
char *name;
unsigned short type; //请求的域名
unsigned short _class; //响应的资源记录的类型 一般为[IN:0x0001]
unsigned int ttl; //该资源记录被缓存的秒数。
unsigned short data_len; //RDATA部分的长度
unsigned short pre; //MX特有的优先级 Preference
char *rdata; //[A:32位的IP地址(4字节)] [CNAME/NS/MX:域名]
}DNS_ResouceRecord;
typedef struct tag
{
unsigned short qr; //[1]标示该消息是请求消息(该位为0)还是应答消息(该位为1)
unsigned short opcode; //[4]0 QUERY。标准查询
unsigned short aa; //[1]只在响应消息中有效。该位标示响应该消息的域名服务器是该域中的权威域名服务器。因为Answer Section中可能会有很多域名
unsigned short tc; //[1]标示这条消息是否因为长度超过UDP数据包的标准长度512字节,如果超过512字节,该位被设置为1
unsigned short rd; //[1]是否递归查询。1为递归查询
unsigned short ra; //[1]在响应消息中清除并设置。标示该DNS域名服务器是否支持递归查询。
unsigned short z; //[3] 冗余res 0
unsigned short rcode; //[4] 0 成功的响应
}TAG;
/***********************缓冲区操作和工具***************************/
/*此函数用于向buffer中写入8bit数据
* *buffer:指向缓冲区
* *bufferPointer:目前已写入的缓冲区最新一位的下一位
* 以下put16bits put32bits同理
*/
void put1Byte(char *buffer,int *bufferPointer, char value)
{
//调整value为网络字节序
value = htons(value);
//void *memcpy(void *dest, void *src, unsigned int count);
//用于 把资源内存(src所指向的内存区域) 拷贝到目标内存(dest所指向的内存区域),count为拷贝区域大小.
//buffer为缓冲区首地址,bufferPointer为缓冲区已写入下标,此函数参数为指向这两个量的指针,通过传递地址来实现主函数与子函数的实参传递。
//value为欲写入缓冲区的数据(value为8bit)
memcpy(buffer + *bufferPointer,&value,1);
//缓冲区已写入下标向后移动,使其指向下一次写入时应该写入的位置,*bufferPointer为指针bufferPointer所指地址的内容
*bufferPointer += 1;
}
void put2Bytes(char *buffer,int *bufferPointer, unsigned short value)
{
value = htons(value);
memcpy(buffer + *bufferPointer,&value,2);
*bufferPointer += 2;
}
void put4Bytes(char *buffer,int *bufferPointer, unsigned int value)
{
value = htons(value);
memcpy(buffer + *bufferPointer,&value,4);
*bufferPointer += 4;
}
//将变长字符串str写入buffer
void putDomainName(char *buffer,int *bufferPointer, char *str)
{
memcpy(buffer + *bufferPointer,str,strlen(str)+1); //末尾0需要一起打印
*bufferPointer += strlen(str)+1;
}
//从缓冲区取16个位
unsigned short get2Bytes(char *buffer,int *bufferPointer)
{
unsigned short value;
memcpy(&value,buffer + *bufferPointer,2);
*bufferPointer += 2;
return ntohs(value);
}
unsigned int get4bits(char *buffer,int *bufferPointer)
{
unsigned int value;
memcpy(&value,buffer + *bufferPointer,4);
*bufferPointer += 4;
return ntohs(value);
}
//读取变长字符串str 读取到0即停止 0即为'\0'
//域名不考虑字节序问题 ,也不用考虑编码问题,都是一个字节一个字节读
void getDomainName(char *buffer,int *bufferPointer,int *lengthOfDomain)
{
int valueWriting=0;
while(buffer[*bufferPointer]!=0)
{
domain_temp[valueWriting] = buffer[*bufferPointer];
valueWriting++;
(*bufferPointer)++;
}
domain_temp[valueWriting] = 0; //末尾为0,写入字符串结束符,方便对字符数组进行字符串操作
(*bufferPointer)++; //缓冲区读写下一位指针指示跳过末尾0
*lengthOfDomain = valueWriting+1; //包含了末尾结束符
}
//eg 3www6google3com0
//生成域名编码
//一个UTF8 数字占1个字节。一个UTF8汉字占3个字节
void encode_domain(char* domain)
{
memset(domain_temp,0,MAX_SIZE_OF_DOMAIN);
int valueWriting=0;
char *p,*q;
q = domain;
p = q;
char count = 0;
while(1)
{
if((*p=='.')||(*p==0))
{
//第一位为count,写入字符串
*(domain_temp+valueWriting)=count; //此处最后一位0的情况写入了
valueWriting += 1;
//写入q开始,长度为count的字符串(长度为count)
memcpy(domain_temp+valueWriting,q,count);
valueWriting += count;
//计数清0
count = 0;
//如果未读到字符串末尾,将q移动到p+1的位置,重新开始下一轮
if (*p=='.')
{
q=p+1;
p = q;
}else break;
}else
{
p++;
count++;
}
}
}
//解析编码的域名
void decode_domain(char* domain)
{
memset(domain_temp,0,MAX_SIZE_OF_DOMAIN);
int valueWriting = 0;
char *p = domain;
int count = *p;
while(count!=0)
{
for(int i=0;i<count;i++)
{
p += 1;
domain_temp[valueWriting] = *p;
valueWriting++;
}
if (*(p+1)!=0)
{
domain_temp[valueWriting] = '.';
valueWriting++;
}
p += 1;
count = *p;
}
domain_temp[valueWriting]=0;
}
//OPCODE、Z、RCODE不用管,无论输入什么都为0。其他都是单位的
unsigned short create_tag(unsigned short qr,unsigned short opcode,unsigned short aa,unsigned short tc,unsigned short rd,unsigned short ra,unsigned short z,unsigned short rcode)
{
unsigned short tag = 0;
if (qr==1) tag = tag | 0x8000;
if (aa==1) tag = tag | 0x0400;
if (tc==1) tag = tag | 0x0200;
if (rd==1) tag = tag | 0x0100;
if (ra==1) tag = tag | 0x0080;
return tag;
}
//类型的名字与编码的转换
unsigned short strTypeToCode(char* type)
{
if (strcmp(type,"A")==0) return 0x0001;
if (strcmp(type,"NS")==0) return 0x0002;
if (strcmp(type,"CNAME")==0) return 0x0005;
if (strcmp(type,"MX")==0) return 0x000F;
return 0;
}
char* codeTypeToStr(unsigned short num)
{
if (num==0x0001) return "A";
if (num==0x0002) return "NS";
if (num==0x0005) return "CNAME";
if (num==0x000F) return "MX";
return "ERROR";
}
/***********************DNS头部操作***************************/
/*
*此函数用于填充客户端发送请求的dns包的头部
*/
void create_query_header(struct DNS_Header *query_header,unsigned short id,unsigned short tag,unsigned short queryNum,unsigned short answerNum,unsigned short authorNum,unsigned short addNum)
{
query_header->id = id;
query_header->tag = tag;
query_header->queryNum = queryNum;
query_header->answerNum = answerNum;
query_header->authorNum = authorNum;
query_header->addNum = addNum;
}
/*此函数用于将已经填充好的dns头部结构体的成员依次写入buffer
* *header: 指向已填充好的dns头部结构体的指针
* *buffer: 指向缓冲区
* *bufferPointer: 目前已写入的缓冲区最新一位的下一位
*/
void encode_header(struct DNS_Header *header,char *buffer,int *bufferPointer)
{
put2Bytes(buffer,bufferPointer,header->id);
put2Bytes(buffer,bufferPointer,header->tag);
put2Bytes(buffer,bufferPointer,header->queryNum);
put2Bytes(buffer,bufferPointer,header->answerNum);
put2Bytes(buffer,bufferPointer,header->authorNum);
put2Bytes(buffer,bufferPointer,header->addNum);
}
void decode_header(struct DNS_Header *header,char *buffer,int *bufferPointer)
{
header->id=get2Bytes(buffer,bufferPointer);
header->tag=get2Bytes(buffer,bufferPointer);
header->queryNum=get2Bytes(buffer,bufferPointer);
header->answerNum=get2Bytes(buffer,bufferPointer);
header->authorNum=get2Bytes(buffer,bufferPointer);
header->addNum=get2Bytes(buffer,bufferPointer);
}
void print_header(struct DNS_Header *query_header)
{
printf("[DNS HEADER]\n");
printf("ID : %d\n",query_header->id);
printf("TAG : 0x%x\n",query_header->tag);
printf("QueryNum : %d\n",query_header->queryNum);
printf("AnswerNum : %d\n",query_header->answerNum);
printf("AuthorNum : %d\n",query_header->authorNum);
printf("AddNum : %d\n",query_header->addNum);
}
/***********************DNS请求部分操作***************************/
/*
*生成DNS包的请求部分
*/
void create_query_section(struct DNS_Query *query_section,char* domain_name, unsigned short qtype, unsigned short qclass)
{
int domain_length = strlen(domain_name);
query_section->name = malloc(domain_length+1);
memcpy(query_section->name,domain_name,domain_length+1);
query_section->qtype = qtype;
query_section->qclass = qclass;
}
/*
*将已经填充好的dns的一个请求结构体的成员依次写入buffer(调用一次该函数只写入一个请求
*/
void encode_query_section(struct DNS_Query *query_section,char *buffer,int *bufferPointer)
{
//先计算用decodeDomain得到字符串
//再用strlen计算字符串长度为点语法name长度+2(头尾多了一个数字)
//再发送
char *domain_name;
int lengthOfEncodedDomain = strlen(query_section->name)+2;
domain_name = malloc(lengthOfEncodedDomain);
encode_domain(query_section->name);
memcpy(domain_name,domain_temp,lengthOfEncodedDomain);
putDomainName(buffer,bufferPointer,domain_name);
put2Bytes(buffer,bufferPointer,query_section->qtype);
put2Bytes(buffer,bufferPointer,query_section->qclass);
}
/*
*解析请求部分。解析即为将缓冲区的字节流提取,转码,生成对应的结构体
*/
void decode_query_section(struct DNS_Query *query_section,char *buffer,int *bufferPointer)
{
//从缓冲区读出编码过的域名
char* domain_name = malloc(MAX_SIZE_OF_DOMAIN);
memset(domain_name,0,MAX_SIZE_OF_DOMAIN);
int lengthOfDomain=0;
getDomainName(buffer,bufferPointer,&lengthOfDomain);
memcpy(domain_name,domain_temp,lengthOfDomain);
//解码域名
decode_domain(domain_name);
memcpy(domain_name,domain_temp,strlen(domain_name));
query_section->name = domain_name;
query_section->qtype = get2Bytes(buffer,bufferPointer);
query_section->qclass = get2Bytes(buffer,bufferPointer);
}
void print_query_section(struct DNS_Query *query_section)
{
printf("[DNS QUERY]\n");
printf("Name : %s\n",query_section->name);
printf("Type : %s\n",codeTypeToStr(query_section->qtype));
printf("Class : IN\n");
}
/***********************DNS RR操作和RR文件解析操作***************************/
//生成resource record记录
void create_resource_record(struct DNS_RR *resource_record,char* name, unsigned short type, unsigned short _class, unsigned int ttl, unsigned short pre,char *rdata) //data_len不用输入
{
//unsigned short pre为一个MX类型特有的优先级,定长,只有MX类型发送。
int domain_length = strlen(name);
//易错点:strlen只读到0但不包含0,所以为了把结束符也复制进去,长度要+1
resource_record->name = malloc(domain_length+1);
memcpy(resource_record->name,name,domain_length+1);
resource_record->type = type;
resource_record->_class = _class;
resource_record->ttl = ttl; //data_len
if (type==0x0001) resource_record->data_len=4; //对于IP,长度为4 data_len是编码后的长度,length是非编码长度,注意
else resource_record->data_len = strlen(rdata) + 2; //对于域名,生成data_len包含末尾结束符(域名末尾结束符)
//pre
if (type==0x000F) {
resource_record->pre = pre;
resource_record->data_len += 2; //对于邮件类型,由于有pre的存在,多占两个字节
}
//char* rdata
int rdata_length = strlen(rdata); //要加上末尾结束符
resource_record->rdata = malloc(rdata_length+1);
memcpy(resource_record->rdata,rdata,rdata_length+1);
}
//编码resource record记录,编码即为将结构体的内容编码,处理为字节流,写入缓冲区
void encode_resource_record(struct DNS_RR *resource_record,char *buffer,int *bufferPointer)
{
char *domain_name;
int lengthOfEncodedDomain = strlen(resource_record->name)+2;
domain_name = malloc(lengthOfEncodedDomain);
encode_domain(resource_record->name);
memcpy(domain_name,domain_temp,lengthOfEncodedDomain);
putDomainName(buffer,bufferPointer,domain_name);
put2Bytes(buffer,bufferPointer,resource_record->type);
put2Bytes(buffer,bufferPointer,resource_record->_class);
put4Bytes(buffer,bufferPointer,resource_record->ttl);
put2Bytes(buffer,bufferPointer,resource_record->data_len);
if (resource_record->type==0x000F)
put2Bytes(buffer,bufferPointer,resource_record->pre);
//如果类型为A,发送的是IP,将IP写入缓冲区
if(resource_record->type == 0x0001)
{
//不能调用get put函数,因为inet_addr自带字节序变换功能
unsigned int rdata = inet_addr(resource_record->rdata);
memcpy(buffer + *bufferPointer,&rdata,4);
*bufferPointer += 4;
}else{
//如果类型为MX、CNAME、NS
//则发送的是域名,则调用域名编码
char *rdata;
int lengthOfEncodedDomain2 = strlen(resource_record->rdata)+2;
rdata = malloc(lengthOfEncodedDomain2);
encode_domain(resource_record->rdata);
memcpy(rdata,domain_temp,lengthOfEncodedDomain2);
putDomainName(buffer,bufferPointer,rdata);
}
}
//解析resource record记录
void decode_resource_record(struct DNS_RR *resource_record,char *buffer,int *bufferPointer)
{
//从缓冲区读出编码过的域名
char* domain_name = malloc(MAX_SIZE_OF_DOMAIN);
memset(domain_name,0,MAX_SIZE_OF_DOMAIN);
int lengthOfDomain=0;
getDomainName(buffer,bufferPointer,&lengthOfDomain);
memcpy(domain_name,domain_temp,lengthOfDomain);
//解码域名
decode_domain(domain_name);
memcpy(domain_name,domain_temp,strlen(domain_name));
resource_record->name = domain_name;
resource_record->type = get2Bytes(buffer,bufferPointer);
resource_record->_class = get2Bytes(buffer,bufferPointer);
resource_record->ttl = get4bits(buffer,bufferPointer);
resource_record->data_len = get2Bytes(buffer,bufferPointer);
if (resource_record->type==0x000F)
resource_record->pre = get2Bytes(buffer,bufferPointer);
//如果发送的是IP(类型为A),则读出IP 。 不能采用get put方法,因为inet_ntoa方法已经更换字节序
if(resource_record->type == 0x0001)
{
unsigned int rdata;
memcpy(&rdata,buffer + *bufferPointer,4);
*bufferPointer += 4;
struct in_addr in;
memcpy(&in, &rdata, 4);
resource_record->rdata = malloc(MAX_SIZE_OF_DOMAIN);
char *temp = inet_ntoa(in);
memcpy(resource_record->rdata,temp,strlen(temp)+1); //+1是为了包含末尾0
}else{
//如果发送的是域名,则调用域名解码(类型为CNAME NS MX)
//从缓冲区读出编码过的域名
char* rdata = malloc(MAX_SIZE_OF_DOMAIN);
int lengthOfDomain2=0;
getDomainName(buffer,bufferPointer,&lengthOfDomain2);
memcpy(rdata,domain_temp,lengthOfDomain2);
//解码域名
decode_domain(rdata);
memcpy(rdata,domain_temp,strlen(rdata));
resource_record->rdata = rdata;
}
}
void print_resource_record(struct DNS_RR *resource_record)
{
printf("[RESOURCE RECORD]\n");
printf("Name : %s\n",resource_record->name);
printf("Type : %s\n",codeTypeToStr(resource_record->type));
printf("Class : IN\n");
printf("TTL : %d\n",resource_record->ttl);
printf("Data_Len : %d\n",resource_record->data_len);
if (resource_record->type==0x000F)
printf("Preference : %d\n",resource_record->pre);
printf("IP|DOMAIN : %s\n",resource_record->rdata);
printf("===================================\n");
}
//砍掉一个域名第一个.之前的部分,如果已经是最后一节,指向域名的指针指向NULL
void cut(char** domainPointer) //这里传入的是 指向指向域名的指针的指针
{
while(1)
{
(*domainPointer)++;
if (**domainPointer=='.')
{
(*domainPointer)++;
break;
}
if (**domainPointer==0)
{
*domainPointer = NULL;
break;
}
}
}
/***********************文件读写***************************/
//将RR写进cache文件里
void addRRToCache(struct DNS_RR *resource_record, char* cacheFile)
{
FILE *RR = fopen(cacheFile, "a+");
fprintf(RR,"%s ",resource_record->name);
fprintf(RR,"%d ",resource_record->ttl);
fprintf(RR,"IN ");
fprintf(RR,"%s ",codeTypeToStr(resource_record->type));
fprintf(RR,"%s\n",resource_record->rdata);
fclose(RR);
}
//第一次在RR文件里扫描 (初次搜索函数)
//如果找到了,返回1,且encode进buffer
int firstFindRR(struct DNS_Query *query_section,char *RRDOCUMENT,char *buffer,int *bufferPointer)
{
int over = 0;
FILE *RR = fopen( RRDOCUMENT, "r" );
//定义一个RR结构体用来储存从文件中读入的一条RR
struct DNS_RR *fileRR;
fileRR = malloc(sizeof(DNS_ResouceRecord));
memset(fileRR,0,sizeof(DNS_ResouceRecord));
fileRR->name=malloc(MAX_SIZE_OF_DOMAIN);
fileRR->rdata=malloc(MAX_SIZE_OF_DOMAIN);
//第一次搜索
while(fscanf(RR,"%s ",fileRR->name)!=EOF)
{
fscanf(RR,"%d",&fileRR->ttl);
char type[10],_class[10];
fscanf(RR,"%s ",_class);
fscanf(RR,"%s ",type);
fileRR->type = strTypeToCode(type);
fscanf(RR,"%s\n",fileRR->rdata);
if((strcmp(query_section->name,fileRR->name)==0) && (query_section->qtype==fileRR->type))
{
printf("\n发送回复:\n");
//生成answer RR
create_resource_record(fileRR,fileRR->name, fileRR->type, 0x0001, fileRR->ttl, 0x0000,fileRR->rdata);
//生成头
struct DNS_Header *header;
header = malloc(sizeof(DNS_HEAD));
unsigned short tag = create_tag(1,0,1,0,0,0,0,0);
if (strcmp(type,"MX")==0) create_query_header(header,0x1235,tag,0,1,0,1);
else create_query_header(header,999,tag,0,1,0,0);
//将头和answer encode进buffer
encode_header(header,buffer,bufferPointer);
print_header(header);
encode_resource_record(fileRR,buffer,bufferPointer);
print_resource_record(fileRR);
over=1;
break;
}
}
//读指针回到开头
fseek(RR,0,0);
//对于MX类型,特殊,需要再搜索一遍,搜索到的邮件服务器域名的IP,并写入addition RR中发送
if ((fileRR->type==0x000F)&&(over==1))
{
struct DNS_RR *addFileRR;
addFileRR = malloc(sizeof(DNS_ResouceRecord));
addFileRR->name=malloc(MAX_SIZE_OF_DOMAIN);
addFileRR->rdata=malloc(MAX_SIZE_OF_DOMAIN);
while(fscanf(RR,"%s ",addFileRR->name)!=EOF)
{
fscanf(RR,"%d ",&addFileRR->ttl);
char type[10],_class[10];
fscanf(RR,"%s ",_class);
fscanf(RR,"%s ",type);
addFileRR->type = strTypeToCode(type);
fscanf(RR,"%s\n",addFileRR->rdata);
if(strcmp(fileRR->rdata,addFileRR->name)==0)
{
printf("邮件服务器:\n");
//生成addition RR
create_resource_record(addFileRR,fileRR->rdata, 1, 1, fileRR->ttl, 0, addFileRR->rdata);
encode_resource_record(addFileRR,buffer,bufferPointer);
print_resource_record(addFileRR);
break;
}
}
}
fclose(RR);
return over;
}
void loopFindNS(struct DNS_Query *query_section,char *RRDOCUMENT,char *buffer,int *bufferPointer)
{
FILE *RR = fopen( RRDOCUMENT, "r" );
cut(&query_section->name);
//剪掉首段地址,进行第二次搜索
while(query_section->name!=NULL)
{
fseek(RR,0,0);
struct DNS_RR *nextRR;
nextRR = malloc(sizeof(DNS_ResouceRecord));
nextRR->name=malloc(MAX_SIZE_OF_DOMAIN);
nextRR->rdata=malloc(MAX_SIZE_OF_DOMAIN);
while(fscanf(RR,"%s ",nextRR->name)!=EOF)
{
fscanf(RR,"%d ",&nextRR->ttl);
char type[10],_class[10];
fscanf(RR,"%s ",_class);
fscanf(RR,"%s ",type);
nextRR->type = strTypeToCode(type);
fscanf(RR,"%s\n",nextRR->rdata);
if(strcmp(query_section->name,nextRR->name)==0)
{
printf("\n下一级服务器信息:\n");
//生成头
struct DNS_Header *header;
header = malloc(sizeof(DNS_HEAD));
unsigned short tag = create_tag(1,0,1,0,0,0,0,0);
create_query_header(header,999,tag,0,0,1,1);
encode_header(header,buffer,bufferPointer);
print_header(header);
//生成authority RR NS记录type=2 此时query_section->name经过cut后已经变成了下一个要去的DNS服务器域名
struct DNS_RR *authRR;
authRR = malloc(sizeof(DNS_ResouceRecord));
create_resource_record(authRR, query_section->name, 2, 1, nextRR->ttl, 0, query_section->name);
encode_resource_record(authRR,buffer,bufferPointer);
print_resource_record(authRR);
//生成additon RR A记录type=1
struct DNS_RR *addRR;
addRR = malloc(sizeof(DNS_ResouceRecord));
create_resource_record(addRR, query_section->name, 1, 1, nextRR->ttl, 0, nextRR->rdata);
encode_resource_record(addRR,buffer,bufferPointer);
print_resource_record(addRR);
goto out;
}
}
cut(&query_section->name);
}
out:
fclose(RR);
}