Handle disconnect request gracefully
[platform/upstream/openconnect.git] / cstp.c
1 /*
2  * Open AnyConnect (SSL + DTLS) client
3  *
4  * © 2008 David Woodhouse <dwmw2@infradead.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1 of
9  * the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to:
18  *
19  *   Free Software Foundation, Inc.
20  *   51 Franklin Street, Fifth Floor,
21  *   Boston, MA 02110-1301 USA
22  */
23 #include <netdb.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <time.h>
27
28 #include <openssl/ssl.h>
29 #include <openssl/err.h>
30
31 #include "openconnect.h"
32
33 /*
34  * Data packets are encapsulated in the SSL stream as follows:
35  * 
36  * 0000: Magic "STF\x1"
37  * 0004: Big-endian 16-bit length (not including 8-byte header)
38  * 0006: Byte packet type (see openconnect.h)
39  * 0008: data payload
40  */
41
42 static char data_hdr[8] = {
43         'S', 'T', 'F', 1,
44         0, 0,           /* Length */
45         AC_PKT_DATA,    /* Type */
46         0               /* Unknown */
47 };
48
49 static struct pkt keepalive_pkt = {
50         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_KEEPALIVE, 0 },
51 };
52
53 static struct pkt dpd_pkt = {
54         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_OUT, 0 },
55 };
56
57 static struct pkt dpd_resp_pkt = {
58         .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_RESP, 0 },
59 };
60
61
62 static int start_cstp_connection(struct openconnect_info *vpninfo)
63 {
64         char buf[65536];
65         int i;
66         int retried = 0;
67         struct vpn_option **next_dtls_option = &vpninfo->dtls_options;
68         struct vpn_option **next_cstp_option = &vpninfo->cstp_options;
69         struct vpn_option *old_cstp_opts = vpninfo->cstp_options;
70         struct vpn_option *old_dtls_opts = vpninfo->dtls_options;
71         const char *old_addr = vpninfo->vpn_addr;
72         const char *old_netmask = vpninfo->vpn_netmask;
73
74         /* Clear old options which will be overwritten */
75         vpninfo->vpn_addr = vpninfo->vpn_netmask = NULL;
76         vpninfo->cstp_options = vpninfo->dtls_options = NULL;
77         for (i=0; i<3; i++)
78                 vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
79
80  retry:
81         openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
82         openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
83         openconnect_SSL_printf(vpninfo->https_ssl, "User-Agent: %s\r\n", vpninfo->useragent);
84         openconnect_SSL_printf(vpninfo->https_ssl, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
85         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Version: 1\r\n");
86         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
87         if (vpninfo->deflate)
88                 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
89         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
90         /* To enable IPv6, send 'IPv6,IPv4'.
91            We don't know how most of that works yet though. */
92         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Address-Type: IPv4\r\n");
93         openconnect_SSL_printf(vpninfo->https_ssl, "X-DTLS-Master-Secret: ");
94         for (i = 0; i < sizeof(vpninfo->dtls_secret); i++)
95                 openconnect_SSL_printf(vpninfo->https_ssl, "%02X", vpninfo->dtls_secret[i]);
96         openconnect_SSL_printf(vpninfo->https_ssl, "\r\nX-DTLS-CipherSuite: AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA\r\n\r\n");
97
98         if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
99                 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTPS response\n");
100                 if (!retried) {
101                         retried = 1;
102                         SSL_free(vpninfo->https_ssl);
103                         close(vpninfo->ssl_fd);
104                 
105                         if (openconnect_open_https(vpninfo)) {
106                                 vpninfo->progress(vpninfo, PRG_ERR,
107                                                   "Failed to open HTTPS connection to %s\n",
108                                                   vpninfo->hostname);
109                                 exit(1);
110                         }
111                         goto retry;
112                 }
113                 return -EINVAL;
114         }
115
116         if (strncmp(buf, "HTTP/1.1 200 ", 13)) {
117                 vpninfo->progress(vpninfo, PRG_ERR, 
118                                   "Got inappropriate HTTP CONNECT response: %s\n",
119                                   buf);
120                 if (!strncmp(buf, "HTTP/1.1 401 ", 13))
121                         exit(2);
122                 openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536);
123                 return -EINVAL;
124         }
125
126         vpninfo->progress(vpninfo, PRG_INFO,
127                           "Got CONNECT response: %s\n", buf);
128
129         /* We may have advertised it, but we only do it if the server agrees */
130         vpninfo->deflate = 0;
131
132         while ((i=openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
133                 struct vpn_option *new_option;
134                 char *colon = strchr(buf, ':');
135                 if (!colon)
136                         continue;
137
138                 *colon = 0;
139                 colon++;
140                 if (*colon == ' ')
141                         colon++;
142
143                 if (strncmp(buf, "X-DTLS-", 7) &&
144                     strncmp(buf, "X-CSTP-", 7))
145                         continue;
146
147                 new_option = malloc(sizeof(*new_option));
148                 if (!new_option) {
149                         vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
150                         return -ENOMEM;
151                 }
152                 new_option->option = strdup(buf);
153                 new_option->value = strdup(colon);
154                 new_option->next = NULL;
155
156                 if (!new_option->option || !new_option->value) {
157                         vpninfo->progress(vpninfo, PRG_ERR, "No memory for options\n");
158                         return -ENOMEM;
159                 }
160
161                 vpninfo->progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
162
163                 if (!strncmp(buf, "X-DTLS-", 7)) {
164                         *next_dtls_option = new_option;
165                         next_dtls_option = &new_option->next;
166                         continue;
167                 }
168                 /* CSTP options... */
169                 *next_cstp_option = new_option;
170                 next_cstp_option = &new_option->next;
171
172
173                 if (!strcmp(buf + 7, "Keepalive")) {
174                         vpninfo->ssl_times.keepalive = atol(colon);
175                 } else if (!strcmp(buf + 7, "DPD")) {
176                         vpninfo->ssl_times.dpd = atol(colon);
177                 } else if (!strcmp(buf + 7, "Content-Encoding")) {
178                         if (!strcmp(colon, "deflate"))
179                                 vpninfo->deflate = 1;
180                         else {
181                                 vpninfo->progress(vpninfo, PRG_ERR, 
182                                         "Unknown CSTP-Content-Encoding %s\n",
183                                         colon);
184                                 return -EINVAL;
185                         }
186                 } else if (!strcmp(buf + 7, "MTU")) {
187                         vpninfo->mtu = atol(colon);
188                 } else if (!strcmp(buf + 7, "Address")) {
189                         vpninfo->vpn_addr = new_option->value;
190                 } else if (!strcmp(buf + 7, "Netmask")) {
191                         vpninfo->vpn_netmask = new_option->value;
192                 } else if (!strcmp(buf + 7, "DNS")) {
193                         int j;
194                         for (j = 0; j < 3; j++) {
195                                 if (!vpninfo->vpn_dns[j]) {
196                                         vpninfo->vpn_dns[j] = new_option->value;
197                                         break;
198                                 }
199                         }
200                 } else if (!strcmp(buf + 7, "NBNS")) {
201                         int j;
202                         for (j = 0; j < 3; j++) {
203                                 if (!vpninfo->vpn_nbns[j]) {
204                                         vpninfo->vpn_nbns[j] = new_option->value;
205                                         break;
206                                 }
207                         }
208                 } else if (!strcmp(buf + 7, "Default-Domain")) {
209                         vpninfo->vpn_domain = new_option->value;
210                 }
211         }
212
213         if (!vpninfo->vpn_addr) {
214                 vpninfo->progress(vpninfo, PRG_ERR, "No IP address received. Aborting\n");
215                 return -EINVAL;
216         }
217         if (!vpninfo->vpn_netmask)
218                 vpninfo->vpn_netmask = "255.255.255.255";
219         if (old_addr) {
220                 if (strcmp(old_addr, vpninfo->vpn_addr)) {
221                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different IP address (%s != %s)\n",
222                                 vpninfo->vpn_addr, old_addr);
223                         return -EINVAL;
224                 }
225         }
226         if (old_netmask) {
227                 if (strcmp(old_netmask, vpninfo->vpn_netmask)) {
228                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect gave different netmask (%s != %s)\n",
229                                 vpninfo->vpn_netmask, old_netmask);
230                         return -EINVAL;
231                 }
232         }
233
234         while (old_dtls_opts) {
235                 struct vpn_option *tmp = old_dtls_opts;
236                 old_dtls_opts = old_dtls_opts->next;
237                 free(tmp->value);
238                 free(tmp->option);
239                 free(tmp);
240         }
241         while (old_cstp_opts) {
242                 struct vpn_option *tmp = old_cstp_opts;
243                 old_cstp_opts = old_cstp_opts->next;
244                 free(tmp->value);
245                 free(tmp->option);
246                 free(tmp);
247         }
248         vpninfo->progress(vpninfo, PRG_INFO, "CSTP connected. DPD %d, Keepalive %d\n",
249                           vpninfo->ssl_times.dpd, vpninfo->ssl_times.keepalive);
250
251         BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl),1);
252         BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl),1);
253
254         fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK);
255         vpninfo->ssl_pfd = vpn_add_pollfd(vpninfo, vpninfo->ssl_fd, POLLIN|POLLHUP|POLLERR);
256
257         vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
258         return 0;
259 }
260
261
262 int make_cstp_connection(struct openconnect_info *vpninfo)
263 {
264         if (!vpninfo->https_ssl && openconnect_open_https(vpninfo))
265                 exit(1);
266
267         if (vpninfo->deflate) {
268                 vpninfo->deflate_adler32 = 1;
269                 vpninfo->inflate_adler32 = 1;
270
271                 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
272                     deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
273                                  Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
274                         vpninfo->progress(vpninfo, PRG_ERR, "Compression setup failed\n");
275                         vpninfo->deflate = 0;
276                 }
277
278                 if (!vpninfo->deflate_pkt) {
279                         vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
280                         if (!vpninfo->deflate_pkt) {
281                                 vpninfo->progress(vpninfo, PRG_ERR, "Allocation of deflate buffer failed\n");
282                                 vpninfo->deflate = 0;
283                         }
284                         memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
285                         memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
286                         vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
287                 }
288         }
289
290         if (start_cstp_connection(vpninfo))
291                 return -EINVAL;
292
293         return 0;
294 }
295
296
297 static int inflate_and_queue_packet(struct openconnect_info *vpninfo, int type, void *buf, int len)
298 {
299         struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
300
301         if (!new)
302                 return -ENOMEM;
303
304         new->type = type;
305         new->next = NULL;
306
307         vpninfo->inflate_strm.next_in = buf;
308         vpninfo->inflate_strm.avail_in = len - 4;
309
310         vpninfo->inflate_strm.next_out = new->data;
311         vpninfo->inflate_strm.avail_out = vpninfo->mtu;
312         vpninfo->inflate_strm.total_out = 0;
313
314         if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
315                 vpninfo->progress(vpninfo, PRG_ERR, "inflate failed\n");
316                 free(new);
317                 return -EINVAL;
318         }
319
320         new->len = vpninfo->inflate_strm.total_out;
321
322         vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
323                                            new->data, new->len);
324
325         if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *)(buf + len - 4))) {
326                 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
327         }
328
329         vpninfo->progress(vpninfo, PRG_TRACE,
330                           "Received compressed data packet of %ld bytes\n",
331                           vpninfo->inflate_strm.total_out);
332
333         queue_packet(&vpninfo->incoming_queue, new);
334         return 0;
335 }
336
337 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
338 {
339         unsigned char buf[16384];
340         int len, ret;
341         int work_done = 0;
342
343         /* FIXME: The poll() handling here is fairly simplistic. Actually,
344            if the SSL connection stalls it could return a WANT_WRITE error
345            on _either_ of the SSL_read() or SSL_write() calls. In that case,
346            we should probably remove POLLIN from the events we're looking for,
347            and add POLLOUT. As it is, though, it'll just chew CPU time in that
348            fairly unlikely situation, until the write backlog clears. */
349         while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
350                 int payload_len;
351
352                 if (buf[0] != 'S' || buf[1] != 'T' ||
353                     buf[2] != 'F' || buf[3] != 1 || buf[7])
354                         goto unknown_pkt;
355
356                 payload_len = (buf[4] << 8) + buf[5];
357                 if (len != 8 + payload_len) {
358                         vpninfo->progress(vpninfo, PRG_ERR,
359                                           "Unexpected packet length. SSL_read returned %d but packet is\n",
360                                           len);
361                         vpninfo->progress(vpninfo, PRG_ERR,
362                                           "%02x %02x %02x %02x %02x %02x %02x %02x\n",
363                                           buf[0], buf[1], buf[2], buf[3],
364                                           buf[4], buf[5], buf[6], buf[7]);
365                         continue;
366                 }
367                 vpninfo->ssl_times.last_rx = time(NULL);
368                 switch(buf[6]) {
369                 case AC_PKT_DPD_OUT:
370                         vpninfo->progress(vpninfo, PRG_TRACE,
371                                           "Got CSTP DPD request\n");
372                         vpninfo->owe_ssl_dpd_response = 1;
373                         continue;
374
375                 case AC_PKT_DPD_RESP:
376                         vpninfo->progress(vpninfo, PRG_TRACE,
377                                           "Got CSTP DPD response\n");
378                         continue;
379
380                 case AC_PKT_KEEPALIVE:
381                         vpninfo->progress(vpninfo, PRG_TRACE,
382                                           "Got CSTP Keepalive\n");
383                         continue;
384
385                 case AC_PKT_DATA:
386                         vpninfo->progress(vpninfo, PRG_TRACE,
387                                           "Received uncompressed data packet of %d bytes\n",
388                                           payload_len);
389                         queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
390                                          payload_len);
391                         work_done = 1;
392                         continue;
393
394                 case AC_PKT_DISCONN: {
395                         int i;
396                         for (i = 0; i < payload_len; i++) {
397                                 if (!isprint(buf[payload_len + 8 + i]))
398                                         buf[payload_len + 8 + i] = '.';
399                         }
400                         buf[payload_len + 8] = 0;
401                         vpninfo->progress(vpninfo, PRG_ERR,
402                                           "Received server disconnect: '%s'\n", buf + 8);
403                         vpninfo->quit_reason = "Server request";
404                         return 1;
405                 }
406                 case AC_PKT_COMPRESSED:
407                         if (!vpninfo->deflate) {
408                                 vpninfo->progress(vpninfo, PRG_ERR, "Compressed packet received in !deflate mode\n");
409                                 goto unknown_pkt;
410                         }
411                         inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
412                         work_done = 1;
413                         continue;
414
415                 case AC_PKT_TERM_SERVER:
416                         vpninfo->progress(vpninfo, PRG_ERR, "received server terminate packet\n");
417                         vpninfo->quit_reason = "Server request";
418                         return 1;
419                 }
420
421         unknown_pkt:
422                 vpninfo->progress(vpninfo, PRG_ERR,
423                                   "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
424                                   buf[0], buf[1], buf[2], buf[3],
425                                   buf[4], buf[5], buf[6], buf[7]);
426                 vpninfo->quit_reason = "Unknown packet received";
427                 return 1;
428         }
429
430
431         /* If SSL_write() fails we are expected to try again. With exactly
432            the same data, at exactly the same location. So we keep the 
433            packet we had before.... */
434         if (vpninfo->current_ssl_pkt) {
435         handle_outgoing:
436                 vpninfo->ssl_times.last_tx = time(NULL);
437                 vpninfo->pfds[vpninfo->ssl_pfd].events &= ~POLLOUT;
438                 ret = SSL_write(vpninfo->https_ssl,
439                                 vpninfo->current_ssl_pkt->hdr,
440                                 vpninfo->current_ssl_pkt->len + 8);
441                 if (ret <= 0) {
442                         ret = SSL_get_error(vpninfo->https_ssl, ret);
443                         switch (ret) {
444                         case SSL_ERROR_WANT_WRITE:
445                                 /* Waiting for the socket to become writable -- it's
446                                    probably stalled, and/or the buffers are full */
447                                 vpninfo->pfds[vpninfo->ssl_pfd].events |= POLLOUT;
448                         case SSL_ERROR_WANT_READ:
449                                 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
450                                         goto peer_dead;
451                                 return work_done;
452                         default:
453                                 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d", ret);
454                                 ERR_print_errors_fp(stderr);
455                                 vpninfo->quit_reason = "SSL write error";
456                                 return 1;
457                         }
458                 }
459                 if (ret != vpninfo->current_ssl_pkt->len + 8) {
460                         vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
461                                 vpninfo->current_ssl_pkt->len + 8, ret);
462                         vpninfo->quit_reason = "Internal error";
463                         return 1;
464                 }
465                 /* Don't free the 'special' packets */
466                 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
467                     vpninfo->current_ssl_pkt != &dpd_pkt &&
468                     vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
469                     vpninfo->current_ssl_pkt != &keepalive_pkt)
470                         free(vpninfo->current_ssl_pkt);
471
472                 vpninfo->current_ssl_pkt = NULL;
473         }
474
475         if (vpninfo->owe_ssl_dpd_response) {
476                 vpninfo->owe_ssl_dpd_response = 0;
477                 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
478                 goto handle_outgoing;
479         }
480
481         switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
482         case KA_REKEY:
483                 /* Not that this will ever happen; we don't even process
484                    the setting when we're asked for it. */
485                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
486                 time(&vpninfo->ssl_times.last_rekey);
487                 work_done = 1;
488                 break;
489
490         case KA_DPD_DEAD:
491         peer_dead:
492                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
493                 SSL_free(vpninfo->https_ssl);
494                 vpninfo->https_ssl = NULL;
495                 close(vpninfo->ssl_fd);
496
497                 /* It's already deflated in the old stream. Extremely 
498                    non-trivial to reconstitute it; just throw it away */
499                 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
500                         vpninfo->current_ssl_pkt = NULL;
501
502                 if (make_cstp_connection(vpninfo)) {
503                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
504                         vpninfo->quit_reason = "SSL DPD detected dead peer; reconnect failed";
505                         return 1;
506                 }
507                 /* I think we can leave DTLS to its own devices; when we reconnect
508                    with the same master secret, we do seem to get the same sessid */
509                 return 1;
510
511         case KA_DPD:
512                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
513
514                 vpninfo->current_ssl_pkt = &dpd_pkt;
515                 goto handle_outgoing;
516
517         case KA_KEEPALIVE:
518                 /* No need to send an explicit keepalive
519                    if we have real data to send */
520                 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
521                         break;
522
523                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
524
525                 vpninfo->current_ssl_pkt = &keepalive_pkt;
526                 goto handle_outgoing;
527
528         case KA_NONE:
529                 ;
530         }
531
532         /* Service outgoing packet queue, if no DTLS */
533         while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
534                 struct pkt *this = vpninfo->outgoing_queue;
535                 vpninfo->outgoing_queue = this->next;
536
537                 if (vpninfo->deflate) {
538                         unsigned char *adler;
539                         int ret;
540
541                         vpninfo->deflate_strm.next_in = this->data;
542                         vpninfo->deflate_strm.avail_in = this->len;
543                         vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
544                         vpninfo->deflate_strm.avail_out = 2040;
545                         vpninfo->deflate_strm.total_out = 0;
546
547                         ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
548                         if (ret) {
549                                 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
550                                 goto uncompr;
551                         }
552
553                         vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
554                         vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
555
556                         /* Add ongoing adler32 to tail of compressed packet */
557                         vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
558                                                            this->data, this->len);
559
560                         adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
561                         *(adler++) =  vpninfo->deflate_adler32 >> 24;
562                         *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
563                         *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
564                         *(adler)   =  vpninfo->deflate_adler32 & 0xff;
565
566                         vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
567
568                         vpninfo->progress(vpninfo, PRG_TRACE,
569                                           "Sending compressed data packet of %d bytes\n",
570                                           this->len);
571
572                         vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
573                 } else {
574                 uncompr:
575                         memcpy(this->hdr, data_hdr, 8);
576                         this->hdr[4] = this->len >> 8;
577                         this->hdr[5] = this->len & 0xff;
578
579                         vpninfo->progress(vpninfo, PRG_TRACE,
580                                           "Sending uncompressed data packet of %d bytes\n",
581                                           this->len);
582
583                         vpninfo->current_ssl_pkt = this;
584                 }
585                 goto handle_outgoing;
586         }
587
588         /* Work is not done if we just got rid of packets off the queue */
589         return work_done;
590 }
591
592 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
593 {
594         unsigned char *bye_pkt;
595         int reason_len = strlen(reason);
596         bye_pkt = malloc(reason_len + 8);
597         if (!bye_pkt)
598                 return -ENOMEM;
599         
600         memcpy(bye_pkt, data_hdr, 8);
601         memcpy(bye_pkt + 8, reason, strlen(reason));
602
603         bye_pkt[4] = reason_len >> 8;
604         bye_pkt[5] = reason_len & 0xff;
605         bye_pkt[6] = AC_PKT_DISCONN;
606
607         SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
608         free(bye_pkt);
609
610         vpninfo->progress(vpninfo, PRG_TRACE,
611                           "Send BYE packet: %s\n", reason);
612
613         return 0;
614 }