Contiki-NG
Loading...
Searching...
No Matches
resolv.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2002-2003, Adam Dunkels.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote
14 * products derived from this software without specific prior
15 * written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * This file is part of the uIP TCP/IP stack.
30 *
31 *
32 */
33
34/**
35 * \file
36 * DNS host name to IP address resolver.
37 * \author Adam Dunkels <adam@dunkels.com>
38 * \author Robert Quattlebaum <darco@deepdarc.com>
39 *
40 * This file implements a DNS host name to IP address resolver,
41 * as well as an MDNS responder and resolver.
42 */
43
44/**
45 * \addtogroup uip
46 * @{
47 */
48
49/**
50 * \defgroup uipdns uIP hostname resolver functions
51 * @{
52 *
53 * The uIP DNS resolver functions are used to lookup a hostname and
54 * map it to a numerical IP address. It maintains a list of resolved
55 * hostnames that can be queried with the resolv_lookup()
56 * function. New hostnames can be resolved using the resolv_query()
57 * function.
58 *
59 * The event resolv_event_found is posted when a hostname has been
60 * resolved. It is up to the receiving process to determine if the
61 * correct hostname has been found by calling the resolv_lookup()
62 * function with the hostname.
63 */
64
65#include "net/ipv6/tcpip.h"
68#include "lib/random.h"
69#include "resolv.h"
70#include <inttypes.h>
71#include <stdbool.h>
72
73/** If RESOLV_CONF_SUPPORTS_MDNS is set, then queries
74 * for domain names in the `local` TLD will use MDNS and
75 * will respond to MDNS queries for this device's hostname,
76 * as described by draft-cheshire-dnsext-multicastdns.
77 */
78#ifndef RESOLV_CONF_SUPPORTS_MDNS
79#define RESOLV_SUPPORTS_MDNS 0
80#else
81#define RESOLV_SUPPORTS_MDNS RESOLV_CONF_SUPPORTS_MDNS
82#endif
83
84#if UIP_UDP
85#include <string.h>
86#if RESOLV_SUPPORTS_MDNS
87#include <ctype.h>
88#endif /* RESOLV_SUPPORTS_MDNS */
89
90#include "sys/log.h"
91#define LOG_MODULE "Resolv"
92#define LOG_LEVEL LOG_LEVEL_NONE
93
94int strcasecmp(const char *s1, const char *s2);
95int strncasecmp(const char *s1, const char *s2, size_t n);
96
97#ifndef RESOLV_CONF_MDNS_INCLUDE_GLOBAL_V6_ADDRS
98#define RESOLV_CONF_MDNS_INCLUDE_GLOBAL_V6_ADDRS 0
99#endif
100
101/** The maximum number of retries when asking for a name. */
102#ifndef RESOLV_CONF_MAX_RETRIES
103#define RESOLV_CONF_MAX_RETRIES 4
104#endif
105
106#ifndef RESOLV_CONF_MAX_MDNS_RETRIES
107#define RESOLV_CONF_MAX_MDNS_RETRIES 3
108#endif
109
110#ifndef RESOLV_CONF_MAX_DOMAIN_NAME_SIZE
111#define RESOLV_CONF_MAX_DOMAIN_NAME_SIZE 32
112#endif
113
114#ifdef RESOLV_CONF_AUTO_REMOVE_TRAILING_DOTS
115#define RESOLV_AUTO_REMOVE_TRAILING_DOTS RESOLV_CONF_AUTO_REMOVE_TRAILING_DOTS
116#else
117#define RESOLV_AUTO_REMOVE_TRAILING_DOTS RESOLV_SUPPORTS_MDNS
118#endif
119
120#ifdef RESOLV_CONF_VERIFY_ANSWER_NAMES
121#define RESOLV_VERIFY_ANSWER_NAMES RESOLV_CONF_VERIFY_ANSWER_NAMES
122#else
123#define RESOLV_VERIFY_ANSWER_NAMES RESOLV_SUPPORTS_MDNS
124#endif
125
126#ifdef RESOLV_CONF_SUPPORTS_RECORD_EXPIRATION
127#define RESOLV_SUPPORTS_RECORD_EXPIRATION RESOLV_CONF_SUPPORTS_RECORD_EXPIRATION
128#else
129#define RESOLV_SUPPORTS_RECORD_EXPIRATION 1
130#endif
131
132#if RESOLV_SUPPORTS_MDNS && !RESOLV_VERIFY_ANSWER_NAMES
133#error RESOLV_SUPPORTS_MDNS cannot be set without RESOLV_CONF_VERIFY_ANSWER_NAMES
134#endif
135
136#if !defined(CONTIKI_TARGET_NAME) && defined(BOARD)
137#define stringy2(x) #x
138#define stringy(x) stringy2(x)
139#define CONTIKI_TARGET_NAME stringy(BOARD)
140#endif
141
142#ifndef CONTIKI_CONF_DEFAULT_HOSTNAME
143#ifdef CONTIKI_TARGET_NAME
144#define CONTIKI_CONF_DEFAULT_HOSTNAME "contiki-"CONTIKI_TARGET_NAME
145#else
146#define CONTIKI_CONF_DEFAULT_HOSTNAME "contiki"
147#endif
148#endif
149
150#define DNS_TYPE_A 1
151#define DNS_TYPE_CNAME 5
152#define DNS_TYPE_PTR 12
153#define DNS_TYPE_MX 15
154#define DNS_TYPE_TXT 16
155#define DNS_TYPE_AAAA 28
156#define DNS_TYPE_SRV 33
157#define DNS_TYPE_ANY 255
158#define DNS_TYPE_NSEC 47
159
160#define NATIVE_DNS_TYPE DNS_TYPE_AAAA /* IPv6 */
161
162#define DNS_CLASS_IN 1
163#define DNS_CLASS_ANY 255
164
165#ifndef DNS_PORT
166#define DNS_PORT 53
167#endif
168
169#ifndef MDNS_PORT
170#define MDNS_PORT 5353
171#endif
172
173#ifndef MDNS_RESPONDER_PORT
174#define MDNS_RESPONDER_PORT 5354
175#endif
176
177/** \internal The DNS message header. */
178struct dns_hdr {
179 uint16_t id;
180 uint8_t flags1, flags2;
181#define DNS_FLAG1_RESPONSE 0x80
182#define DNS_FLAG1_OPCODE_STATUS 0x10
183#define DNS_FLAG1_OPCODE_INVERSE 0x08
184#define DNS_FLAG1_OPCODE_STANDARD 0x00
185#define DNS_FLAG1_AUTHORATIVE 0x04
186#define DNS_FLAG1_TRUNC 0x02
187#define DNS_FLAG1_RD 0x01
188#define DNS_FLAG2_RA 0x80
189#define DNS_FLAG2_ERR_MASK 0x0f
190#define DNS_FLAG2_ERR_NONE 0x00
191#define DNS_FLAG2_ERR_NAME 0x03
192 uint16_t numquestions;
193 uint16_t numanswers;
194 uint16_t numauthrr;
195 uint16_t numextrarr;
196};
197
198/** \internal The DNS answer message structure. */
199struct dns_answer {
200 /* DNS answer record starts with either a domain name or a pointer
201 * to a name already present somewhere in the packet. */
202 uint16_t type;
203 uint16_t class;
204 uint16_t ttl[2];
205 uint16_t len;
206 uint8_t ipaddr[16];
207};
208
209struct namemap {
210#define STATE_UNUSED 0
211#define STATE_ERROR 1
212#define STATE_NEW 2
213#define STATE_ASKING 3
214#define STATE_DONE 4
215 uint8_t state;
216 uint8_t tmr;
217 uint16_t id;
218 uint8_t retries;
219 uint8_t seqno;
220#if RESOLV_SUPPORTS_RECORD_EXPIRATION
221 unsigned long expiration;
222#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
223 uip_ipaddr_t ipaddr;
224 uint8_t err;
225 uint8_t server;
226#if RESOLV_SUPPORTS_MDNS
227 bool is_mdns;
228 bool is_probe;
229#endif
230 char name[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1];
231};
232
233#ifndef UIP_CONF_RESOLV_ENTRIES
234#define RESOLV_ENTRIES 4
235#else /* UIP_CONF_RESOLV_ENTRIES */
236#define RESOLV_ENTRIES UIP_CONF_RESOLV_ENTRIES
237#endif /* UIP_CONF_RESOLV_ENTRIES */
238
239static struct namemap names[RESOLV_ENTRIES];
240static uint8_t seqno;
241static struct uip_udp_conn *resolv_conn = NULL;
242static struct etimer retry;
243process_event_t resolv_event_found;
244
245PROCESS(resolv_process, "DNS resolver");
246
247static void resolv_found(char *name, uip_ipaddr_t *ipaddr);
248
249/** \internal The DNS question message structure. */
250struct dns_question {
251 uint16_t type;
252 uint16_t class;
253};
254
255#if RESOLV_SUPPORTS_MDNS
256static char resolv_hostname[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1];
257
258enum {
259 MDNS_STATE_WAIT_BEFORE_PROBE,
260 MDNS_STATE_PROBING,
261 MDNS_STATE_READY,
262};
263
264static uint8_t mdns_state;
265
266static const uip_ipaddr_t resolv_mdns_addr =
267{ { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
268 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb } };
269#include "net/ipv6/uip-ds6.h"
270
271static int mdns_needs_host_announce;
272
273PROCESS(mdns_probe_process, "mDNS probe");
274#endif /* RESOLV_SUPPORTS_MDNS */
275
276/*---------------------------------------------------------------------------*/
277/** \internal
278 * \brief Decodes a DNS name from the DNS format into the given string.
279 * \return 1 upon success, 0 if the size of the name would be too large.
280 *
281 * \note `dest` must point to a buffer with at least
282 * `RESOLV_CONF_MAX_DOMAIN_NAME_SIZE+1` bytes large.
283 */
284static uint8_t
285decode_name(const unsigned char *query, char *dest,
286 const unsigned char *packet, size_t packet_len)
287{
288 const unsigned char *end = packet + packet_len;
289 int dest_len = RESOLV_CONF_MAX_DOMAIN_NAME_SIZE;
290 unsigned char label_len;
291
292 LOG_DBG("decoding name: \"");
293
294 if(query >= end) {
295 return 0;
296 }
297 label_len = *query++;
298
299 while(dest_len && label_len) {
300 if(label_len & 0xc0) {
301 if(query >= end) {
302 return 0;
303 }
304 const uint16_t offset = query[0] + ((label_len & ~0xC0) << 8);
305 if(offset >= packet_len) {
306 LOG_ERR("Offset %"PRIu16" exceeds packet length %zu\n",
307 offset, packet_len);
308 return 0;
309 }
310 LOG_DBG_("<skip-to-%d>", offset);
311 query = packet + offset;
312 label_len = *query++;
313 }
314
315 if(label_len == 0) {
316 break;
317 }
318
319 if(query - packet + label_len > packet_len) {
320 LOG_ERR("Cannot read outside the packet data\n");
321 return 0;
322 }
323
324 for(; label_len; --label_len) {
325 LOG_DBG_("%c", *query);
326
327 *dest++ = *query++;
328
329 if(--dest_len == 0) {
330 *dest = 0;
331 LOG_DBG_("\"\n");
332 return 0;
333 }
334 }
335
336 if(query >= end) {
337 LOG_ERR("Cannot read outside the packet data\n");
338 return 0;
339 }
340 label_len = *query++;
341
342 if(label_len > 0) {
343 LOG_DBG_(".");
344 *dest++ = '.';
345 --dest_len;
346 }
347 }
348
349 LOG_DBG_("\"\n");
350 *dest = 0;
351 return dest_len != 0;
352}
353/*---------------------------------------------------------------------------*/
354/** \internal
355 */
356#if RESOLV_SUPPORTS_MDNS
357static uint8_t
358dns_name_isequal(const unsigned char *queryptr, const char *name,
359 const unsigned char *packet, size_t packet_len)
360{
361 const unsigned char *end = packet + packet_len;
362 unsigned char label_len;
363
364 if(*name == 0 || queryptr >= end) {
365 return 0;
366 }
367
368 label_len = *queryptr++;
369
370 while(label_len > 0) {
371 if(label_len & 0xc0) {
372 if(queryptr >= end) {
373 return 0;
374 }
375 const uint16_t offset = queryptr[0] + ((label_len & ~0xC0) << 8);
376 if(offset >= packet_len) {
377 return 0;
378 }
379 queryptr = packet + offset;
380 label_len = *queryptr++;
381 }
382
383 for(; label_len; --label_len) {
384 if(!*name || queryptr >= end) {
385 return 0;
386 }
387
388 if(tolower(*name++) != tolower(*queryptr++)) {
389 return 0;
390 }
391 }
392
393 if(queryptr >= end) {
394 return 0;
395 }
396 label_len = *queryptr++;
397
398 if(label_len != 0 && *name++ != '.') {
399 return 0;
400 }
401 }
402
403 if(*name == '.') {
404 ++name;
405 }
406
407 return name[0] == 0;
408}
409#endif /* RESOLV_SUPPORTS_MDNS */
410/*---------------------------------------------------------------------------*/
411/** \internal
412 */
413static unsigned char *
414skip_name(unsigned char *query, const unsigned char *packet, size_t packet_len)
415{
416 const unsigned char *end = packet + packet_len;
417
418 LOG_DBG("skip name: ");
419
420 while(query < end) {
421 unsigned char n = *query;
422
423 if(n & 0xc0) {
424 /* A compression pointer is two bytes long and terminates the name. */
425 if(query + 2 > end) {
426 break;
427 }
428 LOG_DBG_("<skip-to-%d>\n", query[0] + ((n & ~0xC0) << 8));
429 return query + 2;
430 }
431
432 ++query;
433
434 if(n == 0) {
435 /* The terminating zero-length label ends the name. */
436 LOG_DBG_("\n");
437 return query;
438 }
439
440 if(query + n > end) {
441 break;
442 }
443
444 while(n > 0) {
445 LOG_DBG_("%c", *query);
446 ++query;
447 --n;
448 }
449 LOG_DBG_(".");
450 }
451
452 LOG_ERR("skip_name: name runs past end of packet\n");
453 return NULL;
454}
455/*---------------------------------------------------------------------------*/
456/** \internal
457 */
458static unsigned char *
459encode_name(unsigned char *query, const char *nameptr)
460{
461 --nameptr;
462 /* Convert hostname into suitable query format. */
463 do {
464 uint8_t n = 0;
465 char *nptr = (char *)query;
466
467 ++nameptr;
468 ++query;
469 for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
470 *query = *nameptr;
471 ++query;
472 ++n;
473 }
474 *nptr = n;
475 } while(*nameptr != 0);
476
477 /* End the the name. */
478 *query++ = 0;
479
480 return query;
481}
482/*---------------------------------------------------------------------------*/
483#if RESOLV_SUPPORTS_MDNS
484/** \internal
485 */
486static void
487mdns_announce_requested(void)
488{
489 mdns_needs_host_announce = 1;
490}
491/*---------------------------------------------------------------------------*/
492/** \internal
493 */
494static void
495start_name_collision_check(clock_time_t after)
496{
497 process_exit(&mdns_probe_process);
498 process_start(&mdns_probe_process, (void *)&after);
499}
500/*---------------------------------------------------------------------------*/
501/** \internal
502 */
503static unsigned char *
504mdns_write_announce_records(unsigned char *queryptr, uint8_t *count)
505{
506 uint8_t i;
507
508 for(i = 0; i < UIP_DS6_ADDR_NB; ++i) {
509 if(uip_ds6_if.addr_list[i].isused
510#if !RESOLV_CONF_MDNS_INCLUDE_GLOBAL_V6_ADDRS
511 && uip_is_addr_linklocal(&uip_ds6_if.addr_list[i].ipaddr)
512#endif
513 ) {
514 if(!*count) {
515 queryptr = encode_name(queryptr, resolv_hostname);
516 } else {
517 /* Use name compression to refer back to the first name */
518 *queryptr++ = 0xc0;
519 *queryptr++ = sizeof(struct dns_hdr);
520 }
521
522 *queryptr++ = (uint8_t)(NATIVE_DNS_TYPE >> 8);
523 *queryptr++ = (uint8_t)NATIVE_DNS_TYPE;
524
525 *queryptr++ = (uint8_t)((DNS_CLASS_IN | 0x8000) >> 8);
526 *queryptr++ = (uint8_t)(DNS_CLASS_IN | 0x8000);
527
528 *queryptr++ = 0;
529 *queryptr++ = 0;
530 *queryptr++ = 0;
531 *queryptr++ = 120;
532
533 *queryptr++ = 0;
534 *queryptr++ = sizeof(uip_ipaddr_t);
535
536 uip_ipaddr_copy((uip_ipaddr_t *)queryptr,
537 &uip_ds6_if.addr_list[i].ipaddr);
538 queryptr += sizeof(uip_ipaddr_t);
539 ++(*count);
540 }
541 }
542 return queryptr;
543}
544/*---------------------------------------------------------------------------*/
545/** \internal
546 * Called when we need to announce ourselves
547 */
548static size_t
549mdns_prep_host_announce_packet(void)
550{
551 static const struct {
552 uint16_t type;
553 uint16_t class;
554 uint16_t ttl[2];
555 uint16_t len;
556 uint8_t data[8];
557 } nsec_record = {
558 UIP_HTONS(DNS_TYPE_NSEC),
559 UIP_HTONS(DNS_CLASS_IN | 0x8000),
560 { 0, UIP_HTONS(120) },
561 UIP_HTONS(8),
562
563 {
564 0xc0,
565 sizeof(struct dns_hdr), /* Name compression. Re-use name of 1st record. */
566 0x00,
567 0x04,
568
569 0x00,
570 0x00,
571 0x00,
572 0x08,
573 }
574 };
575
576 /* Be aware that, unless `ARCH_DOESNT_NEED_ALIGNED_STRUCTS` is set,
577 * writing directly to the uint16_t members of this struct is an error. */
578 struct dns_hdr *hdr = (struct dns_hdr *)uip_appdata;
579 unsigned char *queryptr = (unsigned char *)uip_appdata + sizeof(*hdr);
580 uint8_t total_answers = 0;
581
582 /* Zero out the header */
583 memset((void *)hdr, 0, sizeof(*hdr));
584 hdr->flags1 |= DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE;
585
586 queryptr = mdns_write_announce_records(queryptr, &total_answers);
587
588 /* We now need to add an NSEC record to indicate
589 * that this is all there is.
590 */
591 if(!total_answers) {
592 queryptr = encode_name(queryptr, resolv_hostname);
593 } else {
594 /* Name compression. Re-using the name of first record. */
595 *queryptr++ = 0xc0;
596 *queryptr++ = sizeof(*hdr);
597 }
598
599 memcpy((void *)queryptr, (void *)&nsec_record, sizeof(nsec_record));
600
601 queryptr += sizeof(nsec_record);
602
603 /* This platform might be picky about alignment. To avoid the possibility
604 * of doing an unaligned write, we are going to do this manually. */
605 ((uint8_t *)&hdr->numanswers)[1] = total_answers;
606 ((uint8_t *)&hdr->numextrarr)[1] = 1;
607
608 return queryptr - (unsigned char *)uip_appdata;
609}
610#endif /* RESOLV_SUPPORTS_MDNS */
611/*---------------------------------------------------------------------------*/
612static char
613try_next_server(struct namemap *namemapptr)
614{
615 namemapptr->server++;
616 if(uip_nameserver_get(namemapptr->server) != NULL) {
617 LOG_DBG("Using server ");
618 LOG_DBG_6ADDR(uip_nameserver_get(namemapptr->server));
619 LOG_DBG_(", num %u\n", namemapptr->server);
620 namemapptr->retries = 0;
621 return 1;
622 }
623 LOG_DBG("No nameserver, num %u\n", namemapptr->server);
624 namemapptr->server = 0;
625 return 0;
626}
627/*---------------------------------------------------------------------------*/
628/** \internal
629 * Runs through the list of names to see if there are any that have
630 * not yet been queried and, if so, sends out a query.
631 */
632static void
633check_entries(void)
634{
635 uint8_t i;
636
637 for(i = 0; i < RESOLV_ENTRIES; ++i) {
638 struct namemap *namemapptr = &names[i];
639 if(namemapptr->state == STATE_NEW || namemapptr->state == STATE_ASKING) {
640 etimer_set(&retry, CLOCK_SECOND / 4);
641 if(namemapptr->state == STATE_ASKING) {
642 if(namemapptr->tmr == 0 || --namemapptr->tmr == 0) {
643#if RESOLV_SUPPORTS_MDNS
644 if(++namemapptr->retries ==
645 (namemapptr->is_mdns ? RESOLV_CONF_MAX_MDNS_RETRIES :
647#else /* RESOLV_SUPPORTS_MDNS */
648 if(++namemapptr->retries == RESOLV_CONF_MAX_RETRIES)
649#endif /* RESOLV_SUPPORTS_MDNS */
650 {
651 /* Try the next server (if possible) before failing. Otherwise
652 simply mark the entry as failed. */
653 if(try_next_server(namemapptr) == 0) {
654 /* STATE_ERROR basically means "not found". */
655 namemapptr->state = STATE_ERROR;
656
657#if RESOLV_SUPPORTS_RECORD_EXPIRATION
658 /* Keep the "not found" error valid for 30 seconds */
659 namemapptr->expiration = clock_seconds() + 30;
660#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
661
662 resolv_found(namemapptr->name, NULL);
663 continue;
664 }
665 }
666 namemapptr->tmr = namemapptr->retries * namemapptr->retries * 3;
667
668#if RESOLV_SUPPORTS_MDNS
669 if(namemapptr->is_probe) {
670 /* Probing retries are much more aggressive, 250ms */
671 namemapptr->tmr = 2;
672 }
673#endif /* RESOLV_SUPPORTS_MDNS */
674 } else {
675 /* Its timer has not run out, so we move on to next entry. */
676 continue;
677 }
678 } else {
679 namemapptr->state = STATE_ASKING;
680 namemapptr->tmr = 1;
681 namemapptr->retries = 0;
682 }
683
684 struct dns_hdr *hdr = (struct dns_hdr *)uip_appdata;
685 memset(hdr, 0, sizeof(struct dns_hdr));
686 hdr->id = random_rand();
687 namemapptr->id = hdr->id;
688
689#if RESOLV_SUPPORTS_MDNS
690 if(!namemapptr->is_mdns || namemapptr->is_probe) {
691 hdr->flags1 = DNS_FLAG1_RD;
692 }
693 if(namemapptr->is_mdns) {
694 hdr->id = 0;
695 }
696#else /* RESOLV_SUPPORTS_MDNS */
697 hdr->flags1 = DNS_FLAG1_RD;
698#endif /* RESOLV_SUPPORTS_MDNS */
699
700 hdr->numquestions = UIP_HTONS(1);
701 uint8_t *query = (unsigned char *)uip_appdata + sizeof(*hdr);
702 query = encode_name(query, namemapptr->name);
703
704#if RESOLV_SUPPORTS_MDNS
705 if(namemapptr->is_probe) {
706 *query++ = (uint8_t)((DNS_TYPE_ANY) >> 8);
707 *query++ = (uint8_t)((DNS_TYPE_ANY));
708 } else
709#endif /* RESOLV_SUPPORTS_MDNS */
710 {
711 *query++ = (uint8_t)(NATIVE_DNS_TYPE >> 8);
712 *query++ = (uint8_t)NATIVE_DNS_TYPE;
713 }
714 *query++ = (uint8_t)(DNS_CLASS_IN >> 8);
715 *query++ = (uint8_t)DNS_CLASS_IN;
716#if RESOLV_SUPPORTS_MDNS
717 if(namemapptr->is_mdns) {
718 if(namemapptr->is_probe) {
719 /* This is our conflict detection request.
720 * In order to be in compliance with the MDNS
721 * spec, we need to add the records we are proposing
722 * to the rrauth section.
723 */
724 uint8_t count = 0;
725
726 query = mdns_write_announce_records(query, &count);
727 hdr->numauthrr = UIP_HTONS(count);
728 }
729 uip_udp_packet_sendto(resolv_conn, uip_appdata,
730 (query - (uint8_t *)uip_appdata),
731 &resolv_mdns_addr, UIP_HTONS(MDNS_PORT));
732
733 LOG_DBG("(i=%d) Sent MDNS %s for \"%s\"\n", i,
734 namemapptr->is_probe ? "probe" : "request", namemapptr->name);
735 } else {
736 uip_udp_packet_sendto(resolv_conn, uip_appdata,
737 (query - (uint8_t *)uip_appdata),
738 (const uip_ipaddr_t *)
739 uip_nameserver_get(namemapptr->server),
740 UIP_HTONS(DNS_PORT));
741
742 LOG_DBG("(i=%d) Sent DNS request for \"%s\"\n", i,
743 namemapptr->name);
744 }
745#else /* RESOLV_SUPPORTS_MDNS */
746 uip_udp_packet_sendto(resolv_conn, uip_appdata,
747 (query - (uint8_t *)uip_appdata),
748 uip_nameserver_get(namemapptr->server),
749 UIP_HTONS(DNS_PORT));
750 LOG_DBG("(i=%d) Sent DNS request for \"%s\"\n", i,
751 namemapptr->name);
752#endif /* RESOLV_SUPPORTS_MDNS */
753 break;
754 }
755 }
756}
757/*---------------------------------------------------------------------------*/
758/** \internal
759 * Called when new UDP data arrives.
760 */
761static void
763{
764 int8_t i = 0;
765 struct dns_hdr const *hdr = (struct dns_hdr *)uip_appdata;
766
767 /*
768 * Make sure the fixed-size DNS header is fully present before reading
769 * any of its fields.
770 */
771 if(uip_datalen() < sizeof(*hdr)) {
772 LOG_DBG("Packet too short to contain a DNS header\n");
773 return;
774 }
775
776 const uint8_t is_request = (hdr->flags1 & ~1) == 0 && hdr->flags2 == 0;
777
778 /* We only care about the question(s) and the answers. The authrr
779 * and the extrarr are simply discarded.
780 */
781 uint16_t nquestions = uip_ntohs(hdr->numquestions);
782 uint16_t nanswers = uip_ntohs(hdr->numanswers);
783
784 unsigned char *queryptr = (unsigned char *)hdr + sizeof(*hdr);
785 i = 0;
786
787 LOG_DBG("flags1=0x%02X flags2=0x%02X nquestions=%d, nanswers=%d, " \
788 "nauthrr=%d, nextrarr=%d\n",
789 hdr->flags1, hdr->flags2, nquestions, nanswers,
790 uip_ntohs(hdr->numauthrr),
791 uip_ntohs(hdr->numextrarr));
792
793 if(is_request && nquestions == 0) {
794 /* Skip requests with no questions. */
795 LOG_DBG("Skipping request with no questions\n");
796 return;
797 }
798
799/** QUESTION HANDLING SECTION ************************************************/
800
801 while(nquestions > 0) {
802 /*
803 * Skip past the question name to reach the question record. The same
804 * name is also used below for the mDNS comparison, so keep a pointer
805 * to it before advancing.
806 */
807 unsigned char *qname = queryptr;
808 unsigned char *qrecord = skip_name(qname, uip_appdata, uip_datalen());
809 if(qrecord == NULL) {
810 return;
811 }
812
813 /* Advance to the next question for the following iteration. */
814 queryptr = qrecord + sizeof(struct dns_question);
815 --nquestions;
816
817#if RESOLV_SUPPORTS_MDNS
818 if(!is_request) {
819 /* If this isn't a request, we don't need to bother
820 * looking at the individual questions. For the most
821 * part, this loop to just used to skip past them.
822 */
823 continue;
824 }
825
826 {
827 struct dns_question *question = (struct dns_question *)qrecord;
828
829 /*
830 * Make sure the fixed-size question record lies within the packet
831 * before dereferencing it.
832 */
833 if(qrecord + sizeof(struct dns_question) >
834 (const unsigned char *)uip_appdata + uip_datalen()) {
835 return;
836 }
837
838#if !ARCH_DOESNT_NEED_ALIGNED_STRUCTS
839 static struct dns_question aligned;
840 memcpy(&aligned, question, sizeof(aligned));
841 question = &aligned;
842#endif /* !ARCH_DOESNT_NEED_ALIGNED_STRUCTS */
843
844 LOG_DBG("Question %d: type=%d class=%d\n", ++i,
845 uip_htons(question->type), uip_htons(question->class));
846
847 if((uip_ntohs(question->class) & 0x7FFF) != DNS_CLASS_IN ||
848 (question->type != UIP_HTONS(DNS_TYPE_ANY) &&
849 question->type != UIP_HTONS(NATIVE_DNS_TYPE))) {
850 /* Skip unrecognised records. */
851 continue;
852 }
853
854 if(!dns_name_isequal(qname, resolv_hostname, uip_appdata, uip_datalen())) {
855 continue;
856 }
857
858 LOG_DBG("Received MDNS request for us\n");
859
860 if(mdns_state == MDNS_STATE_READY) {
861 /* We only send immediately if this isn't an MDNS request.
862 * Otherwise, we schedule ourselves to send later.
863 */
864 if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT)) {
865 mdns_announce_requested();
866 } else {
867 uip_udp_packet_sendto(resolv_conn, uip_appdata,
868 mdns_prep_host_announce_packet(),
869 &UIP_IP_BUF->srcipaddr,
870 UIP_UDP_BUF->srcport);
871 }
872 return;
873 } else {
874 uint16_t nauthrr;
875
876 LOG_DBG("But we are still probing. Waiting...\n");
877
878 /* We are still probing. We need to do the mDNS
879 * probe race condition check here and make sure
880 * we don't need to delay probing for a second.
881 */
882 nauthrr = uip_ntohs(hdr->numauthrr);
883
884 /* For now, we will always restart the collision check if
885 * there are *any* authority records present.
886 * In the future we should follow the spec more closely,
887 * but this should eventually converge to something reasonable.
888 */
889 if(nauthrr) {
890 start_name_collision_check(CLOCK_SECOND);
891 }
892 }
893 }
894#endif /* RESOLV_SUPPORTS_MDNS */
895 }
896
897/** ANSWER HANDLING SECTION **************************************************/
898 struct namemap *namemapptr = NULL;
899
900#if RESOLV_SUPPORTS_MDNS
901 if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT) && hdr->id == 0) {
902 /* OK, this was from MDNS. Things get a little weird here,
903 * because we can't use the `id` field. We will look up the
904 * appropriate request in a later step. */
905
906 i = -1;
907 namemapptr = NULL;
908 } else
909#endif /* RESOLV_SUPPORTS_MDNS */
910 {
911 for(i = 0; i < RESOLV_ENTRIES; ++i) {
912 namemapptr = &names[i];
913 if(namemapptr->state == STATE_ASKING &&
914 namemapptr->id == hdr->id) {
915 break;
916 }
917 }
918
919 if(i >= RESOLV_ENTRIES || i < 0 || namemapptr->state != STATE_ASKING) {
920 LOG_DBG("DNS response has bad ID (%04X)\n", uip_ntohs(hdr->id));
921 return;
922 }
923
924 LOG_DBG("Incoming response for \"%s\"\n", namemapptr->name);
925
926 /* We'll change this to DONE when we find the record. */
927 namemapptr->state = STATE_ERROR;
928 namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
929
930#if RESOLV_SUPPORTS_RECORD_EXPIRATION
931 /* If we remain in the error state, keep it cached for 30 seconds. */
932 namemapptr->expiration = clock_seconds() + 30;
933#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
934
935 /* Check for error. If so, call callback to inform. */
936 if(namemapptr->err != 0) {
937 namemapptr->state = STATE_ERROR;
938 resolv_found(namemapptr->name, NULL);
939 return;
940 }
941 }
942
943 i = 0;
944
945 /* Answer parsing loop */
946 while(nanswers > 0) {
947 /*
948 * The fixed part of a DNS answer record (type, class, ttl, len) is
949 * 10 bytes; the rdata that follows is ans->len bytes long.
950 */
951 const unsigned char *packet_end =
952 (const unsigned char *)uip_appdata + uip_datalen();
953 unsigned char *arecord = skip_name(queryptr, uip_appdata, uip_datalen());
954 if(arecord == NULL) {
955 break;
956 }
957
958 struct dns_answer *ans = (struct dns_answer *)arecord;
959
960 /*
961 * Make sure the fixed-size answer header lies within the packet before
962 * dereferencing it.
963 */
964 if(arecord + 10 > packet_end) {
965 break;
966 }
967
968#if !ARCH_DOESNT_NEED_ALIGNED_STRUCTS
969 {
970 /*
971 * Copy only the bytes that are actually present in the packet. The
972 * rdata (ipaddr) tail is read only after the length check below has
973 * confirmed the full record is present, so a short copy here is safe.
974 */
975 static struct dns_answer aligned;
976 size_t avail = (size_t)(packet_end - arecord);
977 memcpy(&aligned, ans, avail < sizeof(aligned) ? avail : sizeof(aligned));
978 ans = &aligned;
979 }
980#endif /* !ARCH_DOESNT_NEED_ALIGNED_STRUCTS */
981
982 if(LOG_DBG_ENABLED) {
983 char debug_name[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1];
984 decode_name(queryptr, debug_name, uip_appdata, uip_datalen());
985 LOG_DBG("Answer %d: \"%s\", type %d, class %d, ttl %"PRIu32", length %d\n",
986 ++i, debug_name, uip_ntohs(ans->type),
987 uip_ntohs(ans->class) & 0x7FFF,
988 (uint32_t)((uint32_t)uip_ntohs(ans->ttl[0]) << 16) |
989 (uint32_t)uip_ntohs(ans->ttl[1]), uip_ntohs(ans->len));
990 }
991
992 /* Check the class and length of the answer to make sure
993 * it matches what we are expecting
994 */
995 if((uip_ntohs(ans->class) & 0x7FFF) != DNS_CLASS_IN ||
996 ans->len != UIP_HTONS(sizeof(uip_ipaddr_t))) {
997 goto skip_to_next_answer;
998 }
999
1000 if(ans->type != UIP_HTONS(NATIVE_DNS_TYPE)) {
1001 goto skip_to_next_answer;
1002 }
1003
1004 /*
1005 * The address rdata is read below; make sure the full record (10-byte
1006 * header plus ans->len bytes of rdata) lies within the packet.
1007 */
1008 if(arecord + 10 + uip_ntohs(ans->len) > packet_end) {
1009 LOG_ERR("Answer rdata runs past end of packet\n");
1010 break;
1011 }
1012
1013#if RESOLV_SUPPORTS_MDNS
1014 if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT) && hdr->id == 0) {
1015 int8_t available_i = RESOLV_ENTRIES;
1016
1017 LOG_DBG("MDNS query\n");
1018
1019 /* For MDNS, we need to actually look up the name we
1020 * are looking for.
1021 */
1022 for(i = 0; i < RESOLV_ENTRIES; ++i) {
1023 namemapptr = &names[i];
1024 if(dns_name_isequal(queryptr, namemapptr->name, uip_appdata,
1025 uip_datalen())) {
1026 break;
1027 }
1028 if((namemapptr->state == STATE_UNUSED)
1029#if RESOLV_SUPPORTS_RECORD_EXPIRATION
1030 || (namemapptr->state == STATE_DONE &&
1031 clock_seconds() > namemapptr->expiration)
1032#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
1033 ) {
1034 available_i = i;
1035 }
1036 }
1037 if(i == RESOLV_ENTRIES) {
1038 /*
1039 * The name is not cached yet. We need a free (or expired) slot to
1040 * store it in; bail out before touching names[] if there is none,
1041 * since available_i is then still RESOLV_ENTRIES (one past the end).
1042 */
1043 if(available_i == RESOLV_ENTRIES) {
1044 LOG_DBG("Not enough room to keep track of unsolicited MDNS answer\n");
1045
1046 if(dns_name_isequal(queryptr, resolv_hostname, uip_appdata,
1047 uip_datalen())) {
1048 /* Oh snap, they say they are us! We had better report them... */
1049 resolv_found(resolv_hostname, (uip_ipaddr_t *)ans->ipaddr);
1050 }
1051 namemapptr = NULL;
1052 goto skip_to_next_answer;
1053 }
1054
1055 LOG_DBG("Unsolicited MDNS response\n");
1056 i = available_i;
1057 namemapptr = &names[i];
1058 if(!decode_name(queryptr, namemapptr->name,
1060 LOG_DBG("MDNS name too big to cache\n");
1061 namemapptr = NULL;
1062 goto skip_to_next_answer;
1063 }
1064 }
1065 namemapptr = &names[i];
1066 } else
1067#endif /* RESOLV_SUPPORTS_MDNS */
1068 {
1069 /* This will force us to stop even if there are more answers. */
1070 nanswers = 1;
1071 }
1072
1073/* This is disabled for now, so that we don't fail on CNAME records.
1074 #if RESOLV_VERIFY_ANSWER_NAMES
1075 if(namemapptr && !dns_name_isequal(queryptr, namemapptr->name, uip_appdata, uip_datalen())) {
1076 LOG_DBG("Answer name doesn't match the query!\n");
1077 goto skip_to_next_answer;
1078 }
1079 #endif
1080 */
1081
1082 LOG_DBG("Answer for \"%s\" is usable\n", namemapptr->name);
1083
1084 namemapptr->state = STATE_DONE;
1085#if RESOLV_SUPPORTS_RECORD_EXPIRATION
1086 namemapptr->expiration = (uint32_t)uip_ntohs(ans->ttl[0]) << 16 |
1087 (uint32_t)uip_ntohs(ans->ttl[1]);
1088 LOG_DBG("Expires in %lu seconds\n", namemapptr->expiration);
1089
1090 namemapptr->expiration += clock_seconds();
1091#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
1092
1093 uip_ipaddr_copy(&namemapptr->ipaddr, (uip_ipaddr_t *)ans->ipaddr);
1094
1095 resolv_found(namemapptr->name, &namemapptr->ipaddr);
1096 break;
1097
1098skip_to_next_answer:
1099 /*
1100 * Advance past this record's name (already validated above via
1101 * arecord), its 10-byte header and its rdata. The next iteration
1102 * re-validates the resulting pointer through skip_name().
1103 */
1104 queryptr = arecord + 10 + uip_ntohs(ans->len);
1105 --nanswers;
1106 }
1107
1108 /* Got to this point there's no answer, try next nameserver if available
1109 since this one doesn't know the answer */
1110#if RESOLV_SUPPORTS_MDNS
1111 if(nanswers == 0 && UIP_UDP_BUF->srcport != UIP_HTONS(MDNS_PORT)
1112 && hdr->id != 0)
1113#else
1114 if(nanswers == 0)
1115#endif
1116 {
1117 if(try_next_server(namemapptr)) {
1118 namemapptr->state = STATE_ASKING;
1119 process_post(&resolv_process, PROCESS_EVENT_TIMER, NULL);
1120 }
1121 }
1122}
1123/*---------------------------------------------------------------------------*/
1124#if RESOLV_SUPPORTS_MDNS
1125/**
1126 * \brief Changes the local hostname advertised by MDNS.
1127 * \param hostname The new hostname to advertise.
1128 */
1129void
1130resolv_set_hostname(const char *hostname)
1131{
1132 strncpy(resolv_hostname, hostname, RESOLV_CONF_MAX_DOMAIN_NAME_SIZE);
1133 /* strncpy() does not terminate when the source is too long; do it here so
1134 * the strlen() calls below cannot read past the end of the buffer. */
1135 resolv_hostname[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE] = '\0';
1136
1137 /* Add the .local suffix if it isn't already there */
1138 if(strlen(resolv_hostname) < 7 ||
1139 strcasecmp(resolv_hostname + strlen(resolv_hostname) - 6, ".local") != 0) {
1140 strncat(resolv_hostname, ".local",
1141 RESOLV_CONF_MAX_DOMAIN_NAME_SIZE - strlen(resolv_hostname));
1142 }
1143
1144 LOG_DBG("hostname changed to \"%s\"\n", resolv_hostname);
1145
1146 start_name_collision_check(0);
1147}
1148/*---------------------------------------------------------------------------*/
1149/**
1150 * \brief Returns the local hostname being advertised via MDNS.
1151 * \return C-string containing the local hostname.
1152 */
1153const char *
1154resolv_get_hostname(void)
1155{
1156 return resolv_hostname;
1157}
1158/*---------------------------------------------------------------------------*/
1159/** \internal
1160 * Process for probing for name conflicts.
1161 */
1162PROCESS_THREAD(mdns_probe_process, ev, data)
1163{
1164 static struct etimer delay;
1165
1166 PROCESS_BEGIN();
1167 mdns_state = MDNS_STATE_WAIT_BEFORE_PROBE;
1168
1169 LOG_DBG("mdns-probe: Process (re)started\n");
1170
1171 /* Wait extra time if specified in data */
1172 if(NULL != data) {
1173 LOG_DBG("mdns-probe: Probing will begin in %ld clocks\n",
1174 (long)*(clock_time_t *)data);
1175 etimer_set(&delay, *(clock_time_t *)data);
1176 PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);
1177 }
1178
1179 /* We need to wait a random (0-250ms) period of time before
1180 * probing to be in compliance with the MDNS spec. */
1181 etimer_set(&delay, CLOCK_SECOND * (random_rand() & 0xFF) / 1024);
1182 PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);
1183
1184 /* Begin searching for our name. */
1185 mdns_state = MDNS_STATE_PROBING;
1186 resolv_query(resolv_hostname);
1187
1188 do {
1190 } while(strcasecmp(resolv_hostname, data) != 0);
1191
1192 mdns_state = MDNS_STATE_READY;
1193 mdns_announce_requested();
1194
1195 LOG_DBG("mdns-probe: Finished probing\n");
1196
1197 PROCESS_END();
1198}
1199#endif /* RESOLV_SUPPORTS_MDNS */
1200/*---------------------------------------------------------------------------*/
1201/** \internal
1202 * The main UDP function.
1203 */
1204PROCESS_THREAD(resolv_process, ev, data)
1205{
1206 PROCESS_BEGIN();
1207
1208 memset(names, 0, sizeof(names));
1209
1211
1212 LOG_DBG("Process started\n");
1213
1214 resolv_conn = udp_new(NULL, 0, NULL);
1215 if(resolv_conn == NULL) {
1216 LOG_ERR("No UDP connection available, exiting the process!\n");
1217 PROCESS_EXIT();
1218 }
1219
1220#if RESOLV_SUPPORTS_MDNS
1221 LOG_DBG("Supports MDNS\n");
1222 uip_udp_bind(resolv_conn, UIP_HTONS(MDNS_PORT));
1223
1224 uip_ds6_maddr_add(&resolv_mdns_addr);
1225
1226 resolv_set_hostname(CONTIKI_CONF_DEFAULT_HOSTNAME);
1227#endif /* RESOLV_SUPPORTS_MDNS */
1228
1229 while(1) {
1231
1232 if(ev == PROCESS_EVENT_TIMER) {
1233 tcpip_poll_udp(resolv_conn);
1234 } else if(ev == tcpip_event && uip_udp_conn == resolv_conn) {
1235 if(uip_newdata()) {
1236 newdata();
1237 }
1238 if(uip_poll()) {
1239#if RESOLV_SUPPORTS_MDNS
1240 if(mdns_needs_host_announce) {
1241 size_t len;
1242
1243 LOG_DBG("Announcing that we are \"%s\"\n",
1244 resolv_hostname);
1245
1246 memset(uip_appdata, 0, sizeof(struct dns_hdr));
1247 len = mdns_prep_host_announce_packet();
1248 uip_udp_packet_sendto(resolv_conn, uip_appdata,
1249 len, &resolv_mdns_addr, UIP_HTONS(MDNS_PORT));
1250 mdns_needs_host_announce = 0;
1251
1252 /*
1253 * Poll again in case this fires at the same time that
1254 * the event timer did.
1255 */
1256 tcpip_poll_udp(resolv_conn);
1257 } else
1258#endif /* RESOLV_SUPPORTS_MDNS */
1259 {
1260 check_entries();
1261 }
1262 }
1263 }
1264
1265#if RESOLV_SUPPORTS_MDNS
1266 if(mdns_needs_host_announce) {
1267 tcpip_poll_udp(resolv_conn);
1268 }
1269#endif /* RESOLV_SUPPORTS_MDNS */
1270 }
1271
1272 PROCESS_END();
1273}
1274/*---------------------------------------------------------------------------*/
1275static void
1276init(void)
1277{
1278 static uint8_t initialized = 0;
1279 if(!initialized) {
1280 process_start(&resolv_process, NULL);
1281 initialized = 1;
1282 }
1283}
1284/*---------------------------------------------------------------------------*/
1285#if RESOLV_AUTO_REMOVE_TRAILING_DOTS
1286static const char *
1287remove_trailing_dots(const char *name)
1288{
1289 static char dns_name_without_dots[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1];
1290 size_t len = strlen(name);
1291
1292 if(len && name[len - 1] == '.') {
1293 strncpy(dns_name_without_dots, name, RESOLV_CONF_MAX_DOMAIN_NAME_SIZE);
1294 while(len && (dns_name_without_dots[len - 1] == '.')) {
1295 dns_name_without_dots[--len] = 0;
1296 }
1297 name = dns_name_without_dots;
1298 }
1299 return name;
1300}
1301#else /* RESOLV_AUTO_REMOVE_TRAILING_DOTS */
1302#define remove_trailing_dots(x) (x)
1303#endif /* RESOLV_AUTO_REMOVE_TRAILING_DOTS */
1304/*---------------------------------------------------------------------------*/
1305/**
1306 * Queues a name so that a question for the name will be sent out.
1307 *
1308 * \param name The hostname that is to be queried.
1309 */
1310void
1311resolv_query(const char *name)
1312{
1313 uint8_t lseqi = 0, lseq = 0, i = 0;
1314 struct namemap *nameptr = 0;
1315
1316 init();
1317
1318 /* Remove trailing dots, if present. */
1319 name = remove_trailing_dots(name);
1320
1321 for(i = 0; i < RESOLV_ENTRIES; ++i) {
1322 nameptr = &names[i];
1323 if(0 == strcasecmp(nameptr->name, name)) {
1324 break;
1325 }
1326 if((nameptr->state == STATE_UNUSED)
1327#if RESOLV_SUPPORTS_RECORD_EXPIRATION
1328 || (nameptr->state == STATE_DONE && clock_seconds() > nameptr->expiration)
1329#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
1330 ) {
1331 lseqi = i;
1332 lseq = 255;
1333 } else if(seqno - nameptr->seqno > lseq) {
1334 lseq = seqno - nameptr->seqno;
1335 lseqi = i;
1336 }
1337 }
1338
1339 if(i == RESOLV_ENTRIES) {
1340 i = lseqi;
1341 nameptr = &names[i];
1342 }
1343
1344 LOG_DBG("Starting query for \"%s\"\n", name);
1345
1346 memset(nameptr, 0, sizeof(*nameptr));
1347
1348 strncpy(nameptr->name, name, sizeof(nameptr->name) - 1);
1349 nameptr->state = STATE_NEW;
1350 nameptr->seqno = seqno;
1351 ++seqno;
1352
1353#if RESOLV_SUPPORTS_MDNS
1354 {
1355 size_t name_len = strlen(name);
1356 const char local_suffix[] = "local";
1357
1358 if(name_len > (sizeof(local_suffix) - 1) &&
1359 strcasecmp(name + name_len - (sizeof(local_suffix) - 1),
1360 local_suffix) == 0) {
1361 LOG_DBG("Using MDNS to look up \"%s\"\n", name);
1362 nameptr->is_mdns = true;
1363 } else {
1364 nameptr->is_mdns = false;
1365 }
1366 }
1367 nameptr->is_probe = mdns_state == MDNS_STATE_PROBING &&
1368 strcmp(nameptr->name, resolv_hostname) == 0;
1369#endif /* RESOLV_SUPPORTS_MDNS */
1370
1371 /* Force check_entires() to run on our process. */
1372 process_post(&resolv_process, PROCESS_EVENT_TIMER, 0);
1373}
1374/*---------------------------------------------------------------------------*/
1375/**
1376 * Look up a hostname in the array of known hostnames.
1377 *
1378 * \note This function only looks in the internal array of known
1379 * hostnames, it does not send out a query for the hostname if none
1380 * was found. The function resolv_query() can be used to send a query
1381 * for a hostname.
1382 *
1383 */
1384resolv_status_t
1385resolv_lookup(const char *name, uip_ipaddr_t **ipaddr)
1386{
1387 resolv_status_t ret = RESOLV_STATUS_UNCACHED;
1388
1389 /* Remove trailing dots, if present. */
1390 name = remove_trailing_dots(name);
1391
1392#if UIP_CONF_LOOPBACK_INTERFACE
1393 if(strcmp(name, "localhost") == 0) {
1394 static uip_ipaddr_t loopback =
1395 { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1396 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } };
1397 if(ipaddr) {
1398 *ipaddr = &loopback;
1399 }
1401 }
1402#endif /* UIP_CONF_LOOPBACK_INTERFACE */
1403
1404 /* Walk through the list to see if the name is in there. */
1405 uint8_t i;
1406 for(i = 0; i < RESOLV_ENTRIES; ++i) {
1407 struct namemap *nameptr = &names[i];
1408
1409 if(strcasecmp(name, nameptr->name) == 0) {
1410 switch(nameptr->state) {
1411 case STATE_DONE:
1413#if RESOLV_SUPPORTS_RECORD_EXPIRATION
1414 if(clock_seconds() > nameptr->expiration) {
1416 }
1417#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
1418 break;
1419 case STATE_NEW:
1420 case STATE_ASKING:
1422 break;
1423 /* Almost certainly a not-found error from server */
1424 case STATE_ERROR:
1426#if RESOLV_SUPPORTS_RECORD_EXPIRATION
1427 if(clock_seconds() > nameptr->expiration) {
1429 }
1430#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
1431 break;
1432 }
1433
1434 if(ipaddr) {
1435 *ipaddr = &nameptr->ipaddr;
1436 }
1437
1438 /* Break out of for loop. */
1439 break;
1440 }
1441 }
1442
1443 if(LOG_DBG_ENABLED) {
1444 switch(ret) {
1446 if(ipaddr) {
1447 LOG_DBG("Found \"%s\" in cache => ", name);
1448 const uip_ipaddr_t *addr = *ipaddr;
1449 LOG_DBG_6ADDR(addr);
1450 LOG_DBG_("\n");
1451 break;
1452 }
1453 default:
1454 LOG_DBG("\"%s\" is NOT cached\n", name);
1455 break;
1456 }
1457 }
1458
1459 return ret;
1460}
1461/*---------------------------------------------------------------------------*/
1462/** \internal
1463 * Callback function which is called when a hostname is found.
1464 *
1465 */
1466static void
1467resolv_found(char *name, uip_ipaddr_t *ipaddr)
1468{
1469#if RESOLV_SUPPORTS_MDNS
1470 if(strncasecmp(resolv_hostname, name, strlen(resolv_hostname)) == 0 &&
1471 ipaddr && !uip_ds6_is_my_addr(ipaddr)) {
1472 uint8_t i;
1473
1474 if(mdns_state == MDNS_STATE_PROBING) {
1475 /* We found this new name while probing.
1476 * We must now rename ourselves.
1477 */
1478 LOG_DBG("Name collision detected for \"%s\"\n", name);
1479
1480 /* Remove the ".local" suffix. */
1481 resolv_hostname[strlen(resolv_hostname) - 6] = 0;
1482
1483 /* Append the last three hex parts of the link-level address. */
1484 for(i = 0; i < 3; ++i) {
1485 uint8_t val = uip_lladdr.addr[(UIP_LLADDR_LEN - 3) + i];
1486
1487 char append_str[4] = "-XX";
1488
1489 append_str[2] = (((val & 0xF) > 9) ? 'a' : '0') + (val & 0xF);
1490 val >>= 4;
1491 append_str[1] = (((val & 0xF) > 9) ? 'a' : '0') + (val & 0xF);
1492 /* -1 in order to fit the terminating null byte. */
1493 strncat(resolv_hostname, append_str,
1494 sizeof(resolv_hostname) - strlen(resolv_hostname) - 1);
1495 }
1496
1497 /* Re-add the .local suffix */
1498 strncat(resolv_hostname, ".local",
1499 RESOLV_CONF_MAX_DOMAIN_NAME_SIZE - strlen(resolv_hostname));
1500
1501 start_name_collision_check(CLOCK_SECOND * 5);
1502 } else if(mdns_state == MDNS_STATE_READY) {
1503 /* We found a collision after we had already asserted
1504 * that we owned this name. We need to immediately
1505 * and explicitly begin probing.
1506 */
1507 LOG_DBG("Possible name collision, probing...\n");
1508 start_name_collision_check(0);
1509 }
1510 } else
1511#endif /* RESOLV_SUPPORTS_MDNS */
1512 if(ipaddr) {
1513 LOG_DBG("Found address for \"%s\" => ", name);
1514 LOG_DBG_6ADDR(ipaddr);
1515 LOG_DBG_("\n");
1516 } else {
1517 LOG_DBG("Unable to retrieve address for \"%s\"\n", name);
1518 }
1519 process_post(PROCESS_BROADCAST, resolv_event_found, name);
1520}
1521/*---------------------------------------------------------------------------*/
1522#endif /* UIP_UDP */
1523
1524/** @} */
1525/** @} */
unsigned long clock_seconds(void)
Get the current value of the platform seconds.
Definition clock.c:130
static int init(void)
Definition cc2538-rf.c:556
static volatile uint64_t count
Num.
Definition clock.c:50
#define CLOCK_SECOND
A second, measured in system clock time.
Definition clock.h:105
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition etimer.c:177
#define PROCESS(name, strname)
Declare a process.
Definition process.h:309
#define PROCESS_EXIT()
Exit the currently running process.
Definition process.h:202
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition process.h:143
int process_post(struct process *p, process_event_t ev, process_data_t data)
Post an asynchronous event.
Definition process.c:325
void process_exit(struct process *p)
Cause a process to exit.
Definition process.c:222
process_event_t process_alloc_event(void)
Allocate a global event number.
Definition process.c:111
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition process.h:122
#define PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
Definition process.h:159
#define PROCESS_END()
Define the end of a process.
Definition process.h:133
void process_start(struct process *p, process_data_t data)
Start a process.
Definition process.c:121
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition process.h:275
process_event_t tcpip_event
The uIP event.
Definition tcpip.c:62
struct uip_udp_conn * udp_new(const uip_ipaddr_t *ripaddr, uint16_t port, void *appstate)
Create a new UDP connection.
Definition tcpip.c:264
void tcpip_poll_udp(struct uip_udp_conn *conn)
Cause a specified UDP connection to be polled.
Definition tcpip.c:739
uip_lladdr_t uip_lladdr
Host L2 address.
Definition uip6.c:107
void * uip_appdata
Pointer to the application data in the packet buffer.
Definition uip6.c:148
#define UIP_LLADDR_LEN
802.15.4 address
Definition uip.h:145
#define uip_is_addr_linklocal(a)
is addr (a) a link local unicast address, see RFC 4291 i.e.
Definition uip.h:1766
uip_ipaddr_t * uip_nameserver_get(uint8_t num)
Get a Nameserver ip address given in RA.
uip_ds6_netif_t uip_ds6_if
The single interface.
Definition uip-ds6.c:74
#define UIP_IP_BUF
Direct access to IPv6 header.
Definition uip.h:71
#define uip_datalen()
The length of any incoming data that is currently available (if available) in the uip_appdata buffer.
Definition uip.h:593
#define uip_newdata()
Is new incoming data available?
Definition uip.h:680
#define uip_poll()
Is the connection being polled by uIP?
Definition uip.h:759
#define uip_udp_bind(conn, port)
Bind a UDP connection to a local port.
Definition uip.h:830
#define UIP_HTONS(n)
Convert 16-bit quantity from host byte order to network byte order.
Definition uip.h:1157
#define uip_ipaddr_copy(dest, src)
Copy an IP address from one place to another.
Definition uip.h:969
uint16_t uip_htons(uint16_t val)
Convert a 16-bit quantity from host byte order to network byte order.
Definition uip6.c:2437
void resolv_query(const char *name)
Queues a name so that a question for the name will be sent out.
Definition resolv.c:1311
#define RESOLV_CONF_MAX_RETRIES
The maximum number of retries when asking for a name.
Definition resolv.c:103
resolv_status_t resolv_lookup(const char *name, uip_ipaddr_t **ipaddr)
Look up a hostname in the array of known hostnames.
Definition resolv.c:1385
static void newdata(void)
Definition resolv.c:762
process_event_t resolv_event_found
Event that is broadcasted when a DNS name has been resolved.
Definition resolv.c:243
Header file for the logging system.
Header file for generating non-cryptographic random numbers.
uIP DNS resolver code header file.
@ RESOLV_STATUS_RESOLVING
This hostname is in the process of being resolved.
Definition resolv.h:73
@ RESOLV_STATUS_EXPIRED
Hostname was found, but it's status has expired.
Definition resolv.h:62
@ RESOLV_STATUS_NOT_FOUND
The server has returned a not-found response for this domain name.
Definition resolv.h:70
@ RESOLV_STATUS_CACHED
Hostname is fresh and usable.
Definition resolv.h:54
@ RESOLV_STATUS_UNCACHED
Hostname was not found in the cache.
Definition resolv.h:57
A timer.
Definition etimer.h:79
Representation of a uIP UDP connection.
Definition uip.h:1309
Header for the Contiki/uIP interface.
Header file for IPv6-related data structures.
uIP Name Server interface
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition uip-nd6.c:116
static uip_ds6_addr_t * addr
Pointer to a nbr cache entry.
Definition uip-nd6.c:107
Header file for module for sending UDP packets through uIP.