1 /* $USAGI: getaddrinfo.c,v 1.16 2001/10/04 09:52:03 sekiya Exp $ */
3 /* The Inner Net License, Version 2.00
5 The author(s) grant permission for redistribution and use in source and
6 binary forms, with or without modification, of the software and documentation
7 provided that the following conditions are met:
9 0. If you receive a version of the software that is specifically labelled
10 as not being for redistribution (check the version message and/or README),
11 you are not permitted to redistribute that version of the software in any
13 1. All terms of the all other applicable copyrights and licenses must be
15 2. Redistributions of source code must retain the authors' copyright
16 notice(s), this list of conditions, and the following disclaimer.
17 3. Redistributions in binary form must reproduce the authors' copyright
18 notice(s), this list of conditions, and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 4. All advertising materials mentioning features or use of this software
21 must display the following acknowledgement with the name(s) of the
22 authors as specified in the copyright notice(s) substituted where
25 This product includes software developed by <name(s)>, The Inner
26 Net, and other contributors.
28 5. Neither the name(s) of the author(s) nor the names of its contributors
29 may be used to endorse or promote products derived from this software
30 without specific prior written permission.
32 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
33 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
34 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
35 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
36 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
37 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
38 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
39 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
41 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 If these license terms cause you a real problem, contact the author. */
45 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
47 #define getservbyname_r __getservbyname_r
48 #define gethostbyname_r __gethostbyname_r
49 #define gethostbyname2_r __gethostbyname2_r
50 #define gethostbyaddr_r __gethostbyaddr_r
51 #define inet_pton __inet_pton
52 #define inet_ntop __inet_ntop
53 #define strtoul __strtoul
54 #define if_nametoindex __if_nametoindex
57 #define stpcpy __stpcpy
58 /* strdupa is using these */
59 #define memcpy __memcpy
60 #define strlen __strlen
74 #include <arpa/inet.h>
75 #include <sys/socket.h>
76 #include <netinet/in.h>
77 #include <sys/types.h>
79 #include <sys/utsname.h>
82 /* The following declarations and definitions have been removed from
83 * the public header since we don't want people to use them. */
84 #define AI_V4MAPPED 0x0008 /* IPv4-mapped addresses are acceptable. */
85 #define AI_ALL 0x0010 /* Return both IPv4 and IPv6 addresses. */
86 #define AI_ADDRCONFIG 0x0020 /* Use configuration of this host to choose
87 returned address type. */
88 #define AI_DEFAULT (AI_V4MAPPED | AI_ADDRCONFIG)
91 #define GAIH_OKIFUNSPEC 0x0100
92 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
95 #define UNIX_PATH_MAX 108
104 struct gaih_servtuple
106 struct gaih_servtuple *next;
112 static const struct gaih_servtuple nullserv;
114 struct gaih_addrtuple
116 struct gaih_addrtuple *next;
122 struct gaih_typeproto
130 /* Values for `protoflag'. */
131 #define GAI_PROTO_NOSERVICE 1
132 #define GAI_PROTO_PROTOANY 2
134 static const struct gaih_typeproto gaih_inet_typeproto[] =
137 { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
138 { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
139 { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
146 int (*gaih)(const char *name, const struct gaih_service *service,
147 const struct addrinfo *req, struct addrinfo **pai);
151 static const struct addrinfo default_hints;
153 static const struct addrinfo default_hints =
154 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
158 static int addrconfig (sa_family_t af)
162 int saved_errno = errno;
163 s = __socket(af, SOCK_DGRAM, 0);
165 ret = (errno == EMFILE) ? 1 : 0;
171 __set_errno (saved_errno);
176 /* Using Unix sockets this way is a security risk. */
178 gaih_local (const char *name, const struct gaih_service *service,
179 const struct addrinfo *req, struct addrinfo **pai)
181 struct utsname utsname;
183 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
184 return GAIH_OKIFUNSPEC | -EAI_NONAME;
186 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
187 if (uname (&utsname) < 0)
192 if (__strcmp(name, "localhost") &&
193 __strcmp(name, "local") &&
194 __strcmp(name, "unix") &&
195 __strcmp(name, utsname.nodename))
196 return GAIH_OKIFUNSPEC | -EAI_NONAME;
199 if (req->ai_protocol || req->ai_socktype)
201 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
204 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
205 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
206 || (req->ai_protocol != 0
207 && !(tp->protoflag & GAI_PROTO_PROTOANY)
208 && req->ai_protocol != tp->protocol)))
213 if (req->ai_socktype)
214 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
216 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
220 *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
221 + ((req->ai_flags & AI_CANONNAME)
222 ? (__strlen(utsname.nodename) + 1): 0));
226 (*pai)->ai_next = NULL;
227 (*pai)->ai_flags = req->ai_flags;
228 (*pai)->ai_family = AF_LOCAL;
229 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
230 (*pai)->ai_protocol = req->ai_protocol;
231 (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
232 (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
235 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
236 sizeof (struct sockaddr_un);
239 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
240 __memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
244 struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
246 if (__strchr (service->name, '/') != NULL)
248 if (__strlen (service->name) >= sizeof (sunp->sun_path))
249 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
251 __strcpy (sunp->sun_path, service->name);
255 if (__strlen (P_tmpdir "/") + 1 + __strlen (service->name) >=
256 sizeof (sunp->sun_path))
257 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
259 stpcpy (stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
264 /* This is a dangerous use of the interface since there is a time
265 window between the test for the file and the actual creation
266 (done by the caller) in which a file with the same name could
268 char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
270 if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
272 || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
276 if (req->ai_flags & AI_CANONNAME)
277 (*pai)->ai_canonname = __strcpy ((char *) *pai + sizeof (struct addrinfo)
278 + sizeof (struct sockaddr_un),
281 (*pai)->ai_canonname = NULL;
287 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
288 const struct addrinfo *req, struct gaih_servtuple *st)
291 size_t tmpbuflen = 1024;
298 tmpbuf = alloca (tmpbuflen);
300 r = getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
302 if (r != 0 || s == NULL)
307 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
313 st->socktype = tp->socktype;
314 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
315 ? req->ai_protocol : tp->protocol);
316 st->port = s->s_port;
321 #define gethosts(_family, _type) \
331 tmpbuf = alloca (tmpbuflen); \
332 rc = gethostbyname2_r (name, _family, &th, tmpbuf, \
333 tmpbuflen, &h, &herrno); \
334 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
337 if (herrno == NETDB_INTERNAL) \
339 __set_h_errno (herrno); \
340 return -EAI_SYSTEM; \
342 if (herrno == TRY_AGAIN) \
343 no_data = EAI_AGAIN; \
345 no_data = herrno == NO_DATA; \
347 else if (h != NULL) \
349 for (i = 0; h->h_addr_list[i]; i++) \
351 if (*pat == NULL) { \
352 *pat = alloca (sizeof(struct gaih_addrtuple)); \
353 (*pat)->scopeid = 0; \
355 (*pat)->next = NULL; \
356 (*pat)->family = _family; \
357 __memcpy ((*pat)->addr, h->h_addr_list[i], \
359 pat = &((*pat)->next); \
365 gaih_inet (const char *name, const struct gaih_service *service,
366 const struct addrinfo *req, struct addrinfo **pai)
368 const struct gaih_typeproto *tp = gaih_inet_typeproto;
369 struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
370 struct gaih_addrtuple *at = NULL;
372 int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6) &&
373 (req->ai_flags & AI_V4MAPPED);
375 if (req->ai_protocol || req->ai_socktype)
380 && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
381 || (req->ai_protocol != 0
382 && !(tp->protoflag & GAI_PROTO_PROTOANY)
383 && req->ai_protocol != tp->protocol)))
388 if (req->ai_socktype)
389 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
391 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
397 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
398 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
400 if (service->num < 0)
404 st = (struct gaih_servtuple *)
405 alloca (sizeof (struct gaih_servtuple));
407 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
412 struct gaih_servtuple **pst = &st;
413 for (tp++; tp->name[0]; tp++)
415 struct gaih_servtuple *newp;
417 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
420 if (req->ai_socktype != 0
421 && req->ai_socktype != tp->socktype)
423 if (req->ai_protocol != 0
424 && !(tp->protoflag & GAI_PROTO_PROTOANY)
425 && req->ai_protocol != tp->protocol)
428 newp = (struct gaih_servtuple *)
429 alloca (sizeof (struct gaih_servtuple));
431 if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
433 if (rc & GAIH_OKIFUNSPEC)
441 if (st == (struct gaih_servtuple *) &nullserv)
442 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
447 st = alloca (sizeof (struct gaih_servtuple));
449 st->socktype = tp->socktype;
450 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
451 ? req->ai_protocol : tp->protocol);
452 st->port = htons (service->num);
455 else if (req->ai_socktype || req->ai_protocol)
457 st = alloca (sizeof (struct gaih_servtuple));
459 st->socktype = tp->socktype;
460 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
461 ? req->ai_protocol : tp->protocol);
467 * Neither socket type nor protocol is set. Return all socket types
470 struct gaih_servtuple **lastp = &st;
471 for (++tp; tp->name[0]; ++tp)
473 struct gaih_servtuple *newp;
475 newp = alloca (sizeof (struct gaih_servtuple));
477 newp->socktype = tp->socktype;
478 newp->protocol = tp->protocol;
488 at = alloca (sizeof (struct gaih_addrtuple));
490 at->family = AF_UNSPEC;
494 if (inet_pton (AF_INET, name, at->addr) > 0)
496 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET || v4mapped)
497 at->family = AF_INET;
502 #if __UCLIBC_HAS_IPV6__
503 if (at->family == AF_UNSPEC)
505 char *namebuf = strdupa (name);
508 scope_delim = __strchr (namebuf, SCOPE_DELIMITER);
509 if (scope_delim != NULL)
512 if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
514 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
515 at->family = AF_INET6;
519 if (scope_delim != NULL)
521 int try_numericscope = 0;
522 if (IN6_IS_ADDR_LINKLOCAL (at->addr)
523 || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
525 at->scopeid = if_nametoindex (scope_delim + 1);
526 if (at->scopeid == 0)
527 try_numericscope = 1;
530 try_numericscope = 1;
532 if (try_numericscope != 0)
535 assert (sizeof (uint32_t) <= sizeof (unsigned long));
536 at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
539 return GAIH_OKIFUNSPEC | -EAI_NONAME;
546 if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
549 struct gaih_addrtuple **pat = &at;
554 * If we are looking for both IPv4 and IPv6 address we don't want
555 * the lookup functions to automatically promote IPv4 addresses to
559 #if __UCLIBC_HAS_IPV6__
560 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
561 gethosts (AF_INET6, struct in6_addr);
563 no_inet6_data = no_data;
565 if (req->ai_family == AF_INET ||
566 (!v4mapped && req->ai_family == AF_UNSPEC) ||
567 (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL))))
568 gethosts (AF_INET, struct in_addr);
570 if (no_data != 0 && no_inet6_data != 0)
572 /* If both requests timed out report this. */
573 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
577 * We made requests but they turned out no data.
578 * The name is known, though.
580 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
584 if (at->family == AF_UNSPEC)
585 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
589 struct gaih_addrtuple *atr;
590 atr = at = alloca (sizeof (struct gaih_addrtuple));
591 __memset (at, '\0', sizeof (struct gaih_addrtuple));
593 if (req->ai_family == 0)
595 at->next = alloca (sizeof (struct gaih_addrtuple));
596 __memset (at->next, '\0', sizeof (struct gaih_addrtuple));
599 #if __UCLIBC_HAS_IPV6__
600 if (req->ai_family == 0 || req->ai_family == AF_INET6)
602 extern const struct in6_addr __in6addr_loopback;
603 at->family = AF_INET6;
604 if ((req->ai_flags & AI_PASSIVE) == 0)
605 __memcpy (at->addr, &__in6addr_loopback, sizeof (struct in6_addr));
610 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 *) atr->addr = htonl (INADDR_LOOPBACK);
622 const char *c = NULL;
623 struct gaih_servtuple *st2;
624 struct gaih_addrtuple *at2 = at;
625 size_t socklen, namelen;
629 * buffer is the size of an unformatted IPv6 address in
632 char buffer[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"];
636 if (req->ai_flags & AI_CANONNAME)
638 struct hostent *h = NULL;
642 size_t tmpbuflen = 512;
648 tmpbuf = alloca (tmpbuflen);
653 rc = gethostbyaddr_r (at2->addr,
654 ((at2->family == AF_INET6)
655 ? sizeof(struct in6_addr)
656 : sizeof(struct in_addr)),
657 at2->family, &th, tmpbuf, tmpbuflen,
661 while (rc == errno && herrno == NETDB_INTERNAL);
663 if (rc != 0 && herrno == NETDB_INTERNAL)
665 __set_h_errno (herrno);
670 c = inet_ntop (at2->family, at2->addr, buffer, sizeof(buffer));
675 return GAIH_OKIFUNSPEC | -EAI_NONAME;
677 namelen = __strlen (c) + 1;
682 #if __UCLIBC_HAS_IPV6__
683 if (at2->family == AF_INET6 || v4mapped)
686 socklen = sizeof (struct sockaddr_in6);
692 socklen = sizeof (struct sockaddr_in);
695 for (st2 = st; st2 != NULL; st2 = st2->next)
697 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
701 (*pai)->ai_flags = req->ai_flags;
702 (*pai)->ai_family = family;
703 (*pai)->ai_socktype = st2->socktype;
704 (*pai)->ai_protocol = st2->protocol;
705 (*pai)->ai_addrlen = socklen;
706 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
708 (*pai)->ai_addr->sa_len = socklen;
710 (*pai)->ai_addr->sa_family = family;
712 #if __UCLIBC_HAS_IPV6__
713 if (family == AF_INET6)
715 struct sockaddr_in6 *sin6p =
716 (struct sockaddr_in6 *) (*pai)->ai_addr;
718 sin6p->sin6_flowinfo = 0;
719 if (at2->family == AF_INET6)
721 __memcpy (&sin6p->sin6_addr,
722 at2->addr, sizeof (struct in6_addr));
726 sin6p->sin6_addr.s6_addr32[0] = 0;
727 sin6p->sin6_addr.s6_addr32[1] = 0;
728 sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
729 __memcpy(&sin6p->sin6_addr.s6_addr32[3],
730 at2->addr, sizeof (sin6p->sin6_addr.s6_addr32[3]));
732 sin6p->sin6_port = st2->port;
733 sin6p->sin6_scope_id = at2->scopeid;
738 struct sockaddr_in *sinp =
739 (struct sockaddr_in *) (*pai)->ai_addr;
741 __memcpy (&sinp->sin_addr,
742 at2->addr, sizeof (struct in_addr));
743 sinp->sin_port = st2->port;
744 __memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
749 (*pai)->ai_canonname = ((void *) (*pai) +
750 sizeof (struct addrinfo) + socklen);
751 __strcpy ((*pai)->ai_canonname, c);
754 (*pai)->ai_canonname = NULL;
756 (*pai)->ai_next = NULL;
757 pai = &((*pai)->ai_next);
766 static struct gaih gaih[] =
768 #if __UCLIBC_HAS_IPV6__
769 { PF_INET6, gaih_inet },
771 { PF_INET, gaih_inet },
773 { PF_LOCAL, gaih_local },
778 void attribute_hidden
779 __freeaddrinfo (struct addrinfo *ai)
790 strong_alias(__freeaddrinfo,freeaddrinfo)
793 __getaddrinfo (const char *name, const char *service,
794 const struct addrinfo *hints, struct addrinfo **pai)
796 int i = 0, j = 0, last_i = 0;
797 struct addrinfo *p = NULL, **end;
798 struct gaih *g = gaih, *pg = NULL;
799 struct gaih_service gaih_service, *pservice;
801 if (name != NULL && name[0] == '*' && name[1] == 0)
804 if (service != NULL && service[0] == '*' && service[1] == 0)
807 if (name == NULL && service == NULL)
811 hints = &default_hints;
813 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
814 AI_ADDRCONFIG|AI_V4MAPPED|AI_ALL))
817 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
820 if (service && service[0])
823 gaih_service.name = service;
824 gaih_service.num = strtoul (gaih_service.name, &c, 10);
826 gaih_service.num = -1;
829 * Can't specify a numerical socket unless a protocol
832 if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
834 pservice = &gaih_service;
846 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
848 if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family))
851 if (pg == NULL || pg->gaih != g->gaih)
854 i = g->gaih (name, pservice, hints, end);
859 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
865 return -(i & GAIH_EAI);
868 while(*end) end = &((*end)->ai_next);
883 if (pai == NULL && last_i == 0)
889 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
891 strong_alias(__getaddrinfo,getaddrinfo)