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. */
48 #include <arpa/inet.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <sys/types.h>
53 #include <sys/utsname.h>
57 #define GAIH_OKIFUNSPEC 0x0100
58 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
61 #define UNIX_PATH_MAX 108
72 struct gaih_servtuple
*next
;
78 static const struct gaih_servtuple nullserv
;
82 struct gaih_addrtuple
*next
;
96 /* Values for `protoflag'. */
97 #define GAI_PROTO_NOSERVICE 1
98 #define GAI_PROTO_PROTOANY 2
100 static const struct gaih_typeproto gaih_inet_typeproto
[] =
103 { SOCK_STREAM
, IPPROTO_TCP
, "tcp", 0 },
104 { SOCK_DGRAM
, IPPROTO_UDP
, "udp", 0 },
105 { SOCK_RAW
, 0, "raw", GAI_PROTO_PROTOANY
|GAI_PROTO_NOSERVICE
},
112 int (*gaih
)(const char *name
, const struct gaih_service
*service
,
113 const struct addrinfo
*req
, struct addrinfo
**pai
);
116 static const struct addrinfo default_hints
=
118 .ai_flags
= AI_DEFAULT
,
119 .ai_family
= PF_UNSPEC
,
124 .ai_canonname
= NULL
,
130 /* Using Unix sockets this way is a security risk. */
132 gaih_local (const char *name
, const struct gaih_service
*service
,
133 const struct addrinfo
*req
, struct addrinfo
**pai
)
135 struct utsname utsname
;
137 if ((name
!= NULL
) && (req
->ai_flags
& AI_NUMERICHOST
))
138 return GAIH_OKIFUNSPEC
| -EAI_NONAME
;
140 if ((name
!= NULL
) || (req
->ai_flags
& AI_CANONNAME
))
141 if (uname (&utsname
) < 0)
146 if (strcmp(name
, "localhost") &&
147 strcmp(name
, "local") &&
148 strcmp(name
, "unix") &&
149 strcmp(name
, utsname
.nodename
))
150 return GAIH_OKIFUNSPEC
| -EAI_NONAME
;
153 if (req
->ai_protocol
|| req
->ai_socktype
)
155 const struct gaih_typeproto
*tp
= gaih_inet_typeproto
+ 1;
158 && ((tp
->protoflag
& GAI_PROTO_NOSERVICE
) != 0
159 || (req
->ai_socktype
!= 0 && req
->ai_socktype
!= tp
->socktype
)
160 || (req
->ai_protocol
!= 0
161 && !(tp
->protoflag
& GAI_PROTO_PROTOANY
)
162 && req
->ai_protocol
!= tp
->protocol
)))
167 if (req
->ai_socktype
)
168 return (GAIH_OKIFUNSPEC
| -EAI_SOCKTYPE
);
170 return (GAIH_OKIFUNSPEC
| -EAI_SERVICE
);
174 *pai
= malloc (sizeof (struct addrinfo
) + sizeof (struct sockaddr_un
)
175 + ((req
->ai_flags
& AI_CANONNAME
)
176 ? (strlen(utsname
.nodename
) + 1): 0));
180 (*pai
)->ai_next
= NULL
;
181 (*pai
)->ai_flags
= req
->ai_flags
;
182 (*pai
)->ai_family
= AF_LOCAL
;
183 (*pai
)->ai_socktype
= req
->ai_socktype
? req
->ai_socktype
: SOCK_STREAM
;
184 (*pai
)->ai_protocol
= req
->ai_protocol
;
185 (*pai
)->ai_addrlen
= sizeof (struct sockaddr_un
);
186 (*pai
)->ai_addr
= (void *) (*pai
) + sizeof (struct addrinfo
);
189 ((struct sockaddr_un
*) (*pai
)->ai_addr
)->sun_len
=
190 sizeof (struct sockaddr_un
);
193 ((struct sockaddr_un
*)(*pai
)->ai_addr
)->sun_family
= AF_LOCAL
;
194 memset(((struct sockaddr_un
*)(*pai
)->ai_addr
)->sun_path
, 0, UNIX_PATH_MAX
);
198 struct sockaddr_un
*sunp
= (struct sockaddr_un
*) (*pai
)->ai_addr
;
200 if (strchr (service
->name
, '/') != NULL
)
202 if (strlen (service
->name
) >= sizeof (sunp
->sun_path
))
203 return GAIH_OKIFUNSPEC
| -EAI_SERVICE
;
205 strcpy (sunp
->sun_path
, service
->name
);
209 if (strlen (P_tmpdir
"/") + 1 + strlen (service
->name
) >=
210 sizeof (sunp
->sun_path
))
211 return GAIH_OKIFUNSPEC
| -EAI_SERVICE
;
213 __stpcpy (__stpcpy (sunp
->sun_path
, P_tmpdir
"/"), service
->name
);
218 /* This is a dangerous use of the interface since there is a time
219 window between the test for the file and the actual creation
220 (done by the caller) in which a file with the same name could
222 char *buf
= ((struct sockaddr_un
*) (*pai
)->ai_addr
)->sun_path
;
224 if (__builtin_expect (__path_search (buf
, L_tmpnam
, NULL
, NULL
, 0),
226 || __builtin_expect (__gen_tempname (buf
, __GT_NOCREATE
), 0) != 0)
230 if (req
->ai_flags
& AI_CANONNAME
)
231 (*pai
)->ai_canonname
= strcpy ((char *) *pai
+ sizeof (struct addrinfo
)
232 + sizeof (struct sockaddr_un
),
235 (*pai
)->ai_canonname
= NULL
;
241 gaih_inet_serv (const char *servicename
, const struct gaih_typeproto
*tp
,
242 const struct addrinfo
*req
, struct gaih_servtuple
*st
)
245 size_t tmpbuflen
= 1024;
252 tmpbuf
= __alloca (tmpbuflen
);
254 r
= __getservbyname_r (servicename
, tp
->name
, &ts
, tmpbuf
, tmpbuflen
,
256 if (r
!= 0 || s
== NULL
)
261 return GAIH_OKIFUNSPEC
| -EAI_SERVICE
;
267 st
->socktype
= tp
->socktype
;
268 st
->protocol
= ((tp
->protoflag
& GAI_PROTO_PROTOANY
)
269 ? req
->ai_protocol
: tp
->protocol
);
270 st
->port
= s
->s_port
;
275 #define gethosts(_family, _type) \
280 char *tmpbuf = NULL; \
284 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
285 rc = __gethostbyname2_r (name, _family, &th, tmpbuf, \
286 tmpbuflen, &h, &herrno); \
287 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
290 if (herrno == NETDB_INTERNAL) \
292 __set_h_errno (herrno); \
293 return -EAI_SYSTEM; \
295 if (herrno == TRY_AGAIN) \
296 no_data = EAI_AGAIN; \
298 no_data = herrno == NO_DATA; \
300 else if (h != NULL) \
302 for (i = 0; h->h_addr_list[i]; i++) \
304 if (*pat == NULL) { \
305 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
306 (*pat)->scopeid = 0; \
308 (*pat)->next = NULL; \
309 (*pat)->family = _family; \
310 memcpy ((*pat)->addr, h->h_addr_list[i], \
312 pat = &((*pat)->next); \
314 if (_family == AF_INET6) \
317 else if (_family == AF_INET6 && (req->ai_flags & AI_V4MAPPED)) \
319 /* We have to add V4 mapped addresses. Maybe we discard them \
320 later again but we get them anyhow for now. */ \
321 while ((rc = __gethostbyname2_r (name, AF_INET6, &th, tmpbuf, \
322 tmpbuflen, &h, &herrno)) == ERANGE \
323 && herrno == NETDB_INTERNAL) \
324 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
328 if (herrno == NETDB_INTERNAL) \
330 __set_h_errno (herrno); \
331 return -EAI_SYSTEM; \
333 if (herrno == TRY_AGAIN) \
334 no_data = EAI_AGAIN; \
336 no_data = herrno == NO_DATA; \
338 else if (h != NULL) \
340 for (i = 0; h->h_addr_list[i]; ++i) \
344 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
345 (*pat)->scopeid = 0; \
347 uint32_t *addr = (uint32_t *) (*pat)->addr; \
348 (*pat)->next = NULL; \
349 (*pat)->family = _family; \
350 addr[3] = *(uint32_t *) h->h_addr_list[i]; \
351 addr[2] = htonl (0xffff); \
354 pat = &((*pat)->next); \
361 #define gethosts2(_family, _type) \
366 char *tmpbuf = NULL; \
370 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
372 status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, \
373 tmpbuflen, &rc, &herrno)); \
374 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
375 if (status == NSS_STATUS_SUCCESS && rc == 0) \
381 if (herrno == NETDB_INTERNAL) \
383 __set_h_errno (herrno); \
384 return -EAI_SYSTEM; \
386 if (herrno == TRY_AGAIN) \
387 no_data = EAI_AGAIN; \
389 no_data = herrno == NO_DATA; \
391 else if (h != NULL) \
393 for (i = 0; h->h_addr_list[i]; i++) \
395 if (*pat == NULL) { \
396 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
397 (*pat)->scopeid = 0; \
399 (*pat)->next = NULL; \
400 (*pat)->family = _family; \
401 memcpy ((*pat)->addr, h->h_addr_list[i], \
403 pat = &((*pat)->next); \
408 typedef enum nss_status (*nss_gethostbyname2_r
)
409 (const char *name
, int af
, struct hostent
*host
,
410 char *buffer
, size_t buflen
, int *errnop
,
412 extern service_user
*__nss_hosts_database attribute_hidden
;
415 gaih_inet (const char *name
, const struct gaih_service
*service
,
416 const struct addrinfo
*req
, struct addrinfo
**pai
)
418 const struct gaih_typeproto
*tp
= gaih_inet_typeproto
;
419 struct gaih_servtuple
*st
= (struct gaih_servtuple
*) &nullserv
;
420 struct gaih_addrtuple
*at
= NULL
;
422 bool got_ipv6
= false;
424 if (req
->ai_protocol
|| req
->ai_socktype
)
429 && ((req
->ai_socktype
!= 0 && req
->ai_socktype
!= tp
->socktype
)
430 || (req
->ai_protocol
!= 0
431 && !(tp
->protoflag
& GAI_PROTO_PROTOANY
)
432 && req
->ai_protocol
!= tp
->protocol
)))
437 if (req
->ai_socktype
)
438 return (GAIH_OKIFUNSPEC
| -EAI_SOCKTYPE
);
440 return (GAIH_OKIFUNSPEC
| -EAI_SERVICE
);
446 if ((tp
->protoflag
& GAI_PROTO_NOSERVICE
) != 0)
447 return (GAIH_OKIFUNSPEC
| -EAI_SERVICE
);
449 if (service
->num
< 0)
453 st
= (struct gaih_servtuple
*)
454 __alloca (sizeof (struct gaih_servtuple
));
456 if ((rc
= gaih_inet_serv (service
->name
, tp
, req
, st
)))
461 struct gaih_servtuple
**pst
= &st
;
462 for (tp
++; tp
->name
[0]; tp
++)
464 struct gaih_servtuple
*newp
;
466 if ((tp
->protoflag
& GAI_PROTO_NOSERVICE
) != 0)
469 if (req
->ai_socktype
!= 0
470 && req
->ai_socktype
!= tp
->socktype
)
472 if (req
->ai_protocol
!= 0
473 && !(tp
->protoflag
& GAI_PROTO_PROTOANY
)
474 && req
->ai_protocol
!= tp
->protocol
)
477 newp
= (struct gaih_servtuple
*)
478 __alloca (sizeof (struct gaih_servtuple
));
480 if ((rc
= gaih_inet_serv (service
->name
, tp
, req
, newp
)))
482 if (rc
& GAIH_OKIFUNSPEC
)
490 if (st
== (struct gaih_servtuple
*) &nullserv
)
491 return (GAIH_OKIFUNSPEC
| -EAI_SERVICE
);
496 st
= __alloca (sizeof (struct gaih_servtuple
));
498 st
->socktype
= tp
->socktype
;
499 st
->protocol
= ((tp
->protoflag
& GAI_PROTO_PROTOANY
)
500 ? req
->ai_protocol
: tp
->protocol
);
501 st
->port
= htons (service
->num
);
504 else if (req
->ai_socktype
|| req
->ai_protocol
)
506 st
= __alloca (sizeof (struct gaih_servtuple
));
508 st
->socktype
= tp
->socktype
;
509 st
->protocol
= ((tp
->protoflag
& GAI_PROTO_PROTOANY
)
510 ? req
->ai_protocol
: tp
->protocol
);
515 /* Neither socket type nor protocol is set. Return all socket types
517 struct gaih_servtuple
**lastp
= &st
;
518 for (++tp
; tp
->name
[0]; ++tp
)
520 struct gaih_servtuple
*newp
;
522 newp
= __alloca (sizeof (struct gaih_servtuple
));
524 newp
->socktype
= tp
->socktype
;
525 newp
->protocol
= tp
->protocol
;
535 at
= __alloca (sizeof (struct gaih_addrtuple
));
537 at
->family
= AF_UNSPEC
;
541 if (inet_pton (AF_INET
, name
, at
->addr
) > 0)
543 if (req
->ai_family
== AF_UNSPEC
|| req
->ai_family
== AF_INET
)
544 at
->family
= AF_INET
;
545 else if (req
->ai_flags
& AI_V4MAPPED
)
547 ((uint32_t *) at
->addr
)[3] = *(uint32_t *) at
->addr
;
548 ((uint32_t *) at
->addr
)[2] = htonl (0xffff);
549 ((uint32_t *) at
->addr
)[1] = 0;
550 ((uint32_t *) at
->addr
)[0] = 0;
553 return -EAI_ADDRFAMILY
;
556 if (at
->family
== AF_UNSPEC
)
558 char *namebuf
= strdupa (name
);
561 scope_delim
= strchr (namebuf
, SCOPE_DELIMITER
);
562 if (scope_delim
!= NULL
)
565 if (inet_pton (AF_INET6
, namebuf
, at
->addr
) > 0)
567 if (req
->ai_family
== AF_UNSPEC
|| req
->ai_family
== AF_INET6
)
568 at
->family
= AF_INET6
;
569 else if (IN6_IS_ADDR_V4MAPPED (at
->addr
))
570 *(uint32_t *) at
->addr
= ((uint32_t *) at
->addr
)[3];
572 return -EAI_ADDRFAMILY
;
574 if (scope_delim
!= NULL
)
576 int try_numericscope
= 0;
577 if (IN6_IS_ADDR_LINKLOCAL (at
->addr
)
578 || IN6_IS_ADDR_MC_LINKLOCAL (at
->addr
))
580 at
->scopeid
= if_nametoindex (scope_delim
+ 1);
581 if (at
->scopeid
== 0)
582 try_numericscope
= 1;
585 try_numericscope
= 1;
587 if (try_numericscope
!= 0)
590 assert (sizeof (uint32_t) <= sizeof (unsigned long));
591 at
->scopeid
= (uint32_t) strtoul (scope_delim
+ 1, &end
,
594 return GAIH_OKIFUNSPEC
| -EAI_NONAME
;
600 if (at
->family
== AF_UNSPEC
&& (req
->ai_flags
& AI_NUMERICHOST
) == 0)
603 struct gaih_addrtuple
**pat
= &at
;
605 int no_inet6_data
= 0;
606 int old_res_options
= _res
.options
;
608 /* If we are looking for both IPv4 and IPv6 address we don't
609 want the lookup functions to automatically promote IPv4
610 addresses to IPv6 addresses. Currently this is decided
611 by setting the RES_USE_INET6 bit in _res.options. */
612 if (req
->ai_family
== AF_UNSPEC
)
614 service_user
*nip
= NULL
;
615 enum nss_status inet6_status
, status
= NSS_STATUS_UNAVAIL
;
617 nss_gethostbyname2_r fct
;
619 if (__nss_hosts_database
!= NULL
)
622 nip
= __nss_hosts_database
;
625 no_more
= __nss_database_lookup ("hosts", NULL
,
626 "dns [!UNAVAIL=return] files", &nip
);
628 _res
.options
&= ~RES_USE_INET6
;
632 fct
= __nss_lookup_function (nip
, "gethostbyname2_r");
636 gethosts2 (AF_INET6
, struct in6_addr
);
637 no_inet6_data
= no_data
;
638 inet6_status
= status
;
639 gethosts2 (AF_INET
, struct in_addr
);
641 /* If we found one address for AF_INET or AF_INET6,
642 don't continue the search. */
643 if (inet6_status
== NSS_STATUS_SUCCESS
||
644 status
== NSS_STATUS_SUCCESS
)
647 /* We can have different states for AF_INET
648 and AF_INET6. Try to find a usefull one for
650 if (inet6_status
== NSS_STATUS_TRYAGAIN
)
651 status
= NSS_STATUS_TRYAGAIN
;
652 else if (status
== NSS_STATUS_UNAVAIL
&&
653 inet6_status
!= NSS_STATUS_UNAVAIL
)
654 status
= inet6_status
;
657 if (nss_next_action (nip
, status
) == NSS_ACTION_RETURN
)
660 if (nip
->next
== NULL
)
666 _res
.options
= old_res_options
;
668 else if (req
->ai_family
== AF_INET6
)
670 gethosts (AF_INET6
, struct in6_addr
);
671 no_inet6_data
= no_data
;
673 else if (req
->ai_family
== AF_INET
)
675 gethosts (AF_INET
, struct in_addr
);
676 no_inet6_data
= no_data
;
679 if (no_data
!= 0 && no_inet6_data
!= 0)
681 /* If both requests timed out report this. */
682 if (no_data
== EAI_AGAIN
&& no_inet6_data
== EAI_AGAIN
)
685 /* We made requests but they turned out no data. The name
687 return (GAIH_OKIFUNSPEC
| -EAI_NODATA
);
691 if (at
->family
== AF_UNSPEC
)
692 return (GAIH_OKIFUNSPEC
| -EAI_NONAME
);
696 struct gaih_addrtuple
*atr
;
697 atr
= at
= __alloca (sizeof (struct gaih_addrtuple
));
698 memset (at
, '\0', sizeof (struct gaih_addrtuple
));
700 if (req
->ai_family
== 0)
702 at
->next
= __alloca (sizeof (struct gaih_addrtuple
));
703 memset (at
->next
, '\0', sizeof (struct gaih_addrtuple
));
706 if (req
->ai_family
== 0 || req
->ai_family
== AF_INET6
)
708 at
->family
= AF_INET6
;
709 if ((req
->ai_flags
& AI_PASSIVE
) == 0)
710 memcpy (at
->addr
, &in6addr_loopback
, sizeof (struct in6_addr
));
714 if (req
->ai_family
== 0 || req
->ai_family
== AF_INET
)
716 atr
->family
= AF_INET
;
717 if ((req
->ai_flags
& AI_PASSIVE
) == 0)
718 *(uint32_t *) atr
->addr
= htonl (INADDR_LOOPBACK
);
726 const char *c
= NULL
;
727 struct gaih_servtuple
*st2
;
728 struct gaih_addrtuple
*at2
= at
;
729 size_t socklen
, namelen
;
733 buffer is the size of an unformatted IPv6 address in printable format.
737 if (req
->ai_flags
& AI_CANONNAME
)
739 struct hostent
*h
= NULL
;
743 size_t tmpbuflen
= 512;
748 tmpbuf
= extend_alloca (tmpbuf
, tmpbuflen
, tmpbuflen
* 2);
749 rc
= __gethostbyaddr_r (at2
->addr
,
750 ((at2
->family
== AF_INET6
)
751 ? sizeof(struct in6_addr
)
752 : sizeof(struct in_addr
)),
753 at2
->family
, &th
, tmpbuf
, tmpbuflen
,
757 while (rc
== ERANGE
&& herrno
== NETDB_INTERNAL
);
759 if (rc
!= 0 && herrno
== NETDB_INTERNAL
)
761 __set_h_errno (herrno
);
769 /* We have to try to get the canonical in some other
770 way. If we are looking for either AF_INET or
771 AF_INET6 try the other line. */
772 if (req
->ai_family
== AF_UNSPEC
)
774 struct addrinfo
*p
= NULL
;
775 struct addrinfo
**end
= &p
;
776 struct addrinfo localreq
= *req
;
777 struct addrinfo
*runp
;
779 localreq
.ai_family
= AF_INET
+ AF_INET6
- at2
->family
;
780 (void) gaih_inet (name
, service
, &localreq
, end
);
785 if (p
->ai_canonname
!= name
)
787 c
= strdupa (p
->ai_canonname
);
790 runp
= runp
->ai_next
;
796 /* If this code is used the missing canonical name is
797 substituted with the name passed in by the user. */
803 return GAIH_OKIFUNSPEC
| -EAI_NONAME
;
805 namelen
= strlen (c
) + 1;
810 if (at2
->family
== AF_INET6
)
813 socklen
= sizeof (struct sockaddr_in6
);
815 /* If we looked up IPv4 mapped address discard them here if
816 the caller isn't interested in all address and we have
817 found at least one IPv6 address. */
819 && (req
->ai_flags
& (AI_V4MAPPED
|AI_ALL
)) == AI_V4MAPPED
820 && IN6_IS_ADDR_V4MAPPED (at2
->addr
))
826 socklen
= sizeof (struct sockaddr_in
);
829 for (st2
= st
; st2
!= NULL
; st2
= st2
->next
)
831 *pai
= malloc (sizeof (struct addrinfo
) + socklen
+ namelen
);
835 (*pai
)->ai_flags
= req
->ai_flags
;
836 (*pai
)->ai_family
= family
;
837 (*pai
)->ai_socktype
= st2
->socktype
;
838 (*pai
)->ai_protocol
= st2
->protocol
;
839 (*pai
)->ai_addrlen
= socklen
;
840 (*pai
)->ai_addr
= (void *) (*pai
) + sizeof (struct addrinfo
);
842 (*pai
)->ai_addr
->sa_len
= socklen
;
844 (*pai
)->ai_addr
->sa_family
= family
;
846 if (family
== AF_INET6
)
848 struct sockaddr_in6
*sin6p
=
849 (struct sockaddr_in6
*) (*pai
)->ai_addr
;
851 sin6p
->sin6_flowinfo
= 0;
852 memcpy (&sin6p
->sin6_addr
,
853 at2
->addr
, sizeof (struct in6_addr
));
854 sin6p
->sin6_port
= st2
->port
;
855 sin6p
->sin6_scope_id
= at2
->scopeid
;
859 struct sockaddr_in
*sinp
=
860 (struct sockaddr_in
*) (*pai
)->ai_addr
;
861 memcpy (&sinp
->sin_addr
,
862 at2
->addr
, sizeof (struct in_addr
));
863 sinp
->sin_port
= st2
->port
;
864 memset (sinp
->sin_zero
, '\0', sizeof (sinp
->sin_zero
));
869 (*pai
)->ai_canonname
= ((void *) (*pai
) +
870 sizeof (struct addrinfo
) + socklen
);
871 strcpy ((*pai
)->ai_canonname
, c
);
874 (*pai
)->ai_canonname
= NULL
;
876 (*pai
)->ai_next
= NULL
;
877 pai
= &((*pai
)->ai_next
);
887 static struct gaih gaih
[] =
889 { PF_INET6
, gaih_inet
},
890 { PF_INET
, gaih_inet
},
892 { PF_LOCAL
, gaih_local
},
898 getaddrinfo (const char *name
, const char *service
,
899 const struct addrinfo
*hints
, struct addrinfo
**pai
)
901 int i
= 0, j
= 0, last_i
= 0;
902 struct addrinfo
*p
= NULL
, **end
;
903 struct gaih
*g
= gaih
, *pg
= NULL
;
904 struct gaih_service gaih_service
, *pservice
;
905 struct addrinfo local_hints
;
907 if (name
!= NULL
&& name
[0] == '*' && name
[1] == 0)
910 if (service
!= NULL
&& service
[0] == '*' && service
[1] == 0)
913 if (name
== NULL
&& service
== NULL
)
917 hints
= &default_hints
;
920 & ~(AI_PASSIVE
|AI_CANONNAME
|AI_NUMERICHOST
|AI_ADDRCONFIG
|AI_V4MAPPED
924 if ((hints
->ai_flags
& AI_CANONNAME
) && name
== NULL
)
927 if (hints
->ai_flags
& AI_ADDRCONFIG
)
929 /* Determine whether we have IPv4 or IPv6 interfaces or both.
930 We cannot cache the results since new interfaces could be
931 added at any time. */
934 __check_pf (&seen_ipv4
, &seen_ipv6
);
936 /* Now make a decision on what we return, if anything. */
937 if (hints
->ai_family
== PF_UNSPEC
)
939 /* If we haven't seen both IPv4 and IPv6 interfaces we can
940 narrow down the search. */
941 if (! seen_ipv4
|| ! seen_ipv6
)
943 local_hints
= *hints
;
944 local_hints
.ai_family
= seen_ipv4
? PF_INET
: PF_INET6
;
945 hints
= &local_hints
;
948 else if ((hints
->ai_family
== PF_INET
&& ! seen_ipv4
)
949 || (hints
->ai_family
== PF_INET6
&& ! seen_ipv6
))
950 /* We cannot possibly return a valid answer. */
954 if (service
&& service
[0])
957 gaih_service
.name
= service
;
958 gaih_service
.num
= strtoul (gaih_service
.name
, &c
, 10);
960 gaih_service
.num
= -1;
962 /* Can't specify a numerical socket unless a protocol family was
964 if (hints
->ai_socktype
== 0 && hints
->ai_protocol
== 0)
966 pservice
= &gaih_service
;
978 if (hints
->ai_family
== g
->family
|| hints
->ai_family
== AF_UNSPEC
)
981 if (pg
== NULL
|| pg
->gaih
!= g
->gaih
)
984 i
= g
->gaih (name
, pservice
, hints
, end
);
987 /* EAI_NODATA is a more specific result as it says that
988 we found a result but it is not usable. */
989 if (last_i
!= (GAIH_OKIFUNSPEC
| -EAI_NODATA
))
992 if (hints
->ai_family
== AF_UNSPEC
&& (i
& GAIH_OKIFUNSPEC
))
1000 return -(i
& GAIH_EAI
);
1003 while(*end
) end
= &((*end
)->ai_next
);
1018 if (pai
== NULL
&& last_i
== 0)
1023 return last_i
? -(last_i
& GAIH_EAI
) : EAI_NONAME
;
1025 libc_hidden_def (getaddrinfo
)
1027 static_link_warning (getaddrinfo
)
1030 freeaddrinfo (struct addrinfo
*ai
)
1041 libc_hidden_def (freeaddrinfo
)