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