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