1 /* xfr-inspect - list the contents and inspect an zone transfer XFR file 2 * By W.C.A. Wijngaards 3 * Copyright 2017, NLnet Labs. 4 * BSD, see LICENSE. 5 */ 6 7 #include "config.h" 8 #include "util.h" 9 #include "buffer.h" 10 #include "packet.h" 11 #include "rdata.h" 12 #include "namedb.h" 13 #include "difffile.h" 14 #include <stdio.h> 15 #include <errno.h> 16 #include <string.h> 17 #include <stdlib.h> 18 #include <unistd.h> 19 #include <ctype.h> 20 21 /** verbosity for inspect */ 22 static int v = 0; 23 /** shorthand for ease */ 24 #ifdef ULL 25 #undef ULL 26 #endif 27 #define ULL (unsigned long long) 28 29 /** print usage text */ 30 static void 31 usage(void) 32 { 33 printf("usage: xfr-inspect [options] file\n"); 34 printf(" -h this help\n"); 35 printf(" -v increase verbosity: " 36 "with -v(list chunks), -vv(inside chunks)\n"); 37 printf(" -l list contents of transfer\n"); 38 } 39 40 static int 41 xi_diff_read_64(FILE *in, uint64_t* result) 42 { 43 if (fread(result, sizeof(*result), 1, in) == 1) { 44 return 1; 45 } else { 46 return 0; 47 } 48 } 49 50 static int 51 xi_diff_read_32(FILE *in, uint32_t* result) 52 { 53 if (fread(result, sizeof(*result), 1, in) == 1) { 54 *result = ntohl(*result); 55 return 1; 56 } else { 57 return 0; 58 } 59 } 60 61 static int 62 xi_diff_read_8(FILE *in, uint8_t* result) 63 { 64 if (fread(result, sizeof(*result), 1, in) == 1) { 65 return 1; 66 } else { 67 return 0; 68 } 69 } 70 71 static int 72 xi_diff_read_str(FILE* in, char* buf, size_t len) 73 { 74 uint32_t disklen; 75 if(!xi_diff_read_32(in, &disklen)) 76 return 0; 77 if(disklen >= len) 78 return 0; 79 if(fread(buf, disklen, 1, in) != 1) 80 return 0; 81 buf[disklen] = 0; 82 return 1; 83 } 84 85 86 /** inspect header of xfr file, return num_parts */ 87 static int 88 inspect_header(FILE* in) 89 { 90 char zone_buf[3072]; 91 char patname_buf[2048]; 92 93 uint32_t old_serial, new_serial, num_parts, type; 94 uint64_t time_end_0, time_start_0; 95 uint32_t time_end_1, time_start_1; 96 uint8_t committed; 97 98 time_t time_end, time_start; 99 100 if(!xi_diff_read_32(in, &type)) { 101 printf("could not read type, file short\n"); 102 fclose(in); 103 exit(1); 104 } 105 if(type != DIFF_PART_XFRF) { 106 printf("type: %x (BAD FILE TYPE)\n", type); 107 fclose(in); 108 exit(1); 109 } 110 if(!xi_diff_read_8(in, &committed) || 111 !xi_diff_read_32(in, &num_parts) || 112 !xi_diff_read_64(in, &time_end_0) || 113 !xi_diff_read_32(in, &time_end_1) || 114 !xi_diff_read_32(in, &old_serial) || 115 !xi_diff_read_32(in, &new_serial) || 116 !xi_diff_read_64(in, &time_start_0) || 117 !xi_diff_read_32(in, &time_start_1) || 118 !xi_diff_read_str(in, zone_buf, sizeof(zone_buf)) || 119 !xi_diff_read_str(in, patname_buf, sizeof(patname_buf))) { 120 printf("diff file bad commit part, file too short"); 121 fclose(in); 122 exit(1); 123 } 124 time_end = (time_t)time_end_0; 125 time_start = (time_t)time_start_0; 126 127 /* printf("type: %x\n", (int)type); */ 128 printf("committed: %d (%s)\n", (int)committed, 129 committed?"yes":"no"); 130 printf("num_parts: %d\n", (int)num_parts); 131 printf("time_end: %d.%6.6d %s", (int)time_end_0, 132 (int)time_end_1, ctime(&time_end)); 133 printf("old_serial: %u\n", (unsigned)old_serial); 134 printf("new_serial: %u\n", (unsigned)new_serial); 135 printf("time_start: %d.%6.6d %s", (int)time_start_0, 136 (int)time_start_1, ctime(&time_start)); 137 printf("zone: %s\n", zone_buf); 138 printf("patname: %s\n", patname_buf); 139 140 return num_parts; 141 } 142 143 /** print records in packet */ 144 static void 145 print_records(region_type* region, buffer_type* pkt, int num, int qsection) 146 { 147 domain_table_type* table; 148 int i; 149 rr_type* rr; 150 region_type* tmpregion = region_create(xalloc, free); 151 buffer_type* tmpbuf; 152 if(!tmpregion) { 153 printf("out of memory\n"); 154 return; 155 } 156 tmpbuf = buffer_create(region, QIOBUFSZ); 157 if(!tmpbuf) { 158 printf("out of memory\n"); 159 return; 160 } 161 table = domain_table_create(tmpregion); 162 if(!table) { 163 printf("out of memory\n"); 164 return; 165 } 166 167 for(i=0; i<num; ++i) { 168 rr = packet_read_rr(region, table, pkt, qsection); 169 if(!rr) { 170 printf("; cannot read rr %d\n", i); 171 return; 172 } 173 if(qsection) { 174 printf("%s", dname_to_string(domain_dname(rr->owner), 175 NULL)); 176 printf("\t%s", rrclass_to_string(rr->klass)); 177 if(rr->type == TYPE_IXFR) 178 printf("\tIXFR\n"); 179 else if(rr->type == TYPE_AXFR) 180 printf("\tAXFR\n"); 181 else printf("\t%s\n", rrtype_to_string(rr->type)); 182 } else { 183 if(!print_rr(stdout, NULL, rr, tmpregion, tmpbuf)) { 184 printf("; cannot print rr %d\n", i); 185 } 186 } 187 } 188 region_destroy(tmpregion); 189 } 190 191 /** inspect packet (IXFR or AXFR) */ 192 static void 193 inspect_packet(region_type* region, buffer_type* pkt) 194 { 195 printf("\n"); 196 if(buffer_limit(pkt) < QHEADERSZ) { 197 printf("packet too short\n"); 198 return; 199 } 200 printf("; id=%4.4x ; flags%s%s%s%s%s%s%s%s ; rcode %s ; opcode %d\n", 201 ID(pkt), QR(pkt)?" QR":"", AA(pkt)?" AA":"", TC(pkt)?" TC":"", 202 RD(pkt)?" RD":"", RA(pkt)?" RA":"", Z(pkt)?" Z":"", 203 AD(pkt)?" AD":"", CD(pkt)?" CD":"", rcode2str(RCODE(pkt)), 204 OPCODE(pkt)); 205 printf("; qdcount %d ; ancount %d ; nscount %d ; arcount %d\n", 206 QDCOUNT(pkt), ANCOUNT(pkt), NSCOUNT(pkt), ARCOUNT(pkt)); 207 buffer_skip(pkt, QHEADERSZ); 208 209 if(QDCOUNT(pkt) != 0) { 210 printf("; QUESTION SECTION\n"); 211 print_records(region, pkt, QDCOUNT(pkt), 1); 212 } 213 if(ANCOUNT(pkt) != 0) { 214 printf("; ANSWER SECTION\n"); 215 print_records(region, pkt, ANCOUNT(pkt), 0); 216 } 217 if(NSCOUNT(pkt) != 0) { 218 printf("; AUTHORITY SECTION\n"); 219 print_records(region, pkt, NSCOUNT(pkt), 0); 220 } 221 if(ARCOUNT(pkt) != 0) { 222 printf("; ADDITIONAL SECTION\n"); 223 print_records(region, pkt, ARCOUNT(pkt), 0); 224 } 225 } 226 227 /** inspect part of xfr file */ 228 static void 229 inspect_part(FILE* in, int partnum) 230 { 231 uint32_t pkttype, msglen, msglen2; 232 region_type* region; 233 buffer_type* packet; 234 region = region_create(xalloc, free); 235 if(!region) { 236 printf("out of memory\n"); 237 fclose(in); 238 exit(1); 239 } 240 packet = buffer_create(region, QIOBUFSZ); 241 if(!xi_diff_read_32(in, &pkttype)) { 242 printf("cannot read part %d\n", partnum); 243 fclose(in); 244 exit(1); 245 } 246 if(pkttype != DIFF_PART_XXFR) { 247 printf("bad part %d: not type XXFR\n", partnum); 248 fclose(in); 249 exit(1); 250 } 251 if(!xi_diff_read_32(in, &msglen)) { 252 printf("bad part %d: not msglen, file too short\n", partnum); 253 fclose(in); 254 exit(1); 255 } 256 if(msglen < QHEADERSZ || msglen > QIOBUFSZ) { 257 printf("bad part %d: msglen %u (too short or too long)\n", 258 partnum, (unsigned)msglen); 259 fclose(in); 260 exit(1); 261 } 262 if(fread(buffer_begin(packet), msglen, 1, in) != 1) { 263 printf("bad part %d: short packet, file too short, %s\n", 264 partnum, strerror(errno)); 265 fclose(in); 266 exit(1); 267 } 268 if(!xi_diff_read_32(in, &msglen2)) { 269 printf("bad part %d: cannot read msglen2, file too short\n", partnum); 270 fclose(in); 271 exit(1); 272 } 273 if(v==0) { 274 region_destroy(region); 275 return; 276 } 277 278 printf("\n"); 279 /* printf("type : %x\n", pkttype); */ 280 printf("part : %d\n", partnum); 281 printf("msglen : %u\n", (unsigned)msglen); 282 printf("msglen2 : %u (%s)\n", (unsigned)msglen2, 283 (msglen==msglen2)?"ok":"wrong"); 284 285 if(v>=2) { 286 buffer_set_limit(packet, msglen); 287 inspect_packet(region, packet); 288 } 289 290 region_destroy(region); 291 } 292 293 /** inspect parts of xfr file */ 294 static void 295 inspect_parts(FILE* in, int num) 296 { 297 int i; 298 for(i=0; i<num; i++) { 299 inspect_part(in, i); 300 } 301 } 302 303 /** inspect trail of xfr file */ 304 static void 305 inspect_trail(FILE* in) 306 { 307 char log_buf[5120]; 308 if(!xi_diff_read_str(in, log_buf, sizeof(log_buf))) { 309 printf("bad trail: cannot read log string\n"); 310 fclose(in); 311 exit(1); 312 } 313 printf("\n"); 314 printf("log: %s\n", log_buf); 315 } 316 317 /** inspect contents of xfr file */ 318 static void 319 inspect_file(char* fname) 320 { 321 FILE* in; 322 int num; 323 log_init("udb-inspect"); 324 if(!(in=fopen(fname, "r"))) { 325 printf("cannot open %s: %s\n", fname, strerror(errno)); 326 exit(1); 327 } 328 printf("file: %s\n", fname); 329 num = inspect_header(in); 330 inspect_parts(in, num); 331 inspect_trail(in); 332 fclose(in); 333 } 334 335 /** list header of xfr file, return num_parts */ 336 static int 337 list_header(FILE* in) 338 { 339 char zone_buf[3072]; 340 char patname_buf[2048]; 341 342 uint32_t old_serial, new_serial, num_parts, type; 343 uint64_t time_end_0, time_start_0; 344 uint32_t time_end_1, time_start_1; 345 uint8_t committed; 346 347 time_t time_end, time_start; 348 349 if(!xi_diff_read_32(in, &type)) { 350 printf("could not read type, file short\n"); 351 fclose(in); 352 exit(1); 353 } 354 if(type != DIFF_PART_XFRF) { 355 printf("type: %x (BAD FILE TYPE)\n", type); 356 fclose(in); 357 exit(1); 358 } 359 if(!xi_diff_read_8(in, &committed) || 360 !xi_diff_read_32(in, &num_parts) || 361 !xi_diff_read_64(in, &time_end_0) || 362 !xi_diff_read_32(in, &time_end_1) || 363 !xi_diff_read_32(in, &old_serial) || 364 !xi_diff_read_32(in, &new_serial) || 365 !xi_diff_read_64(in, &time_start_0) || 366 !xi_diff_read_32(in, &time_start_1) || 367 !xi_diff_read_str(in, zone_buf, sizeof(zone_buf)) || 368 !xi_diff_read_str(in, patname_buf, sizeof(patname_buf))) { 369 printf("diff file bad commit part, file too short"); 370 fclose(in); 371 exit(1); 372 } 373 time_end = (time_t)time_end_0; 374 time_start = (time_t)time_start_0; 375 376 /* printf("; type: %x\n", (int)type); */ 377 printf("; committed: %d (%s)\n", (int)committed, 378 committed?"yes":"no"); 379 printf("; num_parts: %d\n", (int)num_parts); 380 printf("; time_end: %d.%6.6d %s", (int)time_end_0, 381 (int)time_end_1, ctime(&time_end)); 382 printf("; old_serial: %u\n", (unsigned)old_serial); 383 printf("; new_serial: %u\n", (unsigned)new_serial); 384 printf("; time_start: %d.%6.6d %s", (int)time_start_0, 385 (int)time_start_1, ctime(&time_start)); 386 printf("; zone: %s\n", zone_buf); 387 printf("; patname: %s\n", patname_buf); 388 389 return num_parts; 390 } 391 392 /** list packet (IXFR or AXFR) */ 393 static void 394 list_packet(region_type* region, buffer_type* pkt, int partnum) 395 { 396 if(buffer_limit(pkt) < QHEADERSZ) { 397 printf("packet too short\n"); 398 return; 399 } 400 buffer_skip(pkt, QHEADERSZ); 401 402 if(partnum == 0 && QDCOUNT(pkt) == 1) { 403 /* print query AXFR or IXFR */ 404 printf("; "); 405 print_records(region, pkt, QDCOUNT(pkt), 1); 406 } 407 if(ANCOUNT(pkt) != 0) { 408 print_records(region, pkt, ANCOUNT(pkt), 0); 409 } 410 } 411 412 /** list part of xfr file */ 413 static void 414 list_part(FILE* in, int partnum) 415 { 416 uint32_t pkttype, msglen, msglen2; 417 region_type* region; 418 buffer_type* packet; 419 region = region_create(xalloc, free); 420 if(!region) { 421 printf("out of memory\n"); 422 fclose(in); 423 exit(1); 424 } 425 packet = buffer_create(region, QIOBUFSZ); 426 if(!xi_diff_read_32(in, &pkttype)) { 427 printf("cannot read part %d\n", partnum); 428 fclose(in); 429 exit(1); 430 } 431 if(pkttype != DIFF_PART_XXFR) { 432 printf("bad part %d: not type XXFR\n", partnum); 433 fclose(in); 434 exit(1); 435 } 436 if(!xi_diff_read_32(in, &msglen)) { 437 printf("bad part %d: not msglen, file too short\n", partnum); 438 fclose(in); 439 exit(1); 440 } 441 if(msglen < QHEADERSZ || msglen > QIOBUFSZ) { 442 printf("bad part %d: msglen %u (too short or too long)\n", 443 partnum, (unsigned)msglen); 444 fclose(in); 445 exit(1); 446 } 447 if(fread(buffer_begin(packet), msglen, 1, in) != 1) { 448 printf("bad part %d: short packet, file too short, %s\n", 449 partnum, strerror(errno)); 450 fclose(in); 451 exit(1); 452 } 453 if(!xi_diff_read_32(in, &msglen2)) { 454 printf("bad part %d: cannot read msglen2, file too short\n", partnum); 455 fclose(in); 456 exit(1); 457 } 458 459 buffer_set_limit(packet, msglen); 460 list_packet(region, packet, partnum); 461 region_destroy(region); 462 } 463 464 /** list parts of xfr file */ 465 static void 466 list_parts(FILE* in, int num) 467 { 468 int i; 469 for(i=0; i<num; i++) { 470 list_part(in, i); 471 } 472 } 473 474 /** list contents of xfr file */ 475 static void 476 list_file(char* fname) 477 { 478 FILE* in; 479 int num; 480 log_init("udb-inspect"); 481 if(!(in=fopen(fname, "r"))) { 482 printf("cannot open %s: %s\n", fname, strerror(errno)); 483 exit(1); 484 } 485 num = list_header(in); 486 list_parts(in, num); 487 488 fclose(in); 489 } 490 491 /** getopt global, in case header files fail to declare it. */ 492 extern int optind; 493 /** getopt global, in case header files fail to declare it. */ 494 extern char* optarg; 495 496 /** 497 * main program. Set options given commandline arguments. 498 * @param argc: number of commandline arguments. 499 * @param argv: array of commandline arguments. 500 * @return: exit status of the program. 501 */ 502 int 503 main(int argc, char* argv[]) 504 { 505 int c, list=0; 506 while( (c=getopt(argc, argv, "hlv")) != -1) { 507 switch(c) { 508 case 'l': 509 list=1; 510 break; 511 case 'v': 512 v++; 513 break; 514 default: 515 case 'h': 516 usage(); 517 return 1; 518 } 519 } 520 argc -= optind; 521 argv += optind; 522 if(argc != 1) { 523 usage(); 524 return 1; 525 } 526 if(list) list_file(argv[0]); 527 else inspect_file(argv[0]); 528 529 return 0; 530 } 531