3 * Web service library with GLib integration
5 * Copyright (C) 2009-2010 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 {
43 GIOChannel *transport;
44 gnutls_certificate_credentials_t cred;
45 gnutls_session session;
49 struct _GIOGnuTLSWatch {
53 GIOCondition condition;
56 static volatile gint global_init_done = 0;
58 static inline void g_io_gnutls_global_init(void)
60 if (g_atomic_int_compare_and_exchange(&global_init_done, 0, 1) == TRUE)
64 static GIOStatus check_handshake(GIOChannel *channel, GError **err)
66 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
69 DBG("channel %p", channel);
72 if (gnutls_channel->established == TRUE)
73 return G_IO_STATUS_NORMAL;
75 result = gnutls_handshake(gnutls_channel->session);
77 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
78 GIOFlags flags = g_io_channel_get_flags(channel);
80 if (flags & G_IO_FLAG_NONBLOCK)
81 return G_IO_STATUS_AGAIN;
87 g_set_error(err, G_IO_CHANNEL_ERROR,
88 G_IO_CHANNEL_ERROR_FAILED, "Handshake failed");
89 return G_IO_STATUS_ERROR;
92 gnutls_channel->established = TRUE;
94 DBG("handshake done");
96 return G_IO_STATUS_NORMAL;
99 static GIOStatus g_io_gnutls_read(GIOChannel *channel, gchar *buf,
100 gsize count, gsize *bytes_read, GError **err)
102 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
106 DBG("channel %p count %zu", channel, count);
111 status = check_handshake(channel, err);
112 if (status != G_IO_STATUS_NORMAL)
115 result = gnutls_record_recv(gnutls_channel->session, buf, count);
117 DBG("result %zd", result);
119 if (result == GNUTLS_E_REHANDSHAKE) {
120 gnutls_channel->established = FALSE;
124 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
125 GIOFlags flags = g_io_channel_get_flags(channel);
127 if (flags & G_IO_FLAG_NONBLOCK)
128 return G_IO_STATUS_AGAIN;
133 if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
134 return G_IO_STATUS_EOF;
137 g_set_error(err, G_IO_CHANNEL_ERROR,
138 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
139 return G_IO_STATUS_ERROR;
142 *bytes_read = result;
144 return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
147 static GIOStatus g_io_gnutls_write(GIOChannel *channel, const gchar *buf,
148 gsize count, gsize *bytes_written, GError **err)
150 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
154 DBG("channel %p count %zu", channel, count);
159 status = check_handshake(channel, err);
160 if (status != G_IO_STATUS_NORMAL)
163 result = gnutls_record_send(gnutls_channel->session, buf, count);
165 DBG("result %zd", result);
167 if (result == GNUTLS_E_REHANDSHAKE) {
168 gnutls_channel->established = FALSE;
172 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN) {
173 GIOFlags flags = g_io_channel_get_flags(channel);
175 if (flags & G_IO_FLAG_NONBLOCK)
176 return G_IO_STATUS_AGAIN;
182 g_set_error(err, G_IO_CHANNEL_ERROR,
183 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
184 return G_IO_STATUS_ERROR;
187 *bytes_written = result;
189 return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
192 static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset,
193 GSeekType type, GError **err)
195 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
196 GIOChannel *transport = gnutls_channel->transport;
198 DBG("channel %p", channel);
200 return transport->funcs->io_seek(transport, offset, type, err);
203 static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err)
205 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
206 GIOChannel *transport = gnutls_channel->transport;
208 DBG("channel %p", channel);
210 if (gnutls_channel->established == TRUE)
211 gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR);
213 return transport->funcs->io_close(transport, err);
216 static void g_io_gnutls_free(GIOChannel *channel)
218 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
220 DBG("channel %p", channel);
222 g_io_channel_unref(gnutls_channel->transport);
224 gnutls_deinit(gnutls_channel->session);
226 gnutls_certificate_free_credentials(gnutls_channel->cred);
228 g_free(gnutls_channel);
231 static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel,
232 GIOFlags flags, GError **err)
234 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
235 GIOChannel *transport = gnutls_channel->transport;
237 DBG("channel %p flags %u", channel, flags);
239 return transport->funcs->io_set_flags(transport, flags, err);
242 static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
244 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
245 GIOChannel *transport = gnutls_channel->transport;
247 DBG("channel %p", channel);
249 return transport->funcs->io_get_flags(transport);
252 static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
254 DBG("source %p", source);
261 static gboolean g_io_gnutls_check(GSource *source)
263 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
264 GIOCondition condition = watch->pollfd.revents;
266 DBG("source %p condition %u", source, condition);
268 if (condition & watch->condition)
274 static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback,
277 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
278 GIOFunc func = (GIOFunc) callback;
279 GIOCondition condition = watch->pollfd.revents;
281 DBG("source %p condition %u", source, condition);
286 return func(watch->channel, condition & watch->condition, user_data);
289 static void g_io_gnutls_finalize(GSource *source)
291 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
293 DBG("source %p", source);
295 g_io_channel_unref(watch->channel);
298 static GSourceFuncs gnutls_watch_funcs = {
301 g_io_gnutls_dispatch,
302 g_io_gnutls_finalize,
305 static GSource *g_io_gnutls_create_watch(GIOChannel *channel,
306 GIOCondition condition)
308 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
309 GIOGnuTLSWatch *watch;
312 DBG("channel %p condition %u", channel, condition);
314 source = g_source_new(&gnutls_watch_funcs, sizeof(GIOGnuTLSWatch));
316 watch = (GIOGnuTLSWatch *) source;
318 watch->channel = channel;
319 g_io_channel_ref(channel);
321 watch->condition = condition;
323 watch->pollfd.fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
324 watch->pollfd.events = condition;
326 g_source_add_poll(source, &watch->pollfd);
331 static GIOFuncs gnutls_channel_funcs = {
336 g_io_gnutls_create_watch,
338 g_io_gnutls_set_flags,
339 g_io_gnutls_get_flags,
342 static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data,
343 const void *buf, size_t count)
345 GIOGnuTLSChannel *gnutls_channel = transport_data;
349 DBG("transport %p count %zu", gnutls_channel->transport, count);
351 fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
353 result = write(fd, buf, count);
355 DBG("result %zd", result);
360 static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
361 void *buf, size_t count)
363 GIOGnuTLSChannel *gnutls_channel = transport_data;
367 DBG("transport %p count %zu", gnutls_channel->transport, count);
369 fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
371 result = read(fd, buf, count);
373 DBG("result %zd", result);
378 GIOChannel *g_io_channel_gnutls_new(int fd)
380 GIOGnuTLSChannel *gnutls_channel;
386 gnutls_channel = g_new(GIOGnuTLSChannel, 1);
388 channel = (GIOChannel *) gnutls_channel;
390 g_io_channel_init(channel);
391 channel->funcs = &gnutls_channel_funcs;
393 gnutls_channel->transport = g_io_channel_unix_new(fd);
395 g_io_channel_set_encoding(gnutls_channel->transport, NULL, NULL);
396 g_io_channel_set_buffered(gnutls_channel->transport, FALSE);
398 channel->is_seekable = FALSE;
399 channel->is_readable = TRUE;
400 channel->is_writeable = TRUE;
402 channel->do_encode = FALSE;
404 g_io_gnutls_global_init();
406 err = gnutls_init(&gnutls_channel->session, GNUTLS_CLIENT);
408 g_free(gnutls_channel);
412 gnutls_transport_set_ptr(gnutls_channel->session, gnutls_channel);
413 gnutls_transport_set_push_function(gnutls_channel->session,
414 g_io_gnutls_push_func);
415 gnutls_transport_set_pull_function(gnutls_channel->session,
416 g_io_gnutls_pull_func);
417 gnutls_transport_set_lowat(gnutls_channel->session, 0);
419 gnutls_priority_set_direct(gnutls_channel->session,
420 "NORMAL:!VERS-TLS1.1:!VERS-TLS1.0", NULL);
422 gnutls_certificate_allocate_credentials(&gnutls_channel->cred);
423 gnutls_credentials_set(gnutls_channel->session,
424 GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred);
426 DBG("channel %p transport %p", channel, gnutls_channel->transport);