DNS Query Code in C with winsock and linux sockets

Introduction

Its no new fact that when we type a web address 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.

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 ofcourse 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.

This is the structure of a DNS header :


+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                     ID                        |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|QR| Opcode    |AA|TC|RD|RA| Z      |  RCODE    |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                   QDCOUNT                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                   ANCOUNT                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                   NSCOUNT                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|                   ARCOUNT                     |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

ID :the identifier
QDCOUNT : how many question are there in the packet.
ANCOUNT : how many answers in the RR queue.
NSCOUNT : Authority RR count
ARCOUNT : Additional Count

for explanation of the other fields lookup the RFC

A DNS packet typically looks like this Header-Query-RR-RR-RR-RR-RR-RR……..……..

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.

Now some more food for thought:

1. In DNS query and responses www.google.com is represented as
3www6google3com0 <<hope you got it :)
if u didnt get that send me a mail ;)

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 occurence.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 u wud need is 1100000000000000 + 1000 <<thats
it.

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)

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,&amp;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 RR’s and Additional RR’s need to be read just like we read Answer
RR’s 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.

Another function RetrieveDnsServersFromRegistry() in dns.cpp retrieves the DNS
server IP’s stored in the system. DNS servers are stored in the registry.
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’s may be present in
one nameserver entry and then they are separated by a comma or simply a space.

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 it up at :

http://www.infologika.com.br/public/dnsquery_main.cpp

Check it out!

Source Code :
Windows :

//DNS Query Program
//Author : Prasshhant Pugalia (prasshhant.p@gmail.com)
//Dated : 26/2/2007

//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 Prototypes
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];

WSADATA firstsock;

RetrieveDnsServersFromRegistry();

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

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(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.nn",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)] = '';

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)]='';
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]='';

//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]=''; //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]=''; //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++='';
}

Linux :

//DNS Query Program on Linux
//Author : Prasshhant Pugalia (prasshhant.p@gmail.com)
//Dated : 29/4/2009

//Header Files
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>

//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 Prototypes
void ngethostbyname (unsigned char*);
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()
{
unsigned char hostname[100];

//Get the DNS servers from the resolv.conf file
get_dns_servers();
//Get the hostname
printf("nEnter Hostname to Lookup : ");
gets((char*)hostname);
//Now get the ip of this hostname
ngethostbyname(hostname);

return 0;
}

void ngethostbyname(unsigned char *host)
{
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;

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(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)) == 0)
{
printf("Error sending socket");
}
printf("Sent");

i = sizeof dest;
printf("nReceiving answer...");
if(recvfrom (s,(char*)buf,65536,0,(struct sockaddr*)&dest,&i) == 0)
{
printf("Failed. Error Code ");
}
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.nn",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)] = '';

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)]='';
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]='';

//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]=''; //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]=''; //remove the last dot
return name;
}

//Retrieve the DNS servers from the registry
void get_dns_servers()
{
/*
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]);
}
*/
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 < (int)strlen((char*)host) ; i++) {
if(host[i]=='.') {
*dns++=i-lock;
for(;lock<i;lock++) {
*dns++=host[lock];
}
lock++; //or lock=i+1;
}
}
*dns++='';
}

Popularity: 54% [?]

13 Responses to “DNS Query Code in C with winsock and linux sockets”

  1. Hey very cool article. It helped me a lot writing a mx record reader.
    thanks!

  2. Nicolas Goles on May 30th, 2009 at 11:53 pm

    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

  3. @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.

  4. Sanarita Holmes on October 11th, 2009 at 3:51 pm

    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

  5. hey there !
    how are you , i want to learn about the “receiving answer” too
    georgios larkou =p

  6. 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!

  7. Hello, doesn’t appear anything on my command prompt when i run the program..

  8. Alessandro I. on May 20th, 2010 at 2:54 pm

    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.

  9. 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?

  10. 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

  11. Sorry, last comment got munged a bit…you need to #include <arpa/inet.h>

  12. 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.

  13. Very good article!
    I have a question, how can i implement a timeout on receive answer?

Leave a Reply