#!/usr/bin/perl

use Net::Pcap qw(pcap_open_live pcap_compile pcap_setfilter pcap_loop pcap_close);
use NetPacket::Ethernet qw(:strip);
use NetPacket::IP qw(:strip);
use NetPacket::TCP;
use Net::RawIP;
use Time::HiRes qw(usleep);
use Data::Dumper qw(Dumper);
use threads;
use threads::shared;
use strict;

unless ($> == 0) { print "needs moar root\n"; exit(); }

$|++;

sub addrtoint { return(unpack("N", pack("C4", split(/\./,$_[0])))); }
sub inttoaddr { return(join(".", unpack("C4", pack("N", $_[0])))); }

my $hosts = 0;
share($hosts);
my @ranges;
my @ports;
my $total = 0;
my $sent = 0;
my $filter_str = '(src port ';
my $my_ip = 'PUTYOURFUCKINGIPHERE'; # !!! LOOK LOOK FILL ME IN LOL !!!
my $packets_since = 0;
my $pps = 0;

foreach my $arg (@ARGV) {
	my ($temp_ip_from, $temp_ip_to) = ('', '');

	if (-e $arg) {
		open(my $fh, $arg);
		while (my $line = <$fh>) {
			chomp $line;

			if ($line =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
				($temp_ip_from, $temp_ip_to) = split(/-/, $line, 2);
				if (addrtoint($temp_ip_from) < addrtoint($temp_ip_to)) { push(@ranges, $line); }
			} elsif ($line =~ /^(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/) {
				($temp_ip_from, $temp_ip_to) = ("$1.$2.$3.$4", "$1.$2.$3.$4");
				$temp_ip_from =~ s/\*/0/g;
				$temp_ip_to =~ s/\*/255/g;
				push(@ranges, $temp_ip_from.'-'.$temp_ip_to);
			}
		}
		close($fh);
	} elsif ($arg =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) {
		($temp_ip_from, $temp_ip_to) = split(/-/, $arg, 2);
		if (addrtoint($temp_ip_from) < addrtoint($temp_ip_to)) { push(@ranges, $arg); }
	} elsif ($arg =~ /^(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/) {
		($temp_ip_from, $temp_ip_to) = ("$1.$2.$3.$4", "$1.$2.$3.$4");
		$temp_ip_from =~ s/\*/0/g;
		$temp_ip_to =~ s/\*/255/g;
		push(@ranges, $temp_ip_from.'-'.$temp_ip_to);
	} elsif ($arg =~ /^\d+$/) {
		push(@ports, $arg);
	}
}

unless (@ports) { push(@ports, '80'); }

foreach my $range (@ranges) {
	my ($temp_ip_from, $temp_ip_to) = split(/-/, $range);
	$total += ((addrtoint($temp_ip_to) - addrtoint($temp_ip_from)) + 1) * scalar(@ports);
}

$filter_str .= join(' or src port ', @ports).') and dst host '.$my_ip;
print "$filter_str\n";

sub process_packet {
	my($user_data, $header, $packet) = @_;
	my $ip_obj = NetPacket::IP->decode(eth_strip($packet));
	my $tcp_obj = NetPacket::TCP->decode(ip_strip(eth_strip($packet)));

	if ($tcp_obj->{flags} == 18) {
		open(my $fh, '>>log.txt');
		print $fh $ip_obj->{src_ip}.':'.$tcp_obj->{src_port}."\n";
		close($fh);
		$hosts++;
	}
}

sub sniff {
	my $err = '';
	my $filter;
	my $dev = 'eth0';

	my $pcap = pcap_open_live($dev, 1024, 1, 0, \$err);

	pcap_compile($pcap, \$filter, $filter_str, 1, '255.255.255.0');
	pcap_setfilter($pcap, $filter);

	pcap_loop($pcap, 0, \&process_packet, "just for the demo");

	pcap_close($pcap);
}

threads->new(\&sniff);

my $time_last = time();
my $hosts_last = $hosts;
my $percent = 0;
my $percent_last = 0;

print "[Progress  ] [   %] [Hosts] [Current IP     ] [Port ] [PPS  ]\n";

my $n = Net::RawIP->new({
	ip  => {
		saddr => $my_ip,
	},
	tcp => {
		source => int(rand()*64511)+1024,
#		psh    => 1,
		syn    => 1,
	},
});

foreach my $range (@ranges) {
	my ($ip_from, $ip_to, $ip_cur);
	($ip_from, $ip_to) = split(/-/, $range);

	foreach my $port (@ports) {
		$n->set({ tcp => { dest => $port } });
		for ($ip_cur=addrtoint($ip_from);$ip_cur<=addrtoint($ip_to);$ip_cur++) {
			$sent++;
			$packets_since++;
			next if inttoaddr($ip_cur) =~ /\.(0|255)$/;

			$n->set({ ip => { daddr => inttoaddr($ip_cur) } });

			$n->send;

			$percent = int((($sent+1)/$total)*100);

			if ((time() > $time_last) || ($hosts > $hosts_last) || ($percent > $percent_last)) {
				$hosts_last = $hosts;
				$percent_last = $percent;

			       printf("\r[%-10s] [%3d%%] [%5d] [%-15s] [%-5d] [%-5d]", ('#' x int($percent/10)), $percent, $hosts, inttoaddr($ip_cur), $port, $pps);
			}

			if (time() > $time_last) {
				$time_last = time();
				$pps = $packets_since;
				$packets_since = 0;
			}
		}
	}
}

print "\nSent all packets, waiting 5 seconds for any late replies...\n";

sleep 5;

system("kill $$");

