| 1 | /* $NetBSD: deflate.c,v 1.22 2015/03/26 17:40:16 prlw1 Exp $ */ |
| 2 | /* $FreeBSD: src/sys/opencrypto/deflate.c,v 1.1.2.1 2002/11/21 23:34:23 sam Exp $ */ |
| 3 | /* $OpenBSD: deflate.c,v 1.3 2001/08/20 02:45:22 hugh Exp $ */ |
| 4 | |
| 5 | /* |
| 6 | * Copyright (c) 2001 Jean-Jacques Bernard-Gundol (jj@wabbitt.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 | * |
| 12 | * 1. Redistributions of source code must retain the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer. |
| 14 | * 2. Redistributions in binary form must reproduce the above copyright |
| 15 | * notice, this list of conditions and the following disclaimer in the |
| 16 | * documentation and/or other materials provided with the distribution. |
| 17 | * 3. The name of the author may not be used to endorse or promote products |
| 18 | * derived from this software without specific prior written permission. |
| 19 | * |
| 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| 22 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| 23 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 30 | */ |
| 31 | |
| 32 | /* |
| 33 | * This file contains a wrapper around the deflate algo compression |
| 34 | * functions using the zlib library (see net/zlib.{c,h}) |
| 35 | */ |
| 36 | |
| 37 | #include <sys/cdefs.h> |
| 38 | __KERNEL_RCSID(0, "$NetBSD: deflate.c,v 1.22 2015/03/26 17:40:16 prlw1 Exp $" ); |
| 39 | |
| 40 | #include <sys/types.h> |
| 41 | #include <sys/malloc.h> |
| 42 | #include <sys/param.h> |
| 43 | #include <sys/systm.h> |
| 44 | #include <net/zlib.h> |
| 45 | |
| 46 | #include <opencrypto/cryptodev.h> |
| 47 | #include <opencrypto/deflate.h> |
| 48 | |
| 49 | #define ZBUF 10 |
| 50 | |
| 51 | struct deflate_buf { |
| 52 | u_int8_t *out; |
| 53 | u_int32_t size; |
| 54 | }; |
| 55 | |
| 56 | int window_inflate = -1 * MAX_WBITS; |
| 57 | int window_deflate = -12; |
| 58 | |
| 59 | /* |
| 60 | * This function takes a block of data and (de)compress it using the deflate |
| 61 | * algorithm |
| 62 | */ |
| 63 | |
| 64 | static void * |
| 65 | ocf_zalloc(void *nil, u_int type, u_int size) |
| 66 | { |
| 67 | void *ptr; |
| 68 | |
| 69 | ptr = malloc(type *size, M_CRYPTO_DATA, M_NOWAIT); |
| 70 | return ptr; |
| 71 | } |
| 72 | |
| 73 | static void |
| 74 | ocf_zfree(void *nil, void *ptr) |
| 75 | { |
| 76 | free(ptr, M_CRYPTO_DATA); |
| 77 | } |
| 78 | |
| 79 | u_int32_t |
| 80 | deflate_global(u_int8_t *data, u_int32_t size, int decomp, u_int8_t **out, |
| 81 | int size_hint) |
| 82 | { |
| 83 | /* decomp indicates whether we compress (0) or decompress (1) */ |
| 84 | |
| 85 | z_stream zbuf; |
| 86 | u_int8_t *output; |
| 87 | u_int32_t count, result, tocopy; |
| 88 | int error, i, j; |
| 89 | struct deflate_buf buf[ZBUF]; |
| 90 | |
| 91 | DPRINTF(("deflate_global: size %u\n" , size)); |
| 92 | |
| 93 | memset(&zbuf, 0, sizeof(z_stream)); |
| 94 | zbuf.next_in = data; /* data that is going to be processed */ |
| 95 | zbuf.zalloc = ocf_zalloc; |
| 96 | zbuf.zfree = ocf_zfree; |
| 97 | zbuf.opaque = Z_NULL; |
| 98 | zbuf.avail_in = size; /* Total length of data to be processed */ |
| 99 | |
| 100 | if (!decomp) { |
| 101 | buf[0].size = size; |
| 102 | } else { |
| 103 | /* |
| 104 | * Choose a buffer with 4x the size of the input buffer |
| 105 | * for the size of the output buffer in the case of |
| 106 | * decompression. If it's not sufficient, it will need to be |
| 107 | * updated while the decompression is going on |
| 108 | */ |
| 109 | |
| 110 | buf[0].size = MAX(size * 4, size_hint); |
| 111 | } |
| 112 | buf[0].out = malloc(buf[0].size, M_CRYPTO_DATA, M_NOWAIT); |
| 113 | if (buf[0].out == NULL) |
| 114 | return 0; |
| 115 | i = 1; |
| 116 | |
| 117 | zbuf.next_out = buf[0].out; |
| 118 | zbuf.avail_out = buf[0].size; |
| 119 | |
| 120 | error = decomp ? inflateInit2(&zbuf, window_inflate) : |
| 121 | deflateInit2(&zbuf, Z_DEFAULT_COMPRESSION, Z_METHOD, |
| 122 | window_deflate, Z_MEMLEVEL, Z_DEFAULT_STRATEGY); |
| 123 | |
| 124 | if (error != Z_OK) |
| 125 | goto bad2; |
| 126 | for (;;) { |
| 127 | error = decomp ? inflate(&zbuf, Z_SYNC_FLUSH) : |
| 128 | deflate(&zbuf, Z_FINISH); |
| 129 | if (error == Z_STREAM_END) /* success */ |
| 130 | break; |
| 131 | /* |
| 132 | * XXX compensate for two problems: |
| 133 | * -Former versions of this code didn't set Z_FINISH |
| 134 | * on compression, so the compressed data are not correctly |
| 135 | * terminated and the decompressor doesn't get Z_STREAM_END. |
| 136 | * Accept such packets for interoperability. |
| 137 | * -sys/net/zlib.c has a bug which makes that Z_BUF_ERROR is |
| 138 | * set after successful decompression under rare conditions. |
| 139 | */ |
| 140 | else if (decomp && (error == Z_OK || error == Z_BUF_ERROR) |
| 141 | && zbuf.avail_in == 0 && zbuf.avail_out != 0) |
| 142 | break; |
| 143 | else if (error != Z_OK) |
| 144 | goto bad; |
| 145 | else if (zbuf.avail_out == 0) { |
| 146 | /* we need more output space, allocate size */ |
| 147 | int nextsize = buf[i-1].size * 2; |
| 148 | if (i == ZBUF || nextsize > 1000000) |
| 149 | goto bad; |
| 150 | buf[i].out = malloc(nextsize, M_CRYPTO_DATA, M_NOWAIT); |
| 151 | if (buf[i].out == NULL) |
| 152 | goto bad; |
| 153 | zbuf.next_out = buf[i].out; |
| 154 | zbuf.avail_out = buf[i].size = nextsize; |
| 155 | i++; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | result = count = zbuf.total_out; |
| 160 | |
| 161 | if (i != 1) { /* copy everything into one buffer */ |
| 162 | output = malloc(result, M_CRYPTO_DATA, M_NOWAIT); |
| 163 | if (output == NULL) |
| 164 | goto bad; |
| 165 | *out = output; |
| 166 | for (j = 0; j < i; j++) { |
| 167 | tocopy = MIN(count, buf[j].size); |
| 168 | /* XXX the last buf can be empty */ |
| 169 | KASSERT(tocopy || j == (i - 1)); |
| 170 | memcpy(output, buf[j].out, tocopy); |
| 171 | output += tocopy; |
| 172 | free(buf[j].out, M_CRYPTO_DATA); |
| 173 | count -= tocopy; |
| 174 | } |
| 175 | KASSERT(count == 0); |
| 176 | } else { |
| 177 | *out = buf[0].out; |
| 178 | } |
| 179 | if (decomp) |
| 180 | inflateEnd(&zbuf); |
| 181 | else |
| 182 | deflateEnd(&zbuf); |
| 183 | return result; |
| 184 | |
| 185 | bad: |
| 186 | if (decomp) |
| 187 | inflateEnd(&zbuf); |
| 188 | else |
| 189 | deflateEnd(&zbuf); |
| 190 | bad2: |
| 191 | for (j = 0; j < i; j++) |
| 192 | free(buf[j].out, M_CRYPTO_DATA); |
| 193 | return 0; |
| 194 | } |
| 195 | |
| 196 | /* |
| 197 | * Initial version will perform a single gzip encapsulation, |
| 198 | * filling in the header, |
| 199 | * and appending the crc and uncompressed length. |
| 200 | * |
| 201 | * Later version will support multiple buffers with |
| 202 | * a flag indication final buffer. The crc is maintained |
| 203 | * over all buffers and appended to the output along with |
| 204 | * the uncompressed length after the final data buffer |
| 205 | * has been compressed and output. |
| 206 | * |
| 207 | * Ditto for uncompress - CRC is extracted from the final packed |
| 208 | * and compared against CRC of uncompressed data. |
| 209 | * |
| 210 | */ |
| 211 | |
| 212 | /* constant header for the gzip */ |
| 213 | static const char [10] = { |
| 214 | 0x1f, 0x8b, /* ID1 ID2 */ |
| 215 | Z_DEFLATED, /* CM */ |
| 216 | 0, /* FLG */ |
| 217 | 0, 0, 0, 0, /* MTIME */ |
| 218 | 0, /* XFL */ |
| 219 | 0x03 /* OS (Unix) */ |
| 220 | }; |
| 221 | |
| 222 | /* Followed by compressed payload */ |
| 223 | /* Followed by uint32_t CRC32 and uint32_t ISIZE */ |
| 224 | #define GZIP_TAIL_SIZE 8 |
| 225 | |
| 226 | u_int32_t |
| 227 | gzip_global(u_int8_t *data, u_int32_t size, |
| 228 | int decomp, u_int8_t **out, int size_hint) |
| 229 | { |
| 230 | /* decomp indicates whether we compress (0) or decompress (1) */ |
| 231 | z_stream zbuf; |
| 232 | u_int8_t *output; |
| 233 | u_int32_t count, result; |
| 234 | int error, i, j; |
| 235 | struct deflate_buf buf[ZBUF]; |
| 236 | u_int32_t crc; |
| 237 | u_int32_t isize = 0, icrc = 0; |
| 238 | |
| 239 | DPRINTF(("gzip_global: decomp %d, size %u\n" , decomp, size)); |
| 240 | |
| 241 | memset(&zbuf, 0, sizeof(z_stream)); |
| 242 | zbuf.zalloc = ocf_zalloc; |
| 243 | zbuf.zfree = ocf_zfree; |
| 244 | zbuf.opaque = Z_NULL; |
| 245 | |
| 246 | if (!decomp) { |
| 247 | /* compress */ |
| 248 | DPRINTF(("gzip_global: compress malloc %u + %zu + %u = %zu\n" , |
| 249 | size, sizeof(gzip_header), GZIP_TAIL_SIZE, |
| 250 | size + sizeof(gzip_header) + GZIP_TAIL_SIZE)); |
| 251 | |
| 252 | buf[0].size = size; |
| 253 | crc = crc32(0, data, size); |
| 254 | DPRINTF(("gzip_compress: size %u, crc 0x%x\n" , size, crc)); |
| 255 | zbuf.avail_in = size; /* Total length of data to be processed */ |
| 256 | zbuf.next_in = data; /* data that is going to be processed */ |
| 257 | } else { |
| 258 | /* decompress */ |
| 259 | /* check the gzip header */ |
| 260 | if (size <= sizeof(gzip_header) + GZIP_TAIL_SIZE) { |
| 261 | /* Not enough data for the header & tail */ |
| 262 | DPRINTF(("gzip_global: not enough data (%u)\n" , |
| 263 | size)); |
| 264 | return 0; |
| 265 | } |
| 266 | |
| 267 | /* XXX this is pretty basic, |
| 268 | * needs to be expanded to ignore MTIME, OS, |
| 269 | * but still ensure flags are 0. |
| 270 | * Q. Do we need to support the flags and |
| 271 | * optional header fields? Likely. |
| 272 | * XXX add flag and field support too. |
| 273 | */ |
| 274 | if (memcmp(data, gzip_header, sizeof(gzip_header)) != 0) { |
| 275 | DPRINTF(("gzip_global: unsupported gzip header (%02x%02x)\n" , |
| 276 | data[0], data[1])); |
| 277 | return 0; |
| 278 | } else { |
| 279 | DPRINTF(("gzip_global.%d: gzip header ok\n" ,__LINE__)); |
| 280 | } |
| 281 | |
| 282 | memcpy(&isize, &data[size-sizeof(uint32_t)], sizeof(uint32_t)); |
| 283 | LE32TOH(isize); |
| 284 | memcpy(&icrc, &data[size-2*sizeof(uint32_t)], sizeof(uint32_t)); |
| 285 | LE32TOH(icrc); |
| 286 | |
| 287 | DPRINTF(("gzip_global: isize = %u (%02x %02x %02x %02x)\n" , |
| 288 | isize, |
| 289 | data[size-4], |
| 290 | data[size-3], |
| 291 | data[size-2], |
| 292 | data[size-1])); |
| 293 | |
| 294 | buf[0].size = isize; |
| 295 | crc = crc32(0, NULL, 0); /* get initial crc value */ |
| 296 | |
| 297 | /* skip over the gzip header */ |
| 298 | zbuf.next_in = data + sizeof(gzip_header); |
| 299 | |
| 300 | /* actual payload size stripped of gzip header and tail */ |
| 301 | zbuf.avail_in = size - sizeof(gzip_header) - GZIP_TAIL_SIZE; |
| 302 | } |
| 303 | |
| 304 | buf[0].out = malloc(buf[0].size, M_CRYPTO_DATA, M_NOWAIT); |
| 305 | if (buf[0].out == NULL) |
| 306 | return 0; |
| 307 | zbuf.next_out = buf[0].out; |
| 308 | zbuf.avail_out = buf[0].size; |
| 309 | DPRINTF(("zbuf avail_in %u, avail_out %u\n" , |
| 310 | zbuf.avail_in, zbuf.avail_out)); |
| 311 | i = 1; |
| 312 | |
| 313 | error = decomp ? inflateInit2(&zbuf, window_inflate) : |
| 314 | deflateInit2(&zbuf, Z_DEFAULT_COMPRESSION, Z_METHOD, |
| 315 | window_deflate, Z_MEMLEVEL, Z_DEFAULT_STRATEGY); |
| 316 | |
| 317 | if (error != Z_OK) { |
| 318 | printf("deflateInit2() failed\n" ); |
| 319 | goto bad2; |
| 320 | } |
| 321 | for (;;) { |
| 322 | DPRINTF(("pre: %s in:%u out:%u\n" , decomp ? "deflate()" : "inflate()" , |
| 323 | zbuf.avail_in, zbuf.avail_out)); |
| 324 | error = decomp ? inflate(&zbuf, Z_SYNC_FLUSH) : |
| 325 | deflate(&zbuf, Z_FINISH); |
| 326 | DPRINTF(("post: %s in:%u out:%u\n" , decomp ? "deflate()" : "inflate()" , |
| 327 | zbuf.avail_in, zbuf.avail_out)); |
| 328 | if (error == Z_STREAM_END) /* success */ |
| 329 | break; |
| 330 | /* |
| 331 | * XXX compensate for a zlib problem: |
| 332 | * -sys/net/zlib.c has a bug which makes that Z_BUF_ERROR is |
| 333 | * set after successful decompression under rare conditions. |
| 334 | */ |
| 335 | else if (decomp && error == Z_BUF_ERROR |
| 336 | && zbuf.avail_in == 0 && zbuf.avail_out != 0) |
| 337 | break; |
| 338 | else if (error != Z_OK) |
| 339 | goto bad; |
| 340 | else if (zbuf.avail_out == 0) { |
| 341 | /* we need more output space, allocate size */ |
| 342 | int nextsize = buf[i-1].size * 2; |
| 343 | if (i == ZBUF || nextsize > 1000000) |
| 344 | goto bad; |
| 345 | buf[i].out = malloc(nextsize, M_CRYPTO_DATA, M_NOWAIT); |
| 346 | if (buf[i].out == NULL) |
| 347 | goto bad; |
| 348 | zbuf.next_out = buf[i].out; |
| 349 | zbuf.avail_out = buf[i].size = nextsize; |
| 350 | i++; |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | if (decomp) { |
| 355 | count = result = zbuf.total_out; |
| 356 | } else { |
| 357 | /* need room for header, CRC, and ISIZE */ |
| 358 | result = zbuf.total_out + sizeof(gzip_header) + GZIP_TAIL_SIZE; |
| 359 | count = zbuf.total_out; |
| 360 | } |
| 361 | |
| 362 | DPRINTF(("gzip_global: in %u -> out %u\n" , size, result)); |
| 363 | |
| 364 | *out = malloc(result, M_CRYPTO_DATA, M_NOWAIT); |
| 365 | if (*out == NULL) |
| 366 | goto bad; |
| 367 | output = *out; |
| 368 | if (decomp) |
| 369 | inflateEnd(&zbuf); |
| 370 | else { |
| 371 | deflateEnd(&zbuf); |
| 372 | |
| 373 | /* fill in gzip header */ |
| 374 | memcpy(output, gzip_header, sizeof(gzip_header)); |
| 375 | output += sizeof(gzip_header); |
| 376 | } |
| 377 | for (j = 0; j < i; j++) { |
| 378 | if (decomp) { |
| 379 | /* update crc for decompressed data */ |
| 380 | crc = crc32(crc, buf[j].out, MIN(count, buf[j].size)); |
| 381 | } |
| 382 | if (count > buf[j].size) { |
| 383 | memcpy(output, buf[j].out, buf[j].size); |
| 384 | output += buf[j].size; |
| 385 | free(buf[j].out, M_CRYPTO_DATA); |
| 386 | count -= buf[j].size; |
| 387 | } else { |
| 388 | /* it should be the last buffer */ |
| 389 | memcpy(output, buf[j].out, count); |
| 390 | output += count; |
| 391 | free(buf[j].out, M_CRYPTO_DATA); |
| 392 | count = 0; |
| 393 | } |
| 394 | } |
| 395 | |
| 396 | if (!decomp) { |
| 397 | /* fill in CRC and ISIZE */ |
| 398 | HTOLE32(crc); |
| 399 | memcpy(output, &crc, sizeof(uint32_t)); |
| 400 | HTOLE32(size); |
| 401 | memcpy(output + sizeof(uint32_t), &size, sizeof(uint32_t)); |
| 402 | |
| 403 | DPRINTF(("gzip_global: size = 0x%x (%02x %02x %02x %02x)\n" , |
| 404 | size, |
| 405 | output[7], |
| 406 | output[3], |
| 407 | output[5], |
| 408 | output[4])); |
| 409 | } else { |
| 410 | if (crc != icrc || result != isize) { |
| 411 | DPRINTF(("gzip_global: crc/size mismatch\n" )); |
| 412 | free(*out, M_CRYPTO_DATA); |
| 413 | *out = NULL; |
| 414 | return 0; |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | return result; |
| 419 | |
| 420 | bad: |
| 421 | if (decomp) |
| 422 | inflateEnd(&zbuf); |
| 423 | else |
| 424 | deflateEnd(&zbuf); |
| 425 | bad2: |
| 426 | *out = NULL; |
| 427 | for (j = 0; j < i; j++) |
| 428 | free(buf[j].out, M_CRYPTO_DATA); |
| 429 | return 0; |
| 430 | } |
| 431 | |