#!/usr/bin/perl -w
#Script to wake-up machines with EUI64 IPv6 addresses

my $toaddr="255.255.255.255";

die "usage: $0 hostname\n" if (!defined($ARGV[0]));


use Net::DNS;
use Socket;

my $addr;

if ($ARGV[0] =~ /:/)
{
	$addr = $ARGV[0];
} else {
	my $resolver=Net::DNS::Resolver->new;
	my $query=$resolver->query($ARGV[0],"AAAA") or die "Can't lookup host\n";
	foreach $record ($query->answer)
	{
		next unless $record->type eq "AAAA";
		$addr = $record->address;
		last;
	}
}

#IPv6 address expansion
if ( $addr=~ /::/ )
{
	my $pos = -1;
	for ( my $ccount=7; $ccount > 0; $ccount--)
	{
		next if (($pos=index($addr,":",$pos+1)) != -1);
		$addr=~s /::/:0000::/;
		$pos=length($addr)+1;
	}
	$addr=~s /::/:0000:/g;
}
$addr=~s /^(.*)$/:$1:/;
$addr=~s /:(.):/:000$1:/g; $addr=~s /:(.):/:000$1:/g;
$addr=~s /:(..):/:00$1:/g; $addr=~s /:(..):/:00$1:/g;
$addr=~s /:(...):/:0$1:/g; $addr=~s /:(...):/:0$1:/g;
$addr=~s /:(.*):/$1/;

die "Invalid address $addr\n"
	if ( ! ($addr=~ /^[[:xdigit:]]{4}:[[:xdigit:]]{4}:[[:xdigit:]]{4}:[[:xdigit:]]{4}:([[:xdigit:]]{4}:[[:xdigit:]]{4}:[[:xdigit:]]{4}:[[:xdigit:]]{4})$/) );

#Enet
my $enet = $1;
$enet=~s /://g;
die "${addr} is not EUI64!\n" if (!($enet=~ /^(......)fffe(......)$/i));
$enet=$1.$2;

$enet=~ /^(..)(.*)$/;
#2nd bit needs to be 0
$enet=sprintf("%2.2x",(hex($1) & (0xff-2))).$2;

my $mac = pack "H12", $enet;

$enet=~s /([^:]{2})/$1:/g;
$enet=~s /:$//;
print "Waking up ${enet}\n";

#WOL-Init
my $wolpkg = pack "H12", "ffffffffffff";

for (my $i=0; $i < 16; $i++)
{
        $wolpkg .= $mac;
}

socket($mysock, PF_INET, SOCK_DGRAM, getprotobyname('udp')) or die "Can't create socket\n";;
setsockopt($mysock, SOL_SOCKET, SO_REUSEADDR, 1);
setsockopt($mysock, SOL_SOCKET, SO_BROADCAST, 1);
bind($mysock, sockaddr_in(0, INADDR_ANY)) or die "can't bind\n";
$saddr = sockaddr_in(4211, inet_aton($toaddr)) or die "sockaddr failed\n";

send($mysock, $wolpkg, 0, $saddr);
send($mysock, $wolpkg, 0, $saddr);
send($mysock, $wolpkg, 0, $saddr);
send($mysock, $wolpkg, 0, $saddr);
send($mysock, $wolpkg, 0, $saddr);
