Rev 5 | Blame | Compare with Previous | Last modification | View Log | RSS feed
#!/usr/bin/perl
#
# VGWhoIs (ViaThinkSoft Global WhoIs, a fork of generic Whois / gwhois)
# Subprogram: Teredo Decoder
#
# (c) 2012 by Daniel Marschall, ViaThinkSoft <info@daniel-marschall.de>
#
# License: https://www.gnu.org/licenses/gpl-2.0.html (GPL version 2)
#
# todo: möglichkeit geben, den prefix-check per CLI zu deaktivieren
use warnings;
use strict;
my $prefix_check = 1;
# install with "cpan Net::IP"
use Net::IP qw(ip_expand_address);
use Net::DNS;
sub parse_teredo_ipv6 {
# See: http://en.wikipedia.org/wiki/Teredo_tunneling#IPv6_addressing
my ($ipv6_addr) = @_;
$ipv6_addr = ip_expand_address($ipv6_addr, 6);
my ($prefix, $teredo_server, $flags, $udp_port, $client_ipv4)
= $ipv6_addr =~ /([0-9a-f]{4}:[0-9a-f]{4}):([0-9a-f]{4}:[0-9a-f]{4}):([0-9a-f]{4}):([0-9a-f]{4}):([0-9a-f]{4}:[0-9a-f]{4})/i;
return ($ipv6_addr, "") if !defined $prefix;
return ($ipv6_addr, $prefix) if $prefix_check && ($prefix ne "2001:0000") && ($prefix ne "3ffe:831f");
# $teredo_server=~ s/://;
$teredo_server = hex2ip4($teredo_server);
# MSB first, is "CRAAAAUG AAAAAAAA",
# where "C" remains the "Cone NAT" flag
# The "R" bit is reserved for future use
# The "U" bit is for the Universal/Local flag (set to 0).
# The "G" bit is Individual/Group flag (set to 0).
# The A bits are set to a 12-bit randomly generated number
# chosen by the Teredo client to introduce additional protection
# for the Teredo node against IPv6-based scanning attacks.
$flags = hex($flags);
my $flag_cone_nat = ($flags & 0x8000) == 0x8000 ? 1 : 0;
my $flag_reserved = ($flags & 0x4000) == 0x4000 ? 1 : 0;
my $flag_universal_local = ($flags & 0x0200) == 0x0200 ? 1 : 0;
my $flag_individual_group = ($flags & 0x0100) == 0x0100 ? 1 : 0;
my $flag_random_12bit = (($flags & 0x3C00) >> 2) | ($flags & 0x00FF);
$udp_port = hex($udp_port) ^ 0xFFFF; # decode
$client_ipv4 =~ s/://;
$client_ipv4 = sprintf('%x', hex($client_ipv4) ^ 0xFFFFFFFF); # decode
$client_ipv4 = hex2ip4($client_ipv4);
return ($ipv6_addr, $prefix, $teredo_server, $flags, $flag_cone_nat, $flag_reserved,
$flag_universal_local, $flag_individual_group,
$flag_random_12bit, $udp_port, $client_ipv4);
}
sub print_teredo_info {
my ($ipv6_addr, $prefix, $teredo_server, $flags, $flag_cone_nat, $flag_reserved,
$flag_universal_local, $flag_individual_group,
$flag_random_12bit, $udp_port, $client_ipv4) = @_;
if ($prefix_check && ($prefix ne "2001:0000") && ($prefix ne "3ffe:831f")) {
print "Invalid Teredo address $ipv6_addr\n";
return;
}
my @revdns;
print "Teredo IPv6 address: ", $ipv6_addr, "\n";
# Is "2001:0000" or "3ffe:831f" (deprecated)
print "Teredo prefix: ", $prefix, "\n";
print "Teredo server: ", $teredo_server, "\n";
@revdns = revdns_ipv4($teredo_server);
foreach my $addr (@revdns) {
print " $addr\n";
}
print "Flags: ", $flags, "\n";
print " Cone NAT flag: ", $flag_cone_nat, "\n";
print " Reserved flag: ", $flag_reserved, "\n";
print " Universal/Local flag: ", $flag_universal_local, "\n";
print " Individual/Group flag: ", $flag_individual_group, "\n";
print " Random 12 bits: ", $flag_random_12bit, "\n"; # 0..4095
print "Client UDP port: ", $udp_port, "\n";
print "Client public IPv4: ", $client_ipv4, "\n";
@revdns = revdns_ipv4($client_ipv4);
foreach my $addr (@revdns) {
print " $addr\n";
}
}
sub hex2ip4 {
my ($hex) = @_;
$hex =~ s/[^0-9a-f]//i;
my ($ip4a, $ip4b, $ip4c, $ip4d) = $hex =~ /([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})/i;
$ip4a = hex($ip4a);
return if !defined $ip4a;
$ip4b = hex($ip4b);
return if !defined $ip4b;
$ip4c = hex($ip4c);
return if !defined $ip4c;
$ip4d = hex($ip4d);
return if !defined $ip4d;
return "$ip4a.$ip4b.$ip4c.$ip4d";
}
sub revdns_ipv4 {
# Source: http://stackoverflow.com/questions/85487/reverse-dns-lookup-in-perl
my ($ipv4) = @_;
my $res = Net::DNS::Resolver->new;
# create the reverse lookup DNS name (note that the octets in the IP address need to be reversed).
my $target_IP = join('.', reverse split(/\./, $ipv4)).".in-addr.arpa";
my @out = qw();
my $query = $res->query("$target_IP", "PTR");
if ($query) {
foreach my $rr ($query->answer) {
next unless $rr->type eq "PTR";
push (@out, $rr->rdatastr);
}
} else {
# warn "Reverse DNS query failed: ", $res->errorstring, "\n";
push (@out, "Reverse DNS query failed: ".$res->errorstring."\n");
}
return @out;
}
if ($ARGV[0]) {
$_ = join(' ',@ARGV);
} else {
$_ = <>;
chomp;
}
my @info = parse_teredo_ipv6($_);
print_teredo_info(@info);