Add.
[libidn.git] / libc / getaddrinfo.c
blobd92efe9009641a8f62aa00a40277aea98ffa71de
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
10 way or form.
11 1. All terms of the all other applicable copyrights and licenses must be
12 followed.
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. */
38 #include <assert.h>
39 #include <errno.h>
40 #include <netdb.h>
41 #include <resolv.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <unistd.h>
46 #include <arpa/inet.h>
47 #include <sys/socket.h>
48 #include <netinet/in.h>
49 #include <sys/types.h>
50 #include <sys/un.h>
51 #include <sys/utsname.h>
52 #include <net/if.h>
53 #include <nsswitch.h>
54 #include <idna.h>
56 #define GAIH_OKIFUNSPEC 0x0100
57 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
59 #ifndef UNIX_PATH_MAX
60 #define UNIX_PATH_MAX 108
61 #endif
63 struct gaih_service
65 const char *name;
66 int num;
69 struct gaih_servtuple
71 struct gaih_servtuple *next;
72 int socktype;
73 int protocol;
74 int port;
77 static const struct gaih_servtuple nullserv;
79 struct gaih_addrtuple
81 struct gaih_addrtuple *next;
82 int family;
83 char addr[16];
84 uint32_t scopeid;
87 struct gaih_typeproto
89 int socktype;
90 int protocol;
91 char name[4];
92 int protoflag;
95 /* Values for `protoflag'. */
96 #define GAI_PROTO_NOSERVICE 1
97 #define GAI_PROTO_PROTOANY 2
99 static const struct gaih_typeproto gaih_inet_typeproto[] =
101 { 0, 0, "", 0 },
102 { SOCK_STREAM, IPPROTO_TCP, "tcp", 0 },
103 { SOCK_DGRAM, IPPROTO_UDP, "udp", 0 },
104 { SOCK_RAW, 0, "raw", GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE },
105 { 0, 0, "", 0 }
108 struct gaih
110 int family;
111 int (*gaih)(const char *name, const struct gaih_service *service,
112 const struct addrinfo *req, struct addrinfo **pai);
115 #if PF_UNSPEC == 0
116 static const struct addrinfo default_hints;
117 #else
118 static const struct addrinfo default_hints =
119 { 0, PF_UNSPEC, 0, 0, 0, NULL, NULL, NULL };
120 #endif
123 #if 0
124 /* Using Unix sockets this way is a security risk. */
125 static int
126 gaih_local (const char *name, const struct gaih_service *service,
127 const struct addrinfo *req, struct addrinfo **pai)
129 struct utsname utsname;
131 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
132 return GAIH_OKIFUNSPEC | -EAI_NONAME;
134 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
135 if (uname (&utsname) < 0)
136 return -EAI_SYSTEM;
138 if (name != NULL)
140 if (strcmp(name, "localhost") &&
141 strcmp(name, "local") &&
142 strcmp(name, "unix") &&
143 strcmp(name, utsname.nodename))
144 return GAIH_OKIFUNSPEC | -EAI_NONAME;
147 if (req->ai_protocol || req->ai_socktype)
149 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
151 while (tp->name[0]
152 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
153 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
154 || (req->ai_protocol != 0
155 && !(tp->protoflag & GAI_PROTO_PROTOANY)
156 && req->ai_protocol != tp->protocol)))
157 ++tp;
159 if (! tp->name[0])
161 if (req->ai_socktype)
162 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
163 else
164 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
168 *pai = malloc (sizeof (struct addrinfo) + sizeof (struct sockaddr_un)
169 + ((req->ai_flags & AI_CANONNAME)
170 ? (strlen(utsname.nodename) + 1): 0));
171 if (*pai == NULL)
172 return -EAI_MEMORY;
174 (*pai)->ai_next = NULL;
175 (*pai)->ai_flags = req->ai_flags;
176 (*pai)->ai_family = AF_LOCAL;
177 (*pai)->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
178 (*pai)->ai_protocol = req->ai_protocol;
179 (*pai)->ai_addrlen = sizeof (struct sockaddr_un);
180 (*pai)->ai_addr = (void *) (*pai) + sizeof (struct addrinfo);
182 #if SALEN
183 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
184 sizeof (struct sockaddr_un);
185 #endif /* SALEN */
187 ((struct sockaddr_un *)(*pai)->ai_addr)->sun_family = AF_LOCAL;
188 memset(((struct sockaddr_un *)(*pai)->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
190 if (service)
192 struct sockaddr_un *sunp = (struct sockaddr_un *) (*pai)->ai_addr;
194 if (strchr (service->name, '/') != NULL)
196 if (strlen (service->name) >= sizeof (sunp->sun_path))
197 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
199 strcpy (sunp->sun_path, service->name);
201 else
203 if (strlen (P_tmpdir "/") + 1 + strlen (service->name) >=
204 sizeof (sunp->sun_path))
205 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
207 __stpcpy (__stpcpy (sunp->sun_path, P_tmpdir "/"), service->name);
210 else
212 /* This is a dangerous use of the interface since there is a time
213 window between the test for the file and the actual creation
214 (done by the caller) in which a file with the same name could
215 be created. */
216 char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
218 if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
219 0) != 0
220 || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
221 return -EAI_SYSTEM;
224 if (req->ai_flags & AI_CANONNAME)
225 (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
226 + sizeof (struct sockaddr_un),
227 utsname.nodename);
228 else
229 (*pai)->ai_canonname = NULL;
230 return 0;
232 #endif /* 0 */
234 static int
235 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
236 const struct addrinfo *req, struct gaih_servtuple *st)
238 struct servent *s;
239 size_t tmpbuflen = 1024;
240 struct servent ts;
241 char *tmpbuf;
242 int r;
246 tmpbuf = __alloca (tmpbuflen);
248 r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
249 &s);
250 if (r != 0 || s == NULL)
252 if (r == ERANGE)
253 tmpbuflen *= 2;
254 else
255 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
258 while (r);
260 st->next = NULL;
261 st->socktype = tp->socktype;
262 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
263 ? req->ai_protocol : tp->protocol);
264 st->port = s->s_port;
266 return 0;
269 #define gethosts(_family, _type) \
271 int i, herrno; \
272 size_t tmpbuflen; \
273 struct hostent th; \
274 char *tmpbuf = NULL; \
275 tmpbuflen = 512; \
276 no_data = 0; \
277 do { \
278 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
279 rc = __gethostbyname2_r (name, _family, &th, tmpbuf, \
280 tmpbuflen, &h, &herrno); \
281 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
282 if (rc != 0) \
284 if (herrno == NETDB_INTERNAL) \
286 __set_h_errno (herrno); \
287 return -EAI_SYSTEM; \
289 if (herrno == TRY_AGAIN) \
290 no_data = EAI_AGAIN; \
291 else \
292 no_data = herrno == NO_DATA; \
294 else if (h != NULL) \
296 for (i = 0; h->h_addr_list[i]; i++) \
298 if (*pat == NULL) { \
299 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
300 (*pat)->scopeid = 0; \
302 (*pat)->next = NULL; \
303 (*pat)->family = _family; \
304 memcpy ((*pat)->addr, h->h_addr_list[i], \
305 sizeof(_type)); \
306 pat = &((*pat)->next); \
311 #define gethosts2(_family, _type) \
313 int i, herrno; \
314 size_t tmpbuflen; \
315 struct hostent th; \
316 char *tmpbuf = NULL; \
317 tmpbuflen = 512; \
318 no_data = 0; \
319 do { \
320 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
321 rc = 0; \
322 status = DL_CALL_FCT (fct, (name, _family, &th, tmpbuf, \
323 tmpbuflen, &rc, &herrno)); \
324 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
325 if (status == NSS_STATUS_SUCCESS && rc == 0) \
326 h = &th; \
327 else \
328 h = NULL; \
329 if (rc != 0) \
331 if (herrno == NETDB_INTERNAL) \
333 __set_h_errno (herrno); \
334 return -EAI_SYSTEM; \
336 if (herrno == TRY_AGAIN) \
337 no_data = EAI_AGAIN; \
338 else \
339 no_data = herrno == NO_DATA; \
341 else if (h != NULL) \
343 for (i = 0; h->h_addr_list[i]; i++) \
345 if (*pat == NULL) { \
346 *pat = __alloca (sizeof (struct gaih_addrtuple)); \
347 (*pat)->scopeid = 0; \
349 (*pat)->next = NULL; \
350 (*pat)->family = _family; \
351 memcpy ((*pat)->addr, h->h_addr_list[i], \
352 sizeof(_type)); \
353 pat = &((*pat)->next); \
358 typedef enum nss_status (*nss_gethostbyname2_r)
359 (const char *name, int af, struct hostent *host,
360 char *buffer, size_t buflen, int *errnop,
361 int *h_errnop);
362 extern service_user *__nss_hosts_database attribute_hidden;
364 static int
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;
371 int rc;
373 if (req->ai_protocol || req->ai_socktype)
375 ++tp;
377 while (tp->name[0]
378 && ((req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
379 || (req->ai_protocol != 0
380 && !(tp->protoflag & GAI_PROTO_PROTOANY)
381 && req->ai_protocol != tp->protocol)))
382 ++tp;
384 if (! tp->name[0])
386 if (req->ai_socktype)
387 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
388 else
389 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
393 if (service != NULL)
395 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
396 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
398 if (service->num < 0)
400 if (tp->name[0])
402 st = (struct gaih_servtuple *)
403 __alloca (sizeof (struct gaih_servtuple));
405 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
406 return rc;
408 else
410 struct gaih_servtuple **pst = &st;
411 for (tp++; tp->name[0]; tp++)
413 struct gaih_servtuple *newp;
415 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
416 continue;
418 if (req->ai_socktype != 0
419 && req->ai_socktype != tp->socktype)
420 continue;
421 if (req->ai_protocol != 0
422 && !(tp->protoflag & GAI_PROTO_PROTOANY)
423 && req->ai_protocol != tp->protocol)
424 continue;
426 newp = (struct gaih_servtuple *)
427 __alloca (sizeof (struct gaih_servtuple));
429 if ((rc = gaih_inet_serv (service->name, tp, req, newp)))
431 if (rc & GAIH_OKIFUNSPEC)
432 continue;
433 return rc;
436 *pst = newp;
437 pst = &(newp->next);
439 if (st == (struct gaih_servtuple *) &nullserv)
440 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
443 else
445 st = __alloca (sizeof (struct gaih_servtuple));
446 st->next = NULL;
447 st->socktype = tp->socktype;
448 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
449 ? req->ai_protocol : tp->protocol);
450 st->port = htons (service->num);
453 else if (req->ai_socktype || req->ai_protocol)
455 st = __alloca (sizeof (struct gaih_servtuple));
456 st->next = NULL;
457 st->socktype = tp->socktype;
458 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
459 ? req->ai_protocol : tp->protocol);
460 st->port = 0;
462 else
464 /* Neither socket type nor protocol is set. Return all socket types
465 we know about. */
466 struct gaih_servtuple **lastp = &st;
467 for (++tp; tp->name[0]; ++tp)
469 struct gaih_servtuple *newp;
471 newp = __alloca (sizeof (struct gaih_servtuple));
472 newp->next = NULL;
473 newp->socktype = tp->socktype;
474 newp->protocol = tp->protocol;
475 newp->port = 0;
477 *lastp = newp;
478 lastp = &newp->next;
482 if (name != NULL)
484 at = __alloca (sizeof (struct gaih_addrtuple));
486 at->family = AF_UNSPEC;
487 at->scopeid = 0;
488 at->next = NULL;
490 if (req->ai_flags & AI_IDN)
492 char *p;
493 rc = idna_to_ascii_lz (name, &p, 0);
494 if (rc != IDNA_SUCCESS)
495 return -EAI_IDN_ENCODE;
496 name = p; /* XXX memory leak */
499 if (inet_pton (AF_INET, name, at->addr) > 0)
501 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
502 at->family = AF_INET;
503 else
504 return -EAI_ADDRFAMILY;
507 if (at->family == AF_UNSPEC)
509 char *namebuf = strdupa (name);
510 char *scope_delim;
512 scope_delim = strchr (namebuf, SCOPE_DELIMITER);
513 if (scope_delim != NULL)
514 *scope_delim = '\0';
516 if (inet_pton (AF_INET6, namebuf, at->addr) > 0)
518 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
519 at->family = AF_INET6;
520 else
521 return -EAI_ADDRFAMILY;
523 if (scope_delim != NULL)
525 int try_numericscope = 0;
526 if (IN6_IS_ADDR_LINKLOCAL (at->addr)
527 || IN6_IS_ADDR_MC_LINKLOCAL (at->addr))
529 at->scopeid = if_nametoindex (scope_delim + 1);
530 if (at->scopeid == 0)
531 try_numericscope = 1;
533 else
534 try_numericscope = 1;
536 if (try_numericscope != 0)
538 char *end;
539 assert (sizeof (uint32_t) <= sizeof (unsigned long));
540 at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
541 10);
542 if (*end != '\0')
543 return GAIH_OKIFUNSPEC | -EAI_NONAME;
549 if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
551 struct hostent *h;
552 struct gaih_addrtuple **pat = &at;
553 int no_data = 0;
554 int no_inet6_data = 0;
555 int old_res_options = _res.options;
557 /* If we are looking for both IPv4 and IPv6 address we don't
558 want the lookup functions to automatically promote IPv4
559 addresses to IPv6 addresses. Currently this is decided
560 by setting the RES_USE_INET6 bit in _res.options. */
561 if (req->ai_family == AF_UNSPEC)
563 service_user *nip = NULL;
564 enum nss_status inet6_status, status = NSS_STATUS_UNAVAIL;
565 int no_more;
566 nss_gethostbyname2_r fct;
568 if (__nss_hosts_database != NULL)
570 no_more = 0;
571 nip = __nss_hosts_database;
573 else
574 no_more = __nss_database_lookup ("hosts", NULL,
575 "dns [!UNAVAIL=return] files", &nip);
577 _res.options &= ~RES_USE_INET6;
579 while (!no_more)
581 fct = __nss_lookup_function (nip, "gethostbyname2_r");
583 if (fct != NULL)
585 gethosts2 (AF_INET6, struct in6_addr);
586 no_inet6_data = no_data;
587 inet6_status = status;
588 gethosts2 (AF_INET, struct in_addr);
590 /* If we found one address for AF_INET or AF_INET6,
591 don't continue the search. */
592 if (inet6_status == NSS_STATUS_SUCCESS ||
593 status == NSS_STATUS_SUCCESS)
594 break;
596 /* We can have different states for AF_INET
597 and AF_INET6. Try to find a usefull one for
598 both. */
599 if (inet6_status == NSS_STATUS_TRYAGAIN)
600 status = NSS_STATUS_TRYAGAIN;
601 else if (status == NSS_STATUS_UNAVAIL &&
602 inet6_status != NSS_STATUS_UNAVAIL)
603 status = inet6_status;
606 if (nss_next_action (nip, status) == NSS_ACTION_RETURN)
607 break;
609 if (nip->next == NULL)
610 no_more = -1;
611 else
612 nip = nip->next;
615 _res.options = old_res_options;
617 else if (req->ai_family == AF_INET6)
619 gethosts (AF_INET6, struct in6_addr);
620 no_inet6_data = no_data;
622 else if (req->ai_family == AF_INET)
623 gethosts (AF_INET, struct in_addr);
625 if (no_data != 0 && no_inet6_data != 0)
627 /* If both requests timed out report this. */
628 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
629 return -EAI_AGAIN;
631 /* We made requests but they turned out no data. The name
632 is known, though. */
633 return (GAIH_OKIFUNSPEC | -EAI_NODATA);
637 if (at->family == AF_UNSPEC)
638 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
640 else
642 struct gaih_addrtuple *atr;
643 atr = at = __alloca (sizeof (struct gaih_addrtuple));
644 memset (at, '\0', sizeof (struct gaih_addrtuple));
646 if (req->ai_family == 0)
648 at->next = __alloca (sizeof (struct gaih_addrtuple));
649 memset (at->next, '\0', sizeof (struct gaih_addrtuple));
652 if (req->ai_family == 0 || req->ai_family == AF_INET6)
654 at->family = AF_INET6;
655 if ((req->ai_flags & AI_PASSIVE) == 0)
656 memcpy (at->addr, &in6addr_loopback, sizeof (struct in6_addr));
657 atr = at->next;
660 if (req->ai_family == 0 || req->ai_family == AF_INET)
662 atr->family = AF_INET;
663 if ((req->ai_flags & AI_PASSIVE) == 0)
664 *(uint32_t *) atr->addr = htonl (INADDR_LOOPBACK);
668 if (pai == NULL)
669 return 0;
672 const char *c = NULL;
673 struct gaih_servtuple *st2;
674 struct gaih_addrtuple *at2 = at;
675 size_t socklen, namelen;
676 sa_family_t family;
679 buffer is the size of an unformatted IPv6 address in printable format.
681 while (at2 != NULL)
683 if (req->ai_flags & AI_CANONNAME)
685 struct hostent *h = NULL;
687 int herrno;
688 struct hostent th;
689 size_t tmpbuflen = 512;
690 char *tmpbuf;
694 tmpbuflen *= 2;
695 tmpbuf = __alloca (tmpbuflen);
697 rc = __gethostbyaddr_r (at2->addr,
698 ((at2->family == AF_INET6)
699 ? sizeof(struct in6_addr)
700 : sizeof(struct in_addr)),
701 at2->family, &th, tmpbuf, tmpbuflen,
702 &h, &herrno);
705 while (rc == errno && herrno == NETDB_INTERNAL);
707 if (rc != 0 && herrno == NETDB_INTERNAL)
709 __set_h_errno (herrno);
710 return -EAI_SYSTEM;
713 if (h != NULL)
714 c = h->h_name;
715 else
717 /* We have to try to get the canonical in some other
718 way. If we are looking for either AF_INET or
719 AF_INET6 try the other line. */
720 if (req->ai_family == AF_UNSPEC)
722 struct addrinfo *p = NULL;
723 struct addrinfo **end = &p;
724 struct addrinfo localreq = *req;
725 struct addrinfo *runp;
727 localreq.ai_family = AF_INET + AF_INET6 - at2->family;
728 (void) gaih_inet (name, service, &localreq, end);
730 runp = p;
731 while (runp != NULL)
733 if (p->ai_canonname != name)
735 c = strdupa (p->ai_canonname);
736 break;
738 runp = runp->ai_next;
741 freeaddrinfo (p);
744 /* If this code is used the missing canonical name is
745 substituted with the name passed in by the user. */
746 if (c == NULL)
747 c = name;
750 if (c == NULL)
751 return GAIH_OKIFUNSPEC | -EAI_NONAME;
753 namelen = strlen (c) + 1;
755 else
756 namelen = 0;
758 if (at2->family == AF_INET6)
760 family = AF_INET6;
761 socklen = sizeof (struct sockaddr_in6);
763 else
765 family = AF_INET;
766 socklen = sizeof (struct sockaddr_in);
769 for (st2 = st; st2 != NULL; st2 = st2->next)
771 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
772 if (*pai == NULL)
773 return -EAI_MEMORY;
775 (*pai)->ai_flags = req->ai_flags;
776 (*pai)->ai_family = family;
777 (*pai)->ai_socktype = st2->socktype;
778 (*pai)->ai_protocol = st2->protocol;
779 (*pai)->ai_addrlen = socklen;
780 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
781 #if SALEN
782 (*pai)->ai_addr->sa_len = socklen;
783 #endif /* SALEN */
784 (*pai)->ai_addr->sa_family = family;
786 if (family == AF_INET6)
788 struct sockaddr_in6 *sin6p =
789 (struct sockaddr_in6 *) (*pai)->ai_addr;
791 sin6p->sin6_flowinfo = 0;
792 memcpy (&sin6p->sin6_addr,
793 at2->addr, sizeof (struct in6_addr));
794 sin6p->sin6_port = st2->port;
795 sin6p->sin6_scope_id = at2->scopeid;
797 else
799 struct sockaddr_in *sinp =
800 (struct sockaddr_in *) (*pai)->ai_addr;
801 memcpy (&sinp->sin_addr,
802 at2->addr, sizeof (struct in_addr));
803 sinp->sin_port = st2->port;
804 memset (sinp->sin_zero, '\0', sizeof (sinp->sin_zero));
807 if (c)
809 (*pai)->ai_canonname = ((void *) (*pai) +
810 sizeof (struct addrinfo) + socklen);
811 strcpy ((*pai)->ai_canonname, c);
813 else
814 (*pai)->ai_canonname = NULL;
816 (*pai)->ai_next = NULL;
817 pai = &((*pai)->ai_next);
820 at2 = at2->next;
823 return 0;
826 static struct gaih gaih[] =
828 { PF_INET6, gaih_inet },
829 { PF_INET, gaih_inet },
830 #if 0
831 { PF_LOCAL, gaih_local },
832 #endif
833 { PF_UNSPEC, NULL }
837 getaddrinfo (const char *name, const char *service,
838 const struct addrinfo *hints, struct addrinfo **pai)
840 int i = 0, j = 0, last_i = 0;
841 struct addrinfo *p = NULL, **end;
842 struct gaih *g = gaih, *pg = NULL;
843 struct gaih_service gaih_service, *pservice;
845 if (name != NULL && name[0] == '*' && name[1] == 0)
846 name = NULL;
848 if (service != NULL && service[0] == '*' && service[1] == 0)
849 service = NULL;
851 if (name == NULL && service == NULL)
852 return EAI_NONAME;
854 if (hints == NULL)
855 hints = &default_hints;
857 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_IDN))
858 return EAI_BADFLAGS;
860 if ((hints->ai_flags & (AI_CANONNAME|AI_IDN)) && name == NULL)
861 return EAI_BADFLAGS;
863 if (service && service[0])
865 char *c;
866 gaih_service.name = service;
867 gaih_service.num = strtoul (gaih_service.name, &c, 10);
868 if (*c)
869 gaih_service.num = -1;
870 else
871 /* Can't specify a numerical socket unless a protocol family was
872 given. */
873 if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
874 return EAI_SERVICE;
875 pservice = &gaih_service;
877 else
878 pservice = NULL;
880 if (pai)
881 end = &p;
882 else
883 end = NULL;
885 while (g->gaih)
887 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
889 j++;
890 if (pg == NULL || pg->gaih != g->gaih)
892 pg = g;
893 i = g->gaih (name, pservice, hints, end);
894 if (i != 0)
896 /* EAI_NODATA is a more specific result as it says that
897 we found a result but it is not usable. */
898 if (last_i != (GAIH_OKIFUNSPEC | -EAI_NODATA))
899 last_i = i;
901 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
903 ++g;
904 continue;
907 freeaddrinfo (p);
909 return -(i & GAIH_EAI);
911 if (end)
912 while(*end) end = &((*end)->ai_next);
915 ++g;
918 if (j == 0)
919 return EAI_FAMILY;
921 if (p)
923 *pai = p;
924 return 0;
927 if (pai == NULL && last_i == 0)
928 return 0;
930 freeaddrinfo (p);
932 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
934 libc_hidden_def (getaddrinfo)
936 void
937 freeaddrinfo (struct addrinfo *ai)
939 struct addrinfo *p;
941 while (ai != NULL)
943 p = ai;
944 ai = ai->ai_next;
945 free (p);
948 libc_hidden_def (freeaddrinfo)