2 * Copyright 1996 by Craig Metz
3 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
4 * Portions from the GNU C library,
5 * Copyright (C) 2003, 2006 Free Software Foundation, Inc.
7 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
10 /* $USAGI: getaddrinfo.c,v 1.16 2001/10/04 09:52:03 sekiya Exp $ */
12 /* The Inner Net License, Version 2.00
14 The author(s) grant permission for redistribution and use in source and
15 binary forms, with or without modification, of the software and documentation
16 provided that the following conditions are met:
18 0. If you receive a version of the software that is specifically labelled
19 as not being for redistribution (check the version message and/or README),
20 you are not permitted to redistribute that version of the software in any
22 1. All terms of the all other applicable copyrights and licenses must be
24 2. Redistributions of source code must retain the authors' copyright
25 notice(s), this list of conditions, and the following disclaimer.
26 3. Redistributions in binary form must reproduce the authors' copyright
27 notice(s), this list of conditions, and the following disclaimer in the
28 documentation and/or other materials provided with the distribution.
29 4. All advertising materials mentioning features or use of this software
30 must display the following acknowledgement with the name(s) of the
31 authors as specified in the copyright notice(s) substituted where
34 This product includes software developed by <name(s)>, The Inner
35 Net, and other contributors.
37 5. Neither the name(s) of the author(s) nor the names of its contributors
38 may be used to endorse or promote products derived from this software
39 without specific prior written permission.
41 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
42 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
43 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
45 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
46 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
48 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 If these license terms cause you a real problem, contact the author. */
59 #ifdef __UCLIBC_HAS_TLS__
67 #include <arpa/inet.h>
68 #include <sys/socket.h>
69 #include <netinet/in.h>
70 #include <sys/types.h>
72 #include <sys/utsname.h>
76 #define GAIH_OKIFUNSPEC 0x0100
77 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
80 #define UNIX_PATH_MAX 108
83 /* Useful for having small structure members/global variables */
84 typedef int8_t socktype_t;
85 typedef int8_t family_t;
86 typedef int8_t protocol_t;
87 struct BUG_too_small {
88 char BUG_socktype_t_too_small[(0
93 char BUG_family_t_too_small[(0
98 char BUG_protocol_t_too_small[(0
104 struct gaih_service {
109 struct gaih_servtuple {
110 struct gaih_servtuple *next;
116 struct gaih_addrtuple {
117 struct gaih_addrtuple *next;
123 struct gaih_typeproto {
129 /* Values for `protoflag'. */
130 #define GAI_PROTO_NOSERVICE 1
131 #define GAI_PROTO_PROTOANY 2
133 static const struct gaih_typeproto gaih_inet_typeproto[] = {
135 { SOCK_STREAM, IPPROTO_TCP, 0, "tcp" },
136 { SOCK_DGRAM , IPPROTO_UDP, 0, "udp" },
137 { SOCK_RAW , 0 , GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, "raw" },
143 int (*gaih)(const char *name, const struct gaih_service *service,
144 const struct addrinfo *req, struct addrinfo **pai);
150 static unsigned __check_pf(void)
154 #if defined __UCLIBC_SUPPORT_AI_ADDRCONFIG__
157 struct ifaddrs *runp;
159 /* Get the interface list via getifaddrs. */
160 if (getifaddrs(&ifa) != 0) {
161 /* We cannot determine what interfaces are available.
163 #if defined __UCLIBC_HAS_IPV4__
166 #if defined __UCLIBC_HAS_IPV6__
172 for (runp = ifa; runp != NULL; runp = runp->ifa_next) {
173 if (runp->ifa_addr == NULL)
175 #if defined __UCLIBC_HAS_IPV4__
176 if (runp->ifa_addr->sa_family == PF_INET)
179 #if defined __UCLIBC_HAS_IPV6__
180 if (runp->ifa_addr->sa_family == PF_INET6)
188 /* AI_ADDRCONFIG is disabled, assume both ipv4 and ipv6 available. */
189 #if defined __UCLIBC_HAS_IPV4__
192 #if defined __UCLIBC_HAS_IPV6__
196 #endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */
201 static int addrconfig(sa_family_t af)
205 int saved_errno = errno;
209 #if defined __UCLIBC_HAS_IPV4__
211 ret = seen & SEEN_IPV4;
214 #if defined __UCLIBC_HAS_IPV6__
216 ret = seen & SEEN_IPV6;
220 s = socket(af, SOCK_DGRAM, 0);
221 ret = 1; /* Assume PF_UNIX. */
228 __set_errno(saved_errno);
233 /* Using Unix sockets this way is a security risk. */
235 gaih_local(const char *name, const struct gaih_service *service,
236 const struct addrinfo *req, struct addrinfo **pai)
238 struct utsname utsname;
239 struct addrinfo *ai = *pai;
241 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
242 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
244 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
245 if (uname(&utsname) < 0)
249 if (strcmp(name, "localhost") &&
250 strcmp(name, "local") &&
251 strcmp(name, "unix") &&
252 strcmp(name, utsname.nodename))
253 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
256 if (req->ai_protocol || req->ai_socktype) {
257 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
260 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
261 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
262 || (req->ai_protocol != 0 && !(tp->protoflag & GAI_PROTO_PROTOANY) && req->ai_protocol != tp->protocol))
267 if (req->ai_socktype)
268 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
269 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
273 *pai = ai = malloc(sizeof(struct addrinfo) + sizeof(struct sockaddr_un)
274 + ((req->ai_flags & AI_CANONNAME)
275 ? (strlen(utsname.nodename) + 1) : 0));
280 ai->ai_flags = req->ai_flags;
281 ai->ai_family = AF_LOCAL;
282 ai->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
283 ai->ai_protocol = req->ai_protocol;
284 ai->ai_addrlen = sizeof(struct sockaddr_un);
285 ai->ai_addr = (void *)ai + sizeof(struct addrinfo);
287 ((struct sockaddr_un *)ai->ai_addr)->sun_len = sizeof(struct sockaddr_un);
290 ((struct sockaddr_un *)ai->ai_addr)->sun_family = AF_LOCAL;
291 memset(((struct sockaddr_un *)ai->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
294 struct sockaddr_un *sunp = (struct sockaddr_un *)ai->ai_addr;
296 if (strchr(service->name, '/') != NULL) {
297 if (strlen(service->name) >= sizeof(sunp->sun_path))
298 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
299 strcpy(sunp->sun_path, service->name);
301 if (strlen(P_tmpdir "/") + 1 + strlen(service->name) >= sizeof(sunp->sun_path))
302 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
303 stpcpy(stpcpy(sunp->sun_path, P_tmpdir "/"), service->name);
306 /* This is a dangerous use of the interface since there is a time
307 window between the test for the file and the actual creation
308 (done by the caller) in which a file with the same name could
310 char *buf = ((struct sockaddr_un *)ai->ai_addr)->sun_path;
312 if (__path_search(buf, L_tmpnam, NULL, NULL, 0) != 0
313 || __gen_tempname(buf, __GT_NOCREATE, 0) != 0
319 ai->ai_canonname = NULL;
320 if (req->ai_flags & AI_CANONNAME)
321 ai->ai_canonname = strcpy((char *)(ai + 1) + sizeof(struct sockaddr_un),
328 gaih_inet_serv(const char *servicename, const struct gaih_typeproto *tp,
329 const struct addrinfo *req, struct gaih_servtuple *st)
332 size_t tmpbuflen = 1024;
338 tmpbuf = alloca(tmpbuflen);
339 r = getservbyname_r(servicename, tp->name, &ts, tmpbuf, tmpbuflen, &s);
340 if (r == 0 && s != NULL)
343 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
347 st->socktype = tp->socktype;
348 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) ? req->ai_protocol : tp->protocol);
349 st->port = s->s_port;
353 /* NB: also uses h,pat,rc,no_data variables */
354 #define gethosts(_family, _type) \
365 tmpbuf = alloca(tmpbuflen); \
366 rc = gethostbyname2_r(name, _family, &th, tmpbuf, \
367 tmpbuflen, &h, &herrno); \
368 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
370 if (herrno == NETDB_INTERNAL) { \
371 __set_h_errno(herrno); \
372 return -EAI_SYSTEM; \
374 if (herrno == TRY_AGAIN) \
375 no_data = EAI_AGAIN; \
377 no_data = (herrno == NO_DATA); \
378 } else if (h != NULL) { \
379 for (i = 0; h->h_addr_list[i]; i++) { \
380 if (*pat == NULL) { \
381 *pat = alloca(sizeof(struct gaih_addrtuple)); \
382 (*pat)->scopeid = 0; \
384 (*pat)->next = NULL; \
385 (*pat)->family = _family; \
386 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(_type)); \
387 pat = &((*pat)->next); \
393 gaih_inet(const char *name, const struct gaih_service *service,
394 const struct addrinfo *req, struct addrinfo **pai)
396 struct gaih_servtuple nullserv;
398 const struct gaih_typeproto *tp;
399 struct gaih_servtuple *st;
400 struct gaih_addrtuple *at;
402 int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6)
403 && (req->ai_flags & AI_V4MAPPED);
405 if (req->ai_flags & AI_ADDRCONFIG) {
406 /* "seen" is only used when AI_ADDRCONFIG is specified.
407 Avoid unnecessary call to __check_pf() otherwise
408 since it can be costly especially when RSBAC-Net is enabled. */
412 memset(&nullserv, 0, sizeof(nullserv));
414 tp = gaih_inet_typeproto;
415 if (req->ai_protocol || req->ai_socktype) {
417 while (tp->name[0]) {
418 if ((req->ai_socktype == 0 || req->ai_socktype == tp->socktype)
419 && (req->ai_protocol == 0 || req->ai_protocol == tp->protocol || (tp->protoflag & GAI_PROTO_PROTOANY))
425 if (req->ai_socktype)
426 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
427 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
432 if (service != NULL) {
433 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
434 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
436 if (service->num < 0) {
438 st = alloca(sizeof(struct gaih_servtuple));
439 rc = gaih_inet_serv(service->name, tp, req, st);
443 struct gaih_servtuple **pst = &st;
444 for (tp++; tp->name[0]; tp++) {
445 struct gaih_servtuple *newp;
447 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
450 if (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
452 if (req->ai_protocol != 0
453 && !(tp->protoflag & GAI_PROTO_PROTOANY)
454 && req->ai_protocol != tp->protocol)
457 newp = alloca(sizeof(struct gaih_servtuple));
458 rc = gaih_inet_serv(service->name, tp, req, newp);
460 if (rc & GAIH_OKIFUNSPEC)
469 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
472 st = alloca(sizeof(struct gaih_servtuple));
474 st->socktype = tp->socktype;
475 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
476 ? req->ai_protocol : tp->protocol);
477 st->port = htons(service->num);
479 } else if (req->ai_socktype || req->ai_protocol) {
480 st = alloca(sizeof(struct gaih_servtuple));
482 st->socktype = tp->socktype;
483 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
484 ? req->ai_protocol : tp->protocol);
488 * Neither socket type nor protocol is set. Return all socket types
491 struct gaih_servtuple **lastp = &st;
492 for (++tp; tp->name[0]; ++tp) {
493 struct gaih_servtuple *newp;
495 newp = alloca(sizeof(struct gaih_servtuple));
497 newp->socktype = tp->socktype;
498 newp->protocol = tp->protocol;
508 at = alloca(sizeof(struct gaih_addrtuple));
509 at->family = AF_UNSPEC;
513 if (inet_pton(AF_INET, name, at->addr) > 0) {
514 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET && !v4mapped)
516 at->family = AF_INET;
519 #if defined __UCLIBC_HAS_IPV6__
520 if (at->family == AF_UNSPEC) {
521 char *namebuf = strdupa(name);
524 scope_delim = strchr(namebuf, SCOPE_DELIMITER);
525 if (scope_delim != NULL)
528 if (inet_pton(AF_INET6, namebuf, at->addr) > 0) {
529 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET6)
531 at->family = AF_INET6;
532 if (scope_delim != NULL) {
533 int try_numericscope = 0;
534 uint32_t *a32 = (uint32_t*)at->addr;
535 if (IN6_IS_ADDR_LINKLOCAL(a32) || IN6_IS_ADDR_MC_LINKLOCAL(at->addr)) {
536 at->scopeid = if_nametoindex(scope_delim + 1);
537 if (at->scopeid == 0)
538 try_numericscope = 1;
540 try_numericscope = 1;
542 if (try_numericscope != 0) {
544 assert(sizeof(uint32_t) <= sizeof(unsigned long));
545 at->scopeid = (uint32_t)strtoul(scope_delim + 1, &end, 10);
547 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
554 if (at->family == AF_UNSPEC && !(req->ai_flags & AI_NUMERICHOST)) {
556 struct gaih_addrtuple **pat = &at;
561 * If we are looking for both IPv4 and IPv6 address we don't want
562 * the lookup functions to automatically promote IPv4 addresses to
565 #if defined __UCLIBC_HAS_IPV6__
566 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
567 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV6))
568 gethosts(AF_INET6, struct in6_addr);
570 no_inet6_data = no_data;
572 if (req->ai_family == AF_INET
573 || (!v4mapped && req->ai_family == AF_UNSPEC)
574 || (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL)))
576 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV4))
577 gethosts(AF_INET, struct in_addr);
580 if (no_data != 0 && no_inet6_data != 0) {
581 /* If both requests timed out report this. */
582 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
585 * We made requests but they turned out no data.
586 * The name is known, though.
588 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
592 if (at->family == AF_UNSPEC)
593 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
595 struct gaih_addrtuple *atr;
597 atr = at = alloca(sizeof(struct gaih_addrtuple));
598 memset(at, '\0', sizeof(struct gaih_addrtuple));
599 if (req->ai_family == 0) {
600 at->next = alloca(sizeof(struct gaih_addrtuple));
601 memset(at->next, '\0', sizeof(struct gaih_addrtuple));
603 #if defined __UCLIBC_HAS_IPV6__
604 if (req->ai_family == 0 || req->ai_family == AF_INET6) {
605 at->family = AF_INET6;
606 if ((req->ai_flags & AI_PASSIVE) == 0)
607 memcpy(at->addr, &in6addr_loopback, sizeof(struct in6_addr));
611 if (req->ai_family == 0 || req->ai_family == AF_INET) {
612 atr->family = AF_INET;
613 if ((req->ai_flags & AI_PASSIVE) == 0) {
614 uint32_t *a = (uint32_t*)atr->addr;
615 *a = htonl(INADDR_LOOPBACK);
624 const char *c = NULL;
625 struct gaih_servtuple *st2;
626 struct gaih_addrtuple *at2 = at;
627 size_t socklen, namelen;
631 * buffer is the size of an unformatted IPv6 address in
634 char buffer[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
636 while (at2 != NULL) {
637 c = inet_ntop(at2->family, at2->addr, buffer, sizeof(buffer));
639 namelen = strlen(c) + 1;
640 } else if (req->ai_flags & AI_CANONNAME) {
641 struct hostent *h = NULL;
644 size_t tmpbuflen = 512;
647 /* Hint says numeric, but address is not */
648 if (req->ai_flags & AI_NUMERICHOST)
653 tmpbuf = alloca(tmpbuflen);
654 rc = gethostbyaddr_r(at2->addr,
655 #ifdef __UCLIBC_HAS_IPV6__
656 ((at2->family == AF_INET6)
657 ? sizeof(struct in6_addr)
658 : sizeof(struct in_addr)),
660 sizeof(struct in_addr),
663 &th, tmpbuf, tmpbuflen,
665 } while (rc == ERANGE && herrno == NETDB_INTERNAL);
667 if (rc != 0 && herrno == NETDB_INTERNAL) {
668 __set_h_errno(herrno);
676 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
678 namelen = strlen(c) + 1;
682 #if defined __UCLIBC_HAS_IPV6__
683 if (at2->family == AF_INET6 || v4mapped) {
685 socklen = sizeof(struct sockaddr_in6);
688 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
691 #if defined __UCLIBC_HAS_IPV4__
694 socklen = sizeof(struct sockaddr_in);
697 for (st2 = st; st2 != NULL; st2 = st2->next) {
698 if (req->ai_flags & AI_ADDRCONFIG) {
699 if (family == AF_INET && !(seen & SEEN_IPV4))
701 #if defined __UCLIBC_HAS_IPV6__
702 else if (family == AF_INET6 && !(seen & SEEN_IPV6))
706 *pai = malloc(sizeof(struct addrinfo) + socklen + namelen);
710 (*pai)->ai_flags = req->ai_flags;
711 (*pai)->ai_family = family;
712 (*pai)->ai_socktype = st2->socktype;
713 (*pai)->ai_protocol = st2->protocol;
714 (*pai)->ai_addrlen = socklen;
715 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
717 (*pai)->ai_addr->sa_len = socklen;
719 (*pai)->ai_addr->sa_family = family;
721 #if defined __UCLIBC_HAS_IPV6__
722 if (family == AF_INET6) {
723 struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) (*pai)->ai_addr;
725 sin6p->sin6_flowinfo = 0;
726 if (at2->family == AF_INET6) {
727 memcpy(&sin6p->sin6_addr,
728 at2->addr, sizeof(struct in6_addr));
730 sin6p->sin6_addr.s6_addr32[0] = 0;
731 sin6p->sin6_addr.s6_addr32[1] = 0;
732 sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
733 memcpy(&sin6p->sin6_addr.s6_addr32[3],
734 at2->addr, sizeof(sin6p->sin6_addr.s6_addr32[3]));
736 sin6p->sin6_port = st2->port;
737 sin6p->sin6_scope_id = at2->scopeid;
740 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
743 #if defined __UCLIBC_HAS_IPV4__
745 struct sockaddr_in *sinp = (struct sockaddr_in *) (*pai)->ai_addr;
747 memcpy(&sinp->sin_addr, at2->addr, sizeof(struct in_addr));
748 sinp->sin_port = st2->port;
749 memset(sinp->sin_zero, '\0', sizeof(sinp->sin_zero));
753 (*pai)->ai_canonname = ((void *) (*pai) +
754 sizeof(struct addrinfo) + socklen);
755 strcpy((*pai)->ai_canonname, c);
757 (*pai)->ai_canonname = NULL;
759 (*pai)->ai_next = NULL;
760 pai = &((*pai)->ai_next);
769 static const struct gaih gaih[] = {
770 #if defined __UCLIBC_HAS_IPV6__
771 { PF_INET6, gaih_inet },
773 { PF_INET, gaih_inet },
775 { PF_LOCAL, gaih_local },
781 freeaddrinfo(struct addrinfo *ai)
791 libc_hidden_def(freeaddrinfo)
794 getaddrinfo(const char *name, const char *service,
795 const struct addrinfo *hints, struct addrinfo **pai)
798 struct addrinfo *p, **end;
799 const struct gaih *g, *pg;
800 struct gaih_service gaih_service, *pservice;
801 struct addrinfo default_hints;
803 if (name != NULL && name[0] == '*' && name[1] == 0)
806 if (service != NULL && service[0] == '*' && service[1] == 0)
809 if (name == NULL && service == NULL)
813 memset(&default_hints, 0, sizeof(default_hints));
815 default_hints.ai_family = AF_UNSPEC;
816 hints = &default_hints;
819 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
820 AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
823 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
826 if (service && service[0]) {
828 gaih_service.name = service;
829 gaih_service.num = strtoul(gaih_service.name, &c, 10);
831 if (hints->ai_flags & AI_NUMERICSERV)
833 gaih_service.num = -1;
835 pservice = &gaih_service;
849 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) {
850 if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) {
855 if (pg == NULL || pg->gaih != g->gaih) {
857 i = g->gaih(name, pservice, hints, end);
860 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
862 /*if (p) - freeaddrinfo works ok on NULL too */
864 return -(i & GAIH_EAI);
868 end = &((*end)->ai_next);
882 if (pai == NULL && last_i == 0)
885 /* if (p) - never happens, see above */
886 /* freeaddrinfo(p); */
888 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
890 libc_hidden_def(getaddrinfo)