summaryrefslogtreecommitdiff
path: root/gweb/giognutls.c
diff options
context:
space:
mode:
authorMarcel Holtmann <marcel@holtmann.org>2010-10-30 03:21:13 +0200
committerMarcel Holtmann <marcel@holtmann.org>2010-10-30 03:21:13 +0200
commit17dbeffefc57c5c5434f65782350249c2a84e853 (patch)
treec2392b6e44ba8d5041307007799eaae78d6a530d /gweb/giognutls.c
parent82998a7dcabc6434ab38914904ce78f31ceadc6a (diff)
downloadconnman-17dbeffefc57c5c5434f65782350249c2a84e853.tar.gz
connman-17dbeffefc57c5c5434f65782350249c2a84e853.tar.bz2
connman-17dbeffefc57c5c5434f65782350249c2a84e853.zip
Add support for g_io_channel_gnutls_new() function
Diffstat (limited to 'gweb/giognutls.c')
-rw-r--r--gweb/giognutls.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/gweb/giognutls.c b/gweb/giognutls.c
new file mode 100644
index 00000000..3d6e39cf
--- /dev/null
+++ b/gweb/giognutls.c
@@ -0,0 +1,404 @@
+/*
+ *
+ * Web service library with GLib integration
+ *
+ * Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <gnutls/gnutls.h>
+
+#include "giognutls.h"
+
+//#define DBG(fmt, arg...) printf("%s: " fmt "\n" , __func__ , ## arg)
+#define DBG(fmt, arg...)
+
+typedef struct _GIOGnuTLSChannel GIOGnuTLSChannel;
+typedef struct _GIOGnuTLSWatch GIOGnuTLSWatch;
+
+struct _GIOGnuTLSChannel {
+ GIOChannel channel;
+ GIOChannel *transport;
+ gnutls_certificate_credentials_t cred;
+ gnutls_session session;
+ gboolean established;
+};
+
+struct _GIOGnuTLSWatch {
+ GSource source;
+ GPollFD pollfd;
+ GIOChannel *channel;
+ GIOCondition condition;
+};
+
+static volatile gint global_init_done = 0;
+
+static inline void g_io_gnutls_global_init(void)
+{
+ if (g_atomic_int_compare_and_exchange(&global_init_done, 0, 1) == TRUE)
+ gnutls_global_init();
+}
+
+static GIOStatus check_handshake(GIOChannel *channel, GError **error)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+ int err;
+
+ DBG("channel %p", channel);
+
+ if (gnutls_channel->established == TRUE)
+ return G_IO_STATUS_NORMAL;
+
+ err = gnutls_handshake(gnutls_channel->session);
+ if (err < 0)
+ return G_IO_STATUS_AGAIN;
+
+ gnutls_channel->established = TRUE;
+
+ DBG("handshake done");
+
+ return G_IO_STATUS_NORMAL;
+}
+
+static GIOStatus g_io_gnutls_read(GIOChannel *channel, gchar *buf,
+ gsize count, gsize *bytes_read, GError **err)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+ GIOStatus status;
+ ssize_t result;
+
+ DBG("channel %p count %zu", channel, count);
+
+ *bytes_read = 0;
+
+again:
+ status = check_handshake(channel, err);
+ if (status != G_IO_STATUS_NORMAL)
+ return status;
+
+ result = gnutls_record_recv(gnutls_channel->session, buf, count);
+
+ DBG("result %zd", result);
+
+ if (result == GNUTLS_E_REHANDSHAKE) {
+ gnutls_channel->established = FALSE;
+ goto again;
+ }
+
+ if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
+ goto again;
+
+ if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
+ return G_IO_STATUS_EOF;
+
+ if (result < 0) {
+ g_set_error(err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
+ return G_IO_STATUS_ERROR;
+ }
+
+ *bytes_read = result;
+
+ return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+}
+
+static GIOStatus g_io_gnutls_write(GIOChannel *channel, const gchar *buf,
+ gsize count, gsize *bytes_written, GError **err)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+ GIOStatus status;
+ ssize_t result;
+
+ DBG("channel %p count %zu", channel, count);
+
+ *bytes_written = 0;
+
+again:
+ status = check_handshake(channel, err);
+ if (status != G_IO_STATUS_NORMAL)
+ return status;
+
+ result = gnutls_record_send(gnutls_channel->session, buf, count);
+
+ DBG("result %zd", result);
+
+ if (result == GNUTLS_E_REHANDSHAKE) {
+ gnutls_channel->established = FALSE;
+ goto again;
+ }
+
+ if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
+ goto again;
+
+ if (result < 0) {
+ g_set_error(err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
+ return G_IO_STATUS_ERROR;
+ }
+
+ *bytes_written = result;
+
+ return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
+}
+
+static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset,
+ GSeekType type, GError **err)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+ GIOChannel *transport = gnutls_channel->transport;
+
+ DBG("channel %p", channel);
+
+ return transport->funcs->io_seek(transport, offset, type, err);
+}
+
+static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+ GIOChannel *transport = gnutls_channel->transport;
+
+ DBG("channel %p", channel);
+
+ if (gnutls_channel->established == TRUE)
+ gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR);
+
+ return transport->funcs->io_close(transport, err);
+}
+
+static void g_io_gnutls_free(GIOChannel *channel)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+
+ DBG("channel %p", channel);
+
+ g_io_channel_unref(gnutls_channel->transport);
+
+ gnutls_deinit(gnutls_channel->session);
+
+ gnutls_certificate_free_credentials(gnutls_channel->cred);
+
+ g_free(gnutls_channel);
+}
+
+static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel,
+ GIOFlags flags, GError **err)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+ GIOChannel *transport = gnutls_channel->transport;
+
+ DBG("channel %p flags %u", channel, flags);
+
+ return transport->funcs->io_set_flags(transport, flags, err);
+}
+
+static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+ GIOChannel *transport = gnutls_channel->transport;
+
+ DBG("channel %p", channel);
+
+ return transport->funcs->io_get_flags(transport);
+}
+
+static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
+{
+ DBG("source %p", source);
+
+ *timeout = -1;
+
+ return FALSE;
+}
+
+static gboolean g_io_gnutls_check(GSource *source)
+{
+ GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
+ GIOCondition condition = watch->pollfd.revents;
+
+ DBG("source %p condition %u", source, condition);
+
+ if (condition & watch->condition)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback,
+ gpointer user_data)
+{
+ GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
+ GIOFunc func = (GIOFunc) callback;
+ GIOCondition condition = watch->pollfd.revents;
+
+ DBG("source %p condition %u", source, condition);
+
+ if (func == NULL)
+ return FALSE;
+
+ return func(watch->channel, condition & watch->condition, user_data);
+}
+
+static void g_io_gnutls_finalize(GSource *source)
+{
+ GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
+
+ DBG("source %p", source);
+
+ g_io_channel_unref(watch->channel);
+}
+
+static GSourceFuncs gnutls_watch_funcs = {
+ g_io_gnutls_prepare,
+ g_io_gnutls_check,
+ g_io_gnutls_dispatch,
+ g_io_gnutls_finalize,
+};
+
+static GSource *g_io_gnutls_create_watch(GIOChannel *channel,
+ GIOCondition condition)
+{
+ GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
+ GIOGnuTLSWatch *watch;
+ GSource *source;
+
+ DBG("channel %p condition %u", channel, condition);
+
+ source = g_source_new(&gnutls_watch_funcs, sizeof(GIOGnuTLSWatch));
+
+ watch = (GIOGnuTLSWatch *) source;
+
+ watch->channel = channel;
+ g_io_channel_ref(channel);
+
+ watch->condition = condition;
+
+ watch->pollfd.fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+ watch->pollfd.events = condition;
+
+ g_source_add_poll(source, &watch->pollfd);
+
+ return source;
+}
+
+static GIOFuncs gnutls_channel_funcs = {
+ g_io_gnutls_read,
+ g_io_gnutls_write,
+ g_io_gnutls_seek,
+ g_io_gnutls_close,
+ g_io_gnutls_create_watch,
+ g_io_gnutls_free,
+ g_io_gnutls_set_flags,
+ g_io_gnutls_get_flags,
+};
+
+static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data,
+ const void *buf, size_t count)
+{
+ GIOGnuTLSChannel *gnutls_channel = transport_data;
+ ssize_t result;
+ int fd;
+
+ DBG("transport %p count %zu", gnutls_channel->transport, count);
+
+ fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+
+ result = write(fd, buf, count);
+
+ DBG("result %zd", result);
+
+ return result;
+}
+
+static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
+ void *buf, size_t count)
+{
+ GIOGnuTLSChannel *gnutls_channel = transport_data;
+ ssize_t result;
+ int fd;
+
+ DBG("transport %p count %zu", gnutls_channel->transport, count);
+
+ fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+
+ result = read(fd, buf, count);
+
+ DBG("result %zd", result);
+
+ return result;
+}
+
+GIOChannel *g_io_channel_gnutls_new(int fd)
+{
+ GIOGnuTLSChannel *gnutls_channel;
+ GIOChannel *channel;
+ int err;
+
+ DBG("");
+
+ gnutls_channel = g_new(GIOGnuTLSChannel, 1);
+
+ channel = (GIOChannel *) gnutls_channel;
+
+ g_io_channel_init(channel);
+ channel->funcs = &gnutls_channel_funcs;
+
+ gnutls_channel->transport = g_io_channel_unix_new(fd);
+
+ g_io_channel_set_encoding(gnutls_channel->transport, NULL, NULL);
+ g_io_channel_set_buffered(gnutls_channel->transport, FALSE);
+
+ channel->is_seekable = FALSE;
+ channel->is_readable = TRUE;
+ channel->is_writeable = TRUE;
+
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ g_io_gnutls_global_init();
+
+ err = gnutls_init(&gnutls_channel->session, GNUTLS_CLIENT);
+ if (err < 0) {
+ g_free(gnutls_channel);
+ return NULL;
+ }
+
+ gnutls_transport_set_ptr(gnutls_channel->session, gnutls_channel);
+ gnutls_transport_set_push_function(gnutls_channel->session,
+ g_io_gnutls_push_func);
+ gnutls_transport_set_pull_function(gnutls_channel->session,
+ g_io_gnutls_pull_func);
+ gnutls_transport_set_lowat(gnutls_channel->session, 0);
+
+ gnutls_priority_set_direct(gnutls_channel->session,
+ "NORMAL:!VERS-TLS1.1:!VERS-TLS1.0", NULL);
+
+ gnutls_certificate_allocate_credentials(&gnutls_channel->cred);
+ gnutls_credentials_set(gnutls_channel->session,
+ GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred);
+
+ DBG("channel %p transport %p", channel, gnutls_channel->transport);
+
+ return channel;
+}