1 1.1 christos #!/usr/bin/perl 2 1.1.1.4 christos 3 1.1 christos # Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4 1.1 christos # 5 1.1.1.4 christos # SPDX-License-Identifier: MPL-2.0 6 1.1.1.4 christos # 7 1.1 christos # This Source Code Form is subject to the terms of the Mozilla Public 8 1.1.1.4 christos # License, v. 2.0. If a copy of the MPL was not distributed with this 9 1.1.1.3 christos # file, you can obtain one at https://mozilla.org/MPL/2.0/. 10 1.1 christos # 11 1.1 christos # See the COPYRIGHT file distributed with this work for additional 12 1.1 christos # information regarding copyright ownership. 13 1.1 christos 14 1.1 christos # 15 1.1 christos # This is the name server from hell. It provides canned 16 1.1 christos # responses based on pattern matching the queries, and 17 1.1 christos # can be reprogrammed on-the-fly over a TCP connection. 18 1.1 christos # 19 1.1 christos # The server listens for queries on port 5300 (or PORT). 20 1.1 christos # 21 1.1 christos # The server listens for control connections on port 5301 (or EXTRAPORT1). 22 1.1 christos # 23 1.1 christos # A control connection is a TCP stream of lines like 24 1.1 christos # 25 1.1 christos # /pattern/ 26 1.1 christos # name ttl type rdata 27 1.1 christos # name ttl type rdata 28 1.1 christos # ... 29 1.1 christos # /pattern/ 30 1.1 christos # name ttl type rdata 31 1.1 christos # name ttl type rdata 32 1.1 christos # ... 33 1.1 christos # 34 1.1 christos # There can be any number of patterns, each associated 35 1.1 christos # with any number of response RRs. Each pattern is a 36 1.1 christos # Perl regular expression. If an empty pattern ("//") is 37 1.1 christos # received, the server will ignore all incoming queries (TCP 38 1.1 christos # connections will still be accepted, but both UDP queries 39 1.1 christos # and TCP queries will not be responded to). If a non-empty 40 1.1 christos # pattern is then received over the same control connection, 41 1.1 christos # default behavior is restored. 42 1.1 christos # 43 1.1 christos # Each incoming query is converted into a string of the form 44 1.1 christos # "qname qtype" (the printable query domain name, space, 45 1.1 christos # printable query type) and matched against each pattern. 46 1.1 christos # 47 1.1 christos # The first pattern matching the query is selected, and 48 1.1 christos # the RR following the pattern line are sent in the 49 1.1 christos # answer section of the response. 50 1.1 christos # 51 1.1 christos # Each new control connection causes the current set of 52 1.1 christos # patterns and responses to be cleared before adding new 53 1.1 christos # ones. 54 1.1 christos # 55 1.1 christos # The server handles UDP and TCP queries. Zone transfer 56 1.1 christos # responses work, but must fit in a single 64 k message. 57 1.1 christos # 58 1.1 christos # Now you can add TSIG, just specify key/key data with: 59 1.1 christos # 60 1.1 christos # /pattern <key> <key_data>/ 61 1.1 christos # name ttl type rdata 62 1.1 christos # name ttl type rdata 63 1.1 christos # 64 1.1 christos # Note that this data will still be sent with any request for 65 1.1 christos # pattern, only this data will be signed. Currently, this is only 66 1.1 christos # done for TCP. 67 1.1.1.2 christos # 68 1.1.1.6 christos # /pattern NOTIMP <key> <key_data>/ 69 1.1.1.6 christos # /pattern NOTIMP/ 70 1.1.1.6 christos # 71 1.1.1.6 christos # Return a NOTIMP response 72 1.1.1.6 christos # 73 1.1.1.6 christos # /pattern EDNS=NOTIMP <key> <key_data>/ 74 1.1.1.6 christos # /pattern EDNS=NOTIMP/ 75 1.1.1.6 christos # 76 1.1.1.6 christos # Return a NOTIMP response to an EDNS request 77 1.1.1.6 christos # 78 1.1.1.6 christos # /pattern EDNS=FORMERR <key> <key_data>/ 79 1.1.1.6 christos # /pattern EDNS=FORMERR/ 80 1.1.1.6 christos # 81 1.1.1.6 christos # Return a FORMERR response to an EDNS request 82 1.1.1.6 christos # 83 1.1.1.2 christos # /pattern bad-id <key> <key_data>/ 84 1.1.1.2 christos # /pattern bad-id/ 85 1.1.1.2 christos # 86 1.1.1.2 christos # will add 50 to the message id of the response. 87 1.1 christos 88 1.1 christos 89 1.1 christos use IO::File; 90 1.1 christos use IO::Socket; 91 1.1 christos use Data::Dumper; 92 1.1 christos use Net::DNS; 93 1.1 christos use Net::DNS::Packet; 94 1.1 christos use strict; 95 1.1 christos 96 1.1 christos # Ignore SIGPIPE so we won't fail if peer closes a TCP socket early 97 1.1 christos local $SIG{PIPE} = 'IGNORE'; 98 1.1 christos 99 1.1 christos # Flush logged output after every line 100 1.1 christos local $| = 1; 101 1.1 christos 102 1.1 christos # We default to listening on 10.53.0.2 for historical reasons 103 1.1 christos # XXX: we should also be able to specify IPv6 104 1.1 christos my $server_addr = "10.53.0.2"; 105 1.1 christos if (@ARGV > 0) { 106 1.1 christos $server_addr = @ARGV[0]; 107 1.1 christos } 108 1.1 christos 109 1.1 christos my $mainport = int($ENV{'PORT'}); 110 1.1 christos if (!$mainport) { $mainport = 5300; } 111 1.1 christos my $ctrlport = int($ENV{'EXTRAPORT1'}); 112 1.1 christos if (!$ctrlport) { $ctrlport = 5301; } 113 1.1.1.6 christos my $hmac_algorithm = $ENV{'DEFAULT_HMAC'}; 114 1.1.1.6 christos if (!defined($hmac_algorithm)) { $hmac_algorithm = "hmac-sha256"; } 115 1.1 christos 116 1.1 christos # XXX: we should also be able to set the port numbers to listen on. 117 1.1 christos my $ctlsock = IO::Socket::INET->new(LocalAddr => "$server_addr", 118 1.1 christos LocalPort => $ctrlport, Proto => "tcp", Listen => 5, Reuse => 1) or die "$!"; 119 1.1 christos 120 1.1 christos my $udpsock = IO::Socket::INET->new(LocalAddr => "$server_addr", 121 1.1 christos LocalPort => $mainport, Proto => "udp", Reuse => 1) or die "$!"; 122 1.1 christos 123 1.1 christos my $tcpsock = IO::Socket::INET->new(LocalAddr => "$server_addr", 124 1.1 christos LocalPort => $mainport, Proto => "tcp", Listen => 5, Reuse => 1) or die "$!"; 125 1.1 christos 126 1.1 christos print "listening on $server_addr:$mainport,$ctrlport.\n"; 127 1.1 christos print "Using Net::DNS $Net::DNS::VERSION\n"; 128 1.1 christos 129 1.1 christos my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!"; 130 1.1 christos print $pidf "$$\n" or die "cannot write pid file: $!"; 131 1.1 christos $pidf->close or die "cannot close pid file: $!";; 132 1.1 christos sub rmpid { unlink "ans.pid"; exit 1; }; 133 1.1 christos 134 1.1 christos $SIG{INT} = \&rmpid; 135 1.1 christos $SIG{TERM} = \&rmpid; 136 1.1 christos 137 1.1 christos #my @answers = (); 138 1.1 christos my @rules; 139 1.1 christos my $udphandler; 140 1.1 christos my $tcphandler; 141 1.1 christos 142 1.1 christos sub handleUDP { 143 1.1 christos my ($buf) = @_; 144 1.1 christos my $request; 145 1.1 christos 146 1.1 christos if ($Net::DNS::VERSION > 0.68) { 147 1.1 christos $request = new Net::DNS::Packet(\$buf, 0); 148 1.1 christos $@ and die $@; 149 1.1 christos } else { 150 1.1 christos my $err; 151 1.1 christos ($request, $err) = new Net::DNS::Packet(\$buf, 0); 152 1.1 christos $err and die $err; 153 1.1 christos } 154 1.1 christos 155 1.1 christos my @questions = $request->question; 156 1.1 christos my $qname = $questions[0]->qname; 157 1.1 christos my $qtype = $questions[0]->qtype; 158 1.1 christos my $qclass = $questions[0]->qclass; 159 1.1 christos my $id = $request->header->id; 160 1.1 christos 161 1.1 christos my $packet = new Net::DNS::Packet($qname, $qtype, $qclass); 162 1.1 christos $packet->header->qr(1); 163 1.1 christos $packet->header->aa(1); 164 1.1 christos $packet->header->id($id); 165 1.1 christos 166 1.1 christos # get the existing signature if any, and clear the additional section 167 1.1 christos my $prev_tsig; 168 1.1 christos while (my $rr = $request->pop("additional")) { 169 1.1 christos $prev_tsig = $rr if ($rr->type eq "TSIG"); 170 1.1 christos } 171 1.1 christos 172 1.1 christos my $r; 173 1.1 christos foreach $r (@rules) { 174 1.1 christos my $pattern = $r->{pattern}; 175 1.1 christos my($dbtype, $key_name, $key_data) = split(/ /,$pattern); 176 1.1.1.5 christos print "[handleUDP] $dbtype, $key_name, $key_data\n"; 177 1.1 christos if ("$qname $qtype" =~ /$dbtype/) { 178 1.1 christos my $a; 179 1.1 christos foreach $a (@{$r->{answer}}) { 180 1.1 christos $packet->push("answer", $a); 181 1.1 christos } 182 1.1.1.5 christos if (defined($key_name) && defined($key_data)) { 183 1.1 christos my $tsig; 184 1.1 christos # Sign the packet 185 1.1 christos print " Signing the response with " . 186 1.1 christos "$key_name/$key_data\n"; 187 1.1 christos 188 1.1 christos if ($Net::DNS::VERSION < 0.69) { 189 1.1 christos $tsig = Net::DNS::RR->new( 190 1.1 christos "$key_name TSIG $key_data"); 191 1.1 christos } else { 192 1.1 christos $tsig = Net::DNS::RR->new( 193 1.1 christos name => $key_name, 194 1.1.1.6 christos algorithm => $hmac_algorithm, 195 1.1 christos type => 'TSIG', 196 1.1 christos key => $key_data); 197 1.1 christos } 198 1.1 christos 199 1.1 christos # These kluges are necessary because Net::DNS 200 1.1 christos # doesn't know how to sign responses. We 201 1.1 christos # clear compnames so that the TSIG key and 202 1.1 christos # algorithm name won't be compressed, and 203 1.1 christos # add one to arcount because the signing 204 1.1 christos # function will attempt to decrement it, 205 1.1 christos # which is incorrect in a response. Finally 206 1.1 christos # we set request_mac to the previous digest. 207 1.1 christos $packet->{"compnames"} = {} 208 1.1 christos if ($Net::DNS::VERSION < 0.70); 209 1.1 christos $packet->{"header"}{"arcount"} += 1 210 1.1 christos if ($Net::DNS::VERSION < 0.70); 211 1.1 christos if (defined($prev_tsig)) { 212 1.1 christos if ($Net::DNS::VERSION < 0.73) { 213 1.1 christos my $rmac = pack('n H*', 214 1.1 christos length($prev_tsig->mac)/2, 215 1.1 christos $prev_tsig->mac); 216 1.1 christos $tsig->{"request_mac"} = 217 1.1 christos unpack("H*", $rmac); 218 1.1 christos } else { 219 1.1 christos $tsig->request_mac( 220 1.1 christos $prev_tsig->mac); 221 1.1 christos } 222 1.1 christos } 223 1.1.1.5 christos 224 1.1 christos $packet->sign_tsig($tsig); 225 1.1 christos } 226 1.1 christos last; 227 1.1 christos } 228 1.1 christos } 229 1.1 christos #$packet->print; 230 1.1 christos 231 1.1 christos return $packet->data; 232 1.1 christos } 233 1.1 christos 234 1.1 christos # namelen: 235 1.1 christos # given a stream of data, reads a DNS-formatted name and returns its 236 1.1 christos # total length, thus making it possible to skip past it. 237 1.1 christos sub namelen { 238 1.1 christos my ($data) = @_; 239 1.1 christos my $len = 0; 240 1.1 christos my $label_len = 0; 241 1.1 christos do { 242 1.1 christos $label_len = unpack("c", $data); 243 1.1 christos $data = substr($data, $label_len + 1); 244 1.1 christos $len += $label_len + 1; 245 1.1 christos } while ($label_len != 0); 246 1.1 christos return ($len); 247 1.1 christos } 248 1.1 christos 249 1.1 christos # packetlen: 250 1.1 christos # given a stream of data, reads a DNS wire-format packet and returns 251 1.1 christos # its total length, making it possible to skip past it. 252 1.1 christos sub packetlen { 253 1.1 christos my ($data) = @_; 254 1.1 christos my $q; 255 1.1 christos my $rr; 256 1.1 christos my $header; 257 1.1 christos my $offset; 258 1.1 christos 259 1.1 christos # 260 1.1 christos # decode/encode were introduced in Net::DNS 0.68 261 1.1 christos # parse is no longer a method and calling it here makes perl croak. 262 1.1 christos # 263 1.1 christos my $decode = 0; 264 1.1 christos $decode = 1 if ($Net::DNS::VERSION >= 0.68); 265 1.1 christos 266 1.1 christos if ($decode) { 267 1.1 christos ($header, $offset) = Net::DNS::Header->decode(\$data); 268 1.1 christos } else { 269 1.1 christos ($header, $offset) = Net::DNS::Header->parse(\$data); 270 1.1 christos } 271 1.1.1.5 christos 272 1.1 christos for (1 .. $header->qdcount) { 273 1.1 christos if ($decode) { 274 1.1 christos ($q, $offset) = 275 1.1 christos Net::DNS::Question->decode(\$data, $offset); 276 1.1 christos } else { 277 1.1 christos ($q, $offset) = 278 1.1 christos Net::DNS::Question->parse(\$data, $offset); 279 1.1 christos } 280 1.1 christos } 281 1.1 christos for (1 .. $header->ancount) { 282 1.1 christos if ($decode) { 283 1.1 christos ($q, $offset) = Net::DNS::RR->decode(\$data, $offset); 284 1.1 christos } else { 285 1.1 christos ($q, $offset) = Net::DNS::RR->parse(\$data, $offset); 286 1.1 christos } 287 1.1 christos } 288 1.1 christos for (1 .. $header->nscount) { 289 1.1 christos if ($decode) { 290 1.1 christos ($q, $offset) = Net::DNS::RR->decode(\$data, $offset); 291 1.1 christos } else { 292 1.1 christos ($q, $offset) = Net::DNS::RR->parse(\$data, $offset); 293 1.1 christos } 294 1.1 christos } 295 1.1 christos for (1 .. $header->arcount) { 296 1.1 christos if ($decode) { 297 1.1 christos ($q, $offset) = Net::DNS::RR->decode(\$data, $offset); 298 1.1 christos } else { 299 1.1 christos ($q, $offset) = Net::DNS::RR->parse(\$data, $offset); 300 1.1 christos } 301 1.1 christos } 302 1.1 christos return $offset; 303 1.1 christos } 304 1.1 christos 305 1.1 christos # sign_tcp_continuation: 306 1.1 christos # This is a hack to correct the problem that Net::DNS has no idea how 307 1.1 christos # to sign multiple-message TCP responses. Several data that are included 308 1.1 christos # in the digest when signing a query or the first message of a response are 309 1.1 christos # omitted when signing subsequent messages in a TCP stream. 310 1.1 christos # 311 1.1 christos # Net::DNS::Packet->sign_tsig() has the ability to use a custom signing 312 1.1 christos # function (specified by calling Packet->sign_func()). We use this 313 1.1 christos # function as the signing function for TCP continuations, and it removes 314 1.1 christos # the unwanted data from the digest before calling the default sign_hmac 315 1.1 christos # function. 316 1.1 christos sub sign_tcp_continuation { 317 1.1 christos my ($key, $data) = @_; 318 1.1 christos 319 1.1 christos # copy out first two bytes: size of the previous MAC 320 1.1 christos my $rmacsize = unpack("n", $data); 321 1.1 christos $data = substr($data, 2); 322 1.1 christos 323 1.1 christos # copy out previous MAC 324 1.1 christos my $rmac = substr($data, 0, $rmacsize); 325 1.1 christos $data = substr($data, $rmacsize); 326 1.1 christos 327 1.1 christos # try parsing out the packet information 328 1.1 christos my $plen = packetlen($data); 329 1.1 christos my $pdata = substr($data, 0, $plen); 330 1.1 christos $data = substr($data, $plen); 331 1.1 christos 332 1.1 christos # remove the keyname, ttl, class, and algorithm name 333 1.1 christos $data = substr($data, namelen($data)); 334 1.1 christos $data = substr($data, 6); 335 1.1 christos $data = substr($data, namelen($data)); 336 1.1 christos 337 1.1 christos # preserve the TSIG data 338 1.1 christos my $tdata = substr($data, 0, 8); 339 1.1 christos 340 1.1 christos # prepare a new digest and sign with it 341 1.1 christos $data = pack("n", $rmacsize) . $rmac . $pdata . $tdata; 342 1.1 christos return Net::DNS::RR::TSIG::sign_hmac($key, $data); 343 1.1 christos } 344 1.1 christos 345 1.1 christos sub handleTCP { 346 1.1 christos my ($buf) = @_; 347 1.1 christos my $request; 348 1.1 christos 349 1.1 christos if ($Net::DNS::VERSION > 0.68) { 350 1.1 christos $request = new Net::DNS::Packet(\$buf, 0); 351 1.1 christos $@ and die $@; 352 1.1 christos } else { 353 1.1 christos my $err; 354 1.1 christos ($request, $err) = new Net::DNS::Packet(\$buf, 0); 355 1.1 christos $err and die $err; 356 1.1 christos } 357 1.1.1.5 christos 358 1.1 christos my @questions = $request->question; 359 1.1 christos my $qname = $questions[0]->qname; 360 1.1 christos my $qtype = $questions[0]->qtype; 361 1.1 christos my $qclass = $questions[0]->qclass; 362 1.1 christos my $id = $request->header->id; 363 1.1.1.6 christos my @additional = $request->additional; 364 1.1.1.6 christos my $has_opt = 0; 365 1.1.1.6 christos foreach (@additional) { 366 1.1.1.6 christos $has_opt = 1 if (ref($_) eq 'Net::DNS::RR::OPT'); 367 1.1.1.6 christos } 368 1.1 christos 369 1.1 christos my $opaque; 370 1.1 christos 371 1.1 christos my $packet = new Net::DNS::Packet($qname, $qtype, $qclass); 372 1.1 christos $packet->header->qr(1); 373 1.1 christos $packet->header->aa(1); 374 1.1 christos $packet->header->id($id); 375 1.1 christos 376 1.1 christos # get the existing signature if any, and clear the additional section 377 1.1 christos my $prev_tsig; 378 1.1 christos my $signer; 379 1.1 christos my $continuation = 0; 380 1.1 christos if ($Net::DNS::VERSION < 0.81) { 381 1.1 christos while (my $rr = $request->pop("additional")) { 382 1.1 christos if ($rr->type eq "TSIG") { 383 1.1 christos $prev_tsig = $rr; 384 1.1 christos } 385 1.1 christos } 386 1.1 christos } 387 1.1 christos 388 1.1 christos my @results = (); 389 1.1 christos my $count_these = 0; 390 1.1 christos 391 1.1 christos my $r; 392 1.1 christos foreach $r (@rules) { 393 1.1 christos my $pattern = $r->{pattern}; 394 1.1.1.5 christos my($dbtype, $key_name, $key_data, $tname) = split(/ /,$pattern); 395 1.1.1.5 christos print "[handleTCP] $dbtype, $key_name, $key_data, $tname \n"; 396 1.1 christos if ("$qname $qtype" =~ /$dbtype/) { 397 1.1 christos $count_these++; 398 1.1 christos my $a; 399 1.1.1.6 christos my $done = 0; 400 1.1.1.6 christos 401 1.1.1.6 christos while (defined($key_name) && 402 1.1.1.6 christos ($key_name eq "NOTIMP" || $key_name eq "EDNS=NOTIMP" || 403 1.1.1.6 christos $key_name eq "EDNS=FORMERR" || $key_name eq "bad-id")) { 404 1.1.1.6 christos 405 1.1.1.6 christos if (defined($key_name) && $key_name eq "NOTIMP") { 406 1.1.1.6 christos $packet->header->rcode('NOTIMP') if (!$done); 407 1.1.1.6 christos $key_name = $key_data; 408 1.1.1.6 christos ($key_data, $tname) = split(/ /,$tname); 409 1.1.1.6 christos $done = 1; 410 1.1.1.6 christos } 411 1.1.1.6 christos 412 1.1.1.6 christos if (defined($key_name) && $key_name eq "EDNS=NOTIMP") { 413 1.1.1.6 christos if ($has_opt) { 414 1.1.1.6 christos $packet->header->rcode('NOTIMP') if (!$done); 415 1.1.1.6 christos $done = 1; 416 1.1.1.6 christos } 417 1.1.1.6 christos $key_name = $key_data; 418 1.1.1.6 christos ($key_data, $tname) = split(/ /,$tname); 419 1.1.1.6 christos } 420 1.1.1.6 christos 421 1.1.1.6 christos if (defined($key_name) && $key_name eq "EDNS=FORMERR") { 422 1.1.1.6 christos if ($has_opt) { 423 1.1.1.6 christos $packet->header->rcode('FORMERR') if (!$done); 424 1.1.1.6 christos $done = 1; 425 1.1.1.6 christos } 426 1.1.1.6 christos $key_name = $key_data; 427 1.1.1.6 christos ($key_data, $tname) = split(/ /,$tname); 428 1.1.1.6 christos } 429 1.1.1.6 christos 430 1.1.1.6 christos if (defined($key_name) && $key_name eq "bad-id") { 431 1.1.1.6 christos $packet->header->id(($id+50)%0xffff); 432 1.1.1.6 christos $key_name = $key_data; 433 1.1.1.6 christos ($key_data, $tname) = split(/ /,$tname); 434 1.1.1.6 christos } 435 1.1 christos } 436 1.1.1.6 christos 437 1.1.1.6 christos if (!$done) { 438 1.1.1.6 christos foreach $a (@{$r->{answer}}) { 439 1.1.1.6 christos $packet->push("answer", $a); 440 1.1.1.6 christos } 441 1.1.1.2 christos } 442 1.1.1.6 christos 443 1.1 christos if (defined($key_name) && defined($key_data)) { 444 1.1 christos my $tsig; 445 1.1 christos # sign the packet 446 1.1.1.5 christos print " Signing the data with " . 447 1.1 christos "$key_name/$key_data\n"; 448 1.1 christos 449 1.1 christos if ($Net::DNS::VERSION < 0.69) { 450 1.1 christos $tsig = Net::DNS::RR->new( 451 1.1 christos "$key_name TSIG $key_data"); 452 1.1.1.6 christos $tsig->algorithm = $hmac_algorithm; 453 1.1 christos } elsif ($Net::DNS::VERSION >= 0.81 && 454 1.1 christos $continuation) { 455 1.1 christos } elsif ($Net::DNS::VERSION >= 0.75 && 456 1.1 christos $continuation) { 457 1.1 christos $tsig = $prev_tsig; 458 1.1 christos } else { 459 1.1 christos $tsig = Net::DNS::RR->new( 460 1.1 christos name => $key_name, 461 1.1.1.6 christos algorithm => $hmac_algorithm, 462 1.1 christos type => 'TSIG', 463 1.1 christos key => $key_data); 464 1.1 christos } 465 1.1 christos 466 1.1 christos # These kluges are necessary because Net::DNS 467 1.1 christos # doesn't know how to sign responses. We 468 1.1 christos # clear compnames so that the TSIG key and 469 1.1 christos # algorithm name won't be compressed, and 470 1.1 christos # add one to arcount because the signing 471 1.1 christos # function will attempt to decrement it, 472 1.1 christos # which is incorrect in a response. Finally 473 1.1 christos # we set request_mac to the previous digest. 474 1.1 christos $packet->{"compnames"} = {} 475 1.1 christos if ($Net::DNS::VERSION < 0.70); 476 1.1 christos $packet->{"header"}{"arcount"} += 1 477 1.1 christos if ($Net::DNS::VERSION < 0.70); 478 1.1 christos if (defined($prev_tsig)) { 479 1.1 christos if ($Net::DNS::VERSION < 0.73) { 480 1.1 christos my $rmac = pack('n H*', 481 1.1 christos length($prev_tsig->mac)/2, 482 1.1 christos $prev_tsig->mac); 483 1.1 christos $tsig->{"request_mac"} = 484 1.1 christos unpack("H*", $rmac); 485 1.1 christos } elsif ($Net::DNS::VERSION < 0.81) { 486 1.1 christos $tsig->request_mac( 487 1.1 christos $prev_tsig->mac); 488 1.1 christos } 489 1.1 christos } 490 1.1.1.5 christos 491 1.1 christos $tsig->sign_func($signer) if defined($signer); 492 1.1 christos $tsig->continuation($continuation) if 493 1.1 christos ($Net::DNS::VERSION >= 0.71 && 494 1.1 christos $Net::DNS::VERSION <= 0.74 ); 495 1.1 christos if ($Net::DNS::VERSION < 0.81) { 496 1.1 christos $packet->sign_tsig($tsig); 497 1.1 christos } elsif ($continuation) { 498 1.1 christos $opaque = $packet->sign_tsig($opaque); 499 1.1 christos } else { 500 1.1 christos $opaque = $packet->sign_tsig($request); 501 1.1 christos } 502 1.1 christos $signer = \&sign_tcp_continuation 503 1.1 christos if ($Net::DNS::VERSION < 0.70); 504 1.1 christos $continuation = 1; 505 1.1 christos 506 1.1 christos my $copy = 507 1.1 christos Net::DNS::Packet->new(\($packet->data)); 508 1.1 christos $prev_tsig = $copy->pop("additional"); 509 1.1 christos } 510 1.1 christos #$packet->print; 511 1.1 christos push(@results,$packet->data); 512 1.1.1.6 christos last if ($done); 513 1.1.1.5 christos if ($tname eq "") { 514 1.1.1.5 christos $tname = $qname; 515 1.1.1.5 christos } 516 1.1.1.5 christos $packet = new Net::DNS::Packet($tname, $qtype, $qclass); 517 1.1 christos $packet->header->qr(1); 518 1.1 christos $packet->header->aa(1); 519 1.1 christos $packet->header->id($id); 520 1.1 christos } 521 1.1 christos } 522 1.1 christos print " A total of $count_these patterns matched\n"; 523 1.1 christos return \@results; 524 1.1 christos } 525 1.1 christos 526 1.1 christos # Main 527 1.1 christos my $rin; 528 1.1 christos my $rout; 529 1.1 christos for (;;) { 530 1.1 christos $rin = ''; 531 1.1 christos vec($rin, fileno($ctlsock), 1) = 1; 532 1.1 christos vec($rin, fileno($tcpsock), 1) = 1; 533 1.1 christos vec($rin, fileno($udpsock), 1) = 1; 534 1.1 christos 535 1.1 christos select($rout = $rin, undef, undef, undef); 536 1.1 christos 537 1.1 christos if (vec($rout, fileno($ctlsock), 1)) { 538 1.1 christos warn "ctl conn"; 539 1.1 christos my $conn = $ctlsock->accept; 540 1.1 christos my $rule = (); 541 1.1 christos @rules = (); 542 1.1 christos while (my $line = $conn->getline) { 543 1.1 christos chomp $line; 544 1.1 christos if ($line =~ m!^/(.*)/$!) { 545 1.1 christos if (length($1) == 0) { 546 1.1 christos $udphandler = sub { return; }; 547 1.1 christos $tcphandler = sub { return; }; 548 1.1 christos } else { 549 1.1 christos $udphandler = \&handleUDP; 550 1.1 christos $tcphandler = \&handleTCP; 551 1.1 christos $rule = { pattern => $1, answer => [] }; 552 1.1 christos push(@rules, $rule); 553 1.1 christos } 554 1.1 christos } else { 555 1.1 christos push(@{$rule->{answer}}, 556 1.1 christos new Net::DNS::RR($line)); 557 1.1 christos } 558 1.1 christos } 559 1.1 christos $conn->close; 560 1.1 christos #print Dumper(@rules); 561 1.1 christos #print "+=+=+ $rules[0]->{'pattern'}\n"; 562 1.1 christos #print "+=+=+ $rules[0]->{'answer'}->[0]->{'rname'}\n"; 563 1.1 christos #print "+=+=+ $rules[0]->{'answer'}->[0]\n"; 564 1.1 christos } elsif (vec($rout, fileno($udpsock), 1)) { 565 1.1 christos printf "UDP request\n"; 566 1.1 christos my $buf; 567 1.1 christos $udpsock->recv($buf, 512); 568 1.1 christos my $result = &$udphandler($buf); 569 1.1 christos if (defined($result)) { 570 1.1 christos my $num_chars = $udpsock->send($result); 571 1.1 christos print " Sent $num_chars bytes via UDP\n"; 572 1.1 christos } 573 1.1 christos } elsif (vec($rout, fileno($tcpsock), 1)) { 574 1.1 christos my $conn = $tcpsock->accept; 575 1.1 christos my $buf; 576 1.1 christos for (;;) { 577 1.1 christos my $lenbuf; 578 1.1 christos my $n = $conn->sysread($lenbuf, 2); 579 1.1 christos last unless $n == 2; 580 1.1 christos my $len = unpack("n", $lenbuf); 581 1.1 christos $n = $conn->sysread($buf, $len); 582 1.1 christos last unless $n == $len; 583 1.1 christos print "TCP request\n"; 584 1.1 christos my $result = &$tcphandler($buf); 585 1.1 christos if (defined($result)) { 586 1.1 christos foreach my $response (@$result) { 587 1.1 christos $len = length($response); 588 1.1 christos $n = $conn->syswrite(pack("n", $len), 2); 589 1.1 christos $n = $conn->syswrite($response, $len); 590 1.1 christos print " Sent: $n chars via TCP\n"; 591 1.1 christos } 592 1.1 christos } 593 1.1 christos } 594 1.1 christos $conn->close; 595 1.1 christos } 596 1.1 christos } 597