2 * Copyright 1996 by Craig Metz
3 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
5 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
8 /* $USAGI: getaddrinfo.c,v 1.16 2001/10/04 09:52:03 sekiya Exp $ */
10 /* The Inner Net License, Version 2.00
12 The author(s) grant permission for redistribution and use in source and
13 binary forms, with or without modification, of the software and documentation
14 provided that the following conditions are met:
16 0. If you receive a version of the software that is specifically labelled
17 as not being for redistribution (check the version message and/or README),
18 you are not permitted to redistribute that version of the software in any
20 1. All terms of the all other applicable copyrights and licenses must be
22 2. Redistributions of source code must retain the authors' copyright
23 notice(s), this list of conditions, and the following disclaimer.
24 3. Redistributions in binary form must reproduce the authors' copyright
25 notice(s), this list of conditions, and the following disclaimer in the
26 documentation and/or other materials provided with the distribution.
27 4. All advertising materials mentioning features or use of this software
28 must display the following acknowledgement with the name(s) of the
29 authors as specified in the copyright notice(s) substituted where
32 This product includes software developed by <name(s)>, The Inner
33 Net, and other contributors.
35 5. Neither the name(s) of the author(s) nor the names of its contributors
36 may be used to endorse or promote products derived from this software
37 without specific prior written permission.
39 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
40 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
41 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
43 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
44 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
45 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
46 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
47 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
48 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 If these license terms cause you a real problem, contact the author. */
63 #include <arpa/inet.h>
64 #include <sys/socket.h>
65 #include <netinet/in.h>
66 #include <sys/types.h>
68 #include <sys/utsname.h>
71 libc_hidden_proto(memcpy)
72 libc_hidden_proto(memset)
73 /* libc_hidden_proto(strcmp) */
74 /* libc_hidden_proto(stpcpy) */
75 libc_hidden_proto(strchr)
76 libc_hidden_proto(strcpy)
77 libc_hidden_proto(strlen)
78 libc_hidden_proto(socket)
79 libc_hidden_proto(close)
80 libc_hidden_proto(getservbyname_r)
81 libc_hidden_proto(gethostbyname_r)
82 libc_hidden_proto(gethostbyname2_r)
83 libc_hidden_proto(gethostbyaddr_r)
84 libc_hidden_proto(inet_pton)
85 libc_hidden_proto(inet_ntop)
86 libc_hidden_proto(strtoul)
87 libc_hidden_proto(if_nametoindex)
88 libc_hidden_proto(__h_errno_location)
89 /* libc_hidden_proto(uname) */
90 #ifdef __UCLIBC_HAS_IPV6__
91 libc_hidden_proto(in6addr_loopback)
94 /* The following declarations and definitions have been removed from
95 * the public header since we don't want people to use them. */
96 #define AI_V4MAPPED 0x0008 /* IPv4-mapped addresses are acceptable. */
97 #define AI_ALL 0x0010 /* Return both IPv4 and IPv6 addresses. */
98 #define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose
99 returned address type. */
100 #define AI_DEFAULT (AI_V4MAPPED | AI_ADDRCONFIG)
103 #define GAIH_OKIFUNSPEC 0x0100
104 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
106 #ifndef UNIX_PATH_MAX
107 #define UNIX_PATH_MAX 108
116 struct gaih_servtuple
118 struct gaih_servtuple *next;
124 static const struct gaih_servtuple nullserv;
126 struct gaih_addrtuple
128 struct gaih_addrtuple *next;
134 struct gaih_typeproto
142 /* Values for `protoflag'. */
143 #define GAI_PROTO_NOSERVICE 1
144 #define GAI_PROTO_PROTOANY 2
146 static const struct gaih_typeproto gaih_inet_typeproto[] =
149 { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
150 { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
151 { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
158 int (*gaih)(const char *name, const struct gaih_service *service,
159 const struct addrinfo *req, struct addrinfo **pai);
163 static const struct addrinfo default_hints;
165 static const struct addrinfo default_hints =
166 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
170 static int addrconfig (sa_family_t af)
174 int saved_errno = errno;
175 s = socket(af, SOCK_DGRAM, 0);
177 ret = (errno == EMFILE) ? 1 : 0;
183 __set_errno (saved_errno);
188 /* Using Unix sockets this way is a security risk. */
190 gaih_local (const char *name, const struct gaih_service *service,
191 const struct addrinfo *req, struct addrinfo **pai)
193 struct utsname utsname;
195 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
196 return GAIH_OKIFUNSPEC | -EAI_NONAME;
198 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
199 if (uname (&utsname) < 0)
204 if (strcmp(name, "localhost") &&
205 strcmp(name, "local") &&
206 strcmp(name, "unix") &&
207 strcmp(name, utsname.nodename))
208 return GAIH_OKIFUNSPEC | -EAI_NONAME;
211 if (req->ai_protocol || req->ai_socktype)
213 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
216 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
217 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
218 || (req->ai_protocol != 0
219 && !(tp->protoflag & GAI_PROTO_PROTOANY)
220 && req->ai_protocol != tp->protocol)))
225 if (req->ai_socktype)
226 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
228 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
232 *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
233 + ((req->ai_flags & AI_CANONNAME)
234 ? (strlen(utsname.nodename) + 1): 0));
238 (*pai)->ai_next = NULL;
239 (*pai)->ai_flags = req->ai_flags;
240 (*pai)->ai_family = AF_LOCAL;
241 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
242 (*pai)->ai_protocol = req->ai_protocol;
243 (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
244 (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
247 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
248 sizeof (struct sockaddr_un);
251 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
252 memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
256 struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
258 if (strchr (service->name, '/') != NULL)
260 if (strlen (service->name) >= sizeof (sunp->sun_path))
261 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
263 strcpy (sunp->sun_path, service->name);
267 if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
268 sizeof (sunp->sun_path))
269 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
271 stpcpy (stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
276 /* This is a dangerous use of the interface since there is a time
277 window between the test for the file and the actual creation
278 (done by the caller) in which a file with the same name could
280 char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
282 if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
284 || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
288 if (req->ai_flags & AI_CANONNAME)
289 (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
290 + sizeof (struct sockaddr_un),
293 (*pai)->ai_canonname = NULL;
299 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
300 const struct addrinfo *req, struct gaih_servtuple *st)
303 size_t tmpbuflen = 1024;
310 tmpbuf = alloca (tmpbuflen);
312 r = getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
314 if (r != 0 || s == NULL)
319 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
325 st->socktype = tp->socktype;
326 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
327 ? req->ai_protocol : tp->protocol);
328 st->port = s->s_port;
333 #define gethosts(_family, _type) \
343 tmpbuf = alloca (tmpbuflen); \
344 rc = gethostbyname2_r (name, _family, &th, tmpbuf, \
345 tmpbuflen, &h, &herrno); \
346 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
349 if (herrno == NETDB_INTERNAL) \
351 __set_h_errno (herrno); \
352 return -EAI_SYSTEM; \
354 if (herrno == TRY_AGAIN) \
355 no_data = EAI_AGAIN; \
357 no_data = herrno == NO_DATA; \
359 else if (h != NULL) \
361 for (i = 0; h->h_addr_list[i]; i++) \
363 if (*pat == NULL) { \
364 *pat = alloca (sizeof(struct gaih_addrtuple)); \
365 (*pat)->scopeid = 0; \
367 (*pat)->next = NULL; \
368 (*pat)->family = _family; \
369 memcpy ((*pat)->addr, h->h_addr_list[i], \
371 pat = &((*pat)->next); \
377 gaih_inet (const char *name, const struct gaih_service *service,
378 const struct addrinfo *req, struct addrinfo **pai)
380 const struct gaih_typeproto *tp = gaih_inet_typeproto;
381 struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
382 struct gaih_addrtuple *at = NULL;
384 int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
385 (req->ai_flags & AI_V4MAPPED);
387 if (req->ai_protocol || req->ai_socktype)
392 && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
393 || (req->ai_protocol != 0
394 && !(tp->protoflag & GAI_PROTO_PROTOANY)
395 && req->ai_protocol != tp->protocol)))
400 if (req->ai_socktype)
401 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
403 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
409 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
410 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
412 if (service->num < 0)
416 st = (struct gaih_servtuple *)
417 alloca (sizeof (struct gaih_servtuple));
419 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
424 struct gaih_servtuple **pst = &st;
425 for (tp++; tp->name[0]; tp++)
427 struct gaih_servtuple *newp;
429 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
432 if (req->ai_socktype != 0
433 && req->ai_socktype != tp->socktype)
435 if (req->ai_protocol != 0
436 && !(tp->protoflag & GAI_PROTO_PROTOANY)
437 && req->ai_protocol != tp->protocol)
440 newp = (struct gaih_servtuple *)
441 alloca (sizeof (struct gaih_servtuple));
443 if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
445 if (rc & GAIH_OKIFUNSPEC)
453 if (st == (struct gaih_servtuple *) &nullserv)
454 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
459 st = alloca (sizeof (struct gaih_servtuple));
461 st->socktype = tp->socktype;
462 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
463 ? req->ai_protocol : tp->protocol);
464 st->port = htons (service->num);
467 else if (req->ai_socktype || req->ai_protocol)
469 st = alloca (sizeof (struct gaih_servtuple));
471 st->socktype = tp->socktype;
472 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
473 ? req->ai_protocol : tp->protocol);
479 * Neither socket type nor protocol is set. Return all socket types
482 struct gaih_servtuple **lastp = &st;
483 for (++tp; tp->name[0]; ++tp)
485 struct gaih_servtuple *newp;
487 newp = alloca (sizeof (struct gaih_servtuple));
489 newp->socktype = tp->socktype;
490 newp->protocol = tp->protocol;
500 at = alloca (sizeof (struct gaih_addrtuple));
502 at->family = AF_UNSPEC;
506 if (inet_pton (AF_INET, name, at->addr) > 0)
508 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET || v4mapped)
509 at->family = AF_INET;
514 #if __UCLIBC_HAS_IPV6__
515 if (at->family == AF_UNSPEC)
517 char *namebuf = strdupa (name);
520 scope_delim = strchr (namebuf, SCOPE_DELIMITER);
521 if (scope_delim != NULL)
524 if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
526 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
527 at->family = AF_INET6;
531 if (scope_delim != NULL)
533 int try_numericscope = 0;
534 if (IN6_IS_ADDR_LINKLOCAL (at->addr)
535 || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
537 at->scopeid = if_nametoindex (scope_delim + 1);
538 if (at->scopeid == 0)
539 try_numericscope = 1;
542 try_numericscope = 1;
544 if (try_numericscope != 0)
547 assert (sizeof (uint32_t) <= sizeof (unsigned long));
548 at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
551 return GAIH_OKIFUNSPEC | -EAI_NONAME;
558 if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
561 struct gaih_addrtuple **pat = &at;
566 * If we are looking for both IPv4 and IPv6 address we don't want
567 * the lookup functions to automatically promote IPv4 addresses to
571 #if __UCLIBC_HAS_IPV6__
572 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
573 gethosts (AF_INET6, struct in6_addr);
575 no_inet6_data = no_data;
577 if (req->ai_family == AF_INET ||
578 (!v4mapped && req->ai_family == AF_UNSPEC) ||
579 (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))))
580 gethosts (AF_INET, struct in_addr);
582 if (no_data != 0 && no_inet6_data != 0)
584 /* If both requests timed out report this. */
585 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
589 * We made requests but they turned out no data.
590 * The name is known, though.
592 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
596 if (at->family == AF_UNSPEC)
597 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
601 struct gaih_addrtuple *atr;
602 atr = at = alloca (sizeof (struct gaih_addrtuple));
603 memset (at, '\0', sizeof (struct gaih_addrtuple));
605 if (req->ai_family == 0)
607 at->next = alloca (sizeof (struct gaih_addrtuple));
608 memset (at->next, '\0', sizeof (struct gaih_addrtuple));
611 #if __UCLIBC_HAS_IPV6__
612 if (req->ai_family == 0 || req->ai_family == AF_INET6)
614 at->family = AF_INET6;
615 if ((req->ai_flags & AI_PASSIVE) == 0)
616 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
621 if (req->ai_family == 0 || req->ai_family == AF_INET)
623 atr->family = AF_INET;
624 if ((req->ai_flags & AI_PASSIVE) == 0)
625 *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
633 const char *c = NULL;
634 struct gaih_servtuple *st2;
635 struct gaih_addrtuple *at2 = at;
636 size_t socklen, namelen;
640 * buffer is the size of an unformatted IPv6 address in
643 char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
647 if (req->ai_flags & AI_CANONNAME)
649 struct hostent *h = NULL;
653 size_t tmpbuflen = 512;
659 tmpbuf = alloca (tmpbuflen);
664 rc = gethostbyaddr_r (at2->addr,
665 ((at2->family == AF_INET6)
666 ? sizeof(struct in6_addr)
667 : sizeof(struct in_addr)),
668 at2->family, &th, tmpbuf, tmpbuflen,
672 while (rc == errno && herrno == NETDB_INTERNAL);
674 if (rc != 0 && herrno == NETDB_INTERNAL)
676 __set_h_errno (herrno);
681 c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
686 return GAIH_OKIFUNSPEC | -EAI_NONAME;
688 namelen = strlen (c) + 1;
693 #if __UCLIBC_HAS_IPV6__
694 if (at2->family == AF_INET6 || v4mapped)
697 socklen = sizeof (struct sockaddr_in6);
703 socklen = sizeof (struct sockaddr_in);
706 for (st2 = st; st2 != NULL; st2 = st2->next)
708 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
712 (*pai)->ai_flags = req->ai_flags;
713 (*pai)->ai_family = family;
714 (*pai)->ai_socktype = st2->socktype;
715 (*pai)->ai_protocol = st2->protocol;
716 (*pai)->ai_addrlen = socklen;
717 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
719 (*pai)->ai_addr->sa_len = socklen;
721 (*pai)->ai_addr->sa_family = family;
723 #if __UCLIBC_HAS_IPV6__
724 if (family == AF_INET6)
726 struct sockaddr_in6 *sin6p =
727 (struct sockaddr_in6 *) (*pai)->ai_addr;
729 sin6p->sin6_flowinfo = 0;
730 if (at2->family == AF_INET6)
732 memcpy (&sin6p->sin6_addr,
733 at2->addr, sizeof (struct in6_addr));
737 sin6p->sin6_addr.s6_addr32[0] = 0;
738 sin6p->sin6_addr.s6_addr32[1] = 0;
739 sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
740 memcpy(&sin6p->sin6_addr.s6_addr32[3],
741 at2->addr, sizeof (sin6p->sin6_addr.s6_addr32[3]));
743 sin6p->sin6_port = st2->port;
744 sin6p->sin6_scope_id = at2->scopeid;
749 struct sockaddr_in *sinp =
750 (struct sockaddr_in *) (*pai)->ai_addr;
752 memcpy (&sinp->sin_addr,
753 at2->addr, sizeof (struct in_addr));
754 sinp->sin_port = st2->port;
755 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
760 (*pai)->ai_canonname = ((void *) (*pai) +
761 sizeof (struct addrinfo) + socklen);
762 strcpy ((*pai)->ai_canonname, c);
765 (*pai)->ai_canonname = NULL;
767 (*pai)->ai_next = NULL;
768 pai = &((*pai)->ai_next);
777 static struct gaih gaih[] =
779 #if __UCLIBC_HAS_IPV6__
780 { PF_INET6, gaih_inet },
782 { PF_INET, gaih_inet },
784 { PF_LOCAL, gaih_local },
789 libc_hidden_proto(freeaddrinfo)
791 freeaddrinfo (struct addrinfo *ai)
802 libc_hidden_def(freeaddrinfo)
804 libc_hidden_proto(getaddrinfo)
806 getaddrinfo (const char *name, const char *service,
807 const struct addrinfo *hints, struct addrinfo **pai)
809 int i = 0, j = 0, last_i = 0;
810 struct addrinfo *p = NULL, **end;
811 struct gaih *g = gaih, *pg = NULL;
812 struct gaih_service gaih_service, *pservice;
814 if (name != NULL && name[0] == '*' && name[1] == 0)
817 if (service != NULL && service[0] == '*' && service[1] == 0)
820 if (name == NULL && service == NULL)
824 hints = &default_hints;
826 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
827 AI_ADDRCONFIG|AI_V4MAPPED|AI_ALL))
830 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
833 if (service && service[0])
836 gaih_service.name = service;
837 gaih_service.num = strtoul (gaih_service.name, &c, 10);
839 gaih_service.num = -1;
842 * Can't specify a numerical socket unless a protocol
845 if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
847 pservice = &gaih_service;
859 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
861 if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family))
864 if (pg == NULL || pg->gaih != g->gaih)
867 i = g->gaih (name, pservice, hints, end);
872 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
878 return -(i & GAIH_EAI);
881 while(*end) end = &((*end)->ai_next);
896 if (pai == NULL && last_i == 0)
902 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
904 libc_hidden_def(getaddrinfo)