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