[glib-networking/wip/pwithnall/dtls: 26/30] gnutls: Implement vectored I/O support for TLS and DTLS
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/pwithnall/dtls: 26/30] gnutls: Implement vectored I/O support for TLS and DTLS
- Date: Thu, 2 Nov 2017 12:15:31 +0000 (UTC)
commit 8e84ca7e1122aa288345bf0839e0f625155a9ac5
Author: Philip Withnall <philip withnall collabora co uk>
Date: Thu Jul 23 17:16:47 2015 +0100
gnutls: Implement vectored I/O support for TLS and DTLS
This bumps the GnuTLS dependency to 3.3.5 for
gnutls_record_recv_packet(), which gives us access to the internal
plaintext GnuTLS buffer, from which we can copy out to multiple
GInputVectors.
Similarly, add support for vectored sends, using gnutls_record_cork()
(requires GnuTLS 3.1.9) to queue up multiple vectors from a single
message before sending the message.
Include some trivial modifications of the unit tests to use multiple
vectors in a message.
https://bugzilla.gnome.org/show_bug.cgi?id=697908
meson.build | 2 +-
tls/gnutls/gtlsconnection-gnutls.c | 187 ++++++++++++++++++++++++++++++++----
tls/tests/dtls-connection.c | 15 ++-
3 files changed, 176 insertions(+), 28 deletions(-)
---
diff --git a/meson.build b/meson.build
index e193b5f..4254037 100644
--- a/meson.build
+++ b/meson.build
@@ -65,7 +65,7 @@ enable_tls_support = get_option('tls_support')
enable_pkcs11_support = false
if enable_tls_support
- gnutls_dep = dependency('gnutls', version: '>= 3.0', required: true)
+ gnutls_dep = dependency('gnutls', version: '>= 3.3.5', required: true)
msg = 'location of system Certificate Authority list: '
res = run_command(join_paths(meson.source_root(), 'find-ca-certificates'),
get_option('ca_certificates_path'))
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 5f4899e..c56b33b 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -2031,6 +2031,97 @@ g_tls_connection_gnutls_read (GTlsConnectionGnutls *gnutls,
return -1;
}
+static gsize
+input_vectors_from_gnutls_datum_t (GInputVector *vectors,
+ guint num_vectors,
+ const gnutls_datum_t *datum)
+{
+ guint i;
+ gsize total = 0;
+
+ /* Copy into the receive vectors. */
+ for (i = 0; i < num_vectors && total < datum->size; i++)
+ {
+ gsize count;
+ GInputVector *vec = &vectors[i];
+
+ count = MIN (vec->size, datum->size - total);
+
+ memcpy (vec->buffer, datum->data + total, count);
+ total += count;
+ }
+
+ g_assert (total <= datum->size);
+
+ return total;
+}
+
+static gssize
+g_tls_connection_gnutls_read_message (GTlsConnectionGnutls *gnutls,
+ GInputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ guint i;
+ gssize ret;
+ gnutls_packet_t packet = { 0, };
+
+ /* Copy data out of the app data buffer first. */
+ if (gnutls->priv->app_data_buf && !gnutls->priv->handshaking)
+ {
+ ret = 0;
+
+ for (i = 0; i < num_vectors; i++)
+ {
+ gsize count;
+ GInputVector *vec = &vectors[i];
+
+ count = MIN (vec->size, gnutls->priv->app_data_buf->len);
+ ret += count;
+
+ memcpy (vec->buffer, gnutls->priv->app_data_buf->data, count);
+ if (count == gnutls->priv->app_data_buf->len)
+ g_clear_pointer (&gnutls->priv->app_data_buf, g_byte_array_unref);
+ else
+ g_byte_array_remove_range (gnutls->priv->app_data_buf, 0, count);
+ }
+
+ return ret;
+ }
+
+ again:
+ if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ,
+ timeout != 0, cancellable, error))
+ return -1;
+
+ BEGIN_GNUTLS_IO (gnutls, G_IO_IN, timeout != 0, cancellable);
+
+ /* Receive the entire datagram (zero-copy). */
+ ret = gnutls_record_recv_packet (gnutls->priv->session, &packet);
+
+ if (ret > 0)
+ {
+ gnutls_datum_t data = { 0, };
+
+ gnutls_packet_get (packet, &data, NULL);
+ ret = input_vectors_from_gnutls_datum_t (vectors, num_vectors, &data);
+ gnutls_packet_deinit (packet);
+ }
+
+ END_GNUTLS_IO (gnutls, G_IO_IN, ret, _("Error reading data from TLS socket: %s"), error);
+
+ yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_READ);
+
+ if (ret >= 0)
+ return ret;
+ else if (ret == GNUTLS_E_REHANDSHAKE)
+ goto again;
+ else
+ return -1;
+}
+
static gint
g_tls_connection_gnutls_receive_messages (GDatagramBased *datagram_based,
GInputMessage *messages,
@@ -2058,16 +2149,12 @@ g_tls_connection_gnutls_receive_messages (GDatagramBased *datagram_based,
GInputMessage *message = &messages[i];
gssize n_bytes_read;
- /* FIXME: Unfortunately GnuTLS doesn’t have a vectored read function.
- * See: https://gitlab.com/gnutls/gnutls/issues/16 */
- g_assert (message->num_vectors == 1);
-
- n_bytes_read = g_tls_connection_gnutls_read (gnutls,
- message->vectors[0].buffer,
- message->vectors[0].size,
- timeout != 0,
- cancellable,
- &child_error);
+ n_bytes_read = g_tls_connection_gnutls_read_message (gnutls,
+ message->vectors,
+ message->num_vectors,
+ timeout,
+ cancellable,
+ &child_error);
if (message->address != NULL)
*message->address = NULL;
@@ -2141,6 +2228,69 @@ g_tls_connection_gnutls_write (GTlsConnectionGnutls *gnutls,
return -1;
}
+static gssize
+g_tls_connection_gnutls_write_message (GTlsConnectionGnutls *gnutls,
+ GOutputVector *vectors,
+ guint num_vectors,
+ gint64 timeout,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gssize ret;
+ guint i;
+ gsize total_message_size;
+
+ again:
+ if (!claim_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE,
+ timeout != 0, cancellable, error))
+ return -1;
+
+ /* Calculate the total message size and check it’s not too big. */
+ for (i = 0, total_message_size = 0; i < num_vectors; i++)
+ total_message_size += vectors[i].size;
+
+ if (gnutls_dtls_get_data_mtu (gnutls->priv->session) < total_message_size)
+ {
+ ret = GNUTLS_E_LARGE_PACKET;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_MESSAGE_TOO_LARGE,
+ _("Message of size %lu bytes is too large for "
+ "DTLS connection, maximum is %u bytes"),
+ total_message_size,
+ (guint) gnutls_dtls_get_data_mtu (gnutls->priv->session));
+ goto done;
+ }
+
+ /* Queue up the data from all the vectors. */
+ gnutls_record_cork (gnutls->priv->session);
+
+ for (i = 0; i < num_vectors; i++)
+ {
+ ret = gnutls_record_send (gnutls->priv->session,
+ vectors[i].buffer, vectors[i].size);
+
+ if (ret < 0 || ret < vectors[i].size)
+ {
+ /* Uncork to restore state, then bail. The peer will receive a
+ * truncated datagram. */
+ break;
+ }
+ }
+
+ BEGIN_GNUTLS_IO (gnutls, G_IO_OUT, timeout != 0, cancellable);
+ ret = gnutls_record_uncork (gnutls->priv->session, 0 /* flags */);
+ END_GNUTLS_IO (gnutls, G_IO_OUT, ret, _("Error writing data to TLS socket: %s"), error);
+
+ done:
+ yield_op (gnutls, G_TLS_CONNECTION_GNUTLS_OP_WRITE);
+
+ if (ret >= 0)
+ return ret;
+ else if (ret == GNUTLS_E_REHANDSHAKE)
+ goto again;
+ else
+ return -1;
+}
+
static gint
g_tls_connection_gnutls_send_messages (GDatagramBased *datagram_based,
GOutputMessage *messages,
@@ -2168,17 +2318,12 @@ g_tls_connection_gnutls_send_messages (GDatagramBased *datagram_based,
GOutputMessage *message = &messages[i];
gssize n_bytes_sent;
- /* FIXME: Unfortunately GnuTLS doesn’t have a vectored write function.
- * See: https://gitlab.com/gnutls/gnutls/issues/16 */
- /* TODO: gnutls_record_cork(), gnutls_record_uncork(), 3.3.0 */
- g_assert (message->num_vectors == 1);
-
- n_bytes_sent = g_tls_connection_gnutls_write (gnutls,
- message->vectors[0].buffer,
- message->vectors[0].size,
- timeout != 0,
- cancellable,
- &child_error);
+ n_bytes_sent = g_tls_connection_gnutls_write_message (gnutls,
+ message->vectors,
+ message->num_vectors,
+ timeout,
+ cancellable,
+ &child_error);
if (n_bytes_sent >= 0)
{
diff --git a/tls/tests/dtls-connection.c b/tls/tests/dtls-connection.c
index 842ba06..5c4e1b9 100644
--- a/tls/tests/dtls-connection.c
+++ b/tls/tests/dtls-connection.c
@@ -214,11 +214,11 @@ on_rehandshake_finish (GObject *object,
{
TestConnection *test = user_data;
GError *error = NULL;
- GOutputVector vector = {
- TEST_DATA + TEST_DATA_LENGTH / 2,
- TEST_DATA_LENGTH / 2
+ GOutputVector vectors[2] = {
+ { TEST_DATA + TEST_DATA_LENGTH / 2, TEST_DATA_LENGTH / 4 },
+ { TEST_DATA + 3 * TEST_DATA_LENGTH / 4, TEST_DATA_LENGTH / 4},
};
- GOutputMessage message = { NULL, &vector, 1, 0, NULL, 0 };
+ GOutputMessage message = { NULL, vectors, G_N_ELEMENTS (vectors), 0, NULL, 0 };
gint n_sent;
g_dtls_connection_handshake_finish (G_DTLS_CONNECTION (object), res, &error);
@@ -404,8 +404,11 @@ read_test_data_async (TestConnection *test)
gchar *check;
GError *error = NULL;
guint8 buf[TEST_DATA_LENGTH * 2];
- GInputVector vector = { &buf, sizeof (buf) };
- GInputMessage message = { NULL, &vector, 1, 0, 0, NULL, NULL };
+ GInputVector vectors[2] = {
+ { &buf, sizeof (buf) / 2 },
+ { &buf + sizeof (buf) / 2, sizeof (buf) / 2 },
+ };
+ GInputMessage message = { NULL, vectors, G_N_ELEMENTS (vectors), 0, 0, NULL, NULL };
gint n_read;
do
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]