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