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