1 /*-
2  * Copyright (c) 2013-2015 Varnish Software AS
3  * All rights reserved.
4  *
5  * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6  *
7  * SPDX-License-Identifier: BSD-2-Clause
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * Interaction with the linvgz (zlib) library.
31  *
32  * The zlib library pollutes namespace a LOT when you include the "vgz.h"
33  * (aka (zlib.h") file so we contain the damage by vectoring all access
34  * to libz through this source file.
35  *
36  * The API defined by this file, will also insulate the rest of the code,
37  * should we find a better gzip library at a later date.
38  *
39  */
40 
41 #include "config.h"
42 
43 #include <stdlib.h>
44 
45 #include "cache_varnishd.h"
46 #include "cache_filter.h"
47 #include "cache_objhead.h"
48 #include "cache_vgz.h"
49 #include "vend.h"
50 
51 #include "vgz.h"
52 
53 struct vgz {
54 	unsigned		magic;
55 #define VGZ_MAGIC		0x162df0cb
56 	enum {VGZ_GZ,VGZ_UN}	dir;
57 	struct vsl_log		*vsl;
58 	const char		*id;
59 	int			last_i;
60 	enum vgz_flag		flag;
61 
62 	char			*m_buf;
63 	ssize_t			m_sz;
64 	ssize_t			m_len;
65 
66 	intmax_t		bits;
67 
68 	z_stream		vz;
69 };
70 
71 static const char *
vgz_msg(const struct vgz * vg)72 vgz_msg(const struct vgz *vg)
73 {
74 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
75 	return (vg->vz.msg ? vg->vz.msg : "(null)");
76 }
77 
78 /*--------------------------------------------------------------------
79  * Set up a gunzip instance
80  */
81 
82 static struct vgz *
vgz_gunzip(struct vsl_log * vsl,const char * id)83 vgz_gunzip(struct vsl_log *vsl, const char *id)
84 {
85 	struct vgz *vg;
86 
87 	ALLOC_OBJ(vg, VGZ_MAGIC);
88 	AN(vg);
89 	vg->vsl = vsl;
90 	vg->id = id;
91 	vg->dir = VGZ_UN;
92 
93 	/*
94 	 * Max memory usage according to zonf.h:
95 	 *	mem_needed = "a few kb" + (1 << (windowBits))
96 	 * Since we don't control windowBits, we have to assume
97 	 * it is 15, so 34-35KB or so.
98 	 */
99 	assert(Z_OK == inflateInit2(&vg->vz, 31));
100 	return (vg);
101 }
102 
103 static struct vgz *
VGZ_NewGunzip(struct vsl_log * vsl,const char * id)104 VGZ_NewGunzip(struct vsl_log *vsl, const char *id)
105 {
106 	VSC_C_main->n_gunzip++;
107 	return (vgz_gunzip(vsl, id));
108 }
109 
110 static struct vgz *
VGZ_NewTestGunzip(struct vsl_log * vsl,const char * id)111 VGZ_NewTestGunzip(struct vsl_log *vsl, const char *id)
112 {
113 	VSC_C_main->n_test_gunzip++;
114 	return (vgz_gunzip(vsl, id));
115 }
116 
117 struct vgz *
VGZ_NewGzip(struct vsl_log * vsl,const char * id)118 VGZ_NewGzip(struct vsl_log *vsl, const char *id)
119 {
120 	struct vgz *vg;
121 	int i;
122 
123 	VSC_C_main->n_gzip++;
124 	ALLOC_OBJ(vg, VGZ_MAGIC);
125 	AN(vg);
126 	vg->vsl = vsl;
127 	vg->id = id;
128 	vg->dir = VGZ_GZ;
129 
130 	/*
131 	 * From zconf.h:
132 	 *
133 	 *	mem_needed = "a few kb"
134 	 *		+ (1 << (windowBits+2))
135 	 *		+  (1 << (memLevel+9))
136 	 *
137 	 * windowBits [8..15] (-> 1K..128K)
138 	 * memLevel [1..9] (-> 1K->256K)
139 	 */
140 	i = deflateInit2(&vg->vz,
141 	    cache_param->gzip_level,		/* Level */
142 	    Z_DEFLATED,				/* Method */
143 	    16 + 15,				/* Window bits (16=gzip) */
144 	    cache_param->gzip_memlevel,		/* memLevel */
145 	    Z_DEFAULT_STRATEGY);
146 	assert(Z_OK == i);
147 	return (vg);
148 }
149 
150 /*--------------------------------------------------------------------
151  */
152 
153 static int
vgz_getmbuf(struct vgz * vg)154 vgz_getmbuf(struct vgz *vg)
155 {
156 
157 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
158 	AZ(vg->m_sz);
159 	AZ(vg->m_len);
160 	AZ(vg->m_buf);
161 
162 	vg->m_sz = cache_param->gzip_buffer;
163 	vg->m_buf = malloc(vg->m_sz);
164 	if (vg->m_buf == NULL) {
165 		vg->m_sz = 0;
166 		return (-1);
167 	}
168 	return (0);
169 }
170 
171 /*--------------------------------------------------------------------*/
172 
173 void
VGZ_Ibuf(struct vgz * vg,const void * ptr,ssize_t len)174 VGZ_Ibuf(struct vgz *vg, const void *ptr, ssize_t len)
175 {
176 
177 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
178 
179 	AZ(vg->vz.avail_in);
180 	vg->vz.next_in = TRUST_ME(ptr);
181 	vg->vz.avail_in = len;
182 }
183 
184 int
VGZ_IbufEmpty(const struct vgz * vg)185 VGZ_IbufEmpty(const struct vgz *vg)
186 {
187 
188 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
189 	return (vg->vz.avail_in == 0);
190 }
191 
192 /*--------------------------------------------------------------------*/
193 
194 void
VGZ_Obuf(struct vgz * vg,void * ptr,ssize_t len)195 VGZ_Obuf(struct vgz *vg, void *ptr, ssize_t len)
196 {
197 
198 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
199 
200 	vg->vz.next_out = TRUST_ME(ptr);
201 	vg->vz.avail_out = len;
202 }
203 
204 int
VGZ_ObufFull(const struct vgz * vg)205 VGZ_ObufFull(const struct vgz *vg)
206 {
207 
208 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
209 	return (vg->vz.avail_out == 0);
210 }
211 
212 /*--------------------------------------------------------------------*/
213 
214 static enum vgzret_e
VGZ_Gunzip(struct vgz * vg,const void ** pptr,ssize_t * plen)215 VGZ_Gunzip(struct vgz *vg, const void **pptr, ssize_t *plen)
216 {
217 	int i;
218 	ssize_t l;
219 	const uint8_t *before;
220 
221 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
222 
223 	*pptr = NULL;
224 	*plen = 0;
225 	AN(vg->vz.next_out);
226 	AN(vg->vz.avail_out);
227 	before = vg->vz.next_out;
228 	i = inflate(&vg->vz, 0);
229 	if (i == Z_OK || i == Z_STREAM_END) {
230 		*pptr = before;
231 		l = (const uint8_t *)vg->vz.next_out - before;
232 		*plen = l;
233 	}
234 	vg->last_i = i;
235 	if (i == Z_OK)
236 		return (VGZ_OK);
237 	if (i == Z_STREAM_END)
238 		return (VGZ_END);
239 	if (i == Z_BUF_ERROR)
240 		return (VGZ_STUCK);
241 	VSLb(vg->vsl, SLT_Gzip, "Gunzip error: %d (%s)", i, vgz_msg(vg));
242 	return (VGZ_ERROR);
243 }
244 
245 /*--------------------------------------------------------------------*/
246 
247 enum vgzret_e
VGZ_Gzip(struct vgz * vg,const void ** pptr,ssize_t * plen,enum vgz_flag flags)248 VGZ_Gzip(struct vgz *vg, const void **pptr, ssize_t *plen, enum vgz_flag flags)
249 {
250 	int i;
251 	int zflg;
252 	ssize_t l;
253 	const uint8_t *before;
254 
255 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
256 
257 	*pptr = NULL;
258 	*plen = 0;
259 	AN(vg->vz.next_out);
260 	AN(vg->vz.avail_out);
261 	before = vg->vz.next_out;
262 	switch (flags) {
263 	case VGZ_NORMAL:	zflg = Z_NO_FLUSH; break;
264 	case VGZ_ALIGN:		zflg = Z_SYNC_FLUSH; break;
265 	case VGZ_RESET:		zflg = Z_FULL_FLUSH; break;
266 	case VGZ_FINISH:	zflg = Z_FINISH; break;
267 	default:		INCOMPL();
268 	}
269 	i = deflate(&vg->vz, zflg);
270 	if (i == Z_OK || i == Z_STREAM_END) {
271 		*pptr = before;
272 		l = (const uint8_t *)vg->vz.next_out - before;
273 		*plen = l;
274 	}
275 	vg->last_i = i;
276 	if (i == Z_OK)
277 		return (VGZ_OK);
278 	if (i == Z_STREAM_END)
279 		return (VGZ_END);
280 	if (i == Z_BUF_ERROR)
281 		return (VGZ_STUCK);
282 	VSLb(vg->vsl, SLT_Gzip, "Gzip error: %d (%s)", i, vgz_msg(vg));
283 	return (VGZ_ERROR);
284 }
285 
286 /*--------------------------------------------------------------------
287  * VDP for gunzip'ing
288  */
289 
v_matchproto_(vdp_init_f)290 static int v_matchproto_(vdp_init_f)
291 vdp_gunzip_init(struct vdp_ctx *vdp, void **priv, struct objcore *oc)
292 {
293 	struct vgz *vg;
294 	struct boc *boc;
295 	struct req *req;
296 	enum boc_state_e bos;
297 	const char *p;
298 	ssize_t dl;
299 	uint64_t u;
300 
301 	CHECK_OBJ_NOTNULL(vdp, VDP_CTX_MAGIC);
302 	CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC);
303 	req = vdp->req;
304 	CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
305 
306 	vg = VGZ_NewGunzip(req->vsl, "U D -");
307 	AN(vg);
308 	if (vgz_getmbuf(vg)) {
309 		(void)VGZ_Destroy(&vg);
310 		return (-1);
311 	}
312 
313 	VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
314 	*priv = vg;
315 
316 	http_Unset(req->resp, H_Content_Encoding);
317 
318 	req->resp_len = -1;
319 
320 	if (oc == NULL)
321 		return (0);
322 
323 	boc = HSH_RefBoc(oc);
324 	if (boc != NULL) {
325 		CHECK_OBJ(boc, BOC_MAGIC);
326 		bos = boc->state;
327 		HSH_DerefBoc(req->wrk, oc);
328 		if (bos < BOS_FINISHED)
329 			return (0); /* OA_GZIPBITS is not stable yet */
330 	}
331 
332 	p = ObjGetAttr(req->wrk, oc, OA_GZIPBITS, &dl);
333 	if (p != NULL && dl == 32) {
334 		u = vbe64dec(p + 24);
335 		if (u != 0)
336 			req->resp_len = u;
337 	}
338 	return (0);
339 }
340 
v_matchproto_(vdp_fini_f)341 static int v_matchproto_(vdp_fini_f)
342 vdp_gunzip_fini(struct vdp_ctx *vdc, void **priv)
343 {
344 	struct vgz *vg;
345 
346 	(void)vdc;
347 	CAST_OBJ_NOTNULL(vg, *priv, VGZ_MAGIC);
348 	AN(vg->m_buf);
349 	(void)VGZ_Destroy(&vg);
350 	*priv = NULL;
351 	return (0);
352 }
353 
v_matchproto_(vdp_bytes_f)354 static int v_matchproto_(vdp_bytes_f)
355 vdp_gunzip_bytes(struct vdp_ctx *vdx, enum vdp_action act, void **priv,
356     const void *ptr, ssize_t len)
357 {
358 	enum vgzret_e vr;
359 	ssize_t dl;
360 	const void *dp;
361 	struct worker *wrk;
362 	struct vgz *vg;
363 
364 	CHECK_OBJ_NOTNULL(vdx, VDP_CTX_MAGIC);
365 	wrk = vdx->wrk;
366 	CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
367 	(void)act;
368 
369 	CAST_OBJ_NOTNULL(vg, *priv, VGZ_MAGIC);
370 	AN(vg->m_buf);
371 
372 	if (len == 0)
373 		return (0);
374 
375 	VGZ_Ibuf(vg, ptr, len);
376 	do {
377 		vr = VGZ_Gunzip(vg, &dp, &dl);
378 		if (vr == VGZ_END && !VGZ_IbufEmpty(vg)) {
379 			VSLb(vg->vsl, SLT_Gzip, "G(un)zip error: %d (%s)",
380 			     vr, "junk after VGZ_END");
381 			return (-1);
382 		}
383 		vg->m_len += dl;
384 		if (vr < VGZ_OK)
385 			return (-1);
386 		if (vg->m_len == vg->m_sz || vr != VGZ_OK) {
387 			if (VDP_bytes(vdx, vr == VGZ_END ? VDP_END : VDP_FLUSH,
388 			    vg->m_buf, vg->m_len))
389 				return (vdx->retval);
390 			vg->m_len = 0;
391 			VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
392 		}
393 	} while (!VGZ_IbufEmpty(vg));
394 	assert(vr == VGZ_STUCK || vr == VGZ_OK || vr == VGZ_END);
395 	return (0);
396 }
397 
398 const struct vdp VDP_gunzip = {
399 	.name =		"gunzip",
400 	.init =		vdp_gunzip_init,
401 	.bytes =	vdp_gunzip_bytes,
402 	.fini =		vdp_gunzip_fini,
403 };
404 
405 /*--------------------------------------------------------------------*/
406 
407 void
VGZ_UpdateObj(const struct vfp_ctx * vc,struct vgz * vg,enum vgz_ua_e e)408 VGZ_UpdateObj(const struct vfp_ctx *vc, struct vgz *vg, enum vgz_ua_e e)
409 {
410 	char *p;
411 	intmax_t ii;
412 
413 	CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
414 	ii = vg->vz.start_bit + vg->vz.last_bit + vg->vz.stop_bit;
415 	if (e == VUA_UPDATE && ii == vg->bits)
416 		return;
417 	vg->bits = ii;
418 	p = ObjSetAttr(vc->wrk, vc->oc, OA_GZIPBITS, 32, NULL);
419 	AN(p);
420 	vbe64enc(p, vg->vz.start_bit);
421 	vbe64enc(p + 8, vg->vz.last_bit);
422 	vbe64enc(p + 16, vg->vz.stop_bit);
423 	if (e == VUA_END_GZIP)
424 		vbe64enc(p + 24, vg->vz.total_in);
425 	if (e == VUA_END_GUNZIP)
426 		vbe64enc(p + 24, vg->vz.total_out);
427 }
428 
429 /*--------------------------------------------------------------------
430  */
431 
432 enum vgzret_e
VGZ_Destroy(struct vgz ** vgp)433 VGZ_Destroy(struct vgz **vgp)
434 {
435 	struct vgz *vg;
436 	enum vgzret_e vr;
437 	int i;
438 
439 	TAKE_OBJ_NOTNULL(vg, vgp, VGZ_MAGIC);
440 	AN(vg->id);
441 	VSLb(vg->vsl, SLT_Gzip, "%s %jd %jd %jd %jd %jd",
442 	    vg->id,
443 	    (intmax_t)vg->vz.total_in,
444 	    (intmax_t)vg->vz.total_out,
445 	    (intmax_t)vg->vz.start_bit,
446 	    (intmax_t)vg->vz.last_bit,
447 	    (intmax_t)vg->vz.stop_bit);
448 	if (vg->dir == VGZ_GZ)
449 		i = deflateEnd(&vg->vz);
450 	else
451 		i = inflateEnd(&vg->vz);
452 	if (vg->last_i == Z_STREAM_END && i == Z_OK)
453 		i = Z_STREAM_END;
454 	if (vg->m_buf)
455 		free(vg->m_buf);
456 	if (i == Z_OK)
457 		vr = VGZ_OK;
458 	else if (i == Z_STREAM_END)
459 		vr = VGZ_END;
460 	else if (i == Z_BUF_ERROR)
461 		vr = VGZ_STUCK;
462 	else {
463 		VSLb(vg->vsl, SLT_Gzip, "G(un)zip error: %d (%s)",
464 		    i, vgz_msg(vg));
465 		vr = VGZ_ERROR;
466 	}
467 	FREE_OBJ(vg);
468 	return (vr);
469 }
470 
471 /*--------------------------------------------------------------------*/
472 
v_matchproto_(vfp_init_f)473 static enum vfp_status v_matchproto_(vfp_init_f)
474 vfp_gzip_init(struct vfp_ctx *vc, struct vfp_entry *vfe)
475 {
476 	struct vgz *vg;
477 
478 	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
479 	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
480 
481 	/*
482 	 * G(un)zip makes no sence on partial responses, but since
483 	 * it is an pure 1:1 transform, we can just ignore it.
484 	 */
485 	if (http_GetStatus(vc->resp) == 206)
486 		return (VFP_NULL);
487 
488 	if (vfe->vfp == &VFP_gzip) {
489 		if (http_GetHdr(vc->resp, H_Content_Encoding, NULL))
490 			return (VFP_NULL);
491 		vg = VGZ_NewGzip(vc->wrk->vsl, vfe->vfp->priv1);
492 		vc->obj_flags |= OF_GZIPED | OF_CHGCE;
493 	} else {
494 		if (!http_HdrIs(vc->resp, H_Content_Encoding, "gzip"))
495 			return (VFP_NULL);
496 		if (vfe->vfp == &VFP_gunzip) {
497 			vg = VGZ_NewGunzip(vc->wrk->vsl, vfe->vfp->priv1);
498 			vc->obj_flags &= ~OF_GZIPED;
499 			vc->obj_flags |= OF_CHGCE;
500 		} else {
501 			vg = VGZ_NewTestGunzip(vc->wrk->vsl, vfe->vfp->priv1);
502 			vc->obj_flags |= OF_GZIPED;
503 		}
504 	}
505 	AN(vg);
506 	vfe->priv1 = vg;
507 	if (vgz_getmbuf(vg))
508 		return (VFP_ERROR);
509 	VGZ_Ibuf(vg, vg->m_buf, 0);
510 	AZ(vg->m_len);
511 
512 	if (vfe->vfp == &VFP_gunzip || vfe->vfp == &VFP_gzip) {
513 		http_Unset(vc->resp, H_Content_Encoding);
514 		http_Unset(vc->resp, H_Content_Length);
515 		RFC2616_Weaken_Etag(vc->resp);
516 	}
517 
518 	if (vfe->vfp == &VFP_gzip)
519 		http_SetHeader(vc->resp, "Content-Encoding: gzip");
520 
521 	if (vfe->vfp == &VFP_gzip || vfe->vfp == &VFP_testgunzip)
522 		RFC2616_Vary_AE(vc->resp);
523 
524 	return (VFP_OK);
525 }
526 
527 /*--------------------------------------------------------------------
528  * VFP_GUNZIP
529  *
530  * A VFP for gunzip'ing an object as we receive it from the backend
531  */
532 
v_matchproto_(vfp_pull_f)533 static enum vfp_status v_matchproto_(vfp_pull_f)
534 vfp_gunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
535     ssize_t *lp)
536 {
537 	ssize_t l;
538 	struct vgz *vg;
539 	enum vgzret_e vr = VGZ_ERROR;
540 	const void *dp;
541 	ssize_t dl;
542 	enum vfp_status vp = VFP_OK;
543 
544 	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
545 	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
546 	CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
547 	AN(p);
548 	AN(lp);
549 	l = *lp;
550 	*lp = 0;
551 	VGZ_Obuf(vg, p, l);
552 	do {
553 		if (VGZ_IbufEmpty(vg)) {
554 			l = vg->m_sz;
555 			vp = VFP_Suck(vc, vg->m_buf, &l);
556 			if (vp == VFP_ERROR)
557 				return (vp);
558 			VGZ_Ibuf(vg, vg->m_buf, l);
559 		}
560 		if (!VGZ_IbufEmpty(vg) || vp == VFP_END) {
561 			vr = VGZ_Gunzip(vg, &dp, &dl);
562 			if (vr == VGZ_END && !VGZ_IbufEmpty(vg))
563 				return (VFP_Error(vc, "Junk after gzip data"));
564 			if (vr < VGZ_OK)
565 				return (VFP_Error(vc,
566 				    "Invalid Gzip data: %s", vgz_msg(vg)));
567 			if (dl > 0) {
568 				*lp = dl;
569 				assert(dp == p);
570 				return (VFP_OK);
571 			}
572 		}
573 		AN(VGZ_IbufEmpty(vg));
574 	} while (vp == VFP_OK);
575 	if (vr != VGZ_END)
576 		return (VFP_Error(vc, "Gunzip error at the very end"));
577 	return (vp);
578 }
579 
580 
581 /*--------------------------------------------------------------------
582  * VFP_GZIP
583  *
584  * A VFP for gzip'ing an object as we receive it from the backend
585  */
586 
v_matchproto_(vfp_pull_f)587 static enum vfp_status v_matchproto_(vfp_pull_f)
588 vfp_gzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
589     ssize_t *lp)
590 {
591 	ssize_t l;
592 	struct vgz *vg;
593 	enum vgzret_e vr = VGZ_ERROR;
594 	const void *dp;
595 	ssize_t dl;
596 	enum vfp_status vp = VFP_ERROR;
597 
598 	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
599 	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
600 	CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
601 	AN(p);
602 	AN(lp);
603 	l = *lp;
604 	*lp = 0;
605 	VGZ_Obuf(vg, p, l);
606 	do {
607 		if (VGZ_IbufEmpty(vg)) {
608 			l = vg->m_sz;
609 			vp = VFP_Suck(vc, vg->m_buf, &l);
610 			if (vp == VFP_ERROR)
611 				break;
612 			if (vp == VFP_END)
613 				vg->flag = VGZ_FINISH;
614 			VGZ_Ibuf(vg, vg->m_buf, l);
615 		}
616 		if (!VGZ_IbufEmpty(vg) || vg->flag == VGZ_FINISH) {
617 			vr = VGZ_Gzip(vg, &dp, &dl, vg->flag);
618 			if (vr < VGZ_OK)
619 				return (VFP_Error(vc, "Gzip failed"));
620 			if (dl > 0) {
621 				VGZ_UpdateObj(vc, vg, VUA_UPDATE);
622 				*lp = dl;
623 				assert(dp == p);
624 				return (VFP_OK);
625 			}
626 		}
627 		AN(VGZ_IbufEmpty(vg));
628 	} while (vg->flag != VGZ_FINISH);
629 
630 	if (vr != VGZ_END)
631 		return (VFP_Error(vc, "Gzip failed"));
632 	VGZ_UpdateObj(vc, vg, VUA_END_GZIP);
633 	return (VFP_END);
634 }
635 
636 /*--------------------------------------------------------------------
637  * VFP_TESTGZIP
638  *
639  * A VFP for testing that received gzip data is valid, and for
640  * collecting the magic bits while we're at it.
641  */
642 
v_matchproto_(vfp_pull_f)643 static enum vfp_status v_matchproto_(vfp_pull_f)
644 vfp_testgunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
645     ssize_t *lp)
646 {
647 	struct vgz *vg;
648 	enum vgzret_e vr = VGZ_ERROR;
649 	const void *dp;
650 	ssize_t dl;
651 	enum vfp_status vp;
652 
653 	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
654 	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
655 	CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
656 	AN(p);
657 	AN(lp);
658 	vp = VFP_Suck(vc, p, lp);
659 	if (vp == VFP_ERROR)
660 		return (vp);
661 	if (*lp > 0 || vp == VFP_END) {
662 		VGZ_Ibuf(vg, p, *lp);
663 		do {
664 			VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
665 			vr = VGZ_Gunzip(vg, &dp, &dl);
666 			if (vr == VGZ_END && !VGZ_IbufEmpty(vg))
667 				return (VFP_Error(vc, "Junk after gzip data"));
668 			if (vr < VGZ_OK)
669 				return (VFP_Error(vc,
670 				    "Invalid Gzip data: %s", vgz_msg(vg)));
671 		} while (!VGZ_IbufEmpty(vg));
672 	}
673 	VGZ_UpdateObj(vc, vg, VUA_UPDATE);
674 	if (vp == VFP_END) {
675 		if (vr != VGZ_END)
676 			return (VFP_Error(vc, "tGunzip failed"));
677 		VGZ_UpdateObj(vc, vg, VUA_END_GUNZIP);
678 	}
679 	return (vp);
680 }
681 
682 /*--------------------------------------------------------------------*/
683 
v_matchproto_(vfp_fini_f)684 static void v_matchproto_(vfp_fini_f)
685 vfp_gzip_fini(struct vfp_ctx *vc, struct vfp_entry *vfe)
686 {
687 	struct vgz *vg;
688 
689 	CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
690 	CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
691 
692 	if (vfe->priv1 != NULL) {
693 		CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
694 		vfe->priv1 = NULL;
695 		(void)VGZ_Destroy(&vg);
696 	}
697 }
698 
699 /*--------------------------------------------------------------------*/
700 
701 const struct vfp VFP_gunzip = {
702 	.name = "gunzip",
703 	.init = vfp_gzip_init,
704 	.pull = vfp_gunzip_pull,
705 	.fini = vfp_gzip_fini,
706 	.priv1 = "U F -",
707 };
708 
709 const struct vfp VFP_gzip = {
710 	.name = "gzip",
711 	.init = vfp_gzip_init,
712 	.pull = vfp_gzip_pull,
713 	.fini = vfp_gzip_fini,
714 	.priv1 = "G F -",
715 };
716 
717 const struct vfp VFP_testgunzip = {
718 	.name = "testgunzip",
719 	.init = vfp_gzip_init,
720 	.pull = vfp_testgunzip_pull,
721 	.fini = vfp_gzip_fini,
722 	.priv1 = "u F -",
723 };
724