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