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