#! /usr/local/bin/perl
#
#  grc - glue records checker
#
#  invoke as:
#       grc [-l msglevel]
#
#  Copyright (C) 1992, 1993 PUUG - Grupo Portugues de Utilizadores
#				   do Sistema UNIX
#                1992, 1993 FCCN - Fundacao para o Desenvolvimento dos Meios 
#                            	   Nacionais de Calculo Cientifico 
#
#  Authors: Jorge Frazao de Oliveira <frazao@puug.pt>
#           Artur Romao <artur@dns.pt>
#
#  This file is part of the DDT package, Version 2.0.
#
#  Permission to use, copy, modify, and distribute this software and its 
#  documentation for any purpose and without any fee is hereby granted, 
#  provided that the above copyright notice appear in all copies.  Neither 
#  PUUG nor FCCN make any representations about the suitability of this
#  software for any purpose.  It is provided "as is" without express or 
#  implied warranty.


# =()<push(@INC, "@<LIBDIR>@");>()=
push(@INC, "/usr/local/lib/ddt/cmd");

require "ddt.pl";


# scan the standard input
while (<STDIN>) {
	next if /^;\s+Ignoring info/;	# if commented lines are not 
					# of this form
	s/^;\s+//;			# check them too

	chop;				# strip record separator
	@Field = split(/\s+/, $_);	# break the input line

	if (/^\$ORIGIN/) {
		$Origin = $Field[2];	# set to a different origin
	
		next;
	}

    	if (/^[*\.\-0-9A-Za-z]+/) { 
		$Name = &make_name($Field[1], $Origin);
	}

    	if (/\tIN\tSOA\t/) {
		&show_zone($Zone);	# show results for the former zone

		$Zone = $Name;		# and let's start examinig this new one

		# get rid of the past
		undef %Address;
		undef %IsNS;
		undef %IsSubzoneNS;
	}
    	elsif (/\tIN\tNS/) {
		&new_NS($Name, $Zone, $Field[$#Field]);	# check the NS record
	}
    	elsif (/\tIN\tA/) {
		$Address{$Name, $Field[$#Field]} = 1;	# there's an address 
							# for this name
	}
}

&show_zone($Zone);	# see the last zone

exit 0;

#
# determine if we're interested in this name server, and why
#
sub new_NS {
	local($name, $zone, $server) = @_;

	$server = &tolower($server);

    	if (&is_below_cut($zone, $name)) {
		$IsSubzoneNS{$server} = 1;	# $server is NS for a sub-zone
						# of this zone 
		$IsNS{$server}        = 1;	# flag it as a name server
    	}
   	else {
		if ($name eq $zone) {		# $server is a name server (for
			$IsNS{$server} = 1;	# this zone, that's why we keep
		}				# track of it)
    	}
}


#
# check the necessity and existence of the A RR's for the found name servers
#
sub check_glue_records {
	local($zone) = @_;
	local(*NecessaryGR, *UnnecessaryGR, *MissingGR);
	local($host, $name, $address, @HasAddress);
	local($s, $e, $t);

    	foreach $host (keys %Address) {
		($name, $address)  = split($;, $host);
		$HasAddress{$name} = 1;

		if ($Level >= 4) {
			$str = sprintf("%s%-25s IN\tA\t%s", 
				       $Lpad[4], $name, $address);

			if ($IsNS{$name}) {	# we have a name server...
				if (&is_below_cut($zone, $name) && 
				   $IsSubzoneNS{$name}) {
					# ... belonging to a delegated domain,
					# so we must have its address listed 
					# in our zone file

					push(@NecessaryGR, $str);
				}
				elsif (!&in_THIS_zone($name, $zone)) {
					# ... whose address can be found
					# somewhere else in the DNS tree

					push(@UnnecessaryGR, $str);
				}
			}
		}
	}

	if ($Level >= 2) {
		foreach $host (keys %IsNS) {
			if (!$HasAddress{$host} && 
			    &is_below_cut($zone, $host) &&
                	    $IsSubzoneNS{$host}) {
				# this name server belongs to a sub-zone, 
				# but its address is not listed in this 
				# zone definition

				push(@MissingGR, sprintf("%-8s%s", "", $host));
			}
		}
	}

	if ($#NecessaryGR) {
		$s = $#UnnecessaryGR > 1 ? "s" : "";

		print "\n$Lpad[4]Necessary glue record$s (name server$s of delegated domains):";

		foreach $str ($[ .. $#NecessaryGR) {
			print shift(@NecessaryGR);
		}
	}

    	if ($#UnnecessaryGR) {
		$s = $#UnnecessaryGR > 1 ? "s" : "";

		print "\n$Lpad[4]Unnecessary glue record$s (if present, should be removed):";

		foreach $str ($[ .. $#UnnecessaryGR) {
			print shift(@UnnecessaryGR);
		}
	}

    	if ($#MissingGR) {
		($s, $e, $t) = $#MissingGR > 1 ? ("s", "es", "these") :
					         ("", "", "this");

		print "\n$Lpad[2]Missing glue record$s (the address$e of $t host$s should be present):";

		foreach $str ($[ .. $#MissingGR) {
			print shift(@MissingGR);
		}
	}		
}


#
# show the results for (real) zones
#
sub show_zone {
	local($zone) = @_;

	if ($zone ne "") {
		print "\n ###",  &toupper($zone), "###";
		
		&check_glue_records($zone);
	}
}
