-
-
Save fffaraz/9d9170b57791c28ccda9255b48315168 to your computer and use it in GitHub Desktop.
//DNS Query Program on Linux | |
//Author : Silver Moon ([email protected]) | |
//Dated : 29/4/2009 | |
//Header Files | |
#include<stdio.h> //printf | |
#include<string.h> //strlen | |
#include<stdlib.h> //malloc | |
#include<sys/socket.h> //you know what this is for | |
#include<arpa/inet.h> //inet_addr , inet_ntoa , ntohs etc | |
#include<netinet/in.h> | |
#include<unistd.h> //getpid | |
//List of DNS Servers registered on the system | |
char dns_servers[10][100]; | |
int dns_server_count = 0; | |
//Types of DNS resource records :) | |
#define T_A 1 //Ipv4 address | |
#define T_NS 2 //Nameserver | |
#define T_CNAME 5 // canonical name | |
#define T_SOA 6 /* start of authority zone */ | |
#define T_PTR 12 /* domain name pointer */ | |
#define T_MX 15 //Mail server | |
//Function Prototypes | |
void ngethostbyname (unsigned char* , int); | |
void ChangetoDnsNameFormat (unsigned char*,unsigned char*); | |
unsigned char* ReadName (unsigned char*,unsigned char*,int*); | |
void get_dns_servers(); | |
//DNS header structure | |
struct DNS_HEADER | |
{ | |
unsigned short id; // identification number | |
unsigned char rd :1; // recursion desired | |
unsigned char tc :1; // truncated message | |
unsigned char aa :1; // authoritive answer | |
unsigned char opcode :4; // purpose of message | |
unsigned char qr :1; // query/response flag | |
unsigned char rcode :4; // response code | |
unsigned char cd :1; // checking disabled | |
unsigned char ad :1; // authenticated data | |
unsigned char z :1; // its z! reserved | |
unsigned char ra :1; // recursion available | |
unsigned short q_count; // number of question entries | |
unsigned short ans_count; // number of answer entries | |
unsigned short auth_count; // number of authority entries | |
unsigned short add_count; // number of resource entries | |
}; | |
//Constant sized fields of query structure | |
struct QUESTION | |
{ | |
unsigned short qtype; | |
unsigned short qclass; | |
}; | |
//Constant sized fields of the resource record structure | |
#pragma pack(push, 1) | |
struct R_DATA | |
{ | |
unsigned short type; | |
unsigned short _class; | |
unsigned int ttl; | |
unsigned short data_len; | |
}; | |
#pragma pack(pop) | |
//Pointers to resource record contents | |
struct RES_RECORD | |
{ | |
unsigned char *name; | |
struct R_DATA *resource; | |
unsigned char *rdata; | |
}; | |
//Structure of a Query | |
typedef struct | |
{ | |
unsigned char *name; | |
struct QUESTION *ques; | |
} QUERY; | |
int main( int argc , char *argv[]) | |
{ | |
unsigned char hostname[100]; | |
//Get the DNS servers from the resolv.conf file | |
get_dns_servers(); | |
//Get the hostname from the terminal | |
printf("Enter Hostname to Lookup : "); | |
scanf("%s" , hostname); | |
//Now get the ip of this hostname , A record | |
ngethostbyname(hostname , T_A); | |
return 0; | |
} | |
/* | |
* Perform a DNS query by sending a packet | |
* */ | |
void ngethostbyname(unsigned char *host , int query_type) | |
{ | |
unsigned char buf[65536],*qname,*reader; | |
int i , j , stop , s; | |
struct sockaddr_in a; | |
struct RES_RECORD answers[20],auth[20],addit[20]; //the replies from the DNS server | |
struct sockaddr_in dest; | |
struct DNS_HEADER *dns = NULL; | |
struct QUESTION *qinfo = NULL; | |
printf("Resolving %s" , host); | |
s = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP); //UDP packet for DNS queries | |
dest.sin_family = AF_INET; | |
dest.sin_port = htons(53); | |
dest.sin_addr.s_addr = inet_addr(dns_servers[0]); //dns servers | |
//Set the DNS structure to standard queries | |
dns = (struct DNS_HEADER *)&buf; | |
dns->id = (unsigned short) htons(getpid()); | |
dns->qr = 0; //This is a query | |
dns->opcode = 0; //This is a standard query | |
dns->aa = 0; //Not Authoritative | |
dns->tc = 0; //This message is not truncated | |
dns->rd = 1; //Recursion Desired | |
dns->ra = 0; //Recursion not available! hey we dont have it (lol) | |
dns->z = 0; | |
dns->ad = 0; | |
dns->cd = 0; | |
dns->rcode = 0; | |
dns->q_count = htons(1); //we have only 1 question | |
dns->ans_count = 0; | |
dns->auth_count = 0; | |
dns->add_count = 0; | |
//point to the query portion | |
qname =(unsigned char*)&buf[sizeof(struct DNS_HEADER)]; | |
ChangetoDnsNameFormat(qname , host); | |
qinfo =(struct QUESTION*)&buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname) + 1)]; //fill it | |
qinfo->qtype = htons( query_type ); //type of the query , A , MX , CNAME , NS etc | |
qinfo->qclass = htons(1); //its internet (lol) | |
printf("\nSending Packet..."); | |
if( sendto(s,(char*)buf,sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION),0,(struct sockaddr*)&dest,sizeof(dest)) < 0) | |
{ | |
perror("sendto failed"); | |
} | |
printf("Done"); | |
//Receive the answer | |
i = sizeof dest; | |
printf("\nReceiving answer..."); | |
if(recvfrom (s,(char*)buf , 65536 , 0 , (struct sockaddr*)&dest , (socklen_t*)&i ) < 0) | |
{ | |
perror("recvfrom failed"); | |
} | |
printf("Done"); | |
dns = (struct DNS_HEADER*) buf; | |
//move ahead of the dns header and the query field | |
reader = &buf[sizeof(struct DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(struct QUESTION)]; | |
printf("\nThe response contains : "); | |
printf("\n %d Questions.",ntohs(dns->q_count)); | |
printf("\n %d Answers.",ntohs(dns->ans_count)); | |
printf("\n %d Authoritative Servers.",ntohs(dns->auth_count)); | |
printf("\n %d Additional records.\n\n",ntohs(dns->add_count)); | |
//Start reading answers | |
stop=0; | |
for(i=0;i<ntohs(dns->ans_count);i++) | |
{ | |
answers[i].name=ReadName(reader,buf,&stop); | |
reader = reader + stop; | |
answers[i].resource = (struct R_DATA*)(reader); | |
reader = reader + sizeof(struct R_DATA); | |
if(ntohs(answers[i].resource->type) == 1) //if its an ipv4 address | |
{ | |
answers[i].rdata = (unsigned char*)malloc(ntohs(answers[i].resource->data_len)); | |
for(j=0 ; j<ntohs(answers[i].resource->data_len) ; j++) | |
{ | |
answers[i].rdata[j]=reader[j]; | |
} | |
answers[i].rdata[ntohs(answers[i].resource->data_len)] = '\0'; | |
reader = reader + ntohs(answers[i].resource->data_len); | |
} | |
else | |
{ | |
answers[i].rdata = ReadName(reader,buf,&stop); | |
reader = reader + stop; | |
} | |
} | |
//read authorities | |
for(i=0;i<ntohs(dns->auth_count);i++) | |
{ | |
auth[i].name=ReadName(reader,buf,&stop); | |
reader+=stop; | |
auth[i].resource=(struct R_DATA*)(reader); | |
reader+=sizeof(struct R_DATA); | |
auth[i].rdata=ReadName(reader,buf,&stop); | |
reader+=stop; | |
} | |
//read additional | |
for(i=0;i<ntohs(dns->add_count);i++) | |
{ | |
addit[i].name=ReadName(reader,buf,&stop); | |
reader+=stop; | |
addit[i].resource=(struct R_DATA*)(reader); | |
reader+=sizeof(struct R_DATA); | |
if(ntohs(addit[i].resource->type)==1) | |
{ | |
addit[i].rdata = (unsigned char*)malloc(ntohs(addit[i].resource->data_len)); | |
for(j=0;j<ntohs(addit[i].resource->data_len);j++) | |
addit[i].rdata[j]=reader[j]; | |
addit[i].rdata[ntohs(addit[i].resource->data_len)]='\0'; | |
reader+=ntohs(addit[i].resource->data_len); | |
} | |
else | |
{ | |
addit[i].rdata=ReadName(reader,buf,&stop); | |
reader+=stop; | |
} | |
} | |
//print answers | |
printf("\nAnswer Records : %d \n" , ntohs(dns->ans_count) ); | |
for(i=0 ; i < ntohs(dns->ans_count) ; i++) | |
{ | |
printf("Name : %s ",answers[i].name); | |
if( ntohs(answers[i].resource->type) == T_A) //IPv4 address | |
{ | |
long *p; | |
p=(long*)answers[i].rdata; | |
a.sin_addr.s_addr=(*p); //working without ntohl | |
printf("has IPv4 address : %s",inet_ntoa(a.sin_addr)); | |
} | |
if(ntohs(answers[i].resource->type)==5) | |
{ | |
//Canonical name for an alias | |
printf("has alias name : %s",answers[i].rdata); | |
} | |
printf("\n"); | |
} | |
//print authorities | |
printf("\nAuthoritive Records : %d \n" , ntohs(dns->auth_count) ); | |
for( i=0 ; i < ntohs(dns->auth_count) ; i++) | |
{ | |
printf("Name : %s ",auth[i].name); | |
if(ntohs(auth[i].resource->type)==2) | |
{ | |
printf("has nameserver : %s",auth[i].rdata); | |
} | |
printf("\n"); | |
} | |
//print additional resource records | |
printf("\nAdditional Records : %d \n" , ntohs(dns->add_count) ); | |
for(i=0; i < ntohs(dns->add_count) ; i++) | |
{ | |
printf("Name : %s ",addit[i].name); | |
if(ntohs(addit[i].resource->type)==1) | |
{ | |
long *p; | |
p=(long*)addit[i].rdata; | |
a.sin_addr.s_addr=(*p); | |
printf("has IPv4 address : %s",inet_ntoa(a.sin_addr)); | |
} | |
printf("\n"); | |
} | |
return; | |
} | |
/* | |
* | |
* */ | |
u_char* ReadName(unsigned char* reader,unsigned char* buffer,int* count) | |
{ | |
unsigned char *name; | |
unsigned int p=0,jumped=0,offset; | |
int i , j; | |
*count = 1; | |
name = (unsigned char*)malloc(256); | |
name[0]='\0'; | |
//read the names in 3www6google3com format | |
while(*reader!=0) | |
{ | |
if(*reader>=192) | |
{ | |
offset = (*reader)*256 + *(reader+1) - 49152; //49152 = 11000000 00000000 ;) | |
reader = buffer + offset - 1; | |
jumped = 1; //we have jumped to another location so counting wont go up! | |
} | |
else | |
{ | |
name[p++]=*reader; | |
} | |
reader = reader+1; | |
if(jumped==0) | |
{ | |
*count = *count + 1; //if we havent jumped to another location then we can count up | |
} | |
} | |
name[p]='\0'; //string complete | |
if(jumped==1) | |
{ | |
*count = *count + 1; //number of steps we actually moved forward in the packet | |
} | |
//now convert 3www6google3com0 to www.google.com | |
for(i=0;i<(int)strlen((const char*)name);i++) | |
{ | |
p=name[i]; | |
for(j=0;j<(int)p;j++) | |
{ | |
name[i]=name[i+1]; | |
i=i+1; | |
} | |
name[i]='.'; | |
} | |
name[i-1]='\0'; //remove the last dot | |
return name; | |
} | |
/* | |
* Get the DNS servers from /etc/resolv.conf file on Linux | |
* */ | |
void get_dns_servers() | |
{ | |
FILE *fp; | |
char line[200] , *p; | |
if((fp = fopen("/etc/resolv.conf" , "r")) == NULL) | |
{ | |
printf("Failed opening /etc/resolv.conf file \n"); | |
} | |
while(fgets(line , 200 , fp)) | |
{ | |
if(line[0] == '#') | |
{ | |
continue; | |
} | |
if(strncmp(line , "nameserver" , 10) == 0) | |
{ | |
p = strtok(line , " "); | |
p = strtok(NULL , " "); | |
//p now is the dns ip :) | |
//???? | |
} | |
} | |
strcpy(dns_servers[0] , "208.67.222.222"); | |
strcpy(dns_servers[1] , "208.67.220.220"); | |
} | |
/* | |
* This will convert www.google.com to 3www6google3com | |
* got it :) | |
* */ | |
void ChangetoDnsNameFormat(unsigned char* dns,unsigned char* host) | |
{ | |
int lock = 0 , i; | |
strcat((char*)host,"."); | |
for(i = 0 ; i < strlen((char*)host) ; i++) | |
{ | |
if(host[i]=='.') | |
{ | |
*dns++ = i-lock; | |
for(;lock<i;lock++) | |
{ | |
*dns++=host[lock]; | |
} | |
lock++; //or lock=i+1; | |
} | |
} | |
*dns++='\0'; | |
} |
Nice. Thanks mahn!
Thank you for this code.
On line 197 you allocate N bytes for answer[i].rdata and then on line 204 you write on the Nth byte of answer[i].rdata which is not allocated.
This works on PC linux but the code crashes on other platforms.
Can you explain the purpose of line 323-328 please?
@arunmir, those lines deal with dns compression techniques. See http://www.tcpipguide.com/free/t_DNSNameNotationandMessageCompressionTechnique-2.htm
what is the purpose of the line 323-327
maybe you have to add:
if ( (int)(offset - 1)>datalen){
reader++;
continue;
}
else your program can segfault
->datalen is the size of your buffer.
I Have try on TXT record and this thing seems to work
I' m think I' Wrong but...
@zoeurk How did you do with TXT record, there are no TXT mod ?
Is this available for Arduino, I want to use this on a card
I do a small sniffer with the raw socket (https://github.com/zoeurk/sniffer) and I see this code.
I supose that they are problem in this code because offset is sometime outside the buffer. (but I 'm not sure).
If you want to see how I can read buffer with this code, see in dns.c and dns.h
I found the problem: your code don't understand the difference between len = 210 (for exemple: in binary: 11010010) and a pointer...
How can we use this code such that it servers AAAA Ipv6 queries.
Nice, but found two problems to report: