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 #pragma ident "%Z%%M% %I% %E% SMI"
28
29 /*
30 * dns_mt.c
31 *
32 * This file contains all the MT related routines for the DNS backend.
33 */
34
35 #include "dns_common.h"
36 #include <dlfcn.h>
37
38 /*
39 * If the DNS name service switch routines are used in a binary that depends
40 * on an older libresolv (libresolv.so.1, say), then having nss_dns.so.1 or
41 * libnss_dns.a depend on a newer libresolv (libresolv.so.2) will cause
42 * relocation problems. In particular, copy relocation of the _res structure
43 * (which changes in size from libresolv.so.1 to libresolv.so.2) could
44 * cause corruption, and result in a number of strange problems, including
45 * core dumps. Hence, we check if a libresolv is already loaded.
46 */
47
48 #pragma init(_nss_dns_init)
49 static void _nss_dns_init(void);
50
51 extern struct hostent *res_gethostbyname(const char *);
52 #pragma weak res_gethostbyname
53
54 #define RES_SET_NO_HOSTS_FALLBACK "__res_set_no_hosts_fallback"
55 extern void __res_set_no_hosts_fallback(void);
56 #pragma weak __res_set_no_hosts_fallback
57
58 #define RES_UNSET_NO_HOSTS_FALLBACK "__res_unset_no_hosts_fallback"
59 extern void __res_unset_no_hosts_fallback(void);
60 #pragma weak __res_unset_no_hosts_fallback
61
62 #define RES_GET_RES "__res_get_res"
63 extern struct __res_state *__res_get_res(void);
64 #pragma weak __res_get_res
65
66 #define RES_ENABLE_MT "__res_enable_mt"
67 extern int __res_enable_mt(void);
68 #pragma weak __res_enable_mt
69
70 #define RES_DISABLE_MT "__res_disable_mt"
71 extern int __res_disable_mt(void);
72 #pragma weak __res_disable_mt
73
74 #define RES_GET_H_ERRNO "__res_get_h_errno"
75 extern int *__res_get_h_errno();
76 #pragma weak __res_get_h_errno
77
78 #define __H_ERRNO "__h_errno"
79 extern int *__h_errno(void);
80 #pragma weak __h_errno
81
82 #define RES_OVERRIDE_RETRY "__res_override_retry"
83 extern int __res_override_retry(int);
84 #pragma weak __res_override_retry
85
86 static void __fallback_set_no_hosts(void);
87 static int *__fallback_h_errno(void);
88 static int __fallback_override_retry(int);
89 static int __is_mt_safe(void);
90
91 void (*set_no_hosts_fallback)(void) = __fallback_set_no_hosts;
92 void (*unset_no_hosts_fallback)(void) = __fallback_set_no_hosts;
93 struct __res_state *(*set_res_retry)() = 0;
94 int (*enable_mt)() = 0;
95 int (*disable_mt)() = 0;
96 int *(*get_h_errno)(void) = 0;
97 int (*override_retry)(int) = 0;
98
99 /* Usually set from the Makefile */
100 #ifndef NSS_DNS_LIBRESOLV
101 #define NSS_DNS_LIBRESOLV "libresolv.so.2"
102 #endif
103
104 /* From libresolv */
105 extern int h_errno;
106
107 mutex_t one_lane = DEFAULTMUTEX;
108
109 void
_nss_dns_init(void)110 _nss_dns_init(void)
111 {
112 void *reslib, (*f_void_ptr)();
113
114 /* If no libresolv library, then load one */
115 if (res_gethostbyname == 0) {
116 if ((reslib =
117 dlopen(NSS_DNS_LIBRESOLV, RTLD_LAZY|RTLD_GLOBAL)) != 0) {
118 /* Turn off /etc/hosts fall back in libresolv */
119 if ((f_void_ptr = (void (*)(void))dlsym(reslib,
120 RES_SET_NO_HOSTS_FALLBACK)) != 0) {
121 set_no_hosts_fallback = f_void_ptr;
122 }
123 if ((f_void_ptr = (void (*)(void))dlsym(reslib,
124 RES_SET_NO_HOSTS_FALLBACK)) != 0) {
125 unset_no_hosts_fallback = f_void_ptr;
126 }
127 /* Set number of resolver retries */
128 if ((override_retry = (int (*)(int))dlsym(reslib,
129 RES_OVERRIDE_RETRY)) == 0) {
130 set_res_retry =
131 (struct __res_state *(*)(void))dlsym(reslib,
132 RES_GET_RES);
133 override_retry = __fallback_override_retry;
134 }
135 /*
136 * Select h_errno retrieval function. A BIND 8.2.2
137 * libresolv.so.2 will have __h_errno, a BIND 8.1.2
138 * one will have __res_get_h_errno, and other
139 * versions may have nothing at all.
140 *
141 * Also try to bind to the relevant MT enable/disable
142 * functions which are also dependent on the version
143 * of the BIND libresolv.so.2 being used.
144 */
145 if ((get_h_errno = (int *(*)(void))dlsym(reslib,
146 __H_ERRNO)) != 0) {
147 /* BIND 8.2.2 libresolv.so.2 is MT safe. */
148 enable_mt = __is_mt_safe;
149 disable_mt = __is_mt_safe;
150 } else {
151 if ((get_h_errno =
152 (int *(*)(void))dlsym(reslib,
153 RES_GET_H_ERRNO)) == 0) {
154 get_h_errno = __fallback_h_errno;
155 }
156 /*
157 * Pre-BIND 8.2.2 was not MT safe. Try to
158 * bind the MT enable/disable functions.
159 */
160 if ((enable_mt = (int (*)(void))dlsym(reslib,
161 RES_ENABLE_MT)) != 0 &&
162 (disable_mt = (int (*)(void))dlsym(reslib,
163 RES_DISABLE_MT)) == 0) {
164 enable_mt = 0;
165 }
166 }
167 }
168 } else {
169 /* Libresolv already loaded */
170 if ((f_void_ptr = __res_set_no_hosts_fallback) != 0) {
171 set_no_hosts_fallback = f_void_ptr;
172 }
173 if ((f_void_ptr = __res_unset_no_hosts_fallback) != 0) {
174 unset_no_hosts_fallback = f_void_ptr;
175 }
176 if ((override_retry = __res_override_retry) == 0) {
177 set_res_retry = __res_get_res;
178 override_retry = __fallback_override_retry;
179 }
180 if ((get_h_errno = __h_errno) == 0 &&
181 (get_h_errno = __res_get_h_errno) == 0) {
182 get_h_errno = __fallback_h_errno;
183 }
184 if (get_h_errno == __h_errno) {
185 enable_mt = __is_mt_safe;
186 disable_mt = __is_mt_safe;
187 } else {
188 if ((enable_mt = __res_enable_mt) != 0 &&
189 (disable_mt = __res_disable_mt) == 0) {
190 enable_mt = 0;
191 }
192 }
193 }
194 }
195
196
197 /*
198 *
199 * Integration of BIND 8.1.2 introduced two new Sun private functions,
200 * __res_enable_mt() and __res_disable_mt(), that enabled and disabled
201 * MT mode per-thread. These functions are in the private libresolv.so.2
202 * interface, and intended for use by nss_dns.so.1.
203 *
204 * BIND 8.2.2 removed the need for those two functions. As similar
205 * functionality was provided in BIND further up the stack. However the
206 * functions remain to satisfy any application that directly called upon
207 * them. Only, __res_enable_mt() was modified to return failure.
208 * Indicated by a non-zero return value. So that those unconventional
209 * applications would not then presume that res_send() and friends are
210 * MT-safe, when in fact they are not.
211 *
212 * To prevent nss_dns from locking inappropriately __is_mt_safe() is
213 * called in place of __res_enable_mt() and __res_disable_mt() if BIND
214 * 8.2.2 libresolv.so.2 being used. __is_mt_safe() returns success
215 * indicated by a return code of zero. Signifying that no locking is
216 * necessary.
217 *
218 * MT applications making calls to gethostby*_r() or getipnodeby*()
219 * linked to libresolv.so.1 or linked statically with pre-BIND 8.2.2
220 * libresolv.a, doubtful as we don't ship a static version, would require
221 * locking within the nsswitch back-end. Hence the mechanism can not
222 * simply be removed.
223 *
224 */
225 static int
__is_mt_safe(void)226 __is_mt_safe(void) {
227 return (0);
228 }
229
230
231 /*
232 * Return pointer to the global h_errno variable
233 */
234 static int *
__fallback_h_errno(void)235 __fallback_h_errno(void) {
236 return (&h_errno);
237 }
238
239
240 /*
241 * This function is called when the resolver library doesn't provide its
242 * own function to establish an override retry. If we can get a pointer
243 * to the per-thread _res (i.e., set_res_retry != 0), we set the retries
244 * directly, and return the previous number of retries. Otherwise, there's
245 * nothing to do.
246 */
247 static int
__fallback_override_retry(int retry)248 __fallback_override_retry(int retry) {
249 struct __res_state *res;
250 int old_retry = 0;
251
252 if (set_res_retry != 0) {
253 res = set_res_retry();
254 old_retry = res->retry;
255 res->retry = retry;
256 }
257 return (old_retry);
258 }
259
260
261 static void
__fallback_set_no_hosts(void)262 __fallback_set_no_hosts(void) {
263 }
264
265
266 /*
267 * Common code to enable/disable MT mode, set/unset no-/etc/hosts fallback,
268 * and to set the number of retries.
269 */
270 void
switch_resolver_setup(int * mt_disabled,sigset_t * oldmask,int * old_retry)271 switch_resolver_setup(int *mt_disabled, sigset_t *oldmask, int *old_retry) {
272
273 /*
274 * Try to enable MT mode. If that isn't possible, mask signals,
275 * and mutex_lock.
276 */
277 *mt_disabled = 1;
278 if (enable_mt == 0 || (*mt_disabled = (*enable_mt)()) != 0) {
279 sigset_t newmask;
280 (void) sigfillset(&newmask);
281 (void) thr_sigsetmask(SIG_SETMASK, &newmask, oldmask);
282 (void) mutex_lock(&one_lane);
283 }
284
285 /*
286 * Disable any fallback to /etc/hosts (or /etc/inet/ipnodes, when
287 * libresolv knows about that file).
288 */
289 (*set_no_hosts_fallback)();
290
291 /*
292 * The NS switch wants to handle retries on its own.
293 */
294 *old_retry = (*override_retry)(1);
295 }
296
297
298 void
switch_resolver_reset(int mt_disabled,sigset_t oldmask,int old_retry)299 switch_resolver_reset(int mt_disabled, sigset_t oldmask, int old_retry) {
300
301 if (mt_disabled) {
302 (void) mutex_unlock(&one_lane);
303 (void) thr_sigsetmask(SIG_SETMASK, &oldmask, NULL);
304 } else {
305 (void) (*disable_mt)();
306 }
307
308 (*unset_no_hosts_fallback)();
309
310 (void) (*override_retry)(old_retry);
311 }
312