xref: /minix3/external/bsd/bind/dist/bin/tests/system/ans.pl (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
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