7
use Test::More qw/no_plan/;
9
# We use ports 9000, 9001 and 9002 -- hope that won't clash
11
my $ssh_address = "::1:9000";
12
my $ssl_address = "::1:9001";
14
my $no_listen = 9003; # Port on which no-one listens
15
my $pidfile = "/tmp/sslh_test.pid";
17
# Which tests do we run
21
my $SSH_PROBE_AGAIN = 1;
24
my $BIG_MSG = 0; # This test is unreliable
25
my $STALL_CNX = 0; # This test needs fixing
27
# Robustness tests. These are mostly to achieve full test
28
# coverage, but do not necessarily result in an actual test
29
# (e.g. some tests need to be run with valgrind to check all
30
# memory management code).
31
my $RB_CNX_NOSERVER = 1;
32
my $RB_PARAM_NOHOST = 1;
33
my $RB_WRONG_USERNAME = 1;
34
my $RB_OPEN_PID_FILE = 1;
35
my $RB_BIND_ADDRESS = 1;
36
my $RB_RESOLVE_ADDRESS = 1;
38
`lcov --directory . --zerocounters`;
41
my ($ssh_pid, $ssl_pid);
43
if (!($ssh_pid = fork)) {
44
exec "./echosrv --listen $ssh_address --prefix 'ssh: '";
47
if (!($ssl_pid = fork)) {
48
exec "./echosrv --listen $ssl_address --prefix 'ssl: '";
51
my @binaries = ('sslh-select', 'sslh-fork');
52
for my $binary (@binaries) {
53
warn "Testing $binary\n";
55
# Start sslh with the right plumbing
57
if (!($sslh_pid = fork)) {
58
my $user = (getpwuid $<)[0]; # Run under current username
59
my $cmd = "./$binary -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile";
64
warn "spawned $sslh_pid\n";
65
sleep 5; # valgrind can be heavy -- wait 5 seconds
68
my $test_data = "hello world\n";
69
# my $ssl_test_data = (pack 'n', ((length $test_data) + 2)) . $test_data;
70
my $ssl_test_data = "\x16\x03\x03$test_data\n";
72
# Test: SSL connection
74
print "***Test: SSL connection\n";
75
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
76
warn "$!\n" unless $cnx_l;
78
print $cnx_l $ssl_test_data;
80
my $n = sysread $cnx_l, $data, 1024;
81
is($data, "ssl: $ssl_test_data", "SSL connection");
85
# Test: Shy SSH connection
87
print "***Test: Shy SSH connection\n";
88
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
89
warn "$!\n" unless $cnx_h;
92
print $cnx_h $test_data;
94
is($data, "ssh: $test_data", "Shy SSH connection");
98
# Test: Bold SSH connection
100
print "***Test: Bold SSH connection\n";
101
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
102
warn "$!\n" unless $cnx_h;
103
if (defined $cnx_h) {
104
my $td = "SSH-2.0 testsuite\t$test_data";
107
is($data, "ssh: $td", "Bold SSH connection");
111
# Test: PROBE_AGAIN, incomplete first frame
112
if ($SSH_PROBE_AGAIN) {
113
print "***Test: incomplete SSH first frame\n";
114
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
115
warn "$!\n" unless $cnx_h;
116
if (defined $cnx_h) {
117
my $td = "SSH-2.0 testsuite\t$test_data";
118
print $cnx_h substr $td, 0, 2;
120
print $cnx_h substr $td, 2;
122
is($data, "ssh: $td", "Incomplete first SSH frame");
127
# Test: One SSL half-started then one SSH
129
print "***Test: One SSL half-started then one SSH\n";
130
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
131
warn "$!\n" unless $cnx_l;
132
if (defined $cnx_l) {
133
print $cnx_l $ssl_test_data;
134
my $cnx_h= new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
135
warn "$!\n" unless $cnx_h;
136
if (defined $cnx_h) {
138
print $cnx_h $test_data;
139
my $data_h = <$cnx_h>;
140
is($data_h, "ssh: $test_data", "SSH during SSL being established");
143
my $n = sysread $cnx_l, $data, 1024;
144
is($data, "ssl: $ssl_test_data", "SSL connection interrupted by SSH");
148
# Test: One SSH half-started then one SSL
150
print "***Test: One SSH half-started then one SSL\n";
151
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
152
warn "$!\n" unless $cnx_h;
153
if (defined $cnx_h) {
155
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
156
warn "$!\n" unless $cnx_l;
157
if (defined $cnx_l) {
158
print $cnx_l $ssl_test_data;
160
my $n = sysread $cnx_l, $data, 1024;
161
is($data, "ssl: $ssl_test_data", "SSL during SSH being established");
163
print $cnx_h $test_data;
165
is($data, "ssh: $test_data", "SSH connection interrupted by SSL");
170
# Test: Big messages (careful: don't go over echosrv's buffer limit (1M))
172
print "***Test: big message\n";
173
my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
174
warn "$!\n" unless $cnx_l;
176
my $test_data2 = $ssl_test_data . ("helloworld"x$rept);
177
if (defined $cnx_l) {
178
my $n = syswrite $cnx_l, $test_data2;
180
$n = sysread $cnx_l, $data, 1 << 20;
181
is($data, "ssl: ". $test_data2, "Big message");
185
# Test: Stalled connection
186
# Create two connections, stall one, check the other one
187
# works, unstall first and check it works fine
188
# This test needs fixing.
189
# Now that echosrv no longer works on "lines" (finishing
190
# with '\n'), it may cut blocks randomly with prefixes.
191
# The whole thing needs to be re-thought as it'll only
194
print "***Test: Stalled connection\n";
195
my $cnx_1 = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
196
warn "$!\n" unless defined $cnx_1;
197
my $cnx_2 = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
198
warn "$!\n" unless defined $cnx_2;
199
my $test_data2 = "helloworld";
202
if (defined $cnx_1 and defined $cnx_2) {
203
print $cnx_1 ($test_data2 x $rept);
205
print $cnx_2 ($test_data2 x $rept);
208
is($data, "ssh: " . ($test_data2 x $rept) . "\n", "Stalled connection (1)");
209
print $cnx_2 ($test_data2 x $rept);
212
is($data, "ssh: " . ($test_data2 x $rept) . "\n", "Stalled connection (2)");
214
is($data, "ssh: " . ($test_data2 x $rept) . "\n", "Stalled connection (3)");
219
my $pid = `cat $pidfile`;
220
warn "killing $pid\n";
221
kill TERM => $pid or warn "kill process: $!\n";
225
# Robustness: Connecting to non-existant server
226
if ($RB_CNX_NOSERVER) {
227
print "***Test: Connecting to non-existant server\n";
229
if (!($sslh_pid = fork)) {
230
my $user = (getpwuid $<)[0]; # Run under current username
231
exec "./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh localhost:$no_listen --ssl localhost:$no_listen -P $pidfile";
233
warn "spawned $sslh_pid\n";
237
my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port");
238
warn "$!\n" unless $cnx_h;
239
if (defined $cnx_h) {
241
my $test_data = "hello";
242
print $cnx_h $test_data;
244
# Ideally we should check a log is emitted.
246
kill TERM => `cat $pidfile` or warn "kill: $!\n";
251
# Robustness: No hostname in address
252
if ($RB_PARAM_NOHOST) {
253
print "***Test: No hostname in address\n";
255
if (!($sslh_pid = fork)) {
256
my $user = (getpwuid $<)[0]; # Run under current username
257
exec "./sslh-select -v -f -u $user --listen $sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile";
259
warn "spawned $sslh_pid\n";
260
waitpid $sslh_pid, 0;
262
warn "exited with $code\n";
263
is($code, 1, "Exit status on illegal option");
266
# Robustness: User does not exist
267
if ($RB_WRONG_USERNAME) {
268
print "***Test: Changing to non-existant username\n";
270
if (!($sslh_pid = fork)) {
271
my $user = (getpwuid $<)[0]; # Run under current username
272
exec "./sslh-select -v -f -u ${user}_doesnt_exist --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile";
274
warn "spawned $sslh_pid\n";
275
waitpid $sslh_pid, 0;
277
warn "exited with $code\n";
278
is($code, 2, "Exit status on non-existant username");
281
# Robustness: Can't open PID file
282
if ($RB_OPEN_PID_FILE) {
283
print "***Test: Can't open PID file\n";
285
if (!($sslh_pid = fork)) {
286
my $user = (getpwuid $<)[0]; # Run under current username
287
exec "./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P /dont_exist/$pidfile";
288
# You don't have a /dont_exist/ directory, do you?!
290
warn "spawned $sslh_pid\n";
291
waitpid $sslh_pid, 0;
293
warn "exited with $code\n";
294
is($code, 3, "Exit status if can't open PID file");
297
# Robustness: Can't bind address
298
if ($RB_BIND_ADDRESS) {
299
print "***Test: Can't bind address\n";
301
if (!($sslh_pid = fork)) {
302
my $user = (getpwuid $<)[0]; # Run under current username
303
exec "./sslh-select -v -f -u $user --listen 74.125.39.106:9000 --ssh $ssh_address --ssl $ssl_address -P $pidfile";
305
warn "spawned $sslh_pid\n";
306
waitpid $sslh_pid, 0;
308
warn "exited with $code\n";
309
is($code, 1, "Exit status if can't bind address");
312
# Robustness: Can't resolve address
313
if ($RB_RESOLVE_ADDRESS) {
314
print "***Test: Can't resolve address\n";
316
if (!($sslh_pid = fork)) {
317
my $user = (getpwuid $<)[0]; # Run under current username
318
exec "./sslh-select -v -f -u $user --listen blahblah.dontexist:9000 --ssh $ssh_address --ssl $ssl_address -P $pidfile";
320
warn "spawned $sslh_pid\n";
321
waitpid $sslh_pid, 0;
323
warn "exited with $code\n";
324
is($code, 4, "Exit status if can't resolve address");
327
`lcov --directory . --capture --output-file sslh_cov.info`;
328
`genhtml sslh_cov.info`;