#!/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;