#!/usr/bin/perl -w

use strict;
use Net::PcapUtils;
use NetPacket::TCP;
use Net::Pcap;
use NetPacket::IP qw (:strip);
use NetPacket::Ethernet qw (:strip);
use Fcntl;

# Settings. -------------------------------------------------------------------

my @ip_addresses = qw(123.123.123.123);  # change this to your server ip
my $interface = "eth1";                  # change this to the interface you are sniffing
my $logfile = "/var/log/mailproto.log";
my $port = "25";

# Globals. --------------------------------------------------------------------

my $filter;
my $pcap;

# Subroutines. ----------------------------------------------------------------

# Prepares pcap and compiles filter.
sub cap_pkt
{
 my ($pcap,$err,$mask,$net,$filter2);
 my $snaplen = 4096;
 my $promisc = 1;
 my $timeout = 500;
 # get netmask for filter
 if ((Net::Pcap::lookupnet($interface, \$net, \$mask, \$err)) == -1 )
 {
   die ("Net::Pcap::lookupnet failed ($err) for device '$interface'\n");
 }
 # open pcap.
 $pcap = Net::Pcap::open_live($interface, $snaplen, $promisc, $timeout, \$err);
 if (!($pcap))
 {
   die ("can't create packet fd ($err) on  '$interface'\n");
 }
 else
 {
   print "dumping on '$interface'\n";
 }
 # make filter struct
 if (Net::Pcap::compile($pcap, \$filter2, $filter, 1, $mask) != '0')
 {
   die ("broken filter ($filter)\n");
 }
 # apply
 Net::Pcap::setfilter($pcap, $filter2);
 return $pcap;
}

# Pcap callback function. Gets called by Net::Pcap::loop
sub proc_pkt
{
 my($user_data, $hdr, $pkt) = @_;
 # Extract packet information.
 my ($user,$msg);
 my $eth = NetPacket::Ethernet->decode($pkt);
 my $ip = NetPacket::IP->decode($eth->{data});
 my $tcp_obj = NetPacket::TCP->decode(ip_strip(eth_strip($pkt)));
 my $ofh = select LOG;
 my $dst = $ip->{dest_ip};
 my $src = $ip->{src_ip};
 my @data = split(/\n/,$tcp_obj->{data});
 # For each line of captured text, prepend the IP address to which the
 # text was sent, and write it to the logfile.
 # This makes it easier to see which text is part of
 # which conversation stream when looking at the log file.
 foreach(@data)
 {
   my $data = $_;
   $| = 1;
   print LOG "$src->$dst | $data\n";
   $| = 0;
   select $ofh;
 }
}

# Cleanup function. After an interrupt signal was captured, make sure we clean
# up after ourselves.
sub cleanup
{
 print "cleaning up...";
 Net::Pcap::close($pcap);
 close(LOG);
}

# Entry point -----------------------------------------------------------------

# output file
open (LOG,">>$logfile");
# daemonize
exit if (fork());
exit if (fork());
sleep 1 until getppid() == 1;
print "mailsniff $$ running...\n";
# Trap interrupts so we can cleanup before exiting.
$SIG{'INT'} = \&cleanup;
# Building up the pcap filter string.
# This section will build up the ip filter list based on the
# list of ip addresses defined in the @ip_addresses array.
my $ip_filters = "";
foreach my $ip(@ip_addresses)
{
 $ip_filters .= "(src host $ip) or (dst host $ip) or";
}
# Cut off the last 'or'.
chop($ip_filters);
chop($ip_filters);
# Now compose the rest of the filter.
$filter = "tcp and ( ($ip_filters) and (dst port $port) )";
# Start the pcap sniffing loop.
$pcap = &cap_pkt;
if (!($pcap))
{
 die ("cant capture\n");
}
Net::Pcap::loop($pcap, -1, \&proc_pkt, 0);