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