mirror of
https://github.com/OpenVPN/openvpn3.git
synced 2024-09-20 12:12:15 +02:00
b2cd82a5bf
Signed-off-by: James Yonan <james@openvpn.net>
158 lines
4.6 KiB
C++
158 lines
4.6 KiB
C++
// OpenVPN -- An application to securely tunnel IP networks
|
|
// over a single port, with support for SSL/TLS-based
|
|
// session authentication and key exchange,
|
|
// packet encryption, packet authentication, and
|
|
// packet compression.
|
|
//
|
|
// Copyright (C) 2012-2017 OpenVPN Technologies, Inc.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License Version 3
|
|
// 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 General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program in the COPYING file.
|
|
// If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef OPENVPN_BUFFER_ZLIB_H
|
|
#define OPENVPN_BUFFER_ZLIB_H
|
|
|
|
#ifdef OPENVPN_GZIP_DEBUG
|
|
#define OPENVPN_GZIP_VERBOSE true
|
|
#else
|
|
#define OPENVPN_GZIP_VERBOSE false
|
|
#endif
|
|
|
|
#ifdef HAVE_ZLIB
|
|
|
|
#include <cstring> // for std::memset
|
|
|
|
#include <zlib.h>
|
|
|
|
#include <openvpn/common/exception.hpp>
|
|
#include <openvpn/buffer/buffer.hpp>
|
|
#include <openvpn/buffer/buflist.hpp>
|
|
|
|
namespace openvpn {
|
|
namespace ZLib {
|
|
OPENVPN_EXCEPTION(zlib_error);
|
|
|
|
class ZStreamBase // used internally by compress_gzip/decompress_gzip
|
|
{
|
|
public:
|
|
z_stream s;
|
|
protected:
|
|
ZStreamBase() { std::memset(&s, 0, sizeof(s)); }
|
|
private:
|
|
ZStreamBase(const ZStreamBase&) = delete;
|
|
ZStreamBase& operator=(const ZStreamBase&) = delete;
|
|
};
|
|
|
|
inline BufferPtr compress_gzip(BufferPtr src,
|
|
const size_t headroom,
|
|
const size_t tailroom,
|
|
const int level,
|
|
const bool verbose=OPENVPN_GZIP_VERBOSE,
|
|
const int window_bits=15,
|
|
const int mem_level=8)
|
|
{
|
|
constexpr int GZIP_ENCODING = 16;
|
|
|
|
struct ZStream : public ZStreamBase {
|
|
~ZStream() { ::deflateEnd(&s); }
|
|
};
|
|
|
|
if (src)
|
|
{
|
|
int status;
|
|
ZStream zs;
|
|
zs.s.next_in = src->data();
|
|
zs.s.avail_in = src->size();
|
|
status = ::deflateInit2(&zs.s,
|
|
level,
|
|
Z_DEFLATED,
|
|
GZIP_ENCODING + window_bits,
|
|
mem_level,
|
|
Z_DEFAULT_STRATEGY);
|
|
if (status != Z_OK)
|
|
OPENVPN_THROW(zlib_error, "zlib deflateinit2 failed, error=" << status);
|
|
const uLong outcap = ::deflateBound(&zs.s, src->size());
|
|
BufferPtr b = new BufferAllocated(outcap + headroom + tailroom, 0);
|
|
b->init_headroom(headroom);
|
|
zs.s.next_out = b->data();
|
|
zs.s.avail_out = outcap;
|
|
status = ::deflate(&zs.s, Z_FINISH);
|
|
if (status != Z_STREAM_END)
|
|
OPENVPN_THROW(zlib_error, "zlib deflate failed, error=" << status);
|
|
b->set_size(zs.s.total_out);
|
|
if (verbose)
|
|
OPENVPN_LOG("*** COMPRESS " << src->size() << " -> " << b->size());
|
|
return b;
|
|
}
|
|
else
|
|
return BufferPtr();
|
|
}
|
|
|
|
inline BufferPtr decompress_gzip(BufferPtr src,
|
|
const size_t headroom,
|
|
const size_t tailroom,
|
|
const size_t max_size,
|
|
const bool verbose=OPENVPN_GZIP_VERBOSE,
|
|
const size_t block_size=4096,
|
|
const int window_bits=15)
|
|
{
|
|
constexpr int GZIP_ENCODING = 16;
|
|
|
|
struct ZStream : public ZStreamBase {
|
|
~ZStream() { ::inflateEnd(&s); }
|
|
};
|
|
|
|
if (src)
|
|
{
|
|
int status;
|
|
ZStream zs;
|
|
zs.s.next_in = src->data();
|
|
zs.s.avail_in = src->size();
|
|
status = ::inflateInit2(&zs.s, GZIP_ENCODING + window_bits);
|
|
if (status != Z_OK)
|
|
OPENVPN_THROW(zlib_error, "zlib inflateinit2 failed, error=" << status);
|
|
|
|
BufferList blist;
|
|
size_t hr = headroom;
|
|
size_t tr = tailroom;
|
|
do {
|
|
// use headroom/tailroom on first block to take advantage
|
|
// of BufferList::join() optimization for one-block lists
|
|
BufferPtr b = new BufferAllocated(block_size + hr + tr, 0);
|
|
b->init_headroom(hr);
|
|
const size_t avail = b->remaining(tr);
|
|
zs.s.next_out = b->data();
|
|
zs.s.avail_out = avail;
|
|
status = ::inflate(&zs.s, Z_SYNC_FLUSH);
|
|
if (status != Z_OK && status != Z_STREAM_END)
|
|
OPENVPN_THROW(zlib_error, "zlib inflate failed, error=" << status);
|
|
b->set_size(avail - zs.s.avail_out);
|
|
blist.push_back(std::move(b));
|
|
if (max_size && zs.s.total_out > max_size)
|
|
OPENVPN_THROW(zlib_error, "zlib inflate max_size " << max_size << " exceeded");
|
|
hr = tr = 0;
|
|
} while (status == Z_OK);
|
|
if (verbose)
|
|
OPENVPN_LOG("*** DECOMPRESS " << src->size() << " -> " << blist.join_size());
|
|
return blist.join(headroom, tailroom, true);
|
|
}
|
|
else
|
|
return BufferPtr();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#endif
|
|
#endif
|