2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "mdns_common.h"
2N/A
2N/Astatic int _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
2N/A DNSServiceQueryRecordReply callback,
2N/A struct mdns_querydata *data);
2N/Astatic void _nss_mdns_get_svcstatetimestamp(struct timeval *);
2N/Astatic void _nss_mdns_loadsmfcfg(mdns_backend_ptr_t);
2N/Astatic void _nss_mdns_freesmfcfg(mdns_backend_ptr_t);
2N/Astatic boolean_t cmpdmn(char *, char **, int);
2N/Astatic char *RDataToName(char *data, char *buffer, int datalen, int buflen);
2N/Astatic int searchdomain(mdns_backend_ptr_t, char *, int, char **);
2N/Astatic boolean_t validdomain(mdns_backend_ptr_t, char *, int);
2N/A
2N/A/*
2N/A * This file includes the functions to query for host name
2N/A * information via Multicast DNS (mDNS). The function
2N/A * _nss_mdns_queryrecord queries for the host information via
2N/A * Multicast DNS. _nss_mdns_querybyname and _nss_mdns_querybyaddr
2N/A * query for host IP address and hostname by querying for A/AAAA
2N/A * and PTR DNS resource records respectively. DNSServiceQueryRecord
2N/A * in libdns_sd sends a request to the mDNS daemon (mdnsd) to place
2N/A * the DNS query via multicast and return the results.
2N/A * mdnsd is managed by SMF (FMRI: svc:/network/dns/multicast:default).
2N/A *
2N/A * gethostent.c and gethostent6.c implement the nsswitch 'hosts'
2N/A * backend module getXbyY functions: getbyname and getbyaddr.
2N/A * getby* functions in gethostent.c supports only IPv4 and
2N/A * getby* functions in gethostent6.c returns both IPv4 and
2N/A * IPv6 results. Functions in gethostent.c and gethostent6.c
2N/A * call the _nss_mdns_queryby* functions in mdns_common.c to
2N/A * query for host information via mDNS.
2N/A *
2N/A * Configuration for mdns is stored in SMF and is accessed using
2N/A * the FMRI: svc:/network/dns/multicast:default. Configuration
2N/A * includes the list of valid DNS domains checked before querying host
2N/A * information via mDNS and the search list to use for host lookup via
2N/A * mDNS. The default valid domain list in the mDNS service supports host
2N/A * lookups for hostnames in the ".local" domain and hostname queries
2N/A * for link-local IPv4 and IPv6 addresses. _nss_mdns_loadsmfcfg
2N/A * loads the nss_mdns configuration from SMF and the function
2N/A * _nss_mdns_updatecfg checks for any updates in nss_mdns configuration.
2N/A */
2N/A
2N/Astatic int
2N/A_nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
2N/A DNSServiceQueryRecordReply callback,
2N/A struct mdns_querydata *data)
2N/A{
2N/A int sockfd;
2N/A int flags = kDNSServiceFlagsForceMulticast; /* Multicast only */
2N/A int opinterface = kDNSServiceInterfaceIndexAny;
2N/A DNSServiceErrorType err;
2N/A DNSServiceRef ref = NULL;
2N/A int ret;
2N/A struct fd_set readfds;
2N/A struct timeval tv;
2N/A
2N/A data->status = NSS_NOTFOUND;
2N/A#ifdef DEBUG
2N/A syslog(LOG_DEBUG, "nss_mdns: query called rrname:%s rrtype:%d",
2N/A rrname, rrtype);
2N/A#endif
2N/A err = DNSServiceQueryRecord(&ref, flags, opinterface,
2N/A rrname, rrtype, rrclass, callback, data);
2N/A if (err != kDNSServiceErr_NoError || ref == NULL ||
2N/A (sockfd = DNSServiceRefSockFD(ref)) == NULL) {
2N/A DNSServiceRefDeallocate(ref);
2N/A data->status = NSS_UNAVAIL;
2N/A data->argp->h_errno = NO_RECOVERY;
2N/A return (NSS_UNAVAIL);
2N/A }
2N/A
2N/A do {
2N/A FD_ZERO(&readfds);
2N/A FD_SET(sockfd, &readfds);
2N/A tv.tv_sec = NSSMDNS_MAXQRYTMO;
2N/A tv.tv_usec = 0;
2N/A
2N/A /* Wait until response received from mDNS daemon */
2N/A ret = select(sockfd + 1, &readfds, NULL, NULL, &tv);
2N/A if (!((ret > 0) && FD_ISSET(sockfd, &readfds) &&
2N/A (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError))) {
2N/A data->status = NSS_NOTFOUND;
2N/A if (errno != EINTR)
2N/A data->qrydone = B_TRUE;
2N/A }
2N/A } while (data->qrydone != B_TRUE);
2N/A
2N/A if (data->status == NSS_SUCCESS && (data->withttlbuffer == NULL)) {
2N/A nss_XbyY_args_t *argp = data->argp;
2N/A if (argp->buf.result != NULL) {
2N/A int stat;
2N/A
2N/A if (data->buffer == NULL) {
2N/A data->status = NSS_NOTFOUND;
2N/A data->argp->h_errno = HOST_NOT_FOUND;
2N/A DNSServiceRefDeallocate(ref);
2N/A return (data->status);
2N/A }
2N/A stat = (*argp->str2ent)(data->buffer,
2N/A strlen(data->buffer),
2N/A argp->buf.result, argp->buf.buffer,
2N/A argp->buf.buflen);
2N/A if (stat == NSS_STR_PARSE_SUCCESS) {
2N/A argp->returnval = argp->buf.result;
2N/A argp->returnlen = 1;
2N/A } else {
2N/A data->status = NSS_NOTFOUND;
2N/A if (stat == NSS_STR_PARSE_ERANGE)
2N/A argp->erange = 1;
2N/A }
2N/A free(data->buffer);
2N/A } else {
2N/A argp->returnval = argp->buf.buffer;
2N/A argp->returnlen = strlen(argp->buf.buffer);
2N/A }
2N/A data->buffer = NULL;
2N/A data->buflen = 0;
2N/A }
2N/A
2N/A data->argp->h_errno = 0;
2N/A if (data->status != NSS_SUCCESS)
2N/A data->argp->h_errno = HOST_NOT_FOUND;
2N/A
2N/A DNSServiceRefDeallocate(ref);
2N/A return (data->status);
2N/A}
2N/A
2N/Astatic void
2N/A/* LINTED E_FUNC_ARG_UNUSED */
2N/A_nss_mdns_querynamereply(DNSServiceRef sdRef, const DNSServiceFlags flags,
2N/A /* LINTED E_FUNC_ARG_UNUSED */
2N/A uint32_t ifIndex, DNSServiceErrorType errorCode,
2N/A const char *fullname, uint16_t rrtype, uint16_t rrclass,
2N/A /* LINTED E_FUNC_ARG_UNUSED */
2N/A uint16_t rdlen, const void *rdata, uint32_t ttl,
2N/A void *context)
2N/A{
2N/A struct mdns_querydata *qdata;
2N/A nss_XbyY_args_t *argp;
2N/A int firstent = 0;
2N/A int af;
2N/A char addrstore[INET6_ADDRSTRLEN];
2N/A char *buffer;
2N/A int len;
2N/A int remlen;
2N/A
2N/A qdata = (struct mdns_querydata *)context;
2N/A argp = qdata->argp;
2N/A
2N/A if (errorCode != kDNSServiceErr_NoError) {
2N/A qdata->qrydone = B_TRUE;
2N/A return;
2N/A }
2N/A if ((flags & kDNSServiceFlagsMoreComing))
2N/A qdata->qrydone = B_FALSE;
2N/A else
2N/A qdata->qrydone = B_TRUE;
2N/A if (!(flags & kDNSServiceFlagsAdd))
2N/A return;
2N/A if (rrclass != kDNSServiceClass_IN)
2N/A return;
2N/A
2N/A if (rrtype == kDNSServiceType_A)
2N/A af = AF_INET;
2N/A else if (rrtype == kDNSServiceType_AAAA)
2N/A af = AF_INET6;
2N/A else
2N/A return;
2N/A
2N/A if (qdata->buffer == NULL) {
2N/A if (qdata->withttlbsize > 0) {
2N/A remlen = qdata->buflen =
2N/A qdata->withttlbsize;
2N/A buffer = qdata->buffer =
2N/A qdata->withttlbuffer;
2N/A (void) memset(qdata->buffer, 0, remlen);
2N/A } else {
2N/A remlen = qdata->buflen =
2N/A argp->buf.buflen;
2N/A if (argp->buf.result != NULL) {
2N/A buffer = qdata->buffer =
2N/A calloc(1, remlen);
2N/A } else {
2N/A /* Return in file format */
2N/A (void) memset(argp->buf.buffer,
2N/A 0, remlen);
2N/A buffer = qdata->buffer = argp->buf.buffer;
2N/A }
2N/A }
2N/A firstent = 1;
2N/A } else {
2N/A buffer = qdata->buffer + strlen(qdata->buffer);
2N/A remlen = qdata->buflen - strlen(qdata->buffer);
2N/A }
2N/A
2N/A#ifdef DEBUG
2N/A syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen);
2N/A#endif
2N/A if (inet_ntop(af, rdata, addrstore, INET6_ADDRSTRLEN) != NULL) {
2N/A if (firstent)
2N/A len = snprintf(buffer, remlen, "%s %s",
2N/A addrstore, fullname);
2N/A else
2N/A len = snprintf(buffer, remlen, "\n%s %s",
2N/A addrstore, fullname);
2N/A if (len >= remlen || len < 0) {
2N/A qdata->status = NSS_NOTFOUND;
2N/A qdata->argp->erange = 1;
2N/A qdata->argp->h_errno = HOST_NOT_FOUND;
2N/A return;
2N/A }
2N/A qdata->ttl = ttl;
2N/A qdata->status = NSS_SUCCESS;
2N/A#ifdef DEBUG
2N/A syslog(LOG_DEBUG, "nss_mdns: querynamereply buffer:%s", buffer);
2N/A#endif
2N/A } else {
2N/A qdata->status = NSS_NOTFOUND;
2N/A qdata->argp->h_errno = HOST_NOT_FOUND;
2N/A }
2N/A}
2N/A
2N/Aint
2N/A_nss_mdns_querybyname(mdns_backend_ptr_t be, char *qname,
2N/A int af, struct mdns_querydata *data)
2N/A{
2N/A int rrtype;
2N/A int rrclass;
2N/A int srchidx = 0;
2N/A int rc;
2N/A char hname[MAXDNAME];
2N/A char *name;
2N/A char *sname;
2N/A
2N/A rrclass = kDNSServiceClass_IN;
2N/A if (af == AF_INET6)
2N/A rrtype = kDNSServiceType_ANY;
2N/A else if (af == AF_INET)
2N/A rrtype = kDNSServiceType_A;
2N/A else {
2N/A data->argp->h_errno = HOST_NOT_FOUND;
2N/A return (NSS_NOTFOUND);
2N/A }
2N/A
2N/A name = strdup(qname);
2N/A if (name == NULL) {
2N/A data->argp->h_errno = NO_RECOVERY;
2N/A return (NSS_UNAVAIL);
2N/A }
2N/A
2N/A while ((srchidx = searchdomain(be, name, srchidx, &sname)) != -1) {
2N/A if (sname != NULL)
2N/A (void) snprintf(hname, sizeof (hname), "%s.%s",
2N/A name, sname);
2N/A else
2N/A (void) strlcpy(hname, name, sizeof (hname));
2N/A#ifdef DEBUG
2N/A syslog(LOG_DEBUG, "nss_mdns: querybyname called" \
2N/A " srchidx:%d af:%d hname:%s", srchidx, af, qname);
2N/A#endif
2N/A rc = _nss_mdns_queryrecord(hname, rrclass, rrtype,
2N/A _nss_mdns_querynamereply, data);
2N/A if ((rc == NSS_UNAVAIL) || (rc == NSS_SUCCESS)) {
2N/A free(name);
2N/A return (rc);
2N/A }
2N/A }
2N/A free(name);
2N/A return (NSS_NOTFOUND);
2N/A}
2N/A
2N/Astatic void
2N/A/* LINTED E_FUNC_ARG_UNUSED */
2N/A_nss_mdns_queryaddrreply(DNSServiceRef sdRef, const DNSServiceFlags flags,
2N/A /* LINTED E_FUNC_ARG_UNUSED */
2N/A uint32_t ifIndex, DNSServiceErrorType errorCode,
2N/A /* LINTED E_FUNC_ARG_UNUSED */
2N/A const char *fullname, uint16_t rrtype, uint16_t rrclass,
2N/A uint16_t rdlen, const void *rdata, uint32_t ttl,
2N/A void *context)
2N/A{
2N/A struct mdns_querydata *qdata;
2N/A nss_XbyY_args_t *argp;
2N/A char hostname[NI_MAXHOST];
2N/A int firstent = 0;
2N/A char *buffer;
2N/A int len;
2N/A int remlen;
2N/A
2N/A qdata = (struct mdns_querydata *)context;
2N/A argp = qdata->argp;
2N/A
2N/A if (errorCode != kDNSServiceErr_NoError) {
2N/A qdata->qrydone = B_TRUE;
2N/A return;
2N/A }
2N/A if ((flags & kDNSServiceFlagsMoreComing))
2N/A qdata->qrydone = B_FALSE;
2N/A else
2N/A qdata->qrydone = B_TRUE;
2N/A if (!(flags & kDNSServiceFlagsAdd))
2N/A return;
2N/A if (rrclass != kDNSServiceClass_IN)
2N/A return;
2N/A if (rrtype != kDNSServiceType_PTR)
2N/A return;
2N/A
2N/A if (qdata->buffer == NULL) {
2N/A remlen = qdata->buflen = argp->buf.buflen;
2N/A if (argp->buf.result != NULL) {
2N/A buffer = qdata->buffer = calloc(1, remlen);
2N/A } else {
2N/A /* Return in file format */
2N/A (void) memset(argp->buf.buffer, 0, remlen);
2N/A buffer = qdata->buffer = argp->buf.buffer;
2N/A }
2N/A firstent = 1;
2N/A } else {
2N/A buffer = qdata->buffer + strlen(qdata->buffer);
2N/A remlen = qdata->buflen - strlen(qdata->buffer);
2N/A }
2N/A
2N/A if (RDataToName((char *)rdata, hostname, rdlen, NI_MAXHOST) == NULL) {
2N/A qdata->status = NSS_NOTFOUND;
2N/A qdata->argp->h_errno = HOST_NOT_FOUND;
2N/A return;
2N/A }
2N/A
2N/A#ifdef DEBUG
2N/A syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen);
2N/A#endif
2N/A if (firstent)
2N/A len = snprintf(buffer, remlen, "%s %s",
2N/A qdata->paddrbuf, hostname);
2N/A else
2N/A len = snprintf(buffer, remlen, "\n%s %s",
2N/A qdata->paddrbuf, hostname);
2N/A if (len >= remlen || len < 0) {
2N/A qdata->status = NSS_NOTFOUND;
2N/A qdata->argp->erange = 1;
2N/A qdata->argp->h_errno = HOST_NOT_FOUND;
2N/A return;
2N/A }
2N/A qdata->status = NSS_SUCCESS;
2N/A qdata->ttl = ttl;
2N/A}
2N/A
2N/Aint
2N/A/* LINTED E_FUNC_ARG_UNUSED */
2N/A_nss_mdns_querybyaddr(mdns_backend_ptr_t be, char *name, int af,
2N/A struct mdns_querydata *data)
2N/A{
2N/A int rrtype;
2N/A int rrclass;
2N/A
2N/A#ifdef DEBUG
2N/A syslog(LOG_DEBUG, "nss_mdns: querybyaddr called" \
2N/A " af:%d addr:%s", af, name);
2N/A#endif
2N/A rrclass = kDNSServiceClass_IN;
2N/A rrtype = kDNSServiceType_PTR;
2N/A
2N/A if (validdomain(be, name, 0) == B_FALSE) {
2N/A data->status = NSS_NOTFOUND;
2N/A data->argp->h_errno = HOST_NOT_FOUND;
2N/A return (NSS_NOTFOUND);
2N/A }
2N/A return (_nss_mdns_queryrecord(name, rrclass, rrtype,
2N/A _nss_mdns_queryaddrreply, data));
2N/A}
2N/A
2N/A/*
2N/A * Converts the encoded name in RData returned
2N/A * by mDNS query to name in file format
2N/A */
2N/Astatic char *
2N/ARDataToName(char *data, char *buffer, int datalen, int buflen)
2N/A{
2N/A char *src = data;
2N/A char *srcend = data + datalen;
2N/A char *ptr = buffer;
2N/A char *end;
2N/A char *bend = buffer + buflen - 1; /* terminal '\0' */
2N/A int domainlen = 0;
2N/A
2N/A while ((src < srcend) && (*src != 0)) {
2N/A
2N/A /* first byte is len */
2N/A domainlen = *src++;
2N/A end = src + domainlen;
2N/A
2N/A while ((src < end) && (ptr < bend)) {
2N/A uint8_t ch = *src++;
2N/A if (ch == '.' || ch == '\\') {
2N/A *ptr++ = '\\';
2N/A }
2N/A *ptr++ = ch;
2N/A }
2N/A
2N/A /*
2N/A * Check if we copied entire domain str. and
2N/A * if space is still remaining for '.' separator
2N/A */
2N/A if ((src != end) || (ptr == bend))
2N/A return (NULL);
2N/A *ptr++ = '.';
2N/A }
2N/A *ptr = '\0';
2N/A return (ptr);
2N/A}
2N/A
2N/Anss_backend_t *
2N/A_nss_mdns_constr(mdns_backend_op_t ops[], int n_ops)
2N/A{
2N/A mdns_backend_ptr_t be;
2N/A
2N/A if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL)
2N/A return (NULL);
2N/A be->ops = ops;
2N/A be->n_ops = n_ops;
2N/A _nss_mdns_updatecfg(be);
2N/A return ((nss_backend_t *)be);
2N/A}
2N/A
2N/Avoid
2N/A_nss_mdns_destr(mdns_backend_ptr_t be)
2N/A{
2N/A if (be != NULL) {
2N/A _nss_mdns_freesmfcfg(be);
2N/A free(be);
2N/A }
2N/A}
2N/A
2N/Astatic int
2N/Asearchdomain(mdns_backend_ptr_t be, char *name, int srchidx, char **sname)
2N/A{
2N/A int trailing_dot = 0;
2N/A char *ch;
2N/A *sname = NULL;
2N/A
2N/A ch = name + strlen(name) - 1;
2N/A if ((*ch) == '.')
2N/A trailing_dot++;
2N/A
2N/A if (trailing_dot && srchidx > 0)
2N/A /*
2N/A * If there is a trailing dot in the query
2N/A * name, do not perform any additional queries
2N/A * with search domains.
2N/A */
2N/A return (-1);
2N/A
2N/A if (srchidx == 0) {
2N/A /*
2N/A * If there is a trailing dot in the query
2N/A * or atleast one dot in the query name then
2N/A * perform a query as-is once first.
2N/A */
2N/A ++srchidx;
2N/A if ((trailing_dot || (strchr(name, '.') != NULL))) {
2N/A if (validdomain(be, name, 1) == B_TRUE)
2N/A return (srchidx);
2N/A else if (trailing_dot)
2N/A return (-1);
2N/A }
2N/A }
2N/A
2N/A if ((srchidx > NSSMDNS_MAXSRCHDMNS) ||
2N/A (be->dmnsrchlist[srchidx-1] == NULL))
2N/A return (-1);
2N/A
2N/A *sname = be->dmnsrchlist[srchidx-1];
2N/A return (++srchidx);
2N/A}
2N/A
2N/A/*
2N/A * This function determines if the domain name in the query
2N/A * matches any of the valid & search domains in the nss_mdns
2N/A * configuration.
2N/A */
2N/Astatic boolean_t
2N/Avaliddomain(mdns_backend_ptr_t be, char *name, int chksrchdmns)
2N/A{
2N/A char *nameptr;
2N/A
2N/A /* Remove any trailing and leading dots in the name */
2N/A nameptr = name + strlen(name) - 1;
2N/A while (*nameptr && (nameptr != name) && (*nameptr == '.'))
2N/A nameptr--;
2N/A *(++nameptr) = '\0';
2N/A nameptr = name;
2N/A while (*nameptr && (*nameptr == '.'))
2N/A nameptr++;
2N/A if (*nameptr == '\0')
2N/A return (B_FALSE);
2N/A
2N/A /* Compare with search domains */
2N/A if (chksrchdmns && (cmpdmn(nameptr, be->dmnsrchlist,
2N/A NSSMDNS_MAXSRCHDMNS) == B_TRUE))
2N/A return (B_TRUE);
2N/A
2N/A /* Compare with valid domains */
2N/A return (cmpdmn(nameptr, be->validdmnlist, NSSMDNS_MAXVALIDDMNS));
2N/A}
2N/A
2N/Astatic boolean_t
2N/Acmpdmn(char *name, char **dmnlist, int maxdmns)
2N/A{
2N/A char *vptr;
2N/A int vdlen;
2N/A char *cptr;
2N/A int nlen;
2N/A int i;
2N/A
2N/A nlen = strlen(name);
2N/A for (i = 0; (i < maxdmns) &&
2N/A ((vptr = dmnlist[i]) != NULL); i++) {
2N/A vdlen = strlen(vptr);
2N/A if (vdlen > nlen)
2N/A continue;
2N/A cptr = name + nlen - vdlen;
2N/A if (strncasecmp(cptr, vptr, vdlen) == 0)
2N/A return (B_TRUE);
2N/A }
2N/A return (B_FALSE);
2N/A}
2N/A
2N/Astatic void
2N/A_nss_mdns_get_svcstatetimestamp(struct timeval *ptv)
2N/A{
2N/A scf_handle_t *h;
2N/A scf_simple_prop_t *sprop;
2N/A int32_t nsec;
2N/A
2N/A (void) memset(ptv, 0, sizeof (struct timeval));
2N/A
2N/A h = scf_handle_create(SCF_VERSION);
2N/A if (h == NULL)
2N/A return;
2N/A
2N/A if (scf_handle_bind(h) == -1) {
2N/A scf_handle_destroy(h);
2N/A return;
2N/A }
2N/A
2N/A if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI,
2N/A SCF_PG_RESTARTER, SCF_PROPERTY_STATE_TIMESTAMP)) != NULL) {
2N/A ptv->tv_sec = *(time_t *)(scf_simple_prop_next_time(sprop,
2N/A &nsec));
2N/A ptv->tv_usec = nsec / 1000;
2N/A scf_simple_prop_free(sprop);
2N/A }
2N/A
2N/A if (h != NULL)
2N/A scf_handle_destroy(h);
2N/A}
2N/A
2N/Avoid
2N/A_nss_mdns_updatecfg(mdns_backend_ptr_t be)
2N/A{
2N/A struct timeval statetimestamp;
2N/A
2N/A /*
2N/A * Update configuration if current svc state timestamp
2N/A * is different from last known svc state timestamp
2N/A */
2N/A _nss_mdns_get_svcstatetimestamp(&statetimestamp);
2N/A if ((statetimestamp.tv_sec == 0) && (statetimestamp.tv_usec == 0)) {
2N/A syslog(LOG_ERR, "nss_mdns: error checking " \
2N/A "svc:/network/dns/multicast:default" \
2N/A " service timestamp");
2N/A } else if ((be->conftimestamp.tv_sec == statetimestamp.tv_sec) &&
2N/A (be->conftimestamp.tv_usec == statetimestamp.tv_usec)) {
2N/A return;
2N/A }
2N/A
2N/A _nss_mdns_freesmfcfg(be);
2N/A _nss_mdns_loadsmfcfg(be);
2N/A be->conftimestamp.tv_sec = statetimestamp.tv_sec;
2N/A be->conftimestamp.tv_usec = statetimestamp.tv_usec;
2N/A}
2N/A
2N/Astatic void
2N/Aload_mdns_domaincfg(scf_handle_t *h, char **storelist,
2N/A const char *scfprop, int maxprops)
2N/A{
2N/A scf_simple_prop_t *sprop;
2N/A char *tchr;
2N/A char *pchr;
2N/A int tlen;
2N/A int cnt = 0;
2N/A
2N/A if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI,
2N/A SMF_NSSMDNSCFG_PROPGRP, scfprop)) == NULL)
2N/A return;
2N/A
2N/A while ((cnt < maxprops) &&
2N/A (tchr = scf_simple_prop_next_astring(sprop)) != NULL) {
2N/A
2N/A /* Remove beginning & trailing '.' chars */
2N/A while (*tchr && (*tchr == '.'))
2N/A tchr++;
2N/A
2N/A if (*tchr && ((tlen = strlen(tchr)) < MAXDNAME)) {
2N/A pchr = &tchr[tlen-1];
2N/A while ((pchr != tchr) && (*pchr == '.'))
2N/A pchr--;
2N/A *(++pchr) = '\0';
2N/A storelist[cnt] = strdup(tchr);
2N/A cnt++;
2N/A }
2N/A }
2N/A scf_simple_prop_free(sprop);
2N/A}
2N/A
2N/Astatic void
2N/A_nss_mdns_loadsmfcfg(mdns_backend_ptr_t be)
2N/A{
2N/A scf_handle_t *h;
2N/A
2N/A h = scf_handle_create(SCF_VERSION);
2N/A if (h == NULL)
2N/A return;
2N/A
2N/A if (scf_handle_bind(h) == -1) {
2N/A scf_handle_destroy(h);
2N/A return;
2N/A }
2N/A
2N/A load_mdns_domaincfg(h, &(be->dmnsrchlist[0]),
2N/A SMF_NSSMDNSCFG_SRCHPROP, NSSMDNS_MAXSRCHDMNS);
2N/A
2N/A load_mdns_domaincfg(h, &(be->validdmnlist[0]),
2N/A SMF_NSSMDNSCFG_DMNPROP, NSSMDNS_MAXVALIDDMNS);
2N/A
2N/A if (h != NULL)
2N/A scf_handle_destroy(h);
2N/A}
2N/A
2N/Astatic void
2N/A_nss_mdns_freesmfcfg(mdns_backend_ptr_t be)
2N/A{
2N/A int idx;
2N/A if (be == NULL)
2N/A return;
2N/A for (idx = 0; idx < NSSMDNS_MAXSRCHDMNS; idx++) {
2N/A if (be->dmnsrchlist[idx] != NULL) {
2N/A free(be->dmnsrchlist[idx]);
2N/A be->dmnsrchlist[idx] = NULL;
2N/A }
2N/A }
2N/A for (idx = 0; idx < NSSMDNS_MAXVALIDDMNS; idx++) {
2N/A if (be->validdmnlist[idx] != NULL) {
2N/A free(be->validdmnlist[idx]);
2N/A be->validdmnlist[idx] = NULL;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Performs lookup for IP address by hostname via mDNS and returns
2N/A * results along with the TTL value from the mDNS resource records.
2N/A * Called by nscd wth a ptr to packed buffer and packed buffer size.
2N/A */
2N/Anss_status_t
2N/A_nss_mdns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
2N/A{
2N/A nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
2N/A nss_XbyY_args_t arg;
2N/A int dbop;
2N/A int af;
2N/A int len;
2N/A int blen;
2N/A char *dbname;
2N/A nss_status_t sret;
2N/A char *hname;
2N/A struct mdns_querydata qdata;
2N/A nssuint_t *pttl;
2N/A mdns_backend_ptr_t be = NULL;
2N/A
2N/A (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
2N/A
2N/A qdata.argp = &arg;
2N/A
2N/A /*
2N/A * Retrieve withttl buffer and size from the passed packed buffer.
2N/A * Results are returned along with ttl in this buffer.
2N/A */
2N/A qdata.withttlbsize = pbuf->data_len - sizeof (nssuint_t);
2N/A qdata.withttlbuffer = (char *)buffer + pbuf->data_off;
2N/A
2N/A sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
2N/A if (sret != NSS_SUCCESS)
2N/A return (NSS_ERROR);
2N/A
2N/A if (ipnode) {
2N/A if (arg.key.ipnode.flags != 0)
2N/A return (NSS_ERROR);
2N/A hname = (char *)arg.key.ipnode.name;
2N/A af = arg.key.ipnode.af_family;
2N/A } else {
2N/A af = AF_INET;
2N/A hname = (char *)arg.key.name;
2N/A }
2N/A
2N/A if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL)
2N/A return (NSS_ERROR);
2N/A _nss_mdns_updatecfg(be);
2N/A
2N/A /* Zero out the withttl buffer prior to use */
2N/A (void) memset(qdata.withttlbuffer, 0, qdata.withttlbsize);
2N/A
2N/A#ifdef DEBUG
2N/A syslog(LOG_DEBUG, "nss_mdns: querybyname withttl called" \
2N/A " af:%d hname:%s", af, hname);
2N/A#endif
2N/A if (_nss_mdns_querybyname(be, hname, af, &qdata) == NSS_SUCCESS) {
2N/A blen = strlen(qdata.buffer);
2N/A len = ROUND_UP(blen, sizeof (nssuint_t));
2N/A
2N/A if (len + sizeof (nssuint_t) > pbuf->data_len) {
2N/A _nss_mdns_freesmfcfg(be);
2N/A free(be);
2N/A return (NSS_ERROR);
2N/A }
2N/A
2N/A pbuf->ext_off = pbuf->data_off + len;
2N/A pbuf->ext_len = sizeof (nssuint_t);
2N/A pbuf->data_len = blen;
2N/A
2N/A /* Return ttl in the packed buffer at ext_off */
2N/A pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
2N/A *pttl = qdata.ttl;
2N/A
2N/A _nss_mdns_freesmfcfg(be);
2N/A free(be);
2N/A return (NSS_SUCCESS);
2N/A }
2N/A _nss_mdns_freesmfcfg(be);
2N/A free(be);
2N/A return (NSS_ERROR);
2N/A}