How C + + implements DNS domain name resolution _c language

Source: Internet
Author: User
Tags filetime sprintf strtok nslookup htons

I. Overview

Now to deal with DNS domain name resolution, in fact, this is a previous article in C + + to implement the remaining problems in Ping, to do the work is the process of the ping Red Line part:

CMD under the Domain Name Resolution command is Nslookup, such as "Nslookup www.baidu.com" The results are as follows:

In which, the address returned is www.baidu.com corresponding IP addresses, this may have multiple

The alias refers to aliases, which means that www.baidu.com is the alias of the www.a.shifen.com, and www.a.shifen.com is the www.baidu.com canonical name (Canonical name,cname). Specific reference RFC1035 3.2.2 & Wikipedia

Second, the implementation of the results preview

Let's see what it turns out to be.

Input: Domain name string

Output: IP list, CNAME list, DNS query time

Third, related technology

3.1, UDP or TCP? (RFC1035 4.2)

Udp:dns queries and replies with low overhead high-performance UDP with a port number of 53.

TCP: When the secondary DNS server pulls the latest data from the primary DNS server, it uses a reliable TCP transport and the port number is 53.

We do DNS queries here using the udp,53 port.

3.2, DNS query/reply packet Head resolution (RFC1035 4.1.1)

Focus on some of our concerns:

ID (16bits): identifier, typically filled in the identifier of this process

QR (1bits): Flag bit, query package is 0, reply package is 1

Opcode (4bits): The type of query, the standard query for 0

Qdcount (16bits): Number of question fields in DNS query/reply package data

Ancount (16bits): Number of answer fields in DNS query/reply package data

3.2. DNS query/Reply packet Data partial parsing (RFC1035 4.1.2 & 4.1.3)

The data portion of the query/reply package is qdcount question field, ancount answer field ....

For any field, the format is as follows:

Name (indefinite length): Domain name, this part of the format is more complex, the back of the individual said.

Type (16bits): Query type/reply package rdata type, such as type=1 for host IP address, type=5 for CNAME, see RFC1035 3.2.2

Class (16bits): classes, generally class=1 for the internet, see RFC1035 3.2.4

TTL (32bits, reply-only package): Time to Live

Rdlength (16bits, only reply package): Rdata part of the number of bytes

RDATA (indefinite length, only reply package): Resource data, the specific format depends on type and CLASS, such as type=1, Class=1, RDATA four-byte IP address

3.3, name resolution & message Compression

3.3.1, General format (RFC1035 4.1.2)

Label content Length (1 bytes) + label content, with label content length 0 as Terminator of name, for example:

3.3.2, message compression format (RFC1035 4.1.4)

Message compression is indicated if the top two digits of the label content length are 11.

At this point, the label content length is 1 bytes + 1 bytes followed by a total of 16 digits, and the latter 14 digits represent the offset (Byte) relative to the DNS packet start address, for example:

In the example above, the DNS packet starting address is 0x0000,c0 13 binary to 11000000 00010003, that is, the jump offset is 0x13 byte, the corresponding data is 6f 6d 00.

In RFC1035, the supported message compression rules are:

① a label sequence ending with the content length 0

② offset pointer

③ tag sequence + offset pointer

That is, the message compression for name requires that the offset pointer be at the end of name, and that there are multiple offset pointers (offset pointer sequences) that are not supported at the same level.

However, message compression for name supports nested offset pointers, where the offset to which the pointer is pointing is still data ending with an offset pointer

Four, code implementation

#pragma once//you need to import library ws2_32.lib, which may not be the same under different Ides//#pragma comment (lib, "Ws2_32.lib") #include <windows.h> #inclu De <string> #include <vector> #define MAX_DOMAINNAME_LEN 255 #define DNS_PORT #define DNS_TYPE_SIZE 2 # 
Define Dns_class_size 2 #define DNS_TTL_SIZE 4 #define DNS_DATALEN_SIZE 2 #define DNS_TYPE_A 0x0001//1 A host Address #define Dns_type_cname 0x0005//5 The canonical name for an alias #define Dns_packet_max_size (sizeof (dnsheader) + max_d Omainname_len + dns_type_size + dns_class_size) struct Dnsheader {USHORT ustransid;//identifier USHORT usflags;//various flag bits USH ORT Usquestioncount; The number of question fields USHORT usanswercount; The number of answer fields USHORT usauthoritycount; The number of authority fields USHORT Usadditionalcount;

Number of additional fields};
 Class Cdnslookup {public:cdnslookup ();

 ~cdnslookup (); BOOL DNSLookup (ULONG uldnsserverip, Char *szdomainname, std::vector<ulong> *pveculiplist = NULL, std::vector< Std::string> *pvecstrcnamelist = NULL, ULONG ultimeout = 1000, ULONG *pultimespent = NULL); BOOL DNSLookup (ULONG uldnsserverip, Char *szdomainname, std::vector<std::string> *pvecstriplist = NULL, std::

Vector<std::string> *pvecstrcnamelist = null, ULONG ultimeout = 1000, ULONG *pultimespent = null);
 Private:bool Init ();
 BOOL UnInit (); BOOL Dnslookupcore (ULONG uldnsserverip, Char *szdomainname, std::vector<ulong> *pveculiplist, std::vector<
 Std::string> *pvecstrcnamelist, ULONG ultimeout, ULONG *pultimespent);
 BOOL senddnsrequest (sockaddr_in sockaddrdnsserver, char *szdomainname); BOOL Recvdnsresponse (sockaddr_in sockaddrdnsserver, ULONG ultimeout, std::vector<ulong> *pveculIPList, std::
 Vector<std::string> *pvecstrcnamelist, ULONG *pultimespent);
 BOOL Encodedotstr (Char *szdotstr, char *szencodedstr, USHORT nencodedstrsize); BOOL Decodedotstr (char *szencodedstr, USHORT *pusencodedstrlen, Char *szdotstr, USHORT ndotstrsize, char *
 Szpacketstartpos = NULL);

ULONG gettickcountcalibrate (); prIvate:bool M_bisinitok;
 SOCKET M_sock;
 Wsaevent m_event;
 USHORT M_uscurrentprocid;
Char *m_szdnspacket;
 }; 
 [DNSLookup.h] #include "DNSLookup.h" #include <stdio.h> #include <string.h> cdnslookup::cdnslookup ():

M_bisinitok (FALSE), M_sock (Invalid_socket), M_szdnspacket (NULL) {M_bisinitok = Init ();}

Cdnslookup::~cdnslookup () {uninit ();} BOOL cdnslookup::D nslookup (ULONG uldnsserverip, Char *szdomainname, std::vector<ulong> *pveculiplist, std:: Vector<std::string> *pvecstrcnamelist, ULONG ultimeout, ULONG *pultimespent) {return Dnslookupcore (
Uldnsserverip, Szdomainname, Pveculiplist, Pvecstrcnamelist, Ultimeout, pultimespent); BOOL cdnslookup::D nslookup (ULONG uldnsserverip, Char *szdomainname, std::vector<std::string> *pvecstriplist, Std::vector<std::string> *pvecstrcnamelist, ULONG ultimeout, ULONG *pultimespent) {std::vector<ulong> *
 Pveculiplist = NULL; if (pvecstriplist!= NULL) {std::vector<ulong> Veculiplist;
 Pveculiplist = &veculIPList;

 BOOL BRet = Dnslookupcore (Uldnsserverip, Szdomainname, Pveculiplist, Pvecstrcnamelist, Ultimeout, pulTimeSpent);
  if (BRet) {pvecstriplist->clear ();
  Char szip[16] = {' I '}; for (Std::vector<ulong>::iterator iter = Pveculiplist->begin (), ITER!= pveculiplist->end (); ++iter) {by
   TE *pbyipsegment = (byte*) (& (*iter));
   sprintf_s (Szip, "%d.%d.%d.%d", Pbyipsegment[0], pbyipsegment[1], pbyipsegment[2], pbyipsegment[3]);
   sprintf (Szip, "%d.%d.%d.%d", Pbyipsegment[0], pbyipsegment[1], pbyipsegment[2], pbyipsegment[3]);
  Pvecstriplist->push_back (Szip);
} return BRet;
 BOOL Cdnslookup::init () {wsadata wsadata;
 if (WSAStartup (Makeword (2, 2), &wsadata) = = Socket_error) {return FALSE;
 } if ((M_sock = socket (af_inet, SOCK_DGRAM, 0)) = = Invalid_socket) {return FALSE;
 } m_event = Wsacreateevent ();

 WSAEventSelect (M_sock, m_event, Fd_read); M_szdnspacket = new (Std::nothrow) cHar[dns_packet_max_size];
 if (M_szdnspacket = = NULL) {return FALSE;

 } m_uscurrentprocid = (USHORT) getcurrentprocessid ();
return TRUE;

 BOOL Cdnslookup::uninit () {wsacleanup ();
 if (M_szdnspacket!= NULL) {delete [] m_szdnspacket;
return TRUE; BOOL cdnslookup::D nslookupcore (ULONG uldnsserverip, Char *szdomainname, std::vector<ulong> *pveculiplist, std: :vector<std::string> *pvecstrcnamelist, ULONG ultimeout, ULONG *pultimespent) {if (M_bisinitok = FALSE | | szDomai
 Nname = = NULL) {return FALSE; 
 }//Configure Socket sockaddr_in sockaddrdnsserver; 
 sockaddrdnsserver.sin_family = af_inet;
 SOCKADDRDNSSERVER.SIN_ADDR.S_ADDR = Uldnsserverip;

 Sockaddrdnsserver.sin_port = htons (Dns_port); DNS Query and parse if (! Senddnsrequest (Sockaddrdnsserver, szdomainname) | | !
 Recvdnsresponse (Sockaddrdnsserver, Ultimeout, Pveculiplist, Pvecstrcnamelist, pultimespent)) {return FALSE;
return TRUE; BOOL cdnslookup::senddnsrequest (sockaddr_in sockaddrdnsserveR, Char *szdomainname) {char *pwritednspacket = m_szdnspacket; 

 memset (pwritednspacket, 0, dns_packet_max_size);
 Populate DNS Query message head dnsheader *pdnsheader = (dnsheader*) pwritednspacket;
 Pdnsheader->ustransid = M_uscurrentprocid;
 Pdnsheader->usflags = htons (0x0100);
 Pdnsheader->usquestioncount = htons (0x0001);
 Pdnsheader->usanswercount = 0x0000;
 Pdnsheader->usauthoritycount = 0x0000;

 Pdnsheader->usadditionalcount = 0x0000;
 Set DNS Query message content USHORT Usqtype = htons (0x0001);
 USHORT Usqclass = htons (0x0001);
 USHORT Ndomainnamelen = strlen (szdomainname);
 Char *szencodeddomainname = (char *) malloc (Ndomainnamelen + 2);
 if (Szencodeddomainname = = NULL) {return FALSE; } if (!
 Encodedotstr (Szdomainname, Szencodeddomainname, Ndomainnamelen + 2)) {return FALSE;
 }//Populate DNS Query message content USHORT Nencodeddomainnamelen = strlen (szencodeddomainname) + 1;
 memcpy (Pwritednspacket + + sizeof (dnsheader), Szencodeddomainname, Nencodeddomainnamelen); memcpy (Pwritednspacket + + nenCodeddomainnamelen, (char*) (&usqtype), dns_type_size);
 memcpy (Pwritednspacket + + dns_type_size, (char*) (&usqclass), dns_class_size);

 Free (szencodeddomainname);
 Send DNS Query message USHORT ndnspacketsize = sizeof (Dnsheader) + Nencodeddomainnamelen + dns_type_size + dns_class_size; if (SendTo (M_sock, M_szdnspacket, Ndnspacketsize, 0, (sockaddr*) &sockaddrdnsserver, sizeof (sockaddrdnsserver)) = =
 SOCKET_ERROR) {return FALSE;
return TRUE; BOOL Cdnslookup::recvdnsresponse (sockaddr_in sockaddrdnsserver, ULONG ultimeout, std::vector<ulong> * Pveculiplist, std::vector<std::string> *pvecstrcnamelist, ULONG *pultimespent) {ULONG Ulsendtimestamp =

 Gettickcountcalibrate ();
 if (pveculiplist!= NULL) {pveculiplist->clear ();
 } if (Pvecstrcnamelist!= NULL) {pvecstrcnamelist->clear ();
 } char recvbuf[1024] = {'; '};
 Char szdotname[128] = {' I '};

 USHORT Nencodednamelen = 0; while (TRUE) {if (wsawaitformultipleevents 1, &m_event, FALSE, 100, FALSE)!= wsa_wait_timeout) {wsanetworkevents netevent;

   Wsaenumnetworkevents (M_sock, m_event, &netevent);
    if (Netevent.lnetworkevents & fd_read) {ULONG ulrecvtimestamp = Gettickcountcalibrate ();

    int nsockaddrdestsize = sizeof (sockaddrdnsserver); Receive Response message if (Recvfrom (M_sock, RECVBUF, 1024, 0, (struct sockaddr*) &sockaddrdnsserver, &nsockaddrdestsize)!= S
     Ocket_error) {Dnsheader *pdnsheader = (dnsheader*) recvbuf;
     USHORT usquestioncount = 0;

     USHORT usanswercount = 0; if (Pdnsheader->ustransid = = M_uscurrentprocid && (Ntohs (pdnsheader->usflags) & 0xfb7f) = = 0x8100
      RFC1035 4.1.1 (Header section format) && (Usquestioncount = Ntohs (pdnsheader->usquestioncount)) >= 0 && (Usanswercount = Ntohs (pdnsheader->usanswercount)) > 0) {char *pdnsdata = recvbuf + siz

      EOF (Dnsheader); Parse question field for (int q = 0; q!= usquestioncount; ++q)
      {if (!
       Decodedotstr (Pdnsdata, &nencodednamelen, Szdotname, sizeof (Szdotname))) {return FALSE;
      } Pdnsdata + = (Nencodednamelen + dns_type_size + dns_class_size); }//Parse answer field for (int a = 0; a!= usanswercount; ++a) {if (!
       Decodedotstr (Pdnsdata, &nencodednamelen, Szdotname, sizeof (szdotname), recvbuf)) {return FALSE;

       } Pdnsdata + = Nencodednamelen;
       USHORT Usanswertype = Ntohs (* (ushort*) (Pdnsdata));
       USHORT Usanswerclass = Ntohs (* (ushort*) (Pdnsdata + dns_type_size));
       ULONG Usanswerttl = Ntohl (* (ulong*) (Pdnsdata + dns_type_size + dns_class_size));
       USHORT Usanswerdatalen = Ntohs (* (ushort*) (Pdnsdata + dns_type_size + dns_class_size + dns_ttl_size));

       Pdnsdata + = (dns_type_size + dns_class_size + dns_ttl_size + dns_datalen_size); if (Usanswertype = = dns_type_a && pveculiplist!= NULL) {ULONG ulip = * (ulong*) (PDNSDATA);
       Pveculiplist->push_back (Ulip); else if (Usanswertype = = Dns_type_cname && pvecstrcnamelist!= NULL) {if (!)
        Decodedotstr (Pdnsdata, &nencodednamelen, Szdotname, sizeof (szdotname), recvbuf)) {return FALSE;
       } pvecstrcnamelist->push_back (Szdotname);
      } Pdnsdata + = (Usanswerdatalen);
      The time used to compute the DNS query if (pultimespent!= NULL) {*pultimespent = Ulrecvtimestamp-ulsendtimestamp;
     } break; }}//Timeout exit if (Gettickcountcalibrate ()-ulsendtimestamp > Ultimeout) {*pultimespent = Ultimeou
   T + 1;
  return FALSE;
} return TRUE; } * * Convert "www.baidu.com" to "\x03www\x05baidu\x03com" * 0x0000 (+) (6f 6d FF * /BOOL Cdnslookup::encodedotstr (char *szdotstr, char *szencodedstr, USHORT nencodedstrsize) {USHORT Ndotstrlen = strlen (

 SZDOTSTR); if (szdotstr = NULL | | szencodeDstr = = NULL | |
 Nencodedstrsize < Ndotstrlen + 2) {return FALSE;
 } char *szdotstrcopy = new Char[ndotstrlen + 1];
 strcpy_s (szdotstrcopy, Ndotstrlen + 1, szdotstr);

 strcpy (Szdotstrcopy, SZDOTSTR);
 char *pnexttoken = NULL;
 Char *plabel = strtok_s (Szdotstrcopy, ".", &pnexttoken);
 Char *plabel = strtok (Szdotstrcopy, ".");
 USHORT Nlabellen = 0;
 USHORT Nencodedstrlen = 0; while (Plabel!= NULL) {if (Nlabellen = strlen (plabel))!= 0) {//sprintf_s (szencodedstr + Nencodedstrlen, Nenco
   Dedstrsize-nencodedstrlen, "%c%s", Nlabellen, Plabel);
   sprintf (Szencodedstr + Nencodedstrlen, "%c%s", Nlabellen, Plabel);
  Nencodedstrlen + + (Nlabellen + 1);
  }//plabel = strtok_s (NULL, ".", &pnexttoken);
 Plabel = Strtok (NULL, ".");
 
 } delete [] szdotstrcopy;
return TRUE; } * * Convert "\x03www\x05baidu\x03com\x00" to "www.baidu.com" * 0x0000 (+) (6f 6d F) F * Convert "\x03www\x05baidu\xc0\x13" to "www.baidu.com" * 0x0Slookup::D ecodedotstr (char *szencodedstr, USHORT *pusencodedstrlen, Char *szdotstr, USHORT ndotstrsize, char *
 Szpacketstartpos) {if (Szencodedstr = null | | | pusencodedstrlen = NULL | | | szdotstr = = NULL) {return FALSE;
 } char *pdecodepos = SZENCODEDSTR;
 USHORT Usplainstrlen = 0; 
 BYTE Nlabeldatalen = 0;

 *pusencodedstrlen = 0;
   while ((Nlabeldatalen = *pdecodepos)!= 0x00) {if (Nlabeldatalen & 0xc0) = = 0)//normal format, Labeldatalen + Label {
   if (Usplainstrlen + Nlabeldatalen + 1 > Ndotstrsize) {return FALSE;
   } memcpy (Szdotstr + usplainstrlen, Pdecodepos + 1, nlabeldatalen);
   memcpy (szdotstr + Usplainstrlen + Nlabeldatalen, ".", 1);
   Pdecodepos + + (Nlabeldatalen + 1);
   Usplainstrlen + + (Nlabeldatalen + 1);
  *pusencodedstrlen + + (Nlabeldatalen + 1); else//message compression format, 11000000 00000000, two bytes, top 2 bit for jump flag, after 14 bit for jump offset {if (SZPAcketstartpos = = NULL) {return FALSE;
   } USHORT Usjumppos = Ntohs (* (ushort*) (Pdecodepos)) & 0x3fff;
   USHORT Nencodestrlen = 0; if (! Decodedotstr (Szpacketstartpos + usjumppos, &nencodestrlen, Szdotstr + Usplainstrlen, Ndotstrsize-usplainstrlen,
   Szpacketstartpos)) {return FALSE;
    else {*pusencodedstrlen = 2;
   return TRUE;
 }} szdotstr[usplainstrlen-1] = ' I ';

 *pusencodedstrlen + 1;
return TRUE;
 } ULONG cdnslookup::gettickcountcalibrate () {static ULONG S_ulfirstcalltick = 0;

 static Longlong s_ullfirstcalltickms = 0;
 SYSTEMTIME SYSTEMTIME;
 FILETIME filetime; 
 Getlocaltime (&systemtime);
 SystemTimeToFileTime (&systemtime, &filetime);
 Large_integer Licurrenttime;
 Licurrenttime.highpart = Filetime.dwhighdatetime;
 Licurrenttime.lowpart = Filetime.dwlowdatetime;

 Longlong llcurrenttimems = licurrenttime.quadpart/10000;
 if (S_ulfirstcalltick = = 0) {S_ulfirstcalltick = GetTickCount (); } if (S_ullfirstcalltickms = = 0) {s_ullfirstcalltickms = Llcurrenttimems;
return S_ulfirstcalltick + (ULONG) (llcurrenttimems-s_ullfirstcalltickms); [DNSLookup.cpp] #include <stdio.h> #include <windows.h> #include "DNSLookup.h" int main (void) {Char
 Szdomainname[] = "www.baidu.com";
 Std::vector<ulong> veculiplist;
 Std::vector<std::string> vecstriplist;
 Std::vector<std::string> veccnamelist;
 ULONG ultimespent = 0;
 Cdnslookup DNSLookup; BOOL BRet = dnslookup. DNSLookup (inet_addr ("114.114.114.114"), Szdomainname, &vecstriplist, &veccnamelist, 1000, &ultimespent)

 ;
 printf ("DNSLookup result (%s): \ n", szdomainname);
  if (!bret) {printf ("timeout!\n");
 return-1;
 for (int i = 0; I!= veculiplist.size (); ++i) {printf ("ip%d (ULONG) =%u\n", i + 1, veculiplist[i]);
 for (int i = 0; I!= vecstriplist.size (); ++i) {printf ("ip%d (string) =%s\n", i + 1, vecstriplist[i].c_str ()); for (int i = 0; I!= veccnamelist.size (); ++i) {printf ("cname%d =%s\n", i + 1, veccnamelist[i].c_str ());
 
 printf ("Time spent =%ums\n", ultimespent);
return 0;
 }

The above is the C + + implementation of DNS domain name resolution of the entire content, I hope to help you learn.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.