diff options
Diffstat (limited to 'test/handshake_helper.c')
-rw-r--r-- | test/handshake_helper.c | 202 |
1 files changed, 172 insertions, 30 deletions
diff --git a/test/handshake_helper.c b/test/handshake_helper.c index 94fa5c578f..4943e82d83 100644 --- a/test/handshake_helper.c +++ b/test/handshake_helper.c @@ -16,6 +16,11 @@ #include <openssl/srp.h> #endif +#ifndef OPENSSL_NO_SOCK +# define USE_SOCKETS +# include "e_os.h" +#endif + #include "handshake_helper.h" #include "testutil.h" @@ -631,7 +636,8 @@ static void configure_handshake_ssl(SSL *server, SSL *client, typedef enum { PEER_SUCCESS, PEER_RETRY, - PEER_ERROR + PEER_ERROR, + PEER_WAITING } peer_status_t; /* An SSL object and associated read-write buffers. */ @@ -898,8 +904,8 @@ static void do_shutdown_step(PEER *peer) peer->status = PEER_SUCCESS; } else if (ret < 0) { /* On 0, we retry. */ int error = SSL_get_error(peer->ssl, ret); - /* Memory bios should never block with SSL_ERROR_WANT_WRITE. */ - if (error != SSL_ERROR_WANT_READ) + + if (error != SSL_ERROR_WANT_READ && error != SSL_ERROR_WANT_WRITE) peer->status = PEER_ERROR; } } @@ -1017,18 +1023,13 @@ static handshake_status_t handshake_status(peer_status_t last_status, } case PEER_RETRY: - if (previous_status == PEER_RETRY) { - /* Neither peer is done. */ - return HANDSHAKE_RETRY; - } else { - /* - * Deadlock: second peer is waiting for more input while first - * peer thinks they're done (no more input is coming). - */ - return INTERNAL_ERROR; - } + return HANDSHAKE_RETRY; + case PEER_ERROR: switch (previous_status) { + case PEER_WAITING: + /* The client failed immediately before sending the ClientHello */ + return client_spoke_last ? CLIENT_ERROR : INTERNAL_ERROR; case PEER_SUCCESS: /* * First peer succeeded but second peer errored. @@ -1091,6 +1092,107 @@ static int peer_pkey_type(SSL *s) return NID_undef; } +#if !defined(OPENSSL_NO_SCTP) && !defined(OPENSSL_NO_SOCK) +static int set_sock_as_sctp(int sock) +{ + /* + * For SCTP we have to set various options on the socket prior to + * connecting. This is done automatically by BIO_new_dgram_sctp(). + * We don't actually need the created BIO though so we free it again + * immediately. + */ + BIO *tmpbio = BIO_new_dgram_sctp(sock, BIO_NOCLOSE); + + if (tmpbio == NULL) + return 0; + BIO_free(tmpbio); + + return 1; +} + +static int create_sctp_socks(int *ssock, int *csock) +{ + BIO_ADDRINFO *res = NULL; + const BIO_ADDRINFO *ai = NULL; + int lsock = INVALID_SOCKET, asock = INVALID_SOCKET; + int consock = INVALID_SOCKET; + int ret = 0; + int family = 0; + + if (!BIO_sock_init()) + return 0; + + /* + * Port is 4463. It could be anything. It will fail if it's already being + * used for some other SCTP service. It seems unlikely though so we don't + * worry about it here. + */ + if (!BIO_lookup_ex(NULL, "4463", BIO_LOOKUP_SERVER, family, SOCK_STREAM, + IPPROTO_SCTP, &res)) + return 0; + + for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) { + family = BIO_ADDRINFO_family(ai); + lsock = BIO_socket(family, SOCK_STREAM, IPPROTO_SCTP, 0); + if (lsock == INVALID_SOCKET) { + /* Maybe the kernel doesn't support the socket family, even if + * BIO_lookup() added it in the returned result... + */ + continue; + } + + if (!set_sock_as_sctp(lsock) + || !BIO_listen(lsock, BIO_ADDRINFO_address(ai), + BIO_SOCK_REUSEADDR)) { + BIO_closesocket(lsock); + lsock = INVALID_SOCKET; + continue; + } + + /* Success, don't try any more addresses */ + break; + } + + if (lsock == INVALID_SOCKET) + goto err; + + BIO_ADDRINFO_free(res); + res = NULL; + + if (!BIO_lookup_ex(NULL, "4463", BIO_LOOKUP_CLIENT, family, SOCK_STREAM, + IPPROTO_SCTP, &res)) + goto err; + + consock = BIO_socket(family, SOCK_STREAM, IPPROTO_SCTP, 0); + if (consock == INVALID_SOCKET) + goto err; + + if (!set_sock_as_sctp(consock) + || !BIO_connect(consock, BIO_ADDRINFO_address(res), 0) + || !BIO_socket_nbio(consock, 1)) + goto err; + + asock = BIO_accept_ex(lsock, NULL, BIO_SOCK_NONBLOCK); + if (asock == INVALID_SOCKET) + goto err; + + *csock = consock; + *ssock = asock; + consock = asock = INVALID_SOCKET; + ret = 1; + + err: + BIO_ADDRINFO_free(res); + if (consock != INVALID_SOCKET) + BIO_closesocket(consock); + if (lsock != INVALID_SOCKET) + BIO_closesocket(lsock); + if (asock != INVALID_SOCKET) + BIO_closesocket(asock); + return ret; +} +#endif + /* * Note that |extra| points to the correct client/server configuration * within |test_ctx|. When configuring the handshake, general mode settings @@ -1110,7 +1212,7 @@ static HANDSHAKE_RESULT *do_handshake_internal( SSL_SESSION *session_in, SSL_SESSION **session_out) { PEER server, client; - BIO *client_to_server, *server_to_client; + BIO *client_to_server = NULL, *server_to_client = NULL; HANDSHAKE_EX_DATA server_ex_data, client_ex_data; CTX_DATA client_ctx_data, server_ctx_data, server2_ctx_data; HANDSHAKE_RESULT *ret = HANDSHAKE_RESULT_new(); @@ -1125,6 +1227,7 @@ static HANDSHAKE_RESULT *do_handshake_internal( unsigned int proto_len = 0; EVP_PKEY *tmp_key; const STACK_OF(X509_NAME) *names; + time_t start; memset(&server_ctx_data, 0, sizeof(server_ctx_data)); memset(&server2_ctx_data, 0, sizeof(server2_ctx_data)); @@ -1154,8 +1257,19 @@ static HANDSHAKE_RESULT *do_handshake_internal( ret->result = SSL_TEST_INTERNAL_ERROR; - client_to_server = BIO_new(BIO_s_mem()); - server_to_client = BIO_new(BIO_s_mem()); + if (test_ctx->use_sctp) { +#if !defined(OPENSSL_NO_SCTP) && !defined(OPENSSL_NO_SOCK) + int csock, ssock; + + if (create_sctp_socks(&ssock, &csock)) { + client_to_server = BIO_new_dgram_sctp(csock, BIO_CLOSE); + server_to_client = BIO_new_dgram_sctp(ssock, BIO_CLOSE); + } +#endif + } else { + client_to_server = BIO_new(BIO_s_mem()); + server_to_client = BIO_new(BIO_s_mem()); + } TEST_check(client_to_server != NULL); TEST_check(server_to_client != NULL); @@ -1168,10 +1282,15 @@ static HANDSHAKE_RESULT *do_handshake_internal( SSL_set_accept_state(server.ssl); /* The bios are now owned by the SSL object. */ - SSL_set_bio(client.ssl, server_to_client, client_to_server); - TEST_check(BIO_up_ref(server_to_client) > 0); - TEST_check(BIO_up_ref(client_to_server) > 0); - SSL_set_bio(server.ssl, client_to_server, server_to_client); + if (test_ctx->use_sctp) { + SSL_set_bio(client.ssl, client_to_server, client_to_server); + SSL_set_bio(server.ssl, server_to_client, server_to_client); + } else { + SSL_set_bio(client.ssl, server_to_client, client_to_server); + TEST_check(BIO_up_ref(server_to_client) > 0); + TEST_check(BIO_up_ref(client_to_server) > 0); + SSL_set_bio(server.ssl, client_to_server, server_to_client); + } ex_data_idx = SSL_get_ex_new_index(0, "ex data", NULL, NULL, NULL); TEST_check(ex_data_idx >= 0); @@ -1182,7 +1301,10 @@ static HANDSHAKE_RESULT *do_handshake_internal( SSL_set_info_callback(server.ssl, &info_cb); SSL_set_info_callback(client.ssl, &info_cb); - client.status = server.status = PEER_RETRY; + client.status = PEER_RETRY; + server.status = PEER_WAITING; + + start = time(NULL); /* * Half-duplex handshake loop. @@ -1197,6 +1319,8 @@ static HANDSHAKE_RESULT *do_handshake_internal( do_connect_step(test_ctx, &client, phase); status = handshake_status(client.status, server.status, 1 /* client went last */); + if (server.status == PEER_WAITING) + server.status = PEER_RETRY; } else { do_connect_step(test_ctx, &server, phase); status = handshake_status(server.status, client.status, @@ -1231,18 +1355,36 @@ static HANDSHAKE_RESULT *do_handshake_internal( ret->result = SSL_TEST_INTERNAL_ERROR; goto err; case HANDSHAKE_RETRY: - if (client_turn_count++ >= 2000) { + if (test_ctx->use_sctp) { + if (time(NULL) - start > 3) { + /* + * We've waited for too long. Give up. + */ + ret->result = SSL_TEST_INTERNAL_ERROR; + goto err; + } /* - * At this point, there's been so many PEER_RETRY in a row - * that it's likely both sides are stuck waiting for a read. - * It's time to give up. + * With "real" sockets we only swap to processing the peer + * if they are expecting to retry. Otherwise we just retry the + * same endpoint again. */ - ret->result = SSL_TEST_INTERNAL_ERROR; - goto err; - } + if ((client_turn && server.status == PEER_RETRY) + || (!client_turn && client.status == PEER_RETRY)) + client_turn ^= 1; + } else { + if (client_turn_count++ >= 2000) { + /* + * At this point, there's been so many PEER_RETRY in a row + * that it's likely both sides are stuck waiting for a read. + * It's time to give up. + */ + ret->result = SSL_TEST_INTERNAL_ERROR; + goto err; + } - /* Continue. */ - client_turn ^= 1; + /* Continue. */ + client_turn ^= 1; + } break; } } |