/*************************************************************************** * God Wars Mud copyright (C) 1994, 1995, 1996 by Richard Woolcock * * * * Legend of Chrystancia copyright (C) 1999, 2000, 2001 by Matthew Little * * This mud is NOT to be copied in whole or in part, or to be run without * * the permission of Matthew Little. Nobody else has permission to * * authorise the use of this code. * ***************************************************************************/ /* * mccp.c - support functions for the Mud Client Compression Protocol * * see http://www.randomly.org/projects/MCCP/ * * Copyright (c) 1999, Oliver Jowett <oliver@randomly.org> * * This code may be freely distributed and used if this copyright * notice is retained intact. */ #include <errno.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/telnet.h> #include <arpa/inet.h> #include <zlib.h> /* MCCP */ #include "merc.h" /* local functions */ bool processCompressed (DESCRIPTOR_DATA * dsock); char enable_compress[] = { IAC, SB, TELOPT_COMPRESS, WILL, SE, '\0' }; char enable_compress2[] = { IAC, SB, TELOPT_COMPRESS2, IAC, SE, '\0' }; /* * Memory management - zlib uses these hooks to allocate and free memory * it needs */ #if defined(__OpenBSD__) || defined(__FreeBSD__) #define ENOSR 63 #endif void *zlib_alloc (void *opaque, unsigned int items, unsigned int size) { extern int nAllocPerm, sAllocPerm; nAllocPerm ++; sAllocPerm += items*size; return calloc (items, size); } void zlib_free (void *opaque, void *address) { free (address); } /* * Begin compressing data on `desc' */ bool compressStart (DESCRIPTOR_DATA * dsock, unsigned char teleopt) { z_stream *s; /* already compressing */ if (dsock->compressing == TELOPT_COMPRESS || dsock->compressing == TELOPT_COMPRESS2) return TRUE; /* allocate and init stream, buffer */ s = (z_stream *) malloc (sizeof (*s)); dsock->out_compress_buf = (unsigned char *) malloc (COMPRESS_BUF_SIZE); s->next_in = NULL; s->avail_in = 0; s->next_out = dsock->out_compress_buf; s->avail_out = COMPRESS_BUF_SIZE; s->zalloc = zlib_alloc; s->zfree = zlib_free; s->opaque = NULL; if (deflateInit (s, 9) != Z_OK) { free (dsock->out_compress_buf); free (s); return FALSE; } /* version 1 or 2 support */ if (teleopt == TELOPT_COMPRESS) write_to_descriptor (dsock, enable_compress, 0); else if (teleopt == TELOPT_COMPRESS2) write_to_descriptor (dsock, enable_compress2, 0); else { bug ("Bad teleoption %d passed", teleopt); free (dsock->out_compress_buf); free (s); return FALSE; } /* now we're compressing */ dsock->compressing = teleopt; dsock->out_compress = s; /* success */ return TRUE; } /* Cleanly shut down compression on `dsock' */ bool compressEnd (DESCRIPTOR_DATA * dsock, unsigned char teleopt, bool forced) { unsigned char dummy[1]; if (!dsock->out_compress) return TRUE; if (dsock->compressing != teleopt) return FALSE; dsock->out_compress->avail_in = 0; dsock->out_compress->next_in = dummy; /* No terminating signature is needed - receiver will get Z_STREAM_END */ if (deflate (dsock->out_compress, Z_FINISH) != Z_STREAM_END && !forced) return FALSE; /* try to send any residual data */ if (!processCompressed (dsock) && !forced) return FALSE; /* reset compression values */ deflateEnd (dsock->out_compress); free (dsock->out_compress_buf); free (dsock->out_compress); dsock->compressing = 0; dsock->out_compress = NULL; dsock->out_compress_buf = NULL; /* success */ return TRUE; } /* Try to send any pending compressed-but-not-sent data in `desc' */ bool processCompressed (DESCRIPTOR_DATA * dsock) { int iStart, nBlock, nWrite, len; if (!dsock->out_compress) return TRUE; len = dsock->out_compress->next_out - dsock->out_compress_buf; if (len > 0) { for (iStart = 0; iStart < len; iStart += nWrite) { nBlock = UMIN (len - iStart, 4096); if ((nWrite = write (dsock->descriptor, dsock->out_compress_buf + iStart, nBlock)) < 0) { if (errno == EAGAIN || errno == ENOSR) break; /* write error */ return FALSE; } if (nWrite <= 0) break; } if (iStart) { if (iStart < len) memmove (dsock->out_compress_buf, dsock->out_compress_buf + iStart, len - iStart); dsock->out_compress->next_out = dsock->out_compress_buf + len - iStart; } } /* success */ return TRUE; } void do_showcompress (CHAR_DATA * ch, char *argument) { DESCRIPTOR_DATA *d; CHAR_DATA *gch; char buf[MAX_STRING_LENGTH]; int count1 = 0; int count2 = 0; if (IS_NPC (ch)) return; for (d = first_descriptor; d != NULL; d = d->next) { if (d->connected != CON_PLAYING) continue; if (d->character != NULL) gch = d->character; else continue; if (gch->desc->out_compress) { sprintf (buf, "%-15s uses mccp\n\r", gch->name); count1++; } else { sprintf (buf, "%-15s Does not use mccp.\n\r", gch->name); count2++; } send_to_char (buf, ch); } sprintf (buf, "\n\r%d out of %d players uses mccp\n\r", count1, count2 + count1); send_to_char (buf, ch); return; }