#!/p/perl/perl -w

package Filter::LibpcapFilter;

use strict;

use ExtUtils::testlib;
use Net::Pcap; 	        # perl interface to libpcap

use AAFID::Filter;
use AAFID::Entity;
use AAFID::Common;
use vars qw(
	%PARAMETERS
	@ISA
	$ps
	    );


%PARAMETERS=(
	Description     => 'This is a filter that interfaces with Libpcap',
	Period		=> 3,
	    );

@ISA=qw(AAFID::Filter);

=pod

This filter interfaces with Libpcap so AAFID agents now have an interface
to arbitrary network packets. To find out how to use it, see the sample
agents: IllegalIPPackets, SYNFloodAsync, PortScanDetect and Land.

NOTE: to be able to install & use this filter, you need to first install
Libpcap and the Net::pcap Perl interface to Libpcap.

=cut

sub Init_log {
    my $self = shift;
    $self->Log("debug", "Just into Init_log.\n");

#    Comm::Reactor::add_event(time()+$self->getParameter('Period'),
#			     sub {
#				my $msg = "This is a test";
#				$self->processLine(undef, $msg);
#			     } );

    $self->Log("debug", "Just leaving Init_log.\n");
    return($self);
}

=head1 command_SETPATTERN

Overrides the one in the base class. This is invoked after communication
between the agent & (child) filter has been established, and the agent
wants to filter to start collecting data as per the expression it specified

=cut

sub command_SETPATTERN {
    # Note: this is only invoked AFTER the child filter has been forked.
    # So we can go into the libpcap infinite Pcap::loop() in here.

    my $self = shift;
    my $msg = shift;
    my %params = @_;

    $self->Log("debug", "Just into command_SETPATTERN().\n");

    # Now process %params{"Expr"} etc.

    my $expr = $params{'Expr'};
    my $interfaceName = $params{'InterfaceName'};
    my $numPackets = $params{'NumPackets'};
    my $err = undef; # For error messages 

    if(!defined($interfaceName)) {
	# Pick one using lookupdev()

	$interfaceName = Net::Pcap::lookupdev(\$err);
    }

    if($err) {
	$self->Log("errors", "$err\n");

	# We really should give up at this point & exit
	# die;
    }
    $self->Log("debug", $interfaceName." selected for packet capture!\n");

    #Get the netmask (& network number) for that interface
    my($net, $mask);
    if(Net::Pcap::lookupnet($interfaceName,\$net,\$mask,\$err) != 0) {
	$self->Log("errors", "$err\n");
        # die;
    }

    # Get a pcap descriptor for that interface
    my $pcapDescriptor = Net::Pcap::open_live($interfaceName, 1024, 1, 0, \$err);
    if(!defined($pcapDescriptor)) {
	$self->Log("errors", "$err\n");
	# die;
    }

    # Set the BPF packet filtering program for that descriptor

    my($bpfProg);
    if(Net::Pcap::compile($pcapDescriptor, \$bpfProg,
			  $expr, 1, $mask) != 0) {

	# The 1 means we want to optimize the bpfProg

	$self->Log("errors", Net::Pcap::geterr($pcapDescriptor));
	# die;
    }

    if(Net::Pcap::setfilter($pcapDescriptor, $bpfProg) == -1) {
	$self->Log("errors", Net::Pcap::geterr($pcapDescriptor));
	# die;
    }

    Comm::Reactor::add_event(time()+$self->getParameter('Period'),
			     sub {

    				# Simply invoke Pcap's loop() function

    				$self->Log("debug", "In GetTraffic()...\n");

    				if(Net::Pcap::loop($pcapDescriptor,$numPackets,
                       		    sub {
                            		my ($usrDat, $hdr, $pkt) = @_;

                            	        # Send this entire packet to the agent

                            		# $self->processLine(undef, "Got a packet");
                            		$self->processLine(undef, $pkt);
					Comm::Reactor::flush();
                       	    	    }, 0) == -1) {

        			    $self->Log("errors", Net::Pcap::geterr($pcapDescriptor));
        			    # die;
    				}
				else {
				    $self->Log("debug", "Returned from Pcap::loop()\n");
				}
			    });


    # Invoke method in Super class

    $self->SUPER::command_SETPATTERN($msg, %params);
    $self->Log("debug", "Just leaving command_SETPATTERN().\n");

    return(undef);
}

=head1 _handleNewConnection

Again, we override the base class method so we can fork() a new
child & use that to handle the new agent that has just connected
to us.

=cut

sub _handleNewConnection {

    my $self = shift;
    my $serverSock = shift;
    my $newSock = shift;

    $self->Log("debug", "Just into _handleNewConnection().\n");

    my $pid;
    if(!defined($pid = fork)) {
	$self->Log("errors", "Could not fork!\n");
	return;
    }

    if($pid) {
	# parent

	$newSock->close;
    }
    else {
	# child
        $self->SUPER::_handleNewConnection($serverSock, $newSock);
	$self->stopServerSocket;
	Comm::Reactor::reset(); # To flush all events
    }


    $self->Log("debug", "Just leaving _handleNewConnection(), pid: $pid.\n");
    return undef;
}

sub makefield {
    # Use a very "generic" one so we don't mess up the packet
    # contents

    my $self = shift;
    my $line = shift;

    return( { Field1 => $line } );
}

sub makeline {
    # "Invert" the generic makefield from above

    my $self = shift;
    my $data = shift;
    my %line = %$data; # We know it is a hash
    my $retVal;

    $retVal = $line{'Field1'};

    $self->Log("debug", "makeline returning: $retVal\n");

    return($retVal);
}

# The following is no longer used, but is left in here because it may
# be useful in future
#sub Init_log_old {
#    my ($self, @dev, $err, $pcap_t);
#
#    $self = shift;
#
#    # Get the output of "ifconfig -a" line by line to find out
#    # what interfaces exist
#
#    my($fh) = IO::File->new("ifconfig -a |");
#    my($lineRead, @ifArray);
#
#    while (defined($lineRead = <$fh>)) {
#        if( ($lineRead =~ /^(\w*):.*/)  &&
#	    !($1 =~ /lo.*/)) {
#
#	    # Add this to the temp array variable
#	    push @ifArray, $1;
#        }
#    }
#    $self->setParameter(InterfaceNames => \@ifArray);
#
#    # Debugging output starts here
#    my($iA) = $self->getParameter('InterfaceNames');
#    foreach (@$iA) {
#	print "IL: $_\n";
#    }
#    # End of debugging output
#}


_EndOfEntity;
