• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  * API functions for name resolving
4  *
5  * @defgroup netdbapi NETDB API
6  * @ingroup socket
7  */
8 
9 /*
10  * Redistribution and use in source and binary forms, with or without modification,
11  * are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright notice,
14  *    this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  *    this list of conditions and the following disclaimer in the documentation
17  *    and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30  * OF SUCH DAMAGE.
31  *
32  * This file is part of the lwIP TCP/IP stack.
33  *
34  * Author: Simon Goldschmidt
35  *
36  */
37 
38 #include "lwip/netdb.h"
39 
40 #if LWIP_DNS && LWIP_SOCKET
41 
42 #include "lwip/err.h"
43 #include "lwip/mem.h"
44 #include "lwip/memp.h"
45 #include "lwip/ip_addr.h"
46 #include "lwip/api.h"
47 #include "lwip/dns.h"
48 
49 #include <string.h> /* memset */
50 #include <stdlib.h> /* atoi */
51 
52 #define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
53   inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
54   (port) = lwip_ntohs((sin6)->sin6_port); } while (0)
55 #define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipaddr, port) do { \
56   inet_addr_to_ip4addr(ip_2_ip4(ipaddr), &((sin)->sin_addr)); \
57   (port) = lwip_ntohs((sin)->sin_port); } while (0)
58 
59 #if LWIP_DNS_REVERSE
60 #if LWIP_IPV4 && LWIP_IPV6
61 static void
sockaddr_to_ipaddr_port(const struct sockaddr * sockaddr,ip_addr_t * ipaddr,u16_t * port)62 sockaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *ipaddr, u16_t *port)
63 {
64   if ((sockaddr->sa_family) == AF_INET6) {
65     SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, *port);
66     ipaddr->type = IPADDR_TYPE_V6;
67   } else {
68     SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, *port);
69     ipaddr->type = IPADDR_TYPE_V4;
70   }
71 }
72 #endif
73 
74 #if LWIP_IPV4 && LWIP_IPV6
75 #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) sockaddr_to_ipaddr_port(sockaddr, ipaddr, &(port))
76 #elif LWIP_IPV6
77 #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
78   SOCKADDR6_TO_IP6ADDR_PORT((const struct sockaddr_in6*)(const void*)(sockaddr), ipaddr, port)
79 #else /* -> LWIP_IPV4: LWIP_IPV4 && LWIP_IPV6 */
80 #define SOCKADDR_TO_IPADDR_PORT(sockaddr, ipaddr, port) \
81   SOCKADDR4_TO_IP4ADDR_PORT((const struct sockaddr_in*)(const void*)(sockaddr), ipaddr, port)
82 #endif
83 #endif /* LWIP_DNS_REVERSE */
84 
85 /** helper struct for gethostbyname_r to access the char* buffer */
86 struct gethostbyname_r_helper {
87   ip_addr_t *addr_list[DNS_MAX_IPADDR+1];
88   ip_addr_t addr[DNS_MAX_IPADDR];
89   char *aliases;
90 };
91 
92 /** h_errno is exported in netdb.h for access by applications. */
93 #if LWIP_DNS_API_DECLARE_H_ERRNO
94 int h_errno;
95 
96 /* Check if err is in range of h_ernno values */
97 #define LWIP_ERR_IS_HERRNO(err) ((err) >= HOST_NOT_FOUND && (err) <= TRY_AGAIN)
98 #endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
99 
100 /** define "hostent" variables storage: 0 if we use a static (but unprotected)
101  * set of variables for lwip_gethostbyname, 1 if we use a local storage */
102 #ifndef LWIP_DNS_API_HOSTENT_STORAGE
103 #define LWIP_DNS_API_HOSTENT_STORAGE 0
104 #endif
105 
106 /** define "hostent" variables storage */
107 #if LWIP_DNS_API_HOSTENT_STORAGE
108 #define HOSTENT_STORAGE
109 #else
110 #define HOSTENT_STORAGE static
111 #endif /* LWIP_DNS_API_STATIC_HOSTENT */
112 
113 /**
114  * Returns an entry containing addresses of address family AF_INET
115  * for the host with the name 'name'.
116  *
117  * @param name the hostname to resolve
118  * @return an entry containing addresses of address family AF_INET
119  *         for the host with the name 'name'.
120  *
121  * Note: This function is not thread-safe and should not be used in multiple threads.
122  */
123 struct hostent *
lwip_gethostbyname(const char * name)124 lwip_gethostbyname(const char *name)
125 {
126   err_t err;
127   u32_t count = DNS_MAX_IPADDR;
128   u32_t  i;
129   s32_t addrfamily = 0;
130 
131   /* buffer variables for lwip_gethostbyname() */
132   HOSTENT_STORAGE struct hostent s_hostent;
133   HOSTENT_STORAGE char *s_aliases;
134   HOSTENT_STORAGE ip_addr_t s_hostent_addr[DNS_MAX_IPADDR];
135   HOSTENT_STORAGE ip_addr_t *s_phostent_addr[DNS_MAX_IPADDR + 1];
136   HOSTENT_STORAGE char s_hostname[DNS_MAX_NAME_LENGTH + 1];
137 
138   if (name == NULL) {
139     LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) name = NULL", name));
140 #if LWIP_DNS_API_DECLARE_H_ERRNO
141     h_errno = EFAULT;
142 #endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
143     return NULL;
144   }
145 
146   /* query host IP address */
147   err = netconn_gethostbyname(name, s_hostent_addr, &count);
148   if (err != ERR_OK) {
149     LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
150 
151 #if LWIP_DNS_API_DECLARE_H_ERRNO
152     if (LWIP_ERR_IS_HERRNO(err)) {
153       h_errno = err;
154     }
155 #endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
156     return NULL;
157   }
158 
159   /* fill hostent */
160   for (i = 0; i < count; i++) {
161     s_phostent_addr[i] = &s_hostent_addr[i];
162   }
163 
164   /* Fix for supporting IPv6 Address Family */
165   if (count > 0) {
166     /* All addresses in s_hostent_addr list will be of same type
167        So, inorder to decide the address family, we need to check the address type of
168        just ony entry  */
169     if (IP_GET_TYPE(&s_hostent_addr[0]) == IPADDR_TYPE_V4) {
170       addrfamily = AF_INET;
171     } else {
172       addrfamily = AF_INET6;
173     }
174   } else {
175     addrfamily = AF_UNSPEC;
176   }
177 
178   s_phostent_addr[i] = NULL;
179   strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH);
180   s_hostname[DNS_MAX_NAME_LENGTH] = 0;
181   s_hostent.h_name = s_hostname;
182   s_aliases = NULL;
183   s_hostent.h_aliases = &s_aliases;
184   s_hostent.h_addrtype = addrfamily;
185   s_hostent.h_length = sizeof(ip_addr_t);
186   s_hostent.h_addr_list = (char**)&(s_phostent_addr[0]);
187 
188 #if DNS_DEBUG
189   /* dump hostent */
190   LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name           == %s\n", s_hostent.h_name));
191   LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases        == %p\n", (void *)s_hostent.h_aliases));
192   LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype       == %d\n", s_hostent.h_addrtype));
193   LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length         == %d\n", s_hostent.h_length));
194   LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list      == %p\n", (void *)s_hostent.h_addr_list));
195 #endif /* DNS_DEBUG */
196 
197 #if LWIP_DNS_API_HOSTENT_STORAGE
198   /* this function should return the "per-thread" hostent after copy from s_hostent */
199   return sys_thread_hostent(&s_hostent);
200 #else
201   return &s_hostent;
202 #endif /* LWIP_DNS_API_HOSTENT_STORAGE */
203 }
204 
205 /**
206  * Thread-safe variant of lwip_gethostbyname: instead of using a static
207  * buffer, this function takes buffer and errno pointers as arguments
208  * and uses these for the result.
209  *
210  * @param name the hostname to resolve
211  * @param ret pre-allocated struct where to store the result
212  * @param buf pre-allocated buffer where to store additional data
213  * @param buflen the size of buf
214  * @param result pointer to a hostent pointer that is set to ret on success
215  *               and set to zero on error
216  * @param h_errnop pointer to an int where to store errors (instead of modifying
217  *                 the global h_errno)
218  * @return 0 on success, non-zero on error, additional error information
219  *         is stored in *h_errnop instead of h_errno to be thread-safe
220  */
221 int
lwip_gethostbyname_r(const char * name,struct hostent * ret,char * buf,size_t buflen,struct hostent ** result,int * h_errnop)222 lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
223                      size_t buflen, struct hostent **result, int *h_errnop)
224 {
225   err_t err;
226   struct gethostbyname_r_helper *h;
227   char *hostname;
228   size_t namelen;
229   int lh_errno;
230   u32_t count = DNS_MAX_IPADDR;
231   u32_t i;
232   s32_t addrfamily = 0;
233 
234   if (h_errnop == NULL) {
235     /* ensure h_errnop is never NULL */
236     h_errnop = &lh_errno;
237   }
238 
239   if (result == NULL) {
240     /* not all arguments given */
241     *h_errnop = EINVAL;
242     return -1;
243   }
244   /* first thing to do: set *result to nothing */
245   *result = NULL;
246   if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
247     /* not all arguments given */
248     *h_errnop = EINVAL;
249     return -1;
250   }
251 
252   namelen = strlen(name);
253   if (buflen < (sizeof(struct gethostbyname_r_helper) + LWIP_MEM_ALIGN_BUFFER(namelen + 1))) {
254     /* buf can't hold the data needed + a copy of name */
255     *h_errnop = ERANGE;
256     return -1;
257   }
258 
259   h = (struct gethostbyname_r_helper *)LWIP_MEM_ALIGN(buf);
260   hostname = ((char *)h) + sizeof(struct gethostbyname_r_helper);
261 
262   /* query host IP address */
263   err = netconn_gethostbyname(name, h->addr, &count);
264   if (err != ERR_OK) {
265     LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
266     /* Fix for supporting proper error information in h_errnop */
267     *h_errnop = err;
268     return -1;
269   }
270 
271   /* copy the hostname into buf */
272   MEMCPY(hostname, name, namelen);
273   hostname[namelen] = 0;
274 
275   /* fill hostent */
276   for (i = 0; i < count; i++) {
277     h->addr_list[i] = &h->addr[i];
278   }
279 
280   /* Fix for supporting IPv6 Address Family */
281   if (count > 0) {
282     /* All addresses in s_hostent_addr list will be of same type
283        So, inorder to decide the address family, we need to check the address type of
284        just ony entry  */
285     if (IP_GET_TYPE(h->addr_list[0]) == IPADDR_TYPE_V4) {
286       addrfamily = AF_INET;
287     } else {
288       addrfamily = AF_INET6;
289     }
290   } else {
291     addrfamily = AF_UNSPEC;
292   }
293 
294   h->addr_list[i] = NULL;
295   h->aliases = NULL;
296   ret->h_name = hostname;
297   ret->h_aliases = &h->aliases;
298   ret->h_addrtype = addrfamily;
299   ret->h_length = sizeof(ip_addr_t);
300   ret->h_addr_list = (char**)&(h->addr_list[0]);
301 
302   /* set result != NULL */
303   *result = ret;
304 
305   /* return success */
306   return 0;
307 }
308 
309 /**
310  * Frees one or more addrinfo structures returned by getaddrinfo(), along with
311  * any additional storage associated with those structures. If the ai_next field
312  * of the structure is not null, the entire list of structures is freed.
313  *
314  * @param ai struct addrinfo to free
315  */
316 void
lwip_freeaddrinfo(struct addrinfo * ai)317 lwip_freeaddrinfo(struct addrinfo *ai)
318 {
319   struct addrinfo *next = NULL;
320 
321   while (ai != NULL) {
322     next = ai->ai_next;
323 #if !MEMP_MEM_MALLOC
324     if (memp_check(MEMP_NETDB, ai)) {
325       memp_free(MEMP_NETDB, ai);
326     } else
327 #endif
328     {
329       mem_free(ai);
330     }
331     ai = next;
332   }
333 }
334 
335 /**
336  * Translates the name of a service location (for example, a host name) and/or
337  * a service name and returns a set of socket addresses and associated
338  * information to be used in creating a socket with which to address the
339  * specified service.
340  * Memory for the result is allocated internally and must be freed by calling
341  * lwip_freeaddrinfo()!
342  *
343  * Due to a limitation in dns_gethostbyname, only the first address of a
344  * host is returned.
345  * Also, service names are not supported (only port numbers)!
346  *
347  * @param nodename descriptive name or address string of the host
348  *                 (may be NULL -> local address)
349  * @param servname port number as string of NULL
350  * @param hints structure containing input values that set socktype and protocol
351  * @param res pointer to a pointer where to store the result (set to NULL on failure)
352  * @return 0 on success, non-zero on failure
353  *
354  * @todo: implement AI_V4MAPPED, AI_ADDRCONFIG
355  */
356 int
lwip_getaddrinfo(const char * nodename,const char * servname,const struct addrinfo * hints,struct addrinfo ** res)357 lwip_getaddrinfo(const char *nodename, const char *servname,
358                  const struct addrinfo *hints, struct addrinfo **res)
359 {
360   s32_t err;
361   ip_addr_t addr[DNS_MAX_IPADDR];
362   struct addrinfo *ai = NULL;
363   struct addrinfo *temp = NULL;
364   struct sockaddr_storage *sa = NULL;
365   int port_nr = 0;
366   size_t total_size;
367   size_t namelen = 0;
368   int ai_family;
369   u32_t count = DNS_MAX_IPADDR;
370   u32_t i;
371   u8_t type = 0;
372 
373 #if !LWIP_IPV6
374   LWIP_UNUSED_ARG(type);
375 #endif
376 
377   if (res == NULL) {
378     return EAI_FAIL;
379   }
380   *res = NULL;
381   if ((nodename == NULL) && (servname == NULL)) {
382     return EAI_NONAME;
383   }
384 
385   if (hints != NULL) {
386     ai_family = hints->ai_family;
387     if ((ai_family != AF_UNSPEC)
388 #if LWIP_IPV4
389         && (ai_family != AF_INET)
390 #endif /* LWIP_IPV4 */
391 #if LWIP_IPV6
392         && (ai_family != AF_INET6)
393 #endif /* LWIP_IPV6 */
394        ) {
395       return EAI_FAMILY;
396     }
397   } else {
398     ai_family = AF_UNSPEC;
399   }
400 
401   if (servname != NULL) {
402     /* service name specified: convert to port number
403      * @todo?: currently, only ASCII integers (port numbers) are supported (AI_NUMERICSERV)! */
404     port_nr = atoi(servname);
405     if (port_nr == 0 && (strcmp(servname, "0") != 0)) {
406       /* atoi failed - service was not numeric */
407       return EAI_NONAME;
408     }
409     if ((port_nr < 0) || (port_nr > 0xffff)) {
410       return EAI_NONAME;
411     }
412   }
413 
414   if (nodename != NULL) {
415     /* service location specified, try to resolve */
416     if ((hints != NULL) && (hints->ai_flags & AI_NUMERICHOST)) {
417       /* no DNS lookup, just parse for an address string */
418       if (!ipaddr_aton(nodename, &addr[0])) {
419         return EAI_NONAME;
420       }
421 #if LWIP_IPV4 && LWIP_IPV6
422       if ((IP_IS_V6_VAL(addr[0]) && ai_family == AF_INET) ||
423           (IP_IS_V4_VAL(addr[0]) && ai_family == AF_INET6)) {
424         return EAI_NONAME;
425       }
426       count = 1;
427 #endif /* LWIP_IPV4 && LWIP_IPV6 */
428     } else {
429       namelen = strlen(nodename);
430       if (namelen > DNS_MAX_NAME_LENGTH) {
431         /* invalid name length */
432         LWIP_DEBUGF(DNS_DEBUG, ("nodename is too large\n"));
433         return EAI_FAIL;
434       }
435       /* AF_UNSPEC: prefer DEFAULT configuration */
436       type = NETCONN_DNS_DEFAULT;
437 #if LWIP_IPV4
438       if (ai_family == AF_INET) {
439         type = NETCONN_DNS_IPV4;
440       }
441 #endif /* LWIP_IPV4 */
442 #if LWIP_IPV6
443       if (ai_family == AF_INET6) {
444         type = NETCONN_DNS_IPV6;
445       }
446 #endif /* LWIP_IPV6 */
447       err = netconn_gethostbyname_addrtype(nodename, &addr[0], &count, type);
448       if (err == TRY_AGAIN) {
449         return EAI_AGAIN;
450       } else if (err != ERR_OK) {
451         return EAI_FAIL;
452       }
453     }
454   } else {
455     /* service location specified, use loopback address */
456     if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
457       ip_addr_set_any_val(ai_family == AF_INET6, addr[0]);
458     } else {
459       ip_addr_set_loopback_val(ai_family == AF_INET6, addr[0]);
460     }
461     count = 1;
462   }
463 
464   total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
465   if (nodename != NULL) {
466     namelen = strlen(nodename);
467     if (namelen > DNS_MAX_NAME_LENGTH) {
468       /* invalid name length */
469       return EAI_FAIL;
470     }
471     LWIP_ASSERT("namelen is too long", total_size + namelen + 1 > total_size);
472     total_size += namelen + 1;
473   }
474   /* If this fails, please report to lwip-devel! :-) */
475   LWIP_ERROR("total_size <= NETDB_ELEM_SIZE: please report this!",
476     (total_size <= NETDB_ELEM_SIZE), return EAI_FAIL);
477   for (i = 0; i < count; i++) {
478     ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
479     if (ai == NULL) {
480       /* dynamic malloc */
481       ai = (struct addrinfo *)mem_malloc(total_size);
482       if (ai == NULL) {
483         LWIP_DEBUGF(DNS_DEBUG, ("malloc failed which made %d addrinfo not deliver to user\n", count - i));
484         if (i == 0) {
485           return EAI_MEMORY;
486         } else {
487           return 0;
488         }
489       }
490     }
491     memset(ai, 0, total_size);
492     /* cast through void* to get rid of alignment warnings */
493     sa = (struct sockaddr_storage *)(void *)((u8_t *)ai + sizeof(struct addrinfo));
494     if (IP_IS_V6_VAL(addr[i])) {
495 #if LWIP_IPV6
496       struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)sa;
497       /* set up sockaddr */
498       inet6_addr_from_ip6addr(&sa6->sin6_addr, ip_2_ip6(&addr[i]));
499       sa6->sin6_family = AF_INET6;
500   /* SIN6_LEN macro is to differntiate whether stack is using 4.3BSD or 4.4BSD variants of sockaddr_in6 structure */
501 #if defined(SIN6_LEN)
502       sa6->sin6_len = sizeof(struct sockaddr_in6);
503 #endif
504       sa6->sin6_flowinfo = 0;
505       sa6->sin6_port = lwip_htons((u16_t)port_nr);
506       sa6->sin6_scope_id = ip6_addr_zone(ip_2_ip6(&addr[i]));
507       ai->ai_family = AF_INET6;
508       ai->ai_addrlen = sizeof(struct sockaddr_in6);
509 #endif /* LWIP_IPV6 */
510     } else {
511 #if LWIP_IPV4
512       struct sockaddr_in *sa4 = (struct sockaddr_in *)sa;
513       /* set up sockaddr */
514       inet_addr_from_ip4addr(&sa4->sin_addr, ip_2_ip4(&addr[i]));
515       sa4->sin_family = AF_INET;
516       sa4->sin_port = lwip_htons((u16_t)port_nr);
517       ai->ai_family = AF_INET;
518       ai->ai_addrlen = sizeof(struct sockaddr_in);
519 #endif /* LWIP_IPV4 */
520     }
521 
522     /* set up addrinfo */
523     if (hints != NULL) {
524       /* copy socktype & protocol from hints if specified */
525       ai->ai_socktype = hints->ai_socktype;
526       ai->ai_protocol = hints->ai_protocol;
527     }
528     if (nodename != NULL) {
529       /* copy nodename to canonname if specified */
530       ai->ai_canonname = ((char *)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage));
531       MEMCPY(ai->ai_canonname, nodename, namelen);
532       ai->ai_canonname[namelen] = 0;
533     }
534     ai->ai_addr = (struct sockaddr*)sa;
535 
536     if (*res == NULL) {
537       *res = ai;
538     } else if (temp != NULL) {
539       temp->ai_next = ai;
540     }
541     temp = ai;
542   }
543 
544   return 0;
545 }
546 
547 #if LWIP_DNS_REVERSE
548 static int
lwip_check_nameinfo_args(const struct sockaddr * sa,char * host,socklen_t host_len,int flags,int sa_family)549 lwip_check_nameinfo_args(const struct sockaddr *sa, char *host,
550                          socklen_t host_len, int flags, int sa_family)
551 {
552   unsigned int acceptable_flags;
553   LWIP_ERROR("lwip_getnameinfo: Parameter sa != NULL", (sa != NULL), return EAI_FAIL);
554   if ((sa_family != AF_INET)
555 #if LWIP_IPV6
556     && (sa_family != AF_INET6)
557 #endif /* LWIP_IPV6 */
558     ) {
559     LWIP_DEBUGF(DNS_DEBUG, ("lwip_check_nameinfo_flags: sa_family = %d is invalid", sa_family));
560     return EAI_FAMILY;
561   }
562 
563   /* Atleast one among "host" or "serv" should be requested through this API, otherwise return error
564    * Currently, we don't support SERVICE NAME Reverse-Lookup */
565   if ((host == NULL) || (host_len == 0)) {
566     LWIP_DEBUGF(DNS_DEBUG, ("lwip_check_nameinfo_flags: Invalid parameters"));
567     return EAI_NONAME;
568   }
569 
570   /* Since we are not supporting SERVICE NAME Reverse-Lookup NI_DGRAM & NI_NUMERICSERV are not allowed */
571   acceptable_flags = NI_NAMEREQD | NI_NUMERICHOST;
572   if ((flags <= 0) || ((u32_t)(~acceptable_flags) & (u32_t)flags)) {
573     LWIP_DEBUGF(DNS_DEBUG, ("lwip_check_nameinfo_flags: Invalid \"flags\" value"));
574     return EAI_BADFLAGS;
575   }
576 
577   if ((u32_t)flags & NI_NUMERICHOST) {
578     LWIP_DEBUGF(DNS_DEBUG,
579                 ("lwip_check_nameinfo_flags: directly copying given IP to destination buffer"));
580 #if LWIP_IPV4
581     if ((sa_family == AF_INET) && (host_len < IP4ADDR_STRLEN_MAX)) {
582       LWIP_DEBUGF(DNS_DEBUG, ("lwip_check_nameinfo_flags: The buffer pointed to by \"host\" is too small"));
583       return EAI_OVERFLOW;
584     }
585 #endif /* LWIP_IPV4 */
586 
587 #if LWIP_IPV6
588     if ((sa_family == AF_INET6) && (host_len < INET6_ADDRSTRLEN)) {
589       LWIP_DEBUGF(DNS_DEBUG, ("lwip_getnameinfo: The buffer pointed to by \"host\" is too small"));
590       return EAI_OVERFLOW;
591     }
592 #endif /* LWIP_IPV6 */
593   }
594 
595   return 0;
596 }
597 
598 static int
lwip_nameinfo_convert_to_numeric(const struct sockaddr * sa,char * host,socklen_t host_len,int sa_family)599 lwip_nameinfo_convert_to_numeric(const struct sockaddr *sa, char *host, socklen_t host_len, int sa_family)
600 {
601   switch (sa_family) {
602 #if LWIP_IPV4
603     case AF_INET:
604       host = (char *)(inet_ntop(AF_INET, (&(((struct sockaddr_in *)sa)->sin_addr)), host, host_len));
605       break;
606 #endif /* LWIP_IPV4 */
607 
608 #if LWIP_IPV6
609     case AF_INET6:
610       host = (char *)(inet_ntop(AF_INET6, (&(((struct sockaddr_in6 *)sa)->sin6_addr)), host, host_len));
611       break;
612 #endif /* LWIP_IPV6 */
613     default:
614       break;
615   }
616   if (host == NULL) {
617     return EAI_FAIL;
618   }
619   return 0;
620 }
621 
622 /*
623 * @ingroup netdbapi
624 * @brief   Converts a socket address to a corresponding host and service, in a
625 * protocol-independent manner. It is reentrant and allows programs to eliminate
626 * IPv4-versus-IPv6 dependencies.
627 * Memory for the result is allocated by the caller.
628 *
629 * @param sa  Indicates a pointer to a generic socket address structure
630 *            (of type sockaddr_in or sockaddr_in6) that holds the
631 *            input IP address and port number.
632 * @param salen Indicates the size of the generic socket address structure "sa".
633 * @param host Indicates a pointer to caller-allocated buffer which will holds the null terminated hostname string.
634 * @param hostlen Indicates the size of "host" buffer.
635 * @param serv Indicates a pointer to caller-allocated buffer which will holds the null terminated service-name string.
636 * @param servlen Indicates the size of "serv" buffer.
637 * @param flags Used to modify the behaviour of lwip_getnameinfo() and can have the following values:
638 * - NI_NAMEREQD : If set, then an error is returned if the hostname cannot be determined.
639 * - NI_DGRAM : If set, then the service is datagram (UDP) based rather than stream (TCP) based and causes
640                getservbyport() to be called with a second  argument of "udp" instead of its default of "tcp".
641                This is required for the few ports (512-514) that have different services for UDP and TCP.
642 * - NI_NOFQDN : If set, return only the hostname part of the fully qualified domain name for local hosts.
643 * - NI_NUMERICHOST : If set, then the numeric form of the hostname is returned.
644                      (When not set, this will still happen in case the node's name cannot be determined.)
645 * - NI_NUMERICSERV : If set, then the numeric form of the service address is returned.
646                      (When not set, this will still happen in case the service's name cannot be determined.)
647 * @return
648 * 0: On success \n
649 * Non-zero error number: On failure
650 * EAI_FAIL   : A nonrecoverable error occurred.
651 * EAI_FAMILY : The requested address family is not supported.
652 * EAI_NONAME : The name does not resolve for the supplied arguments.
653                NI_NAMEREQD is set and the host's name cannot be located,
654                or neither hostname nor service name were requested.
655 * EAI_OVERFLOW : The buffer pointed to by host or serv was too small.
656 * EAI_BADFLAGS : The "flags" argument has an invalid value..
657 
658 * @par Note
659 * - No support for translation of service names.\n
660 * - Since there is no support for Service names, the flags - NI_DGRAM, NI_NUMERICSERV is not supported.
661 * - NI_NOFQDN is not currently implemented
662 */
663 
664 /**
665  * @page RFC-2553 RFC-2553
666  * @par RFC-2553 Compliance Information
667  * @par Compliant Section
668  * Section 6.5
669  * @par Compliance Information
670  * The following flag values are supported.
671  * - NI_NAMEREQD: If set, then an error is returned if the hostname cannot be determined. \n
672  *   NI_NUMERICHOST: If set, then the numeric form of the hostname is returned.\n
673  *   When not set, this will still happen in case the node's name cannot be determined.\n
674  * @par Non-Compliance Information
675  *   NI_NOFQDN flag is not supported.\n
676  *   EAI_AGAIN, EAI_MEMORY, EAI_SYSTEM error codes not supported for the API getnameinfo().\n
677  * @par Not Applicable Sections
678  * Since there is no service-name lookup, NI_DGRAM & NI_NUMERICSERV flags are not supported.
679 */
680 int
lwip_getnameinfo(const struct sockaddr * sa,socklen_t sa_len,char * host,socklen_t host_len,char * serv,socklen_t serve_len,int flags)681 lwip_getnameinfo(const struct sockaddr *sa, socklen_t sa_len, char *host,
682                  socklen_t host_len, char *serv, socklen_t serve_len, int flags)
683 {
684   int ret, sa_family;
685   ip_addr_t ip_addr_to_resolve;
686   u16_t service_port;
687   char hostname[NI_MAXHOST] = "";
688 
689   LWIP_UNUSED_ARG(sa_len);
690   /* Currently, we don't support SERVICE NAME Reverse-Lookup */
691   LWIP_UNUSED_ARG(serv);
692   LWIP_UNUSED_ARG(serve_len);
693   sa_family = sa->sa_family;
694   ret = lwip_check_nameinfo_args(sa, host, host_len, flags, sa_family);
695   if (ret != 0) {
696     return ret;
697   }
698 
699   /* If NI_NUMERICHOST is set, then no need to go for resolution,
700     directly copy the given IP to the destination buffer */
701   if ((u32_t)flags & NI_NUMERICHOST) {
702     return lwip_nameinfo_convert_to_numeric(sa, host, host_len, sa_family);
703   }
704 
705   SOCKADDR_TO_IPADDR_PORT(sa, &ip_addr_to_resolve, service_port);
706 
707   if (netconn_getnameinfo(&ip_addr_to_resolve, hostname) != ERR_OK) {
708     if ((u32_t)flags & NI_NAMEREQD) {
709       return EAI_NONAME;
710     } else {
711       return lwip_nameinfo_convert_to_numeric(sa, host, host_len, sa_family);
712     }
713   }
714 
715   if (host_len <= strlen(hostname)) {
716     LWIP_DEBUGF(DNS_DEBUG, ("lwip_getnameinfo: The buffer pointed to by \"host\" is too small"));
717     return EAI_OVERFLOW;
718   }
719   (void)strncpy_s(host, host_len, hostname, strlen(hostname));
720   return 0;
721 }
722 #endif /* LWIP_DNS_REVERSE */
723 
724 #endif /* LWIP_DNS && LWIP_SOCKET */
725