Mercurial > hg > nginx-tests
changeset 360:82db241184e0
Tests: added AAAA specific resolver tests.
author | Sergey Kandaurov <pluknet@nginx.com> |
---|---|
date | Sat, 14 Dec 2013 00:02:33 +0400 |
parents | cdab739eb6ea |
children | aac06d3bdc05 |
files | http_resolver_aaaa.t |
diffstat | 1 files changed, 634 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/http_resolver_aaaa.t Sat Dec 14 00:02:33 2013 +0400 @@ -0,0 +1,634 @@ +#!/usr/bin/perl + +# (C) Sergey Kandaurov +# (C) Nginx, Inc. + +# Tests for AAAA capable http resolver. + +############################################################################### + +use warnings; +use strict; + +use Test::More; + +BEGIN { use FindBin; chdir($FindBin::Bin); } + +use lib 'lib'; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->has(qw/http proxy rewrite ipv6/); + +plan(skip_all => 'no ipv6 capable resolver') unless $t->has_version('1.5.8'); + +$t->write_file_expand('nginx.conf', <<'EOF'); + +%%TEST_GLOBALS%% + +daemon off; + +events { +} + +http { + %%TEST_GLOBALS_HTTP%% + + server { + listen 127.0.0.1:8080; + listen [::1]:8080; + server_name localhost; + + location / { + resolver 127.0.0.1:8081; + proxy_pass http://$host:8080/backend; + + proxy_next_upstream http_504 timeout error; + proxy_intercept_errors on; + proxy_connect_timeout 50ms; + error_page 504 502 /50x; + } + location /two { + resolver 127.0.0.1:8081 127.0.0.1:8082; + proxy_pass http://$host:8080/backend; + } + + location /backend { + return 200; + } + location /50x { + return 200 $upstream_addr; + } + } +} + +EOF + +eval { + open OLDERR, ">&", \*STDERR; close STDERR; + $t->run(); + open STDERR, ">&", \*OLDERR; +}; +plan(skip_all => 'no inet6 support') if $@; + +$t->run_daemon(\&dns_daemon, 8081, $t); +$t->run_daemon(\&dns_daemon, 8082, $t); + +$t->waitforfile($t->testdir . '/8081'); +$t->waitforfile($t->testdir . '/8082'); + +$t->plan(72); + +############################################################################### + +my (@n, $response); + +like(http_host_header('aaaa.example.net', '/'), qr/\[fe80::1\]/, 'AAAA'); +like(http_host_header('cname.example.net', '/'), qr/\[fe80::1\]/, 'CNAME'); +like(http_host_header('cname.example.net', '/'), qr/\[fe80::1\]/, + 'CNAME cached'); + +# CNAME + AAAA combined answer +# demonstrates the name in answer section different from what is asked + +like(http_host_header('cname_a.example.net', '/'), qr/200 OK/, 'CNAME + AAAA'); + +# many AAAA records in round robin +# nonexisting IPs enumerated with proxy_next_upstream + +like(http_host_header('many.example.net', '/'), + qr/^\[fe80::(1\]:8080, \[fe80::2\]:8080|2\]:8080, \[fe80::1\]:8080)$/m, + 'AAAA many'); + +like(http_host_header('many.example.net', '/'), + qr/^\[fe80::(1\]:8080, \[fe80::2\]:8080|2\]:8080, \[fe80::1\]:8080)$/m, + 'AAAA many cached'); + +# tests for several resolvers specified in directive +# query bad ns, make sure that error responses are not cached + +like(http_host_header('2.example.net', '/two'), qr/502 Bad/, 'two ns bad'); + +# now get correct response + +like(http_host_header('2.example.net', '/two'), qr/200 OK/, 'two ns good'); + +# response is cached, actual request would get error + +like(http_host_header('2.example.net', '/two'), qr/200 OK/, 'two ns cached'); + +# various ipv4/ipv6 combinations + +$response = http_host_header('z_z.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'zero zero responses'); +like($response, qr/502 Bad/, 'zero zero'); + +like(http_host_header('z_n.example.net', '/'), qr/^\[fe80::1\]:8080$/ms, + 'zero AAAA'); + +$response = http_host_header('z_c.example.net', '/'); +is(@n = $response =~ /8080/g, 2, 'zero CNAME responses'); +like($response, qr/127.0.0.201:8080/, 'zero CNAME 1'); +like($response, qr/\[fe80::1\]:8080/, 'zero CNAME 2'); + +$response = http_host_header('z_cn.example.net', '/'); +is(@n = $response =~ /8080/g, 2, 'zero CNAME+AAAA responses'); +like($response, qr/\[fe80::1\]:8080/, 'zero CNAME+AAAA 1'); +like($response, qr/\[fe80::2\]:8080/, 'zero CNAME+AAAA 2'); + +$response = http_host_header('z_e.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'zero error responses'); +like($response, qr/502 Bad/, 'zero error'); + +like(http_host_header('n_z.example.net', '/'), qr/^127.0.0.201:8080$/ms, + 'A zero'); + +$response = http_host_header('n_n.example.net', '/'); +is(@n = $response =~ /8080/g, 2, 'A AAAA responses'); +like($response, qr/127.0.0.201:8080/, 'A AAAA 1'); +like($response, qr/\[fe80::1\]:8080/, 'A AAAA 2'); + +like(http_host_header('n_c.example.net', '/'), qr/^127.0.0.201:8080$/ms, + 'A CNAME'); + +$response = http_host_header('n_cn.example.net', '/'); +is(@n = $response =~ /8080/g, 4, 'A CNAME+AAAA responses'); +like($response, qr/127.0.0.201:8080/, 'A CNAME+AAAA 1'); +like($response, qr/127.0.0.202:8080/, 'A CNAME+AAAA 2'); +like($response, qr/\[fe80::1\]:8080/, 'A CNAME+AAAA 3'); +like($response, qr/\[fe80::2\]:8080/, 'A CNAME+AAAA 4'); + +$response = http_host_header('n_e.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'A error responses'); +like($response, qr/502 Bad/, 'A error'); + +$response = http_host_header('c_z.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'CNAME zero responses'); +like($response, qr/502 Bad/, 'CNAME zero'); + +like(http_host_header('c_n.example.net', '/'), qr/^\[fe80::1\]:8080$/ms, + 'CNAME AAAA'); + +$response = http_host_header('c_c.example.net', '/'); +is(@n = $response =~ /8080/g, 2, 'CNAME CNAME responses'); +like($response, qr/127.0.0.201:8080/, 'CNAME CNAME 1'); +like($response, qr/\[fe80::1\]:8080/, 'CNAME CNAME 2'); + +like(http_host_header('c1_c2.example.net', '/'), qr/^\[fe80::1\]:8080$/ms, + 'CNAME1 CNAME2'); + +$response = http_host_header('c_cn.example.net', '/'); +is(@n = $response =~ /8080/g, 2, 'CNAME CNAME+AAAA responses'); +like($response, qr/\[fe80::1\]:8080/, 'CNAME CNAME+AAAA 1'); +like($response, qr/\[fe80::2\]:8080/, 'CNAME CNAME+AAAA 1'); + +$response = http_host_header('c_e.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'CNAME error responses'); +like($response, qr/502 Bad/, 'CNAME error'); + +$response = http_host_header('cn_z.example.net', '/'); +is(@n = $response =~ /8080/g, 2, 'CNAME+A zero responses'); +like($response, qr/127.0.0.201:8080/, 'CNAME+A zero 1'); +like($response, qr/127.0.0.202:8080/, 'CNAME+A zero 2'); + +$response = http_host_header('cn_n.example.net', '/'); +is(@n = $response =~ /8080/g, 4, 'CNAME+A AAAA responses'); +like($response, qr/127.0.0.201:8080/, 'CNAME+A AAAA 1'); +like($response, qr/127.0.0.202:8080/, 'CNAME+A AAAA 2'); +like($response, qr/\[fe80::1\]:8080/, 'CNAME+A AAAA 3'); +like($response, qr/\[fe80::2\]:8080/, 'CNAME+A AAAA 4'); + +$response = http_host_header('cn_c.example.net', '/'); +is(@n = $response =~ /8080/g, 2, 'CNAME+A CNAME responses'); +like($response, qr/127.0.0.201:8080/, 'CNAME+A CNAME 1'); +like($response, qr/127.0.0.202:8080/, 'CNAME+A CNAME 2'); + +$response = http_host_header('cn_cn.example.net', '/'); +is(@n = $response =~ /8080/g, 4, 'CNAME+A CNAME+AAAA responses'); +like($response, qr/127.0.0.201:8080/, 'CNAME+A CNAME+AAAA 1'); +like($response, qr/127.0.0.202:8080/, 'CNAME+A CNAME+AAAA 2'); +like($response, qr/\[fe80::1\]:8080/, 'CNAME+A CNAME+AAAA 3'); +like($response, qr/\[fe80::2\]:8080/, 'CNAME+A CNAME+AAAA 4'); + +$response = http_host_header('cn_e.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'CNAME+A error responses'); +like($response, qr/502 Bad/, 'CNAME+A error'); + +$response = http_host_header('e_z.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'error zero responses'); +like($response, qr/502 Bad/, 'error zero'); + +$response = http_host_header('e_n.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'error AAAA responses'); +like($response, qr/502 Bad/, 'error AAAA'); + +$response = http_host_header('e_c.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'error CNAME responses'); +like($response, qr/502 Bad/, 'error CNAME'); + +$response = http_host_header('e_cn.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'error CNAME+AAAA responses'); +like($response, qr/502 Bad/, 'error CNAME+AAAA'); + +$response = http_host_header('e_e.example.net', '/'); +is(@n = $response =~ /8080/g, 0, 'error error responses'); +like($response, qr/502 Bad/, 'error error'); + +############################################################################### + +sub http_host_header { + my ($host, $uri) = @_; + return http(<<EOF); +GET $uri HTTP/1.0 +Host: $host + +EOF +} + +############################################################################### + +sub reply_handler { + my ($recv_data, $port, $state) = @_; + + my (@name, @rdata); + + use constant NOERROR => 0; + use constant SERVFAIL => 2; + use constant NXDOMAIN => 3; + + use constant A => 1; + use constant CNAME => 5; + use constant AAAA => 28; + use constant DNAME => 39; + + use constant IN => 1; + + # default values + + my ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600); + + # decode name + + my ($len, $offset) = (undef, 12); + while (1) { + $len = unpack("\@$offset C", $recv_data); + last if $len == 0; + $offset++; + push @name, unpack("\@$offset A$len", $recv_data); + $offset += $len; + } + + $offset -= 1; + my ($id, $type, $class) = unpack("n x$offset n2", $recv_data); + + my $name = join('.', @name); + if (($name eq 'aaaa.example.net') || ($name eq 'alias.example.net')) { + if ($type == AAAA) { + push @rdata, rd_addr6($ttl, "fe80::1"); + } + + } elsif ($name eq 'alias2.example.net') { + if ($type == A) { + push @rdata, rd_addr($ttl, '127.0.0.201'); + } + if ($type == AAAA) { + push @rdata, rd_addr6($ttl, "fe80::1"); + } + + } elsif ($name eq 'alias4.example.net') { + if ($type == A) { + push @rdata, rd_addr($ttl, '127.0.0.201'); + } + + } elsif ($name eq 'alias6.example.net') { + if ($type == AAAA) { + push @rdata, rd_addr6($ttl, "fe80::1"); + } + + } elsif (($name eq 'many.example.net') && $type == AAAA) { + $state->{manycnt}++; + if ($state->{manycnt} > 1) { + $rcode = SERVFAIL; + } + + push @rdata, rd_addr6($ttl, 'fe80::1'); + push @rdata, rd_addr6($ttl, 'fe80::2'); + + } elsif ($name eq 'cname.example.net') { + $state->{cnamecnt}++; + if ($state->{cnamecnt} > 2) { + $rcode = SERVFAIL; + } + push @rdata, pack("n3N nCa5n", 0xc00c, CNAME, IN, $ttl, + 8, 5, 'alias', 0xc012); + + } elsif ($name eq 'cname_a.example.net') { + push @rdata, pack("n3N nCa5n", 0xc00c, CNAME, IN, $ttl, + 8, 5, 'alias', 0xc014); + + # points to "alias" set in previous rdata + + if ($type == AAAA) { + push @rdata, pack('n3N nn8', 0xc031, AAAA, IN, $ttl, + 16, expand_ip6("::1")); + } + + } elsif ($name eq '2.example.net') { + if ($port == 8081) { + $state->{twocnt}++; + } + if ($state->{twocnt} & 1) { + $rcode = SERVFAIL; + } + + if ($type == AAAA) { + push @rdata, rd_addr6($ttl, '::1'); + } + + } elsif ($name eq 'z_z.example.net') { + # assume no answers given + + } elsif ($name eq 'z_n.example.net') { + if ($type == AAAA) { + push @rdata, rd_addr6($ttl, 'fe80::1'); + } + + } elsif ($name eq 'z_c.example.net') { + if ($type == AAAA) { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc010); + } + + } elsif ($name eq 'z_cn.example.net') { + if ($type == AAAA) { + push @rdata, pack("n3N nCa5n", 0xc00c, CNAME, IN, $ttl, + 8, 5, 'alias', 0xc011); + push @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl, + 16, expand_ip6("fe80::1")); + push @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl, + 16, expand_ip6("fe80::2")); + } + + } elsif ($name eq 'z_e.example.net') { + if ($type == AAAA) { + $rcode = SERVFAIL; + } + + } elsif ($name eq 'n_z.example.net') { + if ($type == A) { + push @rdata, rd_addr($ttl, '127.0.0.201'); + } + + } elsif ($name eq 'n_n.example.net') { + if ($type == A) { + push @rdata, rd_addr($ttl, '127.0.0.201'); + } + if ($type == AAAA) { + push @rdata, rd_addr6($ttl, 'fe80::1'); + } + + } elsif ($name eq 'n_c.example.net') { + if ($type == A) { + push @rdata, rd_addr($ttl, '127.0.0.201'); + } + if ($type == AAAA) { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc010); + } + + } elsif ($name eq 'n_cn.example.net') { + if ($type == A) { + push @rdata, rd_addr($ttl, '127.0.0.201'); + push @rdata, rd_addr($ttl, '127.0.0.202'); + } + if ($type == AAAA) { + push @rdata, pack("n3N nCa5n", 0xc00c, CNAME, IN, $ttl, + 8, 5, 'alias', 0xc011); + push @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl, + 16, expand_ip6("fe80::1")); + push @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl, + 16, expand_ip6("fe80::2")); + } + + } elsif ($name eq 'n_e.example.net') { + if ($type == A) { + push @rdata, rd_addr($ttl, '127.0.0.201'); + } + if ($type == AAAA) { + $rcode = SERVFAIL; + } + + } elsif ($name eq 'c_z.example.net') { + if ($type == A) { + push @rdata, pack("n3N nCa5n", 0xc00c, CNAME, IN, $ttl, + 8, 5, 'alias', 0xc010); + } + + } elsif ($name eq 'c_n.example.net') { + if ($type == A) { + push @rdata, pack("n3N nCa5n", 0xc00c, CNAME, IN, $ttl, + 8, 5, 'alias', 0xc010); + } + if ($type == AAAA) { + push @rdata, rd_addr6($ttl, "fe80::1"); + } + + } elsif ($name eq 'c_c.example.net') { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc010); + + } elsif ($name eq 'c1_c2.example.net') { + if ($type == A) { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias4', 0xc012); + } + if ($type == AAAA) { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias6', 0xc012); + } + + } elsif ($name eq 'c_cn.example.net') { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc011); + + if ($type == AAAA) { + push @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl, + 16, expand_ip6("fe80::1")); + push @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl, + 16, expand_ip6("fe80::2")); + } + + } elsif ($name eq 'cn_z.example.net') { + if ($type == A) { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc011); + push @rdata, pack("n3N nC4", 0xc02e, A, IN, $ttl, + 4, split('\.', '127.0.0.201')); + push @rdata, pack("n3N nC4", 0xc02e, A, IN, $ttl, + 4, split('\.', '127.0.0.202')); + } + + } elsif ($name eq 'cn_n.example.net') { + if ($type == A) { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc011); + push @rdata, pack("n3N nC4", 0xc02e, A, IN, $ttl, + 4, split('\.', '127.0.0.201')); + push @rdata, pack("n3N nC4", 0xc02e, A, IN, $ttl, + 4, split('\.', '127.0.0.202')); + } + if ($type == AAAA) { + push @rdata, pack('n3N nn8', 0xc00c, AAAA, IN, $ttl, + 16, expand_ip6("fe80::1")); + push @rdata, pack('n3N nn8', 0xc00c, AAAA, IN, $ttl, + 16, expand_ip6("fe80::2")); + } + + } elsif ($name eq 'cn_c.example.net') { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc011); + if ($type == A) { + push @rdata, pack("n3N nC4", 0xc02e, A, IN, $ttl, + 4, split('\.', '127.0.0.201')); + push @rdata, pack("n3N nC4", 0xc02e, A, IN, $ttl, + 4, split('\.', '127.0.0.202')); + } + + } elsif ($name eq 'cn_cn.example.net') { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc012); + + if ($type == A) { + push @rdata, pack("n3N nC4", 0xc02f, A, IN, $ttl, + 4, split('\.', '127.0.0.201')); + push @rdata, pack("n3N nC4", 0xc02f, A, IN, $ttl, + 4, split('\.', '127.0.0.202')); + } + if ($type == AAAA) { + push @rdata, pack('n3N nn8', 0xc02f, AAAA, IN, $ttl, + 16, expand_ip6("fe80::1")); + push @rdata, pack('n3N nn8', 0xc02f, AAAA, IN, $ttl, + 16, expand_ip6("fe80::2")); + } + + } elsif ($name eq 'cn_e.example.net') { + if ($type == A) { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc011); + push @rdata, pack("n3N nC4", 0xc02e, A, IN, $ttl, + 4, split('\.', '127.0.0.201')); + push @rdata, pack("n3N nC4", 0xc02e, A, IN, $ttl, + 4, split('\.', '127.0.0.202')); + } + if ($type == AAAA) { + $rcode = SERVFAIL; + } + + + } elsif ($name eq 'e_z.example.net') { + if ($type == A) { + $rcode = SERVFAIL; + } + + } elsif ($name eq 'e_n.example.net') { + if ($type == A) { + $rcode = SERVFAIL; + } + if ($type == AAAA) { + push @rdata, rd_addr6($ttl, 'fe80::1'); + } + + } elsif ($name eq 'e_c.example.net') { + if ($type == A) { + $rcode = SERVFAIL; + } + if ($type == AAAA) { + push @rdata, pack("n3N nCa6n", 0xc00c, CNAME, IN, $ttl, + 9, 6, 'alias2', 0xc010); + } + + } elsif ($name eq 'e_cn.example.net') { + if ($type == A) { + $rcode = SERVFAIL; + } + if ($type == AAAA) { + push @rdata, pack("n3N nCa5n", 0xc00c, CNAME, IN, $ttl, + 8, 5, 'alias', 0xc011); + push @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl, + 16, expand_ip6("fe80::1")); + push @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl, + 16, expand_ip6("fe80::2")); + } + + } elsif ($name eq 'e_e.example.net') { + if ($type == A) { + $rcode = SERVFAIL; + } + if ($type == AAAA) { + $rcode = NXDOMAIN; + } + } + + $len = @name; + pack("n6 (w/a*)$len x n2", $id, $hdr | $rcode, 1, scalar @rdata, + 0, 0, @name, $type, $class) . join('', @rdata); +} + +sub rd_addr { + my ($ttl, $addr) = @_; + + my $code = 'split(/\./, $addr)'; + + pack 'n3N nC4', 0xc00c, A, IN, $ttl, eval "scalar $code", eval($code); +} + +sub expand_ip6 { + my ($addr) = @_; + + substr ($addr, index($addr, "::"), 2) = + join "0", map { ":" } (0 .. 8 - (split /:/, $addr) + 1); + map { hex "0" x (4 - length $_) . "$_" } split /:/, $addr; +} + +sub rd_addr6 { + my ($ttl, $addr) = @_; + + pack 'n3N nn8', 0xc00c, AAAA, IN, $ttl, 16, expand_ip6($addr); +} + +sub dns_daemon { + my ($port, $t) = @_; + + my ($data, $recv_data); + my $socket = IO::Socket::INET->new( + LocalAddr => '127.0.0.1', + LocalPort => $port, + Proto => 'udp', + ) + or die "Can't create listening socket: $!\n"; + + # track number of relevant queries + + my %state = ( + cnamecnt => 0, + twocnt => 0, + manycnt => 0, + ); + + # signal we are ready + + open my $fh, '>', $t->testdir() . '/' . $port; + close $fh; + + while (1) { + $socket->recv($recv_data, 65536); + $data = reply_handler($recv_data, $port, \%state); + $socket->send($data); + } +} + +###############################################################################