1 #!/usr/bin/env perl 2 3 # Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4 # 5 # SPDX-License-Identifier: MPL-2.0 6 # 7 # This Source Code Form is subject to the terms of the Mozilla Public 8 # License, v. 2.0. If a copy of the MPL was not distributed with this 9 # file, you can obtain one at https://mozilla.org/MPL/2.0/. 10 # 11 # See the COPYRIGHT file distributed with this work for additional 12 # information regarding copyright ownership. 13 14 use strict; 15 use warnings; 16 17 use IO::File; 18 use IO::Socket; 19 use Getopt::Long; 20 use Net::DNS; 21 use Time::HiRes qw(usleep nanosleep); 22 23 my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!"; 24 print $pidf "$$\n" or die "cannot write pid file: $!"; 25 $pidf->close or die "cannot close pid file: $!"; 26 sub rmpid { unlink "ans.pid"; exit 1; }; 27 28 $SIG{INT} = \&rmpid; 29 $SIG{TERM} = \&rmpid; 30 31 # If send_response is set, the server will respond, otherwise the query will 32 # be dropped. 33 my $send_response = 1; 34 # If slow_response is set, a lookup for the CNAME target (target.example) is 35 # delayed. Other lookups will not be delayed. 36 my $slow_response = 0; 37 38 my $localaddr = "10.53.0.2"; 39 40 my $localport = int($ENV{'PORT'}); 41 if (!$localport) { $localport = 5300; } 42 43 my $udpsock = IO::Socket::INET->new(LocalAddr => "$localaddr", 44 LocalPort => $localport, Proto => "udp", Reuse => 1) or die "$!"; 45 46 # 47 # Delegations 48 # 49 my $SOA = "example 300 IN SOA . . 0 0 0 0 300"; 50 my $NS = "example 300 IN NS ns.example"; 51 my $A = "ns.example 300 IN A $localaddr"; 52 my $ssSOA = "delegated.serve.stale 300 IN SOA . . 0 0 0 0 300"; 53 my $ssNS = "delegated.serve.stale 300 IN NS ns.delegated.serve.stale"; 54 my $ssA = "ns.delegated.serve.stale 300 IN A $localaddr"; 55 56 # 57 # Slow delegation 58 # 59 my $slowSOA = "slow 300 IN SOA . . 0 0 0 0 300"; 60 my $slowNS = "slow 300 IN NS ns.slow"; 61 my $slowA = "ns.slow 300 IN A $localaddr"; 62 my $slowTXT = "data.slow 2 IN TXT \"A slow text record with a 2 second ttl\""; 63 my $slownegSOA = "slow 2 IN SOA . . 0 0 0 0 300"; 64 65 # 66 # Records to be TTL stretched 67 # 68 my $TXT = "data.example 2 IN TXT \"A text record with a 2 second ttl\""; 69 my $LONGTXT = "longttl.example 600 IN TXT \"A text record with a 600 second ttl\""; 70 my $CAA = "othertype.example 2 IN CAA 0 issue \"ca1.example.net\""; 71 my $negSOA = "example 2 IN SOA . . 0 0 0 0 300"; 72 my $ssnegSOA = "delegated.serve.stale 2 IN SOA . . 0 0 0 0 300"; 73 my $CNAME = "cname.example 7 IN CNAME target.example"; 74 my $TARGET = "target.example 9 IN A $localaddr"; 75 my $SHORTCNAME = "shortttl.cname.example 1 IN CNAME longttl.target.example"; 76 my $LONGTARGET = "longttl.target.example 600 IN A $localaddr"; 77 78 sub reply_handler { 79 my ($qname, $qclass, $qtype) = @_; 80 my ($rcode, @ans, @auth, @add); 81 82 print ("request: $qname/$qtype\n"); 83 STDOUT->flush(); 84 85 # Control whether we send a response or not. 86 # We always respond to control commands. 87 if ($qname eq "enable" ) { 88 if ($qtype eq "TXT") { 89 $send_response = 1; 90 my $rr = new Net::DNS::RR("$qname 0 $qclass TXT \"$send_response\""); 91 push @ans, $rr; 92 } 93 $rcode = "NOERROR"; 94 return ($rcode, \@ans, \@auth, \@add, { aa => 1 }); 95 } elsif ($qname eq "disable" ) { 96 if ($qtype eq "TXT") { 97 $send_response = 0; 98 my $rr = new Net::DNS::RR("$qname 0 $qclass TXT \"$send_response\""); 99 push @ans, $rr; 100 } 101 $rcode = "NOERROR"; 102 return ($rcode, \@ans, \@auth, \@add, { aa => 1 }); 103 } elsif ($qname eq "slowdown" ) { 104 if ($qtype eq "TXT") { 105 $send_response = 1; 106 $slow_response = 1; 107 my $rr = new Net::DNS::RR("$qname 0 $qclass TXT \"$send_response\""); 108 push @ans, $rr; 109 } 110 $rcode = "NOERROR"; 111 return ($rcode, \@ans, \@auth, \@add, { aa => 1 }); 112 } 113 114 # If we are not responding to queries we are done. 115 return if (!$send_response); 116 117 if (index($qname, "latency") == 0) { 118 # simulate network latency before answering 119 print " Sleeping 50 milliseconds\n"; 120 select(undef, undef, undef, 0.05); 121 } 122 123 # Construct the response and send it. 124 if ($qname eq "ns.example" ) { 125 if ($qtype eq "A") { 126 my $rr = new Net::DNS::RR($A); 127 push @ans, $rr; 128 } else { 129 my $rr = new Net::DNS::RR($SOA); 130 push @auth, $rr; 131 } 132 $rcode = "NOERROR"; 133 } elsif ($qname eq "example") { 134 if ($qtype eq "NS") { 135 my $rr = new Net::DNS::RR($NS); 136 push @auth, $rr; 137 $rr = new Net::DNS::RR($A); 138 push @add, $rr; 139 } elsif ($qtype eq "SOA") { 140 my $rr = new Net::DNS::RR($SOA); 141 push @ans, $rr; 142 } else { 143 my $rr = new Net::DNS::RR($SOA); 144 push @auth, $rr; 145 } 146 $rcode = "NOERROR"; 147 } elsif ($qname eq "nodata.example") { 148 my $rr = new Net::DNS::RR($negSOA); 149 push @auth, $rr; 150 $rcode = "NOERROR"; 151 } elsif ($qname eq "data.example") { 152 if ($qtype eq "TXT") { 153 my $rr = new Net::DNS::RR($TXT); 154 push @ans, $rr; 155 } else { 156 my $rr = new Net::DNS::RR($negSOA); 157 push @auth, $rr; 158 } 159 $rcode = "NOERROR"; 160 } elsif ($qname eq "a-only.example") { 161 if ($qtype eq "A") { 162 my $rr = new Net::DNS::RR("a-only.example 2 IN A $localaddr"); 163 push @ans, $rr; 164 } else { 165 my $rr = new Net::DNS::RR($negSOA); 166 push @auth, $rr; 167 } 168 $rcode = "NOERROR"; 169 } elsif ($qname eq "cname.example") { 170 if ($qtype eq "A") { 171 my $rr = new Net::DNS::RR($CNAME); 172 push @ans, $rr; 173 } else { 174 my $rr = new Net::DNS::RR($negSOA); 175 push @auth, $rr; 176 } 177 $rcode = "NOERROR"; 178 } elsif ($qname eq "target.example") { 179 if ($slow_response) { 180 print " Sleeping 3 seconds\n"; 181 sleep(3); 182 } 183 if ($qtype eq "A") { 184 my $rr = new Net::DNS::RR($TARGET); 185 push @ans, $rr; 186 } else { 187 my $rr = new Net::DNS::RR($negSOA); 188 push @auth, $rr; 189 } 190 $rcode = "NOERROR"; 191 } elsif ($qname eq "shortttl.cname.example") { 192 my $rr = new Net::DNS::RR($SHORTCNAME); 193 push @ans, $rr; 194 $rcode = "NOERROR"; 195 } elsif ($qname eq "longttl.target.example") { 196 if ($slow_response) { 197 print " Sleeping 3 seconds\n"; 198 sleep(3); 199 } 200 if ($qtype eq "A") { 201 my $rr = new Net::DNS::RR($LONGTARGET); 202 push @ans, $rr; 203 } else { 204 my $rr = new Net::DNS::RR($negSOA); 205 push @auth, $rr; 206 } 207 $rcode = "NOERROR"; 208 } elsif ($qname eq "longttl.example") { 209 if ($qtype eq "TXT") { 210 my $rr = new Net::DNS::RR($LONGTXT); 211 push @ans, $rr; 212 } else { 213 my $rr = new Net::DNS::RR($negSOA); 214 push @auth, $rr; 215 } 216 $rcode = "NOERROR"; 217 } elsif ($qname eq "nxdomain.example") { 218 my $rr = new Net::DNS::RR($negSOA); 219 push @auth, $rr; 220 $rcode = "NXDOMAIN"; 221 } elsif ($qname eq "othertype.example") { 222 if ($qtype eq "CAA") { 223 my $rr = new Net::DNS::RR($CAA); 224 push @ans, $rr; 225 } else { 226 my $rr = new Net::DNS::RR($negSOA); 227 push @auth, $rr; 228 } 229 $rcode = "NOERROR"; 230 } elsif ($qname eq "ns.delegated.serve.stale" ) { 231 if ($qtype eq "A") { 232 my $rr = new Net::DNS::RR($ssA); 233 push @ans, $rr; 234 } else { 235 my $rr = new Net::DNS::RR($ssSOA); 236 push @auth, $rr; 237 } 238 $rcode = "NOERROR"; 239 } elsif ($qname eq "delegated.serve.stale") { 240 if ($qtype eq "NS") { 241 my $rr = new Net::DNS::RR($ssNS); 242 push @auth, $rr; 243 $rr = new Net::DNS::RR($ssA); 244 push @add, $rr; 245 } elsif ($qtype eq "SOA") { 246 my $rr = new Net::DNS::RR($ssSOA); 247 push @ans, $rr; 248 } else { 249 my $rr = new Net::DNS::RR($ssSOA); 250 push @auth, $rr; 251 } 252 $rcode = "NOERROR"; 253 } elsif ($qname eq "www.delegated.serve.stale") { 254 if ($qtype eq "A") { 255 my $rr = new Net::DNS::RR("www.delegated.serve.stale 2 IN A 10.53.0.99"); 256 push @ans, $rr; 257 } else { 258 my $rr = new Net::DNS::RR($ssnegSOA); 259 push @auth, $rr; 260 } 261 $rcode = "NOERROR"; 262 } elsif ($qname eq "cname.delegated.serve.stale") { 263 if ($qtype eq "A") { 264 my $rr = new Net::DNS::RR("cname.delegated.serve.stale 2 IN CNAME cname-target.serve.stale."); 265 push @ans, $rr; 266 } else { 267 my $rr = new Net::DNS::RR($ssnegSOA); 268 push @auth, $rr; 269 } 270 $rcode = "NOERROR"; 271 } elsif ($qname eq "ns.slow" ) { 272 if ($qtype eq "A") { 273 my $rr = new Net::DNS::RR($slowA); 274 push @ans, $rr; 275 } else { 276 my $rr = new Net::DNS::RR($slowSOA); 277 push @auth, $rr; 278 } 279 $rcode = "NOERROR"; 280 } elsif ($qname eq "slow") { 281 if ($qtype eq "NS") { 282 my $rr = new Net::DNS::RR($slowNS); 283 push @auth, $rr; 284 $rr = new Net::DNS::RR($slowA); 285 push @add, $rr; 286 } elsif ($qtype eq "SOA") { 287 my $rr = new Net::DNS::RR($slowSOA); 288 push @ans, $rr; 289 } else { 290 my $rr = new Net::DNS::RR($slowSOA); 291 push @auth, $rr; 292 } 293 $rcode = "NOERROR"; 294 } elsif ($qname eq "data.slow") { 295 if ($slow_response) { 296 print " Sleeping 3 seconds\n"; 297 sleep(3); 298 # only one time 299 $slow_response = 0; 300 } 301 if ($qtype eq "TXT") { 302 my $rr = new Net::DNS::RR($slowTXT); 303 push @ans, $rr; 304 } else { 305 my $rr = new Net::DNS::RR($slownegSOA); 306 push @auth, $rr; 307 } 308 $rcode = "NOERROR"; 309 } else { 310 my $rr = new Net::DNS::RR($SOA); 311 push @auth, $rr; 312 $rcode = "NXDOMAIN"; 313 } 314 315 # mark the answer as authoritative (by setting the 'aa' flag) 316 return ($rcode, \@ans, \@auth, \@add, { aa => 1 }); 317 } 318 319 GetOptions( 320 'port=i' => \$localport, 321 ); 322 323 my $rin; 324 my $rout; 325 326 for (;;) { 327 $rin = ''; 328 vec($rin, fileno($udpsock), 1) = 1; 329 330 select($rout = $rin, undef, undef, undef); 331 332 if (vec($rout, fileno($udpsock), 1)) { 333 my ($buf, $request, $err); 334 $udpsock->recv($buf, 512); 335 336 if ($Net::DNS::VERSION > 0.68) { 337 $request = new Net::DNS::Packet(\$buf, 0); 338 $@ and die $@; 339 } else { 340 my $err; 341 ($request, $err) = new Net::DNS::Packet(\$buf, 0); 342 $err and die $err; 343 } 344 345 my @questions = $request->question; 346 my $qname = $questions[0]->qname; 347 my $qclass = $questions[0]->qclass; 348 my $qtype = $questions[0]->qtype; 349 my $id = $request->header->id; 350 351 my ($rcode, $ans, $auth, $add, $headermask) = reply_handler($qname, $qclass, $qtype); 352 353 if (!defined($rcode)) { 354 print " Silently ignoring query\n"; 355 next; 356 } 357 358 my $reply = Net::DNS::Packet->new(); 359 $reply->header->qr(1); 360 $reply->header->aa(1) if $headermask->{'aa'}; 361 $reply->header->id($id); 362 $reply->header->rcode($rcode); 363 $reply->push("question", @questions); 364 $reply->push("answer", @$ans) if $ans; 365 $reply->push("authority", @$auth) if $auth; 366 $reply->push("additional", @$add) if $add; 367 368 my $num_chars = $udpsock->send($reply->data); 369 print " Sent $num_chars bytes via UDP\n"; 370 } 371 } 372