add SEEK_DATA/SEEK_HOLE
[uclibc-ng.git] / libc / inet / getaddrinfo.c
blobf34a4726c8d18f05f993b180f4d21275d4204108
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 <stdbool.h>
66 #include <arpa/inet.h>
67 #include <sys/socket.h>
68 #include <netinet/in.h>
69 #include <sys/types.h>
70 #include <sys/un.h>
71 #include <sys/utsname.h>
72 #include <net/if.h>
73 #include <ifaddrs.h>
74 #include "internal/parse_config.h"
76 #define GAIH_OKIFUNSPEC 0x0100
77 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
79 #ifndef UNIX_PATH_MAX
80 #define UNIX_PATH_MAX 108
81 #endif
83 /* Useful for having small structure members/global variables */
84 typedef int8_t socktype_t;
85 typedef int8_t family_t;
86 typedef int8_t protocol_t;
87 struct BUG_too_small {
88 char BUG_socktype_t_too_small[(0
89 | SOCK_STREAM
90 | SOCK_DGRAM
91 | SOCK_RAW
92 ) <= 127 ? 1 : -1];
93 char BUG_family_t_too_small[(0
94 | AF_UNSPEC
95 | AF_INET
96 | AF_INET6
97 ) <= 127 ? 1 : -1];
98 char BUG_protocol_t_too_small[(0
99 | IPPROTO_TCP
100 | IPPROTO_UDP
101 ) <= 127 ? 1 : -1];
104 struct gaih_service {
105 const char *name;
106 int num;
109 struct gaih_servtuple {
110 struct gaih_servtuple *next;
111 int socktype;
112 int protocol;
113 int port;
116 struct gaih_addrtuple {
117 struct gaih_addrtuple *next;
118 int family;
119 char addr[16];
120 uint32_t scopeid;
123 struct gaih_typeproto {
124 socktype_t socktype;
125 protocol_t protocol;
126 int8_t protoflag;
127 char name[4];
129 /* Values for `protoflag'. */
130 #define GAI_PROTO_NOSERVICE 1
131 #define GAI_PROTO_PROTOANY 2
133 static const struct gaih_typeproto gaih_inet_typeproto[] = {
134 { 0 , 0 , 0, "" },
135 { SOCK_STREAM, IPPROTO_TCP, 0, "tcp" },
136 { SOCK_DGRAM , IPPROTO_UDP, 0, "udp" },
137 { SOCK_RAW , 0 , GAI_PROTO_PROTOANY|GAI_PROTO_NOSERVICE, "raw" },
138 { 0 , 0 , 0, "" },
141 struct gaih {
142 int family;
143 int (*gaih)(const char *name, const struct gaih_service *service,
144 const struct addrinfo *req, struct addrinfo **pai);
147 #define SEEN_IPV4 1
148 #define SEEN_IPV6 2
150 static unsigned __check_pf(void)
152 unsigned seen = 0;
154 #if defined __UCLIBC_SUPPORT_AI_ADDRCONFIG__
156 struct ifaddrs *ifa;
157 struct ifaddrs *runp;
159 /* Get the interface list via getifaddrs. */
160 if (getifaddrs(&ifa) != 0) {
161 /* We cannot determine what interfaces are available.
162 * Be optimistic. */
163 #if defined __UCLIBC_HAS_IPV4__
164 seen |= SEEN_IPV4;
165 #endif
166 #if defined __UCLIBC_HAS_IPV6__
167 seen |= SEEN_IPV6;
168 #endif
169 return seen;
172 for (runp = ifa; runp != NULL; runp = runp->ifa_next) {
173 if (runp->ifa_addr == NULL)
174 continue;
175 #if defined __UCLIBC_HAS_IPV4__
176 if (runp->ifa_addr->sa_family == PF_INET)
177 seen |= SEEN_IPV4;
178 #endif
179 #if defined __UCLIBC_HAS_IPV6__
180 if (runp->ifa_addr->sa_family == PF_INET6)
181 seen |= SEEN_IPV6;
182 #endif
184 freeifaddrs(ifa);
186 #else
188 /* AI_ADDRCONFIG is disabled, assume both ipv4 and ipv6 available. */
189 #if defined __UCLIBC_HAS_IPV4__
190 seen |= SEEN_IPV4;
191 #endif
192 #if defined __UCLIBC_HAS_IPV6__
193 seen |= SEEN_IPV6;
194 #endif
196 #endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */
198 return seen;
201 static int addrconfig(sa_family_t af)
203 int s;
204 int ret;
205 int saved_errno = errno;
206 unsigned seen;
208 seen = __check_pf();
209 #if defined __UCLIBC_HAS_IPV4__
210 if (af == AF_INET)
211 ret = seen & SEEN_IPV4;
212 else
213 #endif
214 #if defined __UCLIBC_HAS_IPV6__
215 if (af == AF_INET6)
216 ret = seen & SEEN_IPV6;
217 else
218 #endif
220 s = socket(af, SOCK_DGRAM, 0);
221 ret = 1; /* Assume PF_UNIX. */
222 if (s < 0) {
223 if (errno != EMFILE)
224 ret = 0;
225 } else
226 close(s);
228 __set_errno(saved_errno);
229 return ret;
232 #if 0
233 /* Using Unix sockets this way is a security risk. */
234 static int
235 gaih_local(const char *name, const struct gaih_service *service,
236 const struct addrinfo *req, struct addrinfo **pai)
238 struct utsname utsname;
239 struct addrinfo *ai = *pai;
241 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
242 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
244 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
245 if (uname(&utsname) < 0)
246 return -EAI_SYSTEM;
248 if (name != NULL) {
249 if (strcmp(name, "localhost") &&
250 strcmp(name, "local") &&
251 strcmp(name, "unix") &&
252 strcmp(name, utsname.nodename))
253 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
256 if (req->ai_protocol || req->ai_socktype) {
257 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
259 while (tp->name[0]
260 && ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0
261 || (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
262 || (req->ai_protocol != 0 && !(tp->protoflag & GAI_PROTO_PROTOANY) && req->ai_protocol != tp->protocol))
264 ++tp;
266 if (! tp->name[0]) {
267 if (req->ai_socktype)
268 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
269 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
273 *pai = ai = malloc(sizeof(struct addrinfo) + sizeof(struct sockaddr_un)
274 + ((req->ai_flags & AI_CANONNAME)
275 ? (strlen(utsname.nodename) + 1) : 0));
276 if (ai == NULL)
277 return -EAI_MEMORY;
279 ai->ai_next = NULL;
280 ai->ai_flags = req->ai_flags;
281 ai->ai_family = AF_LOCAL;
282 ai->ai_socktype = req->ai_socktype ? req->ai_socktype : SOCK_STREAM;
283 ai->ai_protocol = req->ai_protocol;
284 ai->ai_addrlen = sizeof(struct sockaddr_un);
285 ai->ai_addr = (void *)ai + sizeof(struct addrinfo);
286 #if 0 /* SALEN */
287 ((struct sockaddr_un *)ai->ai_addr)->sun_len = sizeof(struct sockaddr_un);
288 #endif /* SALEN */
290 ((struct sockaddr_un *)ai->ai_addr)->sun_family = AF_LOCAL;
291 memset(((struct sockaddr_un *)ai->ai_addr)->sun_path, 0, UNIX_PATH_MAX);
293 if (service) {
294 struct sockaddr_un *sunp = (struct sockaddr_un *)ai->ai_addr;
296 if (strchr(service->name, '/') != NULL) {
297 if (strlen(service->name) >= sizeof(sunp->sun_path))
298 return GAIH_OKIFUNSPEC | -EAI_SERVICE;
299 strcpy(sunp->sun_path, service->name);
300 } else {
301 if (strlen(P_tmpdir "/") + 1 + strlen(service->name) >= sizeof(sunp->sun_path))
302 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
303 stpcpy(stpcpy(sunp->sun_path, P_tmpdir "/"), service->name);
305 } else {
306 /* This is a dangerous use of the interface since there is a time
307 window between the test for the file and the actual creation
308 (done by the caller) in which a file with the same name could
309 be created. */
310 char *buf = ((struct sockaddr_un *)ai->ai_addr)->sun_path;
312 if (__path_search(buf, L_tmpnam, NULL, NULL, 0) != 0
313 || __gen_tempname(buf, __GT_NOCREATE, 0, 0) != 0
315 return -EAI_SYSTEM;
319 ai->ai_canonname = NULL;
320 if (req->ai_flags & AI_CANONNAME)
321 ai->ai_canonname = strcpy((char *)(ai + 1) + sizeof(struct sockaddr_un),
322 utsname.nodename);
323 return 0;
325 #endif /* 0 */
327 static int
328 gaih_inet_serv(const char *servicename, const struct gaih_typeproto *tp,
329 const struct addrinfo *req, struct gaih_servtuple *st)
331 struct servent *s;
332 size_t tmpbuflen = 1024;
333 struct servent ts;
334 char *tmpbuf;
335 int r;
337 while (1) {
338 tmpbuf = alloca(tmpbuflen);
339 r = getservbyname_r(servicename, tp->name, &ts, tmpbuf, tmpbuflen, &s);
340 if (r == 0 && s != NULL)
341 break;
342 if (r != ERANGE)
343 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
344 tmpbuflen *= 2;
346 st->next = NULL;
347 st->socktype = tp->socktype;
348 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY) ? req->ai_protocol : tp->protocol);
349 st->port = s->s_port;
350 return 0;
353 #if defined __UCLIBC_HAS_IPV6__
354 static uint8_t __gai_precedence = 0; /* =1 - IPv6, IPv4
355 =2 - IPv4, IPv6 */
356 #endif
358 /* NB: also uses h,pat,rc,no_data variables */
359 #define gethosts(_family, _type) \
361 int i, herrno; \
362 size_t tmpbuflen; \
363 struct hostent th; \
364 char *tmpbuf; \
366 tmpbuflen = 512; \
367 no_data = 0; \
368 do { \
369 tmpbuflen *= 2; \
370 tmpbuf = alloca(tmpbuflen); \
371 rc = gethostbyname2_r(name, _family, &th, tmpbuf, \
372 tmpbuflen, &h, &herrno); \
373 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
374 if (rc != 0) { \
375 if (herrno == NETDB_INTERNAL) { \
376 __set_h_errno(herrno); \
377 return -EAI_SYSTEM; \
379 if (herrno == TRY_AGAIN) \
380 no_data = EAI_AGAIN; \
381 else \
382 no_data = (herrno == NO_DATA); \
383 } else if (h != NULL) { \
384 for (i = 0; h->h_addr_list[i]; i++) { \
385 if (*pat == NULL) { \
386 *pat = alloca(sizeof(struct gaih_addrtuple)); \
387 (*pat)->scopeid = 0; \
389 (*pat)->next = NULL; \
390 (*pat)->family = _family; \
391 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(_type)); \
392 pat = &((*pat)->next); \
394 if (_family == AF_INET6 && i > 0) { \
395 got_ipv6 = true; \
400 static int
401 gaih_inet(const char *name, const struct gaih_service *service,
402 const struct addrinfo *req, struct addrinfo **pai)
404 struct gaih_servtuple nullserv;
406 const struct gaih_typeproto *tp;
407 struct gaih_servtuple *st;
408 struct gaih_addrtuple *at;
409 int rc;
410 bool got_ipv6 = false;
411 int v4mapped = req->ai_family == PF_INET6 && (req->ai_flags & AI_V4MAPPED);
412 unsigned seen = 0;
413 if (req->ai_flags & AI_ADDRCONFIG) {
414 /* "seen" is only used when AI_ADDRCONFIG is specified.
415 Avoid unnecessary call to __check_pf() otherwise
416 since it can be costly especially when RSBAC-Net is enabled. */
417 seen = __check_pf();
420 memset(&nullserv, 0, sizeof(nullserv));
422 tp = gaih_inet_typeproto;
423 if (req->ai_protocol || req->ai_socktype) {
424 ++tp;
425 while (tp->name[0]) {
426 if ((req->ai_socktype == 0 || req->ai_socktype == tp->socktype)
427 && (req->ai_protocol == 0 || req->ai_protocol == tp->protocol || (tp->protoflag & GAI_PROTO_PROTOANY))
429 goto found;
431 ++tp;
433 if (req->ai_socktype)
434 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
435 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
436 found: ;
439 st = &nullserv;
440 if (service != NULL) {
441 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
442 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
444 if (service->num < 0) {
445 if (tp->name[0]) {
446 st = alloca(sizeof(struct gaih_servtuple));
447 rc = gaih_inet_serv(service->name, tp, req, st);
448 if (rc)
449 return rc;
450 } else {
451 struct gaih_servtuple **pst = &st;
452 for (tp++; tp->name[0]; tp++) {
453 struct gaih_servtuple *newp;
455 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
456 continue;
458 if (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
459 continue;
460 if (req->ai_protocol != 0
461 && !(tp->protoflag & GAI_PROTO_PROTOANY)
462 && req->ai_protocol != tp->protocol)
463 continue;
465 newp = alloca(sizeof(struct gaih_servtuple));
466 rc = gaih_inet_serv(service->name, tp, req, newp);
467 if (rc) {
468 if (rc & GAIH_OKIFUNSPEC)
469 continue;
470 return rc;
473 *pst = newp;
474 pst = &(newp->next);
476 if (st == &nullserv)
477 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
479 } else {
480 st = alloca(sizeof(struct gaih_servtuple));
481 st->next = NULL;
482 st->socktype = tp->socktype;
483 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
484 ? req->ai_protocol : tp->protocol);
485 st->port = htons(service->num);
487 } else if (req->ai_socktype || req->ai_protocol) {
488 st = alloca(sizeof(struct gaih_servtuple));
489 st->next = NULL;
490 st->socktype = tp->socktype;
491 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
492 ? req->ai_protocol : tp->protocol);
493 st->port = 0;
494 } else {
496 * Neither socket type nor protocol is set. Return all socket types
497 * we know about.
499 struct gaih_servtuple **lastp = &st;
500 for (++tp; tp->name[0]; ++tp) {
501 struct gaih_servtuple *newp;
503 newp = alloca(sizeof(struct gaih_servtuple));
504 newp->next = NULL;
505 newp->socktype = tp->socktype;
506 newp->protocol = tp->protocol;
507 newp->port = 0;
509 *lastp = newp;
510 lastp = &newp->next;
514 at = NULL;
515 if (name != NULL) {
516 at = alloca(sizeof(struct gaih_addrtuple));
517 at->family = AF_UNSPEC;
518 at->scopeid = 0;
519 at->next = NULL;
521 if (inet_pton(AF_INET, name, at->addr) > 0) {
522 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET && !v4mapped)
523 return -EAI_FAMILY;
524 at->family = AF_INET;
527 #if defined __UCLIBC_HAS_IPV6__
528 if (at->family == AF_UNSPEC) {
529 char *namebuf = strdupa(name);
530 char *scope_delim;
532 scope_delim = strchr(namebuf, SCOPE_DELIMITER);
533 if (scope_delim != NULL)
534 *scope_delim = '\0';
536 if (inet_pton(AF_INET6, namebuf, at->addr) > 0) {
537 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET6)
538 return -EAI_FAMILY;
539 at->family = AF_INET6;
540 if (scope_delim != NULL) {
541 int try_numericscope = 0;
542 uint32_t *a32 = (uint32_t*)at->addr;
543 if (IN6_IS_ADDR_LINKLOCAL(a32) || IN6_IS_ADDR_MC_LINKLOCAL(at->addr)) {
544 at->scopeid = if_nametoindex(scope_delim + 1);
545 if (at->scopeid == 0)
546 try_numericscope = 1;
547 } else
548 try_numericscope = 1;
550 if (try_numericscope != 0) {
551 char *end;
552 assert(sizeof(uint32_t) <= sizeof(unsigned long));
553 at->scopeid = (uint32_t)strtoul(scope_delim + 1, &end, 10);
554 if (*end != '\0')
555 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
560 #endif
562 if (at->family == AF_UNSPEC && !(req->ai_flags & AI_NUMERICHOST)) {
563 struct hostent *h;
564 struct gaih_addrtuple **pat = &at;
565 int no_data, no_inet6_data;
566 #if defined __UCLIBC_HAS_IPV6__
567 bool first_try = true;
568 #endif
571 * If we are looking for both IPv4 and IPv6 address we don't want
572 * the lookup functions to automatically promote IPv4 addresses to
573 * IPv6 addresses.
575 no_inet6_data = no_data = 0;
576 #if defined __UCLIBC_HAS_IPV6__
577 if (__gai_precedence == 2)
578 goto try_v4;
580 try_v6:
581 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
582 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV6))
583 gethosts(AF_INET6, struct in6_addr);
584 no_inet6_data = no_data;
585 if (!first_try)
586 goto tried_all;
587 first_try = false;
589 try_v4:
590 #endif
591 if (req->ai_family == AF_INET
592 || (!v4mapped && req->ai_family == AF_UNSPEC)
593 || (v4mapped && (!got_ipv6 || (req->ai_flags & AI_ALL)))
595 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV4))
596 gethosts(AF_INET, struct in_addr);
598 #if defined __UCLIBC_HAS_IPV6__
599 if (first_try) {
600 first_try = false;
601 goto try_v6;
604 tried_all:
605 #endif
606 if (no_data != 0 && no_inet6_data != 0) {
607 /* If both requests timed out report this. */
608 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
609 return -EAI_AGAIN;
611 * We made requests but they turned out no data.
612 * The name is known, though.
614 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
618 if (at->family == AF_UNSPEC)
619 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
620 } else {
621 struct gaih_addrtuple *atr;
623 atr = at = alloca(sizeof(struct gaih_addrtuple));
624 memset(at, '\0', sizeof(struct gaih_addrtuple));
625 if (req->ai_family == 0) {
626 at->next = alloca(sizeof(struct gaih_addrtuple));
627 memset(at->next, '\0', sizeof(struct gaih_addrtuple));
629 #if defined __UCLIBC_HAS_IPV6__
630 if (req->ai_family == 0 || req->ai_family == AF_INET6) {
631 at->family = AF_INET6;
632 if ((req->ai_flags & AI_PASSIVE) == 0)
633 memcpy(at->addr, &in6addr_loopback, sizeof(struct in6_addr));
634 atr = at->next;
636 #endif
637 if (req->ai_family == 0 || req->ai_family == AF_INET) {
638 atr->family = AF_INET;
639 if ((req->ai_flags & AI_PASSIVE) == 0) {
640 uint32_t *a = (uint32_t*)atr->addr;
641 *a = htonl(INADDR_LOOPBACK);
646 if (pai == NULL)
647 return 0;
650 const char *c = NULL;
651 struct gaih_servtuple *st2;
652 struct gaih_addrtuple *at2 = at;
653 size_t socklen, namelen;
654 sa_family_t family;
657 * buffer is the size of an unformatted IPv6 address in
658 * printable format.
660 char buffer[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
662 while (at2 != NULL) {
663 c = inet_ntop(at2->family, at2->addr, buffer, sizeof(buffer));
664 if (c) {
665 namelen = strlen(c) + 1;
666 } else if (req->ai_flags & AI_CANONNAME) {
667 struct hostent *h = NULL;
668 int herrno;
669 struct hostent th;
670 size_t tmpbuflen = 512;
671 char *tmpbuf;
673 /* Hint says numeric, but address is not */
674 if (req->ai_flags & AI_NUMERICHOST)
675 return -EAI_NONAME;
677 do {
678 tmpbuflen *= 2;
679 tmpbuf = alloca(tmpbuflen);
680 rc = gethostbyaddr_r(at2->addr,
681 #ifdef __UCLIBC_HAS_IPV6__
682 ((at2->family == AF_INET6)
683 ? sizeof(struct in6_addr)
684 : sizeof(struct in_addr)),
685 #else
686 sizeof(struct in_addr),
687 #endif
688 at2->family,
689 &th, tmpbuf, tmpbuflen,
690 &h, &herrno);
691 } while (rc == ERANGE && herrno == NETDB_INTERNAL);
693 if (rc != 0 && herrno == NETDB_INTERNAL) {
694 __set_h_errno(herrno);
695 return -EAI_SYSTEM;
698 if (h != NULL)
699 c = h->h_name;
701 if (c == NULL)
702 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
704 namelen = strlen(c) + 1;
705 } else
706 namelen = 0;
708 #if defined __UCLIBC_HAS_IPV6__
709 if (at2->family == AF_INET6 || v4mapped) {
710 family = AF_INET6;
711 socklen = sizeof(struct sockaddr_in6);
713 /* If we looked up IPv4 mapped address discard them here if
714 the caller isn't interested in all address and we have
715 found at least one IPv6 address. */
716 if (got_ipv6
717 && (req->ai_flags & (AI_V4MAPPED|AI_ALL)) == AI_V4MAPPED
718 && IN6_IS_ADDR_V4MAPPED (at2->addr))
719 goto ignore;
721 #endif
722 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
723 else
724 #endif
725 #if defined __UCLIBC_HAS_IPV4__
727 family = AF_INET;
728 socklen = sizeof(struct sockaddr_in);
730 #endif
731 for (st2 = st; st2 != NULL; st2 = st2->next) {
732 if (req->ai_flags & AI_ADDRCONFIG) {
733 if (family == AF_INET && !(seen & SEEN_IPV4))
734 break;
735 #if defined __UCLIBC_HAS_IPV6__
736 else if (family == AF_INET6 && !(seen & SEEN_IPV6))
737 break;
738 #endif
740 *pai = malloc(sizeof(struct addrinfo) + socklen + namelen);
741 if (*pai == NULL)
742 return -EAI_MEMORY;
744 (*pai)->ai_flags = req->ai_flags;
745 (*pai)->ai_family = family;
746 (*pai)->ai_socktype = st2->socktype;
747 (*pai)->ai_protocol = st2->protocol;
748 (*pai)->ai_addrlen = socklen;
749 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
750 #if 0 /* SALEN */
751 (*pai)->ai_addr->sa_len = socklen;
752 #endif
753 (*pai)->ai_addr->sa_family = family;
755 #if defined __UCLIBC_HAS_IPV6__
756 if (family == AF_INET6) {
757 struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) (*pai)->ai_addr;
759 sin6p->sin6_flowinfo = 0;
760 if (at2->family == AF_INET6) {
761 memcpy(&sin6p->sin6_addr,
762 at2->addr, sizeof(struct in6_addr));
763 } else {
764 sin6p->sin6_addr.s6_addr32[0] = 0;
765 sin6p->sin6_addr.s6_addr32[1] = 0;
766 sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
767 memcpy(&sin6p->sin6_addr.s6_addr32[3],
768 at2->addr, sizeof(sin6p->sin6_addr.s6_addr32[3]));
770 sin6p->sin6_port = st2->port;
771 sin6p->sin6_scope_id = at2->scopeid;
773 #endif
774 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
775 else
776 #endif
777 #if defined __UCLIBC_HAS_IPV4__
779 struct sockaddr_in *sinp = (struct sockaddr_in *) (*pai)->ai_addr;
781 memcpy(&sinp->sin_addr, at2->addr, sizeof(struct in_addr));
782 sinp->sin_port = st2->port;
783 memset(sinp->sin_zero, '\0', sizeof(sinp->sin_zero));
785 #endif
786 if (c) {
787 (*pai)->ai_canonname = ((void *) (*pai) +
788 sizeof(struct addrinfo) + socklen);
789 strcpy((*pai)->ai_canonname, c);
790 } else {
791 (*pai)->ai_canonname = NULL;
793 (*pai)->ai_next = NULL;
794 pai = &((*pai)->ai_next);
796 ignore:
797 at2 = at2->next;
800 return 0;
803 static const struct gaih gaih[] = {
804 #if defined __UCLIBC_HAS_IPV6__
805 { PF_INET6, gaih_inet },
806 #endif
807 { PF_INET, gaih_inet },
808 #if 0
809 { PF_LOCAL, gaih_local },
810 #endif
811 { PF_UNSPEC, NULL }
814 #if defined __UCLIBC_HAS_IPV6__
817 * A call to getaddrinfo might return multiple answers. To provide
818 * possibility to change the sorting we must use /etc/gai.conf file,
819 * like glibc.
821 * gai.conf format:
823 * label <netmask> <precedence>
824 * The value is added to the label table used in
825 * the RFC 3484 sorting. If any label definition
826 * is present in the configuration file is present,
827 * the default table is not used. All the label
828 * definitions of the default table which are to
829 * be maintained have to be duplicated.
830 * precedence <netmask> <precedence>
831 * This keyword is similar to label, but instead
832 * the value is added to the precedence table as
833 * specified in RFC 3484. Once again, the presence
834 * of a single precedence line in the configuration
835 * file causes the default table to not be used.
837 * The simplified uclibc's implementation allows to change the IPv4/IPv6
838 * sorting order for a whole address space only, i.e
839 * "precedence ::ffff:0:0/96 100" is only supported.
841 static void __gai_conf_parse(void)
843 /* NO reread of /etc/gai.conf on change. */
844 if (__gai_precedence != 0)
845 return;
847 __gai_precedence = 1; /* default IPv6 */
849 parser_t *parser;
850 char **tok = NULL;
852 parser = config_open("/etc/gai.conf");
853 if (!parser)
854 return;
856 while (config_read(parser, &tok, 3, 3, "# \t", PARSE_NORMAL)) {
857 if (strcmp(tok[0], "precedence") == 0) {
858 char *pfx;
859 struct in6_addr mask;
861 pfx = strchr(tok[1], '/');
862 if (!pfx)
863 continue;
864 *(pfx++) = 0;
865 if (inet_pton(AF_INET6, tok[1], &mask) <= 0)
866 continue;
867 if (IN6_IS_ADDR_V4MAPPED(&mask)
868 && mask.s6_addr32[3] == 0
869 && atoi(pfx) == 96 && atoi(tok[2]) == 100)
870 __gai_precedence = 2; /* IPv4 first */
873 config_close(parser);
875 #else
876 # define __gai_conf_parse(x)
877 #endif /* __UCLIBC_HAS_IPV6__ */
879 void
880 freeaddrinfo(struct addrinfo *ai)
882 struct addrinfo *p;
884 while (ai != NULL) {
885 p = ai;
886 ai = ai->ai_next;
887 free(p);
890 libc_hidden_def(freeaddrinfo)
893 getaddrinfo(const char *name, const char *service,
894 const struct addrinfo *hints, struct addrinfo **pai)
896 int i, j, last_i;
897 struct addrinfo *p, **end;
898 const struct gaih *g, *pg;
899 struct gaih_service gaih_service, *pservice;
900 struct addrinfo default_hints;
902 if (name != NULL && name[0] == '*' && name[1] == 0)
903 name = NULL;
905 if (service != NULL && service[0] == '*' && service[1] == 0)
906 service = NULL;
908 if (name == NULL && service == NULL)
909 return EAI_NONAME;
911 if (hints == NULL) {
912 memset(&default_hints, 0, sizeof(default_hints));
913 if (AF_UNSPEC != 0)
914 default_hints.ai_family = AF_UNSPEC;
915 hints = &default_hints;
918 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
919 AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
920 return EAI_BADFLAGS;
922 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
923 return EAI_BADFLAGS;
925 if (service && service[0]) {
926 char *c;
927 gaih_service.name = service;
928 gaih_service.num = strtoul(gaih_service.name, &c, 10);
929 if (*c != '\0') {
930 if (hints->ai_flags & AI_NUMERICSERV)
931 return EAI_NONAME;
932 gaih_service.num = -1;
934 pservice = &gaih_service;
935 } else
936 pservice = NULL;
938 __gai_conf_parse();
939 g = gaih;
940 pg = NULL;
941 p = NULL;
942 end = NULL;
943 if (pai)
944 end = &p;
945 i = 0;
946 last_i = 0;
947 j = 0;
948 while (g->gaih) {
949 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) {
950 if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) {
951 ++g;
952 continue;
954 j++;
955 if (pg == NULL || pg->gaih != g->gaih) {
956 pg = g;
957 i = g->gaih(name, pservice, hints, end);
958 if (i != 0) {
959 last_i = i;
960 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
961 continue;
962 freeaddrinfo(p); /* freeaddrinfo works ok on NULL too */
963 return -(i & GAIH_EAI);
965 if (end)
966 while (*end)
967 end = &((*end)->ai_next);
970 ++g;
973 if (j == 0)
974 return EAI_FAMILY;
976 if (p) {
977 *pai = p;
978 return 0;
981 if (pai == NULL && last_i == 0)
982 return 0;
984 /* if (p) - never happens, see above */
985 /* freeaddrinfo(p); */
987 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
989 libc_hidden_def(getaddrinfo)