changeset 362:8ca9c75c97d2

Tests: more resolver tests. - Uncompressed answer section. - TTL handling in CNAME + A combined answer. - Case-insensitive lookups. - PTR type checking in RR for address to name resolving.
author Sergey Kandaurov <pluknet@nginx.com>
date Mon, 16 Dec 2013 18:59:16 +0400
parents aac06d3bdc05
children 98f3a8568b0c
files http_resolver.t mail_resolver.t
diffstat 2 files changed, 165 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/http_resolver.t
+++ b/http_resolver.t
@@ -58,6 +58,10 @@ http {
             resolver    127.0.0.1:8081 valid=3s;
             proxy_pass  http://$host:8080/backend;
         }
+        location /case {
+            resolver    127.0.0.1:8081;
+            proxy_pass  http://$http_x_name:8080/backend;
+        }
         location /invalid {
             proxy_pass  http://$host:8080/backend;
         }
@@ -76,7 +80,7 @@ EOF
 $t->run_daemon(\&dns_daemon, 8081, $t);
 $t->run_daemon(\&dns_daemon, 8082, $t);
 
-$t->run()->plan(27);
+$t->run()->plan(31);
 
 $t->waitforfile($t->testdir . '/8081');
 $t->waitforfile($t->testdir . '/8082');
@@ -84,6 +88,20 @@ EOF
 ###############################################################################
 
 like(http_host_header('a.example.net', '/'), qr/200 OK/, 'A');
+
+# ensure that resolver serves queries from cache in a case-insensitive manner
+# we check this by marking 2nd and subsequent queries on backend with SERVFAIL
+
+TODO: {
+local $TODO = 'not yet' unless $t->has_version('1.5.8');
+
+http_x_name_header('case.example.net', '/case');
+like(http_x_name_header('CASE.example.net', '/case'), qr/200 OK/,
+	'A case-insensitive');
+
+}
+
+like(http_host_header('awide.example.net', '/'), qr/200 OK/, 'A uncompressed');
 like(http_host_header('short.example.net', '/'), qr/502 Bad/,
 	'A short dns response');
 
@@ -186,6 +204,31 @@ sleep 2;
 like(http_host_header('ttl.example.net', '/valid'), qr/502 Bad/,
 	'valid expired');
 
+TODO: {
+local $TODO = 'not yet';
+
+# Ensure that resolver respects expired A in CNAME + A combined response.
+# When ttl in A is expired, only the canonical name should be queried.
+# Catch this by returning SERVFAIL on the 2nd and subsequent queries for
+# original name.
+
+http_host_header('cname_a_ttl.example.net', '/');
+
+# Ensure that resolver respects expired CNAME in CNAME + A combined response.
+# When ttl in CNAME is expired, the answer should not be served from cache.
+# Catch this by returning SERVFAIL on the 2nd and subsequent queries.
+
+http_host_header('cname_a_ttl2.example.net', '/');
+
+sleep 2;
+
+like(http_host_header('cname_a_ttl.example.net', '/'), qr/200 OK/,
+	'CNAME + A with expired A ttl');
+like(http_host_header('cname_a_ttl2.example.net', '/'), qr/502 Bad/,
+	'CNAME + A with expired CNAME ttl');
+
+}
+
 like(http_host_header('example.net', '/invalid'), qr/502 Bad/, 'no resolver');
 
 ###############################################################################
@@ -199,6 +242,15 @@ Host: $host
 EOF
 }
 
+sub http_x_name_header {
+	my ($host, $uri) = @_;
+	return http(<<EOF);
+GET $uri HTTP/1.0
+X-Name: $host
+
+EOF
+}
+
 ###############################################################################
 
 sub reply_handler {
@@ -240,6 +292,18 @@ sub reply_handler {
 			push @rdata, rd_addr($ttl, '127.0.0.1');
 		}
 
+	} elsif ($name eq 'case.example.net' && $type == A) {
+		if (++$state->{casecnt} > 1) {
+			$rcode = SERVFAIL;
+		}
+
+		push @rdata, rd_addr($ttl, '127.0.0.1');
+
+	} elsif ($name eq 'awide.example.net' && $type == A) {
+		push @rdata, pack '(w/a*)3x n2N nC4',
+			('awide', 'example', 'net'), A, IN, $ttl,
+			4, (127, 0, 0, 1);
+
 	} elsif (($name eq 'many.example.net') && $type == A) {
 		$state->{manycnt}++;
 		if ($state->{manycnt} > 1) {
@@ -289,6 +353,30 @@ sub reply_handler {
 				4, split(/\./, '127.0.0.1'));
 		}
 
+	} elsif ($name eq 'cname_a_ttl.example.net') {
+		push @rdata, pack("n3N nCa17n", 0xc00c, CNAME, IN, $ttl,
+			20, 17, 'cname_a_ttl_alias', 0xc018);
+
+		if ($type == A) {
+			if (++$state->{cttlcnt} >= 2) {
+			        $rcode = SERVFAIL;
+			}
+			push @rdata, pack('n3N nC4', 0xc035, A, IN, 1,
+				4, split(/\./, '127.0.0.1'));
+		}
+
+	} elsif ($name eq 'cname_a_ttl2.example.net' && $type == A) {
+		push @rdata, pack("n3N nCa18n", 0xc00c, CNAME, IN, 1,
+			21, 18, 'cname_a_ttl2_alias', 0xc019);
+		if (++$state->{cttl2cnt} >= 2) {
+		        $rcode = SERVFAIL;
+		}
+		push @rdata, pack('n3N nC4', 0xc036, A, IN, $ttl,
+			4, split(/\./, '127.0.0.1'));
+
+	} elsif ($name eq 'cname_a_ttl_alias.example.net' && $type == A) {
+		push @rdata, rd_addr($ttl, '127.0.0.1');
+
 	} elsif ($name eq 'cname2.example.net') {
 		# points to non-existing A
 
@@ -373,7 +461,10 @@ sub dns_daemon {
 		twocnt       => 0,
 		ttlcnt       => 0,
 		ttl0cnt      => 0,
+		cttlcnt      => 0,
+		cttl2cnt     => 0,
 		manycnt      => 0,
+		casecnt      => 0,
 	);
 
 	# signal we are ready
--- a/mail_resolver.t
+++ b/mail_resolver.t
@@ -42,6 +42,9 @@ mail {
     smtp_auth    none;
     server_name  locahost;
 
+    # prevent useless resend
+    resolver_timeout 1s;
+
     server {
         listen    127.0.0.1:8025;
         protocol  smtp;
@@ -59,8 +62,6 @@ mail {
         protocol  smtp;
         resolver  127.0.0.1:8083;
 
-        # prevent useless resend
-        resolver_timeout 1s;
     }
 
     server {
@@ -68,6 +69,18 @@ mail {
         protocol  smtp;
         resolver  127.0.0.1:8084;
     }
+
+    server {
+        listen    127.0.0.1:8030;
+        protocol  smtp;
+        resolver  127.0.0.1:8085;
+    }
+
+    server {
+        listen    127.0.0.1:8031;
+        protocol  smtp;
+        resolver  127.0.0.1:8086;
+    }
 }
 
 http {
@@ -94,18 +107,16 @@ http {
 
 EOF
 
-$t->run_daemon(\&dns_daemon, 8081, $t);
-$t->run_daemon(\&dns_daemon, 8082, $t);
-$t->run_daemon(\&dns_daemon, 8083, $t);
-$t->run_daemon(\&dns_daemon, 8084, $t);
+for (8081 .. 8086) {
+	$t->run_daemon(\&dns_daemon, $_, $t);
+}
 $t->run();
 
-$t->waitforfile($t->testdir . '/8081');
-$t->waitforfile($t->testdir . '/8082');
-$t->waitforfile($t->testdir . '/8083');
-$t->waitforfile($t->testdir . '/8084');
+for (8081 .. 8086) {
+	$t->waitforfile($t->testdir . "/$_");
+}
 
-$t->plan(5);
+$t->plan(7);
 
 ###############################################################################
 
@@ -199,6 +210,46 @@ close $s;
 
 }
 
+# uncompressed answer
+
+TODO: {
+local $TODO = 'support for uncompressed name in PTR';
+
+$s = Test::Nginx::SMTP->new(PeerAddr => "127.0.0.1:8030");
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+$s->send('MAIL FROM:<test@example.com> SIZE=100');
+$s->read();
+
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('uncompressed PTR');
+
+$s->send('QUIT');
+$s->read();
+close $s;
+
+}
+
+TODO: {
+local $TODO = 'PTR type checking';
+
+$s = Test::Nginx::SMTP->new(PeerAddr => "127.0.0.1:8031");
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+$s->send('MAIL FROM:<test@example.com> SIZE=100');
+$s->read();
+
+$s->send('RCPT TO:<test@example.com>');
+$s->check(qr/TEMPUNAVAIL/, 'PTR type');
+
+$s->send('QUIT');
+$s->read();
+close $s;
+
+}
+
 ###############################################################################
 
 sub reply_handler {
@@ -255,6 +306,16 @@ sub reply_handler {
 
 			push @rdata, rd_name(CNAME, $ttl,
 				'1.1.0.0.127.in-addr.arpa');
+
+		} elsif ($port == 8085) {
+			# uncompressed answer
+
+			push @rdata, pack("(w/a*)6x n2N n(w/a*)3x",
+				('1', '0', '0', '127', 'in-addr', 'arpa'),
+				PTR, IN, $ttl, 15, ('a', 'example', 'net'));
+
+		} elsif ($port == 8086) {
+			push @rdata, rd_name(CNAME, $ttl, 'a.example.net');
 		}
 
 	} elsif ($name eq '1.1.0.0.127.in-addr.arpa' && $type == PTR) {
@@ -272,7 +333,7 @@ sub rd_name {
 
 	@rdname = split /\./, $name;
 	$rdlen = length(join '', @rdname) + @rdname + 1;
-	pack("n3N n(w/a*)* x", 0xc00c, PTR, IN, $ttl, $rdlen, @rdname);
+	pack("n3N n(w/a*)* x", 0xc00c, $type, IN, $ttl, $rdlen, @rdname);
 }
 
 sub rd_addr {