131921Sbrian/*-
231921Sbrian * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
331921Sbrian * All rights reserved.
431921Sbrian *
531921Sbrian * Redistribution and use in source and binary forms, with or without
631921Sbrian * modification, are permitted provided that the following conditions
731921Sbrian * are met:
831921Sbrian * 1. Redistributions of source code must retain the above copyright
931921Sbrian *    notice, this list of conditions and the following disclaimer.
1031921Sbrian * 2. Redistributions in binary form must reproduce the above copyright
1131921Sbrian *    notice, this list of conditions and the following disclaimer in the
1231921Sbrian *    documentation and/or other materials provided with the distribution.
1331921Sbrian *
1431921Sbrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1531921Sbrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1631921Sbrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1731921Sbrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1831921Sbrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1931921Sbrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2031921Sbrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2131921Sbrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2231921Sbrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2331921Sbrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2431921Sbrian * SUCH DAMAGE.
2531921Sbrian *
2650479Speter * $FreeBSD: releng/11.0/usr.sbin/ppp/deflate.c 134789 2004-09-05 01:46:52Z brian $
2731514Sbrian */
2831514Sbrian
2936285Sbrian#include <sys/types.h>
3031514Sbrian
3131514Sbrian#include <stdio.h>
3231514Sbrian#include <stdlib.h>
3331514Sbrian#include <zlib.h>
3431514Sbrian
3531514Sbrian#include "mbuf.h"
3631514Sbrian#include "log.h"
3736285Sbrian#include "timer.h"
3836285Sbrian#include "fsm.h"
3931514Sbrian#include "ccp.h"
4031514Sbrian#include "deflate.h"
4131514Sbrian
4231514Sbrian/* Our state */
4331514Sbrianstruct deflate_state {
4431514Sbrian    u_short seqno;
4532381Sbrian    int uncomp_rec;
4636285Sbrian    int winsize;
4731514Sbrian    z_stream cx;
4831514Sbrian};
4931514Sbrian
5031514Sbrianstatic char garbage[10];
5131514Sbrianstatic u_char EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff };
5231514Sbrian
5354913Sbrian#define DEFLATE_CHUNK_LEN (1536 - sizeof(struct mbuf))
5431514Sbrian
5578411Sbrianstatic int
5636285SbrianDeflateResetOutput(void *v)
5731514Sbrian{
5836285Sbrian  struct deflate_state *state = (struct deflate_state *)v;
5936285Sbrian
6036285Sbrian  state->seqno = 0;
6136285Sbrian  state->uncomp_rec = 0;
6236285Sbrian  deflateReset(&state->cx);
6336285Sbrian  log_Printf(LogCCP, "Deflate: Output channel reset\n");
6478411Sbrian
6578411Sbrian  return 1;		/* Ask FSM to ACK */
6631514Sbrian}
6731514Sbrian
6846686Sbrianstatic struct mbuf *
69134789SbrianDeflateOutput(void *v, struct ccp *ccp, struct link *l __unused,
70134789Sbrian	      int pri __unused, u_short *proto, struct mbuf *mp)
7131514Sbrian{
7236285Sbrian  struct deflate_state *state = (struct deflate_state *)v;
7331514Sbrian  u_char *wp, *rp;
7431514Sbrian  int olen, ilen, len, res, flush;
7531514Sbrian  struct mbuf *mo_head, *mo, *mi_head, *mi;
7631514Sbrian
7754912Sbrian  ilen = m_length(mp);
7846686Sbrian  log_Printf(LogDEBUG, "DeflateOutput: Proto %02x (%d bytes)\n", *proto, ilen);
7936285Sbrian  log_DumpBp(LogDEBUG, "DeflateOutput: Compress packet:", mp);
8031514Sbrian
8131514Sbrian  /* Stuff the protocol in front of the input */
8254912Sbrian  mi_head = mi = m_get(2, MB_CCPOUT);
8354912Sbrian  mi->m_next = mp;
8431514Sbrian  rp = MBUF_CTOP(mi);
8546686Sbrian  if (*proto < 0x100) {			/* Compress the protocol */
8646686Sbrian    rp[0] = *proto & 0377;
8754912Sbrian    mi->m_len = 1;
8831514Sbrian  } else {				/* Don't compress the protocol */
8946686Sbrian    rp[0] = *proto >> 8;
9046686Sbrian    rp[1] = *proto & 0377;
9154912Sbrian    mi->m_len = 2;
9231514Sbrian  }
9331514Sbrian
9431514Sbrian  /* Allocate the initial output mbuf */
9554912Sbrian  mo_head = mo = m_get(DEFLATE_CHUNK_LEN, MB_CCPOUT);
9654912Sbrian  mo->m_len = 2;
9731514Sbrian  wp = MBUF_CTOP(mo);
9836285Sbrian  *wp++ = state->seqno >> 8;
9936285Sbrian  *wp++ = state->seqno & 0377;
10036285Sbrian  log_Printf(LogDEBUG, "DeflateOutput: Seq %d\n", state->seqno);
10136285Sbrian  state->seqno++;
10231514Sbrian
10331514Sbrian  /* Set up the deflation context */
10436285Sbrian  state->cx.next_out = wp;
10536285Sbrian  state->cx.avail_out = DEFLATE_CHUNK_LEN - 2;
10636285Sbrian  state->cx.next_in = MBUF_CTOP(mi);
10754912Sbrian  state->cx.avail_in = mi->m_len;
10831514Sbrian  flush = Z_NO_FLUSH;
10931514Sbrian
11031514Sbrian  olen = 0;
11131514Sbrian  while (1) {
11236285Sbrian    if ((res = deflate(&state->cx, flush)) != Z_OK) {
11331514Sbrian      if (res == Z_STREAM_END)
11431514Sbrian        break;			/* Done */
11537019Sbrian      log_Printf(LogWARN, "DeflateOutput: deflate returned %d (%s)\n",
11636285Sbrian                res, state->cx.msg ? state->cx.msg : "");
11754912Sbrian      m_freem(mo_head);
11854912Sbrian      m_free(mi_head);
11936285Sbrian      state->seqno--;
12046686Sbrian      return mp;		/* Our dictionary's probably dead now :-( */
12131514Sbrian    }
12231514Sbrian
12336285Sbrian    if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
12431514Sbrian      break;
12531514Sbrian
12654912Sbrian    if (state->cx.avail_in == 0 && mi->m_next != NULL) {
12754912Sbrian      mi = mi->m_next;
12836285Sbrian      state->cx.next_in = MBUF_CTOP(mi);
12954912Sbrian      state->cx.avail_in = mi->m_len;
13054912Sbrian      if (mi->m_next == NULL)
13131514Sbrian        flush = Z_SYNC_FLUSH;
13231514Sbrian    }
13331514Sbrian
13436285Sbrian    if (state->cx.avail_out == 0) {
13554912Sbrian      mo->m_next = m_get(DEFLATE_CHUNK_LEN, MB_CCPOUT);
13654912Sbrian      olen += (mo->m_len = DEFLATE_CHUNK_LEN);
13754912Sbrian      mo = mo->m_next;
13854912Sbrian      mo->m_len = 0;
13936285Sbrian      state->cx.next_out = MBUF_CTOP(mo);
14036285Sbrian      state->cx.avail_out = DEFLATE_CHUNK_LEN;
14131514Sbrian    }
14231514Sbrian  }
14331514Sbrian
14454912Sbrian  olen += (mo->m_len = DEFLATE_CHUNK_LEN - state->cx.avail_out);
14531514Sbrian  olen -= 4;		/* exclude the trailing EMPTY_BLOCK */
14631514Sbrian
14731514Sbrian  /*
14831514Sbrian   * If the output packet (including seqno and excluding the EMPTY_BLOCK)
14936285Sbrian   * got bigger, send the original.
15031514Sbrian   */
15131514Sbrian  if (olen >= ilen) {
15254912Sbrian    m_freem(mo_head);
15354912Sbrian    m_free(mi_head);
15436285Sbrian    log_Printf(LogDEBUG, "DeflateOutput: %d => %d: Uncompressible (0x%04x)\n",
15546686Sbrian              ilen, olen, *proto);
15636285Sbrian    ccp->uncompout += ilen;
15736285Sbrian    ccp->compout += ilen;	/* We measure this stuff too */
15846686Sbrian    return mp;
15931514Sbrian  }
16031514Sbrian
16154912Sbrian  m_freem(mi_head);
16231514Sbrian
16331514Sbrian  /*
16431514Sbrian   * Lose the last four bytes of our output.
16531514Sbrian   * XXX: We should probably assert that these are the same as the
16631514Sbrian   *      contents of EMPTY_BLOCK.
16731514Sbrian   */
16854912Sbrian  mo = mo_head;
16954912Sbrian  for (len = mo->m_len; len < olen; mo = mo->m_next, len += mo->m_len)
17031514Sbrian    ;
17154912Sbrian  mo->m_len -= len - olen;
17254912Sbrian  if (mo->m_next != NULL) {
17354912Sbrian    m_freem(mo->m_next);
17454912Sbrian    mo->m_next = NULL;
17531514Sbrian  }
17631514Sbrian
17736285Sbrian  ccp->uncompout += ilen;
17836285Sbrian  ccp->compout += olen;
17931514Sbrian
18036285Sbrian  log_Printf(LogDEBUG, "DeflateOutput: %d => %d bytes, proto 0x%04x\n",
18146686Sbrian            ilen, olen, *proto);
18231514Sbrian
18346686Sbrian  *proto = ccp_Proto(ccp);
18446686Sbrian  return mo_head;
18531514Sbrian}
18631514Sbrian
18731514Sbrianstatic void
18836285SbrianDeflateResetInput(void *v)
18931514Sbrian{
19036285Sbrian  struct deflate_state *state = (struct deflate_state *)v;
19136285Sbrian
19236285Sbrian  state->seqno = 0;
19336285Sbrian  state->uncomp_rec = 0;
19436285Sbrian  inflateReset(&state->cx);
19536285Sbrian  log_Printf(LogCCP, "Deflate: Input channel reset\n");
19631514Sbrian}
19731514Sbrian
19831514Sbrianstatic struct mbuf *
19936285SbrianDeflateInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mi)
20031514Sbrian{
20136285Sbrian  struct deflate_state *state = (struct deflate_state *)v;
20231514Sbrian  struct mbuf *mo, *mo_head, *mi_head;
20331514Sbrian  u_char *wp;
20431514Sbrian  int ilen, olen;
20531514Sbrian  int seq, flush, res, first;
20631514Sbrian  u_char hdr[2];
20731514Sbrian
20836285Sbrian  log_DumpBp(LogDEBUG, "DeflateInput: Decompress packet:", mi);
20936285Sbrian  mi_head = mi = mbuf_Read(mi, hdr, 2);
21031514Sbrian  ilen = 2;
21131514Sbrian
21231514Sbrian  /* Check the sequence number. */
21331514Sbrian  seq = (hdr[0] << 8) + hdr[1];
21436285Sbrian  log_Printf(LogDEBUG, "DeflateInput: Seq %d\n", seq);
21536285Sbrian  if (seq != state->seqno) {
21636285Sbrian    if (seq <= state->uncomp_rec)
21732381Sbrian      /*
21832381Sbrian       * So the peer's started at zero again - fine !  If we're wrong,
21932381Sbrian       * inflate() will fail.  This is better than getting into a loop
22032381Sbrian       * trying to get a ResetReq to a busy sender.
22132381Sbrian       */
22236285Sbrian      state->seqno = seq;
22332038Sbrian    else {
22444650Sbrian      log_Printf(LogCCP, "DeflateInput: Seq error: Got %d, expected %d\n",
22536285Sbrian                seq, state->seqno);
22654912Sbrian      m_freem(mi_head);
22736285Sbrian      ccp_SendResetReq(&ccp->fsm);
22832038Sbrian      return NULL;
22932038Sbrian    }
23031514Sbrian  }
23136285Sbrian  state->seqno++;
23236285Sbrian  state->uncomp_rec = 0;
23331514Sbrian
23431514Sbrian  /* Allocate an output mbuf */
23554912Sbrian  mo_head = mo = m_get(DEFLATE_CHUNK_LEN, MB_CCPIN);
23631514Sbrian
23731514Sbrian  /* Our proto starts with 0 if it's compressed */
23831514Sbrian  wp = MBUF_CTOP(mo);
23931514Sbrian  wp[0] = '\0';
24031514Sbrian
24131514Sbrian  /*
24231514Sbrian   * We set avail_out to 1 initially so we can look at the first
24331514Sbrian   * byte of the output and decide whether we have a compressed
24431514Sbrian   * proto field.
24531514Sbrian   */
24636285Sbrian  state->cx.next_in = MBUF_CTOP(mi);
24754912Sbrian  state->cx.avail_in = mi->m_len;
24836285Sbrian  state->cx.next_out = wp + 1;
24936285Sbrian  state->cx.avail_out = 1;
25054912Sbrian  ilen += mi->m_len;
25131514Sbrian
25254912Sbrian  flush = mi->m_next ? Z_NO_FLUSH : Z_SYNC_FLUSH;
25331514Sbrian  first = 1;
25431514Sbrian  olen = 0;
25531514Sbrian
25631514Sbrian  while (1) {
25736285Sbrian    if ((res = inflate(&state->cx, flush)) != Z_OK) {
25831514Sbrian      if (res == Z_STREAM_END)
25931514Sbrian        break;			/* Done */
26044650Sbrian      log_Printf(LogCCP, "DeflateInput: inflate returned %d (%s)\n",
26136285Sbrian                res, state->cx.msg ? state->cx.msg : "");
26254912Sbrian      m_freem(mo_head);
26354912Sbrian      m_freem(mi);
26436285Sbrian      ccp_SendResetReq(&ccp->fsm);
26531514Sbrian      return NULL;
26631514Sbrian    }
26731514Sbrian
26836285Sbrian    if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
26931514Sbrian      break;
27031514Sbrian
27154912Sbrian    if (state->cx.avail_in == 0 && mi && (mi = m_free(mi)) != NULL) {
27231514Sbrian      /* underflow */
27336285Sbrian      state->cx.next_in = MBUF_CTOP(mi);
27454912Sbrian      ilen += (state->cx.avail_in = mi->m_len);
27554912Sbrian      if (mi->m_next == NULL)
27631514Sbrian        flush = Z_SYNC_FLUSH;
27731514Sbrian    }
27831514Sbrian
27936285Sbrian    if (state->cx.avail_out == 0) {
28031514Sbrian      /* overflow */
28131514Sbrian      if (first) {
28231514Sbrian        if (!(wp[1] & 1)) {
28331514Sbrian          /* 2 byte proto, shuffle it back in output */
28431514Sbrian          wp[0] = wp[1];
28536285Sbrian          state->cx.next_out--;
28636285Sbrian          state->cx.avail_out = DEFLATE_CHUNK_LEN-1;
28731514Sbrian        } else
28836285Sbrian          state->cx.avail_out = DEFLATE_CHUNK_LEN-2;
28931514Sbrian        first = 0;
29031514Sbrian      } else {
29154912Sbrian        olen += (mo->m_len = DEFLATE_CHUNK_LEN);
29254912Sbrian        mo->m_next = m_get(DEFLATE_CHUNK_LEN, MB_CCPIN);
29354912Sbrian        mo = mo->m_next;
29436285Sbrian        state->cx.next_out = MBUF_CTOP(mo);
29536285Sbrian        state->cx.avail_out = DEFLATE_CHUNK_LEN;
29631514Sbrian      }
29734536Sbrian    }
29831514Sbrian  }
29931514Sbrian
30031514Sbrian  if (mi != NULL)
30154912Sbrian    m_freem(mi);
30231514Sbrian
30331514Sbrian  if (first) {
30444650Sbrian    log_Printf(LogCCP, "DeflateInput: Length error\n");
30554912Sbrian    m_freem(mo_head);
30636285Sbrian    ccp_SendResetReq(&ccp->fsm);
30731514Sbrian    return NULL;
30831514Sbrian  }
30931514Sbrian
31054912Sbrian  olen += (mo->m_len = DEFLATE_CHUNK_LEN - state->cx.avail_out);
31131514Sbrian
31231514Sbrian  *proto = ((u_short)wp[0] << 8) | wp[1];
31354912Sbrian  mo_head->m_offset += 2;
31454912Sbrian  mo_head->m_len -= 2;
31531514Sbrian  olen -= 2;
31631514Sbrian
31736285Sbrian  ccp->compin += ilen;
31836285Sbrian  ccp->uncompin += olen;
31931514Sbrian
32036285Sbrian  log_Printf(LogDEBUG, "DeflateInput: %d => %d bytes, proto 0x%04x\n",
32131514Sbrian            ilen, olen, *proto);
32231514Sbrian
32331514Sbrian  /*
32431514Sbrian   * Simulate an EMPTY_BLOCK so that our dictionary stays in sync.
32531514Sbrian   * The peer will have silently removed this!
32631514Sbrian   */
32736285Sbrian  state->cx.next_out = garbage;
32836285Sbrian  state->cx.avail_out = sizeof garbage;
32936285Sbrian  state->cx.next_in = EMPTY_BLOCK;
33036285Sbrian  state->cx.avail_in = sizeof EMPTY_BLOCK;
33136285Sbrian  inflate(&state->cx, Z_SYNC_FLUSH);
33231514Sbrian
33331514Sbrian  return mo_head;
33431514Sbrian}
33531514Sbrian
33631514Sbrianstatic void
33736285SbrianDeflateDictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *mi)
33831514Sbrian{
33936285Sbrian  struct deflate_state *state = (struct deflate_state *)v;
34031632Sbrian  int res, flush, expect_error;
34131514Sbrian  u_char *rp;
34231514Sbrian  struct mbuf *mi_head;
34331514Sbrian  short len;
34431514Sbrian
34536285Sbrian  log_Printf(LogDEBUG, "DeflateDictSetup: Got seq %d\n", state->seqno);
34631514Sbrian
34731514Sbrian  /*
34831514Sbrian   * Stuff an ``uncompressed data'' block header followed by the
34931514Sbrian   * protocol in front of the input
35031514Sbrian   */
35154912Sbrian  mi_head = m_get(7, MB_CCPOUT);
35254912Sbrian  mi_head->m_next = mi;
35354912Sbrian  len = m_length(mi);
35431514Sbrian  mi = mi_head;
35531514Sbrian  rp = MBUF_CTOP(mi);
35631514Sbrian  if (proto < 0x100) {			/* Compress the protocol */
35731514Sbrian    rp[5] = proto & 0377;
35854912Sbrian    mi->m_len = 6;
35931514Sbrian    len++;
36031514Sbrian  } else {				/* Don't compress the protocol */
36131514Sbrian    rp[5] = proto >> 8;
36231514Sbrian    rp[6] = proto & 0377;
36354912Sbrian    mi->m_len = 7;
36431514Sbrian    len += 2;
36531514Sbrian  }
36631514Sbrian  rp[0] = 0x80;				/* BITS: 100xxxxx */
36731514Sbrian  rp[1] = len & 0377;			/* The length */
36831514Sbrian  rp[2] = len >> 8;
36931514Sbrian  rp[3] = (~len) & 0377;		/* One's compliment of the length */
37031514Sbrian  rp[4] = (~len) >> 8;
37131514Sbrian
37236285Sbrian  state->cx.next_in = rp;
37354912Sbrian  state->cx.avail_in = mi->m_len;
37436285Sbrian  state->cx.next_out = garbage;
37536285Sbrian  state->cx.avail_out = sizeof garbage;
37631514Sbrian  flush = Z_NO_FLUSH;
37731632Sbrian  expect_error = 0;
37831514Sbrian
37931514Sbrian  while (1) {
38036285Sbrian    if ((res = inflate(&state->cx, flush)) != Z_OK) {
38131514Sbrian      if (res == Z_STREAM_END)
38231514Sbrian        break;			/* Done */
38331632Sbrian      if (expect_error && res == Z_BUF_ERROR)
38431632Sbrian        break;
38544650Sbrian      log_Printf(LogCCP, "DeflateDictSetup: inflate returned %d (%s)\n",
38636285Sbrian                res, state->cx.msg ? state->cx.msg : "");
38744650Sbrian      log_Printf(LogCCP, "DeflateDictSetup: avail_in %d, avail_out %d\n",
38836285Sbrian                state->cx.avail_in, state->cx.avail_out);
38936285Sbrian      ccp_SendResetReq(&ccp->fsm);
39054912Sbrian      m_free(mi_head);		/* lose our allocated ``head'' buf */
39131514Sbrian      return;
39231514Sbrian    }
39331514Sbrian
39436285Sbrian    if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
39531514Sbrian      break;
39631514Sbrian
39754912Sbrian    if (state->cx.avail_in == 0 && mi && (mi = mi->m_next) != NULL) {
39831514Sbrian      /* underflow */
39936285Sbrian      state->cx.next_in = MBUF_CTOP(mi);
40054912Sbrian      state->cx.avail_in = mi->m_len;
40154912Sbrian      if (mi->m_next == NULL)
40231514Sbrian        flush = Z_SYNC_FLUSH;
40331514Sbrian    }
40431514Sbrian
40536285Sbrian    if (state->cx.avail_out == 0) {
40636285Sbrian      if (state->cx.avail_in == 0)
40731632Sbrian        /*
40831632Sbrian         * This seems to be a bug in libz !  If inflate() finished
40931632Sbrian         * with 0 avail_in and 0 avail_out *and* this is the end of
41031632Sbrian         * our input *and* inflate() *has* actually written all the
41131632Sbrian         * output it's going to, it *doesn't* return Z_STREAM_END !
41231632Sbrian         * When we subsequently call it with no more input, it gives
41331632Sbrian         * us Z_BUF_ERROR :-(  It seems pretty safe to ignore this
41431632Sbrian         * error (the dictionary seems to stay in sync).  In the worst
41531632Sbrian         * case, we'll drop the next compressed packet and do a
41631632Sbrian         * CcpReset() then.
41731632Sbrian         */
41831632Sbrian        expect_error = 1;
41931514Sbrian      /* overflow */
42036285Sbrian      state->cx.next_out = garbage;
42136285Sbrian      state->cx.avail_out = sizeof garbage;
42231514Sbrian    }
42331514Sbrian  }
42431514Sbrian
42536285Sbrian  ccp->compin += len;
42636285Sbrian  ccp->uncompin += len;
42731514Sbrian
42836285Sbrian  state->seqno++;
42936285Sbrian  state->uncomp_rec++;
43054912Sbrian  m_free(mi_head);		/* lose our allocated ``head'' buf */
43131514Sbrian}
43231514Sbrian
43331514Sbrianstatic const char *
43494894SbrianDeflateDispOpts(struct fsm_opt *o)
43531514Sbrian{
43637010Sbrian  static char disp[7];		/* Must be used immediately */
43731514Sbrian
43831514Sbrian  sprintf(disp, "win %d", (o->data[0]>>4) + 8);
43931514Sbrian  return disp;
44031514Sbrian}
44131514Sbrian
44231514Sbrianstatic void
443134789SbrianDeflateInitOptsOutput(struct bundle *bundle __unused, struct fsm_opt *o,
44498132Sbrian                      const struct ccp_config *cfg)
44531514Sbrian{
44694894Sbrian  o->hdr.len = 4;
44736285Sbrian  o->data[0] = ((cfg->deflate.out.winsize - 8) << 4) + 8;
44831514Sbrian  o->data[1] = '\0';
44931514Sbrian}
45031514Sbrian
45136285Sbrianstatic int
452134789SbrianDeflateSetOptsOutput(struct bundle *bundle __unused, struct fsm_opt *o,
453134789Sbrian                     const struct ccp_config *cfg __unused)
45431514Sbrian{
45594894Sbrian  if (o->hdr.len != 4 || (o->data[0] & 15) != 8 || o->data[1] != '\0')
45636285Sbrian    return MODE_REJ;
45731518Sbrian
45836285Sbrian  if ((o->data[0] >> 4) + 8 > 15) {
45936285Sbrian    o->data[0] = ((15 - 8) << 4) + 8;
46036285Sbrian    return MODE_NAK;
46136285Sbrian  }
46231514Sbrian
46336285Sbrian  return MODE_ACK;
46431514Sbrian}
46531514Sbrian
46631514Sbrianstatic int
467134789SbrianDeflateSetOptsInput(struct bundle *bundle __unused, struct fsm_opt *o,
46898132Sbrian                    const struct ccp_config *cfg)
46931514Sbrian{
47036285Sbrian  int want;
47136285Sbrian
47294894Sbrian  if (o->hdr.len != 4 || (o->data[0] & 15) != 8 || o->data[1] != '\0')
47331514Sbrian    return MODE_REJ;
47436285Sbrian
47536285Sbrian  want = (o->data[0] >> 4) + 8;
47636285Sbrian  if (cfg->deflate.in.winsize == 0) {
47736285Sbrian    if (want < 8 || want > 15) {
47836285Sbrian      o->data[0] = ((15 - 8) << 4) + 8;
47936285Sbrian    }
48036285Sbrian  } else if (want != cfg->deflate.in.winsize) {
48136285Sbrian    o->data[0] = ((cfg->deflate.in.winsize - 8) << 4) + 8;
48231514Sbrian    return MODE_NAK;
48331514Sbrian  }
48431514Sbrian
48531514Sbrian  return MODE_ACK;
48631514Sbrian}
48731514Sbrian
48836285Sbrianstatic void *
489134789SbrianDeflateInitInput(struct bundle *bundle __unused, struct fsm_opt *o)
49031514Sbrian{
49136285Sbrian  struct deflate_state *state;
49231514Sbrian
49336285Sbrian  state = (struct deflate_state *)malloc(sizeof(struct deflate_state));
49436285Sbrian  if (state != NULL) {
49536285Sbrian    state->winsize = (o->data[0] >> 4) + 8;
49636285Sbrian    state->cx.zalloc = NULL;
49736285Sbrian    state->cx.opaque = NULL;
49836285Sbrian    state->cx.zfree = NULL;
49936285Sbrian    state->cx.next_out = NULL;
50036285Sbrian    if (inflateInit2(&state->cx, -state->winsize) == Z_OK)
50136285Sbrian      DeflateResetInput(state);
50236285Sbrian    else {
50336285Sbrian      free(state);
50436285Sbrian      state = NULL;
50536285Sbrian    }
50636285Sbrian  }
50731514Sbrian
50836285Sbrian  return state;
50931518Sbrian}
51031518Sbrian
51136285Sbrianstatic void *
512134789SbrianDeflateInitOutput(struct bundle *bundle __unused, struct fsm_opt *o)
51331518Sbrian{
51436285Sbrian  struct deflate_state *state;
51531518Sbrian
51636285Sbrian  state = (struct deflate_state *)malloc(sizeof(struct deflate_state));
51736285Sbrian  if (state != NULL) {
51836285Sbrian    state->winsize = (o->data[0] >> 4) + 8;
51936285Sbrian    state->cx.zalloc = NULL;
52036285Sbrian    state->cx.opaque = NULL;
52136285Sbrian    state->cx.zfree = NULL;
52236285Sbrian    state->cx.next_in = NULL;
52336285Sbrian    if (deflateInit2(&state->cx, Z_DEFAULT_COMPRESSION, 8,
52436285Sbrian                     -state->winsize, 8, Z_DEFAULT_STRATEGY) == Z_OK)
52536285Sbrian      DeflateResetOutput(state);
52636285Sbrian    else {
52736285Sbrian      free(state);
52836285Sbrian      state = NULL;
52936285Sbrian    }
53036285Sbrian  }
53131514Sbrian
53236285Sbrian  return state;
53331514Sbrian}
53431514Sbrian
53531514Sbrianstatic void
53636285SbrianDeflateTermInput(void *v)
53731514Sbrian{
53836285Sbrian  struct deflate_state *state = (struct deflate_state *)v;
53936285Sbrian
54036285Sbrian  inflateEnd(&state->cx);
54136285Sbrian  free(state);
54231514Sbrian}
54331514Sbrian
54431514Sbrianstatic void
54536285SbrianDeflateTermOutput(void *v)
54631514Sbrian{
54736285Sbrian  struct deflate_state *state = (struct deflate_state *)v;
54836285Sbrian
54936285Sbrian  deflateEnd(&state->cx);
55036285Sbrian  free(state);
55131514Sbrian}
55231514Sbrian
55331518Sbrianconst struct ccp_algorithm PppdDeflateAlgorithm = {
55472025Sbrian  TY_PPPD_DEFLATE,	/* Older versions of pppd expected this ``type'' */
55536285Sbrian  CCP_NEG_DEFLATE24,
55631518Sbrian  DeflateDispOpts,
55778411Sbrian  ccp_DefaultUsable,
55878411Sbrian  ccp_DefaultRequired,
55931518Sbrian  {
56036285Sbrian    DeflateSetOptsInput,
56131518Sbrian    DeflateInitInput,
56231518Sbrian    DeflateTermInput,
56331518Sbrian    DeflateResetInput,
56431518Sbrian    DeflateInput,
56531518Sbrian    DeflateDictSetup
56631518Sbrian  },
56731518Sbrian  {
56879165Sbrian    0,
56936285Sbrian    DeflateInitOptsOutput,
57036285Sbrian    DeflateSetOptsOutput,
57131518Sbrian    DeflateInitOutput,
57231518Sbrian    DeflateTermOutput,
57331518Sbrian    DeflateResetOutput,
57431518Sbrian    DeflateOutput
57531518Sbrian  },
57631518Sbrian};
57731518Sbrian
57831514Sbrianconst struct ccp_algorithm DeflateAlgorithm = {
57931518Sbrian  TY_DEFLATE,		/* rfc 1979 */
58036285Sbrian  CCP_NEG_DEFLATE,
58131514Sbrian  DeflateDispOpts,
58278411Sbrian  ccp_DefaultUsable,
58378411Sbrian  ccp_DefaultRequired,
58431514Sbrian  {
58536285Sbrian    DeflateSetOptsInput,
58631514Sbrian    DeflateInitInput,
58731514Sbrian    DeflateTermInput,
58831514Sbrian    DeflateResetInput,
58931514Sbrian    DeflateInput,
59031514Sbrian    DeflateDictSetup
59131514Sbrian  },
59231514Sbrian  {
59379165Sbrian    0,
59436285Sbrian    DeflateInitOptsOutput,
59536285Sbrian    DeflateSetOptsOutput,
59631514Sbrian    DeflateInitOutput,
59731514Sbrian    DeflateTermOutput,
59831514Sbrian    DeflateResetOutput,
59931514Sbrian    DeflateOutput
60031514Sbrian  },
60131514Sbrian};
602