File indexing completed on 2024-04-21 04:02:43

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 }