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