1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-06-27 19:06:38 +02:00
Files
unrealircd/src/zip.c
T
Bram Matthys 48dc0d824a - Hopefully fixed 'Overflowed unzipbuf increase UNZIP_BUFFER_SIZE' issue,
reported by Monk (#0003453). It should be large enough now. Also changed the
  way we deal with this when it happens (if it ever happens again..): we now
  close the server connection, instead of trying to continue, because continueing
  is too dangerous.
2007-10-02 09:14:25 +00:00

390 lines
12 KiB
C

/*
* IRC - Internet Relay Chat, ircd/s_zip.c
* Copyright (C) 1996 Christophe Kalt
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id$
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "version.h"
#include "proto.h"
#include "h.h"
#include <string.h>
#include <stdlib.h>
#ifdef ZIP_LINKS
/*
** Important note:
** The provided buffers for uncompression and compression *MUST* be big
** enough for any operation to complete.
**
** s_bsd.c current settings are that the biggest packet size is 16k
** (but socket buffers are set to 8k..)
*/
/*
** size of the buffer holding compressed data
**
** outgoing data:
** must be enough to hold compressed data resulting of the compression
** of up to ZIP_MAXIMUM bytes
** incoming data:
** must be enough to hold cptr->zip->inbuf + what was just read
** (cptr->zip->inbuf should never hold more than ONE compression block.
** The biggest block allowed for compression is ZIP_MAXIMUM bytes)
*/
#define ZIP_BUFFER_SIZE (ZIP_MAXIMUM + READBUF_SIZE)
/*
** size of the buffer where zlib puts compressed data
** must be enough to hold uncompressed data resulting of the
** uncompression of zibuffer
**
** I'm assuming that at best, ratio will be 25%. (tests show that
** best ratio is around 40%).
**
** ------
**
** On an hybrid test net, we kept filling up unzipbuf
** original was 4*ZIP_BUFFER_SIZE
** -Dianora
**
** ------
**
** And even that (6x) is not enough, now set to 16x and made it
** dynamically allocated on the heap upon the first zip_init()
** since the size of this is getting a bit out hand (ahem..).
** -Syzop
**/
#define UNZIP_BUFFER_SIZE 16 * ZIP_BUFFER_SIZE
/* buffers */
static char *unzipbuf = NULL;
/* static char unzipbuf[UNZIP_BUFFER_SIZE]; */
static char zipbuf[ZIP_BUFFER_SIZE];
/*
** zip_init
** Initialize compression structures for a server.
** If failed, zip_free() has to be called.
*/
int zip_init(aClient *cptr, int compressionlevel)
{
cptr->zip = (aZdata *) MyMalloc(sizeof(aZdata));
cptr->zip->incount = 0;
cptr->zip->outcount = 0;
cptr->zip->in = (z_stream *) MyMalloc(sizeof(z_stream));
bzero(cptr->zip->in, sizeof(z_stream)); /* Just to be sure -- Syzop */
cptr->zip->in->total_in = 0;
cptr->zip->in->total_out = 0;
cptr->zip->in->zalloc = NULL;
cptr->zip->in->zfree = NULL;
cptr->zip->in->data_type = Z_ASCII;
if (inflateInit(cptr->zip->in) != Z_OK)
{
cptr->zip->out = NULL;
return -1;
}
cptr->zip->out = (z_stream *) MyMalloc(sizeof(z_stream));
bzero(cptr->zip->out, sizeof(z_stream)); /* Just to be sure -- Syzop */
cptr->zip->out->total_in = 0;
cptr->zip->out->total_out = 0;
cptr->zip->out->zalloc = NULL;
cptr->zip->out->zfree = NULL;
cptr->zip->out->data_type = Z_ASCII;
if (deflateInit(cptr->zip->out, compressionlevel) != Z_OK)
return -1;
if (!unzipbuf)
{
unzipbuf = MyMallocEx(UNZIP_BUFFER_SIZE); /* big chunk! */
if (!unzipbuf)
{
ircd_log(LOG_ERROR, "zip_init(): out of memory (trying to alloc %d bytes)!", UNZIP_BUFFER_SIZE);
sendto_realops("zip_init(): out of memory (trying to alloc %d bytes)!", UNZIP_BUFFER_SIZE);
return -1;
}
}
return 0;
}
/*
** zip_free
*/
void zip_free(aClient *cptr)
{
ClearZipped(cptr);
if (cptr->zip)
{
if (cptr->zip->in)
inflateEnd(cptr->zip->in);
MyFree(cptr->zip->in);
cptr->zip->in = NULL;
if (cptr->zip->out)
deflateEnd(cptr->zip->out);
MyFree(cptr->zip->out);
cptr->zip->out = NULL;
MyFree(cptr->zip);
cptr->zip = NULL;
}
}
/*
** unzip_packet
** Unzip the buffer,
** put anything left in cptr->zip->inbuf, update cptr->zip->incount
**
** will return the uncompressed buffer, length will be updated.
** if a fatal error occurs, length will be set to -1
*/
char *unzip_packet(aClient *cptr, char *buffer, int *length)
{
z_stream *zin = cptr->zip->in;
int r;
char *p;
if(cptr->zip->incount)
{
/* There was a "chunk" of uncompressed data without a newline
* left over from last unzip_packet. So pick that up, and unzip
* some more data. Note, buffer parameter isn't used in this case.
* -Dianora
*/
memcpy((void *)unzipbuf,(void *)cptr->zip->inbuf,cptr->zip->incount);
zin->avail_out = UNZIP_BUFFER_SIZE - cptr->zip->incount;
zin->next_out = (Bytef *) (unzipbuf + cptr->zip->incount);
cptr->zip->incount = 0;
cptr->zip->inbuf[0] = '\0'; /* again unnecessary but nice for debugger */
}
else
{
/* Start unzipping this buffer, if I fill up output buffer,
* then snag whatever uncompressed incomplete chunk I have at
* the top of the uncompressed buffer, save it for next pass.
* -Dianora
*/
if(!buffer) /* Sanity test never hurts */
{
*length = -1;
return((char *)NULL);
}
zin->next_in = (Bytef *) buffer;
zin->avail_in = *length;
zin->next_out = (Bytef *) unzipbuf;
zin->avail_out = UNZIP_BUFFER_SIZE;
}
switch (r = inflate(zin, Z_NO_FLUSH))
{
case Z_OK:
if (zin->avail_in)
{
cptr->zip->incount = 0;
if(zin->avail_out == 0)
{
/* ok, filled up output buffer, complain about it, but go on.
* I need to find any incomplete "chunk" at the top of
* the uncompressed output buffer, and save it for next call.
* N.B. That cptr->zip->inbuf isn't really necessary
* i.e. re-entrancy is not required since I know
* there is more uncompressed data to do, and dopacket()
* will not return until its all parsed. -db
*/
sendto_realops("Overflowed unzipbuf increase UNZIP_BUFFER_SIZE");
/* We now give up again, continueing was a nice idea but is too dangerous
* and often leads to crashes!
* Note that any code below the two lines will not be executed.
*/
*length = -1;
return((char *)NULL);
#if 0
/*
* I used to just give up here....
* length = -1;
* return((char *)NULL);
*/
/* Check for pathological case where output
* just happened to have finished with a newline
* and there is still input to do
* Just stuff a newline in for now, it will be discarded
* anyway, and continue parsing. -db
*/
if((zin->next_out[0] == '\n') || (zin->next_out[0] == '\r'))
{
cptr->zip->inbuf[0] = '\n';
cptr->zip->incount = 1;
}
else
{
/* Scan from the top of output, look for a complete
* "chunk" N.B. there should be a check for p hitting
* the bottom of the unzip buffer. -db
*/
for(p = (char *) zin->next_out;p >= unzipbuf;)
{
if((*p == '\r') || (*p == '\n'))
break;
zin->avail_out++;
p--;
cptr->zip->incount++;
}
/* A little sanity test never hurts -db */
if(p == unzipbuf)
{
cptr->zip->incount = 0;
cptr->zip->inbuf[0] = '\0'; /* only for debugger */
*length = -1;
return((char *)NULL);
}
/* Ok, stuff this "chunk" into inbuf
* for next call -Dianora
*/
p++;
cptr->zip->incount--;
memcpy((void *)cptr->zip->inbuf,
(void *)p,cptr->zip->incount);
}
#endif
}
else
{
/* outbuf buffer is not full, but still
* input to do. I suppose its just bad data.
* However I don't have much other choice here but to
* give up in complete disgust -db
*/
*length = -1;
return((char *)NULL);
}
}
*length = UNZIP_BUFFER_SIZE - zin->avail_out;
return unzipbuf;
case Z_BUF_ERROR:
if (zin->avail_out == 0)
{
sendto_realops("inflate() returned Z_BUF_ERROR: %s",
(zin->msg) ? zin->msg : "?");
*length = -1;
}
break;
case Z_DATA_ERROR: /* the buffer might not be compressed.. */
if (!strncmp("ERROR ", buffer, 6))
{
cptr->zip->first = 0;
ClearZipped(cptr);
/*
* This is not sane at all. But if other server
* has sent an error now, it is probably closing
* the link as well.
*/
return buffer;
}
/* Let's be nice and give them a hint ;) -- Syzop */
sendto_realops("inflate() error: * Are you perhaps linking zipped with non-zipped? *");
sendto_realops("Hint: link::options::zip should be the same at both sides (either both disabled or both enabled)");
/* no break */
default: /* error ! */
/* should probably mark link as dead or something... */
sendto_realops("inflate() error(%d): %s", r,
(zin->msg) ? zin->msg : "?");
*length = -1; /* report error condition */
break;
}
return((char *)NULL);
}
/*
** zip_buffer
** Zip the content of cptr->zip->outbuf and of the buffer,
** put anything left in cptr->zip->outbuf, update cptr->zip->outcount
**
** if flush is set, then all available data will be compressed,
** otherwise, compression only occurs if there's enough to compress,
** or if we are reaching the maximum allowed size during a connect burst.
**
** will return the uncompressed buffer, length will be updated.
** if a fatal error occurs, length will be set to -1
*/
char *zip_buffer(aClient *cptr, char *buffer, int *length, int flush)
{
z_stream *zout = cptr->zip->out;
int r;
if (buffer)
{
/* concatenate buffer in cptr->zip->outbuf */
memcpy((void *)(cptr->zip->outbuf + cptr->zip->outcount), (void *)buffer,
*length );
cptr->zip->outcount += *length;
}
*length = 0;
#if 0
if (!flush && ((cptr->zip->outcount < ZIP_MINIMUM) ||
((cptr->zip->outcount < (ZIP_MAXIMUM - BUFSIZE)) &&
CBurst(cptr))))
/* Implement this? more efficient? or not? -- Syzop */
#else
if (!flush && (cptr->zip->outcount < ZIP_MINIMUM))
#endif
return((char *)NULL);
zout->next_in = (Bytef *) cptr->zip->outbuf;
zout->avail_in = cptr->zip->outcount;
zout->next_out = (Bytef *) zipbuf;
zout->avail_out = ZIP_BUFFER_SIZE;
switch (r = deflate(zout, Z_PARTIAL_FLUSH))
{
case Z_OK:
if (zout->avail_in)
{
/* can this occur?? I hope not... */
sendto_realops("deflate() didn't process all available data!");
}
cptr->zip->outcount = 0;
*length = ZIP_BUFFER_SIZE - zout->avail_out;
return zipbuf;
default: /* error ! */
sendto_realops("deflate() error(%d): %s", r, (zout->msg) ? zout->msg : "?");
*length = -1;
break;
}
return((char *)NULL);
}
#endif /* ZIP_LINKS */