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
usage(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
xi_diff_read_64(FILE * in,uint64_t * result)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
xi_diff_read_32(FILE * in,uint32_t * result)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
xi_diff_read_8(FILE * in,uint8_t * result)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
xi_diff_read_str(FILE * in,char * buf,size_t len)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
inspect_header(FILE * in)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
print_records(region_type * region,buffer_type * pkt,int num,int qsection)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
inspect_packet(region_type * region,buffer_type * pkt)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
inspect_part(FILE * in,int partnum)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
inspect_parts(FILE * in,int num)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
inspect_trail(FILE * in)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
inspect_file(char * fname)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
list_header(FILE * in)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
list_packet(region_type * region,buffer_type * pkt,int partnum)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
list_part(FILE * in,int partnum)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
list_parts(FILE * in,int num)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
list_file(char * fname)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
main(int argc,char * argv[])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