mirror of
https://github.com/moparisthebest/sslh
synced 2024-12-21 23:08:58 -05:00
9bcb2cdd7a
Added support for configuration file. New protocol probes can be defined using regular expressions that match the first packet sent by the client. sslh now connects timed out connections to the first configured protocol instead of 'ssh' (just make sure ssh is the first defined protocol). sslh now tries protocols in the order in which they are defined (just make sure sslh is the last defined protocol).
305 lines
10 KiB
Perl
Executable File
305 lines
10 KiB
Perl
Executable File
#! /usr/bin/perl -w
|
|
|
|
# Test script for sslh
|
|
|
|
use strict;
|
|
use IO::Socket::INET6;
|
|
use Test::More qw/no_plan/;
|
|
|
|
# We use ports 9000, 9001 and 9002 -- hope that won't clash
|
|
# with anything...
|
|
my $ssh_address = "ip6-localhost:9000";
|
|
my $ssl_address = "ip6-localhost:9001";
|
|
my $sslh_port = 9002;
|
|
my $no_listen = 9003; # Port on which no-one listens
|
|
my $pidfile = "/tmp/sslh_test.pid";
|
|
|
|
# Which tests do we run
|
|
my $SSL_CNX = 1;
|
|
my $SSH_SHY_CNX = 1;
|
|
my $SSH_BOLD_CNX = 1;
|
|
my $SSL_MIX_SSH = 1;
|
|
my $SSH_MIX_SSL = 1;
|
|
my $BIG_MSG = 1;
|
|
my $STALL_CNX = 1;
|
|
|
|
# Robustness tests. These are mostly to achieve full test
|
|
# coverage, but do not necessarily result in an actual test
|
|
# (e.g. some tests need to be run with valgrind to check all
|
|
# memory management code).
|
|
my $RB_CNX_NOSERVER = 1;
|
|
my $RB_PARAM_NOHOST = 1;
|
|
my $RB_WRONG_USERNAME = 1;
|
|
my $RB_OPEN_PID_FILE = 1;
|
|
my $RB_BIND_ADDRESS = 1;
|
|
my $RB_RESOLVE_ADDRESS = 1;
|
|
|
|
`lcov --directory . --zerocounters`;
|
|
|
|
|
|
my ($ssh_pid, $ssl_pid);
|
|
|
|
if (!($ssh_pid = fork)) {
|
|
exec "./echosrv --listen $ssh_address --prefix 'ssh: '";
|
|
}
|
|
|
|
if (!($ssl_pid = fork)) {
|
|
exec "./echosrv --listen $ssl_address --prefix 'ssl: '";
|
|
}
|
|
|
|
my @binaries = ('sslh-select', 'sslh-fork');
|
|
for my $binary (@binaries) {
|
|
warn "Testing $binary\n";
|
|
|
|
# Start sslh with the right plumbing
|
|
my $sslh_pid;
|
|
if (!($sslh_pid = fork)) {
|
|
my $user = (getpwuid $<)[0]; # Run under current username
|
|
my $cmd = "./$binary -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile";
|
|
warn "$cmd\n";
|
|
exec $cmd;
|
|
#exec "valgrind --leak-check=full ./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address -ssl $ssl_address -P $pidfile";
|
|
exit 0;
|
|
}
|
|
warn "spawned $sslh_pid\n";
|
|
sleep 1; # valgrind can be heavy -- wait 5 seconds
|
|
|
|
|
|
my $test_data = "hello world\n";
|
|
|
|
# Test: SSL connection
|
|
if ($SSL_CNX) {
|
|
print "***Test: SSL connection\n";
|
|
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_l;
|
|
if (defined $cnx_l) {
|
|
print $cnx_l $test_data;
|
|
my $data = <$cnx_l>;
|
|
is($data, "ssl: $test_data", "SSL connection");
|
|
}
|
|
}
|
|
|
|
# Test: Shy SSH connection
|
|
if ($SSH_SHY_CNX) {
|
|
print "***Test: Shy SSH connection\n";
|
|
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_h;
|
|
if (defined $cnx_h) {
|
|
sleep 3;
|
|
print $cnx_h $test_data;
|
|
my $data = <$cnx_h>;
|
|
is($data, "ssh: $test_data", "Shy SSH connection");
|
|
}
|
|
}
|
|
|
|
# Test: Bold SSH connection
|
|
if ($SSH_BOLD_CNX) {
|
|
print "***Test: Bold SSH connection\n";
|
|
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_h;
|
|
if (defined $cnx_h) {
|
|
my $td = "SSH-2.0 testsuite\t$test_data";
|
|
print $cnx_h $td;
|
|
my $data = <$cnx_h>;
|
|
is($data, "ssh: $td", "Bold SSH connection");
|
|
}
|
|
}
|
|
|
|
# Test: One SSL half-started then one SSH
|
|
if ($SSL_MIX_SSH) {
|
|
print "***Test: One SSL half-started then one SSH\n";
|
|
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_l;
|
|
if (defined $cnx_l) {
|
|
print $cnx_l $test_data;
|
|
my $cnx_h= new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_h;
|
|
if (defined $cnx_h) {
|
|
sleep 3;
|
|
print $cnx_h $test_data;
|
|
my $data_h = <$cnx_h>;
|
|
is($data_h, "ssh: $test_data", "SSH during SSL being established");
|
|
}
|
|
my $data = <$cnx_l>;
|
|
is($data, "ssl: $test_data", "SSL connection interrupted by SSH");
|
|
}
|
|
}
|
|
|
|
# Test: One SSH half-started then one SSL
|
|
if ($SSH_MIX_SSL) {
|
|
print "***Test: One SSH half-started then one SSL\n";
|
|
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_h;
|
|
if (defined $cnx_h) {
|
|
sleep 3;
|
|
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_l;
|
|
if (defined $cnx_l) {
|
|
print $cnx_l $test_data;
|
|
my $data = <$cnx_l>;
|
|
is($data, "ssl: $test_data", "SSL during SSH being established");
|
|
}
|
|
print $cnx_h $test_data;
|
|
my $data = <$cnx_h>;
|
|
is($data, "ssh: $test_data", "SSH connection interrupted by SSL");
|
|
}
|
|
}
|
|
|
|
|
|
# Test: Big messages (careful: don't go over echosrv's buffer limit (1M))
|
|
if ($BIG_MSG) {
|
|
print "***Test: big message\n";
|
|
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_l;
|
|
my $test_data2 = "helloworld";
|
|
my $rept = 10000;
|
|
if (defined $cnx_l) {
|
|
print $cnx_l ($test_data2 x $rept);
|
|
print $cnx_l "\n";
|
|
my $data = <$cnx_l>;
|
|
is($data, "ssl: ". ($test_data2 x $rept) . "\n", "Big message");
|
|
}
|
|
}
|
|
|
|
# Test: Stalled connection
|
|
# Create two connections, stall one, check the other one
|
|
# works, unstall first and check it works fine
|
|
if ($STALL_CNX) {
|
|
print "***Test: Stalled connection\n";
|
|
my $cnx_1 = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless defined $cnx_1;
|
|
my $cnx_2 = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless defined $cnx_2;
|
|
my $test_data2 = "helloworld";
|
|
my $rept = 10000;
|
|
if (defined $cnx_1 and defined $cnx_2) {
|
|
print $cnx_1 ($test_data2 x $rept);
|
|
print $cnx_1 "\n";
|
|
print $cnx_2 ($test_data2 x $rept);
|
|
print $cnx_2 "\n";
|
|
my $data = <$cnx_2>;
|
|
is($data, "ssl: " . ($test_data2 x $rept) . "\n", "Stalled connection (1)");
|
|
print $cnx_2 ($test_data2 x $rept);
|
|
print $cnx_2 "\n";
|
|
$data = <$cnx_2>;
|
|
is($data, "ssl: " . ($test_data2 x $rept) . "\n", "Stalled connection (2)");
|
|
$data = <$cnx_1>;
|
|
is($data, "ssl: " . ($test_data2 x $rept) . "\n", "Stalled connection (3)");
|
|
|
|
}
|
|
}
|
|
|
|
my $pid = `cat $pidfile`;
|
|
warn "killing $pid\n";
|
|
kill TERM => $pid or warn "kill process: $!\n";
|
|
sleep 1;
|
|
}
|
|
|
|
# Robustness: Connecting to non-existant server
|
|
if ($RB_CNX_NOSERVER) {
|
|
print "***Test: Connecting to non-existant server\n";
|
|
my $sslh_pid;
|
|
if (!($sslh_pid = fork)) {
|
|
my $user = (getpwuid $<)[0]; # Run under current username
|
|
exec "./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh localhost:$no_listen --ssl localhost:$no_listen -P $pidfile";
|
|
}
|
|
warn "spawned $sslh_pid\n";
|
|
|
|
sleep 1;
|
|
|
|
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
|
|
warn "$!\n" unless $cnx_h;
|
|
if (defined $cnx_h) {
|
|
sleep 1;
|
|
my $test_data = "hello";
|
|
print $cnx_h $test_data;
|
|
}
|
|
# Ideally we should check a log is emitted.
|
|
|
|
kill TERM => `cat $pidfile` or warn "kill: $!\n";
|
|
sleep 1;
|
|
}
|
|
|
|
|
|
# Robustness: No hostname in address
|
|
if ($RB_PARAM_NOHOST) {
|
|
print "***Test: No hostname in address\n";
|
|
my $sslh_pid;
|
|
if (!($sslh_pid = fork)) {
|
|
my $user = (getpwuid $<)[0]; # Run under current username
|
|
exec "./sslh-select -v -f -u $user --listen $sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile";
|
|
}
|
|
warn "spawned $sslh_pid\n";
|
|
waitpid $sslh_pid, 0;
|
|
my $code = $? >> 8;
|
|
warn "exited with $code\n";
|
|
is($code, 1, "Exit status on illegal option");
|
|
}
|
|
|
|
# Robustness: User does not exist
|
|
if ($RB_WRONG_USERNAME) {
|
|
print "***Test: Changing to non-existant username\n";
|
|
my $sslh_pid;
|
|
if (!($sslh_pid = fork)) {
|
|
my $user = (getpwuid $<)[0]; # Run under current username
|
|
exec "./sslh-select -v -f -u ${user}_doesnt_exist --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile";
|
|
}
|
|
warn "spawned $sslh_pid\n";
|
|
waitpid $sslh_pid, 0;
|
|
my $code = $? >> 8;
|
|
warn "exited with $code\n";
|
|
is($code, 2, "Exit status on non-existant username");
|
|
}
|
|
|
|
# Robustness: Can't open PID file
|
|
if ($RB_OPEN_PID_FILE) {
|
|
print "***Test: Can't open PID file\n";
|
|
my $sslh_pid;
|
|
if (!($sslh_pid = fork)) {
|
|
my $user = (getpwuid $<)[0]; # Run under current username
|
|
exec "./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P /dont_exist/$pidfile";
|
|
# You don't have a /dont_exist/ directory, do you?!
|
|
}
|
|
warn "spawned $sslh_pid\n";
|
|
waitpid $sslh_pid, 0;
|
|
my $code = $? >> 8;
|
|
warn "exited with $code\n";
|
|
is($code, 3, "Exit status if can't open PID file");
|
|
}
|
|
|
|
# Robustness: Can't bind address
|
|
if ($RB_BIND_ADDRESS) {
|
|
print "***Test: Can't bind address\n";
|
|
my $sslh_pid;
|
|
if (!($sslh_pid = fork)) {
|
|
my $user = (getpwuid $<)[0]; # Run under current username
|
|
exec "./sslh-select -v -f -u $user --listen 74.125.39.106:9000 --ssh $ssh_address --ssl $ssl_address -P $pidfile";
|
|
}
|
|
warn "spawned $sslh_pid\n";
|
|
waitpid $sslh_pid, 0;
|
|
my $code = $? >> 8;
|
|
warn "exited with $code\n";
|
|
is($code, 1, "Exit status if can't bind address");
|
|
}
|
|
|
|
# Robustness: Can't resolve address
|
|
if ($RB_RESOLVE_ADDRESS) {
|
|
print "***Test: Can't resolve address\n";
|
|
my $sslh_pid;
|
|
if (!($sslh_pid = fork)) {
|
|
my $user = (getpwuid $<)[0]; # Run under current username
|
|
exec "./sslh-select -v -f -u $user --listen blahblah.dontexist:9000 --ssh $ssh_address --ssl $ssl_address -P $pidfile";
|
|
}
|
|
warn "spawned $sslh_pid\n";
|
|
waitpid $sslh_pid, 0;
|
|
my $code = $? >> 8;
|
|
warn "exited with $code\n";
|
|
is($code, 4, "Exit status if can't resolve address");
|
|
}
|
|
|
|
`lcov --directory . --capture --output-file sslh_cov.info`;
|
|
`genhtml sslh_cov.info`;
|
|
|
|
`killall echosrv`;
|
|
|