#!/usr/bin/perl
use strict;
use warnings;

use Config::Simple;
use Net::Pcap qw( :functions );
use NetPacket::Ethernet;
use NetPacket::IP;
use NetPacket::TCP;
use NetPacket::ICMP;

our (%config,$pcap,@all_packet_info,@open_ports,@udp_packets,@icmp_packets);

&read_config_file;
&read_offline_file;
&list_unique_connections;
&scan_statistics;
&get_open_ports;
&report();

sub read_config_file() {
	#Read PCAP Location
	tie %config, "Config::Simple",'config.txt';
}

sub read_offline_file() {
	my ($dump,$err);
	#File to be opened
	$dump = $config{capture_file_location};
	#Opening the Pcap file to read
	$pcap = pcap_open_offline($dump, \$err) or die "Can't read '$dump': $err\n";
	process_packet();
	#Closing after processing the Pcap file
	pcap_close($pcap);
}

sub process_packet {
       my($user_data, $header, $packet) = @_;
	my ($ether,$ip,$icmp,$trans,%header);
	open(PACKET , ">pktdump") or die "Cannot open file:$!";			

	#Grab only selected fields from each packet
	while ($packet = pcap_next( $pcap, \%header )) {
		$ether = NetPacket::Ethernet->decode($packet);	
		$ip = NetPacket::IP->decode($ether->{'data'});
		$icmp = NetPacket::ICMP->decode($ip->{'data'});
		$trans = NetPacket::TCP->decode($ip->{'data'});

			print PACKET "$ip->{src_ip}:$ether->{src_mac}:$ip->{dest_ip}:$ether->{dest_mac}:$ip->{proto}:$trans->{src_port}:$trans->{dest_port}:$trans->{flags}\n";
	}
	close(PACKET);
}

sub list_unique_connections() {
	#Get a list of all unique connections between pairs of hosts
	my ($packet,@split_packet,@t1,$count,%t2,@t3);
	@t1=""; $count=0;

	open(PACKET , "<pktdump") or die "Cannot open file:$!";
	@all_packet_info = <PACKET>;
	close(PACKET);

	open(LIST , ">list_of_connections") or die "Cannot open file:$!";
	print LIST "Source IP\tSource MAC\tDestination IP\tDestination MAC\n";
	print LIST "-" x 64 . "\n";
	foreach $packet (@all_packet_info) {
		@split_packet = split(":" , $packet);
		$t1[$count] = "$split_packet[0]\t$split_packet[1]\t$split_packet[2]\t$split_packet[3]\n"; 
		$count++;
	}
	%t2   = map { $_, 1 } @t1;
	@t3 = keys %t2;
	print LIST @t3;
	close(LIST);
}

sub scan_statistics(){
	#Get all types of scans attempted to be made by each IP Address.
	my (@split_packet,$packet,%stats,$syn_count,$t1,$t2,$udp_count,$icmp_count);
	$syn_count=0; $udp_count=0;$icmp_count=0;$udp_count=0;
	open(PORTS , ">>all_ports") or die "Cannot open file:$!";
	foreach $packet (@all_packet_info) {
		@split_packet = split(":" , $packet);

		#Check if open ports revealed in this packet.
		get_open_ports($packet);

		#Comparing value of the TCP Flags/Protocols and deciding type of scan.
		if ($split_packet[7] == 2) {
			$stats{$split_packet[0]}{syn} += 1;
		}
		elsif ($split_packet[4] == 17) {
			$stats{$split_packet[0]}{udp} +=1;
			$udp_packets[$udp_count] = $packet;
			$udp_count++;
		}
		elsif ($split_packet[4] == 1) {
			$icmp_packets[$icmp_count] = $packet;
			$icmp_count++;
		}
		elsif ($split_packet[7] == 41) {
			$stats{$split_packet[0]}{xmas} +=1;
		}
		elsif ($split_packet[7] == 16) {
			$stats{$split_packet[0]}{ack} +=1;
		}
		elsif ($split_packet[7] == 4) {
			$stats{$split_packet[0]}{rst} +=1;
		}
	}
	
	open(STATS , ">port_scan_stats") or die "Cannot open file:$!";
	for my $k (keys %stats) {
		print STATS "============================IP\t$k:==========================\n";
		if (defined $stats{$k}{syn}) {
			print STATS "SYN Scan Stats\t$stats{$k}{syn}\n";
		}
		if (defined $stats{$k}{udp}) {
			print STATS "UDP Scan Stats\t$stats{$k}{udp}\n";
		}
		if (defined $stats{$k}{xmas}) {
			print STATS "XMAS Scan Stats\t$stats{$k}{xmas}\n";
		}
		if (defined $stats{$k}{ack}) {
			print STATS "ACK Scan Stats\t$stats{$k}{ack}\n";
		}
		if (defined $stats{$k}{rst}) {
			print STATS "RST Scan Stats\t$stats{$k}{rst}\n";
		}
	}
	close(PORTS);
	close(STATS);
}

sub get_open_ports() {
	#Check if the packet contains a SYN/ACK. This means the destination host definitely has this port open.
	my ($packet,@split_packet,$count);
	
	$packet = shift;
	if (defined $packet) {
		@split_packet = split(":" , $packet);
		if ($split_packet[7] == 18) {
			print PORTS "$split_packet[0]".':'."$split_packet[5]\n";
		}
	}
}

sub report() {
	#Print out the results of Open Ports after stripping all duplicates.
	open(PORTS , "<all_ports") or die "Cannot open file:$!";
	my @t1 = <PORTS>;
	close(PORTS);

	my %t2   = map { $_, 1 } @t1;
	my @t3 = keys %t2;

	open(PORTS , ">all_ports") or die "Cannot open file:$!";
	print PORTS "==================Here is a list of open ports in the form IP:Port==================\n";
	print PORTS @t3;
	close(PORTS);
}
