xref: /netbsd-src/external/mpl/bind/dist/bin/tools/named-rrchecker.c (revision 924795e69c8bb3f17afd8fcbb799710cc1719dc4)
1 /*	$NetBSD: named-rrchecker.c,v 1.6 2022/09/23 12:15:26 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 #include <stdbool.h>
17 #include <stdlib.h>
18 
19 #include <isc/buffer.h>
20 #include <isc/commandline.h>
21 #include <isc/lex.h>
22 #include <isc/mem.h>
23 #include <isc/print.h>
24 #include <isc/string.h>
25 #include <isc/util.h>
26 
27 #include <dns/fixedname.h>
28 #include <dns/name.h>
29 #include <dns/rdata.h>
30 #include <dns/rdataclass.h>
31 #include <dns/rdatatype.h>
32 #include <dns/result.h>
33 
34 static isc_mem_t *mctx;
35 static isc_lex_t *lex;
36 
37 static isc_lexspecials_t specials;
38 
39 ISC_PLATFORM_NORETURN_PRE static void
40 usage(void) ISC_PLATFORM_NORETURN_POST;
41 
42 static void
43 usage(void) {
44 	fprintf(stderr, "usage: named-rrchecker [-o origin] [-hpCPTu]\n");
45 	fprintf(stderr, "\t-h: print this help message\n");
46 	fprintf(stderr, "\t-o origin: set origin to be used when "
47 			"interpreting the record\n");
48 	fprintf(stderr, "\t-p: print the record in canonical format\n");
49 	fprintf(stderr, "\t-C: list the supported class names\n");
50 	fprintf(stderr, "\t-P: list the supported private type names\n");
51 	fprintf(stderr, "\t-T: list the supported standard type names\n");
52 	fprintf(stderr, "\t-u: print the record in unknown record format\n");
53 	exit(0);
54 }
55 
56 ISC_PLATFORM_NORETURN_PRE static void
57 fatal(const char *format, ...) ISC_PLATFORM_NORETURN_POST;
58 
59 static void
60 fatal(const char *format, ...) {
61 	va_list args;
62 
63 	fprintf(stderr, "named-rrchecker: ");
64 	va_start(args, format);
65 	vfprintf(stderr, format, args);
66 	va_end(args);
67 	fputc('\n', stderr);
68 	exit(1);
69 }
70 
71 int
72 main(int argc, char *argv[]) {
73 	isc_token_t token;
74 	isc_result_t result;
75 	int c;
76 	unsigned int options = 0;
77 	dns_rdatatype_t rdtype;
78 	dns_rdataclass_t rdclass;
79 	char text[256 * 1024];
80 	char data[64 * 1024];
81 	isc_buffer_t tbuf;
82 	isc_buffer_t dbuf;
83 	dns_rdata_t rdata = DNS_RDATA_INIT;
84 	bool doexit = false;
85 	bool once = false;
86 	bool print = false;
87 	bool unknown = false;
88 	unsigned int t;
89 	char *origin = NULL;
90 	dns_fixedname_t fixed;
91 	dns_name_t *name = NULL;
92 
93 	while ((c = isc_commandline_parse(argc, argv, "ho:puCPT")) != -1) {
94 		switch (c) {
95 		case 'o':
96 			origin = isc_commandline_argument;
97 			break;
98 
99 		case 'p':
100 			print = true;
101 			break;
102 
103 		case 'u':
104 			unknown = true;
105 			break;
106 
107 		case 'C':
108 			for (t = 1; t <= 0xfeffu; t++) {
109 				if (dns_rdataclass_ismeta(t)) {
110 					continue;
111 				}
112 				dns_rdataclass_format(t, text, sizeof(text));
113 				if (strncmp(text, "CLASS", 4) != 0) {
114 					fprintf(stdout, "%s\n", text);
115 				}
116 			}
117 			exit(0);
118 
119 		case 'P':
120 			for (t = 0xff00; t <= 0xfffeu; t++) {
121 				if (dns_rdatatype_ismeta(t)) {
122 					continue;
123 				}
124 				dns_rdatatype_format(t, text, sizeof(text));
125 				if (strncmp(text, "TYPE", 4) != 0) {
126 					fprintf(stdout, "%s\n", text);
127 				}
128 			}
129 			doexit = true;
130 			break;
131 
132 		case 'T':
133 			for (t = 1; t <= 0xfeffu; t++) {
134 				if (dns_rdatatype_ismeta(t)) {
135 					continue;
136 				}
137 				dns_rdatatype_format(t, text, sizeof(text));
138 				if (strncmp(text, "TYPE", 4) != 0) {
139 					fprintf(stdout, "%s\n", text);
140 				}
141 			}
142 			doexit = true;
143 			break;
144 
145 		case '?':
146 		case 'h':
147 			/* Does not return. */
148 			usage();
149 
150 		default:
151 			fprintf(stderr, "%s: unhandled option -%c\n", argv[0],
152 				isc_commandline_option);
153 			exit(1);
154 		}
155 	}
156 	if (doexit) {
157 		exit(0);
158 	}
159 
160 	isc_mem_create(&mctx);
161 	RUNTIME_CHECK(isc_lex_create(mctx, 256, &lex) == ISC_R_SUCCESS);
162 
163 	/*
164 	 * Set up to lex DNS master file.
165 	 */
166 
167 	specials['('] = 1;
168 	specials[')'] = 1;
169 	specials['"'] = 1;
170 	isc_lex_setspecials(lex, specials);
171 	options = ISC_LEXOPT_EOL;
172 	isc_lex_setcomments(lex, ISC_LEXCOMMENT_DNSMASTERFILE);
173 
174 	RUNTIME_CHECK(isc_lex_openstream(lex, stdin) == ISC_R_SUCCESS);
175 
176 	if (origin != NULL) {
177 		name = dns_fixedname_initname(&fixed);
178 		result = dns_name_fromstring(name, origin, 0, NULL);
179 		if (result != ISC_R_SUCCESS) {
180 			fatal("dns_name_fromstring: %s",
181 			      dns_result_totext(result));
182 		}
183 	}
184 
185 	while ((result = isc_lex_gettoken(lex, options | ISC_LEXOPT_NUMBER,
186 					  &token)) == ISC_R_SUCCESS)
187 	{
188 		if (token.type == isc_tokentype_eof) {
189 			break;
190 		}
191 		if (token.type == isc_tokentype_eol) {
192 			continue;
193 		}
194 		if (once) {
195 			fatal("extra data");
196 		}
197 		/*
198 		 * Get class.
199 		 */
200 		if (token.type == isc_tokentype_number) {
201 			rdclass = (dns_rdataclass_t)token.value.as_ulong;
202 			if (token.value.as_ulong > 0xffffu) {
203 				fatal("class value too big %lu",
204 				      token.value.as_ulong);
205 			}
206 			if (dns_rdataclass_ismeta(rdclass)) {
207 				fatal("class %lu is a meta value",
208 				      token.value.as_ulong);
209 			}
210 		} else if (token.type == isc_tokentype_string) {
211 			result = dns_rdataclass_fromtext(
212 				&rdclass, &token.value.as_textregion);
213 			if (result != ISC_R_SUCCESS) {
214 				fatal("dns_rdataclass_fromtext: %s",
215 				      dns_result_totext(result));
216 			}
217 			if (dns_rdataclass_ismeta(rdclass)) {
218 				fatal("class %.*s(%d) is a meta value",
219 				      (int)token.value.as_textregion.length,
220 				      token.value.as_textregion.base, rdclass);
221 			}
222 		} else {
223 			fatal("unexpected token %u", token.type);
224 		}
225 
226 		result = isc_lex_gettoken(lex, options | ISC_LEXOPT_NUMBER,
227 					  &token);
228 		if (result != ISC_R_SUCCESS) {
229 			break;
230 		}
231 		if (token.type == isc_tokentype_eol) {
232 			continue;
233 		}
234 		if (token.type == isc_tokentype_eof) {
235 			break;
236 		}
237 
238 		/*
239 		 * Get type.
240 		 */
241 		if (token.type == isc_tokentype_number) {
242 			rdtype = (dns_rdatatype_t)token.value.as_ulong;
243 			if (token.value.as_ulong > 0xffffu) {
244 				fatal("type value too big %lu",
245 				      token.value.as_ulong);
246 			}
247 			if (dns_rdatatype_ismeta(rdtype)) {
248 				fatal("type %lu is a meta value",
249 				      token.value.as_ulong);
250 			}
251 		} else if (token.type == isc_tokentype_string) {
252 			result = dns_rdatatype_fromtext(
253 				&rdtype, &token.value.as_textregion);
254 			if (result != ISC_R_SUCCESS) {
255 				fatal("dns_rdatatype_fromtext: %s",
256 				      dns_result_totext(result));
257 			}
258 			if (dns_rdatatype_ismeta(rdtype)) {
259 				fatal("type %.*s(%d) is a meta value",
260 				      (int)token.value.as_textregion.length,
261 				      token.value.as_textregion.base, rdtype);
262 			}
263 		} else {
264 			fatal("unexpected token %u", token.type);
265 		}
266 
267 		isc_buffer_init(&dbuf, data, sizeof(data));
268 		result = dns_rdata_fromtext(&rdata, rdclass, rdtype, lex, name,
269 					    0, mctx, &dbuf, NULL);
270 		if (result != ISC_R_SUCCESS) {
271 			fatal("dns_rdata_fromtext: %s",
272 			      dns_result_totext(result));
273 		}
274 		once = true;
275 	}
276 	if (result != ISC_R_EOF) {
277 		fatal("eof not found");
278 	}
279 	if (!once) {
280 		fatal("no records found");
281 	}
282 
283 	if (print) {
284 		isc_buffer_init(&tbuf, text, sizeof(text));
285 		result = dns_rdataclass_totext(rdclass, &tbuf);
286 		if (result != ISC_R_SUCCESS) {
287 			fatal("dns_rdataclass_totext: %s",
288 			      dns_result_totext(result));
289 		}
290 		isc_buffer_putstr(&tbuf, "\t");
291 		result = dns_rdatatype_totext(rdtype, &tbuf);
292 		if (result != ISC_R_SUCCESS) {
293 			fatal("dns_rdatatype_totext: %s",
294 			      dns_result_totext(result));
295 		}
296 		isc_buffer_putstr(&tbuf, "\t");
297 		result = dns_rdata_totext(&rdata, NULL, &tbuf);
298 		if (result != ISC_R_SUCCESS) {
299 			fatal("dns_rdata_totext: %s",
300 			      dns_result_totext(result));
301 		}
302 
303 		printf("%.*s\n", (int)tbuf.used, (char *)tbuf.base);
304 		fflush(stdout);
305 	}
306 
307 	if (unknown) {
308 		isc_buffer_init(&tbuf, text, sizeof(text));
309 		result = dns_rdataclass_tounknowntext(rdclass, &tbuf);
310 		if (result != ISC_R_SUCCESS) {
311 			fatal("dns_rdataclass_tounknowntext: %s",
312 			      dns_result_totext(result));
313 		}
314 		isc_buffer_putstr(&tbuf, "\t");
315 		result = dns_rdatatype_tounknowntext(rdtype, &tbuf);
316 		if (result != ISC_R_SUCCESS) {
317 			fatal("dns_rdatatype_tounknowntext: %s",
318 			      dns_result_totext(result));
319 		}
320 		isc_buffer_putstr(&tbuf, "\t");
321 		result = dns_rdata_tofmttext(&rdata, NULL,
322 					     DNS_STYLEFLAG_UNKNOWNFORMAT, 0, 0,
323 					     "", &tbuf);
324 		if (result != ISC_R_SUCCESS) {
325 			fatal("dns_rdata_tofmttext: %sn",
326 			      dns_result_totext(result));
327 		}
328 
329 		printf("%.*s\n", (int)tbuf.used, (char *)tbuf.base);
330 		fflush(stdout);
331 	}
332 
333 	isc_lex_close(lex);
334 	isc_lex_destroy(&lex);
335 	isc_mem_destroy(&mctx);
336 	return (0);
337 }
338