added class to open ssh connection using libssh and write data to ssh channel
This commit is contained in:
parent
e481bd4ec5
commit
c8085a368f
7 changed files with 271 additions and 137 deletions
|
@ -21,13 +21,13 @@ set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "Autogen")
|
||||||
find_package(Qt6 REQUIRED COMPONENTS
|
find_package(Qt6 REQUIRED COMPONENTS
|
||||||
Widgets Core Gui Network Xml
|
Widgets Core Gui Network Xml
|
||||||
RemoteObjects Quick Svg QuickControls2
|
RemoteObjects Quick Svg QuickControls2
|
||||||
Core5Compat
|
Core5Compat Concurrent
|
||||||
)
|
)
|
||||||
set(LIBS ${LIBS}
|
set(LIBS ${LIBS}
|
||||||
Qt6::Widgets Qt6::Core Qt6::Gui
|
Qt6::Widgets Qt6::Core Qt6::Gui
|
||||||
Qt6::Network Qt6::Xml Qt6::RemoteObjects
|
Qt6::Network Qt6::Xml Qt6::RemoteObjects
|
||||||
Qt6::Quick Qt6::Svg Qt6::QuickControls2
|
Qt6::Quick Qt6::Svg Qt6::QuickControls2
|
||||||
Qt6::Core5Compat
|
Qt6::Core5Compat Qt6::Concurrent
|
||||||
)
|
)
|
||||||
|
|
||||||
qt_standard_project_setup()
|
qt_standard_project_setup()
|
||||||
|
@ -71,6 +71,8 @@ set(HEADERS ${HEADERS}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/uilogic.h
|
${CMAKE_CURRENT_LIST_DIR}/ui/uilogic.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
|
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.h
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
|
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/sshsession.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(NOT IOS)
|
||||||
|
@ -93,6 +95,8 @@ set(SOURCES ${SOURCES}
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/uilogic.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/uilogic.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
${CMAKE_CURRENT_LIST_DIR}/ui/qautostart.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
|
${CMAKE_CURRENT_LIST_DIR}/protocols/vpnprotocol.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/core/sshsession.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(NOT IOS)
|
if(NOT IOS)
|
||||||
|
|
|
@ -43,12 +43,10 @@ using namespace QSsh;
|
||||||
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) :
|
ServerController::ServerController(std::shared_ptr<Settings> settings, QObject *parent) :
|
||||||
m_settings(settings)
|
m_settings(settings)
|
||||||
{
|
{
|
||||||
ssh_init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerController::~ServerController()
|
ServerController::~ServerController()
|
||||||
{
|
{
|
||||||
ssh_finalize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorCode ServerController::connectToHost(const ServerCredentials &credentials, ssh_session &session) {
|
ErrorCode ServerController::connectToHost(const ServerCredentials &credentials, ssh_session &session) {
|
||||||
|
@ -58,14 +56,14 @@ ErrorCode ServerController::connectToHost(const ServerCredentials &credentials,
|
||||||
}
|
}
|
||||||
|
|
||||||
int port = credentials.port;
|
int port = credentials.port;
|
||||||
int log_verbosity = SSH_LOG_NOLOG;
|
int logVerbosity = SSH_LOG_NOLOG;
|
||||||
std::string host_ip = credentials.hostName.toStdString();
|
std::string hostIp = credentials.hostName.toStdString();
|
||||||
std::string host_username = credentials.userName.toStdString() + "@" + host_ip;
|
std::string hostUsername = credentials.userName.toStdString() + "@" + hostIp;
|
||||||
|
|
||||||
ssh_options_set(session, SSH_OPTIONS_HOST, host_ip.c_str());
|
ssh_options_set(session, SSH_OPTIONS_HOST, hostIp.c_str());
|
||||||
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
|
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
|
||||||
ssh_options_set(session, SSH_OPTIONS_USER, host_username.c_str());
|
ssh_options_set(session, SSH_OPTIONS_USER, hostUsername.c_str());
|
||||||
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &log_verbosity);
|
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &logVerbosity);
|
||||||
|
|
||||||
int connection_result = ssh_connect(session);
|
int connection_result = ssh_connect(session);
|
||||||
|
|
||||||
|
@ -82,7 +80,7 @@ ErrorCode ServerController::connectToHost(const ServerCredentials &credentials,
|
||||||
auth_result = ssh_userauth_publickey(session, auth_username.c_str(), priv_key);
|
auth_result = ssh_userauth_publickey(session, auth_username.c_str(), priv_key);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
auth_result = ssh_userauth_password(session, auth_username.c_str(), credentials.password.toStdString().c_str());
|
auth_result = ssh_userauth_password(session, auth_username.c_str(), credentials.password.toStdString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (auth_result != SSH_OK) {
|
if (auth_result != SSH_OK) {
|
||||||
|
@ -98,67 +96,38 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdOut,
|
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdOut,
|
||||||
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr) {
|
const std::function<void(const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr) {
|
||||||
|
|
||||||
ssh_session ssh = ssh_new();
|
std::shared_ptr<SshSession> session = m_sshClient.getSession();
|
||||||
|
if (!session) {
|
||||||
ErrorCode e = connectToHost(credentials, ssh);
|
return ErrorCode::SshInternalError;
|
||||||
if (e) {
|
|
||||||
ssh_free(ssh);
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
ssh_channel channel = ssh_channel_new(ssh);
|
auto error = session->initChannel(credentials);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
if (channel == NULL) {
|
return error;
|
||||||
ssh_disconnect(ssh);
|
|
||||||
ssh_free(ssh);
|
|
||||||
return ErrorCode::SshAuthenticationError;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssh_channel_open_session(channel);
|
|
||||||
|
|
||||||
if (ssh_channel_is_open(channel)) {
|
|
||||||
qDebug() << "SSH chanel opened";
|
|
||||||
} else {
|
|
||||||
ssh_channel_free(channel);
|
|
||||||
ssh_disconnect(ssh);
|
|
||||||
ssh_free(ssh);
|
|
||||||
return ErrorCode::SshAuthenticationError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
script.replace("\r", "");
|
script.replace("\r", "");
|
||||||
|
|
||||||
qDebug() << "Run script " << script;
|
qDebug() << "Run script " << script;
|
||||||
|
|
||||||
int exec_result = ssh_channel_request_pty(channel);
|
|
||||||
|
|
||||||
ssh_channel_change_pty_size(channel, 80, 1024);
|
|
||||||
ssh_channel_request_shell(channel);
|
|
||||||
|
|
||||||
QString totalLine;
|
QString totalLine;
|
||||||
const QStringList &lines = script.split("\n", Qt::SkipEmptyParts);
|
const QStringList &lines = script.split("\n", Qt::SkipEmptyParts);
|
||||||
for (int i = 0; i < lines.count(); i++) {
|
for (int i = 0; i < lines.count(); i++) {
|
||||||
QString currentLine = lines.at(i);
|
QString currentLine = lines.at(i);
|
||||||
QString nextLine;
|
|
||||||
if (i + 1 < lines.count()) nextLine = lines.at(i+1);
|
|
||||||
|
|
||||||
if (totalLine.isEmpty()) {
|
if (totalLine.isEmpty()) {
|
||||||
totalLine = currentLine;
|
totalLine = currentLine;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
totalLine = totalLine + "\n" + currentLine;
|
totalLine = totalLine + "\n" + currentLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString lineToExec;
|
QString lineToExec;
|
||||||
if (currentLine.endsWith("\\")) {
|
if (currentLine.endsWith("\\")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
lineToExec = totalLine;
|
lineToExec = totalLine;
|
||||||
totalLine.clear();
|
totalLine.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug()<<"total line " << totalLine;
|
|
||||||
|
|
||||||
// Run collected line
|
|
||||||
if (lineToExec.startsWith("#")) {
|
if (lineToExec.startsWith("#")) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -167,43 +136,14 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
||||||
|
|
||||||
qDebug().noquote() << "EXEC" << lineToExec;
|
qDebug().noquote() << "EXEC" << lineToExec;
|
||||||
Debug::appendSshLog("Run command:" + lineToExec);
|
Debug::appendSshLog("Run command:" + lineToExec);
|
||||||
int nwritten, nbytes = 0, tryes = 0;
|
|
||||||
char buffer[2048];
|
|
||||||
if (ssh_channel_write(channel, lineToExec.toUtf8(), (uint32_t)lineToExec.size()) == lineToExec.size() &&
|
|
||||||
ssh_channel_write(channel, "\n", 1) == 1){
|
|
||||||
while (nbytes !=0 || tryes < 100){
|
|
||||||
if (ssh_channel_is_open(channel) && !ssh_channel_is_eof(channel)) {
|
|
||||||
//nbytes = ssh_channel_read_nonblocking(channel, buffer, sizeof(buffer), 0);
|
|
||||||
|
|
||||||
nbytes = ssh_channel_read_timeout(channel, buffer, sizeof(buffer), 0, 50);
|
error = session->writeToChannel(lineToExec);
|
||||||
|
if (error != ErrorCode::NoError) {
|
||||||
if (nbytes > 0) {
|
return error;
|
||||||
tryes = 0;
|
|
||||||
std::string strbuf;
|
|
||||||
strbuf.assign(buffer, nbytes);
|
|
||||||
QByteArray qbuff(buffer, nbytes);
|
|
||||||
QString outp(qbuff);
|
|
||||||
|
|
||||||
if (cbReadStdOut){
|
|
||||||
cbReadStdOut(outp, nullptr);
|
|
||||||
}
|
|
||||||
qDebug().noquote() << QString(strbuf.c_str());
|
|
||||||
|
|
||||||
} else {
|
|
||||||
tryes++;
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ssh_channel_send_eof(channel);
|
qDebug() << "ServerController::runScript finished\n";
|
||||||
ssh_channel_free(channel);
|
|
||||||
ssh_disconnect(ssh);
|
|
||||||
ssh_free(ssh);
|
|
||||||
|
|
||||||
return ErrorCode::NoError;
|
return ErrorCode::NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +154,7 @@ ErrorCode ServerController::runContainerScript(const ServerCredentials &credenti
|
||||||
const std::function<void (const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr)
|
const std::function<void (const QString &, QSharedPointer<QSsh::SshRemoteProcess>)> &cbReadStdErr)
|
||||||
{
|
{
|
||||||
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
QString fileName = "/opt/amnezia/" + Utils::getRandomString(16) + ".sh";
|
||||||
Debug::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + ":\n" + script);
|
Debug::appendSshLog("Run container script for " + ContainerProps::containerToString(container) + QStringLiteral(":\n") + script);
|
||||||
|
|
||||||
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
ErrorCode e = uploadTextFileToContainer(container, credentials, script, fileName);
|
||||||
if (e) return e;
|
if (e) return e;
|
||||||
|
@ -888,60 +828,6 @@ QString ServerController::checkSshConnection(const ServerCredentials &credential
|
||||||
return stdOut;
|
return stdOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
SshConnection *ServerController::connectToHost(const SshConnectionParameters &sshParams)
|
|
||||||
{
|
|
||||||
SshConnection *client = acquireConnection(sshParams);
|
|
||||||
if (!client) return nullptr;
|
|
||||||
|
|
||||||
QEventLoop waitssh;
|
|
||||||
QObject::connect(client, &SshConnection::connected, &waitssh, [&]() {
|
|
||||||
qDebug() << "Server connected by ssh";
|
|
||||||
waitssh.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
QObject::connect(client, &SshConnection::disconnected, &waitssh, [&]() {
|
|
||||||
qDebug() << "Server disconnected by ssh";
|
|
||||||
waitssh.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
QObject::connect(client, &SshConnection::error, &waitssh, [&](QSsh::SshError error) {
|
|
||||||
qCritical() << "Ssh error:" << error << client->errorString();
|
|
||||||
waitssh.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// QObject::connect(client, &SshConnection::dataAvailable, [&](const QString &message) {
|
|
||||||
// qCritical() << "Ssh message:" << message;
|
|
||||||
// });
|
|
||||||
|
|
||||||
//qDebug() << "Connection state" << client->state();
|
|
||||||
|
|
||||||
if (client->state() == SshConnection::State::Unconnected) {
|
|
||||||
client->connectToHost();
|
|
||||||
waitssh.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// QObject::connect(&client, &SshClient::sshDataReceived, [&](){
|
|
||||||
// qDebug().noquote() << "Data received";
|
|
||||||
// });
|
|
||||||
|
|
||||||
|
|
||||||
// if(client.sshState() != SshClient::SshState::Ready) {
|
|
||||||
// qCritical() << "Can't connect to server";
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// qDebug() << "SSh connection established";
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// QObject::connect(proc, &SshProcess::finished, &wait, &QEventLoop::quit);
|
|
||||||
// QObject::connect(proc, &SshProcess::failed, &wait, &QEventLoop::quit);
|
|
||||||
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerController::disconnectFromHost(const ServerCredentials &credentials)
|
void ServerController::disconnectFromHost(const ServerCredentials &credentials)
|
||||||
{
|
{
|
||||||
SshConnection *client = acquireConnection(sshParams(credentials));
|
SshConnection *client = acquireConnection(sshParams(credentials));
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "containers/containers_defs.h"
|
#include "containers/containers_defs.h"
|
||||||
|
|
||||||
#include "sftpdefs.h"
|
#include "sftpdefs.h"
|
||||||
|
#include "sshclient.h"
|
||||||
|
|
||||||
class Settings;
|
class Settings;
|
||||||
class VpnConfigurator;
|
class VpnConfigurator;
|
||||||
|
@ -76,7 +77,6 @@ public:
|
||||||
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject());
|
Vars genVarsForScript(const ServerCredentials &credentials, DockerContainer container = DockerContainer::None, const QJsonObject &config = QJsonObject());
|
||||||
|
|
||||||
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
|
QString checkSshConnection(const ServerCredentials &credentials, ErrorCode *errorCode = nullptr);
|
||||||
QSsh::SshConnection *connectToHost(const QSsh::SshConnectionParameters &sshParams);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -92,6 +92,8 @@ private:
|
||||||
|
|
||||||
std::shared_ptr<Settings> m_settings;
|
std::shared_ptr<Settings> m_settings;
|
||||||
std::shared_ptr<VpnConfigurator> m_configurator;
|
std::shared_ptr<VpnConfigurator> m_configurator;
|
||||||
|
|
||||||
|
SshClient m_sshClient;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SERVERCONTROLLER_H
|
#endif // SERVERCONTROLLER_H
|
||||||
|
|
19
client/core/sshclient.cpp
Normal file
19
client/core/sshclient.cpp
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
#include "sshclient.h"
|
||||||
|
|
||||||
|
#include <libssh/libssh.h>
|
||||||
|
#include <libssh/sftp.h>
|
||||||
|
|
||||||
|
SshClient::SshClient(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
ssh_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
SshClient::~SshClient()
|
||||||
|
{
|
||||||
|
ssh_finalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<SshSession> SshClient::getSession()
|
||||||
|
{
|
||||||
|
return std::make_shared<SshSession>();
|
||||||
|
}
|
20
client/core/sshclient.h
Normal file
20
client/core/sshclient.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef SSHCLIENT_H
|
||||||
|
#define SSHCLIENT_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "sshsession.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
class SshClient : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SshClient(QObject *parent = nullptr);
|
||||||
|
~SshClient();
|
||||||
|
|
||||||
|
std::shared_ptr<SshSession> getSession();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SSHCLIENT_H
|
167
client/core/sshsession.cpp
Normal file
167
client/core/sshsession.cpp
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
#include "sshsession.h"
|
||||||
|
|
||||||
|
#include <QEventLoop>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
|
SshSession::SshSession(QObject *parent) : QObject(parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SshSession::~SshSession()
|
||||||
|
{
|
||||||
|
if (m_isNeedSendChannelEof) {
|
||||||
|
ssh_channel_send_eof(m_channel);
|
||||||
|
}
|
||||||
|
if (m_isChannelOpened) {
|
||||||
|
ssh_channel_free(m_channel);
|
||||||
|
}
|
||||||
|
if (m_isSessionConnected) {
|
||||||
|
ssh_disconnect(m_session);
|
||||||
|
}
|
||||||
|
ssh_free(m_session);
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode SshSession::connectToHost(const ServerCredentials &credentials)
|
||||||
|
{
|
||||||
|
if (m_session == NULL) {
|
||||||
|
return ErrorCode::InternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int port = credentials.port;
|
||||||
|
int logVerbosity = SSH_LOG_NOLOG;
|
||||||
|
std::string hostIp = credentials.hostName.toStdString();
|
||||||
|
std::string hostUsername = credentials.userName.toStdString() + "@" + hostIp;
|
||||||
|
|
||||||
|
ssh_options_set(m_session, SSH_OPTIONS_HOST, hostIp.c_str());
|
||||||
|
ssh_options_set(m_session, SSH_OPTIONS_PORT, &port);
|
||||||
|
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);
|
||||||
|
|
||||||
|
if (connectionResult != SSH_OK) {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return ErrorCode::SshTimeoutError;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_isSessionConnected = true;
|
||||||
|
|
||||||
|
std::string authUsername = credentials.userName.toStdString();
|
||||||
|
|
||||||
|
int authResult = SSH_ERROR;
|
||||||
|
if (credentials.password.contains("BEGIN") && credentials.password.contains("PRIVATE KEY")) {
|
||||||
|
ssh_key privateKey;
|
||||||
|
ssh_pki_import_privkey_base64(credentials.password.toStdString().c_str(), nullptr, nullptr, nullptr, &privateKey);
|
||||||
|
authResult = ssh_userauth_publickey(m_session, authUsername.c_str(), privateKey);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
authResult = ssh_userauth_password(m_session, authUsername.c_str(), credentials.password.toStdString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authResult != SSH_OK) {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return ErrorCode::SshAuthenticationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode SshSession::initChannel(const ServerCredentials &credentials)
|
||||||
|
{
|
||||||
|
m_session = ssh_new();
|
||||||
|
|
||||||
|
ErrorCode error = connectToHost(credentials);
|
||||||
|
if (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
m_channel = ssh_channel_new(m_session);
|
||||||
|
|
||||||
|
if (m_channel == NULL) {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return ErrorCode::SshAuthenticationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result = ssh_channel_open_session(m_channel);
|
||||||
|
|
||||||
|
if (result == SSH_OK && ssh_channel_is_open(m_channel)) {
|
||||||
|
qDebug() << "SSH chanel opened";
|
||||||
|
} else {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return ErrorCode::SshAuthenticationError;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ssh_channel_request_pty(m_channel);
|
||||||
|
if (result != SSH_OK) {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return ErrorCode::SshInternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ssh_channel_change_pty_size(m_channel, 80, 1024);
|
||||||
|
if (result != SSH_OK) {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return ErrorCode::SshInternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = ssh_channel_request_shell(m_channel);
|
||||||
|
if (result != SSH_OK) {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return ErrorCode::SshInternalError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode SshSession::writeToChannel(const QString &data)
|
||||||
|
{
|
||||||
|
QFutureWatcher<ErrorCode> watcher;
|
||||||
|
connect(&watcher, &QFutureWatcher<ErrorCode>::finished, this, &SshSession::writeToChannelFinished);
|
||||||
|
|
||||||
|
QFuture<ErrorCode> future = QtConcurrent::run([this, &data]() {
|
||||||
|
return write(data);
|
||||||
|
});
|
||||||
|
watcher.setFuture(future);
|
||||||
|
|
||||||
|
QEventLoop wait;
|
||||||
|
|
||||||
|
QObject::connect(this, &SshSession::writeToChannelFinished, &wait, &QEventLoop::quit);
|
||||||
|
wait.exec();
|
||||||
|
|
||||||
|
return watcher.result();
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorCode SshSession::write(const QString &data)
|
||||||
|
{
|
||||||
|
const int channelReadTimeoutMs = 10;
|
||||||
|
const size_t bufferSize = 2048;
|
||||||
|
|
||||||
|
int bytesToRead = 0;
|
||||||
|
int attempts = 0;
|
||||||
|
char buffer[bufferSize];
|
||||||
|
|
||||||
|
int bytesWritten = ssh_channel_write(m_channel, data.toUtf8(), (uint32_t)data.size());
|
||||||
|
if (bytesWritten == data.size()) {
|
||||||
|
while (bytesToRead != 0 || attempts < 100){
|
||||||
|
if (ssh_channel_is_open(m_channel) && !ssh_channel_is_eof(m_channel)) {
|
||||||
|
bytesToRead = ssh_channel_read_timeout(m_channel, buffer, sizeof(buffer), 0, channelReadTimeoutMs);
|
||||||
|
if (bytesToRead > 0) {
|
||||||
|
attempts = 0;
|
||||||
|
std::string strbuf(buffer, bytesToRead);
|
||||||
|
// QByteArray qbuff(buffer, bytesToRead);
|
||||||
|
// QString outp(buffer);
|
||||||
|
|
||||||
|
// if (cbReadStdOut){
|
||||||
|
// cbReadStdOut(outp, nullptr);
|
||||||
|
// }
|
||||||
|
qDebug().noquote() << QString(strbuf.c_str());
|
||||||
|
} else {
|
||||||
|
attempts++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qDebug() << ssh_get_error(m_session);
|
||||||
|
return ErrorCode::SshInternalError;
|
||||||
|
}
|
||||||
|
return ErrorCode::NoError;
|
||||||
|
}
|
36
client/core/sshsession.h
Normal file
36
client/core/sshsession.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#ifndef SSHSESSION_H
|
||||||
|
#define SSHSESSION_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <libssh/libssh.h>
|
||||||
|
#include <libssh/sftp.h>
|
||||||
|
|
||||||
|
#include "defs.h"
|
||||||
|
|
||||||
|
using namespace amnezia;
|
||||||
|
|
||||||
|
class SshSession : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SshSession(QObject *parent = nullptr);
|
||||||
|
~SshSession();
|
||||||
|
|
||||||
|
ErrorCode initChannel(const ServerCredentials &credentials);
|
||||||
|
ErrorCode writeToChannel(const QString &data);
|
||||||
|
private:
|
||||||
|
ErrorCode connectToHost(const ServerCredentials &credentials);
|
||||||
|
ErrorCode write(const QString &data);
|
||||||
|
|
||||||
|
ssh_session m_session;
|
||||||
|
ssh_channel m_channel;
|
||||||
|
|
||||||
|
bool m_isChannelOpened = false;
|
||||||
|
bool m_isSessionConnected = false;
|
||||||
|
bool m_isNeedSendChannelEof = false;
|
||||||
|
signals:
|
||||||
|
void writeToChannelFinished();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SSHSESSION_H
|
Loading…
Add table
Add a link
Reference in a new issue