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 **error)
66 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
69 DBG("channel %p", channel);
71 if (gnutls_channel->established == TRUE)
72 return G_IO_STATUS_NORMAL;
74 err = gnutls_handshake(gnutls_channel->session);
76 return G_IO_STATUS_AGAIN;
78 gnutls_channel->established = TRUE;
80 DBG("handshake done");
82 return G_IO_STATUS_NORMAL;
85 static GIOStatus g_io_gnutls_read(GIOChannel *channel, gchar *buf,
86 gsize count, gsize *bytes_read, GError **err)
88 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
92 DBG("channel %p count %zu", channel, count);
97 status = check_handshake(channel, err);
98 if (status != G_IO_STATUS_NORMAL)
101 result = gnutls_record_recv(gnutls_channel->session, buf, count);
103 DBG("result %zd", result);
105 if (result == GNUTLS_E_REHANDSHAKE) {
106 gnutls_channel->established = FALSE;
110 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
113 if (result == GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
114 return G_IO_STATUS_EOF;
117 g_set_error(err, G_IO_CHANNEL_ERROR,
118 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
119 return G_IO_STATUS_ERROR;
122 *bytes_read = result;
124 return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
127 static GIOStatus g_io_gnutls_write(GIOChannel *channel, const gchar *buf,
128 gsize count, gsize *bytes_written, GError **err)
130 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
134 DBG("channel %p count %zu", channel, count);
139 status = check_handshake(channel, err);
140 if (status != G_IO_STATUS_NORMAL)
143 result = gnutls_record_send(gnutls_channel->session, buf, count);
145 DBG("result %zd", result);
147 if (result == GNUTLS_E_REHANDSHAKE) {
148 gnutls_channel->established = FALSE;
152 if (result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
156 g_set_error(err, G_IO_CHANNEL_ERROR,
157 G_IO_CHANNEL_ERROR_FAILED, "Stream corrupted");
158 return G_IO_STATUS_ERROR;
161 *bytes_written = result;
163 return (result > 0) ? G_IO_STATUS_NORMAL : G_IO_STATUS_EOF;
166 static GIOStatus g_io_gnutls_seek(GIOChannel *channel, gint64 offset,
167 GSeekType type, GError **err)
169 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
170 GIOChannel *transport = gnutls_channel->transport;
172 DBG("channel %p", channel);
174 return transport->funcs->io_seek(transport, offset, type, err);
177 static GIOStatus g_io_gnutls_close(GIOChannel *channel, GError **err)
179 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
180 GIOChannel *transport = gnutls_channel->transport;
182 DBG("channel %p", channel);
184 if (gnutls_channel->established == TRUE)
185 gnutls_bye(gnutls_channel->session, GNUTLS_SHUT_RDWR);
187 return transport->funcs->io_close(transport, err);
190 static void g_io_gnutls_free(GIOChannel *channel)
192 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
194 DBG("channel %p", channel);
196 g_io_channel_unref(gnutls_channel->transport);
198 gnutls_deinit(gnutls_channel->session);
200 gnutls_certificate_free_credentials(gnutls_channel->cred);
202 g_free(gnutls_channel);
205 static GIOStatus g_io_gnutls_set_flags(GIOChannel *channel,
206 GIOFlags flags, GError **err)
208 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
209 GIOChannel *transport = gnutls_channel->transport;
211 DBG("channel %p flags %u", channel, flags);
213 return transport->funcs->io_set_flags(transport, flags, err);
216 static GIOFlags g_io_gnutls_get_flags(GIOChannel *channel)
218 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
219 GIOChannel *transport = gnutls_channel->transport;
221 DBG("channel %p", channel);
223 return transport->funcs->io_get_flags(transport);
226 static gboolean g_io_gnutls_prepare(GSource *source, gint *timeout)
228 DBG("source %p", source);
235 static gboolean g_io_gnutls_check(GSource *source)
237 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
238 GIOCondition condition = watch->pollfd.revents;
240 DBG("source %p condition %u", source, condition);
242 if (condition & watch->condition)
248 static gboolean g_io_gnutls_dispatch(GSource *source, GSourceFunc callback,
251 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
252 GIOFunc func = (GIOFunc) callback;
253 GIOCondition condition = watch->pollfd.revents;
255 DBG("source %p condition %u", source, condition);
260 return func(watch->channel, condition & watch->condition, user_data);
263 static void g_io_gnutls_finalize(GSource *source)
265 GIOGnuTLSWatch *watch = (GIOGnuTLSWatch *) source;
267 DBG("source %p", source);
269 g_io_channel_unref(watch->channel);
272 static GSourceFuncs gnutls_watch_funcs = {
275 g_io_gnutls_dispatch,
276 g_io_gnutls_finalize,
279 static GSource *g_io_gnutls_create_watch(GIOChannel *channel,
280 GIOCondition condition)
282 GIOGnuTLSChannel *gnutls_channel = (GIOGnuTLSChannel *) channel;
283 GIOGnuTLSWatch *watch;
286 DBG("channel %p condition %u", channel, condition);
288 source = g_source_new(&gnutls_watch_funcs, sizeof(GIOGnuTLSWatch));
290 watch = (GIOGnuTLSWatch *) source;
292 watch->channel = channel;
293 g_io_channel_ref(channel);
295 watch->condition = condition;
297 watch->pollfd.fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
298 watch->pollfd.events = condition;
300 g_source_add_poll(source, &watch->pollfd);
305 static GIOFuncs gnutls_channel_funcs = {
310 g_io_gnutls_create_watch,
312 g_io_gnutls_set_flags,
313 g_io_gnutls_get_flags,
316 static ssize_t g_io_gnutls_push_func(gnutls_transport_ptr_t transport_data,
317 const void *buf, size_t count)
319 GIOGnuTLSChannel *gnutls_channel = transport_data;
323 DBG("transport %p count %zu", gnutls_channel->transport, count);
325 fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
327 result = write(fd, buf, count);
329 DBG("result %zd", result);
334 static ssize_t g_io_gnutls_pull_func(gnutls_transport_ptr_t transport_data,
335 void *buf, size_t count)
337 GIOGnuTLSChannel *gnutls_channel = transport_data;
341 DBG("transport %p count %zu", gnutls_channel->transport, count);
343 fd = g_io_channel_unix_get_fd(gnutls_channel->transport);
345 result = read(fd, buf, count);
347 DBG("result %zd", result);
352 GIOChannel *g_io_channel_gnutls_new(int fd)
354 GIOGnuTLSChannel *gnutls_channel;
360 gnutls_channel = g_new(GIOGnuTLSChannel, 1);
362 channel = (GIOChannel *) gnutls_channel;
364 g_io_channel_init(channel);
365 channel->funcs = &gnutls_channel_funcs;
367 gnutls_channel->transport = g_io_channel_unix_new(fd);
369 g_io_channel_set_encoding(gnutls_channel->transport, NULL, NULL);
370 g_io_channel_set_buffered(gnutls_channel->transport, FALSE);
372 channel->is_seekable = FALSE;
373 channel->is_readable = TRUE;
374 channel->is_writeable = TRUE;
376 channel->do_encode = FALSE;
378 g_io_gnutls_global_init();
380 err = gnutls_init(&gnutls_channel->session, GNUTLS_CLIENT);
382 g_free(gnutls_channel);
386 gnutls_transport_set_ptr(gnutls_channel->session, gnutls_channel);
387 gnutls_transport_set_push_function(gnutls_channel->session,
388 g_io_gnutls_push_func);
389 gnutls_transport_set_pull_function(gnutls_channel->session,
390 g_io_gnutls_pull_func);
391 gnutls_transport_set_lowat(gnutls_channel->session, 0);
393 gnutls_priority_set_direct(gnutls_channel->session,
394 "NORMAL:!VERS-TLS1.1:!VERS-TLS1.0", NULL);
396 gnutls_certificate_allocate_credentials(&gnutls_channel->cred);
397 gnutls_credentials_set(gnutls_channel->session,
398 GNUTLS_CRD_CERTIFICATE, gnutls_channel->cred);
400 DBG("channel %p transport %p", channel, gnutls_channel->transport);