int s2n_negotiate_impl()

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;
}