1. braindamaged
  2. openbsd/src

Commits

bluhm  committed 0c6c99d

Allow the relayd regression tests to run the relayd on a different
host from client and server. Real TCP connections not running on
localhost provide a different timing uncovering more bugs.

  • Participants
  • Parent commits 7b29177
  • Branches master

Comments (0)

Files changed (8)

File regress/usr.sbin/relayd/Client.pm

View file
-#	$OpenBSD: Client.pm,v 1.2 2011/09/02 21:05:41 bluhm Exp $
+#	$OpenBSD: Client.pm,v 1.3 2012/12/28 20:36:25 bluhm Exp $
 
 # Copyright (c) 2010,2011 Alexander Bluhm <bluhm@openbsd.org>
 #
 	    Domain	=> $self->{connectdomain},
 	    PeerAddr    => $self->{connectaddr},
 	    PeerPort    => $self->{connectport},
-	) or die ref($self), " socket connect failed: $!";
+	) or die ref($self), " $iosocket socket connect failed: $!";
 	print STDERR "connect sock: ",$cs->sockhost()," ",$cs->sockport(),"\n";
 	print STDERR "connect peer: ",$cs->peerhost()," ",$cs->peerport(),"\n";
 

File regress/usr.sbin/relayd/Makefile

View file
-#	$OpenBSD: Makefile,v 1.3 2012/11/28 00:40:36 bluhm Exp $
+#	$OpenBSD: Makefile,v 1.4 2012/12/28 20:36:25 bluhm Exp $
 
 # The following ports must be installed for the regression tests:
 # p5-IO-Socket-INET6	object interface for AF_INET and AF_INET6 domain sockets
 REGRESS_TARGETS =	${TARGETS:S/^/run-regress-/}
 CLEANFILES =		*.log *.pem relayd.conf ktrace.out
 
+# Fill out these variables if you want to test relayd with
+# the relayd process running on a remote machine.  You have to specify
+# a local and remote ip address for the tcp connections.  To control
+# the remote machine you need a hostname for ssh to log in.  All the
+# test files must be in the same directory local and remote.
+
+LOCAL_ADDR ?=
+REMOTE_ADDR ?=
+REMOTE_SSH ?=
+
 # Set variables so that make runs with and without obj directory.
 # Only do that if necessary to keep visible output short.
 
 
 .for a in ${ARGS}
 run-regress-$a: $a
+.if empty (REMOTE_SSH)
 	time SUDO=${SUDO} KTRACE=${KTRACE} RELAYD=${RELAYD} perl ${PERLINC} ${PERLPATH}relayd.pl copy ${PERLPATH}$a
 	time SUDO=${SUDO} KTRACE=${KTRACE} RELAYD=${RELAYD} perl ${PERLINC} ${PERLPATH}relayd.pl splice ${PERLPATH}$a
+.else
+	ssh -t q0 ${SUDO} true
+	time SUDO=${SUDO} KTRACE=${KTRACE} RELAYD=${RELAYD} perl ${PERLINC} ${PERLPATH}remote.pl copy ${LOCAL_ADDR} ${REMOTE_ADDR} ${REMOTE_SSH} ${PERLPATH}$a
+	time SUDO=${SUDO} KTRACE=${KTRACE} RELAYD=${RELAYD} perl ${PERLINC} ${PERLPATH}remote.pl splice ${LOCAL_ADDR} ${REMOTE_ADDR} ${REMOTE_SSH} ${PERLPATH}$a
+.endif
 .endfor
 
 /etc/ssl/127.0.0.1.crt:

File regress/usr.sbin/relayd/Proc.pm

View file
-#	$OpenBSD: Proc.pm,v 1.2 2011/09/02 10:45:36 bluhm Exp $
+#	$OpenBSD: Proc.pm,v 1.3 2012/12/28 20:36:25 bluhm Exp $
 
 # Copyright (c) 2010,2011 Alexander Bluhm <bluhm@openbsd.org>
 #
 
 package Proc;
 use Carp;
-use List::Util qw(first);
+use Errno;
 use POSIX;
 use Time::HiRes qw(time alarm sleep);
 
 my %CHILDREN;
 
 sub kill_children {
-	my @pids = keys %CHILDREN
+	my @pids = @_ ? @_ : keys %CHILDREN
 	    or return;
-	if (my $sudo = $ENV{SUDO}) {
+	my @perms;
+	foreach my $pid (@pids) {
+		if (kill(TERM => $pid) != 1 and $!{EPERM}) {
+			push @perms, $pid;
+		}
+	}
+	if (my $sudo = $ENV{SUDO} and @perms) {
 		local $?;  # do not modify during END block
-		my @cmd = ($sudo, '/bin/kill', '-TERM', @pids);
+		my @cmd = ($sudo, '/bin/kill', '-TERM', @perms);
 		system(@cmd);
-	} else {
-		kill TERM => @pids;
 	}
-	%CHILDREN = ();
+	delete @CHILDREN{@pids};
 }
 
 BEGIN {
 sub run {
 	my $self = shift;
 
+	pipe(my $reader, my $writer)
+	    or die ref($self), " pipe to child failed";
 	defined(my $pid = fork())
 	    or die ref($self), " fork child failed";
 	if ($pid) {
 		$CHILDREN{$pid} = 1;
 		$self->{pid} = $pid;
+		close($reader);
+		$self->{pipe} = $writer;
 		return $self;
 	}
 	%CHILDREN = ();
 	};
 	open(STDERR, '>&', $self->{log})
 	    or die ref($self), " dup STDERR failed: $!";
+	close($writer);
+	open(STDIN, '<&', $reader)
+	    or die ref($self), " dup STDIN failed: $!";
+	close($reader);
 
 	$self->child();
 	print STDERR $self->{up}, "\n";
 	return $self;
 }
 
+sub kill_child {
+	my $self = shift;
+	kill_children($self->{pid});
+	return $self;
+}
+
 1;

File regress/usr.sbin/relayd/Relayd.pm

View file
-#	$OpenBSD: Relayd.pm,v 1.4 2012/11/28 00:40:36 bluhm Exp $
+#	$OpenBSD: Relayd.pm,v 1.5 2012/12/28 20:36:25 bluhm Exp $
 
 # Copyright (c) 2010-2012 Alexander Bluhm <bluhm@openbsd.org>
 #
 	$self->{connectport}
 	    or croak "$class connect port not given";
 
-	my $test = basename($self->{test} || "");
+	my $test = basename($self->{testfile} || "");
 	open(my $fh, '>', $self->{conffile})
 	    or die ref($self), " conf file $self->{conffile} create failed: $!";
 	print $fh "log all\n";
 	return $self;
 }
 
-sub down {
-	my $self = shift;
-	my @sudo = $ENV{SUDO} ? $ENV{SUDO} : ();
-	my @cmd = (@sudo, '/bin/kill', $self->{pid});
-	system(@cmd);
-	return Proc::down($self, @_);
-}
-
 sub child {
 	my $self = shift;
 	print STDERR $self->{up}, "\n";

File regress/usr.sbin/relayd/Remote.pm

View file
+#	$OpenBSD: Remote.pm,v 1.1 2012/12/28 20:36:25 bluhm Exp $
+
+# Copyright (c) 2010-2012 Alexander Bluhm <bluhm@openbsd.org>
+#
+# 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 Remote;
+use parent 'Proc';
+use Carp;
+use Cwd;
+use File::Basename;
+
+my %PIPES;
+
+sub close_pipes {
+	my @pipes = @_ ? @_ : keys %PIPES
+	    or return;
+	foreach (@pipes) {
+		# file descriptor cannot be a hash key, so use hash value
+		my $fh = $PIPES{$_};
+		# also print new line as close is delayed by forked processes
+		print $fh "close\n";
+		close($fh);
+	}
+	sleep 1;  # give other end a chance to finish process
+	delete @PIPES{@pipes};
+}
+
+END {
+	close_pipes();
+}
+
+sub new {
+	my $class = shift;
+	my %args = @_;
+	$args{logfile} ||= "remote.log";
+	$args{up} ||= "Started";
+	$args{down} ||= "parent terminating";
+	$args{func} = sub { Carp::confess "$class func may not be called" };
+	$args{remotessh}
+	    or croak "$class remote ssh host not given";
+	$args{forward}
+	    or croak "$class forward not given";
+	my $self = Proc::new($class, %args);
+	$self->{listenaddr}
+	    or croak "$class listen addr not given";
+	$self->{connectaddr}
+	    or croak "$class connect addr not given";
+	$self->{connectport}
+	    or croak "$class connect port not given";
+	return $self;
+}
+
+sub run {
+	my $self = Proc::run(shift, @_);
+	$PIPES{$self->{pipe}} = $self->{pipe};
+	return $self;
+}
+
+sub up {
+	my $self = Proc::up(shift, @_);
+	my $timeout = shift || 10;
+	my $lsock = $self->loggrep(qr/^listen sock: /, $timeout)
+	    or croak ref($self), " no listen sock in $self->{logfile} ".
+		"after $timeout seconds";
+	my($addr, $port) = $lsock =~ /: (\S+) (\S+)$/
+	    or croak ref($self), " no listen addr and port in $self->{logfile}";
+	$self->{listenaddr} = $addr;
+	$self->{listenport} = $port;
+	return $self;
+}
+
+sub child {
+	my $self = shift;
+
+	print STDERR $self->{up}, "\n";
+	my @opts = split(' ', $ENV{SSH_OPTIONS}) if $ENV{SSH_OPTIONS};
+	my @sudo = $ENV{SUDO} ? "SUDO=$ENV{SUDO}" : ();
+	my @ktrace = $ENV{KTRACE} ? "KTRACE=$ENV{KTRACE}" : ();
+	my @relayd = $ENV{RELAYD} ? "RELAYD=$ENV{RELAYD}" : ();
+	my $dir = dirname($0);
+	$dir = getcwd() if ! $dir || $dir eq '.';
+	my @cmd = ('ssh', @opts, $self->{remotessh},
+	    @sudo, @ktrace, @relayd, 'perl',
+	    '-I', $dir, $dir.'/'.basename($0), $self->{forward},
+	    $self->{listenaddr}, $self->{connectaddr}, $self->{connectport},
+	    ($self->{testfile} ? $dir.'/'.basename($self->{testfile}) : ()));
+	print STDERR "execute: @cmd\n";
+	exec @cmd;
+	die "Exec @cmd failed: $!";
+}
+
+sub close_child {
+	my $self = shift;
+	close_pipes(delete $self->{pipe});
+	return $self;
+}
+
+1;

File regress/usr.sbin/relayd/Server.pm

View file
-#	$OpenBSD: Server.pm,v 1.1 2011/09/01 17:33:17 bluhm Exp $
+#	$OpenBSD: Server.pm,v 1.2 2012/12/28 20:36:25 bluhm Exp $
 
 # Copyright (c) 2010,2011 Alexander Bluhm <bluhm@openbsd.org>
 #
 	    $self->{listenport} ? (LocalPort => $self->{listenport}) : (),
 	    SSL_key_file  => "server-key.pem",
 	    SSL_cert_file => "server-cert.pem",
-	) or die ref($self), " socket failed: $!";
+	) or die ref($self), " $iosocket socket listen failed: $!";
 	my $log = $self->{log};
 	print $log "listen sock: ",$ls->sockhost()," ",$ls->sockport(),"\n";
 	$self->{listenaddr} = $ls->sockhost() unless $self->{listenaddr};
 sub child {
 	my $self = shift;
 
+	my $iosocket = $self->{ssl} ? "IO::Socket::SSL" : "IO::Socket::INET6";
 	my $as = $self->{ls}->accept()
-	    or die ref($self), " socket accept failed: $!";
+	    or die ref($self), " $iosocket socket accept failed: $!";
 	print STDERR "accept sock: ",$as->sockhost()," ",$as->sockport(),"\n";
 	print STDERR "accept peer: ",$as->peerhost()," ",$as->peerport(),"\n";
 

File regress/usr.sbin/relayd/relayd.pl

View file
 #!/usr/bin/perl
-#	$OpenBSD: relayd.pl,v 1.5 2012/11/02 17:47:04 bluhm Exp $
+#	$OpenBSD: relayd.pl,v 1.6 2012/12/28 20:36:25 bluhm Exp $
 
 # Copyright (c) 2010-2012 Alexander Bluhm <bluhm@openbsd.org>
 #
     connectaddr         => "127.0.0.1",
     connectport         => $sport,
     %{$args{relayd}},
-    test                => $test,
+    testfile            => $test,
 );
 my $c = Client->new(
     func                => \&write_char,
 
 $c->down;
 $s->down;
+$r->kill_child;
 $r->down;
 
 foreach ([ client => $c ], [ relayd => $r ], [ server => $s ]) {

File regress/usr.sbin/relayd/remote.pl

View file
+#!/usr/bin/perl
+#	$OpenBSD: remote.pl,v 1.1 2012/12/28 20:36:25 bluhm Exp $
+
+# Copyright (c) 2010-2012 Alexander Bluhm <bluhm@openbsd.org>
+#
+# 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;
+use File::Basename;
+use File::Copy;
+use Socket;
+use Socket6;
+
+use Client;
+use Relayd;
+use Server;
+use Remote;
+require 'funcs.pl';
+
+sub usage {
+	die <<"EOF";
+usage:
+    remote.pl localport remoteaddr remoteport [test-args.pl]
+	Run test with local client and server.  Remote relayd
+	forwarding from remoteaddr remoteport to server localport
+	has to be started manually.
+    remote.pl copy|splice listenaddr connectaddr connectport [test-args.pl]
+	Only start remote relayd.
+    remote.pl copy|splice localaddr remoteaddr remotessh [test-args.pl]
+	Run test with local client and server.  Remote relayd is
+	started automatically with ssh on remotessh.
+EOF
+}
+
+my $test;
+our %args;
+if (@ARGV and -f $ARGV[-1]) {
+	$test = pop;
+	do $test
+	    or die "Do test file $test failed: ", $@ || $!;
+}
+my $mode =
+	@ARGV == 3 && $ARGV[0] =~ /^\d+$/ && $ARGV[2] =~ /^\d+$/ ? "manual" :
+	@ARGV == 4 && $ARGV[1] !~ /^\d+$/ && $ARGV[3] =~ /^\d+$/ ? "relay"  :
+	@ARGV == 4 && $ARGV[1] !~ /^\d+$/ && $ARGV[3] !~ /^\d+$/ ? "auto"   :
+	usage();
+
+my $r;
+if ($mode eq "relay") {
+	my($rport) = find_ports(num => 1);
+	$r = Relayd->new(
+	    forward             => $ARGV[0],
+	    %{$args{relayd}},
+	    listendomain        => AF_INET,
+	    listenaddr          => $ARGV[1],
+	    listenport          => $rport,
+	    connectdomain       => AF_INET,
+	    connectaddr         => $ARGV[2],
+	    connectport         => $ARGV[3],
+	    logfile             => dirname($0)."/remote.log",
+	    conffile            => dirname($0)."/relayd.conf",
+	    testfile            => $test,
+	);
+	open(my $log, '<', $r->{logfile})
+	    or die "Remote log file open failed: $!";
+	$SIG{__DIE__} = sub {
+		die @_ if $^S;
+		copy($log, \*STDERR);
+		warn @_;
+		exit 255;
+	};
+	copy($log, \*STDERR);
+	$r->run;
+	copy($log, \*STDERR);
+	$r->up;
+	copy($log, \*STDERR);
+	print STDERR "listen sock: $ARGV[1] $rport\n";
+	<STDIN>;
+	copy($log, \*STDERR);
+	print STDERR "stdin closed\n";
+	$r->kill_child;
+	$r->down;
+	copy($log, \*STDERR);
+
+	exit;
+}
+
+my $s = Server->new(
+    func                => \&read_char,
+    %{$args{server}},
+    listendomain        => AF_INET,
+    listenaddr          => ($mode eq "auto" ? $ARGV[1] : undef),
+    listenport          => ($mode eq "manual" ? $ARGV[0] : undef),
+);
+if ($mode eq "auto") {
+	$r = Remote->new(
+	    forward             => $ARGV[0],
+	    logfile             => "relayd.log",
+	    testfile            => $test,
+	    %{$args{relay}},
+	    remotessh           => $ARGV[3],
+	    listenaddr          => $ARGV[2],
+	    connectaddr         => $ARGV[1],
+	    connectport         => $s->{listenport},
+	);
+	$r->run->up;
+}
+my $c = Client->new(
+    func                => \&write_char,
+    %{$args{client}},
+    connectdomain       => AF_INET,
+    connectaddr         => ($mode eq "manual" ? $ARGV[1] : $r->{listenaddr}),
+    connectport         => ($mode eq "manual" ? $ARGV[2] : $r->{listenport}),
+);
+
+$s->run;
+$c->run->up;
+$s->up;
+
+$c->down;
+$s->down;
+$r->close_child;
+$r->down;
+
+foreach ([ client => $c ], [ relayd => $r ], [ server => $s ]) {
+	my($name, $proc) = @$_;
+	my $pattern = $args{$name}{loggrep} or next;
+	$pattern = [ $pattern ] unless ref($pattern) eq 'ARRAY';
+	foreach my $pat (@$pattern) {
+		if (ref($pat) eq 'HASH') {
+			while (my($re, $num) = each %$pat) {
+				my @matches = $proc->loggrep($re);
+				@matches == $num or
+				    die "$name matches @matches: $re => $num";
+			}
+		} else {
+			$proc->loggrep($pat)
+			    or die "$name log missing pattern: $pat";
+		}
+	}
+}
+
+exit if $args{nocheck};
+
+my @clen = $c->loggrep(qr/^LEN: /) or die "no client len"
+    unless $args{client}{nocheck};
+my @slen = $s->loggrep(qr/^LEN: /) or die "no server len"
+    unless $args{server}{nocheck};
+!@clen || !@slen || @clen ~~ @slen
+    or die "client: @clen", "server: @slen", "len mismatch";
+!defined($args{len}) || !$clen[0] || $clen[0] eq "LEN: $args{len}\n"
+    or die "client: $clen[0]", "len $args{len} expected";
+!defined($args{len}) || !$slen[0] || $slen[0] eq "LEN: $args{len}\n"
+    or die "server: $slen[0]", "len $args{len} expected";
+foreach my $len (map { ref eq 'ARRAY' ? @$_ : $_ } @{$args{lengths} || []}) {
+	my $clen = shift @clen;
+	$clen eq "LEN: $len\n"
+	    or die "client: $clen", "len $len expected";
+	my $slen = shift @slen;
+	$slen eq "LEN: $len\n"
+	    or die "server: $slen", "len $len expected";
+}
+
+my $cmd5 = $c->loggrep(qr/^MD5: /) unless $args{client}{nocheck};
+my $smd5 = $s->loggrep(qr/^MD5: /) unless $args{server}{nocheck};
+!$cmd5 || !$smd5 || ref($args{md5}) eq 'ARRAY' || $cmd5 eq $smd5
+    or die "client: $cmd5", "server: $smd5", "md5 mismatch";
+my $md5 = ref($args{md5}) eq 'ARRAY' ? join('|', @{$args{md5}}) : $args{md5};
+!$md5 || !$cmd5 || $cmd5 =~ /^MD5: ($md5)$/
+    or die "client: $cmd5", "md5 $md5 expected";
+!$md5 || !$smd5 || $smd5 =~ /^MD5: ($md5)$/
+    or die "server: $smd5", "md5 $md5 expected";