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