nslookup 用来在cmd中
为什么使用UDP 因为和TCP相比 响应速度快多用于游戏 传输速度快多用于下载
查看网址对应的IP 格式:nslookup www.xxx.com
本文代码注意片段 dns_parse_name(),dns_parse_response(); is_pointer();
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//#include <m
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#define DNS_SERVER_PORT 53 //服务器端口
#define DNS_SERVER_IP "114.114.114.114" //服务器IP
#define DNS_HOST 0x01 //
#define DNS_CNAME 0x05
//报文头部 Header
struct dns_header
{
//报文中每个占据一个字节 16b 具体参考DNS报文头部格式
unsigned short id; //会话标识
unsigned short flags; //标志
unsigned short questions; //问题数
unsigned short answer; //回答 资源记录数
unsigned short authority; //授权 资源记录数
unsigned short additional;//附加 资源记录数
};
//报文正文部分:Queries
struct dns_question
{
int length; //查询名长度
unsigned char* name; //查询名 存储查询域名
unsigned short qtype; //查询类别
unsigned short qclass; //查询类
};
struct dns_item {
char* domain;
char* ip;
};
//发送报文 给服务器
//填充header
int dns_create_header(struct dns_header *header)
{
if (header == NULL) return -1;
memset(header, 0, sizeof(struct dns_header));
//随机值random
srandom(time(NULL)); //随机数种子 指范围大小
header->id = random(); //范围中随机出一个数值
header->flags = htons(0x0100); //需要转换成网络字节序 htons()//这个函数用来转换网络字节
header->questions = htons(1) ; //代表存储一个域名
//header->answer = ;
//header->authority = ;
//header->additional= ;
return 0;
}
//www.baidu.com
//name中存储的数据应该是 3www5baidu3com这种格式 hostname是域名
int dns_create_questions(struct dns_question*question ,char *hostname)
{
if (question == NULL || hostname == NULL) return -1;
memset(question, 0, sizeof(struct dns_question));
question->name = (char*)malloc(strlen(hostname) + 2);
if (question->name == NULL) return -2;
question->length = strlen(hostname) + 2;
question->qtype = htons(1);
question->qclass = htons(1);
//name存储
const char delime[2] = ".";//以某个东西为截取点
char* qname = question->name;
char* hostname_dup = strdup(hostname); //将hostname复制一份 因为 strdup 自带malloc 所以完成操作之后要进行释放
char* token = strtok(hostname_dup,delime); //前面放要截取的字段 后面放按什么截取
while (token != NULL)
{
size_t len = strlen(token);//取得截取的长度
*qname = (char)len;
qname++;
strncpy(qname, token, len + 1); //strncpy 可以指定copy的长度 把token的len+1位复制给name
qname += len;
token = strtok(NULL, delime); //strtok和strdup都是依赖上一次执行结果的 所以不是属于线程安全
}
//free(token);
free(hostname_dup);
//return 0;
}
//打包 前面两个报文合并到request中
int dns_bulid_request(struct dns_question *question, struct dns_header *header, char *request, int rlen)
{
if (question == NULL || header == NULL || request == NULL) return -1;
memset(request, 0, rlen);
//copy header ->request
memcpy(request, header, sizeof(struct dns_header));
int offset = sizeof(struct dns_header);
//copy question ->request
memcpy(request + offset, question->name, question->length); //request+offset指指针从header数据后一位开始写入
offset += question->length;
memcpy(request + offset, &question->qtype, sizeof(question->qtype)); //request+offset指指针从header数据后一位开始写入
offset += sizeof(question->qtype);
memcpy(request + offset, &question->qclass, sizeof(question->qclass)); //request+offset指指针从header数据后一位开始写入
offset += sizeof(question->qclass);
return offset;
}
static int is_pointer(int in) {
return ((in & 0xC0) == 0xC0);
}
static void dns_parse_name(unsigned char* chunk, unsigned char* ptr, char* out, int* len) {
int flag = 0, n = 0, alen = 0;
char* pos = out + (*len);
while (1) {
flag = (int)ptr[0];
if (flag == 0) break;
if (is_pointer(flag)) {
n = (int)ptr[1];
ptr = chunk + n;
dns_parse_name(chunk, ptr, out, len);
break;
}
else {
ptr++;
memcpy(pos, ptr, flag);
pos += flag;
ptr += flag;
*len += flag;
if ((int)ptr[0] != 0) {
memcpy(pos, ".", 1);
pos += 1;
(*len) += 1;
}
}
}
}
//解析的入口函数
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);
ptr += 2;
int answers = ntohs(*(unsigned short*)ptr);
ptr += 6;
for (i = 0; i < querys; i++) {
while (1) {
int flag = (int)ptr[0];
ptr += (flag + 1);
if (flag == 0) break;
}
ptr += 4;
}
char cname[128], aname[128], 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++) {
bzero(aname, sizeof(aname));
len = 0;
dns_parse_name(buffer, ptr, aname, &len);
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) {
bzero(cname, sizeof(cname));
len = 0;
dns_parse_name(buffer, ptr, cname, &len);
ptr += datalen;
}
else if (type == DNS_HOST) {
bzero(ip, sizeof(ip));
if (datalen == 4) {
memcpy(netip, ptr, datalen);
inet_ntop(AF_INET, netip, ip, sizeof(struct sockaddr));
printf("%s has address %s\n", aname, ip);
printf("\tTime to live: %d minutes , %d seconds\n", ttl / 60, ttl % 60);
list[cnt].domain = (char*)calloc(strlen(aname) + 1, 1);
memcpy(list[cnt].domain, aname, strlen(aname));
list[cnt].ip = (char*)calloc(strlen(ip) + 1, 1);
memcpy(list[cnt].ip, ip, strlen(ip));
cnt++;
}
ptr += datalen;
}
}
*domains = list;
ptr += 2;
return cnt;
}
//使用udp发送报文 domain域名
int dns_client_commit(char *domain)
{
int sockfd = socket(AF_INET,SOCK_DGRAM,0);//SOCK_DGRAM数据以报文的方式发送
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);
// connect 用来TCP的三次握手
int ret = connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr));
printf("connect : %d\n", ret);
struct dns_header header = { 0 };
dns_create_header(&header);
struct dns_question question = { 0 };
dns_create_questions(&question, domain);
char request[1024] = { 0 };
int len = dns_bulid_request( &question, &header, request,1024);
//request 请求数据
int slen = sendto(sockfd, request, len, 0, (struct sockaddr*)&servaddr,sizeof(struct sockaddr)); // 1 2.request 3.返回的request长度 4.0 5.服务区IP 6.服务器地址长度
//recvfrom 接收返回的数据
char response[1024] = { 0 }; //存放服务器返回的数据
size_t addr_len = sizeof(struct sockaddr_in);
struct sockaddr_in addr;
int relen = recvfrom(sockfd, response, sizeof(response), 0, (struct sockaddr*)&addr, (socklen_t*)&addr_len);
//printf("recvfrom:%d ,%s \n", relen, response);
//int i = 0;
//for (i = 0; i < relen; i++)
//{
// printf("%c",response[i]);
//}
//printf("\n");
//for (i = 0; i < relen; i++)
//{
// printf("%x", response[i]);
//}
//printf("\n");
struct dns_item* dns_domain = NULL;//存储返回的解析数据
dns_parse_response(response, &dns_domain);
free(dns_domain);
return relen;
}
int main(int argc, char* argv[]) {
if (argc < 2) return -1;
dns_client_commit(argv[1]);
}