00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #ifdef __GNUG__
00024 # pragma implementation "TelnetChunker.h"
00025 #endif
00026
00027 #include "StrMod/TelnetChunker.h"
00028
00029 #ifdef __GNUG__
00030 # pragma implementation "TelnetChunkerData.h"
00031 #endif
00032
00033 #include "StrMod/TelnetChunkerData.h"
00034 #include "StrMod/TelnetParser.h"
00035 #include "StrMod/TelnetChunkBuilder.h"
00036 #include "StrMod/StrSubChunk.h"
00037 #include "StrMod/GroupChunk.h"
00038 #include "StrMod/PreAllocBuffer.h"
00039 #include "StrMod/DynamicBuffer.h"
00040 #include "StrMod/ApplyVisitor.h"
00041 #include "StrMod/StrChunkPtrT.h"
00042 #include "StrMod/StrChunkPtr.h"
00043 #include <cassert>
00044 #include <deque>
00045
00046 namespace strmod {
00047 namespace strmod {
00048
00049 using lcore::U1Byte;
00050
00051 const STR_ClassIdent TelnetChunker::identifier(33UL);
00052 const STR_ClassIdent TelnetChunker::TelnetData::identifier(34UL);
00053 const STR_ClassIdent TelnetChunker::SingleChar::identifier(35UL);
00054 const STR_ClassIdent TelnetChunker::Suboption::identifier(36UL);
00055 const STR_ClassIdent TelnetChunker::OptionNegotiation::identifier(37UL);
00056
00057 const U1Byte TelnetChunker::Suboption::optend_[2] = {
00058 TelnetChars::IAC, TelnetChars::SE
00059 };
00060
00061 class TelnetChunker::Builder : public TelnetChunkBuilder {
00062 private:
00063 static const int qsize_ = 16;
00064 public:
00065 Builder() : curlen_(0) { }
00066 virtual ~Builder() { }
00067
00068 virtual void addDataBlock(size_t regionbegin, size_t regionend);
00069 virtual void addCharCommand(TelnetChars::Commands command);
00070 virtual void addNegotiationCommand(TelnetChars::OptionNegotiations negtype,
00071 U1Byte opt_type);
00072 virtual void addSuboption(U1Byte opt_type,
00073 size_t regionbegin, size_t regionend,
00074 StrChunkPtrT<BufferChunk> &cooked);
00075
00076 void addOutgoing(const StrChunkPtr &ptr);
00077
00078 void addIncoming(const StrChunkPtr &ptr);
00079 size_t curIncomingLen() const { return curlen_; }
00080 inline void clearIncoming();
00081 inline void setIncoming(const StrChunkPtr &ptr);
00082 inline void setIncoming(const StrChunkPtr &ptr, size_t inlen);
00083
00084 inline bool hasData() const;
00085 const StrChunkPtr getFromQueue();
00086
00087 private:
00088 std::deque<StrChunk *> chunks_;
00089 StrChunkPtr curchunk_;
00090 size_t curlen_;
00091
00092 inline StrChunk *makeDataPtr(size_t regionbegin, size_t regionend);
00093 };
00094
00095 inline void TelnetChunker::Builder::clearIncoming()
00096 {
00097 curchunk_.ReleasePtr();
00098 curlen_ = 0;
00099 }
00100
00101 inline void TelnetChunker::Builder::setIncoming(const StrChunkPtr &ptr)
00102 {
00103 setIncoming(ptr, ptr->Length());
00104 }
00105
00106 inline void
00107 TelnetChunker::Builder::setIncoming(const StrChunkPtr &ptr, size_t inlen)
00108 {
00109 curchunk_ = ptr;
00110 curlen_ = inlen;
00111 }
00112
00113 inline bool TelnetChunker::Builder::hasData() const
00114 {
00115 return !chunks_.empty();
00116 }
00117
00118 inline StrChunk *
00119 TelnetChunker::Builder::makeDataPtr(size_t regionbegin, size_t regionend)
00120 {
00121 if ((regionbegin == 0) && (regionend == curlen_))
00122 {
00123 return curchunk_.GetPtr();
00124 }
00125 else
00126 {
00127 return new StrSubChunk(curchunk_,
00128 LinearExtent(regionbegin,
00129 regionend - regionbegin));
00130 }
00131 }
00132
00133 void TelnetChunker::Builder::addDataBlock(size_t regionbegin, size_t regionend)
00134 {
00135 assert(curchunk_);
00136 assert(regionend >= regionbegin);
00137 assert(regionbegin <= curlen_);
00138 assert(regionend <= curlen_);
00139 if (regionend == regionbegin)
00140 {
00141 return;
00142 }
00143 StrChunk *ptr = makeDataPtr(regionbegin, regionend);
00144 ptr->AddReference();
00145 chunks_.push_back(ptr);
00146 }
00147
00148 void TelnetChunker::Builder::addCharCommand(TelnetChars::Commands command)
00149 {
00150 StrChunk *ptr = new SingleChar(command);
00151 ptr->AddReference();
00152 chunks_.push_back(ptr);
00153 }
00154
00155 void TelnetChunker::Builder::addNegotiationCommand(
00156 TelnetChars::OptionNegotiations negtype, U1Byte opt_type)
00157 {
00158 StrChunk *ptr = new OptionNegotiation(negtype, opt_type);
00159 ptr->AddReference();
00160 chunks_.push_back(ptr);
00161 }
00162
00163 void TelnetChunker::Builder::addSuboption(U1Byte opt_type,
00164 size_t regionbegin, size_t regionend,
00165 StrChunkPtrT<BufferChunk> &cooked)
00166 {
00167 assert(curchunk_);
00168 assert(regionend > regionbegin);
00169 assert(regionbegin < curlen_);
00170 assert(regionend <= curlen_);
00171 StrChunk *data = 0;
00172 if (regionend > regionbegin)
00173 {
00174 data = makeDataPtr(regionbegin, regionend);
00175 }
00176 StrChunk *ptr = new Suboption(opt_type, cooked, data);
00177 ptr->AddReference();
00178 chunks_.push_back(ptr);
00179 }
00180
00181 void TelnetChunker::Builder::addOutgoing(const StrChunkPtr &ptr)
00182 {
00183 StrChunk *rawptr = ptr.GetPtr();
00184 rawptr->AddReference();
00185 chunks_.push_back(rawptr);
00186 }
00187
00188 void TelnetChunker::Builder::addIncoming(const StrChunkPtr &ptr)
00189 {
00190 const size_t inlen = ptr->Length();
00191 const size_t oldlen = curlen_;
00192 assert(inlen > 0);
00193 if (inlen == 0)
00194 {
00195 return;
00196 }
00197 assert((curchunk_ && (curlen_ > 0)) || (!curchunk_ && (curlen_ == 0)));
00198 if (!curchunk_)
00199 {
00200 setIncoming(ptr, inlen);
00201 }
00202 else
00203 {
00204 StrChunkPtrT<GroupChunk> gc;
00205 if ((curchunk_->NumReferences() == 1) &&
00206 curchunk_->AreYouA(GroupChunk::identifier))
00207 {
00208 gc = static_cast<GroupChunk *>(curchunk_.GetPtr());
00209 }
00210 else
00211 {
00212 GroupChunk *tmp = new GroupChunk;
00213 tmp->push_back(curchunk_);
00214 gc = tmp;
00215 curchunk_ = tmp;
00216 }
00217 gc->push_back(ptr);
00218 curlen_ += inlen;
00219 assert(curlen_ == curchunk_->Length());
00220 }
00221 assert(curlen_ == (oldlen + inlen));
00222 }
00223
00224 const StrChunkPtr TelnetChunker::Builder::getFromQueue()
00225 {
00226 assert(!chunks_.empty());
00227 if (!chunks_.empty())
00228 {
00229 StrChunk *ptr = chunks_.front();
00230 chunks_.pop_front();
00231 assert(ptr->NumReferences() > 0);
00232 ptr->DelReference();
00233 return(ptr);
00234 }
00235 else
00236 {
00237 return 0;
00238 }
00239 }
00240
00241 struct TelnetChunker::Internals {
00242 TelnetParser parser_;
00243 Builder builder_;
00244 };
00245
00246 TelnetChunker::TelnetChunker()
00247 : data_(*(new Internals))
00248 {
00249 }
00250
00251 TelnetChunker::~TelnetChunker()
00252 {
00253 delete &data_;
00254 }
00255
00256 class TelnetChunker::DataFunctor {
00257 public:
00258 DataFunctor(TelnetParser &parser, TelnetChunkBuilder &builder)
00259 : parser_(parser), builder_(builder)
00260 {
00261 }
00262 void operator ()(const void *data, size_t len) const {
00263 parser_.processData(data, len, builder_);
00264 }
00265
00266 TelnetParser &parser_;
00267 TelnetChunkBuilder &builder_;
00268 };
00269
00270 void TelnetChunker::processIncoming()
00271 {
00272 assert(incoming_);
00273 const size_t inlen = incoming_->Length();
00274
00275 if (!data_.builder_.hasData() && (inlen <= 0))
00276 {
00277 if (data_.parser_.getRegionBegin() != data_.builder_.curIncomingLen())
00278 {
00279 data_.parser_.reset(data_.builder_);
00280 assert(data_.parser_.getRegionBegin() ==
00281 data_.builder_.curIncomingLen());
00282 }
00283 data_.parser_.dropBytes(data_.parser_.getRegionBegin());
00284 data_.builder_.clearIncoming();
00285 data_.builder_.addOutgoing(incoming_);
00286 }
00287 else if (!data_.builder_.hasData())
00288 {
00289 data_.builder_.addIncoming(incoming_);
00290 {
00291 DataFunctor tmp(data_.parser_, data_.builder_);
00292 for_each(incoming_, tmp);
00293 }
00294 data_.parser_.endOfChunk(data_.builder_);
00295
00296 const size_t regionbegin = data_.parser_.getRegionBegin();
00297 const size_t totallen = data_.builder_.curIncomingLen();
00298 assert(regionbegin <= totallen);
00299
00300
00301
00302 if ((totallen - regionbegin) > MAX_SUBOPTSIZE)
00303 {
00304 data_.parser_.reset(data_.builder_);
00305 assert(data_.parser_.getRegionBegin() == totallen);
00306 data_.parser_.dropBytes(totallen);
00307 data_.builder_.clearIncoming();
00308 if (!data_.builder_.hasData())
00309 {
00310 incoming_.ReleasePtr();
00311
00312 return;
00313 }
00314 }
00315
00316 else if (regionbegin == totallen)
00317 {
00318
00319 data_.parser_.dropBytes(totallen);
00320 data_.builder_.clearIncoming();
00321 }
00322 else
00323 {
00324 assert(totallen >= inlen);
00325
00326
00327 if ((regionbegin != 0) && ((totallen - regionbegin) <= inlen))
00328 {
00329 data_.parser_.dropBytes(totallen - inlen);
00330 data_.builder_.setIncoming(incoming_, inlen);
00331 }
00332 }
00333 }
00334 assert(data_.builder_.hasData());;
00335 if (data_.builder_.hasData())
00336 {
00337 outgoing_ = data_.builder_.getFromQueue();
00338 outgoing_ready_ = true;
00339 }
00340 if (!data_.builder_.hasData())
00341 {
00342 incoming_.ReleasePtr();
00343 }
00344 }
00345
00346 void TelnetChunker::SingleChar::acceptVisitor(ChunkVisitor &visitor)
00347 throw(ChunkVisitor::halt_visitation)
00348 {
00349 call_visitDataBlock(visitor, buf_, 2);
00350 }
00351
00352 TelnetChunker::Suboption::Suboption(U1Byte type,
00353 const StrChunkPtrT<BufferChunk> &cooked)
00354 : raw_(cookedToRaw(cooked)), rawlen_(raw_->Length()), cooked_(cooked)
00355 {
00356 optstart_[0] = TelnetChars::IAC;
00357 optstart_[1] = TelnetChars::SB;
00358 optstart_[2] = type;
00359 }
00360
00361 const StrChunkPtr
00362 TelnetChunker::Suboption::cookedToRaw(const StrChunkPtrT<BufferChunk> &cooked)
00363 {
00364 const U1Byte * const cookedbuf = cooked->getCharP();
00365 const size_t cookedlen = cooked->Length();
00366 unsigned int countIAC = 0;
00367
00368 for (size_t i = 0; i < cookedlen; ++i)
00369 {
00370 if (cookedbuf[i] == TelnetChars::IAC)
00371 {
00372 ++countIAC;
00373 }
00374 }
00375 if (countIAC == 0)
00376 {
00377 return(cooked);
00378 }
00379 else
00380 {
00381
00382 size_t rawlen = cooked->Length() + countIAC;
00383 BufferChunk *raw = 0;
00384 if (rawlen <= 16)
00385 {
00386 raw = new PreAllocBuffer<16>;
00387 raw->resize(rawlen);
00388 }
00389 else
00390 {
00391 raw = new DynamicBuffer(rawlen);
00392 }
00393 {
00394 U1Byte * const rawbuf = raw->getCharP();
00395 size_t o = 0;
00396
00397 for (size_t i = 0; i < cookedlen; ++i)
00398 {
00399 U1Byte curchar = cookedbuf[i];
00400 if (curchar == TelnetChars::IAC)
00401 {
00402 rawbuf[o++] = curchar;
00403 rawbuf[o++] = curchar;
00404 }
00405 else
00406 {
00407 rawbuf[o++] = curchar;
00408 }
00409 }
00410 assert(o == rawlen);
00411 }
00412 return(raw);
00413 }
00414 }
00415
00416 void TelnetChunker::Suboption::acceptVisitor(ChunkVisitor &visitor)
00417 throw(ChunkVisitor::halt_visitation)
00418 {
00419 call_visitDataBlock(visitor, optstart_, 3);
00420 call_visitStrChunk(visitor, raw_);
00421 call_visitDataBlock(visitor, const_cast<unsigned char *>(optend_), 2);
00422 }
00423
00424 void TelnetChunker::OptionNegotiation::acceptVisitor(ChunkVisitor &visitor)
00425 throw(ChunkVisitor::halt_visitation)
00426 {
00427 call_visitDataBlock(visitor, buf_, 3);
00428 }
00429
00430 };
00431 };