2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008-2010 Intel Corporation.
5 * Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
7 * Author: David Woodhouse <dwmw2@infradead.org>
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.
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.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to:
21 * Free Software Foundation, Inc.
22 * 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301 USA
33 #include <openssl/ssl.h>
34 #include <openssl/err.h>
35 #include <openssl/rand.h>
37 #include "openconnect-internal.h"
40 * Data packets are encapsulated in the SSL stream as follows:
42 * 0000: Magic "STF\x1"
43 * 0004: Big-endian 16-bit length (not including 8-byte header)
44 * 0006: Byte packet type (see openconnect-internal.h)
48 static char data_hdr[8] = {
51 AC_PKT_DATA, /* Type */
55 static struct pkt keepalive_pkt = {
56 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_KEEPALIVE, 0 },
59 static struct pkt dpd_pkt = {
60 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_OUT, 0 },
63 static struct pkt dpd_resp_pkt = {
64 .hdr = { 'S', 'T', 'F', 1, 0, 0, AC_PKT_DPD_RESP, 0 },
68 static int start_cstp_connection(struct openconnect_info *vpninfo)
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;
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;
91 vpninfo->vpn_dns[i] = vpninfo->vpn_nbns[i] = NULL;
93 for (inc = vpninfo->split_includes; inc; ) {
94 struct split_include *next = inc->next;
98 for (inc = vpninfo->split_excludes; inc; ) {
99 struct split_include *next = inc->next;
103 vpninfo->split_includes = vpninfo->split_excludes = NULL;
105 /* Create (new) random master key for DTLS connection, if needed */
106 if (vpninfo->dtls_times.last_rekey + vpninfo->dtls_times.rekey <
108 RAND_bytes(vpninfo->dtls_secret, sizeof(vpninfo->dtls_secret)) != 1) {
109 fprintf(stderr, _("Failed to initialise DTLS secret\n"));
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");
131 if (openconnect_SSL_gets(vpninfo->https_ssl, buf, 65536) < 0) {
132 vpn_progress(vpninfo, PRG_ERR,
133 _("Error fetching HTTPS response\n"));
136 openconnect_close_https(vpninfo);
138 if (openconnect_open_https(vpninfo)) {
139 vpn_progress(vpninfo, PRG_ERR,
140 _("Failed to open HTTPS connection to %s\n"),
149 if (strncmp(buf, "HTTP/1.1 200 ", 13)) {
150 if (!strncmp(buf, "HTTP/1.1 503 ", 13)) {
151 /* "Service Unavailable. Why? */
152 const char *reason = "<unknown>";
153 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
154 if (!strncmp(buf, "X-Reason: ", 10)) {
159 vpn_progress(vpninfo, PRG_ERR,
160 _("VPN service unavailable; reason: %s\n"),
164 vpn_progress(vpninfo, PRG_ERR,
165 _("Got inappropriate HTTP CONNECT response: %s\n"),
167 if (!strncmp(buf, "HTTP/1.1 401 ", 13))
172 vpn_progress(vpninfo, PRG_INFO, _("Got CONNECT response: %s\n"), buf);
174 /* We may have advertised it, but we only do it if the server agrees */
175 vpninfo->deflate = 0;
177 while ((i = openconnect_SSL_gets(vpninfo->https_ssl, buf, sizeof(buf)))) {
178 struct vpn_option *new_option;
179 char *colon = strchr(buf, ':');
188 if (strncmp(buf, "X-DTLS-", 7) &&
189 strncmp(buf, "X-CSTP-", 7))
192 new_option = malloc(sizeof(*new_option));
194 vpn_progress(vpninfo, PRG_ERR, _("No memory for options\n"));
197 new_option->option = strdup(buf);
198 new_option->value = strdup(colon);
199 new_option->next = NULL;
201 if (!new_option->option || !new_option->value) {
202 vpn_progress(vpninfo, PRG_ERR, _("No memory for options\n"));
206 vpn_progress(vpninfo, PRG_TRACE, "%s: %s\n", buf, colon);
208 if (!strncmp(buf, "X-DTLS-", 7)) {
209 *next_dtls_option = new_option;
210 next_dtls_option = &new_option->next;
212 if (!strcmp(buf + 7, "Session-ID")) {
213 if (strlen(colon) != 64) {
214 vpn_progress(vpninfo, PRG_ERR,
215 _("X-DTLS-Session-ID not 64 characters; is: \"%s\"\n"),
217 vpninfo->dtls_attempt_period = 0;
220 for (i = 0; i < 64; i += 2)
221 vpninfo->dtls_session_id[i/2] = unhex(colon + i);
223 time(&vpninfo->dtls_times.last_rekey);
227 /* CSTP options... */
228 *next_cstp_option = new_option;
229 next_cstp_option = &new_option->next;
232 if (!strcmp(buf + 7, "Keepalive")) {
233 vpninfo->ssl_times.keepalive = atol(colon);
234 } else if (!strcmp(buf + 7, "DPD")) {
236 if (j && (!vpninfo->ssl_times.dpd || j < vpninfo->ssl_times.dpd))
237 vpninfo->ssl_times.dpd = j;
238 } else if (!strcmp(buf + 7, "Rekey-Time")) {
239 vpninfo->ssl_times.rekey = atol(colon);
240 } else if (!strcmp(buf + 7, "Content-Encoding")) {
241 if (!strcmp(colon, "deflate"))
242 vpninfo->deflate = 1;
244 vpn_progress(vpninfo, PRG_ERR,
245 _("Unknown CSTP-Content-Encoding %s\n"),
249 } else if (!strcmp(buf + 7, "MTU")) {
250 vpninfo->mtu = atol(colon);
251 } else if (!strcmp(buf + 7, "Address")) {
252 if (strchr(new_option->value, ':'))
253 vpninfo->vpn_addr6 = new_option->value;
255 vpninfo->vpn_addr = new_option->value;
256 } else if (!strcmp(buf + 7, "Netmask")) {
257 if (strchr(new_option->value, ':'))
258 vpninfo->vpn_netmask6 = new_option->value;
260 vpninfo->vpn_netmask = new_option->value;
261 } else if (!strcmp(buf + 7, "DNS")) {
263 for (j = 0; j < 3; j++) {
264 if (!vpninfo->vpn_dns[j]) {
265 vpninfo->vpn_dns[j] = new_option->value;
269 } else if (!strcmp(buf + 7, "NBNS")) {
271 for (j = 0; j < 3; j++) {
272 if (!vpninfo->vpn_nbns[j]) {
273 vpninfo->vpn_nbns[j] = new_option->value;
277 } else if (!strcmp(buf + 7, "Default-Domain")) {
278 vpninfo->vpn_domain = new_option->value;
279 } else if (!strcmp(buf + 7, "MSIE-Proxy-PAC-URL")) {
280 vpninfo->vpn_proxy_pac = new_option->value;
281 } else if (!strcmp(buf + 7, "Banner")) {
282 vpninfo->banner = new_option->value;
283 } else if (!strcmp(buf + 7, "Split-Include")) {
284 struct split_include *inc = malloc(sizeof(*inc));
287 inc->route = new_option->value;
288 inc->next = vpninfo->split_includes;
289 vpninfo->split_includes = inc;
290 } else if (!strcmp(buf + 7, "Split-Exclude")) {
291 struct split_include *exc = malloc(sizeof(*exc));
294 exc->route = new_option->value;
295 exc->next = vpninfo->split_excludes;
296 vpninfo->split_excludes = exc;
300 if (!vpninfo->vpn_addr && !vpninfo->vpn_addr6) {
301 vpn_progress(vpninfo, PRG_ERR,
302 _("No IP address received. Aborting\n"));
306 if (strcmp(old_addr, vpninfo->vpn_addr)) {
307 vpn_progress(vpninfo, PRG_ERR,
308 _("Reconnect gave different Legacy IP address (%s != %s)\n"),
309 vpninfo->vpn_addr, old_addr);
314 if (strcmp(old_netmask, vpninfo->vpn_netmask)) {
315 vpn_progress(vpninfo, PRG_ERR,
316 _("Reconnect gave different Legacy IP netmask (%s != %s)\n"),
317 vpninfo->vpn_netmask, old_netmask);
322 if (strcmp(old_addr6, vpninfo->vpn_addr6)) {
323 vpn_progress(vpninfo, PRG_ERR,
324 _("Reconnect gave different IPv6 address (%s != %s)\n"),
325 vpninfo->vpn_addr6, old_addr6);
330 if (strcmp(old_netmask6, vpninfo->vpn_netmask6)) {
331 vpn_progress(vpninfo, PRG_ERR,
332 _("Reconnect gave different IPv6 netmask (%s != %s)\n"),
333 vpninfo->vpn_netmask6, old_netmask6);
338 while (old_dtls_opts) {
339 struct vpn_option *tmp = old_dtls_opts;
340 old_dtls_opts = old_dtls_opts->next;
345 while (old_cstp_opts) {
346 struct vpn_option *tmp = old_cstp_opts;
347 old_cstp_opts = old_cstp_opts->next;
352 vpn_progress(vpninfo, PRG_INFO, _("CSTP connected. DPD %d, Keepalive %d\n"),
353 vpninfo->ssl_times.dpd, vpninfo->ssl_times.keepalive);
355 BIO_set_nbio(SSL_get_rbio(vpninfo->https_ssl), 1);
356 BIO_set_nbio(SSL_get_wbio(vpninfo->https_ssl), 1);
358 fcntl(vpninfo->ssl_fd, F_SETFL, fcntl(vpninfo->ssl_fd, F_GETFL) | O_NONBLOCK);
359 if (vpninfo->select_nfds <= vpninfo->ssl_fd)
360 vpninfo->select_nfds = vpninfo->ssl_fd + 1;
362 FD_SET(vpninfo->ssl_fd, &vpninfo->select_rfds);
363 FD_SET(vpninfo->ssl_fd, &vpninfo->select_efds);
366 vpninfo->dtls_attempt_period = 0;
368 vpninfo->ssl_times.last_rekey = vpninfo->ssl_times.last_rx =
369 vpninfo->ssl_times.last_tx = time(NULL);
374 int make_cstp_connection(struct openconnect_info *vpninfo)
378 if (!vpninfo->https_ssl && (ret = openconnect_open_https(vpninfo)))
381 if (vpninfo->deflate) {
382 vpninfo->deflate_adler32 = 1;
383 vpninfo->inflate_adler32 = 1;
385 if (inflateInit2(&vpninfo->inflate_strm, -12) ||
386 deflateInit2(&vpninfo->deflate_strm, Z_DEFAULT_COMPRESSION,
387 Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY)) {
388 vpn_progress(vpninfo, PRG_ERR, _("Compression setup failed\n"));
389 vpninfo->deflate = 0;
392 if (!vpninfo->deflate_pkt) {
393 vpninfo->deflate_pkt = malloc(sizeof(struct pkt) + 2048);
394 if (!vpninfo->deflate_pkt) {
395 vpn_progress(vpninfo, PRG_ERR,
396 _("Allocation of deflate buffer failed\n"));
397 inflateEnd(&vpninfo->inflate_strm);
398 deflateEnd(&vpninfo->deflate_strm);
399 vpninfo->deflate = 0;
401 memset(vpninfo->deflate_pkt, 0, sizeof(struct pkt));
402 memcpy(vpninfo->deflate_pkt->hdr, data_hdr, 8);
403 vpninfo->deflate_pkt->hdr[6] = AC_PKT_COMPRESSED;
408 return start_cstp_connection(vpninfo);
411 int cstp_reconnect(struct openconnect_info *vpninfo)
417 openconnect_close_https(vpninfo);
419 /* Requeue the original packet that was deflated */
420 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt) {
421 vpninfo->current_ssl_pkt = NULL;
422 queue_packet(&vpninfo->outgoing_queue, vpninfo->pending_deflated_pkt);
423 vpninfo->pending_deflated_pkt = NULL;
425 if (vpninfo->deflate) {
426 inflateEnd(&vpninfo->inflate_strm);
427 deflateEnd(&vpninfo->deflate_strm);
429 timeout = vpninfo->reconnect_timeout;
430 interval = vpninfo->reconnect_interval;
432 while ((ret = make_cstp_connection(vpninfo))) {
435 vpn_progress(vpninfo, PRG_INFO,
436 _("sleep %ds, remaining timeout %ds\n"),
442 interval += vpninfo->reconnect_interval;
443 if (interval > RECONNECT_INTERVAL_MAX)
444 interval = RECONNECT_INTERVAL_MAX;
446 script_config_tun(vpninfo, "reconnect");
450 static int inflate_and_queue_packet(struct openconnect_info *vpninfo,
451 unsigned char *buf, int len)
453 struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->mtu);
461 vpninfo->inflate_strm.next_in = buf;
462 vpninfo->inflate_strm.avail_in = len - 4;
464 vpninfo->inflate_strm.next_out = new->data;
465 vpninfo->inflate_strm.avail_out = vpninfo->mtu;
466 vpninfo->inflate_strm.total_out = 0;
468 if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
469 vpn_progress(vpninfo, PRG_ERR, _("inflate failed\n"));
474 new->len = vpninfo->inflate_strm.total_out;
476 vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
477 new->data, new->len);
479 pkt_sum = buf[len - 1] | (buf[len - 2] << 8) |
480 (buf[len - 3] << 16) | (buf[len - 4] << 24);
482 if (vpninfo->inflate_adler32 != pkt_sum) {
483 vpninfo->quit_reason = "Compression (inflate) adler32 failure";
486 vpn_progress(vpninfo, PRG_TRACE,
487 _("Received compressed data packet of %ld bytes\n"),
488 (long)vpninfo->inflate_strm.total_out);
490 queue_packet(&vpninfo->incoming_queue, new);
494 int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
496 unsigned char buf[16384];
500 /* FIXME: The poll() handling here is fairly simplistic. Actually,
501 if the SSL connection stalls it could return a WANT_WRITE error
502 on _either_ of the SSL_read() or SSL_write() calls. In that case,
503 we should probably remove POLLIN from the events we're looking for,
504 and add POLLOUT. As it is, though, it'll just chew CPU time in that
505 fairly unlikely situation, until the write backlog clears. */
506 while ( (len = SSL_read(vpninfo->https_ssl, buf, sizeof(buf))) > 0) {
509 if (buf[0] != 'S' || buf[1] != 'T' ||
510 buf[2] != 'F' || buf[3] != 1 || buf[7])
513 payload_len = (buf[4] << 8) + buf[5];
514 if (len != 8 + payload_len) {
515 vpn_progress(vpninfo, PRG_ERR,
516 _("Unexpected packet length. SSL_read returned %d but packet is\n"),
518 vpn_progress(vpninfo, PRG_ERR,
519 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
520 buf[0], buf[1], buf[2], buf[3],
521 buf[4], buf[5], buf[6], buf[7]);
524 vpninfo->ssl_times.last_rx = time(NULL);
527 vpn_progress(vpninfo, PRG_TRACE,
528 _("Got CSTP DPD request\n"));
529 vpninfo->owe_ssl_dpd_response = 1;
532 case AC_PKT_DPD_RESP:
533 vpn_progress(vpninfo, PRG_TRACE,
534 _("Got CSTP DPD response\n"));
537 case AC_PKT_KEEPALIVE:
538 vpn_progress(vpninfo, PRG_TRACE,
539 _("Got CSTP Keepalive\n"));
543 vpn_progress(vpninfo, PRG_TRACE,
544 _("Received uncompressed data packet of %d bytes\n"),
546 queue_new_packet(&vpninfo->incoming_queue, buf + 8,
551 case AC_PKT_DISCONN: {
553 for (i = 0; i < payload_len; i++) {
554 if (!isprint(buf[payload_len + 8 + i]))
555 buf[payload_len + 8 + i] = '.';
557 buf[payload_len + 8] = 0;
558 vpn_progress(vpninfo, PRG_ERR,
559 _("Received server disconnect: %02x '%s'\n"),
561 vpninfo->quit_reason = "Server request";
564 case AC_PKT_COMPRESSED:
565 if (!vpninfo->deflate) {
566 vpn_progress(vpninfo, PRG_ERR,
567 _("Compressed packet received in !deflate mode\n"));
570 inflate_and_queue_packet(vpninfo, buf + 8, payload_len);
574 case AC_PKT_TERM_SERVER:
575 vpn_progress(vpninfo, PRG_ERR, _("received server terminate packet\n"));
576 vpninfo->quit_reason = "Server request";
581 vpn_progress(vpninfo, PRG_ERR,
582 _("Unknown packet %02x %02x %02x %02x %02x %02x %02x %02x\n"),
583 buf[0], buf[1], buf[2], buf[3],
584 buf[4], buf[5], buf[6], buf[7]);
585 vpninfo->quit_reason = "Unknown packet received";
589 ret = SSL_get_error(vpninfo->https_ssl, len);
590 if (ret == SSL_ERROR_SYSCALL || ret == SSL_ERROR_ZERO_RETURN) {
591 vpn_progress(vpninfo, PRG_ERR,
592 _("SSL read error %d (server probably closed connection); reconnecting.\n"),
598 /* If SSL_write() fails we are expected to try again. With exactly
599 the same data, at exactly the same location. So we keep the
600 packet we had before.... */
601 if (vpninfo->current_ssl_pkt) {
603 vpninfo->ssl_times.last_tx = time(NULL);
604 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
605 ret = SSL_write(vpninfo->https_ssl,
606 vpninfo->current_ssl_pkt->hdr,
607 vpninfo->current_ssl_pkt->len + 8);
609 ret = SSL_get_error(vpninfo->https_ssl, ret);
611 case SSL_ERROR_WANT_WRITE:
612 /* Waiting for the socket to become writable -- it's
613 probably stalled, and/or the buffers are full */
614 FD_SET(vpninfo->ssl_fd, &vpninfo->select_wfds);
616 case SSL_ERROR_WANT_READ:
617 if (ka_stalled_dpd_time(&vpninfo->ssl_times, timeout))
621 vpn_progress(vpninfo, PRG_ERR, _("SSL_write failed: %d\n"), ret);
622 report_ssl_errors(vpninfo);
626 if (ret != vpninfo->current_ssl_pkt->len + 8) {
627 vpn_progress(vpninfo, PRG_ERR,
628 _("SSL wrote too few bytes! Asked for %d, sent %d\n"),
629 vpninfo->current_ssl_pkt->len + 8, ret);
630 vpninfo->quit_reason = "Internal error";
633 /* Don't free the 'special' packets */
634 if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt)
635 free(vpninfo->pending_deflated_pkt);
636 else if (vpninfo->current_ssl_pkt != &dpd_pkt &&
637 vpninfo->current_ssl_pkt != &dpd_resp_pkt &&
638 vpninfo->current_ssl_pkt != &keepalive_pkt)
639 free(vpninfo->current_ssl_pkt);
641 vpninfo->current_ssl_pkt = NULL;
644 if (vpninfo->owe_ssl_dpd_response) {
645 vpninfo->owe_ssl_dpd_response = 0;
646 vpninfo->current_ssl_pkt = &dpd_resp_pkt;
647 goto handle_outgoing;
650 switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
652 /* Not that this will ever happen; we don't even process
653 the setting when we're asked for it. */
654 vpn_progress(vpninfo, PRG_INFO, _("CSTP rekey due\n"));
660 vpn_progress(vpninfo, PRG_ERR,
661 _("CSTP Dead Peer Detection detected dead peer!\n"));
663 if (cstp_reconnect(vpninfo)) {
664 vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n"));
665 vpninfo->quit_reason = "CSTP reconnect failed";
668 /* I think we can leave DTLS to its own devices; when we reconnect
669 with the same master secret, we do seem to get the same sessid */
673 vpn_progress(vpninfo, PRG_TRACE, _("Send CSTP DPD\n"));
675 vpninfo->current_ssl_pkt = &dpd_pkt;
676 goto handle_outgoing;
679 /* No need to send an explicit keepalive
680 if we have real data to send */
681 if (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue)
684 vpn_progress(vpninfo, PRG_TRACE, _("Send CSTP Keepalive\n"));
686 vpninfo->current_ssl_pkt = &keepalive_pkt;
687 goto handle_outgoing;
693 /* Service outgoing packet queue, if no DTLS */
694 while (vpninfo->dtls_fd == -1 && vpninfo->outgoing_queue) {
695 struct pkt *this = vpninfo->outgoing_queue;
696 vpninfo->outgoing_queue = this->next;
697 vpninfo->outgoing_qlen--;
699 if (vpninfo->deflate) {
700 unsigned char *adler;
703 vpninfo->deflate_strm.next_in = this->data;
704 vpninfo->deflate_strm.avail_in = this->len;
705 vpninfo->deflate_strm.next_out = (void *)vpninfo->deflate_pkt->data;
706 vpninfo->deflate_strm.avail_out = 2040;
707 vpninfo->deflate_strm.total_out = 0;
709 ret = deflate(&vpninfo->deflate_strm, Z_SYNC_FLUSH);
711 vpn_progress(vpninfo, PRG_ERR, _("deflate failed %d\n"), ret);
715 vpninfo->deflate_pkt->hdr[4] = (vpninfo->deflate_strm.total_out + 4) >> 8;
716 vpninfo->deflate_pkt->hdr[5] = (vpninfo->deflate_strm.total_out + 4) & 0xff;
718 /* Add ongoing adler32 to tail of compressed packet */
719 vpninfo->deflate_adler32 = adler32(vpninfo->deflate_adler32,
720 this->data, this->len);
722 adler = &vpninfo->deflate_pkt->data[vpninfo->deflate_strm.total_out];
723 *(adler++) = vpninfo->deflate_adler32 >> 24;
724 *(adler++) = (vpninfo->deflate_adler32 >> 16) & 0xff;
725 *(adler++) = (vpninfo->deflate_adler32 >> 8) & 0xff;
726 *(adler) = vpninfo->deflate_adler32 & 0xff;
728 vpninfo->deflate_pkt->len = vpninfo->deflate_strm.total_out + 4;
730 vpn_progress(vpninfo, PRG_TRACE,
731 _("Sending compressed data packet of %d bytes\n"),
734 vpninfo->pending_deflated_pkt = this;
735 vpninfo->current_ssl_pkt = vpninfo->deflate_pkt;
738 memcpy(this->hdr, data_hdr, 8);
739 this->hdr[4] = this->len >> 8;
740 this->hdr[5] = this->len & 0xff;
742 vpn_progress(vpninfo, PRG_TRACE,
743 _("Sending uncompressed data packet of %d bytes\n"),
746 vpninfo->current_ssl_pkt = this;
748 goto handle_outgoing;
751 /* Work is not done if we just got rid of packets off the queue */
755 int cstp_bye(struct openconnect_info *vpninfo, const char *reason)
757 unsigned char *bye_pkt;
760 /* already lost connection? */
761 if (!vpninfo->https_ssl)
764 reason_len = strlen(reason);
765 bye_pkt = malloc(reason_len + 9);
769 memcpy(bye_pkt, data_hdr, 8);
770 memcpy(bye_pkt + 9, reason, reason_len);
772 bye_pkt[4] = (reason_len + 1) >> 8;
773 bye_pkt[5] = (reason_len + 1) & 0xff;
774 bye_pkt[6] = AC_PKT_DISCONN;
777 SSL_write(vpninfo->https_ssl, bye_pkt, reason_len + 9);
780 vpn_progress(vpninfo, PRG_INFO,
781 _("Send BYE packet: %s\n"), reason);