Add DTLS support for GnuTLS
[platform/upstream/openconnect.git] / mainloop.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2011 Intel Corporation.
5  *
6  * Author: David Woodhouse <dwmw2@infradead.org>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1, as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to:
19  *
20  *   Free Software Foundation, Inc.
21  *   51 Franklin Street, Fifth Floor,
22  *   Boston, MA 02110-1301 USA
23  */
24
25 #include <errno.h>
26 #include <poll.h>
27 #include <limits.h>
28 #include <sys/select.h>
29 #include <signal.h>
30 #include <unistd.h>
31 #include <string.h>
32
33 #include <openssl/ssl.h>
34
35 #include "openconnect-internal.h"
36
37 void queue_packet(struct pkt **q, struct pkt *new)
38 {
39         while (*q)
40                 q = &(*q)->next;
41
42         new->next = NULL;
43         *q = new;
44 }
45
46 int queue_new_packet(struct pkt **q, void *buf, int len)
47 {
48         struct pkt *new = malloc(sizeof(struct pkt) + len);
49         if (!new)
50                 return -ENOMEM;
51
52         new->len = len;
53         new->next = NULL;
54         memcpy(new->data, buf, len);
55         queue_packet(q, new);
56         return 0;
57 }
58
59 int killed;
60
61 static void handle_sigint(int sig)
62 {
63         killed = sig;
64 }
65
66 int vpn_mainloop(struct openconnect_info *vpninfo)
67 {
68         struct sigaction sa;
69         memset(&sa, 0, sizeof(sa));
70         sa.sa_handler = handle_sigint;
71
72         sigaction(SIGTERM, &sa, NULL);
73         sigaction(SIGINT, &sa, NULL);
74         sigaction(SIGHUP, &sa, NULL);
75
76         while (!vpninfo->quit_reason) {
77                 int did_work = 0;
78                 int timeout = INT_MAX;
79                 struct timeval tv;
80                 fd_set rfds, wfds, efds;
81
82 #ifdef HAVE_DTLS
83                 if (vpninfo->new_dtls_ssl)
84                         dtls_try_handshake(vpninfo);
85
86                 if (vpninfo->dtls_attempt_period && !vpninfo->dtls_ssl && !vpninfo->new_dtls_ssl &&
87                     vpninfo->new_dtls_started + vpninfo->dtls_attempt_period < time(NULL)) {
88                         vpn_progress(vpninfo, PRG_TRACE, _("Attempt new DTLS connection\n"));
89                         connect_dtls_socket(vpninfo);
90                 }
91                 if (vpninfo->dtls_ssl)
92                         did_work += dtls_mainloop(vpninfo, &timeout);
93 #endif
94                 if (vpninfo->quit_reason)
95                         break;
96
97                 did_work += cstp_mainloop(vpninfo, &timeout);
98                 if (vpninfo->quit_reason)
99                         break;
100
101                 /* Tun must be last because it will set/clear its bit
102                    in the select_rfds according to the queue length */
103                 did_work += tun_mainloop(vpninfo, &timeout);
104                 if (vpninfo->quit_reason)
105                         break;
106
107                 if (killed) {
108                         if (killed == SIGHUP)
109                                 vpninfo->quit_reason = "Client received SIGHUP";
110                         else if (killed == SIGINT)
111                                 vpninfo->quit_reason = "Client received SIGINT";
112                         else
113                                 vpninfo->quit_reason = "Client killed";
114                         break;
115                 }
116
117                 if (did_work)
118                         continue;
119
120                 vpn_progress(vpninfo, PRG_TRACE,
121                              _("No work to do; sleeping for %d ms...\n"), timeout);
122                 memcpy(&rfds, &vpninfo->select_rfds, sizeof(rfds));
123                 memcpy(&wfds, &vpninfo->select_wfds, sizeof(wfds));
124                 memcpy(&efds, &vpninfo->select_efds, sizeof(efds));
125
126                 tv.tv_sec = timeout / 1000;
127                 tv.tv_usec = (timeout % 1000) * 1000;
128
129                 select(vpninfo->select_nfds, &rfds, &wfds, &efds, &tv);
130         }
131
132         cstp_bye(vpninfo, vpninfo->quit_reason);
133
134         shutdown_tun(vpninfo);
135         return 0;
136 }
137
138 /* Called when the socket is unwritable, to get the deadline for DPD.
139    Returns 1 if DPD deadline has already arrived. */
140 int ka_stalled_dpd_time(struct keepalive_info *ka, int *timeout)
141 {
142         time_t now, due;
143
144         if (!ka->dpd)
145                 return 0;
146
147         time(&now);
148         due = ka->last_rx + (2 * ka->dpd);
149
150         if (now > due)
151                 return 1;
152
153         if (*timeout > (due - now) * 1000)
154                 *timeout = (due - now) * 1000;
155
156         return 0;
157 }
158
159
160 int keepalive_action(struct keepalive_info *ka, int *timeout)
161 {
162         time_t now = time(NULL);
163
164         if (ka->rekey) {
165                 time_t due = ka->last_rekey + ka->rekey;
166
167                 if (now >= due)
168                         return KA_REKEY;
169
170                 if (*timeout > (due - now) * 1000)
171                         *timeout = (due - now) * 1000;
172         }
173
174         /* DPD is bidirectional -- PKT 3 out, PKT 4 back */
175         if (ka->dpd) {
176                 time_t due = ka->last_rx + ka->dpd;
177                 time_t overdue = ka->last_rx + (2 * ka->dpd);
178
179                 /* Peer didn't respond */
180                 if (now > overdue)
181                         return KA_DPD_DEAD;
182
183                 /* If we already have DPD outstanding, don't flood. Repeat by
184                    all means, but only after half the DPD period. */
185                 if (ka->last_dpd > ka->last_rx)
186                         due = ka->last_dpd + ka->dpd / 2;
187
188                 /* We haven't seen a packet from this host for $DPD seconds.
189                    Prod it to see if it's still alive */
190                 if (now >= due) {
191                         ka->last_dpd = now;
192                         return KA_DPD;
193                 }
194                 if (*timeout > (due - now) * 1000)
195                         *timeout = (due - now) * 1000;
196         }
197
198         /* Keepalive is just client -> server */
199         if (ka->keepalive) {
200                 time_t due = ka->last_tx + ka->keepalive;
201
202                 /* If we haven't sent anything for $KEEPALIVE seconds, send a
203                    dummy packet (which the server will discard) */
204                 if (now >= due)
205                         return KA_KEEPALIVE;
206
207                 if (*timeout > (due - now) * 1000)
208                         *timeout = (due - now) * 1000;
209         }
210
211         return KA_NONE;
212 }