summaryrefslogtreecommitdiff
path: root/regress
diff options
context:
space:
mode:
Diffstat (limited to 'regress')
-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";