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