dns_common.c revision 2830:5228d1267a01
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 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 *	dns_common.c
30 */
31
32#include "dns_common.h"
33
34#pragma weak	dn_expand
35#pragma weak	res_ninit
36#pragma weak	res_nsearch
37#pragma weak	res_nclose
38#pragma weak	ns_get16
39#pragma weak	ns_get32
40#pragma weak	__ns_get16
41#pragma weak	__ns_get32
42
43#define	DNS_ALIASES	0
44#define	DNS_ADDRLIST	1
45#define	DNS_MAPDLIST	2
46
47static int
48dns_netdb_aliases(from_list, to_list, aliaspp, type, count, af_type)
49	char	**from_list, **to_list,	**aliaspp;
50	int	type, *count, af_type;
51{
52	char		*fstr;
53	int		cnt = 0;
54	size_t		len;
55
56	*count = 0;
57	if ((char *)to_list >= *aliaspp)
58		return (NSS_STR_PARSE_ERANGE);
59
60	for (fstr = from_list[cnt]; fstr != NULL; fstr = from_list[cnt]) {
61		if (type == DNS_ALIASES)
62			len = strlen(fstr) + 1;
63		else
64			len = (af_type == AF_INET) ? sizeof (struct in_addr)
65						: sizeof (struct in6_addr);
66		*aliaspp -= len;
67		to_list[cnt] = *aliaspp;
68		if (*aliaspp <= (char *)&to_list[cnt+1])
69			return (NSS_STR_PARSE_ERANGE);
70		if (type == DNS_MAPDLIST) {
71			/* LINTED: E_BAD_PTR_CAST_ALIGN */
72			struct in6_addr *addr6p = (struct in6_addr *)*aliaspp;
73
74			(void) memset(addr6p, '\0', sizeof (struct in6_addr));
75			(void) memcpy(&addr6p->s6_addr[12], fstr,
76					sizeof (struct in_addr));
77			addr6p->s6_addr[10] = 0xffU;
78			addr6p->s6_addr[11] = 0xffU;
79			++cnt;
80		} else {
81			(void) memcpy (*aliaspp, fstr, len);
82			++cnt;
83		}
84	}
85	to_list[cnt] = NULL;
86
87	*count = cnt;
88	if (cnt == 0)
89		return (NSS_STR_PARSE_PARSE);
90
91	return (NSS_STR_PARSE_SUCCESS);
92}
93
94
95int
96ent2result(he, argp, af_type)
97	struct hostent		*he;
98	nss_XbyY_args_t		*argp;
99	int			af_type;
100{
101	char		*buffer, *limit;
102	int		buflen = argp->buf.buflen;
103	int		ret, count;
104	size_t len;
105	struct hostent 	*host;
106	struct in_addr	*addrp;
107	struct in6_addr	*addrp6;
108
109	limit = argp->buf.buffer + buflen;
110	host = (struct hostent *)argp->buf.result;
111	buffer = argp->buf.buffer;
112
113	/* h_addrtype and h_length */
114	host->h_addrtype = af_type;
115	host->h_length = (af_type == AF_INET) ? sizeof (struct in_addr)
116					: sizeof (struct in6_addr);
117
118	/* h_name */
119	len = strlen(he->h_name) + 1;
120	host->h_name = buffer;
121	if (host->h_name + len >= limit)
122		return (NSS_STR_PARSE_ERANGE);
123	(void) memcpy(host->h_name, he->h_name, len);
124	buffer += len;
125
126	/* h_addr_list */
127	if (af_type == AF_INET) {
128		addrp = (struct in_addr *)ROUND_DOWN(limit, sizeof (*addrp));
129		host->h_addr_list = (char **)
130				ROUND_UP(buffer, sizeof (char **));
131		ret = dns_netdb_aliases(he->h_addr_list, host->h_addr_list,
132			(char **)&addrp, DNS_ADDRLIST, &count, af_type);
133		if (ret != NSS_STR_PARSE_SUCCESS)
134			return (ret);
135		/* h_aliases */
136		host->h_aliases = host->h_addr_list + count + 1;
137		ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
138			(char **)&addrp, DNS_ALIASES, &count, af_type);
139	} else {
140		addrp6 = (struct in6_addr *)
141			ROUND_DOWN(limit, sizeof (*addrp6));
142		host->h_addr_list = (char **)
143			ROUND_UP(buffer, sizeof (char **));
144		if (he->h_addrtype == AF_INET && af_type == AF_INET6) {
145			ret = dns_netdb_aliases(he->h_addr_list,
146				host->h_addr_list, (char **)&addrp6,
147				DNS_MAPDLIST, &count, af_type);
148		} else {
149			ret = dns_netdb_aliases(he->h_addr_list,
150				host->h_addr_list, (char **)&addrp6,
151				DNS_ADDRLIST, &count, af_type);
152		}
153		if (ret != NSS_STR_PARSE_SUCCESS)
154			return (ret);
155		/* h_aliases */
156		host->h_aliases = host->h_addr_list + count + 1;
157		ret = dns_netdb_aliases(he->h_aliases, host->h_aliases,
158			(char **)&addrp6, DNS_ALIASES, &count, af_type);
159	}
160	if (ret == NSS_STR_PARSE_PARSE)
161		ret = NSS_STR_PARSE_SUCCESS;
162
163	return (ret);
164}
165
166/*
167 * Convert the hostent structure into string in the following
168 * format:
169 *
170 * IP-address official-host-name nicknames ...
171 *
172 * If more than one IP-addresses matches the official-host-name,
173 * the above line will be followed by:
174 * IP-address-1 official-host-name
175 * IP-address-2 official-host-name
176 * ...
177 *
178 * This is so that the str2hostent function in libnsl
179 * can convert the string back to the original hostent
180 * data.
181 */
182int
183ent2str(
184	struct hostent	*hp,
185	nss_XbyY_args_t *ap,
186	int		af_type)
187{
188	char		**p;
189	char		obuf[INET6_ADDRSTRLEN];
190	void		*addr;
191	struct in_addr	in4;
192	int		af;
193	int		n;
194	const char	*res;
195	char		**q;
196	int		l = ap->buf.buflen;
197	char		*s = ap->buf.buffer;
198
199	/*
200	 * for "hosts" lookup, we only want address type of
201	 * AF_INET. For "ipnodes", we can have both AF_INET
202	 * and AF_INET6.
203	 */
204	if (af_type == AF_INET && hp->h_addrtype != AF_INET)
205		return (NSS_STR_PARSE_PARSE);
206
207	for (p = hp->h_addr_list; *p != 0; p++) {
208
209		if (p != hp->h_addr_list) {
210			*s = '\n';
211			s++;
212			l--;
213		}
214
215		if (hp->h_addrtype == AF_INET6) {
216			/* LINTED: E_BAD_PTR_CAST_ALIGN */
217			if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) {
218				/* LINTED: E_BAD_PTR_CAST_ALIGN */
219				IN6_V4MAPPED_TO_INADDR((struct in6_addr *)*p,
220							&in4);
221				af = AF_INET;
222				addr = &in4;
223			} else {
224				af = AF_INET6;
225				addr = *p;
226			}
227		} else {
228			af = AF_INET;
229			addr = *p;
230		}
231		res = inet_ntop(af, addr, obuf, sizeof (obuf));
232		if (res == NULL)
233			return (NSS_STR_PARSE_PARSE);
234
235		if ((n = snprintf(s, l, "%s %s", res, hp->h_name)) >= l)
236			return (NSS_STR_PARSE_ERANGE);
237		l -= n;
238		s += n;
239		if (p == hp->h_addr_list) {
240			for (q = hp->h_aliases; q && *q; q++) {
241				if ((n = snprintf(s, l, " %s", *q)) >= l)
242					return (NSS_STR_PARSE_ERANGE);
243				l -= n;
244				s += n;
245			}
246		}
247	}
248
249	ap->returnlen = s - ap->buf.buffer;
250	return (NSS_STR_PARSE_SUCCESS);
251}
252
253nss_backend_t *
254_nss_dns_constr(dns_backend_op_t ops[], int n_ops)
255{
256	dns_backend_ptr_t	be;
257
258	if ((be = (dns_backend_ptr_t)malloc(sizeof (*be))) == 0)
259		return (0);
260
261	be->ops = ops;
262	be->n_ops = n_ops;
263	return ((nss_backend_t *)be);
264}
265
266
267/*
268 * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
269 *      nss2 get hosts/ipnodes with ttl backend DNS search engine.
270 *
271 * This API is given a pointer to a packed buffer, and the buffer size
272 * It's job is to perform the appropriate res_nsearch, extract the
273 * results and build a unmarshalled hosts/ipnodes result buffer.
274 * Additionally in the extended results a nssuint_t ttl is placed.
275 * This ttl is the lessor of the ttl's extracted from the result.
276 *
277 * ***Currently the first version of this API only performs simple
278 *    single res_nsearch lookups for with T_A or T_AAAA results.
279 *    Other searches are deferred to the generic API w/t ttls.
280 *
281 *    This function is not a generic res_* operation.  It only performs
282 *    a single T_A or T_AAAA lookups***
283 *
284 * RETURNS:  NSS_SUCCESS or NSS_ERROR
285 *	If an NSS_ERROR result is returned, nscd is expected
286 *	to resubmit the gethosts request using the old style
287 *	nsswitch lookup format.
288 */
289
290struct tsd_priv {
291	struct __res_state *statp;	/* dns state block */
292	union msg {
293		uchar_t	buf[NS_MAXMSG];	/* max legal DNS answer size */
294		HEADER	h;
295	} resbuf;
296	char aliases[NS_MAXMSG];	/* set of aliases */
297};
298
299static void ghttlcleanup(void *ptr)
300{
301	struct tsd_priv	*priv = (struct tsd_priv *)ptr;
302
303	if (priv) {
304		if (priv->statp != NULL) {
305			res_nclose(priv->statp);
306			free((void *)priv->statp);
307		}
308		free(ptr);
309	}
310}
311
312nss_status_t
313_nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
314{
315	/* nss buffer variables */
316	nss_pheader_t	*pbuf = (nss_pheader_t *)buffer;
317	nss_XbyY_args_t	arg;
318	char		*dbname;
319	int		dbop;
320	nss_status_t	sret;
321	size_t		bsize, blen;
322	char		*bptr;
323	/* resolver query variables */
324	static mutex_t		keylock;
325	static thread_key_t	key;
326	static int		once_per_keyname = 0;
327	struct tsd_priv		*tsd = NULL;
328	const char	*name;
329	int		qtype;
330	/* answer parsing variables */
331	HEADER		*hp;
332	uchar_t		*cp;	/* current location in message */
333	uchar_t		*bom;	/* start of message */
334	uchar_t		*eom;	/* end of message */
335	uchar_t		*eor;	/* end of record */
336	int		ancount, qdcount;
337	int		type, class;
338	nssuint_t	nttl, ttl, *pttl;	/* The purpose of this API */
339	int		n, ret;
340	const char	*np;
341	/* temporary buffers */
342	char		nbuf[INET6_ADDRSTRLEN];	/* address parser */
343	char		host[MAXHOSTNAMELEN];	/* result host name */
344	char		ans[MAXHOSTNAMELEN];	/* record name */
345	char		aname[MAXHOSTNAMELEN];	/* alias result (C_NAME) */
346	/* misc variables */
347	int		af;
348	char		*ap, *apc;
349	int		hlen, alen, iplen, len;
350
351	if (!once_per_keyname) {
352		(void) mutex_lock(&keylock);
353		if (!once_per_keyname) {
354			(void) thr_keycreate(&key, ghttlcleanup);
355			once_per_keyname++;
356		}
357		(void) mutex_unlock(&keylock);
358	}
359	(void) thr_getspecific(key, (void **)&tsd);
360	if (tsd == NULL) {
361		tsd = (struct tsd_priv *)calloc(1, sizeof (struct tsd_priv));
362		(void) thr_setspecific(key, (void *)tsd);
363		(void) thr_getspecific(key, (void **)&tsd);
364		tsd->statp = (struct __res_state *)
365				calloc(1, sizeof (struct __res_state));
366		if (tsd->statp == NULL)
367			return (NSS_ERROR);
368		if (res_ninit(tsd->statp) == -1) {
369			free(tsd->statp);
370			return (NSS_ERROR);
371		}
372	}
373	ap = apc = (char *)tsd->aliases;
374	alen = 0;
375	ttl = (nssuint_t)0xFFFFFFF;		/* start w/max, find smaller */
376
377	/* save space for ttl otherwise, why bother... */
378	bsize = pbuf->data_len - sizeof (nssuint_t);
379	bptr = (char *)buffer + pbuf->data_off;
380	blen = 0;
381	sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
382	if (sret != NSS_SUCCESS) {
383		return (NSS_ERROR);
384	}
385
386	if (ipnode) {
387		/* initially only handle the simple cases */
388		if (arg.key.ipnode.flags != 0)
389			return (NSS_ERROR);
390		name = arg.key.ipnode.name;
391		if (arg.key.ipnode.af_family == AF_INET6)
392			qtype = T_AAAA;
393		else
394			qtype = T_A;
395	} else {
396		name = arg.key.name;
397		qtype = T_A;
398	}
399	ret = res_nsearch(tsd->statp, name, C_IN, qtype,
400				tsd->resbuf.buf, NS_MAXMSG);
401	if (ret == -1) {
402		if (tsd->statp->res_h_errno == HOST_NOT_FOUND) {
403			pbuf->p_herrno = HOST_NOT_FOUND;
404			pbuf->p_status = NSS_NOTFOUND;
405			pbuf->data_len = 0;
406			return (NSS_NOTFOUND);
407		}
408		/* else lookup error - handle in general code */
409		return (NSS_ERROR);
410	}
411
412	cp = tsd->resbuf.buf;
413	hp = (HEADER *)&tsd->resbuf.h;
414	bom = cp;
415	eom = cp + ret;
416
417	ancount = ntohs(hp->ancount);
418	qdcount = ntohs(hp->qdcount);
419	cp += HFIXEDSZ;
420	if (qdcount != 1)
421		return (NSS_ERROR);
422	n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN);
423	if (n < 0) {
424		return (NSS_ERROR);
425	} else
426		hlen = strlen(host);
427	cp += n + QFIXEDSZ;
428	if (cp > eom)
429		return (NSS_ERROR);
430	while (ancount-- > 0 && cp < eom && blen < bsize) {
431		n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN);
432		if (n > 0) {
433			if (strncasecmp(host, ans, hlen) != 0)
434				return (NSS_ERROR);	/* spoof? */
435		}
436		cp += n;
437		/* bounds check */
438		type = ns_get16(cp);			/* type */
439		cp += INT16SZ;
440		class = ns_get16(cp);			/* class */
441		cp += INT16SZ;
442		nttl = (nssuint_t)ns_get32(cp);	/* ttl in sec */
443		if (nttl < ttl)
444			ttl = nttl;
445		cp += INT32SZ;
446		n = ns_get16(cp);			/* len */
447		cp += INT16SZ;
448		if (class != C_IN) {
449			cp += n;
450			continue;
451		}
452		eor = cp + n;
453		if (type == T_CNAME) {
454			/* add an alias to the alias list */
455			n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN);
456			if (n > 0) {
457				len = strlen(aname);
458				if (len > 0) {
459					/*
460					 * Just error out if there is an
461					 * attempted buffer overflow exploit
462					 * generic code will do a syslog
463					 */
464					if (alen + len + 2 > NS_MAXMSG)
465						return (NSS_ERROR);
466					*apc++ = ' ';
467					alen++;
468					(void) strlcpy(apc, aname, len + 1);
469					alen += len;
470					apc += len;
471				}
472			}
473			cp += n;
474			continue;
475		}
476		if (type != qtype) {
477			cp += n;
478			continue;
479		}
480		/* check data size */
481		if ((type == T_A && n != INADDRSZ) ||
482		    (type == T_AAAA && n != IN6ADDRSZ)) {
483			cp += n;
484			continue;
485		}
486		af = (type == T_A ? AF_INET : AF_INET6);
487		np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN);
488		if (np == NULL)
489			return (NSS_ERROR);
490		cp += n;
491		/* append IP host aliases to results */
492		iplen = strlen(np);
493		/* ip <SP> hostname [<SP>][aliases] */
494		len = iplen + 2 + hlen + alen;
495		if (alen > 0)
496			len++;
497		if (blen + len > bsize)
498			return (NSS_ERROR);
499		(void) strlcpy(bptr, np, bsize - blen);
500		blen += iplen;
501		bptr += iplen;
502		*bptr++ = ' ';
503		blen++;
504		(void) strlcpy(bptr, host, bsize - blen);
505		blen += hlen;
506		bptr += hlen;
507		if (alen > 0) {
508			*bptr++ = ' ';
509			blen++;
510			(void) strlcpy(bptr, ap, bsize - blen);
511			blen += alen;
512			bptr += alen;
513		}
514		*bptr++ = '\n';
515		blen++;
516	}
517	/* Presumably the buffer is now filled. */
518	len = ROUND_UP(blen, sizeof (nssuint_t));
519	/* still room? */
520	if (len + sizeof (nssuint_t) > pbuf->data_len) {
521		/* sigh, no, what happened? */
522		return (NSS_ERROR);
523	}
524	pbuf->ext_off = pbuf->data_off + len;
525	pbuf->ext_len = sizeof (nssuint_t);
526	pbuf->data_len = blen;
527	pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
528	*pttl = ttl;
529	return (NSS_SUCCESS);
530}
531