Main Page Data Structures File List Data Fields Globals Related Pages

# inet/dns.cpp

Go to the documentation of this file.

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