Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / lwip / core / dns.c
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 */