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