#!/usr/bin/perl

use IO::Socket::INET;
use IO::Socket::Timeout;
use LWP::UserAgent;
use Thread::Pool;

# auto-flush on socket
$| = 1;

my $pool = Thread::Pool->new(
  {
   optimize => 'cpu',

   do => sub { my ($s) = @_; scan_host($s); },
   stream => sub {shift; print "streamline with @_\n";},
   pre => sub { },
   post => sub { },

   pre_post_monitor_only => 0,
   checkpoint => \&checkpoint,
   frequency => 1000,

   autoshutdown => 1, # default: 1 = yes

   workers => 10,     # default: 1
   maxjobs => 50,     # default: 5 * workers
   minjobs => 5,      # default: maxjobs / 2
  },
 );

my @servers = qw(
103.25.58.54
103.53.199.149
103.71.254.150
104.153.108.108
111.118.183.209
138.121.202.77
144.217.66.145
146.0.36.7
146.255.79.130
154.127.57.64
154.16.63.3
158.58.172.79
160.202.163.196
173.193.216.105
173.254.195.170
178.124.140.132
178.175.137.250
178.255.40.138
181.215.247.1
184.75.209.154
185.10.17.105
185.145.45.4
185.166.237.41
185.166.237.7
185.171.25.2
185.189.51.91
185.19.85.132
185.209.85.176
185.217.1.130
185.32.221.4
185.4.29.64
185.42.221.56
185.56.90.76
185.84.181.63
190.123.36.112
191.96.71.28
192.162.101.88
192.223.25.69
195.154.233.95
212.7.192.208
212.7.217.38
213.183.50.10
213.183.58.2
213.184.126.130
213.208.152.194
217.170.207.12
23.105.128.146
31.171.155.32
37.235.49.88
41.231.120.127
46.167.245.24
5.187.34.230
51.255.83.107
67.215.4.122
69.65.31.14
78.130.176.160
79.134.225.4
79.172.193.109
81.95.115.62
87.118.122.86
88.208.245.136
89.35.232.90
91.192.100.2
91.233.116.103
93.189.62.90
95.140.125.2
95.167.151.226
);

 $pool->workers( 10 );

foreach $s (@servers)
{
	print "=== Processing $s \n";
	$pool->job($s);
}

$pool->shutdown;

sub scan_host
{
       my ($s) = @_;
	print "$s\n";

       # response from iptdaemon of forwarded hosts/ports
       my @forwarded = split(/,/, get_forwarded_ports($s));

       foreach $f (@forwarded)
       {
                next if index($f, '.') == -1;

                my @parts = split('=', $f);

                my $hostname = $parts[0];
                my @ports = split('\|', $parts[1]);

                foreach (@ports)
                {
                        my $response_raw = request_socket($hostname, $_, pack("i*", 0x17, 0x03, 0x03, 0x04, 0x50, 0xfe, 0x64, 0xff));
                        my $response = substr(unpack("H*", $response_raw), 0, 8);

                        if ($response =~ /15030300/) {
                                print "Testing $hostname:$_ ... infected \n";
                                report_infection($hostname, $_);
                        } else {
                                #print "Testing $hostname:$_ ... clean \n";
                        }
                }
        }

}

sub report_infection
{
	my ($ip, $port) = @_;
	http_get("http://nvpn.net/memberss/jbi.php", "ip=$ip&port=$port");
}

sub get_forwarded_ports
{
	my ($srvaddr) = @_;
	return http_post("http://$srvaddr:80/dy58jdjsr8y1.pl", 
			"secret=s4rt84hsrt564isr64str7ur567q8e6rh7q6&command=list&internal=1&external=1");
}

sub http_get
{
	my ($url, $data) = @_;

	my $ua = LWP::UserAgent->new;
	my $req = HTTP::Request->new(GET => "$url?$data");

	my $resp = $ua->request($req);
	if (!$resp->is_success) {
		print "Unable to call: $url?$data\n";
	}	
}

sub http_post
{
	my ($url, $data) = @_;

	my $ua = LWP::UserAgent->new;
	my $req = HTTP::Request->new(POST => $url);
	$req->content($data);

	my $resp = $ua->request($req);
	if ($resp->is_success) {
		#print $resp->decoded_content . "\n";
		return $resp->decoded_content;
	}

	return "";
}


sub request_socket
{
	my ($ip, $port, $data) = @_;

	# create a connecting socket
	my $socket = new IO::Socket::INET(
		PeerHost => $ip,
		PeerPort => $port,
		Timeout  => 3,
		Proto => 'tcp');

	unless ($socket) {
		#print "cannot connect to the server $!\n";
		return "";
	}

	my $seconds  = int(3);
	my $useconds = int( 1_000_000 * ( $timeout - $seconds ) );
	my $timeout  = pack( 'l!l!', $seconds, $useconds );
	$socket->setsockopt( SOL_SOCKET, SO_RCVTIMEO, $timeout );

	IO::Socket::Timeout->enable_timeouts_on($socket);
	$socket->read_timeout(3);
  	$socket->write_timeout(3);

	$socket->send($data);

	# notify server that request has been sent
	shutdown($socket, 1);

	my $response = "";
	$socket->recv($response, 1024);
	$socket->close();
	
	return $response;
}
