Summerschool Aachen 2004/Fingerprinting Tool

From C4 Wiki
< Summerschool Aachen 2004
Revision as of 17:44, 5 October 2004 by Mario Manno (talk | contribs) (extraced long code)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
#!/usr/bin/perl -w

use strict;

# ---------------------------------------------------------------------------------
package Net::DNS::PacketExt;

   use strict;
   use vars qw(@ISA);

   @ISA = qw(Net::DNS::Packet);

   sub new
   {
      my $proto = shift;
      my $class = ref $proto || $proto;
      my $data  = shift;

      my $self = Net::DNS::Packet->new($data);
      $self->{"header"} = Net::DNS::HeaderExt->new($data);

      bless $self, $class;
      return $self;
   }

1;

# ---------------------------------------------------------------------------------

package Net::DNS::HeaderExt;

   use strict;
   use vars qw(@ISA);

   @ISA = qw(Net::DNS::Header);

   sub new
   {
      my $proto = shift;
      my $class = ref $proto || $proto;
      my $data  = shift;

      my $self = Net::DNS::Header->new($data);

      my @a = unpack("n C2 n4", $$data);

      $self->{"z0"} = ($a[2] >> 6) & 0x1;
      $self->{"z1"} = ($a[2] >> 5) & 0x1;
      $self->{"z2"} = ($a[2] >> 4) & 0x1;

      bless $self, $class;
      return $self;
   }

1;

# ---------------------------------------------------------------------------------

package MapTool;

   use strict;
   use Carp;

   sub new
   {
      my $proto = shift;
      my $class = ref $proto || $proto;
      my $targethost = $_[0] or croak "need target host";

      my %self;
      $self{targethost} = $targethost;

      $self{smtp} = MapTool::Smtp->new();
      $self{ftp}  = MapTool::Ftp->new();
      $self{dns}  = MapTool::Dns->new();

      bless \%self, $class;
      return \%self;
   }

   sub probe
   {
      my $self = shift;

      $self->{dns}->probe($self->{targethost});
   }

   sub evaluate
   {
      my $self = shift;

      $self->{dns}->evaluate();
   }

   sub printresults
   {
      my $self = shift;

      print $self->{dns}->printresults();
   }

1;

# ---------------------------------------------------------------------------------

package MapTool::Smtp;

   use strict;
   use vars qw/@ISA/;
   use Carp;
   use Net::DNS::Resolver;

   @ISA = ("MapTool");

   my $mydomain   = 'ccc.de';
   my $validsrc   = 'cpunkt@ccc.de';

   sub new
   {
      my $proto = shift;
      my $class = ref($proto) || $proto;

      my $fpres   = "smtp_fingerprints";
      my $fpcmd   = "smtp_tests";


      my $self = {};

      bless $self, $class;
      $self->mkdb($fpcmd, $fpres);

      return $self;
   }

   sub mkdb
   {
      my $self = shift;

      $self->{commands} = [];
      $self->{fingerprints}  = {};

      open TESTS,   "$_[0]" or croak "can't open $_[0]";
      open RESULTS, "$_[1]" or croak "can't open $_[1]";

      while (my $tline = <TESTS>) {
         next if ($tline =~ /^#/);
         chomp $tline;

         my $invalidsrc = $self->generate_source_address();

         $tline =~ s/\$MY_DOMAIN/${mydomain}/g;
         $tline =~ s/\$VALID_SOURCE/${validsrc}/g;
         $tline =~ s/\$INVALID_SOURCE/${invalidsrc}/g;

         my @cmd_sequence = split /->/, $tline;

         push @{$self->{commands}}, \@cmd_sequence;
      }

      close TESTS;

      while (my $rline = <RESULTS>) {
         next if ($rline =~ /^#/);
         chomp $rline;

         my @res_sequence = split /:/, $rline;
         my $software_version = shift @res_sequence;

         $self->{fingerprints}->{$software_version} = \@res_sequence;
      }

      close RESULTS;
   }

   sub generate_source_address
   {
      my ($i, $ok, $query);
      my ($fake_domain, $fake_user) = ('', '');
      my $res = Net::DNS::Resolver->new;
      my @VALID_CHARS = ( 'A' .. 'Z', 'a' .. 'z', '0' .. '9', '_');

      while (not $ok) {
         for $i (1 .. 25) {
            $fake_domain .= $VALID_CHARS[int rand(@VALID_CHARS)];
         }

         $fake_domain .= '.com';
         $ok = 1;

         $query = $res->query($fake_domain, 'SOA');

         $ok = 1 if (not $query);
      }

      for $i (1 .. 10) {
         $fake_user .= $VALID_CHARS[int rand(@VALID_CHARS)];
      }

      return "$fake_user\@$fake_domain";
   }

1;

# ---------------------------------------------------------------------------------

package MapTool::Ftp;

   use strict;
   use vars qw/@ISA/;
   use Carp;

   @ISA = ("MapTool");

   sub new
   {
      my $proto = shift;
      my $class = ref($proto) || $proto;

      my $fpres   = "ftp_fingerprints";
      my $fpcmd   = "ftp_tests";

      my $self = {};

      bless $self, $class;
      $self->mkdb($fpcmd, $fpres);

      return $self;
   }

   sub mkdb
   {
      my $self = shift;

      $self->{commands} = [];
      $self->{fingerprints}  = {};

      open TESTS,   "$_[0]" or croak "can't open $_[0]";
      open RESULTS, "$_[1]" or croak "can't open $_[1]";

      my $isnulled = 0;

      while (my $tline = <TESTS>) {
         next if ($tline =~ /^#/);
         chomp $tline;

         if ($tline =~ /^#if 0/) {
            $isnulled = 1;
         } elsif ($isnulled && $tline =~ /^#endif/) {
            $isnulled = 0;
         }

         next if ($isnulled || $tline !~ /^\s*"([^"]+)"/);

         push @{$self->{commands}}, $1;
      }

      close TESTS;

      my $inblock = 0;
      my $softwareversion;

      while (my $rline = <RESULTS>) {
         chomp $rline;

         if ($inblock && $rline =~ /^\s*\}/) {
            $inblock = 0;
            $softwareversion = '';
            next;
         } elsif ($inblock) {
            my @checksums = split /,/, $rline;
            push @{$self->{fingerprints}->{$softwareversion}}, \@checksums;
            next;
         }

         if ($rline =~ /^\s*0UL,\s*"([^"]+)"/) {
            $softwareversion = $1;
            $inblock = 1
         }
      }

      close RESULTS;
   }

1;

# ---------------------------------------------------------------------------------

package MapTool::Dns;

   use strict;
   use vars qw/@ISA/;
   use Carp;
   use Sys::Hostname;
   use Socket;

   @ISA = ("MapTool");

   sub new
   {
      my $proto = shift;
      my $class = ref($proto) || $proto;

      my $self = {};

      bless $self, $class;

      $self->mkdb();
      return $self;
   }

   sub mkdb
   {
      my $self = shift;

      $self->{commands} = [];
      $self->{fingerprints}  = {};

      $self->{commands} = [
         "\0\0\10\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\3",
         "\0\0\0\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\77",
         "\0\0\0\0\0\2\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1\0\0\20\0\1",
         "\0\0\40\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\50\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\0\0\0\0\0\0\0\0\0",
         "\0\0\0\0\0\1\0\0\0\0\0\0\6",
         "\0\0\0\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0",
         "\0\0\2\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\0\0\1\0\0\0\0\0\0\7Authors\4BIND\0\0\20\0\3",
         "\0\0\4\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\17\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\20\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\40\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\100\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\20\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\30\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\60\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\0\0\1\0\0\0\0\0\0\4test\17network-surveys\2cr\2yp\2to\0\0\20\0\1",
         "\0\0\0\0\0\1\0\0\0\0\0\0\25erre-con-erre-cigarro\7maradns\3org\0\0\20\0\1",
         "\0\0\0\0\0\1\0\0\0\0\0\0\7version\6server\0\0\20\0\3",
         "\0\0\0\0\0\1\0\0\0\0\0\0\7version\4bind\0\0\20\0\3"
      ];

      open FPR, "dns_fingerprints" || croak "couldn't open dns_fingerprints";

      my $oldsep = $/; $/ = undef;
      my $fingerprints = <FPR>;
      $/ = $oldsep;

      close FPR;
      my $VAR1;

      eval $fingerprints;
      $self->{fingerprints} = $VAR1;
   }

   sub probe
   {
      my $self = shift;
      my $targethost = $_[0];

      my $iaddr = inet_aton("0.0.0.0");
      my $proto = getprotobyname('udp');
      my $port  = getservbyname('domain', 'udp');
      my $paddr = sockaddr_in(0, $iaddr);

      socket(SOCK, PF_INET, SOCK_DGRAM, $proto)  || croak "socket: $!";
      bind(SOCK, $paddr) || croak "bind: $!";

      $| = 1;

      my $hisiaddr = inet_aton($targethost) || croak "unknown host";
      my $hispaddr = sockaddr_in($port, $hisiaddr);

      my $count = 0;

      foreach my $request (@{$self->{commands}}) {
         $count++;

         defined(send(SOCK, $request, 0, $hispaddr)) || croak "send ".$targethost.": $!";

         my $rin = '';
         vec($rin, fileno(SOCK), 1) = 1;

         my $rout;

         if (!select($rout = $rin, undef, undef, 5.0)) {
            $count > 2 && do { $count = 0; $self->pushresult("timeout"); next; };
            redo;
         }

         my $response;

         ($hispaddr = recv(SOCK, $response, 1000, 0)) || croak "recv: $!";

         $self->pushresult($response);

         $count = 0;
      }
   }

   sub pushresult
   {
      my $self = shift;
      my $result = $_[0];

      push @{$self->{answers}}, $result;
   }

   sub evaluate
   {
      my $self = shift;

      if (scalar @{$self->{answers}} != scalar @{$self->{commands}}) {
         croak "number of answers doesn't match number of commands";
      }

      $self->{results} = {};
      $self->{results}->{probecount} = scalar @{$self->{answers}};

      foreach my $swversion (keys %{$self->{fingerprints}}) {
         for (my $j = 0; $j < scalar @{$self->{fingerprints}->{$swversion}}; $j++) {
            my $matchcount;

            for (my $i = 0; $i < scalar @{$self->{answers}}; $i++) {
               $matchcount += $self->compare($self->{answers}->[$i],
                                             $self->{fingerprints}->{$swversion}->[$j]->[$i]);
            }

            push @{$self->{results}->{$matchcount}}, $swversion;
         }
      }

      $self->{results}->{signature} = $self->getsig;
   }

   my $cmpmatrix =
      [ ['Z0','Z1','Z2','Q2','0','1','2','3','4','5','TC','RD','AA','q','X','D'],
        ['$header->z0','$header->z1','$header->z2','$#queries == 1','$header->rcode eq "NOERROR"',
         '$header->rcode eq "FORMERR"','$header->rcode eq "SERVFAIL"','$header->rcode eq "NXDOMAIN"',
         '$header->rcode eq "NOTIMP"','$header->rcode eq "REFUSED"','$header->tc','$header->rd',
         '$header->aa','$#queries == -1','1','$#answers > -1'] ];

   sub compare
   {
      my $self = shift;
      my ($response, $fingerprint) = @_;

      $fingerprint =~ s/\s+//g;

      if ($fingerprint =~ /t/) {
         return($response eq 'timeout');
      } elsif ($response eq 'timeout') {
         return 0;
      }

      my @alternatives = split /,/, $fingerprint;
      my $packet = Net::DNS::PacketExt->new(\$response);

      $packet || croak("DNS::Packet refuses the response...");

      my $header  = $packet->header;
      my @queries = $packet->question;
      my @answers = $packet->answer;

      my $match = 0;

      foreach my $alternative (@alternatives) {
         for (my $testcase = 0; $testcase < scalar @{$cmpmatrix->[0]}; $testcase++) {
            my ($pattern, $value) = ($cmpmatrix->[0]->[$testcase], $cmpmatrix->[1]->[$testcase]);

            if ($alternative =~ s/${pattern}//) {
               $match = (eval $value);
               next unless ($match);
            }
         }
      }

      return $match;
   }

   sub getsig
   {
      my $self = shift;

      my $fingerprint;

      for (my $i = 0; $i < scalar @{$self->{answers}}; $i++) {
         if ($self->{answers}->[$i] eq 'timeout') {
            $fingerprint .= 't --';
            next;
         }
      
         my $packet = Net::DNS::PacketExt->new(\$self->{answers}->[$i]);

         $packet || croak("DNS::Packet refuses the response...");

         my $header  = $packet->header;
         my @queries = $packet->question;
         my @answers = $packet->answer;

         for (my $testcase = 0; $testcase < scalar @{$cmpmatrix->[1]}; $testcase++) {
            my ($condition, $output) = ($cmpmatrix->[1]->[$testcase], $cmpmatrix->[0]->[$testcase]);

            next if ($condition eq '1');

            if (eval $condition) {
               $fingerprint .= "$output ";
            }
         }

         $fingerprint .= "-- ";
      }

      return $fingerprint;
   }

   sub printresults
   {
      my $self = shift;
      my $rv;

      for (my $score = $self->{results}->{probecount}; $score >= 0; $score--) {
         next unless (defined $self->{results}->{$score});

         $rv .= "matched with score ".int ($score/(scalar @{$self->{answers}})*100).":\n";

         my %uniq;
         map { next if $uniq{$_}; $uniq{$_}++; $rv .= "\t$_\n"; } @{$self->{results}->{$score}};
      }

      $rv .= "\nserver signature:\n\t".$self->{results}->{signature}."\n";

      return $rv;
   }

1;

# ---------------------------------------------------------------------------------

use strict;

if ($#ARGV != 0) {
   print "usage: $0 <hostname>\n\n";
   exit 1;
}

my $targethost = $ARGV[0];

my $map = MapTool->new($targethost);

$map->probe();
$map->evaluate();
$map->printresults();

This is the file "dns_fingerprints", I have built from djb's HTML page. I've also added some new signatures...

$VAR1 = {
          'tinydns 1.04 with local root' => [ [ '4AA','t','t','t','4AA','4AA','t','t','t','4AA','t','4AA','0AA','0AA','0AA','0AA','4AA','4AA','4AA','0AA','0AA','t','t' ] ],
          'tinydns 1.05 with local root' => [ [ '4AA','1RD','1RD','t','4AA','4AA','t','t','t','4AA','1RD','4AA','0AA','0AA','0AA','0AA','4AA','4AA','4AA','0AA','0AA','1RD','1RD' ] ],
          'tinydns 1.05 with wildcard root' => [ [ '4AA','1RD','1RD','t','4AA','4AA','t','t','t','4AA','1RD','4AA','3AA','3AA','3AA','3AA','4AA','4AA','4AA','3AA','3AA','1RD','1RD' ] ],
          'tinydns 1.05 variant' => [ [ '4AA','1RD','1RD','t','4AA','4AA','t','t','t','4AA','1RD','4AA','5','5','5','5','4AA','4AA','4AA','5','5','1RD','1RD' ] ],
          'tinydns 1.04 with wildcard root' => [ [ '4AA','t','t','t','4AA','4AA','t','t','t','4AA','t','4AA','3AA','3AA','3AA','3AA','4AA','4AA','4AA','3AA','3AA','t','t' ] ],
          'tinydns 1.05' => [ [ '4AA','1RD','1RD','t','4AA','4AA','t','t','t','4AA','1RD','4AA','t','t','t','t','4AA','4AA','4AA','t','t','1RD','1RD' ] ],
          'tinydns 1.05 with big root' => [ [ '4AA','1RD','1RD','t','4AA','4AA','t','t','t','4AA','1RD','4AA','0TC','0TC','0TC','0TC','4AA','4AA','4AA','0TC','0TC','1RD','1RD' ] ],
          'tinydns 1.04' => [ [ '4AA','t','t','t','4AA','4AA','t','t','t','4AA','t','4AA','t','t','t','t','4AA','4AA','4AA','t','t','t','t' ] ],
          'tinydns 1.04 with root NS' => [ [ '4AA','t','t','t','4AA','4AA','t','t','t','4AA','t','4AA','0','0','0','0','4AA','4AA','4AA','0','0','t','t' ] ],
          'tinydns 1.05 with root NS' => [ [ '4AA','1RD','1RD','t','4AA','4AA','t','t','t','4AA','1RD','4AA','0','0','0','0','4AA','4AA','4AA','0','0,0TC','1RD','1RD' ] ],
          'BIND 4.9.8-' => [ [ '1q','0,2','0,2','1q','4q,5,2','4q','1q','1q','1q','0TC','0,2','0,2','0,2','0Z0,2Z0','0Z1,2Z1','0Z2,2Z2','4q','4q','4q','0,2','0,2','0,2','0,2,0AAD' ] ],
          'BIND 9.1' => [ [ '4q','5','5','1q','5','1q','1q','1q','1q','0','0AA','0','15','0','0','0','4q','4q','4q','0','0','5','0AAD,2,5' ],
                          [ '4q','5','5','1q','5','1q','1q','1q','1q','2','0AA','2','2','2','2','2','4q','4q','4q','2','2','5','0AAD,2,5' ],
                          [ '4q','5','5','1q','5','1q','1q','1q','1q','5','0AA','5','5','5','5','5','4q','4q','4q','5','5','5','0AAD,2,5' ] ],
          'BIND 9.2' => [ [ '4q','5','5','1q','2','1q','1q','1q','1q','0AA','0AA,5','0AA','0AA','0AA','0AA','0AA','4q','4q','4q','0AA','0AA','5','0AAD,2,5' ],
                          [ '4q','5','5','1q','2','1q','1q','1q','1q','3AA','0AA','3AA','3AA','3AA','3AA','3AA','4q','4q','4q','3AA','3AA','5','0AAD,2,5' ],
                          [ '4q','5','5','1q','2','1q','1q','1q','1q','0,2,3,5','0AA,2,5','0,2,3,5','0,5,15','0,2,3,5','0,2,3,5','0,2,3,5','4q','4q','4q','0,2,3,5','0,2,3AA,5','5','0AAD,2,5' ] ],
          'BIND 8.2-' => [ [ '1q','0,2,5','0,2,5','1q','2,4,5','1q','1q','1q','1q','0TC,0TCZ1,3TC,5TC','0,0AA,2,3,3AA,5','0,2,3,3AA,5','0,2,3,3AA,5','0Z0,2Z0,3AAZ0,3Z0,5Z0','0Z1,2Z1,3AAZ1,3Z1,5Z1',
                             '0Z2,2Z2,3AAZ2,3Z2,5Z2','4q','4q','4q','0,2,3,3AA,5','0,0AA,2,3,3AA,5','0,2,3,5','0AAD,0,3,5' ],
                           [ '1q','0,2,5','0,2,5','1q','2,4,5','1q','1q','1q','1q','0TC,0TCZ1,3TC,5TC','0,0AA,2,3,3AA,5','0,0AA,2,3,3AA,5','0,0AA,2,3,3AA,5','0Z0,0AAZ0,2Z0,3AAZ0,3Z0,5Z0','0,0AA,2,3,3AA',
                             '0Z2,0AAZ2,2Z2,3AAZ2,3Z2,5Z2','4q','4q','4q','0,0AA,2,3,3AA,5','0,0AA,2,3,3AA,5','0,2,3,5','0AAD,0,3,5' ] ],
          'BIND 9.3' => [ [ '4q','2','5','1q','2,5','1q','1q','1q','1q','0,5','0AA,2,3AA,5','0','0','0','0','0','4q','4q','4q','0','0','2,5','0AAD,2,3AA,5' ],
                          [ '4q','2','5','1q','2,5','1q','1q','1q','1q','0,5','0AA,2,3AA,5','0','15','0','0','0','4q','4q','4q','0','0','2','0AAD,2,3AA,5' ],
                          [ '4q','2','5','1q','2,5','1q','1q','1q','1q','0,5','0AA,2,3AA,5','5','5','5','5','5','4q','4q','4q','5','5','2','0AAD,2,3AA,5' ] ],
          'BIND 4.9.7' => [ [ '1q','0,2','0,2','1q','4q,5,2','4q','1q','1X','1','0TC,2TC','0,2','0,2,0AA,2AA','0,2,0AA','0AAZ0','0AAZ1','0AAZ2','4q','4q','4q,5q','0AA','0AA','0,2','0,2,0AAD' ],
                            [ '1q','0,2','0,2','1q','4q,5,2','4q','1q','1X','1','0TC,2TC','0,2','0,2,0AA,2AA','0,2,0AA','0Z0,2Z0','0Z1,2Z1','0Z2,2Z2','4q','4q','4q,5q','0,2','0,2','0,2','0,2,0AAD' ] ],
          'BIND 8.1' => [ [ '1q','0,2,5','0,2,5','1q','2,4,5','1q','1q','1X','1','0TC,0TCZ1,3TC,5TC','0,0AA,2,3,3AA,5','0,2,3,3AA,5','0,2,3,3AA,5','0Z0,2Z0,3AAZ0,3Z0,5Z0','0Z1,2Z1,3AAZ1,3Z1,5Z1','0Z2,2Z2,3AAZ2,3Z2,5Z2',
                            '4q','4q','4q','0,2,3,3AA,5','0,0AA,2,3,3AA,5','0,2,3,5','0AAD,0,3,5' ],
                          [ '1q','0,2,5','0,2,5','1q','2,4,5','1q','1q','1X','1','0TC,0TCZ1,3TC,5TC','0,0AA,2,3,3AA,5','0,2,3,3AA,5','0,2,3,3AA,5','0Z0,2Z0,3AAZ0,3Z0,5Z0','0','0Z2,2Z2,3AAZ2,3Z2,5Z2','4q','4q','4q',
                            '0,2,3,3AA,5','0,0AA,2,3,3AA,5','0,2,3,5','0AAD,0,3,5' ],
                          [ '1q','0,2,5','0,2,5','1q','2,4,5','1q','1q','1X','1q','0TC,0TCZ1,3TC,5TC','0,0AA,2,3,3AA,5','0,2,3,3AA,5','0,2,3,3AA,5','0Z0,2Z0,3AAZ0,3Z0,5Z0','0Z1,2Z1,3AAZ1,3Z1,5Z1','0Z2,2Z2,3AAZ2,3Z2,5Z2',
                            '4q','4q','4q','0,2,3,3AA,5','0,0AA,2,3,3AA,5','0,2,3,5','0AAD,0,3,5' ] ],
          'BIND 9.0' => [ [ '4q','5','5','1q','5','1q','5q','1q','1q','0Z1','5','0Z1','15Z1','0Z1','0Z1','0Z1','4q','4q','4q','0Z1','0Z1','5','0AAZ1D,2,5' ] ],
          'BIND 8.3.4' => [ ['1q','2','2','1q','2','1q','1q','1q','1q','0TC','2','0','0','0Z2','0','0Z0','4q','4q','4q','0','0','2','0AAD' ] ],
          'POWERDNS' => [ [ '4AA','0AA','0AAX','2X,0AAX','0AAX,2AA','0AAX,4AA','0AA,t','t','0AA,2,t','0AAX,2','0AA','0AAX,2','0AAX','0AAX','0AAX','0AAX','0AAX','0AAX','0AAX','0AAX','0AA,2','0AA','0AAD' ],
                          [ '4AA','0AA','0AAX','2X,0AAX','0AAX,2AA','0AAX,4AA','0AA,t','t','0AA,2,t','0AAX,2','0AA','0AAX,2','0AAX','0AAX','0AAX','2','2','2','2','2','2','0AA','0AAD' ],
                          [ '4AA','0AA','0AAX','2X,0AAX','0AAX,2AA','0AAX,4AA','0AA,t','t','0AA,2,t','0AAX,2','0AA','0AAX,2','2','2','2','2','2','2','0AAX,2','0AAX,2','2','0AA','0AAD' ],
                          [ '4AA','0AA','0AA','2X','4AA','4AA','0AA','t','0AA','2','0AA','2','2','2','2','2','4AA','4AA','4AA','2','2','0AA','0AAD' ],
                          [ '4AA','0AA','0AAX','3AAX','0AAX','0AAX','0AA','t','3AA','3AA','0AA','3AA','3AA','3AA','3AA','3AA','3AA','3AA','3AA','3AA','3AA','0AA','0AAD' ] ],
          'eNom DNS server' => [ [ '0','0X','0X','0Q2','0','0','1q','t','t','0','0X','0','0','0','0','0','0','0','0','0','0','0X','0X' ] ],
          'NSD' => [ [ '4q','5','5','1q','4q','4q','1q','1q','1q','1q','5','2','2','2','2','2','4q','4q','4q','2','2','0D','5' ] ],
          'MaraDNS 1.0-' => [ [ '4q','5q','5q','t','4q','4q','4q','t','t','5q','5q','5q','5q','5q','5q','5q','4q','4q','4q','5q','0D,5q','5q','5q' ] ],
          'Incognito DNS Commander 3.5-' => [ [ '4X','0,2,5','1','0,0AA,0AAQ2,0Q2,3AAQ2,3Q2X','3','1,5','1','1X','1','0','0AA,2','0','15','0','0','0','4X','4X','4X','0','0,0AA','2,5','0AAD' ] ],
          'Incognito DNS Commander -3.2' => [ [ '4X','0,2,5','1','0,0AA,0AAQ2,0Q2,3AAQ2,3Q2X','0,3','0X,1,5','0q','1,2X','1','0','0','0','15','0','0','0','4X','4X','4X','0','0','0','0AAD' ] ],
          'UltraDNS' => [ [ '4','0','0','1q','3','9','1q','1q','0','0','0','0','0','0','0','0','4','4','4','0','0','0','0AAD' ] ],
          'Simple DNS Plus' => [ [ '4','4','4','0X','4','1','t','1','1','0','4','0','0','0','0','0','4','4','4','0','0','4','0D,4' ],
                                 [ '4','4','4','0AAX','4','1','t','1','1','0AA','4','0AA','0AA','0AA','0AA','0AA','4','4','4','0AA','0AA','4','0D,4' ],
                                 [ '4','4','4','0X','t','4','t','t','t','0','4','0','0','0','0','0','4','4','4','0','0','4','0D,4' ] ],
          'MaraDNS before 1.0' => [ [ '4q','5q','5q','4q','4q','4q','4q','t','t','5q','5q','5q','5q','5q','5q','5q','4q','4q','4q','5q','0D','5q','5q' ] ]
        };

--Cpunkt 18:16, 30 Sep 2004 (CEST)