Listing 1

use strict;
use Math::VecStat qw(sum);
use Crypt::CBC;
use IO::Socket::INET;
use Getopt::Long;
use vars qw($opt_client $opt_remote $opt_port $opt_time);

GetOptions("client=s","remote=s","port=i","time=i");

$opt_client || die "specify IP address (-client) for which the port will be opened."; 
$opt_remote || die "specify IP address (-remote) of server listening to knocks";      
$opt_port   || die "specify remote port (-port)"; 
$opt_time = (defined $opt_time)?$opt_time:5;  

use constant PORTMIN => 745;
use constant KEY     => "knock";    # keyphrase for the encryption
use constant CIPHER  => "Blowfish"; # encryption algorithm

my $cipher = Crypt::CBC->new({key        => KEY,
			      iv         => "01234567",
			      prepend_iv => 0,
			      cipher     => CIPHER});

# data to encrypt will be our IP address + port + time + checksum
my @data = (split(/\./,$opt_client),$opt_port,$opt_time);
# compute crc and push it onto the data array
push(@data,sum(@data) % 255);

print "encrypting data ",join(" ",@data),"\n";

# encrypt the packed data
my $ciphertext = $cipher->encrypt(pack("C*",@data));
# unpack the ciphered data into unsigned char values 0-255
my @cipherpack = unpack("C*",$ciphertext);
# create the knock sequence by mapping to ports PORTMIN-PORTMIN+255

print "ciphered data ",join(" ",@cipherpack),"\n";

my @knocks = map {PORTMIN+$_} @cipherpack;

print "knock sequence [",int(@cipherpack)," knocks]: ",join(" ",@knocks),"\n";

# create TCP connection to the remote host to each of the ports in the knock sequence
for my $port (@knocks) {
  my $sock = IO::Socket::INET->new(PeerAddr => $opt_remote,
				   PeerPort => $port,
				   Timeout  => 0.5,
				   Proto    => 'tcp');
  print "  knocked on $opt_remote:$port\n";
}
exit 0;
### end of Listing 1

