ans.pl revision 490b05960c9956cc37f52f5fd4443829d75ce7d6
#
# Copyright (C) 2011 Internet Systems Consortium, Inc. ("ISC")
#
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
# PERFORMANCE OF THIS SOFTWARE.
# $Id: ans.pl,v 1.5 2012/02/22 14:22:54 marka Exp $
#
# This is the name server from hell. It provides canned
# responses based on pattern matching the queries, and
# can be reprogrammed on-the-fly over a TCP connection.
#
# The server listens for control connections on port 5301.
# A control connection is a TCP stream of lines like
#
# /pattern/
# name ttl type rdata
# name ttl type rdata
# ...
# /pattern/
# name ttl type rdata
# name ttl type rdata
# ...
#
# There can be any number of patterns, each associated
# with any number of response RRs. Each pattern is a
# Perl regular expression.
#
# Each incoming query is converted into a string of the form
# "qname qtype" (the printable query domain name, space,
# printable query type) and matched against each pattern.
#
# The first pattern matching the query is selected, and
# the RR following the pattern line are sent in the
# answer section of the response.
#
# Each new control connection causes the current set of
# patterns and responses to be cleared before adding new
# ones.
#
# The server handles UDP and TCP queries. Zone transfer
# responses work, but must fit in a single 64 k message.
#
#
# /pattern <key> <key_data>/
# name ttl type rdata
# name ttl type rdata
#
# Note that this data will still be sent with any request for
# pattern, only this data will be signed. Currently, this is only
# done for TCP.
use strict;
# Ignore SIGPIPE so we won't fail if peer closes a TCP socket early
# Flush logged output after every line
local $| = 1;
# We default to listening on 10.53.0.2 for historical reasons
# XXX: we should also be able to specify IPv6
my $server_addr = "10.53.0.2";
if (@ARGV > 0) {
}
# XXX: we should also be able to set the port numbers to listen on.
print "listening on $server_addr:5300,5301.\n";
$pidf->close or die "cannot close pid file: $!";;
#my @answers = ();
my @rules;
sub handleUDP {
my ($buf) = @_;
# get the existing signature if any, and clear the additional section
my $prev_tsig;
}
}
my $r;
foreach $r (@rules) {
print "[handleUDP] $dbtype, $key_name, $key_data \n";
if ("$qname $qtype" =~ /$dbtype/) {
my $a;
foreach $a (@{$r->{answer}}) {
$packet->push("answer", $a);
}
# Sign the packet
print " Signing the response with " .
"$key_name/$key_data\n";
new("$key_name TSIG $key_data");
# These kluges are necessary because Net::DNS
# doesn't know how to sign responses. We
# clear compnames so that the TSIG key and
# algorithm name won't be compressed, and
# add one to arcount because the signing
# function will attempt to decrement it,
# which is incorrect in a response. Finally
# we set request_mac to the previous digest.
$packet->{"compnames"} = {};
if (defined($prev_tsig)) {
my $rmac = pack('n H*',
$tsig->{"request_mac"} =
unpack("H*", $rmac);
}
}
last;
}
}
#$packet->print;
}
# namelen:
# given a stream of data, reads a DNS-formatted name and returns its
# total length, thus making it possible to skip past it.
sub namelen {
my ($data) = @_;
my $len = 0;
my $label_len = 0;
do {
} while ($label_len != 0);
return ($len);
}
# packetlen:
# given a stream of data, reads a DNS wire-format packet and returns
# its total length, making it possible to skip past it.
sub packetlen {
my ($data) = @_;
my $q;
my $rr;
my $header;
my $offset;
#
# parse is no longer a method and calling it here makes perl croak.
#
my $decode = 0;
if ($decode) {
} else {
}
if ($decode) {
($q, $offset) =
} else {
($q, $offset) =
}
}
if ($decode) {
} else {
}
}
if ($decode) {
} else {
}
}
if ($decode) {
} else {
}
}
return $offset;
}
# sign_tcp_continuation:
# This is a hack to correct the problem that Net::DNS has no idea how
# to sign multiple-message TCP responses. Several data that are included
# in the digest when signing a query or the first message of a response are
# omitted when signing subsequent messages in a TCP stream.
#
# Net::DNS::Packet->sign_tsig() has the ability to use a custom signing
# function (specified by calling Packet->sign_func()). We use this
# function as the signing function for TCP continuations, and it removes
# the unwanted data from the digest before calling the default sign_hmac
# function.
sub sign_tcp_continuation {
# copy out first two bytes: size of the previous MAC
# copy out previous MAC
# try parsing out the packet information
# remove the keyname, ttl, class, and algorithm name
# preserve the TSIG data
# prepare a new digest and sign with it
}
sub handleTCP {
my ($buf) = @_;
# get the existing signature if any, and clear the additional section
my $prev_tsig;
my $signer;
}
}
my @results = ();
my $count_these = 0;
my $r;
foreach $r (@rules) {
print "[handleTCP] $dbtype, $key_name, $key_data \n";
if ("$qname $qtype" =~ /$dbtype/) {
$count_these++;
my $a;
foreach $a (@{$r->{answer}}) {
$packet->push("answer", $a);
}
# sign the packet
print " Signing the data with " .
"$key_name/$key_data\n";
new("$key_name TSIG $key_data");
# These kluges are necessary because Net::DNS
# doesn't know how to sign responses. We
# clear compnames so that the TSIG key and
# algorithm name won't be compressed, and
# add one to arcount because the signing
# function will attempt to decrement it,
# which is incorrect in a response. Finally
# we set request_mac to the previous digest.
$packet->{"compnames"} = {};
if (defined($prev_tsig)) {
my $rmac = pack('n H*',
$tsig->{"request_mac"} =
unpack("H*", $rmac);
}
$signer = \&sign_tcp_continuation;
my $copy =
}
#$packet->print;
}
}
print " A total of $count_these patterns matched\n";
return \@results;
}
# Main
my $rin;
my $rout;
for (;;) {
$rin = '';
warn "ctl conn";
my $rule = ();
@rules = ();
chomp $line;
if ($line =~ m!^/(.*)/$!) {
} else {
}
}
$conn->close;
#print Dumper(@rules);
#print "+=+=+ $rules[0]->{'pattern'}\n";
#print "+=+=+ $rules[0]->{'answer'}->[0]->{'rname'}\n";
#print "+=+=+ $rules[0]->{'answer'}->[0]\n";
printf "UDP request\n";
my $buf;
print " Sent $num_chars bytes via UDP\n";
my $buf;
for (;;) {
my $lenbuf;
last unless $n == 2;
last unless $n == $len;
print "TCP request\n";
print " Sent: $n chars via TCP\n";
}
}
$conn->close;
}
}