diff --git a/README.md b/README.md index 32c0191..271dd99 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ In the build directory you can find the resulting library and binary files. If you want to start the proxy server from the build directory, you need to export the library path before starting the application: - $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/libnymea-remoteproxy - $ ./server/nymea-remoteproxy + $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$(pwd)/libnymea-remoteproxy:$(pwd)/libnymea-remoteproxyclient + $ ./server/nymea-remoteproxy -c ../nymea-remoteproxy/tests/test-certificate.crt -k ../nymea-remoteproxy/tests/test-certificate.key # Install @@ -46,24 +46,28 @@ In order to get information about the server you can start the command with the $ nymea-remoteproxy --help - usage: nymea-remoteproxy [options] + Usage: nymea-remoteproxy [options] - The nymea remote proxy server. + The nymea remote proxy server. This server allowes nymea-cloud users and registered nymea deamons to establish a tunnel connection. Copyright © 2018 Simon Stürz Options: -h, --help Displays this help. -l, --logging Write log file to the given logfile. - -s, --server The authentication server url. - -p, --port The proxy server port. - -c, --certificate The path to the SSL certificate. + -s, --server The server address this proxy will + listen on. Default is 127.0.0.1 + -p, --port The proxy server port. Default is + 1212 + -c, --certificate The path to the SSL certificate used + for this proxy server. -k, --certificate-key The path to the SSL certificate key - (KEY). - -v, --verbose Print the whole traffic. - - - + used for this proxy server. + -a, --authentication-server The server url of the AWS + authentication server. + -v, --verbose Print more verbose. + + # Server API Once a client connects to the proxy server, he must authenticate him self by passing the token received from the nymea-cloud mqtt connection request. diff --git a/libnymea-remoteproxy/engine.cpp b/libnymea-remoteproxy/engine.cpp index 64431d9..ce6dcc1 100644 --- a/libnymea-remoteproxy/engine.cpp +++ b/libnymea-remoteproxy/engine.cpp @@ -1,4 +1,5 @@ #include "engine.h" +#include "websocketserver.h" #include "loggingcategories.h" Engine *Engine::s_instance = nullptr; @@ -30,24 +31,40 @@ void Engine::destroy() void Engine::start() { - qCDebug(dcEngine()) << "Start server engine"; + if (!m_running) + qCDebug(dcEngine()) << "Start server engine"; + QUrl proxyUrl; proxyUrl.setScheme("wss"); - proxyUrl.setHost("0.0.0.0"); - proxyUrl.setPort(static_cast(m_port)); + proxyUrl.setHost(m_webSocketServerHostAddress.toString()); + proxyUrl.setPort(m_webSocketServerPort); + qCDebug(dcApplication()) << "Authentication server" << m_authenticationServerUrl.toString(); qCDebug(dcApplication()) << "Start server" << proxyUrl.toString(); - // TODO: init stuff + // Init WebSocketServer + if (m_webSocketServer) { + delete m_webSocketServer; + m_webSocketServer = nullptr; + } + + m_webSocketServer = new WebSocketServer(m_sslConfiguration, this); + m_webSocketServer->setServerUrl(proxyUrl); + m_webSocketServer->startServer(); setRunning(true); } void Engine::stop() { - qCDebug(dcEngine()) << "Stop server engine"; + if (m_running) + qCDebug(dcEngine()) << "Stop server engine"; - // TODO: deinit stuff + if (m_webSocketServer) { + m_webSocketServer->stopServer(); + m_webSocketServer->deleteLater(); + m_webSocketServer = nullptr; + } setRunning(false); } @@ -57,32 +74,49 @@ bool Engine::running() const return m_running; } -void Engine::setHost(const QHostAddress &hostAddress) +void Engine::setWebSocketServerHostAddress(const QHostAddress &hostAddress) { - m_hostAddress = hostAddress; + qCDebug(dcEngine()) << "Websocket server host address:" << hostAddress; + m_webSocketServerHostAddress = hostAddress; } -void Engine::setAuthenticationServerUrl(const QUrl &url) +void Engine::setWebSocketServerPort(const quint16 &port) { - m_authenticationServerUrl = url; -} - -void Engine::setPort(const quint16 &port) -{ - m_port = port; + qCDebug(dcEngine()) << "Websocket server port:" << port; + m_webSocketServerPort = port; } void Engine::setSslConfiguration(const QSslConfiguration &configuration) { + qCDebug(dcEngine()) << "SSL Configuration:"; + qCDebug(dcEngine()) << " Common name:" << configuration.localCertificate().issuerInfo(QSslCertificate::CommonName); + qCDebug(dcEngine()) << " Organisation:" << configuration.localCertificate().issuerInfo(QSslCertificate::Organization); + qCDebug(dcEngine()) << " Organisation unit name:" << configuration.localCertificate().issuerInfo(QSslCertificate::OrganizationalUnitName); + qCDebug(dcEngine()) << " Country name:" << configuration.localCertificate().issuerInfo(QSslCertificate::CountryName); + qCDebug(dcEngine()) << " Locality name:" << configuration.localCertificate().issuerInfo(QSslCertificate::LocalityName); + qCDebug(dcEngine()) << " State/Province:" << configuration.localCertificate().issuerInfo(QSslCertificate::StateOrProvinceName); + qCDebug(dcEngine()) << " Email address:" << configuration.localCertificate().issuerInfo(QSslCertificate::EmailAddress); + m_sslConfiguration = configuration; } +void Engine::setAuthenticationServerUrl(const QUrl &url) +{ + qCDebug(dcEngine()) << "Authentication server URL" << url.toString(); + m_authenticationServerUrl = url; +} + Engine::Engine(QObject *parent) : QObject(parent) { } +Engine::~Engine() +{ + stop(); +} + void Engine::setRunning(bool running) { if (m_running == running) diff --git a/libnymea-remoteproxy/engine.h b/libnymea-remoteproxy/engine.h index 59029e0..b2d1e71 100644 --- a/libnymea-remoteproxy/engine.h +++ b/libnymea-remoteproxy/engine.h @@ -6,6 +6,8 @@ #include #include +class WebSocketServer; + class Engine : public QObject { Q_OBJECT @@ -19,23 +21,27 @@ public: bool running() const; - void setHost(const QHostAddress &hostAddress); - void setAuthenticationServerUrl(const QUrl &url); - void setPort(const quint16 &port); + void setWebSocketServerHostAddress(const QHostAddress &hostAddress); + void setWebSocketServerPort(const quint16 &port); + void setSslConfiguration(const QSslConfiguration &configuration); + void setAuthenticationServerUrl(const QUrl &url); private: explicit Engine(QObject *parent = nullptr); + ~Engine(); static Engine *s_instance; bool m_running = false; - quint16 m_port = 0; - QHostAddress m_hostAddress; - QSslConfiguration m_sslConfiguration; + quint16 m_webSocketServerPort = 1212; + QHostAddress m_webSocketServerHostAddress = QHostAddress::LocalHost; + QSslConfiguration m_sslConfiguration; QUrl m_authenticationServerUrl; + WebSocketServer *m_webSocketServer = nullptr; + void setRunning(bool running); signals: diff --git a/libnymea-remoteproxy/libnymea-remoteproxy.pro b/libnymea-remoteproxy/libnymea-remoteproxy.pro index 0d0aa84..078a69e 100644 --- a/libnymea-remoteproxy/libnymea-remoteproxy.pro +++ b/libnymea-remoteproxy/libnymea-remoteproxy.pro @@ -5,8 +5,21 @@ TARGET = nymea-remoteproxy HEADERS += \ engine.h \ - loggingcategories.h + loggingcategories.h \ + transportinterface.h \ + websocketserver.h SOURCES += \ engine.cpp \ - loggingcategories.cpp + loggingcategories.cpp \ + transportinterface.cpp \ + websocketserver.cpp + + +# install header file with relative subdirectory +for(header, HEADERS) { + path = /usr/include/nymea-remoteproxy/$${dirname(header)} + eval(headers_$${path}.files += $${header}) + eval(headers_$${path}.path = $${path}) + eval(INSTALLS *= headers_$${path}) +} diff --git a/libnymea-remoteproxy/loggingcategories.cpp b/libnymea-remoteproxy/loggingcategories.cpp index 193f15c..92f4931 100644 --- a/libnymea-remoteproxy/loggingcategories.cpp +++ b/libnymea-remoteproxy/loggingcategories.cpp @@ -4,6 +4,7 @@ Q_LOGGING_CATEGORY(dcApplication, "Application") Q_LOGGING_CATEGORY(dcEngine, "Engine") Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc") Q_LOGGING_CATEGORY(dcWebSocketServer, "WebSocketServer") +Q_LOGGING_CATEGORY(dcWebSocketServerTraffic, "WebSocketServerTraffic") Q_LOGGING_CATEGORY(dcAuthenticator, "Authenticator") Q_LOGGING_CATEGORY(dcDebug, "Debug") Q_LOGGING_CATEGORY(dcConnectionManager, "ConnectionManager") diff --git a/libnymea-remoteproxy/loggingcategories.h b/libnymea-remoteproxy/loggingcategories.h index c0891f1..070af87 100644 --- a/libnymea-remoteproxy/loggingcategories.h +++ b/libnymea-remoteproxy/loggingcategories.h @@ -8,6 +8,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcApplication) Q_DECLARE_LOGGING_CATEGORY(dcEngine) Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc) Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServer) +Q_DECLARE_LOGGING_CATEGORY(dcWebSocketServerTraffic) Q_DECLARE_LOGGING_CATEGORY(dcAuthenticator) Q_DECLARE_LOGGING_CATEGORY(dcConnectionManager) Q_DECLARE_LOGGING_CATEGORY(dcDebug) diff --git a/libnymea-remoteproxy/transportinterface.cpp b/libnymea-remoteproxy/transportinterface.cpp new file mode 100644 index 0000000..5aff4e6 --- /dev/null +++ b/libnymea-remoteproxy/transportinterface.cpp @@ -0,0 +1,12 @@ +#include "transportinterface.h" + +TransportInterface::TransportInterface(QObject *parent) : + QObject(parent) +{ + +} + +TransportInterface::~TransportInterface() +{ + +} diff --git a/libnymea-remoteproxy/transportinterface.h b/libnymea-remoteproxy/transportinterface.h new file mode 100644 index 0000000..b1a208e --- /dev/null +++ b/libnymea-remoteproxy/transportinterface.h @@ -0,0 +1,27 @@ +#ifndef TRANSPORTINTERFACE_H +#define TRANSPORTINTERFACE_H + +#include + +class TransportInterface : public QObject +{ + Q_OBJECT +public: + explicit TransportInterface(QObject *parent = nullptr); + virtual ~TransportInterface() = 0; + + virtual void sendData(const QUuid &clientId, const QByteArray &data) = 0; + virtual void sendData(const QList &clients, const QByteArray &data) = 0; + +signals: + void clientConnected(const QUuid &clientId); + void clientDisconnected(const QUuid &clientId); + void dataAvailable(const QUuid &clientId, const QByteArray &data); + +public slots: + virtual bool startServer() = 0; + virtual bool stopServer() = 0; + +}; + +#endif // TRANSPORTINTERFACE_H diff --git a/libnymea-remoteproxy/websocketserver.cpp b/libnymea-remoteproxy/websocketserver.cpp new file mode 100644 index 0000000..d698148 --- /dev/null +++ b/libnymea-remoteproxy/websocketserver.cpp @@ -0,0 +1,162 @@ +#include "websocketserver.h" +#include "loggingcategories.h" + +#include + +WebSocketServer::WebSocketServer(const QSslConfiguration &sslConfiguration, QObject *parent) : + TransportInterface(parent), + m_sslConfiguration(sslConfiguration) +{ + +} + +WebSocketServer::~WebSocketServer() +{ + stopServer(); +} + +QUrl WebSocketServer::serverUrl() const +{ + return m_serverUrl; +} + +void WebSocketServer::setServerUrl(const QUrl &serverUrl) +{ + m_serverUrl = serverUrl; +} + +bool WebSocketServer::running() const +{ + return m_server->isListening(); +} + +QSslConfiguration WebSocketServer::sslConfiguration() const +{ + return m_sslConfiguration; +} + +void WebSocketServer::sendData(const QUuid &clientId, const QByteArray &data) +{ + QWebSocket *client = nullptr; + client = m_clientList.value(clientId); + if (client) { + qCDebug(dcWebSocketServerTraffic()) << "--> Sending data to client:" << data; + client->sendTextMessage(data + '\n'); + } else { + qCWarning(dcWebSocketServer()) << "Client" << clientId << "unknown to this transport"; + } +} + +void WebSocketServer::sendData(const QList &clients, const QByteArray &data) +{ + foreach (const QUuid &client, clients) { + sendData(client, data); + } +} + +void WebSocketServer::onClientConnected() +{ + // Got a new client connected + QWebSocket *client = m_server->nextPendingConnection(); + + // Check websocket version + if (client->version() != QWebSocketProtocol::Version13) { + qCWarning(dcWebSocketServer()) << "Client with invalid protocol version" << client->version() << ". Rejecting."; + client->close(QWebSocketProtocol::CloseCodeProtocolError, QString("invalid protocol version: %1 != Supported Version 13").arg(client->version())); + delete client; + return; + } + + // Create new uuid for this connection + QUuid clientId = QUuid::createUuid(); + qCDebug(dcWebSocketServer()) << "New client connected:" << client->peerAddress().toString() << clientId; + + // Append the new client to the client list + m_clientList.insert(clientId, client); + + connect(client, SIGNAL(pong(quint64,QByteArray)), this, SLOT(onPing(quint64,QByteArray))); + connect(client, SIGNAL(binaryMessageReceived(QByteArray)), this, SLOT(onBinaryMessageReceived(QByteArray))); + connect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(onTextMessageReceived(QString))); + connect(client, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(onClientError(QAbstractSocket::SocketError))); + connect(client, SIGNAL(disconnected()), this, SLOT(onClientDisconnected())); + + emit clientConnected(clientId); +} + +void WebSocketServer::onClientDisconnected() +{ + QWebSocket *client = static_cast(sender()); + QUuid clientId = m_clientList.key(client); + + qCDebug(dcWebSocketServer()) << "Client disconnected:" << client->peerAddress().toString() << clientId; + + m_clientList.take(clientId)->deleteLater(); + emit clientDisconnected(clientId); +} + +void WebSocketServer::onBinaryMessageReceived(const QByteArray &data) +{ + QWebSocket *client = static_cast(sender()); + qCDebug(dcWebSocketServerTraffic()) << "<-- Binary message from" << client->peerAddress().toString() << ":" << data; +} + +void WebSocketServer::onTextMessageReceived(const QString &message) +{ + QWebSocket *client = static_cast(sender()); + qCDebug(dcWebSocketServerTraffic()) << "Text message from" << client->peerAddress().toString() << ":" << message; + emit dataAvailable(m_clientList.key(client), message.toUtf8()); +} + +void WebSocketServer::onClientError(QAbstractSocket::SocketError error) +{ + QWebSocket *client = static_cast(sender()); + qCWarning(dcWebSocketServer()) << "Client error occured:" << error << client->errorString(); +} + +void WebSocketServer::onServerError(QAbstractSocket::SocketError error) +{ + qCWarning(dcWebSocketServer()) << "Server error occured:" << error << m_server->errorString(); +} + +void WebSocketServer::onPing(quint64 elapsedTime, const QByteArray &payload) +{ + QWebSocket *client = static_cast(sender()); + qCDebug(dcWebSocketServer) << "ping response" << client->peerAddress() << elapsedTime << payload; +} + +bool WebSocketServer::startServer() +{ + m_server = new QWebSocketServer(QCoreApplication::applicationName(), QWebSocketServer::SecureMode, this); + m_server->setSslConfiguration(m_sslConfiguration); + + connect (m_server, &QWebSocketServer::newConnection, this, &WebSocketServer::onClientConnected); + connect (m_server, &QWebSocketServer::acceptError, this, &WebSocketServer::onServerError); + + qCDebug(dcWebSocketServer()) << "Starting server" << m_server->serverName() << m_serverUrl.toString(); + if (!m_server->listen(QHostAddress(m_serverUrl.host()), static_cast(m_serverUrl.port()))) { + qCWarning(dcWebSocketServer()) << "Server" << m_server->serverName() << "could not listen on" << serverUrl().toString(); + return false; + } + + qCDebug(dcWebSocketServer()) << "Server started successfully."; + return true; +} + +bool WebSocketServer::stopServer() +{ + + // Clean up client connections + foreach (QWebSocket *client, m_clientList.values()) { + client->close(QWebSocketProtocol::CloseCodeNormal, "Stop server"); + } + + // Delete the server object + if (m_server) { + qCDebug(dcWebSocketServer()) << "Stop server" << m_server->serverName() << m_serverUrl.toString(); + m_server->close(); + delete m_server; + m_server = nullptr; + } + + return true; +} diff --git a/libnymea-remoteproxy/websocketserver.h b/libnymea-remoteproxy/websocketserver.h new file mode 100644 index 0000000..8d10769 --- /dev/null +++ b/libnymea-remoteproxy/websocketserver.h @@ -0,0 +1,53 @@ +#ifndef WEBSOCKETSERVER_H +#define WEBSOCKETSERVER_H + +#include +#include +#include +#include +#include +#include + +#include "transportinterface.h" + +class WebSocketServer : public TransportInterface +{ + Q_OBJECT +public: + explicit WebSocketServer(const QSslConfiguration &sslConfiguration, QObject *parent = nullptr); + ~WebSocketServer() override; + + QUrl serverUrl() const; + void setServerUrl(const QUrl &serverUrl); + + bool running() const; + + QSslConfiguration sslConfiguration() const; + + void sendData(const QUuid &clientId, const QByteArray &data) override; + void sendData(const QList &clients, const QByteArray &data) override; + +private: + QUrl m_serverUrl; + QWebSocketServer *m_server = nullptr; + QSslConfiguration m_sslConfiguration; + bool m_enabled = false; + + QHash m_clientList; + +private slots: + void onClientConnected(); + void onClientDisconnected(); + void onBinaryMessageReceived(const QByteArray &data); + void onTextMessageReceived(const QString &message); + void onClientError(QAbstractSocket::SocketError error); + void onServerError(QAbstractSocket::SocketError error); + void onPing(quint64 elapsedTime, const QByteArray & payload); + +public slots: + bool startServer() override; + bool stopServer() override; + +}; + +#endif // WEBSOCKETSERVER_H diff --git a/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro b/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro new file mode 100644 index 0000000..8ad071f --- /dev/null +++ b/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro @@ -0,0 +1,20 @@ +include(../nymea-remoteproxy.pri) + +TEMPLATE = lib +TARGET = nymea-remoteproxyclient + +HEADERS += \ + remoteproxyconnector.h + + +SOURCES += \ + remoteproxyconnector.cpp + + +# install header file with relative subdirectory +for(header, HEADERS) { + path = /usr/include/nymea-remoteproxyclient/$${dirname(header)} + eval(headers_$${path}.files += $${header}) + eval(headers_$${path}.path = $${path}) + eval(INSTALLS *= headers_$${path}) +} diff --git a/libnymea-remoteproxyclient/remoteproxyconnector.cpp b/libnymea-remoteproxyclient/remoteproxyconnector.cpp new file mode 100644 index 0000000..5d9d6ba --- /dev/null +++ b/libnymea-remoteproxyclient/remoteproxyconnector.cpp @@ -0,0 +1,6 @@ +#include "remoteproxyconnector.h" + +RemoteProxyConnector::RemoteProxyConnector(QObject *parent) : QObject(parent) +{ + +} diff --git a/libnymea-remoteproxyclient/remoteproxyconnector.h b/libnymea-remoteproxyclient/remoteproxyconnector.h new file mode 100644 index 0000000..b883259 --- /dev/null +++ b/libnymea-remoteproxyclient/remoteproxyconnector.h @@ -0,0 +1,18 @@ +#ifndef REMOTEPROXYCONNECTOR_H +#define REMOTEPROXYCONNECTOR_H + +#include + +class RemoteProxyConnector : public QObject +{ + Q_OBJECT +public: + explicit RemoteProxyConnector(QObject *parent = nullptr); + +signals: + +public slots: + +}; + +#endif // REMOTEPROXYCONNECTOR_H diff --git a/nymea-remoteproxy.pri b/nymea-remoteproxy.pri index cccb9ae..efcba55 100644 --- a/nymea-remoteproxy.pri +++ b/nymea-remoteproxy.pri @@ -1,4 +1,4 @@ -QT *= network +QT *= network websockets QT -= gui CONFIG += c++11 console diff --git a/nymea-remoteproxy.pro b/nymea-remoteproxy.pro index 1e6ecac..67c3a5a 100644 --- a/nymea-remoteproxy.pro +++ b/nymea-remoteproxy.pro @@ -1,8 +1,8 @@ include(nymea-remoteproxy.pri) TEMPLATE=subdirs -SUBDIRS += server libnymea-remoteproxy tests +SUBDIRS += server libnymea-remoteproxy libnymea-remoteproxyclient tests server.depends = libnymea-remoteproxy -tests.depends = libnymea-remoteproxy +tests.depends = libnymea-remoteproxy libnymea-remoteproxyclient diff --git a/server/main.cpp b/server/main.cpp index 8be4d87..9635ab0 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -97,25 +97,28 @@ int main(int argc, char *argv[]) // command line parser QCommandLineParser parser; parser.addHelpOption(); - parser.setApplicationDescription(QString("\nThe nymea remote proxy server.\n\n" + parser.setApplicationDescription(QString("\nThe nymea remote proxy server. This server allowes nymea-cloud users and registered nymea deamons to establish a tunnel connection.\n\n" "Copyright %1 2018 Simon Stürz ").arg(QChar(0xA9))); QCommandLineOption logfileOption(QStringList() << "l" << "logging", "Write log file to the given logfile.", "logfile", "/var/log/nymea-remoteproxy.log"); parser.addOption(logfileOption); - QCommandLineOption serverOption(QStringList() << "s" << "server", "The authentication server url.", "url", "http://localhost:8000"); + QCommandLineOption serverOption(QStringList() << "s" << "server", "The server address this proxy will listen on. Default is 127.0.0.1", "hostaddress", "127.0.0.1"); parser.addOption(serverOption); - QCommandLineOption portOption(QStringList() << "p" << "port", "The proxy server port.", "port", "1212"); + QCommandLineOption portOption(QStringList() << "p" << "port", "The proxy server port. Default is 1212", "port", "1212"); parser.addOption(portOption); - QCommandLineOption certOption(QStringList() << "c" <<"certificate", "The path to the SSL certificate.", "certificate"); + QCommandLineOption certOption(QStringList() << "c" <<"certificate", "The path to the SSL certificate used for this proxy server.", "certificate"); parser.addOption(certOption); - QCommandLineOption certKeyOption(QStringList() << "k" << "certificate-key", "The path to the SSL certificate key (KEY).", "certificate-key"); + QCommandLineOption certKeyOption(QStringList() << "k" << "certificate-key", "The path to the SSL certificate key used for this proxy server.", "certificate-key"); parser.addOption(certKeyOption); - QCommandLineOption verboseOption(QStringList() << "v" << "verbose", "Print the whole traffic."); + QCommandLineOption authenticationUrlOption(QStringList() << "a" << "authentication-server", "The server url of the AWS authentication server.", "url", "https://127.0.0.1"); + parser.addOption(authenticationUrlOption); + + QCommandLineOption verboseOption(QStringList() << "v" << "verbose", "Print more verbose."); parser.addOption(verboseOption); parser.process(application); @@ -141,37 +144,24 @@ int main(int argc, char *argv[]) s_loggingEnabled = true; } - qCDebug(dcApplication()) << "=============================================="; - qCDebug(dcApplication()) << "Starting" << application.applicationName() << application.applicationVersion(); - qCDebug(dcApplication()) << "=============================================="; - - if (s_loggingEnabled) - qCDebug(dcApplication()) << "Logging enabled. Writing logs to" << s_logFile.fileName(); - - // Authentication server url - QUrl serverUrl("http://localhost:8000"); - if (parser.isSet(serverOption)) - serverUrl = QUrl(parser.value(serverOption)); - - if (!serverUrl.isValid()) { - qCCritical(dcApplication()) << "Invalid authentication server url:" << parser.value(serverOption); + // Proxy server host address + QHostAddress serverHostAddress = QHostAddress(parser.value(serverOption)); + if (serverHostAddress.isNull()) { + qCCritical(dcApplication()) << "Invalid hostaddress for the proxy server:" << parser.value(serverOption); exit(-1); } // Port - uint port = 1212; - if (parser.isSet(portOption)) { - bool ok = false; - port = parser.value(portOption).toUInt(&ok); - if (!ok) { - qCCritical(dcApplication()) << "Invalud port value:" << parser.value(portOption); - exit(-1); - } + bool ok = false; + uint port = parser.value(portOption).toUInt(&ok); + if (!ok) { + qCCritical(dcApplication()) << "Invalid port value:" << parser.value(portOption); + exit(-1); + } - if (port > 65535) { - qCCritical(dcApplication()) << "Port value is out of range:" << parser.value(portOption); - exit(-1); - } + if (port > 65535) { + qCCritical(dcApplication()) << "Port value is out of range:" << parser.value(portOption); + exit(-1); } // SSL certificate @@ -198,18 +188,40 @@ int main(int argc, char *argv[]) if (parser.isSet(certKeyOption)) { QFile certKeyFile(parser.value(certKeyOption)); if (!certKeyFile.open(QIODevice::ReadOnly)) { - qCCritical(dcApplication()) << "ERROR: Could not open certificate key file:" << parser.value(certKeyOption) << certKeyFile.errorString(); + qCCritical(dcApplication()) << "Could not open certificate key file:" << parser.value(certKeyOption) << certKeyFile.errorString(); exit(-1); } + QSslKey sslKey(&certKeyFile, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey); qCDebug(dcApplication()) << "Loaded successfully certificate key" << parser.value(certKeyOption); certKeyFile.close(); sslConfiguration.setPrivateKey(sslKey); } - Engine::instance()->setAuthenticationServerUrl(serverUrl); - Engine::instance()->setPort(static_cast(port)); + if (sslConfiguration.isNull()) { + qCCritical(dcApplication()) << "No SSL configuration specified. The server does not suppoert insecure connections."; + exit(-1); + } + + // Authentication server url + QUrl authenticationServerUrl(parser.value(authenticationUrlOption)); + if (!authenticationServerUrl.isValid()) { + qCCritical(dcApplication()) << "Invalid authentication server url:" << parser.value(authenticationUrlOption); + exit(-1); + } + + qCDebug(dcApplication()) << "=============================================="; + qCDebug(dcApplication()) << "Starting" << application.applicationName() << application.applicationVersion(); + qCDebug(dcApplication()) << "=============================================="; + + if (s_loggingEnabled) + qCDebug(dcApplication()) << "Logging enabled. Writing logs to" << s_logFile.fileName(); + + // Configure and start the engines + Engine::instance()->setWebSocketServerHostAddress(serverHostAddress); + Engine::instance()->setWebSocketServerPort(static_cast(port)); Engine::instance()->setSslConfiguration(sslConfiguration); + Engine::instance()->setAuthenticationServerUrl(authenticationServerUrl); Engine::instance()->start(); return application.exec(); diff --git a/tests/certificate.qrc b/tests/certificate.qrc new file mode 100644 index 0000000..f92e6f8 --- /dev/null +++ b/tests/certificate.qrc @@ -0,0 +1,6 @@ + + + test-certificate.crt + test-certificate.key + + diff --git a/tests/nymea-remoteproxy-tests.cpp b/tests/nymea-remoteproxy-tests.cpp index 7578713..5c1f50a 100644 --- a/tests/nymea-remoteproxy-tests.cpp +++ b/tests/nymea-remoteproxy-tests.cpp @@ -6,7 +6,28 @@ RemoteProxyTests::RemoteProxyTests(QObject *parent) : QObject(parent) { + QFile certificateFile(":/test-certificate.crt"); + if (!certificateFile.open(QIODevice::ReadOnly)) { + qWarning() << "Could not open resource file" << certificateFile.fileName(); + exit(1); + } + QByteArray certificateData = certificateFile.readAll(); + //qDebug() << "Certificate:" << endl << qUtf8Printable(certificateData); + + QFile keyFile(":/test-certificate.key"); + if (!keyFile.open(QIODevice::ReadOnly)) { + qWarning() << "Could not open resource file" << keyFile.fileName(); + exit(1); + } + + QByteArray keyData = keyFile.readAll(); + //qDebug() << "Certificate key:" << endl << qUtf8Printable(keyData); + + m_sslConfiguration.setPrivateKey(QSslKey(keyData, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey)); + m_sslConfiguration.setLocalCertificate(QSslCertificate(certificateData, QSsl::Pem)); + m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); + m_sslConfiguration.setProtocol(QSsl::TlsV1_2OrLater); } void RemoteProxyTests::cleanUpEngine() @@ -25,6 +46,17 @@ void RemoteProxyTests::restartEngine() QVERIFY(Engine::exists()); } +void RemoteProxyTests::startServer() +{ + restartEngine(); + + Engine::instance()->setWebSocketServerPort(m_port); + Engine::instance()->setWebSocketServerHostAddress(QHostAddress::LocalHost); + Engine::instance()->setSslConfiguration(m_sslConfiguration); + Engine::instance()->start(); + +} + void RemoteProxyTests::initTestCase() { qCDebug(dcApplication()) << "Init test case."; @@ -37,6 +69,14 @@ void RemoteProxyTests::cleanupTestCase() cleanUpEngine(); } +void RemoteProxyTests::authenticate() +{ + startServer(); + + + Engine::instance()->stop(); +} + void RemoteProxyTests::startStopServer() { cleanUpEngine(); diff --git a/tests/nymea-remoteproxy-tests.h b/tests/nymea-remoteproxy-tests.h index 9d72ac4..6bb982d 100644 --- a/tests/nymea-remoteproxy-tests.h +++ b/tests/nymea-remoteproxy-tests.h @@ -2,7 +2,11 @@ #define NYMEA_REMOTEPROXY_TESTS_H #include +#include #include +#include +#include +#include class RemoteProxyTests : public QObject { @@ -11,8 +15,13 @@ public: explicit RemoteProxyTests(QObject *parent = nullptr); private: + quint16 m_port = 1212; + QHostAddress m_serverAddress = QHostAddress::LocalHost; + QSslConfiguration m_sslConfiguration; + void cleanUpEngine(); void restartEngine(); + void startServer(); protected slots: void initTestCase(); @@ -21,9 +30,7 @@ protected slots: private slots: void startStopServer(); - - - + void authenticate(); }; diff --git a/tests/test-certificate.crt b/tests/test-certificate.crt new file mode 100644 index 0000000..f49f60c --- /dev/null +++ b/tests/test-certificate.crt @@ -0,0 +1,23 @@ +-----BEGIN CERTIFICATE----- +MIID4TCCAsmgAwIBAgIJAPikDhNDwsqGMA0GCSqGSIb3DQEBCwUAMIGFMQswCQYD +VQQGEwJBVDEPMA0GA1UECAwGVmllbm5hMQ8wDQYDVQQHDAZWaWVubmExEjAQBgNV +BAoMCWd1aCBHbWJILjELMAkGA1UECwwCUUExEjAQBgNVBAMMCWxvY2FsaG9zdDEf +MB0GCSqGSIb3DQEJARYQZGV2ZWxvcGVyQGd1aC5pbzAgFw0xODA4MDIxNDU3NTZa +GA8yMTE4MDcwOTE0NTc1NlowgYUxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZWaWVu +bmExDzANBgNVBAcMBlZpZW5uYTESMBAGA1UECgwJZ3VoIEdtYkguMQswCQYDVQQL +DAJRQTESMBAGA1UEAwwJbG9jYWxob3N0MR8wHQYJKoZIhvcNAQkBFhBkZXZlbG9w +ZXJAZ3VoLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoNm4oJfX +4E7RNzJsscs1+sAgDG2I0fDAwHM1evStk9hjZTHy+T2aLPih1nkAiZvt5mso8v6W +rrRc1a3MtHinJRXP4wv/WPLxACmEzNhEzRpl4d+JmcOHwNclT3xjRJCBsW5SNxr1 +Z2GQICTFJeIBmyHzVcLNGnlrXL61tkSeDCaY3TiaxCRXgtPBtdoJttFFJTXWld2P +V4QSlkPA+9edQmdG6jr5MW/LxxdEyp1kqp+9erZ0BbLFly/sNv2hL1Gy6rlPBdxJ +S09IYXAvqCjSwzA1VN0P35/iHZLigrwe9bt5jMC8bYAU1yOKrMHVwQhSBe6fij7g +EezK1C90BWLOgwIDAQABo1AwTjAdBgNVHQ4EFgQUrT5VDdjUEOzKPWRYelLAI3Nw +4uUwHwYDVR0jBBgwFoAUrT5VDdjUEOzKPWRYelLAI3Nw4uUwDAYDVR0TBAUwAwEB +/zANBgkqhkiG9w0BAQsFAAOCAQEARHMVjrBEKbfNXTsfNAQcSRkAMkCbWG5/NQDl +wHbNEu+VV0OF0AJ0IE1NCC4ZLuyUxSztMJdTOSM2lLmSGjCWyUlKhGvq79MDlomN +jHYLPhSV3pYwaO86qNTtBqAQ1YXuauFboxHrpvsXx5a6ivPl1UyiDP9EtHDvkUpo +tSW0HBC0n+o78vHbGXyzvlyI25UE9jnIDPD2K3jm7bcGqiqrDgT06QyE7GD43eeL +0X6dBKYQWUWcG6cBR9Cvoj1Ec3IcXuprHwfbNrVtYOwdO7678U6gJuk1GeblcsgX +9HfkjP61NIqUpu2U0BQrd3mDkjsNuta2IKLW7ojALRpP1shorA== +-----END CERTIFICATE----- diff --git a/tests/test-certificate.key b/tests/test-certificate.key new file mode 100644 index 0000000..25a383a --- /dev/null +++ b/tests/test-certificate.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCg2bigl9fgTtE3 +MmyxyzX6wCAMbYjR8MDAczV69K2T2GNlMfL5PZos+KHWeQCJm+3mayjy/pautFzV +rcy0eKclFc/jC/9Y8vEAKYTM2ETNGmXh34mZw4fA1yVPfGNEkIGxblI3GvVnYZAg +JMUl4gGbIfNVws0aeWtcvrW2RJ4MJpjdOJrEJFeC08G12gm20UUlNdaV3Y9XhBKW +Q8D7151CZ0bqOvkxb8vHF0TKnWSqn716tnQFssWXL+w2/aEvUbLquU8F3ElLT0hh +cC+oKNLDMDVU3Q/fn+IdkuKCvB71u3mMwLxtgBTXI4qswdXBCFIF7p+KPuAR7MrU +L3QFYs6DAgMBAAECggEAIp+bAV6OBmeQ21vMT90ZKneOrn4pdF9pbkOeYHAoqOls +Q80jqwjalhGS7JKxTe21oOKyNtQcZVDHpus5ZCOQfwgZ7pqXhXMN82X8gweOj8O9 +6Ifwm5ueLiFhk6GH0Kt2b1X69oekCXPTzUd89HGYvObq9SvwcGFwfc9DWA+fIagZ +3PRZunEIJlWYRtksMCdsiHMOCeUt2alz4XMYLb0ng+dHEmjnQEWPGPbKMBUnIM0t +g5yKZCJ0i/FG+to27+hio9dzG4K/bRLvwlFswLZRfEcvqkBXpQ+aATUNA7jQpRxp +e6sWAhpiS+tn2nMniLI+voe0W+v0s+5XLtDRX7LFUQKBgQDQhBTcKXXdnykIOslm +p9D+rFaUf10aXpFz3aq0jjy5lHkCXOPc1NTG29e7Ysi8xKwlZdSca6l+zu0zQf1m +cW4r/ejzwKM3qrLLeh4Hn7wpoWpliH5Z7BCdkTmeqGJI/9h1VxXgjc6j1DTGsyGb +nezNEsNEYmaSZZreConLVGHcuwKBgQDFet79wnbTnqPohySLJy4Q3j+zYXHs/+3I +dgRSg9Q/W2uE2ki58oDc10CRgGCet3iyXL5Pbq+QO/N50jpJHKow0DZqka3PTaLz +hqqj1FK08/Ual6cZ4TViZ09DE6TnQYXEKtdqakr+iNhNu1O4NQpvW+fjMUaSTqTy +DCfqMVrc2QKBgDc6JIRDfvJaC3Ygi+Nio1owryXZrgEj9wZnOaVk3vSQPJTBoaCF +xm19IOCCw4Qr2yR0miFr6pKpn+2plsIhXPrN33tgFFrUHVMDXxzJ0/56wEFwK3cJ +rUTuiAJbutx21xANJsA5DvgZZnkSTN54r5WJWKquHVi9DI2u4Nw310utAoGBALRm +m6DbbivB52A7XLHWF+oG7dBTu3xTDAitbyNmFJXQUPdrZTahBwE3jl+dFCbJ2nOt +75imt7EeYzytRNuXWQ4Al0VhUZNoC+1F30qMYwW1OvqRAXDN4DPFxo2zO1ia7bUX +ofQvp9TAdEEGWOOVaLP0FHDiieKL/3iyjCAP+D55AoGBAMKi7ZkIBqzRhknwp13v +iutRynDO8lXUeLxp/WhPj5Go5S7LbUxEwTlEikxAEdYqtCl7DMxWBgaDojxOahY6 +LBuK53LWCG9y06laGy8gxbNJ86VbjpwwTm1eQeryPg/CSRPVb4G5Rhe4hZJEMdv3 +Kt0IfWn4z2YLCtPtOrqMERSk +-----END PRIVATE KEY----- diff --git a/tests/tests.pro b/tests/tests.pro index 64d4782..c6134be 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -8,6 +8,8 @@ TARGET = nymea-remoteproxy-tests INCLUDEPATH += ../libnymea-remoteproxy LIBS += -L$$top_builddir/libnymea-remoteproxy/ -lnymea-remoteproxy +RESOURCES += certificate.qrc + HEADERS += nymea-remoteproxy-tests.h SOURCES += nymea-remoteproxy-tests.cpp