f145c17039
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.
1517 lines
40 KiB
C
1517 lines
40 KiB
C
/**
|
|
* \addtogroup uip
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* \defgroup uipdns uIP hostname resolver functions
|
|
* @{
|
|
*
|
|
* The uIP DNS resolver functions are used to lookup a hostname and
|
|
* map it to a numerical IP address. It maintains a list of resolved
|
|
* hostnames that can be queried with the resolv_lookup()
|
|
* function. New hostnames can be resolved using the resolv_query()
|
|
* function.
|
|
*
|
|
* The event resolv_event_found is posted when a hostname has been
|
|
* resolved. It is up to the receiving process to determine if the
|
|
* correct hostname has been found by calling the resolv_lookup()
|
|
* function with the hostname.
|
|
*/
|
|
|
|
/**
|
|
* \file
|
|
* DNS host name to IP address resolver.
|
|
* \author Adam Dunkels <adam@dunkels.com>
|
|
* \author Robert Quattlebaum <darco@deepdarc.com>
|
|
*
|
|
* This file implements a DNS host name to IP address resolver,
|
|
* as well as an MDNS responder and resolver.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2002-2003, Adam Dunkels.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote
|
|
* products derived from this software without specific prior
|
|
* written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
|
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* This file is part of the uIP TCP/IP stack.
|
|
*
|
|
*
|
|
*/
|
|
|
|
#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 <string.h>
|
|
#include <stdio.h>
|
|
#include <stdbool.h>
|
|
#include <ctype.h>
|
|
|
|
#ifndef NULL
|
|
#define NULL (void *)0
|
|
#endif /* NULL */
|
|
|
|
#if !defined(__SDCC) && defined(SDCC_REVISION)
|
|
#define __SDCC 1
|
|
#endif
|
|
|
|
#if VERBOSE_DEBUG
|
|
#define DEBUG_PRINTF(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define DEBUG_PRINTF(...) do { } while(0)
|
|
#endif
|
|
|
|
#if DEBUG || VERBOSE_DEBUG
|
|
#define PRINTF(...) printf(__VA_ARGS__)
|
|
#else
|
|
#define PRINTF(...) do { } while(0)
|
|
#endif
|
|
|
|
#ifdef __SDCC
|
|
static int
|
|
strncasecmp(const char *s1, const char *s2, size_t n)
|
|
{
|
|
/* 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
|
|
|
|
#define UIP_UDP_BUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])
|
|
|
|
/* 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 {
|
|
uint16_t id;
|
|
uint8_t flags1, flags2;
|
|
#define DNS_FLAG1_RESPONSE 0x80
|
|
#define DNS_FLAG1_OPCODE_STATUS 0x10
|
|
#define DNS_FLAG1_OPCODE_INVERSE 0x08
|
|
#define DNS_FLAG1_OPCODE_STANDARD 0x00
|
|
#define DNS_FLAG1_AUTHORATIVE 0x04
|
|
#define DNS_FLAG1_TRUNC 0x02
|
|
#define DNS_FLAG1_RD 0x01
|
|
#define DNS_FLAG2_RA 0x80
|
|
#define DNS_FLAG2_ERR_MASK 0x0f
|
|
#define DNS_FLAG2_ERR_NONE 0x00
|
|
#define DNS_FLAG2_ERR_NAME 0x03
|
|
uint16_t numquestions;
|
|
uint16_t numanswers;
|
|
uint16_t numauthrr;
|
|
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:
|
|
* <https://developers.google.com/speed/public-dns/docs/using>
|
|
*/
|
|
#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. */
|
|
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_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;
|
|
#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
|
|
#define RESOLV_ENTRIES 4
|
|
#else /* UIP_CONF_RESOLV_ENTRIES */
|
|
#define RESOLV_ENTRIES UIP_CONF_RESOLV_ENTRIES
|
|
#endif /* UIP_CONF_RESOLV_ENTRIES */
|
|
|
|
static struct namemap names[RESOLV_ENTRIES];
|
|
|
|
static uint8_t seqno;
|
|
|
|
static struct uip_udp_conn *resolv_conn = NULL;
|
|
|
|
static struct etimer retry;
|
|
|
|
process_event_t resolv_event_found;
|
|
|
|
PROCESS(resolv_process, "DNS resolver");
|
|
|
|
static void resolv_found(char *name, uip_ipaddr_t * ipaddr);
|
|
|
|
enum {
|
|
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
|
|
* \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.
|
|
*
|
|
* \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("<skip-to-%d>",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 *
|
|
skip_name(unsigned char *query)
|
|
{
|
|
unsigned char n;
|
|
|
|
DEBUG_PRINTF("resolver: skip name: ");
|
|
|
|
do {
|
|
n = *query;
|
|
if(n & 0xc0) {
|
|
DEBUG_PRINTF("<skip-to-%d>", query[0] + ((n & ~0xC0) << 8));
|
|
query++;
|
|
break;
|
|
}
|
|
|
|
query++;
|
|
|
|
while(n > 0) {
|
|
DEBUG_PRINTF("%c", *query);
|
|
++query;
|
|
--n;
|
|
};
|
|
DEBUG_PRINTF(".");
|
|
} while(*query != 0);
|
|
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;
|
|
|
|
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 / 4);
|
|
if(namemapptr->state == STATE_ASKING) {
|
|
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;
|
|
}
|
|
hdr = (struct dns_hdr *)uip_appdata;
|
|
memset(hdr, 0, sizeof(struct dns_hdr));
|
|
hdr->id = RESOLV_ENCODE_INDEX(i);
|
|
#if RESOLV_CONF_SUPPORTS_MDNS
|
|
if(!namemapptr->is_mdns || namemapptr->is_probe) {
|
|
hdr->flags1 = DNS_FLAG1_RD;
|
|
}
|
|
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)
|
|
{
|
|
struct dns_answer *ans;
|
|
|
|
struct dns_hdr const *hdr = (struct dns_hdr *)uip_appdata;
|
|
|
|
unsigned char *queryptr = (unsigned char *)hdr + sizeof(*hdr);
|
|
|
|
register struct namemap *namemapptr;
|
|
|
|
static uint8_t nquestions, nanswers, nauthrr = 0;
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
PROCESS_BEGIN();
|
|
|
|
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) {
|
|
tcpip_poll_udp(resolv_conn);
|
|
} else if(ev == tcpip_event) {
|
|
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 = 0;
|
|
|
|
lseq = lseqi = 0;
|
|
|
|
{ /* 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(0 == strcasecmp(nameptr->name, name)) {
|
|
break;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
if(i == RESOLV_ENTRIES) {
|
|
i = lseqi;
|
|
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_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.
|
|
*
|
|
* \note This function only looks in the internal array of known
|
|
* hostnames, it does not send out a query for the hostname if none
|
|
* was found. The function resolv_query() can be used to send a query
|
|
* for a hostname.
|
|
*
|
|
*/
|
|
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;
|
|
|
|
{ /* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
#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.
|
|
*
|
|
* \return A pointer to a 4-byte representation of the IP address of
|
|
* the currently configured DNS server or NULL if no DNS server has
|
|
* been configured.
|
|
*/
|
|
uip_ipaddr_t *
|
|
resolv_getserver(void)
|
|
{
|
|
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)
|
|
{
|
|
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)
|
|
{
|
|
#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_UDP */
|
|
|
|
/** @} */
|
|
/** @} */
|