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