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