背景需求:使用指定dns服务器去解析域名可以使用”nslookup www.baidu.com 114.114.114.114“,但如果使用api接口实现则没有现成的代码。
DNS协议参考:http://c.biancheng.net/view/6457.html
简易方法: nslookup www.baidu.com 114.114.114.114|grep Address: |awk '{print $2}'|sed -n '2p'
实现方法:向指定的DNS服务器发送UDP数据,然后将返回的数据按照DNS协议进行解析。
参考网友代码实现使用指定dns ip去解析域名。基本功能已经完成,健壮性需要再调试。
/**
* Parse data chunk into dns name
* @param domain 域名
* @param dnsip dnsip 目前支持ipv4的dnsip
* @param flag 解析ip的类型。4:ipv4 ; 6:ipv6
* @param resolved 解析结果
*/
int nslookup_domain(char* domain, char* dnsip, int flag, char* resolved_output)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#define DNS_HOST 0x01
#define DNS_CNAME 0x05
#define DNS_IPV6 0x1c
int socketfd;
struct sockaddr_in dest;
static int is_pointer(int in)
{
return ((in & 0xc0) == 0xc0);
}
int creat_dns_socket( char* dnsip )
{
socketfd = socket(AF_INET , SOCK_DGRAM , 0);
if(socketfd < 0)
{
perror("create socket failed");
return -1;
}
bzero(&dest , sizeof(dest));
dest.sin_family = AF_INET;
dest.sin_port = htons(53);
dest.sin_addr.s_addr = inet_addr(dnsip);
return 0;
}
static void generate_question(const char *dns_name, int flag, unsigned char *buf , int *len)
{
char *pos;
unsigned char *ptr;
int n;
*len = 0;
ptr = buf;
pos = (char*)dns_name;
for(;;)
{
n = strlen(pos) - (strstr(pos , ".") ? strlen(strstr(pos , ".")) : 0);
*ptr ++ = (unsigned char)n;
memcpy(ptr , pos , n);
*len += n + 1;
ptr += n;
if(!strstr(pos , "."))
{
*ptr = (unsigned char)0;
ptr ++;
*len += 1;
break;
}
pos += n + 1;
}
if(flag == 6)
*((unsigned short*)ptr) = htons(28); //1 ipv4; 28 ipv6
else
*((unsigned short*)ptr) = htons(1); //1 ipv4; 28 ipv6
*len += 2;
ptr += 2;
*((unsigned short*)ptr) = htons(1);
*len += 2;
}
static void send_dns_request(const char *domain, int flag)
{
unsigned char request[256];
unsigned char *ptr = request;
unsigned char question[128];
int question_len;
generate_question(domain , flag, question , &question_len);
*((unsigned short*)ptr) = htons(0xff00); //Transaction ID
ptr += 2;
*((unsigned short*)ptr) = htons(0x0100); //Flags: 0x0100 Standard query
ptr += 2;
*((unsigned short*)ptr) = htons(1); //Questions:1
ptr += 2;
*((unsigned short*)ptr) = 0; //Answer RRs:0
ptr += 2;
*((unsigned short*)ptr) = 0; //Authority RRs:0
ptr += 2;
*((unsigned short*)ptr) = 0; //Additional RRs:0
ptr += 2;
memcpy(ptr , question , question_len);
ptr += question_len;
sendto(socketfd , request , question_len + 12 , 0, (struct sockaddr*)&dest , sizeof(struct sockaddr));
}
/**
* Parse data chunk into dns name
* @param chunk The complete response chunk
* @param ptr The pointer points to data
* @param out This will be filled with dns name
* @param len This will be filled with the length of dns name
*/
static void parse_dns_name(unsigned char *chunk, unsigned char *ptr , char *out , int *len)
{
int n , flag;
char *pos = out + (*len);
for(;;)
{
flag = (int)ptr[0];
if(flag == 0)
break;
if(is_pointer(flag))
{
n = (int)ptr[1];
ptr = chunk + n;
parse_dns_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 void parse_dns_response(char* resolved_output)
{
unsigned char buf[1024];
unsigned char *ptr = buf;
struct sockaddr_in addr;
int i , n, flag , querys , answers;
int type , datalen , len;
int ttl =0;
int curttl = 0;
char cname[128] , aname[128] , ip[64] ;
unsigned char netip[4];
unsigned char netip6[24];
char ipv6[64];
size_t addr_len = sizeof(struct sockaddr_in);
n = recvfrom(socketfd , buf , sizeof(buf) , 0, (struct sockaddr*)&addr , (socklen_t*)&addr_len);
if(n == -1)
{
printf("recvfrom is failed.");
return;
}
ptr += 4; /* move ptr to Questions; Transaction ID+Flags */
querys = ntohs(*((unsigned short*)ptr));
ptr += 2; /* move ptr to Answer RRs */
answers = ntohs(*((unsigned short*)ptr));
ptr += 6; /* move ptr to Querys */
/* move over Querys */
for(i= 0 ; i < querys ; i ++)
{
for(;;)
{
flag = (int)ptr[0];
ptr += (flag + 1);
if(flag == 0)
break;
}
ptr += 4;
}
/* now ptr points to Answers */
for(i = 0 ; i < answers ; i ++)
{
bzero(aname , sizeof(aname));
len = 0;
parse_dns_name(buf , ptr , aname , &len);
ptr += 2; /* move ptr to Type*/
type = htons(*((unsigned short*)ptr));
ptr += 4; /* move ptr to Time to live */
ttl = htonl(*((unsigned int*)ptr));
ptr += 4; /* move ptr to Data lenth */
datalen = ntohs(*((unsigned short*)ptr));
ptr += 2; /* move ptr to Data*/
if(type == DNS_CNAME)
{
bzero(cname , sizeof(cname));
len = 0;
parse_dns_name(buf , ptr , cname , &len);
// printf("%s is an alias for %s\n" , aname , cname);
ptr += datalen;
}
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);*/
if(curttl < ttl)
{
memcpy(resolved_output, ip, strlen(ip));
curttl = ttl;
/*printf("ip[%s]\n", ip);
printf("resolved_output[%s]\n", resolved_output);*/
}
}
ptr += datalen;
}
if(type == DNS_IPV6)
{
bzero(ipv6 , sizeof(ipv6));
if(datalen == 16)
{
memcpy(netip6 , ptr , datalen);
inet_ntop(AF_INET6 , netip6 , ipv6 , 46);
/* printf("%s has address %s\n" , aname , ipv6);
printf("\tTime to live: %d minutes , %d seconds\n", ttl / 60 , ttl % 60);*/
if(curttl < ttl)
{
memcpy(resolved_output, ipv6, strlen(ipv6));
curttl = ttl;
/* printf("ipv6[%s]\n", ipv6);
printf("resolved_output[%s]\n", resolved_output);*/
}
}
ptr += datalen;
}
}
ptr += 2;
}
int nslookup_domain(char* domain, char* dnsip, int flag, char* resolved_output)
{
if(domain == NULL || dnsip == NULL || resolved_output == NULL)
{
printf("input param is empty.please check it.");
return -1;
}
if( 0 != creat_dns_socket(dnsip) )
{
return -1;
}
send_dns_request(domain, flag);
parse_dns_response(resolved_output);
return 0;
}
int main(int argc , char *argv[])
{
char domain[] = "www.163.com";
char dnsip[] = "114.114.114.114";
char resolved_output[100] = "";
nslookup_domain(domain, dnsip, 4, resolved_output);
printf("\nv4[%s]\n", resolved_output);
memset(resolved_output, 0, sizeof(resolved_output));
nslookup_domain(domain, dnsip, 6, resolved_output);
printf("\nv6[%s]\n", resolved_output);
return 0;
}