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