1 /* The Inner Net License, Version 2.00
3 The author(s) grant permission for redistribution and use in source and
4 binary forms, with or without modification, of the software and documentation
5 provided that the following conditions are met:
7 0. If you receive a version of the software that is specifically labelled
8 as not being for redistribution (check the version message and/or README),
9 you are not permitted to redistribute that version of the software in any
11 1. All terms of the all other applicable copyrights and licenses must be
13 2. Redistributions of source code must retain the authors' copyright
14 notice(s), this list of conditions, and the following disclaimer.
15 3. Redistributions in binary form must reproduce the authors' copyright
16 notice(s), this list of conditions, and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18 4. [The copyright holder has authorized the removal of this clause.]
19 5. Neither the name(s) of the author(s) nor the names of its contributors
20 may be used to endorse or promote products derived from this software
21 without specific prior written permission.
23 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
24 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
27 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
30 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 If these license terms cause you a real problem, contact the author. */
36 /* This software is Copyright 1996 by Craig Metz, All Rights Reserved. */
46 #include <arpa/inet.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <sys/types.h>
51 #include <sys/utsname.h>
55 #define GAIH_OKIFUNSPEC 0x0100
56 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
59 #define UNIX_PATH_MAX 108
70 struct gaih_servtuple *next;
76 static const struct gaih_servtuple nullserv;
80 struct gaih_addrtuple *next;
94 /* Values for `protoflag'. */
95 #define GAI_PROTO_NOSERVICE 1
96 #define GAI_PROTO_PROTOANY 2
98 static const struct gaih_typeproto gaih_inet_typeproto[] =
101 { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
102 { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
103 { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
110 int (*gaih)(const char *name, const struct gaih_service *service,
111 const struct addrinfo *req, struct addrinfo **pai);
115 static const struct addrinfo default_hints;
117 static const struct addrinfo default_hints =
118 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
123 /* Using Unix sockets this way is a security risk. */
125 gaih_local (const char *name, const struct gaih_service *service,
126 const struct addrinfo *req, struct addrinfo **pai)
128 struct utsname utsname;
130 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
131 return GAIH_OKIFUNSPEC | -EAI_NONAME;
133 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
134 if (uname (&utsname) < 0)
139 if (strcmp(name, "localhost") &&
140 strcmp(name, "local") &&
141 strcmp(name, "unix") &&
142 strcmp(name, utsname.nodename))
143 return GAIH_OKIFUNSPEC | -EAI_NONAME;
146 if (req->ai_protocol || req->ai_socktype)
148 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
151 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
152 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
153 || (req->ai_protocol != 0
154 && !(tp->protoflag & GAI_PROTO_PROTOANY)
155 && req->ai_protocol != tp->protocol)))
160 if (req->ai_socktype)
161 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
163 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
167 *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
168 + ((req->ai_flags & AI_CANONNAME)
169 ? (strlen(utsname.nodename) + 1): 0));
173 (*pai)->ai_next = NULL;
174 (*pai)->ai_flags = req->ai_flags;
175 (*pai)->ai_family = AF_LOCAL;
176 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
177 (*pai)->ai_protocol = req->ai_protocol;
178 (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
179 (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
182 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
183 sizeof (struct sockaddr_un);
186 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
187 memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
191 struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
193 if (strchr (service->name, '/') != NULL)
195 if (strlen (service->name) >= sizeof (sunp->sun_path))
196 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
198 strcpy (sunp->sun_path, service->name);
202 if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
203 sizeof (sunp->sun_path))
204 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
206 __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
211 /* This is a dangerous use of the interface since there is a time
212 window between the test for the file and the actual creation
213 (done by the caller) in which a file with the same name could
215 char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
217 if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
219 || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
223 if (req->ai_flags & AI_CANONNAME)
224 (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
225 + sizeof (struct sockaddr_un),
228 (*pai)->ai_canonname = NULL;
234 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
235 const struct addrinfo *req, struct gaih_servtuple *st)
238 size_t tmpbuflen = 1024;
245 tmpbuf = __alloca (tmpbuflen);
247 r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
249 if (r != 0 || s == NULL)
254 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
260 st->socktype = tp->socktype;
261 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
262 ? req->ai_protocol : tp->protocol);
263 st->port = s->s_port;
268 #define gethosts(_family, _type) \
273 char *tmpbuf = NULL; \
277 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
278 rc = __gethostbyname2_r (name, _family, &th, tmpbuf, \
279 tmpbuflen, &h, &herrno); \
280 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
283 if (herrno == NETDB_INTERNAL) \
285 __set_h_errno (herrno); \
286 return -EAI_SYSTEM; \
288 if (herrno == TRY_AGAIN) \
289 no_data = EAI_AGAIN; \
291 no_data = herrno == NO_DATA; \
293 else if (h != NULL) \
295 for (i = 0; h->h_addr_list[i]; i++) \
297 if (*pat == NULL) { \
298 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
299 (*pat)->scopeid = 0; \
301 (*pat)->next = NULL; \
302 (*pat)->family = _family; \
303 memcpy ((*pat)->addr, h->h_addr_list[i], \
305 pat = &((*pat)->next); \
310 #define gethosts2(_family, _type) \
315 char *tmpbuf = NULL; \
319 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
321 status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, \
322 tmpbuflen, &rc, &herrno)); \
323 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
324 if (status == NSS_STATUS_SUCCESS && rc == 0) \
330 if (herrno == NETDB_INTERNAL) \
332 __set_h_errno (herrno); \
333 return -EAI_SYSTEM; \
335 if (herrno == TRY_AGAIN) \
336 no_data = EAI_AGAIN; \
338 no_data = herrno == NO_DATA; \
340 else if (h != NULL) \
342 for (i = 0; h->h_addr_list[i]; i++) \
344 if (*pat == NULL) { \
345 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
346 (*pat)->scopeid = 0; \
348 (*pat)->next = NULL; \
349 (*pat)->family = _family; \
350 memcpy ((*pat)->addr, h->h_addr_list[i], \
352 pat = &((*pat)->next); \
357 typedef enum nss_status (*nss_gethostbyname2_r)
358 (const char *name, int af, struct hostent *host,
359 char *buffer, size_t buflen, int *errnop,
361 extern service_user *__nss_hosts_database attribute_hidden;
364 gaih_inet (const char *name, const struct gaih_service *service,
365 const struct addrinfo *req, struct addrinfo **pai)
367 const struct gaih_typeproto *tp = gaih_inet_typeproto;
368 struct gaih_servtuple *st = (struct gaih_servtuple *) &nullserv;
369 struct gaih_addrtuple *at = NULL;
372 if (req->ai_protocol || req->ai_socktype)
377 && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
378 || (req->ai_protocol != 0
379 && !(tp->protoflag & GAI_PROTO_PROTOANY)
380 && req->ai_protocol != tp->protocol)))
385 if (req->ai_socktype)
386 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
388 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
394 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
395 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
397 if (service->num < 0)
401 st = (struct gaih_servtuple *)
402 __alloca (sizeof (struct gaih_servtuple));
404 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
409 struct gaih_servtuple **pst = &st;
410 for (tp++; tp->name[0]; tp++)
412 struct gaih_servtuple *newp;
414 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
417 if (req->ai_socktype != 0
418 && req->ai_socktype != tp->socktype)
420 if (req->ai_protocol != 0
421 && !(tp->protoflag & GAI_PROTO_PROTOANY)
422 && req->ai_protocol != tp->protocol)
425 newp = (struct gaih_servtuple *)
426 __alloca (sizeof (struct gaih_servtuple));
428 if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
430 if (rc & GAIH_OKIFUNSPEC)
438 if (st == (struct gaih_servtuple *) &nullserv)
439 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
444 st = __alloca (sizeof (struct gaih_servtuple));
446 st->socktype = tp->socktype;
447 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
448 ? req->ai_protocol : tp->protocol);
449 st->port = htons (service->num);
452 else if (req->ai_socktype || req->ai_protocol)
454 st = __alloca (sizeof (struct gaih_servtuple));
456 st->socktype = tp->socktype;
457 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
458 ? req->ai_protocol : tp->protocol);
463 /* Neither socket type nor protocol is set. Return all socket types
465 struct gaih_servtuple **lastp = &st;
466 for (++tp; tp->name[0]; ++tp)
468 struct gaih_servtuple *newp;
470 newp = __alloca (sizeof (struct gaih_servtuple));
472 newp->socktype = tp->socktype;
473 newp->protocol = tp->protocol;
483 at = __alloca (sizeof (struct gaih_addrtuple));
485 at->family = AF_UNSPEC;
489 if (inet_pton (AF_INET, name, at->addr) > 0)
491 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
492 at->family = AF_INET;
494 return -EAI_ADDRFAMILY;
497 if (at->family == AF_UNSPEC)
499 char *namebuf = strdupa (name);
502 scope_delim = strchr (namebuf, SCOPE_DELIMITER);
503 if (scope_delim != NULL)
506 if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
508 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
509 at->family = AF_INET6;
511 return -EAI_ADDRFAMILY;
513 if (scope_delim != NULL)
515 int try_numericscope = 0;
516 if (IN6_IS_ADDR_LINKLOCAL (at->addr)
517 || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
519 at->scopeid = if_nametoindex (scope_delim + 1);
520 if (at->scopeid == 0)
521 try_numericscope = 1;
524 try_numericscope = 1;
526 if (try_numericscope != 0)
529 assert (sizeof (uint32_t) <= sizeof (unsigned long));
530 at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
533 return GAIH_OKIFUNSPEC | -EAI_NONAME;
539 if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
542 struct gaih_addrtuple **pat = &at;
544 int no_inet6_data = 0;
545 int old_res_options = _res.options;
547 /* If we are looking for both IPv4 and IPv6 address we don't
548 want the lookup functions to automatically promote IPv4
549 addresses to IPv6 addresses. Currently this is decided
550 by setting the RES_USE_INET6 bit in _res.options. */
551 if (req->ai_family == AF_UNSPEC)
553 service_user *nip = NULL;
554 enum nss_status inet6_status, status = NSS_STATUS_UNAVAIL;
556 nss_gethostbyname2_r fct;
558 if (__nss_hosts_database != NULL)
561 nip = __nss_hosts_database;
564 no_more = __nss_database_lookup ("hosts", NULL,
565 "dns [!UNAVAIL=return] files", &nip);
567 _res.options &= ~RES_USE_INET6;
571 fct = __nss_lookup_function (nip, "gethostbyname2_r");
573 gethosts2 (AF_INET6, struct in6_addr);
574 no_inet6_data = no_data;
575 inet6_status = status;
576 gethosts2 (AF_INET, struct in_addr);
578 /* If we found one address for AF_INET or AF_INET6,
579 don't continue the search. */
580 if (inet6_status == NSS_STATUS_SUCCESS ||
581 status == NSS_STATUS_SUCCESS)
584 /* We can have different states for AF_INET
585 and AF_INET6. Try to find a usefull one for
587 if (inet6_status == NSS_STATUS_TRYAGAIN)
588 status = NSS_STATUS_TRYAGAIN;
589 else if (status == NSS_STATUS_UNAVAIL &&
590 inet6_status != NSS_STATUS_UNAVAIL)
591 status = inet6_status;
593 if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
596 if (nip->next == NULL)
602 _res.options = old_res_options;
604 else if (req->ai_family == AF_INET6)
606 gethosts (AF_INET6, struct in6_addr);
607 no_inet6_data = no_data;
609 else if (req->ai_family == AF_INET)
610 gethosts (AF_INET, struct in_addr);
612 if (no_data != 0 && no_inet6_data != 0)
614 /* If both requests timed out report this. */
615 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
618 /* We made requests but they turned out no data. The name
620 return (GAIH_OKIFUNSPEC | -EAI_NODATA);
624 if (at->family == AF_UNSPEC)
625 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
629 struct gaih_addrtuple *atr;
630 atr = at = __alloca (sizeof (struct gaih_addrtuple));
631 memset (at, '\0', sizeof (struct gaih_addrtuple));
633 if (req->ai_family == 0)
635 at->next = __alloca (sizeof (struct gaih_addrtuple));
636 memset (at->next, '\0', sizeof (struct gaih_addrtuple));
639 if (req->ai_family == 0 || req->ai_family == AF_INET6)
641 at->family = AF_INET6;
642 if ((req->ai_flags & AI_PASSIVE) == 0)
643 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
647 if (req->ai_family == 0 || req->ai_family == AF_INET)
649 atr->family = AF_INET;
650 if ((req->ai_flags & AI_PASSIVE) == 0)
651 *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
659 const char *c = NULL;
660 struct gaih_servtuple *st2;
661 struct gaih_addrtuple *at2 = at;
662 size_t socklen, namelen;
666 buffer is the size of an unformatted IPv6 address in printable format.
670 if (req->ai_flags & AI_CANONNAME)
672 struct hostent *h = NULL;
676 size_t tmpbuflen = 512;
682 tmpbuf = __alloca (tmpbuflen);
684 rc = __gethostbyaddr_r (at2->addr,
685 ((at2->family == AF_INET6)
686 ? sizeof(struct in6_addr)
687 : sizeof(struct in_addr)),
688 at2->family, &th, tmpbuf, tmpbuflen,
692 while (rc == errno && herrno == NETDB_INTERNAL);
694 if (rc != 0 && herrno == NETDB_INTERNAL)
696 __set_h_errno (herrno);
704 /* We have to try to get the canonical in some other
705 way. If we are looking for either AF_INET or
706 AF_INET6 try the other line. */
707 if (req->ai_family == AF_UNSPEC)
709 struct addrinfo *p = NULL;
710 struct addrinfo **end = &p;
711 struct addrinfo localreq = *req;
712 struct addrinfo *runp;
714 localreq.ai_family = AF_INET + AF_INET6 - at2->family;
715 (void) gaih_inet (name, service, &localreq, end);
720 if (p->ai_canonname != name)
722 c = strdupa (p->ai_canonname);
725 runp = runp->ai_next;
731 /* If this code is used the missing canonical name is
732 substituted with the name passed in by the user. */
738 return GAIH_OKIFUNSPEC | -EAI_NONAME;
740 namelen = strlen (c) + 1;
745 if (at2->family == AF_INET6)
748 socklen = sizeof (struct sockaddr_in6);
753 socklen = sizeof (struct sockaddr_in);
756 for (st2 = st; st2 != NULL; st2 = st2->next)
758 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
762 (*pai)->ai_flags = req->ai_flags;
763 (*pai)->ai_family = family;
764 (*pai)->ai_socktype = st2->socktype;
765 (*pai)->ai_protocol = st2->protocol;
766 (*pai)->ai_addrlen = socklen;
767 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
769 (*pai)->ai_addr->sa_len = socklen;
771 (*pai)->ai_addr->sa_family = family;
773 if (family == AF_INET6)
775 struct sockaddr_in6 *sin6p =
776 (struct sockaddr_in6 *) (*pai)->ai_addr;
778 sin6p->sin6_flowinfo = 0;
779 memcpy (&sin6p->sin6_addr,
780 at2->addr, sizeof (struct in6_addr));
781 sin6p->sin6_port = st2->port;
782 sin6p->sin6_scope_id = at2->scopeid;
786 struct sockaddr_in *sinp =
787 (struct sockaddr_in *) (*pai)->ai_addr;
788 memcpy (&sinp->sin_addr,
789 at2->addr, sizeof (struct in_addr));
790 sinp->sin_port = st2->port;
791 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
796 (*pai)->ai_canonname = ((void *) (*pai) +
797 sizeof (struct addrinfo) + socklen);
798 strcpy ((*pai)->ai_canonname, c);
801 (*pai)->ai_canonname = NULL;
803 (*pai)->ai_next = NULL;
804 pai = &((*pai)->ai_next);
813 static struct gaih gaih[] =
815 { PF_INET6, gaih_inet },
816 { PF_INET, gaih_inet },
818 { PF_LOCAL, gaih_local },
824 getaddrinfo (const char *name, const char *service,
825 const struct addrinfo *hints, struct addrinfo **pai)
827 int i = 0, j = 0, last_i = 0;
828 struct addrinfo *p = NULL, **end;
829 struct gaih *g = gaih, *pg = NULL;
830 struct gaih_service gaih_service, *pservice;
832 if (name != NULL && name[0] == '*' && name[1] == 0)
835 if (service != NULL && service[0] == '*' && service[1] == 0)
838 if (name == NULL && service == NULL)
842 hints = &default_hints;
844 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST))
847 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
850 if (service && service[0])
853 gaih_service.name = service;
854 gaih_service.num = strtoul (gaih_service.name, &c, 10);
856 gaih_service.num = -1;
858 /* Can't specify a numerical socket unless a protocol family was
860 if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
862 pservice = &gaih_service;
874 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
877 if (pg == NULL || pg->gaih != g->gaih)
880 i = g->gaih (name, pservice, hints, end);
883 /* EAI_NODATA is a more specific result as it says that
884 we found a result but it is not usable. */
885 if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
888 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
896 return -(i & GAIH_EAI);
899 while(*end) end = &((*end)->ai_next);
914 if (pai == NULL && last_i == 0)
919 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
921 libc_hidden_def (getaddrinfo)
924 freeaddrinfo (struct addrinfo *ai)
935 libc_hidden_def (freeaddrinfo)