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.
6 *
7 * Licensed under the LGPL v2.1, see the file COPYING.LIB in this tarball.
8 */
9
10 /* $USAGI: getaddrinfo.c,v 1.16 2001/10/04 09:52:03 sekiya Exp $ */
11
12 /* The Inner Net License, Version 2.00
13
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:
17
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:
33
34 This product includes software developed by <name(s)>, The Inner
35 Net, and other contributors.
36
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.
40
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.
51
52 If these license terms cause you a real problem, contact the author. */
53
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"
75
76 #define GAIH_OKIFUNSPEC 0x0100
77 #define GAIH_EAI ~(GAIH_OKIFUNSPEC)
78
79 #ifndef UNIX_PATH_MAX
80 #define UNIX_PATH_MAX 108
81 #endif
82
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];
102 };
103
104 struct gaih_service {
105 const char *name;
106 int num;
107 };
108
109 struct gaih_servtuple {
110 struct gaih_servtuple *next;
111 int socktype;
112 int protocol;
113 int port;
114 };
115
116 struct gaih_addrtuple {
117 struct gaih_addrtuple *next;
118 int family;
119 char addr[16];
120 uint32_t scopeid;
121 };
122
123 struct gaih_typeproto {
124 socktype_t socktype;
125 protocol_t protocol;
126 int8_t protoflag;
127 char name[4];
128 };
129 /* Values for `protoflag'. */
130 #define GAI_PROTO_NOSERVICE 1
131 #define GAI_PROTO_PROTOANY 2
132
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, "" },
139 };
140
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);
145 };
146
147 #define SEEN_IPV4 1
148 #define SEEN_IPV6 2
149
__check_pf(void)150 static unsigned __check_pf(void)
151 {
152 unsigned seen = 0;
153
154 #if defined __UCLIBC_SUPPORT_AI_ADDRCONFIG__
155
156 struct ifaddrs *ifa;
157 struct ifaddrs *runp;
158
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;
170 }
171
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
183 }
184 freeifaddrs(ifa);
185
186 #else
187
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
195
196 #endif /* __UCLIBC_SUPPORT_AI_ADDRCONFIG__ */
197
198 return seen;
199 }
200
addrconfig(sa_family_t af)201 static int addrconfig(sa_family_t af)
202 {
203 int s;
204 int ret;
205 int saved_errno = errno;
206 unsigned seen;
207
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
219 {
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);
227 }
228 __set_errno(saved_errno);
229 return ret;
230 }
231
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)
237 {
238 struct utsname utsname;
239 struct addrinfo *ai = *pai;
240
241 if ((name != NULL) && (req->ai_flags & AI_NUMERICHOST))
242 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
243
244 if ((name != NULL) || (req->ai_flags & AI_CANONNAME))
245 if (uname(&utsname) < 0)
246 return -EAI_SYSTEM;
247
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);
254 }
255
256 if (req->ai_protocol || req->ai_socktype) {
257 const struct gaih_typeproto *tp = gaih_inet_typeproto + 1;
258
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))
263 ) {
264 ++tp;
265 }
266 if (! tp->name[0]) {
267 if (req->ai_socktype)
268 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
269 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
270 }
271 }
272
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;
278
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 */
289
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);
292
293 if (service) {
294 struct sockaddr_un *sunp = (struct sockaddr_un *)ai->ai_addr;
295
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);
304 }
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;
311
312 if (__path_search(buf, L_tmpnam, NULL, NULL, 0) != 0
313 || __gen_tempname(buf, __GT_NOCREATE, 0, 0) != 0
314 ) {
315 return -EAI_SYSTEM;
316 }
317 }
318
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;
324 }
325 #endif /* 0 */
326
327 static int
gaih_inet_serv(const char * servicename,const struct gaih_typeproto * tp,const struct addrinfo * req,struct gaih_servtuple * st)328 gaih_inet_serv(const char *servicename, const struct gaih_typeproto *tp,
329 const struct addrinfo *req, struct gaih_servtuple *st)
330 {
331 struct servent *s;
332 size_t tmpbuflen = 1024;
333 struct servent ts;
334 char *tmpbuf;
335 int r;
336
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;
345 }
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;
351 }
352
353 #if defined __UCLIBC_HAS_IPV6__
354 static uint8_t __gai_precedence = 0; /* =1 - IPv6, IPv4
355 =2 - IPv4, IPv6 */
356 #endif
357
358 /* NB: also uses h,pat,rc,no_data variables */
359 #define gethosts(_family, _type) \
360 { \
361 int i, herrno; \
362 size_t tmpbuflen; \
363 struct hostent th; \
364 char *tmpbuf; \
365 \
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; \
378 } \
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; \
388 } \
389 (*pat)->next = NULL; \
390 (*pat)->family = _family; \
391 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(_type)); \
392 pat = &((*pat)->next); \
393 } \
394 if (_family == AF_INET6 && i > 0) { \
395 got_ipv6 = true; \
396 } \
397 } \
398 }
399
400 static int
gaih_inet(const char * name,const struct gaih_service * service,const struct addrinfo * req,struct addrinfo ** pai)401 gaih_inet(const char *name, const struct gaih_service *service,
402 const struct addrinfo *req, struct addrinfo **pai)
403 {
404 struct gaih_servtuple nullserv;
405
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();
418 }
419
420 memset(&nullserv, 0, sizeof(nullserv));
421
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))
428 ) {
429 goto found;
430 }
431 ++tp;
432 }
433 if (req->ai_socktype)
434 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
435 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
436 found: ;
437 }
438
439 st = &nullserv;
440 if (service != NULL) {
441 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
442 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
443
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;
454
455 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
456 continue;
457
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;
464
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;
471 }
472
473 *pst = newp;
474 pst = &(newp->next);
475 }
476 if (st == &nullserv)
477 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
478 }
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);
486 }
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 {
495 /*
496 * Neither socket type nor protocol is set. Return all socket types
497 * we know about.
498 */
499 struct gaih_servtuple **lastp = &st;
500 for (++tp; tp->name[0]; ++tp) {
501 struct gaih_servtuple *newp;
502
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;
508
509 *lastp = newp;
510 lastp = &newp->next;
511 }
512 }
513
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;
520
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;
525 }
526
527 #if defined __UCLIBC_HAS_IPV6__
528 if (at->family == AF_UNSPEC) {
529 char *namebuf = strdupa(name);
530 char *scope_delim;
531
532 scope_delim = strchr(namebuf, SCOPE_DELIMITER);
533 if (scope_delim != NULL)
534 *scope_delim = '\0';
535
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;
549
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);
556 }
557 }
558 }
559 }
560 #endif
561
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
569
570 /*
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.
574 */
575 no_inet6_data = no_data = 0;
576 #if defined __UCLIBC_HAS_IPV6__
577 if (__gai_precedence == 2)
578 goto try_v4;
579
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;
588
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)))
594 ) {
595 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV4))
596 gethosts(AF_INET, struct in_addr);
597 }
598 #if defined __UCLIBC_HAS_IPV6__
599 if (first_try) {
600 first_try = false;
601 goto try_v6;
602 }
603
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;
610 /*
611 * We made requests but they turned out no data.
612 * The name is known, though.
613 */
614 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
615 }
616 }
617
618 if (at->family == AF_UNSPEC)
619 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
620 } else {
621 struct gaih_addrtuple *atr;
622
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));
628 }
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;
635 }
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);
642 }
643 }
644 }
645
646 if (pai == NULL)
647 return 0;
648
649 {
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;
655
656 /*
657 * buffer is the size of an unformatted IPv6 address in
658 * printable format.
659 */
660 char buffer[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
661
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;
672
673 /* Hint says numeric, but address is not */
674 if (req->ai_flags & AI_NUMERICHOST)
675 return -EAI_NONAME;
676
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);
692
693 if (rc != 0 && herrno == NETDB_INTERNAL) {
694 __set_h_errno(herrno);
695 return -EAI_SYSTEM;
696 }
697
698 if (h != NULL)
699 c = h->h_name;
700
701 if (c == NULL)
702 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
703
704 namelen = strlen(c) + 1;
705 } else
706 namelen = 0;
707
708 #if defined __UCLIBC_HAS_IPV6__
709 if (at2->family == AF_INET6 || v4mapped) {
710 family = AF_INET6;
711 socklen = sizeof(struct sockaddr_in6);
712
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;
720 }
721 #endif
722 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
723 else
724 #endif
725 #if defined __UCLIBC_HAS_IPV4__
726 {
727 family = AF_INET;
728 socklen = sizeof(struct sockaddr_in);
729 }
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
739 }
740 *pai = malloc(sizeof(struct addrinfo) + socklen + namelen);
741 if (*pai == NULL)
742 return -EAI_MEMORY;
743
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;
754
755 #if defined __UCLIBC_HAS_IPV6__
756 if (family == AF_INET6) {
757 struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) (*pai)->ai_addr;
758
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]));
769 }
770 sin6p->sin6_port = st2->port;
771 sin6p->sin6_scope_id = at2->scopeid;
772 }
773 #endif
774 #if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
775 else
776 #endif
777 #if defined __UCLIBC_HAS_IPV4__
778 {
779 struct sockaddr_in *sinp = (struct sockaddr_in *) (*pai)->ai_addr;
780
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));
784 }
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;
792 }
793 (*pai)->ai_next = NULL;
794 pai = &((*pai)->ai_next);
795 }
796 ignore:
797 at2 = at2->next;
798 }
799 }
800 return 0;
801 }
802
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 }
812 };
813
814 #if defined __UCLIBC_HAS_IPV6__
815
816 /*
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.
820 *
821 * gai.conf format:
822 *
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.
836 *
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.
840 */
__gai_conf_parse(void)841 static void __gai_conf_parse(void)
842 {
843 /* NO reread of /etc/gai.conf on change. */
844 if (__gai_precedence != 0)
845 return;
846
847 __gai_precedence = 1; /* default IPv6 */
848
849 parser_t *parser;
850 char **tok = NULL;
851
852 parser = config_open("/etc/gai.conf");
853 if (!parser)
854 return;
855
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;
860
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 */
871 }
872 }
873 config_close(parser);
874 }
875 #else
876 # define __gai_conf_parse(x)
877 #endif /* __UCLIBC_HAS_IPV6__ */
878
879 void
freeaddrinfo(struct addrinfo * ai)880 freeaddrinfo(struct addrinfo *ai)
881 {
882 struct addrinfo *p;
883
884 while (ai != NULL) {
885 p = ai;
886 ai = ai->ai_next;
887 free(p);
888 }
889 }
libc_hidden_def(freeaddrinfo)890 libc_hidden_def(freeaddrinfo)
891
892 int
893 getaddrinfo(const char *name, const char *service,
894 const struct addrinfo *hints, struct addrinfo **pai)
895 {
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;
901
902 if (name != NULL && name[0] == '*' && name[1] == 0)
903 name = NULL;
904
905 if (service != NULL && service[0] == '*' && service[1] == 0)
906 service = NULL;
907
908 if (name == NULL && service == NULL)
909 return EAI_NONAME;
910
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;
916 }
917
918 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
919 AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
920 return EAI_BADFLAGS;
921
922 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
923 return EAI_BADFLAGS;
924
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;
933 }
934 pservice = &gaih_service;
935 } else
936 pservice = NULL;
937
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;
953 }
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);
964 }
965 if (end)
966 while (*end)
967 end = &((*end)->ai_next);
968 }
969 }
970 ++g;
971 }
972
973 if (j == 0)
974 return EAI_FAMILY;
975
976 if (p) {
977 *pai = p;
978 return 0;
979 }
980
981 if (pai == NULL && last_i == 0)
982 return 0;
983
984 /* if (p) - never happens, see above */
985 /* freeaddrinfo(p); */
986
987 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
988 }
989 libc_hidden_def(getaddrinfo)
990