Skip to content

Commit

Permalink
Merge pull request #266 from chobits/for_pull_request
Browse files Browse the repository at this point in the history
Fixed test cases that failed to start the DNS daemon
  • Loading branch information
chobits committed May 29, 2023
2 parents 75febef + 8fef990 commit cf52749
Show file tree
Hide file tree
Showing 4 changed files with 385 additions and 334 deletions.
246 changes: 130 additions & 116 deletions t/http_proxy_connect.t
Expand Up @@ -10,20 +10,20 @@ use warnings;
use strict;

use Test::More;
# use Test::Simple 'no_plan';

use IO::Select;

BEGIN { use FindBin; chdir($FindBin::Bin); }

use lib 'lib';
use Test::Nginx;
use Net::DNS::Nameserver;

###############################################################################

select STDERR; $| = 1;
select STDOUT; $| = 1;

my $t = Test::Nginx->new()->has(qw/http proxy/); #->plan(12);
my $t = Test::Nginx->new()->has(qw/http proxy/)->plan(22);

###############################################################################

Expand All @@ -37,9 +37,6 @@ print("+ test_enable_rewrite_phase: $test_enable_rewrite_phase\n");

# --- init DNS server ---

my $bind_pid;
my $bind_server_port = 18085;

# SRV record, not used
my %route_map;

Expand All @@ -52,19 +49,6 @@ my %aroute_map = (
'set-response-status.com' => [[300, "127.0.0.1"]],
);

# AAAA record (ipv6)
my %aaaaroute_map;
# my %aaaaroute_map = (
# 'www.test-a.com' => [[300, "[::1]"]],
# 'www.test-b.com' => [[300, "[::1]"]],
# #'www.test-a.com' => [[300, "127.0.0.1"]],
# #'www.test-b.com' => [[300, "127.0.0.1"]],
# );

start_bind();

# --- end ---

###############################################################################

my $nginx_conf = <<'EOF';
Expand All @@ -88,7 +72,7 @@ http {
access_log %%TESTDIR%%/connect.log connect;
resolver 127.0.0.1:18085 ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
server {
listen 127.0.0.1:8081;
Expand Down Expand Up @@ -157,6 +141,9 @@ EOF

$t->write_file_expand('nginx.conf', $nginx_conf);

$t->run_daemon(\&dns_daemon, port(8981), $t);
$t->waitforfile($t->testdir . '/' . port(8981));

eval {
$t->run();
};
Expand Down Expand Up @@ -313,7 +300,7 @@ http {
access_log off;
resolver 127.0.0.1:18085 ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
server {
listen 127.0.0.1:8080;
Expand Down Expand Up @@ -380,7 +367,7 @@ http {
access_log off;
resolver 127.0.0.1:18085 ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
server {
listen 127.0.0.1:8080;
Expand Down Expand Up @@ -410,17 +397,8 @@ if ($test_enable_rewrite_phase) {

$t->stop();



# --- stop DNS server ---

stop_bind();

done_testing();

###############################################################################


sub http_connect_request {
my ($host, $port, $url) = @_;
my $r = http_connect($host, $port, <<EOF);
Expand Down Expand Up @@ -523,111 +501,147 @@ EOF
return $reply;
}

# --- DNS Server ---
###############################################################################

sub reply_handler {
my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
my ($rcode, @ans, @auth, @add);
# print("DNS reply: receive query=$qname, $qclass, $qtype, $peerhost, $query, $conn\n");

if ($qtype eq "SRV" && exists($route_map{$qname})) {
my @records = @{$route_map{$qname}};
for (my $i = 0; $i < scalar(@records); $i++) {
my ($ttl, $weight, $priority, $port, $origin_addr) = @{$records[$i]};
my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $priority $weight $port $origin_addr");
push @ans, $rr;
# print("DNS reply: $qname $ttl $qclass $qtype $origin_addr\n");
}
my ($recv_data, $port, $state, %extra) = @_;

$rcode = "NOERROR";
} elsif (($qtype eq "A") && exists($aroute_map{$qname})) {
my @records = @{$aroute_map{$qname}};
for (my $i = 0; $i < scalar(@records); $i++) {
my ($ttl, $origin_addr) = @{$records[$i]};
my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $origin_addr");
push @ans, $rr;
# print("DNS reply: $qname $ttl $qclass $qtype $origin_addr\n");
}
my (@name, @rdata);

$rcode = "NOERROR";
} elsif (($qtype eq "AAAA") && exists($aaaaroute_map{$qname})) {
my @records = @{$aaaaroute_map{$qname}};
for (my $i = 0; $i < scalar(@records); $i++) {
my ($ttl, $origin_addr) = @{$records[$i]};
my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $origin_addr");
push @ans, $rr;
# print("DNS reply: $qname $ttl $qclass $qtype $origin_addr\n");
}
use constant NOERROR => 0;
use constant FORMERR => 1;
use constant SERVFAIL => 2;
use constant NXDOMAIN => 3;

$rcode = "NOERROR";
} else {
$rcode = "NXDOMAIN";
}
use constant A => 1;
use constant CNAME => 5;
use constant DNAME => 39;

# mark the answer as authoritative (by setting the 'aa' flag)
my $headermask = { ra => 1 };
use constant IN => 1;

# specify EDNS options { option => value }
my $optionmask = { };
# default values

return ($rcode, \@ans, \@auth, \@add, $headermask, $optionmask);
}
my ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);

sub bind_daemon {
my $ns = new Net::DNS::Nameserver(
LocalAddr => ['127.0.0.1'],
LocalPort => $bind_server_port,
ReplyHandler => \&reply_handler,
Verbose => 0, # Verbose = 1 to print debug info
Truncate => 0
) || die "[D] DNS server: couldn't create nameserver object\n";
# decode name

$ns->main_loop;
}

sub start_bind {
if (defined $bind_server_port) {
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;
}

print "+ DNS server: try to bind server port: $bind_server_port\n";
$offset -= 1;
my ($id, $type, $class) = unpack("n x$offset n2", $recv_data);

$t->run_daemon(\&bind_daemon);
$bind_pid = pop @{$t->{_daemons}};
my $name = join('.', @name);

print "+ DNS server: daemon pid: $bind_pid\n";
if (($type == A) && exists($aroute_map{$name})) {

my $s;
my $i = 1;
while (not $s) {
$s = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => "127.0.0.1",
PeerPort => $bind_server_port
);
sleep 0.1;
$i++ > 20 and last;
}
sleep 0.1;
$s or die "cannot connect to DNS server";
close($s) or die 'can not connect to DNS server';
my @records = @{$aroute_map{$name}};

print "+ DNS server: working\n";
for (my $i = 0; $i < scalar(@records); $i++) {
my ($ttl, $origin_addr) = @{$records[$i]};
push @rdata, rd_addr($ttl, $origin_addr);

END {
print("+ try to stop\n");
stop_bind();
#print("dns reply: $name $ttl $class $type $origin_addr\n");
}
}
}

$len = @name;
pack("n6 (C/a*)$len x n2", $id, $hdr | $rcode, 1, scalar @rdata,
0, 0, @name, $type, $class) . join('', @rdata);
}

sub stop_bind {
if (defined $bind_pid) {
# kill dns daemon
kill $^O eq 'MSWin32' ? 15 : 'TERM', $bind_pid;
wait;
sub rd_addr {
my ($ttl, $addr) = @_;

$bind_pid = undef;
print ("+ DNS server: stop\n");
}
my $code = 'split(/\./, $addr)';

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, $t, %extra) = @_;

print("+ dns daemon: try to listen on 127.0.0.1:$port\n");

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";

my $sel = IO::Select->new($socket);
my $tcp = 0;

if ($extra{tcp}) {
$tcp = port(8983, socket => 1);
$sel->add($tcp);
}

local $SIG{PIPE} = 'IGNORE';

# track number of relevant queries

my %state = (
cnamecnt => 0,
twocnt => 0,
ttlcnt => 0,
ttl0cnt => 0,
cttlcnt => 0,
cttl2cnt => 0,
manycnt => 0,
casecnt => 0,
idcnt => 0,
fecnt => 0,
);

# signal we are ready

open my $fh, '>', $t->testdir() . '/' . $port;
close $fh;

while (my @ready = $sel->can_read) {
foreach my $fh (@ready) {
if ($tcp == $fh) {
my $new = $fh->accept;
$new->autoflush(1);
$sel->add($new);

} elsif ($socket == $fh) {
$fh->recv($recv_data, 65536);
$data = reply_handler($recv_data, $port,
\%state);
$fh->send($data);

} else {
$fh->recv($recv_data, 65536);
unless (length $recv_data) {
$sel->remove($fh);
$fh->close;
next;
}

again:
my $len = unpack("n", $recv_data);
$data = substr $recv_data, 2, $len;
$data = reply_handler($data, $port, \%state,
tcp => 1);
$data = pack("n", length $data) . $data;
$fh->send($data);
$recv_data = substr $recv_data, 2 + $len;
goto again if length $recv_data;
}
}
}
}

###############################################################################

0 comments on commit cf52749

Please sign in to comment.