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