Move TCP closure detection to cstp.c, make it reconnect when it happens
[platform/upstream/openconnect.git] / cstp.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008 Intel Corporation.
5  * Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
6  *
7  * Author: David Woodhouse <dwmw2@infradead.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * version 2.1, as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to:
20  *
21  *   Free Software Foundation, Inc.
22  *   51 Franklin Street, Fifth Floor,
23  *   Boston, MA 02110-1301 USA
24  */
25 #include <netdb.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <time.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <arpa/inet.h>
32
33 #include <openssl/ssl.h>
34 #include <openssl/err.h>
35
36 #include "openconnect.h"
37
38 /*
39  * Data packets are encapsulated in the SSL stream as follows:
40  *
41  * 0000: Magic "STF\x1"
42  * 0004: Big-endian 16-bit length (not including 8-byte header)
43  * 0006: Byte packet type (see openconnect.h)
44  * 0008: data payload
45  */
46
47 static char data_hdr[8] = {
48         'S', 'T', 'F', 1,
49         0, 0,           /* Length */
50         AC_PKT_DATA,    /* Type */
51         0               /* Unknown */
52 };
53
54 static struct pkt keepalive_pkt = {
55         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_KEEPALIVE, 0 },
56 };
57
58 static struct pkt dpd_pkt = {
59         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_OUT, 0 },
60 };
61
62 static struct pkt dpd_resp_pkt = {
63         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_RESP, 0 },
64 };
65
66
67 static int start_cstp_connection(struct openconnect_info *vpninfo)
68 {
69         char buf[65536];
70         int i;
71         int retried = 0;
72         struct vpn_option **next_dtls_option = &vpninfo->dtls_options;
73         struct vpn_option **next_cstp_option = &vpninfo->cstp_options;
74         struct vpn_option *old_cstp_opts = vpninfo->cstp_options;
75         struct vpn_option *old_dtls_opts = vpninfo->dtls_options;
76         const char *old_addr = vpninfo->vpn_addr;
77         const char *old_netmask = vpninfo->vpn_netmask;
78         struct split_include *inc;
79
80         /* Clear old options which will be overwritten */
81         vpninfo->vpn_addr = vpninfo->vpn_netmask = NULL;
82         vpninfo->cstp_options = vpninfo->dtls_options = NULL;
83         vpninfo->vpn_domain = vpninfo->vpn_proxy_pac = NULL;
84
85         for (i=0; i<3; i++)
86                 vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
87
88         for (inc = vpninfo->split_includes; inc; ) {
89                 struct split_include *next = inc->next;
90                 free(inc);
91                 inc = next;
92         }
93         for (inc = vpninfo->split_excludes; inc; ) {
94                 struct split_include *next = inc->next;
95                 free(inc);
96                 inc = next;
97         }
98         vpninfo->split_includes = vpninfo->split_excludes = NULL;
99  retry:
100         openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
101         openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
102         openconnect_SSL_printf(vpninfo->https_ssl, "User-Agent: %s\r\n", vpninfo->useragent);
103         openconnect_SSL_printf(vpninfo->https_ssl, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
104         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Version: 1\r\n");
105         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
106         if (vpninfo->deflate)
107                 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
108         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
109         /* To enable IPv6, send 'IPv6,IPv4'.
110            We don't know how most of that works yet though. */
111         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Address-Type: IPv4\r\n");
112         openconnect_SSL_printf(vpninfo->https_ssl, "X-DTLS-Master-Secret: ");
113         for (i = 0; i < sizeof(vpninfo->dtls_secret); i++)
114                 openconnect_SSL_printf(vpninfo->https_ssl, "%02X", vpninfo->dtls_secret[i]);
115         openconnect_SSL_printf(vpninfo->https_ssl, "\r\nX-DTLS-CipherSuite: %s\r\n\r\n",
116                                vpninfo->dtls_ciphers?:"AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA");
117
118         if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
119                 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTPS response\n");
120                 if (!retried) {
121                         retried = 1;
122                         openconnect_close_https(vpninfo);
123
124                         if (openconnect_open_https(vpninfo)) {
125                                 vpninfo->progress(vpninfo, PRG_ERR,
126                                                   "Failed to open HTTPS connection to %s\n",
127                                                   vpninfo->hostname);
128                                 exit(1);
129                         }
130                         goto retry;
131                 }
132                 return -EINVAL;
133         }
134
135         if (strncmp(buf, "HTTP/1.1 200 ", 13)) {
136                 if (!strncmp(buf, "HTTP/1.1 503 ", 13)) {
137                         /* "Service Unavailable. Why? */
138                         char *reason = "<unknown>";
139                         while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
140                                 if (!strncmp(buf, "X-Reason: ", 10)) {
141                                         reason = buf + 10;
142                                         break;
143                                 }
144                         }
145                         vpninfo->progress(vpninfo, PRG_ERR, "VPN service unavailable; reason: %s\n",
146                                           reason);
147                         return -EINVAL;
148                 }
149                 vpninfo->progress(vpninfo, PRG_ERR,
150                                   "Got inappropriate HTTP CONNECT response: %s\n",
151                                   buf);
152                 if (!strncmp(buf, "HTTP/1.1 401 ", 13))
153                         exit(2);
154                 return -EINVAL;
155         }
156
157         vpninfo->progress(vpninfo, PRG_INFO,
158                           "Got CONNECT response: %s\n", buf);
159
160         /* We may have advertised it, but we only do it if the server agrees */
161         vpninfo->deflate = 0;
162
163         while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
164                 struct vpn_option *new_option;
165                 char *colon = strchr(buf, ':');
166                 if (!colon)
167                         continue;
168
169                 *colon = 0;
170                 colon++;
171                 if (*colon == ' ')
172                         colon++;
173
174                 if (strncmp(buf, "X-DTLS-", 7) &&
175                     strncmp(buf, "X-CSTP-", 7))
176                         continue;
177
178                 new_option = malloc(sizeof(*new_option));
179                 if (!new_option) {
180                         vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
181                         return -ENOMEM;
182                 }
183                 new_option->option = strdup(buf);
184                 new_option->value = strdup(colon);
185                 new_option->next = NULL;
186
187                 if (!new_option->option || !new_option->value) {
188                         vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
189                         return -ENOMEM;
190                 }
191
192                 vpninfo->progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
193
194                 if (!strncmp(buf, "X-DTLS-", 7)) {
195                         *next_dtls_option = new_option;
196                         next_dtls_option = &new_option->next;
197                         continue;
198                 }
199                 /* CSTP options... */
200                 *next_cstp_option = new_option;
201                 next_cstp_option = &new_option->next;
202
203
204                 if (!strcmp(buf + 7, "Keepalive")) {
205                         vpninfo->ssl_times.keepalive = atol(colon);
206                 } else if (!strcmp(buf + 7, "DPD")) {
207                         vpninfo->ssl_times.dpd = atol(colon);
208                 } else if (!strcmp(buf + 7, "Content-Encoding")) {
209                         if (!strcmp(colon, "deflate"))
210                                 vpninfo->deflate = 1;
211                         else {
212                                 vpninfo->progress(vpninfo, PRG_ERR,
213                                         "Unknown CSTP-Content-Encoding %s\n",
214                                         colon);
215                                 return -EINVAL;
216                         }
217                 } else if (!strcmp(buf + 7, "MTU")) {
218                         vpninfo->mtu = atol(colon);
219                 } else if (!strcmp(buf + 7, "Address")) {
220                         vpninfo->vpn_addr = new_option->value;
221                 } else if (!strcmp(buf + 7, "Netmask")) {
222                         vpninfo->vpn_netmask = new_option->value;
223                 } else if (!strcmp(buf + 7, "DNS")) {
224                         int j;
225                         for (j = 0; j < 3; j++) {
226                                 if (!vpninfo->vpn_dns[j]) {
227                                         vpninfo->vpn_dns[j] = new_option->value;
228                                         break;
229                                 }
230                         }
231                 } else if (!strcmp(buf + 7, "NBNS")) {
232                         int j;
233                         for (j = 0; j < 3; j++) {
234                                 if (!vpninfo->vpn_nbns[j]) {
235                                         vpninfo->vpn_nbns[j] = new_option->value;
236                                         break;
237                                 }
238                         }
239                 } else if (!strcmp(buf + 7, "Default-Domain")) {
240                         vpninfo->vpn_domain = new_option->value;
241                 } else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) {
242                         vpninfo->vpn_proxy_pac = new_option->value;
243                 } else if (!strcmp(buf + 7, "Split-Include")) {
244                         struct split_include *inc = malloc(sizeof(*inc));
245                         if (!inc)
246                                 continue;
247                         inc->route = new_option->value;
248                         inc->next = vpninfo->split_includes;
249                         vpninfo->split_includes = inc;
250                 } else if (!strcmp(buf + 7, "Split-Exclude")) {
251                         struct split_include *exc = malloc(sizeof(*exc));
252                         if (!exc)
253                                 continue;
254                         exc->route = new_option->value;
255                         exc->next = vpninfo->split_excludes;
256                         vpninfo->split_excludes = exc;
257                 }
258         }
259
260         if (!vpninfo->vpn_addr) {
261                 vpninfo->progress(vpninfo, PRG_ERR, "No IP address received. Aborting\n");
262                 return -EINVAL;
263         }
264         if (!vpninfo->vpn_netmask)
265                 vpninfo->vpn_netmask = "255.255.255.255";
266         if (old_addr) {
267                 if (strcmp(old_addr, vpninfo->vpn_addr)) {
268                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different IP address (%s != %s)\n",
269                                 vpninfo->vpn_addr, old_addr);
270                         return -EINVAL;
271                 }
272         }
273         if (old_netmask) {
274                 if (strcmp(old_netmask, vpninfo->vpn_netmask)) {
275                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different netmask (%s != %s)\n",
276                                 vpninfo->vpn_netmask, old_netmask);
277                         return -EINVAL;
278                 }
279         }
280
281         while (old_dtls_opts) {
282                 struct vpn_option *tmp = old_dtls_opts;
283                 old_dtls_opts = old_dtls_opts->next;
284                 free(tmp->value);
285                 free(tmp->option);
286                 free(tmp);
287         }
288         while (old_cstp_opts) {
289                 struct vpn_option *tmp = old_cstp_opts;
290                 old_cstp_opts = old_cstp_opts->next;
291                 free(tmp->value);
292                 free(tmp->option);
293                 free(tmp);
294         }
295         vpninfo->progress(vpninfo, PRG_INFO, "CSTP connected. DPD %d, Keepalive %d\n",
296                           vpninfo->ssl_times.dpd, vpninfo->ssl_times.keepalive);
297
298         BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl), 1);
299         BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl), 1);
300
301         fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK);
302         if (vpninfo->select_nfds <= vpninfo->ssl_fd)
303                 vpninfo->select_nfds = vpninfo->ssl_fd + 1;
304
305         FD_SET(vpninfo->ssl_fd, &vpninfo->select_rfds);
306         FD_SET(vpninfo->ssl_fd, &vpninfo->select_efds);
307
308         vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
309         return 0;
310 }
311
312
313 int make_cstp_connection(struct openconnect_info *vpninfo)
314 {
315         int ret;
316
317         if (!vpninfo->https_ssl && (ret = openconnect_open_https(vpninfo)))
318                 return ret;
319
320         if (vpninfo->deflate) {
321                 vpninfo->deflate_adler32 = 1;
322                 vpninfo->inflate_adler32 = 1;
323
324                 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
325                     deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
326                                  Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
327                         vpninfo->progress(vpninfo, PRG_ERR, "Compression setup failed\n");
328                         vpninfo->deflate = 0;
329                 }
330
331                 if (!vpninfo->deflate_pkt) {
332                         vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
333                         if (!vpninfo->deflate_pkt) {
334                                 vpninfo->progress(vpninfo, PRG_ERR, "Allocation of deflate buffer failed\n");
335                                 vpninfo->deflate = 0;
336                         }
337                         memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
338                         memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
339                         vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
340                 }
341         }
342
343         return start_cstp_connection(vpninfo);
344 }
345
346 static int cstp_reconnect(struct openconnect_info *vpninfo)
347 {
348         int ret;
349         int timeout;
350         int interval;
351
352         timeout = vpninfo->reconnect_timeout;
353         interval = vpninfo->reconnect_interval;
354
355         while ((ret = make_cstp_connection(vpninfo))) {
356                 if (timeout <= 0)
357                         return ret;
358                 vpninfo->progress(vpninfo, PRG_INFO,
359                                   "sleep %ds, remaining timeout %ds\n",
360                                   interval, timeout);
361                 sleep(interval);
362                 if (killed)
363                         return 1;
364                 timeout -= interval;
365                 interval += vpninfo->reconnect_interval;
366                 if (interval > RECONNECT_INTERVAL_MAX)
367                         interval = RECONNECT_INTERVAL_MAX;
368         }
369         return 0;
370 }
371
372 static int inflate_and_queue_packet(struct openconnect_info *vpninfo, int type, void *buf, int len)
373 {
374         struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
375
376         if (!new)
377                 return -ENOMEM;
378
379         new->type = type;
380         new->next = NULL;
381
382         vpninfo->inflate_strm.next_in = buf;
383         vpninfo->inflate_strm.avail_in = len - 4;
384
385         vpninfo->inflate_strm.next_out = new->data;
386         vpninfo->inflate_strm.avail_out = vpninfo->mtu;
387         vpninfo->inflate_strm.total_out = 0;
388
389         if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
390                 vpninfo->progress(vpninfo, PRG_ERR, "inflate failed\n");
391                 free(new);
392                 return -EINVAL;
393         }
394
395         new->len = vpninfo->inflate_strm.total_out;
396
397         vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
398                                            new->data, new->len);
399
400         if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *) (buf + len - 4) )) {
401                 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
402         }
403
404         vpninfo->progress(vpninfo, PRG_TRACE,
405                           "Received compressed data packet of %ld bytes\n",
406                           vpninfo->inflate_strm.total_out);
407
408         queue_packet(&vpninfo->incoming_queue, new);
409         return 0;
410 }
411
412 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
413 {
414         unsigned char buf[16384];
415         int len, ret;
416         int work_done = 0;
417
418         /* FIXME: The poll() handling here is fairly simplistic. Actually,
419            if the SSL connection stalls it could return a WANT_WRITE error
420            on _either_ of the SSL_read() or SSL_write() calls. In that case,
421            we should probably remove POLLIN from the events we're looking for,
422            and add POLLOUT. As it is, though, it'll just chew CPU time in that
423            fairly unlikely situation, until the write backlog clears. */
424         while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
425                 int payload_len;
426
427                 if (buf[0] != 'S' || buf[1] != 'T' ||
428                     buf[2] != 'F' || buf[3] != 1 || buf[7])
429                         goto unknown_pkt;
430
431                 payload_len = (buf[4] << 8) + buf[5];
432                 if (len != 8 + payload_len) {
433                         vpninfo->progress(vpninfo, PRG_ERR,
434                                           "Unexpected packet length. SSL_read returned %d but packet is\n",
435                                           len);
436                         vpninfo->progress(vpninfo, PRG_ERR,
437                                           "%02x %02x %02x %02x %02x %02x %02x %02x\n",
438                                           buf[0], buf[1], buf[2], buf[3],
439                                           buf[4], buf[5], buf[6], buf[7]);
440                         continue;
441                 }
442                 vpninfo->ssl_times.last_rx = time(NULL);
443                 switch(buf[6]) {
444                 case AC_PKT_DPD_OUT:
445                         vpninfo->progress(vpninfo, PRG_TRACE,
446                                           "Got CSTP DPD request\n");
447                         vpninfo->owe_ssl_dpd_response = 1;
448                         continue;
449
450                 case AC_PKT_DPD_RESP:
451                         vpninfo->progress(vpninfo, PRG_TRACE,
452                                           "Got CSTP DPD response\n");
453                         continue;
454
455                 case AC_PKT_KEEPALIVE:
456                         vpninfo->progress(vpninfo, PRG_TRACE,
457                                           "Got CSTP Keepalive\n");
458                         continue;
459
460                 case AC_PKT_DATA:
461                         vpninfo->progress(vpninfo, PRG_TRACE,
462                                           "Received uncompressed data packet of %d bytes\n",
463                                           payload_len);
464                         queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
465                                          payload_len);
466                         work_done = 1;
467                         continue;
468
469                 case AC_PKT_DISCONN: {
470                         int i;
471                         for (i = 0; i < payload_len; i++) {
472                                 if (!isprint(buf[payload_len + 8 + i]))
473                                         buf[payload_len + 8 + i] = '.';
474                         }
475                         buf[payload_len + 8] = 0;
476                         vpninfo->progress(vpninfo, PRG_ERR,
477                                           "Received server disconnect: %02x '%s'\n", buf[8], buf + 9);
478                         vpninfo->quit_reason = "Server request";
479                         return 1;
480                 }
481                 case AC_PKT_COMPRESSED:
482                         if (!vpninfo->deflate) {
483                                 vpninfo->progress(vpninfo, PRG_ERR, "Compressed packet received in !deflate mode\n");
484                                 goto unknown_pkt;
485                         }
486                         inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
487                         work_done = 1;
488                         continue;
489
490                 case AC_PKT_TERM_SERVER:
491                         vpninfo->progress(vpninfo, PRG_ERR, "received server terminate packet\n");
492                         vpninfo->quit_reason = "Server request";
493                         return 1;
494                 }
495
496         unknown_pkt:
497                 vpninfo->progress(vpninfo, PRG_ERR,
498                                   "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
499                                   buf[0], buf[1], buf[2], buf[3],
500                                   buf[4], buf[5], buf[6], buf[7]);
501                 vpninfo->quit_reason = "Unknown packet received";
502                 return 1;
503         }
504
505         ret = SSL_get_error(vpninfo->https_ssl, len);
506         if (ret == SSL_ERROR_SYSCALL || ret == SSL_ERROR_ZERO_RETURN) {
507                 vpninfo->progress(vpninfo, PRG_ERR,
508                                   "SSL read error %d (server probably closed connection); reconnecting.\n",
509                                   ret);
510                         goto do_reconnect;
511         }
512
513
514         /* If SSL_write() fails we are expected to try again. With exactly
515            the same data, at exactly the same location. So we keep the
516            packet we had before.... */
517         if (vpninfo->current_ssl_pkt) {
518         handle_outgoing:
519                 vpninfo->ssl_times.last_tx = time(NULL);
520                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
521                 ret = SSL_write(vpninfo->https_ssl,
522                                 vpninfo->current_ssl_pkt->hdr,
523                                 vpninfo->current_ssl_pkt->len + 8);
524                 if (ret <= 0) {
525                         ret = SSL_get_error(vpninfo->https_ssl, ret);
526                         switch (ret) {
527                         case SSL_ERROR_WANT_WRITE:
528                                 /* Waiting for the socket to become writable -- it's
529                                    probably stalled, and/or the buffers are full */
530                                 FD_SET(vpninfo->ssl_fd, &vpninfo->select_wfds);
531
532                         case SSL_ERROR_WANT_READ:
533                                 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
534                                         goto peer_dead;
535                                 return work_done;
536                         default:
537                                 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d\n", ret);
538                                 report_ssl_errors(vpninfo);
539                                 goto do_reconnect;
540                         }
541                 }
542                 if (ret != vpninfo->current_ssl_pkt->len + 8) {
543                         vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
544                                 vpninfo->current_ssl_pkt->len + 8, ret);
545                         vpninfo->quit_reason = "Internal error";
546                         return 1;
547                 }
548                 /* Don't free the 'special' packets */
549                 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
550                     vpninfo->current_ssl_pkt != &dpd_pkt &&
551                     vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
552                     vpninfo->current_ssl_pkt != &keepalive_pkt)
553                         free(vpninfo->current_ssl_pkt);
554
555                 vpninfo->current_ssl_pkt = NULL;
556         }
557
558         if (vpninfo->owe_ssl_dpd_response) {
559                 vpninfo->owe_ssl_dpd_response = 0;
560                 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
561                 goto handle_outgoing;
562         }
563
564         switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
565         case KA_REKEY:
566                 /* Not that this will ever happen; we don't even process
567                    the setting when we're asked for it. */
568                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
569                 time(&vpninfo->ssl_times.last_rekey);
570                 work_done = 1;
571                 break;
572
573         case KA_DPD_DEAD:
574         peer_dead:
575                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
576         do_reconnect:
577                 openconnect_close_https(vpninfo);
578
579                 /* It's already deflated in the old stream. Extremely
580                    non-trivial to reconstitute it; just throw it away */
581                 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
582                         vpninfo->current_ssl_pkt = NULL;
583
584                 if (cstp_reconnect(vpninfo)) {
585                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
586                         vpninfo->quit_reason = "CSTP reconnect failed";
587                         return 1;
588                 }
589                 /* I think we can leave DTLS to its own devices; when we reconnect
590                    with the same master secret, we do seem to get the same sessid */
591                 return 1;
592
593         case KA_DPD:
594                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
595
596                 vpninfo->current_ssl_pkt = &dpd_pkt;
597                 goto handle_outgoing;
598
599         case KA_KEEPALIVE:
600                 /* No need to send an explicit keepalive
601                    if we have real data to send */
602                 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
603                         break;
604
605                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
606
607                 vpninfo->current_ssl_pkt = &keepalive_pkt;
608                 goto handle_outgoing;
609
610         case KA_NONE:
611                 ;
612         }
613
614         /* Service outgoing packet queue, if no DTLS */
615         while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
616                 struct pkt *this = vpninfo->outgoing_queue;
617                 vpninfo->outgoing_queue = this->next;
618                 vpninfo->outgoing_qlen--;
619
620                 /* FIXME: Don't know how to handle IPv6 yet */
621                 if (this->type != AF_INET)
622                         continue;
623
624                 if (vpninfo->deflate) {
625                         unsigned char *adler;
626                         int ret;
627
628                         vpninfo->deflate_strm.next_in = this->data;
629                         vpninfo->deflate_strm.avail_in = this->len;
630                         vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
631                         vpninfo->deflate_strm.avail_out = 2040;
632                         vpninfo->deflate_strm.total_out = 0;
633
634                         ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
635                         if (ret) {
636                                 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
637                                 goto uncompr;
638                         }
639
640                         vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
641                         vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
642
643                         /* Add ongoing adler32 to tail of compressed packet */
644                         vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
645                                                            this->data, this->len);
646
647                         adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
648                         *(adler++) =  vpninfo->deflate_adler32 >> 24;
649                         *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
650                         *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
651                         *(adler)   =  vpninfo->deflate_adler32 & 0xff;
652
653                         vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
654
655                         vpninfo->progress(vpninfo, PRG_TRACE,
656                                           "Sending compressed data packet of %d bytes\n",
657                                           this->len);
658
659                         vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
660                 } else {
661                 uncompr:
662                         memcpy(this->hdr, data_hdr, 8);
663                         this->hdr[4] = this->len >> 8;
664                         this->hdr[5] = this->len & 0xff;
665
666                         vpninfo->progress(vpninfo, PRG_TRACE,
667                                           "Sending uncompressed data packet of %d bytes\n",
668                                           this->len);
669
670                         vpninfo->current_ssl_pkt = this;
671                 }
672                 goto handle_outgoing;
673         }
674
675         /* Work is not done if we just got rid of packets off the queue */
676         return work_done;
677 }
678
679 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
680 {
681         unsigned char *bye_pkt;
682         int reason_len;
683
684         /* already lost connection? */
685         if (!vpninfo->https_ssl)
686                 return 0;
687
688         reason_len = strlen(reason);
689         bye_pkt = malloc(reason_len + 9);
690         if (!bye_pkt)
691                 return -ENOMEM;
692
693         memcpy(bye_pkt, data_hdr, 8);
694         memcpy(bye_pkt + 9, reason, reason_len);
695
696         bye_pkt[4] = (reason_len + 1) >> 8;
697         bye_pkt[5] = (reason_len + 1) & 0xff;
698         bye_pkt[6] = AC_PKT_DISCONN;
699         bye_pkt[8] = 0xb0;
700
701         SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 9);
702         free(bye_pkt);
703
704         vpninfo->progress(vpninfo, PRG_INFO,
705                           "Send BYE packet: %s\n", reason);
706
707         return 0;
708 }