blob: 88bd745a99704bfde42491278bbd93b14ed851ce [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/*
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
15binary forms, with or without modification, of the software and documentation
16provided that the following conditions are met:
17
180. 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.
221. All terms of the all other applicable copyrights and licenses must be
23 followed.
242. Redistributions of source code must retain the authors' copyright
25 notice(s), this list of conditions, and the following disclaimer.
263. 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.
294. 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
375. 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
41THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``AS IS'' AND ANY
42EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
43WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY
45DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
46(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
48ANY 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
50SOFTWARE, 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#define __FORCE_GLIBC
55#include <features.h>
56#include <assert.h>
57#include <errno.h>
58#include <netdb.h>
59#ifdef __UCLIBC_HAS_TLS__
60#include <tls.h>
61#endif
62#include <resolv.h>
63#include <stdio.h>
64#include <stdlib.h>
65#include <string.h>
66#include <unistd.h>
67#include <arpa/inet.h>
68#include <sys/socket.h>
69#include <netinet/in.h>
70#include <sys/types.h>
71#include <sys/un.h>
72#include <sys/utsname.h>
73#include <net/if.h>
74#include <ifaddrs.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 */
84typedef int8_t socktype_t;
85typedef int8_t family_t;
86typedef int8_t protocol_t;
87struct 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
104struct gaih_service {
105 const char *name;
106 int num;
107};
108
109struct gaih_servtuple {
110 struct gaih_servtuple *next;
111 int socktype;
112 int protocol;
113 int port;
114};
115
116struct gaih_addrtuple {
117 struct gaih_addrtuple *next;
118 int family;
119 char addr[16];
120 uint32_t scopeid;
121};
122
123struct 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
133static 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
141struct 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
150static 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
201static 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. */
234static int
235gaih_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 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
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
327static int
328gaih_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/* NB: also uses h,pat,rc,no_data variables */
354#define gethosts(_family, _type) \
355{ \
356 int i, herrno; \
357 size_t tmpbuflen; \
358 struct hostent th; \
359 char *tmpbuf; \
360 \
361 tmpbuflen = 512; \
362 no_data = 0; \
363 do { \
364 tmpbuflen *= 2; \
365 tmpbuf = alloca(tmpbuflen); \
366 rc = gethostbyname2_r(name, _family, &th, tmpbuf, \
367 tmpbuflen, &h, &herrno); \
368 } while (rc == ERANGE && herrno == NETDB_INTERNAL); \
369 if (rc != 0) { \
370 if (herrno == NETDB_INTERNAL) { \
371 __set_h_errno(herrno); \
372 return -EAI_SYSTEM; \
373 } \
374 if (herrno == TRY_AGAIN) \
375 no_data = EAI_AGAIN; \
376 else \
377 no_data = (herrno == NO_DATA); \
378 } else if (h != NULL) { \
379 for (i = 0; h->h_addr_list[i]; i++) { \
380 if (*pat == NULL) { \
381 *pat = alloca(sizeof(struct gaih_addrtuple)); \
382 (*pat)->scopeid = 0; \
383 } \
384 (*pat)->next = NULL; \
385 (*pat)->family = _family; \
386 memcpy((*pat)->addr, h->h_addr_list[i], sizeof(_type)); \
387 pat = &((*pat)->next); \
388 } \
389 } \
390}
391
392static int
393gaih_inet(const char *name, const struct gaih_service *service,
394 const struct addrinfo *req, struct addrinfo **pai)
395{
396 struct gaih_servtuple nullserv;
397
398 const struct gaih_typeproto *tp;
399 struct gaih_servtuple *st;
400 struct gaih_addrtuple *at;
401 int rc;
402 int v4mapped = (req->ai_family == PF_UNSPEC || req->ai_family == PF_INET6)
403 && (req->ai_flags & AI_V4MAPPED);
404 unsigned seen = 0;
405 if (req->ai_flags & AI_ADDRCONFIG) {
406 /* "seen" is only used when AI_ADDRCONFIG is specified.
407 Avoid unnecessary call to __check_pf() otherwise
408 since it can be costly especially when RSBAC-Net is enabled. */
409 seen = __check_pf();
410 }
411
412 memset(&nullserv, 0, sizeof(nullserv));
413
414 tp = gaih_inet_typeproto;
415 if (req->ai_protocol || req->ai_socktype) {
416 ++tp;
417 while (tp->name[0]) {
418 if ((req->ai_socktype == 0 || req->ai_socktype == tp->socktype)
419 && (req->ai_protocol == 0 || req->ai_protocol == tp->protocol || (tp->protoflag & GAI_PROTO_PROTOANY))
420 ) {
421 goto found;
422 }
423 ++tp;
424 }
425 if (req->ai_socktype)
426 return (GAIH_OKIFUNSPEC | -EAI_SOCKTYPE);
427 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
428 found: ;
429 }
430
431 st = &nullserv;
432 if (service != NULL) {
433 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
434 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
435
436 if (service->num < 0) {
437 if (tp->name[0]) {
438 st = alloca(sizeof(struct gaih_servtuple));
439 rc = gaih_inet_serv(service->name, tp, req, st);
440 if (rc)
441 return rc;
442 } else {
443 struct gaih_servtuple **pst = &st;
444 for (tp++; tp->name[0]; tp++) {
445 struct gaih_servtuple *newp;
446
447 if ((tp->protoflag & GAI_PROTO_NOSERVICE) != 0)
448 continue;
449
450 if (req->ai_socktype != 0 && req->ai_socktype != tp->socktype)
451 continue;
452 if (req->ai_protocol != 0
453 && !(tp->protoflag & GAI_PROTO_PROTOANY)
454 && req->ai_protocol != tp->protocol)
455 continue;
456
457 newp = alloca(sizeof(struct gaih_servtuple));
458 rc = gaih_inet_serv(service->name, tp, req, newp);
459 if (rc) {
460 if (rc & GAIH_OKIFUNSPEC)
461 continue;
462 return rc;
463 }
464
465 *pst = newp;
466 pst = &(newp->next);
467 }
468 if (st == &nullserv)
469 return (GAIH_OKIFUNSPEC | -EAI_SERVICE);
470 }
471 } else {
472 st = alloca(sizeof(struct gaih_servtuple));
473 st->next = NULL;
474 st->socktype = tp->socktype;
475 st->protocol = ((tp->protoflag & GAI_PROTO_PROTOANY)
476 ? req->ai_protocol : tp->protocol);
477 st->port = htons(service->num);
478 }
479 } else if (req->ai_socktype || req->ai_protocol) {
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 = 0;
486 } else {
487 /*
488 * Neither socket type nor protocol is set. Return all socket types
489 * we know about.
490 */
491 struct gaih_servtuple **lastp = &st;
492 for (++tp; tp->name[0]; ++tp) {
493 struct gaih_servtuple *newp;
494
495 newp = alloca(sizeof(struct gaih_servtuple));
496 newp->next = NULL;
497 newp->socktype = tp->socktype;
498 newp->protocol = tp->protocol;
499 newp->port = 0;
500
501 *lastp = newp;
502 lastp = &newp->next;
503 }
504 }
505
506 at = NULL;
507 if (name != NULL) {
508 at = alloca(sizeof(struct gaih_addrtuple));
509 at->family = AF_UNSPEC;
510 at->scopeid = 0;
511 at->next = NULL;
512
513 if (inet_pton(AF_INET, name, at->addr) > 0) {
514 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET && !v4mapped)
515 return -EAI_FAMILY;
516 at->family = AF_INET;
517 }
518
519#if defined __UCLIBC_HAS_IPV6__
520 if (at->family == AF_UNSPEC) {
521 char *namebuf = strdupa(name);
522 char *scope_delim;
523
524 scope_delim = strchr(namebuf, SCOPE_DELIMITER);
525 if (scope_delim != NULL)
526 *scope_delim = '\0';
527
528 if (inet_pton(AF_INET6, namebuf, at->addr) > 0) {
529 if (req->ai_family != AF_UNSPEC && req->ai_family != AF_INET6)
530 return -EAI_FAMILY;
531 at->family = AF_INET6;
532 if (scope_delim != NULL) {
533 int try_numericscope = 0;
534 uint32_t *a32 = (uint32_t*)at->addr;
535 if (IN6_IS_ADDR_LINKLOCAL(a32) || IN6_IS_ADDR_MC_LINKLOCAL(at->addr)) {
536 at->scopeid = if_nametoindex(scope_delim + 1);
537 if (at->scopeid == 0)
538 try_numericscope = 1;
539 } else
540 try_numericscope = 1;
541
542 if (try_numericscope != 0) {
543 char *end;
544 assert(sizeof(uint32_t) <= sizeof(unsigned long));
545 at->scopeid = (uint32_t)strtoul(scope_delim + 1, &end, 10);
546 if (*end != '\0')
547 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
548 }
549 }
550 }
551 }
552#endif
553
554 if (at->family == AF_UNSPEC && !(req->ai_flags & AI_NUMERICHOST)) {
555 struct hostent *h;
556 struct gaih_addrtuple **pat = &at;
557 int no_data = 0;
558 int no_inet6_data;
559
560 /*
561 * If we are looking for both IPv4 and IPv6 address we don't want
562 * the lookup functions to automatically promote IPv4 addresses to
563 * IPv6 addresses.
564 */
565#if defined __UCLIBC_HAS_IPV6__
566 if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET6)
567 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV6))
568 gethosts(AF_INET6, struct in6_addr);
569#endif
570 no_inet6_data = no_data;
571
572 if (req->ai_family == AF_INET
573 || (!v4mapped && req->ai_family == AF_UNSPEC)
574 || (v4mapped && (no_inet6_data != 0 || (req->ai_flags & AI_ALL)))
575 ) {
576 if (!(req->ai_flags & AI_ADDRCONFIG) || (seen & SEEN_IPV4))
577 gethosts(AF_INET, struct in_addr);
578 }
579
580 if (no_data != 0 && no_inet6_data != 0) {
581 /* If both requests timed out report this. */
582 if (no_data == EAI_AGAIN && no_inet6_data == EAI_AGAIN)
583 return -EAI_AGAIN;
584 /*
585 * We made requests but they turned out no data.
586 * The name is known, though.
587 */
588 return (GAIH_OKIFUNSPEC | -EAI_AGAIN);
589 }
590 }
591
592 if (at->family == AF_UNSPEC)
593 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
594 } else {
595 struct gaih_addrtuple *atr;
596
597 atr = at = alloca(sizeof(struct gaih_addrtuple));
598 memset(at, '\0', sizeof(struct gaih_addrtuple));
599 if (req->ai_family == 0) {
600 at->next = alloca(sizeof(struct gaih_addrtuple));
601 memset(at->next, '\0', sizeof(struct gaih_addrtuple));
602 }
603#if defined __UCLIBC_HAS_IPV6__
604 if (req->ai_family == 0 || req->ai_family == AF_INET6) {
605 at->family = AF_INET6;
606 if ((req->ai_flags & AI_PASSIVE) == 0)
607 memcpy(at->addr, &in6addr_loopback, sizeof(struct in6_addr));
608 atr = at->next;
609 }
610#endif
611 if (req->ai_family == 0 || req->ai_family == AF_INET) {
612 atr->family = AF_INET;
613 if ((req->ai_flags & AI_PASSIVE) == 0) {
614 uint32_t *a = (uint32_t*)atr->addr;
615 *a = htonl(INADDR_LOOPBACK);
616 }
617 }
618 }
619
620 if (pai == NULL)
621 return 0;
622
623 {
624 const char *c = NULL;
625 struct gaih_servtuple *st2;
626 struct gaih_addrtuple *at2 = at;
627 size_t socklen, namelen;
628 sa_family_t family;
629
630 /*
631 * buffer is the size of an unformatted IPv6 address in
632 * printable format.
633 */
634 char buffer[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
635
636 while (at2 != NULL) {
637 c = inet_ntop(at2->family, at2->addr, buffer, sizeof(buffer));
638 if (c) {
639 namelen = strlen(c) + 1;
640 } else if (req->ai_flags & AI_CANONNAME) {
641 struct hostent *h = NULL;
642 int herrno;
643 struct hostent th;
644 size_t tmpbuflen = 512;
645 char *tmpbuf;
646
647 /* Hint says numeric, but address is not */
648 if (req->ai_flags & AI_NUMERICHOST)
649 return -EAI_NONAME;
650
651 do {
652 tmpbuflen *= 2;
653 tmpbuf = alloca(tmpbuflen);
654 rc = gethostbyaddr_r(at2->addr,
655#ifdef __UCLIBC_HAS_IPV6__
656 ((at2->family == AF_INET6)
657 ? sizeof(struct in6_addr)
658 : sizeof(struct in_addr)),
659#else
660 sizeof(struct in_addr),
661#endif
662 at2->family,
663 &th, tmpbuf, tmpbuflen,
664 &h, &herrno);
665 } while (rc == ERANGE && herrno == NETDB_INTERNAL);
666
667 if (rc != 0 && herrno == NETDB_INTERNAL) {
668 __set_h_errno(herrno);
669 return -EAI_SYSTEM;
670 }
671
672 if (h != NULL)
673 c = h->h_name;
674
675 if (c == NULL)
676 return (GAIH_OKIFUNSPEC | -EAI_NONAME);
677
678 namelen = strlen(c) + 1;
679 } else
680 namelen = 0;
681
682#if defined __UCLIBC_HAS_IPV6__
683 if (at2->family == AF_INET6 || v4mapped) {
684 family = AF_INET6;
685 socklen = sizeof(struct sockaddr_in6);
686 }
687#endif
688#if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
689 else
690#endif
691#if defined __UCLIBC_HAS_IPV4__
692 {
693 family = AF_INET;
694 socklen = sizeof(struct sockaddr_in);
695 }
696#endif
697 for (st2 = st; st2 != NULL; st2 = st2->next) {
698 if (req->ai_flags & AI_ADDRCONFIG) {
699 if (family == AF_INET && !(seen & SEEN_IPV4))
700 break;
701#if defined __UCLIBC_HAS_IPV6__
702 else if (family == AF_INET6 && !(seen & SEEN_IPV6))
703 break;
704#endif
705 }
706 *pai = malloc(sizeof(struct addrinfo) + socklen + namelen);
707 if (*pai == NULL)
708 return -EAI_MEMORY;
709
710 (*pai)->ai_flags = req->ai_flags;
711 (*pai)->ai_family = family;
712 (*pai)->ai_socktype = st2->socktype;
713 (*pai)->ai_protocol = st2->protocol;
714 (*pai)->ai_addrlen = socklen;
715 (*pai)->ai_addr = (void *) (*pai) + sizeof(struct addrinfo);
716#if defined SALEN
717 (*pai)->ai_addr->sa_len = socklen;
718#endif
719 (*pai)->ai_addr->sa_family = family;
720
721#if defined __UCLIBC_HAS_IPV6__
722 if (family == AF_INET6) {
723 struct sockaddr_in6 *sin6p = (struct sockaddr_in6 *) (*pai)->ai_addr;
724
725 sin6p->sin6_flowinfo = 0;
726 if (at2->family == AF_INET6) {
727 memcpy(&sin6p->sin6_addr,
728 at2->addr, sizeof(struct in6_addr));
729 } else {
730 sin6p->sin6_addr.s6_addr32[0] = 0;
731 sin6p->sin6_addr.s6_addr32[1] = 0;
732 sin6p->sin6_addr.s6_addr32[2] = htonl(0x0000ffff);
733 memcpy(&sin6p->sin6_addr.s6_addr32[3],
734 at2->addr, sizeof(sin6p->sin6_addr.s6_addr32[3]));
735 }
736 sin6p->sin6_port = st2->port;
737 sin6p->sin6_scope_id = at2->scopeid;
738 }
739#endif
740#if defined __UCLIBC_HAS_IPV4__ && defined __UCLIBC_HAS_IPV6__
741 else
742#endif
743#if defined __UCLIBC_HAS_IPV4__
744 {
745 struct sockaddr_in *sinp = (struct sockaddr_in *) (*pai)->ai_addr;
746
747 memcpy(&sinp->sin_addr, at2->addr, sizeof(struct in_addr));
748 sinp->sin_port = st2->port;
749 memset(sinp->sin_zero, '\0', sizeof(sinp->sin_zero));
750 }
751#endif
752 if (c) {
753 (*pai)->ai_canonname = ((void *) (*pai) +
754 sizeof(struct addrinfo) + socklen);
755 strcpy((*pai)->ai_canonname, c);
756 } else {
757 (*pai)->ai_canonname = NULL;
758 }
759 (*pai)->ai_next = NULL;
760 pai = &((*pai)->ai_next);
761 }
762
763 at2 = at2->next;
764 }
765 }
766 return 0;
767}
768
769static const struct gaih gaih[] = {
770#if defined __UCLIBC_HAS_IPV6__
771 { PF_INET6, gaih_inet },
772#endif
773 { PF_INET, gaih_inet },
774#if 0
775 { PF_LOCAL, gaih_local },
776#endif
777 { PF_UNSPEC, NULL }
778};
779
780void
781freeaddrinfo(struct addrinfo *ai)
782{
783 struct addrinfo *p;
784
785 while (ai != NULL) {
786 p = ai;
787 ai = ai->ai_next;
788 free(p);
789 }
790}
791libc_hidden_def(freeaddrinfo)
792
793int
794getaddrinfo(const char *name, const char *service,
795 const struct addrinfo *hints, struct addrinfo **pai)
796{
797 int i, j, last_i;
798 struct addrinfo *p, **end;
799 const struct gaih *g, *pg;
800 struct gaih_service gaih_service, *pservice;
801 struct addrinfo default_hints;
802
803 if (name != NULL && name[0] == '*' && name[1] == 0)
804 name = NULL;
805
806 if (service != NULL && service[0] == '*' && service[1] == 0)
807 service = NULL;
808
809 if (name == NULL && service == NULL)
810 return EAI_NONAME;
811
812 if (hints == NULL) {
813 memset(&default_hints, 0, sizeof(default_hints));
814 if (AF_UNSPEC != 0)
815 default_hints.ai_family = AF_UNSPEC;
816 hints = &default_hints;
817 }
818
819 if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
820 AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
821 return EAI_BADFLAGS;
822
823 if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
824 return EAI_BADFLAGS;
825
826 if (service && service[0]) {
827 char *c;
828 gaih_service.name = service;
829 gaih_service.num = strtoul(gaih_service.name, &c, 10);
830 if (*c != '\0') {
831 if (hints->ai_flags & AI_NUMERICSERV)
832 return EAI_NONAME;
833 gaih_service.num = -1;
834 }
835 pservice = &gaih_service;
836 } else
837 pservice = NULL;
838
839 g = gaih;
840 pg = NULL;
841 p = NULL;
842 end = NULL;
843 if (pai)
844 end = &p;
845 i = 0;
846 last_i = 0;
847 j = 0;
848 while (g->gaih) {
849 if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) {
850 if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) {
851 ++g;
852 continue;
853 }
854 j++;
855 if (pg == NULL || pg->gaih != g->gaih) {
856 pg = g;
857 i = g->gaih(name, pservice, hints, end);
858 if (i != 0) {
859 last_i = i;
860 if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
861 continue;
862 /*if (p) - freeaddrinfo works ok on NULL too */
863 freeaddrinfo(p);
864 return -(i & GAIH_EAI);
865 }
866 if (end)
867 while (*end)
868 end = &((*end)->ai_next);
869 }
870 }
871 ++g;
872 }
873
874 if (j == 0)
875 return EAI_FAMILY;
876
877 if (p) {
878 *pai = p;
879 return 0;
880 }
881
882 if (pai == NULL && last_i == 0)
883 return 0;
884
885 /* if (p) - never happens, see above */
886 /* freeaddrinfo(p); */
887
888 return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
889}
890libc_hidden_def(getaddrinfo)