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