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 }

Generated on Sun Aug 4 21:47:27 2002 for k/os mp3v2 by doxygen1.2.16