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