xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/compress.c (revision 9ee9e0d7de4c59c936a17df52be682915dc66f43)
1 /*
2  * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
3  * All rights reserved.
4  * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
5  * their moral rights under the UK Copyright Design and Patents Act 1988 to
6  * be recorded as the authors of this copyright work.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9  * use this file except in compliance with the License.
10  *
11  * You may obtain a copy of the License at
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 /** \file
23  */
24 #include "config.h"
25 
26 #ifdef HAVE_ZLIB_H
27 #include <zlib.h>
28 #endif
29 
30 #ifdef HAVE_BZLIB_H
31 #include <bzlib.h>
32 #endif
33 
34 #ifdef HAVE_ASSERT_H
35 #include <assert.h>
36 #endif
37 
38 #include <string.h>
39 
40 #include "packet-parse.h"
41 #include "errors.h"
42 #include "netpgpdefs.h"
43 #include "parse_local.h"
44 #include "memory.h"
45 #include "writer.h"
46 
47 #define DECOMPRESS_BUFFER	1024
48 
49 typedef struct {
50 	__ops_compression_type_t type;
51 	__ops_region_t   *region;
52 	unsigned char   in[DECOMPRESS_BUFFER];
53 	unsigned char   out[DECOMPRESS_BUFFER];
54 	z_stream        zstream;/* ZIP and ZLIB */
55 	size_t          offset;
56 	int             inflate_ret;
57 }               z_decompress_t;
58 
59 typedef struct {
60 	__ops_compression_type_t type;
61 	__ops_region_t   *region;
62 	char            in[DECOMPRESS_BUFFER];
63 	char            out[DECOMPRESS_BUFFER];
64 	bz_stream       bzstream;	/* BZIP2 */
65 	size_t          offset;
66 	int             inflate_ret;
67 }               bz_decompress_t;
68 
69 typedef struct {
70 	z_stream        stream;
71 	unsigned char  *src;
72 	unsigned char  *dst;
73 }               compress_t;
74 
75 /*
76  * \todo remove code duplication between this and
77  * bzip2_compressed_data_reader
78  */
79 static int
80 zlib_compressed_data_reader(void *dest, size_t length,
81 			    __ops_error_t ** errors,
82 			    __ops_reader_info_t *rinfo,
83 			    __ops_parse_cb_info_t *cbinfo)
84 {
85 	z_decompress_t *z = __ops_reader_get_arg(rinfo);
86 	size_t           len;
87 	size_t		 cc;
88 	char		*cdest = dest;
89 
90 	assert(z->type == OPS_C_ZIP || z->type == OPS_C_ZLIB);
91 
92 	if (z->inflate_ret == Z_STREAM_END &&
93 	    z->zstream.next_out == &z->out[z->offset]) {
94 		return 0;
95 	}
96 
97 	if (__ops_get_debug_level(__FILE__)) {
98 		(void) fprintf(stderr, "zlib_compressed_data_reader: length %" PRIsize "d\n", length);
99 	}
100 
101 	if (z->region->length_read == z->region->length) {
102 		if (z->inflate_ret != Z_STREAM_END) {
103 			OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,
104 				"Compressed data didn't end when region ended.");
105 		}
106 	}
107 	for (cc = 0 ; cc < length ; cc += len) {
108 		if (&z->out[z->offset] == z->zstream.next_out) {
109 			int             ret;
110 
111 			z->zstream.next_out = z->out;
112 			z->zstream.avail_out = sizeof(z->out);
113 			z->offset = 0;
114 			if (z->zstream.avail_in == 0) {
115 				unsigned        n = z->region->length;
116 
117 				if (!z->region->indeterminate) {
118 					n -= z->region->length_read;
119 					if (n > sizeof(z->in)) {
120 						n = sizeof(z->in);
121 					}
122 				} else {
123 					n = sizeof(z->in);
124 				}
125 
126 				if (!__ops_stacked_limited_read(z->in, n, z->region,
127 						     errors, rinfo, cbinfo)) {
128 					return -1;
129 				}
130 
131 				z->zstream.next_in = z->in;
132 				z->zstream.avail_in = (z->region->indeterminate) ?
133 					z->region->last_read : n;
134 			}
135 			ret = inflate(&z->zstream, Z_SYNC_FLUSH);
136 			if (ret == Z_STREAM_END) {
137 				if (!z->region->indeterminate &&
138 				    z->region->length_read != z->region->length) {
139 					OPS_ERROR(cbinfo->errors,
140 						OPS_E_P_DECOMPRESSION_ERROR,
141 						"Compressed stream ended before packet end.");
142 				}
143 			} else if (ret != Z_OK) {
144 				(void) fprintf(stderr, "ret=%d\n", ret);
145 				OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, z->zstream.msg);
146 			}
147 			z->inflate_ret = ret;
148 		}
149 		assert(z->zstream.next_out > &z->out[z->offset]);
150 		len = z->zstream.next_out - &z->out[z->offset];
151 		if (len > length) {
152 			len = length;
153 		}
154 		(void) memcpy(&cdest[cc], &z->out[z->offset], len);
155 		z->offset += len;
156 	}
157 
158 	return length;
159 }
160 
161 /* \todo remove code duplication between this and zlib_compressed_data_reader */
162 static int
163 bzip2_compressed_data_reader(void *dest, size_t length,
164 			     __ops_error_t ** errors,
165 			     __ops_reader_info_t * rinfo,
166 			     __ops_parse_cb_info_t * cbinfo)
167 {
168 	bz_decompress_t *bz = __ops_reader_get_arg(rinfo);
169 	size_t		len;
170 	size_t		 cc;
171 	char		*cdest = dest;
172 
173 	assert(bz->type == OPS_C_BZIP2);
174 
175 	if (bz->inflate_ret == BZ_STREAM_END &&
176 	    bz->bzstream.next_out == &bz->out[bz->offset]) {
177 		return 0;
178 	}
179 	if (bz->region->length_read == bz->region->length) {
180 		if (bz->inflate_ret != BZ_STREAM_END) {
181 			OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,
182 				"Compressed data didn't end when region ended.");
183 		}
184 	}
185 	for (cc = 0 ; cc < length ; cc += len) {
186 		if (&bz->out[bz->offset] == bz->bzstream.next_out) {
187 			int             ret;
188 
189 			bz->bzstream.next_out = (char *) bz->out;
190 			bz->bzstream.avail_out = sizeof(bz->out);
191 			bz->offset = 0;
192 			if (bz->bzstream.avail_in == 0) {
193 				unsigned        n = bz->region->length;
194 
195 				if (!bz->region->indeterminate) {
196 					n -= bz->region->length_read;
197 					if (n > sizeof(bz->in))
198 						n = sizeof(bz->in);
199 				} else
200 					n = sizeof(bz->in);
201 
202 				if (!__ops_stacked_limited_read((unsigned char *) bz->in, n, bz->region,
203 						     errors, rinfo, cbinfo))
204 					return -1;
205 
206 				bz->bzstream.next_in = bz->in;
207 				bz->bzstream.avail_in = bz->region->indeterminate
208 					? bz->region->last_read : n;
209 			}
210 			ret = BZ2_bzDecompress(&bz->bzstream);
211 			if (ret == BZ_STREAM_END) {
212 				if (!bz->region->indeterminate &&
213 				    bz->region->length_read != bz->region->length)
214 					OPS_ERROR(cbinfo->errors,
215 						OPS_E_P_DECOMPRESSION_ERROR,
216 						"Compressed stream ended before packet end.");
217 			} else if (ret != BZ_OK) {
218 				OPS_ERROR_1(cbinfo->errors,
219 					OPS_E_P_DECOMPRESSION_ERROR,
220 					"Invalid return %d from BZ2_bzDecompress", ret);
221 			}
222 			bz->inflate_ret = ret;
223 		}
224 		assert(bz->bzstream.next_out > &bz->out[bz->offset]);
225 		len = bz->bzstream.next_out - &bz->out[bz->offset];
226 		if (len > length)
227 			len = length;
228 		(void) memcpy(&cdest[cc], &bz->out[bz->offset], len);
229 		bz->offset += len;
230 	}
231 
232 	return length;
233 }
234 
235 /**
236  * \ingroup Core_Compress
237  *
238  * \param *region 	Pointer to a region
239  * \param *parse_info 	How to parse
240  * \param type Which compression type to expect
241 */
242 
243 int
244 __ops_decompress(__ops_region_t *region, __ops_parse_info_t *parse_info,
245 	       __ops_compression_type_t type)
246 {
247 	z_decompress_t z;
248 	bz_decompress_t bz;
249 	int             ret;
250 
251 	switch (type) {
252 	case OPS_C_ZIP:
253 	case OPS_C_ZLIB:
254 		(void) memset(&z, 0x0, sizeof(z));
255 
256 		z.region = region;
257 		z.offset = 0;
258 		z.type = type;
259 
260 		z.zstream.next_in = Z_NULL;
261 		z.zstream.avail_in = 0;
262 		z.zstream.next_out = z.out;
263 		z.zstream.zalloc = Z_NULL;
264 		z.zstream.zfree = Z_NULL;
265 		z.zstream.opaque = Z_NULL;
266 
267 		break;
268 
269 	case OPS_C_BZIP2:
270 		(void) memset(&bz, 0x0, sizeof(bz));
271 
272 		bz.region = region;
273 		bz.offset = 0;
274 		bz.type = type;
275 
276 		bz.bzstream.next_in = NULL;
277 		bz.bzstream.avail_in = 0;
278 		bz.bzstream.next_out = bz.out;
279 		bz.bzstream.bzalloc = NULL;
280 		bz.bzstream.bzfree = NULL;
281 		bz.bzstream.opaque = NULL;
282 
283 		break;
284 
285 	default:
286 		OPS_ERROR_1(&parse_info->errors,
287 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
288 			"Compression algorithm %d is not yet supported", type);
289 		return 0;
290 	}
291 
292 	switch (type) {
293 	case OPS_C_ZIP:
294 		ret = inflateInit2(&z.zstream, -15);
295 		break;
296 
297 	case OPS_C_ZLIB:
298 		ret = inflateInit(&z.zstream);
299 		break;
300 
301 	case OPS_C_BZIP2:
302 		ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0);
303 		break;
304 
305 	default:
306 		OPS_ERROR_1(&parse_info->errors,
307 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
308 			"Compression algorithm %d is not yet supported", type);
309 		return 0;
310 	}
311 
312 	switch (type) {
313 	case OPS_C_ZIP:
314 	case OPS_C_ZLIB:
315 		if (ret != Z_OK) {
316 			OPS_ERROR_1(&parse_info->errors,
317 				OPS_E_P_DECOMPRESSION_ERROR,
318 				"Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret);
319 			return 0;
320 		}
321 		__ops_reader_push(parse_info, zlib_compressed_data_reader, NULL, &z);
322 		break;
323 
324 	case OPS_C_BZIP2:
325 		if (ret != BZ_OK) {
326 			OPS_ERROR_1(&parse_info->errors,
327 				OPS_E_P_DECOMPRESSION_ERROR,
328 				"Cannot initialise BZIP2 stream for decompression: error=%d", ret);
329 			return 0;
330 		}
331 		__ops_reader_push(parse_info, bzip2_compressed_data_reader, NULL, &bz);
332 		break;
333 
334 	default:
335 		OPS_ERROR_1(&parse_info->errors,
336 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
337 			"Compression algorithm %d is not yet supported", type);
338 		return 0;
339 	}
340 
341 	ret = __ops_parse(parse_info);
342 
343 	__ops_reader_pop(parse_info);
344 
345 	return ret;
346 }
347 
348 /**
349 \ingroup Core_WritePackets
350 \brief Writes Compressed packet
351 \param data Data to write out
352 \param len Length of data
353 \param cinfo Write settings
354 \return true if OK; else false
355 */
356 
357 bool
358 __ops_write_compressed(const unsigned char *data,
359 		     const unsigned int len,
360 		     __ops_create_info_t *cinfo)
361 {
362 	compress_t	*zip = calloc(1, sizeof(compress_t));
363 	size_t		 sz_in = 0;
364 	size_t		 sz_out = 0;
365 	int              r = 0;
366 
367 	/* compress the data */
368 	const int       level = Z_DEFAULT_COMPRESSION;	/* \todo allow varying
369 							 * levels */
370 	zip->stream.zalloc = Z_NULL;
371 	zip->stream.zfree = Z_NULL;
372 	zip->stream.opaque = NULL;
373 
374 	/* all other fields set to zero by use of calloc */
375 
376 	if (deflateInit(&zip->stream, level) != Z_OK) {
377 		/* can't initialise */
378 		assert(/* CONSTCOND */0);
379 	}
380 	/* do necessary transformation */
381 	/* copy input to maintain const'ness of src */
382 	assert(zip->src == NULL);
383 	assert(zip->dst == NULL);
384 
385 	sz_in = len * sizeof(unsigned char);
386 	sz_out = (sz_in * 1.01) + 12;	/* from zlib webpage */
387 	zip->src = calloc(1, sz_in);
388 	zip->dst = calloc(1, sz_out);
389 	(void) memcpy(zip->src, data, len);
390 
391 	/* setup stream */
392 	zip->stream.next_in = zip->src;
393 	zip->stream.avail_in = sz_in;
394 	zip->stream.total_in = 0;
395 
396 	zip->stream.next_out = zip->dst;
397 	zip->stream.avail_out = sz_out;
398 	zip->stream.total_out = 0;
399 
400 	r = deflate(&zip->stream, Z_FINISH);
401 	assert(r == Z_STREAM_END);	/* need to loop if not */
402 
403 	/* write it out */
404 	return (__ops_write_ptag(OPS_PTAG_CT_COMPRESSED, cinfo) &&
405 		__ops_write_length((unsigned)(zip->stream.total_out + 1), cinfo) &&
406 		__ops_write_scalar(OPS_C_ZLIB, 1, cinfo) &&
407 		__ops_write(zip->dst, (unsigned)zip->stream.total_out, cinfo));
408 }
409