xref: /netbsd-src/external/mpl/bind/dist/bin/check/named-checkzone.c (revision a8c74629f602faa0ccf8a463757d7baf858bbf3a)
1 /*	$NetBSD: named-checkzone.c,v 1.5 2020/08/03 17:23:36 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * This Source Code Form is subject to the terms of the Mozilla Public
7  * License, v. 2.0. If a copy of the MPL was not distributed with this
8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * See the COPYRIGHT file distributed with this work for additional
11  * information regarding copyright ownership.
12  */
13 
14 /*! \file */
15 
16 #include <inttypes.h>
17 #include <stdbool.h>
18 #include <stdlib.h>
19 
20 #include <isc/app.h>
21 #include <isc/commandline.h>
22 #include <isc/dir.h>
23 #include <isc/hash.h>
24 #include <isc/log.h>
25 #include <isc/mem.h>
26 #include <isc/print.h>
27 #include <isc/socket.h>
28 #include <isc/string.h>
29 #include <isc/task.h>
30 #include <isc/timer.h>
31 #include <isc/util.h>
32 
33 #include <dns/db.h>
34 #include <dns/fixedname.h>
35 #include <dns/log.h>
36 #include <dns/master.h>
37 #include <dns/masterdump.h>
38 #include <dns/name.h>
39 #include <dns/rdataclass.h>
40 #include <dns/rdataset.h>
41 #include <dns/result.h>
42 #include <dns/types.h>
43 #include <dns/zone.h>
44 
45 #include "check-tool.h"
46 
47 static int quiet = 0;
48 static isc_mem_t *mctx = NULL;
49 dns_zone_t *zone = NULL;
50 dns_zonetype_t zonetype = dns_zone_master;
51 static int dumpzone = 0;
52 static const char *output_filename;
53 static const char *prog_name = NULL;
54 static const dns_master_style_t *outputstyle = NULL;
55 static enum { progmode_check, progmode_compile } progmode;
56 
57 #define ERRRET(result, function)                                              \
58 	do {                                                                  \
59 		if (result != ISC_R_SUCCESS) {                                \
60 			if (!quiet)                                           \
61 				fprintf(stderr, "%s() returned %s\n",         \
62 					function, dns_result_totext(result)); \
63 			return (result);                                      \
64 		}                                                             \
65 	} while (/*CONSTCOND*/0)
66 
67 ISC_PLATFORM_NORETURN_PRE static void
68 usage(void) ISC_PLATFORM_NORETURN_POST;
69 
70 static void
71 usage(void) {
72 	fprintf(stderr,
73 		"usage: %s [-djqvD] [-c class] "
74 		"[-f inputformat] [-F outputformat] [-J filename] "
75 		"[-s (full|relative)] [-t directory] [-w directory] "
76 		"[-k (ignore|warn|fail)] [-m (ignore|warn|fail)] "
77 		"[-n (ignore|warn|fail)] [-r (ignore|warn|fail)] "
78 		"[-i (full|full-sibling|local|local-sibling|none)] "
79 		"[-M (ignore|warn|fail)] [-S (ignore|warn|fail)] "
80 		"[-W (ignore|warn)] "
81 		"%s zonename filename\n",
82 		prog_name,
83 		progmode == progmode_check ? "[-o filename]" : "-o filename");
84 	exit(1);
85 }
86 
87 static void
88 destroy(void) {
89 	if (zone != NULL) {
90 		dns_zone_detach(&zone);
91 	}
92 }
93 
94 /*% main processing routine */
95 int
96 main(int argc, char **argv) {
97 	int c;
98 	char *origin = NULL;
99 	char *filename = NULL;
100 	isc_log_t *lctx = NULL;
101 	isc_result_t result;
102 	char classname_in[] = "IN";
103 	char *classname = classname_in;
104 	const char *workdir = NULL;
105 	const char *inputformatstr = NULL;
106 	const char *outputformatstr = NULL;
107 	dns_masterformat_t inputformat = dns_masterformat_text;
108 	dns_masterformat_t outputformat = dns_masterformat_text;
109 	dns_masterrawheader_t header;
110 	uint32_t rawversion = 1, serialnum = 0;
111 	dns_ttl_t maxttl = 0;
112 	bool snset = false;
113 	bool logdump = false;
114 	FILE *errout = stdout;
115 	char *endp;
116 
117 	/*
118 	 * Uncomment the following line if memory debugging is needed:
119 	 * isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
120 	 */
121 
122 	outputstyle = &dns_master_style_full;
123 
124 	prog_name = strrchr(argv[0], '/');
125 	if (prog_name == NULL) {
126 		prog_name = strrchr(argv[0], '\\');
127 	}
128 	if (prog_name != NULL) {
129 		prog_name++;
130 	} else {
131 		prog_name = argv[0];
132 	}
133 	/*
134 	 * Libtool doesn't preserve the program name prior to final
135 	 * installation.  Remove the libtool prefix ("lt-").
136 	 */
137 	if (strncmp(prog_name, "lt-", 3) == 0) {
138 		prog_name += 3;
139 	}
140 
141 #define PROGCMP(X) \
142 	(strcasecmp(prog_name, X) == 0 || strcasecmp(prog_name, X ".exe") == 0)
143 
144 	if (PROGCMP("named-checkzone")) {
145 		progmode = progmode_check;
146 	} else if (PROGCMP("named-compilezone")) {
147 		progmode = progmode_compile;
148 	} else {
149 		INSIST(0);
150 		ISC_UNREACHABLE();
151 	}
152 
153 	/* Compilation specific defaults */
154 	if (progmode == progmode_compile) {
155 		zone_options |= (DNS_ZONEOPT_CHECKNS | DNS_ZONEOPT_FATALNS |
156 				 DNS_ZONEOPT_CHECKSPF | DNS_ZONEOPT_CHECKDUPRR |
157 				 DNS_ZONEOPT_CHECKNAMES |
158 				 DNS_ZONEOPT_CHECKNAMESFAIL |
159 				 DNS_ZONEOPT_CHECKWILDCARD);
160 	} else {
161 		zone_options |= (DNS_ZONEOPT_CHECKDUPRR | DNS_ZONEOPT_CHECKSPF);
162 	}
163 
164 #define ARGCMP(X) (strcmp(isc_commandline_argument, X) == 0)
165 
166 	isc_commandline_errprint = false;
167 
168 	while ((c = isc_commandline_parse(argc, argv,
169 					  "c:df:hi:jJ:k:L:l:m:n:qr:s:t:o:vw:DF:"
170 					  "M:S:T:W:")) != EOF)
171 	{
172 		switch (c) {
173 		case 'c':
174 			classname = isc_commandline_argument;
175 			break;
176 
177 		case 'd':
178 			debug++;
179 			break;
180 
181 		case 'i':
182 			if (ARGCMP("full")) {
183 				zone_options |= DNS_ZONEOPT_CHECKINTEGRITY |
184 						DNS_ZONEOPT_CHECKSIBLING;
185 				docheckmx = true;
186 				docheckns = true;
187 				dochecksrv = true;
188 			} else if (ARGCMP("full-sibling")) {
189 				zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
190 				zone_options &= ~DNS_ZONEOPT_CHECKSIBLING;
191 				docheckmx = true;
192 				docheckns = true;
193 				dochecksrv = true;
194 			} else if (ARGCMP("local")) {
195 				zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
196 				zone_options |= DNS_ZONEOPT_CHECKSIBLING;
197 				docheckmx = false;
198 				docheckns = false;
199 				dochecksrv = false;
200 			} else if (ARGCMP("local-sibling")) {
201 				zone_options |= DNS_ZONEOPT_CHECKINTEGRITY;
202 				zone_options &= ~DNS_ZONEOPT_CHECKSIBLING;
203 				docheckmx = false;
204 				docheckns = false;
205 				dochecksrv = false;
206 			} else if (ARGCMP("none")) {
207 				zone_options &= ~DNS_ZONEOPT_CHECKINTEGRITY;
208 				zone_options &= ~DNS_ZONEOPT_CHECKSIBLING;
209 				docheckmx = false;
210 				docheckns = false;
211 				dochecksrv = false;
212 			} else {
213 				fprintf(stderr, "invalid argument to -i: %s\n",
214 					isc_commandline_argument);
215 				exit(1);
216 			}
217 			break;
218 
219 		case 'f':
220 			inputformatstr = isc_commandline_argument;
221 			break;
222 
223 		case 'F':
224 			outputformatstr = isc_commandline_argument;
225 			break;
226 
227 		case 'j':
228 			nomerge = false;
229 			break;
230 
231 		case 'J':
232 			journal = isc_commandline_argument;
233 			nomerge = false;
234 			break;
235 
236 		case 'k':
237 			if (ARGCMP("warn")) {
238 				zone_options |= DNS_ZONEOPT_CHECKNAMES;
239 				zone_options &= ~DNS_ZONEOPT_CHECKNAMESFAIL;
240 			} else if (ARGCMP("fail")) {
241 				zone_options |= DNS_ZONEOPT_CHECKNAMES |
242 						DNS_ZONEOPT_CHECKNAMESFAIL;
243 			} else if (ARGCMP("ignore")) {
244 				zone_options &= ~(DNS_ZONEOPT_CHECKNAMES |
245 						  DNS_ZONEOPT_CHECKNAMESFAIL);
246 			} else {
247 				fprintf(stderr, "invalid argument to -k: %s\n",
248 					isc_commandline_argument);
249 				exit(1);
250 			}
251 			break;
252 
253 		case 'L':
254 			snset = true;
255 			endp = NULL;
256 			serialnum = strtol(isc_commandline_argument, &endp, 0);
257 			if (*endp != '\0') {
258 				fprintf(stderr, "source serial number "
259 						"must be numeric");
260 				exit(1);
261 			}
262 			break;
263 
264 		case 'l':
265 			zone_options |= DNS_ZONEOPT_CHECKTTL;
266 			endp = NULL;
267 			maxttl = strtol(isc_commandline_argument, &endp, 0);
268 			if (*endp != '\0') {
269 				fprintf(stderr, "maximum TTL "
270 						"must be numeric");
271 				exit(1);
272 			}
273 			break;
274 
275 		case 'n':
276 			if (ARGCMP("ignore")) {
277 				zone_options &= ~(DNS_ZONEOPT_CHECKNS |
278 						  DNS_ZONEOPT_FATALNS);
279 			} else if (ARGCMP("warn")) {
280 				zone_options |= DNS_ZONEOPT_CHECKNS;
281 				zone_options &= ~DNS_ZONEOPT_FATALNS;
282 			} else if (ARGCMP("fail")) {
283 				zone_options |= DNS_ZONEOPT_CHECKNS |
284 						DNS_ZONEOPT_FATALNS;
285 			} else {
286 				fprintf(stderr, "invalid argument to -n: %s\n",
287 					isc_commandline_argument);
288 				exit(1);
289 			}
290 			break;
291 
292 		case 'm':
293 			if (ARGCMP("warn")) {
294 				zone_options |= DNS_ZONEOPT_CHECKMX;
295 				zone_options &= ~DNS_ZONEOPT_CHECKMXFAIL;
296 			} else if (ARGCMP("fail")) {
297 				zone_options |= DNS_ZONEOPT_CHECKMX |
298 						DNS_ZONEOPT_CHECKMXFAIL;
299 			} else if (ARGCMP("ignore")) {
300 				zone_options &= ~(DNS_ZONEOPT_CHECKMX |
301 						  DNS_ZONEOPT_CHECKMXFAIL);
302 			} else {
303 				fprintf(stderr, "invalid argument to -m: %s\n",
304 					isc_commandline_argument);
305 				exit(1);
306 			}
307 			break;
308 
309 		case 'o':
310 			output_filename = isc_commandline_argument;
311 			break;
312 
313 		case 'q':
314 			quiet++;
315 			break;
316 
317 		case 'r':
318 			if (ARGCMP("warn")) {
319 				zone_options |= DNS_ZONEOPT_CHECKDUPRR;
320 				zone_options &= ~DNS_ZONEOPT_CHECKDUPRRFAIL;
321 			} else if (ARGCMP("fail")) {
322 				zone_options |= DNS_ZONEOPT_CHECKDUPRR |
323 						DNS_ZONEOPT_CHECKDUPRRFAIL;
324 			} else if (ARGCMP("ignore")) {
325 				zone_options &= ~(DNS_ZONEOPT_CHECKDUPRR |
326 						  DNS_ZONEOPT_CHECKDUPRRFAIL);
327 			} else {
328 				fprintf(stderr, "invalid argument to -r: %s\n",
329 					isc_commandline_argument);
330 				exit(1);
331 			}
332 			break;
333 
334 		case 's':
335 			if (ARGCMP("full")) {
336 				outputstyle = &dns_master_style_full;
337 			} else if (ARGCMP("relative")) {
338 				outputstyle = &dns_master_style_default;
339 			} else {
340 				fprintf(stderr,
341 					"unknown or unsupported style: %s\n",
342 					isc_commandline_argument);
343 				exit(1);
344 			}
345 			break;
346 
347 		case 't':
348 			result = isc_dir_chroot(isc_commandline_argument);
349 			if (result != ISC_R_SUCCESS) {
350 				fprintf(stderr, "isc_dir_chroot: %s: %s\n",
351 					isc_commandline_argument,
352 					isc_result_totext(result));
353 				exit(1);
354 			}
355 			break;
356 
357 		case 'v':
358 			printf(VERSION "\n");
359 			exit(0);
360 
361 		case 'w':
362 			workdir = isc_commandline_argument;
363 			break;
364 
365 		case 'D':
366 			dumpzone++;
367 			break;
368 
369 		case 'M':
370 			if (ARGCMP("fail")) {
371 				zone_options &= ~DNS_ZONEOPT_WARNMXCNAME;
372 				zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
373 			} else if (ARGCMP("warn")) {
374 				zone_options |= DNS_ZONEOPT_WARNMXCNAME;
375 				zone_options &= ~DNS_ZONEOPT_IGNOREMXCNAME;
376 			} else if (ARGCMP("ignore")) {
377 				zone_options |= DNS_ZONEOPT_WARNMXCNAME;
378 				zone_options |= DNS_ZONEOPT_IGNOREMXCNAME;
379 			} else {
380 				fprintf(stderr, "invalid argument to -M: %s\n",
381 					isc_commandline_argument);
382 				exit(1);
383 			}
384 			break;
385 
386 		case 'S':
387 			if (ARGCMP("fail")) {
388 				zone_options &= ~DNS_ZONEOPT_WARNSRVCNAME;
389 				zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
390 			} else if (ARGCMP("warn")) {
391 				zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
392 				zone_options &= ~DNS_ZONEOPT_IGNORESRVCNAME;
393 			} else if (ARGCMP("ignore")) {
394 				zone_options |= DNS_ZONEOPT_WARNSRVCNAME;
395 				zone_options |= DNS_ZONEOPT_IGNORESRVCNAME;
396 			} else {
397 				fprintf(stderr, "invalid argument to -S: %s\n",
398 					isc_commandline_argument);
399 				exit(1);
400 			}
401 			break;
402 
403 		case 'T':
404 			if (ARGCMP("warn")) {
405 				zone_options |= DNS_ZONEOPT_CHECKSPF;
406 			} else if (ARGCMP("ignore")) {
407 				zone_options &= ~DNS_ZONEOPT_CHECKSPF;
408 			} else {
409 				fprintf(stderr, "invalid argument to -T: %s\n",
410 					isc_commandline_argument);
411 				exit(1);
412 			}
413 			break;
414 
415 		case 'W':
416 			if (ARGCMP("warn")) {
417 				zone_options |= DNS_ZONEOPT_CHECKWILDCARD;
418 			} else if (ARGCMP("ignore")) {
419 				zone_options &= ~DNS_ZONEOPT_CHECKWILDCARD;
420 			}
421 			break;
422 
423 		case '?':
424 			if (isc_commandline_option != '?') {
425 				fprintf(stderr, "%s: invalid argument -%c\n",
426 					prog_name, isc_commandline_option);
427 			}
428 		/* FALLTHROUGH */
429 		case 'h':
430 			usage();
431 
432 		default:
433 			fprintf(stderr, "%s: unhandled option -%c\n", prog_name,
434 				isc_commandline_option);
435 			exit(1);
436 		}
437 	}
438 
439 	if (workdir != NULL) {
440 		result = isc_dir_chdir(workdir);
441 		if (result != ISC_R_SUCCESS) {
442 			fprintf(stderr, "isc_dir_chdir: %s: %s\n", workdir,
443 				isc_result_totext(result));
444 			exit(1);
445 		}
446 	}
447 
448 	if (inputformatstr != NULL) {
449 		if (strcasecmp(inputformatstr, "text") == 0) {
450 			inputformat = dns_masterformat_text;
451 		} else if (strcasecmp(inputformatstr, "raw") == 0) {
452 			inputformat = dns_masterformat_raw;
453 		} else if (strncasecmp(inputformatstr, "raw=", 4) == 0) {
454 			inputformat = dns_masterformat_raw;
455 			fprintf(stderr, "WARNING: input format raw, version "
456 					"ignored\n");
457 		} else if (strcasecmp(inputformatstr, "map") == 0) {
458 			inputformat = dns_masterformat_map;
459 		} else {
460 			fprintf(stderr, "unknown file format: %s\n",
461 				inputformatstr);
462 			exit(1);
463 		}
464 	}
465 
466 	if (outputformatstr != NULL) {
467 		if (strcasecmp(outputformatstr, "text") == 0) {
468 			outputformat = dns_masterformat_text;
469 		} else if (strcasecmp(outputformatstr, "raw") == 0) {
470 			outputformat = dns_masterformat_raw;
471 		} else if (strncasecmp(outputformatstr, "raw=", 4) == 0) {
472 			char *end;
473 
474 			outputformat = dns_masterformat_raw;
475 			rawversion = strtol(outputformatstr + 4, &end, 10);
476 			if (end == outputformatstr + 4 || *end != '\0' ||
477 			    rawversion > 1U) {
478 				fprintf(stderr, "unknown raw format version\n");
479 				exit(1);
480 			}
481 		} else if (strcasecmp(outputformatstr, "map") == 0) {
482 			outputformat = dns_masterformat_map;
483 		} else {
484 			fprintf(stderr, "unknown file format: %s\n",
485 				outputformatstr);
486 			exit(1);
487 		}
488 	}
489 
490 	if (progmode == progmode_compile) {
491 		dumpzone = 1; /* always dump */
492 		logdump = !quiet;
493 		if (output_filename == NULL) {
494 			fprintf(stderr, "output file required, but not "
495 					"specified\n");
496 			usage();
497 		}
498 	}
499 
500 	if (output_filename != NULL) {
501 		dumpzone = 1;
502 	}
503 
504 	/*
505 	 * If we are printing to stdout then send the informational
506 	 * output to stderr.
507 	 */
508 	if (dumpzone &&
509 	    (output_filename == NULL || strcmp(output_filename, "-") == 0 ||
510 	     strcmp(output_filename, "/dev/fd/1") == 0 ||
511 	     strcmp(output_filename, "/dev/stdout") == 0))
512 	{
513 		errout = stderr;
514 		logdump = false;
515 	}
516 
517 	if (isc_commandline_index + 2 != argc) {
518 		usage();
519 	}
520 
521 #ifdef _WIN32
522 	InitSockets();
523 #endif /* ifdef _WIN32 */
524 
525 	isc_mem_create(&mctx);
526 	if (!quiet) {
527 		RUNTIME_CHECK(setup_logging(mctx, errout, &lctx) ==
528 			      ISC_R_SUCCESS);
529 	}
530 
531 	dns_result_register();
532 
533 	origin = argv[isc_commandline_index++];
534 	filename = argv[isc_commandline_index++];
535 	result = load_zone(mctx, origin, filename, inputformat, classname,
536 			   maxttl, &zone);
537 
538 	if (snset) {
539 		dns_master_initrawheader(&header);
540 		header.flags = DNS_MASTERRAW_SOURCESERIALSET;
541 		header.sourceserial = serialnum;
542 		dns_zone_setrawdata(zone, &header);
543 	}
544 
545 	if (result == ISC_R_SUCCESS && dumpzone) {
546 		if (logdump) {
547 			fprintf(errout, "dump zone to %s...", output_filename);
548 			fflush(errout);
549 		}
550 		result = dump_zone(origin, zone, output_filename, outputformat,
551 				   outputstyle, rawversion);
552 		if (logdump) {
553 			fprintf(errout, "done\n");
554 		}
555 	}
556 
557 	if (!quiet && result == ISC_R_SUCCESS) {
558 		fprintf(errout, "OK\n");
559 	}
560 	destroy();
561 	if (lctx != NULL) {
562 		isc_log_destroy(&lctx);
563 	}
564 	isc_mem_destroy(&mctx);
565 #ifdef _WIN32
566 	DestroySockets();
567 #endif /* ifdef _WIN32 */
568 	return ((result == ISC_R_SUCCESS) ? 0 : 1);
569 }
570