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