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