Add ability to initiate connection from server (#7222)
* Add a new hidden readonly option "initiateConnectionFromServer" * Add option "clientHostMode" * Add host mode to the client GUI * Hide main window controls for the client host mode * Store client address into the server config file * Add client mode the server * Pass option --host into the client * Add --host parameter into the synergyc * Add InverseSocketFactory * Add InverseClientSocket * Add InverseServerSocket * Add AutoArchSocket * Add listener on the client side * Add connection from the server side * Fix code smells * Additional code smells * Requested changes Co-authored-by: Serhii Hadzhilov <serhii@symless.com>
This commit is contained in:
parent
452c631d6e
commit
c37b0d2fcf
30 changed files with 1468 additions and 116 deletions
|
|
@ -1,3 +1,9 @@
|
|||
1.14.6
|
||||
======
|
||||
|
||||
Enhancements:
|
||||
- #7222 Ability to initiate connection from server
|
||||
|
||||
1.14.5
|
||||
======
|
||||
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ cmake_minimum_required (VERSION 3.4)
|
|||
|
||||
set (SYNERGY_VERSION_MAJOR 1)
|
||||
set (SYNERGY_VERSION_MINOR 14)
|
||||
set (SYNERGY_VERSION_PATCH 5)
|
||||
set (SYNERGY_VERSION_PATCH 6)
|
||||
set (SYNERGY_VERSION_BUILD 1)
|
||||
set (SYNERGY_VERSION_STAGE "stable")
|
||||
set (SYNERGY_VERSION_STAGE "snapshot")
|
||||
|
||||
#
|
||||
# Version from CI
|
||||
|
|
|
|||
|
|
@ -83,7 +83,10 @@ const char* AppConfig::m_SynergySettingsName[] = {
|
|||
"eliteBackersUrl",
|
||||
"guid",
|
||||
"licenseRegistryUrl",
|
||||
"licenseNextCheck"
|
||||
"licenseNextCheck",
|
||||
"initiateConnectionFromServer",
|
||||
"clientHostMode",
|
||||
"serverClientMode"
|
||||
};
|
||||
|
||||
static const char* logLevelNames[] =
|
||||
|
|
@ -99,19 +102,19 @@ AppConfig::AppConfig() :
|
|||
m_Port(24800),
|
||||
m_Interface(),
|
||||
m_LogLevel(0),
|
||||
m_LogToFile(),
|
||||
m_WizardLastRun(0),
|
||||
m_ProcessMode(DEFAULT_PROCESS_MODE),
|
||||
m_StartedBefore(),
|
||||
m_AutoConfig(true),
|
||||
m_AutoConfigServer(),
|
||||
m_ElevateMode(defaultElevateMode),
|
||||
m_Edition(kUnregistered),
|
||||
m_CryptoEnabled(false),
|
||||
m_AutoHide(false),
|
||||
m_LastExpiringWarningTime(0),
|
||||
m_AutoConfigServer(),
|
||||
m_MinimizeToTray(false),
|
||||
m_Edition(kUnregistered),
|
||||
m_LogToFile(),
|
||||
m_StartedBefore(),
|
||||
m_ActivationHasRun(),
|
||||
m_MinimizeToTray(false),
|
||||
m_ServerGroupChecked(),
|
||||
m_UseExternalConfig(),
|
||||
m_UseInternalConfig(),
|
||||
|
|
@ -253,6 +256,9 @@ void AppConfig::loadSettings()
|
|||
m_guid = loadCommonSetting(kGuid, QUuid::createUuid()).toString();
|
||||
m_licenseRegistryUrl = loadCommonSetting(kLicenseRegistryUrl, "https://api2.prod.symless.com/license/register").toString();
|
||||
m_licenseNextCheck = loadCommonSetting(kLicenseNextCheck, 0).toULongLong();
|
||||
m_ClientHostMode = loadSetting(kClientHostMode, true).toBool();
|
||||
m_ServerClientMode = loadSetting(kServerClientMode, true).toBool();
|
||||
m_InitiateConnectionFromServer = loadSetting(kInitiateConnectionFromServer, false).toBool();
|
||||
|
||||
//only change the serial key if the settings being loaded contains a key
|
||||
bool updateSerial = ConfigWriter::make()
|
||||
|
|
@ -320,6 +326,8 @@ void AppConfig::saveSettings()
|
|||
setSetting(kPreventSleep, m_PreventSleep);
|
||||
setSetting(kLanguageSync, m_LanguageSync);
|
||||
setSetting(kInvertScrollDirection, m_InvertScrollDirection);
|
||||
setSetting(kClientHostMode, m_ClientHostMode);
|
||||
setSetting(kServerClientMode, m_ServerClientMode);
|
||||
}
|
||||
|
||||
m_unsavedChanges = false;
|
||||
|
|
@ -512,6 +520,18 @@ void AppConfig::setLanguageSync(bool newValue) {
|
|||
|
||||
bool AppConfig::getPreventSleep() const { return m_PreventSleep; }
|
||||
|
||||
bool AppConfig::getClientHostMode() const {
|
||||
return (m_ClientHostMode && getInitiateConnectionFromServer());
|
||||
}
|
||||
|
||||
bool AppConfig::getServerClientMode() const {
|
||||
return (m_ServerClientMode && getInitiateConnectionFromServer());
|
||||
}
|
||||
|
||||
bool AppConfig::getInitiateConnectionFromServer() const {
|
||||
return m_InitiateConnectionFromServer;
|
||||
}
|
||||
|
||||
void AppConfig::setPreventSleep(bool newValue) {
|
||||
setSettingModified(m_PreventSleep, newValue);
|
||||
}
|
||||
|
|
@ -641,6 +661,14 @@ void AppConfig::setServerHostname(const QString& newValue) {
|
|||
setSettingModified(m_ServerHostname, newValue);
|
||||
}
|
||||
|
||||
void AppConfig::setClientHostMode(bool newValue) {
|
||||
setSettingModified(m_ClientHostMode, newValue);
|
||||
}
|
||||
|
||||
void AppConfig::setServerClientMode(bool newValue) {
|
||||
setSettingModified(m_ServerClientMode, newValue);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void AppConfig::setSettingModified(T &variable, const T& newValue) {
|
||||
if (variable != newValue)
|
||||
|
|
|
|||
|
|
@ -125,6 +125,9 @@ class AppConfig: public QObject, public GUI::Config::ConfigBase
|
|||
bool getLanguageSync() const;
|
||||
void setPreventSleep(bool b);
|
||||
bool getPreventSleep() const;
|
||||
bool getClientHostMode() const;
|
||||
bool getServerClientMode() const;
|
||||
bool getInitiateConnectionFromServer() const;
|
||||
#ifndef SYNERGY_ENTERPRISE
|
||||
bool activationHasRun() const;
|
||||
AppConfig& activationHasRun(bool value);
|
||||
|
|
@ -157,6 +160,8 @@ class AppConfig: public QObject, public GUI::Config::ConfigBase
|
|||
void setUseInternalConfig(bool) ;
|
||||
void setClientGroupChecked(bool) ;
|
||||
void setServerHostname(const QString&);
|
||||
void setClientHostMode(bool newValue);
|
||||
void setServerClientMode(bool newValue);
|
||||
|
||||
/// @brief Set the path to the TLS/SSL certificate file that will be used
|
||||
/// @param [in] path The path to the Certificate
|
||||
|
|
@ -219,7 +224,10 @@ protected:
|
|||
kEliteBackersUrl,
|
||||
kGuid,
|
||||
kLicenseRegistryUrl,
|
||||
kLicenseNextCheck
|
||||
kLicenseNextCheck,
|
||||
kInitiateConnectionFromServer,
|
||||
kClientHostMode,
|
||||
kServerClientMode
|
||||
};
|
||||
|
||||
void setScreenName(const QString& s);
|
||||
|
|
@ -269,6 +277,9 @@ protected:
|
|||
bool m_InvertScrollDirection = false;
|
||||
bool m_LanguageSync = true;
|
||||
bool m_PreventSleep = false;
|
||||
bool m_InitiateConnectionFromServer = false;
|
||||
bool m_ClientHostMode = true;
|
||||
bool m_ServerClientMode = true;
|
||||
|
||||
bool m_ServerGroupChecked;
|
||||
bool m_UseExternalConfig;
|
||||
|
|
|
|||
|
|
@ -307,6 +307,7 @@ void MainWindow::loadSettings()
|
|||
enableClient(appConfig().getClientGroupChecked());
|
||||
|
||||
m_pLineEditHostname->setText(appConfig().getServerHostname());
|
||||
m_pLineEditClienIp->setText(serverConfig().getClientAddress());
|
||||
}
|
||||
|
||||
void MainWindow::initConnections()
|
||||
|
|
@ -325,6 +326,7 @@ void MainWindow::saveSettings()
|
|||
appConfig().setServerGroupChecked(m_pRadioGroupServer->isChecked());
|
||||
appConfig().setClientGroupChecked(m_pRadioGroupClient->isChecked());
|
||||
appConfig().setServerHostname(m_pLineEditHostname->text());
|
||||
serverConfig().setClientAddress(m_pLineEditClienIp->text());
|
||||
|
||||
/* Save everything */
|
||||
GUI::Config::ConfigWriter::make()->globalSave();
|
||||
|
|
@ -836,7 +838,7 @@ bool MainWindow::clientArgs(QStringList& args, QString& app)
|
|||
}
|
||||
#endif
|
||||
|
||||
if (m_pLineEditHostname->text().isEmpty())
|
||||
if (m_pLineEditHostname->text().isEmpty() && !appConfig().getClientHostMode())
|
||||
{
|
||||
#if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
|
||||
//check if autoconfig mode is enabled
|
||||
|
|
@ -858,18 +860,24 @@ bool MainWindow::clientArgs(QStringList& args, QString& app)
|
|||
#endif
|
||||
}
|
||||
|
||||
QString hostName = m_pLineEditHostname->text();
|
||||
// if interface is IPv6 - ensure that ip is in square brackets
|
||||
if (hostName.count(':') > 1) {
|
||||
if(hostName[0] != '[') {
|
||||
hostName.insert(0, '[');
|
||||
}
|
||||
if(hostName[hostName.size() - 1] != ']') {
|
||||
hostName.push_back(']');
|
||||
if (appConfig().getClientHostMode()) {
|
||||
args <<"--host";
|
||||
args <<":" + QString::number(appConfig().port());
|
||||
}
|
||||
else {
|
||||
QString hostName = m_pLineEditHostname->text();
|
||||
// if interface is IPv6 - ensure that ip is in square brackets
|
||||
if (hostName.count(':') > 1) {
|
||||
if(hostName[0] != '[') {
|
||||
hostName.insert(0, '[');
|
||||
}
|
||||
if(hostName[hostName.size() - 1] != ']') {
|
||||
hostName.push_back(']');
|
||||
}
|
||||
}
|
||||
args << hostName + ":" + QString::number(appConfig().port());
|
||||
}
|
||||
|
||||
args << hostName + ":" + QString::number(appConfig().port());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -947,6 +955,14 @@ bool MainWindow::serverArgs(QStringList& args, QString& app)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (appConfig().getServerClientMode() &&
|
||||
m_pLineEditClienIp->text().isEmpty())
|
||||
{
|
||||
QMessageBox::warning(this, tr("Client IP address or name is empty"),
|
||||
tr("Please fill in a client IP address or name."));
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
// wrap in quotes so a malicious user can't start \Program.exe as admin.
|
||||
app = QString("\"%1\"").arg(app);
|
||||
|
|
@ -1330,6 +1346,8 @@ void MainWindow::on_m_pActionSettings_triggered()
|
|||
auto result = SettingsDialog(this, appConfig()).exec();
|
||||
if(result == QDialog::Accepted)
|
||||
{
|
||||
enableServer(appConfig().getServerGroupChecked());
|
||||
enableClient(appConfig().getClientGroupChecked());
|
||||
auto state = synergyState();
|
||||
if ((state == synergyConnected) || (state == synergyConnecting) || (state == synergyListening)) {
|
||||
restartSynergy();
|
||||
|
|
@ -1497,6 +1515,17 @@ void MainWindow::enableServer(bool enable)
|
|||
|
||||
if (enable)
|
||||
{
|
||||
if (m_AppConfig->getServerClientMode()) {
|
||||
m_pLabelClientIp->show();
|
||||
m_pLineEditClienIp->show();
|
||||
m_pButtonConnectToClient->show();
|
||||
}
|
||||
else {
|
||||
m_pLabelClientIp->hide();
|
||||
m_pLineEditClienIp->hide();
|
||||
m_pButtonConnectToClient->hide();
|
||||
}
|
||||
|
||||
m_pButtonConfigureServer->show();
|
||||
m_pLabelServerState->show();
|
||||
updateLocalFingerprint();
|
||||
|
|
@ -1507,6 +1536,9 @@ void MainWindow::enableServer(bool enable)
|
|||
m_pLabelFingerprint->hide();
|
||||
m_pButtonConfigureServer->hide();
|
||||
m_pLabelServerState->hide();
|
||||
m_pLabelClientIp->hide();
|
||||
m_pLineEditClienIp->hide();
|
||||
m_pButtonConnectToClient->hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1517,9 +1549,18 @@ void MainWindow::enableClient(bool enable)
|
|||
|
||||
if (enable)
|
||||
{
|
||||
m_pLabelServerName->show();
|
||||
m_pLineEditHostname->show();
|
||||
m_pButtonConnect->show();
|
||||
if (m_AppConfig->getClientHostMode())
|
||||
{
|
||||
m_pLabelServerName->hide();
|
||||
m_pLineEditHostname->hide();
|
||||
m_pButtonConnect->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pLabelServerName->show();
|
||||
m_pLineEditHostname->show();
|
||||
m_pButtonConnect->show();
|
||||
}
|
||||
m_pButtonToggleStart->setEnabled(enable);
|
||||
}
|
||||
else
|
||||
|
|
@ -1558,3 +1599,8 @@ void MainWindow::on_m_pButtonConnect_clicked()
|
|||
on_m_pButtonApply_clicked();
|
||||
}
|
||||
|
||||
void MainWindow::on_m_pButtonConnectToClient_clicked()
|
||||
{
|
||||
on_m_pButtonApply_clicked();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -263,6 +263,7 @@ private slots:
|
|||
void on_m_pComboServerList_currentIndexChanged(const QString &arg1);
|
||||
|
||||
void on_m_pButtonConnect_clicked();
|
||||
void on_m_pButtonConnectToClient_clicked();
|
||||
|
||||
signals:
|
||||
void windowShown();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>720</width>
|
||||
<height>658</height>
|
||||
<height>552</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
|
@ -244,11 +244,41 @@ background-color: rgba(192,192,192, 0.1);
|
|||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_pLabelClientIp">
|
||||
<property name="text">
|
||||
<string>Client IP address or name:</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>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="m_pLineEditClienIp"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_pButtonConnectToClient">
|
||||
<property name="text">
|
||||
<string>Connect</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="synergy_widgets::ServerStateLabel" name="m_pLabelServerState">
|
||||
<property name="text">
|
||||
|
|
@ -685,7 +715,6 @@ background-color: rgba(192,192,192, 0.1);
|
|||
<font>
|
||||
<family>Arial</family>
|
||||
<pointsize>-1</pointsize>
|
||||
<weight>87</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -150,6 +150,10 @@ void ServerConfig::saveSettings()
|
|||
settings().setValue("clipboardSharing", clipboardSharing());
|
||||
settings().setValue("clipboardSharingSize", QVariant::fromValue(clipboardSharingSize()));
|
||||
|
||||
if (!getClientAddress().isEmpty()) {
|
||||
settings().setValue("clientAddress", getClientAddress());
|
||||
}
|
||||
|
||||
writeSettings(settings(), switchCorners(), "switchCorner");
|
||||
|
||||
settings().beginWriteArray("screens");
|
||||
|
|
@ -204,6 +208,7 @@ void ServerConfig::loadSettings()
|
|||
setClipboardSharingSize(settings().value("clipboardSharingSize",
|
||||
(int) ServerConfig::defaultClipboardSharingSize()).toULongLong());
|
||||
setClipboardSharing(settings().value("clipboardSharing", true).toBool());
|
||||
setClientAddress(settings().value("clientAddress", "").toString());
|
||||
|
||||
readSettings(settings(), switchCorners(), "switchCorner", false, NumSwitchCorners);
|
||||
|
||||
|
|
@ -296,6 +301,10 @@ QTextStream& operator<<(QTextStream& outStream, const ServerConfig& config)
|
|||
outStream << "\t" << "clipboardSharing = " << (config.clipboardSharing() ? "true" : "false") << endl;
|
||||
outStream << "\t" << "clipboardSharingSize = " << config.clipboardSharingSize() << endl;
|
||||
|
||||
if (!config.getClientAddress().isEmpty()) {
|
||||
outStream << "\t" << "clientAddress = " << config.getClientAddress() << endl;
|
||||
}
|
||||
|
||||
if (config.hasSwitchDelay())
|
||||
outStream << "\t" << "switchDelay = " << config.switchDelay() << endl;
|
||||
|
||||
|
|
@ -554,6 +563,22 @@ size_t ServerConfig::setClipboardSharingSize(size_t size) {
|
|||
return size;
|
||||
}
|
||||
|
||||
void ServerConfig::setClientAddress(const QString& address) {
|
||||
if (m_pAppConfig->getServerClientMode()) {
|
||||
m_clientAddress = address;
|
||||
}
|
||||
}
|
||||
|
||||
QString ServerConfig::getClientAddress() const {
|
||||
QString clientAddress;
|
||||
|
||||
if (m_pAppConfig->getServerClientMode()) {
|
||||
clientAddress = m_clientAddress.trimmed();
|
||||
}
|
||||
|
||||
return clientAddress;
|
||||
}
|
||||
|
||||
QSettings &ServerConfig::settings() {
|
||||
using GUI::Config::ConfigWriter;
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,8 @@ class ServerConfig : public BaseConfig, public GUI::Config::ConfigBase
|
|||
bool isScreenExists(const QString& screenName) const;
|
||||
void addClient(const QString& clientName);
|
||||
bool isHotkeysAvailable() const;
|
||||
QString getClientAddress() const;
|
||||
void setClientAddress(const QString& address);
|
||||
|
||||
protected:
|
||||
QSettings& settings();
|
||||
|
|
@ -147,6 +149,7 @@ class ServerConfig : public BaseConfig, public GUI::Config::ConfigBase
|
|||
bool m_DisableLockToScreen;
|
||||
bool m_ClipboardSharing;
|
||||
size_t m_ClipboardSharingSize;
|
||||
QString m_clientAddress;
|
||||
MainWindow* m_pMainWindow;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -53,8 +53,11 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
|
|||
buttonBox->button(QDialogButtonBox::Save)->setEnabled(false);
|
||||
enableControls(appConfig().isWritable());
|
||||
|
||||
m_pCheckBoxLanguageSync->setVisible(m_pMainWindow->synergyType() == MainWindow::synergyClient);
|
||||
m_pCheckBoxScrollDirection->setVisible(m_pMainWindow->synergyType() == MainWindow::synergyClient);
|
||||
const auto isClientMode = m_pMainWindow->synergyType() == MainWindow::synergyClient;
|
||||
m_pCheckBoxLanguageSync->setVisible(isClientMode);
|
||||
m_pCheckBoxScrollDirection->setVisible(isClientMode);
|
||||
m_pCheckBoxClientHostMode->setVisible(isClientMode && appConfig().getInitiateConnectionFromServer());
|
||||
m_pCheckBoxServerClientMode->setVisible(!isClientMode && appConfig().getInitiateConnectionFromServer());
|
||||
|
||||
const auto& serveConfig = m_pMainWindow->serverConfig();
|
||||
m_pLineEditScreenName->setValidator(new validators::ScreenNameValidator(m_pLineEditScreenName, m_pLabelNameError, (&serveConfig.screens())));
|
||||
|
|
@ -72,6 +75,8 @@ SettingsDialog::SettingsDialog(QWidget* parent, AppConfig& config) :
|
|||
connect(m_pComboElevate, SIGNAL(currentIndexChanged(int)), this, SLOT(onChange()));
|
||||
connect(m_pCheckBoxLanguageSync, SIGNAL(clicked()), this, SLOT(onChange()));
|
||||
connect(m_pCheckBoxScrollDirection, SIGNAL(clicked()), this, SLOT(onChange()));
|
||||
connect(m_pCheckBoxClientHostMode, SIGNAL(clicked()), this, SLOT(onChange()));
|
||||
connect(m_pCheckBoxServerClientMode,SIGNAL(clicked()), this, SLOT(onChange()));
|
||||
|
||||
adjustSize();
|
||||
}
|
||||
|
|
@ -96,6 +101,8 @@ void SettingsDialog::accept()
|
|||
appConfig().setCryptoEnabled(m_pCheckBoxEnableCrypto->isChecked());
|
||||
appConfig().setLanguageSync(m_pCheckBoxLanguageSync->isChecked());
|
||||
appConfig().setInvertScrollDirection(m_pCheckBoxScrollDirection->isChecked());
|
||||
appConfig().setClientHostMode(m_pCheckBoxClientHostMode->isChecked());
|
||||
appConfig().setServerClientMode(m_pCheckBoxServerClientMode->isChecked());
|
||||
|
||||
appConfig().saveSettings();
|
||||
QDialog::accept();
|
||||
|
|
@ -155,6 +162,8 @@ void SettingsDialog::loadFromConfig() {
|
|||
m_pCheckBoxEnableCrypto->setChecked(m_appConfig.getCryptoEnabled());
|
||||
m_pCheckBoxLanguageSync->setChecked(m_appConfig.getLanguageSync());
|
||||
m_pCheckBoxScrollDirection->setChecked(m_appConfig.getInvertScrollDirection());
|
||||
m_pCheckBoxClientHostMode->setChecked(m_appConfig.getClientHostMode());
|
||||
m_pCheckBoxServerClientMode->setChecked(m_appConfig.getServerClientMode());
|
||||
|
||||
setupSeurity();
|
||||
|
||||
|
|
@ -367,6 +376,8 @@ bool SettingsDialog::isModified()
|
|||
|| appConfig().getCryptoEnabled() != m_pCheckBoxEnableCrypto->isChecked()
|
||||
|| appConfig().isSystemScoped() != m_isSystemAtStart
|
||||
|| appConfig().getLanguageSync() != m_pCheckBoxLanguageSync->isChecked()
|
||||
|| appConfig().getClientHostMode() != m_pCheckBoxClientHostMode->isChecked()
|
||||
|| appConfig().getServerClientMode() != m_pCheckBoxServerClientMode->isChecked()
|
||||
|| appConfig().getInvertScrollDirection() != m_pCheckBoxScrollDirection->isChecked())
|
||||
);
|
||||
}
|
||||
|
|
@ -390,6 +401,8 @@ void SettingsDialog::enableControls(bool enable) {
|
|||
m_labelAdminRightsMessage->setVisible(!enable);
|
||||
m_pCheckBoxLanguageSync->setEnabled(enable);
|
||||
m_pCheckBoxScrollDirection->setEnabled(enable);
|
||||
m_pCheckBoxClientHostMode->setEnabled(enable);
|
||||
m_pCheckBoxServerClientMode->setEnabled(enable);
|
||||
|
||||
if (enable) {
|
||||
m_pLabelLogPath->setEnabled(m_pCheckBoxLogToFile->isChecked());
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>404</width>
|
||||
<height>620</height>
|
||||
<height>819</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
|
|
@ -337,62 +337,7 @@ background-color: rgba(192,192,192, 0.1);
|
|||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="m_pLabelElevate">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Elevate privileges</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="m_pCheckBoxLanguageSync">
|
||||
<property name="text">
|
||||
<string>&Use server's keyboard language on this machine</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="m_pLabel_27">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>75</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>User interface language</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>m_pComboLanguage</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<item row="6" column="1">
|
||||
<widget class="QComboBox" name="m_pComboElevate">
|
||||
<property name="toolTip">
|
||||
<string>Specify when the Synergy service should run at an elevated privilege level</string>
|
||||
|
|
@ -417,16 +362,85 @@ background-color: rgba(192,192,192, 0.1);
|
|||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<item row="3" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="m_pCheckBoxLanguageSync">
|
||||
<property name="text">
|
||||
<string>&Use server's keyboard language on this machine</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<widget class="QCheckBox" name="m_pCheckBoxClientHostMode">
|
||||
<property name="text">
|
||||
<string>Host mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<spacer name="verticalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="m_pLabelElevate">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Elevate privileges</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="m_pComboLanguage"/>
|
||||
</item>
|
||||
<item row="1" column="0" colspan="3">
|
||||
<item row="2" column="0" colspan="3">
|
||||
<widget class="QCheckBox" name="m_pCheckBoxScrollDirection">
|
||||
<property name="text">
|
||||
<string>Invert scroll direction on this computer</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="m_pLabel_27">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>75</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>User interface language</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>m_pComboLanguage</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="0">
|
||||
<widget class="QCheckBox" name="m_pCheckBoxServerClientMode">
|
||||
<property name="text">
|
||||
<string>Client mode</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
@ -695,7 +709,6 @@ background-color: rgba(192,192,192, 0.1);
|
|||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>50</weight>
|
||||
<bold>false</bold>
|
||||
</font>
|
||||
</property>
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ Client::Client(
|
|||
m_events(events),
|
||||
m_sendFileThread(nullptr),
|
||||
m_writeToDropDirThread(nullptr),
|
||||
m_socket(NULL),
|
||||
m_useSecureNetwork(args.m_enableCrypto),
|
||||
m_args(args),
|
||||
m_enableClipboard(true),
|
||||
|
|
@ -135,29 +134,32 @@ Client::connect(size_t addressIndex)
|
|||
}
|
||||
|
||||
try {
|
||||
// resolve the server hostname. do this every time we connect
|
||||
// in case we couldn't resolve the address earlier or the address
|
||||
// has changed (which can happen frequently if this is a laptop
|
||||
// being shuttled between various networks). patch by Brent
|
||||
// Priddy.
|
||||
m_resolvedAddressesCount = m_serverAddress.resolve(addressIndex);
|
||||
if (m_args.m_hostMode)
|
||||
{
|
||||
LOG((CLOG_NOTE "waiting for server conection on %i port", m_serverAddress.getPort()));
|
||||
}
|
||||
else {
|
||||
// resolve the server hostname. do this every time we connect
|
||||
// in case we couldn't resolve the address earlier or the address
|
||||
// has changed (which can happen frequently if this is a laptop
|
||||
// being shuttled between various networks). patch by Brent
|
||||
// Priddy.
|
||||
m_resolvedAddressesCount = m_serverAddress.resolve(addressIndex);
|
||||
|
||||
// m_serverAddress will be null if the hostname address is not reolved
|
||||
if (m_serverAddress.getAddress() != nullptr) {
|
||||
// to help users troubleshoot, show server host name (issue: 60)
|
||||
LOG((CLOG_NOTE "connecting to '%s': %s:%i",
|
||||
m_serverAddress.getHostname().c_str(),
|
||||
ARCH->addrToString(m_serverAddress.getAddress()).c_str(),
|
||||
m_serverAddress.getPort()));
|
||||
// m_serverAddress will be null if the hostname address is not reolved
|
||||
if (m_serverAddress.getAddress() != nullptr) {
|
||||
// to help users troubleshoot, show server host name (issue: 60)
|
||||
LOG((CLOG_NOTE "connecting to '%s': %s:%i",
|
||||
m_serverAddress.getHostname().c_str(),
|
||||
ARCH->addrToString(m_serverAddress.getAddress()).c_str(),
|
||||
m_serverAddress.getPort()));
|
||||
}
|
||||
}
|
||||
|
||||
// create the socket
|
||||
IDataSocket* socket = m_socketFactory->create(m_useSecureNetwork, ARCH->getAddrFamily(m_serverAddress.getAddress()));
|
||||
m_socket = dynamic_cast<TCPSocket*>(socket);
|
||||
|
||||
// filter socket messages, including a packetizing filter
|
||||
m_stream = socket;
|
||||
m_stream = new PacketStreamFilter(m_events, m_stream, true);
|
||||
m_stream = new PacketStreamFilter(m_events, socket, true);
|
||||
|
||||
// connect
|
||||
LOG((CLOG_DEBUG1 "connecting to server"));
|
||||
|
|
@ -546,10 +548,12 @@ Client::setupTimer()
|
|||
{
|
||||
assert(m_timer == NULL);
|
||||
|
||||
m_timer = m_events->newOneShotTimer(2.0, NULL);
|
||||
m_events->adoptHandler(Event::kTimer, m_timer,
|
||||
new TMethodEventJob<Client>(this,
|
||||
&Client::handleConnectTimeout));
|
||||
if (!m_args.m_hostMode) {
|
||||
m_timer = m_events->newOneShotTimer(2.0, NULL);
|
||||
m_events->adoptHandler(Event::kTimer, m_timer,
|
||||
new TMethodEventJob<Client>(this,
|
||||
&Client::handleConnectTimeout));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -238,7 +238,6 @@ private:
|
|||
using AutoThread = std::unique_ptr<Thread>;
|
||||
AutoThread m_sendFileThread;
|
||||
AutoThread m_writeToDropDirThread;
|
||||
TCPSocket* m_socket;
|
||||
bool m_useSecureNetwork;
|
||||
bool m_enableClipboard;
|
||||
size_t m_maximumClipboardSize;
|
||||
|
|
|
|||
|
|
@ -14,8 +14,14 @@
|
|||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
file(GLOB headers "*.h")
|
||||
file(GLOB sources "*.cpp")
|
||||
file(GLOB headers
|
||||
"*.h"
|
||||
"InverseSockets/*.h"
|
||||
)
|
||||
file(GLOB sources
|
||||
"*.cpp"
|
||||
"InverseSockets/*.cpp"
|
||||
)
|
||||
|
||||
if (SYNERGY_ADD_HEADERS)
|
||||
list(APPEND sources ${headers})
|
||||
|
|
|
|||
201
src/lib/net/InverseSockets/AutoArchSocket.cpp
Normal file
201
src/lib/net/InverseSockets/AutoArchSocket.cpp
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2022 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "AutoArchSocket.h"
|
||||
|
||||
#include "base/Log.h"
|
||||
#include "arch/Arch.h"
|
||||
#include "arch/XArch.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* 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* 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;
|
||||
}
|
||||
53
src/lib/net/InverseSockets/AutoArchSocket.h
Normal file
53
src/lib/net/InverseSockets/AutoArchSocket.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2022 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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* buffer, size_t size);
|
||||
size_t writeSocket(const UInt8* buffer, size_t size);
|
||||
void throwErrorOnSocket();
|
||||
|
||||
bool isValid() const;
|
||||
ArchSocket getRawSocket() const;
|
||||
void operator =(ArchSocket socket);
|
||||
|
||||
private:
|
||||
ArchSocket m_socket = nullptr;
|
||||
};
|
||||
452
src/lib/net/InverseSockets/InverseClientSocket.cpp
Normal file
452
src/lib/net/InverseSockets/InverseClientSocket.cpp
Normal file
|
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2022 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "InverseClientSocket.h"
|
||||
|
||||
#include "net/NetworkAddress.h"
|
||||
#include "net/SocketMultiplexer.h"
|
||||
#include "net/TSocketMultiplexerMethodJob.h"
|
||||
#include "net/XSocket.h"
|
||||
#include "mt/Lock.h"
|
||||
#include "arch/Arch.h"
|
||||
#include "arch/XArch.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/IEventQueue.h"
|
||||
#include "base/IEventJob.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#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
|
||||
InverseClientSocket::read(void* buffer, UInt32 n)
|
||||
{
|
||||
// copy data directly from our input buffer
|
||||
Lock lock(&m_mutex);
|
||||
UInt32 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 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
|
||||
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 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>(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 bufferSize = m_outputBuffer.getSize();
|
||||
auto buffer = static_cast<const UInt8*>(m_outputBuffer.peek(bufferSize));
|
||||
const auto bytesWrote = static_cast<UInt32>(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;
|
||||
}
|
||||
111
src/lib/net/InverseSockets/InverseClientSocket.h
Normal file
111
src/lib/net/InverseSockets/InverseClientSocket.h
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2022 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net/IDataSocket.h"
|
||||
#include "io/StreamBuffer.h"
|
||||
#include "mt/CondVar.h"
|
||||
#include "mt/Mutex.h"
|
||||
#include "arch/IArchNetwork.h"
|
||||
#include "AutoArchSocket.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 read(void* buffer, UInt32 n) override;
|
||||
void write(const void* buffer, UInt32 n) override;
|
||||
void flush() override;
|
||||
void shutdownInput() override;
|
||||
void shutdownOutput() override;
|
||||
bool isReady() const override;
|
||||
bool isFatal() const override;
|
||||
UInt32 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);
|
||||
|
||||
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;
|
||||
};
|
||||
122
src/lib/net/InverseSockets/InverseServerSocket.cpp
Normal file
122
src/lib/net/InverseSockets/InverseServerSocket.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2022 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "InverseServerSocket.h"
|
||||
|
||||
#include "net/NetworkAddress.h"
|
||||
#include "net/SocketMultiplexer.h"
|
||||
#include "net/TCPSocket.h"
|
||||
#include "net/TSocketMultiplexerMethodJob.h"
|
||||
#include "net/XSocket.h"
|
||||
#include "io/XIO.h"
|
||||
#include "mt/Lock.h"
|
||||
#include "mt/Mutex.h"
|
||||
#include "arch/Arch.h"
|
||||
#include "arch/XArch.h"
|
||||
#include "base/Log.h"
|
||||
#include "base/IEventQueue.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_socket.connectSocket(addr);
|
||||
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 &ex) {
|
||||
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) {
|
||||
close();
|
||||
return nullptr;
|
||||
}
|
||||
if (write) {
|
||||
m_events->addEvent(Event(m_events->forIListenSocket().connecting(), this));
|
||||
// stop polling on this socket until the client accepts
|
||||
return nullptr;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
61
src/lib/net/InverseSockets/InverseServerSocket.h
Normal file
61
src/lib/net/InverseSockets/InverseServerSocket.h
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2022 Symless Ltd.
|
||||
* Copyright (C) 2002 Chris Schoeneman
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "net/IListenSocket.h"
|
||||
#include "arch/IArchNetwork.h"
|
||||
#include "mt/Mutex.h"
|
||||
#include "AutoArchSocket.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);
|
||||
|
||||
private:
|
||||
AutoArchSocket m_socket;
|
||||
Mutex m_mutex;
|
||||
IEventQueue* m_events;
|
||||
SocketMultiplexer* m_socketMultiplexer;
|
||||
};
|
||||
59
src/lib/net/InverseSockets/InverseSocketFactory.cpp
Normal file
59
src/lib/net/InverseSockets/InverseSocketFactory.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2022 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "InverseSocketFactory.h"
|
||||
#include "net/InverseSockets/InverseClientSocket.h"
|
||||
#include "net/InverseSockets/InverseServerSocket.h"
|
||||
#include "net/SecureSocket.h"
|
||||
#include "net/SecureListenSocket.h"
|
||||
|
||||
//
|
||||
// InverseSocketFactory
|
||||
//
|
||||
|
||||
InverseSocketFactory::InverseSocketFactory(IEventQueue* events, SocketMultiplexer* socketMultiplexer) :
|
||||
m_events(events),
|
||||
m_socketMultiplexer(socketMultiplexer)
|
||||
{
|
||||
}
|
||||
|
||||
IDataSocket*
|
||||
InverseSocketFactory::create(bool secure, IArchNetwork::EAddressFamily family) const
|
||||
{
|
||||
if (secure) {
|
||||
auto secureSocket = new SecureSocket(m_events, m_socketMultiplexer, family);
|
||||
secureSocket->initSsl (false);
|
||||
return secureSocket;
|
||||
}
|
||||
else {
|
||||
return new InverseClientSocket(m_events, m_socketMultiplexer, family);
|
||||
}
|
||||
}
|
||||
|
||||
IListenSocket*
|
||||
InverseSocketFactory::createListen(bool secure, IArchNetwork::EAddressFamily family) const
|
||||
{
|
||||
IListenSocket* socket = nullptr;
|
||||
|
||||
if (secure) {
|
||||
socket = new SecureListenSocket(m_events, m_socketMultiplexer, family);
|
||||
}
|
||||
else {
|
||||
socket = new InverseServerSocket(m_events, m_socketMultiplexer, family);
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
34
src/lib/net/InverseSockets/InverseSocketFactory.h
Normal file
34
src/lib/net/InverseSockets/InverseSocketFactory.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* synergy -- mouse and keyboard sharing utility
|
||||
* Copyright (C) 2012-2022 Symless Ltd.
|
||||
*
|
||||
* This package is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* found in the file LICENSE that should have accompanied this file.
|
||||
*
|
||||
* This package is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "net/ISocketFactory.h"
|
||||
|
||||
class IEventQueue;
|
||||
class SocketMultiplexer;
|
||||
|
||||
class InverseSocketFactory : public ISocketFactory {
|
||||
public:
|
||||
InverseSocketFactory(IEventQueue* events, SocketMultiplexer* socketMultiplexer);
|
||||
|
||||
// ISocketFactory overrides
|
||||
IDataSocket* create(bool secure, IArchNetwork::EAddressFamily family = IArchNetwork::kINET) const override;
|
||||
IListenSocket* createListen(bool secure, IArchNetwork::EAddressFamily family = IArchNetwork::kINET) const override;
|
||||
|
||||
private:
|
||||
IEventQueue* m_events = nullptr;
|
||||
SocketMultiplexer* m_socketMultiplexer = nullptr;
|
||||
};
|
||||
|
|
@ -653,6 +653,11 @@ Config::formatInterval(const Interval& x)
|
|||
(int)(x.second * 100.0f + 0.5f));
|
||||
}
|
||||
|
||||
String Config::getClientAddress() const
|
||||
{
|
||||
return m_clientAddress;
|
||||
}
|
||||
|
||||
void
|
||||
Config::readSection(ConfigReadContext& s)
|
||||
{
|
||||
|
|
@ -773,6 +778,9 @@ Config::readSectionOptions(ConfigReadContext& s)
|
|||
else if (name == "clipboardSharingSize") {
|
||||
addOption("", kOptionClipboardSharingSize, s.parseInt(value));
|
||||
}
|
||||
else if (name == "clientAddress") {
|
||||
m_clientAddress = value;
|
||||
}
|
||||
else {
|
||||
handled = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,6 +450,11 @@ public:
|
|||
*/
|
||||
static String formatInterval(const Interval&);
|
||||
|
||||
//! Get client address as a string
|
||||
/*!
|
||||
* Return client address a string.
|
||||
*/
|
||||
String getClientAddress() const;
|
||||
//@}
|
||||
|
||||
private:
|
||||
|
|
@ -481,6 +486,7 @@ private:
|
|||
InputFilter m_inputFilter;
|
||||
bool m_hasLockToScreenAction;
|
||||
IEventQueue* m_events;
|
||||
String m_clientAddress;
|
||||
};
|
||||
|
||||
//! Configuration read context
|
||||
|
|
|
|||
|
|
@ -117,6 +117,9 @@ ArgParser::parseClientArgs(lib::synergy::ClientArgs& args, int argc, const char*
|
|||
else if (isArg(i, argc, argv, nullptr, "--invert-scroll")) {
|
||||
args.m_clientScrollDirection = lib::synergy::ClientScrollDirection::INVERT_SERVER;
|
||||
}
|
||||
else if (isArg(i, argc, argv, nullptr, "--host")) {
|
||||
args.m_hostMode = true;
|
||||
}
|
||||
else {
|
||||
if (i + 1 == argc) {
|
||||
args.m_synergyAddress = argv[i];
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include "synergy/ClientArgs.h"
|
||||
#include "net/NetworkAddress.h"
|
||||
#include "net/TCPSocketFactory.h"
|
||||
#include "net/InverseSockets/InverseSocketFactory.h"
|
||||
#include "net/SocketMultiplexer.h"
|
||||
#include "net/XSocket.h"
|
||||
#include "mt/Thread.h"
|
||||
|
|
@ -128,6 +129,7 @@ ClientApp::help()
|
|||
" [--yscroll <delta>]"
|
||||
" [--sync-language]"
|
||||
" [--invert-scroll]"
|
||||
" [--host]"
|
||||
WINAPI_ARG
|
||||
HELP_SYS_ARGS
|
||||
HELP_COMMON_ARGS
|
||||
|
|
@ -142,6 +144,7 @@ ClientApp::help()
|
|||
" 120 by default.\n"
|
||||
" --sync-language set this parameter to enable language synchronization.\n"
|
||||
" --invert-scroll invert scroll direction on this computer.\n"
|
||||
" --host client starts a listener and waits for a server connection.\n"
|
||||
HELP_COMMON_INFO_2
|
||||
"\n"
|
||||
"* marks defaults.\n"
|
||||
|
|
@ -366,7 +369,7 @@ ClientApp::openClient(const String& name, const NetworkAddress& address,
|
|||
m_events,
|
||||
name,
|
||||
address,
|
||||
new TCPSocketFactory(m_events, getSocketMultiplexer()),
|
||||
getSocketFactory(),
|
||||
screen,
|
||||
args());
|
||||
|
||||
|
|
@ -599,3 +602,17 @@ ClientApp::startNode()
|
|||
m_bye(kExitFailed);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Event;
|
|||
class Client;
|
||||
class NetworkAddress;
|
||||
class Thread;
|
||||
class ISocketFactory;
|
||||
namespace lib {
|
||||
namespace synergy {
|
||||
class ClientArgs;
|
||||
|
|
@ -82,6 +83,8 @@ public:
|
|||
Client* getClientPtr() { return m_client; }
|
||||
|
||||
private:
|
||||
ISocketFactory* getSocketFactory() const;
|
||||
|
||||
Client* m_client;
|
||||
synergy::Screen* m_clientScreen;
|
||||
NetworkAddress* m_serverAddress;
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ namespace lib {
|
|||
*/
|
||||
ClientScrollDirection m_clientScrollDirection = ClientScrollDirection::SERVER;
|
||||
|
||||
/**
|
||||
* @brief m_hostMode - activates host mode.
|
||||
* Client starts a listener and waits for a server connection.
|
||||
*/
|
||||
bool m_hostMode = false;
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
#include "synergy/ServerArgs.h"
|
||||
#include "net/SocketMultiplexer.h"
|
||||
#include "net/TCPSocketFactory.h"
|
||||
#include "net/InverseSockets/InverseSocketFactory.h"
|
||||
#include "net/XSocket.h"
|
||||
#include "arch/Arch.h"
|
||||
#include "base/EventQueue.h"
|
||||
|
|
@ -633,8 +634,8 @@ ClientListener*
|
|||
ServerApp::openClientListener(const NetworkAddress& address)
|
||||
{
|
||||
ClientListener* listen = new ClientListener(
|
||||
address,
|
||||
new TCPSocketFactory(m_events, getSocketMultiplexer()),
|
||||
getAddress(address),
|
||||
getSocketFactory(),
|
||||
m_events,
|
||||
args().m_enableCrypto);
|
||||
|
||||
|
|
@ -678,6 +679,34 @@ ServerApp::handleScreenSwitched(const Event& e, void*)
|
|||
{
|
||||
}
|
||||
|
||||
ISocketFactory* ServerApp::getSocketFactory() const
|
||||
{
|
||||
ISocketFactory* socketFactory = nullptr;
|
||||
const auto clientAddress = args().m_config->getClientAddress();
|
||||
|
||||
if (clientAddress.empty()) {
|
||||
socketFactory = new TCPSocketFactory(m_events, getSocketMultiplexer());
|
||||
}
|
||||
else {
|
||||
socketFactory = new InverseSocketFactory(m_events, getSocketMultiplexer());
|
||||
}
|
||||
|
||||
return socketFactory;
|
||||
}
|
||||
|
||||
NetworkAddress ServerApp::getAddress(const NetworkAddress& address) const
|
||||
{
|
||||
const auto clientAddress = args().m_config->getClientAddress();
|
||||
if (clientAddress.empty()) {
|
||||
return address;
|
||||
}
|
||||
else {
|
||||
NetworkAddress addr(clientAddress.c_str(), kDefaultPort);
|
||||
addr.resolve();
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
ServerApp::mainLoop()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ class ClientListener;
|
|||
class EventQueueTimer;
|
||||
class ILogOutputter;
|
||||
class IEventQueue;
|
||||
class ISocketFactory;
|
||||
namespace lib {
|
||||
namespace synergy{
|
||||
class ServerArgs;
|
||||
|
|
@ -119,6 +120,8 @@ public:
|
|||
|
||||
private:
|
||||
void handleScreenSwitched(const Event&, void* data);
|
||||
ISocketFactory* getSocketFactory() const;
|
||||
NetworkAddress getAddress(const NetworkAddress& address) const;
|
||||
};
|
||||
|
||||
// configuration file name
|
||||
|
|
|
|||
Loading…
Reference in a new issue