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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * gethostent.c
30  *
31  * In order to avoid duplicating libresolv code here, and since libresolv.so.2
32  * provides res_-equivalents of the getXbyY and {set,get}Xent, lets call
33  * re_gethostbyaddr() and so on from this file. Among other things, this
34  * should help us avoid problems like the one described in bug 1264386,
35  * where the internal getanswer() acquired new functionality in BIND 4.9.3,
36  * but the local copy of getanswer() in this file wasn't updated, so that new
37  * functionality wasn't available to the name service switch.
38  */
39 
40 #define	gethostbyaddr	res_gethostbyaddr
41 #define	gethostbyname	res_gethostbyname
42 #define	gethostbyname2	res_gethostbyname2
43 #define	sethostent	res_sethostent
44 #define	endhostent	res_endhostent
45 
46 #include "dns_common.h"
47 
48 extern char *inet_ntoa(struct in_addr in);
49 
50 struct hostent *_gethostbyname(int *h_errnop, const char *name);
51 static struct hostent *_gethostbyaddr(int *h_errnop, const char *addr,
52     int len, int type);
53 struct hostent *_nss_dns_gethostbyname2(int *h_errnop, const char *name);
54 
55 #pragma weak	res_gethostbyname
56 #pragma weak	res_gethostbyname2
57 #pragma weak	res_gethostbyaddr
58 #pragma weak	res_sethostent
59 #pragma weak	res_endhostent
60 
61 nss_backend_t *_nss_dns_constr(dns_backend_op_t ops[], int n_ops);
62 nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *);
63 
64 typedef union {
65 	long al;
66 	char ac;
67 } align;
68 
69 /*
70  * Internet Name Domain Server (DNS) only implementation.
71  */
72 static struct hostent *
73 _gethostbyaddr(int *h_errnop, const char *addr, int len, int type)
74 {
75 	struct hostent	*hp;
76 
77 	hp = gethostbyaddr(addr, len, type);
78 	*h_errnop = *get_h_errno();
79 	return (hp);
80 }
81 
82 struct hostent *
83 _nss_dns_gethostbyname2(int *h_errnop, const char *name)
84 {
85 	struct hostent *hp;
86 
87 	hp = gethostbyname2(name, AF_INET6);
88 	*h_errnop = *get_h_errno();
89 	return (hp);
90 }
91 
92 struct hostent *
93 _gethostbyname(int *h_errnop, const char *name)
94 {
95 	struct hostent *hp;
96 
97 	hp = gethostbyname(name);
98 	*h_errnop = *get_h_errno();
99 	return (hp);
100 }
101 
102 static void
103 _sethostent(errp, stayopen)
104 	nss_status_t	*errp;
105 	int		stayopen;
106 {
107 	int	ret;
108 
109 	ret = sethostent(stayopen);
110 	if (ret == 0)
111 		*errp = NSS_SUCCESS;
112 	else
113 		*errp = NSS_UNAVAIL;
114 }
115 
116 static void
117 _endhostent(errp)
118 	nss_status_t	*errp;
119 {
120 	int	ret;
121 
122 	ret = endhostent();
123 	if (ret == 0)
124 		*errp = NSS_SUCCESS;
125 	else
126 		*errp = NSS_UNAVAIL;
127 }
128 
129 
130 /*ARGSUSED*/
131 static nss_status_t
132 getbyname(be, a)
133 	dns_backend_ptr_t	be;
134 	void			*a;
135 {
136 	struct hostent	*he;
137 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
138 	int		ret, mt_disabled;
139 	int		old_retry;
140 	sigset_t	oldmask;
141 
142 	switch_resolver_setup(&mt_disabled, &oldmask, &old_retry);
143 
144 	he = _gethostbyname(&argp->h_errno, argp->key.name);
145 	if (he != NULL) {
146 		if (argp->buf.result == NULL) {
147 			/*
148 			 * if asked to return data in string,
149 			 * convert the hostent structure into
150 			 * string data
151 			 */
152 			ret = ent2str(he, a, AF_INET);
153 			if (ret == NSS_STR_PARSE_SUCCESS)
154 				argp->returnval = argp->buf.buffer;
155 		} else {
156 			ret = ent2result(he, a, AF_INET);
157 			if (ret == NSS_STR_PARSE_SUCCESS)
158 				argp->returnval = argp->buf.result;
159 		}
160 
161 		if (ret != NSS_STR_PARSE_SUCCESS) {
162 			argp->h_errno = HOST_NOT_FOUND;
163 			if (ret == NSS_STR_PARSE_ERANGE) {
164 				argp->erange = 1;
165 			}
166 		}
167 	}
168 
169 	switch_resolver_reset(mt_disabled, oldmask, old_retry);
170 
171 	return (_herrno2nss(argp->h_errno));
172 }
173 
174 
175 
176 /*ARGSUSED*/
177 static nss_status_t
178 getbyaddr(be, a)
179 	dns_backend_ptr_t	be;
180 	void			*a;
181 {
182 	return (__nss_dns_getbyaddr(be, a));
183 }
184 
185 
186 /*
187  * Exposing a DNS backend specific interface so that it doesn't conflict
188  * with other getbyaddr() routines from other switch backends.
189  */
190 /*ARGSUSED*/
191 nss_status_t
192 __nss_dns_getbyaddr(be, a)
193 	dns_backend_ptr_t	be;
194 	void			*a;
195 {
196 	size_t	n;
197 	struct hostent	*he, *he2;
198 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
199 	int		ret, save_h_errno, mt_disabled;
200 	char		**ans, hbuf[MAXHOSTNAMELEN];
201 	char		dst[INET6_ADDRSTRLEN];
202 	struct in_addr	unmapv4;
203 	sigset_t	oldmask;
204 	int		af, addrlen;
205 	void		*addrp;
206 	int		old_retry;
207 
208 	switch_resolver_setup(&mt_disabled, &oldmask, &old_retry);
209 
210 	/* LINTED: E_BAD_PTR_CAST_ALIGN */
211 	if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)argp->key.hostaddr.addr)) {
212 		addrp = &unmapv4;
213 		addrlen = sizeof (unmapv4);
214 		af = AF_INET;
215 		(void) memcpy(addrp, &argp->key.hostaddr.addr[12], addrlen);
216 	} else {
217 		addrp = (void *)argp->key.hostaddr.addr;
218 		addrlen = argp->key.hostaddr.len;
219 		af = argp->key.hostaddr.type;
220 	}
221 	he = _gethostbyaddr(&argp->h_errno, addrp, addrlen, af);
222 
223 	if (he != NULL) {
224 		if (strlen(he->h_name) >= MAXHOSTNAMELEN)
225 			ret = NSS_STR_PARSE_ERANGE;
226 		else {
227 			/* save a copy of the (alleged) hostname */
228 			(void) strcpy(hbuf, he->h_name);
229 			n = strlen(hbuf);
230 			if (n < MAXHOSTNAMELEN-1 && hbuf[n-1] != '.') {
231 				(void) strcat(hbuf, ".");
232 			}
233 
234 			/*
235 			 * if asked to return data in string,
236 			 * convert the hostent structure into
237 			 * string data
238 			 */
239 			if (argp->buf.result == NULL)
240 				ret = ent2str(he, a, argp->key.hostaddr.type);
241 			else
242 				ret = ent2result(he, a,
243 					argp->key.hostaddr.type);
244 
245 			save_h_errno = argp->h_errno;
246 		}
247 		if (ret == NSS_STR_PARSE_SUCCESS) {
248 			/*
249 			 * check to make sure by doing a forward query
250 			 * We use _gethostbyname() to avoid the stack, and
251 			 * then we throw the result from argp->h_errno away,
252 			 * becase we don't care.  And besides you want the
253 			 * return code from _gethostbyaddr() anyway.
254 			 */
255 
256 			if (af == AF_INET)
257 				he2 = _gethostbyname(&argp->h_errno, hbuf);
258 			else
259 				he2 = _nss_dns_gethostbyname2(&argp->h_errno,
260 					hbuf);
261 			if (he2 != (struct hostent *)NULL) {
262 
263 				/* until we prove name and addr match */
264 				argp->h_errno = HOST_NOT_FOUND;
265 				for (ans = he2->h_addr_list; *ans; ans++)
266 					if (memcmp(*ans, addrp,	addrlen) ==
267 						0) {
268 					argp->h_errno = save_h_errno;
269 					if (argp->buf.result == NULL)
270 						argp->returnval =
271 							argp->buf.buffer;
272 					else
273 						argp->returnval =
274 							argp->buf.result;
275 					break;
276 						}
277 			} else {
278 
279 				/*
280 				 * What to do if _gethostbyname() fails ???
281 				 * We assume they are doing something stupid
282 				 * like registering addresses but not names
283 				 * (some people actually think that provides
284 				 * some "security", through obscurity).  So for
285 				 * these poor lost souls, because we can't
286 				 * PROVE spoofing and because we did try (and
287 				 * we don't want a bug filed on this), we let
288 				 * this go.  And return the name from byaddr.
289 				 */
290 				argp->h_errno = save_h_errno;
291 				if (argp->buf.result == NULL)
292 					argp->returnval = argp->buf.buffer;
293 				else
294 					argp->returnval = argp->buf.result;
295 			}
296 			/* we've been spoofed, make sure to log it. */
297 			if (argp->h_errno == HOST_NOT_FOUND) {
298 				if (argp->key.hostaddr.type == AF_INET)
299 		syslog(LOG_NOTICE, "gethostbyaddr: %s != %s",
300 		/* LINTED: E_BAD_PTR_CAST_ALIGN */
301 		hbuf, inet_ntoa(*(struct in_addr *)argp->key.hostaddr.addr));
302 				else
303 		syslog(LOG_NOTICE, "gethostbyaddr: %s != %s",
304 		hbuf, inet_ntop(AF_INET6, (void *) argp->key.hostaddr.addr,
305 		dst, sizeof (dst)));
306 			}
307 		} else {
308 			argp->h_errno = HOST_NOT_FOUND;
309 			if (ret == NSS_STR_PARSE_ERANGE) {
310 				argp->erange = 1;
311 			}
312 		}
313 	}
314 
315 	switch_resolver_reset(mt_disabled, oldmask, old_retry);
316 
317 	return (_herrno2nss(argp->h_errno));
318 }
319 
320 
321 /*ARGSUSED*/
322 static nss_status_t
323 _nss_dns_getent(be, args)
324 	dns_backend_ptr_t	be;
325 	void			*args;
326 {
327 	return (NSS_UNAVAIL);
328 }
329 
330 
331 /*ARGSUSED*/
332 static nss_status_t
333 _nss_dns_setent(be, dummy)
334 	dns_backend_ptr_t	be;
335 	void			*dummy;
336 {
337 	nss_status_t	errp;
338 
339 	sigset_t	oldmask, newmask;
340 	int		mt_disabled = 1;
341 
342 	/*
343 	 * Try to enable MT; if not, we have to single-thread libresolv
344 	 * access
345 	 */
346 	if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) {
347 		(void) sigfillset(&newmask);
348 		_thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
349 		_mutex_lock(&one_lane);
350 	}
351 
352 	_sethostent(&errp, 1);
353 
354 	if (mt_disabled) {
355 		_mutex_unlock(&one_lane);
356 		_thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
357 	} else {
358 		(void) (*disable_mt)();
359 	}
360 
361 	return (errp);
362 }
363 
364 
365 /*ARGSUSED*/
366 static nss_status_t
367 _nss_dns_endent(be, dummy)
368 	dns_backend_ptr_t	be;
369 	void			*dummy;
370 {
371 	nss_status_t	errp;
372 
373 	sigset_t	oldmask, newmask;
374 	int		mt_disabled = 1;
375 
376 	/*
377 	 * Try to enable MT; if not, we have to single-thread libresolv
378 	 * access
379 	 */
380 	if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) {
381 		(void) sigfillset(&newmask);
382 		_thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
383 		_mutex_lock(&one_lane);
384 	}
385 
386 	_endhostent(&errp);
387 
388 	if (mt_disabled) {
389 		_mutex_unlock(&one_lane);
390 		_thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
391 	} else {
392 		(void) (*disable_mt)();
393 	}
394 
395 	return (errp);
396 }
397 
398 
399 /*ARGSUSED*/
400 static nss_status_t
401 _nss_dns_destr(be, dummy)
402 	dns_backend_ptr_t	be;
403 	void			*dummy;
404 {
405 	nss_status_t	errp;
406 
407 	if (be != 0) {
408 		/* === Should change to invoke ops[ENDENT] ? */
409 		sigset_t	oldmask, newmask;
410 		int		mt_disabled = 1;
411 
412 		if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) {
413 			(void) sigfillset(&newmask);
414 			_thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask);
415 			_mutex_lock(&one_lane);
416 		}
417 
418 		_endhostent(&errp);
419 
420 		if (mt_disabled) {
421 			_mutex_unlock(&one_lane);
422 			_thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
423 		} else {
424 			(void) (*disable_mt)();
425 		}
426 
427 		free(be);
428 	}
429 	return (NSS_SUCCESS);   /* In case anyone is dumb enough to check */
430 }
431 
432 
433 static dns_backend_op_t host_ops[] = {
434 	_nss_dns_destr,
435 	_nss_dns_endent,
436 	_nss_dns_setent,
437 	_nss_dns_getent,
438 	getbyname,
439 	getbyaddr,
440 };
441 
442 /*ARGSUSED*/
443 nss_backend_t *
444 _nss_dns_hosts_constr(dummy1, dummy2, dummy3)
445 	const char	*dummy1, *dummy2, *dummy3;
446 {
447 	return (_nss_dns_constr(host_ops,
448 		sizeof (host_ops) / sizeof (host_ops[0])));
449 }
450 
451 /*
452  * optional NSS2 packed backend gethostsbyname with ttl
453  * entry point.
454  *
455  * Returns:
456  *	NSS_SUCCESS - successful
457  *	NSS_NOTFOUND - successful but nothing found
458  *	NSS_ERROR - fallback to NSS backend lookup mode
459  * If successful, buffer will be filled with valid data
460  *
461  */
462 
463 /*ARGSUSED*/
464 nss_status_t
465 _nss_get_dns_hosts_name(dns_backend_ptr_t *be, void **bufp, size_t *sizep)
466 {
467 	return (_nss_dns_gethost_withttl(*bufp, *sizep, 0));
468 }
469