Commit 84ecff1b authored by David Woodhouse's avatar David Woodhouse

Add decompress-only support for LZS

Newer gateways support LZS compression, and it seems to be mutually
exclusive with deflate.
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 3020d284
......@@ -18,7 +18,7 @@ openconnect_SOURCES = xml.c main.c
openconnect_CFLAGS = $(AM_CFLAGS) $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(ZLIB_CFLAGS) $(LIBSTOKEN_CFLAGS) $(LIBOATH_CFLAGS) $(LIBPSKC_CFLAGS) $(GSSAPI_CFLAGS) $(INTL_CFLAGS) $(ICONV_CFLAGS) $(LIBPCSCLITE_CFLAGS)
openconnect_LDADD = libopenconnect.la $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(INTL_LIBS) $(ICONV_LIBS)
library_srcs = ssl.c http.c auth.c library.c compat.c dtls.c cstp.c \
library_srcs = ssl.c http.c auth.c library.c compat.c dtls.c cstp.c lzs.c \
mainloop.c script.c ntlm.c digest.c
lib_srcs_gnutls = gnutls.c gnutls_pkcs12.c gnutls_tpm.c
lib_srcs_openssl = openssl.c openssl-pkcs11.c
......
......@@ -191,6 +191,10 @@ static int start_cstp_connection(struct openconnect_info *vpninfo)
if (vpninfo->req_compr) {
char sep = ' ';
buf_append(reqbuf, "X-CSTP-Accept-Encoding:");
if (vpninfo->req_compr & COMPR_LZS) {
buf_append(reqbuf, "%clzs", sep);
sep = ',';
}
if (vpninfo->req_compr & COMPR_DEFLATE) {
buf_append(reqbuf, "%cdeflate", sep);
sep = ',';
......@@ -375,6 +379,8 @@ static int start_cstp_connection(struct openconnect_info *vpninfo)
} else if (!strcmp(buf + 7, "Content-Encoding")) {
if (!strcmp(colon, "deflate"))
vpninfo->cstp_compr = COMPR_DEFLATE;
else if (!strcmp(colon, "lzs"))
vpninfo->cstp_compr = COMPR_LZS;
else {
vpn_progress(vpninfo, PRG_ERR,
_("Unknown CSTP-Content-Encoding %s\n"),
......@@ -653,44 +659,61 @@ static int cstp_reconnect(struct openconnect_info *vpninfo)
return 0;
}
static int inflate_and_queue_packet(struct openconnect_info *vpninfo,
unsigned char *buf, int len)
static int decompress_and_queue_packet(struct openconnect_info *vpninfo,
unsigned char *buf, int len)
{
struct pkt *new = malloc(sizeof(struct pkt) + vpninfo->ip_info.mtu);
uint32_t pkt_sum;
const char *comprtype;
if (!new)
return -ENOMEM;
new->next = NULL;
vpninfo->inflate_strm.next_in = buf;
vpninfo->inflate_strm.avail_in = len - 4;
if (vpninfo->cstp_compr == COMPR_DEFLATE) {
uint32_t pkt_sum;
/* Not sure this actually needs to be translated? */
comprtype = _("deflate");
vpninfo->inflate_strm.next_out = new->data;
vpninfo->inflate_strm.avail_out = vpninfo->ip_info.mtu;
vpninfo->inflate_strm.total_out = 0;
vpninfo->inflate_strm.next_in = buf;
vpninfo->inflate_strm.avail_in = len - 4;
if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
vpn_progress(vpninfo, PRG_ERR, _("inflate failed\n"));
free(new);
return -EINVAL;
}
vpninfo->inflate_strm.next_out = new->data;
vpninfo->inflate_strm.avail_out = vpninfo->ip_info.mtu;
vpninfo->inflate_strm.total_out = 0;
new->len = vpninfo->inflate_strm.total_out;
if (inflate(&vpninfo->inflate_strm, Z_SYNC_FLUSH)) {
vpn_progress(vpninfo, PRG_ERR, _("inflate failed\n"));
free(new);
return -EINVAL;
}
new->len = vpninfo->inflate_strm.total_out;
vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
new->data, new->len);
vpninfo->inflate_adler32 = adler32(vpninfo->inflate_adler32,
new->data, new->len);
pkt_sum = buf[len - 1] | (buf[len - 2] << 8) |
(buf[len - 3] << 16) | (buf[len - 4] << 24);
pkt_sum = buf[len - 1] | (buf[len - 2] << 8) |
(buf[len - 3] << 16) | (buf[len - 4] << 24);
if (vpninfo->inflate_adler32 != pkt_sum)
vpninfo->quit_reason = "Compression (inflate) adler32 failure";
if (vpninfo->inflate_adler32 != pkt_sum)
vpninfo->quit_reason = "Compression (inflate) adler32 failure";
} else {
comprtype = "LZS";
new->len = lzs_decompress(new->data, vpninfo->ip_info.mtu, buf, len);
if (new->len < 0) {
vpn_progress(vpninfo, PRG_ERR, _("LZS decompression failed: %s\n"),
strerror(-new->len));
free(new);
return len;
}
}
vpn_progress(vpninfo, PRG_TRACE,
_("Received compressed data packet of %ld bytes\n"),
(long)vpninfo->inflate_strm.total_out);
_("Received %s compressed data packet of %d bytes (was %d)\n"),
comprtype, new->len, len);
queue_packet(&vpninfo->incoming_queue, new);
return 0;
......@@ -880,7 +903,8 @@ int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout)
_("Compressed packet received in !deflate mode\n"));
goto unknown_pkt;
}
inflate_and_queue_packet(vpninfo, vpninfo->cstp_pkt->data, payload_len);
decompress_and_queue_packet(vpninfo, vpninfo->cstp_pkt->data,
payload_len);
work_done = 1;
continue;
......
/*
* OpenConnect (SSL + DTLS) VPN client
*
* Copyright © 2008-2014 Intel Corporation.
* Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#include <config.h>
#include <errno.h>
#include "openconnect-internal.h"
#define GET_BITS(bits) \
do { \
if (srclen < 1 + (bits_left < bits)) \
return -EINVAL; \
/* Explicit comparison with 8 to optimise the bits == 9 case \
* because the compiler doesn't know that bits_left can never \
* be larger than 8. */ \
if (bits >= 8 || bits >= bits_left) { \
/* We need *all* the bits that are left in the current \
* byte. Take them and bump the input pointer. */ \
data = (src[0] << (bits - bits_left)) & ((1ULL << bits) - 1); \
src++; \
srclen--; \
bits_left += 8 - bits; \
if (bits > 8 || bits_left < 8) { \
/* We need bits from the next byte too... */ \
data |= src[0] >> bits_left; \
/* ...if we used *all* of them then bump the \
* input pointer again so we never leave \
* bits_left == 0. */ \
if (!bits_left) { \
bits_left = 8; \
src++; \
srclen--; \
} \
} \
} else { \
/* We need fewer bits than are left in the current byte */ \
data = (src[0] >> (bits_left - bits)) & ((1ULL << bits) - 1); \
bits_left -= bits; \
} \
} while (0)
int lzs_decompress(unsigned char *dst, int dstlen, const unsigned char *src, int srclen)
{
int outlen = 0;
int bits_left = 8; /* Bits left in the current byte at *src */
int data;
int offset, length;
while (1) {
/* Get 9 bits, which is the minimum and a common case */
GET_BITS(9);
/* 0bbbbbbbb is a literal byte */
if (data < 0x100) {
if (outlen == dstlen)
return -EFBIG;
dst[outlen++] = data;
continue;
}
/* 110000000 is the end marker */
if (data == 0x180)
return outlen;
/* 11bbbbbbb is a 7-bit offset */
offset = data & 0x7f;
/* 10bbbbbbbbbbb is an 11-bit offset, so get the next 4 bits */
if (data < 0x180) {
GET_BITS(4);
offset <<= 4;
offset |= data;
}
/* This is a compressed sequence; now get the length */
GET_BITS(2);
if (data != 3) {
/* 00, 01, 10 ==> 2, 3, 4 */
length = data + 2;
} else {
GET_BITS(2);
if (data != 3) {
/* 1100, 1101, 1110 => 5, 6, 7 */
length = data + 5;
} else {
/* For each 1111 prefix add 15 to the length. Then add
the value of final nybble. */
length = 8;
while (1) {
GET_BITS(4);
if (data != 15) {
length += data;
break;
}
length += 15;
}
}
}
if (offset > outlen)
return -EINVAL;
if (length + outlen > dstlen)
return -EFBIG;
while (length) {
dst[outlen] = dst[outlen - offset];
outlen++;
length--;
}
}
return -EINVAL;
}
......@@ -1429,7 +1429,8 @@ int main(int argc, char **argv)
(ip_info->netmask6 && ip_info->addr) ? " + " : "",
ip_info->netmask6 ? : "",
(vpninfo->dtls_state != DTLS_CONNECTED) ?
(vpninfo->cstp_compr == COMPR_DEFLATE) ? "SSL + deflate" : "SSL"
(vpninfo->cstp_compr == COMPR_DEFLATE) ? "SSL + deflate" :
(vpninfo->cstp_compr == COMPR_LZS) ? "SSL + lzs" : "SSL"
: "DTLS");
if (!vpninfo->vpnc_script) {
......
......@@ -141,7 +141,8 @@ struct pkt {
#define DTLS_CONNECTED 4
#define COMPR_DEFLATE (1<<0)
#define COMPR_ALL (COMPR_DEFLATE)
#define COMPR_LZS (1<<1)
#define COMPR_ALL (COMPR_DEFLATE | COMPR_LZS)
struct keepalive_info {
int dpd;
......@@ -631,6 +632,9 @@ int cstp_mainloop(struct openconnect_info *vpninfo, int *timeout);
int cstp_bye(struct openconnect_info *vpninfo, const char *reason);
void cstp_free_splits(struct openconnect_info *vpninfo);
/* lzs.c */
int lzs_decompress(unsigned char *dst, int dstlen, const unsigned char *src, int srclen);
/* ssl.c */
unsigned string_is_hostname(const char* str);
int connect_https_socket(struct openconnect_info *vpninfo);
......
......@@ -15,7 +15,7 @@
<ul>
<li><b>OpenConnect HEAD</b>
<ul>
<li><i>No changelog entries yet</i></li>
<li>Add support for LZS decompression.</li>
</ul><br/>
</li>
<li><b><a href="ftp://ftp.infradead.org/pub/openconnect/openconnect-7.02.tar.gz">OpenConnect v7.02</a></b>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment