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