in tls/s2n_handshake_io.c [1604:1693]
int s2n_negotiate_impl(struct s2n_connection *conn, s2n_blocked_status *blocked)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(blocked);
while (!s2n_handshake_is_complete(conn) && ACTIVE_MESSAGE(conn) != conn->handshake.end_of_messages) {
errno = 0;
s2n_errno = S2N_ERR_OK;
/* Flush any pending I/O or alert messages */
POSIX_GUARD(s2n_flush(conn, blocked));
POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX), S2N_ERR_CLOSED);
/* If the handshake was paused, retry the current message */
if (conn->handshake.paused) {
*blocked = S2N_BLOCKED_ON_APPLICATION_INPUT;
POSIX_GUARD(s2n_handle_retry_state(conn));
}
if (CONNECTION_IS_WRITER(conn)) {
*blocked = S2N_BLOCKED_ON_WRITE;
const int write_result = s2n_handshake_write_io(conn);
if (write_result < S2N_SUCCESS) {
if (!S2N_ERROR_IS_BLOCKING(s2n_errno)) {
/* Non-retryable write error. The peer might have sent an alert. Try and read it. */
const int write_errno = errno;
const int write_s2n_errno = s2n_errno;
struct s2n_debug_info write_s2n_debug_info = _s2n_debug_info;
if (s2n_handshake_read_io(conn) < 0 && s2n_errno == S2N_ERR_ALERT) {
/* s2n_handshake_read_io has set s2n_errno */
S2N_ERROR_PRESERVE_ERRNO();
} else {
/* Let the write error take precedence if we didn't read an alert. */
errno = write_errno;
s2n_errno = write_s2n_errno;
_s2n_debug_info = write_s2n_debug_info;
S2N_ERROR_PRESERVE_ERRNO();
}
}
if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) {
*blocked = S2N_BLOCKED_ON_APPLICATION_INPUT;
conn->handshake.paused = true;
} else if (s2n_errno == S2N_ERR_EARLY_DATA_BLOCKED) {
*blocked = S2N_BLOCKED_ON_EARLY_DATA;
}
S2N_ERROR_PRESERVE_ERRNO();
}
} else {
*blocked = S2N_BLOCKED_ON_READ;
const int read_result = s2n_handshake_read_io(conn);
if (read_result < S2N_SUCCESS) {
/* One blocking condition is waiting on the session resumption cache. */
/* So we don't want to delete anything if we are blocked. */
if (!S2N_ERROR_IS_BLOCKING(s2n_errno) && conn->session_id_len) {
s2n_try_delete_session_cache(conn);
}
if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) {
*blocked = S2N_BLOCKED_ON_APPLICATION_INPUT;
conn->handshake.paused = true;
} else if (s2n_errno == S2N_ERR_EARLY_DATA_BLOCKED) {
*blocked = S2N_BLOCKED_ON_EARLY_DATA;
}
S2N_ERROR_PRESERVE_ERRNO();
}
}
if (ACTIVE_STATE(conn).writer == 'B') {
/* Clean up handshake secrets */
POSIX_GUARD_RESULT(s2n_tls13_secrets_clean(conn));
/* Send any pending post-handshake messages */
POSIX_GUARD(s2n_post_handshake_send(conn, blocked));
/* If the handshake has just ended, free up memory */
POSIX_GUARD(s2n_stuffer_resize(&conn->handshake.io, 0));
}
}
*blocked = S2N_NOT_BLOCKED;
return S2N_SUCCESS;
}