File: compress.hpp

package info (click to toggle)
openvpn3-client 25%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 19,276 kB
  • sloc: cpp: 190,085; python: 7,218; ansic: 1,866; sh: 1,361; java: 402; lisp: 81; makefile: 17
file content (537 lines) | stat: -rw-r--r-- 12,814 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
//    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- OpenVPN Inc.
//
//    SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//

// 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>
#include <openvpn/log/logger.hpp>

namespace openvpn {

//! Debugging level for message emitted from compression
#if !defined(OPENVPN_DEBUG_COMPRESS)
#define OPENVPN_DEBUG_COMPRESS 1
#endif

class Compress : public RC<thread_unsafe_refcount>,
                 public logging::LoggingMixin<OPENVPN_DEBUG_COMPRESS, logging::LOG_LEVEL_TRACE, Compress>
{
  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, unsigned char 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;
};
} // namespace openvpn

// 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_LZO:
        case LZO_STUB:
            return new CompressStub(frame, stats, false);
        case ANY:
        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