/* This file is in the public domain. */ /* * libz is horribly under-documented. Here included is documentation * on the things we assume are true but which the interface spec does * not mention. * * It is not at all clear from the doc comments in zlib.h which of its * many interfaces should be used to get RFC1950-format compression. * Turns out deflate() and inflate() are what we want. * * It is not clear what all the funky types are. For example, the * first argument to deflateInit() and deflate() is a z_streamp, but * you have to go digging for the definition of that to discover it's * actually a pointer, and you have to pass one that points to a * z_stream (itself another typedef). * * There are also all the arguments which are Bytef * but which really * ought to be void * (and, worse, some should be const void *). We * assume Bytef * is assignment-compatible with void *. * * The missing const on z_stream.next_in is a pain, necessitating a * copy which really should be totally unnecessary (but which we must * do because the libz interface does not promise it won't write into * that data, so just dequal()ing isn't suitable). * * We assume Z_NO_FLUSH is a valid second argument to deflate(), * indicating that no particular flushing is requested. (The doc * comments don't specify what to pass for no flushing.) * * We assume that whenever a libz routine updates avail_in, it updates * next_in to match (possibly excepting the case where avail_in * becomes zero, in which case we assume nothing about next_in, except * that it's not nil - see the next item). * * inflate() seems to demand that next_in be non-nil even if avail_in * is zero. (IMO this is a bug.) * * We assume Z_NULL is a nil pointer constant. Or, more precisely, we * assume that a nil pointer is suitable for cases where the * documentation says that something is Z_NULL. * * A comment in zlib.h says that inflate() "should normally be called * until it returns Z_STREAM_END or an error". This means that * there's an error return that is not actually an error, but it's not * specified what that error return is. Based on testing, we assume * it's Z_BUF_ERROR (which seems to actually mean "no progress * possible", which - as here - is not always an error condition). */ #include #include #include "algs.h" #include "errf.h" #include "nested.h" #include "verbose.h" typedef struct ctx CTX; typedef struct ops OPS; struct ops { void *(*init)(void); void (*process)(void *, const void *, int, void (*)(const void *, int)); void (*flush)(void *, void (*)(const void *, int)); void (*done)(void *); } ; struct ctx { const OPS *ops; void *private; } ; static void *comp_zlib_comp_init(void) { z_stream *s; int err; s = malloc(sizeof(z_stream)); s->zalloc = 0; s->zfree = 0; s->opaque = 0; err = deflateInit(s,Z_DEFAULT_COMPRESSION); if (err != Z_OK) { logmsg(LM_ERR|LM_PEER,"impossible return %d from deflateInit()",err); exit(1); } return(s); } static void comp_zlib_comp_process( void *pv, const void *data, int datalen, void (*cb)(const void *, int) ) { z_stream *s; const char *dp; char iblk[512]; int ni; char oblk[512]; int err; NESTED void CB(const void *data, int len) { if (VERB(COMP)) { verb(COMP,"comp data out:\n"); verb_data_block(VERBOSE_COMP,data,len); } (*cb)(data,len); } if (VERB(COMP)) { verb(COMP,"comp data in:\n"); verb_data_block(VERBOSE_COMP,data,datalen); } s = pv; dp = data; while (datalen > 0) { ni = sizeof(iblk); if (ni > datalen) ni = datalen; bcopy(dp,&iblk[0],ni); s->next_in = (void *)&iblk[0]; s->avail_in = ni; do { s->next_out = &oblk[0]; s->avail_out = sizeof(oblk); err = deflate(s,Z_NO_FLUSH); if (err != Z_OK) { logmsg(LM_ERR|LM_PEER,"impossible return %d from deflate()",err); exit(1); } if (s->avail_out < sizeof(oblk)) CB(&oblk[0],sizeof(oblk)-s->avail_out); } while (s->avail_in > 0); datalen -= ni; dp += ni; } } static void comp_zlib_comp_flush(void *pv, void (*cb)(const void *, int)) { z_stream *s; char oblk[512]; int err; NESTED void CB(const void *data, int len) { if (VERB(COMP)) { verb(COMP,"comp data out:\n"); verb_data_block(VERBOSE_COMP,data,len); } (*cb)(data,len); } if (VERB(COMP)) verb(COMP,"comp flush\n"); s = pv; do { s->next_in = 0; s->avail_in = 0; s->next_out = &oblk[0]; s->avail_out = sizeof(oblk); err = deflate(s,Z_SYNC_FLUSH); switch (err) { case Z_OK: break; default: logmsg(LM_ERR|LM_PEER,"impossible return %d from deflate()",err); exit(1); } if (s->avail_out < sizeof(oblk)) CB(&oblk[0],sizeof(oblk)-s->avail_out); } while (s->avail_out == 0); } static void comp_zlib_comp_done(void *pv) { deflateEnd((z_stream *)pv); free(pv); } static const OPS comp_zlib_ops_comp = { &comp_zlib_comp_init, &comp_zlib_comp_process, &comp_zlib_comp_flush, &comp_zlib_comp_done }; static void *comp_zlib_decomp_init(void) { z_stream *s; int err; s = malloc(sizeof(z_stream)); s->zalloc = 0; s->zfree = 0; s->opaque = 0; s->next_in = 0; s->avail_in = 0; err = inflateInit(s); if (err != Z_OK) { logmsg(LM_ERR|LM_PEER,"impossible return %d from inflateInit()",err); exit(1); } return(s); } static void comp_zlib_decomp_process( void *pv, const void *data, int datalen, void (*cb)(const void *, int) ) { z_stream *s; const char *dp; char iblk[512]; int ni; char oblk[512]; int err; NESTED void CB(const void *data, int len) { if (VERB(COMP)) { verb(COMP,"decomp data out:\n"); verb_data_block(VERBOSE_COMP,data,len); } (*cb)(data,len); } if (VERB(COMP)) { verb(COMP,"decomp data in:\n"); verb_data_block(VERBOSE_COMP,data,datalen); } s = pv; dp = data; while (datalen > 0) { ni = sizeof(iblk); if (ni > datalen) ni = datalen; s->next_in = (void *)&iblk[0]; s->avail_in = ni; bcopy(dp,&iblk[0],ni); do { s->next_out = &oblk[0]; s->avail_out = sizeof(oblk); err = inflate(s,Z_NO_FLUSH); switch (err) { case Z_OK: break; case Z_DATA_ERROR: case Z_NEED_DICT: logmsg(LM_ERR|LM_PEER,"corrupted compressed data"); exit(1); default: logmsg(LM_ERR|LM_PEER,"impossible return %d from inflate()",err); exit(1); } if (s->avail_out < sizeof(oblk)) CB(&oblk[0],sizeof(oblk)-s->avail_out); } while (s->avail_in > 0); datalen -= ni; dp += ni; } } static void comp_zlib_decomp_flush(void *pv, void (*cb)(const void *, int)) { z_stream *s; char oblk[512]; int err; NESTED void CB(const void *data, int len) { if (VERB(COMP)) { verb(COMP,"decomp data out:\n"); verb_data_block(VERBOSE_COMP,data,len); } (*cb)(data,len); } if (VERB(COMP)) verb(COMP,"decomp flush\n"); s = pv; while <"flushing"> (1) { s->next_in = &oblk[0]; s->avail_in = 0; s->next_out = &oblk[0]; s->avail_out = sizeof(oblk); err = inflate(s,Z_SYNC_FLUSH); switch (err) { case Z_OK: break; case Z_DATA_ERROR: case Z_NEED_DICT: logmsg(LM_ERR|LM_PEER,"corrupted compressed data"); exit(1); break; case Z_BUF_ERROR: break <"flushing">; break; default: logmsg(LM_ERR|LM_PEER,"impossible return %d from inflate()",err); exit(1); } if (s->avail_out < sizeof(oblk)) CB(&oblk[0],sizeof(oblk)-s->avail_out); } } static void comp_zlib_decomp_done(void *pv) { inflateEnd((z_stream *)pv); free(pv); } static const OPS comp_zlib_ops_decomp = { &comp_zlib_decomp_init, &comp_zlib_decomp_process, &comp_zlib_decomp_flush, &comp_zlib_decomp_done }; static void *comp_zlib_init_common(const OPS *ops) { CTX *c; c = malloc(sizeof(CTX)); c->ops = ops; c->private = (*c->ops->init)(); return(c); } static void *comp_zlib_init_comp(void) { return(comp_zlib_init_common(&comp_zlib_ops_comp)); } static void *comp_zlib_init_decomp(void) { return(comp_zlib_init_common(&comp_zlib_ops_decomp)); } static void comp_zlib_process(void *state, const void *data, int datalen, void (*out)(const void *, int)) { CTX *c; c = state; (*c->ops->process)(c->private,data,datalen,out); } static void comp_zlib_flush(void *state, void (*out)(const void *, int)) { CTX *c; c = state; (*c->ops->flush)(c->private,out); } static void comp_zlib_done(void *state) { CTX *c; c = state; (*c->ops->done)(c->private); free(c); } COMPALG compalg_zlib = { "zlib", 0, &comp_zlib_init_comp, &comp_zlib_init_decomp, &comp_zlib_process, &comp_zlib_flush, &comp_zlib_done };