mirror of
https://github.com/deneraraujo/OpenVPNAdapter.git
synced 2026-04-24 00:00:05 +08:00
Merge commit '86cc97e55fe346502462284d2e636a2b3708163e' as 'Sources/OpenVPN3'
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// A null compression class.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_COMPNULL_H
|
||||
#define OPENVPN_COMPRESS_COMPNULL_H
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressNull : public Compress
|
||||
{
|
||||
public:
|
||||
CompressNull(const Frame::Ptr& frame, const SessionStats::Ptr& stats)
|
||||
: Compress(frame, stats)
|
||||
{
|
||||
}
|
||||
|
||||
virtual const char *name() const { return "null"; }
|
||||
virtual void compress(BufferAllocated& buf, const bool hint) {}
|
||||
virtual void decompress(BufferAllocated& buf) {}
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_COMPNULL_H
|
||||
@@ -0,0 +1,548 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Base class and factory for compression/decompression objects.
|
||||
// Currently we support LZO, Snappy, and LZ4 implementations.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_COMPRESS_H
|
||||
#define OPENVPN_COMPRESS_COMPRESS_H
|
||||
|
||||
#include <openvpn/common/size.hpp>
|
||||
#include <openvpn/common/exception.hpp>
|
||||
#include <openvpn/common/rc.hpp>
|
||||
#include <openvpn/common/likely.hpp>
|
||||
#include <openvpn/buffer/buffer.hpp>
|
||||
#include <openvpn/frame/frame.hpp>
|
||||
#include <openvpn/log/sessionstats.hpp>
|
||||
|
||||
#define OPENVPN_LOG_COMPRESS(x)
|
||||
#define OPENVPN_LOG_COMPRESS_VERBOSE(x)
|
||||
|
||||
#if defined(OPENVPN_DEBUG_COMPRESS)
|
||||
#if OPENVPN_DEBUG_COMPRESS >= 1
|
||||
#undef OPENVPN_LOG_COMPRESS
|
||||
#define OPENVPN_LOG_COMPRESS(x) OPENVPN_LOG(x)
|
||||
#endif
|
||||
#if OPENVPN_DEBUG_COMPRESS >= 2
|
||||
#undef OPENVPN_LOG_COMPRESS_VERBOSE
|
||||
#define OPENVPN_LOG_COMPRESS_VERBOSE(x) OPENVPN_LOG(x)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
class Compress : public RC<thread_unsafe_refcount>
|
||||
{
|
||||
public:
|
||||
typedef RCPtr<Compress> Ptr;
|
||||
|
||||
// Compressor name
|
||||
virtual const char *name() const = 0;
|
||||
|
||||
// Compression method implemented by underlying compression class.
|
||||
// hint should normally be true to compress the data. If hint is
|
||||
// false, the data may be uncompressible or already compressed,
|
||||
// so method shouldn't attempt compression.
|
||||
virtual void compress(BufferAllocated& buf, const bool hint) = 0;
|
||||
|
||||
// Decompression method implemented by underlying compression class.
|
||||
virtual void decompress(BufferAllocated& buf) = 0;
|
||||
|
||||
protected:
|
||||
// magic numbers to indicate no compression
|
||||
enum {
|
||||
NO_COMPRESS = 0xFA,
|
||||
NO_COMPRESS_SWAP = 0xFB, // for better alignment handling, replace this byte with last byte of packet
|
||||
};
|
||||
|
||||
// Compress V2 constants
|
||||
enum {
|
||||
COMPRESS_V2_ESCAPE=0x50,
|
||||
|
||||
// Compression algs
|
||||
OVPN_COMPv2_NONE=0,
|
||||
OVPN_COMPv2_LZ4=1,
|
||||
};
|
||||
|
||||
Compress(const Frame::Ptr& frame_arg,
|
||||
const SessionStats::Ptr& stats_arg)
|
||||
: frame(frame_arg), stats(stats_arg) {}
|
||||
|
||||
void error(BufferAllocated& buf)
|
||||
{
|
||||
stats->error(Error::COMPRESS_ERROR);
|
||||
buf.reset_size();
|
||||
}
|
||||
|
||||
void do_swap(Buffer& buf, unsigned char op)
|
||||
{
|
||||
if (buf.size())
|
||||
{
|
||||
buf.push_back(buf[0]);
|
||||
buf[0] = op;
|
||||
}
|
||||
else
|
||||
buf.push_back(op);
|
||||
}
|
||||
|
||||
void do_unswap(Buffer& buf)
|
||||
{
|
||||
if (buf.size() >= 2)
|
||||
{
|
||||
const unsigned char first = buf.pop_back();
|
||||
buf.push_front(first);
|
||||
}
|
||||
}
|
||||
|
||||
// Push a COMPRESS_V2 header byte (value).
|
||||
// Pass value == 0 to omit push.
|
||||
void v2_push(Buffer& buf, int value)
|
||||
{
|
||||
unsigned char uc = buf[0];
|
||||
if (value == 0 && uc != COMPRESS_V2_ESCAPE)
|
||||
return;
|
||||
unsigned char *esc = buf.prepend_alloc(2);
|
||||
esc[0] = COMPRESS_V2_ESCAPE;
|
||||
esc[1] = value;
|
||||
}
|
||||
|
||||
// Pull a COMPRESS_V2 header byte.
|
||||
// Returns the compress op (> 0) on success.
|
||||
// Returns 0 if no compress op.
|
||||
int v2_pull(Buffer& buf)
|
||||
{
|
||||
unsigned char uc = buf[0];
|
||||
if (uc != COMPRESS_V2_ESCAPE)
|
||||
return 0;
|
||||
uc = buf[1];
|
||||
buf.advance(2);
|
||||
return uc;
|
||||
}
|
||||
|
||||
Frame::Ptr frame;
|
||||
SessionStats::Ptr stats;
|
||||
};
|
||||
}
|
||||
|
||||
// include compressor implementations here
|
||||
#include <openvpn/compress/compnull.hpp>
|
||||
#include <openvpn/compress/compstub.hpp>
|
||||
|
||||
#ifndef NO_LZO
|
||||
#include <openvpn/compress/lzoselect.hpp>
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
#include <openvpn/compress/lz4.hpp>
|
||||
#endif
|
||||
#ifdef HAVE_SNAPPY
|
||||
#include <openvpn/compress/snappy.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
class CompressContext
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
NONE,
|
||||
COMP_STUB, // generic compression stub
|
||||
COMP_STUBv2, // generic compression stub using v2 protocol
|
||||
ANY, // placeholder for any method on client, before server assigns it
|
||||
ANY_LZO, // placeholder for LZO or LZO_STUB methods on client, before server assigns it
|
||||
LZO,
|
||||
LZO_SWAP,
|
||||
LZO_STUB,
|
||||
LZ4,
|
||||
LZ4v2,
|
||||
SNAPPY,
|
||||
};
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(compressor_unavailable);
|
||||
|
||||
CompressContext() {}
|
||||
|
||||
CompressContext(const Type t, const bool asym)
|
||||
: asym_(asym) // asym indicates asymmetrical compression where only downlink is compressed
|
||||
{
|
||||
if (!compressor_available(t))
|
||||
throw compressor_unavailable();
|
||||
type_ = t;
|
||||
}
|
||||
|
||||
Type type() const { return type_; }
|
||||
bool asym() const { return asym_; }
|
||||
|
||||
unsigned int extra_payload_bytes() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case NONE:
|
||||
return 0;
|
||||
case COMP_STUBv2:
|
||||
case LZ4v2:
|
||||
return 2; // worst case
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
Compress::Ptr new_compressor(const Frame::Ptr& frame, const SessionStats::Ptr& stats)
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case NONE:
|
||||
return new CompressNull(frame, stats);
|
||||
case ANY:
|
||||
case ANY_LZO:
|
||||
case LZO_STUB:
|
||||
return new CompressStub(frame, stats, false);
|
||||
case COMP_STUB:
|
||||
return new CompressStub(frame, stats, true);
|
||||
case COMP_STUBv2:
|
||||
return new CompressStubV2(frame, stats);
|
||||
#ifndef NO_LZO
|
||||
case LZO:
|
||||
return new CompressLZO(frame, stats, false, asym_);
|
||||
case LZO_SWAP:
|
||||
return new CompressLZO(frame, stats, true, asym_);
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
case LZ4:
|
||||
return new CompressLZ4(frame, stats, asym_);
|
||||
case LZ4v2:
|
||||
return new CompressLZ4v2(frame, stats, asym_);
|
||||
#endif
|
||||
#ifdef HAVE_SNAPPY
|
||||
case SNAPPY:
|
||||
return new CompressSnappy(frame, stats, asym_);
|
||||
#endif
|
||||
default:
|
||||
throw compressor_unavailable();
|
||||
}
|
||||
}
|
||||
|
||||
static bool compressor_available(const Type t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case NONE:
|
||||
case ANY:
|
||||
case ANY_LZO:
|
||||
case LZO_STUB:
|
||||
case COMP_STUB:
|
||||
case COMP_STUBv2:
|
||||
return true;
|
||||
case LZO:
|
||||
case LZO_SWAP:
|
||||
#ifndef NO_LZO
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case LZ4:
|
||||
#ifdef HAVE_LZ4
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case LZ4v2:
|
||||
#ifdef HAVE_LZ4
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
case SNAPPY:
|
||||
#ifdef HAVE_SNAPPY
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// On the client, used to tell server which compression methods we support.
|
||||
// Includes compression V1 and V2 methods.
|
||||
const char *peer_info_string() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
#ifndef NO_LZO
|
||||
case LZO:
|
||||
return "IV_LZO=1\n";
|
||||
case LZO_SWAP:
|
||||
return "IV_LZO_SWAP=1\n";
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
case LZ4:
|
||||
return "IV_LZ4=1\n";
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
case LZ4v2:
|
||||
return "IV_LZ4v2=1\n";
|
||||
#endif
|
||||
#ifdef HAVE_SNAPPY
|
||||
case SNAPPY:
|
||||
return "IV_SNAPPY=1\n";
|
||||
#endif
|
||||
case LZO_STUB:
|
||||
case COMP_STUB:
|
||||
case COMP_STUBv2:
|
||||
return
|
||||
"IV_LZO_STUB=1\n"
|
||||
"IV_COMP_STUB=1\n"
|
||||
"IV_COMP_STUBv2=1\n"
|
||||
;
|
||||
case ANY:
|
||||
return
|
||||
#ifdef HAVE_SNAPPY
|
||||
"IV_SNAPPY=1\n"
|
||||
#endif
|
||||
#ifndef NO_LZO
|
||||
"IV_LZO=1\n"
|
||||
"IV_LZO_SWAP=1\n"
|
||||
#else
|
||||
"IV_LZO_STUB=1\n"
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
"IV_LZ4=1\n"
|
||||
"IV_LZ4v2=1\n"
|
||||
#endif
|
||||
"IV_COMP_STUB=1\n"
|
||||
"IV_COMP_STUBv2=1\n"
|
||||
;
|
||||
case ANY_LZO:
|
||||
return
|
||||
#ifndef NO_LZO
|
||||
"IV_LZO=1\n"
|
||||
"IV_LZO_SWAP=1\n"
|
||||
#else
|
||||
"IV_LZO_STUB=1\n"
|
||||
#endif
|
||||
"IV_COMP_STUB=1\n"
|
||||
"IV_COMP_STUBv2=1\n"
|
||||
;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// On the client, used to tell server which compression methods we support.
|
||||
// Limited only to compression V1 methods.
|
||||
const char *peer_info_string_v1() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
#ifndef NO_LZO
|
||||
case LZO:
|
||||
return "IV_LZO=1\n";
|
||||
case LZO_SWAP:
|
||||
return "IV_LZO_SWAP=1\n";
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
case LZ4:
|
||||
return "IV_LZ4=1\n";
|
||||
#endif
|
||||
#ifdef HAVE_SNAPPY
|
||||
case SNAPPY:
|
||||
return "IV_SNAPPY=1\n";
|
||||
#endif
|
||||
case LZO_STUB:
|
||||
case COMP_STUB:
|
||||
return
|
||||
"IV_LZO_STUB=1\n"
|
||||
"IV_COMP_STUB=1\n"
|
||||
;
|
||||
case ANY:
|
||||
return
|
||||
#ifdef HAVE_SNAPPY
|
||||
"IV_SNAPPY=1\n"
|
||||
#endif
|
||||
#ifndef NO_LZO
|
||||
"IV_LZO=1\n"
|
||||
"IV_LZO_SWAP=1\n"
|
||||
#else
|
||||
"IV_LZO_STUB=1\n"
|
||||
#endif
|
||||
#ifdef HAVE_LZ4
|
||||
"IV_LZ4=1\n"
|
||||
#endif
|
||||
"IV_COMP_STUB=1\n"
|
||||
;
|
||||
case ANY_LZO:
|
||||
return
|
||||
#ifndef NO_LZO
|
||||
"IV_LZO=1\n"
|
||||
"IV_LZO_SWAP=1\n"
|
||||
#else
|
||||
"IV_LZO_STUB=1\n"
|
||||
#endif
|
||||
"IV_COMP_STUB=1\n"
|
||||
;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const char *options_string() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case LZO:
|
||||
case LZO_STUB:
|
||||
case SNAPPY:
|
||||
case LZ4:
|
||||
case LZ4v2:
|
||||
case LZO_SWAP:
|
||||
case COMP_STUB:
|
||||
case COMP_STUBv2:
|
||||
case ANY:
|
||||
case ANY_LZO:
|
||||
return "comp-lzo";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const char *str() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case LZO:
|
||||
return "LZO";
|
||||
case LZO_SWAP:
|
||||
return "LZO_SWAP";
|
||||
case LZ4:
|
||||
return "LZ4";
|
||||
case LZ4v2:
|
||||
return "LZ4v2";
|
||||
case SNAPPY:
|
||||
return "SNAPPY";
|
||||
case LZO_STUB:
|
||||
return "LZO_STUB";
|
||||
case COMP_STUB:
|
||||
return "COMP_STUB";
|
||||
case COMP_STUBv2:
|
||||
return "COMP_STUBv2";
|
||||
case ANY:
|
||||
return "ANY";
|
||||
case ANY_LZO:
|
||||
return "ANY_LZO";
|
||||
default:
|
||||
return "NONE";
|
||||
}
|
||||
}
|
||||
|
||||
/* This function returns a parseable string representation of the compress
|
||||
* method. NOTE: returns nullptr if no mapping is possible */
|
||||
const char *method_to_string() const
|
||||
{
|
||||
switch (type_)
|
||||
{
|
||||
case LZO:
|
||||
return "lzo";
|
||||
case LZO_SWAP:
|
||||
return "lzo-swap";
|
||||
case LZO_STUB:
|
||||
return "lzo-stub";
|
||||
case LZ4:
|
||||
return "lz4";
|
||||
case LZ4v2:
|
||||
return "lz4v2";
|
||||
case SNAPPY:
|
||||
return "snappy";
|
||||
case COMP_STUB:
|
||||
return "stub";
|
||||
case COMP_STUBv2:
|
||||
return "stub-v2";
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static Type parse_method(const std::string& method)
|
||||
{
|
||||
if (method == "stub-v2")
|
||||
return COMP_STUBv2;
|
||||
else if (method == "lz4-v2")
|
||||
return LZ4v2;
|
||||
else if (method == "lz4")
|
||||
return LZ4;
|
||||
else if (method == "lzo")
|
||||
return LZO;
|
||||
else if (method == "lzo-swap")
|
||||
return LZO_SWAP;
|
||||
else if (method == "lzo-stub")
|
||||
return LZO_STUB;
|
||||
else if (method == "snappy")
|
||||
return SNAPPY;
|
||||
else if (method == "stub")
|
||||
return COMP_STUB;
|
||||
else
|
||||
return NONE;
|
||||
}
|
||||
|
||||
static Type stub(const Type t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case COMP_STUBv2:
|
||||
case LZ4v2:
|
||||
return COMP_STUBv2;
|
||||
default:
|
||||
return COMP_STUB;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the compression type is one of the available stub modes
|
||||
*
|
||||
* @param t The CompressContext::Type value
|
||||
* @return Returns true if the type is one of the *_STUB{,v2} types,
|
||||
* otherwise false.
|
||||
*/
|
||||
static bool is_any_stub(const Type t)
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case LZO_STUB:
|
||||
case COMP_STUB:
|
||||
case COMP_STUBv2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void init_static()
|
||||
{
|
||||
#ifndef NO_LZO
|
||||
CompressLZO::init_static();
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_ = NONE;
|
||||
bool asym_ = false;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_COMPRESS_H
|
||||
@@ -0,0 +1,140 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This is a "stub" compression object. It acts like a compressor
|
||||
// in the sense that it plays along with compression framing in
|
||||
// the OpenVPN protocol, but it always sends packets with NO_COMPRESS
|
||||
// or NO_COMPRESS_SWAP compression status. While it's not designed
|
||||
// to receive compressed packets, it will try to handle received LZO
|
||||
// packets, but it will never send compressed packets.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_COMPSTUB_H
|
||||
#define OPENVPN_COMPRESS_COMPSTUB_H
|
||||
|
||||
#ifndef NO_LZO
|
||||
#include <openvpn/compress/lzoselect.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressStub : public Compress
|
||||
{
|
||||
public:
|
||||
CompressStub(const Frame::Ptr& frame, const SessionStats::Ptr& stats, const bool support_swap_arg)
|
||||
: Compress(frame, stats),
|
||||
support_swap(support_swap_arg)
|
||||
#ifndef NO_LZO
|
||||
,lzo(frame, stats, false, true)
|
||||
#endif
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS("Comp-stub init swap=" << support_swap_arg);
|
||||
}
|
||||
|
||||
virtual const char *name() const { return "stub"; }
|
||||
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
// indicate that we didn't compress
|
||||
if (support_swap)
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
else
|
||||
buf.push_front(NO_COMPRESS);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
case NO_COMPRESS:
|
||||
break;
|
||||
#ifndef NO_LZO
|
||||
// special mode to support older servers that ignore
|
||||
// compression handshake -- this will handle receiving
|
||||
// compressed packets even if we didn't ask for them
|
||||
case CompressLZO::LZO_COMPRESS:
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("CompressStub: handled unsolicited LZO packet");
|
||||
lzo.decompress_work(buf);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("CompressStub: unable to handle op=" << int(c));
|
||||
error(buf);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const bool support_swap;
|
||||
#ifndef NO_LZO
|
||||
CompressLZO lzo;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Compression stub using V2 protocol
|
||||
class CompressStubV2 : public Compress
|
||||
{
|
||||
public:
|
||||
CompressStubV2(const Frame::Ptr& frame, const SessionStats::Ptr& stats)
|
||||
: Compress(frame, stats)
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS("Comp-stubV2 init");
|
||||
}
|
||||
|
||||
virtual const char *name() const { return "stubv2"; }
|
||||
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
// indicate that we didn't compress
|
||||
v2_push(buf, OVPN_COMPv2_NONE);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const int cop = v2_pull(buf);
|
||||
if (cop)
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("CompressStubV2: unable to handle op=" << c);
|
||||
error(buf);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_COMPSTUB_H
|
||||
@@ -0,0 +1,227 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_LZ4_H
|
||||
#define OPENVPN_COMPRESS_LZ4_H
|
||||
|
||||
// Implement LZ4 compression.
|
||||
// Should only be included by compress.hpp
|
||||
|
||||
#include <algorithm> // for std::max
|
||||
|
||||
#include <lz4.h>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressLZ4Base : public Compress
|
||||
{
|
||||
protected:
|
||||
CompressLZ4Base(const Frame::Ptr& frame, const SessionStats::Ptr& stats)
|
||||
: Compress(frame, stats)
|
||||
{
|
||||
}
|
||||
|
||||
bool do_decompress(BufferAllocated& buf)
|
||||
{
|
||||
// initialize work buffer
|
||||
const int payload_size = frame->prepare(Frame::DECOMPRESS_WORK, work);
|
||||
|
||||
// do uncompress
|
||||
const int decomp_size = LZ4_decompress_safe((const char *)buf.c_data(), (char *)work.data(),
|
||||
(int)buf.size(), payload_size);
|
||||
if (decomp_size < 0)
|
||||
{
|
||||
error(buf);
|
||||
return false;
|
||||
}
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("LZ4 uncompress " << buf.size() << " -> " << decomp_size);
|
||||
work.set_size(decomp_size);
|
||||
buf.swap(work);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool do_compress(BufferAllocated& buf)
|
||||
{
|
||||
// initialize work buffer
|
||||
frame->prepare(Frame::COMPRESS_WORK, work);
|
||||
|
||||
// verify that input data length is not too large
|
||||
if (lz4_extra_buffer(buf.size()) > work.max_size())
|
||||
{
|
||||
error(buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
// do compress
|
||||
const int comp_size = LZ4_compress_default((const char *)buf.c_data(), (char *)work.data(),
|
||||
(int)buf.size(), (int)work.capacity());
|
||||
|
||||
// did compression actually reduce data length?
|
||||
if (comp_size < buf.size())
|
||||
{
|
||||
if (comp_size <= 0)
|
||||
{
|
||||
error(buf);
|
||||
return false;
|
||||
}
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("LZ4 compress " << buf.size() << " -> " << comp_size);
|
||||
work.set_size(comp_size);
|
||||
buf.swap(work);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Worst case size expansion on compress.
|
||||
// Official LZ4 worst-case size expansion alg is
|
||||
// LZ4_COMPRESSBOUND macro in lz4.h.
|
||||
// However we optimize it slightly here to lose the integer division
|
||||
// when len < 65535.
|
||||
size_t lz4_extra_buffer(const size_t len)
|
||||
{
|
||||
if (likely(len < 65535))
|
||||
return len + len/256 + 17;
|
||||
else
|
||||
return len + len/255 + 16;
|
||||
}
|
||||
|
||||
BufferAllocated work;
|
||||
};
|
||||
|
||||
class CompressLZ4 : public CompressLZ4Base
|
||||
{
|
||||
// magic number for LZ4 compression
|
||||
enum {
|
||||
LZ4_COMPRESS = 0x69,
|
||||
};
|
||||
|
||||
public:
|
||||
CompressLZ4(const Frame::Ptr& frame, const SessionStats::Ptr& stats, const bool asym_arg)
|
||||
: CompressLZ4Base(frame, stats),
|
||||
asym(asym_arg)
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS("LZ4 init asym=" << asym_arg);
|
||||
}
|
||||
|
||||
virtual const char *name() const { return "lz4"; }
|
||||
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
if (hint && !asym)
|
||||
{
|
||||
if (do_compress(buf))
|
||||
{
|
||||
do_swap(buf, LZ4_COMPRESS);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// indicate that we didn't compress
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
break;
|
||||
case LZ4_COMPRESS:
|
||||
do_unswap(buf);
|
||||
do_decompress(buf);
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const bool asym;
|
||||
};
|
||||
|
||||
class CompressLZ4v2 : public CompressLZ4Base
|
||||
{
|
||||
public:
|
||||
CompressLZ4v2(const Frame::Ptr& frame, const SessionStats::Ptr& stats, const bool asym_arg)
|
||||
: CompressLZ4Base(frame, stats),
|
||||
asym(asym_arg)
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS("LZ4v2 init asym=" << asym_arg);
|
||||
}
|
||||
|
||||
virtual const char *name() const { return "lz4v2"; }
|
||||
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
if (hint && !asym)
|
||||
{
|
||||
if (do_compress(buf))
|
||||
{
|
||||
v2_push(buf, OVPN_COMPv2_LZ4);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// indicate that we didn't compress
|
||||
v2_push(buf, OVPN_COMPv2_NONE);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const int c = v2_pull(buf);
|
||||
switch (c)
|
||||
{
|
||||
case OVPN_COMPv2_NONE:
|
||||
break;
|
||||
case OVPN_COMPv2_LZ4:
|
||||
do_decompress(buf);
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const bool asym;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,169 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_LZO_H
|
||||
#define OPENVPN_COMPRESS_LZO_H
|
||||
|
||||
// Implement LZO compression.
|
||||
// Should only be included by lzoselect.hpp
|
||||
|
||||
#include "lzo/lzoutil.h"
|
||||
#include "lzo/lzo1x.h"
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressLZO : public Compress
|
||||
{
|
||||
public:
|
||||
// magic number for LZO compression
|
||||
enum {
|
||||
LZO_COMPRESS = 0x66,
|
||||
LZO_COMPRESS_SWAP = 0x67,
|
||||
};
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(lzo_init_failed);
|
||||
|
||||
CompressLZO(const Frame::Ptr& frame,
|
||||
const SessionStats::Ptr& stats,
|
||||
const bool support_swap_arg,
|
||||
const bool asym_arg)
|
||||
: Compress(frame, stats),
|
||||
support_swap(support_swap_arg),
|
||||
asym(asym_arg)
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS("LZO init swap=" << support_swap_arg << " asym=" << asym_arg);
|
||||
lzo_workspace.init(LZO1X_1_15_MEM_COMPRESS, BufferAllocated::ARRAY);
|
||||
}
|
||||
|
||||
static void init_static()
|
||||
{
|
||||
if (::lzo_init() != LZO_E_OK)
|
||||
throw lzo_init_failed();
|
||||
}
|
||||
|
||||
virtual const char *name() const { return "lzo"; }
|
||||
|
||||
void decompress_work(BufferAllocated& buf)
|
||||
{
|
||||
// initialize work buffer
|
||||
lzo_uint zlen = frame->prepare(Frame::DECOMPRESS_WORK, work);
|
||||
|
||||
// do uncompress
|
||||
const int err = lzo1x_decompress_safe(buf.c_data(), buf.size(), work.data(), &zlen, lzo_workspace.data());
|
||||
if (err != LZO_E_OK)
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("LZO uncompress " << buf.size() << " -> " << zlen);
|
||||
work.set_size(zlen);
|
||||
buf.swap(work);
|
||||
}
|
||||
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
if (hint && !asym)
|
||||
{
|
||||
// initialize work buffer
|
||||
frame->prepare(Frame::COMPRESS_WORK, work);
|
||||
|
||||
// verify that input data length is not too large
|
||||
if (lzo_extra_buffer(buf.size()) > work.max_size())
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
// do compress
|
||||
lzo_uint zlen = 0;
|
||||
const int err = ::lzo1x_1_15_compress(buf.c_data(), buf.size(), work.data(), &zlen, lzo_workspace.data());
|
||||
|
||||
// check for errors
|
||||
if (err != LZO_E_OK)
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
// did compression actually reduce data length?
|
||||
if (zlen < buf.size())
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("LZO compress " << buf.size() << " -> " << zlen);
|
||||
work.set_size(zlen);
|
||||
if (support_swap)
|
||||
do_swap(work, LZO_COMPRESS_SWAP);
|
||||
else
|
||||
work.push_front(LZO_COMPRESS);
|
||||
buf.swap(work);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// indicate that we didn't compress
|
||||
if (support_swap)
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
else
|
||||
buf.push_front(NO_COMPRESS);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
case NO_COMPRESS:
|
||||
break;
|
||||
case LZO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
case LZO_COMPRESS:
|
||||
decompress_work(buf);
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// worst case size expansion on compress
|
||||
size_t lzo_extra_buffer(const size_t len)
|
||||
{
|
||||
return len + len/8 + 128 + 3;
|
||||
}
|
||||
|
||||
const bool support_swap;
|
||||
const bool asym;
|
||||
BufferAllocated work;
|
||||
BufferAllocated lzo_workspace;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_LZO_H
|
||||
@@ -0,0 +1,119 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_LZOASYM_H
|
||||
#define OPENVPN_COMPRESS_LZOASYM_H
|
||||
|
||||
#include <openvpn/compress/lzoasym_impl.hpp>
|
||||
|
||||
// Implement asymmetrical LZO compression (only uncompress, don't compress)
|
||||
// Should only be included by lzoselect.hpp
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressLZOAsym : public Compress
|
||||
{
|
||||
public:
|
||||
// magic number for LZO compression
|
||||
enum {
|
||||
LZO_COMPRESS = 0x66,
|
||||
LZO_COMPRESS_SWAP = 0x67,
|
||||
};
|
||||
|
||||
OPENVPN_SIMPLE_EXCEPTION(lzo_init_failed);
|
||||
|
||||
CompressLZOAsym(const Frame::Ptr& frame,
|
||||
const SessionStats::Ptr& stats,
|
||||
const bool support_swap_arg,
|
||||
const bool asym_arg) // we are always asymmetrical, regardless of setting
|
||||
: Compress(frame, stats),
|
||||
support_swap(support_swap_arg)
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS("LZO-ASYM init swap=" << support_swap_arg << " asym=" << asym_arg);
|
||||
}
|
||||
|
||||
static void init_static()
|
||||
{
|
||||
}
|
||||
|
||||
virtual const char *name() const { return "lzo-asym"; }
|
||||
|
||||
void decompress_work(BufferAllocated& buf)
|
||||
{
|
||||
// initialize work buffer
|
||||
size_t zlen = frame->prepare(Frame::DECOMPRESS_WORK, work);
|
||||
|
||||
// do uncompress
|
||||
const int err = lzo_asym_impl::lzo1x_decompress_safe(buf.c_data(), buf.size(), work.data(), &zlen);
|
||||
if (err != lzo_asym_impl::LZOASYM_E_OK)
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("LZO-ASYM uncompress " << buf.size() << " -> " << zlen);
|
||||
work.set_size(zlen);
|
||||
buf.swap(work);
|
||||
}
|
||||
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
// indicate that we didn't compress
|
||||
if (support_swap)
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
else
|
||||
buf.push_front(NO_COMPRESS);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
case NO_COMPRESS:
|
||||
break;
|
||||
case LZO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
case LZO_COMPRESS:
|
||||
decompress_work(buf);
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const bool support_swap;
|
||||
BufferAllocated work;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_LZOASYM_H
|
||||
@@ -0,0 +1,345 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// This is a special OpenVPN-specific implementation of LZO decompression.
|
||||
// It is generally only used when OpenVPN is built without linkage to the
|
||||
// actual LZO library, but where we want to maintain compatibility with
|
||||
// peers that might send us LZO-compressed packets.
|
||||
//
|
||||
// It is significantly faster than LZO 2 on ARM because it makes heavy use
|
||||
// of branch prediction hints.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_LZOASYM_IMPL_H
|
||||
#define OPENVPN_COMPRESS_LZOASYM_IMPL_H
|
||||
|
||||
#include <cstdint> // for std::uint32_t, etc.
|
||||
#include <cstring> // for memcpy, memmove
|
||||
#include <algorithm>
|
||||
|
||||
#include <openvpn/common/size.hpp> // for ssize_t
|
||||
#include <openvpn/common/likely.hpp> // for likely/unlikely
|
||||
|
||||
// Implementation of asymmetrical LZO compression (only uncompress, don't compress)
|
||||
|
||||
// Branch prediction hints (these make a difference on ARM)
|
||||
# define LZOASYM_LIKELY(x) likely(x)
|
||||
# define LZOASYM_UNLIKELY(x) unlikely(x)
|
||||
|
||||
// Failure modes
|
||||
#define LZOASYM_CHECK_INPUT_OVERFLOW(x) if (LZOASYM_UNLIKELY(int(input_ptr_end - input_ptr) < int(x))) goto input_overflow
|
||||
#define LZOASYM_CHECK_OUTPUT_OVERFLOW(x) if (LZOASYM_UNLIKELY(int(output_ptr_end - output_ptr) < int(x))) goto output_overflow
|
||||
#define LZOASYM_CHECK_MATCH_OVERFLOW(match_ptr) if (LZOASYM_UNLIKELY(match_ptr < output) || LZOASYM_UNLIKELY(match_ptr >= output_ptr)) goto match_overflow
|
||||
#define LZOASYM_ASSERT(cond) if (LZOASYM_UNLIKELY(!(cond))) goto assert_fail
|
||||
|
||||
namespace openvpn {
|
||||
namespace lzo_asym_impl {
|
||||
// Return status values
|
||||
enum {
|
||||
LZOASYM_E_OK=0,
|
||||
LZOASYM_E_EOF_NOT_FOUND=-1,
|
||||
LZOASYM_E_INPUT_NOT_CONSUMED=-2,
|
||||
LZOASYM_E_INPUT_OVERFLOW=-3,
|
||||
LZOASYM_E_OUTPUT_OVERFLOW=-4,
|
||||
LZOASYM_E_MATCH_OVERFLOW=-5,
|
||||
LZOASYM_E_ASSERT_FAILED=-6,
|
||||
LZOASYM_E_INPUT_TOO_LARGE=-7,
|
||||
};
|
||||
|
||||
// Internal constants
|
||||
enum {
|
||||
LZOASYM_EOF_CODE=1,
|
||||
LZOASYM_M2_MAX_OFFSET=0x0800,
|
||||
};
|
||||
|
||||
// Polymorphic get/set/copy
|
||||
|
||||
template <typename T>
|
||||
inline T get_mem(const void *p)
|
||||
{
|
||||
typedef volatile const T* cptr;
|
||||
return *cptr(p);
|
||||
}
|
||||
|
||||
// take the number of objects difference between two pointers
|
||||
template <typename T>
|
||||
inline size_t ptr_diff(const T* a, const T* b)
|
||||
{
|
||||
return a - b;
|
||||
}
|
||||
|
||||
// read uint16_t from memory
|
||||
inline size_t get_u16(const unsigned char *p)
|
||||
{
|
||||
// NOTE: assumes little-endian and unaligned 16-bit access is okay.
|
||||
// For a slower alternative without these assumptions, try: p[0] | (p[1] << 8)
|
||||
return get_mem<std::uint16_t>(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function emulates copying bytes one by one from src to dest.
|
||||
* if src+len and dest+len overlap it repeats the non-overlapping
|
||||
* section of src until it copied 'len' bytes
|
||||
*
|
||||
* A slow simple version of this method looks like this:
|
||||
*
|
||||
* do {
|
||||
* *dest++ = *src++;
|
||||
* } while (--len);
|
||||
*
|
||||
* @param src Source of the memory
|
||||
* @param dest Destination of the memory, must be >= src
|
||||
* @param len Number of bytes to copy from src to dest
|
||||
*/
|
||||
inline void incremental_copy(unsigned char* dest, const unsigned char* src, ssize_t len)
|
||||
{
|
||||
size_t copylen = dest - src;
|
||||
while (len > 0)
|
||||
{
|
||||
memcpy(dest, src, std::min((size_t)len, (size_t)copylen));
|
||||
dest += copylen;
|
||||
len -= copylen;
|
||||
|
||||
/* we can double copylen every time
|
||||
* we copied the pattern */
|
||||
copylen = copylen * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline int lzo1x_decompress_safe(const unsigned char *input,
|
||||
size_t input_length,
|
||||
unsigned char *output,
|
||||
size_t *output_length)
|
||||
{
|
||||
size_t z;
|
||||
const unsigned char *input_ptr;
|
||||
unsigned char *output_ptr;
|
||||
const unsigned char *match_ptr;
|
||||
const unsigned char *const input_ptr_end = input + input_length;
|
||||
unsigned char *const output_ptr_end = output + *output_length;
|
||||
|
||||
*output_length = 0;
|
||||
|
||||
input_ptr = input;
|
||||
output_ptr = output;
|
||||
|
||||
if (LZOASYM_UNLIKELY(input_length > 65536)) // quick fix to prevent 16MB integer overflow vulnerability
|
||||
goto input_too_large;
|
||||
|
||||
if (LZOASYM_LIKELY(*input_ptr <= 17))
|
||||
{
|
||||
while (LZOASYM_LIKELY(input_ptr < input_ptr_end) && LZOASYM_LIKELY(output_ptr <= output_ptr_end))
|
||||
{
|
||||
z = *input_ptr++;
|
||||
if (z < 16) // literal data?
|
||||
{
|
||||
if (LZOASYM_UNLIKELY(z == 0))
|
||||
{
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(1);
|
||||
while (LZOASYM_UNLIKELY(*input_ptr == 0))
|
||||
{
|
||||
z += 255;
|
||||
input_ptr++;
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(1);
|
||||
}
|
||||
z += 15 + *input_ptr++;
|
||||
}
|
||||
|
||||
// copy literal data
|
||||
{
|
||||
LZOASYM_ASSERT(z > 0);
|
||||
const size_t len = z + 3;
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(len);
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(len+1);
|
||||
memcpy(output_ptr, input_ptr, len);
|
||||
input_ptr += len;
|
||||
output_ptr += len;
|
||||
}
|
||||
|
||||
initial_literal:
|
||||
z = *input_ptr++;
|
||||
if (LZOASYM_UNLIKELY(z < 16))
|
||||
{
|
||||
match_ptr = output_ptr - (1 + LZOASYM_M2_MAX_OFFSET);
|
||||
match_ptr -= z >> 2;
|
||||
match_ptr -= *input_ptr++ << 2;
|
||||
|
||||
LZOASYM_CHECK_MATCH_OVERFLOW(match_ptr);
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(3);
|
||||
*output_ptr++ = *match_ptr++;
|
||||
*output_ptr++ = *match_ptr++;
|
||||
*output_ptr++ = *match_ptr;
|
||||
goto match_complete;
|
||||
}
|
||||
}
|
||||
|
||||
// found a match (M2, M3, M4, or M1)
|
||||
do {
|
||||
if (LZOASYM_LIKELY(z >= 64)) // LZO "M2" match (most likely)
|
||||
{
|
||||
match_ptr = output_ptr - 1;
|
||||
match_ptr -= (z >> 2) & 7;
|
||||
match_ptr -= *input_ptr++ << 3;
|
||||
z = (z >> 5) - 1;
|
||||
}
|
||||
else if (LZOASYM_LIKELY(z >= 32)) // LZO "M3" match
|
||||
{
|
||||
z &= 31;
|
||||
if (LZOASYM_UNLIKELY(z == 0))
|
||||
{
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(1);
|
||||
while (LZOASYM_UNLIKELY(*input_ptr == 0))
|
||||
{
|
||||
z += 255;
|
||||
input_ptr++;
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(1);
|
||||
}
|
||||
z += 31 + *input_ptr++;
|
||||
}
|
||||
|
||||
match_ptr = output_ptr - 1;
|
||||
match_ptr -= get_u16(input_ptr) >> 2;
|
||||
input_ptr += 2;
|
||||
}
|
||||
else if (LZOASYM_LIKELY(z >= 16)) // LZO "M4" match
|
||||
{
|
||||
match_ptr = output_ptr;
|
||||
match_ptr -= (z & 8) << 11;
|
||||
z &= 7;
|
||||
if (LZOASYM_UNLIKELY(z == 0))
|
||||
{
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(1);
|
||||
while (LZOASYM_UNLIKELY(*input_ptr == 0))
|
||||
{
|
||||
z += 255;
|
||||
input_ptr++;
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(1);
|
||||
}
|
||||
z += 7 + *input_ptr++;
|
||||
}
|
||||
|
||||
match_ptr -= get_u16(input_ptr) >> 2;
|
||||
input_ptr += 2;
|
||||
if (LZOASYM_UNLIKELY(match_ptr == output_ptr))
|
||||
goto success;
|
||||
match_ptr -= 0x4000;
|
||||
}
|
||||
else // LZO "M1" match (least likely)
|
||||
{
|
||||
match_ptr = output_ptr - 1;
|
||||
match_ptr -= z >> 2;
|
||||
match_ptr -= *input_ptr++ << 2;
|
||||
|
||||
LZOASYM_CHECK_MATCH_OVERFLOW(match_ptr);
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(2);
|
||||
*output_ptr++ = *match_ptr++;
|
||||
*output_ptr++ = *match_ptr;
|
||||
goto match_complete;
|
||||
}
|
||||
|
||||
// copy the match we found above
|
||||
{
|
||||
LZOASYM_CHECK_MATCH_OVERFLOW(match_ptr);
|
||||
LZOASYM_ASSERT(z > 0);
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(z+3-1);
|
||||
|
||||
const size_t len = z + 2;
|
||||
incremental_copy(output_ptr, match_ptr, len);
|
||||
match_ptr += len;
|
||||
output_ptr += len;
|
||||
}
|
||||
|
||||
match_complete:
|
||||
z = input_ptr[-2] & 3;
|
||||
if (LZOASYM_LIKELY(z == 0))
|
||||
break;
|
||||
|
||||
match_continue:
|
||||
// copy literal data
|
||||
LZOASYM_ASSERT(z > 0);
|
||||
LZOASYM_ASSERT(z < 4);
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(z);
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(z+1);
|
||||
*output_ptr++ = *input_ptr++;
|
||||
if (LZOASYM_LIKELY(z > 1))
|
||||
{
|
||||
*output_ptr++ = *input_ptr++;
|
||||
if (z > 2)
|
||||
*output_ptr++ = *input_ptr++;
|
||||
}
|
||||
z = *input_ptr++;
|
||||
} while (LZOASYM_LIKELY(input_ptr < input_ptr_end) && LZOASYM_LIKELY(output_ptr <= output_ptr_end));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// input began with a match or a literal (rare)
|
||||
z = *input_ptr++ - 17;
|
||||
if (z < 4)
|
||||
goto match_continue;
|
||||
LZOASYM_ASSERT(z > 0);
|
||||
LZOASYM_CHECK_OUTPUT_OVERFLOW(z);
|
||||
LZOASYM_CHECK_INPUT_OVERFLOW(z+1);
|
||||
do {
|
||||
*output_ptr++ = *input_ptr++;
|
||||
} while (--z > 0);
|
||||
goto initial_literal;
|
||||
}
|
||||
|
||||
*output_length = ptr_diff(output_ptr, output);
|
||||
return LZOASYM_E_EOF_NOT_FOUND;
|
||||
|
||||
success:
|
||||
LZOASYM_ASSERT(z == 1);
|
||||
*output_length = ptr_diff(output_ptr, output);
|
||||
return (input_ptr == input_ptr_end ? LZOASYM_E_OK :
|
||||
(input_ptr < input_ptr_end ? LZOASYM_E_INPUT_NOT_CONSUMED : LZOASYM_E_INPUT_OVERFLOW));
|
||||
|
||||
input_overflow:
|
||||
*output_length = ptr_diff(output_ptr, output);
|
||||
return LZOASYM_E_INPUT_OVERFLOW;
|
||||
|
||||
output_overflow:
|
||||
*output_length = ptr_diff(output_ptr, output);
|
||||
return LZOASYM_E_OUTPUT_OVERFLOW;
|
||||
|
||||
match_overflow:
|
||||
*output_length = ptr_diff(output_ptr, output);
|
||||
return LZOASYM_E_MATCH_OVERFLOW;
|
||||
|
||||
assert_fail:
|
||||
return LZOASYM_E_ASSERT_FAILED;
|
||||
|
||||
input_too_large:
|
||||
return LZOASYM_E_INPUT_TOO_LARGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef LZOASYM_CHECK_INPUT_OVERFLOW
|
||||
#undef LZOASYM_CHECK_OUTPUT_OVERFLOW
|
||||
#undef LZOASYM_CHECK_MATCH_OVERFLOW
|
||||
#undef LZOASYM_ASSERT
|
||||
#undef LZOASYM_LIKELY
|
||||
#undef LZOASYM_UNLIKELY
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// If we are linked with the LZO library, use it. Otherwise default
|
||||
// to an intrinstic LZO implementation that only handles decompression.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_LZOSELECT_H
|
||||
#define OPENVPN_COMPRESS_LZOSELECT_H
|
||||
|
||||
#if defined(HAVE_LZO)
|
||||
#include <openvpn/compress/lzo.hpp>
|
||||
#else
|
||||
#include <openvpn/compress/lzoasym.hpp>
|
||||
#endif
|
||||
|
||||
namespace openvpn {
|
||||
#if !defined(HAVE_LZO)
|
||||
typedef CompressLZOAsym CompressLZO;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
// 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 Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program in the COPYING file.
|
||||
// If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef OPENVPN_COMPRESS_SNAPPY_H
|
||||
#define OPENVPN_COMPRESS_SNAPPY_H
|
||||
|
||||
// Implement Snappy compression.
|
||||
// Should only be included by compress.hpp
|
||||
|
||||
#include <snappy.h>
|
||||
|
||||
namespace openvpn {
|
||||
|
||||
class CompressSnappy : public Compress
|
||||
{
|
||||
// magic number for Snappy compression
|
||||
enum {
|
||||
SNAPPY_COMPRESS = 0x68,
|
||||
};
|
||||
|
||||
public:
|
||||
CompressSnappy(const Frame::Ptr& frame, const SessionStats::Ptr& stats, const bool asym_arg)
|
||||
: Compress(frame, stats),
|
||||
asym(asym_arg)
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS("SNAPPY init asym=" << asym_arg);
|
||||
}
|
||||
|
||||
virtual const char *name() const { return "snappy"; }
|
||||
|
||||
virtual void compress(BufferAllocated& buf, const bool hint)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
if (hint && !asym)
|
||||
{
|
||||
// initialize work buffer
|
||||
frame->prepare(Frame::COMPRESS_WORK, work);
|
||||
|
||||
// verify that input data length is not too large
|
||||
if (snappy::MaxCompressedLength(buf.size()) > work.max_size())
|
||||
{
|
||||
error(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
// do compress
|
||||
size_t comp_size;
|
||||
snappy::RawCompress((const char *)buf.c_data(), buf.size(), (char *)work.data(), &comp_size);
|
||||
|
||||
// did compression actually reduce data length?
|
||||
if (comp_size < buf.size())
|
||||
{
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("SNAPPY compress " << buf.size() << " -> " << comp_size);
|
||||
work.set_size(comp_size);
|
||||
do_swap(work, SNAPPY_COMPRESS);
|
||||
buf.swap(work);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// indicate that we didn't compress
|
||||
do_swap(buf, NO_COMPRESS_SWAP);
|
||||
}
|
||||
|
||||
virtual void decompress(BufferAllocated& buf)
|
||||
{
|
||||
// skip null packets
|
||||
if (!buf.size())
|
||||
return;
|
||||
|
||||
const unsigned char c = buf.pop_front();
|
||||
switch (c)
|
||||
{
|
||||
case NO_COMPRESS_SWAP:
|
||||
do_unswap(buf);
|
||||
break;
|
||||
case SNAPPY_COMPRESS:
|
||||
{
|
||||
do_unswap(buf);
|
||||
|
||||
// initialize work buffer
|
||||
const size_t payload_size = frame->prepare(Frame::DECOMPRESS_WORK, work);
|
||||
|
||||
// do uncompress
|
||||
size_t decomp_size;
|
||||
if (!snappy::GetUncompressedLength((const char *)buf.c_data(), buf.size(), &decomp_size)
|
||||
|| decomp_size > payload_size)
|
||||
{
|
||||
error(buf);
|
||||
break;
|
||||
}
|
||||
if (!snappy::RawUncompress((const char *)buf.c_data(), buf.size(), (char *)work.data()))
|
||||
{
|
||||
error(buf);
|
||||
break;
|
||||
}
|
||||
OPENVPN_LOG_COMPRESS_VERBOSE("SNAPPY uncompress " << buf.size() << " -> " << decomp_size);
|
||||
work.set_size(decomp_size);
|
||||
buf.swap(work);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error(buf); // unknown op
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const bool asym;
|
||||
BufferAllocated work;
|
||||
};
|
||||
|
||||
} // namespace openvpn
|
||||
|
||||
#endif // OPENVPN_COMPRESS_SNAPPY_H
|
||||
Reference in New Issue
Block a user