1 /** 2 * \addtogroup apps 3 * @{ 4 */ 5 6 /** 7 * \defgroup resolv DNS resolver 8 * @{ 9 * 10 * The uIP DNS resolver functions are used to lookup a hostname and 11 * map it to a numerical IP address. It maintains a list of resolved 12 * hostnames that can be queried with the resolv_lookup() 13 * function. New hostnames can be resolved using the resolv_query() 14 * function. 15 * 16 * When a hostname has been resolved (or found to be non-existant), 17 * the resolver code calls a callback function called resolv_found() 18 * that must be implemented by the module that uses the resolver. 19 */ 20 21 /** 22 * \file 23 * DNS host name to IP address resolver. 24 * \author Adam Dunkels <[email protected]> 25 * 26 * This file implements a DNS host name to IP address resolver. 27 */ 28 29 /* 30 * Copyright (c) 2002-2003, Adam Dunkels. 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. The name of the author may not be used to endorse or promote 42 * products derived from this software without specific prior 43 * written permission. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 46 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 47 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 49 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 51 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 52 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 54 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 55 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 * 57 * This file is part of the uIP TCP/IP stack. 58 * 59 * $Id: resolv.c,v 1.5 2006/06/11 21:46:37 adam Exp $ 60 * 61 */ 62 63 #include "resolv.h" 64 #include "uip.h" 65 66 #include <string.h> 67 68 #ifndef NULL 69 #define NULL (void *)0 70 #endif /* NULL */ 71 72 /** \internal The maximum number of retries when asking for a name. */ 73 #define MAX_RETRIES 8 74 75 /** \internal The DNS message header. */ 76 struct dns_hdr { 77 u16_t id; 78 u8_t flags1, flags2; 79 #define DNS_FLAG1_RESPONSE 0x80 80 #define DNS_FLAG1_OPCODE_STATUS 0x10 81 #define DNS_FLAG1_OPCODE_INVERSE 0x08 82 #define DNS_FLAG1_OPCODE_STANDARD 0x00 83 #define DNS_FLAG1_AUTHORATIVE 0x04 84 #define DNS_FLAG1_TRUNC 0x02 85 #define DNS_FLAG1_RD 0x01 86 #define DNS_FLAG2_RA 0x80 87 #define DNS_FLAG2_ERR_MASK 0x0f 88 #define DNS_FLAG2_ERR_NONE 0x00 89 #define DNS_FLAG2_ERR_NAME 0x03 90 u16_t numquestions; 91 u16_t numanswers; 92 u16_t numauthrr; 93 u16_t numextrarr; 94 }; 95 96 /** \internal The DNS answer message structure. */ 97 struct dns_answer { 98 /* DNS answer record starts with either a domain name or a pointer 99 to a name already present somewhere in the packet. */ 100 u16_t type; 101 u16_t class; 102 u16_t ttl[2]; 103 u16_t len; 104 uip_ipaddr_t ipaddr; 105 }; 106 107 struct namemap { 108 #define STATE_UNUSED 0 109 #define STATE_NEW 1 110 #define STATE_ASKING 2 111 #define STATE_DONE 3 112 #define STATE_ERROR 4 113 u8_t state; 114 u8_t tmr; 115 u8_t retries; 116 u8_t seqno; 117 u8_t err; 118 char name[32]; 119 uip_ipaddr_t ipaddr; 120 }; 121 122 #ifndef UIP_CONF_RESOLV_ENTRIES 123 #define RESOLV_ENTRIES 4 124 #else /* UIP_CONF_RESOLV_ENTRIES */ 125 #define RESOLV_ENTRIES UIP_CONF_RESOLV_ENTRIES 126 #endif /* UIP_CONF_RESOLV_ENTRIES */ 127 128 129 static struct namemap names[RESOLV_ENTRIES]; 130 131 static u8_t seqno; 132 133 static struct uip_udp_conn *resolv_conn = NULL; 134 135 136 /*---------------------------------------------------------------------------*/ 137 /** \internal 138 * Walk through a compact encoded DNS name and return the end of it. 139 * 140 * \return The end of the name. 141 */ 142 /*---------------------------------------------------------------------------*/ 143 static unsigned char * 144 parse_name(unsigned char *query) 145 { 146 unsigned char n; 147 148 do { 149 n = *query++; 150 151 while(n > 0) { 152 /* printf("%c", *query);*/ 153 ++query; 154 --n; 155 }; 156 /* printf(".");*/ 157 } while(*query != 0); 158 /* printf("\n");*/ 159 return query + 1; 160 } 161 /*---------------------------------------------------------------------------*/ 162 /** \internal 163 * Runs through the list of names to see if there are any that have 164 * not yet been queried and, if so, sends out a query. 165 */ 166 /*---------------------------------------------------------------------------*/ 167 static void 168 check_entries(void) 169 { 170 register struct dns_hdr *hdr; 171 char *query, *nptr, *nameptr; 172 static u8_t i; 173 static u8_t n; 174 register struct namemap *namemapptr; 175 176 for(i = 0; i < RESOLV_ENTRIES; ++i) { 177 namemapptr = &names[i]; 178 if(namemapptr->state == STATE_NEW || 179 namemapptr->state == STATE_ASKING) { 180 if(namemapptr->state == STATE_ASKING) { 181 if(--namemapptr->tmr == 0) { 182 if(++namemapptr->retries == MAX_RETRIES) { 183 namemapptr->state = STATE_ERROR; 184 resolv_found(namemapptr->name, NULL); 185 continue; 186 } 187 namemapptr->tmr = namemapptr->retries; 188 } else { 189 /* printf("Timer %d\n", namemapptr->tmr);*/ 190 /* Its timer has not run out, so we move on to next 191 entry. */ 192 continue; 193 } 194 } else { 195 namemapptr->state = STATE_ASKING; 196 namemapptr->tmr = 1; 197 namemapptr->retries = 0; 198 } 199 hdr = (struct dns_hdr *)uip_appdata; 200 memset(hdr, 0, sizeof(struct dns_hdr)); 201 hdr->id = htons(i); 202 hdr->flags1 = DNS_FLAG1_RD; 203 hdr->numquestions = HTONS(1); 204 query = (char *)uip_appdata + 12; 205 nameptr = namemapptr->name; 206 --nameptr; 207 /* Convert hostname into suitable query format. */ 208 do { 209 ++nameptr; 210 nptr = query; 211 ++query; 212 for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) { 213 *query = *nameptr; 214 ++query; 215 ++n; 216 } 217 *nptr = n; 218 } while(*nameptr != 0); 219 { 220 static unsigned char endquery[] = 221 {0,0,1,0,1}; 222 memcpy(query, endquery, 5); 223 } 224 uip_udp_send((unsigned char)(query + 5 - (char *)uip_appdata)); 225 break; 226 } 227 } 228 } 229 /*---------------------------------------------------------------------------*/ 230 /** \internal 231 * Called when new UDP data arrives. 232 */ 233 /*---------------------------------------------------------------------------*/ 234 static void 235 newdata(void) 236 { 237 char *nameptr; 238 struct dns_answer *ans; 239 struct dns_hdr *hdr; 240 static u8_t nquestions, nanswers; 241 static u8_t i; 242 register struct namemap *namemapptr; 243 244 hdr = (struct dns_hdr *)uip_appdata; 245 /* printf("ID %d\n", htons(hdr->id)); 246 printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE); 247 printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK); 248 printf("Num questions %d, answers %d, authrr %d, extrarr %d\n", 249 htons(hdr->numquestions), 250 htons(hdr->numanswers), 251 htons(hdr->numauthrr), 252 htons(hdr->numextrarr)); 253 */ 254 255 /* The ID in the DNS header should be our entry into the name 256 table. */ 257 i = htons(hdr->id); 258 namemapptr = &names[i]; 259 if(i < RESOLV_ENTRIES && 260 namemapptr->state == STATE_ASKING) { 261 262 /* This entry is now finished. */ 263 namemapptr->state = STATE_DONE; 264 namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; 265 266 /* Check for error. If so, call callback to inform. */ 267 if(namemapptr->err != 0) { 268 namemapptr->state = STATE_ERROR; 269 resolv_found(namemapptr->name, NULL); 270 return; 271 } 272 273 /* We only care about the question(s) and the answers. The authrr 274 and the extrarr are simply discarded. */ 275 nquestions = htons(hdr->numquestions); 276 nanswers = htons(hdr->numanswers); 277 278 /* Skip the name in the question. XXX: This should really be 279 checked agains the name in the question, to be sure that they 280 match. */ 281 nameptr = parse_name((char *)uip_appdata + 12) + 4; 282 283 while(nanswers > 0) { 284 /* The first byte in the answer resource record determines if it 285 is a compressed record or a normal one. */ 286 if(*nameptr & 0xc0) { 287 /* Compressed name. */ 288 nameptr +=2; 289 /* printf("Compressed anwser\n");*/ 290 } else { 291 /* Not compressed name. */ 292 nameptr = parse_name((char *)nameptr); 293 } 294 295 ans = (struct dns_answer *)nameptr; 296 /* printf("Answer: type %x, class %x, ttl %x, length %x\n", 297 htons(ans->type), htons(ans->class), (htons(ans->ttl[0]) 298 << 16) | htons(ans->ttl[1]), htons(ans->len));*/ 299 300 /* Check for IP address type and Internet class. Others are 301 discarded. */ 302 if(ans->type == HTONS(1) && 303 ans->class == HTONS(1) && 304 ans->len == HTONS(4)) { 305 /* printf("IP address %d.%d.%d.%d\n", 306 htons(ans->ipaddr[0]) >> 8, 307 htons(ans->ipaddr[0]) & 0xff, 308 htons(ans->ipaddr[1]) >> 8, 309 htons(ans->ipaddr[1]) & 0xff);*/ 310 /* XXX: we should really check that this IP address is the one 311 we want. */ 312 namemapptr->ipaddr[0] = ans->ipaddr[0]; 313 namemapptr->ipaddr[1] = ans->ipaddr[1]; 314 315 resolv_found(namemapptr->name, namemapptr->ipaddr); 316 return; 317 } else { 318 nameptr = nameptr + 10 + htons(ans->len); 319 } 320 --nanswers; 321 } 322 } 323 324 } 325 /*---------------------------------------------------------------------------*/ 326 /** \internal 327 * The main UDP function. 328 */ 329 /*---------------------------------------------------------------------------*/ 330 void 331 resolv_appcall(void) 332 { 333 if(uip_udp_conn->rport == HTONS(53)) { 334 if(uip_poll()) { 335 check_entries(); 336 } 337 if(uip_newdata()) { 338 newdata(); 339 } 340 } 341 } 342 /*---------------------------------------------------------------------------*/ 343 /** 344 * Queues a name so that a question for the name will be sent out. 345 * 346 * \param name The hostname that is to be queried. 347 */ 348 /*---------------------------------------------------------------------------*/ 349 void 350 resolv_query(char *name) 351 { 352 static u8_t i; 353 static u8_t lseq, lseqi; 354 register struct namemap *nameptr; 355 356 lseq = lseqi = 0; 357 358 for(i = 0; i < RESOLV_ENTRIES; ++i) { 359 nameptr = &names[i]; 360 if(nameptr->state == STATE_UNUSED) { 361 break; 362 } 363 if(seqno - nameptr->seqno > lseq) { 364 lseq = seqno - nameptr->seqno; 365 lseqi = i; 366 } 367 } 368 369 if(i == RESOLV_ENTRIES) { 370 i = lseqi; 371 nameptr = &names[i]; 372 } 373 374 /* printf("Using entry %d\n", i);*/ 375 376 strcpy(nameptr->name, name); 377 nameptr->state = STATE_NEW; 378 nameptr->seqno = seqno; 379 ++seqno; 380 } 381 /*---------------------------------------------------------------------------*/ 382 /** 383 * Look up a hostname in the array of known hostnames. 384 * 385 * \note This function only looks in the internal array of known 386 * hostnames, it does not send out a query for the hostname if none 387 * was found. The function resolv_query() can be used to send a query 388 * for a hostname. 389 * 390 * \return A pointer to a 4-byte representation of the hostname's IP 391 * address, or NULL if the hostname was not found in the array of 392 * hostnames. 393 */ 394 /*---------------------------------------------------------------------------*/ 395 u16_t * 396 resolv_lookup(char *name) 397 { 398 static u8_t i; 399 struct namemap *nameptr; 400 401 /* Walk through the list to see if the name is in there. If it is 402 not, we return NULL. */ 403 for(i = 0; i < RESOLV_ENTRIES; ++i) { 404 nameptr = &names[i]; 405 if(nameptr->state == STATE_DONE && 406 strcmp(name, nameptr->name) == 0) { 407 return nameptr->ipaddr; 408 } 409 } 410 return NULL; 411 } 412 /*---------------------------------------------------------------------------*/ 413 /** 414 * Obtain the currently configured DNS server. 415 * 416 * \return A pointer to a 4-byte representation of the IP address of 417 * the currently configured DNS server or NULL if no DNS server has 418 * been configured. 419 */ 420 /*---------------------------------------------------------------------------*/ 421 u16_t * 422 resolv_getserver(void) 423 { 424 if(resolv_conn == NULL) { 425 return NULL; 426 } 427 return resolv_conn->ripaddr; 428 } 429 /*---------------------------------------------------------------------------*/ 430 /** 431 * Configure which DNS server to use for queries. 432 * 433 * \param dnsserver A pointer to a 4-byte representation of the IP 434 * address of the DNS server to be configured. 435 */ 436 /*---------------------------------------------------------------------------*/ 437 void 438 resolv_conf(u16_t *dnsserver) 439 { 440 if(resolv_conn != NULL) { 441 uip_udp_remove(resolv_conn); 442 } 443 444 resolv_conn = uip_udp_new(dnsserver, HTONS(53)); 445 } 446 /*---------------------------------------------------------------------------*/ 447 /** 448 * Initalize the resolver. 449 */ 450 /*---------------------------------------------------------------------------*/ 451 void 452 resolv_init(void) 453 { 454 static u8_t i; 455 456 for(i = 0; i < RESOLV_ENTRIES; ++i) { 457 names[i].state = STATE_DONE; 458 } 459 460 } 461 /*---------------------------------------------------------------------------*/ 462 463 /** @} */ 464 /** @} */ 465