S2N_RESULT s2n_send_early_data_impl()

in tls/s2n_early_data_io.c [149:207]


S2N_RESULT s2n_send_early_data_impl(struct s2n_connection *conn, const uint8_t *data, ssize_t data_len_signed,
        ssize_t *data_sent, s2n_blocked_status *blocked)
{
    RESULT_ENSURE_GTE(data_len_signed, 0);
    size_t data_len = data_len_signed;
    RESULT_ENSURE_REF(conn);
    RESULT_ENSURE_REF(blocked);
    *blocked = S2N_NOT_BLOCKED;
    RESULT_ENSURE_REF(data_sent);
    *data_sent = 0;

    RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_SERVER_MODE);
    RESULT_ENSURE(s2n_connection_supports_tls13(conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);

    if (!s2n_early_data_can_continue(conn)) {
        return S2N_RESULT_OK;
    }

    /* Attempt to make progress in the handshake even if s2n_send eventually fails.
     * We only care about the result of this call if it would prevent us from calling s2n_send. */
    int negotiate_result = s2n_negotiate(conn, blocked);
    if (negotiate_result < S2N_SUCCESS) {
        if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) {
            RESULT_GUARD_POSIX(negotiate_result);
        } else if (*blocked != S2N_BLOCKED_ON_EARLY_DATA && *blocked != S2N_BLOCKED_ON_READ) {
            RESULT_GUARD_POSIX(negotiate_result);
        }
    }
    /* Save the error status for later */
    int negotiate_error = s2n_errno;
    s2n_blocked_status negotiate_blocked = *blocked;

    /* Attempt to send the early data.
     * We only care about the result of this call if it fails. */
    uint32_t early_data_to_send = 0;
    RESULT_GUARD_POSIX(s2n_connection_get_remaining_early_data_size(conn, &early_data_to_send));
    early_data_to_send = MIN(data_len, early_data_to_send);
    if (early_data_to_send) {
        ssize_t send_result = s2n_send(conn, data, early_data_to_send, blocked);
        RESULT_GUARD_POSIX(send_result);
        *data_sent = send_result;
    }
    *blocked = S2N_NOT_BLOCKED;

    /* Since the send was successful, report the result of the original negotiate call.
     * If we got this far, the result must have been success or a blocking error. */
    if (negotiate_result < S2N_SUCCESS) {
        RESULT_ENSURE_EQ(s2n_error_get_type(negotiate_error), S2N_ERR_T_BLOCKED);
        if (negotiate_blocked == S2N_BLOCKED_ON_EARLY_DATA) {
            return S2N_RESULT_OK;
        } else if (s2n_early_data_can_continue(conn)) {
            *blocked = negotiate_blocked;
            RESULT_BAIL(negotiate_error);
        } else {
            return S2N_RESULT_OK;
        }
    }
    return S2N_RESULT_OK;
}