Fix build with -ansi and without optimisation.
[platform/upstream/openconnect.git] / mainloop.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2010 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 #define _POSIX_SOURCE
26 #include <errno.h>
27 #include <poll.h>
28 #include <limits.h>
29 #include <sys/select.h>
30 #include <signal.h>
31 #include <arpa/inet.h>
32 #include <unistd.h>
33 #include <string.h>
34
35 #include <openssl/ssl.h>
36
37 #include "openconnect-internal.h"
38
39 void queue_packet(struct pkt **q, struct pkt *new)
40 {
41         while (*q)
42                 q = &(*q)->next;
43
44         new->next = NULL;
45         *q = new;
46 }
47
48 int queue_new_packet(struct pkt **q, void *buf, int len)
49 {
50         struct pkt *new = malloc(sizeof(struct pkt) + len);
51         if (!new)
52                 return -ENOMEM;
53
54         new->len = len;
55         new->next = NULL;
56         memcpy(new->data, buf, len);
57         queue_packet(q, new);
58         return 0;
59 }
60
61 int killed;
62
63 static void handle_sigint(int sig)
64 {
65         killed = sig;
66 }
67
68 int vpn_mainloop(struct openconnect_info *vpninfo)
69 {
70         struct sigaction sa;
71         memset(&sa, 0, sizeof(sa));
72         sa.sa_handler = handle_sigint;
73
74         sigaction(SIGTERM, &sa, NULL);
75         sigaction(SIGINT, &sa, NULL);
76         sigaction(SIGHUP, &sa, NULL);
77
78         while (!vpninfo->quit_reason) {
79                 int did_work = 0;
80                 int timeout = INT_MAX;
81                 struct timeval tv;
82                 fd_set rfds, wfds, efds;
83
84 #ifdef SSL_OP_CISCO_ANYCONNECT
85                 if (vpninfo->new_dtls_ssl)
86                         dtls_try_handshake(vpninfo);
87
88                 if (vpninfo->dtls_attempt_period && !vpninfo->dtls_ssl && !vpninfo->new_dtls_ssl &&
89                     vpninfo->new_dtls_started + vpninfo->dtls_attempt_period < time(NULL)) {
90                         vpn_progress(vpninfo, PRG_TRACE, _("Attempt new DTLS connection\n"));
91                         connect_dtls_socket(vpninfo);
92                 }
93                 if (vpninfo->dtls_ssl)
94                         did_work += dtls_mainloop(vpninfo, &timeout);
95 #endif
96                 if (vpninfo->quit_reason)
97                         break;
98
99                 did_work += cstp_mainloop(vpninfo, &timeout);
100                 if (vpninfo->quit_reason)
101                         break;
102
103                 /* Tun must be last because it will set/clear its bit
104                    in the select_rfds according to the queue length */
105                 did_work += tun_mainloop(vpninfo, &timeout);
106                 if (vpninfo->quit_reason)
107                         break;
108
109                 if (killed) {
110                         if (killed == SIGHUP)
111                                 vpninfo->quit_reason = "Client received SIGHUP";
112                         else if (killed == SIGINT)
113                                 vpninfo->quit_reason = "Client received SIGINT";
114                         else
115                                 vpninfo->quit_reason = "Client killed";
116                         break;
117                 }
118
119                 if (did_work)
120                         continue;
121
122                 vpn_progress(vpninfo, PRG_TRACE,
123                              _("No work to do; sleeping for %d ms...\n"), timeout);
124                 memcpy(&rfds, &vpninfo->select_rfds, sizeof(rfds));
125                 memcpy(&wfds, &vpninfo->select_wfds, sizeof(wfds));
126                 memcpy(&efds, &vpninfo->select_efds, sizeof(efds));
127
128                 tv.tv_sec = timeout / 1000;
129                 tv.tv_usec = (timeout % 1000) * 1000;
130
131                 select(vpninfo->select_nfds, &rfds, &wfds, &efds, &tv);
132         }
133
134         cstp_bye(vpninfo, vpninfo->quit_reason);
135
136         shutdown_tun(vpninfo);
137         return 0;
138 }
139
140 /* Called when the socket is unwritable, to get the deadline for DPD.
141    Returns 1 if DPD deadline has already arrived. */
142 int ka_stalled_dpd_time(struct keepalive_info *ka, int *timeout)
143 {
144         time_t now, due;
145
146         if (!ka->dpd)
147                 return 0;
148
149         time(&now);
150         due = ka->last_rx + (2 * ka->dpd);
151
152         if (now > due)
153                 return 1;
154
155         if (*timeout > (due - now) * 1000)
156                 *timeout = (due - now) * 1000;
157
158         return 0;
159 }
160
161
162 int keepalive_action(struct keepalive_info *ka, int *timeout)
163 {
164         time_t now = time(NULL);
165
166         if (ka->rekey) {
167                 time_t due = ka->last_rekey + ka->rekey;
168
169                 if (now >= due)
170                         return KA_REKEY;
171
172                 if (*timeout > (due - now) * 1000)
173                         *timeout = (due - now) * 1000;
174         }
175
176         /* DPD is bidirectional -- PKT 3 out, PKT 4 back */
177         if (ka->dpd) {
178                 time_t due = ka->last_rx + ka->dpd;
179                 time_t overdue = ka->last_rx + (2 * ka->dpd);
180
181                 /* Peer didn't respond */
182                 if (now > overdue)
183                         return KA_DPD_DEAD;
184
185                 /* If we already have DPD outstanding, don't flood. Repeat by
186                    all means, but only after half the DPD period. */
187                 if (ka->last_dpd > ka->last_rx)
188                         due = ka->last_dpd + ka->dpd / 2;
189
190                 /* We haven't seen a packet from this host for $DPD seconds.
191                    Prod it to see if it's still alive */
192                 if (now >= due) {
193                         ka->last_dpd = now;
194                         return KA_DPD;
195                 }
196                 if (*timeout > (due - now) * 1000)
197                         *timeout = (due - now) * 1000;
198         }
199
200         /* Keepalive is just client -> server */
201         if (ka->keepalive) {
202                 time_t due = ka->last_tx + ka->keepalive;
203
204                 /* If we haven't sent anything for $KEEPALIVE seconds, send a
205                    dummy packet (which the server will discard) */
206                 if (now >= due)
207                         return KA_KEEPALIVE;
208
209                 if (*timeout > (due - now) * 1000)
210                         *timeout = (due - now) * 1000;
211         }
212
213         return KA_NONE;
214 }