*
* Web service library with GLib integration
*
- * Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
+ * Copyright (C) 2009-2012 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
#include <stdio.h>
#include <errno.h>
-#include <ctype.h>
+#include <fcntl.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
struct _GIOGnuTLSChannel {
GIOChannel channel;
- GIOChannel *transport;
+ gint fd;
gnutls_certificate_credentials_t cred;
- gnutls_session session;
+ gnutls_session_t session;
gboolean established;
+ gboolean again;
};
struct _GIOGnuTLSWatch {
GIOCondition condition;
};
-static volatile gint global_init_done = 0;
+static volatile int 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)
+ if (__sync_bool_compare_and_swap(&global_init_done, 0, 1) == TRUE)
gnutls_global_init();
}
-static GIOStatus check_handshake(GIOChannel *channel, GError **error)
+static GIOStatus check_handshake(GIOChannel *channel, GError **err)
{
GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
- int err;
+ int result;
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;
+again:
+ result = gnutls_handshake(gnutls_channel->session);
+
+ if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+ GIOFlags flags = g_io_channel_get_flags(channel);
+
+ if (gnutls_channel->again == TRUE)
+ return G_IO_STATUS_AGAIN;
+
+ if (flags & G_IO_FLAG_NONBLOCK)
+ return G_IO_STATUS_AGAIN;
+
+ goto again;
+ }
+
+ if (result < 0) {
+ g_set_error(err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED, "Handshake failed");
+ return G_IO_STATUS_ERROR;
+ }
gnutls_channel->established = TRUE;
goto again;
}
- if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
+ if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+ GIOFlags flags = g_io_channel_get_flags(channel);
+
+ if (gnutls_channel->again == TRUE)
+ return G_IO_STATUS_AGAIN;
+
+ if (flags & G_IO_FLAG_NONBLOCK)
+ return G_IO_STATUS_AGAIN;
+
goto again;
+ }
if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
return G_IO_STATUS_EOF;
goto again;
}
- if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
+ if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
+ GIOFlags flags = g_io_channel_get_flags(channel);
+
+ if (gnutls_channel->again == TRUE)
+ return G_IO_STATUS_AGAIN;
+
+ if (flags & G_IO_FLAG_NONBLOCK)
+ return G_IO_STATUS_AGAIN;
+
goto again;
+ }
if (result < 0) {
g_set_error(err, G_IO_CHANNEL_ERROR,
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);
+ g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED, "Not supported");
+ return G_IO_STATUS_ERROR;
}
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);
+ if (close(gnutls_channel->fd) < 0) {
+ g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED, "Closing failed");
+ return G_IO_STATUS_ERROR;
+ }
+
+ return G_IO_STATUS_NORMAL;
}
static void g_io_gnutls_free(GIOChannel *channel)
DBG("channel %p", channel);
- g_io_channel_unref(gnutls_channel->transport);
-
gnutls_deinit(gnutls_channel->session);
gnutls_certificate_free_credentials(gnutls_channel->cred);
GIOFlags flags, GError **err)
{
GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
- GIOChannel *transport = gnutls_channel->transport;
+ glong fcntl_flags = 0;
DBG("channel %p flags %u", channel, flags);
- return transport->funcs->io_set_flags(transport, flags, err);
+ if (flags & G_IO_FLAG_NONBLOCK)
+ fcntl_flags |= O_NONBLOCK;
+
+ if (fcntl(gnutls_channel->fd, F_SETFL, fcntl_flags) < 0) {
+ g_set_error_literal(err, G_IO_CHANNEL_ERROR,
+ G_IO_CHANNEL_ERROR_FAILED, "Setting flags failed");
+ return G_IO_STATUS_ERROR;
+ }
+
+ return G_IO_STATUS_NORMAL;
}
static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
{
GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
- GIOChannel *transport = gnutls_channel->transport;
+ GIOFlags flags = 0;
+ glong fcntl_flags;
DBG("channel %p", channel);
- return transport->funcs->io_get_flags(transport);
+ fcntl_flags = fcntl(gnutls_channel->fd, F_GETFL);
+ if (fcntl_flags < 0)
+ return 0;
+
+ if (fcntl_flags & O_NONBLOCK)
+ flags |= G_IO_FLAG_NONBLOCK;
+
+ return flags;
}
static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
watch->condition = condition;
- watch->pollfd.fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+ watch->pollfd.fd = gnutls_channel->fd;
watch->pollfd.events = condition;
g_source_add_poll(source, &watch->pollfd);
{
GIOGnuTLSChannel *gnutls_channel = transport_data;
ssize_t result;
- int fd;
- DBG("transport %p count %zu", gnutls_channel->transport, count);
+ DBG("count %zu", count);
- fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+ result = write(gnutls_channel->fd, buf, count);
- result = write(fd, buf, count);
+ if (result < 0 && errno == EAGAIN)
+ gnutls_channel->again = TRUE;
+ else
+ gnutls_channel->again = FALSE;
DBG("result %zd", result);
{
GIOGnuTLSChannel *gnutls_channel = transport_data;
ssize_t result;
- int fd;
- DBG("transport %p count %zu", gnutls_channel->transport, count);
+ DBG("count %zu", count);
- fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
+ result = read(gnutls_channel->fd, buf, count);
- result = read(fd, buf, count);
+ if (result < 0 && errno == EAGAIN)
+ gnutls_channel->again = TRUE;
+ else
+ gnutls_channel->again = FALSE;
DBG("result %zd", result);
return result;
}
+gboolean g_io_channel_supports_tls(void)
+{
+ return TRUE;
+}
+
GIOChannel *g_io_channel_gnutls_new(int fd)
{
GIOGnuTLSChannel *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);
+ gnutls_channel->fd = fd;
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);
+ channel->do_encode = FALSE;
g_io_gnutls_global_init();
g_io_gnutls_push_func);
gnutls_transport_set_pull_function(gnutls_channel->session,
g_io_gnutls_pull_func);
+#if GNUTLS_VERSION_NUMBER < 0x020c00
gnutls_transport_set_lowat(gnutls_channel->session, 0);
gnutls_priority_set_direct(gnutls_channel->session,
- "NORMAL:!VERS-TLS1.1:!VERS-TLS1.0", NULL);
+ "NORMAL:%COMPAT", NULL);
+#else
+ gnutls_priority_set_direct(gnutls_channel->session,
+ "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL);
+#endif
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);
+ DBG("channel %p", channel);
return channel;
}