xref: /titanic_51/usr/src/lib/libmapid/common/mapid.c (revision 8200fe25ffab8b2032d046c88710a949f361b700)
1*8200fe25Srmesta /*
2*8200fe25Srmesta  * CDDL HEADER START
3*8200fe25Srmesta  *
4*8200fe25Srmesta  * The contents of this file are subject to the terms of the
5*8200fe25Srmesta  * Common Development and Distribution License (the "License").
6*8200fe25Srmesta  * You may not use this file except in compliance with the License.
7*8200fe25Srmesta  *
8*8200fe25Srmesta  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*8200fe25Srmesta  * or http://www.opensolaris.org/os/licensing.
10*8200fe25Srmesta  * See the License for the specific language governing permissions
11*8200fe25Srmesta  * and limitations under the License.
12*8200fe25Srmesta  *
13*8200fe25Srmesta  * When distributing Covered Code, include this CDDL HEADER in each
14*8200fe25Srmesta  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*8200fe25Srmesta  * If applicable, add the following below this CDDL HEADER, with the
16*8200fe25Srmesta  * fields enclosed by brackets "[]" replaced with your own identifying
17*8200fe25Srmesta  * information: Portions Copyright [yyyy] [name of copyright owner]
18*8200fe25Srmesta  *
19*8200fe25Srmesta  * CDDL HEADER END
20*8200fe25Srmesta  */
21*8200fe25Srmesta /*
22*8200fe25Srmesta  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*8200fe25Srmesta  * Use is subject to license terms.
24*8200fe25Srmesta  */
25*8200fe25Srmesta 
26*8200fe25Srmesta #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*8200fe25Srmesta 
28*8200fe25Srmesta /*
29*8200fe25Srmesta  * PSARC/2004/154 nfsmapid DNS enhancements implementation.
30*8200fe25Srmesta  *
31*8200fe25Srmesta  * As per RFC 3530, file owner and group attributes in version 4 of the
32*8200fe25Srmesta  * NFS protocol are no longer exchanged between client and server as 32
33*8200fe25Srmesta  * bit integral values. Instead, owner and group file attributes are
34*8200fe25Srmesta  * exchanged between client and server as UTF8 strings of form
35*8200fe25Srmesta  *
36*8200fe25Srmesta  *      'user@domain'		(ie. "joeblow@central.sun.com")
37*8200fe25Srmesta  *      'group@domain'		(ie. "staff@central.sun.com")
38*8200fe25Srmesta  *
39*8200fe25Srmesta  * This NFSv4 feature is far beyond anything NFSv2/v3 ever provided, as
40*8200fe25Srmesta  * being able to describe a user with a unique string identifier provides
41*8200fe25Srmesta  * a much more powerful and administrative friendly way of dealing with
42*8200fe25Srmesta  * overlaps in the uid/gid number spaces. That notwithstanding, dealing
43*8200fe25Srmesta  * with issues of correctly mapping user and group ownership in a cross-
44*8200fe25Srmesta  * domain environment has proven a difficult problem to solve, since
45*8200fe25Srmesta  * dealing with different permutations of client naming configurations
46*8200fe25Srmesta  * (ie. NIS only, LDAP only, etc.) have bloated the problem. Thus, users
47*8200fe25Srmesta  * utilizing clients and servers that have the 'domain' portion of the
48*8200fe25Srmesta  * UTF8 attribute string configured differently than its peer server and
49*8200fe25Srmesta  * client accordingly, will experience watching their files owned by the
50*8200fe25Srmesta  * 'nobody' user and group. This is due to the fact that the 'domain's
51*8200fe25Srmesta  * don't match and the nfsmapid daemon treats the attribute strings as
52*8200fe25Srmesta  * unknown user(s) or group(s) (even though the actual uid/gid's may exist
53*8200fe25Srmesta  * in the executing daemon's system). Please refer to PSARC/2004/154 for
54*8200fe25Srmesta  * further background and motivation for these enhancements.
55*8200fe25Srmesta  *
56*8200fe25Srmesta  * The latest implementation of the nfsmapid daemon relies on a DNS TXT
57*8200fe25Srmesta  * record. The behavior of nfsmapid is to first use the NFSMAPID_DOMAIN
58*8200fe25Srmesta  * configuration option in /etc/default/nfs. If the option has not been
59*8200fe25Srmesta  * set, then the nfsmapid daemon queries the configured DNS domain server
60*8200fe25Srmesta  * for the _nfsv4idmapdomain TXT record. If the record exists, then the
61*8200fe25Srmesta  * record's value is used as the 'domain' portion of the UTF8 attribute
62*8200fe25Srmesta  * strings. If the TXT record has not been configured in the DNS server,
63*8200fe25Srmesta  * then the daemon falls back to using the DNS domain name itself as the
64*8200fe25Srmesta  * 'domain' portion of the attribute strings. Lastly, if the configured
65*8200fe25Srmesta  * DNS server is unresponsive, the nfsmapid daemon falls back to using
66*8200fe25Srmesta  * the DNS domain name as the 'domain' portion of the attribute strings,
67*8200fe25Srmesta  * and fires up a query thread to keep contacting the DNS server until
68*8200fe25Srmesta  * it responds with either a TXT record, or a lack thereof, in which
69*8200fe25Srmesta  * case, nfsmapid just continues to utilize the DNS domain name.
70*8200fe25Srmesta  */
71*8200fe25Srmesta #define	__LIBMAPID_IMPL
72*8200fe25Srmesta #include <nfs/mapid.h>
73*8200fe25Srmesta #pragma	init(_lib_init)
74*8200fe25Srmesta 
75*8200fe25Srmesta /*
76*8200fe25Srmesta  * DEBUG Only
77*8200fe25Srmesta  * Decode any resolver errors and print out message to log
78*8200fe25Srmesta  */
79*8200fe25Srmesta static int
80*8200fe25Srmesta resolv_error(void)
81*8200fe25Srmesta {
82*8200fe25Srmesta #ifndef	DEBUG
83*8200fe25Srmesta 
84*8200fe25Srmesta 	return (h_errno);
85*8200fe25Srmesta 
86*8200fe25Srmesta #else	/* DEBUG */
87*8200fe25Srmesta 
88*8200fe25Srmesta 	static uint64_t	 msg_done[NS_ERRS] = {0};
89*8200fe25Srmesta 
90*8200fe25Srmesta 	switch (h_errno) {
91*8200fe25Srmesta 	case NETDB_INTERNAL:
92*8200fe25Srmesta 		syslog(LOG_ERR, EMSG_NETDB_INTERNAL, strerror(errno));
93*8200fe25Srmesta 		break;
94*8200fe25Srmesta 
95*8200fe25Srmesta 	case HOST_NOT_FOUND:
96*8200fe25Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
97*8200fe25Srmesta 		msg_done[h_errno]++;
98*8200fe25Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
99*8200fe25Srmesta 			syslog(LOG_ERR, EMSG_HOST_NOT_FOUND, s_dname);
100*8200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
101*8200fe25Srmesta 		break;
102*8200fe25Srmesta 
103*8200fe25Srmesta 	case TRY_AGAIN:
104*8200fe25Srmesta 		/*
105*8200fe25Srmesta 		 * Nameserver is not responding.
106*8200fe25Srmesta 		 * Try again after a given timeout.
107*8200fe25Srmesta 		 */
108*8200fe25Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
109*8200fe25Srmesta 		msg_done[h_errno]++;
110*8200fe25Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
111*8200fe25Srmesta 			syslog(LOG_ERR, EMSG_TRY_AGAIN, s_dname);
112*8200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
113*8200fe25Srmesta 		break;
114*8200fe25Srmesta 
115*8200fe25Srmesta 	case NO_RECOVERY:
116*8200fe25Srmesta 		/*
117*8200fe25Srmesta 		 * This msg only really happens once, due
118*8200fe25Srmesta 		 * to s_dns_disabled flag (see below)
119*8200fe25Srmesta 		 */
120*8200fe25Srmesta 		syslog(LOG_ERR, EMSG_NO_RECOVERY, hstrerror(h_errno));
121*8200fe25Srmesta 		break;
122*8200fe25Srmesta 
123*8200fe25Srmesta 	case NO_DATA:
124*8200fe25Srmesta 		/*
125*8200fe25Srmesta 		 * No entries in the nameserver for
126*8200fe25Srmesta 		 * the specific record or record type.
127*8200fe25Srmesta 		 */
128*8200fe25Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
129*8200fe25Srmesta 		msg_done[h_errno]++;
130*8200fe25Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
131*8200fe25Srmesta 			syslog(LOG_ERR, EMSG_NO_DATA, NFSMAPID_DNS_RR, s_dname);
132*8200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
133*8200fe25Srmesta 		break;
134*8200fe25Srmesta 
135*8200fe25Srmesta 	case NETDB_SUCCESS:
136*8200fe25Srmesta 	default:
137*8200fe25Srmesta 		break;
138*8200fe25Srmesta 	}
139*8200fe25Srmesta 	return (h_errno);
140*8200fe25Srmesta 
141*8200fe25Srmesta #endif	/* DEBUG */
142*8200fe25Srmesta }
143*8200fe25Srmesta 
144*8200fe25Srmesta /*
145*8200fe25Srmesta  * Reset the global state variables used for the TXT record.
146*8200fe25Srmesta  * Having these values reset to zero helps nfsmapid confirm
147*8200fe25Srmesta  * that a valid DNS TXT record was not found; in which case,
148*8200fe25Srmesta  * it would fall back to using the configured DNS domain name.
149*8200fe25Srmesta  *
150*8200fe25Srmesta  * If a valid DNS TXT record _was_ found, but subsequent contact
151*8200fe25Srmesta  * to the DNS server is somehow hindered, the previous DNS TXT
152*8200fe25Srmesta  * RR value continues to be used. Thus, in such instances, we
153*8200fe25Srmesta  * forego clearing the global config variables so nfsmapid can
154*8200fe25Srmesta  * continue to use a valid DNS TXT RR while contact to the DNS
155*8200fe25Srmesta  * server is reestablished.
156*8200fe25Srmesta  */
157*8200fe25Srmesta static void
158*8200fe25Srmesta resolv_txt_reset(void)
159*8200fe25Srmesta {
160*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
161*8200fe25Srmesta 	bzero(s_txt_rr, sizeof (s_txt_rr));
162*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
163*8200fe25Srmesta 
164*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
165*8200fe25Srmesta 	if (!dns_txt_cached) {
166*8200fe25Srmesta 		dns_txt_domain_len = 0;
167*8200fe25Srmesta 		bzero(dns_txt_domain, DNAMEMAX);
168*8200fe25Srmesta 	}
169*8200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
170*8200fe25Srmesta }
171*8200fe25Srmesta 
172*8200fe25Srmesta /*
173*8200fe25Srmesta  * Initialize resolver and populate &s_res struct
174*8200fe25Srmesta  *
175*8200fe25Srmesta  * DNS Domain is saved off sysdns_domain in case we
176*8200fe25Srmesta  * need to fall back to using the DNS domain name as
177*8200fe25Srmesta  * the v4 attribute string domain.
178*8200fe25Srmesta  */
179*8200fe25Srmesta static int
180*8200fe25Srmesta resolv_init(void)
181*8200fe25Srmesta {
182*8200fe25Srmesta 	size_t			len;
183*8200fe25Srmesta 	int			n;
184*8200fe25Srmesta 	struct __res_state	res;
185*8200fe25Srmesta 
186*8200fe25Srmesta 	(void) mutex_lock(&s_res_lock);
187*8200fe25Srmesta 	bzero(&s_res, sizeof (struct __res_state));
188*8200fe25Srmesta 	n = h_errno = errno = 0;
189*8200fe25Srmesta 	if ((n = res_ninit(&s_res)) < 0) {
190*8200fe25Srmesta 		(void) mutex_unlock(&s_res_lock);
191*8200fe25Srmesta 		(void) resolv_error();
192*8200fe25Srmesta 		return (n);
193*8200fe25Srmesta 	}
194*8200fe25Srmesta 	res = s_res;
195*8200fe25Srmesta 	(void) mutex_unlock(&s_res_lock);
196*8200fe25Srmesta 
197*8200fe25Srmesta 	len = strlen(res.defdname) + 1;
198*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
199*8200fe25Srmesta 	bzero(s_dname, sizeof (s_dname));
200*8200fe25Srmesta 	(void) snprintf(s_dname, len, "%s", res.defdname);
201*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
202*8200fe25Srmesta 
203*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
204*8200fe25Srmesta 	(void) snprintf(sysdns_domain, len, "%s", res.defdname);
205*8200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
206*8200fe25Srmesta 
207*8200fe25Srmesta 	return (0);
208*8200fe25Srmesta }
209*8200fe25Srmesta 
210*8200fe25Srmesta /*
211*8200fe25Srmesta  * Search criteria assumptions:
212*8200fe25Srmesta  *
213*8200fe25Srmesta  * The onus will fall on the sysadmins to correctly configure the TXT
214*8200fe25Srmesta  * record in the DNS domain where the box currently resides in order
215*8200fe25Srmesta  * for the record to be found. However, if they sysadmin chooses to
216*8200fe25Srmesta  * add the 'search' key to /etc/resolv.conf, then resolv_search()
217*8200fe25Srmesta  * _will_ traverse up the DNS tree as specified in the 'search' key.
218*8200fe25Srmesta  * Otherwise, we'll default the domain to the DNS domain itself.
219*8200fe25Srmesta  */
220*8200fe25Srmesta static int
221*8200fe25Srmesta resolv_search(void)
222*8200fe25Srmesta {
223*8200fe25Srmesta 	int			len;
224*8200fe25Srmesta 	ans_t			ans = {0};
225*8200fe25Srmesta 	struct __res_state	res;
226*8200fe25Srmesta 	int			type = T_TXT;
227*8200fe25Srmesta 	int			class = C_IN;
228*8200fe25Srmesta 
229*8200fe25Srmesta 	(void) mutex_lock(&s_res_lock);
230*8200fe25Srmesta 	res = s_res;
231*8200fe25Srmesta 	(void) mutex_unlock(&s_res_lock);
232*8200fe25Srmesta 
233*8200fe25Srmesta 	/*
234*8200fe25Srmesta 	 * Avoid holding locks across the res_nsearch() call to
235*8200fe25Srmesta 	 * prevent stalling threads during network partitions.
236*8200fe25Srmesta 	 */
237*8200fe25Srmesta 	len = h_errno = errno = 0;
238*8200fe25Srmesta 	if ((len = res_nsearch(&res, NFSMAPID_DNS_RR, class, type,
239*8200fe25Srmesta 	    ans.buf, sizeof (ans))) < 0)
240*8200fe25Srmesta 		return (resolv_error());
241*8200fe25Srmesta 
242*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
243*8200fe25Srmesta 	s_ans = ans;
244*8200fe25Srmesta 	s_anslen = len;
245*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
246*8200fe25Srmesta 
247*8200fe25Srmesta 	return (NETDB_SUCCESS);
248*8200fe25Srmesta }
249*8200fe25Srmesta 
250*8200fe25Srmesta /*
251*8200fe25Srmesta  * Skip one DNS record
252*8200fe25Srmesta  */
253*8200fe25Srmesta static uchar_t  *
254*8200fe25Srmesta resolv_skip_rr(uchar_t *p, uchar_t *eom)
255*8200fe25Srmesta {
256*8200fe25Srmesta 	int	t;
257*8200fe25Srmesta 	int	dlen;
258*8200fe25Srmesta 
259*8200fe25Srmesta 	/*
260*8200fe25Srmesta 	 * Skip compressed name
261*8200fe25Srmesta 	 */
262*8200fe25Srmesta 	errno = 0;
263*8200fe25Srmesta 	if ((t = dn_skipname(p, eom)) < 0) {
264*8200fe25Srmesta #ifdef	DEBUG
265*8200fe25Srmesta 		syslog(LOG_ERR, "%s", strerror(errno));
266*8200fe25Srmesta #endif
267*8200fe25Srmesta 		return (NULL);
268*8200fe25Srmesta 	}
269*8200fe25Srmesta 
270*8200fe25Srmesta 	/*
271*8200fe25Srmesta 	 * Advance pointer and make sure
272*8200fe25Srmesta 	 * we're still within the message
273*8200fe25Srmesta 	 */
274*8200fe25Srmesta 	p += t;
275*8200fe25Srmesta 	if ((p + RRFIXEDSZ) > eom)
276*8200fe25Srmesta 		return (NULL);
277*8200fe25Srmesta 
278*8200fe25Srmesta 	/*
279*8200fe25Srmesta 	 * Now, just skip over the rr fields
280*8200fe25Srmesta 	 */
281*8200fe25Srmesta 	p += INT16SZ;	/* type */
282*8200fe25Srmesta 	p += INT16SZ;	/* class */
283*8200fe25Srmesta 	p += INT32SZ;	/* ttl */
284*8200fe25Srmesta 	dlen = ns_get16(p);
285*8200fe25Srmesta 	p += INT16SZ;
286*8200fe25Srmesta 	p += dlen;	/* dlen */
287*8200fe25Srmesta 	if (p > eom)
288*8200fe25Srmesta 		return (NULL);
289*8200fe25Srmesta 
290*8200fe25Srmesta 	return (p);
291*8200fe25Srmesta }
292*8200fe25Srmesta 
293*8200fe25Srmesta /*
294*8200fe25Srmesta  * Process one TXT record.
295*8200fe25Srmesta  *
296*8200fe25Srmesta  * nfsmapid queries the DNS server for the specific _nfsv4idmapdomain
297*8200fe25Srmesta  * TXT record. Thus, if the TXT record exists, the answer section of
298*8200fe25Srmesta  * the DNS response carries the TXT record's value. Thus, we check that
299*8200fe25Srmesta  * the value is indeed a valid domain and set the modular s_txt_rr
300*8200fe25Srmesta  * global to the domain value.
301*8200fe25Srmesta  */
302*8200fe25Srmesta static void
303*8200fe25Srmesta resolve_process_txt(uchar_t *p, int dlen)
304*8200fe25Srmesta {
305*8200fe25Srmesta 	char		*rr_base = (char *)(p + 1);
306*8200fe25Srmesta 	char		*rr_end = (char *)(p + dlen);
307*8200fe25Srmesta 	size_t		 len = rr_end - rr_base;
308*8200fe25Srmesta #ifdef	DEBUG
309*8200fe25Srmesta 	static uint64_t	 msg_done = 0;
310*8200fe25Srmesta #endif
311*8200fe25Srmesta 	char		 tmp_txt_rr[DNAMEMAX];
312*8200fe25Srmesta 
313*8200fe25Srmesta 	if (len >= DNAMEMAX)
314*8200fe25Srmesta 		return;		/* process next TXT RR */
315*8200fe25Srmesta 
316*8200fe25Srmesta 	/*
317*8200fe25Srmesta 	 * make sure we have a clean buf since
318*8200fe25Srmesta 	 * we may've processed several TXT rr's
319*8200fe25Srmesta 	 */
320*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
321*8200fe25Srmesta 	bzero(s_txt_rr, sizeof (s_txt_rr));
322*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
323*8200fe25Srmesta 
324*8200fe25Srmesta 	(void) strncpy(tmp_txt_rr, rr_base, len);
325*8200fe25Srmesta 	tmp_txt_rr[len] = '\0';
326*8200fe25Srmesta 
327*8200fe25Srmesta 	/*
328*8200fe25Srmesta 	 * If there is a record and it's a valid domain, we're done.
329*8200fe25Srmesta 	 */
330*8200fe25Srmesta 	if (rr_base[0] != '\0' && mapid_stdchk_domain(tmp_txt_rr) > 0) {
331*8200fe25Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
332*8200fe25Srmesta 		(void) strncpy(s_txt_rr, rr_base, len);
333*8200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
334*8200fe25Srmesta #ifdef	DEBUG
335*8200fe25Srmesta 		syslog(LOG_ERR, "TXT (Rec):\t%s", s_txt_rr);
336*8200fe25Srmesta 
337*8200fe25Srmesta 	} else if (!(msg_done++ % NFSMAPID_SLOG_RATE)) {
338*8200fe25Srmesta 		/*
339*8200fe25Srmesta 		 * Otherwise, log the error
340*8200fe25Srmesta 		 */
341*8200fe25Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
342*8200fe25Srmesta 		syslog(LOG_ERR, EMSG_DNS_RR_INVAL, NFSMAPID_DNS_RR, s_dname);
343*8200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
344*8200fe25Srmesta #endif
345*8200fe25Srmesta 	}
346*8200fe25Srmesta }
347*8200fe25Srmesta 
348*8200fe25Srmesta /*
349*8200fe25Srmesta  * Decode any answer received from the DNS server. This interface is
350*8200fe25Srmesta  * capable of much more than just decoding TXT records. We maintain
351*8200fe25Srmesta  * focus on TXT rr's for now, but this will probably change once we
352*8200fe25Srmesta  * get the IETF approved application specific DNS RR.
353*8200fe25Srmesta  *
354*8200fe25Srmesta  * Here's an example of the TXT record we're decoding (as would appear
355*8200fe25Srmesta  * in the DNS zone file):
356*8200fe25Srmesta  *
357*8200fe25Srmesta  *            _nfsv4idmapdomain    IN    TXT    "sun.com"
358*8200fe25Srmesta  *
359*8200fe25Srmesta  * Once the IETF application specific DNS RR is granted, we should only
360*8200fe25Srmesta  * be changing the record flavor, but all should pretty much stay the
361*8200fe25Srmesta  * same.
362*8200fe25Srmesta  */
363*8200fe25Srmesta static void
364*8200fe25Srmesta resolv_decode(void)
365*8200fe25Srmesta {
366*8200fe25Srmesta 	uchar_t		*buf;
367*8200fe25Srmesta 	HEADER		*hp;
368*8200fe25Srmesta 	uchar_t		 name[DNAMEMAX];
369*8200fe25Srmesta 	uchar_t		*eom;
370*8200fe25Srmesta 	uchar_t		*p;
371*8200fe25Srmesta 	int		 n;
372*8200fe25Srmesta 	uint_t		 qd_cnt;
373*8200fe25Srmesta 	uint_t		 an_cnt;
374*8200fe25Srmesta 	uint_t		 ns_cnt;
375*8200fe25Srmesta 	uint_t		 ar_cnt;
376*8200fe25Srmesta 	uint_t		 cnt;
377*8200fe25Srmesta 	uint_t		 type;
378*8200fe25Srmesta 	int		 dlen;
379*8200fe25Srmesta 	ans_t		 answer = {0};
380*8200fe25Srmesta 	int		 answer_len = 0;
381*8200fe25Srmesta 
382*8200fe25Srmesta 	/*
383*8200fe25Srmesta 	 * Check the HEADER for any signs of errors
384*8200fe25Srmesta 	 * and extract the answer counts for later.
385*8200fe25Srmesta 	 */
386*8200fe25Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
387*8200fe25Srmesta 	answer = s_ans;
388*8200fe25Srmesta 	answer_len = s_anslen;
389*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
390*8200fe25Srmesta 
391*8200fe25Srmesta 	buf = (uchar_t *)&answer.buf;
392*8200fe25Srmesta 	hp = (HEADER *)&answer.hdr;
393*8200fe25Srmesta 	eom = (uchar_t *)(buf + answer_len);
394*8200fe25Srmesta 	if (hp->rcode !=  NOERROR) {
395*8200fe25Srmesta #ifdef	DEBUG
396*8200fe25Srmesta 		syslog(LOG_ERR, "errno: %s", strerror(errno));
397*8200fe25Srmesta 		syslog(LOG_ERR, "h_errno: %s", hstrerror(h_errno));
398*8200fe25Srmesta #endif
399*8200fe25Srmesta 		return;
400*8200fe25Srmesta 	}
401*8200fe25Srmesta 	qd_cnt = ntohs(hp->qdcount);
402*8200fe25Srmesta 	an_cnt = ntohs(hp->ancount);
403*8200fe25Srmesta 	ns_cnt = ntohs(hp->nscount);
404*8200fe25Srmesta 	ar_cnt = ntohs(hp->arcount);
405*8200fe25Srmesta 
406*8200fe25Srmesta 	/*
407*8200fe25Srmesta 	 * skip query entries
408*8200fe25Srmesta 	 */
409*8200fe25Srmesta 	p = (uchar_t *)(buf + HFIXEDSZ);
410*8200fe25Srmesta 	errno = 0;
411*8200fe25Srmesta 	while (qd_cnt-- > 0) {
412*8200fe25Srmesta 		n = dn_skipname(p, eom);
413*8200fe25Srmesta 		if (n < 0) {
414*8200fe25Srmesta #ifdef	DEBUG
415*8200fe25Srmesta 			syslog(LOG_ERR, "%s", strerror(errno));
416*8200fe25Srmesta #endif
417*8200fe25Srmesta 			return;
418*8200fe25Srmesta 		}
419*8200fe25Srmesta 		p += n;
420*8200fe25Srmesta 		p += INT16SZ;	/* type */
421*8200fe25Srmesta 		p += INT16SZ;	/* class */
422*8200fe25Srmesta 	}
423*8200fe25Srmesta 
424*8200fe25Srmesta #ifdef	DEBUG
425*8200fe25Srmesta 	/*
426*8200fe25Srmesta 	 * If debugging... print query only once.
427*8200fe25Srmesta 	 * NOTE: Don't advance pointer... this is done
428*8200fe25Srmesta 	 *	 in while() loop on a per record basis !
429*8200fe25Srmesta 	 */
430*8200fe25Srmesta 	n = h_errno = errno = 0;
431*8200fe25Srmesta 	n = dn_expand(buf, eom, p, (char *)name, sizeof (name));
432*8200fe25Srmesta 	if (n < 0) {
433*8200fe25Srmesta 		(void) resolv_error();
434*8200fe25Srmesta 		return;
435*8200fe25Srmesta 	}
436*8200fe25Srmesta 	syslog(LOG_ERR, "Query:\t\t%-30s", name);
437*8200fe25Srmesta #endif
438*8200fe25Srmesta 
439*8200fe25Srmesta 	/*
440*8200fe25Srmesta 	 * Process actual answer(s).
441*8200fe25Srmesta 	 */
442*8200fe25Srmesta 	cnt = an_cnt;
443*8200fe25Srmesta 	while (cnt-- > 0 && p < eom) {
444*8200fe25Srmesta 		/* skip the name field */
445*8200fe25Srmesta 		n = dn_expand(buf, eom, p, (char *)name, sizeof (name));
446*8200fe25Srmesta 		if (n < 0) {
447*8200fe25Srmesta 			(void) resolv_error();
448*8200fe25Srmesta 			return;
449*8200fe25Srmesta 		}
450*8200fe25Srmesta 		p += n;
451*8200fe25Srmesta 
452*8200fe25Srmesta 		if ((p + 3 * INT16SZ + INT32SZ) > eom)
453*8200fe25Srmesta 			return;
454*8200fe25Srmesta 
455*8200fe25Srmesta 		type = ns_get16(p);
456*8200fe25Srmesta 		p += INT16SZ;
457*8200fe25Srmesta 		p += INT16SZ + INT32SZ;	/* skip class & ttl */
458*8200fe25Srmesta 		dlen = ns_get16(p);
459*8200fe25Srmesta 		p += INT16SZ;
460*8200fe25Srmesta 
461*8200fe25Srmesta 		if ((p + dlen) > eom)
462*8200fe25Srmesta 			return;
463*8200fe25Srmesta 
464*8200fe25Srmesta 		switch (type) {
465*8200fe25Srmesta 			case T_TXT:
466*8200fe25Srmesta 				resolve_process_txt(p, dlen);
467*8200fe25Srmesta 				break;
468*8200fe25Srmesta 
469*8200fe25Srmesta 			default:
470*8200fe25Srmesta 				/*
471*8200fe25Srmesta 				 * Advance to next answer record for any
472*8200fe25Srmesta 				 * other record types. Again, this will
473*8200fe25Srmesta 				 * probably change (see block comment).
474*8200fe25Srmesta 				 */
475*8200fe25Srmesta 				p += dlen;
476*8200fe25Srmesta 				break;
477*8200fe25Srmesta 		}
478*8200fe25Srmesta 	}
479*8200fe25Srmesta 
480*8200fe25Srmesta 	/*
481*8200fe25Srmesta 	 * Skip name server and additional records for now.
482*8200fe25Srmesta 	 */
483*8200fe25Srmesta 	cnt = ns_cnt + ar_cnt;
484*8200fe25Srmesta 	if (cnt > 0) {
485*8200fe25Srmesta 		while (--cnt != 0 && p < eom) {
486*8200fe25Srmesta 			p = resolv_skip_rr(p, eom);
487*8200fe25Srmesta 			if (p == NULL)
488*8200fe25Srmesta 				return;
489*8200fe25Srmesta 		}
490*8200fe25Srmesta 	}
491*8200fe25Srmesta }
492*8200fe25Srmesta 
493*8200fe25Srmesta /*
494*8200fe25Srmesta  * If a valid TXT record entry exists, s_txt_rr contains the domain
495*8200fe25Srmesta  * value (as set in resolv_process_txt) and we extract the value into
496*8200fe25Srmesta  * dns_txt_domain (the exported global). If there was _no_ valid TXT
497*8200fe25Srmesta  * entry, we simply return and check_domain() will default to the
498*8200fe25Srmesta  * DNS domain since we did resolv_txt_reset() first.
499*8200fe25Srmesta  */
500*8200fe25Srmesta static void
501*8200fe25Srmesta resolv_get_txt_data()
502*8200fe25Srmesta {
503*8200fe25Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
504*8200fe25Srmesta 	if (s_txt_rr[0] != '\0') {
505*8200fe25Srmesta 		(void) rw_wrlock(&s_dns_data_lock);
506*8200fe25Srmesta 		(void) snprintf(dns_txt_domain, strlen(s_txt_rr) + 1, "%s",
507*8200fe25Srmesta 								s_txt_rr);
508*8200fe25Srmesta 		dns_txt_domain_len = strlen(dns_txt_domain);
509*8200fe25Srmesta 		dns_txt_cached = 1;
510*8200fe25Srmesta 		(void) rw_unlock(&s_dns_data_lock);
511*8200fe25Srmesta 	}
512*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
513*8200fe25Srmesta }
514*8200fe25Srmesta 
515*8200fe25Srmesta static void
516*8200fe25Srmesta domain_sync(cb_t *argp, char *dname)
517*8200fe25Srmesta {
518*8200fe25Srmesta 	int	dlen = 0;
519*8200fe25Srmesta 	void	*(*fcn)(void *) = NULL;
520*8200fe25Srmesta 	int	sighup = 0;
521*8200fe25Srmesta 	int	domchg = 0;
522*8200fe25Srmesta 
523*8200fe25Srmesta 	/*
524*8200fe25Srmesta 	 * Make sure values passed are sane and initialize accordingly.
525*8200fe25Srmesta 	 */
526*8200fe25Srmesta 	if (dname != NULL)
527*8200fe25Srmesta 		dlen = strlen(dname);
528*8200fe25Srmesta 	if (argp) {
529*8200fe25Srmesta 		if (argp->fcn)
530*8200fe25Srmesta 			fcn = argp->fcn;
531*8200fe25Srmesta 		if (argp->signal)
532*8200fe25Srmesta 			sighup = argp->signal;
533*8200fe25Srmesta 	}
534*8200fe25Srmesta 
535*8200fe25Srmesta 	/*
536*8200fe25Srmesta 	 * Update the library's mapid_domain variable if 'dname' is different.
537*8200fe25Srmesta 	 */
538*8200fe25Srmesta 	if (dlen != 0 && strncasecmp(dname, mapid_domain, NS_MAXCDNAME)) {
539*8200fe25Srmesta 		(void) rw_wrlock(&mapid_domain_lock);
540*8200fe25Srmesta 		(void) strncpy(mapid_domain, dname, NS_MAXCDNAME);
541*8200fe25Srmesta 		mapid_domain_len = dlen;
542*8200fe25Srmesta 		(void) rw_unlock(&mapid_domain_lock);
543*8200fe25Srmesta 		domchg++;
544*8200fe25Srmesta 	}
545*8200fe25Srmesta 
546*8200fe25Srmesta 	/*
547*8200fe25Srmesta 	 * If the caller gave us a valid callback routine, we
548*8200fe25Srmesta 	 * instantiate it to announce the domain change, but
549*8200fe25Srmesta 	 * only if either the domain changed _or_ the caller
550*8200fe25Srmesta 	 * was issued a SIGHUP.
551*8200fe25Srmesta 	 */
552*8200fe25Srmesta 	if (fcn != NULL && (sighup || domchg))
553*8200fe25Srmesta 		(void) fcn((void *)mapid_domain);
554*8200fe25Srmesta }
555*8200fe25Srmesta 
556*8200fe25Srmesta /*
557*8200fe25Srmesta  * Thread to keep pinging  DNS  server for  TXT  record if nfsmapid's
558*8200fe25Srmesta  * initial attempt at contact with server failed. We could potentially
559*8200fe25Srmesta  * have a substantial number of NFSv4 clients and having all of them
560*8200fe25Srmesta  * hammering on an already unresponsive DNS server would not help
561*8200fe25Srmesta  * things. So, we limit the number of live query threads to at most
562*8200fe25Srmesta  * 1 at any one time to keep things from getting out of hand.
563*8200fe25Srmesta  */
564*8200fe25Srmesta /* ARGSUSED */
565*8200fe25Srmesta static void *
566*8200fe25Srmesta resolv_query_thread(void *arg)
567*8200fe25Srmesta {
568*8200fe25Srmesta 	unsigned int	 nap_time;
569*8200fe25Srmesta 
570*8200fe25Srmesta #ifdef	DEBUG
571*8200fe25Srmesta 	char		*whoami = "query_thread";
572*8200fe25Srmesta 
573*8200fe25Srmesta 	syslog(LOG_ERR, "%s active !", whoami);
574*8200fe25Srmesta #endif
575*8200fe25Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
576*8200fe25Srmesta 	nap_time = s_dns_tout;
577*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
578*8200fe25Srmesta 
579*8200fe25Srmesta 	for (;;) {
580*8200fe25Srmesta 		(void) sleep(nap_time);
581*8200fe25Srmesta 
582*8200fe25Srmesta 		resolv_txt_reset();
583*8200fe25Srmesta 		(void) resolv_init();
584*8200fe25Srmesta 		switch (resolv_search()) {
585*8200fe25Srmesta 		case NETDB_SUCCESS:
586*8200fe25Srmesta 			resolv_decode();
587*8200fe25Srmesta 			resolv_get_txt_data();
588*8200fe25Srmesta 
589*8200fe25Srmesta 			/*
590*8200fe25Srmesta 			 * This is a bit different than what we
591*8200fe25Srmesta 			 * do in get_dns_txt_domain(), where we
592*8200fe25Srmesta 			 * simply return and let the caller
593*8200fe25Srmesta 			 * access dns_txt_domain directly.
594*8200fe25Srmesta 			 *
595*8200fe25Srmesta 			 * Here we invoke the callback routine
596*8200fe25Srmesta 			 * provided by the caller to the
597*8200fe25Srmesta 			 * mapid_reeval_domain() interface via
598*8200fe25Srmesta 			 * the cb_t's fcn param.
599*8200fe25Srmesta 			 */
600*8200fe25Srmesta 			domain_sync((cb_t *)arg, dns_txt_domain);
601*8200fe25Srmesta 			goto thr_okay;
602*8200fe25Srmesta 
603*8200fe25Srmesta 		case NO_DATA:
604*8200fe25Srmesta 			/*
605*8200fe25Srmesta 			 * DNS is up now, but does not have
606*8200fe25Srmesta 			 * the NFSV4IDMAPDOMAIN TXT record.
607*8200fe25Srmesta 			 */
608*8200fe25Srmesta #ifdef	DEBUG
609*8200fe25Srmesta 			syslog(LOG_ERR, "%s: DNS has no TXT Record", whoami);
610*8200fe25Srmesta #endif
611*8200fe25Srmesta 			goto thr_reset;
612*8200fe25Srmesta 
613*8200fe25Srmesta 		case NO_RECOVERY:
614*8200fe25Srmesta 			/*
615*8200fe25Srmesta 			 * Non-Recoverable error occurred. No sense
616*8200fe25Srmesta 			 * in keep pinging the DNS server at this
617*8200fe25Srmesta 			 * point, so we disable any further contact.
618*8200fe25Srmesta 			 */
619*8200fe25Srmesta #ifdef	DEBUG
620*8200fe25Srmesta 			syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami);
621*8200fe25Srmesta #endif
622*8200fe25Srmesta 			(void) rw_wrlock(&s_dns_impl_lock);
623*8200fe25Srmesta 			s_dns_disabled = TRUE;
624*8200fe25Srmesta 			(void) rw_unlock(&s_dns_impl_lock);
625*8200fe25Srmesta 			goto thr_reset;
626*8200fe25Srmesta 
627*8200fe25Srmesta 		case HOST_NOT_FOUND:
628*8200fe25Srmesta 			/*
629*8200fe25Srmesta 			 * Authoritative NS not responding...
630*8200fe25Srmesta 			 * keep trying for non-authoritative reply
631*8200fe25Srmesta 			 */
632*8200fe25Srmesta 			/*FALLTHROUGH*/
633*8200fe25Srmesta 
634*8200fe25Srmesta 		case TRY_AGAIN:
635*8200fe25Srmesta 			/* keep trying */
636*8200fe25Srmesta #ifdef	DEBUG
637*8200fe25Srmesta 			syslog(LOG_ERR, "%s: retrying...", whoami);
638*8200fe25Srmesta #endif
639*8200fe25Srmesta 			break;
640*8200fe25Srmesta 
641*8200fe25Srmesta 		case NETDB_INTERNAL:
642*8200fe25Srmesta 		default:
643*8200fe25Srmesta #ifdef	DEBUG
644*8200fe25Srmesta 			syslog(LOG_ERR, "%s: Internal resolver error: %s",
645*8200fe25Srmesta 			    whoami, strerror(errno));
646*8200fe25Srmesta #endif
647*8200fe25Srmesta 			goto thr_reset;
648*8200fe25Srmesta 		}
649*8200fe25Srmesta 	}
650*8200fe25Srmesta thr_reset:
651*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
652*8200fe25Srmesta 	dns_txt_cached = 0;
653*8200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
654*8200fe25Srmesta 	resolv_txt_reset();
655*8200fe25Srmesta 
656*8200fe25Srmesta thr_okay:
657*8200fe25Srmesta 	/* mark thread as done */
658*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
659*8200fe25Srmesta 	s_dns_qthr_created = FALSE;
660*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
661*8200fe25Srmesta 
662*8200fe25Srmesta 	(void) thr_exit(NULL);
663*8200fe25Srmesta 	/*NOTREACHED*/
664*8200fe25Srmesta 	return (NULL);
665*8200fe25Srmesta }
666*8200fe25Srmesta 
667*8200fe25Srmesta /*
668*8200fe25Srmesta  * nfsmapid's interface into the resolver for getting the TXT record.
669*8200fe25Srmesta  *
670*8200fe25Srmesta  * Key concepts:
671*8200fe25Srmesta  *
672*8200fe25Srmesta  * o If the DNS server is available and the TXT record is found, we
673*8200fe25Srmesta  *   simply decode the output and fill the exported dns_txt_domain
674*8200fe25Srmesta  *   global, so our caller can configure the daemon appropriately.
675*8200fe25Srmesta  *
676*8200fe25Srmesta  * o If the TXT record is not found, then having done resolv_txt_reset()
677*8200fe25Srmesta  *   first will allow our caller to recognize that the exported globals
678*8200fe25Srmesta  *   are empty and thus configure nfsmapid to use the default DNS domain.
679*8200fe25Srmesta  *
680*8200fe25Srmesta  * o Having no /etc/resolv.conf file is pretty much a show stopper, since
681*8200fe25Srmesta  *   there is no name server address information. We return since we've
682*8200fe25Srmesta  *   already have reset the TXT global state.
683*8200fe25Srmesta  *
684*8200fe25Srmesta  * o If a previous call to the DNS server resulted in an unrecoverable
685*8200fe25Srmesta  *   error, then we disable further contact to the DNS server and return.
686*8200fe25Srmesta  *   Having the TXT global state already reset guarantees that our caller
687*8200fe25Srmesta  *   will fall back to the right configuration.
688*8200fe25Srmesta  *
689*8200fe25Srmesta  * o Query thread creation is throttled by s_dns_qthr_created. We mitigate
690*8200fe25Srmesta  *   the problem of an already unresponsive DNS server by allowing at most
691*8200fe25Srmesta  *   1 outstanding query thread since we could potentially have a substantial
692*8200fe25Srmesta  *   amount of clients hammering on the same DNS server attempting to get
693*8200fe25Srmesta  *   the TXT record.
694*8200fe25Srmesta  */
695*8200fe25Srmesta static void
696*8200fe25Srmesta get_dns_txt_domain(cb_t *argp)
697*8200fe25Srmesta {
698*8200fe25Srmesta 	int		err;
699*8200fe25Srmesta #ifdef	DEBUG
700*8200fe25Srmesta 	static uint64_t	msg_done = 0;
701*8200fe25Srmesta 	char		*whoami = "get_dns_txt_domain";
702*8200fe25Srmesta #endif
703*8200fe25Srmesta 	long		thr_flags = THR_DETACHED;
704*8200fe25Srmesta 	struct stat	st;
705*8200fe25Srmesta 
706*8200fe25Srmesta 	/*
707*8200fe25Srmesta 	 * We reset TXT variables first in case /etc/resolv.conf
708*8200fe25Srmesta 	 * is missing or we've had unrecoverable resolver errors,
709*8200fe25Srmesta 	 * we'll default to get_dns_domain(). If a previous DNS
710*8200fe25Srmesta 	 * TXT RR was found, don't clear it until we're certain
711*8200fe25Srmesta 	 * that contact can be made to the DNS server (see block
712*8200fe25Srmesta 	 * comment atop resolv_txt_reset). If we're responding to
713*8200fe25Srmesta 	 * a SIGHUP signal, force a reset of the cached copy.
714*8200fe25Srmesta 	 */
715*8200fe25Srmesta 	if (argp && argp->signal) {
716*8200fe25Srmesta 		(void) rw_wrlock(&s_dns_data_lock);
717*8200fe25Srmesta 		dns_txt_cached = 0;
718*8200fe25Srmesta 		(void) rw_unlock(&s_dns_data_lock);
719*8200fe25Srmesta 	}
720*8200fe25Srmesta 	resolv_txt_reset();
721*8200fe25Srmesta 
722*8200fe25Srmesta 	errno = 0;
723*8200fe25Srmesta 	if (stat(_PATH_RESCONF, &st) < 0 && errno == ENOENT) {
724*8200fe25Srmesta 		/*
725*8200fe25Srmesta 		 * If /etc/resolv.conf is not there, then we'll
726*8200fe25Srmesta 		 * get the domain from domainname(1M). No real
727*8200fe25Srmesta 		 * reason to query DNS or fire a thread since we
728*8200fe25Srmesta 		 * have no nameserver addresses.
729*8200fe25Srmesta 		 */
730*8200fe25Srmesta 		goto txtclear;
731*8200fe25Srmesta 	}
732*8200fe25Srmesta 
733*8200fe25Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
734*8200fe25Srmesta 	if (s_dns_disabled) {
735*8200fe25Srmesta 		/*
736*8200fe25Srmesta 		 * If there were non-recoverable problems with DNS,
737*8200fe25Srmesta 		 * we have stopped querying DNS entirely. See
738*8200fe25Srmesta 		 * NO_RECOVERY clause below.
739*8200fe25Srmesta 		 */
740*8200fe25Srmesta #ifdef	DEBUG
741*8200fe25Srmesta 		syslog(LOG_ERR, "%s: DNS queries disabled", whoami);
742*8200fe25Srmesta #endif
743*8200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
744*8200fe25Srmesta 		return;
745*8200fe25Srmesta 	}
746*8200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
747*8200fe25Srmesta 
748*8200fe25Srmesta 	(void) resolv_init();
749*8200fe25Srmesta 	switch (resolv_search()) {
750*8200fe25Srmesta 	case NETDB_SUCCESS:
751*8200fe25Srmesta 		/*
752*8200fe25Srmesta 		 * If there _is_ a TXT record, we let
753*8200fe25Srmesta 		 * our caller set the global state.
754*8200fe25Srmesta 		 */
755*8200fe25Srmesta 		resolv_decode();
756*8200fe25Srmesta 		resolv_get_txt_data();
757*8200fe25Srmesta 		return;
758*8200fe25Srmesta 
759*8200fe25Srmesta 	case TRY_AGAIN:
760*8200fe25Srmesta 		if (argp == NULL || argp->fcn == NULL)
761*8200fe25Srmesta 			/*
762*8200fe25Srmesta 			 * If no valid argument was passed or
763*8200fe25Srmesta 			 * callback defined, don't fire thread
764*8200fe25Srmesta 			 */
765*8200fe25Srmesta 			return;
766*8200fe25Srmesta 
767*8200fe25Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
768*8200fe25Srmesta 		if (s_dns_qthr_created) {
769*8200fe25Srmesta 			/*
770*8200fe25Srmesta 			 * We may have lots of clients, so we don't
771*8200fe25Srmesta 			 * want to bog down the DNS server with tons
772*8200fe25Srmesta 			 * of requests... lest it becomes even more
773*8200fe25Srmesta 			 * unresponsive, so limit 1 thread to query
774*8200fe25Srmesta 			 * DNS at a time.
775*8200fe25Srmesta 			 */
776*8200fe25Srmesta #ifdef	DEBUG
777*8200fe25Srmesta 			syslog(LOG_ERR, "%s: query thread already active",
778*8200fe25Srmesta 			    whoami);
779*8200fe25Srmesta #endif
780*8200fe25Srmesta 			(void) rw_unlock(&s_dns_impl_lock);
781*8200fe25Srmesta 				return;
782*8200fe25Srmesta 		}
783*8200fe25Srmesta 
784*8200fe25Srmesta 		/*
785*8200fe25Srmesta 		 * DNS did not respond ! Set timeout and kick off
786*8200fe25Srmesta 		 * thread to try op again after s_dns_tout seconds.
787*8200fe25Srmesta 		 * We've made sure that we don't have an already
788*8200fe25Srmesta 		 * running thread above.
789*8200fe25Srmesta 		 */
790*8200fe25Srmesta 		s_dns_tout = NFSMAPID_DNS_TOUT_SECS;
791*8200fe25Srmesta 		err = thr_create(NULL, 0, resolv_query_thread, (void *)argp,
792*8200fe25Srmesta 		    thr_flags, &s_dns_qthread);
793*8200fe25Srmesta 		if (!err) {
794*8200fe25Srmesta 			s_dns_qthr_created = TRUE;
795*8200fe25Srmesta 		}
796*8200fe25Srmesta #ifdef DEBUG
797*8200fe25Srmesta 		else {
798*8200fe25Srmesta 			msg_done++;
799*8200fe25Srmesta 			if (!(msg_done % NFSMAPID_SLOG_RATE))
800*8200fe25Srmesta 				syslog(LOG_ERR, EMSG_DNS_THREAD_ERROR);
801*8200fe25Srmesta 		}
802*8200fe25Srmesta #endif
803*8200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
804*8200fe25Srmesta 		return;
805*8200fe25Srmesta 
806*8200fe25Srmesta 	case NO_RECOVERY:
807*8200fe25Srmesta #ifdef	DEBUG
808*8200fe25Srmesta 		syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami);
809*8200fe25Srmesta #endif
810*8200fe25Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
811*8200fe25Srmesta 		s_dns_disabled = TRUE;
812*8200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
813*8200fe25Srmesta 
814*8200fe25Srmesta 		/*FALLTHROUGH*/
815*8200fe25Srmesta 
816*8200fe25Srmesta 	default:
817*8200fe25Srmesta 		/*
818*8200fe25Srmesta 		 * For any other errors... DNS is responding, but
819*8200fe25Srmesta 		 * either it has no data, or some other problem is
820*8200fe25Srmesta 		 * occuring. At any rate, the TXT domain should not
821*8200fe25Srmesta 		 * be used, so we default to the DNS domain.
822*8200fe25Srmesta 		 */
823*8200fe25Srmesta 		break;
824*8200fe25Srmesta 	}
825*8200fe25Srmesta 
826*8200fe25Srmesta txtclear:
827*8200fe25Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
828*8200fe25Srmesta 	dns_txt_cached = 0;
829*8200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
830*8200fe25Srmesta 	resolv_txt_reset();
831*8200fe25Srmesta }
832*8200fe25Srmesta 
833*8200fe25Srmesta static int
834*8200fe25Srmesta get_mtime(const char *fname, timestruc_t *mtim)
835*8200fe25Srmesta {
836*8200fe25Srmesta 	struct stat st;
837*8200fe25Srmesta 	int err;
838*8200fe25Srmesta 
839*8200fe25Srmesta 	if ((err = stat(fname, &st)) != 0)
840*8200fe25Srmesta 		return (err);
841*8200fe25Srmesta 
842*8200fe25Srmesta 	*mtim = st.st_mtim;
843*8200fe25Srmesta 	return (0);
844*8200fe25Srmesta }
845*8200fe25Srmesta 
846*8200fe25Srmesta 
847*8200fe25Srmesta /*
848*8200fe25Srmesta  * trim_wspace is a destructive interface; it is up to
849*8200fe25Srmesta  * the caller to save off an original copy if needed.
850*8200fe25Srmesta  */
851*8200fe25Srmesta static char *
852*8200fe25Srmesta trim_wspace(char *dp)
853*8200fe25Srmesta {
854*8200fe25Srmesta 	char	*r;
855*8200fe25Srmesta 	char	*ndp;
856*8200fe25Srmesta 
857*8200fe25Srmesta 	/*
858*8200fe25Srmesta 	 * Any empty domain is not valid
859*8200fe25Srmesta 	 */
860*8200fe25Srmesta 	if (dp == NULL)
861*8200fe25Srmesta 		return (NULL);
862*8200fe25Srmesta 
863*8200fe25Srmesta 	/*
864*8200fe25Srmesta 	 * Skip leading blanks
865*8200fe25Srmesta 	 */
866*8200fe25Srmesta 	for (ndp = dp; *ndp != '\0'; ndp++) {
867*8200fe25Srmesta 		if (!isspace(*ndp))
868*8200fe25Srmesta 			break;
869*8200fe25Srmesta 	}
870*8200fe25Srmesta 
871*8200fe25Srmesta 	/*
872*8200fe25Srmesta 	 * If we reached the end of the string w/o
873*8200fe25Srmesta 	 * finding a non-blank char, return error
874*8200fe25Srmesta 	 */
875*8200fe25Srmesta 	if (*ndp == '\0')
876*8200fe25Srmesta 		return (NULL);
877*8200fe25Srmesta 
878*8200fe25Srmesta 	/*
879*8200fe25Srmesta 	 * Find next blank in string
880*8200fe25Srmesta 	 */
881*8200fe25Srmesta 	for (r = ndp; *r != '\0'; r++) {
882*8200fe25Srmesta 		if (isspace(*r))
883*8200fe25Srmesta 			break;
884*8200fe25Srmesta 	}
885*8200fe25Srmesta 
886*8200fe25Srmesta 	/*
887*8200fe25Srmesta 	 * No more blanks found, we are done
888*8200fe25Srmesta 	 */
889*8200fe25Srmesta 	if (*r == '\0')
890*8200fe25Srmesta 		return (ndp);
891*8200fe25Srmesta 
892*8200fe25Srmesta 	/*
893*8200fe25Srmesta 	 * Terminate string at blank
894*8200fe25Srmesta 	 */
895*8200fe25Srmesta 	*r++ = '\0';
896*8200fe25Srmesta 
897*8200fe25Srmesta 	/*
898*8200fe25Srmesta 	 * Skip any trailing spaces
899*8200fe25Srmesta 	 */
900*8200fe25Srmesta 	while (*r != '\0') {
901*8200fe25Srmesta 		/*
902*8200fe25Srmesta 		 * If a non-blank is found, it is an
903*8200fe25Srmesta 		 * illegal domain (embedded blanks).
904*8200fe25Srmesta 		 */
905*8200fe25Srmesta 		if (!isspace(*r))
906*8200fe25Srmesta 			return (NULL);
907*8200fe25Srmesta 		r++;
908*8200fe25Srmesta 	}
909*8200fe25Srmesta 	return (ndp);
910*8200fe25Srmesta }
911*8200fe25Srmesta 
912*8200fe25Srmesta static void
913*8200fe25Srmesta get_nfs_domain(void)
914*8200fe25Srmesta {
915*8200fe25Srmesta 	char		*ndomain;
916*8200fe25Srmesta 	timestruc_t	 ntime;
917*8200fe25Srmesta 
918*8200fe25Srmesta 	/*
919*8200fe25Srmesta 	 * If we can't get stats for the config file, then
920*8200fe25Srmesta 	 * zap the NFS domain info.  If mtime hasn't changed,
921*8200fe25Srmesta 	 * then there's no work to do, so just return.
922*8200fe25Srmesta 	 */
923*8200fe25Srmesta 	if (get_mtime(NFSADMIN, &ntime) != 0) {
924*8200fe25Srmesta 		ZAP_DOMAIN(nfs);
925*8200fe25Srmesta 		return;
926*8200fe25Srmesta 	}
927*8200fe25Srmesta 
928*8200fe25Srmesta 	if (TIMESTRUC_EQ(ntime, nfs_mtime))
929*8200fe25Srmesta 		return;
930*8200fe25Srmesta 
931*8200fe25Srmesta 	/*
932*8200fe25Srmesta 	 * Get NFSMAPID_DOMAIN value from /etc/default/nfs for now.
933*8200fe25Srmesta 	 * Note: defread() returns a ptr to TSD.
934*8200fe25Srmesta 	 */
935*8200fe25Srmesta 	if (defopen(NFSADMIN) == 0) {
936*8200fe25Srmesta 		char	*dp = NULL;
937*8200fe25Srmesta #ifdef	DEBUG
938*8200fe25Srmesta 		char	*whoami = "get_nfs_domain";
939*8200fe25Srmesta 		char	 orig[NS_MAXCDNAME] = {0};
940*8200fe25Srmesta #endif
941*8200fe25Srmesta 		ndomain = (char *)defread("NFSMAPID_DOMAIN=");
942*8200fe25Srmesta 		(void) defopen(NULL);
943*8200fe25Srmesta #ifdef	DEBUG
944*8200fe25Srmesta 		if (ndomain)
945*8200fe25Srmesta 			(void) strncpy(orig, ndomain, NS_MAXCDNAME);
946*8200fe25Srmesta #endif
947*8200fe25Srmesta 		/*
948*8200fe25Srmesta 		 * NFSMAPID_DOMAIN was set, so it's time for validation. If
949*8200fe25Srmesta 		 * it's okay, then update NFS domain and return. If not,
950*8200fe25Srmesta 		 * bail (syslog in DEBUG). We make nfsmapid more a bit
951*8200fe25Srmesta 		 * more forgiving of trailing and leading white space.
952*8200fe25Srmesta 		 */
953*8200fe25Srmesta 		if ((dp = trim_wspace(ndomain)) != NULL) {
954*8200fe25Srmesta 			if (mapid_stdchk_domain(dp) > 0) {
955*8200fe25Srmesta 				nfs_domain_len = strlen(dp);
956*8200fe25Srmesta 				(void) strncpy(nfs_domain, dp, NS_MAXCDNAME);
957*8200fe25Srmesta 				nfs_domain[NS_MAXCDNAME] = '\0';
958*8200fe25Srmesta 				nfs_mtime = ntime;
959*8200fe25Srmesta 				return;
960*8200fe25Srmesta 			}
961*8200fe25Srmesta 		}
962*8200fe25Srmesta #ifdef	DEBUG
963*8200fe25Srmesta 		if (orig[0] != '\0') {
964*8200fe25Srmesta 			syslog(LOG_ERR, gettext("%s: Invalid domain name \"%s\""
965*8200fe25Srmesta 				" found in configuration file."), whoami, orig);
966*8200fe25Srmesta 		}
967*8200fe25Srmesta #endif
968*8200fe25Srmesta 	}
969*8200fe25Srmesta 
970*8200fe25Srmesta 	/*
971*8200fe25Srmesta 	 * So the NFS config file changed but it couldn't be opened or
972*8200fe25Srmesta 	 * it didn't specify NFSMAPID_DOMAIN or it specified an invalid
973*8200fe25Srmesta 	 * NFSMAPID_DOMAIN.  Time to zap current NFS domain info.
974*8200fe25Srmesta 	 */
975*8200fe25Srmesta 	ZAP_DOMAIN(nfs);
976*8200fe25Srmesta }
977*8200fe25Srmesta 
978*8200fe25Srmesta static void
979*8200fe25Srmesta get_dns_domain(void)
980*8200fe25Srmesta {
981*8200fe25Srmesta 	timestruc_t	 ntime = {0};
982*8200fe25Srmesta 
983*8200fe25Srmesta 	/*
984*8200fe25Srmesta 	 * If we can't get stats for the config file, then
985*8200fe25Srmesta 	 * zap the DNS domain info.  If mtime hasn't changed,
986*8200fe25Srmesta 	 * then there's no work to do, so just return.
987*8200fe25Srmesta 	 */
988*8200fe25Srmesta 	errno = 0;
989*8200fe25Srmesta 	if (get_mtime(_PATH_RESCONF, &ntime) != 0) {
990*8200fe25Srmesta 		switch (errno) {
991*8200fe25Srmesta 			case ENOENT:
992*8200fe25Srmesta 				/*
993*8200fe25Srmesta 				 * The resolver defaults to obtaining the
994*8200fe25Srmesta 				 * domain off of the NIS domainname(1M) if
995*8200fe25Srmesta 				 * /etc/resolv.conf does not exist, so we
996*8200fe25Srmesta 				 * move forward.
997*8200fe25Srmesta 				 */
998*8200fe25Srmesta 				break;
999*8200fe25Srmesta 
1000*8200fe25Srmesta 			default:
1001*8200fe25Srmesta 				ZAP_DOMAIN(dns);
1002*8200fe25Srmesta 				return;
1003*8200fe25Srmesta 		}
1004*8200fe25Srmesta 	} else if (TIMESTRUC_EQ(ntime, dns_mtime))
1005*8200fe25Srmesta 		return;
1006*8200fe25Srmesta 
1007*8200fe25Srmesta 	/*
1008*8200fe25Srmesta 	 * Re-initialize resolver to zap DNS domain from previous
1009*8200fe25Srmesta 	 * resolv_init() calls.
1010*8200fe25Srmesta 	 */
1011*8200fe25Srmesta 	(void) resolv_init();
1012*8200fe25Srmesta 
1013*8200fe25Srmesta 	/*
1014*8200fe25Srmesta 	 * Update cached DNS domain.  No need for validation since
1015*8200fe25Srmesta 	 * domain comes from resolver.  If resolver doesn't return the
1016*8200fe25Srmesta 	 * domain, then zap the DNS domain.  This shouldn't ever happen,
1017*8200fe25Srmesta 	 * and if it does, the machine has bigger problems (so no need
1018*8200fe25Srmesta 	 * to generate a message that says DNS appears to be broken).
1019*8200fe25Srmesta 	 */
1020*8200fe25Srmesta 	(void) rw_rdlock(&s_dns_data_lock);
1021*8200fe25Srmesta 	if (sysdns_domain[0] != '\0') {
1022*8200fe25Srmesta 		(void) strncpy(dns_domain, sysdns_domain, NS_MAXCDNAME);
1023*8200fe25Srmesta 		dns_domain_len = strlen(sysdns_domain);
1024*8200fe25Srmesta 		(void) rw_unlock(&s_dns_data_lock);
1025*8200fe25Srmesta 		dns_mtime = ntime;
1026*8200fe25Srmesta 		return;
1027*8200fe25Srmesta 	}
1028*8200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
1029*8200fe25Srmesta 
1030*8200fe25Srmesta 	ZAP_DOMAIN(dns);
1031*8200fe25Srmesta }
1032*8200fe25Srmesta 
1033*8200fe25Srmesta /*
1034*8200fe25Srmesta  * PSARC 2005/487 Contracted Sun Private Interface
1035*8200fe25Srmesta  * mapid_stdchk_domain()
1036*8200fe25Srmesta  * Changes must be reviewed by Solaris File Sharing
1037*8200fe25Srmesta  * Changes must be communicated to contract-2005-487-01@sun.com
1038*8200fe25Srmesta  *
1039*8200fe25Srmesta  * Based on the recommendations from RFC1033 and RFC1035, check
1040*8200fe25Srmesta  * if a given domain name string is valid. Return values are:
1041*8200fe25Srmesta  *
1042*8200fe25Srmesta  *       1 = valid domain name
1043*8200fe25Srmesta  *       0 = invalid domain name (or invalid embedded character)
1044*8200fe25Srmesta  *      -1 = domain length > NS_MAXCDNAME
1045*8200fe25Srmesta  */
1046*8200fe25Srmesta int
1047*8200fe25Srmesta mapid_stdchk_domain(const char *ds)
1048*8200fe25Srmesta {
1049*8200fe25Srmesta 	int	i;
1050*8200fe25Srmesta 	size_t	len;
1051*8200fe25Srmesta 
1052*8200fe25Srmesta 	if (ds[0] == '\0')
1053*8200fe25Srmesta 		return (0);
1054*8200fe25Srmesta 	else
1055*8200fe25Srmesta 		len = strlen(ds) - 1;
1056*8200fe25Srmesta 
1057*8200fe25Srmesta 	/*
1058*8200fe25Srmesta 	 * 1st char _must_ be alphabetic char _AND_ last char _must_
1059*8200fe25Srmesta 	 * be alphanumeric. We check for other valid chars below.
1060*8200fe25Srmesta 	 */
1061*8200fe25Srmesta 	if (!isalpha(ds[0]) || !isalpha(ds[len]) && !isdigit(ds[len]))
1062*8200fe25Srmesta 		return (0);
1063*8200fe25Srmesta 
1064*8200fe25Srmesta 	for (i = 0; *ds && i <= NS_MAXCDNAME; i++, ds++) {
1065*8200fe25Srmesta 		if (!isalpha(*ds) && !isdigit(*ds) &&
1066*8200fe25Srmesta 		    (*ds != '.') && (*ds != '-') && (*ds != '_'))
1067*8200fe25Srmesta 			return (0);
1068*8200fe25Srmesta 	}
1069*8200fe25Srmesta 	return (i == (NS_MAXCDNAME + 1) ? -1 : 1);
1070*8200fe25Srmesta }
1071*8200fe25Srmesta 
1072*8200fe25Srmesta /*
1073*8200fe25Srmesta  * PSARC 2005/487 Consolidation Private
1074*8200fe25Srmesta  * mapid_reeval_domain()
1075*8200fe25Srmesta  * Changes must be reviewed by Solaris File Sharing
1076*8200fe25Srmesta  */
1077*8200fe25Srmesta void
1078*8200fe25Srmesta mapid_reeval_domain(cb_t *arg)
1079*8200fe25Srmesta {
1080*8200fe25Srmesta 	char	*domain = NULL;
1081*8200fe25Srmesta 
1082*8200fe25Srmesta 	get_nfs_domain();
1083*8200fe25Srmesta 	if (nfs_domain_len != 0) {
1084*8200fe25Srmesta 		domain = nfs_domain;
1085*8200fe25Srmesta 		goto dsync;
1086*8200fe25Srmesta 	}
1087*8200fe25Srmesta 
1088*8200fe25Srmesta 	get_dns_txt_domain(arg);
1089*8200fe25Srmesta 	if (dns_txt_domain_len != 0)
1090*8200fe25Srmesta 		domain = dns_txt_domain;
1091*8200fe25Srmesta 	else {
1092*8200fe25Srmesta 		/*
1093*8200fe25Srmesta 		 * We're either here because:
1094*8200fe25Srmesta 		 *
1095*8200fe25Srmesta 		 *  . NFSMAPID_DOMAIN was not set in /etc/default/nfs
1096*8200fe25Srmesta 		 *  . No suitable DNS TXT resource record exists
1097*8200fe25Srmesta 		 *  . DNS server is not responding to requests
1098*8200fe25Srmesta 		 *
1099*8200fe25Srmesta 		 * in either case, we want to default to using the
1100*8200fe25Srmesta 		 * system configured DNS domain. If this fails, then
1101*8200fe25Srmesta 		 * dns_domain will be empty and dns_domain_len will
1102*8200fe25Srmesta 		 * be 0.
1103*8200fe25Srmesta 		 */
1104*8200fe25Srmesta 		get_dns_domain();
1105*8200fe25Srmesta 		domain = dns_domain;
1106*8200fe25Srmesta 	}
1107*8200fe25Srmesta 
1108*8200fe25Srmesta dsync:
1109*8200fe25Srmesta 	domain_sync(arg, domain);
1110*8200fe25Srmesta }
1111*8200fe25Srmesta 
1112*8200fe25Srmesta /*
1113*8200fe25Srmesta  * PSARC 2005/487 Consolidation Private
1114*8200fe25Srmesta  * mapid_get_domain()
1115*8200fe25Srmesta  * Changes must be reviewed by Solaris File Sharing
1116*8200fe25Srmesta  *
1117*8200fe25Srmesta  * The use of TSD in mapid_get_domain() diverges slightly from the typical
1118*8200fe25Srmesta  * TSD use, since here, the benefit of doing TSD is mostly to allocate
1119*8200fe25Srmesta  * a per-thread buffer that will be utilized by other up-calls to the
1120*8200fe25Srmesta  * daemon.
1121*8200fe25Srmesta  *
1122*8200fe25Srmesta  * In doors, the thread used for the upcall never really exits, hence
1123*8200fe25Srmesta  * the typical destructor function defined via thr_keycreate() will
1124*8200fe25Srmesta  * never be called. Thus, we only use TSD to allocate the per-thread
1125*8200fe25Srmesta  * buffer and fill it up w/the configured 'mapid_domain' on each call.
1126*8200fe25Srmesta  * This still alleviates the problem of having the caller free any
1127*8200fe25Srmesta  * malloc'd space.
1128*8200fe25Srmesta  */
1129*8200fe25Srmesta char *
1130*8200fe25Srmesta mapid_get_domain(void)
1131*8200fe25Srmesta {
1132*8200fe25Srmesta 	void	*tsd = NULL;
1133*8200fe25Srmesta 
1134*8200fe25Srmesta 	(void) thr_getspecific(s_thr_key, &tsd);
1135*8200fe25Srmesta 	if (tsd == NULL) {
1136*8200fe25Srmesta 		tsd = malloc(NS_MAXCDNAME+1);
1137*8200fe25Srmesta 		if (tsd != NULL) {
1138*8200fe25Srmesta 			(void) rw_rdlock(&mapid_domain_lock);
1139*8200fe25Srmesta 			(void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME);
1140*8200fe25Srmesta 			(void) rw_unlock(&mapid_domain_lock);
1141*8200fe25Srmesta 			(void) thr_setspecific(s_thr_key, tsd);
1142*8200fe25Srmesta 		}
1143*8200fe25Srmesta 	} else {
1144*8200fe25Srmesta 		(void) rw_rdlock(&mapid_domain_lock);
1145*8200fe25Srmesta 		(void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME);
1146*8200fe25Srmesta 		(void) rw_unlock(&mapid_domain_lock);
1147*8200fe25Srmesta 	}
1148*8200fe25Srmesta 	return ((char *)tsd);
1149*8200fe25Srmesta }
1150*8200fe25Srmesta 
1151*8200fe25Srmesta /*
1152*8200fe25Srmesta  * PSARC 2005/487 Contracted Sun Private Interface
1153*8200fe25Srmesta  * mapid_derive_domain()
1154*8200fe25Srmesta  * Changes must be reviewed by Solaris File Sharing
1155*8200fe25Srmesta  * Changes must be communicated to contract-2005-487-01@sun.com
1156*8200fe25Srmesta  *
1157*8200fe25Srmesta  * This interface is called solely via sysidnfs4 iff no
1158*8200fe25Srmesta  * NFSMAPID_DOMAIN was found. So, there is no ill effect
1159*8200fe25Srmesta  * of having the reeval function call get_nfs_domain().
1160*8200fe25Srmesta  */
1161*8200fe25Srmesta char *
1162*8200fe25Srmesta mapid_derive_domain(void)
1163*8200fe25Srmesta {
1164*8200fe25Srmesta 	cb_t	cb = {0};
1165*8200fe25Srmesta 
1166*8200fe25Srmesta 	_lib_init();
1167*8200fe25Srmesta 	mapid_reeval_domain(&cb);
1168*8200fe25Srmesta 	return (mapid_get_domain());
1169*8200fe25Srmesta }
1170*8200fe25Srmesta 
1171*8200fe25Srmesta void
1172*8200fe25Srmesta _lib_init(void)
1173*8200fe25Srmesta {
1174*8200fe25Srmesta 	(void) resolv_init();
1175*8200fe25Srmesta 	(void) rwlock_init(&mapid_domain_lock, USYNC_THREAD, NULL);
1176*8200fe25Srmesta 	(void) thr_keycreate(&s_thr_key, NULL);
1177*8200fe25Srmesta 	lib_init_done++;
1178*8200fe25Srmesta }
1179