Home | History | Annotate | Download | only in core
      1 /**
      2  * @file
      3  * DNS - host name to IP address resolver.
      4  *
      5  */
      6 
      7 /**
      8 
      9  * This file implements a DNS host name to IP address resolver.
     10 
     11  * Port to lwIP from uIP
     12  * by Jim Pettinato April 2007
     13 
     14  * uIP version Copyright (c) 2002-2003, Adam Dunkels.
     15  * All rights reserved.
     16  *
     17  * Redistribution and use in source and binary forms, with or without
     18  * modification, are permitted provided that the following conditions
     19  * are met:
     20  * 1. Redistributions of source code must retain the above copyright
     21  *    notice, this list of conditions and the following disclaimer.
     22  * 2. Redistributions in binary form must reproduce the above copyright
     23  *    notice, this list of conditions and the following disclaimer in the
     24  *    documentation and/or other materials provided with the distribution.
     25  * 3. The name of the author may not be used to endorse or promote
     26  *    products derived from this software without specific prior
     27  *    written permission.
     28  *
     29  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     30  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     31  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     32  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     33  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     35  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     36  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     37  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     38  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     39  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     40  *
     41  *
     42  * DNS.C
     43  *
     44  * The lwIP DNS resolver functions are used to lookup a host name and
     45  * map it to a numerical IP address. It maintains a list of resolved
     46  * hostnames that can be queried with the dns_lookup() function.
     47  * New hostnames can be resolved using the dns_query() function.
     48  *
     49  * The lwIP version of the resolver also adds a non-blocking version of
     50  * gethostbyname() that will work with a raw API application. This function
     51  * checks for an IP address string first and converts it if it is valid.
     52  * gethostbyname() then does a dns_lookup() to see if the name is
     53  * already in the table. If so, the IP is returned. If not, a query is
     54  * issued and the function returns with a ERR_INPROGRESS status. The app
     55  * using the dns client must then go into a waiting state.
     56  *
     57  * Once a hostname has been resolved (or found to be non-existent),
     58  * the resolver code calls a specified callback function (which
     59  * must be implemented by the module that uses the resolver).
     60  */
     61 
     62 /*-----------------------------------------------------------------------------
     63  * RFC 1035 - Domain names - implementation and specification
     64  * RFC 2181 - Clarifications to the DNS Specification
     65  *----------------------------------------------------------------------------*/
     66 
     67 /** @todo: define good default values (rfc compliance) */
     68 /** @todo: improve answer parsing, more checkings... */
     69 /** @todo: check RFC1035 - 7.3. Processing responses */
     70 
     71 /*-----------------------------------------------------------------------------
     72  * Includes
     73  *----------------------------------------------------------------------------*/
     74 
     75 #include "lwip/opt.h"
     76 
     77 #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
     78 
     79 #include "lwip/udp.h"
     80 #include "lwip/mem.h"
     81 #include "lwip/memp.h"
     82 #include "lwip/dns.h"
     83 
     84 #include <string.h>
     85 
     86 /** DNS server IP address */
     87 #ifndef DNS_SERVER_ADDRESS
     88 #define DNS_SERVER_ADDRESS(ipaddr)        (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
     89 #endif
     90 
     91 /** DNS server port address */
     92 #ifndef DNS_SERVER_PORT
     93 #define DNS_SERVER_PORT           53
     94 #endif
     95 
     96 /** DNS maximum number of retries when asking for a name, before "timeout". */
     97 #ifndef DNS_MAX_RETRIES
     98 #define DNS_MAX_RETRIES           4
     99 #endif
    100 
    101 /** DNS resource record max. TTL (one week as default) */
    102 #ifndef DNS_MAX_TTL
    103 #define DNS_MAX_TTL               604800
    104 #endif
    105 
    106 /* DNS protocol flags */
    107 #define DNS_FLAG1_RESPONSE        0x80
    108 #define DNS_FLAG1_OPCODE_STATUS   0x10
    109 #define DNS_FLAG1_OPCODE_INVERSE  0x08
    110 #define DNS_FLAG1_OPCODE_STANDARD 0x00
    111 #define DNS_FLAG1_AUTHORATIVE     0x04
    112 #define DNS_FLAG1_TRUNC           0x02
    113 #define DNS_FLAG1_RD              0x01
    114 #define DNS_FLAG2_RA              0x80
    115 #define DNS_FLAG2_ERR_MASK        0x0f
    116 #define DNS_FLAG2_ERR_NONE        0x00
    117 #define DNS_FLAG2_ERR_NAME        0x03
    118 
    119 /* DNS protocol states */
    120 #define DNS_STATE_UNUSED          0
    121 #define DNS_STATE_NEW             1
    122 #define DNS_STATE_ASKING          2
    123 #define DNS_STATE_DONE            3
    124 
    125 #ifdef PACK_STRUCT_USE_INCLUDES
    126 #  include "arch/bpstruct.h"
    127 #endif
    128 PACK_STRUCT_BEGIN
    129 /** DNS message header */
    130 struct dns_hdr {
    131   PACK_STRUCT_FIELD(u16_t id);
    132   PACK_STRUCT_FIELD(u8_t flags1);
    133   PACK_STRUCT_FIELD(u8_t flags2);
    134   PACK_STRUCT_FIELD(u16_t numquestions);
    135   PACK_STRUCT_FIELD(u16_t numanswers);
    136   PACK_STRUCT_FIELD(u16_t numauthrr);
    137   PACK_STRUCT_FIELD(u16_t numextrarr);
    138 } PACK_STRUCT_STRUCT;
    139 PACK_STRUCT_END
    140 #ifdef PACK_STRUCT_USE_INCLUDES
    141 #  include "arch/epstruct.h"
    142 #endif
    143 #define SIZEOF_DNS_HDR 12
    144 
    145 /** DNS query message structure.
    146     No packing needed: only used locally on the stack. */
    147 struct dns_query {
    148   /* DNS query record starts with either a domain name or a pointer
    149      to a name already present somewhere in the packet. */
    150   u16_t type;
    151   u16_t cls;
    152 };
    153 #define SIZEOF_DNS_QUERY 4
    154 
    155 /** DNS answer message structure.
    156     No packing needed: only used locally on the stack. */
    157 struct dns_answer {
    158   /* DNS answer record starts with either a domain name or a pointer
    159      to a name already present somewhere in the packet. */
    160   u16_t type;
    161   u16_t cls;
    162   u32_t ttl;
    163   u16_t len;
    164 };
    165 #define SIZEOF_DNS_ANSWER 10
    166 
    167 /** DNS table entry */
    168 struct dns_table_entry {
    169   u8_t  state;
    170   u8_t  numdns;
    171   u8_t  tmr;
    172   u8_t  retries;
    173   u8_t  seqno;
    174   u8_t  err;
    175   u32_t ttl;
    176   char name[DNS_MAX_NAME_LENGTH];
    177   ip_addr_t ipaddr;
    178   /* pointer to callback on DNS query done */
    179   dns_found_callback found;
    180   void *arg;
    181 };
    182 
    183 #if DNS_LOCAL_HOSTLIST
    184 
    185 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
    186 /** Local host-list. For hostnames in this list, no
    187  *  external name resolution is performed */
    188 static struct local_hostlist_entry *local_hostlist_dynamic;
    189 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
    190 
    191 /** Defining this allows the local_hostlist_static to be placed in a different
    192  * linker section (e.g. FLASH) */
    193 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
    194 #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
    195 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
    196 /** Defining this allows the local_hostlist_static to be placed in a different
    197  * linker section (e.g. FLASH) */
    198 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
    199 #define DNS_LOCAL_HOSTLIST_STORAGE_POST
    200 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
    201 DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
    202   DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
    203 
    204 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
    205 
    206 static void dns_init_local();
    207 #endif /* DNS_LOCAL_HOSTLIST */
    208 
    209 
    210 /* forward declarations */
    211 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
    212 static void dns_check_entries(void);
    213 
    214 /*-----------------------------------------------------------------------------
    215  * Globales
    216  *----------------------------------------------------------------------------*/
    217 
    218 /* DNS variables */
    219 static struct udp_pcb        *dns_pcb;
    220 static u8_t                   dns_seqno;
    221 static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
    222 static ip_addr_t              dns_servers[DNS_MAX_SERVERS];
    223 /** Contiguous buffer for processing responses */
    224 static u8_t                   dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
    225 static u8_t*                  dns_payload;
    226 
    227 /**
    228  * Initialize the resolver: set up the UDP pcb and configure the default server
    229  * (DNS_SERVER_ADDRESS).
    230  */
    231 void
    232 dns_init()
    233 {
    234   ip_addr_t dnsserver;
    235 
    236   dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
    237 
    238   /* initialize default DNS server address */
    239   DNS_SERVER_ADDRESS(&dnsserver);
    240 
    241   LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
    242 
    243   /* if dns client not yet initialized... */
    244   if (dns_pcb == NULL) {
    245     dns_pcb = udp_new();
    246 
    247     if (dns_pcb != NULL) {
    248       /* initialize DNS table not needed (initialized to zero since it is a
    249        * global variable) */
    250       LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
    251         DNS_STATE_UNUSED == 0);
    252 
    253       /* initialize DNS client */
    254       udp_bind(dns_pcb, IP_ADDR_ANY, 0);
    255       udp_recv(dns_pcb, dns_recv, NULL);
    256 
    257       /* initialize default DNS primary server */
    258       dns_setserver(0, &dnsserver);
    259     }
    260   }
    261 #if DNS_LOCAL_HOSTLIST
    262   dns_init_local();
    263 #endif
    264 }
    265 
    266 /**
    267  * Initialize one of the DNS servers.
    268  *
    269  * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
    270  * @param dnsserver IP address of the DNS server to set
    271  */
    272 void
    273 dns_setserver(u8_t numdns, ip_addr_t *dnsserver)
    274 {
    275   /*
    276    * hpa: the lwip code has the dnsserver->addr != 0 test, but that would
    277    * seem to indicate that there is no way to cancel a previously given
    278    * DNS server...
    279    */
    280   if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
    281       (dnsserver != NULL) /* && !ip_addr_isany(dnsserver) */) {
    282     dns_servers[numdns] = (*dnsserver);
    283   }
    284 }
    285 
    286 /**
    287  * Obtain one of the currently configured DNS server.
    288  *
    289  * @param numdns the index of the DNS server
    290  * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
    291  *         server has not been configured.
    292  */
    293 ip_addr_t
    294 dns_getserver(u8_t numdns)
    295 {
    296   if (numdns < DNS_MAX_SERVERS) {
    297     return dns_servers[numdns];
    298   } else {
    299     return *IP_ADDR_ANY;
    300   }
    301 }
    302 
    303 /**
    304  * The DNS resolver client timer - handle retries and timeouts and should
    305  * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
    306  */
    307 void
    308 dns_tmr(void)
    309 {
    310   if (dns_pcb != NULL) {
    311     LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
    312     dns_check_entries();
    313   }
    314 }
    315 
    316 #if DNS_LOCAL_HOSTLIST
    317 static void
    318 dns_init_local()
    319 {
    320 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
    321   int i;
    322   struct local_hostlist_entry *entry;
    323   /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
    324   struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
    325   size_t namelen;
    326   for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
    327     struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
    328     LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
    329     namelen = strlen(init_entry->name);
    330     LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
    331     entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
    332     LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
    333     if (entry != NULL) {
    334       entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
    335       MEMCPY((char*)entry->name, init_entry->name, namelen);
    336       ((char*)entry->name)[namelen] = 0;
    337       entry->addr = init_entry->addr;
    338       entry->next = local_hostlist_dynamic;
    339       local_hostlist_dynamic = entry;
    340     }
    341   }
    342 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
    343 }
    344 
    345 /**
    346  * Scans the local host-list for a hostname.
    347  *
    348  * @param hostname Hostname to look for in the local host-list
    349  * @return The first IP address for the hostname in the local host-list or
    350  *         IPADDR_NONE if not found.
    351  */
    352 static u32_t
    353 dns_lookup_local(const char *hostname)
    354 {
    355 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
    356   struct local_hostlist_entry *entry = local_hostlist_dynamic;
    357   while(entry != NULL) {
    358     if(strcmp(entry->name, hostname) == 0) {
    359       return ip4_addr_get_u32(&entry->addr);
    360     }
    361     entry = entry->next;
    362   }
    363 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
    364   int i;
    365   for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
    366     if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
    367       return ip4_addr_get_u32(&local_hostlist_static[i].addr);
    368     }
    369   }
    370 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
    371   return IPADDR_NONE;
    372 }
    373 
    374 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
    375 /** Remove all entries from the local host-list for a specific hostname
    376  * and/or IP addess
    377  *
    378  * @param hostname hostname for which entries shall be removed from the local
    379  *                 host-list
    380  * @param addr address for which entries shall be removed from the local host-list
    381  * @return the number of removed entries
    382  */
    383 int
    384 dns_local_removehost(const char *hostname, const ip_addr_t *addr)
    385 {
    386   int removed = 0;
    387   struct local_hostlist_entry *entry = local_hostlist_dynamic;
    388   struct local_hostlist_entry *last_entry = NULL;
    389   while (entry != NULL) {
    390     if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
    391         ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
    392       struct local_hostlist_entry *free_entry;
    393       if (last_entry != NULL) {
    394         last_entry->next = entry->next;
    395       } else {
    396         local_hostlist_dynamic = entry->next;
    397       }
    398       free_entry = entry;
    399       entry = entry->next;
    400       memp_free(MEMP_LOCALHOSTLIST, free_entry);
    401       removed++;
    402     } else {
    403       last_entry = entry;
    404       entry = entry->next;
    405     }
    406   }
    407   return removed;
    408 }
    409 
    410 /**
    411  * Add a hostname/IP address pair to the local host-list.
    412  * Duplicates are not checked.
    413  *
    414  * @param hostname hostname of the new entry
    415  * @param addr IP address of the new entry
    416  * @return ERR_OK if succeeded or ERR_MEM on memory error
    417  */
    418 err_t
    419 dns_local_addhost(const char *hostname, const ip_addr_t *addr)
    420 {
    421   struct local_hostlist_entry *entry;
    422   size_t namelen;
    423   LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
    424   namelen = strlen(hostname);
    425   LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
    426   entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
    427   if (entry == NULL) {
    428     return ERR_MEM;
    429   }
    430   entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
    431   MEMCPY((char*)entry->name, hostname, namelen);
    432   ((char*)entry->name)[namelen] = 0;
    433   ip_addr_copy(entry->addr, *addr);
    434   entry->next = local_hostlist_dynamic;
    435   local_hostlist_dynamic = entry;
    436   return ERR_OK;
    437 }
    438 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
    439 #endif /* DNS_LOCAL_HOSTLIST */
    440 
    441 /**
    442  * Look up a hostname in the array of known hostnames.
    443  *
    444  * @note This function only looks in the internal array of known
    445  * hostnames, it does not send out a query for the hostname if none
    446  * was found. The function dns_enqueue() can be used to send a query
    447  * for a hostname.
    448  *
    449  * @param name the hostname to look up
    450  * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
    451  *         better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
    452  *         was not found in the cached dns_table.
    453  */
    454 static u32_t
    455 dns_lookup(const char *name)
    456 {
    457   u8_t i;
    458 #if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
    459   u32_t addr;
    460 #endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
    461 #if DNS_LOCAL_HOSTLIST
    462   if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
    463     return addr;
    464   }
    465 #endif /* DNS_LOCAL_HOSTLIST */
    466 #ifdef DNS_LOOKUP_LOCAL_EXTERN
    467   if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
    468     return addr;
    469   }
    470 #endif /* DNS_LOOKUP_LOCAL_EXTERN */
    471 
    472   /* Walk through name list, return entry if found. If not, return NULL. */
    473   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
    474     if ((dns_table[i].state == DNS_STATE_DONE) &&
    475         (strcmp(name, dns_table[i].name) == 0)) {
    476       LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
    477       ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
    478       LWIP_DEBUGF(DNS_DEBUG, ("\n"));
    479       return ip4_addr_get_u32(&dns_table[i].ipaddr);
    480     }
    481   }
    482 
    483   return IPADDR_NONE;
    484 }
    485 
    486 #if DNS_DOES_NAME_CHECK
    487 /**
    488  * Compare the "dotted" name "query" with the encoded name "response"
    489  * to make sure an answer from the DNS server matches the current dns_table
    490  * entry (otherwise, answers might arrive late for hostname not on the list
    491  * any more).
    492  *
    493  * @param query hostname (not encoded) from the dns_table
    494  * @param response encoded hostname in the DNS response
    495  * @return 0: names equal; 1: names differ
    496  */
    497 static u8_t
    498 dns_compare_name(unsigned char *query, unsigned char *response)
    499 {
    500   unsigned char n;
    501 
    502   do {
    503     n = *response++;
    504     /** @see RFC 1035 - 4.1.4. Message compression */
    505     if ((n & 0xc0) == 0xc0) {
    506       /* Compressed name */
    507       break;
    508     } else {
    509       /* Not compressed name */
    510       while (n > 0) {
    511         if ((*query) != (*response)) {
    512           return 1;
    513         }
    514         ++response;
    515         ++query;
    516         --n;
    517       };
    518       ++query;
    519     }
    520   } while (*response != 0);
    521 
    522   return 0;
    523 }
    524 #endif /* DNS_DOES_NAME_CHECK */
    525 
    526 /**
    527  * Walk through a compact encoded DNS name and return the end of the name.
    528  *
    529  * @param query encoded DNS name in the DNS server response
    530  * @return end of the name
    531  */
    532 static unsigned char *
    533 dns_parse_name(unsigned char *query)
    534 {
    535   unsigned char n;
    536 
    537   do {
    538     n = *query++;
    539     /** @see RFC 1035 - 4.1.4. Message compression */
    540     if ((n & 0xc0) == 0xc0) {
    541       /* Compressed name */
    542       break;
    543     } else {
    544       /* Not compressed name */
    545       while (n > 0) {
    546         ++query;
    547         --n;
    548       };
    549     }
    550   } while (*query != 0);
    551 
    552   return query + 1;
    553 }
    554 
    555 /**
    556  * Send a DNS query packet.
    557  *
    558  * @param numdns index of the DNS server in the dns_servers table
    559  * @param name hostname to query
    560  * @param id index of the hostname in dns_table, used as transaction ID in the
    561  *        DNS query packet
    562  * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
    563  */
    564 static err_t
    565 dns_send(u8_t numdns, const char* name, u8_t id)
    566 {
    567   err_t err;
    568   struct dns_hdr *hdr;
    569   struct dns_query qry;
    570   struct pbuf *p;
    571   char *query, *nptr;
    572   const char *pHostname;
    573   u8_t n;
    574 
    575   LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
    576               (u16_t)(numdns), name));
    577   LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
    578   LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
    579 
    580   /* if here, we have either a new query or a retry on a previous query to process */
    581   p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH +
    582                  SIZEOF_DNS_QUERY, PBUF_RAM);
    583   if (p != NULL) {
    584     LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
    585     /* fill dns header */
    586     hdr = (struct dns_hdr*)p->payload;
    587     memset(hdr, 0, SIZEOF_DNS_HDR);
    588     hdr->id = htons(id);
    589     hdr->flags1 = DNS_FLAG1_RD;
    590     hdr->numquestions = PP_HTONS(1);
    591     query = (char*)hdr + SIZEOF_DNS_HDR;
    592     pHostname = name;
    593     --pHostname;
    594 
    595     /* convert hostname into suitable query format. */
    596     do {
    597       ++pHostname;
    598       nptr = query;
    599       ++query;
    600       for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
    601         *query = *pHostname;
    602         ++query;
    603         ++n;
    604       }
    605       *nptr = n;
    606     } while(*pHostname != 0);
    607     *query++='\0';
    608 
    609     /* fill dns query */
    610     qry.type = PP_HTONS(DNS_RRTYPE_A);
    611     qry.cls = PP_HTONS(DNS_RRCLASS_IN);
    612     SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
    613 
    614     /* resize pbuf to the exact dns query */
    615     pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))));
    616 
    617     /* connect to the server for faster receiving */
    618     udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
    619     /* send dns packet */
    620     err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
    621 
    622     /* free pbuf */
    623     pbuf_free(p);
    624   } else {
    625     err = ERR_MEM;
    626   }
    627 
    628   return err;
    629 }
    630 
    631 /**
    632  * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
    633  * Check an entry in the dns_table:
    634  * - send out query for new entries
    635  * - retry old pending entries on timeout (also with different servers)
    636  * - remove completed entries from the table if their TTL has expired
    637  *
    638  * @param i index of the dns_table entry to check
    639  */
    640 static void
    641 dns_check_entry(u8_t i)
    642 {
    643   err_t err;
    644   struct dns_table_entry *pEntry = &dns_table[i];
    645 
    646   LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
    647 
    648   switch(pEntry->state) {
    649 
    650     case DNS_STATE_NEW: {
    651       /* initialize new entry */
    652       pEntry->state   = DNS_STATE_ASKING;
    653       pEntry->numdns  = 0;
    654       pEntry->tmr     = 1;
    655       pEntry->retries = 0;
    656 
    657       /* send DNS packet for this entry */
    658       err = dns_send(pEntry->numdns, pEntry->name, i);
    659       if (err != ERR_OK) {
    660         LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
    661                     ("dns_send returned error: %s\n", lwip_strerr(err)));
    662       }
    663       break;
    664     }
    665 
    666     case DNS_STATE_ASKING: {
    667       if (--pEntry->tmr == 0) {
    668         if (++pEntry->retries == DNS_MAX_RETRIES) {
    669           if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
    670             /* change of server */
    671             pEntry->numdns++;
    672             pEntry->tmr     = 1;
    673             pEntry->retries = 0;
    674             break;
    675           } else {
    676             LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
    677             /* call specified callback function if provided */
    678             if (pEntry->found)
    679               (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
    680             /* flush this entry */
    681             pEntry->state   = DNS_STATE_UNUSED;
    682             pEntry->found   = NULL;
    683             break;
    684           }
    685         }
    686 
    687         /* wait longer for the next retry */
    688         pEntry->tmr = pEntry->retries;
    689 
    690         /* send DNS packet for this entry */
    691         err = dns_send(pEntry->numdns, pEntry->name, i);
    692         if (err != ERR_OK) {
    693           LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
    694                       ("dns_send returned error: %s\n", lwip_strerr(err)));
    695         }
    696       }
    697       break;
    698     }
    699 
    700     case DNS_STATE_DONE: {
    701       /* if the time to live is nul */
    702       if (--pEntry->ttl == 0) {
    703         LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
    704         /* flush this entry */
    705         pEntry->state = DNS_STATE_UNUSED;
    706         pEntry->found = NULL;
    707       }
    708       break;
    709     }
    710     case DNS_STATE_UNUSED:
    711       /* nothing to do */
    712       break;
    713     default:
    714       LWIP_ASSERT("unknown dns_table entry state:", 0);
    715       break;
    716   }
    717 }
    718 
    719 /**
    720  * Call dns_check_entry for each entry in dns_table - check all entries.
    721  */
    722 static void
    723 dns_check_entries(void)
    724 {
    725   u8_t i;
    726 
    727   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
    728     dns_check_entry(i);
    729   }
    730 }
    731 
    732 /**
    733  * Receive input function for DNS response packets arriving for the dns UDP pcb.
    734  *
    735  * @params see udp.h
    736  */
    737 static void
    738 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
    739 {
    740   u16_t i;
    741   char *pHostname;
    742   struct dns_hdr *hdr;
    743   struct dns_answer ans;
    744   struct dns_table_entry *pEntry;
    745   u16_t nquestions, nanswers;
    746 
    747   LWIP_UNUSED_ARG(arg);
    748   LWIP_UNUSED_ARG(pcb);
    749   LWIP_UNUSED_ARG(addr);
    750   LWIP_UNUSED_ARG(port);
    751 
    752   /* is the dns message too big ? */
    753   if (p->tot_len > DNS_MSG_SIZE) {
    754     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
    755     /* free pbuf and return */
    756     goto memerr;
    757   }
    758 
    759   /* is the dns message big enough ? */
    760   if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
    761     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
    762     /* free pbuf and return */
    763     goto memerr;
    764   }
    765 
    766   /* copy dns payload inside static buffer for processing */
    767   if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
    768     /* The ID in the DNS header should be our entry into the name table. */
    769     hdr = (struct dns_hdr*)dns_payload;
    770     i = htons(hdr->id);
    771     if (i < DNS_TABLE_SIZE) {
    772       pEntry = &dns_table[i];
    773       if(pEntry->state == DNS_STATE_ASKING) {
    774         /* This entry is now completed. */
    775         pEntry->state = DNS_STATE_DONE;
    776         pEntry->err   = hdr->flags2 & DNS_FLAG2_ERR_MASK;
    777 
    778         /* We only care about the question(s) and the answers. The authrr
    779            and the extrarr are simply discarded. */
    780         nquestions = htons(hdr->numquestions);
    781         nanswers   = htons(hdr->numanswers);
    782 
    783         /* Check for error. If so, call callback to inform. */
    784         if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
    785           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
    786           /* call callback to indicate error, clean up memory and return */
    787           goto responseerr;
    788         }
    789 
    790 #if DNS_DOES_NAME_CHECK
    791         /* Check if the name in the "question" part match with the name in the entry. */
    792         if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
    793           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
    794           /* call callback to indicate error, clean up memory and return */
    795           goto responseerr;
    796         }
    797 #endif /* DNS_DOES_NAME_CHECK */
    798 
    799         /* Skip the name in the "question" part */
    800         pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
    801 
    802         while (nanswers > 0) {
    803           /* skip answer resource record's host name */
    804           pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
    805 
    806           /* Check for IP address type and Internet class. Others are discarded. */
    807           SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
    808           if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) &&
    809              (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) {
    810             /* read the answer resource record's TTL, and maximize it if needed */
    811             pEntry->ttl = ntohl(ans.ttl);
    812             if (pEntry->ttl > DNS_MAX_TTL) {
    813               pEntry->ttl = DNS_MAX_TTL;
    814             }
    815             /* read the IP address after answer resource record's header */
    816             SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
    817             LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
    818             ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
    819             LWIP_DEBUGF(DNS_DEBUG, ("\n"));
    820             /* call specified callback function if provided */
    821             if (pEntry->found) {
    822               (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
    823             }
    824             /* deallocate memory and return */
    825             goto memerr;
    826           } else {
    827             pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
    828           }
    829           --nanswers;
    830         }
    831         LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
    832         /* call callback to indicate error, clean up memory and return */
    833         goto responseerr;
    834       }
    835     }
    836   }
    837 
    838   /* deallocate memory and return */
    839   goto memerr;
    840 
    841 responseerr:
    842   /* ERROR: call specified callback function with NULL as name to indicate an error */
    843   if (pEntry->found) {
    844     (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
    845   }
    846   /* flush this entry */
    847   pEntry->state = DNS_STATE_UNUSED;
    848   pEntry->found = NULL;
    849 
    850 memerr:
    851   /* free pbuf */
    852   pbuf_free(p);
    853   return;
    854 }
    855 
    856 /**
    857  * Queues a new hostname to resolve and sends out a DNS query for that hostname
    858  *
    859  * @param name the hostname that is to be queried
    860  * @param found a callback founction to be called on success, failure or timeout
    861  * @param callback_arg argument to pass to the callback function
    862  * @return @return a err_t return code.
    863  */
    864 static err_t
    865 dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)
    866 {
    867   u8_t i;
    868   u8_t lseq, lseqi;
    869   struct dns_table_entry *pEntry = NULL;
    870   size_t namelen;
    871 
    872   /* search an unused entry, or the oldest one */
    873   lseq = lseqi = 0;
    874   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
    875     pEntry = &dns_table[i];
    876     /* is it an unused entry ? */
    877     if (pEntry->state == DNS_STATE_UNUSED)
    878       break;
    879 
    880     /* check if this is the oldest completed entry */
    881     if (pEntry->state == DNS_STATE_DONE) {
    882       if ((dns_seqno - pEntry->seqno) > lseq) {
    883         lseq = dns_seqno - pEntry->seqno;
    884         lseqi = i;
    885       }
    886     }
    887   }
    888 
    889   /* if we don't have found an unused entry, use the oldest completed one */
    890   if (i == DNS_TABLE_SIZE) {
    891     if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
    892       /* no entry can't be used now, table is full */
    893       LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
    894       return ERR_MEM;
    895     } else {
    896       /* use the oldest completed one */
    897       i = lseqi;
    898       pEntry = &dns_table[i];
    899     }
    900   }
    901 
    902   /* use this entry */
    903   LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
    904 
    905   /* fill the entry */
    906   pEntry->state = DNS_STATE_NEW;
    907   pEntry->seqno = dns_seqno++;
    908   pEntry->found = found;
    909   pEntry->arg   = callback_arg;
    910   namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1);
    911   MEMCPY(pEntry->name, name, namelen);
    912   pEntry->name[namelen] = 0;
    913 
    914   /* force to send query without waiting timer */
    915   dns_check_entry(i);
    916 
    917   /* dns query is enqueued */
    918   return ERR_INPROGRESS;
    919 }
    920 
    921 /**
    922  * Resolve a hostname (string) into an IP address.
    923  * NON-BLOCKING callback version for use with raw API!!!
    924  *
    925  * Returns immediately with one of err_t return codes:
    926  * - ERR_OK if hostname is a valid IP address string or the host
    927  *   name is already in the local names table.
    928  * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
    929  *   for resolution if no errors are present.
    930  * - ERR_ARG: dns client not initialized or invalid hostname
    931  *
    932  * @param hostname the hostname that is to be queried
    933  * @param addr pointer to a ip_addr_t where to store the address if it is already
    934  *             cached in the dns_table (only valid if ERR_OK is returned!)
    935  * @param found a callback function to be called on success, failure or timeout (only if
    936  *              ERR_INPROGRESS is returned!)
    937  * @param callback_arg argument to pass to the callback function
    938  * @return a err_t return code.
    939  */
    940 err_t
    941 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
    942                   void *callback_arg)
    943 {
    944   u32_t ipaddr;
    945   /* not initialized or no valid server yet, or invalid addr pointer
    946    * or invalid hostname or invalid hostname length */
    947   if ((dns_pcb == NULL) || (addr == NULL) ||
    948       (!hostname) || (!hostname[0]) ||
    949       (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {
    950     return ERR_ARG;
    951   }
    952 
    953 #if LWIP_HAVE_LOOPIF
    954   if (strcmp(hostname, "localhost")==0) {
    955     ip_addr_set_loopback(addr);
    956     return ERR_OK;
    957   }
    958 #endif /* LWIP_HAVE_LOOPIF */
    959 
    960   /* host name already in octet notation? set ip addr and return ERR_OK */
    961   ipaddr = ipaddr_addr(hostname);
    962   if (ipaddr == IPADDR_NONE) {
    963     /* already have this address cached? */
    964     ipaddr = dns_lookup(hostname);
    965   }
    966   if (ipaddr != IPADDR_NONE) {
    967     ip4_addr_set_u32(addr, ipaddr);
    968     return ERR_OK;
    969   }
    970 
    971   /* queue query with specified callback */
    972   return dns_enqueue(hostname, found, callback_arg);
    973 }
    974 
    975 #endif /* LWIP_DNS */
    976