1 /*
2  *  OpenVPN -- An application to securely tunnel IP networks
3  *             over a single UDP port, with support for SSL/TLS-based
4  *             session authentication and key exchange,
5  *             packet encryption, packet authentication, and
6  *             packet compression.
7  *
8  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License version 2
12  *  as published by the Free Software Foundation.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License along
20  *  with this program; if not, write to the Free Software Foundation, Inc.,
21  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #elif defined(_MSC_VER)
27 #include "config-msvc.h"
28 #endif
29 
30 #include "syshead.h"
31 
32 #ifdef USE_COMP
33 
34 #include "comp.h"
35 #include "error.h"
36 #include "otime.h"
37 
38 #include "memdbg.h"
39 
40 struct compress_context *
comp_init(const struct compress_options * opt)41 comp_init(const struct compress_options *opt)
42 {
43     struct compress_context *compctx = NULL;
44     switch (opt->alg)
45     {
46         case COMP_ALG_STUB:
47             ALLOC_OBJ_CLEAR(compctx, struct compress_context);
48             compctx->flags = opt->flags;
49             compctx->alg = comp_stub_alg;
50             break;
51 
52         case COMP_ALGV2_UNCOMPRESSED:
53             ALLOC_OBJ_CLEAR(compctx, struct compress_context);
54             compctx->flags = opt->flags;
55             compctx->alg = compv2_stub_alg;
56             break;
57 
58 #ifdef ENABLE_LZO
59         case COMP_ALG_LZO:
60             ALLOC_OBJ_CLEAR(compctx, struct compress_context);
61             compctx->flags = opt->flags;
62             compctx->alg = lzo_alg;
63             break;
64 
65 #endif
66 #ifdef ENABLE_LZ4
67         case COMP_ALG_LZ4:
68             ALLOC_OBJ_CLEAR(compctx, struct compress_context);
69             compctx->flags = opt->flags;
70             compctx->alg = lz4_alg;
71             break;
72 
73         case COMP_ALGV2_LZ4:
74             ALLOC_OBJ_CLEAR(compctx, struct compress_context);
75             compctx->flags = opt->flags;
76             compctx->alg = lz4v2_alg;
77             break;
78 #endif
79     }
80     if (compctx)
81     {
82         (*compctx->alg.compress_init)(compctx);
83     }
84 
85     return compctx;
86 }
87 
88 /* In the v2 compression schemes, an uncompressed packet has
89  * has no opcode in front, unless the first byte is 0x50. In this
90  * case the packet needs to be escaped */
91 void
compv2_escape_data_ifneeded(struct buffer * buf)92 compv2_escape_data_ifneeded(struct buffer *buf)
93 {
94     uint8_t *head = BPTR(buf);
95     if (head[0] != COMP_ALGV2_INDICATOR_BYTE)
96     {
97         return;
98     }
99 
100     /* Header is 0x50 */
101     ASSERT(buf_prepend(buf, 2));
102 
103     head = BPTR(buf);
104     head[0] = COMP_ALGV2_INDICATOR_BYTE;
105     head[1] = COMP_ALGV2_UNCOMPRESSED;
106 }
107 
108 
109 void
comp_uninit(struct compress_context * compctx)110 comp_uninit(struct compress_context *compctx)
111 {
112     if (compctx)
113     {
114         (*compctx->alg.compress_uninit)(compctx);
115         free(compctx);
116     }
117 }
118 
119 void
comp_add_to_extra_frame(struct frame * frame)120 comp_add_to_extra_frame(struct frame *frame)
121 {
122     /* Leave room for our one-byte compressed/didn't-compress prefix byte. */
123     frame_add_to_extra_frame(frame, COMP_PREFIX_LEN);
124 }
125 
126 void
comp_add_to_extra_buffer(struct frame * frame)127 comp_add_to_extra_buffer(struct frame *frame)
128 {
129     /* Leave room for compression buffer to expand in worst case scenario
130      * where data is totally incompressible */
131     frame_add_to_extra_buffer(frame, COMP_EXTRA_BUFFER(EXPANDED_SIZE(frame)));
132 }
133 
134 void
comp_print_stats(const struct compress_context * compctx,struct status_output * so)135 comp_print_stats(const struct compress_context *compctx, struct status_output *so)
136 {
137     if (compctx)
138     {
139         status_printf(so, "pre-compress bytes," counter_format, compctx->pre_compress);
140         status_printf(so, "post-compress bytes," counter_format, compctx->post_compress);
141         status_printf(so, "pre-decompress bytes," counter_format, compctx->pre_decompress);
142         status_printf(so, "post-decompress bytes," counter_format, compctx->post_decompress);
143     }
144 }
145 
146 /*
147  * Tell our peer which compression algorithms we support.
148  */
149 void
comp_generate_peer_info_string(const struct compress_options * opt,struct buffer * out)150 comp_generate_peer_info_string(const struct compress_options *opt, struct buffer *out)
151 {
152     if (opt)
153     {
154         bool lzo_avail = false;
155         if (!(opt->flags & COMP_F_ADVERTISE_STUBS_ONLY))
156         {
157 #if defined(ENABLE_LZ4)
158             buf_printf(out, "IV_LZ4=1\n");
159             buf_printf(out, "IV_LZ4v2=1\n");
160 #endif
161 #if defined(ENABLE_LZO)
162             buf_printf(out, "IV_LZO=1\n");
163             lzo_avail = true;
164 #endif
165         }
166         if (!lzo_avail)
167         {
168             buf_printf(out, "IV_LZO_STUB=1\n");
169         }
170         buf_printf(out, "IV_COMP_STUB=1\n");
171         buf_printf(out, "IV_COMP_STUBv2=1\n");
172         buf_printf(out, "IV_TCPNL=1\n");
173     }
174 }
175 
176 #endif /* USE_COMP */
177