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