Contiki 2.6
|
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 /** @} */