static void qc_set_default_xso_keep_ref(QUIC_CONNECTION *qc, QUIC_XSO *xso,
int touch, QUIC_XSO **old_xso);
static SSL *quic_conn_stream_new(QCTX *ctx, uint64_t flags, int need_lock);
+static int quic_validate_for_write(QUIC_XSO *xso, int *err);
+static int quic_mutation_allowed(QUIC_CONNECTION *qc, int req_active);
/*
* QUIC Front-End I/O API: Common Utilities
assert(qc->ch != NULL);
+ /*
+ * Any attempt to block auto-disables tick inhibition as otherwise we will
+ * hang around forever.
+ */
+ ossl_quic_channel_set_inhibit_tick(qc->ch, 0);
+
rtor = ossl_quic_channel_get_reactor(qc->ch);
return ossl_quic_reactor_block_until_pred(rtor, pred, pred_arg, flags,
qc->mutex);
{
va_list args;
- ERR_new();
- ERR_set_debug(file, line, func);
-
- va_start(args, fmt);
- ERR_vset_error(ERR_LIB_SSL, reason, fmt, args);
- va_end(args);
-
if (ctx != NULL) {
if (ctx->is_stream && ctx->xso != NULL)
ctx->xso->last_error = SSL_ERROR_SSL;
else if (!ctx->is_stream && ctx->qc != NULL)
ctx->qc->last_error = SSL_ERROR_SSL;
+
+ if (reason == SSL_R_PROTOCOL_IS_SHUTDOWN && ctx->qc != NULL)
+ ossl_quic_channel_restore_err_state(ctx->qc->ch);
}
+ ERR_new();
+ ERR_set_debug(file, line, func);
+
+ va_start(args, fmt);
+ ERR_vset_error(ERR_LIB_SSL, reason, fmt, args);
+ va_end(args);
+
return 0;
}
quic_lock(ctx->qc);
if (ctx->xso == NULL && remote_init >= 0) {
- if (ossl_quic_channel_is_term_any(ctx->qc->ch)) {
+ if (!quic_mutation_allowed(ctx->qc, /*req_active=*/0)) {
QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
goto err;
}
#endif
}
+/*
+ * This predicate is the criterion which should determine API call rejection for
+ * *most* mutating API calls, particularly stream-related operations for send
+ * parts.
+ *
+ * A call is rejected (this function returns 0) if shutdown is in progress
+ * (stream flushing), or we are in a TERMINATING or TERMINATED state. If
+ * req_active=1, the connection must be active (i.e., the IDLE state is also
+ * rejected).
+ */
+static int quic_mutation_allowed(QUIC_CONNECTION *qc, int req_active)
+{
+ if (qc->shutting_down || ossl_quic_channel_is_term_any(qc->ch))
+ return 0;
+
+ if (req_active && !ossl_quic_channel_is_active(qc->ch))
+ return 0;
+
+ return 1;
+}
/*
* QUIC Front-End I/O API: Initialization
* ossl_quic_deinit
* SSL_free => ossl_quic_free
*
+ * SSL_set_options => ossl_quic_set_options
+ * SSL_get_options => ossl_quic_get_options
+ * SSL_clear_options => ossl_quic_clear_options
+ *
*/
/* SSL_new */
if (qc->tls == NULL || (sc = SSL_CONNECTION_FROM_SSL(qc->tls)) == NULL)
goto err;
+ /* override the user_ssl of the inner connection */
+ sc->s3.flags |= TLS1_FLAGS_QUIC;
+
+ /* Restrict options derived from the SSL_CTX. */
+ sc->options &= OSSL_QUIC_PERMITTED_OPTIONS_CONN;
+ sc->pha_enabled = 0;
+
#if defined(OPENSSL_THREADS)
if ((qc->mutex = ossl_crypto_mutex_new()) == NULL)
goto err;
= (ssl_base->method == OSSL_QUIC_client_thread_method());
#endif
- qc->as_server = 0; /* TODO(QUIC): server support */
+ qc->as_server = 0; /* TODO(QUIC SERVER): add server support */
qc->as_server_state = qc->as_server;
qc->default_stream_mode = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI;
qc->default_ssl_mode = qc->ssl.ctx->mode;
+ qc->default_ssl_options = qc->ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS;
qc->default_blocking = 1;
+ qc->blocking = 1;
qc->incoming_stream_policy = SSL_INCOMING_STREAM_POLICY_AUTO;
qc->last_error = SSL_ERROR_NONE;
--ctx.qc->num_xso;
/* If a stream's send part has not been finished, auto-reset it. */
- if (ctx.xso->stream->sstream != NULL
+ if (( ctx.xso->stream->send_state == QUIC_SSTREAM_STATE_READY
+ || ctx.xso->stream->send_state == QUIC_SSTREAM_STATE_SEND)
&& !ossl_quic_sstream_get_final_size(ctx.xso->stream->sstream, NULL))
ossl_quic_stream_map_reset_stream_send_part(ossl_quic_channel_get_qsm(ctx.qc->ch),
ctx.xso->stream, 0);
/* Do STOP_SENDING for the receive part, if applicable. */
- if (ctx.xso->stream->rstream != NULL)
+ if ( ctx.xso->stream->recv_state == QUIC_RSTREAM_STATE_RECV
+ || ctx.xso->stream->recv_state == QUIC_RSTREAM_STATE_SIZE_KNOWN)
ossl_quic_stream_map_stop_sending_recv_part(ossl_quic_channel_get_qsm(ctx.qc->ch),
ctx.xso->stream, 0);
/* No-op. */
}
-/* SSL_reset */
+/* SSL_clear (ssl_reset method) */
int ossl_quic_reset(SSL *s)
{
QCTX ctx;
if (!expect_quic(s, &ctx))
return 0;
- /* TODO(QUIC); Currently a no-op. */
- return 1;
+ /* Not supported. */
+ return 0;
}
-/* SSL_clear */
+/* ssl_clear method (unused) */
int ossl_quic_clear(SSL *s)
{
QCTX ctx;
if (!expect_quic(s, &ctx))
return 0;
- /* TODO(QUIC): Currently a no-op. */
+ /* TODO(QUIC FUTURE): Currently a no-op. */
return 1;
}
*/
assert(*old_xso == NULL);
- CRYPTO_DOWN_REF(&qc->ssl.references, &refs, &qc->ssl.lock);
+ CRYPTO_DOWN_REF(&qc->ssl.references, &refs);
assert(refs > 0);
}
}
SSL_free(&old_xso->ssl);
}
+QUIC_NEEDS_LOCK
+static void xso_update_options(QUIC_XSO *xso)
+{
+ int cleanse = ((xso->ssl_options & SSL_OP_CLEANSE_PLAINTEXT) != 0);
+
+ if (xso->stream->rstream != NULL)
+ ossl_quic_rstream_set_cleanse(xso->stream->rstream, cleanse);
+
+ if (xso->stream->sstream != NULL)
+ ossl_quic_sstream_set_cleanse(xso->stream->sstream, cleanse);
+}
+
+/*
+ * SSL_set_options
+ * ---------------
+ *
+ * Setting options on a QCSO
+ * - configures the handshake-layer options;
+ * - configures the default data-plane options for new streams;
+ * - configures the data-plane options on the default XSO, if there is one.
+ *
+ * Setting options on a QSSO
+ * - configures data-plane options for that stream only.
+ */
+QUIC_TAKES_LOCK
+static uint64_t quic_mask_or_options(SSL *ssl, uint64_t mask_value, uint64_t or_value)
+{
+ QCTX ctx;
+ uint64_t hs_mask_value, hs_or_value, ret;
+
+ if (!expect_quic(ssl, &ctx))
+ return 0;
+
+ quic_lock(ctx.qc);
+
+ if (!ctx.is_stream) {
+ /*
+ * If we were called on the connection, we apply any handshake option
+ * changes.
+ */
+ hs_mask_value = (mask_value & OSSL_QUIC_PERMITTED_OPTIONS_CONN);
+ hs_or_value = (or_value & OSSL_QUIC_PERMITTED_OPTIONS_CONN);
+
+ SSL_clear_options(ctx.qc->tls, hs_mask_value);
+ SSL_set_options(ctx.qc->tls, hs_or_value);
+
+ /* Update defaults for new streams. */
+ ctx.qc->default_ssl_options
+ = ((ctx.qc->default_ssl_options & ~mask_value) | or_value)
+ & OSSL_QUIC_PERMITTED_OPTIONS;
+ }
+
+ if (ctx.xso != NULL) {
+ ctx.xso->ssl_options
+ = ((ctx.xso->ssl_options & ~mask_value) | or_value)
+ & OSSL_QUIC_PERMITTED_OPTIONS_STREAM;
+
+ xso_update_options(ctx.xso);
+ }
+
+ ret = ctx.is_stream ? ctx.xso->ssl_options : ctx.qc->default_ssl_options;
+
+ quic_unlock(ctx.qc);
+ return ret;
+}
+
+uint64_t ossl_quic_set_options(SSL *ssl, uint64_t options)
+{
+ return quic_mask_or_options(ssl, 0, options);
+}
+
+/* SSL_clear_options */
+uint64_t ossl_quic_clear_options(SSL *ssl, uint64_t options)
+{
+ return quic_mask_or_options(ssl, options, 0);
+}
+
+/* SSL_get_options */
+uint64_t ossl_quic_get_options(const SSL *ssl)
+{
+ return quic_mask_or_options((SSL *)ssl, 0, 0);
+}
+
/*
* QUIC Front-End I/O API: Network BIO Configuration
* =================================================
*
*/
+QUIC_NEEDS_LOCK
+static void qc_shutdown_flush_init(QUIC_CONNECTION *qc)
+{
+ QUIC_STREAM_MAP *qsm;
+
+ if (qc->shutting_down)
+ return;
+
+ qsm = ossl_quic_channel_get_qsm(qc->ch);
+
+ ossl_quic_stream_map_begin_shutdown_flush(qsm);
+ qc->shutting_down = 1;
+}
+
+/* Returns 1 if all shutdown-flush streams have been done with. */
+QUIC_NEEDS_LOCK
+static int qc_shutdown_flush_finished(QUIC_CONNECTION *qc)
+{
+ QUIC_STREAM_MAP *qsm = ossl_quic_channel_get_qsm(qc->ch);
+
+ return qc->shutting_down
+ && ossl_quic_stream_map_is_shutdown_flush_finished(qsm);
+}
+
/* SSL_shutdown */
static int quic_shutdown_wait(void *arg)
{
return ossl_quic_channel_is_terminated(qc->ch);
}
+/* Returns 1 if shutdown flush process has finished or is inapplicable. */
+static int quic_shutdown_flush_wait(void *arg)
+{
+ QUIC_CONNECTION *qc = arg;
+
+ return ossl_quic_channel_is_term_any(qc->ch)
+ || qc_shutdown_flush_finished(qc);
+}
+
QUIC_TAKES_LOCK
int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
const SSL_SHUTDOWN_EX_ARGS *args,
{
int ret;
QCTX ctx;
+ int stream_flush = ((flags & SSL_SHUTDOWN_FLAG_NO_STREAM_FLUSH) == 0);
if (!expect_quic(s, &ctx))
- return 0;
+ return -1;
if (ctx.is_stream)
- /* TODO(QUIC): Semantics currently undefined for QSSOs */
return -1;
quic_lock(ctx.qc);
+ if (ossl_quic_channel_is_terminated(ctx.qc->ch)) {
+ quic_unlock(ctx.qc);
+ return 1;
+ }
+
+ /* Phase 1: Stream Flushing */
+ if (stream_flush) {
+ qc_shutdown_flush_init(ctx.qc);
+
+ if (!qc_shutdown_flush_finished(ctx.qc)) {
+ if (qc_blocking_mode(ctx.qc))
+ block_until_pred(ctx.qc, quic_shutdown_flush_wait, ctx.qc, 0);
+ else
+ ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(ctx.qc->ch), 0);
+ }
+
+ if (!qc_shutdown_flush_finished(ctx.qc)) {
+ quic_unlock(ctx.qc);
+ return 0; /* ongoing */
+ }
+ }
+
+ /* Phase 2: Connection Closure */
ossl_quic_channel_local_close(ctx.qc->ch,
args != NULL ? args->quic_error_code : 0);
- /* TODO(QUIC): !SSL_SHUTDOWN_FLAG_NO_STREAM_FLUSH */
+ SSL_set_shutdown(ctx.qc->tls, SSL_SENT_SHUTDOWN);
if (ossl_quic_channel_is_terminated(ctx.qc->ch)) {
quic_unlock(ctx.qc);
return 1;
}
+ /* Phase 3: Terminating Wait Time */
if (qc_blocking_mode(ctx.qc) && (flags & SSL_SHUTDOWN_FLAG_RAPID) == 0)
block_until_pred(ctx.qc, quic_shutdown_wait, ctx.qc, 0);
else
{
struct quic_handshake_wait_args *args = arg;
- if (!ossl_quic_channel_is_active(args->qc->ch))
+ if (!quic_mutation_allowed(args->qc, /*req_active=*/1))
return -1;
if (ossl_quic_channel_is_handshake_complete(args->qc->ch))
/* Handshake already completed. */
return 1;
- if (ossl_quic_channel_is_term_any(qc->ch))
+ if (!quic_mutation_allowed(qc, /*req_active=*/0))
return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
if (BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) {
}
if (qc->as_server != qc->as_server_state) {
- /* TODO(QUIC): Must match the method used to create the QCSO */
QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_PASSED_INVALID_ARGUMENT, NULL);
return -1; /* Non-protocol error */
}
args.qc = qc;
ret = block_until_pred(qc, quic_handshake_wait, &args, 0);
- if (!ossl_quic_channel_is_active(qc->ch)) {
+ if (!quic_mutation_allowed(qc, /*req_active=*/1)) {
QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
return 0; /* Shutdown before completion */
} else if (ret <= 0) {
{
struct quic_wait_for_stream_args *args = arg;
- if (!ossl_quic_channel_is_active(args->qc->ch)) {
+ if (!quic_mutation_allowed(args->qc, /*req_active=*/1)) {
/* If connection is torn down due to an error while blocking, stop. */
QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
return -1;
xso->conn = qc;
xso->blocking = qc->default_blocking;
xso->ssl_mode = qc->default_ssl_mode;
+ xso->ssl_options
+ = qc->default_ssl_options & OSSL_QUIC_PERMITTED_OPTIONS_STREAM;
xso->last_error = SSL_ERROR_NONE;
xso->stream = qs;
++qc->num_xso;
+ xso_update_options(xso);
return xso;
err:
if (need_lock)
quic_lock(qc);
- if (ossl_quic_channel_is_term_any(qc->ch)) {
+ if (!quic_mutation_allowed(qc, /*req_active=*/0)) {
QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
goto err;
}
* (BIO/)SSL_write => ossl_quic_write
* SSL_pending => ossl_quic_pending
* SSL_stream_conclude => ossl_quic_conn_stream_conclude
+ * SSL_key_update => ossl_quic_key_update
*/
/* SSL_get_error */
int ossl_quic_get_error(const SSL *s, int i)
{
QCTX ctx;
+ int net_error, last_error;
if (!expect_quic(s, &ctx))
return 0;
- return ctx.is_stream ? ctx.xso->last_error : ctx.qc->last_error;
+ quic_lock(ctx.qc);
+ net_error = ossl_quic_channel_net_error(ctx.qc->ch);
+ last_error = ctx.is_stream ? ctx.xso->last_error : ctx.qc->last_error;
+ quic_unlock(ctx.qc);
+
+ if (net_error)
+ return SSL_ERROR_SYSCALL;
+
+ return last_error;
}
/*
/*
* Try and send.
*
- * TODO(QUIC): It is probably inefficient to try and do this immediately,
- * plus we should eventually consider Nagle's algorithm.
+ * TODO(QUIC FUTURE): It is probably inefficient to try and do this
+ * immediately, plus we should eventually consider Nagle's algorithm.
*/
if (do_tick)
ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(xso->conn->ch), 0);
const unsigned char *buf;
size_t len;
size_t total_written;
+ int err;
};
QUIC_NEEDS_LOCK
struct quic_write_again_args *args = arg;
size_t actual_written = 0;
- if (!ossl_quic_channel_is_active(args->xso->conn->ch))
+ if (!quic_mutation_allowed(args->xso->conn, /*req_active=*/1))
/* If connection is torn down due to an error while blocking, stop. */
return -2;
+ if (!quic_validate_for_write(args->xso, &args->err))
+ /*
+ * Stream may have become invalid for write due to connection events
+ * while we blocked.
+ */
+ return -2;
+
+ args->err = ERR_R_INTERNAL_ERROR;
if (!ossl_quic_sstream_append(args->xso->stream->sstream,
args->buf, args->len, &actual_written))
return -2;
args.buf = (const unsigned char *)buf + actual_written;
args.len = len - actual_written;
args.total_written = 0;
+ args.err = ERR_R_INTERNAL_ERROR;
res = block_until_pred(xso->conn, quic_write_again, &args, 0);
if (res <= 0) {
- if (!ossl_quic_channel_is_active(xso->conn->ch))
+ if (!quic_mutation_allowed(xso->conn, /*req_active=*/1))
return QUIC_RAISE_NON_NORMAL_ERROR(ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
else
- return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
+ return QUIC_RAISE_NON_NORMAL_ERROR(ctx, args.err, NULL);
}
*written = args.total_written;
return 1;
}
+QUIC_NEEDS_LOCK
+static int quic_validate_for_write(QUIC_XSO *xso, int *err)
+{
+ QUIC_STREAM_MAP *qsm;
+
+ if (xso == NULL || xso->stream == NULL) {
+ *err = ERR_R_INTERNAL_ERROR;
+ return 0;
+ }
+
+ switch (xso->stream->send_state) {
+ default:
+ case QUIC_SSTREAM_STATE_NONE:
+ *err = SSL_R_STREAM_RECV_ONLY;
+ return 0;
+
+ case QUIC_SSTREAM_STATE_READY:
+ qsm = ossl_quic_channel_get_qsm(xso->conn->ch);
+
+ if (!ossl_quic_stream_map_ensure_send_part_id(qsm, xso->stream)) {
+ *err = ERR_R_INTERNAL_ERROR;
+ return 0;
+ }
+
+ /* FALLTHROUGH */
+ case QUIC_SSTREAM_STATE_SEND:
+ case QUIC_SSTREAM_STATE_DATA_SENT:
+ case QUIC_SSTREAM_STATE_DATA_RECVD:
+ if (ossl_quic_sstream_get_final_size(xso->stream->sstream, NULL)) {
+ *err = SSL_R_STREAM_FINISHED;
+ return 0;
+ }
+
+ return 1;
+
+ case QUIC_SSTREAM_STATE_RESET_SENT:
+ case QUIC_SSTREAM_STATE_RESET_RECVD:
+ *err = SSL_R_STREAM_RESET;
+ return 0;
+ }
+}
+
QUIC_TAKES_LOCK
int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
{
int ret;
QCTX ctx;
- int partial_write;
+ int partial_write, err;
*written = 0;
- if (len == 0)
- return 1;
-
if (!expect_quic_with_stream_lock(s, /*remote_init=*/0, &ctx))
return 0;
partial_write = ((ctx.xso->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0);
- if (ossl_quic_channel_is_term_any(ctx.qc->ch)) {
+ if (!quic_mutation_allowed(ctx.qc, /*req_active=*/0)) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
goto out;
}
goto out;
}
- if (ctx.xso->stream == NULL || ctx.xso->stream->sstream == NULL) {
- ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL);
+ /* Ensure correct stream state, stream send part not concluded, etc. */
+ if (!quic_validate_for_write(ctx.xso, &err)) {
+ ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, err, NULL);
+ goto out;
+ }
+
+ if (len == 0) {
+ ret = 1;
goto out;
}
int peek;
};
+QUIC_NEEDS_LOCK
+static int quic_validate_for_read(QUIC_XSO *xso, int *err, int *eos)
+{
+ QUIC_STREAM_MAP *qsm;
+
+ *eos = 0;
+
+ if (xso == NULL || xso->stream == NULL) {
+ *err = ERR_R_INTERNAL_ERROR;
+ return 0;
+ }
+
+ switch (xso->stream->recv_state) {
+ default:
+ case QUIC_RSTREAM_STATE_NONE:
+ *err = SSL_R_STREAM_SEND_ONLY;
+ return 0;
+
+ case QUIC_RSTREAM_STATE_RECV:
+ case QUIC_RSTREAM_STATE_SIZE_KNOWN:
+ case QUIC_RSTREAM_STATE_DATA_RECVD:
+ return 1;
+
+ case QUIC_RSTREAM_STATE_DATA_READ:
+ *eos = 1;
+ return 0;
+
+ case QUIC_RSTREAM_STATE_RESET_RECVD:
+ qsm = ossl_quic_channel_get_qsm(xso->conn->ch);
+ ossl_quic_stream_map_notify_app_read_reset_recv_part(qsm, xso->stream);
+
+ /* FALLTHROUGH */
+ case QUIC_RSTREAM_STATE_RESET_READ:
+ *err = SSL_R_STREAM_RESET;
+ return 0;
+ }
+}
+
QUIC_NEEDS_LOCK
static int quic_read_actual(QCTX *ctx,
QUIC_STREAM *stream,
size_t *bytes_read,
int peek)
{
- int is_fin = 0;
+ int is_fin = 0, err, eos;
QUIC_CONNECTION *qc = ctx->qc;
- /* If the receive part of the stream is over, issue EOF. */
- if (stream->recv_fin_retired)
- return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_ZERO_RETURN);
-
- if (stream->rstream == NULL)
- return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
+ if (!quic_validate_for_read(ctx->xso, &err, &eos)) {
+ if (eos)
+ return QUIC_RAISE_NORMAL_ERROR(ctx, SSL_ERROR_ZERO_RETURN);
+ else
+ return QUIC_RAISE_NON_NORMAL_ERROR(ctx, err, NULL);
+ }
if (peek) {
if (!ossl_quic_rstream_peek(stream->rstream, buf, buf_len,
return QUIC_RAISE_NON_NORMAL_ERROR(ctx, ERR_R_INTERNAL_ERROR, NULL);
}
- if (is_fin)
- stream->recv_fin_retired = 1;
+ if (is_fin && !peek) {
+ QUIC_STREAM_MAP *qsm = ossl_quic_channel_get_qsm(ctx->qc->ch);
+
+ ossl_quic_stream_map_notify_totally_read(qsm, ctx->xso->stream);
+ }
if (*bytes_read > 0)
ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(qc->ch),
{
struct quic_read_again_args *args = arg;
- if (!ossl_quic_channel_is_active(args->ctx->qc->ch)) {
+ if (!quic_mutation_allowed(args->ctx->qc, /*req_active=*/1)) {
/* If connection is torn down due to an error while blocking, stop. */
QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
return -1;
quic_lock(ctx.qc);
- if (ossl_quic_channel_is_term_any(ctx.qc->ch)) {
+ if (!quic_mutation_allowed(ctx.qc, /*req_active=*/0)) {
ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
goto out;
}
ctx.xso = ctx.qc->default_xso;
}
- if (ctx.xso->stream == NULL) {
- ret = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR, NULL);
- goto out;
- }
-
if (!quic_read_actual(&ctx, ctx.xso->stream, buf, len, bytes_read, peek)) {
ret = 0; /* quic_read_actual raised error here */
goto out;
ret = 1;
} else {
- /* We did not get any bytes and are not in blocking mode. */
- ret = QUIC_RAISE_NORMAL_ERROR(&ctx, SSL_ERROR_WANT_READ);
+ /*
+ * We did not get any bytes and are not in blocking mode.
+ * Tick to see if this delivers any more.
+ */
+ ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(ctx.qc->ch), 0);
+
+ /* Try the read again. */
+ if (!quic_read_actual(&ctx, ctx.xso->stream, buf, len, bytes_read, peek)) {
+ ret = 0; /* quic_read_actual raised error here */
+ goto out;
+ }
+
+ if (*bytes_read > 0)
+ ret = 1; /* Succeeded this time. */
+ else
+ ret = QUIC_RAISE_NORMAL_ERROR(&ctx, SSL_ERROR_WANT_READ);
}
out:
* SSL_pending
* -----------
*/
+
QUIC_TAKES_LOCK
-static size_t ossl_quic_pending_int(const SSL *s)
+static size_t ossl_quic_pending_int(const SSL *s, int check_channel)
{
QCTX ctx;
size_t avail = 0;
int fin = 0;
- if (!expect_quic_with_stream_lock(s, /*remote_init=*/-1, &ctx))
+
+ if (!expect_quic(s, &ctx))
return 0;
- if (ctx.xso->stream == NULL || ctx.xso->stream->rstream == NULL)
+ quic_lock(ctx.qc);
+
+ if (ctx.xso == NULL)
+ goto out;
+
+ if (ctx.xso->stream == NULL
+ || !ossl_quic_stream_has_recv_buffer(ctx.xso->stream))
/* Cannot raise errors here because we are const, just fail. */
goto out;
if (!ossl_quic_rstream_available(ctx.xso->stream->rstream, &avail, &fin))
avail = 0;
+ if (avail == 0 && check_channel && ossl_quic_channel_has_pending(ctx.qc->ch))
+ avail = 1;
+
out:
quic_unlock(ctx.qc);
return avail;
size_t ossl_quic_pending(const SSL *s)
{
- return ossl_quic_pending_int(s);
+ return ossl_quic_pending_int(s, /*check_channel=*/0);
}
int ossl_quic_has_pending(const SSL *s)
{
- return ossl_quic_pending_int(s) > 0;
+ /* Do we have app-side pending data or pending URXEs or RXEs? */
+ return ossl_quic_pending_int(s, /*check_channel=*/1) > 0;
}
/*
{
QCTX ctx;
QUIC_STREAM *qs;
+ int err;
if (!expect_quic_with_stream_lock(s, /*remote_init=*/0, &ctx))
return 0;
qs = ctx.xso->stream;
- if (qs == NULL || qs->sstream == NULL) {
+ if (!quic_mutation_allowed(ctx.qc, /*req_active=*/1)) {
quic_unlock(ctx.qc);
- return 0;
+ return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
}
- if (!ossl_quic_channel_is_active(ctx.qc->ch)
- || ossl_quic_sstream_get_final_size(qs->sstream, NULL)) {
+ if (!quic_validate_for_write(ctx.xso, &err)) {
+ quic_unlock(ctx.qc);
+ return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, err, NULL);
+ }
+
+ if (ossl_quic_sstream_get_final_size(qs->sstream, NULL)) {
quic_unlock(ctx.qc);
return 1;
}
* It is a caller error for the XSO being attached as a default XSO to have
* more than one ref.
*/
- if (!CRYPTO_GET_REF(&xso->ssl.references, &nref, &xso->ssl.lock)) {
+ if (!CRYPTO_GET_REF(&xso->ssl.references, &nref)) {
quic_unlock(ctx.qc);
return QUIC_RAISE_NON_NORMAL_ERROR(&ctx, ERR_R_INTERNAL_ERROR,
"ref");
QUIC_CONNECTION *qc = args->ctx->qc;
QUIC_STREAM_MAP *qsm = ossl_quic_channel_get_qsm(qc->ch);
- if (!ossl_quic_channel_is_active(qc->ch)) {
+ if (!quic_mutation_allowed(qc, /*req_active=*/1)) {
/* If connection is torn down due to an error while blocking, stop. */
QUIC_RAISE_NON_NORMAL_ERROR(args->ctx, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
return -1;
QUIC_STREAM_MAP *qsm;
QUIC_STREAM *qs;
uint64_t error_code;
+ int ok, err;
if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/0, &ctx))
return 0;
qs = ctx.xso->stream;
error_code = (args != NULL ? args->quic_error_code : 0);
- ossl_quic_stream_map_reset_stream_send_part(qsm, qs, error_code);
+ if (!quic_validate_for_write(ctx.xso, &err)) {
+ ok = QUIC_RAISE_NON_NORMAL_ERROR(&ctx, err, NULL);
+ goto err;
+ }
+ ok = ossl_quic_stream_map_reset_stream_send_part(qsm, qs, error_code);
+
+err:
quic_unlock(ctx.qc);
- return 1;
+ return ok;
}
/*
} else if (ossl_quic_channel_is_term_any(qc->ch)) {
/* Connection already closed. */
*state = SSL_STREAM_STATE_CONN_CLOSED;
- } else if (!is_write && qs->recv_fin_retired) {
+ } else if (!is_write && qs->recv_state == QUIC_RSTREAM_STATE_DATA_READ) {
/* Application has read a FIN. */
*state = SSL_STREAM_STATE_FINISHED;
} else if ((!is_write && qs->stop_sending)
- || (is_write && qs->reset_stream)) {
+ || (is_write && ossl_quic_stream_send_is_reset(qs))) {
/*
* Stream has been reset locally. FIN takes precedence over this for the
* read case as the application need not care if the stream is reset
*app_error_code = !is_write
? qs->stop_sending_aec
: qs->reset_stream_aec;
- } else if ((!is_write && qs->peer_reset_stream)
+ } else if ((!is_write && ossl_quic_stream_recv_is_reset(qs))
|| (is_write && qs->peer_stop_sending)) {
/*
* Stream has been reset remotely. */
return quic_get_stream_error_code(ssl, /*is_write=*/1, app_error_code);
}
+/*
+ * Write buffer size mutation
+ * --------------------------
+ */
+int ossl_quic_set_write_buffer_size(SSL *ssl, size_t size)
+{
+ int ret = 0;
+ QCTX ctx;
+
+ if (!expect_quic_with_stream_lock(ssl, /*remote_init=*/-1, &ctx))
+ return 0;
+
+ if (!ossl_quic_stream_has_send(ctx.xso->stream))
+ /* Called on a unidirectional receive-only stream - error. */
+ goto out;
+
+ if (!ossl_quic_stream_has_send_buffer(ctx.xso->stream)) {
+ /*
+ * If the stream has a send part but we have disposed of it because we
+ * no longer need it, this is a no-op.
+ */
+ ret = 1;
+ goto out;
+ }
+
+ if (!ossl_quic_sstream_set_buffer_size(ctx.xso->stream->sstream, size))
+ goto out;
+
+ ret = 1;
+
+out:
+ quic_unlock(ctx.qc);
+ return ret;
+}
+
/*
* SSL_get_conn_close_info
* -----------------------
return 1;
}
+/*
+ * SSL_key_update
+ * --------------
+ */
+int ossl_quic_key_update(SSL *ssl, int update_type)
+{
+ QCTX ctx;
+
+ if (!expect_quic_conn_only(ssl, &ctx))
+ return 0;
+
+ switch (update_type) {
+ case SSL_KEY_UPDATE_NOT_REQUESTED:
+ /*
+ * QUIC signals peer key update implicily by triggering a local
+ * spontaneous TXKU. Silently upgrade this to SSL_KEY_UPDATE_REQUESTED.
+ */
+ case SSL_KEY_UPDATE_REQUESTED:
+ break;
+
+ default:
+ /* Unknown type - error. */
+ return 0;
+ }
+
+ quic_lock(ctx.qc);
+
+ /* Attempt to perform a TXKU. */
+ if (!ossl_quic_channel_trigger_txku(ctx.qc->ch)) {
+ quic_unlock(ctx.qc);
+ return 0;
+ }
+
+ quic_unlock(ctx.qc);
+ return 1;
+}
+
+/*
+ * SSL_get_key_update_type
+ * -----------------------
+ */
+int ossl_quic_get_key_update_type(const SSL *s)
+{
+ /*
+ * We always handle key updates immediately so a key update is never
+ * pending.
+ */
+ return SSL_KEY_UPDATE_NONE;
+}
+
/*
* QUIC Front-End I/O API: SSL_CTX Management
* ==========================================