diff --git a/regress/Makefile b/regress/Makefile index 2f7b526..631657a 100644 --- a/regress/Makefile +++ b/regress/Makefile @@ -9,7 +9,12 @@ PFRESOLVED ?= ${.CURDIR}/../pfresolved PERLS = Proc.pm Pfresolved.pm funcs.pl pfresolved.pl ARGS != cd ${.CURDIR} && ls args-*.pl REGRESS_TARGETS = ${ARGS:S/^/run-/} -CLEANFILES = *.log *.conf ktrace.out stamp-* *.pid *.ktrace +CLEANFILES = *.log *.conf ktrace.out stamp-* *.pid *.ktrace *.zone + +REGRESS_SETUP_ONCE = chmod-obj +chmod-obj: + # nsd user needs read permission for zone file + -chmod o+rx ${.OBJDIR} # Set variables so that make runs with and without obj directory. # Only do that if necessary to keep visible output short. diff --git a/regress/Nsd.pm b/regress/Nsd.pm new file mode 100644 index 0000000..0dc419b --- /dev/null +++ b/regress/Nsd.pm @@ -0,0 +1,89 @@ +# $OpenBSD$ + +# Copyright (c) 2010-2023 Alexander Bluhm +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +use strict; +use warnings; + +package Nsd; +use parent 'Proc'; +use Carp; +use File::Basename; +use Sys::Hostname; + +sub new { + my $class = shift; + my %args = @_; + $args{conffile} ||= "nsd.conf"; + $args{down} ||= "shutting down"; + $args{func} = sub { Carp::confess "$class func may not be called" }; + $args{ktraceexec} = "ktrace" if $args{ktrace}; + $args{ktraceexec} = $ENV{KTRACE} if $ENV{KTRACE}; + $args{ktracefile} ||= "nsd.ktrace"; + $args{logfile} ||= "nsd.log"; + $args{serial} ||= time(); + $args{up} ||= "nsd started"; + + my $self = Proc::new($class, %args); + + my $test = basename($self->{testfile} || ""); + open(my $fh, '>', $self->{conffile}) or die ref($self), + " config file '$self->{conffile}' create failed: $!"; + print $fh "# test $test\n"; + print $fh "server:\n"; + print $fh " chroot: \"\"\n"; + print $fh " ip-address: $self->{addr}\n"; + print $fh " pidfile: \"\"\n"; + print $fh " port: $self->{port}\n"; + print $fh " verbosity: 3\n"; + print $fh " zonesdir: .\n"; + print $fh "zone:\n"; + print $fh " name: regress.invalid.\n"; + print $fh " zonefile: nsd.zone\n"; + + open(my $fz, '>', "nsd.zone") or die ref($self), + " zone file 'nsd.zone' create failed: $!"; + print $fz "; test $test\n"; + print $fz "\$ORIGIN regress.invalid.\n"; + print $fz "\$TTL 86400\n"; + print $fz "\@ IN SOA pfresolved root.pfresolved (\n"; + print $fz " $args{serial} ; serial number\n"; + print $fz " 7200 ; refresh\n"; + print $fz " 600 ; retry\n"; + print $fz " 86400 ; expire\n"; + print $fz " 3600 ; minimum TTL\n"; + print $fz " )\n"; + foreach my $r (@{$self->{record_list} || []}) { + print $fz "$r\n"; + } + + return $self; +} + +sub child { + my $self = shift; + my @sudo = $ENV{SUDO} ? $ENV{SUDO} : (); + + my @ktrace; + @ktrace = ($self->{ktraceexec}, "-i", "-f", $self->{ktracefile}) + if $self->{ktraceexec}; + my @cmd = (@sudo, @ktrace, "/usr/sbin/nsd", "-d", + "-c", $self->{conffile}); + print STDERR "execute: @cmd\n"; + exec @cmd; + die ref($self), " exec '@cmd' failed: $!"; +} + +1; diff --git a/regress/Pfctl.pm b/regress/Pfctl.pm index 2b901ec..3d34d92 100644 --- a/regress/Pfctl.pm +++ b/regress/Pfctl.pm @@ -29,6 +29,7 @@ sub new { $args{up} ||= "Table"; my $self = Proc::new($class, %args); + return $self; } @@ -53,7 +54,7 @@ sub func { my $self = shift; my @sudo = $ENV{SUDO} ? $ENV{SUDO} : (); - my @cmd = (@sudo, qw(pfctl -t regress-pfresolved -T show)); + my @cmd = (@sudo, qw(/sbin/pfctl -t regress-pfresolved -T show)); system(@cmd) and die die ref($self), " command '@cmd' failed: $?"; } diff --git a/regress/Pfresolved.pm b/regress/Pfresolved.pm index 6d29a3b..221dd0f 100644 --- a/regress/Pfresolved.pm +++ b/regress/Pfresolved.pm @@ -20,22 +20,22 @@ use warnings; package Pfresolved; use parent 'Proc'; use Carp; -use Cwd; use File::Basename; use Sys::Hostname; sub new { my $class = shift; my %args = @_; + $args{conffile} ||= "pfresolved.conf"; + $args{down} ||= "parent terminating"; + $args{execfile} ||= $ENV{PFRESOLVED} ? $ENV{PFRESOLVED} : "pfresolved"; + $args{func} = sub { Carp::confess "$class func may not be called" }; $args{ktraceexec} = "ktrace" if $args{ktrace}; $args{ktraceexec} = $ENV{KTRACE} if $ENV{KTRACE}; $args{ktracefile} ||= "pfresolved.ktrace"; $args{logfile} ||= "pfresolved.log"; $args{up} ||= "forwarder starting"; - $args{down} ||= "parent terminating"; - $args{func} = sub { Carp::confess "$class func may not be called" }; - $args{execfile} ||= $ENV{PFRESOLVED} ? $ENV{PFRESOLVED} : "pfresolved"; - $args{conffile} ||= "pfresolved.conf"; + my $self = Proc::new($class, %args); my $test = basename($self->{testfile} || ""); @@ -43,9 +43,10 @@ sub new { " config file '$self->{conffile}' create failed: $!"; print $fh "# test $test\n"; print $fh "regress-pfresolved {\n"; - print $fh "\t", join(",", @{$self->{address_list}}), "\n" - if $self->{address_list}; - print $fh "\n}\n"; + foreach my $a (@{$self->{address_list} || []}) { + print $fh " $a\n"; + } + print $fh "}\n"; return $self; } @@ -68,8 +69,12 @@ sub child { my @ktrace; @ktrace = ($self->{ktraceexec}, "-i", "-f", $self->{ktracefile}) if $self->{ktraceexec}; + my $resolver; + $resolver = $self->{addr} if $self->{addr}; + $resolver .= '@'.$self->{port} if $self->{port}; my @cmd = (@sudo, @ktrace, $self->{execfile}, "-dvv", "-f", $self->{conffile}); + push @cmd, "-r", $resolver if $resolver; print STDERR "execute: @cmd\n"; exec @cmd; die ref($self), " exec '@cmd' failed: $!"; diff --git a/regress/args-nsd.pl b/regress/args-nsd.pl new file mode 100644 index 0000000..82c25e2 --- /dev/null +++ b/regress/args-nsd.pl @@ -0,0 +1,40 @@ +# Create zone file with A and AAAA records in zone regress.invalid. +# Start nsd with zone file listening on 127.0.0.1. +# Write hosts of regress.invalid zone into pfresolved config. +# Start pfresolved with nsd as resolver. +# Wait until pfresolved creates table regress-pfresolved. +# Read IP addresses from pf table with pfctl. +# Check that pfresolved added 192.0.2. and 2001:DB8:: addresses. +# Check that table contains all 192.0.2. and 2001:DB8:: addresses. + +use strict; +use warnings; + +our %args = ( + nsd => { + record_list => [ + "foo IN A 192.0.2.1", + "bar IN AAAA 2001:DB8::1", + "foobar IN A 192.0.2.2", + "foobar IN AAAA 2001:DB8::2", + ], + }, + pfresolved => { + address_list => [ map { "$_.regress.invalid." } qw(foo bar foobar) ], + loggrep => { + qr{added: 192.0.2.1/32,} => 1, + qr{added: 2001:DB8::1/128,} => 1, + qr{added: 192.0.2.2/32,} => 1, + qr{added: 2001:DB8::2/128,} => 1, + }, + }, + pfctl => { + updates => 5, + loggrep => { + qr/^ 192.0.2.[12]$/ => 2, + qr/^ 2001:DB8::[12]$/ => 2, + }, + }, +); + +1; diff --git a/regress/funcs.pl b/regress/funcs.pl index 2e2ed15..600c55d 100644 --- a/regress/funcs.pl +++ b/regress/funcs.pl @@ -16,6 +16,28 @@ use strict; use warnings; +use IO::Socket::IP; + +sub find_ports { + my %args = @_; + my $num = delete $args{num} // 1; + my $domain = delete $args{domain} // AF_INET; + my $addr = delete $args{addr} // "127.0.0.1"; + my $proto = delete $args{proto} // "udp"; + $proto = "tcp" if $proto eq "tls"; + + my @sockets = (1..$num); + foreach my $s (@sockets) { + $s = IO::Socket::IP->new( + Domain => $domain, + LocalAddr => $addr, + Proto => $proto, + ) or die "find_ports: create and bind socket failed: $!"; + } + my @ports = map { $_->sockport() } @sockets; + + return wantarray ? @ports : $ports[0]; +} sub check_logs { my ($n, $d, $s, %args) = @_; diff --git a/regress/pfresolved.pl b/regress/pfresolved.pl index e8fdc1f..0843d0f 100644 --- a/regress/pfresolved.pl +++ b/regress/pfresolved.pl @@ -18,6 +18,7 @@ use strict; use warnings; +use Nsd; use Pfresolved; use Pfctl; require 'funcs.pl'; @@ -35,22 +36,29 @@ sub usage { } @ARGV == 0 or usage(); +my $n = Nsd->new( + addr => $args{nsd}{listen}{addr} //= "127.0.0.1", + port => scalar find_ports(%{$args{nsd}{listen}}), + %{$args{nsd}}, + testfile => $testfile, +) if $args{nsd}; my $d = Pfresolved->new( + addr => $n && $n->{addr}, + port => $n && $n->{port}, %{$args{pfresolved}}, testfile => $testfile, ); my $s = Pfctl->new( %{$args{pfctl}}, pfresolved => $d, -); - -$d->run; -$d->up; +) if $args{pfctl}; -$s->run; -$s->up; +$n->run->up if $n; +$d->run->up; +$s->run->up if $s; -$d->kill_child; -$d->down; +$s->down if $s; +$d->kill_child->down; +$n->kill_child->down if $n; -check_logs(undef, $d, $s, %args); +check_logs($n, $d, $s, %args);