1 /* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
2
3 This program is free software: you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation, either version 3 of the License, or
6 (at your option) any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <https://www.gnu.org/licenses/>.
15 */
16
17 #include <getopt.h>
18 #include <libgen.h>
19 #include <stdio.h>
20
21 #include "contrib/time.h"
22 #include "contrib/tolower.h"
23 #include "libknot/libknot.h"
24 #include "knot/common/log.h"
25 #include "knot/zone/semantic-check.h"
26 #include "utils/common/params.h"
27 #include "utils/kzonecheck/zone_check.h"
28
29 #define PROGRAM_NAME "kzonecheck"
30
31 #define STDIN_SUBST "-"
32 #define STDIN_REPL "/dev/stdin"
33
print_help(void)34 static void print_help(void)
35 {
36 printf("Usage: %s [parameters] <filename>\n"
37 "\n"
38 "Parameters:\n"
39 " -o, --origin <zone_origin> Zone name.\n"
40 " (default filename without .zone)\n"
41 " -d, --dnssec <on|off> Also check DNSSEC-related records.\n"
42 " -t, --time <timestamp> Current time specification.\n"
43 " (default current UNIX time)\n"
44 " -v, --verbose Enable debug output.\n"
45 " -h, --help Print the program help.\n"
46 " -V, --version Print the program version.\n"
47 "\n",
48 PROGRAM_NAME);
49 }
50
str2bool(const char * s)51 static bool str2bool(const char *s)
52 {
53 switch (knot_tolower(s[0])) {
54 case '1':
55 case 'y':
56 case 't':
57 return true;
58 case 'o':
59 return knot_tolower(s[1]) == 'n';
60 default:
61 return false;
62 }
63 }
64
main(int argc,char * argv[])65 int main(int argc, char *argv[])
66 {
67 const char *origin = NULL;
68 bool verbose = false;
69 semcheck_optional_t optional = SEMCHECK_AUTO_DNSSEC; // default value for --dnssec
70 knot_time_t check_time = (knot_time_t)time(NULL);
71
72 /* Long options. */
73 struct option opts[] = {
74 { "origin", required_argument, NULL, 'o' },
75 { "time", required_argument, NULL, 't' },
76 { "dnssec", required_argument, NULL, 'd' },
77 { "verbose", no_argument, NULL, 'v' },
78 { "help", no_argument, NULL, 'h' },
79 { "version", no_argument, NULL, 'V' },
80 { NULL }
81 };
82
83 /* Set the time zone. */
84 tzset();
85
86 /* Parse command line arguments */
87 int opt = 0;
88 while ((opt = getopt_long(argc, argv, "o:t:d:vVh", opts, NULL)) != -1) {
89 switch (opt) {
90 case 'o':
91 origin = optarg;
92 break;
93 case 'v':
94 verbose = true;
95 break;
96 case 'h':
97 print_help();
98 return EXIT_SUCCESS;
99 case 'V':
100 print_version(PROGRAM_NAME);
101 return EXIT_SUCCESS;
102 case 'd':
103 optional = str2bool(optarg) ? SEMCHECK_DNSSEC : SEMCHECK_NO_DNSSEC;
104 break;
105 case 't':
106 if (knot_time_parse("YMDhms|#|+-#U|+-#",
107 optarg, &check_time) != KNOT_EOK) {
108 fprintf(stderr, "Unknown time format\n");
109 return EXIT_FAILURE;
110 }
111 break;
112 default:
113 print_help();
114 return EXIT_FAILURE;
115 }
116 }
117
118 /* Check if there's at least one remaining non-option. */
119 if (optind >= argc) {
120 fprintf(stderr, "Expected zone file name\n");
121 print_help();
122 return EXIT_FAILURE;
123 }
124
125 char *filename = argv[optind];
126 if (strncmp(filename, STDIN_SUBST, sizeof(STDIN_SUBST)) == 0) {
127 filename = STDIN_REPL;
128 }
129
130 char *zonename;
131 if (origin == NULL) {
132 /* Get zone name from file name. */
133 const char *ext = ".zone";
134 zonename = basename(filename);
135 if (strcmp(zonename + strlen(zonename) - strlen(ext), ext) == 0) {
136 zonename = strndup(zonename, strlen(zonename) - strlen(ext));
137 } else {
138 zonename = strdup(zonename);
139 }
140 } else {
141 zonename = strdup(origin);
142 }
143
144 log_init();
145 log_levels_set(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, 0);
146 log_levels_set(LOG_TARGET_STDERR, LOG_SOURCE_ANY, 0);
147 log_levels_set(LOG_TARGET_SYSLOG, LOG_SOURCE_ANY, 0);
148 log_flag_set(LOG_FLAG_NOTIMESTAMP | LOG_FLAG_NOINFO);
149 if (verbose) {
150 log_levels_add(LOG_TARGET_STDOUT, LOG_SOURCE_ANY, LOG_UPTO(LOG_DEBUG));
151 }
152
153 knot_dname_t *dname = knot_dname_from_str_alloc(zonename);
154 knot_dname_to_lower(dname);
155 free(zonename);
156 int ret = zone_check(filename, dname, stdout, optional, (time_t)check_time);
157 knot_dname_free(dname, NULL);
158
159 log_close();
160
161 switch (ret) {
162 case KNOT_EOK:
163 if (verbose) {
164 fprintf(stdout, "No semantic error found\n");
165 }
166 return EXIT_SUCCESS;
167 case KNOT_EZONEINVAL:
168 fprintf(stdout, "Serious semantic error detected\n");
169 // FALLTHROUGH
170 case KNOT_ESEMCHECK:
171 return EXIT_FAILURE;
172 case KNOT_EACCES:
173 case KNOT_EFILE:
174 fprintf(stderr, "Failed to load the zone file\n");
175 return EXIT_FAILURE;
176 default:
177 fprintf(stderr, "Failed to run semantic checks (%s)\n", knot_strerror(ret));
178 return EXIT_FAILURE;
179 }
180 }
181