From f145c17039b778cde1d53b6828757cd91dc8480a Mon Sep 17 00:00:00 2001 From: Robert Quattlebaum Date: Thu, 17 May 2012 08:24:08 -0700 Subject: [PATCH] core/net/resolv: IPv6 and mDNS ("Bonjour") support. Major refactor. This patch updates the DNS resolver to support IPv6 and introduces an improved API for looking up DNS entries. This patch also adds optional support for mDNS lookups and responses to the DNS resolver. Here is a quick summary of the changes: * Added support for IPv6 lookups. * DNS queries now honor record expiration. * Added support for mDNS, compatible with "Bonjour". * Implemented a new lookup api, `resolv_lookup2()`, which provides more information about the state of the record(error, expired, looking-up, etc.). About mDNS/Bonjour Support -------------------------- This patch adds basic support for mDNS/Bonjour, which allows you to refer to the name of a device instead of its IP address. This is incredibly convenient for IPv6 addresses because they tend to be very long and difficult to remember. It is especially important for link-local IPv6 addresses, since not all programs support the '%' notation for indicating a network interface (required on systems with more than one network interface to disambiguate). In other words, instead of typing in this: * `http://[fe80::58dc:d7ed:a644:628f%en1]/` You can type this instead: * `http://contiki.local/` Huge improvement, no? The convenience extends beyond that: this mechanism can be used for nodes to talk to each other based on their human-readable names instead of their IPv6 addresses. So instead of a switch on `aaaa::58dc:d7ed:a644:628f` triggering an actuator on `aaaa::ed26:19c1:4bd2:f95b`, `light-switch.local` can trigger the actuator on `living-room-lights.local`. What you need to do to be able to look up `.local` names on your workstation depends on a few factors: * Your machine needs to be able to send and receive multicast packets to and from the LoWPAN. You can do this easily with the Jackdaw firmware on an RZUSBStick. If you have a border router, you will need it to bridge the mDNS multicast packets across the border. * If you are using a Mac, you win. All Apple devices support mDNS lookups. * If you are using Windows, you can install Apple's Bonjour for Windows package. (This may be already installed on your machine if you have installed iTunes) After you install this you can easily do `.local` lookups. * If you are using a Unix machine, you can install Avahi. The default hostname is set to `contiki.local.`. You can change the hostname programmatically by calling `resolv_set_hostname()`. You can change the default hostname by changing `CONTIKI_CONF_DEFAULT_HOSTNAME`. You may disable mDNS support by setting `RESOLV_CONF_SUPPORTS_MDNS` to `0`. --------------------------------- core/net/resolv: `resolv_lookup2()` -> `resolv_lookup()` Note that this patch should fix several `resolv_lookup()` bugs that already existed. There were many cases where `resolv_lookup()` was being called and the IP address ignored, but later code assumed that the IP address had been fetched... ANYWAY, those should be fixed now. --------------------------------- examples/udp-ipv6: Updated client to use MDNS to lookup the server. Also updated the Cooja regression test simulation. --- apps/email/email.c | 5 +- apps/ftp/ftp.c | 5 +- apps/httpd-ws/httpd-ws.c | 4 +- apps/irc/irc.c | 6 +- apps/shell/shell-irc.c | 2 +- apps/shell/shell-ping.c | 2 +- apps/shell/shell-tcpsend.c | 2 +- apps/shell/shell-udpsend.c | 2 +- apps/shell/shell-wget.c | 6 +- apps/telnet/simpletelnet.c | 5 +- apps/vnc/vnc.c | 5 +- apps/webbrowser/webclient.c | 6 +- apps/webbrowser/www.c | 7 +- core/net/resolv.c | 1462 ++++++++++++++--- core/net/resolv.h | 56 +- examples/ipv6/json-ws/json-ws-udp.c | 4 +- examples/sky-shell-webserver/Makefile | 2 +- examples/udp-ipv6/udp-client.c | 55 +- examples/udp-ipv6/udp-server.c | 6 +- examples/wget/wget.c | 7 +- .../10-ipv6/01-cooja-ipv6-udp.csc | 2 +- 21 files changed, 1358 insertions(+), 293 deletions(-) diff --git a/apps/email/email.c b/apps/email/email.c index 760155041..ae74e4e1b 100644 --- a/apps/email/email.c +++ b/apps/email/email.c @@ -191,8 +191,7 @@ applyconfig(void) addrptr = &addr; #if UIP_UDP if(uiplib_ipaddrconv(smtpserver, &addr) == 0) { - addrptr = resolv_lookup(smtpserver); - if(addrptr == NULL) { + if(resolv_lookup(smtpserver, &addrptr) != RESOLV_STATUS_CACHED) { resolv_query(smtpserver); ctk_label_set_text(&statuslabel, "Resolving host..."); return; @@ -334,7 +333,7 @@ PROCESS_THREAD(email_process, ev, data) #if UIP_UDP } else if(ev == resolv_event_found) { if(strcmp(data, smtpserver) == 0) { - if(resolv_lookup(smtpserver) != NULL) { + if(resolv_lookup(smtpserver, NULL) == RESOLV_STATUS_CACHED) { applyconfig(); ctk_label_set_text(&statuslabel, ""); } else { diff --git a/apps/ftp/ftp.c b/apps/ftp/ftp.c index 45f24df1e..b20499aee 100644 --- a/apps/ftp/ftp.c +++ b/apps/ftp/ftp.c @@ -434,7 +434,7 @@ PROCESS_THREAD(ftp_process, ev, data) } else if(ev == resolv_event_found) { /* Either found a hostname, or not. */ if((char *)data != NULL && - (ipaddrptr = resolv_lookup((char *)data)) != NULL) { + resolv_lookup((char *)data, &ipaddrptr) == RESOLV_STATUS_CACHED) { connection = ftpc_connect(ipaddrptr, UIP_HTONS(21)); show_statustext("Connecting to ", hostname); } else { @@ -508,8 +508,7 @@ PROCESS_THREAD(ftp_process, ev, data) ptractive = 1; #if UIP_UDP if(uiplib_ipaddrconv(hostname, &ipaddr) == 0) { - ipaddrptr = resolv_lookup(hostname); - if(ipaddrptr == NULL) { + if(resolv_lookup(hostname, &ipaddrptr) != RESOLV_STATUS_CACHED) { resolv_query(hostname); show_statustext("Resolving host ", hostname); break; diff --git a/apps/httpd-ws/httpd-ws.c b/apps/httpd-ws/httpd-ws.c index c28230897..1107e53cc 100644 --- a/apps/httpd-ws/httpd-ws.c +++ b/apps/httpd-ws/httpd-ws.c @@ -378,9 +378,7 @@ httpd_ws_request(char request_type, const char *host_ip, const char *host_hdr, ipaddr = &addr; if(uiplib_ipaddrconv(host_ip, &addr) == 0) { #if 0 && UIP_UDP - ipaddr = resolv_lookup(host_ip); - - if(ipaddr == NULL) { + if(resolv_lookup(host, &ipaddr) != RESOLV_STATUS_CACHED) { return NULL; } #else /* UIP_UDP */ diff --git a/apps/irc/irc.c b/apps/irc/irc.c index ba46a369f..733426944 100644 --- a/apps/irc/irc.c +++ b/apps/irc/irc.c @@ -246,8 +246,7 @@ PROCESS_THREAD(irc_process, ev, data) ipaddr = &serveraddr; #if UIP_UDP if(uiplib_ipaddrconv(server, &serveraddr) == 0) { - ipaddr = resolv_lookup(server); - if(ipaddr == NULL) { + if(resolv_lookup(server, &ipaddr) != RESOLV_STATUS_CACHED) { resolv_query(server); } else { uip_ipaddr_copy(&serveraddr, ipaddr); @@ -264,8 +263,7 @@ PROCESS_THREAD(irc_process, ev, data) #if UIP_UDP } else if(ev == resolv_event_found) { - ipaddr = resolv_lookup(server); - if(ipaddr == NULL) { + if(resolv_lookup(server, &ipaddr) != RESOLV_STATUS_CACHED) { ircc_text_output(&s, server, "hostname not found"); } else { uip_ipaddr_copy(&serveraddr, ipaddr); diff --git a/apps/shell/shell-irc.c b/apps/shell/shell-irc.c index 8dfd241f6..dc4ee2b30 100644 --- a/apps/shell/shell-irc.c +++ b/apps/shell/shell-irc.c @@ -157,7 +157,7 @@ PROCESS_THREAD(shell_irc_process, ev, data) } else if(ev == resolv_event_found) { /* Either found a hostname, or not. */ if((char *)data != NULL && - resolv_lookup((char *)data) != NULL) { + resolv_lookup((char *)data, &ipaddr) == RESOLV_STATUS_CACHED) { uip_ipaddr_copy(serveraddr, ipaddr); ircc_connect(&s, server, serveraddr, nick); } else { diff --git a/apps/shell/shell-ping.c b/apps/shell/shell-ping.c index 0fea8bb90..8f202d162 100644 --- a/apps/shell/shell-ping.c +++ b/apps/shell/shell-ping.c @@ -166,7 +166,7 @@ PROCESS_THREAD(shell_ping_process, ev, data) } else if(ev == resolv_event_found) { /* Either found a hostname, or not. */ if((char *)data != NULL && - resolv_lookup((char *)data) != NULL) { + resolv_lookup((char *)data, &ipaddr) == RESOLV_STATUS_CACHED) { uip_ipaddr_copy(serveraddr, ipaddr); telnet_connect(&s, server, serveraddr, nick); } else { diff --git a/apps/shell/shell-tcpsend.c b/apps/shell/shell-tcpsend.c index cd1be1411..923cda181 100644 --- a/apps/shell/shell-tcpsend.c +++ b/apps/shell/shell-tcpsend.c @@ -173,7 +173,7 @@ PROCESS_THREAD(shell_tcpsend_process, ev, data) } else if(ev == resolv_event_found) { /* Either found a hostname, or not. */ if((char *)data != NULL && - resolv_lookup((char *)data) != NULL) { + resolv_lookup((char *)data, &ipaddr) == RESOLV_STATUS_CACHED) { uip_ipaddr_copy(serveraddr, ipaddr); telnet_connect(&s, server, serveraddr, nick); } else { diff --git a/apps/shell/shell-udpsend.c b/apps/shell/shell-udpsend.c index 3568aa86e..ee20e2754 100644 --- a/apps/shell/shell-udpsend.c +++ b/apps/shell/shell-udpsend.c @@ -123,7 +123,7 @@ PROCESS_THREAD(shell_udpsend_process, ev, data) } else if(ev == resolv_event_found) { /* Either found a hostname, or not. */ if((char *)data != NULL && - resolv_lookup((char *)data) != NULL) { + resolv_lookup((char *)data, &ipaddr) == RESOLV_STATUS_CACHED) { uip_ipaddr_copy(serveraddr, ipaddr); telnet_connect(&s, server, serveraddr, nick); } else { diff --git a/apps/shell/shell-wget.c b/apps/shell/shell-wget.c index c2986d1a0..6374032fc 100644 --- a/apps/shell/shell-wget.c +++ b/apps/shell/shell-wget.c @@ -130,12 +130,14 @@ open_url(char *url) /* Try to lookup the hostname. If it fails, we initiate a hostname lookup and print out an informative message on the statusbar. */ if(uiplib_ipaddrconv(host, &addr) == 0) { + uip_ipaddr_t *addrptr; shell_output_str(&wget_command, "Not an IP address", ""); - if(resolv_lookup(host) == NULL) { + if(resolv_lookup(host, &addrptr) == RESOLV_STATUS_UNCACHED) { shell_output_str(&wget_command, "Not resolved", ""); resolv_query(host); return; } + uip_ipaddr_copy(&addr, addrptr); } #else /* UIP_UDP */ uiplib_ipaddrconv(host, &addr); @@ -171,7 +173,7 @@ PROCESS_THREAD(shell_wget_process, ev, data) } else if(ev == resolv_event_found) { /* Either found a hostname, or not. */ if((char *)data != NULL && - resolv_lookup((char *)data) != NULL) { + resolv_lookup((char *)data, NULL) == RESOLV_STATUS_CACHED) { open_url(url); } else { shell_output_str(&wget_command, "Host not found.", ""); diff --git a/apps/telnet/simpletelnet.c b/apps/telnet/simpletelnet.c index bbc43d435..42fe50f45 100644 --- a/apps/telnet/simpletelnet.c +++ b/apps/telnet/simpletelnet.c @@ -163,8 +163,7 @@ connect(void) addrptr = &addr; #if UIP_UDP if(uiplib_ipaddrconv(telnethost, &addr) == 0) { - addrptr = resolv_lookup(telnethost); - if(addrptr == NULL) { + if(resolv_lookup(telnethost, &addrptr) == RESOLV_STATUS_UNCACHED) { resolv_query(telnethost); show("Resolving host..."); return; @@ -252,7 +251,7 @@ PROCESS_THREAD(simpletelnet_process, ev, data) #if UIP_UDP } else if(ev == resolv_event_found) { if(strcmp(data, telnethost) == 0) { - if(resolv_lookup(telnethost) != NULL) { + if(resolv_lookup(telnethost, NULL) == RESOLV_STATUS_CACHED) { connect(); } else { show("Host not found"); diff --git a/apps/vnc/vnc.c b/apps/vnc/vnc.c index 8a1f53ef5..b9bdb7e07 100644 --- a/apps/vnc/vnc.c +++ b/apps/vnc/vnc.c @@ -134,8 +134,7 @@ connect(void) addrptr = &addr; if(uiplib_ipaddrconv(host, &addr) == 0) { - addrptr = resolv_lookup(host); - if(addrptr == NULL) { + if(resolv_lookup(host, &addrptr) == RESOLV_STATUS_UNCACHED) { resolv_query(host); show("Resolving host..."); return; @@ -198,7 +197,7 @@ PROCESS_THREAD(vnc_process, ev, data) LOADER_UNLOAD(); } else if(ev == resolv_event_found) { if(strcmp(data, host) == 0) { - if(resolv_lookup(host) != NULL) { + if(resolv_lookup(host, NULL) == RESOLV_STATUS_CACHED) { connect(); } else { show("Host not found"); diff --git a/apps/webbrowser/webclient.c b/apps/webbrowser/webclient.c index f5e6ca508..08c6ed211 100644 --- a/apps/webbrowser/webclient.c +++ b/apps/webbrowser/webclient.c @@ -140,9 +140,7 @@ webclient_get(const char *host, uint16_t port, const char *file) ipaddr = &addr; if(uiplib_ipaddrconv(host, &addr) == 0) { #if UIP_UDP - ipaddr = resolv_lookup(host); - - if(ipaddr == NULL) { + if(resolv_lookup(host,&ipaddr) != RESOLV_STATUS_CACHED) { return 0; } #else /* UIP_UDP */ @@ -486,7 +484,7 @@ webclient_appcall(void *state) init_connection(); }*/ #if UIP_UDP - if(resolv_lookup(s.host) == NULL) { + if(resolv_lookup(s.host, NULL) != RESOLV_STATUS_CACHED) { resolv_query(s.host); } #endif /* UIP_UDP */ diff --git a/apps/webbrowser/www.c b/apps/webbrowser/www.c index 53d8becf7..8de3d7578 100644 --- a/apps/webbrowser/www.c +++ b/apps/webbrowser/www.c @@ -348,11 +348,13 @@ open_url(void) /* Try to lookup the hostname. If it fails, we initiate a hostname lookup and print out an informative message on the statusbar. */ if(uiplib_ipaddrconv(host, &addr) == 0) { - if(resolv_lookup(host) == NULL) { + uip_ipaddr_t *addrptr; + if(resolv_lookup(host, &addrptr) != RESOLV_STATUS_CACHED) { resolv_query(host); show_statustext("Resolving host..."); return; } + uip_ipaddr_copy(&addr, addrptr); } #else /* UIP_UDP */ uiplib_ipaddrconv(host, &addr); @@ -553,7 +555,8 @@ PROCESS_THREAD(www_process, ev, data) #if UIP_UDP } else if(ev == resolv_event_found) { /* Either found a hostname, or not. */ - if((char *)data != NULL && resolv_lookup((char *)data) != NULL) { + if((char *)data != NULL && + resolv_lookup((char *)data, NULL) == RESOLV_STATUS_CACHED) { open_url(); } else { show_statustext("Host not found"); diff --git a/core/net/resolv.c b/core/net/resolv.c index c75bcc94c..f4cc310b9 100644 --- a/core/net/resolv.c +++ b/core/net/resolv.c @@ -21,10 +21,12 @@ /** * \file - * DNS host name to IP address resolver. + * DNS host name to IP address resolver. * \author Adam Dunkels + * \author Robert Quattlebaum * - * This file implements a DNS host name to IP address resolver. + * This file implements a DNS host name to IP address resolver, + * as well as an MDNS responder and resolver. */ /* @@ -62,41 +64,120 @@ #include "net/tcpip.h" #include "net/resolv.h" +#include "net/uip-udp-packet.h" +#include "lib/random.h" + +#ifndef DEBUG +#define DEBUG defined(CONTIKI_TARGET_COOJA) +#endif + #if UIP_UDP #include +#include +#include +#include #ifndef NULL #define NULL (void *)0 #endif /* NULL */ -#if UIP_CONF_IPV6 +#if !defined(__SDCC) && defined(SDCC_REVISION) +#define __SDCC 1 +#endif -/* Currently this implementation only supports IPv4 DNS lookups. - Until support for IPv6 is added, dummy functions are used to - enable compilation with IPv6. -*/ +#if VERBOSE_DEBUG +#define DEBUG_PRINTF(...) printf(__VA_ARGS__) +#else +#define DEBUG_PRINTF(...) do { } while(0) +#endif -process_event_t resolv_event_found; +#if DEBUG || VERBOSE_DEBUG +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) do { } while(0) +#endif -PROCESS(resolv_process, "DNS resolver"); - -void resolv_conf(const uip_ipaddr_t *dnsserver) { } -uip_ipaddr_t *resolv_getserver(void) { return NULL; } -uip_ipaddr_t *resolv_lookup(const char *name) { return NULL; } -void resolv_query(const char *name) { } - -PROCESS_THREAD(resolv_process, ev, data) +#ifdef __SDCC +static int +strncasecmp(const char *s1, const char *s2, size_t n) { - PROCESS_BEGIN(); - resolv_event_found = process_alloc_event(); - PROCESS_END(); + /* TODO: Add case support! */ + return strncmp(s1, s2, n); } +static int +strcasecmp(const char *s1, const char *s2) +{ + /* TODO: Add case support! */ + return strcmp(s1, s2); +} +#endif -#else /* UIP_CONF_IPV6 */ +#define UIP_UDP_BUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN]) -/** \internal The maximum number of retries when asking for a name. */ -#define MAX_RETRIES 8 +/* If RESOLV_CONF_SUPPORTS_MDNS is set, then queries + * for domain names in the local TLD will use mDNS as + * described by draft-cheshire-dnsext-multicastdns. + */ +#ifndef RESOLV_CONF_SUPPORTS_MDNS +#define RESOLV_CONF_SUPPORTS_MDNS (1) +#endif + +#ifndef RESOLV_CONF_MDNS_INCLUDE_GLOBAL_V6_ADDRS +#define RESOLV_CONF_MDNS_INCLUDE_GLOBAL_V6_ADDRS (0) +#endif + +/** The maximum number of retries when asking for a name. */ +#ifndef RESOLV_CONF_MAX_RETRIES +#define RESOLV_CONF_MAX_RETRIES (4) +#endif + +#ifndef RESOLV_CONF_MAX_MDNS_RETRIES +#define RESOLV_CONF_MAX_MDNS_RETRIES (3) +#endif + +#ifndef RESOLV_CONF_MAX_DOMAIN_NAME_SIZE +#define RESOLV_CONF_MAX_DOMAIN_NAME_SIZE (32) +#endif + +#if !defined(CONTIKI_TARGET_NAME) && defined(BOARD) +#define stringy2(x) #x +#define stringy(x) stringy2(x) +#define CONTIKI_TARGET_NAME stringy(BOARD) +#endif + +#ifndef CONTIKI_CONF_DEFAULT_HOSTNAME +#ifdef CONTIKI_TARGET_NAME +#define CONTIKI_CONF_DEFAULT_HOSTNAME "contiki-"CONTIKI_TARGET_NAME +#else +#define CONTIKI_CONF_DEFAULT_HOSTNAME "contiki" +#endif +#endif + +#define DNS_TYPE_A (1) +#define DNS_TYPE_CNAME (5) +#define DNS_TYPE_PTR (12) +#define DNS_TYPE_MX (15) +#define DNS_TYPE_TXT (16) +#define DNS_TYPE_AAAA (28) +#define DNS_TYPE_SRV (33) +#define DNS_TYPE_ANY (255) +#define DNS_TYPE_NSEC (47) + +#define DNS_CLASS_IN (1) +#define DNS_CLASS_ANY (255) + +#ifndef DNS_PORT +#define DNS_PORT (53) +#endif + +#ifndef MDNS_PORT +#define MDNS_PORT (5353) +#endif + +#ifndef MDNS_RESPONDER_PORT +#define MDNS_RESPONDER_PORT (5354) +#endif /** \internal The DNS message header. */ struct dns_hdr { @@ -119,30 +200,57 @@ struct dns_hdr { uint16_t numextrarr; }; +#define RESOLV_ENCODE_INDEX(i) (uip_htons(i+1)) +#define RESOLV_DECODE_INDEX(i) (unsigned char)(uip_ntohs(i-1)) + +/** These default values for the DNS server are Google's public DNS: + * + */ +#if UIP_CONF_IPV6 +static uip_ipaddr_t resolv_default_dns_server = { + .u8 = { + 0x20, 0x01, 0x48, 0x60, + 0x48, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x88, 0x88, + } +}; +#else +static uip_ipaddr_t resolv_default_dns_server = {.u8 = {8, 8, 8, 8} }; +#endif + /** \internal The DNS answer message structure. */ struct dns_answer { /* DNS answer record starts with either a domain name or a pointer - to a name already present somewhere in the packet. */ + * to a name already present somewhere in the packet. */ uint16_t type; uint16_t class; uint16_t ttl[2]; uint16_t len; +#if UIP_CONF_IPV6 + uint8_t ipaddr[16]; +#else uint8_t ipaddr[4]; +#endif }; struct namemap { #define STATE_UNUSED 0 -#define STATE_NEW 1 -#define STATE_ASKING 2 -#define STATE_DONE 3 -#define STATE_ERROR 4 +#define STATE_ERROR 1 +#define STATE_NEW 2 +#define STATE_ASKING 3 +#define STATE_DONE 4 uint8_t state; uint8_t tmr; uint8_t retries; uint8_t seqno; + unsigned long expiration; uint8_t err; - char name[32]; +#if RESOLV_CONF_SUPPORTS_MDNS + uint8_t is_mdns:1, is_probe:1; +#endif uip_ipaddr_t ipaddr; + char name[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1]; }; #ifndef UIP_CONF_RESOLV_ENTRIES @@ -151,7 +259,6 @@ struct namemap { #define RESOLV_ENTRIES UIP_CONF_RESOLV_ENTRIES #endif /* UIP_CONF_RESOLV_ENTRIES */ - static struct namemap names[RESOLV_ENTRIES]; static uint8_t seqno; @@ -164,271 +271,982 @@ process_event_t resolv_event_found; PROCESS(resolv_process, "DNS resolver"); -static void resolv_found(char *name, uip_ipaddr_t *ipaddr); +static void resolv_found(char *name, uip_ipaddr_t * ipaddr); enum { - EVENT_NEW_SERVER=0 + EVENT_NEW_SERVER = 0 }; -/*-----------------------------------------------------------------------------------*/ +/*---------------------------------------------------------------------------*/ +#if RESOLV_CONF_SUPPORTS_MDNS +/** \internal The DNS question message structure. */ +struct dns_question { + uint16_t type; + uint16_t class; +}; +static char resolv_hostname[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1]; + +enum { + MDNS_STATE_WAIT_BEFORE_PROBE, + MDNS_STATE_PROBING, + MDNS_STATE_READY, +}; + +static uint8_t mdns_state; + +#if UIP_CONF_IPV6 +#include "net/uip-ds6.h" +static const uip_ipaddr_t resolv_mdns_addr = { + .u8 = { + 0xff, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfb, + } +}; +#else /* UIP_CONF_IPV6 */ +static const uip_ipaddr_t resolv_mdns_addr = { + .u8 = {224, 0, 0, 251} +}; +#endif /* !UIP_CONF_IPV6 */ +static bool mdns_needs_host_announce; + +PROCESS(mdns_probe_process, "mDNS probe"); +#endif /* RESOLV_CONF_SUPPORTS_MDNS */ + +/*---------------------------------------------------------------------------*/ /** \internal - * Walk through a compact encoded DNS name and return the end of it. + * \brief Decodes a DNS name from the DNS format into the given string. + * \return True upon success, False if the size of the name would be too large. * - * \return The end of the name. + * \note `dest` must point to a buffer with at least + * `RESOLV_CONF_MAX_DOMAIN_NAME_SIZE+1` bytes large. + */ +static bool +decode_name(const unsigned char *query, char *dest, + const unsigned char *packet) +{ + int len = RESOLV_CONF_MAX_DOMAIN_NAME_SIZE; + + unsigned char n = *query++; + + //DEBUG_PRINTF("resolver: decoding name: \""); + + while(len && n) { + if(n & 0xc0) { + const uint16_t offset = query[0] + ((n & ~0xC0) << 8); + + //DEBUG_PRINTF("",offset); + query = packet + offset; + n = *query++; + } + + if(!n) + break; + + for(; n; n--) { + //DEBUG_PRINTF("%c",*query); + + *dest++ = *query++; + + if(!--len) { + *dest = 0; + return false; + } + } + + n = *query++; + + if(n) { + //DEBUG_PRINTF("."); + *dest++ = '.'; + len--; + } + } + + //DEBUG_PRINTF("\"\n"); + *dest = 0; + return len != 0; +} + +/*---------------------------------------------------------------------------*/ +/** \internal + */ +static bool +dns_name_isequal(const unsigned char *queryptr, const char *name, + const unsigned char *packet) +{ + unsigned char n = *queryptr++; + + if(*name == 0) + return false; + + while(n) { + if(n & 0xc0) { + queryptr = packet + queryptr[0] + ((n & ~0xC0) << 8); + n = *queryptr++; + } + + for(; n; n--) { + if(!*name) { + return false; + } + + if(tolower(*name++) != tolower(*queryptr++)) { + return false; + } + } + + n = *queryptr++; + + if((n != 0) && (*name++ != '.')) { + return false; + } + } + + if(*name == '.') + name++; + + return name[0] == 0; +} + +/*---------------------------------------------------------------------------*/ +/** \internal */ -/*-----------------------------------------------------------------------------------*/ static unsigned char * -parse_name(unsigned char *query) +skip_name(unsigned char *query) { unsigned char n; + DEBUG_PRINTF("resolver: skip name: "); + do { - n = *query++; - + n = *query; + if(n & 0xc0) { + DEBUG_PRINTF("", query[0] + ((n & ~0xC0) << 8)); + query++; + break; + } + + query++; + while(n > 0) { - /* printf("%c", *query);*/ + DEBUG_PRINTF("%c", *query); ++query; --n; }; - /* printf(".");*/ + DEBUG_PRINTF("."); } while(*query != 0); - /* printf("\n");*/ + DEBUG_PRINTF("\n"); return query + 1; } -/*-----------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/** \internal + */ +static unsigned char * +encode_name(unsigned char *query, const char *nameptr) +{ + char *nptr; + + --nameptr; + /* Convert hostname into suitable query format. */ + do { + uint8_t n = 0; + + ++nameptr; + nptr = (char *)query; + ++query; + for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) { + *query = *nameptr; + ++query; + ++n; + } + *nptr = n; + } while(*nameptr != 0); + + /* End the the name. */ + *query++ = 0; + + return query; +} + +#if RESOLV_CONF_SUPPORTS_MDNS + +/*---------------------------------------------------------------------------*/ +/** \internal + */ +static void +mdns_announce_requested(void) +{ + mdns_needs_host_announce = true; +} + +/*---------------------------------------------------------------------------*/ +/** \internal + */ +static void +start_name_collision_check(clock_time_t after) +{ + process_exit(&mdns_probe_process); + process_start(&mdns_probe_process, (void *)&after); +} + +/*---------------------------------------------------------------------------*/ +/** \internal + */ +static unsigned char * +mdns_write_announce_records(unsigned char *queryptr, uint8_t * count) +{ + struct dns_answer *ans; + +#if UIP_CONF_IPV6 + uint8_t i; + + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + if(uip_ds6_if.addr_list[i].isused +#if !RESOLV_CONF_MDNS_INCLUDE_GLOBAL_V6_ADDRS + && uip_is_addr_link_local(&uip_ds6_if.addr_list[i].ipaddr) +#endif + ) { + if(!*count) { + queryptr = encode_name(queryptr, resolv_hostname); + } else { + *queryptr++ = 0xc0; + *queryptr++ = sizeof(struct dns_hdr); + } + ans = (struct dns_answer *)queryptr; + + *queryptr++ = (uint8_t) ((DNS_TYPE_AAAA) >> 8); + *queryptr++ = (uint8_t) ((DNS_TYPE_AAAA)); + + *queryptr++ = (uint8_t) ((DNS_CLASS_IN | 0x8000) >> 8); + *queryptr++ = (uint8_t) ((DNS_CLASS_IN | 0x8000)); + + *queryptr++ = 0; + *queryptr++ = 0; + *queryptr++ = 0; + *queryptr++ = 120; + + *queryptr++ = 0; + *queryptr++ = sizeof(uip_ipaddr_t); + + uip_ipaddr_copy((uip_ipaddr_t *) queryptr, + &uip_ds6_if.addr_list[i].ipaddr); + queryptr += sizeof(uip_ipaddr_t); + (*count)++; + } + } +#else + queryptr = encode_name(queryptr, resolv_hostname); + ans = (struct dns_answer *)queryptr; + ans->type = UIP_HTONS(DNS_TYPE_A); + ans->class = UIP_HTONS(DNS_CLASS_IN | 0x8000); + ans->ttl[0] = 0; + ans->ttl[1] = UIP_HTONS(120); + ans->len = UIP_HTONS(sizeof(uip_ipaddr_t)); + uip_gethostaddr((uip_ipaddr_t *) ans->ipaddr); + queryptr = (unsigned char *)ans + sizeof(*ans); + (*count)++; +#endif + return queryptr; +} + +/*---------------------------------------------------------------------------*/ +/** \internal + * Called when we need to announce ourselves + */ +static size_t +mdns_prep_host_announce_packet(void) +{ + unsigned char *queryptr; + + struct dns_answer *ans; + + struct dns_hdr *hdr; + + uint8_t total_answers = 0; + + hdr = (struct dns_hdr *)uip_appdata; + + hdr->flags1 |= DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE; + hdr->numquestions = UIP_HTONS(0); + hdr->numauthrr = 0; + hdr->numextrarr = 0; + + queryptr = (unsigned char *)hdr + sizeof(*hdr); + + queryptr = mdns_write_announce_records(queryptr, &total_answers); + + /* We now need to add an NSEC record to indicate + * that this is all there is. + */ + if(!total_answers) { + queryptr = encode_name(queryptr, resolv_hostname); + } else { + *queryptr++ = 0xc0; + *queryptr++ = sizeof(*hdr); + } + ans = (struct dns_answer *)queryptr; + ans->type = UIP_HTONS(DNS_TYPE_NSEC); + ans->class = UIP_HTONS(DNS_CLASS_IN | 0x8000); + ans->ttl[0] = 0; + ans->ttl[1] = UIP_HTONS(120); + ans->len = UIP_HTONS(8); + queryptr += 10; + *queryptr++ = 0xc0; + *queryptr++ = sizeof(*hdr); + *queryptr++ = 0x00; + *queryptr++ = 0x04; +#if UIP_CONF_IPV6 + *queryptr++ = 0x00; + *queryptr++ = 0x00; + *queryptr++ = 0x00; + *queryptr++ = 0x08; +#else + *queryptr++ = 0x40; + *queryptr++ = 0x00; + *queryptr++ = 0x00; + *queryptr++ = 0x00; +#endif + + hdr->numanswers = uip_htons(total_answers); + hdr->numextrarr = UIP_HTONS(1); + + return (queryptr - (unsigned char *)uip_appdata); +} +#endif // #if RESOLV_CONF_SUPPORTS_MDNS + +/*---------------------------------------------------------------------------*/ /** \internal * Runs through the list of names to see if there are any that have * not yet been queried and, if so, sends out a query. */ -/*-----------------------------------------------------------------------------------*/ static void check_entries(void) { + volatile uint8_t i; + + uint8_t *query; + register struct dns_hdr *hdr; - char *query, *nptr, *nameptr; - uint8_t i; - uint8_t n; + register struct namemap *namemapptr; - + for(i = 0; i < RESOLV_ENTRIES; ++i) { namemapptr = &names[i]; - if(namemapptr->state == STATE_NEW || - namemapptr->state == STATE_ASKING) { - etimer_set(&retry, CLOCK_SECOND); + if(namemapptr->state == STATE_NEW || namemapptr->state == STATE_ASKING) { + etimer_set(&retry, CLOCK_SECOND / 4); if(namemapptr->state == STATE_ASKING) { - if(--namemapptr->tmr == 0) { - if(++namemapptr->retries == MAX_RETRIES) { - namemapptr->state = STATE_ERROR; - resolv_found(namemapptr->name, NULL); - continue; - } - namemapptr->tmr = namemapptr->retries; - } else { - /* printf("Timer %d\n", namemapptr->tmr);*/ - /* Its timer has not run out, so we move on to next - entry. */ - continue; - } + if(--namemapptr->tmr == 0) { +#if RESOLV_CONF_SUPPORTS_MDNS + if(++namemapptr->retries == + (namemapptr->is_mdns ? RESOLV_CONF_MAX_MDNS_RETRIES : + RESOLV_CONF_MAX_RETRIES)) +#else + if(++namemapptr->retries == RESOLV_CONF_MAX_RETRIES) +#endif + { + /* STATE_ERROR basically means "not found". */ + namemapptr->state = STATE_ERROR; + + /* Keep the "not found" error valid for 30 seconds */ + namemapptr->expiration = clock_seconds() + 30; + + resolv_found(namemapptr->name, NULL); + continue; + } + namemapptr->tmr = namemapptr->retries * namemapptr->retries * 3; + +#if RESOLV_CONF_SUPPORTS_MDNS + if(namemapptr->is_probe) { + /* Probing retries are much more aggressive, 250ms */ + namemapptr->tmr = 2; + } +#endif + } else { + /* Its timer has not run out, so we move on to next + * entry. + */ + continue; + } } else { - namemapptr->state = STATE_ASKING; - namemapptr->tmr = 1; - namemapptr->retries = 0; + namemapptr->state = STATE_ASKING; + namemapptr->tmr = 1; + namemapptr->retries = 0; } hdr = (struct dns_hdr *)uip_appdata; memset(hdr, 0, sizeof(struct dns_hdr)); - hdr->id = uip_htons(i); - hdr->flags1 = DNS_FLAG1_RD; - hdr->numquestions = UIP_HTONS(1); - query = (char *)uip_appdata + 12; - nameptr = namemapptr->name; - --nameptr; - /* Convert hostname into suitable query format. */ - do { - ++nameptr; - nptr = query; - ++query; - for(n = 0; *nameptr != '.' && *nameptr != 0; ++nameptr) { - *query = *nameptr; - ++query; - ++n; - } - *nptr = n; - } while(*nameptr != 0); - { - static unsigned char endquery[] = - {0,0,1,0,1}; - memcpy(query, endquery, 5); + hdr->id = RESOLV_ENCODE_INDEX(i); +#if RESOLV_CONF_SUPPORTS_MDNS + if(!namemapptr->is_mdns || namemapptr->is_probe) { + hdr->flags1 = DNS_FLAG1_RD; } - uip_udp_send((unsigned char)(query + 5 - (char *)uip_appdata)); + if(namemapptr->is_mdns) { + hdr->id = 0; + } +#else + hdr->flags1 = DNS_FLAG1_RD; +#endif + hdr->numquestions = UIP_HTONS(1); + query = uip_appdata + sizeof(*hdr); + query = encode_name(query, namemapptr->name); +#if RESOLV_CONF_SUPPORTS_MDNS + if(namemapptr->is_probe) { + *query++ = (uint8_t) ((DNS_TYPE_ANY) >> 8); + *query++ = (uint8_t) ((DNS_TYPE_ANY)); + } else +#endif + { +#if UIP_CONF_IPV6 + *query++ = (uint8_t) ((DNS_TYPE_AAAA) >> 8); + *query++ = (uint8_t) ((DNS_TYPE_AAAA)); +#else + *query++ = (uint8_t) ((DNS_TYPE_A) >> 8); + *query++ = (uint8_t) ((DNS_TYPE_A)); +#endif + } + *query++ = (uint8_t) ((DNS_CLASS_IN) >> 8); + *query++ = (uint8_t) ((DNS_CLASS_IN)); +#if RESOLV_CONF_SUPPORTS_MDNS + if(namemapptr->is_mdns) { +#if RESOLV_CONF_SUPPORTS_MDNS + if(namemapptr->is_probe) { + /* This is our conflict detection request. + * In order to be in compliance with the MDNS + * spec, we need to add the records we are proposing + * to the rrauth section. + */ + uint8_t count = 0; + + query = mdns_write_announce_records(query, &count); + hdr->numauthrr = UIP_HTONS(count); + } +#endif + uip_udp_packet_sendto(resolv_conn, + uip_appdata, + (query - (uint8_t *) uip_appdata), + &resolv_mdns_addr, UIP_HTONS(MDNS_PORT) + ); + + PRINTF("resolver: (i=%d) Sent MDNS request for \"%s\".\n", i, + namemapptr->name); + } else { + uip_udp_packet_sendto(resolv_conn, + uip_appdata, + (query - (uint8_t *) uip_appdata), + &resolv_default_dns_server, UIP_HTONS(DNS_PORT) + ); + + PRINTF("resolver: (i=%d) Sent DNS request for \"%s\".\n", i, + namemapptr->name); + } +#else + uip_udp_packet_sendto(resolv_conn, + uip_appdata, + (query - (uint8_t *) uip_appdata), + &resolv_default_dns_server, UIP_HTONS(DNS_PORT) + ); + PRINTF("resolver: (i=%d) Sent DNS request for \"%s\".\n", i, + namemapptr->name); +#endif break; } } } -/*-----------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ /** \internal * Called when new UDP data arrives. */ -/*-----------------------------------------------------------------------------------*/ static void newdata(void) { - unsigned char *nameptr; struct dns_answer *ans; - struct dns_hdr *hdr; - static uint8_t nquestions, nanswers; - static uint8_t i; + + struct dns_hdr const *hdr = (struct dns_hdr *)uip_appdata; + + unsigned char *queryptr = (unsigned char *)hdr + sizeof(*hdr); + register struct namemap *namemapptr; - - hdr = (struct dns_hdr *)uip_appdata; - /* printf("ID %d\n", uip_htons(hdr->id)); - printf("Query %d\n", hdr->flags1 & DNS_FLAG1_RESPONSE); - printf("Error %d\n", hdr->flags2 & DNS_FLAG2_ERR_MASK); - printf("Num questions %d, answers %d, authrr %d, extrarr %d\n", - uip_htons(hdr->numquestions), - uip_htons(hdr->numanswers), - uip_htons(hdr->numauthrr), - uip_htons(hdr->numextrarr)); - */ - /* The ID in the DNS header should be our entry into the name - table. */ - i = (uint8_t)uip_htons(hdr->id); - namemapptr = &names[i]; - if(i < RESOLV_ENTRIES && - namemapptr->state == STATE_ASKING) { + static uint8_t nquestions, nanswers, nauthrr = 0; - /* This entry is now finished. */ - namemapptr->state = STATE_DONE; + static int8_t i; + + /* We only care about the question(s) and the answers. The authrr + * and the extrarr are simply discarded. + */ + nquestions = (uint8_t) uip_ntohs(hdr->numquestions); + nanswers = (uint8_t) uip_ntohs(hdr->numanswers); + + DEBUG_PRINTF + ("resolver: flags1=0x%02X flags2=0x%02X nquestions=%d, nanswers=%d, nauthrr=%d, nextrarr=%d\n", + hdr->flags1, hdr->flags2, (uint8_t) nquestions, (uint8_t) nanswers, + (uint8_t) uip_ntohs(hdr->numauthrr), + (uint8_t) uip_ntohs(hdr->numextrarr)); + +/** QUESTION HANDLING SECTION ************************************************/ + + if(((hdr->flags1 & ~1) == 0) && (hdr->flags2 == 0)) { + /* This is an DNS request! */ +#if RESOLV_CONF_SUPPORTS_MDNS + + /* Skip requests with no questions. */ + if(!nquestions) { + return; + } + + queryptr = (unsigned char *)hdr + sizeof(*hdr); + + i = 0; + + for(; nquestions > 0; + queryptr = skip_name(queryptr) + sizeof(struct dns_question), + nquestions--) { + struct dns_question *question = + (struct dns_question *)skip_name(queryptr); + +#if __ARM__ + static struct dns_question aligned; + + memcpy(&aligned, question, sizeof(aligned)); + question = &aligned; +#endif + + DEBUG_PRINTF("resolver: Question %d: type=%d class=%d\n", ++i, + uip_htons(question->type), uip_htons(question->class)); + + if(((uip_ntohs(question->class) & 0x7FFF) != DNS_CLASS_IN) + || ((question->type != UIP_HTONS(DNS_TYPE_ANY)) + && (question->type != UIP_HTONS(DNS_TYPE_AAAA)) + && (question->type != UIP_HTONS(DNS_TYPE_A)) + )) { + /* Skip unrecognised records. */ + continue; + } + + if(!dns_name_isequal(queryptr, resolv_hostname, uip_appdata)) { + continue; + } + + PRINTF("resolver: THIS IS A REQUEST FOR US!!!\n"); + + if(mdns_state == MDNS_STATE_READY) { + /* We only send immediately if this isn't an MDNS request. + * Otherwise, we schedule ourselves to send later. + */ + if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT)) { + mdns_announce_requested(); + } else { + uip_udp_packet_sendto(resolv_conn, + uip_appdata, + mdns_prep_host_announce_packet(), + &UIP_UDP_BUF->srcipaddr, + UIP_UDP_BUF->srcport); + } + return; + } else { + PRINTF("resolver: But we are still probing. Waiting...\n"); + /* We are still probing. We need to do the mDNS + * probe race condition check here and make sure + * we don't need to delay probing for a second. + */ + nauthrr = (uint8_t) uip_ntohs(hdr->numauthrr); + + /* For now, we will always restart the collision check if + * there are *any* authority records present. + * In the future we should follow the spec more closely, + * but this should eventually converge to something reasonable. + */ + if(nauthrr) { + start_name_collision_check(CLOCK_SECOND * 1.5); + } + } + } +#endif + } + +/** ANSWER HANDLING SECTION **************************************************/ + + if(!nanswers) { + DEBUG_PRINTF("resolver: Skipping request/response with no answers.\n"); + /* We demand answers! */ + return; + } +#if RESOLV_CONF_SUPPORTS_MDNS + if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT) + && hdr->id == 0) { + /* OK, this was from MDNS. Things get a little weird here, + * because we can't use the `id` field. We will look up the + * appropriate request in a later step. */ + + i = -1; + namemapptr = NULL; + } else +#endif // RESOLV_CONF_SUPPORTS_MDNS + { + /* The ID in the DNS header should be our entry into the name table. */ + i = RESOLV_DECODE_INDEX(hdr->id); + + namemapptr = &names[i]; + + if(i >= RESOLV_ENTRIES || i < 0 || namemapptr->state != STATE_ASKING) { + PRINTF("resolver: Bad ID (%04X) on incoming DNS response\n", + uip_ntohs(hdr->id)); + return; + } + + PRINTF("resolver: Incoming response for \"%s\".\n", namemapptr->name); + + namemapptr->state = STATE_ERROR; /* We'll change this to DONE when we find the record. */ namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK; + /* If we remain in the error state, keep it cached for 30 seconds. */ + namemapptr->expiration = clock_seconds() + 30; + /* Check for error. If so, call callback to inform. */ if(namemapptr->err != 0) { namemapptr->state = STATE_ERROR; resolv_found(namemapptr->name, NULL); return; } + } - /* We only care about the question(s) and the answers. The authrr - and the extrarr are simply discarded. */ - nquestions = (uint8_t)uip_htons(hdr->numquestions); - nanswers = (uint8_t)uip_htons(hdr->numanswers); - - /* Skip the name in the question. XXX: This should really be - checked agains the name in the question, to be sure that they - match. */ - nameptr = parse_name((uint8_t *)uip_appdata + 12) + 4; - - while(nanswers > 0) { - /* The first byte in the answer resource record determines if it - is a compressed record or a normal one. */ - if(*nameptr & 0xc0) { - /* Compressed name. */ - nameptr +=2; - /* printf("Compressed anwser\n");*/ - } else { - /* Not compressed name. */ - nameptr = parse_name((uint8_t *)nameptr); - } - - ans = (struct dns_answer *)nameptr; - /* printf("Answer: type %x, class %x, ttl %x, length %x\n", - uip_htons(ans->type), uip_htons(ans->class), (uip_htons(ans->ttl[0]) - << 16) | uip_htons(ans->ttl[1]), uip_htons(ans->len));*/ - - /* Check for IP address type and Internet class. Others are - discarded. */ - if(ans->type == UIP_HTONS(1) && - ans->class == UIP_HTONS(1) && - ans->len == UIP_HTONS(4)) { - /* printf("IP address %d.%d.%d.%d\n", - ans->ipaddr[0], - ans->ipaddr[1], - ans->ipaddr[2], - ans->ipaddr[3]);*/ - /* XXX: we should really check that this IP address is the one - we want. */ - for(i = 0; i < 4; i++) { - namemapptr->ipaddr.u8[i] = ans->ipaddr[i]; - } - - resolv_found(namemapptr->name, &namemapptr->ipaddr); - return; - } else { - nameptr = nameptr + 10 + uip_htons(ans->len); - } - --nanswers; + /* Discard all remaining questions */ + for(; nquestions > 0; queryptr += 4, nquestions--) { + if(namemapptr + && 0 != dns_name_isequal(queryptr, namemapptr->name, uip_appdata)) { + DEBUG_PRINTF("resolver: Question name doesn't look familiar...!\n"); + return; } + queryptr = skip_name(queryptr); + } + + /* Answer parsing loop */ + while(nanswers > 0) { + ans = (struct dns_answer *)skip_name(queryptr); + +#if __ARM__ + static struct dns_answer aligned; + + memcpy(&aligned, ans, sizeof(aligned)); + ans = &aligned; +#endif + +#if VERBOSE_DEBUG + static char debug_name[40]; + + decode_name(queryptr, debug_name, uip_appdata); + DEBUG_PRINTF("resolver: \"%s\", type %d, class %d, ttl %d, length %d\n", + debug_name, uip_ntohs(ans->type), + uip_ntohs(ans->class) & 0x7FFF, + (int)((uint32_t) uip_ntohs(ans->ttl[0]) << 16) | (uint32_t) + uip_ntohs(ans->ttl[1]), uip_ntohs(ans->len) + ); +#endif + + /* Check the class and length of the answer to make sure + * it matches what we are expecting + */ + if(((uip_ntohs(ans->class) & 0x7FFF) != DNS_CLASS_IN) + || (ans->len != UIP_HTONS(sizeof(uip_ipaddr_t))) + ) { + goto skip_to_next_answer; + } + +#if UIP_CONF_IPV6 + if(ans->type != UIP_HTONS(DNS_TYPE_AAAA)) { + goto skip_to_next_answer; + } +#else // UIP_CONF_IPV6 + if(ans->type != UIP_HTONS(DNS_TYPE_A)) { + goto skip_to_next_answer; + } +#endif + +#if RESOLV_CONF_SUPPORTS_MDNS + if(UIP_UDP_BUF->srcport == UIP_HTONS(MDNS_PORT) + && hdr->id == 0) { + int8_t available_i = RESOLV_ENTRIES; + + DEBUG_PRINTF("resolver: MDNS query.\n"); + + /* For MDNS, we need to actually look up the name we + * are looking for. + */ + for(i = 0; i < RESOLV_ENTRIES; ++i) { + namemapptr = &names[i]; + if(dns_name_isequal(queryptr, namemapptr->name, uip_appdata)) { + break; + } + if((namemapptr->state == STATE_UNUSED) + || (namemapptr->state == STATE_DONE + && clock_seconds() > namemapptr->expiration) + ) { + available_i = i; + } + } + if(i == RESOLV_ENTRIES) { + DEBUG_PRINTF("resolver: Unsolicited MDNS response.\n"); + i = available_i; + namemapptr = &names[i]; + if(!decode_name(queryptr, namemapptr->name, uip_appdata)) { + DEBUG_PRINTF("resolver: MDNS name too big to cache.\n"); + namemapptr = NULL; + goto skip_to_next_answer; + } + } + if(i == RESOLV_ENTRIES) { + DEBUG_PRINTF + ("resolver: Not enough room to keep track of unsolicited MDNS answer.\n"); + + if(dns_name_isequal(queryptr, resolv_hostname, uip_appdata)) { + /* Oh snap, they say they are us! We had better report them... */ + resolv_found(resolv_hostname, (uip_ipaddr_t *) ans->ipaddr); + } + namemapptr = NULL; + goto skip_to_next_answer; + } + namemapptr = &names[i]; + + } else +#endif // #if RESOLV_CONF_SUPPORTS_MDNS + { + /* This will force us to stop even if there are more answers. */ + nanswers = 1; + } + + if(namemapptr + && !dns_name_isequal(queryptr, namemapptr->name, uip_appdata)) { + DEBUG_PRINTF("resolver: Answer name doesn't match question...!\n"); + goto skip_to_next_answer; + } + + DEBUG_PRINTF("resolver: Answer for \"%s\" is usable.\n", + namemapptr->name); + + namemapptr->state = STATE_DONE; + namemapptr->expiration = ans->ttl[1] + (ans->ttl[0] << 8); + namemapptr->expiration += clock_seconds(); + + uip_ipaddr_copy(&namemapptr->ipaddr, (uip_ipaddr_t *) ans->ipaddr); + + resolv_found(namemapptr->name, &namemapptr->ipaddr); + + skip_to_next_answer: + queryptr = + (unsigned char *)skip_name(queryptr) + 10 + uip_htons(ans->len); + --nanswers; } } -/*-----------------------------------------------------------------------------------*/ + +#if RESOLV_CONF_SUPPORTS_MDNS +/*---------------------------------------------------------------------------*/ +/** + * \brief Changes the local hostname advertised by MDNS. + * \param hostname The new hostname to advertise. + */ +void +resolv_set_hostname(const char *hostname) +{ + strncpy(resolv_hostname, hostname, RESOLV_CONF_MAX_DOMAIN_NAME_SIZE); + + /* Add the .local suffix if it isn't already there */ + if(strlen(resolv_hostname) < 7 + || strcasecmp(resolv_hostname + strlen(resolv_hostname) - 6, + ".local") != 0) { + strncat(resolv_hostname, ".local", RESOLV_CONF_MAX_DOMAIN_NAME_SIZE); + } + + PRINTF("resolver: hostname changed to \"%s\"\n", resolv_hostname); + + start_name_collision_check(0); +} + +/*---------------------------------------------------------------------------*/ +/** + * \brief Returns the local hostname being advertised via MDNS. + * \return C-string containing the local hostname. + */ +const char * +resolv_get_hostname(void) +{ + return resolv_hostname; +} + +/*---------------------------------------------------------------------------*/ +/** \internal + * Process for probing for name conflicts. + */ +PROCESS_THREAD(mdns_probe_process, ev, data) +{ + static struct etimer delay; + + PROCESS_BEGIN(); + mdns_state = MDNS_STATE_WAIT_BEFORE_PROBE; + + PRINTF("mdns-probe: Process (re)started.\n"); + + /* Wait extra time if specified in data */ + if(NULL != data) { + PRINTF("mdns-probe: Probing will begin in %ld clocks.\n", + (long)*(clock_time_t *) data); + etimer_set(&delay, *(clock_time_t *) data); + PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); + } + + /* We need to wait a random (0-250ms) period of time before + * probing to be in compliance with the MDNS spec. */ + etimer_set(&delay, CLOCK_SECOND * (random_rand() & 0xFF) / 1024); + PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); + + /* Begin searching for our name. */ + mdns_state = MDNS_STATE_PROBING; + resolv_query(resolv_hostname); + + do { + PROCESS_WAIT_EVENT_UNTIL(ev == resolv_event_found); + } while(strcasecmp(resolv_hostname, data) != 0); + + mdns_state = MDNS_STATE_READY; + mdns_announce_requested(); + + PRINTF("mdns-probe: Finished probing.\n"); + + PROCESS_END(); +} +#endif // RESOLV_CONF_SUPPORTS_MDNS + + +/*---------------------------------------------------------------------------*/ /** \internal * The main UDP function. */ -/*-----------------------------------------------------------------------------------*/ PROCESS_THREAD(resolv_process, ev, data) { - int i; - PROCESS_BEGIN(); - for(i = 0; i < RESOLV_ENTRIES; ++i) { - names[i].state = STATE_UNUSED; - } - resolv_conn = NULL; + memset(names, 0, sizeof(names)); + resolv_event_found = process_alloc_event(); - - + + PRINTF("resolver: Process started.\n"); + + resolv_conn = udp_new(NULL, 0, NULL); + resolv_conn->rport = 0; + +#if RESOLV_CONF_SUPPORTS_MDNS + PRINTF("resolver: Supports MDNS name resolution.\n"); +#endif + +#if RESOLV_CONF_SUPPORTS_MDNS + PRINTF("resolver: Supports MDNS responder.\n"); + uip_udp_bind(resolv_conn, UIP_HTONS(MDNS_PORT)); + +#if UIP_CONF_IPV6 + uip_ds6_maddr_add(&resolv_mdns_addr); +#endif + + resolv_set_hostname(CONTIKI_CONF_DEFAULT_HOSTNAME); +#endif /* RESOLV_CONF_SUPPORTS_MDNS */ + while(1) { PROCESS_WAIT_EVENT(); - - if(ev == PROCESS_EVENT_TIMER) { - if(resolv_conn != NULL) { - tcpip_poll_udp(resolv_conn); - } - } else if(ev == EVENT_NEW_SERVER) { - if(resolv_conn != NULL) { - uip_udp_remove(resolv_conn); - } - resolv_conn = udp_new((uip_ipaddr_t *)data, UIP_HTONS(53), NULL); - + if(ev == PROCESS_EVENT_TIMER) { + tcpip_poll_udp(resolv_conn); } else if(ev == tcpip_event) { - if(uip_udp_conn->rport == UIP_HTONS(53)) { - if(uip_poll()) { - check_entries(); - } - if(uip_newdata()) { - newdata(); - } + if(uip_udp_conn == resolv_conn) { + if(uip_newdata()) { + newdata(); + } + if(uip_poll()) { +#if RESOLV_CONF_SUPPORTS_MDNS + if(mdns_needs_host_announce) { + size_t len; + + PRINTF("resolver: Announcing that we are \"%s\".\n", + resolv_hostname); + + memset(uip_appdata, 0, sizeof(struct dns_hdr)); + + len = mdns_prep_host_announce_packet(); + + uip_udp_packet_sendto(resolv_conn, + uip_appdata, + len, &resolv_mdns_addr, UIP_HTONS(MDNS_PORT) + ); + + mdns_needs_host_announce = 0; + + /* Poll again in case this fired + * at the same time the event timer did. + */ + tcpip_poll_udp(resolv_conn); + } else +#endif + { + check_entries(); + } + } } } + +#if RESOLV_CONF_SUPPORTS_MDNS + if(mdns_needs_host_announce) { + tcpip_poll_udp(resolv_conn); + } +#endif } - + PROCESS_END(); } -/*-----------------------------------------------------------------------------------*/ + +/* For removing trailing dots in resolv_query() and resolve_lookup2(). */ +static char dns_name_without_dots[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1]; + +/*---------------------------------------------------------------------------*/ /** * Queues a name so that a question for the name will be sent out. * * \param name The hostname that is to be queried. */ -/*-----------------------------------------------------------------------------------*/ void resolv_query(const char *name) { static uint8_t i; + static uint8_t lseq, lseqi; - register struct namemap *nameptr; - + + register struct namemap *nameptr = 0; + lseq = lseqi = 0; - nameptr = 0; //compiler warning if not initialized - + + { /* Remove trailing dots, if present. */ + size_t len = strlen(name); + + if(name[len - 1] == '.') { + strncpy(dns_name_without_dots, name, sizeof(dns_name_without_dots)); + while(len && (dns_name_without_dots[len - 1] == '.')) { + dns_name_without_dots[--len] = 0; + } + name = dns_name_without_dots; + } + } + for(i = 0; i < RESOLV_ENTRIES; ++i) { nameptr = &names[i]; - if(nameptr->state == STATE_UNUSED) { + if(0 == strcasecmp(nameptr->name, name)) { break; } - if(seqno - nameptr->seqno > lseq) { + if((nameptr->state == STATE_UNUSED) + || (nameptr->state == STATE_DONE + && clock_seconds() > nameptr->expiration) + ) { + lseqi = i; + lseq = 255; + } else if(seqno - nameptr->seqno > lseq) { lseq = seqno - nameptr->seqno; lseqi = i; } @@ -439,16 +1257,41 @@ resolv_query(const char *name) nameptr = &names[i]; } + PRINTF("resolver: Starting query for \"%s\".\n", name); + + memset(nameptr, 0, sizeof(*nameptr)); + strncpy(nameptr->name, name, sizeof(nameptr->name)); nameptr->state = STATE_NEW; nameptr->seqno = seqno; ++seqno; - if(resolv_conn != NULL) { - tcpip_poll_udp(resolv_conn); +#if RESOLV_CONF_SUPPORTS_MDNS + { + size_t name_len = strlen(name); + + static const char local_suffix[] = "local"; + + if((name_len > (sizeof(local_suffix) - 1)) + && (0 == + strcasecmp(name + name_len - (sizeof(local_suffix) - 1), + local_suffix)) + ) { + PRINTF("resolver: Using MDNS to look up \"%s\".\n", name); + nameptr->is_mdns = 1; + } else { + nameptr->is_mdns = 0; + } } + nameptr->is_probe = (mdns_state == MDNS_STATE_PROBING) + && (0 == strcmp(nameptr->name, resolv_hostname)); +#endif + + /* Force check_entires() to run on our process. */ + process_post(&resolv_process, PROCESS_EVENT_TIMER, 0); } -/*-----------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ /** * Look up a hostname in the array of known hostnames. * @@ -457,29 +1300,111 @@ resolv_query(const char *name) * was found. The function resolv_query() can be used to send a query * for a hostname. * - * \return A pointer to a 4-byte representation of the hostname's IP - * address, or NULL if the hostname was not found in the array of - * hostnames. */ -/*-----------------------------------------------------------------------------------*/ -uip_ipaddr_t * -resolv_lookup(const char *name) +resolv_status_t +resolv_lookup(const char *name, uip_ipaddr_t ** ipaddr) { + resolv_status_t ret = RESOLV_STATUS_UNCACHED; + static uint8_t i; + struct namemap *nameptr; - - /* Walk through the list to see if the name is in there. If it is - not, we return NULL. */ - for(i = 0; i < RESOLV_ENTRIES; ++i) { - nameptr = &names[i]; - if(nameptr->state == STATE_DONE && - strcmp(name, nameptr->name) == 0) { - return &nameptr->ipaddr; + + { /* Remove trailing dots, if present. */ + size_t len = strlen(name); + + if(name[len - 1] == '.') { + strncpy(dns_name_without_dots, name, sizeof(dns_name_without_dots) - 1); + name = dns_name_without_dots; + while(len && (dns_name_without_dots[len - 1] == '.')) { + dns_name_without_dots[--len] = 0; + } } } - return NULL; + +#if UIP_CONF_LOOPBACK_INTERFACE + if(strcmp(name, "localhost")) { +#if UIP_CONF_IPV6 + static uip_ipaddr_t loopback = { + .u8 = { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, + } + }; +#else + static uip_ipaddr_t loopback = { + .u8 = {127, 0, 0, 1} + }; +#endif + if(ipaddr) { + *ipaddr = &loopback; + } + ret = RESOLV_STATUS_CACHED; + } +#endif + + /* Walk through the list to see if the name is in there. */ + for(i = 0; i < RESOLV_ENTRIES; ++i) { + nameptr = &names[i]; + + if(strcasecmp(name, nameptr->name) == 0) { + switch (nameptr->state) { + case STATE_DONE: + if(clock_seconds() > nameptr->expiration) { + ret = RESOLV_STATUS_EXPIRED; + } else { + ret = RESOLV_STATUS_CACHED; + } + break; + case STATE_NEW: + case STATE_ASKING: + ret = RESOLV_STATUS_RESOLVING; + break; + case STATE_ERROR: /* Almost certainly a not-found error from server */ + if(clock_seconds() >= nameptr->expiration) { + ret = RESOLV_STATUS_NOT_FOUND; + } + break; + } + + if(ipaddr) { + *ipaddr = &nameptr->ipaddr; + } + + /* Break out of for loop. */ + break; + } + } + +#if VERBOSE_DEBUG + switch (ret) { + case RESOLV_STATUS_CACHED:{ + PRINTF("resolver: Found \"%s\" in cache.\n", name); + const uip_ipaddr_t *addr = *ipaddr; + + DEBUG_PRINTF + ("resolver: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x \n", + ((uint8_t *) addr)[0], ((uint8_t *) addr)[1], ((uint8_t *) addr)[2], + ((uint8_t *) addr)[3], ((uint8_t *) addr)[4], ((uint8_t *) addr)[5], + ((uint8_t *) addr)[6], ((uint8_t *) addr)[7], ((uint8_t *) addr)[8], + ((uint8_t *) addr)[9], ((uint8_t *) addr)[10], + ((uint8_t *) addr)[11], ((uint8_t *) addr)[12], + ((uint8_t *) addr)[13], ((uint8_t *) addr)[14], + ((uint8_t *) addr)[15]); + break; + } + default: + DEBUG_PRINTF("resolver: \"%s\" is NOT cached.\n", name); + break; + } +#endif + + return ret; } -/*-----------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ /** * Obtain the currently configured DNS server. * @@ -487,49 +1412,104 @@ resolv_lookup(const char *name) * the currently configured DNS server or NULL if no DNS server has * been configured. */ -/*-----------------------------------------------------------------------------------*/ uip_ipaddr_t * resolv_getserver(void) { - if(resolv_conn == NULL) { - return NULL; - } - return &resolv_conn->ripaddr; + return &resolv_default_dns_server; } -/*-----------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ /** * Configure a DNS server. * * \param dnsserver A pointer to a 4-byte representation of the IP * address of the DNS server to be configured. */ -/*-----------------------------------------------------------------------------------*/ void -resolv_conf(const uip_ipaddr_t *dnsserver) +resolv_conf(const uip_ipaddr_t * dnsserver) { - static uip_ipaddr_t server; - uip_ipaddr_copy(&server, dnsserver); - process_post(&resolv_process, EVENT_NEW_SERVER, &server); - - /* if(resolv_conn != NULL) { - uip_udp_remove(resolv_conn); - } - - resolv_conn = udp_new(dnsserver, 53, NULL);*/ + uip_ipaddr_copy(&resolv_default_dns_server, dnsserver); + process_post(&resolv_process, EVENT_NEW_SERVER, &resolv_default_dns_server); } -/*-----------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ /** \internal * Callback function which is called when a hostname is found. * */ -/*-----------------------------------------------------------------------------------*/ static void -resolv_found(char *name, uip_ipaddr_t *ipaddr) +resolv_found(char *name, uip_ipaddr_t * ipaddr) { +#if RESOLV_CONF_SUPPORTS_MDNS + if(strncasecmp(resolv_hostname, name, strlen(resolv_hostname)) == 0 + && ipaddr +#if UIP_CONF_IPV6 + && !uip_ds6_is_my_addr(ipaddr) +#else + && uip_ipaddr_cmp(&uip_hostaddr, ipaddr) != 0 +#endif + ) { + uint8_t i; + + if(mdns_state == MDNS_STATE_PROBING) { + /* We found this new name while probing. + * We must now rename ourselves. + */ + PRINTF("resolver: Name collision detected for \"%s\".\n", name); + + /* Remove the ".local" suffix. */ + resolv_hostname[strlen(resolv_hostname) - 6] = 0; + + /* Append the last three hex parts of the link-level address. */ + for(i = 0; i < 3; i++) { + uint8_t val = uip_lladdr.addr[(UIP_LLADDR_LEN - 3) + i]; + + char append_str[4] = "-XX"; + + append_str[2] = (((val & 0xF) > 9) ? 'a' : '0') + (val & 0xF); + val >>= 4; + append_str[1] = (((val & 0xF) > 9) ? 'a' : '0') + (val & 0xF); + strncat(resolv_hostname, append_str, + sizeof(resolv_hostname) - strlen(resolv_hostname)); + } + + /* Re-add the .local suffix */ + strncat(resolv_hostname, ".local", RESOLV_CONF_MAX_DOMAIN_NAME_SIZE); + + start_name_collision_check(CLOCK_SECOND * 5); + } else if(mdns_state == MDNS_STATE_READY) { + /* We found a collision after we had already asserted + * that we owned this name. We need to immediately + * and explicitly begin probing. + */ + PRINTF("resolver: Possible name collision, probing...\n"); + start_name_collision_check(0); + } + + } else +#endif + +#if VERBOSE_DEBUG + if(ipaddr) { + PRINTF("resolver: Found address for \"%s\".\n", name); + PRINTF + ("resolver: %02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x \n", + ((uint8_t *) ipaddr)[0], ((uint8_t *) ipaddr)[1], + ((uint8_t *) ipaddr)[2], ((uint8_t *) ipaddr)[3], + ((uint8_t *) ipaddr)[4], ((uint8_t *) ipaddr)[5], + ((uint8_t *) ipaddr)[6], ((uint8_t *) ipaddr)[7], + ((uint8_t *) ipaddr)[8], ((uint8_t *) ipaddr)[9], + ((uint8_t *) ipaddr)[10], ((uint8_t *) ipaddr)[11], + ((uint8_t *) ipaddr)[12], ((uint8_t *) ipaddr)[13], + ((uint8_t *) ipaddr)[14], ((uint8_t *) ipaddr)[15]); + } else { + PRINTF("resolver: Unable to retrieve address for \"%s\".\n", name); + } +#endif + process_post(PROCESS_BROADCAST, resolv_event_found, name); } -/*-----------------------------------------------------------------------------------*/ -#endif /* UIP_CONF_IPV6 */ +/*---------------------------------------------------------------------------*/ #endif /* UIP_UDP */ /** @} */ diff --git a/core/net/resolv.h b/core/net/resolv.h index 85b666645..32a866bb9 100644 --- a/core/net/resolv.h +++ b/core/net/resolv.h @@ -40,6 +40,16 @@ #define __RESOLV_H__ #include "contiki.h" +#include "uip.h" + +/** If RESOLV_CONF_SUPPORTS_MDNS is set, then queries + * for domain names in the `local` TLD will use MDNS and + * will respond to MDNS queries for this device's hostname, + * as described by draft-cheshire-dnsext-multicastdns. + */ +#ifndef RESOLV_CONF_SUPPORTS_MDNS +#define RESOLV_CONF_SUPPORTS_MDNS (1) +#endif /** * Event that is broadcasted when a DNS name has been resolved. @@ -47,11 +57,53 @@ CCIF extern process_event_t resolv_event_found; /* Functions. */ -CCIF void resolv_conf(const uip_ipaddr_t *dnsserver); +CCIF void resolv_conf(const uip_ipaddr_t * dnsserver); + CCIF uip_ipaddr_t *resolv_getserver(void); -CCIF uip_ipaddr_t *resolv_lookup(const char *name); + +enum { + /** Hostname is fresh and usable. This response is cached and will eventually + * expire to RESOLV_STATUS_EXPIRED.*/ + RESOLV_STATUS_CACHED = 0, + + /** Hostname was not found in the cache. Use resolv_query() to look it up. */ + RESOLV_STATUS_UNCACHED, + + /** Hostname was found, but it's status has expired. The address returned + * should not be used. Use resolv_query() to freshen it up. + */ + RESOLV_STATUS_EXPIRED, + + /** The server has returned a not-found response for this domain name. + * This response is cached for the period described in the server. + * You may issue a new query at any time using resolv_query(), but + * you will generally want to wait until this domain's status becomes + * RESOLV_STATUS_EXPIRED. + */ + RESOLV_STATUS_NOT_FOUND, + + /** This hostname is in the process of being resolved. Try again soon. */ + RESOLV_STATUS_RESOLVING, + + /** Some sort of server error was encountered while trying to look up this + * record. This response is cached and will eventually expire to + * RESOLV_STATUS_EXPIRED. + */ + RESOLV_STATUS_ERROR, +}; + +typedef uint8_t resolv_status_t; + +CCIF resolv_status_t resolv_lookup(const char *name, uip_ipaddr_t ** ipaddr); + CCIF void resolv_query(const char *name); +#if RESOLV_CONF_SUPPORTS_MDNS +CCIF void resolv_set_hostname(const char *hostname); + +CCIF const char *resolv_get_hostname(void); +#endif + PROCESS_NAME(resolv_process); #endif /* __RESOLV_H__ */ diff --git a/examples/ipv6/json-ws/json-ws-udp.c b/examples/ipv6/json-ws/json-ws-udp.c index d948e2bd8..f43a6f29a 100644 --- a/examples/ipv6/json-ws/json-ws-udp.c +++ b/examples/ipv6/json-ws/json-ws-udp.c @@ -75,9 +75,7 @@ json_ws_udp_setup(const char *host, uint16_t port) ipaddr = &server_ipaddr; if(uiplib_ipaddrconv(host, &server_ipaddr) == 0) { #if 0 && UIP_UDP - ipaddr = resolv_lookup(host); - - if(ipaddr == NULL) { + if(resolv_lookup(host, &ipaddr) != RESOLV_STATUS_CACHED) { return 0; } #else /* UIP_UDP */ diff --git a/examples/sky-shell-webserver/Makefile b/examples/sky-shell-webserver/Makefile index ba237314e..224409442 100644 --- a/examples/sky-shell-webserver/Makefile +++ b/examples/sky-shell-webserver/Makefile @@ -2,7 +2,7 @@ CONTIKI_PROJECT = sky-shell-webserver all: $(CONTIKI_PROJECT) PROJECT_SOURCEFILES = webserver-nogui.c HTTPD_CFS=1 -CFLAGS = -DWITH_UIP=1 +CFLAGS = -DWITH_UIP=1 -DRESOLV_CONF_SUPPORTS_MDNS=0 DEFINES=NETSTACK_MAC=nullmac_driver,NETSTACK_RDC=nullrdc_driver SMALL=1 diff --git a/examples/udp-ipv6/udp-client.c b/examples/udp-ipv6/udp-client.c index 96c06e066..ce330970d 100644 --- a/examples/udp-ipv6/udp-client.c +++ b/examples/udp-ipv6/udp-client.c @@ -30,8 +30,10 @@ #include "contiki.h" #include "contiki-lib.h" #include "contiki-net.h" +#include "net/resolv.h" #include +#include #define DEBUG DEBUG_PRINT #include "net/uip-debug.h" @@ -42,7 +44,7 @@ static struct uip_udp_conn *client_conn; /*---------------------------------------------------------------------------*/ PROCESS(udp_client_process, "UDP client process"); -AUTOSTART_PROCESSES(&udp_client_process); +AUTOSTART_PROCESSES(&resolv_process,&udp_client_process); /*---------------------------------------------------------------------------*/ static void tcpip_handler(void) @@ -102,20 +104,43 @@ set_global_address(void) } #endif /* UIP_CONF_ROUTER */ /*---------------------------------------------------------------------------*/ -static void +static resolv_status_t set_connection_address(uip_ipaddr_t *ipaddr) { +#ifndef UDP_CONNECTION_ADDR +#if RESOLV_CONF_SUPPORTS_MDNS +#define UDP_CONNECTION_ADDR contiki-udp-server.local +#elif UIP_CONF_ROUTER +#define UDP_CONNECTION_ADDR aaaa:0:0:0:0212:7404:0004:0404 +#else +#define UDP_CONNECTION_ADDR fe80:0:0:0:6466:6666:6666:6666 +#endif +#endif /* !UDP_CONNECTION_ADDR */ + #define _QUOTEME(x) #x #define QUOTEME(x) _QUOTEME(x) -#ifdef UDP_CONNECTION_ADDR + + resolv_status_t status = RESOLV_STATUS_ERROR; + if(uiplib_ipaddrconv(QUOTEME(UDP_CONNECTION_ADDR), ipaddr) == 0) { - PRINTF("UDP client failed to parse address '%s'\n", QUOTEME(UDP_CONNECTION_ADDR)); + uip_ipaddr_t *resolved_addr = NULL; + status = resolv_lookup(QUOTEME(UDP_CONNECTION_ADDR),&resolved_addr); + if(status == RESOLV_STATUS_UNCACHED || status == RESOLV_STATUS_EXPIRED) { + PRINTF("Attempting to look up %s\n",QUOTEME(UDP_CONNECTION_ADDR)); + resolv_query(QUOTEME(UDP_CONNECTION_ADDR)); + status = RESOLV_STATUS_RESOLVING; + } else if(status == RESOLV_STATUS_CACHED && resolved_addr != NULL) { + PRINTF("Lookup of \"%s\" succeded!\n",QUOTEME(UDP_CONNECTION_ADDR)); + } else { + PRINTF("Lookup of \"%s\" failed. status = %d\n",QUOTEME(UDP_CONNECTION_ADDR),status); + } + if(resolved_addr) + uip_ipaddr_copy(ipaddr, resolved_addr); + } else { + status = RESOLV_STATUS_CACHED; } -#elif UIP_CONF_ROUTER - uip_ip6addr(ipaddr,0xaaaa,0,0,0,0x0212,0x7404,0x0004,0x0404); -#else - uip_ip6addr(ipaddr,0xfe80,0,0,0,0x6466,0x6666,0x6666,0x6666); -#endif /* UDP_CONNECTION_ADDR */ + + return status; } /*---------------------------------------------------------------------------*/ PROCESS_THREAD(udp_client_process, ev, data) @@ -132,7 +157,17 @@ PROCESS_THREAD(udp_client_process, ev, data) print_local_addresses(); - set_connection_address(&ipaddr); + static resolv_status_t status = RESOLV_STATUS_UNCACHED; + while(status != RESOLV_STATUS_CACHED) { + status = set_connection_address(&ipaddr); + + if(status == RESOLV_STATUS_RESOLVING) { + PROCESS_WAIT_EVENT_UNTIL(ev == resolv_event_found); + } else if(status != RESOLV_STATUS_CACHED) { + PRINTF("Can't get connection address.\n"); + PROCESS_YIELD(); + } + } /* new connection with remote host */ client_conn = udp_new(&ipaddr, UIP_HTONS(3000), NULL); diff --git a/examples/udp-ipv6/udp-server.c b/examples/udp-ipv6/udp-server.c index 1dc9279a5..8c88cf704 100644 --- a/examples/udp-ipv6/udp-server.c +++ b/examples/udp-ipv6/udp-server.c @@ -43,7 +43,7 @@ static struct uip_udp_conn *server_conn; PROCESS(udp_server_process, "UDP server process"); -AUTOSTART_PROCESSES(&udp_server_process); +AUTOSTART_PROCESSES(&resolv_process,&udp_server_process); /*---------------------------------------------------------------------------*/ static void tcpip_handler(void) @@ -94,6 +94,10 @@ PROCESS_THREAD(udp_server_process, ev, data) PROCESS_BEGIN(); PRINTF("UDP server started\n"); +#if RESOLV_CONF_SUPPORTS_MDNS + resolv_set_hostname("contiki-udp-server"); +#endif + #if UIP_CONF_ROUTER uip_ip6addr(&ipaddr, 0xaaaa, 0, 0, 0, 0, 0, 0, 0); uip_ds6_set_addr_iid(&ipaddr, &uip_lladdr); diff --git a/examples/wget/wget.c b/examples/wget/wget.c index 5cac9b885..a792ad3ed 100644 --- a/examples/wget/wget.c +++ b/examples/wget/wget.c @@ -113,15 +113,16 @@ start_get(void) #if UIP_UDP /* First check if the host is an IP address. */ if(uiplib_ipaddrconv(host, &addr) == 0) { - + uip_ipaddr_t *addrptr; /* Try to lookup the hostname. If it fails, we initiate a hostname lookup and print out an informative message on the statusbar. */ - if(resolv_lookup(host) == NULL) { + if(resolv_lookup(host, &addrptr) != RESOLV_STATUS_CACHED) { resolv_query(host); puts("Resolving host..."); return; } + uip_ipaddr_copy(&addr, addrptr); } #else /* UIP_UDP */ uiplib_ipaddrconv(host, &addr); @@ -184,7 +185,7 @@ PROCESS_THREAD(wget_process, ev, data) } else if(ev == resolv_event_found) { /* Either found a hostname, or not. */ if((char *)data != NULL && - resolv_lookup((char *)data) != NULL) { + resolv_lookup((char *)data, NULL) == RESOLV_STATUS_CACHED) { start_get(); } else { puts("Host not found"); diff --git a/regression-tests/10-ipv6/01-cooja-ipv6-udp.csc b/regression-tests/10-ipv6/01-cooja-ipv6-udp.csc index c258e098e..8ba0cca25 100644 --- a/regression-tests/10-ipv6/01-cooja-ipv6-udp.csc +++ b/regression-tests/10-ipv6/01-cooja-ipv6-udp.csc @@ -49,7 +49,7 @@ make udp-server.cooja TARGET=cooja mtype512 Sender [CONTIKI_DIR]/examples/udp-ipv6/udp-client.c - make udp-client.cooja TARGET=cooja DEFINES=UDP_CONNECTION_ADDR=fe80::201:1:1:1 + make udp-client.cooja TARGET=cooja se.sics.cooja.interfaces.Position se.sics.cooja.interfaces.Battery se.sics.cooja.contikimote.interfaces.ContikiVib