#! /usr/local/bin/perl
#
#  mxc - mail exchange (MX) checker
#
#  invoke as:
#	mxc [-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 /^;/;			# ignore commented lines

	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/) {
		$Zone = $Name;		# there's a new zone
	}
    	elsif (/\tIN\tMX\t/) {
		# add a new MX RR to the list
		&new_MX($Zone, $Name, $Field[$#Field - 1], $Field[$#Field]);
    	}
    	elsif (/\tIN\tCNAME\t/) {
	    	$IsAlias{$Name} = 1;	# this name is an alias
    	}
}

foreach $Zone (keys %MX) {
	&show_zone($Zone);
}

exit 0;


#
# examine each of the $zone's MX's, and print the anomalies
#
sub show_zone {
	local($zone) = @_;
	local(@MXlist, $name, $rest, @NMX);

        print " ### ", &toupper($zone), " ###";

	@MXlist = split($LISTsep, $MX{$zone});	# this is the list of 
						# the zone's MX's
						
        undef %NMX;	# or else it survives between calls

	while ($mx = shift(@MXlist)) {
		# get the values of the RR...
                ($name, $rest) = split($ELEMsep2, $mx);

		# ... and put them in the "private" list for this name
                $NMX{$name} = &add_list($NMX{$name}, $rest);
        }

        foreach $name (keys %NMX) {
                &check_MX($name, $NMX{$name});
        }

	print "\n";
}


#
# find out if there's anything wrong with the MX's for this $name
#
sub check_MX {
	local($name, $MXlist) = @_;
	local($mx, $str);
	local($weight, $pointer, *Xmx, *Amx, *MXarray);
	local($min_out, $s, $a, $e);
	
	$min_out = -1;
	@MXarray = split($LISTsep, $MXlist);

	# scan the records and get those pointing to external hosts
    	foreach $mx ($[ .. $#MXarray) {
		($weight, $pointer) = split($ELEMsep1, $MXarray[$mx]);

		if (!&is_below_cut($zone, $pointer)) {
	    		$str = sprintf("%s%-20s IN  MX %4d  %s", 
	    				$Lpad[4], $name, $weight, $pointer);

			push(@Xmx, $str);

			# find the value for the most preferable external host
	    		if (($min_out > $weight) || ($min_out < 0)) {
				$min_out = $weight
	    		}
		}
    	}

    	if ($#Xmx && $Level >= 4) {
		($s, $a) = $#Xmx > 1 ? ("s", "") : ("", " an");

	    	print "\n$Lpad[4]MX record$s pointing to$a external host$s";

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

	# get the internal hosts which are less preferred than external ones
    	if ($min_out >= 0 && $Level >= 4) {
		foreach $mx ($[ .. $#MXarray) {
			($weight, $pointer) = split($ELEMsep1, $MXarray[$mx]);

		    	if (&is_below_cut($zone, $pointer) && 
	    		    ($weight >= $min_out)) {	
				$str = sprintf("%s%-20s IN  MX %4d  %s", 
	                                        $Lpad[4], $name, 
						$weight, $pointer);

				push(@Xmx, $str);
			}
		}

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

			print "\n$Lpad[4]MX record$s to local or delegated host$s after MX records to external hosts";

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

	if ($Level >= 2) {
		# get the MX RR's pointing to aliases
	    	foreach $mx ($[ .. $#MXarray) {
			($weight, $pointer) = split($ELEMsep1, $MXarray[$mx]);

			if ($IsAlias{$pointer}) {
				$str = sprintf("%s%-20s IN  MX %4d  %s", 
					$Lpad[2], $name, $weight, $pointer);

				push(@Amx, $str);
			}
    		}

		if ($#Amx) {
			($s, $e, $a) = $#Amx > 1 ? ("s", "es", "") : 
						   ("", "", " an");

			print "\n$Lpad[2]MX record$s pointing to$a alias$e";

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


#
# add ($name, $weight, $pointer) tupple to the list of MX record for this $zone
#
sub new_MX {
	local($zone, $name, $weight, $pointer) = @_;
	local($tuple);

	$tuple = join($ELEMsep2, $name, join($ELEMsep1, $weight, $pointer));

        $MX{$zone} = &add_list($MX{$zone}, $tuple);
}

