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