xref: /openssl/test/bio_tfo_test.c (revision d272ef53)
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