#!/usr/bin/perl
#
# 		MailTunnel Node
#						logic@nocrew.org
#
#  WARNING: This is ALPHA software (see DISCLAIMER). Use at your own risk.
#
#
#   Note:
#   'packets' is used thru this file to describe the data being tunneled.
#   
#
#
#   BUGS:  
#    Alot.       
#
#
#    Copyright (C) Magnus Lundstrm 1999
#

require "common.pl" ;
require "conf.pl" ;
setup(); # fetch settings.


$mode = "@ARGV" ;

if ($mode =~ /^forward\s+(\S+)\s+(\d+)$/) {
  $mode = "forward" ;
  $forward_host = $1 ;
  $forward_port = $2 ;
} elsif ($mode =~ /^listen\s+(\d+)$/) {
  $mode = "listen";
  $listen_port = $1 ;
} elsif ($mode =~ /^\-[Vv]/) {
  print "mtn (MailTunnel Node) v$version ($release)\n" ;
  print "Copyright Magnus Lundstrm 1999 (logic\@nocrew.org)\n" ;
  print "\n" ;
  print "DISCLAIMER:\n" ;
  print "This is PRE-ALPHA software. It worked for me, it wont work for you,\n" ;
  print "it will just crash your system and empty your refrigerator.\n" ;
  print "If You're happy with that, go ahead and use it\n" ;
  exit(0) ;

} else {
  die "Usage: $0 [forward <host> | listen] <port>\n" ;
}




dbg("STARTUP",1);
use Socket ;
use Fcntl ;
require "common.pl" ;
require "conf.pl" ;
setup();






start_poll_mail();

if ($mode eq "listen") {
    # initialize the listening socket. (We will only handle one simultaenous client.)

    dbg("Creating socket", 5);
    $proto = getprotobyname('tcp') or die "$0: getprotobyname: $!\n" ; 
    socket(SOCKFD, PF_INET, SOCK_STREAM, $proto) or die "$0: socket: $!\n" ;


    dbg("Setting socket options", 5);
    setsockopt(SOCKFD, SOL_SOCKET, SO_REUSEADDR, pack("l", 1)) or die "$0: setsockopt: $!\n" ;

    dbg("Binding port $listen_port", 4);
    bind(SOCKFD, sockaddr_in($listen_port, INADDR_ANY)) or die "$0: bind: $!\n" ;
    dbg("Listening...", 5);
    listen(SOCKFD, SOMAXCONN) or die "$0: listen: $!\n"; 

    my $paddr ;
    my $buf ;
    my $xtime ;
    my $lsize ;
    my $last_poll_time ;


    for ( ; $paddr = accept(CLIFD, SOCKFD); close CLIFD) {
#      select(CLIFD) ;
#      $|=0;
#      $/ = undef ;
#      select(STDOUT);
      dbg("Connection accepted", 3);
      $buf = "" ;
 #     $/ = undef ;
    fcntl(CLIFD, F_SETFL(), O_NONBLOCK() ) or die "$0: fcntl failed: $!\n" ;
      while (1) {
	$bits="";
	vec($bits,fileno(CLIFD),1) = 1;
	$n=select($foo=$bits, undef, undef, $send_poll_interval);  #  Thanks Lars(@nocrew.org)

	  dbg("select() returned $n, length(buf)=", length($buf), ";", 6);
	if ($n < 0) {
	  dbg("select() returned error -> $n : $!", 2);


	} elsif ($n == 0) { 
	  dbg("timeout ($send_poll_interval)", 6) ;
	  # timeout, no data received, send any data in our buffer
#	  $buf.=<CLIFD> ;
	  if (length($buf)>0) { # verify that the buffer is not empty...
	    dbg("buf is not empty (", length($buf), ") sending", 6) ;
	    send_packet($buf) ;
	    $buf = "";
	  }
	} else {
	  # data was received, check if the buffer is full, if so, send, elseways, do nothing
	  dbg("data received on socket", 6);
	  $buf.=<CLIFD>;
	  if (length($buf) > $max_buffersize) {
	    dbg("buffer length (", length($buf), ") exceeds $max_buffersize : sending data", 6) ;
	    send_packet($buf) ;
	    $buf = "";
	  }
	}

	if ($last_poll_time + $drop_poll_interval < time) {
	  poll_mail();
	  $last_poll_time = time();
	}

      }
	  dbg("Connection dropped?", 3) ;


    }
    



} elsif ($mode eq "forward") {
  # Create socket and connect to Peer.
  dbg("Creating socket", 5);
  
    $proto = getprotobyname('tcp') or die "$0: getprotobyname: $!\n" ; 
    socket(SOCKFD, AF_INET, SOCK_STREAM, $proto) or die "$0: socket: $!\n" ;


    dbg("Connecting to $forward_host:$forward_port", 3);
      $forward_addr = gethostbyname($forward_host) or die "$0: gethostbyname: $!\n" ;
      
    $sin = sockaddr_in($forward_port, $forward_addr) or die "$0: sockaddr_in: $!\n" ;
    connect(SOCKFD, $sin) or die "$0: connect: $!\n" ;

  dbg("Entering forward-client loop",4);
  my $cbuf ;
  my $ltime ;
  my $ptime;
  my $lsize ;
  my $clast_poll_time ;
  my $clast_send_time ;
  $clast_send_time = time();
    fcntl(SOCKFD, F_SETFL, O_NONBLOCK) or die "$0: fcntl failed: $!\n" ;

  while (1) {
	$bits="";
	vec($bits,fileno(SOCKFD),1) = 1;
	$n=select($foo=$bits, undef, undef, 1);  #  Thanks Lars(@nocrew.org)

	  dbg("select() returned $n, length(cbuf)=", length($cbuf), ";", 6);
	if ($n < 0) {
	  dbg("select() returned error -> $n : $!", 2);
	} elsif ($n == 0) {
	  # timeout, no data received. if theres enuff data and we've not sent anything in X seconds, send it.
	  dbg("(\$clast_send_time + \$send_poll_interval) =", ($clast_send_time + $send_poll_interval) - time(),".", 7) ;
	  if (length($cbuf)>0 && ($clast_send_time + $send_poll_interval < time)) { 
	    dbg("cbuf is not empty (", length($cbuf), ") sending", 6) ;
	    send_packet($cbuf) ;
	    $cbuf = "";
	    $clast_send_time = time();
	  }


	} else {
	  # data was received, check if the buffer is full, if so, send, elseways, do nothing
	  dbg("data received on socket", 6);
	  $cbuf.=<SOCKFD>;
	  if (length($cbuf) > $max_buffersize) {
	    dbg("buffer length (", length($cbuf), ") exceeds $max_buffersize : sending data", 6) ;
	    send_packet($cbuf) ;
	    $cbuf = "";
	    $clast_send_time = time ;
	  }
	}
	if ($clast_poll_time + $drop_poll_interval < time) {
	  poll_mail();
	  $clast_poll_time = time();
	}


  }



} else {
  die "$0: unexpected: mode($mode) not set properly.\n"; 
}






exit(0); 

