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 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 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 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