/*
 * 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.
 *
 *
 */

/**
 * \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.
 */

/**
 * \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.
 */

#include "net/ip/tcpip.h"
#include "net/ip/resolv.h"
#include "net/ip/uip-udp-packet.h"
#include "net/ip/uip-nameserver.h"
#include "lib/random.h"

#ifndef DEBUG
#define DEBUG CONTIKI_TARGET_COOJA
#endif

#if UIP_UDP

#include <string.h>
#include <stdio.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 /* __SDCC */

#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

#ifdef RESOLV_CONF_AUTO_REMOVE_TRAILING_DOTS
#define RESOLV_AUTO_REMOVE_TRAILING_DOTS RESOLV_CONF_AUTO_REMOVE_TRAILING_DOTS
#else
#define RESOLV_AUTO_REMOVE_TRAILING_DOTS RESOLV_CONF_SUPPORTS_MDNS
#endif

#ifdef RESOLV_CONF_VERIFY_ANSWER_NAMES
#define RESOLV_VERIFY_ANSWER_NAMES RESOLV_CONF_VERIFY_ANSWER_NAMES
#else
#define RESOLV_VERIFY_ANSWER_NAMES RESOLV_CONF_SUPPORTS_MDNS
#endif

#ifdef RESOLV_CONF_SUPPORTS_RECORD_EXPIRATION
#define RESOLV_SUPPORTS_RECORD_EXPIRATION RESOLV_CONF_SUPPORTS_RECORD_EXPIRATION
#else
#define RESOLV_SUPPORTS_RECORD_EXPIRATION 1
#endif

#if RESOLV_CONF_SUPPORTS_MDNS && !RESOLV_VERIFY_ANSWER_NAMES
#error RESOLV_CONF_SUPPORTS_MDNS cannot be set without RESOLV_CONF_VERIFY_ANSWER_NAMES
#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

#if NETSTACK_CONF_WITH_IPV6
#define NATIVE_DNS_TYPE DNS_TYPE_AAAA /* IPv6 */
#else
#define NATIVE_DNS_TYPE DNS_TYPE_A    /* IPv4 */
#endif

#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;
};

/** \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 NETSTACK_CONF_WITH_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;
  uint16_t id;
  uint8_t retries;
  uint8_t seqno;
#if RESOLV_SUPPORTS_RECORD_EXPIRATION
  unsigned long expiration;
#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
  uip_ipaddr_t ipaddr;
  uint8_t err;
  uint8_t server;
#if RESOLV_CONF_SUPPORTS_MDNS
  int is_mdns:1, is_probe:1;
#endif
  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);

/** \internal The DNS question message structure. */
struct dns_question {
  uint16_t type;
  uint16_t class;
};

#if RESOLV_CONF_SUPPORTS_MDNS
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;

static const uip_ipaddr_t resolv_mdns_addr =
#if NETSTACK_CONF_WITH_IPV6
  { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfb } };
#include "net/ipv6/uip-ds6.h"
#else  /* NETSTACK_CONF_WITH_IPV6 */
  { { 224, 0, 0, 251 } };
#endif /* NETSTACK_CONF_WITH_IPV6 */
static int mdns_needs_host_announce;

PROCESS(mdns_probe_process, "mDNS probe");
#endif /* RESOLV_CONF_SUPPORTS_MDNS */

/*---------------------------------------------------------------------------*/
#if RESOLV_VERIFY_ANSWER_NAMES || VERBOSE_DEBUG
/** \internal
 * \brief Decodes a DNS name from the DNS format into the given string.
 * \return 1 upon success, 0 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 uint8_t
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 0;
      }
    }

    n = *query++;

    if(n) {
      //DEBUG_PRINTF(".");
      *dest++ = '.';
      --len;
    }
  }

  //DEBUG_PRINTF("\"\n");
  *dest = 0;
  return len != 0;
}
/*---------------------------------------------------------------------------*/
/** \internal
 */
static uint8_t
dns_name_isequal(const unsigned char *queryptr, const char *name,
                 const unsigned char *packet)
{
  unsigned char n = *queryptr++;

  if(*name == 0)
    return 0;

  while(n) {
    if(n & 0xc0) {
      queryptr = packet + queryptr[0] + ((n & ~0xC0) << 8);
      n = *queryptr++;
    }

    for(; n; --n) {
      if(!*name) {
        return 0;
      }

      if(tolower((unsigned int)*name++) != tolower((unsigned int)*queryptr++)) {
        return 0;
      }
    }

    n = *queryptr++;

    if((n != 0) && (*name++ != '.')) {
      return 0;
    }
  }

  if(*name == '.')
    ++name;

  return name[0] == 0;
}
#endif /* RESOLV_VERIFY_ANSWER_NAMES */
/*---------------------------------------------------------------------------*/
/** \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 = 1;
}
/*---------------------------------------------------------------------------*/
/** \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)
{
#if NETSTACK_CONF_WITH_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_linklocal(&uip_ds6_if.addr_list[i].ipaddr)
#endif
      ) {
      if(!*count) {
        queryptr = encode_name(queryptr, resolv_hostname);
      } else {
        /* Use name compression to refer back to the first name */
        *queryptr++ = 0xc0;
        *queryptr++ = sizeof(struct dns_hdr);
      }

      *queryptr++ = (uint8_t) ((NATIVE_DNS_TYPE) >> 8);
      *queryptr++ = (uint8_t) ((NATIVE_DNS_TYPE));

      *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 /* NETSTACK_CONF_WITH_IPV6 */
  struct dns_answer *ans;

  queryptr = encode_name(queryptr, resolv_hostname);
  ans = (struct dns_answer *)queryptr;
  ans->type = UIP_HTONS(NATIVE_DNS_TYPE);
  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 /* NETSTACK_CONF_WITH_IPV6 */
  return queryptr;
}
/*---------------------------------------------------------------------------*/
/** \internal
 * Called when we need to announce ourselves
 */
static size_t
mdns_prep_host_announce_packet(void)
{
  static const struct {
    uint16_t type;
    uint16_t class;
    uint16_t ttl[2];
    uint16_t len;
    uint8_t data[8];

  } nsec_record = {
    UIP_HTONS(DNS_TYPE_NSEC),
    UIP_HTONS(DNS_CLASS_IN | 0x8000),
    { 0, UIP_HTONS(120) },
    UIP_HTONS(8),

    {
      0xc0,
      sizeof(struct dns_hdr), /* Name compression. Re-using the name of first record. */
      0x00,
      0x04,

#if NETSTACK_CONF_WITH_IPV6
      0x00,
      0x00,
      0x00,
      0x08,
#else /* NETSTACK_CONF_WITH_IPV6 */
      0x40,
      0x00,
      0x00,
      0x00,
#endif /* NETSTACK_CONF_WITH_IPV6 */
    }
  };

  unsigned char *queryptr;

  uint8_t total_answers = 0;

  /* Be aware that, unless `ARCH_DOESNT_NEED_ALIGNED_STRUCTS` is set,
   * writing directly to the uint16_t members of this struct is an error. */
  struct dns_hdr *hdr = (struct dns_hdr *)uip_appdata;

  /* Zero out the header */
  memset((void *)hdr, 0, sizeof(*hdr));

  hdr->flags1 |= DNS_FLAG1_RESPONSE | DNS_FLAG1_AUTHORATIVE;

  queryptr = (unsigned char *)uip_appdata + 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 {
    /* Name compression. Re-using the name of first record. */
    *queryptr++ = 0xc0;
    *queryptr++ = sizeof(*hdr);
  }

  memcpy((void *)queryptr, (void *)&nsec_record, sizeof(nsec_record));

  queryptr += sizeof(nsec_record);

  /* This platform might be picky about alignment. To avoid the possibility
   * of doing an unaligned write, we are going to do this manually. */
  ((uint8_t*)&hdr->numanswers)[1] = total_answers;
  ((uint8_t*)&hdr->numextrarr)[1] = 1;

  return (queryptr - (unsigned char *)uip_appdata);
}
#endif /* RESOLV_CONF_SUPPORTS_MDNS */
/*---------------------------------------------------------------------------*/
static char
try_next_server(struct namemap *namemapptr)
{
#if VERBOSE_DEBUG
  printf("server %d\n", namemapptr->server);
#endif
  namemapptr->server++;
  if(uip_nameserver_get(namemapptr->server) != NULL) {
    namemapptr->retries = 0;
    return 1;
  }
  namemapptr->server = 0;
  return 0;
}
/*---------------------------------------------------------------------------*/
/** \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 /* RESOLV_CONF_SUPPORTS_MDNS */
          if(++namemapptr->retries == RESOLV_CONF_MAX_RETRIES)
#endif /* RESOLV_CONF_SUPPORTS_MDNS */
          {
            /* Try the next server (if possible) before failing. Otherwise
               simply mark the entry as failed. */
            if(try_next_server(namemapptr) == 0) {
              /* STATE_ERROR basically means "not found". */
              namemapptr->state = STATE_ERROR;

#if RESOLV_SUPPORTS_RECORD_EXPIRATION
              /* Keep the "not found" error valid for 30 seconds */
              namemapptr->expiration = clock_seconds() + 30;
#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */

              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 /* RESOLV_CONF_SUPPORTS_MDNS */
        } 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 = random_rand();
      namemapptr->id = hdr->id;
#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 /* RESOLV_CONF_SUPPORTS_MDNS */
      hdr->flags1 = DNS_FLAG1_RD;
#endif /* RESOLV_CONF_SUPPORTS_MDNS */
      hdr->numquestions = UIP_HTONS(1);
      query = (unsigned char *)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 /* RESOLV_CONF_SUPPORTS_MDNS */
      {
        *query++ = (uint8_t) ((NATIVE_DNS_TYPE) >> 8);
        *query++ = (uint8_t) ((NATIVE_DNS_TYPE));
      }
      *query++ = (uint8_t) ((DNS_CLASS_IN) >> 8);
      *query++ = (uint8_t) ((DNS_CLASS_IN));
#if RESOLV_CONF_SUPPORTS_MDNS
      if(namemapptr->is_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);
        }
        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 %s for \"%s\".\n", i,
               namemapptr->is_probe?"probe":"request",namemapptr->name);
      } else {
        uip_udp_packet_sendto(resolv_conn, uip_appdata,
                              (query - (uint8_t *) uip_appdata),
                              (const uip_ipaddr_t *)
                                uip_nameserver_get(namemapptr->server), 
                              UIP_HTONS(DNS_PORT));

        PRINTF("resolver: (i=%d) Sent DNS request for \"%s\".\n", i,
               namemapptr->name);
      }
#else /* RESOLV_CONF_SUPPORTS_MDNS */
      uip_udp_packet_sendto(resolv_conn, uip_appdata,
                            (query - (uint8_t *) uip_appdata),
                            uip_nameserver_get(namemapptr->server), 
                            UIP_HTONS(DNS_PORT));
      PRINTF("resolver: (i=%d) Sent DNS request for \"%s\".\n", i,
             namemapptr->name);
#endif /* RESOLV_CONF_SUPPORTS_MDNS */
      break;
    }
  }
}
/*---------------------------------------------------------------------------*/
/** \internal
 * Called when new UDP data arrives.
 */
static void
newdata(void)
{
  static uint8_t nquestions, nanswers;

  static int8_t i;

  register struct namemap *namemapptr = NULL;

  struct dns_answer *ans;

  register struct dns_hdr const *hdr = (struct dns_hdr *)uip_appdata;

  unsigned char *queryptr = (unsigned char *)hdr + sizeof(*hdr);

  const uint8_t is_request = ((hdr->flags1 & ~1) == 0) && (hdr->flags2 == 0);

  /* 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);

  queryptr = (unsigned char *)hdr + sizeof(*hdr);
  i = 0;

  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));

  if(is_request && (nquestions == 0)) {
    /* Skip requests with no questions. */
    DEBUG_PRINTF("resolver: Skipping request with no questions.\n");
    return;
  }

/** QUESTION HANDLING SECTION ************************************************/

  for(; nquestions > 0;
      queryptr = skip_name(queryptr) + sizeof(struct dns_question),
      --nquestions
  ) {
#if RESOLV_CONF_SUPPORTS_MDNS
    if(!is_request) {
      /* If this isn't a request, we don't need to bother
       * looking at the individual questions. For the most
       * part, this loop to just used to skip past them.
       */
      continue;
    }

    {
      struct dns_question *question = (struct dns_question *)skip_name(queryptr);

#if !ARCH_DOESNT_NEED_ALIGNED_STRUCTS
      static struct dns_question aligned;
      memcpy(&aligned, question, sizeof(aligned));
      question = &aligned;
#endif /* !ARCH_DOESNT_NEED_ALIGNED_STRUCTS */

      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(NATIVE_DNS_TYPE)))) {
        /* 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 {
        static uint8_t nauthrr;
        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);
        }
      }
    }
#endif /* RESOLV_CONF_SUPPORTS_MDNS */
  }

/** ANSWER HANDLING SECTION **************************************************/

  if(nanswers == 0) {
    /* Skip responses with no 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 */
  {
    for(i = 0; i < RESOLV_ENTRIES; ++i) {
      namemapptr = &names[i];
      if(namemapptr->state == STATE_ASKING &&
         namemapptr->id == hdr->id) {
        break;
      }
    }

    if(i >= RESOLV_ENTRIES || i < 0 || namemapptr->state != STATE_ASKING) {
      PRINTF("resolver: DNS response has bad ID (%04X) \n", uip_ntohs(hdr->id));
      return;
    }

    PRINTF("resolver: Incoming response for \"%s\".\n", namemapptr->name);

    /* We'll change this to DONE when we find the record. */
    namemapptr->state = STATE_ERROR;

    namemapptr->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;

#if RESOLV_SUPPORTS_RECORD_EXPIRATION
    /* If we remain in the error state, keep it cached for 30 seconds. */
    namemapptr->expiration = clock_seconds() + 30;
#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */

    /* Check for error. If so, call callback to inform. */
    if(namemapptr->err != 0) {
      namemapptr->state = STATE_ERROR;
      resolv_found(namemapptr->name, NULL);
      return;
    }
  }

  i = 0;

  /* Answer parsing loop */
  while(nanswers > 0) {
    ans = (struct dns_answer *)skip_name(queryptr);

#if !ARCH_DOESNT_NEED_ALIGNED_STRUCTS
    {
      static struct dns_answer aligned;
      memcpy(&aligned, ans, sizeof(aligned));
      ans = &aligned;
    }
#endif /* !ARCH_DOESNT_NEED_ALIGNED_STRUCTS */

#if VERBOSE_DEBUG
    static char debug_name[40];
    decode_name(queryptr, debug_name, uip_appdata);
    DEBUG_PRINTF("resolver: Answer %d: \"%s\", type %d, class %d, ttl %d, length %d\n",
                 ++i, 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 /* VERBOSE_DEBUG */

    /* 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(ans->type != UIP_HTONS(NATIVE_DNS_TYPE)) {
      goto skip_to_next_answer;
    }

#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)
#if RESOLV_SUPPORTS_RECORD_EXPIRATION
          || (namemapptr->state == STATE_DONE && clock_seconds() > namemapptr->expiration)
#endif /* RESOLV_SUPPORTS_RECORD_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 /* RESOLV_CONF_SUPPORTS_MDNS */
    {
      /* This will force us to stop even if there are more answers. */
      nanswers = 1;
    }

/*  This is disabled for now, so that we don't fail on CNAME records.
#if RESOLV_VERIFY_ANSWER_NAMES
    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;
    }
#endif
*/

    DEBUG_PRINTF("resolver: Answer for \"%s\" is usable.\n", namemapptr->name);

    namemapptr->state = STATE_DONE;
#if RESOLV_SUPPORTS_RECORD_EXPIRATION
    namemapptr->expiration = ans->ttl[1] + (ans->ttl[0] << 8);
    namemapptr->expiration += clock_seconds();
#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */

    uip_ipaddr_copy(&namemapptr->ipaddr, (uip_ipaddr_t *) ans->ipaddr);

    resolv_found(namemapptr->name, &namemapptr->ipaddr);
    break;

  skip_to_next_answer:
    queryptr = (unsigned char *)skip_name(queryptr) + 10 + uip_htons(ans->len);
    --nanswers;
  }

  /* Got to this point there's no answer, try next nameserver if available
     since this one doesn't know the answer */
#if RESOLV_CONF_SUPPORTS_MDNS
  if(nanswers == 0 && UIP_UDP_BUF->srcport != UIP_HTONS(MDNS_PORT) 
      && hdr->id != 0)
#else
  if(nanswers == 0) 
#endif
  {
    if(try_next_server(namemapptr)) {
      namemapptr->state = STATE_ASKING;
      process_post(&resolv_process, PROCESS_EVENT_TIMER, NULL);
    }
  }

}
/*---------------------------------------------------------------------------*/
#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);

#if RESOLV_CONF_SUPPORTS_MDNS
  PRINTF("resolver: Supports MDNS.\n");
  uip_udp_bind(resolv_conn, UIP_HTONS(MDNS_PORT));

#if NETSTACK_CONF_WITH_IPV6
  uip_ds6_maddr_add(&resolv_mdns_addr);
#else
  /* TODO: Is there anything we need to do here for IPv4 multicast? */
#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 /* RESOLV_CONF_SUPPORTS_MDNS */
          {
            check_entries();
          }
        }
      }
    }

#if RESOLV_CONF_SUPPORTS_MDNS
    if(mdns_needs_host_announce) {
      tcpip_poll_udp(resolv_conn);
    }
#endif /* RESOLV_CONF_SUPPORTS_MDNS */
  }

  PROCESS_END();
}
/*---------------------------------------------------------------------------*/
static void
init(void)
{
  static uint8_t initialized = 0;
  if(!initialized) {
    process_start(&resolv_process, NULL);
    initialized = 1;
  }
}
/*---------------------------------------------------------------------------*/
#if RESOLV_AUTO_REMOVE_TRAILING_DOTS
static const char *
remove_trailing_dots(const char *name) {
  static char dns_name_without_dots[RESOLV_CONF_MAX_DOMAIN_NAME_SIZE + 1];
  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;
  }
  return name;
}
#else /* RESOLV_AUTO_REMOVE_TRAILING_DOTS */
#define remove_trailing_dots(x) (x)
#endif /* RESOLV_AUTO_REMOVE_TRAILING_DOTS */
/*---------------------------------------------------------------------------*/
/**
 * 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;

  init();
  
  lseq = lseqi = 0;

  /* Remove trailing dots, if present. */
  name = remove_trailing_dots(name);

  for(i = 0; i < RESOLV_ENTRIES; ++i) {
    nameptr = &names[i];
    if(0 == strcasecmp(nameptr->name, name)) {
      break;
    }
    if((nameptr->state == STATE_UNUSED)
#if RESOLV_SUPPORTS_RECORD_EXPIRATION
      || (nameptr->state == STATE_DONE && clock_seconds() > nameptr->expiration)
#endif /* RESOLV_SUPPORTS_RECORD_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 /* RESOLV_CONF_SUPPORTS_MDNS */

  /* 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. */
  name = remove_trailing_dots(name);

#if UIP_CONF_LOOPBACK_INTERFACE
  if(strcmp(name, "localhost")) {
    static uip_ipaddr_t loopback =
#if NETSTACK_CONF_WITH_IPV6
    { { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } };
#else /* NETSTACK_CONF_WITH_IPV6 */
    { { 127, 0, 0, 1 } };
#endif /* NETSTACK_CONF_WITH_IPV6 */
    if(ipaddr) {
      *ipaddr = &loopback;
    }
    ret = RESOLV_STATUS_CACHED;
  }
#endif /* UIP_CONF_LOOPBACK_INTERFACE */

  /* 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:
        ret = RESOLV_STATUS_CACHED;
#if RESOLV_SUPPORTS_RECORD_EXPIRATION
        if(clock_seconds() > nameptr->expiration) {
          ret = RESOLV_STATUS_EXPIRED;
        }
#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
        break;
      case STATE_NEW:
      case STATE_ASKING:
        ret = RESOLV_STATUS_RESOLVING;
        break;
      /* Almost certainly a not-found error from server */
      case STATE_ERROR:
        ret = RESOLV_STATUS_NOT_FOUND;
#if RESOLV_SUPPORTS_RECORD_EXPIRATION
        if(clock_seconds() > nameptr->expiration) {
          ret = RESOLV_STATUS_UNCACHED;
        }
#endif /* RESOLV_SUPPORTS_RECORD_EXPIRATION */
        break;
      }

      if(ipaddr) {
        *ipaddr = &nameptr->ipaddr;
      }

      /* Break out of for loop. */
      break;
    }
  }

#if VERBOSE_DEBUG
  switch (ret) {
  case RESOLV_STATUS_CACHED:
    if(ipaddr) {
      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 /* VERBOSE_DEBUG */

  return ret;
}
/*---------------------------------------------------------------------------*/
/** \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 NETSTACK_CONF_WITH_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) - 1); /* -1 in order to fit the terminating null byte. */
      }

      /* 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 /* RESOLV_CONF_SUPPORTS_MDNS */

#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 /* VERBOSE_DEBUG */

  process_post(PROCESS_BROADCAST, resolv_event_found, name);
}
/*---------------------------------------------------------------------------*/
#endif /* UIP_UDP */

/** @} */
/** @} */
