5a767f52f0f2fc1c9240180c022fff421f38f329
[oweals/tinc.git] / tnl / tnl.c
1 /*
2     tnl.c -- tunnels
3
4     Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
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.
15
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., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id$
21 */
22
23 #include "system.h"
24
25 #include <gnutls/gnutls.h>
26
27 #include "support/avl.h"
28 #include "support/sockaddr.h"
29 #include "support/xalloc.h"
30 #include "tnl/tnl.h"
31
32 static avl_tree_t *tnls, *listeners;
33
34 bool tnl_init(void) {
35         tnls = avl_tree_new(NULL, (avl_action_t)free);
36         listeners = avl_tree_new(NULL, (avl_action_t)free);
37
38         return true;
39 }
40
41 bool tnl_exit(void) {
42         avl_tree_del(listeners);
43         avl_tree_del(tnls);
44
45         return true;
46 }
47
48 #define tnl_add(t) avl_add(tnls, t)
49 #define tnl_del(t) avl_del(tnls, t)
50 #define tnl_listen_add(l) avl_add(listeners, l)
51 #define tnl_listen_del(l) avl_del(listeners, l)
52
53 static bool tnl_send(tnl_t *tnl, const char *buf, int len) {
54         int result;
55
56         while(len) {
57                 result = gnutls_record_send(tnl->session, buf, len);
58                 if(result <= 0) {
59                         if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
60                                 continue;
61
62                         if(result)
63                                 logger(LOG_ERR, _("tnl: error while sending: %s"), gnutls_strerror(result));
64                         else
65                                 logger(LOG_INFO, _("tnl: connection closed by peer"));
66
67                         tnl->error(tnl, result);
68                         tnl->close(tnl);
69                         return !result;
70                 }
71
72                 buf += result;
73                 len -= result;
74         }
75
76         return true;
77 }
78
79 static bool tnl_recv(tnl_t *tnl) {
80         int result;
81         tnl_record_t *record = (tnl_record_t *)tnl->buf;
82
83         result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof tnl->buf - tnl->bufread);
84         if(result <= 0) {
85                 if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
86                         return true;
87
88                 if(result)
89                         logger(LOG_ERR, _("tnl: error while receiving: %s"), gnutls_strerror(result));
90                 else
91                         logger(LOG_INFO, _("tnl: connection closed by peer"));
92
93                 tnl->error(tnl, result);
94                 tnl->close(tnl);
95                 return !result;
96         }
97
98         tnl->bufread += result;
99
100         while(tnl->bufread >= sizeof *record && tnl->bufread - sizeof *record >= record->len) {
101                 switch(record->type) {
102                         case TNL_RECORD_META:
103                                 tnl->recv_meta(tnl, record->data, record->len);
104                                 break;
105
106                         case TNL_RECORD_PACKET:
107                                 tnl->recv_packet(tnl, record->data, record->len);
108                                 break;
109                                 
110                         default:
111                                 logger(LOG_ERR, _("tnl: error while receiving: %s"), _("unknown record type"));
112                                 tnl->error(tnl, EINVAL);
113                                 tnl->close(tnl);
114                                 return false;
115                 }
116
117                 tnl->bufread -= sizeof *record + record->len;
118                 memmove(tnl->buf, record->data + record->len, tnl->bufread);
119         }
120 }
121
122 static bool tnl_recv_handler(fd_t *fd) {
123         tnl_t *tnl = fd->data;
124         int result;
125
126         result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof(tnl->buf) - tnl->bufread);
127         if(result < 0) {
128                 if(gnutls_error_is_fatal(result)) {
129                         logger(LOG_DEBUG, _("tnl: reception failed: %s\n"), gnutls_strerror(result));
130                         tnl->error(tnl, result);
131                         tnl->close(tnl);
132                         return false;
133                 }
134
135                 return true;
136         }
137
138         tnl->bufread += result;
139         return tnl_recv(tnl);
140 }
141
142 static bool tnl_handshake_handler(fd_t *fd) {
143         tnl_t *tnl = fd->data;
144         int result;
145
146         result = gnutls_handshake(tnl->session);
147         if(result < 0) {
148                 if(gnutls_error_is_fatal(result)) {
149                         logger(LOG_ERR, "tnl: handshake error: %s\n", gnutls_strerror(result));
150                         tnl->close(tnl);
151                         return false;
152                 }
153
154                 /* check other stuff? */
155                 return true;
156         }
157         
158         logger(LOG_DEBUG, _("tnl: handshake finished"));
159
160         result = gnutls_certificate_verify_peers(tnl->session);
161         if(result < 0) {
162                 logger(LOG_ERR, "tnl: certificate error: %s\n", gnutls_strerror(result));
163                 tnl->close(tnl);
164                 return false;
165         }
166
167         if(result) {
168                 logger(LOG_ERR, "tnl: certificate not good, verification result %x", result);
169                 tnl->close(tnl);
170                 return false;
171         }
172
173         tnl->status == TNL_STATUS_UP;
174         tnl->fd.handler = tnl_recv_handler;
175         tnl->accept(tnl);
176         return true;
177 }
178
179 static bool tnl_send_meta(tnl_t *tnl, const char *buf, int len) {
180         tnl_record_t record = {
181                 .type = TNL_RECORD_META,
182                 .len = len,
183         };
184
185         return tnl_send(tnl, (char *)&record, sizeof(record)) && tnl_send(tnl, buf, len);
186 }
187
188 static bool tnl_send_packet(tnl_t *tnl, const char *buf, int len) {
189         tnl_record_t record = {
190                 .type = TNL_RECORD_PACKET,
191                 .len = len,
192         };
193
194         return tnl_send(tnl, (char *)&record, sizeof(record)) && tnl_send(tnl, buf, len);
195 }
196
197 static bool tnl_close(tnl_t *tnl) {
198         if(tnl->session) {
199                 gnutls_bye(tnl->session, GNUTLS_SHUT_RDWR);
200                 gnutls_deinit(tnl->session);
201         }
202                 
203         fd_del(&tnl->fd);
204         close(tnl->fd.fd);
205         
206         tnl_del(tnl);
207
208         return true;
209 }
210
211 static bool tnl_accept_error(tnl_t *tnl, int errnum) {
212         logger(LOG_ERR, _("tnl: error %d on accepted tunnel"));
213         return true;
214 }
215
216 static bool tnl_accept_handler(fd_t *fd) {
217         tnl_listen_t *listener = fd->data;
218         tnl_t *tnl;
219         struct sockaddr_storage ss;
220         socklen_t len = sizeof ss;
221         int sock;       
222         
223         sock = accept(fd->fd, sa(&ss), &len);
224
225         if(sock == -1) {
226                 logger(LOG_ERR, _("tnl: could not accept incoming connection: %s"), strerror(errno));
227                 return false;
228         }
229
230         logger(LOG_DEBUG, _("tnl: accepted incoming connection"));
231
232         sa_unmap(&ss);
233
234         new(tnl);
235         tnl->local = listener->local;
236         tnl->remote.address = ss;
237         len = sizeof tnl->local.address;
238         getsockname(sock, sa(&tnl->local.address), &len);
239         sa_unmap(&tnl->local.address);
240         tnl->type = listener->type;
241         tnl->protocol = listener->protocol;
242         tnl->status = TNL_STATUS_CONNECTING;
243         tnl->error = tnl_accept_error;
244         tnl->close = tnl_close;
245
246         tnl->fd.fd = sock;
247         tnl->fd.mode = FD_MODE_READ;
248         tnl->fd.handler = tnl_handshake_handler;
249         tnl->fd.data = tnl;
250
251         fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
252
253         tnl_add(tnl);
254
255         gnutls_init(&tnl->session, GNUTLS_SERVER);
256         //gnutls_handshake_set_private_extensions(tnl->session, 1);
257         gnutls_set_default_priority(tnl->session);
258         gnutls_credentials_set(tnl->session, GNUTLS_CRD_CERTIFICATE, tnl->local.cred);
259         gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST);
260         gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)sock);
261         gnutls_handshake(tnl->session);
262
263         tnl->accept = listener->accept;
264         
265         fd_add(&tnl->fd);
266         
267         return true;
268 }       
269
270 static bool tnl_connect_handler(fd_t *fd) {
271         tnl_t *tnl = fd->data;
272         int result;
273         socklen_t len;
274
275         len = sizeof result;
276         getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &result, &len);
277         if(result) {
278                 logger(LOG_ERR, "tnl: error while connecting: %s", strerror(result));
279                 tnl->error(tnl, result);
280                 tnl->close(tnl);
281                 return false;
282         }
283         
284         fd_del(&tnl->fd);
285
286         fcntl(tnl->fd.fd, F_SETFL, fcntl(tnl->fd.fd, F_GETFL) | O_NONBLOCK);
287
288         tnl->status = TNL_STATUS_HANDSHAKE;
289         gnutls_init(&tnl->session, GNUTLS_CLIENT);
290         //gnutls_handshake_set_private_extensions(tnl->session, 1);
291         gnutls_set_default_priority(tnl->session);
292         gnutls_credentials_set(tnl->session, GNUTLS_CRD_CERTIFICATE, tnl->local.cred);
293         gnutls_certificate_server_set_request(tnl->session, GNUTLS_CERT_REQUEST);
294         gnutls_transport_set_ptr(tnl->session, (gnutls_transport_ptr)fd->fd);
295         gnutls_handshake(tnl->session);
296
297         tnl->fd.mode = FD_MODE_READ;
298         tnl->fd.handler = tnl_handshake_handler;
299         fd_add(&tnl->fd);
300
301         logger(LOG_DEBUG, _("tnl: connected"));
302         
303         return true;
304 }
305
306 bool tnl_connect(tnl_t *tnl) {
307         int sock;
308
309         sock = socket(sa_family(&tnl->remote.address), tnl->type, tnl->protocol);
310
311         if(sock == -1) {
312                 logger(LOG_ERR, _("tnl: could not create socket: %s"), strerror(errno));
313                 return false;
314         }
315         
316 #if 0
317         if(sa_nonzero(&tnl->local.address) && bind(sock, sa(&tnl->local.address), sa_len(&tnl->local.address)) == -1) {
318                 logger(LOG_ERR, _("tnl: could not bind socket: %s"), strerror(errno));
319                 close(sock);
320                 return false;
321         }
322 #endif
323
324         if(connect(sock, sa(&tnl->remote.address), sa_len(&tnl->remote.address)) == -1) {
325                 logger(LOG_ERR, _("tnl: could not connect: %s"), strerror(errno));
326                 close(sock);
327                 return false;
328         }
329
330         tnl->status = TNL_STATUS_CONNECTING;
331
332         tnl->fd.fd = sock;
333         tnl->fd.mode = FD_MODE_WRITE;
334         tnl->fd.handler = tnl_connect_handler;
335         tnl->fd.data = tnl;
336
337         tnl->send_packet = tnl_send_packet;
338         tnl->send_meta = tnl_send_meta;
339         tnl->close = tnl_close;
340         
341         tnl_add(tnl);
342
343
344         fd_add(&tnl->fd);
345
346         return true;
347 }
348
349 static bool tnl_listen_close(tnl_listen_t *listener) {
350         fd_del(&listener->fd);
351         close(listener->fd.fd);
352         tnl_listen_del(listener);
353         return true;
354 }
355
356 bool tnl_listen(tnl_listen_t *listener) {
357         int sock;
358
359         sock = socket(sa_family(&listener->local.address), listener->type, listener->protocol);
360
361         if(sock == -1) {
362                 logger(LOG_ERR, _("tnl: could not create listener socket: %s"), strerror(errno));
363                 return false;
364         }
365         
366         if(bind(sock, sa(&listener->local.address), sa_len(&listener->local.address)) == -1) {
367                 logger(LOG_ERR, _("tnl: could not bind listener socket: %s"), strerror(errno));
368                 return false;
369         }
370         
371         if(listen(sock, 10) == -1) {
372                 logger(LOG_ERR, _("tnl: could not listen on listener socket: %s"), strerror(errno));
373                 return false;
374         }
375
376         listener->fd.fd = sock;
377         listener->fd.mode = FD_MODE_READ;
378         listener->fd.handler = tnl_accept_handler;
379         listener->fd.data = listener;
380         listener->close = tnl_listen_close;
381
382         tnl_listen_add(listener);
383         fd_add(&listener->fd);
384
385         return true;
386 }