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