Home | History | Annotate | Line # | Download | only in dist
      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