feat: Remove Inverse Connections Option

This commit is contained in:
sithlord48
2025-02-22 01:01:58 -05:00
committed by Nick Bolton
parent f9b6bcc950
commit b7eb5c467b
27 changed files with 18 additions and 2072 deletions

View File

@ -280,7 +280,6 @@ void MainWindow::connectSlots()
connect(&m_appConfig, &AppConfig::tlsChanged, this, &MainWindow::appConfigTlsChanged);
connect(&m_appConfig, &AppConfig::screenNameChanged, this, &MainWindow::updateScreenName);
connect(&m_appConfig, &AppConfig::invertConnectionChanged, this, &MainWindow::applyConfig);
connect(&m_coreProcess, &CoreProcess::starting, this, &MainWindow::coreProcessStarting, Qt::DirectConnection);
connect(&m_coreProcess, &CoreProcess::error, this, &MainWindow::coreProcessError);
@ -319,14 +318,10 @@ void MainWindow::connectSlots()
connect(ui->btnToggleCore, &QPushButton::clicked, m_actionStartCore, &QAction::trigger, Qt::UniqueConnection);
connect(ui->btnApplySettings, &QPushButton::clicked, this, &MainWindow::resetCore);
connect(ui->btnConnect, &QPushButton::clicked, this, &MainWindow::resetCore);
connect(ui->btnConnectToClient, &QPushButton::clicked, this, &MainWindow::resetCore);
connect(ui->lineHostname, &QLineEdit::returnPressed, ui->btnConnect, &QPushButton::click);
connect(ui->lineHostname, &QLineEdit::textChanged, &m_coreProcess, &deskflow::gui::CoreProcess::setAddress);
connect(ui->lineClientIp, &QLineEdit::returnPressed, ui->btnConnectToClient, &QPushButton::click);
connect(ui->lineClientIp, &QLineEdit::textChanged, &m_coreProcess, &deskflow::gui::CoreProcess::setAddress);
connect(ui->btnConfigureServer, &QPushButton::clicked, this, [this] { showConfigureServer(""); });
connect(ui->lblComputerName, &QLabel::linkActivated, this, &MainWindow::openSettings);
connect(m_btnFingerprint, &QToolButton::clicked, this, &MainWindow::showMyFingerprint);
@ -670,7 +665,6 @@ void MainWindow::applyConfig()
enableClient(m_appConfig.clientGroupChecked());
ui->lineHostname->setText(m_appConfig.serverHostname());
ui->lineClientIp->setText(m_serverConfig.getClientAddress());
updateLocalFingerprint();
setIcon();
}
@ -680,7 +674,6 @@ void MainWindow::saveSettings()
m_appConfig.setServerGroupChecked(ui->rbModeServer->isChecked());
m_appConfig.setClientGroupChecked(ui->rbModeClient->isChecked());
m_appConfig.setServerHostname(ui->lineHostname->text());
m_serverConfig.setClientAddress(ui->lineClientIp->text());
m_configScopes.save();
}
@ -1033,7 +1026,6 @@ void MainWindow::enableServer(bool enable)
m_appConfig.setServerGroupChecked(enable);
ui->rbModeServer->setChecked(enable);
ui->widgetServer->setEnabled(enable);
ui->widgetServerInput->setVisible(m_appConfig.invertConnection());
if (enable) {
ui->btnToggleCore->setEnabled(true);
@ -1062,7 +1054,7 @@ void MainWindow::enableClient(bool enable)
m_appConfig.setClientGroupChecked(enable);
ui->rbModeClient->setChecked(enable);
ui->widgetClientInput->setEnabled(enable);
ui->widgetClientInput->setVisible(!m_appConfig.invertConnection());
ui->widgetClientInput->setVisible(true);
if (enable) {
ui->btnToggleCore->setEnabled(true);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>883</width>
<height>583</height>
<height>521</height>
</rect>
</property>
<property name="sizePolicy">
@ -171,54 +171,6 @@
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QWidget" name="widgetServerInput" native="true">
<layout class="QVBoxLayout" name="m_pLayoutServerInverse">
<property name="spacing">
<number>15</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lblClientIp">
<property name="text">
<string>Client IP address or hostname:</string>
</property>
<property name="indent">
<number>20</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="leftMargin">
<number>20</number>
</property>
<item>
<widget class="QLineEdit" name="lineClientIp"/>
</item>
<item>
<widget class="QPushButton" name="btnConnectToClient">
<property name="text">
<string>Connect</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
@ -494,8 +446,6 @@
</widget>
<tabstops>
<tabstop>rbModeServer</tabstop>
<tabstop>lineClientIp</tabstop>
<tabstop>btnConnectToClient</tabstop>
<tabstop>btnConfigureServer</tabstop>
<tabstop>rbModeClient</tabstop>
<tabstop>lineHostname</tabstop>

View File

@ -130,10 +130,6 @@ void ServerConfig::commit()
settings().setValue("clipboardSharing", clipboardSharing());
settings().setValue("clipboardSharingSize", QVariant::fromValue(clipboardSharingSize()));
if (!getClientAddress().isEmpty()) {
settings().setValue("clientAddress", getClientAddress());
}
writeSettings(settings(), switchCorners(), "switchCorner");
settings().beginWriteArray("screens");
@ -186,7 +182,6 @@ void ServerConfig::recall()
settings().value("clipboardSharingSize", (int)ServerConfig::defaultClipboardSharingSize()).toULongLong()
);
setClipboardSharing(settings().value("clipboardSharing", true).toBool());
setClientAddress(settings().value("clientAddress", "").toString());
readSettings(settings(), switchCorners(), "switchCorner", 0, static_cast<int>(NumSwitchCorners));
@ -293,11 +288,6 @@ QTextStream &operator<<(QTextStream &outStream, const ServerConfig &config)
outStream << "\t"
<< "clipboardSharingSize = " << config.clipboardSharingSize() << Qt::endl;
if (!config.getClientAddress().isEmpty()) {
outStream << "\t"
<< "clientAddress = " << config.getClientAddress() << Qt::endl;
}
if (config.hasSwitchDelay())
outStream << "\t"
<< "switchDelay = " << config.switchDelay() << Qt::endl;
@ -545,24 +535,6 @@ size_t ServerConfig::setClipboardSharingSize(size_t size)
return size;
}
void ServerConfig::setClientAddress(const QString &address)
{
if (m_pAppConfig->invertConnection()) {
m_ClientAddress = address;
}
}
QString ServerConfig::getClientAddress() const
{
QString clientAddress;
if (m_pAppConfig->invertConnection()) {
clientAddress = m_ClientAddress.trimmed();
}
return clientAddress;
}
QSettingsProxy &ServerConfig::settings()
{
return m_pAppConfig->scopes().activeSettings();

View File

@ -190,7 +190,6 @@ void SettingsDialog::accept()
m_appConfig.setInvertScrollDirection(ui->m_pCheckBoxScrollDirection->isChecked());
m_appConfig.setEnableService(ui->m_pCheckBoxServiceEnabled->isChecked());
m_appConfig.setCloseToTray(ui->m_pCheckBoxCloseToTray->isChecked());
m_appConfig.setInvertConnection(ui->m_pInvertConnection->isChecked());
m_appConfig.setColorfulTrayIcon(ui->rb_icon_colorful->isChecked());
m_appConfig.setRequireClientCerts(ui->chkRequireClientCert->isChecked());
@ -236,8 +235,6 @@ void SettingsDialog::loadFromConfig()
ui->m_pRadioUserScope->setChecked(true);
}
ui->m_pInvertConnection->setChecked(m_appConfig.invertConnection());
if (m_appConfig.colorfulTrayIcon())
ui->rb_icon_colorful->setChecked(true);
else

View File

@ -457,13 +457,6 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="m_pInvertConnection">
<property name="text">
<string>Invert server/client TCP connection</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -19,7 +19,6 @@
#include "deskflow/Screen.h"
#include "deskflow/XScreen.h"
#include "deskflow/protocol_types.h"
#include "net/InverseSockets/InverseSocketFactory.h"
#include "net/NetworkAddress.h"
#include "net/SocketMultiplexer.h"
#include "net/TCPSocketFactory.h"
@ -547,13 +546,5 @@ void ClientApp::startNode()
ISocketFactory *ClientApp::getSocketFactory() const
{
ISocketFactory *socketFactory = nullptr;
if (args().m_hostMode) {
socketFactory = new InverseSocketFactory(m_events, getSocketMultiplexer());
} else {
socketFactory = new TCPSocketFactory(m_events, getSocketMultiplexer());
}
return socketFactory;
return new TCPSocketFactory(m_events, getSocketMultiplexer());
}

View File

@ -19,7 +19,6 @@
#include "deskflow/Screen.h"
#include "deskflow/ServerArgs.h"
#include "deskflow/XScreen.h"
#include "net/InverseSockets/InverseSocketFactory.h"
#include "net/SocketMultiplexer.h"
#include "net/TCPSocketFactory.h"
#include "net/XSocket.h"
@ -660,15 +659,7 @@ void ServerApp::handleScreenSwitched(const Event &e, void *)
ISocketFactory *ServerApp::getSocketFactory() const
{
ISocketFactory *socketFactory = nullptr;
if (args().m_config->isClientMode()) {
socketFactory = new InverseSocketFactory(m_events, getSocketMultiplexer());
} else {
socketFactory = new TCPSocketFactory(m_events, getSocketMultiplexer());
}
return socketFactory;
return new TCPSocketFactory(m_events, getSocketMultiplexer());
}
NetworkAddress ServerApp::getAddress(const NetworkAddress &address) const

View File

@ -63,12 +63,12 @@ const char *const AppConfig::m_SettingsName[] = {
"preventSleep",
"languageSync",
"invertScrollDirection",
"", // 31 = guid, obsolete
"", // 32 = licenseRegistryUrl, obsolete
"", // 33 = licenseNextCheck, obsolete
"initiateConnectionFromServer", // kInvertConnection
"", // 35 = clientHostMode, obsolete
"", // 36 = serverClientMode, obsolete
"", // 31 = guid, obsolete
"", // 32 = licenseRegistryUrl, obsolete
"", // 33 = licenseNextCheck, obsolete
"", // 34 = kInvertConnection, obsolete
"", // 35 = clientHostMode, obsolete
"", // 36 = serverClientMode, obsolete
"enableService",
"closeToTray",
"mainWindowSize",
@ -133,7 +133,6 @@ void AppConfig::recallFromCurrentScope()
m_PreventSleep = getFromCurrentScope(kPreventSleep, m_PreventSleep).toBool();
m_LanguageSync = getFromCurrentScope(kLanguageSync, m_LanguageSync).toBool();
m_InvertScrollDirection = getFromCurrentScope(kInvertScrollDirection, m_InvertScrollDirection).toBool();
m_InvertConnection = getFromCurrentScope(kInvertConnection, m_InvertConnection).toBool();
m_EnableService = getFromCurrentScope(kEnableService, m_EnableService).toBool();
m_CloseToTray = getFromCurrentScope(kCloseToTray, m_CloseToTray).toBool();
m_TlsEnabled = getFromCurrentScope(kTlsEnabled, m_TlsEnabled).toBool();
@ -197,7 +196,6 @@ void AppConfig::commit()
setInCurrentScope(kPreventSleep, m_PreventSleep);
setInCurrentScope(kLanguageSync, m_LanguageSync);
setInCurrentScope(kInvertScrollDirection, m_InvertScrollDirection);
setInCurrentScope(kInvertConnection, m_InvertConnection);
setInCurrentScope(kEnableService, m_EnableService);
setInCurrentScope(kCloseToTray, m_CloseToTray);
setInCurrentScope(kMainWindowSize, m_MainWindowSize);
@ -508,11 +506,6 @@ bool AppConfig::preventSleep() const
return m_PreventSleep;
}
bool AppConfig::invertConnection() const
{
return m_InvertConnection;
}
QString AppConfig::tlsCertPath() const
{
return m_TlsCertPath;
@ -745,12 +738,6 @@ void AppConfig::setCloseToTray(bool minimize)
m_CloseToTray = minimize;
}
void AppConfig::setInvertConnection(bool value)
{
m_InvertConnection = value;
Q_EMIT invertConnectionChanged();
}
void AppConfig::setRequireClientCerts(bool requireClientCerts)
{
if (requireClientCerts == m_RequireClientCert)

View File

@ -89,7 +89,7 @@ private:
// 31 = guid, obsolete
// 32 = license registry url, obsolete
// 33 = license next check, obsolete
kInvertConnection = 34,
// 34 = InvertConnection, obsolete
// 35 = client-host-mode, obsolete
// 36 = server-client-mode, obsolete
kEnableService = 37,
@ -145,7 +145,6 @@ public:
const QString &logFilename() const override;
QString coreServerName() const override;
QString coreClientName() const override;
bool invertConnection() const override;
void persistLogDir() const override;
bool languageSync() const override;
bool invertScrollDirection() const override;
@ -200,7 +199,6 @@ public:
void setCloseToTray(bool minimize) override;
void setTlsCertPath(const QString &path) override;
void setTlsKeyLength(int length) override;
void setInvertConnection(bool value) override;
void setRequireClientCerts(bool requireClientCerts) override;
//
@ -312,7 +310,6 @@ private:
bool m_InvertScrollDirection = false;
bool m_LanguageSync = true;
bool m_PreventSleep = false;
bool m_InvertConnection = false;
bool m_ServerGroupChecked = false;
bool m_UseExternalConfig = false;
QString m_ConfigFile = QStringLiteral("%1/%2.%3").arg(QDir::homePath(), kAppId, s_ConfigFileExt);
@ -345,5 +342,4 @@ private:
signals:
void tlsChanged();
void screenNameChanged();
void invertConnectionChanged();
};

View File

@ -44,7 +44,6 @@ public:
virtual const QString &logFilename() const = 0;
virtual QString coreServerName() const = 0;
virtual QString coreClientName() const = 0;
virtual bool invertConnection() const = 0;
virtual void persistLogDir() const = 0;
virtual bool languageSync() const = 0;
virtual bool invertScrollDirection() const = 0;
@ -83,7 +82,6 @@ public:
virtual void setInvertScrollDirection(bool invertScrollDirection) = 0;
virtual void setEnableService(bool enableService) = 0;
virtual void setCloseToTray(bool closeToTray) = 0;
virtual void setInvertConnection(bool invertConnection) = 0;
virtual void setRequireClientCerts(bool requireClientCerts) = 0;
};

View File

@ -564,16 +564,6 @@ bool CoreProcess::addServerArgs(QStringList &args, QString &app)
return false;
}
if (m_appConfig.invertConnection()) {
qDebug("inverting server connection");
if (correctedAddress().isEmpty()) {
Q_EMIT error(Error::AddressMissing);
qDebug("address is missing for server args");
return false;
}
}
// the address arg is dual purpose; when in listening mode, it's the address
// that the server listens on. when tcp sockets are inverted, it connects to
// that address. this is a bit confusing, and there should be probably be
@ -619,21 +609,14 @@ bool CoreProcess::addClientArgs(QStringList &args, QString &app)
args << "--invert-scroll";
}
if (m_appConfig.invertConnection()) {
qDebug("inverting client connection");
args << "--host";
args << ":" + QString::number(m_appConfig.port());
} else {
if (correctedAddress().isEmpty()) {
Q_EMIT error(Error::AddressMissing);
qDebug("address is missing for client args");
return false;
}
args << correctedAddress() + ":" + QString::number(m_appConfig.port());
if (correctedAddress().isEmpty()) {
Q_EMIT error(Error::AddressMissing);
qDebug("address is missing for client args");
return false;
}
args << correctedAddress() + ":" + QString::number(m_appConfig.port());
return true;
}

View File

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024 Chris Rizzitello <sithlord48@gmail.com>
# SPDX-FileCopyrightText: 2024 - 2025 Chris Rizzitello <sithlord48@gmail.com>
# SPDX-FileCopyrightText: 2012 - 2024 Symless Ltd
# SPDX-FileCopyrightText: 2009 - 2012 Nick Bolton
# SPDX-License-Identifier: MIT
@ -36,20 +36,6 @@ add_library(net STATIC
TSocketMultiplexerMethodJob.h
XSocket.cpp
XSocket.h
InverseSockets/AutoArchSocket.cpp
InverseSockets/AutoArchSocket.h
InverseSockets/InverseClientSocket.cpp
InverseSockets/InverseClientSocket.h
InverseSockets/InverseServerSocket.cpp
InverseSockets/InverseServerSocket.h
InverseSockets/InverseSocketFactory.cpp
InverseSockets/InverseSocketFactory.h
InverseSockets/SecureClientSocket.cpp
InverseSockets/SecureClientSocket.h
InverseSockets/SecureServerSocket.cpp
InverseSockets/SecureServerSocket.h
InverseSockets/SslApi.cpp
InverseSockets/SslApi.h
)
target_link_libraries(

View File

@ -1,182 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2022 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "AutoArchSocket.h"
#include "arch/Arch.h"
#include "arch/XArch.h"
#include "base/Log.h"
#include "net/XSocket.h"
AutoArchSocket::AutoArchSocket(IArchNetwork::EAddressFamily family)
{
try {
m_socket = ARCH->newSocket(family, IArchNetwork::kSTREAM);
LOG((CLOG_DEBUG "opening new socket: %08X", m_socket));
} catch (const XArchNetwork &e) {
throw XSocketCreate(e.what());
}
}
AutoArchSocket::~AutoArchSocket()
{
closeSocket();
}
void AutoArchSocket::setNoDelayOnSocket(bool value)
{
if (isValid()) {
ARCH->setNoDelayOnSocket(m_socket, value);
}
}
void AutoArchSocket::setReuseAddrOnSocket(bool value)
{
if (isValid()) {
ARCH->setReuseAddrOnSocket(m_socket, value);
}
}
void AutoArchSocket::closeSocket()
{
if (isValid()) {
try {
LOG((CLOG_DEBUG "closing socket: %08X", m_socket));
ARCH->closeSocket(m_socket);
m_socket = nullptr;
} catch (const XArchNetwork &e) {
// ignore, there's not much we can do
LOG((CLOG_WARN "error closing socket: %s", e.what()));
}
} else {
LOG((CLOG_WARN "error closing socket because the socket already closed"));
}
}
void AutoArchSocket::bindSocket(const NetworkAddress &addr)
{
if (isValid()) {
try {
ARCH->bindSocket(m_socket, addr.getAddress());
} catch (const XArchNetworkAddressInUse &e) {
throw XSocketAddressInUse(e.what());
} catch (const XArchNetwork &e) {
throw XSocketBind(e.what());
}
}
}
void AutoArchSocket::bindAndListen(const NetworkAddress &addr)
{
try {
setReuseAddrOnSocket();
bindSocket(addr);
listenOnSocket();
} catch (const XArchNetworkAddressInUse &e) {
throw XSocketAddressInUse(e.what());
} catch (const XArchNetwork &e) {
throw XSocketBind(e.what());
}
}
void AutoArchSocket::listenOnSocket()
{
if (isValid()) {
ARCH->listenOnSocket(m_socket);
}
}
ArchSocket AutoArchSocket::acceptSocket()
{
return ARCH->acceptSocket(m_socket, nullptr);
}
void AutoArchSocket::closeSocketForRead()
{
if (isValid()) {
try {
ARCH->closeSocketForRead(m_socket);
} catch (const XArchNetwork &e) {
// ignore, there's not much we can do
LOG((CLOG_WARN "error closing socket: %s", e.what()));
}
}
}
void AutoArchSocket::closeSocketForWrite()
{
if (isValid()) {
try {
ARCH->closeSocketForWrite(m_socket);
} catch (const XArchNetwork &e) {
// ignore, there's not much we can do
LOG((CLOG_WARN "error closing socket: %s", e.what()));
}
}
}
bool AutoArchSocket::connectSocket(const NetworkAddress &addr)
{
bool result = false;
if (isValid()) {
// turn off Nagle algorithm. we send lots of very short messages
// that should be sent without (much) delay. for example, the
// mouse motion messages are much less useful if they're delayed.
setNoDelayOnSocket();
result = ARCH->connectSocket(m_socket, addr.getAddress());
}
return result;
}
size_t AutoArchSocket::readSocket(uint8_t *buffer, size_t size)
{
size_t result = 0;
if (isValid()) {
result = ARCH->readSocket(m_socket, buffer, size);
}
return result;
}
size_t AutoArchSocket::writeSocket(const uint8_t *buffer, size_t size)
{
size_t result = 0;
if (isValid()) {
result = ARCH->writeSocket(m_socket, buffer, size);
}
return result;
}
void AutoArchSocket::throwErrorOnSocket()
{
if (isValid()) {
ARCH->throwErrorOnSocket(m_socket);
}
}
ArchSocket AutoArchSocket::getRawSocket() const
{
return m_socket;
}
bool AutoArchSocket::isValid() const
{
return (m_socket != nullptr);
}
void AutoArchSocket::operator=(ArchSocket socket)
{
if (isValid()) {
closeSocket();
}
m_socket = socket;
}

View File

@ -1,43 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2022 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "net/NetworkAddress.h"
class AutoArchSocket
{
public:
explicit AutoArchSocket(IArchNetwork::EAddressFamily family);
~AutoArchSocket();
AutoArchSocket(const AutoArchSocket &) = delete;
AutoArchSocket &operator=(const AutoArchSocket &) = delete;
void setNoDelayOnSocket(bool value = true);
void setReuseAddrOnSocket(bool value = true);
void listenOnSocket();
ArchSocket acceptSocket();
void bindSocket(const NetworkAddress &addr);
bool connectSocket(const NetworkAddress &addr);
void bindAndListen(const NetworkAddress &addr);
void closeSocket();
void closeSocketForRead();
void closeSocketForWrite();
size_t readSocket(uint8_t *buffer, size_t size);
size_t writeSocket(const uint8_t *buffer, size_t size);
void throwErrorOnSocket();
bool isValid() const;
ArchSocket getRawSocket() const;
void operator=(ArchSocket socket);
private:
ArchSocket m_socket = nullptr;
};

View File

@ -1,409 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2022 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "InverseClientSocket.h"
#include "arch/Arch.h"
#include "arch/XArch.h"
#include "base/IEventJob.h"
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "mt/Lock.h"
#include "net/NetworkAddress.h"
#include "net/SocketMultiplexer.h"
#include "net/TSocketMultiplexerMethodJob.h"
#include "net/XSocket.h"
#include <cstdlib>
#include <cstring>
#include <memory>
//
// InverseClientSocket
//
InverseClientSocket::InverseClientSocket(
IEventQueue *events, SocketMultiplexer *socketMultiplexer, IArchNetwork::EAddressFamily family
)
: IDataSocket(events),
m_events(events),
m_socket(family),
m_listener(family),
m_flushed(&m_mutex, true),
m_socketMultiplexer(socketMultiplexer)
{
}
InverseClientSocket::~InverseClientSocket()
{
try {
// warning virtual function in destructor is very danger practice
InverseClientSocket::close();
} catch (...) {
LOG((CLOG_DEBUG "error while TCP socket destruction"));
}
}
void InverseClientSocket::bind(const NetworkAddress &addr)
{
m_socket.bindSocket(addr);
}
void InverseClientSocket::close()
{
setJob(nullptr);
Lock lock(&m_mutex);
onDisconnected();
}
void *InverseClientSocket::getEventTarget() const
{
return const_cast<void *>(static_cast<const void *>(this));
}
uint32_t InverseClientSocket::read(void *buffer, uint32_t n)
{
// copy data directly from our input buffer
Lock lock(&m_mutex);
uint32_t size = m_inputBuffer.getSize();
if (n > size) {
n = size;
}
if (buffer != nullptr && n != 0) {
memcpy(buffer, m_inputBuffer.peek(n), n);
}
m_inputBuffer.pop(n);
// if no more data and we cannot read or write then send disconnected
if (n > 0 && m_inputBuffer.getSize() == 0 && !m_readable && !m_writable) {
sendEvent(m_events->forISocket().disconnected());
m_connected = false;
}
return n;
}
void InverseClientSocket::write(const void *buffer, uint32_t n)
{
bool wasEmpty;
{
Lock lock(&m_mutex);
// must not have shutdown output
if (!m_writable) {
sendEvent(m_events->forIStream().outputError());
return;
}
// ignore empty writes
if (n == 0) {
return;
}
// copy data to the output buffer
wasEmpty = (m_outputBuffer.getSize() == 0);
m_outputBuffer.write(buffer, n);
// there's data to write
m_flushed = false;
}
// make sure we're waiting to write
if (wasEmpty) {
setJob(newJob(m_socket.getRawSocket()));
}
}
void InverseClientSocket::flush()
{
Lock lock(&m_mutex);
while (m_flushed == false) {
m_flushed.wait();
}
}
void InverseClientSocket::shutdownInput()
{
bool useNewJob = false;
{
Lock lock(&m_mutex);
// shutdown socket for reading
m_socket.closeSocketForRead();
// shutdown buffer for reading
if (m_readable) {
sendEvent(m_events->forIStream().inputShutdown());
onInputShutdown();
useNewJob = true;
}
}
if (useNewJob) {
setJob(newJob(m_socket.getRawSocket()));
}
}
void InverseClientSocket::shutdownOutput()
{
bool useNewJob = false;
{
Lock lock(&m_mutex);
// shutdown socket for writing
m_socket.closeSocketForWrite();
// shutdown buffer for writing
if (m_writable) {
sendEvent(m_events->forIStream().outputShutdown());
onOutputShutdown();
useNewJob = true;
}
}
if (useNewJob) {
setJob(newJob(m_socket.getRawSocket()));
}
}
bool InverseClientSocket::isReady() const
{
Lock lock(&m_mutex);
return (m_inputBuffer.getSize() > 0);
}
bool InverseClientSocket::isFatal() const
{
// TCP sockets aren't ever left in a fatal state.
LOG((CLOG_ERR "isFatal() not valid for non-secure connections"));
return false;
}
uint32_t InverseClientSocket::getSize() const
{
Lock lock(&m_mutex);
return m_inputBuffer.getSize();
}
void InverseClientSocket::connect(const NetworkAddress &addr)
{
{
Lock lock(&m_mutex);
m_listener.bindAndListen(addr);
m_writable = true;
m_readable = true;
}
setJob(newJob(m_listener.getRawSocket()));
}
InverseClientSocket::EJobResult InverseClientSocket::doRead()
{
uint8_t buffer[4096] = {0};
size_t bytesRead = m_socket.readSocket(buffer, sizeof(buffer));
if (bytesRead > 0) {
bool wasEmpty = (m_inputBuffer.getSize() == 0);
// slurp up as much as possible
do {
m_inputBuffer.write(buffer, static_cast<uint32_t>(bytesRead));
bytesRead = m_socket.readSocket(buffer, sizeof(buffer));
} while (bytesRead > 0);
// send input ready if input buffer was empty
if (wasEmpty) {
sendEvent(m_events->forIStream().inputReady());
}
} else {
// remote write end of stream hungup. our input side
// has therefore shutdown but don't flush our buffer
// since there's still data to be read.
sendEvent(m_events->forIStream().inputShutdown());
if (!m_writable && m_inputBuffer.getSize() == 0) {
sendEvent(m_events->forISocket().disconnected());
m_connected = false;
}
m_readable = false;
return InverseClientSocket::EJobResult::kNew;
}
return InverseClientSocket::EJobResult::kRetry;
}
InverseClientSocket::EJobResult InverseClientSocket::doWrite()
{
uint32_t bufferSize = m_outputBuffer.getSize();
auto buffer = static_cast<const uint8_t *>(m_outputBuffer.peek(bufferSize));
const auto bytesWrote = static_cast<uint32_t>(m_socket.writeSocket(buffer, bufferSize));
if (bytesWrote > 0) {
discardWrittenData(bytesWrote);
return InverseClientSocket::EJobResult::kNew;
}
return InverseClientSocket::EJobResult::kRetry;
}
void InverseClientSocket::setJob(ISocketMultiplexerJob *job)
{
// multiplexer will delete the old job
if (job == nullptr) {
m_socketMultiplexer->removeSocket(this);
} else {
m_socketMultiplexer->addSocket(this, job);
}
}
ISocketMultiplexerJob *InverseClientSocket::newJob(ArchSocket socket)
{
// note -- must have m_mutex locked on entry
ISocketMultiplexerJob *result = nullptr;
if (socket) {
auto isWritable = m_writable;
auto handler = &InverseClientSocket::serviceConnecting;
if (m_connected) {
handler = &InverseClientSocket::serviceConnected;
isWritable = (isWritable && (m_outputBuffer.getSize() > 0));
}
if (m_readable || isWritable) {
result = new TSocketMultiplexerMethodJob<InverseClientSocket>(this, handler, socket, m_readable, isWritable);
}
}
return result;
}
void InverseClientSocket::sendConnectionFailedEvent(const char *msg)
{
auto info = new ConnectionFailedInfo(msg);
m_events->addEvent(Event(m_events->forIDataSocket().connectionFailed(), getEventTarget(), info, Event::kDontFreeData)
);
}
void InverseClientSocket::sendEvent(Event::Type type)
{
m_events->addEvent(Event(type, getEventTarget()));
}
void InverseClientSocket::discardWrittenData(int bytesWrote)
{
m_outputBuffer.pop(bytesWrote);
if (m_outputBuffer.getSize() == 0) {
sendEvent(m_events->forIStream().outputFlushed());
m_flushed = true;
m_flushed.broadcast();
}
}
void InverseClientSocket::onConnected()
{
sendEvent(m_events->forIDataSocket().connected());
m_connected = true;
m_readable = true;
m_writable = true;
}
void InverseClientSocket::onInputShutdown()
{
m_inputBuffer.pop(m_inputBuffer.getSize());
m_readable = false;
}
void InverseClientSocket::onOutputShutdown()
{
m_outputBuffer.pop(m_outputBuffer.getSize());
m_writable = false;
// we're now flushed
m_flushed = true;
m_flushed.broadcast();
}
void InverseClientSocket::onDisconnected()
{
if (m_connected) {
sendEvent(m_events->forISocket().disconnected());
}
// disconnected
onInputShutdown();
onOutputShutdown();
m_connected = false;
}
ISocketMultiplexerJob *InverseClientSocket::serviceConnecting(ISocketMultiplexerJob *job, bool read, bool, bool)
{
Lock lock(&m_mutex);
if (read) {
m_socket = m_listener.acceptSocket();
onConnected();
return newJob(m_socket.getRawSocket());
}
return job;
}
ISocketMultiplexerJob *
InverseClientSocket::serviceConnected(ISocketMultiplexerJob *job, bool read, bool write, bool error)
{
Lock lock(&m_mutex);
if (error) {
onDisconnected();
return newJob(m_listener.getRawSocket());
}
EJobResult result = InverseClientSocket::EJobResult::kRetry;
if (write) {
try {
result = doWrite();
} catch (const XArchNetworkShutdown &) {
// remote read end of stream hungup. our output side
// has therefore shutdown.
onOutputShutdown();
sendEvent(m_events->forIStream().outputShutdown());
if (!m_readable && m_inputBuffer.getSize() == 0) {
sendEvent(m_events->forISocket().disconnected());
m_connected = false;
}
result = InverseClientSocket::EJobResult::kNew;
} catch (const XArchNetworkDisconnected &) {
// stream hungup
onDisconnected();
result = InverseClientSocket::EJobResult::kNew;
} catch (const XArchNetwork &e) {
// other write error
LOG((CLOG_WARN "error writing socket: %s", e.what()));
onDisconnected();
sendEvent(m_events->forIStream().outputError());
result = InverseClientSocket::EJobResult::kNew;
}
}
if (read && m_readable) {
try {
result = doRead();
} catch (const XArchNetworkDisconnected &) {
// stream hungup
onDisconnected();
result = InverseClientSocket::EJobResult::kNew;
} catch (const XArchNetwork &e) {
// ignore other read error
LOG((CLOG_WARN "error reading socket: %s", e.what()));
}
}
if (result == InverseClientSocket::EJobResult::kBreak) {
return nullptr;
}
return result == InverseClientSocket::EJobResult::kNew ? newJob(m_socket.getRawSocket()) : job;
}

View File

@ -1,115 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2022 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "AutoArchSocket.h"
#include "arch/IArchNetwork.h"
#include "io/StreamBuffer.h"
#include "mt/CondVar.h"
#include "mt/Mutex.h"
#include "net/IDataSocket.h"
class ISocketMultiplexerJob;
class IEventQueue;
class SocketMultiplexer;
class InverseClientSocket : public IDataSocket
{
public:
InverseClientSocket(
IEventQueue *events, SocketMultiplexer *socketMultiplexer,
IArchNetwork::EAddressFamily family = IArchNetwork::kINET
);
InverseClientSocket(InverseClientSocket const &) = delete;
InverseClientSocket(InverseClientSocket &&) = delete;
~InverseClientSocket() override;
InverseClientSocket &operator=(InverseClientSocket const &) = delete;
InverseClientSocket &operator=(InverseClientSocket &&) = delete;
// ISocket overrides
void bind(const NetworkAddress &) override;
void close() override;
void *getEventTarget() const override;
// IStream overrides
uint32_t read(void *buffer, uint32_t n) override;
void write(const void *buffer, uint32_t n) override;
void flush() override;
void shutdownInput() override;
void shutdownOutput() override;
bool isReady() const override;
bool isFatal() const override;
uint32_t getSize() const override;
// IDataSocket overrides
void connect(const NetworkAddress &) override;
virtual ISocketMultiplexerJob *newJob(ArchSocket socket);
protected:
enum class EJobResult
{
kBreak = -1, //!< Break the Job chain
kRetry, //!< Retry the same job
kNew //!< Require a new job
};
ArchSocket getSocket()
{
return m_socket.getRawSocket();
}
IEventQueue *getEvents()
{
return m_events;
}
virtual EJobResult doRead();
virtual EJobResult doWrite();
void setJob(ISocketMultiplexerJob *);
bool isReadable() const
{
return m_readable;
}
bool isWritable() const
{
return m_writable;
}
Mutex &getMutex()
{
return m_mutex;
}
void sendEvent(Event::Type);
void discardWrittenData(int bytesWrote);
private:
void sendConnectionFailedEvent(const char *);
void onConnected();
void onInputShutdown();
void onOutputShutdown();
void onDisconnected();
ISocketMultiplexerJob *serviceConnecting(ISocketMultiplexerJob *, bool, bool, bool);
ISocketMultiplexerJob *serviceConnected(ISocketMultiplexerJob *, bool, bool, bool);
protected:
bool m_readable = false;
bool m_writable = false;
bool m_connected = false;
IEventQueue *m_events;
StreamBuffer m_inputBuffer;
StreamBuffer m_outputBuffer;
Mutex m_mutex;
AutoArchSocket m_socket;
AutoArchSocket m_listener;
CondVar<bool> m_flushed;
SocketMultiplexer *m_socketMultiplexer;
};

View File

@ -1,106 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2022 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "InverseServerSocket.h"
#include "arch/Arch.h"
#include "arch/XArch.h"
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "io/XIO.h"
#include "mt/Lock.h"
#include "mt/Mutex.h"
#include "net/NetworkAddress.h"
#include "net/SocketMultiplexer.h"
#include "net/TCPSocket.h"
#include "net/TSocketMultiplexerMethodJob.h"
#include "net/XSocket.h"
//
// InverseServerSocket
//
InverseServerSocket::InverseServerSocket(
IEventQueue *events, SocketMultiplexer *socketMultiplexer, IArchNetwork::EAddressFamily family
)
: m_socket(family),
m_events(events),
m_socketMultiplexer(socketMultiplexer)
{
}
InverseServerSocket::~InverseServerSocket()
{
m_socketMultiplexer->removeSocket(this);
}
void InverseServerSocket::bind(const NetworkAddress &addr)
{
Lock lock(&m_mutex);
m_address = addr;
m_socket.connectSocket(m_address);
setListeningJob(true);
}
void InverseServerSocket::close()
{
Lock lock(&m_mutex);
m_socketMultiplexer->removeSocket(this);
m_socket.closeSocket();
}
void *InverseServerSocket::getEventTarget() const
{
return const_cast<void *>(static_cast<const void *>(this));
}
IDataSocket *InverseServerSocket::accept()
{
IDataSocket *socket = nullptr;
try {
socket = new TCPSocket(m_events, m_socketMultiplexer, m_socket.getRawSocket());
if (socket != nullptr) {
setListeningJob();
}
return socket;
} catch (const XArchNetwork &) {
if (socket != nullptr) {
delete socket;
setListeningJob();
}
return nullptr;
} catch (const std::exception &) {
if (socket != nullptr) {
delete socket;
setListeningJob();
}
throw;
}
}
void InverseServerSocket::setListeningJob(bool read)
{
m_socketMultiplexer->addSocket(
this, new TSocketMultiplexerMethodJob<InverseServerSocket>(
this, &InverseServerSocket::serviceListening, m_socket.getRawSocket(), true, read
)
);
}
ISocketMultiplexerJob *InverseServerSocket::serviceListening(ISocketMultiplexerJob *job, bool, bool write, bool error)
{
if (error) {
m_socket.connectSocket(m_address);
return job;
}
if (write) {
m_events->addEvent(Event(m_events->forIListenSocket().connecting(), this));
// stop polling on this socket until the client accepts
return nullptr;
}
return job;
}

View File

@ -1,52 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2012 - 2022 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2002 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "AutoArchSocket.h"
#include "arch/IArchNetwork.h"
#include "mt/Mutex.h"
#include "net/IListenSocket.h"
class ISocketMultiplexerJob;
class IEventQueue;
class SocketMultiplexer;
class InverseServerSocket : public IListenSocket
{
public:
InverseServerSocket(IEventQueue *events, SocketMultiplexer *socketMultiplexer, IArchNetwork::EAddressFamily family);
InverseServerSocket(InverseServerSocket const &) = delete;
InverseServerSocket(InverseServerSocket &&) = delete;
~InverseServerSocket() override;
InverseServerSocket &operator=(InverseServerSocket const &) = delete;
InverseServerSocket &operator=(InverseServerSocket &&) = delete;
// ISocket overrides
void bind(const NetworkAddress &) override;
void close() override;
void *getEventTarget() const override;
// IListenSocket overrides
IDataSocket *accept() override;
protected:
void setListeningJob(bool read = false);
public:
ISocketMultiplexerJob *serviceListening(ISocketMultiplexerJob *, bool, bool, bool);
protected:
AutoArchSocket m_socket;
Mutex m_mutex;
IEventQueue *m_events;
SocketMultiplexer *m_socketMultiplexer;
private:
NetworkAddress m_address;
};

View File

@ -1,46 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2022 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "InverseSocketFactory.h"
#include "net/InverseSockets/InverseClientSocket.h"
#include "net/InverseSockets/InverseServerSocket.h"
#include "net/InverseSockets/SecureClientSocket.h"
#include "net/InverseSockets/SecureServerSocket.h"
//
// InverseSocketFactory
//
InverseSocketFactory::InverseSocketFactory(IEventQueue *events, SocketMultiplexer *socketMultiplexer)
: m_events(events),
m_socketMultiplexer(socketMultiplexer)
{
}
IDataSocket *InverseSocketFactory::create(IArchNetwork::EAddressFamily family, SecurityLevel securityLevel) const
{
if (securityLevel != SecurityLevel::PlainText) {
auto secureSocket = new SecureClientSocket(m_events, m_socketMultiplexer, family);
return secureSocket;
} else {
return new InverseClientSocket(m_events, m_socketMultiplexer, family);
}
}
IListenSocket *
InverseSocketFactory::createListen(IArchNetwork::EAddressFamily family, SecurityLevel securityLevel) const
{
IListenSocket *socket = nullptr;
if (securityLevel != SecurityLevel::PlainText) {
socket = new SecureServerSocket(m_events, m_socketMultiplexer, family);
} else {
socket = new InverseServerSocket(m_events, m_socketMultiplexer, family);
}
return socket;
}

View File

@ -1,30 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2022 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "net/ISocketFactory.h"
class IEventQueue;
class SocketMultiplexer;
class InverseSocketFactory : public ISocketFactory
{
public:
InverseSocketFactory(IEventQueue *events, SocketMultiplexer *socketMultiplexer);
// ISocketFactory overrides
IDataSocket *create(
IArchNetwork::EAddressFamily family = IArchNetwork::kINET, SecurityLevel securityLevel = SecurityLevel::PlainText
) const override;
IListenSocket *createListen(
IArchNetwork::EAddressFamily family = IArchNetwork::kINET, SecurityLevel securityLevel = SecurityLevel::PlainText
) const override;
private:
IEventQueue *m_events = nullptr;
SocketMultiplexer *m_socketMultiplexer = nullptr;
};

View File

@ -1,445 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2015 - 2022 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "SecureClientSocket.h"
#include "../SslLogger.h"
#include <fstream>
#include <set>
#include <sstream>
#include <base/Log.h>
#include <base/Path.h>
#include <base/TMethodEventJob.h>
#include <arch/XArch.h>
#include <mt/Lock.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <net/NetworkAddress.h>
#include <net/TSocketMultiplexerMethodJob.h>
//
// SecureClientSocket
//
constexpr float s_retryDelay = 0.01f;
SecureClientSocket::SecureClientSocket(
IEventQueue *events, SocketMultiplexer *socketMultiplexer, IArchNetwork::EAddressFamily family
)
: InverseClientSocket(events, socketMultiplexer, family)
{
}
void SecureClientSocket::connect(const NetworkAddress &addr)
{
m_events->adoptHandler(
m_events->forIDataSocket().connected(), getEventTarget(),
new TMethodEventJob<SecureClientSocket>(this, &SecureClientSocket::handleTCPConnected)
);
InverseClientSocket::connect(addr);
}
ISocketMultiplexerJob *SecureClientSocket::newJob()
{
// after TCP connection is established, SecureClientSocket will pick up
// connected event and do secureConnect
if (m_connected && !m_secureReady) {
return nullptr;
}
return InverseClientSocket::newJob(getSocket());
}
void SecureClientSocket::secureConnect()
{
setJob(new TSocketMultiplexerMethodJob<SecureClientSocket>(
this, &SecureClientSocket::serviceConnect, getSocket(), isReadable(), isWritable()
));
}
void SecureClientSocket::secureAccept()
{
setJob(new TSocketMultiplexerMethodJob<SecureClientSocket>(
this, &SecureClientSocket::serviceAccept, getSocket(), isReadable(), isWritable()
));
}
InverseClientSocket::EJobResult SecureClientSocket::doRead()
{
uint8_t buffer[4096] = {0};
int bytesRead = 0;
int status = 0;
if (isSecureReady()) {
status = secureRead(buffer, sizeof(buffer), bytesRead);
if (status < 0) {
return InverseClientSocket::EJobResult::kBreak;
} else if (status == 0) {
return InverseClientSocket::EJobResult::kNew;
}
} else {
return InverseClientSocket::EJobResult::kRetry;
}
if (bytesRead > 0) {
bool wasEmpty = (m_inputBuffer.getSize() == 0);
// slurp up as much as possible
do {
m_inputBuffer.write(buffer, bytesRead);
status = secureRead(buffer, sizeof(buffer), bytesRead);
if (status < 0) {
return InverseClientSocket::EJobResult::kBreak;
}
} while (bytesRead > 0 || status > 0);
// send input ready if input buffer was empty
if (wasEmpty) {
sendEvent(m_events->forIStream().inputReady());
}
} else {
// remote write end of stream hungup. our input side
// has therefore shutdown but don't flush our buffer
// since there's still data to be read.
sendEvent(m_events->forIStream().inputShutdown());
if (!m_writable && m_inputBuffer.getSize() == 0) {
sendEvent(m_events->forISocket().disconnected());
m_connected = false;
}
m_readable = false;
return InverseClientSocket::EJobResult::kNew;
}
return InverseClientSocket::EJobResult::kRetry;
}
InverseClientSocket::EJobResult SecureClientSocket::doWrite()
{
static bool s_retry = false;
static int s_retrySize = 0;
static int s_staticBufferSize = 0;
static void *s_staticBuffer = nullptr;
// write data
int bufferSize = 0;
int bytesWrote = 0;
int status = 0;
if (s_retry) {
bufferSize = s_retrySize;
} else {
bufferSize = m_outputBuffer.getSize();
if (bufferSize != 0) {
if (bufferSize > s_staticBufferSize) {
s_staticBuffer = realloc(s_staticBuffer, bufferSize);
s_staticBufferSize = bufferSize;
}
memcpy(s_staticBuffer, m_outputBuffer.peek(bufferSize), bufferSize);
}
}
if (bufferSize == 0) {
return InverseClientSocket::EJobResult::kRetry;
}
if (isSecureReady()) {
status = secureWrite(s_staticBuffer, bufferSize, bytesWrote);
if (status > 0) {
s_retry = false;
} else if (status < 0) {
return InverseClientSocket::EJobResult::kBreak;
} else if (status == 0) {
s_retry = true;
s_retrySize = bufferSize;
return InverseClientSocket::EJobResult::kNew;
}
} else {
return InverseClientSocket::EJobResult::kRetry;
}
if (bytesWrote > 0) {
discardWrittenData(bytesWrote);
return InverseClientSocket::EJobResult::kNew;
}
return InverseClientSocket::EJobResult::kRetry;
}
int SecureClientSocket::secureRead(void *buffer, int size, int &read)
{
LOG((CLOG_DEBUG2 "reading secure socket"));
read = m_ssl.read(static_cast<char *>(buffer), size);
static int retry = 0;
// Check result will cleanup the connection in the case of a fatal
checkResult(read, retry);
if (retry) {
return 0;
}
if (isFatal()) {
return -1;
}
// According to SSL spec, the number of bytes read must not be negative and
// not have an error code from SSL_get_error(). If this happens, it is
// itself an error. Let the parent handle the case
return read;
}
int SecureClientSocket::secureWrite(const void *buffer, int size, int &wrote)
{
LOG((CLOG_DEBUG2 "writing secure socket: %p", this));
wrote = m_ssl.write(static_cast<const char *>(buffer), size);
static int retry = 0;
// Check result will cleanup the connection in the case of a fatal
checkResult(wrote, retry);
if (retry) {
return 0;
}
if (isFatal()) {
return -1;
}
// According to SSL spec, r must not be negative and not have an error code
// from SSL_get_error(). If this happens, it is itself an error. Let the
// parent handle the case
return wrote;
}
bool SecureClientSocket::isSecureReady() const
{
return m_secureReady;
}
bool SecureClientSocket::loadCertificates(const std::string &filename)
{
return m_ssl.loadCertificate(filename);
}
int SecureClientSocket::secureAccept(int socket)
{
LOG((CLOG_DEBUG2 "accepting secure socket"));
static int retry = 0;
checkResult(m_ssl.accept(socket), retry);
if (isFatal()) {
// tell user and sleep so the socket isn't hammered.
LOG((CLOG_ERR "failed to accept secure socket"));
LOG((CLOG_WARN "client connection may not be secure"));
m_secureReady = false;
ARCH->sleep(1);
retry = 0;
return -1; // Failed, error out
}
// If not fatal and no retry, state is good
if (retry == 0) {
m_secureReady = true;
LOG((CLOG_INFO "accepted secure socket"));
m_ssl.logSecureInfo();
return 1;
}
// If not fatal and retry is set, not ready, and return retry
if (retry > 0) {
LOG((CLOG_DEBUG2 "retry accepting secure socket"));
m_secureReady = false;
ARCH->sleep(s_retryDelay);
return 0;
}
// no good state exists here
LOG((CLOG_ERR "unexpected state attempting to accept connection"));
return -1;
}
int SecureClientSocket::secureConnect(int socket)
{
LOG((CLOG_DEBUG2 "connecting secure socket"));
static int retry = 0;
checkResult(m_ssl.connect(socket), retry);
if (isFatal()) {
LOG((CLOG_ERR "failed to connect secure socket"));
retry = 0;
return -1;
}
// If we should retry, not ready and return 0
if (retry > 0) {
LOG((CLOG_DEBUG2 "retry connect secure socket"));
m_secureReady = false;
ARCH->sleep(s_retryDelay);
return 0;
}
retry = 0;
// No error, set ready, process and return ok
m_secureReady = true;
sendEvent(m_events->forIDataSocket().secureConnected());
auto fingerprint = m_ssl.getFingerprint();
LOG((CLOG_NOTE "server fingerprint: %s", fingerprint.c_str()));
if (m_ssl.isTrustedFingerprint(fingerprint)) {
LOG((CLOG_INFO "connected to secure socket"));
m_ssl.logSecureInfo();
return 1;
} else {
LOG((CLOG_ERR "failed to verify server certificate fingerprint"));
disconnect();
return -1; // Fingerprint failed, error
}
}
void SecureClientSocket::setFatal(int code)
{
const std::set<int> nonFatal{
SSL_ERROR_NONE, SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT
};
m_fatal = nonFatal.find(code) == nonFatal.end();
}
int SecureClientSocket::getRetry(int errorCode, int retry) const
{
const std::set<int> retryCodes{
SSL_ERROR_WANT_READ, SSL_ERROR_WANT_WRITE, SSL_ERROR_WANT_CONNECT, SSL_ERROR_WANT_ACCEPT
};
if (errorCode == SSL_ERROR_NONE || isFatal()) {
retry = 0;
} else if (retryCodes.find(errorCode) != retryCodes.end()) {
++retry;
}
return retry;
}
void SecureClientSocket::checkResult(int status, int &retry)
{
// ssl errors are a little quirky. the "want" errors are normal and
// should result in a retry.
int errorCode = m_ssl.getErrorCode(status);
setFatal(errorCode);
retry = getRetry(errorCode, retry);
switch (errorCode) {
case SSL_ERROR_WANT_WRITE:
// Need to make sure the socket is known to be writable so the impending
// select action actually triggers on a write. This isn't necessary for
// m_readable because the socket logic is always readable
m_writable = true;
break;
case SSL_ERROR_SYSCALL:
if (ERR_peek_error() == 0) {
if (status == 0) {
LOG((CLOG_ERR "eof violates tls protocol"));
} else if (status == -1) {
// underlying socket I/O reproted an error
try {
ARCH->throwErrorOnSocket(getSocket());
} catch (const XArchNetwork &e) {
LOG((CLOG_ERR "%s", e.what()));
}
}
}
break;
default:
break;
}
SslLogger::logErrorByCode(errorCode, retry);
if (isFatal()) {
SslLogger::logError();
disconnect();
}
}
void SecureClientSocket::disconnect()
{
sendEvent(getEvents()->forISocket().stopRetry());
sendEvent(getEvents()->forISocket().disconnected());
sendEvent(getEvents()->forIStream().inputShutdown());
}
ISocketMultiplexerJob *SecureClientSocket::serviceConnect(ISocketMultiplexerJob *, bool, bool, bool)
{
Lock lock(&getMutex());
int status = 0;
#ifdef SYSAPI_WIN32
status = secureConnect(static_cast<int>(getSocket()->m_socket));
#elif SYSAPI_UNIX
status = secureConnect(getSocket()->m_fd);
#endif
// If status < 0, error happened
if (status < 0) {
return nullptr;
}
// If status > 0, success
if (status > 0) {
sendEvent(m_events->forIDataSocket().secureConnected());
return newJob();
}
// Retry case
return new TSocketMultiplexerMethodJob<SecureClientSocket>(
this, &SecureClientSocket::serviceConnect, getSocket(), isReadable(), isWritable()
);
}
ISocketMultiplexerJob *SecureClientSocket::serviceAccept(ISocketMultiplexerJob *, bool, bool, bool)
{
Lock lock(&getMutex());
int status = 0;
#ifdef SYSAPI_WIN32
status = secureAccept(static_cast<int>(getSocket()->m_socket));
#elif SYSAPI_UNIX
status = secureAccept(getSocket()->m_fd);
#endif
// If status < 0, error happened
if (status < 0) {
return nullptr;
}
// If status > 0, success
if (status > 0) {
sendEvent(m_events->forClientListener().accepted());
return newJob();
}
// Retry case
return new TSocketMultiplexerMethodJob<SecureClientSocket>(
this, &SecureClientSocket::serviceAccept, getSocket(), isReadable(), isWritable()
);
}
void SecureClientSocket::handleTCPConnected(const Event &, void *)
{
if (getSocket()) {
secureConnect();
} else {
LOG((CLOG_DEBUG "disregarding stale connect event"));
}
}

View File

@ -1,61 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2015 - 2022 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "InverseClientSocket.h"
#include "SslApi.h"
//! Secure socket
/*!
A secure socket using SSL.
*/
class SecureClientSocket : public InverseClientSocket
{
public:
SecureClientSocket(IEventQueue *events, SocketMultiplexer *socketMultiplexer, IArchNetwork::EAddressFamily family);
SecureClientSocket(SecureClientSocket const &) = delete;
SecureClientSocket(SecureClientSocket &&) = delete;
SecureClientSocket &operator=(SecureClientSocket const &) = delete;
SecureClientSocket &operator=(SecureClientSocket &&) = delete;
// IDataSocket overrides
void connect(const NetworkAddress &) override;
ISocketMultiplexerJob *newJob();
bool isFatal() const override
{
return m_fatal;
}
void setFatal(int code);
int getRetry(int errorCode, int retry) const;
bool isSecureReady() const;
void secureConnect();
void secureAccept();
int secureRead(void *buffer, int size, int &read);
int secureWrite(const void *buffer, int size, int &wrote);
EJobResult doRead() override;
EJobResult doWrite() override;
bool loadCertificates(const std::string &CertFile);
private:
// SSL
void initContext(bool server);
int secureAccept(int s);
int secureConnect(int s);
void checkResult(int n, int &retry);
void disconnect();
ISocketMultiplexerJob *serviceConnect(ISocketMultiplexerJob *, bool, bool, bool);
ISocketMultiplexerJob *serviceAccept(ISocketMultiplexerJob *, bool, bool, bool);
void handleTCPConnected(const Event &, void *);
deskflow::ssl::SslApi m_ssl{false};
bool m_secureReady = false;
bool m_fatal = false;
};

View File

@ -1,72 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2015 - 2022 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "SecureServerSocket.h"
#include <arch/XArch.h>
#include <base/String.h>
#include <common/constants.h>
#include <deskflow/ArgParser.h>
#include <deskflow/ArgsBase.h>
#include <net/SecureSocket.h>
#include <net/TSocketMultiplexerMethodJob.h>
//
// SecureServerSocket
//
SecureServerSocket::SecureServerSocket(
IEventQueue *events, SocketMultiplexer *socketMultiplexer, IArchNetwork::EAddressFamily family
)
: InverseServerSocket(events, socketMultiplexer, family)
{
}
IDataSocket *SecureServerSocket::accept()
{
SecureSocket *socket = nullptr;
try {
socket = new SecureSocket(m_events, m_socketMultiplexer, m_socket.getRawSocket());
socket->initSsl(true);
setListeningJob();
auto certificateFilename = getCertificateFileName();
if (socket->loadCertificates(certificateFilename)) {
socket->secureAccept();
} else {
delete socket;
socket = nullptr;
}
} catch (const XArchNetwork &) {
if (socket) {
delete socket;
socket = nullptr;
setListeningJob();
}
} catch (const std::exception &) {
if (socket) {
delete socket;
setListeningJob();
}
throw;
}
return dynamic_cast<IDataSocket *>(socket);
}
std::string SecureServerSocket::getCertificateFileName() const
{
// if the tls cert option is set use that for the certificate file
auto certificateFilename = ArgParser::argsBase().m_tlsCertFile;
if (certificateFilename.empty()) {
// TODO: Reduce duplication of these strings between here and
// SecureSocket.cpp
certificateFilename = deskflow::string::sprintf("%s/tls/%s.pem", ARCH->getProfileDirectory().c_str(), kAppId);
}
return certificateFilename;
}

View File

@ -1,20 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2015 - 2022 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include "InverseServerSocket.h"
class SecureServerSocket : public InverseServerSocket
{
public:
SecureServerSocket(IEventQueue *events, SocketMultiplexer *socketMultiplexer, IArchNetwork::EAddressFamily family);
// IListenSocket overrides
IDataSocket *accept() override;
private:
std::string getCertificateFileName() const;
};

View File

@ -1,261 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2015 - 2022 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#include "SslApi.h"
#include "../SslLogger.h"
#include <fstream>
#include <memory>
#include <base/Log.h>
#include <base/Path.h>
#include <base/String.h>
#include <openssl/err.h>
namespace deskflow {
namespace ssl {
using AutoX509 = std::unique_ptr<X509, decltype(&X509_free)>;
SslApi::SslApi(bool isServer)
{
SSL_library_init();
// load & register all cryptos, etc.
OpenSSL_add_all_algorithms();
// load all error messages
SSL_load_error_strings();
createContext(isServer);
SslLogger::logSecureLibInfo();
}
SslApi::~SslApi()
{
if (m_ssl) {
SSL_shutdown(m_ssl);
SSL_free(m_ssl);
m_ssl = nullptr;
}
if (m_context) {
SSL_CTX_free(m_context);
m_context = nullptr;
}
}
int SslApi::read(char *buffer, int size)
{
auto read = 0;
if (m_ssl) {
read = SSL_read(m_ssl, buffer, size);
}
return read;
}
int SslApi::write(const char *buffer, int size)
{
auto wrote = 0;
if (m_ssl) {
wrote = SSL_write(m_ssl, buffer, size);
}
return wrote;
}
int SslApi::accept(int socket)
{
int result = 0;
if (m_ssl) {
// set connection socket to SSL state
SSL_set_fd(m_ssl, socket);
result = SSL_accept(m_ssl);
}
return result;
}
int SslApi::connect(int socket)
{
auto result = 0;
if (m_ssl) {
// attach the socket descriptor
SSL_set_fd(m_ssl, socket);
result = SSL_connect(m_ssl);
}
return result;
}
void SslApi::createSSL()
{
if (m_ssl == nullptr && m_context != nullptr) {
m_ssl = SSL_new(m_context);
}
}
bool SslApi::loadCertificate(const std::string &filename)
{
bool result = false;
if (isCertificateExists(filename)) {
auto r = SSL_CTX_use_certificate_file(m_context, filename.c_str(), SSL_FILETYPE_PEM);
if (r <= 0) {
SslLogger::logError("could not use tls certificate");
return false;
}
r = SSL_CTX_use_PrivateKey_file(m_context, filename.c_str(), SSL_FILETYPE_PEM);
if (r <= 0) {
SslLogger::logError("could not use tls private key");
return false;
}
r = SSL_CTX_check_private_key(m_context);
if (!r) {
SslLogger::logError("could not verify tls private key");
return false;
}
}
return result;
}
bool SslApi::showCertificate() const
{
bool result = false;
if (m_ssl) {
// get the server's certificate
AutoX509 cert(SSL_get_peer_certificate(m_ssl), &X509_free);
if (cert) {
auto line = X509_NAME_oneline(X509_get_subject_name(cert.get()), nullptr, 0);
LOG((CLOG_INFO "server tls certificate info: %s", line));
OPENSSL_free(line);
result = true;
} else {
SslLogger::logError("server has no tls certificate");
}
}
return result;
}
std::string SslApi::getFingerprint() const
{
// calculate received certificate fingerprint
AutoX509 cert(SSL_get_peer_certificate(m_ssl), &X509_free);
unsigned int tempFingerprintLen = 0;
unsigned char tempFingerprint[EVP_MAX_MD_SIZE] = {0};
int digestResult = X509_digest(cert.get(), EVP_sha256(), tempFingerprint, &tempFingerprintLen);
if (digestResult <= 0) {
LOG((CLOG_ERR "failed to calculate fingerprint, digest result: %d", digestResult));
return "";
}
// format fingerprint into hexdecimal format with colon separator
std::string fingerprint(static_cast<char *>(static_cast<void *>(tempFingerprint)), tempFingerprintLen);
formatFingerprint(fingerprint);
return fingerprint;
}
bool SslApi::isTrustedFingerprint(const std::string &fingerprint) const
{
// TODO: Reduce duplication of these strings between here and SecureSocket.cpp
auto trustedServersFilename =
deskflow::string::sprintf("%s/tls/trusted-servers", ARCH->getProfileDirectory().c_str());
// check if this fingerprint exist
std::ifstream file;
file.open(deskflow::filesystem::path(trustedServersFilename));
bool isValid = false;
if (file.is_open()) {
while (!file.eof()) {
std::string fileLine;
getline(file, fileLine);
if (!fileLine.empty() && !fileLine.compare(fingerprint)) {
isValid = true;
break;
}
}
} else {
LOG((CLOG_ERR "failed to open trusted fingerprints file: %s", trustedServersFilename.c_str()));
}
return (isValid && showCertificate());
}
void SslApi::createContext(bool isServer)
{
// create new context from method
if (isServer) {
m_context = SSL_CTX_new(SSLv23_server_method());
} else {
m_context = SSL_CTX_new(SSLv23_client_method());
}
// Prevent the usage of of all version prior to TLSv1.2 as they are known to
// be vulnerable
SSL_CTX_set_options(m_context, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
if (m_context) {
m_ssl = SSL_new(m_context);
} else {
SslLogger::logError();
}
}
void SslApi::logSecureInfo() const
{
SslLogger::logSecureCipherInfo(m_ssl);
SslLogger::logSecureConnectInfo(m_ssl);
}
int SslApi::getErrorCode(int status) const
{
return SSL_get_error(m_ssl, status);
}
void SslApi::formatFingerprint(std::string &fingerprint) const
{
// to hexidecimal
fingerprint = deskflow::string::toHex(fingerprint, 2);
// all uppercase
deskflow::string::uppercase(fingerprint);
// add colon to separate each 2 charactors
size_t separators = fingerprint.size() / 2;
for (size_t i = 1; i < separators; i++) {
fingerprint.insert(i * 3 - 1, ":");
}
}
bool SslApi::isCertificateExists(const std::string &filename) const
{
bool result = (!filename.empty());
if (result) {
std::ifstream file(deskflow::filesystem::path(filename));
result = file.good();
if (!result) {
std::string errorMsg("tls certificate doesn't exist: ");
errorMsg.append(filename);
SslLogger::logError(errorMsg.c_str());
}
} else {
SslLogger::logError("tls certificate is not specified");
}
return result;
}
} // namespace ssl
} // namespace deskflow

View File

@ -1,46 +0,0 @@
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2015 - 2022 Symless Ltd.
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
*/
#pragma once
#include <openssl/ssl.h>
#include <string>
namespace deskflow {
namespace ssl {
class SslApi
{
public:
explicit SslApi(bool isServer = false);
SslApi(SslApi const &) = delete;
SslApi &operator=(SslApi const &) = delete;
~SslApi();
int read(char *buffer, int size);
int write(const char *buffer, int size);
int accept(int socket);
int connect(int socket);
bool loadCertificate(const std::string &filename);
bool showCertificate() const;
std::string getFingerprint() const;
bool isTrustedFingerprint(const std::string &fingerprint) const;
void logSecureInfo() const;
int getErrorCode(int status) const;
private:
void createSSL();
void formatFingerprint(std::string &fingerprint) const;
bool isCertificateExists(const std::string &filename) const;
void createContext(bool isServer = false);
SSL *m_ssl = nullptr;
SSL_CTX *m_context = nullptr;
};
} // namespace ssl
} // namespace deskflow

View File

@ -45,7 +45,6 @@ public:
MOCK_METHOD(const QString &, logFilename, (), (const, override));
MOCK_METHOD(QString, coreServerName, (), (const, override));
MOCK_METHOD(QString, coreClientName, (), (const, override));
MOCK_METHOD(bool, invertConnection, (), (const, override));
MOCK_METHOD(void, persistLogDir, (), (const, override));
MOCK_METHOD(bool, languageSync, (), (const, override));
MOCK_METHOD(bool, invertScrollDirection, (), (const, override));
@ -84,7 +83,6 @@ public:
MOCK_METHOD(void, setInvertScrollDirection, (bool invertScrollDirection), (override));
MOCK_METHOD(void, setEnableService, (bool enableService), (override));
MOCK_METHOD(void, setCloseToTray, (bool closeToTray), (override));
MOCK_METHOD(void, setInvertConnection, (bool invertConnection), (override));
MOCK_METHOD(void, setRequireClientCerts, (bool requireClientCerts), (override));
private: