#!/usr/bin/perl -T use strict; use warnings; delete $ENV{PATH}; use CGI; $CGI::DISABLE_UPLOADS = 1; #use CGI::Carp 'fatalsToBrowser'; #use Data::Dumper; use Data::FormValidator; use File::Basename; use HTML::Template; use HTML::TokeParser::Simple; use Socket; #requires... # Net::DNS # URI # ping - include 'count' flag if needed! my $ping_exec = '/usr/sbin/ping'; # traceroute # -m maxhop, default 30 # -Q maxtimeout, default 5 # -q nqueries, default 3 # -w waittime, default 5 my $traceroute_exec = '/usr/sbin/traceroute -Q 3 -w 3'; my $whois_exec = '/bin/whois'; my $cgi = CGI->new; my %valid; # valid input from Data::FormValidator my @invalid; # these will be flagged with css, class="alert" my %tmpl; # to be used by HTML::Template->param my %form; # form pairs # used for automated form filling and Data::FormValidator # profile generation my @ip_parts = qw/ ip iphost ipping iptraceroute /; my @hosts = qw/ host nameserver hostmx hostping hosttraceroute whois /; parse_input(); pre_fill_form(); process() unless @invalid; my $html = fill_template(); my $output = fill_form( \$html ); print $cgi->header; print $output; exit; sub parse_input { my %param = $cgi->Vars; my $results = Data::FormValidator->check( \%param, input_profile() ); my $ok = $results->valid; for (keys %$ok) { $valid{$_} = $ok->{$_}; } for ($results->missing, $results->invalid) { push @invalid, $_; } return; } sub pre_fill_form { my @id = keys %{ input_profile()->{constraints} }; for (@id) { $form{$_} = $valid{$_} if exists $valid{$_}; } return; } sub process { if ($valid{ipsubmit}) { ip_to_hex(); } elsif ($valid{hexsubmit}) { hex_to_ip(); } elsif ($valid{iphostsubmit}) { ip_to_hostname(); } elsif ($valid{hostsubmit}) { hostname_to_ip(); } elsif ($valid{nameserversubmit}) { nameservers(); } elsif ($valid{hostmxsubmit}) { mxrecords(); } elsif ($valid{ippingsubmit}) { $valid{run} ? ping(join('.', map {$form{"ipping$_"}} (1..4))) : ping_ip(); } elsif ($valid{hostpingsubmit}) { $valid{run} ? ping($valid{hostping}) : ping_host(); } elsif ($valid{iptraceroutesubmit}) { $valid{run} ? traceroute( join('.', map {$form{"iptraceroute$_"}} (1..4))) : traceroute_ip(); } elsif ($valid{hosttraceroutesubmit}) { $valid{run} ? traceroute($valid{hosttraceroute}) : traceroute_host(); } elsif ($valid{whois}) { $valid{run} ? whois($valid{whois}) : whois_host(); } return; } sub fill_template { my $tmpl = HTML::Template->new(filehandle => *DATA); $tmpl->param( %tmpl, ); return $tmpl->output; } sub fill_form { my ($html) = @_; my $parser = HTML::TokeParser::Simple->new($html); my $new; while (my $t = $parser->get_token) { if ($t->is_start_tag('form')) { $t->set_attr('action', basename($0)); $new .= $t->as_is; } elsif ($t->is_start_tag('input')) { for my $key (keys %form) { $t->set_attr('value', $form{$key}) if $t->get_attr('name') eq $key; } for (@invalid) { $t->set_attr('class', 'alert') if $t->get_attr('name') eq $_; } $new .= $t->as_is; } else { $new .= $t->as_is; } } return $new; } ### Calculation subs sub ip_to_hex { for (1..4) { $form{"hex$_"} = sprintf("%02x", $valid{"ip$_"}); } fill_form_ip_parts( 'ip' ); $tmpl{result} = join('', map {$form{"hex$_"}} (1..4)); return; } sub hex_to_ip { for (1..4) { $form{"ip$_"} = hex($valid{"hex$_"}); $form{"hex$_"} = $valid{"hex$_"}; } fill_form_ip_parts( 'ip' ); $tmpl{result} = join('.', map {$form{"ip$_"}} (1..4)); return; } sub ip_to_hostname { my $ip = join('.', map {$valid{"iphost$_"}} (1..4)); $ip = inet_aton($ip); my ($host) = gethostbyaddr($ip, AF_INET); if ($host) { $form{host} = $host; fill_form_hosts( 'host' ); } else { $host = 'unknown'; } fill_form_ip_parts( 'iphost' ); $tmpl{result} = $host; return; } sub hostname_to_ip { require Net::DNS; my $res = Net::DNS::Resolver->new; my $query = $res->search($valid{host}); if ($query) { my @valid_ip; for my $rr (grep { $_->type eq 'A' } $query->answer) { push @valid_ip, $rr->address; } $tmpl{result} = join "\n", @valid_ip; @form{qw/ iphost1 iphost2 iphost3 iphost4 /} = split /\./, $valid_ip[0]; fill_form_ip_parts( 'iphost' ); fill_form_hosts( 'host' ); } else { push @invalid, 'host'; } return; } sub nameservers { require Net::DNS; my $res = Net::DNS::Resolver->new; my $query = $res->query($valid{nameserver}, "NS"); if ($query) { my @valid_ns; for my $rr (grep { $_->type eq 'NS' } $query->answer) { push @valid_ns, $rr->nsdname; } $tmpl{result} = join "\n", @valid_ns; } else { push @invalid, 'nameserver'; } fill_form_hosts( 'nameserver' ); return; } sub mxrecords { require Net::DNS; my $res = Net::DNS::Resolver->new; my @mx = mx($res, $valid{hostmx}); my @valid; if (@mx) { for (@mx) { push @valid, [$_->preference, $_->exchange]; } $tmpl{result} = join "\n", map {$_->[1]} sort {$a->[0]<=>$b->[0]} @valid; } else { push @invalid, 'hostmx'; } fill_form_hosts( 'hostmx' ); return; } sub ping_ip { require URI; my $url = URI->new (basename($0)); $url->query_form( ipping1 => $valid{ipping1}, ipping2 => $valid{ipping2}, ipping3 => $valid{ipping3}, ipping4 => $valid{ipping4}, ippingsubmit => 1, run => 1, ); $tmpl{dynamic} = $url; fill_form_ip_parts( 'ipping' ); } sub ping_host { require URI; my $url = URI->new (basename($0)); $url->query_form( hostping => $valid{hostping}, hostpingsubmit => 1, run => 1, ); $tmpl{dynamic} = $url; fill_form_hosts( 'hostping' ); } sub ping { my ($target) = @_; $| = 1; print $cgi->header; print "
"; print "$ping_exec $target\n\n"; print `$ping_exec $target`; print ""; exit; } sub traceroute_ip { require URI; my $url = URI->new (basename($0)); $url->query_form( iptraceroute1 => $valid{iptraceroute1}, iptraceroute2 => $valid{iptraceroute2}, iptraceroute3 => $valid{iptraceroute3}, iptraceroute4 => $valid{iptraceroute4}, iptraceroutesubmit => 1, run => 1, ); $tmpl{dynamic} = $url; fill_form_ip_parts( 'iptraceroute' ); } sub traceroute_host { require URI; my $url = URI->new (basename($0)); $url->query_form( hosttraceroute => $valid{hosttraceroute}, hosttraceroutesubmit => 1, run => 1, ); $tmpl{dynamic} = $url; fill_form_hosts( 'hosttraceroute' ); } sub traceroute { my ($target) = @_; $| = 1; print $cgi->header; print "
"; print "$traceroute_exec $target\n\n"; print `$traceroute_exec $target`; print ""; exit; } sub whois_host { require URI; my $url = URI->new (basename($0)); $url->query_form( whois => $valid{whois}, whoissubmit => 1, run => 1, ); $tmpl{dynamic} = $url; fill_form_hosts( 'whois' ); } sub whois { my ($target) = @_; $| = 1; print $cgi->header; print "
"; print "$whois_exec $target\n"; print `$whois_exec $target`; print ""; exit; } ### sub fill_form_ip_parts { my ($name) = @_; for my $part (@ip_parts) { for my $num (1..4) { $form{"$part$num"} = $form{"$name$num"}; } } } sub fill_form_hosts { my ($name) = @_; for my $host (@hosts) { $form{$host} = $form{"$name"}; } } ### DATA::FormValidator subs sub input_profile { my %ip; for my $part (@ip_parts) { for my $num (1..4) { $ip{"$part$num"} = \&valid_ip_part; } } my %host; for my $host (@hosts) { $host{$host} = \&valid_hostname; } return { optional => [qw/ ip1 ip2 ip3 ip4 ipsubmit hex1 hex2 hex3 hex4 hexsubmit iphost1 iphost2 iphost3 iphost4 iphostsubmit host hostsubmit nameserver nameserversubmit hostmx hostmxsubmit ipping1 ipping2 ipping3 ipping4 ippingsubmit hostping hostpingsubmit iptraceroute1 iptraceroute2 iptraceroute3 iptraceroute4 iptraceroutesubmit hosttraceroute hosttraceroutesubmit whois whoissubmit run /], dependencies => { ipsubmit => [qw/ ip1 ip2 ip3 ip4 /], hexsubmit => [qw/ hex1 hex2 hex3 hex4 /], iphostsubmit => [qw/ iphost1 iphost2 iphost3 iphost4 /], hostsubmit => 'host', nameserversubmit => 'nameserver', hostmxsubmit => 'hostmx', ippingsubmit => [qw/ ipping1 ipping2 ipping3 ipping4 /], hostpingsubmit => 'hostping', iptraceroutesubmit => [qw/ iptraceroute1 iptraceroute2 iptraceroute3 iptraceroute4 /], hosttraceroutesubmit => 'hosttraceroute', whoissubmit => 'whois', }, constraints => { %ip, %host, hex1 => \&valid_hex_part, hex2 => \&valid_hex_part, hex3 => \&valid_hex_part, hex4 => \&valid_hex_part, }, untaint_all_constraints => 1, }; } sub valid_ip_part { my $test = shift; if ($test =~ /^([0-9]{1,3})$/ && $test <= 255) { return $1; } } sub valid_hex_part { my $test = shift; if ($test eq '0') { return '0 but true'; } if ($test =~ /^([0-9a-f]{1,2})$/) { return $1; } } sub valid_hostname { my $test = shift; my $end = qr'[a-zA-Z0-9]'; my $any = qr'[-a-zA-Z0-9]'; if ($test =~ /^( $end # word must be 1 char long (?: $any* # any number of chars $end # must end with valid )? # longer than 1 char is optional (?: \. # any further words must start with dot . $end (?: $any* $end )? )* # further words are optional ) $/x) { return $1; } } __DATA__