summaryrefslogtreecommitdiff
path: root/usr/src/lib/nsswitch/mdns/common
diff options
context:
space:
mode:
authorrs200217 <none@none>2007-08-20 11:44:22 -0700
committerrs200217 <none@none>2007-08-20 11:44:22 -0700
commit4b22b9337f359bfd063322244f5336cc7c6ffcfa (patch)
treed935e6adb14cc353974e0fcf2dc89a47f18b9971 /usr/src/lib/nsswitch/mdns/common
parentd96018ff0218b4466f62ea165b3dfd64ef271965 (diff)
downloadillumos-gate-4b22b9337f359bfd063322244f5336cc7c6ffcfa.tar.gz
PSARC 2005/562 Multicast DNS and Service Discovery
6550311 Multicast DNS and service discovery support
Diffstat (limited to 'usr/src/lib/nsswitch/mdns/common')
-rw-r--r--usr/src/lib/nsswitch/mdns/common/gethostent.c153
-rw-r--r--usr/src/lib/nsswitch/mdns/common/gethostent6.c174
-rw-r--r--usr/src/lib/nsswitch/mdns/common/mapfile-vers36
-rw-r--r--usr/src/lib/nsswitch/mdns/common/mdns_common.c763
-rw-r--r--usr/src/lib/nsswitch/mdns/common/mdns_common.h107
5 files changed, 1233 insertions, 0 deletions
diff --git a/usr/src/lib/nsswitch/mdns/common/gethostent.c b/usr/src/lib/nsswitch/mdns/common/gethostent.c
new file mode 100644
index 0000000000..6b5756f0e9
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/gethostent.c
@@ -0,0 +1,153 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mdns_common.h"
+
+/*
+ * gethostby* functions for the hosts database. The hosts
+ * database stores IPv4 addresses only.
+ * mDNS query functions to perform the host lookup
+ * are in mdns/common/mdns_common.c file.
+ * _nss_mdns_hosts_constr is called to initialize
+ * the nsswitch backend data structures.
+ */
+
+static nss_status_t
+getbyname(be, a)
+ mdns_backend_ptr_t be;
+ void *a;
+{
+ struct mdns_querydata qdata;
+ char *hname;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+
+ qdata.argp = (nss_XbyY_args_t *)a;
+ hname = (char *)qdata.argp->key.name;
+
+ _nss_mdns_updatecfg(be);
+ return (_nss_mdns_querybyname(be, hname, AF_INET, &qdata));
+}
+
+/*ARGSUSED*/
+static nss_status_t
+getbyaddr(be, a)
+ mdns_backend_ptr_t be;
+ void *a;
+{
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
+ struct in_addr addr;
+ struct mdns_querydata qdata;
+ char buffer[sizeof ("255.255.255.255.in-addr.arpa.")];
+ uint8_t *p;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+ qdata.argp = argp;
+
+ argp->h_errno = 0;
+ if ((argp->key.hostaddr.type != AF_INET) ||
+ (argp->key.hostaddr.len != sizeof (addr)))
+ return (NSS_NOTFOUND);
+
+ (void) memcpy(&addr, argp->key.hostaddr.addr, sizeof (addr));
+
+ if (inet_ntop(AF_INET, (void *) &addr.s_addr,
+ (void *)qdata.paddrbuf,
+ sizeof (qdata.paddrbuf)) == NULL)
+ return (NSS_NOTFOUND);
+
+ qdata.af = AF_INET;
+ p = (uint8_t *)&addr.s_addr;
+ (void) snprintf(buffer, sizeof (buffer),
+ "%u.%u.%u.%u.in-addr.arpa.", p[3], p[2], p[1], p[0]);
+
+ _nss_mdns_updatecfg(be);
+ return (_nss_mdns_querybyaddr(be, buffer, qdata.af, &qdata));
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_getent(be, args)
+ mdns_backend_ptr_t be;
+ void *args;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_setent(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_endent(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_hosts_destr(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ _nss_mdns_destr(be);
+ return (NSS_SUCCESS);
+}
+
+static mdns_backend_op_t host_ops[] = {
+ _nss_mdns_hosts_destr,
+ _nss_mdns_endent,
+ _nss_mdns_setent,
+ _nss_mdns_getent,
+ getbyname,
+ getbyaddr,
+};
+
+/*ARGSUSED*/
+nss_backend_t *
+_nss_mdns_hosts_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_mdns_constr(host_ops,
+ sizeof (host_ops) / sizeof (host_ops[0])));
+}
+
+/*ARGSUSED*/
+nss_status_t
+_nss_get_mdns_hosts_name(mdns_backend_ptr_t *be, void **bufp, size_t *sizep)
+{
+ return (_nss_mdns_gethost_withttl(*bufp, *sizep, 0));
+}
diff --git a/usr/src/lib/nsswitch/mdns/common/gethostent6.c b/usr/src/lib/nsswitch/mdns/common/gethostent6.c
new file mode 100644
index 0000000000..e4288de82b
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/gethostent6.c
@@ -0,0 +1,174 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mdns_common.h"
+
+/*
+ * gethostby* functions for the ipnodes database. The ipnodes
+ * database stores both IPv4 and IPv6 address information.
+ * mDNS query functions to perform the host lookup are
+ * in mdns/common/mdns_common.c file.
+ * _nss_mdns_ipnodes_constr is called to initialize
+ * the nsswitch backend data structures.
+ */
+
+static nss_status_t
+getbyname(be, a)
+ mdns_backend_ptr_t be;
+ void *a;
+{
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
+ int af = argp->key.ipnode.af_family;
+ char *hname = (char *)argp->key.ipnode.name;
+ struct mdns_querydata qdata;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+ qdata.argp = argp;
+
+ _nss_mdns_updatecfg(be);
+ return (_nss_mdns_querybyname(be, hname, af, &qdata));
+}
+
+static nss_status_t
+getbyaddr(be, a)
+ mdns_backend_ptr_t be;
+ void *a;
+{
+ int i;
+ char ch;
+ char *chptr;
+ uint8_t *p;
+ struct in6_addr *addr;
+ struct mdns_querydata qdata;
+ char addrqryname[ sizeof ("f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f") + \
+ sizeof (".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.ip6.arpa.")];
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+ qdata.argp = argp;
+ argp->h_errno = 0;
+
+ if ((argp->key.hostaddr.type != AF_INET6) ||
+ (argp->key.hostaddr.len != sizeof (*addr)))
+ return (NSS_NOTFOUND);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ addr = (struct in6_addr *)(argp->key.hostaddr.addr);
+
+ if (IN6_IS_ADDR_V4MAPPED(addr)) {
+ struct in_addr ipv4addr;
+
+ IN6_V4MAPPED_TO_INADDR(addr, &ipv4addr);
+ if (inet_ntop(AF_INET, (void *) &ipv4addr.s_addr,
+ (void *)qdata.paddrbuf, sizeof (qdata.paddrbuf)) == NULL)
+ return (NSS_NOTFOUND);
+ qdata.af = AF_INET;
+ p = (uint8_t *)&ipv4addr.s_addr;
+ (void) snprintf(addrqryname, sizeof (addrqryname),
+ "%u.%u.%u.%u.in-addr.arpa.", p[3], p[2], p[1], p[0]);
+ } else {
+ if (inet_ntop(AF_INET6, (void *)addr,
+ (void *)qdata.paddrbuf,
+ sizeof (qdata.paddrbuf)) == NULL)
+ return (NSS_NOTFOUND);
+ qdata.af = AF_INET6;
+ chptr = addrqryname;
+ for (i = 0; i < 16; i++)
+ {
+ ch = ((char *)addr)[15-i];
+ chptr += snprintf(chptr, sizeof (addrqryname)-i*4,
+ "%X.%X.", ch&0x0f, (ch>>4)&0x0f);
+ }
+ (void) strlcpy(chptr, "ip6.arpa.", sizeof (addrqryname)-64);
+ }
+
+ _nss_mdns_updatecfg(be);
+ return (_nss_mdns_querybyaddr(be, addrqryname, qdata.af, &qdata));
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_getent(be, args)
+ mdns_backend_ptr_t be;
+ void *args;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_setent(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_endent(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_ipnodes_destr(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ _nss_mdns_destr(be);
+ return (NSS_SUCCESS);
+}
+
+static mdns_backend_op_t ipnodes_ops[] = {
+ _nss_mdns_ipnodes_destr,
+ _nss_mdns_endent,
+ _nss_mdns_setent,
+ _nss_mdns_getent,
+ getbyname,
+ getbyaddr,
+};
+
+/*ARGSUSED*/
+nss_backend_t *
+_nss_mdns_ipnodes_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_mdns_constr(ipnodes_ops,
+ sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
+}
+
+/*ARGSUSED*/
+nss_status_t
+_nss_get_mdns_ipnodes_name(mdns_backend_ptr_t *be, void **bufp, size_t *sizep)
+{
+ return (_nss_mdns_gethost_withttl(*bufp, *sizep, 1));
+}
diff --git a/usr/src/lib/nsswitch/mdns/common/mapfile-vers b/usr/src/lib/nsswitch/mdns/common/mapfile-vers
new file mode 100644
index 0000000000..cc7767771a
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/mapfile-vers
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Generic interface definition for usr/src/lib/nsswitch/mdns.
+
+SUNWprivate_1.1 {
+ global:
+ _nss_mdns_hosts_constr;
+ _nss_mdns_ipnodes_constr;
+ _nss_get_mdns_hosts_name;
+ _nss_get_mdns_ipnodes_name;
+ local:
+ *;
+};
diff --git a/usr/src/lib/nsswitch/mdns/common/mdns_common.c b/usr/src/lib/nsswitch/mdns/common/mdns_common.c
new file mode 100644
index 0000000000..3e914e5744
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/mdns_common.c
@@ -0,0 +1,763 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mdns_common.h"
+
+static int _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
+ DNSServiceQueryRecordReply callback,
+ struct mdns_querydata *data);
+static void _nss_mdns_get_svcstatetimestamp(struct timeval *);
+static void _nss_mdns_loadsmfcfg(mdns_backend_ptr_t);
+static void _nss_mdns_freesmfcfg(mdns_backend_ptr_t);
+static boolean_t cmpdmn(char *, char **, int);
+static char *RDataToName(char *data, char *buffer, int datalen, int buflen);
+static int searchdomain(mdns_backend_ptr_t, char *, int, char **);
+static boolean_t validdomain(mdns_backend_ptr_t, char *, int);
+
+/*
+ * This file includes the functions to query for host name
+ * information via Multicast DNS (mDNS). The function
+ * _nss_mdns_queryrecord queries for the host information via
+ * Multicast DNS. _nss_mdns_querybyname and _nss_mdns_querybyaddr
+ * query for host IP address and hostname by querying for A/AAAA
+ * and PTR DNS resource records respectively. DNSServiceQueryRecord
+ * in libdns_sd sends a request to the mDNS daemon (mdnsd) to place
+ * the DNS query via multicast and return the results.
+ * mdnsd is managed by SMF (FMRI: svc:/network/dns/multicast:default).
+ *
+ * gethostent.c and gethostent6.c implement the nsswitch 'hosts'
+ * backend module getXbyY functions: getbyname and getbyaddr.
+ * getby* functions in gethostent.c supports only IPv4 and
+ * getby* functions in gethostent6.c returns both IPv4 and
+ * IPv6 results. Functions in gethostent.c and gethostent6.c
+ * call the _nss_mdns_queryby* functions in mdns_common.c to
+ * query for host information via mDNS.
+ *
+ * Configuration for mdns is stored in SMF and is accessed using
+ * the FMRI: svc:/network/dns/multicast:default. Configuration
+ * includes the list of valid DNS domains checked before querying host
+ * information via mDNS and the search list to use for host lookup via
+ * mDNS. The default valid domain list in the mDNS service supports host
+ * lookups for hostnames in the ".local" domain and hostname queries
+ * for link-local IPv4 and IPv6 addresses. _nss_mdns_loadsmfcfg
+ * loads the nss_mdns configuration from SMF and the function
+ * _nss_mdns_updatecfg checks for any updates in nss_mdns configuration.
+ */
+
+static int
+_nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
+ DNSServiceQueryRecordReply callback,
+ struct mdns_querydata *data)
+{
+ int sockfd;
+ int flags = kDNSServiceFlagsForceMulticast; /* Multicast only */
+ int opinterface = kDNSServiceInterfaceIndexAny;
+ DNSServiceErrorType err;
+ DNSServiceRef ref = NULL;
+ int ret;
+ struct fd_set readfds;
+ struct timeval tv;
+
+ data->status = NSS_NOTFOUND;
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: query called rrname:%s rrtype:%d",
+ rrname, rrtype);
+#endif
+ err = DNSServiceQueryRecord(&ref, flags, opinterface,
+ rrname, rrtype, rrclass, callback, data);
+ if (err != kDNSServiceErr_NoError || ref == NULL ||
+ (sockfd = DNSServiceRefSockFD(ref)) == NULL) {
+ DNSServiceRefDeallocate(ref);
+ data->status = NSS_UNAVAIL;
+ return (NSS_UNAVAIL);
+ }
+
+ do {
+ FD_ZERO(&readfds);
+ FD_SET(sockfd, &readfds);
+ tv.tv_sec = NSSMDNS_MAXQRYTMO;
+ tv.tv_usec = 0;
+
+ /* Wait until response received from mDNS daemon */
+ ret = select(sockfd + 1, &readfds, NULL, NULL, &tv);
+ if (!((ret > 0) && FD_ISSET(sockfd, &readfds) &&
+ (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError))) {
+ data->status = NSS_NOTFOUND;
+ if (errno != EINTR)
+ data->qrydone = B_TRUE;
+ }
+ } while (data->qrydone != B_TRUE);
+
+ if (data->status == NSS_SUCCESS && (data->withttlbuffer == NULL)) {
+ nss_XbyY_args_t *argp = data->argp;
+ if (argp->buf.result != NULL) {
+ int stat;
+
+ if (data->buffer == NULL) {
+ data->status = NSS_NOTFOUND;
+ DNSServiceRefDeallocate(ref);
+ return (data->status);
+ }
+ stat = (*argp->str2ent)(data->buffer,
+ strlen(data->buffer),
+ argp->buf.result, argp->buf.buffer,
+ argp->buf.buflen);
+ if (stat == NSS_STR_PARSE_SUCCESS) {
+ argp->returnval = argp->buf.result;
+ argp->returnlen = 1;
+ } else {
+ data->status = NSS_NOTFOUND;
+ if (stat == NSS_STR_PARSE_ERANGE)
+ argp->erange = 1;
+ }
+ free(data->buffer);
+ } else {
+ argp->returnval = argp->buf.buffer;
+ argp->returnlen = strlen(argp->buf.buffer);
+ }
+ data->buffer = NULL;
+ data->buflen = 0;
+ }
+
+ if (data->status != NSS_SUCCESS)
+ data->argp->h_errno = HOST_NOT_FOUND;
+
+ DNSServiceRefDeallocate(ref);
+ return (data->status);
+}
+
+static void
+/* LINTED E_FUNC_ARG_UNUSED */
+_nss_mdns_querynamereply(DNSServiceRef sdRef, const DNSServiceFlags flags,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ uint32_t ifIndex, DNSServiceErrorType errorCode,
+ const char *fullname, uint16_t rrtype, uint16_t rrclass,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ uint16_t rdlen, const void *rdata, uint32_t ttl,
+ void *context)
+{
+ struct mdns_querydata *qdata;
+ nss_XbyY_args_t *argp;
+ int firstent = 0;
+ int af;
+ char addrstore[INET6_ADDRSTRLEN];
+ char *buffer;
+ int len;
+ int remlen;
+
+ qdata = (struct mdns_querydata *)context;
+ argp = qdata->argp;
+
+ if (errorCode != kDNSServiceErr_NoError) {
+ qdata->qrydone = B_TRUE;
+ return;
+ }
+ if ((flags & kDNSServiceFlagsMoreComing))
+ qdata->qrydone = B_FALSE;
+ else
+ qdata->qrydone = B_TRUE;
+ if (!(flags & kDNSServiceFlagsAdd))
+ return;
+ if (rrclass != kDNSServiceClass_IN)
+ return;
+
+ if (rrtype == kDNSServiceType_A)
+ af = AF_INET;
+ else if (rrtype == kDNSServiceType_AAAA)
+ af = AF_INET6;
+ else
+ return;
+
+ if (qdata->buffer == NULL) {
+ if (qdata->withttlbsize > 0) {
+ remlen = qdata->buflen =
+ qdata->withttlbsize;
+ buffer = qdata->buffer =
+ qdata->withttlbuffer;
+ (void) memset(qdata->buffer, 0, remlen);
+ } else {
+ remlen = qdata->buflen =
+ argp->buf.buflen;
+ if (argp->buf.result != NULL) {
+ buffer = qdata->buffer =
+ calloc(1, remlen);
+ } else {
+ /* Return in file format */
+ (void) memset(argp->buf.buffer,
+ 0, remlen);
+ buffer = qdata->buffer = argp->buf.buffer;
+ }
+ }
+ firstent = 1;
+ } else {
+ buffer = qdata->buffer + strlen(qdata->buffer);
+ remlen = qdata->buflen - strlen(qdata->buffer);
+ }
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen);
+#endif
+ if (inet_ntop(af, rdata, addrstore, INET6_ADDRSTRLEN) != NULL) {
+ if (firstent)
+ len = snprintf(buffer, remlen, "%s %s",
+ addrstore, fullname);
+ else
+ len = snprintf(buffer, remlen, "\n%s %s",
+ addrstore, fullname);
+ if (len >= remlen || len < 0) {
+ qdata->status = NSS_NOTFOUND;
+ qdata->argp->erange = 1;
+ qdata->argp->h_errno = HOST_NOT_FOUND;
+ return;
+ }
+ qdata->ttl = ttl;
+ qdata->status = NSS_SUCCESS;
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querynamereply buffer:%s", buffer);
+#endif
+ } else {
+ qdata->status = NSS_NOTFOUND;
+ qdata->argp->h_errno = HOST_NOT_FOUND;
+ }
+}
+
+int
+_nss_mdns_querybyname(mdns_backend_ptr_t be, char *qname,
+ int af, struct mdns_querydata *data)
+{
+ int rrtype;
+ int rrclass;
+ int srchidx = 0;
+ int rc;
+ char hname[MAXDNAME];
+ char *name;
+ char *sname;
+
+ rrclass = kDNSServiceClass_IN;
+ if (af == AF_INET6)
+ rrtype = kDNSServiceType_ANY;
+ else if (af == AF_INET)
+ rrtype = kDNSServiceType_A;
+ else
+ return (NSS_NOTFOUND);
+
+ name = strdup(qname);
+ if (name == NULL)
+ return (NSS_UNAVAIL);
+
+ while ((srchidx = searchdomain(be, name, srchidx, &sname)) != -1) {
+ if (sname != NULL)
+ (void) snprintf(hname, sizeof (hname), "%s.%s",
+ name, sname);
+ else
+ (void) strlcpy(hname, name, sizeof (hname));
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querybyname called" \
+ " srchidx:%d af:%d hname:%s", srchidx, af, qname);
+#endif
+ rc = _nss_mdns_queryrecord(hname, rrclass, rrtype,
+ _nss_mdns_querynamereply, data);
+ if ((rc == NSS_UNAVAIL) || (rc == NSS_SUCCESS)) {
+ free(name);
+ return (rc);
+ }
+ }
+ free(name);
+ return (NSS_NOTFOUND);
+}
+
+static void
+/* LINTED E_FUNC_ARG_UNUSED */
+_nss_mdns_queryaddrreply(DNSServiceRef sdRef, const DNSServiceFlags flags,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ uint32_t ifIndex, DNSServiceErrorType errorCode,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ const char *fullname, uint16_t rrtype, uint16_t rrclass,
+ uint16_t rdlen, const void *rdata, uint32_t ttl,
+ void *context)
+{
+ struct mdns_querydata *qdata;
+ nss_XbyY_args_t *argp;
+ char hostname[NI_MAXHOST];
+ int firstent = 0;
+ char *buffer;
+ int len;
+ int remlen;
+
+ qdata = (struct mdns_querydata *)context;
+ argp = qdata->argp;
+
+ if (errorCode != kDNSServiceErr_NoError) {
+ qdata->qrydone = B_TRUE;
+ return;
+ }
+ if ((flags & kDNSServiceFlagsMoreComing))
+ qdata->qrydone = B_FALSE;
+ else
+ qdata->qrydone = B_TRUE;
+ if (!(flags & kDNSServiceFlagsAdd))
+ return;
+ if (rrclass != kDNSServiceClass_IN)
+ return;
+ if (rrtype != kDNSServiceType_PTR)
+ return;
+
+ if (qdata->buffer == NULL) {
+ remlen = qdata->buflen = argp->buf.buflen;
+ if (argp->buf.result != NULL) {
+ buffer = qdata->buffer = calloc(1, remlen);
+ } else {
+ /* Return in file format */
+ (void) memset(argp->buf.buffer, 0, remlen);
+ buffer = qdata->buffer = argp->buf.buffer;
+ }
+ firstent = 1;
+ } else {
+ buffer = qdata->buffer + strlen(qdata->buffer);
+ remlen = qdata->buflen - strlen(qdata->buffer);
+ }
+
+ if (RDataToName((char *)rdata, hostname, rdlen, NI_MAXHOST) == NULL) {
+ qdata->status = NSS_NOTFOUND;
+ qdata->argp->h_errno = HOST_NOT_FOUND;
+ return;
+ }
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen);
+#endif
+ if (firstent)
+ len = snprintf(buffer, remlen, "%s %s",
+ qdata->paddrbuf, hostname);
+ else
+ len = snprintf(buffer, remlen, "\n%s %s",
+ qdata->paddrbuf, hostname);
+ if (len >= remlen || len < 0) {
+ qdata->status = NSS_NOTFOUND;
+ qdata->argp->erange = 1;
+ qdata->argp->h_errno = HOST_NOT_FOUND;
+ return;
+ }
+ qdata->status = NSS_SUCCESS;
+ qdata->ttl = ttl;
+}
+
+int
+/* LINTED E_FUNC_ARG_UNUSED */
+_nss_mdns_querybyaddr(mdns_backend_ptr_t be, char *name, int af,
+ struct mdns_querydata *data)
+{
+ int rrtype;
+ int rrclass;
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querybyaddr called" \
+ " af:%d addr:%s", af, name);
+#endif
+ rrclass = kDNSServiceClass_IN;
+ rrtype = kDNSServiceType_PTR;
+
+ if (validdomain(be, name, 0) == B_FALSE) {
+ data->status = NSS_NOTFOUND;
+ return (NSS_NOTFOUND);
+ }
+ return (_nss_mdns_queryrecord(name, rrclass, rrtype,
+ _nss_mdns_queryaddrreply, data));
+}
+
+/*
+ * Converts the encoded name in RData returned
+ * by mDNS query to name in file format
+ */
+static char *
+RDataToName(char *data, char *buffer, int datalen, int buflen)
+{
+ char *src = data;
+ char *srcend = data + datalen;
+ char *ptr = buffer;
+ char *end;
+ char *bend = buffer + buflen - 1; /* terminal '\0' */
+ int domainlen = 0;
+
+ while ((src < srcend) && (*src != 0)) {
+
+ /* first byte is len */
+ domainlen = *src++;
+ end = src + domainlen;
+
+ while ((src < end) && (ptr < bend)) {
+ uint8_t ch = *src++;
+ if (ch == '.' || ch == '\\') {
+ *ptr++ = '\\';
+ }
+ *ptr++ = ch;
+ }
+
+ /*
+ * Check if we copied entire domain str. and
+ * if space is still remaining for '.' seperator
+ */
+ if ((src != end) || (ptr == bend))
+ return (NULL);
+ *ptr++ = '.';
+ }
+ *ptr = '\0';
+ return (ptr);
+}
+
+nss_backend_t *
+_nss_mdns_constr(mdns_backend_op_t ops[], int n_ops)
+{
+ mdns_backend_ptr_t be;
+
+ if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL)
+ return (NULL);
+ be->ops = ops;
+ be->n_ops = n_ops;
+ _nss_mdns_updatecfg(be);
+ return ((nss_backend_t *)be);
+}
+
+void
+_nss_mdns_destr(mdns_backend_ptr_t be)
+{
+ if (be != NULL) {
+ _nss_mdns_freesmfcfg(be);
+ free(be);
+ }
+}
+
+static int
+searchdomain(mdns_backend_ptr_t be, char *name, int srchidx, char **sname)
+{
+ int trailing_dot = 0;
+ char *ch;
+ *sname = NULL;
+
+ ch = name + strlen(name) - 1;
+ if ((*ch) == '.')
+ trailing_dot++;
+
+ if (trailing_dot && srchidx > 0)
+ /*
+ * If there is a trailing dot in the query
+ * name, do not perform any additional queries
+ * with search domains.
+ */
+ return (-1);
+
+ if (srchidx == 0) {
+ /*
+ * If there is a trailing dot in the query
+ * or atleast one dot in the query name then
+ * perform a query as-is once first.
+ */
+ ++srchidx;
+ if ((trailing_dot || (strchr(name, '.') != NULL))) {
+ if (validdomain(be, name, 1) == B_TRUE)
+ return (srchidx);
+ else if (trailing_dot)
+ return (-1);
+ }
+ }
+
+ if ((srchidx > NSSMDNS_MAXSRCHDMNS) ||
+ (be->dmnsrchlist[srchidx-1] == NULL))
+ return (-1);
+
+ *sname = be->dmnsrchlist[srchidx-1];
+ return (++srchidx);
+}
+
+/*
+ * This function determines if the domain name in the query
+ * matches any of the valid & search domains in the nss_mdns
+ * configuration.
+ */
+static boolean_t
+validdomain(mdns_backend_ptr_t be, char *name, int chksrchdmns)
+{
+ char *nameptr;
+
+ /* Remove any trailing and leading dots in the name */
+ nameptr = name + strlen(name) - 1;
+ while (*nameptr && (nameptr != name) && (*nameptr == '.'))
+ nameptr--;
+ *(++nameptr) = '\0';
+ nameptr = name;
+ while (*nameptr && (*nameptr == '.'))
+ nameptr++;
+ if (*nameptr == '\0')
+ return (B_FALSE);
+
+ /* Compare with search domains */
+ if (chksrchdmns && (cmpdmn(nameptr, be->dmnsrchlist,
+ NSSMDNS_MAXSRCHDMNS) == B_TRUE))
+ return (B_TRUE);
+
+ /* Compare with valid domains */
+ return (cmpdmn(nameptr, be->validdmnlist, NSSMDNS_MAXVALIDDMNS));
+}
+
+static boolean_t
+cmpdmn(char *name, char **dmnlist, int maxdmns)
+{
+ char *vptr;
+ int vdlen;
+ char *cptr;
+ int nlen;
+ int i;
+
+ nlen = strlen(name);
+ for (i = 0; (i < maxdmns) &&
+ ((vptr = dmnlist[i]) != NULL); i++) {
+ vdlen = strlen(vptr);
+ if (vdlen > nlen)
+ continue;
+ cptr = name + nlen - vdlen;
+ if (strncasecmp(cptr, vptr, vdlen) == 0)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+static void
+_nss_mdns_get_svcstatetimestamp(struct timeval *ptv)
+{
+ scf_handle_t *h;
+ scf_simple_prop_t *sprop;
+ int32_t nsec;
+
+ (void) memset(ptv, 0, sizeof (struct timeval));
+
+ h = scf_handle_create(SCF_VERSION);
+ if (h == NULL)
+ return;
+
+ if (scf_handle_bind(h) == -1) {
+ scf_handle_destroy(h);
+ return;
+ }
+
+ if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI,
+ SCF_PG_RESTARTER, SCF_PROPERTY_STATE_TIMESTAMP)) != NULL) {
+ ptv->tv_sec = *(time_t *)(scf_simple_prop_next_time(sprop,
+ &nsec));
+ ptv->tv_usec = nsec / 1000;
+ scf_simple_prop_free(sprop);
+ }
+
+ if (h != NULL)
+ scf_handle_destroy(h);
+}
+
+void
+_nss_mdns_updatecfg(mdns_backend_ptr_t be)
+{
+ struct timeval statetimestamp;
+
+ /*
+ * Update configuration if current svc state timestamp
+ * is different from last known svc state timestamp
+ */
+ _nss_mdns_get_svcstatetimestamp(&statetimestamp);
+ if ((statetimestamp.tv_sec == 0) && (statetimestamp.tv_usec == 0)) {
+ syslog(LOG_ERR, "nss_mdns: error checking " \
+ "svc:/network/dns/multicast:default" \
+ " service timestamp");
+ } else if ((be->conftimestamp.tv_sec == statetimestamp.tv_sec) &&
+ (be->conftimestamp.tv_usec == statetimestamp.tv_usec)) {
+ return;
+ }
+
+ _nss_mdns_freesmfcfg(be);
+ _nss_mdns_loadsmfcfg(be);
+ be->conftimestamp.tv_sec = statetimestamp.tv_sec;
+ be->conftimestamp.tv_usec = statetimestamp.tv_usec;
+}
+
+static void
+load_mdns_domaincfg(scf_handle_t *h, char **storelist,
+ const char *scfprop, int maxprops)
+{
+ scf_simple_prop_t *sprop;
+ char *tchr;
+ char *pchr;
+ int tlen;
+ int cnt = 0;
+
+ if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI,
+ SMF_NSSMDNSCFG_PROPGRP, scfprop)) == NULL)
+ return;
+
+ while ((cnt < maxprops) &&
+ (tchr = scf_simple_prop_next_astring(sprop)) != NULL) {
+
+ /* Remove beginning & trailing '.' chars */
+ while (*tchr && (*tchr == '.'))
+ tchr++;
+
+ if (*tchr && ((tlen = strlen(tchr)) < MAXDNAME)) {
+ pchr = &tchr[tlen-1];
+ while ((pchr != tchr) && (*pchr == '.'))
+ pchr--;
+ *(++pchr) = '\0';
+ storelist[cnt] = strdup(tchr);
+ cnt++;
+ }
+ }
+ scf_simple_prop_free(sprop);
+}
+
+static void
+_nss_mdns_loadsmfcfg(mdns_backend_ptr_t be)
+{
+ scf_handle_t *h;
+
+ h = scf_handle_create(SCF_VERSION);
+ if (h == NULL)
+ return;
+
+ if (scf_handle_bind(h) == -1) {
+ scf_handle_destroy(h);
+ return;
+ }
+
+ load_mdns_domaincfg(h, &(be->dmnsrchlist[0]),
+ SMF_NSSMDNSCFG_SRCHPROP, NSSMDNS_MAXSRCHDMNS);
+
+ load_mdns_domaincfg(h, &(be->validdmnlist[0]),
+ SMF_NSSMDNSCFG_DMNPROP, NSSMDNS_MAXVALIDDMNS);
+
+ if (h != NULL)
+ scf_handle_destroy(h);
+}
+
+static void
+_nss_mdns_freesmfcfg(mdns_backend_ptr_t be)
+{
+ int idx;
+ if (be == NULL)
+ return;
+ for (idx = 0; idx < NSSMDNS_MAXSRCHDMNS; idx++) {
+ if (be->dmnsrchlist[idx] != NULL) {
+ free(be->dmnsrchlist[idx]);
+ be->dmnsrchlist[idx] = NULL;
+ }
+ }
+ for (idx = 0; idx < NSSMDNS_MAXVALIDDMNS; idx++) {
+ if (be->validdmnlist[idx] != NULL) {
+ free(be->validdmnlist[idx]);
+ be->validdmnlist[idx] = NULL;
+ }
+ }
+}
+
+/*
+ * Performs lookup for IP address by hostname via mDNS and returns
+ * results along with the TTL value from the mDNS resource records.
+ * Called by nscd wth a ptr to packed bufer and packed buffer size.
+ */
+nss_status_t
+_nss_mdns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
+{
+ nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
+ nss_XbyY_args_t arg;
+ int dbop;
+ int af;
+ int len;
+ int blen;
+ char *dbname;
+ nss_status_t sret;
+ char *hname;
+ struct mdns_querydata qdata;
+ nssuint_t *pttl;
+ mdns_backend_ptr_t be = NULL;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+
+ qdata.argp = &arg;
+
+ /*
+ * Retrieve withttl buffer and size from the passed packed buffer.
+ * Results are returned along with ttl in this buffer.
+ */
+ qdata.withttlbsize = pbuf->data_len - sizeof (nssuint_t);
+ qdata.withttlbuffer = (char *)buffer + pbuf->data_off;
+
+ sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
+ if (sret != NSS_SUCCESS)
+ return (NSS_ERROR);
+
+ if (ipnode) {
+ if (arg.key.ipnode.flags != 0)
+ return (NSS_ERROR);
+ hname = (char *)arg.key.ipnode.name;
+ af = arg.key.ipnode.af_family;
+ } else {
+ af = AF_INET;
+ hname = (char *)arg.key.name;
+ }
+
+ if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL)
+ return (NSS_ERROR);
+ _nss_mdns_updatecfg(be);
+
+ /* Zero out the withttl buffer prior to use */
+ (void) memset(qdata.withttlbuffer, 0, qdata.withttlbsize);
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querybyname withttl called" \
+ " af:%d hname:%s", af, hname);
+#endif
+ if (_nss_mdns_querybyname(be, hname, af, &qdata) == NSS_SUCCESS) {
+ blen = strlen(qdata.buffer);
+ len = ROUND_UP(blen, sizeof (nssuint_t));
+
+ if (len + sizeof (nssuint_t) > pbuf->data_len) {
+ _nss_mdns_freesmfcfg(be);
+ free(be);
+ return (NSS_ERROR);
+ }
+
+ pbuf->ext_off = pbuf->data_off + len;
+ pbuf->ext_len = sizeof (nssuint_t);
+ pbuf->data_len = blen;
+
+ /* Return ttl in the packed buffer at ext_off */
+ pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
+ *pttl = qdata.ttl;
+
+ _nss_mdns_freesmfcfg(be);
+ free(be);
+ return (NSS_SUCCESS);
+ }
+ _nss_mdns_freesmfcfg(be);
+ free(be);
+ return (NSS_ERROR);
+}
diff --git a/usr/src/lib/nsswitch/mdns/common/mdns_common.h b/usr/src/lib/nsswitch/mdns/common/mdns_common.h
new file mode 100644
index 0000000000..0170b0fe18
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/mdns_common.h
@@ -0,0 +1,107 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef _MDNS_COMMON_H
+#define _MDNS_COMMON_H
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <strings.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <nsswitch.h>
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <libscf.h>
+#include <dns_sd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NSSMDNS_MAXQRYTMO 1
+#define NSSMDNS_MAXSRCHDMNS 6
+#define NSSMDNS_MAXVALIDDMNS 10
+
+#define SMF_MDNS_FMRI "svc:/network/dns/multicast:default"
+#define SMF_NSSMDNSCFG_PROPGRP "nss_mdns_config"
+#define SMF_NSSMDNSCFG_SRCHPROP "search"
+#define SMF_NSSMDNSCFG_DMNPROP "domain"
+
+typedef struct mdns_backend *mdns_backend_ptr_t;
+typedef nss_status_t (*mdns_backend_op_t)(mdns_backend_ptr_t, void *);
+
+struct mdns_backend {
+ mdns_backend_op_t *ops;
+ nss_dbop_t n_ops;
+ char *dmnsrchlist[NSSMDNS_MAXSRCHDMNS];
+ char *validdmnlist[NSSMDNS_MAXVALIDDMNS];
+ struct timeval conftimestamp;
+};
+
+struct mdns_querydata {
+ nss_XbyY_args_t *argp;
+ char *buffer;
+ int buflen;
+ int ttl;
+ boolean_t qrydone;
+ int status;
+ int af;
+ char paddrbuf[INET6_ADDRSTRLEN + 1];
+ int withttlbsize;
+ char *withttlbuffer;
+};
+
+nss_backend_t *_nss_mdns_constr(mdns_backend_op_t *, int);
+void _nss_mdns_destr(mdns_backend_ptr_t);
+int _nss_mdns_querybyname(mdns_backend_ptr_t, char *name,
+ int af, struct mdns_querydata *data);
+int _nss_mdns_querybyaddr(mdns_backend_ptr_t, char *name,
+ int af, struct mdns_querydata *data);
+void _nss_mdns_updatecfg(mdns_backend_ptr_t);
+
+nss_status_t _nss_mdns_gethost_withttl(void *buf, size_t bufsize, int ipnode);
+nss_status_t _nss_get_mdns_hosts_name(mdns_backend_ptr_t *be,
+ void **bufp, size_t *sizep);
+nss_status_t _nss_get_mdns_ipnodes_name(mdns_backend_ptr_t *be,
+ void **bufp, size_t *sizep);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDNS_COMMON_H */