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>
54 #define GAIH_OKIFUNSPEC 0x0100
55 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
58 #define UNIX_PATH_MAX 108
69 struct gaih_servtuple
*next
;
75 static const struct gaih_servtuple nullserv
;
79 struct gaih_addrtuple
*next
;
93 /* Values for `protoflag'. */
94 #define GAI_PROTO_NOSERVICE 1
95 #define GAI_PROTO_PROTOANY 2
97 static const struct gaih_typeproto gaih_inet_typeproto
[] =
100 { SOCK_STREAM
, IPPROTO_TCP
, "tcp", 0 },
101 { SOCK_DGRAM
, IPPROTO_UDP
, "udp", 0 },
102 { SOCK_RAW
, 0, "raw", GAI_PROTO_PROTOANY
|GAI_PROTO_NOSERVICE
},
109 int (*gaih
)(const char *name
, const struct gaih_service
*service
,
110 const struct addrinfo
*req
, struct addrinfo
**pai
);
114 static const struct addrinfo default_hints
;
116 static const struct addrinfo default_hints
=
117 { 0, PF_UNSPEC
, 0, 0, 0, NULL
, NULL
, NULL
};
122 /* Using Unix sockets this way is a security risk. */
124 gaih_local (const char *name
, const struct gaih_service
*service
,
125 const struct addrinfo
*req
, struct addrinfo
**pai
)
127 struct utsname utsname
;
129 if ((name
!= NULL
) && (req
->ai_flags
& AI_NUMERICHOST
))
130 return GAIH_OKIFUNSPEC
| -EAI_NONAME
;
132 if ((name
!= NULL
) || (req
->ai_flags
& AI_CANONNAME
))
133 if (uname (&utsname
) < 0)
138 if (strcmp(name
, "localhost") &&
139 strcmp(name
, "local") &&
140 strcmp(name
, "unix") &&
141 strcmp(name
, utsname
.nodename
))
142 return GAIH_OKIFUNSPEC
| -EAI_NONAME
;
145 if (req
->ai_protocol
|| req
->ai_socktype
)
147 const struct gaih_typeproto
*tp
= gaih_inet_typeproto
+ 1;
150 && ((tp
->protoflag
& GAI_PROTO_NOSERVICE
) != 0
151 || (req
->ai_socktype
!= 0 && req
->ai_socktype
!= tp
->socktype
)
152 || (req
->ai_protocol
!= 0
153 && !(tp
->protoflag
& GAI_PROTO_PROTOANY
)
154 && req
->ai_protocol
!= tp
->protocol
)))
159 if (req
->ai_socktype
)
160 return (GAIH_OKIFUNSPEC
| -EAI_SOCKTYPE
);
162 return (GAIH_OKIFUNSPEC
| -EAI_SERVICE
);
166 *pai
= malloc (sizeof (struct addrinfo
) + sizeof (struct sockaddr_un
)
167 + ((req
->ai_flags
& AI_CANONNAME
)
168 ? (strlen(utsname
.nodename
) + 1): 0));
172 (*pai
)->ai_next
= NULL
;
173 (*pai
)->ai_flags
= req
->ai_flags
;
174 (*pai
)->ai_family
= AF_LOCAL
;
175 (*pai
)->ai_socktype
= req
->ai_socktype
? req
->ai_socktype
: SOCK_STREAM
;
176 (*pai
)->ai_protocol
= req
->ai_protocol
;
177 (*pai
)->ai_addrlen
= sizeof (struct sockaddr_un
);
178 (*pai
)->ai_addr
= (void *) (*pai
) + sizeof (struct addrinfo
);
181 ((struct sockaddr_un
*) (*pai
)->ai_addr
)->sun_len
=
182 sizeof (struct sockaddr_un
);
185 ((struct sockaddr_un
*)(*pai
)->ai_addr
)->sun_family
= AF_LOCAL
;
186 memset(((struct sockaddr_un
*)(*pai
)->ai_addr
)->sun_path
, 0, UNIX_PATH_MAX
);
190 struct sockaddr_un
*sunp
= (struct sockaddr_un
*) (*pai
)->ai_addr
;
192 if (strchr (service
->name
, '/') != NULL
)
194 if (strlen (service
->name
) >= sizeof (sunp
->sun_path
))
195 return GAIH_OKIFUNSPEC
| -EAI_SERVICE
;
197 strcpy (sunp
->sun_path
, service
->name
);
201 if (strlen (P_tmpdir
"/") + 1 + strlen (service
->name
) >=
202 sizeof (sunp
->sun_path
))
203 return GAIH_OKIFUNSPEC
| -EAI_SERVICE
;
205 __stpcpy (__stpcpy (sunp
->sun_path
, P_tmpdir
"/"), service
->name
);
210 /* This is a dangerous use of the interface since there is a time
211 window between the test for the file and the actual creation
212 (done by the caller) in which a file with the same name could
214 char *buf
= ((struct sockaddr_un
*) (*pai
)->ai_addr
)->sun_path
;
216 if (__builtin_expect (__path_search (buf
, L_tmpnam
, NULL
, NULL
, 0),
218 || __builtin_expect (__gen_tempname (buf
, __GT_NOCREATE
), 0) != 0)
222 if (req
->ai_flags
& AI_CANONNAME
)
223 (*pai
)->ai_canonname
= strcpy ((char *) *pai
+ sizeof (struct addrinfo
)
224 + sizeof (struct sockaddr_un
),
227 (*pai
)->ai_canonname
= NULL
;
233 gaih_inet_serv (const char *servicename
, const struct gaih_typeproto
*tp
,
234 const struct addrinfo
*req
, struct gaih_servtuple
*st
)
237 size_t tmpbuflen
= 1024;
244 tmpbuf
= __alloca (tmpbuflen
);
246 r
= __getservbyname_r (servicename
, tp
->name
, &ts
, tmpbuf
, tmpbuflen
,
248 if (r
!= 0 || s
== NULL
)
253 return GAIH_OKIFUNSPEC
| -EAI_SERVICE
;
259 st
->socktype
= tp
->socktype
;
260 st
->protocol
= ((tp
->protoflag
& GAI_PROTO_PROTOANY
)
261 ? req
->ai_protocol
: tp
->protocol
);
262 st
->port
= s
->s_port
;
267 #define gethosts(_family, _type) \
277 tmpbuf = __alloca (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); \
311 gaih_inet (const char *name
, const struct gaih_service
*service
,
312 const struct addrinfo
*req
, struct addrinfo
**pai
)
314 const struct gaih_typeproto
*tp
= gaih_inet_typeproto
;
315 struct gaih_servtuple
*st
= (struct gaih_servtuple
*) &nullserv
;
316 struct gaih_addrtuple
*at
= NULL
;
319 if (req
->ai_protocol
|| req
->ai_socktype
)
324 && ((req
->ai_socktype
!= 0 && req
->ai_socktype
!= tp
->socktype
)
325 || (req
->ai_protocol
!= 0
326 && !(tp
->protoflag
& GAI_PROTO_PROTOANY
)
327 && req
->ai_protocol
!= tp
->protocol
)))
332 if (req
->ai_socktype
)
333 return (GAIH_OKIFUNSPEC
| -EAI_SOCKTYPE
);
335 return (GAIH_OKIFUNSPEC
| -EAI_SERVICE
);
341 if ((tp
->protoflag
& GAI_PROTO_NOSERVICE
) != 0)
342 return (GAIH_OKIFUNSPEC
| -EAI_SERVICE
);
344 if (service
->num
< 0)
348 st
= (struct gaih_servtuple
*)
349 __alloca (sizeof (struct gaih_servtuple
));
351 if ((rc
= gaih_inet_serv (service
->name
, tp
, req
, st
)))
356 struct gaih_servtuple
**pst
= &st
;
357 for (tp
++; tp
->name
[0]; tp
++)
359 struct gaih_servtuple
*newp
;
361 if ((tp
->protoflag
& GAI_PROTO_NOSERVICE
) != 0)
364 if (req
->ai_socktype
!= 0
365 && req
->ai_socktype
!= tp
->socktype
)
367 if (req
->ai_protocol
!= 0
368 && !(tp
->protoflag
& GAI_PROTO_PROTOANY
)
369 && req
->ai_protocol
!= tp
->protocol
)
372 newp
= (struct gaih_servtuple
*)
373 __alloca (sizeof (struct gaih_servtuple
));
375 if ((rc
= gaih_inet_serv (service
->name
, tp
, req
, newp
)))
377 if (rc
& GAIH_OKIFUNSPEC
)
385 if (st
== (struct gaih_servtuple
*) &nullserv
)
386 return (GAIH_OKIFUNSPEC
| -EAI_SERVICE
);
391 st
= __alloca (sizeof (struct gaih_servtuple
));
393 st
->socktype
= tp
->socktype
;
394 st
->protocol
= ((tp
->protoflag
& GAI_PROTO_PROTOANY
)
395 ? req
->ai_protocol
: tp
->protocol
);
396 st
->port
= htons (service
->num
);
399 else if (req
->ai_socktype
|| req
->ai_protocol
)
401 st
= __alloca (sizeof (struct gaih_servtuple
));
403 st
->socktype
= tp
->socktype
;
404 st
->protocol
= ((tp
->protoflag
& GAI_PROTO_PROTOANY
)
405 ? req
->ai_protocol
: tp
->protocol
);
410 /* Neither socket type nor protocol is set. Return all socket types
412 struct gaih_servtuple
**lastp
= &st
;
413 for (++tp
; tp
->name
[0]; ++tp
)
415 struct gaih_servtuple
*newp
;
417 newp
= __alloca (sizeof (struct gaih_servtuple
));
419 newp
->socktype
= tp
->socktype
;
420 newp
->protocol
= tp
->protocol
;
430 at
= __alloca (sizeof (struct gaih_addrtuple
));
432 at
->family
= AF_UNSPEC
;
436 if (inet_pton (AF_INET
, name
, at
->addr
) > 0)
438 if (req
->ai_family
== AF_UNSPEC
|| req
->ai_family
== AF_INET
)
439 at
->family
= AF_INET
;
441 return -EAI_ADDRFAMILY
;
444 if (at
->family
== AF_UNSPEC
)
446 char *namebuf
= strdupa (name
);
449 scope_delim
= strchr (namebuf
, SCOPE_DELIMITER
);
450 if (scope_delim
!= NULL
)
453 if (inet_pton (AF_INET6
, namebuf
, at
->addr
) > 0)
455 if (req
->ai_family
== AF_UNSPEC
|| req
->ai_family
== AF_INET6
)
456 at
->family
= AF_INET6
;
458 return -EAI_ADDRFAMILY
;
460 if (scope_delim
!= NULL
)
462 int try_numericscope
= 0;
463 if (IN6_IS_ADDR_LINKLOCAL (at
->addr
)
464 || IN6_IS_ADDR_MC_LINKLOCAL (at
->addr
))
466 at
->scopeid
= if_nametoindex (scope_delim
+ 1);
467 if (at
->scopeid
== 0)
468 try_numericscope
= 1;
471 try_numericscope
= 1;
473 if (try_numericscope
!= 0)
476 assert (sizeof (uint32_t) <= sizeof (unsigned long));
477 at
->scopeid
= (uint32_t) strtoul (scope_delim
+ 1, &end
,
480 return GAIH_OKIFUNSPEC
| -EAI_NONAME
;
486 if (at
->family
== AF_UNSPEC
&& (req
->ai_flags
& AI_NUMERICHOST
) == 0)
489 struct gaih_addrtuple
**pat
= &at
;
492 int old_res_options
= _res
.options
;
494 /* If we are looking for both IPv4 and IPv6 address we don't
495 want the lookup functions to automatically promote IPv4
496 addresses to IPv6 addresses. Currently this is decided
497 by setting the RES_USE_INET6 bit in _res.options. */
498 if (req
->ai_family
== AF_UNSPEC
)
499 _res
.options
&= ~RES_USE_INET6
;
501 if (req
->ai_family
== AF_UNSPEC
|| req
->ai_family
== AF_INET6
)
502 gethosts (AF_INET6
, struct in6_addr
);
503 no_inet6_data
= no_data
;
505 if (req
->ai_family
== AF_UNSPEC
)
506 _res
.options
= old_res_options
;
508 if (req
->ai_family
== AF_UNSPEC
|| req
->ai_family
== AF_INET
)
509 gethosts (AF_INET
, struct in_addr
);
511 if (no_data
!= 0 && no_inet6_data
!= 0)
513 /* If both requests timed out report this. */
514 if (no_data
== EAI_AGAIN
&& no_inet6_data
== EAI_AGAIN
)
517 /* We made requests but they turned out no data. The name
519 return (GAIH_OKIFUNSPEC
| -EAI_NODATA
);
523 if (at
->family
== AF_UNSPEC
)
524 return (GAIH_OKIFUNSPEC
| -EAI_NONAME
);
528 struct gaih_addrtuple
*atr
;
529 atr
= at
= __alloca (sizeof (struct gaih_addrtuple
));
530 memset (at
, '\0', sizeof (struct gaih_addrtuple
));
532 if (req
->ai_family
== 0)
534 at
->next
= __alloca (sizeof (struct gaih_addrtuple
));
535 memset (at
->next
, '\0', sizeof (struct gaih_addrtuple
));
538 if (req
->ai_family
== 0 || req
->ai_family
== AF_INET6
)
540 at
->family
= AF_INET6
;
541 if ((req
->ai_flags
& AI_PASSIVE
) == 0)
542 memcpy (at
->addr
, &in6addr_loopback
, sizeof (struct in6_addr
));
546 if (req
->ai_family
== 0 || req
->ai_family
== AF_INET
)
548 atr
->family
= AF_INET
;
549 if ((req
->ai_flags
& AI_PASSIVE
) == 0)
550 *(uint32_t *) atr
->addr
= htonl (INADDR_LOOPBACK
);
558 const char *c
= NULL
;
559 struct gaih_servtuple
*st2
;
560 struct gaih_addrtuple
*at2
= at
;
561 size_t socklen
, namelen
;
565 buffer is the size of an unformatted IPv6 address in printable format.
569 if (req
->ai_flags
& AI_CANONNAME
)
571 struct hostent
*h
= NULL
;
575 size_t tmpbuflen
= 512;
581 tmpbuf
= __alloca (tmpbuflen
);
583 rc
= __gethostbyaddr_r (at2
->addr
,
584 ((at2
->family
== AF_INET6
)
585 ? sizeof(struct in6_addr
)
586 : sizeof(struct in_addr
)),
587 at2
->family
, &th
, tmpbuf
, tmpbuflen
,
591 while (rc
== errno
&& herrno
== NETDB_INTERNAL
);
593 if (rc
!= 0 && herrno
== NETDB_INTERNAL
)
595 __set_h_errno (herrno
);
603 /* We have to try to get the canonical in some other
604 way. If we are looking for either AF_INET or
605 AF_INET6 try the other line. */
606 if (req
->ai_family
== AF_UNSPEC
)
608 struct addrinfo
*p
= NULL
;
609 struct addrinfo
**end
= &p
;
610 struct addrinfo localreq
= *req
;
611 struct addrinfo
*runp
;
613 localreq
.ai_family
= AF_INET
+ AF_INET6
- at2
->family
;
614 (void) gaih_inet (name
, service
, &localreq
, end
);
619 if (p
->ai_canonname
!= name
)
621 c
= strdupa (p
->ai_canonname
);
624 runp
= runp
->ai_next
;
630 /* If this code is used the missing canonical name is
631 substituted with the name passed in by the user. */
637 return GAIH_OKIFUNSPEC
| -EAI_NONAME
;
639 namelen
= strlen (c
) + 1;
644 if (at2
->family
== AF_INET6
)
647 socklen
= sizeof (struct sockaddr_in6
);
652 socklen
= sizeof (struct sockaddr_in
);
655 for (st2
= st
; st2
!= NULL
; st2
= st2
->next
)
657 *pai
= malloc (sizeof (struct addrinfo
) + socklen
+ namelen
);
661 (*pai
)->ai_flags
= req
->ai_flags
;
662 (*pai
)->ai_family
= family
;
663 (*pai
)->ai_socktype
= st2
->socktype
;
664 (*pai
)->ai_protocol
= st2
->protocol
;
665 (*pai
)->ai_addrlen
= socklen
;
666 (*pai
)->ai_addr
= (void *) (*pai
) + sizeof(struct addrinfo
);
668 (*pai
)->ai_addr
->sa_len
= socklen
;
670 (*pai
)->ai_addr
->sa_family
= family
;
672 if (family
== AF_INET6
)
674 struct sockaddr_in6
*sin6p
=
675 (struct sockaddr_in6
*) (*pai
)->ai_addr
;
677 sin6p
->sin6_flowinfo
= 0;
678 memcpy (&sin6p
->sin6_addr
,
679 at2
->addr
, sizeof (struct in6_addr
));
680 sin6p
->sin6_port
= st2
->port
;
681 sin6p
->sin6_scope_id
= at2
->scopeid
;
685 struct sockaddr_in
*sinp
=
686 (struct sockaddr_in
*) (*pai
)->ai_addr
;
687 memcpy (&sinp
->sin_addr
,
688 at2
->addr
, sizeof (struct in_addr
));
689 sinp
->sin_port
= st2
->port
;
690 memset (sinp
->sin_zero
, '\0', sizeof (sinp
->sin_zero
));
695 (*pai
)->ai_canonname
= ((void *) (*pai
) +
696 sizeof (struct addrinfo
) + socklen
);
697 strcpy ((*pai
)->ai_canonname
, c
);
700 (*pai
)->ai_canonname
= NULL
;
702 (*pai
)->ai_next
= NULL
;
703 pai
= &((*pai
)->ai_next
);
712 static struct gaih gaih
[] =
714 { PF_INET6
, gaih_inet
},
715 { PF_INET
, gaih_inet
},
717 { PF_LOCAL
, gaih_local
},
723 getaddrinfo (const char *name
, const char *service
,
724 const struct addrinfo
*hints
, struct addrinfo
**pai
)
726 int i
= 0, j
= 0, last_i
= 0;
727 struct addrinfo
*p
= NULL
, **end
;
728 struct gaih
*g
= gaih
, *pg
= NULL
;
729 struct gaih_service gaih_service
, *pservice
;
731 if (name
!= NULL
&& name
[0] == '*' && name
[1] == 0)
734 if (service
!= NULL
&& service
[0] == '*' && service
[1] == 0)
737 if (name
== NULL
&& service
== NULL
)
741 hints
= &default_hints
;
743 if (hints
->ai_flags
& ~(AI_PASSIVE
|AI_CANONNAME
|AI_NUMERICHOST
))
746 if ((hints
->ai_flags
& AI_CANONNAME
) && name
== NULL
)
749 if (service
&& service
[0])
752 gaih_service
.name
= service
;
753 gaih_service
.num
= strtoul (gaih_service
.name
, &c
, 10);
755 gaih_service
.num
= -1;
757 /* Can't specify a numerical socket unless a protocol family was
759 if (hints
->ai_socktype
== 0 && hints
->ai_protocol
== 0)
761 pservice
= &gaih_service
;
773 if (hints
->ai_family
== g
->family
|| hints
->ai_family
== AF_UNSPEC
)
776 if (pg
== NULL
|| pg
->gaih
!= g
->gaih
)
779 i
= g
->gaih (name
, pservice
, hints
, end
);
782 /* EAI_NODATA is a more specific result as it says that
783 we found a result but it is not usable. */
784 if (last_i
!= (GAIH_OKIFUNSPEC
| -EAI_NODATA
))
787 if (hints
->ai_family
== AF_UNSPEC
&& (i
& GAIH_OKIFUNSPEC
))
795 return -(i
& GAIH_EAI
);
798 while(*end
) end
= &((*end
)->ai_next
);
813 if (pai
== NULL
&& last_i
== 0)
818 return last_i
? -(last_i
& GAIH_EAI
) : EAI_NONAME
;
820 libc_hidden_def (getaddrinfo
)
823 freeaddrinfo (struct addrinfo
*ai
)
834 libc_hidden_def (freeaddrinfo
)