summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Bluhm <bluhm@cvs.openbsd.org>2011-09-01 17:33:18 +0000
committerAlexander Bluhm <bluhm@cvs.openbsd.org>2011-09-01 17:33:18 +0000
commita1045c89b975d3bf62a4d8059fc39df13a626292 (patch)
treeb93389704a9f1ef1c14480970b3f386eaf14738f
parentaa843864e79d5d0d29cd8a16ed547d2719623e5b (diff)
Add regression tests for relaying connections through relayd. This
is useful to ensure that socket splicing is working correctly. Server, relayd, client with different config are started for each subtest. Data must pass unmodified.
-rw-r--r--regress/usr.sbin/relayd/Client.pm61
-rw-r--r--regress/usr.sbin/relayd/Makefile43
-rw-r--r--regress/usr.sbin/relayd/Proc.pm167
-rw-r--r--regress/usr.sbin/relayd/Relayd.pm108
-rw-r--r--regress/usr.sbin/relayd/Server.pm67
-rw-r--r--regress/usr.sbin/relayd/args-default.pl11
-rw-r--r--regress/usr.sbin/relayd/args-reverse.pl17
-rw-r--r--regress/usr.sbin/relayd/args-ssl-client.pl17
-rw-r--r--regress/usr.sbin/relayd/args-ssl-server.pl17
-rw-r--r--regress/usr.sbin/relayd/args-ssl.pl21
-rwxr-xr-xregress/usr.sbin/relayd/direct.pl75
-rw-r--r--regress/usr.sbin/relayd/funcs.pl126
-rw-r--r--regress/usr.sbin/relayd/relayd.pl99
13 files changed, 829 insertions, 0 deletions
diff --git a/regress/usr.sbin/relayd/Client.pm b/regress/usr.sbin/relayd/Client.pm
new file mode 100644
index 00000000000..3b70147babe
--- /dev/null
+++ b/regress/usr.sbin/relayd/Client.pm
@@ -0,0 +1,61 @@
+# $OpenBSD: Client.pm,v 1.1 2011/09/01 17:33:17 bluhm Exp $
+
+# Copyright (c) 2010,2011 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 Client;
+use parent 'Proc';
+use Carp;
+use Socket qw(IPPROTO_TCP TCP_NODELAY);
+use Socket6;
+use IO::Socket;
+use IO::Socket::INET6;
+use IO::Socket::SSL;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{logfile} ||= "client.log";
+ $args{up} ||= "Connected";
+ $args{down} ||= "Shutdown|Broken pipe|Connection reset by peer";
+ my $self = Proc::new($class, %args);
+ $self->{connectdomain}
+ or croak "$class connect domain not given";
+ $self->{connectaddr}
+ or croak "$class connect addr not given";
+ $self->{connectport}
+ or croak "$class connect port not given";
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ my $iosocket = $self->{ssl} ? "IO::Socket::SSL" : "IO::Socket::INET6";
+ my $cs = $iosocket->new(
+ Proto => "tcp",
+ Domain => $self->{connectdomain},
+ PeerAddr => $self->{connectaddr},
+ PeerPort => $self->{connectport},
+ ) or die ref($self), " socket connect failed: $!";
+ print STDERR "connect sock: ",$cs->sockhost()," ",$cs->sockport(),"\n";
+ print STDERR "connect peer: ",$cs->peerhost()," ",$cs->peerport(),"\n";
+
+ *STDIN = *STDOUT = $self->{cs} = $cs;
+}
+
+1;
diff --git a/regress/usr.sbin/relayd/Makefile b/regress/usr.sbin/relayd/Makefile
new file mode 100644
index 00000000000..a471c10eeb8
--- /dev/null
+++ b/regress/usr.sbin/relayd/Makefile
@@ -0,0 +1,43 @@
+# $OpenBSD: Makefile,v 1.1 2011/09/01 17:33:17 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
+# p5-Socket6 Perl defines relating to AF_INET6 sockets
+# p5-IO-Socket-SSL perl interface to SSL sockets
+
+ARGS != cd ${.CURDIR} && ls args-*.pl
+TARGETS ?= ${ARGS}
+REGRESS_TARGETS = ${TARGETS:S/^/run-regress-/}
+CLEANFILES = *.log *.pem relayd.conf ktrace.out
+
+# Set variables so that make runs with and without obj directory.
+# Only do that if necessary to keep visible output short.
+
+.if ${.CURDIR} == ${.OBJDIR}
+PERLINC =
+PERLPATH =
+.else
+PERLINC = -I${.CURDIR}
+PERLPATH = ${.CURDIR}/
+.endif
+
+# The arg tests take a perl hash with arguments controlling the
+# test parameters. Generally they consist of client, relayd, server.
+
+.for a in ${ARGS}
+run-regress-$a: $a
+ time SUDO=${SUDO} perl ${PERLINC} ${PERLPATH}relayd.pl copy ${PERLPATH}$a
+ time SUDO=${SUDO} perl ${PERLINC} ${PERLPATH}relayd.pl splice ${PERLPATH}$a
+.endfor
+
+/etc/ssl/127.0.0.1.crt:
+ ${SUDO} openssl req -batch -new -nodes -newkey rsa -keyout /etc/ssl/private/127.0.0.1.key -subj /CN=127.0.0.1/ -x509 -out $@
+
+server-cert.pem:
+ openssl req -batch -new -nodes -newkey rsa -keyout server-key.pem -subj /CN=localhost/ -x509 -out $@
+
+${REGRESS_TARGETS:M*ssl-client*}: /etc/ssl/127.0.0.1.crt
+
+${REGRESS_TARGETS:M*ssl-server*}: server-cert.pem
+
+.include <bsd.regress.mk>
diff --git a/regress/usr.sbin/relayd/Proc.pm b/regress/usr.sbin/relayd/Proc.pm
new file mode 100644
index 00000000000..b43f13978bc
--- /dev/null
+++ b/regress/usr.sbin/relayd/Proc.pm
@@ -0,0 +1,167 @@
+# $OpenBSD: Proc.pm,v 1.1 2011/09/01 17:33:17 bluhm Exp $
+
+# Copyright (c) 2010,2011 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 Proc;
+use Carp;
+use List::Util qw(first);
+use POSIX;
+use Time::HiRes qw(time alarm sleep);
+
+my %CHILDREN;
+
+sub kill_children {
+ my @pids = keys %CHILDREN
+ or return;
+ if (my $sudo = $ENV{SUDO}) {
+ local $?; # do not modify during END block
+ my @cmd = ($sudo, '/bin/kill', '-TERM', @pids);
+ system(@cmd);
+ } else {
+ kill TERM => @pids;
+ }
+ %CHILDREN = ();
+}
+
+BEGIN {
+ $SIG{TERM} = $SIG{INT} = sub {
+ my $sig = shift;
+ kill_children();
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+ POSIX::raise($sig);
+ };
+}
+
+END {
+ kill_children();
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+}
+
+sub new {
+ my $class = shift;
+ my $self = { @_ };
+ $self->{down} ||= "Shutdown";
+ $self->{func} && ref($self->{func}) eq 'CODE'
+ or croak "$class func not given";
+ $self->{logfile}
+ or croak "$class log file not given";
+ open(my $fh, '>', $self->{logfile})
+ or die "$class log file $self->{logfile} create failed: $!";
+ $self->{log} = $fh;
+ return bless $self, $class;
+}
+
+sub run {
+ my $self = shift;
+
+ defined(my $pid = fork())
+ or die ref($self), " fork child failed";
+ if ($pid) {
+ $CHILDREN{$pid} = 1;
+ $self->{pid} = $pid;
+ return $self;
+ }
+ %CHILDREN = ();
+ $SIG{TERM} = $SIG{INT} = 'DEFAULT';
+ $SIG{__DIE__} = sub {
+ die @_ if $^S;
+ warn @_;
+ IO::Handle::flush(\*STDERR);
+ POSIX::_exit(255);
+ };
+ open(STDERR, '>&', $self->{log})
+ or die ref($self), " dup STDERR failed: $!";
+
+ $self->child();
+ print STDERR $self->{up}, "\n";
+ $self->{func}->($self);
+ print STDERR "Shutdown", "\n";
+ IO::Handle::flush(\*STDOUT);
+ IO::Handle::flush(\*STDERR);
+
+ POSIX::_exit(0);
+}
+
+sub wait {
+ my $self = shift;
+ my $flags = shift;
+
+ my $pid = $self->{pid}
+ or croak ref($self), " no child pid";
+ my $kid = waitpid($pid, $flags);
+ if ($kid > 0) {
+ my $status = $?;
+ my $code;
+ $code = "exit: ". WEXITSTATUS($?) if WIFEXITED($?);
+ $code = "signal: ". WTERMSIG($?) if WIFSIGNALED($?);
+ $code = "stop: ". WSTOPSIG($?) if WIFSTOPPED($?);
+ delete $CHILDREN{$pid} if WIFEXITED($?) || WIFSIGNALED($?);
+ return wantarray ? ($kid, $status, $code) : $kid;
+ }
+ return $kid;
+}
+
+sub loggrep {
+ my $self = shift;
+ my($regex, $timeout) = @_;
+
+ my $end = time() + $timeout if $timeout;
+
+ do {
+ my($kid, $status, $code) = $self->wait(WNOHANG);
+ if ($kid > 0 && $status != 0) {
+ # child terminated with failure
+ die ref($self), " child status: $status $code";
+ }
+ open(my $fh, '<', $self->{logfile})
+ or die ref($self), " log file open failed: $!";
+ my $match = first { /$regex/ } <$fh>;
+ return $match if $match;
+ close($fh);
+ # pattern not found
+ if ($kid == 0) {
+ # child still running, wait for log data
+ sleep .1;
+ } else {
+ # child terminated, no new log data possible
+ return;
+ }
+ } while ($timeout and time() < $end);
+
+ return;
+}
+
+sub up {
+ my $self = shift;
+ my $timeout = shift || 10;
+ $self->loggrep(qr/$self->{up}/, $timeout)
+ or croak ref($self), " no $self->{up} in $self->{logfile} ".
+ "after $timeout seconds";
+ return $self;
+}
+
+sub down {
+ my $self = shift;
+ my $timeout = shift || 30;
+ $self->loggrep(qr/$self->{down}/, $timeout)
+ or croak ref($self), " no $self->{down} in $self->{logfile} ".
+ "after $timeout seconds";
+ return $self;
+}
+
+1;
diff --git a/regress/usr.sbin/relayd/Relayd.pm b/regress/usr.sbin/relayd/Relayd.pm
new file mode 100644
index 00000000000..9cd55d868be
--- /dev/null
+++ b/regress/usr.sbin/relayd/Relayd.pm
@@ -0,0 +1,108 @@
+# $OpenBSD: Relayd.pm,v 1.1 2011/09/01 17:33:17 bluhm Exp $
+
+# Copyright (c) 2010,2011 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 Relayd;
+use parent 'Proc';
+use Carp;
+use File::Basename;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{logfile} ||= "relayd.log";
+ $args{up} ||= "Started";
+ $args{down} ||= "parent terminating";
+ $args{func} = sub { Carp::confess "$class func may not be called" };
+ $args{conffile} ||= "relayd.conf";
+ $args{forward}
+ or croak "$class forward not given";
+ my $self = Proc::new($class, %args);
+ ref($self->{protocol}) eq 'ARRAY'
+ or $self->{protocol} = [ split("\n", $self->{protocol} || "") ];
+ ref($self->{relay}) eq 'ARRAY'
+ or $self->{relay} = [ split("\n", $self->{relay} || "") ];
+ $self->{listenaddr}
+ or croak "$class listen addr not given";
+ $self->{listenport}
+ or croak "$class listen port not given";
+ $self->{connectaddr}
+ or croak "$class connect addr not given";
+ $self->{connectport}
+ or croak "$class connect port not given";
+
+ open(my $fh, '>', $self->{conffile})
+ or die ref($self), " conf file $self->{conffile} create failed: $!";
+ my $test = basename($self->{test} || "");
+
+ my @protocol = @{$self->{protocol}};
+ my $proto = shift @protocol;
+ $proto = defined($proto) ? "$proto " : "";
+ unshift @protocol,
+ $self->{forward} eq "splice" ? "tcp splice" :
+ $self->{forward} eq "copy" ? "tcp no splice" :
+ die ref($self), " invalid forward $self->{forward}"
+ unless grep { /splice/ } @protocol;
+ print $fh "${proto}protocol proto-$test {";
+ print $fh map { "\n\t$_" } @protocol;
+ print $fh "\n}\n";
+
+ my @relay = @{$self->{relay}};
+ print $fh "relay relay-$test {";
+ print $fh "\n\tprotocol proto-$test"
+ unless grep { /^protocol / } @relay;
+ my $ssl = $self->{listenssl} ? " ssl" : "";
+ print $fh "\n\tlisten on $self->{listenaddr} ".
+ "port $self->{listenport}$ssl" unless grep { /^listen / } @relay;
+ my $withssl = $self->{forwardssl} ? " with ssl" : "";
+ print $fh "\n\tforward$withssl to $self->{connectaddr} ".
+ "port $self->{connectport}" unless grep { /^forward / } @relay;
+ print $fh map { "\n\t$_" } @relay;
+ print $fh "\n}\n";
+
+ return $self;
+}
+
+sub up {
+ my $self = Proc::up(shift, @_);
+ my $timeout = shift || 10;
+ my $lsock = $self->loggrep(qr/relay_launch: /, $timeout)
+ or croak ref($self), " no relay_launch in $self->{logfile} ".
+ "after $timeout seconds";
+ return $self;
+}
+
+sub down {
+ my $self = shift;
+ my @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";
+ my @sudo = $ENV{SUDO} || ();
+ my $relayd = $ENV{RELAYD} || "relayd";
+ my @cmd = (@sudo, $relayd, '-dvv', '-f', $self->{conffile});
+ exec @cmd;
+ die "Exec @cmd failed: $!";
+}
+
+1;
diff --git a/regress/usr.sbin/relayd/Server.pm b/regress/usr.sbin/relayd/Server.pm
new file mode 100644
index 00000000000..2db2a276e1c
--- /dev/null
+++ b/regress/usr.sbin/relayd/Server.pm
@@ -0,0 +1,67 @@
+# $OpenBSD: Server.pm,v 1.1 2011/09/01 17:33:17 bluhm Exp $
+
+# Copyright (c) 2010,2011 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 Server;
+use parent 'Proc';
+use Carp;
+use Socket qw(IPPROTO_TCP TCP_NODELAY);
+use Socket6;
+use IO::Socket;
+use IO::Socket::INET6;
+use IO::Socket::SSL;
+
+sub new {
+ my $class = shift;
+ my %args = @_;
+ $args{logfile} ||= "server.log";
+ $args{up} ||= "Accepted";
+ my $self = Proc::new($class, %args);
+ $self->{listendomain}
+ or croak "$class listen domain not given";
+ my $iosocket = $self->{ssl} ? "IO::Socket::SSL" : "IO::Socket::INET6";
+ my $ls = $iosocket->new(
+ Proto => "tcp",
+ ReuseAddr => 1,
+ Domain => $self->{listendomain},
+ Listen => 1,
+ $self->{listenaddr} ? (LocalAddr => $self->{listenaddr}) : (),
+ $self->{listenport} ? (LocalPort => $self->{listenport}) : (),
+ SSL_key_file => "server-key.pem",
+ SSL_cert_file => "server-cert.pem",
+ ) or die ref($self), " socket failed: $!";
+ my $log = $self->{log};
+ print $log "listen sock: ",$ls->sockhost()," ",$ls->sockport(),"\n";
+ $self->{listenaddr} = $ls->sockhost() unless $self->{listenaddr};
+ $self->{listenport} = $ls->sockport() unless $self->{listenport};
+ $self->{ls} = $ls;
+ return $self;
+}
+
+sub child {
+ my $self = shift;
+
+ my $as = $self->{ls}->accept()
+ or die ref($self), " socket accept failed: $!";
+ print STDERR "accept sock: ",$as->sockhost()," ",$as->sockport(),"\n";
+ print STDERR "accept peer: ",$as->peerhost()," ",$as->peerport(),"\n";
+
+ *STDIN = *STDOUT = $self->{as} = $as;
+}
+
+1;
diff --git a/regress/usr.sbin/relayd/args-default.pl b/regress/usr.sbin/relayd/args-default.pl
new file mode 100644
index 00000000000..28e53a92126
--- /dev/null
+++ b/regress/usr.sbin/relayd/args-default.pl
@@ -0,0 +1,11 @@
+# test default values
+
+use strict;
+use warnings;
+
+our %args = (
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/usr.sbin/relayd/args-reverse.pl b/regress/usr.sbin/relayd/args-reverse.pl
new file mode 100644
index 00000000000..d9005e6f00f
--- /dev/null
+++ b/regress/usr.sbin/relayd/args-reverse.pl
@@ -0,0 +1,17 @@
+# test reverse sending from server to client
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ func => \&read_char,
+ },
+ server => {
+ func => \&write_char,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/usr.sbin/relayd/args-ssl-client.pl b/regress/usr.sbin/relayd/args-ssl-client.pl
new file mode 100644
index 00000000000..2c657a5f1bd
--- /dev/null
+++ b/regress/usr.sbin/relayd/args-ssl-client.pl
@@ -0,0 +1,17 @@
+# test client ssl connection
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ ssl => 1,
+ },
+ relay => {
+ listenssl => 1,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/usr.sbin/relayd/args-ssl-server.pl b/regress/usr.sbin/relayd/args-ssl-server.pl
new file mode 100644
index 00000000000..629e46582cb
--- /dev/null
+++ b/regress/usr.sbin/relayd/args-ssl-server.pl
@@ -0,0 +1,17 @@
+# test server ssl connection
+
+use strict;
+use warnings;
+
+our %args = (
+ relay => {
+ forwardssl => 1,
+ },
+ server => {
+ ssl => 1,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/usr.sbin/relayd/args-ssl.pl b/regress/usr.sbin/relayd/args-ssl.pl
new file mode 100644
index 00000000000..80653efb12d
--- /dev/null
+++ b/regress/usr.sbin/relayd/args-ssl.pl
@@ -0,0 +1,21 @@
+# test both client and server ssl connection
+
+use strict;
+use warnings;
+
+our %args = (
+ client => {
+ ssl => 1,
+ },
+ relay => {
+ forwardssl => 1,
+ listenssl => 1,
+ },
+ server => {
+ ssl => 1,
+ },
+ len => 251,
+ md5 => "bc3a3f39af35fe5b1687903da2b00c7f",
+);
+
+1;
diff --git a/regress/usr.sbin/relayd/direct.pl b/regress/usr.sbin/relayd/direct.pl
new file mode 100755
index 00000000000..417a38a606b
--- /dev/null
+++ b/regress/usr.sbin/relayd/direct.pl
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+# $OpenBSD: direct.pl,v 1.1 2011/09/01 17:33:17 bluhm Exp $
+
+# Copyright (c) 2010,2011 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 Socket;
+use Socket6;
+
+use Client;
+use Server;
+require 'funcs.pl';
+
+our %args;
+if (my $test = pop) {
+ do $test
+ or die "Do test file $test failed: ", $@ || $!;
+}
+
+@ARGV == 0 or die "usage: direct.pl [test-args.pl]\n";
+
+my $s = Server->new(
+ func => \&read_char,
+ %{$args{server}},
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+);
+my $c = Client->new(
+ func => \&write_char,
+ %{$args{client}},
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $s->{listenport},
+);
+
+$s->run;
+$c->run->up;
+$s->up;
+
+$c->down;
+$s->down;
+
+exit if $args{nocheck} || $args{client}{nocheck};
+
+my $clen = $c->loggrep(qr/^LEN: /) unless $args{client}{nocheck};
+my $slen = $s->loggrep(qr/^LEN: /) unless $args{server}{nocheck};
+!$clen || !$slen || $clen eq $slen
+ or die "client: $clen", "server: $slen", "len mismatch";
+!defined($args{len}) || !$clen || $clen eq "LEN: $args{len}\n"
+ or die "client: $clen", "len $args{len} expected";
+!defined($args{len}) || !$slen || $slen eq "LEN: $args{len}\n"
+ or die "server: $slen", "len $args{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";
diff --git a/regress/usr.sbin/relayd/funcs.pl b/regress/usr.sbin/relayd/funcs.pl
new file mode 100644
index 00000000000..c628e791194
--- /dev/null
+++ b/regress/usr.sbin/relayd/funcs.pl
@@ -0,0 +1,126 @@
+# $OpenBSD: funcs.pl,v 1.1 2011/09/01 17:33:17 bluhm Exp $
+
+# Copyright (c) 2010,2011 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 feature 'switch';
+use Errno;
+use Digest::MD5;
+use Socket;
+use Socket6;
+use IO::Socket;
+use IO::Socket::INET6;
+
+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 @sockets = (1..$num);
+ foreach my $s (@sockets) {
+ $s = IO::Socket::INET6->new(
+ Proto => "tcp",
+ Domain => $domain,
+ $addr ? (LocalAddr => $addr) : (),
+ ) or die "find_ports: create and bind socket failed: $!";
+ }
+ my @ports = map { $_->sockport() } @sockets;
+
+ return @ports;
+}
+
+########################################################################
+# Client funcs
+########################################################################
+
+sub write_char {
+ my $self = shift;
+ my $len = shift // $self->{len} // 251;
+ my $sleep = $self->{sleep};
+
+ my $ctx = Digest::MD5->new();
+ my $char = '0';
+ for (my $i = 1; $i < $len; $i++) {
+ $ctx->add($char);
+ print $char
+ or die ref($self), " print failed: $!";
+ given ($char) {
+ when(/9/) { $char = 'A' }
+ when(/Z/) { $char = 'a' }
+ when(/z/) { $char = "\n" }
+ when(/\n/) { print STDERR "."; $char = '0' }
+ default { $char++ }
+ }
+ if ($self->{sleep}) {
+ IO::Handle::flush(\*STDOUT);
+ sleep $self->{sleep};
+ }
+ }
+ if ($len) {
+ $char = "\n";
+ $ctx->add($char);
+ print $char
+ or die ref($self), " print failed: $!";
+ print STDERR ".\n";
+ }
+ IO::Handle::flush(\*STDOUT);
+
+ print STDERR "LEN: ", $len, "\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+sub errignore {
+ $SIG{PIPE} = 'IGNORE';
+ $SIG{__DIE__} = sub {
+ die @_ if $^S;
+ warn @_;
+ my $soerror;
+ $soerror = getsockopt(STDIN, SOL_SOCKET, SO_ERROR);
+ print STDERR "ERROR IN: ", unpack('i', $soerror), "\n";
+ $soerror = getsockopt(STDOUT, SOL_SOCKET, SO_ERROR);
+ print STDERR "ERROR OUT: ", unpack('i', $soerror), "\n";
+ IO::Handle::flush(\*STDERR);
+ POSIX::_exit(0);
+ };
+}
+
+########################################################################
+# Server funcs
+########################################################################
+
+sub read_char {
+ my $self = shift;
+ my $max = $self->{max};
+
+ my $ctx = Digest::MD5->new();
+ my $len = 0;
+ while (<STDIN>) {
+ $len += length($_);
+ $ctx->add($_);
+ print STDERR ".";
+ if ($max && $len >= $max) {
+ print STDERR "\nMax";
+ last;
+ }
+ }
+ print STDERR "\n";
+
+ print STDERR "LEN: ", $len, "\n";
+ print STDERR "MD5: ", $ctx->hexdigest, "\n";
+}
+
+1;
diff --git a/regress/usr.sbin/relayd/relayd.pl b/regress/usr.sbin/relayd/relayd.pl
new file mode 100644
index 00000000000..9ebbc5298a1
--- /dev/null
+++ b/regress/usr.sbin/relayd/relayd.pl
@@ -0,0 +1,99 @@
+#!/usr/bin/perl
+# $OpenBSD: relayd.pl,v 1.1 2011/09/01 17:33:17 bluhm Exp $
+
+# Copyright (c) 2010,2011 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 Socket;
+use Socket6;
+
+use Client;
+use Relayd;
+use Server;
+require 'funcs.pl';
+
+sub usage {
+ die "usage: relay.pl copy|splice [test-args.pl]\n";
+}
+
+my $test;
+our %args;
+if (@ARGV and -f $ARGV[-1]) {
+ $test = pop;
+ do $test
+ or die "Do test file $test failed: ", $@ || $!;
+}
+@ARGV == 1 or usage();
+
+my($sport, $rport) = find_ports(num => 2);
+my $s = Server->new(
+ func => \&read_char,
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ listenport => $sport,
+ %{$args{server}},
+);
+my $r = Relayd->new(
+ forward => $ARGV[0],
+ listendomain => AF_INET,
+ listenaddr => "127.0.0.1",
+ listenport => $rport,
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $sport,
+ %{$args{relay}},
+ test => $test,
+);
+my $c = Client->new(
+ func => \&write_char,
+ connectdomain => AF_INET,
+ connectaddr => "127.0.0.1",
+ connectport => $rport,
+ %{$args{client}},
+);
+
+$s->run;
+$r->run;
+$r->up;
+$c->run->up;
+$s->up;
+
+$c->down;
+$s->down;
+$r->down;
+
+exit if $args{nocheck};
+
+my $clen = $c->loggrep(qr/^LEN: /) // die "no client len"
+ unless $args{client}{nocheck};
+my $slen = $s->loggrep(qr/^LEN: /) // die "no server len"
+ unless $args{server}{nocheck};
+!$clen || !$slen || $clen eq $slen
+ or die "client: $clen", "server: $slen", "len mismatch";
+!defined($args{len}) || !$clen || $clen eq "LEN: $args{len}\n"
+ or die "client: $clen", "len $args{len} expected";
+!defined($args{len}) || !$slen || $slen eq "LEN: $args{len}\n"
+ or die "server: $slen", "len $args{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";