diff options
author | rs200217 <none@none> | 2007-08-20 11:44:22 -0700 |
---|---|---|
committer | rs200217 <none@none> | 2007-08-20 11:44:22 -0700 |
commit | 4b22b9337f359bfd063322244f5336cc7c6ffcfa (patch) | |
tree | d935e6adb14cc353974e0fcf2dc89a47f18b9971 /usr/src/lib/nsswitch/mdns/common | |
parent | d96018ff0218b4466f62ea165b3dfd64ef271965 (diff) | |
download | illumos-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.c | 153 | ||||
-rw-r--r-- | usr/src/lib/nsswitch/mdns/common/gethostent6.c | 174 | ||||
-rw-r--r-- | usr/src/lib/nsswitch/mdns/common/mapfile-vers | 36 | ||||
-rw-r--r-- | usr/src/lib/nsswitch/mdns/common/mdns_common.c | 763 | ||||
-rw-r--r-- | usr/src/lib/nsswitch/mdns/common/mdns_common.h | 107 |
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 */ |