Use progress callback for output
[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_COMPRESSED:
395                         if (!vpninfo->deflate) {
396                                 vpninfo->progress(vpninfo, PRG_ERR, "Compressed packet received in !deflate mode\n");
397                                 goto unknown_pkt;
398                         }
399                         inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
400                         work_done = 1;
401                         continue;
402
403                 case AC_PKT_TERM_SERVER:
404                         vpninfo->progress(vpninfo, PRG_ERR, "received server terminate packet\n");
405                         vpninfo->quit_reason = "Server request";
406                         return 1;
407                 }
408
409         unknown_pkt:
410                 vpninfo->progress(vpninfo, PRG_ERR,
411                                   "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
412                                   buf[0], buf[1], buf[2], buf[3],
413                                   buf[4], buf[5], buf[6], buf[7]);
414                 vpninfo->quit_reason = "Unknown packet received";
415                 return 1;
416         }
417
418
419         /* If SSL_write() fails we are expected to try again. With exactly
420            the same data, at exactly the same location. So we keep the 
421            packet we had before.... */
422         if (vpninfo->current_ssl_pkt) {
423         handle_outgoing:
424                 vpninfo->ssl_times.last_tx = time(NULL);
425                 vpninfo->pfds[vpninfo->ssl_pfd].events &= ~POLLOUT;
426                 ret = SSL_write(vpninfo->https_ssl,
427                                 vpninfo->current_ssl_pkt->hdr,
428                                 vpninfo->current_ssl_pkt->len + 8);
429                 if (ret <= 0) {
430                         ret = SSL_get_error(vpninfo->https_ssl, ret);
431                         switch (ret) {
432                         case SSL_ERROR_WANT_WRITE:
433                                 /* Waiting for the socket to become writable -- it's
434                                    probably stalled, and/or the buffers are full */
435                                 vpninfo->pfds[vpninfo->ssl_pfd].events |= POLLOUT;
436                         case SSL_ERROR_WANT_READ:
437                                 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
438                                         goto peer_dead;
439                                 return work_done;
440                         default:
441                                 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d", ret);
442                                 ERR_print_errors_fp(stderr);
443                                 vpninfo->quit_reason = "SSL write error";
444                                 return 1;
445                         }
446                 }
447                 if (ret != vpninfo->current_ssl_pkt->len + 8) {
448                         vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
449                                 vpninfo->current_ssl_pkt->len + 8, ret);
450                         vpninfo->quit_reason = "Internal error";
451                         return 1;
452                 }
453                 /* Don't free the 'special' packets */
454                 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
455                     vpninfo->current_ssl_pkt != &dpd_pkt &&
456                     vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
457                     vpninfo->current_ssl_pkt != &keepalive_pkt)
458                         free(vpninfo->current_ssl_pkt);
459
460                 vpninfo->current_ssl_pkt = NULL;
461         }
462
463         if (vpninfo->owe_ssl_dpd_response) {
464                 vpninfo->owe_ssl_dpd_response = 0;
465                 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
466                 goto handle_outgoing;
467         }
468
469         switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
470         case KA_REKEY:
471                 /* Not that this will ever happen; we don't even process
472                    the setting when we're asked for it. */
473                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
474                 time(&vpninfo->ssl_times.last_rekey);
475                 work_done = 1;
476                 break;
477
478         case KA_DPD_DEAD:
479         peer_dead:
480                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
481                 SSL_free(vpninfo->https_ssl);
482                 vpninfo->https_ssl = NULL;
483                 close(vpninfo->ssl_fd);
484
485                 /* It's already deflated in the old stream. Extremely 
486                    non-trivial to reconstitute it; just throw it away */
487                 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
488                         vpninfo->current_ssl_pkt = NULL;
489
490                 if (make_cstp_connection(vpninfo)) {
491                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
492                         vpninfo->quit_reason = "SSL DPD detected dead peer; reconnect failed";
493                         return 1;
494                 }
495                 /* I think we can leave DTLS to its own devices; when we reconnect
496                    with the same master secret, we do seem to get the same sessid */
497                 return 1;
498
499         case KA_DPD:
500                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
501
502                 vpninfo->current_ssl_pkt = &dpd_pkt;
503                 goto handle_outgoing;
504
505         case KA_KEEPALIVE:
506                 /* No need to send an explicit keepalive
507                    if we have real data to send */
508                 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
509                         break;
510
511                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
512
513                 vpninfo->current_ssl_pkt = &keepalive_pkt;
514                 goto handle_outgoing;
515
516         case KA_NONE:
517                 ;
518         }
519
520         /* Service outgoing packet queue, if no DTLS */
521         while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
522                 struct pkt *this = vpninfo->outgoing_queue;
523                 vpninfo->outgoing_queue = this->next;
524
525                 if (vpninfo->deflate) {
526                         unsigned char *adler;
527                         int ret;
528
529                         vpninfo->deflate_strm.next_in = this->data;
530                         vpninfo->deflate_strm.avail_in = this->len;
531                         vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
532                         vpninfo->deflate_strm.avail_out = 2040;
533                         vpninfo->deflate_strm.total_out = 0;
534
535                         ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
536                         if (ret) {
537                                 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
538                                 goto uncompr;
539                         }
540
541                         vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
542                         vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
543
544                         /* Add ongoing adler32 to tail of compressed packet */
545                         vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
546                                                            this->data, this->len);
547
548                         adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
549                         *(adler++) =  vpninfo->deflate_adler32 >> 24;
550                         *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
551                         *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
552                         *(adler)   =  vpninfo->deflate_adler32 & 0xff;
553
554                         vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
555
556                         vpninfo->progress(vpninfo, PRG_TRACE,
557                                           "Sending compressed data packet of %d bytes\n",
558                                           this->len);
559
560                         vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
561                 } else {
562                 uncompr:
563                         memcpy(this->hdr, data_hdr, 8);
564                         this->hdr[4] = this->len >> 8;
565                         this->hdr[5] = this->len & 0xff;
566
567                         vpninfo->progress(vpninfo, PRG_TRACE,
568                                           "Sending uncompressed data packet of %d bytes\n",
569                                           this->len);
570
571                         vpninfo->current_ssl_pkt = this;
572                 }
573                 goto handle_outgoing;
574         }
575
576         /* Work is not done if we just got rid of packets off the queue */
577         return work_done;
578 }
579
580 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
581 {
582         unsigned char *bye_pkt;
583         int reason_len = strlen(reason);
584         bye_pkt = malloc(reason_len + 8);
585         if (!bye_pkt)
586                 return -ENOMEM;
587         
588         memcpy(bye_pkt, data_hdr, 8);
589         memcpy(bye_pkt + 8, reason, strlen(reason));
590
591         bye_pkt[4] = reason_len >> 8;
592         bye_pkt[5] = reason_len & 0xff;
593         bye_pkt[6] = 5;
594
595         SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
596         free(bye_pkt);
597
598         vpninfo->progress(vpninfo, PRG_TRACE,
599                           "Send BYE packet: %s\n", reason);
600
601         return 0;
602 }