1 /**
2 * @file
3 * DNS - host name to IP address resolver.
4 *
5 * @defgroup dns DNS
6 * @ingroup callbackstyle_api
7 *
8 * Implements a DNS host name to IP address resolver.
9 *
10 * The lwIP DNS resolver functions are used to lookup a host name and
11 * map it to a numerical IP address. It maintains a list of resolved
12 * hostnames that can be queried with the dns_lookup() function.
13 * New hostnames can be resolved using the dns_query() function.
14 *
15 * The lwIP version of the resolver also adds a non-blocking version of
16 * gethostbyname() that will work with a raw API application. This function
17 * checks for an IP address string first and converts it if it is valid.
18 * gethostbyname() then does a dns_lookup() to see if the name is
19 * already in the table. If so, the IP is returned. If not, a query is
20 * issued and the function returns with a ERR_INPROGRESS status. The app
21 * using the dns client must then go into a waiting state.
22 *
23 * Once a hostname has been resolved (or found to be non-existent),
24 * the resolver code calls a specified callback function (which
25 * must be implemented by the module that uses the resolver).
26 *
27 * Multicast DNS queries are supported for names ending on ".local".
28 * However, only "One-Shot Multicast DNS Queries" are supported (RFC 6762
29 * chapter 5.1), this is not a fully compliant implementation of continuous
30 * mDNS querying!
31 *
32 * All functions must be called from TCPIP thread.
33 *
34 * @see DNS_MAX_SERVERS
35 * @see LWIP_DHCP_MAX_DNS_SERVERS
36 * @see @ref netconn_common for thread-safe access.
37 */
38
39 /*
40 * Port to lwIP from uIP
41 * by Jim Pettinato April 2007
42 *
43 * security fixes and more by Simon Goldschmidt
44 *
45 * uIP version Copyright (c) 2002-2003, Adam Dunkels.
46 * All rights reserved.
47 *
48 * Redistribution and use in source and binary forms, with or without
49 * modification, are permitted provided that the following conditions
50 * are met:
51 * 1. Redistributions of source code must retain the above copyright
52 * notice, this list of conditions and the following disclaimer.
53 * 2. Redistributions in binary form must reproduce the above copyright
54 * notice, this list of conditions and the following disclaimer in the
55 * documentation and/or other materials provided with the distribution.
56 * 3. The name of the author may not be used to endorse or promote
57 * products derived from this software without specific prior
58 * written permission.
59 *
60 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
61 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
62 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
63 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
64 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
65 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
66 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
67 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
68 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
69 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
70 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
71 */
72
73 /*-----------------------------------------------------------------------------
74 * RFC 1035 - Domain names - implementation and specification
75 * RFC 2181 - Clarifications to the DNS Specification
76 *----------------------------------------------------------------------------*/
77
78 /** @todo: define good default values (rfc compliance) */
79 /** @todo: improve answer parsing, more checkings... */
80 /** @todo: check RFC1035 - 7.3. Processing responses */
81 /** @todo: one-shot mDNS: dual-stack fallback to another IP version */
82
83 /*-----------------------------------------------------------------------------
84 * Includes
85 *----------------------------------------------------------------------------*/
86
87 #include "lwip/opt.h"
88
89 #if LWIP_ENABLE_DISTRIBUTED_NET && !LWIP_USE_GET_HOST_BY_NAME_EXTERNAL
90 #include "lwip/distributed_net/distributed_net.h"
91 #include "lwip/distributed_net/udp_transmit.h"
92 #endif
93
94 #if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
95
96 #include "lwip/def.h"
97 #include "lwip/udp.h"
98 #include "lwip/mem.h"
99 #include "lwip/memp.h"
100 #include "lwip/dns.h"
101 #include "lwip/prot/dns.h"
102
103 #include <string.h>
104
105 /** Random generator function to create random TXIDs and source ports for queries */
106 #ifndef DNS_RAND_TXID
107 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_XID) != 0)
108 #define DNS_RAND_TXID LWIP_RAND
109 #else
110 static u16_t dns_txid;
111 #define DNS_RAND_TXID() (++dns_txid)
112 #endif
113 #endif
114
115 /** Limits the source port to be >= 1024 by default */
116 #ifndef DNS_PORT_ALLOWED
117 #define DNS_PORT_ALLOWED(port) ((port) >= 1024)
118 #endif
119
120 /** DNS resource record max. TTL (one week as default) */
121 #ifndef DNS_MAX_TTL
122 #define DNS_MAX_TTL 604800
123 #elif DNS_MAX_TTL > 0x7FFFFFFF
124 #error DNS_MAX_TTL must be a positive 32-bit value
125 #endif
126
127 #if DNS_TABLE_SIZE > 255
128 #error DNS_TABLE_SIZE must fit into an u8_t
129 #endif
130 #if DNS_MAX_SERVERS > 255
131 #error DNS_MAX_SERVERS must fit into an u8_t
132 #endif
133
134 /* The number of parallel requests (i.e. calls to dns_gethostbyname
135 * that cannot be answered from the DNS table.
136 * This is set to the table size by default.
137 */
138 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
139 #ifndef DNS_MAX_REQUESTS
140 #define DNS_MAX_REQUESTS DNS_TABLE_SIZE
141 #else
142 #if DNS_MAX_REQUESTS > 255
143 #error DNS_MAX_REQUESTS must fit into an u8_t
144 #endif
145 #endif
146 #else
147 /* In this configuration, both arrays have to have the same size and are used
148 * like one entry (used/free) */
149 #define DNS_MAX_REQUESTS DNS_TABLE_SIZE
150 #endif
151
152 /* The number of UDP source ports used in parallel */
153 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
154 #ifndef DNS_MAX_SOURCE_PORTS
155 #define DNS_MAX_SOURCE_PORTS DNS_MAX_REQUESTS
156 #else
157 #if DNS_MAX_SOURCE_PORTS > 255
158 #error DNS_MAX_SOURCE_PORTS must fit into an u8_t
159 #endif
160 #endif
161 #else
162 #ifdef DNS_MAX_SOURCE_PORTS
163 #undef DNS_MAX_SOURCE_PORTS
164 #endif
165 #define DNS_MAX_SOURCE_PORTS 1
166 #endif
167
168 #if LWIP_IPV4 && LWIP_IPV6
169 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) (((t) == LWIP_DNS_ADDRTYPE_IPV6_IPV4) || ((t) == LWIP_DNS_ADDRTYPE_IPV6))
170 #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)))
171 #define LWIP_DNS_ADDRTYPE_ARG(x) , x
172 #define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) x
173 #define LWIP_DNS_SET_ADDRTYPE(x, y) do { x = y; } while(0)
174 #else
175 #if LWIP_IPV6
176 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 1
177 #else
178 #define LWIP_DNS_ADDRTYPE_IS_IPV6(t) 0
179 #endif
180 #define LWIP_DNS_ADDRTYPE_MATCH_IP(t, ip) 1
181 #define LWIP_DNS_ADDRTYPE_ARG(x)
182 #define LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(x) 0
183 #define LWIP_DNS_SET_ADDRTYPE(x, y)
184 #endif /* LWIP_IPV4 && LWIP_IPV6 */
185
186 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
187 #define LWIP_DNS_ISMDNS_ARG(x) , x
188 #else
189 #define LWIP_DNS_ISMDNS_ARG(x)
190 #endif
191
192 /** DNS query message structure.
193 No packing needed: only used locally on the stack. */
194 struct dns_query {
195 /* DNS query record starts with either a domain name or a pointer
196 to a name already present somewhere in the packet. */
197 u16_t type;
198 u16_t cls;
199 };
200 #define SIZEOF_DNS_QUERY 4
201
202 /** DNS answer message structure.
203 No packing needed: only used locally on the stack. */
204 struct dns_answer {
205 /* DNS answer record starts with either a domain name or a pointer
206 to a name already present somewhere in the packet. */
207 u16_t type;
208 u16_t cls;
209 u32_t ttl;
210 u16_t len;
211 };
212 #define SIZEOF_DNS_ANSWER 10
213 /* maximum allowed size for the struct due to non-packed */
214 #define SIZEOF_DNS_ANSWER_ASSERT 12
215
216 /* DNS table entry states */
217 typedef enum {
218 DNS_STATE_UNUSED = 0,
219 DNS_STATE_NEW = 1,
220 DNS_STATE_ASKING = 2,
221 DNS_STATE_DONE = 3
222 } dns_state_enum_t;
223
224 /** DNS table entry */
225 struct dns_table_entry {
226 u32_t ttl;
227 ip_addr_t ipaddr;
228 u16_t txid;
229 u8_t state;
230 u8_t server_idx;
231 u8_t tmr;
232 u8_t retries;
233 u8_t seqno;
234 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
235 u8_t pcb_idx;
236 #endif
237 char name[DNS_MAX_NAME_LENGTH];
238 #if LWIP_IPV4 && LWIP_IPV6
239 u8_t reqaddrtype;
240 #endif /* LWIP_IPV4 && LWIP_IPV6 */
241 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
242 u8_t is_mdns;
243 #endif
244 };
245
246 /** DNS request table entry: used when dns_gehostbyname cannot answer the
247 * request from the DNS table */
248 struct dns_req_entry {
249 /* pointer to callback on DNS query done */
250 dns_found_callback found;
251 /* argument passed to the callback function */
252 void *arg;
253 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
254 u8_t dns_table_idx;
255 #endif
256 #if LWIP_IPV4 && LWIP_IPV6
257 u8_t reqaddrtype;
258 #endif /* LWIP_IPV4 && LWIP_IPV6 */
259 };
260
261 #if DNS_LOCAL_HOSTLIST
262
263 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
264 /** Local host-list. For hostnames in this list, no
265 * external name resolution is performed */
266 static struct local_hostlist_entry *local_hostlist_dynamic;
267 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
268
269 /** Defining this allows the local_hostlist_static to be placed in a different
270 * linker section (e.g. FLASH) */
271 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
272 #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
273 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
274 /** Defining this allows the local_hostlist_static to be placed in a different
275 * linker section (e.g. FLASH) */
276 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
277 #define DNS_LOCAL_HOSTLIST_STORAGE_POST
278 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
279 DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
280 DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
281
282 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
283
284 static void dns_init_local(void);
285 static err_t dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype));
286 #endif /* DNS_LOCAL_HOSTLIST */
287
288
289 /* forward declarations */
290 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
291 static void dns_check_entries(void);
292 static void dns_call_found(u8_t idx, ip_addr_t *addr);
293
294 /*-----------------------------------------------------------------------------
295 * Globals
296 *----------------------------------------------------------------------------*/
297
298 /* DNS variables */
299 static struct udp_pcb *dns_pcbs[DNS_MAX_SOURCE_PORTS];
300 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
301 static u8_t dns_last_pcb_idx;
302 #endif
303 static u8_t dns_seqno;
304 static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
305 static struct dns_req_entry dns_requests[DNS_MAX_REQUESTS];
306 static ip_addr_t dns_servers[DNS_MAX_SERVERS];
307
308 #if LWIP_IPV4
309 const ip_addr_t dns_mquery_v4group = DNS_MQUERY_IPV4_GROUP_INIT;
310 #endif /* LWIP_IPV4 */
311 #if LWIP_IPV6
312 const ip_addr_t dns_mquery_v6group = DNS_MQUERY_IPV6_GROUP_INIT;
313 #endif /* LWIP_IPV6 */
314
315 /**
316 * Initialize the resolver: set up the UDP pcb and configure the default server
317 * (if DNS_SERVER_ADDRESS is set).
318 */
319 void
dns_init(void)320 dns_init(void)
321 {
322 #ifdef DNS_SERVER_ADDRESS
323 /* initialize default DNS server address */
324 ip_addr_t dnsserver;
325 DNS_SERVER_ADDRESS(&dnsserver);
326 dns_setserver(0, &dnsserver);
327 #ifdef DNS_SERVER_ADDRESS_SECONDARY
328 DNS_SERVER_ADDRESS_SECONDARY(&dnsserver);
329 dns_setserver(1, &dnsserver);
330 #endif
331 #endif /* DNS_SERVER_ADDRESS */
332
333 LWIP_ASSERT("sanity check SIZEOF_DNS_QUERY",
334 sizeof(struct dns_query) == SIZEOF_DNS_QUERY);
335 LWIP_ASSERT("sanity check SIZEOF_DNS_ANSWER",
336 sizeof(struct dns_answer) <= SIZEOF_DNS_ANSWER_ASSERT);
337
338 LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
339
340 /* if dns client not yet initialized... */
341 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
342 if (dns_pcbs[0] == NULL) {
343 dns_pcbs[0] = udp_new_ip_type(IPADDR_TYPE_ANY);
344 LWIP_ASSERT("dns_pcbs[0] != NULL", dns_pcbs[0] != NULL);
345
346 /* initialize DNS table not needed (initialized to zero since it is a
347 * global variable) */
348 LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
349 DNS_STATE_UNUSED == 0);
350
351 /* initialize DNS client */
352 udp_bind(dns_pcbs[0], IP_ANY_TYPE, 0);
353 udp_recv(dns_pcbs[0], dns_recv, NULL);
354 }
355 #endif
356
357 #if DNS_LOCAL_HOSTLIST
358 dns_init_local();
359 #endif
360 }
361
362 /**
363 * @ingroup dns
364 * Initialize one of the DNS servers.
365 *
366 * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
367 * @param dnsserver IP address of the DNS server to set
368 */
369 void
dns_setserver(u8_t numdns,const ip_addr_t * dnsserver)370 dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
371 {
372 if (numdns < DNS_MAX_SERVERS) {
373 if (dnsserver != NULL) {
374 dns_servers[numdns] = (*dnsserver);
375 } else {
376 dns_servers[numdns] = *IP_ADDR_ANY;
377 }
378 }
379 }
380
381 /**
382 * @ingroup dns
383 * Obtain one of the currently configured DNS server.
384 *
385 * @param numdns the index of the DNS server
386 * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
387 * server has not been configured.
388 */
389 const ip_addr_t *
dns_getserver(u8_t numdns)390 dns_getserver(u8_t numdns)
391 {
392 if (numdns < DNS_MAX_SERVERS) {
393 return &dns_servers[numdns];
394 } else {
395 return IP_ADDR_ANY;
396 }
397 }
398
399 /**
400 * The DNS resolver client timer - handle retries and timeouts and should
401 * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
402 */
403 void
dns_tmr(void)404 dns_tmr(void)
405 {
406 LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
407 dns_check_entries();
408 }
409
410 #if DNS_LOCAL_HOSTLIST
411 static void
dns_init_local(void)412 dns_init_local(void)
413 {
414 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
415 size_t i;
416 struct local_hostlist_entry *entry;
417 /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
418 struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
419 size_t namelen;
420 for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_init); i++) {
421 struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
422 LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
423 namelen = strlen(init_entry->name);
424 LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
425 entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
426 LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
427 if (entry != NULL) {
428 char *entry_name = (char *)entry + sizeof(struct local_hostlist_entry);
429 MEMCPY(entry_name, init_entry->name, namelen);
430 entry_name[namelen] = 0;
431 entry->name = entry_name;
432 entry->addr = init_entry->addr;
433 entry->next = local_hostlist_dynamic;
434 local_hostlist_dynamic = entry;
435 }
436 }
437 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
438 }
439
440 /**
441 * @ingroup dns
442 * Iterate the local host-list for a hostname.
443 *
444 * @param iterator_fn a function that is called for every entry in the local host-list
445 * @param iterator_arg 3rd argument passed to iterator_fn
446 * @return the number of entries in the local host-list
447 */
448 size_t
dns_local_iterate(dns_found_callback iterator_fn,void * iterator_arg)449 dns_local_iterate(dns_found_callback iterator_fn, void *iterator_arg)
450 {
451 size_t i;
452 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
453 struct local_hostlist_entry *entry = local_hostlist_dynamic;
454 i = 0;
455 while (entry != NULL) {
456 if (iterator_fn != NULL) {
457 iterator_fn(entry->name, &entry->addr, iterator_arg);
458 }
459 i++;
460 entry = entry->next;
461 }
462 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
463 for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
464 if (iterator_fn != NULL) {
465 iterator_fn(local_hostlist_static[i].name, &local_hostlist_static[i].addr, iterator_arg);
466 }
467 }
468 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
469 return i;
470 }
471
472 /**
473 * @ingroup dns
474 * Scans the local host-list for a hostname.
475 *
476 * @param hostname Hostname to look for in the local host-list
477 * @param addr the first IP address for the hostname in the local host-list or
478 * IPADDR_NONE if not found.
479 * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 (ATTENTION: no fallback here!)
480 * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 (ATTENTION: no fallback here!)
481 * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
482 * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
483 * @return ERR_OK if found, ERR_ARG if not found
484 */
485 err_t
dns_local_lookup(const char * hostname,ip_addr_t * addr,u8_t dns_addrtype)486 dns_local_lookup(const char *hostname, ip_addr_t *addr, u8_t dns_addrtype)
487 {
488 LWIP_UNUSED_ARG(dns_addrtype);
489 return dns_lookup_local(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype));
490 }
491
492 /* Internal implementation for dns_local_lookup and dns_lookup */
493 static err_t
dns_lookup_local(const char * hostname,ip_addr_t * addr LWIP_DNS_ADDRTYPE_ARG (u8_t dns_addrtype))494 dns_lookup_local(const char *hostname, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
495 {
496 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
497 struct local_hostlist_entry *entry = local_hostlist_dynamic;
498 while (entry != NULL) {
499 if ((lwip_stricmp(entry->name, hostname) == 0) &&
500 LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, entry->addr)) {
501 if (addr) {
502 ip_addr_copy(*addr, entry->addr);
503 }
504 return ERR_OK;
505 }
506 entry = entry->next;
507 }
508 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
509 size_t i;
510 for (i = 0; i < LWIP_ARRAYSIZE(local_hostlist_static); i++) {
511 if ((lwip_stricmp(local_hostlist_static[i].name, hostname) == 0) &&
512 LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, local_hostlist_static[i].addr)) {
513 if (addr) {
514 ip_addr_copy(*addr, local_hostlist_static[i].addr);
515 }
516 return ERR_OK;
517 }
518 }
519 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
520 return ERR_ARG;
521 }
522
523 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
524 /**
525 * @ingroup dns
526 * Remove all entries from the local host-list for a specific hostname
527 * and/or IP address
528 *
529 * @param hostname hostname for which entries shall be removed from the local
530 * host-list
531 * @param addr address for which entries shall be removed from the local host-list
532 * @return the number of removed entries
533 */
534 int
dns_local_removehost(const char * hostname,const ip_addr_t * addr)535 dns_local_removehost(const char *hostname, const ip_addr_t *addr)
536 {
537 int removed = 0;
538 struct local_hostlist_entry *entry = local_hostlist_dynamic;
539 struct local_hostlist_entry *last_entry = NULL;
540 while (entry != NULL) {
541 if (((hostname == NULL) || !lwip_stricmp(entry->name, hostname)) &&
542 ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
543 struct local_hostlist_entry *free_entry;
544 if (last_entry != NULL) {
545 last_entry->next = entry->next;
546 } else {
547 local_hostlist_dynamic = entry->next;
548 }
549 free_entry = entry;
550 entry = entry->next;
551 memp_free(MEMP_LOCALHOSTLIST, free_entry);
552 removed++;
553 } else {
554 last_entry = entry;
555 entry = entry->next;
556 }
557 }
558 return removed;
559 }
560
561 /**
562 * @ingroup dns
563 * Add a hostname/IP address pair to the local host-list.
564 * Duplicates are not checked.
565 *
566 * @param hostname hostname of the new entry
567 * @param addr IP address of the new entry
568 * @return ERR_OK if succeeded or ERR_MEM on memory error
569 */
570 err_t
dns_local_addhost(const char * hostname,const ip_addr_t * addr)571 dns_local_addhost(const char *hostname, const ip_addr_t *addr)
572 {
573 struct local_hostlist_entry *entry;
574 size_t namelen;
575 char *entry_name;
576 LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
577 namelen = strlen(hostname);
578 LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
579 entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
580 if (entry == NULL) {
581 return ERR_MEM;
582 }
583 entry_name = (char *)entry + sizeof(struct local_hostlist_entry);
584 MEMCPY(entry_name, hostname, namelen);
585 entry_name[namelen] = 0;
586 entry->name = entry_name;
587 ip_addr_copy(entry->addr, *addr);
588 entry->next = local_hostlist_dynamic;
589 local_hostlist_dynamic = entry;
590 return ERR_OK;
591 }
592 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
593 #endif /* DNS_LOCAL_HOSTLIST */
594
595 /**
596 * @ingroup dns
597 * Look up a hostname in the array of known hostnames.
598 *
599 * @note This function only looks in the internal array of known
600 * hostnames, it does not send out a query for the hostname if none
601 * was found. The function dns_enqueue() can be used to send a query
602 * for a hostname.
603 *
604 * @param name the hostname to look up
605 * @param addr the hostname's IP address, as u32_t (instead of ip_addr_t to
606 * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
607 * was not found in the cached dns_table.
608 * @return ERR_OK if found, ERR_ARG if not found
609 */
610 static err_t
dns_lookup(const char * name,ip_addr_t * addr LWIP_DNS_ADDRTYPE_ARG (u8_t dns_addrtype))611 dns_lookup(const char *name, ip_addr_t *addr LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype))
612 {
613 u8_t i;
614 #if DNS_LOCAL_HOSTLIST
615 if (dns_lookup_local(name, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
616 return ERR_OK;
617 }
618 #endif /* DNS_LOCAL_HOSTLIST */
619 #ifdef DNS_LOOKUP_LOCAL_EXTERN
620 if (DNS_LOOKUP_LOCAL_EXTERN(name, addr, LWIP_DNS_ADDRTYPE_ARG_OR_ZERO(dns_addrtype)) == ERR_OK) {
621 return ERR_OK;
622 }
623 #endif /* DNS_LOOKUP_LOCAL_EXTERN */
624
625 /* Walk through name list, return entry if found. If not, return NULL. */
626 for (i = 0; i < DNS_TABLE_SIZE; ++i) {
627 if ((dns_table[i].state == DNS_STATE_DONE) &&
628 (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0) &&
629 LWIP_DNS_ADDRTYPE_MATCH_IP(dns_addrtype, dns_table[i].ipaddr)) {
630 LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
631 ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr);
632 LWIP_DEBUGF(DNS_DEBUG, ("\n"));
633 if (addr) {
634 ip_addr_copy(*addr, dns_table[i].ipaddr);
635 }
636 return ERR_OK;
637 }
638 }
639
640 return ERR_ARG;
641 }
642
643 /**
644 * Compare the "dotted" name "query" with the encoded name "response"
645 * to make sure an answer from the DNS server matches the current dns_table
646 * entry (otherwise, answers might arrive late for hostname not on the list
647 * any more).
648 *
649 * For now, this function compares case-insensitive to cope with all kinds of
650 * servers. This also means that "dns 0x20 bit encoding" must be checked
651 * externally, if we want to implement it.
652 * Currently, the request is sent exactly as passed in by he user request.
653 *
654 * @param query hostname (not encoded) from the dns_table
655 * @param p pbuf containing the encoded hostname in the DNS response
656 * @param start_offset offset into p where the name starts
657 * @return 0xFFFF: names differ, other: names equal -> offset behind name
658 */
659 static u16_t
dns_compare_name(const char * query,struct pbuf * p,u16_t start_offset)660 dns_compare_name(const char *query, struct pbuf *p, u16_t start_offset)
661 {
662 int n;
663 u16_t response_offset = start_offset;
664
665 do {
666 n = pbuf_try_get_at(p, response_offset);
667 if ((n < 0) || (response_offset == 0xFFFF)) {
668 /* error or overflow */
669 return 0xFFFF;
670 }
671 response_offset++;
672 /** @see RFC 1035 - 4.1.4. Message compression */
673 if ((n & 0xc0) == 0xc0) {
674 /* Compressed name: cannot be equal since we don't send them */
675 return 0xFFFF;
676 } else {
677 /* Not compressed name */
678 while (n > 0) {
679 int c = pbuf_try_get_at(p, response_offset);
680 if (c < 0) {
681 return 0xFFFF;
682 }
683 if (lwip_tolower((*query)) != lwip_tolower((u8_t)c)) {
684 return 0xFFFF;
685 }
686 if (response_offset == 0xFFFF) {
687 /* would overflow */
688 return 0xFFFF;
689 }
690 response_offset++;
691 ++query;
692 --n;
693 }
694 ++query;
695 }
696 n = pbuf_try_get_at(p, response_offset);
697 if (n < 0) {
698 return 0xFFFF;
699 }
700 } while (n != 0);
701
702 if (response_offset == 0xFFFF) {
703 /* would overflow */
704 return 0xFFFF;
705 }
706 return (u16_t)(response_offset + 1);
707 }
708
709 /**
710 * Walk through a compact encoded DNS name and return the end of the name.
711 *
712 * @param p pbuf containing the name
713 * @param query_idx start index into p pointing to encoded DNS name in the DNS server response
714 * @return index to end of the name
715 */
716 static u16_t
dns_skip_name(struct pbuf * p,u16_t query_idx)717 dns_skip_name(struct pbuf *p, u16_t query_idx)
718 {
719 int n;
720 u16_t offset = query_idx;
721
722 do {
723 n = pbuf_try_get_at(p, offset++);
724 if ((n < 0) || (offset == 0)) {
725 return 0xFFFF;
726 }
727 /** @see RFC 1035 - 4.1.4. Message compression */
728 if ((n & 0xc0) == 0xc0) {
729 /* Compressed name: since we only want to skip it (not check it), stop here */
730 break;
731 } else {
732 /* Not compressed name */
733 if (offset + n >= p->tot_len) {
734 return 0xFFFF;
735 }
736 offset = (u16_t)(offset + n);
737 }
738 n = pbuf_try_get_at(p, offset);
739 if (n < 0) {
740 return 0xFFFF;
741 }
742 } while (n != 0);
743
744 if (offset == 0xFFFF) {
745 return 0xFFFF;
746 }
747 return (u16_t)(offset + 1);
748 }
749
750 /**
751 * Send a DNS query packet.
752 *
753 * @param idx the DNS table entry index for which to send a request
754 * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
755 */
756 static err_t
dns_send(u8_t idx)757 dns_send(u8_t idx)
758 {
759 err_t err;
760 struct dns_hdr hdr;
761 struct dns_query qry;
762 struct pbuf *p;
763 u16_t query_idx, copy_len;
764 const char *hostname, *hostname_part;
765 u8_t n;
766 u8_t pcb_idx;
767 struct dns_table_entry *entry = &dns_table[idx];
768
769 LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
770 (u16_t)(entry->server_idx), entry->name));
771 LWIP_ASSERT("dns server out of array", entry->server_idx < DNS_MAX_SERVERS);
772 if (ip_addr_isany_val(dns_servers[entry->server_idx])
773 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
774 && !entry->is_mdns
775 #endif
776 ) {
777 /* DNS server not valid anymore, e.g. PPP netif has been shut down */
778 /* call specified callback function if provided */
779 dns_call_found(idx, NULL);
780 /* flush this entry */
781 entry->state = DNS_STATE_UNUSED;
782 return ERR_OK;
783 }
784
785 /* if here, we have either a new query or a retry on a previous query to process */
786 #if LWIP_ENABLE_DISTRIBUTED_NET && !LWIP_USE_GET_HOST_BY_NAME_EXTERNAL
787 if (is_distributed_net_enabled()) {
788 p = pbuf_alloc(PBUF_TRANSPORT,
789 (u16_t)(sizeof(udp_data) + SIZEOF_DNS_HDR + strlen(entry->name) + 2 + SIZEOF_DNS_QUERY), PBUF_RAM);
790 } else {
791 p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 + SIZEOF_DNS_QUERY), PBUF_RAM);
792 }
793 #else
794 p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
795 SIZEOF_DNS_QUERY), PBUF_RAM);
796 #endif
797
798 if (p != NULL) {
799 const ip_addr_t *dst;
800 u16_t dst_port;
801 /* fill dns header */
802 memset(&hdr, 0, SIZEOF_DNS_HDR);
803 hdr.id = lwip_htons(entry->txid);
804 hdr.flags1 = DNS_FLAG1_RD;
805 hdr.numquestions = PP_HTONS(1);
806 #if LWIP_ENABLE_DISTRIBUTED_NET && !LWIP_USE_GET_HOST_BY_NAME_EXTERNAL
807 if (is_distributed_net_enabled()) {
808 udp_data udp_data_hdr = {0};
809 (void)memset_s(&udp_data_hdr, sizeof(udp_data_hdr), 0, sizeof(udp_data_hdr));
810 dst = &dns_servers[entry->server_idx];
811
812 #if LWIP_IPV6
813 (void)strcpy_s(udp_data_hdr.dest_addr, sizeof(udp_data_hdr.dest_addr), ip4addr_ntoa(&dst->u_addr.ip4));
814 #else
815 (void)strcpy_s(udp_data_hdr.dest_addr, sizeof(udp_data_hdr.dest_addr), ip4addr_ntoa(dst));
816 #endif
817
818 udp_data_hdr.dest_port = DNS_SERVER_PORT;
819
820 pbuf_take(p, &udp_data_hdr, sizeof(udp_data_hdr));
821 pbuf_take_at(p, &hdr, SIZEOF_DNS_HDR, sizeof(udp_data_hdr));
822 } else {
823 pbuf_take(p, &hdr, SIZEOF_DNS_HDR);
824 }
825 #else
826 pbuf_take(p, &hdr, SIZEOF_DNS_HDR);
827 #endif
828 hostname = entry->name;
829 --hostname;
830
831 /* convert hostname into suitable query format. */
832 #if LWIP_ENABLE_DISTRIBUTED_NET && !LWIP_USE_GET_HOST_BY_NAME_EXTERNAL
833 if (is_distributed_net_enabled()) {
834 query_idx = sizeof(udp_data) + SIZEOF_DNS_HDR;
835 } else {
836 query_idx = SIZEOF_DNS_HDR;
837 }
838 #else
839 query_idx = SIZEOF_DNS_HDR;
840 #endif
841 do {
842 ++hostname;
843 hostname_part = hostname;
844 for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) {
845 ++n;
846 }
847 copy_len = (u16_t)(hostname - hostname_part);
848 if (query_idx + n + 1 > 0xFFFF) {
849 /* u16_t overflow */
850 goto overflow_return;
851 }
852 pbuf_put_at(p, query_idx, n);
853 pbuf_take_at(p, hostname_part, copy_len, (u16_t)(query_idx + 1));
854 query_idx = (u16_t)(query_idx + n + 1);
855 } while (*hostname != 0);
856 pbuf_put_at(p, query_idx, 0);
857 query_idx++;
858
859 /* fill dns query */
860 if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
861 qry.type = PP_HTONS(DNS_RRTYPE_AAAA);
862 } else {
863 qry.type = PP_HTONS(DNS_RRTYPE_A);
864 }
865 qry.cls = PP_HTONS(DNS_RRCLASS_IN);
866 pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx);
867
868 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
869 pcb_idx = entry->pcb_idx;
870 #else
871 pcb_idx = 0;
872 #endif
873 /* send dns packet */
874 LWIP_DEBUGF(DNS_DEBUG, ("sending DNS request ID %d for name \"%s\" to server %d\r\n",
875 entry->txid, entry->name, entry->server_idx));
876 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
877 if (entry->is_mdns) {
878 dst_port = DNS_MQUERY_PORT;
879 #if LWIP_IPV6
880 if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype)) {
881 dst = &dns_mquery_v6group;
882 }
883 #endif
884 #if LWIP_IPV4 && LWIP_IPV6
885 else
886 #endif
887 #if LWIP_IPV4
888 {
889 dst = &dns_mquery_v4group;
890 }
891 #endif
892 } else
893 #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
894 {
895 dst_port = DNS_SERVER_PORT;
896 dst = &dns_servers[entry->server_idx];
897 }
898 #if LWIP_ENABLE_DISTRIBUTED_NET && !LWIP_USE_GET_HOST_BY_NAME_EXTERNAL
899 if (is_distributed_net_enabled()) {
900 ip_addr_t local_addr = {0};
901 dst_port = get_local_udp_server_port();
902
903 #if LWIP_IPV6
904 local_addr.u_addr.ip4.addr = ipaddr_addr(LOCAL_SERVER_IP);
905 local_addr.type = IPADDR_TYPE_V4;
906 #else
907 local_addr.addr = ipaddr_addr(LOCAL_SERVER_IP);
908 #endif
909
910 err = udp_sendto(dns_pcbs[pcb_idx], p, &local_addr, dst_port);
911 } else {
912 err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
913 }
914 #else
915 err = udp_sendto(dns_pcbs[pcb_idx], p, dst, dst_port);
916 #endif
917
918 /* free pbuf */
919 pbuf_free(p);
920 } else {
921 err = ERR_MEM;
922 }
923
924 return err;
925 overflow_return:
926 pbuf_free(p);
927 return ERR_VAL;
928 }
929
930 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
931 static struct udp_pcb *
dns_alloc_random_port(void)932 dns_alloc_random_port(void)
933 {
934 err_t err;
935 struct udp_pcb *pcb;
936
937 pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
938 if (pcb == NULL) {
939 /* out of memory, have to reuse an existing pcb */
940 return NULL;
941 }
942 do {
943 u16_t port = (u16_t)DNS_RAND_TXID();
944 if (DNS_PORT_ALLOWED(port)) {
945 err = udp_bind(pcb, IP_ANY_TYPE, port);
946 } else {
947 /* this port is not allowed, try again */
948 err = ERR_USE;
949 }
950 } while (err == ERR_USE);
951 if (err != ERR_OK) {
952 udp_remove(pcb);
953 return NULL;
954 }
955 udp_recv(pcb, dns_recv, NULL);
956 return pcb;
957 }
958
959 /**
960 * dns_alloc_pcb() - allocates a new pcb (or reuses an existing one) to be used
961 * for sending a request
962 *
963 * @return an index into dns_pcbs
964 */
965 static u8_t
dns_alloc_pcb(void)966 dns_alloc_pcb(void)
967 {
968 u8_t i;
969 u8_t idx;
970
971 for (i = 0; i < DNS_MAX_SOURCE_PORTS; i++) {
972 if (dns_pcbs[i] == NULL) {
973 break;
974 }
975 }
976 if (i < DNS_MAX_SOURCE_PORTS) {
977 dns_pcbs[i] = dns_alloc_random_port();
978 if (dns_pcbs[i] != NULL) {
979 /* succeeded */
980 dns_last_pcb_idx = i;
981 return i;
982 }
983 }
984 /* if we come here, creating a new UDP pcb failed, so we have to use
985 an already existing one (so overflow is no issue) */
986 for (i = 0, idx = (u8_t)(dns_last_pcb_idx + 1); i < DNS_MAX_SOURCE_PORTS; i++, idx++) {
987 if (idx >= DNS_MAX_SOURCE_PORTS) {
988 idx = 0;
989 }
990 if (dns_pcbs[idx] != NULL) {
991 dns_last_pcb_idx = idx;
992 return idx;
993 }
994 }
995 return DNS_MAX_SOURCE_PORTS;
996 }
997 #endif /* ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0) */
998
999 /**
1000 * dns_call_found() - call the found callback and check if there are duplicate
1001 * entries for the given hostname. If there are any, their found callback will
1002 * be called and they will be removed.
1003 *
1004 * @param idx dns table index of the entry that is resolved or removed
1005 * @param addr IP address for the hostname (or NULL on error or memory shortage)
1006 */
1007 static void
dns_call_found(u8_t idx,ip_addr_t * addr)1008 dns_call_found(u8_t idx, ip_addr_t *addr)
1009 {
1010 #if ((LWIP_DNS_SECURE & (LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING | LWIP_DNS_SECURE_RAND_SRC_PORT)) != 0)
1011 u8_t i;
1012 #endif
1013
1014 #if LWIP_IPV4 && LWIP_IPV6
1015 if (addr != NULL) {
1016 /* check that address type matches the request and adapt the table entry */
1017 if (IP_IS_V6_VAL(*addr)) {
1018 LWIP_ASSERT("invalid response", LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
1019 dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
1020 } else {
1021 LWIP_ASSERT("invalid response", !LWIP_DNS_ADDRTYPE_IS_IPV6(dns_table[idx].reqaddrtype));
1022 dns_table[idx].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
1023 }
1024 }
1025 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1026
1027 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
1028 for (i = 0; i < DNS_MAX_REQUESTS; i++) {
1029 if (dns_requests[i].found && (dns_requests[i].dns_table_idx == idx)) {
1030 (*dns_requests[i].found)(dns_table[idx].name, addr, dns_requests[i].arg);
1031 /* flush this entry */
1032 dns_requests[i].found = NULL;
1033 }
1034 }
1035 #else
1036 if (dns_requests[idx].found) {
1037 (*dns_requests[idx].found)(dns_table[idx].name, addr, dns_requests[idx].arg);
1038 }
1039 dns_requests[idx].found = NULL;
1040 #endif
1041 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
1042 /* close the pcb used unless other request are using it */
1043 for (i = 0; i < DNS_MAX_REQUESTS; i++) {
1044 if (i == idx) {
1045 continue; /* only check other requests */
1046 }
1047 if (dns_table[i].state == DNS_STATE_ASKING) {
1048 if (dns_table[i].pcb_idx == dns_table[idx].pcb_idx) {
1049 /* another request is still using the same pcb */
1050 dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
1051 break;
1052 }
1053 }
1054 }
1055 if (dns_table[idx].pcb_idx < DNS_MAX_SOURCE_PORTS) {
1056 /* if we come here, the pcb is not used any more and can be removed */
1057 udp_remove(dns_pcbs[dns_table[idx].pcb_idx]);
1058 dns_pcbs[dns_table[idx].pcb_idx] = NULL;
1059 dns_table[idx].pcb_idx = DNS_MAX_SOURCE_PORTS;
1060 }
1061 #endif
1062 }
1063
1064 /* Create a query transmission ID that is unique for all outstanding queries */
1065 static u16_t
dns_create_txid(void)1066 dns_create_txid(void)
1067 {
1068 u16_t txid;
1069 u8_t i;
1070
1071 again:
1072 txid = (u16_t)DNS_RAND_TXID();
1073
1074 /* check whether the ID is unique */
1075 for (i = 0; i < DNS_TABLE_SIZE; i++) {
1076 if ((dns_table[i].state == DNS_STATE_ASKING) &&
1077 (dns_table[i].txid == txid)) {
1078 /* ID already used by another pending query */
1079 goto again;
1080 }
1081 }
1082
1083 return txid;
1084 }
1085
1086 /**
1087 * Check whether there are other backup DNS servers available to try
1088 */
1089 static u8_t
dns_backupserver_available(struct dns_table_entry * pentry)1090 dns_backupserver_available(struct dns_table_entry *pentry)
1091 {
1092 u8_t ret = 0;
1093
1094 if (pentry) {
1095 if ((pentry->server_idx + 1 < DNS_MAX_SERVERS) && !ip_addr_isany_val(dns_servers[pentry->server_idx + 1])) {
1096 ret = 1;
1097 }
1098 }
1099
1100 return ret;
1101 }
1102
1103 /**
1104 * dns_check_entry() - see if entry has not yet been queried and, if so, sends out a query.
1105 * Check an entry in the dns_table:
1106 * - send out query for new entries
1107 * - retry old pending entries on timeout (also with different servers)
1108 * - remove completed entries from the table if their TTL has expired
1109 *
1110 * @param i index of the dns_table entry to check
1111 */
1112 static void
dns_check_entry(u8_t i)1113 dns_check_entry(u8_t i)
1114 {
1115 err_t err;
1116 struct dns_table_entry *entry = &dns_table[i];
1117
1118 LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
1119
1120 switch (entry->state) {
1121 case DNS_STATE_NEW:
1122 /* initialize new entry */
1123 entry->txid = dns_create_txid();
1124 entry->state = DNS_STATE_ASKING;
1125 entry->server_idx = 0;
1126 entry->tmr = 1;
1127 entry->retries = 0;
1128
1129 /* send DNS packet for this entry */
1130 err = dns_send(i);
1131 if (err != ERR_OK) {
1132 LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
1133 ("dns_send returned error: %s\n", lwip_strerr(err)));
1134 }
1135 break;
1136 case DNS_STATE_ASKING:
1137 if (--entry->tmr == 0) {
1138 if (++entry->retries == DNS_MAX_RETRIES) {
1139 if (dns_backupserver_available(entry)
1140 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1141 && !entry->is_mdns
1142 #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
1143 ) {
1144 /* change of server */
1145 entry->server_idx++;
1146 entry->tmr = 1;
1147 entry->retries = 0;
1148 } else {
1149 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name));
1150 /* call specified callback function if provided */
1151 dns_call_found(i, NULL);
1152 /* flush this entry */
1153 entry->state = DNS_STATE_UNUSED;
1154 break;
1155 }
1156 } else {
1157 /* wait longer for the next retry */
1158 entry->tmr = entry->retries;
1159 }
1160
1161 /* send DNS packet for this entry */
1162 err = dns_send(i);
1163 if (err != ERR_OK) {
1164 LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
1165 ("dns_send returned error: %s\n", lwip_strerr(err)));
1166 }
1167 }
1168 break;
1169 case DNS_STATE_DONE:
1170 /* if the time to live is nul */
1171 if ((entry->ttl == 0) || (--entry->ttl == 0)) {
1172 LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", entry->name));
1173 /* flush this entry, there cannot be any related pending entries in this state */
1174 entry->state = DNS_STATE_UNUSED;
1175 }
1176 break;
1177 case DNS_STATE_UNUSED:
1178 /* nothing to do */
1179 break;
1180 default:
1181 LWIP_ASSERT("unknown dns_table entry state:", 0);
1182 break;
1183 }
1184 }
1185
1186 /**
1187 * Call dns_check_entry for each entry in dns_table - check all entries.
1188 */
1189 static void
dns_check_entries(void)1190 dns_check_entries(void)
1191 {
1192 u8_t i;
1193
1194 for (i = 0; i < DNS_TABLE_SIZE; ++i) {
1195 dns_check_entry(i);
1196 }
1197 }
1198
1199 /**
1200 * Save TTL and call dns_call_found for correct response.
1201 */
1202 static void
dns_correct_response(u8_t idx,u32_t ttl)1203 dns_correct_response(u8_t idx, u32_t ttl)
1204 {
1205 struct dns_table_entry *entry = &dns_table[idx];
1206
1207 entry->state = DNS_STATE_DONE;
1208
1209 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", entry->name));
1210 ip_addr_debug_print_val(DNS_DEBUG, entry->ipaddr);
1211 LWIP_DEBUGF(DNS_DEBUG, ("\n"));
1212
1213 /* read the answer resource record's TTL, and maximize it if needed */
1214 entry->ttl = ttl;
1215 if (entry->ttl > DNS_MAX_TTL) {
1216 entry->ttl = DNS_MAX_TTL;
1217 }
1218 dns_call_found(idx, &entry->ipaddr);
1219
1220 if (entry->ttl == 0) {
1221 /* RFC 883, page 29: "Zero values are
1222 interpreted to mean that the RR can only be used for the
1223 transaction in progress, and should not be cached."
1224 -> flush this entry now */
1225 /* entry reused during callback? */
1226 if (entry->state == DNS_STATE_DONE) {
1227 entry->state = DNS_STATE_UNUSED;
1228 }
1229 }
1230 }
1231
1232 /**
1233 * Receive input function for DNS response packets arriving for the dns UDP pcb.
1234 */
1235 static void
dns_recv(void * arg,struct udp_pcb * pcb,struct pbuf * p,const ip_addr_t * addr,u16_t port)1236 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
1237 {
1238 u8_t i;
1239 u16_t txid;
1240 u16_t res_idx;
1241 struct dns_hdr hdr;
1242 struct dns_answer ans;
1243 struct dns_query qry;
1244 u16_t nquestions, nanswers;
1245
1246 LWIP_UNUSED_ARG(arg);
1247 LWIP_UNUSED_ARG(pcb);
1248 LWIP_UNUSED_ARG(port);
1249
1250 /* is the dns message big enough ? */
1251 if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY)) {
1252 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
1253 /* free pbuf and return */
1254 goto ignore_packet;
1255 }
1256
1257 /* copy dns payload inside static buffer for processing */
1258 if (pbuf_copy_partial(p, &hdr, SIZEOF_DNS_HDR, 0) == SIZEOF_DNS_HDR) {
1259 /* Match the ID in the DNS header with the name table. */
1260 txid = lwip_htons(hdr.id);
1261 for (i = 0; i < DNS_TABLE_SIZE; i++) {
1262 struct dns_table_entry *entry = &dns_table[i];
1263 if ((entry->state == DNS_STATE_ASKING) &&
1264 (entry->txid == txid)) {
1265
1266 /* We only care about the question(s) and the answers. The authrr
1267 and the extrarr are simply discarded. */
1268 nquestions = lwip_htons(hdr.numquestions);
1269 nanswers = lwip_htons(hdr.numanswers);
1270
1271 /* Check for correct response. */
1272 if ((hdr.flags1 & DNS_FLAG1_RESPONSE) == 0) {
1273 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": not a response\n", entry->name));
1274 goto ignore_packet; /* ignore this packet */
1275 }
1276 if (nquestions != 1) {
1277 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
1278 goto ignore_packet; /* ignore this packet */
1279 }
1280
1281 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1282 if (!entry->is_mdns)
1283 #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
1284 {
1285 /* Check whether response comes from the same network address to which the
1286 question was sent. (RFC 5452) */
1287 #if LWIP_ENABLE_DISTRIBUTED_NET && !LWIP_USE_GET_HOST_BY_NAME_EXTERNAL
1288 if (is_distributed_net_enabled()) {
1289 #if LWIP_IPV6
1290 if (addr->type != IPADDR_TYPE_V4 || addr->u_addr.ip4.addr != ipaddr_addr(LOCAL_SERVER_IP) ||
1291 port != get_local_udp_server_port()) {
1292 goto ignore_packet; /* ignore this packet */
1293 }
1294 #else
1295 if (addr->addr != ipaddr_addr(LOCAL_SERVER_IP) || port != get_local_udp_server_port()) {
1296 goto ignore_packet; /* ignore this packet */
1297 }
1298 #endif
1299 } else {
1300 if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
1301 goto ignore_packet; /* ignore this packet */
1302 }
1303 }
1304 #else
1305 if (!ip_addr_cmp(addr, &dns_servers[entry->server_idx])) {
1306 goto ignore_packet; /* ignore this packet */
1307 }
1308 #endif
1309 }
1310
1311 /* Check if the name in the "question" part match with the name in the entry and
1312 skip it if equal. */
1313 res_idx = dns_compare_name(entry->name, p, SIZEOF_DNS_HDR);
1314 if (res_idx == 0xFFFF) {
1315 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
1316 goto ignore_packet; /* ignore this packet */
1317 }
1318
1319 /* check if "question" part matches the request */
1320 if (pbuf_copy_partial(p, &qry, SIZEOF_DNS_QUERY, res_idx) != SIZEOF_DNS_QUERY) {
1321 goto ignore_packet; /* ignore this packet */
1322 }
1323 if ((qry.cls != PP_HTONS(DNS_RRCLASS_IN)) ||
1324 (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_AAAA))) ||
1325 (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype) && (qry.type != PP_HTONS(DNS_RRTYPE_A)))) {
1326 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", entry->name));
1327 goto ignore_packet; /* ignore this packet */
1328 }
1329 /* skip the rest of the "question" part */
1330 if (res_idx + SIZEOF_DNS_QUERY > 0xFFFF) {
1331 goto ignore_packet;
1332 }
1333 res_idx = (u16_t)(res_idx + SIZEOF_DNS_QUERY);
1334
1335 /* Check for error. If so, call callback to inform. */
1336 if (hdr.flags2 & DNS_FLAG2_ERR_MASK) {
1337 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", entry->name));
1338
1339 /* if there is another backup DNS server to try
1340 * then don't stop the DNS request
1341 */
1342 if (dns_backupserver_available(entry)) {
1343 /* avoid retrying the same server */
1344 entry->retries = DNS_MAX_RETRIES-1;
1345 entry->tmr = 1;
1346
1347 /* contact next available server for this entry */
1348 dns_check_entry(i);
1349
1350 goto ignore_packet;
1351 }
1352 } else {
1353 while ((nanswers > 0) && (res_idx < p->tot_len)) {
1354 /* skip answer resource record's host name */
1355 res_idx = dns_skip_name(p, res_idx);
1356 if (res_idx == 0xFFFF) {
1357 goto ignore_packet; /* ignore this packet */
1358 }
1359
1360 /* Check for IP address type and Internet class. Others are discarded. */
1361 if (pbuf_copy_partial(p, &ans, SIZEOF_DNS_ANSWER, res_idx) != SIZEOF_DNS_ANSWER) {
1362 goto ignore_packet; /* ignore this packet */
1363 }
1364 if (res_idx + SIZEOF_DNS_ANSWER > 0xFFFF) {
1365 goto ignore_packet;
1366 }
1367 res_idx = (u16_t)(res_idx + SIZEOF_DNS_ANSWER);
1368
1369 if (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) {
1370 #if LWIP_IPV4
1371 if ((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.len == PP_HTONS(sizeof(ip4_addr_t)))) {
1372 #if LWIP_IPV4 && LWIP_IPV6
1373 if (!LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
1374 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1375 {
1376 ip4_addr_t ip4addr;
1377 /* read the IP address after answer resource record's header */
1378 if (pbuf_copy_partial(p, &ip4addr, sizeof(ip4_addr_t), res_idx) != sizeof(ip4_addr_t)) {
1379 goto ignore_packet; /* ignore this packet */
1380 }
1381 ip_addr_copy_from_ip4(dns_table[i].ipaddr, ip4addr);
1382 pbuf_free(p);
1383 /* handle correct response */
1384 dns_correct_response(i, lwip_ntohl(ans.ttl));
1385 return;
1386 }
1387 }
1388 #endif /* LWIP_IPV4 */
1389 #if LWIP_IPV6
1390 if ((ans.type == PP_HTONS(DNS_RRTYPE_AAAA)) && (ans.len == PP_HTONS(sizeof(ip6_addr_p_t)))) {
1391 #if LWIP_IPV4 && LWIP_IPV6
1392 if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
1393 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1394 {
1395 ip6_addr_p_t ip6addr;
1396 /* read the IP address after answer resource record's header */
1397 if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_p_t), res_idx) != sizeof(ip6_addr_p_t)) {
1398 goto ignore_packet; /* ignore this packet */
1399 }
1400 /* @todo: scope ip6addr? Might be required for link-local addresses at least? */
1401 ip_addr_copy_from_ip6_packed(dns_table[i].ipaddr, ip6addr);
1402 pbuf_free(p);
1403 /* handle correct response */
1404 dns_correct_response(i, lwip_ntohl(ans.ttl));
1405 return;
1406 }
1407 }
1408 #endif /* LWIP_IPV6 */
1409 }
1410 /* skip this answer */
1411 if ((int)(res_idx + lwip_htons(ans.len)) > 0xFFFF) {
1412 goto ignore_packet; /* ignore this packet */
1413 }
1414 res_idx = (u16_t)(res_idx + lwip_htons(ans.len));
1415 --nanswers;
1416 }
1417 #if LWIP_IPV4 && LWIP_IPV6
1418 if ((entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) ||
1419 (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
1420 if (entry->reqaddrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
1421 /* IPv4 failed, try IPv6 */
1422 dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV6;
1423 } else {
1424 /* IPv6 failed, try IPv4 */
1425 dns_table[i].reqaddrtype = LWIP_DNS_ADDRTYPE_IPV4;
1426 }
1427 pbuf_free(p);
1428 dns_table[i].state = DNS_STATE_NEW;
1429 dns_check_entry(i);
1430 return;
1431 }
1432 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1433 LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", entry->name));
1434 }
1435 /* call callback to indicate error, clean up memory and return */
1436 pbuf_free(p);
1437 dns_call_found(i, NULL);
1438 dns_table[i].state = DNS_STATE_UNUSED;
1439 return;
1440 }
1441 }
1442 }
1443
1444 ignore_packet:
1445 /* deallocate memory and return */
1446 pbuf_free(p);
1447 return;
1448 }
1449
1450 /**
1451 * Queues a new hostname to resolve and sends out a DNS query for that hostname
1452 *
1453 * @param name the hostname that is to be queried
1454 * @param hostnamelen length of the hostname
1455 * @param found a callback function to be called on success, failure or timeout
1456 * @param callback_arg argument to pass to the callback function
1457 * @return err_t return code.
1458 */
1459 static err_t
dns_enqueue(const char * name,size_t hostnamelen,dns_found_callback found,void * callback_arg LWIP_DNS_ADDRTYPE_ARG (u8_t dns_addrtype)LWIP_DNS_ISMDNS_ARG (u8_t is_mdns))1460 dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
1461 void *callback_arg LWIP_DNS_ADDRTYPE_ARG(u8_t dns_addrtype) LWIP_DNS_ISMDNS_ARG(u8_t is_mdns))
1462 {
1463 u8_t i;
1464 u8_t lseq, lseqi;
1465 struct dns_table_entry *entry = NULL;
1466 size_t namelen;
1467 struct dns_req_entry *req;
1468
1469 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
1470 u8_t r;
1471 /* check for duplicate entries */
1472 for (i = 0; i < DNS_TABLE_SIZE; i++) {
1473 if ((dns_table[i].state == DNS_STATE_ASKING) &&
1474 (lwip_strnicmp(name, dns_table[i].name, sizeof(dns_table[i].name)) == 0)) {
1475 #if LWIP_IPV4 && LWIP_IPV6
1476 if (dns_table[i].reqaddrtype != dns_addrtype) {
1477 /* requested address types don't match
1478 this can lead to 2 concurrent requests, but mixing the address types
1479 for the same host should not be that common */
1480 continue;
1481 }
1482 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1483 /* this is a duplicate entry, find a free request entry */
1484 for (r = 0; r < DNS_MAX_REQUESTS; r++) {
1485 if (dns_requests[r].found == 0) {
1486 dns_requests[r].found = found;
1487 dns_requests[r].arg = callback_arg;
1488 dns_requests[r].dns_table_idx = i;
1489 LWIP_DNS_SET_ADDRTYPE(dns_requests[r].reqaddrtype, dns_addrtype);
1490 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": duplicate request\n", name));
1491 return ERR_INPROGRESS;
1492 }
1493 }
1494 }
1495 }
1496 /* no duplicate entries found */
1497 #endif
1498
1499 /* search an unused entry, or the oldest one */
1500 lseq = 0;
1501 lseqi = DNS_TABLE_SIZE;
1502 for (i = 0; i < DNS_TABLE_SIZE; ++i) {
1503 entry = &dns_table[i];
1504 /* is it an unused entry ? */
1505 if (entry->state == DNS_STATE_UNUSED) {
1506 break;
1507 }
1508 /* check if this is the oldest completed entry */
1509 if (entry->state == DNS_STATE_DONE) {
1510 u8_t age = (u8_t)(dns_seqno - entry->seqno);
1511 if (age > lseq) {
1512 lseq = age;
1513 lseqi = i;
1514 }
1515 }
1516 }
1517
1518 /* if we don't have found an unused entry, use the oldest completed one */
1519 if (i == DNS_TABLE_SIZE) {
1520 if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
1521 /* no entry can be used now, table is full */
1522 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
1523 return ERR_MEM;
1524 } else {
1525 /* use the oldest completed one */
1526 i = lseqi;
1527 entry = &dns_table[i];
1528 }
1529 }
1530
1531 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_NO_MULTIPLE_OUTSTANDING) != 0)
1532 /* find a free request entry */
1533 req = NULL;
1534 for (r = 0; r < DNS_MAX_REQUESTS; r++) {
1535 if (dns_requests[r].found == NULL) {
1536 req = &dns_requests[r];
1537 break;
1538 }
1539 }
1540 if (req == NULL) {
1541 /* no request entry can be used now, table is full */
1542 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS request entries table is full\n", name));
1543 return ERR_MEM;
1544 }
1545 req->dns_table_idx = i;
1546 #else
1547 /* in this configuration, the entry index is the same as the request index */
1548 req = &dns_requests[i];
1549 #endif
1550
1551 /* use this entry */
1552 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
1553
1554 /* fill the entry */
1555 entry->state = DNS_STATE_NEW;
1556 entry->seqno = dns_seqno;
1557 LWIP_DNS_SET_ADDRTYPE(entry->reqaddrtype, dns_addrtype);
1558 LWIP_DNS_SET_ADDRTYPE(req->reqaddrtype, dns_addrtype);
1559 req->found = found;
1560 req->arg = callback_arg;
1561 namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH - 1);
1562 MEMCPY(entry->name, name, namelen);
1563 entry->name[namelen] = 0;
1564
1565 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
1566 entry->pcb_idx = dns_alloc_pcb();
1567 if (entry->pcb_idx >= DNS_MAX_SOURCE_PORTS) {
1568 /* failed to get a UDP pcb */
1569 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": failed to allocate a pcb\n", name));
1570 entry->state = DNS_STATE_UNUSED;
1571 req->found = NULL;
1572 return ERR_MEM;
1573 }
1574 LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS pcb %"U16_F"\n", name, (u16_t)(entry->pcb_idx)));
1575 #endif
1576
1577 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1578 entry->is_mdns = is_mdns;
1579 #endif
1580
1581 dns_seqno++;
1582
1583 /* force to send query without waiting timer */
1584 dns_check_entry(i);
1585
1586 /* dns query is enqueued */
1587 return ERR_INPROGRESS;
1588 }
1589
1590 /**
1591 * @ingroup dns
1592 * Resolve a hostname (string) into an IP address.
1593 * NON-BLOCKING callback version for use with raw API!!!
1594 *
1595 * Returns immediately with one of err_t return codes:
1596 * - ERR_OK if hostname is a valid IP address string or the host
1597 * name is already in the local names table.
1598 * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
1599 * for resolution if no errors are present.
1600 * - ERR_ARG: dns client not initialized or invalid hostname
1601 *
1602 * @param hostname the hostname that is to be queried
1603 * @param addr pointer to a ip_addr_t where to store the address if it is already
1604 * cached in the dns_table (only valid if ERR_OK is returned!)
1605 * @param found a callback function to be called on success, failure or timeout (only if
1606 * ERR_INPROGRESS is returned!)
1607 * @param callback_arg argument to pass to the callback function
1608 * @return a err_t return code.
1609 */
1610 err_t
dns_gethostbyname(const char * hostname,ip_addr_t * addr,dns_found_callback found,void * callback_arg)1611 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
1612 void *callback_arg)
1613 {
1614 return dns_gethostbyname_addrtype(hostname, addr, found, callback_arg, LWIP_DNS_ADDRTYPE_DEFAULT);
1615 }
1616
1617 /**
1618 * @ingroup dns
1619 * Like dns_gethostbyname, but returned address type can be controlled:
1620 * @param hostname the hostname that is to be queried
1621 * @param addr pointer to a ip_addr_t where to store the address if it is already
1622 * cached in the dns_table (only valid if ERR_OK is returned!)
1623 * @param found a callback function to be called on success, failure or timeout (only if
1624 * ERR_INPROGRESS is returned!)
1625 * @param callback_arg argument to pass to the callback function
1626 * @param dns_addrtype - LWIP_DNS_ADDRTYPE_IPV4_IPV6: try to resolve IPv4 first, try IPv6 if IPv4 fails only
1627 * - LWIP_DNS_ADDRTYPE_IPV6_IPV4: try to resolve IPv6 first, try IPv4 if IPv6 fails only
1628 * - LWIP_DNS_ADDRTYPE_IPV4: try to resolve IPv4 only
1629 * - LWIP_DNS_ADDRTYPE_IPV6: try to resolve IPv6 only
1630 */
1631 err_t
dns_gethostbyname_addrtype(const char * hostname,ip_addr_t * addr,dns_found_callback found,void * callback_arg,u8_t dns_addrtype)1632 dns_gethostbyname_addrtype(const char *hostname, ip_addr_t *addr, dns_found_callback found,
1633 void *callback_arg, u8_t dns_addrtype)
1634 {
1635 size_t hostnamelen;
1636 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1637 u8_t is_mdns;
1638 #endif
1639 /* not initialized or no valid server yet, or invalid addr pointer
1640 * or invalid hostname or invalid hostname length */
1641 if ((addr == NULL) ||
1642 (!hostname) || (!hostname[0])) {
1643 return ERR_ARG;
1644 }
1645 #if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) == 0)
1646 if (dns_pcbs[0] == NULL) {
1647 return ERR_ARG;
1648 }
1649 #endif
1650 hostnamelen = strlen(hostname);
1651 if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
1652 LWIP_DEBUGF(DNS_DEBUG, ("dns_gethostbyname: name too long to resolve"));
1653 return ERR_ARG;
1654 }
1655
1656
1657 #if LWIP_HAVE_LOOPIF
1658 if (strcmp(hostname, "localhost") == 0) {
1659 ip_addr_set_loopback(LWIP_DNS_ADDRTYPE_IS_IPV6(dns_addrtype), addr);
1660 return ERR_OK;
1661 }
1662 #endif /* LWIP_HAVE_LOOPIF */
1663
1664 /* host name already in octet notation? set ip addr and return ERR_OK */
1665 if (ipaddr_aton(hostname, addr)) {
1666 #if LWIP_IPV4 && LWIP_IPV6
1667 if ((IP_IS_V6(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV4)) ||
1668 (IP_IS_V4(addr) && (dns_addrtype != LWIP_DNS_ADDRTYPE_IPV6)))
1669 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1670 {
1671 return ERR_OK;
1672 }
1673 }
1674 /* already have this address cached? */
1675 if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)) == ERR_OK) {
1676 return ERR_OK;
1677 }
1678 #if LWIP_IPV4 && LWIP_IPV6
1679 if ((dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) || (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV6_IPV4)) {
1680 /* fallback to 2nd IP type and try again to lookup */
1681 u8_t fallback;
1682 if (dns_addrtype == LWIP_DNS_ADDRTYPE_IPV4_IPV6) {
1683 fallback = LWIP_DNS_ADDRTYPE_IPV6;
1684 } else {
1685 fallback = LWIP_DNS_ADDRTYPE_IPV4;
1686 }
1687 if (dns_lookup(hostname, addr LWIP_DNS_ADDRTYPE_ARG(fallback)) == ERR_OK) {
1688 return ERR_OK;
1689 }
1690 }
1691 #else /* LWIP_IPV4 && LWIP_IPV6 */
1692 LWIP_UNUSED_ARG(dns_addrtype);
1693 #endif /* LWIP_IPV4 && LWIP_IPV6 */
1694
1695 #if LWIP_DNS_SUPPORT_MDNS_QUERIES
1696 if (strstr(hostname, ".local") == &hostname[hostnamelen] - 6) {
1697 is_mdns = 1;
1698 } else {
1699 is_mdns = 0;
1700 }
1701
1702 if (!is_mdns)
1703 #endif /* LWIP_DNS_SUPPORT_MDNS_QUERIES */
1704 {
1705 /* prevent calling found callback if no server is set, return error instead */
1706 if (ip_addr_isany_val(dns_servers[0])) {
1707 return ERR_VAL;
1708 }
1709 }
1710
1711 /* queue query with specified callback */
1712 return dns_enqueue(hostname, hostnamelen, found, callback_arg LWIP_DNS_ADDRTYPE_ARG(dns_addrtype)
1713 LWIP_DNS_ISMDNS_ARG(is_mdns));
1714 }
1715
1716 #endif /* LWIP_DNS */
1717