531 lines
12 KiB
C
531 lines
12 KiB
C
/* xfr-inspect - list the contents and inspect an zone transfer XFR file
|
|
* By W.C.A. Wijngaards
|
|
* Copyright 2017, NLnet Labs.
|
|
* BSD, see LICENSE.
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "util.h"
|
|
#include "buffer.h"
|
|
#include "packet.h"
|
|
#include "rdata.h"
|
|
#include "namedb.h"
|
|
#include "difffile.h"
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
|
|
/** verbosity for inspect */
|
|
static int v = 0;
|
|
/** shorthand for ease */
|
|
#ifdef ULL
|
|
#undef ULL
|
|
#endif
|
|
#define ULL (unsigned long long)
|
|
|
|
/** print usage text */
|
|
static void
|
|
usage(void)
|
|
{
|
|
printf("usage: xfr-inspect [options] file\n");
|
|
printf(" -h this help\n");
|
|
printf(" -v increase verbosity: "
|
|
"with -v(list chunks), -vv(inside chunks)\n");
|
|
printf(" -l list contents of transfer\n");
|
|
}
|
|
|
|
static int
|
|
xi_diff_read_64(FILE *in, uint64_t* result)
|
|
{
|
|
if (fread(result, sizeof(*result), 1, in) == 1) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
xi_diff_read_32(FILE *in, uint32_t* result)
|
|
{
|
|
if (fread(result, sizeof(*result), 1, in) == 1) {
|
|
*result = ntohl(*result);
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
xi_diff_read_8(FILE *in, uint8_t* result)
|
|
{
|
|
if (fread(result, sizeof(*result), 1, in) == 1) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
xi_diff_read_str(FILE* in, char* buf, size_t len)
|
|
{
|
|
uint32_t disklen;
|
|
if(!xi_diff_read_32(in, &disklen))
|
|
return 0;
|
|
if(disklen >= len)
|
|
return 0;
|
|
if(fread(buf, disklen, 1, in) != 1)
|
|
return 0;
|
|
buf[disklen] = 0;
|
|
return 1;
|
|
}
|
|
|
|
|
|
/** inspect header of xfr file, return num_parts */
|
|
static int
|
|
inspect_header(FILE* in)
|
|
{
|
|
char zone_buf[3072];
|
|
char patname_buf[2048];
|
|
|
|
uint32_t old_serial, new_serial, num_parts, type;
|
|
uint64_t time_end_0, time_start_0;
|
|
uint32_t time_end_1, time_start_1;
|
|
uint8_t committed;
|
|
|
|
time_t time_end, time_start;
|
|
|
|
if(!xi_diff_read_32(in, &type)) {
|
|
printf("could not read type, file short\n");
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(type != DIFF_PART_XFRF) {
|
|
printf("type: %x (BAD FILE TYPE)\n", type);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(!xi_diff_read_8(in, &committed) ||
|
|
!xi_diff_read_32(in, &num_parts) ||
|
|
!xi_diff_read_64(in, &time_end_0) ||
|
|
!xi_diff_read_32(in, &time_end_1) ||
|
|
!xi_diff_read_32(in, &old_serial) ||
|
|
!xi_diff_read_32(in, &new_serial) ||
|
|
!xi_diff_read_64(in, &time_start_0) ||
|
|
!xi_diff_read_32(in, &time_start_1) ||
|
|
!xi_diff_read_str(in, zone_buf, sizeof(zone_buf)) ||
|
|
!xi_diff_read_str(in, patname_buf, sizeof(patname_buf))) {
|
|
printf("diff file bad commit part, file too short");
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
time_end = (time_t)time_end_0;
|
|
time_start = (time_t)time_start_0;
|
|
|
|
/* printf("type: %x\n", (int)type); */
|
|
printf("committed: %d (%s)\n", (int)committed,
|
|
committed?"yes":"no");
|
|
printf("num_parts: %d\n", (int)num_parts);
|
|
printf("time_end: %d.%6.6d %s", (int)time_end_0,
|
|
(int)time_end_1, ctime(&time_end));
|
|
printf("old_serial: %u\n", (unsigned)old_serial);
|
|
printf("new_serial: %u\n", (unsigned)new_serial);
|
|
printf("time_start: %d.%6.6d %s", (int)time_start_0,
|
|
(int)time_start_1, ctime(&time_start));
|
|
printf("zone: %s\n", zone_buf);
|
|
printf("patname: %s\n", patname_buf);
|
|
|
|
return num_parts;
|
|
}
|
|
|
|
/** print records in packet */
|
|
static void
|
|
print_records(region_type* region, buffer_type* pkt, int num, int qsection)
|
|
{
|
|
domain_table_type* table;
|
|
int i;
|
|
rr_type* rr;
|
|
region_type* tmpregion = region_create(xalloc, free);
|
|
buffer_type* tmpbuf;
|
|
if(!tmpregion) {
|
|
printf("out of memory\n");
|
|
return;
|
|
}
|
|
tmpbuf = buffer_create(region, QIOBUFSZ);
|
|
if(!tmpbuf) {
|
|
printf("out of memory\n");
|
|
return;
|
|
}
|
|
table = domain_table_create(tmpregion);
|
|
if(!table) {
|
|
printf("out of memory\n");
|
|
return;
|
|
}
|
|
|
|
for(i=0; i<num; ++i) {
|
|
rr = packet_read_rr(region, table, pkt, qsection);
|
|
if(!rr) {
|
|
printf("; cannot read rr %d\n", i);
|
|
return;
|
|
}
|
|
if(qsection) {
|
|
printf("%s", dname_to_string(domain_dname(rr->owner),
|
|
NULL));
|
|
printf("\t%s", rrclass_to_string(rr->klass));
|
|
if(rr->type == TYPE_IXFR)
|
|
printf("\tIXFR\n");
|
|
else if(rr->type == TYPE_AXFR)
|
|
printf("\tAXFR\n");
|
|
else printf("\t%s\n", rrtype_to_string(rr->type));
|
|
} else {
|
|
if(!print_rr(stdout, NULL, rr, tmpregion, tmpbuf)) {
|
|
printf("; cannot print rr %d\n", i);
|
|
}
|
|
}
|
|
}
|
|
region_destroy(tmpregion);
|
|
}
|
|
|
|
/** inspect packet (IXFR or AXFR) */
|
|
static void
|
|
inspect_packet(region_type* region, buffer_type* pkt)
|
|
{
|
|
printf("\n");
|
|
if(buffer_limit(pkt) < QHEADERSZ) {
|
|
printf("packet too short\n");
|
|
return;
|
|
}
|
|
printf("; id=%4.4x ; flags%s%s%s%s%s%s%s%s ; rcode %s ; opcode %d\n",
|
|
ID(pkt), QR(pkt)?" QR":"", AA(pkt)?" AA":"", TC(pkt)?" TC":"",
|
|
RD(pkt)?" RD":"", RA(pkt)?" RA":"", Z(pkt)?" Z":"",
|
|
AD(pkt)?" AD":"", CD(pkt)?" CD":"", rcode2str(RCODE(pkt)),
|
|
OPCODE(pkt));
|
|
printf("; qdcount %d ; ancount %d ; nscount %d ; arcount %d\n",
|
|
QDCOUNT(pkt), ANCOUNT(pkt), NSCOUNT(pkt), ARCOUNT(pkt));
|
|
buffer_skip(pkt, QHEADERSZ);
|
|
|
|
if(QDCOUNT(pkt) != 0) {
|
|
printf("; QUESTION SECTION\n");
|
|
print_records(region, pkt, QDCOUNT(pkt), 1);
|
|
}
|
|
if(ANCOUNT(pkt) != 0) {
|
|
printf("; ANSWER SECTION\n");
|
|
print_records(region, pkt, ANCOUNT(pkt), 0);
|
|
}
|
|
if(NSCOUNT(pkt) != 0) {
|
|
printf("; AUTHORITY SECTION\n");
|
|
print_records(region, pkt, NSCOUNT(pkt), 0);
|
|
}
|
|
if(ARCOUNT(pkt) != 0) {
|
|
printf("; ADDITIONAL SECTION\n");
|
|
print_records(region, pkt, ARCOUNT(pkt), 0);
|
|
}
|
|
}
|
|
|
|
/** inspect part of xfr file */
|
|
static void
|
|
inspect_part(FILE* in, int partnum)
|
|
{
|
|
uint32_t pkttype, msglen, msglen2;
|
|
region_type* region;
|
|
buffer_type* packet;
|
|
region = region_create(xalloc, free);
|
|
if(!region) {
|
|
printf("out of memory\n");
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
packet = buffer_create(region, QIOBUFSZ);
|
|
if(!xi_diff_read_32(in, &pkttype)) {
|
|
printf("cannot read part %d\n", partnum);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(pkttype != DIFF_PART_XXFR) {
|
|
printf("bad part %d: not type XXFR\n", partnum);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(!xi_diff_read_32(in, &msglen)) {
|
|
printf("bad part %d: not msglen, file too short\n", partnum);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(msglen < QHEADERSZ || msglen > QIOBUFSZ) {
|
|
printf("bad part %d: msglen %u (too short or too long)\n",
|
|
partnum, (unsigned)msglen);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(fread(buffer_begin(packet), msglen, 1, in) != 1) {
|
|
printf("bad part %d: short packet, file too short, %s\n",
|
|
partnum, strerror(errno));
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(!xi_diff_read_32(in, &msglen2)) {
|
|
printf("bad part %d: cannot read msglen2, file too short\n", partnum);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(v==0) {
|
|
region_destroy(region);
|
|
return;
|
|
}
|
|
|
|
printf("\n");
|
|
/* printf("type : %x\n", pkttype); */
|
|
printf("part : %d\n", partnum);
|
|
printf("msglen : %u\n", (unsigned)msglen);
|
|
printf("msglen2 : %u (%s)\n", (unsigned)msglen2,
|
|
(msglen==msglen2)?"ok":"wrong");
|
|
|
|
if(v>=2) {
|
|
buffer_set_limit(packet, msglen);
|
|
inspect_packet(region, packet);
|
|
}
|
|
|
|
region_destroy(region);
|
|
}
|
|
|
|
/** inspect parts of xfr file */
|
|
static void
|
|
inspect_parts(FILE* in, int num)
|
|
{
|
|
int i;
|
|
for(i=0; i<num; i++) {
|
|
inspect_part(in, i);
|
|
}
|
|
}
|
|
|
|
/** inspect trail of xfr file */
|
|
static void
|
|
inspect_trail(FILE* in)
|
|
{
|
|
char log_buf[5120];
|
|
if(!xi_diff_read_str(in, log_buf, sizeof(log_buf))) {
|
|
printf("bad trail: cannot read log string\n");
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
printf("\n");
|
|
printf("log: %s\n", log_buf);
|
|
}
|
|
|
|
/** inspect contents of xfr file */
|
|
static void
|
|
inspect_file(char* fname)
|
|
{
|
|
FILE* in;
|
|
int num;
|
|
log_init("udb-inspect");
|
|
if(!(in=fopen(fname, "r"))) {
|
|
printf("cannot open %s: %s\n", fname, strerror(errno));
|
|
exit(1);
|
|
}
|
|
printf("file: %s\n", fname);
|
|
num = inspect_header(in);
|
|
inspect_parts(in, num);
|
|
inspect_trail(in);
|
|
fclose(in);
|
|
}
|
|
|
|
/** list header of xfr file, return num_parts */
|
|
static int
|
|
list_header(FILE* in)
|
|
{
|
|
char zone_buf[3072];
|
|
char patname_buf[2048];
|
|
|
|
uint32_t old_serial, new_serial, num_parts, type;
|
|
uint64_t time_end_0, time_start_0;
|
|
uint32_t time_end_1, time_start_1;
|
|
uint8_t committed;
|
|
|
|
time_t time_end, time_start;
|
|
|
|
if(!xi_diff_read_32(in, &type)) {
|
|
printf("could not read type, file short\n");
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(type != DIFF_PART_XFRF) {
|
|
printf("type: %x (BAD FILE TYPE)\n", type);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(!xi_diff_read_8(in, &committed) ||
|
|
!xi_diff_read_32(in, &num_parts) ||
|
|
!xi_diff_read_64(in, &time_end_0) ||
|
|
!xi_diff_read_32(in, &time_end_1) ||
|
|
!xi_diff_read_32(in, &old_serial) ||
|
|
!xi_diff_read_32(in, &new_serial) ||
|
|
!xi_diff_read_64(in, &time_start_0) ||
|
|
!xi_diff_read_32(in, &time_start_1) ||
|
|
!xi_diff_read_str(in, zone_buf, sizeof(zone_buf)) ||
|
|
!xi_diff_read_str(in, patname_buf, sizeof(patname_buf))) {
|
|
printf("diff file bad commit part, file too short");
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
time_end = (time_t)time_end_0;
|
|
time_start = (time_t)time_start_0;
|
|
|
|
/* printf("; type: %x\n", (int)type); */
|
|
printf("; committed: %d (%s)\n", (int)committed,
|
|
committed?"yes":"no");
|
|
printf("; num_parts: %d\n", (int)num_parts);
|
|
printf("; time_end: %d.%6.6d %s", (int)time_end_0,
|
|
(int)time_end_1, ctime(&time_end));
|
|
printf("; old_serial: %u\n", (unsigned)old_serial);
|
|
printf("; new_serial: %u\n", (unsigned)new_serial);
|
|
printf("; time_start: %d.%6.6d %s", (int)time_start_0,
|
|
(int)time_start_1, ctime(&time_start));
|
|
printf("; zone: %s\n", zone_buf);
|
|
printf("; patname: %s\n", patname_buf);
|
|
|
|
return num_parts;
|
|
}
|
|
|
|
/** list packet (IXFR or AXFR) */
|
|
static void
|
|
list_packet(region_type* region, buffer_type* pkt, int partnum)
|
|
{
|
|
if(buffer_limit(pkt) < QHEADERSZ) {
|
|
printf("packet too short\n");
|
|
return;
|
|
}
|
|
buffer_skip(pkt, QHEADERSZ);
|
|
|
|
if(partnum == 0 && QDCOUNT(pkt) == 1) {
|
|
/* print query AXFR or IXFR */
|
|
printf("; ");
|
|
print_records(region, pkt, QDCOUNT(pkt), 1);
|
|
}
|
|
if(ANCOUNT(pkt) != 0) {
|
|
print_records(region, pkt, ANCOUNT(pkt), 0);
|
|
}
|
|
}
|
|
|
|
/** list part of xfr file */
|
|
static void
|
|
list_part(FILE* in, int partnum)
|
|
{
|
|
uint32_t pkttype, msglen, msglen2;
|
|
region_type* region;
|
|
buffer_type* packet;
|
|
region = region_create(xalloc, free);
|
|
if(!region) {
|
|
printf("out of memory\n");
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
packet = buffer_create(region, QIOBUFSZ);
|
|
if(!xi_diff_read_32(in, &pkttype)) {
|
|
printf("cannot read part %d\n", partnum);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(pkttype != DIFF_PART_XXFR) {
|
|
printf("bad part %d: not type XXFR\n", partnum);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(!xi_diff_read_32(in, &msglen)) {
|
|
printf("bad part %d: not msglen, file too short\n", partnum);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(msglen < QHEADERSZ || msglen > QIOBUFSZ) {
|
|
printf("bad part %d: msglen %u (too short or too long)\n",
|
|
partnum, (unsigned)msglen);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(fread(buffer_begin(packet), msglen, 1, in) != 1) {
|
|
printf("bad part %d: short packet, file too short, %s\n",
|
|
partnum, strerror(errno));
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
if(!xi_diff_read_32(in, &msglen2)) {
|
|
printf("bad part %d: cannot read msglen2, file too short\n", partnum);
|
|
fclose(in);
|
|
exit(1);
|
|
}
|
|
|
|
buffer_set_limit(packet, msglen);
|
|
list_packet(region, packet, partnum);
|
|
region_destroy(region);
|
|
}
|
|
|
|
/** list parts of xfr file */
|
|
static void
|
|
list_parts(FILE* in, int num)
|
|
{
|
|
int i;
|
|
for(i=0; i<num; i++) {
|
|
list_part(in, i);
|
|
}
|
|
}
|
|
|
|
/** list contents of xfr file */
|
|
static void
|
|
list_file(char* fname)
|
|
{
|
|
FILE* in;
|
|
int num;
|
|
log_init("udb-inspect");
|
|
if(!(in=fopen(fname, "r"))) {
|
|
printf("cannot open %s: %s\n", fname, strerror(errno));
|
|
exit(1);
|
|
}
|
|
num = list_header(in);
|
|
list_parts(in, num);
|
|
|
|
fclose(in);
|
|
}
|
|
|
|
/** getopt global, in case header files fail to declare it. */
|
|
extern int optind;
|
|
/** getopt global, in case header files fail to declare it. */
|
|
extern char* optarg;
|
|
|
|
/**
|
|
* main program. Set options given commandline arguments.
|
|
* @param argc: number of commandline arguments.
|
|
* @param argv: array of commandline arguments.
|
|
* @return: exit status of the program.
|
|
*/
|
|
int
|
|
main(int argc, char* argv[])
|
|
{
|
|
int c, list=0;
|
|
while( (c=getopt(argc, argv, "hlv")) != -1) {
|
|
switch(c) {
|
|
case 'l':
|
|
list=1;
|
|
break;
|
|
case 'v':
|
|
v++;
|
|
break;
|
|
default:
|
|
case 'h':
|
|
usage();
|
|
return 1;
|
|
}
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
if(argc != 1) {
|
|
usage();
|
|
return 1;
|
|
}
|
|
if(list) list_file(argv[0]);
|
|
else inspect_file(argv[0]);
|
|
|
|
return 0;
|
|
}
|