[glib/tls: 10/10] Add TLS (SSL) support to gio



commit 63e48dde64ed8fca223aa88aa7b8a478bc81201c
Author: Dan Winship <danw gnome org>
Date:   Mon Dec 21 20:50:32 2009 +0100

    Add TLS (SSL) support to gio
    
    This adds support for TLS connections to GSocketConnection, with
    an initial implementation using gnutls.
    
    Still a work in progress; the gnutls code needs to migrate to
    glib-networking, some APIs need to be added, and some may be changed.

 docs/reference/gio/gio-docs.xml     |    9 +
 docs/reference/gio/gio-sections.txt |  130 ++++++-
 docs/reference/gio/gio.types        |    5 +
 gio/Makefile.am                     |   13 +-
 gio/TLS-NOTES.txt                   |   37 ++
 gio/gdummytlsbackend.c              |  274 +++++++++++++
 gio/gdummytlsbackend.h              |   48 +++
 gio/gio-marshal.list                |    2 +
 gio/gio.h                           |    5 +
 gio/gio.symbols                     |   77 ++++-
 gio/gioenums.h                      |   81 ++++
 gio/giomodule.c                     |    6 +
 gio/giotypes.h                      |    8 +
 gio/gsocketclient.c                 |  313 +++++++++++++---
 gio/gsocketclient.h                 |    7 +
 gio/gtlsbackend.c                   |  177 +++++++++
 gio/gtlsbackend.h                   |   89 +++++
 gio/gtlscertificate.c               |  508 ++++++++++++++++++++++++
 gio/gtlscertificate.h               |   78 ++++
 gio/gtlsclientconnection.c          |  341 ++++++++++++++++
 gio/gtlsclientconnection.h          |   71 ++++
 gio/gtlsconnection.c                |  741 +++++++++++++++++++++++++++++++++++
 gio/gtlsconnection.h                |  141 +++++++
 gio/gtlsserverconnection.c          |  105 +++++
 gio/gtlsserverconnection.h          |   60 +++
 gio/tests/.gitignore                |    1 +
 gio/tests/Makefile.am               |    9 +-
 gio/tests/socket-client.c           |  198 ++++++----
 gio/tests/socket-common.c           |   61 +++
 gio/tests/socket-server.c           |  167 +++++----
 gio/tests/test-cert.pem             |   37 ++
 gio/tests/tls-test.c                |  340 ++++++++++++++++
 32 files changed, 3927 insertions(+), 212 deletions(-)
---
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
index 4285a65..c137362 100644
--- a/docs/reference/gio/gio-docs.xml
+++ b/docs/reference/gio/gio-docs.xml
@@ -126,6 +126,15 @@
       <xi:include href="xml/gsocketservice.xml"/>
       <xi:include href="xml/gthreadedsocketservice.xml"/>
     </chapter>
+    <chapter id="tls">
+      <title>TLS (SSL) support</title>
+      <xi:include href="xml/gtls.xml"/>
+      <xi:include href="xml/gtlscertificate.xml"/>
+      <xi:include href="xml/gtlsconnection.xml"/>
+      <xi:include href="xml/gtlsclientconnection.xml"/>
+      <xi:include href="xml/gtlsserverconnection.xml"/>
+      <xi:include href="xml/gtlsbackend.xml"/>
+    </chapter>
     <chapter id="resolver">
       <title>DNS resolution</title>
       <xi:include href="xml/gresolver.xml"/>
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 199b9b4..f930328 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -1796,13 +1796,17 @@ g_socket_client_set_local_address
 g_socket_client_set_protocol
 g_socket_client_set_socket_type
 g_socket_client_set_timeout
+g_socket_client_set_enable_proxy
+g_socket_client_set_tls
+g_socket_client_set_tls_validation_flags
 g_socket_client_get_family
 g_socket_client_get_local_address
 g_socket_client_get_protocol
 g_socket_client_get_socket_type
 g_socket_client_get_timeout
 g_socket_client_get_enable_proxy
-g_socket_client_set_enable_proxy
+g_socket_client_get_tls
+g_socket_client_get_tls_validation_flags
 <SUBSECTION Standard>
 GSocketClientClass
 G_IS_SOCKET_CLIENT
@@ -3004,3 +3008,127 @@ G_TYPE_POLLABLE_OUTPUT_STREAM
 <SUBSECTION Private>
 g_pollable_output_stream_get_type
 </SECTION>
+
+<SECTION>
+<FILE>gtls</FILE>
+G_TLS_ERROR
+GTlsError
+<SUBSECTION>
+GTlsAuthenticationMode
+GTlsValidationFlags
+</SECTION>
+
+<SECTION>
+<FILE>gtlsbackend</FILE>
+<TITLE>GTlsBackend</FILE>
+G_TLS_BACKEND_EXTENSION_POINT_NAME
+GTlsBackend
+GTlsBackendInterface
+g_tls_backend_get_default
+g_tls_backend_get_certificate_type
+g_tls_backend_get_client_connection_type
+g_tls_backend_get_client_context_type
+g_tls_backend_get_server_connection_type
+g_tls_backend_get_server_context_type
+<SUBSECTION Standard>
+G_IS_TLS_BACKEND
+G_TLS_BACKEND
+G_TLS_BACKEND_GET_INTERFACE
+G_TYPE_TLS_BACKEND
+g_tls_error_quark
+<SUBSECTION Private>
+g_tls_backend_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtlscertificate</FILE>
+<TITLE>GTlsCertificate</TITLE>
+GTlsCertificate
+g_tls_certificate_new
+g_tls_certificate_new_from_pem
+g_tls_certificate_new_from_file
+g_tls_certificate_new_from_files
+g_tls_certificate_list_new_from_file
+g_tls_certificate_get_issuer
+<SUBSECTION Standard>
+GTlsCertificateClass
+GTlsCertificatePrivate
+G_IS_TLS_CERTIFICATE
+G_IS_TLS_CERTIFICATE_CLASS
+G_TLS_CERTIFICATE
+G_TLS_CERTIFICATE_CLASS
+G_TLS_CERTIFICATE_GET_CLASS
+G_TYPE_TLS_CERTIFICATE
+<SUBSECTION Private>
+g_tls_certificate_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtlsconnection</FILE>
+<TITLE>GTlsConnection</TITLE>
+GTlsConnection
+g_tls_connection_set_certificate
+g_tls_connection_get_certificate
+g_tls_connection_get_peer_certificate
+g_tls_connection_set_require_close_notify
+g_tls_connection_get_require_close_notify
+g_tls_connection_get_ca_list
+g_tls_connection_set_ca_list
+g_tls_connection_set_ca_list_from_file
+<SUBSECTION>
+g_tls_connection_handshake
+g_tls_connection_handshake_async
+g_tls_connection_handshake_finish
+<SUBSECTION>
+g_tls_connection_set_peer_certificate
+g_tls_connection_emit_accept_certificate
+g_tls_connection_emit_need_certificate
+<SUBSECTION Standard>
+GTlsConnectionClass
+GTlsConnectionPrivate
+G_IS_TLS_CONNECTION
+G_IS_TLS_CONNECTION_CLASS
+G_TLS_CONNECTION
+G_TLS_CONNECTION_CLASS
+G_TLS_CONNECTION_GET_CLASS
+G_TYPE_TLS_CONNECTION
+<SUBSECTION Private>
+g_tls_connection_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtlsclientconnection</FILE>
+<TITLE>GTlsClientConnection</TITLE>
+GTlsClientConnection
+GTlsClientConnectionInterface
+g_tls_client_connection_new
+g_tls_client_connection_set_server_identity
+g_tls_client_connection_get_server_identity
+g_tls_client_connection_set_validation_flags
+g_tls_client_connection_get_validation_flags
+g_tls_client_connection_set_use_ssl3
+g_tls_client_connection_get_use_ssl3
+g_tls_client_connection_get_accepted_cas
+<SUBSECTION Standard>
+G_IS_TLS_CLIENT_CONNECTION
+G_TLS_CLIENT_CONNECTION
+G_TLS_CLIENT_CONNECTION_GET_INTERFACE
+G_TYPE_TLS_CLIENT_CONNECTION
+<SUBSECTION Private>
+g_tls_client_connection_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gtlsserverconnection</FILE>
+<TITLE>GTlsServerConnection</TITLE>
+GTlsServerConnection
+GTlsServerConnectionInterface
+g_tls_server_connection_new
+<SUBSECTION Standard>
+G_IS_TLS_SERVER_CONNECTION
+G_TLS_SERVER_CONNECTION
+G_TLS_SERVER_CONNECTION_GET_INTERFACE
+G_TYPE_TLS_SERVER_CONNECTION
+<SUBSECTION Private>
+g_tls_server_connection_get_type
+</SECTION>
diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types
index 4436178..4b3afe0 100644
--- a/docs/reference/gio/gio.types
+++ b/docs/reference/gio/gio.types
@@ -108,6 +108,11 @@ g_tcp_connection_get_type
 g_tcp_wrapper_connection_get_type
 g_themed_icon_get_type
 g_threaded_socket_service_get_type
+g_tls_backend_get_type
+g_tls_certificate_get_type
+g_tls_client_connection_get_type
+g_tls_connection_get_type
+g_tls_server_connection_get_type
 g_unix_connection_get_type
 g_unix_fd_list_get_type
 g_unix_fd_message_get_type
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 06c444f..6466159 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -202,7 +202,6 @@ platform_libadd += win32/libgiowin32.la
 platform_deps += win32/libgiowin32.la
 endif
 
-
 SUBDIRS += .
 
 if HAVE_FAM
@@ -307,6 +306,8 @@ libgio_2_0_la_SOURCES =		\
 	gdummyfile.c 		\
 	gdummyproxyresolver.c	\
 	gdummyproxyresolver.h	\
+	gdummytlsbackend.c	\
+	gdummytlsbackend.h	\
 	gemblem.h 		\
 	gemblem.c 		\
 	gemblemedicon.h		\
@@ -382,6 +383,11 @@ libgio_2_0_la_SOURCES =		\
 	gthemedicon.c 		\
 	gthreadedresolver.c	\
 	gthreadedresolver.h	\
+	gtlsbackend.c		\
+	gtlscertificate.c	\
+	gtlsclientconnection.c	\
+	gtlsconnection.c	\
+	gtlsserverconnection.c	\
 	gunionvolumemonitor.c 	\
 	gunionvolumemonitor.h 	\
 	gvfs.c 			\
@@ -532,6 +538,11 @@ gio_headers =			\
 	gtcpwrapperconnection.h \
 	gthreadedsocketservice.h\
 	gthemedicon.h 		\
+	gtlsbackend.h		\
+	gtlscertificate.h	\
+	gtlsclientconnection.h	\
+	gtlsconnection.h	\
+	gtlsserverconnection.h	\
 	gvfs.h 			\
 	gvolume.h 		\
 	gvolumemonitor.h 	\
diff --git a/gio/TLS-NOTES.txt b/gio/TLS-NOTES.txt
new file mode 100644
index 0000000..9923c35
--- /dev/null
+++ b/gio/TLS-NOTES.txt
@@ -0,0 +1,37 @@
+CRLs?
+  - get/set on connection
+
+pkcs11? tokens, etc
+  - GTlsCertificateDatabase, g_tls_certificate_database_get_default()
+  - looking up a cert may invoke password callback
+    - automatically does looking in a thread so callback can block it?
+  - lookup by DN/nick?/other?
+  - get list of available certs
+
+sessions?
+  - NSS handles this transparently on the client side; could make
+    the gnutls backend do this too, via a global (mutexed) sessiondb
+
+misc options
+  - ciphersuites
+  - compression options?
+  - list/enable/disable TLS extensions?
+
+stupid server SSL 3.0 fallback support
+  - via GSocketClient, or higher level?
+  - if this is really only a problem for HTTP then just punt to libsoup
+
+XMPP forbids renegotiation
+  - could be handled by having an about-to-handshake signal, and
+    the handler could kill the connection from there
+
+need a FIPS140 violation error code
+
+"handshaked" signal?
+  autohandshake in a thread so callback can block in signals?
+
+how will the dynamic types work with bindings?
+
+GTlsConnection:validation-errors
+
+g_socket_client_connect_to_uri(): does TLS? when?
diff --git a/gio/gdummytlsbackend.c b/gio/gdummytlsbackend.c
new file mode 100644
index 0000000..62da19e
--- /dev/null
+++ b/gio/gdummytlsbackend.c
@@ -0,0 +1,274 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gdummytlsbackend.h"
+
+#include <glib.h>
+
+#include "gasyncresult.h"
+#include "gcancellable.h"
+#include "ginitable.h"
+#include "gtlsbackend.h"
+#include "gtlscertificate.h"
+#include "gtlsclientconnection.h"
+#include "gtlsserverconnection.h"
+#include "gsimpleasyncresult.h"
+
+#include "giomodule.h"
+#include "giomodule-priv.h"
+
+#include "glibintl.h"
+
+static GType _g_dummy_tls_certificate_get_type (void);
+static GType _g_dummy_tls_connection_get_type (void);
+
+struct _GDummyTlsBackend {
+  GObject parent_instance;
+};
+
+static void g_dummy_tls_backend_iface_init (GTlsBackendInterface *iface);
+
+#define g_dummy_tls_backend_get_type _g_dummy_tls_backend_get_type
+G_DEFINE_TYPE_WITH_CODE (GDummyTlsBackend, g_dummy_tls_backend, G_TYPE_OBJECT,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_TLS_BACKEND,
+						g_dummy_tls_backend_iface_init)
+			 _g_io_modules_ensure_extension_points_registered ();
+			 g_io_extension_point_implement (G_TLS_BACKEND_EXTENSION_POINT_NAME,
+							 g_define_type_id,
+							 "dummy",
+							 -100))
+
+static void
+g_dummy_tls_backend_init (GDummyTlsBackend *backend)
+{
+}
+
+static void
+g_dummy_tls_backend_class_init (GDummyTlsBackendClass *backend_class)
+{
+}
+
+static void
+g_dummy_tls_backend_iface_init (GTlsBackendInterface *iface)
+{
+  iface->get_certificate_type = _g_dummy_tls_certificate_get_type;
+  iface->get_client_connection_type = _g_dummy_tls_connection_get_type;
+  iface->get_server_connection_type = _g_dummy_tls_connection_get_type;
+}
+
+/* Dummy certificate type */
+
+typedef struct _GDummyTlsCertificate      GDummyTlsCertificate;
+typedef struct _GDummyTlsCertificateClass GDummyTlsCertificateClass;
+
+struct _GDummyTlsCertificate {
+  GTlsCertificate parent_instance;
+};
+
+struct _GDummyTlsCertificateClass {
+  GTlsCertificateClass parent_class;
+};
+
+enum
+{
+  PROP_CERTIFICATE_0,
+
+  PROP_CERTIFICATE,
+  PROP_CERTIFICATE_PEM,
+  PROP_PRIVATE_KEY,
+  PROP_PRIVATE_KEY_PEM
+};
+
+static void g_dummy_tls_certificate_initable_iface_init (GInitableIface *iface);
+
+#define g_dummy_tls_certificate_get_type _g_dummy_tls_certificate_get_type
+G_DEFINE_TYPE_WITH_CODE (GDummyTlsCertificate, g_dummy_tls_certificate, G_TYPE_TLS_CERTIFICATE,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+						g_dummy_tls_certificate_initable_iface_init);)
+
+static void
+g_dummy_tls_certificate_get_property (GObject    *object,
+				      guint       prop_id,
+				      GValue     *value,
+				      GParamSpec *pspec)
+{
+  /* We need to define this method to make GObject happy, but it will
+   * never be possible to construct a working GDummyTlsCertificate, so
+   * it doesn't have to do anything useful.
+   */
+}
+
+static void
+g_dummy_tls_certificate_set_property (GObject      *object,
+				      guint         prop_id,
+				      const GValue *value,
+				      GParamSpec   *pspec)
+{
+  /* Just ignore all attempts to set properties. */
+}
+
+static void
+g_dummy_tls_certificate_class_init (GDummyTlsCertificateClass *certificate_class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (certificate_class);
+
+  gobject_class->get_property = g_dummy_tls_certificate_get_property;
+  gobject_class->set_property = g_dummy_tls_certificate_set_property;
+
+  g_object_class_override_property (gobject_class, PROP_CERTIFICATE, "certificate");
+  g_object_class_override_property (gobject_class, PROP_CERTIFICATE_PEM, "certificate-pem");
+  g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
+  g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
+}
+
+static void
+g_dummy_tls_certificate_init (GDummyTlsCertificate *certificate)
+{
+}
+
+static gboolean
+g_dummy_tls_certificate_initable_init (GInitable       *initable,
+				       GCancellable    *cancellable,
+				       GError         **error)
+{
+  g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+		       _("TLS support is not available"));
+  return FALSE;
+}
+
+static void
+g_dummy_tls_certificate_initable_iface_init (GInitableIface  *iface)
+{
+  iface->init = g_dummy_tls_certificate_initable_init;
+}
+
+/* Dummy connection type; since GTlsClientConnection and
+ * GTlsServerConnection are just interfaces, we can implement them
+ * both on a single object.
+ */
+
+typedef struct _GDummyTlsConnection      GDummyTlsConnection;
+typedef struct _GDummyTlsConnectionClass GDummyTlsConnectionClass;
+
+struct _GDummyTlsConnection {
+  GTlsConnection parent_instance;
+};
+
+struct _GDummyTlsConnectionClass {
+  GTlsConnectionClass parent_class;
+};
+
+enum
+{
+  PROP_CONNECTION_0,
+
+  PROP_BASE_IO_STREAM,
+  PROP_CA_LIST,
+  PROP_REQUIRE_CLOSE_NOTIFY,
+  PROP_VALIDATION_FLAGS,
+  PROP_SERVER_IDENTITY,
+  PROP_USE_SSL3,
+  PROP_ACCEPTED_CAS,
+  PROP_AUTHENTICATION_MODE
+};
+
+static void g_dummy_tls_connection_initable_iface_init (GInitableIface *iface);
+
+#define g_dummy_tls_connection_get_type _g_dummy_tls_connection_get_type
+G_DEFINE_TYPE_WITH_CODE (GDummyTlsConnection, g_dummy_tls_connection, G_TYPE_TLS_CONNECTION,
+			 G_IMPLEMENT_INTERFACE (G_TYPE_TLS_CLIENT_CONNECTION, NULL);
+			 G_IMPLEMENT_INTERFACE (G_TYPE_TLS_SERVER_CONNECTION, NULL);
+			 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+						g_dummy_tls_connection_initable_iface_init);)
+
+static void
+g_dummy_tls_connection_get_property (GObject    *object,
+				     guint       prop_id,
+				     GValue     *value,
+				     GParamSpec *pspec)
+{
+}
+
+static void
+g_dummy_tls_connection_set_property (GObject      *object,
+				     guint         prop_id,
+				     const GValue *value,
+				     GParamSpec   *pspec)
+{
+}
+
+static gboolean
+g_dummy_tls_connection_close (GIOStream     *stream,
+			      GCancellable  *cancellable,
+			      GError       **error)
+{
+  return TRUE;
+}
+
+static void
+g_dummy_tls_connection_class_init (GDummyTlsConnectionClass *connection_class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (connection_class);
+  GIOStreamClass *io_stream_class = G_IO_STREAM_CLASS (connection_class);
+
+  gobject_class->get_property = g_dummy_tls_connection_get_property;
+  gobject_class->set_property = g_dummy_tls_connection_set_property;
+
+  /* Need to override this because when initable_init fails it will
+   * dispose the connection, which will close it, which would
+   * otherwise try to close its input/output streams, which don't
+   * exist.
+   */
+  io_stream_class->close_fn = g_dummy_tls_connection_close;
+
+  g_object_class_override_property (gobject_class, PROP_BASE_IO_STREAM, "base-io-stream");
+  g_object_class_override_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY, "require-close-notify");
+  g_object_class_override_property (gobject_class, PROP_CA_LIST, "ca-list");
+  g_object_class_override_property (gobject_class, PROP_VALIDATION_FLAGS, "validation-flags");
+  g_object_class_override_property (gobject_class, PROP_SERVER_IDENTITY, "server-identity");
+  g_object_class_override_property (gobject_class, PROP_USE_SSL3, "use-ssl3");
+  g_object_class_override_property (gobject_class, PROP_ACCEPTED_CAS, "accepted-cas");
+  g_object_class_override_property (gobject_class, PROP_AUTHENTICATION_MODE, "authentication-mode");
+
+}
+
+static void
+g_dummy_tls_connection_init (GDummyTlsConnection *connection)
+{
+}
+
+static gboolean
+g_dummy_tls_connection_initable_init (GInitable       *initable,
+				      GCancellable    *cancellable,
+				      GError         **error)
+{
+  g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+		       _("TLS support is not available"));
+  return FALSE;
+}
+
+static void
+g_dummy_tls_connection_initable_iface_init (GInitableIface  *iface)
+{
+  iface->init = g_dummy_tls_connection_initable_init;
+}
+
diff --git a/gio/gdummytlsbackend.h b/gio/gdummytlsbackend.h
new file mode 100644
index 0000000..099ac36
--- /dev/null
+++ b/gio/gdummytlsbackend.h
@@ -0,0 +1,48 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 Collabora, Ltd.
+ *
+ * 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Nicolas Dufresne <nicolas dufresne collabora co uk>
+ */
+
+#ifndef __G_DUMMY_TLS_BACKEND_H__
+#define __G_DUMMY_TLS_BACKEND_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DUMMY_TLS_BACKEND         (_g_dummy_tls_backend_get_type ())
+#define G_DUMMY_TLS_BACKEND(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DUMMY_TLS_BACKEND, GDummyTlsBackend))
+#define G_DUMMY_TLS_BACKEND_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DUMMY_TLS_BACKEND, GDummyTlsBackendClass))
+#define G_IS_DUMMY_TLS_BACKEND(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DUMMY_TLS_BACKEND))
+#define G_IS_DUMMY_TLS_BACKEND_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DUMMY_TLS_BACKEND))
+#define G_DUMMY_TLS_BACKEND_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DUMMY_TLS_BACKEND, GDummyTlsBackendClass))
+
+typedef struct _GDummyTlsBackend       GDummyTlsBackend;
+typedef struct _GDummyTlsBackendClass  GDummyTlsBackendClass;
+
+struct _GDummyTlsBackendClass {
+  GObjectClass parent_class;
+};
+
+GType _g_dummy_tls_backend_get_type       (void);
+
+G_END_DECLS
+
+#endif /* __G_DUMMY_TLS_BACKEND_H__ */
diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list
index 00d16a9..8a42ad7 100644
--- a/gio/gio-marshal.list
+++ b/gio/gio-marshal.list
@@ -27,3 +27,5 @@ INT:OBJECT
 VOID:INT64
 VOID:UINT64
 BOOLEAN:FLAGS
+BOOLEAN:OBJECT,FLAGS
+OBJECT:VOID
diff --git a/gio/gio.h b/gio/gio.h
index 6f650df..a21dbec 100644
--- a/gio/gio.h
+++ b/gio/gio.h
@@ -121,6 +121,11 @@
 #include <gio/gtcpwrapperconnection.h>
 #include <gio/gthemedicon.h>
 #include <gio/gthreadedsocketservice.h>
+#include <gio/gtlsbackend.h>
+#include <gio/gtlscertificate.h>
+#include <gio/gtlsclientconnection.h>
+#include <gio/gtlsconnection.h>
+#include <gio/gtlsserverconnection.h>
 #include <gio/gvfs.h>
 #include <gio/gvolume.h>
 #include <gio/gvolumemonitor.h>
diff --git a/gio/gio.symbols b/gio/gio.symbols
index eaabd8b..ae96d78 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1065,6 +1065,8 @@ g_dbus_signal_flags_get_type G_GNUC_CONST
 g_dbus_send_message_flags_get_type G_GNUC_CONST
 g_credentials_type_get_type G_GNUC_CONST
 g_dbus_message_byte_order_get_type G_GNUC_CONST
+g_tls_authentication_mode_get_type G_GNUC_CONST
+g_tls_validation_flags_get_type G_GNUC_CONST
 #endif
 #endif
 
@@ -1369,6 +1371,7 @@ g_socket_control_message_serialize
 #if IN_HEADER(__G_SOCKET_CLIENT_H__)
 #if IN_FILE(__G_SOCKET_CLIENT_C__)
 g_socket_client_get_type G_GNUC_CONST
+g_socket_client_add_application_proxy
 g_socket_client_connect
 g_socket_client_connect_async
 g_socket_client_connect_finish
@@ -1381,20 +1384,23 @@ g_socket_client_connect_to_service_finish
 g_socket_client_connect_to_uri
 g_socket_client_connect_to_uri_async
 g_socket_client_connect_to_uri_finish
+g_socket_client_get_enable_proxy
 g_socket_client_get_family
 g_socket_client_get_local_address
 g_socket_client_get_protocol
 g_socket_client_get_socket_type
 g_socket_client_get_timeout
-g_socket_client_get_enable_proxy
+g_socket_client_get_tls
+g_socket_client_get_tls_validation_flags
 g_socket_client_new
+g_socket_client_set_enable_proxy
 g_socket_client_set_family
 g_socket_client_set_local_address
 g_socket_client_set_protocol
 g_socket_client_set_socket_type
 g_socket_client_set_timeout
-g_socket_client_set_enable_proxy
-g_socket_client_add_application_proxy
+g_socket_client_set_tls
+g_socket_client_set_tls_validation_flags
 #endif
 #endif
 
@@ -2004,3 +2010,68 @@ g_tcp_wrapper_connection_get_base_io_stream
 g_tcp_wrapper_connection_new
 #endif
 #endif
+
+#if IN_HEADER(__G_TLS_BACKEND_H__)
+#if IN_FILE(__G_TLS_BACKEND_C__)
+g_tls_backend_get_certificate_type
+g_tls_backend_get_client_connection_type
+g_tls_backend_get_default
+g_tls_backend_get_server_connection_type
+g_tls_backend_get_type G_GNUC_CONST
+g_tls_error_get_type G_GNUC_CONST
+g_tls_error_quark
+#endif
+#endif
+
+#if IN_HEADER(__G_TLS_CERTIFICATE_H__)
+#if IN_FILE(__G_TLS_CERTIFICATE_C__)
+g_tls_certificate_get_issuer
+g_tls_certificate_get_type G_GNUC_CONST
+g_tls_certificate_list_new_from_file
+g_tls_certificate_new
+g_tls_certificate_new_from_file
+g_tls_certificate_new_from_files
+g_tls_certificate_new_from_pem
+#endif
+#endif
+
+#if IN_HEADER(__G_TLS_CONNECTION_H__)
+#if IN_FILE(__G_TLS_CONNECTION_C__)
+g_tls_connection_emit_accept_certificate
+g_tls_connection_emit_need_certificate
+g_tls_connection_get_ca_list
+g_tls_connection_get_certificate
+g_tls_connection_get_peer_certificate
+g_tls_connection_get_require_close_notify
+g_tls_connection_get_type G_GNUC_CONST
+g_tls_connection_handshake
+g_tls_connection_handshake_async
+g_tls_connection_handshake_finish
+g_tls_connection_set_ca_list
+g_tls_connection_set_ca_list_from_file
+g_tls_connection_set_certificate
+g_tls_connection_set_peer_certificate
+g_tls_connection_set_require_close_notify
+#endif
+#endif
+
+#if IN_HEADER(__G_TLS_CLIENT_CONNECTION_H__)
+#if IN_FILE(__G_TLS_CLIENT_CONNECTION_C__)
+g_tls_client_connection_get_accepted_cas
+g_tls_client_connection_get_server_identity
+g_tls_client_connection_get_type G_GNUC_CONST
+g_tls_client_connection_get_use_ssl3
+g_tls_client_connection_get_validation_flags
+g_tls_client_connection_new
+g_tls_client_connection_set_server_identity
+g_tls_client_connection_set_use_ssl3
+g_tls_client_connection_set_validation_flags
+#endif
+#endif
+
+#if IN_HEADER(__G_TLS_SERVER_CONNECTION_H__)
+#if IN_FILE(__G_TLS_SERVER_CONNECTION_C__)
+g_tls_server_connection_get_type G_GNUC_CONST
+g_tls_server_connection_new
+#endif
+#endif
diff --git a/gio/gioenums.h b/gio/gioenums.h
index d534eed..57fe5c6 100644
--- a/gio/gioenums.h
+++ b/gio/gioenums.h
@@ -1249,6 +1249,87 @@ typedef enum
   G_APPLICATION_SEND_ENVIRONMENT    =  (1 << 4)
 } GApplicationFlags;
 
+/**
+ * GTlsError:
+ * @G_TLS_ERROR_MISC: Miscellaneous TLS error
+ * @G_TLS_ERROR_BAD_CERTIFICATE: A certificate could not be parsed
+ * @G_TLS_ERROR_NOT_TLS: The TLS handshake failed because the
+ *   peer does not seem to be a TLS server.
+ * @G_TLS_ERROR_HANDSHAKE: The TLS handshake failed because the
+ *   peer's certificate was not acceptable.
+ * @G_TLS_ERROR_CERTIFICATE_REQUIRED: The TLS handshake failed because
+ *   the server requested a client-side certificate, but none was
+ *   provided. See #GTlsConnection::need-certificate.
+ * @G_TLS_ERROR_EOF: The TLS connection was closed without proper
+ *   notice, which may indicate an attack. See
+ *   g_tls_connection_set_require_close_notify().
+ *
+ * An error code used with %G_TLS_ERROR in a #GError returned from a
+ * TLS-related routine.
+ *
+ * Since: 2.28
+ */
+typedef enum {
+  G_TLS_ERROR_MISC,
+  G_TLS_ERROR_BAD_CERTIFICATE,
+  G_TLS_ERROR_NOT_TLS,
+  G_TLS_ERROR_HANDSHAKE,
+  G_TLS_ERROR_CERTIFICATE_REQUIRED,
+  G_TLS_ERROR_EOF
+} GTlsError;
+
+/**
+ * GTlsValidationFlags:
+ * @G_TLS_VALIDATE_GENERIC_ERROR: Generic error
+ * @G_TLS_VALIDATE_CA: The signing certificate authority is
+ *   not known.
+ * @G_TLS_VALIDATE_IDENTITY: The certificate does not match the
+ *   expected identity of the site that it was retrieved from.
+ * @G_TLS_VALIDATE_ACTIVATION: The certificate's activation time
+ *   is still in the future
+ * @G_TLS_VALIDATE_EXPIRATION: The certificate has expired
+ * @G_TLS_VALIDATE_REVOCATION: The certificate has been revoked
+ *   according to the #GTlsContext's certificate revocation list.
+ * @G_TLS_VALIDATE_ALGORITHM: The certificate's algorithm is
+ *   considered insecure.
+ * @G_TLS_VALIDATE_ALL: the combination of all of the above flags
+ *
+ * A set of flags describing TLS certification validation. This can be
+ * used to set which validation steps to perform (eg, with
+ * g_tls_client_connection_set_validation_flags()), or to describe why
+ * a particular certificate was rejected (eg, in
+ * #GTlsConnection::accept-certificate).
+ *
+ * Since: 2.28
+ */
+typedef enum {
+  G_TLS_VALIDATE_GENERIC_ERROR = (2 << 0),
+  G_TLS_VALIDATE_CA            = (2 << 1),
+  G_TLS_VALIDATE_IDENTITY      = (2 << 2),
+  G_TLS_VALIDATE_ACTIVATION    = (2 << 3),
+  G_TLS_VALIDATE_EXPIRATION    = (2 << 4),
+  G_TLS_VALIDATE_REVOCATION    = (2 << 5),
+  G_TLS_VALIDATE_ALGORITHM     = (2 << 6),
+
+  G_TLS_VALIDATE_ALL           = 0x003f
+} GTlsValidationFlags;
+
+/**
+ * GTlsAuthenticationMode:
+ * @G_TLS_AUTHENTICATION_NONE: client authentication not required
+ * @G_TLS_AUTHENTICATION_REQUESTED: client authentication is requested
+ * @G_TLS_AUTHENTICATION_REQUIRED: client authentication is required
+ *
+ * The client authentication mode for a #GTlsServerConnection.
+ *
+ * Since: 2.28
+ */
+typedef enum {
+  G_TLS_AUTHENTICATION_NONE,
+  G_TLS_AUTHENTICATION_REQUESTED,
+  G_TLS_AUTHENTICATION_REQUIRED
+} GTlsAuthenticationMode;
+
 G_END_DECLS
 
 #endif /* __GIO_ENUMS_H__ */
diff --git a/gio/giomodule.c b/gio/giomodule.c
index d131893..ecdaab4 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -35,6 +35,7 @@
 #include "gsocks4proxy.h"
 #include "gsocks4aproxy.h"
 #include "gsocks5proxy.h"
+#include "gtlsbackend.h"
 #include "gvfs.h"
 #ifdef G_OS_WIN32
 #include "gregistrysettingsbackend.h"
@@ -480,6 +481,7 @@ extern GType g_win32_directory_monitor_get_type (void);
 extern GType _g_winhttp_vfs_get_type (void);
 
 extern GType _g_dummy_proxy_resolver_get_type (void);
+extern GType _g_dummy_tls_backend_get_type (void);
 
 #ifdef G_PLATFORM_WIN32
 
@@ -556,6 +558,9 @@ _g_io_modules_ensure_extension_points_registered (void)
 
       ep = g_io_extension_point_register (G_PROXY_EXTENSION_POINT_NAME);
       g_io_extension_point_set_required_type (ep, G_TYPE_PROXY);
+
+      ep = g_io_extension_point_register (G_TLS_BACKEND_EXTENSION_POINT_NAME);
+      g_io_extension_point_set_required_type (ep, G_TYPE_TLS_BACKEND);
     }
   
   G_UNLOCK (registered_extensions);
@@ -618,6 +623,7 @@ _g_io_modules_ensure_loaded (void)
       _g_socks4a_proxy_get_type ();
       _g_socks4_proxy_get_type ();
       _g_socks5_proxy_get_type ();
+      _g_dummy_tls_backend_get_type ();
     }
 
   G_UNLOCK (loaded_dirs);
diff --git a/gio/giotypes.h b/gio/giotypes.h
index cb48a12..178b72b 100644
--- a/gio/giotypes.h
+++ b/gio/giotypes.h
@@ -109,6 +109,7 @@ typedef struct _GIOExtension                  GIOExtension;
  * Opaque class for definining and scheduling IO jobs.
  **/
 typedef struct _GIOSchedulerJob               GIOSchedulerJob;
+typedef struct _GIOStreamAdapter              GIOStreamAdapter;
 typedef struct _GLoadableIcon                 GLoadableIcon; /* Dummy typedef */
 typedef struct _GMemoryInputStream            GMemoryInputStream;
 typedef struct _GMemoryOutputStream           GMemoryOutputStream;
@@ -203,6 +204,13 @@ typedef struct _GTcpWrapperConnection                       GTcpWrapperConnectio
  **/
 typedef struct _GThreadedSocketService                      GThreadedSocketService;
 typedef struct _GThemedIcon                   GThemedIcon;
+typedef struct _GTlsCertificate               GTlsCertificate;
+typedef struct _GTlsClientConnection          GTlsClientConnection; /* Dummy typedef */
+typedef struct _GTlsClientContext             GTlsClientContext; /* Dummy typedef */
+typedef struct _GTlsConnection                GTlsConnection;
+typedef struct _GTlsContext                   GTlsContext;
+typedef struct _GTlsServerConnection          GTlsServerConnection; /* Dummy typedef */
+typedef struct _GTlsServerContext             GTlsServerContext; /* Dummy typedef */
 typedef struct _GVfs                          GVfs; /* Dummy typedef */
 
 /**
diff --git a/gio/gsocketclient.c b/gio/gsocketclient.c
index 30e677a..5ea7926 100644
--- a/gio/gsocketclient.c
+++ b/gio/gsocketclient.c
@@ -45,6 +45,8 @@
 #include <gio/gsocketaddress.h>
 #include <gio/gtcpconnection.h>
 #include <gio/gtcpwrapperconnection.h>
+#include <gio/gtlscertificate.h>
+#include <gio/gtlsclientconnection.h>
 #include "glibintl.h"
 
 
@@ -80,6 +82,8 @@ enum
   PROP_LOCAL_ADDRESS,
   PROP_TIMEOUT,
   PROP_ENABLE_PROXY,
+  PROP_TLS,
+  PROP_TLS_VALIDATION_FLAGS
 };
 
 struct _GSocketClientPrivate
@@ -91,6 +95,8 @@ struct _GSocketClientPrivate
   guint timeout;
   gboolean enable_proxy;
   GHashTable *app_proxies;
+  gboolean tls;
+  GTlsValidationFlags tls_validation_flags;
 };
 
 static GSocket *
@@ -219,6 +225,14 @@ g_socket_client_get_property (GObject    *object,
 	g_value_set_boolean (value, client->priv->enable_proxy);
 	break;
 
+      case PROP_TLS:
+	g_value_set_boolean (value, g_socket_client_get_tls (client));
+	break;
+
+      case PROP_TLS_VALIDATION_FLAGS:
+	g_value_set_flags (value, g_socket_client_get_tls_validation_flags (client));
+	break;
+
       default:
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -258,6 +272,14 @@ g_socket_client_set_property (GObject      *object,
       g_socket_client_set_enable_proxy (client, g_value_get_boolean (value));
       break;
 
+    case PROP_TLS:
+      g_socket_client_set_tls (client, g_value_get_boolean (value));
+      break;
+
+    case PROP_TLS_VALIDATION_FLAGS:
+      g_socket_client_set_tls_validation_flags (client, g_value_get_flags (value));
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     }
@@ -526,6 +548,91 @@ g_socket_client_set_enable_proxy (GSocketClient *client,
   g_object_notify (G_OBJECT (client), "enable-proxy");
 }
 
+/**
+ * g_socket_client_get_tls:
+ * @client: a #GSocketClient.
+ *
+ * Gets whether @client creates TLS connections. See
+ * g_socket_client_set_tls() for details.
+ *
+ * Returns: whether @client uses TLS
+ *
+ * Since: 2.28
+ */
+gboolean
+g_socket_client_get_tls (GSocketClient *client)
+{
+  return client->priv->tls;
+}
+
+/**
+ * g_socket_client_set_tls:
+ * @client: a #GSocketClient.
+ * @tls: whether to use TLS
+ *
+ * Sets whether @client creates TLS (aka SSL) connections. If @tls is
+ * %TRUE, @client will wrap its connections in a #GTlsClientConnection
+ * and perform a TLS handshake when connecting.
+ *
+ * Note that since #GSocketClient must return a #GSocketConnection,
+ * but #GTlsClientConnection is not a #GSocketConnection, this
+ * actually wraps the resulting #GTlsClientConnection in a
+ * #GTcpWrapperConnection when returning it. You can use
+ * g_tcp_wrapper_connection_get_base_io_stream() on the return value
+ * to extract the #GTlsClientConnection.
+ *
+ * Since: 2.28
+ */
+void
+g_socket_client_set_tls (GSocketClient *client,
+			 gboolean       tls)
+{
+  tls = !!tls;
+  if (tls == client->priv->tls)
+    return;
+
+  client->priv->tls = tls;
+  g_object_notify (G_OBJECT (client), "tls");
+}
+
+/**
+ * g_socket_client_get_tls_validation_flags:
+ * @client: a #GSocketClient.
+ *
+ * Gets the TLS validation flags used creating TLS connections via
+ * @client.
+ *
+ * Returns: the TLS validation flags
+ *
+ * Since: 2.26
+ */
+GTlsValidationFlags
+g_socket_client_get_tls_validation_flags (GSocketClient *client)
+{
+  return client->priv->tls_validation_flags;
+}
+
+/**
+ * g_socket_client_set_tls_validation_flags:
+ * @client: a #GSocketClient.
+ * @flags: the validation flags
+ *
+ * Sets the TLS validation flags used when creating TLS connections
+ * via @client. The default value is %G_TLS_VALIDATE_ALL.
+ *
+ * Since: 2.26
+ */
+void
+g_socket_client_set_tls_validation_flags (GSocketClient       *client,
+					  GTlsValidationFlags  flags)
+{
+  if (client->priv->tls_validation_flags != flags)
+    {
+      client->priv->tls_validation_flags = flags;
+      g_object_notify (G_OBJECT (client), "tls-validation-flags");
+    }
+}
+
 static void
 g_socket_client_class_init (GSocketClientClass *class)
 {
@@ -594,6 +701,23 @@ g_socket_client_class_init (GSocketClientClass *class)
 							  G_PARAM_READWRITE |
 							  G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_class, PROP_TLS,
+				   g_param_spec_boolean ("tls",
+							 P_("TLS"),
+							 P_("Whether to create TLS connections"),
+							 FALSE,
+							 G_PARAM_CONSTRUCT |
+							 G_PARAM_READWRITE |
+							 G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_class, PROP_TLS_VALIDATION_FLAGS,
+				   g_param_spec_flags ("tls-validation-flags",
+						       P_("TLS validation flags"),
+						       P_("TLS validation flags to use"),
+						       G_TYPE_TLS_VALIDATION_FLAGS,
+						       G_TLS_VALIDATE_ALL,
+						       G_PARAM_CONSTRUCT |
+						       G_PARAM_READWRITE |
+						       G_PARAM_STATIC_STRINGS));
 }
 
 /**
@@ -632,7 +756,7 @@ g_socket_client_connect (GSocketClient       *client,
 			 GCancellable        *cancellable,
 			 GError             **error)
 {
-  GSocketConnection *connection = NULL;
+  GIOStream *connection = NULL;
   GSocketAddressEnumerator *enumerator = NULL;
   GError *last_error, *tmp_error;
 
@@ -687,7 +811,7 @@ g_socket_client_connect (GSocketClient       *client,
 	}
 
       if (g_socket_connect (socket, address, cancellable, &last_error))
-	connection = g_socket_connection_factory_create_connection (socket);
+	connection = (GIOStream *)g_socket_connection_factory_create_connection (socket);
 
       if (connection &&
 	  G_IS_PROXY_ADDRESS (address) &&
@@ -717,31 +841,16 @@ g_socket_client_connect (GSocketClient       *client,
             }
           else if (proxy)
 	    {
-              GIOStream *io_stream;
+              GIOStream *proxy_connection;
 
-	      io_stream = g_proxy_connect (proxy,
-					   G_IO_STREAM (connection),
-					   proxy_addr,
-					   cancellable,
-					   &last_error);
+	      proxy_connection = g_proxy_connect (proxy,
+						  connection,
+						  proxy_addr,
+						  cancellable,
+						  &last_error);
 	      g_object_unref (connection);
+	      connection = proxy_connection;
 	      g_object_unref (proxy);
-
-              if (io_stream)
-                {
-                  if (G_IS_SOCKET_CONNECTION (io_stream))
-                    connection = G_SOCKET_CONNECTION (io_stream);
-                  else
-		    {
-		      connection = g_tcp_wrapper_connection_new (G_POLLABLE_IO_STREAM (io_stream),
-								 socket);
-		      g_object_unref (io_stream);
-		    }
-                }
-              else
-                {
-                  connection = NULL;
-                }
 	    }
 	  else if (!g_hash_table_lookup_extended (client->priv->app_proxies,
 						  protocol, NULL, NULL))
@@ -754,12 +863,44 @@ g_socket_client_connect (GSocketClient       *client,
 	    }
 	}
 
+      if (connection && client->priv->tls)
+	{
+	  GTlsClientConnection *tlsconn;
+
+	  tlsconn = g_tls_client_connection_new (&last_error,
+						 "base-io-stream", connection,
+						 "server-identity", connectable,
+						 "validation-flags", client->priv->tls_validation_flags,
+						 NULL);
+	  g_object_unref (connection);
+	  connection = (GIOStream *)tlsconn;
+
+	  if (tlsconn)
+	    {
+	      if (!g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn),
+					       cancellable, &last_error))
+		{
+		  g_object_unref (tlsconn);
+		  connection = NULL;
+		}
+	    }
+	}
+
+      if (connection && !G_IS_SOCKET_CONNECTION (connection))
+	{
+	  GSocketConnection *wrapper_connection;
+
+	  wrapper_connection = g_tcp_wrapper_connection_new (G_POLLABLE_IO_STREAM (connection), socket);
+	  g_object_unref (connection);
+	  connection = (GIOStream *)wrapper_connection;
+	}
+
       g_object_unref (socket);
       g_object_unref (address);
     }
   g_object_unref (enumerator);
 
-  return connection;
+  return G_SOCKET_CONNECTION (connection);
 }
 
 /**
@@ -927,10 +1068,11 @@ typedef struct
   GCancellable *cancellable;
   GSocketClient *client;
 
+  GSocketConnectable *connectable;
   GSocketAddressEnumerator *enumerator;
   GProxyAddress *proxy_addr;
   GSocket *current_socket;
-  GSocketConnection *connection;
+  GIOStream *connection;
 
   GError *last_error;
 } GSocketClientAsyncConnectData;
@@ -946,6 +1088,16 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
     {
       g_assert (data->connection);
 
+      if (!G_IS_SOCKET_CONNECTION (data->connection))
+	{
+	  GSocketConnection *wrapper_connection;
+
+	  wrapper_connection = g_tcp_wrapper_connection_new (G_POLLABLE_IO_STREAM (data->connection),
+							     data->current_socket);
+	  g_object_unref (data->connection);
+	  data->connection = (GIOStream *)wrapper_connection;
+	}
+
       g_simple_async_result_set_op_res_gpointer (data->result,
 						 data->connection,
 						 g_object_unref);
@@ -953,6 +1105,7 @@ g_socket_client_async_connect_complete (GSocketClientAsyncConnectData *data)
 
   g_simple_async_result_complete (data->result);
   g_object_unref (data->result);
+  g_object_unref (data->connectable);
   g_object_unref (data->enumerator);
   if (data->cancellable)
     g_object_unref (data->cancellable);
@@ -987,45 +1140,104 @@ enumerator_next_async (GSocketClientAsyncConnectData *data)
 }
 
 static void
-g_socket_client_proxy_connect_callback (GObject      *object,
+g_socket_client_tls_handshake_callback (GObject      *object,
 					GAsyncResult *result,
 					gpointer      user_data)
 {
   GSocketClientAsyncConnectData *data = user_data;
-  GIOStream *io_stream;
 
-  io_stream = g_proxy_connect_finish (G_PROXY (object),
-				      result,
-				      &data->last_error);
-  g_object_unref (data->connection);
+  if (g_tls_connection_handshake_finish (G_TLS_CONNECTION (object),
+					 result,
+					 &data->last_error))
+    {
+      g_object_unref (data->connection);
+      data->connection = G_IO_STREAM (object);
+    }
+  else
+    {
+      g_object_unref (object);
+      g_object_unref (data->current_socket);
+      data->current_socket = NULL;
+      g_object_unref (data->connection);
+      data->connection = NULL;
+
+      enumerator_next_async (data);
+    }
+
+  g_socket_client_async_connect_complete (data);
+}
+
+static void
+g_socket_client_tls_handshake (GSocketClientAsyncConnectData *data)
+{
+  GTlsClientConnection *tlsconn;
 
-  if (io_stream)
+  if (!data->client->priv->tls)
     {
-      if (G_IS_SOCKET_CONNECTION (io_stream))
-        data->connection = G_SOCKET_CONNECTION (io_stream);
-      else
-	{
-	  data->connection = g_tcp_wrapper_connection_new (G_POLLABLE_IO_STREAM (io_stream),
-							   data->current_socket);
-	  g_object_unref (io_stream);
-	}
+      g_socket_client_async_connect_complete (data);
+      return;
+    }
+
+  tlsconn = g_tls_client_connection_new (&data->last_error,
+					 "base-io-stream", data->connection,
+					 "server-identity", data->connectable,
+					 "validation-flags", data->client->priv->tls_validation_flags,
+					 NULL);
+  if (tlsconn)
+    {
+      g_tls_connection_handshake_async (G_TLS_CONNECTION (tlsconn),
+					G_PRIORITY_DEFAULT,
+					data->cancellable,
+					g_socket_client_tls_handshake_callback,
+					data);
     }
   else
     {
+      g_object_unref (data->current_socket);
+      data->current_socket = NULL;
+      g_object_unref (data->connection);
       data->connection = NULL;
+
+      enumerator_next_async (data);
+    }
+}
+
+static void
+g_socket_client_proxy_connect_callback (GObject      *object,
+					GAsyncResult *result,
+					gpointer      user_data)
+{
+  GSocketClientAsyncConnectData *data = user_data;
+
+  g_object_unref (data->connection);
+  data->connection = g_proxy_connect_finish (G_PROXY (object),
+					     result,
+					     &data->last_error);
+  if (!data->connection)
+    {
       g_object_unref (data->current_socket);
       data->current_socket = NULL;
+
+      enumerator_next_async (data);
+      return;
     }
 
-  g_socket_client_async_connect_complete (data);
+  g_socket_client_tls_handshake (data);
 }
 
 static void
 g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data)
 {
   GProxy *proxy;
-  const gchar *protocol = g_proxy_address_get_protocol (data->proxy_addr);
+  const gchar *protocol;
+
+  if (!data->proxy_addr)
+    {
+      g_socket_client_tls_handshake (data);
+      return;
+    }
 
+  protocol  = g_proxy_address_get_protocol (data->proxy_addr);
   proxy = g_proxy_get_default_for_protocol (protocol);
 
   /* The connection should not be anything else then TCP Connection,
@@ -1050,7 +1262,7 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data)
   else if (proxy)
     {
       g_proxy_connect_async (proxy,
-                             G_IO_STREAM (data->connection),
+                             data->connection,
                              data->proxy_addr,
                              data->cancellable,
                              g_socket_client_proxy_connect_callback,
@@ -1066,6 +1278,8 @@ g_socket_client_proxy_connect (GSocketClientAsyncConnectData *data)
           _("Proxy protocol '%s' is not supported."),
           protocol);
 
+      g_object_unref (data->current_socket);
+      data->current_socket = NULL;
       g_object_unref (data->connection);
       data->connection = NULL;
       g_object_unref (data->current_socket);
@@ -1080,13 +1294,10 @@ g_socket_client_socket_connected (GSocketClientAsyncConnectData *data)
 {
   g_socket_set_blocking (data->current_socket, TRUE);
 
-  data->connection =
+  data->connection = (GIOStream *)
     g_socket_connection_factory_create_connection (data->current_socket);
 
-  if (data->proxy_addr)
-    g_socket_client_proxy_connect (data);
-  else
-    g_socket_client_async_connect_complete (data);
+  g_socket_client_proxy_connect (data);
 }
 
 static gboolean
@@ -1243,6 +1454,10 @@ g_socket_client_connect_async (GSocketClient       *client,
   data->client = client;
   if (cancellable)
     data->cancellable = g_object_ref (cancellable);
+  else
+    data->cancellable = NULL;
+  data->last_error = NULL;
+  data->connectable = g_object_ref (connectable);
 
   if (can_use_proxy (client))
       data->enumerator = g_socket_connectable_proxy_enumerate (connectable);
diff --git a/gio/gsocketclient.h b/gio/gsocketclient.h
index 2586f70..f71c02e 100644
--- a/gio/gsocketclient.h
+++ b/gio/gsocketclient.h
@@ -89,6 +89,13 @@ gboolean                g_socket_client_get_enable_proxy                (GSocket
 void                    g_socket_client_set_enable_proxy                (GSocketClient        *client,
     									 gboolean	      enable);
 
+gboolean                g_socket_client_get_tls                         (GSocketClient        *client);
+void                    g_socket_client_set_tls                         (GSocketClient        *client,
+									 gboolean              tls);
+GTlsValidationFlags     g_socket_client_get_tls_validation_flags        (GSocketClient        *client);
+void                    g_socket_client_set_tls_validation_flags        (GSocketClient        *client,
+									 GTlsValidationFlags   flags);
+
 GSocketConnection *     g_socket_client_connect                         (GSocketClient        *client,
                                                                          GSocketConnectable   *connectable,
                                                                          GCancellable         *cancellable,
diff --git a/gio/gtlsbackend.c b/gio/gtlsbackend.c
new file mode 100644
index 0000000..5245d68
--- /dev/null
+++ b/gio/gtlsbackend.c
@@ -0,0 +1,177 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include "gtlsbackend.h"
+#include "gioenumtypes.h"
+#include "giomodule-priv.h"
+
+/**
+ * SECTION:gtls
+ * @title: TLS Overview
+ * @short_description: TLS (aka SSL) support for #GSocketConnection
+ * @include: gio/gio.h
+ *
+ * #GTlsConnection and related classes provide TLS (Transport Layer
+ * Security, previously known as SSL, Secure Sockets Layer) support for
+ * gio-based network streams.
+ *
+ * In the simplest case, for a client connection, you can just set the
+ * #GSocketClient:tls flag on a #GSocketClient, and then any
+ * connections created by that client will have TLS negotiated
+ * automatically, using appropriate default settings, and rejecting
+ * any invalid or self-signed certificates (unless you change that
+ * default by setting the #GSocketClient:tls-validation-flags
+ * property). The returned object will be a #GTcpWrapperConnection,
+ * which wraps the underlying #GTlsClientConnection.
+ *
+ * For greater control, you can create your own #GTlsClientConnection,
+ * wrapping a #GSocketConnection (or an arbitrary #GPollableIOStream)
+ * and then connect to its signals, such as
+ * #GTlsConnection::accept-certificate, before starting the handshake.
+ *
+ * Server-side TLS is similar, using #GTlsServerConnection. At the
+ * moment, there is no support for automatically wrapping server-side
+ * connections in the way #GSocketClient does for client-side
+ * connections.
+ */
+
+/**
+ * SECTION:gtlsbackend
+ * @title: GTlsBackend
+ * @short_description: TLS backend implementation
+ * @include: gio/gio.h
+ */
+
+/**
+ * GTlsBackend:
+ *
+ * Type implemented by TLS #GIOModules to provide access to additional
+ * TLS-related types.
+ *
+ * Since: 2.28
+ */
+
+G_DEFINE_INTERFACE (GTlsBackend, g_tls_backend, G_TYPE_OBJECT);
+
+static void
+g_tls_backend_default_init (GTlsBackendInterface *iface)
+{
+}
+
+static gpointer
+get_default_tls_backend (gpointer arg)
+{
+  const char *use_this;
+  GList *extensions;
+  GIOExtensionPoint *ep;
+  GIOExtension *extension;
+
+  _g_io_modules_ensure_loaded ();
+
+  ep = g_io_extension_point_lookup (G_TLS_BACKEND_EXTENSION_POINT_NAME);
+
+  use_this = g_getenv ("GIO_USE_TLS");
+  if (use_this)
+    {
+      extension = g_io_extension_point_get_extension_by_name (ep, use_this);
+      if (extension)
+	return g_object_new (g_io_extension_get_type (extension), NULL);
+    }
+
+  extensions = g_io_extension_point_get_extensions (ep);
+  if (extensions)
+    {
+      extension = extensions->data;
+      return g_object_new (g_io_extension_get_type (extension), NULL);
+    }
+
+  return NULL;
+}
+
+/**
+ * g_tls_backend_get_default:
+ * 
+ * Gets the default #GTlsBackend for the system.
+ * 
+ * Returns: a #GTlsBackend
+ *
+ * Since: 2.28
+ */
+GTlsBackend *
+g_tls_backend_get_default (void)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  
+  return g_once (&once_init, get_default_tls_backend, NULL);
+}
+
+/**
+ * g_tls_backend_get_certificate_type:
+ * @backend: the #GTlsBackend
+ *
+ * Gets the #GType of @backend's #GTlsCertificate implementation.
+ *
+ * Return value: the #GType of @backend's #GTlsCertificate
+ *   implementation.
+ *
+ * Since: 2.28
+ */
+GType
+g_tls_backend_get_certificate_type (GTlsBackend *backend)
+{
+  return G_TLS_BACKEND_GET_INTERFACE (backend)->get_certificate_type ();
+}
+
+/**
+ * g_tls_backend_get_client_connection_type:
+ * @backend: the #GTlsBackend
+ *
+ * Gets the #GType of @backend's #GTlsClientConnection implementation.
+ *
+ * Return value: the #GType of @backend's #GTlsClientConnection
+ *   implementation.
+ *
+ * Since: 2.28
+ */
+GType
+g_tls_backend_get_client_connection_type (GTlsBackend *backend)
+{
+  return G_TLS_BACKEND_GET_INTERFACE (backend)->get_client_connection_type ();
+}
+
+/**
+ * g_tls_backend_get_server_connection_type:
+ * @backend: the #GTlsBackend
+ *
+ * Gets the #GType of @backend's #GTlsServerConnection implementation.
+ *
+ * Return value: the #GType of @backend's #GTlsServerConnection
+ *   implementation.
+ *
+ * Since: 2.28
+ */
+GType
+g_tls_backend_get_server_connection_type (GTlsBackend *backend)
+{
+  return G_TLS_BACKEND_GET_INTERFACE (backend)->get_server_connection_type ();
+}
diff --git a/gio/gtlsbackend.h b/gio/gtlsbackend.h
new file mode 100644
index 0000000..891fa49
--- /dev/null
+++ b/gio/gtlsbackend.h
@@ -0,0 +1,89 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2009 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_BACKEND_H__
+#define __G_TLS_BACKEND_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+/**
+ * G_TLS_BACKEND_EXTENSION_POINT_NAME:
+ *
+ * Extension point for TLS functionality via #GTlsBackend.
+ * See <link linkend="extending-gio">Extending GIO</link>.
+ */
+#define G_TLS_BACKEND_EXTENSION_POINT_NAME "gio-tls-backend"
+
+#define G_TYPE_TLS_BACKEND               (g_tls_backend_get_type ())
+#define G_TLS_BACKEND(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_TLS_BACKEND, GTlsBackend))
+#define G_IS_TLS_BACKEND(obj)	         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_TLS_BACKEND))
+#define G_TLS_BACKEND_GET_INTERFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_TLS_BACKEND, GTlsBackendInterface))
+
+/**
+ * GTlsBackend:
+ *
+ * TLS (Transport Layer Security, aka SSL) backend. This is an
+ * internal type used to coordinate the different classes implemented
+ * by a TLS backend.
+ *
+ * Since: 2.28
+ */
+
+typedef struct _GTlsBackend          GTlsBackend;
+typedef struct _GTlsBackendInterface GTlsBackendInterface;
+
+/**
+ * GTlsBackendInterface:
+ * @g_iface: The parent interface.
+ * @get_certificate_type: returns the #GTlsCertificate implementation type
+ * @get_client_connection_type: returns the #GTlsClientConnection implementation type
+ * @get_server_connection_type: returns the #GTlsServerConnection implementation type
+ *
+ * Provides an interface for describing TLS-related types.
+ *
+ * Since: 2.28
+ */
+struct _GTlsBackendInterface
+{
+  GTypeInterface g_iface;
+
+  /* methods */
+  GType ( *get_certificate_type)       (void);
+  GType ( *get_client_connection_type) (void);
+  GType ( *get_server_connection_type) (void);
+};
+
+GType        g_tls_backend_get_type                   (void) G_GNUC_CONST;
+
+GTlsBackend *g_tls_backend_get_default                (void);
+
+GType        g_tls_backend_get_certificate_type       (GTlsBackend *backend);
+GType        g_tls_backend_get_client_connection_type (GTlsBackend *backend);
+GType        g_tls_backend_get_server_connection_type (GTlsBackend *backend);
+
+G_END_DECLS
+
+#endif /* __G_TLS_BACKEND_H__ */
diff --git a/gio/gtlscertificate.c b/gio/gtlscertificate.c
new file mode 100644
index 0000000..d154cd2
--- /dev/null
+++ b/gio/gtlscertificate.c
@@ -0,0 +1,508 @@
+/* GIO - GLib Input, Output and Certificateing Library
+ *
+ * Copyright (C) 2009 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gtlscertificate.h"
+
+#include <string.h>
+#include "ginitable.h"
+#include "gtlsbackend.h"
+#include "gtlsconnection.h"
+#include "glibintl.h"
+
+/**
+ * SECTION: gtlscertificate
+ * @title: GTlsCertificate
+ * @short_description: a TLS certificate
+ * @see_also: #GTlsConnection
+ *
+ * A certificate used for TLS authentication and encryption.
+ * This can represent either a public key only (eg, the certificate
+ * received by a client from a server), or the combination of
+ * a public key and a private key (which is needed when acting as a
+ * #GTlsServerConnection).
+ *
+ * Since: 2.28
+ */
+
+/**
+ * GTlsCertificate:
+ *
+ * Abstract base class for TLS certificate types.
+ *
+ * Since: 2.28
+ */
+
+G_DEFINE_ABSTRACT_TYPE (GTlsCertificate, g_tls_certificate, G_TYPE_OBJECT);
+
+struct _GTlsCertificatePrivate
+{
+  GTlsCertificate *issuer;
+};
+
+enum
+{
+  PROP_0,
+
+  PROP_CERTIFICATE,
+  PROP_CERTIFICATE_PEM,
+  PROP_PRIVATE_KEY,
+  PROP_PRIVATE_KEY_PEM,
+  PROP_ISSUER
+};
+
+static void
+g_tls_certificate_init (GTlsCertificate *cert)
+{
+  cert->priv = G_TYPE_INSTANCE_GET_PRIVATE (cert,
+					    G_TYPE_TLS_CERTIFICATE,
+					    GTlsCertificatePrivate);
+}
+
+static void
+g_tls_certificate_get_property (GObject    *object,
+				guint       prop_id,
+				GValue     *value,
+				GParamSpec *pspec)
+{
+  GTlsCertificate *cert = G_TLS_CERTIFICATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_ISSUER:
+      g_value_set_object (value, cert->priv->issuer);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_certificate_set_property (GObject      *object,
+				guint         prop_id,
+				const GValue *value,
+				GParamSpec   *pspec)
+{
+  GTlsCertificate *cert = G_TLS_CERTIFICATE (object);
+
+  switch (prop_id)
+    {
+    case PROP_ISSUER:
+      cert->priv->issuer = g_value_dup_object (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+g_tls_certificate_finalize (GObject *object)
+{
+  GTlsCertificate *cert = G_TLS_CERTIFICATE (object);
+
+  if (cert->priv->issuer)
+    g_object_unref (cert->priv->issuer);
+
+  G_OBJECT_CLASS (g_tls_certificate_parent_class)->finalize (object);
+}
+
+static void
+g_tls_certificate_class_init (GTlsCertificateClass *class)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+
+  g_type_class_add_private (class, sizeof (GTlsCertificatePrivate));
+
+  gobject_class->set_property = g_tls_certificate_set_property;
+  gobject_class->get_property = g_tls_certificate_get_property;
+  gobject_class->finalize = g_tls_certificate_finalize;
+
+  /**
+   * GTlsCertificate:certificate:
+   *
+   * The DER (binary) encoded representation of the certificate's
+   * public key. This property and the
+   * #GTlsCertificate:certificate-pem property represent the same
+   * data, just in different forms.
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_CERTIFICATE,
+				   g_param_spec_boxed ("certificate",
+						       P_("Certificate"),
+						       P_("The DER representation of the certificate"),
+						       G_TYPE_BYTE_ARRAY,
+						       G_PARAM_READWRITE |
+						       G_PARAM_CONSTRUCT_ONLY |
+						       G_PARAM_STATIC_STRINGS));
+  /**
+   * GTlsCertificate:certificate-pem:
+   *
+   * The PEM (ASCII) encoded representation of the certificate's
+   * public key. This property and the #GTlsCertificate:certificate
+   * property represent the same data, just in different forms.
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_CERTIFICATE_PEM,
+				   g_param_spec_string ("certificate-pem",
+							P_("Certificate (PEM)"),
+							P_("The PEM representation of the certificate"),
+							NULL,
+							G_PARAM_READWRITE |
+							G_PARAM_CONSTRUCT_ONLY |
+							G_PARAM_STATIC_STRINGS));
+  /**
+   * GTlsCertificate:private-key:
+   *
+   * The DER (binary) encoded representation of the certificate's
+   * private key. This property (or the
+   * #GTlsCertificate:private-key-pem property) can be set when
+   * constructing a key (eg, from a file), but cannot be read.
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY,
+				   g_param_spec_boxed ("private-key",
+						       P_("Private key"),
+						       P_("The DER representation of the certificate's private key"),
+						       G_TYPE_BYTE_ARRAY,
+						       G_PARAM_WRITABLE |
+						       G_PARAM_CONSTRUCT_ONLY |
+						       G_PARAM_STATIC_STRINGS));
+  /**
+   * GTlsCertificate:private-key-pem:
+   *
+   * The PEM (ASCII) encoded representation of the certificate's
+   * private key. This property (or the #GTlsCertificate:private-key
+   * property) can be set when constructing a key (eg, from a file),
+   * but cannot be read.
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY_PEM,
+				   g_param_spec_string ("private-key-pem",
+							P_("Private key (PEM)"),
+							P_("The PEM representation of the certificate's private key"),
+							NULL,
+							G_PARAM_WRITABLE |
+							G_PARAM_CONSTRUCT_ONLY |
+							G_PARAM_STATIC_STRINGS));
+  /**
+   * GTlsCertificate:issuer:
+   *
+   * A #GTlsCertificate representing the entity that issued this
+   * certificate. If %NULL, this means that the certificate is either
+   * self-signed, or else the certificate of the issuer is not
+   * available.
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_ISSUER,
+				   g_param_spec_object ("issuer",
+							P_("Issuer"),
+							P_("The certificate for the issuing entity"),
+							G_TYPE_TLS_CERTIFICATE,
+							G_PARAM_READWRITE |
+							G_PARAM_CONSTRUCT_ONLY |
+							G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * g_tls_certificate_new:
+ * @error: #GError for error reporting, or %NULL to ignore.
+ * @...: a %NULL-terminated list of property values and names
+ *
+ * Creates a new #GTlsCertificate based on the given properties. This
+ * is a wrapper around g_initable_new() that looks up the correct type
+ * from the active #GTlsBackend.
+ *
+ * Return value: the new #GTlsCertificate, or %NULL on error
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_certificate_new (GError      **error,
+		       ...)
+{
+  GTlsCertificate *cert;
+  GTlsBackend *backend;
+  const gchar *first_property_name;
+  va_list ap;
+
+  backend = g_tls_backend_get_default ();
+
+  va_start (ap, error);
+  first_property_name = va_arg (ap, const gchar *);
+  cert = (GTlsCertificate *) g_initable_new_valist (g_tls_backend_get_certificate_type (backend),
+						    first_property_name, ap,
+						    NULL, error);
+  va_end (ap);
+
+  return cert;
+}
+
+#define PEM_CERTIFICATE_HEADER "-----BEGIN CERTIFICATE-----"
+#define PEM_CERTIFICATE_FOOTER "-----END CERTIFICATE-----"
+#define PEM_PRIVKEY_HEADER     "-----BEGIN RSA PRIVATE KEY-----"
+#define PEM_PRIVKEY_FOOTER     "-----END RSA PRIVATE KEY-----"
+
+static GTlsCertificate *
+parse_next_pem_certificate (const gchar **data,
+			    const gchar  *data_end,
+			    gboolean      required,
+			    GError      **error)
+{
+  const gchar *start, *end, *next;
+  gchar *cert_pem, *privkey_pem = NULL;
+  GTlsCertificate *cert;
+
+  start = g_strstr_len (*data, data_end - *data, PEM_CERTIFICATE_HEADER);
+  if (!start)
+    {
+      if (required)
+	{
+	  g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			       _("No PEM-encoded certificate found"));
+	}
+      return NULL;
+    }
+
+  end = g_strstr_len (start, data_end - start, PEM_CERTIFICATE_FOOTER);
+  if (!end)
+    {
+      g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			   _("Could not parse PEM-encoded certificate"));
+      return NULL;
+    }
+  end += strlen (PEM_CERTIFICATE_FOOTER);
+  while (*end == '\r' || *end == '\n')
+    end++;
+
+  cert_pem = g_strndup (start, end - start);
+
+  *data = end;
+
+  next = g_strstr_len (*data, data_end - *data, PEM_CERTIFICATE_HEADER);
+  start = g_strstr_len (*data, data_end - *data, PEM_PRIVKEY_HEADER);
+  if (start)
+    end = g_strstr_len (start, data_end - start, PEM_PRIVKEY_FOOTER);
+
+  if (start && (!next || start < next))
+    {
+      if (!end || (next && end > next))
+	{
+	  g_set_error_literal (error, G_TLS_ERROR, G_TLS_ERROR_BAD_CERTIFICATE,
+			       _("Could not parse PEM-encoded private key"));
+	  return NULL;
+	}
+
+      end += strlen (PEM_PRIVKEY_FOOTER);
+      while (*end == '\r' || *end == '\n')
+	end++;
+
+      privkey_pem = g_strndup (start, end - start);
+
+      *data = end + strlen (PEM_PRIVKEY_FOOTER);
+    }
+
+  cert = g_tls_certificate_new (error,
+				"certificate-pem", cert_pem,
+				"private-key-pem", privkey_pem,
+				NULL);
+  g_free (cert_pem);
+  g_free (privkey_pem);
+
+  return cert;
+}
+
+/**
+ * g_tls_certificate_new_from_pem:
+ * @data: PEM-encoded certificate data
+ * @length: the length of @data, or -1 if it's 0-terminated.
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Creates a new #GTlsCertificate from the PEM-encoded data in @data.
+ * If @data includes both a certificate and a private key, then the
+ * returned certificate will include the private key data as well.
+ *
+ * If @data includes multiple certificates, only the first one will be
+ * parsed.
+ *
+ * Return value: the new certificate, or %NULL if @data is invalid
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_certificate_new_from_pem  (const gchar  *data,
+				 gssize        length,
+				 GError      **error)
+{
+  const gchar *data_end;
+
+  g_return_val_if_fail (data != NULL, NULL);
+
+  if (length == -1)
+    data_end = data + strlen (data);
+  else
+    data_end = data + length;
+  return parse_next_pem_certificate (&data, data_end, TRUE, error);
+}
+
+/**
+ * g_tls_certificate_new_from_file:
+ * @file: file containing a PEM-encoded certificate to import
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Creates a #GTlsCertificate from the PEM-encoded data in @file. If
+ * @file cannot be read or parsed, the function will return %NULL and
+ * set @error. Otherwise, this behaves like g_tls_certificate_new().
+ *
+ * Return value: the new certificate, or %NULL on error
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_certificate_new_from_file (const gchar  *file,
+				 GError      **error)
+{
+  GTlsCertificate *cert;
+  gchar *contents;
+  gsize length;
+
+  if (!g_file_get_contents (file, &contents, &length, error))
+    return NULL;
+
+  cert = g_tls_certificate_new_from_pem (contents, length, error);
+  g_free (contents);
+  return cert;
+}
+
+/**
+ * g_tls_certificate_new_from_files:
+ * @cert_file: file containing a PEM-encoded certificate to import
+ * @key_file: file containing a PEM-encoded private key to import
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Creates a #GTlsCertificate from the PEM-encoded data in @cert_file
+ * and @key_file. If either file cannot be read or parsed, the
+ * function will return %NULL and set @error. Otherwise, this behaves
+ * like g_tls_certificate_new().
+ *
+ * Return value: the new certificate, or %NULL on error
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_certificate_new_from_files (const gchar  *cert_file,
+				  const gchar  *key_file,
+				  GError      **error)
+{
+  GTlsCertificate *cert;
+  gchar *cert_data, *key_data;
+
+  if (!g_file_get_contents (cert_file, &cert_data, NULL, error))
+    return NULL;
+  if (!g_file_get_contents (key_file, &key_data, NULL, error))
+    {
+      g_free (cert_data);
+      return NULL;
+    }
+
+  cert = g_tls_certificate_new (error,
+				"certificate-pem", cert_data,
+				"private-key-pem", key_data,
+				NULL);
+  g_free (cert_data);
+  g_free (key_data);
+  return cert;
+}
+
+/**
+ * g_tls_certificate_list_new_from_file:
+ * @file: file containing PEM-encoded certificates to import
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Creates one or more #GTlsCertificate<!-- -->s from the PEM-encoded
+ * data in @file. If @file cannot be read or parsed, the function will
+ * return %NULL and set @error. If @file does not contain any
+ * PEM-encoded certificates, this will return an empty list and not
+ * set @error.
+ *
+ * Return value: (element-type Gio.TlsCertificate) (transfer full): a
+ * #GList containing #GTlsCertificate objects. You must free the list
+ * and its contents when you are done with it.
+ *
+ * Since: 2.28
+ */
+GList *
+g_tls_certificate_list_new_from_file (const gchar  *file,
+				      GError      **error)
+{
+  GTlsCertificate *cert;
+  GList *list, *l;
+  gchar *contents, *end;
+  const gchar *p;
+  gsize length;
+
+  if (!g_file_get_contents (file, &contents, &length, error))
+    return NULL;
+
+  list = NULL;
+  end = contents + length;
+  p = contents;
+  while (p && *p)
+    {
+      cert = parse_next_pem_certificate (&p, end, FALSE, error);
+      if (!cert)
+	{
+	  for (l = list; l; l = l->next)
+	    g_object_unref (l->data);
+	  g_list_free (list);
+	  list = NULL;
+	  break;
+	}
+      list = g_list_prepend (list, cert);
+    }
+
+  return g_list_reverse (list);
+}
+
+
+/**
+ * g_tls_certificate_get_issuer:
+ * @cert: a #GTlsCertificate
+ *
+ * Gets the #GTlsCertificate representing @cert's issuer, if known
+ *
+ * Return value: (transfer none): The certificate of @cert's issuer,
+ * or %NULL if @cert is self-signed or signed with an unknown
+ * certificate.
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_certificate_get_issuer (GTlsCertificate  *cert)
+{
+  return cert->priv->issuer;
+}
diff --git a/gio/gtlscertificate.h b/gio/gtlscertificate.h
new file mode 100644
index 0000000..4abed4c
--- /dev/null
+++ b/gio/gtlscertificate.h
@@ -0,0 +1,78 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2009 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_CERTIFICATE_H__
+#define __G_TLS_CERTIFICATE_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CERTIFICATE            (g_tls_certificate_get_type ())
+#define G_TLS_CERTIFICATE(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CERTIFICATE, GTlsCertificate))
+#define G_TLS_CERTIFICATE_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CERTIFICATE, GTlsCertificateClass))
+#define G_IS_TLS_CERTIFICATE(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CERTIFICATE))
+#define G_IS_TLS_CERTIFICATE_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CERTIFICATE))
+#define G_TLS_CERTIFICATE_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CERTIFICATE, GTlsCertificateClass))
+
+typedef struct _GTlsCertificateClass   GTlsCertificateClass;
+typedef struct _GTlsCertificatePrivate GTlsCertificatePrivate;
+
+struct _GTlsCertificate {
+  GObject parent_instance;
+
+  GTlsCertificatePrivate *priv;
+};
+
+struct _GTlsCertificateClass
+{
+  GObjectClass parent_class;
+
+  /*< private >*/
+  /* Padding for future expansion */
+  gpointer padding[8];
+};
+
+GType            g_tls_certificate_get_type           (void) G_GNUC_CONST;
+
+GTlsCertificate *g_tls_certificate_new                (GError          **error,
+						       ...) G_GNUC_NULL_TERMINATED;
+
+GTlsCertificate *g_tls_certificate_new_from_pem       (const gchar      *data,
+						       gssize            length,
+						       GError          **error);
+
+GTlsCertificate *g_tls_certificate_new_from_file      (const gchar      *file,
+						       GError          **error);
+GTlsCertificate *g_tls_certificate_new_from_files     (const gchar      *cert_file,
+						       const gchar      *key_file,
+						       GError          **error);
+GList           *g_tls_certificate_list_new_from_file (const gchar      *file,
+						       GError          **error);
+
+GTlsCertificate *g_tls_certificate_get_issuer         (GTlsCertificate  *cert);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CERTIFICATE_H__ */
diff --git a/gio/gtlsclientconnection.c b/gio/gtlsclientconnection.c
new file mode 100644
index 0000000..33fd0ac
--- /dev/null
+++ b/gio/gtlsclientconnection.c
@@ -0,0 +1,341 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include "gtlsclientconnection.h"
+#include "ginitable.h"
+#include "gioenumtypes.h"
+#include "gio-marshal.h"
+#include "gsocket.h"
+#include "gsocketconnectable.h"
+#include "gtlsbackend.h"
+#include "gtlscertificate.h"
+#include "glibintl.h"
+
+/**
+ * SECTION:gtlsclientconnection
+ * @short_description: TLS client-side connection
+ * @include: gio/gio.h
+ *
+ * #GTlsClientConnection is the client-side subclass of
+ * #GTlsConnection, representing a client-side TLS connection.
+ *
+ * Since: 2.28
+ */
+
+/**
+ * GTlsClientConnection:
+ *
+ * Abstract base class for the backend-specific client connection
+ * type.
+ *
+ * Since: 2.28
+ */
+
+G_DEFINE_INTERFACE (GTlsClientConnection, g_tls_client_connection, G_TYPE_TLS_CONNECTION)
+
+static void
+g_tls_client_connection_default_init (GTlsClientConnectionInterface *iface)
+{
+  /**
+   * GTlsClientConnection:validation-flags:
+   *
+   * What steps to perform when validating a certificate received from
+   * a server. Server certificates that fail to validate in all of the
+   * ways indicated here will be rejected unless the application
+   * overrides the default via #GTlsConnection::accept-certificate.
+   *
+   * Since: 2.28
+   */
+  g_object_interface_install_property (iface,
+				       g_param_spec_flags ("validation-flags",
+							   P_("Validation flags"),
+							   P_("What certificate validation to perform"),
+							   G_TYPE_TLS_VALIDATION_FLAGS,
+							   G_TLS_VALIDATE_ALL,
+							   G_PARAM_READWRITE |
+							   G_PARAM_CONSTRUCT |
+							   G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GTlsClientConnection:server-identity:
+   *
+   * A #GSocketConnectable describing the identity of the server that
+   * is expected on the other end of the connection.
+   *
+   * If the %G_TLS_VALIDATE_IDENTITY flag is set in
+   * #GTlsClientConnection:validation-flags, this object will be used
+   * to determine the expected identify of the remote end of the
+   * connection; if #GTlsClientConnection:server-identity is not set,
+   * or does not match the identity presented by the server, then the
+   * %G_TLS_VALIDATE_IDENTITY validation will fail.
+   *
+   * In addition to its use in verifying the server certificate,
+   * this is also used to give a hint to the server about what
+   * certificate we expect, which is useful for servers that serve
+   * virtual hosts.
+   *
+   * Since: 2.28
+   */
+  g_object_interface_install_property (iface,
+				       g_param_spec_object ("server-identity",
+							    P_("Server identity"),
+							    P_("GSocketConnectable identifying the server"),
+							    G_TYPE_SOCKET_CONNECTABLE,
+							    G_PARAM_READWRITE |
+							    G_PARAM_CONSTRUCT |
+							    G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GTlsClientConnection:use-ssl3:
+   *
+   * If %TRUE, tells the connection to use SSL 3.0 rather than trying
+   * to negotiate the best version of TLS or SSL to use. This can be
+   * used when talking to servers that don't implement version
+   * negotiation correctly and therefore refuse to handshake at all with
+   * a "modern" TLS handshake.
+   *
+   * Since: 2.28
+   */
+  g_object_interface_install_property (iface,
+				       g_param_spec_boolean ("use-ssl3",
+							     P_("Use SSL3"),
+							     P_("Use SSL 3.0 rather than trying to use TLS 1.x"),
+							     FALSE,
+							     G_PARAM_READWRITE |
+							     G_PARAM_CONSTRUCT |
+							     G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GTlsClientConnection:accepted-cas:
+   *
+   * A list of the distinguished names of the Certificate Authorities
+   * that the server will accept client certificates signed by. If the
+   * server requests a client certificate during the handshake, then
+   * this property will be set by the time the
+   * #GTlsConnection::need-certificate signal is emitted.
+   *
+   * Since: 2.28
+   */
+  g_object_interface_install_property (iface,
+				       g_param_spec_boxed ("accepted-cas",
+							   P_("Accepted CAs"),
+							   P_("Distinguished names of the CAs the server accepts certificates from"),
+							   G_TYPE_STRV,
+							   G_PARAM_READABLE |
+							   G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * g_tls_client_connection_new:
+ * @error: #GError for error reporting, or %NULL to ignore.
+ * @...: a %NULL-terminated list of property values and names
+ *
+ * Creates a new #GTlsClientConnection based on the #GIOstream
+ * specified as "base-io-stream" in the property list. This is a
+ * wrapper around g_initable_new() that looks up the correct type from
+ * the active #GTlsBackend.
+ *
+ * Return value: the new #GTlsClientConnection, or %NULL on error
+ *
+ * Since: 2.28
+ */
+GTlsClientConnection *
+g_tls_client_connection_new (GError    **error,
+			     ...)
+{
+  GObject *conn;
+  GTlsBackend *backend;
+  const gchar *first_property_name;
+  va_list ap;
+
+  va_start (ap, error);
+  first_property_name = va_arg (ap, const gchar *);
+  /* There must be at least a "base-io-stream" property, since that's
+   * required and construct-only.
+   */
+  g_return_val_if_fail (first_property_name != NULL, NULL);
+
+  backend = g_tls_backend_get_default ();
+  conn = g_initable_new_valist (g_tls_backend_get_client_connection_type (backend),
+				first_property_name, ap,
+				NULL, error);
+  va_end (ap);
+
+  return G_TLS_CLIENT_CONNECTION (conn);
+}
+
+/**
+ * g_tls_client_connection_get_validation_flags:
+ * @conn: the #GTlsClientConnection
+ *
+ * Gets @conn's validation flags
+ *
+ * Return value: the validation flags
+ *
+ * Since: 2.28
+ */
+GTlsValidationFlags
+g_tls_client_connection_get_validation_flags (GTlsClientConnection *conn)
+{
+  GTlsValidationFlags flags = 0;
+
+  g_return_val_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn), 0);
+
+  g_object_get (G_OBJECT (conn), "validation-flags", &flags, NULL);
+  return flags;
+}
+
+/**
+ * g_tls_client_connection_set_validation_flags:
+ * @conn: the #GTlsClientConnection
+ * @flags: the #GTlsValidationFlags to use
+ *
+ * Sets @conn's validation flags, to override the default set of
+ * checks performed when validating a server certificate.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_client_connection_set_validation_flags (GTlsClientConnection *conn,
+					      GTlsValidationFlags   flags)
+{
+  g_return_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn));
+
+  g_object_set (G_OBJECT (conn), "validation-flags", flags, NULL);
+}
+
+/**
+ * g_tls_client_connection_get_server_identity:
+ * @conn: the #GTlsClientConnection
+ *
+ * Gets @conn's expected server identity
+ *
+ * Return value: a #GSocketConnectable describing the
+ * expected server identity, or %NULL if the expected identity is not
+ * known.
+ *
+ * Since: 2.28
+ */
+GSocketConnectable *
+g_tls_client_connection_get_server_identity (GTlsClientConnection *conn)
+{
+  GSocketConnectable *identity = NULL;
+
+  g_return_val_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn), 0);
+
+  g_object_get (G_OBJECT (conn), "server-identity", &identity, NULL);
+  if (identity)
+    g_object_unref (identity);
+  return identity;
+}
+
+/**
+ * g_tls_client_connection_set_server_identity:
+ * @conn: the #GTlsClientConnection
+ * @identity: a #GSocketConnectable describing the expected server identity
+ *
+ * Sets @conn's expected server identity, which is used both to tell
+ * servers on virtual hosts which certificate to present, and also
+ * to let @conn know what name to look for in the certificate when
+ * performing %G_TLS_VALIDATE_IDENTITY validation, if enabled.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_client_connection_set_server_identity (GTlsClientConnection *conn,
+					     GSocketConnectable   *identity)
+{
+  g_return_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn));
+
+  g_object_set (G_OBJECT (conn), "server-identity", identity, NULL);
+}
+
+/**
+ * g_tls_client_connection_get_use_ssl3:
+ * @conn: the #GTlsClientConnection
+ *
+ * Gets whether @conn will use SSL 3.0 rather than the
+ * highest-supported version of TLS; see
+ * g_tls_client_connection_set_use_ssl3().
+ *
+ * Return value: whether @conn will use SSL 3.0
+ *
+ * Since: 2.28
+ */
+gboolean
+g_tls_client_connection_get_use_ssl3 (GTlsClientConnection *conn)
+{
+  gboolean use_ssl3 = FALSE;
+
+  g_return_val_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn), 0);
+
+  g_object_get (G_OBJECT (conn), "use-ssl3", &use_ssl3, NULL);
+  return use_ssl3;
+}
+
+/**
+ * g_tls_client_connection_set_use_ssl3:
+ * @conn: the #GTlsClientConnection
+ * @use_ssl3: whether to use SSL 3.0
+ *
+ * If @use_ssl3 is %TRUE, this forces @conn to use SSL 3.0 rather than
+ * trying to properly negotiate the right version of TLS or SSL to use.
+ * This can be used when talking to servers that do not implement the
+ * fallbacks correctly and which will therefore fail to handshake with
+ * a "modern" TLS handshake attempt.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_client_connection_set_use_ssl3 (GTlsClientConnection *conn,
+				      gboolean              use_ssl3)
+{
+  g_return_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn));
+
+  g_object_set (G_OBJECT (conn), "use-ssl3", use_ssl3, NULL);
+}
+
+/**
+ * g_tls_client_connection_get_accepted_cas:
+ * @conn: the #GTlsClientConnection
+ *
+ * Gets the list of distinguished names of the Certificate Authorities
+ * that the server will accept certificates from. This will be set
+ * during the TLS handshake if the server requests a certificate.
+ * Otherwise, it will be %NULL.
+ *
+ * Return value: (transfer full) (array zero-terminated=1): the list
+ * of CA names, which you must free (eg, with g_strfreev()).
+ *
+ * Since: 2.28
+ */
+char **
+g_tls_client_connection_get_accepted_cas (GTlsClientConnection *conn)
+{
+  char **accepted_cas = NULL;
+
+  g_return_val_if_fail (G_IS_TLS_CLIENT_CONNECTION (conn), NULL);
+
+  g_object_get (G_OBJECT (conn), "accepted-cas", &accepted_cas, NULL);
+  return accepted_cas;
+}
diff --git a/gio/gtlsclientconnection.h b/gio/gtlsclientconnection.h
new file mode 100644
index 0000000..b7389d5
--- /dev/null
+++ b/gio/gtlsclientconnection.h
@@ -0,0 +1,71 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2009 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_CLIENT_CONNECTION_H__
+#define __G_TLS_CLIENT_CONNECTION_H__
+
+#include <gio/gtlsconnection.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CLIENT_CONNECTION                (g_tls_client_connection_get_type ())
+#define G_TLS_CLIENT_CONNECTION(inst)               (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CLIENT_CONNECTION, GTlsClientConnection))
+#define G_IS_TLS_CLIENT_CONNECTION(inst)            (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CLIENT_CONNECTION))
+#define G_TLS_CLIENT_CONNECTION_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), G_TYPE_TLS_CLIENT_CONNECTION, GTlsClientConnectionInterface))
+
+/**
+ * GTlsClientConnection:
+ *
+ * TLS client-side connection; the client-side implementation of a
+ * #GTlsConnection
+ *
+ * Since: 2.28
+ */
+typedef struct _GTlsClientConnectionInterface GTlsClientConnectionInterface;
+
+struct _GTlsClientConnectionInterface
+{
+  GTypeInterface g_iface;
+
+};
+
+GType                 g_tls_client_connection_get_type             (void) G_GNUC_CONST;
+
+GTlsClientConnection *g_tls_client_connection_new                  (GError                 **error,
+								    ...) G_GNUC_NULL_TERMINATED;
+
+GTlsValidationFlags   g_tls_client_connection_get_validation_flags (GTlsClientConnection    *conn);
+void                  g_tls_client_connection_set_validation_flags (GTlsClientConnection    *conn,
+								    GTlsValidationFlags      flags);
+GSocketConnectable   *g_tls_client_connection_get_server_identity  (GTlsClientConnection    *conn);
+void                  g_tls_client_connection_set_server_identity  (GTlsClientConnection    *conn,
+								    GSocketConnectable      *identity);
+gboolean              g_tls_client_connection_get_use_ssl3         (GTlsClientConnection    *conn);
+void                  g_tls_client_connection_set_use_ssl3         (GTlsClientConnection    *conn,
+								    gboolean                 use_ssl3);
+char **               g_tls_client_connection_get_accepted_cas     (GTlsClientConnection    *conn);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CLIENT_CONNECTION_H__ */
diff --git a/gio/gtlsconnection.c b/gio/gtlsconnection.c
new file mode 100644
index 0000000..7cd0621
--- /dev/null
+++ b/gio/gtlsconnection.c
@@ -0,0 +1,741 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include "gtlsconnection.h"
+#include "gcancellable.h"
+#include "gioenumtypes.h"
+#include "gio-marshal.h"
+#include "gpollableiostream.h"
+#include "gsocket.h"
+#include "gtlsbackend.h"
+#include "gtlscertificate.h"
+#include "gtlsclientconnection.h"
+#include "glibintl.h"
+
+/**
+ * SECTION:gtlsconnection
+ * @short_description: TLS connection type
+ * @include: gio/gio.h
+ *
+ * #GTlsConnection is the base TLS connection class type, which wraps
+ * a #GIOStream and provides TLS encryption on top of it. Its
+ * subclasses, #GTlsClientConnection and #GTlsServerConnection,
+ * implement client-side and server-side TLS, respectively.
+ *
+ * Since: 2.28
+ */
+
+/**
+ * GTlsConnection:
+ *
+ * Abstract base class for the backend-specific #GTlsClientConnection
+ * and #GTlsServerConnection types.
+ *
+ * Since: 2.28
+ */
+
+G_DEFINE_ABSTRACT_TYPE (GTlsConnection, g_tls_connection, G_TYPE_IO_STREAM)
+
+static void g_tls_connection_get_property (GObject    *object,
+					   guint       prop_id,
+					   GValue     *value,
+					   GParamSpec *pspec);
+static void g_tls_connection_set_property (GObject      *object,
+					   guint         prop_id,
+					   const GValue *value,
+					   GParamSpec   *pspec);
+static void g_tls_connection_finalize     (GObject      *object);
+
+static gboolean g_tls_connection_certificate_accumulator (GSignalInvocationHint *ihint,
+							  GValue                *return_accu,
+							  const GValue          *handler_return,
+							  gpointer               dummy);
+
+enum {
+  NEED_CERTIFICATE,
+  ACCEPT_CERTIFICATE,
+
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+enum {
+  PROP_0,
+  PROP_BASE_IO_STREAM,
+  PROP_REQUIRE_CLOSE_NOTIFY,
+  PROP_CA_LIST,
+  PROP_CERTIFICATE,
+  PROP_PEER_CERTIFICATE
+};
+
+struct _GTlsConnectionPrivate {
+  GTlsCertificate *certificate, *peer_certificate;
+};
+
+static void
+g_tls_connection_class_init (GTlsConnectionClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (GTlsConnectionPrivate));
+
+  gobject_class->get_property = g_tls_connection_get_property;
+  gobject_class->set_property = g_tls_connection_set_property;
+  gobject_class->finalize = g_tls_connection_finalize;
+
+  /**
+   * GTlsConnection:base-io-stream:
+   *
+   * The #GIOStream that the connection wraps
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_BASE_IO_STREAM,
+				   g_param_spec_object ("base-io-stream",
+							P_("Base IOStream"),
+							P_("The GIOStream that the connection wraps"),
+							G_TYPE_POLLABLE_IO_STREAM,
+							G_PARAM_READWRITE |
+							G_PARAM_CONSTRUCT_ONLY |
+							G_PARAM_STATIC_STRINGS));
+  /**
+   * GTlsConnection:ca-list:
+   *
+   * The list of trusted Certificate Authorities for authenticating
+   * peer certificates. This will be initialized with a default
+   * list of CAs trusted by the system.
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_CA_LIST,
+				   g_param_spec_pointer ("ca-list",
+							 P_("CA List"),
+							 P_("The list of trusted Certificate Authorities"),
+							 G_PARAM_READWRITE |
+							 G_PARAM_STATIC_STRINGS));
+  /**
+   * GTlsConnection:require-close-notify:
+   *
+   * Whether or not proper TLS close notification is required.
+   * See g_tls_connection_set_require_close_notify().
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_REQUIRE_CLOSE_NOTIFY,
+				   g_param_spec_boolean ("require-close-notify",
+							 P_("Require close notify"),
+							 P_("Whether to require proper TLS close notification"),
+							 TRUE,
+							 G_PARAM_READWRITE |
+							 G_PARAM_STATIC_STRINGS));
+  /**
+   * GTlsConnection:certificate:
+   *
+   * The connection's certificate; see
+   * g_tls_connection_set_certificate().
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_CERTIFICATE,
+				   g_param_spec_object ("certificate",
+							P_("Certificate"),
+							P_("The connection's certificate"),
+							G_TYPE_TLS_CERTIFICATE,
+							G_PARAM_READWRITE |
+							G_PARAM_STATIC_STRINGS));
+  /**
+   * GTlsConnection:peer-certificate:
+   *
+   * The connection's peer's certificate, after it has been set during
+   * the TLS handshake.
+   *
+   * Since: 2.28
+   */
+  g_object_class_install_property (gobject_class, PROP_PEER_CERTIFICATE,
+				   g_param_spec_object ("peer-certificate",
+							P_("Peer Certificate"),
+							P_("The connection's peer's certificate"),
+							G_TYPE_TLS_CERTIFICATE,
+							G_PARAM_READABLE |
+							G_PARAM_STATIC_STRINGS));
+
+  /**
+   * GTlsConnection::need-certificate:
+   * @conn: a #GTlsConnection
+   *
+   * Emitted during the TLS handshake if a certificate is needed and
+   * one has not been set via g_tls_connection_set_certificate().
+   *
+   * For server-side connections, a certificate is always needed, and
+   * the connection will fail if none is provided.
+   *
+   * For client-side connections, the signal will be emitted only if
+   * the server has requested a certificate; you can call
+   * g_tls_client_connection_get_accepted_cas() to get a list of
+   * Certificate Authorities that the server will accept certificates
+   * from. If you do not return a certificate (and have not provided
+   * one via g_tls_connection_set_certificate()) then the server may
+   * reject the handshake, in which case the operation will eventually
+   * fail with %G_TLS_ERROR_CERTIFICATE_REQUIRED.
+   *
+   * Note that if this signal is emitted as part of asynchronous I/O
+   * in the main thread, then you should not attempt to interact with
+   * the user before returning from the signal handler. If you want to
+   * let the user choose a certificate to return, you would have to
+   * return %NULL from the signal handler on the first attempt, and
+   * then after the connection attempt returns a
+   * %G_TLS_ERROR_CERTIFICATE_REQUIRED, you can interact with the
+   * user, create a new connection, and call
+   * g_tls_connection_set_certificate() on it before handshaking (or
+   * just connect to the signal again and return the certificate the
+   * next time).
+   *
+   * If you are doing I/O in another thread, you do not
+   * need to worry about this, and can simply block in the signal
+   * handler until the UI thread returns an answer.
+   *
+   * Return value: the certificate to send to the peer, or %NULL to
+   * send no certificate. If you return a certificate, the signal
+   * emission will be stopped and further handlers will not be called.
+   *
+   * Since: 2.28
+   */
+  signals[NEED_CERTIFICATE] =
+    g_signal_new (I_("need-certificate"),
+		  G_TYPE_TLS_CONNECTION,
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GTlsConnectionClass, need_certificate),
+		  g_tls_connection_certificate_accumulator, NULL,
+		  _gio_marshal_OBJECT__VOID,
+		  G_TYPE_TLS_CERTIFICATE, 0);
+
+  /**
+   * GTlsConnection::accept-certificate:
+   * @conn: a #GTlsConnection
+   * @peer_cert: the peer's #GTlsCertificate
+   * @errors: the problems with @peer_cert.
+   *
+   * Emitted during the TLS handshake after the peer certificate has
+   * been received. You can examine @peer_cert's certification path by
+   * calling g_tls_certificate_get_issuer() on it.
+   *
+   * For a client-side connection, @peer_cert is the server's
+   * certificate, and the signal will only be emitted if the
+   * certificate was not acceptable according to @conn's
+   * #GTlsClientConnection:validation_flags. If you would like the
+   * certificate to be accepted despite @errors, return %TRUE from the
+   * signal handler. Otherwise, if no handler accepts the certificate,
+   * the handshake will fail with %G_TLS_ERROR_BAD_CERTIFICATE.
+   *
+   * For a server-side connection, @peer_cert is the certificate
+   * presented by the client, if this was requested via the server's
+   * #GTlsServerConnection:authentication_mode. On the server side,
+   * the signal is always emitted when the client presents a
+   * certificate, and the certificate will only be accepted if a
+   * handler returns %TRUE.
+   *
+   * As with #GTlsConnection::need_certificate, you should not
+   * interact with the user during the signal emission if the signal
+   * was emitted as part of an asynchronous operation in the main
+   * thread.
+   *
+   * Return value: %TRUE to accept @peer_cert (which will also
+   * immediately end the signal emission). %FALSE to allow the signal
+   * emission to continue, which will cause the handshake to fail if
+   * no one else overrides it.
+   *
+   * Since: 2.28
+   */
+  signals[ACCEPT_CERTIFICATE] =
+    g_signal_new (I_("accept-certificate"),
+		  G_TYPE_TLS_CONNECTION,
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (GTlsConnectionClass, accept_certificate),
+		  g_signal_accumulator_true_handled, NULL,
+		  _gio_marshal_BOOLEAN__OBJECT_FLAGS,
+		  G_TYPE_BOOLEAN, 2,
+		  G_TYPE_TLS_CERTIFICATE,
+		  G_TYPE_TLS_VALIDATION_FLAGS);
+}
+
+static void
+g_tls_connection_init (GTlsConnection *conn)
+{
+  conn->priv = G_TYPE_INSTANCE_GET_PRIVATE (conn, G_TYPE_TLS_CONNECTION, GTlsConnectionPrivate);
+}
+
+static void
+g_tls_connection_finalize (GObject *object)
+{
+  GTlsConnection *conn = G_TLS_CONNECTION (object);
+
+  if (conn->priv->certificate)
+    g_object_unref (conn->priv->certificate);
+  if (conn->priv->peer_certificate)
+    g_object_unref (conn->priv->peer_certificate);
+
+  G_OBJECT_CLASS (g_tls_connection_parent_class)->finalize (object);
+}
+
+static void
+g_tls_connection_get_property (GObject    *object,
+			       guint       prop_id,
+			       GValue     *value,
+			       GParamSpec *pspec)
+{
+  GTlsConnection *conn = G_TLS_CONNECTION (object);
+
+  switch (prop_id)
+    {
+    case PROP_CERTIFICATE:
+      g_value_set_object (value, conn->priv->certificate);
+      break;
+
+    case PROP_PEER_CERTIFICATE:
+      g_value_set_object (value, conn->priv->peer_certificate);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_tls_connection_set_property (GObject      *object,
+			       guint         prop_id,
+			       const GValue *value,
+			       GParamSpec   *pspec)
+{
+  GTlsConnection *conn = G_TLS_CONNECTION (object);
+
+  switch (prop_id)
+    {
+    case PROP_CERTIFICATE:
+      g_tls_connection_set_certificate (conn, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/**
+ * g_tls_connection_get_ca_list:
+ * @connection: the #GTlsConnection
+ *
+ * Gets @connection's trusted Certificate Authority list. See
+ * g_tls_connection_set_ca_list() for more details.
+ *
+ * Return value: (element-type Gio.TlsCertificate) (transfer full):
+ *   the list of CA certificates.
+ *
+ * Since: 2.28
+ */
+GList *
+g_tls_connection_get_ca_list (GTlsConnection *conn)
+{
+  GList *ca_list = NULL;
+
+  g_object_get (G_OBJECT (conn), "ca-list", &ca_list, NULL);
+  return ca_list;
+}
+
+/**
+ * g_tls_connection_set_ca_list:
+ * @connection: the #GTlsConnection
+ * @ca_list: (transfer full) (element-type Gio.TlsCertificate): a list
+ *   of #GTlsCertificates
+ *
+ * Sets @connection's trusted Certificate Authority list to the provided
+ * list of #GTlsCertificate.
+ *
+ * You should not normally need to call this; a #GTlsConnection will be
+ * initialized to use a default list of CAs provided by the system.
+ * However, this could be used if you wish to trust only a more
+ * restricted set of CAs, or (in conjuction with
+ * g_tls_connection_get_ca_list()) if you need to add a CA to the list.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_connection_set_ca_list (GTlsConnection *conn,
+			      GList          *ca_list)
+{
+  g_object_set (G_OBJECT (conn), "ca-list", ca_list, NULL);
+}
+
+/**
+ * g_tls_connection_set_ca_list_from_file:
+ * @connection: the #GTlsConnection
+ * @ca_file: the name of a file containing PEM-encoded certificates
+ * @error: #GError for error reporting, or %NULL to ignore.
+ *
+ * Sets @connection's trusted Certificate Authority list by parsing the
+ * contents of @ca_file. As with g_tls_connection_set_ca_list(), this can
+ * be used to override the default set of CAs.
+ *
+ * If @file cannot be read or parsed, @connection's CA list will be
+ * unchanged, and @error will be set.
+ *
+ * Return value: success or failure
+ *
+ * Since: 2.28
+ */
+gboolean
+g_tls_connection_set_ca_list_from_file (GTlsConnection  *conn,
+					const char      *ca_file,
+					GError         **error)
+{
+  GList *ca_list, *l;
+  GError *my_error = NULL;
+
+  ca_list = g_tls_certificate_list_new_from_file (ca_file, &my_error);
+  if (my_error)
+    {
+      g_propagate_error (error, my_error);
+      return FALSE;
+    }
+
+  g_tls_connection_set_ca_list (conn, ca_list);
+  for (l = ca_list; l; l = l->next)
+    g_object_unref (l->data);
+  g_list_free (ca_list);
+  return TRUE;
+}
+
+/**
+ * g_tls_connection_set_certificate:
+ * @conn: a #GTlsConnection
+ * @certificate: the certificate to use for @conn
+ *
+ * This sets the certificate that @conn will present to its peer
+ * during the TLS handshake. If this is not set,
+ * #GTlsConnection::need-certificate will be emitted during the
+ * handshake if needed.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_connection_set_certificate (GTlsConnection  *conn,
+				  GTlsCertificate *certificate)
+{
+  g_return_if_fail (G_IS_TLS_CONNECTION (conn));
+  g_return_if_fail (G_IS_TLS_CERTIFICATE (certificate));
+
+  if (conn->priv->certificate)
+    g_object_unref (conn->priv->certificate);
+  conn->priv->certificate = certificate ? g_object_ref (certificate) : NULL;
+  g_object_notify (G_OBJECT (conn), "certificate");
+}
+
+/**
+ * g_tls_connection_get_certificate:
+ * @conn: a #GTlsConnection
+ *
+ * Gets @conn's certificate, as set by
+ * g_tls_connection_set_certificate() or returned from one of the
+ * signals.
+ *
+ * Return value: @conn's certificate, or %NULL
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_connection_get_certificate (GTlsConnection *conn)
+{
+  g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), NULL);
+
+  return conn->priv->certificate;
+}
+
+/**
+ * g_tls_connection_get_peer_certificate:
+ * @conn: a #GTlsConnection
+ *
+ * Gets @conn's peer's certificate after it has been set during the
+ * handshake.
+ *
+ * Return value: @conn's peer's certificate, or %NULL
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_connection_get_peer_certificate (GTlsConnection *conn)
+{
+  g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), NULL);
+
+  return conn->priv->peer_certificate;
+}
+
+/**
+ * g_tls_connection_set_require_close_notify:
+ * @conn: a #GTlsConnection
+ * @require_close_notify: whether or not to require close notification
+ *
+ * Sets whether or not @conn requires a proper TLS close notification
+ * before closing the connection. If this is %TRUE (the default), then
+ * calling g_io_stream_close() on @conn will send a TLS close
+ * notification, and likewise it will expect to receive a close
+ * notification before the connection is closed when reading, and will
+ * return a %G_TLS_ERROR_EOF error if the connection is closed without
+ * proper notification (since this may indicate a network error, or
+ * man-in-the-middle attack).
+ *
+ * In some protocols, the application will know whether or not the
+ * connection was closed cleanly based on application-level data
+ * (because the application-level data includes a length field, or is
+ * somehow self-delimiting); in this case, the close notify is
+ * redundant and sometimes omitted. (TLS 1.1 explicitly allows this;
+ * in TLS 1.0 it is technically an error, but often done anyway.) You
+ * can use g_tls_connection_set_require_close_notify() to tell @conn to
+ * allow an "unannounced" connection close, in which case it is up to
+ * the application to check that the data has been fully received.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_connection_set_require_close_notify (GTlsConnection *conn,
+					   gboolean        require_close_notify)
+{
+  g_return_if_fail (G_IS_TLS_CONNECTION (conn));
+
+  g_object_set (G_OBJECT (conn),
+		"require-close-notify", require_close_notify,
+		NULL);
+}
+
+/**
+ * g_tls_connection_get_require_close_notify:
+ * @conn: a #GTlsConnection
+ *
+ * Tests whether or not @conn requires a proper TLS close notification
+ * before closing the connection. See
+ * g_tls_connection_set_require_close_notify() for details.
+ *
+ * Return value: %TRUE if @conn requires a proper TLS close
+ * notification.
+ *
+ * Since: 2.28
+ */
+gboolean
+g_tls_connection_get_require_close_notify (GTlsConnection *conn)
+{
+  gboolean require_close_notify;
+
+  g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), TRUE);
+
+  g_object_get (G_OBJECT (conn),
+		"require-close-notify", &require_close_notify,
+		NULL);
+  return require_close_notify;
+}
+
+/**
+ * g_tls_connection_handshake:
+ * @conn: a #GTlsConnection
+ * @cancellable: a #GCancellable, or %NULL
+ * @error: a #GError, or %NULL
+ *
+ * Attempts a TLS handshake on @conn.
+ *
+ * On the client side, it is never necessary to call this method;
+ * although the connection needs to perform a handshake after
+ * connecting (or after sending a "STARTTLS"-type command) and may
+ * need to rehandshake later if the server requests it,
+ * #GTlsConnection will handle this for you automatically when you try
+ * to send or receive data on the connection. However, you can call
+ * g_tls_connection_handshake() manually if you want to know for sure
+ * whether the initial handshake succeeded or failed (as opposed to
+ * just immediately trying to write to @conn's output stream, in which
+ * case if it fails, it may not be possible to tell if it failed
+ * before or after completing the handshake).
+ *
+ * Likewise, on the server side, although a handshake is necessary at
+ * the beginning of the communication, you do not need to call this
+ * function explicitly unless you want clearer error reporting.
+ * However, you may call g_tls_connection_handshake() later on to
+ * renegotiate parameters (encryption methods, etc) with the client.
+ *
+ * #GTlsConnection::accept_certificate and
+ * #GTlsConnection::need_certificate may be emitted during the
+ * handshake.
+ *
+ * Return value: success or failure
+ *
+ * Since: 2.28
+ */
+gboolean
+g_tls_connection_handshake (GTlsConnection   *conn,
+			    GCancellable     *cancellable,
+			    GError          **error)
+{
+  g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), FALSE);
+
+  return G_TLS_CONNECTION_GET_CLASS (conn)->handshake (conn, cancellable, error);
+}
+
+/**
+ * g_tls_connection_handshake_async:
+ * @conn: a #GTlsConnection
+ * @io_priority: the <link linkend="io-priority">I/O priority</link>
+ * of the request.
+ * @cancellable: a #GCancellable, or %NULL
+ * @callback: callback to call when the handshake is complete
+ * @user_data: the data to pass to the callback function
+ *
+ * Asynchronously performs a TLS handshake on @conn. See
+ * g_tls_connection_handshake() for more information.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_connection_handshake_async (GTlsConnection       *conn,
+				  int                   io_priority,
+				  GCancellable         *cancellable,
+				  GAsyncReadyCallback   callback,
+				  gpointer              user_data)
+{
+  g_return_if_fail (G_IS_TLS_CONNECTION (conn));
+
+  return G_TLS_CONNECTION_GET_CLASS (conn)->handshake_async (conn, io_priority,
+							     cancellable,
+							     callback, user_data);
+}
+
+/**
+ * g_tls_connection_handshake_finish:
+ * @conn: a #GTlsConnection
+ * @result: a #GAsyncResult.
+ * @error: a #GError pointer, or %NULL
+ *
+ * Finish an asynchronous TLS handshake operation. See
+ * g_tls_connection_handshake() for more information.
+ *
+ * Return value: %TRUE on success, %FALSE on failure, in which
+ * case @error will be set.
+ *
+ * Since: 2.28
+ */
+gboolean
+g_tls_connection_handshake_finish (GTlsConnection  *conn,
+				   GAsyncResult    *result,
+				   GError         **error)
+{
+  g_return_val_if_fail (G_IS_TLS_CONNECTION (conn), FALSE);
+
+  return G_TLS_CONNECTION_GET_CLASS (conn)->handshake_finish (conn, result, error);
+}
+
+/**
+ * g_tls_error_quark:
+ *
+ * Gets the TLS error quark.
+ *
+ * Return value: a #GQuark.
+ *
+ * Since: 2.28
+ */
+GQuark
+g_tls_error_quark (void)
+{
+  return g_quark_from_static_string ("g-tls-error-quark");
+}
+
+
+static gboolean
+g_tls_connection_certificate_accumulator (GSignalInvocationHint *ihint,
+					  GValue                *return_accu,
+					  const GValue          *handler_return,
+					  gpointer               dummy)
+{
+  GTlsCertificate *cert;
+
+  cert = g_value_get_object (handler_return);
+  if (cert)
+    g_value_set_object (return_accu, cert);
+
+  return cert != NULL;
+}
+
+/**
+ * g_tls_connection_emit_need_certificate:
+ * @conn: a #GTlsConnection
+ *
+ * Used by #GTlsConnection implementations to emit the
+ * #GTlsConnection::need-certificate signal.
+ *
+ * Since: 2.28
+ */
+GTlsCertificate *
+g_tls_connection_emit_need_certificate (GTlsConnection *conn)
+{
+  GTlsCertificate *cert = NULL;
+
+  g_signal_emit (conn, signals[NEED_CERTIFICATE], 0,
+		 &cert);
+  return cert;
+}
+
+/**
+ * g_tls_connection_emit_accept_certificate:
+ * @conn: a #GTlsConnection
+ *
+ * Used by #GTlsConnection implementations to emit the
+ * #GTlsConnection::accept-certificate signal.
+ *
+ * Since: 2.28
+ */
+gboolean
+g_tls_connection_emit_accept_certificate (GTlsConnection       *conn,
+					  GTlsCertificate      *peer_cert,
+					  GTlsValidationFlags   errors)
+{
+  gboolean accept = FALSE;
+
+  g_signal_emit (conn, signals[ACCEPT_CERTIFICATE], 0,
+		 peer_cert, errors, &accept);
+  return accept;
+}
+
+/**
+ * g_tls_connection_set_peer_certificate:
+ * @conn: a #GTlsConnection
+ * @certificate: the peer certificate
+ *
+ * Used by #GTlsConnection implementations to set the connection's
+ * peer certificate.
+ *
+ * Since: 2.28
+ */
+void
+g_tls_connection_set_peer_certificate (GTlsConnection  *conn,
+				       GTlsCertificate *certificate)
+{
+  if (conn->priv->peer_certificate)
+    g_object_unref (conn->priv->peer_certificate);
+  conn->priv->peer_certificate = certificate ? g_object_ref (certificate) : NULL;
+  g_object_notify (G_OBJECT (conn), "peer-certificate");
+}
diff --git a/gio/gtlsconnection.h b/gio/gtlsconnection.h
new file mode 100644
index 0000000..728fdab
--- /dev/null
+++ b/gio/gtlsconnection.h
@@ -0,0 +1,141 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2009 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_CONNECTION_H__
+#define __G_TLS_CONNECTION_H__
+
+#include <gio/giostream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_CONNECTION            (g_tls_connection_get_type ())
+#define G_TLS_CONNECTION(inst)           (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_CONNECTION, GTlsConnection))
+#define G_TLS_CONNECTION_CLASS(class)    (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_TLS_CONNECTION, GTlsConnectionClass))
+#define G_IS_TLS_CONNECTION(inst)        (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_CONNECTION))
+#define G_IS_TLS_CONNECTION_CLASS(class) (G_TYPE_CHECK_CLASS_TYPE ((class), G_TYPE_TLS_CONNECTION))
+#define G_TLS_CONNECTION_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), G_TYPE_TLS_CONNECTION, GTlsConnectionClass))
+
+/**
+ * GTlsConnection:
+ *
+ * TLS connection. This is an abstract type that will be subclassed by
+ * a TLS-library-specific subtype.
+ *
+ * Since: 2.28
+ */
+typedef struct _GTlsConnectionClass   GTlsConnectionClass;
+typedef struct _GTlsConnectionPrivate GTlsConnectionPrivate;
+
+struct _GTlsConnection {
+  GIOStream parent_instance;
+
+  GTlsConnectionPrivate *priv;
+};
+
+struct _GTlsConnectionClass
+{
+  GIOStreamClass parent_class;
+
+  /* signals */
+  GTlsCertificate * ( *need_certificate)   (GTlsConnection      *connection);
+
+  gboolean          ( *accept_certificate) (GTlsConnection      *connection,
+					    GTlsCertificate     *peer_cert,
+					    GTlsValidationFlags  errors);
+
+  /* methods */
+  gboolean ( *handshake )        (GTlsConnection       *conn,
+				  GCancellable         *cancellable,
+				  GError              **error);
+
+  void     ( *handshake_async )  (GTlsConnection       *conn,
+				  int                   io_priority,
+				  GCancellable         *cancellable,
+				  GAsyncReadyCallback   callback,
+				  gpointer              user_data);
+  gboolean ( *handshake_finish ) (GTlsConnection       *conn,
+				  GAsyncResult         *result,
+				  GError              **error);
+
+  /*< private >*/
+  /* Padding for future expansion */
+  gpointer padding[8];
+};
+
+GType            g_tls_connection_get_type                 (void) G_GNUC_CONST;
+
+GList *          g_tls_connection_get_ca_list              (GTlsConnection       *conn);
+void             g_tls_connection_set_ca_list              (GTlsConnection       *conn,
+							    GList                *ca_list);
+gboolean         g_tls_connection_set_ca_list_from_file    (GTlsConnection       *conn,
+							    const char           *ca_file,
+							    GError              **error);
+
+void             g_tls_connection_set_certificate          (GTlsConnection       *conn,
+							    GTlsCertificate      *certificate);
+GTlsCertificate *g_tls_connection_get_certificate          (GTlsConnection       *conn);
+
+GTlsCertificate *g_tls_connection_get_peer_certificate     (GTlsConnection       *conn);
+
+void             g_tls_connection_set_require_close_notify (GTlsConnection       *conn,
+							    gboolean              require_close_notify);
+gboolean         g_tls_connection_get_require_close_notify (GTlsConnection       *conn);
+
+
+gboolean         g_tls_connection_handshake                (GTlsConnection       *conn,
+							    GCancellable         *cancellable,
+							    GError              **error);
+
+void             g_tls_connection_handshake_async          (GTlsConnection       *conn,
+							    int                   io_priority,
+							    GCancellable         *cancellable,
+							    GAsyncReadyCallback   callback,
+							    gpointer              user_data);
+gboolean         g_tls_connection_handshake_finish         (GTlsConnection       *conn,
+							    GAsyncResult         *result,
+							    GError              **error);
+
+/**
+ * G_TLS_ERROR:
+ *
+ * Error domain for TLS. Errors in this domain will be from the
+ * #GTlsError enumeration. See #GError for more information on error
+ * domains.
+ */
+#define G_TLS_ERROR (g_tls_error_quark ())
+GQuark g_tls_error_quark (void);
+
+
+/*< protected >*/
+GTlsCertificate      *g_tls_connection_emit_need_certificate    (GTlsConnection       *conn);
+gboolean              g_tls_connection_emit_accept_certificate  (GTlsConnection       *conn,
+								 GTlsCertificate      *peer_cert,
+								 GTlsValidationFlags   errors);
+
+void                  g_tls_connection_set_peer_certificate     (GTlsConnection       *conn,
+								 GTlsCertificate      *certificate);
+
+G_END_DECLS
+
+#endif /* __G_TLS_CONNECTION_H__ */
diff --git a/gio/gtlsserverconnection.c b/gio/gtlsserverconnection.c
new file mode 100644
index 0000000..b7d555b
--- /dev/null
+++ b/gio/gtlsserverconnection.c
@@ -0,0 +1,105 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "glib.h"
+
+#include "gtlsserverconnection.h"
+#include "ginitable.h"
+#include "gio-marshal.h"
+#include "gioenumtypes.h"
+#include "gsocket.h"
+#include "gtlsbackend.h"
+#include "gtlscertificate.h"
+#include "glibintl.h"
+
+/**
+ * SECTION:gtlsserverconnection
+ * @short_description: TLS server-side connection
+ * @include: gio/gio.h
+ *
+ * #GTlsServerConnection is the server-side subclass of #GTlsConnection,
+ * representing a server-side TLS connection.
+ *
+ * Since: 2.28
+ */
+
+G_DEFINE_INTERFACE (GTlsServerConnection, g_tls_server_connection, G_TYPE_TLS_CONNECTION)
+
+static void
+g_tls_server_connection_default_init (GTlsServerConnectionInterface *iface)
+{
+  /**
+   * GTlsServerConnection:authentication-mode:
+   *
+   * The #GTlsAuthenticationMode for the server. This can be changed
+   * before calling g_tls_connection_handshake() if you want to
+   * rehandshake with a different mode from the initial handshake.
+   *
+   * Since: 2.28
+   */
+  g_object_interface_install_property (iface,
+				       g_param_spec_enum ("authentication-mode",
+							  P_("Authentication Mode"),
+							  P_("The client authentication mode"),
+							  G_TYPE_TLS_AUTHENTICATION_MODE,
+							  G_TLS_AUTHENTICATION_NONE,
+							  G_PARAM_READWRITE |
+							  G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * g_tls_server_connection_new:
+ * @error: #GError for error reporting, or %NULL to ignore.
+ * @...: a %NULL-terminated list of property values and names
+ *
+ * Creates a new #GTlsServerConnection based on the #GIOstream
+ * specified as "base-io-stream" in the property list. This is a
+ * wrapper around g_initable_new() that looks up the correct type from
+ * the active #GTlsBackend.
+ *
+ * Return value: the new #GTlsServerConnection, or %NULL on error
+ *
+ * Since: 2.28
+ */
+GTlsServerConnection *
+g_tls_server_connection_new (GError    **error,
+			     ...)
+{
+  GObject *conn;
+  GTlsBackend *backend;
+  const gchar *first_property_name;
+  va_list ap;
+
+  va_start (ap, error);
+  first_property_name = va_arg (ap, const gchar *);
+  /* There must be at least a "base-io-stream" property, since that's
+   * required and construct-only.
+   */
+  g_return_val_if_fail (first_property_name != NULL, NULL);
+
+  backend = g_tls_backend_get_default ();
+  conn = g_initable_new_valist (g_tls_backend_get_server_connection_type (backend),
+				first_property_name, ap,
+				NULL, error);
+  va_end (ap);
+
+  return G_TLS_SERVER_CONNECTION (conn);
+}
diff --git a/gio/gtlsserverconnection.h b/gio/gtlsserverconnection.h
new file mode 100644
index 0000000..07e33cd
--- /dev/null
+++ b/gio/gtlsserverconnection.h
@@ -0,0 +1,60 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 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 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, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#if !defined (__GIO_GIO_H_INSIDE__) && !defined (GIO_COMPILATION)
+#error "Only <gio/gio.h> can be included directly."
+#endif
+
+#ifndef __G_TLS_SERVER_CONNECTION_H__
+#define __G_TLS_SERVER_CONNECTION_H__
+
+#include <gio/gtlsconnection.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_SERVER_CONNECTION                (g_tls_server_connection_get_type ())
+#define G_TLS_SERVER_CONNECTION(inst)               (G_TYPE_CHECK_INSTANCE_CAST ((inst), G_TYPE_TLS_SERVER_CONNECTION, GTlsServerConnection))
+#define G_IS_TLS_SERVER_CONNECTION(inst)            (G_TYPE_CHECK_INSTANCE_TYPE ((inst), G_TYPE_TLS_SERVER_CONNECTION))
+#define G_TLS_SERVER_CONNECTION_GET_INTERFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), G_TYPE_TLS_SERVER_CONNECTION, GTlsServerConnectionInterface))
+
+/**
+ * GTlsServerConnection:
+ *
+ * TLS server-side connection. This is the server-side implementation
+ * of a #GTlsConnection.
+ *
+ * Since: 2.28
+ */
+typedef struct _GTlsServerConnectionInterface GTlsServerConnectionInterface;
+
+struct _GTlsServerConnectionInterface
+{
+  GTypeInterface g_iface;
+
+};
+
+GType                 g_tls_server_connection_get_type                 (void) G_GNUC_CONST;
+
+GTlsServerConnection *g_tls_server_connection_new                      (GError                 **error,
+									...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* __G_TLS_SERVER_CONNECTION_H__ */
diff --git a/gio/tests/.gitignore b/gio/tests/.gitignore
index 0663fef..5e5601e 100644
--- a/gio/tests/.gitignore
+++ b/gio/tests/.gitignore
@@ -72,6 +72,7 @@ socket-client
 socket-server
 srvtarget
 test.mo
+tls-test
 unix-fd
 unix-streams
 volumemonitor
diff --git a/gio/tests/Makefile.am b/gio/tests/Makefile.am
index ad2273a..160699c 100644
--- a/gio/tests/Makefile.am
+++ b/gio/tests/Makefile.am
@@ -90,9 +90,9 @@ SAMPLE_PROGS = 				\
 	gapplication-example-cmdline	\
 	gapplication-example-cmdline2	\
 	gapplication-example-actions	\
+	tls-test			\
 	$(NULL)
 
-
 if OS_UNIX
 TEST_PROGS += live-g-file desktop-app-info unix-fd unix-streams gapplication
 SAMPLE_PROGS +=	gdbus-example-unix-fd-client
@@ -403,6 +403,11 @@ proxy_SOURCES = proxy.c
 proxy_LDADD   = $(progs_ldadd) \
 	$(top_builddir)/gthread/libgthread-2.0.la
 
+tls_test_SOURCES	  = tls-test.c
+tls_test_LDADD		  = $(progs_ldadd) \
+	$(top_builddir)/gthread/libgthread-2.0.la \
+	$(LIBGNUTLS_LIBS) $(LIBGCRYPT_LIBS)
+
 EXTRA_DIST += \
 	socket-common.c			\
 	org.gtk.test.gschema		\
@@ -417,7 +422,7 @@ EXTRA_DIST += \
 	appinfo-test-notgnome.desktop	\
 	gdbus-testserver.py
 
-MISC_STUFF = test.mo
+MISC_STUFF = gschemas.compiled test.mo
 
 test.mo: de.po
 	$(MSGFMT) -o test.mo $(srcdir)/de.po; \
diff --git a/gio/tests/socket-client.c b/gio/tests/socket-client.c
index 1f1455e..52f1392 100644
--- a/gio/tests/socket-client.c
+++ b/gio/tests/socket-client.c
@@ -5,17 +5,15 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "socket-common.c"
-
 GMainLoop *loop;
 
 gboolean verbose = FALSE;
 gboolean non_blocking = FALSE;
 gboolean use_udp = FALSE;
-gboolean use_source = FALSE;
 int cancel_timeout = 0;
 int read_timeout = 0;
 gboolean unix_socket = FALSE;
+gboolean tls = FALSE;
 
 static GOptionEntry cmd_entries[] = {
   {"cancel", 'c', 0, G_OPTION_ARG_INT, &cancel_timeout,
@@ -26,70 +24,39 @@ static GOptionEntry cmd_entries[] = {
    "Be verbose", NULL},
   {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking,
    "Enable non-blocking i/o", NULL},
-  {"use-source", 's', 0, G_OPTION_ARG_NONE, &use_source,
-   "Use GSource to wait for non-blocking i/o", NULL},
 #ifdef G_OS_UNIX
   {"unix", 'U', 0, G_OPTION_ARG_NONE, &unix_socket,
    "Use a unix socket instead of IP", NULL},
 #endif
   {"timeout", 't', 0, G_OPTION_ARG_INT, &read_timeout,
    "Time out reads after the specified number of seconds", NULL},
+  {"tls", 'T', 0, G_OPTION_ARG_NONE, &tls,
+   "Use TLS (SSL)", NULL},
   {NULL}
 };
 
-static gboolean
-source_ready (gpointer data,
-	      GIOCondition condition)
-{
-  g_main_loop_quit (loop);
-  return FALSE;
-}
-
-static void
-ensure_condition (GSocket *socket,
-		  const char *where,
-		  GCancellable *cancellable,
-		  GIOCondition condition)
-{
-  GError *error = NULL;
-  GSource *source;
-
-  if (!non_blocking)
-    return;
-
-  if (use_source)
-    {
-      source = g_socket_create_source (socket,
-                                       condition,
-                                       cancellable);
-      g_source_set_callback (source,
-                             (GSourceFunc) source_ready,
-			     NULL, NULL);
-      g_source_attach (source, NULL);
-      g_source_unref (source);
-      g_main_loop_run (loop);
-    }
-  else
-    {
-      if (!g_socket_condition_wait (socket, condition, cancellable, &error))
-	{
-	  g_printerr ("condition wait error for %s: %s\n",
-		      where,
-		      error->message);
-	  exit (1);
-	}
-    }
-}
+#include "socket-common.c"
 
-static gpointer
-cancel_thread (gpointer data)
+static gboolean
+accept_certificate (GTlsClientConnection *conn, GTlsCertificate *cert,
+		    GTlsValidationFlags errors, gpointer user_data)
 {
-  GCancellable *cancellable = data;
-
-  g_usleep (1000*1000*cancel_timeout);
-  g_print ("Cancelling\n");
-  g_cancellable_cancel (cancellable);
-  return NULL;
+  g_print ("Certificate would have been rejected ( ");
+  if (errors & G_TLS_VALIDATE_CA)
+    g_print ("unknown-ca ");
+  if (errors & G_TLS_VALIDATE_IDENTITY)
+    g_print ("identity-mismatch ");
+  if (errors & G_TLS_VALIDATE_ACTIVATION)
+    g_print ("not-activated ");
+  if (errors & G_TLS_VALIDATE_EXPIRATION)
+    g_print ("expired ");
+  if (errors & G_TLS_VALIDATE_REVOCATION)
+    g_print ("revoked ");
+  if (errors & G_TLS_VALIDATE_ALGORITHM)
+    g_print ("bad-algorithm ");
+  g_print (") but accepting anyway.\n");
+
+  return TRUE;
 }
 
 int
@@ -106,6 +73,9 @@ main (int argc,
   GCancellable *cancellable;
   GSocketAddressEnumerator *enumerator;
   GSocketConnectable *connectable;
+  GIOStream *connection;
+  GInputStream *istream;
+  GOutputStream *ostream;
 
   g_thread_init (NULL);
 
@@ -125,6 +95,12 @@ main (int argc,
       return 1;
     }
 
+  if (use_udp && tls)
+    {
+      g_printerr ("DTLS (TLS over UDP) is not supported");
+      return 1;
+    }
+
   if (cancel_timeout)
     {
       cancellable = g_cancellable_new ();
@@ -201,15 +177,10 @@ main (int argc,
       g_object_unref (address);
     }
   g_object_unref (enumerator);
-  g_object_unref (connectable);
 
   g_print ("Connected to %s\n",
 	   socket_address_to_string (address));
 
-  /* TODO: Test non-blocking connect */
-  if (non_blocking)
-    g_socket_set_blocking (socket, FALSE);
-
   src_address = g_socket_get_local_address (socket, &error);
   if (!src_address)
     {
@@ -221,6 +192,52 @@ main (int argc,
 	   socket_address_to_string (src_address));
   g_object_unref (src_address);
 
+  if (use_udp)
+    connection = NULL;
+  else
+    connection = G_IO_STREAM (g_socket_connection_factory_create_connection (socket));
+
+  if (tls)
+    {
+      GTlsClientConnection *tls_conn;
+
+      tls_conn = g_tls_client_connection_new (&error,
+					      "base-io-stream", connection,
+					      "server-identity", connectable,
+					      NULL);
+      if (!tls_conn)
+	{
+	  g_printerr ("Could not create TLS connection: %s\n",
+		      error->message);
+	  return 1;
+	}
+
+      g_signal_connect (tls_conn, "accept-certificate",
+			G_CALLBACK (accept_certificate), NULL);
+
+      if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn),
+				       cancellable, &error))
+	{
+	  g_printerr ("Error during TLS handshake: %s\n",
+		      error->message);
+	  return 1;
+	}
+
+      g_object_unref (connection);
+      connection = G_IO_STREAM (tls_conn);
+    }
+  g_object_unref (connectable);
+
+  if (connection)
+    {
+      istream = g_io_stream_get_input_stream (connection);
+      ostream = g_io_stream_get_output_stream (connection);
+    }
+
+  /* TODO: Test non-blocking connect/handshake */
+  if (non_blocking)
+    g_socket_set_blocking (socket, FALSE);
+
   while (TRUE)
     {
       gchar buffer[4096];
@@ -233,14 +250,20 @@ main (int argc,
       to_send = strlen (buffer);
       while (to_send > 0)
 	{
-	  ensure_condition (socket, "send", cancellable, G_IO_OUT);
 	  if (use_udp)
-	    size = g_socket_send_to (socket, address,
-				     buffer, to_send,
-				     cancellable, &error);
+	    {
+	      ensure_socket_condition (socket, G_IO_OUT, cancellable);
+	      size = g_socket_send_to (socket, address,
+				       buffer, to_send,
+				       cancellable, &error);
+	    }
 	  else
-	    size = g_socket_send (socket, buffer, to_send,
-				  cancellable, &error);
+	    {
+	      ensure_connection_condition (connection, G_IO_OUT, cancellable);
+	      size = g_output_stream_write (ostream,
+					    buffer, to_send,
+					    cancellable, &error);
+	    }
 
 	  if (size < 0)
 	    {
@@ -272,14 +295,20 @@ main (int argc,
 	  to_send -= size;
 	}
 
-      ensure_condition (socket, "receive", cancellable, G_IO_IN);
       if (use_udp)
-	size = g_socket_receive_from (socket, &src_address,
+	{
+	  ensure_socket_condition (socket, G_IO_IN, cancellable);
+	  size = g_socket_receive_from (socket, &src_address,
+					buffer, sizeof buffer,
+					cancellable, &error);
+	}
+      else
+	{
+	  ensure_connection_condition (connection, G_IO_IN, cancellable);
+	  size = g_input_stream_read (istream,
 				      buffer, sizeof buffer,
 				      cancellable, &error);
-      else
-	size = g_socket_receive (socket, buffer, sizeof buffer,
-				 cancellable, &error);
+	}
 
       if (size < 0)
 	{
@@ -306,15 +335,28 @@ main (int argc,
 
   g_print ("closing socket\n");
 
-  if (!g_socket_close (socket, &error))
+  if (connection)
     {
-      g_printerr ("Error closing master socket: %s\n",
-		  error->message);
-      return 1;
+      if (!g_io_stream_close (connection, cancellable, &error))
+	{
+	  g_printerr ("Error closing connection: %s\n",
+		      error->message);
+	  return 1;
+	}
+      g_object_unref (connection);
+    }
+  else
+    {
+      if (!g_socket_close (socket, &error))
+	{
+	  g_printerr ("Error closing master socket: %s\n",
+		      error->message);
+	  return 1;
+	}
     }
 
-  g_object_unref (G_OBJECT (socket));
-  g_object_unref (G_OBJECT (address));
+  g_object_unref (socket);
+  g_object_unref (address);
 
   return 0;
 }
diff --git a/gio/tests/socket-common.c b/gio/tests/socket-common.c
index 8d2933b..fdee383 100644
--- a/gio/tests/socket-common.c
+++ b/gio/tests/socket-common.c
@@ -58,3 +58,64 @@ socket_address_from_string (const char *name)
 #endif
   return NULL;
 }
+
+static gboolean
+source_ready (GPollableInputStream *stream,
+	      gpointer              data)
+{
+  g_main_loop_quit (loop);
+  return FALSE;
+}
+
+static void
+ensure_socket_condition (GSocket      *socket,
+			 GIOCondition  condition,
+			 GCancellable *cancellable)
+{
+  GSource *source;
+
+  if (!non_blocking)
+    return;
+
+  source = g_socket_create_source (socket, condition, cancellable);
+  g_source_set_callback (source,
+			 (GSourceFunc) source_ready,
+			 NULL, NULL);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+  g_main_loop_run (loop);
+}
+
+static void
+ensure_connection_condition (GIOStream    *stream,
+			     GIOCondition  condition,
+			     GCancellable *cancellable)
+{
+  GSource *source;
+
+  if (!non_blocking)
+    return;
+
+  if (condition & G_IO_IN)
+    source = g_pollable_input_stream_create_source (G_POLLABLE_INPUT_STREAM (g_io_stream_get_input_stream (stream)), cancellable);
+  else
+    source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (g_io_stream_get_output_stream (stream)), cancellable);
+
+  g_source_set_callback (source,
+			 (GSourceFunc) source_ready,
+			 NULL, NULL);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+  g_main_loop_run (loop);
+}
+
+static gpointer
+cancel_thread (gpointer data)
+{
+  GCancellable *cancellable = data;
+
+  g_usleep (1000*1000*cancel_timeout);
+  g_print ("Cancelling\n");
+  g_cancellable_cancel (cancellable);
+  return NULL;
+}
diff --git a/gio/tests/socket-server.c b/gio/tests/socket-server.c
index 562f1ef..0bf8c33 100644
--- a/gio/tests/socket-server.c
+++ b/gio/tests/socket-server.c
@@ -4,8 +4,6 @@
 #include <stdlib.h>
 #include <string.h>
 
-#include "socket-common.c"
-
 GMainLoop *loop;
 
 int port = 7777;
@@ -13,11 +11,11 @@ gboolean verbose = FALSE;
 gboolean dont_reuse_address = FALSE;
 gboolean non_blocking = FALSE;
 gboolean use_udp = FALSE;
-gboolean use_source = FALSE;
 int cancel_timeout = 0;
 int read_timeout = 0;
 int delay = 0;
 gboolean unix_socket = FALSE;
+const char *tls_cert_file = NULL;
 
 static GOptionEntry cmd_entries[] = {
   {"port", 'p', 0, G_OPTION_ARG_INT, &port,
@@ -32,8 +30,6 @@ static GOptionEntry cmd_entries[] = {
    "Don't SOADDRREUSE", NULL},
   {"non-blocking", 'n', 0, G_OPTION_ARG_NONE, &non_blocking,
    "Enable non-blocking i/o", NULL},
-  {"use-source", 's', 0, G_OPTION_ARG_NONE, &use_source,
-   "Use GSource to wait for non-blocking i/o", NULL},
 #ifdef G_OS_UNIX
   {"unix", 'U', 0, G_OPTION_ARG_NONE, &unix_socket,
    "Use a unix socket instead of IP", NULL},
@@ -42,63 +38,12 @@ static GOptionEntry cmd_entries[] = {
    "Delay responses by the specified number of seconds", NULL},
   {"timeout", 't', 0, G_OPTION_ARG_INT, &read_timeout,
    "Time out reads after the specified number of seconds", NULL},
+  {"tls", 'T', 0, G_OPTION_ARG_STRING, &tls_cert_file,
+   "Use TLS (SSL) with indicated server certificate", "CERTFILE"},
   {NULL}
 };
 
-static gboolean
-source_ready (gpointer data,
-	      GIOCondition condition)
-{
-  g_main_loop_quit (loop);
-  return FALSE;
-}
-
-static void
-ensure_condition (GSocket *socket,
-		  const char *where,
-		  GCancellable *cancellable,
-		  GIOCondition condition)
-{
-  GError *error = NULL;
-  GSource *source;
-
-  if (!non_blocking)
-    return;
-
-  if (use_source)
-    {
-      source = g_socket_create_source (socket,
-                                       condition,
-                                       cancellable);
-      g_source_set_callback (source,
-                             (GSourceFunc) source_ready,
-			     NULL, NULL);
-      g_source_attach (source, NULL);
-      g_source_unref (source);
-      g_main_loop_run (loop);
-    }
-  else
-    {
-      if (!g_socket_condition_wait (socket, condition, cancellable, &error))
-	{
-	  g_printerr ("condition wait error for %s: %s\n",
-		      where,
-		      error->message);
-	  exit (1);
-	}
-    }
-}
-
-static gpointer
-cancel_thread (gpointer data)
-{
-  GCancellable *cancellable = data;
-
-  g_usleep (1000*1000*cancel_timeout);
-  g_print ("Cancelling\n");
-  g_cancellable_cancel (cancellable);
-  return NULL;
-}
+#include "socket-common.c"
 
 int
 main (int argc,
@@ -113,6 +58,10 @@ main (int argc,
   GOptionContext *context;
   GCancellable *cancellable;
   char *display_addr;
+  GTlsCertificate *tlscert = NULL;
+  GIOStream *connection;
+  GInputStream *istream;
+  GOutputStream *ostream;
 
   g_thread_init (NULL);
 
@@ -142,6 +91,23 @@ main (int argc,
       cancellable = NULL;
     }
 
+  if (tls_cert_file)
+    {
+      if (use_udp)
+	{
+	  g_printerr ("DTLS (TLS over UDP) is not supported");
+	  return 1;
+	}
+
+      tlscert = g_tls_certificate_new_from_file (tls_cert_file, &error);
+      if (!tlscert)
+	{
+	  g_printerr ("Could not read server certificate '%s': %s\n",
+		      tls_cert_file, error->message);
+	  return 1;
+	}
+    }
+
   loop = g_main_loop_new (NULL, FALSE);
 
   if (use_udp)
@@ -205,7 +171,7 @@ main (int argc,
       g_print ("listening on %s...\n", display_addr);
       g_free (display_addr);
 
-      ensure_condition (socket, "accept", cancellable, G_IO_IN);
+      ensure_socket_condition (socket, G_IO_IN, cancellable);
       new_socket = g_socket_accept (socket, cancellable, &error);
       if (!new_socket)
 	{
@@ -233,13 +199,48 @@ main (int argc,
       g_object_unref (address);
 
       recv_socket = new_socket;
+
+      connection = G_IO_STREAM (g_socket_connection_factory_create_connection (recv_socket));
+      g_object_unref (new_socket);
     }
   else
     {
       recv_socket = socket;
-      new_socket = NULL;
+      connection = NULL;
     }
 
+  if (tlscert)
+    {
+      GTlsServerConnection *tls_conn;
+
+      tls_conn = g_tls_server_connection_new (&error,
+					      "base-io-stream", connection,
+					      "certificate", tlscert,
+					      NULL);
+      if (!tls_conn)
+	{
+	  g_printerr ("Could not create TLS connection: %s\n",
+		      error->message);
+	  return 1;
+	}
+
+      if (!g_tls_connection_handshake (G_TLS_CONNECTION (tls_conn),
+				       cancellable, &error))
+	{
+	  g_printerr ("Error during TLS handshake: %s\n",
+		      error->message);
+	  return 1;
+       }
+
+      g_object_unref (connection);
+      connection = G_IO_STREAM (tls_conn);
+    }
+
+  if (connection)
+    {
+      istream = g_io_stream_get_input_stream (connection);
+      ostream = g_io_stream_get_output_stream (connection);
+    }
 
   while (TRUE)
     {
@@ -247,14 +248,20 @@ main (int argc,
       gssize size;
       gsize to_send;
 
-      ensure_condition (recv_socket, "receive", cancellable, G_IO_IN);
       if (use_udp)
-	size = g_socket_receive_from (recv_socket, &address,
+	{
+	  ensure_socket_condition (recv_socket, G_IO_IN, cancellable);
+	  size = g_socket_receive_from (recv_socket, &address,
+					buffer, sizeof buffer,
+					cancellable, &error);
+	}
+      else
+	{
+	  ensure_connection_condition (connection, G_IO_IN, cancellable);
+	  size = g_input_stream_read (istream,
 				      buffer, sizeof buffer,
 				      cancellable, &error);
-      else
-	size = g_socket_receive (recv_socket, buffer, sizeof buffer,
-				 cancellable, &error);
+	}
 
       if (size < 0)
 	{
@@ -288,13 +295,19 @@ main (int argc,
 
       while (to_send > 0)
 	{
-	  ensure_condition (recv_socket, "send", cancellable, G_IO_OUT);
 	  if (use_udp)
-	    size = g_socket_send_to (recv_socket, address,
-				     buffer, to_send, cancellable, &error);
+	    {
+	      ensure_socket_condition (recv_socket, G_IO_OUT, cancellable);
+	      size = g_socket_send_to (recv_socket, address,
+				       buffer, to_send, cancellable, &error);
+	    }
 	  else
-	    size = g_socket_send (recv_socket, buffer, to_send,
-				  cancellable, &error);
+	    {
+	      ensure_connection_condition (connection, G_IO_OUT, cancellable);
+	      size = g_output_stream_write (ostream,
+					    buffer, to_send,
+					    cancellable, &error);
+	    }
 
 	  if (size < 0)
 	    {
@@ -329,16 +342,15 @@ main (int argc,
 
   g_print ("connection closed\n");
 
-  if (new_socket)
+  if (connection)
     {
-      if (!g_socket_close (new_socket, &error))
+      if (!g_io_stream_close (connection, NULL, &error))
 	{
-	  g_printerr ("Error closing connection socket: %s\n",
+	  g_printerr ("Error closing connection stream: %s\n",
 		      error->message);
 	  return 1;
 	}
-
-      g_object_unref (G_OBJECT (new_socket));
+      g_object_unref (connection);
     }
 
   if (!g_socket_close (socket, &error))
@@ -347,8 +359,7 @@ main (int argc,
 		  error->message);
       return 1;
     }
-
-  g_object_unref (G_OBJECT (socket));
+  g_object_unref (socket);
 
   return 0;
 }
diff --git a/gio/tests/test-cert.pem b/gio/tests/test-cert.pem
new file mode 100644
index 0000000..09c87b0
--- /dev/null
+++ b/gio/tests/test-cert.pem
@@ -0,0 +1,37 @@
+-----BEGIN CERTIFICATE-----
+MIIDjzCCAvigAwIBAgIBADANBgkqhkiG9w0BAQQFADCBkjELMAkGA1UEBhMCVVMx
+FjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxDzANBgNVBAcTBkJvc3RvbjEPMA0GA1UE
+ChMGWGltaWFuMRUwEwYDVQQLEwxTb3VwIEtpdGNoZW4xEjAQBgNVBAMTCWxvY2Fs
+aG9zdDEeMBwGCSqGSIb3DQEJARYPc291cEB4aW1pYW4uY29tMB4XDTAzMDkyMzE4
+Mzc0MVoXDTEzMDkyMzE4Mzc0MVowgZIxCzAJBgNVBAYTAlVTMRYwFAYDVQQIEw1N
+YXNzYWNodXNldHRzMQ8wDQYDVQQHEwZCb3N0b24xDzANBgNVBAoTBlhpbWlhbjEV
+MBMGA1UECxMMU291cCBLaXRjaGVuMRIwEAYDVQQDEwlsb2NhbGhvc3QxHjAcBgkq
+hkiG9w0BCQEWD3NvdXBAeGltaWFuLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAwzT/WxfdXqb2hbyjQav3FtN7tLxj3UbZKCKDYlizBsNLxb9exfebhV4h
+CoAcaSNvLUnk3tAXnk+BDsIC1V4SbwqHYR17PnO3YZ8fkNwh5RGZwNx+zafdfFyu
++3Sh+mE03bljpDlTsgPL8CiFCd68MPRnuHoKt5iTpSyLC6Df0qcCAwEAAaOB8jCB
+7zAdBgNVHQ4EFgQU9A9omrgBK5Kkl6FRxrgJU2voj4Uwgb8GA1UdIwSBtzCBtIAU
+9A9omrgBK5Kkl6FRxrgJU2voj4WhgZikgZUwgZIxCzAJBgNVBAYTAlVTMRYwFAYD
+VQQIEw1NYXNzYWNodXNldHRzMQ8wDQYDVQQHEwZCb3N0b24xDzANBgNVBAoTBlhp
+bWlhbjEVMBMGA1UECxMMU291cCBLaXRjaGVuMRIwEAYDVQQDEwlsb2NhbGhvc3Qx
+HjAcBgkqhkiG9w0BCQEWD3NvdXBAeGltaWFuLmNvbYIBADAMBgNVHRMEBTADAQH/
+MA0GCSqGSIb3DQEBBAUAA4GBAGCV56N7bEDNdE76T8i68gS00NIVVosVQjS39Ojd
+ED+rvq0YYvuc2UXlzAonuCJfwFc73g4wSIjS0xijF5rnugZ+aay0LNv2y+Rf34CQ
+RNswrwurFjlxgTOO+Wx2IM64mAnBfj43M8uKEZFqAiGKrZZ0xIqyUMlku0FgXDH2
+Jvpg
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDDNP9bF91epvaFvKNBq/cW03u0vGPdRtkoIoNiWLMGw0vFv17F
+95uFXiEKgBxpI28tSeTe0BeeT4EOwgLVXhJvCodhHXs+c7dhnx+Q3CHlEZnA3H7N
+p918XK77dKH6YTTduWOkOVOyA8vwKIUJ3rww9Ge4egq3mJOlLIsLoN/SpwIDAQAB
+AoGAOGAi6zzuKrrPcXo0L/ApEQeMr3rE4I/ogUXOaeWx9l8KkBafmU7UNGUl57Fu
+AxM/tXWkypCQcaEGZau0Q8jCS5wKgynNi72F4OzBqgjgW4vvtrjfC1LagnCd2ZMX
+V5XVECjO/sEDg0hJeOsXlKbECAgvHMU3dSCGO7DmuG9tIxkCQQDsth1VvVjOdfp6
+klOfYzbAM1p9HIcNPJMeuBFqq//UHX4aPqh/6G6W06TOTN+bjZBmitG9yjV958t2
+rPxl64f7AkEA0x0WOLm5S0LNsv7zwjXuTcj+NCHL36b3dK90oxX8Gq69PANL/EJY
+ItpHNLgzzo4DRmQy8q0WZlC9HYk1YljERQJAEN7+AkFnlfeErb3GJgMNQO+oEGi7
+G29o0PSvkRnHNxgPB9HVcqBfWXKmOWnzOgQB+b0FK/DAlUOzFbdImf8KhwJAFLty
+hzeV/tIcqUtoXNY3BOSMMkpvXxNikc75QVrTWzt10gLw32EUjreo7oB4dfx0TeFh
+L3vYC0w6hkAHQhU9kQJAPSEQ+Bqzlk6BrQNrNFEVzi1Rwpz7LOzhOjuYW6bsiAdX
+axA4r6Xh25x08ZU7cqX7gwVLHL6pgrEKuUs0Nc5Klg==
+-----END RSA PRIVATE KEY-----
diff --git a/gio/tests/tls-test.c b/gio/tests/tls-test.c
new file mode 100644
index 0000000..853e1dd
--- /dev/null
+++ b/gio/tests/tls-test.c
@@ -0,0 +1,340 @@
+#include <gnutls/gnutls.h>
+#include <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <gio/gio.h>
+
+#define BUFSIZE 1024
+
+static GMainLoop *loop;
+
+/* SERVER */
+
+/* Read @bufsize bytes into @buf from @session. */
+static void
+server_read (GTlsConnection *conn, char *buf, int bufsize)
+{
+  int total, nread;
+  GError *error = NULL;
+  GInputStream *stream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
+
+  total = 0;
+  while (total < bufsize)
+    {
+      nread = g_input_stream_read (stream, buf + total,
+				   bufsize - total, NULL,
+				   &error);
+      if (nread <= 0)
+	{
+	  g_error ("server read failed at position %d: %s", total,
+		   error->message);
+	}
+      total += nread;
+    }
+}
+
+/* Write @bufsize bytes from @buf to @session, forcing 3 rehandshakes
+ * along the way. (We do an odd number of rehandshakes to make sure
+ * they occur at weird times relative to the client's read buffer
+ * size.)
+ */
+static void
+server_write (GTlsConnection *conn, char *buf, int bufsize)
+{
+  int total, nwrote;
+  int next_rehandshake = bufsize / 3;
+  GError *error = NULL;
+  GOutputStream *stream = g_io_stream_get_output_stream (G_IO_STREAM (conn));
+
+  total = 0;
+  while (total < bufsize)
+    {
+      if (total >= next_rehandshake)
+	{
+	  if (!g_tls_connection_handshake (conn, NULL, &error))
+	    {
+	      g_error ("server rehandshake failed at position %d: %s",
+		       total, error->message);
+	    }
+	  next_rehandshake = MIN (bufsize, next_rehandshake + bufsize / 3);
+	}
+
+      nwrote = g_output_stream_write (stream, buf + total,
+				      next_rehandshake - total,
+				      NULL, &error);
+      if (nwrote <= 0)
+	{
+	  g_error ("server write failed at position %d: %s", total,
+		   error->message);
+	}
+      total += nwrote;
+    }
+}
+
+static const char *ssl_cert_file = SRCDIR G_DIR_SEPARATOR_S "test-cert.pem";
+
+static gpointer
+server_thread (gpointer user_data)
+{
+  GSocket *listener = user_data, *socket;
+  GSocketConnection *conn;
+  GTlsServerConnection *tls_server;
+  GTlsConnection *tls_conn;
+  GTlsCertificate *server_cert;
+  char buf[BUFSIZE];
+  GError *error = NULL;
+
+  /* Wait for client thread to connect */
+  g_socket_set_blocking (listener, TRUE);
+  socket = g_socket_accept (listener, NULL, &error);
+  if (!socket)
+    g_error ("Could not accept on listening socket: %s", error->message);
+
+  conn = g_socket_connection_factory_create_connection (socket);
+  g_object_unref (socket);
+
+  server_cert = g_tls_certificate_new_from_file (ssl_cert_file, &error);
+  if (!server_cert)
+    g_error ("Could not read TLS certificate file: %s", error->message);
+
+  tls_server = g_tls_server_connection_new (&error,
+					    "base-io-stream", conn,
+					    "certificate", server_cert,
+					    NULL);
+  if (!tls_server)
+    g_error ("Could not create TLS connection: %s", error->message);
+  g_object_unref (conn);
+  g_object_unref (server_cert);
+  tls_conn = G_TLS_CONNECTION (tls_server);
+
+  /* Initial handshake */
+  if (!g_tls_connection_handshake (tls_conn, NULL, &error))
+    g_error ("initial handshake failed: %s", error->message);
+
+  /* Synchronous client test. */
+  server_read (tls_conn, buf, BUFSIZE);
+  server_write (tls_conn, buf, BUFSIZE);
+
+  /* Async client test. */
+  server_read (tls_conn, buf, BUFSIZE);
+  server_write (tls_conn, buf, BUFSIZE);
+
+  /* That's all, folks. */
+  if (!g_io_stream_close (G_IO_STREAM (tls_conn), NULL, &error))
+    g_error ("TLS close failed: %s", error->message);
+
+  g_object_unref (tls_conn);
+
+  return NULL;
+}
+
+/* async client code */
+
+typedef struct {
+  GSocketConnection *conn;
+  char writebuf[BUFSIZE], readbuf[BUFSIZE];
+  int total;
+} AsyncData;
+
+static void async_read (AsyncData *data);
+static void async_write (AsyncData *data);
+
+static void
+async_read_ready (GObject *istream, GAsyncResult *result, gpointer user_data)
+{
+  AsyncData *data = user_data;
+  gsize n;
+  GError *error = NULL;
+
+  n = g_input_stream_read_finish (G_INPUT_STREAM (istream), result, &error);
+  g_assert_no_error (error);
+  data->total += n;
+
+  if (data->total < BUFSIZE)
+    {
+      async_read (data);
+      return;
+    }
+
+  if (memcmp (data->writebuf, data->readbuf, BUFSIZE) != 0)
+    g_error ("Async read didn't match write");
+
+  g_free (data);
+  g_main_loop_quit (loop);
+}
+
+static void
+async_read (AsyncData *data)
+{
+  GInputStream *istream;
+
+  istream = g_io_stream_get_input_stream (G_IO_STREAM (data->conn));
+  g_input_stream_read_async (istream, data->readbuf + data->total,
+			     BUFSIZE - data->total, G_PRIORITY_DEFAULT, NULL,
+			     async_read_ready, data);
+}
+
+static void
+async_write_ready (GObject *ostream, GAsyncResult *result, gpointer user_data)
+{
+  AsyncData *data = user_data;
+  gsize n;
+  GError *error = NULL;
+
+  n = g_output_stream_write_finish (G_OUTPUT_STREAM (ostream), result, &error);
+  g_assert_no_error (error);
+  data->total += n;
+
+  if (data->total < BUFSIZE)
+    {
+      async_write (data);
+      return;
+    }
+
+  data->total = 0;
+  async_read (data);
+}
+
+static void
+async_write (AsyncData *data)
+{
+  GOutputStream *ostream;
+
+  ostream = g_io_stream_get_output_stream (G_IO_STREAM (data->conn));
+  g_output_stream_write_async (ostream, data->writebuf + data->total,
+			       BUFSIZE - data->total, G_PRIORITY_DEFAULT, NULL,
+			       async_write_ready, data);
+}
+
+static gboolean
+start_writing (gpointer user_data)
+{
+  GSocketConnection *conn = user_data;
+  AsyncData *data;
+  int i;
+
+  data = g_new (AsyncData, 1);
+  data->conn = conn;
+  for (i = 0; i < BUFSIZE; i++)
+    data->writebuf[i] = i & 0xFF;
+  data->total = 0;
+
+  async_write (data);
+  return FALSE;
+}
+
+static GSocketConnection *conn;
+static GOutputStream *ostream;
+static GInputStream *istream;
+static GThread *server;
+
+static void
+tls_test_setup (void)
+{
+  int port;
+  GSocket *listener;
+  GInetAddress *iaddr;
+  GSocketAddress *saddr;
+  GSocketClient *client;
+  GError *error = NULL;
+
+  /* Create server socket */
+  listener = g_socket_new (G_SOCKET_FAMILY_IPV4, G_SOCKET_TYPE_STREAM, 0, &error);
+  if (!listener)
+    g_error ("Could not create listening socket: %s\n", error->message);
+
+  iaddr = g_inet_address_new_any (G_SOCKET_FAMILY_IPV4);
+  saddr = g_inet_socket_address_new (iaddr, 0);
+  g_object_unref (iaddr);
+
+  if (!g_socket_bind (listener, saddr, TRUE, &error))
+    g_error ("Could not bind listening socket: %s\n", error->message);
+  g_object_unref (saddr);
+
+  saddr = g_socket_get_local_address (listener, NULL);
+  port = g_inet_socket_address_get_port (G_INET_SOCKET_ADDRESS (saddr));
+  g_object_unref (saddr);
+
+  if (!g_socket_listen (listener, &error))
+    g_error ("Could not listen on socket: %s\n", error->message);
+
+  /* Now spawn server thread */
+  server = g_thread_create (server_thread, listener, TRUE, NULL);
+
+  client = g_socket_client_new ();
+  g_socket_client_set_tls (client, TRUE);
+  /* The test program uses a self-signed certificate, so we need to
+   * not validate that.
+   */
+  g_socket_client_set_tls_validation_flags (client, G_TLS_VALIDATE_ALL & ~G_TLS_VALIDATE_CA);
+
+  conn = g_socket_client_connect_to_host (client, "localhost", port,
+					  NULL, &error);
+  g_assert_no_error (error);
+}
+
+static void
+test_tls_sync (void)
+{
+  int i;
+  char writebuf[BUFSIZE], readbuf[BUFSIZE];
+  gsize n, total;
+  GError *error = NULL;
+
+  for (i = 0; i < BUFSIZE; i++)
+    writebuf[i] = i & 0xFF;
+
+  ostream = g_io_stream_get_output_stream (G_IO_STREAM (conn));
+  istream = g_io_stream_get_input_stream (G_IO_STREAM (conn));
+
+  total = 0;
+  while (total < BUFSIZE)
+    {
+      n = g_output_stream_write (ostream, writebuf + total,
+				 BUFSIZE - total, NULL, &error);
+      g_assert_no_error (error);
+      total += n;
+    }
+
+  total = 0;
+  while (total < BUFSIZE)
+    {
+      n = g_input_stream_read (istream, readbuf + total,
+			       BUFSIZE - total, NULL, &error);
+      g_assert_no_error (error);
+      total += n;
+    }
+
+  if (memcmp (writebuf, readbuf, BUFSIZE) != 0)
+    g_error ("Sync read didn't match write");
+}
+
+static void
+test_tls_async (void)
+{
+  g_idle_add (start_writing, conn);
+  loop = g_main_loop_new (NULL, TRUE);
+  g_main_loop_run (loop);
+  g_main_loop_unref (loop);
+  g_main_context_unref (g_main_context_default ());
+
+  g_object_unref (conn);
+  g_thread_join (server);
+}
+
+int
+main (int argc, char **argv)
+{
+  g_type_init ();
+  g_test_init (&argc, &argv, NULL);
+
+  tls_test_setup ();
+
+  g_test_add_func ("/tls/sync", test_tls_sync);
+  g_test_add_func ("/tls/async", test_tls_async);
+
+  return g_test_run();
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]