1 /* $OpenBSD: ppp-deflate.c,v 1.7 2002/09/13 00:12:07 deraadt Exp $ */ 2 /* $NetBSD: ppp-deflate.c,v 1.1 1996/03/15 02:28:09 paulus Exp $ */ 3 4 /* 5 * ppp_deflate.c - interface the zlib procedures for Deflate compression 6 * and decompression (as used by gzip) to the PPP code. 7 * This version is for use with mbufs on BSD-derived systems. 8 * 9 * Copyright (c) 1989-2002 Paul Mackerras. All rights reserved. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in 20 * the documentation and/or other materials provided with the 21 * distribution. 22 * 23 * 3. The name(s) of the authors of this software must not be used to 24 * endorse or promote products derived from this software without 25 * prior written permission. 26 * 27 * 4. Redistributions of any form whatsoever must retain the following 28 * acknowledgment: 29 * "This product includes software developed by Paul Mackerras 30 * <paulus@samba.org>". 31 * 32 * THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO 33 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 34 * AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY 35 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 36 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 37 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 38 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 39 */ 40 41 #include <sys/param.h> 42 #include <sys/types.h> 43 #include <sys/systm.h> 44 #include <sys/mbuf.h> 45 #include <net/ppp_defs.h> 46 #include <net/zlib.h> 47 48 #define PACKETPTR struct mbuf * 49 #include <net/ppp-comp.h> 50 51 #if DO_DEFLATE 52 53 /* 54 * State for a Deflate (de)compressor. 55 */ 56 struct deflate_state { 57 int seqno; 58 int w_size; 59 int unit; 60 int hdrlen; 61 int mru; 62 int debug; 63 z_stream strm; 64 struct compstat stats; 65 }; 66 67 #define DEFLATE_OVHD 2 /* Deflate overhead/packet */ 68 69 static void *zalloc(void *, u_int items, u_int size); 70 static void zfree(void *, void *ptr, u_int nb); 71 static void *z_comp_alloc(u_char *options, int opt_len); 72 static void *z_decomp_alloc(u_char *options, int opt_len); 73 static void z_comp_free(void *state); 74 static void z_decomp_free(void *state); 75 static int z_comp_init(void *state, u_char *options, int opt_len, 76 int unit, int hdrlen, int debug); 77 static int z_decomp_init(void *state, u_char *options, int opt_len, 78 int unit, int hdrlen, int mru, int debug); 79 static int z_compress(void *state, struct mbuf **mret, 80 struct mbuf *mp, int slen, int maxolen); 81 static void z_incomp(void *state, struct mbuf *dmsg); 82 static int z_decompress(void *state, struct mbuf *cmp, 83 struct mbuf **dmpp); 84 static void z_comp_reset(void *state); 85 static void z_decomp_reset(void *state); 86 static void z_comp_stats(void *state, struct compstat *stats); 87 88 /* 89 * Procedures exported to if_ppp.c. 90 */ 91 struct compressor ppp_deflate = { 92 CI_DEFLATE, /* compress_proto */ 93 z_comp_alloc, /* comp_alloc */ 94 z_comp_free, /* comp_free */ 95 z_comp_init, /* comp_init */ 96 z_comp_reset, /* comp_reset */ 97 z_compress, /* compress */ 98 z_comp_stats, /* comp_stat */ 99 z_decomp_alloc, /* decomp_alloc */ 100 z_decomp_free, /* decomp_free */ 101 z_decomp_init, /* decomp_init */ 102 z_decomp_reset, /* decomp_reset */ 103 z_decompress, /* decompress */ 104 z_incomp, /* incomp */ 105 z_comp_stats, /* decomp_stat */ 106 }; 107 108 struct compressor ppp_deflate_draft = { 109 CI_DEFLATE_DRAFT, /* compress_proto */ 110 z_comp_alloc, /* comp_alloc */ 111 z_comp_free, /* comp_free */ 112 z_comp_init, /* comp_init */ 113 z_comp_reset, /* comp_reset */ 114 z_compress, /* compress */ 115 z_comp_stats, /* comp_stat */ 116 z_decomp_alloc, /* decomp_alloc */ 117 z_decomp_free, /* decomp_free */ 118 z_decomp_init, /* decomp_init */ 119 z_decomp_reset, /* decomp_reset */ 120 z_decompress, /* decompress */ 121 z_incomp, /* incomp */ 122 z_comp_stats, /* decomp_stat */ 123 }; 124 /* 125 * Space allocation and freeing routines for use by zlib routines. 126 */ 127 void * 128 zalloc(notused, items, size) 129 void *notused; 130 u_int items, size; 131 { 132 void *ptr; 133 134 MALLOC(ptr, void *, items * size, M_DEVBUF, M_NOWAIT); 135 return ptr; 136 } 137 138 void 139 zfree(notused, ptr, nbytes) 140 void *notused; 141 void *ptr; 142 u_int nbytes; 143 { 144 FREE(ptr, M_DEVBUF); 145 } 146 147 /* 148 * Allocate space for a compressor. 149 */ 150 static void * 151 z_comp_alloc(options, opt_len) 152 u_char *options; 153 int opt_len; 154 { 155 struct deflate_state *state; 156 int w_size; 157 158 if (opt_len != CILEN_DEFLATE 159 || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) 160 || options[1] != CILEN_DEFLATE 161 || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL 162 || options[3] != DEFLATE_CHK_SEQUENCE) 163 return NULL; 164 w_size = DEFLATE_SIZE(options[2]); 165 if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) 166 return NULL; 167 168 MALLOC(state, struct deflate_state *, sizeof(struct deflate_state), 169 M_DEVBUF, M_NOWAIT); 170 if (state == NULL) 171 return NULL; 172 173 state->strm.next_in = NULL; 174 state->strm.zalloc = zalloc; 175 state->strm.zfree = zfree; 176 if (deflateInit2(&state->strm, Z_DEFAULT_COMPRESSION, DEFLATE_METHOD_VAL, 177 -w_size, 8, Z_DEFAULT_STRATEGY, DEFLATE_OVHD+2) != Z_OK) { 178 FREE(state, M_DEVBUF); 179 return NULL; 180 } 181 182 state->w_size = w_size; 183 bzero(&state->stats, sizeof(state->stats)); 184 return (void *) state; 185 } 186 187 static void 188 z_comp_free(arg) 189 void *arg; 190 { 191 struct deflate_state *state = (struct deflate_state *) arg; 192 193 deflateEnd(&state->strm); 194 FREE(state, M_DEVBUF); 195 } 196 197 static int 198 z_comp_init(arg, options, opt_len, unit, hdrlen, debug) 199 void *arg; 200 u_char *options; 201 int opt_len, unit, hdrlen, debug; 202 { 203 struct deflate_state *state = (struct deflate_state *) arg; 204 205 if (opt_len < CILEN_DEFLATE 206 || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) 207 || options[1] != CILEN_DEFLATE 208 || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL 209 || DEFLATE_SIZE(options[2]) != state->w_size 210 || options[3] != DEFLATE_CHK_SEQUENCE) 211 return 0; 212 213 state->seqno = 0; 214 state->unit = unit; 215 state->hdrlen = hdrlen; 216 state->debug = debug; 217 218 deflateReset(&state->strm); 219 220 return 1; 221 } 222 223 static void 224 z_comp_reset(arg) 225 void *arg; 226 { 227 struct deflate_state *state = (struct deflate_state *) arg; 228 229 state->seqno = 0; 230 deflateReset(&state->strm); 231 } 232 233 int 234 z_compress(arg, mret, mp, orig_len, maxolen) 235 void *arg; 236 struct mbuf **mret; /* compressed packet (out) */ 237 struct mbuf *mp; /* uncompressed packet (in) */ 238 int orig_len, maxolen; 239 { 240 struct deflate_state *state = (struct deflate_state *) arg; 241 u_char *rptr, *wptr; 242 int proto, olen, wspace, r, flush; 243 struct mbuf *m; 244 245 /* 246 * Check that the protocol is in the range we handle. 247 */ 248 rptr = mtod(mp, u_char *); 249 proto = PPP_PROTOCOL(rptr); 250 if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) { 251 *mret = NULL; 252 return orig_len; 253 } 254 255 /* Allocate one mbuf initially. */ 256 if (maxolen > orig_len) 257 maxolen = orig_len; 258 MGET(m, M_DONTWAIT, MT_DATA); 259 *mret = m; 260 if (m != NULL) { 261 m->m_len = 0; 262 if (maxolen + state->hdrlen > MLEN) 263 MCLGET(m, M_DONTWAIT); 264 wspace = M_TRAILINGSPACE(m); 265 if (state->hdrlen + PPP_HDRLEN + 2 < wspace) { 266 m->m_data += state->hdrlen; 267 wspace -= state->hdrlen; 268 } 269 wptr = mtod(m, u_char *); 270 271 /* 272 * Copy over the PPP header and store the 2-byte sequence number. 273 */ 274 wptr[0] = PPP_ADDRESS(rptr); 275 wptr[1] = PPP_CONTROL(rptr); 276 wptr[2] = PPP_COMP >> 8; 277 wptr[3] = PPP_COMP; 278 wptr += PPP_HDRLEN; 279 wptr[0] = state->seqno >> 8; 280 wptr[1] = state->seqno; 281 wptr += 2; 282 state->strm.next_out = wptr; 283 state->strm.avail_out = wspace - (PPP_HDRLEN + 2); 284 } else { 285 state->strm.next_out = NULL; 286 state->strm.avail_out = 1000000; 287 wptr = NULL; 288 wspace = 0; 289 } 290 ++state->seqno; 291 292 rptr += (proto > 0xff)? 2: 3; /* skip 1st proto byte if 0 */ 293 state->strm.next_in = rptr; 294 state->strm.avail_in = mtod(mp, u_char *) + mp->m_len - rptr; 295 mp = mp->m_next; 296 flush = (mp == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH; 297 olen = 0; 298 for (;;) { 299 r = deflate(&state->strm, flush); 300 if (r != Z_OK) { 301 printf("z_compress: deflate returned %d (%s)\n", 302 r, (state->strm.msg? state->strm.msg: "")); 303 break; 304 } 305 if (flush != Z_NO_FLUSH && state->strm.avail_out != 0) 306 break; /* all done */ 307 if (state->strm.avail_in == 0 && mp != NULL) { 308 state->strm.next_in = mtod(mp, u_char *); 309 state->strm.avail_in = mp->m_len; 310 mp = mp->m_next; 311 if (mp == NULL) 312 flush = Z_PACKET_FLUSH; 313 } 314 if (state->strm.avail_out == 0) { 315 if (m != NULL) { 316 m->m_len = wspace; 317 olen += wspace; 318 MGET(m->m_next, M_DONTWAIT, MT_DATA); 319 m = m->m_next; 320 if (m != NULL) { 321 m->m_len = 0; 322 if (maxolen - olen > MLEN) 323 MCLGET(m, M_DONTWAIT); 324 state->strm.next_out = mtod(m, u_char *); 325 state->strm.avail_out = wspace = M_TRAILINGSPACE(m); 326 } 327 } 328 if (m == NULL) { 329 state->strm.next_out = NULL; 330 state->strm.avail_out = 1000000; 331 } 332 } 333 } 334 if (m != NULL) 335 olen += (m->m_len = wspace - state->strm.avail_out); 336 337 /* 338 * See if we managed to reduce the size of the packet. 339 * If the compressor just gave us a single zero byte, it means 340 * the packet was incompressible. 341 */ 342 if (m != NULL && olen < orig_len 343 && !(olen == PPP_HDRLEN + 3 && *wptr == 0)) { 344 state->stats.comp_bytes += olen; 345 state->stats.comp_packets++; 346 } else { 347 if (*mret != NULL) { 348 m_freem(*mret); 349 *mret = NULL; 350 } 351 state->stats.inc_bytes += orig_len; 352 state->stats.inc_packets++; 353 olen = orig_len; 354 } 355 state->stats.unc_bytes += orig_len; 356 state->stats.unc_packets++; 357 358 return olen; 359 } 360 361 static void 362 z_comp_stats(arg, stats) 363 void *arg; 364 struct compstat *stats; 365 { 366 struct deflate_state *state = (struct deflate_state *) arg; 367 u_int out; 368 369 *stats = state->stats; 370 stats->ratio = stats->unc_bytes; 371 out = stats->comp_bytes + stats->inc_bytes; 372 if (stats->ratio <= 0x7ffffff) 373 stats->ratio <<= 8; 374 else 375 out >>= 8; 376 if (out != 0) 377 stats->ratio /= out; 378 } 379 380 /* 381 * Allocate space for a decompressor. 382 */ 383 static void * 384 z_decomp_alloc(options, opt_len) 385 u_char *options; 386 int opt_len; 387 { 388 struct deflate_state *state; 389 int w_size; 390 391 if (opt_len != CILEN_DEFLATE 392 || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) 393 || options[1] != CILEN_DEFLATE 394 || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL 395 || options[3] != DEFLATE_CHK_SEQUENCE) 396 return NULL; 397 w_size = DEFLATE_SIZE(options[2]); 398 if (w_size < DEFLATE_MIN_SIZE || w_size > DEFLATE_MAX_SIZE) 399 return NULL; 400 401 MALLOC(state, struct deflate_state *, sizeof(struct deflate_state), 402 M_DEVBUF, M_NOWAIT); 403 if (state == NULL) 404 return NULL; 405 406 state->strm.next_out = NULL; 407 state->strm.zalloc = zalloc; 408 state->strm.zfree = zfree; 409 if (inflateInit2(&state->strm, -w_size) != Z_OK) { 410 FREE(state, M_DEVBUF); 411 return NULL; 412 } 413 414 state->w_size = w_size; 415 bzero(&state->stats, sizeof(state->stats)); 416 return (void *) state; 417 } 418 419 static void 420 z_decomp_free(arg) 421 void *arg; 422 { 423 struct deflate_state *state = (struct deflate_state *) arg; 424 425 inflateEnd(&state->strm); 426 FREE(state, M_DEVBUF); 427 } 428 429 static int 430 z_decomp_init(arg, options, opt_len, unit, hdrlen, mru, debug) 431 void *arg; 432 u_char *options; 433 int opt_len, unit, hdrlen, mru, debug; 434 { 435 struct deflate_state *state = (struct deflate_state *) arg; 436 437 if (opt_len < CILEN_DEFLATE 438 || (options[0] != CI_DEFLATE && options[0] != CI_DEFLATE_DRAFT) 439 || options[1] != CILEN_DEFLATE 440 || DEFLATE_METHOD(options[2]) != DEFLATE_METHOD_VAL 441 || DEFLATE_SIZE(options[2]) != state->w_size 442 || options[3] != DEFLATE_CHK_SEQUENCE) 443 return 0; 444 445 state->seqno = 0; 446 state->unit = unit; 447 state->hdrlen = hdrlen; 448 state->debug = debug; 449 state->mru = mru; 450 451 inflateReset(&state->strm); 452 453 return 1; 454 } 455 456 static void 457 z_decomp_reset(arg) 458 void *arg; 459 { 460 struct deflate_state *state = (struct deflate_state *) arg; 461 462 state->seqno = 0; 463 inflateReset(&state->strm); 464 } 465 466 /* 467 * Decompress a Deflate-compressed packet. 468 * 469 * Because of patent problems, we return DECOMP_ERROR for errors 470 * found by inspecting the input data and for system problems, but 471 * DECOMP_FATALERROR for any errors which could possibly be said to 472 * be being detected "after" decompression. For DECOMP_ERROR, 473 * we can issue a CCP reset-request; for DECOMP_FATALERROR, we may be 474 * infringing a patent of Motorola's if we do, so we take CCP down 475 * instead. 476 * 477 * Given that the frame has the correct sequence number and a good FCS, 478 * errors such as invalid codes in the input most likely indicate a 479 * bug, so we return DECOMP_FATALERROR for them in order to turn off 480 * compression, even though they are detected by inspecting the input. 481 */ 482 int 483 z_decompress(arg, mi, mop) 484 void *arg; 485 struct mbuf *mi, **mop; 486 { 487 struct deflate_state *state = (struct deflate_state *) arg; 488 struct mbuf *mo, *mo_head; 489 u_char *rptr, *wptr; 490 int rlen, olen, ospace; 491 int seq, i, flush, r, decode_proto; 492 u_char hdr[PPP_HDRLEN + DEFLATE_OVHD]; 493 494 *mop = NULL; 495 rptr = mtod(mi, u_char *); 496 rlen = mi->m_len; 497 for (i = 0; i < PPP_HDRLEN + DEFLATE_OVHD; ++i) { 498 while (rlen <= 0) { 499 mi = mi->m_next; 500 if (mi == NULL) 501 return DECOMP_ERROR; 502 rptr = mtod(mi, u_char *); 503 rlen = mi->m_len; 504 } 505 hdr[i] = *rptr++; 506 --rlen; 507 } 508 509 /* Check the sequence number. */ 510 seq = (hdr[PPP_HDRLEN] << 8) + hdr[PPP_HDRLEN+1]; 511 if (seq != state->seqno) { 512 if (state->debug) 513 printf("z_decompress%d: bad seq # %d, expected %d\n", 514 state->unit, seq, state->seqno); 515 return DECOMP_ERROR; 516 } 517 ++state->seqno; 518 519 /* Allocate an output mbuf. */ 520 MGETHDR(mo, M_DONTWAIT, MT_DATA); 521 if (mo == NULL) 522 return DECOMP_ERROR; 523 mo_head = mo; 524 mo->m_len = 0; 525 mo->m_next = NULL; 526 MCLGET(mo, M_DONTWAIT); 527 ospace = M_TRAILINGSPACE(mo); 528 if (state->hdrlen + PPP_HDRLEN < ospace) { 529 mo->m_data += state->hdrlen; 530 ospace -= state->hdrlen; 531 } 532 533 /* 534 * Fill in the first part of the PPP header. The protocol field 535 * comes from the decompressed data. 536 */ 537 wptr = mtod(mo, u_char *); 538 wptr[0] = PPP_ADDRESS(hdr); 539 wptr[1] = PPP_CONTROL(hdr); 540 wptr[2] = 0; 541 542 /* 543 * Set up to call inflate. We set avail_out to 1 initially so we can 544 * look at the first byte of the output and decide whether we have 545 * a 1-byte or 2-byte protocol field. 546 */ 547 state->strm.next_in = rptr; 548 state->strm.avail_in = rlen; 549 mi = mi->m_next; 550 flush = (mi == NULL)? Z_PACKET_FLUSH: Z_NO_FLUSH; 551 rlen += PPP_HDRLEN + DEFLATE_OVHD; 552 state->strm.next_out = wptr + 3; 553 state->strm.avail_out = 1; 554 decode_proto = 1; 555 olen = PPP_HDRLEN; 556 557 /* 558 * Call inflate, supplying more input or output as needed. 559 */ 560 for (;;) { 561 r = inflate(&state->strm, flush); 562 if (r != Z_OK) { 563 #ifndef DEFLATE_DEBUG 564 if (state->debug) 565 #endif 566 printf("z_decompress%d: inflate returned %d (%s)\n", 567 state->unit, r, (state->strm.msg? state->strm.msg: "")); 568 m_freem(mo_head); 569 return DECOMP_FATALERROR; 570 } 571 if (flush != Z_NO_FLUSH && state->strm.avail_out != 0) 572 break; /* all done */ 573 if (state->strm.avail_in == 0 && mi != NULL) { 574 state->strm.next_in = mtod(mi, u_char *); 575 state->strm.avail_in = mi->m_len; 576 rlen += mi->m_len; 577 mi = mi->m_next; 578 if (mi == NULL) 579 flush = Z_PACKET_FLUSH; 580 } 581 if (state->strm.avail_out == 0) { 582 if (decode_proto) { 583 state->strm.avail_out = ospace - PPP_HDRLEN; 584 if ((wptr[3] & 1) == 0) { 585 /* 2-byte protocol field */ 586 wptr[2] = wptr[3]; 587 --state->strm.next_out; 588 ++state->strm.avail_out; 589 --olen; 590 } 591 decode_proto = 0; 592 } else { 593 mo->m_len = ospace; 594 olen += ospace; 595 MGET(mo->m_next, M_DONTWAIT, MT_DATA); 596 mo = mo->m_next; 597 if (mo == NULL) { 598 m_freem(mo_head); 599 return DECOMP_ERROR; 600 } 601 MCLGET(mo, M_DONTWAIT); 602 state->strm.next_out = mtod(mo, u_char *); 603 state->strm.avail_out = ospace = M_TRAILINGSPACE(mo); 604 } 605 } 606 } 607 if (decode_proto) { 608 m_freem(mo_head); 609 return DECOMP_ERROR; 610 } 611 olen += (mo->m_len = ospace - state->strm.avail_out); 612 #ifdef DEFLATE_DEBUG 613 if (olen > state->mru + PPP_HDRLEN) 614 printf("ppp_deflate%d: exceeded mru (%d > %d)\n", 615 state->unit, olen, state->mru + PPP_HDRLEN); 616 #endif 617 618 state->stats.unc_bytes += olen; 619 state->stats.unc_packets++; 620 state->stats.comp_bytes += rlen; 621 state->stats.comp_packets++; 622 623 *mop = mo_head; 624 return DECOMP_OK; 625 } 626 627 /* 628 * Incompressible data has arrived - add it to the history. 629 */ 630 static void 631 z_incomp(arg, mi) 632 void *arg; 633 struct mbuf *mi; 634 { 635 struct deflate_state *state = (struct deflate_state *) arg; 636 u_char *rptr; 637 int rlen, proto, r; 638 639 /* 640 * Check that the protocol is one we handle. 641 */ 642 rptr = mtod(mi, u_char *); 643 proto = PPP_PROTOCOL(rptr); 644 if (proto > 0x3fff || proto == 0xfd || proto == 0xfb) 645 return; 646 647 ++state->seqno; 648 649 /* 650 * Iterate through the mbufs, adding the characters in them 651 * to the decompressor's history. For the first mbuf, we start 652 * at the either the 1st or 2nd byte of the protocol field, 653 * depending on whether the protocol value is compressible. 654 */ 655 rlen = mi->m_len; 656 state->strm.next_in = rptr + 3; 657 state->strm.avail_in = rlen - 3; 658 if (proto > 0xff) { 659 --state->strm.next_in; 660 ++state->strm.avail_in; 661 } 662 for (;;) { 663 r = inflateIncomp(&state->strm); 664 if (r != Z_OK) { 665 /* gak! */ 666 #ifndef DEFLATE_DEBUG 667 if (state->debug) 668 #endif 669 printf("z_incomp%d: inflateIncomp returned %d (%s)\n", 670 state->unit, r, (state->strm.msg? state->strm.msg: "")); 671 return; 672 } 673 mi = mi->m_next; 674 if (mi == NULL) 675 break; 676 state->strm.next_in = mtod(mi, u_char *); 677 state->strm.avail_in = mi->m_len; 678 rlen += mi->m_len; 679 } 680 681 /* 682 * Update stats. 683 */ 684 state->stats.inc_bytes += rlen; 685 state->stats.inc_packets++; 686 state->stats.unc_bytes += rlen; 687 state->stats.unc_packets++; 688 } 689 690 #endif /* DO_DEFLATE */ 691