Move to using select() instead of poll(). poll() doesn't work on MacOS
[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
75         /* Clear old options which will be overwritten */
76         vpninfo->vpn_addr = vpninfo->vpn_netmask = NULL;
77         vpninfo->cstp_options = vpninfo->dtls_options = NULL;
78         for (i=0; i<3; i++)
79                 vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
80
81  retry:
82         openconnect_SSL_printf(vpninfo->https_ssl, "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
83         openconnect_SSL_printf(vpninfo->https_ssl, "Host: %s\r\n", vpninfo->hostname);
84         openconnect_SSL_printf(vpninfo->https_ssl, "User-Agent: %s\r\n", vpninfo->useragent);
85         openconnect_SSL_printf(vpninfo->https_ssl, "Cookie: webvpn=%s\r\n", vpninfo->cookie);
86         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Version: 1\r\n");
87         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
88         if (vpninfo->deflate)
89                 openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
90         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
91         /* To enable IPv6, send 'IPv6,IPv4'.
92            We don't know how most of that works yet though. */
93         openconnect_SSL_printf(vpninfo->https_ssl, "X-CSTP-Address-Type: IPv4\r\n");
94         openconnect_SSL_printf(vpninfo->https_ssl, "X-DTLS-Master-Secret: ");
95         for (i = 0; i < sizeof(vpninfo->dtls_secret); i++)
96                 openconnect_SSL_printf(vpninfo->https_ssl, "%02X", vpninfo->dtls_secret[i]);
97         openconnect_SSL_printf(vpninfo->https_ssl, "\r\nX-DTLS-CipherSuite: AES256-SHA:AES128-SHA:DES-CBC3-SHA:DES-CBC-SHA\r\n\r\n");
98
99         if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
100                 vpninfo->progress(vpninfo, PRG_ERR, "Error fetching HTTPS response\n");
101                 if (!retried) {
102                         retried = 1;
103                         openconnect_close_https(vpninfo);
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         if (vpninfo->select_nfds <= vpninfo->ssl_fd)
256                 vpninfo->select_nfds = vpninfo->ssl_fd + 1;
257
258         FD_SET(vpninfo->ssl_fd, &vpninfo->select_rfds);
259         FD_SET(vpninfo->ssl_fd, &vpninfo->select_efds);
260
261         vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
262         return 0;
263 }
264
265
266 int make_cstp_connection(struct openconnect_info *vpninfo)
267 {
268         if (!vpninfo->https_ssl && openconnect_open_https(vpninfo))
269                 exit(1);
270
271         if (vpninfo->deflate) {
272                 vpninfo->deflate_adler32 = 1;
273                 vpninfo->inflate_adler32 = 1;
274
275                 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
276                     deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
277                                  Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
278                         vpninfo->progress(vpninfo, PRG_ERR, "Compression setup failed\n");
279                         vpninfo->deflate = 0;
280                 }
281
282                 if (!vpninfo->deflate_pkt) {
283                         vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
284                         if (!vpninfo->deflate_pkt) {
285                                 vpninfo->progress(vpninfo, PRG_ERR, "Allocation of deflate buffer failed\n");
286                                 vpninfo->deflate = 0;
287                         }
288                         memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
289                         memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
290                         vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
291                 }
292         }
293
294         if (start_cstp_connection(vpninfo))
295                 return -EINVAL;
296
297         return 0;
298 }
299
300
301 static int inflate_and_queue_packet(struct openconnect_info *vpninfo, int type, void *buf, int len)
302 {
303         struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
304
305         if (!new)
306                 return -ENOMEM;
307
308         new->type = type;
309         new->next = NULL;
310
311         vpninfo->inflate_strm.next_in = buf;
312         vpninfo->inflate_strm.avail_in = len - 4;
313
314         vpninfo->inflate_strm.next_out = new->data;
315         vpninfo->inflate_strm.avail_out = vpninfo->mtu;
316         vpninfo->inflate_strm.total_out = 0;
317
318         if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
319                 vpninfo->progress(vpninfo, PRG_ERR, "inflate failed\n");
320                 free(new);
321                 return -EINVAL;
322         }
323
324         new->len = vpninfo->inflate_strm.total_out;
325
326         vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
327                                            new->data, new->len);
328
329         if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *)(buf + len - 4))) {
330                 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
331         }
332
333         vpninfo->progress(vpninfo, PRG_TRACE,
334                           "Received compressed data packet of %ld bytes\n",
335                           vpninfo->inflate_strm.total_out);
336
337         queue_packet(&vpninfo->incoming_queue, new);
338         return 0;
339 }
340
341 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
342 {
343         unsigned char buf[16384];
344         int len, ret;
345         int work_done = 0;
346
347         /* FIXME: The poll() handling here is fairly simplistic. Actually,
348            if the SSL connection stalls it could return a WANT_WRITE error
349            on _either_ of the SSL_read() or SSL_write() calls. In that case,
350            we should probably remove POLLIN from the events we're looking for,
351            and add POLLOUT. As it is, though, it'll just chew CPU time in that
352            fairly unlikely situation, until the write backlog clears. */
353         while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
354                 int payload_len;
355
356                 if (buf[0] != 'S' || buf[1] != 'T' ||
357                     buf[2] != 'F' || buf[3] != 1 || buf[7])
358                         goto unknown_pkt;
359
360                 payload_len = (buf[4] << 8) + buf[5];
361                 if (len != 8 + payload_len) {
362                         vpninfo->progress(vpninfo, PRG_ERR,
363                                           "Unexpected packet length. SSL_read returned %d but packet is\n",
364                                           len);
365                         vpninfo->progress(vpninfo, PRG_ERR,
366                                           "%02x %02x %02x %02x %02x %02x %02x %02x\n",
367                                           buf[0], buf[1], buf[2], buf[3],
368                                           buf[4], buf[5], buf[6], buf[7]);
369                         continue;
370                 }
371                 vpninfo->ssl_times.last_rx = time(NULL);
372                 switch(buf[6]) {
373                 case AC_PKT_DPD_OUT:
374                         vpninfo->progress(vpninfo, PRG_TRACE,
375                                           "Got CSTP DPD request\n");
376                         vpninfo->owe_ssl_dpd_response = 1;
377                         continue;
378
379                 case AC_PKT_DPD_RESP:
380                         vpninfo->progress(vpninfo, PRG_TRACE,
381                                           "Got CSTP DPD response\n");
382                         continue;
383
384                 case AC_PKT_KEEPALIVE:
385                         vpninfo->progress(vpninfo, PRG_TRACE,
386                                           "Got CSTP Keepalive\n");
387                         continue;
388
389                 case AC_PKT_DATA:
390                         vpninfo->progress(vpninfo, PRG_TRACE,
391                                           "Received uncompressed data packet of %d bytes\n",
392                                           payload_len);
393                         queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
394                                          payload_len);
395                         work_done = 1;
396                         continue;
397
398                 case AC_PKT_DISCONN: {
399                         int i;
400                         for (i = 0; i < payload_len; i++) {
401                                 if (!isprint(buf[payload_len + 8 + i]))
402                                         buf[payload_len + 8 + i] = '.';
403                         }
404                         buf[payload_len + 8] = 0;
405                         vpninfo->progress(vpninfo, PRG_ERR,
406                                           "Received server disconnect: '%s'\n", buf + 8);
407                         vpninfo->quit_reason = "Server request";
408                         return 1;
409                 }
410                 case AC_PKT_COMPRESSED:
411                         if (!vpninfo->deflate) {
412                                 vpninfo->progress(vpninfo, PRG_ERR, "Compressed packet received in !deflate mode\n");
413                                 goto unknown_pkt;
414                         }
415                         inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
416                         work_done = 1;
417                         continue;
418
419                 case AC_PKT_TERM_SERVER:
420                         vpninfo->progress(vpninfo, PRG_ERR, "received server terminate packet\n");
421                         vpninfo->quit_reason = "Server request";
422                         return 1;
423                 }
424
425         unknown_pkt:
426                 vpninfo->progress(vpninfo, PRG_ERR,
427                                   "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
428                                   buf[0], buf[1], buf[2], buf[3],
429                                   buf[4], buf[5], buf[6], buf[7]);
430                 vpninfo->quit_reason = "Unknown packet received";
431                 return 1;
432         }
433
434
435         /* If SSL_write() fails we are expected to try again. With exactly
436            the same data, at exactly the same location. So we keep the 
437            packet we had before.... */
438         if (vpninfo->current_ssl_pkt) {
439         handle_outgoing:
440                 vpninfo->ssl_times.last_tx = time(NULL);
441                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
442                 ret = SSL_write(vpninfo->https_ssl,
443                                 vpninfo->current_ssl_pkt->hdr,
444                                 vpninfo->current_ssl_pkt->len + 8);
445                 if (ret <= 0) {
446                         ret = SSL_get_error(vpninfo->https_ssl, ret);
447                         switch (ret) {
448                         case SSL_ERROR_WANT_WRITE:
449                                 /* Waiting for the socket to become writable -- it's
450                                    probably stalled, and/or the buffers are full */
451                                 FD_SET(vpninfo->ssl_fd, &vpninfo->select_wfds);
452
453                         case SSL_ERROR_WANT_READ:
454                                 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
455                                         goto peer_dead;
456                                 return work_done;
457                         default:
458                                 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d", ret);
459                                 ERR_print_errors_fp(stderr);
460                                 vpninfo->quit_reason = "SSL write error";
461                                 return 1;
462                         }
463                 }
464                 if (ret != vpninfo->current_ssl_pkt->len + 8) {
465                         vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
466                                 vpninfo->current_ssl_pkt->len + 8, ret);
467                         vpninfo->quit_reason = "Internal error";
468                         return 1;
469                 }
470                 /* Don't free the 'special' packets */
471                 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
472                     vpninfo->current_ssl_pkt != &dpd_pkt &&
473                     vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
474                     vpninfo->current_ssl_pkt != &keepalive_pkt)
475                         free(vpninfo->current_ssl_pkt);
476
477                 vpninfo->current_ssl_pkt = NULL;
478         }
479
480         if (vpninfo->owe_ssl_dpd_response) {
481                 vpninfo->owe_ssl_dpd_response = 0;
482                 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
483                 goto handle_outgoing;
484         }
485
486         switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
487         case KA_REKEY:
488                 /* Not that this will ever happen; we don't even process
489                    the setting when we're asked for it. */
490                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
491                 time(&vpninfo->ssl_times.last_rekey);
492                 work_done = 1;
493                 break;
494
495         case KA_DPD_DEAD:
496         peer_dead:
497                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
498                 openconnect_close_https(vpninfo);
499
500                 /* It's already deflated in the old stream. Extremely 
501                    non-trivial to reconstitute it; just throw it away */
502                 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
503                         vpninfo->current_ssl_pkt = NULL;
504
505                 if (make_cstp_connection(vpninfo)) {
506                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
507                         vpninfo->quit_reason = "SSL DPD detected dead peer; reconnect failed";
508                         return 1;
509                 }
510                 /* I think we can leave DTLS to its own devices; when we reconnect
511                    with the same master secret, we do seem to get the same sessid */
512                 return 1;
513
514         case KA_DPD:
515                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
516
517                 vpninfo->current_ssl_pkt = &dpd_pkt;
518                 goto handle_outgoing;
519
520         case KA_KEEPALIVE:
521                 /* No need to send an explicit keepalive
522                    if we have real data to send */
523                 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
524                         break;
525
526                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
527
528                 vpninfo->current_ssl_pkt = &keepalive_pkt;
529                 goto handle_outgoing;
530
531         case KA_NONE:
532                 ;
533         }
534
535         /* Service outgoing packet queue, if no DTLS */
536         while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
537                 struct pkt *this = vpninfo->outgoing_queue;
538                 vpninfo->outgoing_queue = this->next;
539
540                 if (vpninfo->deflate) {
541                         unsigned char *adler;
542                         int ret;
543
544                         vpninfo->deflate_strm.next_in = this->data;
545                         vpninfo->deflate_strm.avail_in = this->len;
546                         vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
547                         vpninfo->deflate_strm.avail_out = 2040;
548                         vpninfo->deflate_strm.total_out = 0;
549
550                         ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
551                         if (ret) {
552                                 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
553                                 goto uncompr;
554                         }
555
556                         vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
557                         vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
558
559                         /* Add ongoing adler32 to tail of compressed packet */
560                         vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
561                                                            this->data, this->len);
562
563                         adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
564                         *(adler++) =  vpninfo->deflate_adler32 >> 24;
565                         *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
566                         *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
567                         *(adler)   =  vpninfo->deflate_adler32 & 0xff;
568
569                         vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
570
571                         vpninfo->progress(vpninfo, PRG_TRACE,
572                                           "Sending compressed data packet of %d bytes\n",
573                                           this->len);
574
575                         vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
576                 } else {
577                 uncompr:
578                         memcpy(this->hdr, data_hdr, 8);
579                         this->hdr[4] = this->len >> 8;
580                         this->hdr[5] = this->len & 0xff;
581
582                         vpninfo->progress(vpninfo, PRG_TRACE,
583                                           "Sending uncompressed data packet of %d bytes\n",
584                                           this->len);
585
586                         vpninfo->current_ssl_pkt = this;
587                 }
588                 goto handle_outgoing;
589         }
590
591         /* Work is not done if we just got rid of packets off the queue */
592         return work_done;
593 }
594
595 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
596 {
597         unsigned char *bye_pkt;
598         int reason_len = strlen(reason);
599         bye_pkt = malloc(reason_len + 8);
600         if (!bye_pkt)
601                 return -ENOMEM;
602         
603         memcpy(bye_pkt, data_hdr, 8);
604         memcpy(bye_pkt + 8, reason, strlen(reason));
605
606         bye_pkt[4] = reason_len >> 8;
607         bye_pkt[5] = reason_len & 0xff;
608         bye_pkt[6] = AC_PKT_DISCONN;
609
610         SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
611         free(bye_pkt);
612
613         vpninfo->progress(vpninfo, PRG_TRACE,
614                           "Send BYE packet: %s\n", reason);
615
616         return 0;
617 }