1218799Snwhitehorn/* xfr-inspect - list the contents and inspect an zone transfer XFR file
2218799Snwhitehorn * By W.C.A. Wijngaards
3218799Snwhitehorn * Copyright 2017, NLnet Labs.
4257842Sdteske * BSD, see LICENSE.
5218799Snwhitehorn */
6218799Snwhitehorn
7218799Snwhitehorn#include "config.h"
8218799Snwhitehorn#include "util.h"
9218799Snwhitehorn#include "buffer.h"
10218799Snwhitehorn#include "packet.h"
11218799Snwhitehorn#include "rdata.h"
12218799Snwhitehorn#include "namedb.h"
13218799Snwhitehorn#include "difffile.h"
14218799Snwhitehorn#include <stdio.h>
15218799Snwhitehorn#include <errno.h>
16218799Snwhitehorn#include <string.h>
17218799Snwhitehorn#include <stdlib.h>
18218799Snwhitehorn#include <unistd.h>
19218799Snwhitehorn#include <ctype.h>
20218799Snwhitehorn
21218799Snwhitehorn/** verbosity for inspect */
22218799Snwhitehornstatic int v = 0;
23218799Snwhitehorn/** shorthand for ease */
24218799Snwhitehorn#ifdef ULL
25218799Snwhitehorn#undef ULL
26218799Snwhitehorn#endif
27218799Snwhitehorn#define ULL (unsigned long long)
28218799Snwhitehorn
29257842Sdteske/** print usage text */
30257842Sdteskestatic void
31218799Snwhitehornusage(void)
32257842Sdteske{
33257842Sdteske	printf("usage:	xfr-inspect [options] file\n");
34285679Sallanjude	printf(" -h		this help\n");
35218799Snwhitehorn	printf(" -v		increase verbosity: "
36257842Sdteske	       "with -v(list chunks), -vv(inside chunks)\n");
37257842Sdteske	printf(" -l		list contents of transfer\n");
38218799Snwhitehorn}
39269653Sthompsa
40269653Sthompsastatic int
41269653Sthompsaxi_diff_read_64(FILE *in, uint64_t* result)
42269653Sthompsa{
43220088Snwhitehorn	if (fread(result, sizeof(*result), 1, in) == 1) {
44220088Snwhitehorn		return 1;
45218799Snwhitehorn	} else {
46218799Snwhitehorn		return 0;
47269653Sthompsa	}
48218799Snwhitehorn}
49225637Snwhitehorn
50218799Snwhitehornstatic int
51218799Snwhitehornxi_diff_read_32(FILE *in, uint32_t* result)
52218799Snwhitehorn{
53218799Snwhitehorn	if (fread(result, sizeof(*result), 1, in) == 1) {
54218799Snwhitehorn		*result = ntohl(*result);
55285679Sallanjude		return 1;
56285732Sallanjude	} else {
57285679Sallanjude		return 0;
58285679Sallanjude	}
59285679Sallanjude}
60285679Sallanjude
61285679Sallanjudestatic int
62285679Sallanjudexi_diff_read_8(FILE *in, uint8_t* result)
63285679Sallanjude{
64285679Sallanjude        if (fread(result, sizeof(*result), 1, in) == 1) {
65285679Sallanjude                return 1;
66285679Sallanjude        } else {
67285679Sallanjude                return 0;
68285679Sallanjude        }
69285679Sallanjude}
70285679Sallanjude
71285679Sallanjudestatic int
72285679Sallanjudexi_diff_read_str(FILE* in, char* buf, size_t len)
73285679Sallanjude{
74285679Sallanjude	uint32_t disklen;
75285679Sallanjude	if(!xi_diff_read_32(in, &disklen))
76285679Sallanjude		return 0;
77285679Sallanjude	if(disklen >= len)
78285679Sallanjude		return 0;
79285679Sallanjude	if(fread(buf, disklen, 1, in) != 1)
80285679Sallanjude		return 0;
81285679Sallanjude	buf[disklen] = 0;
82285679Sallanjude	return 1;
83285679Sallanjude}
84285679Sallanjude
85285679Sallanjude
86285679Sallanjude/** inspect header of xfr file, return num_parts */
87285679Sallanjudestatic int
88285679Sallanjudeinspect_header(FILE* in)
89285679Sallanjude{
90285679Sallanjude	char zone_buf[3072];
91285679Sallanjude	char patname_buf[2048];
92285679Sallanjude
93285679Sallanjude	uint32_t old_serial, new_serial, num_parts, type;
94285679Sallanjude	uint64_t time_end_0, time_start_0;
95285679Sallanjude	uint32_t time_end_1, time_start_1;
96285679Sallanjude	uint8_t committed;
97285679Sallanjude
98285679Sallanjude	time_t time_end, time_start;
99285679Sallanjude
100285679Sallanjude	if(!xi_diff_read_32(in, &type)) {
101285679Sallanjude		printf("could not read type, file short\n");
102285679Sallanjude		fclose(in);
103257842Sdteske		exit(1);
104218799Snwhitehorn	}
105257842Sdteske	if(type != DIFF_PART_XFRF) {
106257842Sdteske		printf("type:	%x (BAD FILE TYPE)\n", type);
107218799Snwhitehorn		fclose(in);
108218799Snwhitehorn		exit(1);
109218799Snwhitehorn	}
110218799Snwhitehorn	if(!xi_diff_read_8(in, &committed) ||
111218799Snwhitehorn		!xi_diff_read_32(in, &num_parts) ||
112218799Snwhitehorn		!xi_diff_read_64(in, &time_end_0) ||
113218799Snwhitehorn		!xi_diff_read_32(in, &time_end_1) ||
114269653Sthompsa		!xi_diff_read_32(in, &old_serial) ||
115218799Snwhitehorn		!xi_diff_read_32(in, &new_serial) ||
116219615Snwhitehorn		!xi_diff_read_64(in, &time_start_0) ||
117219615Snwhitehorn		!xi_diff_read_32(in, &time_start_1) ||
118293223Sgjb		!xi_diff_read_str(in, zone_buf, sizeof(zone_buf)) ||
119293223Sgjb		!xi_diff_read_str(in, patname_buf, sizeof(patname_buf))) {
120218947Snwhitehorn		printf("diff file bad commit part, file too short");
121219615Snwhitehorn		fclose(in);
122241902Sdteske		exit(1);
123241902Sdteske	}
124241902Sdteske	time_end = (time_t)time_end_0;
125241902Sdteske	time_start = (time_t)time_start_0;
126241902Sdteske
127241902Sdteske	/* printf("type:		%x\n", (int)type); */
128219615Snwhitehorn	printf("committed:	%d (%s)\n", (int)committed,
129219615Snwhitehorn		committed?"yes":"no");
130219615Snwhitehorn	printf("num_parts:	%d\n", (int)num_parts);
131219615Snwhitehorn	printf("time_end:	%d.%6.6d %s", (int)time_end_0,
132218947Snwhitehorn		(int)time_end_1, ctime(&time_end));
133293223Sgjb	printf("old_serial:	%u\n", (unsigned)old_serial);
134218799Snwhitehorn	printf("new_serial:	%u\n", (unsigned)new_serial);
135218799Snwhitehorn	printf("time_start:	%d.%6.6d %s", (int)time_start_0,
136218799Snwhitehorn		(int)time_start_1, ctime(&time_start));
137218799Snwhitehorn	printf("zone:		%s\n", zone_buf);
138293223Sgjb	printf("patname:	%s\n", patname_buf);
139293223Sgjb
140218799Snwhitehorn	return num_parts;
141218799Snwhitehorn}
142293223Sgjb
143220080Snwhitehorn/** print records in packet */
144218799Snwhitehornstatic void
145220080Snwhitehornprint_records(region_type* region, buffer_type* pkt, int num, int qsection)
146293223Sgjb{
147218799Snwhitehorn	domain_table_type* table;
148218799Snwhitehorn	int i;
149218799Snwhitehorn	rr_type* rr;
150218799Snwhitehorn	region_type* tmpregion = region_create(xalloc, free);
151220088Snwhitehorn	buffer_type* tmpbuf;
152220080Snwhitehorn	if(!tmpregion) {
153220834Snwhitehorn		printf("out of memory\n");
154220080Snwhitehorn		return;
155220080Snwhitehorn	}
156269653Sthompsa	tmpbuf = buffer_create(region, QIOBUFSZ);
157220080Snwhitehorn	if(!tmpbuf) {
158220080Snwhitehorn		printf("out of memory\n");
159220080Snwhitehorn		return;
160259572Sdteske	}
161218799Snwhitehorn	table = domain_table_create(tmpregion);
162218799Snwhitehorn	if(!table) {
163285679Sallanjude		printf("out of memory\n");
164285679Sallanjude		return;
165285679Sallanjude	}
166285679Sallanjude
167285679Sallanjude	for(i=0; i<num; ++i) {
168285679Sallanjude		rr = packet_read_rr(region, table, pkt, qsection);
169285679Sallanjude		if(!rr) {
170285679Sallanjude			printf("; cannot read rr %d\n", i);
171285679Sallanjude			return;
172285679Sallanjude		}
173285679Sallanjude		if(qsection) {
174285732Sallanjude			printf("%s", dname_to_string(domain_dname(rr->owner),
175285732Sallanjude				NULL));
176285732Sallanjude			printf("\t%s", rrclass_to_string(rr->klass));
177285732Sallanjude			if(rr->type == TYPE_IXFR)
178285732Sallanjude				printf("\tIXFR\n");
179285732Sallanjude			else if(rr->type == TYPE_AXFR)
180285732Sallanjude				printf("\tAXFR\n");
181285732Sallanjude			else printf("\t%s\n", rrtype_to_string(rr->type));
182285679Sallanjude		} else {
183285679Sallanjude			if(!print_rr(stdout, NULL, rr, tmpregion, tmpbuf)) {
184285679Sallanjude				printf("; cannot print rr %d\n", i);
185302320Sallanjude			}
186285679Sallanjude		}
187285679Sallanjude	}
188285679Sallanjude	region_destroy(tmpregion);
189285679Sallanjude}
190285679Sallanjude
191285679Sallanjude/** inspect packet (IXFR or AXFR) */
192285679Sallanjudestatic void
193285679Sallanjudeinspect_packet(region_type* region, buffer_type* pkt)
194285679Sallanjude{
195285679Sallanjude	printf("\n");
196285679Sallanjude	if(buffer_limit(pkt) < QHEADERSZ) {
197285679Sallanjude		printf("packet too short\n");
198302320Sallanjude		return;
199285679Sallanjude	}
200285679Sallanjude	printf("; id=%4.4x ; flags%s%s%s%s%s%s%s%s ; rcode %s ; opcode %d\n",
201285679Sallanjude		ID(pkt), QR(pkt)?" QR":"", AA(pkt)?" AA":"", TC(pkt)?" TC":"",
202285679Sallanjude		RD(pkt)?" RD":"", RA(pkt)?" RA":"", Z(pkt)?" Z":"",
203285679Sallanjude		AD(pkt)?" AD":"", CD(pkt)?" CD":"", rcode2str(RCODE(pkt)),
204285679Sallanjude		OPCODE(pkt));
205285679Sallanjude	printf("; qdcount %d ; ancount %d ; nscount %d ; arcount %d\n",
206285679Sallanjude		QDCOUNT(pkt), ANCOUNT(pkt), NSCOUNT(pkt), ARCOUNT(pkt));
207285679Sallanjude	buffer_skip(pkt, QHEADERSZ);
208285679Sallanjude
209287843Sallanjude	if(QDCOUNT(pkt) != 0) {
210287843Sallanjude		printf("; QUESTION SECTION\n");
211287843Sallanjude		print_records(region, pkt, QDCOUNT(pkt), 1);
212287843Sallanjude	}
213287843Sallanjude	if(ANCOUNT(pkt) != 0) {
214287843Sallanjude		printf("; ANSWER SECTION\n");
215287843Sallanjude		print_records(region, pkt, ANCOUNT(pkt), 0);
216287843Sallanjude	}
217287843Sallanjude	if(NSCOUNT(pkt) != 0) {
218287843Sallanjude		printf("; AUTHORITY SECTION\n");
219287843Sallanjude		print_records(region, pkt, NSCOUNT(pkt), 0);
220287843Sallanjude	}
221287843Sallanjude	if(ARCOUNT(pkt) != 0) {
222285679Sallanjude		printf("; ADDITIONAL SECTION\n");
223285732Sallanjude		print_records(region, pkt, ARCOUNT(pkt), 0);
224285732Sallanjude	}
225285732Sallanjude}
226285732Sallanjude
227285732Sallanjude/** inspect part of xfr file */
228285732Sallanjudestatic void
229287843Sallanjudeinspect_part(FILE* in, int partnum)
230285732Sallanjude{
231285732Sallanjude	uint32_t pkttype, msglen, msglen2;
232285732Sallanjude	region_type* region;
233285732Sallanjude	buffer_type* packet;
234285732Sallanjude	region = region_create(xalloc, free);
235285732Sallanjude	if(!region) {
236285732Sallanjude		printf("out of memory\n");
237285732Sallanjude		fclose(in);
238285732Sallanjude		exit(1);
239285732Sallanjude	}
240287843Sallanjude	packet = buffer_create(region, QIOBUFSZ);
241287843Sallanjude	if(!xi_diff_read_32(in, &pkttype)) {
242287843Sallanjude		printf("cannot read part %d\n", partnum);
243287843Sallanjude		fclose(in);
244287843Sallanjude		exit(1);
245287843Sallanjude	}
246287843Sallanjude	if(pkttype != DIFF_PART_XXFR) {
247287843Sallanjude		printf("bad part %d: not type XXFR\n", partnum);
248287843Sallanjude		fclose(in);
249287843Sallanjude		exit(1);
250287843Sallanjude	}
251287843Sallanjude	if(!xi_diff_read_32(in, &msglen)) {
252287843Sallanjude		printf("bad part %d: not msglen, file too short\n", partnum);
253285732Sallanjude		fclose(in);
254285679Sallanjude		exit(1);
255285679Sallanjude	}
256256343Sdteske	if(msglen < QHEADERSZ || msglen > QIOBUFSZ) {
257271553Snwhitehorn		printf("bad part %d: msglen %u (too short or too long)\n",
258271553Snwhitehorn			partnum, (unsigned)msglen);
259256343Sdteske		fclose(in);
260218799Snwhitehorn		exit(1);
261256343Sdteske	}
262256343Sdteske	if(fread(buffer_begin(packet), msglen, 1, in) != 1) {
263256343Sdteske		printf("bad part %d: short packet, file too short, %s\n",
264271567Snwhitehorn			partnum, strerror(errno));
265256343Sdteske		fclose(in);
266256343Sdteske		exit(1);
267256343Sdteske	}
268256343Sdteske	if(!xi_diff_read_32(in, &msglen2)) {
269256343Sdteske		printf("bad part %d: cannot read msglen2, file too short\n", partnum);
270256343Sdteske		fclose(in);
271256343Sdteske		exit(1);
272256343Sdteske	}
273256343Sdteske	if(v==0) {
274259570Sdteske		region_destroy(region);
275256343Sdteske		return;
276256343Sdteske	}
277256343Sdteske
278271553Snwhitehorn	printf("\n");
279269653Sthompsa	/* printf("type	: %x\n", pkttype); */
280269653Sthompsa	printf("part	: %d\n", partnum);
281218799Snwhitehorn	printf("msglen	: %u\n", (unsigned)msglen);
282256343Sdteske	printf("msglen2	: %u (%s)\n", (unsigned)msglen2,
283218799Snwhitehorn		(msglen==msglen2)?"ok":"wrong");
284218799Snwhitehorn
285222425Snwhitehorn	if(v>=2) {
286218799Snwhitehorn		buffer_set_limit(packet, msglen);
287256343Sdteske		inspect_packet(region, packet);
288257842Sdteske	}
289257842Sdteske
290269653Sthompsa	region_destroy(region);
291257842Sdteske}
292269653Sthompsa
293257842Sdteske/** inspect parts of xfr file */
294269653Sthompsastatic void
295218799Snwhitehorninspect_parts(FILE* in, int num)
296271553Snwhitehorn{
297269653Sthompsa	int i;
298269653Sthompsa	for(i=0; i<num; i++) {
299256343Sdteske		inspect_part(in, i);
300218799Snwhitehorn	}
301269653Sthompsa}
302218799Snwhitehorn
303218799Snwhitehorn/** inspect trail of xfr file */
304218799Snwhitehornstatic void
305218799Snwhitehorninspect_trail(FILE* in)
306218799Snwhitehorn{
307293223Sgjb	char log_buf[5120];
308218799Snwhitehorn	if(!xi_diff_read_str(in, log_buf, sizeof(log_buf))) {
309218799Snwhitehorn		printf("bad trail: cannot read log string\n");
310219528Snwhitehorn		fclose(in);
311269653Sthompsa		exit(1);
312218799Snwhitehorn	}
313218799Snwhitehorn	printf("\n");
314218799Snwhitehorn	printf("log:	%s\n", log_buf);
315220088Snwhitehorn}
316220080Snwhitehorn
317223897Snwhitehorn/** inspect contents of xfr file */
318225637Snwhitehornstatic void
319293223Sgjbinspect_file(char* fname)
320218799Snwhitehorn{
321218799Snwhitehorn	FILE* in;
322218799Snwhitehorn	int num;
323225637Snwhitehorn	log_init("udb-inspect");
324293223Sgjb	if(!(in=fopen(fname, "r"))) {
325293223Sgjb		printf("cannot open %s: %s\n", fname, strerror(errno));
326293223Sgjb		exit(1);
327293223Sgjb	}
328293223Sgjb	printf("file:	%s\n", fname);
329293223Sgjb	num = inspect_header(in);
330293223Sgjb	inspect_parts(in, num);
331293223Sgjb	inspect_trail(in);
332293223Sgjb	fclose(in);
333293223Sgjb}
334293223Sgjb
335293223Sgjb/** list header of xfr file, return num_parts */
336293223Sgjbstatic int
337293223Sgjblist_header(FILE* in)
338293223Sgjb{
339293223Sgjb	char zone_buf[3072];
340293223Sgjb	char patname_buf[2048];
341293223Sgjb
342293223Sgjb	uint32_t old_serial, new_serial, num_parts, type;
343293223Sgjb	uint64_t time_end_0, time_start_0;
344293223Sgjb	uint32_t time_end_1, time_start_1;
345293223Sgjb	uint8_t committed;
346293223Sgjb
347293223Sgjb	time_t time_end, time_start;
348293223Sgjb
349293223Sgjb	if(!xi_diff_read_32(in, &type)) {
350293223Sgjb		printf("could not read type, file short\n");
351293223Sgjb		fclose(in);
352293223Sgjb		exit(1);
353293223Sgjb	}
354293223Sgjb	if(type != DIFF_PART_XFRF) {
355293223Sgjb		printf("type:	%x (BAD FILE TYPE)\n", type);
356293223Sgjb		fclose(in);
357293223Sgjb		exit(1);
358293223Sgjb	}
359218799Snwhitehorn	if(!xi_diff_read_8(in, &committed) ||
360218799Snwhitehorn		!xi_diff_read_32(in, &num_parts) ||
361218799Snwhitehorn		!xi_diff_read_64(in, &time_end_0) ||
362293223Sgjb		!xi_diff_read_32(in, &time_end_1) ||
363293223Sgjb		!xi_diff_read_32(in, &old_serial) ||
364293223Sgjb		!xi_diff_read_32(in, &new_serial) ||
365293223Sgjb		!xi_diff_read_64(in, &time_start_0) ||
366293223Sgjb		!xi_diff_read_32(in, &time_start_1) ||
367293223Sgjb		!xi_diff_read_str(in, zone_buf, sizeof(zone_buf)) ||
368293223Sgjb		!xi_diff_read_str(in, patname_buf, sizeof(patname_buf))) {
369293223Sgjb		printf("diff file bad commit part, file too short");
370293223Sgjb		fclose(in);
371293223Sgjb		exit(1);
372293223Sgjb	}
373293223Sgjb	time_end = (time_t)time_end_0;
374293223Sgjb	time_start = (time_t)time_start_0;
375293223Sgjb
376293223Sgjb	/* printf("; type:		%x\n", (int)type); */
377293223Sgjb	printf("; committed:	%d (%s)\n", (int)committed,
378269653Sthompsa		committed?"yes":"no");
379269653Sthompsa	printf("; num_parts:	%d\n", (int)num_parts);
380269653Sthompsa	printf("; time_end:	%d.%6.6d %s", (int)time_end_0,
381218799Snwhitehorn		(int)time_end_1, ctime(&time_end));
382218799Snwhitehorn	printf("; old_serial:	%u\n", (unsigned)old_serial);
383218799Snwhitehorn	printf("; new_serial:	%u\n", (unsigned)new_serial);
384218799Snwhitehorn	printf("; time_start:	%d.%6.6d %s", (int)time_start_0,
385218799Snwhitehorn		(int)time_start_1, ctime(&time_start));
386218799Snwhitehorn	printf("; zone:		%s\n", zone_buf);
387218799Snwhitehorn	printf("; patname:	%s\n", patname_buf);
388303447Srobak
389218799Snwhitehorn	return num_parts;
390218799Snwhitehorn}
391218799Snwhitehorn
392218799Snwhitehorn/** list packet (IXFR or AXFR) */
393218799Snwhitehornstatic void
394218799Snwhitehornlist_packet(region_type* region, buffer_type* pkt, int partnum)
395218799Snwhitehorn{
396218799Snwhitehorn	if(buffer_limit(pkt) < QHEADERSZ) {
397218799Snwhitehorn		printf("packet too short\n");
398228194Snwhitehorn		return;
399226507Skensmith	}
400218799Snwhitehorn	buffer_skip(pkt, QHEADERSZ);
401218799Snwhitehorn
402218799Snwhitehorn	if(partnum == 0 && QDCOUNT(pkt) == 1) {
403218799Snwhitehorn		/* print query AXFR or IXFR */
404218799Snwhitehorn		printf("; ");
405303447Srobak		print_records(region, pkt, QDCOUNT(pkt), 1);
406218799Snwhitehorn	}
407228194Snwhitehorn	if(ANCOUNT(pkt) != 0) {
408218799Snwhitehorn		print_records(region, pkt, ANCOUNT(pkt), 0);
409218799Snwhitehorn	}
410218799Snwhitehorn}
411218799Snwhitehorn
412218799Snwhitehorn/** list part of xfr file */
413218799Snwhitehornstatic void
414218799Snwhitehornlist_part(FILE* in, int partnum)
415218799Snwhitehorn{
416218799Snwhitehorn	uint32_t pkttype, msglen, msglen2;
417218799Snwhitehorn	region_type* region;
418218799Snwhitehorn	buffer_type* packet;
419218799Snwhitehorn	region = region_create(xalloc, free);
420218799Snwhitehorn	if(!region) {
421218799Snwhitehorn		printf("out of memory\n");
422218799Snwhitehorn		fclose(in);
423218799Snwhitehorn		exit(1);
424218799Snwhitehorn	}
425218799Snwhitehorn	packet = buffer_create(region, QIOBUFSZ);
426218799Snwhitehorn	if(!xi_diff_read_32(in, &pkttype)) {
427218799Snwhitehorn		printf("cannot read part %d\n", partnum);
428218799Snwhitehorn		fclose(in);
429218799Snwhitehorn		exit(1);
430218799Snwhitehorn	}
431303447Srobak	if(pkttype != DIFF_PART_XXFR) {
432303447Srobak		printf("bad part %d: not type XXFR\n", partnum);
433303447Srobak		fclose(in);
434303447Srobak		exit(1);
435218799Snwhitehorn	}
436218799Snwhitehorn	if(!xi_diff_read_32(in, &msglen)) {
437218799Snwhitehorn		printf("bad part %d: not msglen, file too short\n", partnum);
438218799Snwhitehorn		fclose(in);
439223897Snwhitehorn		exit(1);
440223897Snwhitehorn	}
441223897Snwhitehorn	if(msglen < QHEADERSZ || msglen > QIOBUFSZ) {
442223897Snwhitehorn		printf("bad part %d: msglen %u (too short or too long)\n",
443218799Snwhitehorn			partnum, (unsigned)msglen);
444218799Snwhitehorn		fclose(in);
445218799Snwhitehorn		exit(1);
446218799Snwhitehorn	}
447218799Snwhitehorn	if(fread(buffer_begin(packet), msglen, 1, in) != 1) {
448218799Snwhitehorn		printf("bad part %d: short packet, file too short, %s\n",
449218799Snwhitehorn			partnum, strerror(errno));
450269653Sthompsa		fclose(in);
451218799Snwhitehorn		exit(1);
452218799Snwhitehorn	}
453218799Snwhitehorn	if(!xi_diff_read_32(in, &msglen2)) {
454218799Snwhitehorn		printf("bad part %d: cannot read msglen2, file too short\n", partnum);
455218799Snwhitehorn		fclose(in);
456218799Snwhitehorn		exit(1);
457218799Snwhitehorn	}
458228194Snwhitehorn
459271552Snwhitehorn	buffer_set_limit(packet, msglen);
460271552Snwhitehorn	list_packet(region, packet, partnum);
461228194Snwhitehorn	region_destroy(region);
462228194Snwhitehorn}
463232531Snwhitehorn
464228194Snwhitehorn/** list parts of xfr file */
465228194Snwhitehornstatic void
466228194Snwhitehornlist_parts(FILE* in, int num)
467228194Snwhitehorn{
468228194Snwhitehorn	int i;
469256338Sdes	for(i=0; i<num; i++) {
470256338Sdes		list_part(in, i);
471256338Sdes	}
472257842Sdteske}
473218799Snwhitehorn
474257842Sdteske/** list contents of xfr file */
475257842Sdteskestatic void
476257842Sdteskelist_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. */
492extern int optind;
493/** getopt global, in case header files fail to declare it. */
494extern 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 */
502int
503main(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