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