File indexing completed on 2024-05-05 08:05:59
0001 /*************************************************************************** 0002 cmccp.cpp - MCCP support 0003 ------------------- 0004 begin : Pi feb 14 2003 0005 copyright : (C) 2003 by Tomas Mecir 0006 email : kmuddy@kmuddy.com 0007 ***************************************************************************/ 0008 0009 /*************************************************************************** 0010 * * 0011 * This program is free software; you can redistribute it and/or modify * 0012 * it under the terms of the GNU General Public License as published by * 0013 * the Free Software Foundation; either version 2 of the License, or * 0014 * (at your option) any later version. * 0015 * * 0016 ***************************************************************************/ 0017 0018 #include "cmccp.h" 0019 #include "ctelnet.h" 0020 0021 cMCCP::cMCCP (cTelnet *_telnet) 0022 { 0023 telnet = _telnet; 0024 instream = false; 0025 reset (); 0026 } 0027 0028 cMCCP::~cMCCP() 0029 { 0030 //nothing here 0031 } 0032 0033 /* 0034 These two functions could be causing problems, if they disabled 0035 decompression while the server was in the 0036 middle of a compressed stream. But unless the server is buggy, this can 0037 NOT happen (if we're reading this seq., then compressed data can not occur 0038 AFTER that sequence, but everything before it has already been decompressed) 0039 */ 0040 0041 void cMCCP::setMCCP1 (bool val) 0042 { 0043 mccp1 = val; 0044 mccp2 = false; 0045 usemccp = val; 0046 } 0047 0048 void cMCCP::setMCCP2 (bool val) 0049 { 0050 mccp1 = false; 0051 mccp2 = val; 0052 usemccp = val; 0053 } 0054 0055 void cMCCP::reset () 0056 { 0057 if (instream) 0058 inflateEnd (&stream); 0059 mccp1 = mccp2 = usemccp = false; 0060 instream = false; 0061 state = 0; 0062 stream.total_in = 0; 0063 stream.total_out = 0; 0064 stream.zalloc = Z_NULL; 0065 stream.zfree = Z_NULL; 0066 compressed = 0; 0067 decompressed = 0; 0068 } 0069 0070 void cMCCP::prepareDecompression (char *_inBuf, char *_outBuf, int inSize, int buflen) 0071 { 0072 inBuf = _inBuf; 0073 outBuf = _outBuf; 0074 len = buflen; 0075 0076 stream.next_in = (Bytef *) _inBuf; 0077 stream.avail_in = inSize; 0078 stream.next_out = (Bytef *) _outBuf; 0079 stream.avail_out = buflen; 0080 0081 //New data received, if we don't use compression, then this will be equal 0082 //to "decompressed" after running uncompressNext(). 0083 compressed += inSize; 0084 } 0085 0086 int cMCCP::uncompressNext () 0087 { 0088 int outSize = doUncompressNext (); 0089 if (outSize >= 0) 0090 decompressed += outSize; 0091 return outSize; 0092 } 0093 0094 int cMCCP::doUncompressNext () 0095 //some telnet parsing occurs here, but only for compressed sequences 0096 //everything else is handled by cTelnet class 0097 //caveat: this function is a bit messy :-( 0098 { 0099 int outSize = 0; 0100 0101 //Only if MCCP is enabled... MCCP must be negotiated first, so we can 0102 //assume that if MCCP is not enabled before parsing the stream, no 0103 //MCCP sequences are in that stream (it could include IAC DO COMPRESS or 0104 //COMPRESS2 though). 0105 0106 if (!usemccp) 0107 { 0108 //if MCCP is off, we simply copy input to output... 0109 if (stream.avail_in == 0) 0110 return -1; //nothing more to read 0111 for (unsigned int i = 0; i < stream.avail_in; i++) 0112 *(stream.next_out++) = *(stream.next_in++); 0113 outSize = stream.avail_in; 0114 stream.avail_in = 0; 0115 return outSize; 0116 } 0117 0118 // MCCP is used 0119 //mark output buffer as empty 0120 stream.next_out = (Bytef *) outBuf; 0121 stream.avail_out = len; 0122 0123 //no more input? 0124 if (stream.avail_in == 0) 0125 return -1; 0126 0127 //there are two basic modes - 1. not in stream, 2. in stream 0128 0129 if (instream) 0130 { 0131 //ask zlib to decompress the stream 0132 int out = stream.avail_out; 0133 int zval = inflate (&stream, Z_SYNC_FLUSH); 0134 outSize = out - stream.avail_out; 0135 0136 //if zlib says it's done, we also proceed with the next uncompressed 0137 //section... 0138 if (zval == Z_STREAM_END) 0139 { 0140 inflateEnd (&stream); 0141 instream = false; 0142 return outSize; 0143 } 0144 else 0145 { 0146 if (zval < 0) //ERROR!!! 0147 { 0148 //TODO: implement this!!! currently we silently ignore this! 0149 } 0150 return outSize; 0151 } 0152 } 0153 // Still in the stream ? Return. 0154 //I don't use "else" so I can read both compressed and uncompressed portion 0155 //of the string if we have these. 0156 if (instream) return outSize; 0157 0158 // We are not in a stream 0159 0160 //we copy inBuf to outBuf byte by byte, looking if we don't find 0161 //the begin-compressed-stream sequence 0162 //the sequence looks like this: 0163 //IAC SB COMPRESS WILL SE for MCCP v1 (invalid telnet sequence) 0164 //IAC SB COMPRESS2 IAC SE for MCCP v2 0165 0166 //there are some caveats: IAC IAC must not be interpreted as IAC 0167 //after IAC SB anything-but-COMPRESS-or-COMPRESS2 we mustn't parse 0168 //anything until IAC SE is received 0169 0170 while (stream.avail_in > 0) 0171 { 0172 if (state < 6) 0173 { 0174 unsigned char letter = 0; 0175 switch (state) { 0176 case 0: letter = TN_IAC; break; 0177 case 1: letter = TN_SB; break; 0178 case 2: letter = mccp1 ? OPT_COMPRESS : OPT_COMPRESS2; break; 0179 case 3: letter = mccp1 ? TN_WILL : TN_IAC; break; 0180 case 4: letter = TN_SE; break; 0181 } 0182 0183 if (*(stream.next_in) == letter) 0184 { 0185 stream.next_in++; 0186 stream.avail_in--; 0187 seqData[state] = letter; 0188 state++; 0189 } 0190 else 0191 { 0192 for (int i = 0; i < state; i++) 0193 { 0194 *(stream.next_out++) = seqData[i]; 0195 stream.avail_out--; 0196 outSize++; 0197 } 0198 //IAC SB followed by s.t. else than COMPRESS(2) 0199 //enter some special state where we'll remain until IAC SE comes 0200 //we cannot be in the middle of that IAC SE, because IAC SB IAC SE 0201 //is not allowed 0202 if (state >= 2) 0203 state = 6; 0204 else 0205 state = 0; 0206 //IAC IAC ?? must not be interpreted as IAC followed by IAC ??, 0207 //so we don't do anything special if we're at second IAC... 0208 0209 //current char must be added 0210 *(stream.next_out++) = *(stream.next_in++); 0211 stream.avail_out--; 0212 stream.avail_in--; 0213 outSize++; 0214 } 0215 if (state == 5) //If we are here, compressed stream should begin NOW. 0216 { //we'll decompress on next run :) 0217 //initialize zlib - NO ERROR CHECKING!!! 0218 stream.total_out = 0; 0219 stream.total_in = 0; 0220 inflateInit (&stream); 0221 0222 //return parsed uncompressed part 0223 instream = true; 0224 state = 0; 0225 return outSize; 0226 } 0227 } 0228 else 0229 { 0230 //waiting for IAC SE 0231 if ((state == 6) && (*(stream.next_in) == TN_IAC)) 0232 state = 7; 0233 else 0234 if ((state == 7) && (*(stream.next_in) == TN_SE)) 0235 state = 0; 0236 else 0237 state = 6; 0238 //current char must be added 0239 *(stream.next_out++) = *(stream.next_in++); 0240 stream.avail_out--; 0241 stream.avail_in--; 0242 outSize++; 0243 } 0244 } 0245 return outSize; 0246 }