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