deflate.c revision 31921
1/*- 2 * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $Id$ 27 */ 28 29#include <sys/param.h> 30#include <netinet/in.h> 31 32#include <stdio.h> 33#include <stdlib.h> 34#include <zlib.h> 35 36#include "command.h" 37#include "mbuf.h" 38#include "log.h" 39#include "defs.h" 40#include "loadalias.h" 41#include "vars.h" 42#include "hdlc.h" 43#include "lcp.h" 44#include "ccp.h" 45#include "lcpproto.h" 46#include "timer.h" 47#include "fsm.h" 48#include "deflate.h" 49 50/* Our state */ 51struct deflate_state { 52 u_short seqno; 53 z_stream cx; 54}; 55 56static int iWindowSize = 15; 57static int oWindowSize = 15; 58static struct deflate_state InputState, OutputState; 59static char garbage[10]; 60static u_char EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff }; 61 62#define DEFLATE_CHUNK_LEN 1024 /* Allocate mbufs this size */ 63 64static void 65DeflateResetOutput(void) 66{ 67 OutputState.seqno = 0; 68 deflateReset(&OutputState.cx); 69 LogPrintf(LogCCP, "Deflate: Output channel reset\n"); 70} 71 72static int 73DeflateOutput(int pri, u_short proto, struct mbuf *mp) 74{ 75 u_char *wp, *rp; 76 int olen, ilen, len, res, flush; 77 struct mbuf *mo_head, *mo, *mi_head, *mi; 78 79 ilen = plength(mp); 80 LogPrintf(LogDEBUG, "DeflateOutput: Proto %02x (%d bytes)\n", proto, ilen); 81 LogDumpBp(LogDEBUG, "DeflateOutput: Compress packet:", mp); 82 83 /* Stuff the protocol in front of the input */ 84 mi_head = mi = mballoc(2, MB_HDLCOUT); 85 mi->next = mp; 86 rp = MBUF_CTOP(mi); 87 if (proto < 0x100) { /* Compress the protocol */ 88 rp[0] = proto & 0377; 89 mi->cnt = 1; 90 } else { /* Don't compress the protocol */ 91 rp[0] = proto >> 8; 92 rp[1] = proto & 0377; 93 mi->cnt = 2; 94 } 95 96 /* Allocate the initial output mbuf */ 97 mo_head = mo = mballoc(DEFLATE_CHUNK_LEN, MB_HDLCOUT); 98 mo->cnt = 2; 99 wp = MBUF_CTOP(mo); 100 *wp++ = OutputState.seqno >> 8; 101 *wp++ = OutputState.seqno & 0377; 102 LogPrintf(LogDEBUG, "DeflateOutput: Seq %d\n", OutputState.seqno); 103 OutputState.seqno++; 104 105 /* Set up the deflation context */ 106 OutputState.cx.next_out = wp; 107 OutputState.cx.avail_out = DEFLATE_CHUNK_LEN - 2; 108 OutputState.cx.next_in = MBUF_CTOP(mi); 109 OutputState.cx.avail_in = mi->cnt; 110 flush = Z_NO_FLUSH; 111 112 olen = 0; 113 while (1) { 114 if ((res = deflate(&OutputState.cx, flush)) != Z_OK) { 115 if (res == Z_STREAM_END) 116 break; /* Done */ 117 LogPrintf(LogERROR, "DeflateOutput: deflate returned %d (%s)\n", 118 res, OutputState.cx.msg ? OutputState.cx.msg : ""); 119 pfree(mo_head); 120 mbfree(mi_head); 121 OutputState.seqno--; 122 return 1; /* packet dropped */ 123 } 124 125 if (flush == Z_SYNC_FLUSH && OutputState.cx.avail_out != 0) 126 break; 127 128 if (OutputState.cx.avail_in == 0 && mi->next != NULL) { 129 mi = mi->next; 130 OutputState.cx.next_in = MBUF_CTOP(mi); 131 OutputState.cx.avail_in = mi->cnt; 132 if (mi->next == NULL) 133 flush = Z_SYNC_FLUSH; 134 } 135 136 if (OutputState.cx.avail_out == 0) { 137 mo->next = mballoc(DEFLATE_CHUNK_LEN, MB_HDLCOUT); 138 olen += (mo->cnt = DEFLATE_CHUNK_LEN); 139 mo = mo->next; 140 mo->cnt = 0; 141 OutputState.cx.next_out = MBUF_CTOP(mo); 142 OutputState.cx.avail_out = DEFLATE_CHUNK_LEN; 143 } 144 } 145 146 olen += (mo->cnt = DEFLATE_CHUNK_LEN - OutputState.cx.avail_out); 147 olen -= 4; /* exclude the trailing EMPTY_BLOCK */ 148 149 /* 150 * If the output packet (including seqno and excluding the EMPTY_BLOCK) 151 * got bigger, send the original - returning 0 to HdlcOutput() will 152 * continue to send ``mp''. 153 */ 154 if (olen >= ilen) { 155 pfree(mo_head); 156 mbfree(mi_head); 157 LogPrintf(LogDEBUG, "DeflateOutput: %d => %d: Uncompressible (0x%04x)\n", 158 ilen, olen, proto); 159 CcpInfo.uncompout += ilen; 160 CcpInfo.compout += ilen; /* We measure this stuff too */ 161 return 0; 162 } 163 164 pfree(mi_head); 165 166 /* 167 * Lose the last four bytes of our output. 168 * XXX: We should probably assert that these are the same as the 169 * contents of EMPTY_BLOCK. 170 */ 171 for (mo = mo_head, len = mo->cnt; len < olen; mo = mo->next, len += mo->cnt) 172 ; 173 mo->cnt -= len - olen; 174 if (mo->next != NULL) { 175 pfree(mo->next); 176 mo->next = NULL; 177 } 178 179 CcpInfo.uncompout += ilen; 180 CcpInfo.compout += olen; 181 182 LogPrintf(LogDEBUG, "DeflateOutput: %d => %d bytes, proto 0x%04x\n", 183 ilen, olen, proto); 184 185 HdlcOutput(PRI_NORMAL, PROTO_COMPD, mo_head); 186 return 1; 187} 188 189static void 190DeflateResetInput(void) 191{ 192 InputState.seqno = 0; 193 inflateReset(&InputState.cx); 194 LogPrintf(LogCCP, "Deflate: Input channel reset\n"); 195} 196 197static struct mbuf * 198DeflateInput(u_short *proto, struct mbuf *mi) 199{ 200 struct mbuf *mo, *mo_head, *mi_head; 201 u_char *wp; 202 int ilen, olen; 203 int seq, flush, res, first; 204 u_char hdr[2]; 205 206 LogDumpBp(LogDEBUG, "DeflateInput: Decompress packet:", mi); 207 mi_head = mi = mbread(mi, hdr, 2); 208 ilen = 2; 209 210 /* Check the sequence number. */ 211 seq = (hdr[0] << 8) + hdr[1]; 212 LogPrintf(LogDEBUG, "DeflateInput: Seq %d\n", seq); 213 if (seq != InputState.seqno) { 214 LogPrintf(LogERROR, "DeflateInput: Seq error: Got %d, expected %d\n", 215 seq, InputState.seqno); 216 pfree(mi_head); 217 CcpSendResetReq(&CcpFsm); 218 return NULL; 219 } 220 InputState.seqno++; 221 222 /* Allocate an output mbuf */ 223 mo_head = mo = mballoc(DEFLATE_CHUNK_LEN, MB_IPIN); 224 225 /* Our proto starts with 0 if it's compressed */ 226 wp = MBUF_CTOP(mo); 227 wp[0] = '\0'; 228 229 /* 230 * We set avail_out to 1 initially so we can look at the first 231 * byte of the output and decide whether we have a compressed 232 * proto field. 233 */ 234 InputState.cx.next_in = MBUF_CTOP(mi); 235 InputState.cx.avail_in = mi->cnt; 236 InputState.cx.next_out = wp + 1; 237 InputState.cx.avail_out = 1; 238 ilen += mi->cnt; 239 240 flush = mi->next ? Z_NO_FLUSH : Z_SYNC_FLUSH; 241 first = 1; 242 olen = 0; 243 244 while (1) { 245 if ((res = inflate(&InputState.cx, flush)) != Z_OK) { 246 if (res == Z_STREAM_END) 247 break; /* Done */ 248 LogPrintf(LogERROR, "DeflateInput: inflate returned %d (%s)\n", 249 res, InputState.cx.msg ? InputState.cx.msg : ""); 250 pfree(mo_head); 251 pfree(mi); 252 CcpSendResetReq(&CcpFsm); 253 return NULL; 254 } 255 256 if (flush == Z_SYNC_FLUSH && InputState.cx.avail_out != 0) 257 break; 258 259 if (InputState.cx.avail_in == 0 && mi && (mi = mbfree(mi)) != NULL) { 260 /* underflow */ 261 InputState.cx.next_in = MBUF_CTOP(mi); 262 ilen += (InputState.cx.avail_in = mi->cnt); 263 if (mi->next == NULL) 264 flush = Z_SYNC_FLUSH; 265 } 266 267 if (InputState.cx.avail_out == 0) 268 /* overflow */ 269 if (first) { 270 if (!(wp[1] & 1)) { 271 /* 2 byte proto, shuffle it back in output */ 272 wp[0] = wp[1]; 273 InputState.cx.next_out--; 274 InputState.cx.avail_out = DEFLATE_CHUNK_LEN-1; 275 } else 276 InputState.cx.avail_out = DEFLATE_CHUNK_LEN-2; 277 first = 0; 278 } else { 279 olen += (mo->cnt = DEFLATE_CHUNK_LEN); 280 mo->next = mballoc(DEFLATE_CHUNK_LEN, MB_IPIN); 281 mo = mo->next; 282 InputState.cx.next_out = MBUF_CTOP(mo); 283 InputState.cx.avail_out = DEFLATE_CHUNK_LEN; 284 } 285 } 286 287 if (mi != NULL) 288 pfree(mi); 289 290 if (first) { 291 LogPrintf(LogERROR, "DeflateInput: Length error\n"); 292 pfree(mo_head); 293 CcpSendResetReq(&CcpFsm); 294 return NULL; 295 } 296 297 olen += (mo->cnt = DEFLATE_CHUNK_LEN - InputState.cx.avail_out); 298 299 *proto = ((u_short)wp[0] << 8) | wp[1]; 300 mo_head->offset += 2; 301 mo_head->cnt -= 2; 302 olen -= 2; 303 304 CcpInfo.compin += ilen; 305 CcpInfo.uncompin += olen; 306 307 LogPrintf(LogDEBUG, "DeflateInput: %d => %d bytes, proto 0x%04x\n", 308 ilen, olen, *proto); 309 310 /* 311 * Simulate an EMPTY_BLOCK so that our dictionary stays in sync. 312 * The peer will have silently removed this! 313 */ 314 InputState.cx.next_out = garbage; 315 InputState.cx.avail_out = sizeof garbage; 316 InputState.cx.next_in = EMPTY_BLOCK; 317 InputState.cx.avail_in = sizeof EMPTY_BLOCK; 318 inflate(&InputState.cx, Z_SYNC_FLUSH); 319 320 return mo_head; 321} 322 323static void 324DeflateDictSetup(u_short proto, struct mbuf *mi) 325{ 326 int res, flush, expect_error; 327 u_char *rp; 328 struct mbuf *mi_head; 329 short len; 330 331 LogPrintf(LogDEBUG, "DeflateDictSetup: Got seq %d\n", InputState.seqno); 332 333 /* 334 * Stuff an ``uncompressed data'' block header followed by the 335 * protocol in front of the input 336 */ 337 mi_head = mballoc(7, MB_HDLCOUT); 338 mi_head->next = mi; 339 len = plength(mi); 340 mi = mi_head; 341 rp = MBUF_CTOP(mi); 342 if (proto < 0x100) { /* Compress the protocol */ 343 rp[5] = proto & 0377; 344 mi->cnt = 6; 345 len++; 346 } else { /* Don't compress the protocol */ 347 rp[5] = proto >> 8; 348 rp[6] = proto & 0377; 349 mi->cnt = 7; 350 len += 2; 351 } 352 rp[0] = 0x80; /* BITS: 100xxxxx */ 353 rp[1] = len & 0377; /* The length */ 354 rp[2] = len >> 8; 355 rp[3] = (~len) & 0377; /* One's compliment of the length */ 356 rp[4] = (~len) >> 8; 357 358 InputState.cx.next_in = rp; 359 InputState.cx.avail_in = mi->cnt; 360 InputState.cx.next_out = garbage; 361 InputState.cx.avail_out = sizeof garbage; 362 flush = Z_NO_FLUSH; 363 expect_error = 0; 364 365 while (1) { 366 if ((res = inflate(&InputState.cx, flush)) != Z_OK) { 367 if (res == Z_STREAM_END) 368 break; /* Done */ 369 if (expect_error && res == Z_BUF_ERROR) 370 break; 371 LogPrintf(LogERROR, "DeflateDictSetup: inflate returned %d (%s)\n", 372 res, InputState.cx.msg ? InputState.cx.msg : ""); 373 LogPrintf(LogERROR, "DeflateDictSetup: avail_in %d, avail_out %d\n", 374 InputState.cx.avail_in, InputState.cx.avail_out); 375 CcpSendResetReq(&CcpFsm); 376 mbfree(mi_head); /* lose our allocated ``head'' buf */ 377 return; 378 } 379 380 if (flush == Z_SYNC_FLUSH && InputState.cx.avail_out != 0) 381 break; 382 383 if (InputState.cx.avail_in == 0 && mi && (mi = mi->next) != NULL) { 384 /* underflow */ 385 InputState.cx.next_in = MBUF_CTOP(mi); 386 InputState.cx.avail_in = mi->cnt; 387 if (mi->next == NULL) 388 flush = Z_SYNC_FLUSH; 389 } 390 391 if (InputState.cx.avail_out == 0) { 392 if (InputState.cx.avail_in == 0) 393 /* 394 * This seems to be a bug in libz ! If inflate() finished 395 * with 0 avail_in and 0 avail_out *and* this is the end of 396 * our input *and* inflate() *has* actually written all the 397 * output it's going to, it *doesn't* return Z_STREAM_END ! 398 * When we subsequently call it with no more input, it gives 399 * us Z_BUF_ERROR :-( It seems pretty safe to ignore this 400 * error (the dictionary seems to stay in sync). In the worst 401 * case, we'll drop the next compressed packet and do a 402 * CcpReset() then. 403 */ 404 expect_error = 1; 405 /* overflow */ 406 InputState.cx.next_out = garbage; 407 InputState.cx.avail_out = sizeof garbage; 408 } 409 } 410 411 CcpInfo.compin += len; 412 CcpInfo.uncompin += len; 413 414 InputState.seqno++; 415 mbfree(mi_head); /* lose our allocated ``head'' buf */ 416} 417 418static const char * 419DeflateDispOpts(struct lcp_opt *o) 420{ 421 static char disp[7]; 422 423 sprintf(disp, "win %d", (o->data[0]>>4) + 8); 424 return disp; 425} 426 427static void 428DeflateGetInputOpts(struct lcp_opt *o) 429{ 430 o->id = TY_DEFLATE; 431 o->len = 4; 432 o->data[0] = ((iWindowSize-8)<<4)+8; 433 o->data[1] = '\0'; 434} 435 436static void 437DeflateGetOutputOpts(struct lcp_opt *o) 438{ 439 o->id = TY_DEFLATE; 440 o->len = 4; 441 o->data[0] = ((oWindowSize-8)<<4)+8; 442 o->data[1] = '\0'; 443} 444 445static void 446PppdDeflateGetInputOpts(struct lcp_opt *o) 447{ 448 o->id = TY_PPPD_DEFLATE; 449 o->len = 4; 450 o->data[0] = ((iWindowSize-8)<<4)+8; 451 o->data[1] = '\0'; 452} 453 454static void 455PppdDeflateGetOutputOpts(struct lcp_opt *o) 456{ 457 o->id = TY_PPPD_DEFLATE; 458 o->len = 4; 459 o->data[0] = ((oWindowSize-8)<<4)+8; 460 o->data[1] = '\0'; 461} 462 463static int 464DeflateSetOpts(struct lcp_opt *o, int *sz) 465{ 466 if (o->len != 4 || (o->data[0]&15) != 8 || o->data[1] != '\0') { 467 return MODE_REJ; 468 } 469 *sz = (o->data[0] >> 4) + 8; 470 if (*sz > 15) { 471 *sz = 15; 472 return MODE_NAK; 473 } 474 475 return MODE_ACK; 476} 477 478static int 479DeflateSetInputOpts(struct lcp_opt *o) 480{ 481 int res; 482 res = DeflateSetOpts(o, &iWindowSize); 483 if (res != MODE_ACK) 484 DeflateGetInputOpts(o); 485 return res; 486} 487 488static int 489DeflateSetOutputOpts(struct lcp_opt *o) 490{ 491 int res; 492 res = DeflateSetOpts(o, &oWindowSize); 493 if (res != MODE_ACK) 494 DeflateGetOutputOpts(o); 495 return res; 496} 497 498static int 499PppdDeflateSetInputOpts(struct lcp_opt *o) 500{ 501 int res; 502 res = DeflateSetOpts(o, &iWindowSize); 503 if (res != MODE_ACK) 504 PppdDeflateGetInputOpts(o); 505 return res; 506} 507 508static int 509PppdDeflateSetOutputOpts(struct lcp_opt *o) 510{ 511 int res; 512 res = DeflateSetOpts(o, &oWindowSize); 513 if (res != MODE_ACK) 514 PppdDeflateGetOutputOpts(o); 515 return res; 516} 517 518static int 519DeflateInitInput(void) 520{ 521 InputState.cx.zalloc = NULL; 522 InputState.cx.opaque = NULL; 523 InputState.cx.zfree = NULL; 524 InputState.cx.next_out = NULL; 525 if (inflateInit2(&InputState.cx, -iWindowSize) != Z_OK) 526 return 0; 527 DeflateResetInput(); 528 return 1; 529} 530 531static int 532DeflateInitOutput(void) 533{ 534 OutputState.cx.zalloc = NULL; 535 OutputState.cx.opaque = NULL; 536 OutputState.cx.zfree = NULL; 537 OutputState.cx.next_in = NULL; 538 if (deflateInit2(&OutputState.cx, Z_DEFAULT_COMPRESSION, 8, 539 -oWindowSize, 8, Z_DEFAULT_STRATEGY) != Z_OK) 540 return 0; 541 DeflateResetOutput(); 542 return 1; 543} 544 545static void 546DeflateTermInput(void) 547{ 548 iWindowSize = 15; 549 inflateEnd(&InputState.cx); 550} 551 552static void 553DeflateTermOutput(void) 554{ 555 oWindowSize = 15; 556 deflateEnd(&OutputState.cx); 557} 558 559const struct ccp_algorithm PppdDeflateAlgorithm = { 560 TY_PPPD_DEFLATE, /* pppd (wrongly) expects this ``type'' field */ 561 ConfPppdDeflate, 562 DeflateDispOpts, 563 { 564 PppdDeflateGetInputOpts, 565 PppdDeflateSetInputOpts, 566 DeflateInitInput, 567 DeflateTermInput, 568 DeflateResetInput, 569 DeflateInput, 570 DeflateDictSetup 571 }, 572 { 573 PppdDeflateGetOutputOpts, 574 PppdDeflateSetOutputOpts, 575 DeflateInitOutput, 576 DeflateTermOutput, 577 DeflateResetOutput, 578 DeflateOutput 579 }, 580}; 581 582const struct ccp_algorithm DeflateAlgorithm = { 583 TY_DEFLATE, /* rfc 1979 */ 584 ConfDeflate, 585 DeflateDispOpts, 586 { 587 DeflateGetInputOpts, 588 DeflateSetInputOpts, 589 DeflateInitInput, 590 DeflateTermInput, 591 DeflateResetInput, 592 DeflateInput, 593 DeflateDictSetup 594 }, 595 { 596 DeflateGetOutputOpts, 597 DeflateSetOutputOpts, 598 DeflateInitOutput, 599 DeflateTermOutput, 600 DeflateResetOutput, 601 DeflateOutput 602 }, 603}; 604