3 * Web service library with GLib integration
5 * Copyright (C) 2009-2012 Intel Corporation. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
31 #include <gnutls/gnutls.h>
33 #include "giognutls.h"
35 //#define DBG(fmt, arg...) printf("%s: " fmt "\n" , __func__ , ## arg)
36 #define DBG(fmt, arg...)
38 typedef struct _GIOGnuTLSChannel GIOGnuTLSChannel;
39 typedef struct _GIOGnuTLSWatch GIOGnuTLSWatch;
41 struct _GIOGnuTLSChannel {
44 gnutls_certificate_credentials_t cred;
45 gnutls_session_t session;
50 struct _GIOGnuTLSWatch {
54 GIOCondition condition;
57 static volatile int global_init_done = 0;
59 static inline void g_io_gnutls_global_init(void)
61 if (__sync_bool_compare_and_swap(&global_init_done, 0, 1))
65 static GIOStatus check_handshake(GIOChannel *channel, GError **err)
67 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
70 DBG("channel %p", channel);
72 if (gnutls_channel->established)
73 return G_IO_STATUS_NORMAL;
76 result = gnutls_handshake(gnutls_channel->session);
78 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
79 GIOFlags flags = g_io_channel_get_flags(channel);
81 if (gnutls_channel->again)
82 return G_IO_STATUS_AGAIN;
84 if (flags & G_IO_FLAG_NONBLOCK)
85 return G_IO_STATUS_AGAIN;
91 g_set_error(err, G_IO_CHANNEL_ERROR,
92 G_IO_CHANNEL_ERROR_FAILED, "Handshake failed");
93 return G_IO_STATUS_ERROR;
96 gnutls_channel->established = true;
98 DBG("handshake done");
100 return G_IO_STATUS_NORMAL;
103 static GIOStatus g_io_gnutls_read(GIOChannel *channel, gchar *buf,
104 gsize count, gsize *bytes_read, GError **err)
106 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
110 DBG("channel %p count %zu", channel, count);
115 status = check_handshake(channel, err);
116 if (status != G_IO_STATUS_NORMAL)
119 result = gnutls_record_recv(gnutls_channel->session, buf, count);
121 DBG("result %zd", result);
123 if (result == GNUTLS_E_REHANDSHAKE) {
124 gnutls_channel->established = false;
128 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
129 GIOFlags flags = g_io_channel_get_flags(channel);
131 if (gnutls_channel->again)
132 return G_IO_STATUS_AGAIN;
134 if (flags & G_IO_FLAG_NONBLOCK)
135 return G_IO_STATUS_AGAIN;
140 if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
141 return G_IO_STATUS_EOF;
144 g_set_error(err, G_IO_CHANNEL_ERROR,
145 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
146 return G_IO_STATUS_ERROR;
149 *bytes_read = result;
151 return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
154 static GIOStatus g_io_gnutls_write(GIOChannel *channel, const gchar *buf,
155 gsize count, gsize *bytes_written, GError **err)
157 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
161 DBG("channel %p count %zu", channel, count);
166 status = check_handshake(channel, err);
167 if (status != G_IO_STATUS_NORMAL)
170 result = gnutls_record_send(gnutls_channel->session, buf, count);
172 DBG("result %zd", result);
174 if (result == GNUTLS_E_REHANDSHAKE) {
175 gnutls_channel->established = false;
179 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
180 GIOFlags flags = g_io_channel_get_flags(channel);
182 if (gnutls_channel->again)
183 return G_IO_STATUS_AGAIN;
185 if (flags & G_IO_FLAG_NONBLOCK)
186 return G_IO_STATUS_AGAIN;
192 g_set_error(err, G_IO_CHANNEL_ERROR,
193 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
194 return G_IO_STATUS_ERROR;
197 *bytes_written = result;
199 return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
202 static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset,
203 GSeekType type, GError **err)
205 DBG("channel %p", channel);
207 g_set_error_literal(err, G_IO_CHANNEL_ERROR,
208 G_IO_CHANNEL_ERROR_FAILED, "Not supported");
209 return G_IO_STATUS_ERROR;
212 static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err)
214 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
216 DBG("channel %p", channel);
218 if (gnutls_channel->established)
219 gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR);
221 if (close(gnutls_channel->fd) < 0) {
222 g_set_error_literal(err, G_IO_CHANNEL_ERROR,
223 G_IO_CHANNEL_ERROR_FAILED, "Closing failed");
224 return G_IO_STATUS_ERROR;
227 return G_IO_STATUS_NORMAL;
230 static void g_io_gnutls_free(GIOChannel *channel)
232 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
234 DBG("channel %p", channel);
236 gnutls_deinit(gnutls_channel->session);
238 gnutls_certificate_free_credentials(gnutls_channel->cred);
240 g_free(gnutls_channel);
243 static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel,
244 GIOFlags flags, GError **err)
246 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
247 glong fcntl_flags = 0;
249 DBG("channel %p flags %u", channel, flags);
251 if (flags & G_IO_FLAG_NONBLOCK)
252 fcntl_flags |= O_NONBLOCK;
254 if (fcntl(gnutls_channel->fd, F_SETFL, fcntl_flags) < 0) {
255 g_set_error_literal(err, G_IO_CHANNEL_ERROR,
256 G_IO_CHANNEL_ERROR_FAILED, "Setting flags failed");
257 return G_IO_STATUS_ERROR;
260 return G_IO_STATUS_NORMAL;
263 static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
265 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
269 DBG("channel %p", channel);
271 fcntl_flags = fcntl(gnutls_channel->fd, F_GETFL);
275 if (fcntl_flags & O_NONBLOCK)
276 flags |= G_IO_FLAG_NONBLOCK;
281 static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
283 DBG("source %p", source);
290 static gboolean g_io_gnutls_check(GSource *source)
292 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
293 GIOCondition condition = watch->pollfd.revents;
295 DBG("source %p condition %u", source, condition);
297 if (condition & watch->condition)
303 static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback,
306 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
307 GIOFunc func = (GIOFunc) callback;
308 GIOCondition condition = watch->pollfd.revents;
310 DBG("source %p condition %u", source, condition);
315 return func(watch->channel, condition & watch->condition, user_data);
318 static void g_io_gnutls_finalize(GSource *source)
320 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
322 DBG("source %p", source);
324 g_io_channel_unref(watch->channel);
327 static GSourceFuncs gnutls_watch_funcs = {
330 g_io_gnutls_dispatch,
331 g_io_gnutls_finalize,
334 static GSource *g_io_gnutls_create_watch(GIOChannel *channel,
335 GIOCondition condition)
337 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
338 GIOGnuTLSWatch *watch;
341 DBG("channel %p condition %u", channel, condition);
343 source = g_source_new(&gnutls_watch_funcs, sizeof(GIOGnuTLSWatch));
345 watch = (GIOGnuTLSWatch *) source;
347 watch->channel = channel;
348 g_io_channel_ref(channel);
350 watch->condition = condition;
352 watch->pollfd.fd = gnutls_channel->fd;
353 watch->pollfd.events = condition;
355 g_source_add_poll(source, &watch->pollfd);
360 static GIOFuncs gnutls_channel_funcs = {
365 g_io_gnutls_create_watch,
367 g_io_gnutls_set_flags,
368 g_io_gnutls_get_flags,
371 static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data,
372 const void *buf, size_t count)
374 GIOGnuTLSChannel *gnutls_channel = transport_data;
377 DBG("count %zu", count);
379 result = write(gnutls_channel->fd, buf, count);
381 if (result < 0 && errno == EAGAIN)
382 gnutls_channel->again = true;
384 gnutls_channel->again = false;
386 DBG("result %zd", result);
391 static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
392 void *buf, size_t count)
394 GIOGnuTLSChannel *gnutls_channel = transport_data;
397 DBG("count %zu", count);
399 result = read(gnutls_channel->fd, buf, count);
401 if (result < 0 && errno == EAGAIN)
402 gnutls_channel->again = true;
404 gnutls_channel->again = false;
406 DBG("result %zd", result);
411 bool g_io_channel_supports_tls(void)
416 GIOChannel *g_io_channel_gnutls_new(int fd)
418 GIOGnuTLSChannel *gnutls_channel;
424 gnutls_channel = g_new(GIOGnuTLSChannel, 1);
426 channel = (GIOChannel *) gnutls_channel;
428 g_io_channel_init(channel);
429 channel->funcs = &gnutls_channel_funcs;
431 gnutls_channel->fd = fd;
433 channel->is_seekable = FALSE;
434 channel->is_readable = TRUE;
435 channel->is_writeable = TRUE;
437 channel->do_encode = FALSE;
439 g_io_gnutls_global_init();
441 err = gnutls_init(&gnutls_channel->session, GNUTLS_CLIENT);
443 g_free(gnutls_channel);
447 gnutls_transport_set_ptr(gnutls_channel->session, gnutls_channel);
448 gnutls_transport_set_push_function(gnutls_channel->session,
449 g_io_gnutls_push_func);
450 gnutls_transport_set_pull_function(gnutls_channel->session,
451 g_io_gnutls_pull_func);
452 #if GNUTLS_VERSION_NUMBER < 0x020c00
453 gnutls_transport_set_lowat(gnutls_channel->session, 0);
455 gnutls_priority_set_direct(gnutls_channel->session,
456 "NORMAL:%COMPAT", NULL);
458 gnutls_priority_set_direct(gnutls_channel->session,
459 "NORMAL:-VERS-TLS-ALL:+VERS-TLS1.0:+VERS-SSL3.0:%COMPAT", NULL);
461 gnutls_certificate_allocate_credentials(&gnutls_channel->cred);
462 gnutls_credentials_set(gnutls_channel->session,
463 GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred);
465 DBG("channel %p", channel);