1*00b67f09SDavid van Moolenbroek#!/usr/bin/perl 2*00b67f09SDavid van Moolenbroek# 3*00b67f09SDavid van Moolenbroek# Copyright (C) 2011, 2012, 2014 Internet Systems Consortium, Inc. ("ISC") 4*00b67f09SDavid van Moolenbroek# 5*00b67f09SDavid van Moolenbroek# Permission to use, copy, modify, and/or distribute this software for any 6*00b67f09SDavid van Moolenbroek# purpose with or without fee is hereby granted, provided that the above 7*00b67f09SDavid van Moolenbroek# copyright notice and this permission notice appear in all copies. 8*00b67f09SDavid van Moolenbroek# 9*00b67f09SDavid van Moolenbroek# THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10*00b67f09SDavid van Moolenbroek# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11*00b67f09SDavid van Moolenbroek# AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12*00b67f09SDavid van Moolenbroek# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13*00b67f09SDavid van Moolenbroek# LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14*00b67f09SDavid van Moolenbroek# OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15*00b67f09SDavid van Moolenbroek# PERFORMANCE OF THIS SOFTWARE. 16*00b67f09SDavid van Moolenbroek 17*00b67f09SDavid van Moolenbroek# Id: ans.pl,v 1.6 2012/02/22 23:47:34 tbox Exp 18*00b67f09SDavid van Moolenbroek 19*00b67f09SDavid van Moolenbroek# 20*00b67f09SDavid van Moolenbroek# This is the name server from hell. It provides canned 21*00b67f09SDavid van Moolenbroek# responses based on pattern matching the queries, and 22*00b67f09SDavid van Moolenbroek# can be reprogrammed on-the-fly over a TCP connection. 23*00b67f09SDavid van Moolenbroek# 24*00b67f09SDavid van Moolenbroek# The server listens for control connections on port 5301. 25*00b67f09SDavid van Moolenbroek# A control connection is a TCP stream of lines like 26*00b67f09SDavid van Moolenbroek# 27*00b67f09SDavid van Moolenbroek# /pattern/ 28*00b67f09SDavid van Moolenbroek# name ttl type rdata 29*00b67f09SDavid van Moolenbroek# name ttl type rdata 30*00b67f09SDavid van Moolenbroek# ... 31*00b67f09SDavid van Moolenbroek# /pattern/ 32*00b67f09SDavid van Moolenbroek# name ttl type rdata 33*00b67f09SDavid van Moolenbroek# name ttl type rdata 34*00b67f09SDavid van Moolenbroek# ... 35*00b67f09SDavid van Moolenbroek# 36*00b67f09SDavid van Moolenbroek# There can be any number of patterns, each associated 37*00b67f09SDavid van Moolenbroek# with any number of response RRs. Each pattern is a 38*00b67f09SDavid van Moolenbroek# Perl regular expression. 39*00b67f09SDavid van Moolenbroek# 40*00b67f09SDavid van Moolenbroek# Each incoming query is converted into a string of the form 41*00b67f09SDavid van Moolenbroek# "qname qtype" (the printable query domain name, space, 42*00b67f09SDavid van Moolenbroek# printable query type) and matched against each pattern. 43*00b67f09SDavid van Moolenbroek# 44*00b67f09SDavid van Moolenbroek# The first pattern matching the query is selected, and 45*00b67f09SDavid van Moolenbroek# the RR following the pattern line are sent in the 46*00b67f09SDavid van Moolenbroek# answer section of the response. 47*00b67f09SDavid van Moolenbroek# 48*00b67f09SDavid van Moolenbroek# Each new control connection causes the current set of 49*00b67f09SDavid van Moolenbroek# patterns and responses to be cleared before adding new 50*00b67f09SDavid van Moolenbroek# ones. 51*00b67f09SDavid van Moolenbroek# 52*00b67f09SDavid van Moolenbroek# The server handles UDP and TCP queries. Zone transfer 53*00b67f09SDavid van Moolenbroek# responses work, but must fit in a single 64 k message. 54*00b67f09SDavid van Moolenbroek# 55*00b67f09SDavid van Moolenbroek# Now you can add TSIG, just specify key/key data with: 56*00b67f09SDavid van Moolenbroek# 57*00b67f09SDavid van Moolenbroek# /pattern <key> <key_data>/ 58*00b67f09SDavid van Moolenbroek# name ttl type rdata 59*00b67f09SDavid van Moolenbroek# name ttl type rdata 60*00b67f09SDavid van Moolenbroek# 61*00b67f09SDavid van Moolenbroek# Note that this data will still be sent with any request for 62*00b67f09SDavid van Moolenbroek# pattern, only this data will be signed. Currently, this is only 63*00b67f09SDavid van Moolenbroek# done for TCP. 64*00b67f09SDavid van Moolenbroek 65*00b67f09SDavid van Moolenbroek 66*00b67f09SDavid van Moolenbroekuse IO::File; 67*00b67f09SDavid van Moolenbroekuse IO::Socket; 68*00b67f09SDavid van Moolenbroekuse Data::Dumper; 69*00b67f09SDavid van Moolenbroekuse Net::DNS; 70*00b67f09SDavid van Moolenbroekuse Net::DNS::Packet; 71*00b67f09SDavid van Moolenbroekuse strict; 72*00b67f09SDavid van Moolenbroek 73*00b67f09SDavid van Moolenbroek# Ignore SIGPIPE so we won't fail if peer closes a TCP socket early 74*00b67f09SDavid van Moolenbroeklocal $SIG{PIPE} = 'IGNORE'; 75*00b67f09SDavid van Moolenbroek 76*00b67f09SDavid van Moolenbroek# Flush logged output after every line 77*00b67f09SDavid van Moolenbroeklocal $| = 1; 78*00b67f09SDavid van Moolenbroek 79*00b67f09SDavid van Moolenbroek# We default to listening on 10.53.0.2 for historical reasons 80*00b67f09SDavid van Moolenbroek# XXX: we should also be able to specify IPv6 81*00b67f09SDavid van Moolenbroekmy $server_addr = "10.53.0.2"; 82*00b67f09SDavid van Moolenbroekif (@ARGV > 0) { 83*00b67f09SDavid van Moolenbroek $server_addr = @ARGV[0]; 84*00b67f09SDavid van Moolenbroek} 85*00b67f09SDavid van Moolenbroek 86*00b67f09SDavid van Moolenbroek# XXX: we should also be able to set the port numbers to listen on. 87*00b67f09SDavid van Moolenbroekmy $ctlsock = IO::Socket::INET->new(LocalAddr => "$server_addr", 88*00b67f09SDavid van Moolenbroek LocalPort => 5301, Proto => "tcp", Listen => 5, Reuse => 1) or die "$!"; 89*00b67f09SDavid van Moolenbroek 90*00b67f09SDavid van Moolenbroekmy $udpsock = IO::Socket::INET->new(LocalAddr => "$server_addr", 91*00b67f09SDavid van Moolenbroek LocalPort => 5300, Proto => "udp", Reuse => 1) or die "$!"; 92*00b67f09SDavid van Moolenbroek 93*00b67f09SDavid van Moolenbroekmy $tcpsock = IO::Socket::INET->new(LocalAddr => "$server_addr", 94*00b67f09SDavid van Moolenbroek LocalPort => 5300, Proto => "tcp", Listen => 5, Reuse => 1) or die "$!"; 95*00b67f09SDavid van Moolenbroek 96*00b67f09SDavid van Moolenbroekprint "listening on $server_addr:5300,5301.\n"; 97*00b67f09SDavid van Moolenbroekprint "Using Net::DNS $Net::DNS::VERSION\n"; 98*00b67f09SDavid van Moolenbroek 99*00b67f09SDavid van Moolenbroekmy $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!"; 100*00b67f09SDavid van Moolenbroekprint $pidf "$$\n" or die "cannot write pid file: $!"; 101*00b67f09SDavid van Moolenbroek$pidf->close or die "cannot close pid file: $!";; 102*00b67f09SDavid van Moolenbroeksub rmpid { unlink "ans.pid"; exit 1; }; 103*00b67f09SDavid van Moolenbroek 104*00b67f09SDavid van Moolenbroek$SIG{INT} = \&rmpid; 105*00b67f09SDavid van Moolenbroek$SIG{TERM} = \&rmpid; 106*00b67f09SDavid van Moolenbroek 107*00b67f09SDavid van Moolenbroek#my @answers = (); 108*00b67f09SDavid van Moolenbroekmy @rules; 109*00b67f09SDavid van Moolenbroeksub handleUDP { 110*00b67f09SDavid van Moolenbroek my ($buf) = @_; 111*00b67f09SDavid van Moolenbroek my $request; 112*00b67f09SDavid van Moolenbroek 113*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION > 0.68) { 114*00b67f09SDavid van Moolenbroek $request = new Net::DNS::Packet(\$buf, 0); 115*00b67f09SDavid van Moolenbroek $@ and die $@; 116*00b67f09SDavid van Moolenbroek } else { 117*00b67f09SDavid van Moolenbroek my $err; 118*00b67f09SDavid van Moolenbroek ($request, $err) = new Net::DNS::Packet(\$buf, 0); 119*00b67f09SDavid van Moolenbroek $err and die $err; 120*00b67f09SDavid van Moolenbroek } 121*00b67f09SDavid van Moolenbroek 122*00b67f09SDavid van Moolenbroek my @questions = $request->question; 123*00b67f09SDavid van Moolenbroek my $qname = $questions[0]->qname; 124*00b67f09SDavid van Moolenbroek my $qtype = $questions[0]->qtype; 125*00b67f09SDavid van Moolenbroek my $qclass = $questions[0]->qclass; 126*00b67f09SDavid van Moolenbroek my $id = $request->header->id; 127*00b67f09SDavid van Moolenbroek 128*00b67f09SDavid van Moolenbroek my $packet = new Net::DNS::Packet($qname, $qtype, $qclass); 129*00b67f09SDavid van Moolenbroek $packet->header->qr(1); 130*00b67f09SDavid van Moolenbroek $packet->header->aa(1); 131*00b67f09SDavid van Moolenbroek $packet->header->id($id); 132*00b67f09SDavid van Moolenbroek 133*00b67f09SDavid van Moolenbroek # get the existing signature if any, and clear the additional section 134*00b67f09SDavid van Moolenbroek my $prev_tsig; 135*00b67f09SDavid van Moolenbroek while (my $rr = $request->pop("additional")) { 136*00b67f09SDavid van Moolenbroek $prev_tsig = $rr if ($rr->type eq "TSIG"); 137*00b67f09SDavid van Moolenbroek } 138*00b67f09SDavid van Moolenbroek 139*00b67f09SDavid van Moolenbroek my $r; 140*00b67f09SDavid van Moolenbroek foreach $r (@rules) { 141*00b67f09SDavid van Moolenbroek my $pattern = $r->{pattern}; 142*00b67f09SDavid van Moolenbroek my($dbtype, $key_name, $key_data) = split(/ /,$pattern); 143*00b67f09SDavid van Moolenbroek print "[handleUDP] $dbtype, $key_name, $key_data \n"; 144*00b67f09SDavid van Moolenbroek if ("$qname $qtype" =~ /$dbtype/) { 145*00b67f09SDavid van Moolenbroek my $a; 146*00b67f09SDavid van Moolenbroek foreach $a (@{$r->{answer}}) { 147*00b67f09SDavid van Moolenbroek $packet->push("answer", $a); 148*00b67f09SDavid van Moolenbroek } 149*00b67f09SDavid van Moolenbroek if(defined($key_name) && defined($key_data)) { 150*00b67f09SDavid van Moolenbroek my $tsig; 151*00b67f09SDavid van Moolenbroek # Sign the packet 152*00b67f09SDavid van Moolenbroek print " Signing the response with " . 153*00b67f09SDavid van Moolenbroek "$key_name/$key_data\n"; 154*00b67f09SDavid van Moolenbroek 155*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.69) { 156*00b67f09SDavid van Moolenbroek $tsig = Net::DNS::RR->new( 157*00b67f09SDavid van Moolenbroek "$key_name TSIG $key_data"); 158*00b67f09SDavid van Moolenbroek } else { 159*00b67f09SDavid van Moolenbroek $tsig = Net::DNS::RR->new( 160*00b67f09SDavid van Moolenbroek name => $key_name, 161*00b67f09SDavid van Moolenbroek type => 'TSIG', 162*00b67f09SDavid van Moolenbroek key => $key_data); 163*00b67f09SDavid van Moolenbroek } 164*00b67f09SDavid van Moolenbroek 165*00b67f09SDavid van Moolenbroek # These kluges are necessary because Net::DNS 166*00b67f09SDavid van Moolenbroek # doesn't know how to sign responses. We 167*00b67f09SDavid van Moolenbroek # clear compnames so that the TSIG key and 168*00b67f09SDavid van Moolenbroek # algorithm name won't be compressed, and 169*00b67f09SDavid van Moolenbroek # add one to arcount because the signing 170*00b67f09SDavid van Moolenbroek # function will attempt to decrement it, 171*00b67f09SDavid van Moolenbroek # which is incorrect in a response. Finally 172*00b67f09SDavid van Moolenbroek # we set request_mac to the previous digest. 173*00b67f09SDavid van Moolenbroek $packet->{"compnames"} = {} 174*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.70); 175*00b67f09SDavid van Moolenbroek $packet->{"header"}{"arcount"} += 1 176*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.70); 177*00b67f09SDavid van Moolenbroek if (defined($prev_tsig)) { 178*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.73) { 179*00b67f09SDavid van Moolenbroek my $rmac = pack('n H*', 180*00b67f09SDavid van Moolenbroek length($prev_tsig->mac)/2, 181*00b67f09SDavid van Moolenbroek $prev_tsig->mac); 182*00b67f09SDavid van Moolenbroek $tsig->{"request_mac"} = 183*00b67f09SDavid van Moolenbroek unpack("H*", $rmac); 184*00b67f09SDavid van Moolenbroek } else { 185*00b67f09SDavid van Moolenbroek $tsig->request_mac( 186*00b67f09SDavid van Moolenbroek $prev_tsig->mac); 187*00b67f09SDavid van Moolenbroek } 188*00b67f09SDavid van Moolenbroek } 189*00b67f09SDavid van Moolenbroek 190*00b67f09SDavid van Moolenbroek $packet->sign_tsig($tsig); 191*00b67f09SDavid van Moolenbroek } 192*00b67f09SDavid van Moolenbroek last; 193*00b67f09SDavid van Moolenbroek } 194*00b67f09SDavid van Moolenbroek } 195*00b67f09SDavid van Moolenbroek #$packet->print; 196*00b67f09SDavid van Moolenbroek 197*00b67f09SDavid van Moolenbroek return $packet->data; 198*00b67f09SDavid van Moolenbroek} 199*00b67f09SDavid van Moolenbroek 200*00b67f09SDavid van Moolenbroek# namelen: 201*00b67f09SDavid van Moolenbroek# given a stream of data, reads a DNS-formatted name and returns its 202*00b67f09SDavid van Moolenbroek# total length, thus making it possible to skip past it. 203*00b67f09SDavid van Moolenbroeksub namelen { 204*00b67f09SDavid van Moolenbroek my ($data) = @_; 205*00b67f09SDavid van Moolenbroek my $len = 0; 206*00b67f09SDavid van Moolenbroek my $label_len = 0; 207*00b67f09SDavid van Moolenbroek do { 208*00b67f09SDavid van Moolenbroek $label_len = unpack("c", $data); 209*00b67f09SDavid van Moolenbroek $data = substr($data, $label_len + 1); 210*00b67f09SDavid van Moolenbroek $len += $label_len + 1; 211*00b67f09SDavid van Moolenbroek } while ($label_len != 0); 212*00b67f09SDavid van Moolenbroek return ($len); 213*00b67f09SDavid van Moolenbroek} 214*00b67f09SDavid van Moolenbroek 215*00b67f09SDavid van Moolenbroek# packetlen: 216*00b67f09SDavid van Moolenbroek# given a stream of data, reads a DNS wire-format packet and returns 217*00b67f09SDavid van Moolenbroek# its total length, making it possible to skip past it. 218*00b67f09SDavid van Moolenbroeksub packetlen { 219*00b67f09SDavid van Moolenbroek my ($data) = @_; 220*00b67f09SDavid van Moolenbroek my $q; 221*00b67f09SDavid van Moolenbroek my $rr; 222*00b67f09SDavid van Moolenbroek my $header; 223*00b67f09SDavid van Moolenbroek my $offset; 224*00b67f09SDavid van Moolenbroek 225*00b67f09SDavid van Moolenbroek # 226*00b67f09SDavid van Moolenbroek # decode/encode were introduced in Net::DNS 0.68 227*00b67f09SDavid van Moolenbroek # parse is no longer a method and calling it here makes perl croak. 228*00b67f09SDavid van Moolenbroek # 229*00b67f09SDavid van Moolenbroek my $decode = 0; 230*00b67f09SDavid van Moolenbroek $decode = 1 if ($Net::DNS::VERSION >= 0.68); 231*00b67f09SDavid van Moolenbroek 232*00b67f09SDavid van Moolenbroek if ($decode) { 233*00b67f09SDavid van Moolenbroek ($header, $offset) = Net::DNS::Header->decode(\$data); 234*00b67f09SDavid van Moolenbroek } else { 235*00b67f09SDavid van Moolenbroek ($header, $offset) = Net::DNS::Header->parse(\$data); 236*00b67f09SDavid van Moolenbroek } 237*00b67f09SDavid van Moolenbroek 238*00b67f09SDavid van Moolenbroek for (1 .. $header->qdcount) { 239*00b67f09SDavid van Moolenbroek if ($decode) { 240*00b67f09SDavid van Moolenbroek ($q, $offset) = 241*00b67f09SDavid van Moolenbroek Net::DNS::Question->decode(\$data, $offset); 242*00b67f09SDavid van Moolenbroek } else { 243*00b67f09SDavid van Moolenbroek ($q, $offset) = 244*00b67f09SDavid van Moolenbroek Net::DNS::Question->parse(\$data, $offset); 245*00b67f09SDavid van Moolenbroek } 246*00b67f09SDavid van Moolenbroek } 247*00b67f09SDavid van Moolenbroek for (1 .. $header->ancount) { 248*00b67f09SDavid van Moolenbroek if ($decode) { 249*00b67f09SDavid van Moolenbroek ($q, $offset) = Net::DNS::RR->decode(\$data, $offset); 250*00b67f09SDavid van Moolenbroek } else { 251*00b67f09SDavid van Moolenbroek ($q, $offset) = Net::DNS::RR->parse(\$data, $offset); 252*00b67f09SDavid van Moolenbroek } 253*00b67f09SDavid van Moolenbroek } 254*00b67f09SDavid van Moolenbroek for (1 .. $header->nscount) { 255*00b67f09SDavid van Moolenbroek if ($decode) { 256*00b67f09SDavid van Moolenbroek ($q, $offset) = Net::DNS::RR->decode(\$data, $offset); 257*00b67f09SDavid van Moolenbroek } else { 258*00b67f09SDavid van Moolenbroek ($q, $offset) = Net::DNS::RR->parse(\$data, $offset); 259*00b67f09SDavid van Moolenbroek } 260*00b67f09SDavid van Moolenbroek } 261*00b67f09SDavid van Moolenbroek for (1 .. $header->arcount) { 262*00b67f09SDavid van Moolenbroek if ($decode) { 263*00b67f09SDavid van Moolenbroek ($q, $offset) = Net::DNS::RR->decode(\$data, $offset); 264*00b67f09SDavid van Moolenbroek } else { 265*00b67f09SDavid van Moolenbroek ($q, $offset) = Net::DNS::RR->parse(\$data, $offset); 266*00b67f09SDavid van Moolenbroek } 267*00b67f09SDavid van Moolenbroek } 268*00b67f09SDavid van Moolenbroek return $offset; 269*00b67f09SDavid van Moolenbroek} 270*00b67f09SDavid van Moolenbroek 271*00b67f09SDavid van Moolenbroek# sign_tcp_continuation: 272*00b67f09SDavid van Moolenbroek# This is a hack to correct the problem that Net::DNS has no idea how 273*00b67f09SDavid van Moolenbroek# to sign multiple-message TCP responses. Several data that are included 274*00b67f09SDavid van Moolenbroek# in the digest when signing a query or the first message of a response are 275*00b67f09SDavid van Moolenbroek# omitted when signing subsequent messages in a TCP stream. 276*00b67f09SDavid van Moolenbroek# 277*00b67f09SDavid van Moolenbroek# Net::DNS::Packet->sign_tsig() has the ability to use a custom signing 278*00b67f09SDavid van Moolenbroek# function (specified by calling Packet->sign_func()). We use this 279*00b67f09SDavid van Moolenbroek# function as the signing function for TCP continuations, and it removes 280*00b67f09SDavid van Moolenbroek# the unwanted data from the digest before calling the default sign_hmac 281*00b67f09SDavid van Moolenbroek# function. 282*00b67f09SDavid van Moolenbroeksub sign_tcp_continuation { 283*00b67f09SDavid van Moolenbroek my ($key, $data) = @_; 284*00b67f09SDavid van Moolenbroek 285*00b67f09SDavid van Moolenbroek # copy out first two bytes: size of the previous MAC 286*00b67f09SDavid van Moolenbroek my $rmacsize = unpack("n", $data); 287*00b67f09SDavid van Moolenbroek $data = substr($data, 2); 288*00b67f09SDavid van Moolenbroek 289*00b67f09SDavid van Moolenbroek # copy out previous MAC 290*00b67f09SDavid van Moolenbroek my $rmac = substr($data, 0, $rmacsize); 291*00b67f09SDavid van Moolenbroek $data = substr($data, $rmacsize); 292*00b67f09SDavid van Moolenbroek 293*00b67f09SDavid van Moolenbroek # try parsing out the packet information 294*00b67f09SDavid van Moolenbroek my $plen = packetlen($data); 295*00b67f09SDavid van Moolenbroek my $pdata = substr($data, 0, $plen); 296*00b67f09SDavid van Moolenbroek $data = substr($data, $plen); 297*00b67f09SDavid van Moolenbroek 298*00b67f09SDavid van Moolenbroek # remove the keyname, ttl, class, and algorithm name 299*00b67f09SDavid van Moolenbroek $data = substr($data, namelen($data)); 300*00b67f09SDavid van Moolenbroek $data = substr($data, 6); 301*00b67f09SDavid van Moolenbroek $data = substr($data, namelen($data)); 302*00b67f09SDavid van Moolenbroek 303*00b67f09SDavid van Moolenbroek # preserve the TSIG data 304*00b67f09SDavid van Moolenbroek my $tdata = substr($data, 0, 8); 305*00b67f09SDavid van Moolenbroek 306*00b67f09SDavid van Moolenbroek # prepare a new digest and sign with it 307*00b67f09SDavid van Moolenbroek $data = pack("n", $rmacsize) . $rmac . $pdata . $tdata; 308*00b67f09SDavid van Moolenbroek return Net::DNS::RR::TSIG::sign_hmac($key, $data); 309*00b67f09SDavid van Moolenbroek} 310*00b67f09SDavid van Moolenbroek 311*00b67f09SDavid van Moolenbroeksub handleTCP { 312*00b67f09SDavid van Moolenbroek my ($buf) = @_; 313*00b67f09SDavid van Moolenbroek my $request; 314*00b67f09SDavid van Moolenbroek 315*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION > 0.68) { 316*00b67f09SDavid van Moolenbroek $request = new Net::DNS::Packet(\$buf, 0); 317*00b67f09SDavid van Moolenbroek $@ and die $@; 318*00b67f09SDavid van Moolenbroek } else { 319*00b67f09SDavid van Moolenbroek my $err; 320*00b67f09SDavid van Moolenbroek ($request, $err) = new Net::DNS::Packet(\$buf, 0); 321*00b67f09SDavid van Moolenbroek $err and die $err; 322*00b67f09SDavid van Moolenbroek } 323*00b67f09SDavid van Moolenbroek 324*00b67f09SDavid van Moolenbroek my @questions = $request->question; 325*00b67f09SDavid van Moolenbroek my $qname = $questions[0]->qname; 326*00b67f09SDavid van Moolenbroek my $qtype = $questions[0]->qtype; 327*00b67f09SDavid van Moolenbroek my $qclass = $questions[0]->qclass; 328*00b67f09SDavid van Moolenbroek my $id = $request->header->id; 329*00b67f09SDavid van Moolenbroek 330*00b67f09SDavid van Moolenbroek my $opaque; 331*00b67f09SDavid van Moolenbroek 332*00b67f09SDavid van Moolenbroek my $packet = new Net::DNS::Packet($qname, $qtype, $qclass); 333*00b67f09SDavid van Moolenbroek $packet->header->qr(1); 334*00b67f09SDavid van Moolenbroek $packet->header->aa(1); 335*00b67f09SDavid van Moolenbroek $packet->header->id($id); 336*00b67f09SDavid van Moolenbroek 337*00b67f09SDavid van Moolenbroek # get the existing signature if any, and clear the additional section 338*00b67f09SDavid van Moolenbroek my $prev_tsig; 339*00b67f09SDavid van Moolenbroek my $signer; 340*00b67f09SDavid van Moolenbroek my $continuation = 0; 341*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.81) { 342*00b67f09SDavid van Moolenbroek while (my $rr = $request->pop("additional")) { 343*00b67f09SDavid van Moolenbroek if ($rr->type eq "TSIG") { 344*00b67f09SDavid van Moolenbroek $prev_tsig = $rr; 345*00b67f09SDavid van Moolenbroek } 346*00b67f09SDavid van Moolenbroek } 347*00b67f09SDavid van Moolenbroek } 348*00b67f09SDavid van Moolenbroek 349*00b67f09SDavid van Moolenbroek my @results = (); 350*00b67f09SDavid van Moolenbroek my $count_these = 0; 351*00b67f09SDavid van Moolenbroek 352*00b67f09SDavid van Moolenbroek my $r; 353*00b67f09SDavid van Moolenbroek foreach $r (@rules) { 354*00b67f09SDavid van Moolenbroek my $pattern = $r->{pattern}; 355*00b67f09SDavid van Moolenbroek my($dbtype, $key_name, $key_data) = split(/ /,$pattern); 356*00b67f09SDavid van Moolenbroek print "[handleTCP] $dbtype, $key_name, $key_data \n"; 357*00b67f09SDavid van Moolenbroek if ("$qname $qtype" =~ /$dbtype/) { 358*00b67f09SDavid van Moolenbroek $count_these++; 359*00b67f09SDavid van Moolenbroek my $a; 360*00b67f09SDavid van Moolenbroek foreach $a (@{$r->{answer}}) { 361*00b67f09SDavid van Moolenbroek $packet->push("answer", $a); 362*00b67f09SDavid van Moolenbroek } 363*00b67f09SDavid van Moolenbroek if (defined($key_name) && defined($key_data)) { 364*00b67f09SDavid van Moolenbroek my $tsig; 365*00b67f09SDavid van Moolenbroek # sign the packet 366*00b67f09SDavid van Moolenbroek print " Signing the data with " . 367*00b67f09SDavid van Moolenbroek "$key_name/$key_data\n"; 368*00b67f09SDavid van Moolenbroek 369*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.69) { 370*00b67f09SDavid van Moolenbroek $tsig = Net::DNS::RR->new( 371*00b67f09SDavid van Moolenbroek "$key_name TSIG $key_data"); 372*00b67f09SDavid van Moolenbroek } elsif ($Net::DNS::VERSION >= 0.81 && 373*00b67f09SDavid van Moolenbroek $continuation) { 374*00b67f09SDavid van Moolenbroek } elsif ($Net::DNS::VERSION >= 0.75 && 375*00b67f09SDavid van Moolenbroek $continuation) { 376*00b67f09SDavid van Moolenbroek $tsig = $prev_tsig; 377*00b67f09SDavid van Moolenbroek } else { 378*00b67f09SDavid van Moolenbroek $tsig = Net::DNS::RR->new( 379*00b67f09SDavid van Moolenbroek name => $key_name, 380*00b67f09SDavid van Moolenbroek type => 'TSIG', 381*00b67f09SDavid van Moolenbroek key => $key_data); 382*00b67f09SDavid van Moolenbroek } 383*00b67f09SDavid van Moolenbroek 384*00b67f09SDavid van Moolenbroek # These kluges are necessary because Net::DNS 385*00b67f09SDavid van Moolenbroek # doesn't know how to sign responses. We 386*00b67f09SDavid van Moolenbroek # clear compnames so that the TSIG key and 387*00b67f09SDavid van Moolenbroek # algorithm name won't be compressed, and 388*00b67f09SDavid van Moolenbroek # add one to arcount because the signing 389*00b67f09SDavid van Moolenbroek # function will attempt to decrement it, 390*00b67f09SDavid van Moolenbroek # which is incorrect in a response. Finally 391*00b67f09SDavid van Moolenbroek # we set request_mac to the previous digest. 392*00b67f09SDavid van Moolenbroek $packet->{"compnames"} = {} 393*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.70); 394*00b67f09SDavid van Moolenbroek $packet->{"header"}{"arcount"} += 1 395*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.70); 396*00b67f09SDavid van Moolenbroek if (defined($prev_tsig)) { 397*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.73) { 398*00b67f09SDavid van Moolenbroek my $rmac = pack('n H*', 399*00b67f09SDavid van Moolenbroek length($prev_tsig->mac)/2, 400*00b67f09SDavid van Moolenbroek $prev_tsig->mac); 401*00b67f09SDavid van Moolenbroek $tsig->{"request_mac"} = 402*00b67f09SDavid van Moolenbroek unpack("H*", $rmac); 403*00b67f09SDavid van Moolenbroek } elsif ($Net::DNS::VERSION < 0.81) { 404*00b67f09SDavid van Moolenbroek $tsig->request_mac( 405*00b67f09SDavid van Moolenbroek $prev_tsig->mac); 406*00b67f09SDavid van Moolenbroek } 407*00b67f09SDavid van Moolenbroek } 408*00b67f09SDavid van Moolenbroek 409*00b67f09SDavid van Moolenbroek $tsig->sign_func($signer) if defined($signer); 410*00b67f09SDavid van Moolenbroek $tsig->continuation($continuation) if 411*00b67f09SDavid van Moolenbroek ($Net::DNS::VERSION >= 0.71 && 412*00b67f09SDavid van Moolenbroek $Net::DNS::VERSION <= 0.74 ); 413*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.81) { 414*00b67f09SDavid van Moolenbroek $packet->sign_tsig($tsig); 415*00b67f09SDavid van Moolenbroek } elsif ($continuation) { 416*00b67f09SDavid van Moolenbroek $opaque = $packet->sign_tsig($opaque); 417*00b67f09SDavid van Moolenbroek } else { 418*00b67f09SDavid van Moolenbroek $opaque = $packet->sign_tsig($request); 419*00b67f09SDavid van Moolenbroek } 420*00b67f09SDavid van Moolenbroek $signer = \&sign_tcp_continuation 421*00b67f09SDavid van Moolenbroek if ($Net::DNS::VERSION < 0.70); 422*00b67f09SDavid van Moolenbroek $continuation = 1; 423*00b67f09SDavid van Moolenbroek 424*00b67f09SDavid van Moolenbroek my $copy = 425*00b67f09SDavid van Moolenbroek Net::DNS::Packet->new(\($packet->data)); 426*00b67f09SDavid van Moolenbroek $prev_tsig = $copy->pop("additional"); 427*00b67f09SDavid van Moolenbroek } 428*00b67f09SDavid van Moolenbroek #$packet->print; 429*00b67f09SDavid van Moolenbroek push(@results,$packet->data); 430*00b67f09SDavid van Moolenbroek $packet = new Net::DNS::Packet($qname, $qtype, $qclass); 431*00b67f09SDavid van Moolenbroek $packet->header->qr(1); 432*00b67f09SDavid van Moolenbroek $packet->header->aa(1); 433*00b67f09SDavid van Moolenbroek $packet->header->id($id); 434*00b67f09SDavid van Moolenbroek } 435*00b67f09SDavid van Moolenbroek } 436*00b67f09SDavid van Moolenbroek print " A total of $count_these patterns matched\n"; 437*00b67f09SDavid van Moolenbroek return \@results; 438*00b67f09SDavid van Moolenbroek} 439*00b67f09SDavid van Moolenbroek 440*00b67f09SDavid van Moolenbroek# Main 441*00b67f09SDavid van Moolenbroekmy $rin; 442*00b67f09SDavid van Moolenbroekmy $rout; 443*00b67f09SDavid van Moolenbroekfor (;;) { 444*00b67f09SDavid van Moolenbroek $rin = ''; 445*00b67f09SDavid van Moolenbroek vec($rin, fileno($ctlsock), 1) = 1; 446*00b67f09SDavid van Moolenbroek vec($rin, fileno($tcpsock), 1) = 1; 447*00b67f09SDavid van Moolenbroek vec($rin, fileno($udpsock), 1) = 1; 448*00b67f09SDavid van Moolenbroek 449*00b67f09SDavid van Moolenbroek select($rout = $rin, undef, undef, undef); 450*00b67f09SDavid van Moolenbroek 451*00b67f09SDavid van Moolenbroek if (vec($rout, fileno($ctlsock), 1)) { 452*00b67f09SDavid van Moolenbroek warn "ctl conn"; 453*00b67f09SDavid van Moolenbroek my $conn = $ctlsock->accept; 454*00b67f09SDavid van Moolenbroek my $rule = (); 455*00b67f09SDavid van Moolenbroek @rules = (); 456*00b67f09SDavid van Moolenbroek while (my $line = $conn->getline) { 457*00b67f09SDavid van Moolenbroek chomp $line; 458*00b67f09SDavid van Moolenbroek if ($line =~ m!^/(.*)/$!) { 459*00b67f09SDavid van Moolenbroek $rule = { pattern => $1, answer => [] }; 460*00b67f09SDavid van Moolenbroek push(@rules, $rule); 461*00b67f09SDavid van Moolenbroek } else { 462*00b67f09SDavid van Moolenbroek push(@{$rule->{answer}}, 463*00b67f09SDavid van Moolenbroek new Net::DNS::RR($line)); 464*00b67f09SDavid van Moolenbroek } 465*00b67f09SDavid van Moolenbroek } 466*00b67f09SDavid van Moolenbroek $conn->close; 467*00b67f09SDavid van Moolenbroek #print Dumper(@rules); 468*00b67f09SDavid van Moolenbroek #print "+=+=+ $rules[0]->{'pattern'}\n"; 469*00b67f09SDavid van Moolenbroek #print "+=+=+ $rules[0]->{'answer'}->[0]->{'rname'}\n"; 470*00b67f09SDavid van Moolenbroek #print "+=+=+ $rules[0]->{'answer'}->[0]\n"; 471*00b67f09SDavid van Moolenbroek } elsif (vec($rout, fileno($udpsock), 1)) { 472*00b67f09SDavid van Moolenbroek printf "UDP request\n"; 473*00b67f09SDavid van Moolenbroek my $buf; 474*00b67f09SDavid van Moolenbroek $udpsock->recv($buf, 512); 475*00b67f09SDavid van Moolenbroek my $result = handleUDP($buf); 476*00b67f09SDavid van Moolenbroek my $num_chars = $udpsock->send($result); 477*00b67f09SDavid van Moolenbroek print " Sent $num_chars bytes via UDP\n"; 478*00b67f09SDavid van Moolenbroek } elsif (vec($rout, fileno($tcpsock), 1)) { 479*00b67f09SDavid van Moolenbroek my $conn = $tcpsock->accept; 480*00b67f09SDavid van Moolenbroek my $buf; 481*00b67f09SDavid van Moolenbroek for (;;) { 482*00b67f09SDavid van Moolenbroek my $lenbuf; 483*00b67f09SDavid van Moolenbroek my $n = $conn->sysread($lenbuf, 2); 484*00b67f09SDavid van Moolenbroek last unless $n == 2; 485*00b67f09SDavid van Moolenbroek my $len = unpack("n", $lenbuf); 486*00b67f09SDavid van Moolenbroek $n = $conn->sysread($buf, $len); 487*00b67f09SDavid van Moolenbroek last unless $n == $len; 488*00b67f09SDavid van Moolenbroek print "TCP request\n"; 489*00b67f09SDavid van Moolenbroek my $result = handleTCP($buf); 490*00b67f09SDavid van Moolenbroek foreach my $response (@$result) { 491*00b67f09SDavid van Moolenbroek $len = length($response); 492*00b67f09SDavid van Moolenbroek $n = $conn->syswrite(pack("n", $len), 2); 493*00b67f09SDavid van Moolenbroek $n = $conn->syswrite($response, $len); 494*00b67f09SDavid van Moolenbroek print " Sent: $n chars via TCP\n"; 495*00b67f09SDavid van Moolenbroek } 496*00b67f09SDavid van Moolenbroek } 497*00b67f09SDavid van Moolenbroek $conn->close; 498*00b67f09SDavid van Moolenbroek } 499*00b67f09SDavid van Moolenbroek} 500