]> git.sur5r.net Git - freertos/blob - Demo/Common/ethernet/lwIP_130/src/core/dns.c
8c2cf08547a27b2d04615477651b1c63ea1eb102
[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 /** DNS message header */\r
129 struct dns_hdr {\r
130   u16_t id;\r
131   u8_t flags1;\r
132   u8_t flags2;\r
133   u16_t numquestions;\r
134   u16_t numanswers;\r
135   u16_t numauthrr;\r
136   u16_t numextrarr;\r
137 } PACK_STRUCT_STRUCT;\r
138 PACK_STRUCT_END\r
139 #ifdef PACK_STRUCT_USE_INCLUDES\r
140 #  include "arch/epstruct.h"\r
141 #endif\r
142 \r
143 #ifdef PACK_STRUCT_USE_INCLUDES\r
144 #  include "arch/bpstruct.h"\r
145 #endif\r
146 PACK_STRUCT_BEGIN\r
147 /** DNS query message structure */\r
148 struct dns_query {\r
149   /* DNS query record starts with either a domain name or a pointer\r
150      to a name already present somewhere in the packet. */\r
151   u16_t type;\r
152   u16_t class;\r
153 } PACK_STRUCT_STRUCT;\r
154 PACK_STRUCT_END\r
155 #ifdef PACK_STRUCT_USE_INCLUDES\r
156 #  include "arch/epstruct.h"\r
157 #endif\r
158 \r
159 #ifdef PACK_STRUCT_USE_INCLUDES\r
160 #  include "arch/bpstruct.h"\r
161 #endif\r
162 PACK_STRUCT_BEGIN\r
163 /** DNS answer message structure */\r
164 struct dns_answer {\r
165   /* DNS answer record starts with either a domain name or a pointer\r
166      to a name already present somewhere in the packet. */\r
167   u16_t type;\r
168   u16_t class;\r
169   u32_t ttl;\r
170   u16_t len;\r
171 } PACK_STRUCT_STRUCT;\r
172 PACK_STRUCT_END\r
173 #ifdef PACK_STRUCT_USE_INCLUDES\r
174 #  include "arch/epstruct.h"\r
175 #endif\r
176 \r
177 /** DNS table entry */\r
178 struct dns_table_entry {\r
179   u8_t  state;\r
180   u8_t  numdns;\r
181   u8_t  tmr;\r
182   u8_t  retries;\r
183   u8_t  seqno;\r
184   u8_t  err;\r
185   u32_t ttl;\r
186   char name[DNS_MAX_NAME_LENGTH];\r
187   struct ip_addr ipaddr;\r
188   /* pointer to callback on DNS query done */\r
189   dns_found_callback found;\r
190   void *arg;\r
191 };\r
192 \r
193 \r
194 /* forward declarations */\r
195 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port);\r
196 static void dns_check_entries(void);\r
197 \r
198 /*-----------------------------------------------------------------------------\r
199  * Globales\r
200  *----------------------------------------------------------------------------*/\r
201 \r
202 /* DNS variables */\r
203 static struct udp_pcb        *dns_pcb;\r
204 static u8_t                   dns_seqno;\r
205 static struct dns_table_entry dns_table[DNS_TABLE_SIZE];\r
206 static struct ip_addr         dns_servers[DNS_MAX_SERVERS];\r
207 \r
208 #if (DNS_USES_STATIC_BUF == 1)\r
209 static u8_t                   dns_payload[DNS_MSG_SIZE];\r
210 #endif /* (DNS_USES_STATIC_BUF == 1) */\r
211 \r
212 /**\r
213  * Initialize the resolver: set up the UDP pcb and configure the default server\r
214  * (DNS_SERVER_ADDRESS).\r
215  */\r
216 void\r
217 dns_init()\r
218 {\r
219   struct ip_addr dnsserver;\r
220   \r
221   /* initialize default DNS server address */\r
222   dnsserver.addr = DNS_SERVER_ADDRESS;\r
223 \r
224   LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));\r
225 \r
226   /* if dns client not yet initialized... */\r
227   if (dns_pcb == NULL) {\r
228     dns_pcb = udp_new();\r
229 \r
230     if (dns_pcb != NULL) {\r
231       /* initialize DNS table not needed (initialized to zero since it is a\r
232        * global variable) */\r
233       LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",\r
234         DNS_STATE_UNUSED == 0);\r
235 \r
236       /* initialize DNS client */\r
237       udp_bind(dns_pcb, IP_ADDR_ANY, 0);\r
238       udp_recv(dns_pcb, dns_recv, NULL);\r
239 \r
240       /* initialize default DNS primary server */\r
241       dns_setserver(0, &dnsserver);\r
242     }\r
243   }\r
244 }\r
245 \r
246 /**\r
247  * Initialize one of the DNS servers.\r
248  *\r
249  * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS\r
250  * @param dnsserver IP address of the DNS server to set\r
251  */\r
252 void\r
253 dns_setserver(u8_t numdns, struct ip_addr *dnsserver)\r
254 {\r
255   if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&\r
256       (dnsserver != NULL) && (dnsserver->addr !=0 )) {\r
257     dns_servers[numdns] = (*dnsserver);\r
258   }\r
259 }\r
260 \r
261 /**\r
262  * Obtain one of the currently configured DNS server.\r
263  *\r
264  * @param numdns the index of the DNS server\r
265  * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS\r
266  *         server has not been configured.\r
267  */\r
268 struct ip_addr\r
269 dns_getserver(u8_t numdns)\r
270 {\r
271   if (numdns < DNS_MAX_SERVERS) {\r
272     return dns_servers[numdns];\r
273   } else {\r
274     return *IP_ADDR_ANY;\r
275   }\r
276 }\r
277 \r
278 /**\r
279  * The DNS resolver client timer - handle retries and timeouts and should\r
280  * be called every DNS_TMR_INTERVAL milliseconds (every second by default).\r
281  */\r
282 void\r
283 dns_tmr(void)\r
284 {\r
285   if (dns_pcb != NULL) {\r
286     LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));\r
287     dns_check_entries();\r
288   }\r
289 }\r
290 \r
291 /**\r
292  * Look up a hostname in the array of known hostnames.\r
293  *\r
294  * @note This function only looks in the internal array of known\r
295  * hostnames, it does not send out a query for the hostname if none\r
296  * was found. The function dns_enqueue() can be used to send a query\r
297  * for a hostname.\r
298  *\r
299  * @param name the hostname to look up\r
300  * @return the hostname's IP address, as u32_t (instead of struct ip_addr to\r
301  *         better check for failure: != 0) or 0 if the hostname was not found\r
302  *         in the cached dns_table.\r
303  */\r
304 static u32_t\r
305 dns_lookup(const char *name)\r
306 {\r
307   u8_t i;\r
308 \r
309   /* Walk through name list, return entry if found. If not, return NULL. */\r
310   for (i = 0; i < DNS_TABLE_SIZE; ++i) {\r
311     if ((dns_table[i].state == DNS_STATE_DONE) &&\r
312         (strcmp(name, dns_table[i].name) == 0)) {\r
313       LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));\r
314       ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));\r
315       LWIP_DEBUGF(DNS_DEBUG, ("\n"));\r
316       return dns_table[i].ipaddr.addr;\r
317     }\r
318   }\r
319 \r
320   return 0;\r
321 }\r
322 \r
323 #if DNS_DOES_NAME_CHECK\r
324 /**\r
325  * Compare the "dotted" name "query" with the encoded name "response"\r
326  * to make sure an answer from the DNS server matches the current dns_table\r
327  * entry (otherwise, answers might arrive late for hostname not on the list\r
328  * any more).\r
329  *\r
330  * @param query hostname (not encoded) from the dns_table\r
331  * @param response encoded hostname in the DNS response\r
332  * @return 0: names equal; 1: names differ\r
333  */\r
334 static u8_t\r
335 dns_compare_name(unsigned char *query, unsigned char *response)\r
336 {\r
337   unsigned char n;\r
338 \r
339   do {\r
340     n = *response++;\r
341     /** @see RFC 1035 - 4.1.4. Message compression */\r
342     if ((n & 0xc0) == 0xc0) {\r
343       /* Compressed name */\r
344       break;\r
345     } else {\r
346       /* Not compressed name */\r
347       while (n > 0) {\r
348         if ((*query) != (*response)) {\r
349           return 1;\r
350         }\r
351         ++response;\r
352         ++query;\r
353         --n;\r
354       };\r
355       ++query;\r
356     }\r
357   } while (*response != 0);\r
358 \r
359   return 0;\r
360 }\r
361 #endif /* DNS_DOES_NAME_CHECK */\r
362 \r
363 /**\r
364  * Walk through a compact encoded DNS name and return the end of the name.\r
365  *\r
366  * @param query encoded DNS name in the DNS server response\r
367  * @return end of the name\r
368  */\r
369 static unsigned char *\r
370 dns_parse_name(unsigned char *query)\r
371 {\r
372   unsigned char n;\r
373 \r
374   do {\r
375     n = *query++;\r
376     /** @see RFC 1035 - 4.1.4. Message compression */\r
377     if ((n & 0xc0) == 0xc0) {\r
378       /* Compressed name */\r
379       break;\r
380     } else {\r
381       /* Not compressed name */\r
382       while (n > 0) {\r
383         ++query;\r
384         --n;\r
385       };\r
386     }\r
387   } while (*query != 0);\r
388 \r
389   return query + 1;\r
390 }\r
391 \r
392 /**\r
393  * Send a DNS query packet.\r
394  *\r
395  * @param numdns index of the DNS server in the dns_servers table\r
396  * @param name hostname to query\r
397  * @param id index of the hostname in dns_table, used as transaction ID in the\r
398  *        DNS query packet\r
399  * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise\r
400  */\r
401 static err_t\r
402 dns_send(u8_t numdns, const char* name, u8_t id)\r
403 {\r
404   err_t err;\r
405   struct dns_hdr *hdr;\r
406   struct dns_query qry;\r
407   struct pbuf *p;\r
408   char *query, *nptr;\r
409   const char *pHostname;\r
410   u8_t n;\r
411 \r
412   LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",\r
413               (u16_t)(numdns), name));\r
414   LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);\r
415   LWIP_ASSERT("dns server has no IP address set", dns_servers[numdns].addr != 0);\r
416 \r
417   /* if here, we have either a new query or a retry on a previous query to process */\r
418   p = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dns_hdr) + DNS_MAX_NAME_LENGTH +\r
419                  sizeof(struct dns_query), PBUF_RAM);\r
420   if (p != NULL) {\r
421     LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);\r
422     /* fill dns header */\r
423     hdr = (struct dns_hdr*)p->payload;\r
424     memset(hdr, 0, sizeof(struct dns_hdr));\r
425     hdr->id = htons(id);\r
426     hdr->flags1 = DNS_FLAG1_RD;\r
427     hdr->numquestions = htons(1);\r
428     query = (char*)hdr + sizeof(struct dns_hdr);\r
429     pHostname = name;\r
430     --pHostname;\r
431 \r
432     /* convert hostname into suitable query format. */\r
433     do {\r
434       ++pHostname;\r
435       nptr = query;\r
436       ++query;\r
437       for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {\r
438         *query = *pHostname;\r
439         ++query;\r
440         ++n;\r
441       }\r
442       *nptr = n;\r
443     } while(*pHostname != 0);\r
444     *query++='\0';\r
445 \r
446     /* fill dns query */\r
447     qry.type  = htons(DNS_RRTYPE_A);\r
448     qry.class = htons(DNS_RRCLASS_IN);\r
449     MEMCPY( query, &qry, sizeof(struct dns_query));\r
450 \r
451     /* resize pbuf to the exact dns query */\r
452     pbuf_realloc(p, (query + sizeof(struct dns_query)) - ((char*)(p->payload)));\r
453 \r
454     /* connect to the server for faster receiving */\r
455     udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);\r
456     /* send dns packet */\r
457     err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);\r
458 \r
459     /* free pbuf */\r
460     pbuf_free(p);\r
461   } else {\r
462     err = ERR_MEM;\r
463   }\r
464 \r
465   return err;\r
466 }\r
467 \r
468 /**\r
469  * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.\r
470  * Check an entry in the dns_table:\r
471  * - send out query for new entries\r
472  * - retry old pending entries on timeout (also with different servers)\r
473  * - remove completed entries from the table if their TTL has expired\r
474  *\r
475  * @param i index of the dns_table entry to check\r
476  */\r
477 static void\r
478 dns_check_entry(u8_t i)\r
479 {\r
480   struct dns_table_entry *pEntry = &dns_table[i];\r
481 \r
482   LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);\r
483 \r
484   switch(pEntry->state) {\r
485 \r
486     case DNS_STATE_NEW: {\r
487       /* initialize new entry */\r
488       pEntry->state   = DNS_STATE_ASKING;\r
489       pEntry->numdns  = 0;\r
490       pEntry->tmr     = 1;\r
491       pEntry->retries = 0;\r
492       \r
493       /* send DNS packet for this entry */\r
494       dns_send(pEntry->numdns, pEntry->name, i);\r
495       break;\r
496     }\r
497 \r
498     case DNS_STATE_ASKING: {\r
499       if (--pEntry->tmr == 0) {\r
500         if (++pEntry->retries == DNS_MAX_RETRIES) {\r
501           if ((pEntry->numdns+1<DNS_MAX_SERVERS) && (dns_servers[pEntry->numdns+1].addr!=0)) {\r
502             /* change of server */\r
503             pEntry->numdns++;\r
504             pEntry->tmr     = 1;\r
505             pEntry->retries = 0;\r
506             break;\r
507           } else {\r
508             LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));\r
509             /* call specified callback function if provided */\r
510             if (pEntry->found)\r
511               (*pEntry->found)(pEntry->name, NULL, pEntry->arg);\r
512             /* flush this entry */\r
513             pEntry->state   = DNS_STATE_UNUSED;\r
514             pEntry->found   = NULL;\r
515             break;\r
516           }\r
517         }\r
518 \r
519         /* wait longer for the next retry */\r
520         pEntry->tmr = pEntry->retries;\r
521 \r
522         /* send DNS packet for this entry */\r
523         dns_send(pEntry->numdns, pEntry->name, i);\r
524       }\r
525       break;\r
526     }\r
527 \r
528     case DNS_STATE_DONE: {\r
529       /* if the time to live is nul */\r
530       if (--pEntry->ttl == 0) {\r
531         LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));\r
532         /* flush this entry */\r
533         pEntry->state = DNS_STATE_UNUSED;\r
534         pEntry->found = NULL;\r
535       }\r
536       break;\r
537     }\r
538     case DNS_STATE_UNUSED:\r
539       /* nothing to do */\r
540       break;\r
541     default:\r
542       LWIP_ASSERT("unknown dns_table entry state:", 0);\r
543       break;\r
544   }\r
545 }\r
546 \r
547 /**\r
548  * Call dns_check_entry for each entry in dns_table - check all entries.\r
549  */\r
550 static void\r
551 dns_check_entries(void)\r
552 {\r
553   u8_t i;\r
554 \r
555   for (i = 0; i < DNS_TABLE_SIZE; ++i) {\r
556     dns_check_entry(i);\r
557   }\r
558 }\r
559 \r
560 /**\r
561  * Receive input function for DNS response packets arriving for the dns UDP pcb.\r
562  *\r
563  * @params see udp.h\r
564  */\r
565 static void\r
566 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, struct ip_addr *addr, u16_t port)\r
567 {\r
568   u8_t i;\r
569   char *pHostname;\r
570   struct dns_hdr *hdr;\r
571   struct dns_answer ans;\r
572   struct dns_table_entry *pEntry;\r
573   u8_t nquestions, nanswers;\r
574 #if (DNS_USES_STATIC_BUF == 0)\r
575   u8_t dns_payload[DNS_MSG_SIZE];\r
576 #endif /* (DNS_USES_STATIC_BUF == 0) */\r
577 #if (DNS_USES_STATIC_BUF == 2)\r
578   u8_t* dns_payload;\r
579 #endif /* (DNS_USES_STATIC_BUF == 2) */\r
580 \r
581   LWIP_UNUSED_ARG(arg);\r
582   LWIP_UNUSED_ARG(pcb);\r
583   LWIP_UNUSED_ARG(addr);\r
584   LWIP_UNUSED_ARG(port);\r
585 \r
586   /* is the dns message too big ? */\r
587   if (p->tot_len > DNS_MSG_SIZE) {\r
588     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));\r
589     /* free pbuf and return */\r
590     goto memerr1;\r
591   }\r
592 \r
593   /* is the dns message big enough ? */\r
594   if (p->tot_len < (sizeof(struct dns_hdr) + sizeof(struct dns_query) + sizeof(struct dns_answer))) {\r
595     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));\r
596     /* free pbuf and return */\r
597     goto memerr1;\r
598   }\r
599 \r
600 #if (DNS_USES_STATIC_BUF == 2)\r
601   dns_payload = mem_malloc(p->tot_len);\r
602   if (dns_payload == NULL) {\r
603     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: mem_malloc error\n"));\r
604     /* free pbuf and return */\r
605     goto memerr1;\r
606   }\r
607 #endif /* (DNS_USES_STATIC_BUF == 2) */\r
608 \r
609   /* copy dns payload inside static buffer for processing */ \r
610   if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {\r
611     /* The ID in the DNS header should be our entry into the name table. */\r
612     hdr = (struct dns_hdr*)dns_payload;\r
613     i = htons(hdr->id);\r
614     if (i < DNS_TABLE_SIZE) {\r
615       pEntry = &dns_table[i];\r
616       if(pEntry->state == DNS_STATE_ASKING) {\r
617         /* This entry is now completed. */\r
618         pEntry->state = DNS_STATE_DONE;\r
619         pEntry->err   = hdr->flags2 & DNS_FLAG2_ERR_MASK;\r
620 \r
621         /* We only care about the question(s) and the answers. The authrr\r
622            and the extrarr are simply discarded. */\r
623         nquestions = htons(hdr->numquestions);\r
624         nanswers   = htons(hdr->numanswers);\r
625 \r
626         /* Check for error. If so, call callback to inform. */\r
627         if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {\r
628           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));\r
629           /* call callback to indicate error, clean up memory and return */\r
630           goto responseerr;\r
631         }\r
632 \r
633 #if DNS_DOES_NAME_CHECK\r
634         /* Check if the name in the "question" part match with the name in the entry. */\r
635         if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + sizeof(struct dns_hdr)) != 0) {\r
636           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));\r
637           /* call callback to indicate error, clean up memory and return */\r
638           goto responseerr;\r
639         }\r
640 #endif /* DNS_DOES_NAME_CHECK */\r
641 \r
642         /* Skip the name in the "question" part */\r
643         pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + sizeof(struct dns_hdr)) + sizeof(struct dns_query);\r
644 \r
645         while(nanswers > 0) {\r
646           /* skip answer resource record's host name */\r
647           pHostname = (char *) dns_parse_name((unsigned char *)pHostname);\r
648 \r
649           /* Check for IP address type and Internet class. Others are discarded. */\r
650           MEMCPY(&ans, pHostname, sizeof(struct dns_answer));\r
651           if((ntohs(ans.type) == DNS_RRTYPE_A) && (ntohs(ans.class) == DNS_RRCLASS_IN) && (ntohs(ans.len) == sizeof(struct ip_addr)) ) {\r
652             /* read the answer resource record's TTL, and maximize it if needed */\r
653             pEntry->ttl = ntohl(ans.ttl);\r
654             if (pEntry->ttl > DNS_MAX_TTL) {\r
655               pEntry->ttl = DNS_MAX_TTL;\r
656             }\r
657             /* read the IP address after answer resource record's header */\r
658             MEMCPY( &(pEntry->ipaddr), (pHostname+sizeof(struct dns_answer)), sizeof(struct ip_addr));\r
659             LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));\r
660             ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));\r
661             LWIP_DEBUGF(DNS_DEBUG, ("\n"));\r
662             /* call specified callback function if provided */\r
663             if (pEntry->found) {\r
664               (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);\r
665             }\r
666             /* deallocate memory and return */\r
667             goto memerr2;\r
668           } else {\r
669             pHostname = pHostname + sizeof(struct dns_answer) + htons(ans.len);\r
670           }\r
671           --nanswers;\r
672         }\r
673         LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));\r
674         /* call callback to indicate error, clean up memory and return */\r
675         goto responseerr;\r
676       }\r
677     }\r
678   }\r
679 \r
680   /* deallocate memory and return */\r
681   goto memerr2;\r
682 \r
683 responseerr:\r
684   /* ERROR: call specified callback function with NULL as name to indicate an error */\r
685   if (pEntry->found) {\r
686     (*pEntry->found)(pEntry->name, NULL, pEntry->arg);\r
687   }\r
688   /* flush this entry */\r
689   pEntry->state = DNS_STATE_UNUSED;\r
690   pEntry->found = NULL;\r
691 \r
692 memerr2:\r
693 #if (DNS_USES_STATIC_BUF == 2)\r
694   /* free dns buffer */\r
695   mem_free(dns_payload);\r
696 #endif /* (DNS_USES_STATIC_BUF == 2) */\r
697 \r
698 memerr1:\r
699   /* free pbuf */\r
700   pbuf_free(p);\r
701   return;\r
702 }\r
703 \r
704 /**\r
705  * Queues a new hostname to resolve and sends out a DNS query for that hostname\r
706  *\r
707  * @param name the hostname that is to be queried\r
708  * @param found a callback founction to be called on success, failure or timeout\r
709  * @param callback_arg argument to pass to the callback function\r
710  * @return @return a err_t return code.\r
711  */\r
712 static err_t\r
713 dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)\r
714 {\r
715   u8_t i;\r
716   u8_t lseq, lseqi;\r
717   struct dns_table_entry *pEntry = NULL;\r
718 \r
719   /* search an unused entry, or the oldest one */\r
720   lseq = lseqi = 0;\r
721   for (i = 0; i < DNS_TABLE_SIZE; ++i) {\r
722     pEntry = &dns_table[i];\r
723     /* is it an unused entry ? */\r
724     if (pEntry->state == DNS_STATE_UNUSED)\r
725       break;\r
726 \r
727     /* check if this is the oldest completed entry */\r
728     if (pEntry->state == DNS_STATE_DONE) {\r
729       if ((dns_seqno - pEntry->seqno) > lseq) {\r
730         lseq = dns_seqno - pEntry->seqno;\r
731         lseqi = i;\r
732       }\r
733     }\r
734   }\r
735 \r
736   /* if we don't have found an unused entry, use the oldest completed one */\r
737   if (i == DNS_TABLE_SIZE) {\r
738     if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {\r
739       /* no entry can't be used now, table is full */\r
740       LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));\r
741       return ERR_MEM;\r
742     } else {\r
743       /* use the oldest completed one */\r
744       i = lseqi;\r
745       pEntry = &dns_table[i];\r
746     }\r
747   }\r
748 \r
749   /* use this entry */\r
750   LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));\r
751 \r
752   /* fill the entry */\r
753   pEntry->state = DNS_STATE_NEW;\r
754   pEntry->seqno = dns_seqno++;\r
755   pEntry->found = found;\r
756   pEntry->arg   = callback_arg;\r
757   strcpy(pEntry->name, name);\r
758 \r
759   /* force to send query without waiting timer */\r
760   dns_check_entry(i);\r
761 \r
762   /* dns query is enqueued */\r
763   return ERR_INPROGRESS;\r
764 }\r
765 \r
766 /**\r
767  * Resolve a hostname (string) into an IP address.\r
768  * NON-BLOCKING callback version for use with raw API!!!\r
769  *\r
770  * Returns immediately with one of err_t return codes:\r
771  * - ERR_OK if hostname is a valid IP address string or the host\r
772  *   name is already in the local names table.\r
773  * - ERR_INPROGRESS enqueue a request to be sent to the DNS server\r
774  *   for resolution if no errors are present.\r
775  *\r
776  * @param hostname the hostname that is to be queried\r
777  * @param addr pointer to a struct ip_addr where to store the address if it is already\r
778  *             cached in the dns_table (only valid if ERR_OK is returned!)\r
779  * @param found a callback function to be called on success, failure or timeout (only if\r
780  *              ERR_INPROGRESS is returned!)\r
781  * @param callback_arg argument to pass to the callback function\r
782  * @return a err_t return code.\r
783  */\r
784 err_t\r
785 dns_gethostbyname(const char *hostname, struct ip_addr *addr, dns_found_callback found,\r
786                   void *callback_arg)\r
787 {\r
788   /* not initialized or no valid server yet, or invalid addr pointer\r
789    * or invalid hostname or invalid hostname length */\r
790   if ((dns_pcb == NULL) || (addr == NULL) ||\r
791       (!hostname) || (!hostname[0]) ||\r
792       (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {\r
793     return ERR_VAL;\r
794   }\r
795 \r
796 #if LWIP_HAVE_LOOPIF\r
797   if (strcmp(hostname,"localhost")==0) {\r
798     addr->addr = INADDR_LOOPBACK;\r
799     return ERR_OK;\r
800   }\r
801 #endif /* LWIP_HAVE_LOOPIF */\r
802 \r
803   /* host name already in octet notation? set ip addr and return ERR_OK\r
804    * already have this address cached? */\r
805   if (((addr->addr = inet_addr(hostname)) != INADDR_NONE) ||\r
806       ((addr->addr = dns_lookup(hostname)) != 0)) {\r
807     return ERR_OK;\r
808   }\r
809 \r
810   /* queue query with specified callback */\r
811   return dns_enqueue(hostname, found, callback_arg);\r
812 }\r
813 \r
814 #endif /* LWIP_DNS */\r