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
31static int _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
32		DNSServiceQueryRecordReply callback,
33		struct mdns_querydata *data);
34static void _nss_mdns_get_svcstatetimestamp(struct timeval *);
35static void _nss_mdns_loadsmfcfg(mdns_backend_ptr_t);
36static void _nss_mdns_freesmfcfg(mdns_backend_ptr_t);
37static boolean_t cmpdmn(char *, char **, int);
38static char *RDataToName(char *data, char *buffer, int datalen, int buflen);
39static int searchdomain(mdns_backend_ptr_t, char *, int, char **);
40static 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
72static 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
154static 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
249int
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
294static 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
370int
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 */
397static char *
398RDataToName(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
433nss_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
446void
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
455static int
456searchdomain(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 */
502static boolean_t
503validdomain(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
527static boolean_t
528cmpdmn(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
549static 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
579void
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
604static void
605load_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
637static 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
661static 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 */
686nss_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