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