#!/usr/bin/perl
# $Header: /u/cvsroot/tinydns-fixup/tinydns-fixup.pl,v 1.5 2005/05/10 17:02:38 mayoff Exp $

use strict;

my %ipMap;

open(IN, $ARGV[0]) || die "$ARGV[0]: $!\n";
seek(IN, 0, 0) || die "could not seek: $!; input must be a regular file\n";

while (<IN>) {
    chomp;

    my $type = substr($_, 0, 1);

    substr($_, 0, 1) = "";
    my %fields;

    next if (!getFields($type, $_, \%fields));

    # Ignore wildcard FQDNs.
    next if (substr($fields{'name'}, 0, 1) eq '*');

    # Ignore records that don't have real IP addresses.
    next if ($fields{'ip'} !~ /^\d+\.\d+\.\d+\.\d+$/);

    if (exists $ipMap{"$fields{'name'}:$fields{'lo'}"}) {
	print STDERR "warning: redefinition of $fields{'name'}:$fields{'lo'} ignored\n";
    } else {
	$ipMap{"$fields{'name'}:$fields{'lo'}"} = $fields{'ip'};
    }
}

seek(IN, 0, 0) || die "could not seek: $!; input must be a regular file\n";

while (<IN>) {
    chomp;

    my $type = substr($_, 0, 1);
    my $rest = substr($_, 1);
    my %fields;

    if (
	getFields($type, $rest, \%fields)
	&& $fields{'ip'} !~ /^(\d+\.\d+\.\d+\.\d+)$/
    ) {
	my $newIP = $fields{'ip'};

	if (exists $ipMap{"$fields{'ip'}:$fields{'lo'}"}) {
	    $newIP = $ipMap{"$fields{'ip'}:$fields{'lo'}"};
	}

	elsif (exists $ipMap{"$fields{'ip'}:"}) {
	    $newIP = $ipMap{"$fields{'ip'}:"};
	}

	my ($typeFqdn, $colon1, $ip, $colon2, $rest) = split(/(:)/, $_, 3);
	$_ = qq($typeFqdn$colon1$newIP$colon2$rest);
    }

    print $_, "\n";
}

exit(0);

sub getFields {
    my ($type, $rest, $fieldsRef) = @_;

    my @fields = split(/:/, $rest);

    if ($type eq '.' || $type eq '&') {
	@{$fieldsRef}{'fqdn','ip','x','ttl','timestamp','lo'} = @fields;

	if (index($fieldsRef->{'x'}, '.') < 0) {
	    $fieldsRef->{'name'} = "$fieldsRef->{'x'}.ns.$fieldsRef->{'fqdn'}";
	} else {
	    $fieldsRef->{'name'} = $fieldsRef->{'x'};
	}
    }

    elsif ($type eq '=' || $type eq '+' || $type eq '-') {
	@{$fieldsRef}{'name','ip','ttl','timestamp','lo'} = @fields;
    }

    elsif ($type eq '@') {
	@{$fieldsRef}{'fqdn','ip','x','dist','ttl','timestamp','lo'} = @fields;

	if (index($fieldsRef->{'x'}, '.') < 0) {
	    $fieldsRef->{'name'} = "$fieldsRef->{'x'}.mx.$fieldsRef->{'fqdn'}";
	} else {
	    $fieldsRef->{'name'} = $fieldsRef->{'x'};
	}
    }

    else {
	return 0;
    }

    return 1;
}

__END__

=head1 NAME

tinydns-fixup - Substitute addresses for names in tinydns data

=head1 SYNOPSIS

B<tinydns-fixup> data.f > data

=head1 DESCRIPTION

I<tinydns-fixup> reads I<data.f>. For each ".", "&", "=", "+", "-", or
"@" record that associates a name with an IP address, I<tinydns-fixup>
stores the IP address in a hash table with the name as the key. If the
record contains a location code, then the location code is also part of
the key.

I<tinydns-fixup> then reads I<data.f> a second time. For each ".",
"&", "=", "+", "-", or "@" record that contains a name where it should
contain an IP address, I<tinydns-fixup> looks up the name in its hash
table. If the record contains a location code, then the location code
is also used in the lookup. If the name (and location) is in the
table, I<tinydns-fixup> substitutes the IP address for the name in the
record. I<tinydns-fixup> then prints the record (regardless of whether it
performed a substitution).  If I<tinydns-fixup> could not find the name
and location in the table, then it tries again with just the name.

The input data file must be seekable; a named pipe will not work.

=head1 AUTHOR

Rob Mayoff <mayoff@dqd.com>

=head1 VERSION

$Id: tinydns-fixup.pl,v 1.5 2005/05/10 17:02:38 mayoff Exp $

=cut

