Contiki-NG
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 int dest_len = RESOLV_CONF_MAX_DOMAIN_NAME_SIZE;
289 unsigned char label_len = *query++;
290
291 LOG_DBG("decoding name: \"");
292
293 while(dest_len && label_len) {
294 if(label_len & 0xc0) {
295 const uint16_t offset = query[0] + ((label_len & ~0xC0) << 8);
296 if(offset >= packet_len) {
297 LOG_ERR("Offset %"PRIu16" exceeds packet length %zu\n",
298 offset, packet_len);
299 return 0;
300 }
301 LOG_DBG_("<skip-to-%d>", offset);
302 query = packet + offset;
303 label_len = *query++;
304 }
305
306 if(label_len == 0) {
307 break;
308 }
309
310 if(query - packet + label_len > packet_len) {
311 LOG_ERR("Cannot read outside the packet data\n");
312 return 0;
313 }
314
315 for(; label_len; --label_len) {
316 LOG_DBG_("%c", *query);
317
318 *dest++ = *query++;
319
320 if(--dest_len == 0) {
321 *dest = 0;
322 LOG_DBG_("\"\n");
323 return 0;
324 }
325 }
326
327 label_len = *query++;
328
329 if(label_len > 0) {
330 LOG_DBG_(".");
331 *dest++ = '.';
332 --dest_len;
333 }
334 }
335
336 LOG_DBG_("\"\n");
337 *dest = 0;
338 return dest_len != 0;
339}
340/*---------------------------------------------------------------------------*/
341/** \internal
342 */
343#if RESOLV_SUPPORTS_MDNS
344static uint8_t
345dns_name_isequal(const unsigned char *queryptr, const char *name,
346 const unsigned char *packet)
347{
348 unsigned char label_len = *queryptr++;
349
350 if(*name == 0) {
351 return 0;
352 }
353
354 while(label_len > 0) {
355 if(label_len & 0xc0) {
356 queryptr = packet + queryptr[0] + ((label_len & ~0xC0) << 8);
357 label_len = *queryptr++;
358 }
359
360 for(; label_len; --label_len) {
361 if(!*name) {
362 return 0;
363 }
364
365 if(tolower(*name++) != tolower(*queryptr++)) {
366 return 0;
367 }
368 }
369
370 label_len = *queryptr++;
371
372 if(label_len != 0 && *name++ != '.') {
373 return 0;
374 }
375 }
376
377 if(*name == '.') {
378 ++name;
379 }
380
381 return name[0] == 0;
382}
383#endif /* RESOLV_SUPPORTS_MDNS */
384/*---------------------------------------------------------------------------*/
385/** \internal
386 */
387static unsigned char *
388skip_name(unsigned char *query)
389{
390 LOG_DBG("skip name: ");
391
392 do {
393 unsigned char n = *query;
394 if(n & 0xc0) {
395 LOG_DBG_("<skip-to-%d>", query[0] + ((n & ~0xC0) << 8));
396 ++query;
397 break;
398 }
399
400 ++query;
401
402 while(n > 0) {
403 LOG_DBG_("%c", *query);
404 ++query;
405 --n;
406 }
407 LOG_DBG_(".");
408 } while(*query != 0);
409 LOG_DBG_("\n");
410 return query + 1;
411}
412/*---------------------------------------------------------------------------*/
413/** \internal
414 */
415static unsigned char *
416encode_name(unsigned char *query, const char *nameptr)
417{
418 --nameptr;
419 /* Convert hostname into suitable query format. */
420 do {
421 uint8_t n = 0;
422 char *nptr = (char *)query;
423
424 ++nameptr;
425 ++query;
426 for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) {
427 *query = *nameptr;
428 ++query;
429 ++n;
430 }
431 *nptr = n;
432 } while(*nameptr != 0);
433
434 /* End the the name. */
435 *query++ = 0;
436
437 return query;
438}
439/*---------------------------------------------------------------------------*/
440#if RESOLV_SUPPORTS_MDNS
441/** \internal
442 */
443static void
444mdns_announce_requested(void)
445{
446 mdns_needs_host_announce = 1;
447}
448/*---------------------------------------------------------------------------*/
449/** \internal
450 */
451static void
452start_name_collision_check(clock_time_t after)
453{
454 process_exit(&mdns_probe_process);
455 process_start(&mdns_probe_process, (void *)&after);
456}
457/*---------------------------------------------------------------------------*/
458/** \internal
459 */
460static unsigned char *
461mdns_write_announce_records(unsigned char *queryptr, uint8_t *count)
462{
463 uint8_t i;
464
465 for(i = 0; i < UIP_DS6_ADDR_NB; ++i) {
466 if(uip_ds6_if.addr_list[i].isused
467#if !RESOLV_CONF_MDNS_INCLUDE_GLOBAL_V6_ADDRS
468 && uip_is_addr_linklocal(&uip_ds6_if.addr_list[i].ipaddr)
469#endif
470 ) {
471 if(!*count) {
472 queryptr = encode_name(queryptr, resolv_hostname);
473 } else {
474 /* Use name compression to refer back to the first name */
475 *queryptr++ = 0xc0;
476 *queryptr++ = sizeof(struct dns_hdr);
477 }
478
479 *queryptr++ = (uint8_t)(NATIVE_DNS_TYPE >> 8);
480 *queryptr++ = (uint8_t)NATIVE_DNS_TYPE;
481
482 *queryptr++ = (uint8_t)((DNS_CLASS_IN | 0x8000) >> 8);
483 *queryptr++ = (uint8_t)(DNS_CLASS_IN | 0x8000);
484
485 *queryptr++ = 0;
486 *queryptr++ = 0;
487 *queryptr++ = 0;
488 *queryptr++ = 120;
489
490 *queryptr++ = 0;
491 *queryptr++ = sizeof(uip_ipaddr_t);
492
493 uip_ipaddr_copy((uip_ipaddr_t *)queryptr,
494 &uip_ds6_if.addr_list[i].ipaddr);
495 queryptr += sizeof(uip_ipaddr_t);
496 ++(*count);
497 }
498 }
499 return queryptr;
500}
501/*---------------------------------------------------------------------------*/
502/** \internal
503 * Called when we need to announce ourselves
504 */
505static size_t
506mdns_prep_host_announce_packet(void)
507{
508 static const struct {
509 uint16_t type;
510 uint16_t class;
511 uint16_t ttl[2];
512 uint16_t len;
513 uint8_t data[8];
514 } nsec_record = {
515 UIP_HTONS(DNS_TYPE_NSEC),
516 UIP_HTONS(DNS_CLASS_IN | 0x8000),
517 { 0, UIP_HTONS(120) },
518 UIP_HTONS(8),
519
520 {
521 0xc0,
522 sizeof(struct dns_hdr), /* Name compression. Re-use name of 1st record. */
523 0x00,
524 0x04,
525
526 0x00,
527 0x00,
528 0x00,
529 0x08,
530 }
531 };
532
533 /* Be aware that, unless `ARCH_DOESNT_NEED_ALIGNED_STRUCTS` is set,
534 * writing directly to the uint16_t members of this struct is an error. */
535 struct dns_hdr *hdr = (struct dns_hdr *)uip_appdata;
536 unsigned char *queryptr = (unsigned char *)uip_appdata + sizeof(*hdr);
537 uint8_t total_answers = 0;
538
539 /* Zero out the header */
540 memset((void *)hdr, 0, sizeof(*hdr));
541 hdr->flags1 |= DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE;
542
543 queryptr = mdns_write_announce_records(queryptr, &total_answers);
544
545 /* We now need to add an NSEC record to indicate
546 * that this is all there is.
547 */
548 if(!total_answers) {
549 queryptr = encode_name(queryptr, resolv_hostname);
550 } else {
551 /* Name compression. Re-using the name of first record. */
552 *queryptr++ = 0xc0;
553 *queryptr++ = sizeof(*hdr);
554 }
555
556 memcpy((void *)queryptr, (void *)&nsec_record, sizeof(nsec_record));
557
558 queryptr += sizeof(nsec_record);
559
560 /* This platform might be picky about alignment. To avoid the possibility
561 * of doing an unaligned write, we are going to do this manually. */
562 ((uint8_t *)&hdr->numanswers)[1] = total_answers;
563 ((uint8_t *)&hdr->numextrarr)[1] = 1;
564
565 return queryptr - (unsigned char *)uip_appdata;
566}
567#endif /* RESOLV_SUPPORTS_MDNS */
568/*---------------------------------------------------------------------------*/
569static char
570try_next_server(struct namemap *namemapptr)
571{
572 namemapptr->server++;
573 if(uip_nameserver_get(namemapptr->server) != NULL) {
574 LOG_DBG("Using server ");
575 LOG_DBG_6ADDR(uip_nameserver_get(namemapptr->server));
576 LOG_DBG_(", num %u\n", namemapptr->server);
577 namemapptr->retries = 0;
578 return 1;
579 }
580 LOG_DBG("No nameserver, num %u\n", namemapptr->server);
581 namemapptr->server = 0;
582 return 0;
583}
584/*---------------------------------------------------------------------------*/
585/** \internal
586 * Runs through the list of names to see if there are any that have
587 * not yet been queried and, if so, sends out a query.
588 */
589static void
590check_entries(void)
591{
592 uint8_t i;
593
594 for(i = 0; i < RESOLV_ENTRIES; ++i) {
595 struct namemap *namemapptr = &names[i];
596 if(namemapptr->state == STATE_NEW || namemapptr->state == STATE_ASKING) {
597 etimer_set(&retry, CLOCK_SECOND / 4);
598 if(namemapptr->state == STATE_ASKING) {
599 if(namemapptr->tmr == 0 || --namemapptr->tmr == 0) {
600#if RESOLV_SUPPORTS_MDNS
601 if(++namemapptr->retries ==
602 (namemapptr->is_mdns ? RESOLV_CONF_MAX_MDNS_RETRIES :
604#else /* RESOLV_SUPPORTS_MDNS */
605 if(++namemapptr->retries == RESOLV_CONF_MAX_RETRIES)
606#endif /* RESOLV_SUPPORTS_MDNS */
607 {
608 /* Try the next server (if possible) before failing. Otherwise
609 simply mark the entry as failed. */
610 if(try_next_server(namemapptr) == 0) {
611 /* STATE_ERROR basically means "not found". */
612 namemapptr->state = STATE_ERROR;
613
614#if RESOLV_SUPPORTS_RECORD_EXPIRATION
615 /* Keep the "not found" error valid for 30 seconds */
616 namemapptr->expiration = clock_seconds() + 30;
617#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
618
619 resolv_found(namemapptr->name, NULL);
620 continue;
621 }
622 }
623 namemapptr->tmr = namemapptr->retries * namemapptr->retries * 3;
624
625#if RESOLV_SUPPORTS_MDNS
626 if(namemapptr->is_probe) {
627 /* Probing retries are much more aggressive, 250ms */
628 namemapptr->tmr = 2;
629 }
630#endif /* RESOLV_SUPPORTS_MDNS */
631 } else {
632 /* Its timer has not run out, so we move on to next entry. */
633 continue;
634 }
635 } else {
636 namemapptr->state = STATE_ASKING;
637 namemapptr->tmr = 1;
638 namemapptr->retries = 0;
639 }
640
641 struct dns_hdr *hdr = (struct dns_hdr *)uip_appdata;
642 memset(hdr, 0, sizeof(struct dns_hdr));
643 hdr->id = random_rand();
644 namemapptr->id = hdr->id;
645
646#if RESOLV_SUPPORTS_MDNS
647 if(!namemapptr->is_mdns || namemapptr->is_probe) {
648 hdr->flags1 = DNS_FLAG1_RD;
649 }
650 if(namemapptr->is_mdns) {
651 hdr->id = 0;
652 }
653#else /* RESOLV_SUPPORTS_MDNS */
654 hdr->flags1 = DNS_FLAG1_RD;
655#endif /* RESOLV_SUPPORTS_MDNS */
656
657 hdr->numquestions = UIP_HTONS(1);
658 uint8_t *query = (unsigned char *)uip_appdata + sizeof(*hdr);
659 query = encode_name(query, namemapptr->name);
660
661#if RESOLV_SUPPORTS_MDNS
662 if(namemapptr->is_probe) {
663 *query++ = (uint8_t)((DNS_TYPE_ANY) >> 8);
664 *query++ = (uint8_t)((DNS_TYPE_ANY));
665 } else
666#endif /* RESOLV_SUPPORTS_MDNS */
667 {
668 *query++ = (uint8_t)(NATIVE_DNS_TYPE >> 8);
669 *query++ = (uint8_t)NATIVE_DNS_TYPE;
670 }
671 *query++ = (uint8_t)(DNS_CLASS_IN >> 8);
672 *query++ = (uint8_t)DNS_CLASS_IN;
673#if RESOLV_SUPPORTS_MDNS
674 if(namemapptr->is_mdns) {
675 if(namemapptr->is_probe) {
676 /* This is our conflict detection request.
677 * In order to be in compliance with the MDNS
678 * spec, we need to add the records we are proposing
679 * to the rrauth section.
680 */
681 uint8_t count = 0;
682
683 query = mdns_write_announce_records(query, &count);
684 hdr->numauthrr = UIP_HTONS(count);
685 }
686 uip_udp_packet_sendto(resolv_conn, uip_appdata,
687 (query - (uint8_t *)uip_appdata),
688 &resolv_mdns_addr, UIP_HTONS(MDNS_PORT));
689
690 LOG_DBG("(i=%d) Sent MDNS %s for \"%s\"\n", i,
691 namemapptr->is_probe ? "probe" : "request", namemapptr->name);
692 } else {
693 uip_udp_packet_sendto(resolv_conn, uip_appdata,
694 (query - (uint8_t *)uip_appdata),
695 (const uip_ipaddr_t *)
696 uip_nameserver_get(namemapptr->server),
697 UIP_HTONS(DNS_PORT));
698
699 LOG_DBG("(i=%d) Sent DNS request for \"%s\"\n", i,
700 namemapptr->name);
701 }
702#else /* RESOLV_SUPPORTS_MDNS */
703 uip_udp_packet_sendto(resolv_conn, uip_appdata,
704 (query - (uint8_t *)uip_appdata),
705 uip_nameserver_get(namemapptr->server),
706 UIP_HTONS(DNS_PORT));
707 LOG_DBG("(i=%d) Sent DNS request for \"%s\"\n", i,
708 namemapptr->name);
709#endif /* RESOLV_SUPPORTS_MDNS */
710 break;
711 }
712 }
713}
714/*---------------------------------------------------------------------------*/
715/** \internal
716 * Called when new UDP data arrives.
717 */
718static void
720{
721 int8_t i = 0;
722 struct dns_hdr const *hdr = (struct dns_hdr *)uip_appdata;
723 unsigned char *queryptr = (unsigned char *)hdr + sizeof(*hdr);
724 const uint8_t is_request = (hdr->flags1 & ~1) == 0 && hdr->flags2 == 0;
725
726 /* We only care about the question(s) and the answers. The authrr
727 * and the extrarr are simply discarded.
728 */
729 uint8_t nquestions = (uint8_t)uip_ntohs(hdr->numquestions);
730 uint8_t nanswers = (uint8_t)uip_ntohs(hdr->numanswers);
731
732 queryptr = (unsigned char *)hdr + sizeof(*hdr);
733 i = 0;
734
735 LOG_DBG("flags1=0x%02X flags2=0x%02X nquestions=%d, nanswers=%d, " \
736 "nauthrr=%d, nextrarr=%d\n",
737 hdr->flags1, hdr->flags2, (uint8_t)nquestions, (uint8_t)nanswers,
738 (uint8_t)uip_ntohs(hdr->numauthrr),
739 (uint8_t)uip_ntohs(hdr->numextrarr));
740
741 if(is_request && nquestions == 0) {
742 /* Skip requests with no questions. */
743 LOG_DBG("Skipping request with no questions\n");
744 return;
745 }
746
747/** QUESTION HANDLING SECTION ************************************************/
748
749 for(; nquestions > 0;
750 queryptr = skip_name(queryptr) + sizeof(struct dns_question),
751 --nquestions
752 ) {
753#if RESOLV_SUPPORTS_MDNS
754 if(!is_request) {
755 /* If this isn't a request, we don't need to bother
756 * looking at the individual questions. For the most
757 * part, this loop to just used to skip past them.
758 */
759 continue;
760 }
761
762 {
763 struct dns_question *question =
764 (struct dns_question *)skip_name(queryptr);
765
766#if !ARCH_DOESNT_NEED_ALIGNED_STRUCTS
767 static struct dns_question aligned;
768 memcpy(&aligned, question, sizeof(aligned));
769 question = &aligned;
770#endif /* !ARCH_DOESNT_NEED_ALIGNED_STRUCTS */
771
772 LOG_DBG("Question %d: type=%d class=%d\n", ++i,
773 uip_htons(question->type), uip_htons(question->class));
774
775 if((uip_ntohs(question->class) & 0x7FFF) != DNS_CLASS_IN ||
776 (question->type != UIP_HTONS(DNS_TYPE_ANY) &&
777 question->type != UIP_HTONS(NATIVE_DNS_TYPE))) {
778 /* Skip unrecognised records. */
779 continue;
780 }
781
782 if(!dns_name_isequal(queryptr, resolv_hostname, uip_appdata)) {
783 continue;
784 }
785
786 LOG_DBG("Received MDNS request for us\n");
787
788 if(mdns_state == MDNS_STATE_READY) {
789 /* We only send immediately if this isn't an MDNS request.
790 * Otherwise, we schedule ourselves to send later.
791 */
792 if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT)) {
793 mdns_announce_requested();
794 } else {
795 uip_udp_packet_sendto(resolv_conn, uip_appdata,
796 mdns_prep_host_announce_packet(),
797 &UIP_IP_BUF->srcipaddr,
798 UIP_UDP_BUF->srcport);
799 }
800 return;
801 } else {
802 uint8_t nauthrr;
803
804 LOG_DBG("But we are still probing. Waiting...\n");
805
806 /* We are still probing. We need to do the mDNS
807 * probe race condition check here and make sure
808 * we don't need to delay probing for a second.
809 */
810 nauthrr = (uint8_t)uip_ntohs(hdr->numauthrr);
811
812 /* For now, we will always restart the collision check if
813 * there are *any* authority records present.
814 * In the future we should follow the spec more closely,
815 * but this should eventually converge to something reasonable.
816 */
817 if(nauthrr) {
818 start_name_collision_check(CLOCK_SECOND);
819 }
820 }
821 }
822#endif /* RESOLV_SUPPORTS_MDNS */
823 }
824
825/** ANSWER HANDLING SECTION **************************************************/
826 struct namemap *namemapptr = NULL;
827
828#if RESOLV_SUPPORTS_MDNS
829 if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT) && hdr->id == 0) {
830 /* OK, this was from MDNS. Things get a little weird here,
831 * because we can't use the `id` field. We will look up the
832 * appropriate request in a later step. */
833
834 i = -1;
835 namemapptr = NULL;
836 } else
837#endif /* RESOLV_SUPPORTS_MDNS */
838 {
839 for(i = 0; i < RESOLV_ENTRIES; ++i) {
840 namemapptr = &names[i];
841 if(namemapptr->state == STATE_ASKING &&
842 namemapptr->id == hdr->id) {
843 break;
844 }
845 }
846
847 if(i >= RESOLV_ENTRIES || i < 0 || namemapptr->state != STATE_ASKING) {
848 LOG_DBG("DNS response has bad ID (%04X)\n", uip_ntohs(hdr->id));
849 return;
850 }
851
852 LOG_DBG("Incoming response for \"%s\"\n", namemapptr->name);
853
854 /* We'll change this to DONE when we find the record. */
855 namemapptr->state = STATE_ERROR;
856 namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
857
858#if RESOLV_SUPPORTS_RECORD_EXPIRATION
859 /* If we remain in the error state, keep it cached for 30 seconds. */
860 namemapptr->expiration = clock_seconds() + 30;
861#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
862
863 /* Check for error. If so, call callback to inform. */
864 if(namemapptr->err != 0) {
865 namemapptr->state = STATE_ERROR;
866 resolv_found(namemapptr->name, NULL);
867 return;
868 }
869 }
870
871 i = 0;
872
873 /* Answer parsing loop */
874 while(nanswers > 0) {
875 struct dns_answer *ans = (struct dns_answer *)skip_name(queryptr);
876
877#if !ARCH_DOESNT_NEED_ALIGNED_STRUCTS
878 {
879 static struct dns_answer aligned;
880 memcpy(&aligned, ans, sizeof(aligned));
881 ans = &aligned;
882 }
883#endif /* !ARCH_DOESNT_NEED_ALIGNED_STRUCTS */
884
885 if(LOG_DBG_ENABLED) {
886 char debug_name[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1];
887 decode_name(queryptr, debug_name, uip_appdata, uip_datalen());
888 LOG_DBG("Answer %d: \"%s\", type %d, class %d, ttl %"PRIu32", length %d\n",
889 ++i, debug_name, uip_ntohs(ans->type),
890 uip_ntohs(ans->class) & 0x7FFF,
891 (uint32_t)((uint32_t)uip_ntohs(ans->ttl[0]) << 16) |
892 (uint32_t)uip_ntohs(ans->ttl[1]), uip_ntohs(ans->len));
893 }
894
895 /* Check the class and length of the answer to make sure
896 * it matches what we are expecting
897 */
898 if((uip_ntohs(ans->class) & 0x7FFF) != DNS_CLASS_IN ||
899 ans->len != UIP_HTONS(sizeof(uip_ipaddr_t))) {
900 goto skip_to_next_answer;
901 }
902
903 if(ans->type != UIP_HTONS(NATIVE_DNS_TYPE)) {
904 goto skip_to_next_answer;
905 }
906
907#if RESOLV_SUPPORTS_MDNS
908 if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT) && hdr->id == 0) {
909 int8_t available_i = RESOLV_ENTRIES;
910
911 LOG_DBG("MDNS query\n");
912
913 /* For MDNS, we need to actually look up the name we
914 * are looking for.
915 */
916 for(i = 0; i < RESOLV_ENTRIES; ++i) {
917 namemapptr = &names[i];
918 if(dns_name_isequal(queryptr, namemapptr->name, uip_appdata)) {
919 break;
920 }
921 if((namemapptr->state == STATE_UNUSED)
922#if RESOLV_SUPPORTS_RECORD_EXPIRATION
923 || (namemapptr->state == STATE_DONE &&
924 clock_seconds() > namemapptr->expiration)
925#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
926 ) {
927 available_i = i;
928 }
929 }
930 if(i == RESOLV_ENTRIES) {
931 LOG_DBG("Unsolicited MDNS response\n");
932 i = available_i;
933 namemapptr = &names[i];
934 if(!decode_name(queryptr, namemapptr->name,
936 LOG_DBG("MDNS name too big to cache\n");
937 namemapptr = NULL;
938 goto skip_to_next_answer;
939 }
940 }
941 if(i == RESOLV_ENTRIES) {
942 LOG_DBG("Not enough room to keep track of unsolicited MDNS answer\n");
943
944 if(dns_name_isequal(queryptr, resolv_hostname, uip_appdata)) {
945 /* Oh snap, they say they are us! We had better report them... */
946 resolv_found(resolv_hostname, (uip_ipaddr_t *)ans->ipaddr);
947 }
948 namemapptr = NULL;
949 goto skip_to_next_answer;
950 }
951 namemapptr = &names[i];
952 } else
953#endif /* RESOLV_SUPPORTS_MDNS */
954 {
955 /* This will force us to stop even if there are more answers. */
956 nanswers = 1;
957 }
958
959/* This is disabled for now, so that we don't fail on CNAME records.
960 #if RESOLV_VERIFY_ANSWER_NAMES
961 if(namemapptr && !dns_name_isequal(queryptr, namemapptr->name, uip_appdata)) {
962 LOG_DBG("Answer name doesn't match the query!\n");
963 goto skip_to_next_answer;
964 }
965 #endif
966 */
967
968 LOG_DBG("Answer for \"%s\" is usable\n", namemapptr->name);
969
970 namemapptr->state = STATE_DONE;
971#if RESOLV_SUPPORTS_RECORD_EXPIRATION
972 namemapptr->expiration = (uint32_t)uip_ntohs(ans->ttl[0]) << 16 |
973 (uint32_t)uip_ntohs(ans->ttl[1]);
974 LOG_DBG("Expires in %lu seconds\n", namemapptr->expiration);
975
976 namemapptr->expiration += clock_seconds();
977#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
978
979 uip_ipaddr_copy(&namemapptr->ipaddr, (uip_ipaddr_t *)ans->ipaddr);
980
981 resolv_found(namemapptr->name, &namemapptr->ipaddr);
982 break;
983
984skip_to_next_answer:
985 queryptr = (unsigned char *)skip_name(queryptr) + 10 + uip_htons(ans->len);
986 --nanswers;
987 }
988
989 /* Got to this point there's no answer, try next nameserver if available
990 since this one doesn't know the answer */
991#if RESOLV_SUPPORTS_MDNS
992 if(nanswers == 0 && UIP_UDP_BUF->srcport != UIP_HTONS(MDNS_PORT)
993 && hdr->id != 0)
994#else
995 if(nanswers == 0)
996#endif
997 {
998 if(try_next_server(namemapptr)) {
999 namemapptr->state = STATE_ASKING;
1000 process_post(&resolv_process, PROCESS_EVENT_TIMER, NULL);
1001 }
1002 }
1003}
1004/*---------------------------------------------------------------------------*/
1005#if RESOLV_SUPPORTS_MDNS
1006/**
1007 * \brief Changes the local hostname advertised by MDNS.
1008 * \param hostname The new hostname to advertise.
1009 */
1010void
1011resolv_set_hostname(const char *hostname)
1012{
1013 strncpy(resolv_hostname, hostname, RESOLV_CONF_MAX_DOMAIN_NAME_SIZE);
1014
1015 /* Add the .local suffix if it isn't already there */
1016 if(strlen(resolv_hostname) < 7 ||
1017 strcasecmp(resolv_hostname + strlen(resolv_hostname) - 6, ".local") != 0) {
1018 strncat(resolv_hostname, ".local",
1019 RESOLV_CONF_MAX_DOMAIN_NAME_SIZE - strlen(resolv_hostname));
1020 }
1021
1022 LOG_DBG("hostname changed to \"%s\"\n", resolv_hostname);
1023
1024 start_name_collision_check(0);
1025}
1026/*---------------------------------------------------------------------------*/
1027/**
1028 * \brief Returns the local hostname being advertised via MDNS.
1029 * \return C-string containing the local hostname.
1030 */
1031const char *
1032resolv_get_hostname(void)
1033{
1034 return resolv_hostname;
1035}
1036/*---------------------------------------------------------------------------*/
1037/** \internal
1038 * Process for probing for name conflicts.
1039 */
1040PROCESS_THREAD(mdns_probe_process, ev, data)
1041{
1042 static struct etimer delay;
1043
1044 PROCESS_BEGIN();
1045 mdns_state = MDNS_STATE_WAIT_BEFORE_PROBE;
1046
1047 LOG_DBG("mdns-probe: Process (re)started\n");
1048
1049 /* Wait extra time if specified in data */
1050 if(NULL != data) {
1051 LOG_DBG("mdns-probe: Probing will begin in %ld clocks\n",
1052 (long)*(clock_time_t *)data);
1053 etimer_set(&delay, *(clock_time_t *)data);
1054 PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);
1055 }
1056
1057 /* We need to wait a random (0-250ms) period of time before
1058 * probing to be in compliance with the MDNS spec. */
1059 etimer_set(&delay, CLOCK_SECOND * (random_rand() & 0xFF) / 1024);
1060 PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);
1061
1062 /* Begin searching for our name. */
1063 mdns_state = MDNS_STATE_PROBING;
1064 resolv_query(resolv_hostname);
1065
1066 do {
1068 } while(strcasecmp(resolv_hostname, data) != 0);
1069
1070 mdns_state = MDNS_STATE_READY;
1071 mdns_announce_requested();
1072
1073 LOG_DBG("mdns-probe: Finished probing\n");
1074
1075 PROCESS_END();
1076}
1077#endif /* RESOLV_SUPPORTS_MDNS */
1078/*---------------------------------------------------------------------------*/
1079/** \internal
1080 * The main UDP function.
1081 */
1082PROCESS_THREAD(resolv_process, ev, data)
1083{
1084 PROCESS_BEGIN();
1085
1086 memset(names, 0, sizeof(names));
1087
1089
1090 LOG_DBG("Process started\n");
1091
1092 resolv_conn = udp_new(NULL, 0, NULL);
1093 if(resolv_conn == NULL) {
1094 LOG_ERR("No UDP connection available, exiting the process!\n");
1095 PROCESS_EXIT();
1096 }
1097
1098#if RESOLV_SUPPORTS_MDNS
1099 LOG_DBG("Supports MDNS\n");
1100 uip_udp_bind(resolv_conn, UIP_HTONS(MDNS_PORT));
1101
1102 uip_ds6_maddr_add(&resolv_mdns_addr);
1103
1104 resolv_set_hostname(CONTIKI_CONF_DEFAULT_HOSTNAME);
1105#endif /* RESOLV_SUPPORTS_MDNS */
1106
1107 while(1) {
1109
1110 if(ev == PROCESS_EVENT_TIMER) {
1111 tcpip_poll_udp(resolv_conn);
1112 } else if(ev == tcpip_event && uip_udp_conn == resolv_conn) {
1113 if(uip_newdata()) {
1114 newdata();
1115 }
1116 if(uip_poll()) {
1117#if RESOLV_SUPPORTS_MDNS
1118 if(mdns_needs_host_announce) {
1119 size_t len;
1120
1121 LOG_DBG("Announcing that we are \"%s\"\n",
1122 resolv_hostname);
1123
1124 memset(uip_appdata, 0, sizeof(struct dns_hdr));
1125 len = mdns_prep_host_announce_packet();
1126 uip_udp_packet_sendto(resolv_conn, uip_appdata,
1127 len, &resolv_mdns_addr, UIP_HTONS(MDNS_PORT));
1128 mdns_needs_host_announce = 0;
1129
1130 /*
1131 * Poll again in case this fires at the same time that
1132 * the event timer did.
1133 */
1134 tcpip_poll_udp(resolv_conn);
1135 } else
1136#endif /* RESOLV_SUPPORTS_MDNS */
1137 {
1138 check_entries();
1139 }
1140 }
1141 }
1142
1143#if RESOLV_SUPPORTS_MDNS
1144 if(mdns_needs_host_announce) {
1145 tcpip_poll_udp(resolv_conn);
1146 }
1147#endif /* RESOLV_SUPPORTS_MDNS */
1148 }
1149
1150 PROCESS_END();
1151}
1152/*---------------------------------------------------------------------------*/
1153static void
1154init(void)
1155{
1156 static uint8_t initialized = 0;
1157 if(!initialized) {
1158 process_start(&resolv_process, NULL);
1159 initialized = 1;
1160 }
1161}
1162/*---------------------------------------------------------------------------*/
1163#if RESOLV_AUTO_REMOVE_TRAILING_DOTS
1164static const char *
1165remove_trailing_dots(const char *name)
1166{
1167 static char dns_name_without_dots[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1];
1168 size_t len = strlen(name);
1169
1170 if(len && name[len - 1] == '.') {
1171 strncpy(dns_name_without_dots, name, RESOLV_CONF_MAX_DOMAIN_NAME_SIZE);
1172 while(len && (dns_name_without_dots[len - 1] == '.')) {
1173 dns_name_without_dots[--len] = 0;
1174 }
1175 name = dns_name_without_dots;
1176 }
1177 return name;
1178}
1179#else /* RESOLV_AUTO_REMOVE_TRAILING_DOTS */
1180#define remove_trailing_dots(x) (x)
1181#endif /* RESOLV_AUTO_REMOVE_TRAILING_DOTS */
1182/*---------------------------------------------------------------------------*/
1183/**
1184 * Queues a name so that a question for the name will be sent out.
1185 *
1186 * \param name The hostname that is to be queried.
1187 */
1188void
1189resolv_query(const char *name)
1190{
1191 uint8_t lseqi = 0, lseq = 0, i = 0;
1192 struct namemap *nameptr = 0;
1193
1194 init();
1195
1196 /* Remove trailing dots, if present. */
1197 name = remove_trailing_dots(name);
1198
1199 for(i = 0; i < RESOLV_ENTRIES; ++i) {
1200 nameptr = &names[i];
1201 if(0 == strcasecmp(nameptr->name, name)) {
1202 break;
1203 }
1204 if((nameptr->state == STATE_UNUSED)
1205#if RESOLV_SUPPORTS_RECORD_EXPIRATION
1206 || (nameptr->state == STATE_DONE && clock_seconds() > nameptr->expiration)
1207#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
1208 ) {
1209 lseqi = i;
1210 lseq = 255;
1211 } else if(seqno - nameptr->seqno > lseq) {
1212 lseq = seqno - nameptr->seqno;
1213 lseqi = i;
1214 }
1215 }
1216
1217 if(i == RESOLV_ENTRIES) {
1218 i = lseqi;
1219 nameptr = &names[i];
1220 }
1221
1222 LOG_DBG("Starting query for \"%s\"\n", name);
1223
1224 memset(nameptr, 0, sizeof(*nameptr));
1225
1226 strncpy(nameptr->name, name, sizeof(nameptr->name) - 1);
1227 nameptr->state = STATE_NEW;
1228 nameptr->seqno = seqno;
1229 ++seqno;
1230
1231#if RESOLV_SUPPORTS_MDNS
1232 {
1233 size_t name_len = strlen(name);
1234 const char local_suffix[] = "local";
1235
1236 if(name_len > (sizeof(local_suffix) - 1) &&
1237 strcasecmp(name + name_len - (sizeof(local_suffix) - 1),
1238 local_suffix) == 0) {
1239 LOG_DBG("Using MDNS to look up \"%s\"\n", name);
1240 nameptr->is_mdns = true;
1241 } else {
1242 nameptr->is_mdns = false;
1243 }
1244 }
1245 nameptr->is_probe = mdns_state == MDNS_STATE_PROBING &&
1246 strcmp(nameptr->name, resolv_hostname) == 0;
1247#endif /* RESOLV_SUPPORTS_MDNS */
1248
1249 /* Force check_entires() to run on our process. */
1250 process_post(&resolv_process, PROCESS_EVENT_TIMER, 0);
1251}
1252/*---------------------------------------------------------------------------*/
1253/**
1254 * Look up a hostname in the array of known hostnames.
1255 *
1256 * \note This function only looks in the internal array of known
1257 * hostnames, it does not send out a query for the hostname if none
1258 * was found. The function resolv_query() can be used to send a query
1259 * for a hostname.
1260 *
1261 */
1262resolv_status_t
1263resolv_lookup(const char *name, uip_ipaddr_t **ipaddr)
1264{
1265 resolv_status_t ret = RESOLV_STATUS_UNCACHED;
1266
1267 /* Remove trailing dots, if present. */
1268 name = remove_trailing_dots(name);
1269
1270#if UIP_CONF_LOOPBACK_INTERFACE
1271 if(strcmp(name, "localhost") == 0) {
1272 static uip_ipaddr_t loopback =
1273 { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1274 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } };
1275 if(ipaddr) {
1276 *ipaddr = &loopback;
1277 }
1279 }
1280#endif /* UIP_CONF_LOOPBACK_INTERFACE */
1281
1282 /* Walk through the list to see if the name is in there. */
1283 uint8_t i;
1284 for(i = 0; i < RESOLV_ENTRIES; ++i) {
1285 struct namemap *nameptr = &names[i];
1286
1287 if(strcasecmp(name, nameptr->name) == 0) {
1288 switch(nameptr->state) {
1289 case STATE_DONE:
1291#if RESOLV_SUPPORTS_RECORD_EXPIRATION
1292 if(clock_seconds() > nameptr->expiration) {
1294 }
1295#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
1296 break;
1297 case STATE_NEW:
1298 case STATE_ASKING:
1300 break;
1301 /* Almost certainly a not-found error from server */
1302 case STATE_ERROR:
1304#if RESOLV_SUPPORTS_RECORD_EXPIRATION
1305 if(clock_seconds() > nameptr->expiration) {
1307 }
1308#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
1309 break;
1310 }
1311
1312 if(ipaddr) {
1313 *ipaddr = &nameptr->ipaddr;
1314 }
1315
1316 /* Break out of for loop. */
1317 break;
1318 }
1319 }
1320
1321 if(LOG_DBG_ENABLED) {
1322 switch(ret) {
1324 if(ipaddr) {
1325 LOG_DBG("Found \"%s\" in cache => ", name);
1326 const uip_ipaddr_t *addr = *ipaddr;
1327 LOG_DBG_6ADDR(addr);
1328 LOG_DBG_("\n");
1329 break;
1330 }
1331 default:
1332 LOG_DBG("\"%s\" is NOT cached\n", name);
1333 break;
1334 }
1335 }
1336
1337 return ret;
1338}
1339/*---------------------------------------------------------------------------*/
1340/** \internal
1341 * Callback function which is called when a hostname is found.
1342 *
1343 */
1344static void
1345resolv_found(char *name, uip_ipaddr_t *ipaddr)
1346{
1347#if RESOLV_SUPPORTS_MDNS
1348 if(strncasecmp(resolv_hostname, name, strlen(resolv_hostname)) == 0 &&
1349 ipaddr && !uip_ds6_is_my_addr(ipaddr)) {
1350 uint8_t i;
1351
1352 if(mdns_state == MDNS_STATE_PROBING) {
1353 /* We found this new name while probing.
1354 * We must now rename ourselves.
1355 */
1356 LOG_DBG("Name collision detected for \"%s\"\n", name);
1357
1358 /* Remove the ".local" suffix. */
1359 resolv_hostname[strlen(resolv_hostname) - 6] = 0;
1360
1361 /* Append the last three hex parts of the link-level address. */
1362 for(i = 0; i < 3; ++i) {
1363 uint8_t val = uip_lladdr.addr[(UIP_LLADDR_LEN - 3) + i];
1364
1365 char append_str[4] = "-XX";
1366
1367 append_str[2] = (((val & 0xF) > 9) ? 'a' : '0') + (val & 0xF);
1368 val >>= 4;
1369 append_str[1] = (((val & 0xF) > 9) ? 'a' : '0') + (val & 0xF);
1370 /* -1 in order to fit the terminating null byte. */
1371 strncat(resolv_hostname, append_str,
1372 sizeof(resolv_hostname) - strlen(resolv_hostname) - 1);
1373 }
1374
1375 /* Re-add the .local suffix */
1376 strncat(resolv_hostname, ".local",
1377 RESOLV_CONF_MAX_DOMAIN_NAME_SIZE - strlen(resolv_hostname));
1378
1379 start_name_collision_check(CLOCK_SECOND * 5);
1380 } else if(mdns_state == MDNS_STATE_READY) {
1381 /* We found a collision after we had already asserted
1382 * that we owned this name. We need to immediately
1383 * and explicitly begin probing.
1384 */
1385 LOG_DBG("Possible name collision, probing...\n");
1386 start_name_collision_check(0);
1387 }
1388 } else
1389#endif /* RESOLV_SUPPORTS_MDNS */
1390 if(ipaddr) {
1391 LOG_DBG("Found address for \"%s\" => ", name);
1392 LOG_DBG_6ADDR(ipaddr);
1393 LOG_DBG_("\n");
1394 } else {
1395 LOG_DBG("Unable to retrieve address for \"%s\"\n", name);
1396 }
1397 process_post(PROCESS_BROADCAST, resolv_event_found, name);
1398}
1399/*---------------------------------------------------------------------------*/
1400#endif /* UIP_UDP */
1401
1402/** @} */
1403/** @} */
unsigned long clock_seconds(void)
Get the current value of the platform seconds.
Definition: clock.c:130
unsigned short random_rand(void)
Generates a new random number using the cc2538 RNG.
Definition: random.c:58
PROCESS_THREAD(cc2538_rf_process, ev, data)
Implementation of the cc2538 RF driver process.
Definition: cc2538-rf.c:1154
static volatile uint64_t count
Num.
Definition: clock.c:50
#define CLOCK_SECOND
A second, measured in system clock time.
Definition: clock.h:82
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:307
#define PROCESS_EXIT()
Exit the currently running process.
Definition: process.h:200
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition: process.h:141
int process_post(struct process *p, process_event_t ev, process_data_t data)
Post an asynchronous event.
Definition: process.c:322
void process_exit(struct process *p)
Cause a process to exit.
Definition: process.c:202
process_event_t process_alloc_event(void)
Allocate a global event number.
Definition: process.c:93
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition: process.h:120
#define PROCESS_WAIT_EVENT_UNTIL(c)
Wait for an event to be posted to the process, with an extra condition.
Definition: process.h:157
#define PROCESS_END()
Define the end of a process.
Definition: process.h:131
void process_start(struct process *p, process_data_t data)
Start a process.
Definition: process.c:99
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:261
void tcpip_poll_udp(struct uip_udp_conn *conn)
Cause a specified UDP connection to be polled.
Definition: tcpip.c:751
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:1775
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:75
#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:2339
void resolv_query(const char *name)
Queues a name so that a question for the name will be sent out.
Definition: resolv.c:1189
#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:1263
static void newdata(void)
Definition: resolv.c:719
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.
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:76
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.