#! /usr/bin/perl -w

# vim:syntax=perl

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

my $ltime   = [localtime];
my $schema  = eval { Lire::DlfSchema::load_schema( "dns" ) };
lr_err( "failed to load dns schema: $@" ) if $@;
my $dlf_maker;

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

    my %dlf = ();

    # Feb 25 11:09:48.739 queries: info: client 10.0.0.3#1035: query: 
    #   3.example.com.nl IN A

    my ( $month, $day, $time, $resolver );
    ( $month,
      $day,
      $time,
      $dlf{requesting_host},
      $dlf{request},
      $dlf{type},
      $resolver
    ) = $line =~ m/^
		  (\w+)\s(\d+)\s([\d.:]+)\s # Type
		  (?:queries:\s)?
		  (?:info:\s)?
		  client\s
		  ([\d.]+)\#\d+:\s  # Client
		  query:\s
		  ([^ ]+)\s	    # Request
		  \w+\s		    # Protocol, e.g. IN
		  (\w+?)	    # Type, e.g. SOA, NS, AAAA, AXFR, TXT...
		  ([-+])?           # rec or not : as patched by Wytze
		  $/x
		    or die "bind9 lexer failed\n";

    $dlf{time} = syslog2cal( $month, $day, $time, $ltime );

    $dlf{resolver} = $resolver eq '+' ? 'recurs' : 'nonrec'
      if defined $resolver;

    unless ( $dlf_maker ) {
	my @fields = qw/time requesting_host request type/;
	if ( defined $resolver ) {
	    push @fields, "resolver";
	    lr_info( "patched Bind9 : resolver is available !" );
	} else {
	    lr_info( "standard Bind9 : resolver is not available" );
	}
	$dlf_maker = $schema->make_hashref2asciidlf_func( @fields );
    }

    $dlf_maker->( \%dlf );
}

init_dlf_converter( "dns" );

my $lines	= 0;
my $dlflines	= 0;
my $errorlines	= 0;

while (<>) {
    chomp;
    $lines++;

    next unless ($_ =~ m/ query: /);

    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

bind9-query2dlf - convert BIND9 querylogs to dlf

=head1 SYNOPSIS

B<bind9-query2dlf>

=head1 DESCRIPTION

bind9-query2dlf expects BIND 9 query log files on stdin.

If you have a

        channel query_logging {
                file "/var/log/named_querylog"
                versions 3 size 100M;
                print-time yes;                 // timestamp log entries
        };

in your named.conf, the produced logfiles are supported.  Optionally, you
could add

                print-category yes;             // print category name
                print-severity yes;             // print severity level

to this channel.  Query logs as produced by a patched BIND (see NOTES below)
are supported too.

=head1 EXAMPLE

With print-time, print-category and print-severity set:

 Feb 25 11:09:43.651 queries: info: client 10.0.0.3#1035:
  query: 3.example.com.nl IN A
 Feb 25 11:09:48.739 queries: info: client 10.0.0.3#1035:
  query: 3.example.com.nl IN A
 Feb 25 12:50:32.476 queries: info: client 10.0.0.3#1035:
  query: 21.example.com.co.uk IN A
 Feb 25 12:50:34.110 queries: info: client 10.0.0.3#1035:
  query: 22.example.com IN A
 Feb 25 12:50:34.525 lame-servers: info: lame server on
  '22.example.com' (in '23.example.com'?): 10.0.0.4#53
 Feb 25 12:50:34.715 queries: info: client 10.0.0.3#1035:
  query: 24.example.com IN A
 Feb 26 07:30:08.211 queries: info: client 10.0.0.1#1050:
  query: 1.0.0.10.in-addr.arpa IN PTR
 Feb 26 12:26:55.455 queries: info: client 10.0.0.1#1051:
  query: 28.example.com.nl IN MX
 Feb 04 04:02:00.932 general: info: loading configuration
  from '/etc/336.example.com'
 Feb 18 04:02:01.023 security: warning: zone
  '337.example.com.nl' allows updates by IP address, which
  is insecure
 Feb 18 04:02:01.049 config: warning: option 'use-id-pool'
  is obsolete
 Feb 18 04:02:01.049 config: warning: option 'check-names'
  is not implemented
 Feb 18 04:02:01.049 config: warning: option
  'statistics-interval' is not yet implemented
 Feb 18 04:02:01.049 network: info: no IPv6 interfaces found
 Feb 04 16:47:18.289 security: info: client 10.0.0.201#137:
  query denied
 Feb 20 07:26:53.731 general: info: running
 Feb 13 08:01:56.138 general: info: shutting down
 Feb 13 08:01:56.140 network: info: no longer listening on
  10.0.0.3#53
 Feb 14 08:02:13.983 general: info: refresh_callback: zone
  384.example.com/IN: failure for 10.0.0.204#53: timed out

With only print-time set:

 Aug 27 04:07:13.361 client 127.0.0.1#3123: query: foo.com IN ANY
 Aug 27 04:07:13.438 client 127.0.0.1#3123: query: fu.bar.nl IN AAAA
 Aug 27 04:07:13.443 client 127.0.0.1#3123: query: fu.bar.nl IN A


=head1 NOTES

Bind9 doesn't log wether the query was recursive, therefore the last
dlf field (DLF_RESOLVER) is a '-'.  However, applying this patch by Wytze
van der Raay:

 # patch bin/named/query.c to log recursive/non-recursive query indication
 SRC=bin/named/query.c
 if [ -f ${SRC}.org ]
 then
         echo "Patched ${SRC} already in place"
 else
         echo "Patch ${SRC} for recursive/non-recursive query indication"
         cp -p ${SRC} ${SRC}.org
         patch -p0 ${SRC} <<\!
 --- bin/named/query.c.org       Mon Sep 24 22:57:48 2001
 +++ bin/named/query.c   Tue Sep 25 09:55:21 2001
 @@ -3272,7 +3272,8 @@
         dns_rdatatype_format(rdataset->type, typename, sizeof(typename));

         ns_client_log(client, NS_LOGCATEGORY_QUERIES, NS_LOGMODULE_QUERY,
 -                     level, "query: %s %s %s", namebuf, classname, typename);
 +                     level, "query: %s %s %s%s", namebuf, classname, typename,
 +                     WANTRECURSION(client) ? "+" : "-");
  }

  void
 !
 fi

will yield loglines like

 Nov 11 12:06:42.829 queries: info: client 10.0.0.1#3664:
   query: 6.example.com.nl IN A+

A '+' indicates a recursive query, - indicates a non-recursive query,
the lack of + or - indicates a non-patched bind9.  See Wytze's message
of Fri, 28 Dec 2001 16:56:30 +0100 on bind9-workers@isc.org , archived
at http://www.mail-archive.com/bind9-workers@isc.org/msg00501.html .

This type of logfiles is recognised by the script.

=head1 THANKS

Wytze van der Raay, for supplying the BIND 9 query log patch.

=head1 SEE ALSO

bind8-query2dlf(1), The bind9 online documentation, as distributed with BIND
(but unfortunately not online at http://isc.org/ , you might like
http://doc.mdcc.cx/doc/bind/html/logging.html though)

=head1 VERSION

$Id: bind9-query2dlf.in,v 1.23 2002/01/11 14:58:51 vanbaal Exp $

=head1 COPYRIGHT

Copyright (C) 2001 Joost Bekkers <joost@jodocus.org>,
Copyright (C) 2000, 2001 Stichting LogReport Foundation LogReport@LogReport.org

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 or write to the Free Software 
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.

=head1 AUTHOR

Joost Bekkers <joost@jodocus.org>, based on Edwin Groothuis and Joost
van Baal's work, now maintained by the LogReport team.

=cut

# Local Variables:
# mode: cperl
# End:

