xref: /illumos-gate/usr/src/lib/nsswitch/dns/common/gethostent6.c (revision f48205be61a214698b763ff550ab9e657525104c)
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 2006 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 	char		*v6Name = 0;
293 	struct in6_addr	**v6Addrs = 0, **mergeAddrs = 0;
294 	char		**v6Aliases = 0, **mergeAliases = 0;
295 	int		v6_h_errno;
296 	int		old_retry;
297 	int		af = argp->key.ipnode.af_family;
298 	int		flags = argp->key.ipnode.flags;
299 
300 	switch_resolver_setup(&mt_disabled, &oldmask, &old_retry);
301 
302 	/* Now get the AAAA records */
303 	if (af == AF_INET6)
304 		he = _nss_dns_gethostbyname2(&argp->h_errno,
305 					argp->key.ipnode.name);
306 	if (he != NULL) {
307 		/*
308 		 * pointer in "he" is part of a static pthread key in libresolv
309 		 * It should be treated as read only.
310 		 * So clone a copy first.
311 		 */
312 		v6Name = cloneName(he, &converr);
313 		if (converr) {
314 			argp->h_errno = HOST_NOT_FOUND;
315 			argp->erange = 1;
316 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
317 			return (_herrno2nss(argp->h_errno));
318 		}
319 		v6Addrs = cloneAddrList(he, 0, &converr);
320 		if (converr) {
321 			if (v6Name != 0)
322 				free(v6Name);
323 			argp->h_errno = HOST_NOT_FOUND;
324 			argp->erange = 1;
325 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
326 			return (_herrno2nss(argp->h_errno));
327 		}
328 		v6Aliases = cloneAliasList(he, 0, &converr);
329 		if (converr) {
330 			if (v6Name != 0)
331 				free(v6Name);
332 			if (v6Addrs != 0)
333 				free(v6Addrs);
334 			argp->h_errno = HOST_NOT_FOUND;
335 			argp->erange = 1;
336 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
337 			return (_herrno2nss(argp->h_errno));
338 		}
339 		v6_h_errno = argp->h_errno;
340 		gotv6 = 1;
341 	}
342 
343 	/*
344 	 * The conditions to search "A" records:
345 	 * 1. af is AF_INET
346 	 * 2. if af is AF_INET6
347 	 *    then flags are either
348 	 *	1) (AI_ALL | AI_V4MAPPED) or
349 	 *	2) AI_V4MAPPED and he == NULL
350 	 *	    (No V6 addresses found or no search for V6 at all)
351 	 */
352 
353 	/* Get the A records, and store the information */
354 	if ((af == AF_INET) ||
355 	    ((af == AF_INET6) &&
356 		((flags & (AI_ALL | AI_V4MAPPED)) ||
357 		((flags & AI_V4MAPPED) && he == NULL))))
358 		he = _gethostbyname(&argp->h_errno, argp->key.ipnode.name);
359 	else
360 		he = NULL;
361 
362 	/* Merge the results */
363 	if (he != NULL) {
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 		he->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 		he->h_aliases = mergeAliases;
397 
398 		/* reset h_length, h_addrtype */
399 		he->h_length = sizeof (struct in6_addr);
400 		he->h_addrtype = AF_INET6;
401 
402 	} else if (gotv6) {
403 		v6he.h_name = v6Name;
404 		v6he.h_length = sizeof (struct in6_addr);
405 		v6he.h_addrtype = AF_INET6;
406 		v6he.h_addr_list = (char **)v6Addrs;
407 		v6he.h_aliases = v6Aliases;
408 		he = &v6he;
409 		argp->h_errno = v6_h_errno;
410 	}
411 
412 	if (he != NULL) {
413 		/*
414 		 * if asked to return data in string,
415 		 * convert the hostent structure into
416 		 * string data
417 		 */
418 		if (argp->buf.result == NULL) {
419 			ret = ent2str(he, a, AF_INET6);
420 			if (ret == NSS_STR_PARSE_SUCCESS)
421 				argp->returnval = argp->buf.buffer;
422 		} else {
423 			ret = ent2result(he, a, AF_INET6);
424 			if (ret == NSS_STR_PARSE_SUCCESS)
425 				argp->returnval = argp->buf.result;
426 		}
427 
428 		if (ret != NSS_STR_PARSE_SUCCESS) {
429 			argp->h_errno = HOST_NOT_FOUND;
430 			if (ret == NSS_STR_PARSE_ERANGE) {
431 				argp->erange = 1;
432 			}
433 		}
434 	}
435 
436 	if (v6Name != 0)
437 		free(v6Name);
438 	if (v6Addrs != 0)
439 		free(v6Addrs);
440 	if (v6Aliases != 0)
441 		free(v6Aliases);
442 	if (mergeAddrs != 0)
443 		free(mergeAddrs);
444 	if (mergeAliases != 0)
445 		free(mergeAliases);
446 
447 	switch_resolver_reset(mt_disabled, oldmask, old_retry);
448 
449 	return (_herrno2nss(argp->h_errno));
450 }
451 
452 
453 extern nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *);
454 
455 static nss_status_t
456 getbyaddr(be, a)
457 	dns_backend_ptr_t	be;
458 	void			*a;
459 {
460 	/* uses the same getbyaddr from IPv4 */
461 	return (__nss_dns_getbyaddr(be, a));
462 }
463 
464 
465 /*ARGSUSED*/
466 static nss_status_t
467 _nss_dns_getent(be, args)
468 	dns_backend_ptr_t	be;
469 	void			*args;
470 {
471 	return (NSS_UNAVAIL);
472 }
473 
474 
475 /*ARGSUSED*/
476 static nss_status_t
477 _nss_dns_setent(be, dummy)
478 	dns_backend_ptr_t	be;
479 	void			*dummy;
480 {
481 	/* XXXX not implemented at this point */
482 	return (NSS_UNAVAIL);
483 }
484 
485 
486 /*ARGSUSED*/
487 static nss_status_t
488 _nss_dns_endent(be, dummy)
489 	dns_backend_ptr_t	be;
490 	void			*dummy;
491 {
492 	/* XXXX not implemented at this point */
493 	return (NSS_UNAVAIL);
494 }
495 
496 
497 /*ARGSUSED*/
498 static nss_status_t
499 _nss_dns_destr(be, dummy)
500 	dns_backend_ptr_t	be;
501 	void			*dummy;
502 {
503 	nss_status_t	errp;
504 
505 	if (be != 0) {
506 		/* === Should change to invoke ops[ENDENT] ? */
507 		sigset_t	oldmask, newmask;
508 		int		mt_disabled = 1;
509 
510 		if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) {
511 			(void) sigfillset(&newmask);
512 			_thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
513 			_mutex_lock(&one_lane);
514 		}
515 
516 		_endhostent(&errp);
517 
518 		if (mt_disabled) {
519 			_mutex_unlock(&one_lane);
520 			_thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
521 		} else {
522 			(void) (*disable_mt)();
523 		}
524 
525 		free(be);
526 	}
527 	return (NSS_SUCCESS);   /* In case anyone is dumb enough to check */
528 }
529 
530 
531 
532 static dns_backend_op_t ipnodes_ops[] = {
533 	_nss_dns_destr,
534 	_nss_dns_endent,
535 	_nss_dns_setent,
536 	_nss_dns_getent,
537 	getbyname,
538 	getbyaddr,
539 };
540 
541 /*ARGSUSED*/
542 nss_backend_t *
543 _nss_dns_ipnodes_constr(dummy1, dummy2, dummy3)
544 	const char	*dummy1, *dummy2, *dummy3;
545 {
546 	return (_nss_dns_constr(ipnodes_ops,
547 		sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
548 }
549 
550 /*
551  * optional NSS2 packed backend gethostsbyipnode with ttl
552  * entry point.
553  *
554  * Returns:
555  *	NSS_SUCCESS - successful
556  *	NSS_NOTFOUND - successful but nothing found
557  *	NSS_ERROR - fallback to NSS backend lookup mode
558  * If successful, buffer will be filled with valid data
559  *
560  */
561 
562 /*ARGSUSED*/
563 nss_status_t
564 _nss_get_dns_ipnodes_name(dns_backend_ptr_t *be, void **bufp, size_t *sizep)
565 {
566 	return (_nss_dns_gethost_withttl(*bufp, *sizep, 1));
567 }
568