#! /usr/bin/perl
 
###
### this is to place an ssh tunnel to the SMTP server sending your messages and running a process that 
### repeatedly sends a HELO/QUIT command pair to the server to keep the tunnelling alive, despite timeouts
 
### Copyright (C) 2006  Marco Danelutto, Dept. Computer Science, Univ. Pisa, Italy
### 
### This program is free software; you can redistribute it and/or
### modify it under the terms of the GNU General Public License
### as published by the Free Software Foundation; either version 2
### of the License, or (at your option) any later version.
### 
### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU General Public License for more details.
### 
### You should have received a copy of the GNU General Public License
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. 
 
#######################################################################################################################################
### this are the default parameters used in case the -d option is specified or in case one of the parameters are not specified 
#######################################################################################################################################
 
$defaultLocalPort      =   34343;                    ### dummy local port, probably unused
$defaulttunnelhostname =   "pacifico.di.unipi.it";   ### cannot tunnel directly on mailserver, therefore use another machine
$defaultsmtpserver     =   "mailserver.di.unipi.it"; ### the target mailserver
$defaultSeconds        =   180;                      ### send an HELO/QUIT message onto the tunnel every 3 minutes, by default
 
### uncomment the following line to see debug printouts
$debug = 1; ### comment this line to avoid printouts ... 
 
####################################################################################################################################
######################################### do not modify code below this line #######################################################
####################################################################################################################################
 
$maiscriptversion = "1.01";
 
use Getopt::Std;
use IO::Socket::INET;
 
getopts("hl:t:s:m:dvu:");
 
if ($opt_h) {
  print "mailscript.pl  [-h]    -l localport    -t tunnelhostname   -s serverhostname -m keepaliveloopSeconds\n";
  print "           -h                prints this help\n";
  print "           -l localport      uses localport to set up the local end of the ssh tunnel. This is the port to be used to connect to SMTP server on localhost (127.0.0.1)\n";
  print "           -t tunnelhostname this is the name of the host to use for the tunnelling. Must be reachable by ssh \n";
  print "           -s serverhostname this is the name of the mailserver\n";
  print "           -m s              this is the amount of minutes to wait before sending a new keepalive HELO/QUIT to the mailserver\n";
  print "           -u username       this is the login name to be used on the remote host\n";
  print "           -d                verbose mode on\n";
  print "           -v                print the version number\n";
  exit;
}
 
 
### 
### handle parameters from command line
###
 
if ($opt_l) {
  $localport = $opt_l; 
} else {
  $localport = $defaultLocalPort;
}
print "Using local port $localport\n";
if ($opt_t) {
  $tunnelhostname = $opt_t; 
} else {
  $tunnelhostname =$defaulttunnelhostname;
}
 
if ($opt_s) {
  $smtpserver = $opt_s; 
} else {
  $smtpserver = $defaultsmtpserver;
}
 
if ($opt_m) {
  $seconds = $opt_m * 60;
} else {
  $seconds = $defaultSeconds; 
}
 
if ($opt_u) {
  $remoteusername = $opt_u."@"; 
} else {
  $remoteusername = "";
}
 
if ($opt_v) {
  print "mailscript.pl version $maiscriptversion\n";
  exit;
}
 
print "Performing keepalive each $seconds seconds\n";
 
###
### set up process to perform tunnelling periodic "refresh"
###
 
$pidRefresh = fork; 
if ($pidRefresh == 0) {
  ###
  ### this is the "refresh" process
  ###
  sleep ($seconds < $defaultSeconds ? $defaultSeconds : $seconds); ### wait for the tunnelling process started (this should be relaced by a more correct 
            ### attempt to connect to the local port, actually
  if($debug) {
    print "Entering the refresh process .... \n";
  }
  while(1==1) {
    if($debug) {
      print "Looping ... \n";
    }
    ### in an infinite loop
    ### connect to the SMTP server
    $remote = "127.0.0.1";
    $socket = IO::Socket::INET->new(PeerAddr => $remote,
				    PeerPort => $localport,
				    Proto    => "tcp",
				    Type     => SOCK_STREAM)
      or die "Couldn't connect to $remote:$localport : $!\n"; ### in questo caso devo invece ritentare, visto che potrebbe essere terminata la connessione per via di uno sleep 
    ### send an HELO message
    print $socket "HELO keep.me.alive\n";
    ### wait answer and then send the quit message
    $answer = <$socket>; 
    if($debug) {
      print "Sent HELO keep.me.alive\nReceived $answer\n";
    }
    print $socket "QUIT";
    ### that's all, connection alive
    $answer = <$socket>; 
   if($debug) {
     print "Sent QUIT\nReceived $answer\n";
   }
    close($socket);
    ### wait the loop timeout ...
   if($debug) {
     print "HELO/QUIT sent!\n";
   }
    sleep $seconds;
    ### ... then restart
  } 
  die "Error while executing refresh of ssh tunnel on port $localport\n";
} else {
  ### this is main process
  if($debug) {
    print "Tunnel refresh process created\n";
  }
  $tunnelPid = fork; 
  if ($tunnelPid == 0) {
    ### 
    ### this is the tunnel process ...
    ###
    if($opt_d) {  # -v or -d is the same ... plenty of options at the moment
	$debugssh = " -v -v -v ";
    } else {
        $debugssh = " ";
    }
    $tunnelcmd = "ssh $debugssh -N -L $localport:$smtpserver:25 $remoteusername$tunnelhostname";
    if($debug) {
      print "Executing tunnel::)$tunnelcmd(::\n";
    }
    exec $tunnelcmd 
      or die "Cannot exec ssh tunnel command $tunnelcmd\n";
  } else {
    ### this is the main process again 
    print "Tunnel executed\n";
    print "To interrupt the tunnel/keepalive service, please issue a ^C on the terminal\n";
    sleep; 
    $terminatecmd = "kill -9 $pidRefresh $tunnelPid";
    if($debuf) {
      print "Going to execute a $terminatecmd\n";
    }
    exit;
  }
}
 
die "terminated\n";
###