4 Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
25 #include <gnutls/gnutls.h>
27 #include "support/avl.h"
28 #include "support/sockaddr.h"
29 #include "support/xalloc.h"
32 static avl_tree_t *tnls, *listeners;
35 tnls = avl_tree_new(NULL, (avl_action_t)free);
36 listeners = avl_tree_new(NULL, (avl_action_t)free);
42 avl_tree_del(listeners);
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)
53 static bool tnl_send(tnl_t *tnl, const char *buf, int len) {
57 result = gnutls_record_send(tnl->session, buf, len);
59 if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
63 logger(LOG_ERR, _("tnl: error while sending: %s"), gnutls_strerror(result));
65 logger(LOG_INFO, _("tnl: connection closed by peer"));
67 tnl->error(tnl, result);
79 static bool tnl_recv(tnl_t *tnl) {
81 tnl_record_t *record = (tnl_record_t *)tnl->buf;
83 result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof tnl->buf - tnl->bufread);
85 if(result == GNUTLS_E_INTERRUPTED || result == GNUTLS_E_AGAIN)
89 logger(LOG_ERR, _("tnl: error while receiving: %s"), gnutls_strerror(result));
91 logger(LOG_INFO, _("tnl: connection closed by peer"));
93 tnl->error(tnl, result);
98 tnl->bufread += result;
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);
106 case TNL_RECORD_PACKET:
107 tnl->recv_packet(tnl, record->data, record->len);
111 logger(LOG_ERR, _("tnl: error while receiving: %s"), _("unknown record type"));
112 tnl->error(tnl, EINVAL);
117 tnl->bufread -= sizeof *record + record->len;
118 memmove(tnl->buf, record->data + record->len, tnl->bufread);
122 static bool tnl_recv_handler(fd_t *fd) {
123 tnl_t *tnl = fd->data;
126 result = gnutls_record_recv(tnl->session, tnl->buf + tnl->bufread, sizeof(tnl->buf) - tnl->bufread);
128 if(gnutls_error_is_fatal(result)) {
129 logger(LOG_DEBUG, _("tnl: reception failed: %s\n"), gnutls_strerror(result));
130 tnl->error(tnl, result);
138 tnl->bufread += result;
139 return tnl_recv(tnl);
142 static bool tnl_handshake_handler(fd_t *fd) {
143 tnl_t *tnl = fd->data;
146 result = gnutls_handshake(tnl->session);
148 if(gnutls_error_is_fatal(result)) {
149 logger(LOG_ERR, "tnl: handshake error: %s\n", gnutls_strerror(result));
154 /* check other stuff? */
158 logger(LOG_DEBUG, _("tnl: handshake finished"));
160 result = gnutls_certificate_verify_peers(tnl->session);
162 logger(LOG_ERR, "tnl: certificate error: %s\n", gnutls_strerror(result));
168 logger(LOG_ERR, "tnl: certificate not good, verification result %x", result);
173 tnl->status == TNL_STATUS_UP;
174 tnl->fd.handler = tnl_recv_handler;
179 static bool tnl_send_meta(tnl_t *tnl, const char *buf, int len) {
180 tnl_record_t record = {
181 .type = TNL_RECORD_META,
185 return tnl_send(tnl, (char *)&record, sizeof(record)) && tnl_send(tnl, buf, len);
188 static bool tnl_send_packet(tnl_t *tnl, const char *buf, int len) {
189 tnl_record_t record = {
190 .type = TNL_RECORD_PACKET,
194 return tnl_send(tnl, (char *)&record, sizeof(record)) && tnl_send(tnl, buf, len);
197 static bool tnl_close(tnl_t *tnl) {
199 gnutls_bye(tnl->session, GNUTLS_SHUT_RDWR);
200 gnutls_deinit(tnl->session);
211 static bool tnl_accept_error(tnl_t *tnl, int errnum) {
212 logger(LOG_ERR, _("tnl: error %d on accepted tunnel"));
216 static bool tnl_accept_handler(fd_t *fd) {
217 tnl_listen_t *listener = fd->data;
219 struct sockaddr_storage ss;
220 socklen_t len = sizeof ss;
223 sock = accept(fd->fd, sa(&ss), &len);
226 logger(LOG_ERR, _("tnl: could not accept incoming connection: %s"), strerror(errno));
230 logger(LOG_DEBUG, _("tnl: accepted incoming connection"));
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;
247 tnl->fd.mode = FD_MODE_READ;
248 tnl->fd.handler = tnl_handshake_handler;
251 fcntl(sock, F_SETFL, fcntl(sock, F_GETFL) | O_NONBLOCK);
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);
263 tnl->accept = listener->accept;
270 static bool tnl_connect_handler(fd_t *fd) {
271 tnl_t *tnl = fd->data;
276 getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &result, &len);
278 logger(LOG_ERR, "tnl: error while connecting: %s", strerror(result));
279 tnl->error(tnl, result);
286 fcntl(tnl->fd.fd, F_SETFL, fcntl(tnl->fd.fd, F_GETFL) | O_NONBLOCK);
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);
297 tnl->fd.mode = FD_MODE_READ;
298 tnl->fd.handler = tnl_handshake_handler;
301 logger(LOG_DEBUG, _("tnl: connected"));
306 bool tnl_connect(tnl_t *tnl) {
309 sock = socket(sa_family(&tnl->remote.address), tnl->type, tnl->protocol);
312 logger(LOG_ERR, _("tnl: could not create socket: %s"), strerror(errno));
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));
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));
330 tnl->status = TNL_STATUS_CONNECTING;
333 tnl->fd.mode = FD_MODE_WRITE;
334 tnl->fd.handler = tnl_connect_handler;
337 tnl->send_packet = tnl_send_packet;
338 tnl->send_meta = tnl_send_meta;
339 tnl->close = tnl_close;
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);
356 bool tnl_listen(tnl_listen_t *listener) {
359 sock = socket(sa_family(&listener->local.address), listener->type, listener->protocol);
362 logger(LOG_ERR, _("tnl: could not create listener socket: %s"), strerror(errno));
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));
371 if(listen(sock, 10) == -1) {
372 logger(LOG_ERR, _("tnl: could not listen on listener socket: %s"), strerror(errno));
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;
382 tnl_listen_add(listener);
383 fd_add(&listener->fd);