Replace sftp with scp (#602)

Replace sftp with scp
This commit is contained in:
dimov96 2024-03-06 02:24:28 +01:00 committed by GitHub
parent 840c388ab9
commit 6b6a76d2cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 98 additions and 141 deletions

View file

@ -76,7 +76,7 @@ OpenVpnConfigurator::ConnectionData OpenVpnConfigurator::prepareOpenVpnConfig(co
if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) { if (connData.caCert.isEmpty() || connData.clientCert.isEmpty() || connData.taKey.isEmpty()) {
if (errorCode) if (errorCode)
*errorCode = ErrorCode::SshSftpFailureError; *errorCode = ErrorCode::SshScpFailureError;
} }
return connData; return connData;

View file

@ -159,7 +159,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
.arg(connData.clientPubKey, connData.pskKey, connData.clientIP); .arg(connData.clientPubKey, connData.pskKey, connData.clientIP);
e = serverController.uploadTextFileToContainer(container, credentials, configPart, m_serverConfigPath, e = serverController.uploadTextFileToContainer(container, credentials, configPart, m_serverConfigPath,
libssh::SftpOverwriteMode::SftpAppendToExisting); libssh::ScpOverwriteMode::ScpAppendToExisting);
if (e) { if (e) {
if (errorCode) if (errorCode)

View file

@ -118,7 +118,7 @@ ServerController::runContainerScript(const ServerCredentials &credentials, Docke
ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials, ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container, const ServerCredentials &credentials,
const QString &file, const QString &path, const QString &file, const QString &path,
libssh::SftpOverwriteMode overwriteMode) libssh::ScpOverwriteMode overwriteMode)
{ {
ErrorCode e = ErrorCode::NoError; ErrorCode e = ErrorCode::NoError;
QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16)); QString tmpFileName = QString("/tmp/%1.tmp").arg(Utils::getRandomString(16));
@ -139,7 +139,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
if (e) if (e)
return e; return e;
if (overwriteMode == libssh::SftpOverwriteMode::SftpOverwriteExisting) { if (overwriteMode == libssh::ScpOverwriteMode::ScpOverwriteExisting) {
e = runScript(credentials, e = runScript(credentials,
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path), replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(path),
genVarsForScript(credentials, container)), genVarsForScript(credentials, container)),
@ -147,7 +147,7 @@ ErrorCode ServerController::uploadTextFileToContainer(DockerContainer container,
if (e) if (e)
return e; return e;
} else if (overwriteMode == libssh::SftpOverwriteMode::SftpAppendToExisting) { } else if (overwriteMode == libssh::ScpOverwriteMode::ScpAppendToExisting) {
e = runScript(credentials, e = runScript(credentials,
replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName), replaceVars(QString("sudo docker cp %1 $CONTAINER_NAME:/%2").arg(tmpFileName).arg(tmpFileName),
genVarsForScript(credentials, container)), genVarsForScript(credentials, container)),
@ -199,7 +199,7 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
} }
ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data,
const QString &remotePath, libssh::SftpOverwriteMode overwriteMode) const QString &remotePath, libssh::ScpOverwriteMode overwriteMode)
{ {
auto error = m_sshClient.connectToHost(credentials); auto error = m_sshClient.connectToHost(credentials);
if (error != ErrorCode::NoError) { if (error != ErrorCode::NoError) {
@ -211,7 +211,7 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
localFile.write(data); localFile.write(data);
localFile.close(); localFile.close();
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc"); error = m_sshClient.scpFileCopy(overwriteMode, localFile.fileName(), remotePath, "non_desc");
if (error != ErrorCode::NoError) { if (error != ErrorCode::NoError) {
return error; return error;

View file

@ -38,7 +38,7 @@ public:
ErrorCode uploadTextFileToContainer( ErrorCode uploadTextFileToContainer(
DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path, DockerContainer container, const ServerCredentials &credentials, const QString &file, const QString &path,
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting); libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials, QByteArray getTextFileFromContainer(DockerContainer container, const ServerCredentials &credentials,
const QString &path, ErrorCode *errorCode = nullptr); const QString &path, ErrorCode *errorCode = nullptr);
@ -80,7 +80,7 @@ private:
ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container); ErrorCode isServerDpkgBusy(const ServerCredentials &credentials, DockerContainer container);
ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath,
libssh::SftpOverwriteMode overwriteMode = libssh::SftpOverwriteMode::SftpOverwriteExisting); libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting);
ErrorCode setupServerFirewall(const ServerCredentials &credentials); ErrorCode setupServerFirewall(const ServerCredentials &credentials);

View file

@ -46,25 +46,12 @@ namespace amnezia
SshPrivateKeyFormatError = 304, SshPrivateKeyFormatError = 304,
SshTimeoutError = 305, SshTimeoutError = 305,
// Ssh sftp errors // Ssh scp errors
SshSftpEofError = 400, SshScpFailureError = 400,
SshSftpNoSuchFileError = 401,
SshSftpPermissionDeniedError = 402,
SshSftpFailureError = 403,
SshSftpBadMessageError = 404,
SshSftpNoConnectionError = 405,
SshSftpConnectionLostError = 406,
SshSftpOpUnsupportedError = 407,
SshSftpInvalidHandleError = 408,
SshSftpNoSuchPathError = 409,
SshSftpFileAlreadyExistsError = 410,
SshSftpWriteProtectError = 411,
SshSftpNoMediaError = 412,
// Local errors // Local errors
OpenVpnConfigMissing = 500, OpenVpnConfigMissing = 500,
OpenVpnManagementServerError = 501, OpenVpnManagementServerError = 501,
ConfigMissing = 502,
// Distro errors // Distro errors
OpenVpnExecutableMissing = 600, OpenVpnExecutableMissing = 600,
@ -92,7 +79,15 @@ namespace amnezia
// Api errors // Api errors
ApiConfigDownloadError = 1100, ApiConfigDownloadError = 1100,
ApiConfigAlreadyAdded = 1101 ApiConfigAlreadyAdded = 1101,
// QFile errors
OpenError = 1200,
ReadError = 1201,
PermissionsError = 1202,
UnspecifiedError = 1203,
FatalError = 1204,
AbortError = 1205
}; };
} // namespace amnezia } // namespace amnezia

View file

@ -28,20 +28,8 @@ QString errorString(ErrorCode code) {
case(SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break; case(SshPrivateKeyFormatError): errorMessage = QObject::tr("The selected private key format is not supported, use openssh ED25519 key types or PEM key types"); break;
case(SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break; case(SshTimeoutError): errorMessage = QObject::tr("Timeout connecting to server"); break;
// Libssh sftp errors // Ssh scp errors
case(SshSftpEofError): errorMessage = QObject::tr("Sftp error: End-of-file encountered"); break; case(SshScpFailureError): errorMessage = QObject::tr("Scp error: Generic failure"); break;
case(SshSftpNoSuchFileError): errorMessage = QObject::tr("Sftp error: File does not exist"); break;
case(SshSftpPermissionDeniedError): errorMessage = QObject::tr("Sftp error: Permission denied"); break;
case(SshSftpFailureError): errorMessage = QObject::tr("Sftp error: Generic failure"); break;
case(SshSftpBadMessageError): errorMessage = QObject::tr("Sftp error: Garbage received from server"); break;
case(SshSftpNoConnectionError): errorMessage = QObject::tr("Sftp error: No connection has been set up"); break;
case(SshSftpConnectionLostError): errorMessage = QObject::tr("Sftp error: There was a connection, but we lost it"); break;
case(SshSftpOpUnsupportedError): errorMessage = QObject::tr("Sftp error: Operation not supported by libssh yet"); break;
case(SshSftpInvalidHandleError): errorMessage = QObject::tr("Sftp error: Invalid file handle"); break;
case(SshSftpNoSuchPathError): errorMessage = QObject::tr("Sftp error: No such file or directory path exists"); break;
case(SshSftpFileAlreadyExistsError): errorMessage = QObject::tr("Sftp error: An attempt to create an already existing file or directory has been made"); break;
case(SshSftpWriteProtectError): errorMessage = QObject::tr("Sftp error: Write-protected filesystem"); break;
case(SshSftpNoMediaError): errorMessage = QObject::tr("Sftp error: No media was in remote drive"); break;
// Local errors // Local errors
case (OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break; case (OpenVpnConfigMissing): errorMessage = QObject::tr("OpenVPN config missing"); break;
@ -68,6 +56,14 @@ QString errorString(ErrorCode code) {
case (ApiConfigDownloadError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break; case (ApiConfigDownloadError): errorMessage = QObject::tr("Error when retrieving configuration from API"); break;
case (ApiConfigAlreadyAdded): errorMessage = QObject::tr("This config has already been added to the application"); break; case (ApiConfigAlreadyAdded): errorMessage = QObject::tr("This config has already been added to the application"); break;
// QFile errors
case(OpenError): errorMessage = QObject::tr("QFile error: The file could not be opened"); break;
case(ReadError): errorMessage = QObject::tr("QFile error: An error occurred when reading from the file"); break;
case(PermissionsError): errorMessage = QObject::tr("QFile error: The file could not be accessed"); break;
case(UnspecifiedError): errorMessage = QObject::tr("QFile error: An unspecified error occurred"); break;
case(FatalError): errorMessage = QObject::tr("QFile error: A fatal error occurred"); break;
case(AbortError): errorMessage = QObject::tr("QFile error: The operation was aborted"); break;
case(InternalError): case(InternalError):
default: default:
errorMessage = QObject::tr("Internal error"); break; errorMessage = QObject::tr("Internal error"); break;

View file

@ -10,16 +10,10 @@ const uint32_t S_IRWXU = 0644;
#endif #endif
namespace libssh { namespace libssh {
const QString libsshTimeoutError = "Timeout connecting to"; constexpr auto libsshTimeoutError{"Timeout connecting to"};
std::function<QString()> Client::m_passphraseCallback; std::function<QString()> Client::m_passphraseCallback;
Client::Client(QObject *parent) : QObject(parent)
{ }
Client::~Client()
{ }
int Client::callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata) int Client::callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata)
{ {
auto passphrase = m_passphraseCallback(); auto passphrase = m_passphraseCallback();
@ -171,13 +165,13 @@ namespace libssh {
return ErrorCode::NoError; return ErrorCode::NoError;
}; };
auto error = readOutput(false); auto errorCode = readOutput(false);
if (error != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return error; return errorCode;
} }
error = readOutput(true); errorCode = readOutput(true);
if (error != ErrorCode::NoError) { if (errorCode != ErrorCode::NoError) {
return error; return errorCode;
} }
} else { } else {
return closeChannel(); return closeChannel();
@ -222,100 +216,79 @@ namespace libssh {
return fromLibsshErrorCode(); return fromLibsshErrorCode();
} }
ErrorCode Client::sftpFileCopy(const SftpOverwriteMode overwriteMode, const QString& localPath, const QString& remotePath, const QString &fileDesc) ErrorCode Client::scpFileCopy(const ScpOverwriteMode overwriteMode, const QString& localPath, const QString& remotePath, const QString &fileDesc)
{ {
m_sftpSession = sftp_new(m_session); m_scpSession = ssh_scp_new(m_session, SSH_SCP_WRITE, remotePath.toStdString().c_str());
if (m_sftpSession == nullptr) { if (m_scpSession == nullptr) {
return closeSftpSession(); return fromLibsshErrorCode();
} }
int result = sftp_init(m_sftpSession); if (ssh_scp_init(m_scpSession) != SSH_OK) {
auto errorCode = fromLibsshErrorCode();
if (result != SSH_OK) { closeScpSession();
return closeSftpSession(); return errorCode;
} }
QFutureWatcher<ErrorCode> watcher; QFutureWatcher<ErrorCode> watcher;
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Client::sftpFileCopyFinished); connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &Client::scpFileCopyFinished);
QFuture<ErrorCode> future = QtConcurrent::run([this, overwriteMode, &localPath, &remotePath, &fileDesc]() { QFuture<ErrorCode> future = QtConcurrent::run([this, overwriteMode, &localPath, &remotePath, &fileDesc]() {
int accessType = O_WRONLY | O_CREAT | overwriteMode; const int accessType = O_WRONLY | O_CREAT | overwriteMode;
sftp_file file; const int localFileSize = QFileInfo(localPath).size();
const size_t bufferSize = 16384;
char buffer[bufferSize];
file = sftp_open(m_sftpSession, remotePath.toStdString().c_str(), accessType, S_IRWXU); int result = ssh_scp_push_file(m_scpSession, remotePath.toStdString().c_str(), localFileSize, accessType);
if (result != SSH_OK) {
if (file == nullptr) { return fromLibsshErrorCode();
return closeSftpSession();
} }
int localFileSize = QFileInfo(localPath).size();
int chunksCount = localFileSize / (bufferSize);
QFile fin(localPath); QFile fin(localPath);
if (fin.open(QIODevice::ReadOnly)) { if (fin.open(QIODevice::ReadOnly)) {
for (int currentChunkId = 0; currentChunkId < chunksCount; currentChunkId++) { constexpr size_t bufferSize = 16384;
QByteArray chunk = fin.read(bufferSize); int transferred = 0;
if (chunk.size() != bufferSize) return ErrorCode::SshSftpEofError; int currentChunkSize = bufferSize;
int bytesWritten = sftp_write(file, chunk.data(), chunk.size()); while (transferred < localFileSize) {
if (bytesWritten != chunk.size()) { // Last Chunk
fin.close(); if ((localFileSize - transferred) < bufferSize) {
sftp_close(file); currentChunkSize = localFileSize % bufferSize;
return closeSftpSession();
}
} }
int lastChunkSize = localFileSize % bufferSize; QByteArray chunk = fin.read(currentChunkSize);
if (chunk.size() != currentChunkSize) {
if (lastChunkSize != 0) { return fromFileErrorCode(fin.error());
QByteArray lastChunk = fin.read(lastChunkSize);
if (lastChunk.size() != lastChunkSize) return ErrorCode::SshSftpEofError;
int bytesWritten = sftp_write(file, lastChunk.data(), lastChunkSize);
if (bytesWritten != lastChunkSize) {
fin.close();
sftp_close(file);
return closeSftpSession();
} }
result = ssh_scp_write(m_scpSession, chunk.data(), chunk.size());
if (result != SSH_OK) {
return fromLibsshErrorCode();
}
transferred += currentChunkSize;
} }
} else { } else {
sftp_close(file); return fromFileErrorCode(fin.error());
return closeSftpSession();
} }
fin.close(); return ErrorCode::NoError;
int result = sftp_close(file);
if (result != SSH_OK) {
return closeSftpSession();
}
return closeSftpSession();
}); });
watcher.setFuture(future); watcher.setFuture(future);
QEventLoop wait; QEventLoop wait;
QObject::connect(this, &Client::sftpFileCopyFinished, &wait, &QEventLoop::quit); QObject::connect(this, &Client::scpFileCopyFinished, &wait, &QEventLoop::quit);
wait.exec(); wait.exec();
closeScpSession();
return watcher.result(); return watcher.result();
} }
ErrorCode Client::closeSftpSession() void Client::closeScpSession()
{ {
auto errorCode = fromLibsshSftpErrorCode(sftp_get_error(m_sftpSession)); if (m_scpSession != nullptr) {
if (m_sftpSession != nullptr) { ssh_scp_free(m_scpSession);
sftp_free(m_sftpSession); m_scpSession = nullptr;
m_sftpSession = nullptr;
} }
qCritical() << ssh_get_error(m_session);
return errorCode;
} }
ErrorCode Client::fromLibsshErrorCode() ErrorCode Client::fromLibsshErrorCode()
@ -337,24 +310,17 @@ namespace libssh {
default: return ErrorCode::SshInternalError; default: return ErrorCode::SshInternalError;
} }
} }
ErrorCode Client::fromLibsshSftpErrorCode(int errorCode)
ErrorCode Client::fromFileErrorCode(QFileDevice::FileError fileError)
{ {
switch (errorCode) { switch (fileError) {
case(SSH_FX_OK): return ErrorCode::NoError; case QFileDevice::NoError: return ErrorCode::NoError;
case(SSH_FX_EOF): return ErrorCode::SshSftpEofError; case QFileDevice::ReadError: return ErrorCode::ReadError;
case(SSH_FX_NO_SUCH_FILE): return ErrorCode::SshSftpNoSuchFileError; case QFileDevice::OpenError: return ErrorCode::OpenError;
case(SSH_FX_PERMISSION_DENIED): return ErrorCode::SshSftpPermissionDeniedError; case QFileDevice::PermissionsError: return ErrorCode::PermissionsError;
case(SSH_FX_FAILURE): return ErrorCode::SshSftpFailureError; case QFileDevice::FatalError: return ErrorCode::FatalError;
case(SSH_FX_BAD_MESSAGE): return ErrorCode::SshSftpBadMessageError; case QFileDevice::AbortError: return ErrorCode::AbortError;
case(SSH_FX_NO_CONNECTION): return ErrorCode::SshSftpNoConnectionError; default: return ErrorCode::UnspecifiedError;
case(SSH_FX_CONNECTION_LOST): return ErrorCode::SshSftpConnectionLostError;
case(SSH_FX_OP_UNSUPPORTED): return ErrorCode::SshSftpOpUnsupportedError;
case(SSH_FX_INVALID_HANDLE): return ErrorCode::SshSftpInvalidHandleError;
case(SSH_FX_NO_SUCH_PATH): return ErrorCode::SshSftpNoSuchPathError;
case(SSH_FX_FILE_ALREADY_EXISTS): return ErrorCode::SshSftpFileAlreadyExistsError;
case(SSH_FX_WRITE_PROTECT): return ErrorCode::SshSftpWriteProtectError;
case(SSH_FX_NO_MEDIA): return ErrorCode::SshSftpNoMediaError;
default: return ErrorCode::SshSftpFailureError;
} }
} }

View file

@ -2,29 +2,29 @@
#define SSHCLIENT_H #define SSHCLIENT_H
#include <QObject> #include <QObject>
#include <QFile>
#include <fcntl.h> #include <fcntl.h>
#include <libssh/libssh.h> #include <libssh/libssh.h>
#include <libssh/sftp.h>
#include "defs.h" #include "defs.h"
using namespace amnezia; using namespace amnezia;
namespace libssh { namespace libssh {
enum SftpOverwriteMode { enum ScpOverwriteMode {
/*! Overwrite any existing files */ /*! Overwrite any existing files */
SftpOverwriteExisting = O_TRUNC, ScpOverwriteExisting = O_TRUNC,
/*! Append new content if the file already exists */ /*! Append new content if the file already exists */
SftpAppendToExisting = O_APPEND ScpAppendToExisting = O_APPEND
}; };
class Client : public QObject class Client : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
Client(QObject *parent = nullptr); Client() = default;
~Client(); ~Client() = default;
ErrorCode connectToHost(const ServerCredentials &credentials); ErrorCode connectToHost(const ServerCredentials &credentials);
void disconnectFromHost(); void disconnectFromHost();
@ -32,26 +32,26 @@ namespace libssh {
const std::function<ErrorCode (const QString &, Client &)> &cbReadStdOut, const std::function<ErrorCode (const QString &, Client &)> &cbReadStdOut,
const std::function<ErrorCode (const QString &, Client &)> &cbReadStdErr); const std::function<ErrorCode (const QString &, Client &)> &cbReadStdErr);
ErrorCode writeResponse(const QString &data); ErrorCode writeResponse(const QString &data);
ErrorCode sftpFileCopy(const SftpOverwriteMode overwriteMode, ErrorCode scpFileCopy(const ScpOverwriteMode overwriteMode,
const QString &localPath, const QString &localPath,
const QString &remotePath, const QString &remotePath,
const QString &fileDesc); const QString &fileDesc);
ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &passphraseCallback); ErrorCode getDecryptedPrivateKey(const ServerCredentials &credentials, QString &decryptedPrivateKey, const std::function<QString()> &passphraseCallback);
private: private:
ErrorCode closeChannel(); ErrorCode closeChannel();
ErrorCode closeSftpSession(); void closeScpSession();
ErrorCode fromLibsshErrorCode(); ErrorCode fromLibsshErrorCode();
ErrorCode fromLibsshSftpErrorCode(int errorCode); ErrorCode fromFileErrorCode(QFileDevice::FileError fileError);
static int callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata); static int callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata);
ssh_session m_session = nullptr; ssh_session m_session = nullptr;
ssh_channel m_channel = nullptr; ssh_channel m_channel = nullptr;
sftp_session m_sftpSession = nullptr; ssh_scp m_scpSession = nullptr;
static std::function<QString()> m_passphraseCallback; static std::function<QString()> m_passphraseCallback;
signals: signals:
void writeToChannelFinished(); void writeToChannelFinished();
void sftpFileCopyFinished(); void scpFileCopyFinished();
}; };
} }