1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
|
/************************************************************************
* 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.
*/
#ifndef lint
static const volatile char rcsid[] = "@(#)$Id: s_zip.c,v 1.12 2005/02/20 23:09:45 chopin Exp $";
#endif
#include "os.h"
#include "s_defines.h"
#define S_ZIP_C
#include "s_externs.h"
#undef S_ZIP_C
#ifdef ZIP_LINKS
/*
** Important note:
** The provided buffers for 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 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 (MAX(ZIP_MAXIMUM, READBUF_SIZE))
/*
** size of the buffer where zlib puts compressed data
** should be enough to hold uncompressed data resulting of the
** uncompression of zipbuffer
**
** tests show that an average ratio is around 40%,
** in some very particular cases, ratio can be VERY low, BUT:
**
** s_bsd.c/read_packet() is now smart enough to detect when uncompression
** stopped because the buffer is too small, and calls dopacket() again
** to finish the work (as many times as needed).
*/
#define UNZIP_BUFFER_SIZE 4*ZIP_BUFFER_SIZE
/* buffers */
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)
{
cptr->zip = (aZdata *) MyMalloc(sizeof(aZdata));
cptr->zip->outcount = 0;
cptr->zip->in = (z_stream *) MyMalloc(sizeof(z_stream));
cptr->zip->in->avail_in = 0;
cptr->zip->in->total_in = 0;
cptr->zip->in->total_out = 0;
cptr->zip->in->zalloc = (alloc_func)0;
cptr->zip->in->zfree = (free_func)0;
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));
cptr->zip->out->total_in = 0;
cptr->zip->out->total_out = 0;
cptr->zip->out->zalloc = (alloc_func)0;
cptr->zip->out->zfree = (free_func)0;
cptr->zip->out->data_type = Z_ASCII;
if (deflateInit(cptr->zip->out, ZIP_LEVEL) != Z_OK)
return -1;
return 0;
}
/*
** zip_free
*/
void zip_free(aClient *cptr)
{
cptr->flags &= ~FLAGS_ZIP;
if (cptr->zip)
{
if (cptr->zip->in)
inflateEnd(cptr->zip->in);
MyFree(cptr->zip->in);
if (cptr->zip->out)
deflateEnd(cptr->zip->out);
MyFree(cptr->zip->out);
MyFree(cptr->zip);
cptr->zip = NULL;
}
}
/*
** unzip_packet
** Unzip the content of the buffer, don't worry about any leftover.
**
** 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)
{
Reg z_stream *zin = cptr->zip->in;
int r;
if (*length != 0 && zin->avail_in != 0)
{
sendto_flag(SCH_ERROR,
"assertion failed in unzip_packet(): %d %d",
*length, zin->avail_in);
sendto_flag(SCH_ERROR, "Please report to ircd-bugs@irc.org");
*length = -1;
return NULL;
}
if (*length)
{
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_SYNC_FLUSH))
{
case Z_OK:
cptr->flags &= ~FLAGS_ZIPRQ;
*length = UNZIP_BUFFER_SIZE - zin->avail_out;
return unzipbuf;
case Z_BUF_ERROR: /*no progress possible or output buffer too small*/
if (zin->avail_out == 0)
{
sendto_flag(SCH_ERROR,
"inflate() returned Z_BUF_ERROR: %s",
(zin->msg) ? zin->msg : "?");
*length = -1;
}
break;
case Z_DATA_ERROR: /* the buffer might not be compressed.. */
if ((cptr->flags & FLAGS_ZIPRQ) &&
!strncmp("ERROR ", buffer, 6))
{
cptr->flags &= ~(FLAGS_ZIP | FLAGS_ZIPRQ);
/*
* 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;
}
/* no break */
default: /* error ! */
/* should probably mark link as dead or something... */
sendto_flag(SCH_ERROR, "inflate() error(%d): %s", r,
(zin->msg) ? zin->msg : "?");
*length = -1; /* report error condition */
break;
}
return 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)
{
Reg z_stream *zout = cptr->zip->out;
int r;
if (buffer)
{
/* concatenate buffer in cptr->zip->outbuf */
bcopy(buffer, cptr->zip->outbuf + cptr->zip->outcount,*length);
cptr->zip->outcount += *length;
}
*length = 0;
if (!flush && ((cptr->zip->outcount < ZIP_MINIMUM) ||
((cptr->zip->outcount < (ZIP_MAXIMUM - BUFSIZE)) &&
CBurst(cptr))))
return NULL;
zout->next_in = 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_SYNC_FLUSH))
{
case Z_OK:
if (zout->avail_in)
{
/* can this occur?? I hope not... */
sendto_flag(SCH_ERROR,
"deflate() didn't process all available data!");
}
cptr->zip->outcount = 0;
*length = ZIP_BUFFER_SIZE - zout->avail_out;
return zipbuf;
default: /* error ! */
sendto_flag(SCH_ERROR, "deflate() error(%d): %s", r,
(zout->msg) ? zout->msg : "?");
*length = -1;
break;
}
return NULL;
}
#endif /* ZIP_LINKS */
|