1#!/usr/bin/perl
2
3# Copyright (C) Internet Systems Consortium, Inc. ("ISC")
4#
5# SPDX-License-Identifier: MPL-2.0
6#
7# This Source Code Form is subject to the terms of the Mozilla Public
8# License, v. 2.0.  If a copy of the MPL was not distributed with this
9# file, you can obtain one at https://mozilla.org/MPL/2.0/.
10#
11# See the COPYRIGHT file distributed with this work for additional
12# information regarding copyright ownership.
13
14# Compare two files, each with the output from dig, for differences.
15# Ignore "unimportant" differences, like ordering of NS lines, TTL's,
16# etc...
17
18$lc = 0;
19if ($ARGV[0] eq "--lc") {
20	$lc = 1;
21	shift;
22}
23$file1 = $ARGV[0];
24$file2 = $ARGV[1];
25
26$count = 0;
27$firstname = "";
28$status = 0;
29$rcode1 = "none";
30$rcode2 = "none";
31
32open(FILE1, $file1) || die("open: $file1: $!\n");
33while (<FILE1>) {
34	~ s/\r\n//g;
35	~ s/\n//g;
36	if (/^;.+status:\s+(\S+).+$/) {
37		$rcode1 = $1;
38	}
39	next if (/^;/);
40	if (/^(\S+)\s+\S+\s+(\S+)\s+(\S+)\s+(.+)$/) {
41		$name = $1;
42		$class = $2;
43		$type = $3;
44		$value = $4;
45		if ($lc) {
46			$name = lc($name);
47			$value = lc($value);
48		}
49		if ($type eq "SOA") {
50			$firstname = $name if ($firstname eq "");
51			if ($name eq $firstname) {
52				$name = "$name$count";
53				$count++;
54			}
55		}
56		if ($entry{"$name ; $class.$type ; $value"} ne "") {
57			$line = $entry{"$name ; $class.$type ; $value"};
58			print("Duplicate entry in $file1:\n> $_\n< $line\n");
59		} else {
60			$entry{"$name ; $class.$type ; $value"} = $_;
61		}
62	} elsif (/^(\S+)\s+\S+\s+(\S+)\s+(\S+)\s*$/) {
63		$name = $1;
64		$class = $2;
65		$type = $3;
66		$value = "";
67		if ($lc) {
68			$name = lc($name);
69			$value = lc($value);
70		}
71		if ($type eq "SOA") {
72			$firstname = $name if ($firstname eq "");
73			if ($name eq $firstname) {
74				$name = "$name$count";
75				$count++;
76			}
77		}
78		if ($entry{"$name ; $class.$type ; $value"} ne "") {
79			$line = $entry{"$name ; $class.$type ; $value"};
80			print("Duplicate entry in $file1:\n> $_\n< $line\n");
81		} else {
82			$entry{"$name ; $class.$type ; $value"} = $_;
83		}
84	}
85}
86close(FILE1);
87
88$printed = 0;
89
90open(FILE2, $file2) || die("open: $file2: $!\n");
91while (<FILE2>) {
92	~ s/\r\n//g;
93	~ s/\n//g;
94	if (/^;.+status:\s+(\S+).+$/) {
95		$rcode2 = $1;
96	}
97	next if (/^;/);
98	if (/^(\S+)\s+\S+\s+(\S+)\s+(\S+)\s+(.+)$/) {
99		$name = $1;
100		$class = $2;
101		$type = $3;
102		$value = $4;
103		if ($lc) {
104			$name = lc($name);
105			$value = lc($value);
106		}
107		if (($name eq $firstname) && ($type eq "SOA")) {
108			$count--;
109			$name = "$name$count";
110		}
111		if ($entry{"$name ; $class.$type ; $value"} ne "") {
112			$entry{"$name ; $class.$type ; $value"} = "";
113		} else {
114			print("Only in $file2 (missing from $file1):\n")
115			    if ($printed == 0);
116			print("> $_\n");
117			$printed++;
118			$status = 1;
119		}
120	} elsif (/^(\S+)\s+\S+\s+(\S+)\s+(\S+)\s*$/) {
121		$name = $1;
122		$class = $2;
123		$type = $3;
124		$value = "";
125		if ($lc) {
126			$name = lc($name);
127			$value = lc($value);
128		}
129		if (($name eq $firstname) && ($type eq "SOA")) {
130			$count--;
131			$name = "$name$count";
132		}
133		if ($entry{"$name ; $class.$type ; $value"} ne "") {
134			$entry{"$name ; $class.$type ; $value"} = "";
135		} else {
136			print("Only in $file2 (missing from $file1):\n")
137			    if ($printed == 0);
138			print("> $_\n");
139			$printed++;
140			$status = 1;
141		}
142	}
143}
144close(FILE2);
145
146$printed = 0;
147
148foreach $key (keys(%entry)) {
149	if ($entry{$key} ne "") {
150		print("Only in $file1 (missing from $file2):\n")
151		    if ($printed == 0);
152		print("< $entry{$key}\n");
153		$status = 1;
154		$printed++;
155	}
156}
157
158if ($rcode1 ne $rcode2) {
159	print("< status: $rcode1\n");
160	print("> status: $rcode2\n");
161	$status = 1;
162}
163
164exit($status);
165