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