Added support for SSL when connecting to server.
[cascardo/rnetproxy.git] / ssl.c
1 /*
2 ** Copyright (C) 2006 Thadeu Lima de Souza Cascardo <cascardo@minaslivre.org>
3 ** Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
4 **  
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **  
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ** GNU General Public License for more details.
14 **  
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 **  
19 */
20
21 #include <gnutls/gnutls.h>
22 #include <gnet.h>
23 #include <glib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include "ssl.h"
27
28 #define container_of(ptr, type, member) ({                      \
29         const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
30         (type *)( (char *)__mptr - offsetof(type,member) );})
31
32 struct ssl_data
33 {
34   char *server;
35   gnutls_session_t session;
36   GString *buffer;
37   gboolean handshaking;
38 };
39
40 static struct ssl_data *
41 ssl_data_new (char *server)
42 {
43   struct ssl_data *ssl;
44   int kx_prio[] = {GNUTLS_KX_RSA, 0};
45   gnutls_certificate_credentials cred;
46   gnutls_certificate_allocate_credentials (&cred);
47   ssl = g_slice_new (struct ssl_data);
48   ssl->server = g_strdup (server);
49   gnutls_init (&ssl->session, GNUTLS_CLIENT);
50   gnutls_set_default_priority (ssl->session);
51   gnutls_set_default_priority (ssl->session);
52   gnutls_kx_set_priority (ssl->session, kx_prio);
53   gnutls_credentials_set (ssl->session, GNUTLS_CRD_CERTIFICATE, cred);
54   ssl->buffer = g_string_sized_new (4096);
55   ssl->handshaking = FALSE;
56   return ssl;
57 }
58
59 static void
60 ssl_data_destroy (struct ssl_data *ssl)
61 {
62   gnutls_deinit (ssl->session);
63   g_free (ssl->server);
64   g_string_free (ssl->buffer, TRUE);
65   g_slice_free (struct ssl_data, ssl);
66 }
67
68 static ssize_t
69 ssl_push (gnutls_transport_ptr_t ptr, const void *buffer, size_t len)
70 {
71   net_hook_t *hook = ptr;
72   struct ssl_data *ssl = hook->data;
73   int r;
74   if (ssl->handshaking == TRUE)
75     {
76       g_io_channel_write_chars (hook->conn->iochannel, buffer, len,
77                                 &r, NULL);
78       return r;
79     }
80   gnet_conn_write (hook->conn, (void *) buffer, len);
81   return len;
82 }
83
84 static ssize_t
85 ssl_pull (gnutls_transport_ptr_t ptr, void *buffer, size_t len)
86 {
87   net_hook_t *hook = ptr;
88   struct ssl_data *ssl = hook->data;
89   int r;
90   if (ssl->handshaking == TRUE)
91     {
92       g_io_channel_read_chars (hook->conn->iochannel, buffer, len,
93                                &r, NULL);
94       return r;
95     }
96   if (len > ssl->buffer->len)
97     {
98       r = ssl->buffer->len;
99       memcpy (buffer, ssl->buffer->str, r);
100       g_string_truncate (ssl->buffer, 0);
101     }
102   else
103     {
104       r = len;
105       memcpy (buffer, ssl->buffer->str, r);
106       g_string_erase (ssl->buffer, 0, r);
107     }
108   if (r == 0)
109     {
110       gnutls_transport_set_errno (ssl->session, EAGAIN);
111       return -1;
112     }
113   return r;
114 }
115
116 static void
117 ssl_server_connect (net_hook_t *hook)
118 {
119   struct ssl_data *ssl = hook->data;
120   int error;
121   gnutls_transport_set_ptr (ssl->session, (gnutls_transport_ptr_t) hook);
122   gnutls_transport_set_push_function (ssl->session, ssl_push);
123   gnutls_transport_set_pull_function (ssl->session, ssl_pull);
124   ssl->handshaking = TRUE;
125   if ((error = gnutls_handshake (ssl->session)) < 0)
126     {
127       g_message ("%satal error while doing TLS handshaking.\n",
128                  gnutls_error_is_fatal (error) ? "F" : "Nonf");
129       g_message ("%s\n", gnutls_strerror (error));
130     }
131   ssl->handshaking = FALSE;
132 }
133
134 static void
135 ssl_server_close (net_hook_t *hook)
136 {
137   struct ssl_data *ssl = hook->data;
138   if (hook->peer)
139     {
140       hook->peer->peer = NULL;
141       gnet_conn_disconnect (hook->peer->conn);
142     }
143   gnet_conn_delete (hook->conn);
144   if (ssl != NULL)
145     {
146       gnutls_bye (ssl->session, GNUTLS_SHUT_RDWR);
147       ssl_data_destroy (ssl);
148     }
149   g_slice_free (net_hook_t, hook);
150 }
151
152 static void
153 ssl_server_write (net_hook_t *hook)
154 {
155 }
156
157 static void
158 ssl_server_read (net_hook_t *hook, gchar *buffer, size_t len)
159 {
160   struct ssl_data *ssl = hook->data;
161   int r;
162   g_string_append_len (ssl->buffer, buffer, len);
163   do
164     {
165       r = gnutls_record_recv (ssl->session, buffer, len);
166       if (r > 0)
167         gnet_conn_write (hook->peer->conn, buffer, r);
168     } while (r > 0);
169 }
170
171 static void
172 ssl_server_error (net_hook_t *hook)
173 {
174   g_message ("Error in POP3 client connection.");
175 }
176
177 static net_hook_t *
178 ssl_server_hook_new (net_hook_t *client_hook, char *server)
179 {
180   net_hook_t *hook;
181   hook = g_slice_new (net_hook_t);
182   hook->conn = gnet_conn_new (server, 995, nethook_event, hook);
183   hook->peer = client_hook;
184   hook->server = TRUE;
185   hook->connect = ssl_server_connect;
186   hook->close = ssl_server_close;
187   hook->write = ssl_server_write;
188   hook->read = ssl_server_read;
189   hook->data = ssl_data_new (server);
190   gnet_conn_connect (hook->conn);
191   gnet_conn_read (hook->conn);
192   return hook;
193 }
194
195 static void
196 ssl_connect (net_hook_t *hook)
197 {
198 }
199
200 static void
201 ssl_close (net_hook_t *hook)
202 {
203 }
204
205 static void
206 ssl_write (net_hook_t *hook)
207 {
208 }
209
210 static void
211 ssl_read (net_hook_t *hook, gchar *buffer, size_t len)
212 {
213   struct ssl_data *ssl = hook->peer->data;
214   gnutls_record_send (ssl->session, buffer, len);
215 }
216
217 static void
218 ssl_error (net_hook_t *hook)
219 {
220 }
221
222 net_hook_t *
223 ssl_hook_new (GConn *conn, char *server)
224 {
225   net_hook_t *hook;
226   hook = g_slice_new (net_hook_t);
227   hook->conn = conn;
228   hook->peer = NULL;
229   hook->server = FALSE;
230   hook->connect = ssl_connect;
231   hook->close = ssl_close;
232   hook->write = ssl_write;
233   hook->read = ssl_read;
234   hook->data = NULL;
235   hook->peer = ssl_server_hook_new (hook, server);
236   gnet_conn_set_callback (hook->conn, nethook_event, hook);
237   return hook;
238 }
239
240 void
241 ssl_destroy (net_hook_t *hook)
242 {
243   g_slice_free (net_hook_t, hook);
244 }