xref: /titanic_44/usr/src/lib/nsswitch/dns/common/gethostent6.c (revision ea8dc4b6d2251b437950c0056bc626b311c73c27)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1993-2000, 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
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 	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 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 			IN6_INADDR_TO_V4MAPPED(
180 			(struct in_addr *)h->h_addr_list[i], addrArray[i]);
181 		} else {
182 			memcpy(addrArray[i], h->h_addr_list[i], 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, k, 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 		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 
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 	char		*v6Name = 0;
291 	struct in6_addr	**v6Addrs = 0, **mergeAddrs = 0;
292 	char		**v6Aliases = 0, **mergeAliases = 0;
293 	int		v6_h_errno;
294 	int		old_retry;
295 	int		af = argp->key.ipnode.af_family;
296 	int		flags = argp->key.ipnode.flags;
297 
298 	switch_resolver_setup(&mt_disabled, &oldmask, &old_retry);
299 
300 	/* Now get the AAAA records */
301 	if (af == AF_INET6)
302 		he = _nss_dns_gethostbyname2(&argp->h_errno,
303 					argp->key.ipnode.name);
304 	if (he != NULL) {
305 		/*
306 		 * pointer in "he" is part of a static pthread key in libresolv
307 		 * It should be treated as read only.
308 		 * So clone a copy first.
309 		 */
310 		v6Name = cloneName(he, &converr);
311 		if (converr) {
312 			argp->h_errno = HOST_NOT_FOUND;
313 			argp->erange = 1;
314 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
315 			return (_herrno2nss(argp->h_errno));
316 		}
317 		v6Addrs = cloneAddrList(he, 0, &converr);
318 		if (converr) {
319 			if (v6Name != 0)
320 				free(v6Name);
321 			argp->h_errno = HOST_NOT_FOUND;
322 			argp->erange = 1;
323 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
324 			return (_herrno2nss(argp->h_errno));
325 		}
326 		v6Aliases = cloneAliasList(he, 0, &converr);
327 		if (converr) {
328 			if (v6Name != 0)
329 				free(v6Name);
330 			if (v6Addrs != 0)
331 				free(v6Addrs);
332 			argp->h_errno = HOST_NOT_FOUND;
333 			argp->erange = 1;
334 			switch_resolver_reset(mt_disabled, oldmask, old_retry);
335 			return (_herrno2nss(argp->h_errno));
336 		}
337 		v6_h_errno = argp->h_errno;
338 		gotv6 = 1;
339 	}
340 
341 	/*
342 	 * The conditions to search "A" records:
343 	 * 1. af is AF_INET
344 	 * 2. if af is AF_INET6
345 	 *    then flags are either
346 	 *	1) (AI_ALL | AI_V4MAPPED) or
347 	 *	2) AI_V4MAPPED and he == NULL
348 	 *	    (No V6 addresses found or no search for V6 at all)
349 	 */
350 
351 	/* Get the A records, and store the information */
352 	if ((af == AF_INET) ||
353 	    ((af == AF_INET6) &&
354 		((flags & (AI_ALL | AI_V4MAPPED)) ||
355 		((flags & AI_V4MAPPED) && he == NULL))))
356 		he = _gethostbyname(&argp->h_errno, argp->key.ipnode.name);
357 	else
358 		he = NULL;
359 
360 	/* Merge the results */
361 	if (he != NULL) {
362 		mergeAddrs = cloneAddrList(he, v6Addrs, &converr);
363 		if (converr) {
364 			if (v6Name != 0)
365 				free(v6Name);
366 			if (v6Addrs != 0)
367 				free(v6Addrs);
368 			if (v6Aliases != 0)
369 				free(v6Aliases);
370 			argp->h_errno = HOST_NOT_FOUND;
371 			argp->erange = 1;
372 			switch_resolver_reset(mt_disabled, oldmask,
373 						old_retry);
374 			return (_herrno2nss(argp->h_errno));
375 		}
376 		he->h_addr_list = (char **)mergeAddrs;
377 
378 		mergeAliases = cloneAliasList(he, v6Aliases, &converr);
379 		if (converr) {
380 			if (v6Name != 0)
381 				free(v6Name);
382 			if (v6Addrs != 0)
383 				free(v6Addrs);
384 			if (v6Aliases != 0)
385 				free(v6Aliases);
386 			if (mergeAddrs != 0)
387 				free(mergeAddrs);
388 			argp->h_errno = HOST_NOT_FOUND;
389 			argp->erange = 1;
390 			switch_resolver_reset(mt_disabled, oldmask,
391 						old_retry);
392 			return (_herrno2nss(argp->h_errno));
393 		}
394 		he->h_aliases = mergeAliases;
395 
396 		/* reset h_length, h_addrtype */
397 		he->h_length = sizeof (struct in6_addr);
398 		he->h_addrtype = AF_INET6;
399 
400 	} else if (gotv6) {
401 		v6he.h_name = v6Name;
402 		v6he.h_length = sizeof (struct in6_addr);
403 		v6he.h_addrtype = AF_INET6;
404 		v6he.h_addr_list = (char **)v6Addrs;
405 		v6he.h_aliases = v6Aliases;
406 		he = &v6he;
407 		argp->h_errno = v6_h_errno;
408 	}
409 
410 	if (he != 0) {
411 		ret = ent2result(he, a, AF_INET6);
412 		if (ret == NSS_STR_PARSE_SUCCESS) {
413 			argp->returnval = argp->buf.result;
414 		} else {
415 			argp->h_errno = HOST_NOT_FOUND;
416 			if (ret == NSS_STR_PARSE_ERANGE) {
417 				argp->erange = 1;
418 			}
419 		}
420 	}
421 
422 	if (v6Name != 0)
423 		free(v6Name);
424 	if (v6Addrs != 0)
425 		free(v6Addrs);
426 	if (v6Aliases != 0)
427 		free(v6Aliases);
428 	if (mergeAddrs != 0)
429 		free(mergeAddrs);
430 	if (mergeAliases != 0)
431 		free(mergeAliases);
432 
433 	switch_resolver_reset(mt_disabled, oldmask, old_retry);
434 
435 	return (_herrno2nss(argp->h_errno));
436 }
437 
438 
439 extern nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *);
440 
441 static nss_status_t
442 getbyaddr(be, a)
443 	dns_backend_ptr_t	be;
444 	void			*a;
445 {
446 	/* uses the same getbyaddr from IPv4 */
447 	return (__nss_dns_getbyaddr(be, a));
448 }
449 
450 
451 /*ARGSUSED*/
452 static nss_status_t
453 _nss_dns_getent(be, args)
454 	dns_backend_ptr_t	be;
455 	void			*args;
456 {
457 	return (NSS_UNAVAIL);
458 }
459 
460 
461 /*ARGSUSED*/
462 static nss_status_t
463 _nss_dns_setent(be, dummy)
464 	dns_backend_ptr_t	be;
465 	void			*dummy;
466 {
467 	/* XXXX not implemented at this point */
468 	return (NSS_UNAVAIL);
469 }
470 
471 
472 /*ARGSUSED*/
473 static nss_status_t
474 _nss_dns_endent(be, dummy)
475 	dns_backend_ptr_t	be;
476 	void			*dummy;
477 {
478 	/* XXXX not implemented at this point */
479 	return (NSS_UNAVAIL);
480 }
481 
482 
483 /*ARGSUSED*/
484 static nss_status_t
485 _nss_dns_destr(be, dummy)
486 	dns_backend_ptr_t	be;
487 	void			*dummy;
488 {
489 	nss_status_t	errp;
490 
491 	if (be != 0) {
492 		/* === Should change to invoke ops[ENDENT] ? */
493 		sigset_t	oldmask, newmask;
494 		int		mt_disabled = 1;
495 
496 		if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) {
497 			(void) sigfillset(&newmask);
498 			_thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
499 			_mutex_lock(&one_lane);
500 		}
501 
502 		_endhostent(&errp);
503 
504 		if (mt_disabled) {
505 			_mutex_unlock(&one_lane);
506 			_thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
507 		} else {
508 			(void) (*disable_mt)();
509 		}
510 
511 		free(be);
512 	}
513 	return (NSS_SUCCESS);   /* In case anyone is dumb enough to check */
514 }
515 
516 
517 
518 static dns_backend_op_t ipnodes_ops[] = {
519 	_nss_dns_destr,
520 	_nss_dns_endent,
521 	_nss_dns_setent,
522 	_nss_dns_getent,
523 	getbyname,
524 	getbyaddr,
525 };
526 
527 /*ARGSUSED*/
528 nss_backend_t *
529 _nss_dns_ipnodes_constr(dummy1, dummy2, dummy3)
530 	const char	*dummy1, *dummy2, *dummy3;
531 {
532 	return (_nss_dns_constr(ipnodes_ops,
533 		sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
534 }
535