[Spice-devel] [PATCH spice-gtk] Use an openssl BIO stream using GSocket

Marc-André Lureau marcandre.lureau at gmail.com
Sun Feb 12 10:04:56 PST 2012


Until now, the BIO object used by openssl to read & write was using
the socket fd directly. But the mainloop integration is done with
GSocket.

On Windows, the read/write events are cleared after
g_socket_send()/receive() with private function
_win32_unset_event_mask. If the glib functions aren't cleared, glib
source will keep notifying of data available in or out. On Windows,
this causes a busy loop when doing SSL_read() for example (glib
POLL_IN data condition is reached and SSL_read() return needs data).

Instead, openssl should read/write using GSocket methods.
---
 gtk/Makefile.am     |    2 +
 gtk/bio-gsocket.c   |  111 +++++++++++++++++++++++++++++++++++++++++++++++++++
 gtk/bio-gsocket.h   |   30 ++++++++++++++
 gtk/spice-channel.c |    9 ++--
 gtk/spice-client.h  |    4 ++
 gtk/spice-util.h    |    2 +-
 6 files changed, 152 insertions(+), 6 deletions(-)
 create mode 100644 gtk/bio-gsocket.c
 create mode 100644 gtk/bio-gsocket.h

diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 9e6af17..788a145 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -186,6 +186,8 @@ USB_ACL_HELPER_SRCS =
 endif
 
 libspice_client_glib_2_0_la_SOURCES =	\
+	bio-gsocket.c			\
+	bio-gsocket.h			\
 	glib-compat.c			\
 	glib-compat.h			\
 	spice-audio.c			\
diff --git a/gtk/bio-gsocket.c b/gtk/bio-gsocket.c
new file mode 100644
index 0000000..ce94afe
--- /dev/null
+++ b/gtk/bio-gsocket.c
@@ -0,0 +1,111 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <string.h>
+#include <glib.h>
+
+#include "spice-util.h"
+#include "bio-gsocket.h"
+
+typedef struct bio_gsocket_method {
+    BIO_METHOD method;
+    GSocket *gsocket;
+} bio_gsocket_method;
+
+#define BIO_GET_GSOCKET(bio)  (((bio_gsocket_method*)bio->method)->gsocket)
+
+static int bio_gsocket_bwrite(BIO *bio, const char *in, int inl)
+{
+    int ret;
+    GError *error = NULL;
+
+    ret = g_socket_send(BIO_GET_GSOCKET(bio),
+                        in, inl, NULL, &error);
+    BIO_clear_retry_flags(bio);
+
+    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+        BIO_set_retry_write(bio);
+    if (error != NULL) {
+        g_warning("%s", error->message);
+        g_clear_error(&error);
+    }
+
+    return ret;
+}
+
+static int bio_gsocket_bread(BIO *bio, char *out, int outl)
+{
+    int ret;
+    GError *error = NULL;
+
+    ret = g_socket_receive(BIO_GET_GSOCKET(bio),
+                           out, outl, NULL, &error);
+    BIO_clear_retry_flags(bio);
+
+    if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK))
+        BIO_set_retry_read(bio);
+    else if (error != NULL) {
+        g_warning("%s", error->message);
+        g_clear_error(&error);
+    }
+
+    return ret;
+}
+
+static int bio_gsocket_destroy(BIO *bio)
+{
+    if (bio == NULL || bio->method == NULL)
+        return 0;
+
+    SPICE_DEBUG("bio gsocket destroy");
+    g_free(bio->method);
+    bio->method = NULL;;
+
+    return 1;
+}
+
+static int bio_gsocket_bputs(BIO *bio, const char *str)
+{
+    int n, ret;
+
+    n = strlen(str);
+    ret = bio_gsocket_bwrite(bio, str, n);
+
+    return ret;
+}
+
+G_GNUC_INTERNAL
+BIO* bio_new_gsocket(GSocket *gsocket)
+{
+    BIO *bio = BIO_new_socket(g_socket_get_fd(gsocket), BIO_NOCLOSE);
+
+    bio_gsocket_method *bio_method = g_new(bio_gsocket_method, 1);
+    bio_method->method = *bio->method;
+    bio_method->gsocket = gsocket;
+
+    bio->method->destroy(bio);
+    bio->method = (BIO_METHOD*)bio_method;
+
+    bio->method->bwrite = bio_gsocket_bwrite;
+    bio->method->bread = bio_gsocket_bread;
+    bio->method->bputs = bio_gsocket_bputs;
+    bio->method->destroy = bio_gsocket_destroy;
+
+    return bio;
+}
+
diff --git a/gtk/bio-gsocket.h b/gtk/bio-gsocket.h
new file mode 100644
index 0000000..7ee5e64
--- /dev/null
+++ b/gtk/bio-gsocket.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef BIO_GSOCKET_H_
+# define BIO_GSOCKET_H_
+
+#include <openssl/bio.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+BIO* bio_new_gsocket(GSocket *gsocket);
+
+G_END_DECLS
+
+#endif /* !BIO_GSOCKET_H_ */
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index de60363..afcee98 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -21,6 +21,7 @@
 #include "spice-channel-priv.h"
 #include "spice-session-priv.h"
 #include "spice-marshal.h"
+#include "bio-gsocket.h"
 
 #include <openssl/rsa.h>
 #include <openssl/evp.h>
@@ -2156,13 +2157,11 @@ reconnect:
             g_critical("SSL_new failed");
             goto cleanup;
         }
-        rc = SSL_set_fd(c->ssl, g_socket_get_fd(c->sock));
-        if (rc <= 0) {
-            g_critical("SSL_set_fd failed");
-            goto cleanup;
-        }
 
 
+        BIO *bio = bio_new_gsocket(c->sock);
+        SSL_set_bio(c->ssl, bio, bio);
+
         {
             guint8 *pubkey;
             guint pubkey_len;
diff --git a/gtk/spice-client.h b/gtk/spice-client.h
index e77c970..2d7c622 100644
--- a/gtk/spice-client.h
+++ b/gtk/spice-client.h
@@ -45,6 +45,8 @@
 #include "usb-device-manager.h"
 #include "spice-audio.h"
 
+G_BEGIN_DECLS
+
 #define SPICE_CLIENT_ERROR spice_client_error_quark()
 
 /**
@@ -60,4 +62,6 @@ typedef enum
 
 GQuark spice_client_error_quark(void);
 
+G_END_DECLS
+
 #endif /* __SPICE_CLIENT_CLIENT_H__ */
diff --git a/gtk/spice-util.h b/gtk/spice-util.h
index 5029bc1..aace4b6 100644
--- a/gtk/spice-util.h
+++ b/gtk/spice-util.h
@@ -18,7 +18,7 @@
 #ifndef SPICE_UTIL_H
 #define SPICE_UTIL_H
 
-#include <glib.h>
+#include <glib-object.h>
 
 G_BEGIN_DECLS
 
-- 
1.7.7.6



More information about the Spice-devel mailing list