diff --git a/codebuild/spec/buildspec_ktls.yml b/codebuild/spec/buildspec_ktls.yml index 5958b9c6711..ddece2a356e 100644 --- a/codebuild/spec/buildspec_ktls.yml +++ b/codebuild/spec/buildspec_ktls.yml @@ -1,5 +1,8 @@ --- version: 0.2 +env: + variables: + S2N_KTLS_TESTING_EXPECTED: true phases: install: commands: diff --git a/tests/testlib/s2n_io_testlib.c b/tests/testlib/s2n_io_testlib.c new file mode 100644 index 00000000000..cf5313412ee --- /dev/null +++ b/tests/testlib/s2n_io_testlib.c @@ -0,0 +1,84 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include "testlib/s2n_testlib.h" + +S2N_CLEANUP_RESULT s2n_test_iovecs_free(struct s2n_test_iovecs *in) +{ + RESULT_ENSURE_REF(in); + for (size_t i = 0; i < in->iovecs_count; i++) { + RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) &in->iovecs[i].iov_base, + in->iovecs[i].iov_len)); + } + RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) &in->iovecs, + sizeof(struct iovec) * in->iovecs_count)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_split_data(struct s2n_test_iovecs *iovecs, struct s2n_blob *data) +{ + RESULT_ENSURE_REF(iovecs); + RESULT_ENSURE_REF(data); + + struct s2n_stuffer in = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&in, data)); + + for (size_t i = 0; i < iovecs->iovecs_count; i++) { + if (iovecs->iovecs[i].iov_len == 0) { + continue; + } + struct s2n_blob mem = { 0 }; + RESULT_GUARD_POSIX(s2n_alloc(&mem, iovecs->iovecs[i].iov_len)); + RESULT_GUARD_POSIX(s2n_stuffer_read(&in, &mem)); + iovecs->iovecs[i].iov_base = mem.data; + } + RESULT_ENSURE_EQ(s2n_stuffer_data_available(&in), 0); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_new_iovecs(struct s2n_test_iovecs *iovecs, + struct s2n_blob *data, const size_t *lens, size_t lens_count) +{ + RESULT_ENSURE_REF(iovecs); + RESULT_ENSURE_REF(data); + RESULT_ENSURE_REF(lens); + + size_t len_total = 0; + for (size_t i = 0; i < lens_count; i++) { + len_total += lens[i]; + } + RESULT_ENSURE_LTE(len_total, data->size); + + size_t iovecs_count = lens_count; + if (len_total < data->size) { + iovecs_count++; + } + + struct s2n_blob iovecs_mem = { 0 }; + RESULT_GUARD_POSIX(s2n_alloc(&iovecs_mem, sizeof(struct iovec) * iovecs_count)); + RESULT_GUARD_POSIX(s2n_blob_zero(&iovecs_mem)); + iovecs->iovecs = (struct iovec *) (void *) iovecs_mem.data; + iovecs->iovecs_count = iovecs_count; + + for (size_t i = 0; i < lens_count; i++) { + iovecs->iovecs[i].iov_len = lens[i]; + } + if (lens_count < iovecs_count) { + iovecs->iovecs[lens_count].iov_len = data->size - len_total; + } + + RESULT_GUARD(s2n_test_split_data(iovecs, data)); + return S2N_RESULT_OK; +} diff --git a/tests/testlib/s2n_testlib.h b/tests/testlib/s2n_testlib.h index 0a5aeeacfc4..e67aaff6a7c 100644 --- a/tests/testlib/s2n_testlib.h +++ b/tests/testlib/s2n_testlib.h @@ -208,6 +208,18 @@ int s2n_shutdown_test_server_and_client(struct s2n_connection *server_conn, stru S2N_RESULT s2n_negotiate_test_server_and_client_with_early_data(struct s2n_connection *server_conn, struct s2n_connection *client_conn, struct s2n_blob *early_data_to_send, struct s2n_blob *early_data_received); +/* Testing only with easily constructed contiguous data buffers could hide errors. + * We should use iovecs where every buffer is allocated separately. + * These test methods construct separate io buffers from one contiguous buffer. + */ +struct s2n_test_iovecs { + struct iovec *iovecs; + size_t iovecs_count; +}; +S2N_RESULT s2n_test_new_iovecs(struct s2n_test_iovecs *iovecs, + struct s2n_blob *data, const size_t *lens, size_t lens_count); +S2N_CLEANUP_RESULT s2n_test_iovecs_free(struct s2n_test_iovecs *in); + struct s2n_kem_kat_test_vector { const struct s2n_kem *kem; const char *kat_file; diff --git a/tests/unit/s2n_ktls_io_test.c b/tests/unit/s2n_ktls_io_test.c index 3f437d92e4d..65dc4c8eca4 100644 --- a/tests/unit/s2n_ktls_io_test.c +++ b/tests/unit/s2n_ktls_io_test.c @@ -72,82 +72,6 @@ ssize_t s2n_test_ktls_recvmsg_io_stuffer_and_ctrunc(void *io_context, struct msg return ret; } -struct s2n_test_iovecs { - struct iovec *iovecs; - size_t iovecs_count; -}; - -static S2N_CLEANUP_RESULT s2n_test_iovecs_free(struct s2n_test_iovecs *in) -{ - RESULT_ENSURE_REF(in); - for (size_t i = 0; i < in->iovecs_count; i++) { - RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) &in->iovecs[i].iov_base, - in->iovecs[i].iov_len)); - } - RESULT_GUARD_POSIX(s2n_free_object((uint8_t **) &in->iovecs, - sizeof(struct iovec) * in->iovecs_count)); - return S2N_RESULT_OK; -} - -/* Testing only with contiguous data could hide errors. - * We should use iovecs where every buffer is allocated separately. - */ -static S2N_RESULT s2n_test_split_data(struct s2n_test_iovecs *iovecs, struct s2n_blob *data) -{ - RESULT_ENSURE_REF(iovecs); - RESULT_ENSURE_REF(data); - - struct s2n_stuffer in = { 0 }; - RESULT_GUARD_POSIX(s2n_stuffer_init_written(&in, data)); - - for (size_t i = 0; i < iovecs->iovecs_count; i++) { - if (iovecs->iovecs[i].iov_len == 0) { - continue; - } - struct s2n_blob mem = { 0 }; - RESULT_GUARD_POSIX(s2n_alloc(&mem, iovecs->iovecs[i].iov_len)); - RESULT_GUARD_POSIX(s2n_stuffer_read(&in, &mem)); - iovecs->iovecs[i].iov_base = mem.data; - } - RESULT_ENSURE_EQ(s2n_stuffer_data_available(&in), 0); - return S2N_RESULT_OK; -} - -static S2N_RESULT s2n_test_new_iovecs(struct s2n_test_iovecs *iovecs, - struct s2n_blob *data, const size_t *lens, size_t lens_count) -{ - RESULT_ENSURE_REF(iovecs); - RESULT_ENSURE_REF(data); - RESULT_ENSURE_REF(lens); - - size_t len_total = 0; - for (size_t i = 0; i < lens_count; i++) { - len_total += lens[i]; - } - RESULT_ENSURE_LTE(len_total, data->size); - - size_t iovecs_count = lens_count; - if (len_total < data->size) { - iovecs_count++; - } - - struct s2n_blob iovecs_mem = { 0 }; - RESULT_GUARD_POSIX(s2n_alloc(&iovecs_mem, sizeof(struct iovec) * iovecs_count)); - RESULT_GUARD_POSIX(s2n_blob_zero(&iovecs_mem)); - iovecs->iovecs = (struct iovec *) iovecs_mem.data; - iovecs->iovecs_count = iovecs_count; - - for (size_t i = 0; i < lens_count; i++) { - iovecs->iovecs[i].iov_len = lens[i]; - } - if (lens_count < iovecs_count) { - iovecs->iovecs[lens_count].iov_len = data->size - len_total; - } - - RESULT_GUARD(s2n_test_split_data(iovecs, data)); - return S2N_RESULT_OK; -} - int main(int argc, char **argv) { BEGIN_TEST(); diff --git a/tests/unit/s2n_self_talk_inet_socket_test.c b/tests/unit/s2n_self_talk_inet_socket_test.c deleted file mode 100644 index 4399d617790..00000000000 --- a/tests/unit/s2n_self_talk_inet_socket_test.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). - * You may not use this file except in compliance with the License. - * A copy of the License is located at - * - * http://aws.amazon.com/apache2.0 - * - * or in the "license" file accompanying this file. This file is distributed - * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing - * permissions and limitations under the License. - */ - -#include <netinet/in.h> -#include <sys/socket.h> -#include <sys/wait.h> -#include <unistd.h> - -#include "s2n_test.h" -#include "testlib/s2n_testlib.h" - -/* There are issues with MacOS and FreeBSD so we define the constant ourselves. - * https://stackoverflow.com/a/34042435 */ -#define S2N_TEST_INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */ - -const char CHAR_A = 'a'; -const char CHAR_B = 'b'; - -/* A collection of callbacks run during inet socket self talk tests */ -struct self_talk_inet_socket_callbacks { - S2N_RESULT (*server_post_handshake_cb)(struct s2n_connection *conn); - S2N_RESULT (*client_post_handshake_cb)(struct s2n_connection *conn); -}; - -S2N_RESULT s2n_noop_inet_socket_cb(struct s2n_connection *conn) -{ - return S2N_RESULT_OK; -} - -static S2N_RESULT start_client(int fd, int read_pipe, const struct self_talk_inet_socket_callbacks *socket_cb) -{ - /* Setup connections */ - DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, s2n_cert_chain_and_key_ptr_free); - RESULT_GUARD_POSIX(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Setup config */ - RESULT_GUARD_POSIX(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); - RESULT_GUARD_POSIX(s2n_connection_set_fd(client_conn, fd)); - RESULT_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default")); - RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(config)); - RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - RESULT_GUARD_POSIX(s2n_connection_set_config(client_conn, config)); - - /* Complete the handshake */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - RESULT_GUARD_POSIX(s2n_negotiate(client_conn, &blocked)); - RESULT_ENSURE_EQ(client_conn->actual_protocol_version, S2N_TLS12); - - RESULT_GUARD(socket_cb->client_post_handshake_cb(client_conn)); - - char sync = 0; - char recv_buffer[1] = { 0 }; - - RESULT_GUARD_POSIX(read(read_pipe, &sync, 1)); - RESULT_GUARD_POSIX(s2n_recv(client_conn, recv_buffer, 1, &blocked)); - RESULT_ENSURE_EQ(memcmp(&CHAR_A, &recv_buffer[0], 1), 0); - - RESULT_GUARD_POSIX(read(read_pipe, &sync, 1)); - RESULT_GUARD_POSIX(s2n_recv(client_conn, recv_buffer, 1, &blocked)); - RESULT_ENSURE_EQ(memcmp(&CHAR_B, &recv_buffer[0], 1), 0); - - return S2N_RESULT_OK; -} - -static S2N_RESULT start_server(int fd, int write_pipe, const struct self_talk_inet_socket_callbacks *socket_cb) -{ - /* Setup connections */ - DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), - s2n_connection_ptr_free); - DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); - - DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, s2n_cert_chain_and_key_ptr_free); - RESULT_GUARD_POSIX(s2n_test_cert_chain_and_key_new(&chain_and_key, - S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); - - /* Setup config */ - RESULT_GUARD_POSIX(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); - RESULT_ENSURE_EQ(s2n_connection_get_delay(server_conn), 0); - RESULT_GUARD_POSIX(s2n_connection_set_fd(server_conn, fd)); - RESULT_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default")); - RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(config)); - RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); - RESULT_GUARD_POSIX(s2n_connection_set_config(server_conn, config)); - - /* Complete the handshake */ - s2n_blocked_status blocked = S2N_NOT_BLOCKED; - RESULT_GUARD_POSIX(s2n_negotiate(server_conn, &blocked)); - RESULT_ENSURE_EQ(server_conn->actual_protocol_version, S2N_TLS12); - - RESULT_GUARD(socket_cb->server_post_handshake_cb(server_conn)); - - char sync = 0; - char send_buffer[1] = { 0 }; - - send_buffer[0] = CHAR_A; - RESULT_GUARD_POSIX(s2n_send(server_conn, send_buffer, 1, &blocked)); - RESULT_GUARD_POSIX(write(write_pipe, &sync, 1)); - - send_buffer[0] = CHAR_B; - RESULT_GUARD_POSIX(s2n_send(server_conn, send_buffer, 1, &blocked)); - RESULT_GUARD_POSIX(write(write_pipe, &sync, 1)); - - return S2N_RESULT_OK; -} - -static S2N_RESULT launch_test(const struct self_talk_inet_socket_callbacks *socket_cb) -{ - /* configure real socket */ - int listener = socket(AF_INET, SOCK_STREAM, 0); - RESULT_GUARD_POSIX(listener); - struct sockaddr_in saddr = { 0 }; - saddr.sin_family = AF_INET; - saddr.sin_addr.s_addr = htonl(S2N_TEST_INADDR_LOOPBACK); - saddr.sin_port = 0; - - /* listen on socket address */ - socklen_t addrlen = sizeof(saddr); - RESULT_GUARD_POSIX(bind(listener, (struct sockaddr *) &saddr, addrlen)); - RESULT_GUARD_POSIX(getsockname(listener, (struct sockaddr *) &saddr, &addrlen)); - - /* Used for synchronizing read and writes between client and server. - * - * The purpose of this mechanism is to prevent the client from reading - * before the server has sent it. The client blocks on reading a single - * char while the server writes a char after it has sent data and wishes - * to unblock the client. */ - int sync_pipe[2] = { 0 }; - RESULT_GUARD_POSIX(pipe(sync_pipe)); - - pid_t child = fork(); - RESULT_ENSURE(child >= 0, S2N_ERR_SAFETY); - int status = 0; - int fd = 0; - if (child) { - /* server */ - RESULT_GUARD_POSIX(listen(listener, 1)); - fd = accept(listener, NULL, NULL); - RESULT_GUARD_POSIX(fd); - - RESULT_GUARD_POSIX(close(sync_pipe[0])); - RESULT_GUARD(start_server(fd, sync_pipe[1], socket_cb)); - - RESULT_ENSURE_EQ(waitpid(-1, &status, 0), child); - RESULT_ENSURE_EQ(status, 0); - } else { - /* client */ - fd = socket(AF_INET, SOCK_STREAM, 0); - RESULT_GUARD_POSIX(fd); - - /* wait for server to start up */ - sleep(1); - RESULT_GUARD_POSIX(connect(fd, (struct sockaddr *) &saddr, addrlen)); - - RESULT_GUARD_POSIX(close(sync_pipe[1])); - RESULT_GUARD(start_client(fd, sync_pipe[0], socket_cb)); - exit(0); - } - - return S2N_RESULT_OK; -} - -/* This test is unique compared to our other self talk tests because it's testing - * AF_INET socket functionality. - * - * When enabled, kTLS offloads the TLS protocol to the socket. It's not possible to - * enable kTLS on an AF_UNIX socket, which is what our other self talk tests use. - * Instead we need to use AF_INET sockets to test kTLS functionality. The use of - * AF_INET sockets also drives the need to `fork` and create two processes (client and - * server) to establish a TLS connection. - */ -int main(int argc, char **argv) -{ - BEGIN_TEST(); - - /* SIGPIPE is received when a process tries to write to a socket which - * has been shutdown. Ignore it and handle it gracefully. */ - signal(SIGPIPE, SIG_IGN); - - /* A regular connection */ - const struct self_talk_inet_socket_callbacks noop_inet_cb = { - .server_post_handshake_cb = s2n_noop_inet_socket_cb, - .client_post_handshake_cb = s2n_noop_inet_socket_cb, - }; - EXPECT_OK(launch_test(&noop_inet_cb)); - - END_TEST(); -} diff --git a/tests/unit/s2n_self_talk_ktls_test.c b/tests/unit/s2n_self_talk_ktls_test.c new file mode 100644 index 00000000000..45743ccb182 --- /dev/null +++ b/tests/unit/s2n_self_talk_ktls_test.c @@ -0,0 +1,271 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#include <fcntl.h> +#include <netinet/in.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_ktls.h" +#include "utils/s2n_random.h" + +/* There are issues with MacOS and FreeBSD so we define the constant ourselves. + * https://stackoverflow.com/a/34042435 */ +#define S2N_TEST_INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */ + +/* Unlike our other self-talk tests, this test cannot use AF_UNIX / AF_LOCAL. + * For a real self-talk test we need real kernel support for kTLS, and only + * AF_INET sockets support kTLS. + */ +static S2N_RESULT s2n_new_inet_socket_pair(struct s2n_test_io_pair *io_pair) +{ + RESULT_ENSURE_REF(io_pair); + + int listener = socket(AF_INET, SOCK_STREAM, 0); + RESULT_ENSURE_GT(listener, 0); + + struct sockaddr_in saddr = { 0 }; + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(S2N_TEST_INADDR_LOOPBACK); + saddr.sin_port = 0; + + socklen_t addrlen = sizeof(saddr); + RESULT_ENSURE_EQ(bind(listener, (struct sockaddr *) &saddr, addrlen), 0); + RESULT_ENSURE_EQ(getsockname(listener, (struct sockaddr *) &saddr, &addrlen), 0); + RESULT_ENSURE_EQ(listen(listener, 1), 0); + + io_pair->client = socket(AF_INET, SOCK_STREAM, 0); + RESULT_ENSURE_GT(io_pair->client, 0); + + fflush(stdout); + pid_t pid = fork(); + RESULT_ENSURE_GTE(pid, 0); + if (pid == 0) { + RESULT_ENSURE_EQ(connect(io_pair->client, (struct sockaddr *) &saddr, addrlen), 0); + ZERO_TO_DISABLE_DEFER_CLEANUP(io_pair); + exit(0); + } + io_pair->server = accept(listener, NULL, NULL); + RESULT_ENSURE_GT(io_pair->server, 0); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* ktls is complicated to enable. We should ensure that it's actually enabled + * where we think we're testing it. + */ + bool ktls_expected = (getenv("S2N_KTLS_TESTING_EXPECTED") != NULL); + + if (!s2n_ktls_is_supported_on_platform() && !ktls_expected) { + END_TEST(); + } + + const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + uint8_t test_data[100] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + DEFER_CLEANUP(struct s2n_test_iovecs test_iovecs = { 0 }, s2n_test_iovecs_free); + size_t test_iovecs_lens[20] = { 5, 6, 1, 10, 0 }; + EXPECT_OK(s2n_test_new_iovecs(&test_iovecs, &test_data_blob, test_iovecs_lens, + s2n_array_len(test_iovecs_lens))); + + const size_t test_offsets[] = { + 0, + test_iovecs_lens[0], + test_iovecs_lens[0] + 1, + test_iovecs_lens[0] + test_iovecs_lens[1], + sizeof(test_data) - 1, + sizeof(test_data), + }; + + uint8_t file_test_data[100] = { 0 }; + int file = open(argv[0], O_RDONLY); + EXPECT_TRUE(file > 0); + int file_read = pread(file, file_test_data, sizeof(file_test_data), 0); + EXPECT_EQUAL(file_read, sizeof(file_test_data)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + /* Test enabling ktls for sending */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + if (s2n_result_is_error(s2n_new_inet_socket_pair(&io_pair))) { + /* We should be able to setup AF_INET sockets everywhere, but if + * we can't, don't block the build unless the build explicitly expects + * to be able to test ktls. + */ + EXPECT_FALSE(ktls_expected); + END_TEST(); + } + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + /* The test negotiate method assumes non-blocking sockets */ + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.client)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + if (s2n_connection_ktls_enable_send(client) != S2N_SUCCESS) { + /* Even if we detected ktls support at compile time, enabling ktls + * can fail at runtime if the system is not properly configured. + */ + EXPECT_FALSE(ktls_expected); + END_TEST(); + } + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server)); + }; + + /* Test sending with ktls */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + const s2n_mode mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + /* The test negotiate method assumes non-blocking sockets */ + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.client)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_connection *conns[] = { + [S2N_CLIENT] = client, + [S2N_SERVER] = server, + }; + struct s2n_connection *writer = conns[mode]; + struct s2n_connection *reader = conns[S2N_PEER_MODE(mode)]; + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(writer)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Our IO methods are more predictable if they use blocking sockets. */ + EXPECT_SUCCESS(s2n_fd_set_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_blocking(io_pair.client)); + + /* Test: s2n_send */ + for (size_t i = 0; i < 5; i++) { + int written = s2n_send(writer, test_data, sizeof(test_data), &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); + } + + /* Test: s2n_sendv */ + for (size_t i = 0; i < 5; i++) { + int written = s2n_sendv(writer, + test_iovecs.iovecs, test_iovecs.iovecs_count, &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); + } + + /* Test: s2n_sendv_with_offset */ + for (size_t offset_i = 0; offset_i < s2n_array_len(test_offsets); offset_i++) { + const size_t offset = test_offsets[offset_i]; + const size_t expected_written = sizeof(test_data) - offset; + + int written = s2n_sendv_with_offset(writer, + test_iovecs.iovecs, test_iovecs.iovecs_count, offset, &blocked); + EXPECT_EQUAL(written, expected_written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, expected_written, &blocked); + EXPECT_EQUAL(read, expected_written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data + offset, buffer, read); + }; + + /* Test: s2n_sendfile */ + for (size_t offset_i = 0; offset_i < s2n_array_len(test_offsets); offset_i++) { + const size_t offset = test_offsets[offset_i]; + const size_t expected_written = sizeof(test_data) - offset; + + size_t written = 0; + EXPECT_SUCCESS(s2n_sendfile(writer, file, offset, expected_written, + &written, &blocked)); + EXPECT_EQUAL(written, expected_written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t buffer[sizeof(file_test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, expected_written, &blocked); + EXPECT_EQUAL(read, expected_written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(file_test_data + offset, buffer, read); + } + + /* Test: s2n_shutdown */ + { + EXPECT_SUCCESS(s2n_shutdown_send(writer, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_SUCCESS(s2n_shutdown(reader, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + }; + }; + + END_TEST(); +}