14b22b933Srs /*
24b22b933Srs  * CDDL HEADER START
34b22b933Srs  *
44b22b933Srs  * The contents of this file are subject to the terms of the
54b22b933Srs  * Common Development and Distribution License (the "License").
64b22b933Srs  * You may not use this file except in compliance with the License.
74b22b933Srs  *
84b22b933Srs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94b22b933Srs  * or http://www.opensolaris.org/os/licensing.
104b22b933Srs  * See the License for the specific language governing permissions
114b22b933Srs  * and limitations under the License.
124b22b933Srs  *
134b22b933Srs  * When distributing Covered Code, include this CDDL HEADER in each
144b22b933Srs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154b22b933Srs  * If applicable, add the following below this CDDL HEADER, with the
164b22b933Srs  * fields enclosed by brackets "[]" replaced with your own identifying
174b22b933Srs  * information: Portions Copyright [yyyy] [name of copyright owner]
184b22b933Srs  *
194b22b933Srs  * CDDL HEADER END
204b22b933Srs  */
214b22b933Srs 
224b22b933Srs /*
234b22b933Srs  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
244b22b933Srs  * Use is subject to license terms.
254b22b933Srs  */
264b22b933Srs 
274b22b933Srs #include "mdns_common.h"
284b22b933Srs 
294b22b933Srs static int _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
304b22b933Srs 		DNSServiceQueryRecordReply callback,
314b22b933Srs 		struct mdns_querydata *data);
324b22b933Srs static void _nss_mdns_get_svcstatetimestamp(struct timeval *);
334b22b933Srs static void _nss_mdns_loadsmfcfg(mdns_backend_ptr_t);
344b22b933Srs static void _nss_mdns_freesmfcfg(mdns_backend_ptr_t);
354b22b933Srs static boolean_t cmpdmn(char *, char **, int);
364b22b933Srs static char *RDataToName(char *data, char *buffer, int datalen, int buflen);
374b22b933Srs static int searchdomain(mdns_backend_ptr_t, char *, int, char **);
384b22b933Srs static boolean_t validdomain(mdns_backend_ptr_t, char *, int);
394b22b933Srs 
404b22b933Srs /*
414b22b933Srs  * This file includes the functions to query for host name
424b22b933Srs  * information via Multicast DNS (mDNS). The function
434b22b933Srs  * _nss_mdns_queryrecord queries for the host information via
444b22b933Srs  * Multicast DNS. _nss_mdns_querybyname and _nss_mdns_querybyaddr
454b22b933Srs  * query for host IP address and hostname by querying for A/AAAA
464b22b933Srs  * and PTR DNS resource records respectively. DNSServiceQueryRecord
474b22b933Srs  * in libdns_sd sends a request to the mDNS daemon (mdnsd) to place
484b22b933Srs  * the DNS query via multicast and return the results.
494b22b933Srs  * mdnsd is managed by SMF (FMRI: svc:/network/dns/multicast:default).
504b22b933Srs  *
514b22b933Srs  * gethostent.c and gethostent6.c implement the nsswitch 'hosts'
524b22b933Srs  * backend module getXbyY functions: getbyname and getbyaddr.
534b22b933Srs  * getby* functions in gethostent.c  supports only IPv4 and
544b22b933Srs  * getby* functions in gethostent6.c returns both IPv4 and
554b22b933Srs  * IPv6 results. Functions in gethostent.c and gethostent6.c
564b22b933Srs  * call the _nss_mdns_queryby* functions in mdns_common.c to
574b22b933Srs  * query for host information via mDNS.
584b22b933Srs  *
594b22b933Srs  * Configuration for mdns is stored in SMF and is accessed using
604b22b933Srs  * the FMRI: svc:/network/dns/multicast:default. Configuration
614b22b933Srs  * includes the list of valid DNS domains checked before querying host
624b22b933Srs  * information via mDNS and the search list to use for host lookup via
634b22b933Srs  * mDNS. The default valid domain list in the mDNS service supports host
644b22b933Srs  * lookups for hostnames in the ".local" domain and hostname queries
654b22b933Srs  * for link-local IPv4 and IPv6 addresses. _nss_mdns_loadsmfcfg
664b22b933Srs  * loads the nss_mdns configuration from SMF and the function
674b22b933Srs  * _nss_mdns_updatecfg checks for any updates in nss_mdns configuration.
684b22b933Srs  */
694b22b933Srs 
704b22b933Srs static int
_nss_mdns_queryrecord(const char * rrname,int rrclass,int rrtype,DNSServiceQueryRecordReply callback,struct mdns_querydata * data)714b22b933Srs _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
724b22b933Srs 		DNSServiceQueryRecordReply callback,
734b22b933Srs 		struct mdns_querydata *data)
744b22b933Srs {
754b22b933Srs 	int sockfd;
764b22b933Srs 	int flags = kDNSServiceFlagsForceMulticast;  /* Multicast only */
774b22b933Srs 	int opinterface = kDNSServiceInterfaceIndexAny;
784b22b933Srs 	DNSServiceErrorType err;
794b22b933Srs 	DNSServiceRef ref = NULL;
804b22b933Srs 	int ret;
814b22b933Srs 	struct fd_set readfds;
824b22b933Srs 	struct timeval tv;
834b22b933Srs 
844b22b933Srs 	data->status = NSS_NOTFOUND;
854b22b933Srs #ifdef DEBUG
864b22b933Srs 	syslog(LOG_DEBUG, "nss_mdns: query called rrname:%s rrtype:%d",
874b22b933Srs 	    rrname, rrtype);
884b22b933Srs #endif
894b22b933Srs 	err = DNSServiceQueryRecord(&ref, flags, opinterface,
904b22b933Srs 	    rrname, rrtype, rrclass, callback, data);
914b22b933Srs 	if (err != kDNSServiceErr_NoError || ref == NULL ||
92*6e6545bfSToomas Soome 	    (sockfd = DNSServiceRefSockFD(ref)) == 0) {
934b22b933Srs 		DNSServiceRefDeallocate(ref);
944b22b933Srs 		data->status = NSS_UNAVAIL;
954b22b933Srs 		return (NSS_UNAVAIL);
964b22b933Srs 	}
974b22b933Srs 
984b22b933Srs 	do {
994b22b933Srs 		FD_ZERO(&readfds);
1004b22b933Srs 		FD_SET(sockfd, &readfds);
1014b22b933Srs 		tv.tv_sec = NSSMDNS_MAXQRYTMO;
1024b22b933Srs 		tv.tv_usec = 0;
1034b22b933Srs 
1044b22b933Srs 		/* Wait until response received from mDNS daemon */
1054b22b933Srs 		ret = select(sockfd + 1, &readfds, NULL, NULL, &tv);
1064b22b933Srs 		if (!((ret > 0) && FD_ISSET(sockfd, &readfds) &&
1074b22b933Srs 		    (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError))) {
1084b22b933Srs 			data->status = NSS_NOTFOUND;
1094b22b933Srs 			if (errno != EINTR)
1104b22b933Srs 				data->qrydone = B_TRUE;
1114b22b933Srs 		}
1124b22b933Srs 	} while (data->qrydone != B_TRUE);
1134b22b933Srs 
1144b22b933Srs 	if (data->status == NSS_SUCCESS && (data->withttlbuffer == NULL)) {
1154b22b933Srs 		nss_XbyY_args_t *argp = data->argp;
1164b22b933Srs 		if (argp->buf.result != NULL) {
1174b22b933Srs 			int stat;
1184b22b933Srs 
1194b22b933Srs 			if (data->buffer == NULL) {
1204b22b933Srs 				data->status = NSS_NOTFOUND;
1214b22b933Srs 				DNSServiceRefDeallocate(ref);
1224b22b933Srs 				return (data->status);
1234b22b933Srs 			}
1244b22b933Srs 			stat = (*argp->str2ent)(data->buffer,
1254b22b933Srs 			    strlen(data->buffer),
1264b22b933Srs 			    argp->buf.result, argp->buf.buffer,
1274b22b933Srs 			    argp->buf.buflen);
1284b22b933Srs 			if (stat == NSS_STR_PARSE_SUCCESS) {
1294b22b933Srs 				argp->returnval = argp->buf.result;
1304b22b933Srs 				argp->returnlen = 1;
1314b22b933Srs 			} else {
1324b22b933Srs 				data->status = NSS_NOTFOUND;
1334b22b933Srs 				if (stat == NSS_STR_PARSE_ERANGE)
1344b22b933Srs 					argp->erange = 1;
1354b22b933Srs 			}
1364b22b933Srs 			free(data->buffer);
1374b22b933Srs 		} else {
1384b22b933Srs 			argp->returnval = argp->buf.buffer;
1394b22b933Srs 			argp->returnlen = strlen(argp->buf.buffer);
1404b22b933Srs 		}
1414b22b933Srs 		data->buffer = NULL;
1424b22b933Srs 		data->buflen = 0;
1434b22b933Srs 	}
1444b22b933Srs 
1454b22b933Srs 	if (data->status != NSS_SUCCESS)
1464b22b933Srs 		data->argp->h_errno = HOST_NOT_FOUND;
1474b22b933Srs 
1484b22b933Srs 	DNSServiceRefDeallocate(ref);
1494b22b933Srs 	return (data->status);
1504b22b933Srs }
1514b22b933Srs 
1524b22b933Srs static void
1534b22b933Srs /* LINTED E_FUNC_ARG_UNUSED */
_nss_mdns_querynamereply(DNSServiceRef sdRef,const DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * fullname,uint16_t rrtype,uint16_t rrclass,uint16_t rdlen,const void * rdata,uint32_t ttl,void * context)1544b22b933Srs _nss_mdns_querynamereply(DNSServiceRef sdRef, const DNSServiceFlags flags,
1554b22b933Srs 		/* LINTED E_FUNC_ARG_UNUSED */
1564b22b933Srs 		uint32_t ifIndex, DNSServiceErrorType errorCode,
1574b22b933Srs 		const char *fullname, uint16_t rrtype, uint16_t rrclass,
1584b22b933Srs 		/* LINTED E_FUNC_ARG_UNUSED */
1594b22b933Srs 		uint16_t rdlen, const void *rdata, uint32_t ttl,
1604b22b933Srs 		void *context)
1614b22b933Srs {
1624b22b933Srs 	struct mdns_querydata *qdata;
1634b22b933Srs 	nss_XbyY_args_t *argp;
1644b22b933Srs 	int firstent = 0;
1654b22b933Srs 	int af;
1664b22b933Srs 	char addrstore[INET6_ADDRSTRLEN];
1674b22b933Srs 	char *buffer;
1684b22b933Srs 	int len;
1694b22b933Srs 	int remlen;
1704b22b933Srs 
1714b22b933Srs 	qdata = (struct mdns_querydata *)context;
1724b22b933Srs 	argp = qdata->argp;
1734b22b933Srs 
1744b22b933Srs 	if (errorCode != kDNSServiceErr_NoError) {
1754b22b933Srs 		qdata->qrydone = B_TRUE;
1764b22b933Srs 		return;
1774b22b933Srs 	}
1784b22b933Srs 	if ((flags & kDNSServiceFlagsMoreComing))
1794b22b933Srs 		qdata->qrydone = B_FALSE;
1804b22b933Srs 	else
1814b22b933Srs 		qdata->qrydone = B_TRUE;
1824b22b933Srs 	if (!(flags & kDNSServiceFlagsAdd))
1834b22b933Srs 		return;
1844b22b933Srs 	if (rrclass != kDNSServiceClass_IN)
1854b22b933Srs 		return;
1864b22b933Srs 
1874b22b933Srs 	if (rrtype == kDNSServiceType_A)
1884b22b933Srs 		af = AF_INET;
1894b22b933Srs 	else if (rrtype == kDNSServiceType_AAAA)
1904b22b933Srs 		af = AF_INET6;
1914b22b933Srs 	else
1924b22b933Srs 		return;
1934b22b933Srs 
1944b22b933Srs 	if (qdata->buffer == NULL) {
1954b22b933Srs 		if (qdata->withttlbsize > 0) {
1964b22b933Srs 			remlen = qdata->buflen =
1974b22b933Srs 			    qdata->withttlbsize;
1984b22b933Srs 			buffer = qdata->buffer =
1994b22b933Srs 			    qdata->withttlbuffer;
2004b22b933Srs 			(void) memset(qdata->buffer, 0, remlen);
2014b22b933Srs 		} else {
2024b22b933Srs 			remlen = qdata->buflen =
2034b22b933Srs 			    argp->buf.buflen;
2044b22b933Srs 			if (argp->buf.result != NULL) {
2054b22b933Srs 				buffer = qdata->buffer =
2064b22b933Srs 				    calloc(1, remlen);
2074b22b933Srs 			} else {
2084b22b933Srs 				/* Return in file format */
2094b22b933Srs 				(void) memset(argp->buf.buffer,
2104b22b933Srs 				    0, remlen);
2114b22b933Srs 				buffer = qdata->buffer = argp->buf.buffer;
2124b22b933Srs 			}
2134b22b933Srs 		}
2144b22b933Srs 		firstent = 1;
2154b22b933Srs 	} else {
2164b22b933Srs 		buffer = qdata->buffer + strlen(qdata->buffer);
2174b22b933Srs 		remlen = qdata->buflen - strlen(qdata->buffer);
2184b22b933Srs 	}
2194b22b933Srs 
2204b22b933Srs #ifdef DEBUG
2214b22b933Srs 	syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen);
2224b22b933Srs #endif
2234b22b933Srs 	if (inet_ntop(af, rdata, addrstore, INET6_ADDRSTRLEN) != NULL) {
2244b22b933Srs 		if (firstent)
2254b22b933Srs 			len = snprintf(buffer, remlen, "%s %s",
2264b22b933Srs 			    addrstore, fullname);
2274b22b933Srs 		else
2284b22b933Srs 			len = snprintf(buffer, remlen, "\n%s %s",
2294b22b933Srs 			    addrstore, fullname);
2304b22b933Srs 		if (len >= remlen || len < 0) {
2314b22b933Srs 			qdata->status = NSS_NOTFOUND;
2324b22b933Srs 			qdata->argp->erange = 1;
2334b22b933Srs 			qdata->argp->h_errno = HOST_NOT_FOUND;
2344b22b933Srs 			return;
2354b22b933Srs 		}
2364b22b933Srs 		qdata->ttl	= ttl;
2374b22b933Srs 		qdata->status	= NSS_SUCCESS;
2384b22b933Srs #ifdef DEBUG
2394b22b933Srs 		syslog(LOG_DEBUG, "nss_mdns: querynamereply buffer:%s", buffer);
2404b22b933Srs #endif
2414b22b933Srs 	} else {
2424b22b933Srs 		qdata->status = NSS_NOTFOUND;
2434b22b933Srs 		qdata->argp->h_errno = HOST_NOT_FOUND;
2444b22b933Srs 	}
2454b22b933Srs }
2464b22b933Srs 
2474b22b933Srs int
_nss_mdns_querybyname(mdns_backend_ptr_t be,char * qname,int af,struct mdns_querydata * data)2484b22b933Srs _nss_mdns_querybyname(mdns_backend_ptr_t be, char *qname,
2494b22b933Srs 		int af, struct mdns_querydata *data)
2504b22b933Srs {
2514b22b933Srs 	int rrtype;
2524b22b933Srs 	int rrclass;
2534b22b933Srs 	int srchidx = 0;
2544b22b933Srs 	int rc;
2554b22b933Srs 	char hname[MAXDNAME];
2564b22b933Srs 	char *name;
2574b22b933Srs 	char *sname;
2584b22b933Srs 
2594b22b933Srs 	rrclass = kDNSServiceClass_IN;
2604b22b933Srs 	if (af == AF_INET6)
2614b22b933Srs 		rrtype = kDNSServiceType_ANY;
2624b22b933Srs 	else if (af == AF_INET)
2634b22b933Srs 		rrtype = kDNSServiceType_A;
2644b22b933Srs 	else
2654b22b933Srs 		return (NSS_NOTFOUND);
2664b22b933Srs 
2674b22b933Srs 	name = strdup(qname);
2684b22b933Srs 	if (name == NULL)
2694b22b933Srs 		return (NSS_UNAVAIL);
2704b22b933Srs 
2714b22b933Srs 	while ((srchidx = searchdomain(be, name, srchidx, &sname)) != -1) {
2724b22b933Srs 		if (sname != NULL)
2734b22b933Srs 			(void) snprintf(hname, sizeof (hname), "%s.%s",
2744b22b933Srs 			    name, sname);
2754b22b933Srs 		else
2764b22b933Srs 			(void) strlcpy(hname, name, sizeof (hname));
2774b22b933Srs #ifdef DEBUG
2784b22b933Srs 	syslog(LOG_DEBUG, "nss_mdns: querybyname called" \
2794b22b933Srs 	    " srchidx:%d af:%d hname:%s", srchidx, af, qname);
2804b22b933Srs #endif
2814b22b933Srs 		rc = _nss_mdns_queryrecord(hname, rrclass, rrtype,
2824b22b933Srs 		    _nss_mdns_querynamereply, data);
2834b22b933Srs 		if ((rc == NSS_UNAVAIL) || (rc == NSS_SUCCESS)) {
2844b22b933Srs 			free(name);
2854b22b933Srs 			return (rc);
2864b22b933Srs 		}
2874b22b933Srs 	}
2884b22b933Srs 	free(name);
2894b22b933Srs 	return (NSS_NOTFOUND);
2904b22b933Srs }
2914b22b933Srs 
2924b22b933Srs static void
2934b22b933Srs /* LINTED E_FUNC_ARG_UNUSED */
_nss_mdns_queryaddrreply(DNSServiceRef sdRef,const DNSServiceFlags flags,uint32_t ifIndex,DNSServiceErrorType errorCode,const char * fullname,uint16_t rrtype,uint16_t rrclass,uint16_t rdlen,const void * rdata,uint32_t ttl,void * context)2944b22b933Srs _nss_mdns_queryaddrreply(DNSServiceRef sdRef, const DNSServiceFlags flags,
2954b22b933Srs 		/* LINTED E_FUNC_ARG_UNUSED */
2964b22b933Srs 		uint32_t ifIndex, DNSServiceErrorType errorCode,
2974b22b933Srs 		/* LINTED E_FUNC_ARG_UNUSED */
2984b22b933Srs 		const char *fullname, uint16_t rrtype, uint16_t rrclass,
2994b22b933Srs 		uint16_t rdlen, const void *rdata, uint32_t ttl,
3004b22b933Srs 		void *context)
3014b22b933Srs {
3024b22b933Srs 	struct mdns_querydata *qdata;
3034b22b933Srs 	nss_XbyY_args_t *argp;
3044b22b933Srs 	char hostname[NI_MAXHOST];
3054b22b933Srs 	int firstent = 0;
3064b22b933Srs 	char *buffer;
3074b22b933Srs 	int len;
3084b22b933Srs 	int remlen;
3094b22b933Srs 
3104b22b933Srs 	qdata = (struct mdns_querydata *)context;
3114b22b933Srs 	argp = qdata->argp;
3124b22b933Srs 
3134b22b933Srs 	if (errorCode != kDNSServiceErr_NoError) {
3144b22b933Srs 		qdata->qrydone = B_TRUE;
3154b22b933Srs 		return;
3164b22b933Srs 	}
3174b22b933Srs 	if ((flags & kDNSServiceFlagsMoreComing))
3184b22b933Srs 		qdata->qrydone = B_FALSE;
3194b22b933Srs 	else
3204b22b933Srs 		qdata->qrydone = B_TRUE;
3214b22b933Srs 	if (!(flags & kDNSServiceFlagsAdd))
3224b22b933Srs 		return;
3234b22b933Srs 	if (rrclass != kDNSServiceClass_IN)
3244b22b933Srs 		return;
3254b22b933Srs 	if (rrtype != kDNSServiceType_PTR)
3264b22b933Srs 		return;
3274b22b933Srs 
3284b22b933Srs 	if (qdata->buffer == NULL) {
3294b22b933Srs 		remlen = qdata->buflen = argp->buf.buflen;
3304b22b933Srs 		if (argp->buf.result != NULL) {
3314b22b933Srs 			buffer = qdata->buffer = calloc(1, remlen);
3324b22b933Srs 		} else {
3334b22b933Srs 			/* Return in file format */
3344b22b933Srs 			(void) memset(argp->buf.buffer, 0, remlen);
3354b22b933Srs 			buffer = qdata->buffer = argp->buf.buffer;
3364b22b933Srs 		}
3374b22b933Srs 		firstent = 1;
3384b22b933Srs 	} else {
3394b22b933Srs 		buffer = qdata->buffer + strlen(qdata->buffer);
3404b22b933Srs 		remlen = qdata->buflen - strlen(qdata->buffer);
3414b22b933Srs 	}
3424b22b933Srs 
3434b22b933Srs 	if (RDataToName((char *)rdata, hostname, rdlen, NI_MAXHOST) == NULL) {
3444b22b933Srs 		qdata->status = NSS_NOTFOUND;
3454b22b933Srs 		qdata->argp->h_errno = HOST_NOT_FOUND;
3464b22b933Srs 		return;
3474b22b933Srs 	}
3484b22b933Srs 
3494b22b933Srs #ifdef DEBUG
3504b22b933Srs 	syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen);
3514b22b933Srs #endif
3524b22b933Srs 	if (firstent)
3534b22b933Srs 		len = snprintf(buffer, remlen, "%s %s",
3544b22b933Srs 		    qdata->paddrbuf, hostname);
3554b22b933Srs 	else
3564b22b933Srs 		len = snprintf(buffer, remlen, "\n%s %s",
3574b22b933Srs 		    qdata->paddrbuf, hostname);
3584b22b933Srs 	if (len >= remlen || len < 0) {
3594b22b933Srs 		qdata->status = NSS_NOTFOUND;
3604b22b933Srs 		qdata->argp->erange = 1;
3614b22b933Srs 		qdata->argp->h_errno = HOST_NOT_FOUND;
3624b22b933Srs 		return;
3634b22b933Srs 	}
3644b22b933Srs 	qdata->status	= NSS_SUCCESS;
3654b22b933Srs 	qdata->ttl	= ttl;
3664b22b933Srs }
3674b22b933Srs 
3684b22b933Srs int
3694b22b933Srs /* LINTED E_FUNC_ARG_UNUSED */
_nss_mdns_querybyaddr(mdns_backend_ptr_t be,char * name,int af,struct mdns_querydata * data)3704b22b933Srs _nss_mdns_querybyaddr(mdns_backend_ptr_t be, char *name, int af,
3714b22b933Srs 		struct mdns_querydata *data)
3724b22b933Srs {
3734b22b933Srs 	int rrtype;
3744b22b933Srs 	int rrclass;
3754b22b933Srs 
3764b22b933Srs #ifdef DEBUG
3774b22b933Srs 	syslog(LOG_DEBUG, "nss_mdns: querybyaddr called" \
3784b22b933Srs 	    " af:%d addr:%s", af, name);
3794b22b933Srs #endif
3804b22b933Srs 	rrclass = kDNSServiceClass_IN;
3814b22b933Srs 	rrtype = kDNSServiceType_PTR;
3824b22b933Srs 
3834b22b933Srs 	if (validdomain(be, name, 0) == B_FALSE) {
3844b22b933Srs 		data->status = NSS_NOTFOUND;
3854b22b933Srs 		return (NSS_NOTFOUND);
3864b22b933Srs 	}
3874b22b933Srs 	return (_nss_mdns_queryrecord(name, rrclass, rrtype,
3884b22b933Srs 	    _nss_mdns_queryaddrreply, data));
3894b22b933Srs }
3904b22b933Srs 
3914b22b933Srs /*
3924b22b933Srs  * Converts the encoded name in RData returned
3934b22b933Srs  * by mDNS query to name in file format
3944b22b933Srs  */
3954b22b933Srs static char *
RDataToName(char * data,char * buffer,int datalen,int buflen)3964b22b933Srs RDataToName(char *data, char *buffer, int datalen, int buflen)
3974b22b933Srs {
3984b22b933Srs 	char *src = data;
3994b22b933Srs 	char *srcend = data + datalen;
4004b22b933Srs 	char *ptr = buffer;
4014b22b933Srs 	char *end;
4024b22b933Srs 	char *bend = buffer + buflen - 1; /* terminal '\0' */
4034b22b933Srs 	int domainlen = 0;
4044b22b933Srs 
4054b22b933Srs 	while ((src < srcend) && (*src != 0)) {
4064b22b933Srs 
4074b22b933Srs 		/* first byte is len */
4084b22b933Srs 		domainlen = *src++;
4094b22b933Srs 		end = src + domainlen;
4104b22b933Srs 
4114b22b933Srs 		while ((src < end) && (ptr < bend)) {
4124b22b933Srs 			uint8_t ch = *src++;
4134b22b933Srs 			if (ch == '.' || ch == '\\') {
4144b22b933Srs 				*ptr++ = '\\';
4154b22b933Srs 			}
4164b22b933Srs 			*ptr++ = ch;
4174b22b933Srs 		}
4184b22b933Srs 
4194b22b933Srs 		/*
4204b22b933Srs 		 * Check if we copied entire domain str. and
4214b22b933Srs 		 * if space is still remaining for '.' seperator
4224b22b933Srs 		 */
4234b22b933Srs 		if ((src != end) || (ptr == bend))
4244b22b933Srs 			return (NULL);
4254b22b933Srs 		*ptr++ = '.';
4264b22b933Srs 	}
4274b22b933Srs 	*ptr = '\0';
4284b22b933Srs 	return (ptr);
4294b22b933Srs }
4304b22b933Srs 
4314b22b933Srs nss_backend_t *
_nss_mdns_constr(mdns_backend_op_t ops[],int n_ops)4324b22b933Srs _nss_mdns_constr(mdns_backend_op_t ops[], int n_ops)
4334b22b933Srs {
4344b22b933Srs 	mdns_backend_ptr_t	be;
4354b22b933Srs 
4364b22b933Srs 	if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL)
4374b22b933Srs 		return (NULL);
4384b22b933Srs 	be->ops = ops;
4394b22b933Srs 	be->n_ops = n_ops;
4404b22b933Srs 	_nss_mdns_updatecfg(be);
4414b22b933Srs 	return ((nss_backend_t *)be);
4424b22b933Srs }
4434b22b933Srs 
4444b22b933Srs void
_nss_mdns_destr(mdns_backend_ptr_t be)4454b22b933Srs _nss_mdns_destr(mdns_backend_ptr_t be)
4464b22b933Srs {
4474b22b933Srs 	if (be != NULL) {
4484b22b933Srs 		_nss_mdns_freesmfcfg(be);
4494b22b933Srs 		free(be);
4504b22b933Srs 	}
4514b22b933Srs }
4524b22b933Srs 
4534b22b933Srs static int
searchdomain(mdns_backend_ptr_t be,char * name,int srchidx,char ** sname)4544b22b933Srs searchdomain(mdns_backend_ptr_t be, char *name, int srchidx, char **sname)
4554b22b933Srs {
4564b22b933Srs 	int trailing_dot = 0;
4574b22b933Srs 	char *ch;
4584b22b933Srs 	*sname = NULL;
4594b22b933Srs 
4604b22b933Srs 	ch = name + strlen(name) - 1;
4614b22b933Srs 	if ((*ch) == '.')
4624b22b933Srs 		trailing_dot++;
4634b22b933Srs 
4644b22b933Srs 	if (trailing_dot && srchidx > 0)
4654b22b933Srs 		/*
4664b22b933Srs 		 * If there is a trailing dot in the query
4674b22b933Srs 		 * name, do not perform any additional queries
4684b22b933Srs 		 * with search domains.
4694b22b933Srs 		 */
4704b22b933Srs 		return (-1);
4714b22b933Srs 
4724b22b933Srs 	if (srchidx == 0) {
4734b22b933Srs 		/*
4744b22b933Srs 		 * If there is a trailing dot in the query
4754b22b933Srs 		 * or atleast one dot in the query name then
4764b22b933Srs 		 * perform a query as-is once first.
4774b22b933Srs 		 */
4784b22b933Srs 		++srchidx;
4794b22b933Srs 		if ((trailing_dot || (strchr(name, '.') != NULL))) {
4804b22b933Srs 			if (validdomain(be, name, 1) == B_TRUE)
4814b22b933Srs 				return (srchidx);
4824b22b933Srs 			else if (trailing_dot)
4834b22b933Srs 				return (-1);
4844b22b933Srs 		}
4854b22b933Srs 	}
4864b22b933Srs 
4874b22b933Srs 	if ((srchidx > NSSMDNS_MAXSRCHDMNS) ||
4884b22b933Srs 	    (be->dmnsrchlist[srchidx-1] == NULL))
4894b22b933Srs 		return (-1);
4904b22b933Srs 
4914b22b933Srs 	*sname = be->dmnsrchlist[srchidx-1];
4924b22b933Srs 	return (++srchidx);
4934b22b933Srs }
4944b22b933Srs 
4954b22b933Srs /*
4964b22b933Srs  * This function determines if the domain name in the query
4974b22b933Srs  * matches any of the valid & search domains in the nss_mdns
4984b22b933Srs  * configuration.
4994b22b933Srs  */
5004b22b933Srs static boolean_t
validdomain(mdns_backend_ptr_t be,char * name,int chksrchdmns)5014b22b933Srs validdomain(mdns_backend_ptr_t be, char *name, int chksrchdmns)
5024b22b933Srs {
5034b22b933Srs 	char *nameptr;
5044b22b933Srs 
5054b22b933Srs 	/* Remove any trailing and leading dots in the name  */
5064b22b933Srs 	nameptr = name + strlen(name) - 1;
5074b22b933Srs 	while (*nameptr && (nameptr != name) && (*nameptr == '.'))
5084b22b933Srs 		nameptr--;
5094b22b933Srs 	*(++nameptr) = '\0';
5104b22b933Srs 	nameptr = name;
5114b22b933Srs 	while (*nameptr && (*nameptr == '.'))
5124b22b933Srs 		nameptr++;
5134b22b933Srs 	if (*nameptr == '\0')
5144b22b933Srs 		return (B_FALSE);
5154b22b933Srs 
5164b22b933Srs 	/* Compare with search domains */
5174b22b933Srs 	if (chksrchdmns && (cmpdmn(nameptr, be->dmnsrchlist,
5184b22b933Srs 	    NSSMDNS_MAXSRCHDMNS) == B_TRUE))
5194b22b933Srs 		return (B_TRUE);
5204b22b933Srs 
5214b22b933Srs 	/* Compare with valid domains */
5224b22b933Srs 	return (cmpdmn(nameptr, be->validdmnlist, NSSMDNS_MAXVALIDDMNS));
5234b22b933Srs }
5244b22b933Srs 
5254b22b933Srs static boolean_t
cmpdmn(char * name,char ** dmnlist,int maxdmns)5264b22b933Srs cmpdmn(char *name, char **dmnlist, int maxdmns)
5274b22b933Srs {
5284b22b933Srs 	char *vptr;
5294b22b933Srs 	int vdlen;
5304b22b933Srs 	char *cptr;
5314b22b933Srs 	int nlen;
5324b22b933Srs 	int i;
5334b22b933Srs 
5344b22b933Srs 	nlen = strlen(name);
5354b22b933Srs 	for (i = 0; (i < maxdmns) &&
5364b22b933Srs 	    ((vptr = dmnlist[i]) != NULL); i++) {
5374b22b933Srs 		vdlen = strlen(vptr);
5384b22b933Srs 		if (vdlen > nlen)
5394b22b933Srs 			continue;
5404b22b933Srs 		cptr = name + nlen - vdlen;
5414b22b933Srs 		if (strncasecmp(cptr, vptr, vdlen) == 0)
5424b22b933Srs 			return (B_TRUE);
5434b22b933Srs 	}
5444b22b933Srs 	return (B_FALSE);
5454b22b933Srs }
5464b22b933Srs 
5474b22b933Srs static void
_nss_mdns_get_svcstatetimestamp(struct timeval * ptv)5484b22b933Srs _nss_mdns_get_svcstatetimestamp(struct timeval *ptv)
5494b22b933Srs {
5504b22b933Srs 	scf_handle_t *h;
5514b22b933Srs 	scf_simple_prop_t *sprop;
5524b22b933Srs 	int32_t nsec;
5534b22b933Srs 
5544b22b933Srs 	(void) memset(ptv, 0, sizeof (struct timeval));
5554b22b933Srs 
5564b22b933Srs 	h = scf_handle_create(SCF_VERSION);
5574b22b933Srs 	if (h == NULL)
5584b22b933Srs 		return;
5594b22b933Srs 
5604b22b933Srs 	if (scf_handle_bind(h) == -1) {
5614b22b933Srs 		scf_handle_destroy(h);
5624b22b933Srs 		return;
5634b22b933Srs 	}
5644b22b933Srs 
5654b22b933Srs 	if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI,
5664b22b933Srs 	    SCF_PG_RESTARTER, SCF_PROPERTY_STATE_TIMESTAMP)) != NULL) {
5674b22b933Srs 		ptv->tv_sec = *(time_t *)(scf_simple_prop_next_time(sprop,
5684b22b933Srs 		    &nsec));
5694b22b933Srs 		ptv->tv_usec = nsec / 1000;
5704b22b933Srs 		scf_simple_prop_free(sprop);
5714b22b933Srs 	}
5724b22b933Srs 
5734b22b933Srs 	if (h != NULL)
5744b22b933Srs 		scf_handle_destroy(h);
5754b22b933Srs }
5764b22b933Srs 
5774b22b933Srs void
_nss_mdns_updatecfg(mdns_backend_ptr_t be)5784b22b933Srs _nss_mdns_updatecfg(mdns_backend_ptr_t be)
5794b22b933Srs {
5804b22b933Srs 	struct timeval statetimestamp;
5814b22b933Srs 
5824b22b933Srs 	/*
5834b22b933Srs 	 * Update configuration if current svc state timestamp
5844b22b933Srs 	 * is different from last known svc state timestamp
5854b22b933Srs 	 */
5864b22b933Srs 	_nss_mdns_get_svcstatetimestamp(&statetimestamp);
5874b22b933Srs 	if ((statetimestamp.tv_sec == 0) && (statetimestamp.tv_usec == 0)) {
5884b22b933Srs 		syslog(LOG_ERR, "nss_mdns: error checking " \
5894b22b933Srs 		    "svc:/network/dns/multicast:default" \
5904b22b933Srs 		    " service timestamp");
5914b22b933Srs 	} else if ((be->conftimestamp.tv_sec == statetimestamp.tv_sec) &&
5924b22b933Srs 	    (be->conftimestamp.tv_usec == statetimestamp.tv_usec)) {
5934b22b933Srs 		return;
5944b22b933Srs 	}
5954b22b933Srs 
5964b22b933Srs 	_nss_mdns_freesmfcfg(be);
5974b22b933Srs 	_nss_mdns_loadsmfcfg(be);
5984b22b933Srs 	be->conftimestamp.tv_sec = statetimestamp.tv_sec;
5994b22b933Srs 	be->conftimestamp.tv_usec = statetimestamp.tv_usec;
6004b22b933Srs }
6014b22b933Srs 
6024b22b933Srs static void
load_mdns_domaincfg(scf_handle_t * h,char ** storelist,const char * scfprop,int maxprops)6034b22b933Srs load_mdns_domaincfg(scf_handle_t *h, char **storelist,
6044b22b933Srs 			const char *scfprop, int maxprops)
6054b22b933Srs {
6064b22b933Srs 	scf_simple_prop_t *sprop;
6074b22b933Srs 	char *tchr;
6084b22b933Srs 	char *pchr;
6094b22b933Srs 	int tlen;
6104b22b933Srs 	int cnt = 0;
6114b22b933Srs 
6124b22b933Srs 	if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI,
6134b22b933Srs 	    SMF_NSSMDNSCFG_PROPGRP, scfprop)) == NULL)
6144b22b933Srs 			return;
6154b22b933Srs 
6164b22b933Srs 	while ((cnt < maxprops) &&
6174b22b933Srs 	    (tchr = scf_simple_prop_next_astring(sprop)) != NULL) {
6184b22b933Srs 
6194b22b933Srs 		/* Remove beginning & trailing '.' chars */
6204b22b933Srs 		while (*tchr && (*tchr == '.'))
6214b22b933Srs 			tchr++;
6224b22b933Srs 
6234b22b933Srs 		if (*tchr && ((tlen = strlen(tchr)) < MAXDNAME)) {
6244b22b933Srs 			pchr = &tchr[tlen-1];
6254b22b933Srs 			while ((pchr != tchr) && (*pchr == '.'))
6264b22b933Srs 				pchr--;
6274b22b933Srs 			*(++pchr) = '\0';
6284b22b933Srs 			storelist[cnt] = strdup(tchr);
6294b22b933Srs 			cnt++;
6304b22b933Srs 		}
6314b22b933Srs 	}
6324b22b933Srs 	scf_simple_prop_free(sprop);
6334b22b933Srs }
6344b22b933Srs 
6354b22b933Srs static void
_nss_mdns_loadsmfcfg(mdns_backend_ptr_t be)6364b22b933Srs _nss_mdns_loadsmfcfg(mdns_backend_ptr_t be)
6374b22b933Srs {
6384b22b933Srs 	scf_handle_t *h;
6394b22b933Srs 
6404b22b933Srs 	h = scf_handle_create(SCF_VERSION);
6414b22b933Srs 	if (h == NULL)
6424b22b933Srs 		return;
6434b22b933Srs 
6444b22b933Srs 	if (scf_handle_bind(h) == -1) {
6454b22b933Srs 		scf_handle_destroy(h);
6464b22b933Srs 		return;
6474b22b933Srs 	}
6484b22b933Srs 
6494b22b933Srs 	load_mdns_domaincfg(h, &(be->dmnsrchlist[0]),
6504b22b933Srs 	    SMF_NSSMDNSCFG_SRCHPROP, NSSMDNS_MAXSRCHDMNS);
6514b22b933Srs 
6524b22b933Srs 	load_mdns_domaincfg(h, &(be->validdmnlist[0]),
6534b22b933Srs 	    SMF_NSSMDNSCFG_DMNPROP, NSSMDNS_MAXVALIDDMNS);
6544b22b933Srs 
6554b22b933Srs 	if (h != NULL)
6564b22b933Srs 		scf_handle_destroy(h);
6574b22b933Srs }
6584b22b933Srs 
6594b22b933Srs static void
_nss_mdns_freesmfcfg(mdns_backend_ptr_t be)6604b22b933Srs _nss_mdns_freesmfcfg(mdns_backend_ptr_t be)
6614b22b933Srs {
6624b22b933Srs 	int idx;
6634b22b933Srs 	if (be == NULL)
6644b22b933Srs 		return;
6654b22b933Srs 	for (idx = 0; idx < NSSMDNS_MAXSRCHDMNS; idx++) {
6664b22b933Srs 		if (be->dmnsrchlist[idx] != NULL) {
6674b22b933Srs 			free(be->dmnsrchlist[idx]);
6684b22b933Srs 			be->dmnsrchlist[idx] = NULL;
6694b22b933Srs 		}
6704b22b933Srs 	}
6714b22b933Srs 	for (idx = 0; idx < NSSMDNS_MAXVALIDDMNS; idx++) {
6724b22b933Srs 		if (be->validdmnlist[idx] != NULL) {
6734b22b933Srs 			free(be->validdmnlist[idx]);
6744b22b933Srs 			be->validdmnlist[idx] = NULL;
6754b22b933Srs 		}
6764b22b933Srs 	}
6774b22b933Srs }
6784b22b933Srs 
6794b22b933Srs /*
6804b22b933Srs  * Performs lookup for IP address by hostname via mDNS and returns
6814b22b933Srs  * results along with the TTL value from the mDNS resource records.
6824b22b933Srs  * Called by nscd wth a ptr to packed bufer and packed buffer size.
6834b22b933Srs  */
6844b22b933Srs nss_status_t
_nss_mdns_gethost_withttl(void * buffer,size_t bufsize,int ipnode)6854b22b933Srs _nss_mdns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
6864b22b933Srs {
6874b22b933Srs 	nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
6884b22b933Srs 	nss_XbyY_args_t arg;
6894b22b933Srs 	int dbop;
6904b22b933Srs 	int af;
6914b22b933Srs 	int len;
6924b22b933Srs 	int blen;
6934b22b933Srs 	char *dbname;
6944b22b933Srs 	nss_status_t sret;
6954b22b933Srs 	char *hname;
6964b22b933Srs 	struct mdns_querydata qdata;
6974b22b933Srs 	nssuint_t *pttl;
6984b22b933Srs 	mdns_backend_ptr_t be = NULL;
6994b22b933Srs 
7004b22b933Srs 	(void) memset(&qdata, 0, sizeof (struct mdns_querydata));
7014b22b933Srs 
7024b22b933Srs 	qdata.argp = &arg;
7034b22b933Srs 
7044b22b933Srs 	/*
7054b22b933Srs 	 * Retrieve withttl buffer and size from the passed packed buffer.
7064b22b933Srs 	 * Results are returned along with ttl in this buffer.
7074b22b933Srs 	 */
7084b22b933Srs 	qdata.withttlbsize = pbuf->data_len - sizeof (nssuint_t);
7094b22b933Srs 	qdata.withttlbuffer = (char *)buffer + pbuf->data_off;
7104b22b933Srs 
7114b22b933Srs 	sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
7124b22b933Srs 	if (sret != NSS_SUCCESS)
7134b22b933Srs 		return (NSS_ERROR);
7144b22b933Srs 
7154b22b933Srs 	if (ipnode) {
7164b22b933Srs 		if (arg.key.ipnode.flags != 0)
7174b22b933Srs 			return (NSS_ERROR);
7184b22b933Srs 		hname = (char *)arg.key.ipnode.name;
7194b22b933Srs 		af = arg.key.ipnode.af_family;
7204b22b933Srs 	} else {
7214b22b933Srs 		af = AF_INET;
7224b22b933Srs 		hname = (char *)arg.key.name;
7234b22b933Srs 	}
7244b22b933Srs 
7254b22b933Srs 	if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL)
7264b22b933Srs 		return (NSS_ERROR);
7274b22b933Srs 	_nss_mdns_updatecfg(be);
7284b22b933Srs 
7294b22b933Srs 	/* Zero out the withttl buffer prior to use */
7304b22b933Srs 	(void) memset(qdata.withttlbuffer, 0, qdata.withttlbsize);
7314b22b933Srs 
7324b22b933Srs #ifdef DEBUG
7334b22b933Srs 	syslog(LOG_DEBUG, "nss_mdns: querybyname withttl called" \
7344b22b933Srs 	    " af:%d hname:%s", af, hname);
7354b22b933Srs #endif
7364b22b933Srs 	if (_nss_mdns_querybyname(be, hname, af, &qdata) == NSS_SUCCESS) {
7374b22b933Srs 		blen = strlen(qdata.buffer);
7384b22b933Srs 		len = ROUND_UP(blen, sizeof (nssuint_t));
7394b22b933Srs 
7404b22b933Srs 		if (len + sizeof (nssuint_t) > pbuf->data_len) {
7414b22b933Srs 			_nss_mdns_freesmfcfg(be);
7424b22b933Srs 			free(be);
7434b22b933Srs 			return (NSS_ERROR);
7444b22b933Srs 		}
7454b22b933Srs 
7464b22b933Srs 		pbuf->ext_off = pbuf->data_off + len;
7474b22b933Srs 		pbuf->ext_len = sizeof (nssuint_t);
7484b22b933Srs 		pbuf->data_len = blen;
7494b22b933Srs 
7504b22b933Srs 		/* Return ttl in the packed buffer at ext_off */
7514b22b933Srs 		pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
7524b22b933Srs 		*pttl = qdata.ttl;
7534b22b933Srs 
7544b22b933Srs 		_nss_mdns_freesmfcfg(be);
7554b22b933Srs 		free(be);
7564b22b933Srs 		return (NSS_SUCCESS);
7574b22b933Srs 	}
7584b22b933Srs 	_nss_mdns_freesmfcfg(be);
7594b22b933Srs 	free(be);
7604b22b933Srs 	return (NSS_ERROR);
7614b22b933Srs }
762