Main Page Data Structures File List Data Fields Globals Related Pages

# clib/DNS.C

Go to the documentation of this file.

    00001 /*
    00002     DNS example
    00003     Written by Ernest Schloesser
    00004     Copyright Beck IPC GmbH
    00005
    00006     You my freely use and change this example, but please state it's origin.
    00007
    00008     This example will use a UDP message to the DNS server to obtain the
    00009     numerical address string belonging to a domain name.
    00010
    00011     Example: the domain www.beck-ipc.com has the address 195.243.140.4
    00012
    00013     First I used the book "TCP/IP Illustrated" by W. Richard Stevens as a source.
    00014     Then I found that theory at praxis are not the same, so I analysed a
    00015     hex dump of the packet returned. The analysis is included in this source.
    00016     Although I could not understand every byte, I did get the valid IP's for
    00017     microsoft and for beck.
    00018
    00019     What is not checked: if the time to live is correct. Could be that it is stored
    00020     in a different byte order then I expected. I got a ttl for Microsoft of 2 minutes....
    00021
    00022     HISTORY:   Ernst Schloesser Beck-ipc             first coding
    00023                Christoph Loibl (tix.at)   20001116   Added support for compressed
    00024                                                      DNS-rr as specified in RFC1035
    00025
    00026    $Log:
    00027     2    Projekte Software1.1         10.01.01 10:17:50    Ernest Schlösser DNS
    00028          modified bei Christoph Loibl to support compression.
    00029     1    Projekte Software1.0         15.12.00 15:25:38    Andre Pribil
    00030    $
    00031
    00032 */
    00033 #include <mem.h>
    00034 #include <string.h>
    00035 #include <stdio.h>
    00036 #include <ctype.h>
    00037
    00038 #include "tcpip.h"
    00039
    00040 #include "dns.h"
    00041
    00042 //#define TRACE   // comment out if you do not want prints of results & progress
    00043 //#define MONITOR // use this to show the cute little hex dump!
    00044
    00045 #define DNS_PORT    53          // Well known DNS port
    00046
    00047
    00048 static unsigned int ident;  // ident counter, count up at every request
    00049
    00050 #define BUFSIZE 600
    00051
    00052 #define RRBUF 200
    00053
    00054 typedef struct dn_rr_struct {
    00055    char rrname[RRBUF];
    00056    unsigned int rrtype;
    00057    unsigned int rrclass;
    00058    unsigned long rrttl;
    00059    char rrrdata[RRBUF];
    00060 } dn_rr;
    00061
    00062 /* Define values for RR-types as specified in RFC1035 */
    00063 #define DN_TYPE_A     1
    00064 #define DN_TYPE_NS    2
    00065 #define DN_TYPE_CNAME 5
    00066 #define DN_TYPE_MX    15
    00067
    00068 /* Define values for RR-class as specified in RFC1035 */
    00069 #define DN_CLASS_IN   1
    00070
    00071
    00072 #ifdef MONITOR
    00073 /* pointer to begin of packet and length - shows up a cute hex-screen */
    00074 void packetmonitor(int len2, char *buffer)
    00075 {
    00076    int j,i,k;
    00077
    00078       j=0;
    00079       for(i=0;i<len2;i++)
    00080       {
    00081   if(j==0)
    00082   {
    00083      printf("\n\r%3X  ",i);
    00084   }
    00085   printf("%02X ",buffer[i]&0xff);
    00086   j++;
    00087   if(j==16)
    00088   {
    00089     for(k=0;k<j;k++)
    00090     {
    00091       if(isprint(buffer[i-j+k+1]&0xff))
    00092         printf("%c",buffer[i-j+k+1]&0xff);
    00093       else
    00094         printf(".");
    00095     }
    00096     j=0;
    00097   }
    00098       }
    00099       i--;
    00100       for(k=0;k<j;k++)
    00101       {
    00102   if(isprint(buffer[i-j+k+1]&0xff))
    00103     printf("%c",buffer[i-j+k+1]&0xff);
    00104   else
    00105     printf(".");
    00106       }
    00107       j=0;
    00108 }
    00109 #endif
    00110
    00111 /*
    00112    convert a compressed domain-name to its ascii representation
    00113    packet: pointer to a dns-query-packet within a dns answer or reply
    00114    name: pointer to the name-field
    00115    dest: destination of the domain-name
    00116    len: length of dest
    00117    returns: length of the compressed name field, -1 if length of
    00118             dest is too small for dest to hold the whole domain-name
    00119 */
    00120 int dn_uncompress(char *dest, int len, char *packet, char *name)
    00121 {
    00122    int nlen, nlenok;
    00123    unsigned int u;
    00124 //   char *ptr;
    00125
    00126    *dest= 0;
    00127    nlen=nlenok= 0;
    00128    while(*name) {
    00129       if((*name&0xc0) == 0xc0) {              /* we found a compressed sign change read pointer */
    00130          u= ((*name&0x3f)<<8) + (*(name+1));  /* resolve pointer */
    00131          if(packet+u > name)                  /* pointer not allowed abort */
    00132             return -1;
    00133          name=packet+u;  //u
    00134          if(nlenok == 0) {                    /* after first compressed sign block setting the length field */
    00135             nlen+= 2;                         /* set the length field for the return value */
    00136             nlenok= 1;
    00137          }
    00138       }
    00139       else {
    00140          if(*name > (len-2))                  /* domain-name is too long for dest */
    00141             return -1;
    00142          len-= *name;
    00143          strncat(dest,name+1,*name);          /* copy next domain-part to the end of dest */
    00144          if(nlenok == 0) {
    00145             nlen+=*name+1;
    00146          }
    00147          name+= *name+1;
    00148          if(*name) {                          /* if name not finished append '.' to dest */
    00149             len--;
    00150             strcat(dest,".");
    00151          }
    00152       }
    00153    }
    00154    if(nlenok == 0)                            /* if there was no compress sign in the domain-name include 0 for the length */
    00155       nlen++;
    00156    return nlen;                               /* return length of name-field */
    00157 }
    00158
    00159 /*
    00160    extract a dns-rr message pointed to by message and return
    00161    the values in an dn_rr struct
    00162    rr: pointer to the dn_rr struct
    00163    packet: pointer to the begin of the packet
    00164    message: pointer in a packet pointing to the message to extract
    00165    returns: the total length of the rr, or -1 if error occured
    00166 */
    00167 int dn_unpackrr(dn_rr *rr, char *packet, char *message)
    00168 {
    00169    int i;
    00170    unsigned int u;
    00171
    00172    if((i=dn_uncompress(rr->rrname,RRBUF,packet,message)) == -1) /* get name */
    00173       return -1;
    00174    message+=i;
    00175    rr->rrtype= ((*message)<<8) + (*(message+1));              /* get type */
    00176    message+=2;
    00177    rr->rrclass= ((*message)<<8) + (*(message+1));             /* get class */
    00178    message+=2;
    00179    rr->rrttl=((*message)<<24)&0xff000000L;                     /* get ttl */
    00180    rr->rrttl+=((*(message+1))<<16)&0xff0000L;
    00181    rr->rrttl+=((*(message+2))<<8)&0xff00;
    00182    rr->rrttl+=((*(message+3)))&0xff;
    00183    message+=4;
    00184    u= ((*message)<<8) + (*(message+1));                       /* get rdlength */
    00185    message+=2;
    00186    switch(rr->rrtype) {
    00187       case DN_TYPE_CNAME:
    00188       case DN_TYPE_NS:
    00189                           if(dn_uncompress(rr->rrrdata,RRBUF,packet,message) == -1)
    00190                              return -1;
    00191                           break;
    00192
    00193       case DN_TYPE_MX:    *(rr->rrrdata)=*message;
    00194                           *(rr->rrrdata+1)=*(message+1);
    00195                           message+=2;
    00196                           if(dn_uncompress(rr->rrrdata+2,RRBUF-2,packet,message) == -1)
    00197                              return -1;
    00198                           break;
    00199
    00200       case DN_TYPE_A:     if(rr->rrclass == DN_CLASS_IN) {
    00201                              *(rr->rrrdata)=*message;            /* copy ip adress to struct */
    00202                              *(rr->rrrdata+1)=*(message+1);
    00203                              *(rr->rrrdata+2)=*(message+2);
    00204                              *(rr->rrrdata+3)=*(message+3);
    00205                              *(rr->rrrdata+4)=0;
    00206                           }
    00207                           else
    00208                              return -2;                         /* unknown CLASS */
    00209                           break;
    00210       default:            *(rr->rrrdata)=0;
    00211    }
    00212    return u+i+10;                                               /* return total length of rr */
    00213 }
    00214
    00215 /*
    00216    convert a string like "www.hello.de" to a string with following
    00217    byte values: 3 119 119 199 5 104 101 108 108 111 2 100 101 0
    00218    The bytes mean: length, ascii chars, length, ascii chars, ...., length=0
    00219    returns length (including terminator) of string.
    00220 */
    00221 static int packdomain(char * dest, const char *src)
    00222 {
    00223   int i,n,cnt;
    00224
    00225   n=strlen(src);
    00226   dest[n+1]=0;  // terminator
    00227   // walk back trough the string
    00228   cnt=0;
    00229   for (i=n; i>0; i--)
    00230   {
    00231     if (src[i-1]=='.')
    00232     {
    00233       dest[i]=cnt;
    00234       cnt=0;
    00235     }
    00236     else
    00237     {
    00238       dest[i]=src[i-1];
    00239       cnt++;
    00240     }
    00241   }
    00242   dest[0]=cnt;
    00243   return n+2;
    00244 }
    00245
    00246 /*
    00247     gethostbyname
    00248     This is the function it is all about.
    00249     It prepares the request
    00250     Opens the socket
    00251     Sends the request
    00252     Receives the answer
    00253     Interprets the answer
    00254     Closes the socket
    00255
    00256     return 1 on succes
    00257 */
    00258 int gethostbyname(  const char *dnsServerIP, // where to ask for the dns resolution
    00259         const char *domainName,  // The domain name "www.beck-ipc.com"
    00260         unsigned long *ttl,      // Time to live in seconds
    00261         unsigned long *IP,       // The 32 bit IP
    00262         char *dest)              // The IP as text (make sure it is long enough)
    00263 {
    00264   unsigned char buffer[BUFSIZE+1];
    00265   int sd,error;
    00266   struct sockaddr_in  addr;
    00267   int i,j,k,len1,len2,nRR;
    00268 //  int type,clss; // type and class of record
    00269   dn_rr myrr;
    00270
    00271   #ifdef TRACE
    00272     char s[200];
    00273     printf("\r\ndns server: %s  Host: %s\r\n",dnsServerIP,domainName);
    00274   #endif
    00275
    00276   setmem(buffer,BUFSIZE,0);
    00277
    00278   // ident is a sequence number
    00279   if (++ident>126) ident=1;
    00280   buffer[0]=(ident>>8);
    00281   buffer[1]=(ident&0xff);
    00282   // flags
    00283   buffer[2]=1;
    00284   buffer[3]=0;
    00285   // number of questions
    00286   buffer[4]=0;
    00287   buffer[5]=1;
    00288   // 12 bytes in header
    00289   len1=12;
    00290   len1+=packdomain((char *)&buffer[12],domainName);
    00291   buffer[len1++]=0; buffer[len1++]=1; // query type is IP address
    00292   buffer[len1++]=0; buffer[len1++]=1; // query class is also IP address
    00293
    00294   #ifdef TRACE
    00295     printf("Request holds %d bytes\r\n",len1);
    00296     for (i=0; i<len1; i++) printf(" %X",buffer[i]&0x00ff);
    00297   #endif
    00298
    00299   sd=opensocket(SOCK_DGRAM,&error);                             /* open the socket */
    00300   if (sd==API_ERROR) {
    00301     printf("\r\nError %d opening socket\r\n",error);
    00302     return 0;
    00303   }
    00304
    00305   addr.sin_family      = AF_INET;                               /* set address family to IP */
    00306   if(inet_addr((char *)dnsServerIP, &addr.sin_addr.s_addr)) {   /* convert the sting holding the ns-ip to binary */
    00307     printf("\r\nError converting DNS Server address %s \r\n",dnsServerIP);
    00308     printf("\r\nIP %lX",addr.sin_addr.s_addr);
    00309     return 0;
    00310   }
    00311   addr.sin_port=htons(DNS_PORT);                                /* set target port number */
    00312   // send data
    00313   if (  API_ERROR==sendto(sd, (char *)buffer, len1, MSG_BLOCKING, (const struct sockaddr * )&addr, &error))
    00314   {
    00315     printf("\r\nError %d sending data in line %d\r\n",error,__LINE__);
    00316     closesocket(sd,&error);
    00317     return 0;
    00318   }
    00319
    00320   api_sleep(100);                                               /* wait a little */
    00321   // receive answer, with timeout
    00322   len2=recvfrom(sd, (char *)buffer, BUFSIZE, MSG_TIMEOUT, 10000,(struct sockaddr *)&addr,&error);
    00323   closesocket(sd,&error);
    00324   if (len2<=len1)
    00325   {
    00326     printf("\r\nError %d receiving data\r\n",error);
    00327     return 0;
    00328   }
    00329
    00330   #ifdef MONITOR
    00331      packetmonitor(len2, buffer);
    00332   #endif
    00333   #ifdef TRACE
    00334      printf("\r\nReceived %d bytes\r\n",len2);
    00335      printf("Header:");
    00336      for (i=0; i<12; i++)
    00337         printf(" %X",buffer[i]&0xff);
    00338      printf("\r\n");
    00339   #endif
    00340
    00341   // At this point I just assume that it is a valid answer
    00342   // really good would be to check..... (I leave this to you)
    00343   // the number of answers
    00344   nRR=buffer[6];
    00345   nRR=(nRR<<8)+buffer[7];
    00346   #ifdef TRACE
    00347      printf("\r\nGot %d answers",nRR);
    00348   #endif
    00349   // we only use one answer
    00350   // the first answer starts where the question stopped
    00351   i=len1;
    00352   // at this point &buffer[i] points to the first answer rr
    00353   k=1;
    00354   while(nRR--) {
    00355
    00356      if((j=dn_unpackrr(&myrr, (char *)buffer,(char *)&buffer[i])) == -1) {           /* get answer-rr from buffer */
    00357         printf("\r\nError in answer section from DNS-server");
    00358         return 0;
    00359      }
    00360      #ifdef TRACE
    00361         printf("\r\nA%i: name=%s type=%i class=%i ttl=%lu",k++,myrr.rrname,myrr.rrtype, myrr.rrclass, myrr.rrttl);
    00362         if(myrr.rrtype == DN_TYPE_CNAME)
    00363            printf(" cname=%s",myrr.rrrdata);
    00364      #endif
    00365      if(myrr.rrtype == DN_TYPE_A && myrr.rrclass == DN_CLASS_IN) {  /* if it is a A record, we found what we want */
    00366         k= -1;
    00367         break;
    00368      }
    00369      i+=j;                                                          /* move position to next answer record */
    00370   }
    00371   if (k != -1) {
    00372      return 0;
    00373   }
    00374   memcpy((void *)IP,(void *)myrr.rrrdata,4);
    00375   *ttl=myrr.rrttl;
    00376   InetToAscii(IP,dest);
    00377   return 1;
    00378 }