freeswitch/libs/ldns/examples/ldns-compare-zones.c

284 lines
6.4 KiB
C

/*
* ldns-compare-zones compares two zone files
*
* Written by Ondrej Sury in 2007
*
* Modified a bit by NLnet Labs.
*
* See the file LICENSE for the license
*/
#include "config.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <ldns/ldns.h>
#include <errno.h>
#define OP_INS '+'
#define OP_DEL '-'
#define OP_CHG '~'
static void
usage(int argc, char **argv)
{
printf("Usage: %s [-v] [-i] [-d] [-c] [-s] <zonefile1> <zonefile2>\n",
argv[0]);
printf(" -i - print inserted\n");
printf(" -d - print deleted\n");
printf(" -c - print changed\n");
printf(" -a - print all differences (-i -d -c)\n");
printf(" -s - do not exclude SOA record from comparison\n");
printf(" -z - do not sort zones\n");
}
int
main(int argc, char **argv)
{
char *fn1, *fn2;
FILE *fp1, *fp2;
ldns_zone *z1, *z2;
ldns_status s;
size_t i , j;
ldns_rr_list *rrl1, *rrl2;
int rr_cmp, rr_chg = 0;
ldns_rr *rr1 = NULL, *rr2 = NULL, *rrx = NULL;
int line_nr1 = 0, line_nr2 = 0;
size_t rrc1 , rrc2;
size_t num_ins = 0, num_del = 0, num_chg = 0;
int c;
bool opt_deleted = false, opt_inserted = false, opt_changed = false;
bool sort = true, inc_soa = false;
char op = 0;
while ((c = getopt(argc, argv, "ahvdicsz")) != -1) {
switch (c) {
case 'h':
usage(argc, argv);
exit(EXIT_SUCCESS);
break;
case 'v':
printf("%s version %s (ldns version %s)\n",
argv[0],
LDNS_VERSION,
ldns_version());
exit(EXIT_SUCCESS);
break;
case 's':
inc_soa = true;
break;
case 'z':
sort = false;
break;
case 'd':
opt_deleted = true;
break;
case 'i':
opt_inserted = true;
break;
case 'c':
opt_changed = true;
break;
case 'a':
opt_deleted = true;
opt_inserted = true;
opt_changed = true;
break;
}
}
argc -= optind;
argv += optind;
if (argc != 2) {
argc -= optind;
argv -= optind;
usage(argc, argv);
exit(EXIT_FAILURE);
}
fn1 = argv[0];
fp1 = fopen(fn1, "r");
if (!fp1) {
fprintf(stderr, "Unable to open %s: %s\n", fn1, strerror(errno));
exit(EXIT_FAILURE);
}
/* Read first zone */
s = ldns_zone_new_frm_fp_l(&z1, fp1, NULL, 0,
LDNS_RR_CLASS_IN, &line_nr1);
if (s != LDNS_STATUS_OK) {
fclose(fp1);
fprintf(stderr, "%s: %s at %d\n",
fn1,
ldns_get_errorstr_by_id(s),
line_nr1);
exit(EXIT_FAILURE);
}
fclose(fp1);
fn2 = argv[1];
fp2 = fopen(fn2, "r");
if (!fp2) {
fprintf(stderr, "Unable to open %s: %s\n", fn2, strerror(errno));
exit(EXIT_FAILURE);
}
/* Read second zone */
s = ldns_zone_new_frm_fp_l(&z2, fp2, NULL, 0,
LDNS_RR_CLASS_IN, &line_nr2);
if (s != LDNS_STATUS_OK) {
ldns_zone_deep_free(z1);
fclose(fp2);
fprintf(stderr, "%s: %s at %d\n",
fn2,
ldns_get_errorstr_by_id(s),
line_nr2);
exit(EXIT_FAILURE);
}
fclose(fp2);
rrl1 = ldns_zone_rrs(z1);
rrc1 = ldns_rr_list_rr_count(rrl1);
rrl2 = ldns_zone_rrs(z2);
rrc2 = ldns_rr_list_rr_count(rrl2);
if (sort) {
/* canonicalize zone 1 */
ldns_rr2canonical(ldns_zone_soa(z1));
for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(z1)); i++) {
ldns_rr2canonical(ldns_rr_list_rr(ldns_zone_rrs(z1), i));
}
/* sort zone 1 */
ldns_zone_sort(z1);
/* canonicalize zone 2 */
ldns_rr2canonical(ldns_zone_soa(z2));
for (i = 0; i < ldns_rr_list_rr_count(ldns_zone_rrs(z2)); i++) {
ldns_rr2canonical(ldns_rr_list_rr(ldns_zone_rrs(z2), i));
}
/* sort zone 2 */
ldns_zone_sort(z2);
}
if(inc_soa) {
ldns_rr_list* wsoa = ldns_rr_list_new();
ldns_rr_list_push_rr(wsoa, ldns_zone_soa(z1));
ldns_rr_list_cat(wsoa, rrl1);
rrl1 = wsoa;
rrc1 = ldns_rr_list_rr_count(rrl1);
wsoa = ldns_rr_list_new();
ldns_rr_list_push_rr(wsoa, ldns_zone_soa(z2));
ldns_rr_list_cat(wsoa, rrl2);
rrl2 = wsoa;
rrc2 = ldns_rr_list_rr_count(rrl2);
if(sort) {
ldns_rr_list_sort(rrl1);
ldns_rr_list_sort(rrl2);
}
}
/*
* Walk through both zones. The previously seen resource record is
* kept (in the variable rrx) so that we can recognize when we are
* handling a new owner name. If the owner name changes, we have to
* set the operator again.
*/
for (i = 0, j = 0; i < rrc1 || j < rrc2;) {
rr_cmp = 0;
if (i < rrc1 && j < rrc2) {
rr1 = ldns_rr_list_rr(rrl1, i);
rr2 = ldns_rr_list_rr(rrl2, j);
rr_cmp = ldns_rr_compare(rr1, rr2);
/* Completely skip if the rrs are equal */
if (rr_cmp == 0) {
i++;
j++;
continue;
}
rr_chg = ldns_dname_compare(ldns_rr_owner(rr1),
ldns_rr_owner(rr2));
} else if (i >= rrc1) {
/* we have reached the end of zone 1, so the current record
* from zone 2 automatically sorts higher
*/
rr1 = NULL;
rr2 = ldns_rr_list_rr(rrl2, j);
rr_chg = rr_cmp = 1;
} else if (j >= rrc2) {
/* we have reached the end of zone 2, so the current record
* from zone 1 automatically sorts lower
*/
rr1 = ldns_rr_list_rr(rrl1, i);
rr2 = NULL;
rr_chg = rr_cmp = -1;
}
if (rr_cmp < 0) {
i++;
if ((rrx != NULL) && (ldns_dname_compare(ldns_rr_owner(rr1),
ldns_rr_owner(rrx)
) != 0)) {
/* The owner name is different, forget previous rr */
rrx = NULL;
}
if (rrx == NULL) {
if (rr_chg == 0) {
num_chg++;
op = OP_CHG;
} else {
num_del++;
op = OP_DEL;
}
rrx = rr1;
}
if (((op == OP_DEL) && opt_deleted) ||
((op == OP_CHG) && opt_changed)) {
printf("%c-", op);
ldns_rr_print(stdout, rr1);
}
} else if (rr_cmp > 0) {
j++;
if ((rrx != NULL) && (ldns_dname_compare(ldns_rr_owner(rr2),
ldns_rr_owner(rrx)
) != 0)) {
rrx = NULL;
}
if (rrx == NULL) {
if (rr_chg == 0) {
num_chg++;
op = OP_CHG;
} else {
num_ins++;
op = OP_INS;
}
/* remember this rr for it's name in the next iteration */
rrx = rr2;
}
if (((op == OP_INS) && opt_inserted) ||
((op == OP_CHG) && opt_changed)) {
printf("%c+", op);
ldns_rr_print(stdout, rr2);
}
}
}
printf("\t%c%u\t%c%u\t%c%u\n",
OP_INS,
(unsigned int) num_ins,
OP_DEL,
(unsigned int) num_del,
OP_CHG,
(unsigned int) num_chg);
/* Free resources */
if(inc_soa) {
ldns_rr_list_free(rrl1);
ldns_rr_list_free(rrl2);
}
ldns_zone_deep_free(z2);
ldns_zone_deep_free(z1);
return 0;
}