xref: /illumos-gate/usr/src/lib/libresolv2/common/irs/dns_nw.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 /*
7  * Copyright (c) 1996-1999 by Internet Software Consortium.
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20  * SOFTWARE.
21  */
22 
23 #pragma ident	"%Z%%M%	%I%	%E% SMI"
24 
25 #if defined(LIBC_SCCS) && !defined(lint)
26 static const char rcsid[] = "$Id: dns_nw.c,v 1.25 2002/07/18 02:07:43 marka Exp $";
27 #endif /* LIBC_SCCS and not lint */
28 
29 /* Imports. */
30 
31 #include "port_before.h"
32 
33 #include <sys/param.h>
34 #include <sys/socket.h>
35 
36 #include <netinet/in.h>
37 #include <arpa/inet.h>
38 #include <arpa/nameser.h>
39 
40 #include <ctype.h>
41 #include <errno.h>
42 #include <netdb.h>
43 #include <resolv.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 
48 #include <isc/memcluster.h>
49 #include <irs.h>
50 
51 #include "port_after.h"
52 
53 #include "irs_p.h"
54 #include "dns_p.h"
55 
56 #ifdef SPRINTF_CHAR
57 # define SPRINTF(x) strlen(sprintf/**/x)
58 #else
59 # define SPRINTF(x) sprintf x
60 #endif
61 
62 /* Definitions. */
63 
64 #define	MAXALIASES	35
65 
66 #define	MAXPACKET	(64*1024)
67 
68 struct pvt {
69 	struct nwent	net;
70 	char *		ali[MAXALIASES];
71 	char		buf[BUFSIZ+1];
72 	struct __res_state * res;
73 	void		(*free_res)(void *);
74 };
75 
76 typedef union {
77 	long	al;
78 	char	ac;
79 } align;
80 
81 enum by_what { by_addr, by_name };
82 
83 /* Forwards. */
84 
85 static void		nw_close(struct irs_nw *);
86 static struct nwent *	nw_byname(struct irs_nw *, const char *, int);
87 static struct nwent *	nw_byaddr(struct irs_nw *, void *, int, int);
88 static struct nwent *	nw_next(struct irs_nw *);
89 static void		nw_rewind(struct irs_nw *);
90 static void		nw_minimize(struct irs_nw *);
91 static struct __res_state * nw_res_get(struct irs_nw *this);
92 static void		nw_res_set(struct irs_nw *this,
93 				   struct __res_state *res,
94 				   void (*free_res)(void *));
95 
96 static struct nwent *	get1101byaddr(struct irs_nw *, u_char *, int);
97 static struct nwent *	get1101byname(struct irs_nw *, const char *);
98 static struct nwent *	get1101answer(struct irs_nw *,
99 				      u_char *ansbuf, int anslen,
100 				      enum by_what by_what,
101 				      int af, const char *name,
102 				      const u_char *addr, int addrlen);
103 static struct nwent *	get1101mask(struct irs_nw *this, struct nwent *);
104 static int		make1101inaddr(const u_char *, int, char *, int);
105 static void		normalize_name(char *name);
106 static int		init(struct irs_nw *this);
107 
108 /* Exports. */
109 
110 struct irs_nw *
111 irs_dns_nw(struct irs_acc *this) {
112 	struct irs_nw *nw;
113 	struct pvt *pvt;
114 
115 	UNUSED(this);
116 
117 	if (!(pvt = memget(sizeof *pvt))) {
118 		errno = ENOMEM;
119 		return (NULL);
120 	}
121 	memset(pvt, 0, sizeof *pvt);
122 	if (!(nw = memget(sizeof *nw))) {
123 		memput(pvt, sizeof *pvt);
124 		errno = ENOMEM;
125 		return (NULL);
126 	}
127 	memset(nw, 0x5e, sizeof *nw);
128 	nw->private = pvt;
129 	nw->close = nw_close;
130 	nw->byname = nw_byname;
131 	nw->byaddr = nw_byaddr;
132 	nw->next = nw_next;
133 	nw->rewind = nw_rewind;
134 	nw->minimize = nw_minimize;
135 	nw->res_get = nw_res_get;
136 	nw->res_set = nw_res_set;
137 	return (nw);
138 }
139 
140 /* Methods. */
141 
142 static void
143 nw_close(struct irs_nw *this) {
144 	struct pvt *pvt = (struct pvt *)this->private;
145 
146 	nw_minimize(this);
147 
148 	if (pvt->res && pvt->free_res)
149 		(*pvt->free_res)(pvt->res);
150 
151 	memput(pvt, sizeof *pvt);
152 	memput(this, sizeof *this);
153 }
154 
155 static struct nwent *
156 nw_byname(struct irs_nw *this, const char *name, int af) {
157 	struct pvt *pvt = (struct pvt *)this->private;
158 
159 	if (init(this) == -1)
160 		return (NULL);
161 
162 	switch (af) {
163 	case AF_INET:
164 		return (get1101byname(this, name));
165 	default:
166 		(void)NULL;
167 	}
168 	RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
169 	errno = EAFNOSUPPORT;
170 	return (NULL);
171 }
172 
173 static struct nwent *
174 nw_byaddr(struct irs_nw *this, void *net, int len, int af) {
175 	struct pvt *pvt = (struct pvt *)this->private;
176 
177 	if (init(this) == -1)
178 		return (NULL);
179 
180 	switch (af) {
181 	case AF_INET:
182 		return (get1101byaddr(this, net, len));
183 	default:
184 		(void)NULL;
185 	}
186 	RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
187 	errno = EAFNOSUPPORT;
188 	return (NULL);
189 }
190 
191 static struct nwent *
192 nw_next(struct irs_nw *this) {
193 
194 	UNUSED(this);
195 
196 	return (NULL);
197 }
198 
199 static void
200 nw_rewind(struct irs_nw *this) {
201 	UNUSED(this);
202 	/* NOOP */
203 }
204 
205 static void
206 nw_minimize(struct irs_nw *this) {
207 	struct pvt *pvt = (struct pvt *)this->private;
208 
209 	if (pvt->res)
210 		res_nclose(pvt->res);
211 }
212 
213 static struct __res_state *
214 nw_res_get(struct irs_nw *this) {
215 	struct pvt *pvt = (struct pvt *)this->private;
216 
217 	if (!pvt->res) {
218 		struct __res_state *res;
219 		res = (struct __res_state *)malloc(sizeof *res);
220 		if (!res) {
221 			errno = ENOMEM;
222 			return (NULL);
223 		}
224 		memset(res, 0, sizeof *res);
225 		nw_res_set(this, res, free);
226 	}
227 
228 	return (pvt->res);
229 }
230 
231 static void
232 nw_res_set(struct irs_nw *this, struct __res_state *res,
233 		void (*free_res)(void *)) {
234 	struct pvt *pvt = (struct pvt *)this->private;
235 
236 	if (pvt->res && pvt->free_res) {
237 		res_nclose(pvt->res);
238 		(*pvt->free_res)(pvt->res);
239 	}
240 
241 	pvt->res = res;
242 	pvt->free_res = free_res;
243 }
244 
245 /* Private. */
246 
247 static struct nwent *
248 get1101byname(struct irs_nw *this, const char *name) {
249 	struct pvt *pvt = (struct pvt *)this->private;
250 	u_char *ansbuf;
251 	int anslen;
252 	struct nwent *result;
253 
254 	ansbuf = memget(MAXPACKET);
255 	if (ansbuf == NULL) {
256 		errno = ENOMEM;
257 		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
258 		return (NULL);
259 	}
260 	anslen = res_nsearch(pvt->res, name, C_IN, T_PTR, ansbuf, MAXPACKET);
261 	if (anslen < 0) {
262 		memput(ansbuf, MAXPACKET);
263 		return (NULL);
264 	}
265 	result = get1101mask(this, get1101answer(this, ansbuf, anslen, by_name,
266 						 AF_INET, name, NULL, 0));
267 	memput(ansbuf, MAXPACKET);
268 	return (result);
269 }
270 
271 static struct nwent *
272 get1101byaddr(struct irs_nw *this, u_char *net, int len) {
273 	struct pvt *pvt = (struct pvt *)this->private;
274 	char qbuf[sizeof "255.255.255.255.in-addr.arpa"];
275 	struct nwent *result;
276 	u_char *ansbuf;
277 	int anslen;
278 
279 	if (len < 1 || len > 32) {
280 		errno = EINVAL;
281 		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
282 		return (NULL);
283 	}
284 	if (make1101inaddr(net, len, qbuf, sizeof qbuf) < 0)
285 		return (NULL);
286 	ansbuf = memget(MAXPACKET);
287 	if (ansbuf == NULL) {
288 		errno = ENOMEM;
289 		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
290 		return (NULL);
291 	}
292 	anslen = res_nquery(pvt->res, qbuf, C_IN, T_PTR, ansbuf, MAXPACKET);
293 	if (anslen < 0) {
294 		memput(ansbuf, MAXPACKET);
295 		return (NULL);
296 	}
297 	result = get1101mask(this, get1101answer(this, ansbuf, anslen, by_addr,
298 						 AF_INET, NULL, net, len));
299 	memput(ansbuf, MAXPACKET);
300 	return (result);
301 }
302 
303 static struct nwent *
304 get1101answer(struct irs_nw *this,
305 	      u_char *ansbuf, int anslen, enum by_what by_what,
306 	      int af, const char *name, const u_char *addr, int addrlen)
307 {
308 	struct pvt *pvt = (struct pvt *)this->private;
309 	int type, class, ancount, qdcount, haveanswer;
310 	char *bp, *ep, **ap;
311 	u_char *cp, *eom;
312 	HEADER *hp;
313 
314 	/* Initialize, and parse header. */
315 	eom = ansbuf + anslen;
316 	if (ansbuf + HFIXEDSZ > eom) {
317 		RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
318 		return (NULL);
319 	}
320 	hp = (HEADER *)ansbuf;
321 	cp = ansbuf + HFIXEDSZ;
322 	qdcount = ntohs(hp->qdcount);
323 	while (qdcount-- > 0) {
324 		int n = dn_skipname(cp, eom);
325 		cp += n + QFIXEDSZ;
326 		if (n < 0 || cp > eom) {
327 			RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
328 			return (NULL);
329 		}
330 	}
331 	ancount = ntohs(hp->ancount);
332 	if (!ancount) {
333 		if (hp->aa)
334 			RES_SET_H_ERRNO(pvt->res, HOST_NOT_FOUND);
335 		else
336 			RES_SET_H_ERRNO(pvt->res, TRY_AGAIN);
337 		return (NULL);
338 	}
339 
340 	/* Prepare a return structure. */
341 	bp = pvt->buf;
342 	ep = pvt->buf + sizeof (pvt->buf);
343 	pvt->net.n_name = NULL;
344 	pvt->net.n_aliases = pvt->ali;
345 	pvt->net.n_addrtype = af;
346 	pvt->net.n_addr = NULL;
347 	pvt->net.n_length = addrlen;
348 
349 	/* Save input key if given. */
350 	switch (by_what) {
351 	case by_name:
352 		if (name != NULL) {
353 			int n = strlen(name) + 1;
354 
355 			if (n > (ep - bp)) {
356 				RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
357 				return (NULL);
358 			}
359 #ifdef HAVE_STRLCPY
360 			strlcpy(bp, name, ep - bp);
361 			pvt->net.n_name = bp;
362 #else
363 			pvt->net.n_name = strcpy(bp, name);
364 #endif
365 			bp += n;
366 		}
367 		break;
368 	case by_addr:
369 		if (addr != NULL && addrlen != 0) {
370 			int n = addrlen / 8 + ((addrlen % 8) != 0);
371 
372 			if (INADDRSZ > (ep - bp)) {
373 				RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
374 				return (NULL);
375 			}
376 			memset(bp, 0, INADDRSZ);
377 			memcpy(bp, addr, n);
378 			pvt->net.n_addr = bp;
379 			bp += INADDRSZ;
380 		}
381 		break;
382 	default:
383 		abort();
384 	}
385 
386 	/* Parse the answer, collect aliases. */
387 	ap = pvt->ali;
388 	haveanswer = 0;
389 	while (--ancount >= 0 && cp < eom) {
390 		int n = dn_expand(ansbuf, eom, cp, bp, ep - bp);
391 
392 		cp += n;		/* Owner */
393 		if (n < 0 || !maybe_dnok(pvt->res, bp) ||
394 		    cp + 3 * INT16SZ + INT32SZ > eom) {
395 			RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
396 			return (NULL);
397 		}
398 		GETSHORT(type, cp);	/* Type */
399 		GETSHORT(class, cp);	/* Class */
400 		cp += INT32SZ;		/* TTL */
401 		GETSHORT(n, cp);	/* RDLENGTH */
402 		if (class == C_IN && type == T_PTR) {
403 			int nn;
404 
405 			nn = dn_expand(ansbuf, eom, cp, bp, ep - bp);
406 			if (nn < 0 || !maybe_hnok(pvt->res, bp) || nn != n) {
407 				RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
408 				return (NULL);
409 			}
410 			normalize_name(bp);
411 			switch (by_what) {
412 			case by_addr: {
413 				if (pvt->net.n_name == NULL)
414 					pvt->net.n_name = bp;
415 				else if (ns_samename(pvt->net.n_name, bp) == 1)
416 					break;
417 				else
418 					*ap++ = bp;
419 				nn = strlen(bp) + 1;
420 				bp += nn;
421 				haveanswer++;
422 				break;
423 			    }
424 			case by_name: {
425 				u_int b1, b2, b3, b4;
426 
427 				if (pvt->net.n_addr != NULL ||
428 				    sscanf(bp, "%u.%u.%u.%u.in-addr.arpa",
429 					   &b1, &b2, &b3, &b4) != 4)
430 					break;
431 				if ((ep - bp) < INADDRSZ) {
432 					RES_SET_H_ERRNO(pvt->res, NO_RECOVERY);
433 					return (NULL);
434 				}
435 				pvt->net.n_addr = bp;
436 				*bp++ = b4;
437 				*bp++ = b3;
438 				*bp++ = b2;
439 				*bp++ = b1;
440 				pvt->net.n_length = INADDRSZ * 8;
441 				haveanswer++;
442 			    }
443 			}
444 		}
445 		cp += n;		/* RDATA */
446 	}
447 	if (!haveanswer) {
448 		RES_SET_H_ERRNO(pvt->res, TRY_AGAIN);
449 		return (NULL);
450 	}
451 	*ap = NULL;
452 
453 	return (&pvt->net);
454 }
455 
456 static struct nwent *
457 get1101mask(struct irs_nw *this, struct nwent *nwent) {
458 	struct pvt *pvt = (struct pvt *)this->private;
459 	char qbuf[sizeof "255.255.255.255.in-addr.arpa"], owner[MAXDNAME];
460 	int anslen, type, class, ancount, qdcount;
461 	u_char *ansbuf, *cp, *eom;
462 	HEADER *hp;
463 
464 	if (!nwent)
465 		return (NULL);
466 	if (make1101inaddr(nwent->n_addr, nwent->n_length, qbuf, sizeof qbuf)
467 	    < 0) {
468 		/* "First, do no harm." */
469 		return (nwent);
470 	}
471 
472 	ansbuf = memget(MAXPACKET);
473 	if (ansbuf == NULL) {
474 		errno = ENOMEM;
475 		RES_SET_H_ERRNO(pvt->res, NETDB_INTERNAL);
476 		return (NULL);
477 	}
478 	/* Query for the A RR that would hold this network's mask. */
479 	anslen = res_nquery(pvt->res, qbuf, C_IN, T_A, ansbuf, MAXPACKET);
480 	if (anslen < HFIXEDSZ) {
481 		memput(ansbuf, MAXPACKET);
482 		return (nwent);
483 	}
484 
485 	/* Initialize, and parse header. */
486 	hp = (HEADER *)ansbuf;
487 	cp = ansbuf + HFIXEDSZ;
488 	eom = ansbuf + anslen;
489 	qdcount = ntohs(hp->qdcount);
490 	while (qdcount-- > 0) {
491 		int n = dn_skipname(cp, eom);
492 		cp += n + QFIXEDSZ;
493 		if (n < 0 || cp > eom) {
494 			memput(ansbuf, MAXPACKET);
495 			return (nwent);
496 		}
497 	}
498 	ancount = ntohs(hp->ancount);
499 
500 	/* Parse the answer, collect aliases. */
501 	while (--ancount >= 0 && cp < eom) {
502 		int n = dn_expand(ansbuf, eom, cp, owner, sizeof owner);
503 
504 		if (n < 0 || !maybe_dnok(pvt->res, owner))
505 			break;
506 		cp += n;		/* Owner */
507 		if (cp + 3 * INT16SZ + INT32SZ > eom)
508 			break;
509 		GETSHORT(type, cp);	/* Type */
510 		GETSHORT(class, cp);	/* Class */
511 		cp += INT32SZ;		/* TTL */
512 		GETSHORT(n, cp);	/* RDLENGTH */
513 		if (cp + n > eom)
514 			break;
515 		if (n == INADDRSZ && class == C_IN && type == T_A &&
516 		    ns_samename(qbuf, owner) == 1) {
517 			/* This A RR indicates the actual netmask. */
518 			int nn, mm;
519 
520 			nwent->n_length = 0;
521 			for (nn = 0; nn < INADDRSZ; nn++)
522 				for (mm = 7; mm >= 0; mm--)
523 					if (cp[nn] & (1 << mm))
524 						nwent->n_length++;
525 					else
526 						break;
527 		}
528 		cp += n;		/* RDATA */
529 	}
530 	memput(ansbuf, MAXPACKET);
531 	return (nwent);
532 }
533 
534 static int
535 make1101inaddr(const u_char *net, int bits, char *name, int size) {
536 	int n, m;
537 	char *ep;
538 
539 	ep = name + size;
540 
541 	/* Zero fill any whole bytes left out of the prefix. */
542 	for (n = (32 - bits) / 8; n > 0; n--) {
543 		if (ep - name < (int)(sizeof "0."))
544 			goto emsgsize;
545 		m = SPRINTF((name, "0."));
546 		name += m;
547 	}
548 
549 	/* Format the partial byte, if any, within the prefix. */
550 	if ((n = bits % 8) != 0) {
551 		if (ep - name < (int)(sizeof "255."))
552 			goto emsgsize;
553 		m = SPRINTF((name, "%u.",
554 			     net[bits / 8] & ~((1 << (8 - n)) - 1)));
555 		name += m;
556 	}
557 
558 	/* Format the whole bytes within the prefix. */
559 	for (n = bits / 8; n > 0; n--) {
560 		if (ep - name < (int)(sizeof "255."))
561 			goto emsgsize;
562 		m = SPRINTF((name, "%u.", net[n - 1]));
563 		name += m;
564 	}
565 
566 	/* Add the static text. */
567 	if (ep - name < (int)(sizeof "in-addr.arpa"))
568 		goto emsgsize;
569 	(void) SPRINTF((name, "in-addr.arpa"));
570 	return (0);
571 
572  emsgsize:
573 	errno = EMSGSIZE;
574 	return (-1);
575 }
576 
577 static void
578 normalize_name(char *name) {
579 	char *t;
580 
581 	/* Make lower case. */
582 	for (t = name; *t; t++)
583 		if (isascii((unsigned char)*t) && isupper((unsigned char)*t))
584 			*t = tolower(*t);
585 
586 	/* Remove trailing dots. */
587 	while (t > name && t[-1] == '.')
588 		*--t = '\0';
589 }
590 
591 static int
592 init(struct irs_nw *this) {
593 	struct pvt *pvt = (struct pvt *)this->private;
594 
595 	if (!pvt->res && !nw_res_get(this))
596 		return (-1);
597 	if (((pvt->res->options & RES_INIT) == 0) &&
598 	    res_ninit(pvt->res) == -1)
599 		return (-1);
600 	return (0);
601 }
602