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