#!/usr/bin/perl
#
# A NNTP (Network News Transfer Protocol) scanner in perl
# James.Abendschan@nau.edu  10 December 1995
#
# usage: nntp-scan hostname
# human-readable logging is done to stdout
#
# jwa 24 June 96 - added sprintf'ing of speed for more "precise" speed
#		   measurments
#
# jwa 28 Oct 96 - added better timeout control values
#

# sys/socket.ph must exist before you run this.  

require 'sys/socket.ph';

# did the program die on the above line?  run h2ph!

$SIG{'ALRM'} = 'do_alarm';

$serverport = "119";
$connecttimeout = "7";		# time to wait for a connect()
$bannertimeout = "10";		# time to wait for a banner message
$chattimeout = "300";		# time to wait for the LIST & POST test

$myname = "$0";
$hostname = $ARGV[0];
if ($hostname eq "") {
 print STDERR "usage: $myname hostname\n";
 exit 1;
}

# main()

{ 

init:
    
 # seed & pick a random port number 
 
 for ($i=0; $i < $$; $i++) { rand(); }

 $clientport = int(rand(32768) + 1024);
 $sockaddr = 'S n a4 x8';
 $locport=pack($sockaddr, &AF_INET, $clientport, "\0\0\0\0");

 if (!socket(C, &PF_INET, &SOCK_STREAM, $proto)) {
  #print "WARNING - couldn't create client socket: $!\n";
  sleep 5;
  goto init;
 }

 if (!bind(C, $locport)) { 
  # die("cannot bind client socket: $!\n");
  #print "WARNING - couldn't bind client socket:$!\n";
  sleep (5);
  goto init;
 }

 ($name, $aliases, $proto) = getprotobyname('tcp');
 ($name, $aliases, $type, $len, $thisaddr) = gethostbyname($hostname);
 ($a,$b,$c,$d) = unpack('C4', $thisaddr);

 $ipaddr="$a.$b.$c.$d";

 if ($a eq "") {
  print STDERR "Can't resolve hostname.\n";
 }
 
 $thatport = pack($sockaddr, &AF_INET, $serverport, $thisaddr);
  
 alarm(0);
 alarm($connecttimeout);

 if (!connect(C, $thatport)) {
  print "connect() failed: $!\n";
  exit(1);
 }

 alarm(0);

 select(C); 
 $| = 1;
 select(STDOUT);

 # Now send/rec data to C
 # need to alarm here

 alarm($bannertimeout); 
 $data = <C>;
 alarm(0);

 alarm($chattimeout);
 #print "Got: $data\n";
 if (index($data, "2") == 0) { 
  $banner = $data;
  chop $banner; chop $banner;
  print C "LIST\n";
  $bytes = "0";
  $group = "";
  $groupcount = "0";
  $maxmsgcount = 0;
  $stime = time();
  while (index($group, ".") != 0) {
   $group = <C>;
   if (index($group, "480") == 0) {
    goto closeup;
   }
   $groupcount++;
   $grold = $grname;
   ($grname, $lastm, $firstm, $modflag) = split(/ /, $group);
   $mcount = $lastm - $firstm;
   if ($mcount > $maxmsgcount) {
    $maxmsgcount = $mcount;
    $maxgroup = $grname;
   }
   $bytes = $bytes + length($group);
  }
  $groupcount = $groupcount - 2;

  if ($groupcount <= 0) {
   goto closeup;
  }
  
  $etime = time();
  $eltime = $etime - $stime;

  print C "GROUP $maxgroup\n";
  ($code, $foo, $first, $last, $groupname) = split (/ /, <C>);
  if ($code ne "211") {
   goto closeup;
  }
  print C "ARTICLE $last\n";
  ($code, $number, $msgid, $foo) = split (/ /, <C>);
  if ($code ne "220") {
   goto closeup;
  }

  $data = <C>;

  while (index($data, ".") != 0) {
   $data = <C>;
  }

  $bytes++;		# get rid of div0 errors..
  $eltime++;
  #$kps = int (($bytes / $eltime) / 1024);
  $kps = sprintf ( "%3.2f", (($bytes / $eltime) / 1024));
  #print "($kps k/sec)\n";
  print C "POST\n";
  $postingcode = <C>; chop $postingcode; chop $postingcode;
  print C ".\n";
  $data = <C>;
  print C ".\n";
  $data = <C>;
  print C "XOVER\n" ;
  $xovercode = <C>; chop $xovercode; chop $xovercode;
  print C "QUIT\n";

  print "Host: $name ($ipaddr)\n";
  print "$banner\n";
  print "Supported groups: $groupcount\n";
  print "Highest message count: $maxmsgcount in $maxgroup\n";
  print "K/sec: $kps\n";  
  print "POST: $postingcode  XOVER: $xovercode\n";
 }
closeup:
 alarm(0);
 shutdown (C, 1);
 close(C);
}

exit(0);

#
# Handle timeouts
#

sub do_alarm {
 alarm(0); # reset alarm clock
 $SIG{'ALRM'} = 'do_alarm';
 print STDERR ".. timeout\n";
 shutdown (C, 1);
 close(C);
 exit(1);
}

