DNS Query
When we type a web url or domain in our browser a dns request is immediately send by our browser to a DNS server to get the IP address of that web address.
In winsock applications we achieve this by gethostbyname() and things are pretty simple. In this article we shall do this simple thing without the help of gethostbyname().
We shall be sending DNS queries and receive the reply and extract the ipv4 address of the specified hostname.
DNS packets
Before writing the code to perform dns query, its important to understand the structure of a dns packet.
RFC 1035 shows the structure of DNS message as follows:
+---------------------+ | Header | +---------------------+ | Question | the question for the name server +---------------------+ | Answer | RRs answering the question +---------------------+ | Authority | RRs pointing toward an authority +---------------------+ | Additional | RRs holding additional information +---------------------+
Pretty simple to understand that queries wont have the answer, authority and additional fields.Packets are of course UDP and DNS servers feel comfortable to operate on port 53. So the first thing is to send a query containing the hostname.
Next task is to receive the reply which is expected to contain the information we are expecting. DNS queries are used for a variety of purpose.
Apart from getting the ipv4 address of a host we also use DNS for getting the mail exchange/server of a specified domain and etc. All type of queries and response packets are build nearly on the same structure depicted above.
When a DNS server replies it sends the question as it is and along with that are a bunch of RR's or resource records. All RR's stand in a queue and certain fields of the header tell that how many of the RR's are answers, how many authority and how many additionals.
DNS Header
The following is the structure of the DNS header. When sending dns queries, we shall need to construct this header structure in our program.
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ID | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ |QR| Opcode |AA|TC|RD|RA| Z | RCODE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QDCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ANCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | NSCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | ARCOUNT | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Here is a detailed explanation of each of the fields in the header. Let go through each of the fields of the header:
ID - A 16 bit identifier assigned by the program that generated the query. This identifier is copied in the corresponding reply and can be used by the client to match up replies with queries. Generate a random 16 bit number for each request. QR - A one bit field that specifies whether this message is a query (0), or a response (1). Obviously, you should use 0 for your requests, and expect to see a 1 in the response you receive. OPCODE - A four bit field that specifies kind of query in this message. Use 0 to indicate a standard query. AA Authoritative Answer - this bit is only meaningful in responses, and specifies that the responding name server is an authority for the domain name in question section. You should use this bit to report whether or not the response you receive is authoritative. TC TrunCation - specifies that this message was truncated. For this project, you must exit and return an error if you receive a response that is truncated. RD Recursion Desired - this bit directs the name server to pursue the query recursively. You should use 1, representing that you desire recursion. RA Recursion Available - this be is set or cleared in a response, and denotes whether recursive query support is available in the name server. Recursive query support is optional. You must exit and return an error if you receive a response that indicates the server does not support recursion. Z Reserved for future use. Set this field to 0. RCODE Response code - this 4 bit field is set as part of responses. The values have the following interpretation: 0 : No error condition 1 : Format error - The name server was unable to interpret the query. 2 : Server failure - The name server was unable to process this query due to a problem with the name server. 3 : Name Error - Meaningful only for responses from an authoritative name server, this code signifies that the domain name referenced in the query does not exist. 4 : Not Implemented - The name server does not support the requested kind of query. 5 : Refused - The name server refuses to perform the specified operation for policy reasons. You should set this field to 0, and should assert an error if you receive a response indicating an error condition. You should treat 3 differently, as this represents the case where a requested name doesn’t exist. QDCOUNT - an unsigned 16 bit integer specifying the number of entries in the question section. Set this field to 1 to indicate one question. ANCOUNT - an unsigned 16 bit integer specifying the number of resource records in the answer section. Set this field to 0 , indicating you are not providing any answers. NSCOUNT - an unsigned 16 bit integer specifying the number of name server resource records in the authority records section. Set this field to 0, and should ignore any response entries in this section. ARCOUNT - an unsigned 16 bit integer specifying the number of resource records in the additional records section. Set this field to 0, and should ignore any response entries in this section.
For further reading checkup the relevant RFC.
A DNS response packet typically looks like this:
Header + Query + RR + RR + RR + RR + RR + RR ...
There is one query, followed by multiple resource record sections. Each resource record has some information about the queried domain name.
A Query structure looks like this
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / QNAME / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QTYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | QCLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Note : QNAME is a variable length field to fit the hostname
QCLASS should be 1 since we are on internet
QTYPE determines what you want to know ; ipv4 address , mx etc.
Resource Record(RR) field looks like this
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | | / / / NAME / | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TYPE | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | CLASS | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | TTL | | | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ | RDLENGTH | +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--| / RDATA / / / +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
Note again: NAME and RDATA are variable length field
Type field tells how RDATA relates to NAME. e.g. if TYPE is 1 then RDATA contains the ipv4 address of the NAME.
That's all about the structures we need.
DNS Compression Scheme
1. In DNS query and responses www.google.com is represented as 3www6google3com0. That is a number followed by that many characters until a period or the end of string.
2. And there is a compression scheme followed which is like this -> if google.com were to occur 10 times in the packet then it will written as google.com for the first time and after that a pointer will be placed at every next occurence of google.com to the position offset of the beginning of the first occurrence.
The starting of the dns header being the offset 0. For example if www.google.com is written starting at a offset of say 12 and some where later ns.google.com is to be written then it will be written as ns.16 which means point to offset 16 (where g of google is).
To implement this pointer technique 2 bytes are used where the first 2 bits are 1 and the rest 14 bits are the offset. So for 16 as offset the number you would need is 1100000000000000 + 1000.
Code
The structure for DNS header
typedef struct { 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 } DNS_HEADER;
Structure for the query ( we wont keep the name in this structure since size
is variable)
In the above structure the rd , tc , aa and opcode are in reverse order. This is due to the difference in the endian-ness of local machine and network. Local machines are little endian but network format is big endian. So fields have to be reversed in groups of 8 bit or 1 byte. Have a closer look at this in the structure.
typedef struct { unsigned short qtype; unsigned short qclass; } QUESTION;
Resource Record
typedef struct { unsigned short type; unsigned short _class; unsigned int ttl; unsigned short data_len; } R_DATA;
Once again name and rdata have been kept out.
These two structures will help
typedef struct { unsigned char *name; R_DATA *resource; unsigned char *rdata; } RES_RECORD; typedef struct { unsigned char *name; QUESTION *ques; } QUERY;
The working will be like
SOCKET s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); //UDP packet for DNS queries RES_RECORD answers[20],auth[20],addit[20]; //the replies from the DNS server sockaddr_in dest; dest.sin_family=AF_INET; dest.sin_port=htons(53); dest.sin_addr.s_addr=inet_addr(dns_servers[0]); //use the first dns server unsigned char buf[65536],*qname,*reader; DNS_HEADER *dns = NULL; QUESTION *qinfo = NULL; //Set the DNS structure to standard queries dns=(DNS_HEADER*)&buf; //set up the header dns->id = (unsigned short)htons(GetCurrentProcessId()); 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(DNS_HEADER)]; ChangetoDnsNameFormat(qname,host); qinfo =(QUESTION*)&buf[sizeof(DNS_HEADER) + (strlen((const char*)qname) + 1)]; //fill it qinfo->qtype = htons(1); //we are requesting the ipv4 address qinfo->qclass = htons(1); //its internet (lol) sendto(s,(char*)buf,sizeof(DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(QUESTION),0,(sockaddr*)&dest,sizeof(dest))==SOCKET_ERROR) int i=sizeof(dest); recvfrom (s,(char*)buf,65536,0,(sockaddr*)&dest,&i); dns=(DNS_HEADER*)buf; //move ahead of the dns header and the query field reader=&buf[sizeof(DNS_HEADER) + (strlen((const char*)qname)+1) + sizeof(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.nn",ntohs(dns->add_count)); //reading answers int stop=0; for(i=0;i<ntohs(dns->ans_count);i++) { answers[i].name=ReadName(reader,buf,stop); reader+=stop; answers[i].resource=(R_DATA*)(reader); reader+=sizeof(R_DATA); if(ntohs(answers[i].resource->type)==1) { answers[i].rdata=new unsigned char[ntohs(answers[i].resource->data_len)]; for(int 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)]=''; reader+=ntohs(answers[i].resource->data_len); } else { answers[i].rdata=ReadName(reader,buf,stop); reader+=stop; } }
and so on....
The IP address in the rdata section will be as numbers which must be converted
to a string and then to the dotted format using inet_ntoa like this
sockaddr_in a; long *p; p=(long*)addit[i].rdata; a.sin_addr.s_addr=(*p); printf("has IPv4 address : %s",inet_ntoa(a.sin_addr));
Authority resource records and Additional resource records need to be read just like Answer RRs. ChangetoDnsNameFormat(qname,host) function will convert the normal www.google.com in host to 3www6google3com0 and store the result in qname.
The ReadName() functions reads a NAME for query and RR blocks keeping in mind the compression strategy.
Fetching the DNS servers from configuration
Another function RetrieveDnsServersFromRegistry() in dns.cpp retrieves the DNS server IP stored on the system. On Windows, DNS servers are stored in the registry.
The registry key named:
HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces
contains the subkeys for the interfaces and each may contain a field called nameserver whose value will have the dns server fed by either you or stored dynamically in case of dialup connections.
Multiple DNS IP addresses may be present in one nameserver entry and then they are separated by a comma or simply a space.
Full working program source code:
//DNS Query Program //Header Files #include "winsock2.h" #include "windows.h" #include "stdio.h" #include "conio.h" #pragma comment(lib,"ws2_32.lib") //Winsock Library //List of DNS Servers registered on the system char dns_servers[10][100]; //Type field of Query and Answer #define T_A 1 /* host address */ #define T_NS 2 /* authoritative server */ #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 routing information */ //Function Declarations void ngethostbyname (unsigned char*); void ChangetoDnsNameFormat (unsigned char*,unsigned char*); unsigned char* ReadName (unsigned char*,unsigned char*,int*); void RetrieveDnsServersFromRegistry(void); unsigned char* PrepareDnsQueryPacket (unsigned char*); //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() //do you know what is int main() ? { unsigned char hostname[100]; RetrieveDnsServersFromRegistry(); WSADATA firstsock; printf("\nInitialising Winsock..."); if (WSAStartup(MAKEWORD(2,2),&firstsock) != 0) { printf("Failed. Error Code : %d",WSAGetLastError()); return 1; } printf("Initialised."); printf("\nEnter Hostname to Lookup : "); gets((char*)hostname); ngethostbyname(hostname); _getch(); return 0; } void ngethostbyname(unsigned char *host) { unsigned char buf[65536],*qname,*reader; int i , j , stop; SOCKET 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; s = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP); //UDP packet for DNS queries //Configure the sockaddress structure with information of DNS server dest.sin_family=AF_INET; dest.sin_port=htons(53); //Set the dns server if(strlen(dns_servers[0]) > 0) { //Use the dns server found on system dest.sin_addr.s_addr=inet_addr(dns_servers[0]); } else { //Use the open dns servers - 208.67.222.222 and 208.67.220.220 dest.sin_addr.s_addr=inet_addr("208.67.222.222"); } //Set the DNS structure to standard queries dns = (struct DNS_HEADER *)&buf; dns->id = (unsigned short) htons(GetCurrentProcessId()); 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(1); //we are requesting the ipv4 address 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))==SOCKET_ERROR) { printf("%d error",WSAGetLastError()); } printf("Sent"); i=sizeof(dest); printf("\nReceiving answer..."); if(recvfrom (s,(char*)buf,65536,0,(struct sockaddr*)&dest,&i)==SOCKET_ERROR) { printf("Failed. Error Code : %d",WSAGetLastError()); } printf("Received."); 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)); //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 for(i=0;i<ntohs(dns->ans_count);i++) { //printf("\nAnswer : %d",i+1); printf("Name : %s ",answers[i].name); if(ntohs(answers[i].resource->type)==1) //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 for(i=0;i<ntohs(dns->auth_count);i++) { //printf("\nAuthorities : %d",i+1); printf("Name : %s ",auth[i].name); if(ntohs(auth[i].resource->type)==2) { printf("has authoritative nameserver : %s",auth[i].rdata); } printf("\n"); } //print additional resource records for(i=0;i<ntohs(dns->add_count);i++) { //printf("\nAdditional : %d",i+1); 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); //working without ntohl printf("has IPv4 address : %s",inet_ntoa(a.sin_addr)); } printf("\n"); } return; } unsigned 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; } //Retrieve the DNS servers from the registry void RetrieveDnsServersFromRegistry() { HKEY hkey=0; char name[256]; char *path = "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces"; char *fullpath[256]; unsigned long s=sizeof(name); int dns_count=0 , err , i , j; HKEY inter; unsigned long count; //Open the registry folder RegOpenKeyEx(HKEY_LOCAL_MACHINE , path , 0 , KEY_READ , &hkey ); //how many interfaces RegQueryInfoKey(hkey, 0 , 0 , 0 , &count , 0 , 0 , 0 , 0 , 0 , 0 , 0 ); for(i=0 ; i<count ; i++) { s=256; //Get the interface subkey name RegEnumKeyEx(hkey , i , (char*)name , &s , 0 , 0 , 0 , 0 ); //Make the full path strcpy((char*)fullpath,path); strcat((char*)fullpath,"\\"); strcat((char*)fullpath,name); //Open the full path name RegOpenKeyEx(HKEY_LOCAL_MACHINE , (const char*)fullpath , 0 , KEY_READ , &inter ); //Extract the value in Nameserver field s=256; err=RegQueryValueEx(inter , "NameServer" , 0 , 0 , (unsigned char*)name , &s ); if(err==ERROR_SUCCESS && strlen(name)>0) { strcpy(dns_servers[dns_count++],name); } } for(i=0;i<dns_count;i++) { for(j=0;j<strlen(dns_servers[i]);j++) { if(dns_servers[i][j]==',' || dns_servers[i][j]==' ') { strcpy(dns_servers[dns_count++] , dns_servers[i]+j+1); dns_servers[i][j] = 0; } } } printf("\nThe following DNS Servers were found on your system..."); for(i=0;i<dns_count;i++) { printf("\n%d) %s",i+1,dns_servers[i]); } } //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<(int)strlen((char*)host) ; i++) { if(host[i]=='.') { *dns++=i-lock; for(;lock<i;lock++) { *dns++=host[lock]; } lock++; //or lock=i+1; } } *dns++='\0'; }
Output
Here is a sample output from the above program
The following DNS Servers were found on your system... 1) 208.67.222.222 2) 208.67.220.220 Initialising Winsock...Initialised. Enter Hostname to Lookup : www.google.com Sending Packet...Sent Receiving answer...Received. The response contains : 1 Questions. 6 Answers. 4 Authoritative Servers. 4 Additional records. Name : www.google.com has alias name : www.l.google.com Name : www.l.google.com has IPv4 address : 74.125.235.16 Name : www.l.google.com has IPv4 address : 74.125.235.17 Name : www.l.google.com has IPv4 address : 74.125.235.18 Name : www.l.google.com has IPv4 address : 74.125.235.19 Name : www.l.google.com has IPv4 address : 74.125.235.20 Name : google.com has authoritative nameserver : ns4.google.com Name : google.com has authoritative nameserver : ns1.google.com Name : google.com has authoritative nameserver : ns3.google.com Name : google.com has authoritative nameserver : ns2.google.com Name : ns1.google.com has IPv4 address : 216.239.32.10 Name : ns2.google.com has IPv4 address : 216.239.34.10 Name : ns3.google.com has IPv4 address : 216.239.36.10 Name : ns4.google.com has IPv4 address : 216.239.38.10
If you get error the error message : "Sending Packet…10049 error" then check if there are any DNS servers in your Network configuration and that the above program is able to list them like this :
The following DNS Servers were found on your system... 1) 208.67.222.222 2) 208.67.220.220
If no DNS servers are found then error 10049 will come.
Update
The code has not been fixed from the 10049 winsock error. If no dns servers are found then the open dns server 208.67.222.222 will be used.
Download Source
https://www.binarytides.com/blog_files/dns_query/dns_query.cpp
For Linux version of the code check :
https://www.binarytides.com/blog/dns-query-code-in-c-with-linux-sockets/
A user named "dfelippa" read this post on codeproject and suggested and made some improvements to the code which include :
1. Better memory management
2. Support for MX Query
He modified the code and put up the new code at:
http://www.infologika.com.br/public/dnsquery_main.cpp
Check it out!
I am looking to run this code in vs code but it always gives me an undefined __imp_ntohs and wont compile do you know how to fix this?
Is it possible that in various places, you have an out of bound bug (and you were lucky not to get an access violation):
For example, in line 212:
answers[i].rdata = (unsigned char*)malloc(ntohs(answers[i].resource->data_len));
for(j=0 ; jdata_len) ; j++)
answers[i].rdata[j]=reader[j];
answers[i].rdata[ntohs(answers[i].resource->data_len)] = ‘\0’;
You are allocating a buffer with data_len length, but you access data_len + 1
in line 163 why is +1 added. I am not able to understand it. Pls can anyone help me. Thanks in advance
how was the buffer size calculated?
Why do you attempt to support domain names with Unicode characters? (>= 192). Section 3.1 of RFC-1035 clearly states: “assuming an ASCII character set, and a high order zero bit”.
I wrote a windows version independent function a while ago to retrieve the dns server(s), perhaps it would be useful to someone (m_slWinsockDNS is type CStringList):
#include
int CSMTPClient::DiscoverWinsockDNSServers()
{
int iCount = 0;
GUID guid = SVCID_NAMESERVER_UDP;
WSAQUERYSET qs = {0};
qs.dwSize = sizeof(WSAQUERYSET);
qs.dwNameSpace = NS_DNS;
qs.lpServiceClassId = &guid;
HANDLE hLookup;
int nRet = WSALookupServiceBegin(&qs, LUP_RETURN_NAME|LUP_RETURN_ADDR, &hLookup);
if(nRet == SOCKET_ERROR)
{
int iError = WSAGetLastError();
ReportError(_T(“Error obtaining DNS Server information (%d) %s”),
iError, GetWin32ErrorText(iError));
return – 1;
}
// Loop through the services
DWORD dwResultLen = 1024;
unsigned char* pResultBuf = new unsigned char[dwResultLen];
while(TRUE)
{
nRet = WSALookupServiceNext(hLookup,
LUP_RETURN_NAME|LUP_RETURN_ADDR,
&dwResultLen, (WSAQUERYSET*)pResultBuf);
if(nRet == SOCKET_ERROR)
{
// Buffer too small?
if(WSAGetLastError() == WSAEFAULT)
{
delete pResultBuf;
pResultBuf = new unsigned char[dwResultLen];
continue;
}
break;
}
WSAQUERYSET* pqs = (LPWSAQUERYSET)pResultBuf;
CSADDR_INFO* pcsa = pqs->lpcsaBuffer;
// Loop through the CSADDR_INFO array
for(int x = 0; x dwNumberOfCsAddrs; x ++)
{
// Get string equivalent for address
TCHAR strAddrBuf[256] = {_T(”)};
DWORD dwLen = sizeof(strAddrBuf);
nRet = WSAAddressToString(pcsa->RemoteAddr.lpSockaddr,
pcsa->RemoteAddr.iSockaddrLength, NULL, strAddrBuf, &dwLen);
if(nRet == SOCKET_ERROR)
break;
// Remove the port number (‘:#’)
CString strResult = strAddrBuf;
CString strIP = _T(“”);
int i = strResult.Find(_T(‘:’));
if(i == -1)
strIP = strResult;
else
strIP = strResult.Left(i);
// Check for the duplicates before adding it
if(m_slWinsockDNS.Find(strIP) == NULL)
{
m_slWinsockDNS.AddTail(strIP);
iCount ++;
}
pcsa++;
}
}
// Clean up
WSALookupServiceEnd(hLookup);
delete pResultBuf;
return iCount;
}
Thanks allot man, real nice work.
[code language=”cpp”]
/*
* this will convert http://www.google.com
* to 3www6google3com;
* got it :)
*/
void ChangetoDnsNameFormat(byte_t* szDns, byte_t* szHost)
{
int src = 0, des = 0, len = 0; // Some positions.
szDns[len] = 0;
while (szHost[src] != ‘\0’) {
if (szHost[src] == ‘.’) {
len += szDns[len] + 1;
szDns[len] = 0;
des = len;
++src; // Ignore ‘.’
} else {
szDns[++des] = szHost[src++];
++szDns[len];
}
}
szDns[++des] = ‘\0’;
}
[/code]
Hey why does the answers differ if i use nslookup and then ur code, they show diffrent result. can u please explain.
the answer might differ due to a number of reasons. post the output here.
Any tips on address format for reverse (PTR) lookup?
I seem to have hit a wall.
I’ve found the answer.
To do a reverse lookup (PTR) on an IP Address:
1. First reverse the IP address (from say 1.23.45.201 to 201.45.23.1 and append “.in-addr.arpa” to it).
Will now be 201.45.23.1.in-addr.arpa.
2. Call the ChangetoDnsNameFormat() function on it.
3. Add a case in the print answers section for 12 (T_PTR) and handle it the same as T_CNAME
(i.e. look at the answers[i].rdata.c_str() variable).
Happy days.
why do we represent http://www.google.com as 3www6google3com0?
This is because : 3www6google3com0
look this 3www.7gooogle4gato3com0
The number is the number of characteres after the point. You replace the point with the number of characteres of the text segments.
Sorry for my english!
Enjoy it.
Hello.
Nice code.
The reversed search is not implemented I think and I can see some interpretation issues when running
Can you fix that?
all the best :)
Hey, very nice article.I have a question for you , how do you modify the code to run on linux instead of windows, for example the WSADATA structures are not recognized in linux and a lot of others. With a simple compile in linux you get lots of this “…” was not declared in this scope.I would love if you could give me an answer.Thank you
Hi guy, if you pay attention, in the bottom of the article, there´s a link to the linux code: https://www.binarytides.com/blog/dns-query-code-in-c-with-linux-sockets/
Regards.
Yes i already found the link , although thank you
Hi my friend, i was looking for this information a long time. I wann say thank you for your great help. There´s no so many people with your knowledge who want to share it. Sorry for my english.
Regards!
Thanks from Argentina!.
It is a DNS resolver and along with it i want to have a code for the DNS name server. Please provide me some tips for the same…
Check out some open source dns server softwares.
http://en.wikipedia.org/wiki/Comparison_of_DNS_server_software
http://www.isc.org/software/bind
Hey, first please I need some help with ReadName(unsigned char* reader,unsigned char* buffer,int* count), I didn’t understand how it works, what are the count, jumped, …
offset = (*reader)*256 + *(reader+1) – 49152; //49152 = 11000000 00000000
what are we doing in this instruction.
Second if I want to make search in a local txt file that contains the RRs in a well structured form, how can i achieve this?
thank you in advance :)
Why you change byte ordering in header structure? Has it relation to endianess?
Yes, the bits are in reverse order per byte. So for every 8 bits the structure has to be in reverse order.
endianess rise some issues on PPC and those pragmas and “:” uses for defining no of bits for every member also create some problems.
nice tut
really excellent tutorial regarding DNS client. Since I don’t find any tutorial regarding DNS, it is a Oasis ….
Good job done!!!
Hi, I have a question on you. Can you tell me what is the format of response? Like your sendto query, you have header question and query. How about response from DNS? How can I find answers at which position or which bit?
The following DNS Servers were found on your system…
Initialising Winsock…Initialised.
Enter Hostname to Lookup : http://www.google.com
Sending Packet…10049 error
————————————————-
The following DNS Servers were found on your system…
Initialising Winsock…Initialised.
Enter Hostname to Lookup : http://www.gmail.com
Sending Packet…10049 error
————————————————-
The following DNS Servers were found on your system…
Initialising Winsock…Initialised.
Enter Hostname to Lookup : google.com
Sending Packet…10049 error
————————————————-
I tried all the possibility, yet it does not work. any clue? Am running this code in XP.
Error 10049 occurs because no dns servers are found in the network settings.
Now the code is fixed. It uses the open dns servers if no dns servers are found, hence the error 10049 should not come.
Well i got it working but did not work out where i went wrong.
The results i’m finding using this new tool is quite interesting and my service provider BT Home is returnig lots of fake DNS results back which presents a security threat because the DNS servers are allowing the malaware servers to hide all over the place and from what i can work out the ISPs see corporations like double-click as being top customers and to hell with the little people like you and me.
A quick and dirty way to test the IP returned in the byte array from the DNS server is to simply strip out the last 4 bytes and put the dots in and if you need to fake the reply then you will need to copy bytes 0,1 from the original request from the client.
Did you know Firefox now sends off DNS requests for any HREF, Atags in the HTML ? Looks like a lot of people have moved over to DNS tracking in an effort to avoid anti-spam software.
Code is incomplete, some lines are missing.
Just see how the last line (line 215) looks like…
the code has been tested to run fine.
what error are you getting on compiling/running the code.
Does anyone know how to send a DNS responce back to the browser ?
I have some code that listens for UDP packets on port 53 and sends the message off to the DNS server and i get a valid responce back and simply want to pass the byte array back to the browser.
i’ve tryed sending the data bask using the sockets remote address (EndPoint) as a UDP but the browser simply fires the request back off again and i’ve test the code using a normal socket and it works fine.
should be a ten second job but i’ve been at it for days and all because BT Home is now sending out fake DNS results from it’s UK servers.
Very good article!
I have a question, how can i implement a timeout on receive answer?
Use select() before recvfrom()
Hi
I wanted to use the code in a commercial product and would like your permission. The product is open source and licensed under BSD license.
Sorry, last comment got munged a bit…you need to #include <arpa/inet.h>
Just a note on the Linux code…unless you #include , this code segfaults…for some reason, inet_ntoa() returns a bogus int instead of a string. Other than that, it works great…Thanks!
@@ron
Hi there,
Sory for my english :) Program run succeded, but IPs are not correct i think. I mean it is different than IPs given from windows ‘ping’ command in command prompt. Why is that?
IP address given by this program might be different from what ping shows. For example http://www.google.com has many ips.
It is sufficient to check whether the ip shown by the program takes you to the correct website/domain or not.
Hi there,
I am trying to get a string representation of the all message sent over the socket. I am not confident with C, how can I print it?
Thanks for the code btw.
Hello, doesn’t appear anything on my command prompt when i run the program..
Hi, i don’t understand why but all the printf’s text do not appear on the command prompt.. Why is that? :S
Thanks in advance!
hey there !
how are you , i want to learn about the “receiving answer” too
georgios larkou =p
Hey i have a question. I run this code with visual c++ but it does not display the host’s ip as if it is stuck in a loop (the cursor keeps blinking at “receiving answer..”) I have tried many hosts simplest of all, http://www.google.com but the console never shows any result. Please help me with this problem…its really urgent. Thanx
You should only provide the domain name , that is : “www.google.com”
Not http://www.google.com
@Nicolas Goles – Because of the little endian machine architecture. In memory the bits are in the reverse order of what is in the structure. So to make the memory representation look like RFC 1035 the structure variables should be in reverse order.
Hey i got a question for you, why is the declaration of the DNS header like that, I mean all the variables that you asignated with the :bit ( unsigned char rd :1; ) are in the inverse order of the proposed in RFC1035
Yes, all the variables are bytewise reversed or reversed in groups of 8 bits. This is done because of the difference in the endianness of the client and the network. Client machines are generally little endian whereas the network is big endian.
For further reading on endianness , check :
http://en.wikipedia.org/wiki/Endianness
Hey very cool article. It helped me a lot writing a mx record reader.
thanks!