xref: /titanic_44/usr/src/lib/libmapid/common/mapid.c (revision 6d9307f996fe070c93b557d0ec10221c004796bc)
18200fe25Srmesta /*
28200fe25Srmesta  * CDDL HEADER START
38200fe25Srmesta  *
48200fe25Srmesta  * The contents of this file are subject to the terms of the
58200fe25Srmesta  * Common Development and Distribution License (the "License").
68200fe25Srmesta  * You may not use this file except in compliance with the License.
78200fe25Srmesta  *
88200fe25Srmesta  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
98200fe25Srmesta  * or http://www.opensolaris.org/os/licensing.
108200fe25Srmesta  * See the License for the specific language governing permissions
118200fe25Srmesta  * and limitations under the License.
128200fe25Srmesta  *
138200fe25Srmesta  * When distributing Covered Code, include this CDDL HEADER in each
148200fe25Srmesta  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
158200fe25Srmesta  * If applicable, add the following below this CDDL HEADER, with the
168200fe25Srmesta  * fields enclosed by brackets "[]" replaced with your own identifying
178200fe25Srmesta  * information: Portions Copyright [yyyy] [name of copyright owner]
188200fe25Srmesta  *
198200fe25Srmesta  * CDDL HEADER END
208200fe25Srmesta  */
218200fe25Srmesta /*
22b9175c69SKenjiro Tsuji  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
238200fe25Srmesta  * Use is subject to license terms.
248200fe25Srmesta  */
258200fe25Srmesta 
268200fe25Srmesta /*
278200fe25Srmesta  * PSARC/2004/154 nfsmapid DNS enhancements implementation.
288200fe25Srmesta  *
298200fe25Srmesta  * As per RFC 3530, file owner and group attributes in version 4 of the
308200fe25Srmesta  * NFS protocol are no longer exchanged between client and server as 32
318200fe25Srmesta  * bit integral values. Instead, owner and group file attributes are
328200fe25Srmesta  * exchanged between client and server as UTF8 strings of form
338200fe25Srmesta  *
348200fe25Srmesta  *      'user@domain'		(ie. "joeblow@central.sun.com")
358200fe25Srmesta  *      'group@domain'		(ie. "staff@central.sun.com")
368200fe25Srmesta  *
378200fe25Srmesta  * This NFSv4 feature is far beyond anything NFSv2/v3 ever provided, as
388200fe25Srmesta  * being able to describe a user with a unique string identifier provides
398200fe25Srmesta  * a much more powerful and administrative friendly way of dealing with
408200fe25Srmesta  * overlaps in the uid/gid number spaces. That notwithstanding, dealing
418200fe25Srmesta  * with issues of correctly mapping user and group ownership in a cross-
428200fe25Srmesta  * domain environment has proven a difficult problem to solve, since
438200fe25Srmesta  * dealing with different permutations of client naming configurations
448200fe25Srmesta  * (ie. NIS only, LDAP only, etc.) have bloated the problem. Thus, users
458200fe25Srmesta  * utilizing clients and servers that have the 'domain' portion of the
468200fe25Srmesta  * UTF8 attribute string configured differently than its peer server and
478200fe25Srmesta  * client accordingly, will experience watching their files owned by the
488200fe25Srmesta  * 'nobody' user and group. This is due to the fact that the 'domain's
498200fe25Srmesta  * don't match and the nfsmapid daemon treats the attribute strings as
508200fe25Srmesta  * unknown user(s) or group(s) (even though the actual uid/gid's may exist
518200fe25Srmesta  * in the executing daemon's system). Please refer to PSARC/2004/154 for
528200fe25Srmesta  * further background and motivation for these enhancements.
538200fe25Srmesta  *
548200fe25Srmesta  * The latest implementation of the nfsmapid daemon relies on a DNS TXT
558200fe25Srmesta  * record. The behavior of nfsmapid is to first use the NFSMAPID_DOMAIN
568200fe25Srmesta  * configuration option in /etc/default/nfs. If the option has not been
578200fe25Srmesta  * set, then the nfsmapid daemon queries the configured DNS domain server
588200fe25Srmesta  * for the _nfsv4idmapdomain TXT record. If the record exists, then the
598200fe25Srmesta  * record's value is used as the 'domain' portion of the UTF8 attribute
608200fe25Srmesta  * strings. If the TXT record has not been configured in the DNS server,
618200fe25Srmesta  * then the daemon falls back to using the DNS domain name itself as the
628200fe25Srmesta  * 'domain' portion of the attribute strings. Lastly, if the configured
638200fe25Srmesta  * DNS server is unresponsive, the nfsmapid daemon falls back to using
648200fe25Srmesta  * the DNS domain name as the 'domain' portion of the attribute strings,
658200fe25Srmesta  * and fires up a query thread to keep contacting the DNS server until
668200fe25Srmesta  * it responds with either a TXT record, or a lack thereof, in which
678200fe25Srmesta  * case, nfsmapid just continues to utilize the DNS domain name.
688200fe25Srmesta  */
698200fe25Srmesta #define	__LIBMAPID_IMPL
708200fe25Srmesta #include <nfs/mapid.h>
718200fe25Srmesta #pragma	init(_lib_init)
72*6d9307f9SVallish Vaidyeshwara #pragma	fini(_lib_fini)
738200fe25Srmesta 
748200fe25Srmesta /*
758200fe25Srmesta  * DEBUG Only
768200fe25Srmesta  * Decode any resolver errors and print out message to log
778200fe25Srmesta  */
788200fe25Srmesta static int
798200fe25Srmesta resolv_error(void)
808200fe25Srmesta {
818200fe25Srmesta #ifndef	DEBUG
828200fe25Srmesta 
838200fe25Srmesta 	return (h_errno);
848200fe25Srmesta 
858200fe25Srmesta #else	/* DEBUG */
868200fe25Srmesta 
878200fe25Srmesta 	static uint64_t	 msg_done[NS_ERRS] = {0};
888200fe25Srmesta 
898200fe25Srmesta 	switch (h_errno) {
908200fe25Srmesta 	case NETDB_INTERNAL:
918200fe25Srmesta 		syslog(LOG_ERR, EMSG_NETDB_INTERNAL, strerror(errno));
928200fe25Srmesta 		break;
938200fe25Srmesta 
948200fe25Srmesta 	case HOST_NOT_FOUND:
958200fe25Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
968200fe25Srmesta 		msg_done[h_errno]++;
978200fe25Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
988200fe25Srmesta 			syslog(LOG_ERR, EMSG_HOST_NOT_FOUND, s_dname);
998200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
1008200fe25Srmesta 		break;
1018200fe25Srmesta 
1028200fe25Srmesta 	case TRY_AGAIN:
1038200fe25Srmesta 		/*
1048200fe25Srmesta 		 * Nameserver is not responding.
1058200fe25Srmesta 		 * Try again after a given timeout.
1068200fe25Srmesta 		 */
1078200fe25Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
1088200fe25Srmesta 		msg_done[h_errno]++;
1098200fe25Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
1108200fe25Srmesta 			syslog(LOG_ERR, EMSG_TRY_AGAIN, s_dname);
1118200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
1128200fe25Srmesta 		break;
1138200fe25Srmesta 
1148200fe25Srmesta 	case NO_RECOVERY:
1158200fe25Srmesta 		/*
1168200fe25Srmesta 		 * This msg only really happens once, due
1178200fe25Srmesta 		 * to s_dns_disabled flag (see below)
1188200fe25Srmesta 		 */
1198200fe25Srmesta 		syslog(LOG_ERR, EMSG_NO_RECOVERY, hstrerror(h_errno));
1208200fe25Srmesta 		break;
1218200fe25Srmesta 
1228200fe25Srmesta 	case NO_DATA:
1238200fe25Srmesta 		/*
1248200fe25Srmesta 		 * No entries in the nameserver for
1258200fe25Srmesta 		 * the specific record or record type.
1268200fe25Srmesta 		 */
1278200fe25Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
1288200fe25Srmesta 		msg_done[h_errno]++;
1298200fe25Srmesta 		if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE))
1308200fe25Srmesta 			syslog(LOG_ERR, EMSG_NO_DATA, NFSMAPID_DNS_RR, s_dname);
1318200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
1328200fe25Srmesta 		break;
1338200fe25Srmesta 
1348200fe25Srmesta 	case NETDB_SUCCESS:
1358200fe25Srmesta 	default:
1368200fe25Srmesta 		break;
1378200fe25Srmesta 	}
1388200fe25Srmesta 	return (h_errno);
1398200fe25Srmesta 
1408200fe25Srmesta #endif	/* DEBUG */
1418200fe25Srmesta }
1428200fe25Srmesta 
1438200fe25Srmesta /*
1448200fe25Srmesta  * Reset the global state variables used for the TXT record.
1458200fe25Srmesta  * Having these values reset to zero helps nfsmapid confirm
1468200fe25Srmesta  * that a valid DNS TXT record was not found; in which case,
1478200fe25Srmesta  * it would fall back to using the configured DNS domain name.
1488200fe25Srmesta  *
1498200fe25Srmesta  * If a valid DNS TXT record _was_ found, but subsequent contact
1508200fe25Srmesta  * to the DNS server is somehow hindered, the previous DNS TXT
1518200fe25Srmesta  * RR value continues to be used. Thus, in such instances, we
1528200fe25Srmesta  * forego clearing the global config variables so nfsmapid can
1538200fe25Srmesta  * continue to use a valid DNS TXT RR while contact to the DNS
1548200fe25Srmesta  * server is reestablished.
1558200fe25Srmesta  */
1568200fe25Srmesta static void
1578200fe25Srmesta resolv_txt_reset(void)
1588200fe25Srmesta {
1598200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
1608200fe25Srmesta 	bzero(s_txt_rr, sizeof (s_txt_rr));
1618200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
1628200fe25Srmesta 
1638200fe25Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
1648200fe25Srmesta 	if (!dns_txt_cached) {
1658200fe25Srmesta 		dns_txt_domain_len = 0;
1668200fe25Srmesta 		bzero(dns_txt_domain, DNAMEMAX);
1678200fe25Srmesta 	}
1688200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
1698200fe25Srmesta }
1708200fe25Srmesta 
1718200fe25Srmesta /*
1728200fe25Srmesta  * Initialize resolver and populate &s_res struct
1738200fe25Srmesta  *
1748200fe25Srmesta  * DNS Domain is saved off sysdns_domain in case we
1758200fe25Srmesta  * need to fall back to using the DNS domain name as
1768200fe25Srmesta  * the v4 attribute string domain.
1778200fe25Srmesta  */
1788200fe25Srmesta static int
1798200fe25Srmesta resolv_init(void)
1808200fe25Srmesta {
1818200fe25Srmesta 	size_t			len;
1828200fe25Srmesta 	int			n;
1838200fe25Srmesta 	struct __res_state	res;
1848200fe25Srmesta 
1858200fe25Srmesta 	(void) mutex_lock(&s_res_lock);
1868200fe25Srmesta 	bzero(&s_res, sizeof (struct __res_state));
1878200fe25Srmesta 	n = h_errno = errno = 0;
1888200fe25Srmesta 	if ((n = res_ninit(&s_res)) < 0) {
1898200fe25Srmesta 		(void) mutex_unlock(&s_res_lock);
1908200fe25Srmesta 		(void) resolv_error();
1918200fe25Srmesta 		return (n);
1928200fe25Srmesta 	}
1938200fe25Srmesta 	res = s_res;
1948200fe25Srmesta 	(void) mutex_unlock(&s_res_lock);
1958200fe25Srmesta 
1968200fe25Srmesta 	len = strlen(res.defdname) + 1;
1978200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
1988200fe25Srmesta 	bzero(s_dname, sizeof (s_dname));
1998200fe25Srmesta 	(void) snprintf(s_dname, len, "%s", res.defdname);
2008200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
2018200fe25Srmesta 
2028200fe25Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
2038200fe25Srmesta 	(void) snprintf(sysdns_domain, len, "%s", res.defdname);
2048200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
2058200fe25Srmesta 
2068200fe25Srmesta 	return (0);
2078200fe25Srmesta }
2088200fe25Srmesta 
2098200fe25Srmesta /*
2108200fe25Srmesta  * Search criteria assumptions:
2118200fe25Srmesta  *
2128200fe25Srmesta  * The onus will fall on the sysadmins to correctly configure the TXT
2138200fe25Srmesta  * record in the DNS domain where the box currently resides in order
2148200fe25Srmesta  * for the record to be found. However, if they sysadmin chooses to
2158200fe25Srmesta  * add the 'search' key to /etc/resolv.conf, then resolv_search()
2168200fe25Srmesta  * _will_ traverse up the DNS tree as specified in the 'search' key.
2178200fe25Srmesta  * Otherwise, we'll default the domain to the DNS domain itself.
2188200fe25Srmesta  */
2198200fe25Srmesta static int
2208200fe25Srmesta resolv_search(void)
2218200fe25Srmesta {
2228200fe25Srmesta 	int			len;
2238200fe25Srmesta 	ans_t			ans = {0};
2248200fe25Srmesta 	struct __res_state	res;
2258200fe25Srmesta 	int			type = T_TXT;
2268200fe25Srmesta 	int			class = C_IN;
2278200fe25Srmesta 
2288200fe25Srmesta 	(void) mutex_lock(&s_res_lock);
2298200fe25Srmesta 	res = s_res;
2308200fe25Srmesta 	(void) mutex_unlock(&s_res_lock);
2318200fe25Srmesta 
2328200fe25Srmesta 	/*
2338200fe25Srmesta 	 * Avoid holding locks across the res_nsearch() call to
2348200fe25Srmesta 	 * prevent stalling threads during network partitions.
2358200fe25Srmesta 	 */
2368200fe25Srmesta 	len = h_errno = errno = 0;
2378200fe25Srmesta 	if ((len = res_nsearch(&res, NFSMAPID_DNS_RR, class, type,
2388200fe25Srmesta 	    ans.buf, sizeof (ans))) < 0)
2398200fe25Srmesta 		return (resolv_error());
2408200fe25Srmesta 
2418200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
2428200fe25Srmesta 	s_ans = ans;
2438200fe25Srmesta 	s_anslen = len;
2448200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
2458200fe25Srmesta 
2468200fe25Srmesta 	return (NETDB_SUCCESS);
2478200fe25Srmesta }
2488200fe25Srmesta 
2498200fe25Srmesta /*
250bfa62c28SVallish Vaidyeshwara  * Free all resolver state information stored in s_res
251bfa62c28SVallish Vaidyeshwara  */
252bfa62c28SVallish Vaidyeshwara static void
253bfa62c28SVallish Vaidyeshwara resolv_destroy(void)
254bfa62c28SVallish Vaidyeshwara {
255bfa62c28SVallish Vaidyeshwara 	(void) mutex_lock(&s_res_lock);
256bfa62c28SVallish Vaidyeshwara 	res_ndestroy(&s_res);
257bfa62c28SVallish Vaidyeshwara 	(void) mutex_unlock(&s_res_lock);
258bfa62c28SVallish Vaidyeshwara }
259bfa62c28SVallish Vaidyeshwara 
260bfa62c28SVallish Vaidyeshwara /*
2618200fe25Srmesta  * Skip one DNS record
2628200fe25Srmesta  */
2638200fe25Srmesta static uchar_t  *
2648200fe25Srmesta resolv_skip_rr(uchar_t *p, uchar_t *eom)
2658200fe25Srmesta {
2668200fe25Srmesta 	int	t;
2678200fe25Srmesta 	int	dlen;
2688200fe25Srmesta 
2698200fe25Srmesta 	/*
2708200fe25Srmesta 	 * Skip compressed name
2718200fe25Srmesta 	 */
2728200fe25Srmesta 	errno = 0;
2738200fe25Srmesta 	if ((t = dn_skipname(p, eom)) < 0) {
2748200fe25Srmesta #ifdef	DEBUG
2758200fe25Srmesta 		syslog(LOG_ERR, "%s", strerror(errno));
2768200fe25Srmesta #endif
2778200fe25Srmesta 		return (NULL);
2788200fe25Srmesta 	}
2798200fe25Srmesta 
2808200fe25Srmesta 	/*
2818200fe25Srmesta 	 * Advance pointer and make sure
2828200fe25Srmesta 	 * we're still within the message
2838200fe25Srmesta 	 */
2848200fe25Srmesta 	p += t;
2858200fe25Srmesta 	if ((p + RRFIXEDSZ) > eom)
2868200fe25Srmesta 		return (NULL);
2878200fe25Srmesta 
2888200fe25Srmesta 	/*
2898200fe25Srmesta 	 * Now, just skip over the rr fields
2908200fe25Srmesta 	 */
2918200fe25Srmesta 	p += INT16SZ;	/* type */
2928200fe25Srmesta 	p += INT16SZ;	/* class */
2938200fe25Srmesta 	p += INT32SZ;	/* ttl */
2948200fe25Srmesta 	dlen = ns_get16(p);
2958200fe25Srmesta 	p += INT16SZ;
2968200fe25Srmesta 	p += dlen;	/* dlen */
2978200fe25Srmesta 	if (p > eom)
2988200fe25Srmesta 		return (NULL);
2998200fe25Srmesta 
3008200fe25Srmesta 	return (p);
3018200fe25Srmesta }
3028200fe25Srmesta 
3038200fe25Srmesta /*
3048200fe25Srmesta  * Process one TXT record.
3058200fe25Srmesta  *
3068200fe25Srmesta  * nfsmapid queries the DNS server for the specific _nfsv4idmapdomain
3078200fe25Srmesta  * TXT record. Thus, if the TXT record exists, the answer section of
3088200fe25Srmesta  * the DNS response carries the TXT record's value. Thus, we check that
3098200fe25Srmesta  * the value is indeed a valid domain and set the modular s_txt_rr
3108200fe25Srmesta  * global to the domain value.
3118200fe25Srmesta  */
3128200fe25Srmesta static void
3138200fe25Srmesta resolve_process_txt(uchar_t *p, int dlen)
3148200fe25Srmesta {
3158200fe25Srmesta 	char		*rr_base = (char *)(p + 1);
3168200fe25Srmesta 	char		*rr_end = (char *)(p + dlen);
3178200fe25Srmesta 	size_t		 len = rr_end - rr_base;
3188200fe25Srmesta #ifdef	DEBUG
3198200fe25Srmesta 	static uint64_t	 msg_done = 0;
3208200fe25Srmesta #endif
3218200fe25Srmesta 	char		 tmp_txt_rr[DNAMEMAX];
3228200fe25Srmesta 
3238200fe25Srmesta 	if (len >= DNAMEMAX)
3248200fe25Srmesta 		return;		/* process next TXT RR */
3258200fe25Srmesta 
3268200fe25Srmesta 	/*
3278200fe25Srmesta 	 * make sure we have a clean buf since
3288200fe25Srmesta 	 * we may've processed several TXT rr's
3298200fe25Srmesta 	 */
3308200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
3318200fe25Srmesta 	bzero(s_txt_rr, sizeof (s_txt_rr));
3328200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
3338200fe25Srmesta 
3348200fe25Srmesta 	(void) strncpy(tmp_txt_rr, rr_base, len);
3358200fe25Srmesta 	tmp_txt_rr[len] = '\0';
3368200fe25Srmesta 
3378200fe25Srmesta 	/*
3388200fe25Srmesta 	 * If there is a record and it's a valid domain, we're done.
3398200fe25Srmesta 	 */
3408200fe25Srmesta 	if (rr_base[0] != '\0' && mapid_stdchk_domain(tmp_txt_rr) > 0) {
3418200fe25Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
3428200fe25Srmesta 		(void) strncpy(s_txt_rr, rr_base, len);
3438200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
3448200fe25Srmesta #ifdef	DEBUG
3458200fe25Srmesta 		syslog(LOG_ERR, "TXT (Rec):\t%s", s_txt_rr);
3468200fe25Srmesta 
3478200fe25Srmesta 	} else if (!(msg_done++ % NFSMAPID_SLOG_RATE)) {
3488200fe25Srmesta 		/*
3498200fe25Srmesta 		 * Otherwise, log the error
3508200fe25Srmesta 		 */
3518200fe25Srmesta 		(void) rw_rdlock(&s_dns_impl_lock);
3528200fe25Srmesta 		syslog(LOG_ERR, EMSG_DNS_RR_INVAL, NFSMAPID_DNS_RR, s_dname);
3538200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
3548200fe25Srmesta #endif
3558200fe25Srmesta 	}
3568200fe25Srmesta }
3578200fe25Srmesta 
3588200fe25Srmesta /*
3598200fe25Srmesta  * Decode any answer received from the DNS server. This interface is
3608200fe25Srmesta  * capable of much more than just decoding TXT records. We maintain
3618200fe25Srmesta  * focus on TXT rr's for now, but this will probably change once we
3628200fe25Srmesta  * get the IETF approved application specific DNS RR.
3638200fe25Srmesta  *
3648200fe25Srmesta  * Here's an example of the TXT record we're decoding (as would appear
3658200fe25Srmesta  * in the DNS zone file):
3668200fe25Srmesta  *
3678200fe25Srmesta  *            _nfsv4idmapdomain    IN    TXT    "sun.com"
3688200fe25Srmesta  *
3698200fe25Srmesta  * Once the IETF application specific DNS RR is granted, we should only
3708200fe25Srmesta  * be changing the record flavor, but all should pretty much stay the
3718200fe25Srmesta  * same.
3728200fe25Srmesta  */
3738200fe25Srmesta static void
3748200fe25Srmesta resolv_decode(void)
3758200fe25Srmesta {
3768200fe25Srmesta 	uchar_t		*buf;
3778200fe25Srmesta 	HEADER		*hp;
3788200fe25Srmesta 	uchar_t		 name[DNAMEMAX];
3798200fe25Srmesta 	uchar_t		*eom;
3808200fe25Srmesta 	uchar_t		*p;
3818200fe25Srmesta 	int		 n;
3828200fe25Srmesta 	uint_t		 qd_cnt;
3838200fe25Srmesta 	uint_t		 an_cnt;
3848200fe25Srmesta 	uint_t		 ns_cnt;
3858200fe25Srmesta 	uint_t		 ar_cnt;
3868200fe25Srmesta 	uint_t		 cnt;
3878200fe25Srmesta 	uint_t		 type;
3888200fe25Srmesta 	int		 dlen;
3898200fe25Srmesta 	ans_t		 answer = {0};
3908200fe25Srmesta 	int		 answer_len = 0;
3918200fe25Srmesta 
3928200fe25Srmesta 	/*
3938200fe25Srmesta 	 * Check the HEADER for any signs of errors
3948200fe25Srmesta 	 * and extract the answer counts for later.
3958200fe25Srmesta 	 */
3968200fe25Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
3978200fe25Srmesta 	answer = s_ans;
3988200fe25Srmesta 	answer_len = s_anslen;
3998200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
4008200fe25Srmesta 
4018200fe25Srmesta 	buf = (uchar_t *)&answer.buf;
4028200fe25Srmesta 	hp = (HEADER *)&answer.hdr;
4038200fe25Srmesta 	eom = (uchar_t *)(buf + answer_len);
4048200fe25Srmesta 	if (hp->rcode !=  NOERROR) {
4058200fe25Srmesta #ifdef	DEBUG
4068200fe25Srmesta 		syslog(LOG_ERR, "errno: %s", strerror(errno));
4078200fe25Srmesta 		syslog(LOG_ERR, "h_errno: %s", hstrerror(h_errno));
4088200fe25Srmesta #endif
4098200fe25Srmesta 		return;
4108200fe25Srmesta 	}
4118200fe25Srmesta 	qd_cnt = ntohs(hp->qdcount);
4128200fe25Srmesta 	an_cnt = ntohs(hp->ancount);
4138200fe25Srmesta 	ns_cnt = ntohs(hp->nscount);
4148200fe25Srmesta 	ar_cnt = ntohs(hp->arcount);
4158200fe25Srmesta 
4168200fe25Srmesta 	/*
4178200fe25Srmesta 	 * skip query entries
4188200fe25Srmesta 	 */
4198200fe25Srmesta 	p = (uchar_t *)(buf + HFIXEDSZ);
4208200fe25Srmesta 	errno = 0;
4218200fe25Srmesta 	while (qd_cnt-- > 0) {
4228200fe25Srmesta 		n = dn_skipname(p, eom);
4238200fe25Srmesta 		if (n < 0) {
4248200fe25Srmesta #ifdef	DEBUG
4258200fe25Srmesta 			syslog(LOG_ERR, "%s", strerror(errno));
4268200fe25Srmesta #endif
4278200fe25Srmesta 			return;
4288200fe25Srmesta 		}
4298200fe25Srmesta 		p += n;
4308200fe25Srmesta 		p += INT16SZ;	/* type */
4318200fe25Srmesta 		p += INT16SZ;	/* class */
4328200fe25Srmesta 	}
4338200fe25Srmesta 
4348200fe25Srmesta #ifdef	DEBUG
4358200fe25Srmesta 	/*
4368200fe25Srmesta 	 * If debugging... print query only once.
4378200fe25Srmesta 	 * NOTE: Don't advance pointer... this is done
4388200fe25Srmesta 	 *	 in while() loop on a per record basis !
4398200fe25Srmesta 	 */
4408200fe25Srmesta 	n = h_errno = errno = 0;
4418200fe25Srmesta 	n = dn_expand(buf, eom, p, (char *)name, sizeof (name));
4428200fe25Srmesta 	if (n < 0) {
4438200fe25Srmesta 		(void) resolv_error();
4448200fe25Srmesta 		return;
4458200fe25Srmesta 	}
4468200fe25Srmesta 	syslog(LOG_ERR, "Query:\t\t%-30s", name);
4478200fe25Srmesta #endif
4488200fe25Srmesta 
4498200fe25Srmesta 	/*
4508200fe25Srmesta 	 * Process actual answer(s).
4518200fe25Srmesta 	 */
4528200fe25Srmesta 	cnt = an_cnt;
4538200fe25Srmesta 	while (cnt-- > 0 && p < eom) {
4548200fe25Srmesta 		/* skip the name field */
4558200fe25Srmesta 		n = dn_expand(buf, eom, p, (char *)name, sizeof (name));
4568200fe25Srmesta 		if (n < 0) {
4578200fe25Srmesta 			(void) resolv_error();
4588200fe25Srmesta 			return;
4598200fe25Srmesta 		}
4608200fe25Srmesta 		p += n;
4618200fe25Srmesta 
4628200fe25Srmesta 		if ((p + 3 * INT16SZ + INT32SZ) > eom)
4638200fe25Srmesta 			return;
4648200fe25Srmesta 
4658200fe25Srmesta 		type = ns_get16(p);
4668200fe25Srmesta 		p += INT16SZ;
4678200fe25Srmesta 		p += INT16SZ + INT32SZ;	/* skip class & ttl */
4688200fe25Srmesta 		dlen = ns_get16(p);
4698200fe25Srmesta 		p += INT16SZ;
4708200fe25Srmesta 
4718200fe25Srmesta 		if ((p + dlen) > eom)
4728200fe25Srmesta 			return;
4738200fe25Srmesta 
4748200fe25Srmesta 		switch (type) {
4758200fe25Srmesta 			case T_TXT:
4768200fe25Srmesta 				resolve_process_txt(p, dlen);
4778200fe25Srmesta 				break;
4788200fe25Srmesta 
4798200fe25Srmesta 			default:
4808200fe25Srmesta 				/*
4818200fe25Srmesta 				 * Advance to next answer record for any
4828200fe25Srmesta 				 * other record types. Again, this will
4838200fe25Srmesta 				 * probably change (see block comment).
4848200fe25Srmesta 				 */
4858200fe25Srmesta 				p += dlen;
4868200fe25Srmesta 				break;
4878200fe25Srmesta 		}
4888200fe25Srmesta 	}
4898200fe25Srmesta 
4908200fe25Srmesta 	/*
4918200fe25Srmesta 	 * Skip name server and additional records for now.
4928200fe25Srmesta 	 */
4938200fe25Srmesta 	cnt = ns_cnt + ar_cnt;
4948200fe25Srmesta 	if (cnt > 0) {
4958200fe25Srmesta 		while (--cnt != 0 && p < eom) {
4968200fe25Srmesta 			p = resolv_skip_rr(p, eom);
4978200fe25Srmesta 			if (p == NULL)
4988200fe25Srmesta 				return;
4998200fe25Srmesta 		}
5008200fe25Srmesta 	}
5018200fe25Srmesta }
5028200fe25Srmesta 
5038200fe25Srmesta /*
5048200fe25Srmesta  * If a valid TXT record entry exists, s_txt_rr contains the domain
5058200fe25Srmesta  * value (as set in resolv_process_txt) and we extract the value into
5068200fe25Srmesta  * dns_txt_domain (the exported global). If there was _no_ valid TXT
5078200fe25Srmesta  * entry, we simply return and check_domain() will default to the
5088200fe25Srmesta  * DNS domain since we did resolv_txt_reset() first.
5098200fe25Srmesta  */
5108200fe25Srmesta static void
5118200fe25Srmesta resolv_get_txt_data()
5128200fe25Srmesta {
5138200fe25Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
5148200fe25Srmesta 	if (s_txt_rr[0] != '\0') {
5158200fe25Srmesta 		(void) rw_wrlock(&s_dns_data_lock);
5168200fe25Srmesta 		(void) snprintf(dns_txt_domain, strlen(s_txt_rr) + 1, "%s",
5178200fe25Srmesta 		    s_txt_rr);
5188200fe25Srmesta 		dns_txt_domain_len = strlen(dns_txt_domain);
5198200fe25Srmesta 		dns_txt_cached = 1;
5208200fe25Srmesta 		(void) rw_unlock(&s_dns_data_lock);
5218200fe25Srmesta 	}
5228200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
5238200fe25Srmesta }
5248200fe25Srmesta 
5258200fe25Srmesta static void
5268200fe25Srmesta domain_sync(cb_t *argp, char *dname)
5278200fe25Srmesta {
5288200fe25Srmesta 	int	dlen = 0;
5298200fe25Srmesta 	void	*(*fcn)(void *) = NULL;
5308200fe25Srmesta 	int	sighup = 0;
5318200fe25Srmesta 	int	domchg = 0;
5328200fe25Srmesta 
5338200fe25Srmesta 	/*
5348200fe25Srmesta 	 * Make sure values passed are sane and initialize accordingly.
5358200fe25Srmesta 	 */
5368200fe25Srmesta 	if (dname != NULL)
5378200fe25Srmesta 		dlen = strlen(dname);
5388200fe25Srmesta 	if (argp) {
5398200fe25Srmesta 		if (argp->fcn)
5408200fe25Srmesta 			fcn = argp->fcn;
5418200fe25Srmesta 		if (argp->signal)
5428200fe25Srmesta 			sighup = argp->signal;
5438200fe25Srmesta 	}
5448200fe25Srmesta 
5458200fe25Srmesta 	/*
5468200fe25Srmesta 	 * Update the library's mapid_domain variable if 'dname' is different.
5478200fe25Srmesta 	 */
5488200fe25Srmesta 	if (dlen != 0 && strncasecmp(dname, mapid_domain, NS_MAXCDNAME)) {
5498200fe25Srmesta 		(void) rw_wrlock(&mapid_domain_lock);
5508200fe25Srmesta 		(void) strncpy(mapid_domain, dname, NS_MAXCDNAME);
5518200fe25Srmesta 		mapid_domain_len = dlen;
5528200fe25Srmesta 		(void) rw_unlock(&mapid_domain_lock);
5538200fe25Srmesta 		domchg++;
5548200fe25Srmesta 	}
5558200fe25Srmesta 
5568200fe25Srmesta 	/*
5578200fe25Srmesta 	 * If the caller gave us a valid callback routine, we
5588200fe25Srmesta 	 * instantiate it to announce the domain change, but
5598200fe25Srmesta 	 * only if either the domain changed _or_ the caller
5608200fe25Srmesta 	 * was issued a SIGHUP.
5618200fe25Srmesta 	 */
5628200fe25Srmesta 	if (fcn != NULL && (sighup || domchg))
5638200fe25Srmesta 		(void) fcn((void *)mapid_domain);
5648200fe25Srmesta }
5658200fe25Srmesta 
5668200fe25Srmesta /*
5678200fe25Srmesta  * Thread to keep pinging  DNS  server for  TXT  record if nfsmapid's
5688200fe25Srmesta  * initial attempt at contact with server failed. We could potentially
5698200fe25Srmesta  * have a substantial number of NFSv4 clients and having all of them
5708200fe25Srmesta  * hammering on an already unresponsive DNS server would not help
5718200fe25Srmesta  * things. So, we limit the number of live query threads to at most
5728200fe25Srmesta  * 1 at any one time to keep things from getting out of hand.
5738200fe25Srmesta  */
5748200fe25Srmesta /* ARGSUSED */
5758200fe25Srmesta static void *
5768200fe25Srmesta resolv_query_thread(void *arg)
5778200fe25Srmesta {
5788200fe25Srmesta 	unsigned int	 nap_time;
5798200fe25Srmesta 
5808200fe25Srmesta #ifdef	DEBUG
5818200fe25Srmesta 	char		*whoami = "query_thread";
5828200fe25Srmesta 
5838200fe25Srmesta 	syslog(LOG_ERR, "%s active !", whoami);
5848200fe25Srmesta #endif
5858200fe25Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
5868200fe25Srmesta 	nap_time = s_dns_tout;
5878200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
5888200fe25Srmesta 
5898200fe25Srmesta 	for (;;) {
5908200fe25Srmesta 		(void) sleep(nap_time);
5918200fe25Srmesta 
5928200fe25Srmesta 		resolv_txt_reset();
593*6d9307f9SVallish Vaidyeshwara 		if (resolv_init() < 0) {
594*6d9307f9SVallish Vaidyeshwara 			/*
595*6d9307f9SVallish Vaidyeshwara 			 * Failed to initialize resolver. Do not
596*6d9307f9SVallish Vaidyeshwara 			 * query DNS server.
597*6d9307f9SVallish Vaidyeshwara 			 */
598*6d9307f9SVallish Vaidyeshwara 			goto thr_reset;
599*6d9307f9SVallish Vaidyeshwara 		}
6008200fe25Srmesta 		switch (resolv_search()) {
6018200fe25Srmesta 		case NETDB_SUCCESS:
6028200fe25Srmesta 			resolv_decode();
6038200fe25Srmesta 			resolv_get_txt_data();
6048200fe25Srmesta 
6058200fe25Srmesta 			/*
6068200fe25Srmesta 			 * This is a bit different than what we
6078200fe25Srmesta 			 * do in get_dns_txt_domain(), where we
6088200fe25Srmesta 			 * simply return and let the caller
6098200fe25Srmesta 			 * access dns_txt_domain directly.
6108200fe25Srmesta 			 *
6118200fe25Srmesta 			 * Here we invoke the callback routine
6128200fe25Srmesta 			 * provided by the caller to the
6138200fe25Srmesta 			 * mapid_reeval_domain() interface via
6148200fe25Srmesta 			 * the cb_t's fcn param.
6158200fe25Srmesta 			 */
6168200fe25Srmesta 			domain_sync((cb_t *)arg, dns_txt_domain);
6178200fe25Srmesta 			goto thr_okay;
6188200fe25Srmesta 
6198200fe25Srmesta 		case NO_DATA:
6208200fe25Srmesta 			/*
6218200fe25Srmesta 			 * DNS is up now, but does not have
6228200fe25Srmesta 			 * the NFSV4IDMAPDOMAIN TXT record.
6238200fe25Srmesta 			 */
6248200fe25Srmesta #ifdef	DEBUG
6258200fe25Srmesta 			syslog(LOG_ERR, "%s: DNS has no TXT Record", whoami);
6268200fe25Srmesta #endif
6278200fe25Srmesta 			goto thr_reset;
6288200fe25Srmesta 
6298200fe25Srmesta 		case NO_RECOVERY:
6308200fe25Srmesta 			/*
6318200fe25Srmesta 			 * Non-Recoverable error occurred. No sense
6328200fe25Srmesta 			 * in keep pinging the DNS server at this
6338200fe25Srmesta 			 * point, so we disable any further contact.
6348200fe25Srmesta 			 */
6358200fe25Srmesta #ifdef	DEBUG
6368200fe25Srmesta 			syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami);
6378200fe25Srmesta #endif
6388200fe25Srmesta 			(void) rw_wrlock(&s_dns_impl_lock);
6398200fe25Srmesta 			s_dns_disabled = TRUE;
6408200fe25Srmesta 			(void) rw_unlock(&s_dns_impl_lock);
6418200fe25Srmesta 			goto thr_reset;
6428200fe25Srmesta 
6438200fe25Srmesta 		case HOST_NOT_FOUND:
6448200fe25Srmesta 			/*
6458200fe25Srmesta 			 * Authoritative NS not responding...
6468200fe25Srmesta 			 * keep trying for non-authoritative reply
6478200fe25Srmesta 			 */
6488200fe25Srmesta 			/*FALLTHROUGH*/
6498200fe25Srmesta 
6508200fe25Srmesta 		case TRY_AGAIN:
6518200fe25Srmesta 			/* keep trying */
6528200fe25Srmesta #ifdef	DEBUG
6538200fe25Srmesta 			syslog(LOG_ERR, "%s: retrying...", whoami);
6548200fe25Srmesta #endif
6558200fe25Srmesta 			break;
6568200fe25Srmesta 
6578200fe25Srmesta 		case NETDB_INTERNAL:
6588200fe25Srmesta 		default:
6598200fe25Srmesta #ifdef	DEBUG
6608200fe25Srmesta 			syslog(LOG_ERR, "%s: Internal resolver error: %s",
6618200fe25Srmesta 			    whoami, strerror(errno));
6628200fe25Srmesta #endif
6638200fe25Srmesta 			goto thr_reset;
6648200fe25Srmesta 		}
665bfa62c28SVallish Vaidyeshwara 
666bfa62c28SVallish Vaidyeshwara 		resolv_destroy();
6678200fe25Srmesta 	}
6688200fe25Srmesta thr_reset:
6698200fe25Srmesta 	(void) rw_wrlock(&s_dns_data_lock);
6708200fe25Srmesta 	dns_txt_cached = 0;
6718200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
6728200fe25Srmesta 	resolv_txt_reset();
6738200fe25Srmesta 
6748200fe25Srmesta thr_okay:
675bfa62c28SVallish Vaidyeshwara 	resolv_destroy();
6768200fe25Srmesta 	/* mark thread as done */
6778200fe25Srmesta 	(void) rw_wrlock(&s_dns_impl_lock);
6788200fe25Srmesta 	s_dns_qthr_created = FALSE;
6798200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
6808200fe25Srmesta 
6818200fe25Srmesta 	(void) thr_exit(NULL);
6828200fe25Srmesta 	/*NOTREACHED*/
6838200fe25Srmesta 	return (NULL);
6848200fe25Srmesta }
6858200fe25Srmesta 
6868200fe25Srmesta /*
6878200fe25Srmesta  * nfsmapid's interface into the resolver for getting the TXT record.
6888200fe25Srmesta  *
6898200fe25Srmesta  * Key concepts:
6908200fe25Srmesta  *
6918200fe25Srmesta  * o If the DNS server is available and the TXT record is found, we
6928200fe25Srmesta  *   simply decode the output and fill the exported dns_txt_domain
6938200fe25Srmesta  *   global, so our caller can configure the daemon appropriately.
6948200fe25Srmesta  *
6958200fe25Srmesta  * o If the TXT record is not found, then having done resolv_txt_reset()
6968200fe25Srmesta  *   first will allow our caller to recognize that the exported globals
6978200fe25Srmesta  *   are empty and thus configure nfsmapid to use the default DNS domain.
6988200fe25Srmesta  *
6998200fe25Srmesta  * o Having no /etc/resolv.conf file is pretty much a show stopper, since
7008200fe25Srmesta  *   there is no name server address information. We return since we've
7018200fe25Srmesta  *   already have reset the TXT global state.
7028200fe25Srmesta  *
7038200fe25Srmesta  * o If a previous call to the DNS server resulted in an unrecoverable
7048200fe25Srmesta  *   error, then we disable further contact to the DNS server and return.
7058200fe25Srmesta  *   Having the TXT global state already reset guarantees that our caller
7068200fe25Srmesta  *   will fall back to the right configuration.
7078200fe25Srmesta  *
7088200fe25Srmesta  * o Query thread creation is throttled by s_dns_qthr_created. We mitigate
7098200fe25Srmesta  *   the problem of an already unresponsive DNS server by allowing at most
7108200fe25Srmesta  *   1 outstanding query thread since we could potentially have a substantial
7118200fe25Srmesta  *   amount of clients hammering on the same DNS server attempting to get
7128200fe25Srmesta  *   the TXT record.
7138200fe25Srmesta  */
7148200fe25Srmesta static void
7158200fe25Srmesta get_dns_txt_domain(cb_t *argp)
7168200fe25Srmesta {
7178200fe25Srmesta 	int		err;
7188200fe25Srmesta #ifdef	DEBUG
7198200fe25Srmesta 	static uint64_t	msg_done = 0;
7208200fe25Srmesta 	char		*whoami = "get_dns_txt_domain";
7218200fe25Srmesta #endif
7228200fe25Srmesta 	long		thr_flags = THR_DETACHED;
7238200fe25Srmesta 	struct stat	st;
7248200fe25Srmesta 
7258200fe25Srmesta 	/*
7268200fe25Srmesta 	 * We reset TXT variables first in case /etc/resolv.conf
7278200fe25Srmesta 	 * is missing or we've had unrecoverable resolver errors,
7288200fe25Srmesta 	 * we'll default to get_dns_domain(). If a previous DNS
7298200fe25Srmesta 	 * TXT RR was found, don't clear it until we're certain
7308200fe25Srmesta 	 * that contact can be made to the DNS server (see block
7318200fe25Srmesta 	 * comment atop resolv_txt_reset). If we're responding to
7328200fe25Srmesta 	 * a SIGHUP signal, force a reset of the cached copy.
7338200fe25Srmesta 	 */
7348200fe25Srmesta 	if (argp && argp->signal) {
7358200fe25Srmesta 		(void) rw_wrlock(&s_dns_data_lock);
7368200fe25Srmesta 		dns_txt_cached = 0;
7378200fe25Srmesta 		(void) rw_unlock(&s_dns_data_lock);
7388200fe25Srmesta 	}
7398200fe25Srmesta 	resolv_txt_reset();
7408200fe25Srmesta 
7418200fe25Srmesta 	errno = 0;
7428200fe25Srmesta 	if (stat(_PATH_RESCONF, &st) < 0 && errno == ENOENT) {
7438200fe25Srmesta 		/*
7448200fe25Srmesta 		 * If /etc/resolv.conf is not there, then we'll
7458200fe25Srmesta 		 * get the domain from domainname(1M). No real
7468200fe25Srmesta 		 * reason to query DNS or fire a thread since we
7478200fe25Srmesta 		 * have no nameserver addresses.
7488200fe25Srmesta 		 */
749bfa62c28SVallish Vaidyeshwara 		(void) rw_wrlock(&s_dns_data_lock);
750bfa62c28SVallish Vaidyeshwara 		dns_txt_cached = 0;
751bfa62c28SVallish Vaidyeshwara 		(void) rw_unlock(&s_dns_data_lock);
752bfa62c28SVallish Vaidyeshwara 		resolv_txt_reset();
753bfa62c28SVallish Vaidyeshwara 		return;
7548200fe25Srmesta 	}
7558200fe25Srmesta 
7568200fe25Srmesta 	(void) rw_rdlock(&s_dns_impl_lock);
7578200fe25Srmesta 	if (s_dns_disabled) {
7588200fe25Srmesta 		/*
7598200fe25Srmesta 		 * If there were non-recoverable problems with DNS,
7608200fe25Srmesta 		 * we have stopped querying DNS entirely. See
7618200fe25Srmesta 		 * NO_RECOVERY clause below.
7628200fe25Srmesta 		 */
7638200fe25Srmesta #ifdef	DEBUG
7648200fe25Srmesta 		syslog(LOG_ERR, "%s: DNS queries disabled", whoami);
7658200fe25Srmesta #endif
7668200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
7678200fe25Srmesta 		return;
7688200fe25Srmesta 	}
7698200fe25Srmesta 	(void) rw_unlock(&s_dns_impl_lock);
7708200fe25Srmesta 
771*6d9307f9SVallish Vaidyeshwara 	if (resolv_init() < 0) {
772*6d9307f9SVallish Vaidyeshwara 		/*
773*6d9307f9SVallish Vaidyeshwara 		 * Failed to initialize resolver. Do not
774*6d9307f9SVallish Vaidyeshwara 		 * query DNS server.
775*6d9307f9SVallish Vaidyeshwara 		 */
776*6d9307f9SVallish Vaidyeshwara 		(void) rw_wrlock(&s_dns_data_lock);
777*6d9307f9SVallish Vaidyeshwara 		dns_txt_cached = 0;
778*6d9307f9SVallish Vaidyeshwara 		(void) rw_unlock(&s_dns_data_lock);
779*6d9307f9SVallish Vaidyeshwara 		resolv_txt_reset();
780*6d9307f9SVallish Vaidyeshwara 		return;
781*6d9307f9SVallish Vaidyeshwara 	}
7828200fe25Srmesta 	switch (resolv_search()) {
7838200fe25Srmesta 	case NETDB_SUCCESS:
7848200fe25Srmesta 		/*
7858200fe25Srmesta 		 * If there _is_ a TXT record, we let
7868200fe25Srmesta 		 * our caller set the global state.
7878200fe25Srmesta 		 */
7888200fe25Srmesta 		resolv_decode();
7898200fe25Srmesta 		resolv_get_txt_data();
790bfa62c28SVallish Vaidyeshwara 		break;
7918200fe25Srmesta 
7928200fe25Srmesta 	case TRY_AGAIN:
7938200fe25Srmesta 		if (argp == NULL || argp->fcn == NULL)
7948200fe25Srmesta 			/*
7958200fe25Srmesta 			 * If no valid argument was passed or
7968200fe25Srmesta 			 * callback defined, don't fire thread
7978200fe25Srmesta 			 */
798bfa62c28SVallish Vaidyeshwara 			break;
7998200fe25Srmesta 
8008200fe25Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
8018200fe25Srmesta 		if (s_dns_qthr_created) {
8028200fe25Srmesta 			/*
8038200fe25Srmesta 			 * We may have lots of clients, so we don't
8048200fe25Srmesta 			 * want to bog down the DNS server with tons
8058200fe25Srmesta 			 * of requests... lest it becomes even more
8068200fe25Srmesta 			 * unresponsive, so limit 1 thread to query
8078200fe25Srmesta 			 * DNS at a time.
8088200fe25Srmesta 			 */
8098200fe25Srmesta #ifdef	DEBUG
8108200fe25Srmesta 			syslog(LOG_ERR, "%s: query thread already active",
8118200fe25Srmesta 			    whoami);
8128200fe25Srmesta #endif
8138200fe25Srmesta 			(void) rw_unlock(&s_dns_impl_lock);
814bfa62c28SVallish Vaidyeshwara 			break;
8158200fe25Srmesta 		}
8168200fe25Srmesta 
8178200fe25Srmesta 		/*
8188200fe25Srmesta 		 * DNS did not respond ! Set timeout and kick off
8198200fe25Srmesta 		 * thread to try op again after s_dns_tout seconds.
8208200fe25Srmesta 		 * We've made sure that we don't have an already
8218200fe25Srmesta 		 * running thread above.
8228200fe25Srmesta 		 */
8238200fe25Srmesta 		s_dns_tout = NFSMAPID_DNS_TOUT_SECS;
8248200fe25Srmesta 		err = thr_create(NULL, 0, resolv_query_thread, (void *)argp,
8258200fe25Srmesta 		    thr_flags, &s_dns_qthread);
8268200fe25Srmesta 		if (!err) {
8278200fe25Srmesta 			s_dns_qthr_created = TRUE;
8288200fe25Srmesta 		}
8298200fe25Srmesta #ifdef DEBUG
8308200fe25Srmesta 		else {
8318200fe25Srmesta 			msg_done++;
8328200fe25Srmesta 			if (!(msg_done % NFSMAPID_SLOG_RATE))
8338200fe25Srmesta 				syslog(LOG_ERR, EMSG_DNS_THREAD_ERROR);
8348200fe25Srmesta 		}
8358200fe25Srmesta #endif
8368200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
837bfa62c28SVallish Vaidyeshwara 		break;
8388200fe25Srmesta 
8398200fe25Srmesta 	case NO_RECOVERY:
8408200fe25Srmesta #ifdef	DEBUG
8418200fe25Srmesta 		syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami);
8428200fe25Srmesta #endif
8438200fe25Srmesta 		(void) rw_wrlock(&s_dns_impl_lock);
8448200fe25Srmesta 		s_dns_disabled = TRUE;
8458200fe25Srmesta 		(void) rw_unlock(&s_dns_impl_lock);
8468200fe25Srmesta 
8478200fe25Srmesta 		/*FALLTHROUGH*/
8488200fe25Srmesta 
8498200fe25Srmesta 	default:
8508200fe25Srmesta 		/*
8518200fe25Srmesta 		 * For any other errors... DNS is responding, but
8528200fe25Srmesta 		 * either it has no data, or some other problem is
8538200fe25Srmesta 		 * occuring. At any rate, the TXT domain should not
8548200fe25Srmesta 		 * be used, so we default to the DNS domain.
8558200fe25Srmesta 		 */
8568200fe25Srmesta 		(void) rw_wrlock(&s_dns_data_lock);
8578200fe25Srmesta 		dns_txt_cached = 0;
8588200fe25Srmesta 		(void) rw_unlock(&s_dns_data_lock);
8598200fe25Srmesta 		resolv_txt_reset();
860bfa62c28SVallish Vaidyeshwara 		break;
861bfa62c28SVallish Vaidyeshwara 	}
862bfa62c28SVallish Vaidyeshwara 
863bfa62c28SVallish Vaidyeshwara 	resolv_destroy();
8648200fe25Srmesta }
8658200fe25Srmesta 
8668200fe25Srmesta static int
8678200fe25Srmesta get_mtime(const char *fname, timestruc_t *mtim)
8688200fe25Srmesta {
8698200fe25Srmesta 	struct stat st;
8708200fe25Srmesta 	int err;
8718200fe25Srmesta 
8728200fe25Srmesta 	if ((err = stat(fname, &st)) != 0)
8738200fe25Srmesta 		return (err);
8748200fe25Srmesta 
8758200fe25Srmesta 	*mtim = st.st_mtim;
8768200fe25Srmesta 	return (0);
8778200fe25Srmesta }
8788200fe25Srmesta 
8798200fe25Srmesta 
8808200fe25Srmesta /*
8818200fe25Srmesta  * trim_wspace is a destructive interface; it is up to
8828200fe25Srmesta  * the caller to save off an original copy if needed.
8838200fe25Srmesta  */
8848200fe25Srmesta static char *
8858200fe25Srmesta trim_wspace(char *dp)
8868200fe25Srmesta {
8878200fe25Srmesta 	char	*r;
8888200fe25Srmesta 	char	*ndp;
8898200fe25Srmesta 
8908200fe25Srmesta 	/*
8918200fe25Srmesta 	 * Any empty domain is not valid
8928200fe25Srmesta 	 */
8938200fe25Srmesta 	if (dp == NULL)
8948200fe25Srmesta 		return (NULL);
8958200fe25Srmesta 
8968200fe25Srmesta 	/*
8978200fe25Srmesta 	 * Skip leading blanks
8988200fe25Srmesta 	 */
8998200fe25Srmesta 	for (ndp = dp; *ndp != '\0'; ndp++) {
9008200fe25Srmesta 		if (!isspace(*ndp))
9018200fe25Srmesta 			break;
9028200fe25Srmesta 	}
9038200fe25Srmesta 
9048200fe25Srmesta 	/*
9058200fe25Srmesta 	 * If we reached the end of the string w/o
9068200fe25Srmesta 	 * finding a non-blank char, return error
9078200fe25Srmesta 	 */
9088200fe25Srmesta 	if (*ndp == '\0')
9098200fe25Srmesta 		return (NULL);
9108200fe25Srmesta 
9118200fe25Srmesta 	/*
9128200fe25Srmesta 	 * Find next blank in string
9138200fe25Srmesta 	 */
9148200fe25Srmesta 	for (r = ndp; *r != '\0'; r++) {
9158200fe25Srmesta 		if (isspace(*r))
9168200fe25Srmesta 			break;
9178200fe25Srmesta 	}
9188200fe25Srmesta 
9198200fe25Srmesta 	/*
9208200fe25Srmesta 	 * No more blanks found, we are done
9218200fe25Srmesta 	 */
9228200fe25Srmesta 	if (*r == '\0')
9238200fe25Srmesta 		return (ndp);
9248200fe25Srmesta 
9258200fe25Srmesta 	/*
9268200fe25Srmesta 	 * Terminate string at blank
9278200fe25Srmesta 	 */
9288200fe25Srmesta 	*r++ = '\0';
9298200fe25Srmesta 
9308200fe25Srmesta 	/*
9318200fe25Srmesta 	 * Skip any trailing spaces
9328200fe25Srmesta 	 */
9338200fe25Srmesta 	while (*r != '\0') {
9348200fe25Srmesta 		/*
9358200fe25Srmesta 		 * If a non-blank is found, it is an
9368200fe25Srmesta 		 * illegal domain (embedded blanks).
9378200fe25Srmesta 		 */
9388200fe25Srmesta 		if (!isspace(*r))
9398200fe25Srmesta 			return (NULL);
9408200fe25Srmesta 		r++;
9418200fe25Srmesta 	}
9428200fe25Srmesta 	return (ndp);
9438200fe25Srmesta }
9448200fe25Srmesta 
9458200fe25Srmesta static void
9468200fe25Srmesta get_nfs_domain(void)
9478200fe25Srmesta {
9488200fe25Srmesta 	char		*ndomain;
9498200fe25Srmesta 	timestruc_t	 ntime;
950b9175c69SKenjiro Tsuji 	void	*defp;
9518200fe25Srmesta 
9528200fe25Srmesta 	/*
9538200fe25Srmesta 	 * If we can't get stats for the config file, then
9548200fe25Srmesta 	 * zap the NFS domain info.  If mtime hasn't changed,
9558200fe25Srmesta 	 * then there's no work to do, so just return.
9568200fe25Srmesta 	 */
9578200fe25Srmesta 	if (get_mtime(NFSADMIN, &ntime) != 0) {
9588200fe25Srmesta 		ZAP_DOMAIN(nfs);
9598200fe25Srmesta 		return;
9608200fe25Srmesta 	}
9618200fe25Srmesta 
9628200fe25Srmesta 	if (TIMESTRUC_EQ(ntime, nfs_mtime))
9638200fe25Srmesta 		return;
9648200fe25Srmesta 
9658200fe25Srmesta 	/*
9668200fe25Srmesta 	 * Get NFSMAPID_DOMAIN value from /etc/default/nfs for now.
967b9175c69SKenjiro Tsuji 	 * Note: defread_r() returns a ptr to libc internal malloc.
9688200fe25Srmesta 	 */
969b9175c69SKenjiro Tsuji 	if ((defp = defopen_r(NFSADMIN)) != NULL) {
9708200fe25Srmesta 		char	*dp = NULL;
9718200fe25Srmesta #ifdef	DEBUG
9728200fe25Srmesta 		char	*whoami = "get_nfs_domain";
9738200fe25Srmesta 		char	 orig[NS_MAXCDNAME] = {0};
9748200fe25Srmesta #endif
975b9175c69SKenjiro Tsuji 		ndomain = defread_r("NFSMAPID_DOMAIN=", defp);
9768200fe25Srmesta #ifdef	DEBUG
9778200fe25Srmesta 		if (ndomain)
9788200fe25Srmesta 			(void) strncpy(orig, ndomain, NS_MAXCDNAME);
9798200fe25Srmesta #endif
9808200fe25Srmesta 		/*
9818200fe25Srmesta 		 * NFSMAPID_DOMAIN was set, so it's time for validation. If
9828200fe25Srmesta 		 * it's okay, then update NFS domain and return. If not,
9838200fe25Srmesta 		 * bail (syslog in DEBUG). We make nfsmapid more a bit
9848200fe25Srmesta 		 * more forgiving of trailing and leading white space.
9858200fe25Srmesta 		 */
9868200fe25Srmesta 		if ((dp = trim_wspace(ndomain)) != NULL) {
9878200fe25Srmesta 			if (mapid_stdchk_domain(dp) > 0) {
9888200fe25Srmesta 				nfs_domain_len = strlen(dp);
9898200fe25Srmesta 				(void) strncpy(nfs_domain, dp, NS_MAXCDNAME);
9908200fe25Srmesta 				nfs_domain[NS_MAXCDNAME] = '\0';
9918200fe25Srmesta 				nfs_mtime = ntime;
992b9175c69SKenjiro Tsuji 				defclose_r(defp);
9938200fe25Srmesta 				return;
9948200fe25Srmesta 			}
9958200fe25Srmesta 		}
996b9175c69SKenjiro Tsuji 		defclose_r(defp);
9978200fe25Srmesta #ifdef	DEBUG
9988200fe25Srmesta 		if (orig[0] != '\0') {
9998200fe25Srmesta 			syslog(LOG_ERR, gettext("%s: Invalid domain name \"%s\""
10008200fe25Srmesta 			    " found in configuration file."), whoami, orig);
10018200fe25Srmesta 		}
10028200fe25Srmesta #endif
10038200fe25Srmesta 	}
10048200fe25Srmesta 
10058200fe25Srmesta 	/*
10068200fe25Srmesta 	 * So the NFS config file changed but it couldn't be opened or
10078200fe25Srmesta 	 * it didn't specify NFSMAPID_DOMAIN or it specified an invalid
10088200fe25Srmesta 	 * NFSMAPID_DOMAIN.  Time to zap current NFS domain info.
10098200fe25Srmesta 	 */
10108200fe25Srmesta 	ZAP_DOMAIN(nfs);
10118200fe25Srmesta }
10128200fe25Srmesta 
10138200fe25Srmesta static void
10148200fe25Srmesta get_dns_domain(void)
10158200fe25Srmesta {
10168200fe25Srmesta 	timestruc_t	 ntime = {0};
10178200fe25Srmesta 
10188200fe25Srmesta 	/*
10198200fe25Srmesta 	 * If we can't get stats for the config file, then
10208200fe25Srmesta 	 * zap the DNS domain info.  If mtime hasn't changed,
10218200fe25Srmesta 	 * then there's no work to do, so just return.
10228200fe25Srmesta 	 */
10238200fe25Srmesta 	errno = 0;
10248200fe25Srmesta 	if (get_mtime(_PATH_RESCONF, &ntime) != 0) {
10258200fe25Srmesta 		switch (errno) {
10268200fe25Srmesta 			case ENOENT:
10278200fe25Srmesta 				/*
10288200fe25Srmesta 				 * The resolver defaults to obtaining the
10298200fe25Srmesta 				 * domain off of the NIS domainname(1M) if
10308200fe25Srmesta 				 * /etc/resolv.conf does not exist, so we
10318200fe25Srmesta 				 * move forward.
10328200fe25Srmesta 				 */
10338200fe25Srmesta 				break;
10348200fe25Srmesta 
10358200fe25Srmesta 			default:
10368200fe25Srmesta 				ZAP_DOMAIN(dns);
10378200fe25Srmesta 				return;
10388200fe25Srmesta 		}
10398200fe25Srmesta 	} else if (TIMESTRUC_EQ(ntime, dns_mtime))
10408200fe25Srmesta 		return;
10418200fe25Srmesta 
10428200fe25Srmesta 	/*
10438200fe25Srmesta 	 * Re-initialize resolver to zap DNS domain from previous
10448200fe25Srmesta 	 * resolv_init() calls.
10458200fe25Srmesta 	 */
10468200fe25Srmesta 	(void) resolv_init();
10478200fe25Srmesta 
10488200fe25Srmesta 	/*
10498200fe25Srmesta 	 * Update cached DNS domain.  No need for validation since
10508200fe25Srmesta 	 * domain comes from resolver.  If resolver doesn't return the
10518200fe25Srmesta 	 * domain, then zap the DNS domain.  This shouldn't ever happen,
10528200fe25Srmesta 	 * and if it does, the machine has bigger problems (so no need
10538200fe25Srmesta 	 * to generate a message that says DNS appears to be broken).
10548200fe25Srmesta 	 */
10558200fe25Srmesta 	(void) rw_rdlock(&s_dns_data_lock);
10568200fe25Srmesta 	if (sysdns_domain[0] != '\0') {
10578200fe25Srmesta 		(void) strncpy(dns_domain, sysdns_domain, NS_MAXCDNAME);
10588200fe25Srmesta 		dns_domain_len = strlen(sysdns_domain);
10598200fe25Srmesta 		(void) rw_unlock(&s_dns_data_lock);
10608200fe25Srmesta 		dns_mtime = ntime;
1061bfa62c28SVallish Vaidyeshwara 		resolv_destroy();
10628200fe25Srmesta 		return;
10638200fe25Srmesta 	}
10648200fe25Srmesta 	(void) rw_unlock(&s_dns_data_lock);
10658200fe25Srmesta 
10668200fe25Srmesta 	ZAP_DOMAIN(dns);
1067bfa62c28SVallish Vaidyeshwara 
1068bfa62c28SVallish Vaidyeshwara 	resolv_destroy();
1069bfa62c28SVallish Vaidyeshwara 
10708200fe25Srmesta }
10718200fe25Srmesta 
10728200fe25Srmesta /*
10738200fe25Srmesta  * PSARC 2005/487 Contracted Sun Private Interface
10748200fe25Srmesta  * mapid_stdchk_domain()
10758200fe25Srmesta  * Changes must be reviewed by Solaris File Sharing
10768200fe25Srmesta  * Changes must be communicated to contract-2005-487-01@sun.com
10778200fe25Srmesta  *
10788200fe25Srmesta  * Based on the recommendations from RFC1033 and RFC1035, check
10798200fe25Srmesta  * if a given domain name string is valid. Return values are:
10808200fe25Srmesta  *
10818200fe25Srmesta  *       1 = valid domain name
10828200fe25Srmesta  *       0 = invalid domain name (or invalid embedded character)
10838200fe25Srmesta  *      -1 = domain length > NS_MAXCDNAME
10848200fe25Srmesta  */
10858200fe25Srmesta int
10868200fe25Srmesta mapid_stdchk_domain(const char *ds)
10878200fe25Srmesta {
10888200fe25Srmesta 	int	i;
10898200fe25Srmesta 	size_t	len;
10908200fe25Srmesta 
10918200fe25Srmesta 	if (ds[0] == '\0')
10928200fe25Srmesta 		return (0);
10938200fe25Srmesta 	else
10948200fe25Srmesta 		len = strlen(ds) - 1;
10958200fe25Srmesta 
10968200fe25Srmesta 	/*
10977bcac252Sevanl 	 * 1st _AND_ last char _must_ be alphanumeric.
10987bcac252Sevanl 	 * We check for other valid chars below.
10998200fe25Srmesta 	 */
11007bcac252Sevanl 	if ((!isalpha(ds[0]) && !isdigit(ds[0])) ||
11017bcac252Sevanl 	    (!isalpha(ds[len]) && !isdigit(ds[len])))
11028200fe25Srmesta 		return (0);
11038200fe25Srmesta 
11048200fe25Srmesta 	for (i = 0; *ds && i <= NS_MAXCDNAME; i++, ds++) {
11058200fe25Srmesta 		if (!isalpha(*ds) && !isdigit(*ds) &&
11068200fe25Srmesta 		    (*ds != '.') && (*ds != '-') && (*ds != '_'))
11078200fe25Srmesta 			return (0);
11088200fe25Srmesta 	}
11098200fe25Srmesta 	return (i == (NS_MAXCDNAME + 1) ? -1 : 1);
11108200fe25Srmesta }
11118200fe25Srmesta 
11128200fe25Srmesta /*
11138200fe25Srmesta  * PSARC 2005/487 Consolidation Private
11148200fe25Srmesta  * mapid_reeval_domain()
11158200fe25Srmesta  * Changes must be reviewed by Solaris File Sharing
11168200fe25Srmesta  */
11178200fe25Srmesta void
11188200fe25Srmesta mapid_reeval_domain(cb_t *arg)
11198200fe25Srmesta {
11208200fe25Srmesta 	char	*domain = NULL;
11218200fe25Srmesta 
11228200fe25Srmesta 	get_nfs_domain();
11238200fe25Srmesta 	if (nfs_domain_len != 0) {
11248200fe25Srmesta 		domain = nfs_domain;
11258200fe25Srmesta 		goto dsync;
11268200fe25Srmesta 	}
11278200fe25Srmesta 
11288200fe25Srmesta 	get_dns_txt_domain(arg);
11298200fe25Srmesta 	if (dns_txt_domain_len != 0)
11308200fe25Srmesta 		domain = dns_txt_domain;
11318200fe25Srmesta 	else {
11328200fe25Srmesta 		/*
11338200fe25Srmesta 		 * We're either here because:
11348200fe25Srmesta 		 *
11358200fe25Srmesta 		 *  . NFSMAPID_DOMAIN was not set in /etc/default/nfs
11368200fe25Srmesta 		 *  . No suitable DNS TXT resource record exists
11378200fe25Srmesta 		 *  . DNS server is not responding to requests
11388200fe25Srmesta 		 *
11398200fe25Srmesta 		 * in either case, we want to default to using the
11408200fe25Srmesta 		 * system configured DNS domain. If this fails, then
11418200fe25Srmesta 		 * dns_domain will be empty and dns_domain_len will
11428200fe25Srmesta 		 * be 0.
11438200fe25Srmesta 		 */
11448200fe25Srmesta 		get_dns_domain();
11458200fe25Srmesta 		domain = dns_domain;
11468200fe25Srmesta 	}
11478200fe25Srmesta 
11488200fe25Srmesta dsync:
11498200fe25Srmesta 	domain_sync(arg, domain);
11508200fe25Srmesta }
11518200fe25Srmesta 
11528200fe25Srmesta /*
11538200fe25Srmesta  * PSARC 2005/487 Consolidation Private
11548200fe25Srmesta  * mapid_get_domain()
11558200fe25Srmesta  * Changes must be reviewed by Solaris File Sharing
11568200fe25Srmesta  *
11578200fe25Srmesta  * The use of TSD in mapid_get_domain() diverges slightly from the typical
11588200fe25Srmesta  * TSD use, since here, the benefit of doing TSD is mostly to allocate
11598200fe25Srmesta  * a per-thread buffer that will be utilized by other up-calls to the
11608200fe25Srmesta  * daemon.
11618200fe25Srmesta  *
11628200fe25Srmesta  * In doors, the thread used for the upcall never really exits, hence
11638200fe25Srmesta  * the typical destructor function defined via thr_keycreate() will
11648200fe25Srmesta  * never be called. Thus, we only use TSD to allocate the per-thread
11658200fe25Srmesta  * buffer and fill it up w/the configured 'mapid_domain' on each call.
11668200fe25Srmesta  * This still alleviates the problem of having the caller free any
11678200fe25Srmesta  * malloc'd space.
11688200fe25Srmesta  */
11698200fe25Srmesta char *
11708200fe25Srmesta mapid_get_domain(void)
11718200fe25Srmesta {
11728200fe25Srmesta 	void	*tsd = NULL;
11738200fe25Srmesta 
11748200fe25Srmesta 	(void) thr_getspecific(s_thr_key, &tsd);
11758200fe25Srmesta 	if (tsd == NULL) {
11768200fe25Srmesta 		tsd = malloc(NS_MAXCDNAME+1);
11778200fe25Srmesta 		if (tsd != NULL) {
11788200fe25Srmesta 			(void) rw_rdlock(&mapid_domain_lock);
11798200fe25Srmesta 			(void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME);
11808200fe25Srmesta 			(void) rw_unlock(&mapid_domain_lock);
11818200fe25Srmesta 			(void) thr_setspecific(s_thr_key, tsd);
11828200fe25Srmesta 		}
11838200fe25Srmesta 	} else {
11848200fe25Srmesta 		(void) rw_rdlock(&mapid_domain_lock);
11858200fe25Srmesta 		(void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME);
11868200fe25Srmesta 		(void) rw_unlock(&mapid_domain_lock);
11878200fe25Srmesta 	}
11888200fe25Srmesta 	return ((char *)tsd);
11898200fe25Srmesta }
11908200fe25Srmesta 
11918200fe25Srmesta /*
11928200fe25Srmesta  * PSARC 2005/487 Contracted Sun Private Interface
11938200fe25Srmesta  * mapid_derive_domain()
11948200fe25Srmesta  * Changes must be reviewed by Solaris File Sharing
11958200fe25Srmesta  * Changes must be communicated to contract-2005-487-01@sun.com
11968200fe25Srmesta  *
11978200fe25Srmesta  * This interface is called solely via sysidnfs4 iff no
11988200fe25Srmesta  * NFSMAPID_DOMAIN was found. So, there is no ill effect
11998200fe25Srmesta  * of having the reeval function call get_nfs_domain().
12008200fe25Srmesta  */
12018200fe25Srmesta char *
12028200fe25Srmesta mapid_derive_domain(void)
12038200fe25Srmesta {
12048200fe25Srmesta 	cb_t	cb = {0};
12058200fe25Srmesta 
12068200fe25Srmesta 	_lib_init();
12078200fe25Srmesta 	mapid_reeval_domain(&cb);
12088200fe25Srmesta 	return (mapid_get_domain());
12098200fe25Srmesta }
12108200fe25Srmesta 
12118200fe25Srmesta void
12128200fe25Srmesta _lib_init(void)
12138200fe25Srmesta {
1214*6d9307f9SVallish Vaidyeshwara 	(void) resolv_init(); /* May fail! */
12158200fe25Srmesta 	(void) rwlock_init(&mapid_domain_lock, USYNC_THREAD, NULL);
12168200fe25Srmesta 	(void) thr_keycreate(&s_thr_key, NULL);
12178200fe25Srmesta 	lib_init_done++;
1218bfa62c28SVallish Vaidyeshwara 	resolv_destroy();
12198200fe25Srmesta }
1220*6d9307f9SVallish Vaidyeshwara 
1221*6d9307f9SVallish Vaidyeshwara void
1222*6d9307f9SVallish Vaidyeshwara _lib_fini(void)
1223*6d9307f9SVallish Vaidyeshwara {
1224*6d9307f9SVallish Vaidyeshwara 	resolv_destroy();
1225*6d9307f9SVallish Vaidyeshwara }
1226