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