1*4904Srs200217 /* 2*4904Srs200217 * CDDL HEADER START 3*4904Srs200217 * 4*4904Srs200217 * The contents of this file are subject to the terms of the 5*4904Srs200217 * Common Development and Distribution License (the "License"). 6*4904Srs200217 * You may not use this file except in compliance with the License. 7*4904Srs200217 * 8*4904Srs200217 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*4904Srs200217 * or http://www.opensolaris.org/os/licensing. 10*4904Srs200217 * See the License for the specific language governing permissions 11*4904Srs200217 * and limitations under the License. 12*4904Srs200217 * 13*4904Srs200217 * When distributing Covered Code, include this CDDL HEADER in each 14*4904Srs200217 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*4904Srs200217 * If applicable, add the following below this CDDL HEADER, with the 16*4904Srs200217 * fields enclosed by brackets "[]" replaced with your own identifying 17*4904Srs200217 * information: Portions Copyright [yyyy] [name of copyright owner] 18*4904Srs200217 * 19*4904Srs200217 * CDDL HEADER END 20*4904Srs200217 */ 21*4904Srs200217 22*4904Srs200217 /* 23*4904Srs200217 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24*4904Srs200217 * Use is subject to license terms. 25*4904Srs200217 */ 26*4904Srs200217 27*4904Srs200217 #pragma ident "%Z%%M% %I% %E% SMI" 28*4904Srs200217 29*4904Srs200217 #include "mdns_common.h" 30*4904Srs200217 31*4904Srs200217 static int _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype, 32*4904Srs200217 DNSServiceQueryRecordReply callback, 33*4904Srs200217 struct mdns_querydata *data); 34*4904Srs200217 static void _nss_mdns_get_svcstatetimestamp(struct timeval *); 35*4904Srs200217 static void _nss_mdns_loadsmfcfg(mdns_backend_ptr_t); 36*4904Srs200217 static void _nss_mdns_freesmfcfg(mdns_backend_ptr_t); 37*4904Srs200217 static boolean_t cmpdmn(char *, char **, int); 38*4904Srs200217 static char *RDataToName(char *data, char *buffer, int datalen, int buflen); 39*4904Srs200217 static int searchdomain(mdns_backend_ptr_t, char *, int, char **); 40*4904Srs200217 static boolean_t validdomain(mdns_backend_ptr_t, char *, int); 41*4904Srs200217 42*4904Srs200217 /* 43*4904Srs200217 * This file includes the functions to query for host name 44*4904Srs200217 * information via Multicast DNS (mDNS). The function 45*4904Srs200217 * _nss_mdns_queryrecord queries for the host information via 46*4904Srs200217 * Multicast DNS. _nss_mdns_querybyname and _nss_mdns_querybyaddr 47*4904Srs200217 * query for host IP address and hostname by querying for A/AAAA 48*4904Srs200217 * and PTR DNS resource records respectively. DNSServiceQueryRecord 49*4904Srs200217 * in libdns_sd sends a request to the mDNS daemon (mdnsd) to place 50*4904Srs200217 * the DNS query via multicast and return the results. 51*4904Srs200217 * mdnsd is managed by SMF (FMRI: svc:/network/dns/multicast:default). 52*4904Srs200217 * 53*4904Srs200217 * gethostent.c and gethostent6.c implement the nsswitch 'hosts' 54*4904Srs200217 * backend module getXbyY functions: getbyname and getbyaddr. 55*4904Srs200217 * getby* functions in gethostent.c supports only IPv4 and 56*4904Srs200217 * getby* functions in gethostent6.c returns both IPv4 and 57*4904Srs200217 * IPv6 results. Functions in gethostent.c and gethostent6.c 58*4904Srs200217 * call the _nss_mdns_queryby* functions in mdns_common.c to 59*4904Srs200217 * query for host information via mDNS. 60*4904Srs200217 * 61*4904Srs200217 * Configuration for mdns is stored in SMF and is accessed using 62*4904Srs200217 * the FMRI: svc:/network/dns/multicast:default. Configuration 63*4904Srs200217 * includes the list of valid DNS domains checked before querying host 64*4904Srs200217 * information via mDNS and the search list to use for host lookup via 65*4904Srs200217 * mDNS. The default valid domain list in the mDNS service supports host 66*4904Srs200217 * lookups for hostnames in the ".local" domain and hostname queries 67*4904Srs200217 * for link-local IPv4 and IPv6 addresses. _nss_mdns_loadsmfcfg 68*4904Srs200217 * loads the nss_mdns configuration from SMF and the function 69*4904Srs200217 * _nss_mdns_updatecfg checks for any updates in nss_mdns configuration. 70*4904Srs200217 */ 71*4904Srs200217 72*4904Srs200217 static int 73*4904Srs200217 _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype, 74*4904Srs200217 DNSServiceQueryRecordReply callback, 75*4904Srs200217 struct mdns_querydata *data) 76*4904Srs200217 { 77*4904Srs200217 int sockfd; 78*4904Srs200217 int flags = kDNSServiceFlagsForceMulticast; /* Multicast only */ 79*4904Srs200217 int opinterface = kDNSServiceInterfaceIndexAny; 80*4904Srs200217 DNSServiceErrorType err; 81*4904Srs200217 DNSServiceRef ref = NULL; 82*4904Srs200217 int ret; 83*4904Srs200217 struct fd_set readfds; 84*4904Srs200217 struct timeval tv; 85*4904Srs200217 86*4904Srs200217 data->status = NSS_NOTFOUND; 87*4904Srs200217 #ifdef DEBUG 88*4904Srs200217 syslog(LOG_DEBUG, "nss_mdns: query called rrname:%s rrtype:%d", 89*4904Srs200217 rrname, rrtype); 90*4904Srs200217 #endif 91*4904Srs200217 err = DNSServiceQueryRecord(&ref, flags, opinterface, 92*4904Srs200217 rrname, rrtype, rrclass, callback, data); 93*4904Srs200217 if (err != kDNSServiceErr_NoError || ref == NULL || 94*4904Srs200217 (sockfd = DNSServiceRefSockFD(ref)) == NULL) { 95*4904Srs200217 DNSServiceRefDeallocate(ref); 96*4904Srs200217 data->status = NSS_UNAVAIL; 97*4904Srs200217 return (NSS_UNAVAIL); 98*4904Srs200217 } 99*4904Srs200217 100*4904Srs200217 do { 101*4904Srs200217 FD_ZERO(&readfds); 102*4904Srs200217 FD_SET(sockfd, &readfds); 103*4904Srs200217 tv.tv_sec = NSSMDNS_MAXQRYTMO; 104*4904Srs200217 tv.tv_usec = 0; 105*4904Srs200217 106*4904Srs200217 /* Wait until response received from mDNS daemon */ 107*4904Srs200217 ret = select(sockfd + 1, &readfds, NULL, NULL, &tv); 108*4904Srs200217 if (!((ret > 0) && FD_ISSET(sockfd, &readfds) && 109*4904Srs200217 (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError))) { 110*4904Srs200217 data->status = NSS_NOTFOUND; 111*4904Srs200217 if (errno != EINTR) 112*4904Srs200217 data->qrydone = B_TRUE; 113*4904Srs200217 } 114*4904Srs200217 } while (data->qrydone != B_TRUE); 115*4904Srs200217 116*4904Srs200217 if (data->status == NSS_SUCCESS && (data->withttlbuffer == NULL)) { 117*4904Srs200217 nss_XbyY_args_t *argp = data->argp; 118*4904Srs200217 if (argp->buf.result != NULL) { 119*4904Srs200217 int stat; 120*4904Srs200217 121*4904Srs200217 if (data->buffer == NULL) { 122*4904Srs200217 data->status = NSS_NOTFOUND; 123*4904Srs200217 DNSServiceRefDeallocate(ref); 124*4904Srs200217 return (data->status); 125*4904Srs200217 } 126*4904Srs200217 stat = (*argp->str2ent)(data->buffer, 127*4904Srs200217 strlen(data->buffer), 128*4904Srs200217 argp->buf.result, argp->buf.buffer, 129*4904Srs200217 argp->buf.buflen); 130*4904Srs200217 if (stat == NSS_STR_PARSE_SUCCESS) { 131*4904Srs200217 argp->returnval = argp->buf.result; 132*4904Srs200217 argp->returnlen = 1; 133*4904Srs200217 } else { 134*4904Srs200217 data->status = NSS_NOTFOUND; 135*4904Srs200217 if (stat == NSS_STR_PARSE_ERANGE) 136*4904Srs200217 argp->erange = 1; 137*4904Srs200217 } 138*4904Srs200217 free(data->buffer); 139*4904Srs200217 } else { 140*4904Srs200217 argp->returnval = argp->buf.buffer; 141*4904Srs200217 argp->returnlen = strlen(argp->buf.buffer); 142*4904Srs200217 } 143*4904Srs200217 data->buffer = NULL; 144*4904Srs200217 data->buflen = 0; 145*4904Srs200217 } 146*4904Srs200217 147*4904Srs200217 if (data->status != NSS_SUCCESS) 148*4904Srs200217 data->argp->h_errno = HOST_NOT_FOUND; 149*4904Srs200217 150*4904Srs200217 DNSServiceRefDeallocate(ref); 151*4904Srs200217 return (data->status); 152*4904Srs200217 } 153*4904Srs200217 154*4904Srs200217 static void 155*4904Srs200217 /* LINTED E_FUNC_ARG_UNUSED */ 156*4904Srs200217 _nss_mdns_querynamereply(DNSServiceRef sdRef, const DNSServiceFlags flags, 157*4904Srs200217 /* LINTED E_FUNC_ARG_UNUSED */ 158*4904Srs200217 uint32_t ifIndex, DNSServiceErrorType errorCode, 159*4904Srs200217 const char *fullname, uint16_t rrtype, uint16_t rrclass, 160*4904Srs200217 /* LINTED E_FUNC_ARG_UNUSED */ 161*4904Srs200217 uint16_t rdlen, const void *rdata, uint32_t ttl, 162*4904Srs200217 void *context) 163*4904Srs200217 { 164*4904Srs200217 struct mdns_querydata *qdata; 165*4904Srs200217 nss_XbyY_args_t *argp; 166*4904Srs200217 int firstent = 0; 167*4904Srs200217 int af; 168*4904Srs200217 char addrstore[INET6_ADDRSTRLEN]; 169*4904Srs200217 char *buffer; 170*4904Srs200217 int len; 171*4904Srs200217 int remlen; 172*4904Srs200217 173*4904Srs200217 qdata = (struct mdns_querydata *)context; 174*4904Srs200217 argp = qdata->argp; 175*4904Srs200217 176*4904Srs200217 if (errorCode != kDNSServiceErr_NoError) { 177*4904Srs200217 qdata->qrydone = B_TRUE; 178*4904Srs200217 return; 179*4904Srs200217 } 180*4904Srs200217 if ((flags & kDNSServiceFlagsMoreComing)) 181*4904Srs200217 qdata->qrydone = B_FALSE; 182*4904Srs200217 else 183*4904Srs200217 qdata->qrydone = B_TRUE; 184*4904Srs200217 if (!(flags & kDNSServiceFlagsAdd)) 185*4904Srs200217 return; 186*4904Srs200217 if (rrclass != kDNSServiceClass_IN) 187*4904Srs200217 return; 188*4904Srs200217 189*4904Srs200217 if (rrtype == kDNSServiceType_A) 190*4904Srs200217 af = AF_INET; 191*4904Srs200217 else if (rrtype == kDNSServiceType_AAAA) 192*4904Srs200217 af = AF_INET6; 193*4904Srs200217 else 194*4904Srs200217 return; 195*4904Srs200217 196*4904Srs200217 if (qdata->buffer == NULL) { 197*4904Srs200217 if (qdata->withttlbsize > 0) { 198*4904Srs200217 remlen = qdata->buflen = 199*4904Srs200217 qdata->withttlbsize; 200*4904Srs200217 buffer = qdata->buffer = 201*4904Srs200217 qdata->withttlbuffer; 202*4904Srs200217 (void) memset(qdata->buffer, 0, remlen); 203*4904Srs200217 } else { 204*4904Srs200217 remlen = qdata->buflen = 205*4904Srs200217 argp->buf.buflen; 206*4904Srs200217 if (argp->buf.result != NULL) { 207*4904Srs200217 buffer = qdata->buffer = 208*4904Srs200217 calloc(1, remlen); 209*4904Srs200217 } else { 210*4904Srs200217 /* Return in file format */ 211*4904Srs200217 (void) memset(argp->buf.buffer, 212*4904Srs200217 0, remlen); 213*4904Srs200217 buffer = qdata->buffer = argp->buf.buffer; 214*4904Srs200217 } 215*4904Srs200217 } 216*4904Srs200217 firstent = 1; 217*4904Srs200217 } else { 218*4904Srs200217 buffer = qdata->buffer + strlen(qdata->buffer); 219*4904Srs200217 remlen = qdata->buflen - strlen(qdata->buffer); 220*4904Srs200217 } 221*4904Srs200217 222*4904Srs200217 #ifdef DEBUG 223*4904Srs200217 syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen); 224*4904Srs200217 #endif 225*4904Srs200217 if (inet_ntop(af, rdata, addrstore, INET6_ADDRSTRLEN) != NULL) { 226*4904Srs200217 if (firstent) 227*4904Srs200217 len = snprintf(buffer, remlen, "%s %s", 228*4904Srs200217 addrstore, fullname); 229*4904Srs200217 else 230*4904Srs200217 len = snprintf(buffer, remlen, "\n%s %s", 231*4904Srs200217 addrstore, fullname); 232*4904Srs200217 if (len >= remlen || len < 0) { 233*4904Srs200217 qdata->status = NSS_NOTFOUND; 234*4904Srs200217 qdata->argp->erange = 1; 235*4904Srs200217 qdata->argp->h_errno = HOST_NOT_FOUND; 236*4904Srs200217 return; 237*4904Srs200217 } 238*4904Srs200217 qdata->ttl = ttl; 239*4904Srs200217 qdata->status = NSS_SUCCESS; 240*4904Srs200217 #ifdef DEBUG 241*4904Srs200217 syslog(LOG_DEBUG, "nss_mdns: querynamereply buffer:%s", buffer); 242*4904Srs200217 #endif 243*4904Srs200217 } else { 244*4904Srs200217 qdata->status = NSS_NOTFOUND; 245*4904Srs200217 qdata->argp->h_errno = HOST_NOT_FOUND; 246*4904Srs200217 } 247*4904Srs200217 } 248*4904Srs200217 249*4904Srs200217 int 250*4904Srs200217 _nss_mdns_querybyname(mdns_backend_ptr_t be, char *qname, 251*4904Srs200217 int af, struct mdns_querydata *data) 252*4904Srs200217 { 253*4904Srs200217 int rrtype; 254*4904Srs200217 int rrclass; 255*4904Srs200217 int srchidx = 0; 256*4904Srs200217 int rc; 257*4904Srs200217 char hname[MAXDNAME]; 258*4904Srs200217 char *name; 259*4904Srs200217 char *sname; 260*4904Srs200217 261*4904Srs200217 rrclass = kDNSServiceClass_IN; 262*4904Srs200217 if (af == AF_INET6) 263*4904Srs200217 rrtype = kDNSServiceType_ANY; 264*4904Srs200217 else if (af == AF_INET) 265*4904Srs200217 rrtype = kDNSServiceType_A; 266*4904Srs200217 else 267*4904Srs200217 return (NSS_NOTFOUND); 268*4904Srs200217 269*4904Srs200217 name = strdup(qname); 270*4904Srs200217 if (name == NULL) 271*4904Srs200217 return (NSS_UNAVAIL); 272*4904Srs200217 273*4904Srs200217 while ((srchidx = searchdomain(be, name, srchidx, &sname)) != -1) { 274*4904Srs200217 if (sname != NULL) 275*4904Srs200217 (void) snprintf(hname, sizeof (hname), "%s.%s", 276*4904Srs200217 name, sname); 277*4904Srs200217 else 278*4904Srs200217 (void) strlcpy(hname, name, sizeof (hname)); 279*4904Srs200217 #ifdef DEBUG 280*4904Srs200217 syslog(LOG_DEBUG, "nss_mdns: querybyname called" \ 281*4904Srs200217 " srchidx:%d af:%d hname:%s", srchidx, af, qname); 282*4904Srs200217 #endif 283*4904Srs200217 rc = _nss_mdns_queryrecord(hname, rrclass, rrtype, 284*4904Srs200217 _nss_mdns_querynamereply, data); 285*4904Srs200217 if ((rc == NSS_UNAVAIL) || (rc == NSS_SUCCESS)) { 286*4904Srs200217 free(name); 287*4904Srs200217 return (rc); 288*4904Srs200217 } 289*4904Srs200217 } 290*4904Srs200217 free(name); 291*4904Srs200217 return (NSS_NOTFOUND); 292*4904Srs200217 } 293*4904Srs200217 294*4904Srs200217 static void 295*4904Srs200217 /* LINTED E_FUNC_ARG_UNUSED */ 296*4904Srs200217 _nss_mdns_queryaddrreply(DNSServiceRef sdRef, const DNSServiceFlags flags, 297*4904Srs200217 /* LINTED E_FUNC_ARG_UNUSED */ 298*4904Srs200217 uint32_t ifIndex, DNSServiceErrorType errorCode, 299*4904Srs200217 /* LINTED E_FUNC_ARG_UNUSED */ 300*4904Srs200217 const char *fullname, uint16_t rrtype, uint16_t rrclass, 301*4904Srs200217 uint16_t rdlen, const void *rdata, uint32_t ttl, 302*4904Srs200217 void *context) 303*4904Srs200217 { 304*4904Srs200217 struct mdns_querydata *qdata; 305*4904Srs200217 nss_XbyY_args_t *argp; 306*4904Srs200217 char hostname[NI_MAXHOST]; 307*4904Srs200217 int firstent = 0; 308*4904Srs200217 char *buffer; 309*4904Srs200217 int len; 310*4904Srs200217 int remlen; 311*4904Srs200217 312*4904Srs200217 qdata = (struct mdns_querydata *)context; 313*4904Srs200217 argp = qdata->argp; 314*4904Srs200217 315*4904Srs200217 if (errorCode != kDNSServiceErr_NoError) { 316*4904Srs200217 qdata->qrydone = B_TRUE; 317*4904Srs200217 return; 318*4904Srs200217 } 319*4904Srs200217 if ((flags & kDNSServiceFlagsMoreComing)) 320*4904Srs200217 qdata->qrydone = B_FALSE; 321*4904Srs200217 else 322*4904Srs200217 qdata->qrydone = B_TRUE; 323*4904Srs200217 if (!(flags & kDNSServiceFlagsAdd)) 324*4904Srs200217 return; 325*4904Srs200217 if (rrclass != kDNSServiceClass_IN) 326*4904Srs200217 return; 327*4904Srs200217 if (rrtype != kDNSServiceType_PTR) 328*4904Srs200217 return; 329*4904Srs200217 330*4904Srs200217 if (qdata->buffer == NULL) { 331*4904Srs200217 remlen = qdata->buflen = argp->buf.buflen; 332*4904Srs200217 if (argp->buf.result != NULL) { 333*4904Srs200217 buffer = qdata->buffer = calloc(1, remlen); 334*4904Srs200217 } else { 335*4904Srs200217 /* Return in file format */ 336*4904Srs200217 (void) memset(argp->buf.buffer, 0, remlen); 337*4904Srs200217 buffer = qdata->buffer = argp->buf.buffer; 338*4904Srs200217 } 339*4904Srs200217 firstent = 1; 340*4904Srs200217 } else { 341*4904Srs200217 buffer = qdata->buffer + strlen(qdata->buffer); 342*4904Srs200217 remlen = qdata->buflen - strlen(qdata->buffer); 343*4904Srs200217 } 344*4904Srs200217 345*4904Srs200217 if (RDataToName((char *)rdata, hostname, rdlen, NI_MAXHOST) == NULL) { 346*4904Srs200217 qdata->status = NSS_NOTFOUND; 347*4904Srs200217 qdata->argp->h_errno = HOST_NOT_FOUND; 348*4904Srs200217 return; 349*4904Srs200217 } 350*4904Srs200217 351*4904Srs200217 #ifdef DEBUG 352*4904Srs200217 syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen); 353*4904Srs200217 #endif 354*4904Srs200217 if (firstent) 355*4904Srs200217 len = snprintf(buffer, remlen, "%s %s", 356*4904Srs200217 qdata->paddrbuf, hostname); 357*4904Srs200217 else 358*4904Srs200217 len = snprintf(buffer, remlen, "\n%s %s", 359*4904Srs200217 qdata->paddrbuf, hostname); 360*4904Srs200217 if (len >= remlen || len < 0) { 361*4904Srs200217 qdata->status = NSS_NOTFOUND; 362*4904Srs200217 qdata->argp->erange = 1; 363*4904Srs200217 qdata->argp->h_errno = HOST_NOT_FOUND; 364*4904Srs200217 return; 365*4904Srs200217 } 366*4904Srs200217 qdata->status = NSS_SUCCESS; 367*4904Srs200217 qdata->ttl = ttl; 368*4904Srs200217 } 369*4904Srs200217 370*4904Srs200217 int 371*4904Srs200217 /* LINTED E_FUNC_ARG_UNUSED */ 372*4904Srs200217 _nss_mdns_querybyaddr(mdns_backend_ptr_t be, char *name, int af, 373*4904Srs200217 struct mdns_querydata *data) 374*4904Srs200217 { 375*4904Srs200217 int rrtype; 376*4904Srs200217 int rrclass; 377*4904Srs200217 378*4904Srs200217 #ifdef DEBUG 379*4904Srs200217 syslog(LOG_DEBUG, "nss_mdns: querybyaddr called" \ 380*4904Srs200217 " af:%d addr:%s", af, name); 381*4904Srs200217 #endif 382*4904Srs200217 rrclass = kDNSServiceClass_IN; 383*4904Srs200217 rrtype = kDNSServiceType_PTR; 384*4904Srs200217 385*4904Srs200217 if (validdomain(be, name, 0) == B_FALSE) { 386*4904Srs200217 data->status = NSS_NOTFOUND; 387*4904Srs200217 return (NSS_NOTFOUND); 388*4904Srs200217 } 389*4904Srs200217 return (_nss_mdns_queryrecord(name, rrclass, rrtype, 390*4904Srs200217 _nss_mdns_queryaddrreply, data)); 391*4904Srs200217 } 392*4904Srs200217 393*4904Srs200217 /* 394*4904Srs200217 * Converts the encoded name in RData returned 395*4904Srs200217 * by mDNS query to name in file format 396*4904Srs200217 */ 397*4904Srs200217 static char * 398*4904Srs200217 RDataToName(char *data, char *buffer, int datalen, int buflen) 399*4904Srs200217 { 400*4904Srs200217 char *src = data; 401*4904Srs200217 char *srcend = data + datalen; 402*4904Srs200217 char *ptr = buffer; 403*4904Srs200217 char *end; 404*4904Srs200217 char *bend = buffer + buflen - 1; /* terminal '\0' */ 405*4904Srs200217 int domainlen = 0; 406*4904Srs200217 407*4904Srs200217 while ((src < srcend) && (*src != 0)) { 408*4904Srs200217 409*4904Srs200217 /* first byte is len */ 410*4904Srs200217 domainlen = *src++; 411*4904Srs200217 end = src + domainlen; 412*4904Srs200217 413*4904Srs200217 while ((src < end) && (ptr < bend)) { 414*4904Srs200217 uint8_t ch = *src++; 415*4904Srs200217 if (ch == '.' || ch == '\\') { 416*4904Srs200217 *ptr++ = '\\'; 417*4904Srs200217 } 418*4904Srs200217 *ptr++ = ch; 419*4904Srs200217 } 420*4904Srs200217 421*4904Srs200217 /* 422*4904Srs200217 * Check if we copied entire domain str. and 423*4904Srs200217 * if space is still remaining for '.' seperator 424*4904Srs200217 */ 425*4904Srs200217 if ((src != end) || (ptr == bend)) 426*4904Srs200217 return (NULL); 427*4904Srs200217 *ptr++ = '.'; 428*4904Srs200217 } 429*4904Srs200217 *ptr = '\0'; 430*4904Srs200217 return (ptr); 431*4904Srs200217 } 432*4904Srs200217 433*4904Srs200217 nss_backend_t * 434*4904Srs200217 _nss_mdns_constr(mdns_backend_op_t ops[], int n_ops) 435*4904Srs200217 { 436*4904Srs200217 mdns_backend_ptr_t be; 437*4904Srs200217 438*4904Srs200217 if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL) 439*4904Srs200217 return (NULL); 440*4904Srs200217 be->ops = ops; 441*4904Srs200217 be->n_ops = n_ops; 442*4904Srs200217 _nss_mdns_updatecfg(be); 443*4904Srs200217 return ((nss_backend_t *)be); 444*4904Srs200217 } 445*4904Srs200217 446*4904Srs200217 void 447*4904Srs200217 _nss_mdns_destr(mdns_backend_ptr_t be) 448*4904Srs200217 { 449*4904Srs200217 if (be != NULL) { 450*4904Srs200217 _nss_mdns_freesmfcfg(be); 451*4904Srs200217 free(be); 452*4904Srs200217 } 453*4904Srs200217 } 454*4904Srs200217 455*4904Srs200217 static int 456*4904Srs200217 searchdomain(mdns_backend_ptr_t be, char *name, int srchidx, char **sname) 457*4904Srs200217 { 458*4904Srs200217 int trailing_dot = 0; 459*4904Srs200217 char *ch; 460*4904Srs200217 *sname = NULL; 461*4904Srs200217 462*4904Srs200217 ch = name + strlen(name) - 1; 463*4904Srs200217 if ((*ch) == '.') 464*4904Srs200217 trailing_dot++; 465*4904Srs200217 466*4904Srs200217 if (trailing_dot && srchidx > 0) 467*4904Srs200217 /* 468*4904Srs200217 * If there is a trailing dot in the query 469*4904Srs200217 * name, do not perform any additional queries 470*4904Srs200217 * with search domains. 471*4904Srs200217 */ 472*4904Srs200217 return (-1); 473*4904Srs200217 474*4904Srs200217 if (srchidx == 0) { 475*4904Srs200217 /* 476*4904Srs200217 * If there is a trailing dot in the query 477*4904Srs200217 * or atleast one dot in the query name then 478*4904Srs200217 * perform a query as-is once first. 479*4904Srs200217 */ 480*4904Srs200217 ++srchidx; 481*4904Srs200217 if ((trailing_dot || (strchr(name, '.') != NULL))) { 482*4904Srs200217 if (validdomain(be, name, 1) == B_TRUE) 483*4904Srs200217 return (srchidx); 484*4904Srs200217 else if (trailing_dot) 485*4904Srs200217 return (-1); 486*4904Srs200217 } 487*4904Srs200217 } 488*4904Srs200217 489*4904Srs200217 if ((srchidx > NSSMDNS_MAXSRCHDMNS) || 490*4904Srs200217 (be->dmnsrchlist[srchidx-1] == NULL)) 491*4904Srs200217 return (-1); 492*4904Srs200217 493*4904Srs200217 *sname = be->dmnsrchlist[srchidx-1]; 494*4904Srs200217 return (++srchidx); 495*4904Srs200217 } 496*4904Srs200217 497*4904Srs200217 /* 498*4904Srs200217 * This function determines if the domain name in the query 499*4904Srs200217 * matches any of the valid & search domains in the nss_mdns 500*4904Srs200217 * configuration. 501*4904Srs200217 */ 502*4904Srs200217 static boolean_t 503*4904Srs200217 validdomain(mdns_backend_ptr_t be, char *name, int chksrchdmns) 504*4904Srs200217 { 505*4904Srs200217 char *nameptr; 506*4904Srs200217 507*4904Srs200217 /* Remove any trailing and leading dots in the name */ 508*4904Srs200217 nameptr = name + strlen(name) - 1; 509*4904Srs200217 while (*nameptr && (nameptr != name) && (*nameptr == '.')) 510*4904Srs200217 nameptr--; 511*4904Srs200217 *(++nameptr) = '\0'; 512*4904Srs200217 nameptr = name; 513*4904Srs200217 while (*nameptr && (*nameptr == '.')) 514*4904Srs200217 nameptr++; 515*4904Srs200217 if (*nameptr == '\0') 516*4904Srs200217 return (B_FALSE); 517*4904Srs200217 518*4904Srs200217 /* Compare with search domains */ 519*4904Srs200217 if (chksrchdmns && (cmpdmn(nameptr, be->dmnsrchlist, 520*4904Srs200217 NSSMDNS_MAXSRCHDMNS) == B_TRUE)) 521*4904Srs200217 return (B_TRUE); 522*4904Srs200217 523*4904Srs200217 /* Compare with valid domains */ 524*4904Srs200217 return (cmpdmn(nameptr, be->validdmnlist, NSSMDNS_MAXVALIDDMNS)); 525*4904Srs200217 } 526*4904Srs200217 527*4904Srs200217 static boolean_t 528*4904Srs200217 cmpdmn(char *name, char **dmnlist, int maxdmns) 529*4904Srs200217 { 530*4904Srs200217 char *vptr; 531*4904Srs200217 int vdlen; 532*4904Srs200217 char *cptr; 533*4904Srs200217 int nlen; 534*4904Srs200217 int i; 535*4904Srs200217 536*4904Srs200217 nlen = strlen(name); 537*4904Srs200217 for (i = 0; (i < maxdmns) && 538*4904Srs200217 ((vptr = dmnlist[i]) != NULL); i++) { 539*4904Srs200217 vdlen = strlen(vptr); 540*4904Srs200217 if (vdlen > nlen) 541*4904Srs200217 continue; 542*4904Srs200217 cptr = name + nlen - vdlen; 543*4904Srs200217 if (strncasecmp(cptr, vptr, vdlen) == 0) 544*4904Srs200217 return (B_TRUE); 545*4904Srs200217 } 546*4904Srs200217 return (B_FALSE); 547*4904Srs200217 } 548*4904Srs200217 549*4904Srs200217 static void 550*4904Srs200217 _nss_mdns_get_svcstatetimestamp(struct timeval *ptv) 551*4904Srs200217 { 552*4904Srs200217 scf_handle_t *h; 553*4904Srs200217 scf_simple_prop_t *sprop; 554*4904Srs200217 int32_t nsec; 555*4904Srs200217 556*4904Srs200217 (void) memset(ptv, 0, sizeof (struct timeval)); 557*4904Srs200217 558*4904Srs200217 h = scf_handle_create(SCF_VERSION); 559*4904Srs200217 if (h == NULL) 560*4904Srs200217 return; 561*4904Srs200217 562*4904Srs200217 if (scf_handle_bind(h) == -1) { 563*4904Srs200217 scf_handle_destroy(h); 564*4904Srs200217 return; 565*4904Srs200217 } 566*4904Srs200217 567*4904Srs200217 if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI, 568*4904Srs200217 SCF_PG_RESTARTER, SCF_PROPERTY_STATE_TIMESTAMP)) != NULL) { 569*4904Srs200217 ptv->tv_sec = *(time_t *)(scf_simple_prop_next_time(sprop, 570*4904Srs200217 &nsec)); 571*4904Srs200217 ptv->tv_usec = nsec / 1000; 572*4904Srs200217 scf_simple_prop_free(sprop); 573*4904Srs200217 } 574*4904Srs200217 575*4904Srs200217 if (h != NULL) 576*4904Srs200217 scf_handle_destroy(h); 577*4904Srs200217 } 578*4904Srs200217 579*4904Srs200217 void 580*4904Srs200217 _nss_mdns_updatecfg(mdns_backend_ptr_t be) 581*4904Srs200217 { 582*4904Srs200217 struct timeval statetimestamp; 583*4904Srs200217 584*4904Srs200217 /* 585*4904Srs200217 * Update configuration if current svc state timestamp 586*4904Srs200217 * is different from last known svc state timestamp 587*4904Srs200217 */ 588*4904Srs200217 _nss_mdns_get_svcstatetimestamp(&statetimestamp); 589*4904Srs200217 if ((statetimestamp.tv_sec == 0) && (statetimestamp.tv_usec == 0)) { 590*4904Srs200217 syslog(LOG_ERR, "nss_mdns: error checking " \ 591*4904Srs200217 "svc:/network/dns/multicast:default" \ 592*4904Srs200217 " service timestamp"); 593*4904Srs200217 } else if ((be->conftimestamp.tv_sec == statetimestamp.tv_sec) && 594*4904Srs200217 (be->conftimestamp.tv_usec == statetimestamp.tv_usec)) { 595*4904Srs200217 return; 596*4904Srs200217 } 597*4904Srs200217 598*4904Srs200217 _nss_mdns_freesmfcfg(be); 599*4904Srs200217 _nss_mdns_loadsmfcfg(be); 600*4904Srs200217 be->conftimestamp.tv_sec = statetimestamp.tv_sec; 601*4904Srs200217 be->conftimestamp.tv_usec = statetimestamp.tv_usec; 602*4904Srs200217 } 603*4904Srs200217 604*4904Srs200217 static void 605*4904Srs200217 load_mdns_domaincfg(scf_handle_t *h, char **storelist, 606*4904Srs200217 const char *scfprop, int maxprops) 607*4904Srs200217 { 608*4904Srs200217 scf_simple_prop_t *sprop; 609*4904Srs200217 char *tchr; 610*4904Srs200217 char *pchr; 611*4904Srs200217 int tlen; 612*4904Srs200217 int cnt = 0; 613*4904Srs200217 614*4904Srs200217 if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI, 615*4904Srs200217 SMF_NSSMDNSCFG_PROPGRP, scfprop)) == NULL) 616*4904Srs200217 return; 617*4904Srs200217 618*4904Srs200217 while ((cnt < maxprops) && 619*4904Srs200217 (tchr = scf_simple_prop_next_astring(sprop)) != NULL) { 620*4904Srs200217 621*4904Srs200217 /* Remove beginning & trailing '.' chars */ 622*4904Srs200217 while (*tchr && (*tchr == '.')) 623*4904Srs200217 tchr++; 624*4904Srs200217 625*4904Srs200217 if (*tchr && ((tlen = strlen(tchr)) < MAXDNAME)) { 626*4904Srs200217 pchr = &tchr[tlen-1]; 627*4904Srs200217 while ((pchr != tchr) && (*pchr == '.')) 628*4904Srs200217 pchr--; 629*4904Srs200217 *(++pchr) = '\0'; 630*4904Srs200217 storelist[cnt] = strdup(tchr); 631*4904Srs200217 cnt++; 632*4904Srs200217 } 633*4904Srs200217 } 634*4904Srs200217 scf_simple_prop_free(sprop); 635*4904Srs200217 } 636*4904Srs200217 637*4904Srs200217 static void 638*4904Srs200217 _nss_mdns_loadsmfcfg(mdns_backend_ptr_t be) 639*4904Srs200217 { 640*4904Srs200217 scf_handle_t *h; 641*4904Srs200217 642*4904Srs200217 h = scf_handle_create(SCF_VERSION); 643*4904Srs200217 if (h == NULL) 644*4904Srs200217 return; 645*4904Srs200217 646*4904Srs200217 if (scf_handle_bind(h) == -1) { 647*4904Srs200217 scf_handle_destroy(h); 648*4904Srs200217 return; 649*4904Srs200217 } 650*4904Srs200217 651*4904Srs200217 load_mdns_domaincfg(h, &(be->dmnsrchlist[0]), 652*4904Srs200217 SMF_NSSMDNSCFG_SRCHPROP, NSSMDNS_MAXSRCHDMNS); 653*4904Srs200217 654*4904Srs200217 load_mdns_domaincfg(h, &(be->validdmnlist[0]), 655*4904Srs200217 SMF_NSSMDNSCFG_DMNPROP, NSSMDNS_MAXVALIDDMNS); 656*4904Srs200217 657*4904Srs200217 if (h != NULL) 658*4904Srs200217 scf_handle_destroy(h); 659*4904Srs200217 } 660*4904Srs200217 661*4904Srs200217 static void 662*4904Srs200217 _nss_mdns_freesmfcfg(mdns_backend_ptr_t be) 663*4904Srs200217 { 664*4904Srs200217 int idx; 665*4904Srs200217 if (be == NULL) 666*4904Srs200217 return; 667*4904Srs200217 for (idx = 0; idx < NSSMDNS_MAXSRCHDMNS; idx++) { 668*4904Srs200217 if (be->dmnsrchlist[idx] != NULL) { 669*4904Srs200217 free(be->dmnsrchlist[idx]); 670*4904Srs200217 be->dmnsrchlist[idx] = NULL; 671*4904Srs200217 } 672*4904Srs200217 } 673*4904Srs200217 for (idx = 0; idx < NSSMDNS_MAXVALIDDMNS; idx++) { 674*4904Srs200217 if (be->validdmnlist[idx] != NULL) { 675*4904Srs200217 free(be->validdmnlist[idx]); 676*4904Srs200217 be->validdmnlist[idx] = NULL; 677*4904Srs200217 } 678*4904Srs200217 } 679*4904Srs200217 } 680*4904Srs200217 681*4904Srs200217 /* 682*4904Srs200217 * Performs lookup for IP address by hostname via mDNS and returns 683*4904Srs200217 * results along with the TTL value from the mDNS resource records. 684*4904Srs200217 * Called by nscd wth a ptr to packed bufer and packed buffer size. 685*4904Srs200217 */ 686*4904Srs200217 nss_status_t 687*4904Srs200217 _nss_mdns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) 688*4904Srs200217 { 689*4904Srs200217 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 690*4904Srs200217 nss_XbyY_args_t arg; 691*4904Srs200217 int dbop; 692*4904Srs200217 int af; 693*4904Srs200217 int len; 694*4904Srs200217 int blen; 695*4904Srs200217 char *dbname; 696*4904Srs200217 nss_status_t sret; 697*4904Srs200217 char *hname; 698*4904Srs200217 struct mdns_querydata qdata; 699*4904Srs200217 nssuint_t *pttl; 700*4904Srs200217 mdns_backend_ptr_t be = NULL; 701*4904Srs200217 702*4904Srs200217 (void) memset(&qdata, 0, sizeof (struct mdns_querydata)); 703*4904Srs200217 704*4904Srs200217 qdata.argp = &arg; 705*4904Srs200217 706*4904Srs200217 /* 707*4904Srs200217 * Retrieve withttl buffer and size from the passed packed buffer. 708*4904Srs200217 * Results are returned along with ttl in this buffer. 709*4904Srs200217 */ 710*4904Srs200217 qdata.withttlbsize = pbuf->data_len - sizeof (nssuint_t); 711*4904Srs200217 qdata.withttlbuffer = (char *)buffer + pbuf->data_off; 712*4904Srs200217 713*4904Srs200217 sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg); 714*4904Srs200217 if (sret != NSS_SUCCESS) 715*4904Srs200217 return (NSS_ERROR); 716*4904Srs200217 717*4904Srs200217 if (ipnode) { 718*4904Srs200217 if (arg.key.ipnode.flags != 0) 719*4904Srs200217 return (NSS_ERROR); 720*4904Srs200217 hname = (char *)arg.key.ipnode.name; 721*4904Srs200217 af = arg.key.ipnode.af_family; 722*4904Srs200217 } else { 723*4904Srs200217 af = AF_INET; 724*4904Srs200217 hname = (char *)arg.key.name; 725*4904Srs200217 } 726*4904Srs200217 727*4904Srs200217 if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL) 728*4904Srs200217 return (NSS_ERROR); 729*4904Srs200217 _nss_mdns_updatecfg(be); 730*4904Srs200217 731*4904Srs200217 /* Zero out the withttl buffer prior to use */ 732*4904Srs200217 (void) memset(qdata.withttlbuffer, 0, qdata.withttlbsize); 733*4904Srs200217 734*4904Srs200217 #ifdef DEBUG 735*4904Srs200217 syslog(LOG_DEBUG, "nss_mdns: querybyname withttl called" \ 736*4904Srs200217 " af:%d hname:%s", af, hname); 737*4904Srs200217 #endif 738*4904Srs200217 if (_nss_mdns_querybyname(be, hname, af, &qdata) == NSS_SUCCESS) { 739*4904Srs200217 blen = strlen(qdata.buffer); 740*4904Srs200217 len = ROUND_UP(blen, sizeof (nssuint_t)); 741*4904Srs200217 742*4904Srs200217 if (len + sizeof (nssuint_t) > pbuf->data_len) { 743*4904Srs200217 _nss_mdns_freesmfcfg(be); 744*4904Srs200217 free(be); 745*4904Srs200217 return (NSS_ERROR); 746*4904Srs200217 } 747*4904Srs200217 748*4904Srs200217 pbuf->ext_off = pbuf->data_off + len; 749*4904Srs200217 pbuf->ext_len = sizeof (nssuint_t); 750*4904Srs200217 pbuf->data_len = blen; 751*4904Srs200217 752*4904Srs200217 /* Return ttl in the packed buffer at ext_off */ 753*4904Srs200217 pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off)); 754*4904Srs200217 *pttl = qdata.ttl; 755*4904Srs200217 756*4904Srs200217 _nss_mdns_freesmfcfg(be); 757*4904Srs200217 free(be); 758*4904Srs200217 return (NSS_SUCCESS); 759*4904Srs200217 } 760*4904Srs200217 _nss_mdns_freesmfcfg(be); 761*4904Srs200217 free(be); 762*4904Srs200217 return (NSS_ERROR); 763*4904Srs200217 } 764