Whitespace cleanups
[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         while (old_dtls_opts) {
262                 struct vpn_option *tmp = old_dtls_opts;
263                 old_dtls_opts = old_dtls_opts->next;
264                 free(tmp->value);
265                 free(tmp->option);
266                 free(tmp);
267         }
268         while (old_cstp_opts) {
269                 struct vpn_option *tmp = old_cstp_opts;
270                 old_cstp_opts = old_cstp_opts->next;
271                 free(tmp->value);
272                 free(tmp->option);
273                 free(tmp);
274         }
275         vpninfo->progress(vpninfo, PRG_INFO, "CSTP connected. DPD %d, Keepalive %d\n",
276                           vpninfo->ssl_times.dpd, vpninfo->ssl_times.keepalive);
277
278         BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl), 1);
279         BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl), 1);
280
281         fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK);
282         if (vpninfo->select_nfds <= vpninfo->ssl_fd)
283                 vpninfo->select_nfds = vpninfo->ssl_fd + 1;
284
285         FD_SET(vpninfo->ssl_fd, &vpninfo->select_rfds);
286         FD_SET(vpninfo->ssl_fd, &vpninfo->select_efds);
287
288         vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
289         return 0;
290 }
291
292
293 int make_cstp_connection(struct openconnect_info *vpninfo)
294 {
295         int ret;
296
297         if (!vpninfo->https_ssl && (ret = openconnect_open_https(vpninfo)))
298                 return ret;
299
300         if (vpninfo->deflate) {
301                 vpninfo->deflate_adler32 = 1;
302                 vpninfo->inflate_adler32 = 1;
303
304                 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
305                     deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
306                                  Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
307                         vpninfo->progress(vpninfo, PRG_ERR, "Compression setup failed\n");
308                         vpninfo->deflate = 0;
309                 }
310
311                 if (!vpninfo->deflate_pkt) {
312                         vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
313                         if (!vpninfo->deflate_pkt) {
314                                 vpninfo->progress(vpninfo, PRG_ERR, "Allocation of deflate buffer failed\n");
315                                 vpninfo->deflate = 0;
316                         }
317                         memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
318                         memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
319                         vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
320                 }
321         }
322
323         return start_cstp_connection(vpninfo);
324 }
325
326 static int cstp_reconnect(struct openconnect_info *vpninfo)
327 {
328         int ret;
329         int timeout;
330         int interval;
331
332         timeout = vpninfo->reconnect_timeout;
333         interval = vpninfo->reconnect_interval;
334
335         while ((ret = make_cstp_connection(vpninfo))) {
336                 if (timeout <= 0)
337                         return ret;
338                 vpninfo->progress(vpninfo, PRG_INFO,
339                                   "sleep %ds, remaining timeout %ds\n",
340                                   interval, timeout);
341                 sleep(interval);
342                 if (killed)
343                         return 1;
344                 timeout -= interval;
345                 interval += vpninfo->reconnect_interval;
346                 if (interval > RECONNECT_INTERVAL_MAX)
347                         interval = RECONNECT_INTERVAL_MAX;
348         }
349         return 0;
350 }
351
352 static int inflate_and_queue_packet(struct openconnect_info *vpninfo, int type, void *buf, int len)
353 {
354         struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
355
356         if (!new)
357                 return -ENOMEM;
358
359         new->type = type;
360         new->next = NULL;
361
362         vpninfo->inflate_strm.next_in = buf;
363         vpninfo->inflate_strm.avail_in = len - 4;
364
365         vpninfo->inflate_strm.next_out = new->data;
366         vpninfo->inflate_strm.avail_out = vpninfo->mtu;
367         vpninfo->inflate_strm.total_out = 0;
368
369         if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
370                 vpninfo->progress(vpninfo, PRG_ERR, "inflate failed\n");
371                 free(new);
372                 return -EINVAL;
373         }
374
375         new->len = vpninfo->inflate_strm.total_out;
376
377         vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
378                                            new->data, new->len);
379
380         if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *) (buf + len - 4) )) {
381                 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
382         }
383
384         vpninfo->progress(vpninfo, PRG_TRACE,
385                           "Received compressed data packet of %ld bytes\n",
386                           vpninfo->inflate_strm.total_out);
387
388         queue_packet(&vpninfo->incoming_queue, new);
389         return 0;
390 }
391
392 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
393 {
394         unsigned char buf[16384];
395         int len, ret;
396         int work_done = 0;
397
398         /* FIXME: The poll() handling here is fairly simplistic. Actually,
399            if the SSL connection stalls it could return a WANT_WRITE error
400            on _either_ of the SSL_read() or SSL_write() calls. In that case,
401            we should probably remove POLLIN from the events we're looking for,
402            and add POLLOUT. As it is, though, it'll just chew CPU time in that
403            fairly unlikely situation, until the write backlog clears. */
404         while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
405                 int payload_len;
406
407                 if (buf[0] != 'S' || buf[1] != 'T' ||
408                     buf[2] != 'F' || buf[3] != 1 || buf[7])
409                         goto unknown_pkt;
410
411                 payload_len = (buf[4] << 8) + buf[5];
412                 if (len != 8 + payload_len) {
413                         vpninfo->progress(vpninfo, PRG_ERR,
414                                           "Unexpected packet length. SSL_read returned %d but packet is\n",
415                                           len);
416                         vpninfo->progress(vpninfo, PRG_ERR,
417                                           "%02x %02x %02x %02x %02x %02x %02x %02x\n",
418                                           buf[0], buf[1], buf[2], buf[3],
419                                           buf[4], buf[5], buf[6], buf[7]);
420                         continue;
421                 }
422                 vpninfo->ssl_times.last_rx = time(NULL);
423                 switch(buf[6]) {
424                 case AC_PKT_DPD_OUT:
425                         vpninfo->progress(vpninfo, PRG_TRACE,
426                                           "Got CSTP DPD request\n");
427                         vpninfo->owe_ssl_dpd_response = 1;
428                         continue;
429
430                 case AC_PKT_DPD_RESP:
431                         vpninfo->progress(vpninfo, PRG_TRACE,
432                                           "Got CSTP DPD response\n");
433                         continue;
434
435                 case AC_PKT_KEEPALIVE:
436                         vpninfo->progress(vpninfo, PRG_TRACE,
437                                           "Got CSTP Keepalive\n");
438                         continue;
439
440                 case AC_PKT_DATA:
441                         vpninfo->progress(vpninfo, PRG_TRACE,
442                                           "Received uncompressed data packet of %d bytes\n",
443                                           payload_len);
444                         queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
445                                          payload_len);
446                         work_done = 1;
447                         continue;
448
449                 case AC_PKT_DISCONN: {
450                         int i;
451                         for (i = 0; i < payload_len; i++) {
452                                 if (!isprint(buf[payload_len + 8 + i]))
453                                         buf[payload_len + 8 + i] = '.';
454                         }
455                         buf[payload_len + 8] = 0;
456                         vpninfo->progress(vpninfo, PRG_ERR,
457                                           "Received server disconnect: '%s'\n", buf + 8);
458                         vpninfo->quit_reason = "Server request";
459                         return 1;
460                 }
461                 case AC_PKT_COMPRESSED:
462                         if (!vpninfo->deflate) {
463                                 vpninfo->progress(vpninfo, PRG_ERR, "Compressed packet received in !deflate mode\n");
464                                 goto unknown_pkt;
465                         }
466                         inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
467                         work_done = 1;
468                         continue;
469
470                 case AC_PKT_TERM_SERVER:
471                         vpninfo->progress(vpninfo, PRG_ERR, "received server terminate packet\n");
472                         vpninfo->quit_reason = "Server request";
473                         return 1;
474                 }
475
476         unknown_pkt:
477                 vpninfo->progress(vpninfo, PRG_ERR,
478                                   "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
479                                   buf[0], buf[1], buf[2], buf[3],
480                                   buf[4], buf[5], buf[6], buf[7]);
481                 vpninfo->quit_reason = "Unknown packet received";
482                 return 1;
483         }
484
485
486         /* If SSL_write() fails we are expected to try again. With exactly
487            the same data, at exactly the same location. So we keep the
488            packet we had before.... */
489         if (vpninfo->current_ssl_pkt) {
490         handle_outgoing:
491                 vpninfo->ssl_times.last_tx = time(NULL);
492                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
493                 ret = SSL_write(vpninfo->https_ssl,
494                                 vpninfo->current_ssl_pkt->hdr,
495                                 vpninfo->current_ssl_pkt->len + 8);
496                 if (ret <= 0) {
497                         ret = SSL_get_error(vpninfo->https_ssl, ret);
498                         switch (ret) {
499                         case SSL_ERROR_WANT_WRITE:
500                                 /* Waiting for the socket to become writable -- it's
501                                    probably stalled, and/or the buffers are full */
502                                 FD_SET(vpninfo->ssl_fd, &vpninfo->select_wfds);
503
504                         case SSL_ERROR_WANT_READ:
505                                 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
506                                         goto peer_dead;
507                                 return work_done;
508                         default:
509                                 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d", ret);
510                                 ERR_print_errors_fp(stderr);
511                                 vpninfo->quit_reason = "SSL write error";
512                                 return 1;
513                         }
514                 }
515                 if (ret != vpninfo->current_ssl_pkt->len + 8) {
516                         vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
517                                 vpninfo->current_ssl_pkt->len + 8, ret);
518                         vpninfo->quit_reason = "Internal error";
519                         return 1;
520                 }
521                 /* Don't free the 'special' packets */
522                 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
523                     vpninfo->current_ssl_pkt != &dpd_pkt &&
524                     vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
525                     vpninfo->current_ssl_pkt != &keepalive_pkt)
526                         free(vpninfo->current_ssl_pkt);
527
528                 vpninfo->current_ssl_pkt = NULL;
529         }
530
531         if (vpninfo->owe_ssl_dpd_response) {
532                 vpninfo->owe_ssl_dpd_response = 0;
533                 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
534                 goto handle_outgoing;
535         }
536
537         switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
538         case KA_REKEY:
539                 /* Not that this will ever happen; we don't even process
540                    the setting when we're asked for it. */
541                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
542                 time(&vpninfo->ssl_times.last_rekey);
543                 work_done = 1;
544                 break;
545
546         case KA_DPD_DEAD:
547         peer_dead:
548                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
549                 openconnect_close_https(vpninfo);
550
551                 /* It's already deflated in the old stream. Extremely
552                    non-trivial to reconstitute it; just throw it away */
553                 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
554                         vpninfo->current_ssl_pkt = NULL;
555
556                 if (cstp_reconnect(vpninfo)) {
557                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
558                         vpninfo->quit_reason = "SSL DPD detected dead peer; reconnect failed";
559                         return 1;
560                 }
561                 /* I think we can leave DTLS to its own devices; when we reconnect
562                    with the same master secret, we do seem to get the same sessid */
563                 return 1;
564
565         case KA_DPD:
566                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
567
568                 vpninfo->current_ssl_pkt = &dpd_pkt;
569                 goto handle_outgoing;
570
571         case KA_KEEPALIVE:
572                 /* No need to send an explicit keepalive
573                    if we have real data to send */
574                 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
575                         break;
576
577                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
578
579                 vpninfo->current_ssl_pkt = &keepalive_pkt;
580                 goto handle_outgoing;
581
582         case KA_NONE:
583                 ;
584         }
585
586         /* Service outgoing packet queue, if no DTLS */
587         while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
588                 struct pkt *this = vpninfo->outgoing_queue;
589                 vpninfo->outgoing_queue = this->next;
590                 vpninfo->outgoing_qlen--;
591
592                 if (vpninfo->deflate) {
593                         unsigned char *adler;
594                         int ret;
595
596                         vpninfo->deflate_strm.next_in = this->data;
597                         vpninfo->deflate_strm.avail_in = this->len;
598                         vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
599                         vpninfo->deflate_strm.avail_out = 2040;
600                         vpninfo->deflate_strm.total_out = 0;
601
602                         ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
603                         if (ret) {
604                                 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
605                                 goto uncompr;
606                         }
607
608                         vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
609                         vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
610
611                         /* Add ongoing adler32 to tail of compressed packet */
612                         vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
613                                                            this->data, this->len);
614
615                         adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
616                         *(adler++) =  vpninfo->deflate_adler32 >> 24;
617                         *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
618                         *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
619                         *(adler)   =  vpninfo->deflate_adler32 & 0xff;
620
621                         vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
622
623                         vpninfo->progress(vpninfo, PRG_TRACE,
624                                           "Sending compressed data packet of %d bytes\n",
625                                           this->len);
626
627                         vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
628                 } else {
629                 uncompr:
630                         memcpy(this->hdr, data_hdr, 8);
631                         this->hdr[4] = this->len >> 8;
632                         this->hdr[5] = this->len & 0xff;
633
634                         vpninfo->progress(vpninfo, PRG_TRACE,
635                                           "Sending uncompressed data packet of %d bytes\n",
636                                           this->len);
637
638                         vpninfo->current_ssl_pkt = this;
639                 }
640                 goto handle_outgoing;
641         }
642
643         /* Work is not done if we just got rid of packets off the queue */
644         return work_done;
645 }
646
647 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
648 {
649         unsigned char *bye_pkt;
650         int reason_len;
651
652         /* already lost connection? */
653         if (!vpninfo->https_ssl)
654                 return 0;
655
656         reason_len = strlen(reason);
657         bye_pkt = malloc(reason_len + 8);
658         if (!bye_pkt)
659                 return -ENOMEM;
660
661         memcpy(bye_pkt, data_hdr, 8);
662         memcpy(bye_pkt + 8, reason, reason_len);
663
664         bye_pkt[4] = reason_len >> 8;
665         bye_pkt[5] = reason_len & 0xff;
666         bye_pkt[6] = AC_PKT_DISCONN;
667
668         SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
669         free(bye_pkt);
670
671         vpninfo->progress(vpninfo, PRG_INFO,
672                           "Send BYE packet: %s\n", reason);
673
674         return 0;
675 }