1 /*
2  * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2010
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that: (1) source code distributions
7  * retain the above copyright notice and this paragraph in its entirety, (2)
8  * distributions including binary code include the above copyright notice and
9  * this paragraph in its entirety in the documentation or other materials
10  * provided with the distribution, and (3) all advertising materials mentioning
11  * features or use of this software display the following acknowledgement:
12  * ``This product includes software developed by the University of California,
13  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14  * the University nor the names of its contributors may be used to endorse
15  * or promote products derived from this software without specific prior
16  * written permission.
17  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20  */
21 #ifndef lint
22 static const char copyright[] =
23     "@(#) Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2010\n\
24 The Regents of the University of California.  All rights reserved.\n";
25 static const char rcsid[] =
26     "@(#) $Id: nslint.c 256 2010-03-06 04:14:09Z leres $ (LBL)";
27 #endif
28 /*
29  * nslint - perform consistency checks on dns files
30  */
31 
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <sys/socket.h>
35 
36 #include <netinet/in.h>
37 
38 #include <arpa/inet.h>
39 
40 #include <ctype.h>
41 #include <errno.h>
42 #ifdef HAVE_FCNTL_H
43 #include <fcntl.h>
44 #endif
45 #ifdef HAVE_MEMORY_H
46 #include <memory.h>
47 #endif
48 #include <netdb.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <time.h>
53 #include <unistd.h>
54 
55 #include "savestr.h"
56 #include "version.h"
57 
58 #include "gnuc.h"
59 #ifdef HAVE_OS_PROTO_H
60 #include "os-proto.h"
61 #endif
62 
63 #define NSLINTBOOT "nslint.boot"	/* default nslint.boot file */
64 #define NSLINTCONF "nslint.conf"	/* default nslint.conf file */
65 
66 /* Is the string just a dot by itself? */
67 #define CHECKDOT(p) (p[0] == '.' && p[1] == '\0')
68 
69 /* Address (network order) */
70 struct addr {
71 	u_int family;
72 	union {
73 		struct in_addr _a_addr4;
74 		struct in6_addr _a_addr6;
75 	} addr;
76 };
77 #define a_addr4 addr._a_addr4.s_addr
78 #define a_addr6 addr._a_addr6.s6_addr
79 
80 /* Network */
81 struct network {
82 	u_int family;
83 	union {
84 		struct in_addr _n_addr4;
85 		struct in6_addr _n_addr6;
86 	} addr;
87 	union {
88 		struct in_addr _n_mask4;
89 		struct in6_addr _n_mask6;
90 	} mask;
91 };
92 #define n_addr4 addr._n_addr4.s_addr
93 #define n_mask4 mask._n_mask4.s_addr
94 #define n_addr6 addr._n_addr6.s6_addr
95 #define n_mask6 mask._n_mask6.s6_addr
96 
97 /* Item struct */
98 struct item {
99 	char *host;		/* pointer to hostname */
100 	struct addr addr;	/* ip address */
101 	u_int ttl;		/* ttl of A records */
102 	int records;		/* resource records seen */
103 	int flags;		/* flags word */
104 };
105 
106 /* Ignored zone struct */
107 struct ignoredzone {
108 	char *zone;		/* zone name */
109 	int len;		/* length of zone */
110 };
111 
112 /* Resource records seen */
113 #define REC_A		0x0001
114 #define REC_AAAA	0x0002
115 #define REC_PTR		0x0004
116 #define REC_WKS		0x0008
117 #define REC_HINFO	0x0010
118 #define REC_MX		0x0020
119 #define REC_CNAME	0x0040
120 #define REC_NS		0x0080
121 #define REC_SOA		0x0100
122 #define REC_RP		0x0200
123 #define REC_TXT		0x0400
124 #define REC_SRV		0x0800
125 
126 /* These aren't real records */
127 #define REC_OTHER	0x1000
128 #define REC_REF		0x2000
129 #define REC_UNKNOWN	0x4000
130 
131 /* resource record types for parsing */
132 enum rrtype {
133 	RR_UNDEF = 0,
134 	RR_A,
135 	RR_AAAA,
136 	RR_ALLOWDUPA,
137 	RR_CNAME,
138 	RR_DNSKEY,
139 	RR_HINFO,
140 	RR_MX,
141 	RR_NS,
142 	RR_PTR,
143 	RR_RP,
144 	RR_SOA,
145 	RR_SRV,
146 	RR_TXT,
147 	RR_WKS,
148 	RR_RRSIG,
149 	RR_NSEC,
150 };
151 
152 /* Test for records we want to map to REC_OTHER */
153 #define MASK_TEST_REC (REC_WKS | REC_HINFO | \
154     REC_MX | REC_SOA | REC_RP | REC_TXT | REC_SRV | REC_UNKNOWN)
155 
156 /* Mask away records we don't care about in the final processing to REC_OTHER */
157 #define MASK_CHECK_REC \
158     (REC_A | REC_AAAA | REC_PTR | REC_CNAME | REC_REF | REC_OTHER)
159 
160 /* Test for records we want to check for duplicate name detection */
161 #define MASK_TEST_DUP \
162     (REC_A | REC_AAAA | REC_HINFO | REC_CNAME)
163 
164 /* Flags */
165 #define FLG_SELFMX	0x001	/* mx record refers to self */
166 #define FLG_MXREF	0x002	/* this record referred to by a mx record */
167 #define FLG_SMTPWKS	0x004	/* saw wks with smtp/tcp */
168 #define FLG_ALLOWDUPA	0x008	/* allow duplicate a records */
169 
170 /* doconf() and doboot() flags */
171 #define CONF_MUSTEXIST	0x001	/* fatal for files to not exist */
172 #define CONF_NOZONE	0x002	/* do not parse zone files */
173 
174 /* Test for smtp problems */
175 #define MASK_TEST_SMTP \
176     (FLG_SELFMX | FLG_SMTPWKS)
177 
178 #define ITEMSIZE (1 << 17)	/* power of two */
179 
180 struct	item items[ITEMSIZE];
181 int	itemcnt;		/* count of items */
182 
183 /* Hostname string storage */
184 #define STRSIZE 8192;		/* size to malloc when more space is needed */
185 char	*strptr;		/* pointer to string pool */
186 int	strsize;		/* size of space left in pool */
187 
188 int	debug;
189 int	errors;
190 #ifdef __FreeBSD__
191 char	*bootfile = "/etc/namedb/named.boot";
192 char	*conffile = "/etc/namedb/named.conf";
193 #else
194 char	*bootfile = "/etc/namedb/named.boot";
195 char	*conffile = "/etc/namedb/named.conf";
196 #endif
197 char	*nslintboot;
198 char	*nslintconf;
199 char	*prog;
200 char	*cwd = ".";
201 
202 static struct network *netlist;
203 static u_int netlistsize;	/* size of array */
204 static u_int netlistcnt;	/* next free element */
205 
206 char **protoserv;		/* valid protocol/service names */
207 int protoserv_init;
208 int protoserv_last;
209 int protoserv_len;
210 
211 static char inaddr[] = ".in-addr.arpa.";
212 static char inaddr6[] = ".ip6.arpa.";
213 
214 /* XXX should be dynamic */
215 static struct ignoredzone ignoredzones[10];
216 static int numignoredzones = 0;
217 #define SIZEIGNOREDZONES (sizeof(ignoredzones) / sizeof(ignoredzones[0]))
218 
219 /* SOA record */
220 #define SOA_SERIAL	0
221 #define SOA_REFRESH	1
222 #define SOA_RETRY	2
223 #define SOA_EXPIRE	3
224 #define SOA_MINIMUM	4
225 
226 static u_int soaval[5];
227 static int nsoaval;
228 #define NSOAVAL (sizeof(soaval) / sizeof(soaval[0]))
229 
230 /* Forwards */
231 void add_domain(char *, const char *);
232 const char *addr2str(struct addr *);
233 int checkaddr(const char *);
234 int checkdots(const char *);
235 void checkdups(struct item *, int);
236 int checkignoredzone(const char *);
237 int checkserv(const char *, char **p);
238 int checkwks(FILE *, char *, int *, char **);
239 int cmpaddr(const void *, const void *);
240 int cmpitemaddr(const void *, const void *);
241 int cmpitemhost(const void *, const void *);
242 int cmpnetwork(const void *, const void *);
243 void doboot(const char *, int);
244 void doconf(const char *, int);
245 const char *extractaddr(const char *, struct addr *);
246 const char *extractnetwork(const char *, struct network *);
247 struct network *findnetwork(struct addr *);
248 void initprotoserv(void);
249 int isip6arpa(const char *);
250 int main(int, char **);
251 int maskwidth(struct network *);
252 const char *network2str(struct network *);
253 void nslint(void);
254 const char *parsenetwork(const char *);
255 const char *parseptr(const char *, struct addr *);
256 char *parsequoted(char *);
257 int parserrsig(const char *, char **);
258 int parsesoa(const char *, char **);
259 void process(const char *, const char *, const char *);
260 int rfc1034host(const char *, int);
261 enum rrtype txt2rrtype(const char *);
262 int samesubnet(struct addr *, struct addr *, struct network *);
263 void setmaskwidth(u_int w, struct network *);
264 int updateitem(const char *, struct addr *, int, u_int, int);
265 void usage(void) __attribute__((noreturn));
266 
267 extern	char *optarg;
268 extern	int optind, opterr;
269 
270 int
main(int argc,char ** argv)271 main(int argc, char **argv)
272 {
273 	char *cp;
274 	int op, donamedboot, donamedconf;
275 
276 	if ((cp = strrchr(argv[0], '/')) != NULL)
277 		prog = cp + 1;
278 	else
279 		prog = argv[0];
280 
281 	donamedboot = 0;
282 	donamedconf = 0;
283 	while ((op = getopt(argc, argv, "b:c:B:C:d")) != -1)
284 		switch (op) {
285 
286 		case 'b':
287 			bootfile = optarg;
288 			++donamedboot;
289 			break;
290 
291 		case 'c':
292 			conffile = optarg;
293 			++donamedconf;
294 			break;
295 
296 		case 'B':
297 			nslintboot = optarg;
298 			++donamedboot;
299 			break;
300 
301 		case 'C':
302 			nslintconf = optarg;
303 			++donamedconf;
304 			break;
305 
306 		case 'd':
307 			++debug;
308 			break;
309 
310 		default:
311 			usage();
312 		}
313 	if (optind != argc || (donamedboot && donamedconf))
314 		usage();
315 
316 	/* Find config file if not manually specified */
317 	if (!donamedboot && !donamedconf) {
318 		if (access(conffile, R_OK) >= 0)
319 			++donamedconf;
320 		if (access(bootfile, R_OK) >= 0)
321 			++donamedboot;
322 
323 		if (donamedboot && donamedconf) {
324 			fprintf(stderr,
325 			    "%s: nslint: both %s and %s exist; use -b or -c\n",
326 			    prog, conffile, bootfile);
327 			exit(1);
328 		}
329 	}
330 
331 	if (donamedboot) {
332 		doboot(bootfile, CONF_MUSTEXIST | CONF_NOZONE);
333 		if (nslintboot != NULL)
334 			doboot(nslintboot, CONF_MUSTEXIST);
335 		else
336 			doboot(NSLINTBOOT, 0);
337 		doboot(bootfile, CONF_MUSTEXIST);
338 	} else {
339 		doconf(conffile, CONF_MUSTEXIST | CONF_NOZONE);
340 		if (nslintconf != NULL)
341 			doconf(nslintconf, CONF_MUSTEXIST);
342 		else
343 			doconf(NSLINTCONF, 0);
344 		doconf(conffile, CONF_MUSTEXIST);
345 	}
346 
347 	/* Sort network list */
348 	if (netlistcnt > 0)
349 		qsort(netlist, netlistcnt, sizeof(netlist[0]), cmpnetwork);
350 
351 	nslint();
352 	exit (errors != 0);
353 }
354 
355 /* add domain if necessary */
356 void
add_domain(char * name,const char * domain)357 add_domain(char *name, const char *domain)
358 {
359 	char *cp;
360 
361 	/* Kill trailing white space and convert to lowercase */
362 	for (cp = name; *cp != '\0' && !isspace(*cp); ++cp)
363 		if (isupper(*cp))
364 			*cp = tolower(*cp);
365 	*cp-- = '\0';
366 	/* If necessary, append domain */
367 	if (cp >= name && *cp++ != '.') {
368 		if (*domain != '.')
369 			*cp++ = '.';
370 		(void)strcpy(cp, domain);
371 	}
372 	/* XXX should we insure a trailing dot? */
373 }
374 
375 const char *
addr2str(struct addr * ap)376 addr2str(struct addr *ap)
377 {
378 	struct network net;
379 
380 	memset(&net, 0, sizeof(net));
381 	net.family = ap->family;
382 	switch (ap->family) {
383 
384 	case AF_INET:
385 		net.n_addr4 = ap->a_addr4;
386 		setmaskwidth(32, &net);
387 		break;
388 
389 	case AF_INET6:
390 		memmove(net.n_addr6, &ap->a_addr6, sizeof(ap->a_addr6));
391 		setmaskwidth(128, &net);
392 		break;
393 
394 	default:
395 		return ("<nil>");
396 	}
397 	return (network2str(&net));
398 }
399 
400 /*
401  * Returns true if name is really an ip address.
402  */
403 int
checkaddr(const char * name)404 checkaddr(const char *name)
405 {
406 	struct in_addr addr;
407 
408 	return (inet_pton(AF_INET, name, (char *)&addr));
409 }
410 
411 /*
412  * Returns true if name contains a dot but not a trailing dot.
413  * Special case: allow a single dot if the second part is not one
414  * of the 3 or 4 letter top level domains or is any 2 letter TLD
415  */
416 int
checkdots(const char * name)417 checkdots(const char *name)
418 {
419 	const char *cp, *cp2;
420 
421 	if ((cp = strchr(name, '.')) == NULL)
422 		return (0);
423 	cp2 = name + strlen(name) - 1;
424 	if (cp2 >= name && *cp2 == '.')
425 		return (0);
426 
427 	/* Return true of more than one dot*/
428 	++cp;
429 	if (strchr(cp, '.') != NULL)
430 		return (1);
431 
432 	if (strlen(cp) == 2 ||
433 	    strcasecmp(cp, "gov") == 0 ||
434 	    strcasecmp(cp, "edu") == 0 ||
435 	    strcasecmp(cp, "com") == 0 ||
436 	    strcasecmp(cp, "net") == 0 ||
437 	    strcasecmp(cp, "org") == 0 ||
438 	    strcasecmp(cp, "mil") == 0 ||
439 	    strcasecmp(cp, "int") == 0 ||
440 	    strcasecmp(cp, "nato") == 0 ||
441 	    strcasecmp(cp, "arpa") == 0)
442 		return (1);
443 	return (0);
444 }
445 
446 /* Records we use to detect duplicates */
447 static struct duprec {
448 	int record;
449 	char *name;
450 } duprec[] = {
451 	{ REC_A, "a" },
452 	{ REC_AAAA, "aaaa" },
453 	{ REC_HINFO, "hinfo" },
454 	{ REC_CNAME, "cname" },
455 	{ 0, NULL },
456 };
457 
458 void
checkdups(struct item * ip,int records)459 checkdups(struct item *ip, int records)
460 {
461 	struct duprec *dp;
462 
463 	records &= (ip->records & MASK_TEST_DUP);
464 	if (records == 0)
465 		return;
466 	for (dp = duprec; dp->name != NULL; ++dp)
467 		if ((records & dp->record) != 0) {
468 			++errors;
469 			fprintf(stderr, "%s: multiple \"%s\" records for %s\n",
470 			    prog, dp->name, ip->host);
471 			records &= ~dp->record;
472 		}
473 	if (records != 0)
474 		fprintf(stderr, "%s: checkdups: records not zero %s (0x%x)\n",
475 		    prog, ip->host, records);
476 }
477 
478 /* Check for an "ignored zone" (usually dynamic dns) */
479 int
checkignoredzone(const char * name)480 checkignoredzone(const char *name)
481 {
482 	int i, len, len2;
483 
484 	len = strlen(name);
485 	if (len > 1 && name[len - 1] == '.')
486 		--len;
487 	for (i = 0; i < numignoredzones; ++i) {
488 		len2 = len - ignoredzones[i].len;
489 		if (len2 >= 0 &&
490 		    strncasecmp(name + len2,
491 			ignoredzones[i].zone, len - len2) == 0)
492 			    return (1);
493 	}
494 	return (0);
495 }
496 
497 int
checkserv(const char * serv,char ** p)498 checkserv(const char *serv, char **p)
499 {
500 	for (; *p != NULL; ++p)
501 		if (*serv == **p && strcmp(serv, *p) == 0)
502 			return (1);
503 	return (0);
504 }
505 
506 int
checkwks(FILE * f,char * proto,int * smtpp,char ** errstrp)507 checkwks(FILE *f, char *proto, int *smtpp, char **errstrp)
508 {
509 	int n, sawparen;
510 	char *cp, *serv, **p;
511 	static char errstr[132];
512 	char buf[1024];
513 	char psbuf[512];
514 
515 	if (!protoserv_init) {
516 		initprotoserv();
517 		++protoserv_init;
518 	}
519 
520 	/* Line count */
521 	n = 0;
522 
523 	/* Terminate protocol */
524 	cp = proto;
525 	while (!isspace(*cp) && *cp != '\0')
526 		++cp;
527 	if (*cp != '\0')
528 		*cp++ = '\0';
529 
530 	/* Find services */
531 	*smtpp = 0;
532 	sawparen = 0;
533 	if (*cp == '(') {
534 		++sawparen;
535 		++cp;
536 		while (isspace(*cp))
537 			++cp;
538 	}
539 	for (;;) {
540 		if (*cp == '\0') {
541 			if (!sawparen)
542 				break;
543 			if (fgets(buf, sizeof(buf), f) == NULL) {
544 				*errstrp = "mismatched parens";
545 				return (n);
546 			}
547 			++n;
548 			cp = buf;
549 			while (isspace(*cp))
550 				++cp;
551 		}
552 		/* Find end of service, converting to lowercase */
553 		for (serv = cp; !isspace(*cp) && *cp != '\0'; ++cp)
554 			if (isupper(*cp))
555 				*cp = tolower(*cp);
556 		if (*cp != '\0')
557 			*cp++ = '\0';
558 		if (sawparen && *cp == ')') {
559 			/* XXX should check for trailing junk */
560 			break;
561 		}
562 
563 		(void)sprintf(psbuf, "%s/%s", serv, proto);
564 
565 		if (*serv == 's' && strcmp(psbuf, "tcp/smtp") == 0)
566 			++*smtpp;
567 
568 		for (p = protoserv; *p != NULL; ++p)
569 			if (*psbuf == **p && strcmp(psbuf, *p) == 0) {
570 				break;
571 			}
572 		if (*p == NULL) {
573 			sprintf(errstr, "%s unknown", psbuf);
574 			*errstrp = errstr;
575 			break;
576 		}
577 	}
578 
579 	return (n);
580 }
581 
582 int
cmpaddr(const void * arg1,const void * arg2)583 cmpaddr(const void *arg1, const void *arg2)
584 {
585 	int i, r1;
586 	const struct network *n1, *n2;
587 
588 	n1 = (const struct network *)arg1;
589 	n2 = (const struct network *)arg2;
590 
591 	/* IPv4 before IPv6 */
592 	if (n1->family != n2->family)
593 		return ((n1->family == AF_INET) ? -1 : 1);
594 
595 	switch (n1->family) {
596 
597 	case AF_INET:
598 		/* Address */
599 		if (ntohl(n1->n_addr4) < ntohl(n2->n_addr4))
600 			return (-1);
601 		else if (ntohl(n1->n_addr4) > ntohl(n2->n_addr4))
602 			return (1);
603 		return (0);
604 
605 	case AF_INET6:
606 		/* Address */
607 		r1 = 0;
608 		for (i = 0; i < 16; ++i) {
609 			if (ntohl(n1->n_addr6[i]) < ntohl(n2->n_addr6[i]))
610 				return (-1);
611 			if (ntohl(n1->n_addr6[i]) > ntohl(n2->n_addr6[i]))
612 				return (1);
613 		}
614 		return (0);
615 
616 	default:
617 		abort();
618 	}
619 }
620 
621 int
cmpitemaddr(const void * arg1,const void * arg2)622 cmpitemaddr(const void *arg1, const void *arg2)
623 {
624 	struct item *i1, *i2;
625 
626 	i1 = (struct item *)arg1;
627 	i2 = (struct item *)arg2;
628 
629 	return (cmpaddr(&i1->addr, &i2->addr));
630 }
631 
632 int
cmpitemhost(const void * arg1,const void * arg2)633 cmpitemhost(const void *arg1, const void *arg2)
634 {
635 	struct item *i1, *i2;
636 
637 	i1 = (struct item *)arg1;
638 	i2 = (struct item *)arg2;
639 
640 	return (strcasecmp(i1->host, i1->host));
641 }
642 
643 /* Sort by network number (use mask when networks are the same) */
644 int
cmpnetwork(const void * arg1,const void * arg2)645 cmpnetwork(const void *arg1, const void *arg2)
646 {
647 	int i, r1, r2;
648 	const struct network *n1, *n2;
649 
650 	n1 = (const struct network *)arg1;
651 	n2 = (const struct network *)arg2;
652 
653 	/* IPv4 before IPv6 */
654 	if (n1->family != n2->family)
655 		return ((n1->family == AF_INET) ? -1 : 1);
656 
657 	switch (n1->family) {
658 
659 	case AF_INET:
660 		/* Address */
661 		if (ntohl(n1->n_addr4) < ntohl(n2->n_addr4))
662 			return (-1);
663 		else if (ntohl(n1->n_addr4) > ntohl(n2->n_addr4))
664 			return (1);
665 
666 		/* Mask */
667 		if (ntohl(n1->n_mask4) < ntohl(n2->n_mask4))
668 			return (1);
669 		else if (ntohl(n1->n_mask4) > ntohl(n2->n_mask4))
670 			return (-1);
671 		return (0);
672 
673 	case AF_INET6:
674 		/* Address */
675 		r1 = 0;
676 		for (i = 0; i < 16; ++i) {
677 			if (ntohl(n1->n_addr6[i]) < ntohl(n2->n_addr6[i]))
678 				return (-1);
679 			if (ntohl(n1->n_addr6[i]) > ntohl(n2->n_addr6[i]))
680 				return (1);
681 		}
682 
683 		/* Mask */
684 		r2 = 0;
685 		for (i = 0; i < 16; ++i) {
686 			if (n1->n_mask6[i] < n2->n_mask6[i])
687 				return (1);
688 			if (n1->n_mask6[i] > n2->n_mask6[i])
689 				return (-1);
690 		}
691 		return (0);
692 		break;
693 
694 	default:
695 		abort();
696 	}
697 	abort();
698 }
699 
700 void
doboot(const char * file,int flags)701 doboot(const char *file, int flags)
702 {
703 	int n;
704 	char *cp, *cp2;
705 	FILE *f;
706 	const char *errstr;
707 	char buf[1024], name[128];
708 
709 	errno = 0;
710 	f = fopen(file, "r");
711 	if (f == NULL) {
712 		/* Not an error if it doesn't exist */
713 		if ((flags & CONF_MUSTEXIST) == 0 && errno == ENOENT) {
714 			if (debug > 1)
715 				printf(
716 				    "%s: doit: %s doesn't exist (ignoring)\n",
717 				    prog, file);
718 			return;
719 		}
720 		fprintf(stderr, "%s: %s: %s\n", prog, file, strerror(errno));
721 		exit(1);
722 	}
723 	if (debug > 1)
724 		printf("%s: doit: opened %s\n", prog, file);
725 
726 	n = 0;
727 	while (fgets(buf, sizeof(buf), f) != NULL) {
728 		++n;
729 
730 		/* Skip comments */
731 		if (buf[0] == ';')
732 			continue;
733 		cp = strchr(buf, ';');
734 		if (cp)
735 			*cp = '\0';
736 		cp = buf + strlen(buf) - 1;
737 		if (cp >= buf && *cp == '\n')
738 			*cp = '\0';
739 		cp = buf;
740 
741 		/* Eat leading whitespace */
742 		while (isspace(*cp))
743 			++cp;
744 
745 		/* Skip blank lines */
746 		if (*cp == '\n' || *cp == '\0')
747 			continue;
748 
749 		/* Get name */
750 		cp2 = cp;
751 		while (!isspace(*cp) && *cp != '\0')
752 			++cp;
753 		*cp++ = '\0';
754 
755 		/* Find next keyword */
756 		while (isspace(*cp))
757 			++cp;
758 		if (strcasecmp(cp2, "directory") == 0) {
759 			/* Terminate directory */
760 			cp2 = cp;
761 			while (!isspace(*cp) && *cp != '\0')
762 				++cp;
763 			*cp = '\0';
764 			if (chdir(cp2) < 0) {
765 				++errors;
766 				fprintf(stderr, "%s: can't chdir %s: %s\n",
767 				    prog, cp2, strerror(errno));
768 				exit(1);
769 			}
770 			cwd = savestr(cp2);
771 			continue;
772 		}
773 		if (strcasecmp(cp2, "primary") == 0) {
774 			/* Extract domain, converting to lowercase */
775 			for (cp2 = name; !isspace(*cp) && *cp != '\0'; ++cp)
776 				if (isupper(*cp))
777 					*cp2++ = tolower(*cp);
778 				else
779 					*cp2++ = *cp;
780 			/* Insure trailing dot */
781 			if (cp2 > name && cp2[-1] != '.')
782 				*cp2++ = '.';
783 			*cp2 = '\0';
784 
785 			/* Find file */
786 			while (isspace(*cp))
787 				++cp;
788 
789 			/* Terminate directory */
790 			cp2 = cp;
791 			while (!isspace(*cp) && *cp != '\0')
792 				++cp;
793 			*cp = '\0';
794 
795 			/* Process it! (zone is the same as the domain) */
796 			nsoaval = -1;
797 			memset(soaval, 0, sizeof(soaval));
798 			if ((flags & CONF_NOZONE) == 0)
799 				process(cp2, name, name);
800 			continue;
801 		}
802 		if (strcasecmp(cp2, "network") == 0) {
803 			errstr = parsenetwork(cp);
804 			if (errstr != NULL) {
805 				++errors;
806 				fprintf(stderr,
807 				    "%s: %s:%d: bad network: %s\n",
808 				    prog, file, n, errstr);
809 			}
810 			continue;
811 		}
812 		if (strcasecmp(cp2, "include") == 0) {
813 			/* Terminate include file */
814 			cp2 = cp;
815 			while (!isspace(*cp) && *cp != '\0')
816 				++cp;
817 			*cp = '\0';
818 			doboot(cp2, 1);
819 			continue;
820 		}
821 		/* Eat any other options */
822 	}
823 	(void)fclose(f);
824 }
825 
826 void
doconf(const char * file,int flags)827 doconf(const char *file, int flags)
828 {
829 	int n, fd, cc, i, depth;
830 	char *cp, *cp2, *buf;
831 	const char *p;
832 	char *name, *zonename, *filename, *typename;
833 	int namelen, zonenamelen, filenamelen, typenamelen;
834 	struct stat sbuf;
835 	char zone[128], includefile[256];
836 
837 	errno = 0;
838 	fd = open(file, O_RDONLY, 0);
839 	if (fd < 0) {
840 		/* Not an error if it doesn't exist */
841 		if ((flags & CONF_MUSTEXIST) == 0 && errno == ENOENT) {
842 			if (debug > 1)
843 				printf(
844 				    "%s: doconf: %s doesn't exist (ignoring)\n",
845 				    prog, file);
846 			return;
847 		}
848 		fprintf(stderr, "%s: %s: %s\n", prog, file, strerror(errno));
849 		exit(1);
850 	}
851 	if (debug > 1)
852 		printf("%s: doconf: opened %s\n", prog, file);
853 
854 	if (fstat(fd, &sbuf) < 0) {
855 		fprintf(stderr, "%s: fstat(%s) %s\n",
856 		    prog, file, strerror(errno));
857 		exit(1);
858 	}
859 	buf = (char *)malloc(sbuf.st_size + 1);
860 	if (buf == NULL) {
861 		fprintf(stderr, "%s: malloc: %s\n", prog, strerror(errno));
862 		exit(1);
863 	}
864 
865 	/* Slurp entire config file */
866 	n = sbuf.st_size;
867 	cp = buf;
868 	do {
869 		cc = read(fd, cp, n);
870 		if (cc < 0) {
871 			fprintf(stderr, "%s: read(%s) %s\n",
872 			    prog, file, strerror(errno));
873 			exit(1);
874 		}
875 		cp += cc;
876 		n -= cc;
877 	} while (cc != 0 && cc < n);
878 	buf[cc] = '\0';
879 
880 #define EATWHITESPACE \
881 	while (isspace(*cp)) { \
882 		if (*cp == '\n') \
883 			++n; \
884 		++cp; \
885 	}
886 
887 /* Handle both to-end-of-line and C style comments */
888 #define EATCOMMENTS \
889 	{ \
890 	int sawcomment; \
891 	do { \
892 		EATWHITESPACE \
893 		sawcomment = 0; \
894 		if (*cp == '#') { \
895 			sawcomment = 1; \
896 			++cp; \
897 			while (*cp != '\n' && *cp != '\0') \
898 				++cp; \
899 		} \
900 		else if (strncmp(cp, "//", 2) == 0) { \
901 			sawcomment = 1; \
902 			cp += 2; \
903 			while (*cp != '\n' && *cp != '\0') \
904 				++cp; \
905 		} \
906 		else if (strncmp(cp, "/*", 2) == 0) { \
907 			sawcomment = 1; \
908 			for (cp += 2; *cp != '\0'; ++cp) { \
909 				if (*cp == '\n') \
910 					++n; \
911 				else if (strncmp(cp, "*/", 2) == 0) { \
912 					cp += 2; \
913 					break; \
914 				} \
915 			} \
916 		} \
917 	} while (sawcomment); \
918 	}
919 
920 #define GETNAME(name, len) \
921 	{ \
922 	(name) = cp; \
923 	(len) = 0; \
924 	while (!isspace(*cp) && *cp != ';' && *cp != '\0') { \
925 		++(len); \
926 		++cp; \
927 	} \
928 	}
929 
930 #define GETQUOTEDNAME(name, len) \
931 	{ \
932 	if (*cp != '"') { \
933 		++errors; \
934 		fprintf(stderr, "%s: %s:%d missing left quote\n", \
935 		    prog, file, n); \
936 	} else \
937 		++cp; \
938 	(name) = cp; \
939 	(len) = 0; \
940 	while (*cp != '"' && *cp != '\n' && *cp != '\0') { \
941 		++(len); \
942 		++cp; \
943 	} \
944 	if (*cp != '"') { \
945 		++errors; \
946 		fprintf(stderr, "%s: %s:%d missing right quote\n", \
947 		    prog, file, n); \
948 	} else \
949 		++cp; \
950 	}
951 
952 /* Eat everything to the next semicolon, perhaps eating matching qbraces */
953 #define EATSEMICOLON \
954 	{ \
955 	int depth = 0; \
956 	while (*cp != '\0') { \
957 		EATCOMMENTS \
958 		if (*cp == ';') { \
959 			++cp; \
960 			if (depth == 0) \
961 				break; \
962 			continue; \
963 		} \
964 		if (*cp == '{') { \
965 			++depth; \
966 			++cp; \
967 			continue; \
968 		} \
969 		if (*cp == '}') { \
970 			--depth; \
971 			++cp; \
972 			continue; \
973 		} \
974 		++cp; \
975 	} \
976 	}
977 
978 /* Eat everything to the next left qbrace */
979 #define EATSLEFTBRACE \
980 	while (*cp != '\0') { \
981 		EATCOMMENTS \
982 		if (*cp == '{') { \
983 			++cp; \
984 			break; \
985 		} \
986 		++cp; \
987 	}
988 
989 	n = 1;
990 	zone[0] = '\0';
991 	cp = buf;
992 	while (*cp != '\0') {
993 		EATCOMMENTS
994 		if (*cp == '\0')
995 			break;
996 		GETNAME(name, namelen)
997 		if (namelen == 0) {
998 			++errors;
999 			fprintf(stderr, "%s: %s:%d garbage char '%c' (1)\n",
1000 			    prog, file, n, *cp);
1001 			++cp;
1002 			continue;
1003 		}
1004 		EATCOMMENTS
1005 		if (strncasecmp(name, "options", namelen) == 0) {
1006 			EATCOMMENTS
1007 			if (*cp != '{')  {
1008 				++errors;
1009 				fprintf(stderr,
1010 			    "%s: %s:%d missing left qbrace in options\n",
1011 				    prog, file, n);
1012 			} else
1013 				++cp;
1014 			EATCOMMENTS
1015 			while (*cp != '}' && *cp != '\0') {
1016 				EATCOMMENTS
1017 				GETNAME(name, namelen)
1018 				if (namelen == 0) {
1019 					++errors;
1020 					fprintf(stderr,
1021 					    "%s: %s:%d garbage char '%c' (2)\n",
1022 					    prog, file, n, *cp);
1023 					++cp;
1024 					break;
1025 				}
1026 
1027 				/* If not the "directory" option, just eat it */
1028 				if (strncasecmp(name, "directory",
1029 				    namelen) == 0) {
1030 					EATCOMMENTS
1031 					GETQUOTEDNAME(cp2, i)
1032 					cp2[i] = '\0';
1033 					if (chdir(cp2) < 0) {
1034 						++errors;
1035 						fprintf(stderr,
1036 					    "%s: %s:.%d can't chdir %s: %s\n",
1037 						    prog, file, n, cp2,
1038 						    strerror(errno));
1039 						exit(1);
1040 					}
1041 					cwd = savestr(cp2);
1042 				}
1043 				EATSEMICOLON
1044 				EATCOMMENTS
1045 			}
1046 			++cp;
1047 			EATCOMMENTS
1048 			if (*cp != ';')  {
1049 				++errors;
1050 				fprintf(stderr,
1051 				    "%s: %s:%d missing options semi\n",
1052 				    prog, file, n);
1053 			} else
1054 				++cp;
1055 			continue;
1056 		}
1057 		if (strncasecmp(name, "zone", namelen) == 0) {
1058 			EATCOMMENTS
1059 			GETQUOTEDNAME(zonename, zonenamelen)
1060 			typename = NULL;
1061 			filename = NULL;
1062 			typenamelen = 0;
1063 			filenamelen = 0;
1064 			EATCOMMENTS
1065 			if (strncasecmp(cp, "in", 2) == 0) {
1066 				cp += 2;
1067 				EATWHITESPACE
1068 			} else if (strncasecmp(cp, "chaos", 5) == 0) {
1069 				cp += 5;
1070 				EATWHITESPACE
1071 			}
1072 			if (*cp != '{')  {	/* } */
1073 				++errors;
1074 				fprintf(stderr,
1075 			    "%s: %s:%d missing left qbrace in zone\n",
1076 				    prog, file, n);
1077 				continue;
1078 			}
1079 			depth = 0;
1080 			EATCOMMENTS
1081 			while (*cp != '\0') {
1082 				if (*cp == '{') {
1083 					++cp;
1084 					++depth;
1085 				} else if (*cp == '}') {
1086 					if (--depth <= 1)
1087 						break;
1088 					++cp;
1089 				}
1090 				EATCOMMENTS
1091 				GETNAME(name, namelen)
1092 				if (namelen == 0) {
1093 					++errors;
1094 					fprintf(stderr,
1095 					    "%s: %s:%d garbage char '%c' (3)\n",
1096 					    prog, file, n, *cp);
1097 					++cp;
1098 					break;
1099 				}
1100 				if (strncasecmp(name, "type",
1101 				    namelen) == 0) {
1102 					EATCOMMENTS
1103 					GETNAME(typename, typenamelen)
1104 					if (namelen == 0) {
1105 						++errors;
1106 						fprintf(stderr,
1107 					    "%s: %s:%d garbage char '%c' (4)\n",
1108 						    prog, file, n, *cp);
1109 						++cp;
1110 						break;
1111 					}
1112 				} else if (strncasecmp(name, "file",
1113 				    namelen) == 0) {
1114 					EATCOMMENTS
1115 					GETQUOTEDNAME(filename, filenamelen)
1116 				}
1117 				/* Just ignore keywords we don't understand */
1118 				EATSEMICOLON
1119 				EATCOMMENTS
1120 			}
1121 			/* { */
1122 			if (*cp != '}')  {
1123 				++errors;
1124 				fprintf(stderr,
1125 				    "%s: %s:%d missing zone right qbrace\n",
1126 				    prog, file, n);
1127 			} else
1128 				++cp;
1129 			if (*cp != ';')  {
1130 				++errors;
1131 				fprintf(stderr,
1132 				    "%s: %s:%d missing zone semi\n",
1133 				    prog, file, n);
1134 			} else
1135 				++cp;
1136 			EATCOMMENTS
1137 			/* If we got something interesting, process it */
1138 			if (typenamelen == 0) {
1139 				++errors;
1140 				fprintf(stderr, "%s: missing zone type!\n",
1141 				    prog);
1142 				continue;
1143 			}
1144 			if (strncasecmp(typename, "master", typenamelen) == 0) {
1145 				if (filenamelen == 0) {
1146 					++errors;
1147 					fprintf(stderr,
1148 					    "%s: missing zone filename!\n",
1149 					    prog);
1150 					continue;
1151 				}
1152 				strncpy(zone, zonename, zonenamelen);
1153 				zone[zonenamelen] = '\0';
1154 				for (cp2 = zone; *cp2 != '\0'; ++cp2)
1155 					if (isupper(*cp2))
1156 						*cp2 = tolower(*cp2);
1157 				/* Insure trailing dot */
1158 				if (cp2 > zone && cp2[-1] != '.') {
1159 					*cp2++ = '.';
1160 					*cp2 = '\0';
1161 				}
1162 				filename[filenamelen] = '\0';
1163 				nsoaval = -1;
1164 				memset(soaval, 0, sizeof(soaval));
1165 				if ((flags & CONF_NOZONE) == 0)
1166 					process(filename, zone, zone);
1167 			}
1168 			continue;
1169 		}
1170 		if (strncasecmp(name, "nslint", namelen) == 0) {
1171 			EATCOMMENTS
1172 			if (*cp != '{')  {
1173 				++errors;
1174 				fprintf(stderr,
1175 			    "%s: %s:%d missing left qbrace in nslint\n",
1176 				    prog, file, n);
1177 			} else
1178 				++cp;
1179 			++cp;
1180 			EATCOMMENTS
1181 			while (*cp != '}' && *cp != '\0') {
1182 				EATCOMMENTS
1183 				GETNAME(name, namelen)
1184 				if (strncasecmp(name, "network",
1185 				    namelen) == 0) {
1186 					EATCOMMENTS
1187 					GETQUOTEDNAME(cp2, i)
1188 
1189 					cp2[i] = '\0';
1190 					p = parsenetwork(cp2);
1191 					if (p != NULL) {
1192 						++errors;
1193 						fprintf(stderr,
1194 					    "%s: %s:%d: bad network: %s\n",
1195 						    prog, file, n, p);
1196 					}
1197 				} else if (strncasecmp(name, "ignorezone",
1198 				    namelen) == 0) {
1199 					EATCOMMENTS
1200 					GETQUOTEDNAME(cp2, i)
1201 					cp2[i] = '\0';
1202 					if (numignoredzones + 1 <
1203 					    sizeof(ignoredzones) /
1204 					    sizeof(ignoredzones[0])) {
1205 						ignoredzones[numignoredzones].zone =
1206 						    savestr(cp2);
1207 						if (ignoredzones[numignoredzones].zone != NULL) {
1208 							ignoredzones[numignoredzones].len = strlen(cp2);
1209 							++numignoredzones;
1210 						}
1211 					}
1212 				} else {
1213 					++errors;
1214 					fprintf(stderr,
1215 					    "%s: unknown nslint \"%.*s\"\n",
1216 					    prog, namelen, name);
1217 				}
1218 				EATSEMICOLON
1219 				EATCOMMENTS
1220 			}
1221 			++cp;
1222 			EATCOMMENTS
1223 			if (*cp != ';')  {
1224 				++errors;
1225 				fprintf(stderr,
1226 				    "%s: %s:%d: missing nslint semi\n",
1227 				    prog, file, n);
1228 			} else
1229 				++cp;
1230 			continue;
1231 		}
1232 		if (strncasecmp(name, "include", namelen) == 0) {
1233 			EATCOMMENTS
1234 			GETQUOTEDNAME(filename, filenamelen)
1235 			strncpy(includefile, filename, filenamelen);
1236 			includefile[filenamelen] = '\0';
1237 			doconf(includefile, 1);
1238 			EATSEMICOLON
1239 			continue;
1240 		}
1241 		if (strncasecmp(name, "view", namelen) == 0) {
1242 			EATSLEFTBRACE
1243 			continue;
1244 		}
1245 
1246 		/* Skip over statements we don't understand */
1247 		EATSEMICOLON
1248 	}
1249 
1250 	free(buf);
1251 	close(fd);
1252 }
1253 
1254 const char *
extractaddr(const char * str,struct addr * ap)1255 extractaddr(const char *str, struct addr *ap)
1256 {
1257 
1258 	memset(ap, 0, sizeof(*ap));
1259 
1260 	/* Let's see what we've got here */
1261 	if (strchr(str, '.') != NULL) {
1262 		ap->family = AF_INET;
1263 	} else if (strchr(str, ':') != NULL) {
1264 		ap->family = AF_INET6;
1265 	} else
1266 		return ("unrecognized address type");
1267 
1268 	switch (ap->family) {
1269 
1270 	case AF_INET:
1271 		if (!inet_pton(ap->family, str, &ap->a_addr4))
1272 			return ("cannot parse IPv4 address");
1273 
1274 		break;
1275 
1276 	case AF_INET6:
1277 		if (!inet_pton(ap->family, str, &ap->a_addr6))
1278 			return ("cannot parse IPv6 address");
1279 		break;
1280 
1281 	default:
1282 		abort();
1283 	}
1284 
1285 	return (NULL);
1286 }
1287 
1288 const char *
extractnetwork(const char * str,struct network * np)1289 extractnetwork(const char *str, struct network *np)
1290 {
1291 	int i;
1292 	long w;
1293 	char *cp, *ep;
1294 	const char *p;
1295 	char temp[64];
1296 
1297 	memset(np, 0, sizeof(*np));
1298 
1299 	/* Let's see what we've got here */
1300 	if (strchr(str, '.') != NULL) {
1301 		np->family = AF_INET;
1302 		w = 32;
1303 	} else if (strchr(str, ':') != NULL) {
1304 		np->family = AF_INET6;
1305 		w = 128;
1306 	} else
1307 		return ("unrecognized address type");
1308 
1309 	p = strchr(str, '/');
1310 	if (p != NULL) {
1311 		/* Mask length was specified */
1312 		strncpy(temp, str, sizeof(temp));
1313 		temp[sizeof(temp) - 1] = '\0';
1314 		cp = strchr(temp, '/');
1315 		if (cp == NULL)
1316 			abort();
1317 		*cp++ = '\0';
1318 		ep = NULL;
1319 		w = strtol(cp, &ep, 10);
1320 		if (*ep != '\0')
1321 			return ("garbage following mask width");
1322 		str = temp;
1323 	}
1324 
1325 	switch (np->family) {
1326 
1327 	case AF_INET:
1328 		if (!inet_pton(np->family, str, &np->n_addr4))
1329 			return ("cannot parse IPv4 address");
1330 
1331 		if (w > 32)
1332 			return ("mask length must be <= 32");
1333 		setmaskwidth(w, np);
1334 
1335 		if ((np->n_addr4 & ~np->n_mask4) != 0)
1336 			return ("non-network bits set in addr");
1337 
1338 #ifdef notdef
1339 		if ((ntohl(np->n_addr4) & 0xff000000) == 0)
1340 			return ("high octet must be non-zero");
1341 #endif
1342 		break;
1343 
1344 	case AF_INET6:
1345 		if (!inet_pton(np->family, str, &np->n_addr6))
1346 			return ("cannot parse IPv6 address");
1347 		if (w > 128)
1348 			return ("mask length must be <= 128");
1349 		setmaskwidth(w, np);
1350 
1351 		for (i = 0; i < 16; ++i) {
1352 			if ((np->n_addr6[i] & ~np->n_mask6[i]) != 0)
1353 				return ("non-network bits set in addr");
1354 		}
1355 		break;
1356 
1357 	default:
1358 		abort();
1359 	}
1360 
1361 	return (NULL);
1362 }
1363 
1364 struct network *
findnetwork(struct addr * ap)1365 findnetwork(struct addr *ap)
1366 {
1367 	int i, j;
1368 	struct network *np;
1369 
1370 	switch (ap->family) {
1371 
1372 	case AF_INET:
1373 		for (i = 0, np = netlist; i < netlistcnt; ++i, ++np)
1374 			if ((ap->a_addr4 & np->n_mask4) == np->n_addr4)
1375 				return (np);
1376 		break;
1377 
1378 	case AF_INET6:
1379 		for (i = 0, np = netlist; i < netlistcnt; ++i, ++np) {
1380 			for (j = 0; j < sizeof(ap->a_addr6); ++j) {
1381 				if ((ap->a_addr6[j] & np->n_mask6[j]) !=
1382 				    np->n_addr6[j])
1383 					break;
1384 			}
1385 			if (j >= sizeof(ap->a_addr6))
1386 				return (np);
1387 		}
1388 		break;
1389 
1390 	default:
1391 		abort();
1392 	}
1393 	return (NULL);
1394 }
1395 
1396 void
initprotoserv(void)1397 initprotoserv(void)
1398 {
1399 	char *cp;
1400 	struct servent *sp;
1401 	char psbuf[512];
1402 
1403 	protoserv_len = 256;
1404 	protoserv = (char **)malloc(protoserv_len * sizeof(*protoserv));
1405 	if (protoserv == NULL) {
1406 		fprintf(stderr, "%s: nslint: malloc: %s\n",
1407 		    prog, strerror(errno));
1408 		exit(1);
1409 	}
1410 
1411 	while ((sp = getservent()) != NULL) {
1412 		(void)sprintf(psbuf, "%s/%s", sp->s_name, sp->s_proto);
1413 
1414 		/* Convert to lowercase */
1415 		for (cp = psbuf; *cp != '\0'; ++cp)
1416 			if (isupper(*cp))
1417 				*cp = tolower(*cp);
1418 
1419 		if (protoserv_last + 1 >= protoserv_len) {
1420 			protoserv_len <<= 1;
1421 			protoserv = realloc(protoserv,
1422 			    protoserv_len * sizeof(*protoserv));
1423 			if (protoserv == NULL) {
1424 				fprintf(stderr, "%s: nslint: realloc: %s\n",
1425 				    prog, strerror(errno));
1426 				exit(1);
1427 			}
1428 		}
1429 		protoserv[protoserv_last] = savestr(psbuf);
1430 		++protoserv_last;
1431 	}
1432 	protoserv[protoserv_last] = NULL;
1433 }
1434 
1435 int
isip6arpa(const char * name)1436 isip6arpa(const char *name)
1437 {
1438 	const char *p;
1439 	const char ip6arpa[] = ".ip6.arpa.";
1440 	p = name + strlen(name) - (sizeof(ip6arpa) - 1);
1441 	return (strcmp(p, ip6arpa) == 0);
1442 }
1443 
1444 int
maskwidth(struct network * np)1445 maskwidth(struct network *np)
1446 {
1447 	int w;
1448 	int i, j;
1449 	u_int32_t m, tm;
1450 
1451 	/* Work backwards until we find a set bit */
1452 	switch (np->family) {
1453 
1454 	case AF_INET:
1455 		m = ntohl(np->n_mask4);
1456 		for (w = 32; w > 0; --w) {
1457 			tm = 0xffffffff << (32 - w);
1458 			if (tm == m)
1459 				break;
1460 		}
1461 		break;
1462 
1463 	case AF_INET6:
1464 		w = 128;
1465 		for (j = 15; j >= 0; --j) {
1466 			m = np->n_mask6[j];
1467 			for (i = 8; i > 0; --w, --i) {
1468 				tm = (0xff << (8 - i)) & 0xff;
1469 				if (tm == m)
1470 					return (w);
1471 			}
1472 		}
1473 		break;
1474 
1475 	default:
1476 		abort();
1477 	}
1478 	return (w);
1479 }
1480 
1481 const char *
network2str(struct network * np)1482 network2str(struct network *np)
1483 {
1484 	int w;
1485 	size_t len, size;
1486 	char *cp;
1487 	static char buf[128];
1488 
1489 	w = maskwidth(np);
1490 	switch (np->family) {
1491 
1492 	case AF_INET:
1493 		if (inet_ntop(np->family, &np->n_addr4,
1494 		    buf, sizeof(buf)) == NULL) {
1495 			fprintf(stderr, "network2str: v4 botch");
1496 			abort();
1497 		}
1498 		if (w == 32)
1499 			return (buf);
1500 		break;
1501 
1502 	case AF_INET6:
1503 		if (inet_ntop(np->family, &np->n_addr6,
1504 		    buf, sizeof(buf)) == NULL) {
1505 			fprintf(stderr, "network2str: v6 botch");
1506 			abort();
1507 		}
1508 		if (w == 128)
1509 			return (buf);
1510 		break;
1511 
1512 	default:
1513 		return ("<nil>");
1514 	}
1515 
1516 	/* Append address mask width */
1517 	cp = buf;
1518 	len = strlen(cp);
1519 	cp += len;
1520 	size = sizeof(buf) - len;
1521 	(void)snprintf(cp, size, "/%d", w);
1522 	return (buf);
1523 }
1524 
1525 void
nslint(void)1526 nslint(void)
1527 {
1528 	int n, records, flags;
1529 	struct item *ip, *lastaip, **ipp, **itemlist;
1530 	struct addr addr, lastaddr;
1531 	struct network *np;
1532 
1533 	itemlist = (struct item **)calloc(itemcnt, sizeof(*ipp));
1534 	if (itemlist == NULL) {
1535 		fprintf(stderr, "%s: nslint: calloc: %s\n",
1536 		    prog, strerror(errno));
1537 		exit(1);
1538 	}
1539 	ipp = itemlist;
1540 	for (n = 0, ip = items; n < ITEMSIZE; ++n, ++ip) {
1541 		if (ip->host == NULL)
1542 			continue;
1543 		/* Save entries with addresses for later check */
1544 		if (ip->addr.family != 0)
1545 			*ipp++ = ip;
1546 
1547 		if (debug > 1) {
1548 			if (debug > 2)
1549 				printf("%d\t", n);
1550 			printf("%s\t%s\t0x%x\t0x%x\n",
1551 			    ip->host, addr2str(&ip->addr),
1552 			    ip->records, ip->flags);
1553 		}
1554 
1555 		/* Check for illegal hostnames (rfc1034) */
1556 		if (rfc1034host(ip->host, ip->records))
1557 			++errors;
1558 
1559 		/* Check for missing ptr records (ok if also an ns record) */
1560 		records = ip->records & MASK_CHECK_REC;
1561 		if ((ip->records & MASK_TEST_REC) != 0)
1562 			records |= REC_OTHER;
1563 		switch (records) {
1564 
1565 		case REC_A | REC_OTHER | REC_PTR | REC_REF:
1566 		case REC_A | REC_OTHER | REC_PTR:
1567 		case REC_A | REC_PTR | REC_REF:
1568 		case REC_A | REC_PTR:
1569 		case REC_AAAA | REC_OTHER | REC_PTR | REC_REF:
1570 		case REC_AAAA | REC_OTHER | REC_PTR:
1571 		case REC_AAAA | REC_PTR | REC_REF:
1572 		case REC_AAAA | REC_PTR:
1573 		case REC_CNAME:
1574 			/* These are O.K. */
1575 			break;
1576 
1577 		case REC_CNAME | REC_REF:
1578 			++errors;
1579 			fprintf(stderr, "%s: \"cname\" referenced by other"
1580 			    " \"cname\" or \"mx\": %s\n", prog, ip->host);
1581 			break;
1582 
1583 		case REC_OTHER | REC_REF:
1584 		case REC_OTHER:
1585 			/*
1586 			 * This is only an error if there is an address
1587 			 * associated with the hostname; this means
1588 			 * there was a wks entry with bogus address.
1589 			 * Otherwise, we have an mx or hinfo.
1590 			 *
1591 			 * XXX ignore localhost for now
1592 			 * (use flag to indicate loopback?)
1593 			 */
1594 			if (ip->addr.family == AF_INET &&
1595 			    ip->addr.a_addr4 != htonl(INADDR_LOOPBACK)) {
1596 				++errors;
1597 				fprintf(stderr,
1598 			    "%s: \"wks\" without \"a\" and \"ptr\": %s -> %s\n",
1599 				    prog, ip->host, addr2str(&ip->addr));
1600 			}
1601 			break;
1602 
1603 		case REC_REF:
1604 			if (!checkignoredzone(ip->host)) {
1605 				++errors;
1606 				fprintf(stderr, "%s: Name referenced without"
1607 				    " other records: %s\n", prog, ip->host);
1608 			}
1609 			break;
1610 
1611 		case REC_A | REC_OTHER | REC_REF:
1612 		case REC_A | REC_OTHER:
1613 		case REC_A | REC_REF:
1614 		case REC_A:
1615 		case REC_AAAA | REC_OTHER | REC_REF:
1616 		case REC_AAAA | REC_OTHER:
1617 		case REC_AAAA | REC_REF:
1618 		case REC_AAAA:
1619 			++errors;
1620 			fprintf(stderr, "%s: Missing \"ptr\": %s -> %s\n",
1621 			    prog, ip->host, addr2str(&ip->addr));
1622 			break;
1623 
1624 		case REC_OTHER | REC_PTR | REC_REF:
1625 		case REC_OTHER | REC_PTR:
1626 		case REC_PTR | REC_REF:
1627 		case REC_PTR:
1628 			++errors;
1629 			fprintf(stderr, "%s: Missing \"a\": %s -> %s\n",
1630 			    prog, ip->host, addr2str(&ip->addr));
1631 			break;
1632 
1633 		case REC_A | REC_CNAME | REC_OTHER | REC_PTR | REC_REF:
1634 		case REC_A | REC_CNAME | REC_OTHER | REC_PTR:
1635 		case REC_A | REC_CNAME | REC_OTHER | REC_REF:
1636 		case REC_A | REC_CNAME | REC_OTHER:
1637 		case REC_A | REC_CNAME | REC_PTR | REC_REF:
1638 		case REC_A | REC_CNAME | REC_PTR:
1639 		case REC_A | REC_CNAME | REC_REF:
1640 		case REC_A | REC_CNAME:
1641 		case REC_AAAA | REC_CNAME | REC_OTHER | REC_PTR | REC_REF:
1642 		case REC_AAAA | REC_CNAME | REC_OTHER | REC_PTR:
1643 		case REC_AAAA | REC_CNAME | REC_OTHER | REC_REF:
1644 		case REC_AAAA | REC_CNAME | REC_OTHER:
1645 		case REC_AAAA | REC_CNAME | REC_PTR | REC_REF:
1646 		case REC_AAAA | REC_CNAME | REC_PTR:
1647 		case REC_AAAA | REC_CNAME | REC_REF:
1648 		case REC_AAAA | REC_CNAME:
1649 		case REC_CNAME | REC_OTHER | REC_PTR | REC_REF:
1650 		case REC_CNAME | REC_OTHER | REC_PTR:
1651 		case REC_CNAME | REC_OTHER | REC_REF:
1652 		case REC_CNAME | REC_OTHER:
1653 		case REC_CNAME | REC_PTR | REC_REF:
1654 		case REC_CNAME | REC_PTR:
1655 			++errors;
1656 			fprintf(stderr, "%s: \"cname\" %s has other records\n",
1657 			    prog, ip->host);
1658 			break;
1659 
1660 		case 0:
1661 			/* Second level test */
1662 			if ((ip->records & ~(REC_NS | REC_TXT)) == 0)
1663 				break;
1664 			/* Fall through... */
1665 
1666 		default:
1667 			++errors;
1668 			fprintf(stderr,
1669 			    "%s: records == 0x%x: can't happen (%s 0x%x)\n",
1670 			    prog, records, ip->host, ip->records);
1671 			break;
1672 		}
1673 
1674 		/* Check for smtp problems */
1675 		flags = ip->flags & MASK_TEST_SMTP;
1676 
1677 		if ((flags & FLG_SELFMX) != 0 &&
1678 		    (ip->records & (REC_A | REC_AAAA)) == 0) {
1679 			++errors;
1680 			fprintf(stderr,
1681 			    "%s: Self \"mx\" for %s missing"
1682 			    " \"a\" or \"aaaa\" record\n",
1683 			    prog, ip->host);
1684 		}
1685 
1686 		switch (flags) {
1687 
1688 		case 0:
1689 		case FLG_SELFMX | FLG_SMTPWKS:
1690 			/* These are O.K. */
1691 			break;
1692 
1693 		case FLG_SELFMX:
1694 			if ((ip->records & REC_WKS) != 0) {
1695 				++errors;
1696 				fprintf(stderr,
1697 				    "%s: smtp/tcp missing from \"wks\": %s\n",
1698 				    prog, ip->host);
1699 			}
1700 			break;
1701 
1702 		case FLG_SMTPWKS:
1703 			++errors;
1704 			fprintf(stderr,
1705 			    "%s: Saw smtp/tcp without self \"mx\": %s\n",
1706 			    prog, ip->host);
1707 			break;
1708 
1709 		default:
1710 			++errors;
1711 			fprintf(stderr,
1712 			    "%s: flags == 0x%x: can't happen (%s)\n",
1713 			    prog, flags, ip->host);
1714 		}
1715 
1716 		/* Check for chained MX records */
1717 		if ((ip->flags & (FLG_SELFMX | FLG_MXREF)) == FLG_MXREF &&
1718 		    (ip->records & REC_MX) != 0) {
1719 			++errors;
1720 			fprintf(stderr, "%s: \"mx\" referenced by other"
1721 			    " \"mx\" record: %s\n", prog, ip->host);
1722 		}
1723 	}
1724 
1725 	/* Check for doubly booked addresses */
1726 	n = ipp - itemlist;
1727 	qsort(itemlist, n, sizeof(itemlist[0]), cmpaddr);
1728 	memset(&lastaddr, 0, sizeof(lastaddr));
1729 	ip = NULL;
1730 	for (ipp = itemlist; n > 0; ++ipp, --n) {
1731 		addr = (*ipp)->addr;
1732 		if (cmpaddr(&lastaddr, &addr) == 0 &&
1733 		    ((*ipp)->flags & FLG_ALLOWDUPA) == 0 &&
1734 		    (ip->flags & FLG_ALLOWDUPA) == 0) {
1735 			++errors;
1736 			fprintf(stderr, "%s: %s in use by %s and %s\n",
1737 			    prog, addr2str(&addr), (*ipp)->host, ip->host);
1738 		}
1739 		memmove(&lastaddr, &addr, sizeof(addr));
1740 		ip = *ipp;
1741 	}
1742 
1743 	/* Check for hosts with multiple addresses on the same subnet */
1744 	n = ipp - itemlist;
1745 	qsort(itemlist, n, sizeof(itemlist[0]), cmpitemhost);
1746 	if (netlistcnt > 0) {
1747 		n = ipp - itemlist;
1748 		lastaip = NULL;
1749 		for (ipp = itemlist; n > 0; ++ipp, --n) {
1750 			ip = *ipp;
1751 			if ((ip->records & (REC_A | REC_AAAA)) == 0 ||
1752 			    (ip->flags & FLG_ALLOWDUPA) != 0)
1753 				continue;
1754 			if (lastaip != NULL &&
1755 			    strcasecmp(ip->host, lastaip->host) == 0) {
1756 				np = findnetwork(&ip->addr);
1757 				if (np == NULL) {
1758 					++errors;
1759 					fprintf(stderr,
1760 					    "%s: Can't find subnet mask"
1761 					    " for %s (%s)\n",
1762 					    prog, ip->host,
1763 					    addr2str(&ip->addr));
1764 				} else if (samesubnet(&lastaip->addr,
1765 				    &ip->addr, np)) {
1766 					++errors;
1767 					fprintf(stderr,
1768 			    "%s: Multiple \"a\" records for %s on subnet %s",
1769 					    prog, ip->host,
1770 					    network2str(np));
1771 					fprintf(stderr, "\n\t(%s",
1772 					    addr2str(&lastaip->addr));
1773 					fprintf(stderr, " and %s)\n",
1774 					    addr2str(&ip->addr));
1775 				}
1776 			}
1777 			lastaip = ip;
1778 		}
1779 	}
1780 
1781 	if (debug)
1782 		printf("%s: %d/%d items used, %d error%s\n", prog, itemcnt,
1783 		    ITEMSIZE, errors, errors == 1 ? "" : "s");
1784 }
1785 
1786 const char *
parsenetwork(const char * cp)1787 parsenetwork(const char *cp)
1788 {
1789 	const char *p;
1790 	struct network net;
1791 
1792 	while (isspace(*cp))
1793 		++cp;
1794 
1795 	p = extractnetwork(cp, &net);
1796 	if (p != NULL)
1797 		return (p);
1798 
1799 	while (isspace(*cp))
1800 		++cp;
1801 
1802 	/* Make sure there's room */
1803 	if (netlistsize <= netlistcnt) {
1804 		if (netlistsize == 0) {
1805 			netlistsize = 32;
1806 			netlist = (struct network *)
1807 			    malloc(netlistsize * sizeof(*netlist));
1808 		} else {
1809 			netlistsize <<= 1;
1810 			netlist = (struct network *)
1811 			    realloc(netlist, netlistsize * sizeof(*netlist));
1812 		}
1813 		if (netlist == NULL) {
1814 			fprintf(stderr,
1815 			    "%s: parsenetwork: malloc/realloc: %s\n",
1816 			    prog, strerror(errno));
1817 			exit(1);
1818 		}
1819 	}
1820 
1821 	/* Add to list */
1822 	memmove(netlist + netlistcnt, &net, sizeof(net));
1823 	++netlistcnt;
1824 
1825 	return (NULL);
1826 }
1827 
1828 const char *
parseptr(const char * str,struct addr * ap)1829 parseptr(const char *str, struct addr *ap)
1830 {
1831 	int i, n, base;
1832 	u_long v, v2;
1833 	char *cp;
1834 	const char *p;
1835 	u_char *up;
1836 
1837 	memset(ap, 0, sizeof(*ap));
1838 	base = -1;
1839 
1840 	/* IPv4 */
1841 	p = str + strlen(str) - sizeof(inaddr) + 1;
1842 	if (p >= str && strcasecmp(p, inaddr) == 0) {
1843 		ap->family = AF_INET;
1844 		n = 4;
1845 		base = 10;
1846 	} else {
1847 		/* IPv6 */
1848 		p = str + strlen(str) - sizeof(inaddr6) + 1;
1849 		if (p >= str && strcasecmp(p, inaddr6) == 0) {
1850 			ap->family = AF_INET6;
1851 			n = 16;
1852 			base = 16;
1853 		}
1854 	}
1855 
1856 	if (base < 0)
1857 		return ("Not a IPv4 or IPv6 \"ptr\" record");
1858 
1859 	up = (u_char *)&ap->addr;
1860 	for (i = 0; i < n; ++i) {
1861 		/* Back up to previous dot or beginning of string */
1862 		while (p > str && p[-1] != '.')
1863 			--p;
1864 		v = strtoul(p, &cp, base);
1865 
1866 		if (base == 10) {
1867 			if (v > 0xff)
1868 				return ("Octet larger than 8 bits");
1869 		} else {
1870 			if (v > 0xf)
1871 				return ("Octet larger than 4 bits");
1872 			if (*cp != '.')
1873 				return ("Junk in \"ptr\" record");
1874 
1875 			/* Back up over dot */
1876 			if (p > str)
1877 				--p;
1878 
1879 			/* Back up to previous dot or beginning of string */
1880 			while (p > str && p[-1] != '.')
1881 				--p;
1882 			v2 = strtoul(p, &cp, base);
1883 			if (v2 > 0xf)
1884 				return ("Octet larger than 4 bits");
1885 			if (*cp != '.')
1886 				return ("Junk in \"ptr\" record");
1887 			v = (v << 4) | v2;
1888 		}
1889 		if (*cp != '.')
1890 			return ("Junk in \"ptr\" record");
1891 
1892 		*up++ = v & 0xff;
1893 
1894 		/* Back up over dot */
1895 		if (p > str)
1896 			--p;
1897 		else if (p == str)
1898 			break;
1899 	}
1900 	if (i < n - 1)
1901 		return ("Too many octets in \"ptr\" record");
1902 	if (p != str)
1903 		return ("Not enough octets in \"ptr\" record");
1904 
1905 	return (NULL);
1906 }
1907 
1908 /* Returns a pointer after the next token or quoted string, else NULL */
1909 char *
parsequoted(char * cp)1910 parsequoted(char *cp)
1911 {
1912 
1913 	if (*cp == '"') {
1914 		++cp;
1915 		while (*cp != '"' && *cp != '\0')
1916 			++cp;
1917 		if (*cp != '"')
1918 			return (NULL);
1919 		++cp;
1920 	} else {
1921 		while (!isspace(*cp) && *cp != '\0')
1922 			++cp;
1923 	}
1924 	return (cp);
1925 }
1926 
1927 /* Return true when done */
1928 int
parserrsig(const char * str,char ** errstrp)1929 parserrsig(const char *str, char **errstrp)
1930 {
1931 	const char *cp;
1932 
1933 	/* XXX just look for closing paren */
1934 	cp = str + strlen(str) - 1;
1935 	while (cp >= str)
1936 		if (*cp-- == ')')
1937 		return (1);
1938 	return (0);
1939 }
1940 
1941 /* Return true when done */
1942 int
parsesoa(const char * cp,char ** errstrp)1943 parsesoa(const char *cp, char **errstrp)
1944 {
1945 	char ch, *garbage;
1946 	static char errstr[132];
1947 
1948 	/* Eat leading whitespace */
1949 	while (isspace(*cp))
1950 		++cp;
1951 
1952 	/* Find opening paren */
1953 	if (nsoaval < 0) {
1954 		cp = strchr(cp, '(');
1955 		if (cp == NULL)
1956 			return (0);
1957 		++cp;
1958 		while (isspace(*cp))
1959 			++cp;
1960 		nsoaval = 0;
1961 	}
1962 
1963 	/* Grab any numbers we find */
1964 	garbage = "leading garbage";
1965 	while (isdigit(*cp) && nsoaval < NSOAVAL) {
1966 		soaval[nsoaval] = atoi(cp);
1967 		do {
1968 			++cp;
1969 		} while (isdigit(*cp));
1970 		if (nsoaval == SOA_SERIAL && *cp == '.' && isdigit(cp[1])) {
1971 			do {
1972 				++cp;
1973 			} while (isdigit(*cp));
1974 		} else {
1975 			ch = *cp;
1976 			if (isupper(ch))
1977 				ch = tolower(ch);
1978 			switch (ch) {
1979 
1980 			case 'w':
1981 				soaval[nsoaval] *= 7;
1982 				/* fall through */
1983 
1984 			case 'd':
1985 				soaval[nsoaval] *= 24;
1986 				/* fall through */
1987 
1988 			case 'h':
1989 				soaval[nsoaval] *= 60;
1990 				/* fall through */
1991 
1992 			case 'm':
1993 				soaval[nsoaval] *= 60;
1994 				/* fall through */
1995 
1996 			case 's':
1997 				++cp;
1998 				break;
1999 
2000 			default:
2001 				;	/* none */
2002 			}
2003 		}
2004 		while (isspace(*cp))
2005 			++cp;
2006 		garbage = "trailing garbage";
2007 		++nsoaval;
2008 	}
2009 
2010 	/* If we're done, do some sanity checks */
2011 	if (nsoaval >= NSOAVAL && *cp == ')') {
2012 		++cp;
2013 		if (*cp != '\0')
2014 			*errstrp = garbage;
2015 		else if (soaval[SOA_EXPIRE] <
2016 		    soaval[SOA_REFRESH] + 10 * soaval[SOA_RETRY]) {
2017 			(void)sprintf(errstr,
2018 		    "expire less than refresh + 10 * retry (%u < %u + 10 * %u)",
2019 			    soaval[SOA_EXPIRE],
2020 			    soaval[SOA_REFRESH],
2021 			    soaval[SOA_RETRY]);
2022 			*errstrp = errstr;
2023 		} else if (soaval[SOA_REFRESH] < 2 * soaval[SOA_RETRY]) {
2024 			(void)sprintf(errstr,
2025 			    "refresh less than 2 * retry (%u < 2 * %u)",
2026 			    soaval[SOA_REFRESH],
2027 			    soaval[SOA_RETRY]);
2028 			*errstrp = errstr;
2029 		}
2030 		return (1);
2031 	}
2032 
2033 	if (*cp != '\0') {
2034 		*errstrp = garbage;
2035 		return (1);
2036 	}
2037 
2038 	return (0);
2039 }
2040 
2041 void
process(const char * file,const char * domain,const char * zone)2042 process(const char *file, const char *domain, const char *zone)
2043 {
2044 	FILE *f;
2045 	char ch, *cp, *cp2, *cp3, *rtype;
2046 	const char *p;
2047 	int n, sawsoa, sawrrsig, flags, i;
2048 	u_int ttl;
2049 	enum rrtype rrtype;
2050 	struct addr *ap;
2051 	struct addr addr;
2052 	// struct network *net;
2053 	int smtp;
2054 	char buf[2048], name[256], lastname[256], odomain[256];
2055 	char *errstr;
2056 	const char *addrfmt =
2057 	    "%s: %s/%s:%d \"%s\" target is an ip address: %s\n";
2058 	const char *dotfmt =
2059 	    "%s: %s/%s:%d \"%s\" target missing trailing dot: %s\n";
2060 
2061 	/* Check for an "ignored zone" (usually dynamic dns) */
2062 	if (checkignoredzone(zone))
2063 		return;
2064 
2065 	f = fopen(file, "r");
2066 	if (f == NULL) {
2067 		fprintf(stderr, "%s: %s/%s: %s\n",
2068 		    prog, cwd, file, strerror(errno));
2069 		++errors;
2070 		return;
2071 	}
2072 	if (debug > 1)
2073 		printf("%s: process: opened %s/%s\n", prog, cwd, file);
2074 
2075 	/* Line number */
2076 	n = 0;
2077 
2078 	ap = &addr;
2079 
2080 	lastname[0] = '\0';
2081 	sawsoa = 0;
2082 	sawrrsig = 0;
2083 	while (fgets(buf, sizeof(buf), f) != NULL) {
2084 		++n;
2085 		cp = buf;
2086 		while (*cp != '\0') {
2087 			/* Handle quoted strings (but don't report errors) */
2088 			if (*cp == '"') {
2089 				++cp;
2090 				while (*cp != '"' && *cp != '\n' && *cp != '\0')
2091 					++cp;
2092 				continue;
2093 			}
2094 			if (*cp == '\n' || *cp == ';')
2095 				break;
2096 			++cp;
2097 		}
2098 		*cp-- = '\0';
2099 
2100 		/* Nuke trailing white space */
2101 		while (cp >= buf && isspace(*cp))
2102 			*cp-- = '\0';
2103 
2104 		cp = buf;
2105 		if (*cp == '\0')
2106 			continue;
2107 
2108 		/* Handle multi-line soa records */
2109 		if (sawsoa) {
2110 			errstr = NULL;
2111 			if (parsesoa(cp, &errstr))
2112 				sawsoa = 0;
2113 			if (errstr != NULL) {
2114 				++errors;
2115 				fprintf(stderr,
2116 				    "%s: %s/%s:%d Bad \"soa\" record (%s)\n",
2117 				    prog, cwd, file, n, errstr);
2118 			}
2119 			continue;
2120 		}
2121 
2122 		/* Handle multi-line rrsig records */
2123 		if (sawrrsig) {
2124 			errstr = NULL;
2125 			if (parserrsig(cp, &errstr))
2126 				sawsoa = 0;
2127 			if (errstr != NULL) {
2128 				++errors;
2129 				fprintf(stderr,
2130 				    "%s: %s/%s:%d Bad \"rrsig\" record (%s)\n",
2131 				    prog, cwd, file, n, errstr);
2132 			}
2133 			continue;
2134 		}
2135 
2136 		if (debug > 3)
2137 			printf(">%s<\n", cp);
2138 
2139 		/* Look for name */
2140 		if (isspace(*cp)) {
2141 			/* Same name as last record */
2142 			if (lastname[0] == '\0') {
2143 				++errors;
2144 				fprintf(stderr,
2145 				    "%s: %s/%s:%d No default name\n",
2146 				    prog, cwd, file, n);
2147 				continue;
2148 			}
2149 			(void)strcpy(name, lastname);
2150 		} else {
2151 			/* Extract name, converting to lowercase */
2152 			for (cp2 = name; !isspace(*cp) && *cp != '\0'; ++cp)
2153 				if (isupper(*cp))
2154 					*cp2++ = tolower(*cp);
2155 				else
2156 					*cp2++ = *cp;
2157 			*cp2 = '\0';
2158 
2159 			/* Check for domain shorthand */
2160 			if (name[0] == '@' && name[1] == '\0')
2161 				(void)strcpy(name, domain);
2162 		}
2163 
2164 		/* Find next token */
2165 		while (isspace(*cp))
2166 			++cp;
2167 
2168 		/* Handle includes (gag) */
2169 		if (name[0] == '$' && strcasecmp(name, "$include") == 0) {
2170 			/* Extract filename */
2171 			cp2 = name;
2172 			while (!isspace(*cp) && *cp != '\0')
2173 				*cp2++ = *cp++;
2174 			*cp2 = '\0';
2175 
2176 			/* Look for optional domain */
2177 			while (isspace(*cp))
2178 				++cp;
2179 			if (*cp == '\0')
2180 				process(name, domain, zone);
2181 			else {
2182 				cp2 = cp;
2183 				/* Convert optional domain to lowercase */
2184 				for (; !isspace(*cp) && *cp != '\0'; ++cp)
2185 					if (isupper(*cp))
2186 						*cp = tolower(*cp);
2187 				*cp = '\0';
2188 				process(name, cp2, cp2);
2189 			}
2190 			continue;
2191 		}
2192 
2193 		/* Handle $origin */
2194 		if (name[0] == '$' && strcasecmp(name, "$origin") == 0) {
2195 			/* Extract domain, converting to lowercase */
2196 			for (cp2 = odomain; !isspace(*cp) && *cp != '\0'; ++cp)
2197 				if (isupper(*cp))
2198 					*cp2++ = tolower(*cp);
2199 				else
2200 					*cp2++ = *cp;
2201 			*cp2 = '\0';
2202 			domain = odomain;
2203 			lastname[0] = '\0';
2204 			continue;
2205 		}
2206 
2207 		/* Handle ttl */
2208 		if (name[0] == '$' && strcasecmp(name, "$ttl") == 0) {
2209 			cp2 = cp;
2210 			while (isdigit(*cp))
2211 				++cp;
2212 			ch = *cp;
2213 			if (isupper(ch))
2214 				ch = tolower(ch);
2215 			if (strchr("wdhms", ch) != NULL)
2216 				++cp;
2217 			while (isspace(*cp))
2218 				++cp;
2219 			if (*cp != '\0') {
2220 				++errors;
2221 				fprintf(stderr,
2222 				    "%s: %s/%s:%d Bad $ttl \"%s\"\n",
2223 				    prog, cwd, file, n, cp2);
2224 			}
2225 			(void)strcpy(name, lastname);
2226 			continue;
2227 		}
2228 
2229 		/* Parse ttl or use default  */
2230 		if (isdigit(*cp)) {
2231 			ttl = atoi(cp);
2232 			do {
2233 				++cp;
2234 			} while (isdigit(*cp));
2235 
2236 			ch = *cp;
2237 			if (isupper(ch))
2238 				ch = tolower(ch);
2239 			switch (ch) {
2240 
2241 			case 'w':
2242 				ttl *= 7;
2243 				/* fall through */
2244 
2245 			case 'd':
2246 				ttl *= 24;
2247 				/* fall through */
2248 
2249 			case 'h':
2250 				ttl *= 60;
2251 				/* fall through */
2252 
2253 			case 'm':
2254 				ttl *= 60;
2255 				/* fall through */
2256 
2257 			case 's':
2258 				++cp;
2259 				break;
2260 
2261 			default:
2262 				;	/* none */
2263 			}
2264 
2265 			if (!isspace(*cp)) {
2266 				++errors;
2267 				fprintf(stderr, "%s: %s/%s:%d Bad ttl\n",
2268 				    prog, cwd, file, n);
2269 				continue;
2270 			}
2271 
2272 			/* Find next token */
2273 			++cp;
2274 			while (isspace(*cp))
2275 				++cp;
2276 		} else
2277 			ttl = soaval[SOA_MINIMUM];
2278 
2279 		/* Eat optional "in" */
2280 		if ((cp[0] == 'i' || cp[0] == 'I') &&
2281 		    (cp[1] == 'n' || cp[1] == 'N') && isspace(cp[2])) {
2282 			/* Find next token */
2283 			cp += 3;
2284 			while (isspace(*cp))
2285 				++cp;
2286 		} else if ((cp[0] == 'c' || cp[0] == 'C') &&
2287 		    isspace(cp[5]) && strncasecmp(cp, "chaos", 5) == 0) {
2288 			/* Find next token */
2289 			cp += 5;
2290 			while (isspace(*cp))
2291 				++cp;
2292 		}
2293 
2294 		/* Find end of record type, converting to lowercase */
2295 		rtype = cp;
2296 		for (rtype = cp; !isspace(*cp) && *cp != '\0'; ++cp)
2297 			if (isupper(*cp))
2298 				*cp = tolower(*cp);
2299 		*cp++ = '\0';
2300 
2301 		/* Find "the rest" */
2302 		while (isspace(*cp))
2303 			++cp;
2304 
2305 		/* Check for non-ptr names with dots but no trailing dot */
2306 		if (!isdigit(*name) &&
2307 		    checkdots(name) &&
2308 		    strcmp(domain, ".") != 0 &&
2309 		    !isip6arpa(domain)) {
2310 			++errors;
2311 			fprintf(stderr,
2312 			  "%s: %s/%s:%d \"%s\" name missing trailing dot: %s\n",
2313 			    prog, cwd, file, n, rtype, name);
2314 		}
2315 
2316 		/* Check for FQDNs outside the zone */
2317 		cp2 = name + strlen(name) - 1;
2318 		if (cp2 >= name && *cp2 == '.' && strchr(name, '.') != NULL) {
2319 			cp2 = name + strlen(name) - strlen(zone);
2320 			if (cp2 >= name && strcasecmp(cp2, zone) != 0) {
2321 				++errors;
2322 				fprintf(stderr,
2323 				    "%s: %s/%s:%d \"%s\" outside zone %s\n",
2324 				    prog, cwd, file, n, name, zone);
2325 			}
2326 		}
2327 
2328 		rrtype = txt2rrtype(rtype);
2329 		switch (rrtype) {
2330 
2331 		case RR_A:
2332 			/* Handle "a" record */
2333 			add_domain(name, domain);
2334 			p = extractaddr(cp, ap);
2335 			if (p != NULL) {
2336 				++errors;
2337 				cp2 = cp + strlen(cp) - 1;
2338 				if (cp2 >= cp && *cp2 == '\n')
2339 					*cp2 = '\0';
2340 				fprintf(stderr,
2341 			    "%s: %s/%s:%d Bad \"a\" record ip addr \"%s\"\n",
2342 				    prog, cwd, file, n, cp);
2343 				continue;
2344 			}
2345 			if (ap->family != AF_INET) {
2346 				++errors;
2347 				cp2 = cp + strlen(cp) - 1;
2348 				if (cp2 >= cp && *cp2 == '\n')
2349 					*cp2 = '\0';
2350 				fprintf(stderr,
2351 			    "%s: %s/%s:%d \"a\"record not AF_INET \"%s\"\n",
2352 				    prog, cwd, file, n, cp);
2353 				continue;
2354 			}
2355 			errors += updateitem(name, ap, REC_A, ttl, 0);
2356 			break;
2357 
2358 		case RR_AAAA:
2359 			/* Handle "aaaa" record */
2360 			add_domain(name, domain);
2361 			p = extractaddr(cp, ap);
2362 			if (p != NULL) {
2363 				++errors;
2364 				cp2 = cp + strlen(cp) - 1;
2365 				if (cp2 >= cp && *cp2 == '\n')
2366 					*cp2 = '\0';
2367 				fprintf(stderr,
2368 			    "%s: %s/%s:%d Bad \"aaaa\" record ip addr \"%s\"\n",
2369 				    prog, cwd, file, n, cp);
2370 				continue;
2371 			}
2372 			if (ap->family != AF_INET6) {
2373 				++errors;
2374 				cp2 = cp + strlen(cp) - 1;
2375 				if (cp2 >= cp && *cp2 == '\n')
2376 					*cp2 = '\0';
2377 				fprintf(stderr,
2378 			    "%s: %s/%s:%d \"aaaa\"record not AF_INET6 \"%s\"\n",
2379 				    prog, cwd, file, n, cp);
2380 				continue;
2381 			}
2382 			errors += updateitem(name, ap, REC_AAAA, ttl, 0);
2383 			break;
2384 
2385 		case RR_PTR:
2386 			/* Handle "ptr" record */
2387 			add_domain(name, domain);
2388 			if (strcmp(cp, "@") == 0)
2389 				(void)strcpy(cp, zone);
2390 			if (checkdots(cp)) {
2391 				++errors;
2392 				fprintf(stderr,
2393 				    checkaddr(cp) ? addrfmt : dotfmt,
2394 				    prog, cwd, file, n, rtype, cp);
2395 			}
2396 			add_domain(cp, domain);
2397 			p = parseptr(name, ap);
2398 			if (p != NULL) {
2399 				++errors;
2400 				fprintf(stderr,
2401 			"%s: %s/%s:%d Bad \"ptr\" record (%s) ip addr \"%s\"\n",
2402 				    prog, cwd, file, n, p, name);
2403 				continue;
2404 			}
2405 			errors += updateitem(cp, ap, REC_PTR, 0, 0);
2406 			break;
2407 
2408 		case RR_SOA:
2409 			/* Handle "soa" record */
2410 			if (!CHECKDOT(name)) {
2411 				add_domain(name, domain);
2412 				errors += updateitem(name, NULL, REC_SOA, 0, 0);
2413 			}
2414 			errstr = NULL;
2415 			if (!parsesoa(cp, &errstr))
2416 				++sawsoa;
2417 			if (errstr != NULL) {
2418 				++errors;
2419 				fprintf(stderr,
2420 				    "%s: %s/%s:%d Bad \"soa\" record (%s)\n",
2421 				    prog, cwd, file, n, errstr);
2422 				continue;
2423 			}
2424 			break;
2425 
2426 		case RR_WKS:
2427 			/* Handle "wks" record */
2428 			p = extractaddr(cp, ap);
2429 			if (p != NULL) {
2430 				++errors;
2431 				cp2 = cp;
2432 				while (!isspace(*cp2) && *cp2 != '\0')
2433 					++cp2;
2434 				*cp2 = '\0';
2435 				fprintf(stderr,
2436 			    "%s: %s/%s:%d Bad \"wks\" record ip addr \"%s\"\n",
2437 				    prog, cwd, file, n, cp);
2438 				continue;
2439 			}
2440 			/* Step over ip address */
2441 			while (*cp == '.' || isdigit(*cp))
2442 				++cp;
2443 			while (isspace(*cp))
2444 				*cp++ = '\0';
2445 			/* Make sure services are legit */
2446 			errstr = NULL;
2447 			n += checkwks(f, cp, &smtp, &errstr);
2448 			if (errstr != NULL) {
2449 				++errors;
2450 				fprintf(stderr,
2451 				    "%s: %s/%s:%d Bad \"wks\" record (%s)\n",
2452 				    prog, cwd, file, n, errstr);
2453 				continue;
2454 			}
2455 			add_domain(name, domain);
2456 			errors += updateitem(name, ap, REC_WKS,
2457 			    0, smtp ? FLG_SMTPWKS : 0);
2458 			/* XXX check to see if ip address records exists? */
2459 			break;
2460 
2461 		case RR_HINFO:
2462 			/* Handle "hinfo" record */
2463 			add_domain(name, domain);
2464 			errors += updateitem(name, NULL, REC_HINFO, 0, 0);
2465 			cp2 = cp;
2466 			cp = parsequoted(cp);
2467 			if (cp == NULL) {
2468 				++errors;
2469 				fprintf(stderr,
2470 			    "%s: %s/%s:%d \"hinfo\" missing quote: %s\n",
2471 				    prog, cwd, file, n, cp2);
2472 				continue;
2473 			}
2474 			if (!isspace(*cp)) {
2475 				++errors;
2476 				fprintf(stderr,
2477 			    "%s: %s/%s:%d \"hinfo\" missing white space: %s\n",
2478 				    prog, cwd, file, n, cp2);
2479 				continue;
2480 			}
2481 			++cp;
2482 			while (isspace(*cp))
2483 				++cp;
2484 			if (*cp == '\0') {
2485 				++errors;
2486 				fprintf(stderr,
2487 			    "%s: %s/%s:%d \"hinfo\" missing keyword: %s\n",
2488 				    prog, cwd, file, n, cp2);
2489 				continue;
2490 			}
2491 			cp = parsequoted(cp);
2492 			if (cp == NULL) {
2493 				++errors;
2494 				fprintf(stderr,
2495 			    "%s: %s/%s:%d \"hinfo\" missing quote: %s\n",
2496 				    prog, cwd, file, n, cp2);
2497 				continue;
2498 			}
2499 			if (*cp != '\0') {
2500 				++errors;
2501 				fprintf(stderr,
2502 			"%s: %s/%s:%d \"hinfo\" garbage after keywords: %s\n",
2503 				    prog, cwd, file, n, cp2);
2504 				continue;
2505 			}
2506 			break;
2507 
2508 		case RR_MX:
2509 			/* Handle "mx" record */
2510 			add_domain(name, domain);
2511 			errors += updateitem(name, NULL, REC_MX, ttl, 0);
2512 
2513 			/* Look for priority */
2514 			if (!isdigit(*cp)) {
2515 				++errors;
2516 				fprintf(stderr,
2517 				    "%s: %s/%s:%d Bad \"mx\" priority: %s\n",
2518 				    prog, cwd, file, n, cp);
2519 			}
2520 
2521 			/* Skip over priority */
2522 			++cp;
2523 			while (isdigit(*cp))
2524 				++cp;
2525 			while (isspace(*cp))
2526 				++cp;
2527 			if (*cp == '\0') {
2528 				++errors;
2529 				fprintf(stderr,
2530 				    "%s: %s/%s:%d Missing \"mx\" hostname\n",
2531 				    prog, cwd, file, n);
2532 			}
2533 			if (strcmp(cp, "@") == 0)
2534 				(void)strcpy(cp, zone);
2535 			if (checkdots(cp)) {
2536 				++errors;
2537 				fprintf(stderr,
2538 				    checkaddr(cp) ? addrfmt : dotfmt,
2539 				    prog, cwd, file, n, rtype, cp);
2540 			}
2541 
2542 			/* Check to see if mx host exists */
2543 			add_domain(cp, domain);
2544 			flags = FLG_MXREF;
2545 			if (*name == *cp && strcmp(name, cp) == 0)
2546 				flags |= FLG_SELFMX;
2547 			errors += updateitem(cp, NULL, REC_REF, 0, flags);
2548 			break;
2549 
2550 		case RR_CNAME:
2551 			/* Handle "cname" record */
2552 			add_domain(name, domain);
2553 			errors += updateitem(name, NULL, REC_CNAME, 0, 0);
2554 			if (checkdots(cp)) {
2555 				++errors;
2556 				fprintf(stderr,
2557 				    checkaddr(cp) ? addrfmt : dotfmt,
2558 				    prog, cwd, file, n, rtype, cp);
2559 			}
2560 
2561 			/* Make sure cname points somewhere */
2562 			if (strcmp(cp, "@") == 0)
2563 				(void)strcpy(cp, zone);
2564 			add_domain(cp, domain);
2565 			errors += updateitem(cp, NULL, REC_REF, 0, 0);
2566 			break;
2567 
2568 		case RR_SRV:
2569 			/* Handle "srv" record */
2570 			add_domain(name, domain);
2571 			errors += updateitem(name, NULL, REC_SRV, 0, 0);
2572 			cp2 = cp;
2573 
2574 			/* Skip over three values */
2575 			for (i = 0; i < 3; ++i) {
2576 				if (!isdigit(*cp)) {
2577 					++errors;
2578 					fprintf(stderr, "%s: %s/%s:%d"
2579 					    " Bad \"srv\" value: %s\n",
2580 					    prog, cwd, file, n, cp);
2581 				}
2582 
2583 				/* Skip over value */
2584 				++cp;
2585 				while (isdigit(*cp))
2586 					++cp;
2587 				while (isspace(*cp))
2588 					++cp;
2589 			}
2590 
2591 			/* Check to see if mx host exists */
2592 			add_domain(cp, domain);
2593 			errors += updateitem(cp, NULL, REC_REF, 0, 0);
2594 			break;
2595 
2596 		case RR_TXT:
2597 			/* Handle "txt" record */
2598 			add_domain(name, domain);
2599 			errors += updateitem(name, NULL, REC_TXT, 0, 0);
2600 			cp2 = cp;
2601 			cp = parsequoted(cp);
2602 			if (cp == NULL) {
2603 				++errors;
2604 				fprintf(stderr,
2605 				    "%s: %s/%s:%d \"txt\" missing quote: %s\n",
2606 				    prog, cwd, file, n, cp2);
2607 				continue;
2608 			}
2609 			while (isspace(*cp))
2610 				++cp;
2611 			if (*cp != '\0') {
2612 				++errors;
2613 				fprintf(stderr,
2614 			    "%s: %s/%s:%d \"txt\" garbage after text: %s\n",
2615 				    prog, cwd, file, n, cp2);
2616 				continue;
2617 			}
2618 			break;
2619 
2620 		case RR_NS:
2621 			/* Handle "ns" record */
2622 			errors += updateitem(zone, NULL, REC_NS, 0, 0);
2623 			if (strcmp(cp, "@") == 0)
2624 				(void)strcpy(cp, zone);
2625 			if (checkdots(cp)) {
2626 				++errors;
2627 				fprintf(stderr,
2628 				    checkaddr(cp) ? addrfmt : dotfmt,
2629 				    prog, cwd, file, n, rtype, cp);
2630 			}
2631 			add_domain(cp, domain);
2632 			errors += updateitem(cp, NULL, REC_REF, 0, 0);
2633 			break;
2634 
2635 		case RR_RP:
2636 			/* Handle "rp" record */
2637 			add_domain(name, domain);
2638 			errors += updateitem(name, NULL, REC_RP, 0, 0);
2639 			cp2 = cp;
2640 
2641 			/* Step over mailbox name */
2642 			/* XXX could add_domain() and check further */
2643 			while (!isspace(*cp) && *cp != '\0')
2644 				++cp;
2645 			if (*cp == '\0') {
2646 				++errors;
2647 				fprintf(stderr,
2648 			    "%s: %s/%s:%d \"rp\" missing text name: %s\n",
2649 				    prog, cwd, file, n, cp2);
2650 				continue;
2651 			}
2652 			++cp;
2653 			cp3 = cp;
2654 
2655 			/* Step over text name */
2656 			while (!isspace(*cp) && *cp != '\0')
2657 				++cp;
2658 
2659 			if (*cp != '\0') {
2660 				++errors;
2661 				fprintf(stderr,
2662 			    "%s: %s/%s:%d \"rp\" garbage after text name: %s\n",
2663 				    prog, cwd, file, n, cp2);
2664 				continue;
2665 			}
2666 
2667 			/* Make sure text name points somewhere (if not ".") */
2668 			if (!CHECKDOT(cp3)) {
2669 				add_domain(cp3, domain);
2670 				errors += updateitem(cp3, NULL, REC_REF, 0, 0);
2671 			}
2672 			break;
2673 
2674 		case RR_ALLOWDUPA:
2675 			/* Handle "allow duplicate a" record */
2676 			add_domain(name, domain);
2677 			p = extractaddr(cp, ap);
2678 			if (p != NULL) {
2679 				++errors;
2680 				cp2 = cp + strlen(cp) - 1;
2681 				if (cp2 >= cp && *cp2 == '\n')
2682 					*cp2 = '\0';
2683 				fprintf(stderr,
2684 		    "%s: %s/%s:%d Bad \"allowdupa\" record ip addr \"%s\"\n",
2685 				    prog, cwd, file, n, cp);
2686 				continue;
2687 			}
2688 			errors += updateitem(name, ap, 0, 0, FLG_ALLOWDUPA);
2689 			break;
2690 
2691 		case RR_DNSKEY:
2692 			/* Handle "dnskey" record */
2693 			add_domain(name, domain);
2694 			errors += updateitem(name, NULL, REC_CNAME, 0, 0);
2695 			if (checkdots(cp)) {
2696 				++errors;
2697 				fprintf(stderr,
2698 				    checkaddr(cp) ? addrfmt : dotfmt,
2699 				    prog, cwd, file, n, rtype, cp);
2700 			}
2701 
2702 			/* Make sure cname points somewhere */
2703 			if (strcmp(cp, "@") == 0)
2704 				(void)strcpy(cp, zone);
2705 			add_domain(cp, domain);
2706 			errors += updateitem(cp, NULL, REC_REF, 0, 0);
2707 			break;
2708 
2709 		case RR_RRSIG:
2710 			errstr = NULL;
2711 			if (!parserrsig(cp, &errstr))
2712 				++sawrrsig;
2713 			if (errstr != NULL) {
2714 				++errors;
2715 				fprintf(stderr,
2716 				    "%s: %s/%s:%d Bad \"rrsig\" record (%s)\n",
2717 				    prog, cwd, file, n, errstr);
2718 				continue;
2719 			}
2720 			break;
2721 
2722 		case RR_NSEC:
2723 			/* XXX */
2724 			continue;
2725 
2726 		default:
2727 			/* Unknown record type */
2728 			++errors;
2729 			fprintf(stderr,
2730 			    "%s: %s/%s:%d Unknown record type \"%s\"\n",
2731 			    prog, cwd, file, n, rtype);
2732 			add_domain(name, domain);
2733 			errors += updateitem(name, NULL, REC_UNKNOWN, 0, 0);
2734 			break;
2735 		}
2736 		(void)strcpy(lastname, name);
2737 	}
2738 	(void)fclose(f);
2739 	return;
2740 }
2741 
2742 static const char *microlist[] = {
2743 	"_tcp",
2744 	"_udp",
2745 	"_msdcs",
2746 	"_sites",
2747 	NULL
2748 };
2749 
2750 int
rfc1034host(const char * host,int recs)2751 rfc1034host(const char *host, int recs)
2752 {
2753 	const char *cp, **p;
2754 	int underok;
2755 
2756 	underok = 0;
2757 	for (p = microlist; *p != NULL ;++p)
2758 		if ((cp = strstr(host, *p)) != NULL &&
2759 		    cp > host &&
2760 		    cp[-1] == '.' &&
2761 		    cp[strlen(*p)] == '.') {
2762 			++underok;
2763 			break;
2764 		}
2765 
2766 	cp = host;
2767 	if (!(isalpha(*cp) || isdigit(*cp) || (*cp == '_' && underok))) {
2768 		fprintf(stderr,
2769 	    "%s: illegal hostname \"%s\" (starts with non-alpha/numeric)\n",
2770 		    prog, host);
2771 		return (1);
2772 	}
2773 	for (++cp; *cp != '.' && *cp != '\0'; ++cp)
2774 		if (!(isalpha(*cp) || isdigit(*cp) || *cp == '-' ||
2775 		    (*cp == '/' && (recs & REC_SOA) != 0))) {
2776 			fprintf(stderr,
2777 		    "%s: Illegal hostname \"%s\" ('%c' illegal character)\n",
2778 			    prog, host, *cp);
2779 			return (1);
2780 		}
2781 	if (--cp >= host && *cp == '-') {
2782 		fprintf(stderr, "%s: Illegal hostname \"%s\" (ends with '-')\n",
2783 		    prog, host);
2784 		return (1);
2785 	}
2786 	return (0);
2787 }
2788 
2789 enum rrtype
txt2rrtype(const char * str)2790 txt2rrtype(const char *str)
2791 {
2792 	if (strcasecmp(str, "aaaa") == 0)
2793 		return (RR_AAAA);
2794 	if (strcasecmp(str, "a") == 0)
2795 		return (RR_A);
2796 	if (strcasecmp(str, "allowdupa") == 0)
2797 		return (RR_ALLOWDUPA);
2798 	if (strcasecmp(str, "cname") == 0)
2799 		return (RR_CNAME);
2800 	if (strcasecmp(str, "dnskey") == 0)
2801 		return (RR_DNSKEY);
2802 	if (strcasecmp(str, "hinfo") == 0)
2803 		return (RR_HINFO);
2804 	if (strcasecmp(str, "mx") == 0)
2805 		return (RR_MX);
2806 	if (strcasecmp(str, "ns") == 0)
2807 		return (RR_NS);
2808 	if (strcasecmp(str, "ptr") == 0)
2809 		return (RR_PTR);
2810 	if (strcasecmp(str, "rp") == 0)
2811 		return (RR_RP);
2812 	if (strcasecmp(str, "soa") == 0)
2813 		return (RR_SOA);
2814 	if (strcasecmp(str, "srv") == 0)
2815 		return (RR_SRV);
2816 	if (strcasecmp(str, "txt") == 0)
2817 		return (RR_TXT);
2818 	if (strcasecmp(str, "wks") == 0)
2819 		return (RR_WKS);
2820 	if (strcasecmp(str, "RRSIG") == 0)
2821 		return (RR_RRSIG);
2822 	if (strcasecmp(str, "NSEC") == 0)
2823 		return (RR_NSEC);
2824 	return (RR_UNDEF);
2825 }
2826 
2827 int
samesubnet(struct addr * a1,struct addr * a2,struct network * np)2828 samesubnet(struct addr *a1, struct addr *a2, struct network *np)
2829 {
2830 	int i;
2831 	u_int32_t v1, v2;
2832 
2833 	/* IPv4 before IPv6 */
2834 	if (a1->family != a2->family)
2835 		return (0);
2836 
2837 	switch (a1->family) {
2838 
2839 	case AF_INET:
2840 		/* Apply the mask to both values */
2841 		v1 = a1->a_addr4 & np->n_mask4;
2842 		v2 = a2->a_addr4 & np->n_mask4;
2843 		return (v1 == v2);
2844 
2845 	case AF_INET6:
2846 		/* Apply the mask to both values */
2847 		for (i = 0; i < 16; ++i) {
2848 			v1 = a1->a_addr6[i] & np->n_mask6[i];
2849 			v2 = a2->a_addr6[i] & np->n_mask6[i];
2850 			if (v1 != v2)
2851 				return (0);
2852 		}
2853 		break;
2854 
2855 	default:
2856 		abort();
2857 	}
2858 	return (1);
2859 }
2860 
2861 /* Set address mask in network order */
2862 void
setmaskwidth(u_int w,struct network * np)2863 setmaskwidth(u_int w, struct network *np)
2864 {
2865 	int i, j;
2866 
2867 	switch (np->family) {
2868 
2869 	case AF_INET:
2870 		if (w <= 0)
2871 			np->n_mask4 = 0;
2872 		else
2873 			np->n_mask4 = htonl(0xffffffff << (32 - w));
2874 		break;
2875 
2876 	case AF_INET6:
2877 		/* XXX is this right? */
2878 		memset(np->n_mask6, 0, sizeof(np->n_mask6));
2879 		for (i = 0; i < w / 8; ++i)
2880 			np->n_mask6[i] = 0xff;
2881 		i = w / 8;
2882 		j = w % 8;
2883 		if (j > 0 && i < 16)
2884 			np->n_mask6[i] = 0xff << (8 - j);
2885 		break;
2886 
2887 	default:
2888 		abort();
2889 	}
2890 }
2891 
2892 int
updateitem(const char * host,struct addr * ap,int records,u_int ttl,int flags)2893 updateitem(const char *host, struct addr *ap, int records, u_int ttl, int flags)
2894 {
2895 	const char *ccp;
2896 	int n, errs;
2897 	u_int i;
2898 	struct item *ip;
2899 	int foundsome;
2900 
2901 	n = 0;
2902 	foundsome = 0;
2903 	errs = 0;
2904 
2905 	/* Hash the host name */
2906 	i = 0;
2907 	ccp = host;
2908 	while (*ccp != '\0')
2909 		i = i * 37 + *ccp++;
2910 	ip = &items[i & (ITEMSIZE - 1)];
2911 
2912 	/* Look for a match or any empty slot */
2913 	while (n < ITEMSIZE && ip->host != NULL) {
2914 
2915 		if ((ap == NULL || ip->addr.family == 0 ||
2916 		    cmpaddr(ap, &ip->addr) == 0) &&
2917 		    *host == *ip->host && strcmp(host, ip->host) == 0) {
2918 			++foundsome;
2919 			if (ip->addr.family == 0 && ap != NULL)
2920 				memmove(&ip->addr, ap, sizeof(*ap));
2921 			if ((records & MASK_TEST_DUP) != 0)
2922 				checkdups(ip, records);
2923 			ip->records |= records;
2924 			/* Only check differing ttl's for A and MX records */
2925 			if (ip->ttl == 0)
2926 				ip->ttl = ttl;
2927 			else if (ttl != 0 && ip->ttl != ttl) {
2928 				fprintf(stderr,
2929 				    "%s: Differing ttls for %s (%u != %u)\n",
2930 				    prog, ip->host, ttl, ip->ttl);
2931 				++errs;
2932 			}
2933 			ip->flags |= flags;
2934 			/* Not done if we wildcard matched the name */
2935 			if (ap != NULL)
2936 				return (errs);
2937 		}
2938 		++n;
2939 		++ip;
2940 		if (ip >= &items[ITEMSIZE])
2941 			ip = items;
2942 	}
2943 
2944 	if (n >= ITEMSIZE) {
2945 		fprintf(stderr, "%s: Out of item slots (max %d)\n",
2946 		    prog, ITEMSIZE);
2947 		exit(1);
2948 	}
2949 
2950 	/* Done if we were wildcarding the name (and found entries for it) */
2951 	if (ap == NULL && foundsome) {
2952 		return (errs);
2953 	}
2954 
2955 	/* Didn't find it, make new entry */
2956 	++itemcnt;
2957 	if (ip->host) {
2958 		fprintf(stderr, "%s: Reusing bucket!\n", prog);
2959 		exit(1);
2960 	}
2961 	if (ap != NULL)
2962 		memmove(&ip->addr, ap, sizeof(*ap));
2963 	ip->host = savestr(host);
2964 	if ((records & MASK_TEST_DUP) != 0)
2965 		checkdups(ip, records);
2966 	ip->records |= records;
2967 	if (ttl != 0)
2968 		ip->ttl = ttl;
2969 	ip->flags |= flags;
2970 	return (errs);
2971 }
2972 
2973 void
usage(void)2974 usage(void)
2975 {
2976 
2977 	fprintf(stderr, "Version %s\n", version);
2978 	fprintf(stderr, "usage: %s [-d] [-b named.boot] [-B nslint.boot]\n",
2979 	    prog);
2980 	fprintf(stderr, "       %s [-d] [-c named.conf] [-C nslint.conf]\n",
2981 	    prog);
2982 	exit(1);
2983 }
2984