#!/usr/bin/perl -w
#
# Here is a nice little irc bounce prog I made. The command line is:
#  sbounce irc.server.com ircport bounceport
# For example "sbounce irc.blackened.com 6667 5555"
# There are two passwords you can set:
# use -pass password to set a password to keep out the undesirables.
# You can then type on most clients "/server bouce.host.com port password"
# use -admin password to set up administator access.  Then you can change
# settings remotely.  Use -maxcli # to limit the number of simaltaneous users.
# use the -logoff options to turn of logging.  Otherwise, info is stored in
# bounce.log in the same dir as the script.
# [SB]Tikiman

use Socket;

$remote = $ARGV[0];
$port = $ARGV[1];
$bport = $ARGV[2];
$log = 'True';

foreach (reverse @ARGV) {
 (/^-max/i) && ($maxcli = $lastval);
 (/^-pass/i) && ($password = $lastval);
 (/^-admin/i) && ($masterpass = $lastval);
 (/^-logoff/i) && ($log = ''); 
 $lastval = $_;
}

($remote) && ($port) && ($bport) || ( comhelp() ) ; 
 
sub sockclose {
 close(SOCK);
}

sub cliclose {
 close(GOTCLI);
}

sub clidec {
 --$connections;
}

sub timeout {
 print GOTCLI "ERROR :Closing Link: Ping Timeout (No soup for you!)\n";
 close(GOTCLI);
}


sub logclose {
 ($$ == $bigdaddy) && bnlog("Bounce Server Terminated at " . localtime() . ".\n") ;
 exit;
}

$SIG{'ALRM'} = 'timeout';
$SIG{'TERM'} = 'logclose';
$SIG{'PIPE'} = 'clidec';
$SIG{'USR1'} = 'sockclose';
$SIG{'TRAP'} = 'cliclose';

$bigfork = fork();
if ($bigfork) {
 print "Listening on $bport - process $bigfork\n";
 defined($maxcli) && print "Max Clients: $maxcli\n";
 defined($password) && print "Access password is $password\n"; 
 defined($masterpass) && print "Master password is $masterpass\n";
 exit;
}

$bigdaddy = $$;
$connections = 0;

$proto = getprotobyname('tcp');
socket(WAITCLI, PF_INET, SOCK_STREAM, $proto);
setsockopt(WAITCLI, SOL_SOCKET, SO_REUSEADDR, pack("l", 1));
bind(WAITCLI, sockaddr_in($bport, INADDR_ANY) ) || die "Cannot listen on $bport\n"; 
listen(WAITCLI,20);
bnlog("Bounce server to $remote:$port activated at " . localtime() . ".\n");
while ($ipaddr = accept(GOTCLI,WAITCLI)) {
 ($name,$iaddrr) = sockaddr_in($ipaddr);
 ($name) = gethostbyaddr($iaddrr,AF_INET);
 select(GOTCLI);
 $| = 1;
 select(STDOUT);
 alarm 5;
 if ($_ = <GOTCLI>) {

 }
 else {
  bnlog("[$name] Timeout at " . localtime() . "\n");
  next;
 }
 alarm 0;
 admincheck() && next;
 maxclicheck() && next;
 passcheck() && next;
 ++$connections;
 conser();
 $spawn = fork();
 if ($spawn) {
  next;
 }
 $daddy = $$;
 bnlog("[$name] Bounce login at " . localtime() . "\n");
 $lilkid = fork();
 if ($lilkid) {
  while (<SOCK>) {
   print GOTCLI $_;
  } 
 kill('TRAP',$lilkid);
 exit;
 }
 else {
  while (<GOTCLI>) {
   if (/^die/i) {
    print GOTCLI "Bounce server terminated, current connections maintained.\n";
    kill("TERM",$bigdaddy);
    next;
   }
   print SOCK $_;
  }
  bnlog("[$name] Disconnected at " . localtime() . "\n");
  kill("PIPE",$bigdaddy);
  kill("USR1",$daddy);
 } 
 exit;
}

sub conser {
 $iaddr = inet_aton($remote);
 $paddr = sockaddr_in($port, $iaddr);
 $proto = getprotobyname('tcp');
 close(SOCK);
 socket(SOCK, PF_INET, SOCK_STREAM, $proto)  || die "socket: $!";
 unless(connect(SOCK, $paddr)) {
  print GOTCLI "Cannot connect to Irc Server\n";
  return();
 }
 select(SOCK);
 $| = 1;
 select('stdout');
 print SOCK;
 print GOTCLI "*** NOTICE AUTH :Welcome to sbounce - Client number $connections\n";
 print GOTCLI "*** NOTICE AUTH :Now redirectiong to $remote on $port\n";
}

sub comhelp {
 print STDOUT <<helpblock;
Usage:
 sbounce irc.server.com ircport bounceport
  -max \# (max connections)
  -pass password (set access password)
  -admin password (set administrator password)
  -logoff (disables logging)
helpblock
 exit;
}

sub bnlog {
   ($line) = @_;
   ($log eq 'True') || return();
   open(LOG,">>bounce.log");
   chomp($line);
   print LOG $line . "\n";
   close(LOG);
}

sub admincheck {
 if ( defined($masterpass) ) {
  if (/^pass\s$masterpass\b/i ) {
   bnlog("[$name] Administrator login at " . localtime() . "\n");
   print GOTCLI "NOTICE AUTH :*** Welcome administrator\n";
   climen();
   while (defined($_ = <GOTCLI>)) {
    if (/^QUIT\b/i) {
      last;
    }
    if (/^DIE\b/i) {
      print GOTCLI "Bounce terminating\n";
      exit;
    }
    if (/^SERVER\s(\S+)/i) {
     $remote = $1;
     print GOTCLI "New bounce server - $remote\n";
    }
    if (/^PORT\s(\S+)/i) {
     $port = $1;
      print GOTCLI "New bounce port - $port\n";
    }
    if (/^HELP\b/i) {
      clicom();
    } 
    if (/^maxcli\s(.+)|^maxcli\b/i) {
     $maxcli = $1;
     if (defined($maxcli)) {
      print GOTCLI "New client limit - $maxcli\n";
     }
     else {
      print GOTCLI "Limit cleared -\n";
     }
    }
 
    if (/^password\s(.+)|^password\b/i) {
     $password = $1;
     if (defined($password)) {
      print GOTCLI "New access password - $password\n";
     }
     else {
      print GOTCLI "Password cleared -\n";
     }
    }
 
   }
   close(GOTCLI);
   return("Outta here!");
  }
 }
 return();
}

sub maxclicheck {
 if (defined($maxcli)) {
  if ($connections == $maxcli) {
   bnlog("[$name] Client Max Reached at " . localtime() ."n");
   print GOTCLI "ERROR :Closing Link: Maximum Bounce connections reached\n";
   return("Lets go");
  }
 }
 return();
}

sub passcheck {
 if ( defined($password) ) {
  unless (/^pass\s$password\b/i) {
   bnlog("[$name] Bad Password " . localtime() . "\n");
   print GOTCLI "ERROR :Closing Link: Unauthorized access\n";
   close(GOTCLI);
   return("Byebye\n");
  }
 }
 return();
}

sub climen {
   select(GOTCLI);
   print GOTCLI <<clihelp ; 
* Bounces in progress - $connections
* Current Server - $remote
* Current Port - $port
clihelp
   if (defined($maxcli)) {
    print "* Client Limit - $maxcli\n";

   }
   else {
    print "* No client limit set\n";
   }
   if (defined($password)) {
    print "* Access password - $password\n";
   }
   else {
    print "* No access password set\n";
   }
   print "Type \"/raw help\" or \"/quote help\" for a list of commands\n";
   select(STDOUT);
}

sub clicom {
 print GOTCLI <<help
* Commands - use \"/raw commands\" or \"/quote command\" to execute
* SERVER servername (Select new bounce server)
* PORT port (Select new bounce server port)
* MAXCLI limit (Sets new limit, \"/raw MAXCLI\" to clear
* PASSWORD newpass (Sets new access password, \"/raw PASSWORD\" to allow open access
help
 
}
