Support cancellation in librt.
[glibc.git] / sysdeps / posix / getaddrinfo.c
blob3b86b251222b4405a7d9ce01022eb13566e63632
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 <ifaddrs.h>
41 #include <netdb.h>
42 #include <resolv.h>
43 #include <stdbool.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <unistd.h>
48 #include <arpa/inet.h>
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <sys/types.h>
52 #include <sys/un.h>
53 #include <sys/utsname.h>
54 #include <net/if.h>
55 #include <nsswitch.h>
57 #define GAIH_OKIFUNSPEC 0x0100
58 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
60 #ifndef UNIX_PATH_MAX
61 #define UNIX_PATH_MAX 108
62 #endif
64 struct gaih_service
66 const char *name;
67 int num;
70 struct gaih_servtuple
72 struct gaih_servtuple *next;
73 int socktype;
74 int protocol;
75 int port;
78 static const struct gaih_servtuple nullserv;
80 struct gaih_addrtuple
82 struct gaih_addrtuple *next;
83 int family;
84 char addr[16];
85 uint32_t scopeid;
88 struct gaih_typeproto
90 int socktype;
91 int protocol;
92 char name[4];
93 int protoflag;
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[] =
102 { 0, 0, "", 0 },
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 },
106 { 0, 0, "", 0 }
109 struct gaih
111 int family;
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,
120 .ai_socktype = 0,
121 .ai_protocol = 0,
122 .ai_addrlen = 0,
123 .ai_addr = NULL,
124 .ai_canonname = NULL,
125 .ai_next = NULL
129 #if 0
130 /* Using Unix sockets this way is a security risk. */
131 static int
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)
142 return -EAI_SYSTEM;
144 if (name != NULL)
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;
157 while (tp->name[0]
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)))
163 ++tp;
165 if (! tp->name[0])
167 if (req->ai_socktype)
168 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
169 else
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));
177 if (*pai == NULL)
178 return -EAI_MEMORY;
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);
188 #if SALEN
189 ((struct sockaddr_un *) (*pai)->ai_addr)->sun_len =
190 sizeof (struct sockaddr_un);
191 #endif /* SALEN */
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);
196 if (service)
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);
207 else
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);
216 else
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
221 be created. */
222 char *buf = ((struct sockaddr_un *) (*pai)->ai_addr)->sun_path;
224 if (__builtin_expect (__path_search (buf, L_tmpnam, NULL, NULL, 0),
225 0) != 0
226 || __builtin_expect (__gen_tempname (buf, __GT_NOCREATE), 0) != 0)
227 return -EAI_SYSTEM;
230 if (req->ai_flags & AI_CANONNAME)
231 (*pai)->ai_canonname = strcpy ((char *) *pai + sizeof (struct addrinfo)
232 + sizeof (struct sockaddr_un),
233 utsname.nodename);
234 else
235 (*pai)->ai_canonname = NULL;
236 return 0;
238 #endif /* 0 */
240 static int
241 gaih_inet_serv (const char *servicename, const struct gaih_typeproto *tp,
242 const struct addrinfo *req, struct gaih_servtuple *st)
244 struct servent *s;
245 size_t tmpbuflen = 1024;
246 struct servent ts;
247 char *tmpbuf;
248 int r;
252 tmpbuf = __alloca (tmpbuflen);
254 r = __getservbyname_r (servicename, tp->name, &ts, tmpbuf, tmpbuflen,
255 &s);
256 if (r != 0 || s == NULL)
258 if (r == ERANGE)
259 tmpbuflen *= 2;
260 else
261 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
264 while (r);
266 st->next = NULL;
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;
272 return 0;
275 #define gethosts(_family, _type) \
277 int i, herrno; \
278 size_t tmpbuflen; \
279 struct hostent th; \
280 char *tmpbuf = NULL; \
281 tmpbuflen = 512; \
282 no_data = 0; \
283 do { \
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); \
288 if (rc != 0) \
290 if (herrno == NETDB_INTERNAL) \
292 __set_h_errno (herrno); \
293 return -EAI_SYSTEM; \
295 if (herrno == TRY_AGAIN) \
296 no_data = EAI_AGAIN; \
297 else \
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], \
311 sizeof(_type)); \
312 pat = &((*pat)->next); \
314 if (_family == AF_INET6) \
315 got_ipv6 = true; \
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); \
326 if (rc != 0) \
328 if (herrno == NETDB_INTERNAL) \
330 __set_h_errno (herrno); \
331 return -EAI_SYSTEM; \
333 if (herrno == TRY_AGAIN) \
334 no_data = EAI_AGAIN; \
335 else \
336 no_data = herrno == NO_DATA; \
338 else if (h != NULL) \
340 for (i = 0; h->h_addr_list[i]; ++i) \
342 if (*pat == NULL) \
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); \
352 addr[1] = 0; \
353 addr[0] = 0; \
354 pat = &((*pat)->next); \
361 #define gethosts2(_family, _type) \
363 int i, herrno; \
364 size_t tmpbuflen; \
365 struct hostent th; \
366 char *tmpbuf = NULL; \
367 tmpbuflen = 512; \
368 no_data = 0; \
369 do { \
370 tmpbuf = extend_alloca (tmpbuf, tmpbuflen, 2 * tmpbuflen); \
371 rc = 0; \
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) \
376 h = &th; \
377 else \
378 h = NULL; \
379 if (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; \
388 else \
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], \
402 sizeof(_type)); \
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,
411 int *h_errnop);
412 extern service_user *__nss_hosts_database attribute_hidden;
414 static int
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;
421 int rc;
422 bool got_ipv6 = false;
424 if (req->ai_protocol || req->ai_socktype)
426 ++tp;
428 while (tp->name[0]
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)))
433 ++tp;
435 if (! tp->name[0])
437 if (req->ai_socktype)
438 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
439 else
440 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
444 if (service != NULL)
446 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
447 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
449 if (service->num < 0)
451 if (tp->name[0])
453 st = (struct gaih_servtuple *)
454 __alloca (sizeof (struct gaih_servtuple));
456 if ((rc = gaih_inet_serv (service->name, tp, req, st)))
457 return rc;
459 else
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)
467 continue;
469 if (req->ai_socktype != 0
470 && req->ai_socktype != tp->socktype)
471 continue;
472 if (req->ai_protocol != 0
473 && !(tp->protoflag & GAI_PROTO_PROTOANY)
474 && req->ai_protocol != tp->protocol)
475 continue;
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)
483 continue;
484 return rc;
487 *pst = newp;
488 pst = &(newp->next);
490 if (st == (struct gaih_servtuple *) &nullserv)
491 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
494 else
496 st = __alloca (sizeof (struct gaih_servtuple));
497 st->next = NULL;
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));
507 st->next = NULL;
508 st->socktype = tp->socktype;
509 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
510 ? req->ai_protocol : tp->protocol);
511 st->port = 0;
513 else
515 /* Neither socket type nor protocol is set. Return all socket types
516 we know about. */
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));
523 newp->next = NULL;
524 newp->socktype = tp->socktype;
525 newp->protocol = tp->protocol;
526 newp->port = 0;
528 *lastp = newp;
529 lastp = &newp->next;
533 if (name != NULL)
535 at = __alloca (sizeof (struct gaih_addrtuple));
537 at->family = AF_UNSPEC;
538 at->scopeid = 0;
539 at->next = NULL;
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;
552 else
553 return -EAI_ADDRFAMILY;
556 if (at->family == AF_UNSPEC)
558 char *namebuf = strdupa (name);
559 char *scope_delim;
561 scope_delim = strchr (namebuf, SCOPE_DELIMITER);
562 if (scope_delim != NULL)
563 *scope_delim = '\0';
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];
571 else
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;
584 else
585 try_numericscope = 1;
587 if (try_numericscope != 0)
589 char *end;
590 assert (sizeof (uint32_t) <= sizeof (unsigned long));
591 at->scopeid = (uint32_t) strtoul (scope_delim + 1, &end,
592 10);
593 if (*end != '\0')
594 return GAIH_OKIFUNSPEC | -EAI_NONAME;
600 if (at->family == AF_UNSPEC && (req->ai_flags & AI_NUMERICHOST) == 0)
602 struct hostent *h;
603 struct gaih_addrtuple **pat = &at;
604 int no_data = 0;
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;
616 int no_more;
617 nss_gethostbyname2_r fct;
619 if (__nss_hosts_database != NULL)
621 no_more = 0;
622 nip = __nss_hosts_database;
624 else
625 no_more = __nss_database_lookup ("hosts", NULL,
626 "dns [!UNAVAIL=return] files", &nip);
628 _res.options &= ~RES_USE_INET6;
630 while (!no_more)
632 fct = __nss_lookup_function (nip, "gethostbyname2_r");
634 if (fct != NULL)
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)
645 break;
647 /* We can have different states for AF_INET
648 and AF_INET6. Try to find a usefull one for
649 both. */
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)
658 break;
660 if (nip->next == NULL)
661 no_more = -1;
662 else
663 nip = nip->next;
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)
683 return -EAI_AGAIN;
685 /* We made requests but they turned out no data. The name
686 is known, though. */
687 return (GAIH_OKIFUNSPEC | -EAI_NODATA);
691 if (at->family == AF_UNSPEC)
692 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
694 else
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));
711 atr = at->next;
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);
722 if (pai == NULL)
723 return 0;
726 const char *c = NULL;
727 struct gaih_servtuple *st2;
728 struct gaih_addrtuple *at2 = at;
729 size_t socklen, namelen;
730 sa_family_t family;
733 buffer is the size of an unformatted IPv6 address in printable format.
735 while (at2 != NULL)
737 if (req->ai_flags & AI_CANONNAME)
739 struct hostent *h = NULL;
741 int herrno;
742 struct hostent th;
743 size_t tmpbuflen = 512;
744 char *tmpbuf = NULL;
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,
754 &h, &herrno);
757 while (rc == ERANGE && herrno == NETDB_INTERNAL);
759 if (rc != 0 && herrno == NETDB_INTERNAL)
761 __set_h_errno (herrno);
762 return -EAI_SYSTEM;
765 if (h != NULL)
766 c = h->h_name;
767 else
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);
782 runp = p;
783 while (runp != NULL)
785 if (p->ai_canonname != name)
787 c = strdupa (p->ai_canonname);
788 break;
790 runp = runp->ai_next;
793 freeaddrinfo (p);
796 /* If this code is used the missing canonical name is
797 substituted with the name passed in by the user. */
798 if (c == NULL)
799 c = name;
802 if (c == NULL)
803 return GAIH_OKIFUNSPEC | -EAI_NONAME;
805 namelen = strlen (c) + 1;
807 else
808 namelen = 0;
810 if (at2->family == AF_INET6)
812 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. */
818 if (! got_ipv6
819 && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
820 && IN6_IS_ADDR_V4MAPPED (at2->addr))
821 goto ignore;
823 else
825 family = AF_INET;
826 socklen = sizeof (struct sockaddr_in);
829 for (st2 = st; st2 != NULL; st2 = st2->next)
831 *pai = malloc (sizeof (struct addrinfo) + socklen + namelen);
832 if (*pai == NULL)
833 return -EAI_MEMORY;
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);
841 #if SALEN
842 (*pai)->ai_addr->sa_len = socklen;
843 #endif /* SALEN */
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;
857 else
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));
867 if (c)
869 (*pai)->ai_canonname = ((void *) (*pai) +
870 sizeof (struct addrinfo) + socklen);
871 strcpy ((*pai)->ai_canonname, c);
873 else
874 (*pai)->ai_canonname = NULL;
876 (*pai)->ai_next = NULL;
877 pai = &((*pai)->ai_next);
880 ignore:
881 at2 = at2->next;
884 return 0;
887 static struct gaih gaih[] =
889 { PF_INET6, gaih_inet },
890 { PF_INET, gaih_inet },
891 #if 0
892 { PF_LOCAL, gaih_local },
893 #endif
894 { PF_UNSPEC, NULL }
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)
908 name = NULL;
910 if (service != NULL && service[0] == '*' && service[1] == 0)
911 service = NULL;
913 if (name == NULL && service == NULL)
914 return EAI_NONAME;
916 if (hints == NULL)
917 hints = &default_hints;
919 if (hints->ai_flags
920 & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|AI_ADDRCONFIG|AI_V4MAPPED
921 |AI_ALL))
922 return EAI_BADFLAGS;
924 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
925 return EAI_BADFLAGS;
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. */
932 bool seen_ipv4;
933 bool seen_ipv6;
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. */
951 return EAI_NONAME;
954 if (service && service[0])
956 char *c;
957 gaih_service.name = service;
958 gaih_service.num = strtoul (gaih_service.name, &c, 10);
959 if (*c)
960 gaih_service.num = -1;
961 else
962 /* Can't specify a numerical socket unless a protocol family was
963 given. */
964 if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
965 return EAI_SERVICE;
966 pservice = &gaih_service;
968 else
969 pservice = NULL;
971 if (pai)
972 end = &p;
973 else
974 end = NULL;
976 while (g->gaih)
978 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC)
980 j++;
981 if (pg == NULL || pg->gaih != g->gaih)
983 pg = g;
984 i = g->gaih (name, pservice, hints, end);
985 if (i != 0)
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))
990 last_i = i;
992 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
994 ++g;
995 continue;
998 freeaddrinfo (p);
1000 return -(i & GAIH_EAI);
1002 if (end)
1003 while(*end) end = &((*end)->ai_next);
1006 ++g;
1009 if (j == 0)
1010 return EAI_FAMILY;
1012 if (p)
1014 *pai = p;
1015 return 0;
1018 if (pai == NULL && last_i == 0)
1019 return 0;
1021 freeaddrinfo (p);
1023 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
1025 libc_hidden_def (getaddrinfo)
1027 static_link_warning (getaddrinfo)
1029 void
1030 freeaddrinfo (struct addrinfo *ai)
1032 struct addrinfo *p;
1034 while (ai != NULL)
1036 p = ai;
1037 ai = ai->ai_next;
1038 free (p);
1041 libc_hidden_def (freeaddrinfo)