diff options
Diffstat (limited to 'Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp')
-rw-r--r-- | Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp new file mode 100644 index 0000000..d22845a --- /dev/null +++ b/Swiften/FileTransfer/SOCKS5BytestreamServerSession.cpp @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2010 Remko Tronçon + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include "Swiften/FileTransfer/SOCKS5BytestreamServerSession.h" + +#include <boost/bind.hpp> + +#include "Swiften/Base/ByteArray.h" +#include "Swiften/FileTransfer/SOCKS5BytestreamRegistry.h" +#include "Swiften/FileTransfer/BytestreamException.h" + +namespace Swift { + +SOCKS5BytestreamServerSession::SOCKS5BytestreamServerSession(boost::shared_ptr<Connection> connection, SOCKS5BytestreamRegistry* bytestreams) : connection(connection), bytestreams(bytestreams), state(Initial), chunkSize(4096) { +} + +SOCKS5BytestreamServerSession::~SOCKS5BytestreamServerSession() { + if (state != Finished && state != Initial) { + std::cerr << "Warning: SOCKS5BytestremServerSession unfinished" << std::endl; + finish(false); + } +} + +void SOCKS5BytestreamServerSession::start() { + connection->onDataRead.connect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1)); + state = WaitingForAuthentication; +} + +void SOCKS5BytestreamServerSession::stop() { + finish(false); +} + +void SOCKS5BytestreamServerSession::handleDataRead(const ByteArray& data) { + unprocessedData += data; + process(); +} + +void SOCKS5BytestreamServerSession::process() { + if (state == WaitingForAuthentication) { + if (unprocessedData.getSize() >= 2) { + int authCount = unprocessedData[1]; + int i = 2; + while (i < 2 + authCount && i < unprocessedData.getSize()) { + // Skip authentication mechanism + ++i; + } + if (i == 2 + authCount) { + // Authentication message is complete + if (i != unprocessedData.getSize()) { + std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism"; + } + unprocessedData.clear(); + connection->write(ByteArray("\x05\x00", 2)); + state = WaitingForRequest; + } + } + } + else if (state == WaitingForRequest) { + if (unprocessedData.getSize() >= 5) { + ByteArray requestID; + int i = 5; + int hostnameSize = unprocessedData[4]; + while (i < 5 + hostnameSize && i < unprocessedData.getSize()) { + requestID += unprocessedData[i]; + ++i; + } + // Skip the port: + i += 2; + if (i >= unprocessedData.getSize()) { + if (i != unprocessedData.getSize()) { + std::cerr << "SOCKS5BytestreamServerSession: Junk after authentication mechanism"; + } + bytestream = bytestreams->getBytestream(requestID.toString()); + ByteArray result("\x05", 1); + result += bytestream ? 0x0 : 0x4; + result += ByteArray("\x00\x03", 2); + result += static_cast<char>(requestID.getSize()); + result += requestID + ByteArray("\x00\x00", 2); + if (!bytestream) { + connection->write(result); + finish(true); + } + else { + state = SendingData; + connection->onDataWritten.connect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this)); + connection->write(result); + } + } + } + } +} + +void SOCKS5BytestreamServerSession::sendData() { + if (!bytestream->isFinished()) { + try { + connection->write(bytestream->read(chunkSize)); + } + catch (const BytestreamException& e) { + finish(true); + } + } + else { + finish(false); + } +} + +void SOCKS5BytestreamServerSession::finish(bool error) { + connection->onDataWritten.disconnect(boost::bind(&SOCKS5BytestreamServerSession::sendData, this)); + connection->onDataRead.disconnect(boost::bind(&SOCKS5BytestreamServerSession::handleDataRead, this, _1)); + bytestream.reset(); + state = Finished; + onFinished(error); +} + +} |