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