xref: /onnv-gate/usr/src/lib/libmapid/common/mapid.c (revision 7693:6fa3b1da138b)
11741Srmesta /*
21741Srmesta  * CDDL HEADER START
31741Srmesta  *
41741Srmesta  * The contents of this file are subject to the terms of the
51741Srmesta  * Common Development and Distribution License (the "License").
61741Srmesta  * You may not use this file except in compliance with the License.
71741Srmesta  *
81741Srmesta  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91741Srmesta  * or http://www.opensolaris.org/os/licensing.
101741Srmesta  * See the License for the specific language governing permissions
111741Srmesta  * and limitations under the License.
121741Srmesta  *
131741Srmesta  * When distributing Covered Code, include this CDDL HEADER in each
141741Srmesta  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151741Srmesta  * If applicable, add the following below this CDDL HEADER, with the
161741Srmesta  * fields enclosed by brackets "[]" replaced with your own identifying
171741Srmesta  * information: Portions Copyright [yyyy] [name of copyright owner]
181741Srmesta  *
191741Srmesta  * CDDL HEADER END
201741Srmesta  */
211741Srmesta /*
22*7693SVallish.Vaidyeshwara@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
231741Srmesta  * Use is subject to license terms.
241741Srmesta  */
251741Srmesta 
261741Srmesta /*
271741Srmesta  * PSARC/2004/154 nfsmapid DNS enhancements implementation.
281741Srmesta  *
291741Srmesta  * As per RFC 3530, file owner and group attributes in version 4 of the
301741Srmesta  * NFS protocol are no longer exchanged between client and server as 32
311741Srmesta  * bit integral values. Instead, owner and group file attributes are
321741Srmesta  * exchanged between client and server as UTF8 strings of form
331741Srmesta  *
341741Srmesta  *      'user@domain'		(ie. "joeblow@central.sun.com")
351741Srmesta  *      'group@domain'		(ie. "staff@central.sun.com")
361741Srmesta  *
371741Srmesta  * This NFSv4 feature is far beyond anything NFSv2/v3 ever provided, as
381741Srmesta  * being able to describe a user with a unique string identifier provides
391741Srmesta  * a much more powerful and administrative friendly way of dealing with
401741Srmesta  * overlaps in the uid/gid number spaces. That notwithstanding, dealing
411741Srmesta  * with issues of correctly mapping user and group ownership in a cross-
421741Srmesta  * domain environment has proven a difficult problem to solve, since
431741Srmesta  * dealing with different permutations of client naming configurations
441741Srmesta  * (ie. NIS only, LDAP only, etc.) have bloated the problem. Thus, users
451741Srmesta  * utilizing clients and servers that have the 'domain' portion of the
461741Srmesta  * UTF8 attribute string configured differently than its peer server and
471741Srmesta  * client accordingly, will experience watching their files owned by the
481741Srmesta  * 'nobody' user and group. This is due to the fact that the 'domain's
491741Srmesta  * don't match and the nfsmapid daemon treats the attribute strings as
501741Srmesta  * unknown user(s) or group(s) (even though the actual uid/gid's may exist
511741Srmesta  * in the executing daemon's system). Please refer to PSARC/2004/154 for
521741Srmesta  * further background and motivation for these enhancements.
531741Srmesta  *
541741Srmesta  * The latest implementation of the nfsmapid daemon relies on a DNS TXT
551741Srmesta  * record. The behavior of nfsmapid is to first use the NFSMAPID_DOMAIN
561741Srmesta  * configuration option in /etc/default/nfs. If the option has not been
571741Srmesta  * set, then the nfsmapid daemon queries the configured DNS domain server
581741Srmesta  * for the _nfsv4idmapdomain TXT record. If the record exists, then the
591741Srmesta  * record's value is used as the 'domain' portion of the UTF8 attribute
601741Srmesta  * strings. If the TXT record has not been configured in the DNS server,
611741Srmesta  * then the daemon falls back to using the DNS domain name itself as the
621741Srmesta  * 'domain' portion of the attribute strings. Lastly, if the configured
631741Srmesta  * DNS server is unresponsive, the nfsmapid daemon falls back to using
641741Srmesta  * the DNS domain name as the 'domain' portion of the attribute strings,
651741Srmesta  * and fires up a query thread to keep contacting the DNS server until
661741Srmesta  * it responds with either a TXT record, or a lack thereof, in which
671741Srmesta  * case, nfsmapid just continues to utilize the DNS domain name.
681741Srmesta  */
691741Srmesta #define	__LIBMAPID_IMPL
701741Srmesta #include <nfs/mapid.h>
711741Srmesta #pragma	init(_lib_init)
721741Srmesta 
731741Srmesta /*
741741Srmesta  * DEBUG Only
751741Srmesta  * Decode any resolver errors and print out message to log
761741Srmesta  */
771741Srmesta static int
781741Srmesta resolv_error(void)
791741Srmesta {
801741Srmesta #ifndef	DEBUG
811741Srmesta 
821741Srmesta 	return (h_errno);
831741Srmesta 
841741Srmesta #else	/* DEBUG */
851741Srmesta 
861741Srmesta 	static uint64_t	 msg_done[NS_ERRS] = {0};
871741Srmesta 
881741Srmesta 	switch (h_errno) {
891741Srmesta 	case NETDB_INTERNAL:
901741Srmesta 		syslog(LOG_ERR, EMSG_NETDB_INTERNAL, strerror(errno));
911741Srmesta 		break;
921741Srmesta 
931741Srmesta 	case HOST_NOT_FOUND:
941741Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
951741Srmesta 		msg_done[h_errno]++;
961741Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
971741Srmesta 			syslog(LOG_ERR, EMSG_HOST_NOT_FOUND, s_dname);
981741Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
991741Srmesta 		break;
1001741Srmesta 
1011741Srmesta 	case TRY_AGAIN:
1021741Srmesta 		/*
1031741Srmesta 		 * Nameserver is not responding.
1041741Srmesta 		 * Try again after a given timeout.
1051741Srmesta 		 */
1061741Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
1071741Srmesta 		msg_done[h_errno]++;
1081741Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
1091741Srmesta 			syslog(LOG_ERR, EMSG_TRY_AGAIN, s_dname);
1101741Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
1111741Srmesta 		break;
1121741Srmesta 
1131741Srmesta 	case NO_RECOVERY:
1141741Srmesta 		/*
1151741Srmesta 		 * This msg only really happens once, due
1161741Srmesta 		 * to s_dns_disabled flag (see below)
1171741Srmesta 		 */
1181741Srmesta 		syslog(LOG_ERR, EMSG_NO_RECOVERY, hstrerror(h_errno));
1191741Srmesta 		break;
1201741Srmesta 
1211741Srmesta 	case NO_DATA:
1221741Srmesta 		/*
1231741Srmesta 		 * No entries in the nameserver for
1241741Srmesta 		 * the specific record or record type.
1251741Srmesta 		 */
1261741Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
1271741Srmesta 		msg_done[h_errno]++;
1281741Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
1291741Srmesta 			syslog(LOG_ERR, EMSG_NO_DATA, NFSMAPID_DNS_RR, s_dname);
1301741Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
1311741Srmesta 		break;
1321741Srmesta 
1331741Srmesta 	case NETDB_SUCCESS:
1341741Srmesta 	default:
1351741Srmesta 		break;
1361741Srmesta 	}
1371741Srmesta 	return (h_errno);
1381741Srmesta 
1391741Srmesta #endif	/* DEBUG */
1401741Srmesta }
1411741Srmesta 
1421741Srmesta /*
1431741Srmesta  * Reset the global state variables used for the TXT record.
1441741Srmesta  * Having these values reset to zero helps nfsmapid confirm
1451741Srmesta  * that a valid DNS TXT record was not found; in which case,
1461741Srmesta  * it would fall back to using the configured DNS domain name.
1471741Srmesta  *
1481741Srmesta  * If a valid DNS TXT record _was_ found, but subsequent contact
1491741Srmesta  * to the DNS server is somehow hindered, the previous DNS TXT
1501741Srmesta  * RR value continues to be used. Thus, in such instances, we
1511741Srmesta  * forego clearing the global config variables so nfsmapid can
1521741Srmesta  * continue to use a valid DNS TXT RR while contact to the DNS
1531741Srmesta  * server is reestablished.
1541741Srmesta  */
1551741Srmesta static void
1561741Srmesta resolv_txt_reset(void)
1571741Srmesta {
1581741Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
1591741Srmesta 	bzero(s_txt_rr, sizeof (s_txt_rr));
1601741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
1611741Srmesta 
1621741Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
1631741Srmesta 	if (!dns_txt_cached) {
1641741Srmesta 		dns_txt_domain_len = 0;
1651741Srmesta 		bzero(dns_txt_domain, DNAMEMAX);
1661741Srmesta 	}
1671741Srmesta 	(void) rw_unlock(&s_dns_data_lock);
1681741Srmesta }
1691741Srmesta 
1701741Srmesta /*
1711741Srmesta  * Initialize resolver and populate &s_res struct
1721741Srmesta  *
1731741Srmesta  * DNS Domain is saved off sysdns_domain in case we
1741741Srmesta  * need to fall back to using the DNS domain name as
1751741Srmesta  * the v4 attribute string domain.
1761741Srmesta  */
1771741Srmesta static int
1781741Srmesta resolv_init(void)
1791741Srmesta {
1801741Srmesta 	size_t			len;
1811741Srmesta 	int			n;
1821741Srmesta 	struct __res_state	res;
1831741Srmesta 
1841741Srmesta 	(void) mutex_lock(&s_res_lock);
1851741Srmesta 	bzero(&s_res, sizeof (struct __res_state));
1861741Srmesta 	n = h_errno = errno = 0;
1871741Srmesta 	if ((n = res_ninit(&s_res)) < 0) {
1881741Srmesta 		(void) mutex_unlock(&s_res_lock);
1891741Srmesta 		(void) resolv_error();
1901741Srmesta 		return (n);
1911741Srmesta 	}
1921741Srmesta 	res = s_res;
1931741Srmesta 	(void) mutex_unlock(&s_res_lock);
1941741Srmesta 
1951741Srmesta 	len = strlen(res.defdname) + 1;
1961741Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
1971741Srmesta 	bzero(s_dname, sizeof (s_dname));
1981741Srmesta 	(void) snprintf(s_dname, len, "%s", res.defdname);
1991741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
2001741Srmesta 
2011741Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
2021741Srmesta 	(void) snprintf(sysdns_domain, len, "%s", res.defdname);
2031741Srmesta 	(void) rw_unlock(&s_dns_data_lock);
2041741Srmesta 
2051741Srmesta 	return (0);
2061741Srmesta }
2071741Srmesta 
2081741Srmesta /*
2091741Srmesta  * Search criteria assumptions:
2101741Srmesta  *
2111741Srmesta  * The onus will fall on the sysadmins to correctly configure the TXT
2121741Srmesta  * record in the DNS domain where the box currently resides in order
2131741Srmesta  * for the record to be found. However, if they sysadmin chooses to
2141741Srmesta  * add the 'search' key to /etc/resolv.conf, then resolv_search()
2151741Srmesta  * _will_ traverse up the DNS tree as specified in the 'search' key.
2161741Srmesta  * Otherwise, we'll default the domain to the DNS domain itself.
2171741Srmesta  */
2181741Srmesta static int
2191741Srmesta resolv_search(void)
2201741Srmesta {
2211741Srmesta 	int			len;
2221741Srmesta 	ans_t			ans = {0};
2231741Srmesta 	struct __res_state	res;
2241741Srmesta 	int			type = T_TXT;
2251741Srmesta 	int			class = C_IN;
2261741Srmesta 
2271741Srmesta 	(void) mutex_lock(&s_res_lock);
2281741Srmesta 	res = s_res;
2291741Srmesta 	(void) mutex_unlock(&s_res_lock);
2301741Srmesta 
2311741Srmesta 	/*
2321741Srmesta 	 * Avoid holding locks across the res_nsearch() call to
2331741Srmesta 	 * prevent stalling threads during network partitions.
2341741Srmesta 	 */
2351741Srmesta 	len = h_errno = errno = 0;
2361741Srmesta 	if ((len = res_nsearch(&res, NFSMAPID_DNS_RR, class, type,
2371741Srmesta 	    ans.buf, sizeof (ans))) < 0)
2381741Srmesta 		return (resolv_error());
2391741Srmesta 
2401741Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
2411741Srmesta 	s_ans = ans;
2421741Srmesta 	s_anslen = len;
2431741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
2441741Srmesta 
2451741Srmesta 	return (NETDB_SUCCESS);
2461741Srmesta }
2471741Srmesta 
2481741Srmesta /*
249*7693SVallish.Vaidyeshwara@Sun.COM  * Free all resolver state information stored in s_res
250*7693SVallish.Vaidyeshwara@Sun.COM  */
251*7693SVallish.Vaidyeshwara@Sun.COM static void
252*7693SVallish.Vaidyeshwara@Sun.COM resolv_destroy(void)
253*7693SVallish.Vaidyeshwara@Sun.COM {
254*7693SVallish.Vaidyeshwara@Sun.COM 	(void) mutex_lock(&s_res_lock);
255*7693SVallish.Vaidyeshwara@Sun.COM 	res_ndestroy(&s_res);
256*7693SVallish.Vaidyeshwara@Sun.COM 	(void) mutex_unlock(&s_res_lock);
257*7693SVallish.Vaidyeshwara@Sun.COM }
258*7693SVallish.Vaidyeshwara@Sun.COM 
259*7693SVallish.Vaidyeshwara@Sun.COM /*
2601741Srmesta  * Skip one DNS record
2611741Srmesta  */
2621741Srmesta static uchar_t  *
2631741Srmesta resolv_skip_rr(uchar_t *p, uchar_t *eom)
2641741Srmesta {
2651741Srmesta 	int	t;
2661741Srmesta 	int	dlen;
2671741Srmesta 
2681741Srmesta 	/*
2691741Srmesta 	 * Skip compressed name
2701741Srmesta 	 */
2711741Srmesta 	errno = 0;
2721741Srmesta 	if ((t = dn_skipname(p, eom)) < 0) {
2731741Srmesta #ifdef	DEBUG
2741741Srmesta 		syslog(LOG_ERR, "%s", strerror(errno));
2751741Srmesta #endif
2761741Srmesta 		return (NULL);
2771741Srmesta 	}
2781741Srmesta 
2791741Srmesta 	/*
2801741Srmesta 	 * Advance pointer and make sure
2811741Srmesta 	 * we're still within the message
2821741Srmesta 	 */
2831741Srmesta 	p += t;
2841741Srmesta 	if ((p + RRFIXEDSZ) > eom)
2851741Srmesta 		return (NULL);
2861741Srmesta 
2871741Srmesta 	/*
2881741Srmesta 	 * Now, just skip over the rr fields
2891741Srmesta 	 */
2901741Srmesta 	p += INT16SZ;	/* type */
2911741Srmesta 	p += INT16SZ;	/* class */
2921741Srmesta 	p += INT32SZ;	/* ttl */
2931741Srmesta 	dlen = ns_get16(p);
2941741Srmesta 	p += INT16SZ;
2951741Srmesta 	p += dlen;	/* dlen */
2961741Srmesta 	if (p > eom)
2971741Srmesta 		return (NULL);
2981741Srmesta 
2991741Srmesta 	return (p);
3001741Srmesta }
3011741Srmesta 
3021741Srmesta /*
3031741Srmesta  * Process one TXT record.
3041741Srmesta  *
3051741Srmesta  * nfsmapid queries the DNS server for the specific _nfsv4idmapdomain
3061741Srmesta  * TXT record. Thus, if the TXT record exists, the answer section of
3071741Srmesta  * the DNS response carries the TXT record's value. Thus, we check that
3081741Srmesta  * the value is indeed a valid domain and set the modular s_txt_rr
3091741Srmesta  * global to the domain value.
3101741Srmesta  */
3111741Srmesta static void
3121741Srmesta resolve_process_txt(uchar_t *p, int dlen)
3131741Srmesta {
3141741Srmesta 	char		*rr_base = (char *)(p + 1);
3151741Srmesta 	char		*rr_end = (char *)(p + dlen);
3161741Srmesta 	size_t		 len = rr_end - rr_base;
3171741Srmesta #ifdef	DEBUG
3181741Srmesta 	static uint64_t	 msg_done = 0;
3191741Srmesta #endif
3201741Srmesta 	char		 tmp_txt_rr[DNAMEMAX];
3211741Srmesta 
3221741Srmesta 	if (len >= DNAMEMAX)
3231741Srmesta 		return;		/* process next TXT RR */
3241741Srmesta 
3251741Srmesta 	/*
3261741Srmesta 	 * make sure we have a clean buf since
3271741Srmesta 	 * we may've processed several TXT rr's
3281741Srmesta 	 */
3291741Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
3301741Srmesta 	bzero(s_txt_rr, sizeof (s_txt_rr));
3311741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
3321741Srmesta 
3331741Srmesta 	(void) strncpy(tmp_txt_rr, rr_base, len);
3341741Srmesta 	tmp_txt_rr[len] = '\0';
3351741Srmesta 
3361741Srmesta 	/*
3371741Srmesta 	 * If there is a record and it's a valid domain, we're done.
3381741Srmesta 	 */
3391741Srmesta 	if (rr_base[0] != '\0' && mapid_stdchk_domain(tmp_txt_rr) > 0) {
3401741Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
3411741Srmesta 		(void) strncpy(s_txt_rr, rr_base, len);
3421741Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
3431741Srmesta #ifdef	DEBUG
3441741Srmesta 		syslog(LOG_ERR, "TXT (Rec):\t%s", s_txt_rr);
3451741Srmesta 
3461741Srmesta 	} else if (!(msg_done++ % NFSMAPID_SLOG_RATE)) {
3471741Srmesta 		/*
3481741Srmesta 		 * Otherwise, log the error
3491741Srmesta 		 */
3501741Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
3511741Srmesta 		syslog(LOG_ERR, EMSG_DNS_RR_INVAL, NFSMAPID_DNS_RR, s_dname);
3521741Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
3531741Srmesta #endif
3541741Srmesta 	}
3551741Srmesta }
3561741Srmesta 
3571741Srmesta /*
3581741Srmesta  * Decode any answer received from the DNS server. This interface is
3591741Srmesta  * capable of much more than just decoding TXT records. We maintain
3601741Srmesta  * focus on TXT rr's for now, but this will probably change once we
3611741Srmesta  * get the IETF approved application specific DNS RR.
3621741Srmesta  *
3631741Srmesta  * Here's an example of the TXT record we're decoding (as would appear
3641741Srmesta  * in the DNS zone file):
3651741Srmesta  *
3661741Srmesta  *            _nfsv4idmapdomain    IN    TXT    "sun.com"
3671741Srmesta  *
3681741Srmesta  * Once the IETF application specific DNS RR is granted, we should only
3691741Srmesta  * be changing the record flavor, but all should pretty much stay the
3701741Srmesta  * same.
3711741Srmesta  */
3721741Srmesta static void
3731741Srmesta resolv_decode(void)
3741741Srmesta {
3751741Srmesta 	uchar_t		*buf;
3761741Srmesta 	HEADER		*hp;
3771741Srmesta 	uchar_t		 name[DNAMEMAX];
3781741Srmesta 	uchar_t		*eom;
3791741Srmesta 	uchar_t		*p;
3801741Srmesta 	int		 n;
3811741Srmesta 	uint_t		 qd_cnt;
3821741Srmesta 	uint_t		 an_cnt;
3831741Srmesta 	uint_t		 ns_cnt;
3841741Srmesta 	uint_t		 ar_cnt;
3851741Srmesta 	uint_t		 cnt;
3861741Srmesta 	uint_t		 type;
3871741Srmesta 	int		 dlen;
3881741Srmesta 	ans_t		 answer = {0};
3891741Srmesta 	int		 answer_len = 0;
3901741Srmesta 
3911741Srmesta 	/*
3921741Srmesta 	 * Check the HEADER for any signs of errors
3931741Srmesta 	 * and extract the answer counts for later.
3941741Srmesta 	 */
3951741Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
3961741Srmesta 	answer = s_ans;
3971741Srmesta 	answer_len = s_anslen;
3981741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
3991741Srmesta 
4001741Srmesta 	buf = (uchar_t *)&answer.buf;
4011741Srmesta 	hp = (HEADER *)&answer.hdr;
4021741Srmesta 	eom = (uchar_t *)(buf + answer_len);
4031741Srmesta 	if (hp->rcode !=  NOERROR) {
4041741Srmesta #ifdef	DEBUG
4051741Srmesta 		syslog(LOG_ERR, "errno: %s", strerror(errno));
4061741Srmesta 		syslog(LOG_ERR, "h_errno: %s", hstrerror(h_errno));
4071741Srmesta #endif
4081741Srmesta 		return;
4091741Srmesta 	}
4101741Srmesta 	qd_cnt = ntohs(hp->qdcount);
4111741Srmesta 	an_cnt = ntohs(hp->ancount);
4121741Srmesta 	ns_cnt = ntohs(hp->nscount);
4131741Srmesta 	ar_cnt = ntohs(hp->arcount);
4141741Srmesta 
4151741Srmesta 	/*
4161741Srmesta 	 * skip query entries
4171741Srmesta 	 */
4181741Srmesta 	p = (uchar_t *)(buf + HFIXEDSZ);
4191741Srmesta 	errno = 0;
4201741Srmesta 	while (qd_cnt-- > 0) {
4211741Srmesta 		n = dn_skipname(p, eom);
4221741Srmesta 		if (n < 0) {
4231741Srmesta #ifdef	DEBUG
4241741Srmesta 			syslog(LOG_ERR, "%s", strerror(errno));
4251741Srmesta #endif
4261741Srmesta 			return;
4271741Srmesta 		}
4281741Srmesta 		p += n;
4291741Srmesta 		p += INT16SZ;	/* type */
4301741Srmesta 		p += INT16SZ;	/* class */
4311741Srmesta 	}
4321741Srmesta 
4331741Srmesta #ifdef	DEBUG
4341741Srmesta 	/*
4351741Srmesta 	 * If debugging... print query only once.
4361741Srmesta 	 * NOTE: Don't advance pointer... this is done
4371741Srmesta 	 *	 in while() loop on a per record basis !
4381741Srmesta 	 */
4391741Srmesta 	n = h_errno = errno = 0;
4401741Srmesta 	n = dn_expand(buf, eom, p, (char *)name, sizeof (name));
4411741Srmesta 	if (n < 0) {
4421741Srmesta 		(void) resolv_error();
4431741Srmesta 		return;
4441741Srmesta 	}
4451741Srmesta 	syslog(LOG_ERR, "Query:\t\t%-30s", name);
4461741Srmesta #endif
4471741Srmesta 
4481741Srmesta 	/*
4491741Srmesta 	 * Process actual answer(s).
4501741Srmesta 	 */
4511741Srmesta 	cnt = an_cnt;
4521741Srmesta 	while (cnt-- > 0 && p < eom) {
4531741Srmesta 		/* skip the name field */
4541741Srmesta 		n = dn_expand(buf, eom, p, (char *)name, sizeof (name));
4551741Srmesta 		if (n < 0) {
4561741Srmesta 			(void) resolv_error();
4571741Srmesta 			return;
4581741Srmesta 		}
4591741Srmesta 		p += n;
4601741Srmesta 
4611741Srmesta 		if ((p + 3 * INT16SZ + INT32SZ) > eom)
4621741Srmesta 			return;
4631741Srmesta 
4641741Srmesta 		type = ns_get16(p);
4651741Srmesta 		p += INT16SZ;
4661741Srmesta 		p += INT16SZ + INT32SZ;	/* skip class & ttl */
4671741Srmesta 		dlen = ns_get16(p);
4681741Srmesta 		p += INT16SZ;
4691741Srmesta 
4701741Srmesta 		if ((p + dlen) > eom)
4711741Srmesta 			return;
4721741Srmesta 
4731741Srmesta 		switch (type) {
4741741Srmesta 			case T_TXT:
4751741Srmesta 				resolve_process_txt(p, dlen);
4761741Srmesta 				break;
4771741Srmesta 
4781741Srmesta 			default:
4791741Srmesta 				/*
4801741Srmesta 				 * Advance to next answer record for any
4811741Srmesta 				 * other record types. Again, this will
4821741Srmesta 				 * probably change (see block comment).
4831741Srmesta 				 */
4841741Srmesta 				p += dlen;
4851741Srmesta 				break;
4861741Srmesta 		}
4871741Srmesta 	}
4881741Srmesta 
4891741Srmesta 	/*
4901741Srmesta 	 * Skip name server and additional records for now.
4911741Srmesta 	 */
4921741Srmesta 	cnt = ns_cnt + ar_cnt;
4931741Srmesta 	if (cnt > 0) {
4941741Srmesta 		while (--cnt != 0 && p < eom) {
4951741Srmesta 			p = resolv_skip_rr(p, eom);
4961741Srmesta 			if (p == NULL)
4971741Srmesta 				return;
4981741Srmesta 		}
4991741Srmesta 	}
5001741Srmesta }
5011741Srmesta 
5021741Srmesta /*
5031741Srmesta  * If a valid TXT record entry exists, s_txt_rr contains the domain
5041741Srmesta  * value (as set in resolv_process_txt) and we extract the value into
5051741Srmesta  * dns_txt_domain (the exported global). If there was _no_ valid TXT
5061741Srmesta  * entry, we simply return and check_domain() will default to the
5071741Srmesta  * DNS domain since we did resolv_txt_reset() first.
5081741Srmesta  */
5091741Srmesta static void
5101741Srmesta resolv_get_txt_data()
5111741Srmesta {
5121741Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
5131741Srmesta 	if (s_txt_rr[0] != '\0') {
5141741Srmesta 		(void) rw_wrlock(&s_dns_data_lock);
5151741Srmesta 		(void) snprintf(dns_txt_domain, strlen(s_txt_rr) + 1, "%s",
516*7693SVallish.Vaidyeshwara@Sun.COM 		    s_txt_rr);
5171741Srmesta 		dns_txt_domain_len = strlen(dns_txt_domain);
5181741Srmesta 		dns_txt_cached = 1;
5191741Srmesta 		(void) rw_unlock(&s_dns_data_lock);
5201741Srmesta 	}
5211741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
5221741Srmesta }
5231741Srmesta 
5241741Srmesta static void
5251741Srmesta domain_sync(cb_t *argp, char *dname)
5261741Srmesta {
5271741Srmesta 	int	dlen = 0;
5281741Srmesta 	void	*(*fcn)(void *) = NULL;
5291741Srmesta 	int	sighup = 0;
5301741Srmesta 	int	domchg = 0;
5311741Srmesta 
5321741Srmesta 	/*
5331741Srmesta 	 * Make sure values passed are sane and initialize accordingly.
5341741Srmesta 	 */
5351741Srmesta 	if (dname != NULL)
5361741Srmesta 		dlen = strlen(dname);
5371741Srmesta 	if (argp) {
5381741Srmesta 		if (argp->fcn)
5391741Srmesta 			fcn = argp->fcn;
5401741Srmesta 		if (argp->signal)
5411741Srmesta 			sighup = argp->signal;
5421741Srmesta 	}
5431741Srmesta 
5441741Srmesta 	/*
5451741Srmesta 	 * Update the library's mapid_domain variable if 'dname' is different.
5461741Srmesta 	 */
5471741Srmesta 	if (dlen != 0 && strncasecmp(dname, mapid_domain, NS_MAXCDNAME)) {
5481741Srmesta 		(void) rw_wrlock(&mapid_domain_lock);
5491741Srmesta 		(void) strncpy(mapid_domain, dname, NS_MAXCDNAME);
5501741Srmesta 		mapid_domain_len = dlen;
5511741Srmesta 		(void) rw_unlock(&mapid_domain_lock);
5521741Srmesta 		domchg++;
5531741Srmesta 	}
5541741Srmesta 
5551741Srmesta 	/*
5561741Srmesta 	 * If the caller gave us a valid callback routine, we
5571741Srmesta 	 * instantiate it to announce the domain change, but
5581741Srmesta 	 * only if either the domain changed _or_ the caller
5591741Srmesta 	 * was issued a SIGHUP.
5601741Srmesta 	 */
5611741Srmesta 	if (fcn != NULL && (sighup || domchg))
5621741Srmesta 		(void) fcn((void *)mapid_domain);
5631741Srmesta }
5641741Srmesta 
5651741Srmesta /*
5661741Srmesta  * Thread to keep pinging  DNS  server for  TXT  record if nfsmapid's
5671741Srmesta  * initial attempt at contact with server failed. We could potentially
5681741Srmesta  * have a substantial number of NFSv4 clients and having all of them
5691741Srmesta  * hammering on an already unresponsive DNS server would not help
5701741Srmesta  * things. So, we limit the number of live query threads to at most
5711741Srmesta  * 1 at any one time to keep things from getting out of hand.
5721741Srmesta  */
5731741Srmesta /* ARGSUSED */
5741741Srmesta static void *
5751741Srmesta resolv_query_thread(void *arg)
5761741Srmesta {
5771741Srmesta 	unsigned int	 nap_time;
5781741Srmesta 
5791741Srmesta #ifdef	DEBUG
5801741Srmesta 	char		*whoami = "query_thread";
5811741Srmesta 
5821741Srmesta 	syslog(LOG_ERR, "%s active !", whoami);
5831741Srmesta #endif
5841741Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
5851741Srmesta 	nap_time = s_dns_tout;
5861741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
5871741Srmesta 
5881741Srmesta 	for (;;) {
5891741Srmesta 		(void) sleep(nap_time);
5901741Srmesta 
5911741Srmesta 		resolv_txt_reset();
5921741Srmesta 		(void) resolv_init();
5931741Srmesta 		switch (resolv_search()) {
5941741Srmesta 		case NETDB_SUCCESS:
5951741Srmesta 			resolv_decode();
5961741Srmesta 			resolv_get_txt_data();
5971741Srmesta 
5981741Srmesta 			/*
5991741Srmesta 			 * This is a bit different than what we
6001741Srmesta 			 * do in get_dns_txt_domain(), where we
6011741Srmesta 			 * simply return and let the caller
6021741Srmesta 			 * access dns_txt_domain directly.
6031741Srmesta 			 *
6041741Srmesta 			 * Here we invoke the callback routine
6051741Srmesta 			 * provided by the caller to the
6061741Srmesta 			 * mapid_reeval_domain() interface via
6071741Srmesta 			 * the cb_t's fcn param.
6081741Srmesta 			 */
6091741Srmesta 			domain_sync((cb_t *)arg, dns_txt_domain);
6101741Srmesta 			goto thr_okay;
6111741Srmesta 
6121741Srmesta 		case NO_DATA:
6131741Srmesta 			/*
6141741Srmesta 			 * DNS is up now, but does not have
6151741Srmesta 			 * the NFSV4IDMAPDOMAIN TXT record.
6161741Srmesta 			 */
6171741Srmesta #ifdef	DEBUG
6181741Srmesta 			syslog(LOG_ERR, "%s: DNS has no TXT Record", whoami);
6191741Srmesta #endif
6201741Srmesta 			goto thr_reset;
6211741Srmesta 
6221741Srmesta 		case NO_RECOVERY:
6231741Srmesta 			/*
6241741Srmesta 			 * Non-Recoverable error occurred. No sense
6251741Srmesta 			 * in keep pinging the DNS server at this
6261741Srmesta 			 * point, so we disable any further contact.
6271741Srmesta 			 */
6281741Srmesta #ifdef	DEBUG
6291741Srmesta 			syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami);
6301741Srmesta #endif
6311741Srmesta 			(void) rw_wrlock(&s_dns_impl_lock);
6321741Srmesta 			s_dns_disabled = TRUE;
6331741Srmesta 			(void) rw_unlock(&s_dns_impl_lock);
6341741Srmesta 			goto thr_reset;
6351741Srmesta 
6361741Srmesta 		case HOST_NOT_FOUND:
6371741Srmesta 			/*
6381741Srmesta 			 * Authoritative NS not responding...
6391741Srmesta 			 * keep trying for non-authoritative reply
6401741Srmesta 			 */
6411741Srmesta 			/*FALLTHROUGH*/
6421741Srmesta 
6431741Srmesta 		case TRY_AGAIN:
6441741Srmesta 			/* keep trying */
6451741Srmesta #ifdef	DEBUG
6461741Srmesta 			syslog(LOG_ERR, "%s: retrying...", whoami);
6471741Srmesta #endif
6481741Srmesta 			break;
6491741Srmesta 
6501741Srmesta 		case NETDB_INTERNAL:
6511741Srmesta 		default:
6521741Srmesta #ifdef	DEBUG
6531741Srmesta 			syslog(LOG_ERR, "%s: Internal resolver error: %s",
6541741Srmesta 			    whoami, strerror(errno));
6551741Srmesta #endif
6561741Srmesta 			goto thr_reset;
6571741Srmesta 		}
658*7693SVallish.Vaidyeshwara@Sun.COM 
659*7693SVallish.Vaidyeshwara@Sun.COM 		resolv_destroy();
6601741Srmesta 	}
6611741Srmesta thr_reset:
6621741Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
6631741Srmesta 	dns_txt_cached = 0;
6641741Srmesta 	(void) rw_unlock(&s_dns_data_lock);
6651741Srmesta 	resolv_txt_reset();
6661741Srmesta 
6671741Srmesta thr_okay:
668*7693SVallish.Vaidyeshwara@Sun.COM 	resolv_destroy();
6691741Srmesta 	/* mark thread as done */
6701741Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
6711741Srmesta 	s_dns_qthr_created = FALSE;
6721741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
6731741Srmesta 
6741741Srmesta 	(void) thr_exit(NULL);
6751741Srmesta 	/*NOTREACHED*/
6761741Srmesta 	return (NULL);
6771741Srmesta }
6781741Srmesta 
6791741Srmesta /*
6801741Srmesta  * nfsmapid's interface into the resolver for getting the TXT record.
6811741Srmesta  *
6821741Srmesta  * Key concepts:
6831741Srmesta  *
6841741Srmesta  * o If the DNS server is available and the TXT record is found, we
6851741Srmesta  *   simply decode the output and fill the exported dns_txt_domain
6861741Srmesta  *   global, so our caller can configure the daemon appropriately.
6871741Srmesta  *
6881741Srmesta  * o If the TXT record is not found, then having done resolv_txt_reset()
6891741Srmesta  *   first will allow our caller to recognize that the exported globals
6901741Srmesta  *   are empty and thus configure nfsmapid to use the default DNS domain.
6911741Srmesta  *
6921741Srmesta  * o Having no /etc/resolv.conf file is pretty much a show stopper, since
6931741Srmesta  *   there is no name server address information. We return since we've
6941741Srmesta  *   already have reset the TXT global state.
6951741Srmesta  *
6961741Srmesta  * o If a previous call to the DNS server resulted in an unrecoverable
6971741Srmesta  *   error, then we disable further contact to the DNS server and return.
6981741Srmesta  *   Having the TXT global state already reset guarantees that our caller
6991741Srmesta  *   will fall back to the right configuration.
7001741Srmesta  *
7011741Srmesta  * o Query thread creation is throttled by s_dns_qthr_created. We mitigate
7021741Srmesta  *   the problem of an already unresponsive DNS server by allowing at most
7031741Srmesta  *   1 outstanding query thread since we could potentially have a substantial
7041741Srmesta  *   amount of clients hammering on the same DNS server attempting to get
7051741Srmesta  *   the TXT record.
7061741Srmesta  */
7071741Srmesta static void
7081741Srmesta get_dns_txt_domain(cb_t *argp)
7091741Srmesta {
7101741Srmesta 	int		err;
7111741Srmesta #ifdef	DEBUG
7121741Srmesta 	static uint64_t	msg_done = 0;
7131741Srmesta 	char		*whoami = "get_dns_txt_domain";
7141741Srmesta #endif
7151741Srmesta 	long		thr_flags = THR_DETACHED;
7161741Srmesta 	struct stat	st;
7171741Srmesta 
7181741Srmesta 	/*
7191741Srmesta 	 * We reset TXT variables first in case /etc/resolv.conf
7201741Srmesta 	 * is missing or we've had unrecoverable resolver errors,
7211741Srmesta 	 * we'll default to get_dns_domain(). If a previous DNS
7221741Srmesta 	 * TXT RR was found, don't clear it until we're certain
7231741Srmesta 	 * that contact can be made to the DNS server (see block
7241741Srmesta 	 * comment atop resolv_txt_reset). If we're responding to
7251741Srmesta 	 * a SIGHUP signal, force a reset of the cached copy.
7261741Srmesta 	 */
7271741Srmesta 	if (argp && argp->signal) {
7281741Srmesta 		(void) rw_wrlock(&s_dns_data_lock);
7291741Srmesta 		dns_txt_cached = 0;
7301741Srmesta 		(void) rw_unlock(&s_dns_data_lock);
7311741Srmesta 	}
7321741Srmesta 	resolv_txt_reset();
7331741Srmesta 
7341741Srmesta 	errno = 0;
7351741Srmesta 	if (stat(_PATH_RESCONF, &st) < 0 && errno == ENOENT) {
7361741Srmesta 		/*
7371741Srmesta 		 * If /etc/resolv.conf is not there, then we'll
7381741Srmesta 		 * get the domain from domainname(1M). No real
7391741Srmesta 		 * reason to query DNS or fire a thread since we
7401741Srmesta 		 * have no nameserver addresses.
7411741Srmesta 		 */
742*7693SVallish.Vaidyeshwara@Sun.COM 		(void) rw_wrlock(&s_dns_data_lock);
743*7693SVallish.Vaidyeshwara@Sun.COM 		dns_txt_cached = 0;
744*7693SVallish.Vaidyeshwara@Sun.COM 		(void) rw_unlock(&s_dns_data_lock);
745*7693SVallish.Vaidyeshwara@Sun.COM 		resolv_txt_reset();
746*7693SVallish.Vaidyeshwara@Sun.COM 		return;
7471741Srmesta 	}
7481741Srmesta 
7491741Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
7501741Srmesta 	if (s_dns_disabled) {
7511741Srmesta 		/*
7521741Srmesta 		 * If there were non-recoverable problems with DNS,
7531741Srmesta 		 * we have stopped querying DNS entirely. See
7541741Srmesta 		 * NO_RECOVERY clause below.
7551741Srmesta 		 */
7561741Srmesta #ifdef	DEBUG
7571741Srmesta 		syslog(LOG_ERR, "%s: DNS queries disabled", whoami);
7581741Srmesta #endif
7591741Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
7601741Srmesta 		return;
7611741Srmesta 	}
7621741Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
7631741Srmesta 
7641741Srmesta 	(void) resolv_init();
7651741Srmesta 	switch (resolv_search()) {
7661741Srmesta 	case NETDB_SUCCESS:
7671741Srmesta 		/*
7681741Srmesta 		 * If there _is_ a TXT record, we let
7691741Srmesta 		 * our caller set the global state.
7701741Srmesta 		 */
7711741Srmesta 		resolv_decode();
7721741Srmesta 		resolv_get_txt_data();
773*7693SVallish.Vaidyeshwara@Sun.COM 		break;
7741741Srmesta 
7751741Srmesta 	case TRY_AGAIN:
7761741Srmesta 		if (argp == NULL || argp->fcn == NULL)
7771741Srmesta 			/*
7781741Srmesta 			 * If no valid argument was passed or
7791741Srmesta 			 * callback defined, don't fire thread
7801741Srmesta 			 */
781*7693SVallish.Vaidyeshwara@Sun.COM 			break;
7821741Srmesta 
7831741Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
7841741Srmesta 		if (s_dns_qthr_created) {
7851741Srmesta 			/*
7861741Srmesta 			 * We may have lots of clients, so we don't
7871741Srmesta 			 * want to bog down the DNS server with tons
7881741Srmesta 			 * of requests... lest it becomes even more
7891741Srmesta 			 * unresponsive, so limit 1 thread to query
7901741Srmesta 			 * DNS at a time.
7911741Srmesta 			 */
7921741Srmesta #ifdef	DEBUG
7931741Srmesta 			syslog(LOG_ERR, "%s: query thread already active",
7941741Srmesta 			    whoami);
7951741Srmesta #endif
7961741Srmesta 			(void) rw_unlock(&s_dns_impl_lock);
797*7693SVallish.Vaidyeshwara@Sun.COM 			break;
7981741Srmesta 		}
7991741Srmesta 
8001741Srmesta 		/*
8011741Srmesta 		 * DNS did not respond ! Set timeout and kick off
8021741Srmesta 		 * thread to try op again after s_dns_tout seconds.
8031741Srmesta 		 * We've made sure that we don't have an already
8041741Srmesta 		 * running thread above.
8051741Srmesta 		 */
8061741Srmesta 		s_dns_tout = NFSMAPID_DNS_TOUT_SECS;
8071741Srmesta 		err = thr_create(NULL, 0, resolv_query_thread, (void *)argp,
8081741Srmesta 		    thr_flags, &s_dns_qthread);
8091741Srmesta 		if (!err) {
8101741Srmesta 			s_dns_qthr_created = TRUE;
8111741Srmesta 		}
8121741Srmesta #ifdef DEBUG
8131741Srmesta 		else {
8141741Srmesta 			msg_done++;
8151741Srmesta 			if (!(msg_done % NFSMAPID_SLOG_RATE))
8161741Srmesta 				syslog(LOG_ERR, EMSG_DNS_THREAD_ERROR);
8171741Srmesta 		}
8181741Srmesta #endif
8191741Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
820*7693SVallish.Vaidyeshwara@Sun.COM 		break;
8211741Srmesta 
8221741Srmesta 	case NO_RECOVERY:
8231741Srmesta #ifdef	DEBUG
8241741Srmesta 		syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami);
8251741Srmesta #endif
8261741Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
8271741Srmesta 		s_dns_disabled = TRUE;
8281741Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
8291741Srmesta 
8301741Srmesta 		/*FALLTHROUGH*/
8311741Srmesta 
8321741Srmesta 	default:
8331741Srmesta 		/*
8341741Srmesta 		 * For any other errors... DNS is responding, but
8351741Srmesta 		 * either it has no data, or some other problem is
8361741Srmesta 		 * occuring. At any rate, the TXT domain should not
8371741Srmesta 		 * be used, so we default to the DNS domain.
8381741Srmesta 		 */
839*7693SVallish.Vaidyeshwara@Sun.COM 		(void) rw_wrlock(&s_dns_data_lock);
840*7693SVallish.Vaidyeshwara@Sun.COM 		dns_txt_cached = 0;
841*7693SVallish.Vaidyeshwara@Sun.COM 		(void) rw_unlock(&s_dns_data_lock);
842*7693SVallish.Vaidyeshwara@Sun.COM 		resolv_txt_reset();
8431741Srmesta 		break;
8441741Srmesta 	}
8451741Srmesta 
846*7693SVallish.Vaidyeshwara@Sun.COM 	resolv_destroy();
8471741Srmesta }
8481741Srmesta 
8491741Srmesta static int
8501741Srmesta get_mtime(const char *fname, timestruc_t *mtim)
8511741Srmesta {
8521741Srmesta 	struct stat st;
8531741Srmesta 	int err;
8541741Srmesta 
8551741Srmesta 	if ((err = stat(fname, &st)) != 0)
8561741Srmesta 		return (err);
8571741Srmesta 
8581741Srmesta 	*mtim = st.st_mtim;
8591741Srmesta 	return (0);
8601741Srmesta }
8611741Srmesta 
8621741Srmesta 
8631741Srmesta /*
8641741Srmesta  * trim_wspace is a destructive interface; it is up to
8651741Srmesta  * the caller to save off an original copy if needed.
8661741Srmesta  */
8671741Srmesta static char *
8681741Srmesta trim_wspace(char *dp)
8691741Srmesta {
8701741Srmesta 	char	*r;
8711741Srmesta 	char	*ndp;
8721741Srmesta 
8731741Srmesta 	/*
8741741Srmesta 	 * Any empty domain is not valid
8751741Srmesta 	 */
8761741Srmesta 	if (dp == NULL)
8771741Srmesta 		return (NULL);
8781741Srmesta 
8791741Srmesta 	/*
8801741Srmesta 	 * Skip leading blanks
8811741Srmesta 	 */
8821741Srmesta 	for (ndp = dp; *ndp != '\0'; ndp++) {
8831741Srmesta 		if (!isspace(*ndp))
8841741Srmesta 			break;
8851741Srmesta 	}
8861741Srmesta 
8871741Srmesta 	/*
8881741Srmesta 	 * If we reached the end of the string w/o
8891741Srmesta 	 * finding a non-blank char, return error
8901741Srmesta 	 */
8911741Srmesta 	if (*ndp == '\0')
8921741Srmesta 		return (NULL);
8931741Srmesta 
8941741Srmesta 	/*
8951741Srmesta 	 * Find next blank in string
8961741Srmesta 	 */
8971741Srmesta 	for (r = ndp; *r != '\0'; r++) {
8981741Srmesta 		if (isspace(*r))
8991741Srmesta 			break;
9001741Srmesta 	}
9011741Srmesta 
9021741Srmesta 	/*
9031741Srmesta 	 * No more blanks found, we are done
9041741Srmesta 	 */
9051741Srmesta 	if (*r == '\0')
9061741Srmesta 		return (ndp);
9071741Srmesta 
9081741Srmesta 	/*
9091741Srmesta 	 * Terminate string at blank
9101741Srmesta 	 */
9111741Srmesta 	*r++ = '\0';
9121741Srmesta 
9131741Srmesta 	/*
9141741Srmesta 	 * Skip any trailing spaces
9151741Srmesta 	 */
9161741Srmesta 	while (*r != '\0') {
9171741Srmesta 		/*
9181741Srmesta 		 * If a non-blank is found, it is an
9191741Srmesta 		 * illegal domain (embedded blanks).
9201741Srmesta 		 */
9211741Srmesta 		if (!isspace(*r))
9221741Srmesta 			return (NULL);
9231741Srmesta 		r++;
9241741Srmesta 	}
9251741Srmesta 	return (ndp);
9261741Srmesta }
9271741Srmesta 
9281741Srmesta static void
9291741Srmesta get_nfs_domain(void)
9301741Srmesta {
9311741Srmesta 	char		*ndomain;
9321741Srmesta 	timestruc_t	 ntime;
9331741Srmesta 
9341741Srmesta 	/*
9351741Srmesta 	 * If we can't get stats for the config file, then
9361741Srmesta 	 * zap the NFS domain info.  If mtime hasn't changed,
9371741Srmesta 	 * then there's no work to do, so just return.
9381741Srmesta 	 */
9391741Srmesta 	if (get_mtime(NFSADMIN, &ntime) != 0) {
9401741Srmesta 		ZAP_DOMAIN(nfs);
9411741Srmesta 		return;
9421741Srmesta 	}
9431741Srmesta 
9441741Srmesta 	if (TIMESTRUC_EQ(ntime, nfs_mtime))
9451741Srmesta 		return;
9461741Srmesta 
9471741Srmesta 	/*
9481741Srmesta 	 * Get NFSMAPID_DOMAIN value from /etc/default/nfs for now.
9491741Srmesta 	 * Note: defread() returns a ptr to TSD.
9501741Srmesta 	 */
9511741Srmesta 	if (defopen(NFSADMIN) == 0) {
9521741Srmesta 		char	*dp = NULL;
9531741Srmesta #ifdef	DEBUG
9541741Srmesta 		char	*whoami = "get_nfs_domain";
9551741Srmesta 		char	 orig[NS_MAXCDNAME] = {0};
9561741Srmesta #endif
9571741Srmesta 		ndomain = (char *)defread("NFSMAPID_DOMAIN=");
9581741Srmesta 		(void) defopen(NULL);
9591741Srmesta #ifdef	DEBUG
9601741Srmesta 		if (ndomain)
9611741Srmesta 			(void) strncpy(orig, ndomain, NS_MAXCDNAME);
9621741Srmesta #endif
9631741Srmesta 		/*
9641741Srmesta 		 * NFSMAPID_DOMAIN was set, so it's time for validation. If
9651741Srmesta 		 * it's okay, then update NFS domain and return. If not,
9661741Srmesta 		 * bail (syslog in DEBUG). We make nfsmapid more a bit
9671741Srmesta 		 * more forgiving of trailing and leading white space.
9681741Srmesta 		 */
9691741Srmesta 		if ((dp = trim_wspace(ndomain)) != NULL) {
9701741Srmesta 			if (mapid_stdchk_domain(dp) > 0) {
9711741Srmesta 				nfs_domain_len = strlen(dp);
9721741Srmesta 				(void) strncpy(nfs_domain, dp, NS_MAXCDNAME);
9731741Srmesta 				nfs_domain[NS_MAXCDNAME] = '\0';
9741741Srmesta 				nfs_mtime = ntime;
9751741Srmesta 				return;
9761741Srmesta 			}
9771741Srmesta 		}
9781741Srmesta #ifdef	DEBUG
9791741Srmesta 		if (orig[0] != '\0') {
9801741Srmesta 			syslog(LOG_ERR, gettext("%s: Invalid domain name \"%s\""
981*7693SVallish.Vaidyeshwara@Sun.COM 			    " found in configuration file."), whoami, orig);
9821741Srmesta 		}
9831741Srmesta #endif
9841741Srmesta 	}
9851741Srmesta 
9861741Srmesta 	/*
9871741Srmesta 	 * So the NFS config file changed but it couldn't be opened or
9881741Srmesta 	 * it didn't specify NFSMAPID_DOMAIN or it specified an invalid
9891741Srmesta 	 * NFSMAPID_DOMAIN.  Time to zap current NFS domain info.
9901741Srmesta 	 */
9911741Srmesta 	ZAP_DOMAIN(nfs);
9921741Srmesta }
9931741Srmesta 
9941741Srmesta static void
9951741Srmesta get_dns_domain(void)
9961741Srmesta {
9971741Srmesta 	timestruc_t	 ntime = {0};
9981741Srmesta 
9991741Srmesta 	/*
10001741Srmesta 	 * If we can't get stats for the config file, then
10011741Srmesta 	 * zap the DNS domain info.  If mtime hasn't changed,
10021741Srmesta 	 * then there's no work to do, so just return.
10031741Srmesta 	 */
10041741Srmesta 	errno = 0;
10051741Srmesta 	if (get_mtime(_PATH_RESCONF, &ntime) != 0) {
10061741Srmesta 		switch (errno) {
10071741Srmesta 			case ENOENT:
10081741Srmesta 				/*
10091741Srmesta 				 * The resolver defaults to obtaining the
10101741Srmesta 				 * domain off of the NIS domainname(1M) if
10111741Srmesta 				 * /etc/resolv.conf does not exist, so we
10121741Srmesta 				 * move forward.
10131741Srmesta 				 */
10141741Srmesta 				break;
10151741Srmesta 
10161741Srmesta 			default:
10171741Srmesta 				ZAP_DOMAIN(dns);
10181741Srmesta 				return;
10191741Srmesta 		}
10201741Srmesta 	} else if (TIMESTRUC_EQ(ntime, dns_mtime))
10211741Srmesta 		return;
10221741Srmesta 
10231741Srmesta 	/*
10241741Srmesta 	 * Re-initialize resolver to zap DNS domain from previous
10251741Srmesta 	 * resolv_init() calls.
10261741Srmesta 	 */
10271741Srmesta 	(void) resolv_init();
10281741Srmesta 
10291741Srmesta 	/*
10301741Srmesta 	 * Update cached DNS domain.  No need for validation since
10311741Srmesta 	 * domain comes from resolver.  If resolver doesn't return the
10321741Srmesta 	 * domain, then zap the DNS domain.  This shouldn't ever happen,
10331741Srmesta 	 * and if it does, the machine has bigger problems (so no need
10341741Srmesta 	 * to generate a message that says DNS appears to be broken).
10351741Srmesta 	 */
10361741Srmesta 	(void) rw_rdlock(&s_dns_data_lock);
10371741Srmesta 	if (sysdns_domain[0] != '\0') {
10381741Srmesta 		(void) strncpy(dns_domain, sysdns_domain, NS_MAXCDNAME);
10391741Srmesta 		dns_domain_len = strlen(sysdns_domain);
10401741Srmesta 		(void) rw_unlock(&s_dns_data_lock);
10411741Srmesta 		dns_mtime = ntime;
1042*7693SVallish.Vaidyeshwara@Sun.COM 		resolv_destroy();
10431741Srmesta 		return;
10441741Srmesta 	}
10451741Srmesta 	(void) rw_unlock(&s_dns_data_lock);
10461741Srmesta 
10471741Srmesta 	ZAP_DOMAIN(dns);
1048*7693SVallish.Vaidyeshwara@Sun.COM 
1049*7693SVallish.Vaidyeshwara@Sun.COM 	resolv_destroy();
1050*7693SVallish.Vaidyeshwara@Sun.COM 
10511741Srmesta }
10521741Srmesta 
10531741Srmesta /*
10541741Srmesta  * PSARC 2005/487 Contracted Sun Private Interface
10551741Srmesta  * mapid_stdchk_domain()
10561741Srmesta  * Changes must be reviewed by Solaris File Sharing
10571741Srmesta  * Changes must be communicated to contract-2005-487-01@sun.com
10581741Srmesta  *
10591741Srmesta  * Based on the recommendations from RFC1033 and RFC1035, check
10601741Srmesta  * if a given domain name string is valid. Return values are:
10611741Srmesta  *
10621741Srmesta  *       1 = valid domain name
10631741Srmesta  *       0 = invalid domain name (or invalid embedded character)
10641741Srmesta  *      -1 = domain length > NS_MAXCDNAME
10651741Srmesta  */
10661741Srmesta int
10671741Srmesta mapid_stdchk_domain(const char *ds)
10681741Srmesta {
10691741Srmesta 	int	i;
10701741Srmesta 	size_t	len;
10711741Srmesta 
10721741Srmesta 	if (ds[0] == '\0')
10731741Srmesta 		return (0);
10741741Srmesta 	else
10751741Srmesta 		len = strlen(ds) - 1;
10761741Srmesta 
10771741Srmesta 	/*
10782268Sevanl 	 * 1st _AND_ last char _must_ be alphanumeric.
10792268Sevanl 	 * We check for other valid chars below.
10801741Srmesta 	 */
10812268Sevanl 	if ((!isalpha(ds[0]) && !isdigit(ds[0])) ||
10822268Sevanl 	    (!isalpha(ds[len]) && !isdigit(ds[len])))
10831741Srmesta 		return (0);
10841741Srmesta 
10851741Srmesta 	for (i = 0; *ds && i <= NS_MAXCDNAME; i++, ds++) {
10861741Srmesta 		if (!isalpha(*ds) && !isdigit(*ds) &&
10871741Srmesta 		    (*ds != '.') && (*ds != '-') && (*ds != '_'))
10881741Srmesta 			return (0);
10891741Srmesta 	}
10901741Srmesta 	return (i == (NS_MAXCDNAME + 1) ? -1 : 1);
10911741Srmesta }
10921741Srmesta 
10931741Srmesta /*
10941741Srmesta  * PSARC 2005/487 Consolidation Private
10951741Srmesta  * mapid_reeval_domain()
10961741Srmesta  * Changes must be reviewed by Solaris File Sharing
10971741Srmesta  */
10981741Srmesta void
10991741Srmesta mapid_reeval_domain(cb_t *arg)
11001741Srmesta {
11011741Srmesta 	char	*domain = NULL;
11021741Srmesta 
11031741Srmesta 	get_nfs_domain();
11041741Srmesta 	if (nfs_domain_len != 0) {
11051741Srmesta 		domain = nfs_domain;
11061741Srmesta 		goto dsync;
11071741Srmesta 	}
11081741Srmesta 
11091741Srmesta 	get_dns_txt_domain(arg);
11101741Srmesta 	if (dns_txt_domain_len != 0)
11111741Srmesta 		domain = dns_txt_domain;
11121741Srmesta 	else {
11131741Srmesta 		/*
11141741Srmesta 		 * We're either here because:
11151741Srmesta 		 *
11161741Srmesta 		 *  . NFSMAPID_DOMAIN was not set in /etc/default/nfs
11171741Srmesta 		 *  . No suitable DNS TXT resource record exists
11181741Srmesta 		 *  . DNS server is not responding to requests
11191741Srmesta 		 *
11201741Srmesta 		 * in either case, we want to default to using the
11211741Srmesta 		 * system configured DNS domain. If this fails, then
11221741Srmesta 		 * dns_domain will be empty and dns_domain_len will
11231741Srmesta 		 * be 0.
11241741Srmesta 		 */
11251741Srmesta 		get_dns_domain();
11261741Srmesta 		domain = dns_domain;
11271741Srmesta 	}
11281741Srmesta 
11291741Srmesta dsync:
11301741Srmesta 	domain_sync(arg, domain);
11311741Srmesta }
11321741Srmesta 
11331741Srmesta /*
11341741Srmesta  * PSARC 2005/487 Consolidation Private
11351741Srmesta  * mapid_get_domain()
11361741Srmesta  * Changes must be reviewed by Solaris File Sharing
11371741Srmesta  *
11381741Srmesta  * The use of TSD in mapid_get_domain() diverges slightly from the typical
11391741Srmesta  * TSD use, since here, the benefit of doing TSD is mostly to allocate
11401741Srmesta  * a per-thread buffer that will be utilized by other up-calls to the
11411741Srmesta  * daemon.
11421741Srmesta  *
11431741Srmesta  * In doors, the thread used for the upcall never really exits, hence
11441741Srmesta  * the typical destructor function defined via thr_keycreate() will
11451741Srmesta  * never be called. Thus, we only use TSD to allocate the per-thread
11461741Srmesta  * buffer and fill it up w/the configured 'mapid_domain' on each call.
11471741Srmesta  * This still alleviates the problem of having the caller free any
11481741Srmesta  * malloc'd space.
11491741Srmesta  */
11501741Srmesta char *
11511741Srmesta mapid_get_domain(void)
11521741Srmesta {
11531741Srmesta 	void	*tsd = NULL;
11541741Srmesta 
11551741Srmesta 	(void) thr_getspecific(s_thr_key, &tsd);
11561741Srmesta 	if (tsd == NULL) {
11571741Srmesta 		tsd = malloc(NS_MAXCDNAME+1);
11581741Srmesta 		if (tsd != NULL) {
11591741Srmesta 			(void) rw_rdlock(&mapid_domain_lock);
11601741Srmesta 			(void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME);
11611741Srmesta 			(void) rw_unlock(&mapid_domain_lock);
11621741Srmesta 			(void) thr_setspecific(s_thr_key, tsd);
11631741Srmesta 		}
11641741Srmesta 	} else {
11651741Srmesta 		(void) rw_rdlock(&mapid_domain_lock);
11661741Srmesta 		(void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME);
11671741Srmesta 		(void) rw_unlock(&mapid_domain_lock);
11681741Srmesta 	}
11691741Srmesta 	return ((char *)tsd);
11701741Srmesta }
11711741Srmesta 
11721741Srmesta /*
11731741Srmesta  * PSARC 2005/487 Contracted Sun Private Interface
11741741Srmesta  * mapid_derive_domain()
11751741Srmesta  * Changes must be reviewed by Solaris File Sharing
11761741Srmesta  * Changes must be communicated to contract-2005-487-01@sun.com
11771741Srmesta  *
11781741Srmesta  * This interface is called solely via sysidnfs4 iff no
11791741Srmesta  * NFSMAPID_DOMAIN was found. So, there is no ill effect
11801741Srmesta  * of having the reeval function call get_nfs_domain().
11811741Srmesta  */
11821741Srmesta char *
11831741Srmesta mapid_derive_domain(void)
11841741Srmesta {
11851741Srmesta 	cb_t	cb = {0};
11861741Srmesta 
11871741Srmesta 	_lib_init();
11881741Srmesta 	mapid_reeval_domain(&cb);
11891741Srmesta 	return (mapid_get_domain());
11901741Srmesta }
11911741Srmesta 
11921741Srmesta void
11931741Srmesta _lib_init(void)
11941741Srmesta {
11951741Srmesta 	(void) resolv_init();
11961741Srmesta 	(void) rwlock_init(&mapid_domain_lock, USYNC_THREAD, NULL);
11971741Srmesta 	(void) thr_keycreate(&s_thr_key, NULL);
11981741Srmesta 	lib_init_done++;
1199*7693SVallish.Vaidyeshwara@Sun.COM 	resolv_destroy();
12001741Srmesta }
1201