1/* $NetBSD: deflate.c,v 1.22 2015/03/26 17:40:16 prlw1 Exp $ */
2/* $FreeBSD: src/sys/opencrypto/deflate.c,v 1.1.2.1 2002/11/21 23:34:23 sam Exp $ */
3/* $OpenBSD: deflate.c,v 1.3 2001/08/20 02:45:22 hugh Exp $ */
4
5/*
6 * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.org)
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/*
33 * This file contains a wrapper around the deflate algo compression
34 * functions using the zlib library (see net/zlib.{c,h})
35 */
36
37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: deflate.c,v 1.22 2015/03/26 17:40:16 prlw1 Exp $");
39
40#include <sys/types.h>
41#include <sys/malloc.h>
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <net/zlib.h>
45
46#include <opencrypto/cryptodev.h>
47#include <opencrypto/deflate.h>
48
49#define ZBUF 10
50
51struct deflate_buf {
52 u_int8_t *out;
53 u_int32_t size;
54};
55
56int window_inflate = -1 * MAX_WBITS;
57int window_deflate = -12;
58
59/*
60 * This function takes a block of data and (de)compress it using the deflate
61 * algorithm
62 */
63
64static void *
65ocf_zalloc(void *nil, u_int type, u_int size)
66{
67 void *ptr;
68
69 ptr = malloc(type *size, M_CRYPTO_DATA, M_NOWAIT);
70 return ptr;
71}
72
73static void
74ocf_zfree(void *nil, void *ptr)
75{
76 free(ptr, M_CRYPTO_DATA);
77}
78
79u_int32_t
80deflate_global(u_int8_t *data, u_int32_t size, int decomp, u_int8_t **out,
81 int size_hint)
82{
83 /* decomp indicates whether we compress (0) or decompress (1) */
84
85 z_stream zbuf;
86 u_int8_t *output;
87 u_int32_t count, result, tocopy;
88 int error, i, j;
89 struct deflate_buf buf[ZBUF];
90
91 DPRINTF(("deflate_global: size %u\n", size));
92
93 memset(&zbuf, 0, sizeof(z_stream));
94 zbuf.next_in = data; /* data that is going to be processed */
95 zbuf.zalloc = ocf_zalloc;
96 zbuf.zfree = ocf_zfree;
97 zbuf.opaque = Z_NULL;
98 zbuf.avail_in = size; /* Total length of data to be processed */
99
100 if (!decomp) {
101 buf[0].size = size;
102 } else {
103 /*
104 * Choose a buffer with 4x the size of the input buffer
105 * for the size of the output buffer in the case of
106 * decompression. If it's not sufficient, it will need to be
107 * updated while the decompression is going on
108 */
109
110 buf[0].size = MAX(size * 4, size_hint);
111 }
112 buf[0].out = malloc(buf[0].size, M_CRYPTO_DATA, M_NOWAIT);
113 if (buf[0].out == NULL)
114 return 0;
115 i = 1;
116
117 zbuf.next_out = buf[0].out;
118 zbuf.avail_out = buf[0].size;
119
120 error = decomp ? inflateInit2(&zbuf, window_inflate) :
121 deflateInit2(&zbuf, Z_DEFAULT_COMPRESSION, Z_METHOD,
122 window_deflate, Z_MEMLEVEL, Z_DEFAULT_STRATEGY);
123
124 if (error != Z_OK)
125 goto bad2;
126 for (;;) {
127 error = decomp ? inflate(&zbuf, Z_SYNC_FLUSH) :
128 deflate(&zbuf, Z_FINISH);
129 if (error == Z_STREAM_END) /* success */
130 break;
131 /*
132 * XXX compensate for two problems:
133 * -Former versions of this code didn't set Z_FINISH
134 * on compression, so the compressed data are not correctly
135 * terminated and the decompressor doesn't get Z_STREAM_END.
136 * Accept such packets for interoperability.
137 * -sys/net/zlib.c has a bug which makes that Z_BUF_ERROR is
138 * set after successful decompression under rare conditions.
139 */
140 else if (decomp && (error == Z_OK || error == Z_BUF_ERROR)
141 && zbuf.avail_in == 0 && zbuf.avail_out != 0)
142 break;
143 else if (error != Z_OK)
144 goto bad;
145 else if (zbuf.avail_out == 0) {
146 /* we need more output space, allocate size */
147 int nextsize = buf[i-1].size * 2;
148 if (i == ZBUF || nextsize > 1000000)
149 goto bad;
150 buf[i].out = malloc(nextsize, M_CRYPTO_DATA, M_NOWAIT);
151 if (buf[i].out == NULL)
152 goto bad;
153 zbuf.next_out = buf[i].out;
154 zbuf.avail_out = buf[i].size = nextsize;
155 i++;
156 }
157 }
158
159 result = count = zbuf.total_out;
160
161 if (i != 1) { /* copy everything into one buffer */
162 output = malloc(result, M_CRYPTO_DATA, M_NOWAIT);
163 if (output == NULL)
164 goto bad;
165 *out = output;
166 for (j = 0; j < i; j++) {
167 tocopy = MIN(count, buf[j].size);
168 /* XXX the last buf can be empty */
169 KASSERT(tocopy || j == (i - 1));
170 memcpy(output, buf[j].out, tocopy);
171 output += tocopy;
172 free(buf[j].out, M_CRYPTO_DATA);
173 count -= tocopy;
174 }
175 KASSERT(count == 0);
176 } else {
177 *out = buf[0].out;
178 }
179 if (decomp)
180 inflateEnd(&zbuf);
181 else
182 deflateEnd(&zbuf);
183 return result;
184
185bad:
186 if (decomp)
187 inflateEnd(&zbuf);
188 else
189 deflateEnd(&zbuf);
190bad2:
191 for (j = 0; j < i; j++)
192 free(buf[j].out, M_CRYPTO_DATA);
193 return 0;
194}
195
196/*
197 * Initial version will perform a single gzip encapsulation,
198 * filling in the header,
199 * and appending the crc and uncompressed length.
200 *
201 * Later version will support multiple buffers with
202 * a flag indication final buffer. The crc is maintained
203 * over all buffers and appended to the output along with
204 * the uncompressed length after the final data buffer
205 * has been compressed and output.
206 *
207 * Ditto for uncompress - CRC is extracted from the final packed
208 * and compared against CRC of uncompressed data.
209 *
210 */
211
212/* constant header for the gzip */
213static const char gzip_header[10] = {
214 0x1f, 0x8b, /* ID1 ID2 */
215 Z_DEFLATED, /* CM */
216 0, /* FLG */
217 0, 0, 0, 0, /* MTIME */
218 0, /* XFL */
219 0x03 /* OS (Unix) */
220};
221
222/* Followed by compressed payload */
223/* Followed by uint32_t CRC32 and uint32_t ISIZE */
224#define GZIP_TAIL_SIZE 8
225
226u_int32_t
227gzip_global(u_int8_t *data, u_int32_t size,
228 int decomp, u_int8_t **out, int size_hint)
229{
230 /* decomp indicates whether we compress (0) or decompress (1) */
231 z_stream zbuf;
232 u_int8_t *output;
233 u_int32_t count, result;
234 int error, i, j;
235 struct deflate_buf buf[ZBUF];
236 u_int32_t crc;
237 u_int32_t isize = 0, icrc = 0;
238
239 DPRINTF(("gzip_global: decomp %d, size %u\n", decomp, size));
240
241 memset(&zbuf, 0, sizeof(z_stream));
242 zbuf.zalloc = ocf_zalloc;
243 zbuf.zfree = ocf_zfree;
244 zbuf.opaque = Z_NULL;
245
246 if (!decomp) {
247 /* compress */
248 DPRINTF(("gzip_global: compress malloc %u + %zu + %u = %zu\n",
249 size, sizeof(gzip_header), GZIP_TAIL_SIZE,
250 size + sizeof(gzip_header) + GZIP_TAIL_SIZE));
251
252 buf[0].size = size;
253 crc = crc32(0, data, size);
254 DPRINTF(("gzip_compress: size %u, crc 0x%x\n", size, crc));
255 zbuf.avail_in = size; /* Total length of data to be processed */
256 zbuf.next_in = data; /* data that is going to be processed */
257 } else {
258 /* decompress */
259 /* check the gzip header */
260 if (size <= sizeof(gzip_header) + GZIP_TAIL_SIZE) {
261 /* Not enough data for the header & tail */
262 DPRINTF(("gzip_global: not enough data (%u)\n",
263 size));
264 return 0;
265 }
266
267 /* XXX this is pretty basic,
268 * needs to be expanded to ignore MTIME, OS,
269 * but still ensure flags are 0.
270 * Q. Do we need to support the flags and
271 * optional header fields? Likely.
272 * XXX add flag and field support too.
273 */
274 if (memcmp(data, gzip_header, sizeof(gzip_header)) != 0) {
275 DPRINTF(("gzip_global: unsupported gzip header (%02x%02x)\n",
276 data[0], data[1]));
277 return 0;
278 } else {
279 DPRINTF(("gzip_global.%d: gzip header ok\n",__LINE__));
280 }
281
282 memcpy(&isize, &data[size-sizeof(uint32_t)], sizeof(uint32_t));
283 LE32TOH(isize);
284 memcpy(&icrc, &data[size-2*sizeof(uint32_t)], sizeof(uint32_t));
285 LE32TOH(icrc);
286
287 DPRINTF(("gzip_global: isize = %u (%02x %02x %02x %02x)\n",
288 isize,
289 data[size-4],
290 data[size-3],
291 data[size-2],
292 data[size-1]));
293
294 buf[0].size = isize;
295 crc = crc32(0, NULL, 0); /* get initial crc value */
296
297 /* skip over the gzip header */
298 zbuf.next_in = data + sizeof(gzip_header);
299
300 /* actual payload size stripped of gzip header and tail */
301 zbuf.avail_in = size - sizeof(gzip_header) - GZIP_TAIL_SIZE;
302 }
303
304 buf[0].out = malloc(buf[0].size, M_CRYPTO_DATA, M_NOWAIT);
305 if (buf[0].out == NULL)
306 return 0;
307 zbuf.next_out = buf[0].out;
308 zbuf.avail_out = buf[0].size;
309 DPRINTF(("zbuf avail_in %u, avail_out %u\n",
310 zbuf.avail_in, zbuf.avail_out));
311 i = 1;
312
313 error = decomp ? inflateInit2(&zbuf, window_inflate) :
314 deflateInit2(&zbuf, Z_DEFAULT_COMPRESSION, Z_METHOD,
315 window_deflate, Z_MEMLEVEL, Z_DEFAULT_STRATEGY);
316
317 if (error != Z_OK) {
318 printf("deflateInit2() failed\n");
319 goto bad2;
320 }
321 for (;;) {
322 DPRINTF(("pre: %s in:%u out:%u\n", decomp ? "deflate()" : "inflate()",
323 zbuf.avail_in, zbuf.avail_out));
324 error = decomp ? inflate(&zbuf, Z_SYNC_FLUSH) :
325 deflate(&zbuf, Z_FINISH);
326 DPRINTF(("post: %s in:%u out:%u\n", decomp ? "deflate()" : "inflate()",
327 zbuf.avail_in, zbuf.avail_out));
328 if (error == Z_STREAM_END) /* success */
329 break;
330 /*
331 * XXX compensate for a zlib problem:
332 * -sys/net/zlib.c has a bug which makes that Z_BUF_ERROR is
333 * set after successful decompression under rare conditions.
334 */
335 else if (decomp && error == Z_BUF_ERROR
336 && zbuf.avail_in == 0 && zbuf.avail_out != 0)
337 break;
338 else if (error != Z_OK)
339 goto bad;
340 else if (zbuf.avail_out == 0) {
341 /* we need more output space, allocate size */
342 int nextsize = buf[i-1].size * 2;
343 if (i == ZBUF || nextsize > 1000000)
344 goto bad;
345 buf[i].out = malloc(nextsize, M_CRYPTO_DATA, M_NOWAIT);
346 if (buf[i].out == NULL)
347 goto bad;
348 zbuf.next_out = buf[i].out;
349 zbuf.avail_out = buf[i].size = nextsize;
350 i++;
351 }
352 }
353
354 if (decomp) {
355 count = result = zbuf.total_out;
356 } else {
357 /* need room for header, CRC, and ISIZE */
358 result = zbuf.total_out + sizeof(gzip_header) + GZIP_TAIL_SIZE;
359 count = zbuf.total_out;
360 }
361
362 DPRINTF(("gzip_global: in %u -> out %u\n", size, result));
363
364 *out = malloc(result, M_CRYPTO_DATA, M_NOWAIT);
365 if (*out == NULL)
366 goto bad;
367 output = *out;
368 if (decomp)
369 inflateEnd(&zbuf);
370 else {
371 deflateEnd(&zbuf);
372
373 /* fill in gzip header */
374 memcpy(output, gzip_header, sizeof(gzip_header));
375 output += sizeof(gzip_header);
376 }
377 for (j = 0; j < i; j++) {
378 if (decomp) {
379 /* update crc for decompressed data */
380 crc = crc32(crc, buf[j].out, MIN(count, buf[j].size));
381 }
382 if (count > buf[j].size) {
383 memcpy(output, buf[j].out, buf[j].size);
384 output += buf[j].size;
385 free(buf[j].out, M_CRYPTO_DATA);
386 count -= buf[j].size;
387 } else {
388 /* it should be the last buffer */
389 memcpy(output, buf[j].out, count);
390 output += count;
391 free(buf[j].out, M_CRYPTO_DATA);
392 count = 0;
393 }
394 }
395
396 if (!decomp) {
397 /* fill in CRC and ISIZE */
398 HTOLE32(crc);
399 memcpy(output, &crc, sizeof(uint32_t));
400 HTOLE32(size);
401 memcpy(output + sizeof(uint32_t), &size, sizeof(uint32_t));
402
403 DPRINTF(("gzip_global: size = 0x%x (%02x %02x %02x %02x)\n",
404 size,
405 output[7],
406 output[3],
407 output[5],
408 output[4]));
409 } else {
410 if (crc != icrc || result != isize) {
411 DPRINTF(("gzip_global: crc/size mismatch\n"));
412 free(*out, M_CRYPTO_DATA);
413 *out = NULL;
414 return 0;
415 }
416 }
417
418 return result;
419
420bad:
421 if (decomp)
422 inflateEnd(&zbuf);
423 else
424 deflateEnd(&zbuf);
425bad2:
426 *out = NULL;
427 for (j = 0; j < i; j++)
428 free(buf[j].out, M_CRYPTO_DATA);
429 return 0;
430}
431