Support proxy autoconfiguration
[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; inc = inc->next) {
87                 struct split_include *next = inc->next;
88                 free(inc);
89                 inc = next;
90         }
91         for (inc = vpninfo->split_excludes; inc; inc = inc->next) {
92                 struct split_include *next = inc->next;
93                 free(inc);
94                 inc = next;
95         }
96  retry:
97         openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
98         openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
99         openconnect_SSL_printf(vpninfo->https_ssl, "User-Agent: %s\r\n", vpninfo->useragent);
100         openconnect_SSL_printf(vpninfo->https_ssl, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
101         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Version: 1\r\n");
102         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
103         if (vpninfo->deflate)
104                 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
105         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
106         /* To enable IPv6, send 'IPv6,IPv4'.
107            We don't know how most of that works yet though. */
108         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Address-Type: IPv4\r\n");
109         openconnect_SSL_printf(vpninfo->https_ssl, "X-DTLS-Master-Secret: ");
110         for (i = 0; i < sizeof(vpninfo->dtls_secret); i++)
111                 openconnect_SSL_printf(vpninfo->https_ssl, "%02X", vpninfo->dtls_secret[i]);
112         openconnect_SSL_printf(vpninfo->https_ssl, "\r\nX-DTLS-CipherSuite: %s\r\n\r\n",
113                                vpninfo->dtls_ciphers?:"AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA");
114
115         if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
116                 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTPS response\n");
117                 if (!retried) {
118                         retried = 1;
119                         openconnect_close_https(vpninfo);
120
121                         if (openconnect_open_https(vpninfo)) {
122                                 vpninfo->progress(vpninfo, PRG_ERR,
123                                                   "Failed to open HTTPS connection to %s\n",
124                                                   vpninfo->hostname);
125                                 exit(1);
126                         }
127                         goto retry;
128                 }
129                 return -EINVAL;
130         }
131
132         if (strncmp(buf, "HTTP/1.1 200 ", 13)) {
133                 if (!strncmp(buf, "HTTP/1.1 503 ", 13)) {
134                         /* "Service Unavailable. Why? */
135                         char *reason = "<unknown>";
136                         while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
137                                 if (!strncmp(buf, "X-Reason: ", 10)) {
138                                         reason = buf + 10;
139                                         break;
140                                 }
141                         }
142                         vpninfo->progress(vpninfo, PRG_ERR, "VPN service unavailable; reason: %s\n",
143                                           reason);
144                         return -EINVAL;
145                 }
146                 vpninfo->progress(vpninfo, PRG_ERR,
147                                   "Got inappropriate HTTP CONNECT response: %s\n",
148                                   buf);
149                 if (!strncmp(buf, "HTTP/1.1 401 ", 13))
150                         exit(2);
151                 return -EINVAL;
152         }
153
154         vpninfo->progress(vpninfo, PRG_INFO,
155                           "Got CONNECT response: %s\n", buf);
156
157         /* We may have advertised it, but we only do it if the server agrees */
158         vpninfo->deflate = 0;
159
160         while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
161                 struct vpn_option *new_option;
162                 char *colon = strchr(buf, ':');
163                 if (!colon)
164                         continue;
165
166                 *colon = 0;
167                 colon++;
168                 if (*colon == ' ')
169                         colon++;
170
171                 if (strncmp(buf, "X-DTLS-", 7) &&
172                     strncmp(buf, "X-CSTP-", 7))
173                         continue;
174
175                 new_option = malloc(sizeof(*new_option));
176                 if (!new_option) {
177                         vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
178                         return -ENOMEM;
179                 }
180                 new_option->option = strdup(buf);
181                 new_option->value = strdup(colon);
182                 new_option->next = NULL;
183
184                 if (!new_option->option || !new_option->value) {
185                         vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
186                         return -ENOMEM;
187                 }
188
189                 vpninfo->progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
190
191                 if (!strncmp(buf, "X-DTLS-", 7)) {
192                         *next_dtls_option = new_option;
193                         next_dtls_option = &new_option->next;
194                         continue;
195                 }
196                 /* CSTP options... */
197                 *next_cstp_option = new_option;
198                 next_cstp_option = &new_option->next;
199
200
201                 if (!strcmp(buf + 7, "Keepalive")) {
202                         vpninfo->ssl_times.keepalive = atol(colon);
203                 } else if (!strcmp(buf + 7, "DPD")) {
204                         vpninfo->ssl_times.dpd = atol(colon);
205                 } else if (!strcmp(buf + 7, "Content-Encoding")) {
206                         if (!strcmp(colon, "deflate"))
207                                 vpninfo->deflate = 1;
208                         else {
209                                 vpninfo->progress(vpninfo, PRG_ERR,
210                                         "Unknown CSTP-Content-Encoding %s\n",
211                                         colon);
212                                 return -EINVAL;
213                         }
214                 } else if (!strcmp(buf + 7, "MTU")) {
215                         vpninfo->mtu = atol(colon);
216                 } else if (!strcmp(buf + 7, "Address")) {
217                         vpninfo->vpn_addr = new_option->value;
218                 } else if (!strcmp(buf + 7, "Netmask")) {
219                         vpninfo->vpn_netmask = new_option->value;
220                 } else if (!strcmp(buf + 7, "DNS")) {
221                         int j;
222                         for (j = 0; j < 3; j++) {
223                                 if (!vpninfo->vpn_dns[j]) {
224                                         vpninfo->vpn_dns[j] = new_option->value;
225                                         break;
226                                 }
227                         }
228                 } else if (!strcmp(buf + 7, "NBNS")) {
229                         int j;
230                         for (j = 0; j < 3; j++) {
231                                 if (!vpninfo->vpn_nbns[j]) {
232                                         vpninfo->vpn_nbns[j] = new_option->value;
233                                         break;
234                                 }
235                         }
236                 } else if (!strcmp(buf + 7, "Default-Domain")) {
237                         vpninfo->vpn_domain = new_option->value;
238                 } else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) {
239                         vpninfo->vpn_proxy_pac = new_option->value;
240                 } else if (!strcmp(buf + 7, "Split-Include")) {
241                         struct split_include *inc = malloc(sizeof(*inc));
242                         if (!inc)
243                                 continue;
244                         inc->route = new_option->value;
245                         inc->next = vpninfo->split_includes;
246                         vpninfo->split_includes = inc;
247                 } else if (!strcmp(buf + 7, "Split-Exclude")) {
248                         struct split_include *exc = malloc(sizeof(*exc));
249                         if (!exc)
250                                 continue;
251                         exc->route = new_option->value;
252                         exc->next = vpninfo->split_includes;
253                         vpninfo->split_excludes = exc;
254                 }
255         }
256
257         if (!vpninfo->vpn_addr) {
258                 vpninfo->progress(vpninfo, PRG_ERR, "No IP address received. Aborting\n");
259                 return -EINVAL;
260         }
261         if (!vpninfo->vpn_netmask)
262                 vpninfo->vpn_netmask = "255.255.255.255";
263         if (old_addr) {
264                 if (strcmp(old_addr, vpninfo->vpn_addr)) {
265                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different IP address (%s != %s)\n",
266                                 vpninfo->vpn_addr, old_addr);
267                         return -EINVAL;
268                 }
269         }
270         if (old_netmask) {
271                 if (strcmp(old_netmask, vpninfo->vpn_netmask)) {
272                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different netmask (%s != %s)\n",
273                                 vpninfo->vpn_netmask, old_netmask);
274                         return -EINVAL;
275                 }
276         }
277
278         free(vpninfo->dtls_cipher);
279         vpninfo->dtls_cipher = NULL;
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
506         /* If SSL_write() fails we are expected to try again. With exactly
507            the same data, at exactly the same location. So we keep the
508            packet we had before.... */
509         if (vpninfo->current_ssl_pkt) {
510         handle_outgoing:
511                 vpninfo->ssl_times.last_tx = time(NULL);
512                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
513                 ret = SSL_write(vpninfo->https_ssl,
514                                 vpninfo->current_ssl_pkt->hdr,
515                                 vpninfo->current_ssl_pkt->len + 8);
516                 if (ret <= 0) {
517                         ret = SSL_get_error(vpninfo->https_ssl, ret);
518                         switch (ret) {
519                         case SSL_ERROR_WANT_WRITE:
520                                 /* Waiting for the socket to become writable -- it's
521                                    probably stalled, and/or the buffers are full */
522                                 FD_SET(vpninfo->ssl_fd, &vpninfo->select_wfds);
523
524                         case SSL_ERROR_WANT_READ:
525                                 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
526                                         goto peer_dead;
527                                 return work_done;
528                         default:
529                                 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d", ret);
530                                 ERR_print_errors_fp(stderr);
531                                 vpninfo->quit_reason = "SSL write error";
532                                 return 1;
533                         }
534                 }
535                 if (ret != vpninfo->current_ssl_pkt->len + 8) {
536                         vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
537                                 vpninfo->current_ssl_pkt->len + 8, ret);
538                         vpninfo->quit_reason = "Internal error";
539                         return 1;
540                 }
541                 /* Don't free the 'special' packets */
542                 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
543                     vpninfo->current_ssl_pkt != &dpd_pkt &&
544                     vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
545                     vpninfo->current_ssl_pkt != &keepalive_pkt)
546                         free(vpninfo->current_ssl_pkt);
547
548                 vpninfo->current_ssl_pkt = NULL;
549         }
550
551         if (vpninfo->owe_ssl_dpd_response) {
552                 vpninfo->owe_ssl_dpd_response = 0;
553                 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
554                 goto handle_outgoing;
555         }
556
557         switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
558         case KA_REKEY:
559                 /* Not that this will ever happen; we don't even process
560                    the setting when we're asked for it. */
561                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
562                 time(&vpninfo->ssl_times.last_rekey);
563                 work_done = 1;
564                 break;
565
566         case KA_DPD_DEAD:
567         peer_dead:
568                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
569                 openconnect_close_https(vpninfo);
570
571                 /* It's already deflated in the old stream. Extremely
572                    non-trivial to reconstitute it; just throw it away */
573                 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
574                         vpninfo->current_ssl_pkt = NULL;
575
576                 if (cstp_reconnect(vpninfo)) {
577                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
578                         vpninfo->quit_reason = "SSL DPD detected dead peer; reconnect failed";
579                         return 1;
580                 }
581                 /* I think we can leave DTLS to its own devices; when we reconnect
582                    with the same master secret, we do seem to get the same sessid */
583                 return 1;
584
585         case KA_DPD:
586                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
587
588                 vpninfo->current_ssl_pkt = &dpd_pkt;
589                 goto handle_outgoing;
590
591         case KA_KEEPALIVE:
592                 /* No need to send an explicit keepalive
593                    if we have real data to send */
594                 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
595                         break;
596
597                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
598
599                 vpninfo->current_ssl_pkt = &keepalive_pkt;
600                 goto handle_outgoing;
601
602         case KA_NONE:
603                 ;
604         }
605
606         /* Service outgoing packet queue, if no DTLS */
607         while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
608                 struct pkt *this = vpninfo->outgoing_queue;
609                 vpninfo->outgoing_queue = this->next;
610                 vpninfo->outgoing_qlen--;
611
612                 if (vpninfo->deflate) {
613                         unsigned char *adler;
614                         int ret;
615
616                         vpninfo->deflate_strm.next_in = this->data;
617                         vpninfo->deflate_strm.avail_in = this->len;
618                         vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
619                         vpninfo->deflate_strm.avail_out = 2040;
620                         vpninfo->deflate_strm.total_out = 0;
621
622                         ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
623                         if (ret) {
624                                 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
625                                 goto uncompr;
626                         }
627
628                         vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
629                         vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
630
631                         /* Add ongoing adler32 to tail of compressed packet */
632                         vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
633                                                            this->data, this->len);
634
635                         adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
636                         *(adler++) =  vpninfo->deflate_adler32 >> 24;
637                         *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
638                         *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
639                         *(adler)   =  vpninfo->deflate_adler32 & 0xff;
640
641                         vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
642
643                         vpninfo->progress(vpninfo, PRG_TRACE,
644                                           "Sending compressed data packet of %d bytes\n",
645                                           this->len);
646
647                         vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
648                 } else {
649                 uncompr:
650                         memcpy(this->hdr, data_hdr, 8);
651                         this->hdr[4] = this->len >> 8;
652                         this->hdr[5] = this->len & 0xff;
653
654                         vpninfo->progress(vpninfo, PRG_TRACE,
655                                           "Sending uncompressed data packet of %d bytes\n",
656                                           this->len);
657
658                         vpninfo->current_ssl_pkt = this;
659                 }
660                 goto handle_outgoing;
661         }
662
663         /* Work is not done if we just got rid of packets off the queue */
664         return work_done;
665 }
666
667 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
668 {
669         unsigned char *bye_pkt;
670         int reason_len;
671
672         /* already lost connection? */
673         if (!vpninfo->https_ssl)
674                 return 0;
675
676         reason_len = strlen(reason);
677         bye_pkt = malloc(reason_len + 8);
678         if (!bye_pkt)
679                 return -ENOMEM;
680
681         memcpy(bye_pkt, data_hdr, 8);
682         memcpy(bye_pkt + 8, reason, reason_len);
683
684         bye_pkt[4] = reason_len >> 8;
685         bye_pkt[5] = reason_len & 0xff;
686         bye_pkt[6] = AC_PKT_DISCONN;
687
688         SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
689         free(bye_pkt);
690
691         vpninfo->progress(vpninfo, PRG_INFO,
692                           "Send BYE packet: %s\n", reason);
693
694         return 0;
695 }