#!/usr/bin/perl use Socket; use Getopt::Std; ######################################################################### # MAC address lookup translation tool MAC -> name, # # collect MAC addresses on the LAN, # # reverse translations range of IP addresse # # # # Author: Pavel Mracek mrak(at)mrak(dot)cz # # Version: 0.5 # ######################################################################### # # example 1.: lookup mac to name # root@nika root]# arp -a |hwa # 10.1.255.10 at 00:40:a1:3c:15:1f(holy_i626_1) [ether] on eth2 # 10.1.2.2 at 00:07:a6:ac:ae:12(blek) [ether] on eth0 # 213.191.108.97 at 00:50:ac:a3:54:a3(unknown) [ether] on eth1 # # example 2.: lookup range of ip adresses # [mrak@cat ~]$ hwa -r 213.192.29.1-6 # 213.192.29.1 - gw2.praslavice.net # 213.192.29.2 - mail.praslavice.net # 213.192.29.4 - ip4.tucnak.org # 213.192.29.5 - lancia.tucnak.org # 213.192.29.6 - ip6.tucnak.org # # example 3.: grab local lan MACs:IPs # [mrak@cat ~]$ hwa -g 172.21.21.1-30 # 0023cdb1b18f:172.21.21.1 # 0090e8193bc3:172.21.21.3 # 000b6bc95fc1:172.21.21.24 # 00094563bc5c:172.21.21.30 # # OR in DNS zone format # [mrak@cat ~]$ bin/hwa -z -g 172.21.21.1-10 # 0023cdb1b18f IN TXT 172.21.21.1 # 0090e8193bc3 IN TXT 172.21.21.3 # # # # example 4.: lookup single MAC to NAME with vendor identification # [mrak@cat ~]$ hwa -V 00:02:72:6b:ea:91 # roman-ap-wa220 - CC&C_Technologies,_Inc. # # # # example 5.: DNS zone file with MAC to NAME records # # $ORIGIN hw.mrak.cz. # $TTL 259200 ; 3 days # @ IN SOA nika.mrak.cz. mrak.nika.mrak.cz. ( # 20040419; serial # 3600 ; refresh (1 hour) # 9000 ; retry (2 hours 30 minutes) # 1209600 ; expire (2 weeks) # 432 ; minimum (7 minutes 12 seconds) # ) # $TTL 432 ; 7 minutes 12 seconds # IN NS nika.mrak.cz. # IN NS holy.mrak.cz. # # $TTL 259200 ; 3 days # ;;;;;;;;;;;;;; mac2name records ;;;;;;;;;;;;;;;; # # 0008c7bbbf51 IN TXT blek # 00e908a9fa1c IN TXT slayer # 0008089daa60 IN TXT bohus # # ######################################################################### # This file may be distributed under the terms of the GNU General # # Public License. # ######################################################################### ## text konfiguration file (kompatibile with iptraf) $conf_file="~/.ethernet.desc"; ## command for searching in DNS records $dns_cmd="host -t txt"; ## default "hw" domain and optional DNS server #$dns_parm="hw.mrak.cz 10.1.1.1"; $dns_parm="hw.mrak.cz"; # waiting for ping in grab_arp function $ping_wait="1"; $arp="/sbin/arp"; ################################################### ########## END of user pref. variables ############ ################################################### my (%addr); my (%ven); $ver="0.5"; ####### ## conf sub c_load{ my ($file)=@_; my @conf; my $l; open(CF,"<$file") || return(0); @conf=; close CF; foreach $l (@conf){ if($l =~ /^(.+):(.*)$/){ $addr{$1}=$2; } } } sub c_print(){ foreach $k (keys(%addr)){ print &expa($k)." - $addr{$k}\n"; } } ############### ## text func # substitute MAC address sub subs(){ open(IN,"<@_") || die "cannot open file @_ \n$!"; my ($linen,$line,$pre,$hw); while($line=){ while(($pre,$hw)=$line =~ /(.*?)(..?$deli..?$deli..?$deli..?$deli..?$deli..?)/){ $line = $'; # store postmatch $hw = zeroexpand(split /$deli/,$hw); $hw=lc("$hw"); $name=resolve($hw); $hw=&expa($hw)."($name)"; $linen .= $pre.$hw; # store prematch + name } # line cykle print $linen.$line; # print linen and end of $line where doesn't match MAC addres $linen=''; } # file while close IN; } sub expa{ my ($ex)=@_; $ex =~ /(..)(..)(..)(..)(..)(..)/; return("$1:$2:$3:$4:$5:$6"); } sub zeroexpand{ my ($k,$res); foreach $k (@_){ $k = length($k) > 1 ? $k : "0".$k; $res.="$k"; } return $res; } ################### ## arp-dns graber/resolver # resolve wrapper parm:(ff11ff11ff11) sub resolve{ my ($hw)=@_; my ($name,$vendor,$hwpr); # prelozeni adresy na jmeno if($addr{$hw}){ $name=$addr{$hw}; }else{ # bere zaznam z DNS jinak vraci unknown $name=&resolve_dns($hw); # ulozi do mistni cache $addr{$hw}=$name; } # prelozeni kodu vyrobce if($opt_V){ $hw =~ /^([\da-f]{6})/; $hwpr=$1; if($ven{$hwpr}){ $vendor=$ven{$hwpr}; }else{ $vendor=&resolve_ven($hwpr); # ulozi do mistni cache $ven{$hwpr}=$vendor; } $name = $name." - ".$vendor; } return($name); } # resolve mac from DNS TXT RR sub resolve_dns{ my ($hw)=@_; my ($res,@res); my ($cmd)=$dns_cmd.' '.$hw.'.'.$dns_parm; open(DNS,"$cmd|") || die("DNS txt resolve failed :(\n $!"); @res=; close DNS; $res=pop(@res); if($res=~/^.*?text\s\"(.*)\"/){ return $1; }else{ return('unknown'); } } # resolve vendor code from DNS TXT RR sub resolve_ven(){ my ($hwpr)=@_; my ($res,@res); my ($cmd)=$dns_cmd.' '.$hwpr.'.'.$dns_parm; open(DNS,"$cmd|") || die("DNS txt resolve failed :(\n $!"); @res=; close DNS; $res=pop(@res); if($res=~/^.*?text\s\"(.*)\"/){ return $1; }else{ return('unknown'); } # pharse result } ## # grab arp adres from range sub get_arp{ my ($ipaddr) = @_; my (@hosts,$h,$line); @hosts=&make_array($ipaddr); foreach $h (@hosts){ print "pinging $h\n" if($opt_v); system("ping -q -c 1 -w $ping_wait $h &>/dev/null"); open(ARP,"$arp $h|") || die "exec ARP $h failed \n"; ;$line=; close ARP; if(($name,$hw)=$line=~/^(.+?)\s+.+\s+(..:..:..:..:..:..)/){ $hw =~ s/://g; $hw=lc($hw); if($opt_z){ print $hw."\tIN\tTXT\t".$name."\n"; }else{ print $hw.':'.$name."\n"; } } } } sub resolv_r(){ my ($ipaddr) = @_; my (@hosts,$h,$name); @hosts=&make_array($ipaddr); foreach $h (@hosts){ print "resolving $h\n" if($opt_v); if(($name) = gethostbyaddr(inet_aton($h), AF_INET)){ print "$h - $name\n"; } } return 1; } sub make_array{ my ($line) = $_[0]; my (@hosts,@tmp,@bip,@eip,$num); if ($line !~ /#/){ if ($line =~ /-/){ @tmp = split /-/, $line; @bip = split /\./,$tmp[0]; @eip = split /\./,$tmp[1]; } else { @bip = split /\./, $line; @eip = split /\./, $line; } $a1 = $bip[0]; $b1 = $bip[1]; $c1 = $bip[2]; $d1 = $bip[3]; $num = @eip; if ($num == 1){ $a2 = $bip[0]; $b2 = $bip[1]; $c2 = $bip[2]; $d2 = $eip[0]; } elsif ($num == 2){ $a2 = $bip[0]; $b2 = $bip[1]; $c2 = $eip[0]; $d2 = $eip[1]; } elsif ($num == 3){ $a2 = $bip[0]; $b2 = $eip[0]; $c2 = $eip[1]; $d2 = $eip[2]; } elsif ($num == 4){ $a2 = $eip[0]; $b2 = $eip[1]; $c2 = $eip[2]; $d2 = $eip[3]; } check_end(); $aend = $a2; while ($a1 <= $aend){ while ($b1 <= $bend){ while ($c1 <= $cend){ while ($d1 <= $dend){ push (@hosts, "$a1.$b1.$c1.$d1"); $d1+=1; check_end(); } $c1+=1; $d1=0; } $b1+=1; $c1=0; } $a1+=1; $b1=0; } } return @hosts; } sub check_end { if(($a1==$a2) && ($b1==$b2) && ($c1==$c2)) { $dend=$d2; } else { $dend=255; } if(($a1==$a2) && ($b1==$b2)){ $cend=$c2; } else { $cend=255; } if($a1==$a2){ $bend=$b2; } else { $bend=255; } } sub test_ip{ my ($ip)=@_; if($ip !~ /\d+[\.\-\d]*/){ die("Usage: $0 192.168.0.10-38\n"); } return 1; } ############################### ########## MAIN ############### &getopts("hvr:d:g:cf:V:z"); $deli = ($opt_d ? $opt_d : ':'); if($opt_r && &test_ip($opt_r)){ &resolv_r($opt_r); }elsif($opt_g && &test_ip($opt_g)){ &get_arp($opt_g); }elsif($opt_c){ &c_load($conf_file); &c_print(); }elsif($opt_f){ &c_load($conf_file); &subs("$opt_f"); }elsif($ARGV[0]){ &c_load($conf_file); $hw=lc("$ARGV[0]"); ### $hw =~ s/://g; ## replaced by zeroexpand func. $hw = zeroexpand(split /:/,$hw); print resolve($hw)."\n"; }elsif($opt_h){ print "hwa - hardware address resolver version $ver arp -a |hwa - Stdin->stdout substitution hwa 00..d4 || 00:..:F9 - Resolve MAC file then dns hwa -V - Resolve vendor code hwa -d - - set delimiter up to \"-\" (default \":\" ) hwa -f file - Read file, resolv it and print on stdout hwa -r 192.168.0.0-255 - Resolve IP range from PTR hwa -z - DNS zone like output, (-g opt. is required) hwa -g 192.168.0.0-255 - Grab MAC:name from IP range hwa -c - Show pharsed config $conf_file hwa -h - Show this text hwa -v - Verbose output " }else{ &c_load($conf_file); &subs("/dev/stdin"); } 1;