[glib-networking/wip/pwithnall/dtls: 6/18] gnutls: Implement half-duplex close for GTlsConnectionGnutls
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/pwithnall/dtls: 6/18] gnutls: Implement half-duplex close for GTlsConnectionGnutls
- Date: Tue, 1 Sep 2015 14:27:53 +0000 (UTC)
commit cd2a0aa47266f829eb32120f30657a1e71726fb2
Author: Philip Withnall <philip withnall collabora co uk>
Date: Fri Sep 26 15:46:26 2014 +0100
gnutls: Implement half-duplex close for GTlsConnectionGnutls
Support closing the read or write substreams of a GTlsConnectionGnutls
independently. This includes the core support for half-duplex close, but
not the implementations in GTls[Input|Output]StreamGnutls.
Based on a patch by Olivier CrĂȘte <olivier crete collabora com>.
https://bugzilla.gnome.org/show_bug.cgi?id=735754
tls/gnutls/gtlsconnection-gnutls.c | 89 +++++++++++++++++++++++++-----------
tls/gnutls/gtlsconnection-gnutls.h | 9 ++++
2 files changed, 71 insertions(+), 27 deletions(-)
---
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index afce917..aa0eae7 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -140,7 +140,10 @@ struct _GTlsConnectionGnutlsPrivate
GError *handshake_error;
GByteArray *app_data_buf;
- gboolean closing, closed;
+ /* read_closed means the read direction has closed; write_closed similarly.
+ * If (and only if) both are set, the entire GTlsConnection is closed. */
+ gboolean read_closing, read_closed;
+ gboolean write_closing, write_closed;
GInputStream *tls_istream;
GOutputStream *tls_ostream;
@@ -574,7 +577,12 @@ claim_op (GTlsConnectionGnutls *gnutls,
g_mutex_lock (&gnutls->priv->op_mutex);
- if (gnutls->priv->closing || gnutls->priv->closed)
+ if (((op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE ||
+ op == G_TLS_CONNECTION_GNUTLS_OP_READ) &&
+ (gnutls->priv->read_closing || gnutls->priv->read_closed)) ||
+ ((op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE ||
+ op == G_TLS_CONNECTION_GNUTLS_OP_WRITE) &&
+ (gnutls->priv->write_closing || gnutls->priv->write_closed)))
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
_("Connection is closed"));
@@ -671,9 +679,11 @@ claim_op (GTlsConnectionGnutls *gnutls,
gnutls->priv->need_handshake = FALSE;
}
if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ ||
+ op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ)
+ gnutls->priv->read_closing = TRUE;
+ if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
- gnutls->priv->closing = TRUE;
+ gnutls->priv->write_closing = TRUE;
if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
gnutls->priv->reading = TRUE;
@@ -693,9 +703,11 @@ yield_op (GTlsConnectionGnutls *gnutls,
if (op == G_TLS_CONNECTION_GNUTLS_OP_HANDSHAKE)
gnutls->priv->handshaking = FALSE;
if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
- op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ ||
+ op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ)
+ gnutls->priv->read_closing = FALSE;
+ if (op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH ||
op == G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE)
- gnutls->priv->closing = FALSE;
+ gnutls->priv->write_closing = FALSE;
if (op != G_TLS_CONNECTION_GNUTLS_OP_WRITE)
gnutls->priv->reading = FALSE;
@@ -881,7 +893,11 @@ g_tls_connection_gnutls_check (GTlsConnectionGnutls *gnutls,
/* If a handshake or close is in progress, then tls_istream and
* tls_ostream are blocked, regardless of the base stream status.
*/
- if (gnutls->priv->handshaking || gnutls->priv->closing)
+ if (gnutls->priv->handshaking)
+ return FALSE;
+
+ if (((condition & G_IO_IN) && gnutls->priv->read_closing) ||
+ ((condition & G_IO_OUT) && gnutls->priv->write_closing))
return FALSE;
if (condition & G_IO_IN)
@@ -1611,45 +1627,63 @@ g_tls_connection_gnutls_get_output_stream (GIOStream *stream)
gboolean
g_tls_connection_gnutls_close_internal (GIOStream *stream,
+ GTlsDirection direction,
GCancellable *cancellable,
GError **error)
{
GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (stream);
GTlsConnectionGnutlsOp op;
- gboolean success;
+ gboolean success = TRUE;
int ret = 0;
- op = G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH;
+ /* This can be called from g_io_stream_close(), g_input_stream_close() or
+ * g_output_stream_close(). In all cases, we only do the gnutls_bye() for
+ * writing. The difference is how we set the flags on this class and how
+ * the underlying stream is closed.
+ */
+
+ g_return_val_if_fail (direction != G_TLS_DIRECTION_NONE, FALSE);
+
+ if (direction == G_TLS_DIRECTION_BOTH)
+ op = G_TLS_CONNECTION_GNUTLS_OP_CLOSE_BOTH;
+ else if (direction == G_TLS_DIRECTION_READ)
+ op = G_TLS_CONNECTION_GNUTLS_OP_CLOSE_READ;
+ else
+ op = G_TLS_CONNECTION_GNUTLS_OP_CLOSE_WRITE;
if (!claim_op (gnutls, op, TRUE, cancellable, error))
return FALSE;
- if (gnutls->priv->closed)
- {
- yield_op (gnutls, op);
- return TRUE;
- }
-
- if (gnutls->priv->ever_handshaked)
+ if (gnutls->priv->ever_handshaked && !gnutls->priv->write_closed &&
+ direction & G_TLS_DIRECTION_WRITE)
{
BEGIN_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, TRUE, cancellable);
ret = gnutls_bye (gnutls->priv->session, GNUTLS_SHUT_WR);
END_GNUTLS_IO (gnutls, G_IO_IN | G_IO_OUT, ret,
_("Error performing TLS close: %s"), error);
- }
- gnutls->priv->closed = TRUE;
-
- if (ret != 0)
- {
- yield_op (gnutls, op);
- return FALSE;
+ gnutls->priv->write_closed = TRUE;
}
- success = g_io_stream_close (gnutls->priv->base_io_stream,
- cancellable, error);
+ if (!gnutls->priv->read_closed && direction & G_TLS_DIRECTION_READ)
+ gnutls->priv->read_closed = TRUE;
+
+ /* Close the underlying streams. Do this even if the gnutls_bye() call failed,
+ * as the parent GIOStream will have set its internal closed flag and hence
+ * this implementation will never be called again. */
+ if (direction == G_TLS_DIRECTION_BOTH)
+ success = g_io_stream_close (gnutls->priv->base_io_stream,
+ cancellable, error);
+ else if (direction & G_TLS_DIRECTION_READ)
+ success = g_input_stream_close (g_io_stream_get_input_stream (gnutls->priv->base_io_stream),
+ cancellable, error);
+ else if (direction & G_TLS_DIRECTION_WRITE)
+ success = g_output_stream_close (g_io_stream_get_output_stream (gnutls->priv->base_io_stream),
+ cancellable, error);
+
yield_op (gnutls, op);
- return success;
+
+ return success && (ret == 0);
}
static gboolean
@@ -1658,6 +1692,7 @@ g_tls_connection_gnutls_close (GIOStream *stream,
GError **error)
{
return g_tls_connection_gnutls_close_internal (stream,
+ G_TLS_DIRECTION_BOTH,
cancellable, error);
}
@@ -1674,7 +1709,7 @@ close_thread (GTask *task,
GIOStream *stream = object;
GError *error = NULL;
- if (!g_tls_connection_gnutls_close_internal (stream,
+ if (!g_tls_connection_gnutls_close_internal (stream, G_TLS_DIRECTION_BOTH,
cancellable, &error))
g_task_return_error (task, error);
else
diff --git a/tls/gnutls/gtlsconnection-gnutls.h b/tls/gnutls/gtlsconnection-gnutls.h
index 9cc895f..a7323a8 100644
--- a/tls/gnutls/gtlsconnection-gnutls.h
+++ b/tls/gnutls/gtlsconnection-gnutls.h
@@ -79,7 +79,16 @@ GSource *g_tls_connection_gnutls_create_source (GTlsConnectionGnutls *gnutls,
GIOCondition condition,
GCancellable *cancellable);
+typedef enum {
+ G_TLS_DIRECTION_NONE = 0,
+ G_TLS_DIRECTION_READ = 1 << 0,
+ G_TLS_DIRECTION_WRITE = 1 << 1,
+} GTlsDirection;
+
+#define G_TLS_DIRECTION_BOTH (G_TLS_DIRECTION_READ | G_TLS_DIRECTION_WRITE)
+
gboolean g_tls_connection_gnutls_close_internal (GIOStream *stream,
+ GTlsDirection direction,
GCancellable *cancellable,
GError **error);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]