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