feat: Remove Inverse Connections Option
This commit is contained in:
@ -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);
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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());
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
};
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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:
|
||||
|
||||
Reference in New Issue
Block a user