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.12 2009/06/09 00:51:01 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 unsigned char in[DECOMPRESS_BUFFER]; 86 unsigned char 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 unsigned char *src; 105 unsigned char *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 = 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 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 (unsigned char *) 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 = 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 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 ret = inflateInit2(&z.zstream, -15); 350 break; 351 352 case OPS_C_ZLIB: 353 ret = inflateInit(&z.zstream); 354 break; 355 356 case OPS_C_BZIP2: 357 ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0); 358 break; 359 360 default: 361 OPS_ERROR_1(&stream->errors, 362 OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, 363 "Compression algorithm %d is not yet supported", type); 364 return 0; 365 } 366 367 switch (type) { 368 case OPS_C_ZIP: 369 case OPS_C_ZLIB: 370 if (ret != Z_OK) { 371 OPS_ERROR_1(&stream->errors, 372 OPS_E_P_DECOMPRESSION_ERROR, 373 "Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret); 374 return 0; 375 } 376 __ops_reader_push(stream, zlib_compressed_data_reader, 377 NULL, &z); 378 break; 379 380 case OPS_C_BZIP2: 381 if (ret != BZ_OK) { 382 OPS_ERROR_1(&stream->errors, 383 OPS_E_P_DECOMPRESSION_ERROR, 384 "Cannot initialise BZIP2 stream for decompression: error=%d", ret); 385 return 0; 386 } 387 __ops_reader_push(stream, bzip2_compressed_data_reader, 388 NULL, &bz); 389 break; 390 391 default: 392 OPS_ERROR_1(&stream->errors, 393 OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, 394 "Compression algorithm %d is not yet supported", type); 395 return 0; 396 } 397 398 ret = __ops_parse(stream, !printerrors); 399 400 __ops_reader_pop(stream); 401 402 return ret; 403 } 404 405 /** 406 \ingroup Core_WritePackets 407 \brief Writes Compressed packet 408 \param data Data to write out 409 \param len Length of data 410 \param output Write settings 411 \return 1 if OK; else 0 412 */ 413 414 unsigned 415 __ops_writez(const unsigned char *data, 416 const unsigned int len, 417 __ops_output_t *out) 418 { 419 compress_t *zip = calloc(1, sizeof(compress_t)); 420 size_t sz_in = 0; 421 size_t sz_out = 0; 422 int r = 0; 423 424 /* compress the data */ 425 const int level = Z_DEFAULT_COMPRESSION; /* \todo allow varying 426 * levels */ 427 zip->stream.zalloc = Z_NULL; 428 zip->stream.zfree = Z_NULL; 429 zip->stream.opaque = NULL; 430 431 /* all other fields set to zero by use of calloc */ 432 433 if (deflateInit(&zip->stream, level) != Z_OK) { 434 (void) fprintf(stderr, "__ops_writez: can't initialise\n"); 435 return 0; 436 } 437 /* do necessary transformation */ 438 /* copy input to maintain const'ness of src */ 439 if (zip->src != NULL || zip->dst != NULL) { 440 (void) fprintf(stderr, "__ops_writez: non-null streams\n"); 441 return 0; 442 } 443 444 sz_in = len * sizeof(unsigned char); 445 sz_out = (sz_in * 1.01) + 12; /* from zlib webpage */ 446 zip->src = calloc(1, sz_in); 447 zip->dst = calloc(1, sz_out); 448 (void) memcpy(zip->src, data, len); 449 450 /* setup stream */ 451 zip->stream.next_in = zip->src; 452 zip->stream.avail_in = sz_in; 453 zip->stream.total_in = 0; 454 455 zip->stream.next_out = zip->dst; 456 zip->stream.avail_out = sz_out; 457 zip->stream.total_out = 0; 458 459 do { 460 r = deflate(&zip->stream, Z_FINISH); 461 } while (r != Z_STREAM_END); 462 463 /* write it out */ 464 return (__ops_write_ptag(out, OPS_PTAG_CT_COMPRESSED) && 465 __ops_write_length(out, (unsigned)(zip->stream.total_out + 1))&& 466 __ops_write_scalar(out, OPS_C_ZLIB, 1) && 467 __ops_write(out, zip->dst, (unsigned)zip->stream.total_out)); 468 } 469