]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netgraph/ng_deflate.c
THIS BRANCH IS OBSOLETE, PLEASE READ:
[FreeBSD/FreeBSD.git] / sys / netgraph / ng_deflate.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2006 Alexander Motin <mav@alkar.net>
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 unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  * $FreeBSD$
30  */
31
32 /*
33  * Deflate PPP compression netgraph node type.
34  */
35
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/mbuf.h>
40 #include <sys/malloc.h>
41 #include <sys/endian.h>
42 #include <sys/errno.h>
43 #include <sys/syslog.h>
44 #include <contrib/zlib/zlib.h>
45
46 #include <netgraph/ng_message.h>
47 #include <netgraph/netgraph.h>
48 #include <netgraph/ng_parse.h>
49 #include <netgraph/ng_deflate.h>
50
51 #include "opt_netgraph.h"
52
53 static MALLOC_DEFINE(M_NETGRAPH_DEFLATE, "netgraph_deflate",
54     "netgraph deflate node");
55
56 /* DEFLATE header length */
57 #define DEFLATE_HDRLEN          2
58
59 #define PROT_COMPD              0x00fd
60
61 #define DEFLATE_BUF_SIZE        4096
62
63 /* Node private data */
64 struct ng_deflate_private {
65         struct ng_deflate_config cfg;           /* configuration */
66         u_char          inbuf[DEFLATE_BUF_SIZE];        /* input buffer */
67         u_char          outbuf[DEFLATE_BUF_SIZE];       /* output buffer */
68         z_stream        cx;                     /* compression context */
69         struct ng_deflate_stats stats;          /* statistics */
70         ng_ID_t         ctrlnode;               /* path to controlling node */
71         uint16_t        seqnum;                 /* sequence number */
72         u_char          compress;               /* compress/decompress flag */
73 };
74 typedef struct ng_deflate_private *priv_p;
75
76 /* Netgraph node methods */
77 static ng_constructor_t ng_deflate_constructor;
78 static ng_rcvmsg_t      ng_deflate_rcvmsg;
79 static ng_shutdown_t    ng_deflate_shutdown;
80 static ng_newhook_t     ng_deflate_newhook;
81 static ng_rcvdata_t     ng_deflate_rcvdata;
82 static ng_disconnect_t  ng_deflate_disconnect;
83
84 /* Helper functions */
85 static int      ng_deflate_compress(node_p, struct mbuf *, struct mbuf **);
86 static int      ng_deflate_decompress(node_p, struct mbuf *, struct mbuf **);
87 static void     ng_deflate_reset_req(node_p);
88
89 /* Parse type for struct ng_deflate_config. */
90 static const struct ng_parse_struct_field ng_deflate_config_type_fields[]
91         = NG_DEFLATE_CONFIG_INFO;
92 static const struct ng_parse_type ng_deflate_config_type = {
93         &ng_parse_struct_type,
94         ng_deflate_config_type_fields
95 };
96
97 /* Parse type for struct ng_deflate_stat. */
98 static const struct ng_parse_struct_field ng_deflate_stats_type_fields[]
99         = NG_DEFLATE_STATS_INFO;
100 static const struct ng_parse_type ng_deflate_stat_type = {
101         &ng_parse_struct_type,
102         ng_deflate_stats_type_fields
103 };
104
105 /* List of commands and how to convert arguments to/from ASCII. */
106 static const struct ng_cmdlist ng_deflate_cmds[] = {
107         {
108           NGM_DEFLATE_COOKIE,
109           NGM_DEFLATE_CONFIG,
110           "config",
111           &ng_deflate_config_type,
112           NULL
113         },
114         {
115           NGM_DEFLATE_COOKIE,
116           NGM_DEFLATE_RESETREQ,
117           "resetreq",
118           NULL,
119           NULL
120         },
121         {
122           NGM_DEFLATE_COOKIE,
123           NGM_DEFLATE_GET_STATS,
124           "getstats",
125           NULL,
126           &ng_deflate_stat_type
127         },
128         {
129           NGM_DEFLATE_COOKIE,
130           NGM_DEFLATE_CLR_STATS,
131           "clrstats",
132           NULL,
133           NULL
134         },
135         {
136           NGM_DEFLATE_COOKIE,
137           NGM_DEFLATE_GETCLR_STATS,
138           "getclrstats",
139           NULL,
140           &ng_deflate_stat_type
141         },
142         { 0 }
143 };
144
145 /* Node type descriptor */
146 static struct ng_type ng_deflate_typestruct = {
147         .version =      NG_ABI_VERSION,
148         .name =         NG_DEFLATE_NODE_TYPE,
149         .constructor =  ng_deflate_constructor,
150         .rcvmsg =       ng_deflate_rcvmsg,
151         .shutdown =     ng_deflate_shutdown,
152         .newhook =      ng_deflate_newhook,
153         .rcvdata =      ng_deflate_rcvdata,
154         .disconnect =   ng_deflate_disconnect,
155         .cmdlist =      ng_deflate_cmds,
156 };
157 NETGRAPH_INIT(deflate, &ng_deflate_typestruct);
158
159 /* Depend on separate zlib module. */
160 MODULE_DEPEND(ng_deflate, zlib, 1, 1, 1);
161
162 #define ERROUT(x)       do { error = (x); goto done; } while (0)
163
164 /************************************************************************
165                         NETGRAPH NODE STUFF
166  ************************************************************************/
167
168 /*
169  * Node type constructor
170  */
171 static int
172 ng_deflate_constructor(node_p node)
173 {
174         priv_p priv;
175
176         /* Allocate private structure. */
177         priv = malloc(sizeof(*priv), M_NETGRAPH_DEFLATE, M_WAITOK | M_ZERO);
178
179         NG_NODE_SET_PRIVATE(node, priv);
180
181         /* This node is not thread safe. */
182         NG_NODE_FORCE_WRITER(node);
183
184         /* Done */
185         return (0);
186 }
187
188 /*
189  * Give our OK for a hook to be added.
190  */
191 static int
192 ng_deflate_newhook(node_p node, hook_p hook, const char *name)
193 {
194         const priv_p priv = NG_NODE_PRIVATE(node);
195
196         if (NG_NODE_NUMHOOKS(node) > 0)
197                 return (EINVAL);
198
199         if (strcmp(name, NG_DEFLATE_HOOK_COMP) == 0)
200                 priv->compress = 1;
201         else if (strcmp(name, NG_DEFLATE_HOOK_DECOMP) == 0)
202                 priv->compress = 0;
203         else
204                 return (EINVAL);
205
206         return (0);
207 }
208
209 /*
210  * Receive a control message
211  */
212 static int
213 ng_deflate_rcvmsg(node_p node, item_p item, hook_p lasthook)
214 {
215         const priv_p priv = NG_NODE_PRIVATE(node);
216         struct ng_mesg *resp = NULL;
217         int error = 0;
218         struct ng_mesg *msg;
219
220         NGI_GET_MSG(item, msg);
221
222         if (msg->header.typecookie != NGM_DEFLATE_COOKIE)
223                 ERROUT(EINVAL);
224
225         switch (msg->header.cmd) {
226         case NGM_DEFLATE_CONFIG:
227             {
228                 struct ng_deflate_config *const cfg
229                     = (struct ng_deflate_config *)msg->data;
230
231                 /* Check configuration. */
232                 if (msg->header.arglen != sizeof(*cfg))
233                         ERROUT(EINVAL);
234                 if (cfg->enable) {
235                     if (cfg->windowBits < 8 || cfg->windowBits > 15)
236                         ERROUT(EINVAL);
237                 } else
238                     cfg->windowBits = 0;
239
240                 /* Clear previous state. */
241                 if (priv->cfg.enable) {
242                         if (priv->compress)
243                                 deflateEnd(&priv->cx);
244                         else
245                                 inflateEnd(&priv->cx);
246                         priv->cfg.enable = 0;
247                 }
248
249                 /* Configuration is OK, reset to it. */
250                 priv->cfg = *cfg;
251
252                 if (priv->cfg.enable) {
253                         priv->cx.next_in = NULL;
254                         int res;
255                         if (priv->compress) {
256                                 if ((res = deflateInit2(&priv->cx,
257                                     Z_DEFAULT_COMPRESSION, Z_DEFLATED,
258                                     -cfg->windowBits, 8,
259                                     Z_DEFAULT_STRATEGY)) != Z_OK) {
260                                         log(LOG_NOTICE,
261                                             "deflateInit2: error %d, %s\n",
262                                             res, priv->cx.msg);
263                                         priv->cfg.enable = 0;
264                                         ERROUT(ENOMEM);
265                                 }
266                         } else {
267                                 if ((res = inflateInit2(&priv->cx,
268                                     -cfg->windowBits)) != Z_OK) {
269                                         log(LOG_NOTICE,
270                                             "inflateInit2: error %d, %s\n",
271                                             res, priv->cx.msg);
272                                         priv->cfg.enable = 0;
273                                         ERROUT(ENOMEM);
274                                 }
275                         }
276                 }
277
278                 /* Initialize other state. */
279                 priv->seqnum = 0;
280
281                 /* Save return address so we can send reset-req's */
282                 priv->ctrlnode = NGI_RETADDR(item);
283                 break;
284             }
285
286         case NGM_DEFLATE_RESETREQ:
287                 ng_deflate_reset_req(node);
288                 break;
289
290         case NGM_DEFLATE_GET_STATS:
291         case NGM_DEFLATE_CLR_STATS:
292         case NGM_DEFLATE_GETCLR_STATS:
293                 /* Create response if requested. */
294                 if (msg->header.cmd != NGM_DEFLATE_CLR_STATS) {
295                         NG_MKRESPONSE(resp, msg,
296                             sizeof(struct ng_deflate_stats), M_NOWAIT);
297                         if (resp == NULL)
298                                 ERROUT(ENOMEM);
299                         bcopy(&priv->stats, resp->data,
300                             sizeof(struct ng_deflate_stats));
301                 }
302
303                 /* Clear stats if requested. */
304                 if (msg->header.cmd != NGM_DEFLATE_GET_STATS)
305                         bzero(&priv->stats,
306                             sizeof(struct ng_deflate_stats));
307                 break;
308
309         default:
310                 error = EINVAL;
311                 break;
312         }
313 done:
314         NG_RESPOND_MSG(error, node, item, resp);
315         NG_FREE_MSG(msg);
316         return (error);
317 }
318
319 /*
320  * Receive incoming data on our hook.
321  */
322 static int
323 ng_deflate_rcvdata(hook_p hook, item_p item)
324 {
325         const node_p node = NG_HOOK_NODE(hook);
326         const priv_p priv = NG_NODE_PRIVATE(node);
327         struct mbuf *m, *out;
328         int error;
329
330         if (!priv->cfg.enable) {
331                 NG_FREE_ITEM(item);
332                 return (ENXIO);
333         }
334
335         NGI_GET_M(item, m);
336         /* Compress */
337         if (priv->compress) {
338                 if ((error = ng_deflate_compress(node, m, &out)) != 0) {
339                         NG_FREE_ITEM(item);
340                         log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
341                         return (error);
342                 }
343         } else { /* Decompress */
344                 if ((error = ng_deflate_decompress(node, m, &out)) != 0) {
345                         NG_FREE_ITEM(item);
346                         log(LOG_NOTICE, "%s: error: %d\n", __func__, error);
347                         if (priv->ctrlnode != 0) {
348                                 struct ng_mesg *msg;
349
350                                 /* Need to send a reset-request. */
351                                 NG_MKMESSAGE(msg, NGM_DEFLATE_COOKIE,
352                                     NGM_DEFLATE_RESETREQ, 0, M_NOWAIT);
353                                 if (msg == NULL)
354                                         return (error);
355                                 NG_SEND_MSG_ID(error, node, msg,
356                                         priv->ctrlnode, 0);
357                         }
358                         return (error);
359                 }
360         }
361
362         NG_FWD_NEW_DATA(error, item, hook, out);
363         return (error);
364 }
365
366 /*
367  * Destroy node.
368  */
369 static int
370 ng_deflate_shutdown(node_p node)
371 {
372         const priv_p priv = NG_NODE_PRIVATE(node);
373
374         /* Take down netgraph node. */
375         if (priv->cfg.enable) {
376             if (priv->compress)
377                 deflateEnd(&priv->cx);
378             else
379                 inflateEnd(&priv->cx);
380         }
381
382         free(priv, M_NETGRAPH_DEFLATE);
383         NG_NODE_SET_PRIVATE(node, NULL);
384         NG_NODE_UNREF(node);            /* let the node escape */
385         return (0);
386 }
387
388 /*
389  * Hook disconnection
390  */
391 static int
392 ng_deflate_disconnect(hook_p hook)
393 {
394         const node_p node = NG_HOOK_NODE(hook);
395         const priv_p priv = NG_NODE_PRIVATE(node);
396
397         if (priv->cfg.enable) {
398             if (priv->compress)
399                 deflateEnd(&priv->cx);
400             else
401                 inflateEnd(&priv->cx);
402             priv->cfg.enable = 0;
403         }
404
405         /* Go away if no longer connected. */
406         if ((NG_NODE_NUMHOOKS(node) == 0) && NG_NODE_IS_VALID(node))
407                 ng_rmnode_self(node);
408         return (0);
409 }
410
411 /************************************************************************
412                         HELPER STUFF
413  ************************************************************************/
414
415 /*
416  * Compress/encrypt a packet and put the result in a new mbuf at *resultp.
417  * The original mbuf is not free'd.
418  */
419 static int
420 ng_deflate_compress(node_p node, struct mbuf *m, struct mbuf **resultp)
421 {
422         const priv_p    priv = NG_NODE_PRIVATE(node);
423         int             outlen, inlen;
424         int             rtn;
425
426         /* Initialize. */
427         *resultp = NULL;
428
429         inlen = m->m_pkthdr.len;
430
431         priv->stats.FramesPlain++;
432         priv->stats.InOctets+=inlen;
433
434         if (inlen > DEFLATE_BUF_SIZE) {
435                 priv->stats.Errors++;
436                 NG_FREE_M(m);
437                 return (ENOMEM);
438         }
439
440         /* We must own the mbuf chain exclusively to modify it. */
441         m = m_unshare(m, M_NOWAIT);
442         if (m == NULL) {
443                 priv->stats.Errors++;
444                 return (ENOMEM);
445         }
446
447         /* Work with contiguous regions of memory. */
448         m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
449         outlen = DEFLATE_BUF_SIZE;
450
451         /* Compress "inbuf" into "outbuf". */
452         /* Prepare to compress. */
453         if (priv->inbuf[0] != 0) {
454                 priv->cx.next_in = priv->inbuf;
455                 priv->cx.avail_in = inlen;
456         } else {
457                 priv->cx.next_in = priv->inbuf + 1; /* compress protocol */
458                 priv->cx.avail_in = inlen - 1;
459         }
460         priv->cx.next_out = priv->outbuf + 2 + DEFLATE_HDRLEN;
461         priv->cx.avail_out = outlen - 2 - DEFLATE_HDRLEN;
462
463         /* Compress. */
464         rtn = deflate(&priv->cx, Z_SYNC_FLUSH);
465
466         /* Check return value. */
467         if (rtn != Z_OK) {
468                 priv->stats.Errors++;
469                 log(LOG_NOTICE, "ng_deflate: compression error: %d (%s)\n",
470                     rtn, priv->cx.msg);
471                 NG_FREE_M(m);
472                 return (EINVAL);
473         }
474
475         /* Calculate resulting size. */
476         outlen -= priv->cx.avail_out;
477         /*
478          * Z_SYNC_FLUSH completes the current deflate block and follows
479          * it with an empty stored block that is three bits plus filler
480          * bits to the next byte, followed by four bytes (00 00 ff ff).
481          * RFC 1979 Section 2.1, "Data" requires the four bytes be
482          * removed before transmission.
483          */
484         outlen -= 4;
485         MPASS(outlen > 0);
486         MPASS(priv->outbuf[outlen + 0] == 0x00);
487         MPASS(priv->outbuf[outlen + 1] == 0x00);
488         MPASS(priv->outbuf[outlen + 2] == 0xff);
489         MPASS(priv->outbuf[outlen + 3] == 0xff);
490
491         /* If we can't compress this packet, send it as-is. */
492         if (outlen > inlen) {
493                 /* Return original packet uncompressed. */
494                 *resultp = m;
495                 priv->stats.FramesUncomp++;
496                 priv->stats.OutOctets+=inlen;
497         } else {
498                 /* Install header. */
499                 be16enc(priv->outbuf, PROT_COMPD);
500                 be16enc(priv->outbuf + 2, priv->seqnum);
501
502                 /* Return packet in an mbuf. */
503                 m_copyback(m, 0, outlen, (caddr_t)priv->outbuf);
504                 if (m->m_pkthdr.len < outlen) {
505                         m_freem(m);
506                         priv->stats.Errors++;
507                         return (ENOMEM);
508                 } else if (outlen < m->m_pkthdr.len)
509                         m_adj(m, outlen - m->m_pkthdr.len);
510                 *resultp = m;
511                 priv->stats.FramesComp++;
512                 priv->stats.OutOctets+=outlen;
513         }
514
515         /* Update sequence number. */
516         priv->seqnum++;
517
518         return (0);
519 }
520
521 /*
522  * Decompress/decrypt packet and put the result in a new mbuf at *resultp.
523  * The original mbuf is not free'd.
524  */
525 static int
526 ng_deflate_decompress(node_p node, struct mbuf *m, struct mbuf **resultp)
527 {
528         const priv_p    priv = NG_NODE_PRIVATE(node);
529         int             outlen, inlen, datalen;
530         int             rtn;
531         uint16_t        proto;
532         int             offset;
533         uint16_t        rseqnum;
534         u_char          headbuf[5];
535         static u_char   EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff };
536
537         /* Initialize. */
538         *resultp = NULL;
539
540         inlen = m->m_pkthdr.len;
541
542         if (inlen > DEFLATE_BUF_SIZE) {
543                 priv->stats.Errors++;
544                 NG_FREE_M(m);
545                 priv->seqnum = 0;
546                 return (ENOMEM);
547         }
548
549         /* We must own the mbuf chain exclusively to modify it. */
550         m = m_unshare(m, M_NOWAIT);
551         if (m == NULL) {
552                 priv->stats.Errors++;
553                 return (ENOMEM);
554         }
555
556         /* Work with contiguous regions of memory. */
557         m_copydata(m, 0, inlen, (caddr_t)priv->inbuf);
558
559         /* Separate proto. */
560         if ((priv->inbuf[0] & 0x01) != 0) {
561                 proto = priv->inbuf[0];
562                 offset = 1;
563         } else {
564                 proto = be16dec(priv->inbuf);
565                 offset = 2;
566         }
567
568         priv->stats.InOctets += inlen;
569
570         /* Packet is compressed, so decompress. */
571         if (proto == PROT_COMPD) {
572                 priv->stats.FramesComp++;
573
574                 /* Check sequence number. */
575                 rseqnum = be16dec(priv->inbuf + offset);
576                 offset += 2;
577                 if (rseqnum != priv->seqnum) {
578                         priv->stats.Errors++;
579                         log(LOG_NOTICE, "ng_deflate: wrong sequence: %u "
580                             "instead of %u\n", rseqnum, priv->seqnum);
581                         NG_FREE_M(m);
582                         priv->seqnum = 0;
583                         return (EPIPE);
584                 }
585
586                 outlen = DEFLATE_BUF_SIZE;
587
588                 /* Decompress "inbuf" into "outbuf". */
589                 /* Prepare to decompress. */
590                 priv->cx.next_in = priv->inbuf + offset;
591                 priv->cx.avail_in = inlen - offset;
592                 /* Reserve space for protocol decompression. */
593                 priv->cx.next_out = priv->outbuf + 1;
594                 priv->cx.avail_out = outlen - 1;
595
596                 /* Decompress. */
597                 rtn = inflate(&priv->cx, Z_SYNC_FLUSH);
598
599                 /* Check return value. */
600                 if (rtn != Z_OK && rtn != Z_STREAM_END) {
601                         priv->stats.Errors++;
602                         NG_FREE_M(m);
603                         priv->seqnum = 0;
604                         log(LOG_NOTICE, "%s: decompression error: %d (%s)\n",
605                             __func__, rtn, priv->cx.msg);
606
607                         switch (rtn) {
608                         case Z_MEM_ERROR:
609                                 return (ENOMEM);
610                         case Z_DATA_ERROR:
611                                 return (EIO);
612                         default:
613                                 return (EINVAL);
614                         }
615                 }
616
617                 /* Handle the EMPTY_BLOCK omitted by sender */
618                 if (inflateSyncPoint(&priv->cx)) {
619                         priv->cx.avail_in = 4;
620                         priv->cx.next_in = EMPTY_BLOCK;
621                         inflate(&priv->cx, Z_SYNC_FLUSH);
622                 }
623
624                 /* Calculate resulting size. */
625                 outlen -= priv->cx.avail_out;
626
627                 /* Decompress protocol. */
628                 if ((priv->outbuf[1] & 0x01) != 0) {
629                         priv->outbuf[0] = 0;
630                         /* Return packet in an mbuf. */
631                         m_copyback(m, 0, outlen, (caddr_t)priv->outbuf);
632                 } else {
633                         outlen--;
634                         /* Return packet in an mbuf. */
635                         m_copyback(m, 0, outlen, (caddr_t)(priv->outbuf + 1));
636                 }
637                 if (m->m_pkthdr.len < outlen) {
638                         m_freem(m);
639                         priv->stats.Errors++;
640                         priv->seqnum = 0;
641                         return (ENOMEM);
642                 } else if (outlen < m->m_pkthdr.len)
643                         m_adj(m, outlen - m->m_pkthdr.len);
644                 *resultp = m;
645                 priv->stats.FramesPlain++;
646                 priv->stats.OutOctets+=outlen;
647
648         } else {
649                 /* Packet is not compressed, just update dictionary. */
650                 priv->stats.FramesUncomp++;
651
652                 /*
653                  * Fake a header for uncompressed data block
654                  */
655                 datalen = inlen - offset + 1;
656                 headbuf[0] = 0x80;
657                 headbuf[1] = datalen & 0xff;
658                 headbuf[2] = datalen >> 8;
659                 headbuf[3] = (~datalen) & 0xff;
660                 headbuf[4] = (~datalen) >> 8;
661
662                 priv->cx.next_in = headbuf;
663                 priv->cx.avail_in = sizeof(headbuf);
664                 priv->cx.next_out = priv->outbuf;
665                 priv->cx.avail_out = DEFLATE_BUF_SIZE;
666
667                 rtn = inflate(&priv->cx, Z_NO_FLUSH);
668
669                 if (priv->inbuf[0] == 0) {
670                         priv->cx.next_in =
671                             priv->inbuf + 1; /* compress protocol */
672                         priv->cx.avail_in = inlen - 1;
673                 } else {
674                         priv->cx.next_in = priv->inbuf;
675                         priv->cx.avail_in = inlen;
676                 }
677                 priv->cx.next_out = priv->outbuf;
678                 priv->cx.avail_out = DEFLATE_BUF_SIZE;
679
680                 rtn = inflate(&priv->cx, Z_SYNC_FLUSH);
681
682                 /* Check return value */
683                 if (rtn != Z_OK) {
684                         priv->stats.Errors++;
685                         log(LOG_NOTICE, "%s: inflate error: %d (%s)\n",
686                             __func__, rtn, priv->cx.msg);
687                         NG_FREE_M(m);
688                         priv->seqnum = 0;
689                         return (EINVAL);
690                 }
691
692                 *resultp = m;
693                 priv->stats.FramesPlain++;
694                 priv->stats.OutOctets += inlen;
695         }
696
697         /* Update sequence number. */
698         priv->seqnum++;
699
700         return (0);
701 }
702
703 /*
704  * The peer has sent us a CCP ResetRequest, so reset our transmit state.
705  */
706 static void
707 ng_deflate_reset_req(node_p node)
708 {
709         const priv_p priv = NG_NODE_PRIVATE(node);
710
711         priv->seqnum = 0;
712         if (priv->cfg.enable) {
713             if (priv->compress)
714                 deflateReset(&priv->cx);
715             else
716                 inflateReset(&priv->cx);
717         }
718 }