ON Semiconductor / mbed-os

Dependents:   mbed-TFT-example-NCS36510 mbed-Accelerometer-example-NCS36510 mbed-Accelerometer-example-NCS36510

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers lwip_dns.c Source File

lwip_dns.c

Go to the documentation of this file.
00001 /**
00002  * @file
00003  * DNS - host name to IP address resolver.
00004  */
00005 
00006 /*
00007  * Port to lwIP from uIP
00008  * by Jim Pettinato April 2007
00009  *
00010  * security fixes and more by Simon Goldschmidt
00011  *
00012  * uIP version Copyright (c) 2002-2003, Adam Dunkels.
00013  * All rights reserved.
00014  *
00015  * Redistribution and use in source and binary forms, with or without
00016  * modification, are permitted provided that the following conditions
00017  * are met:
00018  * 1. Redistributions of source code must retain the above copyright
00019  *    notice, this list of conditions and the following disclaimer.
00020  * 2. Redistributions in binary form must reproduce the above copyright
00021  *    notice, this list of conditions and the following disclaimer in the
00022  *    documentation and/or other materials provided with the distribution.
00023  * 3. The name of the author may not be used to endorse or promote
00024  *    products derived from this software without specific prior
00025  *    written permission.
00026  *
00027  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
00028  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00029  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00030  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
00031  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00032  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
00033  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00034  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
00035  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00036  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00037  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00038  */
00039 
00040 /**
00041  * @defgroup dns DNS
00042  * @ingroup callbackstyle_api
00043  *
00044  * Implements a DNS host name to IP address resolver.
00045  *
00046  * The lwIP DNS resolver functions are used to lookup a host name and
00047  * map it to a numerical IP address. It maintains a list of resolved
00048  * hostnames that can be queried with the dns_lookup() function.
00049  * New hostnames can be resolved using the dns_query() function.
00050  *
00051  * The lwIP version of the resolver also adds a non-blocking version of
00052  * gethostbyname() that will work with a raw API application. This function
00053  * checks for an IP address string first and converts it if it is valid.
00054  * gethostbyname() then does a dns_lookup() to see if the name is
00055  * already in the table. If so, the IP is returned. If not, a query is
00056  * issued and the function returns with a ERR_INPROGRESS status. The app
00057  * using the dns client must then go into a waiting state.
00058  *
00059  * Once a hostname has been resolved (or found to be non-existent),
00060  * the resolver code calls a specified callback function (which
00061  * must be implemented by the module that uses the resolver).
00062  * 
00063  * All functions must be called from TCPIP thread.
00064  * 
00065  * @see @ref netconn_common for thread-safe access.
00066  */
00067 
00068 /*-----------------------------------------------------------------------------
00069  * RFC 1035 - Domain names - implementation and specification
00070  * RFC 2181 - Clarifications to the DNS Specification
00071  *----------------------------------------------------------------------------*/
00072 
00073 /** @todo: define good default values (rfc compliance) */
00074 /** @todo: improve answer parsing, more checkings... */
00075 /** @todo: check RFC1035 - 7.3. Processing responses */
00076 
00077 /*-----------------------------------------------------------------------------
00078  * Includes
00079  *----------------------------------------------------------------------------*/
00080 
00081 #include "lwip/opt.h"
00082 
00083 #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
00084 
00085 #include "lwip/udp.h"
00086 #include "lwip/mem.h"
00087 #include "lwip/memp.h"
00088 #include "lwip/dns.h"
00089 
00090 #include <string.h>
00091 
00092 /** Random generator function to create random TXIDs and source ports for queries */
00093 #ifndef DNS_RAND_TXID
00094 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0)
00095 #define DNS_RAND_TXID LWIP_RAND
00096 #else
00097 static u16_t dns_txid;
00098 #define DNS_RAND_TXID() (++dns_txid)
00099 #endif
00100 #endif
00101 
00102 /** Limits the source port to be >= 1024 by default */
00103 #ifndef DNS_PORT_ALLOWED
00104 #define DNS_PORT_ALLOWED(port) ((port) >= 1024)
00105 #endif
00106 
00107 /** DNS server port address */
00108 #ifndef DNS_SERVER_PORT
00109 #define DNS_SERVER_PORT           53
00110 #endif
00111 
00112 /** DNS maximum number of retries when asking for a name, before "timeout". */
00113 #ifndef DNS_MAX_RETRIES
00114 #define DNS_MAX_RETRIES           4
00115 #endif
00116 
00117 /** DNS resource record max. TTL (one week as default) */
00118 #ifndef DNS_MAX_TTL
00119 #define DNS_MAX_TTL               604800
00120 #elif DNS_MAX_TTL > 0x7FFFFFFF
00121 #error DNS_MAX_TTL must be a positive 32-bit value
00122 #endif
00123 
00124 /* The number of parallel requests (i.e. calls to dns_gethostbyname
00125  * that cannot be answered from the DNS table.
00126  * This is set to the table size by default.
00127  */
00128 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
00129 #ifndef DNS_MAX_REQUESTS
00130 #define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
00131 #endif
00132 #else
00133 /* In this configuration, both arrays have to have the same size and are used
00134  * like one entry (used/free) */
00135 #define DNS_MAX_REQUESTS          DNS_TABLE_SIZE
00136 #endif
00137 
00138 /* The number of UDP source ports used in parallel */
00139 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
00140 #ifndef DNS_MAX_SOURCE_PORTS
00141 #define DNS_MAX_SOURCE_PORTS      DNS_MAX_REQUESTS
00142 #endif
00143 #else
00144 #ifdef DNS_MAX_SOURCE_PORTS
00145 #undef DNS_MAX_SOURCE_PORTS
00146 #endif
00147 #define DNS_MAX_SOURCE_PORTS      1
00148 #endif
00149 
00150 #if LWIP_IPV4 && LWIP_IPV6
00151 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6))
00152 #define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) (IP_IS_V6_VAL(ip) ? LWIP_DNS_ADDRTYPE_IS_IPV6(t) : (!LWIP_DNS_ADDRTYPE_IS_IPV6(t)))
00153 #define LWIP_DNS_ADDRTYPE_ARG(x) , x
00154 #define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x
00155 #define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0)
00156 #else
00157 #if LWIP_IPV6
00158 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1
00159 #else
00160 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0
00161 #endif
00162 #define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1
00163 #define LWIP_DNS_ADDRTYPE_ARG(x)
00164 #define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0
00165 #define LWIP_DNS_SET_ADDRTYPE(x, y)
00166 #endif /* LWIP_IPV4 && LWIP_IPV6 */
00167 
00168 /** DNS field TYPE used for "Resource Records" */
00169 #define DNS_RRTYPE_A              1     /* a host address */
00170 #define DNS_RRTYPE_NS             2     /* an authoritative name server */
00171 #define DNS_RRTYPE_MD             3     /* a mail destination (Obsolete - use MX) */
00172 #define DNS_RRTYPE_MF             4     /* a mail forwarder (Obsolete - use MX) */
00173 #define DNS_RRTYPE_CNAME          5     /* the canonical name for an alias */
00174 #define DNS_RRTYPE_SOA            6     /* marks the start of a zone of authority */
00175 #define DNS_RRTYPE_MB             7     /* a mailbox domain name (EXPERIMENTAL) */
00176 #define DNS_RRTYPE_MG             8     /* a mail group member (EXPERIMENTAL) */
00177 #define DNS_RRTYPE_MR             9     /* a mail rename domain name (EXPERIMENTAL) */
00178 #define DNS_RRTYPE_NULL           10    /* a null RR (EXPERIMENTAL) */
00179 #define DNS_RRTYPE_WKS            11    /* a well known service description */
00180 #define DNS_RRTYPE_PTR            12    /* a domain name pointer */
00181 #define DNS_RRTYPE_HINFO          13    /* host information */
00182 #define DNS_RRTYPE_MINFO          14    /* mailbox or mail list information */
00183 #define DNS_RRTYPE_MX             15    /* mail exchange */
00184 #define DNS_RRTYPE_TXT            16    /* text strings */
00185 #define DNS_RRTYPE_AAAA           28    /* IPv6 address */
00186 
00187 /** DNS field CLASS used for "Resource Records" */
00188 #define DNS_RRCLASS_IN            1     /* the Internet */
00189 #define DNS_RRCLASS_CS            2     /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
00190 #define DNS_RRCLASS_CH            3     /* the CHAOS class */
00191 #define DNS_RRCLASS_HS            4     /* Hesiod [Dyer 87] */
00192 #define DNS_RRCLASS_FLUSH         0x800 /* Flush bit */
00193 
00194 /* DNS protocol flags */
00195 #define DNS_FLAG1_RESPONSE        0x80
00196 #define DNS_FLAG1_OPCODE_STATUS   0x10
00197 #define DNS_FLAG1_OPCODE_INVERSE  0x08
00198 #define DNS_FLAG1_OPCODE_STANDARD 0x00
00199 #define DNS_FLAG1_AUTHORATIVE     0x04
00200 #define DNS_FLAG1_TRUNC           0x02
00201 #define DNS_FLAG1_RD              0x01
00202 #define DNS_FLAG2_RA              0x80
00203 #define DNS_FLAG2_ERR_MASK        0x0f
00204 #define DNS_FLAG2_ERR_NONE        0x00
00205 #define DNS_FLAG2_ERR_NAME        0x03
00206 
00207 /* DNS protocol states */
00208 #define DNS_STATE_UNUSED            0
00209 #define DNS_STATE_NEW               1
00210 #define DNS_STATE_ASKING            2
00211 #define DNS_STATE_DONE              3
00212 
00213 #ifdef PACK_STRUCT_USE_INCLUDES
00214 #  include "arch/bpstruct.h"
00215 #endif
00216 PACK_STRUCT_BEGIN
00217 /** DNS message header */
00218 struct dns_hdr {
00219   PACK_STRUCT_FIELD(u16_t id);
00220   PACK_STRUCT_FLD_8(u8_t flags1);
00221   PACK_STRUCT_FLD_8(u8_t flags2);
00222   PACK_STRUCT_FIELD(u16_t numquestions);
00223   PACK_STRUCT_FIELD(u16_t numanswers);
00224   PACK_STRUCT_FIELD(u16_t numauthrr);
00225   PACK_STRUCT_FIELD(u16_t numextrarr);
00226 } PACK_STRUCT_STRUCT;
00227 PACK_STRUCT_END
00228 #ifdef PACK_STRUCT_USE_INCLUDES
00229 #  include "arch/epstruct.h"
00230 #endif
00231 #define SIZEOF_DNS_HDR 12
00232 
00233 /** DNS query message structure.
00234     No packing needed: only used locally on the stack. */
00235 struct dns_query {
00236   /* DNS query record starts with either a domain name or a pointer
00237      to a name already present somewhere in the packet. */
00238   u16_t type;
00239   u16_t cls;
00240 };
00241 #define SIZEOF_DNS_QUERY 4
00242 
00243 /** DNS answer message structure.
00244     No packing needed: only used locally on the stack. */
00245 struct dns_answer {
00246   /* DNS answer record starts with either a domain name or a pointer
00247      to a name already present somewhere in the packet. */
00248   u16_t type;
00249   u16_t cls;
00250   u32_t ttl;
00251   u16_t len;
00252 };
00253 #define SIZEOF_DNS_ANSWER 10
00254 /* maximum allowed size for the struct due to non-packed */
00255 #define SIZEOF_DNS_ANSWER_ASSERT 12
00256 
00257 /** DNS table entry */
00258 struct dns_table_entry {
00259   u32_t ttl;
00260   ip_addr_t ipaddr;
00261   u16_t txid;
00262   u8_t  state;
00263   u8_t  server_idx;
00264   u8_t  tmr;
00265   u8_t  retries;
00266   u8_t  seqno;
00267 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
00268   u8_t pcb_idx;
00269 #endif
00270   char name[DNS_MAX_NAME_LENGTH];
00271 #if LWIP_IPV4 && LWIP_IPV6
00272   u8_t reqaddrtype;
00273 #endif /* LWIP_IPV4 && LWIP_IPV6 */
00274 };
00275 
00276 /** DNS request table entry: used when dns_gehostbyname cannot answer the
00277  * request from the DNS table */
00278 struct dns_req_entry {
00279   /* pointer to callback on DNS query done */
00280   dns_found_callback found;
00281   /* argument passed to the callback function */
00282   void *arg;
00283 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
00284   u8_t dns_table_idx;
00285 #endif
00286 #if LWIP_IPV4 && LWIP_IPV6
00287   u8_t reqaddrtype;
00288 #endif /* LWIP_IPV4 && LWIP_IPV6 */
00289 };
00290 
00291 #if DNS_LOCAL_HOSTLIST
00292 
00293 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
00294 /** Local host-list. For hostnames in this list, no
00295  *  external name resolution is performed */
00296 static struct local_hostlist_entry *local_hostlist_dynamic;
00297 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
00298 
00299 /** Defining this allows the local_hostlist_static to be placed in a different
00300  * linker section (e.g. FLASH) */
00301 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
00302 #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
00303 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
00304 /** Defining this allows the local_hostlist_static to be placed in a different
00305  * linker section (e.g. FLASH) */
00306 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
00307 #define DNS_LOCAL_HOSTLIST_STORAGE_POST
00308 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
00309 DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
00310   DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
00311 
00312 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
00313 
00314 static void dns_init_local(void);
00315 #endif /* DNS_LOCAL_HOSTLIST */
00316 
00317 
00318 /* forward declarations */
00319 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
00320 static void dns_check_entries(void);
00321 static void dns_call_found(u8_t idx, ip_addr_t* addr);
00322 
00323 /*-----------------------------------------------------------------------------
00324  * Globals
00325  *----------------------------------------------------------------------------*/
00326 
00327 /* DNS variables */
00328 static struct udp_pcb        *dns_pcbs[DNS_MAX_SOURCE_PORTS];
00329 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
00330 static u8_t                   dns_last_pcb_idx;
00331 #endif
00332 static u8_t                   dns_seqno;
00333 static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
00334 static struct dns_req_entry   dns_requests[DNS_MAX_REQUESTS];
00335 static ip_addr_t              dns_servers[DNS_MAX_SERVERS];
00336 
00337 #ifndef LWIP_DNS_STRICMP
00338 #define LWIP_DNS_STRICMP(str1, str2) dns_stricmp(str1, str2)
00339 /**
00340  * A small but sufficient implementation for case insensitive strcmp.
00341  * This can be defined to e.g. stricmp for windows or strcasecmp for linux. */
00342 static int
00343 dns_stricmp(const char* str1, const char* str2)
00344 {
00345   char c1, c2;
00346 
00347   do {
00348     c1 = *str1++;
00349     c2 = *str2++;
00350     if (c1 != c2) {
00351       char c1_upc = c1 | 0x20;
00352       if ((c1_upc >= 'a') && (c1_upc <= 'z')) {
00353         /* characters are not equal an one is in the alphabet range:
00354         downcase both chars and check again */
00355         char c2_upc = c2 | 0x20;
00356         if (c1_upc != c2_upc) {
00357           /* still not equal */
00358           /* don't care for < or > */
00359           return 1;
00360         }
00361       } else {
00362         /* characters are not equal but none is in the alphabet range */
00363         return 1;
00364       }
00365     }
00366   } while (c1 != 0);
00367   return 0;
00368 }
00369 #endif /* LWIP_DNS_STRICMP */
00370 
00371 /**
00372  * Initialize the resolver: set up the UDP pcb and configure the default server
00373  * (if DNS_SERVER_ADDRESS is set).
00374  */
00375 void
00376 dns_init(void)
00377 {
00378 #ifdef DNS_SERVER_ADDRESS
00379   /* initialize default DNS server address */
00380   ip_addr_t dnsserver;
00381   DNS_SERVER_ADDRESS(&dnsserver);
00382   dns_setserver(0, &dnsserver);
00383 #endif /* DNS_SERVER_ADDRESS */
00384 
00385   LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
00386     sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
00387   LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
00388     sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
00389 
00390   LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
00391 
00392   /* if dns client not yet initialized... */
00393 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
00394   if (dns_pcbs[0] == NULL) {
00395     dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
00396     LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
00397 
00398     /* initialize DNS table not needed (initialized to zero since it is a
00399      * global variable) */
00400     LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
00401       DNS_STATE_UNUSED == 0);
00402 
00403     /* initialize DNS client */
00404     udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
00405     udp_recv(dns_pcbs[0], dns_recv, NULL);
00406   }
00407 #endif
00408 
00409 #if DNS_LOCAL_HOSTLIST
00410   dns_init_local();
00411 #endif
00412 }
00413 
00414 /**
00415  * @ingroup dns
00416  * Initialize one of the DNS servers.
00417  *
00418  * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
00419  * @param dnsserver IP address of the DNS server to set
00420  */
00421 void
00422 dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
00423 {
00424   if (numdns < DNS_MAX_SERVERS) {
00425     if (dnsserver != NULL) {
00426       dns_servers[numdns] = (*dnsserver);
00427     } else {
00428       dns_servers[numdns] = *IP_ADDR_ANY;
00429     }
00430   }
00431 }
00432 
00433 /**
00434  * @ingroup dns
00435  * Obtain one of the currently configured DNS server.
00436  *
00437  * @param numdns the index of the DNS server
00438  * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
00439  *         server has not been configured.
00440  */
00441 const ip_addr_t*
00442 dns_getserver(u8_t numdns)
00443 {
00444   if (numdns < DNS_MAX_SERVERS) {
00445     return &dns_servers[numdns];
00446   } else {
00447     return IP_ADDR_ANY;
00448   }
00449 }
00450 
00451 /**
00452  * The DNS resolver client timer - handle retries and timeouts and should
00453  * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
00454  */
00455 void
00456 dns_tmr(void)
00457 {
00458   LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
00459   dns_check_entries();
00460 }
00461 
00462 #if DNS_LOCAL_HOSTLIST
00463 static void
00464 dns_init_local(void)
00465 {
00466 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
00467   size_t i;
00468   struct local_hostlist_entry *entry;
00469   /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
00470   struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
00471   size_t namelen;
00472   for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) {
00473     struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
00474     LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
00475     namelen = strlen(init_entry->name);
00476     LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
00477     entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
00478     LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
00479     if (entry != NULL) {
00480       char* entry_name = (char*)entry + sizeof(struct local_hostlist_entry);
00481       MEMCPY(entry_name, init_entry->name, namelen);
00482       entry_name[namelen] = 0;
00483       entry->name = entry_name;
00484       entry->addr = init_entry->addr;
00485       entry->next = local_hostlist_dynamic;
00486       local_hostlist_dynamic = entry;
00487     }
00488   }
00489 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
00490 }
00491 
00492 /**
00493  * @ingroup dns
00494  * Scans the local host-list for a hostname.
00495  *
00496  * @param hostname Hostname to look for in the local host-list
00497  * @param addr the first IP address for the hostname in the local host-list or
00498  *         IPADDR_NONE if not found.
00499  * @return ERR_OK if found, ERR_ARG if not found
00500  */
00501 static err_t
00502 dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
00503 {
00504 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
00505   struct local_hostlist_entry *entry = local_hostlist_dynamic;
00506   while (entry != NULL) {
00507     if ((LWIP_DNS_STRICMP(entry->name, hostname) == 0) &&
00508         LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) {
00509       if (addr) {
00510         ip_addr_copy(*addr, entry->addr);
00511       }
00512       return ERR_OK;
00513     }
00514     entry = entry->next;
00515   }
00516 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
00517   size_t i;
00518   for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
00519     if ((LWIP_DNS_STRICMP(local_hostlist_static[i].name, hostname) == 0) &&
00520         LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) {
00521       if (addr) {
00522         ip_addr_copy(*addr, local_hostlist_static[i].addr);
00523       }
00524       return ERR_OK;
00525     }
00526   }
00527 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
00528   return ERR_ARG;
00529 }
00530 
00531 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
00532 /**
00533  * @ingroup dns
00534  * Remove all entries from the local host-list for a specific hostname
00535  * and/or IP address
00536  *
00537  * @param hostname hostname for which entries shall be removed from the local
00538  *                 host-list
00539  * @param addr address for which entries shall be removed from the local host-list
00540  * @return the number of removed entries
00541  */
00542 int
00543 dns_local_removehost(const char *hostname, const ip_addr_t *addr)
00544 {
00545   int removed = 0;
00546   struct local_hostlist_entry *entry = local_hostlist_dynamic;
00547   struct local_hostlist_entry *last_entry = NULL;
00548   while (entry != NULL) {
00549     if (((hostname == NULL) || !LWIP_DNS_STRICMP(entry->name, hostname)) &&
00550         ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
00551       struct local_hostlist_entry *free_entry;
00552       if (last_entry != NULL) {
00553         last_entry->next = entry->next;
00554       } else {
00555         local_hostlist_dynamic = entry->next;
00556       }
00557       free_entry = entry;
00558       entry = entry->next;
00559       memp_free(MEMP_LOCALHOSTLIST, free_entry);
00560       removed++;
00561     } else {
00562       last_entry = entry;
00563       entry = entry->next;
00564     }
00565   }
00566   return removed;
00567 }
00568 
00569 /**
00570  * @ingroup dns
00571  * Add a hostname/IP address pair to the local host-list.
00572  * Duplicates are not checked.
00573  *
00574  * @param hostname hostname of the new entry
00575  * @param addr IP address of the new entry
00576  * @return ERR_OK if succeeded or ERR_MEM on memory error
00577  */
00578 err_t
00579 dns_local_addhost(const char *hostname, const ip_addr_t *addr)
00580 {
00581   struct local_hostlist_entry *entry;
00582   size_t namelen;
00583   char* entry_name;
00584   LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
00585   namelen = strlen(hostname);
00586   LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
00587   entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
00588   if (entry == NULL) {
00589     return ERR_MEM;
00590   }
00591   entry_name = (char*)entry + sizeof(struct local_hostlist_entry);
00592   MEMCPY(entry_name, hostname, namelen);
00593   entry_name[namelen] = 0;
00594   entry->name = entry_name;
00595   ip_addr_copy(entry->addr, *addr);
00596   entry->next = local_hostlist_dynamic;
00597   local_hostlist_dynamic = entry;
00598   return ERR_OK;
00599 }
00600 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
00601 #endif /* DNS_LOCAL_HOSTLIST */
00602 
00603 /**
00604  * @ingroup dns
00605  * Look up a hostname in the array of known hostnames.
00606  *
00607  * @note This function only looks in the internal array of known
00608  * hostnames, it does not send out a query for the hostname if none
00609  * was found. The function dns_enqueue() can be used to send a query
00610  * for a hostname.
00611  *
00612  * @param name the hostname to look up
00613  * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to
00614  *         better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
00615  *         was not found in the cached dns_table.
00616  * @return ERR_OK if found, ERR_ARG if not found
00617  */
00618 static err_t
00619 dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
00620 {
00621   u8_t i;
00622 #if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
00623 #endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
00624 #if DNS_LOCAL_HOSTLIST
00625   if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
00626     return ERR_OK;
00627   }
00628 #endif /* DNS_LOCAL_HOSTLIST */
00629 #ifdef DNS_LOOKUP_LOCAL_EXTERN
00630   if (DNS_LOOKUP_LOCAL_EXTERN(name, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) {
00631     return ERR_OK;
00632   }
00633 #endif /* DNS_LOOKUP_LOCAL_EXTERN */
00634 
00635   /* Walk through name list, return entry if found. If not, return NULL. */
00636   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
00637     if ((dns_table[i].state == DNS_STATE_DONE) &&
00638         (LWIP_DNS_STRICMP(name, dns_table[i].name) == 0) &&
00639         LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) {
00640       LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
00641       ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
00642       LWIP_DEBUGF(DNS_DEBUG, ("\n"));
00643       if (addr) {
00644         ip_addr_copy(*addr, dns_table[i].ipaddr);
00645       }
00646       return ERR_OK;
00647     }
00648   }
00649 
00650   return ERR_ARG;
00651 }
00652 
00653 /**
00654  * Compare the "dotted" name "query" with the encoded name "response"
00655  * to make sure an answer from the DNS server matches the current dns_table
00656  * entry (otherwise, answers might arrive late for hostname not on the list
00657  * any more).
00658  *
00659  * @param query hostname (not encoded) from the dns_table
00660  * @param p pbuf containing the encoded hostname in the DNS response
00661  * @param start_offset offset into p where the name starts
00662  * @return 0xFFFF: names differ, other: names equal -> offset behind name
00663  */
00664 static u16_t
00665 dns_compare_name(const char *query, struct pbuf* p, u16_t start_offset)
00666 {
00667   unsigned char n;
00668   u16_t response_offset = start_offset;
00669 
00670   do {
00671     n = pbuf_get_at(p, response_offset++);
00672     /** @see RFC 1035 - 4.1.4. Message compression */
00673     if ((n & 0xc0) == 0xc0) {
00674       /* Compressed name: cannot be equal since we don't send them */
00675       return 0xFFFF;
00676     } else {
00677       /* Not compressed name */
00678       while (n > 0) {
00679         if ((*query) != pbuf_get_at(p, response_offset)) {
00680           return 0xFFFF;
00681         }
00682         ++response_offset;
00683         ++query;
00684         --n;
00685       }
00686       ++query;
00687     }
00688   } while (pbuf_get_at(p, response_offset) != 0);
00689 
00690   return response_offset + 1;
00691 }
00692 
00693 /**
00694  * Walk through a compact encoded DNS name and return the end of the name.
00695  *
00696  * @param p pbuf containing the name
00697  * @param query_idx start index into p pointing to encoded DNS name in the DNS server response
00698  * @return index to end of the name
00699  */
00700 static u16_t
00701 dns_parse_name(struct pbuf* p, u16_t query_idx)
00702 {
00703   unsigned char n;
00704 
00705   do {
00706     n = pbuf_get_at(p, query_idx++);
00707     /** @see RFC 1035 - 4.1.4. Message compression */
00708     if ((n & 0xc0) == 0xc0) {
00709       /* Compressed name */
00710       break;
00711     } else {
00712       /* Not compressed name */
00713       while (n > 0) {
00714         ++query_idx;
00715         --n;
00716       }
00717     }
00718   } while (pbuf_get_at(p, query_idx) != 0);
00719 
00720   return query_idx + 1;
00721 }
00722 
00723 /**
00724  * Send a DNS query packet.
00725  *
00726  * @param idx the DNS table entry index for which to send a request
00727  * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
00728  */
00729 static err_t
00730 dns_send(u8_t idx)
00731 {
00732   err_t err;
00733   struct dns_hdr hdr;
00734   struct dns_query qry;
00735   struct pbuf *p;
00736   u16_t query_idx, copy_len;
00737   const char *hostname, *hostname_part;
00738   u8_t n;
00739   u8_t pcb_idx;
00740   struct dns_table_entry* entry = &dns_table[idx];
00741 
00742   LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
00743               (u16_t)(entry->server_idx), entry->name));
00744   LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
00745   if (ip_addr_isany_val(dns_servers[entry->server_idx])) {
00746     /* DNS server not valid anymore, e.g. PPP netif has been shut down */
00747     /* call specified callback function if provided */
00748     dns_call_found(idx, NULL);
00749     /* flush this entry */
00750     entry->state = DNS_STATE_UNUSED;
00751     return ERR_OK;
00752   }
00753 
00754   /* if here, we have either a new query or a retry on a previous query to process */
00755   p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
00756                  SIZEOF_DNS_QUERY), PBUF_RAM);
00757   if (p != NULL) {
00758     /* fill dns header */
00759     memset(&hdr, 0, SIZEOF_DNS_HDR);
00760     hdr.id = htons(entry->txid);
00761     hdr.flags1 = DNS_FLAG1_RD;
00762     hdr.numquestions = PP_HTONS(1);
00763     pbuf_take(p, &hdr, SIZEOF_DNS_HDR);
00764     hostname = entry->name;
00765     --hostname;
00766 
00767     /* convert hostname into suitable query format. */
00768     query_idx = SIZEOF_DNS_HDR;
00769     do {
00770       ++hostname;
00771       hostname_part = hostname;
00772       for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) {
00773         ++n;
00774       }
00775       copy_len = (u16_t)(hostname - hostname_part);
00776       pbuf_put_at(p, query_idx, n);
00777       pbuf_take_at(p, hostname_part, copy_len, query_idx + 1);
00778       query_idx += n + 1;
00779     } while (*hostname != 0);
00780     pbuf_put_at(p, query_idx, 0);
00781     query_idx++;
00782 
00783     /* fill dns query */
00784     if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
00785       qry.type = PP_HTONS(DNS_RRTYPE_AAAA);
00786     } else {
00787       qry.type = PP_HTONS(DNS_RRTYPE_A);
00788     }
00789     qry.cls = PP_HTONS(DNS_RRCLASS_IN);
00790     pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx);
00791 
00792 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
00793     pcb_idx = entry->pcb_idx;
00794 #else
00795     pcb_idx = 0;
00796 #endif
00797     /* send dns packet */
00798     LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
00799       entry->txid, entry->name, entry->server_idx));
00800     err = udp_sendto(dns_pcbs[pcb_idx], p, &dns_servers[entry->server_idx], DNS_SERVER_PORT);
00801 
00802     /* free pbuf */
00803     pbuf_free(p);
00804   } else {
00805     err = ERR_MEM;
00806   }
00807 
00808   return err;
00809 }
00810 
00811 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
00812 static struct udp_pcb*
00813 dns_alloc_random_port(void)
00814 {
00815   err_t err;
00816   struct udp_pcb* ret;
00817 
00818   ret = udp_new_ip_type(IPADDR_TYPE_ANY);
00819   if (ret == NULL) {
00820     /* out of memory, have to reuse an existing pcb */
00821     return NULL;
00822   }
00823   do {
00824     u16_t port = (u16_t)DNS_RAND_TXID();
00825     if (!DNS_PORT_ALLOWED(port)) {
00826       /* this port is not allowed, try again */
00827       err = ERR_USE;
00828       continue;
00829     }
00830     err = udp_bind(ret, IP_ANY_TYPE, port);
00831   } while (err == ERR_USE);
00832   if (err != ERR_OK) {
00833     udp_remove(ret);
00834     return NULL;
00835   }
00836   udp_recv(ret, dns_recv, NULL);
00837   return ret;
00838 }
00839 
00840 /**
00841  * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used
00842  * for sending a request
00843  *
00844  * @return an index into dns_pcbs
00845  */
00846 static u8_t
00847 dns_alloc_pcb(void)
00848 {
00849   u8_t i;
00850   u8_t idx;
00851 
00852   for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) {
00853     if (dns_pcbs[i] == NULL) {
00854       break;
00855     }
00856   }
00857   if (i < DNS_MAX_SOURCE_PORTS) {
00858     dns_pcbs[i] = dns_alloc_random_port();
00859     if (dns_pcbs[i] != NULL) {
00860       /* succeeded */
00861       dns_last_pcb_idx = i;
00862       return i;
00863     }
00864   }
00865   /* if we come here, creating a new UDP pcb failed, so we have to use
00866      an already existing one */
00867   for (i = 0, idx = dns_last_pcb_idx + 1; i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
00868     if (idx >= DNS_MAX_SOURCE_PORTS) {
00869       idx = 0;
00870     }
00871     if (dns_pcbs[idx] != NULL) {
00872       dns_last_pcb_idx = idx;
00873       return idx;
00874     }
00875   }
00876   return DNS_MAX_SOURCE_PORTS;
00877 }
00878 #endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */
00879 
00880 /**
00881  * dns_call_found() - call the found callback and check if there are duplicate
00882  * entries for the given hostname. If there are any, their found callback will
00883  * be called and they will be removed.
00884  *
00885  * @param idx dns table index of the entry that is resolved or removed
00886  * @param addr IP address for the hostname (or NULL on error or memory shortage)
00887  */
00888 static void
00889 dns_call_found(u8_t idx, ip_addr_t* addr)
00890 {
00891 #if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0)
00892   u8_t i;
00893 #endif
00894 
00895 #if LWIP_IPV4 && LWIP_IPV6
00896   if (addr != NULL) {
00897     /* check that address type matches the request and adapt the table entry */
00898     if (IP_IS_V6_VAL(*addr)) {
00899       LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
00900       dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
00901     } else {
00902       LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
00903       dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
00904     }
00905   }
00906 #endif /* LWIP_IPV4 && LWIP_IPV6 */
00907 
00908 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
00909   for (i = 0; i < DNS_MAX_REQUESTS; i++) {
00910     if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) {
00911       (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg);
00912       /* flush this entry */
00913       dns_requests[i].found = NULL;
00914     }
00915   }
00916 #else
00917   if (dns_requests[idx].found) {
00918     (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg);
00919   }
00920   dns_requests[idx].found = NULL;
00921 #endif
00922 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
00923   /* close the pcb used unless other request are using it */
00924   for (i = 0; i < DNS_MAX_REQUESTS; i++) {
00925     if (i == idx) {
00926       continue; /* only check other requests */
00927     }
00928     if (dns_table[i].state == DNS_STATE_ASKING) {
00929       if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) {
00930         /* another request is still using the same pcb */
00931         dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
00932         break;
00933       }
00934     }
00935   }
00936   if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) {
00937     /* if we come here, the pcb is not used any more and can be removed */
00938     udp_remove(dns_pcbs[dns_table[idx].pcb_idx]);
00939     dns_pcbs[dns_table[idx].pcb_idx] = NULL;
00940     dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
00941   }
00942 #endif
00943 }
00944 
00945 /* Create a query transmission ID that is unique for all outstanding queries */
00946 static u16_t
00947 dns_create_txid(void)
00948 {
00949   u16_t txid;
00950   u8_t i;
00951 
00952 again:
00953   txid = (u16_t)DNS_RAND_TXID();
00954 
00955   /* check whether the ID is unique */
00956   for (i = 0; i < DNS_TABLE_SIZE; i++) {
00957     if ((dns_table[i].state == DNS_STATE_ASKING) &&
00958         (dns_table[i].txid == txid)) {
00959       /* ID already used by another pending query */
00960       goto again;
00961     }
00962   }
00963 
00964   return txid;
00965 }
00966 
00967 /**
00968  * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query.
00969  * Check an entry in the dns_table:
00970  * - send out query for new entries
00971  * - retry old pending entries on timeout (also with different servers)
00972  * - remove completed entries from the table if their TTL has expired
00973  *
00974  * @param i index of the dns_table entry to check
00975  */
00976 static void
00977 dns_check_entry(u8_t i)
00978 {
00979   err_t err;
00980   struct dns_table_entry *entry = &dns_table[i];
00981 
00982   LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
00983 
00984   switch (entry->state) {
00985 
00986     case DNS_STATE_NEW: {
00987       u16_t txid;
00988       /* initialize new entry */
00989       txid = dns_create_txid();
00990       entry->txid = txid;
00991       entry->state = DNS_STATE_ASKING;
00992       entry->server_idx = 0;
00993       entry->tmr = 1;
00994       entry->retries = 0;
00995 
00996       /* send DNS packet for this entry */
00997       err = dns_send(i);
00998       if (err != ERR_OK) {
00999         LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
01000                     ("dns_send returned error: %s\n", lwip_strerr(err)));
01001       }
01002       break;
01003     }
01004 
01005     case DNS_STATE_ASKING:
01006       if (--entry->tmr == 0) {
01007         if (++entry->retries == DNS_MAX_RETRIES) {
01008           if ((entry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[entry->server_idx + 1])) {
01009             /* change of server */
01010             entry->server_idx++;
01011             entry->tmr = 1;
01012             entry->retries = 0;
01013           } else {
01014             LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name));
01015             /* call specified callback function if provided */
01016             dns_call_found(i, NULL);
01017             /* flush this entry */
01018             entry->state = DNS_STATE_UNUSED;
01019             break;
01020           }
01021         } else {
01022           /* wait longer for the next retry */
01023           entry->tmr = entry->retries;
01024         }
01025 
01026         /* send DNS packet for this entry */
01027         err = dns_send(i);
01028         if (err != ERR_OK) {
01029           LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
01030                       ("dns_send returned error: %s\n", lwip_strerr(err)));
01031         }
01032       }
01033       break;
01034     case DNS_STATE_DONE:
01035       /* if the time to live is nul */
01036       if ((entry->ttl == 0) || (--entry->ttl == 0)) {
01037         LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name));
01038         /* flush this entry, there cannot be any related pending entries in this state */
01039         entry->state = DNS_STATE_UNUSED;
01040       }
01041       break;
01042     case DNS_STATE_UNUSED:
01043       /* nothing to do */
01044       break;
01045     default:
01046       LWIP_ASSERT("unknown dns_table entry state:", 0);
01047       break;
01048   }
01049 }
01050 
01051 /**
01052  * Call dns_check_entry for each entry in dns_table - check all entries.
01053  */
01054 static void
01055 dns_check_entries(void)
01056 {
01057   u8_t i;
01058 
01059   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
01060     dns_check_entry(i);
01061   }
01062 }
01063 
01064 /**
01065  * Save TTL and call dns_call_found for correct response.
01066  */
01067 static void
01068 dns_correct_response(u8_t idx, u32_t ttl)
01069 {
01070   struct dns_table_entry *entry = &dns_table[idx];
01071 
01072   entry->state = DNS_STATE_DONE;
01073 
01074   LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
01075   ip_addr_debug_print(DNS_DEBUG, (&(entry->ipaddr)));
01076   LWIP_DEBUGF(DNS_DEBUG, ("\n"));
01077 
01078   /* read the answer resource record's TTL, and maximize it if needed */
01079   entry->ttl = ttl;
01080   if (entry->ttl > DNS_MAX_TTL) {
01081     entry->ttl = DNS_MAX_TTL;
01082   }
01083   dns_call_found(idx, &entry->ipaddr);
01084 
01085   if (entry->ttl == 0) {
01086     /* RFC 883, page 29: "Zero values are
01087        interpreted to mean that the RR can only be used for the
01088        transaction in progress, and should not be cached."
01089        -> flush this entry now */
01090     /* entry reused during callback? */
01091     if (entry->state == DNS_STATE_DONE) {
01092       entry->state = DNS_STATE_UNUSED;
01093     }
01094   }
01095 }
01096 /**
01097  * Receive input function for DNS response packets arriving for the dns UDP pcb.
01098  *
01099  * @params see udp.h
01100  */
01101 static void
01102 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
01103 {
01104   u8_t i;
01105   u16_t txid;
01106   u16_t res_idx;
01107   struct dns_hdr hdr;
01108   struct dns_answer ans;
01109   struct dns_query qry;
01110   u16_t nquestions, nanswers;
01111 
01112   LWIP_UNUSED_ARG(arg);
01113   LWIP_UNUSED_ARG(pcb);
01114   LWIP_UNUSED_ARG(port);
01115 
01116   /* is the dns message big enough ? */
01117   if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) {
01118     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
01119     /* free pbuf and return */
01120     goto memerr;
01121   }
01122 
01123   /* copy dns payload inside static buffer for processing */
01124   if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) {
01125     /* Match the ID in the DNS header with the name table. */
01126     txid = htons(hdr.id);
01127     for (i = 0; i < DNS_TABLE_SIZE; i++) {
01128       const struct dns_table_entry *entry = &dns_table[i];
01129       if ((entry->state == DNS_STATE_ASKING) &&
01130           (entry->txid == txid)) {
01131 
01132         /* We only care about the question(s) and the answers. The authrr
01133            and the extrarr are simply discarded. */
01134         nquestions = htons(hdr.numquestions);
01135         nanswers   = htons(hdr.numanswers);
01136 
01137         /* Check for correct response. */
01138         if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) {
01139           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name));
01140           goto memerr; /* ignore this packet */
01141         }
01142         if (nquestions != 1) {
01143           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
01144           goto memerr; /* ignore this packet */
01145         }
01146 
01147         /* Check whether response comes from the same network address to which the
01148            question was sent. (RFC 5452) */
01149         if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
01150           goto memerr; /* ignore this packet */
01151         }
01152 
01153         /* Check if the name in the "question" part match with the name in the entry and
01154            skip it if equal. */
01155         res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR);
01156         if (res_idx == 0xFFFF) {
01157           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
01158           goto memerr; /* ignore this packet */
01159         }
01160 
01161         /* check if "question" part matches the request */
01162         pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx);
01163         if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) ||
01164           (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) ||
01165           (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) {
01166           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
01167           goto memerr; /* ignore this packet */
01168         }
01169         /* skip the rest of the "question" part */
01170         res_idx += SIZEOF_DNS_QUERY;
01171 
01172         /* Check for error. If so, call callback to inform. */
01173         if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
01174           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name));
01175         } else {
01176           while ((nanswers > 0) && (res_idx < p->tot_len)) {
01177             /* skip answer resource record's host name */
01178             res_idx = dns_parse_name(p, res_idx);
01179 
01180             /* Check for IP address type and Internet class. Others are discarded. */
01181             pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx);
01182             res_idx += SIZEOF_DNS_ANSWER;
01183             if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
01184 #if LWIP_IPV4
01185               if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) {
01186 #if LWIP_IPV4 && LWIP_IPV6
01187                 if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
01188 #endif /* LWIP_IPV4 && LWIP_IPV6 */
01189                 {
01190                   ip4_addr_t ip4addr;
01191                   /* read the IP address after answer resource record's header */
01192                   pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx);
01193                   ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr);
01194                   pbuf_free(p);
01195                   /* handle correct response */
01196                   dns_correct_response(i, ntohl(ans.ttl));
01197                   return;
01198                 }
01199               }
01200 #endif /* LWIP_IPV4 */
01201 #if LWIP_IPV6
01202               if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_t)))) {
01203 #if LWIP_IPV4 && LWIP_IPV6
01204                 if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
01205 #endif /* LWIP_IPV4 && LWIP_IPV6 */
01206                 {
01207                   ip6_addr_t ip6addr;
01208                   /* read the IP address after answer resource record's header */
01209                   pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_t), res_idx);
01210                   ip_addr_copy_from_ip6(dns_table[i].ipaddr, ip6addr);
01211                   pbuf_free(p);
01212                   /* handle correct response */
01213                   dns_correct_response(i, ntohl(ans.ttl));
01214                   return;
01215                 }
01216               }
01217 #endif /* LWIP_IPV6 */
01218             }
01219             /* skip this answer */
01220             res_idx += htons(ans.len);
01221             --nanswers;
01222           }
01223 #if LWIP_IPV4 && LWIP_IPV6
01224           if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) ||
01225               (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
01226             if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
01227               /* IPv4 failed, try IPv6 */
01228               dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
01229             } else {
01230               /* IPv6 failed, try IPv4 */
01231               dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
01232             }
01233             pbuf_free(p);
01234             dns_table[i].state = DNS_STATE_NEW;
01235             dns_check_entry(i);
01236             return;
01237           }
01238 #endif /* LWIP_IPV4 && LWIP_IPV6 */
01239           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name));
01240         }
01241         /* call callback to indicate error, clean up memory and return */
01242         pbuf_free(p);
01243         dns_call_found(i, NULL);
01244         dns_table[i].state = DNS_STATE_UNUSED;
01245         return;
01246       }
01247     }
01248   }
01249 
01250 memerr:
01251   /* deallocate memory and return */
01252   pbuf_free(p);
01253   return;
01254 }
01255 
01256 /**
01257  * Queues a new hostname to resolve and sends out a DNS query for that hostname
01258  *
01259  * @param name the hostname that is to be queried
01260  * @param hostnamelen length of the hostname
01261  * @param found a callback function to be called on success, failure or timeout
01262  * @param callback_arg argument to pass to the callback function
01263  * @return @return a err_t return code.
01264  */
01265 static err_t
01266 dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
01267             void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
01268 {
01269   u8_t i;
01270   u8_t lseq, lseqi;
01271   struct dns_table_entry *entry = NULL;
01272   size_t namelen;
01273   struct dns_req_entry* req;
01274 
01275 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
01276   u8_t r;
01277   /* check for duplicate entries */
01278   for (i = 0; i < DNS_TABLE_SIZE; i++) {
01279     if ((dns_table[i].state == DNS_STATE_ASKING) &&
01280         (LWIP_DNS_STRICMP(name, dns_table[i].name) == 0)) {
01281 #if LWIP_IPV4 && LWIP_IPV6
01282       if (dns_table[i].reqaddrtype != dns_addrtype) {
01283         /* requested address types don't match
01284            this can lead to 2 concurrent requests, but mixing the address types
01285            for the same host should not be that common */
01286         continue;
01287       }
01288 #endif /* LWIP_IPV4 && LWIP_IPV6 */
01289       /* this is a duplicate entry, find a free request entry */
01290       for (r = 0; r < DNS_MAX_REQUESTS; r++) {
01291         if (dns_requests[r].found == 0) {
01292           dns_requests[r].found = found;
01293           dns_requests[r].arg = callback_arg;
01294           dns_requests[r].dns_table_idx = i;
01295           LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype);
01296           LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name));
01297           return ERR_INPROGRESS;
01298         }
01299       }
01300     }
01301   }
01302   /* no duplicate entries found */
01303 #endif
01304 
01305   /* search an unused entry, or the oldest one */
01306   lseq = 0;
01307   lseqi = DNS_TABLE_SIZE;
01308   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
01309     entry = &dns_table[i];
01310     /* is it an unused entry ? */
01311     if (entry->state == DNS_STATE_UNUSED) {
01312       break;
01313     }
01314     /* check if this is the oldest completed entry */
01315     if (entry->state == DNS_STATE_DONE) {
01316       if ((u8_t)(dns_seqno - entry->seqno) > lseq) {
01317         lseq = dns_seqno - entry->seqno;
01318         lseqi = i;
01319       }
01320     }
01321   }
01322 
01323   /* if we don't have found an unused entry, use the oldest completed one */
01324   if (i == DNS_TABLE_SIZE) {
01325     if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
01326       /* no entry can be used now, table is full */
01327       LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
01328       return ERR_MEM;
01329     } else {
01330       /* use the oldest completed one */
01331       i = lseqi;
01332       entry = &dns_table[i];
01333     }
01334   }
01335 
01336 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
01337   /* find a free request entry */
01338   req = NULL;
01339   for (r = 0; r < DNS_MAX_REQUESTS; r++) {
01340     if (dns_requests[r].found == NULL) {
01341       req = &dns_requests[r];
01342       break;
01343     }
01344   }
01345   if (req == NULL) {
01346     /* no request entry can be used now, table is full */
01347     LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name));
01348     return ERR_MEM;
01349   }
01350   req->dns_table_idx = i;
01351 #else
01352   /* in this configuration, the entry index is the same as the request index */
01353   req = &dns_requests[i];
01354 #endif
01355 
01356   /* use this entry */
01357   LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
01358 
01359   /* fill the entry */
01360   entry->state = DNS_STATE_NEW;
01361   entry->seqno = dns_seqno;
01362   LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype);
01363   LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype);
01364   req->found = found;
01365   req->arg   = callback_arg;
01366   namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1);
01367   MEMCPY(entry->name, name, namelen);
01368   entry->name[namelen] = 0;
01369 
01370 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
01371   entry->pcb_idx = dns_alloc_pcb();
01372   if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) {
01373     /* failed to get a UDP pcb */
01374     LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name));
01375     entry->state = DNS_STATE_UNUSED;
01376     req->found = NULL;
01377     return ERR_MEM;
01378   }
01379   LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
01380 #endif
01381 
01382   dns_seqno++;
01383 
01384   /* force to send query without waiting timer */
01385   dns_check_entry(i);
01386 
01387   /* dns query is enqueued */
01388   return ERR_INPROGRESS;
01389 }
01390 
01391 /**
01392  * @ingroup dns
01393  * Resolve a hostname (string) into an IP address.
01394  * NON-BLOCKING callback version for use with raw API!!!
01395  *
01396  * Returns immediately with one of err_t return codes:
01397  * - ERR_OK if hostname is a valid IP address string or the host
01398  *   name is already in the local names table.
01399  * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
01400  *   for resolution if no errors are present.
01401  * - ERR_ARG: dns client not initialized or invalid hostname
01402  *
01403  * @param hostname the hostname that is to be queried
01404  * @param addr pointer to a ip_addr_t where to store the address if it is already
01405  *             cached in the dns_table (only valid if ERR_OK is returned!)
01406  * @param found a callback function to be called on success, failure or timeout (only if
01407  *              ERR_INPROGRESS is returned!)
01408  * @param callback_arg argument to pass to the callback function
01409  * @return a err_t return code.
01410  */
01411 err_t
01412 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
01413                   void *callback_arg)
01414 {
01415   return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
01416 }
01417 
01418 /**
01419  * @ingroup dns
01420  * Like dns_gethostbyname, but returned address type can be controlled:
01421  * @param hostname the hostname that is to be queried
01422  * @param addr pointer to a ip_addr_t where to store the address if it is already
01423  *             cached in the dns_table (only valid if ERR_OK is returned!)
01424  * @param found a callback function to be called on success, failure or timeout (only if
01425  *              ERR_INPROGRESS is returned!)
01426  * @param callback_arg argument to pass to the callback function
01427  * @param dns_addrtype: - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
01428  *                      - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
01429  *                      - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
01430  *                      - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
01431  */
01432 err_t
01433 dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
01434                            void *callback_arg, u8_t dns_addrtype)
01435 {
01436   size_t hostnamelen;
01437   /* not initialized or no valid server yet, or invalid addr pointer
01438    * or invalid hostname or invalid hostname length */
01439   if ((addr == NULL) ||
01440       (!hostname) || (!hostname[0])) {
01441     return ERR_ARG;
01442   }
01443 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
01444   if (dns_pcbs[0] == NULL) {
01445     return ERR_ARG;
01446   }
01447 #endif
01448   hostnamelen = strlen(hostname);
01449   if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
01450     LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve"));
01451     return ERR_ARG;
01452   }
01453 
01454 
01455 #if LWIP_HAVE_LOOPIF
01456   if (strcmp(hostname, "localhost") == 0) {
01457     ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr);
01458     return ERR_OK;
01459   }
01460 #endif /* LWIP_HAVE_LOOPIF */
01461 
01462   /* host name already in octet notation? set ip addr and return ERR_OK */
01463   if (ipaddr_aton(hostname, addr)) {
01464 #if LWIP_IPV4 && LWIP_IPV6
01465     if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) ||
01466         (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6)))
01467 #endif /* LWIP_IPV4 && LWIP_IPV6 */
01468     {
01469       return ERR_OK;
01470     }
01471   }
01472   /* already have this address cached? */
01473   if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
01474     return ERR_OK;
01475   }
01476 #if LWIP_IPV4 && LWIP_IPV6
01477   if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
01478     /* fallback to 2nd IP type and try again to lookup */
01479     u8_t fallback;
01480     if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
01481       fallback = LWIP_DNS_ADDRTYPE_IPV6;
01482     } else {
01483       fallback = LWIP_DNS_ADDRTYPE_IPV4;
01484     }
01485     if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) {
01486       return ERR_OK;
01487     }
01488   }
01489 #else /* LWIP_IPV4 && LWIP_IPV6 */
01490   LWIP_UNUSED_ARG(dns_addrtype);
01491 #endif /* LWIP_IPV4 && LWIP_IPV6 */
01492 
01493   /* prevent calling found callback if no server is set, return error instead */
01494   if (ip_addr_isany_val(dns_servers[0])) {
01495     return ERR_VAL;
01496   }
01497 
01498   /* queue query with specified callback */
01499   return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
01500 }
01501 
01502 #endif /* LWIP_DNS */