From 73f9f0ee5dca1485a8f35ff67848be43a7d75978 Mon Sep 17 00:00:00 2001 From: Thilo Cestonaro Date: Wed, 4 May 2011 19:00:08 +0200 Subject: some SOCKS5ProxiedConnection unittests fixed disconnecting when SOCKS Proxy response with an unsupported auth method License: This patch is BSD-licensed, see http://www.opensource.org/licenses/bsd-license.php diff --git a/Swiften/Network/SOCKS5ProxiedConnection.cpp b/Swiften/Network/SOCKS5ProxiedConnection.cpp index 163e23a..838bcd2 100644 --- a/Swiften/Network/SOCKS5ProxiedConnection.cpp +++ b/Swiften/Network/SOCKS5ProxiedConnection.cpp @@ -126,6 +126,7 @@ void SOCKS5ProxiedConnection::handleDataRead(boost::shared_ptr da connection_->write(socksConnect); break; default: + disconnect(); onConnectFinished(true); break; } diff --git a/Swiften/Network/UnitTest/SOCKS5ProxiedConnectionTest.cpp b/Swiften/Network/UnitTest/SOCKS5ProxiedConnectionTest.cpp new file mode 100644 index 0000000..c820221 --- /dev/null +++ b/Swiften/Network/UnitTest/SOCKS5ProxiedConnectionTest.cpp @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2010-2011 Thilo Cestonaro + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace Swift; + +class SOCKS5ProxiedConnectionTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(SOCKS5ProxiedConnectionTest); + CPPUNIT_TEST(testConnect_CreatesConnectionToProxy); + CPPUNIT_TEST(testConnect_ConnectHandshake); + CPPUNIT_TEST(testConnect_ReceiveMalformedConnectResponse); + CPPUNIT_TEST(testConnect_ReceiveConnectResponseWithUnsupportedAuthMethod); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + proxyHost = HostAddressPort(HostAddress("1.1.1.1"), 1234); + host = HostAddressPort(HostAddress("2.2.2.2"), 2345); + eventLoop = new DummyEventLoop(); + connectionFactory = new MockConnectionFactory(eventLoop); + connectFinished = false; + disconnected = false; + } + + void tearDown() { + delete connectionFactory; + delete eventLoop; + } + + void testConnect_CreatesConnectionToProxy() { + SOCKS5ProxiedConnection::ref testling(createTestling()); + + testling->connect(host); + eventLoop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(connectionFactory->connections.size())); + CPPUNIT_ASSERT(connectionFactory->connections[0]->hostAddressPort); + CPPUNIT_ASSERT(proxyHost == *connectionFactory->connections[0]->hostAddressPort); + CPPUNIT_ASSERT(!connectFinished); + } + + void testConnect_ConnectHandshake() { + ByteArray connectResponse; + ByteArray connectRequest; + SOCKS5ProxiedConnection::ref testling(createTestling()); + testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); + eventLoop->processEvents(); + + // response is always first byte 0x05 and second 0x00 + connectResponse += 0x05; + connectResponse += 0x00; + + // first part of the connection handshake + connectRequest += 0x05; // Version: SOCKS5 = 0x05 + connectRequest += 0x01; // Number of authentication methods after this byte. + connectRequest += 0x00; // authmethod = noAuth = 0x00 + + CPPUNIT_ASSERT_EQUAL(connectRequest, connectionFactory->connections[0]->dataWritten); + + // response + connectionFactory->connections[0]->onDataRead(connectResponse); + eventLoop->processEvents(); + + + // second part of the connection handshake + connectRequest += 0x05; // Version: SOCKS5 = 0x05 + connectRequest += 0x01; // Number of authentication methods after this byte. + connectRequest += 0x00; // authmethod = noAuth = 0x00 + connectRequest += 0x01; // IPv4 + connectRequest += 0x02; // IP + connectRequest += 0x02; // IP + connectRequest += 0x02; // IP + connectRequest += 0x02; // IP + connectRequest += 0x09; // Port + connectRequest += 0x29; // Port + + CPPUNIT_ASSERT_EQUAL(connectRequest, connectionFactory->connections[0]->dataWritten); + + // response + connectionFactory->connections[0]->onDataRead(connectResponse); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(connectFinished); + CPPUNIT_ASSERT(!connectFinishedWithError); + CPPUNIT_ASSERT(dataRead.isEmpty()); + } + + void testConnect_ReceiveMalformedConnectResponse() { + SOCKS5ProxiedConnection::ref testling(createTestling()); + testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); + eventLoop->processEvents(); + + connectionFactory->connections[0]->onDataRead(ByteArray("FLOOP")); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(connectFinished); + CPPUNIT_ASSERT(connectFinishedWithError); + CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); + } + + void testConnect_ReceiveConnectResponseWithUnsupportedAuthMethod() { + SOCKS5ProxiedConnection::ref testling(createTestling()); + testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); + eventLoop->processEvents(); + + ByteArray connectResponse; + connectResponse += 0x05; + connectResponse += 0x01; + + connectionFactory->connections[0]->onDataRead(connectResponse); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(connectFinished); + CPPUNIT_ASSERT(connectFinishedWithError); + CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); + } + + void testConnect_ReceiveConnectResponseWithFailure() { + ByteArray connectResponse; + ByteArray connectRequest; + SOCKS5ProxiedConnection::ref testling(createTestling()); + testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); + eventLoop->processEvents(); + + // response is always first byte 0x05 and second 0x00 + connectResponse += 0x05; + connectResponse += 0x00; + + // first part of the connection handshake + connectRequest += 0x05; // Version: SOCKS5 = 0x05 + connectRequest += 0x01; // Number of authentication methods after this byte. + connectRequest += 0x00; // authmethod = noAuth = 0x00 + + CPPUNIT_ASSERT_EQUAL(connectRequest, connectionFactory->connections[0]->dataWritten); + + // response + connectionFactory->connections[0]->onDataRead(connectResponse); + eventLoop->processEvents(); + + + // second part of the connection handshake + connectRequest += 0x05; // Version: SOCKS5 = 0x05 + connectRequest += 0x01; // Number of authentication methods after this byte. + connectRequest += 0x00; // authmethod = noAuth = 0x00 + connectRequest += 0x01; // IPv4 + connectRequest += 0x02; // IP + connectRequest += 0x02; // IP + connectRequest += 0x02; // IP + connectRequest += 0x02; // IP + connectRequest += 0x09; // Port + connectRequest += 0x29; // Port + + CPPUNIT_ASSERT_EQUAL(connectRequest, connectionFactory->connections[0]->dataWritten); + + // response + connectResponse.clear(); + connectResponse += 0x05; + connectResponse += 0x01; // general SOCKS failure + connectionFactory->connections[0]->onDataRead(connectResponse); + eventLoop->processEvents(); + + CPPUNIT_ASSERT(connectFinished); + CPPUNIT_ASSERT(connectFinishedWithError); + CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); + } + +/* + void testConnect_ReceiveDataAfterConnect() { + SOCKS5ProxiedConnection::ref testling(createTestling()); + testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); + eventLoop->processEvents(); + connectionFactory->connections[0]->onDataRead(ByteArray("HTTP/1.0 200 Connection established\r\n\r\n")); + eventLoop->processEvents(); + + connectionFactory->connections[0]->onDataRead(ByteArray("abcdef")); + + CPPUNIT_ASSERT_EQUAL(ByteArray("abcdef"), dataRead); + } + + void testWrite_AfterConnect() { + SOCKS5ProxiedConnection::ref testling(createTestling()); + testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); + eventLoop->processEvents(); + connectionFactory->connections[0]->onDataRead(ByteArray("HTTP/1.0 200 Connection established\r\n\r\n")); + eventLoop->processEvents(); + connectionFactory->connections[0]->dataWritten.clear(); + + testling->write(ByteArray("abcdef")); + + CPPUNIT_ASSERT_EQUAL(ByteArray("abcdef"), connectionFactory->connections[0]->dataWritten); + } + + void testDisconnect_AfterConnectRequest() { + SOCKS5ProxiedConnection::ref testling(createTestling()); + testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); + eventLoop->processEvents(); + + testling->disconnect(); + + CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); + CPPUNIT_ASSERT(disconnected); + CPPUNIT_ASSERT(!disconnectedError); + } + + void testDisconnect_AfterConnect() { + SOCKS5ProxiedConnection::ref testling(createTestling()); + testling->connect(HostAddressPort(HostAddress("2.2.2.2"), 2345)); + eventLoop->processEvents(); + connectionFactory->connections[0]->onDataRead(ByteArray("HTTP/1.0 200 Connection established\r\n\r\n")); + eventLoop->processEvents(); + + testling->disconnect(); + + CPPUNIT_ASSERT(connectionFactory->connections[0]->disconnected); + CPPUNIT_ASSERT(disconnected); + CPPUNIT_ASSERT(!disconnectedError); + } +*/ + private: + SOCKS5ProxiedConnection::ref createTestling() { + boost::shared_ptr c = SOCKS5ProxiedConnection::create(connectionFactory, proxyHost); + c->onConnectFinished.connect(boost::bind(&SOCKS5ProxiedConnectionTest::handleConnectFinished, this, _1)); + c->onDisconnected.connect(boost::bind(&SOCKS5ProxiedConnectionTest::handleDisconnected, this, _1)); + c->onDataRead.connect(boost::bind(&SOCKS5ProxiedConnectionTest::handleDataRead, this, _1)); + return c; + } + + void handleConnectFinished(bool error) { + connectFinished = true; + connectFinishedWithError = error; + } + + void handleDisconnected(const boost::optional& e) { + disconnected = true; + disconnectedError = e; + } + + void handleDataRead(const ByteArray& d) { + dataRead += d; + } + + struct MockConnection : public Connection { + public: + MockConnection(const std::vector& failingPorts, EventLoop* eventLoop) : eventLoop(eventLoop), failingPorts(failingPorts), disconnected(false) { + } + + void listen() { assert(false); } + + void connect(const HostAddressPort& address) { + hostAddressPort = address; + bool fail = std::find(failingPorts.begin(), failingPorts.end(), address) != failingPorts.end(); + eventLoop->postEvent(boost::bind(boost::ref(onConnectFinished), fail)); + } + + HostAddressPort getLocalAddress() const { return HostAddressPort(); } + + void disconnect() { + disconnected = true; + onDisconnected(boost::optional()); + } + + void write(const ByteArray& d) { + dataWritten += d; + } + + EventLoop* eventLoop; + boost::optional hostAddressPort; + std::vector failingPorts; + ByteArray dataWritten; + bool disconnected; + }; + + struct MockConnectionFactory : public ConnectionFactory { + MockConnectionFactory(EventLoop* eventLoop) : eventLoop(eventLoop) { + } + + boost::shared_ptr createConnection() { + boost::shared_ptr connection = boost::make_shared(failingPorts, eventLoop); + connections.push_back(connection); + return connection; + } + + EventLoop* eventLoop; + std::vector< boost::shared_ptr > connections; + std::vector failingPorts; + }; + + private: + HostAddressPort proxyHost; + HostAddressPort host; + DummyEventLoop* eventLoop; + MockConnectionFactory* connectionFactory; + std::vector< boost::shared_ptr > connections; + bool connectFinished; + bool connectFinishedWithError; + bool disconnected; + boost::optional disconnectedError; + ByteArray dataRead; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(SOCKS5ProxiedConnectionTest); diff --git a/Swiften/SConscript b/Swiften/SConscript index 9e61fc6..151ab74 100644 --- a/Swiften/SConscript +++ b/Swiften/SConscript @@ -288,6 +288,7 @@ if env["SCONS_STAGE"] == "build" : File("Network/UnitTest/HTTPConnectProxiedConnectionTest.cpp"), File("Network/UnitTest/BOSHConnectionTest.cpp"), File("Network/UnitTest/BOSHConnectionPoolTest.cpp"), + File("Network/UnitTest/SOCKS5ProxiedConnectionTest.cpp"), File("Parser/PayloadParsers/UnitTest/BodyParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/DiscoInfoParserTest.cpp"), File("Parser/PayloadParsers/UnitTest/ErrorParserTest.cpp"), -- cgit v0.10.2-6-g49f6