1 /*- 2 * Copyright (c) 2009 The NetBSD Foundation, Inc. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to The NetBSD Foundation 6 * by Alistair Crooks (agc@NetBSD.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 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 * POSSIBILITY OF SUCH DAMAGE. 28 */ 29 /* 30 * Copyright (c) 2005-2008 Nominet UK (www.nic.uk) 31 * All rights reserved. 32 * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted 33 * their moral rights under the UK Copyright Design and Patents Act 1988 to 34 * be recorded as the authors of this copyright work. 35 * 36 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 37 * use this file except in compliance with the License. 38 * 39 * You may obtain a copy of the License at 40 * http://www.apache.org/licenses/LICENSE-2.0 41 * 42 * Unless required by applicable law or agreed to in writing, software 43 * distributed under the License is distributed on an "AS IS" BASIS, 44 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 45 * 46 * See the License for the specific language governing permissions and 47 * limitations under the License. 48 */ 49 50 /** \file 51 */ 52 #include "config.h" 53 54 #ifdef HAVE_SYS_CDEFS_H 55 #include <sys/cdefs.h> 56 #endif 57 58 #if defined(__NetBSD__) 59 __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved."); 60 __RCSID("$NetBSD: compress.c,v 1.17 2010/08/15 16:10:56 agc Exp $"); 61 #endif 62 63 #ifdef HAVE_ZLIB_H 64 #include <zlib.h> 65 #endif 66 67 #ifdef HAVE_BZLIB_H 68 #include <bzlib.h> 69 #endif 70 71 #include <string.h> 72 73 #include "packet-parse.h" 74 #include "errors.h" 75 #include "netpgpdefs.h" 76 #include "crypto.h" 77 #include "memory.h" 78 #include "writer.h" 79 80 #define DECOMPRESS_BUFFER 1024 81 82 typedef struct { 83 __ops_compression_type_t type; 84 __ops_region_t *region; 85 uint8_t in[DECOMPRESS_BUFFER]; 86 uint8_t out[DECOMPRESS_BUFFER]; 87 z_stream zstream;/* ZIP and ZLIB */ 88 size_t offset; 89 int inflate_ret; 90 } z_decompress_t; 91 92 typedef struct { 93 __ops_compression_type_t type; 94 __ops_region_t *region; 95 char in[DECOMPRESS_BUFFER]; 96 char out[DECOMPRESS_BUFFER]; 97 bz_stream bzstream; /* BZIP2 */ 98 size_t offset; 99 int inflate_ret; 100 } bz_decompress_t; 101 102 typedef struct { 103 z_stream stream; 104 uint8_t *src; 105 uint8_t *dst; 106 } compress_t; 107 108 /* 109 * \todo remove code duplication between this and 110 * bzip2_compressed_data_reader 111 */ 112 static int 113 zlib_compressed_data_reader(void *dest, size_t length, 114 __ops_error_t **errors, 115 __ops_reader_t *readinfo, 116 __ops_cbdata_t *cbinfo) 117 { 118 z_decompress_t *z = __ops_reader_get_arg(readinfo); 119 size_t len; 120 size_t cc; 121 char *cdest = dest; 122 123 if (z->type != OPS_C_ZIP && z->type != OPS_C_ZLIB) { 124 (void) fprintf(stderr, 125 "zlib_compressed_data_reader: weird type %d\n", 126 z->type); 127 return 0; 128 } 129 130 if (z->inflate_ret == Z_STREAM_END && 131 z->zstream.next_out == &z->out[z->offset]) { 132 return 0; 133 } 134 135 if (__ops_get_debug_level(__FILE__)) { 136 (void) fprintf(stderr, 137 "zlib_compressed_data_reader: length %" PRIsize "d\n", 138 length); 139 } 140 141 if (z->region->readc == z->region->length) { 142 if (z->inflate_ret != Z_STREAM_END) { 143 OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, 144 "Compressed data didn't end when region ended."); 145 } 146 } 147 for (cc = 0 ; cc < length ; cc += len) { 148 if (&z->out[z->offset] == z->zstream.next_out) { 149 int ret; 150 151 z->zstream.next_out = z->out; 152 z->zstream.avail_out = sizeof(z->out); 153 z->offset = 0; 154 if (z->zstream.avail_in == 0) { 155 unsigned n = z->region->length; 156 157 if (!z->region->indeterminate) { 158 n -= z->region->readc; 159 if (n > sizeof(z->in)) { 160 n = sizeof(z->in); 161 } 162 } else { 163 n = sizeof(z->in); 164 } 165 if (!__ops_stacked_limited_read(z->in, n, 166 z->region, 167 errors, readinfo, cbinfo)) { 168 return -1; 169 } 170 171 z->zstream.next_in = z->in; 172 z->zstream.avail_in = (z->region->indeterminate) ? 173 z->region->last_read : n; 174 } 175 ret = inflate(&z->zstream, Z_SYNC_FLUSH); 176 if (ret == Z_STREAM_END) { 177 if (!z->region->indeterminate && 178 z->region->readc != z->region->length) { 179 OPS_ERROR(cbinfo->errors, 180 OPS_E_P_DECOMPRESSION_ERROR, 181 "Compressed stream ended before packet end."); 182 } 183 } else if (ret != Z_OK) { 184 (void) fprintf(stderr, "ret=%d\n", ret); 185 OPS_ERROR(cbinfo->errors, 186 OPS_E_P_DECOMPRESSION_ERROR, z->zstream.msg); 187 } 188 z->inflate_ret = ret; 189 } 190 if (z->zstream.next_out <= &z->out[z->offset]) { 191 (void) fprintf(stderr, "Out of memory in buffer\n"); 192 return 0; 193 } 194 len = (size_t)(z->zstream.next_out - &z->out[z->offset]); 195 if (len > length) { 196 len = length; 197 } 198 (void) memcpy(&cdest[cc], &z->out[z->offset], len); 199 z->offset += len; 200 } 201 202 return (int)length; 203 } 204 205 /* \todo remove code duplication between this and zlib_compressed_data_reader */ 206 static int 207 bzip2_compressed_data_reader(void *dest, size_t length, 208 __ops_error_t **errors, 209 __ops_reader_t *readinfo, 210 __ops_cbdata_t *cbinfo) 211 { 212 bz_decompress_t *bz = __ops_reader_get_arg(readinfo); 213 size_t len; 214 size_t cc; 215 char *cdest = dest; 216 217 if (bz->type != OPS_C_BZIP2) { 218 (void) fprintf(stderr, "Weird type %d\n", bz->type); 219 return 0; 220 } 221 222 if (bz->inflate_ret == BZ_STREAM_END && 223 bz->bzstream.next_out == &bz->out[bz->offset]) { 224 return 0; 225 } 226 if (bz->region->readc == bz->region->length) { 227 if (bz->inflate_ret != BZ_STREAM_END) { 228 OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, 229 "Compressed data didn't end when region ended."); 230 } 231 } 232 for (cc = 0 ; cc < length ; cc += len) { 233 if (&bz->out[bz->offset] == bz->bzstream.next_out) { 234 int ret; 235 236 bz->bzstream.next_out = (char *) bz->out; 237 bz->bzstream.avail_out = sizeof(bz->out); 238 bz->offset = 0; 239 if (bz->bzstream.avail_in == 0) { 240 unsigned n = bz->region->length; 241 242 if (!bz->region->indeterminate) { 243 n -= bz->region->readc; 244 if (n > sizeof(bz->in)) 245 n = sizeof(bz->in); 246 } else 247 n = sizeof(bz->in); 248 249 if (!__ops_stacked_limited_read( 250 (uint8_t *) bz->in, 251 n, bz->region, 252 errors, readinfo, cbinfo)) 253 return -1; 254 255 bz->bzstream.next_in = bz->in; 256 bz->bzstream.avail_in = 257 (bz->region->indeterminate) ? 258 bz->region->last_read : n; 259 } 260 ret = BZ2_bzDecompress(&bz->bzstream); 261 if (ret == BZ_STREAM_END) { 262 if (!bz->region->indeterminate && 263 bz->region->readc != bz->region->length) 264 OPS_ERROR(cbinfo->errors, 265 OPS_E_P_DECOMPRESSION_ERROR, 266 "Compressed stream ended before packet end."); 267 } else if (ret != BZ_OK) { 268 OPS_ERROR_1(cbinfo->errors, 269 OPS_E_P_DECOMPRESSION_ERROR, 270 "Invalid return %d from BZ2_bzDecompress", ret); 271 } 272 bz->inflate_ret = ret; 273 } 274 if (bz->bzstream.next_out <= &bz->out[bz->offset]) { 275 (void) fprintf(stderr, "Out of bz memroy\n"); 276 return 0; 277 } 278 len = (size_t)(bz->bzstream.next_out - &bz->out[bz->offset]); 279 if (len > length) { 280 len = length; 281 } 282 (void) memcpy(&cdest[cc], &bz->out[bz->offset], len); 283 bz->offset += len; 284 } 285 286 return (int)length; 287 } 288 289 /** 290 * \ingroup Core_Compress 291 * 292 * \param *region Pointer to a region 293 * \param *stream How to parse 294 * \param type Which compression type to expect 295 */ 296 297 int 298 __ops_decompress(__ops_region_t *region, __ops_stream_t *stream, 299 __ops_compression_type_t type) 300 { 301 z_decompress_t z; 302 bz_decompress_t bz; 303 const int printerrors = 1; 304 int ret; 305 306 switch (type) { 307 case OPS_C_ZIP: 308 case OPS_C_ZLIB: 309 (void) memset(&z, 0x0, sizeof(z)); 310 311 z.region = region; 312 z.offset = 0; 313 z.type = type; 314 315 z.zstream.next_in = Z_NULL; 316 z.zstream.avail_in = 0; 317 z.zstream.next_out = z.out; 318 z.zstream.zalloc = Z_NULL; 319 z.zstream.zfree = Z_NULL; 320 z.zstream.opaque = Z_NULL; 321 322 break; 323 324 case OPS_C_BZIP2: 325 (void) memset(&bz, 0x0, sizeof(bz)); 326 327 bz.region = region; 328 bz.offset = 0; 329 bz.type = type; 330 331 bz.bzstream.next_in = NULL; 332 bz.bzstream.avail_in = 0; 333 bz.bzstream.next_out = bz.out; 334 bz.bzstream.bzalloc = NULL; 335 bz.bzstream.bzfree = NULL; 336 bz.bzstream.opaque = NULL; 337 338 break; 339 340 default: 341 OPS_ERROR_1(&stream->errors, 342 OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, 343 "Compression algorithm %d is not yet supported", type); 344 return 0; 345 } 346 347 switch (type) { 348 case OPS_C_ZIP: 349 /* LINTED */ /* this is a lint problem in zlib.h header */ 350 ret = (int)inflateInit2(&z.zstream, -15); 351 break; 352 353 case OPS_C_ZLIB: 354 /* LINTED */ /* this is a lint problem in zlib.h header */ 355 ret = (int)inflateInit(&z.zstream); 356 break; 357 358 case OPS_C_BZIP2: 359 ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0); 360 break; 361 362 default: 363 OPS_ERROR_1(&stream->errors, 364 OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, 365 "Compression algorithm %d is not yet supported", type); 366 return 0; 367 } 368 369 switch (type) { 370 case OPS_C_ZIP: 371 case OPS_C_ZLIB: 372 if (ret != Z_OK) { 373 OPS_ERROR_1(&stream->errors, 374 OPS_E_P_DECOMPRESSION_ERROR, 375 "Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret); 376 return 0; 377 } 378 __ops_reader_push(stream, zlib_compressed_data_reader, 379 NULL, &z); 380 break; 381 382 case OPS_C_BZIP2: 383 if (ret != BZ_OK) { 384 OPS_ERROR_1(&stream->errors, 385 OPS_E_P_DECOMPRESSION_ERROR, 386 "Cannot initialise BZIP2 stream for decompression: error=%d", ret); 387 return 0; 388 } 389 __ops_reader_push(stream, bzip2_compressed_data_reader, 390 NULL, &bz); 391 break; 392 393 default: 394 OPS_ERROR_1(&stream->errors, 395 OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, 396 "Compression algorithm %d is not yet supported", type); 397 return 0; 398 } 399 400 ret = __ops_parse(stream, !printerrors); 401 402 __ops_reader_pop(stream); 403 404 return ret; 405 } 406 407 /** 408 \ingroup Core_WritePackets 409 \brief Writes Compressed packet 410 \param data Data to write out 411 \param len Length of data 412 \param output Write settings 413 \return 1 if OK; else 0 414 */ 415 416 unsigned 417 __ops_writez(__ops_output_t *out, const uint8_t *data, const unsigned len) 418 { 419 compress_t *zip; 420 size_t sz_in; 421 size_t sz_out; 422 int ret; 423 int r = 0; 424 425 /* compress the data */ 426 const int level = Z_DEFAULT_COMPRESSION; /* \todo allow varying 427 * levels */ 428 429 if ((zip = calloc(1, sizeof(*zip))) == NULL) { 430 (void) fprintf(stderr, "__ops_writez: bad alloc\n"); 431 return 0; 432 } 433 zip->stream.zalloc = Z_NULL; 434 zip->stream.zfree = Z_NULL; 435 zip->stream.opaque = NULL; 436 437 /* all other fields set to zero by use of calloc */ 438 439 /* LINTED */ /* this is a lint problem in zlib.h header */ 440 if ((int)deflateInit(&zip->stream, level) != Z_OK) { 441 (void) fprintf(stderr, "__ops_writez: can't initialise\n"); 442 return 0; 443 } 444 /* do necessary transformation */ 445 /* copy input to maintain const'ness of src */ 446 if (zip->src != NULL || zip->dst != NULL) { 447 (void) fprintf(stderr, "__ops_writez: non-null streams\n"); 448 return 0; 449 } 450 451 sz_in = len * sizeof(uint8_t); 452 sz_out = ((101 * sz_in) / 100) + 12; /* from zlib webpage */ 453 if ((zip->src = calloc(1, sz_in)) == NULL) { 454 free(zip); 455 (void) fprintf(stderr, "__ops_writez: bad alloc2\n"); 456 return 0; 457 } 458 if ((zip->dst = calloc(1, sz_out)) == NULL) { 459 free(zip->src); 460 free(zip); 461 (void) fprintf(stderr, "__ops_writez: bad alloc3\n"); 462 return 0; 463 } 464 (void) memcpy(zip->src, data, len); 465 466 /* setup stream */ 467 zip->stream.next_in = zip->src; 468 zip->stream.avail_in = (unsigned)sz_in; 469 zip->stream.total_in = 0; 470 471 zip->stream.next_out = zip->dst; 472 zip->stream.avail_out = (unsigned)sz_out; 473 zip->stream.total_out = 0; 474 475 do { 476 r = deflate(&zip->stream, Z_FINISH); 477 } while (r != Z_STREAM_END); 478 479 /* write it out */ 480 ret = __ops_write_ptag(out, OPS_PTAG_CT_COMPRESSED) && 481 __ops_write_length(out, (unsigned)(zip->stream.total_out + 1))&& 482 __ops_write_scalar(out, OPS_C_ZLIB, 1) && 483 __ops_write(out, zip->dst, (unsigned)zip->stream.total_out); 484 485 free(zip->src); 486 free(zip->dst); 487 free(zip); 488 return ret; 489 } 490