xref: /illumos-gate/usr/src/lib/libmapid/common/mapid.c (revision 7c478bd9)
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 2005 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  * PSARC/2004/154 nfsmapid DNS enhancements implementation.
31  *
32  * As per RFC 3050, file owner and group attributes in version 4 of the
33  * NFS protocol are no longer exchanged between client and server as 32
34  * bit integral values. Instead, owner and group file attributes are
35  * exchanged between client and server as UTF8 strings of form
36  *
37  *      'user@domain'		(ie. "joeblow@central.sun.com")
38  *      'group@domain'		(ie. "staff@central.sun.com")
39  *
40  * This NFSv4 feature is far beyond anything NFSv2/v3 ever provided, as
41  * being able to describe a user with a unique string identifier provides
42  * a much more powerful and administrative friendly way of dealing with
43  * overlaps in the uid/gid number spaces. That notwithstanding, dealing
44  * with issues of correctly mapping user and group ownership in a cross-
45  * domain environment has proven a difficult problem to solve, since
46  * dealing with different permutations of client naming configurations
47  * (ie. NIS only, LDAP only, etc.) have bloated the problem. Thus, users
48  * utilizing clients and servers that have the 'domain' portion of the
49  * UTF8 attribute string configured differently than its peer server and
50  * client accordingly, will experience watching their files owned by the
51  * 'nobody' user and group. This is due to the fact that the 'domain's
52  * don't match and the nfsmapid daemon treats the attribute strings as
53  * unknown user(s) or group(s) (even though the actual uid/gid's may exist
54  * in the executing daemon's system). Please refer to PSARC/2004/154 for
55  * further background and motivation for these enhancements.
56  *
57  * The latest implementation of the nfsmapid daemon relies on a DNS TXT
58  * record. The behavior of nfsmapid is to first use the NFSMAPID_DOMAIN
59  * configuration option in /etc/default/nfs. If the option has not been
60  * set, then the nfsmapid daemon queries the configured DNS domain server
61  * for the _nfsv4idmapdomain TXT record. If the record exists, then the
62  * record's value is used as the 'domain' portion of the UTF8 attribute
63  * strings. If the TXT record has not been configured in the DNS server,
64  * then the daemon falls back to using the DNS domain name itself as the
65  * 'domain' portion of the attribute strings. Lastly, if the configured
66  * DNS server is unresponsive, the nfsmapid daemon falls back to using
67  * the DNS domain name as the 'domain' portion of the attribute strings,
68  * and fires up a query thread to keep contacting the DNS server until
69  * it responds with either a TXT record, or a lack thereof, in which
70  * case, nfsmapid just continues to utilize the DNS domain name.
71  */
72 #define	__NFSMAPID_RES_IMPL
73 #include "nfsmapid_resolv.h"
74 
75 /*
76  * DEBUG Only
77  * Decode any resolver errors and print out message to log
78  */
79 static int
80 resolv_error(void)
81 {
82 	static uint64_t	 msg_done[NS_ERRS] = {0};
83 
84 	switch (h_errno) {
85 		case NETDB_INTERNAL:
86 			IDMAP_DBG(EMSG_NETDB_INTERNAL, strerror(errno), NULL);
87 			break;
88 
89 		case HOST_NOT_FOUND:
90 			(void) rw_rdlock(&s_dns_impl_lock);
91 			msg_done[h_errno]++;
92 #ifdef DEBUG
93 			if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
94 				IDMAP_DBG(EMSG_HOST_NOT_FOUND, s_dname, NULL);
95 #endif
96 			(void) rw_unlock(&s_dns_impl_lock);
97 			break;
98 
99 		case TRY_AGAIN:
100 			/*
101 			 * Nameserver is not responding.
102 			 * Try again after a given timeout.
103 			 */
104 			(void) rw_rdlock(&s_dns_impl_lock);
105 			msg_done[h_errno]++;
106 #ifdef DEBUG
107 			if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
108 				IDMAP_DBG(EMSG_TRY_AGAIN, s_dname, NULL);
109 #endif
110 			(void) rw_unlock(&s_dns_impl_lock);
111 			break;
112 
113 		case NO_RECOVERY:
114 			/*
115 			 * This msg only really happens once, due
116 			 * to s_dns_disabled flag (see below)
117 			 */
118 			IDMAP_DBG(EMSG_NO_RECOVERY, hstrerror(h_errno), NULL);
119 			break;
120 
121 		case NO_DATA:
122 			/*
123 			 * No entries in the nameserver for
124 			 * the specific record or record type.
125 			 */
126 			(void) rw_rdlock(&s_dns_impl_lock);
127 			msg_done[h_errno]++;
128 #ifdef DEBUG
129 			if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
130 				IDMAP_DBG(EMSG_NO_DATA, NFSMAPID_DNS_RR,
131 				    s_dname);
132 #endif
133 			(void) rw_unlock(&s_dns_impl_lock);
134 			break;
135 
136 		case NETDB_SUCCESS:
137 		default:
138 			break;
139 	}
140 	return (h_errno);
141 }
142 
143 /*
144  * Reset the global state variables used for the TXT record.
145  * Having these values reset to zero helps nfsmapid confirm
146  * that a valid DNS TXT record was not found; in which case,
147  * it would fall back to using the configured DNS domain name.
148  *
149  * If a valid DNS TXT record _was_ found, but subsequent contact
150  * to the DNS server is somehow hindered, the previous DNS TXT
151  * RR value continues to be used. Thus, in such instances, we
152  * forego clearing the global config variables so nfsmapid can
153  * continue to use a valid DNS TXT RR while contact to the DNS
154  * server is reestablished.
155  */
156 static void
157 resolv_txt_reset(void)
158 {
159 	(void) rw_wrlock(&s_dns_impl_lock);
160 	bzero(s_txt_rr, sizeof (s_txt_rr));
161 	(void) rw_unlock(&s_dns_impl_lock);
162 
163 	(void) rw_wrlock(&dns_data_lock);
164 	if (!dns_txt_cached) {
165 		dns_txt_domain_len = 0;
166 		bzero(dns_txt_domain, DNAMEMAX);
167 	}
168 	(void) rw_unlock(&dns_data_lock);
169 }
170 
171 /*
172  * Initialize resolver and populate &s_res struct
173  *
174  * DNS Domain is saved off sysdns_domain in case we
175  * need to fall back to using the DNS domain name as
176  * the v4 attribute string domain.
177  */
178 int
179 resolv_init(void)
180 {
181 	size_t			len;
182 	int			n;
183 	struct __res_state	res;
184 
185 	(void) mutex_lock(&s_res_lock);
186 	bzero(&s_res, sizeof (struct __res_state));
187 	n = h_errno = errno = 0;
188 	if ((n = res_ninit(&s_res)) < 0) {
189 		(void) mutex_unlock(&s_res_lock);
190 		(void) resolv_error();
191 		return (n);
192 	}
193 	res = s_res;
194 	(void) mutex_unlock(&s_res_lock);
195 
196 	len = strlen(res.defdname) + 1;
197 	(void) rw_wrlock(&s_dns_impl_lock);
198 	bzero(s_dname, sizeof (s_dname));
199 	(void) snprintf(s_dname, len, "%s", res.defdname);
200 	(void) rw_unlock(&s_dns_impl_lock);
201 
202 	(void) rw_wrlock(&dns_data_lock);
203 	(void) snprintf(sysdns_domain, len, "%s", res.defdname);
204 	(void) rw_unlock(&dns_data_lock);
205 
206 	return (0);
207 }
208 
209 /*
210  * Search criteria assumptions:
211  *
212  * The onus will fall on the sysadmins to correctly configure the TXT
213  * record in the DNS domain where the box currently resides in order
214  * for the record to be found. However, if they sysadmin chooses to
215  * add the 'search' key to /etc/resolv.conf, then resolv_search()
216  * _will_ traverse up the DNS tree as specified in the 'search' key.
217  * Otherwise, we'll default the domain to the DNS domain itself.
218  */
219 static int
220 resolv_search(void)
221 {
222 	int			len;
223 	ans_t			ans = {0};
224 	struct __res_state	res;
225 	int			type = T_TXT;
226 	int			class = C_IN;
227 
228 	(void) mutex_lock(&s_res_lock);
229 	res = s_res;
230 	(void) mutex_unlock(&s_res_lock);
231 
232 	/*
233 	 * Avoid holding locks across the res_nsearch() call to
234 	 * prevent stalling threads during network partitions.
235 	 */
236 	len = h_errno = errno = 0;
237 	if ((len = res_nsearch(&res, NFSMAPID_DNS_RR, class, type,
238 	    ans.buf, sizeof (ans))) < 0)
239 		return (resolv_error());
240 
241 	(void) rw_wrlock(&s_dns_impl_lock);
242 	s_ans = ans;
243 	s_anslen = len;
244 	(void) rw_unlock(&s_dns_impl_lock);
245 
246 	return (NETDB_SUCCESS);
247 }
248 
249 /*
250  * Skip one DNS record
251  */
252 static uchar_t  *
253 resolv_skip_rr(uchar_t *p, uchar_t *eom)
254 {
255 	int	t;
256 	int	dlen;
257 
258 	/*
259 	 * Skip compressed name
260 	 */
261 	errno = 0;
262 	if ((t = dn_skipname(p, eom)) < 0) {
263 		IDMAP_DBG("%s", strerror(errno), NULL);
264 		return (NULL);
265 	}
266 
267 	/*
268 	 * Advance pointer and make sure
269 	 * we're still within the message
270 	 */
271 	p += t;
272 	if ((p + RRFIXEDSZ) > eom)
273 		return (NULL);
274 
275 	/*
276 	 * Now, just skip over the rr fields
277 	 */
278 	p += INT16SZ;	/* type */
279 	p += INT16SZ;	/* class */
280 	p += INT32SZ;	/* ttl */
281 	NS_GET16(dlen, p);
282 	p += dlen;	/* dlen */
283 	if (p > eom)
284 		return (NULL);
285 
286 	return (p);
287 }
288 
289 /*
290  * Process one TXT record.
291  *
292  * nfsmapid queries the DNS server for the specific _nfsv4idmapdomain
293  * TXT record. Thus, if the TXT record exists, the answer section of
294  * the DNS response carries the TXT record's value. Thus, we check that
295  * the value is indeed a valid domain and set the modular s_txt_rr
296  * global to the domain value.
297  */
298 static void
299 resolve_process_txt(uchar_t *p, int dlen)
300 {
301 	char		*rr_base = (char *)(p + 1);
302 	char		*rr_end = (char *)(p + dlen);
303 	size_t		 len = rr_end - rr_base;
304 	static uint64_t	 msg_done = 0;
305 	char		 tmp_txt_rr[DNAMEMAX];
306 
307 	if (len >= DNAMEMAX)
308 		return;		/* process next TXT RR */
309 
310 	/*
311 	 * make sure we have a clean buf since
312 	 * we may've processed several TXT rr's
313 	 */
314 	(void) rw_wrlock(&s_dns_impl_lock);
315 	bzero(s_txt_rr, sizeof (s_txt_rr));
316 	(void) rw_unlock(&s_dns_impl_lock);
317 
318 	(void) strncpy(tmp_txt_rr, rr_base, len);
319 	tmp_txt_rr[len] = '\0';
320 
321 	/*
322 	 * If there is a record and it's a valid domain, we're done.
323 	 */
324 	if (rr_base[0] != '\0' && standard_domain_str(tmp_txt_rr)) {
325 		(void) rw_wrlock(&s_dns_impl_lock);
326 		(void) strncpy(s_txt_rr, rr_base, len);
327 		(void) rw_unlock(&s_dns_impl_lock);
328 		IDMAP_DBG("TXT (Rec):\t%s", s_txt_rr, NULL);
329 
330 	} else if (!(msg_done++ % NFSMAPID_SLOG_RATE)) {
331 		/*
332 		 * Otherwise, log the error
333 		 */
334 		(void) rw_rdlock(&s_dns_impl_lock);
335 		IDMAP_DBG(EMSG_DNS_RR_INVAL, NFSMAPID_DNS_RR, s_dname);
336 		(void) rw_unlock(&s_dns_impl_lock);
337 	}
338 }
339 
340 /*
341  * Decode any answer received from the DNS server. This interface is
342  * capable of much more than just decoding TXT records. We maintain
343  * focus on TXT rr's for now, but this will probably change once we
344  * get the IETF approved application specific DNS RR.
345  *
346  * Here's an example of the TXT record we're decoding (as would appear
347  * in the DNS zone file):
348  *
349  *            _nfsv4idmapdomain    IN    TXT    "sun.com"
350  *
351  * Once the IETF application specific DNS RR is granted, we should only
352  * be changing the record flavor, but all should pretty much stay the
353  * same.
354  */
355 static void
356 resolv_decode(void)
357 {
358 	uchar_t		*buf;
359 	HEADER		*hp;
360 	uchar_t		 name[DNAMEMAX];
361 	uchar_t		*eom;
362 	uchar_t		*p;
363 	int		 n;
364 	uint_t		 qd_cnt;
365 	uint_t		 an_cnt;
366 	uint_t		 ns_cnt;
367 	uint_t		 ar_cnt;
368 	uint_t		 cnt;
369 	uint_t		 type;
370 	uint_t		 class;
371 	int		 dlen;
372 	ulong_t		 ttl;
373 	ans_t		 answer = {0};
374 	int		 answer_len = 0;
375 
376 	/*
377 	 * Check the HEADER for any signs of errors
378 	 * and extract the answer counts for later.
379 	 */
380 	(void) rw_rdlock(&s_dns_impl_lock);
381 	answer = s_ans;
382 	answer_len = s_anslen;
383 	(void) rw_unlock(&s_dns_impl_lock);
384 
385 	buf = (uchar_t *)&answer.buf;
386 	hp = (HEADER *)&answer.hdr;
387 	eom = (uchar_t *)(buf + answer_len);
388 	if (hp->rcode !=  NOERROR) {
389 		IDMAP_DBG("errno: %s", strerror(errno), NULL);
390 		IDMAP_DBG("h_errno: %s", hstrerror(h_errno), NULL);
391 		return;
392 	}
393 	qd_cnt = ntohs(hp->qdcount);
394 	an_cnt = ntohs(hp->ancount);
395 	ns_cnt = ntohs(hp->nscount);
396 	ar_cnt = ntohs(hp->arcount);
397 
398 	/*
399 	 * skip query entries
400 	 */
401 	p = (uchar_t *)(buf + HFIXEDSZ);
402 	errno = 0;
403 	while (qd_cnt-- > 0) {
404 		n = dn_skipname(p, eom);
405 		if (n < 0) {
406 			IDMAP_DBG("%s", strerror(errno), NULL);
407 			return;
408 		}
409 		p += n;
410 		p += INT16SZ;	/* type */
411 		p += INT16SZ;	/* class */
412 	}
413 
414 #ifdef	DEBUG
415 	/*
416 	 * If debugging... print query only once.
417 	 * NOTE: Don't advance pointer... this is done
418 	 *	 in while() loop on a per record basis !
419 	 */
420 	n = h_errno = errno = 0;
421 	n = dn_expand(buf, eom, p, (char *)name, sizeof (name));
422 	if (n < 0) {
423 		(void) resolv_error();
424 		return;
425 	}
426 	IDMAP_DBG("Query:\t\t%-30s", name, NULL);
427 #endif
428 
429 	/*
430 	 * Process actual answer(s).
431 	 */
432 	cnt = an_cnt;
433 	while (cnt-- > 0 && p < eom) {
434 		/* skip the name field */
435 		n = dn_expand(buf, eom, p, (char *)name, sizeof (name));
436 		if (n < 0) {
437 			(void) resolv_error();
438 			return;
439 		}
440 		p += n;
441 
442 		if ((p + 3 * INT16SZ + INT32SZ) > eom)
443 			return;
444 
445 		NS_GET16(type, p);
446 		NS_GET16(class, p);
447 		NS_GET32(ttl, p);
448 		NS_GET16(dlen, p);
449 
450 		if ((p + dlen) > eom)
451 			return;
452 
453 		switch (type) {
454 			case T_TXT:
455 				resolve_process_txt(p, dlen);
456 				break;
457 
458 			default:
459 				/*
460 				 * Advance to next answer record for any
461 				 * other record types. Again, this will
462 				 * probably change (see block comment).
463 				 */
464 				p += dlen;
465 				break;
466 		}
467 	}
468 
469 	/*
470 	 * Skip name server and additional records for now.
471 	 */
472 	cnt = ns_cnt + ar_cnt;
473 	if (cnt > 0) {
474 		while (--cnt != 0 && p < eom) {
475 			p = resolv_skip_rr(p, eom);
476 			if (p == NULL)
477 				return;
478 		}
479 	}
480 }
481 
482 /*
483  * If a valid TXT record entry exists, s_txt_rr contains the domain
484  * value (as set in resolv_process_txt) and we extract the value into
485  * dns_txt_domain (the exported global). If there was _no_ valid TXT
486  * entry, we simply return and check_domain() will default to the
487  * DNS domain since we did resolv_txt_reset() first.
488  */
489 static void
490 resolv_get_txt_data()
491 {
492 	(void) rw_rdlock(&s_dns_impl_lock);
493 	if (s_txt_rr[0] != '\0') {
494 		(void) rw_wrlock(&dns_data_lock);
495 		(void) snprintf(dns_txt_domain, strlen(s_txt_rr) + 1, "%s",
496 								s_txt_rr);
497 		dns_txt_domain_len = strlen(dns_txt_domain);
498 		dns_txt_cached = 1;
499 		(void) rw_unlock(&dns_data_lock);
500 	}
501 	(void) rw_unlock(&s_dns_impl_lock);
502 }
503 
504 /*
505  * Thread to keep pinging  DNS  server for  TXT  record if nfsmapid's
506  * initial attempt at contact with server failed. We could potentially
507  * have a substantial number of NFSv4 clients and having all of them
508  * hammering on an already unresponsive DNS server would not help
509  * things. So, we limit the number of live query threads to at most
510  * 1 at any one time to keep things from getting out of hand.
511  */
512 /* ARGSUSED */
513 void *
514 resolv_query_thread(void *arg)
515 {
516 #ifdef DEBUG
517 	char		*whoami = "query_thread";
518 #endif
519 	uint32_t	 nap_time;
520 
521 	IDMAP_DBG("query_thread active !", NULL, NULL);
522 	(void) rw_rdlock(&s_dns_impl_lock);
523 	nap_time = s_dns_tout;
524 	(void) rw_unlock(&s_dns_impl_lock);
525 
526 	for (;;) {
527 		(void) sleep(nap_time);
528 
529 		resolv_txt_reset();
530 		(void) resolv_init();
531 		switch (resolv_search()) {
532 			case NETDB_SUCCESS:
533 				IDMAP_DBG("%s: DNS replied", whoami, NULL);
534 				resolv_decode();
535 				resolv_get_txt_data();
536 
537 				/*
538 				 * This is a bit different than what we
539 				 * do in get_dns_txt_domain(). Here, the
540 				 * thread _must_ update the global state
541 				 * if a new TXT record was found.
542 				 */
543 				(void) rw_rdlock(&dns_data_lock);
544 				if (dns_txt_domain_len != 0) {
545 					/*
546 					 * Update global state and only
547 					 * flush the cache if there were
548 					 * any updates to cur_domain
549 					 */
550 					(void) rw_wrlock(&domain_cfg_lock);
551 					(void) strncpy(cur_domain,
552 							dns_txt_domain,
553 							DNAMEMAX-1);
554 					cur_domain_len = dns_txt_domain_len;
555 					update_diag_file(cur_domain);
556 					DTRACE_PROBE1(nfsmapid, thread__domain,
557 					    cur_domain);
558 					(void) rw_unlock(&domain_cfg_lock);
559 					idmap_kcall(-1);
560 				}
561 				(void) rw_unlock(&dns_data_lock);
562 				goto thr_okay;
563 
564 			case NO_DATA:
565 				/*
566 				 * DNS is up now, but does not have
567 				 * the NFSV4IDMAPDOMAIN TXT record.
568 				 */
569 				IDMAP_DBG("%s: DNS has no TXT Record", whoami,
570 				    NULL);
571 				goto thr_reset;
572 
573 			case NO_RECOVERY:
574 				/*
575 				 * Non-Recoverable error occurred. No sense
576 				 * in keep pinging the DNS server at this
577 				 * point, so we disable any further contact.
578 				 */
579 				IDMAP_DBG(EMSG_DNS_DISABLE, whoami, NULL);
580 				(void) rw_wrlock(&s_dns_impl_lock);
581 				s_dns_disabled = TRUE;
582 				(void) rw_unlock(&s_dns_impl_lock);
583 				goto thr_reset;
584 
585 			case HOST_NOT_FOUND:
586 				/*
587 				 * Authoritative NS not responding...
588 				 * keep trying for non-authoritative reply
589 				 */
590 				/*FALLTHROUGH*/
591 
592 			case TRY_AGAIN:
593 				/* keep trying */
594 				IDMAP_DBG("%s: retrying...", whoami, NULL);
595 				break;
596 
597 			case NETDB_INTERNAL:
598 			default:
599 				IDMAP_DBG("%s: Internal resolver error: %s",
600 				    whoami, strerror(errno));
601 				goto thr_reset;
602 		}
603 	}
604 thr_reset:
605 	(void) rw_wrlock(&dns_data_lock);
606 	dns_txt_cached = 0;
607 	(void) rw_unlock(&dns_data_lock);
608 	resolv_txt_reset();
609 
610 thr_okay:
611 	/* mark thread as done */
612 	(void) rw_wrlock(&s_dns_impl_lock);
613 	s_dns_qthr_created = FALSE;
614 	(void) rw_unlock(&s_dns_impl_lock);
615 
616 	(void) thr_exit(NULL);
617 	/*NOTREACHED*/
618 	return (NULL);
619 }
620 
621 /*
622  * nfsmapid's interface into the resolver for getting the TXT record.
623  *
624  * Key concepts:
625  *
626  * o If the DNS server is available and the TXT record is found, we
627  *   simply decode the output and fill the exported dns_txt_domain
628  *   global, so our caller can configure the daemon appropriately.
629  *
630  * o If the TXT record is not found, then having done resolv_txt_reset()
631  *   first will allow our caller to recognize that the exported globals
632  *   are empty and thus configure nfsmapid to use the default DNS domain.
633  *
634  * o Having no /etc/resolv.conf file is pretty much a show stopper, since
635  *   there is no name server address information. We return since we've
636  *   already have reset the TXT global state.
637  *
638  * o If a previous call to the DNS server resulted in an unrecoverable
639  *   error, then we disable further contact to the DNS server and return.
640  *   Having the TXT global state already reset guarantees that our caller
641  *   will fall back to the right configuration.
642  *
643  * o Query thread creation is throttled by s_dns_qthr_created. We mitigate
644  *   the problem of an already unresponsive DNS server by allowing at most
645  *   1 outstanding query thread since we could potentially have a substantial
646  *   amount of clients hammering on the same DNS server attempting to get
647  *   the TXT record.
648  */
649 void
650 get_dns_txt_domain(int sighup)
651 {
652 	int		err;
653 #ifdef DEBUG
654 	static uint64_t	msg_done = 0;
655 	char		*whoami = "get_dns_txt_domain";
656 #endif
657 	long		thr_flags = THR_DETACHED;
658 	struct stat	st;
659 
660 	/*
661 	 * We reset TXT variables first in case /etc/resolv.conf
662 	 * is missing or we've had unrecoverable resolver errors,
663 	 * we'll default to get_dns_domain(). If a previous DNS
664 	 * TXT RR was found, don't clear it until we're certain
665 	 * that contact can be made to the DNS server (see block
666 	 * comment atop resolv_txt_reset). If we're responding to
667 	 * a SIGHUP signal, force a reset of the cached copy.
668 	 */
669 	if (sighup) {
670 		(void) rw_wrlock(&dns_data_lock);
671 		dns_txt_cached = 0;
672 		(void) rw_unlock(&dns_data_lock);
673 	}
674 	resolv_txt_reset();
675 
676 	errno = 0;
677 	if (stat(_PATH_RESCONF, &st) < 0 && errno == ENOENT) {
678 		/*
679 		 * If /etc/resolv.conf is not there, then we'll
680 		 * get the domain from domainname(1M). No real
681 		 * reason to query DNS or fire a thread since we
682 		 * have no nameserver addresses.
683 		 */
684 		goto txtclear;
685 	}
686 
687 	(void) rw_rdlock(&s_dns_impl_lock);
688 	if (s_dns_disabled) {
689 		/*
690 		 * If there were non-recoverable problems with DNS,
691 		 * we have stopped querying DNS entirely. See
692 		 * NO_RECOVERY clause below.
693 		 */
694 		IDMAP_DBG("%s: DNS queries disabled", whoami, NULL);
695 		(void) rw_unlock(&s_dns_impl_lock);
696 		return;
697 	}
698 	(void) rw_unlock(&s_dns_impl_lock);
699 
700 	(void) resolv_init();
701 	switch (resolv_search()) {
702 		case NETDB_SUCCESS:
703 			/*
704 			 * If there _is_ a TXT record, we let
705 			 * our caller set the global state.
706 			 */
707 			resolv_decode();
708 			resolv_get_txt_data();
709 			return;
710 
711 		case TRY_AGAIN:
712 			(void) rw_wrlock(&s_dns_impl_lock);
713 			if (s_dns_qthr_created) {
714 				/*
715 				 * We may have lots of clients, so we don't
716 				 * want to bog down the DNS server with tons
717 				 * of requests... lest it becomes even more
718 				 * unresponsive, so limit 1 thread to query
719 				 * DNS at a time.
720 				 */
721 				IDMAP_DBG("%s: query thread already active",
722 				    whoami, NULL);
723 				(void) rw_unlock(&s_dns_impl_lock);
724 				return;
725 			}
726 
727 			/*
728 			 * DNS did not respond ! Set timeout and kick off
729 			 * thread to try op again after s_dns_tout seconds.
730 			 * We've made sure that we don't have an already
731 			 * running thread above.
732 			 */
733 			s_dns_tout = NFSMAPID_DNS_TOUT_SECS;
734 			err = thr_create(NULL, 0, resolv_query_thread, NULL,
735 			    thr_flags, &s_dns_qthread);
736 			if (!err) {
737 				s_dns_qthr_created = TRUE;
738 			}
739 #ifdef DEBUG
740 			else {
741 				msg_done++;
742 				if (!(msg_done % NFSMAPID_SLOG_RATE))
743 					IDMAP_DBG(EMSG_DNS_THREAD_ERROR, NULL,
744 					    NULL);
745 			}
746 #endif
747 			(void) rw_unlock(&s_dns_impl_lock);
748 			return;
749 
750 		case NO_RECOVERY:
751 			IDMAP_DBG(EMSG_DNS_DISABLE, whoami, NULL);
752 			(void) rw_wrlock(&s_dns_impl_lock);
753 			s_dns_disabled = TRUE;
754 			(void) rw_unlock(&s_dns_impl_lock);
755 
756 			/*FALLTHROUGH*/
757 		default:
758 			/*
759 			 * For any other errors... DNS is responding, but
760 			 * either it has no data, or some other problem is
761 			 * occuring. At any rate, the TXT domain should not
762 			 * be used, so we default to the DNS domain.
763 			 */
764 			break;
765 	}
766 
767 txtclear:
768 	(void) rw_wrlock(&dns_data_lock);
769 	dns_txt_cached = 0;
770 	(void) rw_unlock(&dns_data_lock);
771 	resolv_txt_reset();
772 }
773