diff mail_resolver.t @ 357:e0c00e695cb5

Tests: removed Net::DNS dependency.
author Sergey Kandaurov <pluknet@nginx.com>
date Fri, 22 Nov 2013 15:20:16 +0400
parents 14eba1ad0880
children cdab739eb6ea
line wrap: on
line diff
--- a/mail_resolver.t
+++ b/mail_resolver.t
@@ -25,9 +25,6 @@ select STDOUT; $| = 1;
 
 local $SIG{PIPE} = 'IGNORE';
 
-eval { require Net::DNS::Nameserver; };
-plan(skip_all => "Net::DNS::Nameserver not installed") if $@;
-
 my $t = Test::Nginx->new()->has(qw/mail smtp http rewrite/)
 	->run_daemon(\&Test::Nginx::SMTP::smtp_test_daemon);
 
@@ -97,16 +94,16 @@ http {
 
 EOF
 
-$t->run_daemon(\&dns_daemon, 8081);
-$t->run_daemon(\&dns_daemon, 8082);
-$t->run_daemon(\&dns_daemon, 8083);
-$t->run_daemon(\&dns_daemon, 8084);
+$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);
 $t->run();
 
-$t->waitforsocket('127.0.0.1:8081');
-$t->waitforsocket('127.0.0.1:8082');
-$t->waitforsocket('127.0.0.1:8083');
-$t->waitforsocket('127.0.0.1:8084');
+$t->waitforfile($t->testdir . '/8081');
+$t->waitforfile($t->testdir . '/8082');
+$t->waitforfile($t->testdir . '/8083');
+$t->waitforfile($t->testdir . '/8084');
 
 $t->plan(5);
 
@@ -205,65 +202,115 @@ close $s;
 ###############################################################################
 
 sub reply_handler {
-	my ($name, $class, $type, $peerhost, $query, $conn) = @_;
-	my ($rcode, @ans, $ttl, $rdata);
+	my ($recv_data, $port) = @_;
+
+	my (@name, @rdata);
 
-	$rcode = 'NOERROR';
-	$ttl = 3600;
+	use constant NOERROR	=> 0;
+	use constant SERVFAIL	=> 2;
+	use constant NXDOMAIN	=> 3;
 
-	if ($name eq 'a.example.net' && $type eq 'A') {
-		($rdata) = ('127.0.0.1');
-		push @ans, Net::DNS::RR->new("$name $ttl $class $type $rdata");
+	use constant A		=> 1;
+	use constant CNAME	=> 5;
+	use constant PTR	=> 12;
+
+	use constant IN 	=> 1;
 
-	} elsif ($name eq '1.0.0.127.in-addr.arpa' && $type eq 'PTR') {
-		if ($conn->{sockport} == 8081) {
-			$rdata = 'a.example.net';
-			push @ans, Net::DNS::RR->new(
-				"$name $ttl $class $type $rdata"
-			);
+	# default values
+
+	my ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);
+
+	# decode name
 
-		} elsif ($conn->{sockport} == 8082) {
-			return 'SERVFAIL';
+	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);
 
-		} elsif ($conn->{sockport} == 8083) {
+	my $name = join('.', @name);
+	if ($name eq 'a.example.net' && $type == A) {
+		push @rdata, rd_addr($ttl, '127.0.0.1');
+
+	} elsif ($name eq '1.0.0.127.in-addr.arpa' && $type == PTR) {
+		if ($port == 8081) {
+			push @rdata, rd_name(PTR, $ttl, 'a.example.net');
+
+		} elsif ($port == 8082) {
+			$rcode = SERVFAIL;
+
+		} elsif ($port == 8083) {
 			# zero length RDATA
-			$rdata = '';
-			push @ans, Net::DNS::RR->new(
-				"$name $ttl $class $type $rdata"
-			);
+
+			push @rdata, pack("n3N n", 0xc00c, PTR, IN, $ttl, 0);
 
-		} elsif ($conn->{sockport} == 8084) {
+		} elsif ($port == 8084) {
 			# PTR answered with CNAME
-			($type, $rdata) = ('CNAME',
+
+			push @rdata, rd_name(CNAME, $ttl,
 				'1.1.0.0.127.in-addr.arpa');
-			push @ans, Net::DNS::RR->new(
-				"$name $ttl $class $type $rdata"
-			);
 		}
 
-	} elsif ($name eq '1.1.0.0.127.in-addr.arpa' && $type eq 'PTR') {
-		$rdata = 'a.example.net';
-		push @ans, Net::DNS::RR->new("$name $ttl $class $type $rdata");
+	} elsif ($name eq '1.1.0.0.127.in-addr.arpa' && $type == PTR) {
+		push @rdata, rd_name(PTR, $ttl, 'a.example.net');
 
 	} else {
-		$rcode = 'NXDOMAIN';
+		$rcode = NXDOMAIN;
 	}
 
-	return ($rcode, \@ans);
+	$len = @name;
+	pack("n6 (w/a*)$len x n2", $id, $hdr | $rcode, 1, scalar @rdata,
+		0, 0, @name, $type, $class) . join('', @rdata);
+}
+
+sub rd_name {
+	my ($type, $ttl, $name) = @_;
+	my ($rdlen, @rdname);
+
+	@rdname = split /\./, $name;
+	$rdlen = length(join '', @rdname) + @rdname + 1;
+	pack("n3N n(w/a*)* x", 0xc00c, PTR, IN, $ttl, $rdlen, @rdname);
+}
+
+sub rd_addr {
+	my ($ttl, $addr) = @_;
+
+	my $code = 'split(/\./, $addr)';
+
+	# use a special pack string to not zero pad
+
+	return pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';
+
+	pack 'n3N nC4', 0xc00c, A, IN, $ttl, eval "scalar $code", eval($code);
 }
 
 sub dns_daemon {
-	my ($port) = @_;
+	my ($port, $t) = @_;
 
-	my $ns = Net::DNS::Nameserver->new(
+	my ($data, $recv_data);
+	my $socket = IO::Socket::INET->new(
 		LocalAddr    => '127.0.0.1',
 		LocalPort    => $port,
 		Proto        => 'udp',
-		ReplyHandler => \&reply_handler,
 	)
-		or die "Can't create nameserver object: $!\n";
+		or die "Can't create listening socket: $!\n";
+
+	# signal we are ready
 
-	$ns->main_loop;
+	open my $fh, '>', $t->testdir() . '/' . $port;
+	close $fh;
+
+	while (1) {
+		$socket->recv($recv_data, 65536);
+		$data = reply_handler($recv_data, $port);
+		$socket->send($data);
+	}
 }
 
 ###############################################################################