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