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