From 8b09f26ac338f3719528b7e6281664604c368d87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 12 Aug 2021 13:54:08 +0200 Subject: [PATCH 1/9] Add tunnel proxy server --- libnymea-core/libnymea-core.pro | 2 + libnymea-core/nymeaconfiguration.cpp | 34 ++++ libnymea-core/nymeaconfiguration.h | 8 + libnymea-core/servermanager.cpp | 50 ++++++ libnymea-core/servermanager.h | 6 + libnymea-core/servers/tunnelproxyserver.cpp | 169 ++++++++++++++++++++ libnymea-core/servers/tunnelproxyserver.h | 88 ++++++++++ 7 files changed, 357 insertions(+) create mode 100644 libnymea-core/servers/tunnelproxyserver.cpp create mode 100644 libnymea-core/servers/tunnelproxyserver.h diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro index e071fd8c..9436d7a6 100644 --- a/libnymea-core/libnymea-core.pro +++ b/libnymea-core/libnymea-core.pro @@ -100,6 +100,7 @@ HEADERS += nymeacore.h \ servers/bluetoothserver.h \ servers/websocketserver.h \ servers/mqttbroker.h \ + servers/tunnelproxyserver.h \ jsonrpc/jsonrpcserverimplementation.h \ jsonrpc/jsonvalidator.h \ jsonrpc/integrationshandler.h \ @@ -191,6 +192,7 @@ SOURCES += nymeacore.cpp \ servers/websocketserver.cpp \ servers/bluetoothserver.cpp \ servers/mqttbroker.cpp \ + servers/tunnelproxyserver.cpp \ jsonrpc/jsonrpcserverimplementation.cpp \ jsonrpc/jsonvalidator.cpp \ jsonrpc/integrationshandler.cpp \ diff --git a/libnymea-core/nymeaconfiguration.cpp b/libnymea-core/nymeaconfiguration.cpp index bc230b1b..31368cd4 100644 --- a/libnymea-core/nymeaconfiguration.cpp +++ b/libnymea-core/nymeaconfiguration.cpp @@ -211,6 +211,21 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) : mqttPolicies.endGroup(); } + // Tunnel Proxy Server + // FIXME: maybe configure default proxy + if (settings.childGroups().contains("TunnelProxyServer")) { + settings.beginGroup("TunnelProxyServer"); + if (settings.value("disabled").toBool()) { + qCDebug(dcConfiguration) << "Tunnel proxy server disabled by configuration."; + } else if (!settings.childGroups().isEmpty()) { + foreach (const QString &key, settings.childGroups()) { + ServerConfiguration config = readServerConfig("TunnelProxyServer", key); + m_tunnelProxyServerConfigs[config.id] = config; + } + } + settings.endGroup(); + } + // Write defaults for log settings settings.beginGroup("Logs"); settings.setValue("logDBDriver", logDBDriver()); @@ -366,6 +381,25 @@ void NymeaConfiguration::removeWebSocketServerConfiguration(const QString &id) emit webSocketServerConfigurationRemoved(id); } +QHash NymeaConfiguration::tunnelProxyServerConfigurations() const +{ + return m_tunnelProxyServerConfigs; +} + +void NymeaConfiguration::setTunnelProxyServerConfiguration(const ServerConfiguration &config) +{ + m_tunnelProxyServerConfigs[config.id] = config; + storeServerConfig("TunnelProxyServer", config); + emit tunnelProxyServerConfigurationChanged(config.id); +} + +void NymeaConfiguration::removeTunnelProxyServerConfiguration(const QString &id) +{ + m_tunnelProxyServerConfigs.take(id); + deleteServerConfig("TunnelProxyServer", id); + emit tunnelProxyServerConfigurationRemoved(id); +} + QHash NymeaConfiguration::mqttServerConfigurations() const { return m_mqttServerConfigs; diff --git a/libnymea-core/nymeaconfiguration.h b/libnymea-core/nymeaconfiguration.h index 5ba15e2c..fee084d3 100644 --- a/libnymea-core/nymeaconfiguration.h +++ b/libnymea-core/nymeaconfiguration.h @@ -147,6 +147,11 @@ public: void setWebSocketServerConfiguration(const ServerConfiguration &config); void removeWebSocketServerConfiguration(const QString &id); + // Tunnel proxy server + QHash tunnelProxyServerConfigurations() const; + void setTunnelProxyServerConfiguration(const ServerConfiguration &config); + void removeTunnelProxyServerConfiguration(const QString &id); + // MQTT QHash mqttServerConfigurations() const; void setMqttServerConfiguration(const ServerConfiguration &config); @@ -187,6 +192,7 @@ private: QHash m_webSocketServerConfigs; QHash m_mqttServerConfigs; QHash m_mqttPolicies; + QHash m_tunnelProxyServerConfigs; void setServerUuid(const QUuid &uuid); void setWebServerPublicFolder(const QString & path); @@ -216,6 +222,8 @@ signals: void webSocketServerConfigurationRemoved(const QString &configId); void mqttServerConfigurationChanged(const QString &configId); void mqttServerConfigurationRemoved(const QString &configId); + void tunnelProxyServerConfigurationChanged(const QString &configId); + void tunnelProxyServerConfigurationRemoved(const QString &configId); void mqttPolicyChanged(const QString &clientId); void mqttPolicyRemoved(const QString &clientId); diff --git a/libnymea-core/servermanager.cpp b/libnymea-core/servermanager.cpp index dabdc32c..9e6ab07c 100644 --- a/libnymea-core/servermanager.cpp +++ b/libnymea-core/servermanager.cpp @@ -57,6 +57,7 @@ #include "servers/webserver.h" #include "servers/bluetoothserver.h" #include "servers/mqttbroker.h" +#include "servers/tunnelproxyserver.h" #include "network/zeroconf/zeroconfservicepublisher.h" @@ -165,6 +166,28 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati m_bluetoothServer->startServer(); } + foreach (const ServerConfiguration &config, configuration->tunnelProxyServerConfigurations()) { + TunnelProxyServer *tunnelProxyServer = new TunnelProxyServer(configuration->serverName(), configuration->serverUuid(), config, this); + qCDebug(dcServerManager()) << "Creating tunnel proxy server" << config; + m_tunnelProxyServers.insert(config.id, tunnelProxyServer); + connect(tunnelProxyServer, &TunnelProxyServer::runningChanged, this, [this, tunnelProxyServer](bool running){ + if (running) { + // Note: enable authentication in any case, we don't want to expose unprotected access trough the internet + m_jsonServer->registerTransportInterface(tunnelProxyServer, true); + } else { + m_jsonServer->unregisterTransportInterface(tunnelProxyServer); + } + }); + + // FIXME: make use of SSL over tunnel proxy connections + // FIXME: make sure the authentication is enabled for the tunnel proxy + + // Note: only start the tunnel proxy server if cloud is connected + if (configuration->cloudEnabled()) { + tunnelProxyServer->startServer(); + } + } + foreach (const WebServerConfiguration &config, configuration->webServerConfigurations()) { WebServer *webServer = new WebServer(config, m_sslConfiguration, this); m_webServers.insert(config.id, webServer); @@ -191,6 +214,9 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati connect(configuration, &NymeaConfiguration::mqttServerConfigurationRemoved, this, &ServerManager::mqttServerConfigurationRemoved); connect(configuration, &NymeaConfiguration::mqttPolicyChanged, this, &ServerManager::mqttPolicyChanged); connect(configuration, &NymeaConfiguration::mqttPolicyRemoved, this, &ServerManager::mqttPolicyRemoved); + connect(configuration, &NymeaConfiguration::tunnelProxyServerConfigurationChanged, this, &ServerManager::tunnelProxyServerConfigurationChanged); + connect(configuration, &NymeaConfiguration::tunnelProxyServerConfigurationRemoved, this, &ServerManager::tunnelProxyServerConfigurationRemoved); + connect(configuration, &NymeaConfiguration::cloudEnabledChanged, this, &ServerManager::cloudEnabledChanged); } /*! Returns the pointer to the created \l{JsonRPCServer} in this \l{ServerManager}. */ @@ -341,6 +367,30 @@ void ServerManager::mqttPolicyRemoved(const QString &clientId) m_mqttBroker->removePolicy(clientId); } +void ServerManager::tunnelProxyServerConfigurationChanged(const QString &id) +{ + // FIXME: not dynamic configurable for now + Q_UNUSED(id) +} + +void ServerManager::tunnelProxyServerConfigurationRemoved(const QString &id) +{ + // FIXME: not dynamic configurable for now + Q_UNUSED(id) +} + +void ServerManager::cloudEnabledChanged(bool enabled) +{ + qCDebug(dcServerManager()) << "Cloud connection" << (enabled ? "enabled. Starting tunnel proxy servers" : "disabled. Stopping tunnel proxy servers."); + foreach (TunnelProxyServer *server, m_tunnelProxyServers) { + if (enabled) { + server->startServer(); + } else { + server->stopServer(); + } + } +} + bool ServerManager::registerZeroConfService(const ServerConfiguration &configuration, const QString &serverType, const QString &serviceType) { // Note: reversed order diff --git a/libnymea-core/servermanager.h b/libnymea-core/servermanager.h index 90469170..49438294 100644 --- a/libnymea-core/servermanager.h +++ b/libnymea-core/servermanager.h @@ -50,6 +50,7 @@ class WebSocketServer; class WebServer; class BluetoothServer; class MqttBroker; +class TunnelProxyServer; class MockTcpServer; @@ -79,6 +80,10 @@ private slots: void mqttServerConfigurationRemoved(const QString &id); void mqttPolicyChanged(const QString &clientId); void mqttPolicyRemoved(const QString &clientId); + void tunnelProxyServerConfigurationChanged(const QString &id); + void tunnelProxyServerConfigurationRemoved(const QString &id); + + void cloudEnabledChanged(bool enabled); private: bool registerZeroConfService(const ServerConfiguration &configuration, const QString &serverType, const QString &serviceType); @@ -94,6 +99,7 @@ private: QHash m_tcpServers; QHash m_webSocketServers; QHash m_webServers; + QHash m_tunnelProxyServers; MockTcpServer *m_mockTcpServer; MqttBroker *m_mqttBroker; diff --git a/libnymea-core/servers/tunnelproxyserver.cpp b/libnymea-core/servers/tunnelproxyserver.cpp new file mode 100644 index 00000000..100d3e85 --- /dev/null +++ b/libnymea-core/servers/tunnelproxyserver.cpp @@ -0,0 +1,169 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 "tunnelproxyserver.h" +#include "loggingcategories.h" + +NYMEA_LOGGING_CATEGORY(dcTunnelProxyServer, "TunnelProxyServer") + +namespace nymeaserver { + +TunnelProxyServer::TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const ServerConfiguration &configuration, QObject *parent) : + TransportInterface(configuration, parent), + m_serverName(serverName), + m_serverUuid(serverUuid) +{ + // Note: the authentication must always be enabled on the tunnel proxy server + if (!configuration.authenticationEnabled) { + qCWarning(dcTunnelProxyServer()) << "============================================================================================================================="; + qCWarning(dcTunnelProxyServer()) << "WARNING! The tunnel proxy server has authentication disabled! This is very dangerous and exposes your system to the internet."; + qCWarning(dcTunnelProxyServer()) << "============================================================================================================================="; + } + + // Default to ssl + m_serverUrl.setScheme("ssl"); + m_serverUrl.setHost(configuration.address.toString()); + m_serverUrl.setPort(configuration.port); + + m_tunnelProxySocketServer = new TunnelProxySocketServer(m_serverUuid, m_serverName, this); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::stateChanged, this, &TunnelProxyServer::onStateChanged); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::runningChanged, this, &TunnelProxyServer::onServerRunningChanged); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::errorOccured, this, &TunnelProxyServer::onErrorOccured); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::serverErrorOccured, this, &TunnelProxyServer::onServerErrorOccured); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::sslErrors, this, &TunnelProxyServer::onSslErrors); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::clientConnected, this, &TunnelProxyServer::onClientConnected); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::clientDisconnected, this, &TunnelProxyServer::onClientDisconnected); + +} + +TunnelProxyServer::~TunnelProxyServer() +{ + stopServer(); +} + +void TunnelProxyServer::sendData(const QUuid &clientId, const QByteArray &data) +{ + TunnelProxySocket *tunnelProxySocket = m_clients.value(clientId); + if (!tunnelProxySocket) { + qCWarning(dcTunnelProxyServer()) << "Failed to send data to client" << clientId.toString() << "because there is no tunnel socket for this client UUID."; + return; + } + + tunnelProxySocket->writeData(data); +} + +void TunnelProxyServer::sendData(const QList &clients, const QByteArray &data) +{ + foreach (const QUuid &client, clients) { + sendData(client, data); + } +} + +void TunnelProxyServer::terminateClientConnection(const QUuid &clientId) +{ + TunnelProxySocket *tunnelProxySocket = m_clients.value(clientId); + if (tunnelProxySocket) { + tunnelProxySocket->disconnectSocket(); + } +} + +void TunnelProxyServer::setServerName(const QString &serverName) +{ + m_serverName = serverName; +} + +bool TunnelProxyServer::startServer() +{ + + m_tunnelProxySocketServer->startServer(m_serverUrl); + return true; +} + +bool TunnelProxyServer::stopServer() +{ + m_tunnelProxySocketServer->stopServer(); + return true; +} + +void TunnelProxyServer::onStateChanged(TunnelProxySocketServer::State state) +{ + if (state == TunnelProxySocketServer::StateRegister) { + qCDebug(dcTunnelProxyServer()) << "Connected with" << m_tunnelProxySocketServer->remoteProxyServer() + << m_tunnelProxySocketServer->remoteProxyServerName() + << m_tunnelProxySocketServer->remoteProxyServerVersion() + << "API:" << m_tunnelProxySocketServer->remoteProxyApiVersion(); + } +} + +void TunnelProxyServer::onServerRunningChanged(bool running) +{ + qCDebug(dcTunnelProxyServer()) << "The server is" << (running ? "now listening for incomming connections on " + m_serverUuid.toString() : "not running any more."); + emit runningChanged(running); +} + +void TunnelProxyServer::onErrorOccured(QAbstractSocket::SocketError error) +{ + qCDebug(dcTunnelProxyServer()) << "Remote proxy connection error occured" << error; +} + +void TunnelProxyServer::onServerErrorOccured(TunnelProxySocketServer::Error error) +{ + qCWarning(dcTunnelProxyServer()) << "Error occured on server" << m_serverUrl.toString() << error; +} + +void TunnelProxyServer::onSslErrors(const QList &errors) +{ + qCDebug(dcTunnelProxyServer()) << "Remote proxy connection SSL errors occured" << errors; + // FIXME: make this configurable + m_tunnelProxySocketServer->ignoreSslErrors(); +} + +void TunnelProxyServer::onClientConnected(TunnelProxySocket *tunnelProxySocket) +{ + QUuid clientId = QUuid::createUuid(); + qCDebug(dcTunnelProxyServer()) << "Client connected:" << clientId.toString() << tunnelProxySocket->clientName() << "(Remote address:" << tunnelProxySocket->clientPeerAddress().toString() << ")"; + m_clients.insert(clientId, tunnelProxySocket); + + connect(tunnelProxySocket, &TunnelProxySocket::dataReceived, this, [this, clientId](const QByteArray &data){ + emit dataAvailable(clientId, data); + }); + + emit clientConnected(clientId); +} + +void TunnelProxyServer::onClientDisconnected(TunnelProxySocket *tunnelProxySocket) +{ + QUuid clientId = m_clients.key(tunnelProxySocket); + qCDebug(dcTunnelProxyServer()) << "Client disconnected:" << clientId.toString() << tunnelProxySocket->clientName() << "(Remote address:" << tunnelProxySocket->clientPeerAddress().toString() << ")"; + m_clients.remove(clientId); + emit clientDisconnected(clientId); +} + +} diff --git a/libnymea-core/servers/tunnelproxyserver.h b/libnymea-core/servers/tunnelproxyserver.h new file mode 100644 index 00000000..cbe66bd9 --- /dev/null +++ b/libnymea-core/servers/tunnelproxyserver.h @@ -0,0 +1,88 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +* +* Copyright 2013 - 2021, 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 +* +* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#ifndef TUNNELPROXYSERVER_H +#define TUNNELPROXYSERVER_H + +#include +#include + +#include "transportinterface.h" + +#include +#include + +namespace nymeaserver { + +using namespace remoteproxyclient; + +class TunnelProxyServer : public TransportInterface +{ + Q_OBJECT +public: + explicit TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const ServerConfiguration &configuration, QObject *parent = nullptr); + ~TunnelProxyServer() override; + + void sendData(const QUuid &clientId, const QByteArray &data) override; + void sendData(const QList &clients, const QByteArray &data) override; + + void terminateClientConnection(const QUuid &clientId) override; + +public slots: + void setServerName(const QString &serverName) override; + bool startServer() override; + bool stopServer() override; + +signals: + void runningChanged(bool running); + +private slots: + void onStateChanged(TunnelProxySocketServer::State state); + void onServerRunningChanged(bool running); + void onErrorOccured(QAbstractSocket::SocketError error); + void onServerErrorOccured(TunnelProxySocketServer::Error error); + void onSslErrors(const QList &errors); + + void onClientConnected(TunnelProxySocket *tunnelProxySocket); + void onClientDisconnected(TunnelProxySocket *tunnelProxySocket); + +private: + TunnelProxySocketServer *m_tunnelProxySocketServer = nullptr; + QString m_serverName; + QUuid m_serverUuid; + QUrl m_serverUrl; + + QHash m_clients; + +}; + +} + +#endif // TUNNELPROXYSERVER_H From 106961557b74d8a161b61bd899f947f2fa4f7cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 16 Aug 2021 07:57:34 +0200 Subject: [PATCH 2/9] Fix typo in several places --- .../hardware/modbus/modbusrtumasterimpl.cpp | 2 +- libnymea-core/servers/tunnelproxyserver.cpp | 14 +++++++------- libnymea-core/servers/tunnelproxyserver.h | 4 ++-- libnymea-core/transportinterface.cpp | 1 + libnymea-core/zigbee/zigbeemanager.cpp | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/libnymea-core/hardware/modbus/modbusrtumasterimpl.cpp b/libnymea-core/hardware/modbus/modbusrtumasterimpl.cpp index eab5c4c7..a11bf6e7 100644 --- a/libnymea-core/hardware/modbus/modbusrtumasterimpl.cpp +++ b/libnymea-core/hardware/modbus/modbusrtumasterimpl.cpp @@ -79,7 +79,7 @@ ModbusRtuMasterImpl::ModbusRtuMasterImpl(const QUuid &modbusUuid, const QString }); connect(m_modbus, &QModbusRtuSerialMaster::errorOccurred, this, [=](QModbusDevice::Error error){ - qCWarning(dcModbusRtu()) << "Error occured for modbus RTU master" << m_modbusUuid.toString() << m_serialPort << error << m_modbus->errorString(); + qCWarning(dcModbusRtu()) << "Error occurred for modbus RTU master" << m_modbusUuid.toString() << m_serialPort << error << m_modbus->errorString(); if (error != QModbusDevice::NoError) { disconnectDevice(); } diff --git a/libnymea-core/servers/tunnelproxyserver.cpp b/libnymea-core/servers/tunnelproxyserver.cpp index 100d3e85..8ef784a0 100644 --- a/libnymea-core/servers/tunnelproxyserver.cpp +++ b/libnymea-core/servers/tunnelproxyserver.cpp @@ -55,8 +55,8 @@ TunnelProxyServer::TunnelProxyServer(const QString &serverName, const QUuid &ser m_tunnelProxySocketServer = new TunnelProxySocketServer(m_serverUuid, m_serverName, this); connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::stateChanged, this, &TunnelProxyServer::onStateChanged); connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::runningChanged, this, &TunnelProxyServer::onServerRunningChanged); - connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::errorOccured, this, &TunnelProxyServer::onErrorOccured); - connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::serverErrorOccured, this, &TunnelProxyServer::onServerErrorOccured); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::errorOccurred, this, &TunnelProxyServer::onErrorOccurred); + connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::serverErrorOccurred, this, &TunnelProxyServer::onServerErrorOccurred); connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::sslErrors, this, &TunnelProxyServer::onSslErrors); connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::clientConnected, this, &TunnelProxyServer::onClientConnected); connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::clientDisconnected, this, &TunnelProxyServer::onClientDisconnected); @@ -128,19 +128,19 @@ void TunnelProxyServer::onServerRunningChanged(bool running) emit runningChanged(running); } -void TunnelProxyServer::onErrorOccured(QAbstractSocket::SocketError error) +void TunnelProxyServer::onErrorOccurred(QAbstractSocket::SocketError error) { - qCDebug(dcTunnelProxyServer()) << "Remote proxy connection error occured" << error; + qCDebug(dcTunnelProxyServer()) << "Remote proxy connection error occurred" << error; } -void TunnelProxyServer::onServerErrorOccured(TunnelProxySocketServer::Error error) +void TunnelProxyServer::onServerErrorOccurred(TunnelProxySocketServer::Error error) { - qCWarning(dcTunnelProxyServer()) << "Error occured on server" << m_serverUrl.toString() << error; + qCWarning(dcTunnelProxyServer()) << "Error occurred on server" << m_serverUrl.toString() << error; } void TunnelProxyServer::onSslErrors(const QList &errors) { - qCDebug(dcTunnelProxyServer()) << "Remote proxy connection SSL errors occured" << errors; + qCDebug(dcTunnelProxyServer()) << "Remote proxy connection SSL errors occurred" << errors; // FIXME: make this configurable m_tunnelProxySocketServer->ignoreSslErrors(); } diff --git a/libnymea-core/servers/tunnelproxyserver.h b/libnymea-core/servers/tunnelproxyserver.h index cbe66bd9..06059fb5 100644 --- a/libnymea-core/servers/tunnelproxyserver.h +++ b/libnymea-core/servers/tunnelproxyserver.h @@ -66,8 +66,8 @@ signals: private slots: void onStateChanged(TunnelProxySocketServer::State state); void onServerRunningChanged(bool running); - void onErrorOccured(QAbstractSocket::SocketError error); - void onServerErrorOccured(TunnelProxySocketServer::Error error); + void onErrorOccurred(QAbstractSocket::SocketError error); + void onServerErrorOccurred(TunnelProxySocketServer::Error error); void onSslErrors(const QList &errors); void onClientConnected(TunnelProxySocket *tunnelProxySocket); diff --git a/libnymea-core/transportinterface.cpp b/libnymea-core/transportinterface.cpp index 3689d360..2324d468 100644 --- a/libnymea-core/transportinterface.cpp +++ b/libnymea-core/transportinterface.cpp @@ -98,6 +98,7 @@ TransportInterface::TransportInterface(const ServerConfiguration &config, QObjec QObject(parent), m_config(config) { + } /*! Set the ServerConfiguration of this TransportInterface to the given \a config. */ diff --git a/libnymea-core/zigbee/zigbeemanager.cpp b/libnymea-core/zigbee/zigbeemanager.cpp index 3b5a68fe..2f2ae3fb 100644 --- a/libnymea-core/zigbee/zigbeemanager.cpp +++ b/libnymea-core/zigbee/zigbeemanager.cpp @@ -457,7 +457,7 @@ void ZigbeeManager::addNetwork(ZigbeeNetwork *network) }); connect(network, &ZigbeeNetwork::errorOccured, this, [this, network](ZigbeeNetwork::Error error){ - qCWarning(dcZigbee()) << "Network error occured for" << network << error; + qCWarning(dcZigbee()) << "Network error occurred for" << network << error; evaluateZigbeeAvailable(); // TODO: handle error }); From fea2ae0866cafffa08584945efbadbec17fa6233 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 20 Dec 2021 15:00:35 +0100 Subject: [PATCH 3/9] Add new line for json rpc data sending --- libnymea-core/servers/tunnelproxyserver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libnymea-core/servers/tunnelproxyserver.cpp b/libnymea-core/servers/tunnelproxyserver.cpp index 8ef784a0..9b1b4c84 100644 --- a/libnymea-core/servers/tunnelproxyserver.cpp +++ b/libnymea-core/servers/tunnelproxyserver.cpp @@ -76,7 +76,8 @@ void TunnelProxyServer::sendData(const QUuid &clientId, const QByteArray &data) return; } - tunnelProxySocket->writeData(data); + // Note: we add a \n at the end of the data for easier json parsing on the other end + tunnelProxySocket->writeData(data + '\n'); } void TunnelProxyServer::sendData(const QList &clients, const QByteArray &data) @@ -108,6 +109,7 @@ bool TunnelProxyServer::startServer() bool TunnelProxyServer::stopServer() { + qCDebug(dcTunnelProxyServer()) << "Stopping server"; m_tunnelProxySocketServer->stopServer(); return true; } From 47add31e63dd2041083d5f82c6b35fc2bdd670a9 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 20 Jan 2022 16:59:56 +0100 Subject: [PATCH 4/9] Add configuration for the tunnel proxy server --- .../mqtt/mqttproviderimplementation.cpp | 16 ++-- .../jsonrpc/configurationhandler.cpp | 91 ++++++++++++++++++- libnymea-core/jsonrpc/configurationhandler.h | 6 ++ libnymea-core/nymeaconfiguration.cpp | 68 +++++++++++--- libnymea-core/nymeaconfiguration.h | 23 +++-- libnymea-core/servermanager.cpp | 35 +++++-- libnymea-core/servermanager.h | 13 +-- libnymea-core/servers/mqttbroker.cpp | 2 +- libnymea-core/servers/tcpserver.cpp | 6 +- libnymea-core/servers/tunnelproxyserver.cpp | 24 +++-- libnymea-core/servers/tunnelproxyserver.h | 3 +- libnymea-core/servers/webserver.cpp | 8 +- libnymea-core/servers/websocketserver.cpp | 4 +- tests/auto/api.json | 45 ++++++++- .../configurations/testconfigurations.cpp | 4 +- tests/auto/mqttbroker/testmqttbroker.cpp | 3 +- tests/auto/webserver/testwebserver.cpp | 6 +- .../websocketserver/testwebsocketserver.cpp | 4 +- 18 files changed, 283 insertions(+), 78 deletions(-) diff --git a/libnymea-core/hardware/network/mqtt/mqttproviderimplementation.cpp b/libnymea-core/hardware/network/mqtt/mqttproviderimplementation.cpp index 9bc0ab5b..11c66f0d 100644 --- a/libnymea-core/hardware/network/mqtt/mqttproviderimplementation.cpp +++ b/libnymea-core/hardware/network/mqtt/mqttproviderimplementation.cpp @@ -94,7 +94,7 @@ MqttChannel *MqttProviderImplementation::createChannel(const QString &clientId, foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) { if (clientAddress.isInSubnet(addressEntry.ip(), addressEntry.prefixLength())) { foreach (const ServerConfiguration &config, m_broker->configurations()) { - if (config.address == QHostAddress("0.0.0.0") || clientAddress.isInSubnet(config.address, addressEntry.prefixLength())) { + if (QHostAddress(config.address) == QHostAddress("0.0.0.0") || clientAddress.isInSubnet(QHostAddress(config.address), addressEntry.prefixLength())) { channel->m_serverAddress = addressEntry.ip(); channel->m_serverPort = config.port; break; @@ -145,9 +145,9 @@ MqttClient *MqttProviderImplementation::createInternalClient(const QString &clie ServerConfiguration preferredConfig; foreach (const ServerConfiguration &config, m_broker->configurations()) { - if (config.address == QHostAddress::Any - || config.address == QHostAddress::AnyIPv4 - || config.address == QHostAddress::LocalHost) { + if (QHostAddress(config.address) == QHostAddress::Any + || QHostAddress(config.address) == QHostAddress::AnyIPv4 + || QHostAddress(config.address) == QHostAddress::LocalHost) { preferredConfig = config; break; } @@ -176,12 +176,12 @@ MqttClient *MqttProviderImplementation::createInternalClient(const QString &clie m_broker->removePolicy(clientId); }); - if (preferredConfig.address == QHostAddress::Any - || preferredConfig.address == QHostAddress::AnyIPv4 - || preferredConfig.address == QHostAddress::LocalHost) { + if (QHostAddress(preferredConfig.address) == QHostAddress::Any + || QHostAddress(preferredConfig.address) == QHostAddress::AnyIPv4 + || QHostAddress(preferredConfig.address) == QHostAddress::LocalHost) { client->connectToHost("127.0.0.1", preferredConfig.port); } else { - client->connectToHost(preferredConfig.address.toString(), preferredConfig.port); + client->connectToHost(preferredConfig.address, preferredConfig.port); } return client; } diff --git a/libnymea-core/jsonrpc/configurationhandler.cpp b/libnymea-core/jsonrpc/configurationhandler.cpp index eb25923f..290ae6ed 100644 --- a/libnymea-core/jsonrpc/configurationhandler.cpp +++ b/libnymea-core/jsonrpc/configurationhandler.cpp @@ -86,6 +86,7 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): // Objects registerObject(); registerObject(); + registerObject(); registerObject(); // Methods @@ -118,12 +119,15 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): QVariantList webSocketServerConfigurations; webSocketServerConfigurations.append(objectRef()); returns.insert("webSocketServerConfigurations", webSocketServerConfigurations); + QVariantList tunnelProxyServerConfigurations; + tunnelProxyServerConfigurations.append(objectRef()); + returns.insert("tunnelProxyServerConfigurations", tunnelProxyServerConfigurations); QVariantList mqttServerConfigurations; mqttServerConfigurations.append(objectRef()); QVariantMap cloudConfiguration; cloudConfiguration.insert("enabled", enumValueName(Bool)); returns.insert("cloud", cloudConfiguration); - registerMethod("GetConfigurations", description, params, returns); + registerMethod("GetConfigurations", description, params, returns, Types::PermissionScopeNone); params.clear(); returns.clear(); description = "Set the name of the server. Default is nymea."; @@ -173,6 +177,18 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): returns.insert("configurationError", enumRef()); registerMethod("DeleteWebSocketServerConfiguration", description, params, returns); + params.clear(); returns.clear(); + description = "Configure a Tunnel Proxy Server interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped."; + params.insert("configuration", objectRef()); + returns.insert("configurationError", enumRef()); + registerMethod("SetTunnelProxyServerConfiguration", description, params, returns); + + params.clear(); returns.clear(); + description = "Delete a Tunnel Proxy Server interface of the server. Note: if you are deleting the configuration for the interface you are currently connected to, the connection will be dropped."; + params.insert("id", enumValueName(String)); + returns.insert("configurationError", enumRef()); + registerMethod("DeleteTunnelProxyServerConfiguration", description, params, returns); + params.clear(); returns.clear(); description = "Configure a WebServer interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added."; params.insert("configuration", objectRef()); @@ -257,6 +273,16 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): params.insert("id", enumValueName(String)); registerNotification("WebSocketServerConfigurationRemoved", description, params); + params.clear(); returns.clear(); + description = "Emitted whenever the tunnel proxy server configuration changes."; + params.insert("tunnelProxyServerConfiguration", objectRef()); + registerNotification("TunnelProxyServerConfigurationChanged", description, params); + + params.clear(); returns.clear(); + description = "Emitted whenever a tunnel proxy server configuration is removed."; + params.insert("id", enumValueName(String)); + registerNotification("TunnelProxyServerConfigurationRemoved", description, params); + params.clear(); returns.clear(); description = "Emitted whenever the MQTT broker configuration is changed."; params.insert("mqttServerConfiguration", objectRef()); @@ -303,6 +329,8 @@ ConfigurationHandler::ConfigurationHandler(QObject *parent): connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::webServerConfigurationRemoved, this, &ConfigurationHandler::onWebServerConfigurationRemoved); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::webSocketServerConfigurationChanged, this, &ConfigurationHandler::onWebSocketServerConfigurationChanged); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::webSocketServerConfigurationRemoved, this, &ConfigurationHandler::onWebSocketServerConfigurationRemoved); + connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::tunnelProxyServerConfigurationChanged, this, &ConfigurationHandler::onTunnelProxyServerConfigurationChanged); + connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::tunnelProxyServerConfigurationRemoved, this, &ConfigurationHandler::onTunnelProxyServerConfigurationRemoved); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::mqttServerConfigurationChanged, this, &ConfigurationHandler::onMqttServerConfigurationChanged); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::mqttServerConfigurationRemoved, this, &ConfigurationHandler::onMqttServerConfigurationRemoved); connect(NymeaCore::instance()->configuration(), &NymeaConfiguration::mqttPolicyChanged, this, &ConfigurationHandler::onMqttPolicyChanged); @@ -340,6 +368,12 @@ JsonReply *ConfigurationHandler::GetConfigurations(const QVariantMap ¶ms) co } returns.insert("webSocketServerConfigurations", webSocketServerConfigs); + QVariantList tunnelProxyServerConfigs; + foreach (const TunnelProxyServerConfiguration &config, NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations()) { + tunnelProxyServerConfigs.append(pack(config)); + } + returns.insert("tunnelProxyServerConfigurations", tunnelProxyServerConfigs); + QVariantMap cloudConfig; cloudConfig.insert("enabled", NymeaCore::instance()->configuration()->cloudEnabled()); returns.insert("cloud", cloudConfig); @@ -422,7 +456,7 @@ JsonReply *ConfigurationHandler::SetTcpServerConfiguration(const QVariantMap &pa return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort)); } - qCDebug(dcJsonRpc()) << QString("Configure TCP server %1:%2").arg(config.address.toString()).arg(config.port); + qCDebug(dcJsonRpc()) << QString("Configure TCP server %1:%2").arg(config.address).arg(config.port); NymeaCore::instance()->configuration()->setTcpServerConfiguration(config); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError)); @@ -453,7 +487,7 @@ JsonReply *ConfigurationHandler::SetWebServerConfiguration(const QVariantMap &pa return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort)); } - qCDebug(dcJsonRpc()) << QString("Configure web server %1:%2").arg(config.address.toString()).arg(config.port); + qCDebug(dcJsonRpc()) << QString("Configure web server %1:%2").arg(config.address).arg(config.port); NymeaCore::instance()->configuration()->setWebServerConfiguration(config); return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError)); @@ -483,7 +517,7 @@ JsonReply *ConfigurationHandler::SetWebSocketServerConfiguration(const QVariantM return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort)); } - qCDebug(dcJsonRpc()) << QString("Configuring web socket server %1:%2").arg(config.address.toString()).arg(config.port); + qCDebug(dcJsonRpc()) << QString("Configuring web socket server %1:%2").arg(config.address).arg(config.port); NymeaCore::instance()->configuration()->setWebSocketServerConfiguration(config); @@ -500,6 +534,37 @@ JsonReply *ConfigurationHandler::DeleteWebSocketServerConfiguration(const QVaria return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError)); } +JsonReply *ConfigurationHandler::SetTunnelProxyServerConfiguration(const QVariantMap ¶ms) const +{ + TunnelProxyServerConfiguration config = unpack(params.value("configuration").toMap()); + if (config.id.isEmpty()) { + return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId)); + } + if (config.address.isNull()) + return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidHostAddress)); + + if (config.port <= 0 || config.port > 65535) { + qCWarning(dcJsonRpc()) << "Port out of range"; + return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort)); + } + + qCDebug(dcJsonRpc()) << QString("Configuring tunnel proxy server %1:%2").arg(config.address).arg(config.port); + + NymeaCore::instance()->configuration()->setTunnelProxyServerConfiguration(config); + + return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError)); +} + +JsonReply *ConfigurationHandler::DeleteTunnelProxyServerConfiguration(const QVariantMap ¶ms) const +{ + QString id = params.value("id").toString(); + if (id.isEmpty() || !NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations().contains(id)) { + return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidId)); + } + NymeaCore::instance()->configuration()->removeTunnelProxyServerConfiguration(id); + return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorNoError)); +} + JsonReply *ConfigurationHandler::GetMqttServerConfigurations(const QVariantMap ¶ms) const { Q_UNUSED(params) @@ -526,7 +591,7 @@ JsonReply *ConfigurationHandler::SetMqttServerConfiguration(const QVariantMap &p return createReply(statusToReply(NymeaConfiguration::ConfigurationErrorInvalidPort)); } - qCDebug(dcJsonRpc()) << QString("Configure MQTT server %1:%2").arg(config.address.toString()).arg(config.port); + qCDebug(dcJsonRpc()) << QString("Configure MQTT server %1:%2").arg(config.address).arg(config.port); NymeaCore::instance()->configuration()->setMqttServerConfiguration(config); @@ -639,6 +704,22 @@ void ConfigurationHandler::onWebSocketServerConfigurationRemoved(const QString & emit WebSocketServerConfigurationRemoved(params); } +void ConfigurationHandler::onTunnelProxyServerConfigurationChanged(const QString &id) +{ + qCDebug(dcJsonRpc()) << "Notification: Tunnel proxy server configuration changed"; + QVariantMap params; + params.insert("tunnelProxyServerConfiguration", pack(NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations().value(id))); + emit TunnelProxyServerConfigurationChanged(params); +} + +void ConfigurationHandler::onTunnelProxyServerConfigurationRemoved(const QString &id) +{ + qCDebug(dcJsonRpc()) << "Notification: Tunnel proxy server configuration removed"; + QVariantMap params; + params.insert("id", id); + emit TunnelProxyServerConfigurationRemoved(params); +} + void ConfigurationHandler::onMqttServerConfigurationChanged(const QString &id) { qCDebug(dcJsonRpc()) << "Notification: MQTT server configuration changed"; diff --git a/libnymea-core/jsonrpc/configurationhandler.h b/libnymea-core/jsonrpc/configurationhandler.h index 47bb5249..25edb0db 100644 --- a/libnymea-core/jsonrpc/configurationhandler.h +++ b/libnymea-core/jsonrpc/configurationhandler.h @@ -60,6 +60,8 @@ public: Q_INVOKABLE JsonReply *DeleteWebServerConfiguration(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *SetWebSocketServerConfiguration(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *DeleteWebSocketServerConfiguration(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply *SetTunnelProxyServerConfiguration(const QVariantMap ¶ms) const; + Q_INVOKABLE JsonReply *DeleteTunnelProxyServerConfiguration(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *GetMqttServerConfigurations(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *SetMqttServerConfiguration(const QVariantMap ¶ms) const; @@ -79,6 +81,8 @@ signals: void WebServerConfigurationRemoved(const QVariantMap ¶ms); void WebSocketServerConfigurationChanged(const QVariantMap ¶ms); void WebSocketServerConfigurationRemoved(const QVariantMap ¶ms); + void TunnelProxyServerConfigurationChanged(const QVariantMap ¶ms); + void TunnelProxyServerConfigurationRemoved(const QVariantMap ¶ms); void MqttServerConfigurationChanged(const QVariantMap ¶ms); void MqttServerConfigurationRemoved(const QVariantMap ¶ms); @@ -95,6 +99,8 @@ private slots: void onWebServerConfigurationRemoved(const QString &id); void onWebSocketServerConfigurationChanged(const QString &id); void onWebSocketServerConfigurationRemoved(const QString &id); + void onTunnelProxyServerConfigurationChanged(const QString &id); + void onTunnelProxyServerConfigurationRemoved(const QString &id); void onMqttServerConfigurationChanged(const QString &id); void onMqttServerConfigurationRemoved(const QString &id); void onMqttPolicyChanged(const QString &clientId); diff --git a/libnymea-core/nymeaconfiguration.cpp b/libnymea-core/nymeaconfiguration.cpp index 31368cd4..a8148624 100644 --- a/libnymea-core/nymeaconfiguration.cpp +++ b/libnymea-core/nymeaconfiguration.cpp @@ -93,7 +93,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) : qCWarning(dcConfiguration) << "No TCP server configuration found. Generating default of nymeas://0.0.0.0:2222"; ServerConfiguration config; config.id = "default"; - config.address = QHostAddress("0.0.0.0"); + config.address = QHostAddress("0.0.0.0").toString(); config.port = 2222; config.sslEnabled = true; config.authenticationEnabled = true; @@ -121,7 +121,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) : qCWarning(dcConfiguration) << "No Web server configuration found. Generating defaults of http://0.0.0.0:80 and https://0.0.0.0:443"; WebServerConfiguration insecureConfig; insecureConfig.id = "insecure"; - insecureConfig.address = QHostAddress("0.0.0.0"); + insecureConfig.address = QHostAddress("0.0.0.0").toString(); insecureConfig.port = 80; insecureConfig.sslEnabled = false; insecureConfig.authenticationEnabled = false; @@ -132,7 +132,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) : WebServerConfiguration secureConfig; secureConfig.id = "secure"; - secureConfig.address = QHostAddress("0.0.0.0"); + secureConfig.address = QHostAddress("0.0.0.0").toString(); secureConfig.port = 443; secureConfig.sslEnabled = true; secureConfig.authenticationEnabled = false; @@ -162,7 +162,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) : qCWarning(dcConfiguration) << "No WebSocket server configuration found. Generating default of wss://0.0.0.0:4444"; ServerConfiguration config; config.id = "default"; - config.address = QHostAddress("0.0.0.0"); + config.address = QHostAddress("0.0.0.0").toString(); config.port = 4444; config.sslEnabled = true; config.authenticationEnabled = true; @@ -190,7 +190,7 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) : qCWarning(dcConfiguration) << "No MQTT server configuration found. Generating default of 0.0.0.0:1883"; ServerConfiguration config; config.id = "default"; - config.address = QHostAddress("0.0.0.0"); + config.address = QHostAddress("0.0.0.0").toString(); config.port = 1883; config.sslEnabled = false; config.authenticationEnabled = true; @@ -212,19 +212,33 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) : } // Tunnel Proxy Server - // FIXME: maybe configure default proxy + createDefaults = !settings.childGroups().contains("TunnelProxyServer"); if (settings.childGroups().contains("TunnelProxyServer")) { settings.beginGroup("TunnelProxyServer"); if (settings.value("disabled").toBool()) { qCDebug(dcConfiguration) << "Tunnel proxy server disabled by configuration."; } else if (!settings.childGroups().isEmpty()) { foreach (const QString &key, settings.childGroups()) { - ServerConfiguration config = readServerConfig("TunnelProxyServer", key); + TunnelProxyServerConfiguration config = readTunnelProxyServerConfig(key); m_tunnelProxyServerConfigs[config.id] = config; } + } else { + createDefaults = true; } settings.endGroup(); } + if (createDefaults) { + qCWarning(dcConfiguration) << "No Tunnel Proxy server configuration found. Generating default of remoteproxy.nymea.io:2213"; + TunnelProxyServerConfiguration config; + config.id = "default"; + config.address = "remoteproxy.nymea.io"; + config.port = 2213; + config.sslEnabled = true; + config.authenticationEnabled = true; + config.ignoreSslErrors = false; + m_tunnelProxyServerConfigs[config.id] = config; + storeTunnelProxyServerConfig(config); + } // Write defaults for log settings settings.beginGroup("Logs"); @@ -381,12 +395,12 @@ void NymeaConfiguration::removeWebSocketServerConfiguration(const QString &id) emit webSocketServerConfigurationRemoved(id); } -QHash NymeaConfiguration::tunnelProxyServerConfigurations() const +QHash NymeaConfiguration::tunnelProxyServerConfigurations() const { return m_tunnelProxyServerConfigs; } -void NymeaConfiguration::setTunnelProxyServerConfiguration(const ServerConfiguration &config) +void NymeaConfiguration::setTunnelProxyServerConfiguration(const TunnelProxyServerConfiguration &config) { m_tunnelProxyServerConfigs[config.id] = config; storeServerConfig("TunnelProxyServer", config); @@ -661,7 +675,7 @@ void NymeaConfiguration::storeServerConfig(const QString &group, const ServerCon settings.beginGroup(group); settings.remove("disabled"); settings.beginGroup(config.id); - settings.setValue("address", config.address.toString()); + settings.setValue("address", config.address); settings.setValue("port", config.port); settings.setValue("sslEnabled", config.sslEnabled); settings.setValue("authenticationEnabled", config.authenticationEnabled); @@ -676,7 +690,7 @@ ServerConfiguration NymeaConfiguration::readServerConfig(const QString &group, c settings.beginGroup(group); settings.beginGroup(id); config.id = id; - config.address = QHostAddress(settings.value("address").toString()); + config.address = settings.value("address").toString(); config.port = settings.value("port").toUInt(); config.sslEnabled = settings.value("sslEnabled", true).toBool(); config.authenticationEnabled = settings.value("authenticationEnabled", true).toBool(); @@ -715,7 +729,7 @@ WebServerConfiguration NymeaConfiguration::readWebServerConfig(const QString &id settings.beginGroup("WebServer"); settings.beginGroup(id); config.id = id; - config.address = QHostAddress(settings.value("address").toString()); + config.address = settings.value("address").toString(); config.port = settings.value("port").toUInt(); config.sslEnabled = settings.value("sslEnabled", true).toBool(); config.authenticationEnabled = settings.value("authenticationEnabled", true).toBool(); @@ -726,6 +740,34 @@ WebServerConfiguration NymeaConfiguration::readWebServerConfig(const QString &id return config; } +void NymeaConfiguration::storeTunnelProxyServerConfig(const TunnelProxyServerConfiguration &config) +{ + storeServerConfig("TunnelProxyServer", config); + NymeaSettings settings(NymeaSettings::SettingsRoleGlobal); + settings.beginGroup("TunnelProxyServer"); + settings.beginGroup(config.id); + settings.setValue("ignoreSslErrors", config.ignoreSslErrors); + settings.endGroup(); + settings.endGroup(); +} + +TunnelProxyServerConfiguration NymeaConfiguration::readTunnelProxyServerConfig(const QString &id) +{ + TunnelProxyServerConfiguration config; + NymeaSettings settings(NymeaSettings::SettingsRoleGlobal); + settings.beginGroup("TunnelProxyServer"); + settings.beginGroup(id); + config.id = id; + config.address = settings.value("address").toString(); + config.port = settings.value("port").toUInt(); + config.sslEnabled = settings.value("sslEnabled", true).toBool(); + config.authenticationEnabled = settings.value("authenticationEnabled", true).toBool(); + config.ignoreSslErrors = settings.value("ignoreSslErrors").toBool(); + settings.endGroup(); + settings.endGroup(); + return config; +} + void NymeaConfiguration::storeMqttPolicy(const MqttPolicy &policy) { NymeaSettings settings(NymeaSettings::SettingsRoleMqttPolicies); @@ -747,7 +789,7 @@ QDebug operator <<(QDebug debug, const ServerConfiguration &configuration) { debug.nospace() << "ServerConfiguration(" << configuration.address; debug.nospace() << ", " << configuration.id; - debug.nospace() << ", " << QString("%1:%2").arg(configuration.address.toString()).arg(configuration.port); + debug.nospace() << ", " << QString("%1:%2").arg(configuration.address).arg(configuration.port); debug.nospace() << ") "; return debug; } diff --git a/libnymea-core/nymeaconfiguration.h b/libnymea-core/nymeaconfiguration.h index fee084d3..18fdc8f1 100644 --- a/libnymea-core/nymeaconfiguration.h +++ b/libnymea-core/nymeaconfiguration.h @@ -42,15 +42,14 @@ namespace nymeaserver { class ServerConfiguration { Q_GADGET Q_PROPERTY(QString id MEMBER id) - Q_PROPERTY(QString address READ addressString WRITE setAddress) + Q_PROPERTY(QString address MEMBER address) Q_PROPERTY(uint port MEMBER port) Q_PROPERTY(bool sslEnabled MEMBER sslEnabled) Q_PROPERTY(bool authenticationEnabled MEMBER authenticationEnabled) public: QString id; - QHostAddress address; - QString addressString() { return address.toString(); } - void setAddress(const QString &addressString) {address = QHostAddress(addressString); } + QString address; + void setAddress(const QString &address) {this->address = address; } uint port = 0; bool sslEnabled = true; bool authenticationEnabled = true; @@ -75,6 +74,14 @@ public: bool restServerEnabled = false; }; +class TunnelProxyServerConfiguration: public ServerConfiguration +{ + Q_GADGET + Q_PROPERTY(bool ignoreSslErrors MEMBER ignoreSslErrors) +public: + bool ignoreSslErrors = false; +}; + class MqttPolicy { Q_GADGET @@ -148,8 +155,8 @@ public: void removeWebSocketServerConfiguration(const QString &id); // Tunnel proxy server - QHash tunnelProxyServerConfigurations() const; - void setTunnelProxyServerConfiguration(const ServerConfiguration &config); + QHash tunnelProxyServerConfigurations() const; + void setTunnelProxyServerConfiguration(const TunnelProxyServerConfiguration &config); void removeTunnelProxyServerConfiguration(const QString &id); // MQTT @@ -192,7 +199,7 @@ private: QHash m_webSocketServerConfigs; QHash m_mqttServerConfigs; QHash m_mqttPolicies; - QHash m_tunnelProxyServerConfigs; + QHash m_tunnelProxyServerConfigs; void setServerUuid(const QUuid &uuid); void setWebServerPublicFolder(const QString & path); @@ -204,6 +211,8 @@ private: void deleteServerConfig(const QString &group, const QString &id); void storeWebServerConfig(const WebServerConfiguration &config); WebServerConfiguration readWebServerConfig(const QString &id); + void storeTunnelProxyServerConfig(const TunnelProxyServerConfiguration &config); + TunnelProxyServerConfiguration readTunnelProxyServerConfig(const QString &id); void storeMqttPolicy(const MqttPolicy &policy); MqttPolicy readMqttPolicy(const QString &clientId); diff --git a/libnymea-core/servermanager.cpp b/libnymea-core/servermanager.cpp index 9e6ab07c..84437975 100644 --- a/libnymea-core/servermanager.cpp +++ b/libnymea-core/servermanager.cpp @@ -70,6 +70,7 @@ namespace nymeaserver { ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configuration, const QStringList &additionalInterfaces, QObject *parent) : QObject(parent), m_platform(platform), + m_nymeaConfiguration(configuration), m_sslConfiguration(QSslConfiguration()) { if (!QSslSocket::supportsSsl()) { @@ -120,7 +121,7 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati QUrl additionalInterface(interfaceString); ServerConfiguration config; config.id = "tmp-" + additionalInterface.host(); - config.address = QHostAddress(additionalInterface.host()); + config.address = additionalInterface.host(); config.port = additionalInterface.port(); TransportInterface *server = nullptr; QString serverType, serviceType; @@ -166,7 +167,7 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati m_bluetoothServer->startServer(); } - foreach (const ServerConfiguration &config, configuration->tunnelProxyServerConfigurations()) { + foreach (const TunnelProxyServerConfiguration &config, configuration->tunnelProxyServerConfigurations()) { TunnelProxyServer *tunnelProxyServer = new TunnelProxyServer(configuration->serverName(), configuration->serverUuid(), config, this); qCDebug(dcServerManager()) << "Creating tunnel proxy server" << config; m_tunnelProxyServers.insert(config.id, tunnelProxyServer); @@ -318,7 +319,7 @@ void ServerManager::webServerConfigurationChanged(const QString &id) server->stopServer(); server->setConfiguration(config); } else { - qDebug(dcServerManager()) << "Received a Web Server config change event but don't have a Web Server instance for it. Creating new WebServer instance on" << config.address.toString() << config.port << "(SSL:" << config.sslEnabled << ")"; + qDebug(dcServerManager()) << "Received a Web Server config change event but don't have a Web Server instance for it. Creating new WebServer instance on" << config.address << config.port << "(SSL:" << config.sslEnabled << ")"; server = new WebServer(config, m_sslConfiguration, this); m_webServers.insert(config.id, server); } @@ -369,14 +370,32 @@ void ServerManager::mqttPolicyRemoved(const QString &clientId) void ServerManager::tunnelProxyServerConfigurationChanged(const QString &id) { - // FIXME: not dynamic configurable for now - Q_UNUSED(id) + TunnelProxyServer *server = m_tunnelProxyServers.value(id); + TunnelProxyServerConfiguration config = NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations().value(id); + if (server) { + qDebug(dcServerManager()) << "Restarting tunnel proxy connection for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled"); + unregisterZeroConfService(id, "ws"); + server->stopServer(); + server->setConfiguration(config); + } else { + qDebug(dcServerManager()) << "Received a tunnel proxy config change event but don't have a WebSocket Server instance for it. Creating new instance."; + server = new TunnelProxyServer(m_nymeaConfiguration->serverName(), m_nymeaConfiguration->serverUuid(), config, this); + m_tunnelProxyServers.insert(server->configuration().id, server); + } + m_jsonServer->registerTransportInterface(server, config.authenticationEnabled); + server->startServer(); } void ServerManager::tunnelProxyServerConfigurationRemoved(const QString &id) { - // FIXME: not dynamic configurable for now - Q_UNUSED(id) + if (!m_tunnelProxyServers.contains(id)) { + qWarning(dcServerManager()) << "Received a tunnel proxy config removed event but don't have a tunnel proxy connection for it."; + return; + } + TunnelProxyServer *server = m_tunnelProxyServers.take(id); + m_jsonServer->unregisterTransportInterface(server); + server->stopServer(); + server->deleteLater(); } void ServerManager::cloudEnabledChanged(bool enabled) @@ -402,7 +421,7 @@ bool ServerManager::registerZeroConfService(const ServerConfiguration &configura txt.insert("name", NymeaCore::instance()->configuration()->serverName()); txt.insert("sslEnabled", configuration.sslEnabled ? "true" : "false"); QString name = "nymea-" + serverType + "-" + configuration.id; - if (!m_platform->zeroConfController()->servicePublisher()->registerService(name, configuration.address, static_cast(configuration.port), serviceType, txt)) { + if (!m_platform->zeroConfController()->servicePublisher()->registerService(name, QHostAddress(configuration.address), static_cast(configuration.port), serviceType, txt)) { qCWarning(dcServerManager()) << "Could not register ZeroConf service for" << configuration; return false; } diff --git a/libnymea-core/servermanager.h b/libnymea-core/servermanager.h index 49438294..4ad35a1e 100644 --- a/libnymea-core/servermanager.h +++ b/libnymea-core/servermanager.h @@ -69,6 +69,10 @@ public: MqttBroker *mqttBroker() const; +public slots: + void setServerName(const QString &serverName); + + private slots: void tcpServerConfigurationChanged(const QString &id); void tcpServerConfigurationRemoved(const QString &id); @@ -89,8 +93,11 @@ private: bool registerZeroConfService(const ServerConfiguration &configuration, const QString &serverType, const QString &serviceType); void unregisterZeroConfService(const QString &configId, const QString &serverType); + bool loadCertificate(const QString &certificateKeyFileName, const QString &certificateFileName); + private: Platform *m_platform = nullptr; + NymeaConfiguration *m_nymeaConfiguration = nullptr; // Interfaces JsonRPCServerImplementation *m_jsonServer; @@ -108,12 +115,6 @@ private: QSslConfiguration m_sslConfiguration; QSslKey m_certificateKey; QSslCertificate m_certificate; - - bool loadCertificate(const QString &certificateKeyFileName, const QString &certificateFileName); - -public slots: - void setServerName(const QString &serverName); - }; } diff --git a/libnymea-core/servers/mqttbroker.cpp b/libnymea-core/servers/mqttbroker.cpp index 0e6c0973..127ebb86 100644 --- a/libnymea-core/servers/mqttbroker.cpp +++ b/libnymea-core/servers/mqttbroker.cpp @@ -140,7 +140,7 @@ MqttBroker::~MqttBroker() bool MqttBroker::startServer(const ServerConfiguration &config, const QSslConfiguration &sslConfiguration) { - int serverAddressId = m_server->listen(config.address, config.port, config.sslEnabled ? sslConfiguration : QSslConfiguration()); + int serverAddressId = m_server->listen(QHostAddress(config.address), config.port, config.sslEnabled ? sslConfiguration : QSslConfiguration()); if (serverAddressId == -1) { qCWarning(dcMqtt) << "Error starting MQTT server on port" << config.port; return false; diff --git a/libnymea-core/servers/tcpserver.cpp b/libnymea-core/servers/tcpserver.cpp index 96cd478e..dc3fa232 100644 --- a/libnymea-core/servers/tcpserver.cpp +++ b/libnymea-core/servers/tcpserver.cpp @@ -101,7 +101,7 @@ TcpServer::~TcpServer() /*! Returns the URL of this server. */ QUrl TcpServer::serverUrl() const { - return QUrl(QString("%1://%2:%3").arg((configuration().sslEnabled ? "nymeas" : "nymea")).arg(configuration().address.toString()).arg(configuration().port)); + return QUrl(QString("%1://%2:%3").arg((configuration().sslEnabled ? "nymeas" : "nymea")).arg(configuration().address).arg(configuration().port)); } /*! Sending \a data to a list of \a clients.*/ @@ -177,8 +177,8 @@ void TcpServer::setServerName(const QString &serverName) bool TcpServer::startServer() { m_server = new SslServer(configuration().sslEnabled, m_sslConfig); - if(!m_server->listen(configuration().address, static_cast(configuration().port))) { - qCWarning(dcTcpServer()) << "Tcp server error: can not listen on" << configuration().address.toString() << configuration().port; + if(!m_server->listen(QHostAddress(configuration().address), static_cast(configuration().port))) { + qCWarning(dcTcpServer()) << "Tcp server error: can not listen on" << configuration().address << configuration().port; delete m_server; m_server = nullptr; return false; diff --git a/libnymea-core/servers/tunnelproxyserver.cpp b/libnymea-core/servers/tunnelproxyserver.cpp index 9b1b4c84..a8187b05 100644 --- a/libnymea-core/servers/tunnelproxyserver.cpp +++ b/libnymea-core/servers/tunnelproxyserver.cpp @@ -35,21 +35,20 @@ NYMEA_LOGGING_CATEGORY(dcTunnelProxyServer, "TunnelProxyServer") namespace nymeaserver { -TunnelProxyServer::TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const ServerConfiguration &configuration, QObject *parent) : +TunnelProxyServer::TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const TunnelProxyServerConfiguration &configuration, QObject *parent) : TransportInterface(configuration, parent), + m_config(configuration), m_serverName(serverName), m_serverUuid(serverUuid) { - // Note: the authentication must always be enabled on the tunnel proxy server if (!configuration.authenticationEnabled) { - qCWarning(dcTunnelProxyServer()) << "============================================================================================================================="; - qCWarning(dcTunnelProxyServer()) << "WARNING! The tunnel proxy server has authentication disabled! This is very dangerous and exposes your system to the internet."; - qCWarning(dcTunnelProxyServer()) << "============================================================================================================================="; + qCWarning(dcTunnelProxyServer()) << "====================================================================================================================="; + qCWarning(dcTunnelProxyServer()) << "WARNING! Tunnel proxy connection has authentication disabled! The system will be publicly accessible on the internet."; + qCWarning(dcTunnelProxyServer()) << "====================================================================================================================="; } - // Default to ssl - m_serverUrl.setScheme("ssl"); - m_serverUrl.setHost(configuration.address.toString()); + m_serverUrl.setScheme(configuration.sslEnabled ? "ssl" : "tcp"); + m_serverUrl.setHost(configuration.address); m_serverUrl.setPort(configuration.port); m_tunnelProxySocketServer = new TunnelProxySocketServer(m_serverUuid, m_serverName, this); @@ -102,7 +101,7 @@ void TunnelProxyServer::setServerName(const QString &serverName) bool TunnelProxyServer::startServer() { - + qCDebug(dcTunnelProxyServer()) << "Connecting to tunnel proxy server at:" << m_serverUrl; m_tunnelProxySocketServer->startServer(m_serverUrl); return true; } @@ -144,7 +143,12 @@ void TunnelProxyServer::onSslErrors(const QList &errors) { qCDebug(dcTunnelProxyServer()) << "Remote proxy connection SSL errors occurred" << errors; // FIXME: make this configurable - m_tunnelProxySocketServer->ignoreSslErrors(); + if (m_config.ignoreSslErrors) { + qCWarning(dcTunnelProxyServer()) << "Ingoring SSL errors on tunnel proxy connection:" << errors; + m_tunnelProxySocketServer->ignoreSslErrors(); + } else { + qCWarning(dcTunnelProxyServer()) << "The remote proxy connection failed due to SSL errors:" << errors; + } } void TunnelProxyServer::onClientConnected(TunnelProxySocket *tunnelProxySocket) diff --git a/libnymea-core/servers/tunnelproxyserver.h b/libnymea-core/servers/tunnelproxyserver.h index 06059fb5..38268bc5 100644 --- a/libnymea-core/servers/tunnelproxyserver.h +++ b/libnymea-core/servers/tunnelproxyserver.h @@ -47,7 +47,7 @@ class TunnelProxyServer : public TransportInterface { Q_OBJECT public: - explicit TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const ServerConfiguration &configuration, QObject *parent = nullptr); + explicit TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const TunnelProxyServerConfiguration &configuration, QObject *parent = nullptr); ~TunnelProxyServer() override; void sendData(const QUuid &clientId, const QByteArray &data) override; @@ -74,6 +74,7 @@ private slots: void onClientDisconnected(TunnelProxySocket *tunnelProxySocket); private: + TunnelProxyServerConfiguration m_config; TunnelProxySocketServer *m_tunnelProxySocketServer = nullptr; QString m_serverName; QUuid m_serverUuid; diff --git a/libnymea-core/servers/webserver.cpp b/libnymea-core/servers/webserver.cpp index 7a93037c..f647ffd1 100644 --- a/libnymea-core/servers/webserver.cpp +++ b/libnymea-core/servers/webserver.cpp @@ -129,7 +129,7 @@ WebServer::~WebServer() /*! Returns the server URL of this WebServer. */ QUrl WebServer::serverUrl() const { - return QUrl(QString("%1://%2:%3").arg((m_configuration.sslEnabled ? "https" : "http")).arg(m_configuration.address.toString()).arg(m_configuration.port)); + return QUrl(QString("%1://%2:%3").arg((m_configuration.sslEnabled ? "https" : "http")).arg(m_configuration.address).arg(m_configuration.port)); } /*! Send the given \a reply map to the corresponding client. @@ -561,7 +561,7 @@ void WebServer::setServerName(const QString &serverName) /*! Returns true if this \l{WebServer} started successfully. */ bool WebServer::startServer() { - if (!listen(m_configuration.address, static_cast(m_configuration.port))) { + if (!listen(QHostAddress(m_configuration.address), static_cast(m_configuration.port))) { qCWarning(dcWebServer()) << "Webserver could not listen on" << serverUrl().toString() << errorString(); m_enabled = false; return false; @@ -712,7 +712,7 @@ QByteArray WebServer::createServerXmlDocument(QHostAddress address) int counter = 1; int sslCounter = 1; foreach (const ServerConfiguration &config, NymeaCore::instance()->configuration()->webSocketServerConfigurations()) { - if (config.address == QHostAddress("0.0.0.0") || config.address == address) { + if (QHostAddress(config.address) == QHostAddress("0.0.0.0") || QHostAddress(config.address) == address) { writer.writeStartElement("service"); writer.writeTextElement("serviceType", QString("urn:nymea.io:service:%1:%2").arg(config.sslEnabled ? "wss" : "ws").arg(config.sslEnabled ? sslCounter : counter)); writer.writeTextElement("serviceId", QString("urn:nymea.io:serviceId:%1:%2").arg(config.sslEnabled ? "wss" : "ws").arg(config.sslEnabled ? sslCounter++ : counter++)); @@ -725,7 +725,7 @@ QByteArray WebServer::createServerXmlDocument(QHostAddress address) counter = 1; sslCounter = 1; foreach (const ServerConfiguration &config, NymeaCore::instance()->configuration()->tcpServerConfigurations()) { - if (config.address == QHostAddress("0.0.0.0") || config.address == address) { + if (QHostAddress(config.address) == QHostAddress("0.0.0.0") || QHostAddress(config.address) == address) { writer.writeStartElement("service"); writer.writeTextElement("serviceType", QString("urn:nymea.io:service:%1:%2").arg(config.sslEnabled ? "nymeas" : "nymea").arg(config.sslEnabled ? sslCounter : counter)); writer.writeTextElement("serviceId", QString("urn:nymea.io:serviceId:%1:%2").arg(config.sslEnabled ? "nymeas" : "nymea").arg(config.sslEnabled ? sslCounter++ : counter++)); diff --git a/libnymea-core/servers/websocketserver.cpp b/libnymea-core/servers/websocketserver.cpp index 4a1e0b93..bb0bf062 100644 --- a/libnymea-core/servers/websocketserver.cpp +++ b/libnymea-core/servers/websocketserver.cpp @@ -87,7 +87,7 @@ WebSocketServer::~WebSocketServer() /*! Returns the url of this server. */ QUrl WebSocketServer::serverUrl() const { - return QUrl(QString("%1://%2:%3").arg((configuration().sslEnabled ? "wss" : "ws")).arg(configuration().address.toString()).arg(configuration().port)); + return QUrl(QString("%1://%2:%3").arg((configuration().sslEnabled ? "wss" : "ws")).arg(configuration().address).arg(configuration().port)); } /*! Send the given \a data map to the client with the given \a clientId. @@ -219,7 +219,7 @@ bool WebSocketServer::startServer() connect (m_server, &QWebSocketServer::newConnection, this, &WebSocketServer::onClientConnected); connect (m_server, &QWebSocketServer::acceptError, this, &WebSocketServer::onServerError); - if (!m_server->listen(configuration().address, static_cast(configuration().port))) { + if (!m_server->listen(QHostAddress(configuration().address), static_cast(configuration().port))) { qCWarning(dcWebSocketServer()) << "Error listening on" << serverUrl().toString(); return false; } diff --git a/tests/auto/api.json b/tests/auto/api.json index 7ce29dd3..21d47c47 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -471,6 +471,16 @@ "configurationError": "$ref:ConfigurationError" } }, + "Configuration.DeleteTunnelProxyServerConfiguration": { + "description": "Delete a Tunnel Proxy Server interface of the server. Note: if you are deleting the configuration for the interface you are currently connected to, the connection will be dropped.", + "params": { + "id": "String" + }, + "permissionScope": "PermissionScopeAdmin", + "returns": { + "configurationError": "$ref:ConfigurationError" + } + }, "Configuration.DeleteWebServerConfiguration": { "description": "Delete a WebServer interface of the server.", "params": { @@ -507,7 +517,7 @@ "description": "Get all configuration parameters of the server.", "params": { }, - "permissionScope": "PermissionScopeAdmin", + "permissionScope": "PermissionScopeNone", "returns": { "basicConfiguration": { "d:language": "String", @@ -523,6 +533,9 @@ "tcpServerConfigurations": [ "$ref:ServerConfiguration" ], + "tunnelProxyServerConfigurations": [ + "$ref:TunnelProxyServerConfiguration" + ], "webServerConfigurations": [ "$ref:WebServerConfiguration" ], @@ -647,6 +660,16 @@ "configurationError": "$ref:ConfigurationError" } }, + "Configuration.SetTunnelProxyServerConfiguration": { + "description": "Configure a Tunnel Proxy Server interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added. Note: if you are changing the configuration for the interface you are currently connected to, the connection will be dropped.", + "params": { + "configuration": "$ref:TunnelProxyServerConfiguration" + }, + "permissionScope": "PermissionScopeAdmin", + "returns": { + "configurationError": "$ref:ConfigurationError" + } + }, "Configuration.SetWebServerConfiguration": { "description": "Configure a WebServer interface of the server. If the ID is an existing one, the existing config will be modified, otherwise a new one will be added.", "params": { @@ -2043,6 +2066,18 @@ "id": "String" } }, + "Configuration.TunnelProxyServerConfigurationChanged": { + "description": "Emitted whenever the tunnel proxy server configuration changes.", + "params": { + "tunnelProxyServerConfiguration": "$ref:TunnelProxyServerConfiguration" + } + }, + "Configuration.TunnelProxyServerConfigurationRemoved": { + "description": "Emitted whenever a tunnel proxy server configuration is removed.", + "params": { + "id": "String" + } + }, "Configuration.WebServerConfigurationChanged": { "description": "Emitted whenever the web server configuration changes.", "params": { @@ -2830,6 +2865,14 @@ "TokenInfoList": [ "$ref:TokenInfo" ], + "TunnelProxyServerConfiguration": { + "address": "String", + "authenticationEnabled": "Bool", + "id": "String", + "ignoreSslErrors": "Bool", + "port": "Uint", + "sslEnabled": "Bool" + }, "UserInfo": { "r:displayName": "String", "r:email": "String", diff --git a/tests/auto/configurations/testconfigurations.cpp b/tests/auto/configurations/testconfigurations.cpp index 1eb4c7e8..5660dd97 100644 --- a/tests/auto/configurations/testconfigurations.cpp +++ b/tests/auto/configurations/testconfigurations.cpp @@ -278,7 +278,7 @@ void TestConfigurations::testDebugServerConfiguration() qCDebug(dcTests()) << "TestWebserver starting"; foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) { - if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) { + if (config.port == 3333 && (QHostAddress(config.address) == QHostAddress("127.0.0.1") || QHostAddress(config.address) == QHostAddress("0.0.0.0"))) { qDebug() << "Already have a webserver listening on 127.0.0.1:3333"; return; } @@ -287,7 +287,7 @@ void TestConfigurations::testDebugServerConfiguration() qCDebug(dcTests) << "Creating new webserver instance on 127.0.0.1:3333"; WebServerConfiguration config; config.id = "Testwebserver for debug server interface"; - config.address = QHostAddress("127.0.0.1"); + config.address = "127.0.0.1"; config.port = 3333; config.sslEnabled = true; NymeaCore::instance()->configuration()->setWebServerConfiguration(config); diff --git a/tests/auto/mqttbroker/testmqttbroker.cpp b/tests/auto/mqttbroker/testmqttbroker.cpp index 0af354b2..84a6965a 100644 --- a/tests/auto/mqttbroker/testmqttbroker.cpp +++ b/tests/auto/mqttbroker/testmqttbroker.cpp @@ -61,8 +61,7 @@ private slots: void TestMqttBroker::initTestCase() { - NymeaTestBase::initTestCase(); - QLoggingCategory::setFilterRules("*.debug=false\nnymea.mqtt*.debug=true\nMqtt.debug=true\nJsonRpc.debug=true"); + NymeaTestBase::initTestCase("*.debug=false\nnymea.mqtt*.debug=true\nMqtt.debug=true\nJsonRpc.debug=true"); } void TestMqttBroker::testServerConfigurationAPI() diff --git a/tests/auto/webserver/testwebserver.cpp b/tests/auto/webserver/testwebserver.cpp index a4d188ed..7b6e877d 100644 --- a/tests/auto/webserver/testwebserver.cpp +++ b/tests/auto/webserver/testwebserver.cpp @@ -79,7 +79,7 @@ void TestWebserver::initTestCase() qDebug() << "TestWebserver starting"; foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) { - if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) { + if (config.port == 3333 && (QHostAddress(config.address) == QHostAddress("127.0.0.1") || QHostAddress(config.address) == QHostAddress("0.0.0.0"))) { qDebug() << "Already have a webserver listening on 127.0.0.1:3333"; return; } @@ -87,7 +87,7 @@ void TestWebserver::initTestCase() qDebug() << "Creating new webserver instance on 127.0.0.1:3333"; WebServerConfiguration config; - config.address = QHostAddress("127.0.0.1"); + config.address = "127.0.0.1"; config.port = 3333; config.sslEnabled = true; config.restServerEnabled = true; @@ -215,7 +215,7 @@ void TestWebserver::checkAllowedMethodCall() QFETCH(int, expectedStatusCode); QNetworkAccessManager nam; - connect(&nam, &QNetworkAccessManager::sslErrors, [this, &nam](QNetworkReply* reply, const QList &) { + connect(&nam, &QNetworkAccessManager::sslErrors, [](QNetworkReply* reply, const QList &) { reply->ignoreSslErrors(); }); QSignalSpy clientSpy(&nam, SIGNAL(finished(QNetworkReply*))); diff --git a/tests/auto/websocketserver/testwebsocketserver.cpp b/tests/auto/websocketserver/testwebsocketserver.cpp index 4e36e170..09f72bcd 100644 --- a/tests/auto/websocketserver/testwebsocketserver.cpp +++ b/tests/auto/websocketserver/testwebsocketserver.cpp @@ -72,7 +72,7 @@ void TestWebSocketServer::initTestCase() ServerConfiguration config; foreach (const ServerConfiguration &c, NymeaCore::instance()->configuration()->webSocketServerConfigurations()) { - if (c.port == 4444 && (c.address == QHostAddress("127.0.0.1") || c.address == QHostAddress("0.0.0.0"))) { + if (c.port == 4444 && (QHostAddress(c.address) == QHostAddress("127.0.0.1") || QHostAddress(c.address) == QHostAddress("0.0.0.0"))) { qDebug() << "Already have a websocketserver listening on 127.0.0.1:4444"; config = c; break; @@ -80,7 +80,7 @@ void TestWebSocketServer::initTestCase() } qDebug() << "Creating new websocketserver instance on 127.0.0.1:4444"; - config.address = QHostAddress("127.0.0.1"); + config.address = "127.0.0.1"; config.port = 4444; config.sslEnabled = true; config.authenticationEnabled = true; From 3e94a1a4e8c1d7ad17bb1de0486dea6d9a51df18 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Tue, 25 Jan 2022 15:06:20 +0100 Subject: [PATCH 5/9] fix typo --- libnymea-core/servers/tunnelproxyserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnymea-core/servers/tunnelproxyserver.cpp b/libnymea-core/servers/tunnelproxyserver.cpp index a8187b05..3694c8fe 100644 --- a/libnymea-core/servers/tunnelproxyserver.cpp +++ b/libnymea-core/servers/tunnelproxyserver.cpp @@ -125,7 +125,7 @@ void TunnelProxyServer::onStateChanged(TunnelProxySocketServer::State state) void TunnelProxyServer::onServerRunningChanged(bool running) { - qCDebug(dcTunnelProxyServer()) << "The server is" << (running ? "now listening for incomming connections on " + m_serverUuid.toString() : "not running any more."); + qCDebug(dcTunnelProxyServer()) << "The server is" << (running ? "now listening for incoming connections on " + m_serverUuid.toString() : "not running any more."); emit runningChanged(running); } From 1e132a49ac40ab9ca6664b99725c62bd0ce78c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 1 Feb 2022 13:57:47 +0100 Subject: [PATCH 6/9] Improve debug print for TunnelProxy --- libnymea-core/servers/tunnelproxyserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnymea-core/servers/tunnelproxyserver.cpp b/libnymea-core/servers/tunnelproxyserver.cpp index 3694c8fe..8ba763d3 100644 --- a/libnymea-core/servers/tunnelproxyserver.cpp +++ b/libnymea-core/servers/tunnelproxyserver.cpp @@ -125,7 +125,7 @@ void TunnelProxyServer::onStateChanged(TunnelProxySocketServer::State state) void TunnelProxyServer::onServerRunningChanged(bool running) { - qCDebug(dcTunnelProxyServer()) << "The server is" << (running ? "now listening for incoming connections on " + m_serverUuid.toString() : "not running any more."); + qCDebug(dcTunnelProxyServer()).noquote() << "The server is" << (running ? "now listening for incoming connections on " + m_serverUuid.toString() : "not running any more."); emit runningChanged(running); } From 3eb3b8ad8a1e788c227c0cf7c79ebfcab4990aa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 1 Mar 2022 13:51:27 +0100 Subject: [PATCH 7/9] Fix tunnel proxy configuration and make it independent from the cloud configuration --- libnymea-core/nymeaconfiguration.cpp | 52 +++++++++++---------- libnymea-core/nymeaconfiguration.h | 16 +++++++ libnymea-core/servermanager.cpp | 45 ++++++++++-------- libnymea-core/servers/tunnelproxyserver.cpp | 25 +++++++--- libnymea-core/servers/tunnelproxyserver.h | 6 ++- 5 files changed, 93 insertions(+), 51 deletions(-) diff --git a/libnymea-core/nymeaconfiguration.cpp b/libnymea-core/nymeaconfiguration.cpp index a8148624..e3ac74f1 100644 --- a/libnymea-core/nymeaconfiguration.cpp +++ b/libnymea-core/nymeaconfiguration.cpp @@ -212,33 +212,14 @@ NymeaConfiguration::NymeaConfiguration(QObject *parent) : } // Tunnel Proxy Server - createDefaults = !settings.childGroups().contains("TunnelProxyServer"); if (settings.childGroups().contains("TunnelProxyServer")) { settings.beginGroup("TunnelProxyServer"); - if (settings.value("disabled").toBool()) { - qCDebug(dcConfiguration) << "Tunnel proxy server disabled by configuration."; - } else if (!settings.childGroups().isEmpty()) { - foreach (const QString &key, settings.childGroups()) { - TunnelProxyServerConfiguration config = readTunnelProxyServerConfig(key); - m_tunnelProxyServerConfigs[config.id] = config; - } - } else { - createDefaults = true; + foreach (const QString &key, settings.childGroups()) { + TunnelProxyServerConfiguration config = readTunnelProxyServerConfig(key); + m_tunnelProxyServerConfigs[config.id] = config; } settings.endGroup(); } - if (createDefaults) { - qCWarning(dcConfiguration) << "No Tunnel Proxy server configuration found. Generating default of remoteproxy.nymea.io:2213"; - TunnelProxyServerConfiguration config; - config.id = "default"; - config.address = "remoteproxy.nymea.io"; - config.port = 2213; - config.sslEnabled = true; - config.authenticationEnabled = true; - config.ignoreSslErrors = false; - m_tunnelProxyServerConfigs[config.id] = config; - storeTunnelProxyServerConfig(config); - } // Write defaults for log settings settings.beginGroup("Logs"); @@ -403,7 +384,7 @@ QHash NymeaConfiguration::tunnelProxySe void NymeaConfiguration::setTunnelProxyServerConfiguration(const TunnelProxyServerConfiguration &config) { m_tunnelProxyServerConfigs[config.id] = config; - storeServerConfig("TunnelProxyServer", config); + storeTunnelProxyServerConfig(config); emit tunnelProxyServerConfigurationChanged(config.id); } @@ -791,7 +772,30 @@ QDebug operator <<(QDebug debug, const ServerConfiguration &configuration) debug.nospace() << ", " << configuration.id; debug.nospace() << ", " << QString("%1:%2").arg(configuration.address).arg(configuration.port); debug.nospace() << ") "; - return debug; + return debug.maybeSpace(); +} + +QDebug operator <<(QDebug debug, const TunnelProxyServerConfiguration &configuration) +{ + debug.nospace() << "TunnelProxyServerConfiguration(" << configuration.id; + debug.nospace() << ", " << QString("%1:%2").arg(configuration.address).arg(configuration.port); + if (configuration.sslEnabled) { + debug.nospace() << ", SSL enabled"; + } else { + debug.nospace() << ", SSL disabled"; + } + if (configuration.authenticationEnabled) { + debug.nospace() << ", Authentication enabled"; + } else { + debug.nospace() << ", Authentication disabled"; + } + + if (configuration.ignoreSslErrors) { + debug.nospace() << ", Ignoring SSL errors"; + } + + debug.nospace() << ") "; + return debug.maybeSpace(); } } diff --git a/libnymea-core/nymeaconfiguration.h b/libnymea-core/nymeaconfiguration.h index 18fdc8f1..a828469a 100644 --- a/libnymea-core/nymeaconfiguration.h +++ b/libnymea-core/nymeaconfiguration.h @@ -46,6 +46,7 @@ class ServerConfiguration { Q_PROPERTY(uint port MEMBER port) Q_PROPERTY(bool sslEnabled MEMBER sslEnabled) Q_PROPERTY(bool authenticationEnabled MEMBER authenticationEnabled) + public: QString id; QString address; @@ -69,19 +70,34 @@ class WebServerConfiguration: public ServerConfiguration { Q_GADGET Q_PROPERTY(QString publicFolder MEMBER publicFolder) + public: QString publicFolder; bool restServerEnabled = false; + + bool operator==(const WebServerConfiguration &other) const { + return ServerConfiguration::operator==(other) + && publicFolder == other.publicFolder + && restServerEnabled == other.restServerEnabled; + } }; class TunnelProxyServerConfiguration: public ServerConfiguration { Q_GADGET Q_PROPERTY(bool ignoreSslErrors MEMBER ignoreSslErrors) + public: bool ignoreSslErrors = false; + + bool operator==(const TunnelProxyServerConfiguration &other) const { + return ServerConfiguration::operator==(other) + && ignoreSslErrors == other.ignoreSslErrors; + } }; +QDebug operator <<(QDebug debug, const TunnelProxyServerConfiguration &configuration); + class MqttPolicy { Q_GADGET diff --git a/libnymea-core/servermanager.cpp b/libnymea-core/servermanager.cpp index 84437975..ff2fe160 100644 --- a/libnymea-core/servermanager.cpp +++ b/libnymea-core/servermanager.cpp @@ -169,7 +169,7 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati foreach (const TunnelProxyServerConfiguration &config, configuration->tunnelProxyServerConfigurations()) { TunnelProxyServer *tunnelProxyServer = new TunnelProxyServer(configuration->serverName(), configuration->serverUuid(), config, this); - qCDebug(dcServerManager()) << "Creating tunnel proxy server" << config; + qCDebug(dcServerManager()) << "Creating tunnel proxy server using" << config; m_tunnelProxyServers.insert(config.id, tunnelProxyServer); connect(tunnelProxyServer, &TunnelProxyServer::runningChanged, this, [this, tunnelProxyServer](bool running){ if (running) { @@ -183,10 +183,7 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati // FIXME: make use of SSL over tunnel proxy connections // FIXME: make sure the authentication is enabled for the tunnel proxy - // Note: only start the tunnel proxy server if cloud is connected - if (configuration->cloudEnabled()) { - tunnelProxyServer->startServer(); - } + tunnelProxyServer->startServer(); } foreach (const WebServerConfiguration &config, configuration->webServerConfigurations()) { @@ -248,12 +245,12 @@ void ServerManager::tcpServerConfigurationChanged(const QString &id) ServerConfiguration config = NymeaCore::instance()->configuration()->tcpServerConfigurations().value(id); TcpServer *server = m_tcpServers.value(id); if (server) { - qDebug(dcServerManager()) << "Restarting TCP server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled"); + qCDebug(dcServerManager()) << "Restarting TCP server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled"); unregisterZeroConfService(config.id, "tcp"); server->stopServer(); server->setConfiguration(config); } else { - qDebug(dcServerManager()) << "Received a TCP Server config change event but don't have a TCP Server instance for it. Creating new Server instance."; + qCDebug(dcServerManager()) << "Received a TCP Server config change event but don't have a TCP Server instance for it. Creating new Server instance."; server = new TcpServer(config, m_sslConfiguration, this); m_tcpServers.insert(config.id, server); } @@ -266,7 +263,7 @@ void ServerManager::tcpServerConfigurationChanged(const QString &id) void ServerManager::tcpServerConfigurationRemoved(const QString &id) { if (!m_tcpServers.contains(id)) { - qWarning(dcServerManager()) << "Received a TCP Server config removed event but don't have a TCP Server instance for it."; + qCWarning(dcServerManager()) << "Received a TCP Server config removed event but don't have a TCP Server instance for it."; return; } TcpServer *server = m_tcpServers.take(id); @@ -281,12 +278,12 @@ void ServerManager::webSocketServerConfigurationChanged(const QString &id) WebSocketServer *server = m_webSocketServers.value(id); ServerConfiguration config = NymeaCore::instance()->configuration()->webSocketServerConfigurations().value(id); if (server) { - qDebug(dcServerManager()) << "Restarting WebSocket server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled"); + qCDebug(dcServerManager()) << "Restarting WebSocket server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled"); unregisterZeroConfService(id, "ws"); server->stopServer(); server->setConfiguration(config); } else { - qDebug(dcServerManager()) << "Received a WebSocket Server config change event but don't have a WebSocket Server instance for it. Creating new instance."; + qCDebug(dcServerManager()) << "Received a WebSocket Server config change event but don't have a WebSocket Server instance for it. Creating new instance."; server = new WebSocketServer(config, m_sslConfiguration, this); m_webSocketServers.insert(server->configuration().id, server); } @@ -299,7 +296,7 @@ void ServerManager::webSocketServerConfigurationChanged(const QString &id) void ServerManager::webSocketServerConfigurationRemoved(const QString &id) { if (!m_webSocketServers.contains(id)) { - qWarning(dcServerManager()) << "Received a WebSocket Server config removed event but don't have a WebSocket Server instance for it."; + qCWarning(dcServerManager()) << "Received a WebSocket Server config removed event but don't have a WebSocket Server instance for it."; return; } WebSocketServer *server = m_webSocketServers.take(id); @@ -314,12 +311,12 @@ void ServerManager::webServerConfigurationChanged(const QString &id) WebServerConfiguration config = NymeaCore::instance()->configuration()->webServerConfigurations().value(id); WebServer *server = m_webServers.value(id); if (server) { - qDebug(dcServerManager()) << "Restarting Web server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled"); + qCDebug(dcServerManager()) << "Restarting Web server for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled"); unregisterZeroConfService(id, "http"); server->stopServer(); server->setConfiguration(config); } else { - qDebug(dcServerManager()) << "Received a Web Server config change event but don't have a Web Server instance for it. Creating new WebServer instance on" << config.address << config.port << "(SSL:" << config.sslEnabled << ")"; + qCDebug(dcServerManager()) << "Received a Web Server config change event but don't have a Web Server instance for it. Creating new WebServer instance on" << config.address << config.port << "(SSL:" << config.sslEnabled << ")"; server = new WebServer(config, m_sslConfiguration, this); m_webServers.insert(config.id, server); } @@ -331,7 +328,7 @@ void ServerManager::webServerConfigurationChanged(const QString &id) void ServerManager::webServerConfigurationRemoved(const QString &id) { if (!m_webServers.contains(id)) { - qWarning(dcServerManager()) << "Received a Web Server config removed event but don't have a Web Server instance for it."; + qCWarning(dcServerManager()) << "Received a Web Server config removed event but don't have a Web Server instance for it."; return; } WebServer *server = m_webServers.take(id); @@ -373,23 +370,31 @@ void ServerManager::tunnelProxyServerConfigurationChanged(const QString &id) TunnelProxyServer *server = m_tunnelProxyServers.value(id); TunnelProxyServerConfiguration config = NymeaCore::instance()->configuration()->tunnelProxyServerConfigurations().value(id); if (server) { - qDebug(dcServerManager()) << "Restarting tunnel proxy connection for" << config.address << config.port << "SSL" << (config.sslEnabled ? "enabled" : "disabled") << "Authentication" << (config.authenticationEnabled ? "enabled" : "disabled"); - unregisterZeroConfService(id, "ws"); + qCDebug(dcServerManager()) << "Restarting tunnel proxy connection using" << config; server->stopServer(); - server->setConfiguration(config); + server->setTunnelProxyConfig(config); } else { - qDebug(dcServerManager()) << "Received a tunnel proxy config change event but don't have a WebSocket Server instance for it. Creating new instance."; + qCDebug(dcServerManager()) << "Received a tunnel proxy config change event but don't have a tunnel proxy server instance for it."; + qCDebug(dcServerManager()) << "Creating tunnel proxy server using" << config; server = new TunnelProxyServer(m_nymeaConfiguration->serverName(), m_nymeaConfiguration->serverUuid(), config, this); m_tunnelProxyServers.insert(server->configuration().id, server); + connect(server, &TunnelProxyServer::runningChanged, this, [this, server](bool running){ + if (running) { + // Note: enable authentication in any case, we don't want to expose unprotected access trough the internet + m_jsonServer->registerTransportInterface(server, true); + } else { + m_jsonServer->unregisterTransportInterface(server); + } + }); } - m_jsonServer->registerTransportInterface(server, config.authenticationEnabled); + server->startServer(); } void ServerManager::tunnelProxyServerConfigurationRemoved(const QString &id) { if (!m_tunnelProxyServers.contains(id)) { - qWarning(dcServerManager()) << "Received a tunnel proxy config removed event but don't have a tunnel proxy connection for it."; + qCWarning(dcServerManager()) << "Received a tunnel proxy config removed event but don't have a tunnel proxy connection for it."; return; } TunnelProxyServer *server = m_tunnelProxyServers.take(id); diff --git a/libnymea-core/servers/tunnelproxyserver.cpp b/libnymea-core/servers/tunnelproxyserver.cpp index 8ba763d3..3c01ed69 100644 --- a/libnymea-core/servers/tunnelproxyserver.cpp +++ b/libnymea-core/servers/tunnelproxyserver.cpp @@ -37,7 +37,7 @@ namespace nymeaserver { TunnelProxyServer::TunnelProxyServer(const QString &serverName, const QUuid &serverUuid, const TunnelProxyServerConfiguration &configuration, QObject *parent) : TransportInterface(configuration, parent), - m_config(configuration), + m_tunnelProxyConfig(configuration), m_serverName(serverName), m_serverUuid(serverUuid) { @@ -47,9 +47,7 @@ TunnelProxyServer::TunnelProxyServer(const QString &serverName, const QUuid &ser qCWarning(dcTunnelProxyServer()) << "====================================================================================================================="; } - m_serverUrl.setScheme(configuration.sslEnabled ? "ssl" : "tcp"); - m_serverUrl.setHost(configuration.address); - m_serverUrl.setPort(configuration.port); + initConfiguration(); m_tunnelProxySocketServer = new TunnelProxySocketServer(m_serverUuid, m_serverName, this); connect(m_tunnelProxySocketServer, &TunnelProxySocketServer::stateChanged, this, &TunnelProxyServer::onStateChanged); @@ -99,8 +97,15 @@ void TunnelProxyServer::setServerName(const QString &serverName) m_serverName = serverName; } +void TunnelProxyServer::setTunnelProxyConfig(const TunnelProxyServerConfiguration &tunnelProxyConfig) +{ + m_tunnelProxyConfig = tunnelProxyConfig; +} + bool TunnelProxyServer::startServer() { + initConfiguration(); + qCDebug(dcTunnelProxyServer()) << "Connecting to tunnel proxy server at:" << m_serverUrl; m_tunnelProxySocketServer->startServer(m_serverUrl); return true; @@ -142,8 +147,7 @@ void TunnelProxyServer::onServerErrorOccurred(TunnelProxySocketServer::Error err void TunnelProxyServer::onSslErrors(const QList &errors) { qCDebug(dcTunnelProxyServer()) << "Remote proxy connection SSL errors occurred" << errors; - // FIXME: make this configurable - if (m_config.ignoreSslErrors) { + if (m_tunnelProxyConfig.ignoreSslErrors) { qCWarning(dcTunnelProxyServer()) << "Ingoring SSL errors on tunnel proxy connection:" << errors; m_tunnelProxySocketServer->ignoreSslErrors(); } else { @@ -172,4 +176,13 @@ void TunnelProxyServer::onClientDisconnected(TunnelProxySocket *tunnelProxySocke emit clientDisconnected(clientId); } +void TunnelProxyServer::initConfiguration() +{ + qCDebug(dcTunnelProxyServer()) << "Init configuration" << m_tunnelProxyConfig; + m_serverUrl.setScheme(m_tunnelProxyConfig.sslEnabled ? "ssl" : "tcp"); + m_serverUrl.setHost(m_tunnelProxyConfig.address); + m_serverUrl.setPort(m_tunnelProxyConfig.port); + qCDebug(dcTunnelProxyServer()) << "Using server URL" << m_serverUrl.toString(); +} + } diff --git a/libnymea-core/servers/tunnelproxyserver.h b/libnymea-core/servers/tunnelproxyserver.h index 38268bc5..4ef15bed 100644 --- a/libnymea-core/servers/tunnelproxyserver.h +++ b/libnymea-core/servers/tunnelproxyserver.h @@ -57,6 +57,8 @@ public: public slots: void setServerName(const QString &serverName) override; + void setTunnelProxyConfig(const TunnelProxyServerConfiguration &tunnelProxyConfig); + bool startServer() override; bool stopServer() override; @@ -74,7 +76,7 @@ private slots: void onClientDisconnected(TunnelProxySocket *tunnelProxySocket); private: - TunnelProxyServerConfiguration m_config; + TunnelProxyServerConfiguration m_tunnelProxyConfig; TunnelProxySocketServer *m_tunnelProxySocketServer = nullptr; QString m_serverName; QUuid m_serverUuid; @@ -82,6 +84,8 @@ private: QHash m_clients; + void initConfiguration(); + }; } From 836e0a5e1479ffaad0345813814b12a669c72af1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Wed, 9 Mar 2022 17:45:32 +0100 Subject: [PATCH 8/9] Default remote connection to TCP connection and handle connection type appropriate --- libnymea-core/cloud/cloudtransport.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/libnymea-core/cloud/cloudtransport.cpp b/libnymea-core/cloud/cloudtransport.cpp index 393346ba..49de362f 100644 --- a/libnymea-core/cloud/cloudtransport.cpp +++ b/libnymea-core/cloud/cloudtransport.cpp @@ -40,7 +40,7 @@ namespace nymeaserver { CloudTransport::CloudTransport(const ServerConfiguration &config, QObject *parent): TransportInterface(config, parent) { - m_defaultProxyUrl = "wss://remoteproxy.nymea.io"; + m_defaultProxyUrl = "ssl://remoteproxy.nymea.io:4433"; } void CloudTransport::sendData(const QUuid &clientId, const QByteArray &data) @@ -89,12 +89,18 @@ void CloudTransport::connectToCloud(const QString &token, const QString &nonce, QString proxyUrl = serverUrl.isEmpty() ? m_defaultProxyUrl : serverUrl; qCDebug(dcCloud()) << "Connecting to remote proxy server" << proxyUrl; + RemoteProxyConnection::ConnectionType connectionType = RemoteProxyConnection::ConnectionTypeTcpSocket; + QUrl url(proxyUrl); + if (url.scheme() == "wss" || url.scheme() == "ws") { + connectionType = RemoteProxyConnection::ConnectionTypeWebSocket; + } + ConnectionContext context; context.clientId = QUuid::createUuid(); context.token = token; context.nonce = nonce; QString identifier = QString("nymea:core (%1)").arg(NymeaCore::instance()->configuration()->serverName()); - context.proxyConnection = new RemoteProxyConnection(NymeaCore::instance()->configuration()->serverUuid().toString(), identifier, this); + context.proxyConnection = new RemoteProxyConnection(NymeaCore::instance()->configuration()->serverUuid().toString(), identifier, connectionType, this); m_connections.insert(context.proxyConnection, context); connect(context.proxyConnection, &RemoteProxyConnection::ready, this, &CloudTransport::transportReady); @@ -103,7 +109,7 @@ void CloudTransport::connectToCloud(const QString &token, const QString &nonce, connect(context.proxyConnection, &RemoteProxyConnection::remoteConnectionEstablished, this, &CloudTransport::transportConnected); connect(context.proxyConnection, &RemoteProxyConnection::disconnected, this, &CloudTransport::transportDisconnected); - context.proxyConnection->connectServer(QUrl(proxyUrl)); + context.proxyConnection->connectServer(url); } void CloudTransport::remoteConnectionStateChanged(RemoteProxyConnection::State state) From 1e2d4b33cfcb9c22bcb468c6436b31b2054409c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 15 Mar 2022 15:44:02 +0100 Subject: [PATCH 9/9] Add new line for proxy connection in order to support tcp paket splitting --- libnymea-core/cloud/cloudtransport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnymea-core/cloud/cloudtransport.cpp b/libnymea-core/cloud/cloudtransport.cpp index 49de362f..b7557ec1 100644 --- a/libnymea-core/cloud/cloudtransport.cpp +++ b/libnymea-core/cloud/cloudtransport.cpp @@ -48,7 +48,7 @@ void CloudTransport::sendData(const QUuid &clientId, const QByteArray &data) qCDebug(dcCloudTraffic()) << "Sending data" << clientId << data; foreach (const ConnectionContext &ctx, m_connections) { if (ctx.clientId == clientId) { - ctx.proxyConnection->sendData(data); + ctx.proxyConnection->sendData(data + '\n'); return; } }