Skip to content

Commit c98cf98

Browse files
authored
Fixed some test cases that failed to start the DNS daemon (#1783)
1. resolver_file.t: reduced dependency on DNS server listening. * In new github workflow environment, we cannot bind 53 port sometimes. In order to make the case pass, we should remove the dependency on 53 port listening, using debug log to check the logic. 2. proxy_connect_*.t: refactored the logic of the DNS daemon test cases to follow the style of the nginx-tests repository. * When attempting to bind UDP ports fails, retry other random ports
1 parent db0777a commit c98cf98

File tree

6 files changed

+403
-462
lines changed

6 files changed

+403
-462
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
LUAJIT_INC: /usr/local/include/luajit-2.1
4545
run: |
4646
./configure \
47+
--with-debug \
4748
--with-ld-opt="-Wl,-rpath,/usr/local/lib" \
4849
--with-ipv6 \
4950
--with-openssl-async \
@@ -86,8 +87,7 @@ jobs:
8687
TEST_NGINX_BINARY: /usr/local/nginx/sbin/nginx
8788
run: |
8889
sudo cpanm --notest Net::DNS::Nameserver > build.log 2>&1 || (cat build.log && exit 1)
89-
prove -v -Inginx-tests/lib $(ls -d tengine-tests/* | grep -v resolver_file)
90-
sudo TEST_NGINX_BINARY=/usr/local/nginx/sbin/nginx prove -v -Inginx-tests/lib tengine-tests/resolver_file.t
90+
prove -v -Inginx-tests/lib tengine-tests/
9191
prove -v -Inginx-tests/lib ../../modules/ngx_http_proxy_connect_module/t
9292
- name: tengine test cases using test-nginx lib
9393
working-directory: tests/test-nginx

modules/ngx_http_proxy_connect_module/t/http_proxy_connect.t

Lines changed: 130 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@ use warnings;
1010
use strict;
1111

1212
use Test::More;
13-
# use Test::Simple 'no_plan';
13+
14+
use IO::Select;
1415

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

1718
use lib 'lib';
1819
use Test::Nginx;
19-
use Net::DNS::Nameserver;
2020

2121
###############################################################################
2222

2323
select STDERR; $| = 1;
2424
select STDOUT; $| = 1;
2525

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

2828
###############################################################################
2929

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

3838
# --- init DNS server ---
3939

40-
my $bind_pid;
41-
my $bind_server_port = 18085;
42-
4340
# SRV record, not used
4441
my %route_map;
4542

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

55-
# AAAA record (ipv6)
56-
my %aaaaroute_map;
57-
# my %aaaaroute_map = (
58-
# 'www.test-a.com' => [[300, "[::1]"]],
59-
# 'www.test-b.com' => [[300, "[::1]"]],
60-
# #'www.test-a.com' => [[300, "127.0.0.1"]],
61-
# #'www.test-b.com' => [[300, "127.0.0.1"]],
62-
# );
63-
64-
start_bind();
65-
66-
# --- end ---
67-
6852
###############################################################################
6953

7054
my $nginx_conf = <<'EOF';
@@ -88,7 +72,7 @@ http {
8872
8973
access_log %%TESTDIR%%/connect.log connect;
9074
91-
resolver 127.0.0.1:18085 ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
75+
resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
9276
9377
server {
9478
listen 127.0.0.1:8081;
@@ -157,6 +141,9 @@ EOF
157141

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

144+
$t->run_daemon(\&dns_daemon, port(8981), $t);
145+
$t->waitforfile($t->testdir . '/' . port(8981));
146+
160147
eval {
161148
$t->run();
162149
};
@@ -313,7 +300,7 @@ http {
313300
314301
access_log off;
315302
316-
resolver 127.0.0.1:18085 ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
303+
resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
317304
318305
server {
319306
listen 127.0.0.1:8080;
@@ -380,7 +367,7 @@ http {
380367
381368
access_log off;
382369
383-
resolver 127.0.0.1:18085 ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
370+
resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off; # NOTE: cannot connect ipv6 address ::1 in mac os x.
384371
385372
server {
386373
listen 127.0.0.1:8080;
@@ -410,17 +397,8 @@ if ($test_enable_rewrite_phase) {
410397

411398
$t->stop();
412399

413-
414-
415-
# --- stop DNS server ---
416-
417-
stop_bind();
418-
419-
done_testing();
420-
421400
###############################################################################
422401

423-
424402
sub http_connect_request {
425403
my ($host, $port, $url) = @_;
426404
my $r = http_connect($host, $port, <<EOF);
@@ -523,111 +501,147 @@ EOF
523501
return $reply;
524502
}
525503

526-
# --- DNS Server ---
504+
###############################################################################
527505

528506
sub reply_handler {
529-
my ($qname, $qclass, $qtype, $peerhost, $query, $conn) = @_;
530-
my ($rcode, @ans, @auth, @add);
531-
# print("DNS reply: receive query=$qname, $qclass, $qtype, $peerhost, $query, $conn\n");
532-
533-
if ($qtype eq "SRV" && exists($route_map{$qname})) {
534-
my @records = @{$route_map{$qname}};
535-
for (my $i = 0; $i < scalar(@records); $i++) {
536-
my ($ttl, $weight, $priority, $port, $origin_addr) = @{$records[$i]};
537-
my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $priority $weight $port $origin_addr");
538-
push @ans, $rr;
539-
# print("DNS reply: $qname $ttl $qclass $qtype $origin_addr\n");
540-
}
507+
my ($recv_data, $port, $state, %extra) = @_;
541508

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

552-
$rcode = "NOERROR";
553-
} elsif (($qtype eq "AAAA") && exists($aaaaroute_map{$qname})) {
554-
my @records = @{$aaaaroute_map{$qname}};
555-
for (my $i = 0; $i < scalar(@records); $i++) {
556-
my ($ttl, $origin_addr) = @{$records[$i]};
557-
my $rr = new Net::DNS::RR("$qname $ttl $qclass $qtype $origin_addr");
558-
push @ans, $rr;
559-
# print("DNS reply: $qname $ttl $qclass $qtype $origin_addr\n");
560-
}
511+
use constant NOERROR => 0;
512+
use constant FORMERR => 1;
513+
use constant SERVFAIL => 2;
514+
use constant NXDOMAIN => 3;
561515

562-
$rcode = "NOERROR";
563-
} else {
564-
$rcode = "NXDOMAIN";
565-
}
516+
use constant A => 1;
517+
use constant CNAME => 5;
518+
use constant DNAME => 39;
566519

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

570-
# specify EDNS options { option => value }
571-
my $optionmask = { };
522+
# default values
572523

573-
return ($rcode, \@ans, \@auth, \@add, $headermask, $optionmask);
574-
}
524+
my ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);
575525

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

585-
$ns->main_loop;
586-
}
587-
588-
sub start_bind {
589-
if (defined $bind_server_port) {
528+
my ($len, $offset) = (undef, 12);
529+
while (1) {
530+
$len = unpack("\@$offset C", $recv_data);
531+
last if $len == 0;
532+
$offset++;
533+
push @name, unpack("\@$offset A$len", $recv_data);
534+
$offset += $len;
535+
}
590536

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

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

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

598-
my $s;
599-
my $i = 1;
600-
while (not $s) {
601-
$s = IO::Socket::INET->new(
602-
Proto => 'tcp',
603-
PeerAddr => "127.0.0.1",
604-
PeerPort => $bind_server_port
605-
);
606-
sleep 0.1;
607-
$i++ > 20 and last;
608-
}
609-
sleep 0.1;
610-
$s or die "cannot connect to DNS server";
611-
close($s) or die 'can not connect to DNS server';
544+
my @records = @{$aroute_map{$name}};
612545

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

615-
END {
616-
print("+ try to stop\n");
617-
stop_bind();
550+
#print("dns reply: $name $ttl $class $type $origin_addr\n");
551+
}
618552
}
619-
}
553+
554+
$len = @name;
555+
pack("n6 (C/a*)$len x n2", $id, $hdr | $rcode, 1, scalar @rdata,
556+
0, 0, @name, $type, $class) . join('', @rdata);
620557
}
621558

622-
sub stop_bind {
623-
if (defined $bind_pid) {
624-
# kill dns daemon
625-
kill $^O eq 'MSWin32' ? 15 : 'TERM', $bind_pid;
626-
wait;
559+
sub rd_addr {
560+
my ($ttl, $addr) = @_;
627561

628-
$bind_pid = undef;
629-
print ("+ DNS server: stop\n");
630-
}
562+
my $code = 'split(/\./, $addr)';
563+
564+
return pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';
565+
566+
pack 'n3N nC4', 0xc00c, A, IN, $ttl, eval "scalar $code", eval($code);
567+
}
568+
569+
sub dns_daemon {
570+
my ($port, $t, %extra) = @_;
571+
572+
print("+ dns daemon: try to listen on 127.0.0.1:$port\n");
573+
574+
my ($data, $recv_data);
575+
my $socket = IO::Socket::INET->new(
576+
LocalAddr => '127.0.0.1',
577+
LocalPort => $port,
578+
Proto => 'udp',
579+
)
580+
or die "Can't create listening socket: $!\n";
581+
582+
my $sel = IO::Select->new($socket);
583+
my $tcp = 0;
584+
585+
if ($extra{tcp}) {
586+
$tcp = port(8983, socket => 1);
587+
$sel->add($tcp);
588+
}
589+
590+
local $SIG{PIPE} = 'IGNORE';
591+
592+
# track number of relevant queries
593+
594+
my %state = (
595+
cnamecnt => 0,
596+
twocnt => 0,
597+
ttlcnt => 0,
598+
ttl0cnt => 0,
599+
cttlcnt => 0,
600+
cttl2cnt => 0,
601+
manycnt => 0,
602+
casecnt => 0,
603+
idcnt => 0,
604+
fecnt => 0,
605+
);
606+
607+
# signal we are ready
608+
609+
open my $fh, '>', $t->testdir() . '/' . $port;
610+
close $fh;
611+
612+
while (my @ready = $sel->can_read) {
613+
foreach my $fh (@ready) {
614+
if ($tcp == $fh) {
615+
my $new = $fh->accept;
616+
$new->autoflush(1);
617+
$sel->add($new);
618+
619+
} elsif ($socket == $fh) {
620+
$fh->recv($recv_data, 65536);
621+
$data = reply_handler($recv_data, $port,
622+
\%state);
623+
$fh->send($data);
624+
625+
} else {
626+
$fh->recv($recv_data, 65536);
627+
unless (length $recv_data) {
628+
$sel->remove($fh);
629+
$fh->close;
630+
next;
631+
}
632+
633+
again:
634+
my $len = unpack("n", $recv_data);
635+
$data = substr $recv_data, 2, $len;
636+
$data = reply_handler($data, $port, \%state,
637+
tcp => 1);
638+
$data = pack("n", length $data) . $data;
639+
$fh->send($data);
640+
$recv_data = substr $recv_data, 2 + $len;
641+
goto again if length $recv_data;
642+
}
643+
}
644+
}
631645
}
632646

633647
###############################################################################

0 commit comments

Comments
 (0)