ans.pl revision e5f5675b1da287ed40aeff081ad2af86090e8d17
#
# Copyright (C) 2011, 2012, 2014, 2016 Internet Systems Consortium, Inc. ("ISC")
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# $Id: ans.pl,v 1.6 2012/02/22 23:47:34 tbox 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. If an empty pattern ("//") is
# received, the server will ignore all incoming queries (TCP
# connections will still be accepted, but both UDP queries
# and TCP queries will not be responded to). If a non-empty
# pattern is then received over the same control connection,
# default behavior is restored.
#
# 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";
print "Using Net::DNS $Net::DNS::VERSION\n";
$pidf->close or die "cannot close pid file: $!";;
#my @answers = ();
my @rules;
my $udphandler;
my $tcphandler;
sub handleUDP {
my ($buf) = @_;
my $request;
$@ and die $@;
} else {
my $err;
}
# 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);
}
my $tsig;
# Sign the packet
print " Signing the response with " .
"$key_name/$key_data\n";
"$key_name TSIG $key_data");
} else {
type => 'TSIG',
}
# 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);
} else {
$tsig->request_mac(
}
}
}
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) = @_;
my $request;
$@ and die $@;
} else {
my $err;
}
my $opaque;
# get the existing signature if any, and clear the additional section
my $prev_tsig;
my $signer;
my $continuation = 0;
}
}
}
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);
}
my $tsig;
# sign the packet
print " Signing the data with " .
"$key_name/$key_data\n";
"$key_name TSIG $key_data");
$continuation) {
$continuation) {
} else {
type => 'TSIG',
}
# 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);
$tsig->request_mac(
}
}
} elsif ($continuation) {
} else {
}
$continuation = 1;
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!^/(.*)/$!) {
if (length($1) == 0) {
$udphandler = sub { return; };
$tcphandler = sub { return; };
} else {
$udphandler = \&handleUDP;
$tcphandler = \&handleTCP;
}
} 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;
if (defined($result)) {
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";
if (defined($result)) {
print " Sent: $n chars via TCP\n";
}
}
}
$conn->close;
}
}