
use experimental "smartmatch"; 

$tlimit = 100; # Limit for concurrent DNS queries that will trigger blacklisting
$blimit = 50; # Blacklist all domains having more than this number of domains


$uc = '/usr/local/sbin/unbound-control';
$log='/var/log/fakeauth.log';
$whiteconf='/home/system/whitelist.conf'; # Domains you never ever want to blacklist
$thirdlevel='/home/system/third_level_domains.conf'; # In case of large domains like co.uk, we blacklist 3rd level

undef @whitelist;

if ( open( WHITE, $whiteconf ) ) {
  while ( $line = <WHITE> ) {
    chomp( $line );
    push( @whitelist, $line ); 
  }
}

close( WHITE );

if ( open( THIRD, $thirdlevel ) ) {
  while ( $line = <THIRD> ) {
    chomp( $line );
    push( @thirdlevel, $line ); 
  }
}

close( THIRD );

my %qlist;
my %iplist;
my @blacklist;
my $total = 0; # Total rqueries

my @reqlist = `$uc dump_requestlist | grep wait`;

my $reqnum = @reqlist;

if ( $reqnum < ( $tlimit + 2 ) ) {
  exit; # Not enough concurrent queries to raise alarm
}

print "$reqnum recursive queries at the moment (limit was set to $tlimit).\n\n";

foreach $line ( @reqlist ) {
  $line = lc( $line );
  if ( $line =~ /\d+\s+\w+\s+\w+\s+(\S+)\s+[\-0-9\.]+\s+iterator\s+wait\s+for\s+(\S+)/ ) {
    $query = $1; 
    $client_ip = $2;
    unless ( $query =~ /([a-zA-Z0-9\-\/\_]+\.[a-zA-Z0-9\-\/\_]+\.*)$/ ) {
      next;
    }
    $domain = $1;
    if ( $domain ~~ @thirdlevel ) { # In case of large 2level domains like co.uk, we blacklist 3rd level
      if ( $query =~ /([a-zA-Z0-9\-\/\_]+\.[a-zA-Z0-9\-\/\_]+\.[a-zA-Z0-9\-\/\_]+\.*)$/ ) {
        $domain = $1; 
      }
    } 

    if ( exists( $qlist{ $domain } ) ) {
      $qlist{ $domain }++;
    } else {
      $qlist{ $domain } = 1;
    }
    $total++;

    if ( exists( $iplist{ $client_ip } ) ) {
      $iplist{ $client_ip }++;
    } else {
      $iplist{ $client_ip } = 1;
    }
  }
}

@blacklist = `$uc list_local_zones | grep deny | cut -d' ' -f1`;
unless ( $? == 0 ) {
  die( "Counld not acquire the list of blacklisted domains from unbound-control" );
}


print "Rquery stats per domain:\n\n";


foreach $zone ( sort{ $qlist{ $b } <=> $qlist{ $a } } keys( %qlist ) ) {
  printf "%-30s%s", $zone, $qlist{ $zone };
  if ( $zone ~~ @whitelist ) {
    print " - whitelisted!\n";
    next;
  }

  if ( $qlist{ $zone } > $blimit ) {

    if ( $zone ~~ @blacklist ) {
      print " - already blacklisted\n";
    } else {

    # Probably a bogus domain, we will deny answers. For more proper response, use "reject" instead
      system ( "$uc -q local_zone $zone deny" );
      print " - blacklisted!\n";
      &log( "$zone blacklised." );
    }
  } else {
    print "\n";
  }
}





sub today {
  my @now                 = localtime(time);
  my @now2;
  $now[4] = $now[4] + 1;
  $now[5] = $now[5] + 1900;

  for ( $i=0 ; $i<6 ; $i++ ) {
    if ( $now[$i] < 10 ) {
      $now2[$i] = '0'.$now[$i];
    } else {
      $now2[$i] = $now[$i];
    }
  }

  my $day                 = $now2[3];
  my $month               = $now2[4];
  my $year                = $now2[5];
  my $sec                 = $now2[0];
  my $min                 = $now2[1];
  my $hour                = $now2[2];
  my $result = $day.'/'.($month).'/'.($year).' '.$hour.':'.$min.'.'.$sec;
  return $result
}

sub log {
  open ( LOG, '>>', $log ) or die ("Cannot open main logfile $log\n");
  my $logline = $_[0];
  my $time = &today();
  print LOG "$time $logline\n";
  close( LOG );
}
