From cd3263db5059e164119bb5c97556a675305758a4 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Sat, 10 Jun 2023 05:25:41 +0300 Subject: [PATCH] made libssh::ssh_connect a non-blocking feature - extended error handling when connecting via ssh --- client/core/defs.h | 2 +- client/core/errorstrings.cpp | 1 + client/core/sshclient.cpp | 49 +++++++++++++------ client/core/sshclient.h | 2 +- client/ui/controllers/connectionController.h | 3 +- client/ui/qml/Controls2/BasicButtonType.qml | 4 +- client/ui/qml/Controls2/PopupType.qml | 12 +++-- .../ui/qml/Pages2/PageSettingsServerData.qml | 1 + client/ui/qml/main2.qml | 8 ++- client/vpnconnection.cpp | 6 +-- 10 files changed, 54 insertions(+), 34 deletions(-) diff --git a/client/core/defs.h b/client/core/defs.h index 61c45e4e..1c268246 100644 --- a/client/core/defs.h +++ b/client/core/defs.h @@ -36,7 +36,7 @@ enum ErrorCode // Ssh connection errors SshRequsetDeniedError, SshInterruptedError, SshInternalError, - SshPrivateKeyError, SshPrivateKeyFormatError, + SshPrivateKeyError, SshPrivateKeyFormatError, SshTimeoutError, // Ssh sftp errors SshSftpEofError, SshSftpNoSuchFileError, SshSftpPermissionDeniedError, diff --git a/client/core/errorstrings.cpp b/client/core/errorstrings.cpp index dd298c76..cd66186d 100644 --- a/client/core/errorstrings.cpp +++ b/client/core/errorstrings.cpp @@ -24,6 +24,7 @@ QString errorString(ErrorCode code){ case(SshInternalError): return QObject::tr("Ssh internal error"); case(SshPrivateKeyError): return QObject::tr("Invalid private key or invalid passphrase entered"); case(SshPrivateKeyFormatError): return QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); + case(SshTimeoutError): return QObject::tr("Timeout connecting to server"); // Libssh sftp errors case(SshSftpEofError): return QObject::tr("Sftp error: End-of-file encountered"); diff --git a/client/core/sshclient.cpp b/client/core/sshclient.cpp index 0367dc1f..e8d021ad 100644 --- a/client/core/sshclient.cpp +++ b/client/core/sshclient.cpp @@ -10,6 +10,8 @@ const uint32_t S_IRWXU = 0644; #endif namespace libssh { + const QString libsshTimeoutError = "Timeout connecting to"; + std::function Client::m_passphraseCallback; Client::Client(QObject *parent) : QObject(parent) @@ -45,11 +47,20 @@ namespace libssh { ssh_options_set(m_session, SSH_OPTIONS_USER, hostUsername.c_str()); ssh_options_set(m_session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity); - int connectionResult = ssh_connect(m_session); + QFutureWatcher watcher; + QFuture future = QtConcurrent::run([this]() { + return ssh_connect(m_session); + }); + + QEventLoop wait; + connect(&watcher, &QFutureWatcher::finished, &wait, &QEventLoop::quit); + watcher.setFuture(future); + wait.exec(); + + int connectionResult = watcher.result(); if (connectionResult != SSH_OK) { - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + return fromLibsshErrorCode(); } std::string authUsername = credentials.userName.toStdString(); @@ -78,8 +89,8 @@ namespace libssh { ssh_key_free(privateKey); } if (authResult != SSH_OK) { - qDebug() << ssh_get_error(m_session); - ErrorCode errorCode = fromLibsshErrorCode(ssh_get_error_code(m_session)); + qCritical() << ssh_get_error(m_session); + ErrorCode errorCode = fromLibsshErrorCode(); if (errorCode == ErrorCode::NoError) { errorCode = ErrorCode::SshPrivateKeyFormatError; } @@ -88,8 +99,7 @@ namespace libssh { } else { authResult = ssh_userauth_password(m_session, authUsername.c_str(), credentials.secretData.toStdString().c_str()); if (authResult != SSH_OK) { - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + return fromLibsshErrorCode(); } } } @@ -188,16 +198,15 @@ namespace libssh { ErrorCode Client::writeResponse(const QString &data) { if (m_channel == nullptr) { - qDebug() << "ssh channel not initialized"; - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + qCritical() << "ssh channel not initialized"; + return fromLibsshErrorCode(); } int bytesWritten = ssh_channel_write(m_channel, data.toUtf8(), (uint32_t)data.size()); if (bytesWritten == data.size() && ssh_channel_write(m_channel, "\n", 1)) { - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + return fromLibsshErrorCode(); } - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + return fromLibsshErrorCode(); } ErrorCode Client::closeChannel() @@ -212,8 +221,7 @@ namespace libssh { ssh_channel_free(m_channel); m_channel = nullptr; } - qDebug() << ssh_get_error(m_session); - return fromLibsshErrorCode(ssh_get_error_code(m_session)); + return fromLibsshErrorCode(); } ErrorCode Client::sftpFileCopy(const SftpOverwriteMode overwriteMode, const std::string& localPath, const std::string& remotePath, const std::string& fileDesc) @@ -312,12 +320,21 @@ namespace libssh { sftp_free(m_sftpSession); m_sftpSession = nullptr; } - qDebug() << ssh_get_error(m_session); + qCritical() << ssh_get_error(m_session); return errorCode; } - ErrorCode Client::fromLibsshErrorCode(int errorCode) + ErrorCode Client::fromLibsshErrorCode() { + int errorCode = ssh_get_error_code(m_session); + if (errorCode != SSH_NO_ERROR) { + QString errorMessage = ssh_get_error(m_session); + qCritical() << errorMessage; + if (errorMessage.contains(libsshTimeoutError)) { + return ErrorCode::SshTimeoutError; + } + } + switch (errorCode) { case(SSH_NO_ERROR): return ErrorCode::NoError; case(SSH_REQUEST_DENIED): return ErrorCode::SshRequsetDeniedError; diff --git a/client/core/sshclient.h b/client/core/sshclient.h index db22d0dd..4e08faaa 100644 --- a/client/core/sshclient.h +++ b/client/core/sshclient.h @@ -40,7 +40,7 @@ namespace libssh { private: ErrorCode closeChannel(); ErrorCode closeSftpSession(); - ErrorCode fromLibsshErrorCode(int errorCode); + ErrorCode fromLibsshErrorCode(); ErrorCode fromLibsshSftpErrorCode(int errorCode); static int callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata); diff --git a/client/ui/controllers/connectionController.h b/client/ui/controllers/connectionController.h index 93ee28a7..c1e81ea3 100644 --- a/client/ui/controllers/connectionController.h +++ b/client/ui/controllers/connectionController.h @@ -19,13 +19,14 @@ public: QObject *parent = nullptr); bool isConnected(); - void setIsConnected(bool isConnected); + void setIsConnected(bool isConnected); //todo take state from vpnconnection? public slots: void openConnection(); void closeConnection(); QString getLastConnectionError(); + Vpn::ConnectionState connectionState(){return {};}; //todo update ConnectButton text on page change signals: void connectToVpn(int serverIndex, const ServerCredentials &credentials, DockerContainer container, const QJsonObject &containerConfig); diff --git a/client/ui/qml/Controls2/BasicButtonType.qml b/client/ui/qml/Controls2/BasicButtonType.qml index 266beefe..05074fa9 100644 --- a/client/ui/qml/Controls2/BasicButtonType.qml +++ b/client/ui/qml/Controls2/BasicButtonType.qml @@ -24,10 +24,10 @@ Button { radius: 16 color: { if (root.enabled) { - if(root.pressed) { + if (root.pressed) { return pressedColor } - return hovered ? hoveredColor : defaultColor + return root.hovered ? hoveredColor : defaultColor } else { return disabledColor } diff --git a/client/ui/qml/Controls2/PopupType.qml b/client/ui/qml/Controls2/PopupType.qml index dd92d5fe..61bdfd18 100644 --- a/client/ui/qml/Controls2/PopupType.qml +++ b/client/ui/qml/Controls2/PopupType.qml @@ -27,15 +27,17 @@ Popup { background: Rectangle { anchors.fill: parent - color: Qt.rgba(215/255, 216/255, 219/255, 0.95) + color: "white"//Qt.rgba(215/255, 216/255, 219/255, 0.95) radius: 4 } contentItem: RowLayout { - width: parent.width + anchors.fill: parent + anchors.leftMargin: 16 + anchors.rightMargin: 16 CaptionTextType { - horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignLeft Layout.fillWidth: true text: root.popupErrorMessageText @@ -44,7 +46,7 @@ Popup { BasicButtonType { visible: closeButtonVisible - defaultColor: Qt.rgba(215/255, 216/255, 219/255, 0.95) + defaultColor: "white"//"transparent"//Qt.rgba(215/255, 216/255, 219/255, 0.95) hoveredColor: "#C1C2C5" pressedColor: "#AEB0B7" disabledColor: "#494B50" @@ -52,7 +54,7 @@ Popup { textColor: "#0E0E11" borderWidth: 0 - text: "Close" + text: qsTr("Close") onClicked: { root.close() } diff --git a/client/ui/qml/Pages2/PageSettingsServerData.qml b/client/ui/qml/Pages2/PageSettingsServerData.qml index c1f84cb0..fe09ed01 100644 --- a/client/ui/qml/Pages2/PageSettingsServerData.qml +++ b/client/ui/qml/Pages2/PageSettingsServerData.qml @@ -74,6 +74,7 @@ PageType { PageController.replaceStartPage() } else { goToStartPage() + goToPage(PageEnum.PageSettingsServersList) } } questionDrawer.noButtonFunction = function() { diff --git a/client/ui/qml/main2.qml b/client/ui/qml/main2.qml index 44a85925..0be8e368 100644 --- a/client/ui/qml/main2.qml +++ b/client/ui/qml/main2.qml @@ -16,6 +16,9 @@ Window { minimumWidth: GC.isDesktop() ? 360 : 0 minimumHeight: GC.isDesktop() ? 640 : 0 + color: "#0E0E11" + + // todo onClosing: function() { console.debug("QML onClosing signal") UiLogic.onCloseWindow() @@ -23,11 +26,6 @@ Window { title: "AmneziaVPN" - Rectangle { - anchors.fill: parent - color: "#0E0E11" - } - StackViewType { id: rootStackView diff --git a/client/vpnconnection.cpp b/client/vpnconnection.cpp index 468a6d96..1f0902fb 100644 --- a/client/vpnconnection.cpp +++ b/client/vpnconnection.cpp @@ -336,7 +336,7 @@ void VpnConnection::connectToVpn(int serverIndex, ErrorCode e = ErrorCode::NoError; - m_vpnConfiguration = createVpnConfiguration(serverIndex, credentials, container, containerConfig); + m_vpnConfiguration = createVpnConfiguration(serverIndex, credentials, container, containerConfig, &e); if (e) { emit connectionStateChanged(Vpn::ConnectionState::Error); return; @@ -345,7 +345,7 @@ void VpnConnection::connectToVpn(int serverIndex, #if !defined (Q_OS_ANDROID) && !defined (Q_OS_IOS) m_vpnProtocol.reset(VpnProtocol::factory(container, m_vpnConfiguration)); if (!m_vpnProtocol) { - emit Vpn::ConnectionState::Error; + emit connectionStateChanged(Vpn::ConnectionState::Error); return; } m_vpnProtocol->prepare(); @@ -371,7 +371,7 @@ void VpnConnection::connectToVpn(int serverIndex, createProtocolConnections(); e = m_vpnProtocol.data()->start(); - if (e) emit Vpn::ConnectionState::Error; + if (e) emit connectionStateChanged(Vpn::ConnectionState::Error); } void VpnConnection::createProtocolConnections() {