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/memp.h"
82 #include "lwip/dns.h"
83 
84 #include <string.h>
85 
86 #include <rtthread.h>
87 
88 /** DNS server IP address */
89 #ifndef DNS_SERVER_ADDRESS
90 #define DNS_SERVER_ADDRESS(ipaddr)        (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
91 #endif
92 
93 /** DNS server port address */
94 #ifndef DNS_SERVER_PORT
95 #define DNS_SERVER_PORT           53
96 #endif
97 
98 /** DNS maximum number of retries when asking for a name, before "timeout". */
99 #ifndef DNS_MAX_RETRIES
100 #define DNS_MAX_RETRIES           4
101 #endif
102 
103 /** DNS resource record max. TTL (one week as default) */
104 #ifndef DNS_MAX_TTL
105 #define DNS_MAX_TTL               604800
106 #endif
107 
108 /* DNS protocol flags */
109 #define DNS_FLAG1_RESPONSE        0x80
110 #define DNS_FLAG1_OPCODE_STATUS   0x10
111 #define DNS_FLAG1_OPCODE_INVERSE  0x08
112 #define DNS_FLAG1_OPCODE_STANDARD 0x00
113 #define DNS_FLAG1_AUTHORATIVE     0x04
114 #define DNS_FLAG1_TRUNC           0x02
115 #define DNS_FLAG1_RD              0x01
116 #define DNS_FLAG2_RA              0x80
117 #define DNS_FLAG2_ERR_MASK        0x0f
118 #define DNS_FLAG2_ERR_NONE        0x00
119 #define DNS_FLAG2_ERR_NAME        0x03
120 
121 /* DNS protocol states */
122 #define DNS_STATE_UNUSED          0
123 #define DNS_STATE_NEW             1
124 #define DNS_STATE_ASKING          2
125 #define DNS_STATE_DONE            3
126 
127 #ifdef PACK_STRUCT_USE_INCLUDES
128 #  include "arch/bpstruct.h"
129 #endif
130 PACK_STRUCT_BEGIN
131 /** DNS message header */
132 struct dns_hdr {
133   PACK_STRUCT_FIELD(u16_t id);
134   PACK_STRUCT_FIELD(u8_t flags1);
135   PACK_STRUCT_FIELD(u8_t flags2);
136   PACK_STRUCT_FIELD(u16_t numquestions);
137   PACK_STRUCT_FIELD(u16_t numanswers);
138   PACK_STRUCT_FIELD(u16_t numauthrr);
139   PACK_STRUCT_FIELD(u16_t numextrarr);
140 } PACK_STRUCT_STRUCT;
141 PACK_STRUCT_END
142 #ifdef PACK_STRUCT_USE_INCLUDES
143 #  include "arch/epstruct.h"
144 #endif
145 #define SIZEOF_DNS_HDR 12
146 
147 /** DNS query message structure.
148     No packing needed: only used locally on the stack. */
149 struct dns_query {
150   /* DNS query record starts with either a domain name or a pointer
151      to a name already present somewhere in the packet. */
152   u16_t type;
153   u16_t cls;
154 };
155 #define SIZEOF_DNS_QUERY 4
156 
157 /** DNS answer message structure.
158     No packing needed: only used locally on the stack. */
159 struct dns_answer {
160   /* DNS answer record starts with either a domain name or a pointer
161      to a name already present somewhere in the packet. */
162   u16_t type;
163   u16_t cls;
164   u32_t ttl;
165   u16_t len;
166 };
167 #define SIZEOF_DNS_ANSWER 10
168 
169 /** DNS table entry */
170 struct dns_table_entry {
171   u8_t  state;
172   u8_t  numdns;
173   u8_t  tmr;
174   u8_t  retries;
175   u8_t  seqno;
176   u8_t  err;
177   u32_t ttl;
178   char name[DNS_MAX_NAME_LENGTH];
179   ip_addr_t ipaddr;
180   /* pointer to callback on DNS query done */
181   dns_found_callback found;
182   void *arg;
183 };
184 
185 #if DNS_LOCAL_HOSTLIST
186 
187 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
188 /** Local host-list. For hostnames in this list, no
189  *  external name resolution is performed */
190 static struct local_hostlist_entry *local_hostlist_dynamic;
191 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
192 
193 /** Defining this allows the local_hostlist_static to be placed in a different
194  * linker section (e.g. FLASH) */
195 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
196 #define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
197 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
198 /** Defining this allows the local_hostlist_static to be placed in a different
199  * linker section (e.g. FLASH) */
200 #ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
201 #define DNS_LOCAL_HOSTLIST_STORAGE_POST
202 #endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
203 DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
204   DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
205 
206 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
207 
208 static void dns_init_local();
209 #endif /* DNS_LOCAL_HOSTLIST */
210 
211 
212 /* forward declarations */
213 static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
214 static void dns_check_entries(void);
215 
216 /*-----------------------------------------------------------------------------
217  * Globales
218  *----------------------------------------------------------------------------*/
219 
220 /* DNS variables */
221 static struct udp_pcb        *dns_pcb;
222 static u8_t                   dns_seqno;
223 static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
224 static ip_addr_t              dns_servers[DNS_MAX_SERVERS];
225 /** Contiguous buffer for processing responses */
226 static u8_t                   dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
227 static u8_t*                  dns_payload;
228 
229 /**
230  * Initialize the resolver: set up the UDP pcb and configure the default server
231  * (DNS_SERVER_ADDRESS).
232  */
233 void
dns_init()234 dns_init()
235 {
236   ip_addr_t dnsserver;
237 
238   dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
239 
240   /* initialize default DNS server address */
241   DNS_SERVER_ADDRESS(&dnsserver);
242 
243   LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
244 
245   /* if dns client not yet initialized... */
246   if (dns_pcb == NULL) {
247     dns_pcb = udp_new();
248 
249     if (dns_pcb != NULL) {
250       /* initialize DNS table not needed (initialized to zero since it is a
251        * global variable) */
252       LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
253         DNS_STATE_UNUSED == 0);
254 
255       /* initialize DNS client */
256       udp_bind(dns_pcb, IP_ADDR_ANY, 0);
257       udp_recv(dns_pcb, dns_recv, NULL);
258 
259       /* initialize default DNS primary server */
260       dns_setserver(0, &dnsserver);
261     }
262   }
263 #if DNS_LOCAL_HOSTLIST
264   dns_init_local();
265 #endif
266 }
267 
268 /**
269  * Initialize one of the DNS servers.
270  *
271  * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
272  * @param dnsserver IP address of the DNS server to set
273  */
274 void
dns_setserver(u8_t numdns,ip_addr_t * dnsserver)275 dns_setserver(u8_t numdns, ip_addr_t *dnsserver)
276 {
277   if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
278       (dnsserver != NULL) && !ip_addr_isany(dnsserver)) {
279     dns_servers[numdns] = (*dnsserver);
280 
281 #ifdef RT_USING_NETDEV
282       extern struct netif *netif_list;
283       extern struct netdev *netdev_get_by_name(const char *name);
284       extern void netdev_low_level_set_dns_server(struct netdev *netdev, uint8_t dns_num, const ip_addr_t *dns_server);
285       struct netif *netif = NULL;
286 
287       /* set network interface device DNS server address */
288       for (netif = netif_list; netif != NULL; netif = netif->next) {
289         netdev_low_level_set_dns_server(netdev_get_by_name(netif->name), numdns, dnsserver);
290       }
291 #endif /* RT_USING_NETDEV */
292   }
293 }
294 
295 /**
296  * Obtain one of the currently configured DNS server.
297  *
298  * @param numdns the index of the DNS server
299  * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
300  *         server has not been configured.
301  */
302 ip_addr_t
dns_getserver(u8_t numdns)303 dns_getserver(u8_t numdns)
304 {
305   if (numdns < DNS_MAX_SERVERS) {
306     return dns_servers[numdns];
307   } else {
308     return *IP_ADDR_ANY;
309   }
310 }
311 
312 /**
313  * The DNS resolver client timer - handle retries and timeouts and should
314  * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
315  */
316 void
dns_tmr(void)317 dns_tmr(void)
318 {
319   if (dns_pcb != NULL) {
320     LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
321     dns_check_entries();
322   }
323 }
324 
325 #if DNS_LOCAL_HOSTLIST
326 static void
dns_init_local()327 dns_init_local()
328 {
329 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
330   int i;
331   struct local_hostlist_entry *entry;
332   /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
333   struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
334   size_t namelen;
335   for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
336     struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
337     LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
338     namelen = strlen(init_entry->name);
339     LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
340     entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
341     LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
342     if (entry != NULL) {
343       entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
344       MEMCPY((char*)entry->name, init_entry->name, namelen);
345       ((char*)entry->name)[namelen] = 0;
346       entry->addr = init_entry->addr;
347       entry->next = local_hostlist_dynamic;
348       local_hostlist_dynamic = entry;
349     }
350   }
351 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
352 }
353 
354 /**
355  * Scans the local host-list for a hostname.
356  *
357  * @param hostname Hostname to look for in the local host-list
358  * @return The first IP address for the hostname in the local host-list or
359  *         IPADDR_NONE if not found.
360  */
361 static u32_t
dns_lookup_local(const char * hostname)362 dns_lookup_local(const char *hostname)
363 {
364 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
365   struct local_hostlist_entry *entry = local_hostlist_dynamic;
366   while(entry != NULL) {
367     if(strcmp(entry->name, hostname) == 0) {
368       return ip4_addr_get_u32(&entry->addr);
369     }
370     entry = entry->next;
371   }
372 #else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
373   int i;
374   for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
375     if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
376       return ip4_addr_get_u32(&local_hostlist_static[i].addr);
377     }
378   }
379 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
380   return IPADDR_NONE;
381 }
382 
383 #if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
384 /** Remove all entries from the local host-list for a specific hostname
385  * and/or IP addess
386  *
387  * @param hostname hostname for which entries shall be removed from the local
388  *                 host-list
389  * @param addr address for which entries shall be removed from the local host-list
390  * @return the number of removed entries
391  */
392 int
dns_local_removehost(const char * hostname,const ip_addr_t * addr)393 dns_local_removehost(const char *hostname, const ip_addr_t *addr)
394 {
395   int removed = 0;
396   struct local_hostlist_entry *entry = local_hostlist_dynamic;
397   struct local_hostlist_entry *last_entry = NULL;
398   while (entry != NULL) {
399     if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
400         ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
401       struct local_hostlist_entry *free_entry;
402       if (last_entry != NULL) {
403         last_entry->next = entry->next;
404       } else {
405         local_hostlist_dynamic = entry->next;
406       }
407       free_entry = entry;
408       entry = entry->next;
409       memp_free(MEMP_LOCALHOSTLIST, free_entry);
410       removed++;
411     } else {
412       last_entry = entry;
413       entry = entry->next;
414     }
415   }
416   return removed;
417 }
418 
419 /**
420  * Add a hostname/IP address pair to the local host-list.
421  * Duplicates are not checked.
422  *
423  * @param hostname hostname of the new entry
424  * @param addr IP address of the new entry
425  * @return ERR_OK if succeeded or ERR_MEM on memory error
426  */
427 err_t
dns_local_addhost(const char * hostname,const ip_addr_t * addr)428 dns_local_addhost(const char *hostname, const ip_addr_t *addr)
429 {
430   struct local_hostlist_entry *entry;
431   size_t namelen;
432   LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
433   namelen = strlen(hostname);
434   LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
435   entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
436   if (entry == NULL) {
437     return ERR_MEM;
438   }
439   entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
440   MEMCPY((char*)entry->name, hostname, namelen);
441   ((char*)entry->name)[namelen] = 0;
442   ip_addr_copy(entry->addr, *addr);
443   entry->next = local_hostlist_dynamic;
444   local_hostlist_dynamic = entry;
445   return ERR_OK;
446 }
447 #endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
448 #endif /* DNS_LOCAL_HOSTLIST */
449 
450 /**
451  * Look up a hostname in the array of known hostnames.
452  *
453  * @note This function only looks in the internal array of known
454  * hostnames, it does not send out a query for the hostname if none
455  * was found. The function dns_enqueue() can be used to send a query
456  * for a hostname.
457  *
458  * @param name the hostname to look up
459  * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
460  *         better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
461  *         was not found in the cached dns_table.
462  */
463 static u32_t
dns_lookup(const char * name)464 dns_lookup(const char *name)
465 {
466   u8_t i;
467 #if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
468   u32_t addr;
469 #endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
470 #if DNS_LOCAL_HOSTLIST
471   if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
472     return addr;
473   }
474 #endif /* DNS_LOCAL_HOSTLIST */
475 #ifdef DNS_LOOKUP_LOCAL_EXTERN
476   if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
477     return addr;
478   }
479 #endif /* DNS_LOOKUP_LOCAL_EXTERN */
480 
481   /* Walk through name list, return entry if found. If not, return NULL. */
482   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
483     if ((dns_table[i].state == DNS_STATE_DONE) &&
484         (strcmp(name, dns_table[i].name) == 0)) {
485       LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
486       ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
487       LWIP_DEBUGF(DNS_DEBUG, ("\n"));
488       return ip4_addr_get_u32(&dns_table[i].ipaddr);
489     }
490   }
491 
492   return IPADDR_NONE;
493 }
494 
495 #if DNS_DOES_NAME_CHECK
496 /**
497  * Compare the "dotted" name "query" with the encoded name "response"
498  * to make sure an answer from the DNS server matches the current dns_table
499  * entry (otherwise, answers might arrive late for hostname not on the list
500  * any more).
501  *
502  * @param query hostname (not encoded) from the dns_table
503  * @param response encoded hostname in the DNS response
504  * @return 0: names equal; 1: names differ
505  */
506 static u8_t
dns_compare_name(unsigned char * query,unsigned char * response)507 dns_compare_name(unsigned char *query, unsigned char *response)
508 {
509   unsigned char n;
510 
511   do {
512     n = *response++;
513     /** @see RFC 1035 - 4.1.4. Message compression */
514     if ((n & 0xc0) == 0xc0) {
515       /* Compressed name */
516       break;
517     } else {
518       /* Not compressed name */
519       while (n > 0) {
520         if ((*query) != (*response)) {
521           return 1;
522         }
523         ++response;
524         ++query;
525         --n;
526       };
527       ++query;
528     }
529   } while (*response != 0);
530 
531   return 0;
532 }
533 #endif /* DNS_DOES_NAME_CHECK */
534 
535 /**
536  * Walk through a compact encoded DNS name and return the end of the name.
537  *
538  * @param query encoded DNS name in the DNS server response
539  * @return end of the name
540  */
541 static unsigned char *
dns_parse_name(unsigned char * query)542 dns_parse_name(unsigned char *query)
543 {
544   unsigned char n;
545 
546   do {
547     n = *query++;
548     /** @see RFC 1035 - 4.1.4. Message compression */
549     if ((n & 0xc0) == 0xc0) {
550       /* Compressed name */
551       break;
552     } else {
553       /* Not compressed name */
554       while (n > 0) {
555         ++query;
556         --n;
557       };
558     }
559   } while (*query != 0);
560 
561   return query + 1;
562 }
563 
564 /**
565  * Send a DNS query packet.
566  *
567  * @param numdns index of the DNS server in the dns_servers table
568  * @param name hostname to query
569  * @param id index of the hostname in dns_table, used as transaction ID in the
570  *        DNS query packet
571  * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
572  */
573 static err_t
dns_send(u8_t numdns,const char * name,u8_t id)574 dns_send(u8_t numdns, const char* name, u8_t id)
575 {
576   err_t err;
577   struct dns_hdr *hdr;
578   struct dns_query qry;
579   struct pbuf *p;
580   char *query, *nptr;
581   const char *pHostname;
582   u8_t n;
583 
584   LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
585               (u16_t)(numdns), name));
586   LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
587   LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
588 
589   /* if here, we have either a new query or a retry on a previous query to process */
590   p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH +
591                  SIZEOF_DNS_QUERY, PBUF_RAM);
592   if (p != NULL) {
593     LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
594     /* fill dns header */
595     hdr = (struct dns_hdr*)p->payload;
596     memset(hdr, 0, SIZEOF_DNS_HDR);
597     hdr->id = htons(id);
598     hdr->flags1 = DNS_FLAG1_RD;
599     hdr->numquestions = PP_HTONS(1);
600     query = (char*)hdr + SIZEOF_DNS_HDR;
601     pHostname = name;
602     --pHostname;
603 
604     /* convert hostname into suitable query format. */
605     do {
606       ++pHostname;
607       nptr = query;
608       ++query;
609       for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
610         *query = *pHostname;
611         ++query;
612         ++n;
613       }
614       *nptr = n;
615     } while(*pHostname != 0);
616     *query++='\0';
617 
618     /* fill dns query */
619     qry.type = PP_HTONS(DNS_RRTYPE_A);
620     qry.cls = PP_HTONS(DNS_RRCLASS_IN);
621     SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
622 
623     /* resize pbuf to the exact dns query */
624     pbuf_realloc(p, (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload))));
625 
626     /* connect to the server for faster receiving */
627     udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
628     /* send dns packet */
629     err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
630 
631     /* free pbuf */
632     pbuf_free(p);
633   } else {
634     err = ERR_MEM;
635   }
636 
637   return err;
638 }
639 
640 /**
641  * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
642  * Check an entry in the dns_table:
643  * - send out query for new entries
644  * - retry old pending entries on timeout (also with different servers)
645  * - remove completed entries from the table if their TTL has expired
646  *
647  * @param i index of the dns_table entry to check
648  */
649 static void
dns_check_entry(u8_t i)650 dns_check_entry(u8_t i)
651 {
652   err_t err;
653   struct dns_table_entry *pEntry = &dns_table[i];
654 
655   LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
656 
657   switch(pEntry->state) {
658 
659     case DNS_STATE_NEW: {
660       /* initialize new entry */
661       pEntry->state   = DNS_STATE_ASKING;
662       pEntry->numdns  = 0;
663       pEntry->tmr     = 1;
664       pEntry->retries = 0;
665 
666       /* send DNS packet for this entry */
667       err = dns_send(pEntry->numdns, pEntry->name, i);
668       if (err != ERR_OK) {
669         LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
670                     ("dns_send returned error: %s\n", lwip_strerr(err)));
671       }
672       break;
673     }
674 
675     case DNS_STATE_ASKING: {
676       if (--pEntry->tmr == 0) {
677         if (++pEntry->retries == DNS_MAX_RETRIES) {
678           if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
679             /* change of server */
680             pEntry->numdns++;
681             pEntry->tmr     = 1;
682             pEntry->retries = 0;
683             break;
684           } else {
685             LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
686             /* call specified callback function if provided */
687             if (pEntry->found)
688               (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
689             /* flush this entry */
690             pEntry->state   = DNS_STATE_UNUSED;
691             pEntry->found   = NULL;
692             break;
693           }
694         }
695 
696         /* wait longer for the next retry */
697         pEntry->tmr = pEntry->retries;
698 
699         /* send DNS packet for this entry */
700         err = dns_send(pEntry->numdns, pEntry->name, i);
701         if (err != ERR_OK) {
702           LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
703                       ("dns_send returned error: %s\n", lwip_strerr(err)));
704         }
705       }
706       break;
707     }
708 
709     case DNS_STATE_DONE: {
710       /* if the time to live is nul */
711       if (--pEntry->ttl == 0) {
712         LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
713         /* flush this entry */
714         pEntry->state = DNS_STATE_UNUSED;
715         pEntry->found = NULL;
716       }
717       break;
718     }
719     case DNS_STATE_UNUSED:
720       /* nothing to do */
721       break;
722     default:
723       LWIP_ASSERT("unknown dns_table entry state:", 0);
724       break;
725   }
726 }
727 
728 /**
729  * Call dns_check_entry for each entry in dns_table - check all entries.
730  */
731 static void
dns_check_entries(void)732 dns_check_entries(void)
733 {
734   u8_t i;
735 
736   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
737     dns_check_entry(i);
738   }
739 }
740 
741 /**
742  * Receive input function for DNS response packets arriving for the dns UDP pcb.
743  *
744  * @params see udp.h
745  */
746 static void
dns_recv(void * arg,struct udp_pcb * pcb,struct pbuf * p,ip_addr_t * addr,u16_t port)747 dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
748 {
749   u16_t i;
750   char *pHostname;
751   struct dns_hdr *hdr;
752   struct dns_answer ans;
753   struct dns_table_entry *pEntry;
754   u16_t nquestions, nanswers;
755 
756   LWIP_UNUSED_ARG(arg);
757   LWIP_UNUSED_ARG(pcb);
758   LWIP_UNUSED_ARG(addr);
759   LWIP_UNUSED_ARG(port);
760 
761   /* is the dns message too big ? */
762   if (p->tot_len > DNS_MSG_SIZE) {
763     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
764     /* free pbuf and return */
765     goto memerr;
766   }
767 
768   /* is the dns message big enough ? */
769   if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
770     LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
771     /* free pbuf and return */
772     goto memerr;
773   }
774 
775   /* copy dns payload inside static buffer for processing */
776   if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
777     /* The ID in the DNS header should be our entry into the name table. */
778     hdr = (struct dns_hdr*)dns_payload;
779     i = htons(hdr->id);
780     if (i < DNS_TABLE_SIZE) {
781       pEntry = &dns_table[i];
782       if(pEntry->state == DNS_STATE_ASKING) {
783         /* This entry is now completed. */
784         pEntry->state = DNS_STATE_DONE;
785         pEntry->err   = hdr->flags2 & DNS_FLAG2_ERR_MASK;
786 
787         /* We only care about the question(s) and the answers. The authrr
788            and the extrarr are simply discarded. */
789         nquestions = htons(hdr->numquestions);
790         nanswers   = htons(hdr->numanswers);
791 
792         /* Check for error. If so, call callback to inform. */
793         if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
794           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
795           /* call callback to indicate error, clean up memory and return */
796           goto responseerr;
797         }
798 
799 #if DNS_DOES_NAME_CHECK
800         /* Check if the name in the "question" part match with the name in the entry. */
801         if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
802           LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
803           /* call callback to indicate error, clean up memory and return */
804           goto responseerr;
805         }
806 #endif /* DNS_DOES_NAME_CHECK */
807 
808         /* Skip the name in the "question" part */
809         pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
810 
811         while (nanswers > 0) {
812           /* skip answer resource record's host name */
813           pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
814 
815           /* Check for IP address type and Internet class. Others are discarded. */
816           SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
817           if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) &&
818              (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) {
819             /* read the answer resource record's TTL, and maximize it if needed */
820             pEntry->ttl = ntohl(ans.ttl);
821             if (pEntry->ttl > DNS_MAX_TTL) {
822               pEntry->ttl = DNS_MAX_TTL;
823             }
824             /* read the IP address after answer resource record's header */
825             SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
826             LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
827             ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
828             LWIP_DEBUGF(DNS_DEBUG, ("\n"));
829             /* call specified callback function if provided */
830             if (pEntry->found) {
831               (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
832             }
833             /* deallocate memory and return */
834             goto memerr;
835           } else {
836             pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
837           }
838           --nanswers;
839         }
840         LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
841         /* call callback to indicate error, clean up memory and return */
842         goto responseerr;
843       }
844     }
845   }
846 
847   /* deallocate memory and return */
848   goto memerr;
849 
850 responseerr:
851   /* ERROR: call specified callback function with NULL as name to indicate an error */
852   if (pEntry->found) {
853     (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
854   }
855   /* flush this entry */
856   pEntry->state = DNS_STATE_UNUSED;
857   pEntry->found = NULL;
858 
859 memerr:
860   /* free pbuf */
861   pbuf_free(p);
862   return;
863 }
864 
865 /**
866  * Queues a new hostname to resolve and sends out a DNS query for that hostname
867  *
868  * @param name the hostname that is to be queried
869  * @param found a callback founction to be called on success, failure or timeout
870  * @param callback_arg argument to pass to the callback function
871  * @return @return a err_t return code.
872  */
873 static err_t
dns_enqueue(const char * name,dns_found_callback found,void * callback_arg)874 dns_enqueue(const char *name, dns_found_callback found, void *callback_arg)
875 {
876   u8_t i;
877   u8_t lseq, lseqi;
878   struct dns_table_entry *pEntry = NULL;
879   size_t namelen;
880 
881   /* search an unused entry, or the oldest one */
882   lseq = lseqi = 0;
883   for (i = 0; i < DNS_TABLE_SIZE; ++i) {
884     pEntry = &dns_table[i];
885     /* is it an unused entry ? */
886     if (pEntry->state == DNS_STATE_UNUSED)
887       break;
888 
889     /* check if this is the oldest completed entry */
890     if (pEntry->state == DNS_STATE_DONE) {
891       if ((dns_seqno - pEntry->seqno) > lseq) {
892         lseq = dns_seqno - pEntry->seqno;
893         lseqi = i;
894       }
895     }
896   }
897 
898   /* if we don't have found an unused entry, use the oldest completed one */
899   if (i == DNS_TABLE_SIZE) {
900     if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
901       /* no entry can't be used now, table is full */
902       LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
903       return ERR_MEM;
904     } else {
905       /* use the oldest completed one */
906       i = lseqi;
907       pEntry = &dns_table[i];
908     }
909   }
910 
911   /* use this entry */
912   LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
913 
914   /* fill the entry */
915   pEntry->state = DNS_STATE_NEW;
916   pEntry->seqno = dns_seqno++;
917   pEntry->found = found;
918   pEntry->arg   = callback_arg;
919   namelen = LWIP_MIN(strlen(name), DNS_MAX_NAME_LENGTH-1);
920   MEMCPY(pEntry->name, name, namelen);
921   pEntry->name[namelen] = 0;
922 
923   /* force to send query without waiting timer */
924   dns_check_entry(i);
925 
926   /* dns query is enqueued */
927   return ERR_INPROGRESS;
928 }
929 
930 /**
931  * Resolve a hostname (string) into an IP address.
932  * NON-BLOCKING callback version for use with raw API!!!
933  *
934  * Returns immediately with one of err_t return codes:
935  * - ERR_OK if hostname is a valid IP address string or the host
936  *   name is already in the local names table.
937  * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
938  *   for resolution if no errors are present.
939  * - ERR_ARG: dns client not initialized or invalid hostname
940  *
941  * @param hostname the hostname that is to be queried
942  * @param addr pointer to a ip_addr_t where to store the address if it is already
943  *             cached in the dns_table (only valid if ERR_OK is returned!)
944  * @param found a callback function to be called on success, failure or timeout (only if
945  *              ERR_INPROGRESS is returned!)
946  * @param callback_arg argument to pass to the callback function
947  * @return a err_t return code.
948  */
949 err_t
dns_gethostbyname(const char * hostname,ip_addr_t * addr,dns_found_callback found,void * callback_arg)950 dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
951                   void *callback_arg)
952 {
953   u32_t ipaddr;
954   /* not initialized or no valid server yet, or invalid addr pointer
955    * or invalid hostname or invalid hostname length */
956   if ((dns_pcb == NULL) || (addr == NULL) ||
957       (!hostname) || (!hostname[0]) ||
958       (strlen(hostname) >= DNS_MAX_NAME_LENGTH)) {
959     return ERR_ARG;
960   }
961 
962 #if LWIP_HAVE_LOOPIF
963   if (strcmp(hostname, "localhost")==0) {
964     ip_addr_set_loopback(addr);
965     return ERR_OK;
966   }
967 #endif /* LWIP_HAVE_LOOPIF */
968 
969   /* host name already in octet notation? set ip addr and return ERR_OK */
970   ipaddr = ipaddr_addr(hostname);
971   if (ipaddr == IPADDR_NONE) {
972     /* already have this address cached? */
973     ipaddr = dns_lookup(hostname);
974   }
975   if (ipaddr != IPADDR_NONE) {
976     ip4_addr_set_u32(addr, ipaddr);
977     return ERR_OK;
978   }
979 
980   /* queue query with specified callback */
981   return dns_enqueue(hostname, found, callback_arg);
982 }
983 
984 #endif /* LWIP_DNS */
985