/***************************************************************************\ [*] ___ ____ ____ __ __ ____ [*] ROGUE: ROM With Attitude [*] [*] /#/ ) /#/ ) /#/ ) /#/ /#/ /#/ [*] All rights reserved [*] [*] /#/ < /#/ / /#/ _ /#/ /#/ /#/-- [*] Copyright(C) 2000-2001 [*] [*] /#/ \(#(__/ (#(__/ (#(__/#/ (#(___ [*] Kenneth Conley (Mendanbar) [*] [*] Expression of Digital Creativity.. [*] scmud@mad.scientist.com [*] [-]---------------------------------------+-+-----------------------------[-] [*] File: mudproto.cpp [*] [*] Usage: MUD Client Protocols (MSP, MXP, MCCP) [*] \***************************************************************************/ /* * Uh.. something something.. * - Mendanbar */ #include "merc.h" #include "telnet.h" #include "clientopt.h" bool processCompress args( (DESCRIPTOR_DATA *desc) ); bool write_to_descriptor args( (DESCRIPTOR_DATA *d, char *txt, int length) ); static char mcp_start[] = { IAC, SB, TELOPT_MCP, IAC, SE, '\0' }; static char mxp_start[] = { IAC, SB, TELOPT_MXP, IAC, SE, '\0' }; void *zlib_alloc(void *opaque, UInt32 items, UInt32 size) { return calloc(items, size); } void zlib_free(void *opaque, void *address) { free(address); } bool compressStart(DESCRIPTOR_DATA *desc) { z_stream *s; if (desc->out_compress) return TRUE; s = (z_stream *)alloc_mem(sizeof(*s)); desc->out_compress_buf = (unsigned char *)alloc_mem(COMPRESS_BUF_SIZE); if (!s) { mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: compressStart: Couldn't find a socket to compress."); close_socket(desc); return FALSE; } s->next_in = NULL; s->avail_in = 0; s->next_out = desc->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) { mudlogf(BRF, LVL_STAFF, TRUE, "SYSERR: problems with zlib, trying to clean up."); free_mem(desc->out_compress_buf, COMPRESS_BUF_SIZE); free_mem(s, sizeof(z_stream)); return FALSE; } SET_BIT(DESC_FLAGS(desc), DESC_MCPON); write_to_descriptor(desc, mcp_start, strlen(mcp_start)); desc->out_compress = s; return TRUE; } bool compressEnd(DESCRIPTOR_DATA *desc) { unsigned char dummy[1]; if (!desc->out_compress) return TRUE; desc->out_compress->avail_in = 0; desc->out_compress->next_in = dummy; if (deflate(desc->out_compress, Z_FINISH) != Z_STREAM_END) return FALSE; if (!processCompressed(desc)) return FALSE; deflateEnd(desc->out_compress); free_mem(desc->out_compress_buf, COMPRESS_BUF_SIZE); free_mem(desc->out_compress, sizeof(z_stream)); desc->out_compress = NULL; desc->out_compress_buf = NULL; REMOVE_BIT(DESC_FLAGS(desc), DESC_MCPON); return TRUE; } bool processCompressed(DESCRIPTOR_DATA *desc) { int iStart, nBlock, nWrite, len; if (!desc->out_compress) return TRUE; len = desc->out_compress->next_out - desc->out_compress_buf; if (len > 0) { for (iStart = 0; iStart < len; iStart += nWrite) { nBlock = UMIN(len - iStart, 4096); if ((nWrite = write(desc->descriptor, desc->out_compress_buf + iStart, nBlock)) < 0) { if (errno == EAGAIN || errno == ENOSR) break; return FALSE; } if (nWrite <= 0) break; } if (iStart) { if (iStart < len) memmove(desc->out_compress_buf, desc->out_compress_buf + iStart, len - iStart); desc->out_compress->next_out = desc->out_compress_buf + len - iStart; } } return TRUE; } bool writeCompressed(DESCRIPTOR_DATA *desc, char *txt, int length) { z_stream *s = desc->out_compress; s->next_in = (unsigned char *)txt; s->avail_in = length; while (s->avail_in) { s->avail_out = COMPRESS_BUF_SIZE - (s->next_out - desc->out_compress_buf); if (s->avail_out) { int status = deflate(s, Z_SYNC_FLUSH); if (status != Z_OK) return FALSE; // Boom? } if (!processCompressed(desc)) return FALSE; } return TRUE; } void toggle_mxp(DESCRIPTOR_DATA *d) { if (!DESC_FLAGGED(d, DESC_MXPON)) { // Initialize and setup elements... SET_BIT(DESC_FLAGS(d), DESC_MXPON); write_to_buffer(d, mxp_start, strlen(mxp_start)); write_to_buffer(d, MXPMODE(MXP_PERM_SECURE), 0); write_to_buffer(d, MXP_TAG("!-- Setup MXP elements --"), 0); write_to_buffer(d, MXP_TAG("!ELEMENT Ex '<send>' FLAG=RoomExit"), 0); write_to_buffer(d, MXP_TAG("!ELEMENT Exit '<send exits>'"), 0); } else { // turn it off REMOVE_BIT(DESC_FLAGS(d), DESC_MXPON); write_to_buffer(d, MXPMODE(MXP_PERM_LOCKED), 0); } return; } void toggle_compression(DESCRIPTOR_DATA *d) { if (d->out_compress) compressEnd(d); else compressStart(d); return; } ACMD(do_compress) { if (!ch->desc) { ch->Send("What descriptor?\n\r"); return; } if (!ch->desc->out_compress) { if (!compressStart(ch->desc)) { ch->Send("Failed.\n\r"); return; } ch->Send("Okay, compression enabled.\n\r"); } else { if (!compressEnd(ch->desc)) { ch->Send("Failed.\n\r"); return; } ch->Send("Okay, compression disabled.\n\r"); } return; } void will_telopt(DESCRIPTOR_DATA *d, unsigned char opt) { unsigned char will_option[] = { IAC, WILL, opt, '\0' }; write_to_buffer(d, (char *)will_option, 0); } void negotiate_telopt(DESCRIPTOR_DATA *d) { // Extention Protocal will_telopt(d, TELOPT_MXP); // Sound Protocal will_telopt(d, TELOPT_MSP); if (!d->out_compress) will_telopt(d, TELOPT_MCP); } int process_telopt(DESCRIPTOR_DATA *d, int first_iac) { bool incomplete = FALSE; int l, iac_sb_index, i = first_iac; unsigned char *in = (unsigned char *)d->inbuf; bool mxp_start = FALSE, mxp_stop = FALSE, mccp_stop = FALSE; for (l = 0; in[i] == IAC && !incomplete && l < 20; l++) { switch (in[i+1]) { case IAC: case '\0': incomplete = TRUE; break; case DO: switch (in[i+2]) { case '\0': i -= 3; incomplete = TRUE; break; case TELOPT_MCP: SET_BIT(DESC_FLAGS(d), DESC_CANZLIB); toggle_compression(d); break; case TELOPT_MSP: SET_BIT(DESC_FLAGS(d), DESC_HASMSP | DESC_MSPON); break; case TELOPT_MXP: SET_BIT(DESC_FLAGS(d), DESC_HASMXP); toggle_mxp(d); break; default: // mudlogf(BRF, LVL_STAFF, TRUE, "process_telopt: unknown DO code (%s)", in[i+2]); break; } i += 3; break; case DONT: switch (in[i+2]) { case '\0': i -= 3; incomplete = true; break; case TELOPT_MCP: case TELOPT_MSP: case TELOPT_MXP: // do nothing break; default: break; } i += 3; break; case SB: iac_sb_index = i; i += 2; while (in[i] && in[i] != SE) i++; if (in[i] == SE) { i++; // subopt_check(d, iac_sb_index); } else { i = iac_sb_index; incomplete = TRUE; } break; default: if (in[+2] == '\0') incomplete = TRUE; else { mudlogf(BRF, LVL_STAFF, TRUE, "process_telopt: ignoring IAC %d %d", in[i+1], in[i+2]); i += 3; } break; } } memmove(&d->inbuf[first_iac], &d->inbuf[i], strlen(&d->inbuf[i])+1); if (l == 20) mudlogf(BRF, LVL_STAFF, TRUE, "process_telopt: More than 20 telnet options"); return 0; } void set_desc_flags(DESCRIPTOR_DATA *d) { CHAR_DATA *ch = Original(d); if (!ch) return; REMOVE_BIT(PLR_FLAGS(ch), PLR_PUEBLO | PLR_MSP ); if (DESC_FLAGGED(d, DESC_PUEBLO)) SET_BIT(PLR_FLAGS(ch), PLR_PUEBLO); } int count_mxp_tags(int mxp, const char *txt, int length) { char c; const char *p; int count = 0, InTag = 0, InEntity = 0; for (p = txt, count = 0; length > 0; p++, length--) { c = *p; if (InTag) { if (!mxp) count--; if (c == MXP_ENDc) InTag = FALSE; } else if (InEntity) { if (!mxp) count--; if (c == ';') InEntity = FALSE; } else { switch (c) { case MXP_BEGc: InTag = TRUE; if (!mxp) count--; break; case MXP_ENDc: if (!mxp) count--; break; case MXP_AMPc: InEntity = TRUE; if (!mxp) count--; break; default: if (mxp) { switch (c) { case '<': case '>': count += 3; break; case '&': count += 4; break; case '"': count += 5; break; } } break; } } } return count; } void convert_mxp_tags(int mxp, char *dest, const char *src, int len) { char *pd, c; const char *ps; bool InTag = FALSE, InEntity = FALSE; for (ps = src, pd = dest; len > 0; ps++, len--) { c = *ps; if (InTag) { if (c == MXP_ENDc) { InTag = FALSE; if (mxp) *pd++ = '>'; } else if (mxp) *pd++ = c; } else if (InEntity) { if (mxp) *pd++ = c; if (c == ';') InEntity = FALSE; } else { switch (c) { case MXP_BEGc: InTag = TRUE; if (mxp) *pd++ = '<'; break; case MXP_ENDc: if (mxp) *pd++ = '>'; break; case MXP_AMPc: InEntity = TRUE; if (mxp) *pd++ = '&'; break; default: if (mxp) { switch (c) { case '<': memcpy(pd, "<", 4); pd += 4; break; case '>': memcpy(pd, ">", 4); pd += 4; break; case '&': memcpy(pd, "&", 5); pd += 5; break; case '"': memcpy(pd, """, 6); pd += 6; break; default: *pd++ = c; break; } } else *pd++ = c; break; } } } }