Contiki 2.6

resolv.c

Go to the documentation of this file.
00001 /**
00002  * \addtogroup uip
00003  * @{
00004  */
00005 
00006 /**
00007  * \defgroup uipdns uIP hostname resolver functions
00008  * @{
00009  *
00010  * The uIP DNS resolver functions are used to lookup a hostname and
00011  * map it to a numerical IP address. It maintains a list of resolved
00012  * hostnames that can be queried with the resolv_lookup()
00013  * function. New hostnames can be resolved using the resolv_query()
00014  * function.
00015  *
00016  * The event resolv_event_found is posted when a hostname has been
00017  * resolved. It is up to the receiving process to determine if the
00018  * correct hostname has been found by calling the resolv_lookup()
00019  * function with the hostname.
00020  */
00021 
00022 /**
00023  * \file
00024  * DNS host name to IP address resolver.
00025  * \author Adam Dunkels <adam@dunkels.com>
00026  *
00027  * This file implements a DNS host name to IP address resolver.
00028  */
00029 
00030 /*
00031  * Copyright (c) 2002-2003, Adam Dunkels.
00032  * All rights reserved.
00033  *
00034  * Redistribution and use in source and binary forms, with or without
00035  * modification, are permitted provided that the following conditions
00036  * are met:
00037  * 1. Redistributions of source code must retain the above copyright
00038  *    notice, this list of conditions and the following disclaimer.
00039  * 2. Redistributions in binary form must reproduce the above copyright
00040  *    notice, this list of conditions and the following disclaimer in the
00041  *    documentation and/or other materials provided with the distribution.
00042  * 3. The name of the author may not be used to endorse or promote
00043  *    products derived from this software without specific prior
00044  *    written permission.
00045  *
00046  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
00047  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00048  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00049  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00050  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00051  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00052  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00053  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00054  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00055  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00056  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00057  *
00058  * This file is part of the uIP TCP/IP stack.
00059  *
00060  * $Id: resolv.c,v 1.10 2010/10/19 18:29:04 adamdunkels Exp $
00061  *
00062  */
00063 
00064 #include "net/tcpip.h"
00065 #include "net/resolv.h"
00066 #if UIP_UDP
00067 
00068 #include <string.h>
00069 
00070 #ifndef NULL
00071 #define NULL (void *)0
00072 #endif /* NULL */
00073 
00074 #if UIP_CONF_IPV6
00075 
00076 /* Currently this implementation only supports IPv4 DNS lookups.
00077    Until support for IPv6 is added, dummy functions are used to
00078    enable compilation with IPv6.
00079 */
00080 
00081 process_event_t resolv_event_found;
00082 
00083 PROCESS(resolv_process, "DNS resolver");
00084 
00085 void resolv_conf(const uip_ipaddr_t *dnsserver) { }
00086 uip_ipaddr_t *resolv_getserver(void) { return NULL; }
00087 uip_ipaddr_t *resolv_lookup(const char *name) { return NULL; }
00088 void resolv_query(const char *name) { }
00089 
00090 PROCESS_THREAD(resolv_process, ev, data)
00091 {
00092   PROCESS_BEGIN();
00093   resolv_event_found = process_alloc_event();
00094   PROCESS_END();
00095 }
00096 
00097 #else /* UIP_CONF_IPV6 */
00098 
00099 /** \internal The maximum number of retries when asking for a name. */
00100 #define MAX_RETRIES 8
00101 
00102 /** \internal The DNS message header. */
00103 struct dns_hdr {
00104   uint16_t id;
00105   uint8_t flags1, flags2;
00106 #define DNS_FLAG1_RESPONSE        0x80
00107 #define DNS_FLAG1_OPCODE_STATUS   0x10
00108 #define DNS_FLAG1_OPCODE_INVERSE  0x08
00109 #define DNS_FLAG1_OPCODE_STANDARD 0x00
00110 #define DNS_FLAG1_AUTHORATIVE     0x04
00111 #define DNS_FLAG1_TRUNC           0x02
00112 #define DNS_FLAG1_RD              0x01
00113 #define DNS_FLAG2_RA              0x80
00114 #define DNS_FLAG2_ERR_MASK        0x0f
00115 #define DNS_FLAG2_ERR_NONE        0x00
00116 #define DNS_FLAG2_ERR_NAME        0x03
00117   uint16_t numquestions;
00118   uint16_t numanswers;
00119   uint16_t numauthrr;
00120   uint16_t numextrarr;
00121 };
00122 
00123 /** \internal The DNS answer message structure. */
00124 struct dns_answer {
00125   /* DNS answer record starts with either a domain name or a pointer
00126      to a name already present somewhere in the packet. */
00127   uint16_t type;
00128   uint16_t class;
00129   uint16_t ttl[2];
00130   uint16_t len;
00131   uint8_t ipaddr[4];
00132 };
00133 
00134 struct namemap {
00135 #define STATE_UNUSED 0
00136 #define STATE_NEW    1
00137 #define STATE_ASKING 2
00138 #define STATE_DONE   3
00139 #define STATE_ERROR  4
00140   uint8_t state;
00141   uint8_t tmr;
00142   uint8_t retries;
00143   uint8_t seqno;
00144   uint8_t err;
00145   char name[32];
00146   uip_ipaddr_t ipaddr;
00147 };
00148 
00149 #ifndef UIP_CONF_RESOLV_ENTRIES
00150 #define RESOLV_ENTRIES 4
00151 #else /* UIP_CONF_RESOLV_ENTRIES */
00152 #define RESOLV_ENTRIES UIP_CONF_RESOLV_ENTRIES
00153 #endif /* UIP_CONF_RESOLV_ENTRIES */
00154 
00155 
00156 static struct namemap names[RESOLV_ENTRIES];
00157 
00158 static uint8_t seqno;
00159 
00160 static struct uip_udp_conn *resolv_conn = NULL;
00161 
00162 static struct etimer retry;
00163 
00164 process_event_t resolv_event_found;
00165 
00166 PROCESS(resolv_process, "DNS resolver");
00167 
00168 static void resolv_found(char *name, uip_ipaddr_t *ipaddr);
00169 
00170 enum {
00171   EVENT_NEW_SERVER=0
00172 };
00173 
00174 /*-----------------------------------------------------------------------------------*/
00175 /** \internal
00176  * Walk through a compact encoded DNS name and return the end of it.
00177  *
00178  * \return The end of the name.
00179  */
00180 /*-----------------------------------------------------------------------------------*/
00181 static unsigned char *
00182 parse_name(unsigned char *query)
00183 {
00184   unsigned char n;
00185 
00186   do {
00187     n = *query++;
00188     
00189     while(n > 0) {
00190       /*      printf("%c", *query);*/
00191       ++query;
00192       --n;
00193     };
00194     /*    printf(".");*/
00195   } while(*query != 0);
00196   /*  printf("\n");*/
00197   return query + 1;
00198 }
00199 /*-----------------------------------------------------------------------------------*/
00200 /** \internal
00201  * Runs through the list of names to see if there are any that have
00202  * not yet been queried and, if so, sends out a query.
00203  */
00204 /*-----------------------------------------------------------------------------------*/
00205 static void
00206 check_entries(void)
00207 {
00208   register struct dns_hdr *hdr;
00209   char *query, *nptr, *nameptr;
00210   uint8_t i;
00211   uint8_t n;
00212   register struct namemap *namemapptr;
00213   
00214   for(i = 0; i < RESOLV_ENTRIES; ++i) {
00215     namemapptr = &names[i];
00216     if(namemapptr->state == STATE_NEW ||
00217        namemapptr->state == STATE_ASKING) {
00218       etimer_set(&retry, CLOCK_SECOND);
00219       if(namemapptr->state == STATE_ASKING) {
00220         if(--namemapptr->tmr == 0) {
00221           if(++namemapptr->retries == MAX_RETRIES) {
00222             namemapptr->state = STATE_ERROR;
00223             resolv_found(namemapptr->name, NULL);
00224             continue;
00225           }
00226           namemapptr->tmr = namemapptr->retries;
00227         } else {
00228           /*      printf("Timer %d\n", namemapptr->tmr);*/
00229           /* Its timer has not run out, so we move on to next
00230              entry. */
00231           continue;
00232         }
00233       } else {
00234         namemapptr->state = STATE_ASKING;
00235         namemapptr->tmr = 1;
00236         namemapptr->retries = 0;
00237       }
00238       hdr = (struct dns_hdr *)uip_appdata;
00239       memset(hdr, 0, sizeof(struct dns_hdr));
00240       hdr->id = uip_htons(i);
00241       hdr->flags1 = DNS_FLAG1_RD;
00242       hdr->numquestions = UIP_HTONS(1);
00243       query = (char *)uip_appdata + 12;
00244       nameptr = namemapptr->name;
00245       --nameptr;
00246       /* Convert hostname into suitable query format. */
00247       do {
00248         ++nameptr;
00249         nptr = query;
00250         ++query;
00251         for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
00252           *query = *nameptr;
00253           ++query;
00254           ++n;
00255         }
00256         *nptr = n;
00257       } while(*nameptr != 0);
00258       {
00259         static unsigned char endquery[] =
00260           {0,0,1,0,1};
00261         memcpy(query, endquery, 5);
00262       }
00263       uip_udp_send((unsigned char)(query + 5 - (char *)uip_appdata));
00264       break;
00265     }
00266   }
00267 }
00268 /*-----------------------------------------------------------------------------------*/
00269 /** \internal
00270  * Called when new UDP data arrives.
00271  */
00272 /*-----------------------------------------------------------------------------------*/
00273 static void
00274 newdata(void)
00275 {
00276   unsigned char *nameptr;
00277   struct dns_answer *ans;
00278   struct dns_hdr *hdr;
00279   static uint8_t nquestions, nanswers;
00280   static uint8_t i;
00281   register struct namemap *namemapptr;
00282   
00283   hdr = (struct dns_hdr *)uip_appdata;
00284   /*  printf("ID %d\n", uip_htons(hdr->id));
00285   printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE);
00286   printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK);
00287   printf("Num questions %d, answers %d, authrr %d, extrarr %d\n",
00288          uip_htons(hdr->numquestions),
00289          uip_htons(hdr->numanswers),
00290          uip_htons(hdr->numauthrr),
00291          uip_htons(hdr->numextrarr));
00292   */
00293 
00294   /* The ID in the DNS header should be our entry into the name
00295      table. */
00296   i = (uint8_t)uip_htons(hdr->id);
00297   namemapptr = &names[i];
00298   if(i < RESOLV_ENTRIES &&
00299      namemapptr->state == STATE_ASKING) {
00300 
00301     /* This entry is now finished. */
00302     namemapptr->state = STATE_DONE;
00303     namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
00304 
00305     /* Check for error. If so, call callback to inform. */
00306     if(namemapptr->err != 0) {
00307       namemapptr->state = STATE_ERROR;
00308       resolv_found(namemapptr->name, NULL);
00309       return;
00310     }
00311 
00312     /* We only care about the question(s) and the answers. The authrr
00313        and the extrarr are simply discarded. */
00314     nquestions = (uint8_t)uip_htons(hdr->numquestions);
00315     nanswers = (uint8_t)uip_htons(hdr->numanswers);
00316 
00317     /* Skip the name in the question. XXX: This should really be
00318        checked agains the name in the question, to be sure that they
00319        match. */
00320     nameptr = parse_name((uint8_t *)uip_appdata + 12) + 4;
00321 
00322     while(nanswers > 0) {
00323       /* The first byte in the answer resource record determines if it
00324          is a compressed record or a normal one. */
00325       if(*nameptr & 0xc0) {
00326         /* Compressed name. */
00327         nameptr +=2;
00328         /*      printf("Compressed anwser\n");*/
00329       } else {
00330         /* Not compressed name. */
00331         nameptr = parse_name((uint8_t *)nameptr);
00332       }
00333 
00334       ans = (struct dns_answer *)nameptr;
00335       /*      printf("Answer: type %x, class %x, ttl %x, length %x\n",
00336              uip_htons(ans->type), uip_htons(ans->class), (uip_htons(ans->ttl[0])
00337              << 16) | uip_htons(ans->ttl[1]), uip_htons(ans->len));*/
00338 
00339       /* Check for IP address type and Internet class. Others are
00340          discarded. */
00341       if(ans->type == UIP_HTONS(1) &&
00342          ans->class == UIP_HTONS(1) &&
00343          ans->len == UIP_HTONS(4)) {
00344         /*      printf("IP address %d.%d.%d.%d\n",
00345                ans->ipaddr[0],
00346                ans->ipaddr[1],
00347                ans->ipaddr[2],
00348                ans->ipaddr[3]);*/
00349         /* XXX: we should really check that this IP address is the one
00350            we want. */
00351         for(i = 0; i < 4; i++) {
00352           namemapptr->ipaddr.u8[i] = ans->ipaddr[i];
00353         }
00354         
00355         resolv_found(namemapptr->name, &namemapptr->ipaddr);
00356         return;
00357       } else {
00358         nameptr = nameptr + 10 + uip_htons(ans->len);
00359       }
00360       --nanswers;
00361     }
00362   }
00363 }
00364 /*-----------------------------------------------------------------------------------*/
00365 /** \internal
00366  * The main UDP function.
00367  */
00368 /*-----------------------------------------------------------------------------------*/
00369 PROCESS_THREAD(resolv_process, ev, data)
00370 {
00371   int i;
00372   
00373   PROCESS_BEGIN();
00374 
00375   for(i = 0; i < RESOLV_ENTRIES; ++i) {
00376     names[i].state = STATE_UNUSED;
00377   }
00378   resolv_conn = NULL;
00379   resolv_event_found = process_alloc_event();
00380   
00381   
00382   while(1) {
00383     PROCESS_WAIT_EVENT();
00384     
00385     if(ev == PROCESS_EVENT_TIMER) {
00386       if(resolv_conn != NULL) {
00387         tcpip_poll_udp(resolv_conn);
00388       }
00389 
00390     } else if(ev == EVENT_NEW_SERVER) {
00391       if(resolv_conn != NULL) {
00392         uip_udp_remove(resolv_conn);
00393       }
00394       resolv_conn = udp_new((uip_ipaddr_t *)data, UIP_HTONS(53), NULL);
00395       
00396     } else if(ev == tcpip_event) {
00397       if(uip_udp_conn->rport == UIP_HTONS(53)) {
00398         if(uip_poll()) {
00399           check_entries();
00400         }
00401         if(uip_newdata()) {
00402           newdata();
00403         }
00404       }
00405     }
00406   }
00407   
00408   PROCESS_END();
00409 }
00410 /*-----------------------------------------------------------------------------------*/
00411 /**
00412  * Queues a name so that a question for the name will be sent out.
00413  *
00414  * \param name The hostname that is to be queried.
00415  */
00416 /*-----------------------------------------------------------------------------------*/
00417 void
00418 resolv_query(const char *name)
00419 {
00420   static uint8_t i;
00421   static uint8_t lseq, lseqi;
00422   register struct namemap *nameptr;
00423       
00424   lseq = lseqi = 0;
00425   nameptr = 0;                //compiler warning if not initialized
00426   
00427   for(i = 0; i < RESOLV_ENTRIES; ++i) {
00428     nameptr = &names[i];
00429     if(nameptr->state == STATE_UNUSED) {
00430       break;
00431     }
00432     if(seqno - nameptr->seqno > lseq) {
00433       lseq = seqno - nameptr->seqno;
00434       lseqi = i;
00435     }
00436   }
00437 
00438   if(i == RESOLV_ENTRIES) {
00439     i = lseqi;
00440     nameptr = &names[i];
00441   }
00442 
00443   strncpy(nameptr->name, name, sizeof(nameptr->name));
00444   nameptr->state = STATE_NEW;
00445   nameptr->seqno = seqno;
00446   ++seqno;
00447 
00448   if(resolv_conn != NULL) {
00449     tcpip_poll_udp(resolv_conn);
00450   }
00451 }
00452 /*-----------------------------------------------------------------------------------*/
00453 /**
00454  * Look up a hostname in the array of known hostnames.
00455  *
00456  * \note This function only looks in the internal array of known
00457  * hostnames, it does not send out a query for the hostname if none
00458  * was found. The function resolv_query() can be used to send a query
00459  * for a hostname.
00460  *
00461  * \return A pointer to a 4-byte representation of the hostname's IP
00462  * address, or NULL if the hostname was not found in the array of
00463  * hostnames.
00464  */
00465 /*-----------------------------------------------------------------------------------*/
00466 uip_ipaddr_t *
00467 resolv_lookup(const char *name)
00468 {
00469   static uint8_t i;
00470   struct namemap *nameptr;
00471   
00472   /* Walk through the list to see if the name is in there. If it is
00473      not, we return NULL. */
00474   for(i = 0; i < RESOLV_ENTRIES; ++i) {
00475     nameptr = &names[i];
00476     if(nameptr->state == STATE_DONE &&
00477        strcmp(name, nameptr->name) == 0) {
00478       return &nameptr->ipaddr;
00479     }
00480   }
00481   return NULL;
00482 }
00483 /*-----------------------------------------------------------------------------------*/
00484 /**
00485  * Obtain the currently configured DNS server.
00486  *
00487  * \return A pointer to a 4-byte representation of the IP address of
00488  * the currently configured DNS server or NULL if no DNS server has
00489  * been configured.
00490  */
00491 /*-----------------------------------------------------------------------------------*/
00492 uip_ipaddr_t *
00493 resolv_getserver(void)
00494 {
00495   if(resolv_conn == NULL) {
00496     return NULL;
00497   }
00498   return &resolv_conn->ripaddr;
00499 }
00500 /*-----------------------------------------------------------------------------------*/
00501 /**
00502  * Configure a DNS server.
00503  *
00504  * \param dnsserver A pointer to a 4-byte representation of the IP
00505  * address of the DNS server to be configured.
00506  */
00507 /*-----------------------------------------------------------------------------------*/
00508 void
00509 resolv_conf(const uip_ipaddr_t *dnsserver)
00510 {
00511   static uip_ipaddr_t server;
00512   uip_ipaddr_copy(&server, dnsserver);
00513   process_post(&resolv_process, EVENT_NEW_SERVER, &server);
00514   
00515   /*  if(resolv_conn != NULL) {
00516     uip_udp_remove(resolv_conn);
00517   }
00518   
00519   resolv_conn = udp_new(dnsserver, 53, NULL);*/
00520 }
00521 /*-----------------------------------------------------------------------------------*/
00522 /** \internal
00523  * Callback function which is called when a hostname is found.
00524  *
00525  */
00526 /*-----------------------------------------------------------------------------------*/
00527 static void
00528 resolv_found(char *name, uip_ipaddr_t *ipaddr)
00529 {
00530   process_post(PROCESS_BROADCAST, resolv_event_found, name);
00531 }
00532 /*-----------------------------------------------------------------------------------*/
00533 #endif /* UIP_CONF_IPV6 */
00534 #endif /* UIP_UDP */
00535 
00536 /** @} */
00537 /** @} */