#!/usr/bin/perl
#Author: Aaron Burgemeister
#Version: 0.2.20070804160600
#This script is made to test networking hardware and software in a simple way by verifying packets
#travel reliably across the network.  This is mostly made to troubleshoot bad switches, NICs, and
#anything else that could corrupt a packet between applications.
#
#Running ./nettest.pl by itself will give you a couple options to help you get going.
#To Listen: ./nettest.pl listen ipAddressToListenOn portToListenOn
#To Send: ./nettest.pl send ipAddressBeingListenedOn portBeingListenedOn randomNumbersToGenerate
#
#The default IP address used is 127.0.0.1 so you definitely should specify one (though it's not
#required).  The default port is 12344 which can easily be changed to any TCP port the user has
#rights to bind (>1023 on most platforms unless run as 'root').  Be sure when specifying a port
#that the firewall on the receiving side (and possibly even the sending side, though not often)
#is allowing packets to be received on that port.
#
#This script uses a couple libraries that should already be on your system including IO::Socket
#and Digest::MD5 which should be available should you not have them.  You'll know they are not there
#if you get an error like "Can't locate Digest/MD5.pm".  An earlier version used to work with a
#md5sum system call but to make this a bit easier to use that has been replaced with the Perl
#equivalents.
#
#Using a randomNumbersToGenerate number of 1000000 (one million) will generate approximately 15MB
#data.  For this reason be sure you know what connection you are testing.  This would be a multi-hour
#test of a dial-up network but would be a relatively small test of a fast LAN.  For most cases is it
#probably a good idea to do 10x that for a LAN at least.  Note that the Perl script is caching this
#string which will be processed in memory before sending it to the command-line.  For this reason
#doing multiple hundred-megabyte tests is probably better than doing a single ten-gigabyte test unless
#you have ten gigabytes of RAM to spare.  If you get outside your RAM and get into swap space (pagefile)
#the performance of this test will be abysmal.  Some quick testing shows that for every increment
#of the 'numberToSend' parameter sixteen to eighteen bytes are sent.  This may help calculate
#a good number for your specific connection.

use strict;
use IO::Socket;
use Digest::MD5;
my($operation) = shift(@ARGV);
my($randomData) = '';

if($operation eq 'listen')
{
  my($listenIP) = shift(@ARGV) || '127.0.0.1';
  my($listenPort) = shift(@ARGV) || 12344;
  my($listenProtocol) = shift(@ARGV) || 'tcp';
  my $sock = new IO::Socket::INET (LocalHost => $listenIP, LocalPort => $listenPort, Proto => $listenProtocol, Listen => 1, Reuse => 1);
  die "Could not create socket: $!\n" unless $sock;

  print STDERR 'Listening on ' . $listenIP . ':' . $listenPort . "\n";

  my $new_sock = $sock->accept();
  while(<$new_sock>)
  {
    $randomData .= $_;
  }#End while
  close($new_sock);

  #exec('echo ' . $randomData . ' | md5sum >> nettestReceive.md5');
  #print $randomData;
  my($context) = new Digest::MD5;
  $context->reset();
  $context->add($randomData);
  print $context->hexdigest() . "\n\n";
}#End if
elsif($operation eq 'send')
{
  my($remoteIP) = shift(@ARGV) || '127.0.0.1';
  my($remotePort) = shift(@ARGV) || 12344;
  my($dataSize) = shift(@ARGV) || 100000;
  my($remoteProtocol) = shift(@ARGV) || 'tcp';
  my $sock = new IO::Socket::INET (PeerAddr => $remoteIP, PeerPort => $remotePort, Proto => $remoteProtocol);
  die "Could not create socket: $!\n" unless $sock;

  print STDERR 'Loading in sending mode for ' . $remoteIP . ':' . $remotePort . "\n";

  $randomData = randomData($dataSize);
  print $sock $randomData;
  close($sock); 

  #exec('echo ' . $randomData . ' | md5sum >> nettestSend.md5');
  #print $randomData;
  my($context) = new Digest::MD5;
  $context->reset();
  $context->add($randomData);
  print $context->hexdigest() . "\n\n";
}#End elsif
else
{
  print "\n\n" . 'No relevant arguments found...' . "\n";
  print './nettest.pl listen listenIpAddress listenPort' . "\n";
  print 'or' . "\n";
  print './nettest.pl send remoteHostAddress remotePort numberToSend' . "\n\n\n";
}#End else



#Method to actually do the random data generation and return one big long string.
sub randomData
{
  my($maxData) = shift(@_);
  my($ctr0) = 0;
  srand(1123581321345589144);
  my($randData) = '';

  for($ctr0 = 0; $ctr0 < $maxData; ++$ctr0)
  {
    $randData .= rand(10000000);
  }#End for
  return $randData;
}

