1*1741Srmesta /* 2*1741Srmesta * CDDL HEADER START 3*1741Srmesta * 4*1741Srmesta * The contents of this file are subject to the terms of the 5*1741Srmesta * Common Development and Distribution License (the "License"). 6*1741Srmesta * You may not use this file except in compliance with the License. 7*1741Srmesta * 8*1741Srmesta * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*1741Srmesta * or http://www.opensolaris.org/os/licensing. 10*1741Srmesta * See the License for the specific language governing permissions 11*1741Srmesta * and limitations under the License. 12*1741Srmesta * 13*1741Srmesta * When distributing Covered Code, include this CDDL HEADER in each 14*1741Srmesta * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*1741Srmesta * If applicable, add the following below this CDDL HEADER, with the 16*1741Srmesta * fields enclosed by brackets "[]" replaced with your own identifying 17*1741Srmesta * information: Portions Copyright [yyyy] [name of copyright owner] 18*1741Srmesta * 19*1741Srmesta * CDDL HEADER END 20*1741Srmesta */ 21*1741Srmesta /* 22*1741Srmesta * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23*1741Srmesta * Use is subject to license terms. 24*1741Srmesta */ 25*1741Srmesta 26*1741Srmesta #pragma ident "%Z%%M% %I% %E% SMI" 27*1741Srmesta 28*1741Srmesta /* 29*1741Srmesta * PSARC/2004/154 nfsmapid DNS enhancements implementation. 30*1741Srmesta * 31*1741Srmesta * As per RFC 3530, file owner and group attributes in version 4 of the 32*1741Srmesta * NFS protocol are no longer exchanged between client and server as 32 33*1741Srmesta * bit integral values. Instead, owner and group file attributes are 34*1741Srmesta * exchanged between client and server as UTF8 strings of form 35*1741Srmesta * 36*1741Srmesta * 'user@domain' (ie. "joeblow@central.sun.com") 37*1741Srmesta * 'group@domain' (ie. "staff@central.sun.com") 38*1741Srmesta * 39*1741Srmesta * This NFSv4 feature is far beyond anything NFSv2/v3 ever provided, as 40*1741Srmesta * being able to describe a user with a unique string identifier provides 41*1741Srmesta * a much more powerful and administrative friendly way of dealing with 42*1741Srmesta * overlaps in the uid/gid number spaces. That notwithstanding, dealing 43*1741Srmesta * with issues of correctly mapping user and group ownership in a cross- 44*1741Srmesta * domain environment has proven a difficult problem to solve, since 45*1741Srmesta * dealing with different permutations of client naming configurations 46*1741Srmesta * (ie. NIS only, LDAP only, etc.) have bloated the problem. Thus, users 47*1741Srmesta * utilizing clients and servers that have the 'domain' portion of the 48*1741Srmesta * UTF8 attribute string configured differently than its peer server and 49*1741Srmesta * client accordingly, will experience watching their files owned by the 50*1741Srmesta * 'nobody' user and group. This is due to the fact that the 'domain's 51*1741Srmesta * don't match and the nfsmapid daemon treats the attribute strings as 52*1741Srmesta * unknown user(s) or group(s) (even though the actual uid/gid's may exist 53*1741Srmesta * in the executing daemon's system). Please refer to PSARC/2004/154 for 54*1741Srmesta * further background and motivation for these enhancements. 55*1741Srmesta * 56*1741Srmesta * The latest implementation of the nfsmapid daemon relies on a DNS TXT 57*1741Srmesta * record. The behavior of nfsmapid is to first use the NFSMAPID_DOMAIN 58*1741Srmesta * configuration option in /etc/default/nfs. If the option has not been 59*1741Srmesta * set, then the nfsmapid daemon queries the configured DNS domain server 60*1741Srmesta * for the _nfsv4idmapdomain TXT record. If the record exists, then the 61*1741Srmesta * record's value is used as the 'domain' portion of the UTF8 attribute 62*1741Srmesta * strings. If the TXT record has not been configured in the DNS server, 63*1741Srmesta * then the daemon falls back to using the DNS domain name itself as the 64*1741Srmesta * 'domain' portion of the attribute strings. Lastly, if the configured 65*1741Srmesta * DNS server is unresponsive, the nfsmapid daemon falls back to using 66*1741Srmesta * the DNS domain name as the 'domain' portion of the attribute strings, 67*1741Srmesta * and fires up a query thread to keep contacting the DNS server until 68*1741Srmesta * it responds with either a TXT record, or a lack thereof, in which 69*1741Srmesta * case, nfsmapid just continues to utilize the DNS domain name. 70*1741Srmesta */ 71*1741Srmesta #define __LIBMAPID_IMPL 72*1741Srmesta #include <nfs/mapid.h> 73*1741Srmesta #pragma init(_lib_init) 74*1741Srmesta 75*1741Srmesta /* 76*1741Srmesta * DEBUG Only 77*1741Srmesta * Decode any resolver errors and print out message to log 78*1741Srmesta */ 79*1741Srmesta static int 80*1741Srmesta resolv_error(void) 81*1741Srmesta { 82*1741Srmesta #ifndef DEBUG 83*1741Srmesta 84*1741Srmesta return (h_errno); 85*1741Srmesta 86*1741Srmesta #else /* DEBUG */ 87*1741Srmesta 88*1741Srmesta static uint64_t msg_done[NS_ERRS] = {0}; 89*1741Srmesta 90*1741Srmesta switch (h_errno) { 91*1741Srmesta case NETDB_INTERNAL: 92*1741Srmesta syslog(LOG_ERR, EMSG_NETDB_INTERNAL, strerror(errno)); 93*1741Srmesta break; 94*1741Srmesta 95*1741Srmesta case HOST_NOT_FOUND: 96*1741Srmesta (void) rw_rdlock(&s_dns_impl_lock); 97*1741Srmesta msg_done[h_errno]++; 98*1741Srmesta if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE)) 99*1741Srmesta syslog(LOG_ERR, EMSG_HOST_NOT_FOUND, s_dname); 100*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 101*1741Srmesta break; 102*1741Srmesta 103*1741Srmesta case TRY_AGAIN: 104*1741Srmesta /* 105*1741Srmesta * Nameserver is not responding. 106*1741Srmesta * Try again after a given timeout. 107*1741Srmesta */ 108*1741Srmesta (void) rw_rdlock(&s_dns_impl_lock); 109*1741Srmesta msg_done[h_errno]++; 110*1741Srmesta if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE)) 111*1741Srmesta syslog(LOG_ERR, EMSG_TRY_AGAIN, s_dname); 112*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 113*1741Srmesta break; 114*1741Srmesta 115*1741Srmesta case NO_RECOVERY: 116*1741Srmesta /* 117*1741Srmesta * This msg only really happens once, due 118*1741Srmesta * to s_dns_disabled flag (see below) 119*1741Srmesta */ 120*1741Srmesta syslog(LOG_ERR, EMSG_NO_RECOVERY, hstrerror(h_errno)); 121*1741Srmesta break; 122*1741Srmesta 123*1741Srmesta case NO_DATA: 124*1741Srmesta /* 125*1741Srmesta * No entries in the nameserver for 126*1741Srmesta * the specific record or record type. 127*1741Srmesta */ 128*1741Srmesta (void) rw_rdlock(&s_dns_impl_lock); 129*1741Srmesta msg_done[h_errno]++; 130*1741Srmesta if (!(msg_done[h_errno] % NFSMAPID_SLOG_RATE)) 131*1741Srmesta syslog(LOG_ERR, EMSG_NO_DATA, NFSMAPID_DNS_RR, s_dname); 132*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 133*1741Srmesta break; 134*1741Srmesta 135*1741Srmesta case NETDB_SUCCESS: 136*1741Srmesta default: 137*1741Srmesta break; 138*1741Srmesta } 139*1741Srmesta return (h_errno); 140*1741Srmesta 141*1741Srmesta #endif /* DEBUG */ 142*1741Srmesta } 143*1741Srmesta 144*1741Srmesta /* 145*1741Srmesta * Reset the global state variables used for the TXT record. 146*1741Srmesta * Having these values reset to zero helps nfsmapid confirm 147*1741Srmesta * that a valid DNS TXT record was not found; in which case, 148*1741Srmesta * it would fall back to using the configured DNS domain name. 149*1741Srmesta * 150*1741Srmesta * If a valid DNS TXT record _was_ found, but subsequent contact 151*1741Srmesta * to the DNS server is somehow hindered, the previous DNS TXT 152*1741Srmesta * RR value continues to be used. Thus, in such instances, we 153*1741Srmesta * forego clearing the global config variables so nfsmapid can 154*1741Srmesta * continue to use a valid DNS TXT RR while contact to the DNS 155*1741Srmesta * server is reestablished. 156*1741Srmesta */ 157*1741Srmesta static void 158*1741Srmesta resolv_txt_reset(void) 159*1741Srmesta { 160*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 161*1741Srmesta bzero(s_txt_rr, sizeof (s_txt_rr)); 162*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 163*1741Srmesta 164*1741Srmesta (void) rw_wrlock(&s_dns_data_lock); 165*1741Srmesta if (!dns_txt_cached) { 166*1741Srmesta dns_txt_domain_len = 0; 167*1741Srmesta bzero(dns_txt_domain, DNAMEMAX); 168*1741Srmesta } 169*1741Srmesta (void) rw_unlock(&s_dns_data_lock); 170*1741Srmesta } 171*1741Srmesta 172*1741Srmesta /* 173*1741Srmesta * Initialize resolver and populate &s_res struct 174*1741Srmesta * 175*1741Srmesta * DNS Domain is saved off sysdns_domain in case we 176*1741Srmesta * need to fall back to using the DNS domain name as 177*1741Srmesta * the v4 attribute string domain. 178*1741Srmesta */ 179*1741Srmesta static int 180*1741Srmesta resolv_init(void) 181*1741Srmesta { 182*1741Srmesta size_t len; 183*1741Srmesta int n; 184*1741Srmesta struct __res_state res; 185*1741Srmesta 186*1741Srmesta (void) mutex_lock(&s_res_lock); 187*1741Srmesta bzero(&s_res, sizeof (struct __res_state)); 188*1741Srmesta n = h_errno = errno = 0; 189*1741Srmesta if ((n = res_ninit(&s_res)) < 0) { 190*1741Srmesta (void) mutex_unlock(&s_res_lock); 191*1741Srmesta (void) resolv_error(); 192*1741Srmesta return (n); 193*1741Srmesta } 194*1741Srmesta res = s_res; 195*1741Srmesta (void) mutex_unlock(&s_res_lock); 196*1741Srmesta 197*1741Srmesta len = strlen(res.defdname) + 1; 198*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 199*1741Srmesta bzero(s_dname, sizeof (s_dname)); 200*1741Srmesta (void) snprintf(s_dname, len, "%s", res.defdname); 201*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 202*1741Srmesta 203*1741Srmesta (void) rw_wrlock(&s_dns_data_lock); 204*1741Srmesta (void) snprintf(sysdns_domain, len, "%s", res.defdname); 205*1741Srmesta (void) rw_unlock(&s_dns_data_lock); 206*1741Srmesta 207*1741Srmesta return (0); 208*1741Srmesta } 209*1741Srmesta 210*1741Srmesta /* 211*1741Srmesta * Search criteria assumptions: 212*1741Srmesta * 213*1741Srmesta * The onus will fall on the sysadmins to correctly configure the TXT 214*1741Srmesta * record in the DNS domain where the box currently resides in order 215*1741Srmesta * for the record to be found. However, if they sysadmin chooses to 216*1741Srmesta * add the 'search' key to /etc/resolv.conf, then resolv_search() 217*1741Srmesta * _will_ traverse up the DNS tree as specified in the 'search' key. 218*1741Srmesta * Otherwise, we'll default the domain to the DNS domain itself. 219*1741Srmesta */ 220*1741Srmesta static int 221*1741Srmesta resolv_search(void) 222*1741Srmesta { 223*1741Srmesta int len; 224*1741Srmesta ans_t ans = {0}; 225*1741Srmesta struct __res_state res; 226*1741Srmesta int type = T_TXT; 227*1741Srmesta int class = C_IN; 228*1741Srmesta 229*1741Srmesta (void) mutex_lock(&s_res_lock); 230*1741Srmesta res = s_res; 231*1741Srmesta (void) mutex_unlock(&s_res_lock); 232*1741Srmesta 233*1741Srmesta /* 234*1741Srmesta * Avoid holding locks across the res_nsearch() call to 235*1741Srmesta * prevent stalling threads during network partitions. 236*1741Srmesta */ 237*1741Srmesta len = h_errno = errno = 0; 238*1741Srmesta if ((len = res_nsearch(&res, NFSMAPID_DNS_RR, class, type, 239*1741Srmesta ans.buf, sizeof (ans))) < 0) 240*1741Srmesta return (resolv_error()); 241*1741Srmesta 242*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 243*1741Srmesta s_ans = ans; 244*1741Srmesta s_anslen = len; 245*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 246*1741Srmesta 247*1741Srmesta return (NETDB_SUCCESS); 248*1741Srmesta } 249*1741Srmesta 250*1741Srmesta /* 251*1741Srmesta * Skip one DNS record 252*1741Srmesta */ 253*1741Srmesta static uchar_t * 254*1741Srmesta resolv_skip_rr(uchar_t *p, uchar_t *eom) 255*1741Srmesta { 256*1741Srmesta int t; 257*1741Srmesta int dlen; 258*1741Srmesta 259*1741Srmesta /* 260*1741Srmesta * Skip compressed name 261*1741Srmesta */ 262*1741Srmesta errno = 0; 263*1741Srmesta if ((t = dn_skipname(p, eom)) < 0) { 264*1741Srmesta #ifdef DEBUG 265*1741Srmesta syslog(LOG_ERR, "%s", strerror(errno)); 266*1741Srmesta #endif 267*1741Srmesta return (NULL); 268*1741Srmesta } 269*1741Srmesta 270*1741Srmesta /* 271*1741Srmesta * Advance pointer and make sure 272*1741Srmesta * we're still within the message 273*1741Srmesta */ 274*1741Srmesta p += t; 275*1741Srmesta if ((p + RRFIXEDSZ) > eom) 276*1741Srmesta return (NULL); 277*1741Srmesta 278*1741Srmesta /* 279*1741Srmesta * Now, just skip over the rr fields 280*1741Srmesta */ 281*1741Srmesta p += INT16SZ; /* type */ 282*1741Srmesta p += INT16SZ; /* class */ 283*1741Srmesta p += INT32SZ; /* ttl */ 284*1741Srmesta dlen = ns_get16(p); 285*1741Srmesta p += INT16SZ; 286*1741Srmesta p += dlen; /* dlen */ 287*1741Srmesta if (p > eom) 288*1741Srmesta return (NULL); 289*1741Srmesta 290*1741Srmesta return (p); 291*1741Srmesta } 292*1741Srmesta 293*1741Srmesta /* 294*1741Srmesta * Process one TXT record. 295*1741Srmesta * 296*1741Srmesta * nfsmapid queries the DNS server for the specific _nfsv4idmapdomain 297*1741Srmesta * TXT record. Thus, if the TXT record exists, the answer section of 298*1741Srmesta * the DNS response carries the TXT record's value. Thus, we check that 299*1741Srmesta * the value is indeed a valid domain and set the modular s_txt_rr 300*1741Srmesta * global to the domain value. 301*1741Srmesta */ 302*1741Srmesta static void 303*1741Srmesta resolve_process_txt(uchar_t *p, int dlen) 304*1741Srmesta { 305*1741Srmesta char *rr_base = (char *)(p + 1); 306*1741Srmesta char *rr_end = (char *)(p + dlen); 307*1741Srmesta size_t len = rr_end - rr_base; 308*1741Srmesta #ifdef DEBUG 309*1741Srmesta static uint64_t msg_done = 0; 310*1741Srmesta #endif 311*1741Srmesta char tmp_txt_rr[DNAMEMAX]; 312*1741Srmesta 313*1741Srmesta if (len >= DNAMEMAX) 314*1741Srmesta return; /* process next TXT RR */ 315*1741Srmesta 316*1741Srmesta /* 317*1741Srmesta * make sure we have a clean buf since 318*1741Srmesta * we may've processed several TXT rr's 319*1741Srmesta */ 320*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 321*1741Srmesta bzero(s_txt_rr, sizeof (s_txt_rr)); 322*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 323*1741Srmesta 324*1741Srmesta (void) strncpy(tmp_txt_rr, rr_base, len); 325*1741Srmesta tmp_txt_rr[len] = '\0'; 326*1741Srmesta 327*1741Srmesta /* 328*1741Srmesta * If there is a record and it's a valid domain, we're done. 329*1741Srmesta */ 330*1741Srmesta if (rr_base[0] != '\0' && mapid_stdchk_domain(tmp_txt_rr) > 0) { 331*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 332*1741Srmesta (void) strncpy(s_txt_rr, rr_base, len); 333*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 334*1741Srmesta #ifdef DEBUG 335*1741Srmesta syslog(LOG_ERR, "TXT (Rec):\t%s", s_txt_rr); 336*1741Srmesta 337*1741Srmesta } else if (!(msg_done++ % NFSMAPID_SLOG_RATE)) { 338*1741Srmesta /* 339*1741Srmesta * Otherwise, log the error 340*1741Srmesta */ 341*1741Srmesta (void) rw_rdlock(&s_dns_impl_lock); 342*1741Srmesta syslog(LOG_ERR, EMSG_DNS_RR_INVAL, NFSMAPID_DNS_RR, s_dname); 343*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 344*1741Srmesta #endif 345*1741Srmesta } 346*1741Srmesta } 347*1741Srmesta 348*1741Srmesta /* 349*1741Srmesta * Decode any answer received from the DNS server. This interface is 350*1741Srmesta * capable of much more than just decoding TXT records. We maintain 351*1741Srmesta * focus on TXT rr's for now, but this will probably change once we 352*1741Srmesta * get the IETF approved application specific DNS RR. 353*1741Srmesta * 354*1741Srmesta * Here's an example of the TXT record we're decoding (as would appear 355*1741Srmesta * in the DNS zone file): 356*1741Srmesta * 357*1741Srmesta * _nfsv4idmapdomain IN TXT "sun.com" 358*1741Srmesta * 359*1741Srmesta * Once the IETF application specific DNS RR is granted, we should only 360*1741Srmesta * be changing the record flavor, but all should pretty much stay the 361*1741Srmesta * same. 362*1741Srmesta */ 363*1741Srmesta static void 364*1741Srmesta resolv_decode(void) 365*1741Srmesta { 366*1741Srmesta uchar_t *buf; 367*1741Srmesta HEADER *hp; 368*1741Srmesta uchar_t name[DNAMEMAX]; 369*1741Srmesta uchar_t *eom; 370*1741Srmesta uchar_t *p; 371*1741Srmesta int n; 372*1741Srmesta uint_t qd_cnt; 373*1741Srmesta uint_t an_cnt; 374*1741Srmesta uint_t ns_cnt; 375*1741Srmesta uint_t ar_cnt; 376*1741Srmesta uint_t cnt; 377*1741Srmesta uint_t type; 378*1741Srmesta int dlen; 379*1741Srmesta ans_t answer = {0}; 380*1741Srmesta int answer_len = 0; 381*1741Srmesta 382*1741Srmesta /* 383*1741Srmesta * Check the HEADER for any signs of errors 384*1741Srmesta * and extract the answer counts for later. 385*1741Srmesta */ 386*1741Srmesta (void) rw_rdlock(&s_dns_impl_lock); 387*1741Srmesta answer = s_ans; 388*1741Srmesta answer_len = s_anslen; 389*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 390*1741Srmesta 391*1741Srmesta buf = (uchar_t *)&answer.buf; 392*1741Srmesta hp = (HEADER *)&answer.hdr; 393*1741Srmesta eom = (uchar_t *)(buf + answer_len); 394*1741Srmesta if (hp->rcode != NOERROR) { 395*1741Srmesta #ifdef DEBUG 396*1741Srmesta syslog(LOG_ERR, "errno: %s", strerror(errno)); 397*1741Srmesta syslog(LOG_ERR, "h_errno: %s", hstrerror(h_errno)); 398*1741Srmesta #endif 399*1741Srmesta return; 400*1741Srmesta } 401*1741Srmesta qd_cnt = ntohs(hp->qdcount); 402*1741Srmesta an_cnt = ntohs(hp->ancount); 403*1741Srmesta ns_cnt = ntohs(hp->nscount); 404*1741Srmesta ar_cnt = ntohs(hp->arcount); 405*1741Srmesta 406*1741Srmesta /* 407*1741Srmesta * skip query entries 408*1741Srmesta */ 409*1741Srmesta p = (uchar_t *)(buf + HFIXEDSZ); 410*1741Srmesta errno = 0; 411*1741Srmesta while (qd_cnt-- > 0) { 412*1741Srmesta n = dn_skipname(p, eom); 413*1741Srmesta if (n < 0) { 414*1741Srmesta #ifdef DEBUG 415*1741Srmesta syslog(LOG_ERR, "%s", strerror(errno)); 416*1741Srmesta #endif 417*1741Srmesta return; 418*1741Srmesta } 419*1741Srmesta p += n; 420*1741Srmesta p += INT16SZ; /* type */ 421*1741Srmesta p += INT16SZ; /* class */ 422*1741Srmesta } 423*1741Srmesta 424*1741Srmesta #ifdef DEBUG 425*1741Srmesta /* 426*1741Srmesta * If debugging... print query only once. 427*1741Srmesta * NOTE: Don't advance pointer... this is done 428*1741Srmesta * in while() loop on a per record basis ! 429*1741Srmesta */ 430*1741Srmesta n = h_errno = errno = 0; 431*1741Srmesta n = dn_expand(buf, eom, p, (char *)name, sizeof (name)); 432*1741Srmesta if (n < 0) { 433*1741Srmesta (void) resolv_error(); 434*1741Srmesta return; 435*1741Srmesta } 436*1741Srmesta syslog(LOG_ERR, "Query:\t\t%-30s", name); 437*1741Srmesta #endif 438*1741Srmesta 439*1741Srmesta /* 440*1741Srmesta * Process actual answer(s). 441*1741Srmesta */ 442*1741Srmesta cnt = an_cnt; 443*1741Srmesta while (cnt-- > 0 && p < eom) { 444*1741Srmesta /* skip the name field */ 445*1741Srmesta n = dn_expand(buf, eom, p, (char *)name, sizeof (name)); 446*1741Srmesta if (n < 0) { 447*1741Srmesta (void) resolv_error(); 448*1741Srmesta return; 449*1741Srmesta } 450*1741Srmesta p += n; 451*1741Srmesta 452*1741Srmesta if ((p + 3 * INT16SZ + INT32SZ) > eom) 453*1741Srmesta return; 454*1741Srmesta 455*1741Srmesta type = ns_get16(p); 456*1741Srmesta p += INT16SZ; 457*1741Srmesta p += INT16SZ + INT32SZ; /* skip class & ttl */ 458*1741Srmesta dlen = ns_get16(p); 459*1741Srmesta p += INT16SZ; 460*1741Srmesta 461*1741Srmesta if ((p + dlen) > eom) 462*1741Srmesta return; 463*1741Srmesta 464*1741Srmesta switch (type) { 465*1741Srmesta case T_TXT: 466*1741Srmesta resolve_process_txt(p, dlen); 467*1741Srmesta break; 468*1741Srmesta 469*1741Srmesta default: 470*1741Srmesta /* 471*1741Srmesta * Advance to next answer record for any 472*1741Srmesta * other record types. Again, this will 473*1741Srmesta * probably change (see block comment). 474*1741Srmesta */ 475*1741Srmesta p += dlen; 476*1741Srmesta break; 477*1741Srmesta } 478*1741Srmesta } 479*1741Srmesta 480*1741Srmesta /* 481*1741Srmesta * Skip name server and additional records for now. 482*1741Srmesta */ 483*1741Srmesta cnt = ns_cnt + ar_cnt; 484*1741Srmesta if (cnt > 0) { 485*1741Srmesta while (--cnt != 0 && p < eom) { 486*1741Srmesta p = resolv_skip_rr(p, eom); 487*1741Srmesta if (p == NULL) 488*1741Srmesta return; 489*1741Srmesta } 490*1741Srmesta } 491*1741Srmesta } 492*1741Srmesta 493*1741Srmesta /* 494*1741Srmesta * If a valid TXT record entry exists, s_txt_rr contains the domain 495*1741Srmesta * value (as set in resolv_process_txt) and we extract the value into 496*1741Srmesta * dns_txt_domain (the exported global). If there was _no_ valid TXT 497*1741Srmesta * entry, we simply return and check_domain() will default to the 498*1741Srmesta * DNS domain since we did resolv_txt_reset() first. 499*1741Srmesta */ 500*1741Srmesta static void 501*1741Srmesta resolv_get_txt_data() 502*1741Srmesta { 503*1741Srmesta (void) rw_rdlock(&s_dns_impl_lock); 504*1741Srmesta if (s_txt_rr[0] != '\0') { 505*1741Srmesta (void) rw_wrlock(&s_dns_data_lock); 506*1741Srmesta (void) snprintf(dns_txt_domain, strlen(s_txt_rr) + 1, "%s", 507*1741Srmesta s_txt_rr); 508*1741Srmesta dns_txt_domain_len = strlen(dns_txt_domain); 509*1741Srmesta dns_txt_cached = 1; 510*1741Srmesta (void) rw_unlock(&s_dns_data_lock); 511*1741Srmesta } 512*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 513*1741Srmesta } 514*1741Srmesta 515*1741Srmesta static void 516*1741Srmesta domain_sync(cb_t *argp, char *dname) 517*1741Srmesta { 518*1741Srmesta int dlen = 0; 519*1741Srmesta void *(*fcn)(void *) = NULL; 520*1741Srmesta int sighup = 0; 521*1741Srmesta int domchg = 0; 522*1741Srmesta 523*1741Srmesta /* 524*1741Srmesta * Make sure values passed are sane and initialize accordingly. 525*1741Srmesta */ 526*1741Srmesta if (dname != NULL) 527*1741Srmesta dlen = strlen(dname); 528*1741Srmesta if (argp) { 529*1741Srmesta if (argp->fcn) 530*1741Srmesta fcn = argp->fcn; 531*1741Srmesta if (argp->signal) 532*1741Srmesta sighup = argp->signal; 533*1741Srmesta } 534*1741Srmesta 535*1741Srmesta /* 536*1741Srmesta * Update the library's mapid_domain variable if 'dname' is different. 537*1741Srmesta */ 538*1741Srmesta if (dlen != 0 && strncasecmp(dname, mapid_domain, NS_MAXCDNAME)) { 539*1741Srmesta (void) rw_wrlock(&mapid_domain_lock); 540*1741Srmesta (void) strncpy(mapid_domain, dname, NS_MAXCDNAME); 541*1741Srmesta mapid_domain_len = dlen; 542*1741Srmesta (void) rw_unlock(&mapid_domain_lock); 543*1741Srmesta domchg++; 544*1741Srmesta } 545*1741Srmesta 546*1741Srmesta /* 547*1741Srmesta * If the caller gave us a valid callback routine, we 548*1741Srmesta * instantiate it to announce the domain change, but 549*1741Srmesta * only if either the domain changed _or_ the caller 550*1741Srmesta * was issued a SIGHUP. 551*1741Srmesta */ 552*1741Srmesta if (fcn != NULL && (sighup || domchg)) 553*1741Srmesta (void) fcn((void *)mapid_domain); 554*1741Srmesta } 555*1741Srmesta 556*1741Srmesta /* 557*1741Srmesta * Thread to keep pinging DNS server for TXT record if nfsmapid's 558*1741Srmesta * initial attempt at contact with server failed. We could potentially 559*1741Srmesta * have a substantial number of NFSv4 clients and having all of them 560*1741Srmesta * hammering on an already unresponsive DNS server would not help 561*1741Srmesta * things. So, we limit the number of live query threads to at most 562*1741Srmesta * 1 at any one time to keep things from getting out of hand. 563*1741Srmesta */ 564*1741Srmesta /* ARGSUSED */ 565*1741Srmesta static void * 566*1741Srmesta resolv_query_thread(void *arg) 567*1741Srmesta { 568*1741Srmesta unsigned int nap_time; 569*1741Srmesta 570*1741Srmesta #ifdef DEBUG 571*1741Srmesta char *whoami = "query_thread"; 572*1741Srmesta 573*1741Srmesta syslog(LOG_ERR, "%s active !", whoami); 574*1741Srmesta #endif 575*1741Srmesta (void) rw_rdlock(&s_dns_impl_lock); 576*1741Srmesta nap_time = s_dns_tout; 577*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 578*1741Srmesta 579*1741Srmesta for (;;) { 580*1741Srmesta (void) sleep(nap_time); 581*1741Srmesta 582*1741Srmesta resolv_txt_reset(); 583*1741Srmesta (void) resolv_init(); 584*1741Srmesta switch (resolv_search()) { 585*1741Srmesta case NETDB_SUCCESS: 586*1741Srmesta resolv_decode(); 587*1741Srmesta resolv_get_txt_data(); 588*1741Srmesta 589*1741Srmesta /* 590*1741Srmesta * This is a bit different than what we 591*1741Srmesta * do in get_dns_txt_domain(), where we 592*1741Srmesta * simply return and let the caller 593*1741Srmesta * access dns_txt_domain directly. 594*1741Srmesta * 595*1741Srmesta * Here we invoke the callback routine 596*1741Srmesta * provided by the caller to the 597*1741Srmesta * mapid_reeval_domain() interface via 598*1741Srmesta * the cb_t's fcn param. 599*1741Srmesta */ 600*1741Srmesta domain_sync((cb_t *)arg, dns_txt_domain); 601*1741Srmesta goto thr_okay; 602*1741Srmesta 603*1741Srmesta case NO_DATA: 604*1741Srmesta /* 605*1741Srmesta * DNS is up now, but does not have 606*1741Srmesta * the NFSV4IDMAPDOMAIN TXT record. 607*1741Srmesta */ 608*1741Srmesta #ifdef DEBUG 609*1741Srmesta syslog(LOG_ERR, "%s: DNS has no TXT Record", whoami); 610*1741Srmesta #endif 611*1741Srmesta goto thr_reset; 612*1741Srmesta 613*1741Srmesta case NO_RECOVERY: 614*1741Srmesta /* 615*1741Srmesta * Non-Recoverable error occurred. No sense 616*1741Srmesta * in keep pinging the DNS server at this 617*1741Srmesta * point, so we disable any further contact. 618*1741Srmesta */ 619*1741Srmesta #ifdef DEBUG 620*1741Srmesta syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami); 621*1741Srmesta #endif 622*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 623*1741Srmesta s_dns_disabled = TRUE; 624*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 625*1741Srmesta goto thr_reset; 626*1741Srmesta 627*1741Srmesta case HOST_NOT_FOUND: 628*1741Srmesta /* 629*1741Srmesta * Authoritative NS not responding... 630*1741Srmesta * keep trying for non-authoritative reply 631*1741Srmesta */ 632*1741Srmesta /*FALLTHROUGH*/ 633*1741Srmesta 634*1741Srmesta case TRY_AGAIN: 635*1741Srmesta /* keep trying */ 636*1741Srmesta #ifdef DEBUG 637*1741Srmesta syslog(LOG_ERR, "%s: retrying...", whoami); 638*1741Srmesta #endif 639*1741Srmesta break; 640*1741Srmesta 641*1741Srmesta case NETDB_INTERNAL: 642*1741Srmesta default: 643*1741Srmesta #ifdef DEBUG 644*1741Srmesta syslog(LOG_ERR, "%s: Internal resolver error: %s", 645*1741Srmesta whoami, strerror(errno)); 646*1741Srmesta #endif 647*1741Srmesta goto thr_reset; 648*1741Srmesta } 649*1741Srmesta } 650*1741Srmesta thr_reset: 651*1741Srmesta (void) rw_wrlock(&s_dns_data_lock); 652*1741Srmesta dns_txt_cached = 0; 653*1741Srmesta (void) rw_unlock(&s_dns_data_lock); 654*1741Srmesta resolv_txt_reset(); 655*1741Srmesta 656*1741Srmesta thr_okay: 657*1741Srmesta /* mark thread as done */ 658*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 659*1741Srmesta s_dns_qthr_created = FALSE; 660*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 661*1741Srmesta 662*1741Srmesta (void) thr_exit(NULL); 663*1741Srmesta /*NOTREACHED*/ 664*1741Srmesta return (NULL); 665*1741Srmesta } 666*1741Srmesta 667*1741Srmesta /* 668*1741Srmesta * nfsmapid's interface into the resolver for getting the TXT record. 669*1741Srmesta * 670*1741Srmesta * Key concepts: 671*1741Srmesta * 672*1741Srmesta * o If the DNS server is available and the TXT record is found, we 673*1741Srmesta * simply decode the output and fill the exported dns_txt_domain 674*1741Srmesta * global, so our caller can configure the daemon appropriately. 675*1741Srmesta * 676*1741Srmesta * o If the TXT record is not found, then having done resolv_txt_reset() 677*1741Srmesta * first will allow our caller to recognize that the exported globals 678*1741Srmesta * are empty and thus configure nfsmapid to use the default DNS domain. 679*1741Srmesta * 680*1741Srmesta * o Having no /etc/resolv.conf file is pretty much a show stopper, since 681*1741Srmesta * there is no name server address information. We return since we've 682*1741Srmesta * already have reset the TXT global state. 683*1741Srmesta * 684*1741Srmesta * o If a previous call to the DNS server resulted in an unrecoverable 685*1741Srmesta * error, then we disable further contact to the DNS server and return. 686*1741Srmesta * Having the TXT global state already reset guarantees that our caller 687*1741Srmesta * will fall back to the right configuration. 688*1741Srmesta * 689*1741Srmesta * o Query thread creation is throttled by s_dns_qthr_created. We mitigate 690*1741Srmesta * the problem of an already unresponsive DNS server by allowing at most 691*1741Srmesta * 1 outstanding query thread since we could potentially have a substantial 692*1741Srmesta * amount of clients hammering on the same DNS server attempting to get 693*1741Srmesta * the TXT record. 694*1741Srmesta */ 695*1741Srmesta static void 696*1741Srmesta get_dns_txt_domain(cb_t *argp) 697*1741Srmesta { 698*1741Srmesta int err; 699*1741Srmesta #ifdef DEBUG 700*1741Srmesta static uint64_t msg_done = 0; 701*1741Srmesta char *whoami = "get_dns_txt_domain"; 702*1741Srmesta #endif 703*1741Srmesta long thr_flags = THR_DETACHED; 704*1741Srmesta struct stat st; 705*1741Srmesta 706*1741Srmesta /* 707*1741Srmesta * We reset TXT variables first in case /etc/resolv.conf 708*1741Srmesta * is missing or we've had unrecoverable resolver errors, 709*1741Srmesta * we'll default to get_dns_domain(). If a previous DNS 710*1741Srmesta * TXT RR was found, don't clear it until we're certain 711*1741Srmesta * that contact can be made to the DNS server (see block 712*1741Srmesta * comment atop resolv_txt_reset). If we're responding to 713*1741Srmesta * a SIGHUP signal, force a reset of the cached copy. 714*1741Srmesta */ 715*1741Srmesta if (argp && argp->signal) { 716*1741Srmesta (void) rw_wrlock(&s_dns_data_lock); 717*1741Srmesta dns_txt_cached = 0; 718*1741Srmesta (void) rw_unlock(&s_dns_data_lock); 719*1741Srmesta } 720*1741Srmesta resolv_txt_reset(); 721*1741Srmesta 722*1741Srmesta errno = 0; 723*1741Srmesta if (stat(_PATH_RESCONF, &st) < 0 && errno == ENOENT) { 724*1741Srmesta /* 725*1741Srmesta * If /etc/resolv.conf is not there, then we'll 726*1741Srmesta * get the domain from domainname(1M). No real 727*1741Srmesta * reason to query DNS or fire a thread since we 728*1741Srmesta * have no nameserver addresses. 729*1741Srmesta */ 730*1741Srmesta goto txtclear; 731*1741Srmesta } 732*1741Srmesta 733*1741Srmesta (void) rw_rdlock(&s_dns_impl_lock); 734*1741Srmesta if (s_dns_disabled) { 735*1741Srmesta /* 736*1741Srmesta * If there were non-recoverable problems with DNS, 737*1741Srmesta * we have stopped querying DNS entirely. See 738*1741Srmesta * NO_RECOVERY clause below. 739*1741Srmesta */ 740*1741Srmesta #ifdef DEBUG 741*1741Srmesta syslog(LOG_ERR, "%s: DNS queries disabled", whoami); 742*1741Srmesta #endif 743*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 744*1741Srmesta return; 745*1741Srmesta } 746*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 747*1741Srmesta 748*1741Srmesta (void) resolv_init(); 749*1741Srmesta switch (resolv_search()) { 750*1741Srmesta case NETDB_SUCCESS: 751*1741Srmesta /* 752*1741Srmesta * If there _is_ a TXT record, we let 753*1741Srmesta * our caller set the global state. 754*1741Srmesta */ 755*1741Srmesta resolv_decode(); 756*1741Srmesta resolv_get_txt_data(); 757*1741Srmesta return; 758*1741Srmesta 759*1741Srmesta case TRY_AGAIN: 760*1741Srmesta if (argp == NULL || argp->fcn == NULL) 761*1741Srmesta /* 762*1741Srmesta * If no valid argument was passed or 763*1741Srmesta * callback defined, don't fire thread 764*1741Srmesta */ 765*1741Srmesta return; 766*1741Srmesta 767*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 768*1741Srmesta if (s_dns_qthr_created) { 769*1741Srmesta /* 770*1741Srmesta * We may have lots of clients, so we don't 771*1741Srmesta * want to bog down the DNS server with tons 772*1741Srmesta * of requests... lest it becomes even more 773*1741Srmesta * unresponsive, so limit 1 thread to query 774*1741Srmesta * DNS at a time. 775*1741Srmesta */ 776*1741Srmesta #ifdef DEBUG 777*1741Srmesta syslog(LOG_ERR, "%s: query thread already active", 778*1741Srmesta whoami); 779*1741Srmesta #endif 780*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 781*1741Srmesta return; 782*1741Srmesta } 783*1741Srmesta 784*1741Srmesta /* 785*1741Srmesta * DNS did not respond ! Set timeout and kick off 786*1741Srmesta * thread to try op again after s_dns_tout seconds. 787*1741Srmesta * We've made sure that we don't have an already 788*1741Srmesta * running thread above. 789*1741Srmesta */ 790*1741Srmesta s_dns_tout = NFSMAPID_DNS_TOUT_SECS; 791*1741Srmesta err = thr_create(NULL, 0, resolv_query_thread, (void *)argp, 792*1741Srmesta thr_flags, &s_dns_qthread); 793*1741Srmesta if (!err) { 794*1741Srmesta s_dns_qthr_created = TRUE; 795*1741Srmesta } 796*1741Srmesta #ifdef DEBUG 797*1741Srmesta else { 798*1741Srmesta msg_done++; 799*1741Srmesta if (!(msg_done % NFSMAPID_SLOG_RATE)) 800*1741Srmesta syslog(LOG_ERR, EMSG_DNS_THREAD_ERROR); 801*1741Srmesta } 802*1741Srmesta #endif 803*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 804*1741Srmesta return; 805*1741Srmesta 806*1741Srmesta case NO_RECOVERY: 807*1741Srmesta #ifdef DEBUG 808*1741Srmesta syslog(LOG_ERR, EMSG_DNS_DISABLE, whoami); 809*1741Srmesta #endif 810*1741Srmesta (void) rw_wrlock(&s_dns_impl_lock); 811*1741Srmesta s_dns_disabled = TRUE; 812*1741Srmesta (void) rw_unlock(&s_dns_impl_lock); 813*1741Srmesta 814*1741Srmesta /*FALLTHROUGH*/ 815*1741Srmesta 816*1741Srmesta default: 817*1741Srmesta /* 818*1741Srmesta * For any other errors... DNS is responding, but 819*1741Srmesta * either it has no data, or some other problem is 820*1741Srmesta * occuring. At any rate, the TXT domain should not 821*1741Srmesta * be used, so we default to the DNS domain. 822*1741Srmesta */ 823*1741Srmesta break; 824*1741Srmesta } 825*1741Srmesta 826*1741Srmesta txtclear: 827*1741Srmesta (void) rw_wrlock(&s_dns_data_lock); 828*1741Srmesta dns_txt_cached = 0; 829*1741Srmesta (void) rw_unlock(&s_dns_data_lock); 830*1741Srmesta resolv_txt_reset(); 831*1741Srmesta } 832*1741Srmesta 833*1741Srmesta static int 834*1741Srmesta get_mtime(const char *fname, timestruc_t *mtim) 835*1741Srmesta { 836*1741Srmesta struct stat st; 837*1741Srmesta int err; 838*1741Srmesta 839*1741Srmesta if ((err = stat(fname, &st)) != 0) 840*1741Srmesta return (err); 841*1741Srmesta 842*1741Srmesta *mtim = st.st_mtim; 843*1741Srmesta return (0); 844*1741Srmesta } 845*1741Srmesta 846*1741Srmesta 847*1741Srmesta /* 848*1741Srmesta * trim_wspace is a destructive interface; it is up to 849*1741Srmesta * the caller to save off an original copy if needed. 850*1741Srmesta */ 851*1741Srmesta static char * 852*1741Srmesta trim_wspace(char *dp) 853*1741Srmesta { 854*1741Srmesta char *r; 855*1741Srmesta char *ndp; 856*1741Srmesta 857*1741Srmesta /* 858*1741Srmesta * Any empty domain is not valid 859*1741Srmesta */ 860*1741Srmesta if (dp == NULL) 861*1741Srmesta return (NULL); 862*1741Srmesta 863*1741Srmesta /* 864*1741Srmesta * Skip leading blanks 865*1741Srmesta */ 866*1741Srmesta for (ndp = dp; *ndp != '\0'; ndp++) { 867*1741Srmesta if (!isspace(*ndp)) 868*1741Srmesta break; 869*1741Srmesta } 870*1741Srmesta 871*1741Srmesta /* 872*1741Srmesta * If we reached the end of the string w/o 873*1741Srmesta * finding a non-blank char, return error 874*1741Srmesta */ 875*1741Srmesta if (*ndp == '\0') 876*1741Srmesta return (NULL); 877*1741Srmesta 878*1741Srmesta /* 879*1741Srmesta * Find next blank in string 880*1741Srmesta */ 881*1741Srmesta for (r = ndp; *r != '\0'; r++) { 882*1741Srmesta if (isspace(*r)) 883*1741Srmesta break; 884*1741Srmesta } 885*1741Srmesta 886*1741Srmesta /* 887*1741Srmesta * No more blanks found, we are done 888*1741Srmesta */ 889*1741Srmesta if (*r == '\0') 890*1741Srmesta return (ndp); 891*1741Srmesta 892*1741Srmesta /* 893*1741Srmesta * Terminate string at blank 894*1741Srmesta */ 895*1741Srmesta *r++ = '\0'; 896*1741Srmesta 897*1741Srmesta /* 898*1741Srmesta * Skip any trailing spaces 899*1741Srmesta */ 900*1741Srmesta while (*r != '\0') { 901*1741Srmesta /* 902*1741Srmesta * If a non-blank is found, it is an 903*1741Srmesta * illegal domain (embedded blanks). 904*1741Srmesta */ 905*1741Srmesta if (!isspace(*r)) 906*1741Srmesta return (NULL); 907*1741Srmesta r++; 908*1741Srmesta } 909*1741Srmesta return (ndp); 910*1741Srmesta } 911*1741Srmesta 912*1741Srmesta static void 913*1741Srmesta get_nfs_domain(void) 914*1741Srmesta { 915*1741Srmesta char *ndomain; 916*1741Srmesta timestruc_t ntime; 917*1741Srmesta 918*1741Srmesta /* 919*1741Srmesta * If we can't get stats for the config file, then 920*1741Srmesta * zap the NFS domain info. If mtime hasn't changed, 921*1741Srmesta * then there's no work to do, so just return. 922*1741Srmesta */ 923*1741Srmesta if (get_mtime(NFSADMIN, &ntime) != 0) { 924*1741Srmesta ZAP_DOMAIN(nfs); 925*1741Srmesta return; 926*1741Srmesta } 927*1741Srmesta 928*1741Srmesta if (TIMESTRUC_EQ(ntime, nfs_mtime)) 929*1741Srmesta return; 930*1741Srmesta 931*1741Srmesta /* 932*1741Srmesta * Get NFSMAPID_DOMAIN value from /etc/default/nfs for now. 933*1741Srmesta * Note: defread() returns a ptr to TSD. 934*1741Srmesta */ 935*1741Srmesta if (defopen(NFSADMIN) == 0) { 936*1741Srmesta char *dp = NULL; 937*1741Srmesta #ifdef DEBUG 938*1741Srmesta char *whoami = "get_nfs_domain"; 939*1741Srmesta char orig[NS_MAXCDNAME] = {0}; 940*1741Srmesta #endif 941*1741Srmesta ndomain = (char *)defread("NFSMAPID_DOMAIN="); 942*1741Srmesta (void) defopen(NULL); 943*1741Srmesta #ifdef DEBUG 944*1741Srmesta if (ndomain) 945*1741Srmesta (void) strncpy(orig, ndomain, NS_MAXCDNAME); 946*1741Srmesta #endif 947*1741Srmesta /* 948*1741Srmesta * NFSMAPID_DOMAIN was set, so it's time for validation. If 949*1741Srmesta * it's okay, then update NFS domain and return. If not, 950*1741Srmesta * bail (syslog in DEBUG). We make nfsmapid more a bit 951*1741Srmesta * more forgiving of trailing and leading white space. 952*1741Srmesta */ 953*1741Srmesta if ((dp = trim_wspace(ndomain)) != NULL) { 954*1741Srmesta if (mapid_stdchk_domain(dp) > 0) { 955*1741Srmesta nfs_domain_len = strlen(dp); 956*1741Srmesta (void) strncpy(nfs_domain, dp, NS_MAXCDNAME); 957*1741Srmesta nfs_domain[NS_MAXCDNAME] = '\0'; 958*1741Srmesta nfs_mtime = ntime; 959*1741Srmesta return; 960*1741Srmesta } 961*1741Srmesta } 962*1741Srmesta #ifdef DEBUG 963*1741Srmesta if (orig[0] != '\0') { 964*1741Srmesta syslog(LOG_ERR, gettext("%s: Invalid domain name \"%s\"" 965*1741Srmesta " found in configuration file."), whoami, orig); 966*1741Srmesta } 967*1741Srmesta #endif 968*1741Srmesta } 969*1741Srmesta 970*1741Srmesta /* 971*1741Srmesta * So the NFS config file changed but it couldn't be opened or 972*1741Srmesta * it didn't specify NFSMAPID_DOMAIN or it specified an invalid 973*1741Srmesta * NFSMAPID_DOMAIN. Time to zap current NFS domain info. 974*1741Srmesta */ 975*1741Srmesta ZAP_DOMAIN(nfs); 976*1741Srmesta } 977*1741Srmesta 978*1741Srmesta static void 979*1741Srmesta get_dns_domain(void) 980*1741Srmesta { 981*1741Srmesta timestruc_t ntime = {0}; 982*1741Srmesta 983*1741Srmesta /* 984*1741Srmesta * If we can't get stats for the config file, then 985*1741Srmesta * zap the DNS domain info. If mtime hasn't changed, 986*1741Srmesta * then there's no work to do, so just return. 987*1741Srmesta */ 988*1741Srmesta errno = 0; 989*1741Srmesta if (get_mtime(_PATH_RESCONF, &ntime) != 0) { 990*1741Srmesta switch (errno) { 991*1741Srmesta case ENOENT: 992*1741Srmesta /* 993*1741Srmesta * The resolver defaults to obtaining the 994*1741Srmesta * domain off of the NIS domainname(1M) if 995*1741Srmesta * /etc/resolv.conf does not exist, so we 996*1741Srmesta * move forward. 997*1741Srmesta */ 998*1741Srmesta break; 999*1741Srmesta 1000*1741Srmesta default: 1001*1741Srmesta ZAP_DOMAIN(dns); 1002*1741Srmesta return; 1003*1741Srmesta } 1004*1741Srmesta } else if (TIMESTRUC_EQ(ntime, dns_mtime)) 1005*1741Srmesta return; 1006*1741Srmesta 1007*1741Srmesta /* 1008*1741Srmesta * Re-initialize resolver to zap DNS domain from previous 1009*1741Srmesta * resolv_init() calls. 1010*1741Srmesta */ 1011*1741Srmesta (void) resolv_init(); 1012*1741Srmesta 1013*1741Srmesta /* 1014*1741Srmesta * Update cached DNS domain. No need for validation since 1015*1741Srmesta * domain comes from resolver. If resolver doesn't return the 1016*1741Srmesta * domain, then zap the DNS domain. This shouldn't ever happen, 1017*1741Srmesta * and if it does, the machine has bigger problems (so no need 1018*1741Srmesta * to generate a message that says DNS appears to be broken). 1019*1741Srmesta */ 1020*1741Srmesta (void) rw_rdlock(&s_dns_data_lock); 1021*1741Srmesta if (sysdns_domain[0] != '\0') { 1022*1741Srmesta (void) strncpy(dns_domain, sysdns_domain, NS_MAXCDNAME); 1023*1741Srmesta dns_domain_len = strlen(sysdns_domain); 1024*1741Srmesta (void) rw_unlock(&s_dns_data_lock); 1025*1741Srmesta dns_mtime = ntime; 1026*1741Srmesta return; 1027*1741Srmesta } 1028*1741Srmesta (void) rw_unlock(&s_dns_data_lock); 1029*1741Srmesta 1030*1741Srmesta ZAP_DOMAIN(dns); 1031*1741Srmesta } 1032*1741Srmesta 1033*1741Srmesta /* 1034*1741Srmesta * PSARC 2005/487 Contracted Sun Private Interface 1035*1741Srmesta * mapid_stdchk_domain() 1036*1741Srmesta * Changes must be reviewed by Solaris File Sharing 1037*1741Srmesta * Changes must be communicated to contract-2005-487-01@sun.com 1038*1741Srmesta * 1039*1741Srmesta * Based on the recommendations from RFC1033 and RFC1035, check 1040*1741Srmesta * if a given domain name string is valid. Return values are: 1041*1741Srmesta * 1042*1741Srmesta * 1 = valid domain name 1043*1741Srmesta * 0 = invalid domain name (or invalid embedded character) 1044*1741Srmesta * -1 = domain length > NS_MAXCDNAME 1045*1741Srmesta */ 1046*1741Srmesta int 1047*1741Srmesta mapid_stdchk_domain(const char *ds) 1048*1741Srmesta { 1049*1741Srmesta int i; 1050*1741Srmesta size_t len; 1051*1741Srmesta 1052*1741Srmesta if (ds[0] == '\0') 1053*1741Srmesta return (0); 1054*1741Srmesta else 1055*1741Srmesta len = strlen(ds) - 1; 1056*1741Srmesta 1057*1741Srmesta /* 1058*1741Srmesta * 1st char _must_ be alphabetic char _AND_ last char _must_ 1059*1741Srmesta * be alphanumeric. We check for other valid chars below. 1060*1741Srmesta */ 1061*1741Srmesta if (!isalpha(ds[0]) || !isalpha(ds[len]) && !isdigit(ds[len])) 1062*1741Srmesta return (0); 1063*1741Srmesta 1064*1741Srmesta for (i = 0; *ds && i <= NS_MAXCDNAME; i++, ds++) { 1065*1741Srmesta if (!isalpha(*ds) && !isdigit(*ds) && 1066*1741Srmesta (*ds != '.') && (*ds != '-') && (*ds != '_')) 1067*1741Srmesta return (0); 1068*1741Srmesta } 1069*1741Srmesta return (i == (NS_MAXCDNAME + 1) ? -1 : 1); 1070*1741Srmesta } 1071*1741Srmesta 1072*1741Srmesta /* 1073*1741Srmesta * PSARC 2005/487 Consolidation Private 1074*1741Srmesta * mapid_reeval_domain() 1075*1741Srmesta * Changes must be reviewed by Solaris File Sharing 1076*1741Srmesta */ 1077*1741Srmesta void 1078*1741Srmesta mapid_reeval_domain(cb_t *arg) 1079*1741Srmesta { 1080*1741Srmesta char *domain = NULL; 1081*1741Srmesta 1082*1741Srmesta get_nfs_domain(); 1083*1741Srmesta if (nfs_domain_len != 0) { 1084*1741Srmesta domain = nfs_domain; 1085*1741Srmesta goto dsync; 1086*1741Srmesta } 1087*1741Srmesta 1088*1741Srmesta get_dns_txt_domain(arg); 1089*1741Srmesta if (dns_txt_domain_len != 0) 1090*1741Srmesta domain = dns_txt_domain; 1091*1741Srmesta else { 1092*1741Srmesta /* 1093*1741Srmesta * We're either here because: 1094*1741Srmesta * 1095*1741Srmesta * . NFSMAPID_DOMAIN was not set in /etc/default/nfs 1096*1741Srmesta * . No suitable DNS TXT resource record exists 1097*1741Srmesta * . DNS server is not responding to requests 1098*1741Srmesta * 1099*1741Srmesta * in either case, we want to default to using the 1100*1741Srmesta * system configured DNS domain. If this fails, then 1101*1741Srmesta * dns_domain will be empty and dns_domain_len will 1102*1741Srmesta * be 0. 1103*1741Srmesta */ 1104*1741Srmesta get_dns_domain(); 1105*1741Srmesta domain = dns_domain; 1106*1741Srmesta } 1107*1741Srmesta 1108*1741Srmesta dsync: 1109*1741Srmesta domain_sync(arg, domain); 1110*1741Srmesta } 1111*1741Srmesta 1112*1741Srmesta /* 1113*1741Srmesta * PSARC 2005/487 Consolidation Private 1114*1741Srmesta * mapid_get_domain() 1115*1741Srmesta * Changes must be reviewed by Solaris File Sharing 1116*1741Srmesta * 1117*1741Srmesta * The use of TSD in mapid_get_domain() diverges slightly from the typical 1118*1741Srmesta * TSD use, since here, the benefit of doing TSD is mostly to allocate 1119*1741Srmesta * a per-thread buffer that will be utilized by other up-calls to the 1120*1741Srmesta * daemon. 1121*1741Srmesta * 1122*1741Srmesta * In doors, the thread used for the upcall never really exits, hence 1123*1741Srmesta * the typical destructor function defined via thr_keycreate() will 1124*1741Srmesta * never be called. Thus, we only use TSD to allocate the per-thread 1125*1741Srmesta * buffer and fill it up w/the configured 'mapid_domain' on each call. 1126*1741Srmesta * This still alleviates the problem of having the caller free any 1127*1741Srmesta * malloc'd space. 1128*1741Srmesta */ 1129*1741Srmesta char * 1130*1741Srmesta mapid_get_domain(void) 1131*1741Srmesta { 1132*1741Srmesta void *tsd = NULL; 1133*1741Srmesta 1134*1741Srmesta (void) thr_getspecific(s_thr_key, &tsd); 1135*1741Srmesta if (tsd == NULL) { 1136*1741Srmesta tsd = malloc(NS_MAXCDNAME+1); 1137*1741Srmesta if (tsd != NULL) { 1138*1741Srmesta (void) rw_rdlock(&mapid_domain_lock); 1139*1741Srmesta (void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME); 1140*1741Srmesta (void) rw_unlock(&mapid_domain_lock); 1141*1741Srmesta (void) thr_setspecific(s_thr_key, tsd); 1142*1741Srmesta } 1143*1741Srmesta } else { 1144*1741Srmesta (void) rw_rdlock(&mapid_domain_lock); 1145*1741Srmesta (void) strncpy((char *)tsd, mapid_domain, NS_MAXCDNAME); 1146*1741Srmesta (void) rw_unlock(&mapid_domain_lock); 1147*1741Srmesta } 1148*1741Srmesta return ((char *)tsd); 1149*1741Srmesta } 1150*1741Srmesta 1151*1741Srmesta /* 1152*1741Srmesta * PSARC 2005/487 Contracted Sun Private Interface 1153*1741Srmesta * mapid_derive_domain() 1154*1741Srmesta * Changes must be reviewed by Solaris File Sharing 1155*1741Srmesta * Changes must be communicated to contract-2005-487-01@sun.com 1156*1741Srmesta * 1157*1741Srmesta * This interface is called solely via sysidnfs4 iff no 1158*1741Srmesta * NFSMAPID_DOMAIN was found. So, there is no ill effect 1159*1741Srmesta * of having the reeval function call get_nfs_domain(). 1160*1741Srmesta */ 1161*1741Srmesta char * 1162*1741Srmesta mapid_derive_domain(void) 1163*1741Srmesta { 1164*1741Srmesta cb_t cb = {0}; 1165*1741Srmesta 1166*1741Srmesta _lib_init(); 1167*1741Srmesta mapid_reeval_domain(&cb); 1168*1741Srmesta return (mapid_get_domain()); 1169*1741Srmesta } 1170*1741Srmesta 1171*1741Srmesta void 1172*1741Srmesta _lib_init(void) 1173*1741Srmesta { 1174*1741Srmesta (void) resolv_init(); 1175*1741Srmesta (void) rwlock_init(&mapid_domain_lock, USYNC_THREAD, NULL); 1176*1741Srmesta (void) thr_keycreate(&s_thr_key, NULL); 1177*1741Srmesta lib_init_done++; 1178*1741Srmesta } 1179