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