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