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