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