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