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_ZLIB_H 55 #include <zlib.h> 56 #endif 57 58 #ifdef HAVE_BZLIB_H 59 #include <bzlib.h> 60 #endif 61 62 #include <string.h> 63 64 #include "packet-parse.h" 65 #include "errors.h" 66 #include "netpgpdefs.h" 67 #include "parse_local.h" 68 #include "memory.h" 69 #include "writer.h" 70 71 #define DECOMPRESS_BUFFER 1024 72 73 typedef struct { 74 __ops_compression_type_t type; 75 __ops_region_t *region; 76 unsigned char in[DECOMPRESS_BUFFER]; 77 unsigned char out[DECOMPRESS_BUFFER]; 78 z_stream zstream;/* ZIP and ZLIB */ 79 size_t offset; 80 int inflate_ret; 81 } z_decompress_t; 82 83 typedef struct { 84 __ops_compression_type_t type; 85 __ops_region_t *region; 86 char in[DECOMPRESS_BUFFER]; 87 char out[DECOMPRESS_BUFFER]; 88 bz_stream bzstream; /* BZIP2 */ 89 size_t offset; 90 int inflate_ret; 91 } bz_decompress_t; 92 93 typedef struct { 94 z_stream stream; 95 unsigned char *src; 96 unsigned char *dst; 97 } compress_t; 98 99 /* 100 * \todo remove code duplication between this and 101 * bzip2_compressed_data_reader 102 */ 103 static int 104 zlib_compressed_data_reader(void *dest, size_t length, 105 __ops_error_t **errors, 106 __ops_reader_t *readinfo, 107 __ops_callback_data_t *cbinfo) 108 { 109 z_decompress_t *z = __ops_reader_get_arg(readinfo); 110 size_t len; 111 size_t cc; 112 char *cdest = dest; 113 114 if (z->type != OPS_C_ZIP && z->type != OPS_C_ZLIB) { 115 (void) fprintf(stderr, 116 "zlib_compressed_data_reader: weird type %d\n", 117 z->type); 118 return 0; 119 } 120 121 if (z->inflate_ret == Z_STREAM_END && 122 z->zstream.next_out == &z->out[z->offset]) { 123 return 0; 124 } 125 126 if (__ops_get_debug_level(__FILE__)) { 127 (void) fprintf(stderr, "zlib_compressed_data_reader: length %" PRIsize "d\n", length); 128 } 129 130 if (z->region->length_read == z->region->length) { 131 if (z->inflate_ret != Z_STREAM_END) { 132 OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, 133 "Compressed data didn't end when region ended."); 134 } 135 } 136 for (cc = 0 ; cc < length ; cc += len) { 137 if (&z->out[z->offset] == z->zstream.next_out) { 138 int ret; 139 140 z->zstream.next_out = z->out; 141 z->zstream.avail_out = sizeof(z->out); 142 z->offset = 0; 143 if (z->zstream.avail_in == 0) { 144 unsigned n = z->region->length; 145 146 if (!z->region->indeterminate) { 147 n -= z->region->length_read; 148 if (n > sizeof(z->in)) { 149 n = sizeof(z->in); 150 } 151 } else { 152 n = sizeof(z->in); 153 } 154 155 if (!__ops_stacked_limited_read(z->in, n, z->region, 156 errors, readinfo, cbinfo)) { 157 return -1; 158 } 159 160 z->zstream.next_in = z->in; 161 z->zstream.avail_in = (z->region->indeterminate) ? 162 z->region->last_read : n; 163 } 164 ret = inflate(&z->zstream, Z_SYNC_FLUSH); 165 if (ret == Z_STREAM_END) { 166 if (!z->region->indeterminate && 167 z->region->length_read != z->region->length) { 168 OPS_ERROR(cbinfo->errors, 169 OPS_E_P_DECOMPRESSION_ERROR, 170 "Compressed stream ended before packet end."); 171 } 172 } else if (ret != Z_OK) { 173 (void) fprintf(stderr, "ret=%d\n", ret); 174 OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, z->zstream.msg); 175 } 176 z->inflate_ret = ret; 177 } 178 if (z->zstream.next_out <= &z->out[z->offset]) { 179 (void) fprintf(stderr, "Out of memory in buffer\n"); 180 return 0; 181 } 182 len = z->zstream.next_out - &z->out[z->offset]; 183 if (len > length) { 184 len = length; 185 } 186 (void) memcpy(&cdest[cc], &z->out[z->offset], len); 187 z->offset += len; 188 } 189 190 return length; 191 } 192 193 /* \todo remove code duplication between this and zlib_compressed_data_reader */ 194 static int 195 bzip2_compressed_data_reader(void *dest, size_t length, 196 __ops_error_t **errors, 197 __ops_reader_t *readinfo, 198 __ops_callback_data_t *cbinfo) 199 { 200 bz_decompress_t *bz = __ops_reader_get_arg(readinfo); 201 size_t len; 202 size_t cc; 203 char *cdest = dest; 204 205 if (bz->type != OPS_C_BZIP2) { 206 (void) fprintf(stderr, "Weird type %d\n", bz->type); 207 return 0; 208 } 209 210 if (bz->inflate_ret == BZ_STREAM_END && 211 bz->bzstream.next_out == &bz->out[bz->offset]) { 212 return 0; 213 } 214 if (bz->region->length_read == bz->region->length) { 215 if (bz->inflate_ret != BZ_STREAM_END) { 216 OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, 217 "Compressed data didn't end when region ended."); 218 } 219 } 220 for (cc = 0 ; cc < length ; cc += len) { 221 if (&bz->out[bz->offset] == bz->bzstream.next_out) { 222 int ret; 223 224 bz->bzstream.next_out = (char *) bz->out; 225 bz->bzstream.avail_out = sizeof(bz->out); 226 bz->offset = 0; 227 if (bz->bzstream.avail_in == 0) { 228 unsigned n = bz->region->length; 229 230 if (!bz->region->indeterminate) { 231 n -= bz->region->length_read; 232 if (n > sizeof(bz->in)) 233 n = sizeof(bz->in); 234 } else 235 n = sizeof(bz->in); 236 237 if (!__ops_stacked_limited_read((unsigned char *) bz->in, n, bz->region, 238 errors, readinfo, cbinfo)) 239 return -1; 240 241 bz->bzstream.next_in = bz->in; 242 bz->bzstream.avail_in = bz->region->indeterminate 243 ? bz->region->last_read : n; 244 } 245 ret = BZ2_bzDecompress(&bz->bzstream); 246 if (ret == BZ_STREAM_END) { 247 if (!bz->region->indeterminate && 248 bz->region->length_read != bz->region->length) 249 OPS_ERROR(cbinfo->errors, 250 OPS_E_P_DECOMPRESSION_ERROR, 251 "Compressed stream ended before packet end."); 252 } else if (ret != BZ_OK) { 253 OPS_ERROR_1(cbinfo->errors, 254 OPS_E_P_DECOMPRESSION_ERROR, 255 "Invalid return %d from BZ2_bzDecompress", ret); 256 } 257 bz->inflate_ret = ret; 258 } 259 if (bz->bzstream.next_out <= &bz->out[bz->offset]) { 260 (void) fprintf(stderr, "Out of bz memroy\n"); 261 return 0; 262 } 263 len = bz->bzstream.next_out - &bz->out[bz->offset]; 264 if (len > length) { 265 len = length; 266 } 267 (void) memcpy(&cdest[cc], &bz->out[bz->offset], len); 268 bz->offset += len; 269 } 270 271 return length; 272 } 273 274 /** 275 * \ingroup Core_Compress 276 * 277 * \param *region Pointer to a region 278 * \param *parse_info How to parse 279 * \param type Which compression type to expect 280 */ 281 282 int 283 __ops_decompress(__ops_region_t *region, __ops_parseinfo_t *parse_info, 284 __ops_compression_type_t type) 285 { 286 z_decompress_t z; 287 bz_decompress_t bz; 288 int ret; 289 290 switch (type) { 291 case OPS_C_ZIP: 292 case OPS_C_ZLIB: 293 (void) memset(&z, 0x0, sizeof(z)); 294 295 z.region = region; 296 z.offset = 0; 297 z.type = type; 298 299 z.zstream.next_in = Z_NULL; 300 z.zstream.avail_in = 0; 301 z.zstream.next_out = z.out; 302 z.zstream.zalloc = Z_NULL; 303 z.zstream.zfree = Z_NULL; 304 z.zstream.opaque = Z_NULL; 305 306 break; 307 308 case OPS_C_BZIP2: 309 (void) memset(&bz, 0x0, sizeof(bz)); 310 311 bz.region = region; 312 bz.offset = 0; 313 bz.type = type; 314 315 bz.bzstream.next_in = NULL; 316 bz.bzstream.avail_in = 0; 317 bz.bzstream.next_out = bz.out; 318 bz.bzstream.bzalloc = NULL; 319 bz.bzstream.bzfree = NULL; 320 bz.bzstream.opaque = NULL; 321 322 break; 323 324 default: 325 OPS_ERROR_1(&parse_info->errors, 326 OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, 327 "Compression algorithm %d is not yet supported", type); 328 return 0; 329 } 330 331 switch (type) { 332 case OPS_C_ZIP: 333 ret = inflateInit2(&z.zstream, -15); 334 break; 335 336 case OPS_C_ZLIB: 337 ret = inflateInit(&z.zstream); 338 break; 339 340 case OPS_C_BZIP2: 341 ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0); 342 break; 343 344 default: 345 OPS_ERROR_1(&parse_info->errors, 346 OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, 347 "Compression algorithm %d is not yet supported", type); 348 return 0; 349 } 350 351 switch (type) { 352 case OPS_C_ZIP: 353 case OPS_C_ZLIB: 354 if (ret != Z_OK) { 355 OPS_ERROR_1(&parse_info->errors, 356 OPS_E_P_DECOMPRESSION_ERROR, 357 "Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret); 358 return 0; 359 } 360 __ops_reader_push(parse_info, zlib_compressed_data_reader, NULL, &z); 361 break; 362 363 case OPS_C_BZIP2: 364 if (ret != BZ_OK) { 365 OPS_ERROR_1(&parse_info->errors, 366 OPS_E_P_DECOMPRESSION_ERROR, 367 "Cannot initialise BZIP2 stream for decompression: error=%d", ret); 368 return 0; 369 } 370 __ops_reader_push(parse_info, bzip2_compressed_data_reader, NULL, &bz); 371 break; 372 373 default: 374 OPS_ERROR_1(&parse_info->errors, 375 OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG, 376 "Compression algorithm %d is not yet supported", type); 377 return 0; 378 } 379 380 ret = __ops_parse(parse_info, 0); 381 382 __ops_reader_pop(parse_info); 383 384 return ret; 385 } 386 387 /** 388 \ingroup Core_WritePackets 389 \brief Writes Compressed packet 390 \param data Data to write out 391 \param len Length of data 392 \param cinfo Write settings 393 \return true if OK; else false 394 */ 395 396 bool 397 __ops_write_compressed(const unsigned char *data, 398 const unsigned int len, 399 __ops_createinfo_t *cinfo) 400 { 401 compress_t *zip = calloc(1, sizeof(compress_t)); 402 size_t sz_in = 0; 403 size_t sz_out = 0; 404 int r = 0; 405 406 /* compress the data */ 407 const int level = Z_DEFAULT_COMPRESSION; /* \todo allow varying 408 * levels */ 409 zip->stream.zalloc = Z_NULL; 410 zip->stream.zfree = Z_NULL; 411 zip->stream.opaque = NULL; 412 413 /* all other fields set to zero by use of calloc */ 414 415 if (deflateInit(&zip->stream, level) != Z_OK) { 416 (void) fprintf(stderr, 417 "__ops_write_compressed: can't initialise\n"); 418 return false; 419 } 420 /* do necessary transformation */ 421 /* copy input to maintain const'ness of src */ 422 if (zip->src != NULL || zip->dst != NULL) { 423 (void) fprintf(stderr, 424 "__ops_write_compressed: non-null streams\n"); 425 return false; 426 } 427 428 sz_in = len * sizeof(unsigned char); 429 sz_out = (sz_in * 1.01) + 12; /* from zlib webpage */ 430 zip->src = calloc(1, sz_in); 431 zip->dst = calloc(1, sz_out); 432 (void) memcpy(zip->src, data, len); 433 434 /* setup stream */ 435 zip->stream.next_in = zip->src; 436 zip->stream.avail_in = sz_in; 437 zip->stream.total_in = 0; 438 439 zip->stream.next_out = zip->dst; 440 zip->stream.avail_out = sz_out; 441 zip->stream.total_out = 0; 442 443 do { 444 r = deflate(&zip->stream, Z_FINISH); 445 } while (r != Z_STREAM_END); 446 447 /* write it out */ 448 return (__ops_write_ptag(OPS_PTAG_CT_COMPRESSED, cinfo) && 449 __ops_write_length((unsigned)(zip->stream.total_out + 1), cinfo) && 450 __ops_write_scalar(OPS_C_ZLIB, 1, cinfo) && 451 __ops_write(zip->dst, (unsigned)zip->stream.total_out, cinfo)); 452 } 453