use adaptive reconnect_interval
[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         int ret;
295
296         if (!vpninfo->https_ssl && (ret=openconnect_open_https(vpninfo)))
297                 return ret;
298
299         if (vpninfo->deflate) {
300                 vpninfo->deflate_adler32 = 1;
301                 vpninfo->inflate_adler32 = 1;
302
303                 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
304                     deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
305                                  Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
306                         vpninfo->progress(vpninfo, PRG_ERR, "Compression setup failed\n");
307                         vpninfo->deflate = 0;
308                 }
309
310                 if (!vpninfo->deflate_pkt) {
311                         vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
312                         if (!vpninfo->deflate_pkt) {
313                                 vpninfo->progress(vpninfo, PRG_ERR, "Allocation of deflate buffer failed\n");
314                                 vpninfo->deflate = 0;
315                         }
316                         memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
317                         memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
318                         vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
319                 }
320         }
321
322         return start_cstp_connection(vpninfo);
323 }
324
325 static int cstp_reconnect(struct openconnect_info *vpninfo)
326 {
327         int ret;
328         int timeout;
329         int interval;
330         
331         timeout = vpninfo->reconnect_timeout;
332         interval = vpninfo->reconnect_interval;
333
334         while ((ret = make_cstp_connection(vpninfo))) {
335                 if (timeout <= 0)
336                         return ret;
337                 vpninfo->progress(vpninfo, PRG_INFO,
338                                   "sleep %ds, remain timeout %ds\n",
339                                   interval, timeout);
340                 sleep(interval);
341                 timeout -= interval;
342                 interval += vpninfo->reconnect_interval;
343                 if (interval > RECONNECT_INTERVAL_MAX)
344                         interval = RECONNECT_INTERVAL_MAX;
345         }
346         return 0;
347 }
348
349 static int inflate_and_queue_packet(struct openconnect_info *vpninfo, int type, void *buf, int len)
350 {
351         struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
352
353         if (!new)
354                 return -ENOMEM;
355
356         new->type = type;
357         new->next = NULL;
358
359         vpninfo->inflate_strm.next_in = buf;
360         vpninfo->inflate_strm.avail_in = len - 4;
361
362         vpninfo->inflate_strm.next_out = new->data;
363         vpninfo->inflate_strm.avail_out = vpninfo->mtu;
364         vpninfo->inflate_strm.total_out = 0;
365
366         if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
367                 vpninfo->progress(vpninfo, PRG_ERR, "inflate failed\n");
368                 free(new);
369                 return -EINVAL;
370         }
371
372         new->len = vpninfo->inflate_strm.total_out;
373
374         vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
375                                            new->data, new->len);
376
377         if (vpninfo->inflate_adler32 != ntohl( *(uint32_t *)(buf + len - 4))) {
378                 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
379         }
380
381         vpninfo->progress(vpninfo, PRG_TRACE,
382                           "Received compressed data packet of %ld bytes\n",
383                           vpninfo->inflate_strm.total_out);
384
385         queue_packet(&vpninfo->incoming_queue, new);
386         return 0;
387 }
388
389 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
390 {
391         unsigned char buf[16384];
392         int len, ret;
393         int work_done = 0;
394
395         /* FIXME: The poll() handling here is fairly simplistic. Actually,
396            if the SSL connection stalls it could return a WANT_WRITE error
397            on _either_ of the SSL_read() or SSL_write() calls. In that case,
398            we should probably remove POLLIN from the events we're looking for,
399            and add POLLOUT. As it is, though, it'll just chew CPU time in that
400            fairly unlikely situation, until the write backlog clears. */
401         while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
402                 int payload_len;
403
404                 if (buf[0] != 'S' || buf[1] != 'T' ||
405                     buf[2] != 'F' || buf[3] != 1 || buf[7])
406                         goto unknown_pkt;
407
408                 payload_len = (buf[4] << 8) + buf[5];
409                 if (len != 8 + payload_len) {
410                         vpninfo->progress(vpninfo, PRG_ERR,
411                                           "Unexpected packet length. SSL_read returned %d but packet is\n",
412                                           len);
413                         vpninfo->progress(vpninfo, PRG_ERR,
414                                           "%02x %02x %02x %02x %02x %02x %02x %02x\n",
415                                           buf[0], buf[1], buf[2], buf[3],
416                                           buf[4], buf[5], buf[6], buf[7]);
417                         continue;
418                 }
419                 vpninfo->ssl_times.last_rx = time(NULL);
420                 switch(buf[6]) {
421                 case AC_PKT_DPD_OUT:
422                         vpninfo->progress(vpninfo, PRG_TRACE,
423                                           "Got CSTP DPD request\n");
424                         vpninfo->owe_ssl_dpd_response = 1;
425                         continue;
426
427                 case AC_PKT_DPD_RESP:
428                         vpninfo->progress(vpninfo, PRG_TRACE,
429                                           "Got CSTP DPD response\n");
430                         continue;
431
432                 case AC_PKT_KEEPALIVE:
433                         vpninfo->progress(vpninfo, PRG_TRACE,
434                                           "Got CSTP Keepalive\n");
435                         continue;
436
437                 case AC_PKT_DATA:
438                         vpninfo->progress(vpninfo, PRG_TRACE,
439                                           "Received uncompressed data packet of %d bytes\n",
440                                           payload_len);
441                         queue_new_packet(&vpninfo->incoming_queue, AF_INET, buf + 8,
442                                          payload_len);
443                         work_done = 1;
444                         continue;
445
446                 case AC_PKT_DISCONN: {
447                         int i;
448                         for (i = 0; i < payload_len; i++) {
449                                 if (!isprint(buf[payload_len + 8 + i]))
450                                         buf[payload_len + 8 + i] = '.';
451                         }
452                         buf[payload_len + 8] = 0;
453                         vpninfo->progress(vpninfo, PRG_ERR,
454                                           "Received server disconnect: '%s'\n", buf + 8);
455                         vpninfo->quit_reason = "Server request";
456                         return 1;
457                 }
458                 case AC_PKT_COMPRESSED:
459                         if (!vpninfo->deflate) {
460                                 vpninfo->progress(vpninfo, PRG_ERR, "Compressed packet received in !deflate mode\n");
461                                 goto unknown_pkt;
462                         }
463                         inflate_and_queue_packet(vpninfo, AF_INET, buf + 8, payload_len);
464                         work_done = 1;
465                         continue;
466
467                 case AC_PKT_TERM_SERVER:
468                         vpninfo->progress(vpninfo, PRG_ERR, "received server terminate packet\n");
469                         vpninfo->quit_reason = "Server request";
470                         return 1;
471                 }
472
473         unknown_pkt:
474                 vpninfo->progress(vpninfo, PRG_ERR,
475                                   "Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n",
476                                   buf[0], buf[1], buf[2], buf[3],
477                                   buf[4], buf[5], buf[6], buf[7]);
478                 vpninfo->quit_reason = "Unknown packet received";
479                 return 1;
480         }
481
482
483         /* If SSL_write() fails we are expected to try again. With exactly
484            the same data, at exactly the same location. So we keep the 
485            packet we had before.... */
486         if (vpninfo->current_ssl_pkt) {
487         handle_outgoing:
488                 vpninfo->ssl_times.last_tx = time(NULL);
489                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
490                 ret = SSL_write(vpninfo->https_ssl,
491                                 vpninfo->current_ssl_pkt->hdr,
492                                 vpninfo->current_ssl_pkt->len + 8);
493                 if (ret <= 0) {
494                         ret = SSL_get_error(vpninfo->https_ssl, ret);
495                         switch (ret) {
496                         case SSL_ERROR_WANT_WRITE:
497                                 /* Waiting for the socket to become writable -- it's
498                                    probably stalled, and/or the buffers are full */
499                                 FD_SET(vpninfo->ssl_fd, &vpninfo->select_wfds);
500
501                         case SSL_ERROR_WANT_READ:
502                                 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
503                                         goto peer_dead;
504                                 return work_done;
505                         default:
506                                 vpninfo->progress(vpninfo, PRG_ERR, "SSL_write failed: %d", ret);
507                                 ERR_print_errors_fp(stderr);
508                                 vpninfo->quit_reason = "SSL write error";
509                                 return 1;
510                         }
511                 }
512                 if (ret != vpninfo->current_ssl_pkt->len + 8) {
513                         vpninfo->progress(vpninfo, PRG_ERR, "SSL wrote too few bytes! Asked for %d, sent %d\n",
514                                 vpninfo->current_ssl_pkt->len + 8, ret);
515                         vpninfo->quit_reason = "Internal error";
516                         return 1;
517                 }
518                 /* Don't free the 'special' packets */
519                 if (vpninfo->current_ssl_pkt != vpninfo->deflate_pkt &&
520                     vpninfo->current_ssl_pkt != &dpd_pkt &&
521                     vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
522                     vpninfo->current_ssl_pkt != &keepalive_pkt)
523                         free(vpninfo->current_ssl_pkt);
524
525                 vpninfo->current_ssl_pkt = NULL;
526         }
527
528         if (vpninfo->owe_ssl_dpd_response) {
529                 vpninfo->owe_ssl_dpd_response = 0;
530                 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
531                 goto handle_outgoing;
532         }
533
534         switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
535         case KA_REKEY:
536                 /* Not that this will ever happen; we don't even process
537                    the setting when we're asked for it. */
538                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP rekey due but we don't know how\n");
539                 time(&vpninfo->ssl_times.last_rekey);
540                 work_done = 1;
541                 break;
542
543         case KA_DPD_DEAD:
544         peer_dead:
545                 vpninfo->progress(vpninfo, PRG_ERR, "CSTP Dead Peer Detection detected dead peer!\n");
546                 openconnect_close_https(vpninfo);
547
548                 /* It's already deflated in the old stream. Extremely 
549                    non-trivial to reconstitute it; just throw it away */
550                 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
551                         vpninfo->current_ssl_pkt = NULL;
552
553                 if (cstp_reconnect(vpninfo)) {
554                         vpninfo->progress(vpninfo, PRG_ERR, "Reconnect failed\n");
555                         vpninfo->quit_reason = "SSL DPD detected dead peer; reconnect failed";
556                         return 1;
557                 }
558                 /* I think we can leave DTLS to its own devices; when we reconnect
559                    with the same master secret, we do seem to get the same sessid */
560                 return 1;
561
562         case KA_DPD:
563                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP DPD\n");
564
565                 vpninfo->current_ssl_pkt = &dpd_pkt;
566                 goto handle_outgoing;
567
568         case KA_KEEPALIVE:
569                 /* No need to send an explicit keepalive
570                    if we have real data to send */
571                 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
572                         break;
573
574                 vpninfo->progress(vpninfo, PRG_TRACE, "Send CSTP Keepalive\n");
575
576                 vpninfo->current_ssl_pkt = &keepalive_pkt;
577                 goto handle_outgoing;
578
579         case KA_NONE:
580                 ;
581         }
582
583         /* Service outgoing packet queue, if no DTLS */
584         while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
585                 struct pkt *this = vpninfo->outgoing_queue;
586                 vpninfo->outgoing_queue = this->next;
587                 vpninfo->outgoing_qlen--;
588
589                 if (vpninfo->deflate) {
590                         unsigned char *adler;
591                         int ret;
592
593                         vpninfo->deflate_strm.next_in = this->data;
594                         vpninfo->deflate_strm.avail_in = this->len;
595                         vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
596                         vpninfo->deflate_strm.avail_out = 2040;
597                         vpninfo->deflate_strm.total_out = 0;
598
599                         ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
600                         if (ret) {
601                                 vpninfo->progress(vpninfo, PRG_ERR, "deflate failed %d\n", ret);
602                                 goto uncompr;
603                         }
604
605                         vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
606                         vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
607
608                         /* Add ongoing adler32 to tail of compressed packet */
609                         vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
610                                                            this->data, this->len);
611
612                         adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
613                         *(adler++) =  vpninfo->deflate_adler32 >> 24;
614                         *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
615                         *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
616                         *(adler)   =  vpninfo->deflate_adler32 & 0xff;
617
618                         vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
619
620                         vpninfo->progress(vpninfo, PRG_TRACE,
621                                           "Sending compressed data packet of %d bytes\n",
622                                           this->len);
623
624                         vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
625                 } else {
626                 uncompr:
627                         memcpy(this->hdr, data_hdr, 8);
628                         this->hdr[4] = this->len >> 8;
629                         this->hdr[5] = this->len & 0xff;
630
631                         vpninfo->progress(vpninfo, PRG_TRACE,
632                                           "Sending uncompressed data packet of %d bytes\n",
633                                           this->len);
634
635                         vpninfo->current_ssl_pkt = this;
636                 }
637                 goto handle_outgoing;
638         }
639
640         /* Work is not done if we just got rid of packets off the queue */
641         return work_done;
642 }
643
644 int cstp_bye(struct openconnect_info *vpninfo, char *reason)
645 {
646         unsigned char *bye_pkt;
647         int reason_len;
648
649         /* already lost connection? */
650         if (!vpninfo->https_ssl)
651                 return 0;
652
653         reason_len = strlen(reason);
654         bye_pkt = malloc(reason_len + 8);
655         if (!bye_pkt)
656                 return -ENOMEM;
657         
658         memcpy(bye_pkt, data_hdr, 8);
659         memcpy(bye_pkt + 8, reason, reason_len);
660
661         bye_pkt[4] = reason_len >> 8;
662         bye_pkt[5] = reason_len & 0xff;
663         bye_pkt[6] = AC_PKT_DISCONN;
664
665         SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 8);
666         free(bye_pkt);
667
668         vpninfo->progress(vpninfo, PRG_INFO,
669                           "Send BYE packet: %s\n", reason);
670
671         return 0;
672 }