Home | History | Annotate | Line # | Download | only in test
      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),
    360             0)) {
    361         sockerr = get_last_socket_error();
    362         goto err;
    363     }
    364 #else
    365     if (!TEST_int_ge(writesocket(cfd, SOCKET_DATA, SOCKET_DATA_LEN), 0)) {
    366         sockerr = get_last_socket_error();
    367         goto err;
    368     }
    369 #endif
    370 
    371     /* macOS needs some time for this to happen, so put in a select */
    372     if (!TEST_int_ge(BIO_socket_wait(afd, 1, time(NULL) + 2), 0)) {
    373         sockerr = get_last_socket_error();
    374         BIO_printf(bio_err, "Error: socket wait failed\n");
    375         goto err;
    376     }
    377 
    378     /* FINAL ACCEPT: if TFO is enabled, socket should be accepted at *this* point */
    379     sfd = BIO_accept_ex(afd, NULL, 0);
    380     if (sfd == -1) {
    381         sockerr = get_last_socket_error();
    382         BIO_printf(bio_err, "Error: socket not accepted\n");
    383         goto err;
    384     }
    385     BIO_printf(bio_err, "Success: Server accepted socket after write\n");
    386     bytes_read = readsocket(sfd, read_buffer, sizeof(read_buffer));
    387     if (!TEST_int_eq(bytes_read, SOCKET_DATA_LEN)
    388         || !TEST_strn_eq(read_buffer, SOCKET_DATA, SOCKET_DATA_LEN)) {
    389         sockerr = get_last_socket_error();
    390         goto err;
    391     }
    392 
    393 success:
    394     sockerr = 0;
    395     ret = 1;
    396 
    397 err:
    398     if (sockerr != 0) {
    399         const char *errstr = strerror(sockerr);
    400 
    401         if (errstr != NULL)
    402             BIO_printf(bio_err, "last errno: %d=%s\n", sockerr, errstr);
    403     }
    404     if (ai != NULL)
    405         freeaddrinfo(ai);
    406     BIO_ADDR_free(baddr);
    407     BIO_closesocket(cfd);
    408     BIO_closesocket(sfd);
    409     BIO_closesocket(afd);
    410     return ret;
    411 }
    412 #endif
    413 
    414 int setup_tests(void)
    415 {
    416 #if !defined(OPENSSL_NO_TFO) && defined(GOOD_OS)
    417     ADD_ALL_TESTS(test_bio_tfo, 5);
    418     ADD_ALL_TESTS(test_fd_tfo, 5);
    419 #endif
    420     return 1;
    421 }
    422