diff --git a/bindings/rust/bench/README.md b/bindings/rust/bench/README.md
index 27ccc15b372..98926ebbbca 100644
--- a/bindings/rust/bench/README.md
+++ b/bindings/rust/bench/README.md
@@ -45,7 +45,7 @@ To generate flamegraphs, run `cargo bench --bench handshake --bench throughput -
## Memory benchmarks
-To run all memory benchmarks, run `memory/bench-memory.sh`. Graphs of memory usage will be generated in `images/`.
+To run all memory benchmarks, run `scripts/bench-memory.sh`. Graphs of memory usage will be generated in `images/`.
Memory benchmark data is generated using the `memory` binary. Command line arguments can be given to `cargo run` or to the built executable located at `target/release/memory`. The usage is as follows:
@@ -88,7 +88,7 @@ Notes:
- Two sets of parameters for the handshake couldn't be benched before 1.3.40, since security policies that negotiated those policies as their top choice did not exist before then.
- There is no data from 1.3.30 to 1.3.37 because those versions have a dependency issue that cause the Rust bindings not to build. However, there is data before and after that period, so the performance for those versions can be inferred via interpolation.
- The improvement in throughput in 1.3.28 was most likely caused by the addition of LTO to the default Rust bindings build.
-- Since the benches are run over a long time, noise on the machine can cause variability, as seen in the throughput graph.
+- Since the benches are run over a long time, noise on the machine can cause variability, and background processes can cause spikes.
- The variability can be seen with throughput especially because it is calculated as the inverse of time taken.
![historical-perf-handshake](images/historical-perf-handshake.svg)
diff --git a/bindings/rust/bench/images/historical-perf-handshake.svg b/bindings/rust/bench/images/historical-perf-handshake.svg
index 79ac67a2507..06f65b30056 100644
--- a/bindings/rust/bench/images/historical-perf-handshake.svg
+++ b/bindings/rust/bench/images/historical-perf-handshake.svg
@@ -4,421 +4,944 @@
Performance of handshake by version since Jun 2022
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Time
Version
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
0 ms
-
+
2 ms
-
-
+
+
4 ms
-
-
+
+
6 ms
-
+
+
+8 ms
+
+
-
+
1.3.16
-
-
+
+
1.3.18
-
-
+
+
1.3.20
-
-
+
+
1.3.22
-
-
+
+
1.3.24
-
-
+
+
1.3.26
-
-
+
+
1.3.28
-
-
+
+
1.3.30
-
-
+
+
1.3.32
-
-
+
+
1.3.34
-
-
+
+
1.3.36
-
-
+
+
1.3.38
-
-
+
+
1.3.40
-
-
+
+
1.3.42
-
-
+
+
1.3.44
-
-
+
+
1.3.46
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-handshake-MutualAuth-SECP256R1
-
-
-handshake-ServerAuth-X25519
-
-
-handshake-MutualAuth-X25519
-
-
-handshake-ServerAuth-SECP256R1
-
-
-
-
-
+
+
+1.3.48
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+handshake-x25519
+
+
+handshake-no-mTLS
+
+
+handshake-rsa2048
+
+
+handshake-rsa4096
+
+
+handshake-rsa3072
+
+
+handshake-secp256r1
+
+
+handshake-mTLS
+
+
+handshake-ecdsa384
+
+
+
+
+
+
+
+
+
diff --git a/bindings/rust/bench/images/historical-perf-throughput.svg b/bindings/rust/bench/images/historical-perf-throughput.svg
index 46aeb36c475..a27ad4e054c 100644
--- a/bindings/rust/bench/images/historical-perf-throughput.svg
+++ b/bindings/rust/bench/images/historical-perf-throughput.svg
@@ -1,341 +1,383 @@
diff --git a/bindings/rust/s2n-tls-sys/templates/Cargo.template b/bindings/rust/s2n-tls-sys/templates/Cargo.template
index 55f8cfef955..ae6cd88427c 100644
--- a/bindings/rust/s2n-tls-sys/templates/Cargo.template
+++ b/bindings/rust/s2n-tls-sys/templates/Cargo.template
@@ -1,7 +1,7 @@
[package]
name = "s2n-tls-sys"
description = "A C99 implementation of the TLS/SSL protocols"
-version = "0.0.35"
+version = "0.0.36"
authors = ["AWS s2n"]
edition = "2021"
rust-version = "1.63.0"
diff --git a/bindings/rust/s2n-tls-tokio/Cargo.toml b/bindings/rust/s2n-tls-tokio/Cargo.toml
index 145e6a05de6..4418291b9a3 100644
--- a/bindings/rust/s2n-tls-tokio/Cargo.toml
+++ b/bindings/rust/s2n-tls-tokio/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "s2n-tls-tokio"
description = "An implementation of TLS streams for Tokio built on top of s2n-tls"
-version = "0.0.35"
+version = "0.0.36"
authors = ["AWS s2n"]
edition = "2021"
rust-version = "1.63.0"
@@ -15,7 +15,7 @@ default = []
errno = { version = "0.3" }
libc = { version = "0.2" }
pin-project-lite = { version = "0.2" }
-s2n-tls = { version = "=0.0.35", path = "../s2n-tls" }
+s2n-tls = { version = "=0.0.36", path = "../s2n-tls" }
tokio = { version = "1", features = ["net", "time"] }
[dev-dependencies]
diff --git a/bindings/rust/s2n-tls/Cargo.toml b/bindings/rust/s2n-tls/Cargo.toml
index 45bd4f574c9..956a0d4c914 100644
--- a/bindings/rust/s2n-tls/Cargo.toml
+++ b/bindings/rust/s2n-tls/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "s2n-tls"
description = "A C99 implementation of the TLS/SSL protocols"
-version = "0.0.35"
+version = "0.0.36"
authors = ["AWS s2n"]
edition = "2021"
rust-version = "1.63.0"
@@ -19,7 +19,7 @@ testing = ["bytes"]
bytes = { version = "1", optional = true }
errno = { version = "0.3" }
libc = "0.2"
-s2n-tls-sys = { version = "=0.0.35", path = "../s2n-tls-sys", features = ["internal"] }
+s2n-tls-sys = { version = "=0.0.36", path = "../s2n-tls-sys", features = ["internal"] }
pin-project-lite = "0.2"
hex = "0.4"
diff --git a/bindings/rust/s2n-tls/src/config.rs b/bindings/rust/s2n-tls/src/config.rs
index 05c1078e101..b3c2c767ea8 100644
--- a/bindings/rust/s2n-tls/src/config.rs
+++ b/bindings/rust/s2n-tls/src/config.rs
@@ -660,6 +660,7 @@ impl Builder {
if key_len < 16 {
return Err(Error::INVALID_INPUT);
}
+ self.enable_session_tickets(true)?;
unsafe {
s2n_config_add_ticket_crypto_key(
self.as_mut_ptr(),
@@ -672,7 +673,6 @@ impl Builder {
)
.into_result()
}?;
- self.enable_session_tickets(true)?;
Ok(self)
}
diff --git a/bindings/rust/s2n-tls/src/testing/resumption.rs b/bindings/rust/s2n-tls/src/testing/resumption.rs
index c07ffe117ef..f8eb8c0d10c 100644
--- a/bindings/rust/s2n-tls/src/testing/resumption.rs
+++ b/bindings/rust/s2n-tls/src/testing/resumption.rs
@@ -43,7 +43,6 @@ mod tests {
// Initialize config for server with a ticket key
let mut server_config_builder = Builder::new();
server_config_builder
- .enable_session_tickets(true)?
.add_session_ticket_key(&KEYNAME, &KEY, SystemTime::now())?
.load_pem(keypair.cert(), keypair.key())?;
let server_config = server_config_builder.build()?;
@@ -123,7 +122,6 @@ mod tests {
// Initialize config for server with a ticket key
let mut server_config_builder = Builder::new();
server_config_builder
- .enable_session_tickets(true)?
.add_session_ticket_key(&KEYNAME, &KEY, SystemTime::now())?
.load_pem(keypair.cert(), keypair.key())?
.set_security_policy(&security::DEFAULT_TLS13)?;
diff --git a/error/s2n_errno.c b/error/s2n_errno.c
index 2fed1cb7eec..fa20836c54d 100644
--- a/error/s2n_errno.c
+++ b/error/s2n_errno.c
@@ -296,6 +296,7 @@ static const char *no_such_error = "Internal s2n error";
ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_CONN, "kTLS is unsupported for this connection") \
ERR_ENTRY(S2N_ERR_KTLS_ULP, "An error occurred when attempting to configure the socket for kTLS. Ensure the 'tls' kernel module is enabled.") \
ERR_ENTRY(S2N_ERR_KTLS_ENABLE_CRYPTO, "An error occurred when attempting to enable kTLS on socket.") \
+ ERR_ENTRY(S2N_ERR_KTLS_BAD_CMSG, "Error handling cmsghdr.") \
ERR_ENTRY(S2N_ERR_ATOMIC, "Atomic operations in this environment would require locking") \
/* clang-format on */
diff --git a/error/s2n_errno.h b/error/s2n_errno.h
index 8558729248c..71e032eaa09 100644
--- a/error/s2n_errno.h
+++ b/error/s2n_errno.h
@@ -312,6 +312,7 @@ typedef enum {
S2N_ERR_KTLS_UNSUPPORTED_CONN,
S2N_ERR_KTLS_ULP,
S2N_ERR_KTLS_ENABLE_CRYPTO,
+ S2N_ERR_KTLS_BAD_CMSG,
S2N_ERR_ATOMIC,
S2N_ERR_T_USAGE_END,
} s2n_error;
diff --git a/nix/shell.sh b/nix/shell.sh
index b828a482f89..14acd88a4aa 100644
--- a/nix/shell.sh
+++ b/nix/shell.sh
@@ -62,7 +62,7 @@ function integ {
ctest --test-dir ./build -L integrationv2 --no-tests=error --output-on-failure -R "$test" --verbose
if [ "$?" -ne 0 ]; then
echo "Test failed, stopping execution"
- exit 1
+ return 1
fi
done
fi
diff --git a/tests/features/S2N_KTLS_SUPPORTED.c b/tests/features/S2N_KTLS_SUPPORTED.c
index 6cb2789d134..b779a832dbc 100644
--- a/tests/features/S2N_KTLS_SUPPORTED.c
+++ b/tests/features/S2N_KTLS_SUPPORTED.c
@@ -26,5 +26,8 @@ int main()
struct tls12_crypto_info_aes_gcm_128 aes_crypto_info;
struct tls_crypto_info crypto_info;
+ int get_record_type = TLS_GET_RECORD_TYPE;
+ int set_record_type = TLS_SET_RECORD_TYPE;
+
return 0;
}
diff --git a/tests/testlib/s2n_ktls_test_utils.c b/tests/testlib/s2n_ktls_test_utils.c
index 2b2a523f841..6df323e4c27 100644
--- a/tests/testlib/s2n_ktls_test_utils.c
+++ b/tests/testlib/s2n_ktls_test_utils.c
@@ -15,9 +15,14 @@
#include "testlib/s2n_ktls_test_utils.h"
+S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size,
+ int cmsg_type, uint8_t record_type);
+S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type);
+
/* Since it is possible to read partial data, we need a way to update the length
* of the previous record for the mock stuffer IO implementation. */
-static S2N_RESULT s2n_test_ktls_update_prev_header_len(struct s2n_test_ktls_io_stuffer *io_ctx, uint16_t remaining_len)
+static S2N_RESULT s2n_test_ktls_update_prev_header_len(struct s2n_test_ktls_io_stuffer *io_ctx,
+ uint16_t remaining_len)
{
RESULT_ENSURE_REF(io_ctx);
RESULT_ENSURE(remaining_len > 0, S2N_ERR_IO);
@@ -38,19 +43,17 @@ static S2N_RESULT s2n_test_ktls_update_prev_header_len(struct s2n_test_ktls_io_s
ssize_t s2n_test_ktls_sendmsg_io_stuffer(void *io_context, const struct msghdr *msg)
{
- POSIX_ENSURE_REF(io_context);
POSIX_ENSURE_REF(msg);
POSIX_ENSURE_REF(msg->msg_iov);
- /* Assuming msg_control is uint8_t is a simplification and will not work when we
- * attempt to test the production s2n_ktls_send implementation. However, setting/parsing
- * cmsg is critical code and will be added in a separate PR. */
- uint8_t *record_type = (uint8_t *) msg->msg_control;
- POSIX_ENSURE_REF(record_type);
struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context;
POSIX_ENSURE_REF(io_ctx);
io_ctx->sendmsg_invoked_count++;
+ uint8_t record_type = 0;
+ struct msghdr msg_to_parse = *msg;
+ POSIX_GUARD_RESULT(s2n_ktls_get_control_data(&msg_to_parse, S2N_TLS_SET_RECORD_TYPE, &record_type));
+
size_t total_len = 0;
for (size_t count = 0; count < msg->msg_iovlen; count++) {
uint8_t *buf = msg->msg_iov[count].iov_base;
@@ -73,7 +76,7 @@ ssize_t s2n_test_ktls_sendmsg_io_stuffer(void *io_context, const struct msghdr *
}
if (total_len) {
/* write record_type and len after some data was written successfully */
- POSIX_GUARD(s2n_stuffer_write_uint8(&io_ctx->ancillary_buffer, *record_type));
+ POSIX_GUARD(s2n_stuffer_write_uint8(&io_ctx->ancillary_buffer, record_type));
POSIX_GUARD(s2n_stuffer_write_uint16(&io_ctx->ancillary_buffer, total_len));
}
@@ -87,18 +90,13 @@ ssize_t s2n_test_ktls_sendmsg_io_stuffer(void *io_context, const struct msghdr *
* are of the same type. */
ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg)
{
- POSIX_ENSURE_REF(io_context);
POSIX_ENSURE_REF(msg);
POSIX_ENSURE_REF(msg->msg_iov);
- /* Assuming msg_control is uint8_t is a simplification and will not work when we
- * attempt to test the production s2n_ktls_recv implementation. However, setting/parsing
- * cmsg is critical code and will be added in a separate PR. */
- uint8_t *record_type = (uint8_t *) msg->msg_control;
- POSIX_ENSURE_REF(record_type);
struct s2n_test_ktls_io_stuffer *io_ctx = (struct s2n_test_ktls_io_stuffer *) io_context;
POSIX_ENSURE_REF(io_ctx);
io_ctx->recvmsg_invoked_count++;
+
uint8_t *buf = msg->msg_iov->iov_base;
POSIX_ENSURE_REF(buf);
@@ -112,10 +110,13 @@ ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg)
POSIX_ENSURE_EQ(msg->msg_iovlen, 1);
size_t size = msg->msg_iov->iov_len;
+ uint8_t record_type = 0;
+ POSIX_GUARD(s2n_stuffer_read_uint8(&io_ctx->ancillary_buffer, &record_type));
+ POSIX_GUARD_RESULT(s2n_ktls_set_control_data(msg, msg->msg_control, msg->msg_controllen,
+ S2N_TLS_GET_RECORD_TYPE, record_type));
+
ssize_t bytes_read = 0;
while (bytes_read < size) {
- /* read record_type and number of bytes available in the next record */
- POSIX_GUARD(s2n_stuffer_read_uint8(&io_ctx->ancillary_buffer, record_type));
uint16_t n_avail = 0;
POSIX_GUARD(s2n_stuffer_read_uint16(&io_ctx->ancillary_buffer, &n_avail));
@@ -129,6 +130,7 @@ ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg)
ssize_t remaining_len = n_avail - n_read;
if (remaining_len) {
POSIX_GUARD_RESULT(s2n_test_ktls_update_prev_header_len(io_ctx, remaining_len));
+ break;
}
/* attempt to read multiple records (must be of the same type) */
@@ -138,17 +140,20 @@ ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg)
if (no_more_records) {
break;
}
- bool next_record_different_type = next_record_type != *record_type;
+
+ bool next_record_different_type = next_record_type != record_type;
if (next_record_different_type) {
break;
}
+
+ POSIX_GUARD(s2n_stuffer_skip_read(&io_ctx->ancillary_buffer, sizeof(record_type)));
}
return bytes_read;
}
-S2N_RESULT s2n_test_init_ktls_io_stuffer(struct s2n_connection *server, struct s2n_connection *client,
- struct s2n_test_ktls_io_stuffer_pair *io_pair)
+S2N_RESULT s2n_test_init_ktls_io_stuffer(struct s2n_connection *server,
+ struct s2n_connection *client, struct s2n_test_ktls_io_stuffer_pair *io_pair)
{
RESULT_ENSURE_REF(server);
RESULT_ENSURE_REF(client);
diff --git a/tests/testlib/s2n_ktls_test_utils.h b/tests/testlib/s2n_ktls_test_utils.h
index d20f1268696..b06373eda1c 100644
--- a/tests/testlib/s2n_ktls_test_utils.h
+++ b/tests/testlib/s2n_ktls_test_utils.h
@@ -62,6 +62,6 @@ struct s2n_test_ktls_io_stuffer_pair {
ssize_t s2n_test_ktls_sendmsg_io_stuffer(void *io_context, const struct msghdr *msg);
ssize_t s2n_test_ktls_recvmsg_io_stuffer(void *io_context, struct msghdr *msg);
-S2N_RESULT s2n_test_init_ktls_io_stuffer(struct s2n_connection *server, struct s2n_connection *client,
- struct s2n_test_ktls_io_stuffer_pair *io_pair);
+S2N_RESULT s2n_test_init_ktls_io_stuffer(struct s2n_connection *server,
+ struct s2n_connection *client, struct s2n_test_ktls_io_stuffer_pair *io_pair);
S2N_CLEANUP_RESULT s2n_ktls_io_stuffer_pair_free(struct s2n_test_ktls_io_stuffer_pair *pair);
diff --git a/tests/unit/s2n_ktls_io_test.c b/tests/unit/s2n_ktls_io_test.c
new file mode 100644
index 00000000000..c8ba335e0a8
--- /dev/null
+++ b/tests/unit/s2n_ktls_io_test.c
@@ -0,0 +1,78 @@
+/*
+ * 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 "s2n_test.h"
+#include "testlib/s2n_testlib.h"
+#include "tls/s2n_ktls.h"
+
+S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size,
+ int cmsg_type, uint8_t record_type);
+S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type);
+
+int main(int argc, char **argv)
+{
+ BEGIN_TEST();
+
+ /* Test s2n_ktls_set_control_data and s2n_ktls_get_control_data */
+ {
+ /* Test: Safety */
+ {
+ struct msghdr msg = { 0 };
+ char buf[100] = { 0 };
+ EXPECT_ERROR_WITH_ERRNO(s2n_ktls_set_control_data(NULL, buf, sizeof(buf), 0, 0),
+ S2N_ERR_NULL);
+ EXPECT_ERROR_WITH_ERRNO(s2n_ktls_set_control_data(&msg, NULL, sizeof(buf), 0, 0),
+ S2N_ERR_NULL);
+ EXPECT_ERROR_WITH_ERRNO(s2n_ktls_set_control_data(&msg, buf, 0, 0, 0),
+ S2N_ERR_NULL);
+
+ uint8_t record_type = 0;
+ EXPECT_ERROR_WITH_ERRNO(s2n_ktls_get_control_data(NULL, 0, &record_type),
+ S2N_ERR_NULL);
+ EXPECT_ERROR_WITH_ERRNO(s2n_ktls_get_control_data(&msg, 0, NULL),
+ S2N_ERR_NULL);
+ };
+
+ /* Test: s2n_ktls_set_control_data msg is parseable by s2n_ktls_get_control_data */
+ {
+ const uint8_t set_record_type = 5;
+ struct msghdr msg = { 0 };
+ const int cmsg_type = 11;
+ char buf[100] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&msg, buf, sizeof(buf), cmsg_type, set_record_type));
+
+ uint8_t get_record_type = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&msg, cmsg_type, &get_record_type));
+
+ EXPECT_EQUAL(set_record_type, get_record_type);
+ };
+
+ /* Test: s2n_ktls_get_control_data fails with unexpected cmsg_type */
+ {
+ const uint8_t set_record_type = 5;
+ struct msghdr msg = { 0 };
+ const int cmsg_type = 11;
+ char buf[100] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&msg, buf, sizeof(buf), cmsg_type, set_record_type));
+
+ const int bad_cmsg_type = 99;
+ uint8_t get_record_type = 0;
+ EXPECT_ERROR_WITH_ERRNO(s2n_ktls_get_control_data(&msg, bad_cmsg_type, &get_record_type),
+ S2N_ERR_KTLS_BAD_CMSG);
+ };
+ };
+
+ END_TEST();
+}
diff --git a/tests/unit/s2n_ktls_test_utils_test.c b/tests/unit/s2n_ktls_test_utils_test.c
index d3fa32da215..6be94a37608 100644
--- a/tests/unit/s2n_ktls_test_utils_test.c
+++ b/tests/unit/s2n_ktls_test_utils_test.c
@@ -19,7 +19,13 @@
#include "testlib/s2n_testlib.h"
#include "utils/s2n_random.h"
-#define S2N_TEST_TO_SEND 10
+#define S2N_TEST_TO_SEND 10
+#define S2N_CONTROL_BUF_SIZE 100
+#define S2N_TEST_MSG_IOVLEN 5
+
+S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size,
+ int cmsg_type, uint8_t record_type);
+S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type);
S2N_RESULT s2n_test_validate_data(struct s2n_test_ktls_io_stuffer *ktls_io, uint8_t *expected_data, uint16_t expected_len)
{
@@ -74,8 +80,10 @@ int main(int argc, char **argv)
EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair));
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND);
@@ -98,8 +106,10 @@ int main(int argc, char **argv)
EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair));
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(client->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND);
@@ -123,8 +133,10 @@ int main(int argc, char **argv)
size_t send_zero = 0;
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = send_zero };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, send_zero);
@@ -134,7 +146,7 @@ int main(int argc, char **argv)
EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1);
};
- /* Send iov_len > 1 */
+ /* Send msg_iovlen > 1 */
{
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
@@ -144,19 +156,18 @@ int main(int argc, char **argv)
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair));
- uint8_t count = 5;
size_t total_sent = 0;
- struct iovec *send_msg_iov = NULL;
- send_msg_iov = malloc(sizeof(*send_msg_iov) * count);
- for (size_t i = 0; i < count; i++) {
+ struct iovec send_msg_iov[sizeof(struct iovec) * S2N_TEST_MSG_IOVLEN] = { 0 };
+ for (size_t i = 0; i < S2N_TEST_MSG_IOVLEN; i++) {
send_msg_iov[i].iov_base = test_data + total_sent;
send_msg_iov[i].iov_len = S2N_TEST_TO_SEND;
total_sent += S2N_TEST_TO_SEND;
}
- struct msghdr send_msg = { .msg_iov = send_msg_iov, .msg_iovlen = count, .msg_control = &test_record_type };
-
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = send_msg_iov, .msg_iovlen = S2N_TEST_MSG_IOVLEN };
+ char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, total_sent);
@@ -166,8 +177,6 @@ int main(int argc, char **argv)
/* validate only 1 record was sent */
EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), S2N_TEST_KTLS_MOCK_HEADER_SIZE);
EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1);
-
- free(send_msg_iov);
};
/* Send multiple records of same type */
@@ -182,13 +191,16 @@ int main(int argc, char **argv)
size_t records_to_send = 5;
struct iovec send_msg_iov = { .iov_len = S2N_TEST_TO_SEND };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+
size_t total_sent = 0;
for (size_t i = 0; i < records_to_send; i++) {
/* increment test data ptr */
send_msg_iov.iov_base = test_data + total_sent;
- /* sendmsg */
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND);
total_sent += bytes_written;
@@ -221,13 +233,15 @@ int main(int argc, char **argv)
size_t records_to_send = 5;
struct iovec send_msg_iov = { .iov_len = S2N_TEST_TO_SEND };
struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+
size_t total_sent = 0;
for (size_t i = 0; i < records_to_send; i++) {
/* increment test data ptr */
send_msg_iov.iov_base = test_data + total_sent;
- send_msg.msg_control = &i;
- /* sendmsg */
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf),
+ S2N_TLS_SET_RECORD_TYPE, i));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND);
total_sent += bytes_written;
@@ -261,9 +275,14 @@ int main(int argc, char **argv)
size_t to_send = 1;
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+
/* attempt sendmsg and expect EAGAIN */
- for (size_t i = 0; i < 5; i++) {
+ size_t blocked_invoked_count = 5;
+ for (size_t i = 0; i < blocked_invoked_count; i++) {
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
EXPECT_EQUAL(s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg), S2N_FAILURE);
EXPECT_EQUAL(errno, EAGAIN);
}
@@ -281,7 +300,7 @@ int main(int argc, char **argv)
/* validate only 1 record was sent */
EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), S2N_TEST_KTLS_MOCK_HEADER_SIZE);
- EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 6);
+ EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, blocked_invoked_count + 1);
};
/* Attempt partial write with iov_len > 1 and expect error */
@@ -297,25 +316,23 @@ int main(int argc, char **argv)
io_pair.client_in.data_buffer.growable = false;
EXPECT_SUCCESS(s2n_stuffer_alloc(&io_pair.client_in.data_buffer, S2N_TEST_TO_SEND));
- uint8_t count = 2;
- struct iovec *send_msg_iov = NULL;
- send_msg_iov = malloc(sizeof(*send_msg_iov) * count);
uint8_t *test_data_ptr = test_data;
- for (size_t i = 0; i < count; i++) {
+ struct iovec send_msg_iov[sizeof(struct iovec) * S2N_TEST_MSG_IOVLEN] = { 0 };
+ for (size_t i = 0; i < S2N_TEST_MSG_IOVLEN; i++) {
send_msg_iov[i].iov_base = (void *) test_data_ptr;
send_msg_iov[i].iov_len = S2N_TEST_TO_SEND;
test_data_ptr += S2N_TEST_TO_SEND;
}
- struct msghdr send_msg = { .msg_iov = send_msg_iov, .msg_iovlen = count, .msg_control = &test_record_type };
-
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = send_msg_iov, .msg_iovlen = S2N_TEST_MSG_IOVLEN };
+ char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
EXPECT_FAILURE_WITH_ERRNO(s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg),
S2N_ERR_SAFETY);
/* validate no record were sent */
EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), 0);
EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1);
- free(send_msg_iov);
};
};
@@ -332,20 +349,28 @@ int main(int argc, char **argv)
EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair));
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, to_send);
uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 };
- uint8_t recv_record_type = 0;
+ char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_send };
- struct msghdr recv_msg = { .msg_iov = &recv_msg_iov, .msg_iovlen = 1, .msg_control = &recv_record_type };
- /* recvmsg */
+ struct msghdr recv_msg = {
+ .msg_iov = &recv_msg_iov,
+ .msg_iovlen = 1,
+ .msg_control = recv_ctrl_buf,
+ .msg_controllen = sizeof(recv_ctrl_buf),
+ };
ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg);
EXPECT_EQUAL(bytes_read, to_send);
/* confirm read data */
EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_send);
+ uint8_t recv_record_type = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type));
EXPECT_EQUAL(recv_record_type, test_record_type);
EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1);
@@ -364,16 +389,23 @@ int main(int argc, char **argv)
size_t to_send = 1;
uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 };
- uint8_t recv_record_type = 0;
struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_send };
- struct msghdr recv_msg = { .msg_iov = &recv_msg_iov, .msg_iovlen = 1, .msg_control = &recv_record_type };
+ char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ struct msghdr recv_msg = {
+ .msg_iov = &recv_msg_iov,
+ .msg_iovlen = 1,
+ .msg_control = recv_ctrl_buf,
+ .msg_controllen = sizeof(recv_ctrl_buf),
+ };
/* attempting to recv data when nothing has been sent blocks */
EXPECT_EQUAL(s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg), S2N_FAILURE);
EXPECT_EQUAL(errno, EAGAIN);
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, to_send);
@@ -383,16 +415,19 @@ int main(int argc, char **argv)
EXPECT_EQUAL(bytes_read, to_send);
/* confirm read data */
EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_send);
+ uint8_t recv_record_type = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type));
EXPECT_EQUAL(recv_record_type, test_record_type);
- for (size_t i = 0; i < 5; i++) {
+ size_t blocked_invoked_count = 5;
+ for (size_t i = 0; i < blocked_invoked_count; i++) {
/* attempting to recv more data blocks */
EXPECT_EQUAL(s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg), S2N_FAILURE);
EXPECT_EQUAL(errno, EAGAIN);
}
EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1);
- EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, 7);
+ EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, blocked_invoked_count + 2);
};
/* Read partial data: request < total sent */
@@ -410,21 +445,29 @@ int main(int argc, char **argv)
size_t remaining_len = to_send - to_recv;
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, to_send);
uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 };
- uint8_t recv_record_type = 0;
+ char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_recv };
- struct msghdr recv_msg = { .msg_iov = &recv_msg_iov, .msg_iovlen = 1, .msg_control = &recv_record_type };
- /* recvmsg */
+ struct msghdr recv_msg = {
+ .msg_iov = &recv_msg_iov,
+ .msg_iovlen = 1,
+ .msg_control = recv_ctrl_buf,
+ .msg_controllen = sizeof(recv_ctrl_buf),
+ };
ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg);
EXPECT_EQUAL(bytes_read, to_recv);
/* confirm read data */
EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_recv);
- EXPECT_EQUAL(recv_record_type, test_record_type);
+ uint8_t recv_record_type_1 = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type_1));
+ EXPECT_EQUAL(recv_record_type_1, test_record_type);
/* confirm that a single records still exists; data len is updated on partial reads */
EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), S2N_TEST_KTLS_MOCK_HEADER_SIZE);
@@ -434,7 +477,10 @@ int main(int argc, char **argv)
recv_msg_iov.iov_len = remaining_len;
bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg);
EXPECT_EQUAL(bytes_read, remaining_len);
- EXPECT_EQUAL(recv_record_type, test_record_type);
+ /* confirm read data */
+ uint8_t recv_record_type_2 = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type_2));
+ EXPECT_EQUAL(recv_record_type_2, test_record_type);
/* validate all sent/recv data */
EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_send);
@@ -459,21 +505,29 @@ int main(int argc, char **argv)
size_t to_recv = 15;
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
- /* sendmsg */
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, to_send);
uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 };
- uint8_t recv_record_type = 0;
struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_recv };
- struct msghdr recv_msg = { .msg_iov = &recv_msg_iov, .msg_iovlen = 1, .msg_control = &recv_record_type };
- /* recvmsg */
+ char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ struct msghdr recv_msg = {
+ .msg_iov = &recv_msg_iov,
+ .msg_iovlen = 1,
+ .msg_control = recv_ctrl_buf,
+ .msg_controllen = sizeof(recv_ctrl_buf),
+ };
ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg);
/* confirm read data: minimum of sent and requested (to_send) */
EXPECT_EQUAL(bytes_read, to_send);
EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_send);
+ uint8_t recv_record_type = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type));
EXPECT_EQUAL(recv_record_type, test_record_type);
EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1);
@@ -496,25 +550,33 @@ int main(int argc, char **argv)
size_t to_recv = 10;
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send };
- struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1, .msg_control = &test_record_type };
+ struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf),
+ S2N_TLS_SET_RECORD_TYPE, test_record_type));
size_t total_sent = 0;
for (size_t i = 0; i < records_to_send; i++) {
/* increment test data ptr */
send_msg_iov.iov_base = test_data + total_sent;
- /* sendmsg */
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, to_send);
total_sent += bytes_written;
}
uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 };
- uint8_t recv_record_type = 0;
+ char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_recv };
- struct msghdr recv_msg = { .msg_iov = &recv_msg_iov, .msg_iovlen = 1, .msg_control = &recv_record_type };
- /* recvmsg */
+ struct msghdr recv_msg = {
+ .msg_iov = &recv_msg_iov,
+ .msg_iovlen = 1,
+ .msg_control = recv_ctrl_buf,
+ .msg_controllen = sizeof(recv_ctrl_buf),
+ };
ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg);
EXPECT_EQUAL(bytes_read, to_recv);
+ uint8_t recv_record_type = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type));
EXPECT_EQUAL(recv_record_type, test_record_type);
/* validate all data was received */
@@ -545,32 +607,44 @@ int main(int argc, char **argv)
struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send };
struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 };
+ char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
/* sendmsg record_type_1 */
- send_msg.msg_control = &record_type_1;
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf),
+ S2N_TLS_SET_RECORD_TYPE, record_type_1));
ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, to_send);
total_sent += bytes_written;
/* sendmsg record_type_2 */
- send_msg.msg_control = &record_type_2;
+ EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf),
+ S2N_TLS_SET_RECORD_TYPE, record_type_2));
send_msg_iov.iov_base = test_data + total_sent;
bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg);
EXPECT_EQUAL(bytes_written, to_send);
total_sent += bytes_written;
uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 };
- uint8_t recv_record_type = 0;
+ char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 };
struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_recv };
- struct msghdr recv_msg = { .msg_iov = &recv_msg_iov, .msg_iovlen = 1, .msg_control = &recv_record_type };
+ struct msghdr recv_msg = {
+ .msg_iov = &recv_msg_iov,
+ .msg_iovlen = 1,
+ .msg_control = recv_ctrl_buf,
+ .msg_controllen = sizeof(recv_ctrl_buf),
+ };
/* only recv record_type_1 even though we request more data */
ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg);
EXPECT_EQUAL(bytes_read, to_send);
- EXPECT_EQUAL(recv_record_type, record_type_1);
+ uint8_t recv_record_type_1 = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type_1));
+ EXPECT_EQUAL(recv_record_type_1, record_type_1);
total_recv += bytes_read;
/* only recv record_type_2; which is all that remains */
recv_msg_iov.iov_base = recv_buffer + bytes_read;
bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg);
EXPECT_EQUAL(bytes_read, to_send);
- EXPECT_EQUAL(recv_record_type, record_type_2);
+ uint8_t recv_record_type_2 = 0;
+ EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type_2));
+ EXPECT_EQUAL(recv_record_type_2, record_type_2);
total_recv += bytes_read;
/* validate all data was received (we offset the test_data/recv_buffer so the
diff --git a/tests/unit/s2n_self_talk_tls13_test.c b/tests/unit/s2n_self_talk_tls13_test.c
index 9a311f82fe3..94e8e3b1b75 100644
--- a/tests/unit/s2n_self_talk_tls13_test.c
+++ b/tests/unit/s2n_self_talk_tls13_test.c
@@ -138,6 +138,7 @@ int main(int argc, char **argv)
/* Negotiate the handshake. */
EXPECT_SUCCESS(s2n_negotiate(conn, &blocked));
+ EXPECT_NOT_NULL(s2n_connection_get_client_hello(conn));
EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version());
char buffer[0xffff];
diff --git a/tests/unit/s2n_server_hello_retry_test.c b/tests/unit/s2n_server_hello_retry_test.c
index 0df4264dff3..ae44a31104c 100644
--- a/tests/unit/s2n_server_hello_retry_test.c
+++ b/tests/unit/s2n_server_hello_retry_test.c
@@ -507,6 +507,7 @@ int main(int argc, char **argv)
EXPECT_TRUE(server_conn->handshake.handshake_type & HELLO_RETRY_REQUEST);
EXPECT_EQUAL(client_hello_ctx.invocations, 1);
+ EXPECT_NOT_NULL(s2n_connection_get_client_hello(server_conn));
EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn));
EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn));
diff --git a/tls/s2n_client_hello.c b/tls/s2n_client_hello.c
index a2391027db1..dacfc0b9d88 100644
--- a/tls/s2n_client_hello.c
+++ b/tls/s2n_client_hello.c
@@ -43,7 +43,7 @@
struct s2n_client_hello *s2n_connection_get_client_hello(struct s2n_connection *conn)
{
- if (conn->client_hello.callback_invoked != 1) {
+ if (conn->client_hello.parsed != 1) {
return NULL;
}
@@ -638,6 +638,7 @@ int s2n_client_hello_recv(struct s2n_connection *conn)
/* Only parse the ClientHello once */
if (!conn->client_hello.parsed) {
POSIX_GUARD(s2n_parse_client_hello(conn));
+ /* Mark the collected client hello as available when parsing is done and before the client hello callback */
conn->client_hello.parsed = true;
}
@@ -647,7 +648,7 @@ int s2n_client_hello_recv(struct s2n_connection *conn)
* callback state may have been cleared while parsing the second ClientHello.
*/
if (!conn->client_hello.callback_invoked && !IS_HELLO_RETRY_HANDSHAKE(conn)) {
- /* Mark the collected client hello as available when parsing is done and before the client hello callback */
+ /* Mark the client hello callback as invoked to avoid calling it again. */
conn->client_hello.callback_invoked = true;
/* Call client_hello_cb if exists, letting application to modify s2n_connection or swap s2n_config */
diff --git a/tls/s2n_ktls.h b/tls/s2n_ktls.h
index 3e4b7809adb..e8d092433d4 100644
--- a/tls/s2n_ktls.h
+++ b/tls/s2n_ktls.h
@@ -36,20 +36,21 @@ typedef enum {
S2N_KTLS_MODE_RECV,
} s2n_ktls_mode;
-/* Used for overriding setsockopt calls in testing */
-typedef int (*s2n_setsockopt_fn)(int socket, int level, int option_name,
- const void *option_value, socklen_t option_len);
-S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb);
-
bool s2n_ktls_is_supported_on_platform();
-S2N_RESULT s2n_ktls_get_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd);
+S2N_RESULT s2n_ktls_get_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode,
+ int *fd);
/* These functions will be part of the public API. */
int s2n_connection_ktls_enable_send(struct s2n_connection *conn);
int s2n_connection_ktls_enable_recv(struct s2n_connection *conn);
/* Testing */
+typedef int (*s2n_setsockopt_fn)(int socket, int level, int option_name, const void *option_value,
+ socklen_t option_len);
+S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb);
typedef ssize_t (*s2n_ktls_sendmsg_fn)(void *io_context, const struct msghdr *msg);
typedef ssize_t (*s2n_ktls_recvmsg_fn)(void *io_context, struct msghdr *msg);
-S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, void *send_ctx);
-S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, void *recv_ctx);
+S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb,
+ void *send_ctx);
+S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb,
+ void *recv_ctx);
diff --git a/tls/s2n_ktls_io.c b/tls/s2n_ktls_io.c
index 16ef4ee7edb..c4b02a88e7a 100644
--- a/tls/s2n_ktls_io.c
+++ b/tls/s2n_ktls_io.c
@@ -13,16 +13,35 @@
* permissions and limitations under the License.
*/
+#if defined(__FreeBSD__) || defined(__APPLE__)
+ /* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html
+ * The POSIX standard does not define the CMSG_LEN and CMSG_SPACE macros. FreeBSD
+ * and APPLE check and disable these macros if the _POSIX_C_SOURCE flag is set.
+ *
+ * Since s2n-tls already unsets the _POSIX_C_SOURCE in other files and is not
+ * POSIX compliant, we continue the pattern here.
+ */
+ #undef _POSIX_C_SOURCE
+#endif
+#include
+
+#include "error/s2n_errno.h"
#include "tls/s2n_ktls.h"
+#include "utils/s2n_result.h"
+#include "utils/s2n_safety.h"
#include "utils/s2n_socket.h"
+/* record_type is of type uint8_t */
+#define S2N_KTLS_RECORD_TYPE_SIZE (sizeof(uint8_t))
+
/* Used to override sendmsg and recvmsg for testing. */
static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg);
static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg);
s2n_ktls_sendmsg_fn s2n_sendmsg_fn = s2n_ktls_default_sendmsg;
s2n_ktls_recvmsg_fn s2n_recvmsg_fn = s2n_ktls_default_recvmsg;
-S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb, void *send_ctx)
+S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb,
+ void *send_ctx)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(send_ctx);
@@ -32,7 +51,8 @@ S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg
return S2N_RESULT_OK;
}
-S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb, void *recv_ctx)
+S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb,
+ void *recv_ctx)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(recv_ctx);
@@ -65,3 +85,92 @@ static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *m
return sendmsg(fd, msg, 0);
}
+
+S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size,
+ int cmsg_type, uint8_t record_type)
+{
+ RESULT_ENSURE_REF(msg);
+ RESULT_ENSURE_REF(buf);
+
+ /*
+ * https://man7.org/linux/man-pages/man3/cmsg.3.html
+ * To create ancillary data, first initialize the msg_controllen
+ * member of the msghdr with the length of the control message
+ * buffer.
+ */
+ msg->msg_control = buf;
+ msg->msg_controllen = buf_size;
+
+ /*
+ * https://man7.org/linux/man-pages/man3/cmsg.3.html
+ * Use CMSG_FIRSTHDR() on the msghdr to get the first
+ * control message and CMSG_NXTHDR() to get all subsequent ones.
+ */
+ struct cmsghdr *hdr = CMSG_FIRSTHDR(msg);
+ RESULT_ENSURE_REF(hdr);
+
+ /*
+ * https://man7.org/linux/man-pages/man3/cmsg.3.html
+ * In each control message, initialize cmsg_len (with CMSG_LEN()), the
+ * other cmsghdr header fields, and the data portion using
+ * CMSG_DATA().
+ */
+ hdr->cmsg_len = CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE);
+ hdr->cmsg_level = S2N_SOL_TLS;
+ hdr->cmsg_type = cmsg_type;
+ *CMSG_DATA(hdr) = record_type;
+
+ /*
+ * https://man7.org/linux/man-pages/man3/cmsg.3.html
+ * Finally, the msg_controllen field of the msghdr
+ * should be set to the sum of the CMSG_SPACE() of the length of all
+ * control messages in the buffer
+ */
+ RESULT_ENSURE_GTE(msg->msg_controllen, CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE));
+ msg->msg_controllen = CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE);
+
+ return S2N_RESULT_OK;
+}
+
+/* Expect to receive a single cmsghdr containing the TLS record_type.
+ *
+ * s2n-tls allocates enough space to receive a single cmsghdr. Since this is
+ * used to get the record_type when receiving over kTLS (enabled via
+ * `s2n_connection_ktls_enable_recv`), the application should not configure
+ * the socket to receive additional control messages. In the event s2n-tls
+ * can not retrieve the record_type, it is safer to drop the record.
+ */
+S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type)
+{
+ RESULT_ENSURE_REF(msg);
+ RESULT_ENSURE_REF(record_type);
+
+ /*
+ * https://man7.org/linux/man-pages/man3/cmsg.3.html
+ * To create ancillary data, first initialize the msg_controllen
+ * member of the msghdr with the length of the control message
+ * buffer.
+ */
+ RESULT_ENSURE(msg->msg_control, S2N_ERR_SAFETY);
+ RESULT_ENSURE(msg->msg_controllen >= CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_SAFETY);
+
+ /* https://man7.org/linux/man-pages/man3/cmsg.3.html
+ * Use CMSG_FIRSTHDR() on the msghdr to get the first
+ * control message and CMSG_NXTHDR() to get all subsequent ones.
+ */
+ struct cmsghdr *hdr = CMSG_FIRSTHDR(msg);
+ RESULT_ENSURE(hdr, S2N_ERR_KTLS_BAD_CMSG);
+
+ /*
+ * https://man7.org/linux/man-pages/man3/cmsg.3.html
+ * In each control message, initialize cmsg_len (with CMSG_LEN()), the
+ * other cmsghdr header fields, and the data portion using
+ * CMSG_DATA().
+ */
+ RESULT_ENSURE(hdr->cmsg_level == S2N_SOL_TLS, S2N_ERR_KTLS_BAD_CMSG);
+ RESULT_ENSURE(hdr->cmsg_type == cmsg_type, S2N_ERR_KTLS_BAD_CMSG);
+ RESULT_ENSURE(hdr->cmsg_len == CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_KTLS_BAD_CMSG);
+ *record_type = *CMSG_DATA(hdr);
+
+ return S2N_RESULT_OK;
+}
diff --git a/tls/s2n_ktls_parameters.h b/tls/s2n_ktls_parameters.h
index 0137467c242..c3f1482c4fb 100644
--- a/tls/s2n_ktls_parameters.h
+++ b/tls/s2n_ktls_parameters.h
@@ -41,6 +41,8 @@
#define S2N_TLS_TX 1
#define S2N_TLS_RX 2
+ #define S2N_TLS_SET_RECORD_TYPE TLS_SET_RECORD_TYPE
+ #define S2N_TLS_GET_RECORD_TYPE TLS_GET_RECORD_TYPE
#else
/* For unsupported platforms 0-init (array of size 1) all values. */
@@ -51,6 +53,9 @@
#define S2N_TLS_TX 0
#define S2N_TLS_RX 0
+
+ #define S2N_TLS_SET_RECORD_TYPE 0
+ #define S2N_TLS_GET_RECORD_TYPE 0
#endif
/* Common */