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