mkostemp: fix implementation
[uclibc-ng.git] / libc / inet / getaddrinfo.c
blob168adb1156b45ca1f1817cd23aff2c4dcb88210f
1 /*
2 * Copyright 1996 by Craig Metz
3 * Copyright (C) 2000-2006 Erik Andersen <andersen@uclibc.org>
4 * Portions from the GNU C library,
5 * Copyright (C) 2003, 2006 Free Software Foundation, Inc.
7 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
8 */
10 /* $USAGI: getaddrinfo.c,v 1.16 2001/10/04 09:52:03 sekiya Exp $ */
12 /* The Inner Net License, Version 2.00
14 The author(s) grant permission for redistribution and use in source and
15 binary forms, with or without modification, of the software and documentation
16 provided that the following conditions are met:
18 0. If you receive a version of the software that is specifically labelled
19 as not being for redistribution (check the version message and/or README),
20 you are not permitted to redistribute that version of the software in any
21 way or form.
22 1. All terms of the all other applicable copyrights and licenses must be
23 followed.
24 2. Redistributions of source code must retain the authors' copyright
25 notice(s), this list of conditions, and the following disclaimer.
26 3. Redistributions in binary form must reproduce the authors' copyright
27 notice(s), this list of conditions, and the following disclaimer in the
28 documentation and/or other materials provided with the distribution.
29 4. All advertising materials mentioning features or use of this software
30 must display the following acknowledgement with the name(s) of the
31 authors as specified in the copyright notice(s) substituted where
32 indicated:
34 This product includes software developed by <name(s)>, The Inner
35 Net, and other contributors.
37 5. Neither the name(s) of the author(s) nor the names of its contributors
38 may be used to endorse or promote products derived from this software
39 without specific prior written permission.
41 THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
42 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
43 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44 DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
45 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
46 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
48 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
49 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
50 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 If these license terms cause you a real problem, contact the author. */
54 #include <assert.h>
55 #include <errno.h>
56 #include <netdb.h>
57 #ifdef __UCLIBC_HAS_TLS__
58 #include <tls.h>
59 #endif
60 #include <resolv.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <unistd.h>
65 #include <arpa/inet.h>
66 #include <sys/socket.h>
67 #include <netinet/in.h>
68 #include <sys/types.h>
69 #include <sys/un.h>
70 #include <sys/utsname.h>
71 #include <net/if.h>
72 #include <ifaddrs.h>
74 #define GAIH_OKIFUNSPEC 0x0100
75 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
77 #ifndef UNIX_PATH_MAX
78 #define UNIX_PATH_MAX 108
79 #endif
81 /* Useful for having small structure members/global variables */
82 typedef int8_t socktype_t;
83 typedef int8_t family_t;
84 typedef int8_t protocol_t;
85 struct BUG_too_small {
86 char BUG_socktype_t_too_small[(0
87 | SOCK_STREAM
88 | SOCK_DGRAM
89 | SOCK_RAW
90 ) <= 127 ? 1 : -1];
91 char BUG_family_t_too_small[(0
92 | AF_UNSPEC
93 | AF_INET
94 | AF_INET6
95 ) <= 127 ? 1 : -1];
96 char BUG_protocol_t_too_small[(0
97 | IPPROTO_TCP
98 | IPPROTO_UDP
99 ) <= 127 ? 1 : -1];
102 struct gaih_service {
103 const char *name;
104 int num;
107 struct gaih_servtuple {
108 struct gaih_servtuple *next;
109 int socktype;
110 int protocol;
111 int port;
114 struct gaih_addrtuple {
115 struct gaih_addrtuple *next;
116 int family;
117 char addr[16];
118 uint32_t scopeid;
121 struct gaih_typeproto {
122 socktype_t socktype;
123 protocol_t protocol;
124 int8_t protoflag;
125 char name[4];
127 /* Values for `protoflag'. */
128 #define GAI_PROTO_NOSERVICE 1
129 #define GAI_PROTO_PROTOANY 2
131 static const struct gaih_typeproto gaih_inet_typeproto[] = {
132 { 0 , 0 , 0, "" },
133 { SOCK_STREAM, IPPROTO_TCP, 0, "tcp" },
134 { SOCK_DGRAM , IPPROTO_UDP, 0, "udp" },
135 { SOCK_RAW , 0 , GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, "raw" },
136 { 0 , 0 , 0, "" },
139 struct gaih {
140 int family;
141 int (*gaih)(const char *name, const struct gaih_service *service,
142 const struct addrinfo *req, struct addrinfo **pai);
145 #define SEEN_IPV4 1
146 #define SEEN_IPV6 2
148 static unsigned __check_pf(void)
150 unsigned seen = 0;
152 #if defined __UCLIBC_SUPPORT_AI_ADDRCONFIG__
154 struct ifaddrs *ifa;
155 struct ifaddrs *runp;
157 /* Get the interface list via getifaddrs. */
158 if (getifaddrs(&ifa) != 0) {
159 /* We cannot determine what interfaces are available.
160 * Be optimistic. */
161 #if defined __UCLIBC_HAS_IPV4__
162 seen |= SEEN_IPV4;
163 #endif
164 #if defined __UCLIBC_HAS_IPV6__
165 seen |= SEEN_IPV6;
166 #endif
167 return seen;
170 for (runp = ifa; runp != NULL; runp = runp->ifa_next) {
171 if (runp->ifa_addr == NULL)
172 continue;
173 #if defined __UCLIBC_HAS_IPV4__
174 if (runp->ifa_addr->sa_family == PF_INET)
175 seen |= SEEN_IPV4;
176 #endif
177 #if defined __UCLIBC_HAS_IPV6__
178 if (runp->ifa_addr->sa_family == PF_INET6)
179 seen |= SEEN_IPV6;
180 #endif
182 freeifaddrs(ifa);
184 #else
186 /* AI_ADDRCONFIG is disabled, assume both ipv4 and ipv6 available. */
187 #if defined __UCLIBC_HAS_IPV4__
188 seen |= SEEN_IPV4;
189 #endif
190 #if defined __UCLIBC_HAS_IPV6__
191 seen |= SEEN_IPV6;
192 #endif
194 #endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */
196 return seen;
199 static int addrconfig(sa_family_t af)
201 int s;
202 int ret;
203 int saved_errno = errno;
204 unsigned seen;
206 seen = __check_pf();
207 #if defined __UCLIBC_HAS_IPV4__
208 if (af == AF_INET)
209 ret = seen & SEEN_IPV4;
210 else
211 #endif
212 #if defined __UCLIBC_HAS_IPV6__
213 if (af == AF_INET6)
214 ret = seen & SEEN_IPV6;
215 else
216 #endif
218 s = socket(af, SOCK_DGRAM, 0);
219 ret = 1; /* Assume PF_UNIX. */
220 if (s < 0) {
221 if (errno != EMFILE)
222 ret = 0;
223 } else
224 close(s);
226 __set_errno(saved_errno);
227 return ret;
230 #if 0
231 /* Using Unix sockets this way is a security risk. */
232 static int
233 gaih_local(const char *name, const struct gaih_service *service,
234 const struct addrinfo *req, struct addrinfo **pai)
236 struct utsname utsname;
237 struct addrinfo *ai = *pai;
239 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
240 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
242 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
243 if (uname(&utsname) < 0)
244 return -EAI_SYSTEM;
246 if (name != NULL) {
247 if (strcmp(name, "localhost") &&
248 strcmp(name, "local") &&
249 strcmp(name, "unix") &&
250 strcmp(name, utsname.nodename))
251 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
254 if (req->ai_protocol || req->ai_socktype) {
255 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
257 while (tp->name[0]
258 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
259 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
260 || (req->ai_protocol != 0 && !(tp->protoflag & GAI_PROTO_PROTOANY) && req->ai_protocol != tp->protocol))
262 ++tp;
264 if (! tp->name[0]) {
265 if (req->ai_socktype)
266 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
267 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
271 *pai = ai = malloc(sizeof(struct addrinfo) + sizeof(struct sockaddr_un)
272 + ((req->ai_flags & AI_CANONNAME)
273 ? (strlen(utsname.nodename) + 1) : 0));
274 if (ai == NULL)
275 return -EAI_MEMORY;
277 ai->ai_next = NULL;
278 ai->ai_flags = req->ai_flags;
279 ai->ai_family = AF_LOCAL;
280 ai->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
281 ai->ai_protocol = req->ai_protocol;
282 ai->ai_addrlen = sizeof(struct sockaddr_un);
283 ai->ai_addr = (void *)ai + sizeof(struct addrinfo);
284 #if 0 /* SALEN */
285 ((struct sockaddr_un *)ai->ai_addr)->sun_len = sizeof(struct sockaddr_un);
286 #endif /* SALEN */
288 ((struct sockaddr_un *)ai->ai_addr)->sun_family = AF_LOCAL;
289 memset(((struct sockaddr_un *)ai->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
291 if (service) {
292 struct sockaddr_un *sunp = (struct sockaddr_un *)ai->ai_addr;
294 if (strchr(service->name, '/') != NULL) {
295 if (strlen(service->name) >= sizeof(sunp->sun_path))
296 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
297 strcpy(sunp->sun_path, service->name);
298 } else {
299 if (strlen(P_tmpdir "/") + 1 + strlen(service->name) >= sizeof(sunp->sun_path))
300 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
301 stpcpy(stpcpy(sunp->sun_path, P_tmpdir "/"), service->name);
303 } else {
304 /* This is a dangerous use of the interface since there is a time
305 window between the test for the file and the actual creation
306 (done by the caller) in which a file with the same name could
307 be created. */
308 char *buf = ((struct sockaddr_un *)ai->ai_addr)->sun_path;
310 if (__path_search(buf, L_tmpnam, NULL, NULL, 0) != 0
311 || __gen_tempname(buf, __GT_NOCREATE, 0, 0) != 0
313 return -EAI_SYSTEM;
317 ai->ai_canonname = NULL;
318 if (req->ai_flags & AI_CANONNAME)
319 ai->ai_canonname = strcpy((char *)(ai + 1) + sizeof(struct sockaddr_un),
320 utsname.nodename);
321 return 0;
323 #endif /* 0 */
325 static int
326 gaih_inet_serv(const char *servicename, const struct gaih_typeproto *tp,
327 const struct addrinfo *req, struct gaih_servtuple *st)
329 struct servent *s;
330 size_t tmpbuflen = 1024;
331 struct servent ts;
332 char *tmpbuf;
333 int r;
335 while (1) {
336 tmpbuf = alloca(tmpbuflen);
337 r = getservbyname_r(servicename, tp->name, &ts, tmpbuf, tmpbuflen, &s);
338 if (r == 0 && s != NULL)
339 break;
340 if (r != ERANGE)
341 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
342 tmpbuflen *= 2;
344 st->next = NULL;
345 st->socktype = tp->socktype;
346 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) ? req->ai_protocol : tp->protocol);
347 st->port = s->s_port;
348 return 0;
351 /* NB: also uses h,pat,rc,no_data variables */
352 #define gethosts(_family, _type) \
354 int i, herrno; \
355 size_t tmpbuflen; \
356 struct hostent th; \
357 char *tmpbuf; \
359 tmpbuflen = 512; \
360 no_data = 0; \
361 do { \
362 tmpbuflen *= 2; \
363 tmpbuf = alloca(tmpbuflen); \
364 rc = gethostbyname2_r(name, _family, &th, tmpbuf, \
365 tmpbuflen, &h, &herrno); \
366 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
367 if (rc != 0) { \
368 if (herrno == NETDB_INTERNAL) { \
369 __set_h_errno(herrno); \
370 return -EAI_SYSTEM; \
372 if (herrno == TRY_AGAIN) \
373 no_data = EAI_AGAIN; \
374 else \
375 no_data = (herrno == NO_DATA); \
376 } else if (h != NULL) { \
377 for (i = 0; h->h_addr_list[i]; i++) { \
378 if (*pat == NULL) { \
379 *pat = alloca(sizeof(struct gaih_addrtuple)); \
380 (*pat)->scopeid = 0; \
382 (*pat)->next = NULL; \
383 (*pat)->family = _family; \
384 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(_type)); \
385 pat = &((*pat)->next); \
390 static int
391 gaih_inet(const char *name, const struct gaih_service *service,
392 const struct addrinfo *req, struct addrinfo **pai)
394 struct gaih_servtuple nullserv;
396 const struct gaih_typeproto *tp;
397 struct gaih_servtuple *st;
398 struct gaih_addrtuple *at;
399 int rc;
400 int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6)
401 && (req->ai_flags & AI_V4MAPPED);
402 unsigned seen = 0;
403 if (req->ai_flags & AI_ADDRCONFIG) {
404 /* "seen" is only used when AI_ADDRCONFIG is specified.
405 Avoid unnecessary call to __check_pf() otherwise
406 since it can be costly especially when RSBAC-Net is enabled. */
407 seen = __check_pf();
410 memset(&nullserv, 0, sizeof(nullserv));
412 tp = gaih_inet_typeproto;
413 if (req->ai_protocol || req->ai_socktype) {
414 ++tp;
415 while (tp->name[0]) {
416 if ((req->ai_socktype == 0 || req->ai_socktype == tp->socktype)
417 && (req->ai_protocol == 0 || req->ai_protocol == tp->protocol || (tp->protoflag & GAI_PROTO_PROTOANY))
419 goto found;
421 ++tp;
423 if (req->ai_socktype)
424 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
425 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
426 found: ;
429 st = &nullserv;
430 if (service != NULL) {
431 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
432 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
434 if (service->num < 0) {
435 if (tp->name[0]) {
436 st = alloca(sizeof(struct gaih_servtuple));
437 rc = gaih_inet_serv(service->name, tp, req, st);
438 if (rc)
439 return rc;
440 } else {
441 struct gaih_servtuple **pst = &st;
442 for (tp++; tp->name[0]; tp++) {
443 struct gaih_servtuple *newp;
445 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
446 continue;
448 if (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
449 continue;
450 if (req->ai_protocol != 0
451 && !(tp->protoflag & GAI_PROTO_PROTOANY)
452 && req->ai_protocol != tp->protocol)
453 continue;
455 newp = alloca(sizeof(struct gaih_servtuple));
456 rc = gaih_inet_serv(service->name, tp, req, newp);
457 if (rc) {
458 if (rc & GAIH_OKIFUNSPEC)
459 continue;
460 return rc;
463 *pst = newp;
464 pst = &(newp->next);
466 if (st == &nullserv)
467 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
469 } else {
470 st = alloca(sizeof(struct gaih_servtuple));
471 st->next = NULL;
472 st->socktype = tp->socktype;
473 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
474 ? req->ai_protocol : tp->protocol);
475 st->port = htons(service->num);
477 } else if (req->ai_socktype || req->ai_protocol) {
478 st = alloca(sizeof(struct gaih_servtuple));
479 st->next = NULL;
480 st->socktype = tp->socktype;
481 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
482 ? req->ai_protocol : tp->protocol);
483 st->port = 0;
484 } else {
486 * Neither socket type nor protocol is set. Return all socket types
487 * we know about.
489 struct gaih_servtuple **lastp = &st;
490 for (++tp; tp->name[0]; ++tp) {
491 struct gaih_servtuple *newp;
493 newp = alloca(sizeof(struct gaih_servtuple));
494 newp->next = NULL;
495 newp->socktype = tp->socktype;
496 newp->protocol = tp->protocol;
497 newp->port = 0;
499 *lastp = newp;
500 lastp = &newp->next;
504 at = NULL;
505 if (name != NULL) {
506 at = alloca(sizeof(struct gaih_addrtuple));
507 at->family = AF_UNSPEC;
508 at->scopeid = 0;
509 at->next = NULL;
511 if (inet_pton(AF_INET, name, at->addr) > 0) {
512 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET && !v4mapped)
513 return -EAI_FAMILY;
514 at->family = AF_INET;
517 #if defined __UCLIBC_HAS_IPV6__
518 if (at->family == AF_UNSPEC) {
519 char *namebuf = strdupa(name);
520 char *scope_delim;
522 scope_delim = strchr(namebuf, SCOPE_DELIMITER);
523 if (scope_delim != NULL)
524 *scope_delim = '\0';
526 if (inet_pton(AF_INET6, namebuf, at->addr) > 0) {
527 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET6)
528 return -EAI_FAMILY;
529 at->family = AF_INET6;
530 if (scope_delim != NULL) {
531 int try_numericscope = 0;
532 uint32_t *a32 = (uint32_t*)at->addr;
533 if (IN6_IS_ADDR_LINKLOCAL(a32) || IN6_IS_ADDR_MC_LINKLOCAL(at->addr)) {
534 at->scopeid = if_nametoindex(scope_delim + 1);
535 if (at->scopeid == 0)
536 try_numericscope = 1;
537 } else
538 try_numericscope = 1;
540 if (try_numericscope != 0) {
541 char *end;
542 assert(sizeof(uint32_t) <= sizeof(unsigned long));
543 at->scopeid = (uint32_t)strtoul(scope_delim + 1, &end, 10);
544 if (*end != '\0')
545 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
550 #endif
552 if (at->family == AF_UNSPEC && !(req->ai_flags & AI_NUMERICHOST)) {
553 struct hostent *h;
554 struct gaih_addrtuple **pat = &at;
555 int no_data = 0;
556 int no_inet6_data;
559 * If we are looking for both IPv4 and IPv6 address we don't want
560 * the lookup functions to automatically promote IPv4 addresses to
561 * IPv6 addresses.
563 #if defined __UCLIBC_HAS_IPV6__
564 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
565 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV6))
566 gethosts(AF_INET6, struct in6_addr);
567 #endif
568 no_inet6_data = no_data;
570 if (req->ai_family == AF_INET
571 || (!v4mapped && req->ai_family == AF_UNSPEC)
572 || (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL)))
574 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV4))
575 gethosts(AF_INET, struct in_addr);
578 if (no_data != 0 && no_inet6_data != 0) {
579 /* If both requests timed out report this. */
580 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
581 return -EAI_AGAIN;
583 * We made requests but they turned out no data.
584 * The name is known, though.
586 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
590 if (at->family == AF_UNSPEC)
591 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
592 } else {
593 struct gaih_addrtuple *atr;
595 atr = at = alloca(sizeof(struct gaih_addrtuple));
596 memset(at, '\0', sizeof(struct gaih_addrtuple));
597 if (req->ai_family == 0) {
598 at->next = alloca(sizeof(struct gaih_addrtuple));
599 memset(at->next, '\0', sizeof(struct gaih_addrtuple));
601 #if defined __UCLIBC_HAS_IPV6__
602 if (req->ai_family == 0 || req->ai_family == AF_INET6) {
603 at->family = AF_INET6;
604 if ((req->ai_flags & AI_PASSIVE) == 0)
605 memcpy(at->addr, &in6addr_loopback, sizeof(struct in6_addr));
606 atr = at->next;
608 #endif
609 if (req->ai_family == 0 || req->ai_family == AF_INET) {
610 atr->family = AF_INET;
611 if ((req->ai_flags & AI_PASSIVE) == 0) {
612 uint32_t *a = (uint32_t*)atr->addr;
613 *a = htonl(INADDR_LOOPBACK);
618 if (pai == NULL)
619 return 0;
622 const char *c = NULL;
623 struct gaih_servtuple *st2;
624 struct gaih_addrtuple *at2 = at;
625 size_t socklen, namelen;
626 sa_family_t family;
629 * buffer is the size of an unformatted IPv6 address in
630 * printable format.
632 char buffer[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
634 while (at2 != NULL) {
635 c = inet_ntop(at2->family, at2->addr, buffer, sizeof(buffer));
636 if (c) {
637 namelen = strlen(c) + 1;
638 } else if (req->ai_flags & AI_CANONNAME) {
639 struct hostent *h = NULL;
640 int herrno;
641 struct hostent th;
642 size_t tmpbuflen = 512;
643 char *tmpbuf;
645 /* Hint says numeric, but address is not */
646 if (req->ai_flags & AI_NUMERICHOST)
647 return -EAI_NONAME;
649 do {
650 tmpbuflen *= 2;
651 tmpbuf = alloca(tmpbuflen);
652 rc = gethostbyaddr_r(at2->addr,
653 #ifdef __UCLIBC_HAS_IPV6__
654 ((at2->family == AF_INET6)
655 ? sizeof(struct in6_addr)
656 : sizeof(struct in_addr)),
657 #else
658 sizeof(struct in_addr),
659 #endif
660 at2->family,
661 &th, tmpbuf, tmpbuflen,
662 &h, &herrno);
663 } while (rc == ERANGE && herrno == NETDB_INTERNAL);
665 if (rc != 0 && herrno == NETDB_INTERNAL) {
666 __set_h_errno(herrno);
667 return -EAI_SYSTEM;
670 if (h != NULL)
671 c = h->h_name;
673 if (c == NULL)
674 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
676 namelen = strlen(c) + 1;
677 } else
678 namelen = 0;
680 #if defined __UCLIBC_HAS_IPV6__
681 if (at2->family == AF_INET6 || v4mapped) {
682 family = AF_INET6;
683 socklen = sizeof(struct sockaddr_in6);
685 #endif
686 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
687 else
688 #endif
689 #if defined __UCLIBC_HAS_IPV4__
691 family = AF_INET;
692 socklen = sizeof(struct sockaddr_in);
694 #endif
695 for (st2 = st; st2 != NULL; st2 = st2->next) {
696 if (req->ai_flags & AI_ADDRCONFIG) {
697 if (family == AF_INET && !(seen & SEEN_IPV4))
698 break;
699 #if defined __UCLIBC_HAS_IPV6__
700 else if (family == AF_INET6 && !(seen & SEEN_IPV6))
701 break;
702 #endif
704 *pai = malloc(sizeof(struct addrinfo) + socklen + namelen);
705 if (*pai == NULL)
706 return -EAI_MEMORY;
708 (*pai)->ai_flags = req->ai_flags;
709 (*pai)->ai_family = family;
710 (*pai)->ai_socktype = st2->socktype;
711 (*pai)->ai_protocol = st2->protocol;
712 (*pai)->ai_addrlen = socklen;
713 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
714 #if 0 /* SALEN */
715 (*pai)->ai_addr->sa_len = socklen;
716 #endif
717 (*pai)->ai_addr->sa_family = family;
719 #if defined __UCLIBC_HAS_IPV6__
720 if (family == AF_INET6) {
721 struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) (*pai)->ai_addr;
723 sin6p->sin6_flowinfo = 0;
724 if (at2->family == AF_INET6) {
725 memcpy(&sin6p->sin6_addr,
726 at2->addr, sizeof(struct in6_addr));
727 } else {
728 sin6p->sin6_addr.s6_addr32[0] = 0;
729 sin6p->sin6_addr.s6_addr32[1] = 0;
730 sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
731 memcpy(&sin6p->sin6_addr.s6_addr32[3],
732 at2->addr, sizeof(sin6p->sin6_addr.s6_addr32[3]));
734 sin6p->sin6_port = st2->port;
735 sin6p->sin6_scope_id = at2->scopeid;
737 #endif
738 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
739 else
740 #endif
741 #if defined __UCLIBC_HAS_IPV4__
743 struct sockaddr_in *sinp = (struct sockaddr_in *) (*pai)->ai_addr;
745 memcpy(&sinp->sin_addr, at2->addr, sizeof(struct in_addr));
746 sinp->sin_port = st2->port;
747 memset(sinp->sin_zero, '\0', sizeof(sinp->sin_zero));
749 #endif
750 if (c) {
751 (*pai)->ai_canonname = ((void *) (*pai) +
752 sizeof(struct addrinfo) + socklen);
753 strcpy((*pai)->ai_canonname, c);
754 } else {
755 (*pai)->ai_canonname = NULL;
757 (*pai)->ai_next = NULL;
758 pai = &((*pai)->ai_next);
761 at2 = at2->next;
764 return 0;
767 static const struct gaih gaih[] = {
768 #if defined __UCLIBC_HAS_IPV6__
769 { PF_INET6, gaih_inet },
770 #endif
771 { PF_INET, gaih_inet },
772 #if 0
773 { PF_LOCAL, gaih_local },
774 #endif
775 { PF_UNSPEC, NULL }
778 void
779 freeaddrinfo(struct addrinfo *ai)
781 struct addrinfo *p;
783 while (ai != NULL) {
784 p = ai;
785 ai = ai->ai_next;
786 free(p);
789 libc_hidden_def(freeaddrinfo)
792 getaddrinfo(const char *name, const char *service,
793 const struct addrinfo *hints, struct addrinfo **pai)
795 int i, j, last_i;
796 struct addrinfo *p, **end;
797 const struct gaih *g, *pg;
798 struct gaih_service gaih_service, *pservice;
799 struct addrinfo default_hints;
801 if (name != NULL && name[0] == '*' && name[1] == 0)
802 name = NULL;
804 if (service != NULL && service[0] == '*' && service[1] == 0)
805 service = NULL;
807 if (name == NULL && service == NULL)
808 return EAI_NONAME;
810 if (hints == NULL) {
811 memset(&default_hints, 0, sizeof(default_hints));
812 if (AF_UNSPEC != 0)
813 default_hints.ai_family = AF_UNSPEC;
814 hints = &default_hints;
817 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
818 AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
819 return EAI_BADFLAGS;
821 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
822 return EAI_BADFLAGS;
824 if (service && service[0]) {
825 char *c;
826 gaih_service.name = service;
827 gaih_service.num = strtoul(gaih_service.name, &c, 10);
828 if (*c != '\0') {
829 if (hints->ai_flags & AI_NUMERICSERV)
830 return EAI_NONAME;
831 gaih_service.num = -1;
833 pservice = &gaih_service;
834 } else
835 pservice = NULL;
837 g = gaih;
838 pg = NULL;
839 p = NULL;
840 end = NULL;
841 if (pai)
842 end = &p;
843 i = 0;
844 last_i = 0;
845 j = 0;
846 while (g->gaih) {
847 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) {
848 if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) {
849 ++g;
850 continue;
852 j++;
853 if (pg == NULL || pg->gaih != g->gaih) {
854 pg = g;
855 i = g->gaih(name, pservice, hints, end);
856 if (i != 0) {
857 last_i = i;
858 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
859 continue;
860 /*if (p) - freeaddrinfo works ok on NULL too */
861 freeaddrinfo(p);
862 return -(i & GAIH_EAI);
864 if (end)
865 while (*end)
866 end = &((*end)->ai_next);
869 ++g;
872 if (j == 0)
873 return EAI_FAMILY;
875 if (p) {
876 *pai = p;
877 return 0;
880 if (pai == NULL && last_i == 0)
881 return 0;
883 /* if (p) - never happens, see above */
884 /* freeaddrinfo(p); */
886 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
888 libc_hidden_def(getaddrinfo)