#! /usr/bin/perl -w

# vim:syntax=perl

use strict;
use lib '/usr/share/perl5';
use Lire::DlfSchema;
use Lire::Time;
use Lire::Program qw( :msg :dlf );
use Lire::Config;

init_dlf_converter( "dns" );

my $schema  = eval { Lire::DlfSchema::load_schema( "dns" ) };
lr_err( "failed to load dns schema: $@" ) if $@;
my $dlf_maker	=
  $schema->make_hashref2asciidlf_func( qw/time requesting_host request
					  type resolver/ );

# If the user is running tinydns then they should (had to)
# have installed the deamontools and ucspi packages.  Those
# packages provide the tai64nlocal binary that is used to
# convert djb's timestamps to human readable form.

my $tai64nlocal = Lire::Config->get('tai64nlocal_name');

# tinydns defines these types only
my %dns_type =
  (
   1 => "a",
   2 => "ns",
   5 => "cname",
   6 => "soa",
   12 => "ptr",
   13 => "hinfo",
   15 => "mx",
   16 => "txt",
   17 => "rp",
   24 => "sig",
   25 => "key",
   28 => "aaaa",
   38 => "a6",
   252 => "axfr",
   255 => "any",
  );

sub parse_query {
    my ( $line ) = @_;

    my %dlf = ();

    my ( $tai64, $requestee, $auth, $type, $requested );
    # @400000000000000000000000 00000000:0000:0000 + 001c slashdot.org
    ( $tai64,
      $requestee,
      $auth,
      $type,
      $requested,
    ) = $line =~ m!^
                  (@[0-9a-f]+)\s
                  ([0-9a-f:]+)\s
                  (\+|\-)\s
                  ([0-9a-f]{4})\s
                  (.*)\s*?
		  $!x
		    or die "tinydns lexer failed\n";

    # FIXME check for errors here
    my $tai = `echo $tai64 | $tai64nlocal`;

    my ( $year, $month, $day, $time );
    ( $year,
      $month,
      $day,
      $time,
    ) = $tai =~ m!^
                 (\d{4})\-
                 (\d{2})\-
                 (\d{2})\s
                 ([\d+:]+)
                 \.\d+
                 $!x
		   or die "tai64nlocal lexer failed\n";

    $dlf{time} = date2cal( $year, $month, $day, $time );
    $dlf{resolver} = $auth eq '+' ? 'nonrec' : 'recurs';

    die "dns type \'$type\' is not defined by tinydns\n"
      unless defined $dns_type{hex($type)};
    $dlf{type} = $dns_type{hex($type)};

    my $ip = (split(/:/, $requestee))[0];
    $dlf{requesting_host} = join(".", unpack("C*", pack("H8", $ip)));
    $dlf{request} = $requested;
    $dlf_maker->( \%dlf );
}

unless ( defined $tai64nlocal) {
  lr_err( qq{tai64nlocal binary does not exist on host system, skipping} );
}

my $lines	= 0;
my $dlflines	= 0;
my $errorlines	= 0;
while (<>) {
    chomp;
    $lines++;

    next unless ($_ =~ m!^@!); # every valid line begins with an @ symbol for the TAI

    eval {
	my $dlf = parse_query( $_ );
	print join( " ", @$dlf), "\n";
	$dlflines++;
    };
    if ( $@ ) {
	lr_warn( $@ );
	lr_notice( qq{cannot convert line $. "$_" to dns dlf, skipping} );
	$errorlines++;
    }
}

end_dlf_converter( $lines, $dlflines, $errorlines );

__END__

=pod 

=head1 NAME

tinydns2dlf - convert tinydns logs, as created by multilog, to dlf

=head1 SYNOPSIS

B<tinydns2dlf>

=head1 DESCRIPTION

This script converts each line in a tinydns(1) query log, with timestamps as
created by multilog(1) to a dns dlf record.  tinydns is part of djbdns.
multilog is part of daemontools.

Logging data is printed automatically by tinydns.  A typical line in a log file
would look like this:

 @400000003e67eeb414752ccc 7f000001:eb9f:80bd + 0001 www.slashdot.org

=head1 EXAMPLES

To process a log as produced by tinydns:

 $ tai64nfraq < current | tinydns2dlf

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

 $ lr_log2report tinydns < /service/tinydns/log/main/current

=head1 SEE ALSO

bind9_query2dlf(1), qmail2dlf(1)

http://cr.yp.to/djbdns.html
http://cr.yp.to/daemontools.html

=head1 VERSION

$Id: tinydns2dlf.in,v 1.8 2006/07/23 13:16:33 vanbaal Exp $

=head1 COPYRIGHT

Copyright (C) 2003 Christopher Boumenot

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. 

=head1 AUTHOR

Christopher Boumenot

=cut

# Local Variables:
# mode: cperl
# End:
