/*
 *  OpenVPN -- An application to securely tunnel IP networks
 *             over a single UDP port, with support for SSL/TLS-based
 *             session authentication and key exchange,
 *             packet encryption, packet authentication, and
 *             packet compression.
 *
 *  Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2
 *  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; if not, see <https://www.gnu.org/licenses/>.
 */

/*
 * Generic compression support.  Currently we support
 * LZO 2 and LZ4.
 */
#ifndef OPENVPN_COMP_H
#define OPENVPN_COMP_H

/* We always parse all compression options, so we include these defines/struct
 * outside of the USE_COMP define */

/* Compression flags */
/* Removed
 #define COMP_F_ADAPTIVE             (1u<<0) / * COMP_ALG_LZO only * /
 #define COMP_F_ALLOW_COMPRESS       (1u<<1) / * not only incoming is compressed but also outgoing *
 /
 */
/** initial command byte is swapped with last byte in buffer to preserve payload alignment */
#define COMP_F_SWAP                 (1u << 2)
/** tell server that we only support compression stubs */
#define COMP_F_ADVERTISE_STUBS_ONLY (1u << 3)
/** Only accept stub compression, even with COMP_F_ADVERTISE_STUBS_ONLY
 * we still accept other compressions to be pushed */
#define COMP_F_ALLOW_STUB_ONLY      (1u << 4)
/** push stub-v2 or comp-lzo no when we see a client with comp-lzo in occ */
#define COMP_F_MIGRATE              (1u << 5)
/** Compression was explicitly set to allow asymetric compression */
#define COMP_F_ALLOW_ASYM           (1u << 6)
/** Do not allow compression framing (breaks DCO) */
#define COMP_F_ALLOW_NOCOMP_ONLY    (1u << 7)

/* algorithms */
#define COMP_ALG_UNDEF  0
/** support compression command byte and framing without actual compression */
#define COMP_ALG_STUB   1
#define COMP_ALG_LZO    2 /**< LZO algorithm */
#define COMP_ALG_SNAPPY 3 /**< Snappy algorithm (no longer supported) */
#define COMP_ALG_LZ4    4 /**< LZ4 algorithm */


/* algorithm v2 */
#define COMP_ALGV2_UNCOMPRESSED 10
#define COMP_ALGV2_LZ4          11
/*
 #define COMP_ALGV2_LZO     12
 #define COMP_ALGV2_SNAPPY   13
 */

/*
 * Information that basically identifies a compression
 * algorithm and related flags.
 */
struct compress_options
{
    int alg;
    unsigned int flags;
};

static inline bool
comp_non_stub_enabled(const struct compress_options *info)
{
    return info->alg != COMP_ALGV2_UNCOMPRESSED && info->alg != COMP_ALG_STUB
           && info->alg != COMP_ALG_UNDEF;
}

#include "error.h"

/**
 * Checks if the compression settings are valid. Takes into account the
 * flags of allow-compression and also the whether algorithms are compiled
 * in
 */
bool check_compression_settings_valid(struct compress_options *info, msglvl_t msglevel);

#ifdef USE_COMP
#include "buffer.h"
#include "mtu.h"
#include "common.h"
#include "status.h"

/*
 * Length of prepended prefix on compressed packets
 */
#define COMP_PREFIX_LEN 1

/*
 * Prefix bytes
 */

/* V1 on wire codes */
/* Initial command byte to tell our peer if we compressed */
#define LZO_COMPRESS_BYTE     0x66
#define LZ4_COMPRESS_BYTE     0x69
#define NO_COMPRESS_BYTE      0xFA
/** to maintain payload alignment, replace this byte with last byte of packet */
#define NO_COMPRESS_BYTE_SWAP 0xFB

/* V2 on wire code */
#define COMP_ALGV2_INDICATOR_BYTE    0x50
#define COMP_ALGV2_UNCOMPRESSED_BYTE 0
#define COMP_ALGV2_LZ4_BYTE          1
#define COMP_ALGV2_LZO_BYTE          2
#define COMP_ALGV2_SNAPPY_BYTE       3

/*
 * Compress worst case size expansion (for any algorithm)
 *
 * LZO:    len + len/8 + 128 + 3
 * Snappy: len + len/6 + 32
 * LZ4:    len + len/255 + 16  (LZ4_COMPRESSBOUND(len))
 */
#define COMP_EXTRA_BUFFER(len) ((len) / 6 + 128 + 3 + COMP_PREFIX_LEN)

/*
 * Don't try to compress any packet smaller than this.
 */
#define COMPRESS_THRESHOLD 100

/* Forward declaration of compression context */
struct compress_context;

/*
 * Virtual methods and other static info for each compression algorithm
 */
struct compress_alg
{
    const char *name;
    void (*compress_init)(struct compress_context *compctx);
    void (*compress_uninit)(struct compress_context *compctx);
    void (*compress)(struct buffer *buf, struct buffer work, struct compress_context *compctx,
                     const struct frame *frame);

    void (*decompress)(struct buffer *buf, struct buffer work, struct compress_context *compctx,
                       const struct frame *frame);
};

/*
 * Headers for each compression implementation
 */
#ifdef ENABLE_LZO
#include "lzo.h"
#endif

#ifdef ENABLE_LZ4
#include "comp-lz4.h"
#endif

/*
 * Workspace union of all supported compression algorithms
 */
union compress_workspace_union
{
#ifdef ENABLE_LZO
    struct lzo_compress_workspace lzo;
#endif
#ifdef ENABLE_LZ4
    struct lz4_workspace lz4;
#endif
};

/*
 * Context for active compression session
 */
struct compress_context
{
    unsigned int flags;
    struct compress_alg alg;
    union compress_workspace_union wu;

    /* statistics */
    counter_type pre_decompress;
    counter_type post_decompress;
    counter_type pre_compress;
    counter_type post_compress;
};

extern const struct compress_alg comp_stub_alg;
extern const struct compress_alg compv2_stub_alg;

struct compress_context *comp_init(const struct compress_options *opt);

void comp_uninit(struct compress_context *compctx);

void comp_print_stats(const struct compress_context *compctx, struct status_output *so);

void comp_generate_peer_info_string(const struct compress_options *opt, struct buffer *out);

void compv2_escape_data_ifneeded(struct buffer *buf);

static inline bool
comp_enabled(const struct compress_options *info)
{
    return info->alg != COMP_ALG_UNDEF;
}
#endif /* USE_COMP */
#endif /* ifndef OPENVPN_COMP_H */
