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