1# Various SSL read and write related tests: SSL_read, SSL_peek, SSL_read_ex,
2# SSL_peek_ex, SSL_write_ex, SSL_pending and SSL_has_pending
3
4use lib 'inc';
5
6use Net::SSLeay;
7use Test::Net::SSLeay qw(
8    can_fork data_file_path initialise_libssl tcp_socket
9);
10
11use Storable;
12
13if (not can_fork()) {
14    plan skip_all => "fork() not supported on this system";
15} else {
16    plan tests => 53;
17}
18
19initialise_libssl();
20
21my $pid;
22alarm(30);
23END { kill 9,$pid if $pid }
24
25my $server = tcp_socket();
26
27# See that lengths differ for all msgs
28my $msg1 = "1 first message from server";
29my $msg2 = "2 second message from server";
30my $msg3 = "3 third message from server: pad";
31
32my @rounds = qw(openssl openssl-1.1.0 openssl-1.1.1);
33
34sub server
35{
36    # SSL server - just handle connections, send to client and exit
37    my $cert_pem = data_file_path('simple-cert.cert.pem');
38    my $key_pem  = data_file_path('simple-cert.key.pem');
39
40    defined($pid = fork()) or BAIL_OUT("failed to fork: $!");
41    if ($pid == 0) {
42	foreach my $round (@rounds)
43	{
44	    my ($ctx, $ssl, $cl);
45
46	    next if skip_round($round);
47
48	    $cl = $server->accept();
49
50	    $ctx = Net::SSLeay::CTX_new();
51	    Net::SSLeay::set_cert_and_key($ctx, $cert_pem, $key_pem);
52
53	    $ssl = Net::SSLeay::new($ctx);
54	    Net::SSLeay::set_fd($ssl, fileno($cl));
55	    Net::SSLeay::accept($ssl);
56
57	    Net::SSLeay::write($ssl, $msg1);
58	    Net::SSLeay::write($ssl, $msg2);
59
60	    my $msg = Net::SSLeay::read($ssl);
61	    Net::SSLeay::write($ssl, $msg);
62	}
63    $server->close();
64	exit(0);
65    }
66}
67
68sub client
69{
70    foreach my $round (@rounds)
71    {
72	my ($ctx, $ssl, $cl);
73
74	$cl = $server->connect();
75
76	$ctx = Net::SSLeay::CTX_new();
77	$ssl = Net::SSLeay::new($ctx);
78
79	my ($reason, $num_tests) = skip_round($round);
80	if ($reason) {
81	  SKIP: {
82	      skip($reason, $num_tests);
83	    }
84	    next;
85	}
86
87	round_openssl($ctx, $ssl, $cl) if $round eq 'openssl';
88	round_openssl_1_1_0($ctx, $ssl, $cl) if $round eq 'openssl-1.1.0';
89	round_openssl_1_1_1($ctx, $ssl, $cl) if $round eq 'openssl-1.1.1';
90
91	Net::SSLeay::shutdown($ssl);
92	Net::SSLeay::free($ssl);
93    }
94    return;
95}
96
97# Returns list for skip() if we should skip this round, false if we
98# shouldn't
99sub skip_round
100{
101    my ($round) = @_;
102
103    return if $round eq 'openssl';
104
105    if ($round eq 'openssl-1.1.0') {
106	if (Net::SSLeay::constant("OPENSSL_VERSION_NUMBER") < 0x1010000f ||
107	    Net::SSLeay::constant("LIBRESSL_VERSION_NUMBER"))
108	{
109	    return ("Need OpenSSL 1.1.0 or later", 6);
110	} else {
111	    return;
112	}
113    }
114
115    if ($round eq 'openssl-1.1.1') {
116	if (Net::SSLeay::constant("OPENSSL_VERSION_NUMBER") < 0x1010100f ||
117	    Net::SSLeay::constant("LIBRESSL_VERSION_NUMBER"))
118	{
119	    return ("Need OpenSSL 1.1.1 or later", 26);
120	} else {
121	    return;
122	}
123    }
124
125    diag("Unknown round: $round");
126    return;
127}
128
129sub round_openssl
130{
131    my ($ctx, $ssl, $cl) = @_;
132
133    my ($peek_msg, $read_msg, $len, $err, $ret);
134
135    # ssl is not connected yet
136    $peek_msg = Net::SSLeay::peek($ssl);
137    is($peek_msg, undef, "scalar: peek returns undef for closed ssl");
138
139    ($peek_msg, $len) = Net::SSLeay::peek($ssl);
140    is($peek_msg, undef, "list: peek returns undef for closed ssl");
141    cmp_ok($len, '<=', 0, 'list: peek returns length <=0 for closed ssl');
142    $err = Net::SSLeay::get_error($ssl, $len);
143    isnt($err, Net::SSLeay::ERROR_WANT_READ(), "peek err $err is not retryable WANT_READ");
144    isnt($err, Net::SSLeay::ERROR_WANT_WRITE(), "peek err $err is not retryable WANT_WRITE");
145
146    $read_msg = Net::SSLeay::read($ssl);
147    is($read_msg, undef, "scalar: read returns undef for closed ssl");
148
149    ($read_msg, $len) = Net::SSLeay::read($ssl);
150    is($read_msg, undef, "list: read returns undef for closed ssl");
151    cmp_ok($len, '<=', 0, 'list: read returns length <=0 for closed ssl');
152    $err = Net::SSLeay::get_error($ssl, $len);
153    isnt($err, Net::SSLeay::ERROR_WANT_READ(), "read err $err is not retryable WANT_READ");
154    isnt($err, Net::SSLeay::ERROR_WANT_WRITE(), "read err $err is not retryable WANT_WRITE");
155
156    $ret = Net::SSLeay::pending($ssl);
157    is($ret, 0, "pending returns 0 for closed ssl");
158
159    Net::SSLeay::set_fd($ssl, $cl);
160    Net::SSLeay::connect($ssl);
161
162    # msg1
163    $ret = Net::SSLeay::pending($ssl);
164    is($ret, 0, "pending returns 0");
165
166    $peek_msg = Net::SSLeay::peek($ssl);
167    is($peek_msg, $msg1, "scalar: peek returns msg1");
168
169    # processing was triggered by peek
170    $ret = Net::SSLeay::pending($ssl);
171    is($ret, length($msg1), "pending returns msg1 length");
172
173    ($peek_msg, $len) = Net::SSLeay::peek($ssl);
174    is($peek_msg, $msg1, "list: peek returns msg1");
175    is($len, length($msg1), "list: peek returns msg1 length");
176
177    $read_msg = Net::SSLeay::read($ssl);
178    is($peek_msg, $read_msg, "scalar: read and peek agree about msg1");
179
180    # msg2
181    $peek_msg = Net::SSLeay::peek($ssl);
182    is($peek_msg, $msg2, "scalar: peek returns msg2");
183
184    ($read_msg, $len) = Net::SSLeay::read($ssl);
185    is($peek_msg, $read_msg, "list: read and peek agree about msg2");
186    is($len, length($msg2), "list: read returns msg2 length");
187
188    # msg3
189    Net::SSLeay::write($ssl, $msg3);
190    is(Net::SSLeay::read($ssl), $msg3, "ping with msg3");
191
192    return;
193}
194
195# Test has_pending and other functionality added in 1.1.0.
196# Revisit: Better tests for has_pending
197sub round_openssl_1_1_0
198{
199    my ($ctx, $ssl, $cl) = @_;
200
201    my ($peek_msg, $read_msg, $len, $err, $ret);
202
203    # ssl is not connected yet
204    $ret = Net::SSLeay::has_pending($ssl);
205    is($ret, 0, "1.1.0: has_pending returns 0 for closed ssl");
206
207    Net::SSLeay::set_fd($ssl, $cl);
208    Net::SSLeay::connect($ssl);
209
210    # msg1
211    $ret = Net::SSLeay::has_pending($ssl);
212    is($ret, 0, "1.1.0: has_pending returns 0");
213
214    # This triggers processing after which we have pending data
215    $peek_msg = Net::SSLeay::peek($ssl);
216    is($peek_msg, $msg1, "1.1.0: peek returns msg1");
217
218    $ret = Net::SSLeay::has_pending($ssl);
219    is($ret, 1, "1.1.0: has_pending returns 1");
220
221    Net::SSLeay::read($ssl); # Read and discard
222
223    $ret = Net::SSLeay::has_pending($ssl);
224    is($ret, 0, "1.1.0: has_pending returns 0 after read");
225
226    # msg2
227    Net::SSLeay::read($ssl); # Read and discard
228
229    # msg3
230    Net::SSLeay::write($ssl, $msg3);
231    is(Net::SSLeay::read($ssl), $msg3, "1.1.0: ping with msg3");
232
233    return;
234}
235
236sub round_openssl_1_1_1
237{
238    my ($ctx, $ssl, $cl) = @_;
239
240    my ($peek_msg, $read_msg, $len, $err, $err_ex, $ret);
241
242    # ssl is not connected yet
243    ($peek_msg, $ret) = Net::SSLeay::peek_ex($ssl);
244    is($peek_msg, undef, "1.1.1: list: peek_ex returns undef message for closed ssl");
245    is($ret, 0, '1.1.1: list: peek_ex returns 0 for closed ssl');
246    $err = Net::SSLeay::get_error($ssl, $ret);
247    isnt($err, Net::SSLeay::ERROR_WANT_READ(), "1.1.1: peek_ex err $err is not retryable WANT_READ");
248    isnt($err, Net::SSLeay::ERROR_WANT_WRITE(), "1.1.1: peek_ex err $err is not retryable WANT_WRITE");
249
250    ($read_msg, $len) = Net::SSLeay::read($ssl);
251    is($read_msg, undef, "1.1.1: list: read returns undef message for closed ssl");
252    cmp_ok($len, '<=', 0, '1.1.1: list: read returns length <=0 for closed ssl');
253    $err = Net::SSLeay::get_error($ssl, $len);
254    isnt($err, Net::SSLeay::ERROR_WANT_READ(), "1.1.1: read err $err is not retryable WANT_READ");
255    isnt($err, Net::SSLeay::ERROR_WANT_WRITE(), "1.1.1: read err $err is not retryable WANT_WRITE");
256
257    ($read_msg, $ret) = Net::SSLeay::read_ex($ssl);
258    is($read_msg, undef, "1.1.1: list: read_ex returns undef message for closed sssl");
259    is($ret, 0, "1.1.1: list: read_ex returns 0 for closed sssl");
260    $err_ex = Net::SSLeay::get_error($ssl, $ret);
261    is ($err_ex, $err, '1.1.1: read_ex and read err are equal');
262
263    Net::SSLeay::set_fd($ssl, $cl);
264    Net::SSLeay::connect($ssl);
265
266    # msg1
267    $ret = Net::SSLeay::has_pending($ssl);
268    is($ret, 0, "1.1.1: has_pending returns 0");
269
270    # This triggers processing after which we have pending data
271    ($peek_msg, $ret) = Net::SSLeay::peek_ex($ssl);
272    is($peek_msg, $msg1, "1.1.1: list: peek_ex returns msg1");
273    is($ret, 1, "1.1.1: list: peek_ex returns 1");
274
275    $len = Net::SSLeay::pending($ssl);
276    is($len, length($msg1), "1.1.1: pending returns msg1 length");
277
278    $ret = Net::SSLeay::has_pending($ssl);
279    is($ret, 1, "1.1.1: has_pending returns 1");
280
281    ($read_msg, $ret) = Net::SSLeay::read_ex($ssl);
282    is($read_msg, $msg1, "1.1.1: list: read_ex returns msg1");
283    is($ret, 1, "1.1.1: list: read_ex returns 1");
284
285    $len = Net::SSLeay::pending($ssl);
286    is($len, 0, "1.1.1: pending returns 0 after read_ex");
287
288    $ret = Net::SSLeay::has_pending($ssl);
289    is($ret, 0, "1.1.1: has_pending returns 0 after read_ex");
290
291    # msg2
292    Net::SSLeay::read($ssl); # Read and discard
293
294    # msg3
295    ($len, $ret) = Net::SSLeay::write_ex($ssl, $msg3);
296    is($len, length($msg3), "1.1.1: write_ex wrote all");
297    is($ret, 1, "1.1.1: write_ex returns 1");
298
299    my ($read_msg1, $ret1) = Net::SSLeay::read_ex($ssl, 5);
300    my ($read_msg2, $ret2) = Net::SSLeay::read_ex($ssl, (length($msg3) - 5));
301
302    is($ret1, 1, '1.1.1: ping with msg3 part1 ok');
303    is($ret2, 1, '1.1.1: ping with msg3 part2 ok');
304    is(length($read_msg1), 5, '1.1.1: ping with msg3, part1 length was 5');
305    is($read_msg1 . $read_msg2, $msg3, "1.1.1: ping with msg3 in two parts");
306
307    return;
308}
309
310server();
311client();
312waitpid $pid, 0;
313exit(0);
314