xref: /freebsd/sys/netgraph/ng_deflate.c (revision af23369a6deaaeb612ab266eb88b8bb8d560c322)
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 }
719