package Lire::Firewall::FortigateDlfConverter;

use strict;

use Lire::DlfConverter;
use Lire::Firewall qw/firewall_number2names/;
use Lire::Syslog;
use Carp;

use base qw/Lire::DlfConverter/;

sub new {
    my $proto = shift;
    bless {}, (ref $proto || $proto);
}

sub name { 'Fortigate' }
sub title { 'Fortigate firewall log' }
sub description { '<para>Fortigate firewall log</para>' }
sub schemas { qw/firewall/ }

sub handle_log_lines { 1 }

sub init_dlf_converter {
    my ($self, $process) = @_;

    $self->{'parser'} = new Lire::Syslog;

    # wether or not to try to resolve IP addresses to hostnames
    ## $self->{'resolve'} = Lire::Config->get( 'resolve_ips' );
    $self->{'resolve'} = 0;
}
# Schema
# <lire:field name="time" type="timestamp" label="Timestamp">
#  <lire:field name="action" type="string" label="Action">
#  <lire:field name="protocol" type="string" label="Protocol">
#  <lire:field name="from_ip" type="ip" label="Source IP">
#  <lire:field name="from_port" type="port" label="Src Port">
#  <lire:field name="from_host" type="hostname" label="Source Host">
#  <lire:field name="rcv_intf" type="string" label="Recv Intf">
#  <lire:field name="rcv_hwaddr" type="string" label="Recv HW Address">
#  <lire:field name="to_ip" type="ip" label="Destination IP">
#  <lire:field name="to_port" type="port" label="Dst Port">
#  <lire:field name="to_host" type="hostname" label="Destination host">
#  <lire:field name="snt_intf" type="string" default="Send Intf">
#  <lire:field name="length" type="bytes" label="Size">
#  <lire:field name="rule" type="string" label="Rule">
#  <lire:field name="msg" type="string" label="Message">
#  <lire:field name="count" type="int" default="1" label="Count">

# Logs : Jan 10 10:26:08 192.168.40.52 date=2008-01-10 time=10:19:53 devname=FG1 device_id=FG200A3907550652 log_id=0021010001 type=traffic subtype=allowed pri=notice vd=root SN=2713769 duration=180 user=N/A group=N/A policyid=130 proto=17 service=123/udp app_type=N/A status=accept src=10.40.31.211 srcname=10.40.31.211 dst=10.40.8.33 dstname=10.40.8.33 src_int=VLAN_K_MODE dst_int=wan2 sent=96 rcvd=96 sent_pkt=1 rcvd_pkt=1 src_port=123 dst_port=123 vpn=N/A tran_ip=0.0.0.0 tran_port=0 dir_disp=org tran_disp=noop

my %ipt2dlf = (
               policyid => "rule",
               status => "action",
               src=> "from_ip",
               srcname => "from_host",
               dst => "to_ip",
               dstname => "to_host",
               src_int => "rcv_intf",
               dst_int => "snd_intf",
               src_port => "from_port",
               dst_port => "to_port",
               devname => "msg"
              );

my $denied_re = qr/deny|denied|drop|reject|unallowed/i;
my $permit_re = qr/accept|permit/i;

my %field_re = ();
foreach my $k ( keys %ipt2dlf ) {
    $field_re{$k} = qr/\b$k=(\S*)/;
}


sub process_log_line {
    my ($self, $process, $line) = @_;

    eval {
        my $log = $self->{'parser'}->parse($line);
        local $_ = $log->{'content'};

        # Skip non-Fortigate records
        #
        # The starts of the line is set by the user.
        #
        return $process->ignore_log_line($line)
          unless $log->{content} =~ /devname=\w* device_id=\w*/;

        return $process->ignore_log_line($line)
          unless $log->{content} =~ / type=traffic /;

        my %dlf = (
          time => $log->{timestamp},
          count => 1,
        );


        
        # Size is splitted between send and received size. Merge them
        my $send_size = $log->{content} =~ /\bsent=(\S*)/;
        my $rcv_size = $log->{content} =~ /\brcvd=(\S*)/;
        # Protocol 
        my ($service,$protoname) = $log->{content} =~ /\bservice=(\d+)\/(\S*)/;
        $dlf{protocol}=$protoname;
        $dlf{length} = int($send_size) + int($rcv_size) unless !defined($rcv_size) || !defined($rcv_size) ;
        while ( my ( $field, $re ) = each %field_re ) {
            my ( $value ) = $log->{content} =~ /$re/;
            ( $dlf{$ipt2dlf{$field}} ) = $value if defined $value;
        }
        # Convert the action 
        $dlf{action} = "denied" if $dlf{action} =~ /$denied_re/;
        $dlf{action} = "permitted" if $dlf{action} =~ /$permit_re/;
        # Fortigate will log the following on all packet
        die "Fortigate lexer failed\n"
          unless exists $dlf{from_ip} &&
                 exists $dlf{to_ip} &&
                 exists $dlf{length} &&
                 exists $dlf{protocol};

        firewall_number2names( \%dlf );

        # fill in $dlf{from_host} and $dlf{to_host}
        ## $self->{'resolve'} && firewall_resolve ( \%dlf );

        $process->write_dlf('firewall', \%dlf);
    };

    if($@) {
        $process->error($line, $@);
    }
}


sub finish_conversion {
    delete $_[0]->{'parser'};
}

1; # nag nag.

__END__

=pod

=head1 NAME

FortigateDlfConverter - convert FortiGate syslog logs to firewall DLF

=head1 DESCRIPTION

B<FortigateDlfConverter> converts FortiGate 3.x packet log into firewall DLF
format.  FortiGate is a security solution by Fortinet, a provider of Unified
Threat Management security systems, founded in 2000 and headquartered in
Sunnyvale, California,

=head1 LIMITATIONS

To be discovered

=head1 EXAMPLES

FortigateDlfConvertor will be rarely used on its own, but is more likely
called by lr_log2report:

 $ lr_log2report Fortigate < /var/log/Fortigate.log > report

=head1 SEE ALSO

FortiGate user manual. Syslog output from Fortigate

=head1 AUTHORS

Jean Benoit Marzio <jbmarzio@netstaff.fr>

=head1 VERSION

$Id: FortigateDlfConverter.pm,v 1.3 2009/01/13 11:26:35 wraay Exp $

=head1 COPYRIGHT

Copyright (C) 2001, 2002, 2003, 2004 Stichting LogReport Foundation
LogReport@LogReport.org

Copyright (C) 2008 Jean Benoit Marzio

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 (see COPYING); if not, check with
http://www.gnu.org/copyleft/gpl.html.

=cut

# Local Variables:
# mode: cperl
# End:
