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