#!/usr/bin/perl
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Copyright (C) 2008 Vino Fernando Crescini
# Google Treasure Hunt 2008 Puzzle 3
# must be xxx.xxx.xxx.xxx
sub validate
{
if ($_[0] =~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/)
{
my ($tmp1, $tmp2, $tmp3, $tmp4) = split(/\./, $_[0], 4);
return ($tmp1 <= 255 && $tmp2 <= 255 && $tmp3 <= 255 && $tmp4 <= 255);
}
return 0;
}
# convert to int
sub aton
{
my $n = 0;
my ($tmp1, $tmp2, $tmp3, $tmp4) = split(/\./, $_[0], 4);
$n += $tmp1 << 24;
$n += $tmp2 << 16;
$n += $tmp3 << 8;
$n += $tmp4;
return $n;
}
# return true if the first arg3 bits of arg1 and arg2 matches
sub match
{
my $addr1 = aton($_[0]);
my $addr2 = aton($_[1]);
my $mask = 0xffffffff << $_[2];
return (($addr1 & $mask) == ($addr2 & $mask));
}
# array, element
sub isin
{
my ($larr, $item) = @_;
foreach my $citem (@$larr)
{
if ($item == $citem)
{
return 1;
}
}
return 0;
}
# curr, dest, ref to table, ref to result, ref to callstack
sub pathfind
{
my ($curr, $dest, $lrtab, $lpath, $lstack) = @_;
if ($curr == $dest)
{
push(@$lpath, $lrtab->{$curr}[1]);
return 1;
}
for(my $i = 0, my $hop = 0; $i < scalar @{$lrtab->{$curr}[2]} && !$hop; $i++)
{
my $cond = $lrtab->{$curr}[2][$i][0];
my $gw = $lrtab->{$curr}[2][$i][1];
my $len = $lrtab->{$curr}[2][$i][2];
# check if this is the default route, or if addr/mask matches
if (length($cond) == 0 || match($dest, $cond, $len))
{
# check if we've been through this node before
if (!isin($lstack, $gw))
{
my $res;
push(@$lstack, $curr);
$res = pathfind($gw, $dest, $lrtab, $lpath, $lstack);
pop(@$lstack);
if ($res)
{
$hop = 1;
push(@$lpath, $lrtab->{$curr}[1]);
return 1;
}
}
}
}
return 0;
}
sub tabread
{
my ($lrtab) = @_;
while(my $line = )
{
my @aline;
$line =~ s/ +/ /g;
$line =~ s/ +/ /g;
$line =~ s/ => /=>/g;
chomp($line);
if (length($line) > 1)
{
@aline = split(/ /, $line);
if (!validate($aline[1]))
{
return 0;
}
$lrtab->{$aline[1]}[0] = $aline[1];
$lrtab->{$aline[1]}[1] = $aline[0];
for (my $i = 2; $i < @aline; $i++)
{
my ($tmp1, $tmp2) = split(/=>/, $aline[$i]);
if (length($tmp2) != 0)
{
my ($tmp3, $tmp4) = split(/\//, $tmp1);
if (!validate($tmp3) || !validate($tmp2) || $tmp4 < 0 || $tmp4 > 32)
{
return 0;
}
$lrtab->{$aline[1]}[2][$i - 2] = [ $tmp3, $tmp2, $tmp4 ];
}
else
{
if (!validate($tmp1))
{
return 0;
}
$lrtab->{$aline[1]}[2][$i - 2] = [ "", $tmp1, 0 ];
}
}
}
}
return 1;
}
my @path;
my %rtab;
my @stack;
if (@ARGV != 2 || !validate($ARGV[0]) || !validate($ARGV[1]))
{
printf(STDERR "usage: $0 \n");
exit 1;
}
if (!tabread(\%rtab))
{
printf(STDERR "parse error\n");
exit 2;
}
if ($rtab{$ARGV[0]} == "" || $rtab{$ARGV[1]} == "")
{
printf(STDERR "start or destination ipaddr is not in route table\n");
exit 3;
}
if (!pathfind($ARGV[0], $ARGV[1], \%rtab, \@path, \@stack))
{
printf(STDERR "no route to %s\n", $ARGV[1]);
exit 4;
}
while(my $node = pop(@path))
{
printf("%s", $node);
}
printf("\n");
exit 0;