• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to
8  * deal in the Software without restriction, including without limitation the
9  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10  * sell copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22  * IN THE SOFTWARE.
23  */
24 
25 #include "private-lib-core.h"
26 #include "extension-permessage-deflate.h"
27 #include <stdio.h>
28 #include <string.h>
29 #include <assert.h>
30 
31 #define LWS_ZLIB_MEMLEVEL 8
32 
33 const struct lws_ext_options lws_ext_pm_deflate_options[] = {
34 	/* public RFC7692 settings */
35 	{ "server_no_context_takeover", EXTARG_NONE },
36 	{ "client_no_context_takeover", EXTARG_NONE },
37 	{ "server_max_window_bits",	EXTARG_OPT_DEC },
38 	{ "client_max_window_bits",	EXTARG_OPT_DEC },
39 	/* ones only user code can set */
40 	{ "rx_buf_size",		EXTARG_DEC },
41 	{ "tx_buf_size",		EXTARG_DEC },
42 	{ "compression_level",		EXTARG_DEC },
43 	{ "mem_level",			EXTARG_DEC },
44 	{ NULL, 0 }, /* sentinel */
45 };
46 
47 static void
lws_extension_pmdeflate_restrict_args(struct lws * wsi,struct lws_ext_pm_deflate_priv * priv)48 lws_extension_pmdeflate_restrict_args(struct lws *wsi,
49 				      struct lws_ext_pm_deflate_priv *priv)
50 {
51 	int n, extra;
52 
53 	/* cap the RX buf at the nearest power of 2 to protocol rx buf */
54 
55 	n = (int)wsi->a.context->pt_serv_buf_size;
56 	if (wsi->a.protocol->rx_buffer_size)
57 		n = (int)wsi->a.protocol->rx_buffer_size;
58 
59 	extra = 7;
60 	while (n >= 1 << (extra + 1))
61 		extra++;
62 
63 	if (extra < priv->args[PMD_RX_BUF_PWR2]) {
64 		priv->args[PMD_RX_BUF_PWR2] = (unsigned char)extra;
65 		lwsl_wsi_info(wsi, " Capping pmd rx to %d", 1 << extra);
66 	}
67 }
68 
69 static unsigned char trail[] = { 0, 0, 0xff, 0xff };
70 
71 LWS_VISIBLE int
lws_extension_callback_pm_deflate(struct lws_context * context,const struct lws_extension * ext,struct lws * wsi,enum lws_extension_callback_reasons reason,void * user,void * in,size_t len)72 lws_extension_callback_pm_deflate(struct lws_context *context,
73 				  const struct lws_extension *ext,
74 				  struct lws *wsi,
75 				  enum lws_extension_callback_reasons reason,
76 				  void *user, void *in, size_t len)
77 {
78 	struct lws_ext_pm_deflate_priv *priv =
79 				     (struct lws_ext_pm_deflate_priv *)user;
80 	struct lws_ext_pm_deflate_rx_ebufs *pmdrx =
81 				(struct lws_ext_pm_deflate_rx_ebufs *)in;
82 	struct lws_ext_option_arg *oa;
83 	int n, ret = 0, was_fin = 0, m;
84 	unsigned int pen = 0;
85 	int penbits = 0;
86 
87 	switch (reason) {
88 	case LWS_EXT_CB_NAMED_OPTION_SET:
89 		oa = in;
90 		if (!oa->option_name)
91 			break;
92 		lwsl_wsi_ext(wsi, "named option set: %s", oa->option_name);
93 		for (n = 0; n < (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options);
94 		     n++)
95 			if (!strcmp(lws_ext_pm_deflate_options[n].name,
96 				    oa->option_name))
97 				break;
98 
99 		if (n == (int)LWS_ARRAY_SIZE(lws_ext_pm_deflate_options))
100 			break;
101 		oa->option_index = n;
102 
103 		/* fallthru */
104 
105 	case LWS_EXT_CB_OPTION_SET:
106 		oa = in;
107 		lwsl_wsi_ext(wsi, "option set: idx %d, %s, len %d",
108 			 oa->option_index, oa->start, oa->len);
109 		if (oa->start)
110 			priv->args[oa->option_index] = (unsigned char)atoi(oa->start);
111 		else
112 			priv->args[oa->option_index] = 1;
113 
114 		if (priv->args[PMD_CLIENT_MAX_WINDOW_BITS] == 8)
115 			priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 9;
116 
117 		lws_extension_pmdeflate_restrict_args(wsi, priv);
118 		break;
119 
120 	case LWS_EXT_CB_OPTION_CONFIRM:
121 		if (priv->args[PMD_SERVER_MAX_WINDOW_BITS] < 8 ||
122 		    priv->args[PMD_SERVER_MAX_WINDOW_BITS] > 15 ||
123 		    priv->args[PMD_CLIENT_MAX_WINDOW_BITS] < 8 ||
124 		    priv->args[PMD_CLIENT_MAX_WINDOW_BITS] > 15)
125 			return -1;
126 		break;
127 
128 	case LWS_EXT_CB_CLIENT_CONSTRUCT:
129 	case LWS_EXT_CB_CONSTRUCT:
130 
131 		n = (int)context->pt_serv_buf_size;
132 		if (wsi->a.protocol->rx_buffer_size)
133 			n = (int)wsi->a.protocol->rx_buffer_size;
134 
135 		if (n < 128) {
136 			lwsl_wsi_info(wsi, " permessage-deflate requires the protocol "
137 				  "(%s) to have an RX buffer >= 128",
138 				  wsi->a.protocol->name);
139 			return -1;
140 		}
141 
142 		/* fill in **user */
143 		priv = lws_zalloc(sizeof(*priv), "pmd priv");
144 		*((void **)user) = priv;
145 		lwsl_wsi_ext(wsi, "LWS_EXT_CB_*CONSTRUCT");
146 		memset(priv, 0, sizeof(*priv));
147 
148 		/* fill in pointer to options list */
149 		if (in)
150 			*((const struct lws_ext_options **)in) =
151 					lws_ext_pm_deflate_options;
152 
153 		/* fallthru */
154 
155 	case LWS_EXT_CB_OPTION_DEFAULT:
156 
157 		/* set the public, RFC7692 defaults... */
158 
159 		priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER] = 0,
160 		priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER] = 0;
161 		priv->args[PMD_SERVER_MAX_WINDOW_BITS] = 15;
162 		priv->args[PMD_CLIENT_MAX_WINDOW_BITS] = 15;
163 
164 		/* ...and the ones the user code can override */
165 
166 		priv->args[PMD_RX_BUF_PWR2] = 10; /* ie, 1024 */
167 		priv->args[PMD_TX_BUF_PWR2] = 10; /* ie, 1024 */
168 		priv->args[PMD_COMP_LEVEL] = 1;
169 		priv->args[PMD_MEM_LEVEL] = 8;
170 
171 		lws_extension_pmdeflate_restrict_args(wsi, priv);
172 		break;
173 
174 	case LWS_EXT_CB_DESTROY:
175 		lwsl_wsi_ext(wsi, "LWS_EXT_CB_DESTROY");
176 		lws_free(priv->buf_rx_inflated);
177 		lws_free(priv->buf_tx_deflated);
178 		if (priv->rx_init)
179 			(void)inflateEnd(&priv->rx);
180 		if (priv->tx_init)
181 			(void)deflateEnd(&priv->tx);
182 		lws_free(priv);
183 
184 		return ret;
185 
186 
187 	case LWS_EXT_CB_PAYLOAD_RX:
188 		/*
189 		 * ie, we are INFLATING
190 		 */
191 		lwsl_wsi_ext(wsi, " LWS_EXT_CB_PAYLOAD_RX: in %d, existing in %d",
192 			 pmdrx->eb_in.len, priv->rx.avail_in);
193 
194 		/*
195 		 * If this frame is not marked as compressed,
196 		 * there is nothing we should do with it
197 		 */
198 
199 		if (!(wsi->ws->rsv_first_msg & 0x40) || (wsi->ws->opcode & 8))
200 			/*
201 			 * This is a bit different than DID_NOTHING... we have
202 			 * identified using ext-private bits in the packet, or
203 			 * by it being a control fragment that we SHOULD not do
204 			 * anything to it, parent should continue as if we
205 			 * processed it
206 			 */
207 			return PMDR_NOTHING_WE_SHOULD_DO;
208 
209 		/*
210 		 * we shouldn't come back in here if we already applied the
211 		 * trailer for this compressed packet
212 		 */
213 		if (!wsi->ws->pmd_trailer_application)
214 			return PMDR_DID_NOTHING;
215 
216 		pmdrx->eb_out.len = 0;
217 
218 		lwsl_wsi_ext(wsi, "LWS_EXT_CB_PAYLOAD_RX: in %d, "
219 			 "existing avail in %d, pkt fin: %d",
220 			 pmdrx->eb_in.len, priv->rx.avail_in, wsi->ws->final);
221 
222 		/* if needed, initialize the inflator */
223 
224 		if (!priv->rx_init) {
225 			if (inflateInit2(&priv->rx,
226 			     -priv->args[PMD_SERVER_MAX_WINDOW_BITS]) != Z_OK) {
227 				lwsl_wsi_err(wsi, "iniflateInit failed");
228 				return PMDR_FAILED;
229 			}
230 			priv->rx_init = 1;
231 			if (!priv->buf_rx_inflated)
232 				priv->buf_rx_inflated = lws_malloc(
233 					(unsigned int)(LWS_PRE + 7 + 5 +
234 					    (1 << priv->args[PMD_RX_BUF_PWR2])),
235 					    "pmd rx inflate buf");
236 			if (!priv->buf_rx_inflated) {
237 				lwsl_wsi_err(wsi, "OOM");
238 				return PMDR_FAILED;
239 			}
240 		}
241 
242 #if 0
243 		/*
244 		 * don't give us new input while we still work through
245 		 * the last input
246 		 */
247 
248 		if (priv->rx.avail_in && pmdrx->eb_in.token &&
249 					 pmdrx->eb_in.len) {
250 			lwsl_wsi_warn(wsi, "priv->rx.avail_in %d while getting new in",
251 					priv->rx.avail_in);
252 	//		assert(0);
253 		}
254 #endif
255 		if (!priv->rx.avail_in && pmdrx->eb_in.token && pmdrx->eb_in.len) {
256 			priv->rx.next_in = (unsigned char *)pmdrx->eb_in.token;
257 			priv->rx.avail_in = (uInt)pmdrx->eb_in.len;
258 		}
259 
260 		priv->rx.next_out = priv->buf_rx_inflated + LWS_PRE;
261 		pmdrx->eb_out.token = priv->rx.next_out;
262 		priv->rx.avail_out = (uInt)(1 << priv->args[PMD_RX_BUF_PWR2]);
263 
264 		/* so... if...
265 		 *
266 		 *  - he has no remaining input content for this message, and
267 		 *
268 		 *  - and this is the final fragment, and
269 		 *
270 		 *  - we used everything that could be drained on the input side
271 		 *
272 		 * ...then put back the 00 00 FF FF the sender stripped as our
273 		 * input to zlib
274 		 */
275 		if (!priv->rx.avail_in &&
276 		    wsi->ws->final &&
277 		    !wsi->ws->rx_packet_length &&
278 		    wsi->ws->pmd_trailer_application) {
279 			lwsl_wsi_ext(wsi, "trailer apply 1");
280 			was_fin = 1;
281 			wsi->ws->pmd_trailer_application = 0;
282 			priv->rx.next_in = trail;
283 			priv->rx.avail_in = sizeof(trail);
284 		}
285 
286 		/*
287 		 * if after all that there's nothing pending and nothing to give
288 		 * him right now, bail without having done anything
289 		 */
290 
291 		if (!priv->rx.avail_in)
292 			return PMDR_DID_NOTHING;
293 
294 		n = inflate(&priv->rx, was_fin ? Z_SYNC_FLUSH : Z_NO_FLUSH);
295 		lwsl_wsi_ext(wsi, "inflate ret %d, avi %d, avo %d, wsifinal %d", n,
296 			 priv->rx.avail_in, priv->rx.avail_out, wsi->ws->final);
297 		switch (n) {
298 		case Z_NEED_DICT:
299 		case Z_STREAM_ERROR:
300 		case Z_DATA_ERROR:
301 		case Z_MEM_ERROR:
302 			lwsl_wsi_err(wsi, "zlib error inflate %d: \"%s\"",
303 				  n, priv->rx.msg);
304 			return PMDR_FAILED;
305 		}
306 
307 		/*
308 		 * track how much input was used, and advance it
309 		 */
310 
311 		pmdrx->eb_in.token = pmdrx->eb_in.token +
312 				         ((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->rx.avail_in);
313 		pmdrx->eb_in.len = (int)priv->rx.avail_in;
314 
315 		lwsl_wsi_debug(wsi, "%d %d %d %d %d",
316 				priv->rx.avail_in,
317 				wsi->ws->final,
318 				(int)wsi->ws->rx_packet_length,
319 				was_fin,
320 				wsi->ws->pmd_trailer_application);
321 
322 		if (!priv->rx.avail_in &&
323 		    wsi->ws->final &&
324 		    !wsi->ws->rx_packet_length &&
325 		    !was_fin &&
326 		    wsi->ws->pmd_trailer_application) {
327 			lwsl_wsi_ext(wsi, "RX trailer apply 2");
328 
329 			/* we overallocated just for this situation where
330 			 * we might issue something */
331 			priv->rx.avail_out += 5;
332 
333 			was_fin = 1;
334 			wsi->ws->pmd_trailer_application = 0;
335 			priv->rx.next_in = trail;
336 			priv->rx.avail_in = sizeof(trail);
337 			n = inflate(&priv->rx, Z_SYNC_FLUSH);
338 			lwsl_wsi_ext(wsi, "RX trailer infl ret %d, avi %d, avo %d",
339 				 n, priv->rx.avail_in, priv->rx.avail_out);
340 			switch (n) {
341 			case Z_NEED_DICT:
342 			case Z_STREAM_ERROR:
343 			case Z_DATA_ERROR:
344 			case Z_MEM_ERROR:
345 				lwsl_wsi_info(wsi, "zlib error inflate %d: %s",
346 					  n, priv->rx.msg);
347 				return -1;
348 			}
349 
350 			assert(priv->rx.avail_out);
351 		}
352 
353 		pmdrx->eb_out.len = lws_ptr_diff(priv->rx.next_out,
354 						 pmdrx->eb_out.token);
355 		priv->count_rx_between_fin = priv->count_rx_between_fin + (size_t)pmdrx->eb_out.len;
356 
357 		lwsl_wsi_ext(wsi, "  RX leaving with new effbuff len %d, "
358 			 "rx.avail_in=%d, TOTAL RX since FIN %lu",
359 			 pmdrx->eb_out.len, priv->rx.avail_in,
360 			 (unsigned long)priv->count_rx_between_fin);
361 
362 		if (was_fin) {
363 			lwsl_wsi_ext(wsi, "was_fin");
364 			priv->count_rx_between_fin = 0;
365 			if (priv->args[PMD_SERVER_NO_CONTEXT_TAKEOVER]) {
366 				lwsl_wsi_ext(wsi, "PMD_SERVER_NO_CONTEXT_TAKEOVER");
367 				(void)inflateEnd(&priv->rx);
368 				priv->rx_init = 0;
369 			}
370 
371 			return PMDR_EMPTY_FINAL;
372 		}
373 
374 		if (priv->rx.avail_in)
375 			return PMDR_HAS_PENDING;
376 
377 		return PMDR_EMPTY_NONFINAL;
378 
379 	case LWS_EXT_CB_PAYLOAD_TX:
380 
381 		/*
382 		 * ie, we are DEFLATING
383 		 *
384 		 * initialize us if needed
385 		 */
386 
387 		if (!priv->tx_init) {
388 			n = deflateInit2(&priv->tx, priv->args[PMD_COMP_LEVEL],
389 					 Z_DEFLATED,
390 					 -priv->args[PMD_SERVER_MAX_WINDOW_BITS +
391 						(wsi->a.vhost->listen_port <= 0)],
392 					 priv->args[PMD_MEM_LEVEL],
393 					 Z_DEFAULT_STRATEGY);
394 			if (n != Z_OK) {
395 				lwsl_wsi_ext(wsi, "inflateInit2 failed %d", n);
396 				return PMDR_FAILED;
397 			}
398 			priv->tx_init = 1;
399 		}
400 
401 		if (!priv->buf_tx_deflated)
402 			priv->buf_tx_deflated = lws_malloc((unsigned int)(LWS_PRE + 7 + 5 +
403 					    (1 << priv->args[PMD_TX_BUF_PWR2])),
404 					    "pmd tx deflate buf");
405 		if (!priv->buf_tx_deflated) {
406 			lwsl_wsi_err(wsi, "OOM");
407 			return PMDR_FAILED;
408 		}
409 
410 		/* hook us up with any deflated input that the caller has */
411 
412 		if (pmdrx->eb_in.token) {
413 
414 			assert(!priv->tx.avail_in);
415 
416 			priv->count_tx_between_fin = priv->count_tx_between_fin + (size_t)pmdrx->eb_in.len;
417 			lwsl_wsi_ext(wsi, "TX: eb_in length %d, "
418 				    "TOTAL TX since FIN: %d",
419 				    pmdrx->eb_in.len,
420 				    (int)priv->count_tx_between_fin);
421 			priv->tx.next_in = (unsigned char *)pmdrx->eb_in.token;
422 			priv->tx.avail_in = (uInt)pmdrx->eb_in.len;
423 		}
424 
425 		priv->tx.next_out = priv->buf_tx_deflated + LWS_PRE + 5;
426 		pmdrx->eb_out.token = priv->tx.next_out;
427 		priv->tx.avail_out = (uInt)(1 << priv->args[PMD_TX_BUF_PWR2]);
428 
429 		pen = 0;
430 		penbits = 0;
431 		deflatePending(&priv->tx, &pen, &penbits);
432 		pen = pen | (unsigned int)penbits;
433 
434 		if (!priv->tx.avail_in && (len & LWS_WRITE_NO_FIN)) {
435 			lwsl_wsi_ext(wsi, "no available in, pen: %u", pen);
436 
437 			if (!pen)
438 				return PMDR_DID_NOTHING;
439 		}
440 
441 		m = Z_NO_FLUSH;
442 		if (!(len & LWS_WRITE_NO_FIN)) {
443 			lwsl_wsi_ext(wsi, "deflate with SYNC_FLUSH, pkt len %d",
444 					(int)wsi->ws->rx_packet_length);
445 			m = Z_SYNC_FLUSH;
446 		}
447 
448 		n = deflate(&priv->tx, m);
449 		if (n == Z_STREAM_ERROR) {
450 			lwsl_wsi_notice(wsi, "Z_STREAM_ERROR");
451 			return PMDR_FAILED;
452 		}
453 
454 		pen = (!priv->tx.avail_out) && n != Z_STREAM_END;
455 
456 		lwsl_wsi_ext(wsi, "deflate ret %d, len 0x%x", n,
457 				(unsigned int)len);
458 
459 		if ((len & 0xf) == LWS_WRITE_TEXT)
460 			priv->tx_first_frame_type = LWSWSOPC_TEXT_FRAME;
461 		if ((len & 0xf) == LWS_WRITE_BINARY)
462 			priv->tx_first_frame_type = LWSWSOPC_BINARY_FRAME;
463 
464 		pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
465 						 pmdrx->eb_out.token);
466 
467 		if (m == Z_SYNC_FLUSH && !(len & LWS_WRITE_NO_FIN) && !pen &&
468 		    pmdrx->eb_out.len < 4) {
469 			lwsl_wsi_err(wsi, "FAIL want to trim out length %d",
470 					(int)pmdrx->eb_out.len);
471 			assert(0);
472 		}
473 
474 		if (!(len & LWS_WRITE_NO_FIN) &&
475 		    m == Z_SYNC_FLUSH &&
476 		    !pen &&
477 		    pmdrx->eb_out.len >= 4) {
478 			// lwsl_wsi_err(wsi, "Trimming 4 from end of write");
479 			priv->tx.next_out -= 4;
480 			priv->tx.avail_out += 4;
481 			priv->count_tx_between_fin = 0;
482 
483 			assert(priv->tx.next_out[0] == 0x00 &&
484 			       priv->tx.next_out[1] == 0x00 &&
485 			       priv->tx.next_out[2] == 0xff &&
486 			       priv->tx.next_out[3] == 0xff);
487 		}
488 
489 
490 		/*
491 		 * track how much input was used and advance it
492 		 */
493 
494 		pmdrx->eb_in.token = pmdrx->eb_in.token +
495 					((unsigned int)pmdrx->eb_in.len - (unsigned int)priv->tx.avail_in);
496 		pmdrx->eb_in.len = (int)priv->tx.avail_in;
497 
498 		priv->compressed_out = 1;
499 		pmdrx->eb_out.len = lws_ptr_diff(priv->tx.next_out,
500 						 pmdrx->eb_out.token);
501 
502 		lwsl_wsi_ext(wsi, "  TX rewritten with new eb_in len %d, "
503 				"eb_out len %d, deflatePending %d",
504 				pmdrx->eb_in.len, pmdrx->eb_out.len, pen);
505 
506 		if (pmdrx->eb_in.len || pen)
507 			return PMDR_HAS_PENDING;
508 
509 		if (!(len & LWS_WRITE_NO_FIN))
510 			return PMDR_EMPTY_FINAL;
511 
512 		return PMDR_EMPTY_NONFINAL;
513 
514 	case LWS_EXT_CB_PACKET_TX_PRESEND:
515 		if (!priv->compressed_out)
516 			break;
517 		priv->compressed_out = 0;
518 
519 		/*
520 		 * we may have not produced any output for the actual "first"
521 		 * write... in that case, we need to fix up the inappropriate
522 		 * use of CONTINUATION when the first real write does come.
523 		 */
524 		if (priv->tx_first_frame_type & 0xf) {
525 			*pmdrx->eb_in.token = (unsigned char)((((unsigned char)*pmdrx->eb_in.token) & (unsigned char)~0xf) |
526 				((unsigned char)priv->tx_first_frame_type & (unsigned char)0xf));
527 			/*
528 			 * We have now written the "first" fragment, only
529 			 * do that once
530 			 */
531 			priv->tx_first_frame_type = 0;
532 		}
533 
534 		n = *(pmdrx->eb_in.token) & 15;
535 
536 		/* set RSV1, but not on CONTINUATION */
537 		if (n == LWSWSOPC_TEXT_FRAME || n == LWSWSOPC_BINARY_FRAME)
538 			*pmdrx->eb_in.token |= 0x40;
539 
540 		lwsl_wsi_ext(wsi, "PRESEND compressed: ws frame 0x%02X, len %d",
541 			    ((*pmdrx->eb_in.token) & 0xff),
542 			    pmdrx->eb_in.len);
543 
544 		if (((*pmdrx->eb_in.token) & 0x80) &&	/* fin */
545 		    priv->args[PMD_CLIENT_NO_CONTEXT_TAKEOVER]) {
546 			lwsl_wsi_debug(wsi, "PMD_CLIENT_NO_CONTEXT_TAKEOVER");
547 			(void)deflateEnd(&priv->tx);
548 			priv->tx_init = 0;
549 		}
550 
551 		break;
552 
553 	default:
554 		break;
555 	}
556 
557 	return 0;
558 }
559 
560