X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/putty/blobdiff_plain/32874aeac8dacbca26663777b39a79efc5d8dc4b..8d90b8b27bf086da6245030459ab3e5977313eb0:/sshzlib.c diff --git a/sshzlib.c b/sshzlib.c index 01249535..05fa5772 100644 --- a/sshzlib.c +++ b/sshzlib.c @@ -38,14 +38,33 @@ */ #include +#include #include -/* FIXME */ -#include -#include -#include "putty.h" +#ifdef ZLIB_STANDALONE +/* + * This module also makes a handy zlib decoding tool for when + * you're picking apart Zip files or PDFs or PNGs. If you compile + * it with ZLIB_STANDALONE defined, it builds on its own and + * becomes a command-line utility. + * + * Therefore, here I provide a self-contained implementation of the + * macros required from the rest of the PuTTY sources. + */ +#define snew(type) ( (type *) malloc(sizeof(type)) ) +#define snewn(n, type) ( (type *) malloc((n) * sizeof(type)) ) +#define sresize(x, n, type) ( (type *) realloc((x), (n) * sizeof(type)) ) +#define sfree(x) ( free((x)) ) + +#else #include "ssh.h" +#endif + +#ifndef FALSE +#define FALSE 0 +#define TRUE (!FALSE) +#endif /* ---------------------------------------------------------------------- * Basic LZ77 code. This bit is designed modularly, so it could be @@ -126,7 +145,7 @@ static int lz77_init(struct LZ77Context *ctx) struct LZ77InternalContext *st; int i; - st = (struct LZ77InternalContext *) smalloc(sizeof(*st)); + st = snew(struct LZ77InternalContext); if (!st) return 0; @@ -206,7 +225,9 @@ static void lz77_compress(struct LZ77Context *ctx, } st->npending -= i; + defermatch.distance = 0; /* appease compiler */ defermatch.len = 0; + deferchr = '\0'; while (len > 0) { /* Don't even look for a match, if we're not compressing. */ @@ -336,8 +357,6 @@ static void lz77_compress(struct LZ77Context *ctx, * having to transmit the trees. */ -static struct LZ77Context ectx; - struct Outbuf { unsigned char *outbuf; int outlen, outsize; @@ -355,7 +374,7 @@ static void outbits(struct Outbuf *out, unsigned long bits, int nbits) while (out->noutbits >= 8) { if (out->outlen >= out->outsize) { out->outsize = out->outlen + 64; - out->outbuf = srealloc(out->outbuf, out->outsize); + out->outbuf = sresize(out->outbuf, out->outsize, unsigned char); } out->outbuf[out->outlen++] = (unsigned char) (out->outbits & 0xFF); out->outbits >>= 8; @@ -519,7 +538,8 @@ static void zlib_match(struct LZ77Context *ectx, int distance, int len) */ i = -1; j = sizeof(lencodes) / sizeof(*lencodes); - while (j - i >= 2) { + while (1) { + assert(j - i >= 2); k = (j + i) / 2; if (thislen < lencodes[k].min) j = k; @@ -554,7 +574,8 @@ static void zlib_match(struct LZ77Context *ectx, int distance, int len) */ i = -1; j = sizeof(distcodes) / sizeof(*distcodes); - while (j - i >= 2) { + while (1) { + assert(j - i >= 2); k = (j + i) / 2; if (distance < distcodes[k].min) j = k; @@ -579,21 +600,30 @@ static void zlib_match(struct LZ77Context *ectx, int distance, int len) } } -void zlib_compress_init(void) +void *zlib_compress_init(void) { struct Outbuf *out; + struct LZ77Context *ectx = snew(struct LZ77Context); - lz77_init(&ectx); - ectx.literal = zlib_literal; - ectx.match = zlib_match; + lz77_init(ectx); + ectx->literal = zlib_literal; + ectx->match = zlib_match; - out = smalloc(sizeof(struct Outbuf)); + out = snew(struct Outbuf); out->outbits = out->noutbits = 0; out->firstblock = 1; out->comp_disabled = FALSE; - ectx.userdata = out; + ectx->userdata = out; - logevent("Initialised zlib (RFC1950) compression"); + return ectx; +} + +void zlib_compress_cleanup(void *handle) +{ + struct LZ77Context *ectx = (struct LZ77Context *)handle; + sfree(ectx->userdata); + sfree(ectx->ictx); + sfree(ectx); } /* @@ -602,9 +632,10 @@ void zlib_compress_init(void) * length adjustment (which is only valid for packets < 65536 * bytes, but that seems reasonable enough). */ -int zlib_disable_compression(void) +static int zlib_disable_compression(void *handle) { - struct Outbuf *out = (struct Outbuf *) ectx.userdata; + struct LZ77Context *ectx = (struct LZ77Context *)handle; + struct Outbuf *out = (struct Outbuf *) ectx->userdata; int n; out->comp_disabled = TRUE; @@ -637,10 +668,11 @@ int zlib_disable_compression(void) return n; } -int zlib_compress_block(unsigned char *block, int len, +int zlib_compress_block(void *handle, unsigned char *block, int len, unsigned char **outblock, int *outlen) { - struct Outbuf *out = (struct Outbuf *) ectx.userdata; + struct LZ77Context *ectx = (struct LZ77Context *)handle; + struct Outbuf *out = (struct Outbuf *) ectx->userdata; int in_block; out->outbuf = NULL; @@ -656,7 +688,8 @@ int zlib_compress_block(unsigned char *block, int len, out->firstblock = 0; in_block = FALSE; - } + } else + in_block = TRUE; if (out->comp_disabled) { if (in_block) @@ -667,9 +700,9 @@ int zlib_compress_block(unsigned char *block, int len, /* * Start a Deflate (RFC1951) uncompressed block. We - * transmit a zero bit (BFINAL=0), followed by a zero - * bit and a one bit (BTYPE=00). Of course these are in - * the wrong order (00 0). + * transmit a zero bit (BFINAL=0), followed by two more + * zero bits (BTYPE=00). Of course these are in the + * wrong order (00 0), not that it matters. */ outbits(out, 0, 3); @@ -698,7 +731,7 @@ int zlib_compress_block(unsigned char *block, int len, * zlib_literal which will spot out->comp_disabled and * emit in the uncompressed format. */ - lz77_compress(&ectx, block, blen, FALSE); + lz77_compress(ectx, block, blen, FALSE); len -= blen; block += blen; @@ -718,7 +751,7 @@ int zlib_compress_block(unsigned char *block, int len, /* * Do the compression. */ - lz77_compress(&ectx, block, len, TRUE); + lz77_compress(ectx, block, len, TRUE); /* * End the block (by transmitting code 256, which is @@ -795,11 +828,11 @@ static struct zlib_table *zlib_mkonetab(int *codes, unsigned char *lengths, int nsyms, int pfx, int pfxbits, int bits) { - struct zlib_table *tab = smalloc(sizeof(struct zlib_table)); + struct zlib_table *tab = snew(struct zlib_table); int pfxmask = (1 << pfxbits) - 1; int nbits, i, j, code; - tab->table = smalloc((1 << bits) * sizeof(struct zlib_tableentry)); + tab->table = snewn(1 << bits, struct zlib_tableentry); tab->mask = (1 << bits) - 1; for (code = 0; code <= tab->mask; code++) { @@ -906,7 +939,7 @@ static int zlib_freetable(struct zlib_table **ztab) return (0); } -static struct zlib_decompress_ctx { +struct zlib_decompress_ctx { struct zlib_table *staticlentable, *staticdisttable; struct zlib_table *currlentable, *currdisttable, *lenlentable; enum { @@ -926,26 +959,45 @@ static struct zlib_decompress_ctx { int winpos; unsigned char *outblk; int outlen, outsize; -} dctx; +}; -void zlib_decompress_init(void) +void *zlib_decompress_init(void) { + struct zlib_decompress_ctx *dctx = snew(struct zlib_decompress_ctx); unsigned char lengths[288]; + memset(lengths, 8, 144); memset(lengths + 144, 9, 256 - 144); memset(lengths + 256, 7, 280 - 256); memset(lengths + 280, 8, 288 - 280); - dctx.staticlentable = zlib_mktable(lengths, 288); + dctx->staticlentable = zlib_mktable(lengths, 288); memset(lengths, 5, 32); - dctx.staticdisttable = zlib_mktable(lengths, 32); - dctx.state = START; /* even before header */ - dctx.currlentable = dctx.currdisttable = dctx.lenlentable = NULL; - dctx.bits = 0; - dctx.nbits = 0; - logevent("Initialised zlib (RFC1950) decompression"); + dctx->staticdisttable = zlib_mktable(lengths, 32); + dctx->state = START; /* even before header */ + dctx->currlentable = dctx->currdisttable = dctx->lenlentable = NULL; + dctx->bits = 0; + dctx->nbits = 0; + dctx->winpos = 0; + + return dctx; } -int zlib_huflookup(unsigned long *bitsp, int *nbitsp, +void zlib_decompress_cleanup(void *handle) +{ + struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle; + + if (dctx->currlentable && dctx->currlentable != dctx->staticlentable) + zlib_freetable(&dctx->currlentable); + if (dctx->currdisttable && dctx->currdisttable != dctx->staticdisttable) + zlib_freetable(&dctx->currdisttable); + if (dctx->lenlentable) + zlib_freetable(&dctx->lenlentable); + zlib_freetable(&dctx->staticlentable); + zlib_freetable(&dctx->staticdisttable); + sfree(dctx); +} + +static int zlib_huflookup(unsigned long *bitsp, int *nbitsp, struct zlib_table *tab) { unsigned long bits = *bitsp; @@ -964,65 +1016,102 @@ int zlib_huflookup(unsigned long *bitsp, int *nbitsp, *nbitsp = nbits; return ent->code; } + + if (!tab) { + /* + * There was a missing entry in the table, presumably + * due to an invalid Huffman table description, and the + * subsequent data has attempted to use the missing + * entry. Return a decoding failure. + */ + return -2; + } } } -static void zlib_emit_char(int c) +static void zlib_emit_char(struct zlib_decompress_ctx *dctx, int c) { - dctx.window[dctx.winpos] = c; - dctx.winpos = (dctx.winpos + 1) & (WINSIZE - 1); - if (dctx.outlen >= dctx.outsize) { - dctx.outsize = dctx.outlen + 512; - dctx.outblk = srealloc(dctx.outblk, dctx.outsize); + dctx->window[dctx->winpos] = c; + dctx->winpos = (dctx->winpos + 1) & (WINSIZE - 1); + if (dctx->outlen >= dctx->outsize) { + dctx->outsize = dctx->outlen + 512; + dctx->outblk = sresize(dctx->outblk, dctx->outsize, unsigned char); } - dctx.outblk[dctx.outlen++] = c; + dctx->outblk[dctx->outlen++] = c; } -#define EATBITS(n) ( dctx.nbits -= (n), dctx.bits >>= (n) ) +#define EATBITS(n) ( dctx->nbits -= (n), dctx->bits >>= (n) ) -int zlib_decompress_block(unsigned char *block, int len, +int zlib_decompress_block(void *handle, unsigned char *block, int len, unsigned char **outblock, int *outlen) { + struct zlib_decompress_ctx *dctx = (struct zlib_decompress_ctx *)handle; const coderecord *rec; - int code, blktype, rep, dist, nlen; + int code, blktype, rep, dist, nlen, header; static const unsigned char lenlenmap[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - dctx.outblk = NULL; - dctx.outsize = dctx.outlen = 0; + dctx->outblk = snewn(256, unsigned char); + dctx->outsize = 256; + dctx->outlen = 0; - while (len > 0 || dctx.nbits > 0) { - while (dctx.nbits < 24 && len > 0) { - dctx.bits |= (*block++) << dctx.nbits; - dctx.nbits += 8; + while (len > 0 || dctx->nbits > 0) { + while (dctx->nbits < 24 && len > 0) { + dctx->bits |= (*block++) << dctx->nbits; + dctx->nbits += 8; len--; } - switch (dctx.state) { + switch (dctx->state) { case START: - /* Expect 16-bit zlib header, which we'll dishonourably ignore. */ - if (dctx.nbits < 16) + /* Expect 16-bit zlib header. */ + if (dctx->nbits < 16) goto finished; /* done all we can */ - EATBITS(16); - dctx.state = OUTSIDEBLK; + + /* + * The header is stored as a big-endian 16-bit integer, + * in contrast to the general little-endian policy in + * the rest of the format :-( + */ + header = (((dctx->bits & 0xFF00) >> 8) | + ((dctx->bits & 0x00FF) << 8)); + EATBITS(16); + + /* + * Check the header: + * + * - bits 8-11 should be 1000 (Deflate/RFC1951) + * - bits 12-15 should be at most 0111 (window size) + * - bit 5 should be zero (no dictionary present) + * - we don't care about bits 6-7 (compression rate) + * - bits 0-4 should be set up to make the whole thing + * a multiple of 31 (checksum). + */ + if ((header & 0x0F00) != 0x0800 || + (header & 0xF000) > 0x7000 || + (header & 0x0020) != 0x0000 || + (header % 31) != 0) + goto decode_error; + + dctx->state = OUTSIDEBLK; break; case OUTSIDEBLK: /* Expect 3-bit block header. */ - if (dctx.nbits < 3) + if (dctx->nbits < 3) goto finished; /* done all we can */ EATBITS(1); - blktype = dctx.bits & 3; + blktype = dctx->bits & 3; EATBITS(2); if (blktype == 0) { - int to_eat = dctx.nbits & 7; - dctx.state = UNCOMP_LEN; + int to_eat = dctx->nbits & 7; + dctx->state = UNCOMP_LEN; EATBITS(to_eat); /* align to byte boundary */ } else if (blktype == 1) { - dctx.currlentable = dctx.staticlentable; - dctx.currdisttable = dctx.staticdisttable; - dctx.state = INBLK; + dctx->currlentable = dctx->staticlentable; + dctx->currdisttable = dctx->staticdisttable; + dctx->state = INBLK; } else if (blktype == 2) { - dctx.state = TREES_HDR; + dctx->state = TREES_HDR; } break; case TREES_HDR: @@ -1030,125 +1119,136 @@ int zlib_decompress_block(unsigned char *block, int len, * Dynamic block header. Five bits of HLIT, five of * HDIST, four of HCLEN. */ - if (dctx.nbits < 5 + 5 + 4) + if (dctx->nbits < 5 + 5 + 4) goto finished; /* done all we can */ - dctx.hlit = 257 + (dctx.bits & 31); + dctx->hlit = 257 + (dctx->bits & 31); EATBITS(5); - dctx.hdist = 1 + (dctx.bits & 31); + dctx->hdist = 1 + (dctx->bits & 31); EATBITS(5); - dctx.hclen = 4 + (dctx.bits & 15); + dctx->hclen = 4 + (dctx->bits & 15); EATBITS(4); - dctx.lenptr = 0; - dctx.state = TREES_LENLEN; - memset(dctx.lenlen, 0, sizeof(dctx.lenlen)); + dctx->lenptr = 0; + dctx->state = TREES_LENLEN; + memset(dctx->lenlen, 0, sizeof(dctx->lenlen)); break; case TREES_LENLEN: - if (dctx.nbits < 3) + if (dctx->nbits < 3) goto finished; - while (dctx.lenptr < dctx.hclen && dctx.nbits >= 3) { - dctx.lenlen[lenlenmap[dctx.lenptr++]] = - (unsigned char) (dctx.bits & 7); + while (dctx->lenptr < dctx->hclen && dctx->nbits >= 3) { + dctx->lenlen[lenlenmap[dctx->lenptr++]] = + (unsigned char) (dctx->bits & 7); EATBITS(3); } - if (dctx.lenptr == dctx.hclen) { - dctx.lenlentable = zlib_mktable(dctx.lenlen, 19); - dctx.state = TREES_LEN; - dctx.lenptr = 0; + if (dctx->lenptr == dctx->hclen) { + dctx->lenlentable = zlib_mktable(dctx->lenlen, 19); + dctx->state = TREES_LEN; + dctx->lenptr = 0; } break; case TREES_LEN: - if (dctx.lenptr >= dctx.hlit + dctx.hdist) { - dctx.currlentable = zlib_mktable(dctx.lengths, dctx.hlit); - dctx.currdisttable = zlib_mktable(dctx.lengths + dctx.hlit, - dctx.hdist); - zlib_freetable(&dctx.lenlentable); - dctx.state = INBLK; + if (dctx->lenptr >= dctx->hlit + dctx->hdist) { + dctx->currlentable = zlib_mktable(dctx->lengths, dctx->hlit); + dctx->currdisttable = zlib_mktable(dctx->lengths + dctx->hlit, + dctx->hdist); + zlib_freetable(&dctx->lenlentable); + dctx->lenlentable = NULL; + dctx->state = INBLK; break; } code = - zlib_huflookup(&dctx.bits, &dctx.nbits, dctx.lenlentable); + zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->lenlentable); if (code == -1) goto finished; + if (code == -2) + goto decode_error; if (code < 16) - dctx.lengths[dctx.lenptr++] = code; + dctx->lengths[dctx->lenptr++] = code; else { - dctx.lenextrabits = (code == 16 ? 2 : code == 17 ? 3 : 7); - dctx.lenaddon = (code == 18 ? 11 : 3); - dctx.lenrep = (code == 16 && dctx.lenptr > 0 ? - dctx.lengths[dctx.lenptr - 1] : 0); - dctx.state = TREES_LENREP; + dctx->lenextrabits = (code == 16 ? 2 : code == 17 ? 3 : 7); + dctx->lenaddon = (code == 18 ? 11 : 3); + dctx->lenrep = (code == 16 && dctx->lenptr > 0 ? + dctx->lengths[dctx->lenptr - 1] : 0); + dctx->state = TREES_LENREP; } break; case TREES_LENREP: - if (dctx.nbits < dctx.lenextrabits) + if (dctx->nbits < dctx->lenextrabits) goto finished; rep = - dctx.lenaddon + - (dctx.bits & ((1 << dctx.lenextrabits) - 1)); - EATBITS(dctx.lenextrabits); - while (rep > 0 && dctx.lenptr < dctx.hlit + dctx.hdist) { - dctx.lengths[dctx.lenptr] = dctx.lenrep; - dctx.lenptr++; + dctx->lenaddon + + (dctx->bits & ((1 << dctx->lenextrabits) - 1)); + EATBITS(dctx->lenextrabits); + while (rep > 0 && dctx->lenptr < dctx->hlit + dctx->hdist) { + dctx->lengths[dctx->lenptr] = dctx->lenrep; + dctx->lenptr++; rep--; } - dctx.state = TREES_LEN; + dctx->state = TREES_LEN; break; case INBLK: code = - zlib_huflookup(&dctx.bits, &dctx.nbits, dctx.currlentable); + zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->currlentable); if (code == -1) goto finished; + if (code == -2) + goto decode_error; if (code < 256) - zlib_emit_char(code); + zlib_emit_char(dctx, code); else if (code == 256) { - dctx.state = OUTSIDEBLK; - if (dctx.currlentable != dctx.staticlentable) - zlib_freetable(&dctx.currlentable); - if (dctx.currdisttable != dctx.staticdisttable) - zlib_freetable(&dctx.currdisttable); + dctx->state = OUTSIDEBLK; + if (dctx->currlentable != dctx->staticlentable) { + zlib_freetable(&dctx->currlentable); + dctx->currlentable = NULL; + } + if (dctx->currdisttable != dctx->staticdisttable) { + zlib_freetable(&dctx->currdisttable); + dctx->currdisttable = NULL; + } } else if (code < 286) { /* static tree can give >285; ignore */ - dctx.state = GOTLENSYM; - dctx.sym = code; + dctx->state = GOTLENSYM; + dctx->sym = code; } break; case GOTLENSYM: - rec = &lencodes[dctx.sym - 257]; - if (dctx.nbits < rec->extrabits) + rec = &lencodes[dctx->sym - 257]; + if (dctx->nbits < rec->extrabits) goto finished; - dctx.len = - rec->min + (dctx.bits & ((1 << rec->extrabits) - 1)); + dctx->len = + rec->min + (dctx->bits & ((1 << rec->extrabits) - 1)); EATBITS(rec->extrabits); - dctx.state = GOTLEN; + dctx->state = GOTLEN; break; case GOTLEN: code = - zlib_huflookup(&dctx.bits, &dctx.nbits, - dctx.currdisttable); + zlib_huflookup(&dctx->bits, &dctx->nbits, + dctx->currdisttable); if (code == -1) goto finished; - dctx.state = GOTDISTSYM; - dctx.sym = code; + if (code == -2) + goto decode_error; + dctx->state = GOTDISTSYM; + dctx->sym = code; break; case GOTDISTSYM: - rec = &distcodes[dctx.sym]; - if (dctx.nbits < rec->extrabits) + rec = &distcodes[dctx->sym]; + if (dctx->nbits < rec->extrabits) goto finished; - dist = rec->min + (dctx.bits & ((1 << rec->extrabits) - 1)); + dist = rec->min + (dctx->bits & ((1 << rec->extrabits) - 1)); EATBITS(rec->extrabits); - dctx.state = INBLK; - while (dctx.len--) - zlib_emit_char(dctx.window[(dctx.winpos - dist) & - (WINSIZE - 1)]); + dctx->state = INBLK; + while (dctx->len--) + zlib_emit_char(dctx, dctx->window[(dctx->winpos - dist) & + (WINSIZE - 1)]); break; case UNCOMP_LEN: /* * Uncompressed block. We expect to see a 16-bit LEN. */ - if (dctx.nbits < 16) + if (dctx->nbits < 16) goto finished; - dctx.uncomplen = dctx.bits & 0xFFFF; + dctx->uncomplen = dctx->bits & 0xFFFF; EATBITS(16); - dctx.state = UNCOMP_NLEN; + dctx->state = UNCOMP_NLEN; break; case UNCOMP_NLEN: /* @@ -1156,35 +1256,131 @@ int zlib_decompress_block(unsigned char *block, int len, * which should be the one's complement of the previous * LEN. */ - if (dctx.nbits < 16) + if (dctx->nbits < 16) goto finished; - nlen = dctx.bits & 0xFFFF; + nlen = dctx->bits & 0xFFFF; EATBITS(16); - dctx.state = UNCOMP_DATA; + if (dctx->uncomplen != (nlen ^ 0xFFFF)) + goto decode_error; + if (dctx->uncomplen == 0) + dctx->state = OUTSIDEBLK; /* block is empty */ + else + dctx->state = UNCOMP_DATA; break; case UNCOMP_DATA: - if (dctx.nbits < 8) + if (dctx->nbits < 8) goto finished; - zlib_emit_char(dctx.bits & 0xFF); + zlib_emit_char(dctx, dctx->bits & 0xFF); EATBITS(8); - if (--dctx.uncomplen == 0) - dctx.state = OUTSIDEBLK; /* end of uncompressed block */ + if (--dctx->uncomplen == 0) + dctx->state = OUTSIDEBLK; /* end of uncompressed block */ break; } } finished: - *outblock = dctx.outblk; - *outlen = dctx.outlen; - + *outblock = dctx->outblk; + *outlen = dctx->outlen; return 1; + + decode_error: + sfree(dctx->outblk); + *outblock = dctx->outblk = NULL; + *outlen = 0; + return 0; } +#ifdef ZLIB_STANDALONE + +#include +#include + +int main(int argc, char **argv) +{ + unsigned char buf[16], *outbuf; + int ret, outlen; + void *handle; + int noheader = FALSE, opts = TRUE; + char *filename = NULL; + FILE *fp; + + while (--argc) { + char *p = *++argv; + + if (p[0] == '-' && opts) { + if (!strcmp(p, "-d")) + noheader = TRUE; + else if (!strcmp(p, "--")) + opts = FALSE; /* next thing is filename */ + else { + fprintf(stderr, "unknown command line option '%s'\n", p); + return 1; + } + } else if (!filename) { + filename = p; + } else { + fprintf(stderr, "can only handle one filename\n"); + return 1; + } + } + + handle = zlib_decompress_init(); + + if (noheader) { + /* + * Provide missing zlib header if -d was specified. + */ + zlib_decompress_block(handle, "\x78\x9C", 2, &outbuf, &outlen); + assert(outlen == 0); + } + + if (filename) + fp = fopen(filename, "rb"); + else + fp = stdin; + + if (!fp) { + assert(filename); + fprintf(stderr, "unable to open '%s'\n", filename); + return 1; + } + + while (1) { + ret = fread(buf, 1, sizeof(buf), fp); + if (ret <= 0) + break; + zlib_decompress_block(handle, buf, ret, &outbuf, &outlen); + if (outbuf) { + if (outlen) + fwrite(outbuf, 1, outlen, stdout); + sfree(outbuf); + } else { + fprintf(stderr, "decoding error\n"); + return 1; + } + } + + zlib_decompress_cleanup(handle); + + if (filename) + fclose(fp); + + return 0; +} + +#else + const struct ssh_compress ssh_zlib = { "zlib", + "zlib@openssh.com", /* delayed version */ zlib_compress_init, + zlib_compress_cleanup, zlib_compress_block, zlib_decompress_init, + zlib_decompress_cleanup, zlib_decompress_block, - zlib_disable_compression + zlib_disable_compression, + "zlib (RFC1950)" }; + +#endif