/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright 2013 - 2022, nymea GmbH * Contact: contact@nymea.io * * This file is part of nymea. * This project including source code and documentation is protected by copyright law, and * remains the property of nymea GmbH. All rights, including reproduction, publication, * editing and translation, are reserved. The use of this project is subject to the terms of a * license agreement to be concluded with nymea GmbH in accordance with the terms * of use of nymea GmbH, available under https://nymea.io/license * * GNU General Public License Usage * Alternatively, this project may be redistributed and/or modified under * the terms of the GNU General Public License as published by the Free Software Foundation, * GNU version 3. this project is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this project. * If not, see . * * For any further details and any questions please contact us under contact@nymea.io * or see our FAQ/Licensing Information on https://nymea.io/license/faq * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "remoteproxyteststunnelproxy.h" #include "engine.h" #include "loggingcategories.h" #include "../common/slipdataprocessor.h" #include "../../version.h" // Client #include "tunnelproxy/tunnelproxysocketserver.h" #include "tunnelproxy/tunnelproxyremoteconnection.h" #include #include #include #include #include using namespace remoteproxyclient; RemoteProxyTestsTunnelProxy::RemoteProxyTestsTunnelProxy(QObject *parent) : BaseTest(parent) { } void RemoteProxyTestsTunnelProxy::startStopServer() { resetDebugCategories(); addDebugCategory("Engine.debug=true"); addDebugCategory("JsonRpc.debug=true"); addDebugCategory("TcpSocketServer.debug=true"); addDebugCategory("WebSocketServer.debug=true"); addDebugCategory("TunnelProxyServer.debug=true"); startServer(); stopServer(); } void RemoteProxyTestsTunnelProxy::apiBasicCallsTcp_data() { QTest::addColumn("data"); QTest::addColumn("responseId"); QTest::addColumn("responseStatus"); QTest::newRow("valid call") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\"}") << 42 << "success"; QTest::newRow("missing id") << QByteArray("{\"method\":\"RemoteProxy.Hello\"}") << -1 << "error"; QTest::newRow("missing method") << QByteArray("{\"id\":42}") << 42 << "error"; //QTest::newRow("invalid json") << QByteArray("{\"id\":42, \"method\":\"RemoteProx") << -1 << "error"; QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Explode\"}") << 42 << "error"; QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"ProxyRemote.Hello\"}") << 42 << "error"; QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"RemoteProxyHello\"}") << 42 << "error"; QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << 42 << "error"; QTest::newRow("invalid authentication params") << QByteArray("{\"id\":42, \"method\":\"Authentication.Authenticate\", \"params\":{\"your\":\"mamma\"}}") << 42 << "error"; } void RemoteProxyTestsTunnelProxy::apiBasicCallsTcp() { resetDebugCategories(); addDebugCategory("Engine.debug=true"); addDebugCategory("TcpSocketServer.debug=true"); QFETCH(QByteArray, data); QFETCH(int, responseId); QFETCH(QString, responseStatus); // Start the server startServer(); QVariant response = injectTcpSocketTunnelProxyData(data); QVERIFY(!response.isNull()); qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QCOMPARE(response.toMap().value("id").toInt(), responseId); QCOMPARE(response.toMap().value("status").toString(), responseStatus); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::getIntrospect() { // Start the server startServer(); QVariantMap response; // WebSocket response = invokeWebSocketTunnelProxyApiCall("RemoteProxy.Introspect").toMap(); qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("methods")); QVERIFY(response.value("params").toMap().contains("notifications")); QVERIFY(response.value("params").toMap().contains("types")); // Tcp response.clear(); response = invokeTcpSocketTunnelProxyApiCall("RemoteProxy.Introspect").toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("methods")); QVERIFY(response.value("params").toMap().contains("notifications")); QVERIFY(response.value("params").toMap().contains("types")); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::getHello() { // Start the server startServer(); // WebSocket QVariantMap response = invokeWebSocketTunnelProxyApiCall("RemoteProxy.Hello").toMap(); qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); // Verify data QVERIFY(!response.isEmpty()); QCOMPARE(response.value("params").toMap().value("name").toString(), Engine::instance()->configuration()->serverName()); QCOMPARE(response.value("params").toMap().value("server").toString(), QString(SERVER_NAME_STRING)); QCOMPARE(response.value("params").toMap().value("version").toString(), QString(SERVER_VERSION_STRING)); QCOMPARE(response.value("params").toMap().value("apiVersion").toString(), QString(API_VERSION_STRING)); // TCP response.clear(); response = invokeTcpSocketTunnelProxyApiCall("RemoteProxy.Hello").toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); // Verify data QVERIFY(!response.isEmpty()); QCOMPARE(response.value("params").toMap().value("name").toString(), Engine::instance()->configuration()->serverName()); QCOMPARE(response.value("params").toMap().value("server").toString(), QString(SERVER_NAME_STRING)); QCOMPARE(response.value("params").toMap().value("version").toString(), QString(SERVER_VERSION_STRING)); QCOMPARE(response.value("params").toMap().value("apiVersion").toString(), QString(API_VERSION_STRING)); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::monitorServer() { // Start the server startServer(); // ** Create the server ** QString serverName = "nymea server"; QUuid serverUuid = QUuid::createUuid(); TunnelProxySocketServer *tunnelProxyServer = new TunnelProxySocketServer(serverUuid, serverName, this); connect(tunnelProxyServer, &TunnelProxySocketServer::sslErrors, this, [=](const QList &errors){ tunnelProxyServer->ignoreSslErrors(errors); }); tunnelProxyServer->startServer(m_serverUrlTunnelProxyTcp); QSignalSpy serverRunningSpy(tunnelProxyServer, &TunnelProxySocketServer::runningChanged); serverRunningSpy.wait(); QVERIFY(serverRunningSpy.count() == 1); QList arguments = serverRunningSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == true); QVERIFY(tunnelProxyServer->running()); QCOMPARE(tunnelProxyServer->serverUrl(), m_serverUrlTunnelProxyTcp); QCOMPARE(tunnelProxyServer->remoteProxyServer(), QString(SERVER_NAME_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyServerName(), Engine::instance()->configuration()->serverName()); QCOMPARE(tunnelProxyServer->remoteProxyServerVersion(), QString(SERVER_VERSION_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyApiVersion(), QString(API_VERSION_STRING)); // ** Remote connection 1 ** QString clientOneName = "Client one"; QUuid clientOneUuid = QUuid::createUuid(); TunnelProxyRemoteConnection *remoteConnectionOne = new TunnelProxyRemoteConnection(clientOneUuid, clientOneName, this); connect(remoteConnectionOne, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){ remoteConnectionOne->ignoreSslErrors(errors); }); remoteConnectionOne->connectServer(m_serverUrlTunnelProxyTcp, serverUuid); // ** Tunnel proxy server socket 1 ** QSignalSpy remoteConnectedOneSpy(tunnelProxyServer, &TunnelProxySocketServer::clientConnected); QVERIFY(remoteConnectedOneSpy.wait()); QVERIFY(remoteConnectedOneSpy.count() == 1); arguments = remoteConnectedOneSpy.takeFirst(); TunnelProxySocket *tunnelProxySocketOne = arguments.at(0).value(); QVERIFY(tunnelProxySocketOne->clientName() == clientOneName); QVERIFY(tunnelProxySocketOne->clientUuid() == clientOneUuid); QVERIFY(!tunnelProxySocketOne->clientPeerAddress().isNull()); QVERIFY(tunnelProxySocketOne->socketAddress() != 0x0000 && tunnelProxySocketOne->socketAddress() != 0xFFFF); QVERIFY(tunnelProxySocketOne->connected()); qDebug() << "Have socket one" << tunnelProxySocketOne; // ** Send data in both directions QByteArray testData("#sdföiabi23u4b34b598gvndafjibnföQIUH34982HRIPURBFÖWLKDÜOQw9h934utbf ljBiH9FBLAJF RF AF,A§uu)(\"§)§(u$)($=$(((($((!"); // Socket -> Remote connection QSignalSpy remoteConnectionOneDataSpy(remoteConnectionOne, &TunnelProxyRemoteConnection::dataReady); tunnelProxySocketOne->writeData(testData); QVERIFY(remoteConnectionOneDataSpy.wait()); QVERIFY(remoteConnectionOneDataSpy.count() == 1); arguments = remoteConnectionOneDataSpy.takeFirst(); QByteArray receivedTestData = arguments.at(0).toByteArray(); QVERIFY(receivedTestData == testData); // Remote connection -> Socket QByteArray testData2("The biggest decision in life is about changing your life through changing your mind. – Albert Schweitzer"); QSignalSpy tunnelProxySocketOneDataSpy(tunnelProxySocketOne, &TunnelProxySocket::dataReceived); remoteConnectionOne->sendData(testData2); QVERIFY(tunnelProxySocketOneDataSpy.wait()); QVERIFY(tunnelProxySocketOneDataSpy.count() == 1); arguments = tunnelProxySocketOneDataSpy.takeFirst(); QByteArray receivedTestData2 = arguments.at(0).toByteArray(); QVERIFY(receivedTestData2 == testData2); // ** Remote connection 2 ** QString clientTwoName = "Client two"; QUuid clientTwoUuid = QUuid::createUuid(); TunnelProxyRemoteConnection *remoteConnectionTwo = new TunnelProxyRemoteConnection(clientTwoUuid, clientTwoName, this); connect(remoteConnectionTwo, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){ remoteConnectionTwo->ignoreSslErrors(errors); }); remoteConnectionTwo->connectServer(m_serverUrlTunnelProxyTcp, serverUuid); // ** Tunnel proxy server socket 2 ** QSignalSpy remoteConnectedTwoSpy(tunnelProxyServer, &TunnelProxySocketServer::clientConnected); QVERIFY(remoteConnectedTwoSpy.wait()); QVERIFY(remoteConnectedTwoSpy.count() == 1); arguments = remoteConnectedTwoSpy.takeFirst(); TunnelProxySocket *tunnelProxySocketTwo = arguments.at(0).value(); QVERIFY(tunnelProxySocketTwo->clientName() == clientTwoName); QVERIFY(tunnelProxySocketTwo->clientUuid() == clientTwoUuid); QVERIFY(!tunnelProxySocketTwo->clientPeerAddress().isNull()); QVERIFY(tunnelProxySocketTwo->socketAddress() != 0x0000 && tunnelProxySocketTwo->socketAddress() != 0xFFFF); QVERIFY(tunnelProxySocketTwo->connected()); qDebug() << "Have socket two" << tunnelProxySocketTwo; // ** Send data in both directions // Socket -> Remote connection QSignalSpy remoteConnectionTwoDataSpy(remoteConnectionTwo, &TunnelProxyRemoteConnection::dataReady); tunnelProxySocketTwo->writeData(testData); QVERIFY(remoteConnectionTwoDataSpy.wait()); QVERIFY(remoteConnectionTwoDataSpy.count() == 1); arguments = remoteConnectionTwoDataSpy.takeFirst(); receivedTestData = arguments.at(0).toByteArray(); QVERIFY(receivedTestData == testData); // Remote connection -> Socket QSignalSpy tunnelProxySocketTwoDataSpy(tunnelProxySocketTwo, &TunnelProxySocket::dataReceived); remoteConnectionTwo->sendData(testData2); QVERIFY(tunnelProxySocketTwoDataSpy.wait()); QVERIFY(tunnelProxySocketTwoDataSpy.count() == 1); arguments = tunnelProxySocketTwoDataSpy.takeFirst(); receivedTestData2 = arguments.at(0).toByteArray(); QVERIFY(receivedTestData2 == testData2); // Get monitor data QLocalSocket *monitor = new QLocalSocket(this); QSignalSpy connectedSpy(monitor, &QLocalSocket::connected); monitor->connectToServer(m_configuration->monitorSocketFileName()); if (connectedSpy.count() < 1) connectedSpy.wait(); QVERIFY(connectedSpy.count() == 1); QSignalSpy dataSpy(monitor, &QLocalSocket::readyRead); QVariantMap request; request.insert("method", "refresh"); QVariantMap params; params.insert("printAll", true); request.insert("params", params); monitor->write(QJsonDocument::fromVariant(request).toJson(QJsonDocument::Compact) + "\n"); if (dataSpy.count() < 1) dataSpy.wait(); QVERIFY(dataSpy.count() == 1); QByteArray data = monitor->readAll(); qDebug() << data; // TODO: verify monitor data QSignalSpy disconnectedSpy(monitor, &QLocalSocket::connected); monitor->disconnectFromServer(); if (disconnectedSpy.count() < 1) disconnectedSpy.wait(); Engine::instance()->tunnelProxyServer()->stopServer(); QTest::qWait(100); QVERIFY(!tunnelProxyServer->running()); QVERIFY(!remoteConnectionOne->remoteConnected()); QVERIFY(!remoteConnectionTwo->remoteConnected()); // Clean up tunnelProxyServer->deleteLater(); remoteConnectionOne->deleteLater(); remoteConnectionTwo->deleteLater(); resetDebugCategories(); stopServer(); } void RemoteProxyTestsTunnelProxy::configuration_data() { QTest::addColumn("fileName"); QTest::addColumn("success"); QTest::newRow("valid configuration") << ":/test-configuration.conf" << true; QTest::newRow("valid configuration chain") << ":/test-configuration-chain.conf" << true; QTest::newRow("faulty filename") << ":/not-exisitng-test-configuration.conf" << false; QTest::newRow("faulty certificate") << ":/test-configuration-faulty-certificate.conf" << false; QTest::newRow("faulty key") << ":/test-configuration-faulty-key.conf" << false; QTest::newRow("faulty chain") << ":/test-configuration-faulty-chain.conf" << false; } void RemoteProxyTestsTunnelProxy::configuration() { QFETCH(QString, fileName); QFETCH(bool, success); ProxyConfiguration configuration; QCOMPARE(configuration.loadConfiguration(fileName), success); } void RemoteProxyTestsTunnelProxy::serverPortBlocked() { cleanUpEngine(); m_configuration = new ProxyConfiguration(this); loadConfiguration(":/test-configuration.conf"); // Create a dummy server which blocks the port QWebSocketServer dummyServer("dummy-server", QWebSocketServer::NonSecureMode); QVERIFY(dummyServer.listen(QHostAddress::LocalHost, m_configuration->webSocketServerTunnelProxyPort())); // Start proxy webserver QSignalSpy runningSpy(Engine::instance(), &Engine::runningChanged); Engine::instance()->start(m_configuration); runningSpy.wait(); QVERIFY(runningSpy.count() == 1); // Make sure the server is not running QVERIFY(Engine::instance()->running()); // Make sure the websocket server is not running QVERIFY(!Engine::instance()->webSocketServerTunnelProxy()->running()); QSignalSpy closedSpy(&dummyServer, &QWebSocketServer::closed); dummyServer.close(); closedSpy.wait(); QVERIFY(closedSpy.count() == 1); // Try again startServer(); // Clean up stopServer(); // Do the same with the tcp server cleanUpEngine(); m_configuration = new ProxyConfiguration(this); loadConfiguration(":/test-configuration.conf"); // Create a dummy server which blocks the port QTcpServer *tcpDummyServer = new QTcpServer(this); QVERIFY(tcpDummyServer->listen(QHostAddress::LocalHost, m_configuration->tcpServerTunnelProxyPort())); // Start proxy webserver QSignalSpy runningSpy2(Engine::instance(), &Engine::runningChanged); Engine::instance()->start(m_configuration); runningSpy2.wait(); QVERIFY(runningSpy2.count() == 1); // Make sure the engine is running QVERIFY(Engine::instance()->running()); // Make sure the TCP server is not running QVERIFY(!Engine::instance()->tcpSocketServerTunnelProxy()->running()); tcpDummyServer->close(); delete tcpDummyServer; // Try again startServer(); // Make sure the TCP server is not running QVERIFY(Engine::instance()->webSocketServerTunnelProxy()->running()); QVERIFY(Engine::instance()->tcpSocketServerTunnelProxy()->running()); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::websocketBinaryData() { // Start the server startServer(); QWebSocket *client = new QWebSocket("bad-client", QWebSocketProtocol::Version13); connect(client, &QWebSocket::sslErrors, this, &BaseTest::sslErrors); QSignalSpy spyConnection(client, SIGNAL(connected())); client->open(Engine::instance()->webSocketServerTunnelProxy()->serverUrl()); spyConnection.wait(); // Send binary data and make sure the server disconnects this socket QSignalSpy spyDisconnected(client, SIGNAL(disconnected())); client->sendBinaryMessage("trying to upload stuff...stuff...more stuff... other stuff"); spyConnection.wait(1000); QVERIFY(spyConnection.count() == 1); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::websocketPing() { // Start the server startServer(); QWebSocket *client = new QWebSocket("bad-client", QWebSocketProtocol::Version13); connect(client, &QWebSocket::sslErrors, this, &BaseTest::sslErrors); QSignalSpy spyConnection(client, SIGNAL(connected())); client->open(Engine::instance()->webSocketServerTunnelProxy()->serverUrl()); spyConnection.wait(); QVERIFY(spyConnection.count() == 1); QByteArray pingMessage = QByteArray("ping!"); QSignalSpy pongSpy(client, &QWebSocket::pong); client->ping(pingMessage); pongSpy.wait(); QVERIFY(pongSpy.count() == 1); qDebug() << pongSpy.at(0).at(0) << pongSpy.at(0).at(1); QVERIFY(pongSpy.at(0).at(1).toByteArray() == pingMessage); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::apiBasicCalls_data() { QTest::addColumn("data"); QTest::addColumn("responseId"); QTest::addColumn("responseStatus"); QTest::newRow("valid call") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\"}") << 42 << "success"; QTest::newRow("missing id") << QByteArray("{\"method\":\"RemoteProxy.Hello\"}") << -1 << "error"; QTest::newRow("missing method") << QByteArray("{\"id\":42}") << 42 << "error"; QTest::newRow("invalid json") << QByteArray("{\"id\":42, \"method\":\"RemoteProx}") << -1 << "error"; QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Explode\"}") << 42 << "error"; QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"ProxyRemote.Hello\"}") << 42 << "error"; QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"RemoteProxyHello\"}") << 42 << "error"; QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"RemoteProxy.Hello\", \"params\":{\"törööö\":\"chooo-chooo\"}}") << 42 << "error"; QTest::newRow("invalid authentication params") << QByteArray("{\"id\":42, \"method\":\"Authentication.Authenticate\", \"params\":{\"your\":\"mamma\"}}") << 42 << "error"; } void RemoteProxyTestsTunnelProxy::apiBasicCalls() { QFETCH(QByteArray, data); QFETCH(int, responseId); QFETCH(QString, responseStatus); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); addDebugCategory("*.debug=true"); // Start the server startServer(); QVariantMap response; // Websocket response = injectWebSocketTunnelProxyData(data).toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(!response.isEmpty()); QCOMPARE(response.value("id").toInt(), responseId); QCOMPARE(response.value("status").toString(), responseStatus); // TCP response.clear(); response = injectTcpSocketTunnelProxyData(data).toMap(); //qDebug() << qUtf8Printable(QJsonDocument::fromVariant(response).toJson(QJsonDocument::Indented)); QVERIFY(!response.isEmpty()); QCOMPARE(response.value("id").toInt(), responseId); QCOMPARE(response.value("status").toString(), responseStatus); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::registerServer_data() { QTest::addColumn("name"); QTest::addColumn("uuid"); QTest::addColumn("expectedError"); QTest::newRow("valid call") << "valid server" << QUuid::createUuid().toString() << TunnelProxyServer::TunnelProxyErrorNoError; QTest::newRow("valid uuid with brackets") << "valid server" << "{00323a95-d1ab-4752-88d4-dbc8b9015b0f}" << TunnelProxyServer::TunnelProxyErrorNoError; QTest::newRow("valid uuid without brackets") << "valid server" << "00323a95-d1ab-4752-88d4-dbc8b9015b0f" << TunnelProxyServer::TunnelProxyErrorNoError; QTest::newRow("invalid null uuid") << "valid server" << QUuid().toString() << TunnelProxyServer::TunnelProxyErrorInvalidUuid; QTest::newRow("invalid null uuid") << "valid server" << "foo" << TunnelProxyServer::TunnelProxyErrorInvalidUuid; } void RemoteProxyTestsTunnelProxy::registerServer() { QFETCH(QString, name); QFETCH(QString, uuid); QFETCH(TunnelProxyServer::TunnelProxyError, expectedError); // Start the server startServer(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); // Register a new server QVariantMap params; params.insert("serverName", name); params.insert("serverUuid", uuid); // Websocket QVariantMap response = invokeWebSocketTunnelProxyApiCall("TunnelProxy.RegisterServer", params).toMap(); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response, expectedError); // TCP Socket response = invokeTcpSocketTunnelProxyApiCall("TunnelProxy.RegisterServer", params).toMap(); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response, expectedError); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::registerClient_data() { QTest::addColumn("name"); QTest::addColumn("uuid"); QTest::addColumn("expectedError"); QTest::addColumn("serverExists"); QTest::addColumn("serverUuid"); QTest::addColumn("expectedServerError"); QTest::newRow("valid client: valid server") << "Friendly client" << QUuid::createUuid().toString() << TunnelProxyServer::TunnelProxyErrorNoError << true << QUuid::createUuid().toString() << TunnelProxyServer::TunnelProxyErrorNoError; QTest::newRow("valid client: no server") << "Friendly client" << QUuid::createUuid().toString() << TunnelProxyServer::TunnelProxyErrorServerNotFound << false << QUuid::createUuid().toString() << TunnelProxyServer::TunnelProxyErrorNoError; QTest::newRow("valid client: invalid server uuid") << "Friendly client" << QUuid::createUuid().toString() << TunnelProxyServer::TunnelProxyErrorInvalidUuid << true << QUuid().toString() << TunnelProxyServer::TunnelProxyErrorInvalidUuid; QTest::newRow("invalid client uuid: valid server uuid") << "Friendly client" << QUuid().toString() << TunnelProxyServer::TunnelProxyErrorInvalidUuid << true << QUuid::createUuid().toString() << TunnelProxyServer::TunnelProxyErrorNoError; QTest::newRow("invalid client uuid: invalid server uuid") << "Friendly client" << "hello again" << TunnelProxyServer::TunnelProxyErrorInvalidUuid << true << "it's me" << TunnelProxyServer::TunnelProxyErrorInvalidUuid; } void RemoteProxyTestsTunnelProxy::registerClient() { QFETCH(QString, name); QFETCH(QString, uuid); QFETCH(TunnelProxyServer::TunnelProxyError, expectedError); QFETCH(bool, serverExists); QFETCH(QString, serverUuid); QFETCH(TunnelProxyServer::TunnelProxyError, expectedServerError); // Start the server startServer(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); QSslSocket *socket = nullptr; if (serverExists) { QVariantMap serverParams; serverParams.insert("serverName", "dummy server"); serverParams.insert("serverUuid", serverUuid); // TCP socket QPair result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", serverParams); QVariantMap response = result.first.toMap(); socket = result.second; QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); QVERIFY(response.value("params").toMap().contains("slipEnabled")); //QVERIFY(response.value("params").toMap().value("slipEnabled").toBool())); verifyTunnelProxyError(response, expectedServerError); } // Register a new client QVariantMap params; params.insert("clientName", name); params.insert("clientUuid", uuid); params.insert("serverUuid", serverUuid); // Websocket QVariantMap response = invokeWebSocketTunnelProxyApiCall("TunnelProxy.RegisterClient", params).toMap(); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response, expectedError); QTest::qWait(100); // TCP Socket response = invokeTcpSocketTunnelProxyApiCall("TunnelProxy.RegisterClient", params).toMap(); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response, expectedError); QTest::qWait(100); if (socket) { // Close the tcp socket socket->close(); delete socket; } // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::testSlip_data() { QTest::addColumn("data"); QTest::addColumn("success"); QTest::newRow("valid: a lot of special characters") << QByteArray::fromHex("C0AABBCCDDEEFF12A1B2C3D4E5FFC0DBDCDDAA") << true; QTest::newRow("valid: start and end with END protocol") << QByteArray::fromHex("C0C0C0C0C0C0C0C0C0C0DDDDCDCDCDCDCDCD") << true; QTest::newRow("valid: normal text with a special characters") << QByteArray("Foo Bar text describing 123456770ß2123#+@$%/(!\"W=$*'*") << true; QTest::newRow("invalid: escape followed by nor escaped special character") << QByteArray::fromHex("AADB12FFC0") << false; } void RemoteProxyTestsTunnelProxy::testSlip() { QFETCH(QByteArray, data); QFETCH(bool, success); if (success) { QByteArray serializedData = SlipDataProcessor::serializeData(data); QVERIFY(serializedData.endsWith(0xC0)); QByteArray deserializedData = SlipDataProcessor::deserializeData(serializedData); QVERIFY(deserializedData == data); } else { QByteArray deserializedData = SlipDataProcessor::deserializeData(data); QVERIFY(deserializedData.isEmpty()); } } void RemoteProxyTestsTunnelProxy::registerServerDuplicated() { // Start the server startServer(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); addDebugCategory("JsonRpcTraffic.debug=true"); // Register a new server QString serverName = "tunnel proxy server awesome nymea installation"; QUuid serverUuid = QUuid::createUuid(); QVariantMap params; params.insert("serverName", serverName); params.insert("serverUuid", serverUuid.toString()); // TCP socket QPair result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params); QVariantMap response = result.first.toMap(); QSslSocket *socket = result.second; QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); QVERIFY(response.value("params").toMap().contains("slipEnabled")); QVERIFY(response.value("params").toMap().value("slipEnabled").toBool()); verifyTunnelProxyError(response); // SLIP Should be enabled now // Try to register again with the same uuid on the same socket result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, true, socket); response = result.first.toMap(); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered); QSignalSpy disconnectedTcpSpy(socket, &QSslSocket::disconnected); QVERIFY(disconnectedTcpSpy.wait()); QVERIFY(disconnectedTcpSpy.count() == 1); // Close the tcp socket delete socket; QTest::qWait(100); // WebSocket // Try to register from a websocket with the same uuid QPair resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params); response = resultWebSocket.first.toMap(); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response); resetDebugCategories(); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::registerClientDuplicated() { // Start the server startServer(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); addDebugCategory("JsonRpcTraffic.debug=true"); // Create the server and keep it up QString serverName = "creative server name"; QUuid serverUuid = QUuid::createUuid(); QVariantMap serverParams; serverParams.insert("serverName", serverName); serverParams.insert("serverUuid", serverUuid.toString()); // TCP socket QPair serverResult = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", serverParams); QVariantMap response = serverResult.first.toMap(); QSslSocket *serverSocket = serverResult.second; QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response); // Connect a client TCP QString clientName = "creative server name"; QUuid clientUuid = QUuid::createUuid(); QVariantMap clientParams; clientParams.insert("clientName", serverName); clientParams.insert("clientUuid", clientUuid.toString()); clientParams.insert("serverUuid", serverUuid.toString()); QPair clientResult = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterClient", clientParams); response = clientResult.first.toMap(); QSslSocket *clientSocketTcp = clientResult.second; QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response); // Connect another client WebSocket QPair clientResultWS = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterClient", clientParams); response = clientResultWS.first.toMap(); QSslSocket *clientSocketWs = clientResultWS.second; QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered); // CleanUp if (clientSocketTcp) { // Close the tcp socket clientSocketTcp->close(); delete clientSocketTcp; } if (clientSocketWs) { // Close the tcp socket clientSocketWs->close(); delete clientSocketWs; } if (serverSocket) { // Close the tcp socket serverSocket->close(); delete serverSocket; } resetDebugCategories(); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::crossRegisterServerClient() { // Start the server startServer(); resetDebugCategories(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); addDebugCategory("TunnelProxyServerTraffic.debug=true"); addDebugCategory("JsonRpcTraffic.debug=true"); // Create the server and keep it up QString serverName = "creative server name"; QUuid serverUuid = QUuid::createUuid(); QVariantMap serverParams; serverParams.insert("serverName", serverName); serverParams.insert("serverUuid", serverUuid.toString()); // TCP server QPair serverResult = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", serverParams); QVariantMap response = serverResult.first.toMap(); QSslSocket *serverSocket = serverResult.second; QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response); // SLIP is enabled now // Now try to register as client QVariantMap clientParams; clientParams.insert("clientName", "creative client name"); clientParams.insert("clientUuid", QUuid::createUuid().toString()); clientParams.insert("serverUuid", serverUuid.toString()); QPair result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterClient", clientParams, true, serverSocket); response = result.first.toMap(); QVERIFY(!response.isEmpty()); QVERIFY(response.value("status").toString() == "success"); QVERIFY(response.value("params").toMap().contains("tunnelProxyError")); verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered); QSignalSpy disconnectedSpy(serverSocket, &QSslSocket::disconnected); QVERIFY(disconnectedSpy.wait()); QVERIFY(disconnectedSpy.count() == 1); serverSocket->close(); delete serverSocket; resetDebugCategories(); // Clean up stopServer(); } void RemoteProxyTestsTunnelProxy::testTunnelProxyServer() { // Start the server startServer(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); addDebugCategory("TunnelProxyServerTraffic.debug=true"); addDebugCategory("JsonRpcTraffic.debug=true"); addDebugCategory("TunnelProxySocketServer.debug=true"); addDebugCategory("TunnelProxyRemoteConnection.debug=true"); addDebugCategory("TunnelProxyRemoteConnectionTraffic.debug=true"); addDebugCategory("RemoteProxyClientJsonRpcTraffic.debug=true"); // Tunnel proxy socket server QString serverName = "SuperDuper server name"; QUuid serverUuid = QUuid::createUuid(); TunnelProxySocketServer *tunnelProxyServer = new TunnelProxySocketServer(serverUuid, serverName, this); connect(tunnelProxyServer, &TunnelProxySocketServer::sslErrors, this, [=](const QList &errors){ tunnelProxyServer->ignoreSslErrors(errors); }); tunnelProxyServer->startServer(m_serverUrlTunnelProxyTcp); QSignalSpy serverRunningSpy(tunnelProxyServer, &TunnelProxySocketServer::runningChanged); serverRunningSpy.wait(); QVERIFY(serverRunningSpy.count() == 1); QList arguments = serverRunningSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == true); QVERIFY(tunnelProxyServer->running()); QCOMPARE(tunnelProxyServer->serverUrl(), m_serverUrlTunnelProxyTcp); QCOMPARE(tunnelProxyServer->remoteProxyServer(), QString(SERVER_NAME_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyServerName(), Engine::instance()->configuration()->serverName()); QCOMPARE(tunnelProxyServer->remoteProxyServerVersion(), QString(SERVER_VERSION_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyApiVersion(), QString(API_VERSION_STRING)); // Tunnel proxy client connection QString clientName = "Awesome client name"; QUuid clientUuid = QUuid::createUuid(); TunnelProxyRemoteConnection *clientConnection = new TunnelProxyRemoteConnection(clientUuid, clientName, this); connect(clientConnection, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){ clientConnection->ignoreSslErrors(errors); }); clientConnection->connectServer(m_serverUrlTunnelProxyTcp, serverUuid); QSignalSpy clientConnectedSpy(tunnelProxyServer, &TunnelProxySocketServer::clientConnected); QVERIFY(clientConnectedSpy.wait()); QVERIFY(clientConnectedSpy.count() == 1); arguments = clientConnectedSpy.takeFirst(); TunnelProxySocket *tunnelProxySocket = arguments.at(0).value(); QVERIFY(tunnelProxySocket->clientName() == clientName); QVERIFY(tunnelProxySocket->clientUuid() == clientUuid); QVERIFY(!tunnelProxySocket->clientPeerAddress().isNull()); QVERIFY(tunnelProxySocket->socketAddress() != 0x0000 && tunnelProxySocket->socketAddress() != 0xFFFF); QVERIFY(tunnelProxySocket->connected()); QCOMPARE(clientConnection->serverUrl(), m_serverUrlTunnelProxyTcp); QCOMPARE(clientConnection->remoteProxyServer(), QString(SERVER_NAME_STRING)); QCOMPARE(clientConnection->remoteProxyServerName(), Engine::instance()->configuration()->serverName()); QCOMPARE(clientConnection->remoteProxyServerVersion(), QString(SERVER_VERSION_STRING)); QCOMPARE(clientConnection->remoteProxyApiVersion(), QString(API_VERSION_STRING)); // We have a remote connection, now disconnect the client and verify the socket dissapears on th server side QSignalSpy clientDisconnectedSpy(tunnelProxyServer, &TunnelProxySocketServer::clientDisconnected); clientConnection->disconnectServer(); QVERIFY(clientDisconnectedSpy.wait()); QVERIFY(clientDisconnectedSpy.count() == 1); arguments = clientDisconnectedSpy.takeFirst(); tunnelProxySocket = arguments.at(0).value(); QVERIFY(tunnelProxySocket->clientName() == clientName); QVERIFY(tunnelProxySocket->clientUuid() == clientUuid); QVERIFY(!tunnelProxySocket->clientPeerAddress().isNull()); QVERIFY(tunnelProxySocket->socketAddress() != 0x0000 && tunnelProxySocket->socketAddress() != 0xFFFF); QVERIFY(!tunnelProxySocket->connected()); // Stop the server connection and verify the client gets disconnected QSignalSpy serverNotRunningSpy(tunnelProxyServer, &TunnelProxySocketServer::runningChanged); tunnelProxyServer->stopServer(); // Verify the remote connection is disconnected serverNotRunningSpy.wait(); QVERIFY(serverNotRunningSpy.count() == 1); arguments = serverNotRunningSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == false); QVERIFY(!tunnelProxyServer->running()); // Clean up tunnelProxyServer->deleteLater(); clientConnection->deleteLater(); resetDebugCategories(); stopServer(); } void RemoteProxyTestsTunnelProxy::testTunnelProxyClient() { // Start the server startServer(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); addDebugCategory("TunnelProxyServerTraffic.debug=true"); addDebugCategory("JsonRpcTraffic.debug=true"); addDebugCategory("TunnelProxySocketServer.debug=true"); addDebugCategory("TunnelProxyRemoteConnection.debug=true"); addDebugCategory("RemoteProxyClientJsonRpcTraffic.debug=true"); // Tunnel proxy socket server QString serverName = "SuperDuper server name"; QUuid serverUuid = QUuid::createUuid(); TunnelProxySocketServer *tunnelProxyServer = new TunnelProxySocketServer(serverUuid, serverName, this); connect(tunnelProxyServer, &TunnelProxySocketServer::sslErrors, this, [=](const QList &errors){ qDebug() << errors; tunnelProxyServer->ignoreSslErrors(errors); }); tunnelProxyServer->startServer(m_serverUrlTunnelProxyTcp); QSignalSpy serverRunningSpy(tunnelProxyServer, &TunnelProxySocketServer::runningChanged); serverRunningSpy.wait(); QVERIFY(serverRunningSpy.count() == 1); QList arguments = serverRunningSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == true); QVERIFY(tunnelProxyServer->running()); QCOMPARE(tunnelProxyServer->serverUrl(), m_serverUrlTunnelProxyTcp); QCOMPARE(tunnelProxyServer->remoteProxyServer(), QString(SERVER_NAME_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyServerName(), Engine::instance()->configuration()->serverName()); QCOMPARE(tunnelProxyServer->remoteProxyServerVersion(), QString(SERVER_VERSION_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyApiVersion(), QString(API_VERSION_STRING)); // Tunnel proxy client connection QString clientName = "Awesome client name"; QUuid clientUuid = QUuid::createUuid(); TunnelProxyRemoteConnection *clientConnection = new TunnelProxyRemoteConnection(clientUuid, clientName, this); connect(clientConnection, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){ clientConnection->ignoreSslErrors(errors); }); clientConnection->connectServer(m_serverUrlTunnelProxyTcp, serverUuid); QSignalSpy clientRemoteConnectedSpy(clientConnection, &TunnelProxyRemoteConnection::remoteConnectedChanged); QVERIFY(clientRemoteConnectedSpy.wait()); QVERIFY(clientRemoteConnectedSpy.count() == 1); arguments = clientRemoteConnectedSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == true); QVERIFY(clientConnection->remoteConnected()); QCOMPARE(clientConnection->serverUrl(), m_serverUrlTunnelProxyTcp); QCOMPARE(clientConnection->remoteProxyServer(), QString(SERVER_NAME_STRING)); QCOMPARE(clientConnection->remoteProxyServerName(), Engine::instance()->configuration()->serverName()); QCOMPARE(clientConnection->remoteProxyServerVersion(), QString(SERVER_VERSION_STRING)); QCOMPARE(clientConnection->remoteProxyApiVersion(), QString(API_VERSION_STRING)); // Stop the server and make sure the client gets disconnected tunnelProxyServer->stopServer(); QSignalSpy clientRemoteDisonnectedSpy(clientConnection, &TunnelProxyRemoteConnection::remoteConnectedChanged); QVERIFY(clientRemoteDisonnectedSpy.wait()); QVERIFY(clientRemoteDisonnectedSpy.count() == 1); arguments = clientRemoteDisonnectedSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == false); QVERIFY(!clientConnection->remoteConnected()); // Clean up tunnelProxyServer->deleteLater(); clientConnection->deleteLater(); resetDebugCategories(); stopServer(); } void RemoteProxyTestsTunnelProxy::testTunnelProxyServerSocketDisconnect() { // Start the server startServer(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); addDebugCategory("TunnelProxyServerTraffic.debug=true"); addDebugCategory("JsonRpcTraffic.debug=true"); addDebugCategory("TunnelProxySocketServer.debug=true"); addDebugCategory("TunnelProxyRemoteConnection.debug=true"); addDebugCategory("TunnelProxyRemoteConnectionTraffic.debug=true"); addDebugCategory("RemoteProxyClientJsonRpcTraffic.debug=true"); // Tunnel proxy socket server QString serverName = "SuperDuper server name"; QUuid serverUuid = QUuid::createUuid(); TunnelProxySocketServer *tunnelProxyServer = new TunnelProxySocketServer(serverUuid, serverName, this); connect(tunnelProxyServer, &TunnelProxySocketServer::sslErrors, this, [=](const QList &errors){ tunnelProxyServer->ignoreSslErrors(errors); }); tunnelProxyServer->startServer(m_serverUrlTunnelProxyTcp); QSignalSpy serverRunningSpy(tunnelProxyServer, &TunnelProxySocketServer::runningChanged); serverRunningSpy.wait(); QVERIFY(serverRunningSpy.count() == 1); QList arguments = serverRunningSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == true); QVERIFY(tunnelProxyServer->running()); QCOMPARE(tunnelProxyServer->serverUrl(), m_serverUrlTunnelProxyTcp); QCOMPARE(tunnelProxyServer->remoteProxyServer(), QString(SERVER_NAME_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyServerName(), Engine::instance()->configuration()->serverName()); QCOMPARE(tunnelProxyServer->remoteProxyServerVersion(), QString(SERVER_VERSION_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyApiVersion(), QString(API_VERSION_STRING)); // Tunnel proxy client connection QString clientName = "Awesome client name"; QUuid clientUuid = QUuid::createUuid(); TunnelProxyRemoteConnection *clientConnection = new TunnelProxyRemoteConnection(clientUuid, clientName, this); connect(clientConnection, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){ clientConnection->ignoreSslErrors(errors); }); clientConnection->connectServer(m_serverUrlTunnelProxyTcp, serverUuid); QSignalSpy clientConnectedSpy(tunnelProxyServer, &TunnelProxySocketServer::clientConnected); QVERIFY(clientConnectedSpy.wait()); QVERIFY(clientConnectedSpy.count() == 1); arguments = clientConnectedSpy.takeFirst(); TunnelProxySocket *tunnelProxySocket = arguments.at(0).value(); QVERIFY(tunnelProxySocket->clientName() == clientName); QVERIFY(tunnelProxySocket->clientUuid() == clientUuid); QVERIFY(!tunnelProxySocket->clientPeerAddress().isNull()); QVERIFY(tunnelProxySocket->socketAddress() != 0x0000 && tunnelProxySocket->socketAddress() != 0xFFFF); QVERIFY(tunnelProxySocket->connected()); QCOMPARE(clientConnection->serverUrl(), m_serverUrlTunnelProxyTcp); QCOMPARE(clientConnection->remoteProxyServer(), QString(SERVER_NAME_STRING)); QCOMPARE(clientConnection->remoteProxyServerName(), Engine::instance()->configuration()->serverName()); QCOMPARE(clientConnection->remoteProxyServerVersion(), QString(SERVER_VERSION_STRING)); QCOMPARE(clientConnection->remoteProxyApiVersion(), QString(API_VERSION_STRING)); QVERIFY(clientConnection->remoteConnected() == true); // Now request the TunnelProxySocket to disconnect and verify the client Connection really gets disconnected QSignalSpy clientRemoteDisonnectedSpy(clientConnection, &TunnelProxyRemoteConnection::remoteConnectedChanged); tunnelProxySocket->disconnectSocket(); QVERIFY(clientRemoteDisonnectedSpy.wait()); QVERIFY(clientRemoteDisonnectedSpy.count() == 1); arguments = clientRemoteDisonnectedSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == false); QVERIFY(!clientConnection->remoteConnected()); QTest::qWait(100); // Clean up tunnelProxyServer->deleteLater(); clientConnection->deleteLater(); resetDebugCategories(); stopServer(); } void RemoteProxyTestsTunnelProxy::tunnelProxyEndToEndTest() { // Start the server startServer(); resetDebugCategories(); addDebugCategory("TunnelProxyServer.debug=true"); addDebugCategory("TunnelProxyServerTraffic.debug=true"); addDebugCategory("JsonRpcTraffic.debug=true"); addDebugCategory("TunnelProxySocketServer.debug=true"); addDebugCategory("TunnelProxyRemoteConnection.debug=true"); addDebugCategory("TunnelProxyRemoteConnectionTraffic.debug=true"); addDebugCategory("RemoteProxyClientJsonRpcTraffic.debug=true"); // ** Create the server ** QString serverName = "nymea server"; QUuid serverUuid = QUuid::createUuid(); TunnelProxySocketServer *tunnelProxyServer = new TunnelProxySocketServer(serverUuid, serverName, this); connect(tunnelProxyServer, &TunnelProxySocketServer::sslErrors, this, [=](const QList &errors){ tunnelProxyServer->ignoreSslErrors(errors); }); tunnelProxyServer->startServer(m_serverUrlTunnelProxyTcp); QSignalSpy serverRunningSpy(tunnelProxyServer, &TunnelProxySocketServer::runningChanged); serverRunningSpy.wait(); QVERIFY(serverRunningSpy.count() == 1); QList arguments = serverRunningSpy.takeFirst(); QVERIFY(arguments.at(0).toBool() == true); QVERIFY(tunnelProxyServer->running()); QCOMPARE(tunnelProxyServer->serverUrl(), m_serverUrlTunnelProxyTcp); QCOMPARE(tunnelProxyServer->remoteProxyServer(), QString(SERVER_NAME_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyServerName(), Engine::instance()->configuration()->serverName()); QCOMPARE(tunnelProxyServer->remoteProxyServerVersion(), QString(SERVER_VERSION_STRING)); QCOMPARE(tunnelProxyServer->remoteProxyApiVersion(), QString(API_VERSION_STRING)); // ** Remote connection 1 ** QString clientOneName = "Client one"; QUuid clientOneUuid = QUuid::createUuid(); TunnelProxyRemoteConnection *remoteConnectionOne = new TunnelProxyRemoteConnection(clientOneUuid, clientOneName, this); connect(remoteConnectionOne, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){ remoteConnectionOne->ignoreSslErrors(errors); }); remoteConnectionOne->connectServer(m_serverUrlTunnelProxyTcp, serverUuid); // ** Tunnel proxy server socket 1 ** QSignalSpy remoteConnectedOneSpy(tunnelProxyServer, &TunnelProxySocketServer::clientConnected); QVERIFY(remoteConnectedOneSpy.wait()); QVERIFY(remoteConnectedOneSpy.count() == 1); arguments = remoteConnectedOneSpy.takeFirst(); TunnelProxySocket *tunnelProxySocketOne = arguments.at(0).value(); QVERIFY(tunnelProxySocketOne->clientName() == clientOneName); QVERIFY(tunnelProxySocketOne->clientUuid() == clientOneUuid); QVERIFY(!tunnelProxySocketOne->clientPeerAddress().isNull()); QVERIFY(tunnelProxySocketOne->socketAddress() != 0x0000 && tunnelProxySocketOne->socketAddress() != 0xFFFF); QVERIFY(tunnelProxySocketOne->connected()); qDebug() << "Have socket one" << tunnelProxySocketOne; // ** Send data in both directions QByteArray testData("#sdföiabi23u4b34b598gvndafjibnföQIUH34982HRIPURBFÖWLKDÜOQw9h934utbf ljBiH9FBLAJF RF AF,A§uu)(\"§)§(u$)($=$(((($((!"); // Socket -> Remote connection QSignalSpy remoteConnectionOneDataSpy(remoteConnectionOne, &TunnelProxyRemoteConnection::dataReady); tunnelProxySocketOne->writeData(testData); QVERIFY(remoteConnectionOneDataSpy.wait()); QVERIFY(remoteConnectionOneDataSpy.count() == 1); arguments = remoteConnectionOneDataSpy.takeFirst(); QByteArray receivedTestData = arguments.at(0).toByteArray(); QVERIFY(receivedTestData == testData); // Remote connection -> Socket QByteArray testData2("The biggest decision in life is about changing your life through changing your mind. – Albert Schweitzer"); QSignalSpy tunnelProxySocketOneDataSpy(tunnelProxySocketOne, &TunnelProxySocket::dataReceived); remoteConnectionOne->sendData(testData2); QVERIFY(tunnelProxySocketOneDataSpy.wait()); QVERIFY(tunnelProxySocketOneDataSpy.count() == 1); arguments = tunnelProxySocketOneDataSpy.takeFirst(); QByteArray receivedTestData2 = arguments.at(0).toByteArray(); QVERIFY(receivedTestData2 == testData2); // ** Remote connection 2 ** QString clientTwoName = "Client two"; QUuid clientTwoUuid = QUuid::createUuid(); TunnelProxyRemoteConnection *remoteConnectionTwo = new TunnelProxyRemoteConnection(clientTwoUuid, clientTwoName, this); connect(remoteConnectionTwo, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){ remoteConnectionTwo->ignoreSslErrors(errors); }); remoteConnectionTwo->connectServer(m_serverUrlTunnelProxyTcp, serverUuid); // ** Tunnel proxy server socket 2 ** QSignalSpy remoteConnectedTwoSpy(tunnelProxyServer, &TunnelProxySocketServer::clientConnected); QVERIFY(remoteConnectedTwoSpy.wait()); QVERIFY(remoteConnectedTwoSpy.count() == 1); arguments = remoteConnectedTwoSpy.takeFirst(); TunnelProxySocket *tunnelProxySocketTwo = arguments.at(0).value(); QVERIFY(tunnelProxySocketTwo->clientName() == clientTwoName); QVERIFY(tunnelProxySocketTwo->clientUuid() == clientTwoUuid); QVERIFY(!tunnelProxySocketTwo->clientPeerAddress().isNull()); QVERIFY(tunnelProxySocketTwo->socketAddress() != 0x0000 && tunnelProxySocketTwo->socketAddress() != 0xFFFF); QVERIFY(tunnelProxySocketTwo->connected()); qDebug() << "Have socket two" << tunnelProxySocketTwo; // ** Send data in both directions // Socket -> Remote connection QSignalSpy remoteConnectionTwoDataSpy(remoteConnectionTwo, &TunnelProxyRemoteConnection::dataReady); tunnelProxySocketTwo->writeData(testData); QVERIFY(remoteConnectionTwoDataSpy.wait()); QVERIFY(remoteConnectionTwoDataSpy.count() == 1); arguments = remoteConnectionTwoDataSpy.takeFirst(); receivedTestData = arguments.at(0).toByteArray(); QVERIFY(receivedTestData == testData); // Remote connection -> Socket QSignalSpy tunnelProxySocketTwoDataSpy(tunnelProxySocketTwo, &TunnelProxySocket::dataReceived); remoteConnectionTwo->sendData(testData2); QVERIFY(tunnelProxySocketTwoDataSpy.wait()); QVERIFY(tunnelProxySocketTwoDataSpy.count() == 1); arguments = tunnelProxySocketTwoDataSpy.takeFirst(); receivedTestData2 = arguments.at(0).toByteArray(); QVERIFY(receivedTestData2 == testData2); Engine::instance()->tunnelProxyServer()->stopServer(); QTest::qWait(100); QVERIFY(!tunnelProxyServer->running()); QVERIFY(!remoteConnectionOne->remoteConnected()); QVERIFY(!remoteConnectionTwo->remoteConnected()); // Clean up tunnelProxyServer->deleteLater(); remoteConnectionOne->deleteLater(); remoteConnectionTwo->deleteLater(); resetDebugCategories(); stopServer(); } QTEST_MAIN(RemoteProxyTestsTunnelProxy)