#include <openssl/sslerr.h>
#include <crypto/rand.h>
#include "quic_local.h"
-#include "internal/quic_dummy_handshake.h"
+#include "internal/quic_tls.h"
#include "internal/quic_rx_depack.h"
#include "internal/quic_error.h"
#include "internal/time.h"
static void aon_write_finish(QUIC_CONNECTION *qc);
+static int ensure_channel(QUIC_CONNECTION *qc);
/*
* QUIC Front-End I/O API: Common Utilities
{
QUIC_CONNECTION *qc = NULL;
SSL *ssl_base = NULL;
+ SSL_CONNECTION *sc = NULL;
qc = OPENSSL_zalloc(sizeof(*qc));
if (qc == NULL)
/* Initialise the QUIC_CONNECTION's stub header. */
ssl_base = &qc->ssl;
- if (!ossl_ssl_init(ssl_base, ctx, SSL_TYPE_QUIC_CONNECTION)) {
+ if (!ossl_ssl_init(ssl_base, ctx, ctx->method, SSL_TYPE_QUIC_CONNECTION)) {
ssl_base = NULL;
goto err;
}
+ qc->tls = ossl_ssl_connection_new_int(ctx, TLS_method());
+ if (qc->tls == NULL || (sc = SSL_CONNECTION_FROM_SSL(qc->tls)) == NULL)
+ goto err;
+
/* Channel is not created yet. */
qc->ssl_mode = qc->ssl.ctx->mode;
qc->last_error = SSL_ERROR_NONE;
qc->blocking = 1;
+
return ssl_base;
err:
BIO_free(qc->net_wbio);
/* Note: SSL_free calls OPENSSL_free(qc) for us */
+
+ SSL_free(qc->tls);
}
/* SSL method init */
*/
static int csm_analyse_init_peer_addr(BIO *net_wbio, BIO_ADDR *peer)
{
- if (!BIO_dgram_get_peer(net_wbio, peer))
+ if (BIO_dgram_get_peer(net_wbio, peer) <= 0)
return 0;
return 1;
return BIO_get_wpoll_descriptor(qc->net_wbio, desc);
}
-/* SSL_want_net_read */
-int ossl_quic_get_want_net_read(QUIC_CONNECTION *qc)
+/* SSL_net_read_desired */
+int ossl_quic_get_net_read_desired(QUIC_CONNECTION *qc)
{
if (qc->ch == NULL)
return 0;
- return ossl_quic_reactor_want_net_read(ossl_quic_channel_get_reactor(qc->ch));
+ return ossl_quic_reactor_net_read_desired(ossl_quic_channel_get_reactor(qc->ch));
}
-/* SSL_want_net_write */
-int ossl_quic_get_want_net_write(QUIC_CONNECTION *qc)
+/* SSL_net_write_desired */
+int ossl_quic_get_net_write_desired(QUIC_CONNECTION *qc)
{
if (qc->ch == NULL)
return 0;
- return ossl_quic_reactor_want_net_write(ossl_quic_channel_get_reactor(qc->ch));
+ return ossl_quic_reactor_net_write_desired(ossl_quic_channel_get_reactor(qc->ch));
}
/*
*/
/* SSL_shutdown */
-int ossl_quic_shutdown(SSL *s)
+static int quic_shutdown_wait(void *arg)
{
- QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s);
-
- if (!expect_quic_conn(qc))
- return 0;
-
- if (qc->ch != NULL)
- ossl_quic_channel_local_close(qc->ch);
+ QUIC_CONNECTION *qc = arg;
- return 1;
+ return qc->ch == NULL || ossl_quic_channel_is_terminated(qc->ch);
}
-/* SSL_ctrl */
-static void fixup_mode_change(QUIC_CONNECTION *qc)
+int ossl_quic_conn_shutdown(QUIC_CONNECTION *qc, uint64_t flags,
+ const SSL_SHUTDOWN_EX_ARGS *args,
+ size_t args_len)
{
- /* If enabling EPW mode, cancel any AON write */
- if ((qc->ssl_mode & SSL_MODE_ENABLE_PARTIAL_WRITE) != 0)
- aon_write_finish(qc);
+ if (!ensure_channel(qc))
+ return -1;
+
+ ossl_quic_channel_local_close(qc->ch,
+ args != NULL ? args->quic_error_code : 0);
+
+ /* TODO(QUIC): !SSL_SHUTDOWN_FLAG_NO_STREAM_FLUSH */
+
+ if (ossl_quic_channel_is_terminated(qc->ch))
+ return 1;
+
+ if (blocking_mode(qc) && (flags & SSL_SHUTDOWN_FLAG_RAPID) == 0)
+ block_until_pred(qc, quic_shutdown_wait, NULL, 0);
+ else
+ ossl_quic_reactor_tick(ossl_quic_channel_get_reactor(qc->ch));
+
+ return ossl_quic_channel_is_terminated(qc->ch);
}
+/* SSL_ctrl */
long ossl_quic_ctrl(SSL *s, int cmd, long larg, void *parg)
{
QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s);
switch (cmd) {
case SSL_CTRL_MODE:
+ /* Cannot enable EPW while AON write in progress. */
+ if (qc->aon_write_in_progress)
+ larg &= ~SSL_MODE_ENABLE_PARTIAL_WRITE;
+
qc->ssl_mode |= (uint32_t)larg;
- fixup_mode_change(qc);
return qc->ssl_mode;
case SSL_CTRL_CLEAR_MODE:
qc->ssl_mode &= ~(uint32_t)larg;
- fixup_mode_change(qc);
return qc->ssl_mode;
default:
return 0;
return 1;
}
-/*
- * Creates a channel and configures it with the information we have accumulated
- * via calls made to us from the application prior to starting a handshake
- * attempt.
- */
-static int ensure_channel_and_start(QUIC_CONNECTION *qc)
+static int ensure_channel(QUIC_CONNECTION *qc)
{
QUIC_CHANNEL_ARGS args = {0};
args.libctx = qc->ssl.ctx->libctx;
args.propq = qc->ssl.ctx->propq;
args.is_server = 0;
+ args.tls = qc->tls;
qc->ch = ossl_quic_channel_new(&args);
if (qc->ch == NULL)
return 0;
+ return 1;
+}
+
+/*
+ * Creates a channel and configures it with the information we have accumulated
+ * via calls made to us from the application prior to starting a handshake
+ * attempt.
+ */
+static int ensure_channel_and_start(QUIC_CONNECTION *qc)
+{
+ if (!ensure_channel(qc))
+ return 0;
+
if (!configure_channel(qc)
|| !ossl_quic_channel_start(qc->ch)) {
ossl_quic_channel_free(qc->ch);
* (BIO/)SSL_read => ossl_quic_read
* (BIO/)SSL_write => ossl_quic_write
* SSL_pending => ossl_quic_pending
+ * SSL_stream_conclude => ossl_quic_conn_stream_conclude
*/
/* SSL_get_error */
args->len -= actual_written;
args->total_written += actual_written;
- if (actual_written == 0)
+ if (args->len == 0)
/* Written everything, done. */
return 1;
if (qc->aon_write_in_progress) {
/*
* We are in the middle of an AON write (i.e., a previous write did not
- * manage to append all data to the SSTREAM and we have EPW mode
- * disabled.)
+ * manage to append all data to the SSTREAM and we have Enable Partial
+ * Write (EPW) mode disabled.)
*/
if ((!accept_moving_buffer && qc->aon_buf_base != buf)
|| len != qc->aon_buf_len)
{
int is_fin = 0;
+ /* If the receive part of the stream is over, issue EOF. */
+ if (stream->recv_fin_retired)
+ return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_ZERO_RETURN);
+
if (stream->rstream == NULL)
return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
{
struct quic_read_again_args *args = arg;
- if (!ossl_quic_channel_is_active(args->qc->ch))
+ if (!ossl_quic_channel_is_active(args->qc->ch)) {
/* If connection is torn down due to an error while blocking, stop. */
+ QUIC_RAISE_NON_NORMAL_ERROR(args->qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
return -1;
+ }
if (!quic_read_actual(args->qc, args->stream,
args->buf, args->len, args->bytes_read,
/* got at least one byte, the SSL_read op can finish now */
return 1;
- return 0; /* did not write anything, keep trying */
+ return 0; /* did not read anything, keep trying */
}
static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek)
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
- /* If we haven't finished the handshake, try to advance it.*/
+ /* If we haven't finished the handshake, try to advance it. */
if (ossl_quic_do_handshake(qc) < 1)
- return 0;
+ return 0; /* ossl_quic_do_handshake raised error here */
if (qc->stream0 == NULL)
return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
if (!quic_read_actual(qc, qc->stream0, buf, len, bytes_read, peek))
- return 0;
+ return 0; /* quic_read_actual raised error here */
if (*bytes_read > 0) {
/*
args.peek = peek;
res = block_until_pred(qc, quic_read_again, &args, 0);
- if (res <= 0) {
- if (!ossl_quic_channel_is_active(qc->ch))
- return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
- else
- return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
- }
- }
+ if (res == 0)
+ return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL);
+ else if (res < 0)
+ return 0; /* quic_read_again raised error here */
- return 1;
+ return 1;
+ } else {
+ /* We did not get any bytes and are not in blocking mode. */
+ return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_WANT_READ);
+ }
}
int ossl_quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read)
return avail;
}
+/*
+ * SSL_stream_conclude
+ * -------------------
+ */
+int ossl_quic_conn_stream_conclude(QUIC_CONNECTION *qc)
+{
+ QUIC_STREAM *qs = qc->stream0;
+
+ if (qs == NULL || qs->sstream == NULL)
+ return 0;
+
+ if (!ossl_quic_channel_is_active(qc->ch)
+ || ossl_quic_sstream_get_final_size(qs->sstream, NULL))
+ return 1;
+
+ ossl_quic_sstream_fin(qs->sstream);
+ quic_post_write(qc, 1, 1);
+ return 1;
+}
+
/*
* QUIC Front-End I/O API: SSL_CTX Management
* ==========================================
/*
* This is the subset of TLS1.3 ciphers which can be used with QUIC and which we
* actually support.
+ *
+ * TODO(QUIC): CCM support
*/
static SSL_CIPHER tls13_quic_ciphers[] = {
{