diff --git a/common/common.pri b/common/common.pri
new file mode 100644
index 0000000..f1eddb5
--- /dev/null
+++ b/common/common.pri
@@ -0,0 +1,7 @@
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/slipdataprocessor.h
+
+SOURCES += \
+ $$PWD/slipdataprocessor.cpp
diff --git a/common/slipdataprocessor.cpp b/common/slipdataprocessor.cpp
new file mode 100644
index 0000000..c02709d
--- /dev/null
+++ b/common/slipdataprocessor.cpp
@@ -0,0 +1,108 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 "slipdataprocessor.h"
+
+#include
+
+QByteArray SlipDataProcessor::deserializeData(const QByteArray &data)
+{
+ QByteArray deserializedData;
+ // Parse serial data
+ bool escaped = false;
+ for (int i = 0; i < data.length(); i++) {
+ quint8 byte = static_cast(data.at(i));
+
+ if (escaped) {
+ if (byte == ProtocolByteTransposedEnd) {
+ deserializedData.append(static_cast(ProtocolByteEnd));
+ } else if (byte == ProtocolByteTransposedEsc) {
+ deserializedData.append(static_cast(ProtocolByteEsc));
+ } else {
+ qWarning() << "Error while deserializing data. Escape character received but the escaped character was not recognized.";
+ return QByteArray();
+ }
+
+ escaped = false;
+ continue;
+ }
+
+ // If escape byte, the next byte has to be a modified byte
+ if (byte == ProtocolByteEsc) {
+ escaped = true;
+ } else {
+ deserializedData.append(static_cast(byte));
+ }
+ }
+
+ return deserializedData;
+}
+
+QByteArray SlipDataProcessor::serializeData(const QByteArray &data)
+{
+ QByteArray serializedData;
+ QDataStream stream(&serializedData, QIODevice::WriteOnly);
+// stream << static_cast(ProtocolByteEnd);
+
+ for (int i = 0; i < data.length(); i++) {
+ quint8 byte = static_cast(data.at(i));
+ switch (byte) {
+ case ProtocolByteEnd:
+ stream << static_cast(ProtocolByteEsc);
+ stream << static_cast(ProtocolByteTransposedEnd);
+ break;
+ case ProtocolByteEsc:
+ stream << static_cast(ProtocolByteEsc);
+ stream << static_cast(ProtocolByteTransposedEsc);
+ break;
+ default:
+ stream << byte;
+ break;
+ }
+ }
+
+ // Add the protocol end byte
+ stream << static_cast(ProtocolByteEnd);
+
+ return serializedData;
+}
+
+SlipDataProcessor::Frame SlipDataProcessor::parseFrame(const QByteArray &data)
+{
+ Frame frame;
+ frame.socketAddress = static_cast(static_cast(data.at(0)) << 8 | data.at(1));
+ frame.data = data.right(data.length() - 2);
+ return frame;
+}
+
+QByteArray SlipDataProcessor::buildFrame(const Frame &frame)
+{
+ QByteArray addressData;
+ QDataStream stream(&addressData, QIODevice::WriteOnly);
+ stream << frame.socketAddress;
+ return addressData + frame.data;
+}
diff --git a/common/slipdataprocessor.h b/common/slipdataprocessor.h
new file mode 100644
index 0000000..9e848f8
--- /dev/null
+++ b/common/slipdataprocessor.h
@@ -0,0 +1,64 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 SLIPDATAPROCESSOR_H
+#define SLIPDATAPROCESSOR_H
+
+#include
+#include
+
+// SLIP: https://tools.ietf.org/html/rfc1055
+
+class SlipDataProcessor
+{
+ Q_GADGET
+
+public:
+ enum ProtocolByte {
+ ProtocolByteEnd = 0xC0,
+ ProtocolByteEsc = 0xDB,
+ ProtocolByteTransposedEnd = 0xDC,
+ ProtocolByteTransposedEsc = 0xDD
+ };
+ Q_ENUM(ProtocolByte)
+
+ typedef struct Frame {
+ quint16 socketAddress;
+ QByteArray data;
+ } Frame;
+
+ explicit SlipDataProcessor() = default;
+
+ static QByteArray deserializeData(const QByteArray &data);
+ static QByteArray serializeData(const QByteArray &data);
+
+ static Frame parseFrame(const QByteArray &data);
+ static QByteArray buildFrame(const Frame &frame);
+
+};
+
+#endif // SLIPDATAPROCESSOR_H
diff --git a/docs/remote-connection-basic-flow.png b/docs/remote-connection-basic-flow.png
index 5048002..dceb02d 100644
Binary files a/docs/remote-connection-basic-flow.png and b/docs/remote-connection-basic-flow.png differ
diff --git a/docs/remote-connection-basic-flow.svg b/docs/remote-connection-basic-flow.svg
index 1d5a26c..f3bf1ea 100644
--- a/docs/remote-connection-basic-flow.svg
+++ b/docs/remote-connection-basic-flow.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/docs/remote-connection-basic-flow.txt b/docs/remote-connection-basic-flow.txt
index 29b93a8..0b80577 100644
--- a/docs/remote-connection-basic-flow.txt
+++ b/docs/remote-connection-basic-flow.txt
@@ -18,11 +18,8 @@ note over proxy: Server: Assign address for this client socket (0x0001)
proxy->nymea: SLIP:0x0000: ProxyTunnel.ClientConnected (address: 0x0001)
-proxy<-nymea: SLIP:0x0000: ProxyTunnel.AcceptClient (address: 0x0001)
-proxy->nymea: TunnelProxyErrorNoError
-
-proxy->client: ProxyTunnel.ConfirmClient(uuid, name, serverUuid)
+proxy->client: TunnelProxyErrorNoError
note over client: Connected\nAny incomming and outgoing data will\nbe from the connected nymea instance\nuntil disconnected.
@@ -30,10 +27,14 @@ note over nymea, client: Connected: The client can now communicate with nymea di
-proxy<-client: "data"
+proxy<-client: "request data"
-nymea<-proxy: SLIP:0x0001 "data"
+nymea<-proxy: SLIP:0x0001 "request data"
-nymea->proxy: SLIP:0x0001 "data"
+nymea->proxy: SLIP:0x0001 "response data"
-proxy->client: "data"
+proxy->client: "response data"
+
+nymea->proxy: SLIP:0x0001 "notification data"
+
+proxy->client: "notification data"
diff --git a/libnymea-remoteproxy/jsonrpc/jsonhandler.cpp b/libnymea-remoteproxy/jsonrpc/jsonhandler.cpp
index fc33f35..8b9dc88 100644
--- a/libnymea-remoteproxy/jsonrpc/jsonhandler.cpp
+++ b/libnymea-remoteproxy/jsonrpc/jsonhandler.cpp
@@ -101,7 +101,7 @@ QPair JsonHandler::validateReturns(const QString &methodName, con
void JsonHandler::setDescription(const QString &methodName, const QString &description)
{
- for(int i = 0; i < metaObject()->methodCount(); ++i) {
+ for (int i = 0; i < metaObject()->methodCount(); ++i) {
QMetaMethod method = metaObject()->method(i);
if (method.name() == methodName) {
m_descriptions.insert(methodName, description);
@@ -113,7 +113,7 @@ void JsonHandler::setDescription(const QString &methodName, const QString &descr
void JsonHandler::setParams(const QString &methodName, const QVariantMap ¶ms)
{
- for(int i = 0; i < metaObject()->methodCount(); ++i) {
+ for (int i = 0; i < metaObject()->methodCount(); ++i) {
QMetaMethod method = metaObject()->method(i);
if (method.name() == methodName) {
m_params.insert(methodName, params);
@@ -125,7 +125,7 @@ void JsonHandler::setParams(const QString &methodName, const QVariantMap ¶ms
void JsonHandler::setReturns(const QString &methodName, const QVariantMap &returns)
{
- for(int i = 0; i < metaObject()->methodCount(); ++i) {
+ for (int i = 0; i < metaObject()->methodCount(); ++i) {
QMetaMethod method = metaObject()->method(i);
if (method.name() == methodName) {
m_returns.insert(methodName, returns);
diff --git a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp
index 277397d..c5456d8 100644
--- a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp
+++ b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.cpp
@@ -47,11 +47,21 @@ TunnelProxyHandler::TunnelProxyHandler(QObject *parent) : JsonHandler(parent)
params.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
setParams("RegisterServer", params);
returns.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorRef());
+ returns.insert("slipEnabled", JsonTypes::basicTypeToString(JsonTypes::Bool));
setReturns("RegisterServer", returns);
+ params.clear(); returns.clear();
+ setDescription("DisconnectClient", "A registered server can ask the remote proxy connection to disconnect a client for whatever reason.");
+ params.insert("socketAddress", JsonTypes::basicTypeToString(JsonTypes::UInt));
+ setParams("DisconnectClient", params);
+ returns.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorRef());
+ setReturns("DisconnectClient", returns);
+
+
// Client
params.clear(); returns.clear();
- setDescription("RegisterClient", "Register a new TunnelProxy client on TunnelProxy server with the given serverUuid..");
+ setDescription("RegisterClient", "Register a new TunnelProxy client on TunnelProxy server with the given serverUuid. "
+ "On success, the remote connection has been accepted and any further data will come from the connected server.");
params.insert("clientName", JsonTypes::basicTypeToString(JsonTypes::String));
params.insert("clientUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
params.insert("serverUuid", JsonTypes::basicTypeToString(JsonTypes::Uuid));
@@ -64,10 +74,11 @@ TunnelProxyHandler::TunnelProxyHandler(QObject *parent) : JsonHandler(parent)
// Server
params.clear(); returns.clear();
setDescription("ClientConnected", "Emitted whenever a new client has been connected to a registered server. "
- "Only tunnel proxy clients registered as server will receive this notification.");
- params.insert("clientId", JsonTypes::basicTypeToString(JsonTypes::UInt));
- params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String));
- params.insert("address", JsonTypes::basicTypeToString(JsonTypes::String));
+ "Only tunnel proxy clients registered as server will receive this notification. The socket address will be used for framing.");
+ params.insert("clientName", JsonTypes::basicTypeToString(JsonTypes::String));
+ params.insert("clientUuid", JsonTypes::basicTypeToString(JsonTypes::String));
+ params.insert("clientPeerAddress", JsonTypes::basicTypeToString(JsonTypes::String));
+ params.insert("socketAddress", JsonTypes::basicTypeToString(JsonTypes::UInt));
setParams("ClientConnected", params);
params.clear(); returns.clear();
@@ -100,9 +111,21 @@ JsonReply *TunnelProxyHandler::RegisterServer(const QVariantMap ¶ms, Transpo
QVariantMap response;
response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
+ response.insert("slipEnabled", error == TunnelProxyServer::TunnelProxyErrorNoError);
return createReply("RegisterServer", response);
}
+JsonReply *TunnelProxyHandler::DisconnectClient(const QVariantMap ¶ms, TransportClient *transportClient)
+{
+ qCDebug(dcJsonRpc()) << name() << "disconnect client requested" << params << transportClient;
+ quint16 socketAddress = static_cast(params.value("socketAddress").toUInt());
+ TunnelProxyServer::TunnelProxyError error = Engine::instance()->tunnelProxyServer()->disconnectClient(transportClient->clientId(), socketAddress);
+
+ QVariantMap response;
+ response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
+ return createReply("DisconnectClient", response);
+}
+
JsonReply *TunnelProxyHandler::RegisterClient(const QVariantMap ¶ms, TransportClient *transportClient)
{
qCDebug(dcJsonRpc()) << name() << "register client" << params << transportClient;
@@ -122,9 +145,10 @@ JsonReply *TunnelProxyHandler::RegisterClient(const QVariantMap ¶ms, Transpo
QVariantMap response;
response.insert("tunnelProxyError", JsonTypes::tunnelProxyErrorToString(error));
- return createReply("RegisterServer", response);
+ return createReply("RegisterClient", response);
}
+
//JsonReply *TunnelProxyHandler::RemoveClient(const QVariantMap ¶ms, TransportClient *transportClient)
//{
diff --git a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h
index 0265e21..4fd1e62 100644
--- a/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h
+++ b/libnymea-remoteproxy/jsonrpc/tunnelproxyhandler.h
@@ -46,9 +46,9 @@ public:
QString name() const override;
Q_INVOKABLE JsonReply *RegisterServer(const QVariantMap ¶ms, TransportClient *transportClient);
+ Q_INVOKABLE JsonReply *DisconnectClient(const QVariantMap ¶ms, TransportClient *transportClient);
Q_INVOKABLE JsonReply *RegisterClient(const QVariantMap ¶ms, TransportClient *transportClient);
-// Q_INVOKABLE JsonReply *RemoveClient(const QVariantMap ¶ms, TransportClient *transportClient);
signals:
void ClientConnected(const QVariantMap ¶ms, TransportClient *transportClient);
diff --git a/libnymea-remoteproxy/libnymea-remoteproxy.pro b/libnymea-remoteproxy/libnymea-remoteproxy.pro
index 7ebe9f4..35d7905 100644
--- a/libnymea-remoteproxy/libnymea-remoteproxy.pro
+++ b/libnymea-remoteproxy/libnymea-remoteproxy.pro
@@ -1,4 +1,5 @@
include(../nymea-remoteproxy.pri)
+include(../common/common.pri)
TEMPLATE = lib
TARGET = nymea-remoteproxy
diff --git a/libnymea-remoteproxy/proxy/proxyclient.cpp b/libnymea-remoteproxy/proxy/proxyclient.cpp
index 7f170a3..1189dd6 100644
--- a/libnymea-remoteproxy/proxy/proxyclient.cpp
+++ b/libnymea-remoteproxy/proxy/proxyclient.cpp
@@ -130,16 +130,16 @@ QList ProxyClient::processData(const QByteArray &data)
QList packages;
// Handle packet fragmentation
- m_dataBuffers.append(data);
- int splitIndex = m_dataBuffers.indexOf("}\n{");
+ m_dataBuffer.append(data);
+ int splitIndex = m_dataBuffer.indexOf("}\n{");
while (splitIndex > -1) {
- packages.append(m_dataBuffers.left(splitIndex + 1));
- m_dataBuffers = m_dataBuffers.right(m_dataBuffers.length() - splitIndex - 2);
- splitIndex = m_dataBuffers.indexOf("}\n{");
+ packages.append(m_dataBuffer.left(splitIndex + 1));
+ m_dataBuffer = m_dataBuffer.right(m_dataBuffer.length() - splitIndex - 2);
+ splitIndex = m_dataBuffer.indexOf("}\n{");
}
- if (m_dataBuffers.trimmed().endsWith("}")) {
- packages.append(m_dataBuffers);
- m_dataBuffers.clear();
+ if (m_dataBuffer.trimmed().endsWith("}")) {
+ packages.append(m_dataBuffer);
+ m_dataBuffer.clear();
}
return packages;
diff --git a/libnymea-remoteproxy/server/jsonrpcserver.cpp b/libnymea-remoteproxy/server/jsonrpcserver.cpp
index c1b6c5e..8f92ee3 100644
--- a/libnymea-remoteproxy/server/jsonrpcserver.cpp
+++ b/libnymea-remoteproxy/server/jsonrpcserver.cpp
@@ -29,6 +29,8 @@
#include "jsonrpcserver.h"
#include "loggingcategories.h"
#include "jsonrpc/jsontypes.h"
+#include "transportclient.h"
+#include "slipdataprocessor.h"
#include
#include
@@ -135,7 +137,14 @@ void JsonRpcServer::sendResponse(TransportClient *client, int commandId, const Q
QByteArray data = QJsonDocument::fromVariant(response).toJson(QJsonDocument::Compact);
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
- client->sendData(data);
+ if (client->slipEnabled()) {
+ SlipDataProcessor::Frame frame;
+ frame.socketAddress = 0x0000;
+ frame.data = data;
+ client->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
+ } else {
+ client->sendData(data);
+ }
}
void JsonRpcServer::sendErrorResponse(TransportClient *client, int commandId, const QString &error)
@@ -147,7 +156,14 @@ void JsonRpcServer::sendErrorResponse(TransportClient *client, int commandId, co
QByteArray data = QJsonDocument::fromVariant(errorResponse).toJson(QJsonDocument::Compact);
qCDebug(dcJsonRpcTraffic()) << "Sending data:" << data;
- client->sendData(data);
+ if (client->slipEnabled()) {
+ SlipDataProcessor::Frame frame;
+ frame.socketAddress = 0x0000;
+ frame.data = data;
+ client->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
+ } else {
+ client->sendData(data);
+ }
}
QString JsonRpcServer::formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const
@@ -239,18 +255,21 @@ void JsonRpcServer::processDataPackage(TransportClient *transportClient, const Q
Q_ASSERT_X((targetNamespace == "RemoteProxy" && method == "Introspect") || handler->validateReturns(method, reply->data()).first
,"validating return value", formatAssertion(targetNamespace, method, handler, reply->data()).toLatin1().data());
-// QPair returnValidation = reply->handler()->validateReturns(reply->method(), reply->data());
-// if (!returnValidation.first) {
-// qCWarning(dcJsonRpc()) << "Return value validation failed of sync reply. This should never happen. Please check the source code.";
-// }
-
reply->setClientId(transportClient->clientId());
reply->setCommandId(commandId);
sendResponse(transportClient, commandId, reply->data());
-
- // TODO: check if the client should be disconnected after this response
-
reply->deleteLater();
+
+ // If the server decided to kill the connection after the response, do it now
+ if (transportClient->killConnectionRequested()) {
+ transportClient->killConnection(transportClient->killConnectionReason());
+ return;
+ }
+
+ // Enable SLIP from now on since requested
+ if (transportClient->slipAfterResponseEnabled()) {
+ transportClient->setSlipEnabled(true);
+ }
}
}
@@ -325,7 +344,7 @@ void JsonRpcServer::processData(TransportClient *transportClient, const QByteArr
if (!m_clients.contains(transportClient))
return;
- qCDebug(dcJsonRpcTraffic()) << "Incoming data from" << transportClient << ": " << data;
+ qCDebug(dcJsonRpcTraffic()) << "Incoming data from" << transportClient << ": " << qUtf8Printable(data);
// Handle package fragmentation
QList packages = transportClient->processData(data);
@@ -350,8 +369,16 @@ void JsonRpcServer::sendNotification(const QString &nameSpace, const QString &me
notification.insert("params", params);
QByteArray data = QJsonDocument::fromVariant(notification).toJson(QJsonDocument::Compact);
- qCDebug(dcJsonRpcTraffic()) << "Sending notification:" << data;
- transportClient->sendData(data);
+ if (transportClient->slipEnabled()) {
+ SlipDataProcessor::Frame frame;
+ frame.socketAddress = 0x0000;
+ frame.data = data;
+ qCDebug(dcJsonRpcTraffic()) << "Sending notification frame:" <sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
+ } else {
+ qCDebug(dcJsonRpcTraffic()) << "Sending notification:" << data;
+ transportClient->sendData(data);
+ }
}
}
diff --git a/libnymea-remoteproxy/server/jsonrpcserver.h b/libnymea-remoteproxy/server/jsonrpcserver.h
index ca84c1b..c294084 100644
--- a/libnymea-remoteproxy/server/jsonrpcserver.h
+++ b/libnymea-remoteproxy/server/jsonrpcserver.h
@@ -69,12 +69,13 @@ private:
QString formatAssertion(const QString &targetNamespace, const QString &method, JsonHandler *handler, const QVariantMap &data) const;
- void processDataPackage(TransportClient *transportClient, const QByteArray &data);
private slots:
void asyncReplyFinished();
public slots:
+ void processDataPackage(TransportClient *transportClient, const QByteArray &data);
+
// Client registration for JSON RPC traffic
void registerClient(TransportClient *transportClient);
void unregisterClient(TransportClient *transportClient);
diff --git a/libnymea-remoteproxy/server/tcpsocketserver.cpp b/libnymea-remoteproxy/server/tcpsocketserver.cpp
index aef4c95..762c069 100644
--- a/libnymea-remoteproxy/server/tcpsocketserver.cpp
+++ b/libnymea-remoteproxy/server/tcpsocketserver.cpp
@@ -65,7 +65,6 @@ void TcpSocketServer::killClientConnection(const QUuid &clientId, const QString
return;
qCWarning(dcTcpSocketServer()) << "Killing client connection" << clientId.toString() << "Reason:" << killReason;
- client->flush();
client->close();
}
diff --git a/libnymea-remoteproxy/server/transportclient.cpp b/libnymea-remoteproxy/server/transportclient.cpp
index 0e108ac..58ca3b3 100644
--- a/libnymea-remoteproxy/server/transportclient.cpp
+++ b/libnymea-remoteproxy/server/transportclient.cpp
@@ -61,6 +61,42 @@ QString TransportClient::creationTimeString() const
return QDateTime::fromMSecsSinceEpoch(creationTime() * 1000).toString("dd.MM.yyyy hh:mm:ss");
}
+void TransportClient::killConnectionAfterResponse(const QString &killConnectionReason)
+{
+ m_killConnectionRequested = true;
+ m_killConnectionReason = killConnectionReason;
+}
+
+bool TransportClient::killConnectionRequested() const
+{
+ return m_killConnectionRequested;
+}
+
+QString TransportClient::killConnectionReason() const
+{
+ return m_killConnectionReason;
+}
+
+void TransportClient::enableSlipAfterResponse()
+{
+ m_slipAfterResponseEnabled = true;
+}
+
+bool TransportClient::slipAfterResponseEnabled() const
+{
+ return m_slipAfterResponseEnabled;
+}
+
+bool TransportClient::slipEnabled() const
+{
+ return m_slipEnabled;
+}
+
+void TransportClient::setSlipEnabled(bool slipEnabled)
+{
+ m_slipEnabled = slipEnabled;
+}
+
TransportInterface *TransportClient::interface() const
{
return m_interface;
@@ -108,7 +144,7 @@ void TransportClient::addTxDataCount(int dataCount)
int TransportClient::bufferSize() const
{
- return m_dataBuffers.size();
+ return m_dataBuffer.size();
}
int TransportClient::generateMessageId()
diff --git a/libnymea-remoteproxy/server/transportclient.h b/libnymea-remoteproxy/server/transportclient.h
index a43e746..caddee0 100644
--- a/libnymea-remoteproxy/server/transportclient.h
+++ b/libnymea-remoteproxy/server/transportclient.h
@@ -50,6 +50,18 @@ public:
uint creationTime() const;
QString creationTimeString() const;
+ // Schedule a disconnect after the response
+ void killConnectionAfterResponse(const QString &killConnectionReason);
+ bool killConnectionRequested() const;
+ QString killConnectionReason() const;
+
+ // Schedule SLIP enable after response
+ void enableSlipAfterResponse();
+ bool slipAfterResponseEnabled() const;
+
+ bool slipEnabled() const;
+ void setSlipEnabled(bool slipEnabled);
+
TransportInterface *interface() const;
// Properties from auth request
@@ -85,7 +97,13 @@ protected:
QString m_name;
QUuid m_uuid;
- QByteArray m_dataBuffers;
+ QByteArray m_dataBuffer;
+
+ bool m_killConnectionRequested = false;
+ QString m_killConnectionReason;
+
+ bool m_slipAfterResponseEnabled = false;
+ bool m_slipEnabled = false;
// Json data information
int m_messageId = 0;
diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.cpp
index ce46ba4..b6f7519 100644
--- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.cpp
+++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.cpp
@@ -1,5 +1,7 @@
#include "tunnelproxyclient.h"
+#include "loggingcategories.h"
#include "server/transportinterface.h"
+#include "slipdataprocessor.h"
namespace remoteproxy {
@@ -23,35 +25,44 @@ void TunnelProxyClient::setType(Type type)
emit typeChanged(m_type);
}
-bool TunnelProxyClient::slipEnabled() const
-{
- return m_slipEnabled;
-}
-
-void TunnelProxyClient::setSlipEnabled(bool slipEnabled)
-{
- m_slipEnabled = slipEnabled;
-}
-
QList TunnelProxyClient::processData(const QByteArray &data)
{
// TODO: unescape if this data is for the json handler
QList packages;
+ // Parse packages depending on the encoded
if (m_slipEnabled) {
+ // Read each byte until we get END byte, then unescape the package
+ for (int i = 0; i < data.length(); i++) {
+ quint8 byte = static_cast(data.at(i));
+ if (byte == SlipDataProcessor::ProtocolByteEnd) {
+ // If there is no data...continue since it might be a starting END byte
+ if (m_dataBuffer.isEmpty())
+ continue;
+ QByteArray frame = SlipDataProcessor::deserializeData(m_dataBuffer);
+ if (frame.isNull()) {
+ qCWarning(dcTunnelProxyServerTraffic()) << "Received inconsistant SLIP encoded message. Ignoring data...";
+ } else {
+ qCDebug(dcTunnelProxyServerTraffic()) << "Frame received";
+ packages.append(m_dataBuffer);
+ }
+ } else {
+ m_dataBuffer.append(data.at(i));
+ }
+ }
} else {
// Handle json packet fragmentation
- m_dataBuffers.append(data);
- int splitIndex = m_dataBuffers.indexOf("}\n{");
+ m_dataBuffer.append(data);
+ int splitIndex = m_dataBuffer.indexOf("}\n{");
while (splitIndex > -1) {
- packages.append(m_dataBuffers.left(splitIndex + 1));
- m_dataBuffers = m_dataBuffers.right(m_dataBuffers.length() - splitIndex - 2);
- splitIndex = m_dataBuffers.indexOf("}\n{");
+ packages.append(m_dataBuffer.left(splitIndex + 1));
+ m_dataBuffer = m_dataBuffer.right(m_dataBuffer.length() - splitIndex - 2);
+ splitIndex = m_dataBuffer.indexOf("}\n{");
}
- if (m_dataBuffers.trimmed().endsWith("}")) {
- packages.append(m_dataBuffers);
- m_dataBuffers.clear();
+ if (m_dataBuffer.trimmed().endsWith("}")) {
+ packages.append(m_dataBuffer);
+ m_dataBuffer.clear();
}
}
diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.h
index ede50de..832be0a 100644
--- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.h
+++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclient.h
@@ -23,9 +23,6 @@ public:
Type type() const;
void setType(Type type);
- bool slipEnabled() const;
- void setSlipEnabled(bool slipEnabled);
-
// Json server methods
QList processData(const QByteArray &data) override;
@@ -34,7 +31,6 @@ signals:
private:
Type m_type = TypeNone;
- bool m_slipEnabled = false;
};
diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.cpp
index 3279be1..667f265 100644
--- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.cpp
+++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.cpp
@@ -66,6 +66,16 @@ QUuid TunnelProxyClientConnection::serverUuid() const
return m_serverUuid;
}
+quint16 TunnelProxyClientConnection::socketAddress() const
+{
+ return m_socketAddress;
+}
+
+void TunnelProxyClientConnection::setSocketAddress(quint16 socketAddress)
+{
+ m_socketAddress = socketAddress;
+}
+
QDebug operator<<(QDebug debug, TunnelProxyClientConnection *clientConnection)
{
debug.nospace() << "TunnelProxyClientConnection(";
diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.h
index b52b121..fbbb986 100644
--- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.h
+++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyclientconnection.h
@@ -51,6 +51,9 @@ public:
QString clientName() const;
QUuid serverUuid() const;
+ quint16 socketAddress() const;
+ void setSocketAddress(quint16 socketAddress);
+
private:
TransportClient *m_transportClient = nullptr;
TunnelProxyServerConnection *m_serverConnection = nullptr;
@@ -58,7 +61,7 @@ private:
QUuid m_clientUuid;
QString m_clientName;
QUuid m_serverUuid;
-
+ quint16 m_socketAddress = 0xFFFF;
};
QDebug operator<<(QDebug debug, TunnelProxyClientConnection *clientConnection);
diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp
index 0924ae9..e56f2af 100644
--- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp
+++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.cpp
@@ -32,6 +32,8 @@
#include "tunnelproxyserverconnection.h"
#include "tunnelproxyclientconnection.h"
+#include "../common/slipdataprocessor.h"
+
namespace remoteproxy {
TunnelProxyServer::TunnelProxyServer(QObject *parent) :
@@ -86,12 +88,14 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerServer(const QUui
TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
if (!tunnelProxyClient) {
qCWarning(dcTunnelProxyServer()) << "There is no client with client uuid" << clientId.toString();
+ tunnelProxyClient->killConnectionAfterResponse("Internal server error");
return TunnelProxyServer::TunnelProxyErrorInternalServerError;
}
// Make sure this client has not been registered as client or re-registration has been called...
if (tunnelProxyClient->type() != TunnelProxyClient::TypeNone) {
qCWarning(dcTunnelProxyServer()) << "Client tried to register as server but has already been registerd as" << tunnelProxyClient->type();
+ tunnelProxyClient->killConnectionAfterResponse("Already registered");
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
}
@@ -100,7 +104,7 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerServer(const QUui
tunnelProxyClient->setName(serverName);
// Enable SLIP from now on
-// tunnelProxyClient->setSlipEnabled(true);
+ tunnelProxyClient->enableSlipAfterResponse();
TunnelProxyServerConnection *serverConnection = new TunnelProxyServerConnection(tunnelProxyClient, serverUuid, serverName, this);
m_tunnelProxyServerConnections.insert(serverUuid, serverConnection);
@@ -110,22 +114,23 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerServer(const QUui
TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerClient(const QUuid &clientId, const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid)
{
- qCDebug(dcTunnelProxyServer()) << "Register new client" << m_proxyClients.value(clientId) << clientName << clientUuid.toString() << "--> server" << serverUuid.toString();
-
TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
if (!tunnelProxyClient) {
qCWarning(dcTunnelProxyServer()) << "There is no client with client uuid" << clientId.toString();
+ tunnelProxyClient->killConnectionAfterResponse("Internal server error");
return TunnelProxyServer::TunnelProxyErrorInternalServerError;
}
// Make sure this client has not been registered as client or re-registration has been called...
if (tunnelProxyClient->type() != TunnelProxyClient::TypeNone) {
qCWarning(dcTunnelProxyServer()) << "Client tried to register as client but has already been registerd as" << tunnelProxyClient->type();
+ tunnelProxyClient->killConnectionAfterResponse("Already registered");
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
}
if (m_tunnelProxyClientConnections.contains(clientUuid)) {
qCWarning(dcTunnelProxyServer()) << "There is a client already registered with client uuid" << clientUuid.toString();
+ tunnelProxyClient->killConnectionAfterResponse("Already registered");
return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
}
@@ -133,6 +138,7 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerClient(const QUui
TunnelProxyServerConnection *serverConnection = m_tunnelProxyServerConnections.value(serverUuid);
if (!serverConnection) {
qCWarning(dcTunnelProxyServer()) << "There is no server registered with server uuid" << serverUuid.toString();
+ tunnelProxyClient->killConnectionAfterResponse("Unknown server");
return TunnelProxyServer::TunnelProxyErrorServerNotFound;
}
@@ -147,8 +153,47 @@ TunnelProxyServer::TunnelProxyError TunnelProxyServer::registerClient(const QUui
qCDebug(dcTunnelProxyServer()) << "Register client" << clientConnection << "-->" << serverConnection;
serverConnection->registerClientConnection(clientConnection);
- // TODO: wait for te aprovement from the server
+ // Tell the server a new client want's to connect
+ QVariantMap params;
+ params.insert("clientName", tunnelProxyClient->name());
+ params.insert("clientUuid", tunnelProxyClient->uuid().toString());
+ params.insert("clientPeerAddress", tunnelProxyClient->peerAddress().toString());
+ params.insert("socketAddress", clientConnection->socketAddress());
+ m_jsonRpcServer->sendNotification("TunnelProxy", "ClientConnected", params, serverConnection->transportClient());
+ // Note: check if a confirmation from the server would be needed, for rejection or limit or something. For now they are directly connected.
+
+ return TunnelProxyServer::TunnelProxyErrorNoError;
+}
+
+TunnelProxyServer::TunnelProxyError TunnelProxyServer::disconnectClient(const QUuid &clientId, quint16 socketAddress)
+{
+ TunnelProxyClient *tunnelProxyClient = m_proxyClients.value(clientId);
+ if (!tunnelProxyClient) {
+ qCWarning(dcTunnelProxyServer()) << "There is no client with client uuid" << clientId.toString();
+ return TunnelProxyServer::TunnelProxyErrorInternalServerError;
+ }
+
+ if (tunnelProxyClient->type() != TunnelProxyClient::TypeServer) {
+ qCWarning(dcTunnelProxyServer()) << "Client tried to disconnect a client but has not been registerd as server" << tunnelProxyClient->type();
+ tunnelProxyClient->killConnectionAfterResponse("Already registered");
+ return TunnelProxyServer::TunnelProxyErrorAlreadyRegistered;
+ }
+
+ TunnelProxyServerConnection *serverConnection = m_tunnelProxyServerConnections.value(tunnelProxyClient->uuid());
+ if (!serverConnection) {
+ qCWarning(dcTunnelProxyServer()) << "Could not find server connection for" << tunnelProxyClient;
+ tunnelProxyClient->killConnectionAfterResponse("Internal server error");
+ return TunnelProxyServer::TunnelProxyErrorInternalServerError;
+ }
+
+ TunnelProxyClientConnection *clientConnection = serverConnection->getClientConnection(socketAddress);
+ if (!clientConnection) {
+ qCWarning(dcTunnelProxyServer()) << "Could not find client connection for socket address" << socketAddress << "on" << serverConnection;
+ return TunnelProxyServer::TunnelProxyErrorUnknownSocketAddress;
+ }
+
+ clientConnection->transportClient()->killConnection("Server requested disconnect.");
return TunnelProxyServer::TunnelProxyErrorNoError;
}
@@ -202,8 +247,11 @@ void TunnelProxyServer::onClientDisconnected(const QUuid &clientId)
qCWarning(dcTunnelProxyServer()) << "Could not find server connection for disconnected tunnel proxy client claiming to be a server.";
} else {
-
- // TODO: kill all related clients
+ foreach (TunnelProxyClientConnection *clientConnection, serverConnection->clientConnections()) {
+ serverConnection->unregisterClientConnection(clientConnection);
+ clientConnection->setSocketAddress(0xFFFF);
+ clientConnection->transportClient()->killConnection("Server disconnected");
+ }
serverConnection->deleteLater();
}
@@ -215,8 +263,10 @@ void TunnelProxyServer::onClientDisconnected(const QUuid &clientId)
qCWarning(dcTunnelProxyServer()) << "Could not find client connection for disconnected tunnel proxy client claiming to be a client.";
} else {
if (clientConnection->serverConnection()) {
+ QVariantMap params;
+ params.insert("socketAddress", clientConnection->socketAddress());
+ m_jsonRpcServer->sendNotification("TunnelProxy", "ClientDisconnected", params, clientConnection->serverConnection()->transportClient());
clientConnection->serverConnection()->unregisterClientConnection(clientConnection);
- // TODO: Send client disconnected to the server
}
clientConnection->deleteLater();
@@ -239,23 +289,63 @@ void TunnelProxyServer::onClientDataAvailable(const QUuid &clientId, const QByte
}
qCDebug(dcTunnelProxyServerTraffic()) << "Client data available" << tunnelProxyClient << qUtf8Printable(data);
+
if (tunnelProxyClient->type() == TunnelProxyClient::TypeClient) {
- // Send the data to the server using slip + frame
+ // Send the data to the server using slip encoded frame
+ TunnelProxyClientConnection *clientConnection = m_tunnelProxyClientConnections.value(tunnelProxyClient->uuid());
+ if (!clientConnection) {
+ qCWarning(dcTunnelProxyServer()) << "Could not find a client connection for client uuid" << tunnelProxyClient->uuid().toString();
+ // FIXME: check what do with this client
+ return;
+ }
+ Q_ASSERT_X(clientConnection->serverConnection(), "TunnelProxyServer", "The client has not been registered to a server connection");
+ SlipDataProcessor::Frame frame;
+ frame.socketAddress = clientConnection->socketAddress();
+ frame.data = data;
+ qCDebug(dcTunnelProxyServerTraffic()) << "Write client data to server using socket address" << clientConnection->socketAddress();
+ clientConnection->serverConnection()->transportClient()->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
} else if (tunnelProxyClient->type() == TunnelProxyClient::TypeServer) {
+ // Data coming from a connected server connection
if (tunnelProxyClient->slipEnabled()) {
+ // Unpack SLIP data, get address, pipe to client or give it to the json rpc server if address 0x0000
+ // Handle package fragmentation
+ QList frames = tunnelProxyClient->processData(data);
+ foreach (const QByteArray &frameData, frames) {
+ SlipDataProcessor::Frame frame = SlipDataProcessor::parseFrame(frameData);
+ if (frame.socketAddress == 0x0000) {
+ qCDebug(dcTunnelProxyServerTraffic()) << "Received frame for the JSON server" << tunnelProxyClient;
+ m_jsonRpcServer->processDataPackage(tunnelProxyClient, frame.data);
+ } else {
+ // This data seems to be for a client with the given address
+ TunnelProxyServerConnection *serverConnection = m_tunnelProxyServerConnections.value(tunnelProxyClient->uuid());
+ if (!serverConnection) {
+ qCWarning(dcTunnelProxyServer()) << "Could not find server connection for" << tunnelProxyClient;
+ continue;
+ }
+
+ // Get the client and send the data to the client
+ TunnelProxyClientConnection *clientConnection = serverConnection->getClientConnection(frame.socketAddress);
+ if (!clientConnection) {
+ qCWarning(dcTunnelProxyServer()) << "The server connection wants to send data to a client connection which has not been registered to the server.";
+ // FIXME: tell the server this client does not exist
+ return;
+ }
+
+ qCDebug(dcTunnelProxyServerTraffic()) << "Sending data to" << clientConnection << qUtf8Printable(data);
+ clientConnection->transportClient()->sendData(frame.data);
+ }
+ }
} else {
m_jsonRpcServer->processData(tunnelProxyClient, data);
}
- // Unpack SLIP data, get address, pipe to client or give it to the json rpc server if address 0x0000
} else {
- // Not registered yet or doing other stuff...let the JSON RPC handle this data
+ // Not registered yet or doing other stuff...let the JSON RPC server handle this data
m_jsonRpcServer->processData(tunnelProxyClient, data);
}
-
}
}
diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h
index 585d229..937b370 100644
--- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h
+++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserver.h
@@ -48,7 +48,10 @@ public:
TunnelProxyErrorInvalidUuid,
TunnelProxyErrorInternalServerError,
TunnelProxyErrorServerNotFound,
- TunnelProxyErrorAlreadyRegistered
+ TunnelProxyErrorForbiddenCall,
+ TunnelProxyErrorAlreadyRegistered,
+ TunnelProxyErrorNotRegistered,
+ TunnelProxyErrorUnknownSocketAddress
};
Q_ENUM(TunnelProxyError)
@@ -62,6 +65,7 @@ public:
TunnelProxyServer::TunnelProxyError registerServer(const QUuid &clientId, const QUuid &serverUuid, const QString &serverName);
TunnelProxyServer::TunnelProxyError registerClient(const QUuid &clientId, const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid);
+ TunnelProxyServer::TunnelProxyError disconnectClient(const QUuid &clientId, quint16 socketAddress);
public slots:
void startServer();
diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.cpp b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.cpp
index 4451977..83b4dd8 100644
--- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.cpp
+++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.cpp
@@ -55,14 +55,44 @@ QString TunnelProxyServerConnection::serverName() const
return m_serverName;
}
+QList TunnelProxyServerConnection::clientConnections() const
+{
+ return m_clientConnections.values();
+}
+
void TunnelProxyServerConnection::registerClientConnection(TunnelProxyClientConnection *clientConnection)
{
m_clientConnections.insert(clientConnection->clientUuid(), clientConnection);
+ quint16 socketAddress = getFreeAddress();
+ clientConnection->setSocketAddress(socketAddress);
+ m_clientConnectionsAddresses.insert(socketAddress, clientConnection);
}
void TunnelProxyServerConnection::unregisterClientConnection(TunnelProxyClientConnection *clientConnection)
{
m_clientConnections.remove(clientConnection->clientUuid());
+ m_clientConnectionsAddresses.remove(clientConnection->socketAddress());
+}
+
+TunnelProxyClientConnection *TunnelProxyServerConnection::getClientConnection(quint16 socketAddress)
+{
+ return m_clientConnectionsAddresses.value(socketAddress);
+}
+
+quint16 TunnelProxyServerConnection::getFreeAddress()
+{
+ m_currentAddressCounter += 1;
+ quint16 address = m_currentAddressCounter;
+ for (int i = 0; i < m_connectionLimit; i++) {
+ if (m_clientConnectionsAddresses.contains(address) || address == 0x0000 || address == 0xFFFF) {
+ address++;
+ } else {
+ break;
+ }
+ }
+
+ Q_ASSERT_X(!m_clientConnectionsAddresses.contains(address), "TunnelProxyServerConnection", "no free address but the maximum of connections has been reached.");
+ return address;
}
QDebug operator<<(QDebug debug, TunnelProxyServerConnection *serverConnection)
diff --git a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.h b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.h
index 3fdaf71..471eb5a 100644
--- a/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.h
+++ b/libnymea-remoteproxy/tunnelproxy/tunnelproxyserverconnection.h
@@ -48,17 +48,27 @@ public:
QUuid serverUuid() const;
QString serverName() const;
+ QList clientConnections() const;
+
void registerClientConnection(TunnelProxyClientConnection *clientConnection);
void unregisterClientConnection(TunnelProxyClientConnection *clientConnection);
+ TunnelProxyClientConnection *getClientConnection(quint16 socketAddress);
+
signals:
private:
TransportClient *m_transportClient = nullptr;
QUuid m_serverUuid;
QString m_serverName;
+ quint16 m_connectionLimit = 100;
+
+ quint16 m_currentAddressCounter = 0;
QHash m_clientConnections;
+ QHash m_clientConnectionsAddresses;
+
+ quint16 getFreeAddress();
};
diff --git a/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro b/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro
index 0541c0f..3c500d6 100644
--- a/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro
+++ b/libnymea-remoteproxyclient/libnymea-remoteproxyclient.pro
@@ -4,6 +4,7 @@ TEMPLATE = lib
TARGET = nymea-remoteproxyclient
target.path = $$[QT_INSTALL_LIBS]
+include(../common/common.pri)
include(libnymea-remoteproxyclient.pri)
installheaders.files = remoteproxyconnection.h
diff --git a/libnymea-remoteproxyclient/proxyjsonrpcclient.cpp b/libnymea-remoteproxyclient/proxyjsonrpcclient.cpp
index c2e04db..be9f7da 100644
--- a/libnymea-remoteproxyclient/proxyjsonrpcclient.cpp
+++ b/libnymea-remoteproxyclient/proxyjsonrpcclient.cpp
@@ -79,6 +79,20 @@ JsonReply *JsonRpcClient::callRegisterServer(const QUuid &serverUuid, const QStr
return reply;
}
+JsonReply *JsonRpcClient::callRegisterClient(const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid)
+{
+ QVariantMap params;
+ params.insert("clientUuid", clientUuid);
+ params.insert("clientName", clientName);
+ params.insert("serverUuid", serverUuid.toString());
+
+ JsonReply *reply = new JsonReply(m_commandId, "TunnelProxy", "RegisterClient", params, this);
+ qCDebug(dcRemoteProxyClientJsonRpc()) << "Calling" << QString("%1.%2").arg(reply->nameSpace()).arg(reply->method());
+ sendRequest(reply->requestMap());
+ m_replies.insert(m_commandId, reply);
+ return reply;
+}
+
void JsonRpcClient::sendRequest(const QVariantMap &request)
{
QByteArray data = QJsonDocument::fromVariant(request).toJson(QJsonDocument::Compact);
@@ -97,6 +111,8 @@ void JsonRpcClient::processDataPackage(const QByteArray &data)
QVariantMap dataMap = jsonDoc.toVariant().toMap();
+ qCDebug(dcRemoteProxyClientJsonRpcTraffic()) << "Data received" << qUtf8Printable(data);
+
// check if this is a reply to a request
int commandId = dataMap.value("id").toInt();
JsonReply *reply = m_replies.take(commandId);
@@ -109,7 +125,7 @@ void JsonRpcClient::processDataPackage(const QByteArray &data)
}
reply->setResponse(dataMap);
- reply->finished();
+ emit reply->finished();
return;
}
@@ -126,6 +142,15 @@ void JsonRpcClient::processDataPackage(const QByteArray &data)
QString clientName = notificationParams.value("name").toString();
QString clientUuid = notificationParams.value("uuid").toString();
emit tunnelEstablished(clientName, clientUuid);
+ } else if (nameSpace == "TunnelProxy" && notificationName == "ClientConnected") {
+ QString clientName = notificationParams.value("clientName").toString();
+ QUuid clientUuid = notificationParams.value("clientUuid").toUuid();
+ QString clientPeerAddress = notificationParams.value("clientPeerAddress").toString();
+ quint16 socketAddress = static_cast(notificationParams.value("socketAddress").toUInt());
+ emit tunnelProxyClientConnected(clientName, clientUuid, clientPeerAddress, socketAddress);
+ } else if (nameSpace == "TunnelProxy" && notificationName == "ClientDisconnected") {
+ quint16 socketAddress = static_cast(notificationParams.value("socketAddress").toUInt());
+ emit tunnelProxyClientDisonnected(socketAddress);
}
}
}
diff --git a/libnymea-remoteproxyclient/proxyjsonrpcclient.h b/libnymea-remoteproxyclient/proxyjsonrpcclient.h
index b3cb0bf..fb44b36 100644
--- a/libnymea-remoteproxyclient/proxyjsonrpcclient.h
+++ b/libnymea-remoteproxyclient/proxyjsonrpcclient.h
@@ -55,6 +55,8 @@ public:
// Tunnel proxy
JsonReply *callRegisterServer(const QUuid &serverUuid, const QString &serverName);
+ JsonReply *callRegisterClient(const QUuid &clientUuid, const QString &clientName, const QUuid &serverUuid);
+ JsonReply *callDisconnectClient(quint16 socketAddress);
private:
ProxyConnection *m_connection = nullptr;
@@ -69,6 +71,8 @@ private:
signals:
void tunnelEstablished(const QString clientName, const QString &clientUuid);
+ void tunnelProxyClientConnected(const QString &clientName, const QUuid &clientUuid, const QString &clientPeerAddress, quint16 socketAddress);
+ void tunnelProxyClientDisonnected(quint16 socketAddress);
public slots:
void processData(const QByteArray &data);
diff --git a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxyremoteconnection.cpp b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxyremoteconnection.cpp
index 7a2e44b..cb0b0dd 100644
--- a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxyremoteconnection.cpp
+++ b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxyremoteconnection.cpp
@@ -1,6 +1,298 @@
-#include "tunnelproxyremoteconnection.h"
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-TunnelProxyRemoteConnection::TunnelProxyRemoteConnection(QObject *parent) : QObject(parent)
+#include "tunnelproxyremoteconnection.h"
+#include "proxyconnection.h"
+#include "tcpsocketconnection.h"
+#include "websocketconnection.h"
+#include "proxyjsonrpcclient.h"
+
+Q_LOGGING_CATEGORY(dcTunnelProxyRemoteConnection, "TunnelProxyRemoteConnection")
+
+namespace remoteproxyclient {
+
+TunnelProxyRemoteConnection::TunnelProxyRemoteConnection(const QUuid &clientUuid, const QString &clientName, QObject *parent) :
+ QObject(parent),
+ m_clientUuid(clientUuid),
+ m_clientName(clientName)
{
}
+
+TunnelProxyRemoteConnection::TunnelProxyRemoteConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType, QObject *parent) :
+ QObject(parent),
+ m_clientUuid(clientUuid),
+ m_clientName(clientName),
+ m_connectionType(connectionType)
+{
+
+}
+
+TunnelProxyRemoteConnection::~TunnelProxyRemoteConnection()
+{
+
+}
+
+bool TunnelProxyRemoteConnection::remoteConnected() const
+{
+ return m_remoteConnected;
+}
+
+QAbstractSocket::SocketError TunnelProxyRemoteConnection::error() const
+{
+ return m_error;
+}
+
+void TunnelProxyRemoteConnection::ignoreSslErrors()
+{
+ m_connection->ignoreSslErrors();
+}
+
+void TunnelProxyRemoteConnection::ignoreSslErrors(const QList &errors)
+{
+ m_connection->ignoreSslErrors(errors);
+}
+
+QString TunnelProxyRemoteConnection::remoteProxyServer() const
+{
+ return m_remoteProxyServer;
+}
+
+QString TunnelProxyRemoteConnection::remoteProxyServerName() const
+{
+ return m_remoteProxyServerName;
+}
+
+QString TunnelProxyRemoteConnection::remoteProxyServerVersion() const
+{
+ return m_remoteProxyServerVersion;
+}
+
+QString TunnelProxyRemoteConnection::remoteProxyApiVersion() const
+{
+ return m_remoteProxyApiVersion;
+}
+
+bool TunnelProxyRemoteConnection::connectServer(const QUrl &url, const QUuid &serverUuid)
+{
+ m_serverUrl = url;
+ m_serverUuid = serverUuid;
+ m_error = QAbstractSocket::UnknownSocketError;
+
+ cleanUp();
+
+ switch (m_connectionType) {
+ case ConnectionTypeWebSocket:
+ qCDebug(dcTunnelProxyRemoteConnection()) << "Creating a web socket connection to" << url.toString();
+ m_connection = qobject_cast(new WebSocketConnection(this));
+ break;
+ case ConnectionTypeTcpSocket:
+ qCDebug(dcTunnelProxyRemoteConnection()) << "Creating a TCP socket connection to" << url.toString();
+ m_connection = qobject_cast(new TcpSocketConnection(this));
+ break;
+ }
+
+ connect(m_connection, &ProxyConnection::connectedChanged, this, &TunnelProxyRemoteConnection::onConnectionChanged);
+ connect(m_connection, &ProxyConnection::dataReceived, this, &TunnelProxyRemoteConnection::onConnectionDataAvailable);
+ connect(m_connection, &ProxyConnection::errorOccured, this, &TunnelProxyRemoteConnection::onConnectionSocketError);
+ connect(m_connection, &ProxyConnection::stateChanged, this, &TunnelProxyRemoteConnection::onConnectionStateChanged);
+ connect(m_connection, &ProxyConnection::sslErrors, this, &TunnelProxyRemoteConnection::sslErrors);
+
+ m_jsonClient = new JsonRpcClient(m_connection, this);
+
+ qCDebug(dcTunnelProxyRemoteConnection()) << "Connecting to" << m_serverUrl.toString();
+ m_connection->connectServer(m_serverUrl);
+
+ return true;
+}
+
+void TunnelProxyRemoteConnection::disconnectServer()
+{
+ if (m_connection) {
+ qCDebug(dcTunnelProxyRemoteConnection()) << "Disconnecting from" << m_connection->serverUrl().toString();
+ m_connection->disconnectServer();
+ }
+}
+
+void TunnelProxyRemoteConnection::onConnectionChanged(bool connected)
+{
+ if (connected) {
+ qCDebug(dcTunnelProxyRemoteConnection()) << "Connected to remote proxy server.";
+ setState(StateConnected);
+ setState(StateInitializing);
+ JsonReply *reply = m_jsonClient->callHello();
+ connect(reply, &JsonReply::finished, this, &TunnelProxyRemoteConnection::onHelloFinished);
+ } else {
+ qCDebug(dcTunnelProxyRemoteConnection()) << "Disconnected from remote proxy server.";
+ setState(StateDisconnected);
+ cleanUp();
+ }
+}
+
+void TunnelProxyRemoteConnection::onConnectionDataAvailable(const QByteArray &data)
+{
+ if (m_state != StateRemoteConnected) {
+ m_jsonClient->processData(data);
+ return;
+ }
+
+ emit dataReady(data);
+}
+
+void TunnelProxyRemoteConnection::onConnectionSocketError(QAbstractSocket::SocketError error)
+{
+ setError(error);
+}
+
+void TunnelProxyRemoteConnection::onConnectionStateChanged(QAbstractSocket::SocketState state)
+{
+ // Process the actuall socket state
+ switch (state) {
+ case QAbstractSocket::UnconnectedState:
+ setState(StateDisconnected);
+ break;
+ case QAbstractSocket::HostLookupState:
+ setState(StateHostLookup);
+ break;
+ case QAbstractSocket::ConnectingState:
+ setState(StateConnecting);
+ break;
+ case QAbstractSocket::ConnectedState:
+ setState(StateConnected);
+ break;
+ case QAbstractSocket::ClosingState:
+ setState(StateDiconnecting);
+ break;
+ case QAbstractSocket::BoundState:
+ case QAbstractSocket::ListeningState:
+ break;
+ }
+}
+
+void TunnelProxyRemoteConnection::onHelloFinished()
+{
+ JsonReply *reply = static_cast(sender());
+ reply->deleteLater();
+
+ QVariantMap response = reply->response();
+ qCDebug(dcTunnelProxyRemoteConnection()) << "Hello response ready" << reply->commandId() << response;
+
+ if (response.value("status").toString() != "success") {
+ qCWarning(dcTunnelProxyRemoteConnection()) << "Could not get initial information from proxy server.";
+ m_connection->disconnectServer();
+ return;
+ }
+
+ QVariantMap responseParams = response.value("params").toMap();
+ m_remoteProxyServer = responseParams.value("server").toString();
+ m_remoteProxyServerName = responseParams.value("name").toString();
+ m_remoteProxyServerVersion = responseParams.value("version").toString();
+ m_remoteProxyApiVersion = responseParams.value("apiVersion").toString();
+
+ setState(StateRegister);
+
+ JsonReply *registerReply = m_jsonClient->callRegisterClient(m_clientUuid, m_clientName, m_serverUuid);
+ connect(registerReply, &JsonReply::finished, this, &TunnelProxyRemoteConnection::onClientRegistrationFinished);
+}
+
+void TunnelProxyRemoteConnection::onClientRegistrationFinished()
+{
+ JsonReply *reply = static_cast(sender());
+ reply->deleteLater();
+
+ QVariantMap response = reply->response();
+ qCDebug(dcTunnelProxyRemoteConnection()) << "RegisterClient response ready" << reply->commandId() << response;
+
+ if (response.value("status").toString() != "success") {
+ qCWarning(dcTunnelProxyRemoteConnection()) << "JSON RPC error. Failed to register as tunnel client on the remote proxy server.";
+ m_connection->disconnectServer();
+ return;
+ }
+
+ QVariantMap responseParams = response.value("params").toMap();
+ if (responseParams.value("tunnelProxyError").toString() != "TunnelProxyErrorNoError") {
+ qCWarning(dcTunnelProxyRemoteConnection()) << "Failed to register as tunnel client on the remote proxy server:" << responseParams.value("tunnelProxyError").toString();
+ m_connection->disconnectServer();
+ return;
+ }
+
+ qCDebug(dcTunnelProxyRemoteConnection()) << "Registered successfully as tunnel client on the remote proxy server.";
+ setState(StateRemoteConnected);
+}
+
+void TunnelProxyRemoteConnection::setState(State state)
+{
+ if (m_state == state)
+ return;
+
+ m_state = state;
+ qCDebug(dcTunnelProxyRemoteConnection()) << "State changed" << m_state;
+ emit stateChanged(m_state);
+
+ setRemoteConnected(m_state == StateRemoteConnected);
+}
+
+void TunnelProxyRemoteConnection::setRemoteConnected(bool remoteConnected)
+{
+ if (m_remoteConnected == remoteConnected)
+ return;
+
+ m_remoteConnected = remoteConnected;
+ emit remoteConnectedChanged(m_remoteConnected);
+}
+
+void TunnelProxyRemoteConnection::setError(QAbstractSocket::SocketError error)
+{
+ if (m_error == error)
+ return;
+
+ m_error = error;
+ qCWarning(dcTunnelProxyRemoteConnection()) << "Error occured" << m_error;
+ emit errorOccured(m_error);
+}
+
+void TunnelProxyRemoteConnection::cleanUp()
+{
+ if (m_jsonClient) {
+ m_jsonClient->deleteLater();
+ m_jsonClient = nullptr;
+ }
+
+ if (m_connection) {
+ m_connection->deleteLater();
+ m_connection = nullptr;
+ }
+
+ m_remoteProxyServer.clear();
+ m_remoteProxyServerName.clear();
+ m_remoteProxyServerVersion.clear();
+ m_remoteProxyApiVersion.clear();
+
+ setState(StateDisconnected);
+}
+
+}
diff --git a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxyremoteconnection.h b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxyremoteconnection.h
index c649e74..b4cd1c4 100644
--- a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxyremoteconnection.h
+++ b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxyremoteconnection.h
@@ -1,16 +1,139 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
+* Lesser General Public License as published by the Free Software Foundation; 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser 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 TUNNELPROXYREMOTECONNECTION_H
#define TUNNELPROXYREMOTECONNECTION_H
+#include
#include
+#include
+
+#include "proxyconnection.h"
+
+Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxyRemoteConnection)
+
+namespace remoteproxyclient {
+
+class JsonRpcClient;
class TunnelProxyRemoteConnection : public QObject
{
Q_OBJECT
public:
- explicit TunnelProxyRemoteConnection(QObject *parent = nullptr);
+
+ enum State {
+ StateConnecting,
+ StateHostLookup,
+ StateConnected,
+ StateInitializing,
+ StateRegister,
+ StateRemoteConnected,
+ StateDiconnecting,
+ StateDisconnected,
+ };
+ Q_ENUM(State)
+
+ enum ConnectionType {
+ ConnectionTypeWebSocket,
+ ConnectionTypeTcpSocket
+ };
+ Q_ENUM(ConnectionType)
+
+ explicit TunnelProxyRemoteConnection(const QUuid &clientUuid, const QString &clientName, QObject *parent = nullptr);
+ explicit TunnelProxyRemoteConnection(const QUuid &clientUuid, const QString &clientName, ConnectionType connectionType, QObject *parent = nullptr);
+ ~TunnelProxyRemoteConnection();
+
+ bool remoteConnected() const;
+
+ QAbstractSocket::SocketError error() const;
+
+ void ignoreSslErrors();
+ void ignoreSslErrors(const QList &errors);
+
+ QString remoteProxyServer() const;
+ QString remoteProxyServerName() const;
+ QString remoteProxyServerVersion() const;
+ QString remoteProxyApiVersion() const;
+
+public slots:
+ bool connectServer(const QUrl &url, const QUuid &serverUuid);
+ void disconnectServer();
signals:
+ void stateChanged(TunnelProxyRemoteConnection::State state);
+ void errorOccured(QAbstractSocket::SocketError error);
+ void sslErrors(const QList &errors);
+
+ void remoteConnectedChanged(bool remoteConnected);
+
+ void dataReady(const QByteArray &data);
+
+private slots:
+ void onConnectionChanged(bool connected);
+ void onConnectionDataAvailable(const QByteArray &data);
+
+ void onConnectionSocketError(QAbstractSocket::SocketError error);
+ void onConnectionStateChanged(QAbstractSocket::SocketState state);
+
+ // Initialization calls
+ void onHelloFinished();
+ void onClientRegistrationFinished();
+
+private:
+ // This server information
+ QUuid m_clientUuid;
+ QString m_clientName;
+ ConnectionType m_connectionType = ConnectionTypeTcpSocket;
+
+ QUuid m_serverUuid;
+
+ bool m_remoteConnected = false;
+
+ // Remote proxy server information
+ QString m_remoteProxyServer;
+ QString m_remoteProxyServerName;
+ QString m_remoteProxyServerVersion;
+ QString m_remoteProxyApiVersion;
+
+ QUrl m_serverUrl;
+ QAbstractSocket::SocketError m_error = QAbstractSocket::UnknownSocketError;
+ State m_state = StateDisconnected;
+
+ ProxyConnection *m_connection = nullptr;
+ JsonRpcClient *m_jsonClient = nullptr;
+
+ void setState(State state);
+ void setRemoteConnected(bool remoteConnected);
+ void setError(QAbstractSocket::SocketError error);
+
+ void cleanUp();
};
+}
+
#endif // TUNNELPROXYREMOTECONNECTION_H
diff --git a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocket.cpp b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocket.cpp
index b033688..169d059 100644
--- a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocket.cpp
+++ b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocket.cpp
@@ -26,12 +26,63 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "tunnelproxysocket.h"
+#include "proxyconnection.h"
+#include "../common/slipdataprocessor.h"
namespace remoteproxyclient {
-TunnelProxySocket::TunnelProxySocket(QObject *parent) : QObject(parent)
+TunnelProxySocket::TunnelProxySocket(ProxyConnection *connection, const QString &clientName, const QUuid &clientUuid, const QHostAddress &clientPeerAddress, quint16 socketAddress, QObject *parent) :
+ QObject(parent),
+ m_connection(connection),
+ m_clientName(clientName),
+ m_clientUuid(clientUuid),
+ m_clientPeerAddress(clientPeerAddress),
+ m_socketAddress(socketAddress)
{
}
+QUuid TunnelProxySocket::clientUuid() const
+{
+ return m_clientUuid;
+}
+
+QString TunnelProxySocket::clientName() const
+{
+ return m_clientName;
+}
+
+QHostAddress TunnelProxySocket::clientPeerAddress() const
+{
+ return m_clientPeerAddress;
+}
+
+quint16 TunnelProxySocket::socketAddress() const
+{
+ return m_socketAddress;
+}
+
+void TunnelProxySocket::writeData(const QByteArray &data)
+{
+ SlipDataProcessor::Frame frame;
+ frame.socketAddress = m_socketAddress;
+ frame.data = data;
+ m_connection->sendData(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
+}
+
+void TunnelProxySocket::disconnectSocket()
+{
+
+}
+
+QDebug operator<<(QDebug debug, TunnelProxySocket *tunnelProxySocket)
+{
+ debug.nospace() << "TunnelProxySocket(";
+ debug.nospace() << tunnelProxySocket->clientName() << ", ";
+ debug.nospace() << tunnelProxySocket->clientUuid().toString() << ", ";
+ debug.nospace() << tunnelProxySocket->clientPeerAddress().toString() << ", ";
+ debug.nospace() << tunnelProxySocket->socketAddress() << ")";
+ return debug.space();
+}
+
}
diff --git a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocket.h b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocket.h
index bd259f9..bb3b6a7 100644
--- a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocket.h
+++ b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocket.h
@@ -28,20 +28,48 @@
#ifndef TUNNELPROXYSOCKET_H
#define TUNNELPROXYSOCKET_H
+#include
#include
+#include
namespace remoteproxyclient {
+class ProxyConnection;
+
class TunnelProxySocket : public QObject
{
Q_OBJECT
+ friend class TunnelProxySocketServer;
+
public:
- explicit TunnelProxySocket(QObject *parent = nullptr);
+ QUuid clientUuid() const;
+ QString clientName() const;
+ QHostAddress clientPeerAddress() const;
+ quint16 socketAddress() const;
+
+ void writeData(const QByteArray &data);
+
+ void disconnectSocket();
signals:
+ void dataReceived(const QByteArray &data);
+ void disconnected();
+
+private:
+ explicit TunnelProxySocket(ProxyConnection *connection, const QString &clientName, const QUuid &clientUuid, const QHostAddress &clientPeerAddress, quint16 socketAddress, QObject *parent = nullptr);
+ ~TunnelProxySocket() = default;
+
+ ProxyConnection *m_connection = nullptr;
+
+ QString m_clientName;
+ QUuid m_clientUuid;
+ QHostAddress m_clientPeerAddress;
+ quint16 m_socketAddress = 0xFFFF;
};
+QDebug operator<<(QDebug debug, TunnelProxySocket *tunnelProxySocket);
+
}
#endif // TUNNELPROXYSOCKET_H
diff --git a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocketserver.cpp b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocketserver.cpp
index 67576c2..616eea6 100644
--- a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocketserver.cpp
+++ b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocketserver.cpp
@@ -30,8 +30,10 @@
#include "tcpsocketconnection.h"
#include "websocketconnection.h"
#include "proxyjsonrpcclient.h"
+#include "../common/slipdataprocessor.h"
Q_LOGGING_CATEGORY(dcTunnelProxySocketServer, "dcTunnelProxySocketServer")
+Q_LOGGING_CATEGORY(dcTunnelProxySocketServerTraffic, "dcTunnelProxySocketServerTraffic")
namespace remoteproxyclient {
@@ -67,10 +69,14 @@ QAbstractSocket::SocketError TunnelProxySocketServer::error() const
return m_error;
}
+TunnelProxySocketServer::Error TunnelProxySocketServer::serverError() const
+{
+ return m_serverError;
+}
+
void TunnelProxySocketServer::ignoreSslErrors()
{
m_connection->ignoreSslErrors();
-
}
void TunnelProxySocketServer::ignoreSslErrors(const QList &errors)
@@ -78,6 +84,32 @@ void TunnelProxySocketServer::ignoreSslErrors(const QList &errors)
m_connection->ignoreSslErrors(errors);
}
+QUrl TunnelProxySocketServer::serverUrl() const
+{
+ return m_serverUrl;
+}
+
+QString TunnelProxySocketServer::remoteProxyServer() const
+{
+ return m_remoteProxyServer;
+}
+
+QString TunnelProxySocketServer::remoteProxyServerName() const
+{
+ return m_remoteProxyServerName;
+}
+
+QString TunnelProxySocketServer::remoteProxyServerVersion() const
+{
+ return m_remoteProxyServerVersion;
+}
+
+QString TunnelProxySocketServer::remoteProxyApiVersion() const
+{
+ return m_remoteProxyApiVersion;
+}
+
+
void TunnelProxySocketServer::startServer(const QUrl &serverUrl)
{
m_serverUrl = serverUrl;
@@ -103,7 +135,8 @@ void TunnelProxySocketServer::startServer(const QUrl &serverUrl)
connect(m_connection, &ProxyConnection::sslErrors, this, &TunnelProxySocketServer::sslErrors);
m_jsonClient = new JsonRpcClient(m_connection, this);
-// connect(m_jsonClient, &JsonRpcClient::tunnelEstablished, this, &RemoteProxyConnection::onTunnelEstablished);
+ connect(m_jsonClient, &JsonRpcClient::tunnelProxyClientConnected, this, &TunnelProxySocketServer::onTunnelProxyClientConnected);
+ connect(m_jsonClient, &JsonRpcClient::tunnelProxyClientDisonnected, this, &TunnelProxySocketServer::onTunnelProxyClientDisconnected);
qCDebug(dcTunnelProxySocketServer()) << "Connecting to" << m_serverUrl.toString();
m_connection->connectServer(m_serverUrl);
@@ -111,7 +144,10 @@ void TunnelProxySocketServer::startServer(const QUrl &serverUrl)
void TunnelProxySocketServer::stopServer()
{
- // Close the server connection and all related sockets
+ if (m_connection) {
+ qCDebug(dcTunnelProxySocketServer()) << "Disconnecting from" << m_connection->serverUrl().toString();
+ m_connection->disconnectServer();
+ }
}
void TunnelProxySocketServer::onConnectionChanged(bool connected)
@@ -119,6 +155,7 @@ void TunnelProxySocketServer::onConnectionChanged(bool connected)
if (connected) {
qCDebug(dcTunnelProxySocketServer()) << "Connected to remote proxy server.";
setState(StateConnected);
+ m_serverError = TunnelProxySocketServer::ErrorNoError;
setState(StateInitializing);
JsonReply *reply = m_jsonClient->callHello();
@@ -126,18 +163,53 @@ void TunnelProxySocketServer::onConnectionChanged(bool connected)
} else {
qCDebug(dcTunnelProxySocketServer()) << "Disconnected from remote proxy server.";
setState(StateDisconnected);
+ setServerError(ErrorNotConnected);
cleanUp();
}
}
void TunnelProxySocketServer::onConnectionDataAvailable(const QByteArray &data)
{
+ qCDebug(dcTunnelProxySocketServerTraffic()) << "Data received" << qUtf8Printable(data);
+
if (m_state != StateRunning) {
m_jsonClient->processData(data);
+ m_dataBuffer.clear();
return;
}
- // Parse SLIP
+ // Parse SLIP frame
+ for (int i = 0; i < data.length(); i++) {
+ quint8 byte = static_cast(data.at(i));
+ if (byte == SlipDataProcessor::ProtocolByteEnd) {
+ // If there is no data...continue since it might be a starting END byte
+ if (m_dataBuffer.isEmpty())
+ continue;
+
+ QByteArray frameData = SlipDataProcessor::deserializeData(m_dataBuffer);
+ if (frameData.isNull()) {
+ qCWarning(dcTunnelProxySocketServerTraffic()) << "Received inconsistant SLIP encoded message. Ignoring data...";
+ } else {
+ SlipDataProcessor::Frame frame = SlipDataProcessor::parseFrame(frameData);
+ qCDebug(dcTunnelProxySocketServerTraffic()) << "Frame received" << frame.socketAddress << qUtf8Printable(frame.data);
+ if (frame.socketAddress == 0x0000) {
+ m_jsonClient->processData(frame.data);
+ } else {
+ // Find the socket and emit the data received signal
+ TunnelProxySocket *tunnlProxySocket = m_tunnelProxySockets.value(frame.socketAddress);
+ if (!tunnlProxySocket) {
+ qCWarning(dcTunnelProxySocketServer()) << "Received data from unknown tunnel proxy client with address" << frame.socketAddress << "...ignoring the data";
+ } else {
+ emit tunnlProxySocket->dataReceived(frame.data);
+ }
+ }
+ }
+
+ m_dataBuffer.clear();
+ } else {
+ m_dataBuffer.append(data.at(i));
+ }
+ }
}
void TunnelProxySocketServer::onConnectionSocketError(QAbstractSocket::SocketError error)
@@ -181,6 +253,7 @@ void TunnelProxySocketServer::onHelloFinished()
if (response.value("status").toString() != "success") {
qCWarning(dcTunnelProxySocketServer()) << "Could not get initial information from proxy server.";
m_connection->disconnectServer();
+ setServerError(ErrorConnectionError);
return;
}
@@ -207,6 +280,7 @@ void TunnelProxySocketServer::onServerRegistrationFinished()
if (response.value("status").toString() != "success") {
qCWarning(dcTunnelProxySocketServer()) << "JSON RPC error. Failed to register as tunnel server on the remote proxy server.";
m_connection->disconnectServer();
+ setServerError(ErrorConnectionError);
return;
}
@@ -214,11 +288,34 @@ void TunnelProxySocketServer::onServerRegistrationFinished()
if (responseParams.value("tunnelProxyError").toString() != "TunnelProxyErrorNoError") {
qCWarning(dcTunnelProxySocketServer()) << "Failed to register as tunnel server on the remote proxy server:" << responseParams.value("tunnelProxyError").toString();
m_connection->disconnectServer();
+ setServerError(ErrorConnectionError);
return;
}
qCDebug(dcTunnelProxySocketServer()) << "Registered successfully as tunnel server on the remote proxy server.";
setState(StateRunning);
+ m_serverError = ErrorNoError;
+}
+
+void TunnelProxySocketServer::onTunnelProxyClientConnected(const QString &clientName, const QUuid &clientUuid, const QString &clientPeerAddress, quint16 socketAddress)
+{
+ TunnelProxySocket *tunnelProxySocket = new TunnelProxySocket(m_connection, clientName, clientUuid, QHostAddress(clientPeerAddress), socketAddress, this);
+ qCDebug(dcTunnelProxySocketServer()) << "--> New client connected" << tunnelProxySocket;
+ m_tunnelProxySockets.insert(socketAddress, tunnelProxySocket);
+ emit clientConnected(tunnelProxySocket);
+}
+
+void TunnelProxySocketServer::onTunnelProxyClientDisconnected(quint16 socketAddress)
+{
+ TunnelProxySocket *tunnelProxySocket = m_tunnelProxySockets.take(socketAddress);
+ if (!tunnelProxySocket) {
+ qCWarning(dcTunnelProxySocketServer()) << "Unknown client disconnected" << socketAddress << ". Ignoring the event.";
+ return;
+ }
+
+ qCDebug(dcTunnelProxySocketServer()) << "--> Client disconnected" << tunnelProxySocket;
+ emit tunnelProxySocket->disconnected();
+ tunnelProxySocket->deleteLater();
}
void TunnelProxySocketServer::setState(State state)
@@ -253,10 +350,21 @@ void TunnelProxySocketServer::setError(QAbstractSocket::SocketError error)
emit errorOccured(m_error);
}
+void TunnelProxySocketServer::setServerError(Error error)
+{
+ if (m_serverError == error)
+ return;
+
+ qCDebug(dcTunnelProxySocketServer()) << "Server error occured" << error;
+ m_serverError = error;
+ emit serverErrorOccured(m_serverError);
+}
+
void TunnelProxySocketServer::cleanUp()
{
- // TODO: cleanup client connections
-
+ foreach (quint16 socketAddress, m_tunnelProxySockets.keys()) {
+ onTunnelProxyClientDisconnected(socketAddress);
+ }
if (m_jsonClient) {
m_jsonClient->deleteLater();
@@ -268,8 +376,10 @@ void TunnelProxySocketServer::cleanUp()
m_connection = nullptr;
}
-// m_serverName = QString();
-// m_serverUuid = QUuid();
+ m_remoteProxyServer.clear();
+ m_remoteProxyServerName.clear();
+ m_remoteProxyServerVersion.clear();
+ m_remoteProxyApiVersion.clear();
setState(StateDisconnected);
}
diff --git a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocketserver.h b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocketserver.h
index 15b4926..7a20bf2 100644
--- a/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocketserver.h
+++ b/libnymea-remoteproxyclient/tunnelproxy/tunnelproxysocketserver.h
@@ -32,14 +32,14 @@
#include
#include
-Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxySocketServer)
-
#include "proxyconnection.h"
#include "tunnelproxysocket.h"
+Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxySocketServer)
+Q_DECLARE_LOGGING_CATEGORY(dcTunnelProxySocketServerTraffic)
+
namespace remoteproxyclient {
-class ProxyConnection;
class JsonRpcClient;
class TunnelProxySocketServer : public QObject
@@ -64,6 +64,15 @@ public:
};
Q_ENUM(ConnectionType)
+ enum Error {
+ ErrorNoError,
+ ErrorConnectionError,
+ ErrorInvalidAddress,
+ ErrorNotConnected,
+ ErrorNotRegistered
+ };
+ Q_ENUM(Error)
+
explicit TunnelProxySocketServer(const QUuid &serverUuid, const QString &serverName, QObject *parent = nullptr);
explicit TunnelProxySocketServer(const QUuid &serverUuid, const QString &serverName, ConnectionType connectionType, QObject *parent = nullptr);
~TunnelProxySocketServer();
@@ -71,21 +80,32 @@ public:
bool running() const;
QAbstractSocket::SocketError error() const;
+ Error serverError() const;
void ignoreSslErrors();
void ignoreSslErrors(const QList &errors);
+ QUrl serverUrl() const;
+
+ QString remoteProxyServer() const;
+ QString remoteProxyServerName() const;
+ QString remoteProxyServerVersion() const;
+ QString remoteProxyApiVersion() const;
+
public slots:
void startServer(const QUrl &serverUrl);
void stopServer();
signals:
- void runningChanged(bool running);
-
void stateChanged(TunnelProxySocketServer::State state);
void errorOccured(QAbstractSocket::SocketError error);
+ void serverErrorOccured(Error error);
void sslErrors(const QList &errors);
+ void runningChanged(bool running);
+
+ void clientConnected(TunnelProxySocket *tunnelProxySocket);
+ void clientDisconnected(TunnelProxySocket *tunnelProxySocket);
private slots:
void onConnectionChanged(bool connected);
@@ -98,6 +118,10 @@ private slots:
void onHelloFinished();
void onServerRegistrationFinished();
+ // Client notifications
+ void onTunnelProxyClientConnected(const QString &clientName, const QUuid &clientUuid, const QString &clientPeerAddress, quint16 socketAddress);
+ void onTunnelProxyClientDisconnected(quint16 socketAddress);
+
private:
// This server information
QUuid m_serverUuid;
@@ -113,14 +137,20 @@ private:
bool m_running = false;
QUrl m_serverUrl;
QAbstractSocket::SocketError m_error = QAbstractSocket::UnknownSocketError;
+ Error m_serverError = ErrorNoError;
State m_state = StateDisconnected;
ProxyConnection *m_connection = nullptr;
JsonRpcClient *m_jsonClient = nullptr;
+ QHash m_tunnelProxySockets;
+
+ QByteArray m_dataBuffer;
+
void setState(State state);
void setRunning(bool running);
void setError(QAbstractSocket::SocketError error);
+ void setServerError(Error error);
void cleanUp();
};
diff --git a/nymea-remoteproxy.pro b/nymea-remoteproxy.pro
index 2e38692..bdd60cf 100644
--- a/nymea-remoteproxy.pro
+++ b/nymea-remoteproxy.pro
@@ -4,11 +4,11 @@ TEMPLATE=subdirs
SUBDIRS += server client libnymea-remoteproxy libnymea-remoteproxyclient
!disabletests {
- SUBDIRS+=tests
+ SUBDIRS += tests
}
!disablemonitor {
- SUBDIRS+=monitor
+ SUBDIRS += monitor
}
server.depends = libnymea-remoteproxy
@@ -26,8 +26,14 @@ message("----------------------------------------------------------")
message("JSON-RPC API version $${API_VERSION_MAJOR}.$${API_VERSION_MINOR}")
message("Qt version:" $$[QT_VERSION])
-coverage { message("Building with coverage report") }
-ccache { message("Building with ccache support") }
+coverage {
+ message("Building with coverage report")
+}
+
+ccache {
+ message("Building with ccache support")
+}
+
disablemonitor {
message("Building without the monitor")
}
diff --git a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp
index f8217bf..0ff10c6 100644
--- a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp
+++ b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.cpp
@@ -33,6 +33,7 @@
// Client
#include "tunnelproxy/tunnelproxysocketserver.h"
+#include "tunnelproxy/tunnelproxyremoteconnection.h"
#include
#include
@@ -296,6 +297,8 @@ void RemoteProxyTestsTunnelProxy::registerClient()
QVERIFY(!response.isEmpty());
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
+ QVERIFY(response.value("params").toMap().contains("slipEnabled"));
+ //QVERIFY(response.value("params").toMap().value("slipEnabled").toBool()));
verifyTunnelProxyError(response, expectedServerError);
}
@@ -358,27 +361,24 @@ void RemoteProxyTestsTunnelProxy::registerServerDuplicated()
QVERIFY(!response.isEmpty());
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
+ QVERIFY(response.value("params").toMap().contains("slipEnabled"));
+ QVERIFY(response.value("params").toMap().value("slipEnabled").toBool());
verifyTunnelProxyError(response);
+ // SLIP Should be enabled now
+
// Try to register again with the same uuid on the same socket
- result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, socket);
+ result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, true, socket);
response = result.first.toMap();
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
- // Try to register an invalid server uuid
- QVariantMap paramsInvalidUuid;
- paramsInvalidUuid.insert("serverName", serverName);
- paramsInvalidUuid.insert("serverUuid", QUuid().toString());
- result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", paramsInvalidUuid, socket);
- response = result.first.toMap();
- QVERIFY(response.value("status").toString() == "success");
- QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
- verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorInvalidUuid);
+ QSignalSpy disconnectedTcpSpy(socket, &QSslSocket::disconnected);
+ QVERIFY(disconnectedTcpSpy.wait());
+ QVERIFY(disconnectedTcpSpy.count() == 1);
// Close the tcp socket
- socket->close();
delete socket;
QTest::qWait(100);
@@ -394,18 +394,16 @@ void RemoteProxyTestsTunnelProxy::registerServerDuplicated()
verifyTunnelProxyError(response);
// Try to register again with the same uuid on the same socket
- resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, webSocket);
+ resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", params, true, webSocket);
response = resultWebSocket.first.toMap();
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
- // Try to register an invalid server uuid
- resultWebSocket = invokeWebSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", paramsInvalidUuid, webSocket);
- response = resultWebSocket.first.toMap();
- QVERIFY(response.value("status").toString() == "success");
- QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
- verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorInvalidUuid);
+
+ QSignalSpy disconnectedWebSocketSpy(webSocket, &QWebSocket::disconnected);
+ QVERIFY(disconnectedWebSocketSpy.wait());
+ QVERIFY(disconnectedWebSocketSpy.count() == 1);
webSocket->close();
delete webSocket;
@@ -470,7 +468,6 @@ void RemoteProxyTestsTunnelProxy::registerClientDuplicated()
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
-
// CleanUp
if (clientSocketTcp) {
// Close the tcp socket
@@ -501,8 +498,10 @@ void RemoteProxyTestsTunnelProxy::crossRegisterServerClient()
// Start the server
startServer();
+ resetDebugCategories();
resetDebugCategories();
addDebugCategory("TunnelProxyServer.debug=true");
+ addDebugCategory("TunnelProxyServerTraffic.debug=true");
addDebugCategory("JsonRpcTraffic.debug=true");
// Create the server and keep it up
@@ -512,7 +511,7 @@ void RemoteProxyTestsTunnelProxy::crossRegisterServerClient()
serverParams.insert("serverName", serverName);
serverParams.insert("serverUuid", serverUuid.toString());
- // TCP socket
+ // TCP server
QPair serverResult = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterServer", serverParams);
QVariantMap response = serverResult.first.toMap();
QSslSocket *serverSocket = serverResult.second;
@@ -521,6 +520,7 @@ void RemoteProxyTestsTunnelProxy::crossRegisterServerClient()
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response);
+ // SLIP is enabled now
// Now try to register as client
QVariantMap clientParams;
@@ -528,13 +528,17 @@ void RemoteProxyTestsTunnelProxy::crossRegisterServerClient()
clientParams.insert("clientUuid", QUuid::createUuid().toString());
clientParams.insert("serverUuid", serverUuid.toString());
- QPair result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterClient", clientParams, serverSocket);
+ QPair result = invokeTcpSocketTunnelProxyApiCallPersistant("TunnelProxy.RegisterClient", clientParams, true, serverSocket);
response = result.first.toMap();
QVERIFY(!response.isEmpty());
QVERIFY(response.value("status").toString() == "success");
QVERIFY(response.value("params").toMap().contains("tunnelProxyError"));
verifyTunnelProxyError(response, TunnelProxyServer::TunnelProxyErrorAlreadyRegistered);
+ QSignalSpy disconnectedSpy(serverSocket, &QSslSocket::disconnected);
+ QVERIFY(disconnectedSpy.wait());
+ QVERIFY(disconnectedSpy.count() == 1);
+
serverSocket->close();
delete serverSocket;
@@ -551,8 +555,79 @@ void RemoteProxyTestsTunnelProxy::testTunnelProxyServer()
resetDebugCategories();
addDebugCategory("TunnelProxyServer.debug=true");
+ addDebugCategory("TunnelProxyServerTraffic.debug=true");
addDebugCategory("JsonRpcTraffic.debug=true");
addDebugCategory("TunnelProxySocketServer.debug=true");
+ addDebugCategory("TunnelProxyRemoteConnection.debug=true");
+ addDebugCategory("RemoteProxyClientJsonRpcTraffic.debug=true");
+
+ // Tunnel proxy socket server
+ QString serverName = "SuperDuper server name";
+ QUuid serverUuid = QUuid::createUuid();
+
+ TunnelProxySocketServer *tunnelProxyServer = new TunnelProxySocketServer(serverUuid, serverName, this);
+ connect(tunnelProxyServer, &TunnelProxySocketServer::sslErrors, this, [=](const QList &errors){
+ tunnelProxyServer->ignoreSslErrors(errors);
+ });
+
+ tunnelProxyServer->startServer(m_serverUrlTunnelProxyTcp);
+
+ QSignalSpy serverRunningSpy(tunnelProxyServer, &TunnelProxySocketServer::runningChanged);
+ serverRunningSpy.wait();
+ QVERIFY(serverRunningSpy.count() == 1);
+ QList arguments = serverRunningSpy.takeFirst();
+ QVERIFY(arguments.at(0).toBool() == true);
+ QVERIFY(tunnelProxyServer->running());
+
+ // Tunnel proxy client connection
+ QString clientName = "Awesome client name";
+ QUuid clientUuid = QUuid::createUuid();
+
+ TunnelProxyRemoteConnection *clientConnection = new TunnelProxyRemoteConnection(clientUuid, clientName, this);
+ connect(clientConnection, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){
+ clientConnection->ignoreSslErrors(errors);
+ });
+
+ clientConnection->connectServer(m_serverUrlTunnelProxyTcp, serverUuid);
+
+ QSignalSpy clientConnectedSpy(tunnelProxyServer, &TunnelProxySocketServer::clientConnected);
+ QVERIFY(clientConnectedSpy.wait());
+ QVERIFY(clientConnectedSpy.count() == 1);
+ arguments = clientConnectedSpy.takeFirst();
+ TunnelProxySocket *tunnelProxySocket = arguments.at(0).value();
+ QVERIFY(tunnelProxySocket->clientName() == clientName);
+ QVERIFY(tunnelProxySocket->clientUuid() == clientUuid);
+
+ // Stop the server connection and verify the client gets disconnected
+
+ tunnelProxyServer->stopServer();
+
+ QSignalSpy disconnectedSpy(clientConnection, &TunnelProxyRemoteConnection::remoteConnectedChanged);
+ QVERIFY(disconnectedSpy.wait());
+ QVERIFY(disconnectedSpy.count() == 1);
+ arguments = disconnectedSpy.takeFirst();
+ QVERIFY(arguments.at(0).toBool() == false);
+ QVERIFY(!clientConnection->remoteConnected());
+
+
+ resetDebugCategories();
+
+ // Clean up
+ stopServer();
+}
+
+void RemoteProxyTestsTunnelProxy::testTunnelProxyClient()
+{
+ // Start the server
+ startServer();
+
+ resetDebugCategories();
+ addDebugCategory("TunnelProxyServer.debug=true");
+ addDebugCategory("TunnelProxyServerTraffic.debug=true");
+ addDebugCategory("JsonRpcTraffic.debug=true");
+ addDebugCategory("TunnelProxySocketServer.debug=true");
+ addDebugCategory("TunnelProxyRemoteConnection.debug=true");
+ addDebugCategory("RemoteProxyClientJsonRpcTraffic.debug=true");
// Tunnel proxy socket server
QString serverName = "SuperDuper server name";
@@ -574,6 +649,24 @@ void RemoteProxyTestsTunnelProxy::testTunnelProxyServer()
// Tunnel proxy client connection
+ QString clientName = "Awesome client name";
+ QUuid clientUuid = QUuid::createUuid();
+
+ TunnelProxyRemoteConnection *clientConnection = new TunnelProxyRemoteConnection(clientUuid, clientName, this);
+ connect(clientConnection, &TunnelProxyRemoteConnection::sslErrors, this, [=](const QList &errors){
+ clientConnection->ignoreSslErrors(errors);
+ });
+
+ clientConnection->connectServer(m_serverUrlTunnelProxyTcp, serverUuid);
+
+ QSignalSpy clientRemoteConnectedSpy(clientConnection, &TunnelProxyRemoteConnection::remoteConnectedChanged);
+ QVERIFY(clientRemoteConnectedSpy.wait());
+ QVERIFY(clientRemoteConnectedSpy.count() == 1);
+ arguments = clientRemoteConnectedSpy.takeFirst();
+ QVERIFY(arguments.at(0).toBool() == true);
+ QVERIFY(clientConnection->remoteConnected());
+
+ // Verify server socket connected
diff --git a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h
index c4d40cb..8c0ca6b 100644
--- a/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h
+++ b/tests/test-tunnelproxy/remoteproxyteststunnelproxy.h
@@ -66,6 +66,7 @@ private slots:
// Client classes
void testTunnelProxyServer();
+ void testTunnelProxyClient();
};
diff --git a/tests/testbase/basetest.cpp b/tests/testbase/basetest.cpp
index cd55fef..e625ef7 100644
--- a/tests/testbase/basetest.cpp
+++ b/tests/testbase/basetest.cpp
@@ -31,6 +31,8 @@
#include "loggingcategories.h"
#include "remoteproxyconnection.h"
+#include "../common/slipdataprocessor.h"
+
#include
#include
#include
@@ -656,7 +658,7 @@ QVariant BaseTest::injectTcpSocketTunnelProxyData(const QByteArray &data)
return jsonDoc.toVariant();
}
-QPair BaseTest::invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, QSslSocket *existingSocket)
+QPair BaseTest::invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, bool slipEnabled, QSslSocket *existingSocket)
{
QSslSocket *socket = nullptr;
if (!existingSocket) {
@@ -680,30 +682,66 @@ QPair BaseTest::invokeTcpSocketTunnelProxyApiCallPersist
request.insert("method", method);
request.insert("params", params);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(request);
-
+ QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact) + '\n';
QSignalSpy dataSpy(socket, &QSslSocket::readyRead);
- socket->write(jsonDoc.toJson(QJsonDocument::Compact) + '\n');
+
+ if (slipEnabled) {
+ SlipDataProcessor::Frame frame;
+ frame.socketAddress = 0x0000;
+ frame.data = payload;
+ socket->write(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame)));
+ } else {
+ socket->write(payload);
+ }
+
// FIXME: check why it waits the full time here
dataSpy.wait(500);
- if (dataSpy.count() != 1) {
+ if (dataSpy.count() < 1) {
qWarning() << "No data received";
return QPair(QVariant(), socket);
}
- QByteArray data = socket->readAll();
+ QByteArray data;
+ if (slipEnabled) {
+ QByteArray rawData = socket->readAll();
+ QByteArray messageData;
+ // Parse SLIP frame
+ for (int i = 0; i < rawData.length(); i++) {
+ quint8 byte = static_cast(rawData.at(i));
+ if (byte == SlipDataProcessor::ProtocolByteEnd) {
+ // If there is no data...continue since it might be a starting END byte
+ if (messageData.isEmpty())
+ continue;
- // Make sure the response ends with '}\n'
- if (!data.endsWith("}\n")) {
- qWarning() << "JSON data does not end with \"}\n\"";
- return QPair(QVariant(), socket);
+ break;
+ messageData.clear();
+ } else {
+ messageData.append(rawData.at(i));
+ }
+ }
+
+ QByteArray deserializedData = SlipDataProcessor::deserializeData(messageData);
+ SlipDataProcessor::Frame frame = SlipDataProcessor::parseFrame(deserializedData);
+ if (frame.data.isEmpty()) {
+ qWarning() << "Could not deserialize SLIP data";
+ return QPair(QVariant(), socket);
+ }
+ data = frame.data;
+ } else {
+ data = socket->readAll();
+ // Make sure the response ends with '}\n'
+ if (!data.endsWith("}\n")) {
+ qWarning() << "JSON data does not end with \"}\n\"";
+ return QPair(QVariant(), socket);
+ }
}
// Make sure the response it a valid JSON string
QJsonParseError error;
jsonDoc = QJsonDocument::fromJson(data, &error);
if (error.error != QJsonParseError::NoError) {
- qWarning() << "JSON parser error" << error.errorString();
+ qWarning() << "JSON parser error" << error.errorString() << qUtf8Printable(data);
return QPair(QVariant(), socket);
}
@@ -718,7 +756,7 @@ QPair BaseTest::invokeTcpSocketTunnelProxyApiCallPersist
return QPair(QVariant(), socket);
}
-QPair BaseTest::invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, QWebSocket *existingSocket)
+QPair BaseTest::invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params, bool slipEnabled, QWebSocket *existingSocket)
{
QWebSocket *socket = nullptr;
if (!existingSocket) {
@@ -740,16 +778,55 @@ QPair BaseTest::invokeWebSocketTunnelProxyApiCallPersist
request.insert("method", method);
request.insert("params", params);
QJsonDocument jsonDoc = QJsonDocument::fromVariant(request);
+ QByteArray payload = jsonDoc.toJson(QJsonDocument::Compact) + '\n';
QSignalSpy dataSpy(socket, SIGNAL(textMessageReceived(QString)));
- socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact)));
+
+ if (slipEnabled) {
+ SlipDataProcessor::Frame frame;
+ frame.socketAddress = 0x0000;
+ frame.data = payload;
+ socket->sendTextMessage(QString(SlipDataProcessor::serializeData(SlipDataProcessor::buildFrame(frame))));
+ } else {
+ socket->sendTextMessage(QString(payload));
+ }
+
dataSpy.wait();
for (int i = 0; i < dataSpy.count(); i++) {
- // Make sure the response ends with '}\n'
- if (!dataSpy.at(i).last().toByteArray().endsWith("}\n")) {
- qWarning() << "JSON data does not end with \"}\n\"";
- return QPair(QVariant(), socket);
+ QByteArray data;
+ if (slipEnabled) {
+ QByteArray rawData = dataSpy.at(i).last().toByteArray();
+ QByteArray messageData;
+ // Parse SLIP frame
+ for (int i = 0; i < rawData.length(); i++) {
+ quint8 byte = static_cast(rawData.at(i));
+ if (byte == SlipDataProcessor::ProtocolByteEnd) {
+ // If there is no data...continue since it might be a starting END byte
+ if (messageData.isEmpty())
+ continue;
+
+ break;
+ messageData.clear();
+ } else {
+ messageData.append(rawData.at(i));
+ }
+ }
+
+ QByteArray deserializedData = SlipDataProcessor::deserializeData(messageData);
+ SlipDataProcessor::Frame frame = SlipDataProcessor::parseFrame(deserializedData);
+ if (frame.data.isEmpty()) {
+ qWarning() << "Could not deserialize SLIP data";
+ return QPair(QVariant(), socket);
+ }
+ data = frame.data;
+ } else {
+ data = dataSpy.at(i).last().toByteArray();
+ // Make sure the response ends with '}\n'
+ if (!data.endsWith("}\n")) {
+ qWarning() << "JSON data does not end with \"}\n\"";
+ return QPair(QVariant(), socket);
+ }
}
// Make sure the response it a valid JSON string
@@ -771,6 +848,7 @@ QPair BaseTest::invokeWebSocketTunnelProxyApiCallPersist
}
}
+ qWarning() << "No websocket data received...";
m_commandCounter++;
return QPair(QVariant(), socket);
}
diff --git a/tests/testbase/basetest.h b/tests/testbase/basetest.h
index 5d321e2..57cebbd 100644
--- a/tests/testbase/basetest.h
+++ b/tests/testbase/basetest.h
@@ -97,8 +97,8 @@ protected:
QVariant invokeTcpSocketTunnelProxyApiCall(const QString &method, const QVariantMap params = QVariantMap());
QVariant injectTcpSocketTunnelProxyData(const QByteArray &data);
- QPair invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), QSslSocket *existingSocket = nullptr);
- QPair invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), QWebSocket *existingSocket = nullptr);
+ QPair invokeTcpSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), bool slipEnabled = false, QSslSocket *existingSocket = nullptr);
+ QPair invokeWebSocketTunnelProxyApiCallPersistant(const QString &method, const QVariantMap params = QVariantMap(), bool slipEnabled = false, QWebSocket *existingSocket = nullptr);
bool createRemoteConnection(const QString &token, const QString &nonce, QObject *parent);
diff --git a/tests/testbase/testbase.pri b/tests/testbase/testbase.pri
index 723b56d..f5e30df 100644
--- a/tests/testbase/testbase.pri
+++ b/tests/testbase/testbase.pri
@@ -1,5 +1,7 @@
RESOURCES += ../resources/resources.qrc
+include(../common/common.pri)
+
CONFIG += testcase
QT += testlib