-
-
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: