#!/usr/bin/perl -w
use Capture::Tiny 'capture';
use Devel::GDB;
use File::Find;
use Getopt::Long;
my %config;
Getopt::Long::Configure("prefix_pattern=(-|--)");
GetOptions( \%config, qw(f=s n=s r=s c=s e=s h help) );

&parseopts();

#Parse the options
sub parseopts {

	#display help if asked
	if ( $config{h} || $config{help} ) {
		&printhelp();
	}

	#filemaks do we have a path to pcaps?
	if ( $config{f} ) {
		@files = <$config{f}>;
	}
	else {
		print("filemask not specified or doesn't exist\n");
		&printhelp();
	}

	#number of times to loop
	if ( $config{n} ) {
		$loopnum = $config{n};
		print( "looping " . $loopnum . " times or until we have an error\n" );
	}
	else {
		print("looping forever or until we have an error\n");
		$loopnum = "infinity";
	}

	#rules file do we have a path and does it exist
	if ( $config{r} && -e $config{r} ) {
		$rules = $config{r};
		print "rules file $rules\n";
	}
	else {
		print("rules file not specified or doesn't exist\n");
		&printhelp();
	}

	#config file do we have a path and does it exist
	if ( $config{c} && -e $config{c} ) {
		$configfile = $config{c};
		print "rules file $rules\n";
	}
	else {
		print("config file not specified or doesn't exist\n");
		&printhelp();
	}

	#error ratio to introduce TODO validate between 0.00 and 1.0
	if ( $config{e} ) {
		$editeratio = $config{e};
		print "using error ratio" . $editeratio . "\n";
	}
	else {
		print("not fuzzing pcap(s)\n");
	}

}

sub printhelp {
	print(
"-f=<filemask for pcaps>\n-n=<number of iterations or if not specified will run until error>\n-r=<path to ids rules file>\n-e=<editcap error ratio to introduce if not specified will not fuzz>\n"
	);
	exit;
}
$successcnt = 0;
while ($successcnt <= $loopnum ) {

	#@files = </home/coz/downloads/pcaps/*/*.pcap>;
	foreach $file (@files) {
		print $file . "\n";
		$file =~ s/\(/\\(/g;
		$file =~ s/\)/\\)/g;
		( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
		  localtime(time);
		$timestamp = sprintf "%4d-%02d-%02d-%02d-%02d-%02d", $year + 1900,
		  $mon + 1, $mday, $hour, $min, $sec;

		if ( defined $editeratio ) {
			$fuzzedfile = $file . "-fuzz-" . $timestamp;
			$editcapcmd = "editcap -E 0.02 " . $file . " " . $fuzzedfile;
			print( "running " . $editcapcmd . "\n" );
			system("$editcapcmd");
		}
		else {
			$fuzzedfile = $file;
		}

		#$fuzzedfile = $file;
		$fullcmd =
		    "ulimit -c unlimited; src/suricata -c "
		  . $configfile . " -r "
		  . $fuzzedfile
		  . " -l ./ -s "
		  . $rules;
		print "running $fullcmd \n";
		( $out, $err ) = capture {
			system $fullcmd;
			$exit = $? >> 8;
		};

		#print "out:$out\n";
		#print "err:$err\n";
		print "exit value $exit\n";
		if ( $exit ne 0 ) {

			$report = $fuzzedfile . "ERR.txt";
			open( REPORT, ">$report" )
			  || ( print "Could not open report file! $report\n" );

			#print REPORT "$0\n";
			print REPORT "COMMAND:$fullcmd\n";
			print REPORT "STDERR:$err\n";
			print REPORT "EXITVAL:$exit\n";
			print REPORT "STDOUT:$out\n";

			&core_dump();
			if ($core_dump) {
				print REPORT $core_dump;
				print "core dump \n $core_dump";
				system( "mv /home/coz/downloads/oisfnew/core "
					  . $fuzzedfile
					  . ".core" );
			}
			close(REPORT);
			exit;
		}
		else {
			$successcnt++;
			print( "we have run with success " . $successcnt . " times\n" );
			if ( defined $editeratio ) {
				$rmcmd = "rm -f " . $fuzzedfile;
				print( "running " . $rmcmd . "\n" );
				system("$rmcmd");
			}
		}
	}
}

sub core_dump {
	my $self = shift;
	$core_dump = "";

	if ( eval { require Devel::GDB } ) {
		find( \&dump_core_file, "/home/coz/downloads/oisfnew/" );
	}

	$core_dump || '  [CORE TRACE COMES HERE]';
}

sub dump_core_file {
	return unless /^core(\.\d+)?$/;

	my $core = $_;
	print "core dump found $core processesing \n";
	my $gdb = new Devel::GDB();
	$core_dump .= join '',
	  $gdb->get("file /home/coz/downloads/oisfnew/src/suricata"),
	  $gdb->get('sharedlibrary'), $gdb->get("core $core"),

	  #$gdb->get('info threads'),
	  #$gdb->get('thread apply all bt full');
	  $gdb->get('bt full');
	print "core dump \n $core_dump";

}
