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