1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  *	gethostent6.c
27  */
28 
29 #pragma ident	"%Z%%M%	%I%	%E% SMI"
30 
31 /*
32  * This is the DNS backend for IPv6 addresses.
33  * getbyname() is a local routine, but getbyaddr() actually shares the
34  * same codes as the one in gethostent.c.
35  */
36 
37 #define	endhostent	res_endhostent
38 
39 #include <malloc.h>
40 #include <stddef.h>
41 #include <string.h>
42 #include "dns_common.h"
43 
44 /*
45  * If the DNS name service switch routines are used in a binary that depends
46  * on an older libresolv (libresolv.so.1, say), then having nss_dns.so.1 or
47  * libnss_dns.a depend on a newer libresolv (libresolv.so.2) will cause
48  * relocation problems. In particular, copy relocation of the _res structure
49  * (which changes in size from libresolv.so.1 to libresolv.so.2) could
50  * cause corruption, and result in a number of strange problems, including
51  * core dumps. Hence, we check if a libresolv is already loaded.
52  */
53 
54 
55 #pragma weak	res_endhostent
56 
57 extern struct hostent *_gethostbyname(int *, const char *);
58 extern struct hostent *_nss_dns_gethostbyname2(int *, const char *);
59 
60 typedef union {
61 	long al;
62 	char ac;
63 } align;
64 
65 
66 static void
67 _endhostent(errp)
68 	nss_status_t	*errp;
69 {
70 	int	ret;
71 
72 	ret = endhostent();
73 	if (ret == 0)
74 		*errp = NSS_SUCCESS;
75 	else
76 		*errp = NSS_UNAVAIL;
77 }
78 
79 
80 #ifdef	RNDUP
81 #undef	RNDUP
82 #endif
83 #define	RNDUP(x)	((1 + (((x)-1)/sizeof (void *))) * sizeof (void *))
84 
85 #ifdef	PTROFF
86 #undef	PTROFF
87 #endif
88 #define	PTROFF(p, o)	(((o) == 0) ? 0 : (void *)((char *)(p) + (o)))
89 
90 
91 /*
92  * Make a copy of h->h_name.
93  */
94 static char *
95 cloneName(struct hostent *h, int *outerr) {
96 
97 	char	*name;
98 	int	len;
99 	int	error, *errp;
100 
101 	if (outerr)
102 		errp = outerr;
103 	else
104 		errp = &error;
105 
106 	if (h == 0 || h->h_name == 0) {
107 		*errp = 0;
108 		return (0);
109 	}
110 
111 	len = strlen(h->h_name);
112 
113 	if ((name = malloc(len+1)) == 0) {
114 		*errp = 1;
115 		return (0);
116 	}
117 
118 	(void) memcpy(name, h->h_name, len+1);
119 
120 	*errp = 0;
121 	return (name);
122 }
123 
124 
125 /*
126  * Copy the h->h_addr_list[] array to a new array, and append the
127  * moreAddrs[] list. If h->h_addr_list[] contains IPv4 addresses,
128  * convert them to v4 mapped IPv6 addresses.
129  *
130  * Note: The pointers to the addresses in the moreAddrs[] array are copied,
131  *       but not the IP addresses themselves.
132  */
133 static struct in6_addr **
134 cloneAddrList(struct hostent *h, struct in6_addr **moreAddrs, int *outerr) {
135 
136 	struct in6_addr	**addrArray, *addrList;
137 	int		domap, addrlen, i, j, addrCount, moreAddrCount = 0;
138 
139 	int	error, *errp;
140 
141 	if (outerr)
142 		errp = outerr;
143 	else
144 		errp = &error;
145 
146 	if (h == 0 || h->h_addr_list == 0) {
147 		*errp = 0;
148 		return (0);
149 	}
150 
151 	/* Should we map v4 to IPv6 ? */
152 	domap = (h->h_length == sizeof (struct in_addr)) &&
153 		(h->h_addrtype == AF_INET);
154 
155 	/* If mapping, make sure we allocate enough memory for addresses */
156 	addrlen = h->h_length;
157 	if (domap && addrlen < sizeof (struct in6_addr))
158 		addrlen = sizeof (struct in6_addr);
159 
160 	for (addrCount = 0; h->h_addr_list[addrCount]; addrCount++);
161 
162 	if (moreAddrs != 0) {
163 		for (moreAddrCount = 0; moreAddrs[moreAddrCount];
164 			moreAddrCount++);
165 	}
166 
167 	if ((addrArray = malloc((addrCount+moreAddrCount+1)*sizeof (addrList) +
168 				addrCount*addrlen)) == 0) {
169 		*errp = 1;
170 		return (0);
171 	}
172 
173 	addrList = PTROFF(addrArray, (addrCount+moreAddrCount+1) *
174 					sizeof (addrList));
175 
176 	for (i = 0; i < addrCount; i++) {
177 		addrArray[i] = addrList;
178 		if (domap) {
179 			/* LINTED: E_BAD_PTR_CAST_ALIGN */
180 			IN6_INADDR_TO_V4MAPPED(
181 			(struct in_addr *)h->h_addr_list[i], addrArray[i]);
182 		} else {
183 			(void) memcpy(addrArray[i], h->h_addr_list[i],
184 				addrlen);
185 		}
186 		addrList = PTROFF(addrList, addrlen);
187 	}
188 
189 	for (j = 0; j < moreAddrCount; j++, i++) {
190 		addrArray[i] = moreAddrs[j];
191 	}
192 
193 	/* Last pointer should be NULL */
194 	addrArray[i] = 0;
195 
196 	*errp = 0;
197 	return (addrArray);
198 }
199 
200 
201 /*
202  * Create a new alias array that is is a copy of h->h_aliases[] plus
203  * the aliases in mergeAliases[] which aren't duplicates of any alias
204  * in h->h_aliases[].
205  *
206  * Note 1: Only the string pointers (NOT the strings) in the mergeAliases[]
207  *         array are copied.
208  *
209  * Note 2: The duplicate aliases in mergeAliases[] are replaced by NULL
210  *         pointers.
211  */
212 static char **
213 cloneAliasList(struct hostent *h, char **mergeAliases, int *outerr) {
214 
215 	char	**aliasArray, *aliasList;
216 	int	i, j, aliasCount, mergeAliasCount = 0, realMac = 0;
217 	int	stringSize = 0;
218 	int	error, *errp;
219 
220 	if (outerr)
221 		errp = outerr;
222 	else
223 		errp = &error;
224 
225 
226 	if (h == 0 || h->h_aliases == 0) {
227 		*errp = 0;
228 		return (0);
229 	}
230 
231 	for (aliasCount = 0; h->h_aliases[aliasCount]; aliasCount++) {
232 		stringSize += RNDUP(strlen(h->h_aliases[aliasCount])+1);
233 	}
234 
235 	if (mergeAliases != 0) {
236 		for (; mergeAliases[mergeAliasCount]; mergeAliasCount++) {
237 			int	countThis = 1;
238 			/* Skip duplicates */
239 			for (j = 0; j < aliasCount; j++) {
240 				if (strcmp(mergeAliases[mergeAliasCount],
241 						h->h_aliases[j]) == 0) {
242 					countThis = 0;
243 					break;
244 				}
245 			}
246 			if (countThis)
247 				realMac++;
248 			else
249 				mergeAliases[mergeAliasCount] = 0;
250 		}
251 	}
252 
253 	if ((aliasArray = malloc((aliasCount+realMac+1)*sizeof (char **)+
254 				stringSize)) == 0) {
255 		*errp = 1;
256 		return (0);
257 	}
258 
259 	aliasList = PTROFF(aliasArray,
260 				(aliasCount+realMac+1)*sizeof (char **));
261 	for (i = 0; i < aliasCount; i++) {
262 		int	len = strlen(h->h_aliases[i]);
263 		aliasArray[i] = aliasList;
264 		(void) memcpy(aliasArray[i], h->h_aliases[i], len+1);
265 		aliasList = PTROFF(aliasList, RNDUP(len+1));
266 	}
267 
268 	for (j = 0; j < mergeAliasCount; j++) {
269 		if (mergeAliases[j] != 0) {
270 			aliasArray[i++] = mergeAliases[j];
271 		}
272 	}
273 
274 	aliasArray[i] = 0;
275 
276 	*errp = 0;
277 	return (aliasArray);
278 }
279 
280 /*ARGSUSED*/
281 static nss_status_t
282 getbyname(be, a)
283 	dns_backend_ptr_t	be;
284 	void			*a;
285 {
286 	struct hostent	*he = NULL;
287 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
288 	int		ret, mt_disabled;
289 	sigset_t	oldmask;
290 	int		converr = 0, gotv6 = 0;
291 	struct hostent	v6he;
292 	struct hostent	mhe;
293 	char		*v6Name = 0;
294 	struct in6_addr	**v6Addrs = 0, **mergeAddrs = 0;
295 	char		**v6Aliases = 0, **mergeAliases = 0;
296 	int		v6_h_errno;
297 	int		old_retry;
298 	int		af = argp->key.ipnode.af_family;
299 	int		flags = argp->key.ipnode.flags;
300 
301 	switch_resolver_setup(&mt_disabled, &oldmask, &old_retry);
302 
303 	/* Now get the AAAA records */
304 	if (af == AF_INET6)
305 		he = _nss_dns_gethostbyname2(&argp->h_errno,
306 					argp->key.ipnode.name);
307 	if (he != NULL) {
308 		/*
309 		 * pointer in "he" is part of a static pthread key in libresolv
310 		 * It should be treated as read only.
311 		 * So clone a copy first.
312 		 */
313 		v6Name = cloneName(he, &converr);
314 		if (converr) {
315 			argp->h_errno = HOST_NOT_FOUND;
316 			argp->erange = 1;
317 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
318 			return (_herrno2nss(argp->h_errno));
319 		}
320 		v6Addrs = cloneAddrList(he, 0, &converr);
321 		if (converr) {
322 			if (v6Name != 0)
323 				free(v6Name);
324 			argp->h_errno = HOST_NOT_FOUND;
325 			argp->erange = 1;
326 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
327 			return (_herrno2nss(argp->h_errno));
328 		}
329 		v6Aliases = cloneAliasList(he, 0, &converr);
330 		if (converr) {
331 			if (v6Name != 0)
332 				free(v6Name);
333 			if (v6Addrs != 0)
334 				free(v6Addrs);
335 			argp->h_errno = HOST_NOT_FOUND;
336 			argp->erange = 1;
337 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
338 			return (_herrno2nss(argp->h_errno));
339 		}
340 		v6_h_errno = argp->h_errno;
341 		gotv6 = 1;
342 	}
343 
344 	/*
345 	 * The conditions to search "A" records:
346 	 * 1. af is AF_INET
347 	 * 2. if af is AF_INET6
348 	 *    then flags are either
349 	 *	1) (AI_ALL | AI_V4MAPPED) or
350 	 *	2) AI_V4MAPPED and he == NULL
351 	 *	    (No V6 addresses found or no search for V6 at all)
352 	 */
353 
354 	/* Get the A records, and store the information */
355 	if ((af == AF_INET) ||
356 	    ((af == AF_INET6) &&
357 		((flags & (AI_ALL | AI_V4MAPPED)) ||
358 		((flags & AI_V4MAPPED) && he == NULL))))
359 		he = _gethostbyname(&argp->h_errno, argp->key.ipnode.name);
360 	else
361 		he = NULL;
362 
363 	/* Merge the results */
364 	if (he != NULL) {
365 		mhe = *he;
366 		mergeAddrs = cloneAddrList(he, v6Addrs, &converr);
367 		if (converr) {
368 			if (v6Name != 0)
369 				free(v6Name);
370 			if (v6Addrs != 0)
371 				free(v6Addrs);
372 			if (v6Aliases != 0)
373 				free(v6Aliases);
374 			argp->h_errno = HOST_NOT_FOUND;
375 			argp->erange = 1;
376 			switch_resolver_reset(mt_disabled, oldmask,
377 						old_retry);
378 			return (_herrno2nss(argp->h_errno));
379 		}
380 		mhe.h_addr_list = (char **)mergeAddrs;
381 
382 		mergeAliases = cloneAliasList(he, v6Aliases, &converr);
383 		if (converr) {
384 			if (v6Name != 0)
385 				free(v6Name);
386 			if (v6Addrs != 0)
387 				free(v6Addrs);
388 			if (v6Aliases != 0)
389 				free(v6Aliases);
390 			if (mergeAddrs != 0)
391 				free(mergeAddrs);
392 			argp->h_errno = HOST_NOT_FOUND;
393 			argp->erange = 1;
394 			switch_resolver_reset(mt_disabled, oldmask,
395 						old_retry);
396 			return (_herrno2nss(argp->h_errno));
397 		}
398 		mhe.h_aliases = mergeAliases;
399 
400 		/* reset h_length, h_addrtype */
401 		mhe.h_length = sizeof (struct in6_addr);
402 		mhe.h_addrtype = AF_INET6;
403 		he = &mhe;
404 
405 	} else if (gotv6) {
406 		v6he.h_name = v6Name;
407 		v6he.h_length = sizeof (struct in6_addr);
408 		v6he.h_addrtype = AF_INET6;
409 		v6he.h_addr_list = (char **)v6Addrs;
410 		v6he.h_aliases = v6Aliases;
411 		he = &v6he;
412 		argp->h_errno = v6_h_errno;
413 	}
414 
415 	if (he != NULL) {
416 		/*
417 		 * if asked to return data in string,
418 		 * convert the hostent structure into
419 		 * string data
420 		 */
421 		if (argp->buf.result == NULL) {
422 			ret = ent2str(he, a, AF_INET6);
423 			if (ret == NSS_STR_PARSE_SUCCESS)
424 				argp->returnval = argp->buf.buffer;
425 		} else {
426 			ret = ent2result(he, a, AF_INET6);
427 			if (ret == NSS_STR_PARSE_SUCCESS)
428 				argp->returnval = argp->buf.result;
429 		}
430 
431 		if (ret != NSS_STR_PARSE_SUCCESS) {
432 			argp->h_errno = HOST_NOT_FOUND;
433 			if (ret == NSS_STR_PARSE_ERANGE) {
434 				argp->erange = 1;
435 			}
436 		}
437 	}
438 
439 	if (v6Name != 0)
440 		free(v6Name);
441 	if (v6Addrs != 0)
442 		free(v6Addrs);
443 	if (v6Aliases != 0)
444 		free(v6Aliases);
445 	if (mergeAddrs != 0)
446 		free(mergeAddrs);
447 	if (mergeAliases != 0)
448 		free(mergeAliases);
449 
450 	switch_resolver_reset(mt_disabled, oldmask, old_retry);
451 
452 	return (_herrno2nss(argp->h_errno));
453 }
454 
455 
456 extern nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *);
457 
458 static nss_status_t
459 getbyaddr(be, a)
460 	dns_backend_ptr_t	be;
461 	void			*a;
462 {
463 	/* uses the same getbyaddr from IPv4 */
464 	return (__nss_dns_getbyaddr(be, a));
465 }
466 
467 
468 /*ARGSUSED*/
469 static nss_status_t
470 _nss_dns_getent(be, args)
471 	dns_backend_ptr_t	be;
472 	void			*args;
473 {
474 	return (NSS_UNAVAIL);
475 }
476 
477 
478 /*ARGSUSED*/
479 static nss_status_t
480 _nss_dns_setent(be, dummy)
481 	dns_backend_ptr_t	be;
482 	void			*dummy;
483 {
484 	/* XXXX not implemented at this point */
485 	return (NSS_UNAVAIL);
486 }
487 
488 
489 /*ARGSUSED*/
490 static nss_status_t
491 _nss_dns_endent(be, dummy)
492 	dns_backend_ptr_t	be;
493 	void			*dummy;
494 {
495 	/* XXXX not implemented at this point */
496 	return (NSS_UNAVAIL);
497 }
498 
499 
500 /*ARGSUSED*/
501 static nss_status_t
502 _nss_dns_destr(be, dummy)
503 	dns_backend_ptr_t	be;
504 	void			*dummy;
505 {
506 	nss_status_t	errp;
507 
508 	if (be != 0) {
509 		/* === Should change to invoke ops[ENDENT] ? */
510 		sigset_t	oldmask, newmask;
511 		int		mt_disabled = 1;
512 
513 		if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) {
514 			(void) sigfillset(&newmask);
515 			_thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
516 			_mutex_lock(&one_lane);
517 		}
518 
519 		_endhostent(&errp);
520 
521 		if (mt_disabled) {
522 			_mutex_unlock(&one_lane);
523 			_thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
524 		} else {
525 			(void) (*disable_mt)();
526 		}
527 
528 		free(be);
529 	}
530 	return (NSS_SUCCESS);   /* In case anyone is dumb enough to check */
531 }
532 
533 
534 
535 static dns_backend_op_t ipnodes_ops[] = {
536 	_nss_dns_destr,
537 	_nss_dns_endent,
538 	_nss_dns_setent,
539 	_nss_dns_getent,
540 	getbyname,
541 	getbyaddr,
542 };
543 
544 /*ARGSUSED*/
545 nss_backend_t *
546 _nss_dns_ipnodes_constr(dummy1, dummy2, dummy3)
547 	const char	*dummy1, *dummy2, *dummy3;
548 {
549 	return (_nss_dns_constr(ipnodes_ops,
550 		sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
551 }
552 
553 /*
554  * optional NSS2 packed backend gethostsbyipnode with ttl
555  * entry point.
556  *
557  * Returns:
558  *	NSS_SUCCESS - successful
559  *	NSS_NOTFOUND - successful but nothing found
560  *	NSS_ERROR - fallback to NSS backend lookup mode
561  * If successful, buffer will be filled with valid data
562  *
563  */
564 
565 /*ARGSUSED*/
566 nss_status_t
567 _nss_get_dns_ipnodes_name(dns_backend_ptr_t *be, void **bufp, size_t *sizep)
568 {
569 	return (_nss_dns_gethost_withttl(*bufp, *sizep, 1));
570 }
571