1 /*
2 * Copyright 2022 The OpenSSL Project Authors. All Rights Reserved.
3 *
4 * Licensed under the Apache License 2.0 (the "License"). You may not use
5 * this file except in compliance with the License. You can obtain a copy
6 * in the file LICENSE in the source distribution or at
7 * https://www.openssl.org/source/license.html
8 */
9
10 #include <openssl/bio.h>
11 #include "internal/e_os.h"
12 #include "internal/sockets.h"
13 #include "internal/bio_tfo.h"
14 #include "testutil.h"
15
16 /* If OS support is added in crypto/bio/bio_tfo.h, add it here */
17 #if defined(OPENSSL_SYS_LINUX)
18 # define GOOD_OS 1
19 #elif defined(__FreeBSD__)
20 # define GOOD_OS 1
21 #elif defined(OPENSSL_SYS_MACOSX)
22 # define GOOD_OS 1
23 #else
24 # ifdef GOOD_OS
25 # undef GOOD_OS
26 # endif
27 #endif
28
29 #if !defined(OPENSSL_NO_TFO) && defined(GOOD_OS)
30
31 /*
32 * This test is to ensure that if TCP Fast Open is configured, that socket
33 * connections will still work. These tests are able to detect if TCP Fast
34 * Open works, but the tests will pass as long as the socket connects.
35 *
36 * The first test function tests the socket interface as implemented as BIOs.
37 *
38 * The second test functions tests the socket interface as implemented as fds.
39 *
40 * The tests are run 5 times. The first time is without TFO.
41 * The second test will create the TCP fast open cookie,
42 * this can be seen in `ip tcp_metrics` and in /proc/net/netstat/ on Linux.
43 * e.g. on Linux 4.15.0-135-generic:
44 * $ grep '^TcpExt:' /proc/net/netstat | cut -d ' ' -f 84-90 | column -t
45 * The third attempt will use the cookie and actually do TCP fast open.
46 * The 4th time is client-TFO only, the 5th time is server-TFO only.
47 */
48
49 # define SOCKET_DATA "FooBar"
50 # define SOCKET_DATA_LEN sizeof(SOCKET_DATA)
51
test_bio_tfo(int idx)52 static int test_bio_tfo(int idx)
53 {
54 BIO *cbio = NULL;
55 BIO *abio = NULL;
56 BIO *sbio = NULL;
57 int ret = 0;
58 int sockerr = 0;
59 const char *port;
60 int server_tfo = 0;
61 int client_tfo = 0;
62 size_t bytes;
63 char read_buffer[20];
64
65 switch (idx) {
66 default:
67 case 0:
68 break;
69 case 1:
70 case 2:
71 server_tfo = 1;
72 client_tfo = 1;
73 break;
74 case 3:
75 client_tfo = 1;
76 break;
77 case 4:
78 server_tfo = 1;
79 break;
80 }
81
82 /* ACCEPT SOCKET */
83 if (!TEST_ptr(abio = BIO_new_accept("localhost:0"))
84 || !TEST_true(BIO_set_nbio_accept(abio, 1))
85 || !TEST_true(BIO_set_tfo_accept(abio, server_tfo))
86 || !TEST_int_gt(BIO_do_accept(abio), 0)
87 || !TEST_ptr(port = BIO_get_accept_port(abio))) {
88 sockerr = get_last_socket_error();
89 goto err;
90 }
91
92 /* Note: first BIO_do_accept will basically do the bind/listen */
93
94 /* CLIENT SOCKET */
95 if (!TEST_ptr(cbio = BIO_new_connect("localhost"))
96 || !TEST_long_gt(BIO_set_conn_port(cbio, port), 0)
97 || !TEST_long_gt(BIO_set_nbio(cbio, 1), 0)
98 || !TEST_long_gt(BIO_set_tfo(cbio, client_tfo), 0)) {
99 sockerr = get_last_socket_error();
100 goto err;
101 }
102
103 /* FIRST ACCEPT: no connection should be established */
104 if (BIO_do_accept(abio) <= 0) {
105 if (!BIO_should_retry(abio)) {
106 sockerr = get_last_socket_error();
107 BIO_printf(bio_err, "Error: failed without EAGAIN\n");
108 goto err;
109 }
110 } else {
111 sbio = BIO_pop(abio);
112 BIO_printf(bio_err, "Error: accepted unknown connection\n");
113 goto err;
114 }
115
116 /* CONNECT ATTEMPT: different behavior based on TFO support */
117 if (BIO_do_connect(cbio) <= 0) {
118 sockerr = get_last_socket_error();
119 if (sockerr == EOPNOTSUPP) {
120 BIO_printf(bio_err, "Skip: TFO not enabled/supported for client\n");
121 goto success;
122 } else if (sockerr != EINPROGRESS) {
123 BIO_printf(bio_err, "Error: failed without EINPROGRESSn");
124 goto err;
125 }
126 }
127
128 /* macOS needs some time for this to happen, so put in a select */
129 if (!TEST_int_ge(BIO_wait(abio, time(NULL) + 2, 0), 0)) {
130 sockerr = get_last_socket_error();
131 BIO_printf(bio_err, "Error: socket wait failed\n");
132 goto err;
133 }
134
135 /* SECOND ACCEPT: if TFO is supported, this will still fail until data is sent */
136 if (BIO_do_accept(abio) <= 0) {
137 if (!BIO_should_retry(abio)) {
138 sockerr = get_last_socket_error();
139 BIO_printf(bio_err, "Error: failed without EAGAIN\n");
140 goto err;
141 }
142 } else {
143 if (idx == 0)
144 BIO_printf(bio_err, "Success: non-TFO connection accepted without data\n");
145 else if (idx == 1)
146 BIO_printf(bio_err, "Ignore: connection accepted before data, possibly no TFO cookie, or TFO may not be enabled\n");
147 else if (idx == 4)
148 BIO_printf(bio_err, "Success: connection accepted before data, client TFO is disabled\n");
149 else
150 BIO_printf(bio_err, "Warning: connection accepted before data, TFO may not be enabled\n");
151 sbio = BIO_pop(abio);
152 goto success;
153 }
154
155 /* SEND DATA: this should establish the actual TFO connection */
156 if (!TEST_true(BIO_write_ex(cbio, SOCKET_DATA, SOCKET_DATA_LEN, &bytes))) {
157 sockerr = get_last_socket_error();
158 goto err;
159 }
160
161 /* macOS needs some time for this to happen, so put in a select */
162 if (!TEST_int_ge(BIO_wait(abio, time(NULL) + 2, 0), 0)) {
163 sockerr = get_last_socket_error();
164 BIO_printf(bio_err, "Error: socket wait failed\n");
165 goto err;
166 }
167
168 /* FINAL ACCEPT: if TFO is enabled, socket should be accepted at *this* point */
169 if (BIO_do_accept(abio) <= 0) {
170 sockerr = get_last_socket_error();
171 BIO_printf(bio_err, "Error: socket not accepted\n");
172 goto err;
173 }
174 BIO_printf(bio_err, "Success: Server accepted socket after write\n");
175 if (!TEST_ptr(sbio = BIO_pop(abio))
176 || !TEST_true(BIO_read_ex(sbio, read_buffer, sizeof(read_buffer), &bytes))
177 || !TEST_size_t_eq(bytes, SOCKET_DATA_LEN)
178 || !TEST_strn_eq(read_buffer, SOCKET_DATA, SOCKET_DATA_LEN)) {
179 sockerr = get_last_socket_error();
180 goto err;
181 }
182
183 success:
184 sockerr = 0;
185 ret = 1;
186
187 err:
188 if (sockerr != 0) {
189 const char *errstr = strerror(sockerr);
190
191 if (errstr != NULL)
192 BIO_printf(bio_err, "last errno: %d=%s\n", sockerr, errstr);
193 }
194 BIO_free(cbio);
195 BIO_free(abio);
196 BIO_free(sbio);
197 return ret;
198 }
199
test_fd_tfo(int idx)200 static int test_fd_tfo(int idx)
201 {
202 struct sockaddr_storage sstorage;
203 socklen_t slen;
204 struct addrinfo *ai = NULL;
205 struct addrinfo hints;
206 int ret = 0;
207 int cfd = -1; /* client socket */
208 int afd = -1; /* accept socket */
209 int sfd = -1; /* server accepted socket */
210 BIO_ADDR *baddr = NULL;
211 char read_buffer[20];
212 int bytes_read;
213 int server_flags = BIO_SOCK_NONBLOCK;
214 int client_flags = BIO_SOCK_NONBLOCK;
215 int sockerr = 0;
216 unsigned short port;
217 void *addr;
218 size_t addrlen;
219
220 switch (idx) {
221 default:
222 case 0:
223 break;
224 case 1:
225 case 2:
226 server_flags |= BIO_SOCK_TFO;
227 client_flags |= BIO_SOCK_TFO;
228 break;
229 case 3:
230 client_flags |= BIO_SOCK_TFO;
231 break;
232 case 4:
233 server_flags |= BIO_SOCK_TFO;
234 break;
235 }
236
237 /* ADDRESS SETUP */
238 memset(&hints, 0, sizeof(hints));
239 hints.ai_family = AF_UNSPEC;
240 hints.ai_socktype = SOCK_STREAM;
241 if (!TEST_int_eq(getaddrinfo(NULL, "0", &hints, &ai), 0))
242 goto err;
243
244 switch (ai->ai_family) {
245 case AF_INET:
246 port = ((struct sockaddr_in *)ai->ai_addr)->sin_port;
247 addr = &((struct sockaddr_in *)ai->ai_addr)->sin_addr;
248 addrlen = sizeof(((struct sockaddr_in *)ai->ai_addr)->sin_addr);
249 BIO_printf(bio_err, "Using IPv4\n");
250 break;
251 case AF_INET6:
252 port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port;
253 addr = &((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr;
254 addrlen = sizeof(((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr);
255 BIO_printf(bio_err, "Using IPv6\n");
256 break;
257 default:
258 BIO_printf(bio_err, "Unknown address family %d\n", ai->ai_family);
259 goto err;
260 }
261
262 if (!TEST_ptr(baddr = BIO_ADDR_new())
263 || !TEST_true(BIO_ADDR_rawmake(baddr, ai->ai_family, addr, addrlen, port)))
264 goto err;
265
266 /* ACCEPT SOCKET */
267
268 if (!TEST_int_ge(afd = BIO_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, 0), 0)
269 || !TEST_true(BIO_listen(afd, baddr, server_flags)))
270 goto err;
271
272 /* UPDATE ADDRESS WITH PORT */
273 slen = sizeof(sstorage);
274 if (!TEST_int_ge(getsockname(afd, (struct sockaddr *)&sstorage, &slen), 0))
275 goto err;
276
277 switch (sstorage.ss_family) {
278 case AF_INET:
279 port = ((struct sockaddr_in *)&sstorage)->sin_port;
280 addr = &((struct sockaddr_in *)&sstorage)->sin_addr;
281 addrlen = sizeof(((struct sockaddr_in *)&sstorage)->sin_addr);
282 break;
283 case AF_INET6:
284 port = ((struct sockaddr_in6 *)&sstorage)->sin6_port;
285 addr = &((struct sockaddr_in6 *)&sstorage)->sin6_addr;
286 addrlen = sizeof(((struct sockaddr_in6 *)&sstorage)->sin6_addr);
287 break;
288 default:
289 goto err;
290 }
291
292 if(!TEST_true(BIO_ADDR_rawmake(baddr, sstorage.ss_family, addr, addrlen, port)))
293 goto err;
294
295 /* CLIENT SOCKET */
296 if (!TEST_int_ge(cfd = BIO_socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol, 0), 0))
297 goto err;
298
299 /* FIRST ACCEPT: no connection should be established */
300 sfd = BIO_accept_ex(afd, NULL, 0);
301 if (sfd == -1) {
302 sockerr = get_last_socket_error();
303 /* Note: Windows would hit WSAEWOULDBLOCK */
304 if (sockerr != EAGAIN) {
305 BIO_printf(bio_err, "Error: failed without EAGAIN\n");
306 goto err;
307 }
308 } else {
309 BIO_printf(bio_err, "Error: accepted unknown connection\n");
310 goto err;
311 }
312
313 /* CONNECT ATTEMPT: different behavior based on TFO support */
314 if (!BIO_connect(cfd, baddr, client_flags)) {
315 sockerr = get_last_socket_error();
316 if (sockerr == EOPNOTSUPP) {
317 BIO_printf(bio_err, "Skip: TFO not enabled/supported for client\n");
318 goto success;
319 } else {
320 /* Note: Windows would hit WSAEWOULDBLOCK */
321 if (sockerr != EINPROGRESS) {
322 BIO_printf(bio_err, "Error: failed without EINPROGRESS\n");
323 goto err;
324 }
325 }
326 }
327
328 /* macOS needs some time for this to happen, so put in a select */
329 if (!TEST_int_ge(BIO_socket_wait(afd, 1, time(NULL) + 2), 0)) {
330 sockerr = get_last_socket_error();
331 BIO_printf(bio_err, "Error: socket wait failed\n");
332 goto err;
333 }
334
335 /* SECOND ACCEPT: if TFO is supported, this will still fail until data is sent */
336 sfd = BIO_accept_ex(afd, NULL, 0);
337 if (sfd == -1) {
338 sockerr = get_last_socket_error();
339 /* Note: Windows would hit WSAEWOULDBLOCK */
340 if (sockerr != EAGAIN) {
341 BIO_printf(bio_err, "Error: failed without EAGAIN\n");
342 goto err;
343 }
344 } else {
345 if (idx == 0)
346 BIO_printf(bio_err, "Success: non-TFO connection accepted without data\n");
347 else if (idx == 1)
348 BIO_printf(bio_err, "Ignore: connection accepted before data, possibly no TFO cookie, or TFO may not be enabled\n");
349 else if (idx == 4)
350 BIO_printf(bio_err, "Success: connection accepted before data, client TFO is disabled\n");
351 else
352 BIO_printf(bio_err, "Warning: connection accepted before data, TFO may not be enabled\n");
353 goto success;
354 }
355
356 /* SEND DATA: this should establish the actual TFO connection */
357 #ifdef OSSL_TFO_SENDTO
358 if (!TEST_int_ge(sendto(cfd, SOCKET_DATA, SOCKET_DATA_LEN, OSSL_TFO_SENDTO,
359 (struct sockaddr *)&sstorage, slen), 0)) {
360 sockerr = get_last_socket_error();
361 goto err;
362 }
363 #else
364 if (!TEST_int_ge(writesocket(cfd, SOCKET_DATA, SOCKET_DATA_LEN), 0)) {
365 sockerr = get_last_socket_error();
366 goto err;
367 }
368 #endif
369
370 /* macOS needs some time for this to happen, so put in a select */
371 if (!TEST_int_ge(BIO_socket_wait(afd, 1, time(NULL) + 2), 0)) {
372 sockerr = get_last_socket_error();
373 BIO_printf(bio_err, "Error: socket wait failed\n");
374 goto err;
375 }
376
377 /* FINAL ACCEPT: if TFO is enabled, socket should be accepted at *this* point */
378 sfd = BIO_accept_ex(afd, NULL, 0);
379 if (sfd == -1) {
380 sockerr = get_last_socket_error();
381 BIO_printf(bio_err, "Error: socket not accepted\n");
382 goto err;
383 }
384 BIO_printf(bio_err, "Success: Server accepted socket after write\n");
385 bytes_read = readsocket(sfd, read_buffer, sizeof(read_buffer));
386 if (!TEST_int_eq(bytes_read, SOCKET_DATA_LEN)
387 || !TEST_strn_eq(read_buffer, SOCKET_DATA, SOCKET_DATA_LEN)) {
388 sockerr = get_last_socket_error();
389 goto err;
390 }
391
392 success:
393 sockerr = 0;
394 ret = 1;
395
396 err:
397 if (sockerr != 0) {
398 const char *errstr = strerror(sockerr);
399
400 if (errstr != NULL)
401 BIO_printf(bio_err, "last errno: %d=%s\n", sockerr, errstr);
402 }
403 if (ai != NULL)
404 freeaddrinfo(ai);
405 BIO_ADDR_free(baddr);
406 BIO_closesocket(cfd);
407 BIO_closesocket(sfd);
408 BIO_closesocket(afd);
409 return ret;
410 }
411 #endif
412
setup_tests(void)413 int setup_tests(void)
414 {
415 #if !defined(OPENSSL_NO_TFO) && defined(GOOD_OS)
416 ADD_ALL_TESTS(test_bio_tfo, 5);
417 ADD_ALL_TESTS(test_fd_tfo, 5);
418 #endif
419 return 1;
420 }
421