/*
 * 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);
}