xref: /openbsd/usr.sbin/nsd/xfr-inspect.c (revision 4bdff4be)
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