Files: 2705426f630f6e62f738ff35da68c91824fe6bbd / t
10849 bytesRaw
1 | #! /usr/bin/perl -w |
2 | |
3 | # Test script for sslh |
4 | |
5 | use strict; |
6 | use IO::Socket::INET6; |
7 | use Test::More qw/no_plan/; |
8 | |
9 | # We use ports 9000, 9001 and 9002 -- hope that won't clash |
10 | # with anything... |
11 | my $ssh_address = "ip6-localhost:9000"; |
12 | my $ssl_address = "ip6-localhost:9001"; |
13 | my $sslh_port = 9002; |
14 | my $no_listen = 9003; # Port on which no-one listens |
15 | my $pidfile = "/tmp/sslh_test.pid"; |
16 | |
17 | # Which tests do we run |
18 | my $SSL_CNX = 1; |
19 | my $SSH_SHY_CNX = 1; |
20 | my $SSH_BOLD_CNX = 1; |
21 | my $SSH_PROBE_AGAIN = 1; |
22 | my $SSL_MIX_SSH = 1; |
23 | my $SSH_MIX_SSL = 1; |
24 | my $BIG_MSG = 0; # This test is unreliable |
25 | my $STALL_CNX = 0; # This test needs fixing |
26 | |
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; |
37 | |
38 | `lcov --directory . --zerocounters`; |
39 | |
40 | |
41 | my ($ssh_pid, $ssl_pid); |
42 | |
43 | if (!($ssh_pid = fork)) { |
44 | exec "./echosrv --listen $ssh_address --prefix 'ssh: '"; |
45 | } |
46 | |
47 | if (!($ssl_pid = fork)) { |
48 | exec "./echosrv --listen $ssl_address --prefix 'ssl: '"; |
49 | } |
50 | |
51 | my @binaries = ('sslh-select', 'sslh-fork'); |
52 | for my $binary (@binaries) { |
53 | warn "Testing $binary\n"; |
54 | |
55 | # Start sslh with the right plumbing |
56 | my $sslh_pid; |
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"; |
60 | warn "$cmd\n"; |
61 | #exec $cmd; |
62 | exec "valgrind --leak-check=full ./$binary -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address -ssl $ssl_address -P $pidfile"; |
63 | exit 0; |
64 | } |
65 | warn "spawned $sslh_pid\n"; |
66 | sleep 5; # valgrind can be heavy -- wait 5 seconds |
67 | |
68 | |
69 | my $test_data = "hello world\n"; |
70 | # my $ssl_test_data = (pack 'n', ((length $test_data) + 2)) . $test_data; |
71 | my $ssl_test_data = "\x16\x03\x03$test_data\n"; |
72 | |
73 | # Test: SSL connection |
74 | if ($SSL_CNX) { |
75 | print "***Test: SSL connection\n"; |
76 | my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
77 | warn "$!\n" unless $cnx_l; |
78 | if (defined $cnx_l) { |
79 | print $cnx_l $ssl_test_data; |
80 | my $data; |
81 | my $n = sysread $cnx_l, $data, 1024; |
82 | is($data, "ssl: $ssl_test_data", "SSL connection"); |
83 | } |
84 | } |
85 | |
86 | # Test: Shy SSH connection |
87 | if ($SSH_SHY_CNX) { |
88 | print "***Test: Shy SSH connection\n"; |
89 | my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
90 | warn "$!\n" unless $cnx_h; |
91 | if (defined $cnx_h) { |
92 | sleep 3; |
93 | print $cnx_h $test_data; |
94 | my $data = <$cnx_h>; |
95 | is($data, "ssh: $test_data", "Shy SSH connection"); |
96 | } |
97 | } |
98 | |
99 | # Test: Bold SSH connection |
100 | if ($SSH_BOLD_CNX) { |
101 | print "***Test: Bold SSH connection\n"; |
102 | my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
103 | warn "$!\n" unless $cnx_h; |
104 | if (defined $cnx_h) { |
105 | my $td = "SSH-2.0 testsuite\t$test_data"; |
106 | print $cnx_h $td; |
107 | my $data = <$cnx_h>; |
108 | is($data, "ssh: $td", "Bold SSH connection"); |
109 | } |
110 | } |
111 | |
112 | # Test: PROBE_AGAIN, incomplete first frame |
113 | if ($SSH_PROBE_AGAIN) { |
114 | print "***Test: incomplete SSH first frame\n"; |
115 | my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
116 | warn "$!\n" unless $cnx_h; |
117 | if (defined $cnx_h) { |
118 | my $td = "SSH-2.0 testsuite\t$test_data"; |
119 | print $cnx_h substr $td, 0, 2; |
120 | sleep 1; |
121 | print $cnx_h substr $td, 2; |
122 | my $data = <$cnx_h>; |
123 | is($data, "ssh: $td", "Incomplete first SSH frame"); |
124 | } |
125 | } |
126 | |
127 | |
128 | # Test: One SSL half-started then one SSH |
129 | if ($SSL_MIX_SSH) { |
130 | print "***Test: One SSL half-started then one SSH\n"; |
131 | my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
132 | warn "$!\n" unless $cnx_l; |
133 | if (defined $cnx_l) { |
134 | print $cnx_l $ssl_test_data; |
135 | my $cnx_h= new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
136 | warn "$!\n" unless $cnx_h; |
137 | if (defined $cnx_h) { |
138 | sleep 3; |
139 | print $cnx_h $test_data; |
140 | my $data_h = <$cnx_h>; |
141 | is($data_h, "ssh: $test_data", "SSH during SSL being established"); |
142 | } |
143 | my $data; |
144 | my $n = sysread $cnx_l, $data, 1024; |
145 | is($data, "ssl: $ssl_test_data", "SSL connection interrupted by SSH"); |
146 | } |
147 | } |
148 | |
149 | # Test: One SSH half-started then one SSL |
150 | if ($SSH_MIX_SSL) { |
151 | print "***Test: One SSH half-started then one SSL\n"; |
152 | my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
153 | warn "$!\n" unless $cnx_h; |
154 | if (defined $cnx_h) { |
155 | sleep 3; |
156 | my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
157 | warn "$!\n" unless $cnx_l; |
158 | if (defined $cnx_l) { |
159 | print $cnx_l $ssl_test_data; |
160 | my $data; |
161 | my $n = sysread $cnx_l, $data, 1024; |
162 | is($data, "ssl: $ssl_test_data", "SSL during SSH being established"); |
163 | } |
164 | print $cnx_h $test_data; |
165 | my $data = <$cnx_h>; |
166 | is($data, "ssh: $test_data", "SSH connection interrupted by SSL"); |
167 | } |
168 | } |
169 | |
170 | |
171 | # Test: Big messages (careful: don't go over echosrv's buffer limit (1M)) |
172 | if ($BIG_MSG) { |
173 | print "***Test: big message\n"; |
174 | my $cnx_l = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
175 | warn "$!\n" unless $cnx_l; |
176 | my $rept = 1000; |
177 | my $test_data2 = $ssl_test_data . ("helloworld"x$rept); |
178 | if (defined $cnx_l) { |
179 | my $n = syswrite $cnx_l, $test_data2; |
180 | my ($data); |
181 | $n = sysread $cnx_l, $data, 1 << 20; |
182 | is($data, "ssl: ". $test_data2, "Big message"); |
183 | } |
184 | } |
185 | |
186 | # Test: Stalled connection |
187 | # Create two connections, stall one, check the other one |
188 | # works, unstall first and check it works fine |
189 | # This test needs fixing. |
190 | # Now that echosrv no longer works on "lines" (finishing |
191 | # with '\n'), it may cut blocks randomly with prefixes. |
192 | # The whole thing needs to be re-thought as it'll only |
193 | # work by chance. |
194 | if ($STALL_CNX) { |
195 | print "***Test: Stalled connection\n"; |
196 | my $cnx_1 = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
197 | warn "$!\n" unless defined $cnx_1; |
198 | my $cnx_2 = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
199 | warn "$!\n" unless defined $cnx_2; |
200 | my $test_data2 = "helloworld"; |
201 | sleep 4; |
202 | my $rept = 1000; |
203 | if (defined $cnx_1 and defined $cnx_2) { |
204 | print $cnx_1 ($test_data2 x $rept); |
205 | print $cnx_1 "\n"; |
206 | print $cnx_2 ($test_data2 x $rept); |
207 | print $cnx_2 "\n"; |
208 | my $data = <$cnx_2>; |
209 | is($data, "ssh: " . ($test_data2 x $rept) . "\n", "Stalled connection (1)"); |
210 | print $cnx_2 ($test_data2 x $rept); |
211 | print $cnx_2 "\n"; |
212 | $data = <$cnx_2>; |
213 | is($data, "ssh: " . ($test_data2 x $rept) . "\n", "Stalled connection (2)"); |
214 | $data = <$cnx_1>; |
215 | is($data, "ssh: " . ($test_data2 x $rept) . "\n", "Stalled connection (3)"); |
216 | |
217 | } |
218 | } |
219 | |
220 | my $pid = `cat $pidfile`; |
221 | warn "killing $pid\n"; |
222 | kill TERM => $pid or warn "kill process: $!\n"; |
223 | sleep 1; |
224 | } |
225 | |
226 | # Robustness: Connecting to non-existant server |
227 | if ($RB_CNX_NOSERVER) { |
228 | print "***Test: Connecting to non-existant server\n"; |
229 | my $sslh_pid; |
230 | if (!($sslh_pid = fork)) { |
231 | my $user = (getpwuid $<)[0]; # Run under current username |
232 | exec "./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh localhost:$no_listen --ssl localhost:$no_listen -P $pidfile"; |
233 | } |
234 | warn "spawned $sslh_pid\n"; |
235 | |
236 | sleep 1; |
237 | |
238 | my $cnx_h = new IO::Socket::INET(PeerHost => "localhost:$sslh_port"); |
239 | warn "$!\n" unless $cnx_h; |
240 | if (defined $cnx_h) { |
241 | sleep 1; |
242 | my $test_data = "hello"; |
243 | print $cnx_h $test_data; |
244 | } |
245 | # Ideally we should check a log is emitted. |
246 | |
247 | kill TERM => `cat $pidfile` or warn "kill: $!\n"; |
248 | sleep 1; |
249 | } |
250 | |
251 | |
252 | # Robustness: No hostname in address |
253 | if ($RB_PARAM_NOHOST) { |
254 | print "***Test: No hostname in address\n"; |
255 | my $sslh_pid; |
256 | if (!($sslh_pid = fork)) { |
257 | my $user = (getpwuid $<)[0]; # Run under current username |
258 | exec "./sslh-select -v -f -u $user --listen $sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile"; |
259 | } |
260 | warn "spawned $sslh_pid\n"; |
261 | waitpid $sslh_pid, 0; |
262 | my $code = $? >> 8; |
263 | warn "exited with $code\n"; |
264 | is($code, 1, "Exit status on illegal option"); |
265 | } |
266 | |
267 | # Robustness: User does not exist |
268 | if ($RB_WRONG_USERNAME) { |
269 | print "***Test: Changing to non-existant username\n"; |
270 | my $sslh_pid; |
271 | if (!($sslh_pid = fork)) { |
272 | my $user = (getpwuid $<)[0]; # Run under current username |
273 | exec "./sslh-select -v -f -u ${user}_doesnt_exist --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P $pidfile"; |
274 | } |
275 | warn "spawned $sslh_pid\n"; |
276 | waitpid $sslh_pid, 0; |
277 | my $code = $? >> 8; |
278 | warn "exited with $code\n"; |
279 | is($code, 2, "Exit status on non-existant username"); |
280 | } |
281 | |
282 | # Robustness: Can't open PID file |
283 | if ($RB_OPEN_PID_FILE) { |
284 | print "***Test: Can't open PID file\n"; |
285 | my $sslh_pid; |
286 | if (!($sslh_pid = fork)) { |
287 | my $user = (getpwuid $<)[0]; # Run under current username |
288 | exec "./sslh-select -v -f -u $user --listen localhost:$sslh_port --ssh $ssh_address --ssl $ssl_address -P /dont_exist/$pidfile"; |
289 | # You don't have a /dont_exist/ directory, do you?! |
290 | } |
291 | warn "spawned $sslh_pid\n"; |
292 | waitpid $sslh_pid, 0; |
293 | my $code = $? >> 8; |
294 | warn "exited with $code\n"; |
295 | is($code, 3, "Exit status if can't open PID file"); |
296 | } |
297 | |
298 | # Robustness: Can't resolve address |
299 | if ($RB_RESOLVE_ADDRESS) { |
300 | print "***Test: Can't resolve address\n"; |
301 | my $sslh_pid; |
302 | if (!($sslh_pid = fork)) { |
303 | my $user = (getpwuid $<)[0]; # Run under current username |
304 | exec "./sslh-select -v -f -u $user --listen blahblah.dontexist:9000 --ssh $ssh_address --ssl $ssl_address -P $pidfile"; |
305 | } |
306 | warn "spawned $sslh_pid\n"; |
307 | waitpid $sslh_pid, 0; |
308 | my $code = $? >> 8; |
309 | warn "exited with $code\n"; |
310 | is($code, 4, "Exit status if can't resolve address"); |
311 | } |
312 | |
313 | `lcov --directory . --capture --output-file sslh_cov.info`; |
314 | `genhtml sslh_cov.info`; |
315 | |
316 | `killall echosrv`; |
317 | |
318 |
Built with git-ssb-web